; 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 ? 0.7 bne.s DSreset ;no, go on 0.7 btst #1,CPdspflg.w ;auto line feed? 0.7 bon.s DSreset ;yes, bypass insertion 0.7 moveq #DSClf,d0 ;add a 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