; ; This is an edit of the original file ATLAS.COM ; ; The edits allow the file to be compiled online at asm80.com ; to avoid the chicken-and-egg problem of needing a CP/M machine ; to assemble it. ; .binfrom $0100 .binto $2100 ; 2022-12-03 Edited to create the version dated 1984-10-23 ; ;============================== .cpu Z80 ;; TITLE EPROM bootstrap and BIOS for ATLAS ; ATLAS EPROM routines and bootstrap for CP/M 3 ; ; Copyright (C) 1984, Arcom Control Systems Ltd. ; ; John Fielden, 14-Sep-84 ; The EPROM contains the low level routines for accessing the character I/O ; devices and disk controller. Thus it provides a hardware independent ; interface for the CP/M 3 BIOS routines. The code in this EPROM can be ; modified to support character I/O using the onboard SCC and various ; offboard I/O devices and video cards. This disk controller code can support ; several of the chips in the WD279x series. ; ; The bootstrap program initialises the console I/O device, and can optionally ; automatically determine its baud rate. The user is then prompted to insert ; the system disk into drive A and then the system tracks (upto a maximum of ; 16k bytes in total) are loaded into memory at 0100H and executed. ; The characteristics of the boot drive and disk format can be set by changing ; the data beginning at 0040H in the EPROM. For 8" drives the program will ; automatically select the density of the system tracks ;; NAME('EPROM') ;; .SFCOND FALSE EQU 0 ;; TRUE EQU NOT FALSE TRUE EQU $FF ; Controller chip type WD2791 EQU TRUE WD2793 EQU FALSE WD2795 EQU FALSE WD2797 EQU FALSE ; Disk controller register addresses FDCMD EQU 8 ; WD279x command register address FDSTAT EQU 8 ; WD279x status register address FDTRK EQU 9 ; WD279x track register address FDSEC EQU 10 ; WD279x sector register address FDDATA EQU 11 ; WD279x data register FDWAIT EQU 12 ; I/O address to wait for INTRQ/DRQ ; SCC port addresses SCCAC EQU 5 ; channel A control register SCCAD EQU 7 ; channel A data register SCCBC EQU 4 ; channel B control register SCCBD EQU 6 ; channel B data register ; COMMON EQU 8000H ; common memory BUFLEN EQU 4000H ; buffer length LOADP EQU 0100H ; load point for CPMLDR ; ASCII control characters CR EQU 13 ; ASCII carriage return LF EQU 10 ; ASCII line feed BEL EQU 7 ; ASCII bell ; WDINV EQU (WD2791 + WD2795) ; true or inverted data bus? ; ;; ASEG ORG 0100H .PHASE 0 ; 0000H JP START ; reset entry point ; reserved space for interrupt/restart jumps DEFB 0FFH DEFW 0FFFFH,0FFFFH DEFW 0FFFFH,0FFFFH,0FFFFH,0FFFFH DEFW 0FFFFH,0FFFFH,0FFFFH,0FFFFH DEFW 0FFFFH,0FFFFH,0FFFFH,0FFFFH DEFW 0FFFFH,0FFFFH,0FFFFH,0FFFFH DEFW 0FFFFH,0FFFFH,0FFFFH,0FFFFH DEFW 0FFFFH,0FFFFH,0FFFFH,0FFFFH DEFW 0FFFFH,0FFFFH,0FFFFH,0FFFFH ; ; Boot configuration data ; 0040H ; ; Set the default baud rate for the console. The allowed values are ; 1=50 baud, 2=75 baud, 3=110 baud, 4=134.5 baud, 5=150 baud, ; 6=300 baud, 7=600 baud, 8=1200 baud, 9=1800 baud, 10=2400 baud, ; 11=3600 baud, 12=4800 baud, 13=7200 baud, 14=9600 baud. BDEF:DEFB 14 ; default is 9600 ; Enable/disable baud rate sensing ; Set 0 to disable this facility, FFH to enable it. AUTO: DEFB 0FFH ; FF to enable autobaud ; ; Disk drive data ; ; Type: bits 7,6,5 and 3 correspond to the bit settings ; to load into the latch at I/O address 0 to select the drive. ; Bit 7: 1=8-inch, 0=mini-floppy ; 6: drive select 1 ; 5: drive select 0 ; 4: must be 1 ; 3: 1=single density, 0=double density ; 2: must be 0 ; 1: must be 0 ; 0: must be 0 TYPE: DEFB 050H ; disk select and control bits ; Set 0 for a single sided drive, 1 for a double sided drive SIDES: DEFB 1 ; double sided ; Set to the step speed bits for WD279x type I command ; 0 = 3ms ; 1 = 6ms ; 2 = 10ms ; 3 = 15ms STEPS: DEFB 0 ; step speed bits ; Set to the number of tracks on the drive ; 8-inch = 77 ; 5.25-inch = 80 ; 3-inch = 40 NTRKS: DEFB 80 ; number of tracks ; CP/M style disk parameter block ; For this control block each side of a physical track is ; treated as a separate logical track. SECPT: DEFW 36 ; number of logical records / track DEFB 5,1FH ; block shift, mask DEFB 3 ; extent mask DEFW 177 ; maximum block number DEFW 127 ; maximum directory entry number DEFB 080H,000H ; reserved directory blocks DEFW 32 ; size of directory check vector SYSTRK: DEFW 1 ; total number of system tracks SECSIZ: DEFB 2,3 ; physical shift, mask DEFB 0FFH ; reserved for future use DEFB 0FFH ; reserved for future use DEFB 0FFH ; reserved for future use DEFB 0FFH ; reserved for future use DEFB 0FFH ; reserved for future use DEFB 0FFH ; reserved for future use DEFB 0FFH ; reserved for future use DEFB 0FFH ; reserved for future use DEFB 0FFH ; reserved for future use ; ; reserved for NMI jump and user patches ; 0060H ; DEFW 0FFFFH,0FFFFH,0FFFFH,0FFFFH DEFW 0FFFFH,0FFFFH,0FFFFH,0FFFFH DEFW 0FFFFH,0FFFFH,0FFFFH,0FFFFH DEFW 0FFFFH,0FFFFH,0FFFFH,0FFFFH ; ; Jump table ; 0080H ; ; character I/O routines JPCIT0: JP CIT0 ; initialise JPCIS0: JP CIS0 ; input status JPCI0: JP CI0 ; input JPCOS0: JP COS0 ; output status JPCO0: JP CO0 ; output status ; disk I/O routines JPCMD: JP FCMD ; send floppy controller command JPSTAT: JP FSTAT ; get floppy controller status JPSTRK: JP SETTRK ; set track register JPRTRK: JP RDTRKR ; read track register JPSSEC: JP SETSEC ; set sector register JPRSEC: JP RDSECR ; read sector register JPSDAT: JP SETDAT ; set data register JPRDAT: JP RDDATR ; read data register JPRDS: JP RDSEC ; read a sector JPWRS: JP WRSEC ; write a sector JPRD: JP RCODE ; read JPWR: JP WCODE ; write ; DEFB 'Copyright (C) 1984, Arcom Control Systems Ltd.' DEFB '841023' ; character initialisation routines CIT0: JP CINI0 JP CINI1 JP CININ JP CININ JP CININ JP CININ JP CININ JP CININ JP CININ JP CININ JP CININ JP CININ JP CININ JP CININ JP CININ JP CININ ; character input status routines CIS0: JP CIST0 JP CIST1 JP CIST2 JP CISTN JP CISTN JP CISTN JP CISTN JP CISTN JP CISTN JP CISTN JP CISTN JP CISTN JP CISTN JP CISTN JP CISTN JP CISTN ; character input routines CI0: JP CIN0 JP CIN1 JP CIN2 JP CINN JP CINN JP CINN JP CINN JP CINN JP CINN JP CINN JP CINN JP CINN JP CINN JP CINN JP CINN JP CINN ; character output status routines COS0: JP COST0 JP COST1 JP COSTN JP COSTN JP COSTN JP COSTN JP COSTN JP COSTN JP COSTN JP COSTN JP COSTN JP COSTN JP COSTN JP COSTN JP COSTN JP COSTN ; character output routines CO0: JP COUT0 JP COUT1 JP COUTN JP COUTN JP COUTN JP COUTN JP COUTN JP COUTN JP COUTN JP COUTN JP COUTN JP COUTN JP COUTN JP COUTN JP COUTN JP COUTN ; SCC initialisation data SCCTA: DEFB 009H ; reset channel A DEFB 080H DEFB 004H ; clock x16, 2 stop bits DEFB 04CH DEFB 001H ; external/status interrupt enable DEFB 001H DEFB 003H ; Rx 8 bits DEFB 0C0H DEFB 005H ; Tx 8 bits, RTS DEFB 062H DEFB 009H ; clear reset DEFB 000H DEFB 00AH DEFB 000H DEFB 00BH ; clocks use baud rate generator DEFB 056H SCCLA1 EQU $-SCCTA DEFB 00EH ; BRG source DEFB 002H DEFB 00EH ; BRG enable DEFB 003H DEFB 003H ; Rx enable DEFB 0C1H DEFB 005H ; Tx enable, DTR, RTS DEFB 0EAH DEFB 00FH ; DCD interrupt enable DEFB 008H DEFB 000H ; Reset external/status interrupts DEFB 010H DEFB 000H ; Twice! DEFB 010H SCCLA2 EQU $-SCCTA-SCCLA1 ; SCCTB: DEFB 009H ; reset channel B DEFB 040H DEFB 004H ; clock x16, 2 stop bits DEFB 04CH DEFB 003H ; Rx 8 bits, auto enables DEFB 0E0H DEFB 005H ; Tx 8 bits, RTS DEFB 062H DEFB 009H ; clear reset DEFB 000H DEFB 00AH DEFB 000H DEFB 00BH ; clocks use baud rate generator DEFB 056H SCCLB1 EQU $-SCCTB DEFB 00EH ; BRG source DEFB 002H DEFB 00EH ; BRG enable DEFB 003H DEFB 003H ; Rx enable DEFB 0E1H DEFB 005H ; Tx enable, DTR, RTS DEFB 0EAH DEFB 00FH ; Disable external/status interrupts DEFB 000H DEFB 000H ; Reset external/status interrupts DEFB 010H DEFB 000H ; Twice! DEFB 010H SCCLB2 EQU $-SCCTB-SCCLB1 ; SCC baud rate divisors for x16 clock, PCLK = 4 MHz DIVIS: DEFW 0,2500,1665,1134, 924, 831, 415, 206 DEFW 102, 67, 50, 33, 24, 15, 11, 5 ; initialise an SCC channel SCCINI: OTIR ; load first part of data ADD A,A ; index into baud rate divisors table JR Z,SCCIN1 ; leave baud rate alone? PUSH HL LD E,A LD D,0 LD HL,DIVIS ADD HL,DE LD B,12 LD A,(HL) OUT (C),B OUT (C),A INC B INC HL LD A,(HL) OUT (C),B OUT (C),A POP HL SCCIN1: POP BC ; load second part of data OTIR POP BC ; restore registers POP DE POP HL RET ; and return ; initialise device 0 CINI0: PUSH HL ; save registers PUSH DE PUSH BC LD C,SCCAC ; address of control register LD B,SCCLA2 ; length of part 2 of table PUSH BC LD B,SCCLA1 ; length of part 1 of table LD HL,SCCTA ; address of table JR SCCINI ; input status of device 0 CIST0: IN A,(SCCAC) AND 1 ; receive character available? RET Z OR 0FFH RET ; input a character from device 0 CIN0: CALL CIST0 ; wait for a character JR Z,CIN0 IN A,(SCCAD) ; get it AND 7FH ; remove parity RET ; output status of device 0 COST0: IN A,(SCCAC) AND 4 ; transmitter buffer empty? RET Z OR 0FFH RET ; output a character to device 0 COUT0: PUSH AF COUT0W: CALL COST0 ; wait until not busy JR Z,COUT0W POP AF OUT (SCCAD),A RET ; initialise device 1 CINI1: PUSH HL ; save registers PUSH DE PUSH BC LD C,SCCBC ; address of control register LD B,SCCLB2 ; length of part 2 of table PUSH BC LD B,SCCLB1 ; length of part 1 of table LD HL,SCCTB ; address of table JR SCCINI ; input status of device 1 CIST1:IN A,(SCCBC) AND 1 ; receive character available? RET Z OR 0FFH RET ; input a character from device 1 CIN1: CALL CIST1 ; wait for character JR Z,CIN1 IN A,(SCCBD) RET ; output status of device 1 COST1: IN A,(SCCBC) AND 4 ; transmitter buffer empty RET Z OR 0FFH RET ; output a character to device 1 COUT1: PUSH AF COUT1W: CALL COST1 ; wait for not busy JR Z,COUT1W POP AF OUT (SCCBD),A RET ; input status of device 2 CIST2:LD A,3 ; external/status interrupt? OUT (SCCAC),A IN A,(SCCAC) AND 1 RET Z IN A,(SCCAC) XOR 8 ; character latched? AND 8 JR Z,CIN22 OR 0FFH RET ; input a character from device 2 CIN2: CALL CIST2 ; wait for character JR Z,CIN2 IN A,(0) CIN22: PUSH AF LD A,10H ; reset external/status interrupts OUT (SCCAC),A POP AF RET ; initialise null device CININ: RET ; input status of null device CISTN: XOR A RET ; input a character from null device CINN: LD A,26 ; ^Z for no device RET ; output status of null device COSTN: LD A,0FFH RET ; output a character to null device COUTN: RET ; send a command to floppy disk controller IF WDINV FCMD: CPL OUT (FDCMD),A CPL ELSE FCMD: OUT (FDCMD),A ENDIF RET ; get floppy disk controller status FSTAT: IN A,(FDSTAT) IF WDINV CPL ENDIF RET ; set floppy disk controller track register IF WDINV SETTRK: CPL OUT (FDTRK),A CPL ELSE SETTRK: OUT (FDTRK),A ENDIF RET ; read floppy disk controller track register RDTRKR: IN A,(FDTRK) IF WDINV CPL ENDIF RET ; set floppy disk controller sector register IF WDINV SETSEC: CPL OUT (FDSEC),A CPL ELSE SETSEC: OUT (FDSEC),A ENDIF RET ; read floppy disk controller sector register RDSECR: IN A,(FDSEC) IF WDINV CPL ENDIF RET ; set floppy disk data register IF WDINV SETDAT: CPL OUT (FDDATA),A CPL ELSE SETDAT: OUT (FDDATA),A ENDIF RET ; read floppy disk data register RDDATR: IN A,(FDDATA) IF WDINV CPL ENDIF RET ; read data from disc ; call with A = command IF WDINV RCODE: CPL LD C,FDWAIT OUT (FDCMD),A RCODE1: IN B,(C) ; wait RET M ; IRQ? IN A,(FDDATA) ; get next data byte CPL LD (HL),A ; store data INC HL ; point to next location JR RCODE1 ; loop ELSE RCODE: LD C,FDDATA OUT (FDCMD),A RCODE1: IN A,(FDWAIT) ; wait RLA ; IRQ? RET C INI ; get next byte JR RCODE1 ; loop ENDIF ; write data to disc ; call with A = command IF WDINV WCODE: CPL LD C,FDWAIT OUT (FDCMD),A WCODE1: LD A,(HL) ; fetch next data byte CPL IN B,(C) ; wait RET M ; IRQ? OUT (FDDATA),A ; put data INC HL ; point to next data byte JR WCODE1 ; loop ELSE WCODE: LD C,FDDATA OUT (FDCMD),A WCODE1: IN A,(FDWAIT) ; wait RLA ; IRQ? RET C OUTI ; put next byte JR WCODE1 ; loop ENDIF ; read a sector ; call with A = 000m0E00 for read sector side 0 ; 000m1E00 for read sector side 1 ; returns with A = status bits RDSEC: AND 1CH IF WD2795 + WD2797 LD C,A ; move side select bit to correct posn AND 8 RRCA RRCA OR C OR 88H ELSE OR 82H ENDIF CALL RCODE ; read sector JP JPSTAT ; get status ; write a sector ; call with A = 000m0E0a for write sector side 0 ; 000m1E0a for write sector side 1 ; returns with A = status bits WRSEC: AND 1DH IF WD2795 + WD2797 LD C,A ; move side select bit to correct posn AND 8 RRCA RRCA OR C OR 0A8H ELSE OR 0A2H ENDIF CALL WCODE ; write sector JP JPSTAT ; get status ; ; data for bootstrap program ; CRLF: DEFB CR,LF,0 PRESS: DEFB CR,LF,'Press RETURN',0 SIGNON: DEFB CR,LF,LF,'Arcom ATLAS',CR,LF,'Serial console running at ',0 BAUD: DEFB ' baud',CR,LF,0 QBOOT: DEFB CR,LF,'Insert system disk in drive A: and press RETURN',0 SPEEDS: DEFW B0,B50,B75,B110,B134,B150,B300,B600 DEFW B1200,B1800,B2400,B3600,B4800,B7200,B9600,B19200 B0: DEFB '?',0 B50: DEFB '50',0 B75: DEFB '75',0 B110: DEFB '110',0 B134: DEFB '134.5',0 B150: DEFB '150',0 B300: DEFB '300',0 B600: DEFB '600',0 B1200: DEFB '1200',0 B1800: DEFB '1800',0 B2400: DEFB '2400',0 B3600: DEFB '3600',0 B4800: DEFB '4800',0 B7200: DEFB '7200',0 B9600: DEFB '9600',0 B19200: DEFB '19200',0 UNKNOW: DEFB CR,LF,BEL,'Unknown disk format',0 NOSYS: DEFB CR,LF,BEL,'No system on disk',0 RDMSG: DEFB CR,LF,BEL,'Error reading system disk:',CR,LF,'Track-',0 SIDMSG: DEFB ', Side-',0 SECMSG: DEFB ', Sector-',0 ERRTAB: DEFW B7,B6,B5,B4,B3,B2 B7: DEFB ', Not ready',0 B6: DEFB ', Write protect',0 B5: DEFB ', Fault',0 B4: DEFB ', Record not found',0 B3: DEFB ', CRC',0 B2: DEFB ', Lost data',0 ERRMSG: DEFB ', Retry (Y/N)? ',0 ; comparison data for baud rate determination CTABLE: DEFW -9, -5, -6, -8, -12, -15, -22, -55 DEFW -130, -263, -258, -131, -310, -590,-32768 ; print a newline NEWLIN: LD HL,CRLF ; print a null-terminated string pointed to by HL WRITES: LD A,(HL) ; get next character INC HL ; increment pointer AND A ; end of string? RET Z CALL JPCO0 ; print it JR WRITES ; loop ; ; Reset entry point ; START: LD A,0F8H ; initialise latch OUT (0),A LD SP,STACK ; initialise SP ; Relocate interbank move code to the start of common memory LD HL,CODE ; move from EPROM LD DE,GO ; to common memory LD BC,CODLEN LDIR ; Reset SCC LD A,0C0H LD B,9 LD C,SCCAC OUT (C),B ; send reset command OUT (C),A XOR A OUT (C),B ; clear OUT (C),A ; Set terminal baud rate LD A,(BDEF) ; default baud rate PUSH AF ; remember it CALL JPCIT0 ; initialise channel A of SCC LD A,(AUTO) ; auto-baud? AND A JR Z,PRINT LD HL,PRESS CALL WRITES LD HL,0 DI AUTO0: IN A,(SCCAC) ; wait for start of character AND 10H JR Z,AUTO0 AUTO1: INC HL ; time start bit IN A,(SCCAC) AND 10H JR NZ,AUTO1 EI PUSH HL ; wait for rest of character AUTO2: LD A,20 AUTO3: DEC A JR NZ,AUTO3 DEC HL LD A,H OR L JR NZ,AUTO2 POP HL LD A,16 ; determine correct baud rate divisor DI LD SP,CTABLE AUTO4: DEC A POP DE ADD HL,DE JR C,AUTO4 LD SP,STACK EI PUSH AF ; remember new baud rate CALL JPCIT0 ; set SCC baud rate divisor AUTO5: CALL JPCIS0 ; throw away corrupt character(s) JR Z,PRINT CALL JPCI0 JR AUTO5 PRINT: LD HL,SIGNON CALL WRITES POP AF ; baud rate ADD A,A LD C,A LD B,0 LD HL,SPEEDS ADD HL,BC LD A,(HL) INC HL LD H,(HL) LD L,A CALL WRITES LD HL,BAUD CALL WRITES ; Wait until user is ready to boot BOOT: LD HL,QBOOT CALL WRITES BWAIT: CALL JPCI0 CP 13 JR NZ,BWAIT CALL NEWLIN ; Boot CALL LOGIN JR Z,BOOT1 ; OK? LD HL,UNKNOW CALL WRITES JR BOOT BOOT1: LD HL,BUFFER XOR A ; start at track 0 BOOT2: LD (TRACK),A XOR A ; start at sector 1 BOOT3: INC A LD (SECTOR),A CALL READ LD A,(TRKLEN) LD E,A LD A,(SECTOR) CP E JR NZ,BOOT3 ; loop until end of track LD A,(TRKS) LD E,A LD A,(TRACK) INC A CP E JR NZ,BOOT2 ; loop until all tracks loaded LD DE,BUFFER ; compute number of bytes loaded XOR A SBC HL,DE LD B,H LD C,L EX DE,HL OR C JR Z,CHECK INC B XOR A CHECK: ADD A,(HL) ; compute checksum INC HL DEC C JR NZ,CHECK DJNZ CHECK INC A JP Z,GO ; if OK, execute it LD HL,NOSYS ; otherwise print error message CALL WRITES JR BOOT ; and try again ; login drive LOGIN: LD A,(SIDES) LD (SIDED),A LD A,(SYSTRK) LD (TRKS),A LD A,(SECSIZ) LD B,A LD A,(SECPT) INC B ; convert number of logical records ADD A,A ; to number of physical records LOGIN1: RRA DJNZ LOGIN1 LD (TRKLEN),A LD A,(TYPE) LD (DBITS),A XOR A ; restore drive LD (TRACK),A DEC A ; force seek LD (CTRACK),A CALL SEEK RET NZ ; drive ready? LD A,(DBITS) ; get drive/disk type LD E,A AND 80H ; mini-floppy? RET Z LD A,(NTRKS) ; is it really 8", not 80-track mini? CP 77 JR Z,LOGIN2 XOR A RET LOGIN2: LD A,E AND 0F8H ; try single-sided single-density OR 18H LD (DBITS),A ; control bits XOR A LD (SIDED),A LD A,2 LD (TRKS),A LD A,26 LD (TRKLEN),A CALL RDADDR ; read address JR NZ,LOGIND ; OK? LD A,(RDBUF+3) ; sector size = 128 bytes? AND A RET LOGIND: LD A,(DBITS) ; now try double-sided double-density AND 0F0H LD (DBITS),A ; control bits LD A,(SIDES) LD (SIDED),A LD A,(SYSTRK) LD (TRKS),A LD A,(SECSIZ) LD B,A LD A,(SECPT) INC B ; convert number of logical records ADD A,A ; to number of physical records LOGIN3: RRA DJNZ LOGIN3 LD (TRKLEN),A CALL RDADDR ; read address RET NZ ; OK? LD A,(RDBUF+3) ; sector size correct? LD HL,SECSIZ CP (HL) RET ; Read address RDADDR: LD B,4 ; retry counter RDADD0: CALL SEEK ; select drive and seek track RET NZ ; drive not ready? PUSH BC LD A,(DBITS) OUT (0),A LD HL,RDBUF ; address buffer LD A,0C4H ; read address command CALL JPRD ; read address POP BC CALL JPSTAT ; check status AND 0F0H RET Z ; OK? DEC B JR NZ,RDADD1 AND 0F0H ; too many tries, give up RET RDADD1: CALL RETRY JR RDADD0 ; Read sector READ: LD B,4 ; retry counter READ0: CALL SEEK ; select drive and seek track JR NZ,RDERR PUSH BC ; save counter PUSH HL ; save address LD A,(SECTOR) ; load sector register CALL JPSSEC LD A,(DBITS) OUT (0),A AND 10H ; side select RRCA XOR 08 DI CALL JPRDS EI POP DE ; remember oLD address POP BC ; counter AND 0FCH ; extract error bits RET Z EX DE,HL ; oLD address in HL, ready to repeat LD DE,READ0 ; return address PUSH DE DJNZ RETRY POP DE ; too many tries, give up ; handle error RDERR: PUSH HL PUSH AF LD HL,RDMSG CALL WRITES ; print message header LD A,(TRACK) CALL PDEC LD HL,SIDMSG CALL WRITES LD A,(DBITS) AND 10H LD A,'0' JR NZ,RDERR1 INC A RDERR1: CALL JPCO0 LD HL,SECMSG CALL WRITES LD A,(SECTOR) CALL PDEC POP AF LD HL,ERRTAB ERROR: LD E,(HL) ; next message INC HL LD D,(HL) INC HL ADD A,A ; test next bit PUSH AF EX DE,HL CALL C,WRITES EX DE,HL POP AF JR NZ,ERROR ; loop if any more bits left LD HL,ERRMSG ; ", Retry (Y/N)" CALL WRITES CALL UINPUT ; get reply POP HL CP 3 ; ^C JP Z,BOOT CP 'Y' JR Z,READ LD A,1 RET ; RETRY: LD A,(TRACK) BIT 0,B ; on alternate tries do a restore JR NZ,RETRY1 AND A ; if at track 0, do a step in instead JR Z,RETRY1 XOR A ; restore JR SEEK0 RETRY1: ADD A,2 ; step in JR SEEK0 ; seek track. SEEK: LD A,(TRACK) ; desired track SEEK0: LD E,A LD A,(SIDED) ; is disk double sided? AND A LD A,(DBITS) JR Z,SEEK1 OR 10H SRL E ; track number / 2 JR NC,SEEK1 AND 0E8H ; select side 1 SEEK1: LD (DBITS),A OR 90H ; side 0, don't enable mini-floppy OUT (0),A LD A,(CTRACK) ; current track PUSH AF CALL JPSTRK ; update track register POP AF CALL JPSDAT ; ; ; Starts to differ from ATLAS source code dated "841006" ; ; if 0 ; code dated 1984-10-06 PUSH BC LD B,200 ; wait up to 2 s for motor to start SEEK2: LD A,10 CALL MSDEL else ; code dated 1984-10-23 ; ; load head/start motor _before_ waiting 2 seconds ; LD A,18H ; load head/start motor CALL DOIT PUSH BC LD B,200 ; wait up to 2 s for motor to start ; ; and don't bother waiting another 10 milliseconds ; jr SEEK2_done ; skips over SEEK2 ; ; ; SEEK2: ld a,10 call MSDEL ; ; ; SEEK2_done: endif LD A,18H ; load head/start motor CALL DOIT AND 80H ; drive ready? JR Z,SEEK3 DJNZ SEEK2 POP BC RET ; no disk! ; ; ; SEEK3: POP BC LD A,(CTRACK) SUB E ; already at wanted track? RET Z LD A,E LD (CTRACK),A PUSH AF CALL JPSDAT ; seek new track POP AF AND A LD A,18H JR NZ,SEEK4 LD A,08H ; track zero wanted so restore SEEK4: CALL DOIT PUSH AF LD A,50 ; 50 ms to allow head to settle CALL MSDEL POP AF RET ; delay A ms MSDEL: LD D,250 MSDEL1: DEC D JR NZ,MSDEL1 DEC A JR NZ,MSDEL RET ; execute WD279x type I command. Status is returned in A. DOIT: PUSH DE LD D,A LD A,(STEPS) ; ADD in step speed bits OR D CALL JPCMD POP DE LD A,12 ; wait until status bits valid DOIT1: DEC A JR NZ,DOIT1 DOIT2: CALL JPSTAT BIT 0,A JR NZ,DOIT2 AND 098H ; extract error bits RET ; print unsigned binary number A in decimal PDEC: LD HL,PDECT ; address of tables of powers of 10 LD B,3 PDEC1: LD D,(HL) LD C,'0'-1 PDEC2: INC C SUB D ; subract until negative JR NC,PDEC2 ADD A,D ; correct for last subtraction LD E,A LD A,C CALL JPCO0 LD A,E INC HL ; next power DJNZ PDEC1 RET ; PDECT: DEFB 100,10,1 ; get console input, echo, and force to upper case UINPUT: CALL JPCIS0 ; throw away any type-ahead JR Z,UIN1 CALL JPCI0 JR UINPUT UIN1: CALL JPCI0 CP ' ' ; control char? CALL NC,JPCO0 CP 'a' ; lower case? RET C SUB 32 ; make upper case RET ; ; Routines and variables in global RAM ; CODE EQU $ .DEPHASE .PHASE COMMON ; Relocated code for moving loader and executing it GO: LD A,(DBITS) ; select RAM INC A OUT (0),A LD HL,BUFFER LD DE,LOADP LD BC,BUFLEN LDIR JP LOADP ; CODLEN EQU $-COMMON ; ; This does not seem to be filling the ROM chip correctly: ; fill $ff, $2000-$ ; ; Variables ; DBITS: DEFS 1 ; disk control/select bits SIDED: DEFS 1 ; number of sides on current disk TRKS: DEFS 1 ; number of system tracks to load TRKLEN: DEFS 1 ; number of sectors per track CTRACK: DEFS 1 ; current track TRACK: DEFS 1 ; wanted track SECTOR: DEFS 1 ; wanted sector RDBUF: DEFS 1 ; address buffer DEFS 1 DEFS 1 DEFS 1 DEFS 1 DEFS 1 STACK EQU $+128 BUFFER EQU STACK .end