Add early reverse-engineering work for Apple II EZIP (Z-Machine architecture v4) interpreters.

This commit is contained in:
Eric Smith 2023-06-29 12:53:20 -06:00
parent d25403a3dd
commit e25e2b336f
4 changed files with 6495 additions and 50 deletions

View File

@ -2,7 +2,13 @@ all: zip1.lst zip1.bin zip1-check \
zip2.lst zip2.bin zip2-check \
zip3.lst zip3.bin zip3-check \
zip3a.lst zip3a.bin zip3a-check \
zip3b.lst zip3b.bin zip3b-check
zip3b.lst zip3b.bin zip3b-check \
ezip2a.lst ezip2a.bin ezip2a-check \
ezip2b.lst ezip2b.bin ezip2b-check \
ezip2c.lst ezip2c.bin ezip2c-check \
ezip2d.lst ezip2d.bin ezip2d-check \
ezip2h.lst ezip2h.bin ezip2h-check
%.p %.lst: %.asm
asl $< -o $*.p -L
@ -58,8 +64,59 @@ zip3b-check: zip3b.bin
echo "98ab866beb68f1d78b978a36106e611fd9196ffe7cd6d2e534c145197be3ebc1 zip3b.bin" | sha256sum -c -
ezip2a.p ezip2a.lst: ezip.asm
asl ezip.asm -o ezip2a.p -L -OLIST ezip2a.lst -D iver='$$0201'
ezip2a.bin: ezip2a.p
p2bin -r '$$d000-$$f6ff' ezip2a.p
ezip2a-check: ezip2a.bin
echo "27791a3458d8c37a8db84a07dc74ce38ec902297a42acf578d259485a4b652db ezip2a.bin" | sha256sum -c
ezip2b.p ezip2b.lst: ezip.asm
asl ezip.asm -o ezip2b.p -L -OLIST ezip2b.lst -D iver='$$0202'
ezip2b.bin: ezip2b.p
p2bin -r '$$d000-$$f7ff' ezip2b.p
ezip2b-check: ezip2b.bin
echo "86b3cfd5a834f304bdf8094ed56efaab5d75f318291590fdc4594c87c731f701 ezip2b.bin" | sha256sum -c
ezip2c.p ezip2c.lst: ezip.asm
asl ezip.asm -o ezip2c.p -L -OLIST ezip2c.lst -D iver='$$0203'
ezip2c.bin: ezip2c.p
p2bin -r '$$d000-$$f7ff' ezip2c.p
ezip2c-check: ezip2c.bin
echo "9fd17f9952a447affc995948ae6356b83f68e828caed50dfaa850826083d5248 ezip2c.bin" | sha256sum -c
ezip2d.p ezip2d.lst: ezip.asm
asl ezip.asm -o ezip2d.p -L -OLIST ezip2d.lst -D iver='$$0204'
ezip2d.bin: ezip2d.p
p2bin -r '$$d000-$$f7ff' ezip2d.p
ezip2d-check: ezip2d.bin
echo "2171db824068a23d9291031a20ea81bbbeea7d5af8174e42153adfb282dc99a5 ezip2d.bin" | sha256sum -c
ezip2h.p ezip2h.lst: ezip.asm
asl ezip.asm -o ezip2h.p -L -OLIST ezip2h.lst -D iver='$$0208'
ezip2h.bin: ezip2h.p
p2bin -r '$$d000-$$f7ff' ezip2h.p
ezip2h-check: ezip2h.bin
echo "2683ce2f038bce968796540a3a9ce652a9e4120d06a75ee2e80c09cab7c09503 ezip2h.bin" | sha256sum -c
clean:
rm -f zip[23].{p,lst,bin}
rm -f zip{1,2,3,3a,3b}.{p,lst,bin}
rm -f ezip2{a,b,c,d,h}.{p,lst,bin}
.PRECIOUS: %.lst

View File

