[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Re-engineered: Wizardry III, Legacy of Llylgamyn
> Tommy,
>
> This level of reverse engineering is nothing but admirable.
>
> -- Vlad
Hi Vlad,
Thank you for the compliment.
If I ever complete writing about Wizardry III re-engineering, I will
consider creating PDFs and ZIP files to archive this work, but it
doesn't seem right now that very many are interested in this effort.
In fact, I feel a bit discouraged and wonder to myself if I should
spend the time writing about decompilation techniques. Is there
really anyone that will seriously read about decompilation techniques
in order to learn how to do them and then actually use those
techniques themselves? Depending on the level of detail that I
provide, it could fill a book, especially as it pertained to my effort
with Wizardry III.
AppleWin and 4 Disk Drives
==========================
In this post, I will describe some fun I had with AppleWin and 4 disk
drives.
In Dec 2011 I realized I would need an Apple II with 4 disk drives in
order to compile the full Wizardry III Pascal program. Up until that
time I had used AppleWin and 2 disk drives. If you ever have a reason
to work with UCSD Pascal on an Apple II, I'd strongly suggest having
at least 4 disk drives.
I sent a request to the AppleWin developers at BerliOS to request that
AppleWin support 4 disk drives, but they declined. They did offer to
send me the AppleWin source code so that I could "hack" at it, and
that is what I did.
I created a version of AppleWin that supports 4 disk drives. I
created 4 "buttons" where there had been 2 buttons on the right side
of the display. Each button supports left-click, right-click, mouse-
over popup, and file drag-and-drop. Each of the 4 buttons has an
"indicator" to display reading/writing/not-spinning. Since the
buttons were smaller, I altered the disk drive picture displayed on
each, and also set the drive path image on the button to be left
justified. I disabled the use of slots 4 and 5 for Mockingboards by
using the "-m" option on the AppleWin call line. I also updated the
Configuration dialog box for "Disk" to have 4 entries instead of 2.
And then I changed the code to recognize the 2 extra disks in slot 5.
I tested the disk drives using Dos3.3. Each drive could initialize a
disk, save files, delete files, load files, display catalogs, etc.
Ok, so it was now time to compile the whole Wizardry III program -- or
so I thought!
I loaded 4 Pascal disks into the 4 disk drives. I pressed the
"Reboot" button and the first disk started the boot process, and maybe
the second also started and stopped, but then....
*** NOTHING ***
The disks stopped spinning and the Pascal system was hung before it
had displayed any messages on the screen.
I let out a few choice words and thought "how the heck am I going to
figure out how or why the Pascal system is hung? What is so different
between DOS and PASCAL?"
After awhile I hit upon the idea of using the AppleWin debugger to
step through the hung system to gather more information and see if I
could discover what was going on. As I was stepping through the code,
I realized that it seemed to be doing something with disks (well duh!)
and the code was also checking the keyboard data input ($C000) and
saving any keystrokes. This jogged something in my memory from long
ago when I had first disassembled the Wizardry III Pascal boot code.
The Pascal operating system on the Wizardry III diskette is of course
a subset of the full blown Pascal system. I looked at my paper
listings (created in 1991) and with my hand notes I discovered that we
were indeed in the Pascal routine to perform disk I/O, similar to RWTS
for DOS.
Here is the partial listing of code as found in Apple ][ Pascal 1.1 P-
Code Interpreter 6502 Disassembly
(ftp://ftp.apple.asimov.net/pub/apple_II/documentation/programming/
pascal/Apple 2 Pascal 11 PCodeIntDism.pdf)
82 INCLUDE DISKII.IAS
1 ;.PAGE '???'
2 ;
3 ;
4 ;
D0E8 A002 5 INTAS LDY #$02
D0EA 8CFFDF 6 STY ADFFF
7 ;
8 ;
9 ;
D0ED A00A 10 LDY #$0A
D0EF 8CFDDF 11 STY ADFFD
12 ;
13 ; Branch if slot number hasn't changed
14 ;
D0F2 AEA103 15 LDX SLOTNO
D0F5 ECAF03 16 CPX PRVSLT
D0F8 F011 17 BE INTAU
18 ;
19 ;
20 ;
D0FA AEAF03 21 LDX PRVSLT
22 ifndef ORIG
23 BZ INTAZ
24 endif
25 ;
26 ;
27 ;
D0FD 2001D0 28 INTAT JSR CCONCK ; INTAD
29 ;
30 ;
31 ;
D100 2082D3 32 JSR INTDV
D103 D0F8 33 BNZ INTAT
34 ;
35 ;
36 ;
37 ifndef ORIG
38 INTAZ
39 endif
D105 AEA103 40 LDX SLOTNO
D108 8EAF03 41 STX PRVSLT
42 ;
43 ;
44 ;
D10B 2082D3 45 INTAU JSR INTDV
D10E 08 46 PHP
47 ;
48 ; Turn drive on
49 ;
D10F BD89C0 50 LDA DRVON,X
51 ;
The BNZ at line 33 was always taking the jump to INTAT at line 28.
CCONCK is checking for keyboard input, and INTDV is testing to see if
the disk drive is still spinning. When it is done spinning, then the
"Z" status in the processor is set and we break out of the loop. But
the "Z" status was never getting set.
In looking at this code, I realized that we are only here "if the slot
number (has) changed"!
Here is the INTDV code:
744 ;
D382 A000 745 INTDV LDY #0
746 ;
747 ;
748 ;
D384 BD8CC0 749 INTDY LDA DRVRD,X
750 ;
751 ; Time delay
752 ;
D387 2094D3 753 JSR INTDZ
D38A 48 754 PHA
D38B 68 755 PLA
756 ;
757 ; Branch if drive still moving???
758 ;
D38C DD8CC0 759 CMP DRVRD,X
D38F D003 760 BNE INTDZ
761 ;
762 ;
763 ;
D391 88 764 DEY
D392 D0F0 765 BNZ INTDY
766 ;
767 ; Return to caller
768 ;
D394 60 769 INTDZ RTS
Ok, we are making real progress! The indicator status lights in
AppleWin for all 4 disk drives showed them as inactive, yet this
Pascal Operating System code thought a drive was still spinning!
So now I turned my attention to the AppleWin code (written in C and C+
+). Eventually I found myself in DiskReadWrite which returns the next
byte of data from a spinning disk:
static BYTE __stdcall DiskReadWrite (WORD programcounter, WORD, BYTE,
BYTE, ULONG)
{
Disk_t * fptr = &g_aFloppyDisk[currdrive];
diskaccessed = 1;
if (!fptr->trackimagedata && fptr->imagehandle)
ReadTrack(currdrive);
if (!fptr->trackimagedata)
return 0xFF;
BYTE result = 0;
if (!floppywritemode || !fptr->bWriteProtected)
{
if (floppywritemode)
{
if (floppylatch & 0x80)
{
*(fptr->trackimage+fptr->byte) = floppylatch;
fptr->trackimagedirty = 1;
}
else
{
return 0;
}
}
else
{
result = *(fptr->trackimage+fptr->byte);
}
}
if (0)
{ LOG_DISK("nib %4X = %2X\r", fptr->byte, result); }
if (++fptr->byte >= fptr->nibbles)
fptr->byte = 0;
return result;
}
While reading data, DiskReadWrite returns "result" using fptr->byte.
Also notice near the end of the routine the code that unconditionally
increments fptr to the next byte on the disk (++fptr->byte).
I found that a Disk_t object has the following field:
DWORD spinning;
And I also found the variable:
static BOOL floppymotoron = 0;
On the physical Apple II computer, you can turn the disk drive off and
for awhile the disk continues to spin and the data latch will continue
to change. Eventually the disk stops spinning. This is also true for
AppleWin. The problem with the code in DiskReadWrite is that
EVEN WHEN THE DISK HAS STOPPED SPINNING, THE fptr POINTER IS UPDATED
TO THE NEXT BYTE.
In essence, for AppleWin, once a disk has started spinning the disk
will continue to spin and the data latch will always keep changing!
This is an existing bug in AppleWin!
Here is a test program I wrote to demonstrate the bug. On a physical
Apple II computer, the program will eventually stop with the BRK
instruction at $0815. On AppleWin 1.20.1.0, the program executes in
an infinite loop.
]CALL-151
*800L
0800- A2 60 LDX #$60 ; Slot 6
0802- BD 89 C0 LDA $C089,X ; Drive ON
0805- A9 20 LDA #$20 ; Input to Wait
0807- 20 A8 FC JSR $FCA8 ; Wait for drive
080A- BD 88 C0 LDA $C088,X ; Drive OFF
080D- 20 16 08 JSR $0816 ; Check data latch
0810- 8D 64 04 STA $0464 ; "Splat" data to screen
0813- D0 F8 BNE $080D ; Done spinning?
0815- 00 BRK ; Yes, BRK
0816- A0 00 LDY #$00 ; Try to get 256 the same
0818- BD 8C C0 LDA $C08C,X ; Read data latch
081B- 20 28 08 JSR $0828 ;
081E- 48 PHA ;
081F- 68 PLA ;
0820- DD 8C C0 CMP $C08C,X ; Compare with next data latch
0823- D0 03 BNE $0828 ; Different? Yes, still spinning
0825- 88 DEY ; That pair matched, try again
0826- D0 F0 BNE $0818 ; Matched 256 times? Yes, RTS
0828- 60 RTS ; Z=0? Yes, done spinning
--Tommy