Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Wizardry re-engineering

3,916 views
Skip to first unread message

Didier VALLET

unread,
Mar 26, 2014, 2:00:07 AM3/26/14
to

Hello, i've read a part of Tommy's big work on Wizardry III and i'm interested in looking on the older ones like Wizardry I (also called Sorcellerie in French)

"
Le mardi 10 avril 2012 09:53:17 UTC+2, TommyGoog a écrit :
> Recently I completed a rather interesting project that I'd like to
> share with the Apple][ community. I re-engineered the two main
> executable files, WIZARDRY.CODE and WIZUTIL.CODE, for "Wizardry III,
> Legacy of Llylgamyn" into Pascal code.
"

I saw that there are tools to do it like his Wiz4 or Hyde.
Does anybody know where are tools to work on p-code or can help me to start.
Thanks a lot. Didier

TommyGoog

unread,
Mar 26, 2014, 10:14:45 PM3/26/14
to
On Wednesday, March 26, 2014 1:00:07 AM UTC-5, Didier VALLET wrote:
> Hello, i've read a part of Tommy's big work on Wizardry III and i'm interested in looking on the older ones like Wizardry I (also called Sorcellerie in French)
>
>
>
> I saw that there are tools to do it like his Wiz4 or Hyde.
>
> Does anybody know where are tools to work on p-code or can help me to start.
>
> Thanks a lot. Didier

Hi Didier,

I am still alive. (Last year I suffered a heart attack.)

I have the Wiz4 program that I wrote. It was written in Applesoft Basic. I have it in a ".dsk" image that I used with AppleWin. I also have the source code text of Wiz4 "printed" to a file (".txt").

If you would like the ".dsk" image (or the text file listing), what is the best way to get it (them) to you?

Tommy

TommyGoog

unread,
Mar 29, 2014, 10:27:56 PM3/29/14
to
On Wednesday, March 26, 2014 1:00:07 AM UTC-5, Didier VALLET wrote:
I've replied to an email from Didier and sent him a copy of WIZ4. If anyone else would like a copy of WIZ4 let me know.

I'm now tempted to re-engineer Wizardry I (Proving Grounds). Would anyone besides Didier be interested if I did? I ask this because there didn't seem to be a lot of interest after I re-engineered Wizardry III (see my post 2 years ago: Re-engineered: Wizardry III, Legacy of Llylgamyn).

Tommy



Tommy

Vladimir Ivanov

unread,
Mar 31, 2014, 6:57:25 AM3/31/14
to


On Sat, 29 Mar 2014, TommyGoog wrote:

> I've replied to an email from Didier and sent him a copy of WIZ4. If
> anyone else would like a copy of WIZ4 let me know.

I'd suggest you upload your works and documentation to Asimov.

> I'm now tempted to re-engineer Wizardry I (Proving Grounds). Would
> anyone besides Didier be interested if I did? I ask this because there
> didn't seem to be a lot of interest after I re-engineered Wizardry III
> (see my post 2 years ago: Re-engineered: Wizardry III, Legacy of
> Llylgamyn).

I followed you previous work, which then led me to discovering Hyde's
book. I suppose there are other people like me that don't have interest in
the Wizardry games themselves, but rather in the reverse-engineering
process.

Good luck!

Rob Craig

unread,
Apr 1, 2014, 5:11:33 PM4/1/14
to
Hi Tommy,
Im very interested in you work. Can you send me the things you have discovered. Especially Wizardry 4. A true masterpiece.

I run www.wizardryarchives.com

Cheers,
Rob

sicklittlemonkey

unread,
Apr 1, 2014, 8:14:58 PM4/1/14
to
On Sunday, 30 March 2014 15:27:56 UTC+13, TommyGoog wrote:
> I'm now tempted to re-engineer Wizardry I (Proving Grounds). Would anyone besides Didier be interested if I did? I ask this because there didn't seem to be a lot of interest after I re-engineered Wizardry III (see my post 2 years ago: Re-engineered: Wizardry III, Legacy of Llylgamyn).

<takes off hat>
Wow. You have completed an Epic Hack, Sir. Larger and longer than the games themselves!

Original discussion (which I missed) is here:
https://groups.google.com/d/topic/comp.sys.apple2/2oDJTbQaJWU/discussion

If you did Wizardy I (Proving Grounds) I'd definitely read your report with great interest. I don't know how many people are likely to actually use the source code, but that's not why you did it, is it? ; - ) At least you've overcome the first _decidedly_ non-trivial hurdle should someone else want to port it to another platform. (There are Pascal to JavaScript compilers ...)

Also, serious academic research is being done into gaming history, and some future researcher will likely use what you've done - if it's preserved. I don't know about you, but even being being a footnote in a published paper would make me happy enough.

I'm thinking of places like this:
http://www.icheg.org/research

They have Wizardry in their collection (this is the PC version though):
http://www.thestrong.org/online-collections/nmop/22/52/110.10828

Cheers,
Nick.

Steve Nickolas

unread,
Apr 1, 2014, 11:42:13 PM4/1/14
to
This makes me wonder: if Wizardry is a 48K Pascal program, would it be
possible to modify the 48K Pascal VM it uses to run on top of ProDOS and
use the ProDOS filesystem?

-uso.

TommyGoog

unread,
Apr 2, 2014, 9:14:01 PM4/2/14
to
On Wednesday, March 26, 2014 1:00:07 AM UTC-5, Didier VALLET wrote:
Re-Engineering Wizardry I Proving Grounds. The adventure begins....

In the coming months I (Tommy) will hopefully be re-engineering Wizardry I Proving Grounds. Instead of making the journey alone, I will frequently update this thread with my progress. I encourage anyone with questions to chime in. There will likely be hints and information regarding re-engineering that were not in the thread where I re-engineered Wizardry III (Legacy of Llylgamyn, LOL or LofL).

Review LofL thread.
Get reading materials (Apple Pascal OS 1.1, Apple Pascal Language Ref + Addendum, etc.)
Set up directory (Wizardry1) and files on my WindowsXP pc:
01 EDIT.DSK
SYSTEM.APPLE
SYSTEM.PASCAL
SYSTEM.MISCINFO
SYSTEM.FILER
SYSTEM.EDITOR
SYSTEM.COMPILER
SYSTEM.SWAPDISK
SYSTEM.LIBRARY
02 COMPILE.DSK
SYSTEM.APPLE
SYSTEM.PASCAL
SYSTEM.MISCINFO
SYSTEM.COMPILER
SYSTEM.LINKER
SYSTEM.ASSMBLER
6500.OPCODES
6500.ERRORS
04 APPLE3.1.1.DSK
FORMATTER.CODE
LIBRARY.CODE
05 DECOMPILE.DSK
HELLO
WIZ4
06 WIZ1A.DSK
07 WIZ1B.DSK
08 WIZ1C.DSK
09 WIZ1D.DSK (For 605 ASM code)
10 WIZ1E.DSK (for SYSTEM.STARTUP)
11 MASTER.DSK (DOS Master Disk)
12 PASCAL BLANK.DSK (Init'd using FORMATTER in PASCAL)
13 MAKELIB.DSK (Used when altering SYSTEM.LIBRARY)
14 UnalteredAPPLE1.1.1.DSK
SYSTEM.LIBRARY

Shortcut to my 4-disk enabled AppleWin.exe (Don't even think about using a 2-disk drive system for this.)
pCodeFrenchProvGrndBoot.txt (Using WIZ4)
pCodeProvGrndBoot.txt (Using WIZ4)
Printer.txt (Output from AppleWin.exe execution)
PROVGRNDBOOT.DSK
PROVGRNDSCN.DSK
Shortcut to WinDiff.exe
Wiz1Design.xls (I generally use Excel to record my progress.)

Verified my version of Wizardry 1 from my "box" set of Wizardry I, II, III is same as one at ASIMOV.

From ASIMOV/Sorcellerie:
Ran WIZ4 for Sorcellerie and did a quick eyeball comparison to English version.
Most of code will be identical I think, except for the language differences.

A lot of the code for Wizardry I will be the same as for Wizardry III.

One thought for developing the Wiz1 Pascal code is to first start with Wiz3 and remove the code that is vastly different, then start building up the resulting code.

Copied LofL source code disks to WIZ1A, WIZ1B, WIZ1C, WIZ1D.
Copied SYSTEM.LIBRARY from LofL to EDIT.dsk and COMPILE.dsk.
Modified source code to refer to new volumne name (WIZ1A:, WIZ1B:, WIZ1C:, etc.) on the include statements.

Compiled the code.
Remembered just how tight the space is on the disks. Need to delete WIZARDRY.CODE first, and then make sure output goes to disk with about 200 free blocks.
Made sure that SYSTEM.LIBRARY contained the 6502 code for Wizardry.
Made sure SWAPFILE was present.
Turned on S(WAP option on PASCAL main menu (to edit large files).
Remembered "($S+)" swapping option.

Where to begin?

(We rest after the first hard day of our journey.)

TommyGoog

unread,
Apr 4, 2014, 1:20:16 PM4/4/14
to
....The journey continues....

Let's start by looking at the directory listings for Wizardry I:

WBV2.1:
1 WIZARDRY.CODE
2 RTSTRP.APPLE
3 SYSTEM.PASCAL
4 SYSTEM.LIBRARY
5 SYSTEM.MISCINFO
6 WT
7 SYSTEM.STARTUP


Compare with LOL:

LOLBOOT:
1 WIZARDRY.CODE
2 SYSTEM.CHARSET
3 SYSTEM.STARTUP
4 PICTURE.BITS
5 WIZUTIL.CODE
6 SYSTEM.LIBRARY
7 RTSTRP.APPLE
8 SYSTEM.PASCAL
9 SYSTEM.MISCINFO

Read materials for Apple Pascal OS (EDIT, COMPILE, LINK, SYSTEM.LIBRARY, etc.)
Play with Pascal Editor a bit.
S(et E(nvironment
A(uto indent TRUE
F(illing FALSE
T(oken def FALSE

Note, use AppleWin in APPLE IIe mode for 80 columns.

DeCompiled Wizardry1 (both English and French versions.

I noticed the DeCompiler (Wiz4) generated a few "AN UNKNOWN STANDARD PROCEDURE ?!?!" messages for Wizardry1. This means that Wizardry1 calls an external procedure that I did not encounter in LOL.

I noticed WizUtil from LOL also had 1 "AN UNKNOWN" message. Turns out that one was MEMAVAIL, but I never updated Wiz4.

Note the SEGMENT names in both main programs (WIZARDRY.CODE) are the same:
WIZARDRY
UTILITIE
SHOPS
SPECIALS
COMBAT
CINIT
CUTIL
MELEE
CASTASPE
SWINGASW
CASTLE
ROLLER
CAMP
REWARDS
RUNNER

In LOL, SYSTEM.STARTUP is mostly the "picture" splash screens.
WT is a data file. Perhaps it is the picture bits for Wizardry1.
LOL moved code from Wiz1 SYSTEM.STARTUP to WIZUTIL.CODE, and Wiz1 does not have a WIZUTIL.CODE file.

Let's look at the file SYSTEM.LIBRARY using the LIBMAP utility:

LIBRARY MAP FOR #11:SYSTEM.LIBRARY
$1F Segment #31:
System version = 3.0, code type is P-Code (least sig. 1st)
PASCALIO library unit (LINKED INTRINSIC)
TYPE DECMAX = INTEGER[36];
STUNT = RECORD CASE INTEGER OF
2:(W2:INTEGER[4]);
3:(W3:INTEGER[8]);
4:(W4:INTEGER[12]);
5:(W5:INTEGER[16]);
6:(W6:INTEGER[20]);
7:(W7:INTEGER[24]);
8:(W8:INTEGER[28]);
9:(W9:INTEGER[32]);
10:(W10:INTEGER[36])
END;
PROCEDURE FSEEK(VAR F: FIB; RECNUM: INTEGER);
PROCEDURE FREADREAL(VAR F: FIB; VAR X: REAL);
PROCEDURE FWRITEREAL(VAR F: FIB; X: REAL; W, D: INTEGER);
PROCEDURE FREADDEC(VAR F: FIB; VAR D: STUNT; L: INTEGER);
PROCEDURE FWRITEDEC(VAR F: FIB; D: DECMAX; RLENG: INTEGER);
FUNCTION SUPER_MOD(A,B : INTEGER) : INTEGER;
FUNCTION SUPER_DIV(A,B : INTEGER) : INTEGER;
----------------------------------------------------------------------
$1E Segment #30:
System version = 3.0, code type is 6502
LONGINTI library unit (LINKED INTRINSIC)
TYPE DECMAX = INTEGER[36];
STUNT = RECORD CASE INTEGER OF
2:(W2:INTEGER[4]);
3:(W3:INTEGER[8]);
4:(W4:INTEGER[12]);
5:(W5:INTEGER[16]);
6:(W6:INTEGER[20]);
7:(W7:INTEGER[24]);
8:(W8:INTEGER[28]);
9:(W9:INTEGER[32]);
10:(W10:INTEGER[36])
END;
PROCEDURE FREADDEC(VAR F: FIB; VAR D: STUNT; L: INTEGER);
PROCEDURE FWRITEDEC(VAR F: FIB; D: DECMAX; RLENG: INTEGER);
----------------------------------------------------------------------
$1C Segment #28:
System version = 3.0, code type is P-Code (least sig. 1st)
CHAINSTU library unit (LINKED INTRINSIC)
PROCEDURE SETCHAIN(TYTLE:STRING);
PROCEDURE SETCVAL(VAL:STRING);
PROCEDURE GETCVAL(VAR VAL:STRING);
PROCEDURE SWAPON;
PROCEDURE SWAPOFF;
----------------------------------------------------------------------
$1D Segment #29:
System version = 3.0, code type is P-Code (least sig. 1st)
TRANSCEN library unit (LINKED INTRINSIC)
FUNCTION SIN(X:REAL):REAL;
FUNCTION COS(X:REAL):REAL;
FUNCTION EXP(X:REAL):REAL;
FUNCTION ATAN(X:REAL):REAL;
FUNCTION LN(X:REAL):REAL;
FUNCTION LOG(X:REAL):REAL;
FUNCTION SQRT(X:REAL):REAL;
----------------------------------------------------------------------
$14 Segment #20:
System version = II.1, code type is 6502
TURTLEGR library unit (LINKED INTRINSIC)
TYPE
SCREENCOLOR=(none,white,black,reverse,radar,
black1,green,violet,white1,black2,orange,blue,white2);
PROCEDURE INITTURTLE;
PROCEDURE TURN(ANGLE: INTEGER);
PROCEDURE TURNTO(ANGLE: INTEGER);
PROCEDURE MOVE(DIST: INTEGER);
PROCEDURE MOVETO(X,Y: INTEGER);
PROCEDURE PENCOLOR(PENMODE: SCREENCOLOR);
PROCEDURE TEXTMODE;
PROCEDURE GRAFMODE;
PROCEDURE FILLSCREEN(FILLCOLOR: SCREENCOLOR);
PROCEDURE VIEWPORT(LEFT,RIGHT,BOTTOM,TOP: INTEGER);
FUNCTION TURTLEX: INTEGER;
FUNCTION TURTLEY: INTEGER;
FUNCTION TURTLEANG: INTEGER;
FUNCTION SCREENBIT(X,Y: INTEGER): BOOLEAN;
PROCEDURE DRAWBLOCK(VAR SOURCE; ROWSIZE,XSKIP,YSKIP,WIDTH,HEIGHT,
XSCREEN,YSCREEN,MODE: INTEGER);
PROCEDURE WCHAR(CH: CHAR);
PROCEDURE WSTRING(S: STRING);
PROCEDURE CHARTYPE(MODE: INTEGER);
----------------------------------------------------------------------
$15 Segment #21:
System version = II.1, code type is P-Code (least sig. 1st)
TURTLEGR data segment
----------------------------------------------------------------------
$16 Segment #22:
System version = 3.0, code type is 6502
APPLESTU library unit (LINKED INTRINSIC)
FUNCTION PADDLE(SELECT: INTEGER): INTEGER;
FUNCTION BUTTON(SELECT: INTEGER): BOOLEAN;
PROCEDURE TTLOUT(SELECT: INTEGER; DATA: BOOLEAN);
FUNCTION KEYPRESS: BOOLEAN;
FUNCTION RANDOM: INTEGER;
PROCEDURE RANDOMIZE;
PROCEDURE NOTE(PITCH,DURATION: INTEGER);
----------------------------------------------------------------------


The first program executed on a "turnkey" Pascal system is SYSTEM.STARTUP. I will start by DeCompiling SYSTEM.STARTUP and then later turn my attention to WIZARDRY.CODE.

SYSTEM.STARTUP

SEGMENT: Procedures:
0 WIZBOOT 5
1 UTILS 36
2 TITLELOA 8
3 OPTIONS 1

WIZBOOT lex Start Exit Order Hierarchy
1 0 50CC 50FF 2 7.0.1
2 6502 code 511A 52EA 3
3 6502 code 52F0 54B6 4
4 1 5000 50BA 1 7.0.4
5 6502 code 54BC 5642 5

Every Pascal procedure on a diskette can be identified by a series of 3 numbers (File, Segment, Procedure). This is the information that Wiz4 solicits and is the way that I first name the procedures in the code when I am creating the Pascal code. For example, SYSTEM.STARTUP is file 7 on the disk. Segment 0 is the first segment, and procedure 1 is the main line. I might refer to this as P070001. (File 07, Segment 00, Procedure 01).

Here is the output from Wiz4 (DeCompiler) for P070001 (SYSTEM.STARTUP, SEGMENT 0, Procedure 01):

FILE: 7 SEG: 0 PROC: 1

JTAB FOR SUBROUTINE 1:
DATA SIZE: 12
PARAM SIZE: 4
EXIT AT: 50FF
ENTER AT: 50CC
PROC NUMBER: 1
LEXICAL LEVEL: 0
50CC B9 36 UJP. JUMP TO 5104
50CE CD 1C 01 CXP. CALL EXTERNAL PROCEDURE: 01 IN SEGMENT: 1C
50D1 CE 04 CLP. CALL CHILD PROCEDURE: 04
50D3 CD 08 01 CXP. CALL EXTERNAL PROCEDURE: 01 IN SEGMENT: 08
50D6 CD 09 01 CXP. CALL EXTERNAL PROCEDURE: 01 IN SEGMENT: 09
50D9 EA SLDO. PUSH BASE.03
50DA B9 12 UJP. JUMP TO 50EE
50DC CE 04 CLP. CALL CHILD PROCEDURE: 04
50DE 01 SLDC. PUSH #0001
50DF 01 SLDC. PUSH #0001
50E0 9E 04 EXIT. EXIT FROM PROCEDURE. (TOS)=PROC# (TOS-1)=SEG#
50E2 B9 18 UJP. JUMP TO 50FC
50E4 CD 07 01 CXP. CALL EXTERNAL PROCEDURE: 01 IN SEGMENT: 07
50E7 B9 13 UJP. JUMP TO 50FC
50E9 CD 08 01 CXP. CALL EXTERNAL PROCEDURE: 01 IN SEGMENT: 08
50EC B9 0E UJP. JUMP TO 50FC
50EE AC (00)53 00 55 00 B9 06 TABLE: 1A 00 0F 00 16 00
XJP. W1,W2,W3,<TABLE>
50FC 00 SLDC. PUSH #0000
50FD A1 F6 UJP. IF NOT (TOS) THEN JUMP TO 50D6
50FF 1C SLDC. PUSH #001C
5100 9E 16 DUSE. DEC USE CNT FOR SEG #(TOS)
5102 B9 05 UJP. JUMP TO 5109
5104 1C SLDC. PUSH #001C
5105 9E 15 RSEG. READ SEGMENT #(TOS) FROM ACTIVE SEG TABLE
5107 B9 F4 UJP. JUMP TO 50CE
5109 C1 00 RBP. RETURN FROM BASE PROCEDURE.
510B 00 SLDC. PUSH #0000
510C 3E SLDC. PUSH #003E
510D 00 SLDC. PUSH #0000
510E 38 SLDC. PUSH #0038
510F 00 SLDC. PUSH #0000

What is the following? What is #001C? And Procedure 01?
50CC B9 36 UJP. JUMP TO 5104
50CE CD 1C 01 CXP. CALL EXTERNAL PROCEDURE: 01 IN SEGMENT: 1C

5104 1C SLDC. PUSH #001C
5105 9E 15 RSEG. READ SEGMENT #(TOS) FROM ACTIVE SEG TABLE

Ok, LOL has the same construct. Oh yeah! INTRINSICS.

See Apple Pascal Language, p. 78, "The Initialization Part of a UNIT":
"The resulting code runs automatically when the host program is executed, before the program is run."

In Language Reference Addendum is a discussion about SEGMENT NUMBERS and their assignment:
"The segment number of an Intrinsic UNIT segment....can be found by examining the segment dictionary of the SYSTEM.LIBRARY file with the LIBMAP utility program."

Looking back a the LIBMAP output earlier in this post we see the following:

Segment #28:
System version = 3.0, code type is P-Code (least sig. 1st)
CHAINSTU library unit (LINKED INTRINSIC)
PROCEDURE SETCHAIN(TYTLE:STRING);

Cool! Another mystery solved! (28 = $1C). WIZBOOT therefore uses CHAINSTU (and the procedure SETCHAIN).

Lets look at more p-code:

50D3 CD 08 01 CXP. CALL EXTERNAL PROCEDURE: 01 IN SEGMENT: 08
50D6 CD 09 01 CXP. CALL EXTERNAL PROCEDURE: 01 IN SEGMENT: 09

Segment 8? Segment 9? I'm lost. There are only 4 segments in SYSTEM.STARTUP.

Time to learn more about Segments....

Apple Pascal Language, p 76-77 has a discussion about File segment numbers compared to the Execution segment numbers.
0 System
1 Program's mainline
2-6 System
7 First declared SEGMENT from program
8 Second declared SEGMENT from program
... Up to 16 (?) of these.

Ok, now we are getting somewhere.
7 = UTILS
8 = TITLELOA
9 = OPTIONS

Hmmm, I see there is 6502 code in SYSTEM.STARTUP. How do we get that code so we can examine it and "de-compile" (disassemble) it into our re-engineered code?

Did I mention this was going to be a long journey?

TommyGoog

unread,
Apr 7, 2014, 3:28:10 PM4/7/14
to
On Friday, April 4, 2014 12:20:16 PM UTC-5, TommyGoog wrote:
> ....The journey continues....

>
> Hmmm, I see there is 6502 code in SYSTEM.STARTUP. How do we get that code so we can examine it and "de-compile" (disassemble) it into our re-engineered code?
>
>
>
> Did I mention this was going to be a long journey?

I use the DeCompiler (Wiz4) to get the procedures that were written in assembly language by the authors of Wizardry. There are other ways to get the code, but this works for me!

----------

PROCESS ALL? N
FILES: 7

1 WIZARDRY.CODE
2 RTSTRP.APPLE
3 SYSTEM.PASCAL
4 SYSTEM.LIBRARY
5 SYSTEM.MISCINFO
6 WT
7 SYSTEM.STARTUP

TYPE A FILE NUMBER (OR -1) 7

SEGMENT T/S = 32/2 --- PASCAL T/S
SEGMENT T/S = 32/13 --- DOS 3.3 T/S

0 WIZBOOT
1 UTILS
2 TITLELOA
3 OPTIONS

TYPE A SEGMENT NUMBER (OR -1)

T/S FOR SEGMENT 34/8 --- PASCAL
T/S FOR SEGMENT 34/7 --- DOS

SEGMENT'S START ADDR($5000):

THERE ARE 5 PROCEDURES.


ENTER PROCEDURE # (OR -1) 2


JTAB FOR SUBROUTINE 2:
DATA SIZE: 72
PARAM SIZE: 8
EXIT AT: 52EA
ENTER AT: 511A
PROC NUMBER: 0
6502 CODE

ENTER PROCEDURE # (OR -1) -1

SEGMENT T/S = 32/2 --- PASCAL T/S
SEGMENT T/S = 32/13 --- DOS 3.3 T/S

0 WIZBOOT
1 UTILS
2 TITLELOA
3 OPTIONS

TYPE A SEGMENT NUMBER (OR -1) -1



TYPE A FILE NUMBER (OR -1) -1


]CALL-151

*511AL

511A- 68 PLA
511B- AA TAX
511C- 68 PLA
511D- A8 TAY
511E- 68 PLA
511F- 85 00 STA $00
5121- 68 PLA
5122- 85 01 STA $01
5124- 68 PLA
5125- 85 02 STA $02
5127- 68 PLA
5128- 85 03 STA $03
512A- 98 TYA
512B- 48 PHA
512C- 8A TXA
512D- 48 PHA
512E- 18 CLC
512F- A5 02 LDA $02
5131- 65 00 ADC $00
5133- 85 02 STA $02
*

----------

The DeCompiler always loads the segment at $5000 (even if you specify a different address). The values in the JTAB displayed for a procedure tells you where that procedure was loaded into memory. Then I simply use the Apple's disassembler to display the information. Since my HELLO program turned on the printer, the text can be found in PRINTER.TXT on my pc thanks to AppleWin.

I compared the code from Wiz1 to my previous 6502 code in LOL. Those routines were:
P050002 COPYPROT
P010024 PRPICCH
P010025 DRAWSCR
P010026 DRAWLINE
P010027 RANDOM
P010028 CHKKEYBD
P010030 SCRNDATA

The code at P070002 ($511A) looks much different from the code in LOL.
The code at P070003 ($52F0) looks like disk handling and might be like COPYPROT.
The code at P070005 ($54BC) is similar to a screen table in LOL at L11E2 in SCRNDATA.

Before working on WIZBOOT (SYSTEM.STARTUP, SEGMENT 0), lets take a peek at Segment UTILS.

I noticed the UTILS segment for Wizardry1 has 36 procedures. Using output from the DeCompiler, I made the following table:

ProvGrnds LofL
7.1.1 5.1.1 UTILS main No "Update" in LOL
7.1.2 5.1.2 GETREC identical
7.1.3 5.1.3 PUTREC identical
7.1.4 5.1.4 GETKEY some variable diffs
7.1.5 5.1.5 FINDFILE identical
7.1.6 5.1.6 GETCR identical
7.1.7 5.1.7 EXITUTIL SYSTEM.PASCAL vs SYSTEM.CHARSET
7.1.8 5.1.8 RDSCNTC2 identical
7.1.9 5.1.9 RDSCNTOC identical
7.1.A 5.1.A GETPASS identical
7.1.B 5.1.B RDCHARAC identical
7.1.C 5.1.C WRCHARAC identical
7.1.D 5.1.D WRICACHE variable stores seem off by 1
7.1.E 5.1.E PRESSRET identical
7.1.F 5.1.F TITLESCR identical
7.1.10 5.1.10 RTNESC identical
7.1.11 5.1.11 CHGNAMES identical
7.1.12 5.1.12 GETNAME identical
7.1.13 5.1.13 BACKUP identical
7.1.14 5.1.14 FRBACKUP identical
7.1.15 5.1.15 TOBACKUP identical
7.1.16 5.1.16 RECOVER identical
7.1.17 5.1.17 TRANSFER "MOVE CHARS BETWEEN SCENARIOS" vs
"MOVE CHARS FROM A SCENARIO"
"TO LEGACY OF LLYLGAMYN ONLY!"
and other differences
7.1.18 5.1.18 REMOVCHR mostly identical, but lots more code at
end in Wiz1
7.1.19 "** CHAR HAS NON-XFERRABLE ITEMS"
7.1.1A 5.1.19 TRANBAD lots of differences in messages/conditions
7.1.1B 5.1.1A TRANGOOD differences in code. More in LOL.

The following procedures are in Wiz1, but don't exist in LOL.

7.1.1C "MAKE NEW SCENARIO DISKETTE"
7.1.1D "ERROR MAKING SCENARIO"
7.1.1E call 1D
7.1.1F "NOT SCENARIO DISK"
7.1.20 "STEP 2 - UPDATE SCENARIO SIDE"
7.1.21 "ERROR DURING UPDATE"
7.1.22 "UPDATING"
7.1.23 Read / Write Volume
7.1.24 "UPDATE DISK"

All of the procedures from 7.1.1C to 7.1.24 are in the MAKESCEN segment for LOL. Since they don't exist in Wiz1, I removed them from the code (recall that I first copied all the LOL code to the disks and I'm removing code that is not in Wiz1.) I also removed the call to MAKESCEN.

I next tried to enter some place holder "dummy" procedures so that UTILS segment would have 36 procedures.

PROCEDURE P07011B;
PROCEDURE P07011C;
PROCEDURE P07011D;
BEGIN
END;
BEGIN
END;

...and so forth.

When I compiled this code I got the message:

ERROR 399

So I looked up that error in the manual:

399: Implementation restriction

That's it! No other information about the error!

After awhile I realized I had placed the "dummy" procedures before some Segment procedures. After I moved the code it compiled cleanly.

Some random notes....

Source files are so large that they must be split so that the editor can load them. This makes it very difficult to do simple things like find and replace text throughout the code. Note also that "included" files cannot be nested.

When working with the DeCompiler, sometimes it helps to transfer the "file.CODE" by itself to a single diskette, and then run the DeCompiler and answer "Y" for all. You can do this for the original Wiz1 file and the re-engineered Wiz1 file and then easily compare the listings using Windiff.

When working with Apple Pascal, I never use the "work" files called SYSTEM.WRK.TEXT or SYSTEM.WRK.CODE. The source files for Wizardry are so large that they need to be on volumes (diskettes) by themselves.

Because of the file size problems, I had to refrain from putting comments in the re-engineered code.

I also use identifier names in the Pascal code that are only 8 characters or fewer. This helps with the file size problem, and also avoids a peculiar scoping problem that can occur in Pascal with variables having the first 8 characters the same.

I make frequent use of the "drag and drop" file to insert a diskette into the AppleWin floppy drive.

I make use of "Scroll Lock" to maximize the speed of AppleWin.

Ok, back to some more coding....

I added SEGMENT TITLELOA(), and also inserted into WIZUTIL the include (*I WIZ1E:TITLELOA *)

I now have the following:

SYSTEM.STARTUP
WIZBOOT 4 procedures
UTILS 36 procedures
TITLELOA 1 procedure
OPTIONS 1 procedure

Note I changed the name from WIZUTIL to SYSTEM.STARTUP to match Wiz1. For compiling I need "#5:SYSTEM.STARTUP." (with the period at end), and for the destination "#5:STARTUP.CODE".

Eventually I changed that to just STARTUP.TEXT and STARTUP.CODE and will make a change at the very end to SYSTEM.STARTUP. It was very confusing using the Pascal system with those other names.

In my next post, I will examine SYSTEM.STARTUP and begin re-engineering starting with the WIZBOOT main line.

Are we there yet?

TommyGoog

unread,
Apr 7, 2014, 7:13:24 PM4/7/14
to
On Monday, April 7, 2014 2:28:10 PM UTC-5, TommyGoog wrote:
> On Friday, April 4, 2014 12:20:16 PM UTC-5, TommyGoog wrote:
>
> > ....The journey continues....

> In my next post, I will examine SYSTEM.STARTUP and begin re-engineering starting with the WIZBOOT main line.

> Are we there yet?

SYSTEM.STARTUP
Segment Procedures
0 WIZBOOT 5
1 UTILS 36
2 TITLELOA 8
3 OPTIONS 1

0 WIZBOOT
proc lex start end order-in-file hierarchy
1 0 7.0.1 50CC 50FF 2 7.0.1
2 6502 7.0.2 511A 52EA 3
3 6502 7.0.3 52F0 54B6 4
4 1 7.0.4 5000 50BA 1 7.0.4
5 6502 7.0.5 54BC 5642 5

I copied SYSTEM.STARTUP from the original Wizardry1 to a blank diskette and ran the DeCompiler and saved the output. I did the same for my STARTUP.CODE that is under construction. I compared the two using Windiff and noticed a lot of the code was the same, but most of the addresses on the left of each line were different. Therefore Windiff reported differences for most almost every line.

This is probably a good place to show a good use for the DeCompiler prompt:

SEGMENT'S START ADDR($5000):

It can be used to help make the output code for the "under construction" version to match more closely with the original. The reason that addresses are different at this point is because I don't have all the code written for the "under construction" version so the addresses relative to the segment load address are smaller than they will be when I have completed the "under construction" version. For example,

Original version:

FILE: 1 SEG: 1 PROC: 1

JTAB FOR SUBROUTINE 1:
DATA SIZE: 1538
PARAM SIZE: 0
EXIT AT: 6B77
ENTER AT: 6A32
PROC NUMBER: 1
LEXICAL LEVEL: 1
6A32 D7 NOP. NOP
6A33 A6 12 57 49 5A 41 52 44 52 59 20 55 54 49 4C 49 54 49 45 53
WIZARDRY UTILITIES
LSA. PUSH #(PC+1) POINTER TO THE STRING
6A47 CE 0F CLP. CALL CHILD PROCEDURE: 0F

Under Construction version:
FILE: 1 SEG: 1 PROC: 1

JTAB FOR SUBROUTINE 1:
DATA SIZE: 1536
PARAM SIZE: 0
EXIT AT: 63DB
ENTER AT: 62A2
PROC NUMBER: 1
LEXICAL LEVEL: 1
62A2 D7 NOP. NOP
62A3 A6 12 57 49 5A 41 52 44 52 59 20 55 54 49 4C 49 54 49 45 53
WIZARDRY UTILITIES
LSA. PUSH #(PC+1) POINTER TO THE STRING
62B7 CE 0F CLP. CALL CHILD PROCEDURE: 0F

The code matches at 6A32 in the original version with that at 62A3. Note the difference is $6A32 - $62A2 = $790. So now when the DeCompiler prompts for "SEGMENT'S START ADDR($5000):" we answer that with "5790" and can see the addresses are the same:

SEGMENT'S START ADDR($5000): 5790

THERE ARE 36 PROCEDURES.


ENTER PROCEDURE # (OR -1) 1


JTAB FOR SUBROUTINE 1:
DATA SIZE: 1536
PARAM SIZE: 0
EXIT AT: 6B6B
ENTER AT: 6A32
PROC NUMBER: 1
LEXICAL LEVEL: 1
6A32 D7 NOP. NOP
6A33 A6 12 57 49 5A 41 52 44 52 59 20 55 54 49 4C 49 54 49 45 53
WIZARDRY UTILITIES
LSA. PUSH #(PC+1) POINTER TO THE STRING
6A47 CE 0F CLP. CALL CHILD PROCEDURE: 0F


By the way, if you don't have something like Windiff, I've found that using Notepad can be used to detect differences in two text files under comparison. You open each ".txt" file in a separate Notepad and maximize both of them. Then you quickly use ALT-TAB to toggle between the two text files and use your eyes to detect any differences. This works great when most of the text is already identical but there might be a few differences. Use the "Page Down" key to advance each one and repeat. Sometimes one will have an extra line in it, so you just adjust that notepad window one line. Rinse and repeat.

A couple more side notes...

My EDIT.DSK can be used to edit or compile code, but not to link or assemble it. The disk COMPILE.DSK can be used to link or assemble code but not to edit.

The "(*$L PRINTER: *)" switch in the source code sends print to the printer when the code is compiled.

In looking at the code for P070001 (WIZBOOT mainline), it becomes clear that the code is something like the following:

STARTUP.TEXT
PROGRAM WIZBOOT;
SEGMENT PROCEDURE UTILS;
SEGMENT PROCEDURE TITLELOA;
SEGMENT PROCEDURE OPTIONS;

PROCEDURE P070002; (* 6502 code *)
PROCEDURE P070003; (* 6502 code *)
PROCEDURE P070004;

BEGIN (* WIZBOOT *)
CALL CHILD 4
CALL PROC 01 SEG 08
REPEAT
CALL PROC 01 SEG 09
50D9
CASE statement
CALL CHILD 4
EXIT PROCEDURE
CALL PROC 01 SEG 07
CALL PROC 01 SEG 08
50EE
UNTIL FALSE;
END.

Let's compare that to the LOL WIZUTIL code:

1230 1 1:0 0 BEGIN (* WIZUTIL *)
1231 1 1:1 0 WRITE( CHR( ESC) );
1232 1 1:1 13 WRITE( CHR( 21) );
1233 1 1:1 21 IF NOT CHKCOPY THEN
1234 1 1:2 28 BEGIN
1235 1 1:3 28 IF NOT CHKCOPY THEN
1236 1 1:4 35 REPEAT UNTIL FALSE
1237 1 1:2 35 END;
1238 1 1:1 38 SVSERIAL;
1239 1 1:1 40 REPEAT
1240 1 1:2 40 OPTIONS;
1241 1 1:2 43 CASE INCHAR OF
1242 1 1:2 46 'S': BEGIN
1243 1 1:4 46 SETCHAIN( 'WIZARDRY');
1244 1 1:4 60 EXIT( WIZBOOT);
1245 1 1:3 64 END;
1246 1 1:3 66
1247 1 1:2 66 'U': BEGIN
1248 1 1:4 66 REPEAT
1249 1 1:5 66 UTILS;
1250 1 1:5 69 IF BASE04 > 0 THEN
1251 1 1:6 74 MAKESCEN
1252 1 1:4 74 UNTIL BASE04 = 0
1253 1 1:3 78 END;
1254 1 1:2 84 END;
1255 1 1:1 98 UNTIL FALSE
1256 1 1:0 98 END.

I will note that in Wizardry1, the range from $53 to $55 on the case statement denotes options "S", "T", and "U".

Wizardry1, P010201 has a SETCHAIN("WIZARDRY"), and is entered by "CALL PROC 01 SEG 08" as shown in the DeCompiler listing.

"CHILD 4" looks like it might be something like "CHKCOPY".

"GLOBAL PROCEDURE: 03" might be "COPYPROT", because the code at 52F0 is definitely accessing the disk drive

5308- A2 60 LDX #$60
530A- BD 8A C0 LDA $C08A,X
530D- BD 89 C0 LDA $C089,X
5310- BD 8E C0 LDA $C08E,X
5313- BD 8C C0 LDA $C08C,X

And now let's just review how Wizardry I actually starts before getting to re-engineering the code.

When booting:

PREPARE YOURSELF
FOR THE ULTIMATE
IN FANTASY GAMES

Then,

Caldron, pipe player, smoke animation, and WIZARDRY logo with sword through it.

At some point there is a check for keypress, so press a key and:

COPYRIGHT @1981...
more legal stuff...
...
VERSION 2.1 of 22-JAN-82 SER: ??????
S)TART GAME U)TILITIES T)ITLE PAGE

[Note, since "SER: ??????" is displayed, clearly not all of my Wizardry disk matches the one at ASIMOV.]

So let's just take a quick stab at the code and see how we do:

CHKCOPY; (* as a PROCEDURE and not as a FUNCTION *)
TITLELOA; (* Load title *)
REPEAT
OPTIONS; (* Get a key *)
CASE INCHAR OF
'S': BEGIN
CHKCOPY; (* CHKCOPY might be bad name as it
does SETCHAIN('WIZARDRY') *)
EXIT( WIZBOOT)
END;
'U': BEGIN
UTILS
END;
'T': BEGIN
TITLELOA
END;
END
UNTIL FALSE


This actually turned out pretty good when I ran the Compiler and then the DeCompiler.
I noticed the DATA SIZE: was 14 instead of 12 (I think BASE04 needs to disappear.)
And I noticed the child procedure displayed by the DeCompiler was 03 and not 04, therefore I need to insert another 6502 External procedure.

Ok, I'm happy with WIZBOOT main line code.

I will tackle CHKCOPY in the next post.


TommyGoog

unread,
Apr 11, 2014, 2:12:32 PM4/11/14
to
> > >I will tackle CHKCOPY in the next post.

Maybe CHKCOPY from LOL will look similar to CHKCOPY for Wiz1.

CHKCOPY from LOL:

BEGIN
UNITREAD( DRIVE1, BUFFER, SIZEOF( BUFFER), SERIALBL, 0);
MOVELEFT( BUFFER, SERIAL, 7);
CHKCOPY := TRUE;
UNITREAD( DRIVE1, BUFFER, SIZEOF( BUFFER), 278, 0);
IF NOT COPYPROT THEN
BEGIN
CHKCOPY := COPYPROT
END
END;

The first part of LOL CHKCOPY is this:

5000 04 SLDC. PUSH #0004
5001 C6 03 LLA. PUSH #MP.03
5003 00 SLDC. PUSH #0000
5004 C7 00 02 LDCI. PUSH #0200
5007 05 SLDC. PUSH #0005
5008 00 SLDC. PUSH #0000
5009 9E 05 READ FROM VOL#

The first part of CHKCOPY( P050003) from Wiz1:

5000 04 SLDC. PUSH #0004
5001 C6 0A LLA. PUSH #MP.0A
5003 00 SLDC. PUSH #0000
5004 C7 00 02 LDCI. PUSH #0200
5007 05 SLDC. PUSH #0005
5008 00 SLDC. PUSH #0000
5009 9E 05 READ FROM VOL#

Clearly this code is generated by a UNITREAD and is similar to LOL. In Wiz1, the buffer is #MP.0A (not #MP.01). This means we need more variables declared before BUFFER in Wiz1. SERIALBL (#0005) is the block on the disk being read. Normally that block is part of a normal PASCAL directory, but for Wizardry it holds the Wizardry "SER: xxxxxx" identifier.

Since SVSERIAL code does not seem to be used, I will remove it for Wiz1.

I insert MP01, MP02, ..., MP05 as INTEGERs into CHKCOPY, and MP06 as ARRAY[ 0..3] OF INTEGER.

The first instruction is decompiled as:

UNITREAD( DRIVE1, BUFFER, SIZEOF( BUFFER), SERIALBL, 0);

The next pcode is:

500B C6 0A LLA. PUSH #MP.0A
500D 00 SLDC. PUSH #0000
500E A5 05 LA0. PUSH #BASE.05
5010 00 SLDC. PUSH #0000
5011 07 SLDC. PUSH #0007
5012 9E 02 MVL. MOVELEFT
5014 1F SLDC. PUSH #001F
5015 CC 04 STL. MP.04 := (TOS)

This converts to:

MOVELEFT( BUFFER, SERIAL, 7);
MP04 := 31;

The next piece of code is a FOR construct:

5017 0A SLDC. PUSH #000A
5018 CC 03 STL. MP.03 := (TOS)
501A 0D SLDC. PUSH #000D
501B CC 81 0A STL. MP.010A := (TOS)
501E DA SLDL. PUSH MP.03
501F CA 81 0A LDL. PUSH MP.010A
5022 C8 LEQI. PUSH ((TOS-1) <= (TOS))
5023 A1 27 UJP. IF NOT (TOS) THEN JUMP TO 504C

5045 DA SLDL. PUSH MP.03
5046 01 SLDC. PUSH #0001
5047 82 ADI. PUSH ((TOS) + (TOS-1))
5048 CC 03 STL. MP.03 := (TOS)
504A B9 F6 UJP. JUMP TO 501E

So let's put it in:

FOR MP03 := 10 to 13 DO
BEGIN
END;

Note the variable MP.010A is automatically generated by the compiler as the FOR loop termination variable. You will see MP.010A re-used later in the code for another FOR loop following this one.

The first instruction in the FOR loop is a MOVELEFT instruction:

5025 C6 0A LLA. PUSH #MP.0A
5027 DB SLDL. PUSH MP.04
5028 C6 06 LLA. PUSH #MP.06
502A DA SLDL. PUSH MP.03
502B 0A SLDC. PUSH #000A
502C 95 SBI. PUSH ((TOS-1) - (TOS))
502D A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
502F 00 SLDC. PUSH #0000
5030 02 SLDC. PUSH #0002
5031 9E 02 MVL. MOVELEFT

Decompiled it is:

MOVELEFT( MP0A[MP04], MP06[MP03-10], 2);

Note, MP0A is a PACKED ARRAY of CHAR. The pcode had me confused at first into thinking there were 4 parameters passed to MOVELEFT, but this is the correct PASCAL code to generate the pcode.

The next sequence of pcode looks intimidating at first. Here it is:

5033 DB SLDL. PUSH MP.04
5034 02 SLDC. PUSH #0002
5035 C6 06 LLA. PUSH #MP.06
5037 DA SLDL. PUSH MP.03
5038 0A SLDC. PUSH #000A
5039 95 SBI. PUSH ((TOS-1) - (TOS))
503A A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
503C F8 SIND. PUSH (TOS)^.0
503D 0D SLDC. PUSH #000D
503E 8E MODI. PUSH ((TOS-1) MOD (TOS))
503F 8F MPI. PUSH ((TOS-1) * (TOS))
5040 82 ADI. PUSH ((TOS) + (TOS-1))
5041 05 SLDC. PUSH #0005
5042 82 ADI. PUSH ((TOS) + (TOS-1))
5043 CC 04 STL. MP.04 := (TOS)

De-Compiled it is:

MP04 := MP04 + 2 * ((MP06[MP03 - 10]) MOD 13) + 5;

This instruction is also within the FOR loop.

This is what the code looks like so far, and I will often compile a PROCEDURE or FUNCTION before it is complete to make sure I am on the right track:


966 1 2:D 3 FUNCTION COPYPROT : BOOLEAN; EXTERNAL; (* P050002 *)
967 1 2:D 3
968 1 2:D 3
969 1 3:D 1 PROCEDURE CHKCOPY; (* P050003 *)
970 1 3:D 1
971 1 3:D 1 VAR
972 1 3:D 1 MP01 : INTEGER;
973 1 3:D 2 MP02 : INTEGER;
974 1 3:D 3 MP03 : INTEGER;
975 1 3:D 4 MP04 : INTEGER;
976 1 3:D 5 MP05 : INTEGER;
977 1 3:D 6 MP06 : ARRAY[0..3] OF INTEGER;
978 1 3:D 10 BUFFER : PACKED ARRAY[ TBLOCKRG] OF CHAR;
979 1 3:D 266
980 1 3:0 0 BEGIN
981 1 3:1 0 UNITREAD( DRIVE1, BUFFER, SIZEOF( BUFFER), SERIALBL, 0);
982 1 3:1 11 MOVELEFT( BUFFER, SERIAL, 7);
983 1 3:1 20 MP04 := 31;
984 1 3:1 23 FOR MP03 := 10 TO 13 DO
985 1 3:2 37 BEGIN
986 1 3:3 37 MOVELEFT( BUFFER[ MP04], MP06[ MP03 - 10], 2);
987 1 3:3 51 MP04 := MP04 + 2 * ((MP06[MP03 - 10]) MOD 13) + 5;
988 1 3:2 69 END;


After the entire routine is completed (and maybe a lot later) I will clean up the variables to something meaningful.

Here is the pcode for the rest of CHKCOPY:

504C 01 SLDC. PUSH #0001
504D CC 02 STL. MP.02 := (TOS)
504F 05 SLDC. PUSH #0005
5050 CC 81 0A STL. MP.010A := (TOS)
5053 D9 SLDL. PUSH MP.02
5054 CA 81 0A LDL. PUSH MP.010A
5057 C8 LEQI. PUSH ((TOS-1) <= (TOS))
5058 A1 5E UJP. IF NOT (TOS) THEN JUMP TO 50B8
505A 01 SLDC. PUSH #0001
505B CC 05 STL. MP.05 := (TOS)
505D 0A SLDC. PUSH #000A
505E CC 03 STL. MP.03 := (TOS)
5060 0D SLDC. PUSH #000D
5061 CC 81 0B STL. MP.010B := (TOS)
5064 DA SLDL. PUSH MP.03
5065 CA 81 0B LDL. PUSH MP.010B
5068 C8 LEQI. PUSH ((TOS-1) <= (TOS))
5069 A1 3F UJP. IF NOT (TOS) THEN JUMP TO 50AA
506B 04 SLDC. PUSH #0004
506C C6 0A LLA. PUSH #MP.0A
506E 00 SLDC. PUSH #0000
506F C7 00 02 LDCI. PUSH #0200
5072 08 SLDC. PUSH #0008
5073 DA SLDL. PUSH MP.03
5074 8F MPI. PUSH ((TOS-1) * (TOS))
5075 00 SLDC. PUSH #0000
5076 9E 05 READ FROM VOL#
5078 C6 04 LLA. PUSH #MP.04
507A CF 03 CGP. CALL GLOBAL PROCEDURE: 03
507C DA SLDL. PUSH MP.03
507D 0A SLDC. PUSH #000A
507E C3 EQUI. PUSH ((TOS-1) = (TOS))
507F A1 0C UJP. IF NOT (TOS) THEN JUMP TO 508D
5081 DB SLDL. PUSH MP.04
5082 C6 06 LLA. PUSH #MP.06
5084 0A SLDC. PUSH #000A
5085 0A SLDC. PUSH #000A
5086 95 SBI. PUSH ((TOS-1) - (TOS))
5087 A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
5089 F8 SIND. PUSH (TOS)^.0
508A 95 SBI. PUSH ((TOS-1) - (TOS))
508B CC 01 STL. MP.01 := (TOS)
508D DB SLDL. PUSH MP.04
508E D8 SLDL. PUSH MP.01
508F 95 SBI. PUSH ((TOS-1) - (TOS))
5090 CC 04 STL. MP.04 := (TOS)
5092 DC SLDL. PUSH MP.05
5093 DB SLDL. PUSH MP.04
5094 C6 06 LLA. PUSH #MP.06
5096 DA SLDL. PUSH MP.03
5097 0A SLDC. PUSH #000A
5098 95 SBI. PUSH ((TOS-1) - (TOS))
5099 A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
509B F8 SIND. PUSH (TOS)^.0
509C 95 SBI. PUSH ((TOS-1) - (TOS))
509D 80 ABI. PUSH ABS( (TOS) ) )
509E 23 SLDC. PUSH #0023
509F C9 LESI. PUSH ((TOS-1) < (TOS))
50A0 84 LAND. PUSH ((TOS-1) AND (TOS))
50A1 CC 05 STL. MP.05 := (TOS)
50A3 DA SLDL. PUSH MP.03
50A4 01 SLDC. PUSH #0001
50A5 82 ADI. PUSH ((TOS) + (TOS-1))
50A6 CC 03 STL. MP.03 := (TOS)
50A8 B9 F4 UJP. JUMP TO 5064
50AA DC SLDL. PUSH MP.05
50AB A1 04 UJP. IF NOT (TOS) THEN JUMP TO 50B1
50AD 01 SLDC. PUSH #0001
50AE 04 SLDC. PUSH #0004
50AF 9E 04 EXIT. EXIT FROM PROCEDURE. (TOS)=PROC# (TOS-1)=SEG#
50B1 D9 SLDL. PUSH MP.02
50B2 01 SLDC. PUSH #0001
50B3 82 ADI. PUSH ((TOS) + (TOS-1))
50B4 CC 02 STL. MP.02 := (TOS)
50B6 B9 F2 UJP. JUMP TO 5053
50B8 9E 27 AN UNKNOWN STANDARD PROCEDURE ?!?!
50BA AD 00 RNP. RETURN FROM NON-BASE PROCEDURE.

Here is the entire decompiled listing:

PROCEDURE CHKCOPY; (* P070004 *)

VAR
MP01 : INTEGER;
MP02 : INTEGER;
MP03 : INTEGER;
MP04 : INTEGER;
MP05 : BOOLEAN;
MP06 : ARRAY[0..3] OF INTEGER;
BUFFER : PACKED ARRAY[ TBLOCKRG] OF CHAR;

BEGIN
UNITREAD( DRIVE1, BUFFER, SIZEOF( BUFFER), SERIALBL, 0);
MOVELEFT( BUFFER, SERIAL, 7);
MP04 := 31;
FOR MP03 := 10 TO 13 DO
BEGIN
MOVELEFT( BUFFER[ MP04], MP06[ MP03 - 10], 2);
MP04 := MP04 + 2 * ((MP06[MP03 - 10]) MOD 13) + 5;
END;
FOR MP02 := 1 TO 5 DO
BEGIN
MP05 := TRUE;
FOR MP03 := 10 TO 13 DO
BEGIN
UNITREAD( DRIVE1, BUFFER, SIZEOF( BUFFER), 8 * MP03, 0);
P070003( MP04); (* CALL GLOBAL PROCEDURE: 03 *)
IF MP03 = 10 THEN
BEGIN
MP01 := MP04 - MP06[ 10 - 10];
END;
MP04 := MP04 - MP01;
MP05 :=MP05 AND (ABS((MP04 - MP06[ MP03 - 10])) < 35);
END;
IF MP05 THEN
BEGIN
EXIT( CHKCOPY);
END;
END;
HALT;
END;


Nothing too remarkable in the rest of that code conversion. You must determine that "CALL GLOBAL PROCEDURE: 03" refers to P070003 and passes MP04.

Also note the sequence of pcode at 5084:

5084 0A SLDC. PUSH #000A
5085 0A SLDC. PUSH #000A
5086 95 SBI. PUSH ((TOS-1) - (TOS))

It looks a bit odd, and the decompiled code I wrote does generate that pcode:

MP01 := MP04 - MP06[ 10 - 10];

Certainly the Wizardry programmers did not write something quite like that ("10 - 10"). I seem to remember sequences like this when doing LOL, and I *think* it has something to do with SCALAR defined constants or subranges, or perhaps something about the "ORD()" function.

I now inserted:

PROCEDURE P070005; EXTERNAL;

following P070004 (CHKCOPY). It needs to be placed there to have the correct number assigned to it by the compiler.

At first I inserted P07003 as this:

PROCEDURE P07003( MP01: INTEGER); EXTERNAL:

It generated the following pcode:

5078 DB SLDL. PUSH MP.04
5079 CF 03 CGP. CALL GLOBAL PROCEDURE: 03

But we really wanted:

5078 C6 04 LLA. PUSH #MP.04
507A CF 03 CGP. CALL GLOBAL PROCEDURE: 03

Therefor the parameter needs to be passed by REFERENCE and not by VALUE. (Put "VAR" before MP01 in declaration.)

I found that the "UNKNOWN" for "9E 27" is the PASCAL "HALT" instruction.

A lot of this code is really confusing to try to understand. I think I looked at it 2.5 years ago. To understand it you need to see at least the sector with the serial number, so here it is (using COPY][+):

00- 06 33 39 31 30 39 30 00 F391090@
08- 00 00 00 01 88 00 00 00 @@@A.@@@
10- 00 00 00 00 00 00 00 00 @@@@@@@@
18- 00 00 00 00 00 00 00 16 @@@@@@@V
20- 12 00 00 00 F0 0C D4 0F R@@@pLTO
28- C7 09 64 16 47 09 47 05 GI$VGIGE
30- 64 16 47 05 F3 06 ED 07 $VGEsFmG
38- 0F 06 12 15 12 31 12 00 OFRUR1R@
40- E5 02 B9 08 12 4B 12 79 eB9HRKR9
48- C6 05 31 13 12 78 17 1B FE1SR8W[
50- 12 06 AB 08 EA 05 12 12 RF+HjERR
58- EE 02 EC 0C 00 00 1C 13 nBlL@@\S
60- 00 12 BE 05 1C 13 E8 05 @R>E\ShE
68- B5 05 00 00 16 09 00 00 5E@@VI@@
70- 00 00 00 00 00 00 00 00 @@@@@@@@
78- 00 00 00 00 00 00 00 00 @@@@@@@@
80- 00 00 00 00 00 00 00 00 @@@@@@@@
88- 00 00 00 00 00 00 00 00 @@@@@@@@
90- 00 00 00 00 00 00 00 00 @@@@@@@@
98- 00 00 00 00 00 00 00 00 @@@@@@@@
A0- 00 00 00 00 00 00 00 00 @@@@@@@@
A8- 00 00 00 00 00 00 00 00 @@@@@@@@
B0- 00 00 00 00 00 00 00 00 @@@@@@@@
B8- 00 00 00 00 00 00 00 00 @@@@@@@@
C0- 00 00 00 00 00 00 00 00 @@@@@@@@
C8- 00 00 00 00 00 00 00 00 @@@@@@@@
D0- 00 00 00 00 00 00 00 00 @@@@@@@@
D8- 00 00 00 00 00 00 00 00 @@@@@@@@
E0- 00 00 00 00 00 00 00 00 @@@@@@@@
E8- 00 00 00 00 00 00 00 00 @@@@@@@@
F0- 00 00 00 00 00 00 00 00 @@@@@@@@
F8- 00 00 00 00 00 00 00 00 @@@@@@@@

Note that MP04 := 31 is looking at $1F. Also note the serial number at the beginning of this sector. This came from my "box" set of Wizardry. The Serial# is: 391090.

One other thing I feel I must mention is the "EXIT" instruction. This is used here (and in LOL) and I think it is generally a bad idea to use this construct. In this routine, EXIT is the "normal" way to return to the caller. YUCK!

Next up is the procedure P070003 which is 6502 assembly code. I need to relearn a bunch more stuff before tackling that.

TommyGoog

unread,
Apr 12, 2014, 4:58:27 PM4/12/14
to

> Next up is the procedure P070003 which is 6502 assembly code. I need to relearn a bunch more stuff before tackling that.

SEGMENT: Procedures:
0 WIZBOOT 5
1 UTILS 36
2 TITLELOA 8
3 OPTIONS 1

WIZBOOT lex Start Exit Order Hierarchy
1 0 50CC 50FF 2 7.0.1
2 6502 code 511A 52EA 3
3 6502 code 52F0 54B6 4
4 1 5000 50BA 1 7.0.4
5 6502 code 54BC 5642 5

There are 3 PROCEDUREs/FUNCTIONs in SEGMENT WIZBOOT that are 6502 code (written in assembler).

I mentioned in an earlier post how to get an Apple "monitor" disassembly for Pascal 6502 procedures.

P070003
52F0- 68 PLA
52F1- 85 00 STA $00
52F3- 68 PLA
52F4- 85 01 STA $01
52F6- 68 PLA
52F7- 85 02 STA $02
52F9- 68 PLA
52FA- 85 03 STA $03
52FC- A5 01 LDA $01
52FE- 48 PHA
52FF- A5 00 LDA $00
5301- 48 PHA
5302- A9 00 LDA #$00
5304- 85 0D STA $0D
5306- 85 0E STA $0E
5308- A2 60 LDX #$60
530A- BD 8A C0 LDA $C08A,X
530D- BD 89 C0 LDA $C089,X
5310- BD 8E C0 LDA $C08E,X
5313- BD 8C C0 LDA $C08C,X
5316- 10 FB BPL $5313
5318- C9 FF CMP #$FF
531A- D0 F7 BNE $5313
531C- BD 8C C0 LDA $C08C,X
531F- 10 FB BPL $531C
5321- C9 FF CMP #$FF
5323- D0 EE BNE $5313
5325- BD 8C C0 LDA $C08C,X
5328- 10 FB BPL $5325
532A- C9 FF CMP #$FF
532C- D0 03 BNE $5331
532E- 4C 35 00 JMP $0035
5331- BD 8C C0 LDA $C08C,X
5334- 10 FB BPL $5331
5336- C9 D5 CMP #$D5
5338- D0 F7 BNE $5331
533A- BD 8C C0 LDA $C08C,X
533D- 10 FB BPL $533A
533F- C9 AA CMP #$AA
5341- D0 EE BNE $5331
5343- BD 8C C0 LDA $C08C,X
5346- 10 FB BPL $5343
5348- C9 96 CMP #$96
534A- D0 E5 BNE $5331
534C- BD 8C C0 LDA $C08C,X
534F- 10 FB BPL $534C
5351- EA NOP
5352- EA NOP
5353- EA NOP
5354- EA NOP
5355- EA NOP
5356- BD 8C C0 LDA $C08C,X
5359- 10 FB BPL $5356
535B- EA NOP
535C- EA NOP
535D- EA NOP
535E- EA NOP
535F- EA NOP
5360- BD 8C C0 LDA $C08C,X
5363- 10 FB BPL $5360
5365- EA NOP
5366- EA NOP
5367- EA NOP
5368- EA NOP
5369- EA NOP
536A- BD 8C C0 LDA $C08C,X
536D- 10 FB BPL $536A
536F- EA NOP
5370- EA NOP
5371- EA NOP
5372- EA NOP
5373- EA NOP
5374- BD 8C C0 LDA $C08C,X
5377- 10 FB BPL $5374
5379- A8 TAY
537A- EA NOP
537B- EA NOP
537C- EA NOP
537D- EA NOP
537E- EA NOP
537F- BD 8C C0 LDA $C08C,X
5382- 10 FB BPL $537F
5384- 85 04 STA $04
5386- 98 TYA
5387- 38 SEC
5388- 2A ROL
5389- 25 04 AND $04
538B- C9 00 CMP #$00
538D- D0 84 BNE $5313
538F- 20 1B 01 JSR $011B
5392- BD 8C C0 LDA $C08C,X
5395- 10 FB BPL $5392
5397- C9 D5 CMP #$D5
5399- F0 09 BEQ $53A4
539B- E6 0D INC $0D
539D- D0 F3 BNE $5392
539F- E6 0E INC $0E
53A1- 4C A2 00 JMP $00A2
53A4- BD 8C C0 LDA $C08C,X
53A7- 10 FB BPL $53A4
53A9- C9 AA CMP #$AA
53AB- D0 E5 BNE $5392
53AD- BD 8C C0 LDA $C08C,X
53B0- 10 FB BPL $53AD
53B2- C9 96 CMP #$96
53B4- D0 DC BNE $5392
53B6- BD 8C C0 LDA $C08C,X
53B9- 10 FB BPL $53B6
53BB- EA NOP
53BC- EA NOP
53BD- EA NOP
53BE- EA NOP
53BF- EA NOP
53C0- BD 8C C0 LDA $C08C,X
53C3- 10 FB BPL $53C0
53C5- EA NOP
53C6- EA NOP
53C7- EA NOP
53C8- EA NOP
53C9- EA NOP
53CA- BD 8C C0 LDA $C08C,X
53CD- 10 FB BPL $53CA
53CF- EA NOP
53D0- EA NOP
53D1- EA NOP
53D2- EA NOP
53D3- EA NOP
53D4- BD 8C C0 LDA $C08C,X
53D7- 10 FB BPL $53D4
53D9- EA NOP
53DA- EA NOP
53DB- EA NOP
53DC- EA NOP
53DD- EA NOP
53DE- BD 8C C0 LDA $C08C,X
53E1- 10 FB BPL $53DE
53E3- A8 TAY
53E4- EA NOP
53E5- EA NOP
53E6- EA NOP
53E7- EA NOP
53E8- EA NOP
53E9- BD 8C C0 LDA $C08C,X
53EC- 10 FB BPL $53E9
53EE- 85 04 STA $04
53F0- 98 TYA
53F1- 38 SEC
53F2- 2A ROL
53F3- 25 04 AND $04
53F5- C9 00 CMP #$00
53F7- F0 03 BEQ $53FC
53F9- 4C A2 00 JMP $00A2
53FC- A0 00 LDY #$00
53FE- A5 0D LDA $0D
5400- 91 02 STA ($02),Y
5402- C8 INY
5403- A5 0E LDA $0E
5405- 91 02 STA ($02),Y
5407- BD 88 C0 LDA $C088,X
540A- 60 RTS

540B- 86 06 STX $06
540D- A0 0C LDY #$0C
540F- 84 07 STY $07
5411- 88 DEY
5412- 88 DEY
5413- 84 08 STY $08
5415- A9 EF LDA #$EF
5417- 85 0B STA $0B
5419- A9 D8 LDA #$D8
541B- 85 0C STA $0C
541D- A9 00 LDA #$00
541F- 85 09 STA $09
5421- A5 08 LDA $08
5423- 85 0A STA $0A
5425- 38 SEC
5426- E5 07 SBC $07
5428- F0 31 BEQ $545B
542A- B0 06 BCS $5432
542C- 49 FF EOR #$FF
542E- E6 08 INC $08
5430- 90 04 BCC $5436
5432- 69 FE ADC #$FE
5434- C6 08 DEC $08
5436- C5 09 CMP $09
5438- 90 02 BCC $543C
543A- A5 09 LDA $09
543C- C9 0C CMP #$0C
543E- B0 01 BCS $5441
5440- A8 TAY
5441- 38 SEC
5442- 20 6F 01 JSR $016F
5445- B9 91 01 LDA $0191,Y
5448- 20 80 01 JSR $0180
544B- A5 0A LDA $0A
544D- 18 CLC
544E- 20 71 01 JSR $0171
5451- B9 9D 01 LDA $019D,Y
5454- 20 80 01 JSR $0180
5457- E6 09 INC $09
5459- D0 C6 BNE $5421
545B- 20 80 01 JSR $0180
545E- 18 CLC
545F- A5 08 LDA $08
5461- 29 03 AND #$03
5463- 2A ROL
5464- 05 06 ORA $06
5466- AA TAX
5467- BD 80 C0 LDA $C080,X
546A- A6 06 LDX $06
546C- 60 RTS

546D- AA TAX
546E- A0 A0 LDY #$A0
5470- A2 11 LDX #$11
5472- CA DEX
5473- D0 FD BNE $5472
5475- E6 0B INC $0B
5477- D0 02 BNE $547B
5479- E6 0C INC $0C
547B- 38 SEC
547C- E9 01 SBC #$01
547E- D0 F0 BNE $5470
5480- 60 RTS

5481- 01 30 ORA ($30,X)
5483- 28 PLP
5484- 20 20 1E JSR $1E20
5487- 1D 1C 1C ORA $1C1C,X
548A- 1C ???
548B- 1C ???
548C- 1C ???
548D- 70 2C BVS $54BB
548F- 26 22 ROL $22
5491- 1F ???
5492- 1E 1D 1C ASL $1C1D,X
5495- 1C ???
5496- 1C ???
5497- 1C ???
5498- 1C ???
5499- 00 BRK
549A- 00 BRK
549B- 00 BRK
549C- 6D 01 0E ADC $0E01
549F- 01 FE ORA ($FE,X)
54A1- 00 BRK
54A2- A8 TAY
54A3- 00 BRK
54A4- 61 00 ADC ($00,X)
54A6- 60 RTS
54A7- 00 BRK
54A8- 5F ???
54A9- 00 BRK
54AA- 5B ???
54AB- 00 BRK
54AC- 5A ???
54AD- 00 BRK
54AE- 59 00 54 EOR $5400,Y
54B1- 00 BRK
54B2- 0B ???
54B3- 00 BRK
54B4- 00 BRK
54B5- 00 BRK
54B6- 00 BRK
54B7- 00 BRK
54B8- C8 INY
54B9- 01 00 ORA ($00,X)
54BB- 00 BRK

This code as loaded at $511A is the code from the Wizardry1 diskette. Note though that it is not yet ready for execution. The Assembler, Linker, and runtime Segment Loader need to coordinate a few things. The Segment Loader can load the segment at different locations in memory. One time it might load it at $5000, and another time at $5502. Therefore a JMP instruction cannot have the same destination address in both places if the JMP is to a location somewhere else in this code.

The Assembler updates the JTAB "Optional Jump Table" for this procedure. (See Apple Pascal Operating System, Operation of P-Machine, P 253). When the Segment Loader loads the segment, it goes through this Jump Table and alters the JMP (and JSR) instructions.

Let's look at an example from the code above for P070003:

545B- 20 80 01 JSR $0180

The JTAB begins at 54BB and is "upside down" as described in the manual.

Also, 2 consecutive addresses are generally grouped in pairs forming a 16 bit value. The lower address of the pair is considered the address of that table entry, and the lower address contains the least significant bits of the 16 bit value ($54B8 is the address with a value of $01C8).

54BB 0
54BA 0 JTAB
54B9 1
54B8 C8 Enter IC
54B7 0
54B6 0
54B5 0
54B4 0
54B3 0
54B2 0B Number of JMP entries in table
54B1 0
54B0 54
54AF 0
54AE 59

Before I get too far, a word about those pointers. They are self-relative pointers. You take the value that is stored there, and subtract it from its address to get the ABSOLUTE address. For example, the Enter IC is at $54B8 and it has the value $01C8. When this segment is loaded at $5000, this is therefore referring to $52F0 ($54B8 - $01C8).

Another example, $54B0 - $0054 = $545C, and we find a JSR instruction there:

545B- 20 80 01 JSR $0180

Ok, so now we see $0180 as the destination. That is a relative pointer using the beginning of the procedure. $5470 ($52F0 + $0180).

For starters we can call the label "L0180, and have JSR L0180.

If there is a data table in the procedure, then the references to it will also need to be ABSOLUTIZED (resolved is probably the proper term to use).

5445- B9 91 01 LDA $0191,Y

$52F0 + $0191 = $5481

Therefore this is " LDA L0191,Y" and the table at $5481 has the label L0191.

Ok, so now we can convert this to source code, but we note that Apple Pascal does have a few differences:

All number must begin with 0 through 9, so "$C08C" must be written as "0C08C".

Some instructions need the operand. ROL must be written as "ROL A".

STA ($02),Y becomes STA @02,Y

I had some problems during my first attempts at Assembling the code, that I thought I'd share with you so that you do not make the same mistakes.

A(ssemble
Assemble what text? #11:P070003
To what codefile? #11:P070003
Output file for assembled listing: #11:P070003.OUT
"bad title, illegal file name"

Don't give the file for output an extension.
And if you try P070003 with no extension you will not be happy. It will overwrite your source file!!

Output file for assembled listing: #11:P07OUT
"no room, insufficient space on disk"

But this is my current disk usage:

#4: 194 unused
#5: 38 unused
#11: 194 unused
#12: 160 unused

Ok, let's try putting the output listing on a different disk:

Output file for assembled listing: #12:POUT
...
Assembly complete: 38 lines
0 Errors flagged on this Assembly

Ok, that worked.

Here is an alternate way of fixing the problem:
Put "size" for Code file:

To what codefile? #11:p070003.CODE[10]
Output file for assembled listing: #11:pout
...
Assembly complete: 38 lines
0 Errors flagged on this Assembly

Now its time to Link P070003.CODE into STARTUP.CODE

L(inking
Host file? #12:STARTUP
Libfile? #11:P070003.CODE
Reading WIZBOOT
Reading P070003
Output file? #11:STARTUP.CODE

Linking UTILS #7
Linking TITLELOA #8
Linking OPTIONS #9
Linking WIZBOOT #1
Proc P070005 undefined
Func COPYPROT undefined

Since there were errors, no .CODE was produced. Therefore, make a dummy P070005 and dummy COPYPROT.

Host file? #12:STARTUP
Libfile?
#11:P070003.CODE
#11:P070005.CODE
#11:COPYPROT.CODE
Output file? #11:STARTUP

In looking at the DeCompiled code for this STARTUP.CODE, I noticed that PROCEDURE 03 started at a smaller address than $52F0, therefore I need to put more data in PROCEDURE 02. Also worth noting is that the order you enter Lib Files is important.

Libfile?
#11:COPYPROT.CODE
#11:P070003.CODE
#11:P070005.CODE

Here is the completed rough draft for P070003

0000| .PROC P070003,1 ; 1 PARAMETER
Current memory available: 8644
0000| 68 PLA
0001| 85 00 STA 00
0003| 68 PLA
0004| 85 01 STA 01
0006| 68 PLA
0007| 85 02 STA 02
0009| 68 PLA
000A| 85 03 STA 03
000C| A5 01 LDA 01
000E| 48 PHA
000F| A5 00 LDA 00
0011| 48 PHA
0012| A9 00 LDA #0
0014| 85 0D STA 0D
0016| 85 0E STA 0E
0018| A2 60 LDX #060
001A| BD 8AC0 LDA 0C08A,X
001D| BD 89C0 LDA 0C089,X
0020| BD 8EC0 LDA 0C08E,X
0023| L5313
0023| BD 8CC0 LDA 0C08C,X
0026| 10FB BPL L5313
0028| C9 FF CMP #0FF
002A| D0F7 BNE L5313
002C| L531C
002C| BD 8CC0 LDA 0C08C,X
002F| 10FB BPL L531C
0031| C9 FF CMP #0FF
0033| D0EE BNE L5313
0035| L0035
0035| BD 8CC0 LDA 0C08C,X
0038| 10FB BPL L0035
003A| C9 FF CMP #0FF
003C| D0** BNE L5331
003E| 4C 3500 JMP L0035
003C* 03
0041| L5331
0041| BD 8CC0 LDA 0C08C,X
0044| 10FB BPL L5331
0046| C9 D5 CMP#0D5
0048| D0F7 BNE L5331
004A| L533A
004A| BD 8CC0 LDA 0C08C,X
004D| 10FB BPL L533A
004F| C9 AA CMP #0AA
0051| D0EE BNE L5331
0053| L5343
0053| BD 8CC0 LDA 0C08C,X
0056| 10FB BPL L5343
0058| C9 96 CMP #096
005A| D0E5 BNE L5331
005C| L534C
005C| BD 8CC0 LDA 0C08C,X
PAGE - 2 P070003 FILE:


005F| 10FB BPL L534C
0061| EA NOP
0062| EA NOP
0063| EA NOP
0064| EA NOP
0065| EA NOP
0066| L5356
0066| BD 8CC0 LDA 0C08C,X
0069| 10FB BPL L5356
006B| EA NOP
006C| EA NOP
006D| EA NOP
006E| EA NOP
006F| EA NOP
0070| L5360
0070| BD 8CC0 LDA 0C08C,X
0073| 10FB BPL L5360
0075| EA NOP
0076| EA NOP
0077| EA NOP
0078| EA NOP
0079| EA NOP
007A| L536A
007A| BD 8CC0 LDA 0C08C,X
007D| 10FB BPL L536A
007F| EA NOP
0080| EA NOP
0081| EA NOP
0082| EA NOP
0083| EA NOP
0084| L5374
0084| BD 8CC0 LDA 0C08C,X
0087| 10FB BPL L5374
0089| A8 TAY
008A| EA NOP
008B| EA NOP
008C| EA NOP
008D| EA NOP
008E| EA NOP
008F| L537F
008F| BD 8CC0 LDA 0C08C,X
0092| 10FB BPL L537F
0094| 85 04 STA 04
0096| 98 TYA
0097| 38 SEC
0098| 2A ROL A
0099| 25 04 AND 04
009B| C9 00 CMP #00
009D| D084 BNE L5313
009F| 20 **** JSR L011B
00A2| L00A2
00A2| L5392
00A2| BD 8CC0 LDA 0C08C,X
00A5| 10FB BPL L5392
00A7| C9 D5 CMP #0D5
PAGE - 3 P070003 FILE:


00A9| F0** BEQ L53A4
00AB| E6 0D INC 0D
00AD| D0F3 BNE L5392
00AF| E6 0E INC 0E
00B1| 4C A200 JMP L00A2
00A9* 09
00B4| L53A4
00B4| BD 8CC0 LDA 0C08C,X
00B7| 10FB BPL L53A4
00B9| C9 AA CMP #0AA
00BB| D0E5 BNE L5392
00BD| L53AD
00BD| BD 8CC0 LDA 0C08C,X
00C0| 10FB BPL L53AD
00C2| C9 96 CMP #096
00C4| D0DC BNE L5392
00C6| L53B6
00C6| BD 8CC0 LDA 0C08C,X
00C9| 10FB BPL L53B6
00CB| EA NOP
00CC| EA NOP
00CD| EA NOP
00CE| EA NOP
00CF| EA NOP
00D0| L53C0
00D0| BD 8CC0 LDA 0C08C,X
00D3| 10FB BPL L53C0
00D5| EA NOP
00D6| EA NOP
00D7| EA NOP
00D8| EA NOP
00D9| EA NOP
00DA| L53CA
00DA| BD 8CC0 LDA 0C08C,X
00DD| 10FB BPL L53CA
00DF| EA NOP
00E0| EA NOP
00E1| EA NOP
00E2| EA NOP
00E3| EA NOP
00E4| L53D4
00E4| BD 8CC0 LDA 0C08C,X
00E7| 10FB BPL L53D4
00E9| EA NOP
00EA| EA NOP
00EB| EA NOP
00EC| EA NOP
00ED| EA NOP
00EE| L53DE
00EE| BD 8CC0 LDA 0C08C,X
00F1| 10FB BPL L53DE
00F3| A8 TAY
00F4| EA NOP
00F5| EA NOP
00F6| EA NOP
PAGE - 4 P070003 FILE:


00F7| EA NOP
00F8| EA NOP
00F9| L53E9
00F9| BD 8CC0 LDA 0C08C,X
00FC| 10FB BPL L53E9
00FE| 85 04 STA 04
0100| 98 TYA
0101| 38 SEC
0102| 2A ROL A
0103| 25 04 AND 04
0105| C9 00 CMP #0
0107| F0** BEQ L53FC
0109| 4C A200 JMP L00A2
0107* 03
010C| L53FC
010C| A0 00 LDY #0
010E| A5 0D LDA 0D
0110| 91 02 STA @02,Y
0112| C8 INY
0113| A5 0E LDA 0E
0115| 91 02 STA @02,Y
0117| BD 88C0 LDA 0C088,X
011A| 60 RTS
011B| ;
00A0* 1B01
011B| L011B
011B| 86 06 STX 06
011D| A0 0C LDY #0C
011F| 84 07 STY 07
0121| 88 DEY
0122| 88 DEY
0123| 84 08 STY 08
0125| A9 EF LDA #0EF
0127| 85 0B STA 0B
0129| A9 D8 LDA #0D8
012B| 85 0C STA 0C
012D| A9 00 LDA #0
012F| 85 09 STA 09
0131| L5421
0131| A5 08 LDA 08
0133| 85 0A STA 0A
0135| 38 SEC
0136| E5 07 SBC 07
0138| F0** BEQ L545B
013A| B0** BCS L5432
013C| 49 FF EOR #0FF
013E| E6 08 INC 08
0140| 90** BCC L5436
013A* 06
0142| L5432
0142| 69 FE ADC #0FE
0144| C6 08 DEC 08
0140* 04
0146| L5436
0146| C5 09 CMP 09
PAGE - 5 P070003 FILE:


0148| 90** BCC L543C
014A| A5 09 LDA 09
0148* 02
014C| L543C
014C| C9 0C CMP #0C
014E| B0** BCS L5441
0150| A8 TAY
014E* 01
0151| L5441
0151| 38 SEC
0152| 20 **** JSR L016F
0155| B9 **** LDA L0191,Y
0158| 20 **** JSR L0180
015B| A5 0A LDA 0A
015D| 18 CLC
015E| 20 **** JSR L0171 ; NOT RESOLVED
0161| B9 **** LDA L019D,Y ; NOT RESOLVED
0164| 20 **** JSR L0180 ; NOT RESOLVED
0167| E6 09 INC 09
0169| D0C6 BNE L5421
0138* 31
016B| L545B
016B| 20 **** JSR L0180 ; NOT RESOLVED
016E| 18 CLC
0153* 6F01
016F| L016F
016F| A5 08 LDA 08
015F* 7101
0171| L0171
0171| 29 03 AND #03
0173| 2A ROL A
0174| 05 06 ORA 06
0176| AA TAX
0177| BD 80C0 LDA 0C080,X
017A| A6 06 LDX 06
017C| 60 RTS
017D| ;
017D| AA TAX
017E| A0 A0 LDY #0A0
016C* 8001
0165* 8001
0159* 8001
0180| L0180
0180| L5470
0180| A2 11 LDX #011
0182| L5472
0182| CA DEX
0183| D0FD BNE L5472
0185| E6 0B INC 0B
0187| D0** BNE L547B
0189| E6 0C INC 0C
0187* 02
018B| L547B
018B| 38 SEC
018C| E9 01 SBC #01
PAGE - 6 P070003 FILE:


018E| D0F0 BNE L5470
0190| 60 RTS
0156* 9101
0191| 01 30 28 20 20 1E 1D L0191 .BYTE 001,030,028,020,020,01E,01D,01C,01C,01C,01C,01C
0198| 1C 1C 1C 1C 1C
0162* 9D01
019D| 70 2C 26 22 1F 1E 1D L019D .BYTE 070,02C,026,022,01F,01E,01D,01C,01C,01C,01C,01C
01A4| 1C 1C 1C 1C 1C
01A9| 00 .BYTE 000
01AA| .END
PAGE - 7 P070003 FILE: SYMBOLTABLE DUMP


AB - Absolute LB - Label UD - Undefined MC - Macro
RF - Ref DF - Def PR - Proc FC - Func
PB - Public PV - Private CS - Consts


L0035 LB 0035| L00A2 LB 00A2| L011B LB 011B| L016F LB 016F| L0171 LB 0171| L0180 LB 0180| L0191 LB 0191
L019D LB 019D| L5313 LB 0023| L531C LB 002C| L5331 LB 0041| L533A LB 004A| L5343 LB 0053| L534C LB 005C
L5356 LB 0066| L5360 LB 0070| L536A LB 007A| L5374 LB 0084| L537F LB 008F| L5392 LB 00A2| L53A4 LB 00B4
L53AD LB 00BD| L53B6 LB 00C6| L53C0 LB 00D0| L53CA LB 00DA| L53D4 LB 00E4| L53DE LB 00EE| L53E9 LB 00F9
L53FC LB 010C| L5421 LB 0131| L5432 LB 0142| L5436 LB 0146| L543C LB 014C| L5441 LB 0151| L545B LB 016B
L5470 LB 0180| L5472 LB 0182| L547B LB 018B| P070003 PR ----|
PAGE - 8 P070003 FILE:


Current minimum space is 8305 words

Assembly complete: 267 lines
0 Errors flagged on this Assembly

I just noticed something a bit unusual in the code. Either I made a mistake or the original coders left some "dead code" in there:

546D- AA TAX
546E- A0 A0 LDY #$A0


Next up is P070002 (COPYPROT)






TommyGoog

unread,
Apr 14, 2014, 10:35:15 PM4/14/14
to

> Next up is P070002 (COPYPROT)

P070002 (COPYPROT)

Right now COPYPROT is just a placeholder routine with " .BLOCK 1C8,2".

I'm not sure what COPYPROT does, so for now I'll rename it to P070002.

Here is how I currently build STARTUP.CODE:

Host file?
#12:STARTUP
Libfile?
#5:P070002.CODE
#5:P070003.CODE
#5:P070005.CODE
Output file?
#5:STARTUP

From the DeCompiler we see:

Start
P070002 $511A
P070003 $52F0

Note, the DeCompiler has a bug if P070002 is not already LINKED into the executable. It displays lots of garbage.

We do the trick with the DeCompiler to get P070002 (from Wiz1 Boot disk) into memory and use Apples monitor to display the code:

]CALL-151

*511ALLLLLLLLLL

511A- 68 PLA
511B- AA TAX
511C- 68 PLA
511D- A8 TAY
511E- 68 PLA
511F- 85 00 STA $00
5121- 68 PLA
5122- 85 01 STA $01
5124- 68 PLA
5125- 85 02 STA $02
5127- 68 PLA
5128- 85 03 STA $03
512A- 98 TYA
512B- 48 PHA
512C- 8A TXA
512D- 48 PHA
512E- 18 CLC
512F- A5 02 LDA $02
5131- 65 00 ADC $00
5133- 85 02 STA $02
5135- A5 03 LDA $03
5137- 65 01 ADC $01
5139- 85 03 STA $03
513B- A2 00 LDX #$00
513D- A9 03 LDA #$03
513F- 0D 1C BF ORA $BF1C
5142- 8D 1C BF STA $BF1C
5145- A1 02 LDA ($02,X)
5147- C9 C0 CMP #$C0
5149- 90 36 BCC $5181
514B- C9 FD CMP #$FD
514D- 90 03 BCC $5152
514F- 4C 32 01 JMP $0132
5152- 38 SEC
5153- E9 C0 SBC #$C0
5155- 85 07 STA $07
5157- A0 02 LDY #$02
5159- B1 02 LDA ($02),Y
515B- 85 06 STA $06
515D- 88 DEY
515E- B1 02 LDA ($02),Y
5160- A8 TAY
5161- B9 BC 04 LDA $04BC,Y
5164- 85 04 STA $04
5166- B9 7C 05 LDA $057C,Y
5169- 85 05 STA $05
516B- A4 07 LDY $07
516D- A5 06 LDA $06
516F- 91 04 STA ($04),Y
5171- 18 CLC
5172- A5 02 LDA $02
5174- 69 03 ADC #$03
5176- 85 02 STA $02
5178- A9 00 LDA #$00
517A- 65 03 ADC $03
517C- 85 03 STA $03
517E- 4C 2B 00 JMP $002B
5181- A8 TAY
5182- B9 BC 04 LDA $04BC,Y
5185- 85 04 STA $04
5187- B9 7C 05 LDA $057C,Y
518A- 85 05 STA $05
518C- A0 01 LDY #$01
518E- B1 02 LDA ($02),Y
5190- 85 06 STA $06
5192- C8 INY
5193- B1 02 LDA ($02),Y
5195- 85 07 STA $07
5197- C8 INY
5198- B1 02 LDA ($02),Y
519A- 85 08 STA $08
519C- C8 INY
519D- B1 02 LDA ($02),Y
519F- 85 09 STA $09
51A1- C8 INY
51A2- B1 02 LDA ($02),Y
51A4- 85 0A STA $0A
51A6- 18 CLC
51A7- A5 02 LDA $02
51A9- 69 06 ADC #$06
51AB- 85 02 STA $02
51AD- A5 03 LDA $03
51AF- 69 00 ADC #$00
51B1- 85 03 STA $03
51B3- A0 00 LDY #$00
51B5- A5 06 LDA $06
51B7- 20 B7 00 JSR $00B7
51BA- A5 07 LDA $07
51BC- 20 B7 00 JSR $00B7
51BF- A5 08 LDA $08
51C1- 20 B7 00 JSR $00B7
51C4- A5 09 LDA $09
51C6- 20 B7 00 JSR $00B7
51C9- A5 0A LDA $0A
51CB- 20 B7 00 JSR $00B7
51CE- 4C 2B 00 JMP $002B
51D1- 85 00 STA $00
51D3- 46 00 LSR $00
51D5- 90 0A BCC $51E1
51D7- A1 02 LDA ($02,X)
51D9- 91 04 STA ($04),Y
51DB- E6 02 INC $02
51DD- D0 02 BNE $51E1
51DF- E6 03 INC $03
51E1- C8 INY
51E2- 46 00 LSR $00
51E4- 90 0A BCC $51F0
51E6- A1 02 LDA ($02,X)
51E8- 91 04 STA ($04),Y
51EA- E6 02 INC $02
51EC- D0 02 BNE $51F0
51EE- E6 03 INC $03
51F0- C8 INY
51F1- 46 00 LSR $00
51F3- 90 0A BCC $51FF
51F5- A1 02 LDA ($02,X)
51F7- 91 04 STA ($04),Y
51F9- E6 02 INC $02
51FB- D0 02 BNE $51FF
51FD- E6 03 INC $03
51FF- C8 INY
5200- 46 00 LSR $00
5202- 90 0A BCC $520E
5204- A1 02 LDA ($02,X)
5206- 91 04 STA ($04),Y
5208- E6 02 INC $02
520A- D0 02 BNE $520E
520C- E6 03 INC $03
520E- C8 INY
520F- 46 00 LSR $00
5211- 90 0A BCC $521D
5213- A1 02 LDA ($02,X)
5215- 91 04 STA ($04),Y
5217- E6 02 INC $02
5219- D0 02 BNE $521D
521B- E6 03 INC $03
521D- C8 INY
521E- 46 00 LSR $00
5220- 90 0A BCC $522C
5222- A1 02 LDA ($02,X)
5224- 91 04 STA ($04),Y
5226- E6 02 INC $02
5228- D0 02 BNE $522C
522A- E6 03 INC $03
522C- C8 INY
522D- 46 00 LSR $00
522F- 90 0A BCC $523B
5231- A1 02 LDA ($02,X)
5233- 91 04 STA ($04),Y
5235- E6 02 INC $02
5237- D0 02 BNE $523B
5239- E6 03 INC $03
523B- C8 INY
523C- 46 00 LSR $00
523E- 90 0A BCC $524A
5240- A1 02 LDA ($02,X)
5242- 91 04 STA ($04),Y
5244- E6 02 INC $02
5246- D0 02 BNE $524A
5248- E6 03 INC $03
524A- C8 INY
524B- 60 RTS
524C- C9 FE CMP #$FE
524E- F0 05 BEQ $5255
5250- C9 FD CMP #$FD
5252- F0 10 BEQ $5264
5254- 60 RTS
5255- A9 30 LDA #$30
5257- 85 09 STA $09
5259- 20 77 01 JSR $0177
525C- C6 09 DEC $09
525E- D0 F9 BNE $5259
5260- 20 77 01 JSR $0177
5263- 60 RTS
5264- A9 30 LDA #$30
5266- 85 09 STA $09
5268- 20 59 01 JSR $0159
526B- C6 09 DEC $09
526D- D0 F9 BNE $5268
526F- 20 59 01 JSR $0159
5272- 60 RTS
5273- A4 09 LDY $09
5275- B9 BC 04 LDA $04BC,Y
5278- 85 04 STA $04
527A- B9 7C 05 LDA $057C,Y
527D- 85 05 STA $05
527F- A0 27 LDY #$27
5281- B1 04 LDA ($04),Y
5283- 49 80 EOR #$80
5285- 91 04 STA ($04),Y
5287- 88 DEY
5288- D0 F7 BNE $5281
528A- B1 04 LDA ($04),Y
528C- 49 80 EOR #$80
528E- 91 04 STA ($04),Y
5290- 60 RTS
5291- 18 CLC
5292- A4 09 LDY $09
5294- B9 BC 04 LDA $04BC,Y
5297- 85 04 STA $04
5299- 69 01 ADC #$01
529B- 85 06 STA $06
529D- B9 7C 05 LDA $057C,Y
52A0- 85 05 STA $05
52A2- 69 00 ADC #$00
52A4- 85 07 STA $07
52A6- A0 00 LDY #$00
52A8- B1 04 LDA ($04),Y
52AA- 85 08 STA $08
52AC- A2 27 LDX #$27
52AE- B1 06 LDA ($06),Y
52B0- 91 04 STA ($04),Y
52B2- C8 INY
52B3- CA DEX
52B4- D0 F8 BNE $52AE
52B6- A5 08 LDA $08
52B8- 91 04 STA ($04),Y
52BA- 60 RTS
52BB- 00 BRK
52BC- 00 BRK
52BD- 00 BRK
52BE- 6E 01 41 ROR $4101
52C1- 01 0A ORA ($0A,X)
52C3- 01 07 ORA ($07,X)
52C5- 01 04 ORA ($04,X)
52C7- 01 01 ORA ($01,X)
52C9- 01 FE ORA ($FE,X)
52CB- 00 BRK
52CC- FD 00 74 SBC $7400,X
52CF- 00 BRK
52D0- 6F ???
52D1- 00 BRK
52D2- 69 00 ADC #$00
52D4- 64 ???
52D5- 00 BRK
52D6- 0C ???
52D7- 00 BRK
52D8- 76 01 ROR $01,X
52DA- 73 ???
52DB- 01 59 ORA ($59,X)
52DD- 01 56 ORA ($56,X)
52DF- 01 6A ORA ($6A,X)
52E1- 00 BRK
52E2- 67 ???
52E3- 00 BRK
52E4- 4F ???
52E5- 00 BRK
52E6- 48 PHA
52E7- 00 BRK
52E8- 08 PHP
52E9- 00 BRK
52EA- 00 BRK
52EB- 00 BRK
52EC- D2 ???
52ED- 01 00 ORA ($00,X)
52EF- 00 BRK

The JTAB begins at the bottom, and we see the self-referencing pointer $01D2 to the start of the code.

This routine has a mystery to solve. Look at:

513F- 0D 1C BF ORA $BF1C

$BF1C? Isn't that part of the Pascal operating system? (Yes!)

Using Google and the internet, I found these references:

Apple II Pascal ATTACH.pdf at ASIMOV (SPCHAR at location BF1C)
Apple II Computer Technical Information
ATTACH-BIOS Documentation for Apple II Pascal 1.1
by Barry Haynes, Apple Computer Inc. -- January 12, 1980

Somewhere in those documents you will find:

BF1C
00 Means do all special character checking
01 Means don't check for Apple screen stuff
02 Means don't check for other screen stuff

3 types of characters:
A. ctrl-A, Z, W, E, K
B. ctrl-S, F
C. ctrl-C, DLE, CR

SPCHAR bit 0 set to 1 === don't look for type A chars
SPCHAR bit 1 set to 1 === don't look for type B chars
When set, special char checking is turned OFF until reboot.

Ok, looks like we will need the following:

SPCHAR .EQU 0BF1C;

On to the next problem:

5161- B9 BC 04 LDA $04BC,Y

Using the Segment load address of $5000 to ABSOLUTIZE this address puts it at $54BC, which isn't in this procedure!

Looking back at my WIZBOOT chart, I think we have our answer:

WIZBOOT
lex Start End Order-in-File
1 0 7.0.1 50CC 50FF 2
2 6502 7.0.2 511A 52EA 3
3 6502 7.0.3 52F0 54B6 4
4 1 7.0.4 5000 50BA 1
5 6502 7.0.5 54BC 5642 5

$54BC is the start of the "code" for P070005.
Earlier I made this observation, and it seems to be true:

"The code at P070005 ($54BC) is similar to a screen table in LOL at L11E2 in SCRNDATA."

To access data in another 6502 procedure, you use the ".REF" instruction:

.REF L04BC, L057C

The JMP and JSR addresses and labels are determined in the same manner as was done with P070003.

I now Assembled and Linked P070005 and got the following errors:

Global L04BC undefined
Global L057c undefined

P070005 will define those 2 labels. P070005 is entirely data.

This is P070005 (Note the ".DEF" instructions to "externalize" those labels.):

.PROC P070005 ; P070003 USES THESE TABLES
;
.DEF L04BC, L057C
;
L54BC
L04BC
.BYTE 000,000,000,000,000,000,000,000
.BYTE 080,080,080,080,080,080,080,080
.BYTE 000,000,000,000,000,000,000,000
.BYTE 080,080,080,080,080,080,080,080
.BYTE 000,000,000,000,000,000,000,000
.BYTE 080,080,080,080,080,080,080,080
.BYTE 000,000,000,000,000,000,000,000
.BYTE 080,080,080,080,080,080,080,080
L54FC
.BYTE 028,028,028,028,028,028,028,028
.BYTE 0A8,0A8,0A8,0A8,0A8,0A8,0A8,0A8
.BYTE 028,028,028,028,028,028,028,028
.BYTE 0A8,0A8,0A8,0A8,0A8,0A8,0A8,0A8
.BYTE 028,028,028,028,028,028,028,028
.BYTE 0A8,0A8,0A8,0A8,0A8,0A8,0A8,0A8
.BYTE 028,028,028,028,028,028,028,028
.BYTE 0A8,0A8,0A8,0A8,0A8,0A8,0A8,0A8
L553C
.BYTE 050,050,050,050,050,050,050,050
.BYTE 0D0,0D0,0D0,0D0,0D0,0D0,0D0,0D0
.BYTE 050,050,050,050,050,050,050,050
.BYTE 0D0,0D0,0D0,0D0,0D0,0D0,0D0,0D0
.BYTE 050,050,050,050,050,050,050,050
.BYTE 0D0,0D0,0D0,0D0,0D0,0D0,0D0,0D0
.BYTE 050,050,050,050,050,050,050,050
.BYTE 0D0,0D0,0D0,0D0,0D0,0D0,0D0,0D0
L557C
L057C
.BYTE 020,024,028,02C,030,034,038,03C
.BYTE 020,024,028,02C,030,034,038,03C
.BYTE 021,025,029,02D,031,035,039,03D
.BYTE 021,025,029,02D,031,035,039,03D
.BYTE 022,026,02A,02E,032,036,03A,03E
.BYTE 022,026,02A,02E,032,036,03A,03E
.BYTE 023,027,02B,02F,033,037,03B,03F
.BYTE 023,027,02B,02F,033,037,03B,03F
L55BC
.BYTE 020,024,028,02C,030,034,038,03C
.BYTE 020,024,028,02C,030,034,038,03C
.BYTE 021,025,029,02D,031,035,039,03D
.BYTE 021,025,029,02D,031,035,039,03D
.BYTE 022,026,02A,02E,032,036,03A,03E
.BYTE 022,026,02A,02E,032,036,03A,03E
.BYTE 023,027,02B,02F,033,037,03B,03F
.BYTE 023,027,02B,02F,033,037,03B,03F
L55FC
.BYTE 020,024,028,02C,030,034,038,03C
.BYTE 020,024,028,02C,030,034,038,03C
.BYTE 021,025,029,02D,031,035,039,03D
.BYTE 021,025,029,02D,031,035,039,03D
.BYTE 022,026,02A,02E,032,036,03A,03E
.BYTE 022,026,02A,02E,032,036,03A,03E
.BYTE 023,027,02B,02F,033,037,03B,03F
.BYTE 023,027,02B,02F,033,037,03B,03F
;
.END

Recall also that hex numbers must begin with 0 - 9. To get them lined up I made each of them 3 digits.

Here is the final rough draft for P070002:

.PROC P070002,2 ; WITH 2 PARAMETERS PASSED INTO (FORMERLY COPYPROT)
;
.REF L04BC, L057C
;
SPCHAR .EQU 0BF1C ; SEE APPLE II COMPUTER TECHNICAL INFORMATION
;
PLA
TAX
PLA
TAY
PLA
STA 00
PLA
STA 01
PLA
STA 02
PLA
STA 03
TYA
PHA
TXA
PHA
CLC
LDA 02
ADC 00
STA 02
LDA 03
ADC 01
STA 03
LDX #00
LDA #03
ORA SPCHAR ;0BF1C ...HMMM....PART OF PASCAL SYSTEM TABLES???
STA SPCHAR ;0BF1C
L5145
L002B
LDA @02,X
CMP #0C0
BCC L5181
CMP #0FD
BCC L5152
JMP L0132 ; L524C
L5152
SEC
SBC #0C0
STA 07
LDY #02
LDA @02,Y
STA 06
DEY
LDA @02,Y
TAY
LDA L04BC,Y ; LOOKS UNRESOLVED ??? MAYBE P070005 STARTS AT $54BC
STA 04
LDA L057C,Y
STA 05
LDY 07
LDA 06
STA @04,Y
CLC
LDA 02
ADC #03
STA 02
LDA #00
ADC 03
STA 03
JMP L002B ; UNRESOLVED L5145 L51D1
L5181
TAY
LDA L04BC,Y ; ???
STA 04
LDA L057C,Y
STA 05
LDY #01
LDA @02,Y
STA 06
INY
LDA @02,Y
STA 07
INY
LDA @02,Y
STA 08
INY
LDA @02,Y
STA 09
INY
LDA @02,Y
STA 0A
CLC
LDA 02
ADC #06
STA 02
LDA 03
ADC #00
STA 03
LDY #00
LDA 06
JSR L00B7 ; UNRESOLVED L51D1
LDA 07
JSR L00B7
LDA 08
JSR L00B7
LDA 09
JSR L00B7
LDA 0A
JSR L00B7
JMP L002B ; UNRESOLVED L5145
L51D1
L00B7
STA 00
LSR 00
BCC L51E1
LDA @02,X
STA @04,Y
INC 02
BNE L51E1
INC 03
L51E1
INY
LSR 00
BCC L51F0
LDA @02,X
STA @04,Y
INC 02
BNE L51F0
INC 03
L51F0
INY
LSR 00
BCC L51FF
LDA @02,X
STA @04,Y
INC 02
BNE L51FF
INC 03
L51FF
INY
LSR 00
BCC L520E
LDA @02,X
STA @04,Y
INC 02
BNE L520E
INC 03
L520E
INY
LSR 00
BCC L521D
LDA @02,X
STA @04,Y
INC 02
BNE L521D
INC 03
L521D
INY
LSR 00
BCC L522C
LDA @02,X
STA @04,Y
INC 02
BNE L522C
INC 03
L522C
INY
LSR 00
BCC L523B
LDA @02,X
STA @04,Y
INC 02
BNE L523B
INC 03
L523B
INY
LSR 00
BCC L524A
LDA @02,X
STA @04,Y
INC 02
BNE L524A
INC 03
L524A
INY
RTS
L524C
L0132
CMP #0FE
BEQ L5255
CMP #0FD
BEQ L5264
RTS
L5255
LDA #030
STA 09
L5259
JSR L0177 ; UNRESOLVED L5291
DEC 09
BNE L5259
JSR L0177 ; UNRESOLVED
RTS
L5264
LDA #030
STA 09
L5268
JSR L0159 ; UNRESOLVED L5273
DEC 09
BNE L5268
JSR L0159 ; UNRESOLVED
RTS
L5273
L0159
LDY 09
LDA L04BC,Y ; ???
STA 04
LDA L057C,Y ; ???
STA 05
LDY #027
L5281
LDA @04,Y
EOR #080
STA @04,Y
DEY
BNE L5281
LDA @04,Y
EOR #080
STA @04,Y
RTS
L5291
L0177
CLC
LDY 09
LDA L04BC,Y ; ???
STA 04
ADC #01
STA 06
LDA L057C,Y ; ???
STA 05
ADC #00
STA 07
LDY #00
LDA @04,Y
STA 08
LDX #027
L52AE
LDA @06,Y
STA @04,Y
INY
DEX
BNE L52AE
LDA 08
STA @04,Y
RTS
;
L52BB ;
;
.END
RTS
.END

I was now able to Compile and Link all of WIZBOOT segment in STARTUP.CODE.

STARTUP.TEXT
Mainline (P070001)
CHKCOPY (P070004)
P070002.TEXT (Check keyboard (?). Accesses BF1C SPCHAR)
P070003.TEXT (Disk I/O, and uses P070005 Screen Data ptrs)
P070005.TEXT (Screen Data ptrs)

Using the Call -151 trick after running DeCompiler, I compared my new WIZBOOT segment with the original and found them to be identical. ($5000 to $
564B).

YAY! I've successfully re-enginneered the first Segment for STARTUP.CODE (SYSTEM.STARTUP)!

(Ok, there's still lots of work to do to clean up variable names, etc.)

Up next is TITLELOA, which is executed from SYSTEM.STARTUP mainline right after CHKCOPY is first called.

TommyGoog

unread,
Apr 15, 2014, 6:50:32 PM4/15/14
to

> Up next is TITLELOA, which is executed from SYSTEM.STARTUP mainline right after CHKCOPY is first called.

TITLELOA FILE: 7 SEG: 2 PROC: 1

lex start end order hierarchy
1 1 7.2.1 531E 5378 8 7.2.1
2 2 7.2.2 5000 500C 1 7.2.2
3 2 7.2.3 5018 504F 2 7.2.3
4 2 7.2.4 50D8 52BD 6 7.2.4
5 3 7.2.5 505C 5077 3 7.2.5
6 3 7.2.6 5086 50A2 4 7.2.6
7 3 7.2.7 50AE 50CC 5 7.2.7
8 2 7.2.8 52DE 530C 7 7.2.8

STARTUP.TEXT
$I WIZUTILB
$I WIZUTILC
$I TITLELOA
$I OPTIONS

Using DeCompiler on Wiz1 Boot diskette:

JTAB FOR SUBROUTINE 1:
DATA SIZE: 9222
PARAM SIZE: 0
EXIT AT: 5378
ENTER AT: 531E
PROC NUMBER: 1
LEXICAL LEVEL: 1
531E D7 NOP. NOP
531F A6 08 57 49 5A 41 52 44 52 59
WIZARDRY
LSA. PUSH #(PC+1) POINTER TO THE STRING
5329 CD 1C 02 CXP. CALL EXTERNAL PROCEDURE: 02 IN SEGMENT: 1C
532C CE 08 CLP. CALL CHILD PROCEDURE: 08
532E C6 02 LLA. PUSH #MP.02
5330 9E 20 MRK. MARK HEAP. (TOS)^ := NP
5332 C7 00 40 LDCI. PUSH #4000
5335 CC 92 03 STL. MP.1203 := (TOS)
5338 C6 92 03 LLA. PUSH #MP.1203
533B 9E 21 RLS. RELEASE HEAP. NP := (TOS)^
533D C7 FD 00 LDCI. PUSH #00FD
5340 CC 01 STL. MP.01 := (TOS)
5342 CE 04 CLP. CALL CHILD PROCEDURE: 04
5344 D8 SLDL. PUSH MP.01
5345 C7 FD 00 LDCI. PUSH #00FD
5348 C3 EQUI. PUSH ((TOS-1) = (TOS))
5349 A1 07 UJP. IF NOT (TOS) THEN JUMP TO 5352
534B C7 FE 00 LDCI. PUSH #00FE
534E CC 01 STL. MP.01 := (TOS)
5350 B9 05 UJP. JUMP TO 5357
5352 C7 FD 00 LDCI. PUSH #00FD
5355 CC 01 STL. MP.01 := (TOS)
5357 C7 AC 3F LDCI. PUSH #3FAC
535A 91 NGI. PUSH -(TOS) (2'S COMP)
535B CE 02 CLP. CALL CHILD PROCEDURE: 02
535D C7 AF 3F LDCI. PUSH #3FAF
5360 91 NGI. PUSH -(TOS) (2'S COMP)
5361 CE 02 CLP. CALL CHILD PROCEDURE: 02
5363 00 SLDC. PUSH #0000
5364 00 SLDC. PUSH #0000
5365 CE 03 CLP. CALL CHILD PROCEDURE: 03
5367 A1 F6 UJP. IF NOT (TOS) THEN JUMP TO 5342
5369 B6 02 03 LOD. PUSH ACTREC(-02).03
536C 20 SLDC. PUSH #0020
536D 00 SLDC. PUSH #0000
536E CD 00 11 CXP. CALL EXTERNAL PROCEDURE: 11 IN SEGMENT: 00
5371 01 SLDC. PUSH #0001
5372 9E 26 CHECK VOL#
5374 C6 02 LLA. PUSH #MP.02
5376 9E 21 RLS. RELEASE HEAP. NP := (TOS)^
5378 AD 00 RNP. RETURN FROM NON-BASE PROCEDURE.
537A 38 SLDC. PUSH #0038
537B 00 SLDC. PUSH #0000


The first pcode is:

531F A6 08 57 49 5A 41 52 44 52 59
WIZARDRY
LSA. PUSH #(PC+1) POINTER TO THE STRING
5329 CD 1C 02 CXP. CALL EXTERNAL PROCEDURE: 02 IN SEGMENT: 1C

Recall that "SEGMENT: 1C" is SETCHAIN in the SYSTEM.LIBRARY.

So this converts to:

SETCHAIN( 'WIZARDRY');

Next code is:

532C CE 08 CLP. CALL CHILD PROCEDURE: 08

This converts to:
P070208;

Oh yeah, lets set up dummy procedures for TITLELOA in the hierarchy that I have in the table earlier.

Next I will compile this little bit to make sure we are on the right path. I will then look at it with the DeCompiler.

Do I need to Link in the 6502 code? Can I put those 6502 procedures in SYSTEM.LIBRARY and then always use SYSTEM.LIBRARY instead of knowing the names and order to Link files in? At this time, the 6502 procedures don't seem to be needed for what I am looking at.

Next up:

532E C6 02 LLA. PUSH #MP.02
5330 9E 20 MRK. MARK HEAP. (TOS)^ := NP
5332 C7 00 40 LDCI. PUSH #4000
5335 CC 92 03 STL. MP.1203 := (TOS)
5338 C6 92 03 LLA. PUSH #MP.1203
533B 9E 21 RLS. RELEASE HEAP. NP := (TOS)^

$4000 is the start of Hi-Res page 2, just above page 1. I think this code is protecting Hi-res Page 1 from being used for heap space. I will have a lot more to say about memory management and the heap when looking at the Wizardry main code.

The Pascal code is:

MARK( MP02);
MP1203 := 16384; (* $4000 *)
RELEASE( MP1203);

MP1203 indicates this routine has a lot of memory used for VARiables.

For now I'll try this:

MP01 : INTEGER
MP02 : ^INTEGER;
MP03 : ARRAY[ 1..3000] OF INTEGER;
MP1203 : ^INTEGER;

That failed because MP02 is used as 2 different variable TYPEs: INTEGER and POINTER.

Oh yeah! Time to relearn VARIANT RECORDs.

BYTE = PACKED ARRAY [0..1] OF 0..255;
MAGIC = RECORD CASE BOOLEAN OF
FALSE : (I : INTEGER);
TRUE : (P : ^BYTE);
END;

Here is the completed rough draft:

SEGMENT PROCEDURE TITLELOA;
TYPE

BYTE = PACKED ARRAY [ 0..1] OF 0..255;

MAGIC = RECORD CASE BOOLEAN OF
FALSE : (I : INTEGER);
TRUE : (P : ^BYTE);
END;

VAR

MP01 : INTEGER;
MP02 : ^INTEGER;
MP03 : ARRAY[ 1..4608] OF INTEGER;
MP1203 : MAGIC;

PROCEDURE P070202( MP01 : INTEGER);
BEGIN
END;

FUNCTION P070203 : BOOLEAN;
BEGIN
END;

PROCEDURE P070204;

PROCEDURE P070205;
BEGIN
END;

PROCEDURE P070206;
BEGIN
END;

PROCEDURE P070207;
BEGIN
END;

BEGIN (* P070204 *)
END;

PROCEDURE P070208;
BEGIN
END;

BEGIN (* P070201 MAINLINE *)

SETCHAIN( 'WIZARDRY');
P070208;
MARK( MP02);
MP1203.I := 16384;
RELEASE( MP1203.P);
MP01 := 253;
REPEAT

P070204;
IF MP01 = 253 THEN
BEGIN
MP01 := 254
END
ELSE
BEGIN
MP01 := 253
END;
P070202( -16300);
P070202( -16303);

UNTIL P070203;
WRITE( CHR( 32));
UNITCLEAR( 1);
RELEASE( MP02);
END;

-16300 and -16303
These are soft switches on the Apple II.
-16300 is $C054 and displays primary page 1.
-16303 is $C051 and displays TEXT mode.

How do I know UNITCLEAR( 1) is generates " CHECK VOL#"?

I have a table that I've created to help me with "SEGMENT: 00" and other system calls. I created this table by looking at examples of other Pascal code and using the DeCompiler.

READ() Procedure: 10
EOLN() Procedure: 0B
WRITE() Procedure: 11 WRITE( CHR( 13))
WRITE() Procedure: 13 WRITE( Astr : 20);
WRITELN() Procedure: 13 and 16
WRITELN Procedure: 16
Write() Procedure: 0D WRITE( Anum : 2);
GOTOXY() Procedure: 1D
COPY() Procedure: 19
CONCAT Procedure: 17
RESET() Procedure: 05

VAR x : FILE; Procedure: 03 Declaring the file generates 2 calls.
"03" at the start of the procedure.
The parameter passed to 03 is the
file "type" (REAL, INTEGER, untyped, ...)
VAR x : FILE; Precedure: 06 "06" at the end of the procedure.

UNITREAD() READ FROM VOL# (* DEVICE, BUFF, CNT, BLOCK #, MODE *)
UNITWRITE() WRITE VOL #
UNITCLEAR( 1) CHECK VOL#

USE CHAINSTUFF Procedure: 01 in Segment: 1C
SETCHAIN() Procedure: 02 in Segment: 1C

Note, this is not a completed table yet. As I encounter more "UNKNOWNs" I will need to update it.

UNITCLEAR( 1) flushes the typeahead buffer for CONSOLE: and resets horizontal scrolling to full left.

Before every FUNCTION() call in Pascal, the pcode generates PUSH #0 and PUSH #0.



P070201 is next:

FILE: 7 SEG: 2 PROC: 2

JTAB FOR SUBROUTINE 2:
DATA SIZE: 0
PARAM SIZE: 2
EXIT AT: 500C
ENTER AT: 5000
PROC NUMBER: 2
LEXICAL LEVEL: 2
5000 D8 SLDL. PUSH MP.01
5001 B8 01 92 03 STR. ACTREC(-01).1203 := TOS
5005 B6 01 92 03 LOD. PUSH ACTREC(-01).1203
5009 00 SLDC. PUSH #0000
500A 00 SLDC. PUSH #0000
500B BF STB. (TOS-2)^.(TOS-1) := TOS BYTE
500C AD 00 RNP. RETURN FROM NON-BASE PROCEDURE.

"PARAM SIZE: 2" means the parameter occupies 2 bytes. This could be an INTEGER variable.

When looking at the DeCompiled code, when no parameters are passed, then the first variable declared is referred to as MP.01. When there are parameters, this changes. Now the first parameter is named MP.01 and the declared variables follow that one.

The LEXICAL LEVEL is 2, so this is "nested" beneath TITLELOA.

The ACTREC(-01) refers to the most recent activation record before this procedure is called. ACTREC(-01).1203 therefore refers to a declared variable in TITLELOA.

It is a PROCEDURE an not a FUNCTION, because RNP ($AD) has 00 as its parameter.

PROCEDURE P070202( MP01 : INTEGER);
BEGIN
MP1203 := MP01;
MP1203^ hey MP1203 is an INTEGER, not a pointer!
END;

Hmmm, MP1203 cannot be an INTEGER and a POINTER.
Look at the definition for MP1203. It is a variant record.
Let's try the following:

SEGMENT PROCEDURE TITLELOA;
VAR
MP1203 : MAGIC;

PROCEDURE P070202( MP01 : INTEGER);
BEGIN
MP1203.I := MP01;
MP1203.P^[ 0] := 0
END;

Side rant:

Whomever invented "E" for "EDIT" and "E" for "EXTENDED DIR" should be shot. How many times have I entered the EDITOR when I wanted an EXTENDED DIR? Maybe I should start using "L" for "DIR".

But Nooooo! "L" is for "LINKER"

Continue with P070203 in TITLELOA.

FILE: 7 SEG: 2 PROC: 3

JTAB FOR SUBROUTINE 3:
DATA SIZE: 0
PARAM SIZE: 4
EXIT AT: 504F
ENTER AT: 5018
PROC NUMBER: 3
LEXICAL LEVEL: 2
5018 01 SLDC. PUSH #0001
5019 CC 01 STL. MP.01 := (TOS)
501B C7 00 40 LDCI. PUSH #4000
501E 91 NGI. PUSH -(TOS) (2'S COMP)
501F B8 01 92 03 STR. ACTREC(-01).1203 := TOS
5023 B6 01 92 03 LOD. PUSH ACTREC(-01).1203
5027 00 SLDC. PUSH #0000
5028 BE LDB. PUSH #00, PUSH (TOS-1)^.TOS BYTE
5029 7F SLDC. PUSH #007F
502A C5 GRTI. PUSH ((TOS-1) > (TOS))
502B A1 04 UJP. IF NOT (TOS) THEN JUMP TO 5031
502D 08 SLDC. PUSH #0008
502E 03 SLDC. PUSH #0003
502F 9E 04 EXIT. EXIT FROM PROCEDURE. (TOS)=PROC# (TOS-1)=SEG#
5031 C7 E8 40 LDCI. PUSH #40E8
5034 91 NGI. PUSH -(TOS) (2'S COMP)
5035 B8 01 92 03 STR. ACTREC(-01).1203 := TOS
5039 B6 01 92 03 LOD. PUSH ACTREC(-01).1203
503D 00 SLDC. PUSH #0000
503E BE LDB. PUSH #00, PUSH (TOS-1)^.TOS BYTE
503F B6 01 92 03 LOD. PUSH ACTREC(-01).1203
5043 01 SLDC. PUSH #0001
5044 BE LDB. PUSH #00, PUSH (TOS-1)^.TOS BYTE
5045 CB NEQI. PUSH ((TOS-1) <> (TOS))
5046 A1 04 UJP. IF NOT (TOS) THEN JUMP TO 504C
5048 08 SLDC. PUSH #0008
5049 03 SLDC. PUSH #0003
504A 9E 04 EXIT. EXIT FROM PROCEDURE. (TOS)=PROC# (TOS-1)=SEG#
504C 00 SLDC. PUSH #0000
504D CC 01 STL. MP.01 := (TOS)
504F AD 01 RNP. RETURN FROM NON-BASE PROCEDURE.
5051 00 SLDC. PUSH #0000

PARAM SIZE indicates 2 parameters.
RNP with AD 01 means this is a FUNCTION
MP.01 is value of function returned.
lex level is 2, so same level as P070202.
I already have this returning BOOLEAN. Why? Because of P070201 Mainline with "UNTIL P070203; 0 = FALSE and 1 = TRUE I think.
DATA SIZE 0 means no VARs here.
$4000 is 16384
IF MP1203.P^[0] > #$7F THEN #$7F === 127
EXIT ( SEG 8, PROC 3); SEG 8 PROC 3 === P070203
MP1203.i := -16616 #$40E8 === 16616

I'm not sure, but I think the code is like the following:
IF MP1203.P^[ 0] <> MP1203.P^[ 1] THEN
EXIT (P070203);
P070203 := FALSE;

Well let's throw it into the editor and try to compile it.

941 8 3:D 3 FUNCTION P070203 : BOOLEAN;
942 8 3:0 0 BEGIN
943 8 3:1 0 P070203 := TRUE;
944 8 3:1 3 MP1203.I := -16384; (* -$4000 *)
945 8 3:1 11 IF MP1203.P^[0] > 127 THEN
946 8 3:2 21 EXIT( P070203);
947 8 3:1 25 MP1203.I := -16616;
948 8 3:1 33 IF MP1203.P^[ 0] <> MP1203.P^[ 1] THEN
949 8 3:2 48 EXIT( P070203);
950 8 3:1 52 P070203 := FALSE
951 8 3:0 52 END;

Wow! That actually compiled. I wasn't really sure if the syntax was correct, but apparently it is.

Double Wow! The DeCompiler says it produced the proper code!

Something about a million monkeys and Shakespeare comes to mind.

I have no idea what this code is doing.
$4000 is secondary hi-res screen location.
$40E8 is also on that screen.

Lol (laughing!) -16384 is $C000 (-$4000), so it is the Keyboard Data Latch.

-$4000 -16384 $C000 Keyboard Data
-$40E8 -16616 $BF18 Pascal System stuff (?) SYSCOM (?)
-16368 $C010 Clear Keyboard Strobe

$BF18 (See ATTACH.PDF)

"1. The type ahead buffer is located at $03B1 hex and is $4E hex in length. It is implemented with a read pointer (RPTR at BF18 hex) and a write pointer WPTR at $BF19 hex). At CONSOLE: init time, these should both be set to 0. When a character is detected by CONCK, the WPTR is incremented then compared with $4E. If it is equal to $4E, it is set to $0 (this is a circular buffer). Then the WPTR is compared with RPTR and if they are equal the buffer is full. If the buffer is not full, the character is stored at $03B1+the value in WPTR.

"When removing a character from the type ahead buffer, use the following sequence. Compare the RPTR with WPTR and if they are equal, the buffer is empty and you must wait until a character is available from the keyboard. If they are not equal, increment the RPTR and compair it to $4E. If it equals $4E, set it to $0. Now get the character from location $03B1+the value in RPTR."

(Note the interesting spelling of "compair".)

This routine, P070203 is checking to see if a keystroke has been made, or if it was made and already put into the Pascal keyboard input buffer. Returns TRUE if key was pressed.

This mention of the keyboard input buffer at $3B1 is something I discovered the hard way back in 1990 or 1991.

BEGIN VERY VERY LONG STORY:

I started the Wizardry III journey by working my way through the bootstrap code for the Wizardry III boot diskette. I first copied as much of the diskette that would copy using COPYII+. Next I edited Track 0, Sector 0, and put a BRK instruction (byte value of 0) at the second byte in the sector. This sector gets loaded at $800, and $801 is the first instruction executed after it is loaded. By doing this, I can boot the diskette and it will drop me into the Apple monitor with the code at $800. I can then change the value at $801 and use the monitor commands to begin my bootstrap investigation.

The code at $800 - $8FF will load another bunch of sectors from the diskette and then start to execute that code as the next part of the bootstrap process. So just after those sectors have been read from the diskette, and before executing that code, I find the place on Track 0, Sector 0, and change the instruction to a BRK (and also restore the second byte in this sector to its original value). Now the diskette can be booted and code will be loaded at $800 - $8FF, and then the next chunk of code gets loaded to where it goes, and then the Apple puts me in the monitor so I can examine memory, etc.

Ok, this worked great for awhile, and I got quite far, but somewhere down the road instead of dropping me into the Apple monitor, the machine would just keep rebooting. Ah! Of course! The bytes at $3F2, $3F3, and $3F4 were being cleared to 0 at some point, so the handling of the BRK instruction by the monitor would think the machine was just turned on and go through its boot sequence. Ok, so I managed to patch the code on the diskette so that those bytes at #3F2 - #3F4 were not cleared. And eventually I got a long way into the bootstrap, and into the execution of the Pascal system, and the loading of SYSTEM.STARTUP, and execution of the Wizardry program.

But I was just getting to the hard part.

One way of continuing this process once you get into the Pascal pcode interpreter is to modify the pcode you are interested in. But you cannot put a "BRK" instruction. One way is to put a "HALT" instruction into the pcode where you want it to stop. But you also need to modify the Pascal handler for the HALT instruction by putting a BRK instruction there. Ok, I did all that.

During the execution of SYSTEM.STARTUP, you press a key and it stops the title screen from displaying and displays the first menu to the user. And since I had placed the HALT instruction at the proper place, it put me into the Apples monitor as expected and I could poke around the Pascal Heap stack, etc.

Now sometimes when I was booting this modified diskette, I would press and hold a key down to get to my inserted HALT instruction. But something weird happened. Instead of putting me into the monitor, it rebooted! Ok, so just press one key and then everything would work fine.

And this was ok for awhile. But then after executing a lot of code I wanted to HALT the program at the point where I was just going to enter the maze. And now the HALT instruction caused the Apple to reboot every time. Now obviously there were many keystrokes entered from the time of first booting until actually entering the maze.

And to make a long story short, the reason the Apple was always rebooting is because the keyboard input buffer which starts at #3B1 was filling up and clobbering (as it should) the bytes $3F2, $3F3, $3F4.

Was I at a dead end? No, I simply modified the Pascal runtime system to think the input buffer was shorter than #$4E, so that it would not clobber those magic power on bytes.

Please let me know if you enjoyed this "detour" from our current journey.

END OF VERY VERY LONG STORY

Up next is P070208.

awanderin

unread,
Apr 16, 2014, 12:05:23 AM4/16/14
to

I'm reading every post! Keep them coming, they are quite interesting!
Thanks!


--
Jerry awanderin at gmail dot com

qkumba

unread,
Apr 16, 2014, 12:51:01 AM4/16/14
to
copyprot:

0000-009D - nibble check #1
looks like a patch 009E from 84 to 00 will defeat it.

00A2-011B - nibble check #2
looks like a patch 0107 from F0 to B0 will defeat it.

011B - 01AA - move the drive head (seek track). standard routine.

511A... - looks like LZW decompression routine.

> LDA L04BC,Y ; LOOKS UNRESOLVED ??? MAYBE P070005 STARTS AT $54BC

this is part of a pre-computed line table for the graphics screen ($54BC, as you have it)
--

I am enjoying these posts very much.
Please let me know if I can be of assistance with understanding any particular part of the disassembly.

TommyGoog

unread,
Apr 17, 2014, 2:37:35 AM4/17/14
to
Thank you Jerry (awanderin) and qkumba (and Vladimir Ivanov and Rob Craig and sicklittlemonkey) for your comments. It's good to see I have at least a small audience and not an empty classroom :)

I hope you've all had a chance to read my earlier thread regarding Wizardry III (Legacy of Llylgamyn). There was one thing I never figured out with that decompiling effort. I could never get the order of the PROCEDURES (and FUNCTIONS) to be in the same order in the final ".CODE" file as they did. Normally the PROCEDUREs are placed in the file in the order in which their "END;" statement is compiled. I don't know if there is a Compiler (or Linker) option I am not aware of that could affect that. My guess 2 years ago (and still today) is that they may have had a specialized Linker or perhaps their own "incremental" Linker to put the pieces into the final ".CODE" file. If you'd like to solve a problem I could not, that would be a good one! Note the order for LOL WIZUTIL.CODE was correct, but the order for WIZARDRY.CODE was not. I suspect I may run into this with Wizardry I also.

As far as my understanding and figuring out the code for Wizardry I, if something comes up that I cannot figure out, I'll ask for help or explain what I have. Sometimes I'll leave my early comments like "LOOKS UNRESOLVED ???" in the code even after I've figured it out. It sort of documents what I was thinking during this work-in-progress. At the very end, in the final cleanup phase, I usually remove things might be confusing to the first time reader.

Regarding defeating the "nibble checks" in the code, another way is to just NOP out the call to the copy protection code (provided no residual information is needed), or to just ignore the result of CHKCOPY after the call.

What's that you say? Pascal has a NOP instruction? If so, how do you put a NOP into Pascal code? See p. 284 of Apple Pascal Operating System. Opcode 215 ($D7) is a NOP. In this instance, CHKCOPY( P070004) saves information at SERIAL, so we must at least make the call to CHKCOPY. If you look at the "IF MP05 THEN EXIT( CHKCOPY);" you will see that we can modify this instruction to always take the "EXIT" instead of getting to the "HALT" instruction.

50AB A1 04       UJP.   IF NOT (TOS) THEN JUMP TO 50B1 We can NOP the 50AB and 50AC instructions and the EXIT will always be taken. These are located on the original diskette at: TRACK $22, SECTOR $7, at address $AB and $AC. How did I determine that? From DeCompiler, FILE: 7 SEG: 0 PROC: 4 has P070004 starting at $5000 (what luck! beginning of segment!). The DeCompiler also shows FILE: 7 starting at Track 34 Sector 7 (DOS formatting,and the "34" is a decimal number not hex like it really should be). Use COPYII+ to read TRACK $22, SECTOR $7, move down to AB and change 2 consecutive bytes (from A1 and 04) to D7 and D7.

If you really want to be clever, you could change the UJP instruction to jump to 50AD instead of jumping to 50B1. I think changing 50AC from 04 to 00 will accomplish this. So just a 1 byte change to defeat the copy protection scheme, and it will be obvious to anyone looking at the modified code that this is what was done!

I haven't really begun analyzing the code for LOL yet. Right now its just "get Pascal source to compile to the pcode". Later I will spend more time trying to see what the code does when fixing the names for variables and procedures.

It takes a lot of time to put these postings together which detracts from my time to do the actual work:) I had almost gotten to the point of beginning to post minimal information, but perhaps some of you would still like to see what I'm doing. At some point it will become quite repetitive. After all, there are over 500 PROCEDUREs in this effort.

I've actually been working a bit further ahead than what I've posted. On April 15 I already completed the entire Pascal (and assembly language) code to compile and get the pcode for all of SYSTEM.STARTUP. I think it actually helps to do the work the first time, then get away from it for a few days, and then come back to it and then try to re-figure out what I did so I can explain it to others.

Once again, thanks for your comments and don't be afraid to ask questions.

Tommy





TommyGoog

unread,
Apr 18, 2014, 5:44:38 PM4/18/14
to

> Up next is P070208.

Here is the pcode we want:

FILE: 7 SEG: 2 PROC: 8

JTAB FOR SUBROUTINE 8:
DATA SIZE: 80
PARAM SIZE: 0
EXIT AT: 530C
ENTER AT: 52DE
PROC NUMBER: 8
LEXICAL LEVEL: 2
52DE C6 01 LLA. PUSH #MP.01
52E0 C6 81 2D LLA. PUSH #MP.012D
52E3 C7 01 00 LDCI. PUSH #0001
52E6 91 NGI. PUSH -(TOS) (2'S COMP)
52E7 CD 00 03 CXP. CALL EXTERNAL PROCEDURE: 03 IN SEGMENT: 00
52EA C6 01 LLA. PUSH #MP.01
52EC D7 NOP. NOP
52ED A6 02 57 54
WT
LSA. PUSH #(PC+1) POINTER TO THE STRING
52F1 01 SLDC. PUSH #0001
52F2 00 SLDC. PUSH #0000
52F3 CD 00 05 CXP. CALL EXTERNAL PROCEDURE: 05 IN SEGMENT: 00
52F6 C6 01 LLA. PUSH #MP.01
52F8 B2 01 03 LDA. PUSH #ACTREC(-01).03
52FB 00 SLDC. PUSH #0000
52FC 12 SLDC. PUSH #0012
52FD 00 SLDC. PUSH #0000
52FE 01 SLDC. PUSH #0001
52FF 00 SLDC. PUSH #0000
5300 00 SLDC. PUSH #0000
5301 CD 00 1C CXP. CALL EXTERNAL PROCEDURE: 1C IN SEGMENT: 00
5304 AB 04 SRO. BASE.04 := (TOS)
5306 C6 01 LLA. PUSH #MP.01
5308 00 SLDC. PUSH #0000
5309 CD 00 06 CXP. CALL EXTERNAL PROCEDURE: 06 IN SEGMENT: 00
530C C6 01 LLA. PUSH #MP.01
530E 00 SLDC. PUSH #0000
530F CD 00 06 CXP. CALL EXTERNAL PROCEDURE: 06 IN SEGMENT: 00
5312 AD 00 RNP. RETURN FROM NON-BASE PROCEDURE.


WIZBOOT calls TITLELOA, and TITLELOA then calls P070208 right away.

It is a procedure (see RNP instruction) with no parameters (PARAM SIZE: 0).

I'm confused by the procedure calls in this routine. I'm not familiar with:

EXTERNAL PROCEDURE: 03 in SEGMENT: 00
EXTERNAL PROCEDURE: 05 in SEGMENT: 00
EXTERNAL PROCEDURE: 1C in SEGMENT: 00
EXTERNAL PROCEDURE: 06 in SEGMENT: 00

Segment 00 is part of the Pascal system's standard routines.

In LOL for SYSTEM.STARTUP (something I have not re-engineered into Pascal) I notice there is a CALL EXTERNAL PROCEDURE: 05 in SEGMENT: 00. One of its parameters is "picture.bits".

I see "WT" in Wiz1 as input to PROCEDURE: 05. It would seem "PROCEDURE: 05" has something to do with files. I never encountered "PROCEDURE: 05 in SEGMENT: 00" in the LOL re-engineering.

Time for a little testing.

Here is the test program:

PROGRAM TEST;
VAR F: FILE OF CHAR;

BEGIN
RESET( F)
END.

Here is the pcode:

JTAB FOR SUBROUTINE 1:
DATA SIZE: 602
PARAM SIZE: 4
EXIT AT: 5015
ENTER AT: 5000
PROC NUMBER: 1
LEXICAL LEVEL: 0
5000 D7 NOP. NOP
5001 D7 NOP. NOP
5002 A5 03 LA0. PUSH #BASE.03
5004 A5 81 2F LA0. PUSH #BASE.012F
5007 C7 02 00 LDCI. PUSH #0002
500A 91 NGI. PUSH -(TOS) (2'S COMP)
500B CD 00 03 CXP. CALL EXTERNAL PROCEDURE: 03 IN SEGMENT: 00
500E A5 03 LA0. PUSH #BASE.03
5010 CD 00 04 CXP. CALL EXTERNAL PROCEDURE: 04 IN SEGMENT: 00
5013 9E 00 AN UNKNOWN STANDARD PROCEDURE ?!?!
5015 A5 03 LA0. PUSH #BASE.03
5017 00 SLDC. PUSH #0000
5018 CD 00 06 CXP. CALL EXTERNAL PROCEDURE: 06 IN SEGMENT: 00
501B C1 00 RBP. RETURN FROM BASE PROCEDURE.
501D 00 SLDC. PUSH #0000


Well, I didn't expect that much pcode from such little Pascal code!

We got a "PROCEDURE: 03" and "PROCEDURE: 06", but no PROCEDURE: 05.

Let's try another test. I will add all the compiler options that LOL had (and Wiz1 too), and try with no code, and try putting (INPUT, OUTPUT) in the program header. (I later learned that putting INPUT and OUTPUT in the header for Apple Pascal programs does nothing.)

Program:
(*$S++*)
(*$R-*)
(*$I-*)
(*$V-*)

PROGRAM TEST( INPUT, OUTPUT);

VAR FILE1 : FILE OF INTEGER;

BEGIN
END.

pcode:

DATA SIZE: 602
PARAM SIZE: 4
EXIT AT: 500B
ENTER AT: 5000
PROC NUMBER: 1
LEXICAL LEVEL: 0
5000 D7 NOP. NOP
5001 D7 NOP. NOP
5002 A5 03 LA0. PUSH #BASE.03
5004 A5 81 2F LA0. PUSH #BASE.012F
5007 01 SLDC. PUSH #0001
5008 CD 00 03 CXP. CALL EXTERNAL PROCEDURE: 03 IN SEGMENT: 00
500B A5 03 LA0. PUSH #BASE.03
500D 00 SLDC. PUSH #0000
500E CD 00 06 CXP. CALL EXTERNAL PROCEDURE: 06 IN SEGMENT: 00
5011 C1 00 RBP. RETURN FROM BASE PROCEDURE.
5013 00 SLDC. PUSH #0000

Another big surprise! With no Pascal instructions, there was a bunch of pcode generated. This is coming from the " VAR FILE1" declaration!

Ok, looks like declaring a file in the VAR section generates a call to "PROCEDURE: 03" at the beginning of the procedure, and a "PROCEDURE: 06" at the end. Also notice the input to "PROCEDURE: 03". With "FILE OF CHAR" it was -2, but with "FILE OF INTEGER" it is +1.

We want the input to 03 to be "-1". Time to experiment again. I tried all the following:

FILE OF INTEGER 0001
FILE OF REAL 0002
FILE OF CHAR -0002
FILE OF BOOLEAN 0001
FILE OF INTERACTIVE 012D
FILE OF STRING 0029
FILE OF TEXT 012D

PACKED FILE OF xxxx didn't make any difference

It sometimes takes a lot of energy to re-engineer 1 freaking simple line of code. You can quote me on that!

VAR FILE1 : FILE; (* -0001 *)

Removing the "type" from FILE1 gives us -1 which is what we want!

With a little more experimenting I found how to generate PROCEDURE: 05

RESET( FILE1, 'WT');

This next chunk of pcode looks like a call to BLOCKREAD()

52F6 C6 01 LLA. PUSH #MP.01
52F8 B2 01 03 LDA. PUSH #ACTREC(-01).03
52FB 00 SLDC. PUSH #0000
52FC 12 SLDC. PUSH #0012
52FD 00 SLDC. PUSH #0000
52FE 01 SLDC. PUSH #0001
52FF 00 SLDC. PUSH #0000
5300 00 SLDC. PUSH #0000
5301 CD 00 1C CXP. CALL EXTERNAL PROCEDURE: 1C IN SEGMENT: 00
5304 AB 04 SRO. BASE.04 := (TOS)

BLOCKREAD() is a function that returns a value.
First parameter is the declared filename.
Second parameter is a buffer.

Here #ACTREC(-1).03 refers to the parent procedure that called P070208, and it is referring to P070201 MP03.

Right now, MP03 in P070208 is defined as ARRAY[ 1..4608] OF INTEGER.
I'm not sure if all of that is for MP03, but it makes sense that it is a buffer.

Note, if P070208 had a variable named MP03, then I would need to change the name MP03 in P070201 or MP03 in P070208.

This next chunk looks like CLOSE( FILE1);

5306 C6 01 LLA. PUSH #MP.01
5308 00 SLDC. PUSH #0000
5309 CD 00 06 CXP. CALL EXTERNAL PROCEDURE: 06 IN SEGMENT: 00

Note, this sequence appears twice in the code we want; the second one is because of the declaration of the FILE1.

BASE04 is the name I gave to the variable in LOL that generates pcode with BASE.04. (Actually most variables that I re-engineer start out something like BASE04.) I left that variable as the final name because its real name was probably something like "TEMP". It had many uses in LOL. And also there were a few bugs associated with it, where I think the authors used the wrong variable name in a couple places associated with "TEMP" (or BASE04, or whatever they called it.)

Hmmm, I tried to use BASE04, but now realize I had already removed it. So this means the variable currently defined as RANDNUM is BASE.04. Hmmmm. Its likely that RANDNUM is not the correct name for BASE.04.

To get things to compile right now, I will use the name RANDNUM in P070208.

I think I may have been wrong about removing BASE04 and not removing RANDNUM earlier.

I think there was a bug I discovered in LOL where RANDNUM was never initialized. Maybe they removed this code and forgot to initialize RANDNUM in LOL (?)


After a little trial and error, I have the final (rough draft) version:

970 8 8:D 1 PROCEDURE P070208;
971 8 8:D 1
972 8 8:D 1 VAR
973 8 8:D 1 FILE1 : FILE; (* UNTYPED FILE *)
974 8 8:D 41
975 8 8:D 41
976 8 8:0 0 BEGIN
977 8 8:1 0 RESET( FILE1, 'WT');
978 8 8:1 24 RANDNUM := BLOCKREAD( FILE1, MP03, 18, 0);
979 8 8:1 40 CLOSE( FILE1)
980 8 8:0 46 END;

Note, 18 x 512 = 4608. Looks like MP03 is already defined with the correct size.

P070204 is next. It has 3 child procedures: P070205, P070206, P070207.

Here is the pcode we want:

FILE: 7 SEG: 2 PROC: 4

JTAB FOR SUBROUTINE 4:
DATA SIZE: 14
PARAM SIZE: 0
EXIT AT: 52BD
ENTER AT: 50D8
PROC NUMBER: 4
LEXICAL LEVEL: 2
50D8 B2 01 03 LDA. PUSH #ACTREC(-01).03
50DB 00 SLDC. PUSH #0000
50DC A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
50DE B2 01 03 LDA. PUSH #ACTREC(-01).03
50E1 08 SLDC. PUSH #0008
50E2 A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
50E4 F8 SIND. PUSH (TOS)^.0
50E5 CD 01 02 CXP. CALL EXTERNAL PROCEDURE: 02 IN SEGMENT: 01
50E8 C7 00 20 LDCI. PUSH #2000
50EB B8 01 92 03 STR. ACTREC(-01).1203 := TOS
50EF B6 01 92 03 LOD. PUSH ACTREC(-01).1203
50F3 00 SLDC. PUSH #0000
50F4 C7 00 20 LDCI. PUSH #2000
50F7 00 SLDC. PUSH #0000
50F8 9E 0A FLC. FILLCHAR (DST+IDX,LEN,CHR)
50FA B6 03 03 LOD. PUSH ACTREC(-03).03
50FD 0C SLDC. PUSH #000C
50FE 00 SLDC. PUSH #0000
50FF CD 00 11 CXP. CALL EXTERNAL PROCEDURE: 11 IN SEGMENT: 00
5102 0C SLDC. PUSH #000C
5103 0A SLDC. PUSH #000A
5104 CD 00 1D CXP. CALL EXTERNAL PROCEDURE: 1D IN SEGMENT: 00
5107 B6 03 03 LOD. PUSH ACTREC(-03).03
510A D7 NOP. NOP
510B A6 10 50 52 45 50 41 52 45 20 59 4F 55 52 53 45 4C 46
PREPARE YOURSELF
LSA. PUSH #(PC+1) POINTER TO THE STRING
511D 00 SLDC. PUSH #0000
511E CD 00 13 CXP. CALL EXTERNAL PROCEDURE: 13 IN SEGMENT: 00
5121 C7 96 00 LDCI. PUSH #0096
5124 CE 05 CLP. CALL CHILD PROCEDURE: 05
5126 0C SLDC. PUSH #000C
5127 0C SLDC. PUSH #000C
5128 CD 00 1D CXP. CALL EXTERNAL PROCEDURE: 1D IN SEGMENT: 00
512B B6 03 03 LOD. PUSH ACTREC(-03).03
512E D7 NOP. NOP
512F A6 10 46 4F 52 20 54 48 45 20 55 4C 54 49 4D 41 54 45
FOR THE ULTIMATE
LSA. PUSH #(PC+1) POINTER TO THE STRING
5141 00 SLDC. PUSH #0000
5142 CD 00 13 CXP. CALL EXTERNAL PROCEDURE: 13 IN SEGMENT: 00
5145 C7 96 00 LDCI. PUSH #0096
5148 CE 05 CLP. CALL CHILD PROCEDURE: 05
514A 0C SLDC. PUSH #000C
514B 0E SLDC. PUSH #000E
514C CD 00 1D CXP. CALL EXTERNAL PROCEDURE: 1D IN SEGMENT: 00
514F B6 03 03 LOD. PUSH ACTREC(-03).03
5152 D7 NOP. NOP
5153 A6 10 49 4E 20 46 41 4E 54 41 53 59 20 47 41 4D 45 53
IN FANTASY GAMES
LSA. PUSH #(PC+1) POINTER TO THE STRING
5165 00 SLDC. PUSH #0000
5166 CD 00 13 CXP. CALL EXTERNAL PROCEDURE: 13 IN SEGMENT: 00
5169 C7 F4 01 LDCI. PUSH #01F4
516C CE 05 CLP. CALL CHILD PROCEDURE: 05
516E C7 AC 3F LDCI. PUSH #3FAC
5171 91 NGI. PUSH -(TOS) (2'S COMP)
5172 AE 02 CIP. CALL INTERMEDIATE PROCEDURE: 02
5174 C7 A9 3F LDCI. PUSH #3FA9
5177 91 NGI. PUSH -(TOS) (2'S COMP)
5178 AE 02 CIP. CALL INTERMEDIATE PROCEDURE: 02
517A C7 AE 3F LDCI. PUSH #3FAE
517D 91 NGI. PUSH -(TOS) (2'S COMP)
517E AE 02 CIP. CALL INTERMEDIATE PROCEDURE: 02
5180 C7 B0 3F LDCI. PUSH #3FB0
5183 91 NGI. PUSH -(TOS) (2'S COMP)
5184 AE 02 CIP. CALL INTERMEDIATE PROCEDURE: 02
5186 B6 03 03 LOD. PUSH ACTREC(-03).03
5189 0C SLDC. PUSH #000C
518A 00 SLDC. PUSH #0000
518B CD 00 11 CXP. CALL EXTERNAL PROCEDURE: 11 IN SEGMENT: 00
518E 29 SLDC. PUSH #0029
518F 00 SLDC. PUSH #0000
5190 CD 00 1D CXP. CALL EXTERNAL PROCEDURE: 1D IN SEGMENT: 00
5193 B2 01 03 LDA. PUSH #ACTREC(-01).03
5196 00 SLDC. PUSH #0000
5197 A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
5199 B2 01 03 LDA. PUSH #ACTREC(-01).03
519C 00 SLDC. PUSH #0000
519D A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
519F F8 SIND. PUSH (TOS)^.0
51A0 CD 01 02 CXP. CALL EXTERNAL PROCEDURE: 02 IN SEGMENT: 01
51A3 00 SLDC. PUSH #0000
51A4 CC 04 STL. MP.04 := (TOS)
51A6 04 SLDC. PUSH #0004
51A7 CC 05 STL. MP.05 := (TOS)
51A9 01 SLDC. PUSH #0001
51AA CC 02 STL. MP.02 := (TOS)
51AC 14 SLDC. PUSH #0014
51AD CC 06 STL. MP.06 := (TOS)
51AF D9 SLDL. PUSH MP.02
51B0 DD SLDL. PUSH MP.06
51B1 C8 LEQI. PUSH ((TOS-1) <= (TOS))
51B2 A1 0C UJP. IF NOT (TOS) THEN JUMP TO 51C0
51B4 0F SLDC. PUSH #000F
51B5 CE 05 CLP. CALL CHILD PROCEDURE: 05
51B7 CE 06 CLP. CALL CHILD PROCEDURE: 06
51B9 D9 SLDL. PUSH MP.02
51BA 01 SLDC. PUSH #0001
51BB 82 ADI. PUSH ((TOS) + (TOS-1))
51BC CC 02 STL. MP.02 := (TOS)
51BE B9 F6 UJP. JUMP TO 51AF
51C0 01 SLDC. PUSH #0001
51C1 CC 02 STL. MP.02 := (TOS)
51C3 18 SLDC. PUSH #0018
51C4 CC 06 STL. MP.06 := (TOS)
51C6 D9 SLDL. PUSH MP.02
51C7 DD SLDL. PUSH MP.06
51C8 C8 LEQI. PUSH ((TOS-1) <= (TOS))
51C9 A1 20 UJP. IF NOT (TOS) THEN JUMP TO 51EB
51CB 01 SLDC. PUSH #0001
51CC CC 01 STL. MP.01 := (TOS)
51CE 02 SLDC. PUSH #0002
51CF CC 07 STL. MP.07 := (TOS)
51D1 D8 SLDL. PUSH MP.01
51D2 DE SLDL. PUSH MP.07
51D3 C8 LEQI. PUSH ((TOS-1) <= (TOS))
51D4 A1 0C UJP. IF NOT (TOS) THEN JUMP TO 51E2
51D6 0C SLDC. PUSH #000C
51D7 CE 05 CLP. CALL CHILD PROCEDURE: 05
51D9 CE 06 CLP. CALL CHILD PROCEDURE: 06
51DB D8 SLDL. PUSH MP.01
51DC 01 SLDC. PUSH #0001
51DD 82 ADI. PUSH ((TOS) + (TOS-1))
51DE CC 01 STL. MP.01 := (TOS)
51E0 B9 F4 UJP. JUMP TO 51D1
51E2 CE 07 CLP. CALL CHILD PROCEDURE: 07
51E4 D9 SLDL. PUSH MP.02
51E5 01 SLDC. PUSH #0001
51E6 82 ADI. PUSH ((TOS) + (TOS-1))
51E7 CC 02 STL. MP.02 := (TOS)
51E9 B9 F2 UJP. JUMP TO 51C6
51EB B2 01 03 LDA. PUSH #ACTREC(-01).03
51EE 00 SLDC. PUSH #0000
51EF A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
51F1 B2 01 03 LDA. PUSH #ACTREC(-01).03
51F4 08 SLDC. PUSH #0008
51F5 A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
51F7 F8 SIND. PUSH (TOS)^.0
51F8 CD 01 02 CXP. CALL EXTERNAL PROCEDURE: 02 IN SEGMENT: 01
51FB CE 06 CLP. CALL CHILD PROCEDURE: 06
51FD 09 SLDC. PUSH #0009
51FE CC 02 STL. MP.02 := (TOS)
5200 17 SLDC. PUSH #0017
5201 CC 06 STL. MP.06 := (TOS)
5203 D9 SLDL. PUSH MP.02
5204 DD SLDL. PUSH MP.06
5205 C8 LEQI. PUSH ((TOS-1) <= (TOS))
5206 A1 30 UJP. IF NOT (TOS) THEN JUMP TO 5238
5208 01 SLDC. PUSH #0001
5209 CC 01 STL. MP.01 := (TOS)
520B 02 SLDC. PUSH #0002
520C CC 07 STL. MP.07 := (TOS)
520E D8 SLDL. PUSH MP.01
520F DE SLDL. PUSH MP.07
5210 C8 LEQI. PUSH ((TOS-1) <= (TOS))
5211 A1 0E UJP. IF NOT (TOS) THEN JUMP TO 5221
5213 05 SLDC. PUSH #0005
5214 D9 SLDL. PUSH MP.02
5215 82 ADI. PUSH ((TOS) + (TOS-1))
5216 CE 05 CLP. CALL CHILD PROCEDURE: 05
5218 CE 06 CLP. CALL CHILD PROCEDURE: 06
521A D8 SLDL. PUSH MP.01
521B 01 SLDC. PUSH #0001
521C 82 ADI. PUSH ((TOS) + (TOS-1))
521D CC 01 STL. MP.01 := (TOS)
521F B9 F0 UJP. JUMP TO 520E
5221 B2 01 03 LDA. PUSH #ACTREC(-01).03
5224 00 SLDC. PUSH #0000
5225 A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
5227 B2 01 03 LDA. PUSH #ACTREC(-01).03
522A D9 SLDL. PUSH MP.02
522B A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
522D F8 SIND. PUSH (TOS)^.0
522E CD 01 02 CXP. CALL EXTERNAL PROCEDURE: 02 IN SEGMENT: 01
5231 D9 SLDL. PUSH MP.02
5232 01 SLDC. PUSH #0001
5233 82 ADI. PUSH ((TOS) + (TOS-1))
5234 CC 02 STL. MP.02 := (TOS)
5236 B9 EE UJP. JUMP TO 5203
5238 18 SLDC. PUSH #0018
5239 CC 02 STL. MP.02 := (TOS)
523B 20 SLDC. PUSH #0020
523C CC 06 STL. MP.06 := (TOS)
523E D9 SLDL. PUSH MP.02
523F DD SLDL. PUSH MP.06
5240 C8 LEQI. PUSH ((TOS-1) <= (TOS))
5241 A1 2E UJP. IF NOT (TOS) THEN JUMP TO 5271
5243 01 SLDC. PUSH #0001
5244 CC 01 STL. MP.01 := (TOS)
5246 04 SLDC. PUSH #0004
5247 CC 07 STL. MP.07 := (TOS)
5249 D8 SLDL. PUSH MP.01
524A DE SLDL. PUSH MP.07
524B C8 LEQI. PUSH ((TOS-1) <= (TOS))
524C A1 0C UJP. IF NOT (TOS) THEN JUMP TO 525A
524E 0A SLDC. PUSH #000A
524F CE 05 CLP. CALL CHILD PROCEDURE: 05
5251 CE 06 CLP. CALL CHILD PROCEDURE: 06
5253 D8 SLDL. PUSH MP.01
5254 01 SLDC. PUSH #0001
5255 82 ADI. PUSH ((TOS) + (TOS-1))
5256 CC 01 STL. MP.01 := (TOS)
5258 B9 EC UJP. JUMP TO 5249
525A B2 01 03 LDA. PUSH #ACTREC(-01).03
525D 00 SLDC. PUSH #0000
525E A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
5260 B2 01 03 LDA. PUSH #ACTREC(-01).03
5263 D9 SLDL. PUSH MP.02
5264 A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
5266 F8 SIND. PUSH (TOS)^.0
5267 CD 01 02 CXP. CALL EXTERNAL PROCEDURE: 02 IN SEGMENT: 01
526A D9 SLDL. PUSH MP.02
526B 01 SLDC. PUSH #0001
526C 82 ADI. PUSH ((TOS) + (TOS-1))
526D CC 02 STL. MP.02 := (TOS)
526F B9 EA UJP. JUMP TO 523E
5271 01 SLDC. PUSH #0001
5272 CC 02 STL. MP.02 := (TOS)
5274 28 SLDC. PUSH #0028
5275 CC 06 STL. MP.06 := (TOS)
5277 D9 SLDL. PUSH MP.02
5278 DD SLDL. PUSH MP.06
5279 C8 LEQI. PUSH ((TOS-1) <= (TOS))
527A A1 0C UJP. IF NOT (TOS) THEN JUMP TO 5288
527C 0C SLDC. PUSH #000C
527D CE 05 CLP. CALL CHILD PROCEDURE: 05
527F CE 06 CLP. CALL CHILD PROCEDURE: 06
5281 D9 SLDL. PUSH MP.02
5282 01 SLDC. PUSH #0001
5283 82 ADI. PUSH ((TOS) + (TOS-1))
5284 CC 02 STL. MP.02 := (TOS)
5286 B9 E8 UJP. JUMP TO 5277
5288 01 SLDC. PUSH #0001
5289 CC 02 STL. MP.02 := (TOS)
528B 50 SLDC. PUSH #0050
528C CC 06 STL. MP.06 := (TOS)
528E D9 SLDL. PUSH MP.02
528F DD SLDL. PUSH MP.06
5290 C8 LEQI. PUSH ((TOS-1) <= (TOS))
5291 A1 13 UJP. IF NOT (TOS) THEN JUMP TO 52A6
5293 B2 01 01 LDA. PUSH #ACTREC(-01).01
5296 00 SLDC. PUSH #0000
5297 CD 01 02 CXP. CALL EXTERNAL PROCEDURE: 02 IN SEGMENT: 01
529A 07 SLDC. PUSH #0007
529B CE 05 CLP. CALL CHILD PROCEDURE: 05
529D CE 06 CLP. CALL CHILD PROCEDURE: 06
529F D9 SLDL. PUSH MP.02
52A0 01 SLDC. PUSH #0001
52A1 82 ADI. PUSH ((TOS) + (TOS-1))
52A2 CC 02 STL. MP.02 := (TOS)
52A4 B9 E6 UJP. JUMP TO 528E
52A6 01 SLDC. PUSH #0001
52A7 CC 02 STL. MP.02 := (TOS)
52A9 28 SLDC. PUSH #0028
52AA CC 06 STL. MP.06 := (TOS)
52AC D9 SLDL. PUSH MP.02
52AD DD SLDL. PUSH MP.06
52AE C8 LEQI. PUSH ((TOS-1) <= (TOS))
52AF A1 0C UJP. IF NOT (TOS) THEN JUMP TO 52BD
52B1 0C SLDC. PUSH #000C
52B2 CE 05 CLP. CALL CHILD PROCEDURE: 05
52B4 CE 06 CLP. CALL CHILD PROCEDURE: 06
52B6 D9 SLDL. PUSH MP.02
52B7 01 SLDC. PUSH #0001
52B8 82 ADI. PUSH ((TOS) + (TOS-1))
52B9 CC 02 STL. MP.02 := (TOS)
52BB B9 E4 UJP. JUMP TO 52AC
52BD AD 00 RNP. RETURN FROM NON-BASE PROCEDURE.
52BF 00 SLDC. PUSH #0000
52C0 14 SLDC. PUSH #0014
52C1 00 SLDC. PUSH #0000
52C2 34 SLDC. PUSH #0034
52C3 00 SLDC. PUSH #0000
52C4 4D SLDC. PUSH #004D
52C5 00 SLDC. PUSH #0000
52C6 88 CHK. IF (TOS-1) <= (TOS-2) <= (TOS) THEN PUSH TOS-2 ELSE ERROR
52C7 00 SLDC. PUSH #0000
52C8 7F SLDC. PUSH #007F
52C9 00 SLDC. PUSH #0000
52CA C7 00 BE LDCI. PUSH #BE00
52CD 00 SLDC. PUSH #0000
52CE 08 SLDC. PUSH #0008
52CF 01 SLDC. PUSH #0001
52D0 FF SIND. PUSH (TOS)^.7
52D1 00 SLDC. PUSH #0000
52D2 23 SLDC. PUSH #0023
52D3 01 SLDC. PUSH #0001


Lets work on this section first:

50D8 B2 01 03 LDA. PUSH #ACTREC(-01).03
50DB 00 SLDC. PUSH #0000
50DC A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
50DE B2 01 03 LDA. PUSH #ACTREC(-01).03
50E1 08 SLDC. PUSH #0008
50E2 A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
50E4 F8 SIND. PUSH (TOS)^.0
50E5 CD 01 02 CXP. CALL EXTERNAL PROCEDURE: 02 IN SEGMENT: 01

#ACTREC(-01).03 is MP03 in P070201. It is a BUFFER of 4608 INTEGERS, enough to hold 18 sectors. I will rename #ACTREC(-01).03 to BUFFER and use that name instead of MP03 in all routines.

I realize now that I misplaced the routines P070002 through P070004. At least P07002 needs to be above TITLELOA segment so that it gets the proper procedure number assigned to it.

SYSTEM.STARTUP
PROGRAM WIZBOOT P070001
$I WIZ1E:WIZUTILB
$I WIZ1E:WIZUTILC
$I WIZ1E:TITLELOA
$I WIZ1E:OPTIONS
FUNCTION P070002 : BOOLEAN; EXTERNAL;
TITLELOA; called by WIZBOOT main P070201

I neglected the parameters passed to P070002. Looks like there are 2.
Now I need to re-assemble P070002 with "2" parameters, and re-link P070002 into STARTUP.CODE. Then I can COMPILE STARTUP and have the P070002( 0, 0) call work properly in P070204.

Procedure: 02 might be a PROCEDURE and not a FUNCTION as in LOL.

This is my first try at the first instruction in P070204:

P070002( BUFFER[ 0], BUFFER[ 8]);

My first attempt at declaring BUFFER was wrong (see 5084 and 5085 below):

BUFFER : ARRAY[ 1..4608] OF CHAR;

5080 B2 01 03 LDA. PUSH #ACTREC(-01).03
5083 00 SLDC. PUSH #0000
5084 01 SLDC. PUSH #0001
5085 95 SBI. PUSH ((TOS-1) - (TOS))
5086 A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01

Therefore, I need to start the index for BUFFER at 0, not 1, and of course adjust 4608 to 4607.

This is what it now generates:

5080 B2 01 03 LDA. PUSH #ACTREC(-01).03
5083 00 SLDC. PUSH #0000
5084 A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
5086 F8 SIND. PUSH (TOS)^.0
5087 B2 01 03 LDA. PUSH #ACTREC(-01).03
508A 08 SLDC. PUSH #0008
508B A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
508D F8 SIND. PUSH (TOS)^.0
508E 00 SLDC. PUSH #0000
508F 00 SLDC. PUSH #0000
5090 CD 01 02 CXP. CALL EXTERNAL PROCEDURE: 02 IN SEGMENT: 01

But line 5086 is wrong. It is pushing a value from the array onto the stack, but the code we want pushes only the pointer. We don't want line 5086 to be generated at all. Therefore, this is telling me to change the parameter passage to P070002 to be by REFERENCE (VAR) and not by VALUE (no VAR).

Note the second parameter IS passed by value as deteremined by the instruction at 508D above which we want.

The 2 pushes of #0 are extra noise for calling an EXTERNAL procedure.

Since the code we want does not immediately do something with a value after calling PROCEDURE: 02, this is therefore a PROCEDURE and not a FUNCTION.

This code generates the first instruction properly:

141 1 2:D 1 PROCEDURE P070002( VAR A1:INTEGER; A2:INTEGER); EXTERNAL; (* FORMERLY COPYPROT *)
973 8 4:0 0 BEGIN (* P070204 *)
974 8 4:1 0 P070002( BUFFER[ 0], BUFFER[ 8]);
975 8 4:0 16 END;

921 8 1:D 1 SEGMENT PROCEDURE TITLELOA; (* P070201 *)
936 8 1:D 3 BUFFER : ARRAY[ 0..4607] OF INTEGER;

P070002.TEXT:
.PROC P070002,2 ; WITH 2 PARAMETERS PASSED INTO (FORMERLY COPYPROT)


This took a lot more time to fix up than I thought it should. It took me as much time to re-engineer that first instruction as it did for the rest of the entire procedure!

There is an instruction in P070204 that de-compiles and uses MP01 in the calling procedure( P070201). Since P070204 has its own variable named MP01 I have changed MP01 in P070201 to MPXX01 for now.

The rest of this procedure is easy to re-engineer once you realize it is mostly GOTOXY() and WRITE().

Here is the code for P070204:

959 8 4:D 1 PROCEDURE P070204;
960 8 4:D 1
961 8 4:D 1 VAR
962 8 4:D 1 MP01 : INTEGER;
963 8 4:D 2 MP02 : INTEGER;
964 8 4:D 3 MP03 : INTEGER;
965 8 4:D 4 MP04 : INTEGER;
966 8 4:D 5 MP05 : INTEGER;
967 8 4:D 6 MP06 : INTEGER;
968 8 4:D 7 MP07 : INTEGER;
969 8 4:D 8
970 8 4:D 8
971 8 5:D 1 PROCEDURE P070205( MP01 : INTEGER);
972 8 5:0 0 BEGIN
973 8 5:0 0 END;
974 8 5:0 12
975 8 6:D 1 PROCEDURE P070206;
976 8 6:0 0 BEGIN
977 8 6:0 0 END;
978 8 6:0 12
979 8 7:D 1 PROCEDURE P070207;
980 8 7:0 0 BEGIN
981 8 7:0 0 END;
982 8 7:0 12
983 8 4:0 0 BEGIN (* P070204 *)
984 8 4:1 0 P070002( BUFFER[ 0], BUFFER[ 8]);
985 8 4:1 16 MP1203.I := 8192; (* $2000 *)
986 8 4:1 23 FILLCHAR( MP1203.P^, 8192, 0);
987 8 4:1 34 WRITE( CHR(12)); (* LINEFEED (?) *)
988 8 4:1 42 GOTOXY( 12, 10);
989 8 4:1 47 WRITE( 'PREPARE YOURSELF');
990 8 4:1 73 P070205( 150); (* $96 *)
991 8 4:1 78 GOTOXY( 12, 12);
992 8 4:1 83 WRITE( 'FOR THE ULTIMATE');
993 8 4:1 109 P070205( 150); (* $96 *)
994 8 4:1 114 GOTOXY( 12, 14);
995 8 4:1 119 WRITE( 'IN FANTASY GAMES');
996 8 4:1 145 P070205( 500); (* $1F4 *)
997 8 4:1 150
998 8 4:1 150 P070202( -16300); (* $3FAC *)
999 8 4:1 156 P070202( -16297); (* $3FA9 *)
1000 8 4:1 162 P070202( -16302); (* $3FAE *)
1001 8 4:1 168 P070202( -16304); (* $3FB0 *)
1002 8 4:1 174
1003 8 4:1 174 WRITE( CHR( 12));
1004 8 4:1 182 GOTOXY( 41, 0); (* OFF SCREEN *)
1005 8 4:1 187
1006 8 4:1 187 P070002( BUFFER[ 0], BUFFER[ 0]);
1007 8 4:1 203 MP04 := 0;
1008 8 4:1 206 MP05 := 4;
1009 8 4:1 209 MP02 := 1;
1010 8 4:1 212 MP06 := 20;
1011 8 4:1 215
1012 8 4:1 215 WHILE MP02 <= MP06 DO
1013 8 4:2 220 BEGIN
1014 8 4:3 220 P070205( 15);
1015 8 4:3 223 P070206;
1016 8 4:3 225 MP02 := MP02 + 1;
1017 8 4:2 230 END;
1018 8 4:2 232
1019 8 4:1 232 MP02 := 1;
1020 8 4:1 235 MP06 := 24;
1021 8 4:1 238 WHILE MP02 <= MP06 DO
1022 8 4:2 243 BEGIN
1023 8 4:3 243 MP01 := 1;
1024 8 4:3 246 MP07 := 2;
1025 8 4:3 249 WHILE MP01 <= MP07 DO
1026 8 4:4 254 BEGIN
1027 8 4:5 254 P070205( 12);
1028 8 4:5 257 P070206;
1029 8 4:5 259 MP01 := MP01 + 1
1030 8 4:4 260 END;
1031 8 4:3 266 P070207;
1032 8 4:3 268 MP02 := MP02 + 1;
1033 8 4:2 273 END;
1034 8 4:1 275 P070002( BUFFER[ 0], BUFFER[ 8]);
1035 8 4:1 291 P070206;
1036 8 4:1 293 MP02 := 9;
1037 8 4:1 296 MP06 := 23;
1038 8 4:1 299 WHILE MP02 <= MP06 DO
1039 8 4:2 304 BEGIN
1040 8 4:3 304 MP01 := 1;
1041 8 4:3 307 MP07 := 2;
1042 8 4:3 310 WHILE MP01 <= MP07 DO
1043 8 4:4 315 BEGIN
1044 8 4:5 315 P070205( 5 + MP02);
1045 8 4:5 320 P070206;
1046 8 4:5 322 MP01 := MP01 + 1
1047 8 4:4 323 END;
1048 8 4:3 329 P070002( BUFFER[ 0], BUFFER[ MP02]);
1049 8 4:3 345 MP02 := MP02 + 1
1050 8 4:2 346 END;
1051 8 4:1 352 MP02 := 24;
1052 8 4:1 355 MP06 := 32;
1053 8 4:1 358 WHILE MP02 <= MP06 DO
1054 8 4:2 363 BEGIN
1055 8 4:3 363 MP01 := 1;
1056 8 4:3 366 MP07 := 4;
1057 8 4:3 369 WHILE MP01 <= MP07 DO
1058 8 4:4 374 BEGIN
1059 8 4:5 374 P070205( 10);
1060 8 4:5 377 P070206;
1061 8 4:5 379 MP01 := MP01 + 1
1062 8 4:4 380 END;
1063 8 4:3 386 P070002( BUFFER[ 0], BUFFER[ MP02]);
1064 8 4:3 402 MP02 := MP02 + 1
1065 8 4:2 403 END;
1066 8 4:1 409 MP02 := 1;
1067 8 4:1 412 MP06 := 40;
1068 8 4:1 415 WHILE MP02 <= MP06 DO
1069 8 4:2 420 BEGIN
1070 8 4:3 420 P070205( 12);
1071 8 4:3 423 P070206;
1072 8 4:3 425 MP02 := MP02 + 1;
1073 8 4:2 430 END;
1074 8 4:1 432 MP02 := 1;
1075 8 4:1 435 MP06 := 80;
1076 8 4:1 438 WHILE MP02 <= MP06 DO
1077 8 4:2 443 BEGIN
1078 8 4:3 443 P070002( MPXX01, 0);
1079 8 4:3 450 P070205( 7); (* BELL *)
1080 8 4:3 453 P070206;
1081 8 4:3 455 MP02 := MP02 + 1
1082 8 4:2 456 END;
1083 8 4:2 462
1084 8 4:1 462 MP02 := 1;
1085 8 4:1 465 MP06 := 40;
1086 8 4:1 468 WHILE MP02 <= MP06 DO
1087 8 4:2 473 BEGIN
1088 8 4:3 473 P070205( 12);
1089 8 4:3 476 P070206;
1090 8 4:3 478 MP02 := MP02 + 1
1091 8 4:2 479 END
1092 8 4:0 483 END;

The 3 child procedures to P070204 were easy and follow in order:
P070205
P070206
P070207

P070205 Pcode:

FILE: 7 SEG: 2 PROC: 5

JTAB FOR SUBROUTINE 5:
DATA SIZE: 0
PARAM SIZE: 2
EXIT AT: 5077
ENTER AT: 505C
PROC NUMBER: 5
LEXICAL LEVEL: 3
505C 29 SLDC. PUSH #0029
505D 00 SLDC. PUSH #0000
505E CD 00 1D CXP. CALL EXTERNAL PROCEDURE: 1D IN SEGMENT: 00
5061 D8 SLDL. PUSH MP.01
5062 00 SLDC. PUSH #0000
5063 C5 GRTI. PUSH ((TOS-1) > (TOS))
5064 A1 11 UJP. IF NOT (TOS) THEN JUMP TO 5077
5066 00 SLDC. PUSH #0000
5067 00 SLDC. PUSH #0000
5068 AE 03 CIP. CALL INTERMEDIATE PROCEDURE: 03
506A A1 04 UJP. IF NOT (TOS) THEN JUMP TO 5070
506C 08 SLDC. PUSH #0008
506D 04 SLDC. PUSH #0004
506E 9E 04 EXIT. EXIT FROM PROCEDURE. (TOS)=PROC# (TOS-1)=SEG#
5070 D8 SLDL. PUSH MP.01
5071 01 SLDC. PUSH #0001
5072 95 SBI. PUSH ((TOS-1) - (TOS))
5073 CC 01 STL. MP.01 := (TOS)
5075 B9 F6 UJP. JUMP TO 5061
5077 AD 00 RNP. RETURN FROM NON-BASE PROCEDURE.
5079 00 SLDC. PUSH #0000
507A 19 SLDC. PUSH #0019
507B 00 SLDC. PUSH #0000


P070205 Pascal code:

971 8 5:D 1 PROCEDURE P070205( MP01 : INTEGER);
972 8 5:0 0 BEGIN
973 8 5:1 0 GOTOXY( 41, 0);
974 8 5:1 5 WHILE MP01 > 0 DO
975 8 5:2 10 BEGIN
976 8 5:3 10 IF P070203 THEN
977 8 5:4 16 EXIT( P070204);
978 8 5:3 20 MP01 := MP01 - 1;
979 8 5:2 25 END;
980 8 5:0 27 END;


P070206 pcode:

FILE: 7 SEG: 2 PROC: 6

JTAB FOR SUBROUTINE 6:
DATA SIZE: 0
PARAM SIZE: 0
EXIT AT: 50A2
ENTER AT: 5086
PROC NUMBER: 6
LEXICAL LEVEL: 3
5086 01 SLDC. PUSH #0001
5087 B6 01 04 LOD. PUSH ACTREC(-01).04
508A 03 SLDC. PUSH #0003
508B 8E MODI. PUSH ((TOS-1) MOD (TOS))
508C 82 ADI. PUSH ((TOS) + (TOS-1))
508D B8 01 04 STR. ACTREC(-01).04 := TOS
5090 B2 02 03 LDA. PUSH #ACTREC(-02).03
5093 00 SLDC. PUSH #0000
5094 A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
5096 B2 02 03 LDA. PUSH #ACTREC(-02).03
5099 B6 01 04 LOD. PUSH ACTREC(-01).04
509C A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
509E F8 SIND. PUSH (TOS)^.0
509F CD 01 02 CXP. CALL EXTERNAL PROCEDURE: 02 IN SEGMENT: 01
50A2 AD 00 RNP. RETURN FROM NON-BASE PROCEDURE.


P070206 Pascal code:

983 8 6:D 1 PROCEDURE P070206;
984 8 6:D 1
985 8 6:0 0 BEGIN
986 8 6:1 0 MP04 := 1 + (MP04 MOD 3);
987 8 6:1 10 P070002( BUFFER[ 0], BUFFER[ MP04])
988 8 6:0 25 END;


P070207 pcode

FILE: 7 SEG: 2 PROC: 7

JTAB FOR SUBROUTINE 7:
DATA SIZE: 0
PARAM SIZE: 0
EXIT AT: 50CC
ENTER AT: 50AE
PROC NUMBER: 7
LEXICAL LEVEL: 3
50AE 05 SLDC. PUSH #0005
50AF B6 01 05 LOD. PUSH ACTREC(-01).05
50B2 04 SLDC. PUSH #0004
50B3 95 SBI. PUSH ((TOS-1) - (TOS))
50B4 03 SLDC. PUSH #0003
50B5 8E MODI. PUSH ((TOS-1) MOD (TOS))
50B6 82 ADI. PUSH ((TOS) + (TOS-1))
50B7 B8 01 05 STR. ACTREC(-01).05 := TOS
50BA B2 02 03 LDA. PUSH #ACTREC(-02).03
50BD 00 SLDC. PUSH #0000
50BE A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
50C0 B2 02 03 LDA. PUSH #ACTREC(-02).03
50C3 B6 01 05 LOD. PUSH ACTREC(-01).05
50C6 A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
50C8 F8 SIND. PUSH (TOS)^.0
50C9 CD 01 02 CXP. CALL EXTERNAL PROCEDURE: 02 IN SEGMENT: 01
50CC AD 00 RNP. RETURN FROM NON-BASE PROCEDURE.

P070207 Pascal code:

991 8 7:D 1 PROCEDURE P070207;
992 8 7:D 1
993 8 7:0 0 BEGIN
994 8 7:1 0 MP05 := 5 + ((MP05 - 4) MOD 3);
995 8 7:1 12 P070002( BUFFER[ 0], BUFFER[ MP05])
996 8 7:0 27 END;


At this point, all of WIZBOOT and all of TITLELOA are completed.

SYSTEM.STARTUP

Segment: Procedures
0 (1) WIZBOOT 5
1 (7) UTILS 36
2 (8) TITLELOA 8
3 (9) OPTIONS 1

Up next is P070301 (SEGMENT OPTIONS)

TommyGoog

unread,
Apr 19, 2014, 12:22:57 AM4/19/14
to
On Tuesday, April 15, 2014 11:51:01 PM UTC-5, qkumba wrote:

>
> 511A... - looks like LZW decompression routine.
>

Here's something to brighten your day (I hope).

I don't doubt that the code at 511A in Wizardry I is *some* kind of decompression, but there might be a liiiiiittle problem with calling it LZW.

Copyright notice displayed by the software for Wizardry I:

COPYRIGHT (C)1981

LZW described in Wikipedia:

Lempel-Ziv-Welch (LZW) is a universal lossless data compression algorithm created by Abraham Lempel, Jacob Ziv, and Terry Welch. It was published by Welch in 1984 as an improved implementation of the LZ78 algorithm published by Lempel and Ziv in 1978.

The authors of Wizardry were good, but I don't think they were time travelers :)

And I hope by pointing this out I don't discourage you (or anyone else) from participating in this thread!

I'm posting this with a smile!

Tommy

qkumba

unread,
Apr 20, 2014, 6:27:04 PM4/20/14
to
> > 511A... - looks like LZW decompression routine.
> Here's something to brighten your day (I hope).
> I don't doubt that the code at 511A in Wizardry I is *some* kind of
> decompression, but there might be a liiiiiittle problem with calling it LZW.

Yes, I am aware that LZW did not exist at that time.
Originally, I wrote LZ, but then the auto-correct in my brain changed it to LZW, since most everything LZ-like these days is really LZW-based.

> And I hope by pointing this out I don't discourage you (or anyone else) from
> participating in this thread!

:-) No chance of that. I don't mind when people point out that I'm wrong.

Didier VALLET

unread,
Apr 21, 2014, 12:23:35 PM4/21/14
to
>
> And I hope by pointing this out I don't discourage you (or anyone else) from participating in this thread!
>
> I'm posting this with a smile!
>
> Tommy

Hello, it's crazy.
You've done more in a few days than i'm able to understand about your job.
At the start, i've try like you to see if some routines are the same in the 2 versions of wizardry.
And on an other hand, i'm looking to make a new version of wiz3 (and wiz1 first, that's the reason for me to get the source code of it) on Oric1 and Apple based on C code. It's quite difficult to understand where is starting wiz3 code in your source because there's a lot of lines and datas aren't linked to the code.

TommyGoog

unread,
Apr 23, 2014, 8:12:23 PM4/23/14
to
On Monday, April 21, 2014 11:23:35 AM UTC-5, Didier VALLET wrote:
>
>
>
> Hello, it's crazy.
>
> You've done more in a few days than i'm able to understand about your job.
>
> At the start, i've try like you to see if some routines are the same in the 2 versions of wizardry.
>
> And on an other hand, i'm looking to make a new version of wiz3 (and wiz1 first, that's the reason for me to get the source code of it) on Oric1 and Apple based on C code. It's quite difficult to understand where is starting wiz3 code in your source because there's a lot of lines and datas aren't linked to the code.

Hi Didier,

I will assume when you are asking for information about "wiz3 code" that you are referring to my program written in Applesoft Basic to take a diskette with compiled Pascal code and display the formatted pcode output.

At the end of this message I will include the source code for this "wiz3" program. To try to stop confusion about the names I am usually now calling the "wiz3" basic program "DeCompiler" (wiz3 is too similar to Wizardry III or Wizardry3).

The code for DeCompiler is not pretty, and not my best coding effort, but as a small tool it definitely made life easier for me 25 years ago and 2 years ago, and today.

DeCompiler is used to display pcode for any Pascal (1.1) diskette which contains pcode. It is not specifically written for Wizardry diskettes.

There are 2 main paths in the DeCompiler. One is if you want the program to decompile all the files and all the procedures on a diskette. The other main path is if you are interested in one particular decompiled procedure on the diskette.

These 2 mainlines are similar. The code to display "ALL" files starts at line 5100, and for just one file at line 5000.

The routine at line 5 sets up the RWTS routine in memory at $9500 as 6502 code. At line 80 is the subroutine that is called to use RWTS to read a sector of information from the diskette in drive 2 of slot 6. As noted, this routine requires input in the array RW as follows:

RW(0) : Track number
RW(1) : Sector number
RW(2) : Buffer pointer (insignificant byte)
RW(3) : Buffer pointer (significant byte)

At line 100 is code to read the Pascal Directory into memory at $8D00 to $94FF. That's 8 sectors of 256 bytes or 4 Pascal blocks.

http://code.google.com/p/profuse/wiki/PascalFileSystem is a good reference describing the Pascal directory structure.

At line 140 we look in the Pascal directory looking for files. We display the file index and the filename.

At line 200 we get the file index for the file that contains the code you are interested in. (Typing "-1" ends the program.)

Starting at line 210 we use the file index to point to the file entry in the directory. At line 212 we examine this file entry to see if it is either a Pascal Code file, or the file "SYSTEM.LIBRARY". Both of those contain pcode.

At line 217 we display the Track/Sector where the file begins on the diskette. All Pascal files written to a diskette occupy one contiguous sequence of blocks. There are no "gaps" and the file is not spread out in smaller pieces about the diskette. The information for sector is displayed in 2 formats: DOS and PASCAL. DOS and PASCAL use different numbering for sectors.

Line 222 sets up to read the "Segment sector" to $8C00 in memory. This is the "Segment Dictionary" as described in the Apple Pascal Operating System. It is described in the section "Operation of P-machine".

When interested in one procedure, we now ask for the "Segment Number" where the code resides that you are interested in. Entering "-1" takes you back to the previous prompt.

At line 285 we calculate the Track and Sector to be able to read the selected Segment into memory. The comment at line 333 says it is read into the Apples memory at $4000, but that is an old comment. It is actually read into memory at $5000.

At line 375 we ask for the address ($5000 is default) that we want to use for the segments "load address". The segment is always read into memory at $5000, but by typing a different number here, you can get different relative addresses displayed by the DeCompiler. For example, if you enter $5400, then the first instruction will be labeled $5400 and not $5000. Usually you will just want to use the default of $5000.

At line 390 we display the count of the number of PROCEDURES in this SEGMENT.

At line 420 you enter the procedure number you are interested in. Entering "-1" takes you back to a previous prompt.

At line 440 we calculate where the PROCEDURE begins in the segment.

At line 460 we display the JTAB for this procedure.

At line 480 we begin the process of de-compiling the code in the procedure. The variable P8 is similar to the "program counter" and P9 is the contents that we find at the memory pointed to by P8.

From there it is one giant case statement to process each of the different pcode instructions that are possible in P9.

If you (or anyone) needs more help in understanding "wiz3" (DeCompiler) let me know and I will try to help.

Tom Ewers (aka TommyGOOG)

As promised, here is the Basic code for the DeCompiler:

PR#1
]POKE 33,33

]LIST

1 INPUT "PROCESS ALL?";XX$
2 IF XX$ = "Y" THEN 5100
3 GOTO 5000
5 RW = 9 * 16 ^ 3 + 5 * 16 ^ 2
6 HH = 5
7 HM = HH * 16 ^ 3
10 HIMEM: HM
20 DATA A9,95,A0,09,20,D9,03,60,00,01,60,02,00,12,06,1A,95,00,40,00,00,01,00,00,60,01,00,01,EF,D8,00
30 FOR X = 0 TO 29
40 READ A$
60 A1$ = LEFT$ (A$,1)
61 A2$ = RIGHT$ (A$,1)
62 A1 = 16 * ( ASC (A1$) - ASC ("A") + 10)
63 IF A1$ < "A" THEN A1 = 16 * ( ASC (A1$) - ASC ("0"))
64 A2 = ASC (A2$) - ASC ("A") + 10
65 IF A2$ < "A" THEN A2 = ASC (A2$) - ASC ("0")
66 POKE RW + X,A1 + A2
70 NEXT X
71 READ A$
75 RETURN
80 POKE RW + 13,RW(0): REM TRACK NUMBER
81 POKE RW + 14,RW(1): REM SECTOR
82 POKE RW + 17,RW(2): REM LOW BUFF
83 POKE RW + 18,RW(3): REM HIGH BUFF
90 CALL 9 * 16 ^ 3 + 5 * 16 ^ 2
92 RETURN
100 RW(0) = 0
104 Y = 0
105 FOR X = 11 TO 4 STEP - 1
106 RW(1) = X
108 RW(2) = 0
109 RW(3) = 8 * 16 + 13 + Y
110 GOSUB 80
115 Y = Y + 1
120 NEXT X
130 DD = 8 * 16 ^ 3 + 13 * 16 ^ 2: REM DIR AT $8D00
132 FC = PEEK (DD + 16): PRINT "FILES: ";FC
133 FX = 0
135 RETURN
140 PRINT :FQ(1) = 0: REM FQ(1)=HIGHEST FILE INDEX
141 FOR X = 1 TO 77
142 D1 = DD + 26 * X: REM D1 = FILE ENTRY ADDRESS
143 IF FX > = FC THEN 198
145 IF PEEK (D1 + 4) = 0 THEN 198
146 IF PEEK (D1 + 4) > 5 THEN 198
147 PRINT X;" ";
150 FOR Y = 0 TO PEEK (D1 + 6) - 1
155 PRINT CHR$ ( PEEK (D1 + 7 + Y));
160 NEXT Y
161 FX = FX + 1
165 PRINT
167 FQ(1) = X
198 NEXT X: PRINT
199 RETURN
200 REM
201 INPUT "TYPE A FILE NUMBER (OR -1) ";FF
202 PRINT
203 RETURN
210 D1 = DD + 26 * FF: REM D1 POINTS TO FILE ENTRY IN DIRECTOR
211 T2$ = "": FOR X = 0 TO 13:T2$ = T2$ + CHR$ ( PEEK (D1 + 7 + X)): NEXT
212 FQ(4) = 0: IF ( PEEK (D1 + 4) < > 2) AND (T2$ < > "SYSTEM.LIBRARY") THEN FQ(4) = - 1: RETURN : REM FILEKIND, 2==CODEFILE
214 T1 = PEEK (D1 + 0):T2 = PEEK (D1 + 1): REM SEG T/S INFO. 5/3/1 FORMAT
215 T = INT (T1 / 8) + (T2 / 2 < > INT (T2 / 2)) * 32
216 S = (T1 - INT (T1 / 8) * 8) * 2
217 PRINT "SEGMENT T/S = ";T;"/";S;" --- PASCAL T/S"
218 IF S < > 15 AND S < > 0 THEN S = 15 - S
219 PRINT "SEGMENT T/S = ";T;"/";S;" --- DOS 3.3 T/S"
220 PRINT
221 RW(0) = T:RW(1) = S
222 RW(2) = 0:RW(3) = 8 * 16 + 12: REM READ SEGMENT SECTOR TO $8C00
225 GOSUB 80
235 SS = 8 * 16 ^ 3 + 12 * 16 ^ 2: REM SEG DICTIONARY AT BEGIN OF FILE
240 FOR X = 0 TO 15
241 IF PEEK (SS + 4 * X) = 0 AND PEEK (SS + 4 * X + 1) = 0 AND PEEK (SS + 4 * X + 2) = 0 AND PEEK (SS + 4 * X + 3) = 0 THEN 257
242 PRINT X;" ";
243 FQ(2) = X
245 FOR Y = 0 TO 7
250 PRINT CHR$ ( PEEK (SS + 64 + 8 * X + Y));
255 NEXT Y
256 PRINT
257 NEXT X
260 RETURN
280 PRINT
281 INPUT "TYPE A SEGMENT NUMBER (OR -1) ";SS
282 PRINT : IF SS > FQ(2) THEN 281
283 RETURN
285 S1 = 8 * 16 ^ 3 + 12 * 16 ^ 2 + SS * 4: REM CALC SEGMENT POINTER START FOR SS
290 T3 = PEEK (S1)
291 T4 = PEEK (S1 + 1)
295 T5 = T1 + T3
299 T6 = 0
300 IF T5 > 255 THEN T5 = T5 - 256:T6 = 1
302 T6 = T2 + T4 + T6
303 T = INT (T5 / 8) + (T6 / 2 < > INT (T6 / 2)) * 32
304 S = (T5 - INT (T5 / 8) * 8) * 2
305 SD = S
306 IF S < > 15 AND S < > 0 THEN SD = 15 - S
315 PRINT "T/S FOR SEGMENT ";T;"/";S;" --- PASCAL"
316 PRINT "T/S FOR SEGMENT ";T;"/";SD;" --- DOS "
320 LL = PEEK (S1 + 3) + ( PEEK (S1 + 2) < > 0)
322 IF LL = 0 THEN RETURN
330 RW(0) = T
331 RW(1) = SD
332 RW(2) = 0
333 RW(3) = HH * 16: REM $4000 IS WHERE SEGMENT IS READ INTO
340 FOR X = 0 TO (LL - 1)
350 GOSUB 80: REM READ A SECTOR
360 IF RW(1) = 1 THEN RW(1) = 15: GOTO 370
361 IF RW(1) = 15 THEN RW(1) = 0:RW(0) = RW(0) + 1: GOTO 370
362 IF RW(1) = 0 THEN RW(1) = 14: GOTO 370
363 RW(1) = RW(1) - 1
370 RW(3) = RW(3) + 1
371 NEXT : PRINT
372 RETURN
375 INPUT "SEGMENT'S START ADDR($5000):";A$
376 IF LEN (A$) = 0 THEN 387: REM 0 TO ADD TO P8
377 IF LEN (A$) > 4 THEN 375
379 FOR X = 0 TO LEN (A$) - 1
380 A1$ = MID$ (A$, LEN (A$) - X,1)
381 A1 = 16 * ( ASC (A1$) - ASC ("A") + 10)
382 IF A1$ < "A" THEN A1 = 16 * ( ASC (A1$) - ASC ("0"))
383 PA = PA + (16 ^ (X - 1)) * A1
385 NEXT X
386 PA = PA - HM
387 RETURN
390 PRINT
400 P1 = HH * 16 ^ 3 + PEEK (S1 + 3) * 16 ^ 2 + PEEK (S1 + 2) - 1: REM LAST BYTE IN SEGMENT IS "PROCEDURE COUNT"
410 P2 = PEEK (P1): REM P2 = NUMBER OF PROCEDURES IN SEGMENT
412 FQ(3) = P2
415 PRINT "THERE ARE ";P2;" PROCEDURES."
417 PRINT
418 RETURN
420 PRINT
421 INPUT "ENTER PROCEDURE # (OR -1) ";P3: REM P3 = PROCEDURE NUMBER
422 PRINT
425 IF P3 = - 1 THEN RETURN
430 IF P3 > P2 OR P3 < 1 THEN 420
435 RETURN
440 P4 = P1 - 2 * P3 - 1: REM P4=PTR TO SUBROUTINE POINTER
450 P5 = P4 - PEEK (P4 + 1) * 16 ^ 2 - PEEK (P4) - 8: REM P5=PTR TO BEGIN OF SUBROUTINE'S JTAB
459 PRINT
460 PRINT "JTAB FOR SUBROUTINE ";P3;":"
462 PRINT "DATA SIZE: "; PEEK (P5) + PEEK (P5 + 1) * 16 ^ 2
464 PRINT "PARAM SIZE: "; PEEK (P5 + 2) + PEEK (P5 + 3) * 16 ^ 2
465 P6 = P5 + 4 - ( PEEK (P5 + 4) + PEEK (P5 + 5) * 16 ^ 2): REM P6=EXIT ADDRESS
466 PRINT "EXIT AT: ";:A = P6 + PA: GOSUB 550: PRINT A$
467 P7 = P5 + 6 - ( PEEK (P5 + 6) + PEEK (P5 + 7) * 16 ^ 2): REM P7=START ADDRESS
468 PRINT "ENTER AT: ";:A = P7 + PA: GOSUB 550: PRINT A$
470 PRINT "PROC NUMBER: "; PEEK (P5 + 8)
472 IF PEEK (P5 + 8) = 0 THEN PRINT "6502 CODE": RETURN
475 PRINT "LEXICAL LEVEL: "; PEEK (P5 + 9)
477 REM RETURN
480 P8 = P7: REM P8 IS THE PROGRAM-COUNTER
500 P9 = PEEK (P8): REM P9=CONTENTS AT PC
501 IF P8 > = P5 THEN RETURN
502 A = P8 + PA: GOSUB 550: PRINT A$;" ";
503 A = P9: GOSUB 550: PRINT RIGHT$ (A$,2);" ";
510 IF P9 < 128 THEN 1000
520 ON P9 - 127 GOTO 1280,1290,1300,1310,1320,1330,1340,1350,1360,1370,1380,1390,1400,1410,1420,1430,1440,1450,1460,1470,1480,1490,1500,1510,1520,1530,1540,1550,1560,1570,1580,1590,1600,1610,1620,1630,1640,1650,1660,1670,1680,1690,1700
522 ON P9 - 170 GOTO 1710,1720,1730,1740,1750,1760,1770,1780,1790,1800,1810,1820,1830,1840,1850,1860,1870,1880,1890,1900,1910,1920,1930,1940,1950,1960,1970,1980,1990,2000,2010,2020,2030,2040,2050,2060,2070,2080,2090,2100
524 ON P9 - 210 GOTO 2110,2120,2130,2140,2150,2160,2170,2180,2190,2200,2210,2220,2230,2240,2250,2260,2270,2280,2290,2300,2310,2320,2320,2320,2320,2320,2320,2320,2320,2320,2320,2320,2320,2320,2320,2320,2320,2480,2480,2480
526 ON P9 - 250 GOTO 2480,2480,2480,2480,2480
530 PRINT
531 P8 = P8 + 1: GOTO 500
532 P8 = P8 + 2: GOTO 500
533 P8 = P8 + 3: GOTO 500
534 P8 = P8 + 4: GOTO 500
550 REM A=DECIMAL NUMBER. RETURNS A$=4 DIGIT HEX NUMBER
551 A$ = ""
552 FOR A1 = 3 TO 0 STEP - 1
555 A2 = INT (A / 16 ^ A1)
556 A = A - A2 * 16 ^ A1
557 IF A2 > 9 THEN A$ = A$ + CHR$ ( ASC ("A") + A2 - 10): GOTO 559
558 A$ = A$ + CHR$ ( ASC ("0") + A2)
559 NEXT
560 RETURN
1000 PRINT " SLDC. PUSH #";:A = P9: GOSUB 550: PRINT A$
1001 GOTO 531
1100 REM GIVEN B1 DETERMINE "B"IG AND PRINT IT (SPLIT RAW FORMAT)
1102 A = PEEK (B1): GOSUB 550: PRINT RIGHT$ (A$,2);" ";
1103 IF PEEK (B1) > 127 THEN A = PEEK (B1 + 1): GOSUB 550: PRINT RIGHT$ (A$,2);" ";: RETURN
1104 PRINT " ";: RETURN
1119 REM GIVEN B1 DETERMINE "B"IG AND PRINT IT (XX OR XXXX)
1120 B = PEEK (B1): IF B > 127 THEN B = ( PEEK (B1) - 128) * 256 + PEEK (B1 + 1)
1121 A = B: GOSUB 550: PRINT RIGHT$ (A$,2 + 2 * (B > 255));
1122 RETURN
1130 IF B1 > 127 THEN 532
1131 GOTO 531
1280 PRINT SPC( 9);"ABI. PUSH ABS( (TOS) ) )
1281 GOTO 531
1290 PRINT SPC( 9);"ABR. PUSH ABS( (TOS) ) )
1291 GOTO 531
1300 PRINT SPC( 9);"ADI. PUSH ((TOS) + (TOS-1))
1301 GOTO 531
1310 PRINT SPC( 9);"ADR. PUSH ((TOS) + (TOS-1))
1311 GOTO 531
1320 PRINT SPC( 9);"LAND. PUSH ((TOS-1) AND (TOS))
1321 GOTO 531
1330 PRINT SPC( 9);"DIF. DIFFERENCE OF SETS (TOS) AND (TOS-1)": GOTO 531
1340 PRINT SPC( 9);"DVI. PUSH ((TOS-1) / (TOS))
1341 GOTO 531
1350 PRINT SPC( 9);"DVR PUSH ((TOS-1) / (TOS))
1351 GOTO 531
1360 PRINT SPC( 9);"CHK. IF (TOS-1) <= (TOS-2) <= (TOS) THEN PUSH TOS-2 ELSE ERROR
1361 GOTO 531
1370 PRINT SPC( 9);"FLO. PUSH ( REAL(TOS-1))
1371 GOTO 531
1380 PRINT SPC( 9);"FLT. PUSH ( REAL(TOS) )
1381 GOTO 531
1390 PRINT SPC( 9);"INN. PUSH TRUE IF (TOS-1) IN SET (TOS)": GOTO 531
1400 PRINT SPC( 9);"INT. INTERSECTION OF SETS (TOS) AND (TOS-1)": GOTO 531
1410 PRINT SPC( 9);"LOR. PUSH ((TOS-1) OR (TOS))
1411 GOTO 531
1420 PRINT SPC( 9);"MODI. PUSH ((TOS-1) MOD (TOS))
1421 GOTO 531
1430 PRINT SPC( 9);"MPI. PUSH ((TOS-1) * (TOS))
1431 GOTO 531
1440 PRINT SPC( 9);"MPR. PUSH ((TOS) * (TOS))
1441 GOTO 531
1450 PRINT SPC( 9);"NGI. PUSH -(TOS) (2'S COMP)
1451 GOTO 531
1460 PRINT SPC( 9);"NGR. PUSH -(TOS)
1461 GOTO 531
1470 PRINT SPC( 9);"LNOT. PUSH -(TOS) (1'S COMP)
1471 GOTO 531
1480 PRINT SPC( 9);"SRS.": GOTO 2560
1490 PRINT SPC( 9);"SBI. PUSH ((TOS-1) - (TOS))
1491 GOTO 531
1500 PRINT SPC( 9);"SBR. PUSH ((TOS-1) - (TOS))
1501 GOTO 531
1510 PRINT SPC( 9);"SGS.": GOTO 2560
1520 PRINT SPC( 9);"SQI. PUSH ((TOS)**2)
1521 GOTO 531
1530 PRINT SPC( 9);"SQR. PUSH ((TOS)**2)
1531 GOTO 531
1540 PRINT " STO. (TOS-1)^ := (TOS)
1541 GOTO 531
1550 PRINT SPC( 9);"IXS. PUSH #((TOS-1)^.(TOS)) --INDEX STRING ARRAY"
1551 GOTO 531
1560 PRINT SPC( 9);"UNI. UNION OF SETS (TOS) OR (TOS-1)": GOTO 531
1570 A = PEEK (P8 + 1): GOSUB 550: PRINT RIGHT$ (A$,2);" ";
1571 B1 = P8 + 2: GOSUB 1100: PRINT "LDE. PUSH DATASEG(";
1572 A = PEEK (P8 + 1): GOSUB 550: PRINT RIGHT$ (A$,2);").";
1573 B1 = P8 + 2: GOSUB 1100: PRINT
1575 IF PEEK (P8 + 2) > 127 THEN 534
1576 GOTO 533
1580 REM MANY SUB FUNCTIONS TO THIS ONE
1581 GOTO 3000: REM 158
1590 PRINT SPC( 9);"LDCN. PUSH #0000"
1591 GOTO 531
1600 A = PEEK (P8 + 1): GOSUB 550: PRINT RIGHT$ (A$,2); SPC( 7);"ADJ. ";"ADJ SET TO "; RIGHT$ (A$,2);" WORDS"
1601 GOTO 532
1610 A = PEEK (P8 + 1): GOSUB 550: PRINT RIGHT$ (A$,2); SPC( 7);"UJP. IF NOT (TOS) THEN JUMP TO ";
1611 A = PEEK (P8 + 1): IF A < 128 THEN A = P8 + A + 2: GOTO 1615
1612 X = P5 + 8 - 256 + A
1613 A = X - PEEK (X) - PEEK (X + 1) * 256
1615 A = A + PA: GOSUB 550: PRINT A$
1616 GOTO 532
1620 B1 = P8 + 1: GOSUB 1100: PRINT " INC. PUSH #(TOS)^.(";
1621 B1 = P8 + 1: GOSUB 1120: PRINT ")"
1622 IF PEEK (P8 + 1) > 127 THEN 533
1623 GOTO 532
1630 B1 = P8 + 1: GOSUB 1100: PRINT " IND. PUSH (TOS)^.";:B1 = P8 + 1: GOSUB 1120: PRINT
1632 IF PEEK (P8 + 1) > 127 THEN GOTO 533
1633 GOTO 532
1640 B1 = P8 + 1: GOSUB 1100: PRINT " IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=";
1641 B1 = P8 + 1: GOSUB 1120: PRINT
1642 IF PEEK (P8 + 1) > 127 THEN 533
1643 GOTO 532
1650 B1 = P8 + 1: GOSUB 1100: REM PRINT "B"IG WORD
1651 PRINT " LA0. PUSH #BASE.";:B1 = P8 + 1: GOSUB 1120: PRINT
1652 IF PEEK (P8 + 1) > 127 THEN 533
1653 GOTO 532
1660 A = PEEK (P8 + 1): GOSUB 550: PRINT RIGHT$ (A$,2);" ";: IF PEEK (P8 + 1) < = 0 THEN 1662
1661 FOR X = 1 TO PEEK (P8 + 1):A = PEEK (P8 + 1 + X): GOSUB 550: PRINT RIGHT$ (A$,2);" ";: NEXT X
1662 PRINT : FOR X = 1 TO PEEK (P8 + 1): PRINT CHR$ ( PEEK (P8 + 1 + X));: NEXT
1663 PRINT : PRINT SPC( 17);"LSA. PUSH #(PC+1) POINTER TO THE STRING"
1664 P8 = P8 + PEEK (P8 + 1) + 2: GOTO 500
1670 A = PEEK (P8 + 1): GOSUB 550: PRINT RIGHT$ (A$,2);" ";
1671 B1 = P8 + 2: GOSUB 1100: PRINT "LAE. PUSH #DATASEG(";
1672 A = PEEK (P8 + 1): GOSUB 550: PRINT RIGHT$ (A$,2);").";
1673 B1 = P8 + 2: GOSUB 1120: PRINT
1675 IF PEEK (P8 + 2) > 127 THEN 534
1676 GOTO 533
1680 B1 = P8 + 1: GOSUB 1100: PRINT " MOV. (TOS-1)^ := (TOS)^ BT ";
1681 B1 = P8 + 1: GOSUB 1120: PRINT " WORDS"
1683 IF PEEK (P8 + 1) > 127 THEN 533
1684 GOTO 532
1690 B1 = P8 + 1: GOSUB 1100: REM PRINT "B"IG WORD "XX XX " OR "XX " IN RAW FORMAT
1691 PRINT " LDO. PUSH BASE.";:B1 = P8 + 1: GOSUB 1120: PRINT : REM PRINT"B"IG WORD "XXXX" OR "XX"
1692 IF PEEK (P8 + 1) > 127 THEN 533
1693 GOTO 532
1700 A = PEEK (P8 + 1): GOSUB 550: PRINT RIGHT$ (A$,2); SPC( 7);"SAS. TOS=CHAR::(TOS-1)^ := TOS CHAR"
1701 PRINT SPC( 17);" TOS=PTR ::(TOS-1)^ := (TOS)^ "; RIGHT$ (A$,2);" CHARACTERS MAX
1702 GOTO 532
1710 B1 = P8 + 1: GOSUB 1100: REM PRINT "B"IG WORD
1711 PRINT " SRO. BASE.";:B1 = P8 + 1: GOSUB 1120: PRINT " := (TOS)
1712 IF PEEK (P8 + 1) > 127 THEN 533
1713 GOTO 532
1720 PZ = 0: IF P8 / 2 = INT (P8 / 2) THEN A = PEEK (P8 + 1): PRINT "(";: GOSUB 550: PRINT RIGHT$ (A$,2);")";:PZ = 1
1721 W1 = PEEK (P8 + PZ + 1) + PEEK (P8 + PZ + 2) * 256:W2 = PEEK (P8 + PZ + 3) + PEEK (P8 + PZ + 4) * 256:W3 = PEEK (P8 + PZ + 5) + PEEK (P8 + PZ + 6) * 256
1722 FOR X = 1 TO 6:A = PEEK (P8 + PZ + X): GOSUB 550: PRINT RIGHT$ (A$,2);" ";: NEXT X
1723 PRINT "TABLE: ";:PY = 2 * (W2 - W1 + 1): REM # OF BYTES
1724 FOR X = 1 TO PY:A = PEEK (P8 + PZ + 6 + X): GOSUB 550: PRINT RIGHT$ (A$,2);" ";: NEXT X
1725 PRINT : PRINT SPC( 17);"XJP. W1,W2,W3,<TABLE>"
1726 P8 = P8 + PZ + 6 + PY * (PY > 0) + 1: GOTO 500
1730 A = PEEK (P8 + 1): GOSUB 550: PRINT RIGHT$ (A$,2);" "; SPC( 6);
1731 PRINT "RNP. RETURN FROM NON-BASE PROCEDURE."
1732 GOTO 532
1740 A = PEEK (P8 + 1): GOSUB 550: PRINT RIGHT$ (A$,2);" "; SPC( 6);
1741 PRINT "CIP. CALL INTERMEDIATE PROCEDURE: ";: PRINT RIGHT$ (A$,2)
1742 GOTO 532
1750 REM EQU
1751 GOTO 2900
1760 REM GEQ
1761 GOTO 2900
1770 REM GRT
1771 GOTO 2900
1780 A = PEEK (P8 + 1): GOSUB 550: PRINT RIGHT$ (A$,2);" ";
1781 B1 = P8 + 2: GOSUB 1100: REM PRINT "B"IG WORD
1782 PRINT "LDA. PUSH #ACTREC(-";:A = PEEK (P8 + 1): GOSUB 550: PRINT RIGHT$ (A$,2);").";:B1 = P8 + 2: GOSUB 1120: PRINT
1783 IF PEEK (P8 + 2) > 127 THEN 534
1784 GOTO 533
1790 A = PEEK (P8 + 1): GOSUB 550: PRINT RIGHT$ (A$,2);" ";
1791 PZ = 0: IF (P8 + 1) / 2 = INT ((P8 + 1) / 2) THEN A = PEEK (P8 + 1): PRINT "(";: GOSUB 550: PRINT RIGHT$ (A$,2);")";:PZ = 1: REM THE WORD BLOCK IS WORD ALIGNED
1792 X = 2 * PEEK (P8 + 1): REM # BYTES IN BLOCK
1793 FOR Y = 1 TO X:A = PEEK (P8 + PZ + 2 + Y - 1): GOSUB 550: PRINT RIGHT$ (A$,2);" ";: NEXT Y
1794 PRINT : PRINT SPC( 17);"LDC. PUSH ";:A = PEEK (P8 + 1): GOSUB 550: PRINT RIGHT$ (A$,2);" WORDS"
1795 P8 = P8 + PZ + 2 + 2 * PEEK (P8 + 1): GOTO 500
1800 REM LEQ
1801 GOTO 2900
1810 REM LES
1811 GOTO 2900
1820 A = PEEK (P8 + 1): GOSUB 550: PRINT RIGHT$ (A$,2);" ";
1821 B1 = P8 + 2: GOSUB 1100: REM PRINT "B"IG WORD
1822 PRINT "LOD. PUSH ACTREC(-";:A = PEEK (P8 + 1): GOSUB 550: PRINT RIGHT$ (A$,2);").";:B1 = P8 + 2: GOSUB 1120: PRINT
1823 IF PEEK (P8 + 2) > 127 THEN 534
1824 GOTO 533
1830 REM NEQ
1831 GOTO 2900
1840 A = PEEK (P8 + 1): GOSUB 550: PRINT RIGHT$ (A$,2);" ";
1841 B1 = P8 + 2: GOSUB 1100: REM PRINT "B"IG WORD
1842 PRINT "STR. ACTREC(-";:A = PEEK (P8 + 1): GOSUB 550: PRINT RIGHT$ (A$,2);").";:B1 = P8 + 2: GOSUB 1120: PRINT " := TOS"
1843 IF PEEK (P8 + 2) > 127 THEN 534
1844 GOTO 533
1850 A = PEEK (P8 + 1): GOSUB 550: PRINT RIGHT$ (A$,2); SPC( 7);"UJP. JUMP TO ";
1851 A = PEEK (P8 + 1): IF A < 128 THEN A = P8 + A + 2: GOTO 1855
1852 X = P5 + 8 - 256 + A
1853 A = X - PEEK (X) - PEEK (X + 1) * 256
1855 A = A + PA: GOSUB 550: PRINT A$
1856 GOTO 532
1860 PRINT SPC( 9);"LDP. PUSH FIELD. TOS: RTBIT;#;^"
1861 GOTO 531
1870 PRINT SPC( 9);"STP. (TOS-1)^FIELDPTR := TOS. (TOS)-1: RTBIT;#;^"
1871 GOTO 531
1880 A = PEEK (P8 + 1): GOSUB 550: PRINT RIGHT$ (A$,2);" LDM. PUSH "; RIGHT$ (A$,2);" WORDS USING (TOS)^"
1881 GOTO 532
1890 A = PEEK (P8 + 1): GOSUB 550: PRINT RIGHT$ (A$,2);" STM. (TOS-1)^ := (TOS)^ ";: PRINT RIGHT$ (A$,2);" WORDS"
1891 GOTO 532
1900 PRINT SPC( 9);"LDB. PUSH #00, PUSH (TOS-1)^.TOS BYTE"
1901 GOTO 531
1910 PRINT SPC( 9);"STB. (TOS-2)^.(TOS-1) := TOS BYTE"
1911 GOTO 531
1920 A = PEEK (P8 + 1): GOSUB 550: PRINT RIGHT$ (A$,2);" ";
1921 A = PEEK (P8 + 2): GOSUB 550: PRINT RIGHT$ (A$,2);" ";
1922 PRINT " IXP. INDEX PACKED ARRAY. USE TOS & TOS-1, UB1, UB2. ???"
1923 GOTO 533
1930 A = PEEK (P8 + 1): GOSUB 550: PRINT RIGHT$ (A$,2);" "; SPC( 6);
1931 PRINT "RBP. RETURN FROM BASE PROCEDURE."
1932 GOTO 532
1940 A = PEEK (P8 + 1): GOSUB 550: PRINT RIGHT$ (A$,2);" "; SPC( 6);
1941 PRINT "CBP. CALL BASE PROCEDURE: "; RIGHT$ (A$,2)
1942 GOTO 532
1950 PRINT SPC( 9);"EQUI. PUSH ((TOS-1) = (TOS))
1951 GOTO 531
1960 PRINT SPC( 9);"GEQI. PUSH ((TOS-1) >= (TOS))
1961 GOTO 531
1970 PRINT SPC( 9);"GRTI. PUSH ((TOS-1) > (TOS))
1971 GOTO 531
1980 REM
1981 B1 = P8 + 1: GOSUB 1100: REM PRINT "B"IG WORD
1984 PRINT " LLA. PUSH #MP.";:B1 = P8 + 1: GOSUB 1120: PRINT
1985 IF PEEK (P8 + 1) > 127 THEN GOTO 533
1986 GOTO 532
1990 A = PEEK (P8 + 1): GOSUB 550: PRINT RIGHT$ (A$,2);" ";
1991 A = PEEK (P8 + 2): GOSUB 550: PRINT RIGHT$ (A$,2);" ";
1992 PRINT " LDCI. PUSH #";
1993 A = PEEK (P8 + 2) * 256 + PEEK (P8 + 1): GOSUB 550: PRINT A$
1994 GOTO 533
2000 PRINT SPC( 9);"LEQI. PUSH ((TOS-1) <= (TOS))
2001 GOTO 531
2010 PRINT SPC( 9);"LESI. PUSH ((TOS-1) < (TOS))
2011 GOTO 531
2020 B1 = P8 + 1: GOSUB 1100: REM PRINT "B"IG
2022 PRINT " LDL. PUSH MP.";:B1 = P8 + 1: GOSUB 1120: PRINT
2025 IF PEEK (P8 + 1) > 127 THEN GOTO 533
2026 GOTO 532
2030 PRINT SPC( 9);"NEQI. PUSH ((TOS-1) <> (TOS))
2031 GOTO 531
2040 B1 = P8 + 1: GOSUB 1100: REM PRINT "B"IG
2041 PRINT " STL. MP.";:B1 = P8 + 1: GOSUB 1120: PRINT " := (TOS)"
2045 IF PEEK (P8 + 1) > 127 THEN GOTO 533
2046 GOTO 532
2050 A = PEEK (P8 + 1): GOSUB 550: PRINT RIGHT$ (A$,2);" ";:A = PEEK (P8 + 2): GOSUB 550: PRINT RIGHT$ (A$,2);" ";
2051 PRINT "CXP. CALL EXTERNAL PROCEDURE: "; RIGHT$ (A$,2);" IN SEGMENT: ";:A = PEEK (P8 + 1): GOSUB 550: PRINT RIGHT$ (A$,2)
2052 GOTO 533
2060 A = PEEK (P8 + 1): GOSUB 550: PRINT RIGHT$ (A$,2);" "; SPC( 6);
2061 PRINT "CLP. CALL CHILD PROCEDURE: "; RIGHT$ (A$,2)
2062 GOTO 532
2070 A = PEEK (P8 + 1): GOSUB 550: PRINT RIGHT$ (A$,2);" "; SPC( 6);
2071 PRINT "CGP. CALL GLOBAL PROCEDURE: "; RIGHT$ (A$,2)
2072 GOTO 532
2080 A = PEEK (P8 + 1): GOSUB 550: PRINT RIGHT$ (A$,2);" ";
2081 FOR X = 1 TO PEEK (P8 + 1):A = PEEK (P8 + 1 + X): GOSUB 550: PRINT RIGHT$ (A$,2);" ";: NEXT X
2082 PRINT : PRINT SPC( 17);"LPA. PUSH #(PC+2) POINTER TO THE ARRAY"
2083 P8 = P8 + PEEK (P8 + 1) + 2: GOTO 500
2090 A = PEEK (P8 + 1): GOSUB 550: PRINT RIGHT$ (A$,2);" ";
2091 B1 = P8 + 2: GOSUB 1100: PRINT "STE. DATASEG(";
2092 A = PEEK (P8 + 1): GOSUB 550: PRINT RIGHT$ (A$,2);").";
2093 B1 = P8 + 2: GOSUB 1120: PRINT " := (TOS)
2095 IF PEEK (P8 + 2) > 127 THEN 534
2096 GOTO 533
2100 PRINT SPC( 9);"???.": GOTO 2560
2110 PRINT SPC( 9);"EFJ.": GOTO 2560
2120 PRINT SPC( 9);"NFJ.": GOTO 2560
2130 B1 = P8 + 1: GOSUB 1100: PRINT "BPT. BREAKPOINT"
2131 IF PEEK (P8 + 1) > 127 THEN 533
2132 GOTO 532
2140 PRINT SPC( 9);"XIT. EXIT OPERATING SYSTEM."
2141 GOTO 531
2150 PRINT SPC( 9);"NOP. NOP"
2151 GOTO 531
2160 X = 1
2161 GOTO 2311
2170 X = 2
2171 GOTO 2311
2180 X = 3
2181 GOTO 2311
2190 X = 4
2191 GOTO 2311
2200 X = 5
2201 GOTO 2311
2210 X = 6
2211 GOTO 2311
2220 X = 7
2221 GOTO 2311
2230 X = 8
2231 GOTO 2311
2240 X = 9
2241 GOTO 2311
2250 X = 10
2251 GOTO 2311
2260 X = 11
2261 GOTO 2311
2270 X = 12
2271 GOTO 2311
2280 X = 13
2281 GOTO 2311
2290 X = 14
2291 GOTO 2311
2300 X = 15
2301 GOTO 2311
2310 X = 16
2311 PRINT SPC( 9);"SLDL.";:A = X: GOSUB 550: PRINT " PUSH MP."; RIGHT$ (A$,2)
2312 GOTO 531
2320 REM
2473 PRINT SPC( 9);"SLDO. PUSH BASE.";:A = P9 - 231: GOSUB 550: PRINT RIGHT$ (A$,2)
2475 GOTO 531
2480 REM
2550 X = P9 - 248
2552 PRINT SPC( 9);"SIND. PUSH (TOS)^.";X
2553 GOTO 531
2560 PRINT "NOT IMPLEMENTED.": GOTO 1531
2900 A = PEEK (P8 + 1): GOSUB 550: PRINT RIGHT$ (A$,2);: GOSUB 2930
2901 IF P9 = 175 THEN PRINT "EQU. PUSH ((TOS-1) = (TOS))";
2902 IF P9 = 183 THEN PRINT "NEQ. PUSH ((TOS-1) <> (TOS))";
2903 IF P9 = 180 THEN PRINT "LEQ. PUSH ((TOS-1) <= (TOS))";
2904 IF P9 = 181 THEN PRINT "LES. PUSH ((TOS-1) < (TOS))";
2905 IF P9 = 176 THEN PRINT "GEQ. PUSH ((TOS-1) >= (TOS))";
2906 IF P9 = 177 THEN PRINT "GRT. PUSH ((TOS-1) > (TOS))";
2910 A = PEEK (P8 + 1)
2911 IF A = 2 THEN PRINT "--REALS"
2912 IF A = 4 THEN PRINT "--STRINGS"
2913 IF A = 6 THEN PRINT "--BOOLEANS"
2914 IF A = 8 THEN PRINT "--SETS"
2915 IF A = 10 THEN PRINT "--BYTE ARRAYS"
2916 IF A = 12 THEN PRINT "--WORDS"
2918 IF A = 10 OR A = 12 THEN 1575
2920 GOTO 532
2930 A = PEEK (P8 + 1)
2932 IF A = 10 OR A = 12 THEN 2935
2934 PRINT SPC( 7);: RETURN
2935 PRINT " ";:B1 = P8 + 2: GOSUB 1100
2937 RETURN
3000 REM 158
3002 A = PEEK (P8 + 1): GOSUB 550: PRINT RIGHT$ (A$,2);" "; SPC( 6);
3004 X = PEEK (P8 + 1): IF X < > 1 THEN 3010
3005 PRINT "NEW. (TOS) HAS # WORDS, (TOS-1)^ IS THE VARIABLE
3006 GOTO 532
3010 IF X < > 32 THEN 3020
3011 PRINT "MRK. MARK HEAP. (TOS)^ := NP"
3012 GOTO 532
3020 IF X < > 33 THEN 3030
3021 PRINT "RLS. RELEASE HEAP. NP := (TOS)^"
3022 GOTO 532
3030 IF X < > 22 THEN 3040
3031 PRINT "DUSE. DEC USE CNT FOR SEG #(TOS)
3032 GOTO 532
3040 IF X < > 23 THEN 3050
3041 PRINT "RND. PUSH ROUND(TOS)
3042 GOTO 532
3050 IF X < > 35 THEN 3060
3051 PRINT "POT. POWER OF TEN. PUSH 10^(TOS)
3052 GOTO 532
3060 IF X < > 4 THEN 3070
3061 PRINT "EXIT. EXIT FROM PROCEDURE. (TOS)=PROC# (TOS-1)=SEG#
3062 GOTO 532
3070 IF X < > 10 THEN 3080
3071 PRINT "FLC. FILLCHAR (DST+IDX,LEN,CHR)"
3072 GOTO 532
3080 IF X < > 11 THEN 3090
3081 PRINT "SCAN. SCAN???"
3082 GOTO 532
3090 IF X < > 02 THEN 3100
3091 PRINT "MVL. MOVELEFT"
3092 GOTO 532
3100 IF X < > 3 THEN 3110
3101 PRINT "MVR. MOVERIGHT"
3102 GOTO 532
3110 IF X < > 9 THEN 3130
3111 PRINT "TIM. TIME."
3112 GOTO 532
3120 PRINT "AN UNKNOWN STANDARD PROCEDURE ?!?!"
3121 GOTO 532
3130 IF X < > 5 THEN 3140
3131 PRINT "READ FROM VOL#"
3132 GOTO 532
3140 IF X < > 21 THEN 3150
3141 PRINT "RSEG. READ SEGMENT #(TOS) FROM ACTIVE SEG TABLE"
3142 GOTO 532
3150 IF X < > 38 THEN 3160
3151 PRINT "CHECK VOL#"
3152 GOTO 532
3160 IF X < > 6 THEN 3170
3161 PRINT "WRITE VOL#"
3162 GOTO 532
3170 IF X < > 34 THEN 3120
3171 PRINT "PUSH I/O ERROR STATUS"
3172 GOTO 532
5000 GOSUB 5: REM SET UP RWTS
5005 GOSUB 100: REM READ DIR
5010 GOSUB 140: REM DISP FNAMES
5015 GOSUB 200: REM GET FILE #
5017 IF FF = - 1 THEN END
5020 GOSUB 210: REM USE FILE#
5025 GOSUB 280: REM GET SEG#
5026 IF SS = - 1 THEN 5010
5030 GOSUB 285
5031 IF LL = 0 THEN 5025
5032 PA = 0
5035 GOSUB 375: REM GET $4000
5037 GOSUB 390
5040 GOSUB 420: REM GET PROC#
5042 IF P3 = - 1 THEN 5020
5045 GOSUB 440
5047 REM GOSUB 480
5048 GOTO 5040
5090 REM ----------
5100 GOSUB 5
5105 GOSUB 100
5110 GOSUB 140
5115 FOR FF = 1 TO FQ(1)
5120 GOSUB 210
5122 IF FQ(4) = - 1 THEN 5154
5125 FOR SS = 0 TO FQ(2)
5130 GOSUB 285
5131 IF LL = 0 THEN 5152
5132 PA = 0
5134 GOSUB 390
5140 FOR P3 = 1 TO FQ(3)
5142 PRINT : PRINT "FILE: ";FF;" SEG: ";SS;" PROC: ";P3
5145 GOSUB 440
5150 NEXT P3
5152 NEXT SS
5154 NEXT FF
5160 END

]


Tommy

Didier VALLET

unread,
Apr 23, 2014, 9:23:41 PM4/23/14
to
Le jeudi 24 avril 2014 02:12:23 UTC+2, TommyGoog a écrit :
> On Monday, April 21, 2014 11:23:35 AM UTC-5, Didier VALLET wrote:
>
>
> I will assume when you are asking for information about "wiz3 code" that you are referring to my program written in Applesoft Basic to take a diskette with compiled Pascal code and display the formatted pcode output.
>
> At the end of this message I will include the source code for this "wiz3" program. To try to stop confusion about the names I am usually now calling the "wiz3" basic program "DeCompiler" (wiz3 is too similar to Wizardry III or Wizardry3).
>

Ooops, Ok
I didn't intend to remake another wiz3. Your process is working well and it will be loosing time to work on another tool like it.
Thanks for your informations about the process. It will help me to understand the datas.

I'm sorry for the confusion (linked to my low english level). I wanted to :
- looking at the pcode of Wizardry 1 and i saw that you're quicker than me in this.
- working on a "remake" on 8bits computers of the Wizardry serie (in C and 6502 code) starting on the original code (and your work on Wizardry 3 is a "gift") because halito and other works were a great piece of time when i was younger.

Didier

TommyGoog

unread,
Apr 23, 2014, 10:12:23 PM4/23/14
to

>
>
> Up next is P070301 (SEGMENT OPTIONS)

With this posting I complete the decompiling for STARTUP.CODE.

P070301 pcode (SEGMENT OPTIONS)

This is the pcode we want to generate:

FILE: 7 SEG: 3 PROC: 1

JTAB FOR SUBROUTINE 1:
DATA SIZE: 512
PARAM SIZE: 0
EXIT AT: 5227
ENTER AT: 5000
PROC NUMBER: 1
LEXICAL LEVEL: 1
5000 04 SLDC. PUSH #0004
5001 C6 01 LLA. PUSH #MP.01
5003 00 SLDC. PUSH #0000
5004 C7 00 02 LDCI. PUSH #0200
5007 00 SLDC. PUSH #0000
5008 00 SLDC. PUSH #0000
5009 9E 05 READ FROM VOL#
500B 04 SLDC. PUSH #0004
500C C6 01 LLA. PUSH #MP.01
500E 00 SLDC. PUSH #0000
500F C7 00 02 LDCI. PUSH #0200
5012 00 SLDC. PUSH #0000
5013 00 SLDC. PUSH #0000
5014 9E 06 WRITE VOL#
5016 9E 22 PUSH I/O ERROR STATUS
5018 10 SLDC. PUSH #0010
5019 CB NEQI. PUSH ((TOS-1) <> (TOS))
501A A1 03 UJP. IF NOT (TOS) THEN JUMP TO 501F
501C 00 SLDC. PUSH #0000
501D A1 F6 UJP. IF NOT (TOS) THEN JUMP TO 501C
501F B6 02 03 LOD. PUSH ACTREC(-02).03
5022 0C SLDC. PUSH #000C
5023 00 SLDC. PUSH #0000
5024 CD 00 11 CXP. CALL EXTERNAL PROCEDURE: 11 IN SEGMENT: 00
5027 B6 02 03 LOD. PUSH ACTREC(-02).03
502A D7 NOP. NOP
502B A6 28 43 4F 50 59 52 49 47 48 54 20 28 43 29 31 39 38 31 20 41 4C 4C 20 52 49 47 48 54 53 20 52 45 53 45 52 56 45 44 20 42 59
COPYRIGHT (C)1981 ALL RIGHTS RESERVED BY
LSA. PUSH #(PC+1) POINTER TO THE STRING
5055 00 SLDC. PUSH #0000
5056 CD 00 13 CXP. CALL EXTERNAL PROCEDURE: 13 IN SEGMENT: 00
5059 B6 02 03 LOD. PUSH ACTREC(-02).03
505C CD 00 16 CXP. CALL EXTERNAL PROCEDURE: 16 IN SEGMENT: 00
505F B6 02 03 LOD. PUSH ACTREC(-02).03
5062 D7 NOP. NOP
5063 A6 28 41 4E 44 52 45 57 20 47 52 45 45 4E 42 45 52 47 2C 20 49 4E 43 20 26 20 52 4F 42 45 52 54 20 57 4F 4F 44 48 45 41 44 2C
ANDREW GREENBERG, INC & ROBERT WOODHEAD,
LSA. PUSH #(PC+1) POINTER TO THE STRING
508D 00 SLDC. PUSH #0000
508E CD 00 13 CXP. CALL EXTERNAL PROCEDURE: 13 IN SEGMENT: 00
5091 B6 02 03 LOD. PUSH ACTREC(-02).03
5094 CD 00 16 CXP. CALL EXTERNAL PROCEDURE: 16 IN SEGMENT: 00
5097 B6 02 03 LOD. PUSH ACTREC(-02).03
509A D7 NOP. NOP
509B A6 28 49 4E 43 2E 20 20 54 48 49 53 20 50 52 4F 47 52 41 4D 20 20 49 53 20 20 50 52 4F 54 45 43 54 45 44 20 20 55 4E 44 45 52
INC. THIS PROGRAM IS PROTECTED UNDER
LSA. PUSH #(PC+1) POINTER TO THE STRING
50C5 00 SLDC. PUSH #0000
50C6 CD 00 13 CXP. CALL EXTERNAL PROCEDURE: 13 IN SEGMENT: 00
50C9 B6 02 03 LOD. PUSH ACTREC(-02).03
50CC CD 00 16 CXP. CALL EXTERNAL PROCEDURE: 16 IN SEGMENT: 00
50CF B6 02 03 LOD. PUSH ACTREC(-02).03
50D2 D7 NOP. NOP
50D3 A6 28 54 48 45 20 4C 41 57 53 20 4F 46 20 54 48 45 20 55 4E 49 54 45 44 20 53 54 41 54 45 53 20 20 41 4E 44 20 4F 54 48 45 52
THE LAWS OF THE UNITED STATES AND OTHER
LSA. PUSH #(PC+1) POINTER TO THE STRING
50FD 00 SLDC. PUSH #0000
50FE CD 00 13 CXP. CALL EXTERNAL PROCEDURE: 13 IN SEGMENT: 00
5101 B6 02 03 LOD. PUSH ACTREC(-02).03
5104 CD 00 16 CXP. CALL EXTERNAL PROCEDURE: 16 IN SEGMENT: 00
5107 B6 02 03 LOD. PUSH ACTREC(-02).03
510A D7 NOP. NOP
510B A6 28 43 4F 55 4E 54 52 49 45 53 2C 20 20 41 4E 44 20 49 4C 4C 45 47 41 4C 20 44 49 53 54 52 49 42 55 54 49 4F 4E 20 4D 41 59
COUNTRIES, AND ILLEGAL DISTRIBUTION MAY
LSA. PUSH #(PC+1) POINTER TO THE STRING
5135 00 SLDC. PUSH #0000
5136 CD 00 13 CXP. CALL EXTERNAL PROCEDURE: 13 IN SEGMENT: 00
5139 B6 02 03 LOD. PUSH ACTREC(-02).03
513C CD 00 16 CXP. CALL EXTERNAL PROCEDURE: 16 IN SEGMENT: 00
513F B6 02 03 LOD. PUSH ACTREC(-02).03
5142 D7 NOP. NOP
5143 A6 28 52 45 53 55 4C 54 20 49 4E 20 43 49 56 49 4C 20 20 4C 49 41 42 49 4C 49 54 59 20 20 41 4E 44 20 43 52 49 4D 49 4E 41 4C
RESULT IN CIVIL LIABILITY AND CRIMINAL
LSA. PUSH #(PC+1) POINTER TO THE STRING
516D 00 SLDC. PUSH #0000
516E CD 00 13 CXP. CALL EXTERNAL PROCEDURE: 13 IN SEGMENT: 00
5171 B6 02 03 LOD. PUSH ACTREC(-02).03
5174 CD 00 16 CXP. CALL EXTERNAL PROCEDURE: 16 IN SEGMENT: 00
5177 B6 02 03 LOD. PUSH ACTREC(-02).03
517A D7 NOP. NOP
517B A6 0C 50 52 4F 53 45 43 55 54 49 4F 4E 2E
PROSECUTION.
LSA. PUSH #(PC+1) POINTER TO THE STRING
5189 00 SLDC. PUSH #0000
518A CD 00 13 CXP. CALL EXTERNAL PROCEDURE: 13 IN SEGMENT: 00
518D B6 02 03 LOD. PUSH ACTREC(-02).03
5190 CD 00 16 CXP. CALL EXTERNAL PROCEDURE: 16 IN SEGMENT: 00
5193 00 SLDC. PUSH #0000
5194 0C SLDC. PUSH #000C
5195 CD 00 1D CXP. CALL EXTERNAL PROCEDURE: 1D IN SEGMENT: 00
5198 B6 02 03 LOD. PUSH ACTREC(-02).03
519B A6 20 20 20 56 45 52 53 49 4F 4E 20 32 2E 31 20 4F 46 20 32 32 2D 4A 41 4E 2D 38 32 20 20 53 45 52 3A
VERSION 2.1 OF 22-JAN-82 SER:
LSA. PUSH #(PC+1) POINTER TO THE STRING
51BD D7 NOP. NOP
51BE 00 SLDC. PUSH #0000
51BF CD 00 13 CXP. CALL EXTERNAL PROCEDURE: 13 IN SEGMENT: 00
51C2 B6 02 03 LOD. PUSH ACTREC(-02).03
51C5 A5 05 LA0. PUSH #BASE.05
51C7 00 SLDC. PUSH #0000
51C8 CD 00 13 CXP. CALL EXTERNAL PROCEDURE: 13 IN SEGMENT: 00
51CB B6 02 03 LOD. PUSH ACTREC(-02).03
51CE CD 00 16 CXP. CALL EXTERNAL PROCEDURE: 16 IN SEGMENT: 00
51D1 B6 02 03 LOD. PUSH ACTREC(-02).03
51D4 CD 00 16 CXP. CALL EXTERNAL PROCEDURE: 16 IN SEGMENT: 00
51D7 B6 02 03 LOD. PUSH ACTREC(-02).03
51DA CD 00 16 CXP. CALL EXTERNAL PROCEDURE: 16 IN SEGMENT: 00
51DD B6 02 03 LOD. PUSH ACTREC(-02).03
51E0 D7 NOP. NOP
51E1 A6 26 20 20 53 29 54 41 52 54 20 47 41 4D 45 20 20 55 29 54 49 4C 49 54 49 45 53 20 20 54 29 49 54 4C 45 20 50 41 47 45
S)TART GAME U)TILITIES T)ITLE PAGE
LSA. PUSH #(PC+1) POINTER TO THE STRING
5209 00 SLDC. PUSH #0000
520A CD 00 13 CXP. CALL EXTERNAL PROCEDURE: 13 IN SEGMENT: 00
520D 29 SLDC. PUSH #0029
520E 00 SLDC. PUSH #0000
520F CD 00 1D CXP. CALL EXTERNAL PROCEDURE: 1D IN SEGMENT: 00
5212 B6 02 02 LOD. PUSH ACTREC(-02).02
5215 A5 03 LA0. PUSH #BASE.03
5217 CD 00 10 CXP. CALL EXTERNAL PROCEDURE: 10 IN SEGMENT: 00
521A EA SLDO. PUSH BASE.03
521B 53 SLDC. PUSH #0053
521C C3 EQUI. PUSH ((TOS-1) = (TOS))
521D EA SLDO. PUSH BASE.03
521E 55 SLDC. PUSH #0055
521F C3 EQUI. PUSH ((TOS-1) = (TOS))
5220 8D LOR. PUSH ((TOS-1) OR (TOS))
5221 EA SLDO. PUSH BASE.03
5222 54 SLDC. PUSH #0054
5223 C3 EQUI. PUSH ((TOS-1) = (TOS))
5224 8D LOR. PUSH ((TOS-1) OR (TOS))
5225 A1 F4 UJP. IF NOT (TOS) THEN JUMP TO 520D
5227 AD 00 RNP. RETURN FROM NON-BASE PROCEDURE.
5229 00 SLDC. PUSH #0000
522A 1D SLDC. PUSH #001D
522B 00 SLDC. PUSH #0000
522C 10 SLDC. PUSH #0010
522D 02 SLDC. PUSH #0002

PROCEDURE OPTIONS is different from the one in LOL.

WRITELN( BASE05) is not correct.
I currently have it called RANDNUM, but see now that it is the SERIALNM
This is my first draft after changing BASE05 to RANDNUM.

1152 9 1:D 1 SEGMENT PROCEDURE OPTIONS; (* P050301 *)
1153 9 1:D 1
1154 9 1:D 1 CONST
1155 9 1:D 1 HOMECLR = 12;
1156 9 1:D 1
1157 9 1:D 1 VAR
1158 9 1:D 1 IOBUFF : PACKED ARRAY[ 0..511] OF CHAR;
1159 9 1:D 257
1160 9 1:0 0 BEGIN
1161 9 1:0 0
1162 9 1:1 0 UNITREAD( DRIVE1, IOBUFF, SIZEOF( IOBUFF), 0, 0);
1163 9 1:1 11 UNITWRITE( DRIVE1, IOBUFF, SIZEOF( IOBUFF), 0, 0);
1164 9 1:1 22
1165 9 1:1 22 IF IORESULT = 16 THEN
1166 9 1:2 28 REPEAT
1167 9 1:2 28 UNTIL FALSE;
1168 9 1:2 31
1169 9 1:1 31 WRITE( CHR( HOMECLR));
1170 9 1:1 39
1171 9 1:1 39 WRITELN( 'COPYRIGHT (C)1981 ALL RIGHTS RESERVED BY');
1172 9 1:1 95 WRITELN( 'ANDREW GREENBERG, INC & ROBERT WOODHEAD,');
1173 9 1:1 151 WRITELN( 'INC. THIS PROGRAM IS PROTECTED UNDER');
1174 9 1:1 204 WRITELN( 'THE LAWS OF THE UNITED STATES AND OTHER');
1175 9 1:1 259 WRITELN( 'COUNTRIES, AND ILLEGAL DISTRIBUTION MAY');
1176 9 1:1 314 WRITELN( 'RESULT IN CIVIL LIABILITY AND CRIMINAL');
1177 9 1:1 368 WRITELN( 'PROSECUTION.');
1178 9 1:1 396
1179 9 1:1 396 GOTOXY( 0, 12);
1180 9 1:1 401
1181 9 1:1 401 WRITE( ' VERSION 2.1 OF 22-JAN-82 SER:');
1182 9 1:1 444 WRITELN( RANDNUM);
1183 9 1:1 458 WRITELN;
1184 9 1:1 464 WRITELN;
1185 9 1:1 470
1186 9 1:1 470 WRITE( 'S)TART GAME U)TILITIES T)ITLE PAGE');
1187 9 1:1 514
1188 9 1:1 514 REPEAT
1189 9 1:2 514 GOTOXY( 41,0);
1190 9 1:2 519 READ( INCHAR)
1191 9 1:1 527 UNTIL (INCHAR = 'S') OR (INCHAR = 'T') OR (INCHAR = 'U')
1192 9 1:1 537
1193 9 1:1 537 (*
1194 9 1:1 537 GOTOXY( 0, 12);
1195 9 1:1 537 WRITE( 'VERSION -4- OF 04-MAY-83 SER:');
1196 9 1:1 537 WRITELN( SERIAL);
1197 9 1:1 537 WRITELN;
1198 9 1:1 537 WRITELN;
1199 9 1:1 537 WRITELN( ' S)TART GAME U)TILITIES' : 32);
1200 9 1:1 537 WRITELN;
1201 9 1:1 537 WRITELN( 'NOTE - YOU MUST MAKE AN ADDITIONAL SCEN-');
1202 9 1:1 537 WRITELN( ' ARIO DISKETTE BEFORE PLAYING. SEE');
1203 9 1:1 537 WRITE ( ' THE MANUAL FOR MORE DETAILS.' );
1204 9 1:1 537
1205 9 1:1 537 REPEAT
1206 9 1:1 537 GOTOXY( 41, 0);
1207 9 1:1 537 READ( INCHAR)
1208 9 1:1 537 UNTIL (INCHAR = 'S') OR (INCHAR = 'U')
1209 9 1:1 537
1210 9 1:1 537 *)
1211 9 1:0 537 END;

Sometimes first drafts have lots of errors, so now it is time to fix them.

Change "IORESULT = 16" to "IORESULT <> 16".

Change "INC. THIS PROGRAM IS PROTECTED UNDER" to "INC. THIS PROGRAM IS PROTECTED UNDER"

(Add some spaces to the message.)

change "THE LAWS OF THE UNITED STATES AND OTHER" to "THE LAWS OF THE UNITED STATES AND OTHER"
change "COUNTRIES, AND ILLEGAL DISTRIBUTION MAY" to "COUNTRIES, AND ILLEGAL DISTRIBUTION MAY
change "RESULT IN CIVIL LIABILITY AND CRIMINAL" to "RESULT IN CIVIL LIABILITY AND CRIMINAL"
change " VERSION 2.1 OF 22-JAN-82 SER:" to " VERSION 2.1 OF 22-JAN-82 SER:"

Now my code says it is using BASE.04 and not BASE.05.
For some reason the routine is generated as PROCEDURE: 0D instead of PROCEDURE: 13.
change "'S)TART GAME U)TILITIES T)ITLE PAGE'" to "' S)TART GAME U)TILITIES T)ITLE PAGE'
Remove temporary comments and old code from LOL.
53 55 54
The order in the UNTIL statement is "S" "U" "T".
Change RANDNUM to SERIAL

Final rough draft for P070301.

1151 8 1:0 104 (*$I WIZ1E:OPTIONS *)
1152 9 1:D 1 SEGMENT PROCEDURE OPTIONS; (* P070301 *)
1153 9 1:D 1
1154 9 1:D 1 CONST
1155 9 1:D 1 HOMECLR = 12;
1156 9 1:D 1
1157 9 1:D 1 VAR
1158 9 1:D 1 IOBUFF : PACKED ARRAY[ 0..511] OF CHAR;
1159 9 1:D 257
1160 9 1:0 0 BEGIN
1161 9 1:0 0
1162 9 1:1 0 UNITREAD( DRIVE1, IOBUFF, SIZEOF( IOBUFF), 0, 0);
1163 9 1:1 11 UNITWRITE( DRIVE1, IOBUFF, SIZEOF( IOBUFF), 0, 0);
1164 9 1:1 22
1165 9 1:1 22 IF IORESULT <> 16 THEN
1166 9 1:2 28 REPEAT
1167 9 1:2 28 UNTIL FALSE;
1168 9 1:2 31
1169 9 1:1 31 WRITE( CHR( HOMECLR));
1170 9 1:1 39
1171 9 1:1 39 WRITELN( 'COPYRIGHT (C)1981 ALL RIGHTS RESERVED BY');
1172 9 1:1 95 WRITELN( 'ANDREW GREENBERG, INC & ROBERT WOODHEAD,');
1173 9 1:1 151 WRITELN( 'INC. THIS PROGRAM IS PROTECTED UNDER');
1174 9 1:1 207 WRITELN( 'THE LAWS OF THE UNITED STATES AND OTHER');
1175 9 1:1 263 WRITELN( 'COUNTRIES, AND ILLEGAL DISTRIBUTION MAY');
1176 9 1:1 319 WRITELN( 'RESULT IN CIVIL LIABILITY AND CRIMINAL');
1177 9 1:1 375 WRITELN( 'PROSECUTION.');
1178 9 1:1 403
1179 9 1:1 403 GOTOXY( 0, 12);
1180 9 1:1 408
1181 9 1:1 408 WRITE( ' VERSION 2.1 OF 22-JAN-82 SER:');
1182 9 1:1 450 WRITELN( SERIAL);
1183 9 1:1 465 WRITELN;
1184 9 1:1 471 WRITELN;
1185 9 1:1 477
1186 9 1:1 477 WRITE( ' S)TART GAME U)TILITIES T)ITLE PAGE');
1187 9 1:1 525
1188 9 1:1 525 REPEAT
1189 9 1:2 525 GOTOXY( 41,0);
1190 9 1:2 530 READ( INCHAR)
1191 9 1:1 538 UNTIL (INCHAR = 'S') OR (INCHAR = 'U') OR (INCHAR = 'T')
1192 9 1:0 548 END;
1193 9 1:0 568 (*$I WIZ1E:OPTIONS *)

Once you're done re-engineering the Pascal code you look at it and say, "That looks obvious. What took you so long?"

Note, IORESULT = 16 means the diskette is write-protected.
So this check is making sure the diskette IS write protected, else it goes into an infinite loop. Trying to stop software pirates.

Next up is SEGMENT UTILS (P070101);

STARTUP.TEXT
$I WIZ1E:WIZUTILB
$I WIZ1E:WIZUTILC
$I WIZ1E:TITLELOA
$I WIZ1E:OPTIONS

I'm confused, why isn't there a WIZUTIL (or WIZUTILA) include file in STARTUP.TEXT?

Oh yeah, in LOL there is a separate executable called WIZUTIL.CODE so that WIZUTIL.TEXT is the code that includes WIZUTILB and WIZUTILC. WIZUTIL.TEXT is now STARTUP.TEXT (SYSTEM.STARTUP).

The reason there is a WIZUTILB.TEXT and WIZUTILC.TEXT is because the segment is too big for the Pascal editor. The placement of the break can be quite arbitrary, but it makes sense to do it "logically". You want about 1/2 the code in one file, and 1/2 in the other, and you usually want to start the second file with a procedure at a high lexical level. The reasons for this are purely logistical with regards to making global editting changes to the code and data. Heck, if you want to you can split the files in 2 places whereever white space is allowed, but I wouldn't recommend splitting onc procedure between the two.

This is the pcode we want for UTILS (P070101):

FILE: 7 SEG: 1 PROC: 1

JTAB FOR SUBROUTINE 1:
DATA SIZE: 1538
PARAM SIZE: 0
EXIT AT: 6B77
ENTER AT: 6A32
PROC NUMBER: 1
LEXICAL LEVEL: 1
6A32 D7 NOP. NOP
6A33 A6 12 57 49 5A 41 52 44 52 59 20 55 54 49 4C 49 54 49 45 53
WIZARDRY UTILITIES
LSA. PUSH #(PC+1) POINTER TO THE STRING
6A47 CE 0F CLP. CALL CHILD PROCEDURE: 0F
6A49 B6 02 03 LOD. PUSH ACTREC(-02).03
6A4C CD 00 16 CXP. CALL EXTERNAL PROCEDURE: 16 IN SEGMENT: 00
6A4F B6 02 03 LOD. PUSH ACTREC(-02).03
6A52 D7 NOP. NOP
6A53 A6 10 43 48 4F 4F 53 45 20 41 4E 20 4F 50 54 49 4F 4E
CHOOSE AN OPTION
LSA. PUSH #(PC+1) POINTER TO THE STRING
6A65 1C SLDC. PUSH #001C
6A66 CD 00 13 CXP. CALL EXTERNAL PROCEDURE: 13 IN SEGMENT: 00
6A69 B6 02 03 LOD. PUSH ACTREC(-02).03
6A6C CD 00 16 CXP. CALL EXTERNAL PROCEDURE: 16 IN SEGMENT: 00
6A6F B6 02 03 LOD. PUSH ACTREC(-02).03
6A72 CD 00 16 CXP. CALL EXTERNAL PROCEDURE: 16 IN SEGMENT: 00
6A75 B6 02 03 LOD. PUSH ACTREC(-02).03
6A78 D7 NOP. NOP
6A79 A6 1B 52 29 45 43 4F 56 45 52 20 20 54 29 52 41 4E 53 46 45 52 20 42 29 41 43 4B 55 50
R)ECOVER T)RANSFER B)ACKUP
LSA. PUSH #(PC+1) POINTER TO THE STRING
6A96 22 SLDC. PUSH #0022
6A97 CD 00 13 CXP. CALL EXTERNAL PROCEDURE: 13 IN SEGMENT: 00
6A9A B6 02 03 LOD. PUSH ACTREC(-02).03
6A9D CD 00 16 CXP. CALL EXTERNAL PROCEDURE: 16 IN SEGMENT: 00
6AA0 B6 02 03 LOD. PUSH ACTREC(-02).03
6AA3 CD 00 16 CXP. CALL EXTERNAL PROCEDURE: 16 IN SEGMENT: 00
6AA6 B6 02 03 LOD. PUSH ACTREC(-02).03
6AA9 A6 1B 4D 29 41 4B 45 20 53 43 45 4E 41 52 49 4F 20 44 49 53 4B 20 55 29 50 44 41 54 45
M)AKE SCENARIO DISK U)PDATE
LSA. PUSH #(PC+1) POINTER TO THE STRING
6AC6 D7 NOP. NOP
6AC7 22 SLDC. PUSH #0022
6AC8 CD 00 13 CXP. CALL EXTERNAL PROCEDURE: 13 IN SEGMENT: 00
6ACB B6 02 03 LOD. PUSH ACTREC(-02).03
6ACE CD 00 16 CXP. CALL EXTERNAL PROCEDURE: 16 IN SEGMENT: 00
6AD1 B6 02 03 LOD. PUSH ACTREC(-02).03
6AD4 CD 00 16 CXP. CALL EXTERNAL PROCEDURE: 16 IN SEGMENT: 00
6AD7 B6 02 03 LOD. PUSH ACTREC(-02).03
6ADA D7 NOP. NOP
6ADB A6 1B 43 29 48 41 4E 47 45 20 4E 41 4D 45 53 20 20 20 20 20 20 20 4C 29 45 41 56 45 20
C)HANGE NAMES L)EAVE
LSA. PUSH #(PC+1) POINTER TO THE STRING
6AF8 22 SLDC. PUSH #0022
6AF9 CD 00 13 CXP. CALL EXTERNAL PROCEDURE: 13 IN SEGMENT: 00
6AFC B6 02 03 LOD. PUSH ACTREC(-02).03
6AFF CD 00 16 CXP. CALL EXTERNAL PROCEDURE: 16 IN SEGMENT: 00
6B02 29 SLDC. PUSH #0029
6B03 00 SLDC. PUSH #0000
6B04 CD 00 1D CXP. CALL EXTERNAL PROCEDURE: 1D IN SEGMENT: 00
6B07 CE 04 CLP. CALL CHILD PROCEDURE: 04
6B09 EA SLDO. PUSH BASE.03
6B0A 52 SLDC. PUSH #0052
6B0B C3 EQUI. PUSH ((TOS-1) = (TOS))
6B0C EA SLDO. PUSH BASE.03
6B0D 54 SLDC. PUSH #0054
6B0E C3 EQUI. PUSH ((TOS-1) = (TOS))
6B0F 8D LOR. PUSH ((TOS-1) OR (TOS))
6B10 EA SLDO. PUSH BASE.03
6B11 4C SLDC. PUSH #004C
6B12 C3 EQUI. PUSH ((TOS-1) = (TOS))
6B13 8D LOR. PUSH ((TOS-1) OR (TOS))
6B14 EA SLDO. PUSH BASE.03
6B15 43 SLDC. PUSH #0043
6B16 C3 EQUI. PUSH ((TOS-1) = (TOS))
6B17 8D LOR. PUSH ((TOS-1) OR (TOS))
6B18 EA SLDO. PUSH BASE.03
6B19 42 SLDC. PUSH #0042
6B1A C3 EQUI. PUSH ((TOS-1) = (TOS))
6B1B 8D LOR. PUSH ((TOS-1) OR (TOS))
6B1C EA SLDO. PUSH BASE.03
6B1D 4D SLDC. PUSH #004D
6B1E C3 EQUI. PUSH ((TOS-1) = (TOS))
6B1F 8D LOR. PUSH ((TOS-1) OR (TOS))
6B20 EA SLDO. PUSH BASE.03
6B21 55 SLDC. PUSH #0055
6B22 C3 EQUI. PUSH ((TOS-1) = (TOS))
6B23 8D LOR. PUSH ((TOS-1) OR (TOS))
6B24 A1 F6 UJP. IF NOT (TOS) THEN JUMP TO 6B02
6B26 EA SLDO. PUSH BASE.03
6B27 B9 1C UJP. JUMP TO 6B45
6B29 CE 07 CLP. CALL CHILD PROCEDURE: 07
6B2B B9 47 UJP. JUMP TO 6B74
6B2D CE 17 CLP. CALL CHILD PROCEDURE: 17
6B2F B9 43 UJP. JUMP TO 6B74
6B31 CE 16 CLP. CALL CHILD PROCEDURE: 16
6B33 B9 3F UJP. JUMP TO 6B74
6B35 CE 11 CLP. CALL CHILD PROCEDURE: 11
6B37 B9 3B UJP. JUMP TO 6B74
6B39 CE 13 CLP. CALL CHILD PROCEDURE: 13
6B3B B9 37 UJP. JUMP TO 6B74
6B3D CE 1C CLP. CALL CHILD PROCEDURE: 1C
6B3F B9 33 UJP. JUMP TO 6B74
6B41 CE 20 CLP. CALL CHILD PROCEDURE: 20
6B43 B9 2F UJP. JUMP TO 6B74
6B45 AC 42 00 55 00 B9 28 TABLE: 13 00 19 00 06 00 08 00 0A 00 0C 00 0E 00 10 00 12 00 14 00 37 00 25 00 1A 00 1C 00 1E 00 20 00 3B 00 24 00 43 00 31 00
XJP. W1,W2,W3,<TABLE>
6B74 00 SLDC. PUSH #0000
6B75 A1 F4 UJP. IF NOT (TOS) THEN JUMP TO 6A32
6B77 AD 00 RNP. RETURN FROM NON-BASE PROCEDURE.
6B79 00 SLDC. PUSH #0000
6B7A 48 SLDC. PUSH #0048
6B7B 01 SLDC. PUSH #0001
6B7C 7A SLDC. PUSH #007A
6B7D 00 SLDC. PUSH #0000

This is a sketch of code we have in LOL:

WIZUTILB.TEXT
SEGMENT PROCEDURE UTILS; (* P050101 *) P070101
FUNCTION GETREC(...) (* P050102 *) P070102
PROCEDURE CHGNAMES ( ...) P070111
PROCEDURE GETNAME( ... ) P070112

WIZUTILC.TEXT
PROCEDURE BACKUP ( ...) P070113
PROCEDURE FRBACKUP( ...) P070114

BEGIN (* UTILS *)
TITLESCR( 'WIZARDRY UTILITIES');
CHOOSE AN OPTION
R)ECOVER T)RANSFER B)ACKUP
M)AKE SCENARIO DISK
C)HANGE NAMES L)EAVE

REPEAT
CASE
UNTIL FALSE
END; (* UTILS *)

Here is what I need to do:

Insert "U)PDATE" into menu.
Add "(INCHAR = 'U')" to REPEAT UNTIL test.
Add " 'U': UPDATE; (* P07011C *)
Add dummy procedures for the missing "U"pdate ones.

The following chart was created with information from the DeCompiler.

UTILS
Proc# lex Start Exit Order Hierarchy
----- --- ----- ---- ----- ----------
WIZUTILB 1 1 6A32 6B77 36 P070101
2 2 5000 5068 1 P070102
3 2 5074 50DC 2 P070103
4 2 50E8 510B 3 P070104
5 2 5118 51D0 4 P070105
6 2 51E0 51EC 5 P070106
7 2 51FA 5274 6 P070107
8 2 5282 52A9 7 P070108
9 2 52B6 5336 8 P070109
10 2 5344 5393 9 P07010A
11 2 53A4 53B7 10 P07010B
12 2 53C4 53D7 11 P07010C
13 2 53E4 53FD 12 P07010D
14 2 540A 5427 13 P07010E
15 2 5434 5480 14 P07010F
16 2 548E 550C 15 P070110
17 2 55AC 57E2 17 CHGNAMES (* P070111 *)
18 3 551A 5598 16 GETNAME (* P070112 *)
WIZUTILC
19 2 5BC2 5C4B 20 P070113
20 3 57F8 5A26 18 P070114
21 3 5A3A 5BB2 19 P070115
22 2 5C58 5D28 21 P070116
23 2 60A6 621D 26 TRANSFER (* P070117 *)
24 3 5D80 5F59 23 P070118
25 4 5D36 5D74 22 P070119
26 3 5F70 6064 24 P07011A
27 3 6074 6099 25 P07011B
28 2 62B2 6555 30 P07011C
29 3 622C 6267 27 P07011D
30 3 6274 6283 28 P07011E
31 3 6290 62A6 29 P07011F
32 2 6806 6A1F 35 P070120
33 3 6566 65A2 31 P070121
34 3 6610 6694 33 P070122
35 4 65AE 6604 32 P070123
36 3 66A2 67FA 34 P070124

I had to insert P070119.

Final rough draft for UTILS mainline:

881 7 1:0 0 BEGIN (* UTILS *)
882 7 1:1 0 REPEAT
883 7 1:2 0 TITLESCR( 'WIZARDRY UTILITIES');
884 7 1:2 23 WRITELN;
885 7 1:2 29 WRITELN( 'CHOOSE AN OPTION' : 28);
886 7 1:2 61 WRITELN;
887 7 1:2 67 WRITELN( 'R)ECOVER T)RANSFER B)ACKUP' : 34);
888 7 1:2 110 WRITELN;
889 7 1:2 116 WRITELN( 'M)AKE SCENARIO DISK U)PDATE' : 34);
890 7 1:2 159 WRITELN;
891 7 1:2 165 WRITELN( 'C)HANGE NAMES L)EAVE ' : 34);
892 7 1:2 208
893 7 1:2 208 REPEAT
894 7 1:3 208 GOTOXY( 41, 0);
895 7 1:3 213 GETKEY
896 7 1:2 213 UNTIL (INCHAR = 'R') OR (INCHAR = 'T') OR
897 7 1:2 222 (INCHAR = 'L') OR (INCHAR = 'C') OR
898 7 1:2 230 (INCHAR = 'B') OR (INCHAR = 'M') OR
899 7 1:2 238 (INCHAR = 'U');
900 7 1:2 244
901 7 1:2 244 CASE INCHAR OF
902 7 1:2 247
903 7 1:2 247 'L': BEGIN
904 7 1:2 247 (* BASE04 := 0; *)
905 7 1:4 247 EXITUTIL
906 7 1:3 247 END;
907 7 1:3 251
908 7 1:2 251 'T': TRANSFER;
909 7 1:2 255 'R': RECOVER;
910 7 1:2 259 'C': CHGNAMES;
911 7 1:2 263 'B': BACKUP;
912 7 1:2 267 'M': BEGIN
913 7 1:2 267 (* BASE04 := 1; *)
914 7 1:4 267 P07011C;
915 7 1:3 269 END;
916 7 1:2 271 'U': P070120;
917 7 1:2 275 END
918 7 1:1 322 UNTIL FALSE
919 7 1:0 322 END; (* UTILS *)


P070102 is next. The version for LOL produces almost exactly the code we want.

This is the pcode we want:

FILE: 7 SEG: 1 PROC: 2

JTAB FOR SUBROUTINE 2:
DATA SIZE: 4
PARAM SIZE: 10
EXIT AT: 5068
ENTER AT: 5000
PROC NUMBER: 2
LEXICAL LEVEL: 2
5000 B2 01 82 33 LDA. PUSH #ACTREC(-01).0233
5004 DC SLDL. PUSH MP.05
5005 A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
5007 F8 SIND. PUSH (TOS)^.0
5008 02 SLDC. PUSH #0002
5009 DB SLDL. PUSH MP.04
500A B2 01 82 1B LDA. PUSH #ACTREC(-01).021B
500E DC SLDL. PUSH MP.05
500F A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
5011 F8 SIND. PUSH (TOS)^.0
5012 86 DVI. PUSH ((TOS-1) / (TOS))
5013 8F MPI. PUSH ((TOS-1) * (TOS))
5014 82 ADI. PUSH ((TOS) + (TOS-1))
5015 CC 07 STL. MP.07 := (TOS)
5017 DA SLDL. PUSH MP.03
5018 DB SLDL. PUSH MP.04
5019 B2 01 82 1B LDA. PUSH #ACTREC(-01).021B
501D DC SLDL. PUSH MP.05
501E A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
5020 F8 SIND. PUSH (TOS)^.0
5021 8E MODI. PUSH ((TOS-1) MOD (TOS))
5022 8F MPI. PUSH ((TOS-1) * (TOS))
5023 CC 06 STL. MP.06 := (TOS)
5025 B6 01 82 02 LOD. PUSH ACTREC(-01).0202
5029 DE SLDL. PUSH MP.07
502A CB NEQI. PUSH ((TOS-1) <> (TOS))
502B A1 38 UJP. IF NOT (TOS) THEN JUMP TO 5065
502D B6 01 82 05 LOD. PUSH ACTREC(-01).0205
5031 A1 14 UJP. IF NOT (TOS) THEN JUMP TO 5047
5033 04 SLDC. PUSH #0004
5034 B2 01 01 LDA. PUSH #ACTREC(-01).01
5037 00 SLDC. PUSH #0000
5038 C7 00 04 LDCI. PUSH #0400
503B B6 01 82 02 LOD. PUSH ACTREC(-01).0202
503F B6 01 82 03 LOD. PUSH ACTREC(-01).0203
5043 82 ADI. PUSH ((TOS) + (TOS-1))
5044 00 SLDC. PUSH #0000
5045 9E 06 WRITE VOL#
5047 00 SLDC. PUSH #0000
5048 B8 01 82 05 STR. ACTREC(-01).0205 := TOS
504C DE SLDL. PUSH MP.07
504D B8 01 82 02 STR. ACTREC(-01).0202 := TOS
5051 04 SLDC. PUSH #0004
5052 B2 01 01 LDA. PUSH #ACTREC(-01).01
5055 00 SLDC. PUSH #0000
5056 C7 00 04 LDCI. PUSH #0400
5059 B6 01 82 02 LOD. PUSH ACTREC(-01).0202
505D B6 01 82 03 LOD. PUSH ACTREC(-01).0203
5061 82 ADI. PUSH ((TOS) + (TOS-1))
5062 00 SLDC. PUSH #0000
5063 9E 05 READ FROM VOL#
5065 DD SLDL. PUSH MP.06
5066 CC 01 STL. MP.01 := (TOS)
5068 AD 01 RNP. RETURN FROM NON-BASE PROCEDURE.


In WIZ1 and LOL, the line at $503F was correct to reference ACTREC(-01).203.

The line at $5048 had 2 different results:
WIZ1: ACTREC(-01).0205
LOL: ACTREC(-01).0204

To fix this difference, I need to insert another variable after the ACTREC(-01).203 variable. By looking at the LOL source code, I could see that "CACHEWRI := FALSE" generated the 0.205. Therefore, I needed to add the variable just before CACHEWRI in the VAR delcarations for UTILS.

These VARs are from WIZUTIL in LOL:

165 7 1:D 1 VAR
166 7 1:D 1
167 7 1:D 1 IOCACHE : PACKED ARRAY[ 0..1023] OF CHAR;
168 7 1:D 513 UNUSEDXX : INTEGER;
169 7 1:D 514 CACHEBL : INTEGER;
170 7 1:D 515 SCNTOCBL : INTEGER;
171 7 1:D 516 CACHEWRI : BOOLEAN;
172 7 1:D 517 SCNTOC : TSCNTOC;

These are the VARs we need for Wiz1 UTILs

VAR

IOCACHE : PACKED ARRAY[ 0..1023] OF CHAR;
UNUSEDXX : INTEGER;
CACHEBL : INTEGER;
SCNTOCBL : INTEGER;
BASE204 : INTEGER; (* NOT IN LOL, BUT IS IN WIZ1 *)
CACHEWRI : BOOLEAN;
SCNTOC : TSCNTOC;

P070102 (GETREC)
175 7 2:D 3 FUNCTION GETREC( DATATYPE : TZSCN; (* P050102 *)
176 7 2:D 4 DATAINDX : INTEGER;
177 7 2:D 5 DATASIZE : INTEGER) : INTEGER;
178 7 2:D 6
179 7 2:D 6 VAR
180 7 2:D 6 BUFFADDR : INTEGER;
181 7 2:D 7 DSKBLOCK : INTEGER;
182 7 2:D 8
183 7 2:0 0 BEGIN
184 7 2:1 0 DSKBLOCK := SCNTOC.BLOFF[ DATATYPE] +
185 7 2:1 8 2 * (DATAINDX DIV SCNTOC.RECPER2B[ DATATYPE]);
186 7 2:1 23 BUFFADDR := DATASIZE * (DATAINDX MOD SCNTOC.RECPER2B[ DATATYPE]);
187 7 2:1 37 IF CACHEBL <> DSKBLOCK THEN
188 7 2:2 45 BEGIN
189 7 2:3 45 IF CACHEWRI THEN
190 7 2:4 51 UNITWRITE( DRIVE1, IOCACHE, SIZEOF( IOCACHE),
191 7 2:4 59 CACHEBL + SCNTOCBL, 0);
192 7 2:3 71 CACHEWRI := FALSE;
193 7 2:3 76 CACHEBL := DSKBLOCK;
194 7 2:3 81 UNITREAD( DRIVE1, IOCACHE, SIZEOF( IOCACHE),
195 7 2:3 89 CACHEBL + SCNTOCBL, 0)
196 7 2:2 101 END;
197 7 2:1 101 GETREC := BUFFADDR
198 7 2:0 101 END;


The following routines are exactly the same in Wiz1 and LOL:
P070103
P070106
P070108
P070109
P07010B
P07010C
P07010D
P07010E
P07010F
P070110
P070111
P070112
P070113
P070114
P070116

No changes are made to those routines.
If you want to see the source code, please look at the WizardyIII (LOL) code that I uploaded to ASIMOV:

ftp://ftp.apple.asimov.net/pub/apple_II/images/games/rpg/wizardry/wizardry_III/
Wizardry_iii_SourceCode.zip/Wizardry.code
WizardryListing.txt


P070104

pcode we want:

FILE: 7 SEG: 1 PROC: 4

JTAB FOR SUBROUTINE 4:
DATA SIZE: 0
PARAM SIZE: 0
EXIT AT: 510B
ENTER AT: 50E8
PROC NUMBER: 4
LEXICAL LEVEL: 2
50E8 B6 03 02 LOD. PUSH ACTREC(-03).02
50EB A5 03 LA0. PUSH #BASE.03
50ED CD 00 10 CXP. CALL EXTERNAL PROCEDURE: 10 IN SEGMENT: 00
50F0 B6 03 02 LOD. PUSH ACTREC(-03).02
50F3 00 SLDC. PUSH #0000
50F4 00 SLDC. PUSH #0000
50F5 CD 00 0B CXP. CALL EXTERNAL PROCEDURE: 0B IN SEGMENT: 00
50F8 A1 03 UJP. IF NOT (TOS) THEN JUMP TO 50FD
50FA 0D SLDC. PUSH #000D
50FB AB 03 SRO. BASE.03 := (TOS)
50FD B6 01 82 04 LOD. PUSH ACTREC(-01).0204
5101 EA SLDO. PUSH BASE.03
5102 82 ADI. PUSH ((TOS) + (TOS-1))
5103 C7 03 04 LDCI. PUSH #0403
5106 8E MODI. PUSH ((TOS-1) MOD (TOS))
5107 B8 01 82 04 STR. ACTREC(-01).0204 := TOS
510B AD 00 RNP. RETURN FROM NON-BASE PROCEDURE.
510D 00 SLDC. PUSH #0000


pcode from LOL:

JTAB FOR SUBROUTINE 4:
DATA SIZE: 0
PARAM SIZE: 0
EXIT AT: 5106
ENTER AT: 50E8
PROC NUMBER: 4
LEXICAL LEVEL: 2
50E8 B6 03 02 LOD. PUSH ACTREC(-03).02
50EB A5 03 LA0. PUSH #BASE.03
50ED CD 00 10 CXP. CALL EXTERNAL PROCEDURE: 10 IN SEGMENT: 00
50F0 B6 03 02 LOD. PUSH ACTREC(-03).02
50F3 00 SLDC. PUSH #0000
50F4 00 SLDC. PUSH #0000
50F5 CD 00 0B CXP. CALL EXTERNAL PROCEDURE: 0B IN SEGMENT: 00
50F8 A1 03 UJP. IF NOT (TOS) THEN JUMP TO 50FD
50FA 0D SLDC. PUSH #000D
50FB AB 03 SRO. BASE.03 := (TOS)
50FD EC SLDO. PUSH BASE.05
50FE EA SLDO. PUSH BASE.03
50FF 82 ADI. PUSH ((TOS) + (TOS-1))
5100 C7 03 04 LDCI. PUSH #0403
5103 8E MODI. PUSH ((TOS-1) MOD (TOS))
5104 AB 05 SRO. BASE.05 := (TOS)
5106 AD 00 RNP. RETURN FROM NON-BASE PROCEDURE.


An earlier change now makes line 50FD read:
PUSH BASE.04

But the code we want is:
PUSH ACTREC(-01).0204

(The line at $5104 also changed to BASE.04.)

In this routine we see that the variable needs to change RANDNUM to ACTREC(-01).0204.

Change from LOL code:
RANDNUM := (RANDNUM + ORD( INCHAR)) MOD 1027
to WIZ1 code:
BASE204 := (BASE204 + ORD( INCHAR)) MOD 1027

P070104 Final rough draft:
PROCEDURE GETKEY; (* P070104 *)

BEGIN
READ( INCHAR);
IF EOLN THEN
INCHAR := CHR( 13);
BASE204 := (BASE204 + ORD( INCHAR)) MOD 1027
END;

The more I look at this, I now realize that BASE204 is just RANDNUM. They moved the variable in LOL to the mainline level.

P070105
pcode we want:
JTAB FOR SUBROUTINE 5:
DATA SIZE: 426
PARAM SIZE: 8
EXIT AT: 51D0
ENTER AT: 5118
PROC NUMBER: 5
LEXICAL LEVEL: 2
5118 C6 05 LLA. PUSH #MP.05
511A DA SLDL. PUSH MP.03
511B AA 50 SAS. TOS=CHAR::(TOS-1)^ := TOS CHAR
TOS=PTR ::(TOS-1)^ := (TOS)^ 50 CHARACTERS MAX
511D DB SLDL. PUSH MP.04
511E B2 01 01 LDA. PUSH #ACTREC(-01).01
5121 00 SLDC. PUSH #0000
5122 C7 00 04 LDCI. PUSH #0400
5125 02 SLDC. PUSH #0002
5126 00 SLDC. PUSH #0000
5127 9E 05 READ FROM VOL#
5129 9E 22 PUSH I/O ERROR STATUS
512B 00 SLDC. PUSH #0000
512C CB NEQI. PUSH ((TOS-1) <> (TOS))
512D A1 08 UJP. IF NOT (TOS) THEN JUMP TO 5137
512F 9E 22 PUSH I/O ERROR STATUS
5131 80 ABI. PUSH ABS( (TOS) ) )
5132 91 NGI. PUSH -(TOS) (2'S COMP)
5133 CC 01 STL. MP.01 := (TOS)
5135 B9 F4 UJP. JUMP TO 51D0
5137 B2 01 01 LDA. PUSH #ACTREC(-01).01
513A 00 SLDC. PUSH #0000
513B C6 2E LLA. PUSH #MP.2E
513D 00 SLDC. PUSH #0000
513E C7 52 01 LDCI. PUSH #0152
5141 9E 02 MVL. MOVELEFT
5143 C6 2E LLA. PUSH #MP.2E
5145 00 SLDC. PUSH #0000
5146 A4 0D IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=0D
5148 A3 08 IND. PUSH (TOS)^.08
514A 00 SLDC. PUSH #0000
514B C9 LESI. PUSH ((TOS-1) < (TOS))
514C C6 2E LLA. PUSH #MP.2E
514E 00 SLDC. PUSH #0000
514F A4 0D IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=0D
5151 A3 08 IND. PUSH (TOS)^.08
5153 08 SLDC. PUSH #0008
5154 C5 GRTI. PUSH ((TOS-1) > (TOS))
5155 8D LOR. PUSH ((TOS-1) OR (TOS))
5156 A1 08 UJP. IF NOT (TOS) THEN JUMP TO 5160
5158 01 SLDC. PUSH #0001
5159 91 NGI. PUSH -(TOS) (2'S COMP)
515A CC 01 STL. MP.01 := (TOS)
515C 07 SLDC. PUSH #0007
515D 05 SLDC. PUSH #0005
515E 9E 04 EXIT. EXIT FROM PROCEDURE. (TOS)=PROC# (TOS-1)=SEG#
5160 00 SLDC. PUSH #0000
5161 CC 80 D7 STL. MP.D7 := (TOS)
5164 01 SLDC. PUSH #0001
5165 CC 80 D8 STL. MP.D8 := (TOS)
5168 C6 2E LLA. PUSH #MP.2E
516A 00 SLDC. PUSH #0000
516B A4 0D IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=0D
516D A3 08 IND. PUSH (TOS)^.08
516F CC 80 D9 STL. MP.D9 := (TOS)
5172 CA 80 D8 LDL. PUSH MP.D8
5175 CA 80 D9 LDL. PUSH MP.D9
5178 C8 LEQI. PUSH ((TOS-1) <= (TOS))
5179 A1 3E UJP. IF NOT (TOS) THEN JUMP TO 51B9
517B C6 2E LLA. PUSH #MP.2E
517D CA 80 D8 LDL. PUSH MP.D8
5180 A4 0D IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=0D
5182 A2 02 INC. PUSH #(TOS)^.(02)
5184 04 SLDC. PUSH #0004
5185 00 SLDC. PUSH #0000
5186 BA LDP. PUSH FIELD. TOS: RTBIT;#;^
5187 01 SLDC. PUSH #0001
5188 C4 GEQI. PUSH ((TOS-1) >= (TOS))
5189 C6 2E LLA. PUSH #MP.2E
518B CA 80 D8 LDL. PUSH MP.D8
518E A4 0D IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=0D
5190 A2 02 INC. PUSH #(TOS)^.(02)
5192 04 SLDC. PUSH #0004
5193 00 SLDC. PUSH #0000
5194 BA LDP. PUSH FIELD. TOS: RTBIT;#;^
5195 07 SLDC. PUSH #0007
5196 C8 LEQI. PUSH ((TOS-1) <= (TOS))
5197 84 LAND. PUSH ((TOS-1) AND (TOS))
5198 A1 15 UJP. IF NOT (TOS) THEN JUMP TO 51AF
519A C6 2E LLA. PUSH #MP.2E
519C CA 80 D8 LDL. PUSH MP.D8
519F A4 0D IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=0D
51A1 A2 03 INC. PUSH #(TOS)^.(03)
51A3 C6 05 LLA. PUSH #MP.05
51A5 AF 04 EQU. PUSH ((TOS-1) = (TOS))--STRINGS
51A7 A1 06 UJP. IF NOT (TOS) THEN JUMP TO 51AF
51A9 CA 80 D8 LDL. PUSH MP.D8
51AC CC 80 D7 STL. MP.D7 := (TOS)
51AF CA 80 D8 LDL. PUSH MP.D8
51B2 01 SLDC. PUSH #0001
51B3 82 ADI. PUSH ((TOS) + (TOS-1))
51B4 CC 80 D8 STL. MP.D8 := (TOS)
51B7 B9 F6 UJP. JUMP TO 5172
51B9 CA 80 D7 LDL. PUSH MP.D7
51BC 00 SLDC. PUSH #0000
51BD C3 EQUI. PUSH ((TOS-1) = (TOS))
51BE A1 06 UJP. IF NOT (TOS) THEN JUMP TO 51C6
51C0 09 SLDC. PUSH #0009
51C1 91 NGI. PUSH -(TOS) (2'S COMP)
51C2 CC 01 STL. MP.01 := (TOS)
51C4 B9 0A UJP. JUMP TO 51D0
51C6 C6 2E LLA. PUSH #MP.2E
51C8 CA 80 D7 LDL. PUSH MP.D7
51CB A4 0D IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=0D
51CD F8 SIND. PUSH (TOS)^.0
51CE CC 01 STL. MP.01 := (TOS)
51D0 AD 01 RNP. RETURN FROM NON-BASE PROCEDURE.
51D2 02 SLDC. PUSH #0002
51D3 00 SLDC. PUSH #0000
51D4 62 SLDC. PUSH #0062
51D5 00 SLDC. PUSH #0000


At line 5153 we need to have PUSH #0008.
5153 08 SLDC. PUSH #0008

In the LOL code this is PUSH #000C. We therefore need the following Pascal code change:

From LOL:
IF (DIR[ 0].FILECNT < 0) OR (DIR[ 0].FILECNT > 12) THEN

To WIZ1:
IF (DIR[ 0].FILECNT < 0) OR (DIR[ 0].FILECNT > 8) THEN


P070105 Final rough draft:

FUNCTION FINDFILE( DRIVE : INTEGER; (* P070105 *)
FILENAME : STRING) : INTEGER;

VAR
DIR : ARRAY[ 0..12] OF DIRENTRY;
FILEIFND : INTEGER;
FILEI : INTEGER;
FILECNT : INTEGER;

BEGIN
UNITREAD( DRIVE, IOCACHE, SIZEOF( IOCACHE), WIZDIRBL, 0);
IF IORESULT <> 0 THEN
FINDFILE := -ABS( IORESULT)
ELSE
BEGIN
MOVELEFT( IOCACHE, DIR, SIZEOF( DIR));
IF (DIR[ 0].FILECNT < 0) OR (DIR[ 0].FILECNT > 8) THEN
BEGIN
FINDFILE := -1;
EXIT( FINDFILE)
END;
FILEIFND := 0;
FILEI := 1;
FILECNT := DIR[ 0].FILECNT;
WHILE FILEI <= FILECNT DO
BEGIN
IF (DIR[ FILEI].FILEKIND.FT >= BADBLK) AND
(DIR[ FILEI].FILEKIND.FT <= FOTOFILE) THEN
BEGIN
IF DIR[ FILEI].FILENAME = FILENAME THEN
FILEIFND := FILEI
END;
FILEI := FILEI + 1;
END;
IF FILEIFND = 0 THEN
FINDFILE := -9
ELSE
FINDFILE := DIR[ FILEIFND].FIRSTBLK
END
END;


P070107 from LOL is almost what we need:

JTAB FOR SUBROUTINE 7:
DATA SIZE: 2
PARAM SIZE: 0
EXIT AT: 5274
ENTER AT: 51FA
PROC NUMBER: 7
LEXICAL LEVEL: 2
51FA 04 SLDC. PUSH #0004
51FB A6 0D 53 59 53 54 45 4D 2E 50 41 53 43 41 4C
SYSTEM.PASCAL
LSA. PUSH #(PC+1) POINTER TO THE STRING
520A D7 NOP. NOP
520B 00 SLDC. PUSH #0000
520C 00 SLDC. PUSH #0000
520D AE 05 CIP. CALL INTERMEDIATE PROCEDURE: 05
520F CC 01 STL. MP.01 := (TOS)
5211 D8 SLDL. PUSH MP.01
5212 00 SLDC. PUSH #0000
5213 C8 LEQI. PUSH ((TOS-1) <= (TOS))
5214 A1 5A UJP. IF NOT (TOS) THEN JUMP TO 5270
5216 B6 03 03 LOD. PUSH ACTREC(-03).03
5219 0C SLDC. PUSH #000C
521A 00 SLDC. PUSH #0000
521B CD 00 11 CXP. CALL EXTERNAL PROCEDURE: 11 IN SEGMENT: 00
521E 00 SLDC. PUSH #0000
521F 0A SLDC. PUSH #000A
5220 CD 00 1D CXP. CALL EXTERNAL PROCEDURE: 1D IN SEGMENT: 00
5223 B6 03 03 LOD. PUSH ACTREC(-03).03
5226 D7 NOP. NOP
5227 A6 28 50 4C 41 43 45 20 42 4F 4F 54 20 44 49 53 4B 20 49 4E 20 44 52 49 56 45 20 31 2C 20 50 52 45 53 53 20 52 45 54 55 52 4E
PLACE BOOT DISK IN DRIVE 1, PRESS RETURN
LSA. PUSH #(PC+1) POINTER TO THE STRING
5251 00 SLDC. PUSH #0000
5252 CD 00 13 CXP. CALL EXTERNAL PROCEDURE: 13 IN SEGMENT: 00
5255 AE 06 CIP. CALL INTERMEDIATE PROCEDURE: 06
5257 04 SLDC. PUSH #0004
5258 D7 NOP. NOP
5259 A6 0D 53 59 53 54 45 4D 2E 50 41 53 43 41 4C
SYSTEM.PASCAL
LSA. PUSH #(PC+1) POINTER TO THE STRING
5268 00 SLDC. PUSH #0000
5269 00 SLDC. PUSH #0000
526A AE 05 CIP. CALL INTERMEDIATE PROCEDURE: 05
526C CC 01 STL. MP.01 := (TOS)
526E B9 F6 UJP. JUMP TO 5211
5270 07 SLDC. PUSH #0007
5271 01 SLDC. PUSH #0001
5272 9E 04 EXIT. EXIT FROM PROCEDURE. (TOS)=PROC# (TOS-1)=SEG#
5274 AD 00 RNP. RETURN FROM NON-BASE PROCEDURE.
5276 65 SLDC. PUSH #0065
5277 00 SLDC. PUSH #0000


In LOL they look for the file "SYSTEM.CHARSET", so that name needs to be changed in 2 places in the Pascal code for Wiz1.

P070107 Final rough draft:

PROCEDURE EXITUTIL; (* P070107 *)

VAR
FILEINDX : INTEGER;

BEGIN
FILEINDX := FINDFILE( DRIVE1, 'SYSTEM.PASCAL');
WHILE FILEINDX <= 0 DO
BEGIN
WRITE( CHR( HOMECLR));
GOTOXY( 0, 10);
WRITE( 'PLACE BOOT DISK IN DRIVE 1, PRESS RETURN');
GETCR;
FILEINDX := FINDFILE( DRIVE1, 'SYSTEM.PASCAL')
END;
EXIT( UTILS)
END;


P07010A is almost exactly what we want, but we need to change RANDNUM to BASE204.

PROCEDURE GETPASS( VAR PASSWD : STRING ); (* P07010A *)

VAR
UNUSEDXX : INTEGER;
I : INTEGER;
PASSI : INTEGER;

BEGIN
PASSI := 0;
REPEAT
GETKEY;
IF INCHAR <> CHR( CR) THEN
BEGIN
WRITE( CHR( BS));
FOR I := 0 TO BASE204 MOD 2 DO
WRITE( 'X')
END;
PASSI := PASSI + 1;
PASSWD[ PASSI] := INCHAR
UNTIL (INCHAR = CHR( CR)) OR (PASSI = 15);
WRITELN;
PASSWD[ 0] := CHR( PASSI - 1)
END;

P070115
Only 1 small change is needed:
From:
WRITELN( 'IN DRIVE 1 AND PRESS [RET] TO SAVE.');
To:
WRITELN( 'IN DRIVE 1 AND PRESS [RETURN] TO SAVE.');


P070117
Comparing the pcode from Wiz3 with the pcode for Wiz1 I noticed we need the following changes:

Change:
MOVE CHARS FROM A SCENARIO
To:
MOVE CHARS BETWEEN SCENARIOS

Change:
TO LEGACY OF LLYLGAMYN ONLY!
To:
'' (Null string)

Change:
BL5BUFF[ 12] := CHR( (ORD( BL5BUFF[ 12]) + RANDNUM) MOD 256);
To:
BL5BUFF[ 12] := CHR( (ORD( BL5BUFF[ 12]) + BASE204) MOD 256);


There are a number of changes needed to the following code (from LOL):

272 UNITWRITE( DRIVE1, BL5BUFF, SIZEOF( BL5BUFF), SERIALBL, 0);
803 7 23:1 284 TODSKERR := IORESULT <> 0;
804 7 23:1 291 IF NOT TODSKERR AND (SERIALFR <> SERIALTO) THEN
805 7 23:2 306 BEGIN
806 7 23:3 306 FOR BASE04 := 0 TO (SCNTOC.RECPERDK[ ZCHAR] - 1) DO
807 7 23:4 329 BEGIN
808 7 23:5 329 RDCHARAC( PLAYREC1, BASE04);
809 7 23:5 334 IF (PLAYREC1.NAME = PLAYREC2.NAME) AND
810 7 23:5 340 (PLAYREC1.STATUS <> LOST) THEN
811 7 23:6 347 PLAYINDX := -1
812 7 23:5 347 ELSE
813 7 23:6 354 IF PLAYINDX = SCNTOC.RECPERDK[ ZCHAR] THEN
814 7 23:7 368 IF PLAYREC1.STATUS = LOST THEN
815 7 23:8 374 PLAYINDX := BASE04
816 7 23:4 374 END;
817 7 23:2 385 END;
818 7 23:1 385 IF (PLAYINDX = - 1) OR
819 7 23:1 391 (PLAYINDX = SCNTOC.RECPERDK[ ZCHAR]) OR
820 7 23:1 404 (COPY( SCNTOC.GAMENAME, 1, 10) <> 'THE LEGACY') THEN
821 7 23:2 437 TRANBAD
822 7 23:1 437 ELSE
823 7 23:2 441 TRANGOOD


This is the pcode I need to generate:
6192 9E 02 MVL. MOVELEFT
6194 B2 01 82 23 LDA. PUSH #ACTREC(-01).0223
6198 05 SLDC. PUSH #0005
6199 A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
619B F8 SIND. PUSH (TOS)^.0
619C CC 80 D2 STL. MP.D2 := (TOS)
619F CA 80 D3 LDL. PUSH MP.D3
61A2 C6 80 FE LLA. PUSH #MP.FE
61A5 0A SLDC. PUSH #000A
61A6 BE LDB. PUSH #00, PUSH (TOS-1)^.TOS BYTE
61A7 CB NEQI. PUSH ((TOS-1) <> (TOS))
61A8 C6 82 02 LLA. PUSH #MP.0202
61AB C6 81 FE LLA. PUSH #MP.01FE
61AE B7 04 NEQ. PUSH ((TOS-1) <> (TOS))--STRINGS
61B0 8D LOR. PUSH ((TOS-1) OR (TOS))
61B1 A1 4F UJP. IF NOT (TOS) THEN JUMP TO 6202
61B3 00 SLDC. PUSH #0000
61B4 AB 04 SRO. BASE.04 := (TOS)
61B6 B2 01 82 23 LDA. PUSH #ACTREC(-01).0223
61BA 05 SLDC. PUSH #0005
61BB A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
61BD F8 SIND. PUSH (TOS)^.0
61BE 01 SLDC. PUSH #0001
61BF 95 SBI. PUSH ((TOS-1) - (TOS))
61C0 CC 82 06 STL. MP.0206 := (TOS)
61C3 EB SLDO. PUSH BASE.04
61C4 CA 82 06 LDL. PUSH MP.0206
61C7 C8 LEQI. PUSH ((TOS-1) <= (TOS))
61C8 A1 38 UJP. IF NOT (TOS) THEN JUMP TO 6202
61CA C6 01 LLA. PUSH #MP.01
61CC EB SLDO. PUSH BASE.04
61CD AE 0B CIP. CALL INTERMEDIATE PROCEDURE: 0B
61CF C6 01 LLA. PUSH #MP.01
61D1 C6 69 LLA. PUSH #MP.69
61D3 AF 04 EQU. PUSH ((TOS-1) = (TOS))--STRINGS
61D5 CA 15 LDL. PUSH MP.15
61D7 07 SLDC. PUSH #0007
61D8 CB NEQI. PUSH ((TOS-1) <> (TOS))
61D9 84 LAND. PUSH ((TOS-1) AND (TOS))
61DA A1 07 UJP. IF NOT (TOS) THEN JUMP TO 61E3
61DC 01 SLDC. PUSH #0001
61DD 91 NGI. PUSH -(TOS) (2'S COMP)
61DE CC 80 D2 STL. MP.D2 := (TOS)
61E1 B9 18 UJP. JUMP TO 61FB
61E3 CA 80 D2 LDL. PUSH MP.D2
61E6 B2 01 82 23 LDA. PUSH #ACTREC(-01).0223
61EA 05 SLDC. PUSH #0005
61EB A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
61ED F8 SIND. PUSH (TOS)^.0
61EE C3 EQUI. PUSH ((TOS-1) = (TOS))
61EF A1 0A UJP. IF NOT (TOS) THEN JUMP TO 61FB
61F1 CA 15 LDL. PUSH MP.15
61F3 07 SLDC. PUSH #0007
61F4 C3 EQUI. PUSH ((TOS-1) = (TOS))
61F5 A1 04 UJP. IF NOT (TOS) THEN JUMP TO 61FB
61F7 EB SLDO. PUSH BASE.04
61F8 CC 80 D2 STL. MP.D2 := (TOS)
61FB EB SLDO. PUSH BASE.04
61FC 01 SLDC. PUSH #0001
61FD 82 ADI. PUSH ((TOS) + (TOS-1))
61FE AB 04 SRO. BASE.04 := (TOS)
6200 B9 F6 UJP. JUMP TO 61C3
6202 CA 80 D2 LDL. PUSH MP.D2
6205 01 SLDC. PUSH #0001
6206 91 NGI. PUSH -(TOS) (2'S COMP)
6207 C3 EQUI. PUSH ((TOS-1) = (TOS))
6208 CA 80 D2 LDL. PUSH MP.D2
620B B2 01 82 23 LDA. PUSH #ACTREC(-01).0223
620F 05 SLDC. PUSH #0005
6210 A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
6212 F8 SIND. PUSH (TOS)^.0
6213 C3 EQUI. PUSH ((TOS-1) = (TOS))
6214 8D LOR. PUSH ((TOS-1) OR (TOS))
6215 A1 04 UJP. IF NOT (TOS) THEN JUMP TO 621B
6217 CE 1A CLP. CALL CHILD PROCEDURE: 1A
6219 B9 02 UJP. JUMP TO 621D
621B CE 1B CLP. CALL CHILD PROCEDURE: 1B
621D AD 00 RNP. RETURN FROM NON-BASE PROCEDURE.

We need BASE.04 in this code. RANDNUM(?). Eventually I will rename the variables correclty so that BASE.204 is RANDNUM for Wiz1.

This code will do it:

793 7 23:0 0 BEGIN (* TRANSFER *)
794 7 23:1 0 IF RTNESC( 'MOVE CHARS BETWEEN SCENARIOS',
795 7 23:1 31 '') THEN
796 7 23:2 40 EXIT( TRANSFER);
797 7 23:1 44 RDSCNTOC( 'INSERT SCENARIO WITH CHAR(S) TO BE MOVED');
798 7 23:1 89 UNITREAD( DRIVE1, BL5BUFF, SIZEOF( BL5BUFF), SERIALBL, 0);
799 7 23:1 101 MPD3 := BL5BUFF[ 10];
800 7 23:1 109 MOVELEFT( BL5BUFF, SERIALFR, 7);
801 7 23:1 120 BL5BUFF[ 12] := CHR( (ORD( BL5BUFF[ 12]) + BASE204) MOD 256);
802 7 23:1 139 UNITWRITE( DRIVE1, BL5BUFF, SIZEOF( BL5BUFF), SERIALBL, 0);
803 7 23:1 151 FRMRNDID := BL5BUFF[ 12];
804 7 23:1 159 REMOVCHR;
805 7 23:1 161 PLAYFRMI := PLAYINDX;
806 7 23:1 167 FRMGAMNM := SCNTOC.GAMENAME;
807 7 23:1 176 RDSCNTOC( 'INSERT SCENARIO TO MOVE CHAR(S) TO');
808 7 23:1 215 UNITREAD( DRIVE1, BL5BUFF, SIZEOF( BL5BUFF), SERIALBL, 0);
809 7 23:1 227 MOVELEFT( BL5BUFF, SERIALTO, 7);
810 7 23:1 238 PLAYINDX := SCNTOC.RECPERDK[ ZCHAR];
811 7 23:1 249
812 7 23:1 249 IF (MPD3 <> BL5BUFF[ 10]) OR
813 7 23:1 258 (SERIALFR <> SERIALTO) THEN
814 7 23:2 269 BEGIN
815 7 23:3 269 FOR RANDNUM := 0 TO SCNTOC.RECPERDK[ ZCHAR] - 1 DO
816 7 23:4 292 BEGIN
817 7 23:5 292 RDCHARAC( PLAYREC1, RANDNUM);
818 7 23:5 297 IF (PLAYREC1.NAME = PLAYREC2.NAME) AND
819 7 23:5 303 (PLAYREC1.STATUS <> LOST) THEN
820 7 23:6 310 PLAYINDX := -1
821 7 23:5 310 ELSE
822 7 23:6 317 IF PLAYINDX = SCNTOC.RECPERDK[ ZCHAR] THEN
823 7 23:7 331 IF PLAYREC1.STATUS = LOST THEN
824 7 23:8 337 PLAYINDX := RANDNUM
825 7 23:4 337 END;
826 7 23:2 348 END;
827 7 23:2 348
828 7 23:1 348 IF (PLAYINDX = -1) OR
829 7 23:1 354 (PLAYINDX = SCNTOC.RECPERDK[ ZCHAR]) THEN
830 7 23:2 369 TRANBAD
831 7 23:1 369 ELSE
832 7 23:2 373 TRANGOOD
833 7 23:0 373 END; (* TRANSFER *)


P070118
Comparing the pcode from LOL with the pcode needed for Wiz1, most of it matched, but there was additional code in Wiz1 that they removed for LOL.
That code is the following, and occurs just after the "UNTIL PASSFROM = PLAYREC2.PASSWORD;" instruction:

5F19 01 SLDC. PUSH #0001
5F1A CC 2B STL. MP.2B := (TOS)
5F1C B6 01 80 86 LOD. PUSH ACTREC(-01).86
5F20 CC 2C STL. MP.2C := (TOS)
5F22 CA 2B LDL. PUSH MP.2B
5F24 CA 2C LDL. PUSH MP.2C
5F26 C8 LEQI. PUSH ((TOS-1) <= (TOS))
5F27 A1 19 UJP. IF NOT (TOS) THEN JUMP TO 5F42
5F29 B2 01 80 87 LDA. PUSH #ACTREC(-01).87
5F2D CA 2B LDL. PUSH MP.2B
5F2F 01 SLDC. PUSH #0001
5F30 95 SBI. PUSH ((TOS-1) - (TOS))
5F31 A4 04 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=04
5F33 FB SIND. PUSH (TOS)^.3
5F34 5D SLDC. PUSH #005D
5F35 C5 GRTI. PUSH ((TOS-1) > (TOS))
5F36 A1 02 UJP. IF NOT (TOS) THEN JUMP TO 5F3A
5F38 CE 19 CLP. CALL CHILD PROCEDURE: 19
5F3A CA 2B LDL. PUSH MP.2B
5F3C 01 SLDC. PUSH #0001
5F3D 82 ADI. PUSH ((TOS) + (TOS-1))
5F3E CC 2B STL. MP.2B := (TOS)
5F40 B9 EE UJP. JUMP TO 5F22

The following code will generate the pcode above:

FOR PLAYERI := 1 TO PLAYREC2.POSS.POSSCNT DO
BEGIN
IF PLAYREC2.POSS.POSSESS[ PLAYERI].EQINDEX > 93 THEN
P070119
END;

How did I produce that code? Devine inspiration? My notes from 2 weeks ago are not very clear, but by looking at the pcode from LOL, and looking at the other pcode generated in this routine, I could start to match things up.

For example, MP.02 was used earlier in the routine and I noticed the name I gave to that variable was PLAYERI. I did the same thing for other variables like actrec(-02).0206 with SCNTOC and actrec(-01).69 with PLAYREC2.

I also found I needed to change the message from "[RET]" to "[RETURN]".


P070119
This procedure is in Wiz1, but is not in LOL.
This is the pcode we want:

FILE: 7 SEG: 1 PROC: 25

JTAB FOR SUBROUTINE 25:
DATA SIZE: 0
PARAM SIZE: 0
EXIT AT: 5D74
ENTER AT: 5D36
PROC NUMBER: 25
LEXICAL LEVEL: 4
5D36 B6 05 03 LOD. PUSH ACTREC(-05).03
5D39 CD 00 16 CXP. CALL EXTERNAL PROCEDURE: 16 IN SEGMENT: 00
5D3C B6 05 03 LOD. PUSH ACTREC(-05).03
5D3F A6 22 2A 2A 20 43 48 41 52 20 48 41 53 20 4E 4F 4E 2D 58 46 45 52 52 41 42 4C 45 20 49 54 45 4D 53 20 2A 2A
** CHAR HAS NON-XFERRABLE ITEMS **
LSA. PUSH #(PC+1) POINTER TO THE STRING
5D63 D7 NOP. NOP
5D64 00 SLDC. PUSH #0000
5D65 CD 00 13 CXP. CALL EXTERNAL PROCEDURE: 13 IN SEGMENT: 00
5D68 B6 05 03 LOD. PUSH ACTREC(-05).03
5D6B CD 00 16 CXP. CALL EXTERNAL PROCEDURE: 16 IN SEGMENT: 00
5D6E AE 0E CIP. CALL INTERMEDIATE PROCEDURE: 0E
5D70 07 SLDC. PUSH #0007
5D71 17 SLDC. PUSH #0017
5D72 9E 04 EXIT. EXIT FROM PROCEDURE. (TOS)=PROC# (TOS-1)=SEG#
5D74 AD 00 RNP. RETURN FROM NON-BASE PROCEDURE.

Things to note:
"PROCEDURE: 16 IN SEGMENT: 0" is "WRITELN;"
"PROCEDURE: 13 IN SEGMENT: 0" is "WRITELN( Astring);"
INTERMEDIATE PROCEDURE: 0E" is "PRESSRET (* P07010E *) "
"EXIT( 07,017)" is "EXIT( TRANSFER)"

Notice that wicked use of EXIT() to exit P070119, then exit P070118, then exit P070117 back to its caller. It's wicked because if you look at P070118 or P070117 you would not know the code does anything more than call a PROCEDURE and then return to the next instruction.


P07011A
This is TRANBAD. Note the numbering is now slightly different between LOL and Wiz1.

pcode we want:

FILE: 7 SEG: 1 PROC: 26

JTAB FOR SUBROUTINE 26:
DATA SIZE: 0
PARAM SIZE: 0
EXIT AT: 6064
ENTER AT: 5F70
PROC NUMBER: 26
LEXICAL LEVEL: 3
5F70 B6 01 80 D3 LOD. PUSH ACTREC(-01).D3
5F74 B2 01 80 FE LDA. PUSH #ACTREC(-01).FE
5F78 0A SLDC. PUSH #000A
5F79 BE LDB. PUSH #00, PUSH (TOS-1)^.TOS BYTE
5F7A C3 EQUI. PUSH ((TOS-1) = (TOS))
5F7B B2 01 82 02 LDA. PUSH #ACTREC(-01).0202
5F7F B2 01 81 FE LDA. PUSH #ACTREC(-01).01FE
5F83 AF 04 EQU. PUSH ((TOS-1) = (TOS))--STRINGS
5F85 84 LAND. PUSH ((TOS-1) AND (TOS))
5F86 A1 2A UJP. IF NOT (TOS) THEN JUMP TO 5FB2
5F88 D7 NOP. NOP
5F89 A6 23 43 41 4E 54 20 43 4F 50 59 20 54 4F 20 42 41 43 4B 55 50 20 2D 20 50 55 54 20 49 4E 20 53 4F 55 52 43 45
CANT COPY TO BACKUP - PUT IN SOURCE
LSA. PUSH #(PC+1) POINTER TO THE STRING
5FAE AE 09 CIP. CALL INTERMEDIATE PROCEDURE: 09
5FB0 B9 51 UJP. JUMP TO 6003
5FB2 B6 01 80 D2 LOD. PUSH ACTREC(-01).D2
5FB6 01 SLDC. PUSH #0001
5FB7 91 NGI. PUSH -(TOS) (2'S COMP)
5FB8 C3 EQUI. PUSH ((TOS-1) = (TOS))
5FB9 A1 25 UJP. IF NOT (TOS) THEN JUMP TO 5FE0
5FBB A6 1E 44 55 50 4C 49 43 41 54 45 20 4E 41 4D 45 20 2D 20 50 55 54 20 49 4E 20 53 4F 55 52 43 45
DUPLICATE NAME - PUT IN SOURCE
LSA. PUSH #(PC+1) POINTER TO THE STRING
5FDB D7 NOP. NOP
5FDC AE 09 CIP. CALL INTERMEDIATE PROCEDURE: 09
5FDE B9 23 UJP. JUMP TO 6003
5FE0 D7 NOP. NOP
5FE1 A6 1E 41 4C 4C 20 53 4C 4F 54 53 20 55 53 45 44 20 2D 20 50 55 54 20 49 4E 20 53 4F 55 52 43 45
ALL SLOTS USED - PUT IN SOURCE
LSA. PUSH #(PC+1) POINTER TO THE STRING
6001 AE 09 CIP. CALL INTERMEDIATE PROCEDURE: 09
6003 04 SLDC. PUSH #0004
6004 B2 01 80 FE LDA. PUSH #ACTREC(-01).FE
6008 00 SLDC. PUSH #0000
6009 C7 00 02 LDCI. PUSH #0200
600C 05 SLDC. PUSH #0005
600D 00 SLDC. PUSH #0000
600E 9E 05 READ FROM VOL#
6010 B2 02 82 06 LDA. PUSH #ACTREC(-02).0206
6014 B2 01 80 D5 LDA. PUSH #ACTREC(-01).D5
6018 AF 04 EQU. PUSH ((TOS-1) = (TOS))--STRINGS
601A B6 01 80 D4 LOD. PUSH ACTREC(-01).D4
601E B2 01 80 FE LDA. PUSH #ACTREC(-01).FE
6022 0C SLDC. PUSH #000C
6023 BE LDB. PUSH #00, PUSH (TOS-1)^.TOS BYTE
6024 C3 EQUI. PUSH ((TOS-1) = (TOS))
6025 84 LAND. PUSH ((TOS-1) AND (TOS))
6026 A1 F6 UJP. IF NOT (TOS) THEN JUMP TO 5F70
6028 B2 01 01 LDA. PUSH #ACTREC(-01).01
602B B6 01 80 D1 LOD. PUSH ACTREC(-01).D1
602F AE 0B CIP. CALL INTERMEDIATE PROCEDURE: 0B
6031 B2 01 01 LDA. PUSH #ACTREC(-01).01
6034 B2 01 69 LDA. PUSH #ACTREC(-01).69
6037 AF 04 EQU. PUSH ((TOS-1) = (TOS))--STRINGS
6039 A1 F4 UJP. IF NOT (TOS) THEN JUMP TO 5F70
603B B2 01 69 LDA. PUSH #ACTREC(-01).69
603E B6 01 80 D1 LOD. PUSH ACTREC(-01).D1
6042 AE 0C CIP. CALL INTERMEDIATE PROCEDURE: 0C
6044 AE 0D CIP. CALL INTERMEDIATE PROCEDURE: 0D
6046 D7 NOP. NOP
6047 A6 17 43 48 41 52 20 48 41 53 20 42 45 45 4E 20 50 55 54 20 42 41 43 4B 2E
CHAR HAS BEEN PUT BACK.
LSA. PUSH #(PC+1) POINTER TO THE STRING
6060 AE 0F CIP. CALL INTERMEDIATE PROCEDURE: 0F
6062 AE 0E CIP. CALL INTERMEDIATE PROCEDURE: 0E
6064 AD 00 RNP. RETURN FROM NON-BASE PROCEDURE.
6066 F6 SLDO. PUSH BASE.0F
6067 00 SLDC. PUSH #0000
6068 F8 SIND. PUSH (TOS)^.0
6069 00 SLDC. PUSH #0000


LOL TRANBAD code:

745 7 25:D 1 PROCEDURE TRANBAD; (* P050119 *)
746 7 25:D 1
747 7 25:0 0 BEGIN
748 7 25:1 0 REPEAT
749 7 25:2 0 REPEAT
750 7 25:3 0 IF PLAYINDX = -1 THEN
751 7 25:4 9 RDSCNTOC( 'DUPLICATE NAME - PUT IN SOURCE')
752 7 25:3 42 ELSE IF PLAYINDX = SCNTOC.RECPERDK[ ZCHAR] THEN
753 7 25:5 61 RDSCNTOC( 'ALL SLOTS USED - PUT IN SOURCE')
754 7 25:4 94 ELSE IF TODSKERR THEN
755 7 25:6 104 RDSCNTOC( 'WRITE PROTECTED - PUT IN SOURCE')
756 7 25:5 138 ELSE
757 7 25:6 142 RDSCNTOC( 'NOT A LOL DISK - PUT IN SOURCE');
758 7 25:3 177 UNITREAD( DRIVE1, BL5BUFF, SIZEOF( BL5BUFF), SERIALBL, 0)
759 7 25:2 190 UNTIL (SCNTOC.GAMENAME = FRMGAMNM) AND (FRMRNDID = BL5BUFF[ 12]);
760 7 25:2 214
761 7 25:2 214 RDCHARAC( PLAYREC1, PLAYFRMI)
762 7 25:2 221
763 7 25:1 221 UNTIL PLAYREC1.NAME = PLAYREC2.NAME;
764 7 25:1 233 WRCHARAC( PLAYREC2, PLAYFRMI);
765 7 25:1 242 WRICACHE;
766 7 25:1 244 TITLESCR( 'CHAR HAS BEEN PUT BACK.');
767 7 25:1 272 PRESSRET
768 7 25:0 272 END;

FILE: 5 SEG: 1 PROC: 25

JTAB FOR SUBROUTINE 25:
DATA SIZE: 0
PARAM SIZE: 0
EXIT AT: 6000
ENTER AT: 5EEE
PROC NUMBER: 25
LEXICAL LEVEL: 3
5EEE B6 01 80 D2 LOD. PUSH ACTREC(-01).D2
5EF2 01 SLDC. PUSH #0001
5EF3 91 NGI. PUSH -(TOS) (2'S COMP)
5EF4 C3 EQUI. PUSH ((TOS-1) = (TOS))
5EF5 A1 25 UJP. IF NOT (TOS) THEN JUMP TO 5F1C
5EF7 A6 1E 44 55 50 4C 49 43 41 54 45 20 4E 41 4D 45 20 2D 20 50 55 54 20 49 4E 20 53 4F 55 52 43 45
DUPLICATE NAME - PUT IN SOURCE
LSA. PUSH #(PC+1) POINTER TO THE STRING
5F17 D7 NOP. NOP
5F18 AE 09 CIP. CALL INTERMEDIATE PROCEDURE: 09
5F1A B9 F6 UJP. JUMP TO 5F9F
5F1C B6 01 80 D2 LOD. PUSH ACTREC(-01).D2
5F20 B2 02 82 22 LDA. PUSH #ACTREC(-02).0222
5F24 05 SLDC. PUSH #0005
5F25 A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
5F27 F8 SIND. PUSH (TOS)^.0
5F28 C3 EQUI. PUSH ((TOS-1) = (TOS))
5F29 A1 25 UJP. IF NOT (TOS) THEN JUMP TO 5F50
5F2B A6 1E 41 4C 4C 20 53 4C 4F 54 53 20 55 53 45 44 20 2D 20 50 55 54 20 49 4E 20 53 4F 55 52 43 45
ALL SLOTS USED - PUT IN SOURCE
LSA. PUSH #(PC+1) POINTER TO THE STRING
5F4B D7 NOP. NOP
5F4C AE 09 CIP. CALL INTERMEDIATE PROCEDURE: 09
5F4E B9 4F UJP. JUMP TO 5F9F
5F50 B6 01 82 06 LOD. PUSH ACTREC(-01).0206
5F54 A1 26 UJP. IF NOT (TOS) THEN JUMP TO 5F7C
5F56 D7 NOP. NOP
5F57 A6 1F 57 52 49 54 45 20 50 52 4F 54 45 43 54 45 44 20 2D 20 50 55 54 20 49 4E 20 53 4F 55 52 43 45
WRITE PROTECTED - PUT IN SOURCE
LSA. PUSH #(PC+1) POINTER TO THE STRING
5F78 AE 09 CIP. CALL INTERMEDIATE PROCEDURE: 09
5F7A B9 23 UJP. JUMP TO 5F9F
5F7C D7 NOP. NOP
5F7D A6 1E 4E 4F 54 20 41 20 4C 4F 4C 20 44 49 53 4B 20 2D 20 50 55 54 20 49 4E 20 53 4F 55 52 43 45
NOT A LOL DISK - PUT IN SOURCE
LSA. PUSH #(PC+1) POINTER TO THE STRING
5F9D AE 09 CIP. CALL INTERMEDIATE PROCEDURE: 09
5F9F 04 SLDC. PUSH #0004
5FA0 B2 01 80 FE LDA. PUSH #ACTREC(-01).FE
5FA4 00 SLDC. PUSH #0000
5FA5 C7 00 02 LDCI. PUSH #0200
5FA8 05 SLDC. PUSH #0005
5FA9 00 SLDC. PUSH #0000
5FAA 9E 05 READ FROM VOL#
5FAC B2 02 82 05 LDA. PUSH #ACTREC(-02).0205
5FB0 B2 01 80 D5 LDA. PUSH #ACTREC(-01).D5
5FB4 AF 04 EQU. PUSH ((TOS-1) = (TOS))--STRINGS
5FB6 B6 01 80 D4 LOD. PUSH ACTREC(-01).D4
5FBA B2 01 80 FE LDA. PUSH #ACTREC(-01).FE
5FBE 0C SLDC. PUSH #000C
5FBF BE LDB. PUSH #00, PUSH (TOS-1)^.TOS BYTE
5FC0 C3 EQUI. PUSH ((TOS-1) = (TOS))
5FC1 84 LAND. PUSH ((TOS-1) AND (TOS))
5FC2 A1 F4 UJP. IF NOT (TOS) THEN JUMP TO 5EEE
5FC4 B2 01 01 LDA. PUSH #ACTREC(-01).01
5FC7 B6 01 80 D1 LOD. PUSH ACTREC(-01).D1
5FCB AE 0B CIP. CALL INTERMEDIATE PROCEDURE: 0B
5FCD B2 01 01 LDA. PUSH #ACTREC(-01).01
5FD0 B2 01 69 LDA. PUSH #ACTREC(-01).69
5FD3 AF 04 EQU. PUSH ((TOS-1) = (TOS))--STRINGS
5FD5 A1 F2 UJP. IF NOT (TOS) THEN JUMP TO 5EEE
5FD7 B2 01 69 LDA. PUSH #ACTREC(-01).69
5FDA B6 01 80 D1 LOD. PUSH ACTREC(-01).D1
5FDE AE 0C CIP. CALL INTERMEDIATE PROCEDURE: 0C
5FE0 AE 0D CIP. CALL INTERMEDIATE PROCEDURE: 0D
5FE2 D7 NOP. NOP
5FE3 A6 17 43 48 41 52 20 48 41 53 20 42 45 45 4E 20 50 55 54 20 42 41 43 4B 2E
CHAR HAS BEEN PUT BACK.
LSA. PUSH #(PC+1) POINTER TO THE STRING
5FFC AE 0F CIP. CALL INTERMEDIATE PROCEDURE: 0F
5FFE AE 0E CIP. CALL INTERMEDIATE PROCEDURE: 0E
6000 AD 00 RNP. RETURN FROM NON-BASE PROCEDURE.
6002 14 SLDC. PUSH #0014
6003 01 SLDC. PUSH #0001
6004 16 SLDC. PUSH #0016
6005 01 SLDC. PUSH #0001
6006 67 SLDC. PUSH #0067
6007 00 SLDC. PUSH #0000


ACTREC( -1).xxx refers to TRANSFER variables

674 7 23:D 1 PROCEDURE TRANSFER; (* P050117 *)
675 7 23:D 1
676 7 23:D 1 VAR
(-1).01 677 7 23:D 1 PLAYREC1 : TCHAR;
(-1).69 678 7 23:D 105 PLAYREC2 : TCHAR;
679 7 23:D 209 PLAYFRMI : INTEGER;
(-1).D2 680 7 23:D 210 PLAYINDX : INTEGER;
(-1).D3 681 7 23:D 211 MPD3 : CHAR;
(-1).D4 682 7 23:D 212 FRMRNDID : CHAR;
(-1).D5 683 7 23:D 213 FRMGAMNM : STRING;
(-1).FE 684 7 23:D 254 BL5BUFF : PACKED ARRAY[ 0..511] OF CHAR;
(-1).1FE 685 7 23:D 510 SERIALTO : STRING[ 7];
(-1).0202 686 7 23:D 514 SERIALFR : STRING[ 7];
687 7 23:D 518 TODSKERR : BOOLEAN;

Re-engineered TRANBAD (* P07011A *)

767 7 26:D 1 PROCEDURE TRANBAD; (* P07011A *)
768 7 26:D 1
769 7 26:0 0 BEGIN
770 7 26:1 0 REPEAT
771 7 26:2 0 REPEAT
772 7 26:3 0 IF (MPD3 = BL5BUFF[ 10]) AND
773 7 26:3 11 (SERIALFR = SERIALTO) THEN
774 7 26:4 24 RDSCNTOC( 'CANT COPY TO BACKUP - PUT IN SOURCE')
775 7 26:3 62 ELSE IF (PLAYINDX = -1) THEN
776 7 26:5 75 RDSCNTOC( 'DUPLICATE NAME - PUT IN SOURCE')
777 7 26:4 108 ELSE
778 7 26:5 112 RDSCNTOC( 'ALL SLOTS USED - PUT IN SOURCE');
779 7 26:3 147 UNITREAD( DRIVE1, BL5BUFF, SIZEOF( BL5BUFF), SERIALBL, 0)
780 7 26:2 160 UNTIL (SCNTOC.GAMENAME = FRMGAMNM) AND (FRMRNDID = BL5BUFF[ 12]);
781 7 26:2 184 RDCHARAC( PLAYREC1, PLAYFRMI)
782 7 26:1 191 UNTIL PLAYREC1.NAME = PLAYREC2.NAME;
783 7 26:1 203 WRCHARAC( PLAYREC2, PLAYFRMI);
784 7 26:1 212 WRICACHE;
785 7 26:1 214 TITLESCR( 'CHAR HAS BEEN PUT BACK.');
786 7 26:1 242 PRESSRET
787 7 26:0 242 END;


P07011B. In LOL this is P05011A.
The only difference is that LOL added a few more lines of code, otherwise identical.

771 7 26:D 1 PROCEDURE TRANGOOD; (* P05011A *)
772 7 26:D 1
773 7 26:0 0 BEGIN
remove 774 7 26:1 0 IF PLAYREC2.POSS.POSSCNT > 0 THEN
remove 775 7 26:2 8 IF PLAYREC2.POSS.POSSESS[ 1].EQINDEX < 1000 THEN
remove 776 7 26:3 24 PLAYREC2.POSS.POSSCNT := 0;
777 7 26:1 29 WRCHARAC( PLAYREC2, PLAYINDX);
778 7 26:1 38 WRICACHE;
779 7 26:1 40 TITLESCR( 'CHAR HAS BEEN MOVED');
780 7 26:1 64 PRESSRET
781 7 26:0 64 END;


P07011C (does not have an LOL equivalent)
P07011C has 3 child procedures
P07011D
P07011E
P07011F

This is the pcode we want:

FILE: 7 SEG: 1 PROC: 28

JTAB FOR SUBROUTINE 28:
DATA SIZE: 3800
PARAM SIZE: 0
EXIT AT: 6555
ENTER AT: 62B2
PROC NUMBER: 28
LEXICAL LEVEL: 2
62B2 D7 NOP. NOP
62B3 A6 1A 4D 41 4B 45 20 4E 45 57 20 53 43 45 4E 41 52 49 4F 20 44 49 53 4B 45 54 54 45
MAKE NEW SCENARIO DISKETTE
LSA. PUSH #(PC+1) POINTER TO THE STRING
62CF A6 00
W
LSA. PUSH #(PC+1) POINTER TO THE STRING
62D1 D7 NOP. NOP
62D2 00 SLDC. PUSH #0000
62D3 00 SLDC. PUSH #0000
62D4 AE 10 CIP. CALL INTERMEDIATE PROCEDURE: 10
62D6 A1 04 UJP. IF NOT (TOS) THEN JUMP TO 62DC
62D8 07 SLDC. PUSH #0007
62D9 1C SLDC. PUSH #001C
62DA 9E 04 EXIT. EXIT FROM PROCEDURE. (TOS)=PROC# (TOS-1)=SEG#
62DC D7 NOP. NOP
62DD A6 1A 4D 41 4B 45 20 4E 45 57 20 53 43 45 4E 41 52 49 4F 20 44 49 53 4B 45 54 54 45
MAKE NEW SCENARIO DISKETTE
LSA. PUSH #(PC+1) POINTER TO THE STRING
62F9 AE 0F CIP. CALL INTERMEDIATE PROCEDURE: 0F
62FB B6 03 03 LOD. PUSH ACTREC(-03).03
62FE D7 NOP. NOP
62FF A6 26 50 4C 41 43 45 20 4F 52 49 47 49 4E 41 4C 20 4D 41 53 54 45 52 20 53 43 45 4E 41 52 49 4F 20 53 49 44 45 20 49 4E
PLACE ORIGINAL MASTER SCENARIO SIDE IN
LSA. PUSH #(PC+1) POINTER TO THE STRING
6327 00 SLDC. PUSH #0000
6328 CD 00 13 CXP. CALL EXTERNAL PROCEDURE: 13 IN SEGMENT: 00
632B B6 03 03 LOD. PUSH ACTREC(-03).03
632E CD 00 16 CXP. CALL EXTERNAL PROCEDURE: 16 IN SEGMENT: 00
6331 B6 03 03 LOD. PUSH ACTREC(-03).03
6334 D7 NOP. NOP
6335 A6 26 44 52 49 56 45 20 32 2C 20 20 41 4E 44 20 41 20 20 46 4F 52 4D 41 54 54 45 44 20 44 49 53 4B 45 54 54 45 20 49 4E
DRIVE 2, AND A FORMATTED DISKETTE IN
LSA. PUSH #(PC+1) POINTER TO THE STRING
635D 00 SLDC. PUSH #0000
635E CD 00 13 CXP. CALL EXTERNAL PROCEDURE: 13 IN SEGMENT: 00
6361 B6 03 03 LOD. PUSH ACTREC(-03).03
6364 CD 00 16 CXP. CALL EXTERNAL PROCEDURE: 16 IN SEGMENT: 00
6367 B6 03 03 LOD. PUSH ACTREC(-03).03
636A D7 NOP. NOP
636B A6 08 44 52 49 56 45 20 31 2E
DRIVE 1.
LSA. PUSH #(PC+1) POINTER TO THE STRING
6375 00 SLDC. PUSH #0000
6376 CD 00 13 CXP. CALL EXTERNAL PROCEDURE: 13 IN SEGMENT: 00
6379 B6 03 03 LOD. PUSH ACTREC(-03).03
637C CD 00 16 CXP. CALL EXTERNAL PROCEDURE: 16 IN SEGMENT: 00
637F B6 03 03 LOD. PUSH ACTREC(-03).03
6382 CD 00 16 CXP. CALL EXTERNAL PROCEDURE: 16 IN SEGMENT: 00
6385 B6 03 03 LOD. PUSH ACTREC(-03).03
6388 D7 NOP. NOP
6389 A6 1C 52 45 4D 45 4D 42 45 52 20 3A 20 4F 52 49 47 49 4E 41 4C 3D 32 2C 20 4E 45 57 3D 31
REMEMBER : ORIGINAL=2, NEW=1
LSA. PUSH #(PC+1) POINTER TO THE STRING
63A7 00 SLDC. PUSH #0000
63A8 CD 00 13 CXP. CALL EXTERNAL PROCEDURE: 13 IN SEGMENT: 00
63AB B6 03 03 LOD. PUSH ACTREC(-03).03
63AE CD 00 16 CXP. CALL EXTERNAL PROCEDURE: 16 IN SEGMENT: 00
63B1 AE 0E CIP. CALL INTERMEDIATE PROCEDURE: 0E
63B3 A6 20 4D 41 4B 49 4E 47 20 53 43 45 4E 41 52 49 4F 20 2D 20 44 4F 20 4E 4F 54 20 44 49 53 54 55 52 42
MAKING SCENARIO - DO NOT DISTURB
LSA. PUSH #(PC+1) POINTER TO THE STRING
63D5 D7 NOP. NOP
63D6 AE 0F CIP. CALL INTERMEDIATE PROCEDURE: 0F
63D8 05 SLDC. PUSH #0005
63D9 A6 0D 53 43 45 4E 41 52 49 4F 2E 44 41 54 41
SCENARIO.DATA
LSA. PUSH #(PC+1) POINTER TO THE STRING
63E8 D7 NOP. NOP
63E9 00 SLDC. PUSH #0000
63EA 00 SLDC. PUSH #0000
63EB AE 05 CIP. CALL INTERMEDIATE PROCEDURE: 05
63ED B8 01 82 03 STR. ACTREC(-01).0203 := TOS
63F1 B6 01 82 03 LOD. PUSH ACTREC(-01).0203
63F5 00 SLDC. PUSH #0000
63F6 C9 LESI. PUSH ((TOS-1) < (TOS))
63F7 A1 02 UJP. IF NOT (TOS) THEN JUMP TO 63FB
63F9 CE 1F CLP. CALL CHILD PROCEDURE: 1F
63FB 05 SLDC. PUSH #0005
63FC C6 86 6C LLA. PUSH #MP.066C
63FF 00 SLDC. PUSH #0000
6400 C7 00 02 LDCI. PUSH #0200
6403 05 SLDC. PUSH #0005
6404 00 SLDC. PUSH #0000
6405 9E 05 READ FROM VOL#
6407 9E 22 PUSH I/O ERROR STATUS
6409 00 SLDC. PUSH #0000
640A CB NEQI. PUSH ((TOS-1) <> (TOS))
640B A1 02 UJP. IF NOT (TOS) THEN JUMP TO 640F
640D CE 1E CLP. CALL CHILD PROCEDURE: 1E
640F C6 86 6C LLA. PUSH #MP.066C
6412 0A SLDC. PUSH #000A
6413 BE LDB. PUSH #00, PUSH (TOS-1)^.TOS BYTE
6414 00 SLDC. PUSH #0000
6415 CB NEQI. PUSH ((TOS-1) <> (TOS))
6416 A1 14 UJP. IF NOT (TOS) THEN JUMP TO 642C
6418 D7 NOP. NOP
6419 A6 0F 4E 4F 54 20 4D 41 53 54 45 52 20 49 4E 20 32
NOT MASTER IN 2
LSA. PUSH #(PC+1) POINTER TO THE STRING
642A CE 1D CLP. CALL CHILD PROCEDURE: 1D
642C C6 86 6C LLA. PUSH #MP.066C
642F 0B SLDC. PUSH #000B
6430 BE LDB. PUSH #00, PUSH (TOS-1)^.TOS BYTE
6431 01 SLDC. PUSH #0001
6432 82 ADI. PUSH ((TOS) + (TOS-1))
6433 CC 02 STL. MP.02 := (TOS)
6435 D9 SLDL. PUSH MP.02
6436 C7 00 01 LDCI. PUSH #0100
6439 C3 EQUI. PUSH ((TOS-1) = (TOS))
643A A1 03 UJP. IF NOT (TOS) THEN JUMP TO 643F
643C 01 SLDC. PUSH #0001
643D CC 02 STL. MP.02 := (TOS)
643F C6 86 6C LLA. PUSH #MP.066C
6442 0B SLDC. PUSH #000B
6443 D9 SLDL. PUSH MP.02
6444 BF STB. (TOS-2)^.(TOS-1) := TOS BYTE
6445 05 SLDC. PUSH #0005
6446 C6 86 6C LLA. PUSH #MP.066C
6449 00 SLDC. PUSH #0000
644A C7 00 02 LDCI. PUSH #0200
644D 05 SLDC. PUSH #0005
644E 00 SLDC. PUSH #0000
644F 9E 06 WRITE VOL#
6451 9E 22 PUSH I/O ERROR STATUS
6453 00 SLDC. PUSH #0000
6454 CB NEQI. PUSH ((TOS-1) <> (TOS))
6455 A1 02 UJP. IF NOT (TOS) THEN JUMP TO 6459
6457 CE 1E CLP. CALL CHILD PROCEDURE: 1E
6459 00 SLDC. PUSH #0000
645A CC 03 STL. MP.03 := (TOS)
645C 22 SLDC. PUSH #0022
645D CC 87 6C STL. MP.076C := (TOS)
6460 DA SLDL. PUSH MP.03
6461 CA 87 6C LDL. PUSH MP.076C
6464 C8 LEQI. PUSH ((TOS-1) <= (TOS))
6465 A1 61 UJP. IF NOT (TOS) THEN JUMP TO 64C8
6467 05 SLDC. PUSH #0005
6468 B2 01 01 LDA. PUSH #ACTREC(-01).01
646B 00 SLDC. PUSH #0000
646C C7 00 04 LDCI. PUSH #0400
646F 08 SLDC. PUSH #0008
6470 DA SLDL. PUSH MP.03
6471 8F MPI. PUSH ((TOS-1) * (TOS))
6472 00 SLDC. PUSH #0000
6473 9E 05 READ FROM VOL#
6475 9E 22 PUSH I/O ERROR STATUS
6477 00 SLDC. PUSH #0000
6478 CB NEQI. PUSH ((TOS-1) <> (TOS))
6479 A1 02 UJP. IF NOT (TOS) THEN JUMP TO 647D
647B CE 1E CLP. CALL CHILD PROCEDURE: 1E
647D 05 SLDC. PUSH #0005
647E C6 6C LLA. PUSH #MP.6C
6480 00 SLDC. PUSH #0000
6481 C7 00 0C LDCI. PUSH #0C00
6484 08 SLDC. PUSH #0008
6485 DA SLDL. PUSH MP.03
6486 8F MPI. PUSH ((TOS-1) * (TOS))
6487 02 SLDC. PUSH #0002
6488 82 ADI. PUSH ((TOS) + (TOS-1))
6489 00 SLDC. PUSH #0000
648A 9E 05 READ FROM VOL#
648C 9E 22 PUSH I/O ERROR STATUS
648E 00 SLDC. PUSH #0000
648F CB NEQI. PUSH ((TOS-1) <> (TOS))
6490 A1 02 UJP. IF NOT (TOS) THEN JUMP TO 6494
6492 CE 1E CLP. CALL CHILD PROCEDURE: 1E
6494 04 SLDC. PUSH #0004
6495 B2 01 01 LDA. PUSH #ACTREC(-01).01
6498 00 SLDC. PUSH #0000
6499 C7 00 04 LDCI. PUSH #0400
649C 08 SLDC. PUSH #0008
649D DA SLDL. PUSH MP.03
649E 8F MPI. PUSH ((TOS-1) * (TOS))
649F 00 SLDC. PUSH #0000
64A0 9E 06 WRITE VOL#
64A2 9E 22 PUSH I/O ERROR STATUS
64A4 00 SLDC. PUSH #0000
64A5 CB NEQI. PUSH ((TOS-1) <> (TOS))
64A6 A1 02 UJP. IF NOT (TOS) THEN JUMP TO 64AA
64A8 CE 1E CLP. CALL CHILD PROCEDURE: 1E
64AA 04 SLDC. PUSH #0004
64AB C6 6C LLA. PUSH #MP.6C
64AD 00 SLDC. PUSH #0000
64AE C7 00 0C LDCI. PUSH #0C00
64B1 08 SLDC. PUSH #0008
64B2 DA SLDL. PUSH MP.03
64B3 8F MPI. PUSH ((TOS-1) * (TOS))
64B4 02 SLDC. PUSH #0002
64B5 82 ADI. PUSH ((TOS) + (TOS-1))
64B6 00 SLDC. PUSH #0000
64B7 9E 06 WRITE VOL#
64B9 9E 22 PUSH I/O ERROR STATUS
64BB 00 SLDC. PUSH #0000
64BC CB NEQI. PUSH ((TOS-1) <> (TOS))
64BD A1 02 UJP. IF NOT (TOS) THEN JUMP TO 64C1
64BF CE 1E CLP. CALL CHILD PROCEDURE: 1E
64C1 DA SLDL. PUSH MP.03
64C2 01 SLDC. PUSH #0001
64C3 82 ADI. PUSH ((TOS) + (TOS-1))
64C4 CC 03 STL. MP.03 := (TOS)
64C6 B9 F6 UJP. JUMP TO 6460
64C8 C6 86 6C LLA. PUSH #MP.066C
64CB 0A SLDC. PUSH #000A
64CC C6 86 6C LLA. PUSH #MP.066C
64CF 0B SLDC. PUSH #000B
64D0 BE LDB. PUSH #00, PUSH (TOS-1)^.TOS BYTE
64D1 BF STB. (TOS-2)^.(TOS-1) := TOS BYTE
64D2 C6 86 6C LLA. PUSH #MP.066C
64D5 0B SLDC. PUSH #000B
64D6 00 SLDC. PUSH #0000
64D7 BF STB. (TOS-2)^.(TOS-1) := TOS BYTE
64D8 C6 86 6C LLA. PUSH #MP.066C
64DB 0C SLDC. PUSH #000C
64DC C6 86 6C LLA. PUSH #MP.066C
64DF 0C SLDC. PUSH #000C
64E0 BE LDB. PUSH #00, PUSH (TOS-1)^.TOS BYTE
64E1 B6 01 82 04 LOD. PUSH ACTREC(-01).0204
64E5 82 ADI. PUSH ((TOS) + (TOS-1))
64E6 C7 00 01 LDCI. PUSH #0100
64E9 8E MODI. PUSH ((TOS-1) MOD (TOS))
64EA BF STB. (TOS-2)^.(TOS-1) := TOS BYTE
64EB 04 SLDC. PUSH #0004
64EC C6 86 6C LLA. PUSH #MP.066C
64EF 00 SLDC. PUSH #0000
64F0 C7 00 02 LDCI. PUSH #0200
64F3 05 SLDC. PUSH #0005
64F4 00 SLDC. PUSH #0000
64F5 9E 06 WRITE VOL#
64F7 9E 22 PUSH I/O ERROR STATUS
64F9 00 SLDC. PUSH #0000
64FA CB NEQI. PUSH ((TOS-1) <> (TOS))
64FB A1 02 UJP. IF NOT (TOS) THEN JUMP TO 64FF
64FD CE 1E CLP. CALL CHILD PROCEDURE: 1E
64FF 04 SLDC. PUSH #0004
6500 D7 NOP. NOP
6501 A6 0D 53 43 45 4E 41 52 49 4F 2E 44 41 54 41
SCENARIO.DATA
LSA. PUSH #(PC+1) POINTER TO THE STRING
6510 00 SLDC. PUSH #0000
6511 00 SLDC. PUSH #0000
6512 AE 05 CIP. CALL INTERMEDIATE PROCEDURE: 05
6514 B8 01 82 03 STR. ACTREC(-01).0203 := TOS
6518 B6 01 82 03 LOD. PUSH ACTREC(-01).0203
651C 00 SLDC. PUSH #0000
651D C9 LESI. PUSH ((TOS-1) < (TOS))
651E A1 02 UJP. IF NOT (TOS) THEN JUMP TO 6522
6520 CE 1F CLP. CALL CHILD PROCEDURE: 1F
6522 AE 08 CIP. CALL INTERMEDIATE PROCEDURE: 08
6524 07 SLDC. PUSH #0007
6525 CC 18 STL. MP.18 := (TOS)
6527 00 SLDC. PUSH #0000
6528 CC 03 STL. MP.03 := (TOS)
652A B2 01 82 23 LDA. PUSH #ACTREC(-01).0223
652E 05 SLDC. PUSH #0005
652F A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
6531 F8 SIND. PUSH (TOS)^.0
6532 CC 87 6C STL. MP.076C := (TOS)
6535 DA SLDL. PUSH MP.03
6536 CA 87 6C LDL. PUSH MP.076C
6539 C8 LEQI. PUSH ((TOS-1) <= (TOS))
653A A1 0C UJP. IF NOT (TOS) THEN JUMP TO 6548
653C C6 04 LLA. PUSH #MP.04
653E DA SLDL. PUSH MP.03
653F AE 0C CIP. CALL INTERMEDIATE PROCEDURE: 0C
6541 DA SLDL. PUSH MP.03
6542 01 SLDC. PUSH #0001
6543 82 ADI. PUSH ((TOS) + (TOS-1))
6544 CC 03 STL. MP.03 := (TOS)
6546 B9 F4 UJP. JUMP TO 6535
6548 AE 0D CIP. CALL INTERMEDIATE PROCEDURE: 0D
654A D7 NOP. NOP
654B A6 04 4D 41 44 45
MADE
LSA. PUSH #(PC+1) POINTER TO THE STRING
6551 AE 0F CIP. CALL INTERMEDIATE PROCEDURE: 0F
6553 AE 0E CIP. CALL INTERMEDIATE PROCEDURE: 0E
6555 AD 00 RNP. RETURN FROM NON-BASE PROCEDURE.
6557 00 SLDC. PUSH #0000
6558 23 SLDC. PUSH #0023
6559 00 SLDC. PUSH #0000
655A FA SIND. PUSH (TOS)^.2
655B 00 SLDC. PUSH #0000



P07011C final rough draft:

843 7 28:D 1 PROCEDURE P07011C; (* P07011C *)
844 7 28:D 1
845 7 28:D 1 VAR
846 7 28:D 1 MP01 : INTEGER;
847 7 28:D 2 MP02 : INTEGER;
848 7 28:D 3 MP03 : INTEGER;
849 7 28:D 4 MP04 : TCHAR;
850 7 28:D 108 (* MP6C : INTEGER; *)
851 7 28:D 108 TEMP : PACKED ARRAY [ 0..3071] OF CHAR;
852 7 28:D 1644 MP066C : PACKED ARRAY [ 0..511] OF CHAR;


867 7 28:0 0 BEGIN (* P07011C *)
868 7 28:1 0 IF RTNESC( 'MAKE NEW SCENARIO DISKETTE', '') THEN
869 7 28:2 38 EXIT( P07011C);
870 7 28:1 42 TITLESCR( 'MAKE NEW SCENARIO DISKETTE');
871 7 28:1 73 WRITELN( 'PLACE ORIGINAL MASTER SCENARIO SIDE IN');
872 7 28:1 127 WRITELN( 'DRIVE 2, AND A FORMATTED DISKETTE IN');
873 7 28:1 181 WRITELN( 'DRIVE 1.');
874 7 28:1 205 WRITELN;
875 7 28:1 211 WRITELN( 'REMEMBER : ORIGINAL=2, NEW=1');
876 7 28:1 255 PRESSRET;
877 7 28:1 257 TITLESCR( 'MAKING SCENARIO - DO NOT DISTURB');
878 7 28:1 294 SCNTOCBL := FINDFILE( DRIVE2, 'SCENARIO.DATA');
879 7 28:1 319 IF SCNTOCBL < 0 THEN
880 7 28:2 327 P07011F;
881 7 28:1 329 UNITREAD( DRIVE2, MP066C, SIZEOF( MP066C), SERIALBL, 0);
882 7 28:1 341 IF IORESULT <> 0 THEN
883 7 28:2 347 P07011E;
884 7 28:1 349 IF MP066C[ 10] <> CHR( 0) THEN
885 7 28:2 358 P07011D( 'NOT MASTER IN 2');
886 7 28:1 378 MP02 := ORD( MP066C[ 11]) + 1;
887 7 28:1 387 IF MP02 = 256 THEN
888 7 28:2 394 MP02 := 1;
889 7 28:1 397 MP066C[ 11] := CHR( MP02);
890 7 28:1 403 UNITWRITE( DRIVE2, MP066C, SIZEOF( MP066C), SERIALBL, 0);
891 7 28:1 415 IF IORESULT <> 0 THEN
892 7 28:2 421 P07011E;
893 7 28:1 423 FOR MP03 := 0 TO 34 DO
894 7 28:2 437 BEGIN
895 7 28:3 437 UNITREAD( DRIVE2, IOCACHE, SIZEOF( IOCACHE), 8 * MP03, 0);
896 7 28:3 451 IF IORESULT <> 0 THEN
897 7 28:4 457 P07011E;
898 7 28:3 459 UNITREAD( DRIVE2, TEMP[ 0], 3072, 8 * MP03 + 2, 0);
899 7 28:3 474 IF IORESULT <> 0 THEN
900 7 28:4 480 P07011E;
901 7 28:3 482 UNITWRITE( DRIVE1, IOCACHE, SIZEOF( IOCACHE), 8 * MP03, 0);
902 7 28:3 496 IF IORESULT <> 0 THEN
903 7 28:4 502 P07011E;
904 7 28:3 504 UNITWRITE( DRIVE1, TEMP[ 0], 3072, 8 * MP03 + 2, 0);
905 7 28:3 519 IF IORESULT <> 0 THEN
906 7 28:4 525 P07011E;
907 7 28:2 527 END;
908 7 28:2 534
909 7 28:1 534 MP066C[ 10] := MP066C[ 11];
910 7 28:1 544 MP066C[ 11] := CHR( 0);
911 7 28:1 550
912 7 28:1 550 MP066C[ 12] := CHR( (ORD( MP066C[ 12]) + BASE204) MOD 256 );
913 7 28:1 569
914 7 28:1 569 UNITWRITE( DRIVE1, MP066C, SIZEOF( MP066C), SERIALBL, 0);
915 7 28:1 581 IF IORESULT <> 0 THEN
916 7 28:2 587 P07011E;
917 7 28:1 589 SCNTOCBL := FINDFILE( DRIVE1, 'SCENARIO.DATA');
918 7 28:1 614 IF SCNTOCBL < 0 THEN
919 7 28:2 622 P07011F;
920 7 28:1 624 RDSCNTC2;
921 7 28:1 626 MP04.STATUS := LOST;
922 7 28:1 629 FOR MP03 := 0 TO SCNTOC.RECPERDK[ ZCHAR] DO
923 7 28:2 650 WRCHARAC( MP04, MP03);
924 7 28:1 662 WRICACHE;
925 7 28:1 664 TITLESCR( 'MADE');
926 7 28:1 673 PRESSRET
927 7 28:0 673 END;


P07011D

JTAB FOR SUBROUTINE 29:
DATA SIZE: 82
PARAM SIZE: 2
EXIT AT: 6267
ENTER AT: 622C
PROC NUMBER: 29
LEXICAL LEVEL: 3
622C C6 02 LLA. PUSH #MP.02
622E D8 SLDL. PUSH MP.01
622F AA 50 SAS. TOS=CHAR::(TOS-1)^ := TOS CHAR
TOS=PTR ::(TOS-1)^ := (TOS)^ 50 CHARACTERS MAX
6231 A6 15 45 52 52 4F 52 20 4D 41 4B 49 4E 47 20 53 43 45 4E 41 52 49 4F
ERROR MAKING SCENARIO
LSA. PUSH #(PC+1) POINTER TO THE STRING
6248 D7 NOP. NOP
6249 AE 0F CIP. CALL INTERMEDIATE PROCEDURE: 0F
624B B6 04 03 LOD. PUSH ACTREC(-04).03
624E C6 02 LLA. PUSH #MP.02
6250 14 SLDC. PUSH #0014
6251 C6 02 LLA. PUSH #MP.02
6253 00 SLDC. PUSH #0000
6254 BE LDB. PUSH #00, PUSH (TOS-1)^.TOS BYTE
6255 02 SLDC. PUSH #0002
6256 86 DVI. PUSH ((TOS-1) / (TOS))
6257 82 ADI. PUSH ((TOS) + (TOS-1))
6258 CD 00 13 CXP. CALL EXTERNAL PROCEDURE: 13 IN SEGMENT: 00
625B B6 04 03 LOD. PUSH ACTREC(-04).03
625E CD 00 16 CXP. CALL EXTERNAL PROCEDURE: 16 IN SEGMENT: 00
6261 AE 0E CIP. CALL INTERMEDIATE PROCEDURE: 0E
6263 07 SLDC. PUSH #0007
6264 1C SLDC. PUSH #001C
6265 9E 04 EXIT. EXIT FROM PROCEDURE. (TOS)=PROC# (TOS-1)=SEG#
6267 AD 00 RNP. RETURN FROM NON-BASE PROCEDURE.
6269 00 SLDC. PUSH #0000


Note the #MP.02 compiler generated variable to hold the PASS BY VALUE of a STRING variable.

PROCEDURE F is: TITLESCR

Notice how LENGTH() function produces pcode.

Use MP01 even though pcode says #MP.02.

Final rough draft for P07011D

855 7 29:D 1 PROCEDURE P07011D( MP01 : STRING); (* P07011D *)
856 7 29:D 43
857 7 29:0 0 BEGIN (* P07011D *)
858 7 29:1 0 TITLESCR( 'ERROR MAKING SCENARIO');
859 7 29:1 31 WRITELN( MP01: (20 + LENGTH( MP01) DIV 2) );
860 7 29:1 53 PRESSRET;
861 7 29:1 55 EXIT( P07011C)
862 7 29:0 59 END;


P07011E
FILE: 7 SEG: 1 PROC: 30

JTAB FOR SUBROUTINE 30:
DATA SIZE: 0
PARAM SIZE: 0
EXIT AT: 6283
ENTER AT: 6274
PROC NUMBER: 30
LEXICAL LEVEL: 3
6274 D7 NOP. NOP
6275 A6 0A 44 49 53 4B 20 45 52 52 4F 52
DISK ERROR
LSA. PUSH #(PC+1) POINTER TO THE STRING
6281 AE 1D CIP. CALL INTERMEDIATE PROCEDURE: 1D
6283 AD 00 RNP. RETURN FROM NON-BASE PROCEDURE.
6285 00 SLDC. PUSH #0000


Final rough draft P07011E

865 7 30:D 1 PROCEDURE P07011E; (* P07011E *)
866 7 30:D 1
867 7 30:0 0 BEGIN
868 7 30:1 0 P07011D( 'DISK ERROR')
869 7 30:0 13 END;


P07011F
FILE: 7 SEG: 1 PROC: 31

JTAB FOR SUBROUTINE 31:
DATA SIZE: 0
PARAM SIZE: 0
EXIT AT: 62A6
ENTER AT: 6290
PROC NUMBER: 31
LEXICAL LEVEL: 3
6290 D7 NOP. NOP
6291 A6 11 4E 4F 54 20 53 43 45 4E 41 52 49 4F 20 44 49 53 4B
NOT SCENARIO DISK
LSA. PUSH #(PC+1) POINTER TO THE STRING
62A4 AE 1D CIP. CALL INTERMEDIATE PROCEDURE: 1D
62A6 AD 00 RNP. RETURN FROM NON-BASE PROCEDURE.


Final rough draft P07011F

872 7 31:D 1 PROCEDURE P07011F; (* P07011F *)
873 7 31:D 1
874 7 31:0 0 BEGIN
875 7 31:1 0 P07011D( 'NOT SCENARIO DISK')
876 7 31:0 20 END;


P070120 Data Size Paramaters
P070120 "STEP 2" 7798 0
P070121 "ERROR DURING UPDATE" 0 0
P070122 UPDATING 2 0
P070123 read/write 0 2
P070124 UPDATE DISK 0 0


JTAB FOR SUBROUTINE 32:
DATA SIZE: 7798
PARAM SIZE: 0
EXIT AT: 6A1F
ENTER AT: 6806
PROC NUMBER: 32
LEXICAL LEVEL: 2


Tons of DATA in P070120

Here is a table I constructed after looking briefly through the code:

P070001 P070120 P070120child
(-3).01
(-1).0203 SCNTOCBL
(-1).0204 BASE204

MP.01
MP.02 (-1).02
MP.03 array of size #$68 at this addr
$0820 = 2080
MP.0823
MP.083B
MP.093B (-1).093B (-2).093B 1536
MP.0F3B Looks like FOR LOOP control variable.

First rough draft after coding the first few instructions in P070120:

941 7 32:D 1 PROCEDURE P070120;
942 7 32:D 1
943 7 32:D 1 VAR
944 7 32:D 1 MP01 : INTEGER;
945 7 32:D 2 MP02 : INTEGER;
946 7 32:D 3 MP03 : ARRAY [ 0..2079] OF INTEGER;
947 7 32:D 2083 MP0823 : STRING[ 46];
948 7 32:D 2107 MP083B : PACKED ARRAY [ 0..511] OF CHAR;
949 7 32:D 2363 MP093B : PACKED ARRAY [ 0..1535] OF CHAR;

Procedure F is "TITLESCR".
Procedure E is "PRESSRET"
Procedure 5 is "FINDFILE"
Procedure 8 is "RDSCNTC2"
Procedure B is "RDCHARAC"
Procedure D is "WRICACHE"

#$04E2 = 1250

MP03 looks like an array of Wizardry Characters (TCHAR record) since we are going through a loop from 0 to 19 and calling WRCHARAC. Note, #$68 is 104, and 20 x 104 = 2080.

First rough draft complete for P070120

941 7 32:D 1 PROCEDURE P070120;
942 7 32:D 1
943 7 32:D 1 VAR
944 7 32:D 1 MP01 : INTEGER;
945 7 32:D 2 MP02 : INTEGER;
946 7 32:D 3 MP03 : ARRAY [ 0..19] OF TCHAR;
947 7 32:D 2083 MP0823 : STRING[ 46];
948 7 32:D 2107 MP083B : PACKED ARRAY [ 0..511] OF CHAR;
949 7 32:D 2363 MP093B : PACKED ARRAY [ 0..3071] OF CHAR;


968 7 32:0 0 BEGIN (* P070120 *)
969 7 32:1 0 P070124;
970 7 32:1 2 P070122;
971 7 32:1 4 UNITREAD( DRIVE1, MP083B, SIZEOF( MP083B), SERIALBL, 0);
972 7 32:1 16 IF IORESULT <> 0 THEN
973 7 32:2 22 P070121;
974 7 32:1 24 MOVELEFT( MP083B, MP0823, 7);
975 7 32:1 35 MP083B[ 10] := CHR( 0);
976 7 32:1 41 MP083B[ 11] := CHR( 1);
977 7 32:1 47 MP083B[ 12] := CHR( BASE204);
978 7 32:1 56 UNITWRITE( DRIVE1, MP083B, SIZEOF( MP083B), SERIALBL, 0);
979 7 32:1 68 IF IORESULT <> 0 THEN
980 7 32:2 74 P070121;
981 7 32:1 76 REPEAT
982 7 32:2 76 TITLESCR( 'STEP 2 - UPDATE SCENARIO SIDE');
983 7 32:2 110 WRITELN( 'NOW FLIP BOTH DISKETTES SO WE CAN COPY');
984 7 32:2 164 WRITELN( 'SCENARIO SIDES.');
985 7 32:2 195 PRESSRET;
986 7 32:2 197 SCNTOCBL := FINDFILE( DRIVE1, 'SCENARIO.DATA')
987 7 32:1 214 UNTIL SCNTOCBL >= 0;
988 7 32:1 230 RDSCNTC2;
989 7 32:1 232 FOR MP02 := 0 TO 19 DO
990 7 32:2 246 BEGIN
991 7 32:3 246 RDCHARAC( MP03[ MP02], MP02)
992 7 32:2 252 END;
993 7 32:1 261 P070122;
994 7 32:1 263 SCNTOCBL := FINDFILE( DRIVE1, 'SCENARIO.DATA');
995 7 32:1 288 RDSCNTC2;
996 7 32:1 290 FOR MP02 := 0 TO 19 DO
997 7 32:2 304 BEGIN
998 7 32:3 304 IF MP03[ MP02].AGE > 1250 THEN
999 7 32:4 317 MP03[ MP02].AGE := 1250;
1000 7 32:3 328 MP03[ MP02].INMAZE := FALSE;
1001 7 32:3 337 WRCHARAC( MP03[ MP02], MP02);
1002 7 32:2 345 END;
1003 7 32:1 352 WRICACHE;
1004 7 32:1 354 UNITREAD( DRIVE1, MP083B, SIZEOF( MP083B), SERIALBL, 0);
1005 7 32:1 366 IF IORESULT <> 0 THEN
1006 7 32:2 372 P070121;
1007 7 32:1 374 MOVELEFT( MP083B, MP0823, 7);
1008 7 32:1 385 MP083B[ 10] := CHR( 0);
1009 7 32:1 391 MP083B[ 11] := CHR( 1);
1010 7 32:1 397 MP083B[ 12] := CHR( BASE204);
1011 7 32:1 406 UNITWRITE( DRIVE1, MP083B, SIZEOF( MP083B), SERIALBL, 0);
1012 7 32:1 418 IF IORESULT <> 0 THEN
1013 7 32:2 424 P070121;
1014 7 32:1 426 TITLESCR( 'DISK UPDATED');
1015 7 32:1 443 WRITELN( 'REMEMBER TO PUT A WRITE-PROTECT TAB ON');
1016 7 32:1 497 WRITELN( 'THE NOTCH YOU PUNCHED!');
1017 7 32:1 535 PRESSRET
1018 7 32:0 535 END;


P070121 to P070124 to do.
Lets start with P070124 which is called first by P070120.

Procedure 10 is RTNESC

Very straight forward -- just TITLESCR, WRITELN(), and WRITELN;, and a few procedure calls.

965 7 36:D 1 PROCEDURE P070124;
966 7 36:D 1
967 7 36:0 0 BEGIN
968 7 36:1 0 IF RTNESC( 'UPDATE DISK',
969 7 36:1 14 'WARNING - FOLLOW DIRECTIONS CAREFULLY') THEN
970 7 36:2 60 EXIT( P070120);
971 7 36:1 64 TITLESCR( 'STEP 1 - UPDATE BOOT SIDE');
972 7 36:1 94 WRITELN( 'PUNCH WRITE-PROTECT NOTCH ON BOOT SIDE');
973 7 36:1 148 WRITELN( 'OF OLD DISKETTE.');
974 7 36:1 180 WRITELN;
975 7 36:1 186 WRITELN( 'PLACE NEW BOOT SIDE IN DRIVE 2');
976 7 36:1 232 WRITELN( ' OLD BOOT SIDE IN DRIVE 1');
977 7 36:1 278 WRITELN;
978 7 36:1 284 WRITELN( 'BE SURE : NEW=2, OLD=1; COPY 2 -> 1!');
979 7 36:1 336 WRITELN;
980 7 36:1 342 PRESSRET
981 7 36:0 342 END;


Let's do P070121 next (very short)

657E B6 04 03 LOD. PUSH ACTREC(-04).03
6581 A6 0E 55 50 44 41 54 45 20 41 42 4F 52 54 45 44
UPDATE ABORTED
LSA. PUSH #(PC+1) POINTER TO THE STRING
6591 D7 NOP. NOP
6592 1B SLDC. PUSH #001B
6593 CD 00 13 CXP. CALL EXTERNAL PROCEDURE: 13 IN SEGMENT: 00


Hmmm, #001B, what is that?
The following almost works, but generates PUSH #0000

952 7 33:D 1 PROCEDURE P070121;
953 7 33:D 1
954 7 33:0 0 BEGIN
955 7 33:1 0 TITLESCR( 'ERROR DURING UPDATE');
956 7 33:1 24 WRITELN( 'UPDATE ABORTED');
957 7 33:1 54 PRESSRET;
958 7 33:1 56 EXIT( P070120)
959 7 33:0 60 END;

Maybe use WRITELN( 'UPDATE ABORTED' : 27); ???????
Yes, that works.


P070122

970 7 34:0 0 BEGIN (* P070122 *)
971 7 34:1 0 TITLESCR( 'UPDATING');
972 7 34:1 13 UNITREAD( DRIVE2, MP093B, 5 * 512, 0, 0);
973 7 34:1 28 IF IORESULT <> 0 THEN
974 7 34:2 34 P070121;
975 7 34:1 36 UNITWRITE( DRIVE1, MP093B, 5 * 512, 0, 0);
976 7 34:1 51 IF IORESULT <> 0 THEN
977 7 34:2 57 P070121;
978 7 34:1 59 UNITREAD( DRIVE2, MP093B, 1024, 6, 0);
979 7 34:1 72 IF IORESULT <> 0 THEN
980 7 34:2 78 P070121;
981 7 34:1 80 UNITWRITE( DRIVE1, MP093B, 1024, 6, 0);
982 7 34:1 93 IF IORESULT <> 0 THEN
983 7 34:2 99 P070121;
984 7 34:1 101 FOR MP02 := 1 TO 34 DO
985 7 34:2 115 P070123( 8 * MP02)
986 7 34:0 120 END;

Found that I needed to remove MP01 from P070122. The variable is created by the FOR loop.

Last procedure for Startup is P070123

Very straight forward re-engineering:

965 7 35:D 1 PROCEDURE P070123( MP01 : INTEGER);
966 7 35:D 2
967 7 35:0 0 BEGIN
968 7 35:1 0 UNITREAD( DRIVE2, IOCACHE, SIZEOF( IOCACHE), MP01, 0);
969 7 35:1 12 IF IORESULT <> 0 THEN
970 7 35:2 18 P070121;
971 7 35:1 20 UNITREAD( DRIVE2, MP093B, SIZEOF( MP093B), MP01 + 2, 0);
972 7 35:1 35 IF IORESULT <> 0 THEN
973 7 35:2 41 P070121;
974 7 35:1 43 UNITWRITE( DRIVE1, IOCACHE, SIZEOF( IOCACHE), MP01, 0);
975 7 35:1 55 IF IORESULT <> 0 THEN
976 7 35:2 61 P070121;
977 7 35:1 63 UNITWRITE( DRIVE1, MP093B, SIZEOF( MP093B), MP01 + 2, 0);
978 7 35:1 78 IF IORESULT <> 0 THEN
979 7 35:2 84 P070121
980 7 35:0 84 END;


I think all of STARTUP.CODE (SYSTEM.STARTUP) is completed for the first rough draft.

To cleanly compile everything, first remove stuff from #4:SYSTEM.LIBRARY
DRAWLINE
PRPICCH
DRAWSCR
SCRNDATA
CHKKEYBD
RANDOM


Use LIBRARY.CODE and LIBMAP to remove those entries in SYSTEM.LIBRARY from long ago. Use LIBMAP to display contents of new SYSTEM.LIBRARY.

Copy SYSTEM.LIBRARY to all diskettes used while building STARTUP.CODE.
Remove all previously compiled *.CODE from diskettes.

WIZ1D: on #5
WIZ1E: on #12

Assemble P070002, p070003, p070005

Assemble what text?
#5:p070002
To what codefile?
#5:p070002
Output file for assembled listing:

Assemble what text?
#5:p070003
Output file for assembled listing:

Assemble what text?
#5:p070005
To what codefile?
#5:p070005
Output file for assembled listing:

Compiling...
Compile what text?
#12:STARTUP
To what codefile?
$

Linking...
Host file?
#12:STARTUP
Lib file?
#4:SYSTEM.LIBRARY
Lib file?
#5:P070002
Lib file?
#5:p070003
Lib file?
#5:P070005
Lib file?
Map file?

Reading WIZBOOT
Reading P070002
Reading P070003
Reading P070005

Output file?
#12:startup

Linking UTILS #7
Linking TITLELOA #8
Linking OPTIONS #9
Linking WIZBOOT #1
Copying proc P070002
Copying proc P070003
Copying proc P070005

#12:STARTUP.CODE should now match Wiz1 Boot diskette SYSTEM.STARTUP.

Move #12:STARTUP.CODE to a BLANK: diskette by itself.
Move Wiz1 SYSTEM.STARTUP to a BLANK: diskette by itself.
Run DeCompiler and answer "Y" for "PROCESS ALL?"
Save output for both.
Compare both using WinDiff.exe

They match.

Also compare the assembled code to make sure it matches.

It does.

All of STARTUP.CODE is completed.

So far we have completed 50 Procedures and Functions. There are 424 more to go.

Did I say this was going to be a long journey?

Next up is Wizardry.Code.

Tommy

ric...@trouty.com

unread,
Apr 29, 2014, 6:58:57 PM4/29/14
to
Just wanted to say that I applaud these efforts to RE Wizardry. Wizardry was an absolutely huge impression when growing up, and blew my friends and I away with what it could do, and the adventure challenge it posed.

I started to program in UCSD Pascal (as an exercise while my classmates were learning BASIC - always meant I could beat any algorithm written in BASIC by a factor of 10).

TommyGoog

unread,
May 4, 2014, 9:26:53 PM5/4/14
to
On Wednesday, April 23, 2014 9:12:23 PM UTC-5, TommyGoog wrote:
>
>
> With this posting I complete the decompiling for STARTUP.CODE.
>
> All of STARTUP.CODE is completed.

>Next up is Wizardry.Code.

>Tommy

It's been awhile since I've posted an update on my progress. Lately I've been concentrating my efforts on re-engineering the code rather than trying to decide what to post to these forums. It takes a bit of effort for me to write up what I've done and right now I'd rather just keep going with the re-engineering effort.

Previously I completed the rough draft of all the Pascal code for SYSTEM.STARTUP.

Now I am working on the Pascal code for WIZARDRY.CODE.

When I say "rough draft" I mean code that compiles into the exact WIZARDRY.CODE pcode but perhaps has generic variable names, etc.

Let's compare the number of PROCEDURES and FUNCTIONS in Wizardry I (Proving Grounds) and Wizardry III (Legacy of Llylgamyn).

WIZARDRY.CODE

Wiz1 LOL
0 WIZARDRY 0 37 48
1 UTILITIE 7 39 42
2 SHOPS 8 30 34
3 SPECIALS 9 42 49
4 COMBAT A 1 3
5 CINIT B 9 19
6 CUTIL C 40 31
7 MELEE D 1 1
8 CASTASP E 29 31
9 SWINGASW F 19 19
10 CASTLE 10 38 37
11 ROLLER 11 28 23
12 CAMP 12 34 31
13 REWARDS 13 39 49
14 RUNNER 14 38 70
--- ---
424 487

To date I have completed the rough draft of Pascal code for SEGMENT 0 (WIZARDRY), SEGMENT 1 (UTILITIE), and SEGMENT 2 (SHOPS).

SEGMENT 0 includes the LOL mainline + global routines and
including assembler routines.

I'm currently working on SEGMENT 3 (SPECIALS).

Although I've made good progress on Wizardry III re-engineering, I was sidetracked a bit in the past 2 weeks while viewing the Vugar Gashimov Memorial Chess tournament. I'm a bit of a Carlsen fan.

At some point in the future I might come back to describing some of the challenges I had while converting this code. Right now I'd like to just keep going. It will still take another two months or so to complete the rough Pascal code.

I tried posting my current Pascal output listing in this message, but received an error. I assume I was trying to post too many lines of text (approximately 5,000 lines). If anyone is interested in these not-yet-completed listings, let me know.

Enjoy,
Tommy

TommyGoog

unread,
May 12, 2014, 4:06:50 AM5/12/14
to
On Sunday, May 4, 2014 8:26:53 PM UTC-5, TommyGoog wrote:

> Tommy

If you'd like a challenge I've included the pcode for routine P010602 later in this post. Your task is to try to determine the major Pascal constructs in this code (IF-THEN, IF-THEN-ELSE, CASE, WHILE loops, FOR loops, REPEAT-UNTIL loops, etc.). Do not use GOTO statements.

If you post your "solution" to the P010602 "problem" I will respond by telling you if you are correct or not. If you are not correct, you can try again. Eventually I will post the Pascal code that I have developed for this routine. Note, bonus points are awarded if you can do it without printing the routine to paper.

It might be nice to format your solution like the following:

WHILE DO
BEGIN
IF THEN
BEGIN
<lots of code>
END
ELSE
BEGIN
CASE
END; (* CASE *)
<lots of code>
END
END; (* WHILE *)

If you need help, this routine is the same as CACTION in Wizardry III (LOL), but there are a lot of differences to P010602 between the two versions.

The reason I am singling out this routine is that it was painful (even for me) to re-engineer. Good Luck and I hope I get a few responses.

To date I have now completed rough drafts for segments WIZARDRY, UTILITIE, SHOPS, SPECIALS, COMBAT (1 procedure), and CINIT. I'm currently working on CUTIL routines (In addition to having a rough draft of SYSTEM.STARTUP.)

P010602

574C CE 13 CLP. CALL CHILD PROCEDURE: 13
574E 00 SLDC. PUSH #0000
574F CC 08 STL. MP.08 := (TOS)
5751 C6 01 LLA. PUSH #MP.01
5753 00 SLDC. PUSH #0000
5754 0C SLDC. PUSH #000C
5755 00 SLDC. PUSH #0000
5756 9E 0A FLC. FILLCHAR (DST+IDX,LEN,CHR)
5758 DF SLDL. PUSH MP.08
5759 EA SLDO. PUSH BASE.03
575A C9 LESI. PUSH ((TOS-1) < (TOS))
575B A1 E6 UJP. IF NOT (TOS) THEN JUMP TO 5A88
575D B2 02 10 LDA. PUSH #ACTREC(-02).10
5760 00 SLDC. PUSH #0000
5761 A4 80 92 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=92
5764 A2 04 INC. PUSH #(TOS)^.(04)
5766 DF SLDL. PUSH MP.08
5767 A4 07 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=07
5769 FE SIND. PUSH (TOS)^.6
576A 00 SLDC. PUSH #0000
576B C3 EQUI. PUSH ((TOS-1) = (TOS))
576C B6 02 02 LOD. PUSH ACTREC(-02).02
576F 02 SLDC. PUSH #0002
5770 CB NEQI. PUSH ((TOS-1) <> (TOS))
5771 84 LAND. PUSH ((TOS-1) AND (TOS))
5772 A1 EE UJP. IF NOT (TOS) THEN JUMP TO 59EF
5774 B2 02 10 LDA. PUSH #ACTREC(-02).10
5777 00 SLDC. PUSH #0000
5778 A4 80 92 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=92
577B A2 04 INC. PUSH #(TOS)^.(04)
577D DF SLDL. PUSH MP.08
577E A4 07 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=07
5780 A2 01 INC. PUSH #(TOS)^.(01)
5782 C7 E7 03 LDCI. PUSH #03E7
5785 91 NGI. PUSH -(TOS) (2'S COMP)
5786 9A STO. (TOS-1)^ := (TOS)
5787 00 SLDC. PUSH #0000
5788 00 SLDC. PUSH #0000
5789 CD 01 1C CXP. CALL EXTERNAL PROCEDURE: 1C IN SEGMENT: 01
578C 0A SLDC. PUSH #000A
578D 8E MODI. PUSH ((TOS-1) MOD (TOS))
578E CC 09 STL. MP.09 := (TOS)
5790 A5 4A LA0. PUSH #BASE.4A
5792 DF SLDL. PUSH MP.08
5793 A4 68 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=68
5795 A2 16 INC. PUSH #(TOS)^.(16)
5797 04 SLDC. PUSH #0004
5798 C0 03 05 IXP. INDEX PACKED ARRAY. USE TOS & TOS-1, UB1, UB2. ???
579B BA LDP. PUSH FIELD. TOS: RTBIT;#;^
579C B9 31 UJP. JUMP TO 57CF
579E E0 SLDL. PUSH MP.09
579F 03 SLDC. PUSH #0003
57A0 82 ADI. PUSH ((TOS) + (TOS-1))
57A1 CC 09 STL. MP.09 := (TOS)
57A3 B9 51 UJP. JUMP TO 57F6
57A5 E0 SLDL. PUSH MP.09
57A6 02 SLDC. PUSH #0002
57A7 82 ADI. PUSH ((TOS) + (TOS-1))
57A8 CC 09 STL. MP.09 := (TOS)
57AA B9 4A UJP. JUMP TO 57F6
57AC E0 SLDL. PUSH MP.09
57AD 01 SLDC. PUSH #0001
57AE 82 ADI. PUSH ((TOS) + (TOS-1))
57AF CC 09 STL. MP.09 := (TOS)
57B1 B9 43 UJP. JUMP TO 57F6
57B3 E0 SLDL. PUSH MP.09
57B4 01 SLDC. PUSH #0001
57B5 95 SBI. PUSH ((TOS-1) - (TOS))
57B6 CC 09 STL. MP.09 := (TOS)
57B8 B9 3C UJP. JUMP TO 57F6
57BA E0 SLDL. PUSH MP.09
57BB 02 SLDC. PUSH #0002
57BC 95 SBI. PUSH ((TOS-1) - (TOS))
57BD CC 09 STL. MP.09 := (TOS)
57BF B9 35 UJP. JUMP TO 57F6
57C1 E0 SLDL. PUSH MP.09
57C2 03 SLDC. PUSH #0003
57C3 95 SBI. PUSH ((TOS-1) - (TOS))
57C4 CC 09 STL. MP.09 := (TOS)
57C6 B9 2E UJP. JUMP TO 57F6
57C8 E0 SLDL. PUSH MP.09
57C9 04 SLDC. PUSH #0004
57CA 95 SBI. PUSH ((TOS-1) - (TOS))
57CB CC 09 STL. MP.09 := (TOS)
57CD B9 27 UJP. JUMP TO 57F6
57CF AC 03 00 12 00 B9 20 TABLE: 38 00 33 00 35 00 30 00 32 00 0C 00 0E 00 10 00 12 00 14 00 16 00 18 00 3B 00 36 00 31 00 2C 00
XJP. W1,W2,W3,<TABLE>
57F6 E0 SLDL. PUSH MP.09
57F7 01 SLDC. PUSH #0001
57F8 C9 LESI. PUSH ((TOS-1) < (TOS))
57F9 A1 05 UJP. IF NOT (TOS) THEN JUMP TO 5800
57FB 01 SLDC. PUSH #0001
57FC CC 09 STL. MP.09 := (TOS)
57FE B9 08 UJP. JUMP TO 5808
5800 E0 SLDL. PUSH MP.09
5801 0A SLDC. PUSH #000A
5802 C5 GRTI. PUSH ((TOS-1) > (TOS))
5803 A1 03 UJP. IF NOT (TOS) THEN JUMP TO 5808
5805 0A SLDC. PUSH #000A
5806 CC 09 STL. MP.09 := (TOS)
5808 B2 02 10 LDA. PUSH #ACTREC(-02).10
580B 00 SLDC. PUSH #0000
580C A4 80 92 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=92
580F A2 04 INC. PUSH #(TOS)^.(04)
5811 DF SLDL. PUSH MP.08
5812 A4 07 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=07
5814 A2 02 INC. PUSH #(TOS)^.(02)
5816 E0 SLDL. PUSH MP.09
5817 9A STO. (TOS-1)^ := (TOS)
5818 01 SLDC. PUSH #0001
5819 9E 26 CHECK VOL#
581B 0D SLDC. PUSH #000D
581C 06 SLDC. PUSH #0006
581D CD 01 1F CXP. CALL EXTERNAL PROCEDURE: 1F IN SEGMENT: 01
5820 A5 4A LA0. PUSH #BASE.4A
5822 DF SLDL. PUSH MP.08
5823 A4 68 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=68
5825 CD 01 17 CXP. CALL EXTERNAL PROCEDURE: 17 IN SEGMENT: 01
5828 D7 NOP. NOP
5829 A6 0A 27 53 20 4F 50 54 49 4F 4E 53
'S OPTIONS
LSA. PUSH #(PC+1) POINTER TO THE STRING
5835 CD 01 17 CXP. CALL EXTERNAL PROCEDURE: 17 IN SEGMENT: 01
5838 0D SLDC. PUSH #000D
5839 08 SLDC. PUSH #0008
583A CD 01 1F CXP. CALL EXTERNAL PROCEDURE: 1F IN SEGMENT: 01
583D DF SLDL. PUSH MP.08
583E 03 SLDC. PUSH #0003
583F C9 LESI. PUSH ((TOS-1) < (TOS))
5840 A1 0E UJP. IF NOT (TOS) THEN JUMP TO 5850
5842 D7 NOP. NOP
5843 A6 08 46 29 49 47 48 54 20 20
F)IGHT
LSA. PUSH #(PC+1) POINTER TO THE STRING
584D CD 01 17 CXP. CALL EXTERNAL PROCEDURE: 17 IN SEGMENT: 01
5850 D7 NOP. NOP
5851 A6 0E 53 29 50 45 4C 4C 20 20 50 29 41 52 52 59
S)PELL P)ARRY
LSA. PUSH #(PC+1) POINTER TO THE STRING
5861 CD 01 17 CXP. CALL EXTERNAL PROCEDURE: 17 IN SEGMENT: 01
5864 0D SLDC. PUSH #000D
5865 09 SLDC. PUSH #0009
5866 CD 01 1F CXP. CALL EXTERNAL PROCEDURE: 1F IN SEGMENT: 01
5869 A6 10 52 29 55 4E 20 20 20 20 55 29 53 45 20 20 20 20
R)UN U)SE
LSA. PUSH #(PC+1) POINTER TO THE STRING
587B D7 NOP. NOP
587C CD 01 17 CXP. CALL EXTERNAL PROCEDURE: 17 IN SEGMENT: 01
587F 00 SLDC. PUSH #0000
5880 CC 07 STL. MP.07 := (TOS)
5882 A5 4A LA0. PUSH #BASE.4A
5884 DF SLDL. PUSH MP.08
5885 A4 68 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=68
5887 A3 12 IND. PUSH (TOS)^.12
5889 02 SLDC. PUSH #0002
588A C3 EQUI. PUSH ((TOS-1) = (TOS))
588B A5 4A LA0. PUSH #BASE.4A
588D DF SLDL. PUSH MP.08
588E A4 68 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=68
5890 A3 12 IND. PUSH (TOS)^.12
5892 06 SLDC. PUSH #0006
5893 C3 EQUI. PUSH ((TOS-1) = (TOS))
5894 A5 4A LA0. PUSH #BASE.4A
5896 DF SLDL. PUSH MP.08
5897 A4 68 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=68
5899 A3 42 IND. PUSH (TOS)^.42
589B 08 SLDC. PUSH #0008
589C C5 GRTI. PUSH ((TOS-1) > (TOS))
589D 84 LAND. PUSH ((TOS-1) AND (TOS))
589E 8D LOR. PUSH ((TOS-1) OR (TOS))
589F A5 4A LA0. PUSH #BASE.4A
58A1 DF SLDL. PUSH MP.08
58A2 A4 68 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=68
58A4 A3 12 IND. PUSH (TOS)^.12
58A6 04 SLDC. PUSH #0004
58A7 C3 EQUI. PUSH ((TOS-1) = (TOS))
58A8 A5 4A LA0. PUSH #BASE.4A
58AA DF SLDL. PUSH MP.08
58AB A4 68 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=68
58AD A3 42 IND. PUSH (TOS)^.42
58AF 03 SLDC. PUSH #0003
58B0 C5 GRTI. PUSH ((TOS-1) > (TOS))
58B1 84 LAND. PUSH ((TOS-1) AND (TOS))
58B2 8D LOR. PUSH ((TOS-1) OR (TOS))
58B3 A1 12 UJP. IF NOT (TOS) THEN JUMP TO 58C7
58B5 01 SLDC. PUSH #0001
58B6 CC 07 STL. MP.07 := (TOS)
58B8 D7 NOP. NOP
58B9 A6 09 44 29 49 53 50 45 4C 4C 20
D)ISPELL
LSA. PUSH #(PC+1) POINTER TO THE STRING
58C4 CD 01 17 CXP. CALL EXTERNAL PROCEDURE: 17 IN SEGMENT: 01
58C7 CD 01 0D CXP. CALL EXTERNAL PROCEDURE: 0D IN SEGMENT: 01
58CA F0 SLDO. PUSH BASE.09
58CB 46 SLDC. PUSH #0046
58CC C3 EQUI. PUSH ((TOS-1) = (TOS))
58CD F0 SLDO. PUSH BASE.09
58CE 53 SLDC. PUSH #0053
58CF C3 EQUI. PUSH ((TOS-1) = (TOS))
58D0 8D LOR. PUSH ((TOS-1) OR (TOS))
58D1 F0 SLDO. PUSH BASE.09
58D2 50 SLDC. PUSH #0050
58D3 C3 EQUI. PUSH ((TOS-1) = (TOS))
58D4 8D LOR. PUSH ((TOS-1) OR (TOS))
58D5 F0 SLDO. PUSH BASE.09
58D6 55 SLDC. PUSH #0055
58D7 C3 EQUI. PUSH ((TOS-1) = (TOS))
58D8 8D LOR. PUSH ((TOS-1) OR (TOS))
58D9 F0 SLDO. PUSH BASE.09
58DA 44 SLDC. PUSH #0044
58DB C3 EQUI. PUSH ((TOS-1) = (TOS))
58DC 8D LOR. PUSH ((TOS-1) OR (TOS))
58DD F0 SLDO. PUSH BASE.09
58DE 52 SLDC. PUSH #0052
58DF C3 EQUI. PUSH ((TOS-1) = (TOS))
58E0 8D LOR. PUSH ((TOS-1) OR (TOS))
58E1 F0 SLDO. PUSH BASE.09
58E2 42 SLDC. PUSH #0042
58E3 C3 EQUI. PUSH ((TOS-1) = (TOS))
58E4 8D LOR. PUSH ((TOS-1) OR (TOS))
58E5 A1 F6 UJP. IF NOT (TOS) THEN JUMP TO 58C7
58E7 0D SLDC. PUSH #000D
58E8 08 SLDC. PUSH #0008
58E9 1A SLDC. PUSH #001A
58EA 02 SLDC. PUSH #0002
58EB CD 01 1E CXP. CALL EXTERNAL PROCEDURE: 1E IN SEGMENT: 01
58EE C6 01 LLA. PUSH #MP.01
58F0 DF SLDL. PUSH MP.08
58F1 A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
58F3 00 SLDC. PUSH #0000
58F4 9A STO. (TOS-1)^ := (TOS)
58F5 F0 SLDO. PUSH BASE.09
58F6 B9 F4 UJP. JUMP TO 598C
58F8 DE SLDL. PUSH MP.07
58F9 A1 1D UJP. IF NOT (TOS) THEN JUMP TO 5918
58FB A6 16 44 49 53 50 45 4C 4C 20 57 48 49 43 48 20 47 52 4F 55 50 23 20 3F
DISPELL WHICH GROUP# ?
LSA. PUSH #(PC+1) POINTER TO THE STRING
5913 D7 NOP. NOP
5914 05 SLDC. PUSH #0005
5915 91 NGI. PUSH -(TOS) (2'S COMP)
5916 CE 03 CLP. CALL CHILD PROCEDURE: 03
5918 B9 F2 UJP. JUMP TO 59BC
591A CE 11 CLP. CALL CHILD PROCEDURE: 11
591C B9 F2 UJP. JUMP TO 59BC
591E DF SLDL. PUSH MP.08
591F 03 SLDC. PUSH #0003
5920 C9 LESI. PUSH ((TOS-1) < (TOS))
5921 A1 1D UJP. IF NOT (TOS) THEN JUMP TO 5940
5923 A6 16 46 49 47 48 54 20 41 47 41 49 4E 53 54 20 47 52 4F 55 50 23 20 3F
FIGHT AGAINST GROUP# ?
LSA. PUSH #(PC+1) POINTER TO THE STRING
593B D7 NOP. NOP
593C 01 SLDC. PUSH #0001
593D 91 NGI. PUSH -(TOS) (2'S COMP)
593E CE 03 CLP. CALL CHILD PROCEDURE: 03
5940 B9 7A UJP. JUMP TO 59BC
5942 B2 02 10 LDA. PUSH #ACTREC(-02).10
5945 00 SLDC. PUSH #0000
5946 A4 80 92 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=92
5949 A2 04 INC. PUSH #(TOS)^.(04)
594B DF SLDL. PUSH MP.08
594C A4 07 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=07
594E A2 01 INC. PUSH #(TOS)^.(01)
5950 00 SLDC. PUSH #0000
5951 9A STO. (TOS-1)^ := (TOS)
5952 B2 02 10 LDA. PUSH #ACTREC(-02).10
5955 00 SLDC. PUSH #0000
5956 A4 80 92 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=92
5959 A2 04 INC. PUSH #(TOS)^.(04)
595B DF SLDL. PUSH MP.08
595C A4 07 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=07
595E A2 02 INC. PUSH #(TOS)^.(02)
5960 01 SLDC. PUSH #0001
5961 91 NGI. PUSH -(TOS) (2'S COMP)
5962 9A STO. (TOS-1)^ := (TOS)
5963 B9 57 UJP. JUMP TO 59BC
5965 CE 0B CLP. CALL CHILD PROCEDURE: 0B
5967 B9 53 UJP. JUMP TO 59BC
5969 CE 04 CLP. CALL CHILD PROCEDURE: 04
596B 01 SLDC. PUSH #0001
596C 0B SLDC. PUSH #000B
596D 26 SLDC. PUSH #0026
596E 04 SLDC. PUSH #0004
596F CD 01 1E CXP. CALL EXTERNAL PROCEDURE: 1E IN SEGMENT: 01
5972 B9 48 UJP. JUMP TO 59BC
5974 DF SLDL. PUSH MP.08
5975 00 SLDC. PUSH #0000
5976 C5 GRTI. PUSH ((TOS-1) > (TOS))
5977 A1 11 UJP. IF NOT (TOS) THEN JUMP TO 598A
5979 B2 02 10 LDA. PUSH #ACTREC(-02).10
597C 00 SLDC. PUSH #0000
597D A4 80 92 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=92
5980 A2 04 INC. PUSH #(TOS)^.(04)
5982 DF SLDL. PUSH MP.08
5983 A4 07 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=07
5985 A2 01 INC. PUSH #(TOS)^.(01)
5987 64 SLDC. PUSH #0064
5988 91 NGI. PUSH -(TOS) (2'S COMP)
5989 9A STO. (TOS-1)^ := (TOS)
598A B9 30 UJP. JUMP TO 59BC
598C AC (00)42 00 55 00 B9 28 TABLE: 20 00 04 00 A0 00 08 00 7E 00 0C 00 0E 00 10 00 12 00 14 00 16 00 18 00 1A 00 1C 00 6E 00 20 00 9A 00 51 00 26 00 51 00
XJP. W1,W2,W3,<TABLE>
59BC 0D SLDC. PUSH #000D
59BD 06 SLDC. PUSH #0006
59BE 1A SLDC. PUSH #001A
59BF 04 SLDC. PUSH #0004
59C0 CD 01 1E CXP. CALL EXTERNAL PROCEDURE: 1E IN SEGMENT: 01
59C3 B2 02 10 LDA. PUSH #ACTREC(-02).10
59C6 00 SLDC. PUSH #0000
59C7 A4 80 92 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=92
59CA A2 04 INC. PUSH #(TOS)^.(04)
59CC DF SLDL. PUSH MP.08
59CD A4 07 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=07
59CF F9 SIND. PUSH (TOS)^.1
59D0 C7 E7 03 LDCI. PUSH #03E7
59D3 91 NGI. PUSH -(TOS) (2'S COMP)
59D4 CB NEQI. PUSH ((TOS-1) <> (TOS))
59D5 A1 F0 UJP. IF NOT (TOS) THEN JUMP TO 5787
59D7 B2 02 10 LDA. PUSH #ACTREC(-02).10
59DA 00 SLDC. PUSH #0000
59DB A4 80 92 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=92
59DE A2 04 INC. PUSH #(TOS)^.(04)
59E0 DF SLDL. PUSH MP.08
59E1 A4 07 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=07
59E3 F9 SIND. PUSH (TOS)^.1
59E4 64 SLDC. PUSH #0064
59E5 91 NGI. PUSH -(TOS) (2'S COMP)
59E6 C3 EQUI. PUSH ((TOS-1) = (TOS))
59E7 A1 04 UJP. IF NOT (TOS) THEN JUMP TO 59ED
59E9 01 SLDC. PUSH #0001
59EA 91 NGI. PUSH -(TOS) (2'S COMP)
59EB CC 08 STL. MP.08 := (TOS)
59ED B9 11 UJP. JUMP TO 5A00
59EF B2 02 10 LDA. PUSH #ACTREC(-02).10
59F2 00 SLDC. PUSH #0000
59F3 A4 80 92 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=92
59F6 A2 04 INC. PUSH #(TOS)^.(04)
59F8 DF SLDL. PUSH MP.08
59F9 A4 07 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=07
59FB A2 02 INC. PUSH #(TOS)^.(02)
59FD 01 SLDC. PUSH #0001
59FE 91 NGI. PUSH -(TOS) (2'S COMP)
59FF 9A STO. (TOS-1)^ := (TOS)
5A00 DF SLDL. PUSH MP.08
5A01 01 SLDC. PUSH #0001
5A02 82 ADI. PUSH ((TOS) + (TOS-1))
5A03 CC 08 STL. MP.08 := (TOS)
5A05 DF SLDL. PUSH MP.08
5A06 EA SLDO. PUSH BASE.03
5A07 C3 EQUI. PUSH ((TOS-1) = (TOS))
5A08 A1 EC UJP. IF NOT (TOS) THEN JUMP TO 575D
5A0A B6 02 02 LOD. PUSH ACTREC(-02).02
5A0D 02 SLDC. PUSH #0002
5A0E CB NEQI. PUSH ((TOS-1) <> (TOS))
5A0F A1 67 UJP. IF NOT (TOS) THEN JUMP TO 5A78
5A11 0E SLDC. PUSH #000E
5A12 06 SLDC. PUSH #0006
5A13 CD 01 1F CXP. CALL EXTERNAL PROCEDURE: 1F IN SEGMENT: 01
5A16 D7 NOP. NOP
5A17 A6 18 50 52 45 53 53 20 5B 52 45 54 55 52 4E 5D 20 54 4F 20 46 49 47 48 54 2C
PRESS [RETURN] TO FIGHT,
LSA. PUSH #(PC+1) POINTER TO THE STRING
5A31 CD 01 17 CXP. CALL EXTERNAL PROCEDURE: 17 IN SEGMENT: 01
5A34 19 SLDC. PUSH #0019
5A35 07 SLDC. PUSH #0007
5A36 CD 01 1F CXP. CALL EXTERNAL PROCEDURE: 1F IN SEGMENT: 01
5A39 A6 02 4F 52
OR
LSA. PUSH #(PC+1) POINTER TO THE STRING
5A3D D7 NOP. NOP
5A3E CD 01 17 CXP. CALL EXTERNAL PROCEDURE: 17 IN SEGMENT: 01
5A41 0E SLDC. PUSH #000E
5A42 08 SLDC. PUSH #0008
5A43 CD 01 1F CXP. CALL EXTERNAL PROCEDURE: 1F IN SEGMENT: 01
5A46 D7 NOP. NOP
5A47 A6 18 47 4F 20 42 29 41 43 4B 20 54 4F 20 52 45 44 4F 20 4F 50 54 49 4F 4E 53
GO B)ACK TO REDO OPTIONS
LSA. PUSH #(PC+1) POINTER TO THE STRING
5A61 CD 01 17 CXP. CALL EXTERNAL PROCEDURE: 17 IN SEGMENT: 01
5A64 CD 01 0D CXP. CALL EXTERNAL PROCEDURE: 0D IN SEGMENT: 01
5A67 F0 SLDO. PUSH BASE.09
5A68 0D SLDC. PUSH #000D
5A69 C3 EQUI. PUSH ((TOS-1) = (TOS))
5A6A F0 SLDO. PUSH BASE.09
5A6B 42 SLDC. PUSH #0042
5A6C C3 EQUI. PUSH ((TOS-1) = (TOS))
5A6D 8D LOR. PUSH ((TOS-1) OR (TOS))
5A6E A1 EA UJP. IF NOT (TOS) THEN JUMP TO 5A64
5A70 F0 SLDO. PUSH BASE.09
5A71 42 SLDC. PUSH #0042
5A72 C3 EQUI. PUSH ((TOS-1) = (TOS))
5A73 A1 03 UJP. IF NOT (TOS) THEN JUMP TO 5A78
5A75 00 SLDC. PUSH #0000
5A76 CC 08 STL. MP.08 := (TOS)
5A78 0D SLDC. PUSH #000D
5A79 06 SLDC. PUSH #0006
5A7A 1A SLDC. PUSH #001A
5A7B 04 SLDC. PUSH #0004
5A7C CD 01 1E CXP. CALL EXTERNAL PROCEDURE: 1E IN SEGMENT: 01
5A7F 01 SLDC. PUSH #0001
5A80 0B SLDC. PUSH #000B
5A81 26 SLDC. PUSH #0026
5A82 04 SLDC. PUSH #0004
5A83 CD 01 1E CXP. CALL EXTERNAL PROCEDURE: 1E IN SEGMENT: 01
5A86 B9 E8 UJP. JUMP TO 5758
5A88 00 SLDC. PUSH #0000
5A89 CC 08 STL. MP.08 := (TOS)
5A8B EA SLDO. PUSH BASE.03
5A8C 01 SLDC. PUSH #0001
5A8D 95 SBI. PUSH ((TOS-1) - (TOS))
5A8E CC 0A STL. MP.0A := (TOS)
5A90 DF SLDL. PUSH MP.08
5A91 E1 SLDL. PUSH MP.0A
5A92 C8 LEQI. PUSH ((TOS-1) <= (TOS))
5A93 A1 6D UJP. IF NOT (TOS) THEN JUMP TO 5B02
5A95 C6 01 LLA. PUSH #MP.01
5A97 DF SLDL. PUSH MP.08
5A98 A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
5A9A F8 SIND. PUSH (TOS)^.0
5A9B 00 SLDC. PUSH #0000
5A9C C5 GRTI. PUSH ((TOS-1) > (TOS))
5A9D A1 5C UJP. IF NOT (TOS) THEN JUMP TO 5AFB
5A9F C6 01 LLA. PUSH #MP.01
5AA1 DF SLDL. PUSH MP.08
5AA2 A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
5AA4 F8 SIND. PUSH (TOS)^.0
5AA5 0A SLDC. PUSH #000A
5AA6 C5 GRTI. PUSH ((TOS-1) > (TOS))
5AA7 A1 2C UJP. IF NOT (TOS) THEN JUMP TO 5AD5
5AA9 A5 4A LA0. PUSH #BASE.4A
5AAB DF SLDL. PUSH MP.08
5AAC A4 68 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=68
5AAE A2 50 INC. PUSH #(TOS)^.(50)
5AB0 C6 01 LLA. PUSH #MP.01
5AB2 DF SLDL. PUSH MP.08
5AB3 A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
5AB5 F8 SIND. PUSH (TOS)^.0
5AB6 0A SLDC. PUSH #000A
5AB7 95 SBI. PUSH ((TOS-1) - (TOS))
5AB8 01 SLDC. PUSH #0001
5AB9 95 SBI. PUSH ((TOS-1) - (TOS))
5ABA A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
5ABC A5 4A LA0. PUSH #BASE.4A
5ABE DF SLDL. PUSH MP.08
5ABF A4 68 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=68
5AC1 A2 50 INC. PUSH #(TOS)^.(50)
5AC3 C6 01 LLA. PUSH #MP.01
5AC5 DF SLDL. PUSH MP.08
5AC6 A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
5AC8 F8 SIND. PUSH (TOS)^.0
5AC9 0A SLDC. PUSH #000A
5ACA 95 SBI. PUSH ((TOS-1) - (TOS))
5ACB 01 SLDC. PUSH #0001
5ACC 95 SBI. PUSH ((TOS-1) - (TOS))
5ACD A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
5ACF F8 SIND. PUSH (TOS)^.0
5AD0 01 SLDC. PUSH #0001
5AD1 95 SBI. PUSH ((TOS-1) - (TOS))
5AD2 9A STO. (TOS-1)^ := (TOS)
5AD3 B9 26 UJP. JUMP TO 5AFB
5AD5 A5 4A LA0. PUSH #BASE.4A
5AD7 DF SLDL. PUSH MP.08
5AD8 A4 68 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=68
5ADA A2 49 INC. PUSH #(TOS)^.(49)
5ADC C6 01 LLA. PUSH #MP.01
5ADE DF SLDL. PUSH MP.08
5ADF A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
5AE1 F8 SIND. PUSH (TOS)^.0
5AE2 01 SLDC. PUSH #0001
5AE3 95 SBI. PUSH ((TOS-1) - (TOS))
5AE4 A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
5AE6 A5 4A LA0. PUSH #BASE.4A
5AE8 DF SLDL. PUSH MP.08
5AE9 A4 68 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=68
5AEB A2 49 INC. PUSH #(TOS)^.(49)
5AED C6 01 LLA. PUSH #MP.01
5AEF DF SLDL. PUSH MP.08
5AF0 A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
5AF2 F8 SIND. PUSH (TOS)^.0
5AF3 01 SLDC. PUSH #0001
5AF4 95 SBI. PUSH ((TOS-1) - (TOS))
5AF5 A4 01 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=01
5AF7 F8 SIND. PUSH (TOS)^.0
5AF8 01 SLDC. PUSH #0001
5AF9 95 SBI. PUSH ((TOS-1) - (TOS))
5AFA 9A STO. (TOS-1)^ := (TOS)
5AFB DF SLDL. PUSH MP.08
5AFC 01 SLDC. PUSH #0001
5AFD 82 ADI. PUSH ((TOS) + (TOS-1))
5AFE CC 08 STL. MP.08 := (TOS)
5B00 B9 E4 UJP. JUMP TO 5A90
5B02 AD 00 RNP. RETURN FROM NON-BASE PROCEDURE.

Tommy

qkumba

unread,
May 12, 2014, 4:38:50 PM5/12/14
to
Here's my incomplete attempt. I can't handle the last part because there are too many indirections and my brain exploded.
I based my code on the CACTION (thanks for that hint), and I'm guessing that a variation of GETWIN() is used, which accepts only four parameters.

P010602
BEGIN
(*
574C CE 13 CLP. CALL CHILD PROCEDURE: 13
*)
(*??*)
MYCHARX := 0;
FILLCHAR(MP01, 12, 0);
WHILE MYCHARX < PARTYCNT
BEGIN
REPEAT
IF (BATTLERC[ 0].A.TEMP04[ MYCHARX].STATUS = OK) AND
(SURPRISE <> 2) THEN
BEGIN
BATTLERC[ 0].A.TEMP04[ MYCHARX].SPELLHSH := -999;
REPEAT
AGIL1TEN := RANDOM MOD 10;
CASE CHARACTR[ MYCHARX].ATTRIB[ AGILITY] OF
3: AGIL1TEN := AGIL1TEN + 3;
4,5: AGIL1TEN := AGIL1TEN + 2;
6,7: AGIL1TEN := AGIL1TEN + 1;
15: AGIL1TEN := AGIL1TEN - 1;
16: AGIL1TEN := AGIL1TEN - 2;
17: AGIL1TEN := AGIL1TEN - 3;
18: AGIL1TEN := AGIL1TEN - 4;
END; (* CASE CHARACTR *)
IF AGIL1TEN < 1 THEN
AGIL1TEN := 1
ELSE
IF AGIL1TEN > 10 THEN
AGIL1TEN := 10;
BATTLERC[ 0].A.TEMP04[ MYCHARX].AGILITY := AGIL1TEN;
UNITCLEAR( 1);
GOTOXY(13, 6);
WRITE(CHARACTR[ MYCHARX].NAME);
WRITE('''S OPTIONS');
GOTOXY(13, 8);
IF MYCHARX < 3 THEN
WRITE('F)IGHT ');
WRITE('S)PELL P)ARRY');
GOTOXY(13, 9);
WRITE('R)UN U)SE ');
BDISPELL := FALSE;
IF (CHARACTR[ MYCHARX].CLASS = PRIEST) OR
((CHARACTR[ MYCHARX].CLASS = LORD) AND
(CHARACTR[ MYCHARX].CHARLEV > 8) ) OR
((CHARACTR[ MYCHARX].CLASS = BISHOP) AND
(CHARACTR[ MYCHARX].CHARLEV > 3) ) THEN
BEGIN
BDISPELL := TRUE;
WRITE('D)ISPELL ')
END;
REPEAT
GETKEY
UNTIL (INCHAR = 'F') OR
(INCHAR = 'S') OR
(INCHAR = 'P') OR
(INCHAR = 'U') OR
(INCHAR = 'D') OR
(INCHAR = 'R') OR
(INCHAR = 'B');
GETWIN(13, 8, 26, 2);
MP01[ MYCHARX] := 0;
CASE INCHAR OF
'D': IF BDISPELL THEN
WHICHGRP( 'DISPELL WHICH GROUP (NUM) ?', -5);
'R': RUNAWAY;
'F': IF MYCHARX < 3 THEN
WHICHGRP( 'FIGHT AGAINST GROUP (NUM) ?', -1);
'P': BEGIN
BATTLERC[ 0].A.TEMP04[ MYCHARX].SPELLHSH := 0;
BATTLERC[ 0].A.TEMP04[ MYCHARX].AGILITY := -1
END;
'S': GETSPELL;
'U': BEGIN
USEITEM;
GETWIN(1, 11, 38, 4);
END;
'B': IF (MYCHARX > 0) THEN
BATTLERC[ 0].A.TEMP04[ MYCHARX].SPELLHSH := -100;
END; (* CASE INCHAR *)
GETWIN(13, 6, 26, 4);
UNTIL BATTLERC[ 0].A.TEMP04[ MYCHARX].SPELLHSH <> -999;
IF (BATTLERC[ 0].A.TEMP04[ MYCHARX].SPELLHSH = -100) THEN
MYCHARX := -1
END (* IF (BATTLERC *)
ELSE
BEGIN
BATTLERC[ 0].A.TEMP04[ MYCHARX].AGILITY := -1
END;
MYCHARX := MYCHARX + 1
UNTIL MYCHARX = PARTYCNT;
IF SURPRISE <> 2 THEN
BEGIN
GOTOXY(14, 6);
WRITE('PRESS [RETURN] TO FIGHT,');
GOTOXY(25, 7);
WRITE('OR');
GOTOXY(14, 8);
WRITE('GO B)ACK TO REDO OPTIONS');
REPEAT
GETKEY
UNTIL (INCHAR = CHR (CRETURN)) OR
(INCHAR = 'B') );
IF (INCHAR = 'B') THEN
MYCHARX := 0
END (* IF SURPRISE *)
GETWIN(13, 6, 26, 4);
GETWIN(1, 11, 38, 4);
END; (* WHILE MYCHARX *)
FOR MYCHARX := 0 TO PARTYCNT - 1 DO
BEGIN
IF (MP01[MYCHARX] > 0) THEN
IF (MP01[MYCHARX] > 10) THEN
(*
*)
(*??*)
ELSE
(*
*)
(*??*)
END (* FOR MYCHARX *)
END.

TommyGoog

unread,
May 13, 2014, 4:54:17 AM5/13/14
to
On Monday, May 12, 2014 3:38:50 PM UTC-5, qkumba wrote:
> Here's my incomplete attempt. I can't handle the last part because there are too many indirections and my brain exploded.
>
> I based my code on the CACTION (thanks for that hint), and I'm guessing that a variation of GETWIN() is used, which accepts only four parameters.
>

Maybe I should have left the hint part out because I think I made it too easy for you. You have the correct code.

Regarding GETWIN(), there was a major change to the interface in Wizardry III (LOL) to use the hi-res display and overlapping windows (in 1984). Wizardry I (Proving Grounds) does not have this type of code and only uses the lo-res screen for the most part.

If you want to examine some really good and interesting code in LOL, I'd suggest looking at the memory management and their use of windows( GETWIN, etc.) I feel they did a remarkable job of working around the restrictions of Apple Pascal 1.1 non-implementation of the DISPOSE function (see MARK and RELEASE).

"I can't handle the last part because there are too many indirections and my brain exploded."

Yeah, looking at pcode to identify "a field in an array of records of an array of records indexed by an array" will do that to you. Here is Pascal code for that last part:

IF CACTMP01[ MYCHARX] > 0 THEN
BEGIN
IF CACTMP01[ MYCHARX] > 10 THEN
CHARACTR[ MYCHARX].PRIESTSP[ CACTMP01[ MYCHARX] - 10]
:= CHARACTR[ MYCHARX].PRIESTSP[ CACTMP01[ MYCHARX] - 10] - 1
ELSE
CHARACTR[ MYCHARX].MAGESP[ CACTMP01[ MYCHARX]]
:= CHARACTR[ MYCHARX].MAGESP[ CACTMP01[ MYCHARX]] - 1
END

I called it CACTMP01 because there were other child procedures that accessed their own MP01 and CACTION's MP01. Eventually the names will be changed.

It's good to know that at least someone (qkumba) is reading this thread and understands some of what I am doing.

Here's another interesting piece of code that I'd like to discuss in more detail after we get to the Pascal code. Try to come up with the Pascal code, but be careful or your head might explode on this one. This is the entire procedure:

50D6 A5 83 B6 LA0. PUSH #BASE.03B6
50D9 04 SLDC. PUSH #0004
50DA A5 4A LA0. PUSH #BASE.4A
50DC B6 02 08 LOD. PUSH ACTREC(-02).08
50DF A4 68 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=68
50E1 A2 1E INC. PUSH #(TOS)^.(1E)
50E3 B6 01 09 LOD. PUSH ACTREC(-01).09
50E6 01 SLDC. PUSH #0001
50E7 95 SBI. PUSH ((TOS-1) - (TOS))
50E8 A4 04 IXA. PUSH #((TOS-1)[TOS]). ARRAY SIZE=04
50EA FB SIND. PUSH (TOS)^.3
50EB 4E SLDC. PUSH #004E
50EC 00 SLDC. PUSH #0000
50ED 00 SLDC. PUSH #0000
50EE CD 01 03 CXP. CALL EXTERNAL PROCEDURE: 03 IN SEGMENT: 01
50F1 B2 01 0A LDA. PUSH #ACTREC(-01).0A
50F4 00 SLDC. PUSH #0000
50F5 4E SLDC. PUSH #004E
50F6 9E 02 MVL. MOVELEFT
50F8 AD 00 RNP. RETURN FROM NON-BASE PROCEDURE.

Tommy

qkumba

unread,
May 14, 2014, 12:44:07 PM5/14/14
to
This one is likely quite wrong:

MOVELEFT(EXTERN(BASE.4A[ACTREC02.08].TEMP30[ACTREC01.09-1].FIELD3, 78, 0, 0), ACTREC.10, 80);

TommyGoog

unread,
May 16, 2014, 12:33:56 PM5/16/14
to
On Wednesday, May 14, 2014 11:44:07 AM UTC-5, qkumba wrote:
>
> MOVELEFT(EXTERN(BASE.4A[ACTREC02.08].TEMP30[ACTREC01.09-1].FIELD3, 78, 0, 0), ACTREC.10, 80);

The previous pcode I posted and the Pascal code that generates it is some of the most elegant code in Wizardry. Yours was a good try, but there are a couple of errors in there. Here is Pascal code to generate the pcode:

PROCEDURE READOBJT; (* P010606 *)

BEGIN
MOVELEFT(
IOCACHE[ GETREC(
ZOBJECT,
CHARACTR[ MYCHARX].POSS.POSSESS[ POSSX].EQINDEX,
SIZEOF( TOBJREC))],
OBJECT,
SIZEOF( TOBJREC))
END;

MOVELEFT and GETREC() are key to understanding the Wizardry data file SCENARIO.DATA and the way the data is accessed by the program. There are many MOVELEFT instructions of this format throughout the Wizardry code (I am basing most of this on my analysis of Wizardry III from some time ago, but I think it is also accurate for Wizardry I.)

MOVELEFT is a built in Apple Pascal function call. It has the form "MOVELEFT( A, B, size)". Size indicates the number of bytes to copy from variable A to variable B.

For this particular MOVELEFT, the first parameter is the IOCACHE array which is indexed by the return value of the GETREC function. For this MOVELEFT the destination variable is OBJECT (type TOBJREC) which contains many fields. Its size is indicated by "SIZEOF( TOBJREC)".

The GETREC call returns an index into IOCACHE for the specific item ( in this case an object) we are interested in. There are several types of data in SCENARIO.DATA (see TZSCN). The file contains information about ENEMYs (ZENEMY), REWARDs (ZREWARD), OBJECTs (ZOBJECT), CHARacters (ZCHAR), etc.

CHARACTR refers to one of the 6 characters we take into the maze. The index MYCHARX indicates the particular character we are interested in. Each character can have a number of possessions with them and we are interested in this characters possession POSSX. EQINDEX is the identifier for this particular possession and is used as an index into other data tables.

Now we get to the interesting part. What is in IOCACHE when this function is called? Let me help you out. Is it:

A. Undefined values
B. Previous data from SCENARIO.DATA that we don't care about any more
C. Previous data from SCENARIO.DATA that contains updated fields that need to be written back to the file
D. Previous data from SCENARIO.DATA that contains EXACTLY the information that we want to transfer to OBJECT
E. Any of the above

The answer is "E". Hopefully this should raise some eyebrows.

The GETREC() function call doesn't just do a simple I/O to the file for the data we are interested in. It first checks to see if there is valid data in the IOCACHE array. If it needs to be written to the diskette, then that is done first. After that I/O (if needed) it checks to see if we already have the data in IOCACHE that is being requested. If we already have it, it calculates the index and returns. If the data is not in IOCACHE it then reads from SCENARIO.DATA to retrieve the information, calculates the index into IOCACHE and returns.

Therefore GETREC() sometimes does a WRITE operation to SCENARIO.DATA, and sometimes (usually) does a READ operation to SCENARIO.DATA, and sometimes it does neither!

Another intersting question: What is in IOCACHE after GETREC() returns. Is it:

A. Just 1 record
B. A whole bunch of records
C. All the records of the type we are interested in.

IOCACHE isn't large enough for "C" in all cases so that is not the correct answer. And reading 1 record doesn't make sense. So by the process of elimination we have B. IOCACHE is large enough to read 2 Pascal blocks (1024 bytes) and it fills this buffer as full as it can with the type of items we have asked for.

To understand the format of SCENARIO.DATA, you need to understand TSCNTOC (Type SCeNario.data Table Of Contents). A field of particular interest here is RECPER2B (RECords PER 2 Blocks). This field is used by GETREC() to determine where the data is in the file that we are interested in. In a later posting I may go into more detail about SCENARIO.DATA and the information contained within it.

You might be wondering why the original authors went to such lengths to get data from the file to a variable in the code. I think its pretty clear they did it to enhance performance.

Also worth mentioning is GETRECW() in Wizardry I is almost identical to the code in GETREC(). In Wizardry III, GETRECW() has been streamlined.

This function GETREC() is a good example of the importance while re-engineering code to name the variables as accurately as possible. I could have named the buffer BUFFER instead of IOCACHE and everything would work the same, but by naming it more appropriately, when you look at the code you have a better idea of its purpose.

I'm probably forgetting some of the things I wanted to mention about this code, but I hope you will study it and appreciate the effort that went into it by the original authors as much as I have.

Tommy

Antoine Vignau

unread,
May 16, 2014, 4:54:49 PM5/16/14
to
I like this thread, very good hacking in there!
av

TommyGoog

unread,
May 18, 2014, 10:36:27 AM5/18/14
to
On Friday, May 16, 2014 3:54:49 PM UTC-5, Antoine Vignau wrote:
> I like this thread, very good hacking in there!
>
> av

Thank you Antoine for the comments. It's nice to know that others are reading this thread and appreciating my efforts.

Yesterday I ran into a problem while re-engineering Wizardy I (Proving Grounds). There is a sequence of pcode that is different from anything else in the 15000 lines of code in Wizardry III or the code for Wiz1 that I have already reengineerred. I spent several hours on it and still could not figure it out. Even after a good nights sleep I still had difficulty figuring out how to do such a simple task. But now I know how.

Here is the sequence of pcode from procedure DAM2ENMY (P01090F for both Wiz1 and Wiz3):

59FA B6 06 03 LOD. PUSH ACTREC(-06).03
59FD A6 03 07 07 07

LSA. PUSH #(PC+1) POINTER TO THE STRING
5A02 D7 NOP. NOP
5A03 00 SLDC. PUSH #0000
5A04 CD 00 13 CXP. CALL EXTERNAL PROCEDURE: 13 IN SEGMENT: 00

I'm not sure how this will display with Google Groups or whatever USENET reader you are using because the third line down has 3 ASCII control characters (the BEL character or ASCII 07).

ACTREC(-06).03 is the standard Pascal OUTPUT file designator.
#0000 is part of the call to PROCEDURE: 13.
PROCEDURE 13 is "WRITE( string)". The parameter is type string.
"A6 03 07 07 07" indicates an ASCII string with 3 characters, each one being a BEL character (ASCII 7).

This code occurs right after displaying "A CRITICAL HIT!", and generates 3 quick sounds on the Apple's speaker.

So the code should look something like the following:

WRITE( ' '); (* 3 BEL characters *)

That's it. What could be so difficult? Well......

How do you enter a BEL character using the Apple Pascal 1.1 Editor?

Go ahead, try it.

Try Insert mode and press what? CTRL-G? CTRL-7? Maybe something like '\07'?
Nope, Nope, Nope.

Maybe you need to use the CONCAT() function?
Nope.

Maybe there is an overloaded operator such as "+" so that CHR(7)+CHR(7)+CHR(7) would work? Are you kidding me? In 1980? For Pascal?

Maybe use CHR or ORD or '' or some combination?
Nope.

Noting that PAGE(OUTPUT) generates WRITE( CHR(12)), maybe there is a built in call for sounding 3 bells, like $FF3A BELL to output a BELL character?
Nope.

Maybe use the Windows "ALT" key method to enter an ASCII character (Press ALT key down, and hold it down while entering 0 0 7 using the keypad.)
Nope.

Maybe use APPLE II and not APPLE IIe?
Maybe I need to use actual hardware and not an emulator?
How about the CAPS LOCK key?
Multiple keys at the same time? Like ALT, SHIFT CTRL G?

I was starting to call this the DAMN code, but then realized it is DAM2ENMY.

Do I really need to use a tool outside the editor to edit the TEXT file (or the .CODE file) to change the string to have 3 BELL characters? (That would work, but really? You think the original authors would go to that trouble?)

So your task is to write 1 line of code using only the Pascal 1.1 editor.

In a few days I will give you my solution for using only the Pascal 1.1 editor to write code that will produce the correct pcode above.

Tommy

Antoine Vignau

unread,
May 18, 2014, 12:53:56 PM5/18/14
to
can you insert asm code in pascal? that would be my solution to insert non-editable chars,
av

qkumba

unread,
May 19, 2014, 2:16:42 PM5/19/14
to
On Sunday, May 18, 2014 9:53:56 AM UTC-7, Antoine Vignau wrote:
> can you insert asm code in pascal? that would be my solution to insert non-editable chars,

Not inline, and it wouldn't be a single line, so that's not Tommy's solution.
I considered using a packed array, but technically that wouldn't be one line, and it would not generate the correct p-code anyway. I don't have an answer.

Antoine Vignau

unread,
May 19, 2014, 4:15:56 PM5/19/14
to
put the editor in inverse mode, enter the chars which will be changed to 7 7 7 by the editor, then quit the inverse mode!

antoine

TommyGoog

unread,
May 22, 2014, 12:42:15 PM5/22/14
to
On Monday, May 19, 2014 3:15:56 PM UTC-5, Antoine Vignau wrote:
> On Monday, May 19, 2014 8:16:42 PM UTC+2, qkumba wrote:

> > > can you insert asm code in pascal? that would be my solution to insert non-editable chars,

>
> put the editor in inverse mode, enter the chars which will be changed to 7 7 7 by the editor, then quit the inverse mode!
>
> antoine


As stated earlier, there is no "inline" (asm) support in Pascal 1.1.

Entering the characters that would change to 7 7 7 after first putting the editor in "inverse" mode would work if you knew what characters to type and if there was an "inverse" mode (there isn't).

Using PACKED arrays also would not generate the proper code so that solution is not correct either.

Before I show you how to enter the control character for BEL (ASCII 7) into a string constant using Apple Pascal 1.1, I will first explain how I arrived at the solution.

After a day of frustation and not finding a simple way to enter 3 BEL characters into a string constant, I had an uncomfortable thought: What if my workaround to edit the source file doesn't work. For example, let's say I edit the 3 BEL characters into the source file, then use the Pascal editor to edit the file. What is to guarantee that the editor won't strip those characters out? What if the compiler decides to abort when encountering those 3 BEL characters imbedded in a string constant?

So first I wrote a small program:

PROGRAM MYPROG;
BEGIN
WRITELN( '???')
END.

Where you see the "???" I typed CTRL-G 3 times. Each keystroke was displayed as a question mark "?". Now what if it really does put a BEL into the string and just displays it as a question mark? But after compiling and checking the ".CODE" file, I found that they really were just question marks (ASCII $3F).

Using COPY II+, I found the source file sector on the diskette and changed the "???" to BEL BEL BEL (07 07 07).

Next, I entered the Pascal editor. The first surprise was that the editor displayed the code and BEEPED the speaker 3 times!

The second surprise was seeing the code:

WRITELN('')

Those are 2 single quotes (apostrophes) and it looks like a NULL string.

I saved the file and then compiled the program and ran it. The speaker BEEPED 3 times! So at least I knew I could edit the BEL characters into the source file, compile the file, and have it work.

The next thing I tried was moving the cursor over the WRITELN using the arrow keys. I found that it moved properly over the characters, but when I got to the ")", I could move the cursor 4 more positions to the right. If it was just a NULL string for the WRITELN, it would go to the next line when pressing the right-arrow key twice while on the parenthesis.

Well, let's try F(ind and see if it can somehow "find" the BEL character. I may have tried a few different things first, but then tried:

F(ind
/CTRL-G/

"CTRL-G" is hold the CTRL key down and press the G key.

It seemed to find it and placed the cursor over the right paren ")".

Next up was to insert "WRITELN('TOM')" and try to change "TOM" to BEL BEL BEL.

Using "X(chng", I entered CTRL-G 3 times and was disappointed to see only "???".

How about using R(place?

R(place
/???//CTRL-G CTRL-G CTRL-G/

That's it!!!

Note, every time the editor displays the page of text with the imbedded BEL BEL BEL, the speaker beeps 3 times! How annoying!

To summarize, you cannot directly enter control characters into the text of a Pascal program, but you can use R(place to change characters to control characters.

And remember, "Ask not for whom the BEL tolls..."

Tommy

Marco Verpelli

unread,
May 22, 2014, 1:52:44 PM5/22/14
to
Very clever!

The "right" method to output control characters in Apple Pascal is:

...
VAR CMD:PACKED ARRAY[0..2] OF 0..255
...
CMD[0]:=7; CMD[1]:=7; CMD[2]:=7;
UNITWRITE(1,CMD,3); {ring the bell 3 times}
...

See Mouse Technical Notes #6

Marco

Antoine Vignau

unread,
May 22, 2014, 10:56:13 PM5/22/14
to
How complicated, Tommy!

> "Entering the characters that would change to 7 7 7 after first putting the editor in "inverse" mode would work if you knew what characters to type and if there was an "inverse" mode (there isn't). "

The manual of Pascal 1.1 states there is an inverse mode.

antoine

TommyGoog

unread,
May 23, 2014, 11:44:59 AM5/23/14
to
This is the manual for Apple Pascal 1.1 that I am using:

ftp://ftp.apple.asimov.net/pub/apple_II/documentation/programming/pascal/Apple%20Pascal%20-%20Operating%20System%20Reference%20Manual.pdf

It covers the Filer and the Editor.

This manual does not mention "inverse" or "inverse mode".

Tommy

TommyGoog

unread,
May 23, 2014, 11:54:02 AM5/23/14
to
That is an interesting find Marco. I'm not sure I've seen any documentation that really says what is "right" or not for sending control characters from Pascal code to perform non-character functions.

For example, in the Pascal 1.2 (follow on to 1.1 version), there is the following in the manual on p. 59:

WRITE( CHR( 5));
WRITE( CHR( 6));
WRITE( CHR( 15));
WRITE( CHR( 14));

It would seem that the following should then also be valid for "ringing the bell" 3 times:

WRITE( CHR(7), CHR(7), CHR(7));

Tommy

Antoine Vignau

unread,
May 23, 2014, 1:10:56 PM5/23/14
to
Tommy,
page 9 of the operating system addendum (apple part #031-0100-00)
ctrl-e and ctrl-w are used to inverse/normal and upper/lower-case chars

does that generate the right BEL char if used?

av

Marco Verpelli

unread,
May 23, 2014, 1:41:49 PM5/23/14
to
I've should wrote "official" rather then "right".

In the Apple Pascal 1.3 manual ch.III pag.266 only control character 5,6,14,15 are used in a write statement for screen control.

Marco

Michael J. Mahon

unread,
May 23, 2014, 4:42:51 PM5/23/14
to
Certainly, the right way is one that makes it obvious in the source what is
going on.

Depending on characters not displayable in the editor is clearly the wrong
way.
--
-michael - NadaNet 3.1 and AppleCrate II: http://home.comcast.net/~mjmahon

TommyGoog

unread,
May 23, 2014, 10:09:03 PM5/23/14
to
Hi Antoine,

The short answer is "no".

I was not familiar with the addendum to the Operating System manual. (I have however used the addendum to the Language Reference manual #031-0101-00).

When I tried using CTRL-E and CTRL-W using the emulator in AppleIIe mode (80 columns), they did not have any effect at all. In AppleII+ mode, they work as described.

CTRL-E and CTRL-W are really not putting the editor into "inverse mode". They are just allowing the user to type lower case characters. The upper case characters are displayed in reverse video (black on white), but the characters entered into the editor are either the lower case alphabetic characters or the upper case alphabetic characters.

To be noted is the paragraph in the addendum:

"Note that only letters are affected by these features...."

So there doesn't seem to be a way of using CTRL-E or CTRL-W to change a character to a BEL (ASCII 7) character. They only affect the upper/lower casing of alphabetic characters.

Tommy

Antoine Vignau

unread,
May 24, 2014, 12:26:17 AM5/24/14
to
Hi Tommy,
Thanks for checking, so your solution seems to be the only one possible,
av

Vladimir Ivanov

unread,
May 24, 2014, 10:42:17 AM5/24/14
to


On Fri, 23 May 2014, TommyGoog wrote:

> When I tried using CTRL-E and CTRL-W using the emulator in AppleIIe mode
> (80 columns), they did not have any effect at all. In AppleII+ mode,
> they work as described.

Now that you mention it, I remember using UCSD Pascal on Apple II+ clone
and doing lots of CTRL+A and CTRL+E.

TommyGoog

unread,
May 30, 2014, 9:54:15 PM5/30/14
to
On Sunday, May 4, 2014 8:26:53 PM UTC-5, TommyGoog wrote:

>
> > All of STARTUP.CODE is completed.
>
> >Next up is Wizardry.Code.
>
> It's been awhile since I've posted an update on my progress. Lately I've been concentrating my efforts on re-engineering the code rather than trying to

A major milestone was completed today! I have Pascal code (and assembly code) for Wizardry I Proving Grounds that when compiled (assembled) and linked generates exactly the code in SYSTEM.STARTUP and WIZARDRY.CODE.

I thought this part of the project was going to take 4-5 months, but it has only taken 2 months. Even though it took less time, I think it was still more difficult than what I thought it would would be.

The next phase is to rename variables and procedures from the generic names such as MP01 and P010017 to more mneumonic names. I will also try to make the Pascal formatting consistent. Then I will upload the results to ASIMOV.

Once I complete the upload to ASIMOV (probably in a few weeks), I will then start more analysis of the code similar to what I did for Legacy of Llylgamyn. I will look for defects in the code and examine changes from Wiz1 to Wiz3. I would estimate that 95 percent of the game logic was the same between Wiz1 and Wiz3. The major data structures were almost 100% the same. What made this difficult was that from Wiz2 to Wiz3 there was a major change in the visual output displayed. A whole new "overlapped windows" system.

If you have any questions or suggestions on what you want to see analyzed in the code, please let me know.

Tommy

Antoine Vignau

unread,
May 30, 2014, 10:37:01 PM5/30/14
to
Congratulations, Tommy!

What will you do with it? Release a version with no copy protection check routines?
av

qkumba

unread,
Jun 1, 2014, 10:15:35 PM6/1/14
to
> A major milestone was completed today! I have Pascal code (and assembly code) for Wizardry I Proving Grounds that when compiled (assembled) and linked generates exactly the code in SYSTEM.STARTUP and WIZARDRY.CODE.

Congratulations to you! I wish that I could have been of some (or any) help.

> If you have any questions or suggestions on what you want to see analyzed in the code, please let me know.

I have one: is there really a way to summon Thundarr the Barbarian in level 4?

Don Bruder

unread,
Jun 1, 2014, 10:54:20 PM6/1/14
to
In article <dcc69a8d-3fd1-434b...@googlegroups.com>,
I'm wanting to say that Thundarr showing up was a random event, rather
than anything you could control by doing (or not doing) something, but
it's been a looooong time since I last played - Definitely DON'T take my
memory as authoritative!

--
Security provided by Mssrs Smith and/or Wesson. Brought to you by the letter Q

TommyGoog

unread,
Jun 8, 2014, 5:05:53 PM6/8/14
to
Hi Antoine,

Wizardry I (Proving Grounds) at ASIMOV already has the copy protection "cracked". I've looked at it a bit this week and was a bit surprised to find that some of the copy protection code resides on side 2 of the disk (the scenario side) and is part of SCENARIO.DATA. As I recall, this is different from Wizardry III (Legacy of Llylgamyn). I will have more to write about the copy protection code in a few weeks -- it is written in a very confusing fashion.

I have thought about Wizardry III and all of its bugs and thought that I should release a version with bug fixes, but not sure anyone would really care about it. I could do the same with Wizardry I.

Tommy

Apple2Games

unread,
Jun 8, 2014, 7:08:45 PM6/8/14
to
And is there anyway to change is so it doesn't do a write protection check when starting? Makes it near impossible to play on cApple Android emulator :)

TommyGoog

unread,
Jun 9, 2014, 12:52:57 AM6/9/14
to
On Sunday, June 8, 2014 6:08:45 PM UTC-5, Apple2Games wrote:
> And is there anyway to change is so it doesn't do a write protection check when starting? Makes it near impossible to play on cApple Android emulator :)
>

Hi Apple2Games,

Yes.
Change 1 byte on Track $22 Sector $0B at address $1E from $F6 to $00.

The code is from SEGMENT OPTIONS (P070301 is my naming convention).

The code checking for write protection is just before the COPYRIGHT (C) notice in the code. By changing from F6 to 00 changes the infinite loop to jump to the next instruction instead of to the previous instruction at 501D in the listing below.

JTAB FOR SUBROUTINE 1:
DATA SIZE: 512
PARAM SIZE: 0
EXIT AT: 5227
ENTER AT: 5000
PROC NUMBER: 1
LEXICAL LEVEL: 1
5000 04 SLDC. PUSH #0004
5001 C6 01 LLA. PUSH #MP.01
5003 00 SLDC. PUSH #0000
5004 C7 00 02 LDCI. PUSH #0200
5007 00 SLDC. PUSH #0000
5008 00 SLDC. PUSH #0000
5009 9E 05 READ FROM VOL#
500B 04 SLDC. PUSH #0004
500C C6 01 LLA. PUSH #MP.01
500E 00 SLDC. PUSH #0000
500F C7 00 02 LDCI. PUSH #0200
5012 00 SLDC. PUSH #0000
5013 00 SLDC. PUSH #0000
5014 9E 06 WRITE VOL#
5016 9E 22 PUSH I/O ERROR STATUS
5018 10 SLDC. PUSH #0010
5019 CB NEQI. PUSH ((TOS-1) <> (TOS))
501A A1 03 UJP. IF NOT (TOS) THEN JUMP TO 501F
501C 00 SLDC. PUSH #0000
501D A1 F6 UJP. IF NOT (TOS) THEN JUMP TO 501C
501F B6 02 03 LOD. PUSH ACTREC(-02).03
5022 0C SLDC. PUSH #000C
5023 00 SLDC. PUSH #0000
5024 CD 00 11 CXP. CALL EXTERNAL PROCEDURE: 11 IN SEGMENT: 00
5027 B6 02 03 LOD. PUSH ACTREC(-02).03
502A D7 NOP. NOP
502B A6 28 43 4F 50 59 52 49 47 48 54 20 28 43 29 31 39 38 31 20 41 4C 4C 20 52 49 47 48 54 53 20 52 45 53 45 52 56 45 44 20 42 59
COPYRIGHT (C)1981 ALL RIGHTS RESERVED BY

Hope this helps!

Tommy

TommyGoog

unread,
Jun 11, 2014, 11:18:59 AM6/11/14
to
On Sunday, June 1, 2014 9:15:35 PM UTC-5, qkumba wrote:

>
> I have one: is there really a way to summon Thundarr the Barbarian in level 4?

I think the short answer is "no", but a very interesting question.

I'm not quite ready to answer questions like this yet, but I was curious and have done a little research on this and found some interesting stuff.

I never made any notes in previous years to "Thundarr the Barbarian", so at first I thought your question might have been about a different version of Wizardry (say on a different platform), or perhaps a different version of the game (say Wizardry IV Return of Werdna). But then I did a search of the internet and found a reference to Thundarr the Barbarian and Wizardry I.

First off, I think you meant "level 6" in your question. And when you say "summon", as far as I can tell, the only thing that would happen would be the display of the following message:

SUDDENLY YOU SEE A GROUP OF 3 HUMAN-
OIDS. ONE IS A BARBARIAN WITH A SWORD
THAT GLOWS, ONE IS A SEXY FEMALE MAGE,
AND THE THIRD LOOKS LIKE A HUGE OGRE!
THE BARBARIAN POINTS TO THE END OF THE
CORRIDOR AND YELLS "ARIEL.. OOKLA..
THIS WAY!! " AND THEY ALL RUN OFF!

The reference I found said this message would be displayed only one time in the game and never displayed again, but when I played the game in the past, I don't think I ever saw this message.

The message is in the SCENARIO.MESGS file and there are remnants of data in SCENARIO.DATA to indicate it would be displayed by going to LEVEL 6, 19 EAST, and 7 NORTH. On my hand drawn map from 2 years ago, I have a "?!" on that square. As I recall the game paused for a bit and there was some disk activity but nothing happened.

In looking at the tables and the code (for both my version and the "cracked" version previously loaded on ASIMOV) it looks like a table entry has a value of "0" instead of some other value, and there is code in Wizardry that if that value is "0" then don't display the message.

The message itself appears near the end of the MESGS file and there are a couple more messages there that I didn't write down 2 years ago when I last played Wizardry I. Those messages are:

ITS GETTING WARM AROUND HERE!

ITS GETTING REALLY HOT!

ITS ALMOST TOO HOT TO BEAR!

The remnant data in the tables seems to imply that these should be displayed on level 7 of the maze.

Has anyone else played the game and seen the Barbarian message, or any of these other 3 messages? Was it with the original Wizardry I game? If you did get the messages could you tell me where I might download the same version that you played so that I can look at the MESGS file?

If you have any other information about Thundarr let me know and I'll try to answer.

Hope this helps!
Tommy

Tempest

unread,
Jun 12, 2014, 9:45:51 AM6/12/14
to
You are correct. The Thundarr encounter never shows up because they accidentally set the odds of it happening to zero. It was supposed to be one of those funny random things that happened very very rarely (something like a 5% chance or less), but in the end they goofed so it never happens. The message is the only thing that appears, there was no fight or anything like that.

Didier VALLET

unread,
Jun 14, 2014, 10:54:55 PM6/14/14
to
Congratulations Tommy
You've been quicker than lightning.
It'll be a pleasure to discover the code of this marvelous classic game

TommyGoog

unread,
Jun 16, 2014, 11:03:05 AM6/16/14
to
On Thursday, June 12, 2014 8:45:51 AM UTC-5, Tempest wrote:
> You are correct. The Thundarr encounter never shows up because they accidentally set the odds of it happening to zero. It was supposed to be one of those funny random things that happened very very rarely (something like a 5% chance or less), but in the end they goofed so it never happens. The message is the only thing that appears, there was no fight or anything like that.

Hi Tempest,

I think you are slightly mistaken about the code. The value set to 0 is not "the odds of it happening". When the value is set non-zero, the message WILL be displayed the first time you enter that square of the maze. The number is decremented each time the message is displayed. Therefore, the authors could have set it to say "3" and the first 3 times you enter that square of the maze the message is displayed, but after that you would never see the message.

My guess is that they had the value set non-zero (probably to 1), then tested the code. By testing the code the value in the database was set to 0. If they now saved the database, this would account for it being 0.

Since there was at least one reference on the internet to someone getting this message at least once, I would opine that perhaps an earlier version of the software had the correct value in Scenario.data, but later releases had it set to 0. I did check the French version of Wizardry 1 and it was also set to 0.

And of course it is possible that the original authors did not "goof" but decided they didn't want the message displayed and purposely set the value to 0 without cleaning up the rest of the database where the message is referenced.

Tommy

TommyGoog

unread,
Jun 16, 2014, 12:15:38 PM6/16/14
to
Today I uploaded the source code and listings to ASIMOV.

/pub/apple_II/images/games/rpg/wizardry/wizardry_I/

In this message I will duplicate the content of WiziSourceCode.ASM.txt, and following that I have more detailed instructions about assembling and compiling the code.

===============================================================
Begin
Title: Wizardry_i_SourceCode
Author: Thomas William Ewers
Version:
Entered-date: June 15, 2014
Description:
Pascal (and assembler) source code files and generated listings for
Wizardry_I, Proving Grounds of the Mad Overlord.

I have re-engineered Pascal source code (and assembler code) from the
Wizardry_I program. The original executable files can be found on:

Wizardry_i_boot.dsk

In this package:

/WizardryCode
Wiz1A.DSK
Wiz1B.DSK
Wiz1C.DSK
Wiz1D.DSK
Wiz1WizardryAssembler.txt
Wiz1WizardryPascal.txt

/SystemStartup
Wiz1E.DSK
Wiz1StartupAssembler.txt
Wiz1StartupPascal.txt

The Pascal code found on the enclosed disks is NOT the code that the original
authors wrote. However, when you compile (and assemble and link) these
files, the EXACT executable code in Wizardry.code and System.Startup are
created.

The source code can be compiled using Apple Pascal 1.1 on an Apple][
computer (or simulator) that has 4 diskette drives.

To compile System.Startup:

1. Assemble WIZ1E:LZDECOMP.TEXT (creates LZDECOMP.CODE)
2. Assemble WIZ1E:CHKSYNCH.TEXT (creates CHKSYNCH.CODE)
3. Assemble WIZ1E:SCREENPT.text (creates SCREENPT.CODE)
4. Compile WIZ1E:STARTUP.TEXT (creates unlinked STARTUP.CODE)
5. Link WIZ1E:STARTUP.CODE (creates STARTUP.CODE)
6. Rename STARTUP.CODE (creates SYSTEM.STARTUP)

To compile WIZARDRY.CODE:

1. Assemble WIZ1D:CLRRECT.TEXT (creates CLRRECT.CODE)
2. Assemble WIZ1D:MVCURSOR.TEXT (creates MVCURSOR.CODE)
3. Assemble WIZ1D:PRGRCHR.TEXT (creates PRGRCHR.CODE)
4. Assemble WIZ1D:DRAWLINE.TEXT (creates DRAWLINE.CODE)
5. Assemble WIZ1D:CLRPICT.TEXT (creates CLRPICT.CODE)
6. Assemble WIZ1D:KEYAVAIL.TEXT (creates KEYAVAIL.CODE)
7. Assemble WIZ1D:RANDOM.TEXT (creates RANDOM.CODE)
8. Compile WIZ1A:WIZ.TEXT (creates unlinked WIZ.CODE)
9. Link WIZ1A:WIZ.CODE (creates WIZARDRY.CODE)

See the discussion on Usenet at:
comp.sys.apple2
"Wizardry re-engineering"

My Google Groups name is: TommyGoog

Keywords: Wizardry, Proving Grounds of the Mad Overlord, SIR-TECH
Uploader: ewe...@msn.com
Primary-site: ftp://ftp.apple.asimov.net
/pub/apple_II/images/games/rpg/wizardry/wizardry_I/

Wizardry_i_SourceCode.zip

WiziSourceCode.ASM.txt

/WizardryCode
Wiz1A.DSK
Wiz1B.DSK
Wiz1C.DSK
Wiz1D.DSK
Wiz1WizardryAssembler.txt
Wiz1WizardryPascal.txt

/SystemStartup
Wiz1E.DSK
Wiz1StartupAssembler.txt
Wiz1StartupPascal.txt


Platform: Apple][
End

===============================================================


Here are more detailed instructions on building and verifying the code.

Build Instructions:

Insert disks into drives:
4: 01 EDIT.DSK
5: 10 WIZ1E.DSK (STARTUP source)
11: 02 COMPILE.DSK
12: 16 WizardryDotCode.dsk

Delete PRINTER.TXT on my computer.
Set AppleWin printer to APPEND.

Boot 01 EDIT.DSK
S)wap ON ("Y")
F)iler
E)xtended #12:
R)emove
STARTUP.CODE and any other files. Should show < UNUSED > 274 6
E)xtended #5
R)emove
remove ALL ".CODE" files
"#5:=.CODE"
K)runch to end of disk.
E)xtended Dir
STARTUP.TEXT
WIZUTILB.TEXT
WIZUTILC.TEXT
TITLELOA.TEXT
OPTIONS.TEXT
LZDECOMP.TEXT
CHKSYNCH.TEXT
SCREENPT.TEXT
< UNUSED > 166 114
A)ssem
Assemble what text?
#5:LZDECOMP
To what codefile?
$
Output file
PRINTER:
A)ssem
#5:CHKSYNCH
$
PRINTER:
A)ssem
#5:SCREENPT
$
PRINTER:
C)ompile
Compile what text?
#5:STARTUP
To what codefile?
$
L)ink
Host file?
#5:STARTUP
Lib file?
#5:LZDECOMP
Lib file?
#5:CHKSYNCH
Lib file?
#5:SCREENPT
Lib file?
<cr>
Map file?
<cr>
see:
Reading WIZBOOT
Reading LZDECOMP
Reading CHKSYNCH
Reading SCREENPT
Output file?
#12:STARTUP
see:
Linking UTILS #7
Linking TITLELOA #8
Linnking OPTIONS #9
Linking WIZBOOT #1
Copying proc LZDECOMP
Copying proc CHKSYNCH
Copying proc SCREENPT
T)ransfer
#12:STARTUP.CODE
to:
#5:STARTUP.CODE
C)hange
#5:STARTUP.CODE
to:
#5:SYSTEM.STARTUP

Eject disk "16 WizardryDotCode" from #12:
Insert "16 WizardryDotCode.DSK" into #5:
Insert "05 DeCompile.DSK" into #4:
Reboot
"Y" for all
Output from all the steps above should be in PRINTER.TXT on my computer.

To find start of PASCAL code, search for "PRINTER:".
To find start of pCode, search for "jtab".
Copy pCode to Wizardry1StartupPcode.txt
Use WINDIFF to compare pCodeProvGrnd STARTUP.txt
to Wizardry1StartupPcode.txt

Compare 6502 code:
RUN DeCompiler
PROCESS ALL?
N
1 STARTUP.CODE
TYPE A FILE NUMBER (or -1)
1
0 WIZBOOT
1 UTILS
2 TITLELOA
3 OPTIONS
TYPE A SEGMENT NUMBER (or -1)
0
SEGMENT'S START ADDR($5000):
<cr>
ENTER PROCEDURE # (OR -1)
-1
TYPE A SEGMENT NUMBER (or -1)
-1
TYPE A FILE NUMBER (or -1)
-1
] (BASIC prompt)
]CALL-151
* (monitor prompt)
*511ALLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
40 "L"s and should display to $5643

Output is in PRINTER.TXT
Save it to TEMP2.
Repeat this displaying of 6502 code using Proving
Grounds Boot Disk.
Save it to TEMP1.
Compare Temp1 to Temp2. They should be the same.

Build Wizardry.Code

Delete PRINTER.TXT on my computer.
Set AppleWin printer to APPEND

Insert disks:

4: 01 EDIT.DSK
5: 09 Wiz1D.DSK
11: 02 COMPILER.DSK
12: 16 WizardryDotCode.DSK

Boot 01 EDIT.DSK
S)wap ON ("Y")
Remove ".CODE" files from #5:
F)iler
R)emove
#5:=.CODE
K)runch #5
E)xtended Dir
CLRRECT.TEXT
MVCURSOR.TEXT
PRGRCHR.TEXT
DRAWLINE.TEXT
CLRPICT.TEXT
KEYAVAIL.TEXT
RANDOM.TEXT
<UNUSED> 220 60

Remove files from #12:
R)emove
#12:=.CODE
<UNUSED> 274 6

A)ssem
Assemble what text?
#5:CLRRECT
To what codefile?
$
Output file
PRINTER:

Assemble the others:

Assemble what text?
#5:MVCURSOR
$
PRINTER:

#5:PRGRCHR
$
PRINTER:

#5:DRAWLINE
$
PRINTER:

#5:CLRPICT
$
PRINTER:

#5:KEYAVAIL
$
PRINTER:

#5:RANDOM
$
PRINTER:

F)iler
E)xtended #5
CLRRECT.TEXT
MVCURSOR.TEXT
PRGRCHR.TEXT
DRAWLINE.TEXT
CLRPICT.TEXT
KEYAVAIL.TEXT
RANDOM.TEXT
CLRRECT.CODE
MVCURSOR.CODE
PRGRCHR.CODE
DRAWLINE.CODE
CLRPICT.CODE
KEYAVAIL.CODE
RANDOM.CODE
<UNUSED> 199 81

Insert Disks:

04: 01 Edit
05: 06 Wiz1A.DSK
11: 07 Wiz1B.DSK
12: 08 Wiz1C.DSK

F)iler
R)emove
#5:WIZ.CODE
K)runch #5:
E)xtended
WIZ.TEXT
WIZ2.TEXT
SHOPS2.TEXT
<UNUSED> 208 72

C)ompile
Compile what text?
#5:WIZ
To what codefile?
$

Insert Disks:

04: 01 Edit
05: 06 Wiz1A.DSK
11: 09 Wiz1D.DSK
12: 16 WizardryDotCode.DSK

Disk 16 should be empty.

L)ink
No file :SYSTEM.LINKER

Insert 02 Compile.DSK into 04:

L)ink
Host file?
#5:WIZ.CODE
Lib file?
#11:CLRRECT
Lib file?
#11:MVCURSOR
Lib file?
#11:PRGRCHR
Lib file?
#11:DRAWLINE
Lib file?
#11:CLRPICT
Lib file?
#11:KEYAVAIL
Lib file?
#11:RANDOM
Lib file?
<cr>
Map file?
<cr>
see:
Reading WIZARDRY
Reading CLRRECT
Reading MVCURSOR
Reading PRGRCHR
Reading DRAWLINE
Reading CLRPICT
Reading KEYAVAIL
Reading RANDOM
Output file?
#12:WIZARDRY
see:
Linking UTILITIE #7
Linking SHOPS #8
Linking SPECIALS #9
Linking COMBAT #10
Linking CINIT #11
Linking CUTIL #12
Linking MELEE #13
Linking CASTASPE #14
Linking SWINGASW #15
Linking CASTLE #16
Linking ROLLER #17
Linking CAMP #18
Linking REWARDS #19
Linking RUNNER #20
Linking WIZARDRY #1
Copying proc CLRRECT
Copying proc MVCURSOR
Copying proc PRGRCHR
Copying proc DRAWLINE
Copying proc CLRPICT
Copying proc KEYAVAIL
Copying proc RANDOM

R)emove
#5:WIZ.CODE
T)ransfer
#12:WIZARDRY.CODE
to:
#5:WIZARDRY.CODE

Insert 01 EDIT.DSK into #4
Eject #12: 16 WizardryDotCode.DSK
Insert #5: 16 WizardryDotCode.DSK
Insert #4: 05 DeCompile.DSK
ReBoot
PROCESS ALL?
Y

Printer.TXT has all the previous output.
Create the following:
Wizardry1WizAssem So Far.txt
Wizardry1WizPascal So Far.txt
Wizardry1WizPcode.txt

Compare Wizardry1WizPcode.txt with pCodeProvGrndBoot.txt

pCodeProvGrndBoot has ALL files on boot diskette pcode,
including SYSTEM.PASCAL.

Compare 6502 code.
RUN DeCompiler
Do same trick as for STARTUP.
Only WIZARDRY.CODE, then SEGMENT 0, then $5000, then -1 -1 -1.
*CALL -151

*5836llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll
about 65 "L"s should do it.

Compare from $5836 to $5D00 as shown in following table.

Link
Order Enter Exit PROC/FUNC
------ -- ----- ----- --------
5 26 5B96 5C80 CLRPICT
4 27 59D8 5B90 DRAWLINE
7 28 5CC0 5D00 RANDOM
6 29 5C86 5CBA KEYAVAIL
1 30 5836 58B6 CLRRECT
2 31 58BC 594E MVCURSOR
3 32 5954 59D2 PRGRCHR


Tommy (Thomas W. Ewers)

Tempest

unread,
Jun 16, 2014, 1:09:21 PM6/16/14
to
Sorry, I thought it was an odds thing, but I guess it's a '# of times to happen' thing. I read how it was accidentally set to zero in an interview that someone did with one of the authors, I need to find that interview. I don't see how it could have ever been seen unless someone hacked the game to fix it. I suppose there could be more than one version out there though (like the Trilogy version).

TommyGoog

unread,
Jun 16, 2014, 2:11:44 PM6/16/14
to
Hi Tempest,

I would be interested in seeing that interview with one of the authors.

It's not unlikely that there was more than one version of the program. The program itself has the command: "U)PDATE" (to current version) built right into it! And the version number I see displayed is "VERSION 2.1 of 22-JAN-82 SER: 391090". There very well could be a VERSION 2.0 out there somewhere. And if anyone has 2.0 (or 1.0), please let me know!

Also regarding version numbers, there are several semi-official online references that state the last version of WizardryI was created in 1981. That seems to conflict with the displayed message.

And as I found out during the Wizardy III decompilation effort, there are sometimes different versions of the program that display exactly the same "version" number at the start of the game.

Let us know if you find that interview.

Tommy

Apple2Games

unread,
Jun 16, 2014, 6:26:49 PM6/16/14
to

> Hope this helps!
>
>
>
> Tommy

Excellent! Now I can play on my phone :)

TommyGoog

unread,
Jun 17, 2014, 7:42:55 PM6/17/14
to
On Monday, June 16, 2014 11:15:38 AM UTC-5, TommyGoog wrote:
> On Saturday, June 14, 2014 9:54:55 PM UTC-5, Didier VALLET wrote:
>
> Today I uploaded the source code and listings to ASIMOV.
> /pub/apple_II/images/games/rpg/wizardry/wizardry_I/
> Tommy (Thomas W. Ewers)

Copy Protection

Now that I've completed the decompilation and uploaded the source code to ASIMOV, the real fun begins of examining the Wizardry1 code.

In this post, I will explain what I've found for the copy protection code.

The copy protection code is some of the most obfuscated code I've ever seen.

Wizardry1 copy protection is based on track synchronization between tracks $A, $B, $C, $D, and $E.

Normally when a track is initialized, each track is written independent of the others. There is no "lining up" of sector 0 on each track to make sure it is lined up with the other tracks. But with a copy protected disk, it is possible to write the tracks in such a way that the sectors on consecutive tracks are somewhat lined up. The software that is written can detect if the tracks have been written in this specific way. If you simply copy the tracks to one of your own formatted disks, your disk will not have the tracks and sectors lined up. The software can detect this difference and then halt the boot process on your "illegal" copy, but allow it to continue on the original disk.

Software emulators also have a problem with copy protected disks, because they (at least AppleWin) do not retain information about the relationship of sectors on one track with sectors on another track.

SYSTEM.STARTUP is the first code executed and it checks the copy protection on the BOOT side of the disk. Later WIZARDRY.CODE is executed, and it checks the copy protection on the SCENARIO side of the diskette. Later on the code checks if the copy protection code for the SCENARIO side was previously executed and if not, it halts execution.

Pull out your listing for SYSTEM.STARTUP and let's look at the mainline code. The first procedure executed is CHKCOPY. It is also executed before exiting SYSTEM.STARTUP (just before WIZARDRY.CODE is executed).

Before continuing to read the rest of this I would encourage you to try to understand what CHKCOPY is doing. Go ahead. Try it. I dare you. I double dare you. Come back here in an hour or two and then find out all the details.

First up is to read the serial block (SERIALBL) from the BOOT disk. This is Pascal block 5 (which coincidentally starts at sector 5 using DOS numbering).

Here is the sector from my real version of Wizardry1 that I purchased back around 1990 in a "box" set.

00- 06 33 39 31 30 39 30 00 F391090@
08- 00 00 00 01 88 00 00 00 @@@A.@@@
10- 00 00 00 00 00 00 00 00 @@@@@@@@
18- 00 00 00 00 00 00 00 16 @@@@@@@V
20- 12 00 00 00 F0 0C D4 0F R@@@pLTO
28- C7 09 64 16 47 09 47 05 GI$VGIGE
30- 64 16 47 05 F3 06 ED 07 $VGEsFmG
38- 0F 06 12 15 12 31 12 00 OFRUR1R@
40- E5 02 B9 08 12 4B 12 79 eB9HRKR9
48- C6 05 31 13 12 78 17 1B FE1SR8W[
50- 12 06 AB 08 EA 05 12 12 RF+HjERR
58- EE 02 EC 0C 00 00 1C 13 nBlL@@\S
60- 00 12 BE 05 1C 13 E8 05 @R>E\ShE
68- B5 05 00 00 16 09 00 00 5E@@VI@@
70- 00 00 00 00 00 00 00 00 @@@@@@@@
78- 00 00 00 00 00 00 00 00 @@@@@@@@
80- 00 00 00 00 00 00 00 00 @@@@@@@@
88- 00 00 00 00 00 00 00 00 @@@@@@@@
90- 00 00 00 00 00 00 00 00 @@@@@@@@
98- 00 00 00 00 00 00 00 00 @@@@@@@@
A0- 00 00 00 00 00 00 00 00 @@@@@@@@
A8- 00 00 00 00 00 00 00 00 @@@@@@@@
B0- 00 00 00 00 00 00 00 00 @@@@@@@@
B8- 00 00 00 00 00 00 00 00 @@@@@@@@
C0- 00 00 00 00 00 00 00 00 @@@@@@@@
C8- 00 00 00 00 00 00 00 00 @@@@@@@@
D0- 00 00 00 00 00 00 00 00 @@@@@@@@
D8- 00 00 00 00 00 00 00 00 @@@@@@@@
E0- 00 00 00 00 00 00 00 00 @@@@@@@@
E8- 00 00 00 00 00 00 00 00 @@@@@@@@
F0- 00 00 00 00 00 00 00 00 @@@@@@@@
F8- 00 00 00 00 00 00 00 00 @@@@@@@@

I used COPY2+ to read and "print" this sector to PRINTER.TXT.

This sector (and the next one) are read into IOCACHE. Note the serial number at the beginning, "391090" which is displayed when the program is booted.

CPTEMP is first set to 31. In the first FOR loop it is an index into IOCACHE for this block we just read.

The FOR loop retrieves 4 integer values (low end first) from IOCACHE and stores them at SYNCH[ 0], SYNCH[ 1], SYNCH[ 2], SYNCH[ 3].

Note the values displayed for the block have hexidecimal numbers. I will use decimal numbers here, and if needed will precede the hex number with a dollar sign ($). For example, at $1F (31) is the value $16 (22). And at $20 is $12. So $1216 is stored at SYNCH[ 0]. $1216 is 4630.

Note the next line of code changes CPTEMP based on the value that we just found at IOCACHE[31] and IOCACHE[32].

CPTEMP now changes to $28 (40). And the value found at IOCACHE[ 40], $09C7 is used to determine the next value for CPTEMP.

CPTEMP = 31
IOCACHE[ 31] = 22
IOCACHE[ 32] = 18
SYNCH[ 0] = 4630

CPTEMP = 40
IOCACHE[ 40] = 199
IOCACHE[ 41] = 9
SYNCH[ 1] = 2503

CPTEMP = 59
IOCACHE[ 59] = 21
IOCACHE[ 60] = 18
SYNCH[ 2] = 4629

CPTEMP = 66
IOCACHE[ 66] = 185
IOCACHE[ 67] = 8
SYNCH[ 3] = 2233

So for this disk, those lines of code could have been written as:

MOVELEFT( IOCACHE[ 31], SYNCH[ 0], 2);
MOVELEFT( IOCACHE[ 40], SYNCH[ 1], 2);
MOVELEFT( IOCACHE[ 59], SYNCH[ 2], 2);
MOVELEFT( IOCACHE[ 66], SYNCH[ 3], 2);

Next the code tries (up to 5 times) to see if it is a good (valid) disk. Each time through the inner loop it is going to read from one track and then read from the next track.

SAVEI goes from 10 to 13, so 8*SAVEI goes from 80 to 104. These are Pascal block numbers, so they refer to tracks $A, $B, $C, $D.

CHKSYNCH is called. I have heavily documented CHKSYNCH in the code.

; CALL: CHKSYNCH( VAR RESULT);

; PURPOSE: COPY PROTECTION BY CHECKING SYNCHRONIZATION
; BETWEEN TRACKS.

; 1. READ SECTOR 0 ADDRESS FIELD PROLOGUE ON CURRENT TRACK.
; 2. MOVE DISK ARM TO NEXT HIGHER NUMBERED TRACK.
; 3. COUNT NUMBER OF BYTES UNTIL SECTOR 0
; ADDRESS FIELD PROLOGUE ON THIS TRACK IS READ.
; 4. RETURN COUNT.

; SEE ALSO SCENARIO.DATA. SIMILAR COPY PROTECTION CODE
; RESIDES THERE TOO, AND IS USED IN WIZARDRY.CODE.

After CHKSYNCH returns we see a calculation for CPCALC.

It is likely that one disk drive can change from track to track faster than another one. Since it starts reading sooner on the next track it will read more bytes before it gets to sector 0. To solve that problem, a difference is calculated between the number that was read compared to the expected value. For each track synchronization this value is then subtracted from the bytes read to account for this faster speed. This value is stored at CPCALC. For all succeeding track synch determinations this value is subtracted from the observed count.

Each time we call CHKSYNCH the returned values must be within 35 of what we expect. If these 5 tracks are in alignment, then it is a good copy and we exit CHKCOPY.

You've probably guessed by now, but the values in SYNCH[ 0] are the number of bytes we expect to read after moving from track $A to track $B and looking for sector 0. SYNCH[ 1] is the count for track $B to $C, etc. For my disk they are:

[0] = 4630
[1] = 2503
[2] = 4629
[3] = 2233

Just for fun, I looked at the CHKSYNCH returned values while booting Wizardry1 on AppleWin. I found that it consistently returned the valued 4992, 3501, 3501, and 2004. So I thought I would change the values in the serial block and see if that would defeat this part of the copy protection. Well my first attempt was bad because I changed the values at offets 31, 40, 59, and 66 in the sector. It did not work. I had forgotten that the values of the synch bytes determines where to get the next synch byte in the block! I needed to change the values at: 31 (32), 37(38), 49(50), and 62(63). And it did defeat the copy protection for this part.

Still in SYSTEM.STARTUP, in the OPTIONS routine, just before displaying the copyright notice, there is a check for the BOOT disk to be write protected. If it is not write protected the code goes into an infinite loop.

Once you enter "S" for S)TART GAME, there is another call to CHKCOPY to ensure you still have the genuine boot disk in the drive.

The EXIT( WIZBOOT) takes us to WIZARDRY.CODE because of the SETCHAIN( 'WIZARDRY') in TITLELOA.

There is still more copy protection to come!

Now pull out your copy of the Wizardry1 mainline. It sets LLBASE04 to -1 and then calls SPECIALS. This tells SPECIALS to call INITGAME.

In INITGAME we test LLBASE04 to see if it is -1 (it could be -2 from a different path here). If it is -1, then we do some one time stuff.

We get the message:

SCENARIO MASTER IN DRV 1, PRESS [RET]

And the code finds SCENARIO.DATA on the disk. This part of copy protection tests the SCENARIO side of the game.

The UNITREAD loads code from SCENARIO.DATA to $2000 in the APPLE. The code starts at block 3 of SCENARIO.DATA, which for my game (all games?) starts at Track $13 Sector $0 following the MAGE and PRIEST spell names.

GTSERIAL is the next code executed in INITGAME. Once again we see the code moving 4 values from the SERIALBL to SYNCH[]. (It also moves the serial number to MASTSER.)

COPYPROT is the next code executed in INITGAME.

The inner FOR loop is reading from the SCENARIO diskette similar to SYSTEM.STARTUP reading from the BOOT side.

UNITREAD positions the disk read head over successive tracks ($A, $B, $C, $D) each time through the loop.

What the hell is this MVCURSOR( 60, 0) you might ask?

Oh, that doesn't really move the cursor like most calls in Wizardry. The MVCURSOR code detects the special value of 60 for the first parameter and decides to perform a JMP to $2002!

WOW!!!

The code at $2002 is nearly identical to the boot side CHKSYNCH code. It stores the synch value at $2000, which is where COPYPROT picks it up.

Note here the error of margin is 29, whereas on the boot side it was 35!? Go figure.

After the FOR loop we check the status and if bad, we RETRY up to 4 more times. If still bad, then we execute:

MVCURSOR( 70, 0).

Huh!?

MVCURSOR detects the special 70 and starts clearing memory from $BFFF down to as far as it can go. After that the system hangs.

Is that it? No!

One other thing that the code at $2002 does is to set $0479 in the APPLE to #$0A (10).

Huh!?

Well...for starters $0479 is RAM space reserved for SLOT #1. Seems questionable to do that, but let's continue any way.

In the CASTLE code CPCALLED is set to 1145 ($0479). The code then checks to see if it is 10, and if not calls MVCURSOR( 70, 0) like above, to CRASH AND BURN.

CPCALLED is also checked in DSPTITLE. DSPTITLE is called whenever you change "shops" (GILGAMESH, BOLTAC, etc.) while outside the maze.

What all that means is that you cannot just NOP out the call to the CHKSYNCH or COPYPROT code without also modifying the code that checks for $0479 = 10 !!!

Ok, so what does the crack at ASIMOV do? How does it get around this track synchronization? It has a hard-coded table in the $2002 (SCENARIO.DATA) code to return successive synch count values that work with the values in sector 5.

I hope you enjoyed reading this. It was a PITA to figure this out and then organize this into something somewhat coherent.

Comments and corrections encouraged.

Enjoy,
Tommy





Antoine Vignau

unread,
Jun 18, 2014, 1:45:33 AM6/18/14
to
Tommy,
Your message justifies why I request a version with no copy protection (but not a cracked version)...
av

geoff body

unread,
Jun 18, 2014, 3:40:59 AM6/18/14
to
Tommy,
I have really enjoyed your postings

GeoffB

sicklittlemonkey

unread,
Jun 19, 2014, 9:30:45 PM6/19/14
to
On Wednesday, 18 June 2014 11:42:55 UTC+12, TommyGoog wrote:
> I hope you enjoyed reading this. It was a PITA to figure this out and then organize this into something somewhat coherent.

A great read! Nice to understand why it was hard to copy 30 years ago. ; - )

Cheers,
Nick.

TommyGoog

unread,
Jun 21, 2014, 6:00:39 PM6/21/14
to
On Tuesday, June 17, 2014 6:42:55 PM UTC-5, TommyGoog wrote:
> On Monday, June 16, 2014 11:15:38 AM UTC-5, TommyGoog wrote:
>
> > Today I uploaded the source code and listings to ASIMOV.
>
> > /pub/apple_II/images/games/rpg/wizardry/wizardry_I/
>
> > Tommy (Thomas W. Ewers)
>


A nefarious Pascal bug!
I am dilegently working on understanding the data in SCENARIO.DATA and the structures used in Wizardry1. I did not catch this strange code when I decompiled Wizardry3, but now I see it is in Wizardry1 too.

Here is the line of code in the procedure HITGROUP:

BATTLERC[ 0].B.WEPVSTY3 := CHARACTR[ CHARI].WEPVSTY3[ 1];

BATTLERC is an array of records with 2 sub-records. The "B" sub-record has a field called WEPVSTY3 defined as:

WEPVSTY3 : PACKED ARRAY[ 0..15] OF BOOLEAN;

CHARACTR is an array of records with WEPVSTY3 defined as a TWO dimensioned array:

WEPVSTY3 : PACKED ARRAY[ 0..1, 0..6] OF BOOLEAN;

Several questions arise:

Is this valid Pascal? (I don't think so.)
If not, why didn't the compiler complain? (Bug in compiler?)
What happens at execution time? (Not predictable?)

I wrote a small test program. You may want to play "predict the results" before looking at the output which I include further down in this posting.

2 1 1:D 1 (*$L PRINTER: *)
3 1 1:D 1
4 1 1:D 1 PROGRAM TESTARR;
5 1 1:D 3
6 1 1:D 3 VAR
7 1 1:D 3 ONE : PACKED ARRAY[ 0..15] OF BOOLEAN;
8 1 1:D 4 TWO : PACKED ARRAY[ 0..1, 0..6] OF BOOLEAN;
9 1 1:D 6
10 1 1:D 6 J, W, X, Y : INTEGER;
11 1 1:D 10
12 1 1:0 0 BEGIN
13 1 1:0 0
14 1 1:1 0 FOR J := 0 TO 6 DO
15 1 1:1 13
16 1 1:2 13 BEGIN
17 1 1:3 13 FOR X := 0 TO 15 DO
18 1 1:4 24 ONE[ X] := FALSE;
19 1 1:4 42
20 1 1:3 42 FOR X := 0 TO 1 DO
21 1 1:4 53 FOR Y := 0 TO 6 DO
22 1 1:5 64 TWO[ X][ Y] := FALSE;
23 1 1:5 95
24 1 1:3 95 TWO[ 1][ J] := TRUE;
25 1 1:3 112 ONE := TWO[ 1];
26 1 1:3 124
27 1 1:3 124 FOR W := 0 TO 15 DO
28 1 1:4 135 WRITE( ORD( ONE[ W]), ' ');
29 1 1:3 171 WRITELN
30 1 1:3 179
31 1 1:2 179 END;
32 1 1:0 186 END.

The APPLE PASCAL LANGUAGE manual on p. 16 explains why the single dimensioned array here occupies one WORD (16 bits) of memory, while the two dimensioned array occupies two WORDS (32 bits) of memory.

There is a 1 dimension array (ONE) and a 2 dimension array (TWO). Both are PACKED.

The outer loop (J) executes 7 times, and each time through one line of text is displayed.

Each time through the loop all elements of both arrays are set to FALSE.

Then we set one element in the 2 dimensioned array (TWO) to TRUE.

Next we transfer 1 dimension of the 2 dimensioned array to the 1 dimensioned array. Really? You can do this in Pascal?

Then we write out each value we find in the ENTIRE 1 dimensioned array (ONE).

(Note: We need to use the ORD() function because this is BOOLEAN. I rewrote this using a range 0..1 instead of BOOLEAN and achieved the same results.)

Here is the output:

1 0 0 0 0 0 0 0 1 0 1 0 1 0 0 1
0 1 0 0 0 0 0 0 1 0 1 0 1 0 0 1
0 0 1 0 0 0 0 0 1 0 1 0 1 0 0 1
0 0 0 1 0 0 0 0 1 0 1 0 1 0 0 1
0 0 0 0 1 0 0 0 1 0 1 0 1 0 0 1
0 0 0 0 0 1 0 0 1 0 1 0 1 0 0 1
0 0 0 0 0 0 1 0 1 0 1 0 1 0 0 1

No surprises for ONE[ 0] to ONE[ 6].

But look at ONE[ 7] to ONE[ 15]!

They done been klobbered.

Enjoy!
Tommy

sicklittlemonkey

unread,
Jun 22, 2014, 7:46:34 AM6/22/14
to
On Sunday, 22 June 2014 10:00:39 UTC+12, TommyGoog wrote:
> A nefarious Pascal bug!

Does it actually affect the games?

BTW, in Google Groups this thread has 1050 "views" (can't find what that means exactly) - way more than any other thread since an "Unhappy" one back in April.

Cheers,
Nick.

Steve Nickolas

unread,
Jun 22, 2014, 7:53:59 AM6/22/14
to
Can't imagine it's very accurate given how many of us use real newsreaders
or even hybrid e-mail clients like Alpine and Thunderbird to read csa2 :P

-uso.

Tempest

unread,
Jun 27, 2014, 11:49:21 AM6/27/14
to
I've run across an article that mentions mouse support in one of the later revisions of Wizardry (from around 84). Has anyone found evidence of this in the code?

TommyGoog

unread,
Jun 27, 2014, 5:40:39 PM6/27/14
to
On Sunday, June 22, 2014 6:46:34 AM UTC-5, sicklittlemonkey wrote:
> On Sunday, 22 June 2014 10:00:39 UTC+12, TommyGoog wrote:
>
> > A nefarious Pascal bug!

>
> Does it actually affect the games?
>
> Nick.

Sorry for not answering this earlier, but I was hoping someone else would come in here and try to answer this question after they looked at the code!?

As it turns out, I think this bug does no actual harm in the program, but these constructs seem to waste a bit of disk space and run-time memory space and some execution time. The upper 7 elements of the single dimension array are not really interogated in the code -- they only hold a copy of the lower 7 elements.

But I will share with you a few bugs that I have found that DO impact the game.


Bug1:
-----

Last weekend I finished retrieving the hi-res "pictures" from SCENARIO.DATA for the monsters and enemies you see in the game. These are displayed in the upper left corner of the screen while playing. I then went to the first level of the game and encountered one of the earliest "bad guys": the BUBBLY SLIME. Unfortunately, the hi-res part of the screen was somewhat mangled where the picture of the SLIME was partially displayed.

I thought, "How could my disk image have gotten corrupted?" Did I insert this diskette at the wrong time when doing something else? Did I mix and match my Scenario version with the other Boot version in the game? Was my diskette not formatted correctly to start with? Was there a bug in the software trying to display the image? Eventually I looked at the SCENARIO.DATA file on the SCENARIO side of the disk I started with. On that diskette the image was intact and correct, but on the "DUPLICATE/SCENARIO" disk I had made, the first block (1 Pascal block = 2 DOS sectors) were clearly just garbage.

I then set out to find the bug, I started from scratch and initialized a diskette and then used the Wizardry "Utilities" to make my scenario diskette.

Sidetrack: Versions 2 and 3 of Wizardry clearly state you must create your own DUPLICATE/SCENARIO diskette to play the game. The instructions for Wizardry I are not very clear and make it sound like you should just use the "MASTER/SCENARIO" to play the game. Since playing the game has some destructive effects on the data on your scenario diskette, if you go that route and solve the game you will never be able to start over and play the entire game. Good for sales, bad for players (and their brothers and sisters).

Ok, so all I did was create the DUPLICATE/SCENARIO and used my DUMPDATA program to extract the image from SCENARIO.DATA and display the image on the Apple screen. And it was already corrupt! I did not need to even start playing the game!

I realized the "make a DUPLICATE/SCENARIO" code must be broken.

In WIZBOOT (that's the name for WizUtil.code), PROCEDURE MAKESCEN() has a bug:

CHARREC0.STATUS := LOST;
FOR TEMPI := 0 TO SCNTOC.RECPERDK[ ZCHAR] DO
WRCHARAC( CHARREC0, TEMPI);

This code occurs right after 35 tracks of data have been copied from the MASTER/SCENARIO to your DUPLICATE/SCENARIO. It is clearing out any CHARACTRs that might have been on the MASTER/SCENARIO disk.

RECPERDK is "RECords of this type PER DisKette". Note TEMPI starts at 0. There is room on the diskette for 20 CHARACTR (ZCHAR) characters, and RECPERDK[ ZCHAR] is 20. Yup! Another off-by-one error. It is writing 1 block too many and clobbering the first hi-res picture image.

Another interesting twist to this bug. If you use only the MASTER/SCENARIO and encounter SLIME (and there are a few more creatures that use this same picture) you see the actual image the way it should be. It is only when you make your DUPLICATE/SCENARIO that you see this error.


Bug2:
-----

I have this comment in MVCURSOR() code:

; MVCURSOR( 80, Y) ADJUST RANDOM # (UNTIL KEY IS AVAILABLE)

In GETKEY() in the main WIZARDRY() program is this:

MVCURSOR( 80, 0); (* ADJUST RANDOM #, AND RETURN WHEN A CHAR IS AVAIL *)

This is a way for the program to help "randomize" the seed for the next time the RANDOM number generator is called.

(As mentioned in an earlier post, there's a bit of strange code associated with MVCURSOR. Sometimes it doesn't do "cursor" stuff.)

Here's the code in MVCURSOR( 80, 0):

L5931
LDX #00
L5933
INC 0047A,X ; INCREMENT RANDOM #
BNE L593D ;
INX ;
CPX #04 ;
BNE L5933 ;
L593D
LDA 0C000 ; KEYBOARD DATA

Here is a partial lising for the table in the APPLE II Reference manual, p. 134:

Table 26: I/O Scratchpad RAM Addresses
Base Address 1 2 3 4 5 6 7
$0478 $0479 $047A $047B $047C $047D $047E $047F
$04F8 $04F9 $04FA ...
$0578 $0579 $057A ...
$05F8 $05F9 $05FA ...
$0678 $0679 $067A ...
$06F8 $06F9 $06FA ...
$0778 $0779 $077A ...
$07F8 $07F9 $07FA ...

Here's some code from the RNG:

LDY #07 ; SHIFT SOME BITS 7 TIMES
L5CCC
ASL 0047A
PHP
ROL 004FA
ROL 0057A
ROL 005FB
BMI L5CE0

It's not the most kosher thing in the world to just use these slot memory locations for your own purposes!

But there is something even worse going on here. This code in MVCURSOR wants to increment the first 4 memory locations listed under slot #2 as found in the table on p 134. In reality it is incrementing the first memory location for slot#2, slot #3, slot #4, slot #5.

Yikes!

Not to mention that only 1 of the 4 bytes used in the Random Number Generator are being altered by this code. Therefore, the RNG they thought they were implementing is not what they wrote! I'm not sure how this affects the numbers generated but it is clearly not a good thing. Maybe altering one byte while using keyboard input makes this RNG "good enough", but further testing might prove otherwise. One other thing, did you notice that the RNG is accessing the first 3 bytes of slot#2 and 1 byte of slot#3?


Bug3:
-----

While playing the game the other day (oops, I meant to write "testing the game"), I had a BISHOP identify an object. No, this is not the famous "identify item #9" bug. I noticed the object had a "-1" in the name. As I found by examining SCENARIO.DATA, the CURSED field for OBJECTS is set for every item that has a negative number in the name. In this instance I did not need to EQUIP the item. My BISHOP took the item to BOLTACs trading post and was able to sell it. But there is code that usually produces this message when you try to sell a cursed item:

"** WE DON'T BUY CURSED ITEMS **"

The code in IDITEM() has a bug where the "CURSED" field is not always set after an item has been identified by a BISHOP.

Although they fixed another bug in this code for WizardyIII (LOL), I think this bug is there also.


Bug4:
-----

What are the possible results for HAMAN and MAHAMAN?

Here is the code with a very bad bug. See if you can spot it.

CASE RANDOM MOD 3 * MAHAMFLG OF (* MAHAMFLG IS 6 OR 7 *)
0, 1, 2, 3, 4, 5: HAMCURE;
7, 8, 9, 10, 11: HAMSILEN;
12, 13, 22, 23: HAMMAGIC;
14, 20, 21: HAMTELEP;
6, 15, 19: HAMHEAL;
17: HAMPROT;
16, 18: HAMALIVE;

Hint: If you search the internet, you will not find all 7 of these actions for HAMAN and MAHAMAN.

These are just a few of the bugs in the code.

At some point in the future, I may "release" a version of Wizardry I (Proving Grounds) that does not have these and many more bugs.

If you find a bug in the code, please let me know.

Enjoy!
Tommy

sicklittlemonkey

unread,
Jun 27, 2014, 7:22:28 PM6/27/14
to
On Saturday, 28 June 2014 09:40:39 UTC+12, TommyGoog wrote:
> Sorry for not answering this earlier, but I was hoping someone else would come in here and try to answer this question after they looked at the code!?

I wish I had time to look more closely at what you've done - it's awesome reading your posts, and I own Wizardry I - whose scenario disk I now harbour grave concerns for! ; - ) But I have my own retro-project to (re)engineer. (Would be easier without a job or family ...)

> At some point in the future, I may "release" a version of Wizardry I (Proving Grounds) that does not have these and many more bugs.

The prospect is tantalizing. Version 2.2? ; - )

Cheers,
Nick.

TommyGoog

unread,
Jun 28, 2014, 3:11:10 AM6/28/14
to
On Friday, June 27, 2014 10:49:21 AM UTC-5, Tempest wrote:
> I've run across an article that mentions mouse support in one of the later revisions of Wizardry (from around 84). Has anyone found evidence of this in the code?

There is no mouse support in Wizardry I (Proving Grounds) or Wizardry III (Legacy of Llylgamyn) for the Apple versions.

Tommy

TommyGoog

unread,
Jun 28, 2014, 3:38:20 AM6/28/14
to
On Friday, June 27, 2014 4:40:39 PM UTC-5, TommyGoog wrote:
> On Sunday, June 22, 2014 6:46:34 AM UTC-5, sicklittlemonkey wrote:
>
> > On Sunday, 22 June 2014 10:00:39 UTC+12, TommyGoog wrote:
>
> Tommy

I've uploaded lots of data from Scenario.Data, in a more readable format, to Google Groups spreadsheets. Please let me know if the following link gets you to the information for Wizardry I (Proving Grounds):

https://docs.google.com/spreadsheet/ccc?key=0ApO5bjpT_F-OdDVnTEpXWExGTXUxb21UcmQ1bXdCdEE&usp=sharing

I've created the following spreadsheets there:

1. Scenario.Data TOC
2. One spreadsheet for each level of the maze:
Level 1
Level 2
Level 3
Level 4
Level 5
Level 6
Level 7
Level 8
Level 9
Level 10
3. Enemies
4. Objects
5. Rewards
6. Enemy Pics
7. Experience Levels
8. Scenario.Mesgs

If you find errors in my uploading or formatting, please let me know.

In upcoming postings I will likely be referencing these spreadsheets. If there is a better place to host this information, let me know.

Spreadsheets are nice for displaying this type of information because of their powerful "filters" functionality. For example, if you want to see all the weapons that a Fighter can use, you go to the OBJECTS spreadsheet, then select "0" under OBJTYP (0 is for weapons, 1 is for armor, etc.), and select "1" under CLASSUSE-FIGHTER (1 is for TRUE, 0 is for FALSE).

I just thought of something. I'm not sure if others can change the settings for these "filters" while viewing these spreadsheets. If you cannot (because I have the sheets protected), you might want to copy these spreadsheets to your own computer or your own Google groups spreadsheets.

In the Enemy Pics spreadsheet I have the "picture" images of the enemies you do combat with. I've also included the "corrupted" image of the BUBBLY SLIME in those images as described in an earlier posting. The more I looked at the corrupted image it finally dawned on my what it reminds me of -- an old CRT type television tuned to a station that isn't there -- an effect sometimes referred to as "snow". (Does that last sentence age me!?)

Let me know if you have any problems accessing the spreadsheets.

Enjoy,
Tommy

sicklittlemonkey

unread,
Jun 28, 2014, 8:32:00 AM6/28/14
to
On Saturday, 28 June 2014 19:38:20 UTC+12, TommyGoog wrote:
> Let me know if you have any problems accessing the spreadsheets.

No problems accessing it. What a great resource!

It can be downloaded or copied (File->Make a copy) to filter etc.

Interestingly, when downloaded the images were included in the Excel (xlsx) format but not the OpenDocument (ods), even though the xlsx file can be opened fine by OpenOffice Calc.

Cheers,
Nick.

Tempest

unread,
Jun 28, 2014, 9:24:19 AM6/28/14
to
Did you check all the versions? The article mentioned this was added sometime around 84. I don't know how many versions are floating around out there.

qkumba

unread,
Jun 29, 2014, 12:10:56 AM6/29/14
to
> Here is the code with a very bad bug. See if you can spot it.
> CASE RANDOM MOD 3 * MAHAMFLG OF (* MAHAMFLG IS 6 OR 7 *)
> 0, 1, 2, 3, 4, 5: HAMCURE;
> 7, 8, 9, 10, 11: HAMSILEN;
> 12, 13, 22, 23: HAMMAGIC;
> 14, 20, 21: HAMTELEP;
> 6, 15, 19: HAMHEAL;
> 17: HAMPROT;
> 16, 18: HAMALIVE;
> Hint: If you search the internet, you will not find all 7 of these actions for HAMAN and MAHAMAN.

It appears that no values larger than 14 can be achieved, thus dropping the last two actions entirely.

TommyGoog

unread,
Jun 29, 2014, 4:03:58 PM6/29/14
to
Yes. And to be more specific, the case expression can only evaluate to the values 0, 6, 7, 12, and 14. It is likely that they really wanted something like:

CASE RANDOM MOD (3 * MAHAMFLG) OF (* MAHAMFLG IS 6 OR 8 *)

Note the two changes: the parenthesis and the MAHAMFLG values. It appears they wanted to have the results weighted in favor of the action HAMCURE for example. Completely butchered code for the lack of the parenthesis.

TommyGoog

unread,
Jul 1, 2014, 10:36:27 PM7/1/14
to
On Saturday, June 28, 2014 2:38:20 AM UTC-5, TommyGoog wrote:
> I've uploaded lots of data from Scenario.Data, in a more readable format, to Google Groups spreadsheets.
> https://docs.google.com/spreadsheet/ccc?key=0ApO5bjpT_F-OdDVnTEpXWExGTXUxb21UcmQ1bXdCdEE&usp=sharing
> Tommy

Time to describe the contents of the spreadsheets I uploaded earlier. First up is "Scenario.Data TOC".

Whenever the program Wizardry needs information about the MAZE, or ENEMYs, or REWARDs, or our CHARACTRs, it gets that information from SCENARIO.DATA.

The first block of the file is what I call the Table of Contents (TOC). A Pascal block is 2 sectors, so this is 512 bytes of data. For a good description of Pascal blocks, see p. 25 of "Apple Pascal Operating System Reference Manual". TSCNTOC is the record that describes the contents of the file and contains a few other pieces of data for the game. The Pascal file structure shows that SCENARIO.DATA begins at block 149 on the disk.

The TOC starts with the name of the game, "PROVING GROUNDS OF THE MAD OVERLORD!". Following that is a table of 4 arrays that describe the other 7 major parts of the file:

MAZE
ENEMY
REWARD
OBJECT
CHARACTERS
ENEMYPICS
EXPERIENCE

After the 4 arrays there is "string" data used by Wizardry, namely, RACE, CLASS, STATUS, ALIGN.

Next is some descriptive information about the spells used in the game. There is a list of "hash" values (SPELLHSH) for identifying each of the spells. After that is a table (SPELLGRP) to show which group each spell is in. There are 7 groups of Mage spells and 7 groups of Priest spells. Following that is a table (SPELL012) that shows for each spell whether it is a "generic" spell, or one that affects 1 character (or enemy), or affects a "group" of characters.

So far all I've described is the contents of block 0 of the file.

I'm not sure what is in blocks 1 or 2 of this file -- they don't seem to be used. Block 3 contains the copy protection code for this side of the diskette. Following that are the 2 blocks containing the names of the Mage and Priest spells.

In the spreadsheet I have taken the 4 arrays following the GAMENAME and arranged them in a table. This table is used by GETREC() and GETRECW() in Wizardry code to retrieve records from the file. RECPER2B is "Records per 2 Blocks". RECPERDK is "Records per disk". UNUSED XX or "(size)" describes the size in blocks of each section. BLOFF is "Block offset" to describe where the beginning of the major section starts relative to the beginning of SCENARIO.DATA.

So let's say you want to know information about object #33 which is "DRAGON SLAYER".

RECPER2B RECPERDK (size) BLOFF
OBJECT 13 101 16 68

Start by calculating where the OBJECT records are in the file.

BLOCK #
149 SCENARIO.DATA
68 BLOFF for OBJECTs
217 Beginning of OBJECT records on the disk (Track $1B, Sector $0D)

There are 101 OBJECTs (including "BROKEN ITEM") so 33 is a valid index.

GETREC() always reads 2 blocks of data into the IOCACHE array and no record is "split" between these double block boundaries.

Objects Disk Block

00 to 12 217 Block 0
218 Block 1

13 to 25 219 Block 2
220 Block 3

26 to 38 221 Block 4
222 Block 5

39 to 51 223 Block 6
224 Block 7

Since 33 is in Block 5, GETREC() reads both Block 4 and Block 5 into IOCACHE.

It then calculates the index into IOCACHE for the OBJECT record #33 as follows:

SIZEOF( TOBJECT) * (33 MOD 13)

33 is the OBJECT index, and 13 is the RECPER2B.

The cool part about this design is that if you are now interested in object #34, you don't need to do another I/O. You can simply calculate the index into IOCACHE and there it is.

The only time you need to write information back to the disk is when the IOCACHE needs to read a different 2 blocks from the file. Then the CACHEWRI flag is interrogated to see if the current IOCACHE has changed, and if it has, then that WRITE I/O occurs first.

The rest of the spreadsheet SCENARIO.DATA.TOC shows the contents of the TOC and blocks 4 and 5 of SCENARIO.DATA.

Enjoy,
Tommy

Antoine Vignau

unread,
Jul 2, 2014, 2:45:38 AM7/2/14
to
Did you share your study with the authors of Wizardry?
av

TommyGoog

unread,
Jul 2, 2014, 10:59:27 AM7/2/14
to
That's a good question!

I have not tried to contact the original authors, or the company Sir-Tech, or whomever might currently own any copyrights to the products. I do not know how to contact them, other than by posting in one of the most viewed Apple II newsgroups. If anyone from Sir-Tech involved with Wizardry I or Wizardry III are reading this forum I'd love for them to chime in here!

I know about 2 years ago I tried to discover a little bit about Sir-Tech, and I was left with the impression that it was no longer a viable company. There was information about it being sold and moved to Canada and there were a number of legal issues and lawsuits into the late 1990s or early 2000s.

I would think anyone reading these forums who knows Andrew Greenberg or Robert Woodhead would probably have already pointed them to these forums, and if so we would have already heard from them, but I could be mistaken.

Thirty to thirty-five years was a long time ago. If they are still alive they would be in their 50s or very early 60s I would assume.

It would be interesting to hear from them.

Tommy

sicklittlemonkey

unread,
Jul 2, 2014, 4:48:01 PM7/2/14
to
On Thursday, 3 July 2014 02:59:27 UTC+12, TommyGoog wrote:
> I know about 2 years ago I tried to discover a little bit about Sir-Tech, and I was left with the impression that it was no longer a viable company. There was information about it being sold and moved to Canada and there were a number of legal issues and lawsuits into the late 1990s or early 2000s.

I've previously looked for stuff about these guys too.

It seems Andrew Greenberg was still fighting Sir-Tech in the courts as late as 2010:
http://www.courts.state.ny.us/Reporter/3dseries/2010/2010_09323.htm

This article gives an overview of the story:
http://venturebeat.com/2013/05/03/wizardrys-wild-ride-from-west-to-east/

At one point I found his personal website where he talks about becoming an IP lawyer to prevent happening to others what happened to him with Sir-Tech.

> It would be interesting to hear from them.

Robert Woodhead wrote all the code in Wizardry I - IV ...

RPG Codex Retrospective Interview: Robert Woodhead on Wizardry
http://www.rpgcodex.net/content.php?id=8129

More detailed interview here:
http://www.gameskinny.com/acpk9/an-interview-with-robert-woodhead-creator-of-the-genre-defining-rpg-wizardry

He says:
"I do get occasional emails from people about the game, which is nice."

But he also says:
"When I left Sir-tech, that part of my life was over -- it was time to move on to other things."

He moved to Japan and got married. (I can relate to that, as I did that too. ; - )

It sounds like they got ripped off. Sir-Tech sound like scumbags.

Cheers,
Nick.

TommyGoog

unread,
Jul 5, 2014, 5:18:37 PM7/5/14
to
On Tuesday, July 1, 2014 9:36:27 PM UTC-5, TommyGoog wrote:
> On Saturday, June 28, 2014 2:38:20 AM UTC-5, TommyGoog wrote:
>
> > I've uploaded lots of data from Scenario.Data, in a more readable format, to Google Groups spreadsheets.
>
> > https://docs.google.com/spreadsheet/ccc?key=0ApO5bjpT_F-OdDVnTEpXWExGTXUxb21UcmQ1bXdCdEE&usp=sharing

> Time to describe the contents of the spreadsheets I uploaded earlier. First up is "Scenario.Data TOC".
>
> Tommy

Next up are the 10 "Maze Level" spreadsheets, with each describing a separate level of the MAZE (see record TMAZE in the code).

Each spreadsheet starts with an APPLE II hi-res display of a map for that level. It's crude, but shows the walls, openings, doorways, and hidden doorways of the 20 x 20 level. The lower left corner of the maze is 0,0.

Each little "square" or location on the map has its own discription for what is seen looking WEST, SOUTH, EAST, and NORTH. So with this design, if you are at 0,0 looking EAST you might see an open space, but if you are at 1,0 looking WEST you might see a WALL. The vertex between 0,0 and 1,0 consists of 2 separate WALL/OPENING descriptions. An example on level 1 is at 5,0, where if you are facing NORTH you see a WALL, but if you are at 5,1 facing SOUTH you see a doorway.

It seems a bit odd to me that each of the WEST facing values are in one array, then all the EAST facing values are in the next array, etc., but that is the way it was designed.

So a TMAZE record describes all the walls, doors, openings, and hidden doorways on the level in the first 4 arrays (W, S, E, N).

Next is the FIGHTS array. Each time you enter a new level of the maze, the FIGHTS array is used to build the FIGHTMAP array. There are several ways that the code determines how and when FIGHTS or COMBAT occur. One way is to look at the FIGHTMAP array. In general a "room" will have all the squares set to TRUE or all set to FALSE. If the square you land on has FIGHTMAP[ x, y] TRUE, then calculations are made to see if a MELEE should occur and the type and number of enemy to fight.

The algorithm for choosing the rooms for fights is as follows: 1. Randomly choose a square on this level of the maze. 2. If it has TRUE set in FIGHTS, and we haven't set FIGHTMAP to TRUE for that square, then we have our room! Fill all of that room. If we already have TRUE in FIGHTMAP, then start looking to the right of that room. If we get to the right edge of the maze level, drop to the next lower line and continue until we find a TRUE in FIGHTS and a FALSE in FIGHTMAP. For level 1 of Wiz1, this would tend to usually choose 2 or 3 of the first rooms you encounter near the lower left edge of this maze. In total, 9 rooms are chosen using this algorithm.

Also 1% of time you go Left, Right, Forward, etc., there is an encounter.

And, if you "K"ick and FIGHTS[ x, y] is set, there is a 1 out of 8 chance for an encounter, even if there already had been an encounter in that room earlier.

Once a fight has concluded, the FIGHTMAP array for that room is set FALSE. A "room" is defined as any square you can get to without having to pass through a wall, a door or a hidden door. It might actually look like a hallway or a large open area rather than a small "room".

For example, on level 1 you can see FIGHTS[ 6, 1], [6,2], [7, 1], [7, 2], [8, 1], and [8,2] are all set TRUE. This is the small room that you enter after moving EAST of the first stairways at 0,0.

There are other ways for fights to occur, so just because there is a FALSE in the FIGHTS array does not mean that a fight won't occur on that location.

The next array for this level of the MAZE is SQREXTRA. This array describes the EXTRA attributes for particular squares on this level. Each value in this array is an index into the arrays that follow (SQRETYPE, AUX0, AUX1, AUX2). This index is just a value from 0 to 15.

Each level has a completely separate set of arrays for these "extra" possibilities. Once you get the index into SQRETYPE, it defines the "extra" stuff. SQRETYPE is defined as TSQUARE:

NORMAL, STAIRS, PIT, CHUTE, SPINNER, DARK, TRANSFER, OUCHY,
BUTTONZ, ROCKWATE, FIZZLE, SCNMSG, ENCOUNTE

It is possible for one level on the maze to have 2 sets of stairways, while another might have 3 or none.

Let's look at an example. At 0,0 in SQREXTRA is the value 1. This is index 1 for SQRTYPE which has TYPE set to STAIRS. You can now look at the AUX0, 1, and 2 values. They are 0, 0, and 0. Therefore, these stairs take you to level 0, square (0, 0). This means you are back in the castle because we are currently looking at level 1 (or minus 1 since we are descending down into the maze.)

The values in AUX0, AUX1, and AUX2 are dependent on the TYPE of square you are looking at. They mean something different for each TYPE. They really are auxillary information.

Some of the SQRETYPE entries might not be used. For example, those above index 10 in maze level 1.

==========

Here are the TSQUARE types:

0. NORMAL - means nothing unusual happens here.

1. STAIRS - can lead you upwards or downwards (or even sideways!?) and can be more than 1 level. AUX0 is level, to location (AUX2, AUX1). Note, there don't necessarily need to be stairs leading back to this location once you take the stairs.

2. PIT - You fell into a pit and some (or none, or all) of your characters are injured. AUX0, AUX1, and AUX2 are used to determine how much damage is done.

For each character in your party that is not DEAD, if his AGILITY < (RANDOM MOD 25) + MAZELEV, he gets hurt! Examples:

MAZELEV = 3 and AGILITY = 15, about 50% chance.
MAZELEV = 3 and AGILITY = 18, about 40% chance
MAZELEV = 7 and AGILITY = 12, about 80% chance.

Amount of damage is calculated by rolling an AUX1 sided die (as in dice) AUX2 times and adding to AUX0.

In Wiz1, all AUX0 values were 0. Therefore the MIN, AVE, and MAX can be calculated as:

MIN: AUX2.
MAX: AUX1 * AUX2
AVE: AUX2 * (AUX1 + 1) / 2

Wiz1 has these PITS:

MazeLevel AUX0 AUX1 AUX2 MIN MAX AVE
--------- ---- ---- ---- --- --- ---
2 0 8 2 2 16 9
3 0 8 3 3 24 14
5 0 8 5 5 40 22
6 0 8 6 6 48 27
7 0 8 7 7 56 32


3. CHUTE - like stairs but you have no choice -- you end up at another location in the maze. There is only 1 CHUTE in Wiz1. It uses AUX0 for level, and you transport to (AUX2, AUX1).

4. SPINNER - you land on this square and it randomly points you in 1 of 4 directions. AUX variables have no meaning

5. DARK - You cannot see the maze walls or doorways. AUX variables have no meaning.

6. TRANSFER - You enter this square and it silently moves you to another square. Devilish especially with the mechanisms of DRAWMAZE where you don't realize it at first. You end up at AUX0, location (AUX2, AUX1).


7. OUCHY - Not used in Wiz1, but it is the same as falling into a PIT.

8. BUTTONZ - The elevator and its buttons.

The elevator buttons in Wiz1 are "A-D" and "A-F", for levels 1-4 and 4-9 respectively. AUX2 is the level that button "A" takes you to. AUX1 represents the highest button letter in the elevator. So the values for AUX1 and AUX2 are 1 and 4, and 4 and 9 for the two elevators in Wiz1.

If AUX0 is non-zero, the elevator takes you to a RANDOM position on whatever level you are going to, but for Wiz1 AUX0 is always 0.

9. ROCKWATE - This changed from Wiz1 to Wiz3. In Wiz3 if you are in ROCK or if you are in WATER without the SHIP-IN-BOTTLE, all your characters die. In Wiz1, this special square takes you back to level AUX0, at (AUX2, AUX1).

10. FIZZLE - Your spells no longer work. AUX variables have no meaning.

11. SCNMSG - A special message from SCENARIO.MESGS.

AUX1 is always the line number in SCENARIO.MESGS for the first line of the message to display.

The values for AUX0 and AUX2 have different meanings depending on their values.

With AUX0 = -1 and AUX2 = 1, a "normal" message is displayed with no further action. For example, the message "A LARGE SIGN ON THE WALL READS: ..."

With AUX0 = -1 and AUX2 = 8, the special message is displayed, and then you are transported back to the CASTLE. This occurs in Wiz1 on Level 1 at (9, 12).

With AUX0 = 0 and AUX2 = 1, 4, or 8, then nothing is displayed. This is why the "Barbarian" message is not displayed on level 6, or the "GETTING HOT" messages on level 7.

With AUX0 > 0 and AUX2 = 4, the AUX0 value represents an ENEMY index. The message has "SEARCH Y/N" appended to it, and if you anwer "Y" you get to combat the enemy. For example, MURHPHY'S GHOST on level 1.

With AUX0 < -1000 and AUX2 = 4, the AUX0 value represents an OBJECT index. The actual index is calculated by adding 1000 and then taking the ABSolute value. The message has "SEARCH Y/N" appended to it. And if "Y" is entered, the object is given to a character. For example, finding the BRONZE KEY on level 1 at (13, 3).

With AUX0 > 0 and AUX2 = 2, this is similar to AUX0 < -1000 and AUX2 = 4, but you don't have the option to "SEARCH Y/N". The message is displayed and the OBJECT (AUX0 is the index) is given to a character who can hold it, and who doesn't already have one. In Wiz1, this is used for the BLUE RIBBON on level 4.

With AUX0 > 0 and AUX2 = 5, AUX0 represents an OBJECT that one of your characters must possess. If you don't have it, you "bounce back" to the location you were at before this square.

With AUX0 > 0 and AUX2 = 9, AUX0 represents an area. 2 * AUX0 + 1 are the dimensions of a square where your current location is the center. The code sets the FIGHTMAP at all those locations within this square to TRUE and sets your current location to FALSE. In essense, the next step you take, N, S, E, W, will result in COMBAT. This is used on level 4 with the "CLANGING OF BELLS" message. See LOOKOUT() code in Wizardry.

12. ENCOUNTE - A special encounter with a special enemy combatant.

AUX0 - if this is > 0 then this counter is decremented each time you reach this square in the maze, and when it reaches 0, SQRETYPE[ SQTYPE] is set to NORMAL.

AUX1 - if this is > 1 then a RANDOM number from 0 to AUX1 is added to AUX2 to determine ENEMYINX.

AUX2 - Lower bounds of ENEMYINX (see AUX1). So AUX2 to AUX2+AUX1 is the range of enemy you will encounter.

The MAZE also needs to have FIGHTS[] set to TRUE for this ENCOUNTE to work. That is why on Wiz1 you don't see the FIRE DRAGON on level 7 even though everything else is set up for it.

Besides the bad attempt for FIRE DRAGON on level 7, this mechanism with AUX0 > 0 occurs in 1 place in Wiz1: Level 3, (13, 17). The first 5 times you land here, you will encounter one of the following:

LVL 5 MAGE
LVL 4 THIEF
KILLER WOLF
SPIRIT

AUX0 < 0 is used for 2 special encounters in Wiz1. On level 4 at (12, 14). This is the encounter with LVL 7 FIGHTERs and the rest of that ENMYTEAM. You can probably guess the other special encounter!?

==========

ENMYCALC is the next structure in TMAZE. This structure is used to determine the main enemies you will encounter on this level.

ENMYCALC is an array with 3 elements, each one a RECORD of the following:

MINENEMY - Minimum enemy identifier
MULTWORS - Multiply to make it worse
WORSE01 - used with PERCWORS and MULTWORS to make it worse
RANGE0N - Used with MINENEMY to determine the normal range of enemy
PERCWORS - used with MULTWORS and WORSE01 to possibly choose a worse enemy.
This is a percentage figure.

The arrays are set up so that only 1 is chosen per combat. They are chosen at random in these percentages:

1 75%
2 19%
3 6%

The enemy index (ENEMYINX) is calculated by summing 3 values:

1. MINENEMY
2. A random number from 0 to RANGE0N.
3. MULTWORS * (a calculation using PERCWORS and WORSE01).

For the first 4 levels of Wiz1, MULTWORS is 0.

Let's look at level 1 ENMYCALC.

MINENEMY MULTWORS WORSE01 RANGE0N PERCWORS
-------- -------- ------- ------- --------
1 0 0 0 4 0
2 1 0 0 5 0
3 3 0 0 3 0

Row 1 is chosen 75% of the time
Row 2 is chosen 19% of the time
Row 3 is chosen 6 % of the time.

Row 1 is chosen 75% of the time and the enemies you will face are 0 to 4 using MINENEMY and RANGE0N. Since MULTWORS is 0, we are done with this calculation.

0 is BUBBLY SLIME
1 is ORC
2 is KOBOLD
3 is UNDEAD KOBOLD
4 is ROGUE

Row 2 is chosen 19% of the time and the range is 1 to 5.
Row 3 is chosen 6% of the time and the range is 3 to 5.

So we can calculate the percentage of times that enemies 0 to 5 are the main enemy in a combat:

Index Combatant ROW 1 ROW 2 ROW 3 Total
--------------- ----- ----- ----- -----
0 BUBBLY SLIME 18% 18%
1 ORC 18% 4% 22%
2 KOBOLD 18% 4% 22%
3 UNDEAD KOBOLD 18% 4% 2% 24%
4 ROGUE 4% 2% 6%
5 BUSHWACKER 4% 2% 6%

The value in the Total column is the percent of time that that enemy will be the main combatant in a "normal" encounter on level 1. Note, sometimes enemies are paired with TEAM members per another combat chart.

The first time we see a non zero value for MULTWORS is on level 5, so let's look at that.

MINENEMY MULTWORS WORSE01 RANGE0N PERCWORS
-------- -------- ------- ------- --------
1 30 10 1 10 35
2 30 10 2 10 35
3 30 10 3 10 30

We do the same calculations as before to get an enemy index, and then see if we should make it "worse".

For each of the 3 ROWs, we see the enemy index will be from 30 to 39 to start with. The value in WORSE01 is the maximum value used to multiply MULTWORS by to then add to the first enemy index calculation. For the first row, the breakdown in percentages for values of WORSE01 are:

0 65%
1 35%

For the second row, the breakdown in percentages for values of WORSE01 are:

0 65%
1 23%
2 12%

For the third row, the breakdown in percentages for values of WORSE01 are:

0 70%
1 21%
2 6%
3 3%

So normally on level 5 you will see enemies in the range 30 to 39, but sometimes might see an enemy from the range 60 to 69. Note, the odds of having an enemy in this upper range is quite slim (less than 1 percent).

Calculations for other levels follow a similar pattern (although there is a typo for level 9 in ROW 2).


Whew! I think I've given too much information for this section! This concludes the description of what you will find in the Level xx spreadsheets describing the levels of the Wizardry Maze.

Enjoy!
Tommy

mrbrad

unread,
Jul 6, 2014, 8:39:36 AM7/6/14
to
you need to make a wizardry re engineering file/doc faq or some such
just to keep all this straight and save all your efforts for others

just my suggestion interesting reading thanks for the effort

brad
former sysop lost gonzo bbs




---
This email is free from viruses and malware because avast! Antivirus protection is active.
http://www.avast.com

TommyGoog

unread,
Jul 11, 2014, 3:59:55 AM7/11/14
to
On Saturday, July 5, 2014 4:18:37 PM UTC-5, TommyGoog wrote:
> On Tuesday, July 1, 2014 9:36:27 PM UTC-5, TommyGoog wrote:
>
> > On Saturday, June 28, 2014 2:38:20 AM UTC-5, TommyGoog wrote:
> > > I've uploaded lots of data from Scenario.Data, in a more readable format, to Google Groups spreadsheets.
> > > https://docs.google.com/spreadsheet/ccc?key=0ApO5bjpT_F-OdDVnTEpXWExGTXUxb21UcmQ1bXdCdEE&usp=sharing
>
> > Time to describe the contents of the spreadsheets I uploaded earlier. First up is "Scenario.Data TOC".
>
> Next up are the 10 "Maze Level" spreadsheets, with each describing a separate level of the MAZE (see record TMAZE in the code).
> Tommy

Next up is "REWARDS".

This is a continuation of describing the SCENARIO.DATA contents in the GOOGLE doc spreadsheets.

After an encounter (melee), sometimes there will be a treasure chest. Treasure chest rewards are covered in this post.

When and where do CHESTS apear in the game?

How are the contents of the CHESTS determined?

When is there more than 1 item in a CHEST?

Is it based on the MAZE level? Or is it based on the difficulty of the ENEMY?

What items appear in CHESTS and what items don't?

How many CHESTS show up on each level of the MAZE?

Rewards in CHESTS are determined by the ENEMY you have fought and the circumstances of the encounter.

Here are ways for an encounter to occur:

1. When you enter a room (usually by Kicking the door), and the square you land on has MAZE.FIGHTS[ x, y] set TRUE, and FIGHTMAP[ x, y] is also TRUE.

2. If you were already opening a CHEST and it was trapped with an ALARM and the alarm goes off.

3. When MAZE.FIGHTS[ x,y] is set TRUE and FIGHTMAP[ x,y] is set FALSE (for example if this room was not picked when this level of the maze was first entered, or if it was picked and you already had a fight in this room). When you Kick into this room, about 1 out of 8 times an encounter will occur.

4. After getting the SEARCH Y/N message, and you answer Y, and AUX2 is 4.

5. Even with MAZE.FIGHTS[ x, y] FALSE, you can sometimes have an encounter on this square. Each time you enter a command (Left, Right, Forward, etc.) there is a 1% chance of an encounter.

Every time you have an encounter, there is one bad guy considered the main enemy (or main group of enemies). His picture will appear in the hi-res picture area, and his name will start at the top row of combatants (I think this part is true, but not 100% sure). During battle they may switch positions, but it is the initial one at top that is the main enemy. It is the main enemy that determines your rewards after battle completes.

Each ENEMY record has 2 fields to determine your rewards after battle: "REWARD1" and "REWARD2". The way the data file (SCENARIO.DATA) is currently set up, every REWARD1 gives a gold reward but does not have a treasure chest reward. REWARD2 has a gold reward, but also has an item (or multiple items) inside a treasure chest.

How does WIZARDRY determine to use REWARD1 or REWARD2? It is based on the way the encounter started:

REWARD1
#3, #4, and #5 from above.

REWARD2
MAZE.FIGHTS[ x, y] TRUE AND FIGHTMAP[ x, y] TRUE (#1 above), or
ALARM had been triggered (#2 above)

Recall from the MAZE discussion, that when first entering a level of the MAZE, the MAZE.FIGHTS[] array is used to build the FIGHTMAP[] array. There are up to 9 rooms filled for FIGHTMAP[], so each level you enter will have up to 9 CHESTS available to be opened. You can exit the level and return to it, and another 9 will be available.

Recall also that after an encounter occurs in a room, FIGHTMAP is set FALSE for that room.

Ok, so now we have a number from REWARD1 or REWARD2. Now what? The number is an index into the REWARDS section of SCENARIO.DATA. For Wizardry1 the values for a reward "package" can be from 0 to 23, although 22 and 23 are not used in Wizardry1. The index refers to an entire horizontal row in the REWARDS table.

Look at the REWARDS chart. Wow! There are a lot of columns for each reward "package"!

The first 9 columns (BCHEST through ALARM) represent one part of the package.

The next column, REWRDCNT, indicates the number of "rewards" in the following columns that are part of this package. Each part here is represented with columns that start with REWDPERC and end with MINADD2. There are a total of 9 of these partial packages, though most are not used in Wizardry 1.

You've probably already noticed the "BITEM = 1:::" line in the headings for REWARDS. This is indicating that when the field BITEM in that part of the package is set to 1, then use the second row of headings (REWDPERC to UNUSEDYY) to refer to the data. These records describe both GOLD rewards and ITEM rewards, and a variant record is used to describe the same data fields.

Note, there is a lot of "noise" settings in the REWARDS section of SCENARIO.DATA. For example, the first 10 packages (0..9) have REWRDCNT set to 1. This means that only the next 9 columns have useful information, yet if you look to the columns to the right of these, many have values set. That is just "noise".

You can see in REWARDS packages 0 to 9 that BCHEST is 0. This means there is no chest and no item (or OBJECT) reward -- just a gold reward. You can also see REWRDCNT set to 1 for each of these, so only the first set of columns following REWRDCNT are used to calculate the gold amount to distribute between the surviving characters.

You can also see in REWARDS packages 10 to 20 that BCHEST is -1. This means there is a chest involved.

There is a lot of "noise" in this table too. With BCHEST = 0, none of the BTRAPTYP values has any meaning. They do have meaning for packages 10 to 19 and 21.

Ok, now show me how that number from REWARD2 gets changed into an OBJECT.

The value in MININDX *should* indicate the smallest OBJECT associated with this part of the reward package. The value in RANGE *should* then indicate a maximum value that is added to MININDX to determine the range of OBJECTs that are associated with this part of the package. For example, for package 10, where REWRDCNT = 2, and BITEM = 1, the field MININDX = 1 and RANGE is 15. This *should* indicate a range of 1 to 16. The REWDPERC field indicates the percentage of time that an OBJECT appears in the treasure chest. So for package 10, there is a 10% chance of finding an object from 1 to 16 (or actually 3 to 17 as described below).

The code and these tables are badly designed. For my example above (1..16) the range is actually (mis-)calculated as (3..17). If you look at all the packages in the game, you will find that there are a few gaps as a result of this miscommunication between the code and these tables. There is clearly a bug in the design here.

Here is a summary for ENEMYs and their REWARD packages:

ENEMY index REWARD1 REWARD2
(gold) (objects)
----------- ------- ---------
0..9 0 10
10..19* 1 11
20..29 2 12
30..39 3 13
40..49 4 14
50..59 5 15
60..69 6 16
70..79** 7 17
80..89 8 18
90..95 9 19
96*** 20 20
97, 98, 99 0 0
100 21 21

*CREEPING COINS
2 4
**MURPHY'S GHOST
3 3
***WERDNA
20 20

97, 98, and 99 are specially paired with enemy 100.

Note, reward packages 22 and 23 don't seem to be used.

You won't see the following in chests:

1. LONG SWORD
2. SHORT SWORD
18. SHORT SWORD+1
34. HELM + 1
53. POTION OF DIAL

SHORT SWORD+1 is in the game because BOLTAC'S has 2 in stock at the beginning of the game.

HELM + 1 and POTION OF DIAL never appear in this APPLE II version of Wizardry 1.

Here is a table summarizing the OBJECTs associated with an ENEMY and the chances of having that object in a chest:


ENEMY Object 1 Object 2 Object 3
INDEX %Chance Range %Chance Range %Chance Range
------ ------- ------ ------- ------ ------- ------
0..09 10% 3..17
10..19 20% 3..17 10% 19..33
20..29 30% 3..17 15% 19..33
30..39 40% 3..17 20% 19..33
40..49 50% 3..17 30% 19..33 10% 35..52
50..59 100% 3..17 50% 19..33 20% 35..52
60..69 75% 19..33 25% 35..52 10% 54..80
70..79 100% 19..33 50% 35..52 15% 54..80
80..89 70% 35..52 25% 54..80 5% 81..93
90..95 100% 35..52 50% 54..80 10% 80..92
96 100% 94
97, 98, 99
100 100% 16 100% 93 100% 61


For example, let's say you fight MINOR DAIMYO (Enemy index 39). There is a chance of getting 2 items from this treasure chest. The first one has a 40% chance of appearing and will be an item from ANOINTED MACE to LONG SWORD+1. The second one has a 20% chance of showing up and will be an item from MACE + 1 to DRAGON SLAYER.

Ok, let's run through an entire scenario. You enter a new level of the MAZE. The code looks at the MAZE.FIGHTS section of SCENARIO.DATA and generates an internal table called FIGHTS[ x, y] to determine up to 9 rooms where a "normal" encounter will occur. You "K"ick down the door to the room that has been selected for a normal fight. You win the encounter and a treasure chest appears. You inspect the chest, disarm the chest, and open the chest. A gold amount is calculated based on the enemy you have defeated. If you defeated a MINOR DAIMYO as the main enemy, then there is a 40% chance that one of your characters will be selected to receive an OBJECT with index from 3 to 17. Another character (or possibly the same one), will have a 10% chance of receiving an OBJECT from 35 to 52. Once the encounter is complete, the treasure chest will not appear again in this room unless you exit and re-enter this level of the maze and this room is selected again.

In Wizardry1, if the character chosen to receive the reward does not have any open slots in his inventory of possessions, the object is simply not given to any character. Therefore it is best after an encounter to "balance" your open possessions among your characters to try to have at least 1 open slot per character.

So a question I had while playing was:

Before I start on level 10 of the MAZE, I'd like to have the biggest and best weapons and armor available. What are the chances of getting items in the range from 54 to 80 while exploring level 9 of the MAZE?

Or more specifically, how many trips to level 9 should be needed to acquire 3 BLADE CUISINARTs and 3 SILVER GLOVES?

From the table above you can see that you need to fight an ENEMY with index 60 or greater to have any chance at those treasures.

Level 9 has this for the enemies (from the MAZE part of Scenario.DATA):

ENMYCALC MINENEMY MULTWORS WORSE01 RANGE0N PERCWORS
1 55 10 1 10 35
2 55 10 2 10 35
3 55 10 3 10 35

Row 1 is chosen 75% of the time.
Row 2 is chosen 19% of the time.
Row 3 is chosen 6% of the time.

Row 1 (75% of time)

65% 55 to 64
50% 55 to 59 0.24375 (75% * 65% * 50%)
50% 60 to 64 0.24375

35% 65 to 74
50% 65 to 69 0.13125
50% 70 to 74 0.13125

Row 2 (19% of time)

65% 55 to 64
50% 55 to 59 0.06175
50% 60 to 64 0.06175

23% 65 to 74
50% 65 to 69 0.02185
50% 70 to 74 0.02185

12% 75 to 84
50% 75 to 79 0.0114
50% 80 to 84 0.0114

Row 3 (6% of time)

65% 55 to 64
50% 55 to 59 0.0195
50% 60 to 64 0.0195

23% 65 to 74
50% 65 to 69 0.0069
50% 70 to 74 0.0069

8% 75 to 84
50% 75 to 79 0.0024
50% 80 to 84 0.0024

4% 85 to 94
50% 85 to 89 0.0012
50% 90 to 94 0.0012

Enemy
Index
-----
55 to 59 0.2438 0.0618 0.0195

60 to 64 0.2438 0.0618 0.0195
65 to 69 0.1313 0.0219 0.0069

70 to 74 0.1313 0.0219 0.0069
75 to 79 0.0114 0.0024

80 to 84 0.0114 0.0024
85 to 89 0.0012

90 to 94 0.0012


...and finally:

Enemy
Index % of time %54 to 80 %80 to 92
-------- --------- --------- ---------
55 to 59 0.3250 0.0000
60 to 69 0.4850 0.0485
70 to 79 0.1738 0.0261
80 to 89 0.0150 0.0038 0.0008
90 to 94 0.0012 0.0006 0.0001
------ ------
0.0789 0.0009

So 8% of the time that you enter a room with a chest, you will find one of the 27 OBJECTS with index 54 to 80.

Answer to my question: About 80 trips to level 9.

If anyone would like to verify my calculations by looking at the code, please do so. Or if you would like to gather some empirical results by testing Wizardry 1 and going to level 9 about 80 times and reporting your results here that would be great!

If you have any questions about Wizardry I please feel free to ask them!

I hope you have enjoyed reading this.

Tommy

TommyGoog

unread,
Jul 15, 2014, 4:43:46 AM7/15/14
to
On Friday, July 11, 2014 2:59:55 AM UTC-5, TommyGoog wrote:
> On Saturday, July 5, 2014 4:18:37 PM UTC-5, TommyGoog wrote:
>
> > On Tuesday, July 1, 2014 9:36:27 PM UTC-5, TommyGoog wrote:
>
> >
>
> > > On Saturday, June 28, 2014 2:38:20 AM UTC-5, TommyGoog wrote:
>
> > > > I've uploaded lots of data from Scenario.Data, in a more readable format, to Google Groups spreadsheets.
>
> > > > https://docs.google.com/spreadsheet/ccc?key=0ApO5bjpT_F-OdDVnTEpXWExGTXUxb21UcmQ1bXdCdEE&usp=sharing
>
> >
>
> > > Time to describe the contents of the spreadsheets I uploaded earlier. First up is "Scenario.Data TOC".
>
> >
>
> > Next up are the 10 "Maze Level" spreadsheets, with each describing a separate level of the MAZE (see record TMAZE in the code).
>
> > Tommy
>
>
>
> Next up is "REWARDS".
>
>
>

> Tommy

Besides gold and objects as rewards, you also gain experience after an encounter. I found an interesting bug in the code that I'd like to share with you. Any Pascal programmers reading this?

CALCKILL() calculates the total amount of experience your party gains based on the attributes of the enemy you have killed. The more difficult the enemy the more points are awarded. CALCKILL() starts by clearing the variable KILLEXP which is a TWIZLONG variable. TWIZLONG is similar to binary coded decimal where there are 3 parts representing an integer from 0 to 999,999,999,999.

KILLEXP declaration:
KILLEXP : TWIZLONG;

CALCKILL()
FILLCHAR( KILLEXP, 6, CHR( 0));

TWIZLONG definition:

TWIZLONG = RECORD
LOW : INTEGER;
MID : INTEGER;
HIGH : INTEGER;
END;

The LOW part represents 10000^0.
The MID part represents 10000^1.
The HIGH part represents 10000^2.

So if you want to represent a value of 25,435 in this format you would set the following:

KILLEXP.LOW := 5435;
KILLEXP.MID := 2;
KILLEXP.HIGH := 0;

If you want to add 2 TWIZLONG varaibles, Wizardry has the following:

PROCEDURE ADDLONGS( VAR FIRST: TWIZLONG; (* P010005 *)
VAR SECOND: TWIZLONG); FORWARD;


PROCEDURE ADDLONGS; (* P010005 *)

BEGIN
FIRST.LOW := FIRST.LOW + SECOND.LOW;
IF FIRST.LOW >= 10000 THEN
BEGIN
FIRST.MID := FIRST.MID + 1;
FIRST.LOW := FIRST.LOW - 10000
END;

FIRST.MID := FIRST.MID + SECOND.MID;
IF FIRST.MID >= 10000 THEN
BEGIN
FIRST.HIGH := FIRST.HIGH + 1;
FIRST.MID := FIRST.MID - 10000
END;

FIRST.HIGH := FIRST.HIGH + SECOND.HIGH;
IF FIRST.HIGH >= 10000 THEN
BEGIN
FIRST.HIGH := 9999;
FIRST.MID := 9999;
FIRST.LOW := 9999
END
END;

CALCKILL() continues after clearing KILLEXP by adding values to KILLEXP based on some attributes of the enemy. Eventually it reaches these lines:

IF ENEMYREC.UNAFFCT > 0 THEN
MLTADDKX( (ENEMYREC.UNAFFCT DIV 10) + 1, 40);

Note, DIV is integer division and drop the remainder.

Most enemies have 0 for UNAFFCT, but there are about 20 that have non-zero values such as:

20, 25, 40, 50, 60, 70, 85, 95

There are other calls to MLTADDKX with other values for each parameter, for example:

MLTADDKX( ENEMYREC.MAGSPELS, 35);
MLTADDKX( ENEMYREC.DRAINAMT, 200);

I did a terrible job of naming this procedure and its variables because I had not taken the time to understand the code. In hindsight I should have named the procedure something like KPOW2KX. It is taking the second parameter, a constant value, and multiplying it by 2 raised to the first parameter less 1, and then adding that value to KILLEXP.

Examples:

MLTADDKX( 1, 20) 20 * 2^(1-1) 20
MLTADDKX( 2, 20) 20 * 2^(2-1) 40
MLTADDKX( 3, 20) 20 * 2^(3-1) 80
MLTADDKX( 4, 20) 20 * 2^(4-1) 160
MLTADDKX( 5, 20) 20 * 2^(5-1) 320

MLTADDKX( 1, 30) 30 * 2^(1-1) 30
MLTADDKX( 2, 30) 30 * 2^(2-1) 60
MLTADDKX( 3, 30) 30 * 2^(3-1) 120
MLTADDKX( 4, 30) 30 * 2^(4-1) 240
MLTADDKX( 5, 30) 30 * 2^(5-1) 480

MLTADDKX( 1, 40) 40 * 2^(1-1) 40
MLTADDKX( 2, 40) 40 * 2^(2-1) 80
MLTADDKX( 3, 40) 40 * 2^(3-1) 160
MLTADDKX( 4, 40) 40 * 2^(4-1) 320
MLTADDKX( 5, 40) 40 * 2^(5-1) 640

PROCEDURE SETKILLX( AMOUNT : INTEGER); (* P010D24 *)

BEGIN (* SETKILLX *)
KILLEXPX.HIGH := 0;
KILLEXPX.MID := 0;
KILLEXPX.LOW := AMOUNT
END; (* SETKILLX *)


PROCEDURE MLTADDKX( MULTIPLY : INTEGER; (* P010D25 *)
AMOUNT : INTEGER);

BEGIN (* MLTADDKX *)
IF MULTIPLY = 0 THEN
EXIT( MLTADDKX);
SETKILLX( AMOUNT);
WHILE MULTIPLY > 1 DO
BEGIN
MULTIPLY := MULTIPLY - 1;
ADDLONGS( KILLEXPX, KILLEXPX)
END;
ADDLONGS( KILLEXP, KILLEXPX)
END; (* MLTADDKX *)


Your task is to find the bug in this code. Here is your hint (note the last entry in this table is wrong):

MLTADDKX( 5, 40) = 640
MLTADDKX( 6, 40) = 1280
MLTADDKX( 7, 40) = 2560
MLTADDKX( 8, 40) = 5120
MLTADDKX( 9, 40) = 20240

The code seems so straightforward that I doubt you will catch the error right away if at all.

In a few days I will post an explanation.

Tommy

Antoine Vignau

unread,
Jul 15, 2014, 7:39:28 AM7/15/14
to
KILLEXP and KILLEPX ?

TommyGoog

unread,
Jul 15, 2014, 11:41:13 AM7/15/14
to
There are 2 separate variables:

KILLEXP
KILLEXPX

Both are TWIZLONG, and both are initialized to 0,0,0.
Sorry for not including this with the original message.
Since 8 characters determine a unique name, there is nothing unusual about the names regarding this "bug".

Tommy

Antoine Vignau

unread,
Jul 15, 2014, 3:52:29 PM7/15/14
to
MLTADDKX( 9, 40) should be 10240 and not 20240
The ADDLONGS does its job but not well,
av

TommyGoog

unread,
Jul 15, 2014, 9:28:29 PM7/15/14
to
Do you see why ADDLONGS returns the wrong value?

Do you also see the one line fix to this problem? It is actually less than one line, and in fact you delete from the code and don't add to it.

Tommy

Michael J. Mahon

unread,
Jul 16, 2014, 3:23:47 AM7/16/14
to
Delete the VAR specification for SECOND, since it is a constant parameter.

When ADDLONGS is called with identical arguments, SECOND.MID is updated as
a side effect of updating FIRST.MID. Of course the same issue occurs with
SECOND.HIGH when there is a carry out of FIRST.MID.

Side effects can be nasty!
--
-michael - NadaNet 3.1 and AppleCrate II: http://home.comcast.net/~mjmahon

TommyGoog

unread,
Jul 16, 2014, 11:12:09 AM7/16/14
to
You are 100% correct! You win a gold star (or is it a BLUE RIBBON?)

Now a philosophical question. If I were to "fix" this problem in a future "release" of WizardryI, should I then also compensate for the bug. It might be that the original authors were happy with the "side-effect" values that were generated even if they did not understand the bug that was there, and "fixing" the problem will make the game worse than by not fixing it!

Of the 101 enemies in the game, only 5 are affected. Here are the total experience values before the fix and after the fix:

EARTH GIANT 20435 10435
WILL O' WISP 42840 22840
FROST GIANT 40875 20875
GREATER DEMON 44090 24090
POISON GIANT 40840 20840

...and for comparison:

W E R D N A 15880 15880

Tommy

Didier VALLET

unread,
Jul 16, 2014, 2:00:45 PM7/16/14
to
Hello Tommy,
The work you've done is very big.
I've print the sourcecode and working to understand the mecanisms of Wiz1. It's very interesting. I spend hours !

I have also a philosophical question : is it a good idea to implement it on another 8bit computer (with C and assembleur code). First of all a Wiz1 clone then a Wiz1+ with enhanced graphics (also possible on iie i think) ; such a big project !)

Despite i've got from my attic USCD rules, i've some difficulties to understand the code of packed data in the case of :
- ATTRIB : PACKED ARRAY[ STRENGTH..LUCK] OF 0..18;
- LUCKSKIL : PACKED ARRAY[ 0..4] OF 0..31;

- SPELLSKN : PACKED ARRAY[ 0..49] OF BOOLEAN;
- WEPVSTY2 : PACKED ARRAY[ 0..1, 0..13] OF BOOLEAN;
WEPVSTY3 : PACKED ARRAY[ 0..1, 0..6] OF BOOLEAN;
WEPVSTYP : PACKED ARRAY[ 0..13] OF BOOLEAN;

And the position + values of
- LOSTXYL : RECORD CASE INTEGER OF
1: (LOCATION : ARRAY[ 1..4] OF INTEGER);
2: (POISNAMT : ARRAY[ 1..4] OF INTEGER);
3: (AWARDS : ARRAY[ 1..4] OF INTEGER);

Thanks a lot for your job and the feedback of all apple experts.
Didier

Michael

unread,
Jul 16, 2014, 3:03:24 PM7/16/14
to
I was always more of an Ultima-fan then Wizardy but darn! This is superb reverse-engineering at its finest!

Well done Tommy!

Marco Verpelli

unread,
Jul 16, 2014, 3:06:39 PM7/16/14
to
Packed array are used to save memory:
SIZEOF(SPELLSKN) = 8, if you declare SPELLSKN without the PACKED keyword then SIZEOF(SPELLSKN) = 98.

Variant record is a way to "recycle" memory (a sort of EQUIVALENCE in FORTRAN or union in C) but in this example is used only to call the same array with 3 different names.

Marco

TommyGoog

unread,
Jul 16, 2014, 7:01:48 PM7/16/14
to
On Wednesday, July 16, 2014 1:00:45 PM UTC-5, Didier VALLET wrote:
> Hello Tommy,
>

> Didier


>>Michael
>>I was always more of an Ultima-fan then Wizardy but darn! This is superb reverse-engineering at its finest!

Thanks for the compliment! It brightens my day and helps me to "keep going" at this re-engineering and analysis when I hear kind words.

>>Didier VALLET
>>I have also a philosophical question : is it a good idea to implement it on another 8bit computer (with C and assembleur code). First of all a Wiz1 clone then a Wiz1+ with enhanced graphics (also possible on iie i think) ; such a big project !)

I'm not sure I have an answer, as I'm not sure exactly what you are asking. But it certainly would be possible to implement the code on another 8 bit computer in C, although that sounds like a lot of work. Another way of doing it would be to implement the Pascal run-time system on the other 8 bit computer, but that also would be a huge undertaking.

Sometimes I do wonder about the legal aspects with this code since we don't have permission from those who own the Copyrights or Trademarks for Wizardry. Certainly if anyone was going to make any money on this type of work I'd really think it questionable.

Marco Verpelli's response to your questions about PACKED arrays and "variant" records is correct and I will add a few examples.

PACKED arrays save internal memory by squishing as much data as possible into a smaller area. For example, let's say you had an array of 12 BOOLEANs. Because a single BOOLEAN normally occupies 2 bytes of data when declared, this array will take up 24 bytes of data space. But if you PACK the array, each BOOLEAN can really be represented by just a 0 or 1, so each of the 12 array values can be represented by just 1 bit. Instead of taking 16 bytes of data, it will occupy 2 bytes (with 4 bits unused).

As far as the execution of the code, it will logically work exactly the same whether the array is PACKED or not.

With a program like Wizardry that relies a lot on data being in files, the PACKED arrays and structures also save disk space in those files.

>>Didier VALLET
>>- LOSTXYL : RECORD CASE INTEGER OF
>> 1: (LOCATION : ARRAY[ 1..4] OF INTEGER);
>> 2: (POISNAMT : ARRAY[ 1..4] OF INTEGER);
>> 3: (AWARDS : ARRAY[ 1..4] OF INTEGER);

This last part of the TCHAR, or CHARACTER record, was really strange during my re-engineering of Wizardry3. It's possible that the original code looks like this:

ABC : ARRAY[ 1..4] OF INTEGER;

And every place in the code where I have LOSTXYL.LOCATION, or LOSTXYL.POISNAMT, or LOSTXYL.AWARDS, is replaced with ABC.

The code would work exactly the same.

Using the "variant" record (RECORD CASE...), the code becomes more readable in the sense that you can tell if the code is using this memory as a location in the maze, or as a poison amount, or for the special awards.

Another concept for "variant" records which is more often used is when you need to represent the same data in two or more different data formats. A good example of that is the following:

MEMPTR : RECORD CASE INTEGER OF
1: (I : INTEGER);
2: (P :^INTEGER);
END;

Here is the code that uses MEMPTR:

MEMPTR.I := 16384; (* $4000 *)
RELEASE( MEMPTR.P);

The code for RELEASE() expects to receive a pointer to an integer. The line above it sets the pointer to an INTEGER value (16384). Note, 16384 is the value of the pointer, and not what it points to.

You could try declaring MEMPTR as a pointer:

MEMPTR : ^INTEGER;

...but then you would not be able to set MEMPTR to an INTEGER value.

You could declare MEMPTR as an INTEGER:

MEMPTR : INTEGER:

...but then you would not be able to pass it to RELEASE().

So by using a "variant" record, you can now use MEMPTR as both an INTEGER and as a POINTER.

I hope this helps!

Tommy

Didier VALLET

unread,
Jul 17, 2014, 7:27:35 AM7/17/14
to

Thanks a lot for your help.
I think it's ok for the end of the structure TCHAR.

However, i don't see what is the packed form for the abilities.
If i understood well i have : 6*(max value 18) = 6 * 5 boolean = 30 bytes = 4 char for implement abilities and 5*(max value 31) = 5* 6 boolean = 30 bytes

Ex1 : JASON on the asimov file, who is 18 for each form.
Strength ........... 18 = %10010
Intelligence ....... 18
Piety .............. 18
Vitality ........... 18
Agility ............ 18
Luck ............... 18
So I think i should find : 10010 (18) in the pack area for 6 times.
At $2C in the char section of Jason, i can see : 52 4A 52 4A
and for luke AD 2D 4D 01
524A 524A = 0101 0010 0100 1010 0101 0010 0100 1010
s ssss iii iipp pppv vvvv aaa aa

AD2D 4D01 = 1010 1101 0010 1101 0100 1101 0000 0001

The same for another char i create :
Strength ........... 8 = 1000
Intelligence ....... 7 = 0111
Piety .............. 7 = 0111
Vitality ........... 6 = 0110
Agility ............ 12 = 1100
Luck ............... 17 = 10001
with on the data file at character $2C : E8 1C 86 45 10 42 10 02
the first 4 bytes : 1110 1000 0001 1100 1000 0110 0100 0101
s ssss iii ii ...
It doesn't work ...
That's the reason for why i say that i don't understand the functionnal way of packed datas.

@Tommy : of course, if i talk about a making of a clone of wizardry, it's not for $ reasons. it's only for the fun, the pleasure to understand the fine mecanism of a game like wizardry on which i spend a lot of time on my apple 2e (which was my 3e computer after Oric1 / Atmos). By your reverse engineering of the game, you've made this known reachable :).

TommyGoog

unread,
Jul 17, 2014, 9:35:57 AM7/17/14
to
On Thursday, July 17, 2014 6:27:35 AM UTC-5, Didier VALLET wrote:
> Thanks a lot for your help.

> The same for another char i create :
>
> Strength ........... 8 = 1000
>
> Intelligence ....... 7 = 0111
>
> Piety .............. 7 = 0111
>
> Vitality ........... 6 = 0110
>
> Agility ............ 12 = 1100
>
> Luck ............... 17 = 10001
>
> with on the data file at character $2C : E8 1C 86 45 10 42 10 02
>
> the first 4 bytes : 1110 1000 0001 1100 1000 0110 0100 0101
>
> s ssss iii ii ...
>
> It doesn't work ...


Hi Didier,

1 bit = 0 or 1
1 byte = 8 bits (%00000000 to %11111111, or $00 to $FF)
1 word = 2 bytes

Apple Pascal likes to work on even word boundaries and doesn't like to split data across those word boundaries, but it will split data across a byte boundary within a word.

Apple Pascal also likes to store multiple byte values with the low order byte first, then the high order byte. This is referred to as "little-endian".

The first 6 attributes (STRENGH..LUCK) occupy 2 words in this format:

Word 0: IIISSSSS xPPPPPII
Word 1: AAAVVVVV xLLLLLAA

SSSSS = STRENGTH
IIIII = IQ
PPPPP = PIETY
VVVVV = VITALITY
AAAAA = AGILITY
LLLLL = LUCK
x = undefined (can be 0 or 1)

Packing of variables begins in the upper byte of Word 0 in the least significant bits. It then continues to the most significant bits in Word 0 and then to the least significant bits in the lower byte of Word 0. It will not attempt to split a variable between Word 0 and Word 1, so the "x" signifies an unused bit. It is undefined and can be 0 or 1.

Packing continues with Word 1 similar to Word 0.

The APPLE Pascal Language Reference Manual has a description of PACKED VARIABLES (files, ARRAYS, and RECORDS) starting on p. 15.

=====

LUCKSKIL is defined as PACKED ARRAY[ 0..4] of 0..31;

Word 0: 11100000 x2222211
Word 1: 44433333 xxxxxx44

=====

Using your character:

STRENGTH = 8 = 01000
IQ = 7 = 00111
PIETY = 7 = 00111
VITALITY = 6 = 00110
AGILITY = 12 = 01100
LUCK = 17 = 10001

Packing these as above:

Word 0: IIISSSSS xPPPPPII
11101000 x0011100

Word 1: AAAVVVVV xLLLLLAA
10000110 x1000101

Result: E8 1C 86 45 (if both "x" values are 0)

Seems to work as advertised.

=====

After I posted earlier that it would be possible to port WizardryI from Pascal to C on an 8-bit computer, it dawned on me that you will run into a problem with fitting the program into memory. I don't know if C has the equivalent of SEGMENTs like Pascal does (or CHAINING as in BASIC). You will not be able to compile the entire program and have all of it fit into 64KB of memory at one time.

One other clarification I'd like to make about PACKING in Wizardry1. You cannot just remove all the PACKing of arrays and structures in Wizardry1 unless you change all the data in the files to also not be PACKed. I suspect that if you tried to do all of that, you would run out of disk memory for the files, or possibly run out of stack space during execution of the program.

Now if you really want to get confused about PACKED arrays and structures, try looking at the generated P-code :)

I hope this has helped.

Tommy

Didier VALLET

unread,
Jul 17, 2014, 2:40:10 PM7/17/14
to

Thanks for your help Tommy, i've red the p.15 but i didn't understood the specific case of these datas.

> After I posted earlier that it would be possible to port WizardryI from Pascal to C on an 8-bit computer, it dawned on me that you will run into a problem with fitting the program into memory. I don't know if C has the equivalent of SEGMENTs like Pascal does (or CHAINING as in BASIC). You will not be able to compile the entire program and have all of it fit into 64KB of memory at one time.

No, Segment structure is unique in pascal !
If it is possible, the program should be cut into pieces with variables at a common places ; and i agree with you, i don't think that the code will fit in 64kb.

> One other clarification I'd like to make about PACKING in Wizardry1. You cannot just remove all the PACKing of arrays and structures in Wizardry1 unless you change all the data in the files to also not be PACKed. I suspect that if you tried to do all of that, you would run out of disk memory for the files, or possibly run out of stack space during execution of the program.

May be the ocnversion from pascal to c is impossible but it's a nice trip to go deeply in this software.

It is loading more messages.
0 new messages