;
;  MDBL PROM (used with 88-PMC)
;
;  The MDBL PROM allows loading of Minidisk BASIC by examining its
;  starting location (highest 256 byte block 0FF00H), selecting
;  the desired I/O device by setting the sense switches and pressing
;  the RUN switch on the Altair 8800 front panel.  Altair Minidisk
;  BASIC will then load and respond with the initialization dialog.
;
;  

PROMADDR    EQU   0FF00H            ;Address of MDBL Prom
MDBLADDR    EQU   4C00H             ;Execution address of MDBL

; Altair 8800 Mini Disk Controller Interface

ENABLE      EQU   08H               ;Drive Enable Port (W)
STATUS      EQU   08H               ;Drive\Controller Status Port (R)
CONTROL     EQU   09H               ;Drive Control Port (W)
SECTORPOS   EQU   09H               ;Sector Position Port (R)
DATA        EQU   0AH               ;Disk Data (R/W)

;Drive/Controller Status Masks
ENWD        EQU   01H               ;Enter New Write Data (Active Low)
MH          EQU   02H               ;Move Head (Active Low)
HS          EQU   04H               ;Head Status (Active Low)
DRVRDY      EQU   08H               ;Drive Ready (Active Low)
INTE        EQU   20H               ;Interrupts Enabled (Active Low)
TRACK0      EQU   40H               ;Track 00 (Active Low)
NRDA        EQU   80H               ;New Read Data Available (Active Low)

; Drive Control Values
STEPIN      EQU   01H               ;Step-In to a higher numbered track
STEPOUT     EQU   02H               ;Step-Out to a lower numbered track
TMRRESET    EQU   04H               ;Reset the 6.4 sec disk disable timer
INTENBL     EQU   10H               ;Enable start of sector interrupt
INTDISABLE  EQU   20H               ;Disable interrupts
WRITEENBL   EQU   80H               ;Enable drive write circuits


; Due to the slow access times of 1702A PROM memories, we copy the
; actual Mini Disk Boot Loader (MDBL) code into RAM before trying
; to access the disk drive.

            ORG   PROMADDR

BOOTCPY:    LD    HL,MDBLIMG        ;HL -> ROM address of MDBL
            LD    DE,MDBLADDR       ;DE -> Execution address of MDBL
            LD    C,MDBLEND - MDBLSTART
BTCPYLP:    LD    A,(HL)            ;Get next program byte from PROM
            LD    (DE),A            ;Store it in RAM
            INC   HL                ;Bump PROM address
            INC   DE                ;Bump RAM address
            DEC   C                 ;More program data to copy?
            JP    NZ,BTCPYLP        ;Yes, loop
            JP    MDBLENTRY         ;Nope, go run the boot program

MDBLIMG:

            ORG   MDBLADDR

MDBLSTART:

MDBLENTRY:  DI                      ;Turn off INTE LED (i.e. no errors yet)
            XOR   A
            OUT   (22H),A           ;4PIO init
            CPL
            OUT   (23H),A
            LD    A,2CH
            OUT   (22H),A
            LD    A,03H             ;Reset UART on 2SIO/Turnkey (6850)
            OUT   (10H),A
            IN    A,(0FFH)          ;Read the sense switches
            AND   10H
            RRCA
            RRCA
            ADD   A,11H
            OUT   (10H),A
            LD    SP,4D71H          ;Initialize the stack
ENBLDRV:    XOR   A                 ;Enable drive zero
            OUT   (ENABLE),A
            IN    A,(STATUS)        ;Read drive status
            AND   DRVRDY            ;Diskette in drive?
            JP    NZ,ENBLDRV        ;Nope, wait for drive ready
            LD    A,TMRRESET        ;Reset the 6.4 second timer
            OUT   (CONTROL),A
            JP    TRK00CHK          ;See if we need to restore the head

WAITHEAD1:  IN    A,(STATUS)        ;Allowed to move disk head?
            AND   MH
            JP    NZ,WAITHEAD1      ;Nope, wait for head to settle
            LD    A,STEPOUT         ;Issue STEP-OUT command
            OUT   (CONTROL),A
TRK00CHK:   IN    A,(STATUS)        ;Read drive status
            AND   TRACK0            ;Are we at track 00?
            JP    NZ,WAITHEAD1      ;Nope, step-out again
            LD    DE,0000H          ;DE = load address

; Here we will read in all 16 sectors on the current track

READTRK:    LD    B,00H             ;B = Sector Id to load
READSECT:   LD    A,10H             ;Set the number of retries to 16
RETRYSECT:  PUSH  AF                ;Stack the retry count remaining
            PUSH  DE                ;Stack the load address
            PUSH  BC                ;Stack the current sector id
            PUSH  DE                ;Stack the load address
            LD    DE,8086H          ;E = bytes/sector + header/trailer bytes
            LD    HL,4CE3H          ;HL -> Sector buffer
