zx128: first pieces towards a disciple disk driver
authorAlan Cox <alan@linux.intel.com>
Thu, 12 Feb 2015 23:21:32 +0000 (23:21 +0000)
committerAlan Cox <alan@linux.intel.com>
Thu, 12 Feb 2015 23:21:32 +0000 (23:21 +0000)
Lots of work needed yet.

Kernel/platform-zx128/disciple.s [new file with mode: 0644]

diff --git a/Kernel/platform-zx128/disciple.s b/Kernel/platform-zx128/disciple.s
new file mode 100644 (file)
index 0000000..1bd7300
--- /dev/null
@@ -0,0 +1,375 @@
+;
+;      Supporting routines for the DISCiple floppy controller
+;
+;      This a WD177x controller coupled to an extra magic switching ROM
+;      chip with its own RAM. From our perspective the other bits don't
+;      matter just the hardware
+;
+;      The timings for the WD1770 on the spectrum are such that
+;      we can't check all the bits each loop. We must also be in
+;      an uncontended bank (even numbered) eg _CODE
+;
+;      Ports
+;      0x1B - status/cmd
+;      0x5B - track
+;      0x9B - sector
+;      0xDB - data
+;
+;      Also uses
+;      0xFB - printer data
+;      0xBB - read to page in DISCiple ROM/RAM, write to page out
+;              *don't do this with IRQ's on for FUZIX*
+;      0x7B - read puts the disciple ROM in the low 8K, write puts the
+;             RAM there (the other of the pair lives at 8K)
+;
+;      0x1F - control (w/o)
+;              bit 0: drive 0/1 (1 = drive 0)
+;              bit 1: side 0/1 (1 = side 1)
+;              bit 2: SD/DD (1 = SD?)
+;              bit 3-5: interface paging control
+;              bit 6: printer strobe
+;              bit 7: network
+;
+;      Commands are like other WD177x with the usual variation
+;      0x78 - step out (for 0 keep stepping out until give up or zero set)
+;      0x58 - step in
+;      0xA0 - write + 0x02 for spin up etc, + 0x08? for precomp
+;      0xD0 - reset (then wait about 1mS)
+;
+
+       .globl _fd_reset
+       .globl _fd_operation
+       .globl _fd_motor_on
+       .globl _fd_motor_off
+       .globl _fd_selected
+       .globl _fd_tab
+       .globl _fd_cmd
+
+FDCREG .equ    0x1B
+FDCTRK .equ    0x5B
+FDCSEC .equ    0x9B
+FDCDATA        .equ    0xDB
+
+FDCCTRL        .equ    0x1F
+
+
+               .area _CODE
+;
+;      The timings on this are ugly and we don't get to do a sanity
+;      length check as would be nice. We may need to double buffer user
+;      copies as our common is contended memory.
+;
+;      FIXME: save original HL - subtract and compare with sector size
+;
+;      Entry: HL = input buffer (sector size)
+;      Exit: Z = ok, NZ A = error or 0 for short
+;      Uses: AF, BC, HL
+;
+;
+;      Timeout useful ?
+;
+cmd1772:
+       in a, (0x1B)            ; read status
+       bit 0, a
+       jr nz, cmd1772
+       ld a,b
+       out (0x1B),a            ; write the CMD register
+       ld b, #32
+cmd1772w:
+       djnz cmd1772w           ; fixme - reduce for DD (test and use 30/15)
+       ret
+
+;
+;      interrupt register reports 0x80 for interrut, 0x40 for drq
+;      (0x20 is the unrelated reset button)
+;
+
+;
+;      Structures we use
+;
+;
+;      Per disk structure to hold device state
+;
+TRKCOPY        .equ    0
+
+;
+;      Command issue
+;
+CMD    .equ    0
+TRACK  .equ    1
+SECTOR .equ    2
+DIRECT .equ    3               ; 0 = read 2 = write 1 = status
+DATA   .equ    4
+
+;
+;      Wait for the drive controller to become ready
+;      Preserve HL, DE
+;
+waitdisk:
+       ld      bc, #0
+waitdisk_l:
+       in      a, (FDCREG)
+       bit     0, a
+       ret     z
+       ;
+       ;       Keep poking fdcctrl to avoid a hardware motor timeout
+       ;
+       djnz    waitdisk_l
+       dec     c
+       jr      nz, waitdisk_l
+       ld      c, #0xD0        ; reset
+       call    cmd1772
+       in      a, (FDCREG)             ; read to reset int status
+       bit     0, a
+       ret
+;
+;      Set up and perform a disk operation
+;
+;      IX points to the command block
+;      HL points to the buffer
+;      DE points to the track reg copy
+;
+;      The caller is responsible for setting up the drive and side bits
+;      and the precomp fieeld in write commands (tracks > 64 for 80 or
+;      > 32 for 40 track drives)
+;
+fdsetup:
+       ld      a, (de)
+       out     (FDCTRK), a
+       cp      TRACK(ix)
+;      jr      z, fdiosetup            ; FIXME
+
+       ;
+       ;       So we can verify
+       ;
+       ld      a, TRACK(ix)
+       out     (FDCDATA), a
+       ld      a, SECTOR(ix)
+       out     (FDCSEC), a
+       ;
+       ;       Need to seek the disk
+       ;
+       ld      b, #0x18        ; seek   (18 or 10 ?)
+       call    cmd1772
+       call    waitdisk
+       jr      nz, setuptimeout
+       and     #0x18           ; error bits
+       jr      z, fdiosetup
+       ; seek failed, not good
+setuptimeout:                  ; NE = bad
+       ld      a, #0xff        ; we have no idea where we are, force a seek
+       ld      (de), a         ; zap track info
+       ret
+;
+;      Head in the right place
+;
+fdiosetup:
+       ld      a, TRACK(ix)
+       ld      (de), a         ; save track
+       ld      a, SECTOR(ix)
+       out     (FDCSEC), a
+       in      a, (FDCREG)     ; Clear any pending status
+
+       ld      c, CMD(ix)
+
+       ld      de, #0          ; timeout handling
+       
+       call    cmd1772
+
+       ld      a, DIRECT(ix)
+       dec     a
+       jr      z, fdio_in
+       jr      nc, fdio_out
+;
+;      Status registers
+;
+fdxferdone:
+       ei
+fdxferdone2:
+       in      a, (FDCREG)
+       ; FIXME: mask varies by type - WP on write etc
+       and     #0x19           ; Error bits + busy
+       bit     0, a            ; Wait for busy to drop, return in a
+       ret     z
+       jr      fdxferdone2
+;
+;      Read from the disk - HL points to the target buffer
+;
+fdio_in:
+       ld bc, #0xDB            ; 256 count, port DB (data of 1770)
+       jr sector_idrq
+sector_byte:
+       ini                     ; 16
+       nop                     ; 4  the ROM does - not clear why
+sector_idrq:
+       in a,(0x1B)             ; 11 like all spectrum hw ports splattered
+                               ;    all over
+       bit 1,a                 ; 8
+       jr nz, sector_byte      ; 7 / 12 if taken
+       in a,(0x1B)             ; 11 like all spectrum hw ports splattered
+                               ;    all over
+       bit 1,a                 ; 8
+       jr nz, sector_byte      ; 7 / 12 if taken
+       in a,(0x1B)             ; 11 like all spectrum hw ports splattered
+                               ;    all over
+       bit 1,a                 ; 8
+       jr nz, sector_byte      ; 7 / 12 if taken
+       in a,(0x1B)             ; 11 like all spectrum hw ports splattered
+                               ;    all over
+       bit 1,a                 ; 8
+       jr nz, sector_byte      ; 7 / 12 if taken
+       
+       ;
+       ; We should have had bits by now
+       ;       
+
+       bit 0,a                 ; 8
+       jr nz, sector_idrq      ; 7 / 12 if taken
+
+       ;
+       ;       Gone non busy
+       ;
+       and     #0x1C
+       jr      fdxferdone
+
+;
+;      Read from the disk - HL points to the target buffer
+;
+fdio_out:
+       ld bc, #0xDB            ; 256 count, port DB (data of 1770)
+       jr sector_odrq
+sector_obyte:
+       outi                    ; 16
+       nop                     ; 4  the ROM does - not clear why
+sector_odrq:
+       in a,(0x1B)             ; 11 like all spectrum hw ports splattered
+                               ;    all over
+       bit 1,a                 ; 8
+       jr nz, sector_obyte     ; 7 / 12 if taken
+       in a,(0x1B)             ; 11 like all spectrum hw ports splattered
+                               ;    all over
+       bit 1,a                 ; 8
+       jr nz, sector_obyte     ; 7 / 12 if taken
+       in a,(0x1B)             ; 11 like all spectrum hw ports splattered
+                               ;    all over
+       bit 1,a                 ; 8
+       jr nz, sector_obyte     ; 7 / 12 if taken
+       in a,(0x1B)             ; 11 like all spectrum hw ports splattered
+                               ;    all over
+       bit 1,a                 ; 8
+       jr nz, sector_obyte     ; 7 / 12 if taken
+       
+       ;
+       ; We should have done the bits by now
+       ;       
+
+       bit 0,a                 ; 8
+       jr nz, sector_idrq      ; 7 / 12 if taken
+
+       ;
+       ;       Gone non busy
+       ;
+       and #0x5C               ; error bits and WP bit (to us an error)
+       jr nz, fdxferdone
+       xor a                   ; 256 bytes ??
+       cp b
+       jr z, fdxferdone
+fdxferbad:
+       ld      a, #0xff
+       ret
+
+;
+;      C glue interface.
+;
+;      Because of the brain dead memory paging we dump the bits into
+;      kernel space always. The thought of taking an NMI while in the
+;      user memory and bank flipping to recover is just too odious !
+;
+
+;
+;      Reset to track 0, wait for the command then idle
+;
+;      fd_reset(uint8_t *drvptr)
+;
+_fd_reset:
+       pop     bc
+       pop     de
+       pop     hl
+       push    hl
+       push    de
+       push    bc
+       ld      a, #1
+       out     (FDCSEC), a
+       xor     a
+       out     (FDCTRK), a
+       ld      c, #0x0C
+       call    cmd1772
+       ld      a, #0xFF
+       ld      (hl), a         ; Zap track pointer
+       call    waitdisk
+       cp      #0xff
+       ret     z
+       and     #0x99           ; Error bit from the reset
+       ret     nz
+       ld      (hl), a         ; Track 0 correctly hit (so 0)
+       ret
+;
+;      fd_operation(uint16_t *cmd, uint16_t *drive)
+;
+;      The caller must ensure the drive has been selected and the motor is
+;      running.
+;
+_fd_operation:
+       pop     hl              ; return address
+       pop     bc              ; padding
+       pop     de              ; drive track ptr
+       push    de
+       push    bc
+       push    hl
+       push    ix
+       ld      ix, #_fd_cmd
+       ld      l, DATA(ix)
+       ld      h, DATA+1(ix)
+       call    fdsetup         ; Set up for a command
+       ld      l, a
+       ld      h, #0
+       pop     ix
+       ret
+;
+;      C interface fd_motor_on(uint16_t drivesel)
+;
+;      Selects this drive and turns on the motors. Also pass in the
+;      choice of density
+;
+;      bits 0-3:       select that drive
+;      bit 4:          side (must rewrite each drive change)
+;      bit 5:          precompensation (not set here but in the I/O ops)
+;      bit 6:          synchronize I/O by stalling the CPU (don't set this)
+;      bit 7:          set for double density (MFM)
+;
+;
+_fd_motor_on:
+       ret
+
+;
+;      C interface fd_motor_off(void)
+;
+;      Turns off the drive motors, deselects all drives
+;
+_fd_motor_off:
+       ret
+
+curdrive:
+       .db     0xff
+motor_running:
+       .db     0
+fdcctrl:
+       .db     0
+fdc_active:
+       .db     0
+_fd_selected:
+       .db     0xFF
+_fd_tab:
+       .db     0xFF, 0xFF
+_fd_cmd:
+       .ds     6