LLX > Neil Parker > Apple II > SHLAM
Many Applesoft programmers are familiar with the following neat trick for poking machine language data into memory, or more generally, executing almost any Monitor command from inside an Applesoft program:
10 H$ = "300:AD 30 C0 60 N D823G" 20 FOR X = 1 TO LEN (H$): POKE 511 + X, ASC ( MID$ (H$,X,1)) + 128: NEXT 30 POKE 72,0: CALL - 144
This pokes the bytes $AD, $30, $C0, $60 into memory starting at address $300. It works by poking the Monitor command into the GETLN input buffer, and calling the Monitor command parser. Since the command parser doesn't return to its caller, the string ends with D823G, which continues the Applesoft CALL hander at the point where it left off.
This trick is commonly known as the S. H. Lam technique, or simply the Lam technique, after the person who first discovered it. It's an incredibly useful trick, since there are several things that are easy to do in the Monitor but are a pain in the butt to do Applesoft (e.g. store hex data in memory, move or fill large memory blocks, etc.).
Unfortunately, it has one serious drawback: It leaves junk on the stack. If you use it enough times in one program, you'll eventually run out of stack space, and your program will crash with ?OUT OF MEMORY ERROR. The extraneous junk on the stack also means that you can't put it in a subroutine, because the stack junk will hide Applesoft's GOSUB data, causing a spurious ?RETURN WITHOUT GOSUB ERROR when it hits the RETURN statement.
The RETURN problem is especially annoying...the advantages of putting lines 20 and 30 above into a subroutine should be obvious.
The stack junk comes from two sources. First is the Applesoft CALL command, which calls its target routine with a JSR. The JSR, of course, leaves a return address on the stack, which never gets used because the Monitor command parser never returns to its caller.
Second is the Monitor G command. This also uses JSR to call its target routine. But the routine at $D823 also doesn't return to its caller--it just jumps back to the Applesoft interpreter main loop.
A frequently-quoted solution to this problem is to replace D823G with D9C6G. The code at $D9C6 looks like this:
D9C6- 48 PLA D9C7- 48 PLA D9C8- 60 RTS
This pops the return address left by the Monitor G command, and RTSes back to the CALL handler. That eliminates the extra junk on the stack, and makes the Lam technique useable in a subroutine.
...Except when it doesn't. The fixed technique works on most Apple II models, but not on the Apple IIGS. The problem is that on the IIGS, the Monitor G command leaves more than just its return address on the stack. After popping the return address, the RTS returns to never-never-land instead of the CALL handler (whose true return address is buried several bytes deeper on the stack).
So is there a way to do it that works on all Apple II models?
Yes. A couple of short machine language routines, poked into memory before using the Lam technique, can solve all the problems described above.
The secret is simple: Save the stack pointer before entering the Monitor command parser, and restore it before trying to RTS back to Applesoft. Here's some code that does the trick:
10 FOR X = 0 TO 10: READ Y: POKE 960 + X,Y: NEXT 20 DATA 162,0,154,96,186,142,193,3,76,112,255 30 H$ = "300:AD 30 C0 60 N 3C0G": GOSUB 100 40 END 100 FOR X = 1 TO LEN (H$): POKE 511 + X, ASC ( MID$ (H$,X,1)) + 128: NEXT 110 POKE 72,0: CALL 964: RETURN
Lines 10 and 20 poke the following machine language code into RAM:
03C0- A2 00 LDX #$00 03C2- 9A TXS 03C3- 60 RTS 03C4- BA TSX 03C5- 8E C1 03 STX $03C1 03C8- 4C 70 FF JMP $FF70
There are two subroutines here. At $3C4 (decimal 964) is the routine that saves the stack pointer (overwriting the operand of the LDX at $3C0), and JMPs to the Monitor command parser. At $3C0 is the other half of the trick: It simply restores the saved stack pointer and RTSes back to Applesoft.
If you don't like poking that extra machine language, there's another way to do pretty much the same thing:
10 H$ = "300:AD 30 C0 60 N F328G": GOSUB 100 20 END 100 FOR X = 1 TO LEN (H$): POKE 511 + X, ASC ( MID$ (H$,X,1)) + 128: NEXT 110 POKE 72,0: POKE 223, PEEK (248): CALL - 144: RETURN
This depends on a couple of Applesoft's internal variables: Applesoft saves the stack pointer in memory location 248 ($F8) at the beginning of every statement, and copies it to location 223 ($DF) whenever an error is trapped by ONERR GOTO. The routine at $F328 is the tail-end of the RESUME statement handler, which looks like this:
F328- A6 DF LDX $DF F32A- 9A TXS F32B- 4C D2 D7 JMP $D7D2
where $D7D2 is the new statement fetcher, which the CALL is supposed to return to anyway.
The reliance on ONERR GOTO internals has a side effect: This method shouldn't be used as-is inside an ONERR GOTO handler. If you must use it in an ONERR GOTO handler, be sure to CALL the famous ONERR-fixer before the POKE 223, PEEK (248) statement.
These have been tested, and found to work, on an Apple II Plus, Apple IIc, and Apple IIGS. I know of no reason why they wouldn't work on other Apple II models too.
(Actually, that's not entirely true. There are rumors of some early Laser 128 versions that lack the CALL -144 entry point, without which the Lam technique is impossible. There are also rumors that ROM 0 Apple IIGSes suffer from the same lack, but I suspect this was a misunderstanding (probably fueled by the fact that the D9C6G fix doesn't work on any Apple IIGS)...the Apple IIGS Firmware Reference documents $FF70 (CALL -144) as an officially supported entry point.)
LLX > Neil Parker > Apple II > SHLAM
Original: July 27, 2004
Modified: March 24, 2006--I got mail from Prof. Sau-Hai (Harvey) Lam, the inventor of the Lam technique. His web page is now linked to above.