Single Density Disks

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
sta $d6c1
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.