        .TITLE  DISK CONTROLLER PROGRAM REV. E
;
;     *********************************
;     *                               *
;     * DISK DRIVE CONTROLLER PROGRAM *
;     *                               *
;     *********************************
;
;          BY BOB WARNE
;           APRIL 1979
;
;               DISASSEMBLED FROM HEX CODE &
;               MODIFIED BY IAN SHEPHARD AND AL MILLER  8-29-79
;
;               1. TO ALLOW HEAD SETTLING TIME AFTER HOME
;               2. FILL UNUSED ADDRESSES WITH ^H000
;
;
;
;
;
;     SOURCE ANNOTATIONS, COMMENTS, & EQUATES
;                    BY
;               DAVE STAUGAS
;
;                 26 JAN 81
;      DAVE'S MODS:
;
;    1. ALL MEMORY REFERENCES, FLAGS, & CONSTANTS USE LABELS
;    2. SOURCE MADE COMPATIBLE W/MAC65 ASSEMBLER
;    3. FORMAT ROUTINE NOW USES 18-BYTE SEC # TABLE FOR WRTRAK & VERIFY
;       MAXIMUM NUMBER OF BAD SECTORS RETURNED REDUCED FROM 63
;       TO 61 SINCE PAGE 0 VARIABLES ARE NEEDED (AN ACCEPTABLE TRADE-OFF).
;    4. ROUTINES MOVED AROUND FOR A MORE LOGICAL ORDER
;    5. RAM VARIABLES MOVED UP AND SCRUNCHED TOGETHER TO FREE UP
;       80 CONTIGUOUS BYTES OF RAM.
;    6. DRIVE DOES NOT SELECT AND HOME HEAD AT POWER-UP.  INSTEAD,
;       THIS FUNCTION IS PERFORMED AFTER RETURNING ACKNOWLEGEMENT
;       FOR THE RECEIPT OF THE FIRST COMMAND AFTER POWER-UP.
;    7. ERROR RETRY COUNT AND CONTANT FOR MOTOR SHUT-OFF TIMER ARE NOW
;       INITIALIZED AT POWER-UP IN RAM (TO ALLOW MODIFICATION VIA DOWNLOADED
;       SOFTWARE).  THE DEFAULT ERROR RETRY IS 3 & THE MOTOR SHUT-OFF
;       DEFAULT IS SHORTENED TO 3 SECONDS (DOWN FROM 7 IN REV B, C).
;    8. ERROR RECOVERY FROM RECORD NOT FOUND ERRORS MODIFIED SO THAT
;       A 1771 READ ADDRESS COMMAND IS ISSUED AND THE TRACK # READ
;       IN THE NEXT ENCOUNTERED ID FIELD (FOR THE TRACK UNDER THE HEAD)
;       REPLACES PRSTRK.  A RE-SEEK IS PERFORMED WITH THE NEW VALUE FOR PRSTRK.
;       THEN, THE RAM VARIABLE CONTAINING THE CURRENT TRACK NUMBER (PRSTRK, INVERTED)
;       IS CHECKED AGAINST THE STEPPER PHASE BITS FOR PROPER MOD 4 PHASE.
;       IF AN OUT OF PHASE CONDITION IS DETECTED, A "HARD HOME" IS PERFORMED
;       TO GUARANTEE PRSTRK VALIDITY.
;    9. THREE COMMANDS ADDED:
;
;          DOWNLOAD--ALLOWS COLLEEN TO SEND A 128-BYTE SUBROUTINE (ORG'D AT
;                    $0080) WHICH IS EXECUTED WITHIN THE CONTROLLER AS
;                    THE COMMAND.
;          READ-FROM-ADDRESS--A RECEIVE DATA COMMAND RETURNING A COPY OF
;                    OF THE INTERNAL CONTROLLER MEMORY STARTING AT
;                    ADDRESS GIVEN IN AUX1, AUX2.
;          USERCMD--A COMMAND TABLE ENTRY POINTING TO $0180, THE START OF
;                    THE 80 UNUSED BYTES OF RAM.  WHEN USED WITH DOWNLOAD,
;                    A USER-SUPPLIED CONTROLLER ROUTINE MAY BE INSTALLED
;                    AT $0180 VIA DOWNLOAD AND BECOME A RESIDENT COMMAND.
;    10. 1771 STATUS IS RETURNED TRUE LOGIC INSTEAD OF INVERTED (REV. B, C)
;       IN RESPONSE TO THE STATUS COMMAND.
;    11. CHECKSUM ERROR IN A COMMAND FRAME NOW CAUSES COMMAND TO BE IGNORED
;       INSTEAD OF RETURNING "NAK" IF DEVICE IS ADDRESSED THE WAY THE OLD
;       CONTROLLERS BEHAVE.
;
        .ENABLE AMA
        .RADIX  10
        .ASECT
;
;
;
;       1771 CONTROLLER REGISTERS
;
;           ALL VALUES TO 1771 ARE INVERTED LOGIC
;
;
        .=0