@ -1,7 +1,7 @@
# a2zip - Infocom ZIP version 1 through 3 interpreters for Apple II, partially reverse-engineered
# a2zip - Infocom ZIP interpreters for Apple II, Z-Machine architectures 1 through 4, partially reverse-engineered
Original code copyright Infocom, Inc.
Disassembly including labels and comments copyright 1984 Eric Smith <spacewar@gmail.com>
Disassembly including labels and comments copyright 1983-2023 Eric Smith <spacewar@gmail.com>
z2zip development is hosted at the
[a2zip Github repository](https://github.com/brouhaha/a2zip/).
@ -10,48 +10,79 @@ z2zip development is hosted at the
Infocom games are written in ZIL and compiled to Z-code, which is
interpreted on a ZIP interpreter. ZIP interpreters were written for
many computers, including the Apple II. In 1984 I did reverse-engineered
many computers, including the Apple II. In 1983 to 1984 I reverse-engineered
a substantial portion of the Apple II version 1 through 3 ZIP interpreters.
Originally I assembled the code using Microsoft ALDS; the version of the
sources for that assembler are in the alds subdirectory.
More recently I've partially reverse-engineered a few newer Apple II ZIP and
EZIP interpreters.
The current source code assembles using the Macro Assembler AS:
http://john.ccac.rwth-aachen.de:8000/as/
## ZIP (Z-Machine architectures v1 through v3)
ZIP version 1 is only known to have been used for Zork I release 2
The Apple II ZIP intepreters for v1, v2, v3, v3 A, and v3B can be built from
the source file "zip.asm".
Z-Machine architectures v1 through v3 have only fairly minor differences. The
interpreters for these versions are known as ZIP. The v1 and v2 architectures
were only used for early releases of Zork I and Zork II, and were quickly
obsolted by v3. which was the most common architecture for Infocom games that
did not require more than 128 KiB of virtual machine memory.
Z-Machine architecture v1 is only known to have been used for Zork I release 2
(which might not have been released for the Apple II), and Zork I release 5.
Zork I release 5 was provided for the Apple II on a 13-sector disk, while no
later releases or other Infocom games were available on 13-sector disks.
ZIP version 2 is only known to have been used for Zork I release 15
Z-Machine architecture v2 is only known to have been used for Zork I release 15
and Zork II release 7.
Later releases of the Zork games, and many other Infocom games, use
ZIP version 3. The earliest version 3 interpreter, like the version 1 and
version 2 interpreters, did not have any more specific interpreter revision
identification. Later version 3 interpreter revisions for the Apple II
had a revision letter, with revisions A, B, E, H, K, and M known to exist.
Of the lettered revisions, only A and B are currently represented here.
The earliest v3 interpreter for the Apple II, like those for v1 and v2, did
not have any more specific interpreter revision identification. Later v3
interpreter revisions had a revision letter, with revisions A, B, F, H, K,
and M known to exist. Of the lettered revisions, only A and B are currently
represented here.
Version 3 revision A added support for the Apple IIe 80-column text mode, with upper
Revision A added support for the Apple IIe 80-column text mode, with upper
and lower case display, and allowed the user to select a slot number for the
printer interface card, which previously was required to be slot 1.
Version 3 revision B added support for splitting the screen into two windows.
Revision B added support for splitting the screen into two windows.
Changes introduced in later revisions of the version 3 interpreter have not
yet been analyzed.
Between Revision B and revision F, a substantial rearrangement of the
code occurred. These and later changes to the v3 interpreter have not yet
been analyzed, and are not currently represented here.
Eventually game images exceeded the 128KiB limit of ZIP version 3, therefore
necessitating new ZIP versions 4, 5, and 6, also known as EZIP, XZIP, and YZIP,
respectively. These are not represented here.
## EZIP
Z-Machine architecture v4 doubled the available virtual machine memory size
to 256 KiB, allowed a game to have more objects, allowed vocabulary words to
have up to nine significant characters, and added some improvements to I/O
capabilities. The v4 interpreters are known as EZIP. There were five
revisions of the Apple II EZIP interpreter, designated 2A through 2D, and 2H.
Early work on reverse-engineering these is present in the source file
"ezip.asm".
## XZIP and YZIP
Eventually Infocom added even more capabilities, resulting in Z-Machine
architectures 5 and 6, with interpreters known as XZIP and YZIP.
These are not currently represented here.
## Archive of earliest reverse-engineered source files
When I originally reverse-engineered the early interpreters in 1983-1984, I
assembled the code using Microsoft ALDS, which is not reasily available
or easy to use today. The version of the sources for that assembler are in
the "alds" subdirectory.
The current source code assembles using the Macro Assembler AS:
http://john.ccac.rwth-aachen.de:8000/as/
## Acknowledgements
Special thanks to:
* Richard Turk, for saving the printed listing from the work I did in 1983,
* Richard Turk, for saving the printed listing from the work I did in 1983-1984,
which was otherwise lost
* 4am, for preserving disk images of huge amounts of Apple II software,

6353
ezip.asm Normal file

File diff suppressed because it is too large Load Diff

54
zip.asm
View File

@ -448,9 +448,10 @@ l0917: pha
l090a: jsr fatal
; class C instructions (implicit or no operand)
; 0OP instructions (no operands, opcodes $80-$bf)
optab1: fdb oprtnt ; return with TRUE
optab_0op:
fdb oprtnt ; return with TRUE
fdb oprtnf ; return with FALSE
fdb oppsi ; print string immediate
fdb oppsic ; print string immediate, CRLF, return true
@ -474,12 +475,13 @@ optab1: fdb oprtnt ; return with TRUE
fdb opcksm ; checksum the program
endif
opmax1 equ (*-optab1)/2
opmax_0op equ (*-optab_0op)/2
; class B instructions (single operand)
; 1OP instructions (single operand)
optab2: fdb optstz ; compare ARG1=0 (ARG1<>0)
optab_1op:
fdb optstz ; compare ARG1=0 (ARG1<>0)
fdb opgtsb ; get thing's sibling
fdb opgtch ; get thing's child
fdb opgtpr ; get thing's parent
@ -495,13 +497,13 @@ optab2: fdb optstz ; compare ARG1=0 (ARG1<>0)
fdb oppsw ; print string at word address
fdb opmove ; move var ARG1 to var
fdb opnot ; 1's complement
opmax2 equ (*-optab2)/2
opmax_1op equ (*-optab_1op)/2
; class A instructions (variable number of operands, may use short form
; opcode)
; 2OP instructions (two operands, opcodes $00-$7f)
optab3: fdb opfatl
optab_2op:
fdb opfatl
fdb opmtch ; match ARG1 against ARG2, ARG3, or ARG4
fdb l0eb7 ; ??? compare ARG1<=ARG2 (ARG1>ARG2)
fdb l0ecf ; ??? compare ARG1>=ARG2 (ARG1<ARG2)
@ -529,12 +531,14 @@ optab3: fdb opfatl
if iver==iver1
fdb oppsbi ; print string at indexed byte address
endif
opmax3 equ (*-optab3)/2
opmax_2op equ (*-optab_2op)/2
; class D instructions (variable number of operands)
; EXT instructions (0 to 4 operands) from $e0-$ff
; ($c0-$ff dispatch as 2OP)
optab4: fdb opcall ; call procedure
optab_ext:
fdb opcall ; call procedure
fdb opptwd ; store a word
fdb opptby ; store a byte
fdb opptp ; store into thing property
@ -548,7 +552,7 @@ optab4: fdb opcall ; call procedure
fdb x_opsplw ; split widnow
fdb x_opsetw ; set window
endif
opmax4 equ (*-optab4)/2
opmax_ext equ (*-optab_ext)/2
mnloop:
@ -622,12 +626,12 @@ l09d7: pla ; get operand count back
jmp l09af ; try for another
l09ed: dmovi optab4,acc ; assume class D
lda opcode ; but if it's $C0-$DF then it's class A
l09ed: dmovi optab_ext,acc ; assume EXT $e0-$ff (0-4 operands)
lda opcode ; but if it's $C0-$DF then it's the EXT form of a 2OP
cmpjl #$e0,l0a98
sbc #$e0 ; adjust to $00..$1F
cmp #opmax4 ; make sure it's not illegal
cmp #opmax_ext ; make sure it's not illegal
if iver<iver3
jge opfatl
@ -660,10 +664,10 @@ dsptch: jsr dsptch
endif
; process opcode group C ($80-$BF)
; process 0OP instructions (no operands, opcodes $80-$bf)
opcgpc: sub ,#$b0 ; adjust to $00..$0F
cmp #opmax1 ; make sure it's not illegal
cmp #opmax_0op ; make sure it's not illegal
if iver<iver3
jge opfatl
@ -672,7 +676,7 @@ opcgpc: sub ,#$b0 ; adjust to $00..$0F
endif
pha ; save it temp.
dmovi optab1,acc ; get base address of proper table
dmovi optab_0op,acc ; get base address of proper table
pla
jmp godoit
@ -681,7 +685,7 @@ l0a2b: jsr fatal ; oops! illegal opcode
endif
; process opcode group B ($80-$AF)
; process 1OP instructions (single operand, opcodes $80-$AF)
opcgpb: and #$30 ; mask off operand type bits
@ -694,7 +698,7 @@ l0a45: mov #$01,argcnt ; one argument
lda opcode ; adjust opcode to $00..$0F
and #$0f
cmp #opmax2 ; make sure it's not illegal
cmp #opmax_1op ; make sure it's not illegal
if iver<iver3
jge opfatl
@ -703,12 +707,12 @@ l0a45: mov #$01,argcnt ; one argument
endif
pha ; save tmep.
dmovi optab2,acc ; get appropriate table base addr
dmovi optab_1op,acc ; get appropriate table base addr
pla
jmp godoit ; and go do it!
; process opcode group A ($00-$7F)
; process 2OP instructions (two operands, opcodes $00-$7f)
opcgpa: and #$40 ; get type bit for ARG1
jsreq ftprby,l0a73 ; 0: byte immediate
@ -725,7 +729,7 @@ l0a8a: dmov acc,arg2 ; save it
lda opcode ; get opcode back
l0a98: and #$1f ; adjust to $00..$1F
cmp #opmax3 ; make sure it's not illegal
cmp #opmax_2op ; make sure it's not illegal
if iver<iver3
jge opfatl
@ -734,7 +738,7 @@ l0a98: and #$1f ; adjust to $00..$1F
endif
pha ; save temp.
dmovi optab3,acc ; get base addr of appropriate table
dmovi optab_2op,acc ; get base addr of appropriate table
pla
jmp godoit ; and go do it!