; CC.PROM.TEXT ---------------------------------------------------------
;
;       CC.PROM -- Corvus CONCEPT Workstation PROM
;
;       Copyright 1982 Corvus Systems, Inc.
;                      San Jose, California
;
;       All Rights Reserved
;
;       v 0.1  05-06-82  LEF  Original program
;       v 0.2  05-27-82  LEF  Add keyboard driver (kb)
;                             Add display driver (mb)
;                             Add 8" Corvus floppy disk driver/boot
;                             Finds first local disk for booting
;                             OMNINET disk driver modifications
;       v 0.3  06-21-82  LEF  Add MACSBUG interface
;                             Add 5" floppy driver/boot
;                             Finds first floppy disk for booting
;                             Local disk driver modifications
;                             OMNINET disk driver modifications
;       v 0.4  06-29-82  LEF  Add time out to OMNINET short commands
;       v 0.5  08-18-82  KB   Modified MacsBug interface
;                             Modified Apple 5" floppy driver
;       v 0.6  10-29-82  LEF  Swap BACKSPACE and \ keys
;                             Modified display driver
;       v 0.7  05-01-83  LEF  Remove Apple floppy boot
;                             Add auto PROM boot
;                             OMNINET disk driver modifications
;                        KB   Add DSDD floppy driver/boot (rev B)
;       v 0.8  07-01-83  LEF  Add Concept Plus, Uniplex, Venus support
;              09-14-83  KB   Enhanced and changed Uniplex support
;       v 1.0  01-18-84  KB   fixed to Const II boot on omnidisk
;              01-27-84  LEF  Boot from selected server
;
;{!UP} Uniplex version                                                  0.8
;-----------------------------------------------------------------------
;{!UP} {/SC} {UC}       ;select generic Uniplex/Concept Plus            0.8
;
;
;{!UP} {/SC} {MM}       ;select units with a memory mgt board           0.8
;
; File: P.PROM.TEXT
; Date: 27-Jan-84
; By:   L. Franklin changes by K. Ball
;
;                        +-----+----------------+
; Stag PROM checksums:   |     |                |
;                        |  H  |                |
;                        |     |                |
;                        +-----+----------------+
;                        |     |                |
;                        |  L  |                |
;                        |     |                |
;                        +-----+----------------+
    page
; File: CC.PROM.EQ.TEXT
; Date: 18-Jan-84
;

PROMvers equ    1               ;Current PROM version number
PROMlevl equ    0         ;{!UP};Current PROM level number
                                ;
RAMbase  equ    $00000          ;Base address of low RAM
RAMlen   equ    $1000           ;Length of low RAM (4k bytes)
RAMkbbuf equ    RAMbase+$300    ;Start of keyboard buffer
RAMkblen equ    $100            ;Length of keyboard buffer
RAMmxbug equ    RAMbase+$400    ;Start of MACSBUG RAM
RAMwksta equ    RAMbase+$700    ;Start of workstation RAM
RAMend   equ    RAMbase+RAMLen  ;End address + 1 of low RAM
                                ;
ROMbase  equ    $10000          ;Base address of workstation PROM
ROMlen   equ    $2000           ;Length of workstation PROM
ROMend   equ    ROMbase+ROMlen  ;End address + 1 of workstation PROM
                                ;
MXBbase  equ    $20000          ;Base address of MACSBUG (if present)
MXBlen   equ    $2000           ;Length of MACSBUG
MXBend   equ    MXBbase+MXBlen  ;End address + 1 of MACSBUG
MXBinit  equ    MXBbase+4       ;Address of MacsBug init vector         0.5
MXBentry equ    MXBbase+8       ;Address of MacsBug entry vector        0.5
                                ;
IOPbase  equ    $30000          ;Base address of I/O pg
IOPprom  equ    IOPbase+$0200   ;Base address of slot PROMs             0.7
VIAbase  equ    IOPbase+$0F00   ;Base address of VIA registers
                                ;
                                ;
DSPbase  equ    $880000         ;{!UC}Base address of display buffer    0.8
DSPlen   equ    $00E000         ;{!UC}Length of display buffer          0.8
DSPend   equ    DSPbase+DSPlen  ;{!UC}End address + 1 of display buffer 0.8
USRbase  equ    $080030         ;{!UC}Base address of user RAM          0.8
                                ;
                                ;
MEMprom  equ    $90000          ;memory limit if not in PROM            0.7
MEM256k  equ    $BFFFC          ;memory limit for 256k system           0.7

MEM512k  equ    $FF7FC          ;{!UC};memory limit for 512k system     0.8

        page
;
; Corvus CONCEPT Workstation interrupt vector definition
;
IVlvl1   equ    $64             ;level 1 interrupt vector (SLOTS)
IVlvl2   equ    $68             ;level 2 interrupt vector (DC1)
IVlvl3   equ    $6C             ;level 3 interrupt vector (OMNINET)
IVlvl4   equ    $70             ;level 4 interrupt vector (DC0)
IVlvl5   equ    $74             ;level 5 interrupt vector (TIMER)
IVlvl6   equ    $78             ;level 6 interrupt vector (KYDB)
IVlvl7   equ    $7C             ;level 7 interrupt vector

;
; Corvus CONCEPT Workstation static RAM address definition
;
CPbtslot equ    RAMwksta+$000   ;(700-700) boot slot number
CPbtsrvr equ    RAMwksta+$001   ;(701-701) boot server number
                                ;(702-705)
CPosslot equ    RAMwksta+$006   ;(706-706) OS volume slot number
CPossrvr equ    RAMwksta+$007   ;(707-707) OS volume server number
CPosdrv  equ    RAMwksta+$008   ;(708-708) OS volume drive number
CPosblk  equ    RAMwksta+$009   ;(709-70B) OS volume block number
CPtrnvrs equ    RAMwksta+$00C   ;(70C-70C) OMNINET transporter version
CPtprnbr equ    RAMwksta+$00D   ;(70D-70D) OMNINET transporter number
CPdiskRC equ    RAMwksta+$00E   ;(70E-70E) disk controller return code
CPomniRC equ    RAMwksta+$00F   ;(70F-70F) OMNINET return code
                                ;
CPtimout equ    RAMwksta+$010   ;(710-713) boot disk timeout value      0.7
CPblkio  equ    RAMwksta+$014   ;(714-717) boot disk blk i/o subr pointer
CPdskio  equ    RAMwksta+$018   ;(718-71B) boot disk i/o subr pointer
                                ;
CPuserID equ    RAMwksta+$01C   ;(71C-725) user name (decrypted)
CPusernm equ    RAMwksta+$026   ;(726-72F) user name (encrypted)
                                ;
CPfinlv  equ    RAMwksta+$030   ;(730-733) floppy interleave table pointer
CPfdvsz  equ    RAMwksta+$034   ;(734-735) floppy device size (blocks)
CPfbps   equ    RAMwksta+$036   ;(736-737) floppy bytes per sector
CPfspt   equ    RAMwksta+$038   ;(738-738) floppy sectors per track
CPftps   equ    RAMwksta+$039   ;(739-739) floppy tracks per side
CPfspd   equ    RAMwksta+$03A   ;(73A-73A) floppy sides per disk
CPfofst  equ    RAMwksta+$03B   ;(73B-73B) floppy first track offset
CPftyp   equ    RAMwksta+$03C   ;(73C-73C) floppy type
                                ;
CPwndrcd equ    RAMwksta+$040   ;(740-763) system window record (36 bytes)
CPscnofs equ    RAMwksta+$064   ;(764-765) bytes per display scan line
CPdspflg equ    RAMwksta+$066   ;(766-766) display flags
CPbblen  equ    RAMwksta+$067   ;(767-767) blind boot user name length  0.8
CPbbname equ    RAMwksta+$068   ;(768-76F) blind boot user name         0.8

        page
CPsl1typ equ    RAMwksta+$071   ;(771-771) slot device type for slot 1
CPsl2typ equ    RAMwksta+$072   ;(772-772) slot device type for slot 2
CPsl3typ equ    RAMwksta+$073   ;(773-773) slot device type for slot 3
CPsl4typ equ    RAMwksta+$074   ;(774-774) slot device type for slot 4
CPsl5typ equ    RAMwksta+$075   ;(775-775) slot device type for slot 5
                                ;
CPuserpw equ    RAMwksta+$080   ;(780-787) user password (encrypted)    0.7
CPomnidv equ    RAMwksta+$090   ;(790-7CF) network devices (64 bytes)   0.7
                                ;          (indexed by transporter nbr) 0.7
CPsrvnam equ    RAMwksta+$0D0   ;(7D0-7D9) disk server name (10 bytes)  0.8
CPticks  equ    RAMwksta+$0DA   ;(7DA-7DD) 50 ms. ticks from timer drvr 0.8
                                ;
CPomnram equ    RAMwksta+$180   ;(880-88F) static RAM for OMNINET
CPsl1ram equ    RAMwksta+$200   ;(900-9FF) static RAM for slot 1 device
CPsl2ram equ    RAMwksta+$300   ;(A00-AFF) static RAM for slot 2 device
CPsl3ram equ    RAMwksta+$400   ;(B00-BFF) static RAM for slot 3 device
CPsl4ram equ    RAMwksta+$500   ;(C00-CFF) static RAM for slot 4 device
                                ;
CPiobuf  equ    RAMwksta+$600   ;(D00-EFF) I/O buffer (512 bytes)
CPstack  equ    RAMwksta+$800   ;(F00-FFF) initial system stack
CPextcrt equ    RAMwksta+$800   ;(F00-F00) external CRT flag
CPsysst  equ    RAMwksta+$801   ;(F01-F01) system initialization status
CPistack equ    RAMwksta+$8FC   ;(FFC-FFC) initial system stack pointer
                                ;