COMAND: .=.                     ;1771 COMMAND REGISTER (WRITE)
STATUS: .=.+1                   ;1771 STATUS REGISTER (READ)
TRACK:  .=.+1                   ;1771 TRACK REGISTER (READ/WRITE)
SECTOR: .=.+1                   ;1771 SECTOR REGISTER (READ/WRITE)
DATA:   .=.+1                   ;1771 DATA REGISTER (READ/WRITE)
;
;
;
;  1771 COMMANDS, INVERTED
;
FORCEI  =       ^C^H0D0         ;FORCE INTERRUPT (TERMINATES WRTRK COMMAND)
RESTOR  =       ^C^H000         ;RESTORE (USED AS A TIMER ONLY)
WRTRK   =       ^C^H0F4         ;WRITE ENTIRE TRACK (USED BY FORMAT)
SEEK    =       ^C^H013         ;SEEK TRACK (USED AS A TIMER ONLY)
WRSEC   =       ^C^H0A8         ;WRITE SECTOR (FIND SECTOR # IN ID AND WRITE)
RDSEC   =       ^C^H088         ;READ SECTOR (WRITE & READ ARE IBM FORMAT)
RDADDR  =       ^C^H0C0         ;READ SECTOR ID FIELD (FOR SEEK VERIFY)
;
;
;   810 CONTROLLER PROGRAM COMMANDS SUPPORTED:
;
DOWNL   =       ^H20            ;DOWN LOAD
FORMT   =       ^H21            ;FORMAT DISK
WRINOV  =       ^H50            ;WRITE A SECTOR W/O READ BACK VERIFY
READSC  =       ^H52            ;READ A SECTOR
STATCD  =       ^H53            ;STATUS COMMAND
READAD  =       ^H54            ;READ INTERNAL MEMORY AT SPECIFIED ADDR
WRIVER  =       ^H57            ;WRITE A SECTOR & VERIFY (READ IT BACK)
USRCMD  =       ^H0FF           ;USER DOWNLOADED COMMAND (RAM RESIDENT)
;
;
;
;
;   MEMORY MAP:
;
;   0000-0003  1771 CONTROLLER REGISTERS
;   0080-00FF  RAM  (USED FOR SCRATCH AND 128 BYTE DATA BUFFER)
;   0180-01BF  RAM  (UNUSED, AVAILABLE FOR USER DOWNLOADED COMMANDS)
;   01C0-01FF  RAM  (USED FOR CONTROLLER VARIABLES AND STACK)
;   0380-039F  HARDWARE REGISTERS FOR PIA INTERFACE
;   0800-0FFF  ROM (DISK CONTROLLER PROGRAM)
;
;
;
STBUF   =       ^H080           ;START OF DATA BUFFER
;
;   NEXT 4 BYTES ARE REFERENCED BY FORMAT ROUTINE
;
        .=      ^H0FA
SECTPL: .=.+1                   ;LSB OF SECTOR TABLE POINTER
SECTPH: .=.+1                   ;MSB OF SECTOR TABLE POINTER
FSCINX: .=.+1                   ;CURRENT SECTOR # INDEX
FMTTRK: .=.+1                   ;CURRENT TRACK #
;
PNTRL:  .=.+1                   ;ADDR USED FOR CMDI INDIRECT JUMP
PNTRH:  .=.+1                   ;ADDR HI FOR CMDI
;
        .=      ^H0180          ;START OF 128-BYTE RAM BLOCK
USER:   .=.+80                  ;80 BYTES OF AVAILABLE RAM (CUSTOM COMMAND MAY BE INSTALLED)
;
;   INTERNAL CONTROLLER VARIABLES
;
UNITNB: .=.+1                   ;DRIVE UNIT #
CTLCMD: .=.+1                   ;CONTROLLER COMMAND
AUX1:   .=.+1                   ;LSB OF 16-BIT SECTOR #
AUX2:   .=.+1                   ;MSB OF 16-BIT SECTOR #
SDATA:  .=.+1                   ;SERIAL DATA SHIFTED FROM THIS REG
CHKSUM: .=.+1                   ;CHECKSUM BYTE
RETRY:  .=.+1                   ;ERROR RETRY COUNT
ONWAIT: .=.+1                   ;MOTOR ON TIME AFTER LAST COMMAND (IN 1/10 SECS)
;
STPSCR: .=.+1                   ;STEP ROUTINE SCRATCH
PRSTRK: .=.+1                   ;PRESENT TRACK #, INVERTED
STPNBR: .=.+1                   ;# OF STEPS NEEDED TO SEEK
;
; THE FOLLOWING 4 BYTES SENT TO COLLEEN WHEN STATUS COMMAND RECEIVED
;
STAT0:  .=.+1                   ;STATUS FLAGS
;
;  STAT0 BIT DEFINITIONS:
;
;  INFO RELATES TO LAST COMMAND PRIOR TO STATUS REQUEST
;
;  7 - UNUSED
;  6 - UNUSED
;  5 - UNUSED
;  4 - MOTOR ON/OFF (1=ON)
;  3 - WRITE PROTECT STATUS (1=WRITE PROTECTED)
;  2 - READ/WRITE ERROR IF =1
;  1 - PREVIOUS DATA FRAME WAS INVALID IF =1
;  0 - PREVIOUS COMMAND FRAMME WAS INVALID IF =1
;
;   INDIVIDUAL BIT EQUATES FOR STAT0:
;
MTR0    =       ^H010           ;MOTOR ON/OFF
WPS0    =       ^H008           ;WRITE PROTECT STATUS
RWE0    =       ^H004           ;READ/WRITE ERROR
INVDF0  =       ^H002           ;INVALID DATA FRAME
INVCF0  =       ^H001           ;INVALID COMMAND FRAME
;
;
;
STAT1:  .=.+1                   ;1771 STATUS, INVERTED
STAT2:  .=.+1                   ;LSB, WORST CASE TIME OUT, INIT=E2, NOT USED
STAT3:  .=.+1                   ;MSB, WORST CASE TIME OUT, INIT=00, NOT USED
STASUM: .=.+1                   ;USED AS CHECKSUM FOR ABOVE 4 BYTES
;
;
TRKNBR: .=.+1                   ;TRACK # COMPUTED FOR 1771, INVERTED
SCTNBR: .=.+1                   ;SECTOR # COMPUTED FOR 1771, INVERTED
INSTAT: .=.+1                   ;INTERNAL STATUS REGISTER
;
;  BIT DEFINITIONS FOR INSTAT:
;
;  7 - UNUSED AS OF 10/25/78
;  6 - BUSY, SET WHENEVER GOING TO A ROUTINE THAT WILL KEEP US
;        AWAY FROM THE COMMAND FRAME MONITOR ROUTINE MORE THAN 400 MS.
;  5 - SHUTDOWN IN PROGRESS, WHEN SET INDICATES CONTROL SHOULD RTS FROM
;        MAIN MONITOR--DOES NOT INDICATE 400 MS ABSENCE
;  4 - IF SET, DRIVE HASN'T BEEN HOMED SINCE POWER UP
;  3 - FORMAT ERROR IF SET, AND READ AFTER WRITE FLAG!!!
;  2 - SPINDLE MOTOR HAS NOT BEEN ON 1 SEC IF SET
;  1 - COMMAND RECEIVE ERRORS (CHECKSUM ERROR) IF SET
;  0 - IF SET, COLLEEN HAS NOT BEEN WITH US
;
;   INSTAT BIT EQUATES:
;
NOTUSD  =       ^H080           ;"NOT USED" BUT REFERENCED
BUSY    =       ^H040           ;CONTROLLER BUSY
SHTDWN  =       ^H020           ;SHUTDOWN IN PROGRESS
NOINIT  =       ^H010           ;DRIVE NOT HOMED YET
FMTER   =       ^H008           ;FORMAT ERROR
RDAFWR  =       ^H008           ;READ AFTER WRITE FLAG (SAME AS FMTER)
SMNRDY  =       ^H004           ;SPINDLE MOTOR NOT READY
CRECR   =       ^H002           ;COMMAND RECEIVE ERROR
NO800   =       ^H001           ;NO COLLEEN
;
;
INSCRT: .=.+1                   ;TERMINATING INDEX (X) VALUE+1 FOR RCSER & CSUM
TMRSCR: .=.+1                   ;TIMER SCRATCH
FSCTBP: .=.+1                   ;FORMAT ERROR RETRY COUNT
BFRPNT: .=.+1                   ;BAD SECTOR # BUFFER INDEX
INVSNB: .=.+1                   ;INVERTED SCTNBR FOR SECTOR BUILD-UP
TMPSB:  .=.+1                   ;*2 MULTIPLY TEMPORARY STORAGE (SECTOR BUILD-UP)
MSBSB:  .=.+1                   ;MSB SECTOR BUILD-UP
SPMSDC: .=.+1                   ;TIME OUT LOOP COUNTER FOR SHUT-DOWN
;
;
;
;
;  PIA INTERFACE REGISTERS
;
;
PORTA   =       ^H0380          ;HARDWARE CONTROL PORT A
;
;  BIT DEFINITIONS FOR PORT A:
;
;  7 - DATA REQUEST FROM 1771        INPUT    (1=ACTIVE)
;  6 - INTERRUPT REQUEST FROM 1771   INPUT    (FINISHED COMMAND OR ERROR IF 1)
;  5 -   (NOT USED)
;  4 - WRITE PROTECT                 INPUT    (1=WP, 0=NOT PROTECTED)
;  3 - PROCEED BAR (NOT USED)
;  2 - DRIVE # SELECT ADDRESS 2      INPUT
;  1 - MOTOR CONTROL/SELECT LITE     OUTPUT   (1=ON, 0=OFF)
;  0 - DRIVE # SELECT ADDRESS 1      INPUT
;
;
;   BIT EQUATES FOR PORT A:
;
DRQ     =       ^H080           ;DATA REQUEST
IRQ     =       ^H040           ;INTERRUPT REQUEST
WRPRO   =       ^H010           ;WRITE PROTECT
PROCED  =       ^H008           ;PROCEED BAR
DRVAD2  =       ^H004           ;DRIVE SELECT ADDR #2
SELDSK  =       ^H002           ;MOTOR CONTROL/SELECT LITE
DRVAD1  =       ^H001           ;DRIVE SELECT ADDR #1
;
;
PADDR   =       ^H0381          ;PORT A DATA DIRECTION REGISTER
;
;  PADDR ASSIGNS DATA DIRECTION TO PORTA; 1=OUTPUT TO PIA
;                                         0=INPUT FROM PIA
;
;
PORTB   =       ^H0382          ;HARDWARE CONTROL PORT B
;
;  BIT ASSIGNMENTS FOR PORT B:
;
;  7 - SERIAL DATA INPUT        INPUT
;  6 - COMMAND FRAME            INPUT   1=RECEIVING CMD FRAME, 0=INACTIVE
;  5 - STEPPER MOTOR PHASE 4    OUTPUT   TWO CIRCULARLY ADJACENT BITS ARE
;  4 - STEPPER MOTOR PHASE 3    OUTPUT   NORMALLY SET, THE OTHER TWO ARE CLEAR.
;  3 - STEPPER MOTOR PHASE 2    OUTPUT   A STEP IS EFFECTED BY ROTATING CIRCULARLY THE
;  2 - STEPPER MOTOR PHASE 1    OUTPUT   4 STEPPER BITS, RIGHT FOR STEP OUT, LEFT FOR IN.
;  1 - READY/VCC                INPUT
;  0 - SERIAL DATA OUTPUT       OUTPUT   SET TO INPUT MODE IN PBDDR UNTIL READY TO SEND DATA.
;
;  BIT EQUATES FOR PORT B:
;
SERIN   =       ^H080           ;SERIAL DATA INPUT, INVERTED LOGIC
CMDFRM  =       ^H040           ;COMMAND FRAME
SPHA4   =       ^H020           ;STEPPER PHASE 4
SPHA3   =       ^H010           ;STEPPER PHASE 3
SPHA2   =       ^H008           ;STEPPER PHASE 2
SPHA1   =       ^H004           ;STEPPER PHASE 1
RDY     =       ^H002           ;READY/VCC
SEROUT  =       ^H001           ;SERIAL DATA OUTPUT, TRUE LOGIC
;
STPMSK  =       SPHA1+SPHA2+SPHA3+SPHA4         ;STEPPER MASK
;
;
;
;
PBDDR   =       ^H0383          ;PORT B DATA DIRECTION REGISTER
;
;  PBDDR DATA DIRECTION DEFINITIONS FOR PORT B, 0=INPUT FROM PIA
;                                               1=OUTPUT PIA
;
A384    =       ^H0384          ;DISABLE BIT 7, PORTA (ANY WRITE)
A394    =       ^H0394
TMSTAT  =       ^H0395          ;TIMER STATUS (PIA), BIT 7 GOES HI AT TIMEOUT
WRTIM1  =       ^H039C          ;PIA WRITE TIMER 1 (0.002 MS * COUNT) ONLY READ?
WRTIM2  =       ^H039E          ;PIA WRITE TIMER 2 (0.128 MS * COUNT)
WRTIM3  =       ^H039F          ;PIA WRITE TIMER 3 (2.048 MS * COUNT)
;
;   MISCELLANEOUS EQUATES:
;
MAXTRK  =       39              ;MAXIMUM TRACK NUMBER
NOTRDY  =       ^H080           ;NOT READY (1771 STATUS CODE)
RNF     =       ^H010           ;RECORD NOT FOUND (1771 STATUS CODE)
CRCERR  =       ^H008           ;CRC ERROR (1771 STATUS CODE)
LOSDAT  =       ^H004           ;LOSTT DATA (1771 STATUS CODE)
BSY     =       ^H001           ;BUSY (1771 STATUS CODE)
ACK     =       'A              ;ACKNOWLEGE CODE
NAK     =       'N              ;NEGATIVE ACKNOWLEGE CODE
CMPLT   =       'C              ;COMMAND SUCCESSFULLY COMPLETED CODE
ERROR   =       'E              ;COMMAND NOT COMPLETED SUCCESSFULLY CODE
TIMOUT  =       224             ;STAT CMD RETURNS THIS IN STAT2, STAT3
;
;
;
;
;
;   ROM BASED PROGRAM STARTS HERE
;
;
        .=^H0800
;
;
;
;     ******************
;       INITIALIZATION
;     ******************
;
;
INIT:   LDX     #^H0FF
        TXS                     ;STACK POINTER AT TOP OF STACK
        CLD                     ;BINARY ARITHMETIC
        LDA     #SELDSK         ;SELECT DISK BIT IS ONLY OUTPUT
        STA     PADDR           ;ASSIGN TO DATA DIRECTION FOR PORT A
        LDX     #STPMSK         ;GET STEPPER BITS MASK
        STX     PBDDR           ;SET STEPPER BITS FOR OUTPUT
        LDA     #0              ;CLEAR ACCUM
        STA     PORTA           ;MAKE SURE MOTOR ISN'T ON AT INIT
        STA     STAT0           ;CLEAR STATUS 0 BITS
        LDA     #SMNRDY+NO800+NOINIT+SHTDWN
        STA     INSTAT          ;SET UP INSTAT FOR INITIAL STATUS
        LDA     #30             ;SET MOTOR WAIT FOR 3 SECS.
        STA     ONWAIT          ;SAVE IN ONWAIT SO USER CAN CHANGE AFTER INIT
        STA     SPMSDC          ;SET UP MOTOR ON COUNTER TOO
        LDA     #4              ;GET RETRY COUNT
        STA     RETRY           ;SAVE IN RAM (FOR POSSIBLE MODIFICATION)
CMDWT:  JSR     MMON            ;LET'S SIT HERE AND WAIT FOR 1ST COMMAND
        JMP     CMDWT           ;LOOP INDEFINATELY
;
;
INIT2:  LDA     #TIMOUT&255     ;SET TIME OUT LSB
        STA     STAT2           ;SAVE IN STAT2
        LDA     #TIMOUT/256     ;SET TIME OUT MSB
        STA     STAT3           ;SAVE IN STAT3
;
INIT3:  LDX     #^H0FF          ;STACK POINTER TO TOP OF STACK
        TXS
        JSR     WPSNS           ;CHECK WP STATUS AND UPDATE STAT0 ACC'LY
        LDA     #^CMAXTRK       ;MAXIMUM TRACK #, INVERTED
        JSR     CSTEP           ;COMPUTE # STEPS, PUT IN Y, SET X
        STY     STPNBR          ;SAVE # STEPS IN STPNBR
DLY:    LDA     #50             ;USE 50 * 2 MS TIMER
        STA     WRTIM3          ;TO GENERATE IRQ
        LDA     #SHTDWN         ;GET BIT 5 FOR INSTAT
        JSR     ORINST          ;SET INSTAT BIT5 FOR SHUTDOWN IN PROGRESS
W3S:    JSR     MMON            ;CHECK SERIAL BUS FOR COLLEEN COMMAND
        LDA     TMSTAT          ;CHECK FOR PIA TIME OUT
        BPL     W3S             ;IF NOT TIMED-OUT, LOOP SO MORE
W3S1:   DEC     SPMSDC          ;ELSE, DEC TIME OUT COUNTER
        BPL     DLY             ;IF NOT NEG YET, LOOP BACK TO RESTORE
;
INSL:   LDX     #^H080          ;X SET FOR STEPPING IN
        LDY     STPNBR          ;GET # OF STEPS IN Y
        BEQ     AT39            ;IF STEPS=0, SKIP STEPPING ROUTINE
        JSR     STEP            ;ELSE, STEP IN & DEC Y
        STY     STPNBR          ;SAVE NEW Y
CMDCK:  JSR     MMON            ;BETTER CHECK SERIAL BUS AGAIN
        LDA     TMSTAT          ;CHECK STEP TIMER FOR TIME OUT
        BPL     CMDCK           ;IF NO TIMED OUT, CHECK CMD FRAME INPUT
        BMI     INSL            ;UNCONDITIONAL JUMP FOR NEXT STEP
AT39:   LDA     #0              ;GET PORT A COMMAND
        STA     PORTA           ;  TO TURN OFF MOTOR/SELECT LITE
        STA     PORTB           ;CLEAR STEPPER BITS
        LDA     #^CSHTDWN       ;TURN OFF BIT5
        JSR     ANDINS          ; OF INSTAT, SHUTDOWN NOT IN PROGRESS
        LDA     #SMNRDY         ;SET BIT2 OF INSTAT
        JSR     ORINST          ;  MOTOR NOT ON FOR 1 SEC
;               FALL THRU TO MAIN MONITOR (NO RETURN IF SHTDWN)
;
;
;     ****************
;       MAIN MONITOR
;     ****************
;
;
MMON:   LDA     PORTB           ;GET PORT B
        AND     #RDY            ;CHECK READY/VCC BIT
        BNE     NCOLL           ;IF SET, GOTO NCOLL
        LDA     INSTAT          ;ELSE, CHECK INTERNAL STAUS REGISTER
        AND     #NO800          ;CHECK COLLEEN NOT WITH US FLAG
        BNE     JCOLL           ;IF FLAG SET, GOTO JCOLL
        BIT     PORTB           ;CHECK BIT 6, PORT B (COMMAND FRAME)
        BVC     NOCF            ;IF COMMAND FRAME=0, GOTO NOCF
        BIT     INSTAT          ;CHECK BUSY BIT
        BVC     EDGE            ;IF NOT BUSY, GOTO EDGE
        LDA     #^H029          ;ELSE, LOAD TIMER FOR
        JSR     TIMER           ;5 MS DELAY (WAIT FOR IT)
        BIT     PORTB           ;NOW CHECK COMMAND FRAME
        BVS     EDGE            ;GOTO EDGE IF ACTIVE
;
NOCF:   LDA     INSTAT          ;GET INTERNAL STATUS REGISTER
        AND     #BUSY+SHTDWN    ;CHECK EITHER BUSY OR SHUTDOWN
        BNE     MMX             ;IF ANY SET, RETURN
        JMP     MMON            ;ELSE, LOOP INDEFINATELY
MMX:    RTS
;
JCOLL:  LDA     #^H029          ;LOAD TIMER FOR 5 MS
        JSR     TIMER           ;WAIT FOR IT
        LDA     #^CNO800        ;YES, COLLEEN! CLEAR NO800
        JSR     ANDINS          ;IN INSTAT
        JMP     MMON            ;LOOP BACK
;
NCOLL:  LDA     #NO800          ;SET NO COLLEEN FLG
        JSR     ORINST          ;OR IT INTO INSTAT
        JMP     NOCF            ;JUMP TO NO COMMAND FRAME
;
EDGE:   LDX     #0              ;SET INDEX FROM UNITNB
        LDA     #5              ;SET MAXIMUM INDEX (FOR RCSER)
        STA     INSCRT          ;SAVE FOR RCSER
        JSR     RCSER           ;RECEIVE 5 BYTES OF CMD FRAME FROM SERIAL BUS
        LDX     #0              ;RESET OFFSET FROM UNITNB
        DEC     INSCRT          ;TO FORM CHECKSUM OF ALL BUT REC'D CHKSUM
        JSR     CSUM            ;COMPARE REC'D WITH GENERATED
        BNE     CCF             ;IF CHKSUM ERR, IGNORE THIS CMD FRAME
;
; DECODE DRV # TO SEE IF WE'RE BEING ADDRESSED
;
CCMD:   LDX     #'4             ;ASSUME DRIVE #4
        LDA     PORTA           ;GET DRV # ADDRESS BITS
        AND     #DRVAD1+DRVAD2  ;MASK OFF OTHERS
        BEQ     SX33            ;IF 0,0 GOTO UNIT #3 CHECK
        CMP     #DRVAD2         ;IF 0,1 GOTO UNIT #2 CHECK
        BEQ     SX32
        CMP     #DRVAD1         ;IF 1,0 GOTO UNIT #4 CHECK
        BEQ     SX34
        DEX                     ;ELSE, 1,1 CHECK UNIT #1
SX32:   DEX                     ;DECREMENT ASCII DRV #
SX33:   DEX                     ;UNIT #3 ENTRY CHECK
SX34:   CPX     UNITNB          ;COMPARE WITH REQUESTED DRIVE #
        BEQ     XCMD            ;THAT'S US, GOTO XCMD
;
CCF:    BIT     PORTB           ;WRONG DRV #, WAIT FOR COMAND FRAME
        BVS     CCF             ;TO BECOME INACTIVE
        LDA     #^C<CRECR>      ;CLEAR COMMAND RECEIVE ERROR & UNUSED BITS
        JSR     ANDINS          ;IN INTERNAL STATUS REGISTER
        JMP     MMON            ;CHECK FOR MORE COMMANDS
XCMD:   JSR     CMDI            ;INTERPRET CMD & JUMP INDIRECT TO IT
        JMP     MMON            ;CHECK FOR MORE COMMANDS
;
;
;     ***********************
;       COMMAND INTERPRETER
;     ***********************
;
;
; CONTROL ENTERS HERE IF REC'D COMMAND FRAME REFERENCES THIS DRIVE.
; CONTROLLER COMMAND BYTE IS COMPARED WITH ALL LEGAL COMMAND TABLE ENTRIES.
; IF GOOD COMMAND IS FOUND, CONTROL IS TRANSFERRED TO THE COMMAND ENTRY
; POINT.  OTHERWISE, A NAK IS SENT.
;
;
;
CMDI:   LDA     CTLCMD          ;FETCH COMMAND CODE FROM FRAME
        LDX     #TBBT-TABB      ;LEGAL COMMAND?
XTAB:   CMP     TABB(X)         ;COMPARE WITH TABLE ENTRY
        BEQ     GTCMD           ;GOT A MATCH, GO EXECUTE
        DEX                     ;DECREMENT INDEX
        BPL     XTAB            ;ANY TABLE ENTRIES LEFT?
        BMI     CMD2            ;NO, UNCONDITIONAL SEND INVALID CMD FRAME
;
GTCMD:  LDA     TABL(X)         ;YES, FETCH LO ADDR OF ROUTINE
        STA     PNTRL           ;SAVE IN INDIRECT JUMP VECTOR
        LDA     TABH(X)         ;FETCH HI ADDR OF ROUTINE
        STA     PNTRH           ;PUT IT IN INDIRECT JUMP VECTOR
        JMP     @PNTRL          ;JUMP TO SELECTED COMMAND ROUTINE
;
CMD2:   LDA     #INVCF0         ;SET INVALID COMMAND FRAME FLAG
        JSR     OR0ST0          ;SET INVCF FLG, CLEAR ALL OTHERS
        JSR     MOTFU           ;UPDATE MOTOR ON/SELECT STATUS IN STAT0
;
CMD3:   BIT     PORTB           ;CHECK COMMAND FRAME BIT
        BVS     CMD3            ;WAIT UNTIL IT GOES LO
        LDA     #NAK            ;GET NAK CODE
        JSR     SAA2            ;SEND IT OVER SERIAL BUS
        LDA     #^CCRECR        ;CLEAR CMD RECEIVE ERROR FLG
        JMP     ANDINS          ;RETURN TO MAIN MONITOR THRU INSTAT AND ROUTINE
;
; *************************************
;   COMMAND INTERPRETER LOOKUP TABLE
; ************************************
;
;
;
TABB:   .BYTE   DOWNL,FORMT,WRINOV,READSC,STATCD,READAD,WRIVER,USRCMD
TBBT    =.-1
;
TABL:   .BYTE   DLOAD&^H0FF     ;^H020 DOWN LOAD
        .BYTE   FRMT&^H0FF      ;^H021 FORMAT
        .BYTE   WR90&^H0FF      ;^H050 WRITE W/O READ CHECK
        .BYTE   READ&^H0FF      ;^H052 READ
        .BYTE   STCMD&^H0FF     ;^H053 STATUS
        .BYTE   RDADR&^H0FF     ;^H054 READ ADDRESS
        .BYTE   WR9&^H0FF       ;^H057 WRITE
        .BYTE   USER&^H0FF      ;ADDR OF USER INSTALLED COMMAND
;
TABH:   .BYTE   DLOAD/256
        .BYTE   FRMT/256
        .BYTE   WR90/256
        .BYTE   READ/256
        .BYTE   STCMD/256
        .BYTE   RDADR/256
        .BYTE   WR9/256
        .BYTE   USER/256
;
;       *************
;         DOWN LOAD
;       *************
;
;
;
;       THIS ROUTINE DOWNLOADS A BLOCK OF DATA FROM THE 400/800
;       MAINFRAME AND EXECUTES IT AS A PROGRAM.
;
;       SIO COMMAND=20H
;
;
DLOAD:  JSR     SAA             ;SEND ACK TO COLLEEN
        JSR     RCDAT           ;GET DATA BUFFER FROM MAINFRAME
        JSR     DTCS            ;GENERATE CHECKSUM AND COMPARE W/ REC'D
        BEQ     DL1             ;IF GOOD CHECKSUM, SKIP ERROR EXIT
        JMP     WR5             ;ELSE, DO WHAT WRITE DOES IF BAD CHECKSUM
DL1:    JSR     SAA1            ;TELL COLLEEN WE GOT THE DATA OK
        JSR     STBUF           ;CALL THE PROGRAM LOADED AT START OF BUFFER
        JMP     WR40            ;DO WHAT WRITE DOES WHEN SUCCESSFUL
;
;
;
; *************************
;   SECTOR SEQUENCE TABLE
; *************************
;
;
;   TABLE IS USED BY FORMAT ROUTINE FROM HIGHEST ADDR TO LOWEST
;
;
;   NO RESTRICTION IS PLACED ON TABLE LOCATION (AS IN REV. D)
;   EXCEPT THAT FOR TIME CRITICAL EXECUTION, IT SHOULD ALL FIT WITHIN 1
;   PAGE (NO PAGE BOUNDARY CROSSING).
;
;
SCTBL:  .BYTE   ^C16,^C14,^C12,^C10,^C8,^C6,^C4,^C2,^C17
        .BYTE   ^C15,^C13,^C11,^C9,^C7,^C5,^C3,^C1,^C18
;
;
;
;
;     **********
;       FORMAT
;     **********
;
;
FRMT:   JSR     SAA             ;SEND ACK CODE
        LDA     #SCTBL&^H0FF    ;SET UP SECTOR TABLE INDIRECT PTR
        STA     SECTPL          ;IN PAGE ZERO
        LDA     #SCTBL&^H0FF00/256   ;HI BYTE TOO
        STA     SECTPH          ;IN PAGE ZERO
        JSR     SPMON           ;TURN ON MOTOR/SELECT LITE IF NOT ON
        JSR     SPSTB           ;WAIT 200 MS
        LDA     #^C0            ;INVERTED TRACK NUMBER TO SEEK
        STA     TRKNBR          ;SAVE IN DESIRED TRK #
        JSR     SET3            ;INIT STEPPER (IF NOT ON), FORCEI 1771 ETC.
        LDA     PORTA           ;LOAD WRTIM3 TO MAX, Y=4
        AND     #WRPRO          ;CHECK WRITE PROTECT STATUS
        BEQ     FRMTC           ;SKIP OVER IF NOT PROTECTED
        LDA     #WPS0           ;ELSE, SET WP FLG
        JSR     ORST0           ;IN STAT0
        JMP     FEERR           ;JUMP TO FORMAT ERROR HANDLER
;
FRMTC:  LDA     #^C0            ;INIT TRACK # TO INVERTED 0
        STA     FMTTRK          ;SAVE IN FORMAT'S TRK #
        JSR     FRMT1           ;FORMAT CURRENT TRACK W/O STEPPING 1ST
;
TALT:   DEC     FMTTRK          ;"INCREMENT" INVERTED TRK #
        LDA     FMTTRK          ;GET TRK # FOR TEST
        CMP     #^C<MAXTRK+1>           ;CHECK FOR OUT OF BOUNDS
        BEQ     DOFT1           ;IF CONDITION MET, GOTO FORMAT TEST
        LDX     #^H080          ;ELSE, SET X FOR STEPPING IN
        JSR     FRMT2           ;CALL STEP (IN) ROUTINE & FORMAT CURRENT TRK
        JMP     TALT            ;LOOP TO TRK # "INCREMENT"
;
DOFT1:  JMP     FMTST           ;GOTO FORMAT VERIFY (READ BACK)
FRMT2:  LDA     #2
        JSR     TIMER           ;WAIT 3 MS, THEN
        JSR     STEP            ; STEP IN
FRMT1:  LDA     #17             ;GET INITIAL SECTOR TABLE INDEX
        STA     FSCINX          ;SAVE IN INDEX SAVE BYTE
        LDA     A394            ;BETTER ASK BOB WHAT THIS DOES
        LDY     #^H0FF          ;Y USED AS BOTH DATA AND COUNTER FOR DATA
        LDX     #^H0FF          ;X USED AS DATA
;
        LDA     #WRTRK          ;ISSUE WRITE TRACK COMMAND TO 1771
        STA     COMAND
;
;                       SEQUENCE OF BYTES WRITTEN TO CURRENT TRACK:
;
;                               # OF BYTES      DATA    INVERTED
;                               ----------      ----    --------
        JSR     OUTY            ;   1            FF       0
;
        LDA     #13     ;SET UP 27 MS TIMER
        STA     WRTIM3
;
;*DAVE* LDY     #255
        JSR     YOUTX           ;   255          FF       0
        STX     WRTIM3  ;SET UP 525 MS TIMER
        LDY     #^C^H0FC  ;INDEX MARK
        JSR     OUTY            ;   1            3        FC
        LDY     #11
        JSR     YOUTX           ;   11           FF       0
;
;  EACH SECTOR GETS FOLLOWING:
;
FMTCN:  LDY     #6
        JSR     YOUTX           ;   6            FF       0
        LDY     #^C^H0FE  ;ID ADDR MARK
        JSR     OUTY            ;   1            1        FE
        LDY     FMTTRK    ;GET INVERTED TRK
        JSR     OUTY            ;   1            ^CTRK#   TRK#
        LDY     #^C0    ;SIDE # (INVERTED 0)
        JSR     OUTY            ;   1            FF       0
        LDY     FSCINX  ;GET SEC # INDEX
        LDA     @SECTPL(Y)      ;GET SEC FROM TABLE
        TAY             ;INTO Y REG.
        BIT     PORTA   ;SPECIAL CASE WRITE
        BMI     WOTO2   ;DRQ ALREADY?!!, GO SERVICE
WOTO:   BVS     FEERR
        BIT     PORTA
        BPL     WOTO
WOTO2:  STY     DATA            ;   1            ^CSEC#  SEC#
        LDY     #^C0    ;SECTOR SIZE CODE?
        JSR     OUTY            ;   1            FF       0
        LDY     #^C^H0F7  ;1771 CODE FOR CRC
        JSR     OUTY            ;   1            8        F7
        LDY     #17
        JSR     YOUTX           ;   17           FF       0
        LDY     #^C^H0FB  ;1771 CODE TO PRESET CRC
        JSR     OUTY            ;   1            4        FB
        LDY     #128
        LDX     #^C^H0E5
        JSR     YOUTX           ;   128          1A       E5
        LDY     #^C^H0F7  ;SEND CRC CODE
        JSR     OUTY            ;   1            8        F7
        LDY     #8
        LDX     #^C0            ;X=FF AGAIN
        JSR     YOUTX           ;   8            FF       0
        JSR     OUTY            ;   1            0        FF
;
;
        DEC     FSCINX          ;DECREMENT SECTOR TABLE INDEX
        BMI     TRKXT           ;FORCE INTERRUPT 1771 & EXIT IF TRACK IS DONE
;
;
        JSR     OUTY            ;   1            0        FF
;
;
        JSR     OUTY            ;  1             0        FF
;
        JMP     FMTCN           ;LOOP FOR NEXT SEC #
;
;
TRKXT:  LDA     #FORCEI         ;KILL 1771 FOR THIS TRACK
        STA     COMAND          ;AND RETURN TO MAIN FORMAT ROUTINE
        RTS
;
;  ROUTINE SENDS BYTE IN X, Y TIMES TO 1771
;     IF TIMER RUNS OUT 1ST, JUMP TO ERROR
;
;  ENTRY POINT IS 2ND LINE, X & A PRESERVED
;
WOT1:   BVS     FEERR           ;CHECK FOR IRQ FROM TIMER
YOUTX:  BIT     PORTA           ;CHECK DRQ FROM 1771
        BPL     WOT1            ;IF NO DRQ, GO UP TO TIMER CHECK
        STX     DATA            ;PUT DATA IN 1771
        DEY                     ;DECREMENT LOOP COUNTER
        BNE     YOUTX           ;DO IT Y TIMES
        RTS
;
;  ROUTINE SENDS BYTE IN Y ONCE
;
;  JUMP TO ERROR IF TIMER TIMES OUT 1ST
;
;  ENTRY POINT IS 2ND LINE
;  ALL REGS (EXCEPT P) PRESERVED.
;
;
WOT2:   BVS     FEERR           ;GOTO ERROR IF TIMER INTERRUPTED
OUTY:   BIT     PORTA           ;CHECK 1771 DRQ
        BPL     WOT2            ;NO DRQ HERE, GO UP TO IRQ CHECK
        STY     DATA            ;ELSE, SEND DATA IN Y
        RTS
;
;  WE END UP HERE IF TIMER RUNS OUT DURING A WRITE TRACK
;    OR IF THE DRIVE TELLS US DISKETTE IS WRITE PROTECTED
;
FEERR:  LDX     #^H0FF          ;PUT IN BAD SECTOR BUFFER TERMINATOR
        STX     STBUF           ;AT BEGINNING OF BAD SECTOR BUFFER
        STX     STBUF+1         ;HI BYTE TOO
        LDA     STATUS          ;GET 1771 STATUS CODE TOO
        EOR     #^H0FF          ;INVERT PLEASE
        STA     STAT1           ;SAVE FOR COLLEEN
        JMP     FERRT           ;SET R/W ERROR FLG & SEND ERROR CODE TO 800
;
;     ***************
;       FORMAT TEST
;     ***************
;
;  SECTORS ARE READ BACK TO ASSURE TRACK WAS FORMATTED
;  SECTOR SEQUENCE TABLE IS USED.
;
FMTST:  LDX     #0              ;CLEAR BAD SEC # BUFFER INDEX
        STX     BFRPNT          ;SAVE IN BFRPNT
FT1:    LDA     PRSTRK          ;USE PRESENT TRK # AS TRACK
        STA     TRACK           ;TELL 1771
        LDA     #17             ;INDEX INTO SEC TABLE STARTS AT HI ADDR
        STA     FSCINX          ;SAVE IN FORMAT SECTOR INDEX
FT2:    LDY     FSCINX          ;GET CURRENT INDEX INTO SECTOR TABLE
        LDA     @SECTPL(Y)      ;USE TO GET SECTOR #
        STA     SECTOR          ;TELL 1771 TOO
        LDY     #2              ;SET UP READ ATTEMPT COUNTER
        STY     FSCTBP          ;SAVE IN FSCTBP
FT200:  LDA     STATUS          ;GET 1771 STATUS
        EOR     #^H0FF          ;INVERT IT BACK
        AND     #NOTRDY+BSY     ;MASK FOR NOT READY OR BUSY
        BNE     FT200           ;IF ANY SET, KEEP WAITING
;
        LDA     #RDSEC          ;GET READ SECTOR COMMAND
        STA     COMAND          ;ISSUE COMMAND
;
        LDA     #255            ;LOAD TIMER WITH MAXIMUM
        STA     WRTIM3          ; SO IT'LL INTERRUPT COMMAND IN 525 MS
;
        LDX     #127            ;READING 128 BYTES PLEASE
        JMP     FT202           ;JUMP INTO READ LOOP
;
FT201:  BVS     HFER            ;IF IRQ BEFORE THRU, IT'S AN ERROR
        LDA     WRTIM1          ;FORCE IRQ LO
FT202:  BIT     PORTA           ;SAMPLE DRQ, IRQ
        BPL     FT201           ;IF NO DRQ, CHECK IRQ
        LDA     DATA            ;ELSE, FETCH THAT BYTE WAITING FOR US
        DEX                     ;DECREMENT BYTE COUNT
        BPL     FT202           ;IF MORE BYTES TO GET, GO FOR 'EM
        LDA     STATUS          ;FETCH STATUS AGAIN
        EOR     #^H0FF          ;INVERT FOR TRUE LOGIC
        STA     STAT1           ;SAVE FOR STATUS COMMAND
        AND     #LOSDAT+CRCERR+RNF      ;CHECK FOR THESE ERRORS
        BEQ     FT20            ;IF NONE, GO FOR NEXT SECTOR
;
HFER:   DEC     FSCTBP          ;ERROR SO DECREMENT RETRY COUNT
        BNE     FT200           ;TRY ONCE MORE TO READ
        LDX     BFRPNT          ;FAILED AGAIN, PUT BAD SEC# INDEX IN X
;
        LDA     #FMTER          ;SET FORMAT ERROR FLAG
        JSR     ORINST          ;IN INTERNAL STATUS REGISTER
        JSR     SBLD            ;INC X, FORM 16-BIT FROM TRK#/SEC# TO BAD SEC# BUFFER
        INX                     ;X INDEXES FOR NEW 2-BYTE ENTRY IN BAD SEC# TABLE
        STX     BFRPNT          ;SAVE INDEX IN BFRPNT
        CPX     #^H07A          ;IS BAD SEC# BUFFER ABOUT TO OVERFLO?
        BEQ     FMTT2           ;IF YES, PUT BUFFER TERMINATOR IN, SEND ERROR
                                ;ELSE, FALL THRU TO FT20
;
FT20:   LDY     FSCINX          ;FETCH INDIRECT INDEX
        BEQ     FT22            ;EXIT IF LAST SECTOR DONE
        DEY                     ;ELSE, DECREMENT
        DEY                     ;TWICE FOR READ BACK SKEW
        STY     FSCINX          ;SAVE NEW INDEX
        BPL     FT2             ;IF NON-NEGATIVE GO FOR NEXT SECTOR
        LDY     #16             ;ELSE, DO EVEN NUMBERED INDEXES
        STY     FSCINX
        JMP     FT2             ;GO FOR MORE SECTORS
FT22:   LDA     PRSTRK          ;ELSE, REGET PRESENT TRK #, INVERTED
        CMP     #^C0            ;HAVE WE STEPPED OUT TO ZERO, YET?
        BEQ     FMTT2           ;IF SO, GOTO FORMAT TEST PART 2
        LDX     #0              ;X=0 SO WE'LL STEP OUT
        LDY     #1              ;Y=# OF STEPS TO TAKE
        JSR     SEEKTR          ;YE OLDE SEEKE TRACK PLEASE
        JMP     FT1             ;GO VERIFY NEXT TRACK
;
;  CONTROL COMES IN THRU HERE WHEN FORMAT TEST
;   FINISHES READING BACK ALL SECTORS ON ALL TRACKS
;
FMTT2:  LDA     #^H0FF          ;GET TERMINATOR BYTE
        LDX     BFRPNT          ;LOAD LAST BAD SEC # TABLE INDEX
        STA     STBUF(X)        ;STUFF TERMINATOR IN HI ORDER POSITION
        INX                     ;INCREMENT INDEX
        STA     STBUF(X)        ;TERMINATOR TO LO ORDER TOO
        LDA     INSTAT          ;CHECK INTERNAL STATUS REGISTER
        AND     #FMTER          ;MASK FORMAT ERROR FLAG
        BNE     FT21            ;IF ERROR(S), SKIP OVER NEXT INSTRUCTION
        JMP     RD32            ;ELSE, SEND CMPLT, 128 BYTES, & GOTO INIT2
;
FT21:   LDA     #^CFMTER        ;CLEAR FORMAT ERROR FLAG
        JSR     ANDINS          ;  IN INTERNAL STATUS REGISTER
FERRT:  LDA     #RWE0           ;SET READ/WRITE ERROR FLAG IN STAT0
        JSR     ORST0           ;OR INTO STAT0
        JMP     RD51            ;SEND ERROR CODE, 128 BYTES, GOTO INIT2
;
;
;     *********
;       WRITE
;     *********
;
;  Y IS USED FOR ERROR RETRY COUNT (SETUP INITIALIZES TO 4)
;
;
;  ENTRY POINT FOR WRITE SECTOR W/ READ VERIFY
;
WR9:    LDA     #RDAFWR         ;SET INTERNAL STATUS TO INDICATE
        JSR     ORINST          ;READ CHECK AFTER WRITE
;
;  ENTRY POINT FOR WRITE SECTOR (NO VERIFY)
;
WR90:   JSR     TSN             ;CHECK 16-BIT SECTOR # VALIDITY
        BCC     WRITE           ;IF WITHIN RANGE, GET ON WITH IT
        JMP     CMD2            ;ELSE, RETURN INVALID CMD FRAME & EXIT
;
;
;
WRITE:  JSR     SAA             ;SEND ACK OVER SERIAL BUS
        JSR     SPMON           ;IF NOT SELECTED, SELECT, 1771 TIMER SET 60 MS
        JSR     RCDAT           ;GET 128 BYTES & CHKSUM FROM SERIAL BUS
        JSR     DTCS            ;GENERATE CHECKSUM AND COMPARE WITH REC'D CHKSUM
        BNE     WR5             ;IF CHECKSUMS DON'T MATCH, GOTO WR5
        JSR     SAA1            ;ELSE, GOOD CHKSUM, SEND ACK (NO WAIT, CMD FRAME INACTIVE)
        JSR     SPSTB           ;WAIT 200 MS
        JSR     SETUP           ;FORM TRK#/SEC#, SEEK, Y=4, X=$FF,TIMER 525 MS
        LDA     PORTA           ;CHECK WRITE PROTECT STATUS
        AND     #WRPRO          ;MASK FOR WRITE PROTECT BIT
        BNE     WR7             ;IF SET, WRITE PROTECTED, GOTO WR7
WR0:    LDA     #WRSEC          ;ELSE, READY TO WRITE, SO ISSUE WRITE SECTOR
        STA     COMAND          ;TO 1771
;
WR01:   BVS     WR4             ;V=0 FROM SETUP, BUT 2ND+ TIMES IT'S IRQ
        LDA     WRTIM1          ;BOB SEZ THIS FORCES IRQ LINE LO
        BIT     PORTA           ;CHECK DRQ FROM 1771
        BMI     WR3             ;1771 WANTS DATA, GOTO WR3
        JMP     WR01            ;ELSE, WAIT FOR EITHER DRQ OR IRQ
;
WR1:    BVS     WR4             ;CHECK IRQ, EXIT IF SET
WR2:    BIT     PORTA           ;CHECK FOR DRQ
        BPL     WR1             ;IF NO DRQ, GOTO IRQ CHECK
WR3:    INX                     ;ELSE, ADVANCE INDEX (X=0 1ST TIME)
        LDA     STBUF(X)        ;GET NEXT BYTE TO WRITE READY
        STA     DATA            ;GIVE IT TO 1771
        JMP     WR2             ;LOOP UNTIL 1771 CMD COMPLETE OR TIME OUT
;
WR4:    JSR     ERRDT           ;DECREMENT Y, CHECK ERRORS
        LDA     #ERROR          ;ASSUME ERROR FOR NOW
        BCS     WR8             ;IF ERROR, JUMP TO WR8 (WRITE PART 2)
        LDA     INSTAT          ;ELSE, OK, CHECK INTERNAL STATUS REGISTER
        AND     #RDAFWR         ;GET READ AFTER WRITE MASK
        BEQ     WR40            ;IF NO READ AFTER WRITE REQUIRED, SEND CMPLT & EXIT
        JSR     WRVRF           ;GO READ TO VERIFY WRITE
        BCC     WR40            ;IF NO VERIFY ERROR, SEND CMPLT CODE & EXIT
        LDA     #ERROR          ;ELSE, SEND ERROR CODE
        BCS     WR6             ;AND JUMP TO INIT2
;
WR40:   LDA     #CMPLT          ;GOOD WRITE, SEND CMPLT CODE & EXIT
        JMP     WR6
;
;  ENTER HERE IF CHECKSUM ERROR IN DATA FIELD
;
WR5:    LDA     #MTR0+INVDF0    ;SET INVALID DATA FRAME & MOTOR ON FLAGS
        JSR     OR0ST0          ;SET THESE IN STAT0, CLEAR OTHERS
        LDA     #NAK            ;GET NAK CODE
WR6:    JSR     SAA2            ;SEND CODE IN ACCUMULATOR OVER SERIAL BUS
        JMP     INIT2           ;EXIT TO MAIN PROGRAM
;
;  CONTROL COMES IN THRU HERE IF WRITE PROTECT ERROR DETECTED
;
WR7:    LDA     #WPS0+RWE0      ;SET WRITE PROTECT & READ/WRITE ERROR FLAGS
        JSR     ORST0           ;IN STAT0 REGISTER
        LDA     #ERROR          ;GET ERROR CODE
        JMP     WR6             ;SEND IT & EXIT
;
;  CONTROL ENTERS HERE WHEN A WRITE ERROR OCCURS
;
WR8:    CPY     #0              ;HAVE WE EXHAUSTED RETRIES?
        BEQ     WR6             ;YES, ->WR6 WHICH SENDS ERROR & EXITS
        JMP     WR0             ;ELSE, RETRY WRITE
;
;
;     ************************
;       READ TO VERIFY WRITE
;     ************************
;
;
WRVRF:  JSR     SET2            ;SET UP 1771 FOR A READ (X=$FF)
        LDY     #1              ;ONLY 1 RETRY PLEASE
WRV1:   INX                     ;X=0 1ST TIME THRU
        LDA     #RDSEC          ;GET READ SECTOR COMMAND
        STA     COMAND          ;ISSUE TO 1771
WRV2:   BVS     WRV4            ;IF CMD COMPLETE OR TIMEOUT, GOTO WRV4
        LDA     WRTIM1          ;PULL IRQ LINE LO
WRV3:   BIT     PORTA           ;CHECK DATA REQUEST
        BPL     WRV2            ;IF NO DRQ, GOTO IRQ CHECK
        LDA     DATA            ;ELSE, FETCH DATA BYTE
        CMP     STBUF(X)        ;COMPARE WITH WRITE BUFFER
        BNE     WRV6            ;IF NO MATCH, SET RWE0, CLEAR RDAFWR, C=1 & EXIT
        INX                     ;ELSE, INCREMENT OFFSET, GO FOR NEXT BYTE
        BNE     WRV3            ;THIS TERMINATES AFTER 256!! (IRQ TERMINATES 1ST)
WRV4:   JSR     ERRDT           ;CHECK ERRORS, DECREMENT Y
        BCC     WRV5            ;GOTO WRV5 IF GOOD. C=0
        BPL     WRV1            ;ERROR DETECTED, TRY AGAIN IF Y >= 0
WRV5:   LDA     #^CRDAFWR       ;CLEAR READ AFTER WRITE FLAG
        JSR     ANDINS          ;IN INTERNAL STATUS REGISTER
        RTS                     ;EXIT TO WRITE ROUTINE
;
WRV6:   JSR     ERD2            ;SET READ/WRITE ERROR FLG IN STAT0, C=1
        JMP     WRV5            ;EXIT THIS WAY
;
;
;     ******************
;       STATUS COMMAND
;     ******************
;
;
STCMD:  JSR     SAA             ;SEND ACK
        LDX     #STAT0-UNITNB   ;STARTING INDEX SET AT STAT0
        LDY     #STASUM-UNITNB  ;ENDING INDEX SET AT STAT3
        STY     INSCRT          ;SAVE FOR CSUM
        JSR     CSUM            ;FORM CHECKSUM OF STATUS BYTES
        STA     STASUM          ;SAVE CHECKSUM HERE
        LDA     #CMPLT          ;GET SUCCESSFUL COMPLETION CODE
        STA     SDATA           ;IN SERIAL DATA SHIFT REGISTER
        JSR     SOP             ;OPEN SERIAL OUTPUT
        LDX     #128-6          ;SINCE XMSER DEX'S, -6 GOES TO -5
        JSR     XMSER           ;SEND CMPLT CODE OVER SERIAL BUS
STC1:   LDY     STAT0-<128-5>(X)  ;GET NEXT STATUS BYTE
        STY     SDATA           ;PUT IT IN SERIAL DATA SHIFT REGISTER
        JSR     XM1             ;XM1 SENDS IT (X IS INC'D)
        BPL     STC1            ;SEND ALL 5 (X=$80 WHEN DONE)
        JSR     DBOT2           ;TURN OFF SERIAL OUTPUT
        JSR     WPSTAT          ;CLEAR ALL BUT WP FLG IN STAT0
        JSR     MOTFU           ;UPDATE MOTOR ON/SELECT LITE FLAG IN STAT0
        JMP     INIT2           ;BRANCH TO MAIN PROGRAM
;
;
;
;      ******
;       READ
;      ******
;
;
READ:   JSR     TSN             ;CHECK 16-BIT SECTOR # FOR VALIDITY
        BCS     BSNO            ;ERROR? GOTO BSNO
        JSR     SAA             ;SEND ACKNOWLEGE OVER SERIAL BUS
        JSR     SPMON           ;TURN ON MOTOR/SELECT LITE IF NOT ON
        JSR     SPSTB           ;WAIT 200 MS
        JSR     SETUP           ;TRK & SEC TO 1771, SEEK TRK, MOTOR READY,
RD1:    LDA     #RDSEC          ;V=0, X=$FF, Y=4 AFTER SETUP EXIT
        STA     COMAND          ;ISSUE READ SECTOR COMMAND TO 1771
;
RD2:    BVS     RD31            ;V=1?  IRQ FROM 1771 (CMD COMPLETE) OR TIMEOUT
        LDA     WRTIM1          ;REMEMBER TIME? (SEEMS TO BE WASTED)
RD3:    BIT     PORTA           ;IS DRQ ACTIVE?
        BPL     RD2             ;NO DATA TODAY, LOOP AROUND
;
        LDA     DATA            ;ELSE, GET THAT BYTE FROM 1771
        INX                     ;INC X (X=0 1ST TIME)
        STA     STBUF(X)        ;SAVE IN DATA BUFFER
        BPL     RD3             ;LOOP FOR ALL 128
RD31:   JSR     ERRDT           ;CHECK ERRORS, DEC Y
        BCS     RD5             ;WE GOT AN ERROR, GOTO RD5
;
;  FORMAT JUMPS IN THRU HERE IF SUCCESSFUL
;
RD32:   LDA     #CMPLT          ;PUT DATA TRANSFER COMPLETE CODE
RD4:    STA     SDATA           ;IN SERIAL DATA REG
        JSR     DTCS            ;FORM CHECKSUM
        STA     CHKSUM          ;SAVE IN CHECK SUM
        JSR     SOP             ;TURN ON SERIAL OUTPUT
        JSR     DBOT            ;SEND SDATA, THEN 128 BYTES IN BUFFER
        JMP     INIT2           ;GO BACK TO MAIN PROGRAM
;
RD5:    BNE     RD1             ;RETRY IF Y <> 0
RD51:   LDA     #ERROR          ;ELSE, BETTER SEND ERROR TO COLLEEN
        JMP     RD4             ;PUT CODE IN SDATA AND SEND
BSNO:   JMP     CMD2            ;GOTO CMD2?
;
;      *******************
;       READ FROM ADRRESS
;      *******************
;
;
;  DAVE'S VERSION OF READ FROM ADDRESS
;
;  ROUTINE USES POINTER IN AUX1 (LSB), AND AUX2 (MSB) AND READS
;  THE NEXT 128 BYTES OFFSET FROM THAT POINTER INTO DATA BUFFER
;  THEN SENDS IT TO COLLEEN.
;
RDADR:  JSR     SAA             ;SEND ACK TO COLLEEN
        LDA     AUX1            ;GET LSB OF POINTER
        STA     PNTRL           ;SAVE IN ZERO PAGE INDIRECT PTR
        LDA     AUX2            ;GET MSB TOO
        STA     PNTRH           ;IN ZERO PAGE (HI)
        LDY     #^H07D          ;WE CAN ONLY GO FOR 126 THIS TIME
RDADRL: LDA     @PNTRL(Y)       ;FETCH FROM USER'S ADDR
        STA     STBUF(Y)        ;SAVE IN DATA BUFFER
        DEY                     ;DEC INDEX & LOOP COUNTER
        BPL     RDADRL          ;IF MORE, GO FOR IT
        LDY     #^H07F          ;NOW GO FOR THOSE OTHER 2 PESKY BYTES
        LDA     @PNTRL(Y)       ;GET OFFSET +127
        PHA                     ;SAVE ON STACK
        DEY                     ;DECREMENT INDEX
        LDA     @PNTRL(Y)       ;GET OFFSET +126
        STA     PNTRL           ;PUT IT IN DATA BUFFER (WE DON'T NEED PNTRL NOW)
        PLA                     ;GET +127 FROM STACK
        STA     PNTRH           ;STUFF IT IN DATA BUFFER TOO.
        JMP     RD32            ;DO WHAT READ DOES WITH A GOOD BUFFER OF DATA
;
;
;     ****************
;       ERROR DETECT
;     ****************
;
;       Y IS DECREMENTED AFFECTING Z & N FLAGS JUST BEFORE RETURN
;       C=0 IF GOOD STATUS, C=1 IF ERROR
;
ERRDT:  LDA     STATUS          ;GET 1771 STATUS, INVERTED
        EOR     #^H0FF          ;GET POSITIVE LOGIC
        STA     STAT1           ;SAVE FOR COLLEEN
        BNE     HVERR           ;IF ANY SET, WE GOT TROUBLE
        CLC                     ;C=0 FOR GOOD READ
        LDA     #^CRWE0         ;CLEAR READ/WRITE ERROR FLAG
        JSR     ANDST0          ;IN STAT0 REG
ERD1:   JSR     SET2            ;Y PRESERVED, FORCEI TO 1771, TRK/SEC SET
        DEY                     ;WRTIM3 WAS SET MAXIMUM (SET2). DEC COUNT
        RTS                     ;RETURN GOOD
;
HVERR:  AND     #RNF            ;CHECK RECORD NOT FOUND FROM 1771 ONLY
        BEQ     ERD2            ;IF ERROR IS NOT RNF, SKIP HOME, RESEEK
        TYA                     ;SAVE Y ON
        PHA                     ;THE STACK
        JSR     SET2            ;SET UP THE 1771 FOR A NEW COMMAND
        LDA     #RDADDR         ;GET 1771 READ ADDRESS COMMAND
        STA     COMAND          ;ISSUE TO 1771
ERD3:   BVS     HARDH           ;IF TIMED OUT, WE RELUCTANTLY HARD HOME
        LDA     WRTIM1          ;FORCE IRQ LO
        BIT     PORTA           ;CHECK FOR IRQ, DRQ
        BPL     ERD3            ;IF NO DRQ, LOOP FOR IRQ CHECK
        LDA     DATA            ;ELSE, GET TRACK # FROM ID FIELD OF DISKETTE
        STA     PRSTRK          ;SAVE IN PRESENT TRACK #
        AND     #3              ;PRSTRK--GET MOD 4 FOR INDEX
        TAX                     ;GET IT IN INDEX X
        LDA     PORTB           ;CHECK STEPPER BITS
        AND     #STPMSK         ;MASK OFF THE OTHERS
        CMP     PHASE(X)        ;IS OUR STEPPER WHERE WE THINK IT IS?
        BEQ     NHARDH          ;YES, SKIP THE FOLLOWING "HARD HOME"
HARDH:  JSR     HHOME           ;ELSE, JAM THAT HEAD!
NHARDH: JSR     RESEEK          ;NOW GET BACK TO TRKNBR
        PLA                     ;REGET Y
        TAY                     ;FROM THE STACK
ERD2:   LDA     #RWE0           ;GET BIT2 SET FOR READ/WRITE ERROR
        JSR     ORST0           ;OR IT INTO STAT0
        SEC                     ;C=1 FOR ERROR RETURN
        JMP     ERD1            ;DECREMENT Y & EXIT
;
RESEEK: LDA     TRKNBR          ;GET INVERTED DESIRED TRACK #
RSEEK1: JSR     CSTEP           ;FORM Y,X FOR RESEEK
        JMP     SEEKTR          ;EXIT THRU SEEK TRACK
;
PHASE:  .BYTE   SPHA3+SPHA4,SPHA2+SPHA3,SPHA1+SPHA2,SPHA4+SPHA1
;
;                 3 MOD 4     2 MOD 4     1 MOD 4      0 MOD 4
;
;
;
;     *********
;       SETUP
;     *********
;
; FUNCTIONS:    1. FORM TRACK# & SECTOR# FROM 16-BIT AUX2, AUX1
;               2. INITIALIZE STEPPER PHASE BITS IF NOT ALREADY INIT'D
;               3. SEEK TRACK INDICATED
;               4. GUARANTEE SPINDLE MOTOR UP TO SPEED
;               5. INITIALIZE ERROR RETRY COUNT (Y=4)
;               6. FORCE INTERRUPT THE 1771
;               7. DELAY 3.5 MS, THEN STUFF TRK & SEC REGISTERS ON 1771
;               8. OVERFLO FLAG RESET (V=0)
;               9. LOAD WRTIM3 TIMER WITH MAXIMUM VALUE (FF), 525 MS
;
;
;
SETUP:  JSR     SECBD           ;BREAK DOWN 16-BIT SEC# TO TRK & SEC
SET3:   LDA     PORTB           ;CHECK STEPPER PHASE BITS
        AND     #STPMSK         ;MASK OFF OTHERS
        BNE     SET10           ;IF ANY SET, SKIP STEPPER INIT
        LDA     #SPHA3+SPHA4    ;ELSE, SET PHASE 3 & 4
        STA     PORTB           ;INIT STEP PHASE
SET10:  JSR     RESEEK          ;SEEK THE TRACK IN TRKNBR
;
SET1:   LDA     INSTAT          ;GET INSTAT TO CHECK BIT2
        AND     #SMNRDY         ;CHECK SPINDLE MOTOR NOT READY BIT
        BEQ     SPD2            ;IF NOT SET, MOTOR OK
        JSR     WAITIM          ;WAIT TIL STEP TIMER RUNS OUT
        LDA     #^CSMNRDY       ;CLEAR SP MOTOR NOT READY FLG
        JSR     ANDINS          ;AND INVERTED MASK W/INSTAT
SPD2:   LDA     #MTR0           ;SET MOTOR ON FLG
        JSR     OR0ST0          ;IN STAT0
        LDY     RETRY           ;GET RETRY COUNT FROM RAM
SET2:   LDA     #FORCEI         ;WAKE UP THE 1771
        STA     COMAND
;
        LDX     #^H0FF
SDLY:   DEX                     ;WAIT 3.5 MS
        BNE     SDLY
;
        LDA     TRKNBR
        STA     TRACK           ;TELL 1771 OUR TRACK #
        LDA     SCTNBR
        STA     SECTOR          ;AND SECTOR # TOO
        CLV                     ;V=0,USED AS IRQ FLAG
        DEX                     ;X=$FF
        STX     WRTIM3          ;PACK TIMER TO THE MAX, 525 MS
        RTS
;
;
;     *************
;       HEAD SEEK
;     *************
;
;
;  UPON ENTRY: IF Z SET, IMMEDIATE RETURN
;              IF Z RESET, STEP IN OR OUT USING X & Y AS INPUT
;
;               X > $7E WILL STEP IN
;               X <= $7E STEPS OUT
;
;               Y = # OF STEPS TO DO
;
; NOTE: IF X <=$7E AND X+Y > $7E THEN STEPPING DIRECTION WILL REVERSE
;
SEEKTR: BEQ     STTX            ;IF Z SET, IMMEDIATE EXIT
STPT1:  JSR     STEP            ;PERFORM A STEP (IN/OUT)
        BEQ     STPT2           ;Y=0?
        JSR     WAITIM          ;WAIT FOR STEP TIMER TO RUN OUT
        BMI     STPT1           ;WAITIM ALWAYS RETURNS N=1, UNCONDITIONAL LOOP
;
STPT2:  LDA     #^H050          ;FINISHED STEPPING, SET 10MS TIMER
        STA     WRTIM2
        JSR     WAITIM          ;WAIT FOR 10MS TIMER
STTX:   RTS
;
;
WAITIM: LDA     TMSTAT          ;CHECK TIMER STATUS (BIT 7)
        BPL     WAITIM          ;IF NOT SET, KEEP WAITING FOR IT
        RTS                     ;TIMED OUT, SO RETURN (ALWAYS RETURNS N=1)
;
;
;      **************
;       COMPUTE STEP
;      **************
;
;
;       UPON ENTRY: ACCUM HAS DESIRED TRACK NUMBER, INVERTED
;               PRSTRK (INVERTED) USED TO FIND DISPLACMENT
;
;       UPON EXIT: X SET TO 0 IF STEPPING OUT 0 OR MORE TRACKS
;                  X SET TO $80 IF STEPPING IN 1 OR MORE TRACKS
;
;                  Y = > 0 (POSITIVE NUMBER OF STEPS NEEDED)
;
CSTEP:  LDX     #0              ;ASSUME STEPPING IN (X=0)
        SEC                     ;C=1 FOR SBC
        SBC     PRSTRK          ;FIND DIFFERENCE
        BPL     CST1            ;IF +, STEPPING OUT; Y = DIFFERENCE
        LDX     #^H080          ;ELSE, NEG, STEPPING IN; Y = -DIFFERENCE
        EOR     #^H0FF          ;FORM POSITIVE OF
        CLC                     ; TWO'S CMPL RESULT
        ADC     #1              ;  IN ACCUM
CST1:   TAY                     ;SET NEW Y
CST2:   RTS
;
;
;      ******
;       STEP
;      ******
;
;
;   UPON ENTRY: IF X REG = < $7E THEN STEP OUT
;               IF X REG > $7E THEN STEP IN
;                 Y IS DECREMENTED AT EACH CALL IF MULTIPLE STEPPING DESIRED
;
;       PRESENT (INVERTED) TRACK # (PRSTRK) IS INCREMENTED OR DECREMENTED
;       STPSCR IS USED AS SCRATCH
;
;   UPON EXIT:  Y IS DECREMENTED, X IS INCREMENTED, ACCUM NOT PRESERVED
;
;
;
;
STEP:   LDA     #^H029          ;SET FOR 5.25 MS STEP WIDTH
        STA     WRTIM2
        LDA     PORTB           ;GET STEPPER PHASE BITS
        AND     #STPMSK         ;MASK OFF OTHER BITS
        INX                     ;INCREMENT X
        BMI     STEPI           ;IF X >= $80, GOTO STEP IN
        INC     PRSTRK          ;INC INVERTED PRESENT TRACK #
        LSR                     ;SHIFT STEPPER BITS RIGHT
        STA     STPSCR          ;SAVE IN SCRATCH
        AND     #^H002          ;CHECK LSB UNDERFLOW
        BEQ     STEP2           ;IF NONE, USE SCRATCH AS PORT B
        LDA     #SPHA4          ;ELSE, SET BIT 5
        ORA     STPSCR          ;EFFECT IS ROTATE CIRCULAR INNER 4 BITS
        BPL     STEP3           ;UNCONDITIONAL (ALWAYS +)
STEPI:  DEC     PRSTRK          ;ELSE, STEP IN (DEC INVERTED PRESENT TRACK)
        ASL                     ;LEFT SHIFT STEPPER BITS
        STA     STPSCR          ;SAVE IN SCRATCH
        AND     #^H040          ;MASK FOR STEPPER BIT OVERFLOW
        BEQ     STEP2           ;IF BIT RESET, SCRATCH WILL DO
        LDA     #SPHA1          ;ELSE, SET BIT 2
        ORA     STPSCR          ;OR IN REST OF STEPPER BITS
        BPL     STEP3           ;UNCONDITIONAL JUMP
STEP2:  LDA     STPSCR          ;ELSE, REGET SCRATCH
STEP3:  STA     PORTB           ;STUFF IN STEPPER PHASE BITS OF PIA
        DEY                     ;DECREMENT STEP COUNTER (SOMEONE ELSE IS WATCHING)
        RTS
;
;
;     *******************************
;       TEST SECTOR NUMBER VALIDITY
;     *******************************
;
;  UPON ENTRY: AUX1 CONTAINS LSB OF 16-BIT SECTOR # FROM COLLEEN
;              AUX2 CONTAINS MSB "  "  "    "     "   "     "
;
;  UPON EXIT: CARRY=0 IF SECTOR # IS VALID, ELSE CARRY=1 FOR INVALID
;             A,X NOT PRESERVED
;
;
TSN:    LDX     AUX2            ;GET MSB IN X
        LDA     AUX1            ;AND LSB IN ACCUM
        BEQ     TSN2            ;IF LSB=0, GOTO TSN2
TSN1:   CPX     #3              ;CHECK MSB > = 3
        BCS     TSN3            ;IF MSB > = 3, ERROR EXIT
        CMP     #^H0D1          ;ELSE, LSB > = $D1?
        BCS     TSN4            ;IF SO, CHECK MSB SPECIAL CASE
        CLC                     ;C=0, GOOD RETURN
        RTS
TSN2:   CPX     #0              ;MSB ALSO = 0?
        BNE     TSN1            ;NO, GOTO UPPER BOUND CHECK
TSN3:   SEC                     ;ELSE, C=1 RETURNS ERROR
        RTS
TSN4:   CPX     #2              ;MSB SPECIAL CASE?
        BEQ     TSN3            ;YES, AN ERROR CALLED FOR
        CLC                     ;C=0 FOR GOOD RETURN
        RTS                     ;
;
;
;     **********************************
;       19.2 KBAUD COMMAND FRAME INPUT
;     **********************************
;
;  USED FOR COMMAND FRAME INPUT FROM SERIAL BUS
;
;  UPON ENTRY: X IS OFFSET FROM UNITNB FOR START OF INPUT BUFFER
;               INSCRT = MAXIMUM OFFSET VALUE FOR X
;
;               A BYTE IS READ FROM THE SERIAL BUS & PUT INTO LOCATION
;               UNITNB+X, INVERTED.  X IS INCREMENTED & WHEN X REACHES
;               (INSCRT), THE ROUTINE IS EXITED.
;
;  UPON EXIT: Y=$80 ON EXIT
;             X=(INSCRT)
;
;
RCSER:  LDA     PORTB           ;GET SERIAL DATA INPUT
        BPL     RCSER           ;WAIT FOR LOGIC 1 (START BIT?)
        TXA                     ;IF X=0,
        BEQ     RCS2            ;SKIP PREVIOUS INPUT INVERTING
        LDA     UNITNB-1(X)     ;ELSE, GET PREVIOUS DATA
        EOR     #^H0FF          ;INVERT IT
        STA     UNITNB-1(X)     ;SAVE IT BACK OUT
RCS1:   NOP                     ;2 CYCLES
        NOP                     ;2 MORE
        NOP                     ;ANOTHER 2
        LDY     #^H080-8        ;LOOP COUNTER FOR 8 ITER.
RCBIT:  NOP                     ;2 CYCLE WAIT
        NOP                     ;2 MORE
        NOP                     ;ANOTHER 2
        NOP
        LDA     PORTB           ;GET SERIAL INPUT DATA IN BIT 7
        ROL                     ;DATA TO CARRY
        ROR     UNITNB(X)       ;ROTATE CARRY INTO BIT 0 OF CURRENT BUFFER BYTE
        INY                     ;INCREMENT LOOP COUNTER
        BPL     RCBIT           ;GO FOR ALL EIGHT BITS
        INX                     ;ANY MORE BYTES TO GET?
        CPX     INSCRT          ;IF X=(INSCRT), THEN NO, EXIT
        BNE     RCSER           ;GOTO TOP OF ROUTINE (STOP BIT GET?)
        LDA     UNITNB-1(X)     ;GOT THE LAST ONE, NOW LET'S INVERT IT
        EOR     #^H0FF          ;EOR INVERTS
        STA     UNITNB-1(X)     ;PUT IT BACK
        RTS                     ;NOW EXIT
;
RCS2:   NOP                     ;IF X=0 ON ENTRY, COME THRU HERE
        NOP                     ;THIS WASTES A LITTLE TIME
        NOP                     ;SO SERIAL BUS CAN GET READY
        JMP     RCS1            ;GO GET A SERIAL BYTE
;
;
;      *******
;       TIMER
;      *******
;
;
;  THE DELAY PROVIDED BY THIS TIMER IS GIVEN BY THE
;  FOLLOWING FORMULA  (INCLUDING JSR & RTS OVERHEAD):
;
;   # CLK CYCLES = 1288*(ACCUM-1) + 5*(YREG-1) + 28 (6507 CLOCKS AT 500 KHZ)
;
;
TIMER:  STA     TMRSCR
TIMR1:  DEY
        BNE     TIMR1
        DEC     TMRSCR
        BNE     TIMR1
        RTS
;
;     ********************
;       MISC SUBROUTINES
;     ********************
;
;
MOTFU:  LDA     PORTA           ;GET PORT A FOR BIT TEST
        LSR                     ;SHIFT OUT BIT 1
        LSR                     ;CARRY=MOTOR ON/SELECT STATUS
        LDA     #MTR0           ;ASSUME WE'RE SELECTED
        BCS     ORST0           ;SKIP OVER ACCUMULATOR CLEAR IF TRUE
        RTS                     ;OR IN 0 MAY AS WELL LEAVE UNTOUCHED!!!
ORST0:  ORA     STAT0           ;OR INTO STAT0
        STA     STAT0           ;SAVE IN STAT0
        RTS
;
OR0ST0: PHA                     ;SAVE ENTRY ACCUMULATOR
        LDA     #0              ;CLEAR A
        STA     STAT0           ;0 -> STAT0
        PLA                     ;GET ENTRY ACCUMULATOR BACK
        JMP     ORST0           ;OR IT IN TO STAT0
;
ORINST: ORA     INSTAT          ;OR ACCUMULATOR INTO INTERNAL STATUS REGISTER
        STA     INSTAT          ;AND SAVE
        RTS
;
ANDINS: AND     INSTAT          ;AND ACCUMULATOR WITH INSTAT
        STA     INSTAT          ;AND SAVE IT
        RTS
;
WPSTAT: LDA     #WPS0           ;GET WRITE PROTECT ON STATUS (CLEAR OTHERS)
;
ANDST0: AND     STAT0           ;AND ACCUM WITH STAT0
        STA     STAT0           ;SAVE IT
        RTS
;
;
;     *******************************
;       19.2 KBAUD INPUT DATA FIELD
;     *******************************
;
;   FUNCTION: READS 128 BYTES FROM SERIAL BUS TO DATA BUFFER
;               THEN, READS 1 MORE BYTE TO UNITNB+6 (CHKSUM)
;
TERM    =       CHKSUM-UNITNB+1
;
;
RCDAT:  LDX     #0              ;CLEAR STBUF INDEX
        LDY     #TERM           ;SET-UP INSCRT FOR RCSER
        STY     INSCRT          ;FOR RCSER CALL FOR CHECKSUM
WSB:    LDA     PORTB           ;CHECK SERIAL DATA INPUT
        BPL     WSB             ;WAIT FOR START BIT, INVERTED
        TXA                     ;CHECK FOR X=0
        BEQ     RCD3            ;IF ZERO, SKIP PREVIOUS DATA INVERSION
        LDA     STBUF-1(X)      ;ELSE, GET PREVIOUSLY REC'D DATA
        EOR     #^H0FF          ;EOR INVERTS IT
        STA     STBUF-1(X)      ;PUT BACK INVERTED VERSION
RCD0:   NOP                     ;DELAY 2 CYCLES
        NOP                     ;2 MORE
        NOP                     ;ANOTHER 2
        LDY     #^H080-8        ;LOOP COUNTER FOR 8 ITER.
RCD1:   NOP                     ;MORE WAITING
        NOP
        NOP
        LDA     STBUF           ;4 CYCLE TIME WASTER
        LDA     PORTB           ;GET SERIAL DATA IN BIT 7
        ROL                     ;ROTATE IT INTO CARRY BIT
        ROR     STBUF(X)        ;NOW ROTATE INTO BIT 0 OF CURRENT DATA ENTRY
        INY                     ;INCREMENT LOOP BIT COUNTER
        BPL     RCD1            ;GO FOR 8 BITS
        INX                     ;INCREMENT BYTE COUNTER
        BPL     RCD2            ;LOOP FOR MORE IF NOT 128 YET
        LDX     #TERM-1          ;GET INDEX FOR RCSER (CHKSUM)
        JSR     RCSER           ;GET CHECKSUM BYTE
        LDA     PNTRH-TERM(X)   ;X=6,SO THIS FETCHES LAST DATA BYTE FROM BUFFER
        EOR     #^H0FF          ;INVERT LAST BYTE
        STA     PNTRH-TERM(X)   ;PUT INVERTED VERSION BACK
        RTS
;
RCD2:   NOP                     ;DELAY BEFORE GETTING NEXT DATA BYTE
        JMP     WSB             ;GOTO WAIT FOR START BIT
;
RCD3:   NOP             ;IF X=0, THIS DELAY IS EQUIV OF PREVIOUS BYTE INVERT
        NOP
        NOP
        JMP     RCD0            ;NOW WE'RE READY, GO FOR 1ST DATA BYTE
;
;
;     ********************
;       CHECKSUM COMPUTE
;     ********************
;
;  UPON ENTRY:  X IS STARTING OFFSET FROM UNITNB
;               (INSCRT) IS ENDING OFFSET FROM UNITNB
;
;  UPON EXIT:   ACCUM HAS CHECKSUM OF ALL BYTES OFFSET FROM UNITNB
;               BETWEEN STARTING X AND (INSCRT)-1.
;               EXIT FLAGS ARE A RESULT OF CMP OF ACCUM WITH UNITNB+(INSCRT)
;
;
;
CSUM:   LDA     #0              ;INIT CHECKSUM TO 0
        CLC                     ;C=0 TOO
CS1:    ADC     UNITNB(X)       ;ADD A BYTE INTO CHECKSUM
        PHP                     ;SAVE CARRY ON STACK
        INX                     ;INCREMENT INDEX
        CPX     INSCRT          ;CHECK WITH TERMINATING OFFSET
        BEQ     CS2             ;IF DONE, EXIT LOOP
        PLP                     ;ELSE, REGET CARRY FROM STACK
        JMP     CS1             ;ADC IT INTO CHECKSUM WITH NEXT BYTE
;
CS2:    PLP                     ;REGET LAST CARRY FROM STACK
        ADC     #0              ;ADD IN THAT CARRY BIT
        CMP     UNITNB(X)       ;COMPARE WITH LAST BYTE (CHECKSUM)
        RTS                     ;LEAVE COMPARE FLAGS IN P BEFORE RETURN
;
;
;     ***********************
;       DATA FIELD CHECKSUM
;     ***********************
;
;  UPON ENTRY: DATA BUFFER AT STBUF IS ASSUMED LOADED
;
;  UPON EXIT: CHECKSUM (CHKSUM) COMPARED TO GENERATED CHECKSUM
;               FLAGS SET ACCORDINGLY AT EXIT
;
DTCS:   LDA     #0              ;CLEAR ACCUM
        TAX                     ;AND X TOO
        CLC                     ;C=0 FOR ADC
SUML:   ADC     STBUF(X)        ;ADD IN DATA
        INX                     ;INCREMENT INDEX
        BPL     SUML            ;GO FOR ALL 128
        ADC     #0              ;ADD IN CARRY TOO
        CMP     CHKSUM          ;SET FLAGS FROM COMPARE
        RTS
;
;
;     *******************
;       SET OUTPUT PORT
;     *******************
;
;  UPON ENTRY:  DATA DIRECTION BIT FOR SERIAL DATA OUTPUT IS
;               CHANGED FROM INPUT TO OUTPUT.
;
;       ROUTINE READS PBDDR, SETS STOP BIT (BIT0) AND WRITES IT BACK
;
;  UPON EXIT:   DATA DIRECTION FOR SERIAL DATA OUTPUT IS NOW SET FOR OUTPUT
;               ACCUMULATOR AND Y REGS PRESERVED
;
;
SOP:    PHA
        LDA     PORTB           ;READ PORTB
        ORA     #SEROUT         ;MAKE SURE STOP BIT IS PRESENT
        STA     PORTB           ;WRITE IT TO BUFFERED PIA
        LDA     PBDDR           ;FETCH DATA DIRECTION REG
        ORA     #SEROUT         ;SET BIT0 TO OUTPUT DIRECTION
        STA     PBDDR           ;WRITE NEW DATA DIRECTION REG, SERIAL OUTPUT READY
        PLA
        RTS
;
;      ****************
;       SEND ACK/ACCUM
;      ****************
;
;       FUNCTION: SENDS AN ACK BYTE ($41) OVER SERIAL BUS
;
;
;
SAA:    BIT     PORTB           ;CHECK COMMAND FRAME OF PORT B
        BVS     SAA             ;WAIT UNTIL COAST IS CLEAR
SAA1:   LDA     #ACK            ;GET ACK BYTE TO SEND
SAA2:   STA     SDATA           ;IN SERIAL DATA REG
        JSR     SOP             ;SET SERIAL OUTPUT DIRECTION TO OUTPUT
        JSR     XMSER           ;SEND BYTE IN SDATA
        JSR     DBOT2           ;SET SERIAL OUTPUT DIRECTION TO INPUT
;
;  IF INITIALIZATION HASN'T HAPPENED YET, NOW IS THE BEST TIME TO DO IT
;
        LDA     INSTAT          ;CHECK INTERNAL STATUS REGISTER
        AND     #NOINIT         ;HAS DRIVE BEEN HOMED YET?
        BEQ     NOT1ST          ;YES, IT'S NO VIRGIN
        LDA     #SELDSK         ;ELSE, TURN ON MOTOR
        STA     PORTA           ;
        STA     A384            ;FORCE IRQ LO
        LDA     #FORCEI
        STA     COMAND          ;FORCE INTERRUPT THE 1771
        JSR     SPSTB           ;WAIT 200 MS
HHOME:  LDY     #MAXTRK         ;LET'S STEP IT OUT 39 TIMES
;
;  UPON ENTRY: Y IS # OF STEP OUTS CONSTITUTING A "HOME"
;
REHME:  LDX     #SPHA1+SPHA4    ;SET STEPPER BITS 1 & 4
        STX     PORTB           ;STUFF IN STEPPER CONTROL PORT B
REHM1:  JSR     STEP            ;X<=$7E, SO STEP OUT (DEC Y TOO)
        BMI     STRK            ;EXIT IF STEP MADE Y NEGATIVE
        JSR     WAITIM          ;WAIT TIL STEP TIMER RUNS OUT
        BMI     REHM1           ;WAITIM ALWAYS RETURNS N=1, SO UNCOND. LOOP
;
STRK:   STY     PRSTRK          ;PUT $FF IN PRESENT TRK (INVERTED 0)
        LDA     #20             ;PUT DELAY IIN THIS WAY
        JSR     TIMER           ;EXIT THRU TIMER
;
        LDA     #^CNOINIT       ;CLEAR NOINIT FLAG
        JSR     ANDINS          ;IN INTERNAL STATUS REGISTER
        LDA     #^H0FF          ;LOAD PIA TIMER TO MAX
        STA     WRTIM3
;
NOT1ST: RTS
;
;
;     *********************************
;       19.2 KBAUD SINGLE BYTE OUTPUT
;     *********************************
;
;  FUNCTION:  SENDS A SINGLE BYTE (SDATA) OVER SERIAL BUS
;
;                       SDATA SHIFT REG
;       START BIT    D0 D1 D2 D3 D4 D5 D6 D7     STOP BIT
;       ---------    -- -- -- -- -- -- -- --     --------
;            0       X  X  X  X  X  X  X  X        1
;
;   UPON EXIT:  X IS INCREMENTED, Y=$80, ACCUM NOT PRESERVED
;
XMSER:  LDA     PORTB           ;READ PORT B
XM1:    AND     #^CSEROUT       ;CLEAR SERIAL OUTPUT BIT (BIT0)
        STA     PORTB           ;WRITE A 0 START BIT
        LDY     #^H080-8        ;SET LOOP COUNTER FOR 8 BITS
        BIT     STBUF           ;WAIT 4 CYCLES
XMS1:   BIT     STBUF           ;ANOTHER 4 CYCLES
        NOP                     ;2 CYCLES
        NOP                     ;2 CYCLES
        LSR                     ;SEND OLD BIT0 TO CARRY
        ROR     SDATA           ;GET NEW CARRY FROM NEXT LO ORDER BIT OF SDATA
        ROL                     ;GET NEW BIT0 FROM CARRY
        STA     PORTB           ;SEND IT OVER SERIAL OUTPUT
        INY                     ;ADVANCE LOOP COUNTER
        BPL     XMS1            ;IF NOT 8 BITS YET, LOOP
        LSR                     ;ELSE, PUT LAST BIT0 IN CARRY
        SEC                     ;C=1
        ROL                     ;SET BIT0 (STOP BIT IS 1)
        NOP
        NOP
        NOP                     ;WAIT 6*2 CYCLES
        NOP
        NOP
        NOP
        STA     PORTB           ;SEND STOP BIT
        INX                     ;INCREMENT X (FOR EXTERNAL USAGE)
        RTS                     ;WE'RE DONE
;
;
;     ***********************************************
;       DATA BUFFER OUTPUT, RESET SERIAL BUS OUTPUT
;     ***********************************************
;
; BYTE IN SDATA, THEN 128 BYTE BUFFER AT STBUF IS SENT OVER SERIAL BUS,
;  THEN THE CHECKSUM BYTE AT CHKSUM IS SENT, AFTER WHICH THE SERIAL
; OUTPUT BIT IS TURNED OFF.
;
;
DBOT:   BIT     PORTB           ;CHECK COMMAND FRAME LO
        BVS     DBOT            ;WAIT UNTIL COAST IS CLEAR
;
;
        LDX     #^H0FF          ;X=$FF
        JSR     XMSER           ;SNED BYTE IN SDATA OVER SERIAL BUS
DBOT1:  LDY     STBUF(X)        ;X=0 1ST TIME, GET BUFFER BYTE
        STY     SDATA           ;PUT IN SERIAL SHIFT REGISTER
        JSR     XM1             ;SEND IT
        BPL     DBOT1           ;GO FOR 128 BYTES
        LDY     CHKSUM          ;NOW, GET CHECKSUM
        STY     SDATA           ;IN SERIAL SHIFT REGISTER
        JSR     XM1             ;SEND IT TOO
DBOT2:  LDA     PBDDR           ;FETCH DATA DIRECTION BITS
        AND     #^CSEROUT       ;SET SERIAL OUTPUT BIT TO INPUT DIRECTION
        STA     PBDDR           ;TELL PORT B
        RTS
;
;
;      **********
;       WP SENSE
;      **********
;
;
;       CHECKS PORT A (BIT 4) WRITE PROTECT STATUS
;       AND STUFFS THIS BIT IN BIT 3 OF STAT0
;
WPSNS:  LDA     PORTA           ;GET PORT A BITS
        AND     #WRPRO          ;MASK OFF ALL BUT WP STATUS
        BEQ     WPS1            ;IF NOT PROTECTED, RESET BIT 3, STAT0
        LDA     #WPS0           ;ELSE, SET BIT 3 OF STAT0
        JMP     ORST0           ;ORST0 ORS IT IN
;       RTS
;
WPS1:   LDA     #^CWPS0         ;MASK TO RESET BIT 3
        JMP     ANDST0          ;ANDST0 ANDS STAT0
;       RTS
;
;
;
;
;      *************************
;       SPM ON TEST AND TURN ON
;      *************************
;
;       ROUTINE CHECKS MOTOR ON STATUS
;          IF ON, SETS SPMSDC TO 1 AND RETURNS
;
;          IF OFF, TURNS IT ON, USES 1771 SEEK AS TIMER, SETS SPMSDC TO 1
;       AND RETURNS.
;
;
SPMON:  LDA     PORTA           ;GET PORT A BITS
        AND     #SELDSK         ;IS THIS DRIVE SELECTED ALREADY?
        BNE     SPO1            ;IF SO, SKIP DRIVE TURN-ON
        LDA     #PROCED+SELDSK  ;ELSE, BETTER TURN IT ON
        STA     PORTA
        STA     DATA            ;PUT A 10 IN TRACK TO SEEK
        LDA     #22             ;PUT A 22 IN 1771 CURRENT TRACK
        STA     TRACK           ;60 MS TIMER LOADED
        LDA     #SEEK           ;USE 1771 SEEK AS A TIMER
        STA     COMAND          ;1/2 SEC FOR MOTOR TO COME UP TO SPEED
SPO1:   LDA     ONWAIT          ;GET MOTOR ON TIME CONSTANT
        STA     SPMSDC          ; SPMSDC
        RTS
;
;
;      ******************
;       SECTOR BREAKDOWN
;      ******************
;
;
;       UPON ENTRY:  USES AUX1 (LSB) AND AUX2 (MSB) OF 16-BIT SECTOR #
;                TO FORM TRACK (TRKNBR) AND SECTOR (SCTNBR) FOR 1771
;
;       UPON EXIT:  A CONVOLUTED DIVIDE BY 18 ROUTINE GENERATES
;                       A TRKNBR AND SCTNBR
;               AUX1, AUX2, ALL REGISTERS NOT PRESERVED
;
;
SECBD:  LDA     AUX1
        LDX     #4
RLP:    ROR     AUX2
        ROR
        DEX
        BNE     RLP
        ROR     AUX2
        LDX     #4
LLP:    LSR     AUX2
        DEX
        BNE     LLP
SBD2:   ASL
        CMP     AUX2
        BCS     SBD1
        STA     AUX1
        LSR
        EOR     #^H0FF
        STA     TRKNBR
        SEC
        LDA     AUX2
        SBC     AUX1
        EOR     #^H0FF
        STA     SCTNBR
        RTS
SBD1:   LSR
        TAX
        DEX
        LDA     #^H010
        CLC
        ADC     AUX2
        STA     AUX2
        TXA
        JMP     SBD2
;
;     ******************
;       SECTOR BUILDUP
;     ******************
;
;
;  UPON ENTRY:  INPUTS ARE PRSTRK AND SCTNBR WHICH ARE USED TO
;               FORM 16-BIT SECTOR # FOR BAD SEC# BUFFER
;
;  UPON EXIT:   16-BIT RESULT IS STORED AT (STBUF+XREG) & (STBUF+1+XREG)
;               X IS INCREMENTED BEFORE EXIT
;
SBLD:   LDY     #0              ;CLEAR MSB
        STY     MSBSB           ;IN SECTOR BUILD-UP SCRATCH
        LDA     SCTNBR          ;FETCH SECTOR #
        EOR     #^H0FF          ;INVERT IT FOR TRUE LOGIC
        STA     INVSNB          ;SAVE IN ANOTHER SCRATCH
        LDA     PRSTRK          ;GET TRACK #
        EOR     #^H0FF          ;INVERT IT FOR TRUE LOGIC ALSO
        ASL                     ;SHIFT LEFT MMULTIPLIES BY 2
        STA     TMPSB           ;SAVE IN ANOTHER SCRATCH
        ASL                     ;*4
        ASL                     ;*8
        ROL     MSBSB           ;SAVE OVERFLO IN MSB BIT 0
        ASL                     ;*16
        ROL     MSBSB           ;PREV OVERFLO TO BIT 1, THIS OVERFLO TO BIT 0
        ADC     TMPSB           ;+ *2 PARTIAL PRODUCT (C=0 FROM MSB OF MSBSB)
        TAY                     ;LSB GOES TO Y
        LDA     #0              ;CARRY MUST BE ADDED TO MSB
        ADC     MSBSB           ;ADD IN CARRY
;
        STA     MSBSB           ;SAVE CALCULATED MSB IN MSMSB
        TYA                     ;GET LSB IN A
        CLC                     ;C=0 FOR ADC
        ADC     INVSNB          ;ADD IN POSITIVE LOGIC SECTOR #
        STA     STBUF(X)        ;SAVE IN BAD SEC# BUFFER (INDEX'D BY X)
        LDA     #0              ;WE NEED TO ADD OVERFLO TO MSB
        ADC     MSBSB           ;ADD CARRY TO MSB
        INX                     ;INCREMENT X
        STA     STBUF(X)        ;SAVE LSB IN ADJACENT BYTE
        RTS                     ;WE'RE DONE
;
;
;
;     *******************************
;       WAIT 200 MS BEFORE STEPPING
;     *******************************
;
;  WAITS 200 MS IF SPINDLE MOTOR NOT READY FLAG IS SET
;
;   ELSE, JUST RETURNS IMMEDIATELY
;
;
SPSTB:  LDA     INSTAT
        AND     #SMNRDY
        BEQ     MTSTB
        LDA     #^H050          ;WAIT 200 MS BEFORE ANY STEPPING
        JSR     TIMER
MTSTB:  RTS
;
        .NLIST
;
;
;
;                 LOOK MA,
        .ASCII  ' DAVE STAUGAS '
;                 WAS HERE!!!
;               12 JANUARY 1981
;
        .LIST
ENDCOD  =       .                       ;1ST FREE BYTE STARTS HERE
AVAIL   =       VEC-ENDCOD              ;# OF FREE BYTES IN THIS ROM
;
;  VECTORS
;
        .=^H0FFC
VEC:    .WORD   INIT,INIT
        .END
