• Please review our updated Terms and Rules here

Where can I find bare metal IIe floppy access code?

hjalfi

Experienced Member
Joined
Feb 11, 2017
Messages
265
Location
Zürich, Switzerland
I would like to port my 6502 CP/M port to the Apple IIe. Rather than running the whole thing on top of DOS, I want it to run bare metal. The plan is to bank out the ROM and replace it with RAM instead, so that I'm not using any ROM code. This allows me to use all of zero page, as well as do what I like with the 6502 vectors.

This, obviously, means I have to reimplement all the OS services the ROM would normally provide. Including the weird code for accessing the floppy drive.

I gather that this code has been rewritten --- apparently even with bugfixes! --- for precisely this purpose; an OS can boot from floppy, load in the replacement floppy code, map the ROM out, and continue loading using its own code. Does anyone know where I can find this?

I want to be able to read and write individual sectors in the standard Apple IIe format (i.e. 256 byte GCR), so track-at-a-time reading and writing isn't any use to me.

Thanks!
 
That last looks perfect --- although I did find prorwts2 (https://github.com/peterferrie/prorwts2/blob/master/PRORWTS2.S) which seems to have better comments. Like, I was finally able to find the GCR table! I was really confused in the original because I couldn't find anything which seemed to be converting between GCR and real data...

Actually adapting it to my own code looks tough, though. It's all so highly integrated and optimised that pulling out just the sector r/w code is going to be difficult. Especially as I have to switch assemblers...
 
The James Sather books on "Understanding the Apple II(e)" is my go-to reference on the workings of the disk controller. It was good enough that I was able to prototype a PC-based read/write card to handle the floppies.
(I have never owned an Apple II of any stripe, so take this cum grano salis)
 
I do have it now working, at least for reads. Because it's CP/M with 128-byte records I initially tried deblocking but this was excruciatingly slow --- the extra overhead after each sector meant I would miss the next one, resulting in one revolution per sector. I eventually switched to track-at-a-time buffering; I even had a spare 4kB of memory I couldn't use for anything else to put the track in. The result is impressively fast.

Of course, that's going to make life harder for writing, which is the next, and much harder task as the timing needs to be precise...
 
It lives!

Writing the write code was horrible. Darn you, Woz, darn you to heck. Everything having to precisely cycle accurate made things so much harder. Also, it's a good thing CP/M doesn't need interrupts, isn't it?

But the result works well and has the fastest disk system so far (mainly due to the 4kB disk buffer). It's got a pretty respectable 46kB TPA and lots of zero page. It'll assemble small programs at about 2kB/s and large ones at about 1kB/s --- the O(n) symbol lookup code in my assembler hurts here...


1681596076996.png
 
Well you can't get more linear free memory than that on an Apple II. What sector interleave did you end up using?
 
Belatedly: standard ProDOS. Turns out the disk image tooling is kinda poor and the choices are either ProDOS or AppleDOS. It used track-at-a-time buffering and is capable of reading and writing a track in two revolutions, so the disk system is super fast.
 
Here's a snippet of code I wrote a long time ago when I was decoding various Catweasel dumps:

Code:
// decode 13-sector nibbles
void post_nibble_13(void)
{
    int i,j;

    i = 0;
    for (j=0; j<51; j++)
    {
        buf[i++] = (secbuf[204 - j] << 3) |  (secbuf[103 + j] >> 2);
        buf[i++] = (secbuf[255 - j] << 3) |  (secbuf[ 52 + j] >> 2);
        buf[i++] = (secbuf[306 - j] << 3) |  (secbuf[  1 + j] >> 2);
        buf[i++] = (secbuf[357 - j] << 3) | ((secbuf[103 + j] & 0x02) << 1 )
                                          |  (secbuf[ 52 + j] & 0x02)
                                          | ((secbuf[  1 + j] & 0x02) >> 1 );
        buf[i++] = (secbuf[408 - j] << 3) | ((secbuf[103 + j] & 0x01) << 2 )
                                          | ((secbuf[ 52 + j] & 0x01) << 1 )
                                          |  (secbuf[  1 + j] & 0x01);
    }
    buf[255] = (secbuf[409] << 3) | (secbuf[0] & 0x07);
}


// decode 16-sector nibbles
void post_nibble_16(void)
{
    int i,j,k,n;

    j=0;
    k=0;

    for (i=0; i<256; i++)
    {
        n = secbuf[j++] >> k;
        buf[i] = (secbuf[i+86] << 2) | ((n << 1) & 2) | ((n >> 1) & 1);
        if (j>85) { j=0; k=k+2; }
    }
}

And here's a diagram of the bit ordering for both 13 and 16 sector data. If you only looked at 16-sector encoding, you didn't even see the worst of it all. The 13 sector layout is super crazy.

Code:
13-sector nybblization:

  0    0  FF.2 FF.1 FF.0 =   0

02.2 02.1 02.0 03.0 04.0 =   1
07.2 07.1 07.0 08.0 09.0 =   2
...
F7.2 F7.1 F7.0 F8.0 F9.0 =  50
FC.2 FC.1 FC.0 FD.0 FE.0 =  51

01.2 01.1 01.0 03.1 04.1 =  52
06.2 06.1 06.0 08.1 09.1 =  53
...
F6.2 F6.1 F6.0 F8.1 F9.1 = 101
FB.2 FB.1 FB.0 FD.1 FE.1 = 102

00.2 00.1 00.0 03.2 04.2 = 103
05.2 05.1 05.0 08.2 09.2 = 104
...
F5.2 F5.1 F5.0 F8.2 F9.2 = 152
FA.2 FA.1 FA.0 FD.2 FE.2 = 153

FA.7 FA.6 FA.5 FA.4 FA.3 = 154
F5.7 F5.6 F5.5 F5.4 F5.3 = 155
 ...
05.7 05.6 05.5 05.4 05.3 = 203
00.7 00.6 00.5 00.4 00.3 = 204

FB.7 FB.6 FB.5 FB.4 FB.3 = 206
F6.7 F6.6 F6.5 F6.4 F6.3 = 205
...
06.7 06.6 06.5 06.4 06.3 = 254
01.7 01.6 01.5 01.4 01.3 = 255

FC.7 FC.6 FC.5 FC.4 FC.3 = 256
F7.7 F7.6 F7.5 F7.4 F7.3 = 257
...
07.7 07.6 07.5 07.4 07.3 = 305
02.7 02.6 02.5 02.4 02.3 = 306

FD.7 FD.6 FD.5 FD.4 FD.3 = 307
F8.7 F8.6 F8.5 F8.4 F8.3 = 308
...
08.7 08.6 08.5 08.4 08.3 = 356
03.7 03.6 03.5 03.4 03.3 = 357

FE.7 FE.6 FE.5 FE.4 FE.3 = 358
F9.7 F9.6 F9.5 F9.4 F9.3 = 359
...
09.7 09.6 09.5 09.4 09.3 = 407
04.7 04.6 04.5 04.4 04.3 = 408

FF.7 FF.6 FF.5 FF.4 FF.3 = 409


16-sector nybblization:

AC.0 AC.1 56.0 56.1 00.0 00.1 =   0
AD.0 AD.1 57.0 57.1 01.0 01.1 =   1
...
FE.0 FE.1 A8.0 A8.1 52.0 52.1 =  82
FF.0 FF.1 A9.0 A9.1 53.0 53.1 =  83
00.0 00.1 AA.0 AA.1 54.0 54.1 =  84
01.0 01.1 AB.0 AB.1 55.0 55.1 =  85

00.7 00.6 00.5 00.4 00.3 00.2 =  86
01.7 01.6 01.5 01.4 01.3 01.2 =  87
...
FE.7 FE.6 FE.5 FE.4 FE.3 FE.2 = 340
FF.7 FF.6 FF.5 FF.4 FF.3 FF.2 = 341
 
I actually have working read and write support for Apple II disks in FluxEngine --- but I will admit that after staring at the documentation for a while I did just copy MAME's implementation of the nibblisation/denibblisation code. It's interesting how much clearer the actual 6502 implementation is; all those weird magic numbers are all just a consequence of the way it's implemented...
 
I'd love to give this a go on my //c but perhaps not so much as to install llvm-mos and all the trimmings. Echoing a cowlark.com commenter: any chance of sharing disk images? :)
 
Oh excellent! I tried it immediately. https://photos.app.goo.gl/yFzwGDEvqouXTeXe6

Results on this particular //c with a Floppy Emu were mixed --- occasionally it failed to boot with "Check Disk Drive"; other times it would get stuck at the CP/M-65 banner, attempting to read from Track 2 if memory serves. DIR worked fine; STAT seemed erratic: sometimes STAT DSK: would report "no disk" or similar, but when it had more to say it would CR but not LF.

I'm not sure where the fault lies, but for now it's still a delightful juxtaposition to achieve on the //c without special hardware. Thanks!
 
Damn, I was worried about that. Clearly the timing of the real thing isn't the same as the timing of the emulators I was using (MAME and AppleWin). Although, I'd expect problems to show up in writes rather than reads, given that reads are self-clocking from the data on the disk. See https://github.com/davidgiven/cpm65/blob/master/src/bios/apple2e.S#L599 for the relevant code. It's good to see it on real hardware, though!

(Although... are there any compatibility issues with the IIc vs the IIe?)

The issue with stat is a bug in some of my llvm-mos tooling; the fix has been upstreamed but is waiting on a release at their end.
 
I'm not sure how much to trust my IIc-and-FloppyEmu setup, but I haven't noticed trouble with other software.

I did some searching and couldn't find many reports of differences between the two machines besides the most obvious one (lack of IIc expansion slots). Nothing reported any changes with disk I/O.

There were several versions of the IIe of course. If your emulator emulates a 6502-based machine, then maybe the 65c02 in the IIc and later IIes might cause trouble somehow? Other differences don't really seem related, but my understanding is pretty superficial!
 
Back
Top