LLX > Neil Parker > Apple II > SHLAM

S. H. Lam Revisited

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. Nor can you put it in a FOR/NEXT loop, for the same reason—you'll get a spurious ?NEXT WITHOUT FOR ERROR when Applesoft reaches the NEXT.

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.)

Downloads

Here's an & command for Applesoft, with source code (Merlin Pro assembler), which can save you from needing to worry about any of the above.

To install, just BRUN SHLAMCALL...it loads at memory location 8192 ($2000), and then relocates and protects itself above HIMEM (the PrDOS version consumes 256 bytes above BASIC.SYSTEM's buffers, and the DOS 3.3 version consumes 141 bytes above HIMEM). It implements two new commands, & "SHLAMCALL" and & CALL.

& "SHLAMCALL"

& "SHLAMCALL"

This is is intended mostly for use under ProDOS (though it's available under DOS 3.3 too). It does nothing, and returns with no error. Its purpose is for testing whether SHLAMCALL is already installed—if & "SHLAMCALL" succeeds without an error, you don't need to install it again. For example,

10  ON  PEEK(116) > 149 OR  PEEK (1015) = 190 GOTO 40: ONERR  GOTO 30
20  & "SHLAMCALL": POKE 216,0, GOTO 50
30  CALL 62248: POKE 216,0
40  PRINT  CHR$ (4);"BRUN SHLAMCALL"
50  REM The rest of your program starts here

Line 10 tests whether HIMEM too high to accommodate SHLAMCALL or whether the & vector hasn't been used yet. If not, line 20 tests whether it's already installed.

Since the DOS 3.3 version isn't safe from FP, INT, or MAXFILES, it's probably easier to just throw away any previous SHLAMCALL and load it anew...for example:

10  POKE 1013, 76: POKE 1014,88: POKE 1015,255: REM Reset & vector
20  PRINT  CHR$ (4);"MAXFILES 3": REM Throw away above-HIMEM buffer
30  PRINT  CHR$ (4);"BRUN SHLAMCALL"
40  REM The rest of your program starts here

& CALL

&  CALL string-expression

This passes the string-expression to the Monitor for execution. The expression should not evaluate to a string longer than 246 characters, because the routine adds nine additional cheracters to it to enable the return back to Applesoft. Do not put your own return-to-Applesoft on the end of the command.

Using & CALL directly from the command line may produce a spurious ?SYNTAX ERROR, due to Applesoft and the Monitor fighting over whose turn it is to use the input buffer. This is not a problem when using & CALL inside a program.

Example usage:

100  &  CALL "300:AD 30 C0 60"

& &

If any other & routines were installed before SHLAMCALL, they can still be invoked as if SHLAMCALL were not present, unless the earlier routine is also named & CALL. In that case, putting an additional & in front of the command will pass it to the earlier routine.

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.
Modified: November 3, 2021—Added downloads. Also, Prof. Lam passed away in 2018, and his Princeton web page no longer exists. Here is the latest version I've been able to find at the Wayback machine.