FNDSECTOR:  IN    A,(CONTROL)       ;Read the sector position
            RRA                     ;Put the first 30us flag in carry
            JP    C,FNDSECTOR       ;Go if not in the first 30us
            AND   1FH               ;Mask off unused bits
            CP    B                 ;Found the desired sector?
            JP    NZ,FNDSECTOR      ;Nope, wait for it
READDATA:   IN    A,(STATUS)        ;Read the drive status
            OR    A                 ;Test for New Read Data Available
            JP    M,READDATA        ;Nope, wait for data
            IN    A,(DATA)          ;Read the data byte from the controller
            LD    (HL),A            ;Store it in the sector buffer
            INC   HL                ;Move to next buffer address
            DEC   E                 ;Decriment count of bytes/sector
            JP    NZ,READDATA       ;Loop if more sector data
            POP   HL                ;Get current load address
            LD    DE,4CE6H          ;DE -> Sector data in sector buffer
            LD    BC,0080H          ;B = Checksum, C = data count
COPYDATA:   LD    A,(DE)            ;Get byte of data from sector buffer
            LD    (HL),A            ;Store it at the destination
            CP    (HL)              ;Did it store correctly?
            JP    NZ,MEMERR         ;Nope, abort with memory error "M"
            ADD   A,B               ;Add current checksum to data byte
            LD    B,A               ;Save the updated checksum
            INC   DE                ;Bump the sector buffer pointer
            INC   HL                ;Bump the destination pointer
            DEC   C                 ;Are there more data bytes to copy?
            JP    NZ,COPYDATA       ;Yes, loop
            LD    A,(DE)            ;Get marker byte
            CP    0FFH              ;Is it a checksum marker?
            JP    NZ,BADCHKSUM      ;Nope, handle bad checksum
            INC   DE                ;yes, point to checksum
            LD    A,(DE)            ;Get the sector's checksum
            CP    B                 ;Compare with the computed checksum
BADCHKSUM:  POP   BC                ;Restore sector id to load
            EX    DE,HL             ;DE = current load address
            JP    NZ,READERR        ;Go if a checksum error
            POP   AF                ;Remove sector load address from stack
            POP   AF                ;Remove retry count from stack
            LD    HL,(4CE4H)
            CALL  CP_DEHL
            JP    NC,LOADDONE       ;Go if the entire program is loaded
            INC   B                 ;Our interleave is 2 so bump the sector
            INC   B                 ;id by two.
            LD    A,B               ;Have we read half of the track?
            CP    10H
            JP    C,READSECT        ;Nope, go read next sector
            LD    B,01H             ;Prepare to read the odd sectors
            JP    Z,READSECT        ;Go if we need to read the odd sectors
WAITHEAD2:  IN    A,(STATUS)        ;Are we allowed to move the head?
            AND   MH
            JP    NZ,WAITHEAD2      ;Nope, wait for the head to settle
            LD    A,STEPIN          ;Yes, step to the next track
            OUT   (CONTROL),A
            JP    READTRK           ;And go read it in
            
LOADDONE:   LD    A,80H             ;Disable the Minidisk system
            OUT   (ENABLE),A
            JP    0000H             ;Execute the loaded program
            
READERR:    POP   DE                ;Restore current sector load address
            POP   AF                ;Restore retry count
            DEC   A                 ;Any more retries left?
            JP    NZ,RETRYSECT       ;Yes, try reading it again
            LD    A,'C'             ;Nope, Signal Checksum error 'C'
            DEFB  01H               ;Hack to ignore next opcode
MEMERR:     LD    A,'M'             ;Signal Memory Error "M"
            EI                      ;Signal error by lighting the INTE LED
            LD    (0000H),A         ;Store the error code
            LD    (0001H),HL        ;Store the bad address
            LD    B,A               ;Save the error code
            LD    A,80H             ;Disable the Minidisk system
            OUT   (ENABLE),A
            LD    A,B               ;Get the error code
DISPERR:    OUT   (01H),A           ;Output to the SIO
            OUT   (11H),A           ;Output to the 2SIO/Turnkey
            OUT   (05H),A           ;Output to the PIO
            OUT   (23H),A           ;Output to the 4PIO
            JP    DISPERR           ;Keep printing error code

; Subroutine to compare the DE register pair to the HL register pair
;
; Z flag is set if DE == HL
; C flag is set if DE <  HL
            
CP_DEHL:    LD    A,D               ;Get the MSB of DE
            CP    H                 ;Is it equal to the MSB of HL?
            RET   NZ                ;Nope, return
            LD    A,E               ;Get the LSB of DE
            CP    L                 ;Is it equal to the LSB of HL?
            RET                     ;Return with flags set

MDBLEND:
            NOP
            NOP
            NOP
            NOP
            NOP
            NOP
            NOP
            NOP
            NOP
            NOP
            
            
            END

