Back in May of 2007 Philip Pemberton asked on the classic computer mailing list for some diagnostic information from single density floppy disks. He said he was building custom hardware to read floppy disks and needed some data.
He was specifically looking for raw Catweasel output. The testhist command from cw2dmk can record this information to a file.
This presented a opportunity for me. Though I didn’t own any single density disks I did own a computer that could create, read and write them. The CoCo hardware supported it, but I needed to modify the software to achieve it.
The Color Computer floppy disk card by Tandy/Radio Shack (and third party compatible cards) did have a density select register that it passed to the flopper disk controller. But that bit also controlled a second function in the card: the NMI enable flag. The card used the MPU’s NMI interrupt to signal the end of a disk operation. This, used in combination with a separate halt flag, allowed the CoCo’s read/write software kernel to be very compact and fast. Fast enough to allow the CoCo to access double density disk’s reliably despite the 0.89 Mhz clock speed of the MPU.
In double density/Halt/NMI mode the CoCo’s MPU would pass a byte to/from the floppy disk controller and this would cause it to trigger the halt line on the MPU. When the controller was ready for another byte it would release the MPU from its halt mode and another byte would be passed. This would continue until the end of the transfer when an NMI interrupt was triggered. This is what would stop the MPU’s data passing loop.
In single density mode the NMI functionality in the floppy card is disabled. But there should be sufficient time to poll the controller for the interrupt request signal. I was not sure how to write such a loop. After a few unsuccessful attempts I gave up. I had heard the Flex operating system started life using a single density disk format and decided to try to find an example single density read/write loop from that operating system. It turns out the Flex users group has a lot of information on-line and it was relatively easy to find the loop I was looking for. I discovered the software loop polled for both the interrupt request and data request signals. So even the halt functionality of the card wasn’t needed in single density mode.
So now it was time to write a patch for RSDOS to support single density. Since this was only an experiment I decided to only patch the lower levels of RSDOS: DSKINI and DSKCON. This is the code that actually does data transfer. RSDOS also contains higher level code for reading/writing files (both executable and data). I did not modify them. Trying to read and write files after this patch will cause errors.
First I had to decide on the on disk sector layout. I choose 9 256 byte sectors, numbered 1 thru 9, on each track for 35 tracks. The I wrote the following program which patches RSDOS version 1.1:
org $7000 * patch DSKINI for SD
Change the loop constructs in DSKINI to end at sector 9 instead of 18.
lda #$09 sta $d5c5 sta $d5c9 sta $d5d2 sta $d67b sta $d6bc
Change track start from 32 bytes of 0x4E to 32 bytes of 0xFF
lda #$ff sta $d696
Change track end from 200 bytes of 0x4E to 200 bytes of 0xFF
New track table ends with 8 blocks not 9.
lda #$08 sta $d6b5
Insert the address of the new track table into DSKINI.
ldd #nt std $d6a4
Change program to jump to our new DSKINI write routine.
lda #$7e sta $d646 ldd #write std $d647
Now we patch DSKCON.
OR in single density (instead of double density) mode
* patch DSKCON for SD lda #$0 sta $d775
Don’t turn on halt mode
lda #$00 sta $d840
Patch DSKCON to jump to new write and read loops
lda #$7e sta $d86b sta $d881 ldd #wdsk std $d86c ldd #rdsk std $d882 rts
New writing routine for DSKINI
write anda #$4f sta $ff40 Turn off halting, double density and NMI w0 ldb ,x+ Read byte from track buffer w1 lda $ff48 Get FDC status rora Roll busy bit into carry flag bcc w3 If not busy, then branch out of loop rora Roll data request bit into carry flag bcs w2 If ready for data, branch to write instruction bra w1 Branch back to test status again. w2 stb ,y Write data to FDC bra w0 Branch back to get new byte w3 jmp $d64f done, jmp back to DSKINI
New writing routine for DSKCON
wdsk ldb ,x+ Load byte from transfer buffer stb $ff4b Write it to FDC wd1 lda ,u Get status rora Roll busy bit into carry flag lbcc $d88b If not busy, branch to end loop rora Roll data request bit into carry flag bcs wdsk If data requested, get new byte from transfer buffer bra wd1 Brach to check status again
New reading routine for DSKCON
rdsk ldb $ff4b Load byte from FDC stb ,x+ Store byte to transfer buffer rd1 lda ,u Get status rora Roll busy bit into carry flag lbcc $d88b If not busy, branch to end loop rora Roll data request bit into carry flag bcs rdsk If data requested, get new byte from transfer buffer bra rd1 Brach to check status again
New disk format table
nt fcb 3, $0 Sync field fcb 3, $0 fcb 1, $FE ID Address Mark * Track, side and sector numbers are inserted here fcb 1, 1 Sector Size (256 byte sectors) fcb 1, $f7 CRC Request fcb 11, $ff GAP II (Post-ID Gap) fcb 6, $0 Sync Field fcb 1, $fb Data Address Mark (AM2) fcb 0, $ff Data Field (256 bytes) fcb 1, $f7 CRC Request fcb 27, $ff Gap III (Post Gap Data) end
Running the above patch will allow you to use BASIC’s DSKINI command to format a single density disk. And use BASIC’s DSKI$ and DSKO$ to read and write sectors.