(Comp.sys.hp48) Item: 2983 by mheiskan@hut.fi [Mika Heiskanen] Subj: Hunt the Bug Date: 15 Dec 1993 Ok, yet again boredom struck so here's another challenge for the ml programmers. The question is, what's wrong with the program? (Try it a couple times) * Create a big string, duplicate it ( --> $ $ ) CODE GOSBVL =SAVPTR GOSBVL =ROOM * C[A] = room A=C A LC(5) 21 * Space needed for prolog, lenght etc C=A-C A * C[A] = nibbles in string GOSBVL =MAKE$N * R0[A] = ->string A=R0 GOSBVL =GPPushA * Push string GOVLNG =PUSHA * Push it again, loop ENDCODE -- ---> Mika Heiskanen, mheiskan@delta.hut.fi ---------- Resp: 1 of 1 by mheiskan@hut.fi [Mika Heiskanen] Date: 17 Dec 1993 >Ok, yet again boredom struck so here's another challenge for the ml >programmers. The question is, what's wrong with the program? >(Try it a couple times) Well, I think I've given enough time. >* Create a big string, duplicate it ( --> $ $ ) The problem with the program is that above stack diagram isn't always correct, very frequently you get an 'external' to stack level 1. >CODE > GOSBVL =SAVPTR > GOSBVL =ROOM * C[A] = room > A=C A > LC(5) 21 * Space needed for prolog, lenght etc > C=A-C A * C[A] = nibbles in string > GOSBVL =MAKE$N * R0[A] = ->string > A=R0 > GOSBVL =GPPushA * Push string > GOVLNG =PUSHA * Push it again, loop >ENDCODE The bug in the program was using PUSHA in the first place, you shouldn't use it to push addresses in tempob area. The MAKE$N call builds as big as string as it can to waste all possible memory. MAKE$N assumes you're going to push the string to the stack though so it checks 5 nibbles will always be available. Thus GPPushA finds enough memory to push the string, but when returning D will be 0. When PUSHA gets D=0 as the RPL state the following happens: =PUSHA D=D-1 A * D goes to FFFFF, carry is set GOC L_03A9B * Jump is executed ( Following is initially skipped ) L_03A8B D1=D1- 5 DAT1=A A A=DAT0 A D0=D0+ 5 PC=(A) L_03A9B R2=A * Save A[A] to R2 GOSUBL SAVPTR * Save rpl state and do GC GOSUBL GARBAGECOL GOSUBL GETPTR A=R2 * Restore A[A] from R2 D=D-1 A * See again if there is room in the stack GONC L_03A8B * Jump to push A[A] if enough memory [do memory error] The thing to note is that the address of the string is saved as is, so when GC happens R2[A] is unlikely to point to the string anymore. Thus in the example program you are likely to push a garbage pointer to the stack (which can easily cause crashes). The conclusion is that you shouldn't use PUSHA to push tempob addresses unless you're sure GC will not be performed. As stated above MAKE$N makes sure there is atleast 1 stack level available so a simple PUSHA call would work (this is in fact why I had to push twice). If you're not sure GC will not be performed you should use GPPushA which when GC happens stores the address in A[A] to a RAM variable which GC will update if necessary (the variable is just for this specific purpose). Thus a possible fix to the program would be: CODE GOSBVL =SAVPTR GOSBVL =ROOM * C[A] = room A=C A LC(5) 21 * Space needed for prolog, lenght etc C=A-C A * C[A] = nibbles in string GOSBVL =MAKE$N * R0[A] = ->string A=R0 GOSBVL =GPPushA * Push string GOSBVL =SAVPTR GOSBVL =GPPushA LOOP ENDCODE Another way would of course be [same] A=R0 GOSBVL =GPPushA LC(5) =DUP * Or simply GOVLNG (=DUP)+5 if you think A=C A * you're not obliged to using supported PC=(A) * entries only ENDCODE The thing that makes me worry is that none of the many ml programmers around responded to the article, makes you wonder how many bugged programs are out there since PUSHA and GPPushA are the only supported entries that push addresses. The chances of PUSHA failing are quite slim but all it takes is that one time to lose your RAM. -- ---> Mika Heiskanen, mheiskan@delta.hut.fi