Discussion:
Writing Apple II emulator, trouble with Disk II simulation
(too old to reply)
Luigi Thirty
2017-08-05 03:10:04 UTC
Permalink
Raw Message
I'm writing an Apple II emulator for Mac in Swift. I have a fully-functional 6502 core (verified by Klaus' functional tests) and enough Apple II hardware in the emulator to run in text mode and low-resolution graphics mode.

I'm running into some problems with the dreaded Disk II though. I've written a simulator, currently with no delay on any operations. Each time the controller requests a byte from the drive (assuming the controller is in read mode) it just serves up the next byte, wrapping around to the beginning of the track eventually. This is enough to at least start booting a DOS 3.3 master disk.

According to Beneath Apple DOS, invoking the controller ROM will start the controller reading T0S0. I've verified that my drive simulation returns the correct data compared to reading the disk in Ciderpress. The first bootloader runs successfully, loading the second bootloader from T0S2 and T0S1 into memory.

The second bootloader is executed and starts reading from T2S4 using the DOS routines. In my emulator, the head is stepped to track 2 but the software gets stuck reading T2S4 over and over again until the bootloader gives up and crashes to the monitor. (I'm sniffing the DOS parameter table to see what the drive is trying to do.)

I'm thinking that it doesn't like what it's reading from the drive, somehow. I've verified that my 6-and-2 encoded T2S4 reads the same as the disk image, so I'm not sure why the software is failing. Is a delay necessary for the DOS sector read routine where it's not necessary for the controller's sector read routine?
Nick Westgate
2017-08-05 06:44:40 UTC
Permalink
Raw Message
Post by Luigi Thirty
I'm running into some problems with the dreaded Disk II though
It's not really that difficult.

You haven't provided any source code though, so it's hard to guess where you've gone wrong.

You could try a NIB image. If that booted it would point to your nibble generation code.

Otherwise one easy brute-force emulator debugging method is to output a trace from a known good emulator, e.g. using AppleWin's TF command, or adding a custom trace in the source code. Add an identical trace to your own emulator and diff them.

Cheers,
Nick.
Luigi Thirty
2017-08-05 07:34:31 UTC
Permalink
Raw Message
I converted my disk image to a .nib with Ciderpress. It turns out my nibble generator is failing for offsets 0x55 and 0x56 in a nibblized sector. Some poking with it showed that my routine that accounts for the rollover of the low bits isn't working right. In the encoded buffer, $100 isn't being set properly because I'm missing the checksum byte.

I'm confused about the encoding process though, specifically the checksum byte. I'm looking at a text file on DOS 3.3 encoding (http://mirrors.apple2.org.za/ground.icaen.uiowa.edu/MiscInfo/Dos33/dos.encoding). It says:

---

Suppose that you have read in (and translated) the 343 bytes (data
field + checksum) of a sector into a buffer with the beginning of the
buffer being represented by byte position HEX $000 and the end being
represented by byte position HEX $156. The buffer would have the
following appearance:

$000: 0 0 a7 a6 a5 a4 a3 a2
$001: 0 0 b7 b6 b5 b4 b3 b2
$002: 0 0 c7 c6 c5 c4 c3 c2
$003: 0 0 | | | | | |
$004: 0 0 | | | | | |
$005: 0 0 v v v v v v

$100: 0 0 < <

$150: 0 0 ^ ^ ^ ^ ^ ^
$151: 0 0 | | | | | |
$152: 0 0 | | | | | |
$153: 0 0 | | | | | |
$154: 0 0 | | | | c0 c1
$155: 0 0 | | | | b0 b1
$156: 0 0 | | | | a0 a1

---

256 / 3 = 85 1/3... but we have 86 low bytes to store. Where does the checksum bit specifically fit into this? That seems to be the missing piece.

The source code in question is here: https://github.com/Luigi30/FruitMachine-Swift/blob/master/FruitMachine/AppleII/Peripherals/DiskII/DiskImage.swift

It's kind of a mess since I'm actively writing the disk image handler. encodeDos33Track() takes in a .do/.dsk disk image and a track number and encodes the data for the Disk II controller, creating an encoded track as described in Beneath Apple DOS. EncodeSectorSixAndTwo takes in the raw binary data and spits out the 342-byte encoded sector to be inserted into the track.
qkumba
2017-08-05 15:43:53 UTC
Permalink
Raw Message
Those two bytes are very special. They are unused during nibble decoding, so DOS provides bits from offsets 0 and 1 to fill the gaps and give constant decoded values.
Nick Westgate
2017-08-05 21:19:24 UTC
Permalink
Raw Message
Post by Luigi Thirty
I'm looking at a text file on DOS 3.3 encoding
I don't have time to look right now, but here's code I write some years back that is (IIRC) a direct port of the DOS 3.3 prenibble routine. You could add some logging to print the exact mapping, which are details I forget.

y = 2;
do // fill buffers
{
x = 0;
do
{
a = Data[sectorOffset + --y];
_secondaryBuffer[x] = (byte)((_secondaryBuffer[x] << 2) | SwapBits[a & 0x03]); // b1,b0 -> secondary buffer
_primaryBuffer[y] = (byte)(a >> 2); // b7-b2 -> primary buffer
}
while (++x < SecondaryBufferLength);
}
while (y != 0);

It's from:
https://github.com/DigitalJellyfish/Virtu/blob/master/Virtu/DiskDsk.cs

Cheers,
Nick.
Luigi Thirty
2017-08-06 07:19:34 UTC
Permalink
Raw Message
Post by Nick Westgate
Post by Luigi Thirty
I'm looking at a text file on DOS 3.3 encoding
I don't have time to look right now, but here's code I write some years back that is (IIRC) a direct port of the DOS 3.3 prenibble routine. You could add some logging to print the exact mapping, which are details I forget.
y = 2;
do // fill buffers
{
x = 0;
do
{
a = Data[sectorOffset + --y];
_secondaryBuffer[x] = (byte)((_secondaryBuffer[x] << 2) | SwapBits[a & 0x03]); // b1,b0 -> secondary buffer
_primaryBuffer[y] = (byte)(a >> 2); // b7-b2 -> primary buffer
}
while (++x < SecondaryBufferLength);
}
while (y != 0);
https://github.com/DigitalJellyfish/Virtu/blob/master/Virtu/DiskDsk.cs
Cheers,
Nick.
Thank you! I was able to fix my prenibble code and get the DOS 3.3 master disk booting just fine. Now I just need to hook up actually writing out to the disk image and I'll be set ;)

private func SixAndTwoPrenibblize(sector: [UInt8]) -> [UInt8] {
//Create a nibblized 342-byte buffer from a 256-byte sector.
var nibblized: [UInt8] = [UInt8](repeating: 0x00, count: 342)
var secondaryShift = 0
for (i, byte) in (0x00 ..< 0x100).enumerated() {
nibblized[byte] = sector[byte] >> 2

let secondaryOffset = 0x100 + (0x55 - (i % 0x56))
nibblized[secondaryOffset] |= GetSwappedLowBits(byte: sector[byte]) << secondaryShift

if(secondaryOffset == 0x100) {
secondaryShift += 2
}
}
return nibblized
}

Loading...