CPomnibf equ    USRbase-$30     ;OMNINET driver buffer (48 bytes)       0.8
                                ;   (OMNINET can't access below $80000)
        page
;
; Corvus CONCEPT Workstation PROM address vector definitions
;
CPsysrst equ    ROMbase+$004    ;(10004) system restart pointer
CPuniqid equ    ROMbase+$008    ;(10008) unique workstation ID
CPromvrs equ    ROMbase+$00C    ;(1000C) PROM version number
CPromlvl equ    ROMbase+$00D    ;(1000D) PROM level number
CPcksum  equ    ROMbase+$00E    ;(1000E) PROM checksum
                                ;
CPobootj equ    ROMbase+$010    ;(10010) jump to OMNINET disk boot subr
CPoboot  equ    ROMbase+$012    ;(10012) OMNINET disk boot subr pointer
CPoblkio equ    ROMbase+$016    ;(10016) OMNINET disk blk i/o subr pointer
CPodskio equ    ROMbase+$01A    ;(1001A) OMNINET disk i/o subr pointer
                                ;
CPlbootj equ    ROMbase+$020    ;(10020) jump to local disk boot subr
CPlboot  equ    ROMbase+$022    ;(10022) local disk boot subr pointer
CPlblkio equ    ROMbase+$026    ;(10026) local disk blk i/o subr pointer
CPldskio equ    ROMbase+$02A    ;(1002A) local disk i/o subr pointer
                                ;
CPfbootj equ    ROMbase+$030    ;(10030) jump to floppy disk boot subr
CPfboot  equ    ROMbase+$032    ;(10032) floppy boot subr pointer
CPfblkio equ    ROMbase+$036    ;(10036) SSSD floppy blk i/o subr pointer
CPfsctio equ    ROMbase+$03A    ;(1003A) SSSD floppy sector i/o subr pointer
CPfinit  equ    ROMbase+$03E    ;(1003E) SSSD floppy initialization
;Pablkio equ    ROMbase+$042    ;(10042) Apple floppy blk i/o subr pointer
;Pasctio equ    ROMbase+$046    ;(10046) Apple floppy sector i/o subr pointer
;Painit  equ    ROMbase+$04A    ;(1004A) Apple floppy initialization
CPxblkio equ    ROMbase+$042    ;(10042) DDDS floppy blk i/o subr ptr   0.7
CPxsctio equ    ROMbase+$046    ;(10046) DDDS floppy sctr i/o subr ptr  0.7
CPxinit  equ    ROMbase+$04A    ;(1004A) DDDS floppy initialization     0.7
                                ;
CPkbinit equ    ROMbase+$050    ;(10050) initialize (reset) keyboard driver
CPkbgetc equ    ROMbase+$054    ;(10054) get a keyboard character
                                ;
CPdsinit equ    ROMbase+$060    ;(10060) initialize display driver
CPdsputc equ    ROMbase+$064    ;(10064) display a character
CPdsputs equ    ROMbase+$068    ;(10068) display a string
CPdscvuc equ    ROMbase+$06C    ;(1006C) convert character to upper case
                                ;
CPivec1  equ    ROMbase+$070    ;(10070) level 1 interrupt vector (SLOTS)
CPivec2  equ    ROMbase+$074    ;(10074) level 2 interrupt vector (DC1)
CPivec3  equ    ROMbase+$078    ;(10078) level 3 interrupt vector (OMNINET)
CPivec4  equ    ROMbase+$07C    ;(1007C) level 4 interrupt vector (DC0)
CPivec5  equ    ROMbase+$080    ;(10080) level 5 interrupt vector (TIMER)
CPivec6  equ    ROMbase+$084    ;(10084) level 6 interrupt vector (KYDB)
CPivec7  equ    ROMbase+$088    ;(10088) level 7 interrupt vector

        page
;
; Corvus CONCEPT Workstation I/O pg definitions
;
IOkybd   equ    VIAbase+$05     ;(30F05) level 6 - keyboard             0.7
IOdc0    equ    VIAbase+$25     ;(30F25) level 4 - data comm 0          0.7
IOdc1    equ    VIAbase+$45     ;(30F45) level 2 - data comm 1          0.7
IObootsw equ    VIAbase+$61     ;(30F61) boot selection switches
IObeepfq equ    VIAbase+$71     ;(30F71) beep frequency
IOtimr   equ    VIAbase+$7D     ;(30F7D) level 5 - timer                0.7
IOslot   equ    VIAbase+$7F     ;(30F7F) level 1 - slots (port A ORA)   0.7
IOomni   equ    VIAbase+$C1     ;(30FC1) level 3 - OMNINET              0.7

;
; Slot device types  (set in CPsl1typ..CPsl5typ)
;
DTndev   equ    0               ;no device
DTlocl   equ    1               ;local disk
DTomni   equ    2               ;OMNINET disk
DTc8     equ    3               ;Corvus 8" SSSD floppy disk
DTc5     equ    4               ;Corvus 5" SSSD floppy disk (unused)
DTa5     equ    5               ;Apple 5" floppy disk
                                ;
DTbank   equ    6               ;Corvus BANK
                                ;
DTf8     equ    7               ;Corvus 8" DSDD floppy disk
DTf5     equ    8               ;Corvus 5" DSDD floppy disk
DTf3     equ    9               ;Corvus 3" DSDD floppy disk

; Miscellaneous equates
;
off      equ    0               ;
on       equ    1               ;
false    equ    0               ;Pascal FALSE boolean
true     equ    1               ;Pascal TRUE boolean

jumpto   equ    $4EF9           ;"jmp" op code

DskRead  equ    $32             ;disk read command
DskWrit  equ    $33             ;disk write command

NTO1     equ    $000C0000       ;{!MM};network timeout - broadcast
NTO2     equ    $0FFFFFFF       ;{!MM};network timeout - normal traffic
                                ;{!MM}; (25 minutes)
    page   ;driver address equates
;
; CC.PROM.KB -- Keyboard driver equates
;
;
; ADDRESSES OF KEYBOARD UART'S I/O REGISTERS
;
KBRdata  EQU    VIAbase+$01     ;DATA INPUT PORT
KBRstat  EQU    VIAbase+$03     ;STATUS REGISTER
KBRcmnd  EQU    VIAbase+$05     ;COMMAND REGISTER
KBRcntl  EQU    VIAbase+$07     ;CONTROL REGISTER
;
; External CRT equates
;
KBcPort equ     $30f21          ;data comm 0 UART pointer
KBuDa   equ     0               ;UART data port offset
KBuSt   equ     2               ;UART status port offset
KBrdBit equ     3               ;busy bit for input


;
; CC.PROM.DS -- Display driver equates
;
DShomeH  equ    $88D55E         ;{!UC}horizontal home location          0.8
DShomeV  equ    $88D506         ;{!UC}vertical home location            0.8
DSdefOf  equ    96              ;default bytes per scan line
DScellW  equ    6               ;character cell width
DScellY  equ    10              ;character cell height
DSmaxXH  equ    719             ;120*DScellW-1
DSmaxYH  equ    559             ;56*DScellY-1
DSmaxXV  equ    557             ;93*DScellW-1
DSmaxYV  equ    719             ;72*DScellY-1
;
; External CRT equates
;
DScPort equ     $30f21          ;data comm 0 UART pointer
DSuDa   equ     0               ;UART data port offset
DSuSt   equ     2               ;UART status port offset
DSwrBit equ     4               ;busy bit for output



;
; CC.PROM.LD -- Local disk driver equates
;


;
; CC.PROM.OD -- Omninet disk driver equates
;
StrAdr  EQU     $30FA1  ;address of Transporter register
RdyAdr  EQU     $30F7F  ;address of VIA register A, used for Omninet ready

        page   ;driver address equates (continued)
;
; CC.PROM.FD -- Corvus 8" SSSD floppy disk driver equates
;
; FLOPPY MAIN EQUATES USED BY THE DRIVERS AND FORMAT CODE GROUPS.
; indices to code in static ram - ram is slot dependent $900 for
; $A00 for slot 2, $B00 for slot 3, and $C00 for slot 4.
;
BASERAM  equ    CPsl1ram        ;ADDRESS OF FIRST RAM FOR SLOTS
BLKSZ    equ    512             ;OS BLOCK SIZE
;
; SLOT BASE ADDRESSES
;
; The floppy controller is inserted into one of the slots.
; Each slot has two address select decodes coming to it.
; One is called NDEVSEL and the other is called SLOTSEL.
;
NDEV1AD   equ   $30001          ;ADRS OF NDEV0 (does not exist)
DEVADOFST equ   $20             ;OFFSET OF OTHER NDEVS ADRS
                                ;
SLOT1AD   equ   $30001          ;ADRS OF slot 0 (does not exist)
SLTADOFST equ   $200            ;ADRS OFST FOR OTHER SLOTS
                                ;
SLTSTAD   equ   $30A01          ;SLOT STATUS ADRS


;
; CC.PROM.FB -- Corvus 8" DDDS floppy disk driver equates
;
; I/O slot base address
;
IOSbase  EQU    $30001          ;as if a slot 0 existed
IOSoffst EQU    $20             ;offset to next slot
                                ;
DBPoffst EQU    $100            ;offset to next pg of static ram
MAXdrive EQU    4               ;maximum number of drives

    page    ;vectors
; Corvus CONCEPT Workstation PROM address vectors
;
         org    ROMbase         ;
         data.l 0               ;(10000) initial stack pointer
         data.l setup           ;(10004) start of PROM code
         data.l $FFFFFFFF       ;(10008) unique workstation ID
         data.b PROMvers        ;(1000C) PROM version number
         data.b PROMlevl        ;(1000D) PROM level number
         data.w $FFFF           ;(1000E) PROM checksum
                                ;
         data.w jumpto          ;(10010) jump to OMNINET disk boot subr
         data.l SBomni          ;(10012) OMNINET disk boot subr pointer
         data.l ODblkIO         ;(10016) OMNINET disk blk i/o subr pointer
         data.l ODdskIO         ;(1001A) OMNINET disk i/o subr pointer
         data.w 0               ;
                                ;
         data.w jumpto          ;(10020) jump to local disk boot subr
         data.l SBlocal         ;(10022) local disk boot subr pointer
         data.l LDblkIO         ;(10026) local disk blk i/o subr pointer
         data.l LDdskIO         ;(1002A) local disk i/o subr pointer
         data.w 0               ;
                                ;
         data.w jumpto          ;(10030) jump to floppy disk boot subr
         data.l SBflpy          ;(10032) floppy boot subr pointer
         data.l 0,0,0     ;{!MM};(10036) ....unused                     0.8
; ----   data.l ADblkIO         ;(10042) Apple floppy blk i/o subr      0.7
; ----   data.l ADsecIO         ;(10046) Apple floppy sector i/o subr   0.7
; ----   data.l ADinit          ;(1004A) Apple floppy initialization    0.7
         data.l FBblkIO         ;(10042) DSDD floppy blk i/o subr       0.7
         data.l FBsctIO         ;(10046) DSDD floppy sector i/o subr    0.7
         data.l FBinit          ;(1004A) DSDD floppy initialization     0.7
         data.w 0               ;

         data.l KBinit          ;(10050) initialize (reset) keyboard driver
         data.l KBgetch         ;(10054) get a keyboard character
         data.l 0               ;(10058)
         data.l 0               ;(1005C)

         data.l DSinit          ;(10060) initialize display driver
         data.l DSputch         ;(10064) display a character
         data.l DSputst         ;(10068) display a string
         data.l DScvtuc         ;(1006C) convert character to upper case

         data.l INTslot         ;(10070) level 1 interrupt vector (SLOTS)
         data.l INTdc1          ;(10074) level 2 interrupt vector (DC1)
         data.l INTomni         ;(10078) level 3 interrupt vector (OMNINET)
         data.l INTdc0          ;(1007C) level 4 interrupt vector (DC0)
         data.l INTtimr         ;(10080) level 5 interrupt vector (TIMER)
         data.l INTkybd         ;(10084) level 6 interrupt vector (KYDB)
         data.l INTlvl7         ;(10088) level 7 interrupt vector
         data.l 0               ;(1008C)
    list   0
msgcpy   data.b 'Copyright '    ;Corvus CONCEPT
         data.b '1984 Corvus '  ;   PROM
         data.b 'Systems, Inc.' ;      copyright notice
         data.b 0               ;
    list   1
    page    ;Setup

; Initialize Corvus CONCEPT hardware
;
Setup   move.w  #$2700,sr       ;set priority to 7, nmi interrupt only
        lea     CPistack.w, SP  ;set system stack pointer               0.8
;
; delay for possible Apple floppy reset  *kb 8/23/82*                   0.5
;                                                                       0.5
        MOVEQ   #-1, D0         ;Must wait at least 1 second            0.8
Setup1  DBRA    D0,Setup1       ;*                                      0.5
Setup2  DBRA    D0,Setup2       ;*                                      0.5
Setup3  DBRA    D0,Setup3       ;*                                      0.5
                                ;
        move.l  #VIAbase,a6     ;get pointer to VIA I/O locations
        clr.w   CPextcrt.w      ;reset system flags (CPextcrt, CPsysst)
        move.b  #$17,$07(a6)    ;kybd control, 600 baud, 8 bit word
        move.b  #$0B,$05(a6)    ;kybd command, no parity, no interrupts
        move.b  #$3E,$27(a6)    ;dcom0 control, 9600 baud, 7 bit word
        move.b  #$AB,$25(a6)    ;dcom0 cmd mark parity, xmit & rcv w/o IRQ
        move.b  #$3E,$47(a6)    ;dcom1 control, 9600 baud, 7 bit word
        move.b  #$AB,$45(a6)    ;dcom1 command
        clr.b   $7F(a6)         ;VIA port A -- ok to read port before  0.8
        clr.b   $61(a6)         ;VIA port B -- write byte of zero      0.8
        move.b  #$80,$67(a6)    ;VIA data direction A
        move.b  #$37,$65(a6)    ;VIA data direction B
        move.b  #$10,$77(a6)    ;free run shift register, counter
        move.b  #$0F,$75(a6)    ;symmetrical wave shape
        move.b  #$A0,$71(a6)    ;fairly low initial frequency
        tst.b   $C1(a6)         ;turn off possible OMNINET interrupt
        move.b  $01(a6),d0      ;clear keyboard data buffer
        move.b  $21(a6),d0      ;clear dcom0 data buffer
        move.b  $41(a6),d0      ;clear dcom1 data buffer

         page
; File: P.PROM.SP.TEXT
; Date: 12-Dec-83
;added altmap = 1
BusErrV  equ    $8              ;addr of bus error exception vector

mmuexcp  equ    $200001         ;MMU exception register, board 0
mmucntxt equ    $200201         ;MMU context register, board 0

Sg0RegLo equ    $280601         ;first seg register alt = 0
Sg1RegLo equ    $200601         ;first seg register alt = 1
SegRegHi equ    $3ffe01         ;last seg register
SegRegIn equ    $008000         ;seg register address increment
SegCntIn equ    $000800         ;seg reg inc between contexts
SegStart equ    $80             ;start value of seg register 16
SegPEnd  equ    $8F             ;end value for seg register 31

Pg0RegLo equ    $200401         ;first page register alt = 0
Pg1RegLo equ    $200401         ;first page register alt = 1
PagRegHi equ    $3ffc01         ;last page register
PagRegIn equ    $000800         ;page register address increment

NumCntxt equ    02              ;number of contexts

; setup bus error interrupt vector in static RAM
;
        lea     SPbusEr,a1      ;get bus error processing address
        move.l  a1, BusErrV.W   ;set bus error interrupt vector
SPbusRS move.b  #1,CPextcrt.w   ;use external CRT for messages
        move.b  #$0,MMUexcp.l   ;zero exception register to prevent
                                ;   parity error causing Bus Errors

    page
; Setup MMU so that logical addresses $80000 to $FFFFF access
; physical addresses $00000 to $7FFFF.  The Concept address space
; is mapped into the physical memory address space.
;
; The Concept memory map is selected on the processor board when the
; ALTMAP bit is 0.  Unfortunately, only 48 of the segment registers
; are accessible.  This is because the start of the segment registers is
; is at $280601 instead of $200601 but still must stop at $3FFE01.
;
; The MMU maintains 16 different contexts each with 64 segment registers
; representing the full logical address space (0 ... $1FFFFF).  Context 0
; is used by the Omninet Transporter for DMA in to/out of host memory.
; Context 1 is used when the processor is in supervisor (or privileged)
; state.  Contexts 2 to 15 are user contexts selectable via the context
; register and are active when the processor is in the user state.  Since
; CCOS runs only in supervisor state contexts 0 and 1 are only necessary
; to initialize.  For use by CCOS ALL the contexts are initialized to the
; same memory map.
;
; Setup the segment registers 16 to 31, inclusive.  These seg regs
; correspond to the logical addresses $80000 to $FFFFF.   We will
; use to first 16 page segments to refer to the physical memory.
; Therefore, put the page segment adresses 0 to 15, inclusive, into
; the segment registers 16 to 31, inclusive. (see ahead for page registers).
;
; Have BUG: try setting all 48 seg regs to 0 to F cyclically.
;
; Segment register form :
;
;   Bits 0 through 5 :  page segment number for this segment.
;   Bits 6 through 7 :  protection bits
;         00 :  no access
;         01 :  read only access
;         10 :  full access, read/write/execute  <== use this for CCOS
;         11 :  execute only access
;
        LEA     jack+$400000.L,A0
        jmp     (A0)           ;go to rom not affected by altmap
;
;  change altmap to allow access to segment 0
;
jack    move.b  #$50,$430f81.l  ;!!!!!! set altmap = 1
        ;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

        lea     Sg1RegLo+(00*SegRegIn).L,a0 ;get first seg register address
        movea.l a0, a1          ;save start address
        clr.l   d3              ;context increment (loop counter)
        move.w  #SegStart,d1    ;read/write in bits 6-7 and page segment 0
        move.l  #SegRegIn,d2    ;get address increment
        moveq   #NumCntxt-1,d4  ;number of contexts(loop counter)
        moveq   #63, d5         ;do all 64 seg regs

; for i := 0 to NumCntxt-1 do
;   repeat  set seg regs 00 to 63 for this context
;
SPseg01 move.b  d1, (a0)        ;set segment register
        adda.l  d2, a0          ;update seg register address
        addq.w  #1, d1          ;bump page segment number
        cmpi.w  #SegPEnd, d1    ;at end of seg registers?
        bls.s   SPseg1a         ;no, write next seg reg
        move.w  #SegStart,d1    ;reset page selector every 16 seg regs
SPseg1a dbra    d5,SPseg01      ;do for each 64 regs
        add.l   #SegCntIn, d3   ;do next context
        lea     0(a1,d3), a0    ;get start of seg regs for this context
        move.w  #SegStart,d1    ;read/write in bits 6-7 and page segment 0
        moveq   #63, d5         ;do all 64 seg regs
        dbra    d4,SPseg01      ;do next context

; Verify segment registers set.  Check for errors.
; note: this does not do thorough test
;
        lea     Sg1RegLo+(00*SegRegIn).L,a0 ;get first seg register address
        movea.l a0, a1          ;save start address
        clr.l   d3              ;context increment (loop counter)
        move.w  #SegStart,d1    ;read/write in bits 6-7 and page segment 0
        move.l  #SegRegIn,d2    ;get address increment
        moveq   #NumCntxt-1,d4  ;number of contexts(loop counter)
        moveq   #63, d5         ;do all 64 seg regs

; for i := 0 to NumCntxt-1 do
;   repeat  set seg regs 00 to 47 for this context
;
SPseg02 cmp.b   (a0), d1        ;is segment register good?
        beq.s   SPseg03         ;yes
        bset    #7, CPsysst.w   ;no, mark error
SPseg03 adda.l  d2, a0          ;update seg register address
        addq.w  #1, d1          ;bump page segment number
        cmpi.w  #SegPEnd, d1    ;at end of seg registers?
        bls.s   SPseg04         ;no, write next seg reg
        move.w  #SegStart,d1    ;reset page selector every 16 seg regs
SPseg04 dbra    d5,SPseg02      ;do for each 64 regs
        add.l   #SegCntIn, d3   ;do next context
        lea     0(a1,d3), a0    ;get start of seg regs for this context
        move.w  #SegStart,d1    ;read/write in bits 6-7 and page segment 0
        moveq   #63, d5         ;do all 64 seg regs
        dbra    d4,SPseg02      ;do next context
     page
; Page registers :
;
; All the page registers in MMU are arranged in 1 page file per memory
; board.  The page file is divided into 64 page segments.  The page
; segments are addressed by the contents of a segment register.  Page
; segments are divided into 16 page registers.  A specific page register
; within a page segment is addressed by bits a11 to a14 of the logical
; address presented to the MMU.  The data in the page register selects
; which physical memory page is accessed.
;
; The last page in the physical memory can never be accessed.  It's page
; address is $FF.  If it is ever accessed a Bus Error occurs.  It is
; reserved as the "invalid page" identifier.
;
; To get segment registers 16 thru 31 which point at page segments 0 thru
; 15 to access the physical memory 0 thru $7F7FF (all except last 2k page)
; contiguously set the page registers 0 thru $FE to 0 thru $FE.  Set page
; register $FF to $FF, to cause a Bus Error if any one tries to read
; or write it.
;
; Logical to physical address conversion :
;
; address lines :   | 2 2 2 | 2 1 1 1 1 1 | 1 1 1 1 | 1 0 0 0 0 0 0 0 0 0 0 |
;                   | 3 2 1 | 0 9 8 7 6 5 | 4 3 2 1 | 0 9 8 7 6 5 4 3 2 1 0 |
; field :           | x MMU |   segment   |  page   |        offset         |
;
; To get a byte accessed the logical address from the processor selects
; which segment register is used.  The data in the segment register selects
; which page segment is selected.  The page field in the logical address
; selects which page register in the page segment is selected.  It page
; field is an index into the page segment.  The data in the page register
; selects which 2k page in the physical address  is selected.  The offset
; in the logical address selects which byte in the 2k page of physical
; memory is accessed.
;
        movea.l #Pg1RegLo,a0    ;get first page register address
        clr.l   d1              ;get first page register value
        move.l  #PagRegIn,d2    ;get address increment

SPpag01 move.b  d1,(a0)         ;set page register
        adda.l  d2,a0           ;update page register address
        addq.w  #1,d1           ;update page register value
        cmp.w   #$FF,d1
        bls.s   SPpag01         ;for i:=0 to FF do
  page
; check page registers
;                               ;d1 is already zero (0)
        movea.l #Pg1RegLo,a0    ;get first page register address
        move.w  #PagRegIn,d2    ;get address increment

SPpag02 cmp.b   (a0), d1        ;is page reg set correctly
        beq.s   SPpag03         ;yes
        bset    #7, CPsysst.w   ;no, show error
SPpag03 adda.w  d2,a0           ;update page register address
        addq.w  #1,d1           ;update page register value
        cmp.w   #$FF,d1
        bls.s   SPpag01         ;for i:=0 to FF do

; all done
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
; set altmap = 0
         move.b    #$10,$430f81.l  ;*MC*
         LEA       SPdone.L,A0
         JMP       (A0)     ;go back to ROM in Concept space
;
SPdone



        bra.s   SetupCk         ;back to normal Concept boot PROM

;
; SPbusEr - Bus error processing
;
SPbusEr
        move.b  #$0,mmuexcp.l   ;zero exception register to prevent
                                ;   parity error causing Bus Errors
        lea     SPmgBus,a0      ;mesg -- Bus error
        bsr     DSputSt         ;*
        lea     CPistack.w, sp  ;reset stack pointer
        bra     SPbusRS         ;restart from init of MMU

SPmgBus data.b  'Bus error',DSCcr,0
        data.b  0            ;force even boundary

        page    ;SetMB
;
;  Check Corvus CONCEPT hardware  (test 1)
;
;       Verify ports making no data accesses
;
SetupCk cmpi.b  #$3E,$27(a6)    ;dcom0 control                          0.8
        bne.s   CHerr           ;
        cmpi.b  #$AB,$25(a6)    ;dcom0 command
        bne.s   CHerr           ;
        cmpi.b  #$3E,$47(a6)    ;dcom1 control
        bne.s   CHerr           ;
        cmpi.b  #$AB,$45(a6)    ;dcom1 command
        bne.s   CHerr           ;
        cmpi.b  #$17,$07(a6)    ;kybd control
        bne.s   CHerr           ;
        cmpi.b  #$0B,$05(a6)    ;kybd command
        bne.s   CHerr           ;
        cmpi.b  #$80,$67(a6)    ;VIA data direction A
        bne.s   CHerr           ;
        cmpi.b  #$37,$65(a6)    ;VIA data direction B
        beq.s   CHend           ;

CHerr   moveq   #-1,d0          ;short delay before error tone          0.8
CHerr1  dbra    d0,CHerr1       ;*
        bset    #0,CPsysst.w    ;set test 1 failed flag
        bsr     Flash           ;*

CHend

CHend2  move.b  #$FF,$75(a6)    ;turn off initial tone                  0.7

; SetMB -- Initialize MACSBUG RAM
;
SetMB   lea     RAMmxbug.w,a0   ;get pointer to start of MACSBUG RAM    0.8
        lea     RAMwksta-4.w,a1 ;get pointer to end of MACSBUG RAM      0.8
        bsr     ZeroRam         ;zero MACSBUG RAM                       0.7
        bsr     SBprom1         ;is debug PROM present?                 0.7
        bne.s   RomTst1         ;no, go on                              0.7
        jsr     (a1)            ;initialize MACSBUG                     0.8
        lea     SPbusEr,a1   ;{!UC}bus error processing address         0.8
        move.l  a1, BusErrV.W;{!UC}set vector                           0.8
        bsr     SBprom2         ;is this a boot to PROM?                0.7
        beq     MemClr1         ;yes, bypass PROM and RAM checks        0.7

        page    ;RomTst1, RamTst1, RamTst2, MemTest
;
; RomTst1 -- Check Corvus CONCEPT PROM  (test 2)
;
RomTst1 move.l  #CPcksum,a0     ;get pointer to start of PROM
        move.l  #ROMend,a1      ;get pointer to end of PROM
        bsr     RomTst          ;check PROM
        beq.s   RamTst1         ;PROM ok, go on
        bset    #1,CPsysst.w    ;set test 2 failed flag
        bsr     Flash           ;*

;
; RamTst1 -- Check Corvus CONCEPT static RAM  (test 3)
;
RamTst1 lea     RAMwksta.w,a0   ;get pointer to start of RAM            0.8
        bsr     WalkBit         ;is RAM valid?
        bne.s   RT1err          ;no, report error
        lea     CPstack.w,a1    ;get pointer to end of RAM              0.8
                                ;   (leave room for stack)
        bsr     March           ;is RAM valid?
        beq.s   RamTst2         ;yes, go on
                                ;
RT1err  bset    #2,CPsysst.w    ;set test 3 failed flag
        bsr     Flash           ;*

;
; RamTst2 -- Check Corvus CONCEPT dynamic RAM  (test 4)
;
RamTst2

        btst    #7, CPsysst.w   ;{!UC};if seg or page reg error         0.8
        bon.s   MemClr1         ;{!UC};then don't check or clear dram   0.8
        move.l  #USRbase,a0     ;get pointer to start of RAM
        bsr     WalkBit         ;is RAM valid?
        bne.s   RT2err          ;no, report error
        bsr     RamSize         ;get dynamic RAM size (a1 = RAM size)
        bsr     March           ;is RAM valid?
        beq.s   MemTest         ;yes, go on
                                ;
RT2err  bset    #3,CPsysst.w    ;set test 4 failed flag
        bsr     Flash           ;*

;
; MemTest -- Check Corvus CONCEPT dynamic RAM  (test 5)
;
MemTest bsr     IncTest         ;test user dynamic RAM
        beq.s   MemClr          ;no error, clear memory
        bset    #4,CPsysst.w    ;set test 5 failed flag
        bsr     Flash           ;*
   page    ;MemClr, SetIntV, SlotID
; MemClr -- Clear memory
;
MemClr  move.l  #USRbase,a0  ;{!UC};get pointer to start of RAM         0.8
        bsr     RamSize         ;get dynamic RAM size (a1 = RAM size)   0.7
        bsr     ZeroRam         ;zero RAM                               0.7
                                ;                                       0.7
MemClr1 lea     RAMwksta.w,a0   ;get pointer to start of RAM            0.8
        lea     CPstack-2.w,a1  ;get pointer to end of RAM              0.8
                                ;   (leave room for stack)              0.7
        bsr     ZeroRam         ;zero static RAM                        0.7

; SetIntV -- Set up interrupt vectors
;
SetIntV move.l  #CPivec1,a0     ;get pointer to interrupt vector table
        lea     IVlvl1.w,a1     ;get pointer to interrupt vectors       0.8
        moveq   #6,d0           ;get number of vectors to move
SUI1    move.l  (a0)+,(a1)+     ;move pointers to interrupt vectors
        dbra    d0,SUI1         ;*
                                ;
        lea     msg1,a0         ;mesg - "Corvus CONCEPT Initialization"
        bsr     DSputst         ;output message
        lea     msgcpy,a0       ;mesg - copyright notice
        bsr     DSputst         ;output message
        lea     msg2,a0         ;mesg - carriage returns
        bsr     DSputst         ;output message

; SlotID -- Examine slots for known devices
;
SlotID  lea     CPsl1typ.w,a5   ;get pointer to slot types table        0.8
        movea.l #IOPprom,a0     ;get pointer to slot 1 interface PROM   0.7
        moveq   #1,d6           ;get index for slot 1

SlotID1 movep.l 01(a0),d1       ;get interface prom code (ID)
        movep.l 09(a0),d2       ;get interface prom code (ID)
        tst.b   IOPbase+$9FFF.L ;disable interface RAM                  0.7
    page    ;SlotID (continued)

        move.b  #DTlocl,-1(a5,d6);set possible device type              0.7
        cmp.l   #$A920A900,d1   ;is this a local disk?
        bne.s   SlotID3         ;no, check next device
        cmp.l   #$A903A93C,d2   ;is this a local disk?
        bne.s   SlotID3         ;no, check next device
        bsr     LDsync          ;sync with local disk
        bge.s   SlotID9         ;check next slot if disk responded      0.7
        bset    #5,CPsysst.w    ;set test 6 failed flag                 0.7
        bsr     Flash           ;*                                      0.7
        bra.s   SlotID8         ;bypass slot if disk did not respond    0.7
                                ;                                       0.7
SlotID3 move.b  #DTa5,-1(a5,d6) ;set possible device type               0.7
        cmp.l   #$A220A000,d1   ;is this an Apple floppy?
        bne.s   SlotID4         ;no, check next device
        cmp.l   #$A203863C,d2   ;*
        beq.s   SlotID9         ;check next slot (dev type set)         0.7
                                ;
SlotID4 cmp.l   #'CORV',d1      ;is this a Corvus floppy?
        bne.s   SlotID8         ;no, check next slot                    0.7
                                ;                                       0.7
        move.b  #DTc8,-1(a5,d6) ;set possible device type               0.7
        cmp.l   #'US01',d2      ;is this a Corvus SSSD floppy?          0.7
        bne.s   SlotID5         ;no, check for another floppy type      0.7
        bsr     SlotAdr         ;compute FDC register base pointer      0.7
        move.b  #$20,1(a1)      ;turn off floppy motor                  0.7
        bra.s   SlotID9         ;check next slot (dev type set)         0.7
                                ;                                       0.7
SlotID5 move.b  #DTf8,-1(a5,d6) ;set possible device type               0.7
        cmp.l   #'US02',d2      ;is this a Corvus DSDD floppy?          0.7
        bne.s   SlotID8         ;no, set no device in slot              0.7
        bsr     SlotAdr         ;compute FDC register base pointer      0.7
        move.b  #$08,9(a1)      ;turn off floppy motor                  0.7
        btst    #7,9(a1)        ;is this an 8" floppy?                  0.7
        bon.s   SlotID9         ;yes, check next slot (dev type set)    0.7
        move.b  #DTf5,-1(a5,d6) ;set device type                        0.7
        bra.s   SlotID9         ;check next slot (dev type set)         0.7
                                ;                                       0.7
SlotID8 clr.b   -1(a5,d6)       ;set no device in slot                  0.7
                                ;
SlotID9 adda.w  #$200,a0        ;update interface PROM pointer
        addq    #1,d6           ;update slot number
        cmp.w   #4,d6           ;have we looked at all slots?
        ble     SlotID1         ;no, check next slot

    page    ;SlotID (continued)
        btst    #7, CPsysst.w   ;{!UC};if seg or page reg error then   0.8
        bon.s   RptStat         ;{!UC};can't do omninet - dram problem 0.8
        moveq   #InitOp,d0      ;get OMNINET Transporter number
        bsr     ODcomnd         ;*
        move.b  d7,CPtprnbr.w   ;save OMNINET Transporter number        0.7
        blt.s   SlotIDb         ;if error, go on                        0.7
                                ;

        bsr     ODpeek          ;check if transporter is a PTom         0.8
        cmpi.b  #$63, D7        ;version less than 64?                  0.8
        bls.s   RptStat         ;yes, is PTom - ignore it               0.8

        moveq   #EchoOp,d0      ;is OMNINET Transporter number in use?
        move.b  CPtprnbr.w,d1   ;get station's transporter host number  0.8
        bsr     ODcomnd         ;*
        cmpi.b  #Echoed,d7      ;*
        bne.s   SlotIDa         ;no, go on
        bset    #6,CPsysst.w    ;set test 7 failed flag
        bsr     Flash           ;*
        bra.s   SlotIDb         ;bypass disk server broadcast

SlotIDa bsr     ODbroad         ;send broadcast message to disk srvr    0.7
                                ;*  in order to get disk server         0.7
                                ;*  Transporter number                  0.7
SlotIDb move.b  d7,CPbtsrvr.w   ;save boot server number
        blt.s   RptStat         ;if error, go on
        move.b  #DTomni,4(a5)   ;set device type
    page    ;RptStat
;
; RptStat -- Report results of system initialization tests
;
RptStat moveq   #0,d1           ;initialize test number
        tst.b   CPsysst.w       ;any system errors?
        bne.s   RptSt1          ;yes, report them
        lea     msg32,a0        ;mesg - All system tests passed
        bsr     DSputst         ;output message
; ----  bra.s   RptSt8          ;output carriage returns
                                ;
RptSt1  btst    d1,CPsysst.w    ;did current test pass?
        boff.s  RptSt2          ;yes, go on
        lea     msg30,a0        ;mesg - System test
        bsr     DSputst         ;output message
        move.b  d1,d0           ;get test number
        addi.b  #$31,d0         ;*
        bsr     DSputch         ;output test number
        lea     msg31,a0        ;mesg - failed
        bsr     DSputst         ;output message
                                ;
RptSt2  addq.w  #1,d1           ;increment test number
        cmp.w   #7,d1           ;finished with all tests?
        bls.s   RptSt1          ;no, process next test                  0.8
                                ;
RptSt8  lea     msg2,a0         ;output carriage returns
        bsr     DSputst         ;*
        move.b  #$0F,$75(a6)    ;symmetrical wave shape
        move.b  #$A0,$71(a6)    ;output a low pitch tone
        moveq   #-1,d0          ;short delay                            0.8
RptSt9  dbra    d0,RptSt9       ;leaves d0 as $FFFFFFFF
        move.b  d0,$75(a6)      ;turn off tone                          0.8

        page    ;SelBoot
;
; SelBoot -- Select boot type
;
SelBoot move.b  IObootsw.L,d0   ;get boot selection switches
        andi.w  #$C0,d0         ;*
        beq     SBuser          ;00 - user select
        cmpi.b  #$40,d0         ;
        beq.s   SBlocal         ;01 - local disk boot
        cmpi.b  #$80,d0         ;
        beq.s   SBomni          ;02 - OMNINET disk boot
        bsr     SBprom1         ;is debug PROM present?                 0.7
        bne.s   SBflpy          ;03 - no, floppy disk boot              0.8
        move.b  #1,CPextcrt.w   ;03 - yes, debug (or other) PROM        0.7
                                ;
SBdebug bsr     SBprom1         ;is debug PROM present?                 0.7
        bne.s   SBuser          ;no, ask user for boot device
        lea     msg4,a0         ;mesg - MACSBUG I/O on DataComm 0       0.6
        bsr     DSputst         ;output message
        movea.l MXBentry.L,a0   ;*kb yes, go to debugger                0.5
        jmp     (a0)            ;*kb                                    0.5
                                ;
SBomni  lea     msg11,a0        ;mesg - "OMNINET disk boot"
        bsr     SBmsg           ;output message
        bsr     Oboot           ;load OS boot code
        bra.s   SBboot          ;transfer control to boot code
                                ;
SBlocal lea     msg12,a0        ;mesg - "Local disk boot"
        bsr     SBmsg           ;output message
        bsr     Lboot           ;load OS boot code
        bra.s   SBboot          ;transfer control to boot code
                                ;
SBflpy  lea     msg13,a0        ;mesg - "Floppy disk boot"
        bsr     SBmsg           ;output message                         1.0
        movea.l #CPsl1typ,a1    ;get pointer to slot 1 type
        moveq   #1,d0           ;get initial slot number

SBflpy1 move.b  -1(a1,d0),d1    ;get device type
; ----  cmpi.b  #DTa5,d1        ;is this an Apple floppy disk?          0.7
; ----  beq.s   SBflpy3         ;yes, use it for booting                0.7
        cmpi.b  #DTf8,d1        ;is this a Corvus DSDD 8" floppy disk?  0.7
        beq.s   SBflpy4         ;yes, use it for booting                0.7
        cmpi.b  #DTf5,d1        ;is this a Corvus DSDD 5" floppy disk?  0.7
        beq.s   SBflpy4         ;yes, use it for booting                0.7
        addq    #1,d0           ;update slot number
        cmp.w   #4,d0           ;have we looked at all slots?
        ble.s   SBflpy1         ;no, check next slot
        bra     GoToBt1         ;output error message
                                ;
                                ;
;Bflpy3 bsr     Aboot           ;load OS boot code                      0.7
; ----  bra.s   SBboot          ;transfer control to boot code          0.7

        page    ;SBuser, SBmsg, SBprom1, SBprom2
SBflpy4 bsr     FBboot          ;load OS boot code                      0.7
SBboot  bra     GoToBt          ;transfer control to boot code          0.7
                                ;
SBuser  bsr     SBprom2         ;boot to debug PROM?                    1.0
        beq     SBdebug         ;yes                                    0.7
        lea     msg10,a0        ;mesg - "Select boot device"
        bsr     DSputst         ;output message
        bsr     KBgetch         ;get reply
        bsr     DScvtUC         ;convert character to upper case
        move.b  d0,-(sp)        ;save reply
        bsr     DSputch         ;echo reply
        sub.b   #'0',d0         ;is reply numeric (0..6)?               1.0
        ext.w   d0              ;*                                      1.0
        blt.s   SBuser1         ;no, output carriage return             1.0
        cmpi.b  #6,d0           ;*                                      1.0
        bgt.s   SBuser1         ;no, output carriage return             1.0
        mulu    #10,d0          ;*                                      1.0
        move.w  d0,d1           ;*                                      1.0
        bsr     KBgetch         ;get reply                              1.0
        bsr     DSputch         ;echo reply                             1.0
        sub.b   #'0',d0         ;is reply numeric (0..9)?               1.0
        ext.w   d0              ;*                                      1.0
        blt.s   SBuser1         ;no, output carriage return             1.0
        cmpi.b  #9,d0           ;*                                      1.0
        bgt.s   SBuser1         ;no, output carriage return             1.0
        add.w   d1,d0           ;compute boot server number             1.0
        move.b  d0,CPbtsrvr.w   ;save boot server number                1.0
        movea.l #CPsl1typ,a5    ;get pointer to slot types table        1.0
        move.b  #DTomni,4(a5)   ;set device type                        1.0
        move.b  (sp)+,d0        ;force Omninet boot                     1.0
        move.b  #'O',-(sp)      ;*                                      1.0
                                ;                                       1.0
SBuser1 moveq   #DSCcr,d0       ;output carriage return                 1.0
        bsr     DSputch         ;*
        move.b  (sp)+,d0        ;restore reply
        cmpi.b  #'F',d0         ;Corvus floppy boot?
        beq     SBflpy          ;yes, do it                             1.0
        cmpi.b  #'D',d0         ;debug?
        beq     SBdebug         ;yes, do it
        cmpi.b  #'L',d0         ;local disk boot?
        beq     SBlocal         ;yes, do it
        cmpi.b  #'O',d0         ;OMNINET disk boot?
        beq     SBomni          ;yes, do it
        bra     Setup           ;no, start over again

;
; SBmsg -- Output message
;
SBmsg   bsr     DSputst         ;output message
        lea     msg19,a0        ;mesg - "disk boot"
        bsr     DSputst         ;output message
        rts                     ;return

;
; SBprom1 -- Is debug PROM present? (EQ - yes, NE - no)
;
SBprom1 lea     MXBinit.L,a0    ;is debug PROM present?                 0.7
        lea     MXBbase+$C.L,a1 ;*                                      0.7
        cmpa.l  (a0),a1         ;*                                      0.7
        rts                     ;return                                 0.7

;
; SBprom2 -- Boot to debug PROM? (EQ - yes, NE - no)
;
SBprom2 movem.l a0-a1/d0,-(sp)  ;save registers                         0.7
        bsr.s   SBprom1         ;is debug PROM present?                 0.7
        bne.s   SBprom9         ;no, return                             0.7
        move.b  IObootsw.L,d0   ;get boot selection switches            0.7
        andi.w  #$C0,d0         ;*                                      0.7
        cmpi.w  #$C0,d0         ;is this a boot to PROM?                0.7
SBprom9 movem.l (sp)+,a0-a1/d0  ;restore registers                      0.7
        rts                     ;return                                 0.7

        page    ;GoToBt
;
; GoToBt -- Transfer control to boot code
;
;       Enter:  A0.L    = Boot code entry point pointer
;
;       Values passed in registers to the boot are:
;
;       +---------------+---------------+---------------+---------------+
;    D0 | low user RAM address                                          |
;       +---------------+---------------+---------------+---------------+
;    D1 | high user RAM address                                         |
;       +---------------+---------------+---------------+---------------+
;    D2 | low user RAM address  (same as D0)                            |
;       +---------------+---------------+---------------+---------------+
;    D3 | high user RAM address (same as D1)                            |
;       +---------------+---------------+---------------+---------------+
;    D4 |             0 |             0 | boot slot     | boot server   |
;       +---------------+---------------+---------------+---------------+
;    D5 |                                                             0 |
;       +---------------+---------------+---------------+---------------+
;    D6 |                                                             0 |
;       +---------------+---------------+---------------+---------------+
;    D7 |                                                             0 |
;       +---------------+---------------+---------------+---------------+
;
GoToBt  bge.s   GoToBt2         ;go on if no boot load error
                                ;
GoToBt1 lea     msg3,a0         ;mesg - "Boot error"
        bsr     DSputst         ;output message
        bra     SBuser          ;select boot device again
                                ;
GoToBt2 bsr     RamSize         ;get dynamic RAM size (a1 = RAM size)   0.6
        cmpa.l  #MEMprom,a1     ;are we in PROM?                        0.7
        bne.s   GoToBt3         ;yes, go on                             0.6
        lea     CPbtslot.w,a1   ;set RAM size to protect code           0.6
                                ;                                       0.6
GoToBt3 move.l  #USRbase,d0     ;D0 - low user RAM address              0.6
        move.l  a1,d1           ;D1 - high user RAM address
        move.l  d0,d2           ;D2 - low user RAM address
        move.l  d1,d3           ;D3 - high user RAM address
        clr.l   d4              ;D4 - 0
        move.b  CPbtslot.w,d4   ;D4 - boot slot
        lsl.w   #8,d4           ;
        move.b  CPbtsrvr.w,d4   ;D4 - boot slot/boot server
        clr.l   d5              ;D5 - 0
        clr.l   d6              ;D6 - 0
        clr.l   d7              ;D7 - 0
        jmp     (a0)            ;enter boot code

        page    ;RomTst
;
; RomTst -- Compute checksum for PROM
;           (PROM checksum is included in address range)
;
;       Enter:  A0.L    = PROM start pointer
;               A1.L    = PROM end pointer
;
;       Exit:   EQ      = PROM checksum valid
;               NE      = PROM checksum error
;
RomTst  move.l  a0,a2           ;get starting address
        clr.w   d0              ;
RT1     move.w  (a2)+,d1        ;
        eor.w   d1,d0           ;
        cmpa.l  a1,a2           ;
        blt.s   RT1             ;
        cmp.w   #$FFFF,d0       ;
        rts                     ;return

;
; WalkBit -- Walking ones and zeros
;
;       Enter:  A0.L    = RAM start pointer
;
WalkBit move.l  a0,a2           ;get starting address
        move.l  a0,a1           ;get ending address
        adda.w  #$10,a1         ;*                         0.8

WB1     moveq   #-2,d0          ;puts $FFFE in low word    0.8
WB2     move.w  d0,(a2)         ;
        cmp.w   (a2),d0         ;
        bne.s   WBerr           ;
        rol     #1,d0           ;
        bcs.s   WB2             ;
                                ;
        moveq   #1,d0           ;                          0.8
WB3     move.w  d0,(a2)         ;
        cmp.w   (a2),d0         ;
        bne.s   WBerr           ;
        asl     #1,d0           ;
        bcc.s   WB3             ;
                                ;
        addq.l  #2,a2           ;                          0.8
        cmpa.l  a1,a2           ;
        blt.s   WB1             ;
                                ;
WBerr   rts                     ;return

        page    ;March
;
; March --
;
;       Enter:  A0.L    = RAM start pointer
;               A1.L    = RAM end pointer
;
March   move.l  a0,a2           ;
        clr.l   d0              ;
                                ;
MR1     move.w  d0,(a2)+        ;
        cmpa.l  a1,a2           ;
        bne.s   MR1             ;
                                ;
        move.w  d0,d2           ;
        not.w   d2              ;
MR2     move.w  -(a2),d1        ;
        cmp.w   d0,d1           ;
        bne.s   MRerr           ;
        move.w  d2,(a2)         ;
        cmpa.l  a0,a2           ;
        bne.s   MR2             ;
                                ;
        move.w  d2,d0           ;
        not.w   d2              ;
MR3     move.w  (a2),d1         ;
        cmp.w   d0,d1           ;
        bne.s   MRerr           ;
        move.w  d2,(a2)+        ;
        cmpa.l  a1,a2           ;
        bne.s   MR3             ;
                                ;
MRerr   rts                     ;return

        page    ;IncTest, RamSize, ZeroRam
;
; IncTest --
;
;       Enter:  A0.L    = RAM start pointer
;               A1.L    = RAM end pointer
;
IncTest move.l  a0,a2           ;
        move.w  #$101,d1        ;
                                ;
IT01    move.w  d1,(a2)+        ;
        rol.w   #1,d1           ;
        cmpa.l  a1,a2           ;
        blt.s   IT01            ;
                                ;
        move.l  a0,a2           ;
        move.w  #$101,d1        ;
                                ;
IT02    cmp.w   (a2)+,d1        ;
        bne.s   IT99            ;
        rol.w   #1,d1           ;
        cmpa.l  a1,a2           ;
        blt.s   IT02            ;
                                ;
IT99    rts                     ;return

;
; RamSize -- Get end of user RAM pointer
;
;       Exit:   A1.L    = RAM end pointer
;
RamSize move.l  #MEMprom,a1     ;are we in PROM?                        0.7
        cmpi.l  #ROMend,(sp)    ;*
        bgt.s   RamSiz9         ;no, return

        lea     MEM512k.L, a1   ;{!UC} only has 512 kbytes              0.8

RamSiz9 rts                     ;return

;
; ZeroRam -- Move 0 to RAM subroutine
;
;       Enter:  A0.L    = RAM start pointer
;               A1.L    = RAM end pointer
;
ZeroRam clr.l   (A0)+           ;
        cmpa.l  a1,a0           ;
        ble.s   ZeroRam         ;
        rts                     ;return

        page    ;Flash
;
; Flash -- Flash display screen subroutine
;
Flash   movem.l a0-a1/a6/d0-d1,-(sp);save registers                     0.8
        moveq   #3, d1          ;{!UC};make sound 4 times               0.8

FL0     move.l  #VIAbase,a6     ;get pointer to VIA I/O locations
        move.b  #$0F,$75(a6)    ;symmetrical wave shape
        move.b  #$40,$71(a6)    ;output a high pitch error tone
                                ;
FL4     moveq   #-1,d0          ;short delay                            0.8
FL5     dbra    d0,FL5          ;*
        move.b  #$FF,$75(a6)    ;turn off tone
        moveq   #-1,d0          ;short delay                            0.8
FL6     dbra    d0,FL6          ;*

        dbra    d1, FL0         ;{!UC} make sound again if no display   0.8
        movem.l  (sp)+,a0-a1/a6/d0-d1;restore registers                 0.8
        rts                     ;return

        page    ;INT...
;
; INTlvl7  -- process level 7 interrupt (ignore interrupt)
;
INTlvl7 rte                     ;return from interrupt

;
; INTkybd -- process KEYBOARD interrupt (ignore interrupt)
;
INTkybd ori.b   #$02,IOkybd.l   ;lvl 6 (KYBD) - turn off recv int       0.7
        andi.b  #$F3,IOkybd.l   ;lvl 6 (KYBD) - turn off xmit int       0.7
        rte                     ;return from interrupt

;
; INTtimr -- process TIMER interrupt (ignore interrupt)
;
INTtimr move.b  #$7F,IOtimr.l   ;lvl 5 (TIMER) - turn off VIA int       0.7
        rte                     ;return from interrupt

;
; INTdc0  -- process DATACOMM0 interrupt (ignore interrupt)
;
INTdc0  ori.b   #$02,IOdc0.l    ;lvl 4 (DC0) - turn off recv int        0.7
        andi.b  #$F3,IOdc0.l    ;lvl 4 (DC0) - turn off xmit int        0.7
        rte                     ;return from interrupt

;
; INTomni -- process OMNINET interrupt (ignore interrupt)
;
INTomni tst.b   IOomni.l        ;lvl 3 (OMNINET) - reset interrupt      0.7
        rte                     ;return from interrupt

; INTdc1  -- process DATACOMM1 interrupt (ignore interrupt)
;
INTdc1  ori.b   #$02,IOdc1.l    ;lvl 2 (DC1) - turn off recv int        0.7
        andi.b  #$F3,IOdc1.l    ;lvl 2 (DC1) - turn off xmit int        0.7
        rte                     ;return from interrupt

; INTslot -- process SLOT interrupt (ignore interrupt)
;
INTslot movem.l D0/A0,-(SP)     ;save registers
        lea     IOslot.l,a0     ;get pointer to port A ORA              0.7
        move.b  (A0),D0         ;read port A w/o handshake
        bchg    #7,D0           ;toggle IOX
        move.b  D0,(A0)         ;write new IOX
        movem.l (SP)+,D0/A0     ;restore registers
        rte                     ;return from interrupt
     page    ;SlotAdr, msg...
;
; SlotAdr -- compute slot address given slot number
;
;       Enter:  D6.B - Slot number
;
;       Exit:   A1.L - I/O port address
;
SlotAdr move.l  d6,-(sp)        ;save register
        ext.w   d6              ;compute disk port address for slot
        lsl.w   #5,d6           ;*
        move.l  #IOPbase,a1     ;*                                      0.7
        adda.w  d6,a1           ;*
        move.l  (sp)+,d6        ;restore register
        rts                     ;return
    list 0
msg1    data.b  DSCcr,DSCcr
        data.b  'Corvus Uniplex Initialization  ('              ;{!UP}  0.8
        data.b  PROMvers+$30,'.',PROMlevl+$30,')'
msg2    data.b  DSCcr,DSCcr,0
msg3    data.b  'Boot error ....',DSCcr,DSCcr,0
msg4    data.b  DSCcr,DSCcr,'MACSBUG I/O on DataComm 0',DSCcr,0
msg10   data.b  'Select boot device (D,F,L,O): ',0
msg11   data.b  'OMNINET',0
msg12   data.b  'Local',0
msg13   data.b  'Floppy',0
msg19   data.b  ' disk boot',DSCcr,0
msg30   data.b  'System test ',0
msg31   data.b  ' failed',DSCcr,0
msg32   data.b  'All system tests passed',DSCcr,0
        data.b  0

        page    ;p.prom.kb              ;
; File: CC.PROM.KB
; Date: 15-Nov-83
; By:   Keith Ball
;
; KEYBOARD DRIVER FOR PROM (kb)

;
; EQUATES FOR ALL KEYBOARD SOFTWARE
;
; KEYBOARD DATA AREA DEFINITIONS
;
KBBflgs EQU     0               ;FLAG JUST HI ORDER BYTE
KBBfrnt EQU     KBBflgs+2       ;FRONT PTR SAVE
KBBrear EQU     KBBfrnt+4       ;REAR PTR SAVE
KBBsrsv EQU     KBBrear+4       ;STATUS REG SAVE AREA
KBBbufr EQU     KBBsrsv+2       ;KEYBOARD BUFFER
KBBlen  EQU     RAMkblen-KBBbufr;NMBR OF BYTES IN BUFFER
;
; FLAG BIT DEFINITIONS
;
KBFfull EQU     0               ;BUFFER FULL FLAG
KBFemty EQU     1               ;BUFFER EMPTY FLAG
KBFclos EQU     2               ;KEY CLOSURE FLAG
KBFshft EQU     3               ;SHIFT KEY
KBFcntl EQU     4               ;CONTROL KEY
KBFlock EQU     5               ;SHIFT LOCK KEY
;
; MISCELLANEOUS EQUATES
;
KBmsk40 EQU     $1F             ;MASK TO CLEAR D7-D5 (CONTROL CODE)
;
; TABLE VALUES FOR PROCESSING CHARACTERS
;
KBCqual EQU     $7F             ;QUALIFIER VALUES > THEN THIS
KBCshft EQU     $FE             ;TABLE VALUE FOR SHIFT
KBCcntl EQU     $FD             ;TABLE VALUE FOR CONTROL
KBClock EQU     $FC             ;TABLE VALUE FOR SHIFT LOCK
KBCnoch EQU     $FF             ;TABLE VALUE FOR NO CHAR CDE
;
; SPECIAL ASCII CHARACTERS
;
KBClca  EQU     'a'             ;LOWER CASE A
KBClcz  EQU     'z'             ;LOWER CASE Z
KBCqmrk EQU     '?'             ;QUESTION MARK

        page
;
; COMMAND AND CONTROL REGISTER VALUES
;
KBccOff EQU     $02             ;TURN OFF UART (CMD)
KBcc600 EQU     $17             ;600 BAUD AND 8 BIT XMIT (CTL)
KBccBrk EQU     $08             ;XMIT A BREAK (CMD)
KBccGo  EQU     $09             ;TURN ON INTS & UART (CMD)
;
KBdsInt EQU     $0700           ;DISABLE 68000 INTERRUPTS

;
; KBinit - Initialize (reset) keyboard
;
;      REGISTER A2 IS USED AS POINTER TO COMMAND REGISTER
;      REGISTER A3 IS ADDRESS OF KBRD DATA AREA
;
KBinit  MOVEM.L D0/A0-A3,-(SP)          ;save registers
        LEA     RAMkbbuf.W,A3
        LEA     KBRcmnd.L,A2
        MOVE.B  #KBccOff,(A2)           ;TURN OFF KBRD
        LEA     KBBflgs(A3),A0          ;CLEAR INT HANDLER FLAGS
        CLR.L   (A0)                    ;INCLUDES QUALIFIERS
        BSET    #KBFemty,(A0)           ;BUFFER IS EMPTY
        ;
        ; INITIALIZE FRONT & REAR POINTERS
        ;
        LEA     KBBbufr(A3),A0
        LEA     KBBfrnt(A3),A1
        MOVE.L  A0,(A1)+
        MOVE.L  A0,(A1)
        LEA     KBintr,A0               ;SETUP AUTOVECTOR 6
        MOVE.L  A0,IVlvl6.W             ;WITH ADDR OF INT HANDLER
        ;
        ; TURN ON KEYBOARD UART
        ;
        MOVE.B  KBRstat-KBRcmnd(A2),D0  ;RESET UART                  0.8
        MOVE.B  KBRdata-KBRcmnd(A2),D0  ;CLEAR RECEIVE               0.8
        MOVE.B  #KBcc600,KBRcntl-KBRcmnd(A2) ;8 BITS, 600 BAUD XMIT  0.8
        MOVE.B  #KBccBrk,(A2)           ;FORCE BREAK OF KBRD
        MOVE.W  #33333,D0               ;DELAY FOR UART TO DO BREAK
KBinit1 DBF     D0,KBinit1              ;NEED MINIMUM OF 33.3 MILLISECS
        MOVE.B  #KBccGo,(A2)            ;TURN ON UART & INTERRUPTS
        move.w  #$2500,sr               ;set priority to 6, KYBD intr only
        MOVEM.L (SP)+,D0/A0-A3          ;restore registers
        RTS

        page
;
; KBintr - Keyboard interrupt service routine
;
; BEGIN INTERRUPT SERVICE ROUTINE.  THIS IS THE ENTRY POINT.  IT'S ADDRESS
; MUST BE PLACED IN AUTO VECTOR INTERRUPT 6 VECTOR BEFORE KEYBOARD INTERRUPT
; IS TURNED ON.
;
; REGISTER USEAGE:  D0 - KEYCODE
;                   D1 - CHARACTER
;                   A0 - ADDRESS OF FLAG BYTE
;                   A2 - BASE ADDRESS OF KBRD DATA AREA
;
KBintr  MOVEM.L D0-A6,-(SP)             ;SAVE REGISTERS ON STACK
        LEA     RAMkbbuf.W,A2           ;BASE ADDR OF KBRD DATA AREA
        BSR     KBgetky                 ;GET KEYCODE FROM UART DATA PORT
        ;
        ; IF BIT 7 OF KEYCODE SET THEN CLOSURE ELSE RELEASE
        ;
        LEA     KBBflgs(A2),A0
        BCLR    #KBFclos,(A0)           ;ASSUME RELEASE
        BTST    #7,D0                   ;KEYCODE BIT D7 CLEAR?
        BEQ.S   KBintr1                 ;YES
        BSET    #KBFclos,(A0)
        BCLR    #7,D0
        ;
        ; GET CHARACTER CODE FOR THIS KEYCODE
        ;
KBintr1 LEA     KBstable,A1             ;ASSUME SHIFT TABLE
        BTST    #KBFshft,(A0)
        BNE.S   KBintr2                 ;SHIFT BIT SET
        LEA     KBrtable,A1             ;ELSE USE REGULAR TABLE
KBintr2 MOVE.B  0(A1,D0.W),D1           ;INDEX TABLE BY KEYCODE
        ;
        ; IF CHAR(D1) = $FF THEN IGNORE AND EXIT
        ;
        CMPI.B  #KBCnoch,D1
        BEQ.S   KBintr9
        BSR.S   KBproky                 ;ELSE PROCESS KEYCODE
        ;
        ; EXIT INTERRUPT SERVICE ROUTINE
        ;
KBintr9 MOVEM.L (SP)+,D0-A6             ;RESTORE REGISTERS
        RTE                             ;EXIT INTERRUPT

        page
;
; KBgetch - Get a keyboard character
;
; Register useage:  A0 = Front pointer
;                   A1 = address of end of buffer + 1
;                   A2 = updated front pointer
;                   A3 = address of front pointer
;                   A4 = address of flag byte
;                   A5 = address of keyboard data area
;                   A6 = address of Status Register save area
;
;       Exit:   D0.B - Next character in buffer
;
KBgetch MOVEM.L A0-A6,-(SP)             ;save all address registers
        LEA     RAMkbbuf.W,A5           ;keyboard data area
        LEA     KBBflgs(A5),A4          ;address of Flag byte
        tst.b   CPextcrt.w              ;using external CRT?            0.8
        bon.s   KBgchra                 ;yes, use data comm 0           0.8
        bsr     SBprom2                 ;debug boot PROM?               0.7
        bne.s   KBgchr1                 ;no, go on                      0.7
KBgchra move.l  #KBcPort,a1             ;get UART pointer               0.8
KBgchr0 btst    #KBrdBit,KBuSt(a1)      ;is char in UART?               0.7
        boff.s  KBgchr0                 ;no, wait some more             0.7
        move.b  KBuDa(a1),d0            ;get character                  0.7
        bra.s   KBgchr9                 ;return                         0.7
        ;
        ; Wait for a character in the Buffer.
        ;
KBgchr1 BTST    #KBFemty,(A4)           ;while (Buffer_empty) do;
        BNE.S   KBgchr1                 ;*
        ;
        ; have char, check for wrap around before get char
        ;
        LEA     KBBfrnt(A5),A3          ;pointer to Front save loc
        MOVE.L  (A3),A0                 ;Front pointer
        LEA     KBBbufr+KBBlen(A5),A1   ;end of buffer + 1
        MOVE.L  A0,A2                   ;
        ADDQ.L  #1,A2                   ;add one to pointer to get next addr
        CMPA.L  A1,A2                   ;Front=end of buffer + 1        0.7
        BNE.S   KBgchr2                 ;No
        LEA     KBBbufr(A5),A2          ;yes, then wrap back to beginning
                                        ;
KBgchr2
;-----  LEA     KBBsrsv(A5),A6          ;don't need                     0.8
        MOVE.W  SR,-(SP)                ;put SR on stack                0.8
        ORI.W   #KBdsInt,SR             ;*** disable interrupts
        MOVE.B  (A0),D0                 ;get char
        MOVE.L  A2,(A3)                 ;save new Front value
        CMPA.L  KBBrear(A5),A2          ;if Front=Rear then
        BNE.S   KBgchr3                 ;Buffer_empty := true;
        BSET    #KBFemty,(A4)           ;
                                        ;
KBgchr3 MOVE.W  (SP)+,SR                ;*** enable interrupts          0.8
KBgchr9 MOVEM.L (SP)+,A0-A6             ;restore callers address regs   0.7
        RTS                             ;return

        page
;
; KBgetky - GET KEYCODE (IGNORES ERRORS)
;
;     EXIT : (D0) - UART DATA PORT BYTE
;
KBgetky CLR.L   D0                      ;MAKE SURE HI 3 BYTES ARE 0
        ;
        ; READ STATUS REGISTER TO CLEAR IRQ BIT
        ; ALWAYS READ DATA PORT SO IF OVERRUN THEN FOR NEXT CHAR
        ; IT WILL BE CLEARED.
        ;
        MOVE.B  KBRstat.L,D1            ;GET STATUS OF RECEIVE
        MOVE.B  KBRdata.L,D0            ;READ UART DATA PORT
        RTS

        page
;
; KBproky - PROCESS CHARACTER OR QUALIFIER
;
;       Enter:  D1 = CHARACTER CODE FROM TABLE
;               D0 = KEYCODE
;               A0 = ADDRESS OF FLAGS
;
KBproky CMPI.B  #KBCqual,D1             ;IS IT A QUALIFIER
        BHI.S   KBpro3                  ;YES
        ;
        ; IGNORE REST OF KEYS IF NOT CLOSURE
        ;
        BTST    #KBFclos,(A0)
        BEQ.S   KBpro9
        ;
        ; TEST FOR CONTROL
        ;
        BTST    #KBFcntl,(A0)
        BEQ.S   KBpro1                  ;NO,TRY SHIFT LOCK
        CMPI.B  #KBCqmrk,D1
        BLS.S   KBpro1
        ANDI.B  #KBmsk40,D1             ;CLEAR BITS D7,D6,D5 OF CHAR
        BRA.S   KBpro2                  ;PUT CHAR
        ;
        ; TEST FOR SHIFT LOCK
        ;
KBpro1  BTST    #KBFlock,(A0)
        BEQ.S   KBpro2                  ;KEY NOT DOWN
        CMPI.B  #KBClca,D1
        BCS.S   KBpro2                  ;NOT WITHIN RANGE
        CMPI.B  #KBClcz,D1
        BHI.S   KBpro2                  ;NOT WITHIN RANGE
        LEA     KBstable,A1
        MOVE.B  0(A1,D0.W),D1           ;INDEX TABLE BY KEYCODE
        ;
        ; IF BUFFER NOT FULL PUT CHARACTER
        ;
KBpro2  BTST    #KBFfull,(A0)
        BNE.S   KBpro9
        BSR.S   KBputch
        BRA.S   KBpro9
        ;
        ; PROCESS A QUALIFIER KEY
        ;
KBpro3  BSR.S   KBqual
KBpro9  RTS

        page
;
; KBputch - PUT ONE CHARACTER IN BUFFER
;
;       Enter:  D1 = BYTE TO PUT IN BUFFER
;               A0 = ADDRESS OF FLAGS
;               A2 = ADDRESS OF KEYBOARD DATA AREA
;
        ;
        ; PUT CHARACTER IN CIRCULAR QUEUE AT REAR
        ;
KBputch LEA     KBBrear(A2),A5          ;get pointer to rear pointer
        MOVE.L  (A5),A3                 ;*
        MOVE.B  D1,(A3)+                ;UPDATE POINTER ALSO
        ;
        ; IF REAR > ENDBUFFER THEN REAR := @BUFFER
        ;
        LEA     KBBbufr+KBBlen(A2),A4   ; A4 = end buffer + 1
        CMPA.L  A4,A3                   ;
        BNE.S   KBput1                  ;NOT BEYOND BUFFER
        LEA     KBBbufr(A2),A3          ;
        ;
        ; IF FRONT = REAR THEN BUFFER FULL
        ;
KBput1  CMPA.L  KBBfrnt(A2),A3          ;
        BNE.S   KBput2                  ;
        BSET    #KBFfull,(A0)           ;
KBput2  MOVE.L  A3,(A5)                 ;UPDATE REAR IN MEMORY
        BCLR    #KBFemty,(A0)           ;SHOW BUFFER NOT EMPTY
        RTS                             ;return

        page
;
; KBqual - PROCESS QUALIFIER KEYS
;
;       Enter:  D1 = CHARACTER CODE FROM TABLE
;               A0 = ADDRESS OF FLAGS
;
KBqual  CMPI.B  #KBCshft,D1             ;IS IT SHIFT?
        BNE.S   KBqual1                 ;NO
        MOVEQ   #KBFshft,D2             ;BIT POSITION OF SHIFT
        BRA.S   KBqual3                 ;CHANGE FLAG
                                        ;
KBqual1 CMPI.B  #KBCcntl,D1             ;IS IT CONTROL?
        BNE.S   KBqual2                 ;NO
        MOVEQ   #KBFcntl,D2             ;BIT POSITION OF CONTROL
        BRA.S   KBqual3                 ;CHANGE FLAG
                                        ;
KBqual2 CMPI.B  #KBClock,D1             ;IS IT SHIFT LOCK?
        BNE.S   KBqual9                 ;NO,THEN IT'S GARBAGE
        MOVEQ   #KBFlock,D2
        ;
        ; IF CLOSURE THEN SET FLAG ELSE CLEAR FLAG
        ;
KBqual3 BTST    #KBFclos,(A0)
        BEQ.S   KBqual8
        BSET    D2,(A0)
        BRA.S   KBqual9
KBqual8 BCLR    D2,(A0)
KBqual9 RTS

        page
;
; THE SHIFT TABLE
;        TABLE IS INDEXED BY KEYCODE.  EACH BYTE REPRESENTS THE ENTRY FOR
;        THE CORRESPONDING KEYCODE.
;
;           0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
KBstable
  ;        ..   3   9  ..   6   ,   -  cr  ..   1   7  ..   4   8   5   2
  DATA.B  $FF,$33,$39,$FF,$36,$2C,$2D,$0D,$FF,$31,$37,$FF,$34,$38,$35,$32
; ;         +  ..   {   |  cr   }  bs  ..   )   ?   P   _   :   ~   "  ..
; DATA.B  $2B,$FF,$7B,$7C,$0D,$7D,$08,$FF,$29,$3F,$50,$5F,$3A,$7E,$22,$FE
  ;         +  ..   {  bs  cr   }   |  ..   )   ?   P   _   :   ~   "  .. ;0.6
  DATA.B  $2B,$FF,$7B,$08,$0D,$7D,$7C,$FF,$29,$3F,$50,$5F,$3A,$7E,$22,$FE ;0.6
  ;        ..  ..  ..  ..  ..  ..  ..  ..   $   %   R   T   F   G   V   B
  DATA.B  $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$24,$25,$52,$54,$46,$47,$56,$42
  ;         @   #   W   E   S   D   X   C esc   !  ..   Q  ..   A  ..   Z
  DATA.B  $40,$23,$57,$45,$53,$44,$58,$43,$1B,$21,$FF,$51,$FC,$41,$FE,$5A
  ;         ^   &   Y   U   H   J   N   M  ..  ..  ..  sp  ..   0  ..   .
  DATA.B  $5E,$26,$59,$55,$48,$4A,$4E,$4D,$FD,$FF,$FF,$20,$FF,$30,$FF,$2E
  ;         *   (   I   O   K   L   <   >  ..  ..  ..  ..  ..  ..  ..  ..
  DATA.B  $2A,$28,$49,$4F,$4B,$4C,$3C,$3E,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF

        page
;
; THE REGULAR TABLE - UNSHIFTED OR LOWER CASE
;        TABLE IS INDEXED BY KEYCODE.  EACH BYTE REPRESENTS THE ENTRY FOR
;        THE CORRESPONDING KEYCODE.
;
;           0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
KBrtable
  ;        ..   3   9  ..   6   ,   -  cr  ..   1   7  ..   4   8   5   2
  DATA.B  $FF,$33,$39,$FF,$36,$2C,$2D,$0D,$FF,$31,$37,$FF,$34,$38,$35,$32
; ;         =  ..   [   \  cr   ]  bs  ..   0   /   p   -   ;   `   ;  ..
; DATA.B  $3D,$FF,$5B,$5C,$0D,$5D,$08,$FF,$30,$2F,$70,$2D,$3B,$60,$27,$FE
  ;         =  ..   [  bs  cr   ]   \  ..   0   /   p   -   ;   `   ;  .. ;0.6
  DATA.B  $3D,$FF,$5B,$08,$0D,$5D,$5C,$FF,$30,$2F,$70,$2D,$3B,$60,$27,$FE ;0.6
  ;        ..  ..  ..  ..  ..  ..  ..  ..   4   5   r   t   f   g   v   b
  DATA.B  $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$34,$35,$72,$74,$66,$67,$76,$62
  ;         2   3   w   e   s   d   x   c esc   1  ..   q  ..   a  ..   z
  DATA.B  $32,$33,$77,$65,$73,$64,$78,$63,$1B,$31,$FF,$71,$FC,$61,$FE,$7A
  ;         6   7   y   u   h   j   n   m  ..  ..  ..  sp  ..   0  ..   .
  DATA.B  $36,$37,$79,$75,$68,$6A,$6E,$6D,$FD,$FF,$FF,$20,$FF,$30,$FF,$2E
  ;         8   9   i   o   k   l   ,   .  ..  ..  ..  ..  ..  ..  ..  ..
  DATA.B  $38,$39,$69,$6F,$6B,$6C,$2C,$2E,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
        page    ;p.prom.ds              ;
; File: CC.PROM.DS
; Date: 15-Nov-83
;
; DISPLAY DRIVER FOR PROM (mb) 05/18/82
;
; BOTH horizontal and vertical display driver
; contains default window records, copies them into memory
; contains default character sets
; no CRTST code: no window functions

DSClf    equ    $0A    ;line feed character
DSCcr    equ    $0D    ;carriage return character
DSCesc   equ    $1B    ;escape character
DSCblnk  equ    $20    ;blank character
DSClca   equ    $61    ;lower case "a"
DSClcz   equ    $7A    ;lower case "z"
DSCdiff  equ    $20
;
; Character Set Record Equates
;
;Stblloc equ    0      ;character set data pointer (not used)
CSlpch   equ    4      ;scanlines per character
CSbpch   equ    6      ;bits per character
CSfrstch equ    8      ;first character code - ascii
CSlastch equ    10     ;last character code - ascii
CSmask   equ    12     ;mask used in positioning cells
CSattr1  equ    16     ;attributes
                       ;  bit 0 = 1 - vertical orientation
CSattr2  equ    17     ;currently unused
CSdata   equ    18     ;offset of char data from char record
;
; Window Record Equates
;
WRcharpt equ    0       ;character set pointer
WRhomept equ    4       ;home (upper left) pointer
WRcuradr equ    8       ;current location pointer
WRhomeof equ    12      ;bit offset of home location
WRbasex  equ    14      ;home x value, relative to root window
WRbasey  equ    16      ;home y value, relative to root window
WRlngthx equ    18      ;maximum x value, relative to window (bits)
WRlngthy equ    20      ;maximum y value, relative to window (bits)
WRcursx  equ    22      ;current x value (bits)
WRcursy  equ    24      ;current y value (bits)
WRbitofs equ    26      ;bit offset of current address
WRgrorgx equ    28      ;graphics - origin x (bits relative to home loc)
WRgrorgy equ    30      ;graphics - origin y (bits relative to home loc)
WRattr1  equ    32      ;attributes
WRattr2  equ    33      ;attributes
vert     equ    0       ;  1 = vertical, 0 = horizontal
WRstate  equ    34      ;used for decoding escape sequences
WRrcdlen equ    35      ;window description record length
                        ;
WRlength equ    36      ;actual window record length
        page
;
; DSinit - Initialize display driver
;
DSinit  MOVEM.L D4/A0-A2,-(SP)          ;save registers
        MOVE.W  #DSdefOf,CPscnofs.W     ;set bytes per scan line
        LEA     DSwndH,A0               ;assume horizontal orientation
        LEA     DScsetH,A2              ;*
        BTST    #3,IObootsw.L           ;is display horizontal?
        BOFF.S  DSinit1                 ;yes, go on
        LEA     DSwndV,A0               ;set vertical orientation
        LEA     DScsetV,A2              ;*
DSinit1 LEA     CPwndrcd.W,A1           ;get pointer to RAM window record 0.8
        MOVEQ   #WRlength-1,D4          ;get window record length       0.6
DSinit2 MOVE.B  (A0)+,(A1)+             ;copy window record to RAM      0.6
        DBRA    D4,DSinit2              ;*
        LEA     CPwndrcd.W,A0           ;get RAM window record pointer    0.8
        MOVE.L  A2,WRcharpt(A0)         ;set character set record pointer
        BSR     DScurs                  ;display cursor on screen
        MOVEM.L (SP)+,D4/A0-A2          ;restore registers
        RTS                             ;return

;
; DScvtUC - Convert character to upper case
;
;       Enter:  D0.B = ASCII character
;
;       Exit:   D0.B = upper case ASCII character
;
DScvtUC CMPI.B  #DSClca,D0              ;is character lower case?
        BLO.S   DScvtU1                 ;no, return
        CMPI.B  #DSClcz,D0              ;*
        BHI.S   DScvtU1                 ;no, return
        SUBI.B  #DSCdiff,D0             ;convert character to upper case
DScvtU1 RTS                             ;return

;
; DSputst - Display a string
;
;       Enter:  A0.L - Character string pointer
;                      (terminated by 0)
;
DSputst movem.l D0/A0,-(SP)             ;save registers
                                        ;
DSpst1  move.b  (a0)+,d0                ;get next character
        beq.s   DSpst9                  ;finished, return
        bsr.s   DSputch                 ;output character
        bra.s   DSpst1                  ;get next character
                                        ;
DSpst9  movem.l (SP)+,D0/A0             ;restore registers
        rts                             ;return

        page
;
; DSputch - Display a character
;
;       Enter:  D0.B - Character to output
;
DSputch MOVEM.L D0-D7/A0-A6,-(SP)       ;save registers
        andi.w  #$7F,d0                 ;make character 7 bits
        LEA     CPwndrcd.W,A0           ;get RAM window record pointer  0.8
        tst.b   CPextcrt.w              ;using external CRT?            0.8
        bon.s   DSxCrt                  ;yes, use data comm 0           0.8
        bsr     SBprom2                 ;debug PROM boot?               0.7
        beq.s   DSxCrt                  ;yes, use data comm 0           0.7
        MOVE.L  WRcharpt(A0),A2         ;get character set record pointer
        CLR.L   D3                      ;
        MOVE.B  WRstate(A0),D3          ;
        LSL.W   #1,D3                   ;convert to state table index
        LEA     DSsTbl,A1               ;
        MOVE.W  0(A1,D3.W),D3           ;D3 = dist from DSsTbl
        JMP     0(A1,D3.W)              ;go to current state processing
                                        ;
DSnxtSt ADDQ.B  #1,WRstate(A0)          ;increment for next state
        BRA.S   DSexit                  ;return
                                        ;
DSxCrt  move.l  #DScPort,a1             ;get UART pointer               0.7
DSxCrt1 btst    #DSwrBit,DSuSt(a1)      ;is UART output busy?           0.7
        boff.s  DSxCrt1                 ;yes, try again                 0.7
        move.b  d0,DSuDa(a1)            ;output the character           0.7
        cmpi.b  #DSCcr,d0               ;was it a <CR>?                 0.7
        bne.s   DSreset                 ;no, go on                      0.7
        btst    #1,CPdspflg.w           ;auto line feed?                0.7
        bon.s   DSreset                 ;yes, bypass <LF> insertion     0.7
        moveq   #DSClf,d0               ;add a <LF>                     0.7
        BRA.S   DSxCrt1                 ;output LF                      0.8

DSreset CLR.B   WRstate(A0)             ;reset current state
                                        ;
DSexit  MOVEM.L (SP)+,D0-D7/A0-A6       ;restore registers
        RTS                             ;return
    page
DSst0   CMP.B   #DSCesc,D0              ;is char ESC?
        BEQ.S   DSnxtSt                 ;yes, go to next state
        CMP.W   CSfrstch(A2),D0         ;ascinum < first char?
        BLO.S   DSctl                   ;yes, it's a control char
        BSR.S   DSshwCh                 ;display character
        BSR     DSincx                  ;inccurx
        BRA.S   DSexit                  ;return

DSctl   SUBQ.W  #8,D0                   ;commence decoding ctrl char
        BMI.S   DSexit                  ;
        CMPI.W  #5,D0                   ;ascinum in [8..13]?
        BHI.S   DSexit                  ;yes, do cursor ctrl
        LEA     DScTbl,A3               ;A3==>jump table for ctrl chars
        LSL.W   #1,D0                   ;make it word count
        PEA     DSexit                  ;ensure RTS to exit
;----   MOVE.W  0(A3,D0),D0             ;D0 is offset from DScTbl  0.8
;----   JMP     0(A3,D0)                ;jump to proper routine    0.8
        BRA.S   DSgortn                 ;goto proper routine       0.8

DSesc   CLR.W   D1                      ;initialize index reg
        LEA     DSeTbl,A3               ;A3==> beginning of table
                                        ;
DSesc1  CMP.W   0(A3,D1),D0             ;does table entry match char?
        BEQ.S   DSesc2                  ;yes, go on
        ADDQ.W  #4,D1                   ;go to next entry
        TST.W   0(A3,D1)                ;end of table?
        BPL.S   DSesc1                  ;no, loop
        BRA.S   DSreset                 ;return
                                        ;
DSesc2  MOVE.W  D1,D0                   ;set D0 to table offset
        ADDQ.W  #2,D0                   ;
        PEA     DSreset                 ;ensure RTS to reset state
DSgortn MOVE.W  0(A3,D0),D0             ;D0 is offset from DSeTbl
        JMP     0(A3,D0)                ;jump to proper routine

        page
;
; DSshwCh - Display character
;
;       Enter:  A0.L = window record pointer
;               A2.L = character set record pointer
;               D0.W = ASCII character
;
;       Note:   Character set must be in bytes, not words
;
DSshwCh BSR     DScvtUC                 ;convert character to upper case
        CMP.W   CSfrstch(A2),D0         ;is character in character set?
        BLT.S   DSshow1                 ;no, output space
        CMP.W   CSlastch(A2),D0         ;*
        BLE.S   DSshow2                 ;yes, output character
DSshow1 MOVE.W  #DSCblnk,D0             ;no, output space
                                        ;
DSshow2 SUB.W   CSfrstch(A2),D0         ;get relative character position
        LEA     DScsetV+CSdata,a3       ;get pointer to character data
        MULU    #DScellW,D0             ;*
        ADDA.L  D0,A3                   ;*
        MOVE.L  WRcuradr(A0),A4         ;get current character address
        MOVE.W  CSlpch(A2),D1           ;get number of scan lines for character
        SUBQ.W  #1,D1                   ;get count for DBRA
        MOVE.W  CPscnofs.W,D3           ;get scan line length
        MOVE.W  WRbitofs(A0),D5         ;get bit offset of character in cell
        MOVE.L  CSmask(A2),D6           ;get character mask
        BTST    #vert,WRattr2(A0)       ;is this vertical orientation?
        BOFF.S  DSshow6                 ;no, output horizontal character
        ;
        ; output vertical orientation character
        ;
        MOVE.L  D6,D0                   ;
        NOT.L   D0                      ;D0 = inverted mask
        ROR.L   D5,D6                   ;D6 = positioned mask
DSshow3 MOVE.B  (A3)+,D2                ;D2 = char data
        LSL.W   #8,D2                   ;
        SWAP    D2                      ;get char in high word
        AND.L   D0,D2                   ;clear rest of source char
        LSR.L   D5,D2                   ;position source char
        AND.L   D6,(A4)                 ;clear dest char area
        OR.L    D2,(A4)                 ;move in character
        SUBA.W  D3,A4                   ;
        DBRA    D1,DSshow3              ;repeat for D1:=CSlpch-1 to 0
        BRA.S   DSshow9                 ;return

        page
        ;
        ; output horizontal orientation character
        ;
DSshow6 TST.W   -(A4)                   ;A4==>long word with cell
        ROL.L   D5,D6                   ;D6 = positioned mask
        MOVE.L  D6,D4                   ;
        NOT.L   D4                      ;D4 = inverted mask
        moveq   #7,d0                   ;use 8 bits of character data
                                        ;
DSshow7 clr.l   d2                      ;clear current scan line of character
        tst.w   d0                      ;have we used 8 bits of character data?
        blt.s   DSshw76                 ;yes, pad with space
        btst    d0,0(a3)                ;construct next horizontal character
        boff.s  DSshw71                 ;*  from vertical character data
        bset    #0,d2                   ;*
DSshw71 btst    d0,1(a3)                ;*
        boff.s  DSshw72                 ;*
        bset    #1,d2                   ;*
DSshw72 btst    d0,2(a3)                ;*
        boff.s  DSshw73                 ;*
        bset    #2,d2                   ;*
DSshw73 btst    d0,3(a3)                ;*
        boff.s  DSshw74                 ;*
        bset    #3,d2                   ;*
DSshw74 btst    d0,4(a3)                ;*
        boff.s  DSshw75                 ;*
        bset    #4,d2                   ;*
DSshw75 btst    d0,5(a3)                ;*
        boff.s  DSshw76                 ;*
        bset    #5,d2                   ;*
DSshw76 subq    #1,d0                   ;indicate another bit used
        LSL.L   D5,D2                   ;shift char into position
        AND.L   D6,(A4)                 ;
        OR.L    D2,(A4)                 ;
        SUBA.W  D3,A4                   ;
        DBRA    D1,DSshow7              ;
DSshow9 RTS                             ;return

        page
;
; DScrsR -- cursor right
;
DScrsR  BSR     DScurs0                 ;remove cursor
DSincx  MOVE.W  WRcursx(A0),D1          ;get current cursor X position
        ADDQ.W  #DScellW,D1             ;increment 1 character space    0.6
        MOVE.W  D1,WRcursx(A0)          ;save new cursor X position     0.6
        CMP.W   WRlngthx(A0),D1         ;at end of line?                0.6
        BGE.S   DSrtrn1                 ;yes, do carriage return        0.6
        BRA.S   DScurs                  ;show cursor                    0.6

;
; DScrsU -- cursor up
;
DScrsU  BSR.S   DScurs0                 ;remove cursor
        BRA.S   DSdecy                  ;decrement cursor Y position

;
; DSrtrn -- return
;
DSrtrn  BSR.S   DScurs0                 ;remove cursor
DSrtrn1 CLR.W   WRcursx(A0)             ;zero current cursor X position
        BTST    #1,CPdspflg.w           ;auto line feed?
        BOFF.S  DSincy                  ;no, increment cursor Y position
        BRA.S   DScurs                  ;show cursor

;
; DScrsD -- cursor down
;
DScrsD  BSR.S   DScurs0                 ;remove cursor
DSincy  MOVE.W  WRcursy(A0),D1          ;get current cursor Y position
        ADDI.W  #DScellY,D1             ;increment 1 character space    0.6
        MOVE.W  D1,WRcursy(A0)          ;save new cursor Y position     0.6
        CMP.W   WRlngthy(A0),D1         ;at bottom of screen?           0.6
        BLE.S   DScurs                  ;on bottom line?                0.6
        BRA     DSclAL                  ;yes, wrap to home position     0.6

;
; DScrsL -- cursor left
;
DScrsL  BSR.S   DScurs0                 ;remove cursor
DSdecx  TST.W   WRcursx(A0)             ;at beginning of line?
        BEQ.S   DSwrapx                 ;yes, wrap to previous line
        SUBQ.W  #DScellW,WRcursx(A0)    ;decrement 1 character space
        BRA.S   DScurs                  ;show cursor

        page
;
; DScrsH -- cursor home
;
DScrsH  BSR.S   DScurs0                 ;remove cursor
DScrsH1 CLR.W   WRcursx(A0)             ;zero current cursor X position
        CLR.W   WRcursy(A0)             ;zero current cursor Y position
        BRA.S   DScurs                  ;show cursor

DSwrapx BSR.S   DSwrap                  ;
        MOVE.W  D0,WRcursx(A0)          ;
DSdecy  TST.W   WRcursy(A0)             ;at top line?
        BEQ.S   DScurs                  ;yes, show cursor
        SUBI.W  #DScellY,WRcursy(A0)    ;decrement 1 character space
        BRA.S   DScurs                  ;show cursor

DSwrap  CLR.L   D0                      ;get current cursor X position
        MOVE.W  WRlngthx(A0),D0         ;*
        MOVEQ   #DScellW,D2             ;get character width
        DIVU    D2,D0                   ;
        MULU    D2,D0                   ;
        RTS                             ;return

DScurs  BSR.S   DScrsAd                 ;compute cursor address
DScurs0 MOVE.W  CSlpch(A2),D1           ;get scan lines per character
        SUBQ.W  #1,D1                   ;set loop counter
        MOVE.L  WRcuradr(A0),A4         ;get current cursor address
        MOVE.W  WRbitofs(A0),D5         ;get current cursor bit offset
        MOVE.L  CSmask(A2),D7           ;get character mask
        BTST    #vert,WRattr2(A0)       ;vertical orientation?
        BOFF.S  DScurs1                 ;no
        ROR.L   D5,D7                   ;
        BRA.S   DScurs2                 ;
DScurs1 TST.W   -(A4)                   ;
        ROL.L   D5,D7                   ;
DScurs2 NOT.L   D7                      ;D7 = positioned inverted mask
DScurs3 EOR.L   D7,(A4)                 ;invert character
        SUBA.W  CPscnofs.W,A4           ;*
        DBRA    D1,DScurs3              ;*
        RTS                             ;return

DScrsAd MOVEM.W WRcursx(A0),D5-D6       ;get current cursor position
        BSR     DSaddr                  ;compute cursor address
        MOVE.W  D7,WRbitofs(A0)         ;save cursor bit offset
        MOVE.L  A4,WRcuradr(A0)         ;save cursor address
        RTS                             ;return

        page
;
; DSclAL -- clear screen
;
DSclAL  BSR     DScrsH1                 ;home cursor

;
; DSclES -- clear to end of screen
;
DSclES  BSR.S   DSclEL                  ;first clear this line
        BTST    #vert,WRattr2(A0)       ;vertical orientation?
        BON.S   DSclES2                 ;yes, clear vertical screen
                                        ; --- clear to end of horizontal screen
        MOVE.W  WRcursy(A0),D6          ;get current cursor Y position
DSclES1 ADDI.W  #DScellY,D6             ;increment to next line
        MOVE.W  WRlngthy(A0),D0         ;get bottom of screen limit
        CMP.W   D0,D6                   ;at bottom of screen?
        BGE.S   DSclES9                 ;yes, return
        SUB.W   D6,D0                   ;compute number of scan lines to clear
        CLR.W   D3                      ;set starting X position to 0
        BSR.S   DSclrH                  ;clear to bottom of screen
        BRA.S   DSclES9                 ;return
                                        ; --- clear to end of vertical screen
DSclES2 MOVE.W  WRcursy(A0),D0          ;get current cursor Y position
DSclES3 ADDI.W  #DScellY,D0             ;increment to next line
        CMP.W   WRlngthy(A0),D0         ;at bottom of screen?
        BGE.S   DSclES9                 ;yes, return
        MOVE.W  D0,D6                   ;
        CLR.W   D5                      ;
        BSR.S   DSclrV                  ;clear one vertical line
        BRA.S   DSclES3                 ;repeat until all lines cleared
                                        ;
DSclES9 RTS                             ;return

;
; DSclEL -- clear to end of line
;
DSclEL  BSR     DScurs0                 ;remove cursor
        MOVEM.W WRcursx(A0),D5-D6       ;get current cursor X and Y
        BTST    #vert,WRattr2(A0)       ;vertical orientation
        BOFF.S  DSclEL1                 ;no, clear horizontal line
        BSR.S   DSclrV                  ;clear one vertical line
        BRA.S   DSclEL2                 ;show cursor
                                        ;
DSclEL1 MOVEQ   #DScellY-1,D0           ;D0 = #scanlines to clear
        MOVE.W  D5,D3                   ;
        BSR.S   DSclrH                  ;clear one horizontal line
DSclEL2 BRA     DScurs0                 ;show cursor

        page
DSclrV  MOVE.W  WRlngthx(A0),D4         ;get length of line
        SUB.W   D5,D4                   ;compute number of scan lines clear
        BSR     DSaddr                  ;compute cursor address
        MOVE.W  CPscnofs.W,D1           ;get bytes per scan line
        MOVE.L  CSmask(A2),D6           ;get character mask
        ROR.L   D7,D6                   ;align character mask
DSclrV1 AND.L   D6,(A4)                 ;clear one scan line
        SUBA.W  D1,A4                   ;compute address of next scan line
        DBRA    D4,DSclrV1              ;repeat to end of line
        RTS                             ;return

DSclrH  MOVE.W  WRlngthx(A0),D4         ;D5 = x, D6 = y
        ADDQ.W  #1,D4                   ;
        MOVE.W  D4,D5                   ;
        SUB.W   D3,D4                   ;
        BSR     DSaddr                  ;A4 = addr(x,y), D7 = bitnum
        SUB.W   D7,D4                   ;
        TST.W   D7                      ;
        BNE.S   DSclrH1                 ;
        TST.W   (A4)+                   ;
DSclrH1 MOVE.W  D4,D3                   ;
        ANDI.W  #$F,D3                  ;
        ASR.W   #4,D4                   ;
        SUBQ.W  #1,D4                   ;
        MOVEQ   #-1,D1                  ;
        MOVE.W  D1,D2                   ;
        LSL.W   D7,D1                   ;
        LSR.W   D3,D2                   ;
DSclrH2 MOVE.L  A4,A5                   ;
        TST.W   D7                      ;
        BEQ.S   DSclrH3                 ;
        AND.W   D1,(A5)+                ;
DSclrH3 MOVE.W  D4,D6                   ;
        BMI.S   DSclrH5                 ;
DSclrH4 CLR.W   (A5)+                   ;
        DBRA    D6,DSclrH4              ;
DSclrH5 TST.W   D3                      ;
        BEQ.S   DSclrH6                 ;
        AND.W   D2,(A5)                 ;
DSclrH6 SUBA.W  CPscnofs.W,A4           ;
        DBRA    D0,DSclrH2              ;
        RTS                             ;return

        page
DStab   MOVE.W  WRcursx(A0),D0          ;get current cursor X position
        AND.L   #$FFFF,D0               ;clear hi word
        MOVEQ   #DScellW*8,D2           ;
        DIVU    D2,D0                   ;find next tab to right
        ADDQ.W  #1,D0                   ;
        MULU    D2,D0                   ;
        CMP.W   WRlngthx(A0),D0         ;new x > right?
        BLS.S   DStab1                  ;no, change x
        RTS                             ;return

DStab1  BSR     DScurs0                 ;remove cursor
        MOVE.W  D0,WRcursx(A0)          ;save new cursor X position
        BRA     DScurs                  ;show cursor

;
; DSaddr -- compute cursor address
;
;       Enter:  D5 = x
;               D6 = y
;
;       Exit:   DSaddr (x,y) in A4, bit offset in D7
;
DSaddr  MOVEA.L WRhomept(A0),A4         ;get home pointer for orientation
        BTST    #vert,WRattr2(A0)       ;vertical orientation?
        BON.S   DSaddrV                 ;yes, compute address for vertical
                                        ;
DSaddrH ADD.W   WRhomeof(A0),D5         ;
        MOVE.W  D5,D7                   ;
        ANDI.W  #$F,D7                  ;
        ASR.W   #4,D5                   ;
        ASL.W   #1,D5                   ;
        SUBA.W  D5,A4                   ;
        MULU    CPscnofs.W,D6           ;
        SUBA.L  D6,A4                   ;
        RTS                             ;return
                                        ;
DSaddrV ADD.W   WRhomeof(A0),D6         ;
        MOVE.W  D6,D7                   ;
        ANDI.W  #$F,D7                  ;
        ASR.W   #4,D6                   ;
        ASL.W   #1,D6                   ;
        ADDA.W  D6,A4                   ;
        MULU    CPscnofs.W,D5           ;
        SUBA.L  D5,A4                   ;
        RTS                             ;return

        page
;
; jump tables
;
DScTbl  DATA.W  DScrsL-DScTbl           ;ctl-H: back space
        DATA.W  DStab-DScTbl            ;ctl-I: tab
        DATA.W  DScrsD-DScTbl           ;ctl-J: line feed
        DATA.W  DScrsU-DScTbl           ;ctl-K: cursor up
        DATA.W  DScrsR-DScTbl           ;ctl-L: cursor right
        DATA.W  DSrtrn-DScTbl           ;ctl-M: carriage return

DSeTbl  DATA.W  $48,DScrsH-DSeTbl       ;esc-H: home cursor
        DATA.W  $4A,DSclAL-DSeTbl       ;esc-J: clear screen
        DATA.W  $4B,DSclEL-DSeTbl       ;esc-K: clear to end of line
        DATA.W  $59,DSclES-DSeTbl       ;esc-Y: clear to end of screen
        DATA.W  -1                      ;end of table

DSsTbl  DATA.W  DSst0-DSsTbl            ;state 0
        DATA.W  DSesc-DSsTbl            ;state 1

DSwndH  DATA.L  DScsetH                 ;WRcharpt
        DATA.L  DShomeH                 ;home
        DATA.L  DShomeH                 ;address
        DATA.W  0,0,0                   ;WRhomeof,WRbasex,WRbasey
        DATA.W  DSmaxXH,DSmaxYH         ;right,bottom
        DATA.W  0,0,0                   ;x,y,WRbitofs
        DATA.W  0,DSmaxYH               ;WRgrorgx,WRgrorgy
        DATA.B  0,$1C                   ;attr1,attr2
        DATA.B  0,WRlength              ;state, WRrcdlen

DScsetH DATA.L  DScsetV+CSdata          ;character set record pointer
        DATA.W  DScellY,DScellW         ;CSlpch, CSbpch
        DATA.W  32,90                   ;CSfrstch, CSlastch
        DATA.W  $FFFF,$FFC0,0           ;mask, dummy, attribs
        ;
        ; use vertical character set data
        ;

        page
DSwndV  DATA.L  DScsetV                 ;WRcharpt
        DATA.L  DShomeV                 ;home
        DATA.L  DShomeV                 ;address
        DATA.W  0,0,0                   ;WRhomeof,WRbasex,WRbasey
        DATA.W  DSmaxXV,DSmaxYV         ;right,bottom
        DATA.W  0,0,0                   ;x,y,WRbitofs
        DATA.W  0,DSmaxYV               ;WRgrorgx,WRgrorgy
        DATA.B  0,$1D                   ;attr1,attr2
        DATA.B  0,WRlength              ;state, WRrcdlen

DScsetV DATA.L  DScsetV+CSdata          ;character set record pointer
        DATA.W  DScellW,DScellY         ;CSlpch, CSbpch
        DATA.W  32,90                   ;CSfrstch, CSlastch
        DATA.W  $003F,$FFFF,256         ;mask, dummy, attribs
        ;
        ; vertical character set data
        ;
        DATA.B  0,0,0,0,0,0             ; blank
        DATA.B  0,0,$FD,0,0,0           ; !
        DATA.B  0,$E0,0,$E0,0,0         ; "
        DATA.B  $28,$FE,$28,$FE,$28,0   ; #
        DATA.B  $24,$54,$FE,$54,$48,0   ; $
        DATA.B  $C4,$C8,$10,$26,$46,0   ; %
        DATA.B  $6C,$92,$6A,$04,$0A,0   ; &
        DATA.B  0,0,$20,$C0,0,0         ; '
        DATA.B  0,$38,$44,$82,0,0       ; (
        DATA.B  0,0,$82,$44,$38,0       ; )
        DATA.B  $08,$2A,$1C,$2A,$08,0   ; *
        DATA.B  $08,$08,$3E,$08,$08,0   ; +
        DATA.B  0,$01,$07,0,0,0         ; ,
        DATA.B  $10,$10,$10,$10,$10,0   ; -
        DATA.B  0,0,$02,0,0,0           ; .
        DATA.B  $04,$08,$10,$20,$40,0   ; /
        DATA.B  $7C,$8A,$92,$A2,$7C,0   ; 0
        DATA.B  0,$42,$FE,$02,0,0       ; 1
        DATA.B  $46,$8A,$92,$92,$62,0   ; 2
        DATA.B  $84,$82,$92,$B2,$CC,0   ; 3
        DATA.B  $18,$28,$48,$FE,$08,0   ; 4
        DATA.B  $E4,$A2,$A2,$A2,$9C,0   ; 5
        DATA.B  $3C,$52,$92,$92,$1C,0   ; 6
        DATA.B  $80,$8E,$90,$A0,$C0,0   ; 7
        DATA.B  $6C,$92,$92,$92,$6C,0   ; 8
        DATA.B  $62,$92,$92,$94,$78,0   ; 9
        DATA.B  0,0,$24,0,0,0           ; :
        DATA.B  0,$01,$26,0,0,0         ; ;
        DATA.B  0,$10,$28,$44,$82,0     ; <
        DATA.B  0,$28,$28,$28,$28,0     ; =
        DATA.B  0,$82,$44,$28,$10,0     ; >
        DATA.B  $40,$80,$9A,$A0,$40,0   ; ?
        DATA.B  $7C,$82,$9A,$9A,$7A,0   ; @
        DATA.B  $3E,$48,$88,$48,$3E,0   ; A
        DATA.B  $FE,$92,$92,$92,$6C,0   ; B
        DATA.B  $7C,$82,$82,$82,$44,0   ; C
        DATA.B  $FE,$82,$82,$82,$7C,0   ; D
        DATA.B  $FE,$92,$92,$82,$82,0   ; E
        DATA.B  $FE,$90,$90,$80,$80,0   ; F
        DATA.B  $7C,$82,$8A,$8A,$4C,0   ; G
        DATA.B  $FE,$10,$10,$10,$FE,0   ; H
        DATA.B  0,$82,$FE,$82,0,0       ; I
        DATA.B  $04,$82,$82,$FC,$80,0   ; J
        DATA.B  $FE,$10,$28,$44,$82,0   ; K
        DATA.B  $FE,$02,$02,$02,$02,0   ; L
        DATA.B  $FE,$40,$30,$40,$FE,0   ; M
        DATA.B  $FE,$20,$10,$08,$FE,0   ; N
        DATA.B  $7C,$82,$82,$82,$7C,0   ; O
        DATA.B  $FE,$90,$90,$90,$60,0   ; P
        DATA.B  $7C,$82,$8A,$84,$7A,0   ; Q
        DATA.B  $FE,$90,$98,$94,$62,0   ; R
        DATA.B  $64,$92,$92,$92,$4C,0   ; S
        DATA.B  $80,$80,$FE,$80,$80,0   ; T
        DATA.B  $FC,$02,$02,$02,$FC,0   ; U
        DATA.B  $F8,$04,$02,$04,$F8,0   ; V
        DATA.B  $FC,$02,$1C,$02,$FC,0   ; W
        DATA.B  $C6,$28,$10,$28,$C6,0   ; X
        DATA.B  $C0,$20,$1E,$20,$C0,0   ; Y
        DATA.B  $86,$8A,$92,$A2,$C2,0   ; Z
        DATA.W  0

        page    ;p.prom.ld              ;
    list 1
; File: CC.PROM.LD.TEXT
; Date: 25-Jan-84
; By:   L. Franklin

;
; Lboot -- Local Corvus disk boot processing
;
Lboot   movea.l #CPsl1typ,a1        ;get pointer to slot 1 type
        moveq   #1,d0               ;get initial slot number
                                    ;
Lboot10 move.b  -1(a1,d0),d1        ;get device type
        cmp.b   #DTlocl,d1          ;is this a local disk interface?
        beq.s   Lboot30             ;yes, use it for booting
        addq    #1,d0               ;update slot number
        cmp.w   #4,d0               ;have we looked at all slots?
        ble.s   Lboot10             ;no, check next slot
        moveq   #-1,d7              ;set error return code
        bra.s   Lboot90             ;return (can not find local disk)
                                    ;
Lboot30 move.b  d0,CPbtslot.w       ;set boot slot number
        clr.b   CPbtsrvr.w          ;set boot server number
        lea     LDblkIO,a6          ;set boot disk blk i/o subr pointer
        move.l  a6,CPblkio.w        ;*
        lea     LDdskIO,a6          ;set boot disk i/o subr pointer
        move.l  a6,CPdskio.w        ;*
;
; fall through to Lboot10 (used by OMNINET boot too)
;
    page
;
; Lboot80 -- Get 2 boot blocks from Corvus disk                  0.8
;            (code shared by local disk boot and OMNINET disk boot)
;      Entry :  A6 = address of disk I/O routine                 0.8
;      Exit  :  lt = error                                       0.8
;               ge = good                                        0.8
;               A0 = if good then address of entry to boot code  0.8
;
Lboot80 movea.l #USRbase,a0     ;get block buffer pointer
        move.b  CPbtsrvr.w,d6   ;get boot server number
        blt.s   Lboot90         ;just return if invalid server number
        lsl.w   #8,d6
        move.b  CPbtslot.w,d6   ;get boot slot number
                                ;setup boot command
;for Const II boot use Concept2 boot file - *kb 1/25/84 1.0
        move.w  #$4417,(a0)     ;put in opcode & computer type 1.0
        moveq   #$01,d0         ;set boot block number         0.8
        move.w  #$203,d1        ;read 2nd block first
        bsr.s   LDgetBB         ;get next boot block
        blt.s   Lboot90         ;just return if error

        moveq   #$3,d1          ;read 1st block 2nd            0.8
        bsr.s   LDgetBB         ;get next boot block
        blt.s   Lboot90         ;just return if error
        adda.w  #4+12,a0        ;get pointer to boot code      0.8
                                ;  past cmd, rslt code and file header
Lboot90 rts

LDgetBB move.b  d0,2(a0)        ;set boot block number
        moveq   #3,d2           ;get number of bytes to send
        moveq   #DskWrit,d5     ;get "write" command
        jsr     (a6)            ;send command
        adda.w  d1,a0           ;adr where to put boot code
        move.w  #513,d2         ;get number of bytes to receive
        moveq   #DskRead,d5     ;get "read" command
        jsr     (a6)            ;read results
        blt.s   LDgetBX         ;just return if error
        suba.w  d1,a0           ;point back at command
        subq.l  #1,d0           ;update boot block number      0.8
LDgetBX tst.b   d7
        rts                     ;return
   page
;
; LDblkIO - Read or write a local disk block subroutine
;
;       Enter:  A0.L - Buffer address
;               D0.L - Block number                                     0.7
;               D1.W - Drive number
;               D5.W - Read ($32) or Write ($33) command
;               D6.B - Slot number
;
;       Exit:   A0.L - Next free location in buffer
;               D0.L - Updated block number                             0.7
;               D7.W - IORESULT (disk controller status)
;
;       All other registers are preserved.
;
;       Corvus controller status register [3(a1)]:
;
;       bit 7: controller ready    off - ready          on - not ready
;       bit 6: bus direction       off - host to cntlr  on - cntlr to host
;
LDblkIO movem.l a1/d0-d2,-(sp)  ;Save registers
        bsr     SlotAdr         ;A1 = I/O port address
        move.w  d5,d2           ;Send a read ($32) or
        bsr.s   LDsend1         ;  write ($33) block command
        move.l  d0,d2           ;Compute drive nmbr/MSN block nmbr      0.7
        swap    d2              ;*                                      0.7
        lsl.w   #4,d2           ;*                                      0.7
        or.b    d1,d2           ;*                                      0.7
        bsr.s   LDsend          ;Send drive number
        move.w  d0,d2           ;
        bsr.s   LDsend          ;Send LSB of block
        lsr.w   #8,d2           ;
        bsr.s   LDsend          ;Send MSB of block
        cmpi.w  #DskWrit,d5     ;Are we reading or writing?
        bne.s   LDrio1          ;Reading
                                ;
                                ;Write block processing
                                ;
        move.w  #$1FF,d2        ;Block size - 1
LDwio1  btst    #7,3(a1)        ;Test controller status
        bon.s   LDwio1          ;Wait until controller ready
        move.b  (a0)+,1(a1)     ;Send a byte
        dbra    d2,LDwio1       ;Loop until done
        bsr.s   LDwait          ;Wait for line to turn
        move.b  1(a1),d7        ;Fetch result code
        bra.s   LDrtrn          ;Return

        page
                                ;
                                ;Read block processing
                                ;
LDrio1  bsr.s   LDwait          ;Wait for the line to turn
        move.b  1(a1),d7        ;Fetch result code
                                ;
LDrio3  btst    #7,3(a1)        ;Test controller status
        bon.s   LDrio3          ;Wait until controller ready
        btst    #6,3(a1)        ;Test bus direction
        boff.s  LDrtrn          ;Finished if "host to controller"
        move.b  1(a1),(a0)+     ;Store next byte
        bra.s   LDrio3          ;Go get any more
                                ;
LDrtrn  movem.l (sp)+,a1/d0-d2  ;Restore registers
        addq.l  #1,d0           ;Update block number                    0.8
        move.b  d7,CPdiskRC.w   ;Save current disk return code          0.7
        ext.w   d7              ;Set return condition code
        rts                     ;Return

        page
;
; LDsend -- Send a byte to the disk port subroutine
;
;       Enter:  A1.L - I/O port address
;               D2.B - Byte to send
;
;       All registers are preserved.
;
LDsend  btst    #7,3(a1)        ;Test controller status
        bon.s   LDsend          ;Wait until controller ready
        move.b  d2,1(a1)        ;Send the byte
        rts                     ;Return

;
; LDsend1 -- Send first byte to the disk port subroutine
;
;       Enter:  A1.L - I/O port address
;               D2.B - Byte to send
;
;       All registers are preserved.
;
LDsend0 move.w  (sp)+,sr        ;enable interrupts
        nop                     ;leave some time for interrupt processing
LDsend1 move.w  sr,-(sp)        ;save interrupt level
        ori.w   #$0700,sr       ;disable interrupts
        btst    #7,3(a1)        ;test controller status
        bon.s   LDsend0         ;wait until controller ready
        move.b  d2,1(a1)        ;send first byte
        move.w  (sp)+,sr        ;enable interrupts
        rts                     ;return

;
; LDwait -- Wait for the line to turn subroutine
;
;       Enter:  A1.L - I/O port address
;
LDwait  move.l  d0,-(sp)        ;save register
        moveq   #100,d0         ;wait a little bit
LDwait1 dbra    d0,LDwait1      ;*
        move.l  (sp)+,d0        ;restore register
        bsr.s   LDwait2         ;check two times in case of glitch
        nop                     ;*
                                ;
LDwait2 btst    #7,3(a1)        ;test controller status
        bon.s   LDwait2         ;wait until controller ready
        btst    #6,3(a1)        ;test bus direction
        boff.s  LDwait2         ;wait until "controller to host"
        rts                     ;return

        page
;
; LDdskIO - Read from/Write to Corvus disk
;
;       Enter:  A0.L - Buffer address
;               D2.W - Count
;               D5.W - Read ($32) or Write ($33) command
;               D6.B - Slot number
;
;       Exit:   D7.W - IORESULT (disk controller status)
;
;       All other registers are preserved.
;
LDdskIO movem.l d0-d3/a0-a1,-(sp);Save registers
        bsr     SlotAdr         ;A1 = I/O port address
        move.w  d2,d3           ;get count
        subq.w  #1,d3           ;Set DBRA loop length
        cmpi.w  #DskWrit,d5     ;Are we reading or writing?
        bne.s   LDdsk2          ;Reading
;
; Write Corvus disk processing
;
        move.b  (a0)+,d2        ;get first byte
        bsr.s   LDsend1         ;send first byte
        bra.s   LDdsk1a         ;send rest of bytes
LDdsk1  move.b  (a0)+,d2        ;get next byte
        bsr     LDsend          ;send next byte
LDdsk1a dbra    d3,LDdsk1       ;loop until done
        moveq   #0,d7           ;force successful result code
        bra.s   LDdsk9          ;finished
;
; Read Corvus disk processing
;
LDdsk2  bsr.s   LDwait          ;Wait for the line to turn
        move.b  1(a1),d7        ;Fetch result code
        move.b  d7,(a0)+        ;Store first byte
                                ;
LDdsk3  btst    #7,3(a1)        ;Test controller status
        bon.s   LDdsk3          ;Wait until controller ready
        btst    #6,3(a1)        ;Test bus direction
        boff.s  LDdsk9          ;Finished if "host to controller"
        move.b  1(a1),(a0)+     ;Store next byte
        bra.s   LDdsk3          ;Go get any more
                                ;
LDdsk9  movem.l (sp)+,d0-d3/a0-a1;Restore registers
        move.b  d7,CPdiskRC.w   ;Save current disk return code          0.7
        ext.w   d7              ;Set return condition code
        rts                     ;Return

        page
;
; LDsync -- Synchronize with Corvus disk controller
;
;       Enter:  D6.B - slot number
;
;       Exit:   D7.W - 0 = no timeout (EQ), -1 = timeout (NE)
;
;       All other registers are preserved.
;
LDsync  movem.l d0/a1,-(sp)     ;save registers
        bsr     SlotAdr         ;get slot address
; ----  move.w  #2000,d7        ;set timeout counter                    0.7
        move.w  #3000,d7        ;set timeout counter                    0.7
                                ;
LDsync1 move.b  #$FF,1(a1)      ;send invalid command to controller
        move.w  #1024,d0        ;wait about 1 ms
LDsync2 dbra    d0,LDsync2      ;*
        btst    #6,3(a1)        ;test bus direction
        bon.s   LDsync3         ;go on if "controller to host"
        dbra    d7,LDsync1      ;send invalid command again
        bra.s   LDsync5         ;set timeout error and return
                                ;
LDsync3 btst    #7,3(a1)        ;test controller status
        boff.s  LDsync6         ;go on if controller ready
        dbra    d7,LDsync3      ;check controller status again
                                ;
LDsync5 moveq   #-1,d7          ;indicate controller timeout
        bra.s   LDsync9         ;return
                                ;
LDsync6 cmpi.b  #$8F,1(a1)      ;did controller respond with error?
        bne.s   LDsync1         ;no, send invalid command again
        moveq   #0,d7           ;indicate no controller timeout
                                ;
LDsync9 movem.l (sp)+,d0/a1     ;restore registers
        tst.w   d7              ;set return condition code
        rts                     ;return


        page    ;p.prom.od              ;
; File: CC.PROM.OD.TEXT
; Date: 18-Jan-84

; OMNINET disk driver data area equates
;
DCmd    EQU     0       ;byte - disk command offset
DCdrv   EQU     1+DCmd  ;byte - offset for drive number
DCblklo EQU     2+DCmd  ;byte - LSB of block number to read or write
DCblkhi EQU     3+DCmd  ;byte - MSB      "     "
DClen   EQU     4+DCmd  ;word - length of request (in bytes)

;  result vector and header used for all setuprecv commands
;
RHdr    EQU     6       ;
RHpktRC EQU     0+RHdr  ;byte - return code from transporter
RHsor   EQU     1+RHdr  ;byte - the source of the message
RHpktLN EQU     2+RHdr  ;word - total length of data portion of packet
RHdskLN EQU     4+RHdr  ;word - length of info returned from drive
RHdskRC EQU     6+RHdr  ;byte - return code from drive
;
;  result vector and header for all sendmsg commands
;
SHdr    EQU     14      ;
SHpktRC EQU     0+SHdr  ;byte - return code from transporter
;       EQU     1+SHdr  ;byte - unused
;       EQU     2+SHdr  ;word - unused
SHtoLN  EQU     4+SHdr  ;word - number of bytes to send to drive
SHfmLN  EQU     6+SHdr  ;word - number of bytes expected from drive
                        ;
GData   EQU     22      ;word - area to receive "GO" into

;  area used for constructing Transporter commands
;
TCmd    EQU     24      ;
TCop    EQU     0+TCmd  ;byte - op code
TCrADhi EQU     1+TCmd  ;byte - result address HI
TCrADlo EQU     2+TCmd  ;word - result address MED, LO
TCsock  EQU     4+TCmd  ;byte - socket number
TC6801Ad EQU    4+TCmd  ;word - 6801 addr for peek/poke              0.8
TCdADhi EQU     5+TCmd  ;byte - data buffer address HI
TCdADlo EQU     6+TCmd  ;word - data buffer address MED, LO
TCpekpok EQU    6+TCmd  ;byte - peek/poke function code              0.8
TCdtaLN EQU     8+TCmd  ;word - data length
TChdrLN EQU     10+TCmd ;byte - header length
TCdest  EQU     11+TCmd ;byte - destination host number
                        ;
ODdw    EQU     36      ;lint - temporary buffer (for 3 byte nmbrs)
ODdwhi  EQU     37      ;byte - temporary HI
ODdwlo  EQU     38      ;word - temporary MID, LO
ODwrAD  EQU     40      ;lint - to save buffer address for CWrites
ODvalid EQU     44      ;word - for marking buffer as valid
EHpktRC EQU     46      ;byte - End Recv result code
    page
; Transporter Return Codes
;
Waiting EQU     $FF     ;
CmdAcpt EQU     $FE     ;
Echoed  EQU     $C0     ;echo command was successful
                        ;
GaveUp  EQU     $80     ;aborted a send command after MaxRetries tries
TooLong EQU     $81     ;last message sent was too long for the receiver
NoSockt EQU     $82     ;sent to an unititialized socket
HdrErr  EQU     $83     ;sender's header length did not match receiver's
BadSock EQU     $84     ;illegal socket number
Inuse   EQU     $85     ;tried to set up a receive on an active socket
BadDest EQU     $86     ;sent to an illegal host number

NoTrans EQU     -112    ;could not strobe cmd addr to Transporter
TimeOut EQU     -111    ;timed out waiting for an Omninet event
NoBufr  EQU     -110    ;tried a CRRead without a valid write buffer
;
; Transporter Opcodes
;
RecvOp  EQU     $F0     ;SETUPRECV opcode
SendOp  EQU     $40     ;SENDMSG opcode
InitOp  EQU     $20     ;INIT opcode
EndOp   EQU     $10     ;ENDRECV opcode
DebOp   EQU     $08     ;PEEK/POKE opcode
EchoOp  EQU     $02     ;ECHOCMD opcode
WhoOp   EQU     $01     ;WHOAMI opcode

; Socket numbers
;
RestSkt EQU     $A0     ;dest. socket for REST packet
CnstSkt EQU     $B0     ;socket for disk server protocol
CIIpSkt EQU     $80     ;socket for Const II protocol                   0.8

; Const II protocol
;
C2pid    EQU     $01FE  ;Const II protocol id                           0.8
C2whomg  EQU     $0200  ;msgtype= who msg                               0.8
C2idmsg  EQU     $1000  ;msgtype= identification                        0.8
C2dsksrv EQU     $0001  ;devtype= disk server                           0.8
C2omndsk EQU     $0006  ;devtype= omnidisk                              1.0

; Const II protocol message form                                        0.8
;                                                                       0.8
C2Ppid   EQU     0           ;word - protocol id                        0.8
C2Pmsgtp EQU     C2Ppid+2    ;word - message type                       0.8
C2Psrc   EQU     C2Pmsgtp+2  ;word - source host number                 0.8
C2Pdevtp EQU     C2Psrc+2    ;word - device type                        0.8
C2PwhoLn EQU     C2Pdevtp+2  ;length of "who" msg                       0.8
C2Pname  EQU     C2Pdevtp+2  ;chars - name  10 bytes                    0.8
C2PmaxLn EQU     C2Pname+10  ;length of other msgs                      0.8

TOintvl EQU     $2FFFF  ;timeout interval                               0.8
    page
;
; Oboot -- OMNINET disk server boot processing
;
Oboot   tst.b   CPbtsrvr.w      ;is server number valid?                0.7
        bge.s   Oboot10         ;yes, go on                             0.7
        bsr     ODbroad         ;send broadcast message to disk srvr    0.7
                                ;*  in order to get disk server         0.7
                                ;*  Transporter number                  0.7
        move.b  d7,CPbtsrvr.w   ;save boot server number                0.7

Oboot10 move.b  #5,CPbtslot.w   ;set boot slot number
        lea     ODblkIO,a6      ;set boot disk blk i/o subr pointer
        move.l  a6,CPblkio.w    ;*
        lea     ODdskIO,a6      ;set boot disk i/o subr pointer
        move.l  a6,CPdskio.w    ;*
        bra     Lboot80         ;load boot code
                                ;   (Lboot80 is in CC.PROM.LD)
;
; ODcomnd -- send simple command to Transporter
;
;       Enter:  D0.B - Transporter command
;               D1.B - Destination host number (if ECHO)                0.7
;               D1.B - Socket number (if ENDRECV)                       0.7
;               D1.B - MSB 6801 address (if PEEK/POKE)                  0.8
;
;       Exit:   D7.B - IORESULT (OMNINET status)
;
ODcomnd movem.l a1-a3/d0,-(sp)          ;save registers                 0.8
        move.l  #CPomnibf,a1            ;get pointer to Data Area
        lea     RHpktRc(A1), A3         ;assume not end recv            0.8
        cmpi.b  #EndOp, D0              ;if is a end recv use different 0.8
        bne.s   ODcmdgo                 ;  result code location than    0.8
        lea     EHpktRC(A1), A3         ;  the recv                     0.8
ODcmdgo move.l  A3,TCop(a1)             ;(A3) = result record pointer   0.8
        move.b  d0,TCop(a1)             ;set Transporter command
        move.b  d1,TCsock(a1)           ;set dest host number (ECHO)    0.7
                                        ;set socket number (ENDRECV)    0.7
        move.b  #Waiting,(A3)           ;set Transporter waiting flag   0.8
        lea     TCmd(A1),A2             ;get command address
        bsr     StrobIt                 ;strobe command address to Transporter
        bne.s   ODcmd9                  ;Transporter not responding

        move.l  #TOintvl,D0             ;get timeout interval           0.8
ODcmd1  move.b  (A3),d7                 ;get Transporter return code    0.8
        cmpi.b  #Waiting, d7            ;has Transporter responded?     0.8
        bne.s   ODcmd9                  ;yes, ready to return           0.8
        subq.l  #1, D0                  ;see if waited long enough      0.8
        bpl.s   ODcmd1                  ;no, look again                 0.8
        moveq   #TimeOut, d7            ;yes, set timeout error & return0.8

ODcmd9  movem.l (sp)+,a1-a3/d0          ;restore registers              0.8
        move.b  d7,CPomniRC.w           ;save OMNINET return code       0.7
        rts                             ;return
    page
;
; ODblkIO - Read or write an OMNINET disk server block subroutine
;
;       Enter:  A0.L - Buffer address
;               D0.L - Block number                                     0.7
;               D1.W - Drive number
;               D5.W - Read ($32) or Write ($33) command
;               D6.W - Destination host number * 256
;
;       Exit:   A0.L - Next free location in buffer
;               D0.L - Updated block number                             0.7
;               D7.W - IORESULT (OMNINET/disk controller status)
;
;       All other registers are preserved.
;
ODblkIO movem.l d0-d2/a1-a2,-(sp);Save registers
        move.l  #CPomnibf,a1    ;A1 points to the start of the Data Area
        clr.w   ODvalid(A1)     ;buffer valid = False... see ODdskIO
        move.b  D5,DCmd(A1)     ;Stuff disk command - read or write
        move.l  d0,d2           ;Compute drive nmbr/MSN block nmbr      0.7
        swap    d2              ;*                                      0.7
        lsl.w   #4,d2           ;*                                      0.7
        or.b    d1,d2           ;*                                      0.7
        move.b  D2,DCDrv(A1)    ;stuff drive number                     0.7
        move.b  D0,DCBlkLo(A1)  ;lo order byte of block number
        lsr.w   #8,D0           ;
        move.b  D0,DCBlkHi(A1)  ;hi order byte of block number
        move.w  #512,DCLen(A1)  ;set length to 512...
        cmpi.w  #DskWrit,D5     ;Are we reading or writing?
        bne.s   ODblk2          ;Reading
                                ;
ODblk1  move.w  #516,SHtoLN(A1) ;number of bytes to send to drive
        clr.w   SHfmLN(A1)      ;number of bytes expected back
        move.l  A0,ODwrAD(A1)   ;save address of REST of data
        bsr     LongCmds        ;Writing
        bra.s   ODblk3          ;return
                                ;
ODblk2  move.w  #4,SHtoLN(A1)   ;number of bytes to send to drive
        move.w  #512,SHfmLN(A1) ;number of bytes expected back
        bsr     ShortCmds       ;
                                ;
ODblk3  movem.l (sp)+,d0-d2/a1-a2;Restore registers
        adda.w  #512,a0         ;Update buffer pointer
        addq.l  #1,d0           ;Update disk block number               0.8
        move.b  d7,CPdiskRC.w   ;Save current disk return code          0.7
        ext.w   d7              ;Set return condition code
        rts                     ;Return
    page
;                                                                       0.7
; ODbroad - Send broadcast message to disk server                       0.7
;                                                                       0.7
;       Exit:   D7.W - disk server number                               0.7
;                                                                       0.7
;       All other registers are preserved.                              0.7
;                                                                       0.7
ODbroad movem.l d2-d6/a0,-(sp)  ;save registers                         0.7
        move.l  #NTO1,CPtimout.w;set timeout for broadcast              0.7
        bsr.s   ODC2broad       ;do const II broadcast                  0.8
        bpl.s   ODbrd90         ;found disk server                      0.8
        move.l  #USRbase,a0     ;send broadcast message to disk srvr    0.7
        move.b  #$FF,(a0)       ;*  in order to get disk server         0.7
        moveq   #1,d2           ;*  Transporter number                  0.7
        moveq   #$33,d5         ;*                                      0.7
        moveq   #-1,d6          ;*                                      0.7
        bsr     ODdskIO         ;*                                      0.7
        moveq   #$32,d5         ;*                                      0.7
        bsr     ODdskIO         ;*                                      0.7
ODbrd90 move.l  #NTO2,CPtimout.w;set timeout for normal network traffic 0.7
        movem.l (sp)+,d2-d6/a0  ;restore registers                      0.7
        rts                     ;return                                 0.7

;
; ODpeek - do peek at $F800 to find version number                      0.8
;                                                                       0.8
;        Exit:  D7.B - peek data or error code                          0.8
;                                                                       0.8
ODpeek   LEA       CPomnibf.L, A1      ;omninet data area               0.8
         MOVE.W    #$F800, TC6801Ad(A1) ;put in 6801 addr to peek       0.8
         CLR.B     TCpekpok(A1)        ;do peek                         0.8
         MOVEQ     #DebOp, D0          ;peek/poke op code               0.8
         MOVE.W    #$F8, D1            ;ODcomnd puts D1 over TC6801Ad   0.8
         BSR       ODcomnd             ;do peek                         0.8
         MOVE.B    RHpktRC(A1), D7     ;get version number              0.8
         RTS                           ;                                0.8
    page
;
; ODC2broad - broadcast the Const II who are you to find disk server    0.8
;             receive ident msg from disk server.                       0.8
;                                                                       0.8
;        Exit:  MI - no response or error                               0.8
;               PL - found disk server                                  0.8
;               D7.W - disk server number or error code                 0.8
;                                                                       0.8
ODC2broad                              ;                                0.8
         MOVEQ     #1, D2              ;try 2 times max.                0.8
         LEA       CPomnibf.L, A1      ;omninet data area               0.8

ODC2b10  BSR.S     Set80Rcv            ;setup recv on socket $80        0.8
         BNE.S     ODC2Err             ;error, do end recv              0.8
         BSR       SendWhoDS           ;send who msg                    0.8
         BMI.S     ODC2Err             ;error, do end recv              0.8

         MOVE.L    CPtimout.W, D0      ;wait for response               0.8
ODC2wait MOVE.B    RHpktRC(A1), D7     ;got it?                         0.8
         BEQ.S     ODC2chk             ;yes, check it out               0.8
         SUBQ.L    #1, D0              ;no, dec retry count             0.8
         BNE.S     ODC2wait            ;if not zero test again          0.8

ODC2Err  MOVEQ     #EndOp, D0          ;ERROR - do endrecv              0.8
         MOVE.W    #CIIpSkt, D1        ;  on socket $80                 0.8
         MOVE.W    D7, -(SP)           ;save error code or server host #0.8
         BSR       ODcomnd             ;do end recv                     0.8
         MOVE.W    (SP)+, D7           ;restore error code              0.8
         DBRA      D2, ODC2b10         ;do again if another retry       0.8
ODC2b90  EXT.W     D7                  ;set cc                          0.8
         RTS                           ;EXIT                            0.8

ODC2chk  BSR.S     Set80Rcv            ;setup recv again just in case   0.8
         BNE.S     ODC2Err             ;error, restart                  0.8
         LEA       USRbase.L, A0       ;verify msg is correct type      0.8
         CMPI.W    #C2pid, (A0)        ;Const II pid?                   0.8
         BNE.S     ODC2wait            ;no, wait for correct msg        0.8
         CMPI.W    #C2idmsg, C2Pmsgtp(A0) ;Ident msg type?              0.8
         BNE.S     ODC2wait            ;no, wait for correct msg        0.8
         CMPI.W    #C2dsksrv, C2Pdevtp(A0) ;from a disk server?         0.8
         BEQ.S     ODC2bDS             ;yes, got old disk server        1.0
         CMPI.W    #C2omndsk, C2Pdevtp(A0) ;from a omnidisk?            1.0
         BNE.S     ODC2wait            ;no, wait for correct msg        1.0

ODC2bDS  MOVE.W    C2Psrc(A0), D7      ;get disk server host number     0.8
         LEA       C2Pname(A0), A0     ;move disk server name           0.8
         LEA       CPsrvnam.W, A1      ;  into static memory            0.8
         MOVEQ     #C2PmaxLn-C2Pname-1, D0 ;10 bytes                    0.8
ODC2move MOVE.B    (A0)+, (A1)+        ;                                0.8
         DBRA      D0, ODC2move        ;                                0.8
         CLR.L     D2                  ;clear retry counter to show done0.8
         BRA.S     ODC2Err             ;do end recv and exit            0.8
    page
;
; Set80Rcv - setup a recv on socket 80 for Const II ident msg.          0.8
;                                                                       0.8
;        Enter: A1 - omninet data area address                          0.8
;        Exit:  NE - error                                              0.8
;               EQ - setup worked                                       0.8
;               D7.W - error code                                       0.8
;                                                                       0.8
Set80Rcv LEA       USRbase.L, A0       ;address to receive ident msg    0.8
         MOVE.L    A0, TCdADhi-1(A1)   ;put in transporter cmd block    0.8
         MOVE.W    #C2PmaxLn, TCdtaLN(A1) ;ident msg is 18 bytes        0.8
         CLR.B     TChdrLN(A1)         ;socket $80 has no header area   0.8
         MOVE.W    #RecvOp, D0         ;setup recv                      0.8
         MOVE.W    #CIIpSkt, D1        ;  on socket $80                 0.8
         BSR       ODcomnd             ;do setup                        0.8
         CMPI.B    #CmdAcpt, D7        ;was socket setup?               0.8
         BNE.S     S80Rexit            ;no, report error                0.8
         CLR.L     D7                  ;yes, show no error              0.8
S80Rexit RTS                           ;                                0.8
    page
;
; SendWhoDS - send Const II who are you msg to find disk server.        0.8
;                                                                       0.8
;        Enter: A1 - omninet data area address                          0.8
;        Exit:  MI - error                                              0.8
;               PL - send worked                                        0.8
;               D7.B - error code                                       0.8
;                                                                       0.8
SendWhoDS                              ;                                0.8
         LEA       USRbase+$20.L, A0   ;address to put who msg          0.8
         MOVE.L    WhoMsg, (A0)        ;move who msg from ROM           0.8
         MOVE.L    WhoMsg+4, 4(A0)     ;  to dynamic RAM                0.8
         MOVE.B    CPtprnbr.W, C2Psrc+1(A0) ;put in station's host #    0.8

         MOVE.L    A0, TCdADhi-1(A1)   ;put in adr of msg as data       0.8
         PEA       SHdr(A1)            ;result vector adr               0.8
         MOVE.L    (SP)+, TCrADhi-1(A1) ; put in cmd block              0.8
         MOVE.B    #SendOp, TCop(A1)   ;send msg                        0.8
         MOVE.B    #CIIpSkt, TCsock(A1);  on socket $80                 0.8
         MOVE.W    #C2PwhoLn, TCdtaLN(A1) ;msg length = 8 bytes         0.8
         CLR.B     TChdrLN(A1)         ;socket $80 has no header area   0.8
         MOVEQ     #-1, D0             ;                                0.8
         MOVE.B    D0, TCdest(A1)      ;$FF means broadcast             0.8
         MOVE.B    D0, SHpktRC(A1)     ;$FF means waiting for trnsprtr  0.8

         LEA       TCmd(A1), A2        ;stobe in cmd                    0.8
         BSR       Strobit             ;   block                        0.8
         BNE.S     SWDSexit            ;timed out waiting for Ready     0.8

         MOVE.L    #TOintvl, D0        ;get timeout interval            0.8
SWDSwait MOVE.B    SHpktRC(A1), D7     ;get result code                 0.8
         CMPI.B    #Waiting, D7        ;has Transporter responded?      0.8
         BNE.S     SWDSdone            ;yes, ready to return            0.8
         SUBQ.L    #1, D0              ;see if waited long enough       0.8
         BPL.S     SWDSwait            ;no, look again                  0.8
         MOVEQ     #TimeOut, D7        ;yes, set timeout error          0.8

SWDSdone MOVE.B    D7, CPomniRC.w      ;save OMNINET return code        0.8
SWDSexit RTS                           ;return                          0.8

WhoMsg   DATA.W    C2pid               ;Const II protocol id            0.8
         DATA.W    C2whomg             ;who msg type code               0.8
         DATA.W    0                   ;source host number              0.8
         DATA.W    C2dsksrv            ;disk server host type           0.8
    page
;
; ODdskIO - Read from/write to Corvus disk
;
;       Enter:  A0.L - Buffer address
;               D2.W - Count
;               D5.W - Read ($32) or Write ($33) command
;               D6.W - Destination host number * 256
;
;       Exit:   D7.W - IORESULT (OMNINET/disk controller status)
;
;       All other registers are preserved.
;
ODdskIO movem.l d0-d2/a0-a2,-(sp);Save registers
        move.l  #CPomnibf,a1    ;A1 points to the start of the Data Area
        moveq   #-1, d0         ;get word of $FFFF                      0.8
        cmpi.w  #DskWrit,D5     ;do we want to read or write
        bne.s   ODdsk2          ;read
                                ;
ODdsk1  move.w  D2,SHtoLN(A1)   ;
        move.l  (A0)+,DCmd(A1)  ;move first four bytes of send data to DiskCmd
        move.l  A0,ODwrAD(A1)   ;save address of REST of data
        clr.w   D7              ;force successful IOResult
        move.w  d0, ODvalid(A1) ;mark send buffer as valid...           0.8
        bra.s   ODdsk9          ;return

ODdsk2  cmp.w   ODvalid(A1),d0  ;is send buffer valid? (=$FFFF)         0.8
        beq.s   ODdsk3          ;yes, go on
        moveq   #NoBufr,D7      ;set IOresult to "no buffer" error      0.8
        bra.s   ODdsk6          ;return
                                ;
ODdsk3  clr.w   ODvalid(A1)     ;mark send buffer as invalid
        move.w  D2,SHfmLN(A1)   ;
        subq.w  #1,SHfmLN(A1)   ;subtract one for the return code       0.8
        addq.l  #1,A0           ;inc buffer pointer past return code    0.8
        cmpi.w  #4,SHtoLN(A1)   ;are we doing a longcmd?
        bhi.s   ODdsk4          ;yes
        bsr     ShortCmds       ;no
        bra.s   ODdsk5          ;
ODdsk4  bsr     LongCmds        ;
ODdsk5  subq.l  #1,A0           ;dec buffer pointer past return code    0.8

ODdsk6  ;
        ; the return code must be loaded explicitly since it comes from
        ; the header portion of the results packet....
        ;
        move.b  D7,(A0)         ;stuff return code in read buffer
                                ;
ODdsk9  movem.l (sp)+,d0-d2/a0-a2;Restore registers
        move.b  d7,CPdiskRC.w   ;Save current disk return code          0.7
        ext.w   d7              ;Set return condition code
        rts                     ;Return
    page
;                                                                       0.7
; EndRecv -- end receive on disk server socket                          0.7
;                                                                       0.7
EndRecv movem.l d0-d1,-(sp)             ;save registers                 0.7
        moveq   #EndOp,d0               ;set command to ENDRECV         0.8
        move.w  #CnstSkt,d1             ;set socket number              0.7
        bsr     ODcomnd                 ;do ENDRECV on disk srvr socket 0.7
        movem.l (sp)+,d0-d1             ;restore registers              0.7
        moveq   #0,d7                   ;set no error IOresult          0.7
        rts                             ;return                         0.7
    page
;
; StrobIt -- Strobe command address to Transporter
;
;       Enter:  A2 = command address
;
;       Exit:   D7 = Transporter strobe status
;
;               EQ = successful
;               NE = Transporter not responding
;
;       All other registers are preserved
;
StrobIt movem.l D0-D1/A0,-(sp)  ;save registers                0.8
        lea     RdyAdr.L,A0     ;get address of ready flag     0.8
        clr.l   D7              ;assume no Transporter error   0.8
        move.l  A2,D0           ;get command address
        rol.l   #8,D0           ;move command address to msb
                                ;
        bsr.s   SBstrob         ;strobe address HI
        beq.s   SBerr           ;
        bsr.s   SBstrob         ;strobe address MED
        beq.s   SBerr           ;
        bsr.s   SBstrob         ;strobe address LO
        beq.s   SBerr           ;
        bsr.s   SBwait          ;wait for Transporter ready
        bne.s   SBexit          ;
                                ;
SBerr   moveq   #NoTrans,d7     ;no transporter ...
                                ;
SBexit  movem.l (sp)+,D0-D1/A0  ;restore registers              0.8
        tst.w   d7              ;set return condition code
        rts                     ;return
                                ;
SBstrob rol.l   #8,D0           ;shift address byte to lsb
        move.b  D0,StrAdr-RdyAdr(A0) ;strobe address            0.8
SBwait  move.l  #TOintvl,D1     ;get timeout interval           0.8
SBW1    btst    #0, (A0)        ;is transporter ready?
        bon.s   SBWchk          ;yes, return                    0.8
        subq.l  #1, D0          ;see if waited long enough      0.8
        bne.s   SBW1            ;no, look again                 0.8
        bra.s   SBWexit         ;timed out                      0.8
SBWchk  nop                     ;check for glitch in ready      0.8
        btst    #0, (A0)        ;is transporter ready?          0.8
        boff.s  SBW1            ;no, then continue to wait      0.8
SBWexit rts                     ;return
    page
;
; SetGo -- set up a receive for the 'GO' packet
;
SetGo   move.w  #2,TCdtaLN(A1)          ;2 bytes of data
        clr.b   TChdrLN(A1)             ;no header
        pea     GData(A1)               ;get address of data area
        move.l  (SP)+,TCdADhi-1(A1)     ;load data buffer address -- TCsock destroyed
        bra.s   SetGo1                  ;
;
; SetRecv -- set up a receive for the disk results and read data
;            returns result in D0
;
SetRecv move.l  A0,TCdADhi-1(A1)        ;load data buffer address -- TCsock destroyed
        move.w  SHfmLN(A1),TCdtaLN(A1)  ;
        move.b  #3,TChdrLN(A1)          ;disk results have a hdr len of 3
                                        ;
SetGo1  move.b  #Waiting,RHpktRC(A1)    ;set result to FF to see it change
                                     ;prepare the command vector
        pea     RHdr(A1)                ;load result vector address
        move.l  (SP)+,TCrADhi-1(A1)     ; -- TCop destroyed
        move.b  #RecvOp,TCop(A1)        ;set up a receive
        move.b  #CnstSkt,TCsock(A1)     ;  on socket B0
                                        ;
        lea     TCmd(A1),A2             ;get command address
        bsr     StrobIt                 ;strobe command address to Transporter
        bne     SCerr2                  ;Transporter not responding

        move.l  #TOintvl,D0             ;for time out                   0.8
SC10    cmpi.b  #Waiting,RHpktRC(A1)
        bne.s   SC12                    ;wait till result changes
        subq.l  #1, D0                  ;see if waited long enough      0.8
        bne.s   SC10                    ;no, look again                 0.8
        bra     SCerr3                  ;timeout error

SC12    move.b  RHpktRC(A1),d0          ;get Transporter return code
        cmpi.b  #CmdAcpt,d0             ;was command accepted?
        blt     SCexit                  ;no, fatal error
        clr.w   D0                      ;indicate success
        bra     SCexit                  ;return
    page
; SndRest -- send the rest of the data (from long command) to the disk server
;            result of call is in D0, 0 = success
;
SndRest move.l  ODwrAD(A1),TCdADhi-1(A1);load data buffer address
        move.b  #RestSkt,TCSock(A1)     ;    - TCsock destroyed
        subq.w  #4,SHtoLN(A1)           ;                               0.8
        bge.s   SC20                    ;
        clr.w   SHtoLN(A1)              ;result was negative, make it zero
SC20    move.w  SHtoLN(A1),TCdtaLN(A1)  ;send length - 4 bytes
        clr.b   TChdrLN(A1)             ;no header for rest packets
        bra.s   SC40                    ;send it

; SndCmds -- send a disk command to the disk server
;            result of call is in D0, 0 = success
;
SndCmds pea     DCmd(A1)                ;data is the Disk command
        move.l  (SP)+,TCdADhi-1(A1)     ;load data buffer address
        move.b  #CnstSkt,TCsock(A1)     ;     - TCsock destroyed
        moveq   #4, D0                  ;4 is data and header lengt     0.8
        move.w  D0,TCdtaLN(A1)          ;disk command is 4 bytes long   0.8
        cmp.w   SHtoLN(A1), D0          ;are we sending less than 4 bytes
        ble.s   SC30                    ;no (dest <= src)
        move.w  SHtoLN(A1),TCdtaLN(A1)  ;less
SC30    move.b  D0,TChdrLN(A1)          ;send header is 4 bytes         0.8
        ror.w   #8,d6                   ;set destination host number
        move.b  d6,TCdest(A1)
        rol.w   #8,d6

SC40    move.b  #Waiting,SHpktRC(A1)    ;set result to FF to see it change
        pea     SHdr(A1)                ;load result vector address
        move.l  (SP)+,TCrADhi-1(A1)     ;          -- TCop destroyed
        move.b  #SendOp,TCop(A1)        ;sendmsg opcode
        lea     TCmd(A1),A2             ;get command address
        bsr     StrobIt                 ;strobe command address to Transporter
        bne.s   SCerr2                  ;Transporter not responding

SC60    move.b  SHpktRC(A1), D7         ;get send result code           0.8
        cmpi.b  #Waiting, D7            ;still waiting?                 0.8
        beq.s   SC60                    ;yes, check status again        0.7
        cmpi.b  #GaveUp, D7             ;did disk srvr accept message?  0.8
        beq.s   SC40                    ;no, try again                  0.7
        cmpi.b  #NoSockt, D7            ;did disk srvr accept message?  0.8
        beq.s   SC40                    ;no, try again                  0.8

        clr.w   D0                      ;indicate success               0.7
        tst.b   D7                      ;did it work?                   0.8
        bge.s   SCexit                  ;yes, return                    0.7
SCerr2  move.b  D7,D0                   ;no transporter ...             0.8
        bra.s   SCexit                  ;return
SCerr3  moveq   #TimeOut,D0             ;time out ...
SCexit  ext.w   D0                      ;make return code a word
        rts                             ;return
    page
LongCmds

; 1. set up a receive for the GO message
;
        bsr     SetGo           ;
        blt.s   LcmdErr         ;if D0 < 0 then fatal DRW error
;
; 2. send disk command
;
        bsr     SndCmds         ;doit
        blt.s   LcmdErr         ;if D0 < 0 then fatal DRW error
;
; 3. wait to receive GO
;
Lcmd1   move.b  RHpktRC(A1),d0  ;get Transporter return code
        cmpi.b  #CmdAcpt,d0     ;has return code changed?
        beq.s   Lcmd1           ;no, wait some more
        tst.b   D0              ;successful receive?
        blt.s   LcmdErr         ;no, set error return
;
; 4. validate GO packet
;
Lcmd3   move.b  TCdest(A1),D0   ;response come from the right place?    0.7
        cmp.b   RHsor(A1),D0    ;*                                      0.7
        bne.s   Lcmd4           ;no, setup receive again                0.7
        btst    #7,Gdata(A1)    ;disk server restart?                   0.7
        bon.s   LongCmds        ;yes, start request over                0.7
        cmpi.w  #'GO',Gdata(A1) ;"GO" command?                          0.7
        beq.s   Lcmd5           ;yes, go on                             0.7
                                ;
Lcmd4   bsr     SetGo           ;set up for GO receive again
        blt.s   LcmdErr         ;
        bra.s   Lcmd1           ;

        page
;
; 5. set up receive for results
;
Lcmd5   bsr     SetRecv         ;
        blt.s   LcmdErr         ;if D0 < 0 then fatal DRW error
;
; 6. send REST
;
        bsr     SndRest         ;
        blt.s   LcmdErr         ;
;
; 7. wait for results
;
Lcmd6   move.b  RHpktRC(A1),d0  ;get Transporter return code
        cmpi.b  #CmdAcpt,d0     ;has return code changed?
        beq.s   Lcmd6           ;no, wait some more
        tst.b   D0              ;successful receive?
        blt.s   LcmdErr         ;no, set error return
;
; 8. validate results
;
Lcmd7   move.b  TCdest(A1),D0   ;response come from the right place?    0.7
        cmp.b   RHsor(A1),D0    ;*                                      0.7
        beq.s   Lcmd8           ;yes                                    0.7
        bsr     SetRecv         ;no, set up receive again....           0.7
        blt.s   LcmdErr         ;if D0 < 0 then fatal DRW error         0.7
        bra.s   Lcmd6           ;go back and wait again...              0.7
                                ;                                       0.7
Lcmd8   btst    #7,RHdskLN(a1)  ;disk server restart?                   0.7
        bon.s   LongCmds        ;yes, start request over                0.7
                                ;no, done with request                  0.7
                                ;                                       0.7
LcmdOK  move.b  RHdskRC(A1),D0  ;get disk server return code            0.7
                                ;                                       0.7
LcmdErr
        move.w  #$1FFF,d7       ;--UP- wait a little                    0.8
lc1err1 dbra    d7,lc1err1      ;--UP-    Why???? a bug                 0.8
        bsr     EndRecv         ;end receive on disk server socket      0.7
        move.b  D0,D7           ;get error return code                  0.7
        ext.w   D7              ;make return code a word                0.7
        rts                     ;return for ShortCmds or LongCmds       0.7
    page
ShortCmds
;
; 1. set up a receive for the results
;
        bsr     SetRecv         ;
        blt.s   LcmdErr         ;if D0 < 0 then fatal DRW error
;
; 2. send disk command to disk server
;
        bsr     SndCmds         ;doit
        blt.s   LcmdErr         ;if D0 < 0 then fatal DRW error
;
; 3. wait to receive results
;
Scmd2   move.l  CPtimout.w,d7   ;get timeout value                      0.7
Scmd2A  move.b  RHpktRC(A1),d0  ;get Transporter return code            0.7
        bge.s   Scmd3           ;successful receive, go on
        cmpi.b  #CmdAcpt,d0     ;has return code changed?
        bne.s   LcmdErr         ;yes, set error return
        subq.l  #1,d7           ;time out?
        bne.s   Scmd2A          ;no, wait some more                     0.7
        bra.s   LcmdErr         ;set error return
;
; 4. validate results
;
Scmd3   move.b  RHsor(A1),D7    ;get source of response
        cmpi.b  #$FF,TCdest(a1) ;is this a broadcast?
        bne.s   Scmd4           ;no, go on
        move.b  d7,RHdskRC(A1)  ;save disk server number
        bra.s   LcmdOk          ;return
                                ;
Scmd4   cmp.b   TCdest(A1),D7   ;response come from the right place?
        bne.s   Scmd5           ;no                                     0.7
        btst    #7,RHdskLN(a1)  ;disk server restart?                   0.7
        bon.s   ShortCmds       ;yes, start request over                0.7
        bra.s   LcmdOK          ;no, done with request                  0.7
                                ;                                       0.7
Scmd5   bsr     SetRecv         ;set up receive again....               0.7
        blt.s   LcmdErr         ;if D0 < 0 then fatal DRW error         0.7
        bra.s   Scmd2           ;go back and wait again...              0.7
    list 0
; ----  page    ;p.prom.ad              ;                               0.7
; ----  include 'P.PROM.AD'             ;Apple floppy driver            0.7
        page    ;p.prom.fb              ;                               0.7
;
; file: cc.prom.fe.text
; date: 13-June-1983
; Floppy driver equates for the new controller.

; BIT NUMBER DEFINITIONS
;
BITD0    EQU       0                   ;BIT 0
BITD1    EQU       1                   ;BIT 1
BITD2    EQU       2                   ;BIT 2
BITD3    EQU       3                   ;BIT 3
BITD4    EQU       4                   ;BIT 4
BITD5    EQU       5                   ;BIT 5
BITD6    EQU       6                   ;BIT 6
BITD7    EQU       7                   ;BIT 7

; Default device table values  - for init procedure
;
D8TYPE   EQU       $FF                 ;Bits D0 and D1, single density & 1 side
D8TRKSID EQU        77                 ;8" standard = 77 tracks per side
D8SECTRK EQU       $1A                 ;8" standard = 26 sectors per track
D8SctrLn EQU       128                 ;default sector length for FM ??127??

D5TYPE   EQU       $FC                 ;Bits D0 and D1, double density & 2 sides
D5TRKSID EQU        80                 ;80 tracks per side
D5SECTRK EQU         9                 ;9 sectors per track
D5SctrLn EQU       512                 ;default sector length for double density

; device table entry, drive type field(UTtyp) bit definitions
;
TYPdnsty EQU       BITD0               ;1=FM, 0=MFM
TYPsides EQU       BITD1               ;1=only SIDE 0, 0=has a SIDE 1

; sector sizes - stored in UTblk+2 (word)
;
SLfmLow  EQU       128                 ;minimum FM sector size
SLmfmLow EQU       256                 ;minimum MFM sector size
  page
;
; TIMER INTERRUPT VECTOR ADDRESS
;
VECTOR5  EQU       $000074             ;INTERRUPT VECTOR #5

; VIA ADDRESS and offsets
;
VIAstart EQU       $30F61              ;base address of VIA
viaT1CL  EQU       $30F69-VIAstart     ;TIMER 1 COUNTER LOW - READ ONLY
viaT1CH  EQU       $30F6B-VIAstart     ;TIMER 1 COUNTER HIGH
viaT1LL  EQU       $30F6D-VIAstart     ;TIMER 1 LATCH LOW
viaT1LH  EQU       $30F6F-VIAstart     ;TIMER 1 LATCH HIGH
viaT2LL  EQU       $30F71-VIAstart     ;TIMER 2 LATCH LOW
viaT2CH  EQU       $30F73-VIAstart     ;TIMER 2 COUNTER HIGH
viaACR   EQU       $30F77-VIAstart     ;AUXILLARY CONTROL REGISTER
viaIFR   EQU       $30F7B-VIAstart     ;INTERRUPT FLAGS REGISTER
viaIER   EQU       $30F7D-VIAstart     ;INTERRUPT ENABLE REGISTER

; VIA REGISTER VALUES
;
ACRBYTE  EQU       $40                 ;ACR DATA - T1 FREE RUN DISABLE PB7
RUNT2    EQU       $10                 ;MASK TO COUNT DOWN T2
STOPT2   EQU       $EF                 ;COMPLEMENTED RUNT2 TO STOP T2
DISABL   EQU       $7F                 ;DISABLE ALL INTERRUPTS
ENBLT1   EQU       $C0                 ;ENABLE IRQ FOR T1
CLEAR    EQU       $FF                 ;CLEAR ALL IFR STAT BITS
T2INT    EQU       $20                 ;TIMER #2 INTERRUPT FLAG BIT
TIME     EQU       50000               ;50,000 MICRO SECONDS
TIMEH    EQU       TIME/256            ;HI ORDER BYTE OF TIME VALUE
TIMEL    EQU       TIME-(TIMEH*256)    ;LOW ORDER BYTE OF TIME VALUE
  page

; I/O error codes
;
IOEcrcer  EQU      -1        ;CRC error
IOEioreq  EQU      -3        ;invalid I/O request
IOEnebhrd EQU      -4        ;nebulous hardware err code- indeterminable from err
IOEoffln  EQU      -5        ;drive went off line, lost ready during cmd
IOEwrprot EQU     -16        ;write protected
IOEunknwn EQU     -64        ;unknown hardware err code- a severe hardware or a software err

IOEinvblk EQU     -18        ;invalid block number
IOEflpto EQU      -24        ;new timeout error code
IOEnoT0  EQU      -25        ;can't restore to track 0
IOEnfmtd EQU      -26        ;can't read/write diskette, maybe not formatted
IOEinvsct EQU     -27        ;invalid sector length error
IOEwrngC EQU      -28        ;read the wrong track?
IOEbdtrk EQU      -29        ;track marked as bad (IBM spec)
  page
; Floppy Disk Controller (FDC) register indices - includes FDC chip and card
;
FDCdata  EQU       0                   ;FDC data register - on FDC chip
FDCstat  EQU       2                   ;FDC status register - on FDC chip
FDCcntr  EQU       4                   ;Controller card buffer address counter
FDCbffr  EQU       6                   ;Controller card buffer
FDClocl  EQU       8                   ;Controller card local command(write) & status(read)

; FDC status register bit definitions
;
Drv0BUSY EQU       BITD0               ;drive 0 busy
Drv1BUSY EQU       BITD1               ;drive 1 busy
Drv2BUSY EQU       BITD2               ;drive 2 busy
Drv3BUSY EQU       BITD3               ;drive 3 busy
FDCBUSY  EQU       BITD4               ;read or write cmd in progress
NDMAmode EQU       BITD5               ;Non-DMA mode operation
DataDir  EQU       BITD6               ;data direction, 1=read
FDCready EQU       BITD7               ;ready to read/write data reg

; Controller card local command register bit definitions
;
DevSel0  EQU       BITD0               ;device select bit 0
DevSel1  EQU       BITD1               ;device select bit 1
DvSenbl  EQU       BITD2               ;device select enable
LCMtrOF  EQU       BITD3               ;motor off for 5 1/4" floppy only
IRQenbl  EQU       BITD5               ;IRQ enable, allows card to interrupt 68K
DMARWdir EQU       BITD6               ;DMA direction, 1=controller read buffer
ResetFDC EQU       BITD7               ;reset the FDC

; masks for local command register
;
EnblDvS  EQU       $04                 ;enable device select
DisblDS  EQU       $00                 ;disable device select
MMtrON   EQU       EnblDvS             ;dev sel enbl, motor on, not reset
DMARead  EQU       $40                 ;set DMA for read all other bits clear
DMAWrite EQU       $00                 ;set DMA for write all other bits clear
MMtrOFF  EQU       DMARead+DisblDS+$08 ;motor off, disable device select, dma read
MRstFDC  EQU       $80+DisblDS         ;reset FDC, don't select device

; Controller card local status register bit definitions
;
LSdskchng EQU      BITD0               ;1=disk has been changed since last dev deselect
LSNirq   EQU       BITD1               ;0=irq has occured
LSNdrRdy EQU       BITD6               ;0=FDD(drive) is ready
LSeight  EQU       BITD7               ;1=8 inch disk drive
  page
; Internal data indices
; each slot has own data area but all drives on a slot have same parameters
;
StepRate EQU       0                   ;step rate in ms.   {1..16}
RdyTo    EQU       StepRate+2          ;timeout counter for ready wait
FBflags  EQU       RdyTo+4             ;flags
IDataLen EQU       FBflags+2           ;data area length

; FBflags - bit flag definitions
;
InDrvr   EQU       BITD0               ;in driver flag
FBmoff   EQU       BITD1               ;motor on flag

; temporary data indices and lengths - allocated on stack
;
CmdRslt  EQU       0                   ;command/result array
CmdRsltLen EQU     10                  ;max. # of bytes in command/result array

;must be on even word boundary and must be in this order
;
Ptrack   EQU       CmdRslt+CmdRsltLen  ;physical track number
Pside    EQU       Ptrack+1            ;side number

DrvNumb  EQU       Pside+1             ;drive number
CurTrack EQU       DrvNumb+1           ;current logical track
LocalFrame EQU     CurTrack+1          ;# of bytes in local stack frame
                                       ;must be on even number
  page
;
; FDC command definitions

; masks common for many commands
;
Mside0   EQU       $00                 ;mask for side 0 select
Mside1   EQU       $04                 ;mask for side 1 select
Mmfmbit  EQU       $40                 ;mask for MFM in command byte
Mfmbit   EQU       $00                 ;mask for FM in command byte

; common result array indices
;
ST1      EQU       $01                 ;status register 1
ST2      EQU       $02                 ;status register 2
RSLTsctrl EQU      $06                 ;sector length code

; common indices into command vector for read and write
;
RWcmdlen EQU       $09                 ;command array is 9 bytes
RSLTrdwr EQU       $07                 ;result array length is 7 bytes

; specify command
;                    D7 D6 D5 D4 D3 D2 D1 D0  |
; 3 bytes : byte 1 =  0  0  0  0  0  0  1  1  |  command byte
;           byte 2 = <--SPT----> <---HUT--->  |  SPT=step rate, HUT=head unload time
;           byte 3 = <-----HLT----------> ND  |  HLT=head load time, ND=Non-DMA mode
;
; NO result array
;
CMDspcfy EQU       $03                 ;specify command byte

; command array codes for specify command
HUTcode  EQU       $01                 ;head unload time code, = 16 ms.
;must include head settle time in HLT parameter
HLT5code EQU       $32                 ;head load time code, pre-shifted, = 50ms.
HLT8code EQU       $02                 ;head load time code, pre-shifted, = 16ms.
NDMAcode EQU       $00                 ;NOT DMA bit = run in DMA mode

; sense interrupt status command
;                    D7 D6 D5 D4 D3 D2 D1 D0  |
; 1 byte  : byte 1 =  0  0  0  0  1  0  0  0  |  command byte
;
; result array
; 2 bytes : byte 1 = <--------ST0---------->  |  ST0=status register 0
;           byte 2 = <---------C----------->  |  C=current track(cylinder)
;
CMDsnsint EQU      $08                 ;sense interrupt status command byte

RSLTsnsint EQU     $02                 ;number of bytes in result array
  page
; sense drive status command
;                    D7 D6 D5 D4 D3 D2 D1 D0  |
; 2 bytes : byte 1 =  0  0  0  0  0  1  0  0  |  command byte
;           byte 2 =  0  0  0  0  0 HD DS1DS0 |  DS1=device select bit 1,DS0=dev select bit 0
;                                                HD=head select
; result array
; 1 bytes : byte 1 = <--------ST3---------->  |  ST3=status register 3
;
CMDsnsdrv EQU      $04                 ;sense drive status command byte

RSLTsnsdrv EQU     $01                 ;number of bytes in result array
;
; recalibrate command  (restore drive to track 0)
;                    D7 D6 D5 D4 D3 D2 D1 D0  |
; 2 bytes : byte 1 =  0  0  0  0  0  1  1  1  |  command byte
;           byte 2 =  0  0  0  0  0  0 DS1DS0 |  DS1=device select bit 1,DS0=dev select bit 0
;
; NO result array
;
CMDrclbrt EQU      $07                 ;recalibrate command byte

RTRYrstr EQU       2                   ;number of retries allowed

; seek command
;                    D7 D6 D5 D4 D3 D2 D1 D0  |
; 2 bytes : byte 1 =  0  0  0  0  1  1  1  1  |  command byte
;           byte 2 =  0  0  0  0  0 HD DS1DS0 |  DS1 & DS0=device select bits, HD=head select
;
; NO result array
;
CMDseek  EQU       $0F                 ;seek command byte

RTRYseek EQU         1                 ;number of retries allowed

; read ID command
;                    D7 D6 D5 D4 D3 D2 D1 D0  |
; 2 bytes : byte 1 =  0MFM  0  0  1  0  1  0  |  command byte,MFM=MFM/FM flag
;           byte 2 =  0  0  0  0  0 HD DS1DS0 |  DS1 & DS0=device select bits, HD=head select
;
; result array
; 7 bytes : byte 1 = <--------ST0---------->  |  ST0=status register 0
;           byte 2 = <--------ST1---------->  |  ST1=status register 1
;           byte 3 = <--------ST2---------->  |  ST2=status register 2
;           byte 4 = <---------C----------->  |  C=current track(cylinder)
;           byte 5 = <---------H----------->  |  H=side
;           byte 6 = <---------R----------->  |  R=sector number
;           byte 7 = <---------N----------->  |  N=sector length code
;
CMDredID EQU       $0A                 ;read ID command byte

RSLTredID EQU      $07                 ;number of bytes in result array
  page
; read sector command
;                    D7 D6 D5 D4 D3 D2 D1 D0  |
; 9 bytes : byte 1 = MTMFM  0  0  0  1  1  0  |  command byte,MT=multitrack,MFM=MFM/FM flag
;           byte 2 =  0  0  0  0  0 HD DS1DS0 |  DS1 & DS0=device select bits, HD=head select
;           byte 3 = <---------C----------->  |  C=current track(cylinder)
;           byte 4 = <---------H----------->  |  H=side
;           byte 5 = <---------R----------->  |  R=sector number
;           byte 6 = <---------N----------->  |  N=sector length code
;           byte 7 = <--------EOT---------->  |  EOT=last sector number on track
;           byte 8 = <--------GPL---------->  |  GPL=gap 3 length
;           byte 9 = <--------DTL---------->  |  DTL=special sector size
;
; result array -> SAME as read ID command
;
CMDread  EQU       $06                 ;read sector command byte

RSLTread EQU       $07                 ;number of bytes in result array

RTRYread EQU       $02                 ;do another read with other density
                                       ;and again with same density (make sure of error
; write sector command
;                    D7 D6 D5 D4 D3 D2 D1 D0  |
; 9 bytes : byte 1 = MTMFM  0  0  0  1  0  1  |  command byte,MT=multitrack,MFM=MFM/FM flag
;           byte 2 =  0  0  0  0  0 HD DS1DS0 |  DS1 & DS0=device select bits, HD=head select
;           byte 3 = <---------C----------->  |  C=current track(cylinder)
;           byte 4 = <---------H----------->  |  H=side
;           byte 5 = <---------R----------->  |  R=sector number
;           byte 6 = <---------N----------->  |  N=sector length code
;           byte 7 = <--------EOT---------->  |  EOT=last sector number on track
;           byte 8 = <--------GPL---------->  |  GPL=gap 3 length
;           byte 9 = <--------DTL---------->  |  DTL=special sector size
;
; result array -> SAME as read ID command
;
CMDwrite EQU       $05                 ;read sector command byte

RSLTwrite EQU      $07                 ;number of bytes in result array

RTRYwrite EQU      $02                 ;do another write with other density
                                       ;and again with same density
  page
; FDC result byte definitions
; values for ST0 - status register 0
;
ST0ds0   EQU       BITD0               ;device select bit 0
ST0ds1   EQU       BITD1               ;device select bit 1
ST0hda   EQU       BITD2               ;head address
ST0nrdy  EQU       BITD3               ;not ready
ST0equp  EQU       BITD4               ;equipment check
ST0skend EQU       BITD5               ;seek end
ST0ic0   EQU       BITD6               ;interrupt code bit 0
ST0ic1   EQU       BITD7               ;interrupt code bit 1

; Masks for ST0 bits
;
MIntCod  EQU       $C0                 ;mask for interupt codes only
MABstp   EQU       $40                 ;mask for abnormal termination, cmd not finished
MABrdy   EQU       $C0                 ;mask for abnormal termination, drive changed ready
MInvCod  EQU       $80                 ;mask for invalid code
MSkEnd   EQU       $20                 ;mask for good seek and restore end
MRstEnd  EQU       MIntCod+MSkEnd      ;ST0 bits for Restore end

; bit flag definitions for ST1 - status register 1
;
ST1nadrm EQU       BITD0               ;missing address mark (MA)
ST1wrpro EQU       BITD1               ;write protect error (NW)
ST1snfnd EQU       BITD2               ;sector not found (ND)
; bit D3 not used always low (0)
ST1ovrrn EQU       BITD4               ;overrun (OR)
ST1dataer EQU      BITD5               ;data error bit - CRC (DE)
; bit D6 not used always low (0)
ST1endtr EQU       BITD7               ;End of track (EN)

; bit flag definitions for ST1 - status register 1
;
ST2ndtam EQU       BITD0               ;missing data address mark
ST2bdtrk EQU       BITD1               ;bad track - cylinder address = FF
ST2Nscan EQU       BITD2               ;scan not satisfied
ST2scnHt EQU       BITD3               ;scan hit, condition satisfied
ST2wrngC EQU       BITD4               ;cylinder address error
ST2dtaer EQU       BITD5               ;crc error in data field
ST2ctlmk EQU       BITD6               ;control mark
; bit D7 not used always low (0)

; bit flag definitions for ST3 - status register 3
;
ST3ds0   EQU       BITD0               ;device select bit 0
ST3ds1   EQU       BITD1               ;device select bit 1
ST3hda   EQU       BITD2               ;head address
ST32sid  EQU       BITD3               ;2 sided
ST3trk0  EQU       BITD4               ;head at track 0
ST3rdy   EQU       BITD5               ;drive ready
ST3wrpr  EQU       BITD6               ;write protected
ST3falt  EQU       BITD7               ;drive fault
  page
; timeout values   **** redo values ****
;
FDCTimeOut EQU     $0FFFF              ;time out waiting for FDC chip to
                                       ;accept send or rcv of data
FDDTimeOut EQU     $10000              ;time out waiting for drive to come READY
CMDTimeOut EQU     $100000             ;time out waiting for command to finish
;
; File: CC.PROM.FB.TEXT
; Date: 13-June-83
;       Keith Ball
;
; Floppy driver for the new controller.
;
;
; ALL REGISTER VALUES ON ENTRY ARE SAVED AND RESTORED EXCEPT D0, D7 & A0
; INTERNAL REGISTER USEAGE :
;
;        D0 = temporary register
;        D1 = temporary register
;        D2 = count of bytes user wants then number of bytes in
;              partial sector - if no partial then = sector size
;        D3 = temporary register
;        D4 = # of sectors to read/write (loop counter)
;        D5 = starting block then current logical sector number
;        D6 = count of bytes in command or result array
;        D7 = current IOresult code
;
;        A0 = temporary register
;        A1 = temporary register
;        A2 = pointer to local temporary variables (even word boundary)
;        A3 = pointer to user's buffer or parameter block
;        A4 = workstaton ram pointer
;        A5 = Slot base address
;        A6 = Static ram Base pg pointer - local data
;
  page
;
;       FBboot -- Rev B floppy disk boot processing
;
FBboot
        move.b  d0,CPbtslot.w       ;set boot slot number
        move.b  d0,CPosslot.w       ;set OS slot number
        clr.b   CPbtsrvr.w          ;set boot server number
        pea     FBblkIO             ;set boot disk blk i/o subr pointer
        move.l  (sp)+,CPblkio.w     ;*
        pea     FBsctIO             ;set boot disk sector i/o subr pointer
        move.l  (sp)+,CPdskio.w     ;*
        moveq   #0,d0               ;
        move.b  d0,CPossrvr.w       ;set OS server number
        move.w  d0,CPosblk+1.w      ;set OS volume block number
        move.b  d0,CPosdrv.w        ;set OS volume drive number
        clr.l   d1                  ;access drive 0
        bsr     FBinit              ;initialize floppy drive
        blt.s   FBboot9             ;just return if error
                                    ;
FBboot1 move.l  #USRbase,a0         ;get block buffer pointer
        moveq   #0,d0               ;1st boot block
        move.w  d0,d1               ;drive number
        moveq   #DskRead,d5         ;get read block function code
                                    ;
        bsr.s   FBblkIO             ;read block 0 of boot code
        blt.s   FBboot9             ;just return if error
        bsr.s   FBblkIO             ;read block 1 of boot code
        blt.s   FBboot9             ;just return if error
        move.l  #USRbase,a0         ;get block buffer pointer
                                    ;
FBboot9 rts                         ;return
  page
;
; FBblkIO - Read a Rev B floppy controller disk block subroutine
;
;       Enter:  A0.L - Buffer address
;               D0.W - Block number
;               D1.W - Drive number
;               D5.W - Read ($32) or Write ($33) command
;
;       Exit:   A0.L - Next free location in buffer
;               D0.W - Updated block number
;               D7.W - IORESULT
;
;       All other registers are preserved.
;
FBblkIO  MOVEQ     #IOEioreq, D7                 ;assume not read
         CMPI.W    #DskRead, D5                  ;is it a read?
         BNE.S     FBBerror                      ;no, exit

         CLR.L     D7
         MOVEM.L   D0-D6/A1-A6,-(SP)             ;setup regs for driver
         MOVE.L    A0, A3                        ;put buffer adr and
         MOVE.W    D0, D5                        ;block number in right regs
         MOVE.W    #BLKSZ, D2                    ;read 1 block
         SUBA.W    #LocalFrame, SP               ;allocate local stack frame
         MOVE.L    SP, A2                        ;must be even # of bytes
         MOVE.B    D1, DrvNumb(A2)               ;save drive number

; Get data Ram(A6), Slot(A5), and workstation Ram(A4) pointers
;
         BSR       FBbaseAdr

         BSR       FBread                        ;
         ADDA.W    #LocalFrame, SP               ;release local stack frame
         MOVE.L    A3, A0                        ; save next free location
         MOVEM.L   (SP)+,D0-D6/A1-A6             ;restore registers
         ADDQ.W    #1,D0                         ;INC BASE BLOCK
FBBerror TST.W     D7                            ;set return condition code
         RTS
  page
;
; FBsctIO - Read a Corvus floppy disk sector
;
;       Enter:  A0.L - Buffer address
;               D1.W - Drive number
;               D3.B - Track number
;               D4.B - Sector number
;               D5.W - Read ($32) or Write ($33) command
;               D6.B - side number (0 or 1)
;
;       Exit:   D7.W - IORESULT
;
;       All other registers are preserved.
;
FBsctIO  MOVEM.L   D0-D6/A0-A6,-(SP)             ;save registers
         MOVE.L    A0, A3                        ;put buffer adr in right reg
         SUBA.W    #LocalFrame, SP               ;allocate local stack frame
         MOVE.L    SP, A2                        ;must be even # of bytes
         MOVE.B    D1, DrvNumb(A2)               ;save drive number
         MOVE.B    D3, Ptrack(A2)                ;save track number
         MOVE.B    D6, Pside(A2)                 ;save side
         MOVE.L    D4, D5                        ;keep sector number

; Get data Ram(A6), Slot(A5), and workstation Ram(A4) pointers
;
         BSR       FBbaseAdr                     ; on Entry: D0 = unit number

         MOVEQ     #DMARead, D0                  ;use dma in read direction
         BSR       FBMtrON                       ;turn on motor and setup controller

; check for disk change
;
         BSR       FBdChng
         BNE.S     FBsio9                        ;I/O error, turn motor off & exit

         BSR       FBseek                        ;get to track
         BNE.S     FBsio9                        ;if error, return

FBsio2   BSR       FBsecRd                       ;read sector
         BNE.S     FBsio9                        ;failed
         BSR       FBmvOUT                       ;move data to user's space

FBsio9   BSR       FBmtrOFF                      ;turn off motor
         ADDA.W    #LocalFrame, SP               ;release local stack frame
         MOVEM.L   (SP)+,D0-D6/A0-A6             ;restore registers
         TST.W     D7                            ;set return condition code
         RTS
  page
; FBinit - init device ram and internal variables.
;          initialize controller
;        Entry: D1.W - Drive number
;
; Get local data(A2), data Ram(A6), Slot(A5), and workstation Ram(A4) pointers
;
FBinit   SUBA.W    #LocalFrame, SP               ;allocate local stack frame
         MOVE.L    SP, A2                        ;must be even # of bytes
         MOVE.B    D1, DrvNumb(A2)               ;save drive number
         BSR       FBbaseAdr

; Put default values in device table entry
;
         BSR.S     FBsltinit

; Bug in FDC - must specify step rate 1 ms. faster than want it
;
         MOVE.W    #4, (A6)                      ;3 millisecond step rate
         MOVE.L    #FDDTimeOut, RdyTO(A6)        ;init ready timeout counter
         CLR.W     FBflags(A6)                   ;clear all flags
         BSR.S     FBctlinit                     ;init controller
         BNE.S     FBIerror                      ;if failed try to recover

; restore
;
         MOVEQ     #DMARead, D0                  ;use dma in read direction
         BSR       FBMtrON                       ;turn on motor and wait for Read
         BNE.S     FBIexit1                      ;timed out waiting for ready
         BSR       FBrstor                       ;restore to track 0
         BNE.S     FBIerror                      ;failed, D7 has error code

; do read id and sense drive status to get disk and drive info
;
         BSR       FBdrvInfo                     ;assumes motor is on

FBIexit1 BSR       FBmtrOFF                      ;turn off motor and deselect dri
FBIexit  ADDA.W    #LocalFrame, SP               ;release stack frame
         TST.W     D7
         RTS

; possible serious I/O error reset chip again to try to recover
;
FBIerror BSR.S     FBctlinit                     ;init controller also turns off
         BEQ.S     FBIexit                       ;if ok exit
         BSR.S     FBintFDC                      ;else reset controller and leave
         BRA.S     FBIexit

; FBctlinit - init the FDC and do a Specify command
;
FBctlinit
         BSR.S     FBintFDC                      ;reset the FDC
         BRA       FBspecify                     ;do a specify command
  page
;
;FBintFDC - reset the floppy disk controller
;        Entry : (A5) = Slot base address
;
FBintFDC MOVE.B    #MRstFDC, FDClocl(A5)         ;reset FDC
         MOVE.B    #MMtrOFF, FDClocl(A5)         ;set it back to normal
         RTS

; FBsltinit - init variables based on floppy type
;
FBsltinit
         MOVE.B    #D8TYPE, CPftyp.W             ; single density and 1 side
         MOVE.B    #D8TRKSID, CPftps.W           ; default TRACKS PER SIDE
         MOVE.B    #D8SECTRK,CPfspt.W            ; default SECTORS PER TRACK
         MOVE.W    #D8SctrLn, CPfbps.W           ; default sector length

         BTST      #LSeight, FDClocl(A5)         ;is it 8 inch?
         BON.S     FBSINex                       ;yes, exit else do for 5 1/4 inch

         MOVE.B    #D5TYPE, CPftyp.W             ; double density and 2 sides
         MOVE.B    #D5TRKSID, CPftps.W           ; default TRACKS PER SIDE
         MOVE.B    #D5SECTRK,CPfspt.W            ; default SECTORS PER TRACK
         MOVE.W    #D5SctrLn, CPfbps.W           ; default sector length
FBSINex  RTS
  page
;
; FBread - read block of floppy
;
; Turn on motor
;
FBread   MOVEQ     #DMARead, D0                  ;use dma in read direction
         BSR       FBMtrON
         BNE.S     FBRmtrOFF                     ;drive not come ready

; check for disk change
;
         BSR.S     FBdChng
         BNE.S     FBRmtrOFF                     ;I/O error, exit

; get starting sector, track and side and # of sectors to read
;
         BSR       FBstSTS
         BNE.S     FBRmtrOFF                     ;invalid block number
         BSR       FBnumbSctr
         SUBQ.W    #1, D4                        ;turn into loop counter

; seek to track and build interleave table
;
         BSR       FBseek
         BNE.S     FBRmtrOFF

; for i := 1 to (# of sectors to read) do
; read sector, move data to user's buffer, update sector,track and side
;
FBRread  BSR.S     FBsecRd                       ;read sector
         BNE.S     FBRmtrOFF                     ;I/O error exit
         BSR       FBmvOUT                       ;move data to user's space
         BSR       FBupSTS                       ;update sector,track and side
         DBNE      D4, FBRread                   ;do until(bad block) or (read al

; turn motor off and exit
;
FBRmtrOFF
         BRA       FBmtrOFF
  page
;common functions - put in groups so can make include files if necessary
;routines to do macro I/O operations

;
; FBdChng - check if disk has changed.  If it has then find out diskette
;           state and restore the drive.
; ASSUMES motor is alreay on and up to speed.
;
;        Exit  : (NE) = failed, I/O error
;                (EQ) = worked
;
FBdChng  BTST      #LSdskchng, FDClocl(A5)       ;disk changed?
         BOFF.S    FBDCexit                      ;no, exit (cc = EQ)

; restore drive
;
         BSR       FBrstor                       ;restore to track 0
         BNE.S     FBDCexit                      ;failed, D7 has error code

; get diskette info
; Drop into drive info routine.
  page
; FBdrvInfo  - get sector length, density and number of sides.
;              get sides from sense drive status command and get sector
;              length and density from read id command.
;        try read id with current density if fails flip density flag.
;        if that fails report error and stop.
;
;        Exit  : (NE) = failed, I/O error or no density????
;                (EQ) = worked
; do read id to find density
;
FBdrvInfo
         MOVEQ     #3, D0                        ;try each density at least twice

FBDIagn  MOVE.W    D0, -(SP)                     ;save retry count
         MOVEQ     #Mside0, D3                   ;need look at side 0 only
         BSR       FBrdid                        ;if works will clear D7
         BEQ.S     FBDIok                        ;did command
         BRA.S     FBDIex1                       ;timedout, error exit

FBDIok   BSR       FBrdidST                      ;see if command worked
         BEQ.S     FBDIsav                       ;worked, save state
         BCHG      #TYPdnsty, CPftyp.W         ;try other density
         MOVE.W    (SP)+, D0                     ;get retry count
         DBRA      D0, FBDIagn                   ;do until no more retries
         BRA.S     FBDIext                       ;failed, exit

;save sector length from read id result
;
FBDIsav  BSR       FBcnvSLCd                     ;convert sector length code and
         BNE.S     FBDIex1                       ;sector size to large error

; see if drive has side 1
;
         MOVEQ     #Mside0, D0                   ;need look at side 0 only
         BSR       FBsnsDrvSt                    ;do drive status to get # of sid
         BNE.S     FBDIex1                       ;timed out, exit
         BSET      #TYPsides, CPftyp.W         ;assume single sided
         BTST      #ST32sid, (A2)                ;test for number of sides
         BOFF.S    FBDIex1                       ;is single sided (1=2 sided)

; make sure can read side 1
;
         MOVEQ     #Mside1, D3                   ;look at side 1
         BSR       FBrdid                        ;if works will clear D7
         BNE.S     FBDIex1                       ;command failed timeout, exit
         BSR       FBrdidST                      ;see if command worked
         BNE.S     FBDIclr                       ;failed leave as 1 sided
         BCLR      #TYPsides, CPftyp.W         ;is double sided
FBDIclr  CLR.L     D7                            ;if read of side 1 failed - igno

FBDIex1  MOVE.W    (SP)+, D0                     ;remove D0 from stack
FBDIext  TST.W     D7                            ;show error state
FBDCexit RTS
  page
;
; FBsecRd    - read 1 sector.  Finds disk parameters if read function
;              returns an "UNFORMATTED" error.
;
;        Entry : (D5) = logical sector number
;                (A2) = pointer to local stack frame
;        Exit  : (NE) = failed, I/O error
;                (EQ) = worked
;
FBsecRd
         MOVEQ     #RTRYread, D0                 ;read sector retry count

; if read fails and get "UNFORMATTED" error then retry with other density
;
FBSRrtry MOVE.W    D0, -(SP)                     ;save retry count
         CLR.L     D7                            ;make sure error code cleared

         MOVEQ     #CMDread, D0                  ;get in command
         BSR       FBrd1                         ;do read operation
         BNE.S     FBSRex1                       ;timedout, error exit

         BSR       FBreadST                      ;see if worked
         BEQ.S     FBSRex1                       ;read sector, all done
         CMPI.W    #IOEnfmtd, D7                 ;not formatted error
         BNE.S     FBSRnot                       ;no, retry with same density
         BSR.S     FBdrvInfo                     ;get drive parameters
         BNE.S     FBSRext                       ;maybe drive is not formatted

FBSRnot  MOVE.W    (SP)+, D0                     ;get retry count
         DBRA      D0, FBSRrtry                  ;do until no more retries then
         BRA.S     FBSRext                       ;exit with error in D7

FBSRex1  MOVE.W    (SP)+, D0                     ;remove retry count from stack
FBSRext  TST.W     D7                            ;show error condition
         RTS
  page
;FDC, FDD commands
;
; FBspecify  - do a specify command to FDC
;        Entry : (A2) = pointer to command and result array
;                (A4) = prom data space pointer
;                (A5) = Slot base address
;                (A6) = Static ram Base pg pointer - local data
;        Exit  : (NE) = failed
;                (EQ) = completed
;
FBspecify
         MOVE.L    A2, A0                        ;save addr of cmd array
         MOVE.B    #CMDspcfy, (A0)+              ;put specify in command buffer

; 2nd byte is step rate code in bits D7 to D4 inclusive, HUT in D3 to D0 inclusiv
; 3rd byte is HLT code in bits D7 to D1 and NOT DMA in bit D0
;
         MOVE.B    #HLT8code+NDMAcode, D3        ;HLT code is pre-shifted, assume 8 inch
         MOVEQ     #16, D1                       ;step rate code goes
         MOVE.W    (A6), D0                      ;from F to 0 where F = 1 ms.,
                                                 ;E = 2 ms., D = 3 ms. and so on
         BTST      #LSeight, FDClocl(A5)         ;is it 8 inch?
         BON.S     FBSPis8                       ;yes, leave step as is else step div 2
         LSR.W     #1, D0                        ;5 goes 2,4,6,..,32 milliseconds
         MOVE.B    #HLT5code+NDMAcode, D3        ;use 5 inch HLT code

FBSPis8  SUB.B     D0, D1                        ;formula = 16 - step rate
         LSL.B     #4, D1                        ;put step rate in correct bits
         ORI.B     #HUTcode, D1                  ;add head unload time code
         MOVE.B    D1, (A0)+                     ;put 2nd byte in command array
         MOVE.B    D3, (A0)                      ;put 3rd byte in command array

         MOVEQ     #3-1, D6                      ;specify command is 3 bytes
         BRA       FBwritCtl                     ;write command array to cntrllr
                                                 ;FBwritCtl sets cc for error
  page
; FBsnsIntSt - do a Sense Interrupt Status command to FDC.  Find out cause of
;               interrupt.
;        Entry : (A2) = pointer to command and result array
;                (A4) = prom data space pointer
;                (A5) = Slot base address
;                (A6) = Static ram Base pg pointer - local data
;        Exit  : (NE) = failed
;                (EQ) = completed
;                IF completed THEN at (A2) is result array of command
;                                          (2 bytes)
;
FBsnsIntSt
         MOVE.B    #CMDsnsint, (A2)              ;put command byte in buffer
         MOVEQ     #1-1, D6                      ;only 1 byte to command
         BSR       FBwritCtl                     ;send command
         BNE.S     FBSISex                       ;if timed out exit
         MOVEQ     #RSLTsnsint-1, D6             ;length of result array
         BSR       FBctlRead                     ;read result
FBSISex
FBSDSex  RTS

; FBsnsDrvSt  - do a Sense Drive Status command to FDC.
; ASSUMES motor is on and up to speed.
;        Entry : (A2) = pointer to command and result array
;                (A4) = device table entry pointer
;                (A5) = Slot base address
;                (A6) = Static ram Base pg pointer - local data
;                (D0) = side select: side 1=Mside1, side 0=Mside0
;        Exit  : (NE) = failed
;                (EQ) = completed
;                IF completed THEN at (A2) is result array of command
;                                          (1 byte: ST3)
;
FBsnsDrvSt
         MOVE.L    A2, A0                        ;save addr of cmd array
         MOVE.B    #CMDsnsdrv, (A0)+             ;put command byte in buffer

; 2nd byte:  bit D2 is side and bits D1 and D0 are the device select
;
         OR.B      DrvNumb(A6), D0               ;put side select in drive number
         MOVE.B    D0, (A0)                      ;put in command array

         MOVEQ     #2-1, D6                      ;2 bytes to command
         BSR       FBwritCtl                     ;send command
         BNE.S     FBSDSex                       ;if timed out exit

         MOVEQ     #RSLTsnsdrv-1, D6             ;length of result array
         BRA       FBctlRead                     ;read result
  page
;
; FBrstor - do a recalibrate command to FDC.  Puts head on Track 0.
; ASSUMES motor is on and up to speed.
;        Entry : (A2) = pointer to command and result array
;                (A4) = device table entry pointer
;                (A5) = Slot base address
;                (A6) = Static ram Base pg pointer - local data
;        Exit  : (NE) = failed
;                (EQ) = completed
;
FBrstor  MOVEQ     #RTRYrstr, D0                 ;restore retry count

FBRSrtry MOVE.W    D0, -(SP)                     ;save retry count
         MOVE.L    A2, A0                        ;save addr of cmd array
         MOVE.B    #CMDrclbrt, (A0)+             ;put recalibrate in command buff

; 2nd byte is drive selected in bits D1 and D0, the other bits are 0
;
         MOVE.B    DrvNumb(A6), (A0)             ;get drive number, put in command array
         MOVEQ     #2-1, D6                      ;2 bytes in command array

         BSR       FBwritCtl                     ;write command array to controller
         BNE.S     FBRSexit                      ;command timed out

; wait for command to complete
;
FBRSwait BSR       FBw8FDC
         BNE.S     FBRSexit                      ;should there be a timeout on cm

; do Sense Interrupt Status command to get command results
;
         BSR.S     FBsnsIntSt
         BNE.S     FBRSexit

; first byte of result array (pointed at by A2) is ST0, see if seek end is good
;
         BSR       FBrstrST                      ;find out type of error
         BCS.S     FBRSwait                      ;wrong drive wait for correct one
         BEQ.S     FBRSexit                      ;was good, so exit
         MOVE.W    (SP)+, D0                     ;get retry count
         DBRA      D0, FBRSrtry                  ;do until no more retries then
         BRA.S     FBRSext1                      ;return failuere

FBRSexit MOVE.W    (SP)+, D0                     ;remove retry count from stack
FBRSext1 TST.W     D7                            ;show error condition
         RTS
  page
;
; FBseek - do a seek to track command to FDC.
; ASSUMES motor is on and up to speed.
;        Entry : (A2) = pointer to local stack frame
;                (A4) = device table entry pointer
;                (A5) = Slot base address
;                (A6) = Static ram Base pg pointer - local data
;                physical side and track setup
;        Exit  : (NE) = failed
;                (EQ) = completed
;
FBseek   MOVEQ     #RTRYseek, D0                 ;restore retry count

FBSKrtry MOVE.W    D0, -(SP)                     ;save retry count
         MOVE.L    A2, A0                        ;save addr of cmd array
         MOVE.B    #CMDseek, (A0)+               ;put seek cmd in command buffer

; 2nd byte is drive selected in bits D1 and D0 and head select in D2
;
         BSR       FBdselHD                      ;get device and head select byte

         MOVE.B    Ptrack(A2), (A0)              ;3rd byte: track wanted

         MOVEQ     #3-1, D6                      ;2 bytes in command array
         BSR       FBwritCtl                     ;write command array to controll
         BNE.S     FBSKexit                      ;command timed out

; wait for command to complete
;
FBSKwait BSR       FBw8FDC
         BNE.S     FBSKexit                      ;should there be a timeout on cm

; do Sense Interrupt Status command to get command results
;
         BSR       FBsnsIntSt
         BNE.S     FBSKexit

; see if seek end is good
;
         BSR       FBskSTAT                      ;find out type of error
         BCS.S     FBSKwait                      ;wrong drive wait for correct one
         BEQ.S     FBSKexit                      ;was good, so exit
         DBRA      D0, FBSKrtry                  ;do until no more retries then
         BRA.S     FBSKext1                      ;report error

FBSKexit MOVE.W    (SP)+, D0                     ;remove retry count from stack
FBSKext1 TST.W     D7                            ;show error condition
         RTS
  page
;
; FBrdid - do a read id command to FDC.
; ASSUMES motor is on and up to speed.
;        Entry : (A2) = pointer to command and result array
;                (A4) = device table entry pointer
;                (A5) = Slot base address
;                (A6) = Static ram Base pg pointer - local data
;                (D3) = side select: side 1=Mside1, side 0=Mside0
;        Exit  : (NE) = failed
;                (EQ) = completed
;
FBrdid   MOVE.L    A2, A0                        ;save addr of cmd array
         MOVEQ     #CMDredID, D0                 ;get in command
         BSR       FBadDnsty                     ;combine command and MFM/FM bit

; 2nd byte:  bit D2 is side and bits D1 and D0 are the device select
;
         MOVE.B    DrvNumb(A6), D1               ;get drive number
         OR.B      D3, D1                        ;put in side select
         MOVE.B    D1, (A0)                      ;put in command array

         MOVEQ     #2-1, D6                      ;2 bytes to command
         BSR       FBwritCtl                     ;send command
         BNE.S     FBRIext                       ;if timed out exit

; wait for command to complete
;
         BSR       FBw8FDC
         BNE.S     FBRIext                       ;rts on next page

; get results of operation
;
         MOVEQ     #RSLTredID-1, D6              ;length of result array
         BRA       FBctlRead                     ;read result
  page
;
; FBrd1 - do 1 read or write sector operation.
; ASSUMES motor is on and up to speed and on correct track.
;        Entry : D0   = command byte
;                (A2) = pointer to command and result array
;                (A4) = device table entry pointer
;                (A5) = Slot base address
;                (A6) = Static ram Base pg pointer - local data
;                D5 = logical sector number
;        Exit  : (NE) = failed, time out
;                (EQ) = completed
;
FBrd1    MOVE.L    A2, A0                        ;save addr of cmd array
         BSR.S     FBsetrdcmd                    ;setup the rest of the cmd array
         BNE.S     FBR1exit                      ;invalid sector length error

         BSR       FBbufCntr                     ;setup controller counter

; send command
;
         MOVEQ     #RWcmdlen-1, D6               ;9 bytes to command
         BSR       FBwritCtl                     ;send command
         BNE.S     FBR1exit                      ;if timed out exit

; wait for command to complete
;
         BSR       FBw8FDC
         BNE.S     FBR1exit                      ;should there be a timeout on cm

; get results of operation
;
         MOVEQ     #RSLTrdwr-1, D6               ;length of result array
         BSR       FBctlRead                     ;read result
FBRIext
FBR1exit RTS
  page
;command operations support functions

;
; FBsetrdcmd - setup common portions of Read and Write command arrays.
;
;        Entry : A0 = points at 2nd byte of command array
;                A2 = local stack frame pointer
;                A4 = device table entry pointer
;                D0 = command byte
;                D5 = logical sector number
;        Exit  : (NE) = invalid length error
;                (EQ) = got code
;
; 2nd byte:  bit D2 is side and bits D1 and D0 are the device select
;
FBsetrdcmd
         BSR.S     FBadDnsty                     ;expects D0 to have command byte
         BSR.S     FBdselHD                      ;device sel and head sel byte

         MOVE.B    Ptrack(A2), (A0)+             ; 3rd byte: track number
         MOVE.B    Pside(A2), (A0)+              ; 4th byte: head number
         MOVE.B    D5, (A0)+                     ; 5th byte: sector number

         BSR.S     FBcnv2slc                     ; 6th byte: sector size code
         BNE.S     FBSCexit                      ; invalid length
         MOVE.B    D0, (A0)+

         MOVE.B    CPfspt.W, (A0)+               ; 7th byte: EOT

         EXT.W     D0                            ; 8th byte: GPL, gap 3 length co
         MOVE.B    GPLtable(D0.W), (A0)+

         MOVEQ     #-128, D1                     ; 9th byte: DTL
         TST.B     D0                            ;use a DTL value ($80) if code
         BEQ.S     FBSCmin                       ;is 0, = 128 bytes sector
         MOVEQ     #-1, D1                       ;use FF if length code > 0
FBSCmin  MOVE.B    D1, (A0)+

FBSCexit TST.W     D7
         RTS

; Gap 3 length code translation table
;
GPLtable DATA.B    $07                 ;for  128 byte sectors
         DATA.B    $0E                 ;for  256 byte sectors
         DATA.B    $1B                 ;for  512 byte sectors
         DATA.B    $35                 ;for 1024 byte sectors
  page
;
; FBdselHD - put device and head select values in byte of command array
;        Entry : A0 = pointer to byte in command array
;
FBdselHD
         MOVE.B    Pside(A2), D0                 ;get head select
         LSL.B     #2, D0                        ;put in correct position
         OR.B      DrvNumb(A6), D0               ;add drive select
FBdsel10 MOVE.B    D0, (A0)+                     ;put in command array
         RTS
;
; FBadDnsty - add density bit to command
;CANNOT use D3
;        Entry : D0 = command byte
;                A0 = pointer to byte in command array
;
FBadDnsty
         MOVEQ     #MfmBit, D1                   ;assume is FM
         BTST      #TYPdnsty, CPftyp.W           ;is it FM?
         BON.S     FBADfm                        ;yes (1=FM)
         MOVEQ     #Mmfmbit, D1                  ;no then use MFM
FBADfm   OR.B      D1, D0                        ;combine command and MFM/FM bit
         BRA.S     FBdsel10                      ;put it in command array
  page
; FBcnv2slc - convert sector length to sector length code.
;CANNOT use A0
;        Entry : (A4) = device table entry pointer
;        Exit  : (D0) = sector length code
;                (NE) = invalid length error
;                (EQ) = got code
;                             FM        MFM
;        sector lengths      code      code
;               128           00        --
;               256           01        01       controller's buffer is 512 bytes
;               512           02        02       therefore it can not read/write
;              1024           --        03       1024 byte sectors
;
FBcnv2slc
         MOVE.W    CPfbps.W, D1                ;get sector length in device tab
         CLR.B     D0                            ;start with code = 0
         LSR.W     #7, D1                        ;put 4 possible bits in low orde

FBC2find LSR.W     #1, D1                        ;shift out next bit
         BCS.S     FBC2tst                       ;if set then D0 = correct code
         BEQ.S     FBC2err                       ;if word clear then invalid leng
         ADDQ.B    #1, D0                        ;do next code
         BRA.S     FBC2find

FBC2tst  CMPI.B    #2, D0                        ;is code in range - 512 <= sector
         BLS.S     FBC2exit                      ;yes
FBC2err  MOVEQ     #IOEinvsct, D7                ;invalid sector length error

FBC2exit TST.W     D7                            ;show error status
         RTS
;
; FBcnvSLCd - convert sector length code to a number and save in device table.
;        Entry : (A2) = address of result array from READ ID command
;                (A4) = device table entry pointer
;        Exit  : (NE) = sector to long
;                (EQ) = ok
;                             FM        MFM
;        sector length       code      code
;               128           00        --
;               256           01        01       controller's buffer is 512 bytes
;               512           02        02       therefore it can not read/write
;              1024           --        03       1024 byte sectors
;
FBcnvSLCd
         MOVE.W    #SLfmLow, D0                  ;get min. sector length(128)
         CLR.W     D1                            ;make sure high order byte is cl
         MOVE.B    RSLTsctrl(A2), D1             ;get sector length code
         LSL.W     D1, D0                        ;length = multiply of integral o
         MOVE.W    D0, CPfbps.W                  ;save length in device table
         MOVE.B    D1, D0                        ;check if value is greater than
         BRA.S     FBC2tst                       ;512 byte sectors
  page
; FBbaseAdr   - Get static ram base pg address for this slot, get slot base
;               address and device table entry address
;
;
;        Exit : A5 = Slot base address
;               A6 = Static ram Base pg pointer - local data
;
FBbaseAdr
;
; FBSlotRam - calculate base pg address of static ram data area
;
;        Exit  : (A6) = Base pg address (a page = 256 bytes)
;
FBSlotRam
         LEA       CPsl1ram.W, A6                ;address of I/O slot Static Ram
         MOVE.B    CPbtslot.W, D0                ;Slot number
         EXT.W     D0                            ;base :=
         MOVE.W    D0, D1
         SUBQ.W    #1,D0                         ;   ((slot - 1) *
         MULU      #DBPoffst, D0                 ;   (pg length)) +
         ADDA.L    D0, A6                        ;   (Slot Static ram base address)
;
;FBSltAdr - Fetches the slot base address.
;
;        Entry : (D1) = slot number
;        Exit  : (A5) = Slot base address
;
FBSltAdr LEA       IOSbase.L, A5                 ;slot 0 BaseBadrs
         MULU      #IOSoffst, D1                 ;slot * offset + slot 0 base
         ADDA.L    D1, A5                        ;equals this slot's base address
         RTS
  page
;
; FBctlRead - read n bytes from controller into command/result buffer
;
;        Entry : (A2) = pointer to command/result array
;                (A5) = slot base address
;                (D6) = number of bytes to read - 1
;        Exit  : (D6) = number of bytes not sent - 1
;                (NE) = timed out and array AND (D6) are invalid
;                (EQ) = worked array is valid
;
FBctlRead
         MOVE.L    A2, A0                        ;get address of array

; while (not timeout) and (haven't sent all the bytes) do
;
FBCRloop MOVE.W    #FDCTimeOut, D0               ;time out counter

;repeat until(timeout) or (FDC is ready)
;
FBCRrdy  BTST      #FDCready, FDCstat(A5)        ;Ready := FDC is ready
         SNE       D3                            ;         and
         BTST      #DataDir, FDCstat(A5)         ;         Data direction is READ
         SNE       D1
         AND.B     D3, D1
         BNE.S     FBCRok                        ;Ready so stop waiting
         SUBQ.W    #1, D0                        ;count down timeout
         BMI.S     FBCRrdy                       ;no, check again
         MOVEQ     #IOEflpto, D7                 ;timeout error
         BRA.S     FBCRexit                      ;timed out so exit (also NE)

FBCRok   MOVE.B    FDCdata(A5), (A0)+            ;get data from reg into array
         DBRA      D6, FBCRloop                  ;see if more to get
         CLR.L     D7                            ;got all the data show no error
FBCRexit RTS
  page
;
; FBwritCtl - write n bytes to controller from command/result buffer
;
;        Entry : (A2) = pointer to command/result array
;                (A5) = slot base address
;                (D6) = number of bytes to write - 1
;        Exit  : (NE) = timed out and did not finish sending array
;                (EQ) = worked array is sent
;
FBwritCtl
         MOVE.L    A2, A0                        ;get address of array

; while (not timeout) and (haven't sent all the bytes) do
;
FBWCloop MOVE.W    #FDCTimeOut, D0               ;time out counter

;repeat until(timeout) or (FDC is ready)
;
FBWCrdy  BTST      #FDCready, FDCstat(A5)        ;Ready := FDC is ready
         SNE       D3                            ;         and
         BTST      #DataDir, FDCstat(A5)         ;         Data direction is WRIT
         SEQ       D1
         AND.B     D3, D1
         BNE.S     FBWCok                        ;Ready so stop waiting
         SUBQ.W    #1, D0                        ;count down timeout
         BNE.S     FBWCrdy                       ;no, check again
         MOVEQ     #IOEflpto, D7                 ;timeout error
         BRA.S     FBWCexit                      ;timed out so exit (also NE)

FBWCok   MOVE.B    (A0)+, FDCdata(A5)            ;get data from array into reg

;FDC will not give a valid ready state for 12 microseconds after
;after issuing the COMMAND byte of the command array
;code is probably slow enough to compensate w/o a wait loop (????)
;;         MOVEQ     #5, D1                        ;wait 12 microsecs. for RQM
;;WRCwait  DBRA      D1, WRCwait                   ;to settle to correct state

         DBRA      D6, FBWCloop                  ;see if more to get
         CLR.L     D7                            ;got all the data show no error
FBWCexit RTS
  page
; FBMtrON - select drive, dma direction and turn motor on.  Wait til
;           drive comes ready.
;CANNOT use D3.
;
;        Entry : (A2) = local stack frame pointer
;                (A5) = Slot base address
;                (D0) = DMARWdir bit = 1 if read dma, = 0 if write dma
;                            all other bits of low order byte clear!!
;        Exit  : (NE) = timed out waiting for drive
;                (EQ) = worked array is sent
;
; Add to DMA direction bit Enable device select,motor on, disable INTS
;        and NOT reset FDC
;
FBMtrON  ORI.B     #MMtrON, D0
         OR.B      DrvNumb(A2), D0               ;get drive number (0..3)
                                                 ;put as dev sel 0 and 1
         MOVE.B    D0, FDClocl(A5)               ;put in local command register

; wait until ready or timeout
;
         MOVE.L    RdyTO(A6), D1                 ;time out counter

FBMOwait BTST      #LSNdrRdy, FDClocl(A5)        ;is drive(FDD) ready?
         BOFF.S    FBMOexit                      ;Ready so stop waiting
         SUBQ.L    #1, D1                        ;count down timeout
         BPL.S     FBMOwait                      ;not timed out so continue
         MOVEQ     #IOEflpto, D7                 ;show timeout error
FBMOexit RTS
;
; FBmtrOFF - deselect drive
;
;        Entry : (A4) = device table entry pointer
;                (A5) = Slot base address
;
FBmtrOFF MOVE.B    #DisblDS, FDClocl(A5)         ;deselect drive
         RTS
  page
;
; FBw8FDC - wait for a command to finish.  FDC generates an IRQ when done.
;
;        Entry : (A5) = Slot base address
;        Exit  : (NE) = timed out waiting for command to finish
;                (EQ) = command completed
;
FBw8FDC  MOVE.L    #CMDTimeOut, D0               ;timeout counter

FBW8wait BTST      #LSNirq, FDClocl(A5)          ;did IRQ happen
         BOFF.S    FBW8exit                      ;yes, command done
         SUBQ.L    #1, D0                        ;no, see if ran out of time
         BPL.S     FBW8wait                      ;if more counts do again else
         MOVEQ     #IOEflpto, D7                 ;show timeout error

FBW8exit RTS
;
; FBbufCntr - setup controller counter to point to big enough buffer
;   Counter is 8 bit interface to a 9 bit down count register which is used
;   as address selector for the controller's buffer.
;   D7 thru D1, inclsive, of interface goto D8 thru D2 of register.
;   D0 of interface goes to D1 and D0 of counter register.
;
;        Entry : (A5) = Slot base address
;
FBbufCntr
         MOVE.W    CPfbps.W, D0                ;counter := (SectorSize
         SUBQ.W    #1, D0                        ;             - 1)
         LSR.W     #1, D0                        ;           DIV 2
         MOVE.B    D0, FDCcntr(A5)
         RTS
  page
;
; FBmvOUT - move data out of controller buffer into user's buffer
;
;        Entry : (D2) = number of bytes in partial sector
;                (D4) = current sector loop counter
;
FBmvOUT  MOVE.W    CPfbps.W, D1                ;if full sector move full sector
         TST.W     D4                            ;is this last sector
         BNE.S     FBMVOful                      ;no
         MOVE.W    D2, D1                        ;then use partial length
FBMVOful SUBQ.W    #1, D1                        ;make into loop counter
         BSR.S     FBbufCntr                     ;init buffer counter

FBMVOmov MOVE.B    FDCbffr(A5), (A3)+            ;get data from buffer to user
         DBRA      D1, FBMVOmov
         RTS
  page
; sector,track,side calc support routines

;
; FBstSTS - calculate starting side, track, and sector from starting block
;           do logical track and sector and physical side and track
;
;        Entry : (D5) = starting block
;        Exit  : (NE) = invalid block, other return params invalid
;                (EQ) = worked
;                (D3) = logical track  (word)
;                (D5) = logical sector (word)
;                Pside and Ptrack inited with physical side and track
;
FBstSTS  CMPI.L    #$7FFF, D5                    ;block bigger than maxint
         BHI.S     FBSSinvblk                    ;yes, invalid block number

; Logical Track := ( ((block #) * (block size)) DIV (sector size) ) DIV (sectors/
;
         MULU      #BLKSZ, D5                  ;sector size divides evenly into
         DIVU      CPfbps.W, D5                ;sector size must be 128,256, or
         CLR.L     D1                            ;get byte and make it a word
         MOVE.B    CPfspt.W, D1                ;
         DIVU      D1, D5                        ;D5 low word is logical track
         MOVE.W    D5, D1                        ;save for later use and validation
         MOVE.B    D1, CurTrack(A2)              ;keep logical track for update
         MOVE.W    D1, D3                        ;save logical track

; logical sector number := remainder + 1
;
         CLR.W     D5                            ;hi word of D5 is logical sector
         SWAP      D5                            ; minus 1.  must add one to it
         ADDQ.W    #1, D5

; Calc physical side and track
;
         BRA.S     FBphysTS

FBSSinvblk
         MOVEQ     #IOEinvblk, D7                ;invalid block number
         RTS
  page
; FBphysTS - calculate physical track and side from logical track
;        Entry : D1 = logical track number
;        Exit  : Pside and Ptrack set up
;                (NE) = invalid block, other return params invalid
;                (EQ) = worked
;
FBphysTS
         CLR.W     Ptrack(A2)                    ;clear track and side, assume si
         BTST      #TYPsides, CPftyp.W
         BON.S     FBPsd0                        ;is side 0 only.

         LSR.W     #1, D1                        ;ptrack := track div 2
         ROXL.W    Ptrack(A2)                    ;pside := track mod 2  shift in

FBPsd0   CLR.L     D0                            ;get max # of tracks per side in
         MOVE.B    CPftps.W, D0                ;a word
         CMP.W     D0, D1                        ;is physical track greater than
         BHI.S     FBSSinvblk                    ;yes, invalid block number
         MOVE.B    D1, Ptrack(A2)                ;save physical track number
FBUexit  TST.W     D7
         RTS
;
; FBupSTS - update side, track and sector
;        Entry : (D5) = current logical sector number
;                (D4) = loop counter of sectors to read/write
;        Exit  : (NE) = invalid block or I/O error
;                (EQ) = worked
;                (D5) = updated logical sector number
;                Pside and Ptrack set up
;                Did seek if track changed
;
FBupSTS  TST.W     D4                            ;was this the last sector?
         BEQ.S     FBUexit                       ;yes, exit
         ADDQ.W    #1, D5                        ;treat as word but value is a byte
         CMP.B     CPfspt.W, D5                ;still in same track
         BLS.S     FBUexit                       ;yes, then all done

; must update track and side
;
         MOVEQ     #1, D5                        ;reset sector number back to 1
         CLR.L     D1                            ;holds logical track number
         ADDQ.B    #1, CurTrack(A2)              ;inc logical track number
         MOVE.B    CurTrack(A2), D1              ;get updated track number
         BSR.S     FBphysTS                      ;calc physical side and track
         BNE.S     FBUexit                       ;invalid block error,track out of range

; do seek then calc new interleave table
;
         BRA       FBseek                        ;seek physical track
  page
;
; FBnumbSctr - calculate number of sectors to read/write and number
;              of bytes in partial sector
;
;        Entry : D2 = number of bytes to read (word)
;        Exit  : D2 = number of bytes in partial sector - if no partial then = sector length
;                D4 = total number of sectors to read,
;                     includes the partial sector
;
FBnumbSctr
         MOVE.W    CPfbps.W, D0                  ;D0 = bytes per sector
         CLR.L     D1                            ;make sure no garbage in hi word
         MOVE.W    D2, D1                        ;# of full sectors :=
         DIVU      D0, D1                        ;(# of bytes) DIV (sector size)
         MOVE.W    D1, D4                        ;save # of full sectors
         CLR.W     D1                            ;the remainder is the number
         SWAP      D1                            ;of bytes in partial sector
         TST.W     D1                            ;if partial is empty
         BEQ.S     FBNpart                       ;then use full sector size for last
         ADDQ.W    #1, D4                        ;else total := (# of full sectors) + 1
         MOVE.W    D1, D0                        ;and use partial byte count for last
FBNpart  MOVE.W    D0, D2                        ;if no partial then set it to sector
         RTS                                     ;size else use partial count
  page
; error routines

;
; FBrstrST   - check error for restore operation
;
;        Entry : (A2) = address of result array from sense int command
;                       2 bytes. 1st is ST0, 2nd is cylinder number.
;        Exit  : (D7) = IOresult code
;                (NE) = I/O error
;                (EQ) = no error
;                (C)  = wrong drive
;
FBrstrST BSR       FBchkST0
         BCS.S     FBrstrex                      ;wrong drive, exit
         BEQ.S     FBrstrex                      ;no error, exit
         CMPI.W    #IOEnoT0, D7                  ;is it type w/ equipment error
         BNE.S     FBrstrex                      ;no
         BTST      #ST0equp, (A2)                ;yes,drive won't ack Track 0 aft
         BON.S     FBrstrex                      ;77 step pulses
         MOVEQ     #IOEnebhrd, D7                ;give nebulous hardware err code
FBrstrex RTS
;
; FBskSTAT - check error for seek operation
;
;        Entry : (A2) = address of result array from sense int command
;                       2 bytes. 1st is ST0, 2nd is cylinder number.
;        Exit  : (D7) = IOresult code
;                (NE) = I/O error
;                (EQ) = no error
;                (C)  = wrong drive
;
FBskSTAT BSR.S     FBchkST0                      ;check ST0 for operation state
         BCS.S     FBsksex                       ;wrong drive, exit
         BEQ.S     FBsksex                       ;no error, exit
         CMPI.W    #IOEnoT0, D7                  ;did cmd stop in middle?
         BNE.S     FBsksex                       ;no, give IOresult FBchkST0 found
         MOVEQ     #IOEnebhrd, D7                ;give nebulous hardware err code
FBsksex  RTS
  page
;
;FBrdidST - check error for read ID operation
;        Entry : (A2) = address of result array of read id command
;        Exit  : (D7) = IOresult code
;                (NE) = I/O error
;                (EQ) = no error
;
FBrdidST BSR.S     FBchkST0                      ;see if command worked
         BEQ.S     FBRIDSex                      ;worked so exit
         CMPI.W    #IOEnoT0, D7                  ;did cntrllr not finish cmd?
         BNE.S     FBRIDSex                      ;no, then exit

; check if can't read disk - maybe unformatted?  {check ST1}
;
         MOVEQ     #IOEnfmtd, D7                 ;assume not formatted
         MOVE.W    (A2), D0                      ;get 2nd byte of array in low by
         BTST      #ST1snfnd, D0                 ;sector not found?
         BON.S     FBRIDSex                      ;yes, maybe unformatted
         BTST      #ST1nadrm, D0                 ;missing address mark?
         BON.S     FBRIDSex                      ;yes, maybe unformatted

; see if CRC error
;
         MOVEQ     #IOEcrcer, D7                 ;assume have CRC error
         BTST      #ST1dataer, D0                ;CRC error?
         BON.S     FBRIDSex                      ;yes
         MOVEQ     #IOEnebhrd, D7                ;give nebulous hardware err code

FBRIDSex TST.W     D7
         RTS
  page
;
;FBreadST - check error for read sector operation
;
;        Entry : (A2) = address of result array of read id command
;        Exit  : (D7) = IOresult code
;                (NE) = I/O error
;                (EQ) = no error
;
FBreadST BSR.S     FBrdidST                      ;do same tests as Read ID operat
         BEQ.S     FBRDSTex                      ;no errors, read worked

; always try wrong cylinder. if is then restore and re-seek
;
         MOVE.B    ST2(A2), D0                   ;get status register 2 from resu
         BTST      #ST2wrngC, D0                 ;read the wrong track?
         BOFF.S    FBRDST10                      ;no
         BSR       FBrstor                       ;do restore
         BNE.S     FBRDSTex                      ;I/O error
         BSR       FBseek                        ;do seek
         BNE.S     FBRDSTex                      ;I/O error
         MOVEQ     #IOEwrngC, D7                 ;tell higher level what failed
         BRA.S     FBRDSTex

; try read/write on bad track, write protect or missing data address mark
;
FBRDST10 CMPI.W    #IOEnebhrd, D7                ;did FBrdidST fail to find cause
         BNE.S     FBRDSTex                      ;no, then exit

         MOVEQ     #IOEbdtrk, D7                 ;track marked as bad (IBM spec)
         BTST      #ST2bdtrk, D0
         BON.S     FBRDSTex

         MOVEQ     #IOEnfmtd, D7                 ;missing data address mark
         BTST      #ST2ndtam, D0                 ;if is disk needs formatting
         BON.S     FBRDSTex                      ;yes disk in bad shape

;not doing write
;        MOVEQ     #IOEwrprot, D7                ;disk write protected?
;        BTST      #ST1wrpro, ST1(A2)            ;
;        BON.S     FBRDSTex                      ;yes

         MOVEQ     #IOEnebhrd, D7                ;give nebulous hardware err code

FBRDSTex TST.W     D7
         RTS
  page
;
;FBchkST0 - check ST0 to see if command failed or succeded
;        Entry : (A2) = address of ST0 byte
;        Exit  : (D7) = IOresult code from ST0 check
;                (NE) = I/O error
;                (EQ) = no error
;                (C)  = wrong drive
;
FBchkST0 MOVE.B    (A2), D0                      ;get ST0
         ANDI.B    #MIntCod, D0                  ;check interrupt code

; see if controller tried but didn't finish
;
         MOVEQ     #IOEnoT0, D7                  ;give can't get to Track 0
         CMPI.B    #MABstp, D0                   ;if cmd stopped in middle
         BEQ.S     FBC0exit                      ;yes

; see if drive changed ready line
;
         CMPI.B    #MABrdy, D0
         BNE.S     FBC0inv
         MOVEQ     #IOEoffln, D7                 ;drive went off line, lost ready
         MOVEQ     #3, D0                        ;see if have correct drive response
         AND.B     (A2), D0                      ;leave just drive sel bits
         CMP.B     DrvNumb(A6), D0               ;is drive the same?
         BEQ.S     FBC0exit                      ;correct drive, exit carry clear
         MOVE.W    #1, CCR                       ;set NE and C got wrong drive
         BRA.S     FBC0ex1                       ;exit

; see if invalid code, if is then have either severe hardware error or software b
;
FBC0inv  MOVEQ     #IOEunknwn, D7                ;give unknown hardware err code
         CMPI.B    #MInvCod, D0                  ;is it invalid?
         BEQ.S     FBC0exit                      ;no, exit no errors
         CLR.L     D7                            ;show no error

FBC0exit TST.W     D7                            ;should clear carry flag
FBC0ex1  RTS

        page
    list 1
PromLength EQU     %         ;bytes of code
        end     setup
