The RI stack again
Posted: Tue May 27, 2025 11:55 am
Handling the arithmetic stack, or RI stack, has been fraught for toolkit writers since the dawn of the QL age. Myths abound, rumours swirl, secret information is whispered among the illuminati, but nothing seems right. Or rather it seems to work for a while, and may then be included in some larger project and forgotten about.
Suddenly you experience mysterious crashes. Programs that used to work start misbehaving and all is chaos as you frantically try to debug millions of line of code before finally succumbing, ending up as a gibbering wreck at some institution for the seriously insane. (Yes, there must be thousands of such individuals dotted around the globe in secure institutions. Where else have all the QLers disappeared to?)
Such a fate need not be yours! There is some literature on the subject. Perhaps survivors could list some vetted ones here as I cant recall the names of any off the top of my head. However, be mindful of anything not written within the last 10 years or so! Thats where the gremlins lurk.
Another thing one must do it TEST the code. The first test is that the code produces the right answer, of course - preferably without crashing the system. The next tests are stress tests. They will be different depending on the function or procedure in question. Below are some rough templates of two such possible tests. These look for memory leakage due to a mishandling og the RI stack and some other ills. The first one is for functions:
This will list every time the functions grabs more memory. One such grab might be as expected, but at the end of the run the memory difference should end up as zero or the first amount grabbed.
As I mentioned, these templates may need to be tailored for the specific function to be tested. If, for example 'r' needed to be a string instead of a number, tests should be made for even and odd lengthed strings.
The next test is for procedures. These dont usually suffer from stack problems. But for people who may, for example, have learnt that A1 has a particular value on entry to a function or procedure, and then use that value to perform some other action, eg reserve memory on the RI stack, they may be in for a surprise!
It is important to do as many rounds as possible to ensure there are no leaks. But for BBQLs 99 iterations is as much as patience may accept. 9999+ for QPC2 et al. Also the more iterations the more accurate the timings, where that may be of interest. If a proc/fn is suspiciously slow, that could also indicate a problem.
Unfortunately, there are slight differences in behaviour between the various OSes: Qdos, Minerva, and SMSQ/E. Provided one sticks to documented calls, values and offsets, and doesnt go off-piste on the above mentioned myths etc, one should be alright. However, it doesnt hurt to run these tests on all systems a toolkit is designed to work with.
Here endeth the lesson. Please feel free to challenge any mistakes you think I may have made. We have suffered long enough!
Suddenly you experience mysterious crashes. Programs that used to work start misbehaving and all is chaos as you frantically try to debug millions of line of code before finally succumbing, ending up as a gibbering wreck at some institution for the seriously insane. (Yes, there must be thousands of such individuals dotted around the globe in secure institutions. Where else have all the QLers disappeared to?)
Such a fate need not be yours! There is some literature on the subject. Perhaps survivors could list some vetted ones here as I cant recall the names of any off the top of my head. However, be mindful of anything not written within the last 10 years or so! Thats where the gremlins lurk.
Another thing one must do it TEST the code. The first test is that the code produces the right answer, of course - preferably without crashing the system. The next tests are stress tests. They will be different depending on the function or procedure in question. Below are some rough templates of two such possible tests. These look for memory leakage due to a mishandling og the RI stack and some other ills. The first one is for functions:
Code: Select all
100 RANDOMISE
102 m = FREE_MEM: mm = m
104 t = DATE
106 :
108 FOR i = 0 TO 999
110 r = RND(-1 TO 1)
112 t = MYFUN(r)
114 t = 1 + MYFUN(r)
116 t = 1 + MYFUN(r) + 1
118 t = MYFUN(r) + 1
120 :
122 IF m <> FREE_MEM THEN
124 PRINT m - FREE_MEM
126 m = FREE_MEM
128 END IF
130 END FOR i
132 :
134 PRINT 'Time:'! DATE - t
136 PRINT'Mem:'! mm - FREE_MEM
138 :
As I mentioned, these templates may need to be tailored for the specific function to be tested. If, for example 'r' needed to be a string instead of a number, tests should be made for even and odd lengthed strings.
The next test is for procedures. These dont usually suffer from stack problems. But for people who may, for example, have learnt that A1 has a particular value on entry to a function or procedure, and then use that value to perform some other action, eg reserve memory on the RI stack, they may be in for a surprise!
Code: Select all
100 m = FREE_MEM: mm = m
110 t = DATE
120 :
130 ch = FOPEN("nul"): REMark SMSQ/E only without extra driver
140 FOR i = 0 TO 999
150 MYPROC#ch; 'some text'
160 MYPROC#ch; 'some texts'
170 MYPROC#ch; 'some tex'
180 :
190 IF m <> FREE_MEM THEN
200 PRINT m - FREE_MEM
210 m = FREE_MEM
220 END IF
230 END FOR i
240 :
250 PRINT 'Time:'! DATE - t
260 PRINT'Mem:'! mm - FREE_MEM
270 CLOSE#ch
280 :
Unfortunately, there are slight differences in behaviour between the various OSes: Qdos, Minerva, and SMSQ/E. Provided one sticks to documented calls, values and offsets, and doesnt go off-piste on the above mentioned myths etc, one should be alright. However, it doesnt hurt to run these tests on all systems a toolkit is designed to work with.
Here endeth the lesson. Please feel free to challenge any mistakes you think I may have made. We have suffered long enough!