IMD 1.17: 4/04/2010 8:02:20 Ampro LittleBoard Source Disk #3      BIOS-1-6ASM   BIOS-1-6ASM y BIOS-3-8ASM  !"#$%&'() BIOS-3-8ASM *+,-./0123456789 BIOS-3-8ASM :;<=>?@ABCDEFGHb -A60103 E DIR COM I HP-1-2 ASM JKLMNOPQRSTUVWXY HP-1-2 ASM tZ[\]^_`a SBT-1-2 ASM Qcdefghijklmnop HG-1-2 ASM lqrstuvw CRC CRC x HF-1-5 ASM yz{|}~ HF-1-5 ASM  HI-1-2 ASM HI-1-2 ASM #  BIOS-3-8ASM -  * * * * * * * * * * * * * * * * * * * * * * * CBIOS FOR AMPRO CP/M 2.2 SYSTEMS * * THIS BIOS IS FOR THE USE OF OWNERS * * OF AMPRO COMPUTER SYSTEMS. ANY OTHER * * USE IS PROHIBITED. * * COPYRIGHT (C) 1984 AMPRO COMPUTERS, INC.* * * * * * * * * * * * * * * * * * * * * * * YES EQU 1 NO EQU 0 ZCPR3 EQU YES ; purpose: the cp/m 2.2 bios for the ampro cpu. based on ; cbios.asm and deblock.asm, modified only as ; necessary. ; differences from standard cp/m bios: ; ; 1) track number is a byte, not a word ; 2) keeps track of last trk accessed, seeks only when ; necessary (and invokes idread before seeks). ; 3) prints a signon message ; 4) disk accessing for two sided disks uses a cylinder ; approach to minimize the need for seeks. ; 5) there is an additional entry point that unlogs all disks ; 6) support for virtual disk allows format translation ; 7) limited iobyte support has been included ; 8) support for zcpr has been included ; VERS EQU 16 ; ve rsion of this bios ; revision log: ; ;version 1.6 29-apr-95 RJB Corrected BIOS overflow by optimizing ; the IOINIT routine. ; ;version 1.5 3-mar-85 RJB Optimized head load delay. ; ; 22-jan-85 RJB Changed con & tty status routines to ; use "ALLSENT" bit rather than TBE. ; Removed buffer flush on warm boot. ; ;version 1.4 24 july 84 : changed motor-on routine to wait ; 1 revolution after successful ; id read. Added mask of MSB ; to TTY anurn (^m) LF EQU 0AH ;line feed (^j) TAB EQU 009H ;tab (^i) BSP EQU 08H ;backspace (^h) DEL EQU 7FH ;character delete CAN EQU 18H ;line cancel (^x) NAK EQU 15H ; (^u) ; misc equates CTS EQU 020H ;clear to send DELSEND EQU 28 TIMEOUT EQU 100 DDLSPT EQU 40 FRESTOR EQU 8 ; spin-up disabled on all commands FSEEK EQU 01CH FSTEPIN EQU 05CH FREADS EQU 088H FWRITES EQU 0A8H ;write precomp on FRDADDR EQU 0C8H AMSSDD EQU 086H AMDSDD EQU 0C6H SSDD96 EQU 087H DSDD96 EQU 0C7H d CRT inputs. ; ;version 1.3 5 april 84 : fixed crt and tty routines ; modified for latest zcpr3 ; ;version 1.2 5 march 84 : optimized motor on delay ; changed default initialization ; changed to support zcpr3 ; ;version 1.1 20 february 84 : added single density support for ; 256 and 512 byte sectors jww ; added one second motor on delay ; ;version 1.0 12 february 84 : production release ; MSIZE EQU 61 ;cp/m version memory ; jump vectors for individual subroutines JMP BOOT ;cold start WBOOTE: JMP WBOOT ;warm start JMP CONST ;console status JMP CONIN ;console character in JMP CONOUT ;console character out JMP LIST ;list character out JMP PUNCH ;punch character out JMP READER ;reader character in JMP HOME ;move head to home position JMP SELDSK ;select disk JMP SETTRK ;set track number JMP SETSEC ;set sector number JMP SETDMA ;set dma address JMP READ ;read disk JMP WRITE ;write dis size in kilobytes BIAS EQU (MSIZE-20)*1024 CCP EQU 3400H+BIAS ;base of ccp BDOS EQU CCP+806H ;base of bdos BIOS EQU CCP+1600H ;base of bios CDISK EQU 0004H ;current disk number 0=a,...,15=p IOBYTE EQU 0003H ;intel i/o byte ; Z-80 equates OTIR80 EQU 0B3EDH ; OTIR (0edh,0b3h) ; cp/m to host disk constants WRALL EQU 0 ;write to allocated WRDIR EQU 1 ;write to directory WRUAL EQU 2 ;write to unallocated ORG BIOS ;origin of this program NSECTS EQU ($-CCP)/128 ;warm start sector ck JMP LISTST ;return list status JMP SECTRAN ;sector translate ; ampro specific bios calls JMP UNLOG ;unlog all disks JMP GETEDSK ;get pointer to e disk storage JMP IOINIT ; set new i/o parameters ORG BIOS+40H CTCVAL: DB 47H,13,47H,208,3,3,3,3 ; ctc0 values SIOAVAL:DB 4,46H,5,0EAH,3,0C1H,0,0,0,0 SIOBVAL:DB 4,86H,5,0EAH,3,0C1H,0,0,0,0 NDSKS: DB 4 ; four drives allowed STPRAT: DB 3,0,0,0 ; step rates of four drives, ; 2nd - 4th not used yet IOBYT: DB 81H AUTOCMD:DB 7,'STAount ; ampro hardware equates ; port addresses STBSET EQU 2 ;sets print strobe STBCLR EQU 3 ;clears print strobe PIO1 EQU 001H ;parallel printer port CTCA EQU 40H CTCA0 EQU CTCA CTCA1 EQU 050H CTCA2 EQU 060H CTCA3 EQU 070H SIO1 EQU 080H SIODPA EQU SIO1 SIODPB EQU 088H SIOCPA EQU 084H SIOCPB EQU 08CH STAT EQU 0C4H ;disk controller command/status register RTRK EQU STAT+1 ;disk controller track register RSEC EQU STAT+2 ;disk controller sector register RDAT EQU STAT+3 ;diRTUP',0 ORG AUTOCMD+10 HSA: DB 0 ;lsb: 1 = yes, 0 = no HSB: DB 1 VER: DB VERS ; version of this bios. ORG BIOS+80H DPBASE: DW XLT0,0,0,0 DW DIRBUF,DPARM,CSV0,ALV0 DW XLT0,0,0,0 DW DIRBUF,DPARM,CSV1,ALV1 DW XLT0,0,0,0 DW DIRBUF,DPARM,CSV2,ALV2 DW XLT0,0,0,0 DW DIRBUF,DPARM,CSV3,ALV3 DW XLT1,0,0,0 ;the e disk DW DIRBUF,EPARM,CSV4,ALV4 SPARM: ;a single-sided ampro disk DW 40 ;sec/trk DB 4 ;block shift DB 15 ;block mask DB 1 ;extent mask DW 94 ;disk sk data port CMND EQU 0C0H ;disk controller command/status register WTRK EQU CMND+1 ;disk controller track register WSEC EQU CMND+2 ;disk controller sector register WDAT EQU CMND+3 ;disk data port HLDELAY EQU 35 ; Head load delay CONT EQU 00H ;disk flag and control port ; mask equates TBE EQU 04H ;dart xmit buffer empty RDA EQU 01H ;dart receive data available DCD EQU 08H ;dart data carrier detect CTS EQU 20H ;dart clear to send ; char equates CR EQU 0DH ;carriage retsize -1 DW 63 ;directory max DB 128 ;alloc 0 DB 0 ;alloc 1 DW 16 ;check size DW 2 ;offset DPARM: ;a double-sided ampro disk DW 40 ;sec/trk DB 4 ;block shift DB 15 ;block mask DB 1 ;extent mask DW 194 ;disk size -1 DW 127 ;directory max DB 192 ;alloc 0 DB 0 ;alloc 1 DW 32 ;check size DW 2 ;offset DPARM96: ; 96 tpi double sided DW 40 ;sec/trk DB 4 ;block shift DB 15 ;block mask DB 0 ;extent mask DW 394 ;disk size -1 DW 255 ;directory max   DB 240 ;alloc 0 DB 0 ;alloc 1 DW 64 ;check size DW 2 ;offset XLT0: ; 48 tpi skew table DB 1,2,3,4,5,6,7,8,9,10 ; the following storage should not be moved for e disk to work. ; a call to bios function getedisk returns the address of eparm. ; the type tab entry is thus defined as the returned value -1, ; and the translate table value as the returned value+15. TYPETAB: DS 4 ;type of disk n ; encoding is as follows: ; bit 7: 0 = sd, 1 = dd ; bit 6: 0 = ss, 1 = ds L SETTRK MVI B,NSECTS ;number of sectors to read MVI C,1 ;first sector LXI H,CCP WBLOOP: PUSH B ;save sector count PUSH H CALL SETSEC ;set the sector for next read POP B ;fetch dma address PUSH B ;save a copy CALL SETDMA CALL READ ;error check? ORA A JNZ WBERR POP H LXI D,128 DAD D ;update dma address POP B INR C ;point to next sector MOV A,C CPI DDLSPT CZ NXTTRK DCR B ;decrement loop counter JNZ WBLOOP ;get another sector ; end of load operation, se ; bits 3-2: 0 = 1k alloc, 1 = 2k alloc ; bits 1-0: 0 = 128, 1 = 256, 2 = 512 DB 082H ;same as kaypro EPARM: ;kaypro disk DW 40 ;sec/trk DB 3 ;block shift DB 7 ;block mask DB 0 ;extent mask DW 194 ;disk size -1 DW 63 ;directory max -1 DB 240 ;alloc 0 DB 0 ;alloc 1 DW 16 ;check size DW 1 ;offset EDSD: DB 1 ; default drive b: XLT1: ;translate table for e disk DB 0,1,2,3,4,5,6,7,8,9 DS 10 GETEDSK: LXI H,EPARM RET IOINIT: LXI H,CTCVAL t parameters and go to cp/m GOCPM: MVI A,0C3H ;c3 is a jmp instruction STA 0 ;for jmp to wboot LXI H,WBOOTE ;wboot entry point SHLD 1 ;set address field for jmp at 0 STA 5 ;for jmp to bdos LXI H,BDOS ;bdos entry point SHLD 6 ;address field of jump at 5 to bdos LXI B,80H ;default dma address is 80h CALL SETDMA EI ;enable the interrupt system LDA CDISK ;get current disk number MOV C,A ;send to the ccp JMP CCP ;go to cp/m for further processing ;home the selected disk LXI B,0240H ; CTC0 DW OTIR80 LXI B,0250H ; CTC1 DW OTIR80 LXI B,0260H ; CTC2 DW OTIR80 LXI B,0270H ; CTC3 DW OTIR80 LXI B,0A84H ; DART A DW OTIR80 LXI B,0A8CH ; DART B DW OTIR80 LDA IOBYT STA IOBYTE RET PUTS: ;send bytes to console until zero encountered MOV A,M ;fetch char ORA A ;done? RZ ;yes if it's a zero byte INX H ;set up for next byte MOV C,A CALL CONOUT ;sent it to the console JMP PUTS WBERR: LXI H, HOME: MVI C,0 ;set the desired track to zero CALL SETTRK LDA HSTWRT ;check for pending write ORA A JNZ HOMED STA HSTACT ;clear host active flag HOMED: RET SELDSK: ;select disk MOV A,C ;selected disk number STA SEKDSK ;seek disk number STA CPMDSK ;working variable CPI 4 ;good disk number? JZ SELEDSK ;always works for e disk LXI H,NDSKS CMP M JNC SELERR ;if not, return error ; disk number is in the proper range (0..3). ; see if disk has been accessed yet - if not, rBOOTMSG WBERR1: CALL PUTS JMP WBOOT BOOTMSG: DB CR, LF, 'Boot failed!', 7, 0 NXTTRK: PUSH B MVI C,1 CALL SETTRK POP B MVI C,0 RET WBOOT: ;simplest case is to read the disk until all sectors loaded ; warning: this bios can only read or write single density ; disks, not cold or warm boot from them. XRA A ;0 to accumulator ; STA HSTACT and STA UNACNT were removed as they caused any sectors ; that were left in the write buffers to be thrown away on a warm ; boot. STA HSead the ; directory track to determine if single or double sided, ; load all disk access tables as appropriate MOV A,E ; test for new mount RAR JC SELEND ; not a new mount ; first access, see if double sided CALL GETTYPE ;ignore result, just use pointer PUSH H ;save pointer into type table MVI A,3 STA TRIES ;try it three times ACCESS: ; first assume it's a double sided double density disk POP H ;get type table ptr PUSH H MVI M,AMDSDD ;set type = dsdd-512 TSID ;assume side zero LXI SP,80H ;use space below buffer for stack CALL UNLOG ; warm boot consists of reading the ccp and bdos in from the ; system tracks: 1600h of code. only double density disks may ; be warm booted on an ampro system. the ten sectors ; on logical track zero (track 0 side 0) and one track on ; logical track one (track 0 side 1) are read to ccp. MVI C,0 CALL SELDSK ;auto select disk type MOV A,H ORA L JZ WBERR ;couldn't do it MVI C,0 CAL CALL SETUP ;select drive and side (don't care) CALL RESTORE CPI 255 JZ SELERR1 ;disk timeout MVI A,2 ; this side STA CPMTRK CALL SEEK ;note seek always seeks on side 0 first. JNZ TRYSS ; check for double sided LDA IDSAVE+2 ; sector CPI 17 JC TRYSS LDA IDSAVE+3 ; size CPI 3 ; 96 tpi has 1024 byte sectors JNZ SELOK POP H ; get type table pointer PUSH H MVI M,DSDD96 JMP SELOK ; seek failed, probably an id read error. change to ; first side and try a gain... TRYSS: POP H PUSH H MVI M,AMSSDD ;set type = ssdd-512 LXI H,CPMTRK DCR M CALL SEEK JNZ TRYAGN LDA IDSAVE+3 ; size CPI 3 ; 96 tpi drives have 1024 byte sectors JNZ SELOK POP H ; get type table pointer PUSH H MVI M,SSDD96 JMP SELOK ; seek failed, maybe a single density disk (only acceptable in e drive) TRYAGN: LXI H,TRIES ;can't read side zero either? DCR M JZ SELERR1 ;quit if last time JMP ACCESS ;else try again ; if we get here, we had a succesuous sector numbering MVI A,0 JZ SELE1X ;exit if duplicate numbering LDA EPARM ;fetch logical sectors per track RAR ;divide by 2 for physical sectors ANI 07FH SELE1X: STA ESECADJ JMP SELEND SELE2: ;512 byte sector, 2k allocation (ampro) MOV A,B ;get type byte back ANI 020H ;look for continuous sector numbering MVI A,0 JZ SELE2X ;exit if duplicate numbering LDA EPARM ;fetch logical sectors per track RAR RAR ;right shift two bits ANI 03FH SELE2X: STA ESECADJ sful id read. ; set up tables... SELOK: POP H ;get rid of saved pointer CALL SELEND ;use selend as a subroutine (wow) LXI D,10 ;and offset to parm table entry DAD D PUSH H ;save pointer CALL GETTYPE POP H ;restore ptr CPI AMSSDD ;ssdd? LXI B,SPARM ;assume single sided JZ SETSSID CPI AMDSDD ; double sided 48? LXI B,DPARM ; ds 48 and ss 96 share dparm JZ SETSSID CPI SSDD96 JZ SETSSID LXI B,DPARM96 ; and fall through SETSSID: LDAX B ; get logical sectors per tracJMP SELEND SELERR1: ;one thing on the stack POP H SELERR: ;disk select failed LXI H,0 RET SETTRK: ;set track given by register c MOV A,C STA SEKTRK STA CPMTRK RET SETSEC: ;set sector given by register c MOV A,C STA SEKSEC ;sector to seek STA CPMSEC RET SETDMA: ;set dma address given by bc MOV H,B MOV L,C SHLD DMAADR RET SECTRAN: ;translate sector number bc MOV H,B MOV L,C RET READ: ;read the selected cp/m sector CALL GETTYPE ANI 3 ;k STA CPMSPT MOV M,C ;and fix the table INX H MOV M,B ;table now set up ; compute proper disk parameter header address SELEND: ; set deblocker variables CALL GETTYPE MOV B,A ; save it a moment ANI 3 ; mask sector size STA SECSHF ; sector shift MOV C,A ; set counter MVI A,1 DB0: ADD A DCR C JNZ DB0 DCR A STA SECMSK ; sector mask MOV A,B ; get type byte ANI 4 ; test block size 0=1k, 1=2k MVI A,8 ; assume 1k JZ DB1 ; if so ADD A ; must be 2k DB1: STA  size JZ READHST ; if 128 bytes XRA A STA UNACNT MVI A,1 STA READOP ;read operation STA RSFLAG ;must read data MVI A,WRUAL STA WRTYPE ;treat as unalloc JMP RWOPER ;to perform the read WRITE: ;write the selected cp/m sector CALL GETTYPE ANI 3 JZ WRITEHST ; if 128 bytes XRA A ;0 to accumulator STA READOP ;not a read operation MOV A,C ;write type in c STA WRTYPE CPI WRUAL ;write unallocated? JNZ CHKUNA ;check for unalloc ; write to unallocated, set parameteMUNACT ; initial unacnt value LDA SEKDSK ;get disk number back GETDPT: MVI H,0 MOV L,A ;put it in hl DAD H DAD H DAD H DAD H LXI D,DPBASE ;base of parm block DAD D ;hl=.dpb(curdsk) RET SELEDSK: ;log in parameters for the e disk LDA EPARM-1 ;fetch type byte MOV B,A ;save it in b ANI 03H ;mask type nibble ORA A JZ SELE0 CPI 1 JZ SELE1 CPI 2 JZ SELE2 SELE3: ; 1024 byte sectors MOV A,B ; get type byte ANI 20H ; look for continuous sector numbering rs ; for edsk, we need to check to see if sekdsk is the e disk. ; if so, then unacnt will be set according to the disk type. ; for ampro disks, its always blksiz/128. LDA MUNACT STA UNACNT LDA SEKDSK ;disk to seek STA UNADSK ;unadsk = sekdsk LDA SEKTRK STA UNATRK ;unatrk = sectrk LDA SEKSEC STA UNASEC ;unasec = seksec CHKUNA: ;check for write to unallocated sector LDA UNACNT ;any unalloc remain? ORA A JZ ALLOC ;skip if not ; more unallocated records remain DCR A ; MVI A,0 JZ SELE3X LDA EPARM ; get logical sectors per track RAR RAR RAR ANI 1FH ; mask off trash SELE3X: STA ESECADJ JMP SELEND SELE0: ;128 byte single density MOV A,B ;get type byte back ANI 020H ;look for continuous sector numbering MVI A,0 JZ SELE0X ;exit if duplicate numbering LDA EPARM ;fetch logical sectors per track SELE0X: STA ESECADJ JMP SELEND SELE1: ;256 byte sector, 1k allocation (whatever) MOV A,B ;get type byte back ANI 020H ;look for continunacnt = unacnt-1 STA UNACNT LDA SEKDSK ;same disk? LXI H,UNADSK CMP M ;sekdsk = unadsk? JNZ ALLOC ;skip if not ; disks are the same LXI H,UNATRK LDA SEKTRK CMP M ;sektrk = unatrk? JNZ ALLOC ;skip if not ; tracks are the same LDA SEKSEC ;same sector? LXI H,UNASEC CMP M ;seksec = unasec? JNZ ALLOC ;skip if not ; match, move to next sector for future ref INR M ;unasec = unasec+1 MOV A,M ;end of track? ; edsk: following code compares to cpmspt (40 for ampro d isks) ; make this a variable. PUSH B ;save bc in case we needed it PUSH PSW ;save the value to be compared LDA SEKDSK CPI 4 ;edisk? LDA CPMSPT MOV B,A JNZ EMATCH LDA EPARM ;fetch table spt (convenient, eh?) MOV B,A EMATCH: POP PSW CMP B ;count cp/m sectors POP B ;restore bc register JC NOOVF ;skip if no overflow ; overflow to next track MVI M,0 ;unasec = 0 LDA UNATRK INR A STA UNATRK ;unatrk = unatrk+1 NOOVF: ;match found, mark as unnecessary read XRA AH DAD H DAD H ; hl has relative host buffer address LXI D,HSTBUF DAD D ;hl = host address XCHG ;now in de LHLD DMAADR ;get/put cp/m data MVI C,128 ;length of move LDA READOP ;which way? ORA A JNZ RWMOVE ;skip if read ; write operation, mark and switch direction MVI A,1 STA HSTWRT ;hstwrt = 1 XCHG ;source/dest swap RWMOVE: ;c initially 128, de is source, hl is dest LDAX D ;source character INX D MOV M,A ;to dest INX H DCR C ;loop 128 times JNZ RWM ;0 to accumulator STA RSFLAG ;rsflag = 0 JMP RWOPER ;to perform the write ALLOC: ;not an unallocated record, requires pre-read XRA A ;0 to accum STA UNACNT ;unacnt = 0 INR A ;1 to accum STA RSFLAG ;rsflag = 1 RWOPER: ;enter here to perform the read/write XRA A ;zero to accum STA ERFLAG ;no errors (yet) PUSH B ;save in case LDA SECSHF MOV B,A LDA SEKSEC ;compute host sector SLOOP: ORA A ;carry = 0 RAR ;shift right DCR B JNZ SLOOP POP B ;restore SOVE ; data has been moved to/from host buffer LDA WRTYPE ;write type CPI WRDIR ;to directory? LDA ERFLAG ;in case of errors RNZ ;no further processing ; clear host buffer for directory write ORA A ;errors? RNZ ;skip if so XRA A ;0 to accum STA HSTWRT ;buffer written LDA HSTDSK STA CPMDSK LDA HSTTRK STA CPMTRK CALL WRITEHST LDA ERFLAG RET WRITEHST: ;cpmdsk = host disk #, cpmtrk = host track #, ;hstsec = host sect #. write "hstsiz" bytes ;from hstbTA SEKHST ;host sector to seek ; active host sector? LXI H,HSTACT ;host active flag MOV A,M MVI M,1 ;always becomes 1 ORA A ;was it already? JZ FILHST ;fill host if not ; host buffer active, same as seek buffer? LDA SEKDSK LXI H,HSTDSK ;same disk? CMP M ;sekdsk = hstdsk? jz samedt ; indicate not same disk mvi a,0ffh sta chgdsk jmp nomatch samedt: ; same disk, same track? LXI H,HSTTRK LDA SEKTRK CMP M ;sektrk = hsttrk? JNZ NOMATCH ; same disk, same track, samuf and return error flag in erflag. ;return erflag non-zero if error XRA A STA RWHOST JMP HOSTIO READHST: ;cpmdsk = host disk #, cpmtrk = host track #, ;hstsec = host sect #. read "hstsiz" bytes ;into hstbuf and return error flag in erflag. MVI A,1 STA RWHOST HOSTIO: CALL SETUP ;output to drive select register MVI A,10 STA TRIES WMI: ;where am i? CALL MAPTRK CALL GETTRK ;what track do we want? CMP M CNZ SEEK ;if not there, go there JNZ RWFAIL ;in case seee buffer? LDA SEKHST LXI H,HSTSEC ;sekhst = hstsec? CMP M JZ MATCH ;skip if match NOMATCH: ;proper disk, but not correct sector LDA HSTWRT ;host written? ORA A LDA HSTDSK ;select host as disk to work on STA CPMDSK LDA HSTTRK STA CPMTRK CNZ WRITEHST ;clear host buff FILHST: ;may have to fill the host buffer LDA SEKDSK STA HSTDSK STA CPMDSK LDA SEKTRK STA HSTTRK STA CPMTRK LDA SEKHST STA HSTSEC LDA RSFLAG ;need to read? ORA A CNZ READHST ;yes, if 1 k fails ; track should be ok, but we need to send it anyway in ; case the last unit was different (there's only one track ; register). then load sector and do it... CALL GETTRK OUT WTRK CALL GETSEC ; check for dsdd to add sector bias if not 'e' drive MOV E,A ; save it for a moment LDA CPMDSK CPI 'E' -41H ; check for 'e' drive MOV A,E ; restore sector number JZ XYZ ; no sector bias for 'e' drive PUSH D ; save sector number in e XRA A ;0 to accum STA HSTWRT ;no pending write MATCH: ;copy data to or from buffer ; edsk: sector mask (secmsk) must be a variable for edisk and ; is calculated by (hstsiz/128)-1. this is three for ampro, ; kaypro, and other 512-byte sectors, and 1 for 256 byte sectors. PUSH B ;save reg LDA SECMSK MOV B,A LDA SEKSEC ;mask buffer number ANA B ;least signif bits POP B ;restore bc contents MOV L,A ;ready to shift MVI H,0 ;double count DAD H DAD H DAD H DAD H DAD reg. CALL GETTYPE POP D ANI 0C0H ; isolate dd and ds indicators CPI 0C0H ; check dd and ds MOV A,E JNZ XYZ ; no bias ADI 16 ; double sided bias XYZ: OUT WSEC CALL SETUP ;select unit and real side CALL GETBUF LDA RWHOST ;see if read (1) or ORA A ; write (0) JZ WRDAT ;branch for writes ; falling through implies this is a read MVI A,FREADS ;read sector command CALL RDATA JNZ RWFAIL RET ;return to bdos, read ok RWFAIL: LXI H,TRIES DCR M ;one less retry JZ  DABORT ;error return CALL MAPTRK MVI M,255 ;force read address JMP WMI ;try again if there DABORT: MVI A,01 ;set error flag STA ERFLAG RET ;return to bdos w/error WAIT: LXI H,50*167 ; 50 ms. WTL: DCX H MOV A,H ORA L JNZ WTL RET WRDAT: PUSH H CALL DWAIT ;wait for permission POP H MVI A,FWRITES ;make it a write command CALL OUTCMD ;wait for cmd to start CALL WR ; write the disk JMP WRTERR ; check for errors and return WR: IN STAT ; get fdc status MOV B,AEAD JMP MT2 ; TRY FOR A GOOD ONE ; procedure dwait: ; wait for permission to write a register DWAIT: LXI H,0 MVI A,TIMEOUT STA TOCNT DWLP: IN STAT ANI 1 JZ DWDONE DCX H MOV A,H ORA L JNZ DWLP ;keep waiting LXI H,TOCNT DCR M LXI H,0 JNZ DWLP MVI A,0D0H ;force int OUT CMND ;give up on waiting MVI A,255 RET DWDONE: IN STAT ;fetch status again RET RESTORE: ;issue a restore command to the controller CALL DWAIT CPI 255 RZ ;ti ; save it for eventual return RAR ; rotate busy bit into carry RNC ; write command is finished RAR ; rotate data request into carry JNC WR ; wait for data request MOV A,M ; get a byte OUT WDAT ; give it to the fdc INX H ; bump the pointer JMP WR ; again WRTERR: MOV A,B ; get fdc status ANI 05CH ;check for errors JNZ RWFAIL RET ; rdata: read data routine. on entry, hl contains buffer, ; a contains read command (which may be read address) RDATA: PUSH PSW PUSH meout, don't bother MVI A,FRESTOR LXI H,STPRAT ORA M CALL OUTCMD CALL DWAIT CALL WAIT ; let drive settle down MVI A,FSTEPIN LXI H,STPRAT ORA M CALL OUTCMD CALL WAIT CALL MAPTRK MVI M,0 RET SEEK: ;seek to alternate track XRA A ;assume side zero STA HSTSID CALL SETUP ;select unit and side (zero is ok) LXI H,IDSAVE ;use id buffer MVI A,FRDADDR CALL RDATA RNZ ;quit if error IN RSEC ;fetch current track again OUT WTRK ;and tell 1770 where we are CALL GH CALL DWAIT POP H POP PSW CALL OUTCMD ;wait for cmd to start CALL RD ; read the disk JMP RDERR ; check for errors and return RD: IN STAT ; get fdc status MOV B,A ; save status for eventual return RAR ; rotate busy bit to carry RNC ; controller has finished RAR ; rotate data request to carry JNC RD ; wait for drq IN RDAT ; get byte from fdc MOV M,A ; put it in memory INX H ; bump the pointer JMP RD ; again RDERR: MOV A,B ; fdc status ANI 01CH ;check foETTRK ;adjust if necessary (and set up side) OUT WDAT ;and tell the fdc where to go CALL SETUP ;set unit and real side CALL DWAIT MVI A,FSEEK ;worst case seek command LXI H,STPRAT ORA M CALL OUTCMD CALL DWAIT ;wait for it to finish ANI 18H ;isolate possible failures RNZ ;seek failed, return w/error ; seek worked, save track in table and return CALL MAPTRK CALL GETTRK ;get physical track MOV M,A XRA A ;ensure zero flag is set RET ;return w/success UNLOG: ;rr errors RET OUTCMD: CALL MOTOR ; insure motor on OC0: OUT CMND ; to fdc MVI A,19 ; wait 66.5 us. for command to set up OC1: DCR A JNZ OC1 RET ; MOTOR-UP-TO-SPEED FOR AMPRO BIOS MOTOR: PUSH H ; SAVE BUFFER POINTER PUSH PSW ; SAVE COMMAND IN STAT ; STATUS RAL ; ROTATE MOTON INTO CARRY JC MOTEND ; MOTORS ALREADY ON, FORGET IT IN RSEC ; GET THE SECTOR NUMBER STA SECTOR ; SAVE IT CALL MT2 ; READ AN ID MOV C,A ; SAVE ID # A MOMENT MT3: CALL MT2 ; READ ANOTHER ID eset logged in disks (allows changing disk type) LXI H,-1 SHLD LTRACK SHLD LTRACK+2 RET MAPTRK: ;this routine returns the ltrack pointer in hl LDA CPMDSK MOV E,A MVI D,0 LXI H,LTRACK DAD D RET SETUP: ;this routine writes to the drvsel register LDA CPMDSK ;fetch unit id CPI 4 ;e disk? PUSH B ;save bc JZ SETEDSK INR A ;setup for decr loop MOV B,A MVI A,1 ;selbits mask SETUP1: DCR B ;are we done? JZ SETUP2 ADD A ;shift selbit left one JMP SETUP1 SETE CMP C ; SAME AS THE FIRST? JNZ MT3 ; TIL SAME ID READ TWICE LDA SECTOR ; GET SECTOR NUMBER OUT WSEC ; TO 1770 MOTEND: POP PSW ; GET COMMAND POP H ; GET POINTER RET ; READ AN ID AND RETURN ITS NUMBER IN A ; MT2: CALL DWAIT ; WAIT FOR CONTROLLER READY MVI A,FRDADDR ; READ ADDRESS COMMAND LXI H,IDSAVE ; PUT IT HERE CALL OC0 ; READ NEXT ID FIELD CALL RD ; READ THE 1770 CALL RDERR ; CHECK FOR GOOD READ ANI 0CH ; RNF OK WITH BLANK DISK LDA IDSAVE+2 ; ID # RZ ; GOOD RDSK: LDA EDSD ; e disk drive MOV C,A INR C XRA A STC SEDL: RAL DCR C JNZ SEDL SETUP2: ;selbits now in a ORI 040H ;turn off eprom LXI H,HSTSID ;point to side select ORA M ;or that in, too MOV B,A CALL GETTYPE RLC ;single density? JNC SETUP3 ;branch if so, else XRA A ;clear accum JMP SETUP4 SETUP3: MVI A,20H ;load single density mask SETUP4: ORA B ;get rest of select back OUT CONT push psw lda chgdsk ora a jz nochange mvi c,hldelay hlwait: mvi  b,189 hlwait2: cmp m dcr b jnz hlwait2 dcr c jnz hlwait mov a,c sta chgdsk nochange: pop psw POP B ;restore saved reg RET GETTYPE: ;get the type of a disk. ; encoding is as follows: ; bit 7: 0 = sd, 1 = dd ; bit 6: 0 = ss, 1 = ds ; bits 3-2: 0 = 1k alloc, 1 = 2k alloc ; bits 1-0: b reg value (bytes/sector) LDA CPMDSK MOV E,A MVI D,0 LXI H,TYPETAB DAD D MOV A,M RET GETSEC: ;convert logical sector to physical sector PUSH H CALL GET;console character output from register c LDA IOBYTE ANI 11B JZ TTYOUT JMP CRTOUT READER: ;read character into register a from reader device JMP TTYIN ;no choices supported PUNCH: ;punch character from register c JMP TTYOUT ;no choices supported LISTST: ;return list status, 0ffh if ready, else 00h LDA IOBYTE ;check device assignment ANI 11000000B ;keep lst bits only JNZ LSTST1 ;tty? JMP TTYOST ;check tty output status LSTST1: ANI 01000000B ;crt? JZ LPTST ;no other choiTYPE ANI 3 ; 128 byte sector? LDA CPMSEC ;nope, do mapping JZ GOTSDS LDA HSTSEC GOTSDS: PUSH PSW ;save sector LDA CPMDSK CALL GETDPT ;fetch pointer to xlt table MOV E,M INX H MOV D,M XCHG ;translate table now in hl POP PSW ;restore desired sector MOV E,A MVI D,0 DAD D MOV A,M ;get physical sector from table MOV E,A ;save it LDA CPMDSK CPI 4 ;was it e disk? JNZ GSEXIT LDA HSTSID ;which side? ORA A JZ GSEXIT ;side zero, ignore LDA ESECADJ ;else getce but lpt JMP CRTOST ;check crt output status LIST: ;list character output from register c CALL LISTST ;wait till ready to send JZ LIST LDA IOBYTE ;check device assignment ANI 11000000B ;keep lst bits only JZ TTYOUT CPI 01000000B JZ CRTOUT JMP LPTOUT ;no fourth choice CRTOST: ;crt output status, return 0ffh if ready to send, 00h if not ;crt output buffer status now checked MVI A,01H ; Check "all sent" bit in register 1 OUT SIOCPA ; . IN SIOCPA ; . ANI 01H ; . RZ  adjustment ADD E MOV E,A ;put it back for move GSEXIT: MOV A,E ;move real sector to a POP H RET GETBUF: ;get the dma address CALL GETTYPE ANI 3 ; 128 byte sectors? LXI H,HSTBUF ;used for dd RNZ LHLD DMAADR ;else use real dma address... RET GETTRK: ;this routine converts logical to physical trk PUSH H CALL GETTYPE LXI H,HSTSID ;point to where we keep it RLC RLC ;test for single sided LDA CPMTRK ;fetch desired logical track JNC SEEK1 ;if single side, lea ;transmit buffer not ready LDA HSA ;see if cts h/s required ORA A JZ CRTRDY ;if 0, no h/s needed ;crt cts handshake signal status now checked MVI A,10H OUT SIOCPA ;update uart status IN SIOCPA ;fetch status ANI CTS RZ ;cts not active CRTRDY: ORI 255 ;show ready to end RET CRTIST: ;crt input status, return 0ffh if data ready, 00h if not IN SIOCPA ;fetch status ANI RDA RZ ;not ready ORI 255 ;got something, signal its presence RET CRTOUT: CALL CRTOST ve it ; we now must convert to real sectors and sides - divide by two ; and make the lsb be the side. the seek command doesn't know ; about sides, and verifies track only. read and write presumably ; should have the side select set properly (as should idread). RAR ;put side bit in carry JNC SEEK1 ;branch if side zero MVI M,10H ;else set mask for later JMP SEEK2 SEEK1: MVI M,0 ;set side zero SEEK2: POP H ;restore entry value ANI 7FH ;make it reasonable RET ; logical  ;ok to send? JZ CRTOUT ;nope, wait MOV A,C ;character to register a OUT SIODPA RET CRTIN: CALL CRTIST ;ready? JZ CRTIN ;nope, wait IN SIODPA ;else fetch it ani 7fh ;strip parity bit RET LPTOUT: ;printer character output from register c CALL LPTST ;printer ready? JZ LPTOUT ;nope, wait MOV A,C OUT PIO1 ;set up the data OUT STBSET ;send a data strobe OUT STBCLR ; (data doesn't matter) RET LPTST: ;return list status, 0ffh if ready, edevice physical device assignments ; -------------- --------------------------- ; con: crt: or tty: ; reader: tty: ; punch: tty: ; list: crt: or tty: or lpt: CONST: ;console status, returns 0ffh if character ready, else 00h LDA IOBYTE ;check device assignment ANI 11B ;keep con bits only JZ TTYIST ;check appropriate input status JMP CRTIST ;no third or fourth choices CONIN: ;console character into register a LDA IOBYTE ANI 11B JZ TTYIN JMP CRTIN CONOUT: lse 00h MVI A,10H ;update uart status OUT SIOCPB IN SIOCPB ;read printer busy signal ANI 10H RZ ;not ready ORI 255 ;show ready RET TTYOST: ;tty output status, return 0ffh if ready to send, 00h if not ;tty output buffer status now checked MVI A,01H ; Check "all sent" bit in register 1 OUT SIOCPB ; . IN SIOCPB ; . ANI 01H ; . RZ ;transmit buffer not ready LDA HSB ;see if cts h/s required ORA A JZ TTYRDY ;if 0, no h/s needed ;tty cts handshake signal sta tus now checked MVI A,10H OUT SIOCPB ;update uart status IN SIOCPB ;fetch status ANI CTS RZ ;cts not active TTYRDY: ORI 255 ;show ready to send RET TTYIST: ;tty input status, return 0ffh if data ready, 00h if not IN SIOCPB ;fetch status ANI RDA RZ ;not ready ORI 255 ;got something, signal its presence RET TTYOUT: CALL TTYOST ;ok to send? JZ TTYOUT ;nope, wait MOV A,C ;character to register a OUT SIODPB RET TTYIN: IN SIOCPB ANI RDA ;char ready? JZ TTYI * ;* memory map of system: * ;* * ;* address range size function * ;* ------------- ------- -------- * ;* 0 - ff 256 b standard cp/m buffers except * ;* 40 - 4a 11 b for zcpr3 external path * ;* 4b 1 b wheel byte * ;* 100 - d7ff ~54 k tpa * ;* d800 - dfff 2 k zcpr3 command processor * ;* e000 - eeff 3.5k bdosz * ;* ef00 - fcff 3.5k cbiosz with buffers * ;* fd00 - fd00 0 k resident command package * ;* fd00 - fd00 0 kN ;nope, wait IN SIODPB ;else fetch it ani 7fh ;strip parity bit RET ; the following code is one-time only BOOT: ;cold boot parameter initialization MVI A,40H ;turn off eprom OUT CONT LDA IOBYT ; get iobyte value STA IOBYTE LXI SP,80H CALL IOINIT LXI H,LOGMSG CALL PUTS ;put sign-on msg on screen CALL UNLOG IF ZCPR3 ;the following code is required for zcpr3 ;expath equ 40h ;ext file search-path base ;z3whl equ 4bh ; wheel byte ;z3cl equ 0ff00h ;multi-cmd b flow command package * ;* fd00 - fd00 0 b memory-based named directory * ;* fd00 - fd7f 128 b zcpr3 shell stack * ;* fd80 - fdcf 80 b zcpr3 message buffers * ;* byte 0: error flag (z/nz) * ;* byte 1: if (8 levels) * ;* byte 2: if active (8 levels) * ;* byte 3: z3 cmd status * ;* 00b - normal * ;* 01b - shell * ;* 10b - error * ;* bytes 4&5: error address if 10b * ;* byte 6: program error code * ;* byte 7: zex message byte * ;* 00b - normauffer storage ;z3cls equ 200 ;..and buffer length ;z3env equ 0fe00h ;named directory (d/u) storage ;shstk equ 0fd00h ;base of 32 byte shell buffers ;z3msg equ 0fd80h ;zcpr3 messages JMP ZBOOT ;go to movement routines * SYSTEM SEGMENT: SYSTEM.ENV * AUTHOR: RICHARD CONN ; program: sysenv.asm ; author: richard conn ; version: 1.0 ; date: 22 feb 84 ; previous versions: none ; ; this version revised for 61k ampro and to assemble with asm.com ; for addition to the ampro bios. 6l * ;* 01b - z3 prompt * ;* 10b - suspend intercept * ;* byte 8: zex running flag (0=no) * ;* bytes 9-10: address of next * ;* char for zex to return * ;* bytes 11-12: address of first * ;* char in zex memory- * ;* based file buffer * ;* byte 13: sh control byte * ;* bit 0: enable shcmt * ;* bit 1: enable shecho * ;* bit 7: enable shell * ;* entry wait * ;* bytes 14-15: shell scratch * ;* bytes 10h-2fh: error cmd * ;* bytes 30h-39h: apr 84 jww ; ; sysenv is the definition for my zcpr3 environment, and it is loaded ; as my zcpr3 environment descriptor by z3ldr. sysenv is named to sys.env ; after assembly to permit this. ; ; ; environment definitions ; ; maclib z3base ; maclib sysenv ;* 61k zcpr3 for ampro ;**************************************************************** ;* * ;* z3base.lib -- base addresses for zcpr3 system on ampro * ;* bookshelf computer by echelon, inc. * ;* * ;* these addre registers * ;* bytes 3ah-3fh: reserved * ;* bytes 40h-4fh: user-defined * ;* fdd0 - fdff 48 b zcpr3 external fcb * ;* fe00 - feff 256 b environment descriptors * ;* bytes 00h-7fh: z3 parameters * ;* bytes 80h-ffh: z3 terminal cap * ;* ff00 - ffcf 208 b multiple command line buffer * ;* ffd0 - ffff 48 b zcpr3 external stack * ;* * ;**************************************************************** ; ; true and false ; ;false equ 0 ;true equ not false ;* ;sses are used by the following system * ;* segments: * ;* * ;* segment function * ;* ------- -------- * ;* ampboot boot system from floppy disk * ;* bdosz customized bdos * ;* cbiosz customized bios * ;* zcpr3 zcpr3 command processor * ;* *.env all environment descriptors * ;* *.fcp all flow command packages * ;* *.iop all input/output packages * ;* *.ndr all named directory definition files * ;* *.rcp all resident command packages * ;* * ;* * zcpr3 base equates ;* ; ; 1. version numbers, memory size, and cp/m base address ; ; the following equates define the version numbers of the zcpr3 ; command processor and the cbiosz. they also explicitly state the size ; of the tpa for inclusion in the cbiosz header printed at cold boot. ; Z3REV EQU 30 ; zcpr3 rev number ;msize equ 54 ; size of tpa ; ; base - base address of user's cp/m system (normally 0 for dr version) ; this equate allows easy modification by non-standard  cp/m (eg,h89) ; ;base equ 0 ; ; 2. processor selection ; ; the following equate selects the use of the 8080/8085 micro or ; the z80 micro for the target for zcpr3. note that selecting the ; 8080/8085 should be done only if you have an 8080 or 8085. if you have ; a z80, by all means select this one since the code is much smaller and ; you can cram more features into the system as a result. ; if the processor is an 8080 or 8085, set this equate to true. ; if the processor is a z80, set it toor in 128-byte blocks ; ; 10. shell stack ; ; the following equates define the address of the zcpr3 shell stack, ; the number of entries permitted in the zcpr3 shell stack, and the size ; of each entry in the shell stack in terms of bytes. if there is no zcpr3 ; shell stack, all three values should be 0. ; SHSTK EQU 0FD00H ; zcpr3 shell stack SHSTKS EQU 4 ; number of shsize-byte shell stack entries SHSIZE EQU 32 ; size of a shell stack entry ; (stack size = shstks * shsize) ; ; 1 false. ; ;i8080 equ false ; ; 3. external path ; ; the following equates define the address of the zcpr3 external ; path and the number of two-byte elements contained in this path (maximum). ; if there is no zcpr3 external path, both of these values should be set to 0. ; EXPATH EQU 40H ; external path EXPATHS EQU 5 ; 5 2-byte path elements ; (path size = expaths*2 + 1) ; ; 4. wheel byte ; ; the following equate defines the address of the zcpr3 wheel byte. ; if there is no zcpr3 w1. zcpr3 messages ; ; the following equate defines the address of the zcpr3 message buffer. ; this buffer is always 80 bytes long. if there is no zcpr3 message buffer, ; this address should be 0. ; Z3MSG EQU 0FD80H ; zcpr3 message buffer ; ; 12. external fcb ; ; the following equate defines the address of the zcpr3 external fcb. ; this buffer is always 36 bytes long. if there is no zcpr3 external fcb, ; this address should be 0. ; EXTFCB EQU 0FDD0H ; zcpr3 external fcb ; ; 13. nameheel byte, this value should be set to 0. ; Z3WHL EQU 4BH ; wheel byte address ; ; 5. ccp location ; ; the following equate defines the address of the zcpr3 command ; processor. this address must be supplied. ; ;ccp equ 0d800h ; zcpr3 command processor ; ; 6. rcp location ; ; the following equates define the address of the zcpr3 resident ; command package and its size in 128-byte blocks. if there is no ; zcpr3 resident command package, both of these values should be 0. ; RCP EQU 0d directory buffer ; ; the following equates define the address and size (in terms of 18-byte ; entries) of the zcpr3 named directory buffer. if there is no such buffer, ; both of these values should be 0. ; Z3NDIR EQU 00000H ; zcpr3 named directory area Z3NDIRS EQU 00 ; 00 18-byte named directory elements permitted ; (ndir size = z3ndirs*18 + 1 for trailing 0) ; ; 14. command line ; ; the following equates define the address and size (in terms of bytes) ; of the zcpr3 command line bu0000H ; resident command package RCPS EQU 00 ; 00 128-byte blocks (0k bytes) ; ; 7. iop location ; ; the following equates define the address of the zcpr3 input/output ; package and its size in 128-byte blocks. if there is no zcpr3 input/output ; package, both of these values should be 0. ; IOP EQU 00000H ; redirectable i/o package IOPS EQU 00 ; 00 128-byte blocks (0k bytes) ; ; 8. fcp location ; ; the following equates define the address of the zcpr3 flow command ; package and itsffer (formerly called the multiple command line ; buffer under zcpr2). if there is no such buffer, both of these values should ; be 0. ; Z3CL EQU 0FF00H ; zcpr3 command line buffer Z3CLS EQU 200 ; size of command line buffer ; ; 15. external stack ; ; the following equate defines the address of the zcpr3 external stack. ; this stack is always 48 bytes in size. if there is no such stack, this ; value should be 0. ; EXTSTK EQU 0FFD0H ; zcpr3 external stack ; ; 16. user equates ; ; t size in 128-byte blocks. if there is no zcpr3 flow command ; package, both of these values should be 0. ; FCP EQU 00000H ; flow command package FCPS EQU 0 ; 0 128-byte blocks (0k bytes) ; ; 9. env location ; ; the following equates define the address of the zcpr3 environment ; descriptor and its size in 128-byte blocks. if there is no zcpr3 environment ; descriptor, both of these values should be 0. ; Z3ENV EQU 0FE00H ; environment descriptors Z3ENVS EQU 2 ; size of environment descripthe following equates are available for the implementer's target ; system. these are implementation-defined. ; ;* ;* end of zcpr3 base equates ;* ; ; include environment descriptor ; ; org 100h ; origin ENV: JMP 0 ; leading jmp ; sysenv ; end ; library: sysenv.lib ; author: richard conn ; version: 1.0 ; date: 22 feb 84 ; previous versions: none ; ; sysenv is the definition for the ampro zcpr3 environment. ; ;sysenv macro ; ; environment descriptor ; if inline,  there is a leading jmp just before this ; ENVORG1: DB 'Z3ENV' ; environment id DB 2 ; class 2 environment (internal) DW EXPATH ; external path address DB EXPATHS ; number of 2-byte elements in path DW RCP ; rcp address DB RCPS ; number of 128-byte blocks in rcp DW IOP ; iop address DB IOPS ; number of 128-byte blocks in iop DW FCP ; fcp address DB FCPS ; number of 128-byte blocks in fcp DW Z3NDIR ; ndr address DB Z3NDIRS ; number of 18-byte entries in ndr bilities data ; ENVORG2: DB ' ' ;name of terminal (none) DB 'E'-'@' ;cursor up DB 'X'-'@' ;cursor down DB 'D'-'@' ;cursor right DB 'S'-'@' ;cursor left DB 00 ;cl delay DB 00 ;cm delay DB 00 ;ce delay DB 0 ;cl string DB 0 ;cm string DB 0 ;ce string DB 0 ;so string DB 0 ;se string DB 0 ;ti string DB 0 ;te string DS 32-($-ENVORG2) ; make exactly 32 bytes long ; ; end of environment descriptor ; ; endm CMDSET: DW Z3CL+4 ; DW Z3CL ; zcpr3 command line DB Z3CLS ; number of bytes in command line DW Z3ENV ; zcpr3 environment descriptor DB Z3ENVS ; number of 128-byte blocks in descriptor DW SHSTK ; shell stack address DB SHSTKS ; number of shsize-byte entires in shell stack DB SHSIZE ; size of a shell stack entry DW Z3MSG ; zcpr3 message buffer DW EXTFCB ; zcpr3 external fcb DW EXTSTK ; zcpr3 external stack DB 0 ; quiet flag (1=quiet, 0=not quiet) DW Z3WHL ; address of wheel byte point to first char in command buf DB Z3CLS ;buffer size DW 0 ;buffer empty PATH: DB '$',0 ;current drive and user 0 DB 1,'$' ;drive 'a' and current user DB 1,0 ;drive 'a' and user 0 DB 1,15 ;drive 'a' and user 15 DB '$',15 ;current drive and user 15 DB 0 ;end of path ZERO: ; clear (hl) for (b) bytes XRA A ; clear a to zero ZL: MOV M,A ; move it to memory INX H ; bump pointer DCR B ; count down JNZ ZL ; u DB 4 ; processor speed in mhz DB 'E'-'@' ; maximum disk DB 31 ; maximum user DB 1 ; 1=ok to accept du, 0=not ok DB 0 ; crt selection (0=crt 0, 1=crt 1) DB 0 ; printer selection (n=printer n) DB 80 ; width of crt 0 DB 24 ; number of lines on crt 0 DB 22 ; number of lines of text on crt 0 DB 80 ; width of crt 1 DB 24 ; number of lines on crt 1 DB 22 ; number of lines of text on crt 1 DB 80 ; width of printer 0 DB 66 ; number of lines on printer 0 DB 58 ;ntil zero RET LDIR: ; move bc bytes from (hl) to (de) MOV A,M STAX D INX H INX D DCX B MOV A,B ORA C JNZ LDIR RET ZBOOT: LXI B,3 ;move data structures to storage areas LXI H,CMDSET LXI D,Z3CL CALL LDIR LXI B,10 LXI H,AUTOCMD LXI D,Z3CL+3 CALL LDIR LXI B,11 ; number of bytes in path LXI H,PATH LXI D,EXPATH CALL LDIR LXI H,Z3WHL ; clear wheel byte MVI M,0 LXI H,SHSTK ; clear shell stack MVI M,0 LXI H,Z3MSG MVI B,80 CALL ZERO L number of lines of text on printer 0 DB 1 ; form feed flag (0=can't formfeed, 1=can) DB 102 ; width of printer 1 DB 66 ; number of lines on printer 1 DB 58 ; number of lines of text on printer 1 DB 1 ; form feed flag (0=can't formfeed, 1=can) DB 80 ; width of printer 2 DB 66 ; number of lines on printer 2 DB 58 ; number of lines of text on printer 2 DB 1 ; form feed flag (0=can't formfeed, 1=can) DB 80 ; width of printer 3 DB 66 ; number of lines on printer 3 DB 58 XI H,ENV LXI D,Z3ENV LXI B,128+32 CALL LDIR ENDIF ; zcpr3 XRA A ;zero in the accum STA CDISK ;select disk zero STA HSTACT ;host buffer inactive STA UNACNT ;clear unalloc count STA HSTSID ;assume side zero JMP GOCPM ;initialize and go to cp/m LOGMSG: DB CR,LF,LF,'AMPRO ' DB MSIZE/10 +'0',MSIZE MOD 10 +'0','k CP/M vers 2.2' DB CR,LF,' BIOS Version ',VERS/10+'0','.',VERS MOD 10+'0' DB CR,LF,0 ORG BOOT UNINIT EQU $ SEKDSK: DS 1 ;seek disk number SEK ; number of lines of text on printer 3 DB 1 ; form feed flag (0=can't formfeed, 1=can) DB 'SH ' ; shell variable filename DB 'VAR' ; shell variable filetype DB ' ' ; filename 1 DB ' ' ; filetype 1 DB ' ' ; filename 2 DB ' ' ; filetype 2 DB ' ' ; filename 3 DB ' ' ; filetype 3 DB ' ' ; filename 4 DB ' ' ; filetype 4 DS 128-($-ENVORG1+3) ; make exactly 128 bytes long ; (+3 compensates for leading jmp) ; ; terminal capaTRK: DS 1 ;seek track number SEKSEC: DS 1 ;seek sector number HSTDSK: DS 1 ;host disk number HSTTRK: DS 1 ;host track number HSTSEC: DS 1 ;host sector number CPMDSK: DS 1 ;single density dsk parm CPMTRK: DS 1 ; and trk CPMSEC: DS 1 ; and sector SEKHST: DS 1 ;seek shr secshf HSTACT: DS 1 ;host active flag HSTWRT: DS 1 ;host written flag UNACNT: DS 1 ;unalloc rec cnt UNADSK: DS 1 ;last unalloc disk UNATRK: DS 1 ;last unalloc track UNASEC: DS 1 ;last unalloc sector CPMSP T: DS 1 ; logical sectors per track SECMSK: DS 1 ; sector mask SECSHF: DS 1 ; sector shift MUNACT: DS 1 ; unallocated count value CHGDSK: ds 1 ; changed disk flag ERFLAG: DS 1 ;error reporting RSFLAG: DS 1 ;read sector flag READOP: DS 1 ;1 if read operation WRTYPE: DS 1 ;write operation type DMAADR: DS 2 ;last dma address HSTBUF: DS 1024 ;host buffer ; ampro bios-specific storage IDSAVE: DS 6 ;read address buffer area LTRACK: DS 5 ;last track accessed TRIES: DS 1 ;number * * * * * * * * * * * * * * * * * * * * * * * * * * * * ; ; Floppy-related features: ; ; o Automatic sensing of 4 AMPRO formats ; ; o Automatic 48tpi (double-stepping) support in 96tpi drives. ; WARNING: writes are allowed, but not recommended. ; ; o "E-drive" supports wide variety of non-AMPRO formats ; ; o Console beeps on attempt to read/write a drive without a ; floppy in it, but only on first access since CTRL-C. ; ; o A step rate per physical drive is now supported, and 2ms anof times to do it RWHOST: DS 1 ;local read/write flag HSTSID: DS 1 ;host disk side select mask TOCNT: DS 1 ;timeout loop counter SECTOR: DS 1 ;temporary storage ; special e disk parameters - filled in when e disk is ; first selected, and used in deblocking. ESECADJ: DS 1 ;sector number adjust for side 1 DIRBUF: DS 128 ;directory access buffer ALV0: DS 50 CSV0: DS 64 ALV1: DS 50 CSV1: DS 64 ALV2: DS 50 CSV2: DS 64 ALV3: DS 50 CSV3: DS 64 ALV4: DS 50 CSV4: DS 64 ENDDAT EQU $d ; 3ms step rates are supported with the 1772 FDC. ; ; o 8" data rates implemented on a per logical drive basis. ; NOTE: Most 1770/1772 FDC chips cannot support this rate. ; ; Hard-disk related features: ; ; o Hard disk support is SCSI generic and installed by HINIT ; as a run-time option, or by boot EPROM after use of HGEN. ; ; o Bus (SCSI) arbitration is an assembly-time option. ; ; o Each hard disk partition has its own DPB, which allows ; flexible partitioning of any disk drive, u DATSIZ EQU $-UNINIT RESERVE EQU 0FD00H ; memory reserved above here REMAINS EQU RESERVE-ENDDAT END BIOS p to 88Mb. ; ; o A direct SCSI call is available as an extended BIOS JMP. ; ; o A SCSI burst/byte option allows some controllers to ; operate at optimum rates. Set by HINIT. ; ; Other features: ; ; o Generic ZCPR3 support. Easily extendable via the AMPRO ; EXTENDED ZCPR3 PACKAGES. ; ; o Console input buffer, via software polling. ; ; o Real time clock, via software polling. ; ; * * * * * * * * ; ; Revision history: ; ; Ver Date Who Description ; --- ----- --- ------------------------------------------ ; 3.8 F4.10 FSW Now include Initiator SCSI ID in select. ; Added SCSI I/O retries. ; Added buffered console and RTC options. ; Added local stack for above two options. ; Fixed problem in SELEND routine. ; Various speed ups and clean ups. ; BEEP is now an option. ; improved WBOOT option for ZRDOS+. ; Now clear PUBLIC bytes for ZRDOS+. ; ; 3.5 - 3.7 Internal unreleased versions ; ; 3.4 EA.30 RJB Moved SCSI mode byte out of buffer area; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ; * * ; * AMPRO Computers, Inc. BIOS Version 3 * ; * * ; * Copyright (C) 1983,1984,1985,1986 * ; * AMPRO Computers, Inc. * ; * All rights reserved. * ; * * ; * This BIOS is designed for use with AMPRO hardware only. * ; * All other use is prohibited without prior written consent * ; * from AMPRO Computers, Inc. * ; * * ; * * * * . ; RBL Added bios hooks for ZCPR3 IOP. Added ; floppy control parameters to cold boot ; code. Set "startup" as standard cold boot ; command (AUTOCMD). Modified SCSI routines ; to provide better internal status when in ; a multi-master environment. Changed SCSI ; bus reset to assembly option (new SCSI ; boot EPROM does it -- if BIOS does it ; again, problems can occur.) ; ; 3.3 E8.21 RJB Changed RESTORE logic to wait longer for ; the FDC to setup. Corrected sector numbe r ; problem with Kaypro 4/10 format. Added ; routine to set memory from the end of BIOS ; thru 0FFFFH to zero in order to support ; ZCPR3 resident system segment options (RCP ; FCP, IOP, etc). ; ; 3.2 E7.30 RJB Removed 2 nested "IF" statements in the ; hard disk section which generated extra ; (unused) code in the floppy-only version. ; NO FUNCTIONAL CHANGES. ; ; 3.1 E7.02 RJB Production release of version 3 bios. ; ; 3.0 E5.21 RJB Beta release of version 3 bios. ; ; * pin 9. ; CLOCK EQU YES ; real time clock LOCAL$STACK EQU BUFFKBD OR CLOCK ; if those options are in, ; local stack is recommended. INTERNAL EQU NO ; internal (unreleased) bios? ; Z-80 opcode equates (reversed so we can use a DW to enter them) CPI80 EQU 0A1EDH ; CPI (0edh,0a1h) CPIR80 EQU 0B1EDH ; CPIR (0edh,0b1h) INIR80 EQU 0B2EDH ; INIR (0edh,0b2h) LDIR80 EQU 0B0EDH ; LDIR (0edh,0b0h) OTIR80 EQU 0B3EDH ; OTIR (0edh,0b3h) INI80 EQU 0A2EDH ; INI (0edh,0a2h) OUTI* * * * * * * ; Bios version and date VERS EQU 38 ; Current version THIS$MONTH EQU 04 ; Today's month THIS$DAY EQU 11 ; day THIS$YEAR EQU 86 ; year INT$REV EQU 02 ; Internal revision number ; TRUE and FALSE NO EQU 0 FALSE EQU 0 YES EQU NOT FALSE TRUE EQU NOT FALSE ; Customization equates: ; The following equate determines the amount of system memory ; available for hard disk buffers and other options: MSIZE EQU 58 ; 60K = Floppy only (must set ZCPR3 and/or HARD$DIS80 EQU 0A3EDH ; OUTI (0edh,0a3h) SBCD80 EQU 043EDH ; SBCD (0edh,043h) LBCD80 EQU 04BEDH ; LBCD (0edh,04bh) SDED80 EQU 053EDH ; SDED (0edh,053h) LDED80 EQU 05BEDH ; LDED (0edh,05bh) SSPD80 EQU 073EDH ; SSPD (0edh,073h) LSPD80 EQU 07BEDH ; LSPD (0edh,07bh) SIXD80 EQU 022DDH ; SIXD (0ddh,022h) LIXD80 EQU 02ADDH ; LIXD (0ddh,02ah) SIYD80 EQU 022FDH ; SIYD (0fdh,022h) LIYD80 EQU 02AFDH ; LIYD (0fdh,02ah) SRLA EQU 02FCBH ; SRL A ; Bit SET/RESET/TEST Z-80 opcode equates (useK to NO) ; 59K = Floppy + 10Mb Hard ; 58K = Floppy + 42Mb Hard ; 57K = Floppy + 74Mb Hard ; 56K = Floppy + 88Mb Hard ZCPR3 EQU YES ; enable ZCPR3? ;The following equate is YES, the BDOS will not be reloaded ;by warm boot. Great with ZRDOS version 1.4 and higher. ONLY$WBOOT$CCP EQU YES ; do not warm boot BDOS ;The following equate should be normally set to NO. MAX$DRIVE EQU NO ; enable max drive checking? HARD$DISK EQU YES ; enable hard disk routines? ARBITRATION EQU NO  DB to enter) ; Example: SET 7,D would be DB BIT,BSET+B7+ZD BIT EQU 0CBH ; Bit prefix BTST EQU 040H ; Bit test BRES EQU 080H ; Bit reset BSET EQU 0C0H ; Bit set B0 EQU 000H ; Bit 0 B1 EQU 008H ; Bit 1 B2 EQU 010H ; Bit 2 B3 EQU 018H ; Bit 3 B4 EQU 020H ; Bit 4 B5 EQU 028H ; Bit 5 B6 EQU 030H ; Bit 6 B7 EQU 038H ; Bit 7 ZB EQU 000H ; B Reg ZC EQU 001H ; C Reg ZD EQU 002H ; D Reg ZE EQU 003H ; E Reg ZH EQU 004H ; H Reg ZL EQU 005H ; L Reg ZM EQU; use arbitrated SCSI select? ;The following equate should be set to NO if you have the ;hard disk boot eprom. SCSI$RESET EQU NO ; reset SCSI bus if ID=7? ;Set the BEEP option to NO for systems without consoles. BEEP EQU YES ; beep on non-existent floppies ;When BUFFKBD is YES, a polled keyboard buffer allows type ;ahead, within the limitations of CP/M and CP/M applications. BUFFKBD EQU YES ; keyboard buffer BUFFSIZE EQU 80 ; size of keyboard buffer ;When CLOCK is YES, a polled real ti 006H ; M Reg ZA EQU 007H ; A Reg ; Jump relative opcode equates (use DB to enter) ; Example: JR AGAIN would be DB JR,AGAIN-$-1 JR EQU 018H ; JR addr JRNZ EQU 020H ; JR NZ,addr JRZ EQU 028H ; JR Z,addr JRNC EQU 030H ; JR NC,addr JRC EQU 038H ; JR C,addr DJNZ EQU 010H ; DCR, JR NZ addr ; IX and IY prefixes (use DB to enter) IX EQU 0DDH ; IX prefix IY EQU 0FDH ; IY prefix ; CP/M internals BIAS EQU (MSIZE-20)*1024 ; CP/M bias CCP EQU 3400H+BIAS ; CCP starting adme clock is available. ;The LITTLE BOARD must CTC ZC/TC2 connected to CLK/TRG3: ; - On assemblies which have the SCSI option, whether ; populated or not, short JMP2 pins 1 and 2, ; and check that JMP2 pins 2 and 3 are unshorted. NOTE: ; some boards have a trace between JMP2 pins 2 and 3 on ; the bottom of the board, which must be cut. ; - On the original LITTLE BOARD, without the SCSI ; option, the trace to the CTC, U6, pin 20 must be cut. ; then wire U6 pin 20 to U6 dress BDOS EQU CCP+806H ; BDOS starting address BIOS EQU CCP+1600H ; BIOS starting address IF ONLY$WBOOT$CCP NSECTS EQU (BDOS-CCP)/128 ; warm boot sector count ENDIF ; just reload CCP IF NOT ONLY$WBOOT$CCP NSECTS EQU (BIOS-CCP)/128 ; Warm boot sector count ENDIF ; reload CCP and BDOS ; CP/M externals CDISK EQU 0004H ; Current disk: 0=A,...,15=P IOBYTE EQU 0003H ; Intel I/O byte ; CP/M to host disk constants WRALL EQU 0 ; Write to allocated WRDIR EQU 1 ; Write to  directory WRUAL EQU 2 ; Write to unallocated ; Ampro hardware port equates CONT EQU 00H ; Ampro system control port PIO1 EQU 001H ; Parallel printer STBSET EQU 002H ; Set print strobe STBCLR EQU 003H ; Clear print strobe CTCA0 EQU 040H ; Clock/timer channel 0 CTCA1 EQU 050H ; Clock/timer channel 1 CTCA2 EQU 060H ; Clock/timer channel 2 CTCA3 EQU 070H ; Clock/timer channel 3 SIODPA EQU 080H ; Serial port A - data SIOCPA EQU 084H ; Serial port A - control SIODPB E+3 ; (RW) Target command register NCRCSBS EQU NCRBASE+4 ; (R) Current SCSI bus status NCRSER EQU NCRBASE+4 ; (W) Select enable register NCRBSR EQU NCRBASE+5 ; (R) Bus & status register NCRSDS EQU NCRBASE+5 ; (W) Start DMA send NCRIDR EQU NCRBASE+6 ; (R) Input data register NCRSDTR EQU NCRBASE+6 ; (W) Start DMA target receive NCRRPI EQU NCRBASE+7 ; (R) Reset parity/interrupt NCRSDIR EQU NCRBASE+7 ; (W) Start DMA initiator receive NCRDACK EQU NCRBASE+8 ; (RW) DACK pseudo-DMA register BSYBEQU 088H ; Serial port B - data SIOCPB EQU 08CH ; Serial port B - control CMND EQU 0C0H ; Disk controller command WTRK EQU CMND+1 ; Disk controller write track WSEC EQU CMND+2 ; Disk controller write sector WDAT EQU CMND+3 ; Disk controller write data STAT EQU 0C4H ; Disk controller status RTRK EQU STAT+1 ; Disk controller read track RSEC EQU STAT+2 ; Disk controller read sector RDAT EQU STAT+3 ; Disk controller read data ; DART masks RDA EQU 01H ; DART recieve data aIT EQU 08H ERROR EQU 02H ; Current SCSI bus status (NCRCSBS) NCRRST EQU 10000000B ; Reset NCRBSY EQU 01000000B ; Busy NCRREQ EQU 00100000B ; Request NCRMSG EQU 00010000B ; Message NCRCD EQU 00001000B ; Control/Data NCRIO EQU 00000100B ; Input/Output NCRSEL EQU 00000010B ; Select NCRDBP EQU 00000001B ; Data bus parity ; Character equates CTRLC EQU 'C'-'@' ; Ctrl-C (Break, abort) BELL EQU 'G'-'@' ; Ctrl-G (Bell) BSP EQU 'H'-'@' ; Ctrl-H (Backspace) TAB EQU 'I'-'@' ; Ctvailable TBE EQU 04H ; DART transmit buffer empty DCD EQU 08H ; DART data carrier detect CTS EQU 20H ; DART clear to send ; FDC masks, commands, and constants FRESTOR EQU 008H ; FDC restore FSEEKNV EQU 018H ; FDC seek, no verify FSEEK EQU 01CH ; FDC seek, with verify FREADS EQU 088H ; FDC read sector FWRITES EQU 0A8H ; FDC write sector FRDADDR EQU 0C8H ; FDC read address FNS EQU 008H ; 0=spin-up, 1=no spin-up FVF EQU 004H ; 0=no verify, 1=verify FNP EQU 002H ; 0=rl-I (Tab) LF EQU 'J'-'@' ; Ctrl-J (Line feed) CR EQU 'M'-'@' ; Ctrl-M (Carriage return) NAK EQU 'U'-'@' ; Ctrl-U CAN EQU 'X'-'@' ; Ctrl-X (Cancel) ESC EQU 1BH ; Ctrl-[ (Escape) DEL EQU 7FH ; (Delete) ; Other equates DELSEND EQU 28 DDLSPT EQU 40 ; ZCPR3 equates IF ZCPR3 Z3REV EQU 30 ; ZCPR 3.0 Z3ENV EQU 0FE00H ; Z3 environment descriptor Z3ENVS EQU 2 ; Size in 128-byte blocks RCP EQU 00000H ; Resident command package RCPS EQU 0 ; Size in 128-byte blocks precomp, 1=no precomp FRETRY EQU 3 ; Number of floppy retries HLDELAY EQU 35 ; Head load delay (ms) DSBIAS EQU 16 ; Double sided sector bias ; Disk type byte definitions: ; Bit: 76543210 ; Density x 0=single 1=double ; Sides x 0=single 1=double ; Sector #'s x 0=same 1=continuous ; Track count x 0=down 1=down front, up back ; Alloc unit xx 00=1K 01=2K 10=4K 11=8K ; Sector size xx 00=128 01=256 10=512 11=1024 ; IOP EQU 00000H ; Redirectable I/O package IOPS EQU 0 ; Size in 128-byte blocks FCP EQU 00000H ; Flow command package FCPS EQU 0 ; Size in 128-byte blocks Z3NDIR EQU 00000H ; Named directory area Z3NDIRS EQU 0 ; Size in 18-byte blocks Z3CL EQU 0FF00H ; Z3 command line buffer Z3CLS EQU 200 ; Size in bytes SHSTK EQU 0FD00H ; Shell stack SHSTKS EQU 4 ; Number of shell elements SHSIZE EQU 32 ; Size of a shell entry EXTSTK EQU 0FFD0H ; Z3 external stack EXPATH E Bit: 76543210 SSDD48 EQU 10000110B ; DD,SS,same,down,2K,512 DSDD48 EQU 11000110B ; DD,DS,same,down,2K,512 SSDD96 EQU 10000111B ; DD,SS,same,down,2K,1024 DSDD96 EQU 11000111B ; DD,DS,same,down,2K,1024 ; NCR controller equates NCRBASE EQU 20H ; Base address of NCR 5380 NCRCSD EQU NCRBASE+0 ; (R) Current SCSI data register NCRODR EQU NCRBASE+0 ; (W) Output data register NCRICR EQU NCRBASE+1 ; (RW) Initiator command register NCRMR EQU NCRBASE+2 ; (RW) Mode register NCRTCR EQU NCRBASQU 40H ; Location of exernal path EXPATHS EQU 5 ; 5 2-byte path elements Z3WHL EQU 4BH ; Wheel byte location Z3MSG EQU 0FD80H ; Z3 message buffers EXTFCB EQU 0FDD0H ; Z3 external FCB ENDIF ; * * * * * * * * ; ; C A V E A T E M P T O R ; ; WARNING: The address offsets from BIOS thru BIOS+017FH must remain ; the same. Any changes in these offsets will cause incompatibility ; with the AMPRO system utilities. ; ; YOU HAVE BEEN WARNED ... ; ; * * * * * * * * ORG  BIOS ; Bios starting location JMP BOOT ; Cold start WBOOTE: JMP WBOOT ; Warm start JMP CONST ; Console status JMP CONIN ; Console character in JMP CONOUT ; Console character out JMP LIST ; List character out JMP PUNCH ; Punch character out JMP READER ; Reader character in JMP HOME ; Seek to home position JMP SELDSK ; Select disk JMP SETTRK ; Set track number JMP SETSEC ; Set sector number JMP SETDMA ; Set DMA address JMP READ ; Read disk JMP WRITE ; Write disk set by the CONFIG program, and is used as the ; initial value for the IOBYTE (location 0003H) on cold boot. ; ; CP/M defines the following IOBYTE devices: ; ; Bit: 76543210 ; xx Console (0=TTY:, 1=CRT:, 2=BAT:, 3=UC1:) ; xx Reader (0=TTY:, 1=PTR:, 2=UR1:, 3=UR2:) ; xx Punch (0=TTY:, 1=PTP:, 2=UP1:, 3=UP2:) ; xx List (0=TTY:, 1=CRT:, 2=LPT:, 3=UL1:) ; ; In the AMPRO 3.0 bios, the devices are mapped to the serial ; and parallel ports as follows: ; ; CRT: Serial Por JMP LISTST ; Return list status JMP SECTRAN ; Sector translate ; Ampro specific bios calls JMP GETTBL ; Point to more jumps JMP GETEDSK ; Get ptr to E-disk table JMP IOINIT ; Set new I/O parameters IF HARD$DISK JMP SCSI ; SCSI direct driver ENDIF IF NOT HARD$DISK MVI A,0FFH ; Dummy SCSI direct driver RET ENDIF ; * * * * * * * * ; ; Current system disk ; ; * * * * * * * * ORG BIOS+03FH SYSDSK DB 0 ; Assume A: for now ; NOTE: BIOS+040H thru BIOS+07FH modt A All other devices are undefined. ; TTY: Serial Port B UC1: PTR: UR1: UR2: ; LPT: Parallel port UL1: PTP: UP1: UP2: ; ; * * * * * * * * ORG BIOS+061H IOBYT: DB 81H ; 10 00 00 01 Initial values: ; || || || || ; || || || \= CON: Serial port A (CRT:) ; || || \==== RDR: Serial port B (TTY:) ; || \======= PUN: Serial port B (TTY:) ; \========== LST: Parallel port (LPT:) ; * * * * * * * * ; ; AutoStart command ; ; This value is set by the CONFIG prified by CONFIG ; * * * * * * * * ; ; Initialization parameters for the CTC and DART/SIO ; ; These values are set by the CONFIG program, and specify the ; CTC and DART/SIO initialization parameters. Each CTC channel ; requires two bytes of initialization, while each DART/SIO ; channel may have up to 10 bytes of initialization information. ; ; * * * * * * * * ORG BIOS+040H CTCVAL: IF CLOCK DB 47H,13,47H,13 ; ctc0,ctc1 DB 25H,125 ; ctc2 DB 55H,125 ; ctc3 ENDIF IF NOT ogram, and is used as the ; initial command to ZCPR3. This command is ignored in the CP/M ; CCP. ; ; The format of this region is: ; ; Command length -- 1 byte ; Command text -- 8 bytes (max) ; Trailing zero -- 1 byte ; ; * * * * * * * * ORG BIOS+062H AUTOCMD: DB 8,'STARTUP ',0 ; Cmd length + cmd + 0 terminator ; * * * * * * * * ; ; Handshake required flags for DART/SIO channels A and B ; ; These values are set by the CONFIG program, and indicate if ; hardware handshakingCLOCK DB 47H,13,47H,13,3,3,3,3 ; CTC0,CTC1,CTC2,CTC3 ENDIF SIOAVAL: DB 4,46H,5,0EAH,3,0C1H,0,0,0,0 ; DART/SIO channel A SIOBVAL: DB 4,46H,5,0EAH,3,0C1H,0,0,0,0 ; DART/SIO channel B ; * * * * * * * * ; ; Maximum drive letter available: ; ; This value is installed by the CONFIG program, and is used by ; the SELECT routine (optionally) to avoid selecting any disk ; units above the maximum letter. This variable was previously ; the NDSKS (# of floppy disks available) parameter in the is required for each of the two serial ; channels. Bit zero (0) of each flag should be set to a one ; to indicate hardware handshaking. ; ; * * * * * * * * ORG BIOS+06CH HSA: DB 0 ; Handshake flag for channel A HSB: DB 1 ; Handshake flag for channel B DB 0 ; (Reserved) DB 0 ; (Reserved) ; * * * * * * * * ; ; Drive select bytes ; ; These values are modified by the floppy drive select code, the ; warm boot code, and several "E" utilities. The format of each ; select byte is a 1.x ; and 2.x bios. ; ; Note: Although this parameter is set with the CONFIG utility, ; it is ignored unless the MAX$DRIVE parameter is set to YES. ; ; * * * * * * * * ORG BIOS+05CH MAX$DRV$LTR: DB 'P'-'A' ; Allow drives A-P ; * * * * * * * * ; ; Step rates for the four floppy drives. ; ; These values are set by the CONFIG program. ; ; * * * * * * * * ORG BIOS+05DH STPRAT: DB 0,0,0,0 ; Initial rates = 6ms ; * * * * * * * * ; ; Initial IOBYTE value ; ; This value is s follows: ; ; Bit: 76543210 ; Speed select x 0=normal, 1=double ; Step select x 0=single, 1=double ; Disk density x 0=double, 1=single ; Side select x 0=Side 0, 1=Side 1 ; Drive unit 4 x . ; Drive unit 3 x . Select drive ; Drive unit 2 x . unit 1,2,3,4 ; Drive unit 1 x . ; Bit: 76543210 ; ; * * * * * * * * ORG BIOS+070H DRIVE$TYPES: DB 01H ; Drive A: DD, Unit 1 DB 02H ; Drive B: DD, Unit  2 DB 04H ; Drive C: DD, Unit 3 DB 08H ; Drive D: DD, Unit 4 DB 02H ; Drive E: DD, Unit 2 DB 0,0,0 ; (Reserved) ; * * * * * * * * ; ; Reserved data area ; ; * * * * * * * * ORG BIOS+078H DW 0,0,0,0 ; (Reserved) ; * * * * * * * * ; ; Disk drive parameter headers ; ; Physical drives A,B,C,D Floppy drives ; ; Physical drive E Special E-disk ; ; Physical drives F,G,H,I ; J,K,L,M ; N,O,P Hard disk drives ; ; * * * * * * * * ORG BIOS+080H DPBASE: DW XLormat skew table DB 17,18,19,20,21,22,23,24,25,26 ; * * * * * * * * ; ; Special user-defined drive "E-disk" ; ; NOTE: The order of the following table must not be changed, ; since many existing utilities depend on this order. ; ; A call to the BIOS function GETEDISK (BIOS+030H) returns the ; address of EPARM. The type table entry is defined as the re- ; turned value -1, and the translate table value as the returned ; value +15. The translate table has space for 20 entries in ; bioTSS,0,0,0,DIRBUF,SPARM,CSVA,ALVA DW XLTSS,0,0,0,DIRBUF,SPARM,CSVB,ALVB DW XLTSS,0,0,0,DIRBUF,SPARM,CSVC,ALVC DW XLTSS,0,0,0,DIRBUF,SPARM,CSVD,ALVD DW XLTE1,0,0,0,DIRBUF,EPARM,CSVE,ALVE IF HARD$DISK DW 0,0,0,0,DIRBUF,FPARM,CSVF,ALVF DW 0,0,0,0,DIRBUF,GPARM,CSVG,ALVG DW 0,0,0,0,DIRBUF,HPARM,CSVH,ALVH DW 0,0,0,0,DIRBUF,IPARM,CSVI,ALVI DW 0,0,0,0,DIRBUF,JPARM,CSVJ,ALVJ DW 0,0,0,0,DIRBUF,KPARM,CSVK,ALVK DW 0,0,0,0,DIRBUF,LPARM,CSVL,ALVL DW 0,0,0,0,DIRBUF,MPARM,CSVM,ALVM s version 1 or 2, 26 entries in bios version 3+. ; ; * * * * * * * * ETYPE DB 82H ; ETYPE must be at EPARM - 1 EPARM: ;Set for: KAYPRO DW 40 ;SEC/TRK KAYPRO IS 40 DB 3 ;BLOCK SHIFT DB 7 ;BLOCK MASK DB 0 ;EXTENT MASK DW 194 ;DISK SIZE -1 KAYPRO IS 194 DW 63 ;DIRECTORY MAX -1 DB 240 ;ALLOC 0 KAYPRO 240 DB 0 ;ALLOC 1 DW 16 ;CHECK SIZE DW 1 ;OFFSET KAYPRO IS 1 EDSD: DB 1 ;DEFAULT DRIVE B: XLTE1: ;TRANSLATE TABLE FOR E DISK  DW 0,0,0,0,DIRBUF,NPARM,CSVN,ALVN DW 0,0,0,0,DIRBUF,OPARM,CSVO,ALVO DW 0,0,0,0,DIRBUF,PPARM,CSVP,ALVP ENDIF IF NOT HARD$DISK DW 0,0,0,0,DIRBUF,SPARM,CSVF,ALVF DW 0,0,0,0,DIRBUF,SPARM,CSVG,ALVG DW 0,0,0,0,DIRBUF,SPARM,CSVH,ALVH DW 0,0,0,0,DIRBUF,SPARM,CSVI,ALVI DW 0,0,0,0,DIRBUF,SPARM,CSVJ,ALVJ DW 0,0,0,0,DIRBUF,SPARM,CSVK,ALVK DW 0,0,0,0,DIRBUF,SPARM,CSVL,ALVL DW 0,0,0,0,DIRBUF,SPARM,CSVM,ALVM DW 0,0,0,0,DIRBUF,SPARM,CSVN,ALVN DW 0,0,0,0,DIRBUF,SPARM,CSVO,ALVO  DB 0,1,2,3,4,5,6,7,8,9 DW 0,0,0,0,0,0,0,0 ; 26 entries max in XLT1 ; End of E-disk user defined table ; * * * * * * * * ; ; GETTBL GET NEXT JUMP TABLE ADDRESS ; ; While this routine returns the address of NXTTBL, it can also ; be used to identify the BIOS version level. ; ; For pre-2.0 systems, the A register is returned unchanged, ; and the HL register pair returned 0FFFFH. ; ; For 2.0 and higher, HL returneds the address of NXTTBL (never ; equal to 0FFFFH), and A contains tDW 0,0,0,0,DIRBUF,SPARM,CSVP,ALVP ENDIF SPARM: ; Ampro single sided 48tpi DW 40 ; Sectors/track DB 4 ; Block shift DB 15 ; Block mask DB 1 ; Extent mask DW 94 ; Disk size -1 DW 63 ; Directory max DB 128 ; Allocation 0 DB 0 ; Allocation 1 DW 16 ; Check size DW 2 ; Offset DPARM: ; Ampro double sided 48tpi ; -or- single sided 96tpi DW 40 ; Sectors/track DB 4 ; Block shift DB 15 ; Block mask DB 1 ; Extent mask DW 194 ; Disk size -1 he assembly version of this ; bios code. ; ; * * * * * * * * GETTBL: LXI H,NXTTBL ; Get address of jump table MVI A,VERS ; . and version number NXTJMP: ; Label for dummy routines RET NXTTBL: JMP SWAP ; Swap two logical drives JMP HD$INFO ; Get HD table information JMP PHTBAC ; Get/set PhyTab access JMP PAGET ; Get PhyTab entry address IF CLOCK JMP TOD ; return address of clock tick ENDIF JMP NXTJMP ; RESERVED ENTRY ; * * * * * * * * ; ; GETEDSK Get E-disk poin DW 127 ; Directory max DB 192 ; Allocation 0 DB 0 ; Allocation 1 DW 32 ; Check size DW 2 ; Offset QPARM: ; Ampro double sided 96tpi DW 40 ; Sectors/track DB 4 ; Block shift DB 15 ; Block mask DB 0 ; Extent mask DW 394 ; Disk size -1 DW 255 ; Directory max DB 240 ; Allocation 0 DB 0 ; Allocation 1 DW 64 ; Check size DW 2 ; Offset XLTSS: ; Single sided format skew table DB 1,2,3,4,5,6,7,8,9,10 XLTDS: ; Double sided fter ; ; * * * * * * * * GETEDSK: LXI H,EPARM RET ; * * * * * * * * ; ; IOINIT ; ; Initialize the CTC and DART comm devices according to the ; information provided at BIOS + 40 hex. ; ; Entry from BOOT and also via the JMP IOINIT from the BIOS jump ; table. ; ; * * * * * * * * IOINIT: LXI H,CTCVAL LXI B,0240H ; Initialize CTC0 DW OTIR80 LXI B,0250H ; Initialize CTC1 DW OTIR80 LXI B,0260H ; Initialize CTC2 DW OTIR80 LXI B,0270H ; Initialize CTC3 DW OTIR80   LXI B,0A84H ; Initialize DART channel A DW OTIR80 LXI B,0A8CH ; Initialize DART channel B DW OTIR80 LDA IOBYT ; Initialize IOBYTE STA IOBYTE RET ; * * * * * * * * ; ; SWAP Swap logical drives ; ; Entry here causes the two LOGICAL drives identified in B and C ; to be swapped in position. This alters the current LOGICAL ; name of the target drive for all subsequent disk operations. ; ; For example, assume the CP/M logical drive A: is the left ; floppy disk drive, and the CPn ; pointed to by HL for the length of the table. ; ; Note: When updating the table, ALL bytes are re-written. No ; length parameter is accepted from the user, and no alternate ; starting address is recognized. ; ; Refer to the PHYTAB for byte definitions... ; ; * * * * * * * * PHTBAC: XCHG ; Save ptr in case we need it MOV A,D ; Test former HL ORA E ; MVI A,PHYEND-PHYTAB ; Get length of table LXI H,PHYTAB ; . and address, too. DB JRNZ,CPY$TBL-$-1; Copy new table if ptr non/M logical drive F: is the first ; hard disk partition. If on entry to the SWAP routine the B ; register contained 0 (logical drive A:) and the C register ; contained 5 (logical drive F:), after SWAP, all accesses to ; CP/M drive A: will be to the first hard disk partition, while ; all accesses to CP/M drive F: would be to the left floppy ; drive. ; ; * * * * * WARNING * * * * * ; ; To maintain compatibility with all utilities, and also for ; some special case coding within this-zero RET ; Provide addr if ptr zero ; * * * * * * * * ; ; PAGET GET DRIVE LOGICAL INFORMATION ; ; Entry here from within the BIOS, as well as from the NXTTBL ; BIOS Jump entry. ; ; Enter with the Drive Logical address in the A reg. ; ; Exits with the Logical Table entry address in the HL reg pair. ; ; NOTE: Destroys DE and A ; ; * * * * * * * * PAGET: LXI H,PHYTAB ; Get base of physical table ADD A ; Compute offset to the descriptor ADD A ; . MOV E,A ; . MVI D,0 ; . BIOS, the user definable ; E-DISK must remain as Drive E logical. ; ; * * * * * * * * SWAP: MOV A,B ; Get first drive logical # CALL PAGET ; Compute current address in table PUSH H ; Save this address MOV A,C ; Get second drive logical # CALL PAGET ; Compute current address in table POP D ; Get pointer to first back in D MVI B,4 ; Set up to move 4 bytes PATMV: MOV C,M ; Get a byte from the first str LDAX D ; Get a byte from the second str MOV M,A ; Swap the bytes MOV A DAD D ; . RET ; Return with ptr in HL CPY$TBL: ; Copy from DE to HL, length A XCHG ; . (exchange DE and HL) MOV C,A ; . (put length info in BC) MVI B,0 ; . (now it's a-ok for LDIR) DW LDIR80 ; . (use LDIR to move table) RET ; and return to caller ; * * * * * * * * ; ; PHYSICAL DRIVER TABLE ; ; This table contains information to identify the Logical vs ; Physical drives requested, and also the disk driver required ; for the particular drive. ; ; The table is,C ; . STAX D ; . INX D ; Bump pointers INX H ; . DB DJNZ,255-($-PATMV) RET ; No -- all done ; * * * * * * * * ; ; HD$INFO Return HD information. ; ; Returns a pointer to various HD pointers. ; ; * * * * * * * * HD$INFO: LXI H,HD$ALV$AVAIL ; Get pointer to available area RET ; Provide addr if ptr zero HD$ALV$AVAIL: DW HD$CURRENT ; Current BIOS buffer area value DW HD$VECTORS ; Hard disk allocation vector base IF HARD$DISK DW HD$BYTE$BLOCK ; Location of SCSI byte built as 16 groups of 4-bytes each, arranged in ; the logical order of drives A thru P for a total of 64 bytes. ; ; The four bytes defining each Logical drive are as follows: ; ; +0 Disk Driver ID number, 0=reserved for error, Max=7 ; ; +1 Physical offset from DPBASE in DPH table, and the drive ; unit number. ; ; Bits 7654: Offset to DPBASE 0 - F ; ; Bits 3210: Physical device address to be passed ; to the respective driver. ; ; For floppies, this is the drive unit #. ; For ha/block flag ENDIF IF NOT HARD$DISK DW 0FFFFH ; "Safe" dummy byte/block location ENDIF DW 0 ; Reserved DW 0 ; Reserved DW 0 ; Reserved ; * * * * * * * * ; ; PHTBAC PHYSICAL TABLE ACCESS ; ; On entry, if the HL register pair is 0000H, the address of the ; Logical vs Physical table is returned in HL, with the length ; of that table in the A register. ; ; If on entry, the HL register pair is non-zero, the current ; table is replaced with the user data starting at the locatiord disks, these are reserved. ; ; +2 Type identifier for this drive ; ; Floppy usage: ; ; Bit: 76543210 ; Density x 0=single 1=double ; Sides x 0=single 1=double ; Sector #'s x 0=same 1=continuous ; Track count x 0=down 1=down front, up back ; Alloc unit xx 00=1K 01=2K 10=4K 11=8K ; Sector size xx 00=128 01=256 10=512 11=1024 ; Bit: 76543210 ; ; Hard disk usage: ; ; Bit: 76543210 ; LUN xxx Logical unit number  (0-7) ; Reserved x ; Alloc unit xx 00=1K 01=2K 10=4K 11=8K ; Sector size xx 00=128 01=256 10=512 11=1024 ; Bit: 76543210 ; ; Note: Allocation unit and sector size usage are the same ; for floppy and hard disk. ; ; ; +3 00 for floppy or SCSI bus address for hard disk. ; ; For normal SCSI operations, only one bit may be set. ; ; Bit: 76543210 ; SCSI address 0 x This is the actual ; SCSI address 1 x bit pattern supplied ; SCSI address 2  Set track given by BC DW SBCD80,SEKTRK DW SBCD80,CPMTRK RET ; * * * * * * * * ; ; SETSEC Set sector (BIOS JMP table entry) ; ; * * * * * * * * SETSEC: ; Set sector given by reg C MOV A,C STA SEKSEC STA CPMSEC RET ; * * * * * * * * ; ; SETDMA Set DMA (BIOS JMP table entry) ; ; * * * * * * * * SETDMA: ; Set DMA address given by BC DW SBCD80,DMAADR XRA A ; clear flags for ZRDOS RET ; * * * * * * * * ; ; SECTRAN Sector translation (BIOS JMP table entry) ; ;  x during the SCSI ; SCSI address 3 x select routine. No ; SCSI address 4 x internal address ; SCSI address 5 x translation or bit ; SCSI address 6 x scaling is done. ; SCSI address 7 x ; ; +4 Duplicated for drives B thru P ; thru ; +63 ; ; Disk Driver Linkage +64 thru +79 ; ; Each disk driver is described by 2 bytes which point to the ; driver to be used, with driver 0 reserved for the SELECT ERROR ; trap for unused or deselected drive letters. ; ; * * * * * * *  NOTE: No sector translation is done for the AMPRO disk formats. ; ; * * * * * * * * SECTRAN: ; Translate sector number in BC MOV H,B MOV L,C RET ; * * * * * * * * ; ; SELECT Select disk routine ; ; Entry via the BIOS Jump Table SELDSK ; ; Entry: ; C CP/M Logical drive ; E Bit zero is non-zero if this is the first select of the ; drive since warm or cold boot. ; ; * * * * * * * * SELDSK: MOV A,C ; Get logical disk requested STA LOGDSK ; Save it STA SEKDSK ; . I* PHYTAB: DB 1,000H, SSDD48, 0H ; Floppy drive A DB 1,011H, SSDD48, 0H ; Floppy drive B DB 1,022H, SSDD48, 0H ; Floppy drive C DB 1,033H, SSDD48, 0H ; Floppy drive D DB 2,044H, 0H, 0FFH ; Special floppy drive E DB 0,050H,0,0 ; Hard disk drive F DB 0,060H,0,0 ; Hard disk drive G DB 0,070H,0,0 ; Hard disk drive H DB 0,080H,0,0 ; Hard disk drive I DB 0,090H,0,0 ; Hard disk drive J DB 0,0A0H,0,0 ; Hard disk drive K DB 0,0B0H,0,0 ; Hard disk drive L DB 0,0C0H,0,0 ; Hard F MAX$DRIVE ; Is the max drive checking enabled? LDA MAX$DRV$LTR ; . Yes -- Is the letter requested CMP C ; . . greater than the max letter? JC SELERR ; . Yes -- return w/select error ENDIF ; . . IF LOCAL$STACK DW 73EDH,OLDSTK ; ld(oldstk),sp - save old stack LXI SP,LOCSTK ; set local stack ENDIF MVI A,0FFH ; No to either -- set SELECT cmd ; NOTE: we fall through to PDRVR ; * * * * * * * * ; ; PDRVR Physical driver linkage ; ; Enter with the Logical Disk Drive saved disk drive M DB 0,0D0H,0,0 ; Hard disk drive N DB 0,0E0H,0,0 ; Hard disk drive O DB 0,0F0H,0,0 ; Hard disk drive P DRVRADR: ; Driver table DW SELERR ; Driver 0 - select error DW FLOPPY ; Driver 1 - floppy DW DISKE ; Driver 2 - E-disk IF HARD$DISK DW DRVR3 ; Driver 3 - SCSI hard disk ENDIF IF NOT HARD$DISK DW SELERR ; Driver 3 - not defined ENDIF DW SELERR ; Driver 4 - not defined DW SELERR ; Driver 5 - not defined DW SELERR ; Driver 6 - not defined at (LOGDSK) and the ; Physical Command in the A reg as: ; ; FF Select Disk Routine ; 00 Write Disk ; 01 Read Disk ; ; The proper driver is computed and jumped to as needed. ; ; * * * * * * * * PDRVR: PUSH D ; Protect first select status STA PHYCMD ; Save cmd for later LDA LOGDSK ; Get logical drive requested CALL PAGET ; Get table address for this drive SHLD PHYTAG ; Save index to logical descriptor PUSH H ; Save start address LXI D,TAGDRV ; Move the four tag bytes LXI BDW SELERR ; Driver 7 - not defined PHYEND EQU $ ; * * * * * * * * ; ; HOME Home the selected disk ; ; Entry via the bios jump table HOME ; ; * * * * * * * * HOME: LXI B,0 ; Set the current track to zero CALL SETTRK ; . LDA HSTWRT ; Check for pending write ORA A ; . DB JRNZ,HOMED-$-1 ; Pending write, leave active alone STA HSTACT ; No writes, clear host active HOMED: RET ; * * * * * * * * ; ; SETTRK Set track (BIOS JMP table entry) ; ; * * * * * * * * SETTRK: ;,4 ; . DW LDIR80 ; (LDIR) POP H ; Restore starting address POP D ; Restore first select status MOV A,M ; Get driver number ORA A ; . If zero, then select error DB JRZ,SELERR-$-1 ; . MOV B,A ; save drive index INX H ; Bump to next position MOV A,M ; Get unit # and DPH offset ANI 0FH ; Mask all but unit # STA PHYDRV ; Save unit # MOV A,M ; Get unit # and DPH offset again ANI 0F0H ; Mask all but DPH offset STA DPHDRV ; Save DPH offset PDRVR1: MOV A,B ; get driver   number back ADD A ; . MOV C,A ; Compute ptr within driver table MVI B,0 ; . LXI H,DRVRADR ; . DAD B ; . MOV A,M ; Get ptr to driver in HL INX H ; . MOV H,M ; . MOV L,A ; . LDA PHYCMD ; Get cmd back in the A reg PCHL ; Jump to the proper driver ; * * * * * * * * ; ; SELERR Indicate select error to the BDOS ; ; * * * * * * * * SELERR: LXI H,0 ; Zero indicates select failed RET ; * * * * * * * * ; ; FLOPPY Floppy disk dispatcher -- driver code (01) ; ; Elogical drive ; A:, any other drive may be referenced by setting SYSDSK to the ; desired logical drive address (0-15). ; ; The warm boot consists of reading 1600 hex bytes of code, from ; CP/M track 0, sector 1, for 44 CP/M records. The code is loaded ; starting with the CCP address, which is defined at the start of ; this BIOS listing. ; ; The disk currently assigned to SYSDSK must have a standard AMPRO ; system image residing on the boot tracks (tracks 0 and 1). ; ; If SYSDSK is not nter with operation code in the A register as: ; ; FF Select Disk ; 00 Write Disk ; 01 Read Disk ; ; * * * * * * * * FLOPPY: INR A ; Code = select? JZ SELFLP ; Yes, perform a floppy select JMP FLOPPYIO ; No, perform host read or write ; * * * * * * * * ; ; DISKE E-disk dispatcher -- driver code (02) ; ; Enter with the operation code in the A register as: ; ; FF Select Disk ; 00 Write Disk ; 01 Read Disk ; ; * * * * * * * * DISKE: PUSH PSW ; Save command PUSH D ; . and logical drive A:, the BDOS will still access the ; current logical drive A: after the GOCPM entry in order to build ; the directory allocation vector table for drive A: prior to the ; selection of the current disk. ; ; * * * * * * * * WBOOT: LXI SP,80H ; Set stack to below default DMA LDA SYSDSK ; Get current system disk MOV C,A ; . MVI E,0 ; Set autoselect bit CALL SELDSK ; Select the system disk MOV A,H ; Check for a select error ORA L ; . DB JRZ,WBERR-$-1 ; Select error first-select status LDA EDSD ; Get E-disk unit MOV E,A ; Get type byte for the drive MVI D,0 ; . we want to be the 'E' LXI H,DRIVE$TYPES ; . disk DAD D ; . MOV A,M ; . ANI 00FH ; Strip all but select bit MOV B,A ; Save select bit LXI H,DRIVE$TYPES+4 ; Point to the E-disk drive byte MOV A,M ; Get the E-disk drive byte ANI 0F0H ; Strip select bits ORA B ; OR in proper select bits MOV M,A ; Save new E-disk drive byte PUSH H ; Save pointer to E-disk drive byte LDA EPA-- try again LXI B,0 ; Start reading from track 0 CALL SETTRK ; . MVI B,NSECTS ; Set the # of sectors to read MVI C,1 ; Start from sector 1 of track 0 LXI H,CCP ; Where we want the data to go WBLOOP: PUSH B ; Save sector count PUSH H ; and DMA address CALL SETSEC ; Set the sector to read next POP B ; Get the DMA address PUSH B ; Save it back CALL SETDMA ; Set the current DMA CALL READ ; Read the sector ORA A ; Check for errors DB JRNZ,WBERR-$-1 ; Disp msg & try agaRM-1 ; Get type byte LHLD PHYTAG ; Save as PHYTAB type byte INX H ; . INX H ; . MOV M,A ; . STA TAGTYP ; and as current type byte POP H ; Get E-disk drive byte ptr back DB BIT,BRES+B5+ZM ; Indicate double density RLC ; See if E-disk is single density DB JRC,E$IS$DD-$-1 ; No, leave bit alone DB BIT,BSET+B5+ZM ; Yes, indicate single density E$IS$DD: POP D ; Recover first select status POP PSW ; . and command INR A ; Select? JZ SELEDSK ; Yes, perform select JMP FLOin on error POP H ; Get DMA address POP B ; Get count & current sector LXI D,128 ; Update DMA address DAD D ; . INR C ; Bump sector count LDA CPMSPT ; Get sectors/track for this disk CMP C ; Are we at the end of track yet? DB JRNZ,NXTSEC-$-1 ; No, just get the next sector PUSH B ; Yes, save count & current sector DW LBCD80,CPMTRK ; Bump current track INX B ; . CALL SETTRK ; . POP B ; Restore count MVI C,0 ; Reset sector to 0 NXTSEC: DB DJNZ,255-($-WBLOOP) ; NPPYIO ; Otherwise use R/W floppy host PUTS: ; Send zero-terminated str to con: MOV A,M ; Get char INX H ; Bump pointer ORA A ; Zero is end-of-string RZ ; Yes -- return MOV C,A ; Otherwise move to C register CALL CONOUT1 ; . and send to console JMP PUTS ; Repeat for remaining bytes ; * * * * * * * * ; ; WBOOT Warm boot system ; ; This routine reloads the CCP and BDOS from the current logical ; disk defined as SYSDSK. While SYSDSK is normally OTE: WBOOT falls through to GOCPM ; * * * * * * * * ; ; GOCPM Setup CP/M vectors and jump to CP/M ; ; * * * * * * * * GOCPM: LXI SP,80H ; set stack pointer XRA A STA HSTACT ; Clear the host active flag STA HSTWRT ; Clear the host written flag STA UNACNT ; clear unacnt value MVI A,0C3H ; Setup the warm boot vector STA 0 ; . LXI H,WBOOTE ; . SHLD 1 ; . STA 5 ; and the BDOS vector LXI H,BDOS ; . SHLD 6 ; . LXI B,80H ; Setup the default DMA CALL SETDMA ; . EI   ; Enable interrupts LDA CDISK ; Get current disk/user MOV C,A ; . JMP CCP ; And go to CP/M ... ; * * * * * * * * ; ; WBERR Display error msg on warm boot error and try again. ; ; * * * * * * * * WBERR: LXI H,BOOTMSG ; Warm boot error -- display CALL PUTS ; . boot failed message and JMP WBOOT ; . try again ... BOOTMSG: DB CR,LF,'BOOT FAILED!',0 ; * * * * * * * * ; ; SELFLP Floppy disk select routine ; ; See if the disk has been accessed since the last warm or cold ; ; SELEND Compute proper deblocker variables and DPT addr ; ; Driver types 1, 2, and 3 all re-enter here. ; ; * * * * * * * * SELEND: CALL GETTYPE ; Get current type byte MOV B,A ; Save for a moment ANI 3 ; Mask all but sector shift bits STA SECSHF ; Save sector shift value MOV E,A ; Get sector shift byte from table MVI D,0 ; . LXI H,SEC$MSK$TBL ; . DAD D ; . MOV A,M ; . STA SECMSK ; Save it MOV A,B ; Get type byte back RAR ; Shift over to move alloc size RAR  boot (bit 0 of the E register is non-zero). If true, seek to ; track 2 and read the ID to determine if the disk is SS or DS, ; 48tpi or 96tpi, and in a 48tpi or 96tpi drive. Set up the disk ; access tables as appropriate. ; ; * * * * * * * * SELFLP: DB BIT,BTST+B0+ZE ; Check for new mount since boot JNZ SELEND ; Not new, no need to determine type CALL MAPTYPE ; Clear double step bit DB BIT,BRES+B6+ZM ; . CALL GETTYPE ; Set type byte to SS48tpi MVI M,SSDD48 ; . CALL SETUP ; Setu; . to the proper position ANI 3 ; Mask all but alloc bits MOV E,A ; Get initial unalloc from table ; MVI D,0 ; . already zero from start of routine LXI H,M$UNACT$TBL ; . DAD D ; . MOV A,M ; . STA MUNACT ; Save it ; NOTE: We fall through to GETDPT . . . ; * * * * * * * * ; ; GETDPT Calculate the DPH address for the current logical disk ; ; Entry with the DPH vector in DPHDRV, exits with the DPH address in ; the HL register pair, and the current DPB saved at CURDPB (primarily p parameters for SSDD CALL RESTORE ; Restore floppy to track 00 JNZ SELERR ; Error on restore, give select err MVI A,2 ; Seek to track 2 STA CPMTRK ; . CALL SEEK ; . LDA IDSAVE+3 ; Get sector size (bytes/sector) DB BIT,BTST+B1+ZA ; Bit 1 non-zero means 512 or 1024 JZ SELERR ; 128, 256 = err for AMPRO fmt ANI 01H ; Reset all but bit 0 ADD A ; *2 ... now 0=48tpi, 2=96tpi MOV C,A ; Save in C reg for the moment LDA IDSAVE+2 ; Get sector # CPI DSBIAS+1 ; Compare to DS bias (16+ ; for the warm boot routine). ; ; * * * * * * * * GETDPT: LDA DPHDRV ; Get DPH pointer MOV E,A ; . MVI D,0 ; . **** LXI H,DPBASE ; . DAD D ; . XCHG ; save it (need to return it in 'hl') MVI L,10 ; add offset to dpb 'h'=zero from **** DAD D ; . MOV A,M ; Copy DPB ptr to HL INX H ; . MOV H,M ; . MOV L,A ; . SHLD CURDPB ; Save current DPB pointer MOV A,M ; Get sectors-per-track STA CPMSPT ; Save it XCHG ; get dph pointer RET ; Leave with DPH pointer in 1) DB JRC,SELSS-$-1 INR C ; Bump pointer if DS SELSS: MVI B,0 ; BC now contains offset to type CALL GETTYPE ; Get ptr to type byte XCHG ; Save ptr in DE LXI H,TYPTBL ; Get type table base DAD B ; Add offset MOV A,M ; Get actual type byte STAX D ; Save in PHYTAB CALL GETDPT ; Get ptr to DPT LXI D,10 ; Bump by 10 to get ptr to DPB DAD D ; . XCHG ; Save ptr in DE LXI H,DPBTBL ; Get DPB ptr base DAD B ; Add offset DAD B ; Add offset again (word ptr) MOV A,M ; GHL TYPTBL: ; Drive type bytes DB SSDD48 DB DSDD48 DB SSDD96 DB DSDD96 DPBTBL: ; DBP pointers DW SPARM DW DPARM DW DPARM DW QPARM SEC$MSK$TBL: ; Sector mask table DB 00H DB 01H DB 03H DB 07H M$UNACT$TBL: ; Initial unalloc count table DB 8 DB 16 DB 32 DB 64 ; * * * * * * * * ; ; SELECT THE E-DISK DRIVE ; ; Builds the appropriate tables, and attempts to re-zero the ; drive. Returns with Select Error if can't re-zero. ; ; * * * * * * * * SELEDSK: et first byte of DPB ptr STAX D ; Save it INX D ; Bump pointers INX H ; . MOV A,M ; Get second byte of DPB ptr STAX D ; Save it LDA IDSAVE ; Get track number DCR A ; Track=1 means set double step DB JRZ,SET$DSTEP-$-1 DCR A ; Track=2 means normal step DB JRZ,SELEND-$-1 JMP SELERR ; Any other value = select err SET$DSTEP: CALL MAPTYPE ; Map byte for this drive, DB BIT,BSET+B6+ZM ; set the double step bit. ; NOTE: We fall through to SELEND ... ; * * * * * * * * ;  DB BIT,BTST+B0+ZE ; Check for new mount since boot JNZ SELEND ; Not new, no need to determine type CALL GETTYPE ; Get type byte & pointer DB BIT,BTST+B5+ZA ; Continuous sector numbers? DB JRZ,NOTCONT-$-1 ; No, don't bother computing E-adj ANI 03H ; Yes, compute E-adj from sector DW LBCD80,EPARM-1 ; . size and sectors per track MVI C,0FFH ; . (A=count,B=SPT,C=mask) SELESHIFT: ; . ORA A ; . Test if done & clear carry DB JRZ,SDONE-$-1 ; . Yes, save parameter DB 0CBH,019H ; .    RR C (Rotate C right) DB 0CBH,018H ; . RR B (Rotate B right) DCR A ; . Decrement count JMP SELESHIFT ; . Next shift ... NOTCONT: LXI B,0 ; Not continuous, set adj to zero SDONE: MOV A,B ; Shifts done, get computed E-adj ANA C ; Mask extra bits off STA ESECADJ ; Save as proper adj CALL SETUP ; Setup parameters for the E-disk CALL RESTORE ; Restore floppy to track 00 INR A ; Check for error during restore JZ SELERR ; Error code, return with select err JMP SELEND ; No CTOR FOR FUTURE REF INR M ;UNASEC = UNASEC+1 MOV A,M ;END OF TRACK? XCHG ; save 'hl' for overflow LHLD CURDPB ; get current DPB as set by select EMATCH: CMP M ; see if end of track JC NOOVF ;SKIP IF NO OVERFLOW ; OVERFLOW TO NEXT TRACK XRA A STAX D ; unasec 'de' = 0 LHLD UNATRK INX H SHLD UNATRK ; MATCH FOUND, MARK AS UNNECESSARY READ NOOVF: XRA A ;0 TO ACCUMULATOR STA RSFLAG ;RSFLAG = 0 JMP RWOPER+1 ;TO PERFORM THE WRITE ; NOT AN UNALLOCATED RECORD, REQUIRerror, finish the select ;--------------------------------------------------------------- ; ; READ CP/M SECTOR FROM THE CURRENTLY SELECTED DISK ; ; ENTRY VIA THE BIOS JUMP TABLE ; ;-------------------------------------------------------------- READ: IF LOCAL$STACK DW 73EDH,OLDSTK ; ld(oldstk),sp - save old stack LXI SP,LOCSTK ; set local stack ENDIF LDA SECSHF ; secshf = 0 if 128 byte sector size ANA A JZ READHST ; IF 128 BYTES XRA A STA UNACNT INR A ; 'a' = 1 STA READOP ES PRE-READ ALLOC: XRA A ;0 TO ACCUM STA UNACNT ;UNACNT = 0 INR A ;1 TO ACCUM STA RSFLAG ;RSFLAG = 1 ; ENTER HERE TO PERFORM THE READ/WRITE RWOPER: XRA A ;ZERO TO ACCUM STA ERFLAG ;NO ERRORS (YET) IF CLOCK CALL CLKCHK ; tick toc ENDIF IF BUFFKBD CALL KEYCHK ; get keyboard input ENDIF LDA SECSHF MOV B,A LDA SEKSEC ;COMPUTE HOST SECTOR SLOOP: DW SRLA ; shift 'a' right, msb = 0 DB DJNZ,255-($-SLOOP) STA SEKHST ;HOST SECTOR TO SEEK ; ACTIVE HOST SECTO;READ OPERATION STA RSFLAG ;MUST READ DATA MVI A,WRUAL STA WRTYPE ;TREAT AS UNALLOC JMP RWOPER ;TO PERFORM THE READ ;--------------------------------------------------------------- ; ; WRITE CP/M SECTOR FROM THE CURRENTLY SELECTED DISK ; ; ENTRY VIA THE BIOS JUMP TABLE ; ;-------------------------------------------------------------- WRITE: IF LOCAL$STACK DW 73EDH,OLDSTK ; ld(oldstk),sp - save callers stack LXI SP,LOCSTK ; set local stack ENDIF LDA SECSHF ; secshf = 0 if 128 R? LXI H,HSTACT ;HOST ACTIVE FLAG MOV A,M MVI M,1 ;ALWAYS BECOMES 1 ORA A ;WAS IT ALREADY? DB JRZ,FILHST-$-1 ; HOST BUFFER ACTIVE, SAME AS SEEK BUFFER? LXI D,SEKDSK LXI H,HSTDSK LXI B,3 MATMOR: LDAX D ; sekdsk=hstdsk, sektrk=hsttrk ? DW CPI80 DB JRNZ,NOMATCH-$-1 INX D JPE MATMOR LDA SEKHST ; sekhst=hstsec ? CMP M DB JRZ,MATCH-$-1 ; match NOMATCH: ; PROPER DISK, BUT NOT CORRECT SECTOR LDA HSTWRT ;HOST WRITTEN? ORA A LDA HSTDSK ;SELECT HOST AS DISK TO WObytes ANA A JZ WRITEHST ; IF 128 BYTES XRA A ;0 TO ACCUMULATOR STA READOP ;NOT A READ OPERATION MOV A,C ;WRITE TYPE IN C STA WRTYPE CPI WRUAL ;WRITE UNALLOCATED? JNZ CHKUNA ; WRITE TO UNALLOCATED, SET PARAMETERS ; FOR EDSK, WE NEED TO CHECK TO SEE IF SEKDSK IS THE E DISK. ; IF SO, THEN UNACNT WILL BE SET ACCORDING TO THE DISK TYPE. ; FOR AMPRO DISKS, ITS ALWAYS BLKSIZ/128. LXI H,MUNACT LXI D,UNACNT LXI B,5 DW LDIR80 CHKUNA: ; CHECK FOR WRITE TO UNALLOCATED SECTOR ; MRK ON STA LOGDSK ;SET LOGICAL DRIVE = BUFFERS DRIVE LHLD HSTTRK SHLD CPMTRK CNZ WRITEHST ;CLEAR HOST BUFF FILHST: LDA SEKDSK ; May have to fill host buffer STA HSTDSK STA LOGDSK ;UPDATE LOGICAL DRIVE LHLD SEKTRK SHLD HSTTRK SHLD CPMTRK LDA SEKHST STA HSTSEC LDA RSFLAG ;NEED TO READ? ORA A CNZ READHST ;YES, IF 1 XRA A ;0 TO ACCUM STA HSTWRT ;NO PENDING WRITE ; ; COPY DATA TO OR FROM BUFFER ; EDSK: SECTOR MASK (SECMSK) MUST BE A VARIABLE FOR EDISK AND ; IS CALCORE UNALLOCATED RECORDS REMAIN ; DISKS ARE THE SAME ; TRACKS ARE THE SAME XRA A ; clear 'a' LXI H,UNACNT ORA M ; see if any more remain DB JRZ,ALLOC-$-1 ; no more remain DCR M ; unacnt -1 INX H ; 'hl' = unadsk ; get ready for block match ; sekdsk=unadsk, sektrk=unatrk, seksec=unasec ? LXI D,SEKDSK ; LXI B,4 MORUNA: LDAX D DW CPI80 DB JRNZ,ALLOC-$-1 ; no match INX D ; next byte JPE MORUNA ; count not over DCX H ; correct 'hl' to unasec ; MATCH, MOVE TO NEXT SEULATED BY (HSTSIZ/128)-1. THIS IS THREE FOR AMPRO, ; KAYPRO, AND OTHER 512-BYTE SECTORS, AND 1 FOR 256 BYTE SECTORS. ; MATCH: LDA SEKSEC ;MASK BUFFER NUMBER LXI H,SECMSK ANA M ;LEAST SIGNIF BITS ADD A ; x2 MOV L,A ; Get offset from table MVI H,0 LXI D,BUFTBL ; Buffer table DAD D ; 'hl' points to offset address MOV A,M INX H MOV H,M MOV L,A ; 'hl' has hostbuffer address DW LDED80,DMAADR ; load 'de' dmaadress LXI B,128 ; set 'bc' up form 128 byte transfer LDA READOP ;WHI CH WAY? ORA A DB JRNZ,RWMOVE-$-1 ; WRITE OPERATION, MARK AND SWITCH DIRECTION MVI A,1 STA HSTWRT ;HSTWRT = 1 XCHG ;SOURCE/DEST SWAP RWMOVE: DW LDIR80 ; 'hl' = source, 'de' = destination ; DATA HAS BEEN MOVED TO/FROM HOST BUFFER LDA WRTYPE ;WRITE TYPE CPI WRDIR ;TO DIRECTORY? LDA ERFLAG ;IN CASE OF ERRORS RNZ ;NO FURTHER PROCESSING ; CLEAR HOST BUFFER FOR DIRECTORY WRITE ORA A ;ERRORS? RNZ ;SKIP IF SO XRA A ;0 TO ACCUM STA HSTWRT ;BUFFER WRITTEN LD; MVI A,2 ; Set up internal retry counter STA INT$RETRIES ; . LDA RWHOST ; Test for read (1) or write (0) ORA A ; . JNZ RDS ; . CALL WRDAT ; Write sector DB JRNZ,RWFAIL-$-1 ; Try again if failed RET ; Otherwise return RDS: MVI A,FREADS ; Indicate read sector CALL RDATA ; Read sector DB JRNZ,RWFAIL-$-1 ; Try again if failed RET ; Otherwise return RWFAIL: CALL RESTORE ; Restore to track 00 on failure LXI H,TRIES ; Point to retry counter DCR M ; Bump it closer to A HSTDSK STA LOGDSK LHLD HSTTRK SHLD CPMTRK CALL WRITEHST LDA ERFLAG RET WRITEHST: ;LOGDSK = HOST DISK #, CPMTRK = HOST TRACK #, ;HSTSEC = HOST SECT #. WRITE "HSTSIZ" BYTES ;FROM HSTBUF AND RETURN ERROR FLAG IN ERFLAG. ;RETURN ERFLAG NON-ZERO IF ERROR XRA A JMP HOSTDO READHST: ;LOGDSK = HOST DISK #, CPMTRK = HOST TRACK #, ;HSTSEC = HOST SECT #. READ "HSTSIZ" BYTES ;INTO HSTBUF AND RETURN ERROR FLAG IN ERFLAG. MVI A,1 HOSTDO: STA RWHOST HOSTIO: JMP PDdoom DB JRNZ,255-($-WMI); and try again if we can . . . MVI A,01 ; No more tries left STA ERFLAG ; Set error flag RET ; and return to BDOS w/error ; * * * * * * * * ; ; WRDAT Floppy disk write data routine ; ; This routine writes the data pointed to by the HL register to ; the currently selected floppy. ; ; The number of bytes written is determined by the FDC controller ; and the ID string currently active for this sector. ; ; Multiple sector transfers are not supported. ; ;RVR ;SELECT NEXT ROUTINE VIA PDRVR ; ; Buffer table, determine start address of host buffer for read/write ; BUFTBL: DW HSTBUF+0000 DW HSTBUF+0128 DW HSTBUF+0256 DW HSTBUF+0384 DW HSTBUF+0512 DW HSTBUF+0640 DW HSTBUF+0768 DW HSTBUF+0896 ; ;-------------------------------------------------------------------- ; ; FLOPPY DISK READ/WRITE HOST ROUTINES ; ;------------------------------------------------------------------- FLOPPYIO: CALL SETUP ;OUTPUT TO DRIVE SELECT REGISTER  NOTE: The fastest way to test for busy and DRQ is to shift the ; status bit right into the carry bit. Therefore, when busy drops, ; the ending status is still shifted right. The checks for lost ; data, write protect, record not found, and crc error must take ; this bit shift into account. ; ; * * * * * * * * WRDAT: SHLD FRWPTR ; Save data ptr in case intr CALL DWAIT ; Wait until the FDC is ready WRDAT2: MVI A,FWRITES ; Set up write sector command LHLD FRWPTR ; . CALL OUTCMD ;  MVI A,FRETRY STA TRIES WMI: CALL MAPTRK CALL GETTRK ;WHAT TRACK DO WE WANT? CMP M CNZ SEEK ;IF NOT THERE, GO THERE DB JRNZ,RWFAIL-$-1 ;IN CASE SEEK FAILS ; TRACK SHOULD BE OK, BUT WE NEED TO SEND IT ANYWAY IN ; CASE THE LAST UNIT WAS DIFFERENT (THERE'S ONLY ONE TRACK ; REGISTER). THEN LOAD SECTOR AND DO IT... CALL GETTRK OUT WTRK CALL GETSEC ; CHECK FOR DSDD TO ADD SECTOR BIAS IF NOT 'E' DRIVE MOV E,A ; Save sector # for a moment LDA PHYDRV ; SeSend the command to the FDC WR: IN STAT ; Get FDC status RAR ; Test busy bit DB JRNC,WRDONE-$-1 ; Exit if no busy bit RAR ; Test DRQ bit DB JRNC,255-($-WR) ; Test status again if no DRQ MOV A,M ; Busy & DRQ ==> OK to write data OUT WDAT ; . INX H ; Bump pointer to next byte JMP WR ; Repeat as long as busy is active WRDONE: DB BIT,BTST+B1+ZA ; Test for lost data DB JRNZ,255-($-WRDAT2) ; Lost data -- Try write cmd again WRNLD: ANI 02CH ; Test for WP, RNF, or CRC RZ e if this is the E-disk CPI 04H ; . (E-disk is drive "4") MVI A,0 ; Restore sector # DB JRZ,NOBIAS-$-1 ; No sector bias if 'E' drive PUSH H PUSH D CALL GETTYPE ; get disk type POP D POP H ANI 0C0H ; Isolate DD, DS indicators CPI 0C0H ; Check DD and DS MVI A,0 ; . DB JRNZ,NOBIAS-$-1 ; Not DSDD, no bias ADI DSBIAS ; Add double sided bias NOBIAS: ADD E ; Add sector # to bias OUT WSEC ; Send sector # to FDC CALL SETUP ; Select unit and real side CALL GETBUF  ; All ok if zero LDA INT$RETRIES ; Get # of internal retries left DCR A ; Bump count one closer to doom! STA INT$RETRIES ; Save count JP WRDAT2 ; Try again if 0, 1, or 2 RET ; Otherwise return with NZ status ; * * * * * * * * ; ; RDATA Floppy disk read data routine ; ; This routine reads the data from the currently selected floppy ; into the area pointed to by the HL register. This data is from ; a read address or read data command. ; ; The number of bytes read is determined   by the 1770 controller ; and the ID string currently active for this sector. ; ; Multiple sector transfers are not supported. ; ; NOTE: The fastest way to test for busy and DRQ is to shift the ; status bit right into the carry bit. Therefore, when busy drops, ; the ending status is still shifted right. The checks for lost ; data, record not found, and crc error must take this bit shift ; into account. ; ; * * * * * * * * RDATA: STA FRWCMD ; Save command and data pointer SHLD FRWP Save BC reg for wait cmd MVI A,4 ; Wait 4 * 250ms = 1 second WAITMORE: PUSH PSW ; Wait 250ms MVI A,250 ; . CALL WAIT ; . POP PSW ; . DCR A ; More waiting? DB JRNZ,WAITMORE-$-1 and 255 POP B ; Get BC reg back MOV A,D ; Restore 1770 registers OUT WDAT ; . MOV A,C ; . OUT WSEC ; . MOV A,B ; . OUT WTRK ; . POP H ; restore buffer pointer MOTOROK: POP PSW ; Get command back RET ; and return ; * * * * * * * * ; ; DWAIT Wait for permission to write a rTR ; . (in case we're interrupted) CALL DWAIT ; Make sure FDC is ready RDATA2: LDA FRWCMD ; Get command and data pointer LHLD FRWPTR ; . CALL OUTCMD ; Send command to FDC RD: IN STAT ; Get FDC status RAR ; Test busy bit DB JRNC,RDDONE-$-1 ; Exit if no busy bit RAR ; Test DRQ bit DB JRNC,255-($-RD) ; Test status again if no DRQ IN RDAT ; Busy & DRQ ==> OK to read data MOV M,A ; . INX H ; Bump pointer to next byte JMP RD ; Repeat as long as busy is active RDDONE: egister ; ; This routine will wait up to 5 seconds for permission to write ; to one of the FDC registers. After 5 seconds, a FORCE INTERRUPT ; command will be issued to the FDC. ; ; * * * * * * * * DWAIT: LXI H,TIMEOUT ; Point to timeout location MVI M,6 ; Set 6 major loops DLOOP: IN STAT ; Get FDC status DB BIT,BTST+B0+ZA ; Test bit 0 (BUSY), return with RZ ; . zero status if busy non-active DCX H ; See if enough minor loops MOV A,H ; . (Approx 58,000 times) ORA L ; . DB BIT,BTST+B1+ZA ; Test for lost data DB JRNZ,255-($-RDATA2) ; Lost data -- try read again RDNCD: ANI 0CH ; Test for RNF or CRC RZ ; All ok if zero LDA INT$RETRIES ; Get # of internal retries left DCR A ; Bump count one closer to doom! STA INT$RETRIES ; Save count JP RDATA2 ; Try again if 0, 1, or 2 RET ; Otherwise return with NZ status ; * * * * * * * * ; ; OUTCMD Issue a cmd to the 1770 floppy controller ; ; * * * * * * * * OUTCMD: CALL MOTOR ; Insure motor on OC JNZ DLOOP ; Not done with minor loop IF CLOCK CALL CLKCHK ; tick toc ENDIF IF BUFFKBD CALL KEYCHK ; get keyboard entry ENDIF LXI H,TIMEOUT ; Decrement major loop counter DCR M ; . (6 times) JNZ DLOOP ; . MVI A,0D0H ; Give up on waiting; issue a FORCED OUT CMND ; . INTERRUPT to the FDC XRA A ; Set A to 0FFH and status to NZ DCR A ; . RET ; Return to caller ; * * * * * * * * ; ; RESTORE Restore a floppy drive to track 00 ; ; This routine issues a re-zero 0: OUT CMND ; Send command to FDC MVI A,15 ; Wait 60 us for cmd to set up OC1: DCR A ; . 15 x 4 usec DB JRNZ,255-($-OC1) RET ; * * * * * * * * ; ; MOTOR Start floppy drive motor ; ; This routine starts the target motor by executing a read address ; command. The xTRK, xSEC, and xDAT registers of the 1770 must be ; saved as the read address command alters their contents. After ; the read address command, we have 2 seconds (10 index pulses) to ; execute the next command before thecommand to the 1770 which causes ; the drive indicated by LOGDSK to slowly find its way back to ; the track zero (00) sensor. ; ; * * * * * * * * RESTORE: ; Restore the disk head to track 00 CALL DWAIT ; Wait for the 1770 to be ready RNZ ; Return to caller if timeout CALL GETSTEP ; Get the step rate for this drive ORI FRESTOR ; Add restore command to step rate CALL OUTCMD ; Send the cmd to the 1770 CALL DWAIT ; Wait for the cmd to finish MVI A,50 ; Wait 50 ms for the d motor shuts off. ; ; * * * * * * * * MOTOR: PUSH PSW ; save command IN STAT ; Check motor on bit RAL ; . DB JRC,MOTOROK-$-1 ; Motor already on ... PUSH H ; Save buffer pointer IN RTRK ; Save 1770 trk, sec, & dta regs MOV B,A ; . IN RSEC ; . MOV C,A ; . IN RDAT ; . MOV D,A ; . CALL DWAIT ; Wait for the 1770 to get ready XRA A ; Setup seek to same track cmd OUT WTRK ; . OUT WDAT ; . MVI A,FSEEKNV ; . (seek, no verify) OUT CMND ; Send the cmd PUSH B ;rive to settle CALL WAIT ; . CALL MAPTRK ; Set this drive's last track MVI M,0 ; . to track 00 NEXT$BLIP: MVI A,FRDADDR ; Send the READ ADDRESS cmd to the CALL OUTCMD ; . FDC and see if we get anything CALL DWAIT ; Return if cmd finished (implies IF BEEP RZ ; . both disk & drive present) MVI C,BELL ; Beep if no disk CALL CONOUT1 ; . CALL CONST1 ; Check console status CNZ CONIN1 ; Get char if one is there CPI CTRLC ; Was the char a ctrl-C? DB JRZ,UABORT-$-1 ; Y es, abort with error CPI ESC ; Was the char an ESC? DB JRNZ,255-($-NEXT$BLIP) ; No, try again UABORT: XRA A ; Return with error DCR A ; . ENDIF RET ; . ; * * * * * * * * ; ; SEEK Seek to track (floppy only) ; ; Moves the floppy head to the track indicated by CPMTRK if not ; already there. ; ; * * * * * * * * SEEK: CALL MOTOR ; Make sure the motor is on CALL GETTRK ; Get track we want PUSH PSW ; Save track # CALL SETUP ; Set up unit and real side POP PSW ; Gel drive indicated by PHYDRV. ; ; The format of the physical type bytes can be found in the ; description of DRIVE$TYPES at BIOS+070H. ; ; Registers modified: DE, HL ; ; * * * * * * * * MAPTYPE: PUSH PSW ; Save entry A reg and flags LDA PHYDRV ; Get physical drive unit MOV E,A ; Compute pointer to physical MVI D,0 ; . type byte LXI H,DRIVE$TYPES ; . DAD D ; . POP PSW ; Get A reg and flags back RET ; And return with ptr in HL ; * * * * * * * * ; ; MAPTRK Return pointt track # back CALL MAPTYPE ; Check double step bit DB BIT,BTST+B6+ZM ; . DB JRZ,NODS1-$-1 ; Skip double step if bit clear ADD A ; Otherwise multiply by 2 NODS1: OUT WDAT ; Tell the 1770 where we want to go CALL MAPTRK ; Get the current track # MOV A,M ; . CALL MAPTYPE ; Check double step bit DB BIT,BTST+B6+ZM ; . DB JRZ,NODS2-$-1 ; Skip double step if bit clear ADD A ; Otherwise multiply by 2 NODS2: OUT WTRK ; Tell the 1770 where we are CALL GETSTEP ; Get the step raer to current track ; ; Sets the HL register pair to the location which contains the ; current track for the drive indicated by PHYDRV. ; ; Registers modified: A, PSW, DE, HL ; ; * * * * * * * * MAPTRK: LDA PHYDRV ; Get physical drive unit CPI 04 ; E-disk? DB JRNZ,MAP$AD-$-1 ; No, use normal unit # LDA EDSD ; Yes, use E-disk unit # MAP$AD: MOV E,A ; Compute ptr to current track MVI D,0 ; . LXI H,LTRACK ; . DAD D ; . RET ; Return with pointer in HL ; * * * * * * * *te for this drive ORI FSEEKNV ; OR in seek without verify cmd PUSH PSW ; Save A reg as DWAIT destroys it CALL DWAIT ; Wait until 1770 is ready POP PSW ; Get A reg back (1770 cmd) CALL OUTCMD ; Send the cmd CALL DWAIT ; Wait for the cmd to finish ANI 18H ; Isolate possible failures RNZ ; Seek failed, return error MVI A,FRDADDR ; Read current track info LXI H,IDSAVE ; . CALL RDATA ; . CALL MAPTRK ; Get ptr to track save area CALL GETTRK ; Get track we were seeking MOV ; ; SETUP Setup the system control register for a drive select ; ; This routine writes to the system control port register at I/O ; 00H, causing the associated drive to be selected, along with ; the head select signal line. ; ; NOTE: This routine always turns the internal system rom OFF, ; without any regard for those who just may have the rom enabled ; prior to entry. ; ; NOTE: This routine modifies all registers ; ; * * * * * * * * SETUP: CALL MAPTYPE ; Get pointer to drive ty B,A ; Save it for a moment IN RSEC ; Get track # from the FDC CMP B ; Compare to what we wanted MOV M,A ; Save FDC track # in either case RET ; Z=ok, NZ=error ; * * * * * * * * ; ; GETSTEP Get the step rate for a particular drive ; ; Sets the HL register pair to the location which contains the ; step rate for the drive indicated by PHYDRV, and returns the ; actual step bits in the A register. ; ; Registers modified: A, PSW, DE, HL ; ; * * * * * * * * GETSTEP: LDA PHYDRV pe byte MOV A,M ; Get byte ORI 040H ; Turn off EPROM LXI H,HSTSID ; Point to side select ORA M ; Include proper side select MOV C,A ; Save control byte ANI 1000$1111B ; Mask out eprom, density, & side LXI H,CHGDSK ; Compare with previous results CMP M ; . MOV M,A ; . (save this result anyway) DB JRZ,SETUP2-$-1 ; Skip split & delay if same MOV A,C ; Get control byte back ANI 0F0H ; Mask out drive bits OUT CONT ; Send speed only to control port MOV A,C ; Get control  ; Get physical drive unit CPI 04 ; E-disk? DB JRNZ,GET$AD-$-1 ; No, use normal unit # LDA EDSD ; Yes, use E-disk unit # GET$AD: MOV E,A ; Get step rate from table MVI D,0 ; . (based on physical unit) LXI H,STPRAT ; . DAD D ; . MOV A,M ; . ANI 03H ; Mask off junk, just in case RET ; And return data in A and HL ; * * * * * * * * ; ; MAPTYPE Get pointer to physical type byte ; ; Sets the HL register pair to the location which contains the ; type byte for the physicabyte back OUT CONT ; Send full byte to the control port MVI A,HLDELAY ; Delay HLDELAY ms (usually 30ms) CALL WAIT ; . RET SETUP2: MOV A,C ; Get control byte back OUT CONT ; Send to control port RET ; * * * * * * * * ; ; WAIT Wait "A" ms ; ; Modifies: A,PSW (A = 0, Z flag set) ; ; * * * * * * * * WAIT: PUSH B WAIT1: MVI B,199 WAIT2: CMP M DB 10H,WAIT2-$-1 and 255 ; DJNZ WAIT1 IF CLOCK CALL CLKCHK ; tick toc ENDIF IF BUFFKBD CALL KEYCHK ; get keyboard   entry ENDIF DCR A DB JRNZ,WAIT1-$-1 and 255 ; JRNZ WAIT2 POP B RET ; * * * * * * * * ; ; GETTYPE Get the type byte and ptr for a drive unit ; ; Returns the current disk type identifier in the A reg, and the ; address of the entry in the HL reg pair. ; ; See PHYTAB (driver table) for the definition of the type byte. ; ; Modifies: A, DE, HL ; ; * * * * * * * * GETTYPE: LDA LOGDSK ; Use logical disk entry value CALL PAGET ; Get ptr to table entry INX H ; Type byte is FH IF CHARACTER READY, ELSE 00H IF LOCAL$STACK DW 73EDH,OLDSTK ; ld(oldstk),sp - save old stack LXI SP,LOCSTK ; set local stack ENDIF CONST1: IF CLOCK CALL CLKCHK ; tick ENDIF IF BUFFKBD CALL KEYSTAT ; see if char in buffer RNZ ; return with buffer status ENDIF LDA IOBYTE ;CHECK DEVICE ASSIGNMENT ANI 11B ;KEEP CON BITS ONLY JZ TTYIST ; check appropriate input status JMP CRTIST ; no third or fourth choices CONIN: ;CONSOLE CHARACTER INTO REGISTER A IF LOCAL$STACK at table+2 INX H ; . MOV A,M ; Get type byte RET ; Return type in A, ptr in HL GETSEC: ;CONVERT LOGICAL SECTOR TO PHYSICAL SECTOR PUSH H CALL GETTYPE ANI 3 ; 128 BYTE SECTOR? LDA CPMSEC ;NOPE, DO MAPPING DB JRZ,GOTSDS-$-1 LDA HSTSEC GOTSDS: PUSH PSW ;SAVE SECTOR CALL GETDPT ;FETCH POINTER TO XLT TABLE MOV E,M INX H MOV D,M XCHG ;TRANSLATE TABLE NOW IN HL POP PSW ;RESTORE DESIRED SECTOR MOV E,A MVI D,0 DAD D MOV A,M ;GET PHYSICAL SECTOR FROM TABLE M DW 73EDH,OLDSTK ; ld(oldstk),sp - save old stack LXI SP,LOCSTK ; local stack ENDIF CONIN1: IF BUFFKBD CALL KEYSTAT JNZ KEYRET ; get character from buffer ENDIF LDA IOBYTE ANI 11B JZ TTYIN ; do ttyin JMP CRTIN ; do crtin CONOUT: ;CONSOLE CHARACTER OUTPUT FROM REGISTER C IF LOCAL$STACK DW 73EDH,OLDSTK ; ld(oldstk),sp - save old stack LXI SP,LOCSTK ; set local stack ENDIF CONOUT1: IF CLOCK CALL CLKCHK ; tick toc ENDIF IF BUFFKBD CALL KEYCHK ; get any keyboard chOV E,A ;SAVE IT LDA PHYDRV CPI 04H ;WAS IT E DISK? DB JRNZ,GSEXIT-$-1 ;NO, THEN NO ADJUST LDA HSTSID ;WHICH SIDE? ORA A DB JRZ,GSEXIT-$-1 LDA ESECADJ ;ELSE GET ADJUSTMENT ADD E MOV E,A ;PUT IT BACK FOR MOVE GSEXIT: MOV A,E ;MOVE REAL SECTOR TO A POP H RET GETBUF: CALL GETTYPE ;GET THE DMA ADDRESS ANI 3 ; 128 BYTE SECTORS? LXI H,HSTBUF ;USED FOR DD RNZ LHLD DMAADR ;ELSE USE REAL DMA ADDRESS... RET ; * * * * * * * * ; ; GETTRK Convert logical to physical traracter ENDIF LDA IOBYTE ANI 11B JZ TTYOUT ; out tty JMP CRTOUT ; out crt READER: ;READ CHARACTER INTO REGISTER A FROM READER DEVICE JMP TTYIN ; no choice supported PUNCH: ;PUNCH CHARACTER FROM REGISTER C JMP TTYOUT ; no choices supported LISTST: ;RETURN LIST STATUS, 0FFH IF READY, ELSE 00H IF LOCAL$STACK DW 73EDH,OLDSTK ; ld(oldstk),sp - save old stack LXI SP,LOCSTK ; local stack ENDIF LSTST1: LDA IOBYTE ;CHECK DEVICE ASSIGNMENT ANI 11000000B ;KEEP LST BITS ONLY DB ack ; ; This routine converts the logical track stored in CPMTRK to ; the actual physical track and side to be used. ; ; The physical track is returned in the A register, and the side ; to be used is returned in HSTSID. ; ; * * * * * * * * GETTRK: PUSH H ; Save HL register CALL GETTYPE ; Get the type byte for this disk LXI H,HSTSID ; Point to the side flag RAL ; Determine SS or DS RAL ; . LDA CPMTRK ; Get track # DB JRNC,GETTRK1-$-1 ; Not DS -- leave track alone RAR ; DivJRNZ,LSTST2-$-1 ; tty ? JMP TTYOST ; check tty output status LSTST2: ANI 01000000B ;CRT? DB JRZ,LPTST-$-1 ; lpt DB JR,CRTOST-$-1 ; crt LIST: ;LIST CHARACTER OUTPUT FROM REGISTER C IF LOCAL$STACK DW 73EDH,OLDSTK ; ld(oldstk),sp - save old stack LXI SP,LOCSTK ; set stack pointer ENDIF LIST1: IF CLOCK CALL CLKCHK ; tic toc ENDIF IF BUFFKBD CALL KEYCHK ; get any keyboard characters ENDIF CALL LSTST1 ;WAIT TILL READY TO SEND DB JRZ,255-($-LIST1) LDA IOBYTE ;CHECK DEVIide track by 2 to get side DB JRNC,GETTRK1-$-1 ; No carry means side 0 MVI M,10H ; Indicate side 1 JMP GETTRK2 ; And finish up GETTRK1: MVI M,0 ; Indicate side 0 GETTRK2: POP H ; Get HL register back ANI 07FH ; Mask high bit, just in case RET ; ... and return ; LOGICAL DEVICE PHYSICAL DEVICE ASSIGNMENTS ; -------------- --------------------------- ; CON: CRT: OR TTY: ; READER: TTY: ; PUNCH: TTY: ; LIST: CRT: OR TTY: OR LPT: CONST: ;CONSOLE STATUS, RETURNS 0FCE ASSIGNMENT ANI 11000000B ;KEEP LST BITS ONLY JZ TTYOUT CPI 01000000B DB JRZ,CRTOUT-$-1 DB JR,LPTOUT-$-1 ; no forth choice CRTOST: ; CRT OUTPUT STATUS, RETURN 0FFH IF READY TO SEND, 00H IF NOT ; CRT "ALL SENT" STATUS NOW CHECKED ; Note: This former routine (IN SIOCPA ANI TBE RZ) requires ; autoenables to be set to function correctly. The present ; routine uses the "ALL SENT" bit to determine if the previous ; byte has been sent from the DART. Under the previous routine, ;  there is a possibility of a character getting into the output ; buffer when there is no ready signal. MVI A,01H ; Check "all sent" bit in register 1 OUT SIOCPA ; . IN SIOCPA ; . ANI 01H ; "ALL SENT" is bit 0 RZ ;TRANSMIT BUFFER NOT READY LDA HSA ;SEE IF CTS H/S REQUIRED ORA A DB JRZ,CRTRDY-$-1 ; if zero, no handshake needed ;CRT CTS HANDSHAKE SIGNAL STATUS NOW CHECKED MVI A,10H OUT SIOCPA ;UPDATE UART STATUS IN SIOCPA ;FETCH STATUS ANI CTS RZ ;CTS NOT ACTIVE CR NOW CHECKED MVI A,10H OUT SIOCPB ;UPDATE UART STATUS IN SIOCPB ;FETCH STATUS ANI CTS RZ ;CTS NOT ACTIVE TTYRDY: ORI 255 ;SHOW READY TO SEND RET TTYIST: ;TTY INPUT STATUS, RETURN 0FFH IF DATA READY, 00H IF NOT IN SIOCPB ;FETCH STATUS ANI RDA RZ ;NOT READY ORI 255 ;GOT SOMETHING, SIGNAL ITS PRESENCE RET TTYOUT: IF BUFFKBD CALL KEYCHK ; check for console char ENDIF IF CLOCK CALL CLKCHK ; tic toc ENDIF CALL TTYOST ;OK TO SEND? DB JRZ,255-($-TTYOUT) ;no TRDY: ORI 255 ;SHOW READY TO END RET CRTIST: ;CRT INPUT STATUS, RETURN 0FFH IF DATA READY, 00H IF NOT IN SIOCPA ;FETCH STATUS ANI RDA RZ ;NOT READY ORI 255 ;GOT SOMETHING, SIGNAL ITS PRESENCE RET CRTOUT: IF BUFFKBD ; Check for console input while waiting CALL KEYCHK ; to output a character (for handshake ENDIF ; intensive terminals, Versabrailles) IF CLOCK CALL CLKCHK ; tick toc ENDIF CALL CRTOST ;OK TO SEND? DB JRZ,255-($-CRTOUT) ; NO WAIT MOV A,C ;CHARACwait MOV A,C ;CHARACTER TO REGISTER A OUT SIODPB RET TTYIN: IF CLOCK CALL CLKCHK ; tick toc ENDIF IN SIOCPB ANI RDA ;CHAR READY? DB JRZ,255-($-TTYIN) ; no, wait IN SIODPB ;ELSE FETCH IT ANI 7FH ;STRIP PARITY BIT RET ; Keychk will poll the console for characters during disk waits or ; during common read/write routines and during terminal output. ; this will prevent most loss of input characters during disk i/o ; when programs are not polling for input. ; NOTE: tesTER TO REGISTER A OUT SIODPA RET CRTIN: IF CLOCK CALL CLKCHK ; tick toc ENDIF CALL CRTIST ;READY? DB JRZ,255-($-CRTIN) ; NO, WAIT IN SIODPA ;ELSE FETCH IT ANI 7FH ;STRIP PARITY BIT RET LPTOUT: ;PRINTER CHARACTER OUTPUT FROM REGISTER C IF BUFFKBD CALL KEYCHK ; test for console input ENDIF IF CLOCK CALL CLKCHK ; tick toc ENDIF CALL LPTST ;PRINTER READY? DB JRZ,255-($-LPTOUT) ; not ready, wait MOV A,C OUT PIO1 ;SET UP THE DATA OUT STBSET ;SEND A DATA STt for console input only (SIO A side). ; IF BUFFKBD KEYCHK: PUSH PSW IN SIOCPA ; get SIO status ANI RDA ; recive ready DB JRZ,KEYEXT-$ ; jrz,keyext+1 PUSH H ; save 'hl' LHLD KEYEND ; next character position in buffer LDA KEYLEN ; get low byte of max addr CMP L ; see if end of buffer DB JRZ,KEYEXT-$-1 ; no more buffer available IN SIODPA ; get data ANI 7FH ; strip parity MOV M,A ; save char INX H ; next position in buffer SHLD KEYEND ; save new pointer KEYEXTROBE OUT STBCLR ; (DATA DOESN'T MATTER) RET LPTST: ;RETURN LIST STATUS, 0FFH IF READY, ELSE 00H MVI A,10H ;UPDATE UART STATUS OUT SIOCPB IN SIOCPB ;READ PRINTER BUSY SIGNAL ANI 10H RZ ;NOT READY ORI 255 ;SHOW READY RET TTYOST: ;TTY OUTPUT STATUS, RETURN 0FFH IF READY TO SEND, 00H IF NOT ;TTY "ALL SENT" STATUS NOW CHECKED ; Note: This former routine (IN SIOCPB ANI TBE RZ) requires ; autoenables to be set to function correctly. The present ; ro: POP H POP PSW RET ; Keystat is called by the console stat routine. returns non-zero if ; character in buffer or zero if no character. ; KEYSTAT: PUSH B ; save 'B' register LDA KEYEND ; get low byte of last byte in buffer MOV B,A ; save LDA KEYST ; get low byte of start of buffer CMP B MVI A,0 DB JRZ,KSTEX-$-1 ; if same exit, nothing in buffer ORI 255 ; show buffer has character KSTEX: POP B ; restore register RET ; zero flag has status ; ; Keyret returns the next cutine uses the "ALL SENT" bit to determine if the previous ; byte has been sent from the DART. Under the previous routine, ; there is a possibility of a character getting into the output ; buffer when there is no ready signal. MVI A,01H ; Check "all sent" bit in register 1 OUT SIOCPB ; . IN SIOCPB ; . ANI 01H ; "ALL SENT" is bit 0 RZ ;TRANSMIT BUFFER NOT READY LDA HSB ;SEE IF CTS H/S REQUIRED ORA A DB JRZ,TTYRDY-$-1 ; if zero, no h/s needed ;TTY CTS HANDSHAKE SIGNAL STATUSharacter in buffer is called by conin ; KEYRET: PUSH H ; save registers PUSH B LHLD KEYST ; first character position MOV B,M ; get character INX H SHLD KEYST ; next character LDA KEYEND ; get low byte of last char in buffer CMP L ; same MOV A,B ; get character for exit DB JRNZ,KREX-$-1 ; LXI H,KEYBUF ; reset buffer start and end SHLD KEYST SHLD KEYEND KREX: POP B ; restore register POP H RET ENDIF ; clkchk will poll the ctc for change in count. Was adapted fro  m ; the clock routines developed by Roger Ward. IF CLOCK CLKCHK: PUSH H ; save registers PUSH PSW LXI H,TICK IN CTCA3 ; get down count value CMP M ; see if count has rolled over JZ CLKEXT ; same count value MOV M,A ; save new count JC CLKEXT ; counter < last count, not rolled LXI H,SECNT ; seconds count INR M ; increment seconds MVI A,60 CMP M DB JRNZ,CLKEXT-$-1 ; exit if no rollover MVI M,0 ; seconds to 0 INX H ; point to min. INR M ; one more min. CMP M 0, 0 HPARM: DB 64,0, 5,31, 1, 16,0, 255,3, 255,0, 0,0, 2,0, 0 IPARM: DB 64,0, 5,31, 1, 16,0, 255,3, 255,0, 0,0, 2,0, 0 JPARM: DB 64,0, 5,31, 1, 16,0, 255,3, 255,0, 0,0, 2,0, 0 KPARM: DB 64,0, 5,31, 1, 16,0, 255,3, 255,0, 0,0, 2,0, 0 LPARM: DB 64,0, 5,31, 1, 16,0, 255,3, 255,0, 0,0, 2,0, 0 MPARM: DB 64,0, 5,31, 1, 16,0, 255,3, 255,0, 0,0, 2,0, 0 NPARM: DB 64,0, 5,31, 1, 16,0, 255,3, 255,0, 0,0, 2,0, 0 OPARM: DB 64,0, 5,31, 1, 16,0, 255,3, 255,0, 0,0, 2,0, 0 PPARM: DB 64,0, 5,31, 1, 16,0, 25 ; 'a' = 60 DB JRNZ,CLKEXT-$-1 ; exit if no rollover MVI M,0 ; min=0 INX H ; point to hours INR M ; one more hour MVI A,24 CMP M DB JRNZ,CLKEXT-$-1 ; exit, same day still MVI M,0 ; hours=0 LHLD DAYCNT INX H ; increment days SHLD DAYCNT DB BIT,BTST+B0+ZH ; test less than >256 DB JRZ,CLKEXT-$-1 ; MVI A,366-256 ; days in calendar(include leap year) CMP L DB JRNZ,CLKEXT-$-1 ; exit no wrap LXI H,0 SHLD DAYCNT ; day 0 LXI H,YRCNT INR M MVI A,100 CMP M ; year wrap5,3, 255,0, 0,0, 2,0, 0 HD$DPB$LEN: EQU $-HD$DPB$BASE MY$ID: DB 0 ; Board SCSI ID saved here HD$BYTE$BLOCK: DB 0 ; SCSI Byte/block mode saved here ; * * * * * * * * ; ; DIRECT SCSI DRIVER ; ; Entry here via the BIOS Jump tables as JMP SCSI ; ; Enter with the following registers set: ; ; A SCSI target address to be used ; HL Pointer to SCSI Command block ; DE Pointer to SCSI Data Block ; ; Exits with HL preserved. DE is preserved if the operation is ; successful, otherwis ? DB JRNZ,CLKEXT-$-1 ; no, exit MVI M,0 ; else clear CLKEXT: POP PSW ; restore stack POP H RET TOD: LXI H,SECNT ; point to clock storage RET ; SECNT: DB 0 ; sec count MINCNT: DB 0 HRCNT: DB 0 DAYCNT DW 0 YRCNT: DB 0 TICK: DB 255 ; storage for clock tick ENDIF ; ; * * * * * * * * ; ; LOCSTK is the common return point for operations that use ; the local bios stack. Routine reatores the callers ; stack pointer and returns. ; * * * * * * * * ; local stack area e DE contains the sense data (error bytes). ; The A regisner contains the ending status of the SCSI command. ; ; NOTE: This routine saves and restores the previous TARGET ; address for normal operations. ; ; * * * * * * * * SCSI: MVI C,1 ; Set C register for 1 byte block SCSI$HSPEED: STA TARGET ; Save target address PUSH H ; Save entry command pointer PUSH D ; . and entry data pointer SHLD CMDPTR ; Set CMDPTR to what HL is pointing to XCHG ; Set the data pointer to HL CALL IF LOCAL$STACK DS 32 ; 16 level stack LOCSTK: DW EXIT ; exit routine OLDSTK: DS 2 ; save area for old stack EXIT: DW 7BEDH,OLDSTK ; ld sp,(oldstk) - restore stack RET ENDIF ; ; Storage for buffered keyboard ; IF BUFFKBD KEYST: DW KEYBUF ; Storage for begining of buffer pointer KEYEND: DW KEYBUF ; Storage for end of buffer pointer KEYLEN: DW KEYSTOP ; last address in keyboard buffer ENDIF MAINEND EQU $ ; End of main body of code IF HARD$DISK ; * * * * * * * * ; ; HARD D SCSI$CMD ; Do the direct routines JZ SCSI$END$OK ; If ok, restore cmd & data ptrs POP H ; Otherwise, throw away data ptr JMP SCSI$END$ERR ; . by popping cmd pointer twice SCSI$END$OK: POP D ; Restore data pointer SCSI$END$ERR: POP H ; Restore command pointer RET ; All done ; ; SCSI return sense data command (Cmd 03) ; SCSI$STAT$CMD: DB 3 ; 00 - REQUEST SENSE COMMAND DB 0 ; 01 - LOGICAL UNIT DB 0 ; 02 - RESERVED DB 0 ; 03 - RESERVED DB 4 ; 04 - NUISK Disk parameter blocks ; ; A system configuration entry, or a user defined assembly ; is to be used to set up the track offset entry to create ; various hard disk partitions. This is done to support drives ; with greater than 20Mbyte storage. ; ; * * * * * * * * HD$DPB$BASE: EQU $ ; ; # of --mask-- disk # of rsv'd chk trk ; sect bs bm em size d ent block siz ofs x FPARM: DB 64,0, 5,31, 1, 16,0, 255,3, 255,0, 0,0, 2,0, 0 GPARM: DB 64,0, 5,31, 1, 16,0, 255,3, 255,0, 0,0, 2,MBER OF BYTES DB 0 ; 05 - RESERVED ; ; SCSI read/write command (Cmd 08/0A) ; SCSI$RD$CMD: EQU 08H ; 08 IS READ DATA SCSI$WR$CMD: EQU 0AH ; 0A IS WRITE DATA SCSI$RW$CMD: DB SCSI$RD$CMD ; 00 - 08=Read, 0A=Write HIGH$ADDR: DB 0 ; 01 - High address MED$ADDR: DB 0 ; 02 - Middle address LOW$ADDR: DB 0 ; 03 - Low address DB 1 ; 04 - Number of sectors STEP$RATE: DB 0 ; 05 - Step rate (Xebec) ; * * * * * * * * ; ; Hard disk driver (Driver #3) ; ; FF Select Disk ; 00 W rite Disk ; 01 Read Disk ; ; * * * * * * * * DRVR3: INR A ; select command = 0ffh JZ SELEND ; do select LXI H,TAGPHY ; Get address & step rate MOV A,M ; . ANI 0FH ; Mask off address STA STEP$RATE ; Store step rate in SCSI cmd INX H ; Bump ptr to unit # MOV A,M ; Get unit # & type ANI 0E0H ; Mask off type STA LOGUNIT ; Save unit # INX H ; Bump ptr to SCSI addr MOV A,M ; Get SCSI address STA TARGET ; Save as target address ; fall through to SCSI$IO and do the read/wr SHLD DATPTR LXI H,SCSI$STAT$CMD ; Set up data and command pointers SHLD CMDPTR LDA LOGUNIT ; Get logical unit number INX H ; Stuff in proper location in MOV M,A ; . SCSI command CALL SELECT ; Execute request sense command ; Restore pointers and retry command LHLD SAVE$DATPTR ; restore old data pointer SHLD DATPTR LHLD SAVE$CMDPTR ; restore old command pointer SHLD CMDPTR JMP SCSI$CMD$RETRY ; command retry SCSI$DONE: ORA A ; Set Z/NZ for user RET ; and return ; ite ; * * * * * * * * ; ; SCSI READ/WRITE HOST ROUTINES ; ; Entry here from the HOSTIO section of the blocking/deblock ; routines. Assumes that the TARGET address has been pre-set. ; ; Exits with the error code in (A), and the sense status saved ; at SCSI$STAT$DAT if an error occurs. ; ; * * * * * * * * SCSI$IO: LDA RWHOST ANA A ; write = 0 ; ; Write to the hard disk described by (TARGET) and (LOGUNIT) ; SCSI$WR: MVI A,SCSI$WR$CMD ; Set write command DB JRZ,SCSI$DO$RW-$-* * * * * * * * ; ; Build 3-byte SCSI sector number and logical unit number ; ; NOTE: This routine assumes 16 sectors per track. ; ; It is safe to assume 16 sectors per track even though some hard ; disk controllers format more (Xebec=17, Adaptec=18, etc.) as the ; read and write commands use a block number, rather than a track ; and sector number to move to the correct block to read or write. ; ; * * * * * * * * BLD$SCSI$SCTR: XRA A ; Clear A & Carry ; A reg C HL reg pair 1 ; ; Read from the hard disk described by (TARGET) and (LOGUNIT) ; SCSI$RD: MVI A,SCSI$RD$CMD ; Set read command ; ; Do the rest of the setup for the read/write command ; SCSI$DO$RW: LXI H,SCSI$RW$CMD ; Get command string MOV M,A ; Plug the SCSI command SHLD CMDPTR ; Save the command pointer CALL BLD$SCSI$SCTR ; Build SCSI sector address LXI H,HSTBUF ; Get pointer to host buffer LDA HD$BYTE$BLOCK ; Get SCSI byte/block mode value MOV C,A ; Stuff in C register ; ; E LHLD CPMTRK ; -------- - FEDCBA9876543210 DAD H ; -------- F EDCBA9876543210- RAL ; -------F - EDCBA9876543210- DAD H ; -------F E DCBA9876543210-- RAL ; ------FE - DCBA9876543210-- DAD H ; ------FE D CBA9876543210--- RAL ; -----FED - CBA9876543210--- DAD H ; -----FED C BA9876543210---- RAL ; ----FEDC - BA9876543210---- MOV B,A ; LDA LOGUNIT ; ORA B ; Or in logical unit number STA HIGH$ADDR ; Save unit number & high byte MOV A,H ; STA MED$Anter here from the SCSI direct driver routine ; ; Exits with status in A. 0FFH = timeout error ; SCSI$CMD: SHLD DATPTR ; Save the data pointer LXI H,INT$RETRIES ; retry save area MVI M,2 ; retries LXI H,SCSI$IO$COUNT ; And the # of bytes to transfer MOV M,C ; . at one time (each block) SCSI$CMD$RETRY: CALL SELECT ; Perform the SCSI operation LDA STATUS ; Get the return status STA ERFLAG ; Save ending status CPI 0FFH ; Timeout? DB JRZ,SCSI$DONE-$-1 ; Yes, go save tiDDR ; Save middle byte LDA HSTSEC ; Get sector number ORA L ; Or in low byte STA LOW$ADDR ; Save new low byte RET ; ; Select controller, and fall through to phase if selected ok. ; busbsy: equ 40h SELECT: XRA A OUT NCRICR ; Clear initiator command register OUT NCRTCR ; . and target command register CLEAR$ARBIT: XRA A OUT NCRMR ; . IN NCRRPI ; reset interrupts ENDIF IF HARD$DISK AND ARBITRATION ARBITRATE: LDA MY$ID ; Assert my ID (the initiator) OUT NCRODR meout status ANI 2 ; Check for SCSI error status RZ ; No error -- return LXI H,INT$RETRIES ; see if any retries remain DCR M DB JRZ,SCSI$DONE-$-1 ; NO more remain, exit and set flags ; Save current command and data pointers. LHLD DATPTR ; save old data pointer SHLD SAVE$DATPTR LHLD CMDPTR ; save old command pointer SHLD SAVE$CMDPTR MVI A,1 STA SCSI$IO$COUNT ; force byte xfer mode for retries.. ; Request SCSI sense. LXI H,SCSI$STAT$DAT ; . for request sense command ; . MVI A,1 ; start arbitration OUT NCRMR ; . IN$PROGRESS: IN NCRICR ; Wait for "arbitration in ANI 40H ; . progress" bit JNZ ARBITRATE$WON ; we have arbitration IN NCRBSR ANI 10H ; see if scsi reset has occured JZ IN$PROGRESS JMP CLEAR$ARBIT ARBITRATE$WON: LDA MY$ID MOV B,A IN NCRCSD ; See if we're the highest priority SUB B ; . remove my addr SUB B ; . compare my addr to bus data JM I$WIN ; We win if result < 0 JMP CLEAR$ARBIT ; . otherwise we lose -- s tart over I$WIN: IN NCRICR ; Check again for lost arbitration ANI 20H ; . (just in case) JNZ CLEAR$ARBIT ; We lost -- start over MVI A,08H ; Set assert BSY bit in ICR OUT NCRICR ; . IN NCRMR ; Reset arbitration bit ANI 0FEH ; . OUT NCRMR ; . IN NCRICR ; OR in SEL to ICR ORI 04H OUT NCRICR ENDIF IF HARD$DISK LDA MY$ID ; Select target: get our ID, MOV B,A ; . LDA TARGET ; . or in target ID ORA B ; . OUT NCRODR ; . and send to NCR chip IN NCRIrwise the interrupt flag may not clear and the DMA Mode ; may not be useable. XRA A ; Clear 5380 Mode Register OUT NCRMR ; . IN NCRRPI ; Reset interrupts MVI A,00000110B ; Set DMA mode and Monitor Busy OUT NCRMR ; . IN NCRCSBS ; Update phase... ANI 00011100B ; Mask all but phase bits, clear carry bit RAR ; Rotate over for target MOV E,A ; . (Save for use with jump table) RAR ; . OUT NCRTCR ; Set phase MVI D,0 ; E is already set (3 insCR ORI 01H ; Assert data bus OUT NCRICR ; . MVI A,05H ; Release BSY, keep SEL OUT NCRICR ; . and assert data bus LXI B,6000H ; 250 ms loop (1M cycles) STIM: IN NCRCSBS ; Wait for BSY ANI BUSBSY ; . JNZ SELECT$OK ; Got him! DCR C JNZ STIM ; inner loop: 41*256 = 10496 cycles DCR B JNZ STIM ; outer loop: 10510*96 = 1M cycles XRA A ; Select timeout -- clear bus OUT NCRODR DCR A ; set timeout to 'a' STA STATUS JMP ALL$DONE ; and clear the registers SE ago) LXI H,PHASE$TABLE ; Get phase jump table base DAD D ; Add offset for this phase MOV A,M ; Get phase pointer into HL INX H ; . MOV H,M ; . MOV L,A ; Pointer is now together MVI D,01000000B ; DMA request mask(used by RSCSI and WSCSI) PCHL ; Go to it! PHASE$TABLE: DW PHASE0 DW PHASE1 DW PHASE2 DW PHASE3 DW PHASE4 DW PHASE5 DW PHASE6 DW PHASE7 PHASE0: ; Data out phase ... LXI H,SCSI$IO$COUNT ; Point to byte/block transfer mode LECT$OK: XRA A ; Set good status ALL$DONE: MOV B,A ; save status MVI A,01H ; Release SEL OUT NCRICR ; . XRA A ; Release data bus OUT NCRICR ; . MOV A,B ; get status back ORA A ; Set status RNZ DCR A ; clear scsi status to timeout, 0ffh STA STATUS ; Save status for PWIDIR routine ; SCSI.011 ; * * * * * ; * --------\ NOTE: we fall through if we successfully ; * --------/ selected the controller!! ; * * * * * MVI A,00000110B ; Set DMA mode and Monitor Busy MOV E,M ; Get transfer mode value LHLD DATPTR ; Use data pointer JMP WSCSI ; Execute SCSI write routine PHASE1: ; Data in phase ... LXI H,SCSI$IO$COUNT ; Point to byte/block transfer mode MOV E,M ; Get transfer mode value LHLD DATPTR ; Use data pointer JMP RSCSI ; Execute SCSI read routine PHASE2: ; Command out phase ... LHLD CMDPTR ; Use command pointer MVI E,1 ; Set mode to byte transfer JMP WSCSI ; Execute SCSI write routine PHASE3: ; StatOUT NCRMR ; . SCSI$RDY: ; Wait for either a 5380 "Interrupt" or a REQ from Target. ; The REQ is needed since it may have come too soon after ; selection to register an Interrupt. IN NCRBSR ; Check for "Interrupt" ANI 00010000B ; . DB JRNZ,SCSI$INT-$-1 IN NCRCSBS ; Check for REQ ANI NCRREQ ; . ; Wait for Interrupt or REQ DB JRZ,255-($-SCSI$RDY) JMP PHASE ; Process phase vector SCSI$INT: ; Determine cause of 5380 "Interrupt". Either phase ; changed, busy dropped, or bus us in phase ... LXI H,STATUS ; Use status pointer MVI E,1 ; Set mode to byte transfer JMP RSCSI ; Execute SCSI read routine PHASE7: ; Message in phase ... LXI H,MESSAGE ; Use message pointer MVI E,1 ; Set mode to byte transfer JMP RSCSI ; Execute SCSI read routine ; Currently unused phases PHASE4: PHASE5: PHASE6: SCSI$EXIT: XRA A ; Clean up 5380 and exit. OUT NCRTCR ; . OUT NCRMR ; . IN NCRRPI ; Reset interrupts RET ; . ; Generalized SCSI was reset. If bits 2 and 3 ; of the NCRBSR are not 0's when the Interrupt flag (bit 4) ; is set, then it is either a loss of BUSY or an SCSI RESET. XRA A OUT NCRICR ; Release data bus IN NCRBSR ; Read 5380 Bus and Stat Reg ANI 00001100B ; Keep interesting bits DB JRNZ,SCSI$EXIT-$-1 ; Reset or Busy Loss: Exit ; 00 --> Process phase vector PHASE: ; DMA mode and Monitor Busy must be cleared prior to clearing ; of the 5380 Interrupt Flag. Then mode register is restored. ; Othewrite routine WSCSI: MVI A,1 ; Assert data bus OUT NCRICR MVI C,NCRDACK ; Set up destination port address OUT NCRSDS ; Start DMA send ; Wait for DMA request, keeping an eye on phase. Note that the NCR ; will not issue an ACK, nor will it generate DMA requests once the ; phase changes, so it is best to treat DMA request checking as a ; higher priority than phase change checking. WSCSI1: IN NCRBSR MOV B,A ; Save status for use below ANA D ; Check  for DMA request DB JRZ,WSCSI2-$-1 ; This is the heart of the pseudo-DMA transfer. On entry, HL points ; to the data buffer and E should be 0 for 256 byte block transfer, ; or 1 for byte-by-byte transfer. NOTE: Use block transfer only if ; you can be sure the controller can buffer 256 bytes of data and ; can transfer at 5.25 us per byte. Extra DACK's after the last REQ ; will do no harm. ; ; OTIR register use: H = memory pointer, C = I/O port, B = counter ; MOV B,E ; Set up loop coun OUT CONT ; . selected (1770 turns it off) LDA IOBYT ; Setup initial IOBYTE value STA IOBYTE ; . LXI SP,80H ; Initialize DART, CTC, etc. CALL IOINIT ; . IF HARD$DISK ; Init HD parameters, if present IN 029h ; Get ID value ANI 07H ; Mask off extra bits PUSH PSW ; . (Put ID in inital message) ADI '0' ; . . STA SCSI$ID$FOUND ; . . POP PSW ; . . INR A ; Bump to make 1 - 8 MOV B,A ; Convert to SCSI address XRA A ; . (Clear receiving register) STC ; . (Carry = bitt DW OTIR80 ; ED B3 = OTIR instruction op code JMP WSCSI1 ; Write more bytes until phase changes ; This code skipped when data is being transferred ... WSCSI2: MOV A,B ; Check 5380 "interrupt" flag ANI 00010000B ; . JZ WSCSI1 ; Wait for DMA request, JMP SCSI$INT ; or process "interrupt" ; Generalized SCSI read routine RSCSI: ; Initiator command reg is already initialized MVI C,NCRDACK ; Source port address OUT NCRSDIR ; Write to this port starts dma recieve  to rotate) NEXT$BIT: ; . RAL ; . (Rotate left through carry) DCR B ; . (Decrement counter) JNZ NEXT$BIT ; . (Not done -- repeat) STA MY$ID ; Save ID value ENDIF ; SCSI reset is done by SCSI boot EPROM, and so should not ; be repeated here unless boot is from floppy. In any ; case, only boards jumpered for SCSI ID # 7 will do reset. IF HARD$DISK AND SCSI$RESET CPI 1000$0000B ; Perform SCSI reset if this is ID # 7 DB JRNZ,NOT7-$-1 ; . MVI A,80H ; Set reset line high OUT ; Wait for DMA request, keeping an eye on phase. Note: we must do ; a check for DMA request before checking for a phase change, since ; a byte may be queued up waiting to be DACKed prior to the phase ; change. ; RSCSI1: IN NCRBSR MOV B,A ; Keep for phase change checking ANA D ; Mask for DMA request DB JRZ,RSCSI2-$-1 ; This is the heart of the pseudo-DMA transfer. On entry, HL points ; to the data buffer and E should be 0 for 256 byte block transfer, ; or 1 for byte- NCRICR ; . HOLD$RESET: DCR A ; Hold reset high for at least 50us DB JRNZ,HOLD$RESET-$-1 and 255 OUT NCRICR ; Clear reset line IN NCRRPI ; . and interrupt line ENDIF IF HARD$DISK NOT7: XRA A ; Clear 5380 registers (SCSI init) OUT NCRICR ; . OUT NCRMR ; . OUT NCRTCR ; . OUT NCRSER ; . ENDIF ; End of HD initialization XRA A ; Patch out old CP/M message STA CPMMSG LXI H,LOGMSG ; Display signon message CALL PUTS LXI H,Z80MSG CALL PUTS LXby-byte transfer. NOTE: Use block transfer only if ; you can be sure the controller can buffer 256 bytes of data and ; can transfer at 5.25 us per byte. Extra DACK's after the last REQ ; will do no harm. ; ; INIR register use: H = memory pointer, C = I/O port, B = counter ; MOV B,E ; Set up loop count DW INIR80 ; ED B2 = INIR instruction op code JMP RSCSI1 ; Read more bytes until phase changes ; Be sure and check phase if no DMA ; request, since NCR won't issue any I H,DRIVE$TDATA ; Init drive type data LXI D,DRIVE$TYPES LXI B,8 DW LDIR80 IF ZCPR3 ; Init ZCPR3 stuff if present LXI H,LAST$SG ; Set up HL and DE to clear memory LXI D,LAST$SG+1 ; from the end of boot to 0ffffh MVI M,0 MOV A,H CMA MOV B,A MOV A,L CMA MOV C,A DW LDIR80 ZBOOT: LXI B,3 ; Set up the ZCPR3 command line LXI H,CMDSET ; . pointers LXI D,Z3CL ; DW LDIR80 ; LXI B,10 ; Move the automatic command to LXI H,AUTOCMD ; . the ZCPR3 command line LXI D, ; unneeded DACKs ; This code skipped when data is being transferred ... RSCSI2: MOV A,B ; Check 5380 "interrupt" flag ANI 00010000B ; . JZ RSCSI1 ; Wait for DMA request, JMP SCSI$INT ; or process "interrupt" ENDIF HDCODE EQU $ ; End of hard disk code ; * * * * * * * * ; ; COLD BOOT ENTRY ; ; NOTE: The following code does not stay resident. It is ; overlayed for use by all bios system variables. ; ; * * * * * * * * BOOT: MVI A,41H ; Turn off EPROM, but leave drive 0 Z3CL+3 ; DW LDIR80 ; LXI B,11 ; Move the initial path descriptor LXI H,PATH ; . to the proper location LXI D,EXPATH ; DW LDIR80 ; LXI H,Z3WHL ; Turn the wheel byte on MVI M,0FFH ; LXI H,ENV ; Move environment and TCAP to the LXI D,Z3ENV ; . proper location LXI B,ENVEND-ENV+1 ; . DW LDIR80 ; ENDIF ; ZCPR3 init XRA A ; STA CDISK ; Indicate disk 0 selected STA HSTACT ; Set host buffer inactive STA UNACNT ; Clear unalloc count STA HSTSID ; Assume side  zero JMP GOCPM ; Initialize & jump to CP/M LOGMSG: DB CR,LF,LF DB MSIZE/10 +'0',MSIZE MOD 10 +'0' CPMMSG: DB 'k CP/M vers 2.2' Z80MSG: DB 'K Z80 Operating System' DB CR,LF,'AMPRO BIOS Version ',VERS/10+'0','.' DB VERS MOD 10+'0' IF INTERNAL ; Display internal revision # DB 'x',INT$REV/10+'0',INT$REV MOD 10 + '0' DB ' [',THIS$YEAR-80+'@' DB THIS$MONTH+'0'+((THIS$MONTH/10)*7),'.' DB THIS$DAY/10+'0',THIS$DAY MOD 10 + '0',']' ENDIF DB CR,LF,'Copyright (C) 1983,8# ~O# ~# 1 0x+~ 1 d\ \0 f ^_yss{ y0{ Ì'ͯͯd ͯ ͯ}0 }o|gھ ñ}o|gy>  y0>.> > * . O* . )   OGH NWoH dWor# xo*}ʄ$. Ɉ* . O* . )   OGH NWoH dWor# xo*}ʄ4,85,86 ' DB 'AMPRO Computers, Inc.' IF HARD$DISK ; Display SCSI ID for this unit DB CR,LF,LF,'SCSI initiator ID = ' SCSI$ID$FOUND: DB 'x' ENDIF IF HARD$DISK AND ARBITRATION DB CR,LF,'(Arbitration Enabled)' ENDIF IF HARD$DISK AND (NOT ARBITRATION) DB CR,LF,'(Arbitration Disabled)' ENDIF IF CLOCK DB CR,LF,'(Clock Enabled)' ENDIF IF BUFFKBD DB CR,LF,'(Console Buffer Enabled)' ENDIF IF INTERNAL DB CR,LF,'WARNING!!! Unreleased BIOS.' ENDIF DB CR,LF,0 IF ZCPR3 ;  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 1984,85,86 AMPRO COMPUTERS, INC. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ; Assemble with asm.com or equivalent. ; All Z80 opcodes are defined with DB or DW statements. ; Revision history: ; ; Ver Date Who Description ; --- ----- --- -------------------------------------------- ; 1.0 E7.15 RJB Original version ; 1.1 F1.30 RBL Now includes Shugart con Z3ENV * R!] ~  >?E] /|DIR 1.0 Syntax: DIR dir:afn o Options: A=All, S=System, T=File Type/Name SorX!m ~#ʼ ʉTʨSʯA‰>ñy OÉ>@y?OOÉ̈́xyY Ovf! " !q#p22xy:|#+* " ͆  ~$rx:<2e:<2+##=e2Pause - e  x: > }.xAyDʨ~# * --͆*  Files Using͆K ͆K Left ~~#>2troller option ; 1.2 F4.04 RBL Now includes Seagate 225N ; Program version, and current version date VERS EQU 12 ; Current version THIS$MONTH EQU 4 ; Today's month THIS$DAY EQU 04 ; . day THIS$YEAR EQU 86 ; . year INT$REV EQU 99 ; Internal revision number ; TRUE and FALSE are defined here NO EQU 0 FALSE EQU 0 YES EQU NOT FALSE TRUE EQU NOT FALSE ; Operating characteristics MIN$VERSION EQU 20 ; Minimum bios version allowed CMD$LINE$OK EQU TRUE ; Allow comm=A ~ "#~+"##= *V/ F##x_ *V ^#V#~ѷ"VU on other LXI H,RET$TO$PARK$C ; . controllers CALL PROMPT ; . JMP SKIP$BLOCK ; . GET$BLOCK: LXI D,BLOCK$MSG ; Get block to park on CALL PROMPT$DECIMAL ; . JZ AGAIN ; . LXI H,SCSI$BLOCK ; Save block in buffer area MOV M,B ; . INX H ; . MOV M,C ; . INX H ; . 0,0,0,0,0 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Various messages . . . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * S$ADDR$MSG: DB CR,LF DB 'What is the SCSI address of your controller ' DB '(0-7)? ','$' S$ADDR$OKC: DB '01234567',0 L$UNIT$MSG: DB CR,LF DB 'What is the logical unit number of the drive ' DB 'you wish to park (0-3)? ','$' L$UNIT$OKC: DB '0123',0 ; Logical unit numbers HDC$MSG: DB CR,LF D MOV M,D ; . INX H ; . MOV M,E ; . SKIP$BLOCK: CALL PARK ; Perform park JMP AGAIN ; Go back for more ... ALL$DONE: ; Don't return to CP/M LXI D,ALL$PARKED ; Give 'all parked message' CALL CENTER$OUTPUT ; . LOONY$TUNES: JMP LOONY$TUNES ; Th-th-th-that's all, folks! * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * General purpose routines . . . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * DO$SCSI: INX H LDA DRV$B 'Of the following list of controllers:',CR,LF DB TAB,'G - Generic SCSI controller',CR,LF DB TAB,' (Adaptec, Seagate 225N, Shugart 1610-4, etc.)',CR,LF DB TAB,'O - Xebec Owl',CR,LF DB TAB,'# - Any other hard disk controller',CR,LF DB 'Which controller are you using (A,O,S, or #)? ','$' HDC$OKC: DB '#OGAS',0 BLOCK$MSG: DB CR,LF DB 'Park the drive at which block address? ','$' RET$TO$PARK: DB CR,LF DB 'Press the key to park this drive: ','$' RET$TO$PARK$C: DB CR,'0' LUN ORA M MOV M,A DCX H LDA SCSI$ADDR CALL LB$SCSIDRV ORA A RET PARK: MVI A,0FFH ; Set "No response" flag STA DRV$RESPONSE ; . LXI H,HDC$COMMAND ; Clear HDC command bytes LXI D,HDC$COMMAND+1 ; . LXI B,9 ; . MVI M,0 ; . DW LDIR80 ; . LDA CONTROLLER ; Init HDC for this drive LXI H,PARK$EXE ; . JMP GO$TABLE ; . PARK$EXE: DW PARK$AT$BLOCK ; Park at block # DW PARK$OWL ; Xebec Owl DW PARK$GENERIC ; PARK Generic SCSI drive DW PARK$GENERIC ; PARK Generic SCSI SCSI$BLOCK: DW 0,0 UNIT$ID: DB 0 CONTROLLER: DB 0 DRV$RESPONSE: DB 0 PARK$RESULT: DB '>>> SCSI ID ' RESULT$ID: DB 'x, Logical unit ' RESULT$LUN: DB 'x parked. <<<',CR,LF,'$' NO$UNITS: DB '>>> SCSI ID ' NO$UNITS$ID: DB 'x, Logical unit ' NO$UNITS$UNIT: DB 'x did not respond. <<<',CR,LF,'$' ALL$PARKED: DB CR,LF,LF,'>>> You may now turn off ' DB 'your system. <<<',CR,LF,'$' * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Library r outines . . . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * CENTER$OUTPUT: ; ; [DC.20] ; ; Automatically centers the output line(s) based on the line width ; stored in SCREEN$WIDTH. Each line is delimited with CR+LF. This ; routine will return to the caller when it encounters the string ; terminator, '$'. Any additional LF characters after a CR+LF pair ; will be passed through. ; ; Entry: ; DE = Pointer to output string(s), terminated with CR+LF. ; ; Exit: ; CALL BDOS POP H POP D POP B POP PSW RET CON$CHR$AC: ; ; [DC.20] ; ; This routine sends the character in the C register to the console ; the number of times in the A register. ; ; Entry: ; A = Number of times to send character ; C = Character to send ; ; Exit: ; Same ; ; Modifies: ; None ; PUSH PSW ; Save all registers PUSH B ; . PUSH D ; . PUSH H ; . MOV B,A ; Move data to accomodate CP/M MOV A,C ; . NEXT$CHR$OUT: CALL CON$CHR ; Send 1 chr DCR B ; D The output string(s) are sent to the screen ; ; Modifies: DE ; PUSH PSW ; Save registers PUSH B ; . PUSH H ; . CENTER$NEXT$LN: CALL GET$STRLEN ; Get length to next CR or '$' in B MOV A,B ; Check for zero length ORA A ; . JZ NEXT$DELIM ; If so, output the CR, LF, etc. LDA SCREEN$WIDTH ; Compute offset needed to center line STC ; . SBB B ; . (if there are too many chrs, just JC NO$BLANKS ; . print the line as is . . . ) ANI 0FEh ; Clear least significant bit RRC ; ecrement counter JNZ NEXT$CHR$OUT ; Done? POP H ; Restore all registers POP D ; . POP B ; . POP PSW ; . RET ; and return CONIN$NE$XC: ; ; [E1.28] ; ; Console input, no echo, exit on ctrl-c ; ; Entry: ; none ; ; Exit: ; A = character from console, except for ctrl-c, which causes ; an immediate jump to ALL$DONE ; ; Modifies: ; A ; CALL LB$CONIN CPI CTRLC JZ ALL$DONE RET CON$MSG: ; ; [DC.20] ; ; Console message ; ; Entry: ; DE = pointer to messag. and rotate to divide by two MVI C,' ' ; Output enough blanks to center line CNZ CON$CHR$AC ; . (only if count is non-zero) NO$BLANKS: MOV A,M ; Save CR for later MVI M,'$' ; Plug position with '$' for cp/m CALL CON$MSG ; . print string function MOV M,A ; Restore saved CR NEXT$DELIM: MOV A,M ; Get character CPI CR ; Print it if CR JZ OUTPUT$DELIM ; . CPI LF ; Print it if LF JZ OUTPUT$DELIM ; . CPI EOS ; Stop processing if EOS ('$') JZ CENTER$DONE ; . XCHG ; Put ne string, terminated with '$' ; ; Exit: ; message printed on console ; ; Modifies: A, BC ; PUSH PSW ; Save registers PUSH B ; . PUSH D ; . PUSH H ; . MVI C,9 ; BDOS print string command CALL BDOS ; . POP H ; Restore registers POP D ; . POP B ; . POP PSW ; . RET ; and return DISPLAY$FDEV: ; ; [E2.15] ; ; LXI D,D$FDEV$HDR ; Print header CALL CENTER$OUTPUT ; . LXI H,ACTIVE$FDEV ; Get pointer to active floppies LXI B,0004H ; Set starting and maximum # ew pointer in DE JMP CENTER$NEXT$LN ; Go & do the next line OUTPUT$DELIM: CALL CON$CHR ; Output delimiter CPI LF ; Wait 10ms if we have a line feed CZ WAIT ; . INX H ; Point to next chr JMP NEXT$DELIM ; and check that one, also CENTER$DONE: POP H ; Restore registers POP B ; . POP PSW ; . RET ; and return CLEAR$SCREEN: ; ; [E1.28] ; ; This routine clears the screen by calling DO$CRLF 26 times. ; ; Entry: ; None ; ; Exit: ; The screen is cleared ; ; Modifies: ; D$NEXT$FDEV: MOV A,M ; Get unit # ORA A ; If zero, skip to next JZ D$BUMP$PTR ; . PUSH H ; Copy output line to output buffer PUSH B ; . LXI H,D$FDEV$LIN ; . LXI D,OUTBUF ; . LXI B,D$FDEV$HLEN ; . DW LDIR80 ; . POP B ; . POP H ; . PUSH PSW ; update 'current' drive letter ANI 07FH ; . (mask out 'E-disk' bit) STA OUTBUF+D$CURRENT; . POP PSW ; . ANI 080H ; get 'E-disk' bit back JNZ D$EDISK$OK ; 'E-disk' present, leave E: alone PUSH H ; . PUSH B ; . LX None ; PUSH PSW ; Save just in case MVI A,26 ; 26 CRLF's C$NEXT$LINE: CALL DO$CRLF ; Next line DCR A ; Done? JNZ C$NEXT$LINE ; Nope. POP PSW ; Restore original AF RET ; and return CON$CHR: ; ; [DC.20] ; ; This routine sends the character in the "A" register to the console ; through the BDOS conout call. ; ; Entry: ; A = character to send ; ; Exit: ; character is sent to the console ; ; Modifies: ; None ; PUSH PSW PUSH B PUSH D PUSH H MVI C,2 MOV E,A I H,D$EBLANK ; . LXI D,OUTBUF+D$EDISK; . LXI B,4 ; . DW LDIR80 ; . POP B ; . POP H ; . D$EDISK$OK: PUSH B ; PUSH H ; MOV A,B ; update floppy device number LXI H,FNAMES ; . LXI B,FNAMES$LEN ; . CALL INDEX$TABLE ; . LXI D,OUTBUF+D$FNAME; . LXI B,FNAMES$LEN ; . DW LDIR80 ; . POP H ; . POP B ; . LXI D,OUTBUF ; and output the line CALL CENTER$OUTPUT ; . D$BUMP$PTR: INX H ; Bump pointer to next device, INR B ; increment letter count, MOV A,B ; Compare t o maximum, CMP C ; . JNZ D$NEXT$FDEV ; Do another if we're not done. RET D$FDEV$HDR: DB '- FLOPPY DISK ASSIGNMENTS -',CR,LF DB 'CP/M drive ' DB 'Floppy disk',CR,LF DB '------------------------',CR,LF,'$' D$FDEV$LIN: DB ' ' D$CURRENT: EQU $-D$FDEV$LIN DB 'x ' D$EDISK: EQU $-D$FDEV$LIN DB '(E)' D$EBLANK: DB ' ' D$FNAME: EQU $-D$FDEV$LIN DB ' ' DB CR,LF,'$' D$FDEV$HLEN EQU $-D$FDEV$LIN ; Line length FNAMES: DB 'First ' DB 'Second' DB 'Third  device INX H ; . INX H ; . INX H ; . INX H ; . MOV B,M ; . LHLD CPM$PTR ; Get ptr to CP/M letter area MOV M,B ; Save CP/M drive letter INX H ; Bump pointer INX H ; . SHLD CPM$PTR ; Save CP/M letter area pointer back D$NEXT$UNIT: LDA TOTAL$ACTIVE ; Get count of active units MOV B,A ; Save for a moment LDA LOGICAL$UNIT ; Get unit we're working on INR A ; Bump to next CMP B ; Are we done? JNZ D$GET$UNIT ; No -- go do another CALL D$SEND$LINE ; Send this line' DB 'Fourth' FNAMES$LEN: EQU 6 ; end of DISPLAY$FDEV DISPLAY$SDEV: ; ; [E2.15] ; ; Display the hard disk data in the active device table. ; ; Entry ; Active devices in the ACTIVE$SDEV area ; ; Exit ; Devices are displayed on the console ; ; Modifies ; All ; LDA TOTAL$ACTIVE ; Check how many devices ORA A ; . RZ ; Return if no devices active LXI D,FD$HEADER ; Display table header CALL CENTER$OUTPUT ; . LXI D,LAST$SCSI ; Init local data LXI B,04FFH ; . (fill la out, too. CALL DO$CRLF ; Send out an extra CR+LF. RET ; all done D$SEND$LINE: PUSH PSW ; Save A & flags and PUSH D ; . D just in case LXI D,OUTBUF ; Send line to console CALL CENTER$OUTPUT ; . D$SEND$DONE: POP D ; POP PSW RET LOGICAL$UNIT: DB 0 ; Current logical unit LAST$SCSI: DB 0 ; Last hard disk scsi address LAST$CTRL: DB 0 ; Last hard disk controller LAST$DTYPE: DB 0 ; Last hard disk drive type LAST$DRIVE: DB 0 ; Last hard disk drive CPM$PTR: DW 0 ; Pointer to nest info with 0FFh's) CALL FILL$BLOCK ; . XRA A ; Current logical D$GET$UNIT: STA LOGICAL$UNIT ; save current unit # CALL INDEX$ACTIVE ; HL = address of active table entry LXI D,LAST$SCSI ; See if same device MVI B,4 ; Compare for 4 bytes CALL STR$COMP ; . JZ D$ADD$LETTER ; Same device -- add CP/M letter LDAX D ; Not same device, check to see ORA A ; . if last device was 'FF'. If so, JM D$DONT$DISP ; . don't display the line. CALL D$SEND$LINE ; Send line to console xt CP/M letter pos CT$NAME: DB 'Xebec ' DB 'Adaptec ' ; DB 'Data Technology' ; Supported later ; DB 'Western Digital' ; Supported later DB 'UNKNOWN ' CT$UN: DB 'UNKNOWN ' CT$NLEN: EQU $-CT$UN FD$HEADER: DB '- HARD DISK ASSIGNMENTS -',CR,LF DB 'CP/M drive(s) Controller ' DB 'Addr Unit #',CR,LF DB '-------------------------------------' DB '-------------',CR,LF,'$' DRIVE$INFO: EQU $ DRIVE$LETTERS: EQU $-DRIVE$INFO DB ' D$DONT$DISP: LXI B,4 ; Since DE & HL still point to last & DW LDIR80 ; . current, move current to last. PUSH H ; save ptr LXI H,DRIVE$INFO ; Copy template to output buffer LXI D,OUTBUF ; . LXI B,DRIVE$LEN ; . DW LDIR80 ; . LXI H,OUTBUF+DRIVE$LETTERS ; Setup CP/M letter ptr SHLD CPM$PTR ; . POP H ; Restore ptr to current PUSH H ; . and save it back MOV A,M ; Plug output buffer w/device info CALL SCSI$TO$BIN ; SCSI address ADI '0' ; . (convert to ascii 0-7) STA  ' DRIVE$CTRL: EQU $-DRIVE$INFO DB ' ' DRIVE$ADDRESS: EQU $-DRIVE$INFO DB '0 ' DRIVE$UNIT: EQU $-DRIVE$INFO DB '0' DB CR,LF,'$' DRIVE$LEN EQU $-DRIVE$INFO ; end of DISP$SDEV DO$CRLF: ; ; [DC.27] ; ; This routine sends a carriage return and a line feed to the terminal, ; and then waits 'SLOW$TERM' ms for a slow terminal to catch up. ; ; Entry: ; none ; ; Exit: ; CR + LF is sent to the screen. ; ; Modifies: ; none ; PUSH PSW ; Save AF OUTBUF+DRIVE$ADDRESS ; . INX H ; . MOV A,M ; Controller type PUSH H ; . LXI H,CT$NAME ; . LXI B,CT$NLEN ; . CALL INDEX$TABLE ; . LXI D,OUTBUF+DRIVE$CTRL ; . DW LDIR80 ; . POP H ; . INX H ; . INX H MOV A,M ; Logical unit # RLC ; . (move bits 7-5 to 2-0) RLC ; . RLC ; . ANI 07H ; . (mask out other bits) ADI '0' ; . (convert to ascii 0-7) STA OUTBUF+DRIVE$UNIT ; . POP H ; Restore ptr so we agree D$ADD$LETTER: INX H ; Get CP/M letter for this MVI A,0Dh ; Send carriage return CALL CON$CHR ; . MVI A,0Ah ; and line feed CALL CON$CHR ; . LDA SLOW$TERM ; Check slow flag ORA A ; . CNZ WAIT ; wait for the s-l-o-w terminals POP PSW ; recover original AF RET ; and return FILL$BLOCK: ; ; [E1.29] ; ; Fills the buffer pointed to by DE with the character in C for ; a length of B bytes (max 256). ; ; Entry: ; B = Length to fill ; C = Character to fill with ; DE = Start of buffer ; ; Exit: ; Buffer filled ; ; Mod ifies: ; Only buffer area affected ; PUSH B ; Save registers PUSH D ; . XCHG ; Set up M register use F$NEXT: MOV M,C ; Stuff a char INX H ; Bump pointer DCR B ; Bounce counter JNZ F$NEXT ; Do it again if not done XCHG ; We are done. restore old HL POP D ; Restore other registers POP B ; . RET ; and return GET$ACTIVE$DEV: ; ; [E2.15] ; ; Get the active devices. Store the floppy and E-disk descriptions ; in the ACTIVE$FDEV table, and store the SCSI descriptionA ; Save new disk letter JMP G$NEXT$DEV ; Get next device info G$EDISK: XCHG ; Get pointer to drive entry back CALL G$F$DRIVE ; Convert floppy drive MOV A,M ; Set high bit for E disk ORI 80h ; . MOV M,A ; . JMP G$NEXT$DEV ; Get next device info G$HARD: XCHG ; Get pointer to drive entry back INX H ; (+1) Controller, Drive, Partition MOV A,M ; Get controller (0-3) RRC ; . RRC ; . ANI 03H ; . STA CTRL$TYPE ; . MOV A,M ; Get drive type (0-3) ANI 03H ; . Ss in the ; ACTIVE$SDEV table. ; ; Entry: ; none ; ; Exit: ; The active device descriptions will be in tables. ; ; ACTIVE$FDEV (4 entries, 1 byte each) format: ; ; +0: Current CP/M letter and "E" disk status. ; (Bit 7 set indicates this device is the "E" disk) ; ; ACTIVE$SDEV (11 entries, 8 bytes each) format: ; ; +0: SCSI Address ; +1: Controller type (0-3) ; +2: Drive type (0-3) ; +3: Drive unit (0-7) -- shifted for use with SCSI commands ; +4: Logical partition (0-7) ; +5: CurrentTA DRV$TYPE ; . MOV A,M ; Get partition ANI 0F0H ; . STA PARTITION ; . INX H ; (+2) Unit # MOV A,M ; . ANI 0E0H ; . STA DRV$LUN ; . INX H ; (+3) SCSI address MOV A,M ; . STA SCSI$ADDR ; . POP PSW ; PUSH PSW ; Get CP/M letter ADI 'A' ; . STA CPM$LETTER ; . LHLD NEXT$POS ; Get next buffer position LXI D,SCSI$ADDR ; . and current scratch area XCHG ; Now DE=buffer, HL=scratch LXI B,8 ; Save data DW LDIR80 ; . LXI H,8 ; Update buffer pointer DAD D  CP/M letter ; +6: spare ; +7: spare ; ; Calls: ; LB$GET$LDTE Get logical drive table entry address ; ; Modifies: All ; LXI D,ACTIVE$SDEV ; Clear the table LXI B,ACTIVE$TLEN SHL 8 ; CALL FILL$BLOCK XCHG ; SHLD NEXT$POS ; Save pointer for later XRA A ; Clear count of active SCSI devices STA TOTAL$ACTIVE ; . G$GET$INFO: PUSH PSW ; Save current logical unit # CALL LB$GETLOGICAL ; Get logical drive table entry addr MOV A,M ; If zero, non-active device ORA A ; . JZ G$NEXT$D ; . SHLD NEXT$POS ; . LXI H,TOTAL$ACTIVE ; Bump count of SCSI devices INR M ; . G$NEXT$DEV: POP PSW ; Get logical back INR A ; Bump to next logical CPI 16 ; Do maximum of 16 logicals JNZ G$GET$INFO ; Not done -- go get more RET DS 16-($ MOD 16) ACTIVE$SDEV: DS 12*8 ; SCSI device info ACTIVE$FDEV: DS 4 ; Floppy CP/M letters ACTIVE$TLEN: EQU $-ACTIVE$SDEV ; Active table length TOTAL$ACTIVE: DB 0 ; # of active SCSI devices NEXT$POS: DW 0 ; Next SCSI buffer position SCSI$AEV ; . CPI 8 ; Only driver codes 0-7 supported JP G$NEXT$DEV ; . LXI D,OUTBUF ; Clear scratch area LXI B,0800H ; Fill with 00h for length of 8 bytes CALL FILL$BLOCK ; . XCHG ; Save pointer to drive entry LXI H,G$EXE$TBL ; JMP GO$TABLE ; Jump to proper routine G$EXE$TBL: DW G$NEXT$DEV ; Driver code = 00.html DW G$FLOPPY ; Driver code = 01.html DW G$EDISK ; Driver code = 02.html DW G$HARD ; Driver code = 03.html DW G$NEXT$DEV ; Driver code = 04.html DW G$NEXT$DEV ; Driver code = 05.html DW G$NEXTDDR: DB 0 ; SCSI Address (0-7) CTRL$TYPE: DB 0 ; Controller type (0-3) DRV$TYPE: DB 0 ; Drive type (0-3) DRV$LUN: DB 0 ; Drive logical unit # (0-7) PARTITION: DB 0 ; Drive partition (0-7) CPM$LETTER: DB 0 ; CP/M letter (0-15) UNIT: DB 0 ; Unit number (1-8) SPARE: DB 0 ; Spare ; end of GET$ACTIVE$DEV routine GET$BIOS$VERS: ; ; [E5.24] ; ; Get bios version -- Copies the current BIOS jump tables (starting ; at warm boot) to a local area for ease of utility access. If the ; BIOS is vers$DEV ; Driver code = 06.html DW G$NEXT$DEV ; Driver code = 07.html G$F$DRIVE: ; Convert floppy drive to ptr INX H ; Get byte MOV A,M ; Floppy drive byte ANI 03h ; Isolate drive bits LXI H,ACTIVE$FDEV ; Compute table addr MOV C,A ; . MVI B,0 ; . DAD B ; . RET ; and return G$FLOPPY: XCHG ; Get pointer to drive entry back CALL G$F$DRIVE ; Convert floppy drive POP PSW ; Get logical unit PUSH PSW ; . ADI 'A' ; Convert 0-15 to A-P ORA M ; Or in E-disk, if present MOV M,ion 2.0 or greater, the secondary jump table is copied ; also. ; ; Entry: ; none ; ; Exit: ; Z = bios 1.0 - 1.4 (floppy only bios) ; NZ = bios 2.0 or greater (floppy & fixed disk bios) ; ; Modifies: All registers ; LHLD 1 ; Get start of bios jump table LXI D,LB$BIOS$TBL ; Move bios to local storage LXI B,LB$LEN ; . (length of bios area) DW LDIR80 ; . (LDIR) MVI A,10 ; Test CP/M version CALL LB$GETNXT ; Get next jump table STA BIOS$VERSION ; Save bios version INX H ; See if  HL is 0FFFFh MOV A,H ; . ORA L ; . RZ ; If so, then old version DCX H ; Fix HL as it has the table addr LXI D,LB$XTBL ; Move extra table to local storage LXI B,LB$XLEN ; . (length of extra table) DW LDIR80 ; . (LDIR) MVI A,0FFH ; Set NZ to indicate bios ORA A ; ... version 2.1+ RET ; ... and return. BIOS$VERSION: DB 0 ; Current bios version GET$HL$PTR: ; ; [DC.20] ; ; Gets the pointer pointed to by HL and puts it in HL ; ; Entry: ; HL = pointer to put in HL  by HL ; ; Entry: ; A = character to check ; HL = pointer to list of "OK" characters ; ; Exit: ; A = original character if ok, 0ffh if not in list ; B = position of character in list ; ; Modifies: ; BC ; PUSH H ; Save original "OK" pointer MOV C,A ; Save chr to check against MVI B,0 ; Clear counter CPI ESC ; If chr is JZ I$CHR$OK ; . then automatically ok INR B ; . otherwise start counting at 1 I$CHK$NEXT: MOV A,M ; Get chr to check against ORA A ; End of table ; ; Exit: ; HL = pointer ; ; Modifies: none ; PUSH PSW ; Save A register MOV A,M ; Get low byte of pointer INX H ; . MOV H,M ; Get high byte of pointer MOV L,A ; Pointer is now together POP PSW ; Restore A register RET ; and return GET$STRLEN ; Searches the string pointed to by HL and returns the string length ; to the next carriage return. The length is returned in B. PUSH D ; Save start of string MVI B,0 ; Clear counter TRY$NEXT$CHR: LDAX D ; Get character C? JNZ I$NOT$EOT ; No, check chr DCR A ; Decrement to get 0ffh MOV B,A ; Stuff for later move JMP I$CHR$OK ; And exit I$NOT$EOT: CMP C ; Chrs match? JZ I$CHR$OK ; . Yes, return INX H ; . No, bump pointer INR B ; . . and bump counter JMP I$CHK$NEXT ; . . and check next chr I$CHR$OK: MOV A,B ; Set status based on ORA A ; . position counter MOV A,C ; Get user chr back I$DONE: POP H ; and original "OK" pointer RET ; and return JUSTIFY: ; ; [E1.30] ; ; This routPI CR ; Is it CR? JZ EOS$FOUND ; . CPI '$' ; Is it '$'? JZ EOS$FOUND ; . INR B ; No -- increment count and INX D ; . point to the next character JMP TRY$NEXT$CHR ; . EOS$FOUND: POP H ; CR or '$' found, recall orig ptr XCHG ; DE=orginial, HL=current RET ; and return GO$TABLE: ; ; [E2.05] ; ; Jump to a routine based on a table of pointers ; ; Entry: ; A = index into table ; HL = table base address ; ; Exit: ; Routine at (A*2)+HL is executed ; ; Modifies: ; B, Hine will send a data stream to the console, with each line ; justified based on the SCREEN$WIDTH value. The stream must terminate ; with the CP/M end of string character ($) and may contain imbedded ; CR,LF pairs to separate paragraphs. ; ; NOTE: To insure proper operation, the LF character should only follow ; a CR character or another LF character, as the CR character is used to ; flush the current line without justification. ; ; Two entry points are provided: ; JUSTIFY Justify output, flush L ; LXI B,2 ; Compute offset to table of routines CALL INDEX$TABLE ; . CALL GET$HL$PTR ; . PCHL ; Jump to proper routine INDEX$ACTIVE: ; ; [E2.10] ; ; Compute index into active table ; ; Entry: ; A = Table entry number (00h-0fh) ; ; Exit: ; HL = Address of table entry ; ; Modifies: ; PSW, DE, HL ; LXI B,8 ; Length of active device entry LXI H,ACTIVE$SDEV ; Table base address JMP INDEX$TABLE ; Return through INDEX$TABLE INDEX$TABLE: ; ; [E1.30] ; ; Computes offright ; JUSTIFY$RAGGED Justify output, ragged right ; ; As of E1.30, the flush right routine was not installed, so either ; entry point will provide the same results. ; ; Entry: ; DE = pointer to line(s) to output ; ; Exit: ; The data is sent to the screen. ; ; Modifies: All registers ; MVI A,80H ; Set flush right mode JMP E$JUSTIFY ; Jump to routine entry JUSTIFY$RAGGED: MVI A,00H ; Set ragged right mode E$JUSTIFY: STA J$MODE ; Save mode byte MVI A,'$' ; Mark start of buffer set to table given base address, entry length, and entry ; requested. ; ; Entry: ; A = entry # ; BC = table entry length ; HL = base address ; ; Exit: ; HL = address to entry ; ; Modifies: ; A, BC, HL ; ORA A ; Set up flags for first check I$TBL$ADD: RZ ; If A=0, we're done DAD B ; Otherwise add length to base, DCR A ; . decrement counter, JMP I$TBL$ADD ; . and check again. IS$IT$OK: ; ; [E1.28] ; ; Check the character in A against the list of "OK" chrs pointed ; toSTA OUTBUF-1 ; . XCHG ; DE is usually print source J$NEXT$LINE: XRA A ; Clear character counter STA BLANK$LEN ; . MOV B,A ; . LXI D,OUTBUF ; Set up buffer pointer J$CHECK$CHR: MOV A,M ; Get character STAX D ; Save in output buffer CPI CR ; CR? CZ J$FLUSH$LINE ; . Yes, flush output line, CZ J$LITERAL ; . . output CR & bump ptr JZ J$NEXT$LINE ; . . and do another. CPI LF ; LF? CZ J$LITERAL ; . Yes, output LF & bump ptr JZ J$CHECK$CHR ; . . and check next chr. CP I '$' ; End of string? CZ J$FLUSH$LINE ; . Yes, flush output line, RZ ; . . and return to caller CPI ' ' ; Blank? JNZ J$NOT$A$BLANK ; . No, don't save position SHLD BLANK$POS ; Save position for later XCHG ; and save corresponding position SHLD OUTBUF$BLANK ; . of the blank we just saved XCHG ; . in the output buffer MOV A,B ; . STA BLANK$LEN ; Save current length also J$NOT$A$BLANK: INR B ; Increment counter INX H ; . and pointer INX D ; . and output pointer s used for input, the following characters ; are translated to new values or new functions: ; ; Character New character or new function ; -------------- ------------------------------------ ; , (comma) key ; . (period) key ; @ (at-sign) repeat existing command line ; _ (underscore) Prompt and get character from user ; ; Entry: ; DE = pointer to prompt string ; HL = pointer to list of valid chars (terminated with 00H) ; ; Exit: ; A = char from the user ; B = position of this LDA SCREEN$WIDTH ; Compare counter against screen width SUB B ; . JP J$CHECK$CHR ; And continue checking if not past end ; ; Screen width exceeded, send this line to the screen. ; LHLD OUTBUF$BLANK ; Get pos of last blank in output buf MVI M,'$' ; and plug with eos ('$') LDA J$MODE ; Justify right edge only if the CM J$ADD$BLANKS ; . right-justify flag is non-zero CALL J$SEND$BUFFER ; Output the line to the screen CALL DO$CRLF ; and a CR / LF LHLD BLANK$POS ; Get pointer to where  character (0, 1, 2, ... n) ; ; Z = char was the escape key ; NZ = char was not the escape key ; ; Modifies: ; A, BC ; MVI A,01H ; Set CRLF after chr JMP E$PROMPT ; Jump to entry point PROMPT$NOLF: MVI A,00H ; Set no CRLF after chr E$PROMPT: STA PROMPT$MODE ; Save prompt mode flag RE$PROMPT: LDA CMD$LINE$CHRS ; Are there any characters left from ORA A ; . the command line? JZ P$DISP ; No -- display & get chr from bios PUSH H ; Yes, save oklist pointer LHLD CMD$LINE$PTR ; awe left off J$SKIP$BLANKS: INX H ; Bump pointer past blank(s) MOV A,M ; . CPI ' ' ; . JZ J$SKIP$BLANKS ; . JMP J$NEXT$LINE ; and check next segment J$FLUSH$LINE: ; Flush line when CR or EOS encountered PUSH PSW ; Save chr & zero flag MVI A,'$' ; Plug current position with EOS ($) STAX D ; . CALL J$SEND$BUFFER ; Send this line of data POP PSW ; Restore chr & zero flag RET ; and return J$LITERAL: ; Send the chr in A PUSH PSW ; Save zero flag CALL CON$CHR ; Consond get chr from command line DCR A ; . Reduce count of chrs by one STA CMD$LINE$CHRS ; . . MOV A,M ; . Get command line character INX H ; . Increment pointer SHLD CMD$LINE$PTR ; . . POP H ; . Restore oklist pointer CPI ' ' ; Ignore blanks JZ RE$PROMPT ; . CPI '@' ; Repeat existing cmd line? JNZ P$NOT$REPEAT ; . (check other chrs if not) PUSH H ; . Save oklist pointer LXI H,INBUF+2 ; . Set command line pointer back SHLD CMD$LINE$PTR ; . . to the beginning POP H le chr out through CP/M INX H ; Bump chr pointer POP PSW ; Restore zero flag RET J$ADD$BLANKS: RET ; At a later time, this routine will justify ; the right margin by inserting extra blanks ; in the output line. J$SEND$BUFFER: MOV A,B ; If line to output is of zero length, ORA A ; . then don't output the line. RZ ; . LXI D,OUTBUF ; Get address of output buffer CALL CON$MSG ; and call our print message routine RET ; return J$MODE DB 0 ; Current right justify; . Restore oklist pointer MVI A,07FH ; . Set the count of chars to 127 STA CMD$LINE$CHRS ; . . (the most it could be) JMP RE$PROMPT ; . and get the next character P$NOT$REPEAT: CPI ',' ; Change ',' to CR JNZ P$NOT$COMMA ; . MVI A,CR ; . P$NOT$COMMA: CPI '.' ; Change '.' to ESC JNZ P$NOT$DOT ; . MVI A,ESC ; . P$NOT$DOT: CPI '_' ; Underline means prompt & get chr JZ P$DISP ; . from bios anyway (user input) CALL TO$UPPER ; Convert the chr to upper case CALL IS$IT$OK ;  mode BLANK$POS DW 0 ; Last blank on this line OUTBUF$BLANK DW 0 ; Last blank in the output buffer BLANK$LEN DB 0 ; Length of line to the blank ; NOTE: OUTBUF is defined to be after the stack and before the heap. ; end of justify$output data area PROMPT: ; ; [E2.19] ; ; Prompt the user or the command line for input. ; ; Two entry points are provided: ; PROMPT standard entry, CRLF after chr from user ; PROMPT$NOLF special entry, No CRLF after chr from user ; ; When the command line iCheck the chr against the ok list RP ; If ok, return XRA A ; Otherwise, cancel the cmd line STA CMD$LINE$CHRS ; . buffer and fall through to p$disp P$DISP: PUSH H ; Save pointer to ok-chrs CALL JUSTIFY ; and call justify routine POP H ; . P$TRY$AGAIN: CALL CONIN$NE$XC ; Console input, no echo, except ^C CALL TO$UPPER ; Convert the chr to upper case CALL IS$IT$OK ; If the character is not "OK" JM P$TRY$AGAIN ; . get another CNZ CON$CHR ; . otherwise display it PUSH PSW ; Ch eck mode flag in case we need LDA PROMPT$MODE ; . to send a CR+LF after the ANI 01H ; . user's input CNZ DO$CRLF ; . POP PSW ; . RET ; and return PROMPT$MODE: DB 0 ; Prompt mode flag PROMPT$DECIMAL: ; ; [E6.11] ; ; Prompt the user for a decimal input of up to 10 digits. The Z flag ; indicates the termination character: Z = ESC key, results may not ; be valid; NZ = RETURN key, results valid. ; ; Entry: ; DE = pointer to prompt string ; ; Exit: ; BC,DE = Value entered by  POP H ; Get original HL back RET ; and return with result in BC & DE DEC$INPUT$MSG: DB '$' ; Decimal input message DEC$INPUT$OKC: DB '0123456789' ; Decimal input 'ok' chrs DB BS,CR,0 ; . BLOT: DB BS,' ',BS,'$' ; Blot out digit SCRATCH: DB ' ',0 ; Max 10 digits PROMPT$D$MODE: DB 0 ; CRLF flag RET$TO$CONT: ; ; [E2.19] ; ; Prompts and waits for the RETURN key to be pressed. ; ; Entry: ; none ; ; Exit: ; Display message and wait for a RETURN key. ; ; Modifies: ; the user, 0-0FFFFFFFFH ; ; Z = ESC key pressed, ignore results ; NZ = RETURN key pressed, results valid ; ; Modifies: ; A, PSW ; MVI A,01H ; Set CRLF after entry DB JR,E$PR$DEC-$-1 ; Jump to entry point PROMPT$DEC$NOLF: MVI A,00H ; Set no CRLF after entry E$PR$DEC: STA PROMPT$D$MODE ; Save prompt mode flag PUSH H ; Save original HL reigster XRA A ; Clear the current digit pointer MOV C,A ; Setup count in C register LXI H,SCRATCH ; Initialize the scratch pointer NEXT$DIGIT: all ; LXI D,RTC$MSG ; Press RETURN to continue ... CALL CENTER$OUTPUT ; . LXI D,NO$MSG ; . LXI H,RTC$OKC ; . CALL PROMPT ; . [ CR ] RET RTC$MSG: DB 'Press the RETURN key to continue ...' NO$MSG: DB '$' RTC$OKC: DB CR,0 ; end of RET$TO$CONT routine SCSI$TO$BIN: ; ; [DC.27] ; ; Converts SCSI address to binary 0-7 ; ; Entry: ; A = SCSI address to convert ; ; Exit: ; A = converted address (0-7) [0FFH = error] ; ; Modifies: B ; MVI B,0FFH ; Set up B register for pPUSH B ; Save digit counter PUSH H ; . and string pointer LXI H,DEC$INPUT$OKC ; Get current 'ok' chrs CALL PROMPT$NOLF ; Prompt for digit POP H ; Get string pointer back POP B ; . along with digit counter DB JRZ,ESCRTN-$-1 ; Return if ESC key hit CPI CR ; Check for return JZ CONVERT$STRING ; Convert string to decimal, if so CPI BS ; Check for backspace JZ BACKUP$DIGIT ; Back up 1 digit, if we can MOV B,A ; Save character for a moment MOV A,C ; Check digit count CPI 10 ossible error ORA A ; If A=0, error JZ SCSI$CONVERTED ; . SCSI$NEXT$BIT: INR B ; Increment count RRC ; Shift address right 1 bit JNC SCSI$NEXT$BIT ; Bit shifted to cary means we're done SCSI$CONVERTED: MOV A,B ; Move converted addr to A RET SORT$ACTIVE: ; ; [E2.21] ; ; Sort active device table entries ; ; Entry: ; none ; ; Exit: ; Active device table is sorted. ; ; Modifies: ; All ; LDA TOTAL$ACTIVE ; Get # of items to sort CPI 02 ; Less than two items? RM ; ; . JC ADD$DIGIT ; 10 digits or less, add to string LXI D,BLOT ; More than 10 digits, blot this one CALL CON$MSG ; . JMP DE$FOR$NEXT ; . ADD$DIGIT: INR C ; Otherwise bump digit count MOV M,B ; Save digit INX H ; Bump digit pointer JMP DE$FOR$NEXT ; Setup DE for next prompt BACKUP$DIGIT: MVI A,' ' ; Bump forward to clear digit CALL CON$CHR ; . XRA A ; Are we at the beginning? CMP C ; . JZ DE$FOR$NEXT ; Yes, don't backup MVI A,BS ; Backup to correct position CALL CO. yes -- no sort necessary S$NEXT$BLOCK: ; . STA SORT$MAX ; Save # to sort LXI B,0001H ; Setup initial compare pointers S$NEXT$ELEM: PUSH B ; Save compare pointers MOV A,C ; Convert pointers to address CALL INDEX$ACTIVE ; . in DE and HL PUSH H ; . MOV A,B ; . CALL INDEX$ACTIVE ; . POP D ; . MVI B,8 ; Compare length = 8 chrs CALL STR$COMP ; Compare string in [DE] to [HL] MVI B,8 ; Swap length = 8 chrs CM STR$SWAP ; Swap if first is less than second POP B ; Get pointN$CHR ; . DCX H ; Backup pointer 1 chr DCR C ; Push count 1 back, also DE$FOR$NEXT: LXI D,DEC$INPUT$MSG ; Setup for next prompt JMP NEXT$DIGIT ; . CONVERT$STRING: MVI M,0 ; Mark end of string LXI H,SCRATCH ; Get beginning of string CALL STR$TO$BCDE ; Convert string to BC & DE regs XRA A ; Insure NZ flag DCR A ; . PUSH PSW ; Check mode flag to see if a LDA PROMPT$D$MODE ; . CR+LF should be sent after ANI 01H ; . the user's input CNZ DO$CRLF ; . POP PSW ; . ESCRTN:ers back INR B ; Point to next item INR C ; LDA SORT$MAX ; Compare with max CMP C ; . JNZ S$NEXT$ELEM ; Not done with this pass ... DCR A ; Make sort limit one smaller CPI 03H ; Done if only 2 elements JP S$NEXT$BLOCK ; Not done if > 2 elements RET SORTMAX: DW 0 ; Sort max range (0-255 elements) STR$COMP: ; ; [E1.18] ; ; Compare two strings ; ; Entry: ; HL = source ; DE = destination ; B = count ; Exit: ; Z = two strings equal ; M = source < dest ; P = sou rce >= dest ; ; Modifies: all ; PUSH D PUSH H NEXT$COMPARE: MOV C,M INX H LDAX D INX D CMP C JNZ NOT$EQUAL DCR B JNZ NEXT$COMPARE NOT$EQUAL: POP H POP D RET STR$SWAP: ; Swap two strings ; ; [E1.18] ; ; Entry: ; HL = source ; DE = destination ; B = count ; Exit: ; data moved ; ; Modifies: all ; MOV C,M LDAX D MOV M,A MOV A,C STAX D INX H INX D DCR B JNZ STR$SWAP RET STR$TO$BCDE: ; ; [E7.09] ; ; Converts the string pointed to by milliseconds in A ; ; Exit: ; time waited ; ; Modifies: A ; PUSH PSW MVI A,221 WAIT$2: DCR A JNZ WAIT$2 POP PSW DCR A JNZ WAIT RET * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Data area . . . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ; Replicated BIOS to make direct calls easier . . . LB$BIOS$TBL: LB$WBOOT DS 3 ; Warm boot LB$CONST DS 3 ; Console status LB$CONIN DS 3 ; Console input LB$CONOUT DS 3 ; Console outpHL to a 32-bit number in the ; DE and A registers. The conversion will continue until the first ; non-numeric chr found. ; ; NOTE: a test for register overflow is not made. If HL points ; to a string whose numerical value is greater than 4,294,967,295 ; (0FFFFFFFFH), inaccurate results will occur. ; ; Entry: ; HL = ptr to string ; ; Exit: ; BC,DE = value of string (BC = high word, DE = low word) ; HL = next character to process ; ; Modifies: ; A, DE, HL ; ; DW LXIX,0 ; Clear totut LB$LISTOUT DS 3 ; List output LB$PUNCH DS 3 ; Punch output LB$READER DS 3 ; Reader input LB$HOMDSK DS 3 ; Home disk (move to track 00) LB$SELDSK DS 3 ; Select disk drive LB$SETTRK DS 3 ; Select track number LB$SETSEC DS 3 ; Select sector number LB$SETDMA DS 3 ; Set DMA address LB$DSKREAD DS 3 ; Disk read LB$DSKWRITE DS 3 ; Disk write LB$LISTST DS 3 ; List status LB$SECTRN DS 3 ; Sector translate routine ; AMPRO-specific BIOS calls LB$GETNXT DS 3 ; Get bios ver & next tbl address LB$GETEDSKals LXI D,0 ; . STN$NEXT$CHR: MOV B,A ; Save current high byte MOV A,M ; Get next chr CPI '0' ; Less than '0'? DB JRC,NO$DIG-$-1 ; Yes, finished CPI '9'+1 ; Greater than '9' DB JRNC,NO$DIG-$-1 ; Yes, finished SUI '0' ; Convert to 0-9 PUSH H ; Save pointer to input string PUSH PSW ; Save converted digit PUSH D ; Setup regs for X10 POP B ; . DW PUSHIX ; . BC = p3, p2 Previous 32-bit POP D ; . DE = p1, p0 number LXI H,0 ; . HL = t3, t2 Current 32-bit DW L DS 3 ; Get pointer to E-disk storage LB$IOINIT DS 3 ; Set new I/O parameters LB$SCSIDRV DS 3 ; SCSI direct driver LB$LEN EQU $-LB$WBOOT ; Length of bios table LB$XTBL: ; 'Extra' table definitions ... LB$SWAP$DRV DS 3 ; Swap two logical drives LB$WINDRV DS 3 ; Set/get win drive parameters LB$PHYTAB DS 3 ; Set/get phytab access LB$GETLOGICAL DS 3 ; Get logical device table entry LB$RESERVED DS 3 ; Reserved entry LB$XLEN EQU $-LB$XTBL ; Length of extra table LB$VERS: DS 1 ; Bios version numbeXIX,0 ; . IX = t1, t0 total MVI A,10 ; Set up count STN$NEXT$X10: ; Add next digit DB 0DDH,019H ; . (ADD IX,DE) DB 0EDH,04AH ; . (ADC HL,BC) DCR A ; . JNZ STN$NEXT$X10 ; . POP PSW ; Get new digit back MOV E,A ; Setup to add new digit MVI D,0 ; . LXI B,0 ; . DB 0DDH,019H ; Add new digit DB 0EDH,04AH ; . XCHG ; Move total in HL back to DE POP H ; Get input string ptr back INX H ; Bump to next chr JMP STN$NEXT$CHR ; Go back for another digit NO$DIG: PUSH D ;r DS 64 ; 32-level stack STACK: DS 2 ; Old stack pointer INBUF: DS 128 ; Command line input buffer IBUFL: EQU 128 ; Input buffer length OBPLUG: DS 1 ; Start of outbuf ('$') OUTBUF: DS PWIDTH ; Output buffer OBUFL: EQU PWIDTH ; Output buffer length HEAPPTR: DS 2 ; Pointer to next area HEAPLEN: DS 2 ; Length of next area HEAP: EQU $ ; Start of heap END START  Move high word to BC POP B ; . DW PUSHIX ; Move low word to DE POP D ; . RET ; Return with results in BC and DE. TO$UPPER: ; ; [E1.08] ; ; Convert the character in A to upper case. ; ; Entry: ; A = character to convert ; ; Exit: ; A = upper case character (if alpha) ; ; Modifies: ; A ; CPI 'z'+1 ; Convert to upper case JP UPPER$ALREADY ; . CPI 'a' ; . JM UPPER$ALREADY ; . ANI 5FH ; . UPPER$ALREADY: RET ; and return WAIT: ; Wait A ms ; ; Entry: ;  : Width DB 88 ; # of lines DB 82 ; # of text lines DB 1 ; FF flag (1=can form feed) DB 'SH ' ; . Shell variable filename DB 'VAR' ; . Shell variable filetype DB ' ' ; . File 1 DB ' ' ; DB ' ' ; . File 2 DB ' ' ; DB ' ' ; . File 3 DB ' ' ; DB ' ' ; . File 4 DB ' ' ; DB 0 ; Public drive area (ZRDOS +) DB 0 ; Public user area (ZRDOS +) ;ENV 128 bytes long ENV2: ; Terminal capabilities data DB 'ADM-3A ' ; . Name; 31 Mar 86.. fsw ; * * * * * * * * * * * * * * * * * * * * * * * ; * * ; * SCSI/Floppy/Hard Disk Boot ROM * ; * * ; * Copyright (C) 1983, 1984, 1985 * ; * AMPRO Computers, Inc. * ; * All rights reserved. * ; * * ; * * * * * * * * * * * * * Include ZCPR3 definitions? ENV: JMP 0 ; Leading JMP ENV1: ; ZCPR3 enviornment descriptor ... DB 'Z3ENV' ; . Environment ID DB 1 ; . Class 1 environment (external) DW EXPATH ; . External path (PATH) DB EXPATHS ; DW RCP ; . Resident command package (RCP) DB RCPS ; DW IOP ; . Input/output package (IOP) DB IOPS ; DW FCP ; . Flow command package (FCP) DB FCPS ; DW Z3NDIR ; . Named directories (NDR) DB Z3NDIRS ; DW Z3CL ; . Command line (CL) DB Z3CLS ; DW * * * * * * * * * * ; ; ; Revision history: ; ; Ver Date Who Description ; --- -------- --- ----------------------------------------- ; 1.2 03/27/86 fsw Changed scsi select routine to current bios ; 3.6 routines. Added SCSI retries. Fixed ; arbitration to provide exit if scsi reset ; occures during arbitration time. ; ; 1.1 10/18/85 fsw Corrected problem booting from 96 tpi disk. ; Added 5 tries for floppy disk reads and ; Z3ENV ; . Environment (ENV) DB Z3ENVS ; DW SHSTK ; . Shell stack (SH) DB SHSTKS ; DB SHSIZE ; DW Z3MSG ; . Message buffer (MSG) DW EXTFCB ; . External FCB (FCB) DW EXTSTK ; . External stack (STK) DB 0 ; . Quiet flag (1=quiet, 0=not quiet) DW Z3WHL ; . Wheel byte (WHL) DB 4 ; . Processor speed (Mhz) DB 'P'-'@' ; . Max disk letter DB 31 ; . Max user number DB 1 ; . 1=ok to accept DU:, 0=not ok DB 0 ; . CRT selection DB 0 ; . Printer selection DB 80 ; . CRT 0 jmp for drive prameters initialization. ; ; 1.0 9/17/85 fsw Initial release. ; ; ; ; DESCRIPTION ; ; This ROM will boot from floppy, hard disk, or SCSI disk server, ; and is based on the orginal AMPRO boot ROM and the SCSI routines ; in Version 3 BIOS. Arbration is used in the SCSI routines. ; ; No alteration to boot sector is required to boot from floppy. ; Booting from hard disk requires the use of the HGEN utility, to ; install a sector on the hard disk: Width DB 24 ; # of lines DB 22 ; # of text lines DB 132 ; . CRT 1: Width DB 24 ; # of lines DB 22 ; # of text lines DB 80 ; . PRT 0: Width DB 66 ; # of lines DB 58 ; # of text lines DB 1 ; FF flag (1=can form feed) DB 96 ; . PRT 1: Width DB 66 ; # of lines DB 58 ; # of text lines DB 1 ; FF flag (1=can form feed) DB 132 ; . PRT 2: Width DB 66 ; # of lines DB 58 ; # of text lines DB 1 ; FF flag (1=can form feed) DB 132 ; . PRT 3 containing hard disk system ; configuration info, and to add code to the boot loader for hard ; disk configuration initialization. ; ; THEORY OF OPERATION ; ; The ROM on reset begins execution at 0000H. It first relocates ; itself to 8000H then continues executation above 8000H. ; The ROM then loads in a "boot loader" sector from floppy, hard disk, ; or other SCSI device. ; ; The first attempt is to load a boot loader from side 0, track 0 ; sector one of floppy drive "A" (drive sel ect "1"), if a floppy ; is present in that drive. If the first byte of that sector is not ; an LXI H or MVI A op code, then an attempt to load a boot loader ; from an SCSI device is made. SCSI ID 0 is used as the SCSI boot ; device if the board's ID is jumpered as 7. Otherwise, SCSI ID 7 ; is used for the attempt to boot from SCSI. ; ; If the appropriate SCSI device is not able to ; provide a legal boot loader (with LXI H or MVI A op code as first ; byte), then the process begins again, loopi ; YES EQU 1 NO EQU 0 MSEC EQU 167 CR EQU 0DH LF EQU 0AH CONT EQU 0 ; system control port SID1 EQU 10H ; select side one of disk SDEN EQU 20H ; select single density ROMOFF EQU 40H ; turn rom off BOOT EQU 8000H ; origin of this program RAM EQU 9000H ; beginning of scratch ram STACK EQU RAM ; stack pointer CTCA EQU 40H ; counter/timer CTCA0 EQU CTCA CTCA1 EQU CTCA+10H CTCA2 EQU CTCA+20H CTCA3 EQU CTCA+30H SIO EQU 80H ; serial io SIOAD EQU SIO SIOBD EQU SIO+8 SIOAC EQU Sng until a legal "boot ; loader" is obtained. ; ; The boot loader is stored at 9000H, followed by a jump to that ; address. The contents of the boot loader determine what happens ; next. Normally the ROM is switched off first thing. ; ; This accommodates: ; ; o Hard disk drive spinup ; o User choice of boot device ; o Networking ; o Odd-ball applications! ; ; ; MISC NOTES ; ; Supports floppy drive step rate of 6 msec only for compatability ; with the 177IO+4 SIOBC EQU SIO+12 CMND EQU 0C0H WTRK EQU CMND+1 WSEC EQU CMND+2 WDAT EQU CMND+3 STAT EQU CMND+4 RTRK EQU CMND+5 RSEC EQU CMND+6 RDAT EQU CMND+7 REST EQU 08H STEPI EQU 58H RDSEC EQU 88H RDID EQU 0C8H FI EQU 0D0H ERMSK EQU 18H ID EQU 29H ; Little board SCSI id ; ; NCR 5380 controller equates ; NCRBASE EQU 20H ; Base address of NCR 5380 NCRCSD EQU NCRBASE+0 ; (R) Current SCSI data register NCRODR EQU NCRBASE+0 ; (W) Output data register NCRICR EQU NCRBASE+1 ; (RW) Init0 or 1772 fdc. ; ; Supports hard disk with 512 byte sectors only. Each call to read ; track reads one AMPRO track of 16 sectors when booting from hard ; disk. ; ; Requires the use of SCSI self-initializing controllers, such as ; Adaptec ACB-4000, Shugart 1610-4, Xebec OWL drive/controller, etc. ; ; FEATURES ; ; Test for presence of 177x floppy disk controller and ; for 5380 SCSI controller. ; ; Test for presence of boot sector on the disk it has read. ; ;iator command register NCRMR EQU NCRBASE+2 ; (RW) Mode register NCRTCR EQU NCRBASE+3 ; (RW) Target command register NCRCSBS EQU NCRBASE+4 ; (R) Current SCSI bus status NCRSER EQU NCRBASE+4 ; (W) Select enable register NCRBSR EQU NCRBASE+5 ; (R) Bus & status register NCRSDS EQU NCRBASE+5 ; (W) Start DMA send NCRIDR EQU NCRBASE+6 ; (R) Input data register NCRSDTR EQU NCRBASE+6 ; (W) Start DMA target receive NCRRPI EQU NCRBASE+7 ; (R) Reset parity/interrupt NCRSDIR EQU NCRBASE+7 ; (W) Start D Test for properly formated floppy disk in drive. ; ; If unable to boot from floppy disk will try SCSI, if ; unable to boot from SCSI, starts over. This sequence ; will repeat untill sucessful. ; ; ; SCSI ID CONVENTIONS ; ; SCSI Master mode: Set board SCSI ID to 7 (refer to board manual). ; When the board's SCSI ID is 7, the boot ROM issues an SCSI ; bus reset, and then uses the device at SCSI ID 0 as the ; SCSI boot device, if floppy boot attempt fails. MA initiator receive NCRDACK EQU NCRBASE+8 ; (RW) DACK pseudo-DMA register ; Current SCSI bus status (NCRCSBS) NCRRST EQU 10000000B ; Reset NCRBSY EQU 01000000B ; Busy NCRREQ EQU 00100000B ; Request NCRMSG EQU 00010000B ; Message NCRCD EQU 00001000B ; Control/Data NCRIO EQU 00000100B ; Input/Output NCRSEL EQU 00000010B ; Select NCRDBP EQU 00000001B ; Data bus parity ;............................................................... ORG BOOT JMP START -BOOT ; power on or re-boot ; ; SCSI Slave mode: Set board SCSI ID to anything other than 7. ; When the board's SCSI ID is not 7, the boot ROM does not ; issue an SCSI bus reset. In this case the device at SCSI ; ID 7 is used as the SCSI boot device if the floppy boot ; attempt fails. This might be a disk server rather than ; a disk controller. ; ; In all cases if will boot from floppy in drive "A" if a legal boot ; sector is present (as defined above) ;******************************************************************** JMP SETUP ; setup disk drive JMP STEPIN ; step in one track JMP SIDEONE ; select side one JMP FDCLR ; clear the fdc JMP SETDMA ; set new dmaadr (hl) JMP READTRK ; read current track at dmaadr JMP READSEC ; read current sector at dmaadr JMP READID ; read next sector address JMP SETPRAM ; pass the hard disk prameters to the ; scsi controller, for dumb controllers. DMAADR: DS 2 IDSV: DS 6 SELBYT: DS 1 TRACK: DS 1 SECTOR: DS 1 STATUS: DS 1 TWOSID: DS 1 SBIAS: DS 1 ST ART: DI XRA A OUT CONT ; clear system control register LXI H,0 LXI D,BOOT RELOC: MOV A,M STAX D INX H INX D MOV A,H CPI 16 ; relocate 16 pages JNZ RELOC -BOOT ; this code runs at address 0 JMP INITSYS ; this code runs relative to boot ; copyright declaration DB 'SCSI Boot Rom Version 1.2 - (C) 1983,1984,1985,1986 ' DB '- AMPRO Computers, Inc. - All rights reserved. ' INITSYS:LXI SP,STACK ; set local stack LXI H,CTCTBL CTCINT: MOV A,M INR A JZ SIOINT DCR A OUT try$hd$dvr ; do hard disk, no fdc present ; ; floppy disk controller present try to boot from floppy ; MVI A,5 STA TRIES ; set up try count CBOOT2: CALL SETUP CALL READID CALL ERROR JNZ CBOOT2 ; try to read id 5 times MVI A,5 STA TRIES ; reset floppy try counter LDA IDSV+2 ; get sector # CPI 17 MVI A,0 JC CB0 INR A CB0: STA TWOSID ORA A RAL RAL RAL RAL STA SBIAS ; 0 if single sided, 16 if 2 sided LXI H,RAM SHLD DMAADR INR A ; sector 1 ( or 17 ) OUT WSE CTCA0 OUT CTCA1 INX H JMP CTCINT CTCTBL: DB 47H,13,255 ;9600 ;CTCTBL: DB 3,0,255 ;19200 SIOINT: LXI H,SIOTBL SIOLP: MOV A,M INR A JZ CBOOT DCR A OUT SIOAC OUT SIOBC INX H JMP SIOLP SIOTBL:DB 4,46H,5,0EAH,3,0C1H,255 ;9600 ;SIOTBL: DB 4,84H,5,06AH,3,0C1H,255 ;19200 ; cboot is the start of the read disk routines. ; ; check little board id, if id = 7 do SCSI reset, if id not=7 ; no reset is issued. ; ; a 1 second time delay is invoked prior to SCSI reset to allow C CALL READSEC CALL ERROR LDA TWOSID JNZ CB0 ; error occured try to read again lda ram ; see if boot record there cpi 21h ; starts with lxi h,xxxx jz ram cpi 3eh ; other choice is mvi a,xx jz ram jmp try$hd$dvr ; see if can do hard disk SETUP: MVI A,'A'-40H OUT CONT ; select a:, side 0 STA SELBYT ; save it CALL FDCLR MVI A,REST ; restore CALL OUTCMD ; to fdc JMP FDWAIT ; Wait for command to complete and ret ; ; Read current track at dmaadr ; READTRK: lda ids; various SCSI devices do their powerup initialization before ; the SCSI reset is issued. ; CBOOT: IN ID ; get little board SCSI id ani 07h ; mask off inr a ; make 1 thru 8 mov b,a ; set 'b' for count xra a stc ; set carry setid: ral dcr b jnz setid sta myid cpi 80h ; scsi id 7 mvi a,10000000b ; target id=7 jnz resetnot MVI A,4 ; 4 x .25 sec CALL TIMER mvi a,80h ; scsi reset out ncricr ; set reset line high holdit: dcr a jnz holdit ; 50 usec min out nv+3 ; sector size cpi 3 lxi h,skwtbl1 ; 96 tpi disk jz rtloop LXI H,SKWTBL ; sector skew table RTLOOP: mvi a,5 ; sta tries ; set floppy try counter MOV C,M INX H INR C RZ ; finished DCR C LDA SBIAS ADD C STA SECTOR OUT WSEC RSAGN: PUSH H ; save skew table pointer CALL READSEC ; read the sector CALL ERROR ; check for errors JNZ RSAGN1 ; try to read the sector again LHLD DMAADR ; get current dmaadr address SHLD DMA ; save current dma addr POP H ; restore scricr ; clear reset line in ncrrpi ; and the interrupt line mvi a,00000001b ; target id=0 ; ; has target id in 'a'. if little board id=7 boot from device 0. ; if little board id not=7, boot from device 7. ; resetnot: sta target ; device to boot from xra a out ncricr ; clear the 5380 registers out ncrmr out ncrtcr out ncrser in ncrmr ; read the mode register ; mode register should equal 0 if ; 5380 is present. sta scsi ; set scsi to zero or non-zero to ; show kew table pointer JMP RTLOOP ; next sector ; ; if error on read sector, restores old dma address and attempts ; to read the same sector again. ; RSAGN1: LHLD DMA ; get old dmaadr dma address SHLD DMAADR ; restore dma address POP H ; restore stack JMP RSAGN ; SKWTBL: DB 1,2,3,4,5,6,7,8,9,10,255 skwtbl1:db 1,2,3,4,5,255 ; ; read id twice, once to see if disk in drive, the second for ; the id value. ; READID: IN STAT IN RDAT MVI A,RDID CALL OUTCMD call fdwait ; See if will timpresence of ncr 5380 ; ; everything on the SCSI buss should have completed initialization and ; SCSI reset may have been issued. ; ; test for presence of 177x floppy disk controller. ; MVI A,0AAH ; test for presence of fdc OUT WSEC ; write pattern to sector reg MOV B,A ; save in 'b' ANI 0FH ; set up loop value CLOOP: DCR A ; give the 177x time for the sec reg JNZ CLOOP ; to set up. IN RSEC ; read the sector reg CMP B ; see if same as written to sec reg JZ CBOOT2 ; JMPe out CPI 255 ; no floppy sta status ; if timeout error, set error status rz ; if timeout just return IN STAT ; clear fdc reg IN RDAT LXI H,IDSV MVI A,RDID CALL OUTCMD CALL RD MOV A,B STA STATUS RET READSEC:IN STAT ; clear status IN RDAT ; clear any trash LHLD DMAADR CALL FDCLR MVI A,RDSEC ; read sector command CALL OUTCMD ; to fdc CALL RD ; read loop MOV A,B STA STATUS SHLD DMAADR RET RD: IN STAT MOV B,A ; save status RAR RNC ; return whe n fdc not busy RAR JNC RD ; wait for drq IN RDAT MOV M,A INX H JMP RD STEPIN: LDA TWOSID ORA A JZ STPI CALL SIDEONE LXI H,TRACK DCR M LXI H,MSEC ; wait one millisecond JMP WT ; wt returns to caller STPI: MVI A,STEPI ; step in command CALL OUTCMD STLP: IN STAT RAR JC STLP RET FDCLR: MVI A,FI OUT CMND MVI A,0 CL: DCR A JNZ CL IN STAT IN RDAT RET OUTCMD: OUT CMND MVI A,19 OC0: DCR A JNZ OC0 ; wait 66.5 usec for fdc to set-up RET SETDMA: SORA H ; 1.0 JNZ TIMER1 ; 2.5 ;total = 6 usec X 41668 LXI H,TIMEOUT DCR M RZ JMP TIMER0 ; ;--------------------------------------------------------------- ; Send bytes to console until zero encountered ; use to insert messages when debuging ; format is ; call puts ; db 'your message',0; must terminate with '0' ; ;PUTS: pop h ; 'hl' has address of msg ; mov c,m ; get char ; inx h ;CONOUT: ; MVI A,01H ; Check "all sent" bit in register 1 ; OUT 84H ; . ; IN 84H ; . ; ANI 0HLD DMAADR SHLD DMA RET SIDEONE:LDA SELBYT ORI SID1 OUT CONT RET ; ; floppy error checking enters here. Returns with zero flag NZ if ; error, or Z if no error. Floppy routines must set STATUS for error ; conditions. ; ERROR: LDA STATUS ANI ERMSK RZ PUSH H LXI H,TRIES ; point to tries DCR M ; tries -1 POP H RNZ ; try again LDA SCSI ; get 5380 present byte ANA A JZ HD$DVR ; Try hard disk, have timed out on floppy ; disk at least 5 times ; ; floppy error fa1H ; "ALL SENT" is bit 0 ; JZ CONOUT ;TRANSMIT BUFFER NOT READY ; MOV A,C ;CHARACTER TO REGISTER A ; OUT 80H ; mov a,m ; ana a ; returns with char in 'a', null = end ; jnz puts+1 ; pchl ; hl has return address ;----------------------------------------------------------------- ; ; if no boot sector is present on the floppy disk, floppy timeout ; has occured, floppy read error, or no floppy disk controller is ; present, entry to the hard disk (scsi) driver is here. ; ; starts with 5 seclls thru to here also hard disk errors enter here to ; start the hole proceedure over and over again. ; ERROR1: XRA A ; make sure prom is turned on OUT CONT JMP 0 ; jmp to prom ; WAIT: LXI H,100 * MSEC WT: DCX H MOV A,H ORA L JNZ WT RET ; ; This routine will wait for a for the FDC to go not busy, showing ; completion of a command. After 1 seconds time out, a FORCE INTERRUPT ; command will be issued to the FDC. Routines will come through here ; at least 5 times before final errond timer to prevent hammering the scsi buss ; with possible scsi resets. ; TRY$HD$DVR: MVI A,20 ; 20 x .25 seconds = 5 sec CALL TIMER ; ; Hard disk driver ; HD$DVR: LXI SP,STACK ; reset stack lxi h,hdtrk shld boot+19 ; patch readtrk xra a ; zero 'a' lxi h,ram ; address to read sector mov m,a ; clear anything at this address sta hdsect ; zero hard disk sector shld dmaadr call scsi$rd ; read boot sector lda ram cpi 21h ; should be lxi h,xxxx jz ram cpi 3eh ; oor occures. Total time out 5 seconds. ; FDWAIT: LXI H,TIMEOUT ; Point to timeout location MVI M,3 ; Set major loops for timeout ; 3 = about 1 sec. DLOOP: IN STAT ; Get FDC status RAR ; test busy bit RNC ; . zero status if busy non-active RAR ; see if byte to be read JC FREAD ; read the byte DCX H ; See if enough minor loops MOV A,H ; . (Approx 34,000 times) ORA L ; . JNZ DLOOP ; Not done with minor loop LXI H,TIMEOUT ; Decrement major loop counter DCR M ; . ther choice is mvi a,xx jz ram jmp error1 ; no boot sector go try floppy again ; ; Read hard disk system to memory. Do 16 sectors at a time. One AMPRO ; hd track. The boot loader will call this routine twice. ; First pass read sectors 0 thru 15, second pass read 16 thru 31 for ; 32 sectors total. ; HDTRK: call scsi$rd cpi 0ffh ; see if return has timeout error jz error1 ; start all over lhld dmaadr lxi d,512 ; update dma address dad d shld dmaadr lda hdsect ; see if two tracks  (timeout loop count) JNZ DLOOP ; . CALL FDCLR ; force fdc clear XRA A ; Set A to 0FFH and status to NZ OUT CONT ; deselect so floppy select light DCR A ; will go out RET ; ; FREAD: IN RDAT ; get the data byte from the fdc JMP DLOOP ; just loop till command over ; ; timer entered with 'a' equal to number of major loops wanted ; each major loop = aprox .25 sec ; TIMER: LXI H,TIMEOUT MOV M,A ; save major loop value TIMER0: LXI H,41668 TIMER1: DCX H ; 1.5 MOV A,L ; 1.0 (32 sectors) inr a ; next sector to read cpi 32 ; have already read 512x32 bytes rz ; cpi 16 ; see if one "track" has been read sta hdsect ; update sector new sector, the boot ; loader reads two tracks. rz ; return to boot jmp hdtrk ; loop till through ; ; ; SCSI return sense data command (Cmd 03) ; SCSI$STAT$CMD: DB 3 ; 00 - REQUEST SENSE COMMAND DB 0 ; 01 - LOGICAL UNIT DB 0 ; 02 - RESERVED DB 0 ; 03 - RESERVED DB 4 ; 04 - NUMBER OF BYTES   DB 0 ; 05 - RESERVED ; ; SCSI read/write command (Cmd 08/0A) ; SCSI$RD$CMD: EQU 08H ; 08 IS READ DATA SCSI$RW$CMD: DB SCSI$RD$CMD ; 00 - 08=Read, 0A=Write HIGH$ADDR: DB 0 ; 01 - High address MED$ADDR: DB 0 ; 02 - Middle address LOW$ADDR: DB 0 ; 03 - Low address DB 1 ; 04 - Number of sectors STEP$RATE: DB 0 ; 05 - Step rate (Xebec) ; ; Init scsi controller prameters ; ; When called HL = address of scsi command ; DE = address of prameter table i reset has occured jz in$progress jmp clear$arbit arbitrate$won: nop ; Arbritration delay nop in ncricr ; Check for lost arbitration ani 20h ; . jnz clear$arbit ; We lost -- start over lda my$id mov b,a in ncrcsd ; See if we're the highest priority sub b ; . remove my addr sub b ; . compare my addr to bus data jm i$win ; We win if result < 0 jmp clear$arbit ; . otherwise we lose -- start over i$win: in ncricr ; Check again for lost arbitration ani 20h ;; SETPRAM: SHLD CMDPTR ; set address of scsi command XCHG ; 'de' to 'hl' SHLD DATPTR ; save address of prameters to pass JMP SCSI$CMD ; ; Read from the hard disk ; SCSI$RD: LXI H,SCSI$RW$CMD ; Get command string SHLD CMDPTR ; Save the command pointer CALL BLD$SCSI$SCTR ; Build SCSI sector address ; ; Exits with status in A. 0FFH = timeout error ; SCSI$CMD: LXI H,TRIES ; set up retry count MVI M,2 SCSI$CMD$RETRY: CALL SELECT ; Perform the SCSI operation LDA STAT . (just in case) jnz clear$arbit ; We lost -- start over mvi a,08h ; set assert BSY bit in icr out ncricr ; . in ncricr ori 04h ; OR in SEL to ICR out ncricr nop ; wait one bus clear delay lda my$id ; Select target: get our ID, mov b,a ; . lda target ; . or in target ID ora b ; . out ncrodr ; . and send to NCR chip mvi a,0dh ; Assert data bus out ncricr ; . (along with SEL & BSY) in ncrmr ; Reset arbitration bit ani 0feh ; . out ncrmr ; . mUS ; Get the return status STA ERFLAG ; save error CPI 0FFH ; Timeout? JZ SCSI$DONE ; Yes, go save timeout status ANI 2 ; Check for SCSI error status JZ SCSI$DONE ; No error -- finish up LHLD CMDPTR SHLD SAVE$CMDPTR ; save old command pointer LXI H,SCSI$STAT$CMD ; get sense SHLD CMDPTR ; set command pointer LHLD DMAADR SHLD SAVE$DMA ; save the current LXI H,MESSAGE ; message area SHLD DMAADR CALL SELECT ; do operation LXI H,TRIES DCR M ; tries -1 LHLD SAvi a,05h ; Release BSY, keep SEL out ncricr ; . and assert data bus nop lxi b,6000h ; 250 ms loop (1M cycles) stim: in ncrcsbs ; Wait for BSY ani busbsy ; . jnz select$ok ; Got him! dcr c jnz stim ; inner loop: 41*256 = 10496 cycles dcr b jnz stim ; outer loop: 10510*96 = 1M cycles xra a ; Select timeout -- clear bus out ncrodr dcr a ; set 'a' to 0ffh sta status ; Save status timeout. jmp all$done ; and clear the registers select$ok: xra a ; Set goodVE$DMA ; get dma address back SHLD DMAADR ; restore dma address LHLD SAVE$CMDPTR SHLD CMDPTR ; restore command pointer JNZ SCSI$CMD$RETRY ; MVI A,0FFH SCSI$DONE: ORA A ; Set Z/NZ for user RET ; and return ; ; ; Build 2-byte SCSI sector number starting with 00 ; ; NOTE: This routine uses only a block number starting with 00 ; and read a maximun of ffh sectors. ; BLD$SCSI$SCTR: lda hdsect sta low$addr ret ; ; Select controller, and fall through to phase if status all$done: mov b,a ; temp save status mvi a,01h ; Release SEL out ncricr ; . xra a ; Remove address from data bus out ncricr ; . mov a,b ; Get status back ora a ; Set status rnz dcr a ; set 'a' to ffh sta status ; clear scsi status to timeout.. ; SCSI.011 ; * * * * * ; * --------\ NOTE: we fall through if we successfully ; * --------/ selected the controller!! ; * * * * * MVI A,00000110B ; Set DMA mode and Monitor Busy OUT NCRMR ; . SCSI$RDY:  selected ok. ; ; busbsy: equ 40h ; SELECT: xra a out ncricr ; Clear initiator command register out ncrtcr ; . and target command register clear$arbit: xra a out ncrmr ; . in ncrrpi ; reset interrupts arbitrate: lda my$id ; Assert my ID (the initiator) out ncrodr ; . mvi a,1 ; start arbitration out ncrmr ; . in$progress: in ncricr ; Wait for "arbitration in ani 40h ; . progress" bit jnz arbitrate$won ; we have argitration in ncrbsr ani 10h ; see if scs; Wait for either a 5380 "Interrupt" or a REQ from Target. ; The REQ is needed since it may have come too soon after ; selection to register an Interrupt. IN NCRBSR ; Check for "Interrupt" ANI 00010000B ; . JNZ SCSI$INT IN NCRCSBS ; Check for REQ ANI NCRREQ ; . JZ SCSI$RDY ; Wait for Interrupt or REQ JMP PHASE ; Process phase vector SCSI$INT: ; Determine cause of 5380 "Interrupt". Either phase ; changed, busy dropped, or bus was reset. If bits 2 and 3 ; of the NCRBSR are not  0's when the Interrupt flag (bit 4) ; is set, then it is either a loss of BUSY or an SCSI RESET. XRA A OUT NCRICR ; Release data bus IN NCRBSR ; Read 5380 Bus and Stat Reg ANI 00001100B ; Keep interesting bits JNZ SCSI$EXIT ; Reset or Busy Loss: Exit ; 00 --> Process phase vector PHASE: ; DMA mode and Monitor Busy must be cleared prior to clearing ; of the 5380 Interrupt Flag. Then mode register is restored. ; Otherwise the interrupt flag may not clear and the DMA Mode ; may notr for multi-sector IN NCRBSR MOV C,A ; Keep for phase change checking ANA D ; Mask for DMA request JZ RSCSI2 ; ; All transfer is one byte at a time ; IN NCRDACK MOV M,A INX H JMP RSCSI1 ; Read until phase changes ; ; This code skipped when data is being transferred ... ; RSCSI2: MOV A,C ; Check phase ANI 00010000B ; . JZ RSCSI1 ; Wait for DMA request JMP SCSI$INT ; Process "interrupt" flag ; ; Generalized SCSI write routine ; WSCSI:  be useable. XRA A ; Clear 5380 Mode Register OUT NCRMR ; . IN NCRRPI ; Reset interrupts MVI A,00000110B ; Set DMA mode and Monitor Busy OUT NCRMR ; . IN NCRCSBS ; Update phase... ANI 00011100B ; Mask all but phase bits, clear carry bit RAR ; Rotate over for target MOV E,A ; . (Save for use with jump table) RAR ; . OUT NCRTCR ; Set phase MVI D,0 ; E is already set (6 ins ago) LXI H,PHASE$TABLE ; Get phase jump table base MVI A,1 ; Assert data bus OUT NCRICR OUT NCRSDS ; Start DMA send ; ; Wait for DMA request, keeping an eye on phase. Note that the NCR ; will not issue an ACK, nor will it generate DMA requests once the ; phase changes, so it is best to treat DMA request checking as a ; higher priority than phase change checking. ; WSCSI1: SHLD DATPTR ; Save (new) data ptr WSCI11: IN NCRBSR MOV C,A ; Save status for use below ANA D ; Check for DMA request JZ WSCSI2 mov a,m  DAD D ; Add offset for this phase MOV A,M ; Get phase pointer into HL INX H ; . MOV H,M ; . MOV L,A ; Pointer is now together MVI D,01000000B ; DMA request mask (used by RSCSI and WRSCSI) PCHL ; Go to it! PHASE$TABLE: DW PHASE0 DW PHASE1 DW PHASE2 DW PHASE3 DW PHASE4 DW PHASE5 DW PHASE6 DW PHASE7 PHASE0: ; Data out phase LHLD DATPTR ; to pass prameters to scsi controller JMP WSCSI ; Execute SCSI write routine PHASE1: ; Data in phase  out ncrdack inx h JMP WSCSI1 ; Write more bytes until phase changes ; ; This code skipped when data is being transferred ... ; WSCSI2: MOV A,C ; Check phase ANI 00010000B ; . JZ WSCSI1 ; Wait for dma request JMP SCSI$INT ; Process "interrupt" ; ; Tempory storage ; TRIES: DB 5 ; number of tries DMA: DS 2 ; save for dma address TIMEOUT: DS 1 MYID: DS 1 TARGET: DS 1 ERFLAG: DS 1 HDSECT: DS 1 DATPTR: DS 2 CMDPTR: DS 2 MESSAGE: DS 4 SCSI: DS 1 ... LHLD DMAADR ; Use data pointer JMP RSCSI ; Execute SCSI read routine PHASE2: ; Command out phase ... LHLD CMDPTR ; Use command pointer JMP WSCSI ; Execute SCSI write routine PHASE3: ; Status in phase ... LXI H,STATUS ; Use status pointer JMP RSCSI ; Execute SCSI read routine PHASE7: ; Message in phase ... LXI H,MESSAGE ; Use message pointer JMP RSCSI ; Execute SCSI read routine ; Currently unused phases PHASE4: PHASE5: PHASE6: SCSISAVE$DMA: DS 2 SAVE$CMDPTR: DS 2 END ferred ... ; WSCSI2: MOV A,C ; Check phase ANI 00010000B ; . JZ WSCSI1 ; Wait for dma request JMP SCSI$INT ; Process "interrupt" ; ; Tempory storage ; TRIES: DB 5 ; number of tries DMA: DS 2 ; save for dma address TIMEOUT: DS 1 MYID: DS 1 TARGET: DS 1 ERFLAG: DS 1 HDSECT: DS 1 DATPTR: DS 2 CMDPTR: DS 2 MESSAGE: DS 4 SCSI: DS 1 $EXIT: XRA A ; clean up 5380 and exit OUT NCRTCR OUT NCRMR IN NCRRPI ; reset unterrupts RET ; Generalized SCSI read routine ; Initiator command reg is already ininialized by phase ; RSCSI: OUT NCRSDIR ; Start DMA initiator receive ; Wait for DMA request, keeping an eye on phase. Note: we must do ; a check for DMA request before checking for a phase change, since ; a byte may be queued up waiting to be DACKed prior to the phase ; change. RSCSI1: SHLD DATPTR ; Save (new) data pttransfer only if ; you can be sure the controller can buffer 256 bytes of data and ; can transfer at 5.25 us per byte. Extra DACK's after the last REQ ; will do no harm. ; ; INIR register use: H = memory pointer, C = I/O port, B = counter ; MOV B,E ; Set up loop count MVI C,NCRDACK ; Set up source port address DB 0EDH ; ED B2 = INIR instruction op code DB 0B2H JMP RSCSI1 ; Read more bytes until phase changes ; Be sure and check phase if no DMA ;  request, since NCR won't issue any ; unneeded DACKs ; This code skipped when data is being transferred ... RSCSI2: MOV A,C ; Check 5380 "interrupt" flag ANI 00010000B ; . JZ RSCSI1 ; Wait for DMA request, JMP SCSI$INT ; or process "interrupt" ENDIF ; ; Temporary storage ; TRIES: DB 5 ; number of tries DMA: DS 2 ; save for dma address TIMEOUT: DS 1 MYID: DS 1 TARGET: DS 1 ERFLAG: DS 1 HDSECT: DS 1 PRAMPTR: DS 2 CMDPTR: DS 2 MESSAGE: DS 4 SCSI: DS 1 SCSI$IOes) ; ; The boot loader is modified to find this offset and install the users ; changes in bios before jumping to cold boot. ; ; NOTES: ; ; Only the system track of the hard disk with SCSI id 0 and LUN 0 will ; be patched. ; ; Only Little Board's jumpered for SCSI initiator id 7 can run this ; utility. ; ; Sector 31 (1F hex) of the hard disk system track is reserved. ; ; Requires special SCSI boot prom . ; ;---------------------------------------------------------------$COUNT DS 1 ; Byte/burst mode control END --------- ; ; The following equates are entry points into the boot prom ; SETDMA EQU 800FH READTRK EQU 8012H ; BOOT EQU 9000h ; location of boot loader TPA EQU 100H OFFSET EQU (BOOT-TPA)-3 ; -3 for jmp start before patch area WBOOT EQU 0 ; location of bios warm boot ; CONTROL EQU 0 ; board control port ; SREAD EQU 08H ; scsi read data SWRITE EQU 0AH ; scsi write data ; CR EQU 0DH LF EQU 0AH ;----------------------------------------------------------------- ; ORG TPA ; cp/m tpa ; * * * * * * * * * * * * * * * * * * * * * * * ; * * ; * Hard Disk Auto-Boot Installer * ; * * ; * Copyright (C) 1985 AMPRO Computers, Inc. * ; * All rights reserved. * ; * * ; * * * * * * * * * * * * * * * * * * * * * * * ; ; ; Revision history: ; ; Ver Date Who JMP START ; ;****************************************************************** ;************************Boot loader******************************* ; values set at 0 are patched to users addresses by the program. ; PATCH$START: MVI A,40H ; must turn off rom out control DMA: lxi H,0 ; set dmaadr address call setdma call readtrk ; read first track call readtrk ; read second track ; ; find the user disk information that was loaded in to memory ; from sector 15 of the hard disk ; lhld Description ; --- -------- --- ----------------------------------------- ; ; 1.2 1/7/85 fsw Installs users HD$ALV$AVAIL: information ; in bios at boot time. ; ; 1.0 9/20/85 fsw Initial release. ; ; VERS EQU 12 ; version 1.2 ; ; This version saves all the setup disk parameters that the user ; has defined by running SYSGEN, HINIT, and SWAP to the hard disk. ; This user defined information is stored on the last sector (sector 31) ; of the system tra (dma+1)+offset ; get load address lxi d,15872 ; offset to where hard disk sector 31 ; landed dad d ; sorce address in 'hl' ; ; move the users dpbase into bios ; DDPBAS: lxi d,0 ; patched to destination address lxi b,256 ; number of bytes to move dw 0b0edh ; z80 ldir ; ; move the users phytab into bios ; DOPHY: lxi d,0 ; patched to address of phytbl lxi b,64 dw 0b0edh ; move the 4 bytes with z80 ldir ; ; move the users dpb's into bios ; DODPB: lxi d,0 ; patched to destick, and is used to "patch" the standard BIOS on boot. ; ; The information on this sector will be read somewhere into memory ; by the hard disk boot prom with an offset of 15872 bytes (3E00 hex) ; from the boot dma address. The user's dpbase (256 bytes), the 64 byte ; PHYTAB with the user's logical/physical configuration, the user's ; hard disk DBP's (176 bytes), the users HD$ALV$AVAIL block (12 bytes), ; and the users hard disk byte/block flag (1 byte) are saved in that order. ; (total 509 bytnation address lxi b,176 ; size hard disk DPB's dw 0b0edh ; z80 ldir ; ; move the users HD$ALV$AVAIL info into bios ; HDAVL: lxi d,0 ; patched to destination address lxi b,12 ; bytes to move dw 0b0edh ; z80 ldir ; ; move the users byte, block mode value to bios ; CTLBYT: lxi d,0 ; patched to destination address mov a,m ; 'hl' has location of byte stax d ; store at 'de' location ; GOBIOS: JMP 0 ; go to cold boot ; SIZE$PATCH: equ $-patch$start ; ;********************End of  Boot loader**************************** ;****************************************************************** ; START: lxi h,0 ; init new stack and save old dad sp shld oldstk lxi sp,stack ; ; clear the buffer area to zeros ; lxi h,btsec lxi b,1024 ; do 1024 bytes CLEAR: xra a mov m,a inx h dcx b mov a,b ora c jnz clear ; ; move the bios jump table to use it's io, easier ; lhld wboot+1 ; get address of bios mvi l,0 ; begining of bios jmp table lxi d,bj$tbl ; bios jmp e boot sector in memory lets see if sysgen has been ; done on the hard disk. two choices are present for the first byte ; of the AMPRO boot sector either mvi a,xx or lxi h,xxxx , check for ; both. ; ldax d ; call to scsi returns data pointer cpi 3eh ; see if mvi a,x jz next cpi 21h ; could be lxi h,xxx jz next call puts ; neither db cr,lf,'Valid Boot sector not present on hard disk, ' db 'do SYSGEN, HINIT, and SWAP, then retry.',0 jmp exit ; ; figure out the dma address to load table lxi b,64 ; move 64 bytes dw 0b0edh ; z80 ldir ; ; check id of board ; in 29h ; get board id ani 07h ; mask board id cpi 7 ; must be 7 jz signon adi '0' sta shid ; show id setting call puts db cr,lf,'Little Board ID = ' SHID: db ' , must = 7 to run this utility.',0 jmp exit ; ; bios jmp table moved, give signon ; SIGNON: call puts db cr,lf,lf,lf db ' AMPRO Hard Disk Auto-Boot Installer ',cr,lf db ' Copyright (C) 1985 AMPtrack 0 at ; the rest if the info we will find in the bios jmp table ; NEXT: ldax d ; find the lxi h,xxx instruction for cpi 21h ; dma address inx d jnz next xchg ; location of dma address in 'hl' mov a,m inx h mov h,m ; dma address in 'hl' mov l,a shld dma+1 ; set dma address in new boot loader ; ; fix the cold boot address ; lhld wboot+1 ; get address of warm boot from cp/m mvi l,0 ; make it a cold boot shld gobios+1 ; patch jmp in boot loader ; ; get users dpbase, RO Computers, Inc. ',cr,lf db ' Version ',VERS/10+'0','.',VERS MOD 10+'0' db cr,lf,lf db 'This utility will install your current hard disk ' db 'configuration on the system ',cr,lf db 'tracks of the SCSI hard disk drive located at ' db 'SCSI ID 0, LUN 0. ',cr,lf,lf db 'Prior to using this program you must use SYSGEN ' db 'to write the proper size ',cr,lf db 'CP/M system to the system tracks of the SCSI hard ' db 'disk drialways at cold boot+80h in AMPRO bios ; mvi l,80h ; offset from cold boot shld ddpbas+1 ; load address in boot loader lxi d,udpbase ; move the dpbase to save area lxi b,256 dw 0b0edh ; move it, ldir push d ; save buffer destination address ; ; get address of phytbl ; call gettbl ; get address of next table lxi d,6 ; offset to phtbac dad d ; 'hl'=address of phtbac routine shld gtbl+1 lxi h,0 ; if 'hl'=00 returns address of phytbl GTBL: call 0 ; call phtbac shld dophy+1 ;ve from which you ',cr,lf db 'will boot. In addition, you must have already ' db 'used the HINIT and SWAP ',cr,lf db 'utilities to customize your system. ',cr,lf,lf db 'NOTE: The CP/M system you install on the hard ' db 'disk must identical to ',cr,lf db 'the system you are running now. ',cr,lf,lf db 'WARNING!!! ',cr,lf db 'As a safety measure, be sure to backup your hard ' db 'disk before you perform ',cr,lf  patch location of phytab in users bios pop d ; get destination address lxi b,64 ; move 64 bytes dw 0b0edh ; z80 ldir push d ; save address ; ; the patch area now contains the phytab as designated by the user. ; ; lets find the users DPB's for the hard disk. Test first to see ; if SWAP has been run ; mvi c,0 ; drive 'A' mvi e,1 ; not first time select call seldsk ; do select to return dpb header address lxi d,10 ; offset to dpb address dad d mov a,m ; get the address of t db 'this function or attempt hard disk auto-booting! ',cr,lf,lf db cr,lf,'Do you wish to continue? (Y/N) ',0 call conin ani 5fh ; make it upper case cpi 'Y' jnz exit ; exits if input not Y ; ; read the current boot sector into memory ; DOIT: mvi a,sread ; scsi read command sta scsicmd lxi h,scsicmd ; point to scsi command lxi d,btsec ; data area mvi a,1 ; target address call scsi jnz scsierr ; not zero results=error ; ; we have thhe dpb in 'hl' inx h mov h,m mov l,a mov a,m ; 'hl' has address of DBP cpi 40+1 ; sectors per track information jnc movdpb ; have hard disk DPB call puts db cr,lf,'Run HINIT and SWAP to set up your system first.' db cr,lf,0 pop d ; restore stack for exit jmp exit ; ; ; find address of first hard disk dpb ; MOVDPB: lhld wboot+1 ; get address of bios mvi l,80h ; offset to dpbase lxi d,16*5 ; offset to first hard disk dpb header dad d ; 'hl'= first hard disk lxi d,10  ; offset to dpb address dad d mov a,m ; get the address of the dpb in 'hl' inx h mov h,m mov l,a pop d ; restore destination address shld dodpb+1 ; address of users hd dpb for boot loader lxi b,176 ; move 176 bytes, 11 dpb's dw 0b0edh ; z80 ldir ; ; find the HD$ALV$AVAIL block to save and the scsi controller burst/byte ; mode value and save. needed for Shugart and other non-burst controllers ; push d ; save 'de' call gettbl ; get address of next table lxi d,3 ; dad dm1 ; numeric adi '7' ; alpha offset (A thru F) jmp num1+2 NUM1: adi '0' ; numeric offset (0 thru 9) sta errtyp+1 ; ERRMSG: call puts db cr,lf,'SCSI read/write error, Sense byte = ' ERRTYP: db ' ',cr,lf,lf,0 ; fall thru and exit ; EXIT: xra a lhld oldstk sphl ret ; ; SEND BYTES TO CONSOLE UNTIL ZERO ENCOUNTERED ; PUTS: pop h ; 'hl' has address of msg mov c,m ; get char call conout ; send to comsole mov a,m cpi lf cz wait ; wait 10 ms after lf for slow terminals  ; shld brst+1 BRST: CALL 0 ; call get hd$info pop d ; get table address back push h ; save address of HD$ALV$AVAIL shld hdavl+1 ; save bios address in boot loader lxi b,12 ; bytes to move into table dw 0b0edh ; z80 ldir pop h ; get location of HD$ALV$AVAIL back push d ; save table address lxi d,4 ; dad d ; 'hl' points to address of byte, burst mov a,m inx h mov h,m mov l,a ; address of byte,burst mode byte mov a,m ; get current value, 0 or 1 pop d stinx h ; next byte mov a,m ; ana a ; see if end=0 jnz puts+1 ; nope pchl ; hl has return address ; WAIT: mvi b,4 lxi d,0 WAIT1: inx d mov a,e cmp d jnz wait1 dcr b rz jmp wait1 ; ; scsi command block ; SCSICMD db sread ; db 0 ; high address db 0 ; middle address LOWADDR db 0 ; low address db 1 ; number of sectors=1 db 0 ; reserved ;***********************Save area************************** ; OLDSTK: ds 2 ; storage for old stack pointer ; BJ$TBL: ; taax d ; save value shld ctlbyt+1 ; save bios address in boot loader ; ; we have all the information from the users bios in the patch area ; read to overlay the first sector and write back to disk. ; lxi h,btsec ; clean the boot sector lxi b,128 CLEAN: xra a mov m,a inx h dcx b mov a,b ora c jnz clean ; ; overlay the new boot loader on the boot sector ; lxi h,patch$start ; begining of new boot loader lxi d,btsec ; location of boot sector in ram lxi b,size$patch ; number of pble of bios jumps CBOOT ds 3 ; cold boot address ds 3 ; warm boot CONST: ds 3 ; console status CONIN: ds 3 ; console character in CONOUT: ds 3 ; console character out ds 3 ; list char out ds 3 ; punch char out ds 3 ; reader char in ds 3 ; home SELDSK: ds 3 ; select disk SETTRK: ds 3 ; set track number SETSEC: ds 3 ; set sector number ds 3 ; set dma address ds 3 ; read disk ds 3 ; write disk ds 3 ; return list status ds 3 ; sector translate GETTBL: ds 3 ; point tatch bytes dw 0b0edh ; z80 ldir, movit ; ; now write it back to hard disk ; mvi a,swrite ; scsi write sta scsicmd mvi a,1 ; target address lxi h,scsicmd ; the scsi command lxi d,btsec ; data area call scsi ; do the write jnz scsierr ; if error ; ; boot sector written, now do users swap and disk information ; mvi a,swrite ; scsi write sta scsicmd mvi a,31 ; sector 31 sta lowaddr ; lxi h,scsicmd lxi d,user$tab ; users bios information mvi a,1 call scsi ; do the wo next jmp table ds 3 ; getedsk, pointer to 'e' disk prameters ds 3 ; ioinit SCSI: ds 3 ; scsi direct driver ; ds 32 ; 32 byte stack area STACK: EQU $ ; BTSEC: DS 512 ; USER$TAB: UDPBASE: DS 256 ; users dpbase UPHYTAB: DS 64 ; users phytab UHDDPB: DS 176 ; users hd dpb's UHDAVAL: DS 12 ; users hd$alv$avail values BYTEBLK: DS 1 ; scsi controller byte, block value TBL$SIZE: EQU $-user$tab END rite jnz scsierr call puts db cr,lf,'Done! You may now boot from hard disk.',cr,lf,lf,0 jmp exit ; ; show any scsi error that may have occured ; SCSIERR: ldax d ; 'de' has address of sense info push psw ; save it ani 0f0h rar ; convert upper nibble to ascii rar rar rar cpi 10 ; alpha or numeric jc num0 adi '7' ; alpha offset (A thru F) jmp num0+1 ; NUM0: adi '0' ; numeric offset (0 thru 9) sta errtyp ; ; do second digit ; pop psw ani 0fh cpi 10 jc nu  Filename.Typ Size Recs CRC 1 -A60103 .E 0K 0 0000 2 BIOS-1-6.ASM 48K 377 5DDE 3 BIOS-3-7.ASM 102K 815 9CAE 4 CRC .CRC 0K 0 0000 5 DIR .COM 2K 16 BE35 6 HF-1-5 .ASM 52K 410 E18F 7 HG-1-2 .ASM 14K 108 F63F 8 HI-1-2 .ASM 54K 419 FDF6 9 HP-1-2 .ASM 48K 372 B83A 10 SBT-1-2 .ASM 28K 209 8ABE  requirement to 3.7 for Seagate support. ; ; 1.4 E7.18 RJB Added DTC format, changed minimum bios ; requirement to 3.1 for Shugart support. ; ; 1.3 E6.27 RJB Changed to support bios 3.0 to allow the ; user to manually enter the drive descriptor. ; ; 1.2 E3.14 RJB Modified signon message to include known ; rom revision limitations. ; ; E3.13 RJB Updated XEBEC and ADAPTEC step routines. ; ; 1.1 E2.14 RJB Added message reminding users to run the ; FINDBAD program to find any bad sectors ; on the disk they just formatted. ; ; E2.12 RJB Added message for those don't have any ; hard disk units defined and run the hard ; disk format program anyway. ; ; E1.30 RJB Fixed Xebec directory clear, Adaptec step, ; device select. Added additional user ; interface, error reporting routines. ; ; 1.0 DC.14 RLD Original version. ; ; Program version, and current version date VERS EQU 15 ; Current version THIS$MONTH EQU 4 ; Today's month THIS$DAY EQU 02 ; .  day THIS$YEAR EQU 86 ; . year INT$REV EQU 99 ; Internal revision number ; TRUE and FALSE are defined here NO EQU 0 FALSE EQU 0 YES EQU NOT FALSE TRUE EQU NOT FALSE ; Operating characteristics MIN$VERSION EQU 37 ; Minimum bios version allowed CMD$LINE$OK EQU FALSE ; Allow command line input? RTN$VIA$WB EQU TRUE ; Return to CP/M via warm boot? INTERNAL EQU FALSE ; Internal (unreleased) revision? ; Include diagnostic messages? TEST EQU NO ; TEST diagnostics?  ; Current screen width and output buffer width SWIDTH EQU 80 ; Screen width PWIDTH EQU 132 ; Output buffer width ; Z-80 opcode equates (reversed so we can use a DW to enter them) CPIR80 EQU 0B1EDH ; CPIR INIR80 EQU 0B2EDH ; INIR LDIR80 EQU 0B0EDH ; LDIR OTIR80 EQU 0B3EDH ; OTIR INI80 EQU 0A2EDH ; INI OUTI80 EQU 0A3EDH ; OUTI SBCD80 EQU 043EDH ; LD (dddd),BC LBCD80 EQU 04BEDH ; LD BC,(dddd) SDED80 EQU 053EDH ; LD (dddd),DE LDED80 EQU 05BEDH ; LD DE,(dddd * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Hard Disk Format Utility * * Copyright (C) 1985, 1986 AMPRO COMPUTERS, INC. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ; Assemble with asm.com or equivalent. ; All Z80 opcodes are defined with DB or DW statements. ; Revision history: ; ; Ver Date Who Description ; --- ----- --- ------------------------------------------ ; 1.5 F4.02 RJB Added Seagate 225N. Changed minimum bios ; ) SSPD80 EQU 073EDH ; LD (dddd),SP LSPD80 EQU 07BEDH ; LD SP,(dddd) LXIX EQU 021DDH ; LXI IX,dddd SIXD80 EQU 022DDH ; LD (dddd),IX LIXD80 EQU 02ADDH ; LD IX,(dddd) POPIX EQU 0E1DDH ; POP IX PUSHIX EQU 0E5DDH ; PUSH IX LXIY EQU 021DDH ; LXI IY,dddd SIYD80 EQU 022FDH ; LD (dddd),IY LIYD80 EQU 02AFDH ; LD IY,(dddd) POPIY EQU 0E1FDH ; POP IY PUSHIY EQU 0E5FDH ; PUSH IY ; Bit SET/RESET/TEST Z-80 opcode equates (use DB to enter) ; Example: SET 7,D would be DB BIT,BSET +B7+ZD BIT EQU 0CBH ; Bit prefix BTST EQU 040H ; Bit test BRES EQU 080H ; Bit reset BSET EQU 0C0H ; Bit set B0 EQU 000H ; Bit 0 B1 EQU 008H ; Bit 1 B2 EQU 010H ; Bit 2 B3 EQU 018H ; Bit 3 B4 EQU 020H ; Bit 4 B5 EQU 028H ; Bit 5 B6 EQU 030H ; Bit 6 B7 EQU 038H ; Bit 7 ZB EQU 000H ; B Reg ZC EQU 001H ; C Reg ZD EQU 002H ; D Reg ZE EQU 003H ; E Reg ZH EQU 004H ; H Reg ZL EQU 005H ; L Reg ZM EQU 006H ; M Reg ZA EQU 007H ; A Reg ; Jump relatiDB 'You will be asked for the controller ' DB 'type, controller SCSI address, drive unit number, ' DB 'and the drive descriptor information.' DB CR,LF,LF,'$' WARNING$MSG: DB 'CAUTION: ALL DATA WILL BE ERASED ON THE DRIVE YOU ' DB 'SELECT TO FORMAT.',CR,LF,LF,LF DB '$' ; ; Initialize the command line input pointer. ; START: IF CMD$LINE$OK ; If we want to allow cmd line input, LXI H,0080H ; . Save the command line. LXI D,INBUF ; . . LXI B,128 ; . . DW LDIR80 ; . . LXIve opcode equates (use DB to enter) ; Example: JR AGAIN would be DB JR,AGAIN-$-1 JR EQU 018H ; JR addr JRNZ EQU 020H ; JR NZ,addr JRZ EQU 028H ; JR Z,addr JRNC EQU 030H ; JR NC,addr JRC EQU 038H ; JR C,addr ; IX and IY prefixes (use DB to enter) IX EQU 0DDH ; IX prefix IY EQU 0FDH ; IY prefix ; Character equates CTRLC EQU 'C'-'@' ; Ctrl-C (ETX) BS EQU 'H'-'@' ; Ctrl-H (Backspace) TAB EQU 'I'-'@' ; Ctrl-I (Tab) LF EQU 'J'-'@' ; Ctrl-J (Line feed) FF EQU 'L'-' H,INBUF ; . Set up ptrs and count of chrs MOV A,M ; . . STA CMD$LINE$CHRS ; . Save count of characters, INX H ; . Bump line ptr, SHLD CMD$LINE$PTR ; . . and save ptr to cmd line ENDIF IF NOT CMD$LINE$OK ; If no command line input allowed, MVI A,0 ; . Clear the count of characters, STA CMD$LINE$CHRS ; . . LXI H,0 ; . but set the ptr up anyway. SHLD CMD$LINE$PTR ; . . ENDIF IF NOT RTN$VIA$WB ; If we're returning without warm boot, LXI H,0000H ; . then get the old @' ; Ctrl-L (Form feed) CR EQU 'M'-'@' ; Ctrl-M (Carriage return) NAK EQU 'U'-'@' ; Ctrl-U CAN EQU 'X'-'@' ; Ctrl-X (Cancel) EOF EQU 'Z'-'@' ; Ctrl-Z (CP/M End-of-file) ESC EQU 1BH ; Ctrl-[ (Escape) EOS EQU '$' ; (CP/M End-of-string) DEL EQU 7FH ; (Delete) ; bdos equates BDOS EQU 5 ; bdos entry ; * * * * * * * * ; ; The code starts here ... ; ; * * * * * * * * ORG 0100H JMP START SCREEN$WIDTH: DB SWIDTH-1 ; 1 less than actual # SLOW$TERM: DB 10 ; Delastack ptr, DAD SP ; . and SHLD STACK ; . save it for later, ENDIF ; ; This is where you can jump to start the program over. ; TOP$MENU: LXI SP,STACK ; Stuff SP with our stack. CALL GET$BIOS$VERS ; Copy the jmp tbl to a local area ; ; Display the name and signon message ; IF CMD$LINE$OK ; If cmd line input is possible LDA CMD$LINE$CHRS ; Check for any input and skip ORA A ; . the initial messages if any JNZ CHECK$B$VERS ; . chrs in the cmd line. ENDIF CALL CLEARy (ms) for slow term CMD$LINE$CHRS: DB 0 ; # of cmd line chrs left CMD$LINE$PTR: DW 0 ; Ptr to next cmd line chr NAME$MSG: DB 0DH,'AMPRO ' NAME: DB 'Hard Disk Format' DB ' Utility',CR,LF DB 'Copyright (C) 1985 AMPRO Computers, Inc.',CR,LF DB 'Version ',VERS/10+'0','.',VERS MOD 10+'0' IF INTERNAL ; Display internal revision number? DB 'x',INT$REV/10+'0',INT$REV MOD 10+'0' DB ' [',THIS$YEAR-80+'@' DB THIS$MONTH+'0'+((THIS$MONTH/10)*7) DB '.',THIS$DAY/10+'0',THIS$DAY MOD 10+'0',']'$SCREEN ; Clear the screen LXI D,NAME$MSG ; Display the name, version, etc. CALL CENTER$OUTPUT ; . LXI D,SIGNON$MSG ; and the initial message CALL JUSTIFY ; . ; ; Check the version of the bios against the minimum version allowed. ; If the bios is not at least the minimum, display an error message ; and exit to the operating system. ; CHECK$B$VERS: LDA BIOS$VERSION ; Get bios version # CPI MIN$VERSION ; Check against minimum version JNC WHICH$BIOS ; At least minimum version . . . L ENDIF DB CR,LF,LF,'$',CR HELP$MSG: DB 'Usage: HFORMAT' DB CR,LF,'$',CR,' ',EOF SIGNON$MSG: DB 'The AMPRO Hard Disk Format utility is used to ' DB 'format hard disk (winchester) drives, on the ' DB 'Adaptec ACB-4000, ' DB 'Data Technology 500 Series ' DB '(510A, 510B, 520A, 520B), ' DB 'Shugart 1610-4, ' DB 'Xebec 1410 (rom rev "E" or later), ' DB 'Xebec 1410A (rom rev "D" or later) ' DB 'controllers, or the ' DB 'Xebec Owl or Seagate 225N combination drive/controllers. ' XI D,BIOS$PLUS ; Not minimum, display error message CALL JUSTIFY ; . JMP LB$WBOOT ; and exit. BIOS$PLUS: DB 'This program requires AMPRO bios version ' VERSION$REQ: DB MIN$VERSION/10,'.',MIN$VERSION MOD 10,' or later.' DB CR,LF,'$' ; ; Perform any initialization particular to each version of the Ampro ; bios, if necessary. ; WHICH$BIOS: CALL RET$TO$CONT JZ ALL$DONE ; ; Program main loop . . . ; AGAIN: CALL CLEAR$SCREEN ; Clear the screen LXI D,SCSI$ADDR$MSG ; Promp t for SCSI address LXI H,SCSI$ADDR$OKC ; . CALL PROMPT ; . [ 0 1 2 3 4 5 6 7 ] JZ TOP$MENU ; . MOV A,B ; . DCR A ; . CALL BIN$TO$SCSI ; . STA SCSI$ADDR ; . CALL A$TO$HL$HEX ; . SHLD S$ADDR$CODE ; . LXI D,HDC$TYPE$MSG ; Prompt for controller type LXI H,HDC$TYPE$OKC ; . CALL PROMPT ; . [ 1 2 3 4 ] JZ AGAIN ; . MOV A,B ; . DCR A ; . STA HDC$TYPE ; . MVI A,0 ; Set logical unit to 0 for SCSI drives STA DRV$LUN ; . LDA HDC$TYPE ; If Xebec OWL or Seagate 225n  done CPI 'N' ; If yes or CR, play it again, sam, JNZ AGAIN ; . otherwise fall into ALL$DONE ; ; Restore the old stack pointer and exit the program. ; ALL$DONE: JMP LB$WBOOT ; Return via warm boot SCSI$ADDR$MSG: DB 'What is the SCSI address of your ' DB 'controller (0-7)? ','$' SCSI$ADDR$OKC: DB '01234567',0 HDC$TYPE$MSG: DB CR,LF,'Which type of controller are you using:' DB CR,LF,TAB,'1 - Adaptec ACB 4000' DB CR,LF,TAB,'2 - Shugart 1610-4' DB CR,LF,TAB,'3 - XebecCPI XEBEC$OWL ; . skip the drive characteristics JZ SCSI$DRIVE ; . as these SCSI drives already CPI SEAGATE$225N ; . "know" what their characteristics JZ SCSI$DRIVE ; . are. LXI D,DRV$LUN$MSG ; Prompt for drive logical unit # LXI H,DRV$LUN$OKC ; . CALL PROMPT ; . [ 0 1 2 3 ] JZ AGAIN ; . MOV A,B ; . DCR A ; . RRC ; Shift response into high bits RRC ; . RRC ; . ANI 0E0H ; . STA DRV$LUN ; . LXI D,DRV$INFO$MSG ; Ask for drive characteristics CALL JUSTIFY ; . OWL (drive/controller)' DB CR,LF,TAB,'4 - Xebec 1410, 1410A' DB CR,LF,TAB,'5 - Seagate 225N (drive/controller)' DB CR,LF,TAB,'6 - Data Technology 500 Series' DB CR,LF,LF DB 'Choose one (1-6): ','$' HDC$TYPE$OKC: DB '123456',0 ADAPTEC: EQU 0 SHUGART: EQU 1 XEBEC$OWL: EQU 2 XEBEC$1410: EQU 3 SEAGATE$225N: EQU 4 DTC$500: EQU 5 DRV$LUN$MSG: DB CR,LF DB 'Which drive on the controller do you want ' DB 'to format (0-3)? ','$' DRV$LUN$OKC: DB '0123',0 DRV$INFO$MSG: DB CR, LXI D,DRV$CYLS$MSG ; . (Cylinders) CALL PROMPT$DECIMAL ; . JZ AGAIN ; . LXI H,DRV$CYLS ; . MOV M,D ; . INX H ; . MOV M,E ; . LXI D,DRV$HEAD$MSG ; . (Heads) CALL PROMPT$DECIMAL ; . JZ AGAIN ; . STA DRV$HEAD ; . LXI D,DRV$RWC$MSG ; . (RWC) CALL PROMPT$DECIMAL ; . JZ AGAIN ; . LXI H,DRV$RWC ; . MOV M,D ; . INX H ; . MOV M,E ; . LXI D,DRV$WPC$MSG ; . (WPC) CALL PROMPT$DECIMAL ; . JZ AGAIN ; . LXI H,DRV$WPC ; . MOV M,D ; . INX H ; . MOV M,E ; . LF DB 'Please enter the characteristics of the ' DB 'drive you wish to format:',CR,LF,'$' DRV$CYLS$MSG: DB ' Number of cylinders: ','$' DRV$HEAD$MSG: DB ' Number of heads: ','$' DRV$RWC$MSG: DB 'Starting cylinder for RWC: ','$' DRV$WPC$MSG: DB 'Starting cylinder for WPC: ','$' DRV$LZ$MSG: DB ' Landing zone cylinder: ','$' ADP$STEP$MSG: DB CR,LF,LF DB 'Adaptec step mode (choose from the ' DB 'following table):',CR,LF DB TAB,'0 - 3ms step',CR,LF DB TAB,'1 - 28us LDA HDC$TYPE ; . CPI SHUGART ; . JNZ NOT$SHUGART ; . LXI D,DRV$LZ$MSG ; . (Landing zone) CALL PROMPT$DECIMAL ; . JZ AGAIN ; . LXI H,DRV$LZ ; . MOV M,D ; . INX H ; . MOV M,E ; . NOT$SHUGART: CALL GET$STEP ; Get step rate table for this HDC CALL PROMPT ; Get step rate factor JZ AGAIN ; . MOV A,B ; . DCR A ; . STA STEP$RATE ; . SCSI$DRIVE: LXI D,INTL$MSG ; Prompt for interleave factor LXI H,INTL$OKC ; . CALL PROMPT ; . [ 1 2 3 4 5 6 7 8 9 ] STA INTL$EC buffered step',CR,LF DB TAB,'2 - 12us buffered step',CR,LF DB 'Which step mode (0, 1, or 2)? ','$' ADP$STEP$OKC: DB '012',0 DTC$STEP$MSG: DB CR,LF,LF DB 'Data Technology step mode (choose ' DB 'from the following table):',CR,LF DB TAB,'0 - 3ms step',CR,LF DB TAB,'1 - 200us buffered step',CR,LF DB TAB,'2 - 150us buffered step',CR,LF DB TAB,'3 - 100us buffered step',CR,LF DB TAB,'4 - 50us buffered step',CR,LF DB 'Which step mode (0, 1, 2, 3, or 4)? ','$' DTC$STEP$OKC: DHO ; . MOV A,B ; . STA INTERLEAVE ; . (& save the interleave) JZ AGAIN ; . if , back to main loop LXI D,RTF$MSG ; Prompt for return key, to LXI H,RTF$OKC ; . main menu, exits CALL PROMPT ; . [ ESC CR ] JZ AGAIN ; . if , back to main loop CALL HD$FORMAT ; Do the format routine LXI D,AGAIN$MSG ; Display successful msg & prompt LXI H,AGAIN$OKC ; . for another drive to format. CALL PROMPT ; . [ ESC N Y CR ] JZ ALL$DONE ; . if , allB '01234',0 SHU$STEP$MSG: DB CR,LF,LF DB 'Shugart step mode (choose from the ' DB 'following table):',CR,LF DB TAB,'0 - 3ms step',CR,LF DB TAB,'1 - 1ms step',CR,LF DB TAB,'2 - 200us buffered step',CR,LF DB TAB,'3 - 70us buffered step',CR,LF DB TAB,'4 - 50us buffered step',CR,LF DB 'Which step mode (0, 1, 2, 3, or 4)? ','$' SHU$STEP$OKC: DB '012345',0 XEB$STEP$MSG: DB CR,LF,LF DB 'Xebec step mode (choose from the ' DB 'following table):',CR,LF DB TAB,'0 - 3ms s tep',CR,LF DB TAB,'4 - 200us buffered step',CR,LF DB TAB,'5 - 70us buffered step',CR,LF DB TAB,'6 - 30us buffered step',CR,LF DB TAB,'7 - 15us buffered step',CR,LF DB 'Which step mode (0, 4, 5, 6, or 7)? ','$' XEB$STEP$OKC: DB '04567',0 INTL$MSG: DB CR,LF,'What sector interleave factor ' DB '(1-9, usually 2)? ','$' INTL$OKC: DB '123456789',0 SCSI$ADDR: DB 01H HDC$TYPE: DB 0 DRV$LUN: DB 0 DRV$CYLS: DW 0 ; Keep these four items in order DRV$HEAD: DB 0 ; . (cyls, head, rwc, ck" by DB 0EDH,0B0H ; . "track". LDA HDC$TYPE ; Get controller type LXI H,H$FMT$EXE ; & table starting address JMP GO$TABLE ; Jump to controller routine H$FMT$EXE: DW H$DO$ADAPTEC ; Adaptec 4000 DW H$DO$SHUGART ; Shugart 1610-4 DW H$DO$OWL ; Xebec Owl DW H$DO$XEBEC ; Xebec 1410 rev e, 1410A rev d DW H$DO$SEAGATE ; Seagate 225N DW H$DO$DTC ; Data Technology 500 Series H$DO$UNKNOWN: LXI D,NEW$CTRL ; Print error message for an unknown CALL JUSTIFY ; . controller type MVI A,0wpc) DRV$RWC: DW 0 ; . DRV$WPC: DW 0 ; . DRV$LZ: DW 0 STEP$RATE: DB 0 INTERLEAVE: DB 2 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Hard disk display & format routines . . . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * GET$STEP: LDA HDC$TYPE ; Get controller type LXI H,H$STEP$EXE ; & step table starting addr JMP GO$TABLE ; Jump to controller routine H$STEP$EXE: DW H$STEP$ADP ; Adaptec ACB 4000 DW H$STEP$SHU ; Shugart 1610-4 FFH ; Set error status JMP H$FMT$DONE ; . H$DO$ADAPTEC: LXI D,FMT$START$MSG ; Show the user we're going ... CALL JUSTIFY ; . LXI D,A$MD ; Where to put the fmt data LXI H,DRV$CYLS ; . LXI B,7 ; . DB 0EDH,0B0H ; . (LDIR) LDA STEP$RATE ; Get step rate STA A$STEP ; Save step rate XRA A ; Set stop position to beyond the STA A$LZ ; last track LDA INTERLEAVE ; Set interleave STA A$INTL ; . LXI H,A$MSC ; Mode Select LXI D,A$MSD ; . CALL DO$SCSI ; . JNZ FMT$ERDW H$STEP$XEB ; Xebec Owl DW H$STEP$XEB ; Xebec 1410, 1410A DW H$STEP$ADP ; Seagate 225N DW H$STEP$DTC ; Data Technology 500 Series H$STEP$ADP: LXI D,ADP$STEP$MSG LXI H,ADP$STEP$OKC RET H$STEP$DTC: LXI D,DTC$STEP$MSG LXI H,DTC$STEP$OKC RET H$STEP$SHU: LXI D,SHU$STEP$MSG LXI H,SHU$STEP$OKC RET H$STEP$XEB: LXI D,XEB$STEP$MSG LXI H,XEB$STEP$OKC RET DO$SCSI: ; ; Entry: ; HL = Command string ; DE = Data string ; SCSI address in SCSI$ADDR ; Drive logical unit iROR ; . LXI H,A$FC ; Format unit LXI D,A$FC ; . (no data required) CALL DO$SCSI ; . JNZ FMT$ERROR ; . ; CALL H$DIR$CLEAN ; Clean directory area (not needed) ; JNZ FMT$ERROR ; . JMP H$FMT$DONE ; and jump to the end routine A$MSC: DB 15H,0,0 ; Mode select DB 0,16H,0 ; . A$MSD: DB 0,0,0,8 ; Mode select data DB 0,0,0,0,0 ; . DB 0,2 ; . (block size = 512 bytes) DB 0 ; . DB 1 ; . A$MD: DB 0,0,0,0,0,0,0 ; . (format data from bios) A$LZ: DB 0 ; . (landing zone position) A$n DRV$LUN ; ; Exit: ; HL = unchanged ; DE = unchanged if no error, else ptr to error data ; A = SCSI error code (0 = no error) ; INX H ; Point to drive unit LDA DRV$LUN ; 'OR' it in ORA M ; . MOV M,A ; . DCX H ; Set HL back LDA SCSI$ADDR ; Get SCSI address CALL LB$SCSIDRV ; Call SCSI driver, ORA A ; set status, RET ; and return. HD$FORMAT: ; ; [E6.07] ; ; Format a hard disk unit. ; ; Entry: ; Drive format information in the following locations: ; ; SCSI addressSTEP: DB 0 ; . (adaptec step rate) A$FC: DB 04H ; Format unit DB 06H ; . (use next byte for fill) DB 0E5H ; . (fill byte) DB 00 ; . A$INTL: DB 00 ; . (interleave) DB 00 ; . H$DO$DTC: LXI D,FMT$START$MSG ; Show the user we're going ... CALL JUSTIFY ; . LHLD DRV$CYLS ; Move the data to the proper area SHLD D$CYL ; . LDA DRV$RWC+1 ; . STA D$RWC ; . LDA DRV$HEAD ; . DCR A ; . STA D$HD ; . LDA STEP$RATE ; Get step rate MOV E,A ; Translate from table M of controller SCSI$ADDR ; Hard disk controller type HDC$TYPE ; Disk drive logical unit # DRV$LUN ; Interleave factor INTERLEAVE ; ; Exit: ; designated disk unit is formatted ; ; Modifies: ; all ; LXI D,E5BUF ; Fill first 256 bytes of buffer LXI B,00E5H ; . with 0E5H, used to clear unit CALL FILL$BLOCK ; . if HDC doesn't clear by itself. LXI H,E5BUF ; Fill next 8K with 0E5H. When the LXI D,E5BUF+256 ; . HD$DIR$CLEAN routine is run, the LXI B,8192-256 ; . unit will be cleared "traVI D,0 ; . LXI H,D$TRAN$TBL ; . DAD D ; . MOV A,M ; . STA D$STPR ; . LDA INTERLEAVE ; Set interleave STA D$INTL ; . LXI H,D$MSC ; Mode Select LXI D,D$MSD ; . CALL DO$SCSI ; . JNZ FMT$ERROR ; . LXI H,D$FC ; Format unit LXI D,D$FC ; . (no data required) CALL DO$SCSI ; . JNZ FMT$ERROR ; . ; CALL H$DIR$CLEAN ; Clean directory area ; JNZ FMT$ERROR ; . JMP H$FMT$DONE ; and jump to the end routine D$TRAN$TBL: DB 60 ; 3ms DB 4 ; 200us DB 3 ; 150us DB  2 ; 100us DB 1 ; 50us D$MSC: DB 0C2H,0,0 ; Mode select DB 0,0,0 ; . D$MSD: DB 11 ; Mode select data D$STPR: DB 0 ; . (Step rate) DB 0 ; . (Step mode) D$HD: DB 0 ; . (# of heads) D$CYL: DB 0,0 ; . (# of cylinders) D$RWC: DB 0 ; . (RWC starting cylinder) DB 0,0 D$FC: DB 04H ; Format unit DB 0 ; . DB 0 ; . DB 0 ; . D$INTL: DB 0 ; . (interleave) DB 0 ; . H$DO$SHUGART: LXI D,FMT$START$MSG ; Show the user we're going ... CALL JUSTIFY ; . LHLD DRV$CYLS ; Mo LXI D,E5BUF ; . CALL DO$SCSI ; . JNZ FMT$ERROR ; . LXI H,X$FC ; Format unit LXI D,X$FC ; . (no data required) CALL DO$SCSI ; . JNZ FMT$ERROR ; . ; CALL H$DIR$CLEAN ; Clean directory area (not needed) ; JNZ FMT$ERROR ; . JMP H$FMT$DONE ; and jump to the end routine X$IC: DB 0CH ; Init Drive Characteristics DB 0,0,0,0,0 ; . X$ID: DB 0,0,0,0,0,0,0,0 ; Init Drive data X$WBC: DB 0FH ; Write sector buffer DB 0,0,0,0,0 ; . X$FC: DB 04H ; Format unit DB 0,0,0 ; . X$INve the data to the proper area SHLD S$CYL ; . LHLD DRV$LZ ; . SHLD S$LZ ; . LHLD DRV$RWC ; . SHLD S$RWC ; . LHLD DRV$WPC ; . SHLD S$WPC ; . LDA DRV$HEAD ; . STA S$HD ; . LDA STEP$RATE ; Get step rate MOV E,A ; Translate from table MVI D,0 ; . LXI H,S$TRAN$TBL ; . DAD D ; . DAD D ; . MOV A,M ; . STA S$STPR ; . INX H ; . MOV A,M ; . STA S$STPR+1 ; . LDA INTERLEAVE ; Set interleave STA S$INTL ; . LXI H,S$MSC ; Mode Select LXI D,S$TL: DB 0 ; . X$STEP: DB 0 ; . H$DO$SEAGATE: LXI D,FMT$START$MSG ; Show the user we're going ... CALL JUSTIFY ; . LDA INTERLEAVE ; Set interleave STA SEA$INTL ; . LXI H,SEA$RSD ; Read sense data LXI D,SEA$RSD ; . (to clear reset error) CALL DO$SCSI ; JNZ FMT$ERROR ; LXI H,SEA$FC ; Format unit LXI D,SEA$FC ; . (no data required) CALL DO$SCSI ; . JNZ FMT$ERROR ; . CALL H$DIR$CLEAN ; Clean directory area JNZ FMT$ERROR ; . JMP H$FMT$DONE ; and jump to the end routinMSD ; . CALL DO$SCSI ; . JNZ FMT$ERROR ; . LXI H,S$FC ; Format unit LXI D,S$FC ; . (no data required) CALL DO$SCSI ; . JNZ FMT$ERROR ; . CALL H$DIR$CLEAN ; Clean directory area JNZ FMT$ERROR ; . JMP H$FMT$DONE ; and jump to the end routine S$TRAN$TBL: DB 4,3 ; 3ms DB 4,1 ; 1ms DB 0,200 ; 200us DB 0,70 ; 70us DB 0,50 ; 50us S$MSC: DB 15H,0,0 ; Mode select DB 0,20H,0 ; . S$MSD: DB 0,0,0,8 ; Mode select data DB 0,0,0,0,0 ; . DB 0,2,0 ; . (Block size = e SEA$RSD: DB 03H ; Read sense data DB 0,0,0,0,0 ; SEA$FC: DB 04H ; Format unit DB 00H ; . DB 00H ; . DB 00 ; . SEA$INTL: DB 00 ; . (interleave) DB 00 ; . H$DIR$CLEAN: ; ; This routine clears the drive by reading the capacity of the unit ; and writing sectors filled with 0E5H. This clears all directory ; areas on the unit, no matter what partitioning is provided. This ; clearing is done 'track by track' by sending the data in 8Kb ; blocks. ; LXI D,CLR$DIR$MSG ; Te512 bytes) DB 80H,0 ; . S$HD: DB 0 ; . (# of heads) S$SPW: DB 16 ; . (Step pulse width = 16us) S$STPR: DB 0,0 ; . (Step rate) S$CYL: DB 0,0 ; . (# of cylinders) S$RWC: DB 0,0 ; . (RWC starting cylinder) S$WPC: DB 0,0 ; . (WPC starting cylinder) S$LZ: DB 0,0 ; . (Landing zone cylinder) DB 0,0,0,0,0,0 ; . S$FC: DB 04H ; Format unit DB 0 ; . DB 0 ; . DB 0 ; . S$INTL: DB 0 ; . (interleave) DB 0 ; . H$DO$OWL: MVI A,7 ; Set step rate to '7' for OWL STA STEP$RATE ; (all 'em we're clearing his drive CALL JUSTIFY ; . LXI H,RD$CAP$CMD ; Read the capacity of the drive LXI D,RD$CAP$DATA ; . CALL DO$SCSI ; . RNZ ; Return with status if error ; Convert from blocks to tracks by multiplying HL and ; A by 16 and throwing away the low 8 bits (which is ; the same as dividing by 16, only this is easier). ; HL reg pair C A reg ORA A ; ---------------- - -------- LDA RD$CAP$DATA+1 ; ---------------- - -------- MOV H,A ; xxxxFEDC-------- - -nd fall through to H$DO$XEBEC ... H$DO$XEBEC: LXI D,FMT$START$MSG ; Show the user we're going ... CALL JUSTIFY ; . LXI D,X$ID ; Where to put the fmt data LXI H,DRV$CYLS ; . LXI B,7 ; . DB 0EDH,0B0H ; . (LDIR) LDA STEP$RATE ; Get step rate ORI 20H ; . (set sector buffer bit) STA X$STEP ; . LDA INTERLEAVE ; Set interleave STA X$INTL ; . LXI H,X$IC ; Init Drive Characteristics LXI D,X$ID ; . CALL DO$SCSI ; . JNZ FMT$ERROR ; . LXI H,X$WBC ; Write Sector Buffer ------- LDA RD$CAP$DATA+2 ; xxxxFEDC-------- - -------- MOV L,A ; xxxxFEDCBA987654 - -------- LDA RD$CAP$DATA+3 ; xxxxFEDCBA987654 - 3210xxxx ADD A ; xxxxFEDCBA987654 3 210xxxx- DB 0EDH,06AH ; xxxFEDCBA9876543 - 210xxxx- ADD A ; xxxFEDCBA9876543 2 10xxxx-- DB 0EDH,06AH ; xxFEDCBA98765432 - 10xxxx-- ADD A ; xxFEDCBA98765432 1 0xxxx--- DB 0EDH,06AH ; xFEDCBA987654321 - 0xxxx--- ADD A ; xFEDCBA987654321 0 xxxx---- DB 0EDH,06AH ; FEDCBA9876543210 - xxxx---- ; HL now contains the n umber of 'tracks' CLR$NEXT$TRACK: DCX H ; Decrement the track count SHLD CUR$TRACK ; Save current track XRA A ; Convert back to 20-bit ; A reg C HL reg pair ; -------- - FEDCBA9876543210 DAD H ; -------- F EDCBA9876543210- RAL ; -------F - EDCBA9876543210- DAD H ; -------F E DCBA9876543210-- RAL ; ------FE - DCBA9876543210-- DAD H ; ------FE D CBA9876543210--- RAL ; -----FED - CBA9876543210--- DAD H ; -----FED C BA9876543210---- RAL ; -re the converted number) ; ; Modifies: none ; PUSH PSW ; Save original number RRC ; Get high nybble RRC ; . RRC ; . RRC ; . ANI 0FH ; . CALL A$TO$HEX ; Convert to hex MOV L,A ; Save in L register POP PSW ; Get original number back PUSH PSW ; Save again for later ANI 0FH ; Get low nybble CALL A$TO$HEX ; Convert to hex MOV H,A ; Save in H register POP PSW ; Get original number back RET ; and return A$TO$HEX: ; Convert A to a hex digit CPI 0AH ; If 0-9---FEDC - BA9876543210---- XCHG ; Save block # in write command LXI H,WRITE$BLK$CMD ; . INX H ; . MOV M,A ; Move high byte INX H ; . MOV M,D ; Move middle byte INX H ; . MOV M,E ; Move low byte LXI H,WRITE$BLK$CMD ; Write 8K block to disk at this blk LXI D,E5BUF ; . CALL DO$SCSI ; . RNZ ; Return with status if error LHLD CUR$TRACK ; Get track number back MOV A,H ; See if we're done ORA L ; . JNZ CLR$NEXT$TRACK ; Non-zero means more to do . . . XRA A ;, we don't need to JM A$TO$HEX$2 ; . add any offset ADI 07H ; Offset for A-F A$TO$HEX$2: ; . ADI 30H ; ASCII bias RET ; and return BIN$TO$SCSI: ; Converts binary 0-7 to SCSI address ; ; Entry: ; A = number to convert (0-7) ; ; Exit: ; A = converted SCSI address [00H = error] ; ; Modifies: B ; ANI 07H ; Mask out all but addrs 0-7 INR A ; Bump A to shift at least one MOV B,A ; . bit and move to B register XRA A ; Clear A register STC ; Set carry for shift Set A-OK status RET RD$CAP$CMD: DB 025H,0 ; Read capacity command DB 0,0,0,0,0,0,0,0 ; . RD$CAP$DATA: DB 0,0,0,0,0,0,0,0 ; Read capacity data area WRITE$BLK$CMD: DB 00AH,0 ; Write data cmd DB 0,0,16,0 ; . CUR$TRACK: DW 0 ; Current track data FMT$ERROR: LXI D,NO$RESPONSE ; CPI 0FFH ; Timeout error? JZ FMT$TIMEOUT ; Yes, print timeout msg MOV A,M ; No, Get last command CALL A$TO$HL$HEX ; Convert to hex SHLD ERR$CMD ; Save in output line LXI H,FMT$ERR$CMD ; Ge BIN$NEXT$BIT: RAL ; Shift left one bit DCR B ; Decrement count JNZ BIN$NEXT$BIT ; Zero means all done RET CENTER$OUTPUT: ; ; [DC.20] ; ; Automatically centers the output line(s) based on the line width ; stored in SCREEN$WIDTH. Each line is delimited with CR+LF. This ; routine will return to the caller when it encounters the string ; terminator, '$'. Any additional LF characters after a CR+LF pair ; will be passed through. ; ; Entry: ; DE = Pointer to output string(s), termint status from controller LXI D,FMT$ERR$DATA ; . CALL DO$SCSI ; . LDAX D ; Get error code CALL A$TO$HL$HEX ; Convert to hex SHLD ERR$CODE LXI D,FORMAT$ERROR ; Print error message FMT$TIMEOUT: CALL JUSTIFY ; . MVI A,0FFH ; Set error status JMP H$FMT$DONE ; and return FMT$ERR$CMD DB 3,0,0 ; Request sense DB 0,4,0 FMT$ERR$DATA DB 0,0,0,0 ; Sense data H$FMT$DONE: ORA A ; Set status LXI D,FMT$ERR$MSG ; Print "Errors occurred during PUSH PSW ; . formatting" if NOT ZERO Cated with CR+LF. ; ; Exit: ; The output string(s) are sent to the screen ; ; Modifies: DE ; PUSH PSW ; Save registers PUSH B ; . PUSH H ; . CENTER$NEXT$LN: CALL GET$STRLEN ; Get length to next CR or '$' in B MOV A,B ; Check for zero length ORA A ; . JZ NEXT$DELIM ; If so, output the CR, LF, etc. LDA SCREEN$WIDTH ; Compute offset needed to center line STC ; . SBB B ; . (if there are too many chrs, just JC NO$BLANKS ; . print the line as is . . . ) ANI 0FEh ; Clear lNZ JUSTIFY ; POP PSW ; LXI D,FMT$OK$MSG ; Print "Format complete. No CZ JUSTIFY ; . errors" if ZERO RET ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Library routines . . . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * A$TO$HL$HEX: ; ; [DC.27] ; ; Converts the number in A to the hex digits in HL ; ; Entry: ; A = number to convert ; ; Exit: ; HL = the hex equivalent of the number (L=high, H=low) ; (use shld to stoeast significant bit RRC ; . and rotate to divide by two MVI C,' ' ; Output enough blanks to center line CNZ CON$CHR$AC ; . (only if count is non-zero) NO$BLANKS: MOV A,M ; Save CR for later MVI M,'$' ; Plug position with '$' for cp/m CALL CON$MSG ; . print string function MOV M,A ; Restore saved CR NEXT$DELIM: MOV A,M ; Get character CPI CR ; Print it if CR JZ OUTPUT$DELIM ; . CPI LF ; Print it if LF JZ OUTPUT$DELIM ; . CPI EOS ; Stop processing if EOS ('$') JZ C ENTER$DONE ; . XCHG ; Put new pointer in DE JMP CENTER$NEXT$LN ; Go & do the next line OUTPUT$DELIM: CALL CON$CHR ; Output delimiter CPI LF ; Wait 10ms if we have a line feed CZ WAIT ; . INX H ; Point to next chr JMP NEXT$DELIM ; and check that one, also CENTER$DONE: POP H ; Restore registers POP B ; . POP PSW ; . RET ; and return CLEAR$SCREEN: ; ; [E1.28] ; ; This routine clears the screen by calling DO$CRLF 26 times. ; ; Entry: ; None ; ; Exit: ; The screeup. ; ; Entry: ; none ; ; Exit: ; CR + LF is sent to the screen. ; ; Modifies: ; none ; PUSH PSW ; Save AF MVI A,0Dh ; Send carriage return CALL CON$CHR ; . MVI A,0Ah ; and line feed CALL CON$CHR ; . LDA SLOW$TERM ; Check slow flag ORA A ; . CNZ WAIT ; wait for the s-l-o-w terminals POP PSW ; recover original AF RET ; and return FILL$BLOCK: ; ; [E1.29] ; ; Fills the buffer pointed to by DE with the character in C for ; a length of B bytes (max 256). ; ; Enn is cleared ; ; Modifies: ; None ; PUSH PSW ; Save just in case MVI A,26 ; 26 CRLF's C$NEXT$LINE: CALL DO$CRLF ; Next line DCR A ; Done? JNZ C$NEXT$LINE ; Nope. POP PSW ; Restore original AF RET ; and return CON$CHR: ; ; [DC.20] ; ; This routine sends the character in the "A" register to the console ; through the BDOS conout call. ; ; Entry: ; A = character to send ; ; Exit: ; character is sent to the console ; ; Modifies: ; None ; PUSH PSW PUSH B PUSH Dtry: ; B = Length to fill ; C = Character to fill with ; DE = Start of buffer ; ; Exit: ; Buffer filled ; ; Modifies: ; Only buffer area affected ; PUSH B ; Save registers PUSH D ; . XCHG ; Set up M register use F$NEXT: MOV M,C ; Stuff a char INX H ; Bump pointer DCR B ; Bounce counter JNZ F$NEXT ; Do it again if not done XCHG ; We are done. restore old HL POP D ; Restore other registers POP B ; . RET ; and return GET$BIOS$VERS: ; ; [DC.20] ; ; Get  PUSH H MVI C,2 MOV E,A CALL BDOS POP H POP D POP B POP PSW RET CON$CHR$AC: ; ; [DC.20] ; ; This routine sends the character in the C register to the console ; the number of times in the A register. ; ; Entry: ; A = Number of times to send character ; C = Character to send ; ; Exit: ; Same ; ; Modifies: ; None ; PUSH PSW ; Save all registers PUSH B ; . PUSH D ; . PUSH H ; . MOV B,A ; Move data to accomodate CP/M MOV A,C ; . NEXT$CHR$OUT: CALL CONbios version -- Copies the current BIOS jump tables (starting ; at warm boot) to a local area for ease of utility access. If the ; BIOS is version 2.0 or greater, the secondary jump table is copied ; also. ; ; Entry: ; none ; ; Exit: ; Z = bios 1.0 - 1.4 (floppy only bios) ; NZ = bios 2.0 or greater (floppy & fixed disk bios) ; ; Modifies: All registers ; LHLD 1 ; Get start of bios jump table LXI D,LB$BIOS$TBL ; Move bios to local storage MVI B,LB$LEN ; . (length of bios area) CALL $CHR ; Send 1 chr DCR B ; Decrement counter JNZ NEXT$CHR$OUT ; Done? POP H ; Restore all registers POP D ; . POP B ; . POP PSW ; . RET ; and return CONIN$NE$XC: ; ; [E1.28] ; ; Console input, no echo, exit on ctrl-c ; ; Entry: ; none ; ; Exit: ; A = character from console, except for ctrl-c, which causes ; an immediate jump to ALL$DONE ; ; Modifies: ; A ; CALL LB$CONIN CPI CTRLC JZ ALL$DONE RET CON$MSG: ; ; [DC.20] ; ; Console message ; ; EMOVE$BLOCK ; . (move routine) MVI A,0 ; Test CP/M version CALL LB$GETNXT ; Get next jump table STA BIOS$VERSION ; Save bios version INX H ; See if HL is 0FFFFh MOV A,H ; . ORA L ; . RZ ; If so, then old version DCX H ; Fix HL as it has the table addr LXI D,LB$XTBL ; Move extra table to local storage MVI B,LB$XLEN ; . (length of extra table) CALL MOVE$BLOCK ; . (move routine) MVI A,0FFH ; Set NZ to indicate bios ORA A ; ... version 2.1+ RET ; ... and return. GETntry: ; DE = pointer to message string, terminated with '$' ; ; Exit: ; message printed on console ; ; Modifies: A, BC ; PUSH PSW ; Save registers PUSH B ; . PUSH D ; . PUSH H ; . MVI C,9 ; BDOS print string command CALL BDOS ; . POP H ; Restore registers POP D ; . POP B ; . POP PSW ; . RET ; and return DO$CRLF: ; ; [DC.27] ; ; This routine sends a carriage return and a line feed to the terminal, ; and then waits 'SLOW$TERM' ms for a slow terminal to catch $HL$PTR: ; ; [DC.20] ; ; Gets the pointer pointed to by HL and puts it in HL ; ; Entry: ; HL = pointer to put in HL ; ; Exit: ; HL = pointer ; ; Modifies: none ; PUSH PSW ; Save A register MOV A,M ; Get low byte of pointer INX H ; . MOV H,M ; Get high byte of pointer MOV L,A ; Pointer is now together POP PSW ; Restore A register RET ; and return GET$STRLEN ; Searches the string pointed to by HL and returns the string length ; to the next carriage return. The length  is returned in B. PUSH D ; Save start of string MVI B,0 ; Clear counter TRY$NEXT$CHR: LDAX D ; Get character CPI CR ; Is it CR? JZ EOS$FOUND ; . CPI '$' ; Is it '$'? JZ EOS$FOUND ; . INR B ; No -- increment count and INX D ; . point to the next character JMP TRY$NEXT$CHR ; . EOS$FOUND: POP H ; CR or '$' found, recall orig ptr XCHG ; DE=orginial, HL=current RET ; and return GO$TABLE: ; ; [E2.05] ; ; Jump to a routine based on a table of pointers ; ; Entry:R,LF pairs to separate paragraphs. ; ; NOTE: To insure proper operation, the LF character should only follow ; a CR character or another LF character, as the CR character is used to ; flush the current line without justification. ; ; Two entry points are provided: ; JUSTIFY Justify output, flush right ; JUSTIFY$RAGGED Justify output, ragged right ; ; As of E6.11, the flush right routine was not installed, so either ; entry point will provide the same results. ; ; Entry: ; DE = pointer to lin ; A = index into table ; HL = table base address ; ; Exit: ; Routine at (A*2)+HL is executed ; ; Modifies: ; B, HL ; LXI B,2 ; Compute offset to table of routines CALL INDEX$TABLE ; . CALL GET$HL$PTR ; . PCHL ; Jump to proper routine INDEX$TABLE: ; ; [E1.30] ; ; Computes offset to table given base address, entry length, and entry ; requested. ; ; Entry: ; A = entry # ; BC = table entry length ; HL = base address ; ; Exit: ; HL = address to entry ; ; Modifies: ; A,e(s) to output ; ; Exit: ; The data is sent to the screen. ; ; Modifies: All registers ; MVI A,80H ; Set flush right mode DB JR,E$JUSTIFY-$-1; Jump to routine entry JUSTIFY$RAGGED: MVI A,00H ; Set ragged right mode E$JUSTIFY: STA J$MODE ; Save mode byte MVI A,'$' ; Mark start of buffer STA OUTBUF-1 ; . XCHG ; DE is usually print source J$NEXT$LINE: XRA A ; Clear character counter STA BLANK$LEN ; . MOV B,A ; . LXI D,OUTBUF ; Set up buffer pointer J$CHECK$CHR: MOV A,M BC, HL ; ORA A ; Set up flags for first check I$TBL$ADD: RZ ; If A=0, we're done DAD B ; Otherwise add length to base, DCR A ; . decrement counter, JMP I$TBL$ADD ; . and check again. IS$IT$OK: ; ; [E1.28] ; ; Check the character in A against the list of "OK" chrs pointed ; to by HL ; ; Entry: ; A = character to check ; HL = pointer to list of "OK" characters ; ; Exit: ; A = original character if ok, 0ffh if not in list ; B = position of character in list ; ; Modifi ; Get character CPI CR ; CR? DB JRNZ,J$NO$R-$-1 ; . CALL J$FLUSH$LINE ; . Flush output line, MVI A,CR ; . Output CR, CALL CON$CHR ; . . INX H ; . bump ptr & check next DB JR,J$NEXT$LINE-$-1 and 255 J$NO$R: CPI LF ; LF? DB JRNZ,J$NO$L-$-1 ; . CALL CON$CHR ; . Output LF, INX H ; . bump ptr & check next DB JR,J$CHECK$CHR-$-1 and 255 J$NO$L: CPI FF ; FF? DB JRNZ,J$NO$F-$-1 ; . CALL CLEAR$SCREEN ; . Clear screen, INX H ; . bump ptr & check next DB JR,J$CHECKes: ; BC ; PUSH H ; Save original "OK" pointer MOV C,A ; Save chr to check against MVI B,0 ; Clear counter CPI ESC ; If chr is JZ I$CHR$OK ; . then automatically ok INR B ; . otherwise start counting at 1 I$CHK$NEXT: MOV A,M ; Get chr to check against ORA A ; End of table? JNZ I$NOT$EOT ; No, check chr DCR A ; Decrement to get 0ffh MOV B,A ; Stuff for later move JMP I$CHR$OK ; And exit I$NOT$EOT: CMP C ; Chrs match? JZ I$CHR$OK ; . Yes, return INX H ; . N$CHR-$-1 and 255 J$NO$F: CPI '$' ; End of string? DB JRNZ,J$NO$S-$-1 ; . CALL J$FLUSH$LINE ; . Flush output line, and RET ; . return to caller. J$NO$S: STAX D ; Not a special chr, save in buffer CPI ' ' ; Blank? DB JRNZ,J$NO$B-$-1 ; . No, don't save position SHLD BLANK$POS ; Save position for later XCHG ; and save corresponding position SHLD OUTBUF$BLANK ; . of the blank we just saved XCHG ; . in the output buffer MOV A,B ; . STA BLANK$LEN ; Save current length ao, bump pointer INR B ; . . and bump counter JMP I$CHK$NEXT ; . . and check next chr I$CHR$OK: MOV A,B ; Set status based on ORA A ; . position counter MOV A,C ; Get user chr back I$DONE: POP H ; and original "OK" pointer RET ; and return JUSTIFY: ; ; [E6.11] ; ; This routine will send a data stream to the console, with each line ; justified based on the SCREEN$WIDTH value. The stream must terminate ; with the CP/M end of string character ($) and may contain imbedded ; Clso J$NO$B: INR B ; Increment counter INX H ; . and input pointer INX D ; . and output pointer LDA SCREEN$WIDTH ; Compare counter against screen width SUB B ; . JP J$CHECK$CHR ; And continue checking if not past end ; ; Screen width exceeded, send this line to the screen. ; LHLD OUTBUF$BLANK ; Get pos of last blank in output buf MVI M,'$' ; and plug with eos ('$') LDA J$MODE ; Justify right edge only if the ORA A ; . right-justify flag is non-zero CM J$ADD$BLANKS ; . CA LL J$SEND$BUFFER ; Output the line to the screen CALL DO$CRLF ; and a CR / LF LHLD BLANK$POS ; Get pointer to where we left off J$SKIP$BLANKS: INX H ; Bump pointer past blank(s) MOV A,M ; . CPI ' ' ; . DB JRZ,J$SKIP$BLANKS-$-1 and 255 JMP J$NEXT$LINE ; and check next segment J$FLUSH$LINE: ; Flush line when CR or EOS encountered MVI A,'$' ; Plug current position with EOS ($) STAX D ; . CALL J$SEND$BUFFER ; Send this line of data RET ; and return J$ADD$BLANKS: RET ; ISP ; No -- display & get chr from bios PUSH H ; Yes, save oklist pointer LHLD CMD$LINE$PTR ; and get chr from command line DCR A ; . Reduce count of chrs by one STA CMD$LINE$CHRS ; . . MOV A,M ; . Get command line character INX H ; . Increment pointer SHLD CMD$LINE$PTR ; . . POP H ; . Restore oklist pointer CPI ',' ; Change ',' to CR JNZ P$NOT$COMMA ; . MVI A,CR ; . P$NOT$COMMA: CPI '.' ; Change '.' to ESC JNZ P$NOT$DOT ; . MVI A,ESC ; . P$NOT$DOT: CPI '_' ;At a later time, this routine will ; justify the right margin by inserting ; extra blanks in the output line. J$SEND$BUFFER: MOV A,B ; If line to output is of zero length, ORA A ; . then don't output the line. RZ ; . LXI D,OUTBUF ; Get address of output buffer CALL CON$MSG ; and call our print message routine RET ; return J$MODE DB 0 ; Current right justify mode BLANK$POS DW 0 ; Last blank on this line OUTBUF$BLANK DW 0 ; Last blank in the output buffer BLANK$LEN DB 0 Underline means prompt & get chr JZ P$DISP ; . from bios anyway (user input) CALL TO$UPPER ; Convert the chr to upper case CALL IS$IT$OK ; Check the chr against the ok list RP ; If ok, return XRA A ; Otherwise, cancel the cmd line STA CMD$LINE$CHRS ; . buffer and fall through to p$disp P$DISP: PUSH H ; Save pointer to ok-chrs CALL JUSTIFY ; and call justify routine POP H ; . P$TRY$AGAIN: CALL CONIN$NE$XC ; Console input, no echo, except ^C CALL TO$UPPER ; Convert the chr to  ; Length of line to the blank ; NOTE: OUTBUF is defined to be after the stack and before the heap. ; end of justify$output data area MOVE$BLOCK: ; ; [DC.20] ; ; Move a block of memory (up to 256 bytes) ; ; Entry: ; HL = source ; DE = destination ; B = count ; Exit: ; data moved ; ; Modifies: ; none ; PUSH PSW ; Save registers PUSH B ; . PUSH D ; . PUSH H ; . M$NEXT$CHR: MOV A,M ; Get next byte, STAX D ; Save in destination. INX H ; Increment pointer to soupper case CALL IS$IT$OK ; If the character is not "OK" JM P$TRY$AGAIN ; . get another CNZ CON$CHR ; . otherwise display it PUSH PSW ; Check mode flag in case we need LDA PROMPT$MODE ; . to send a CR+LF after the ANI 01H ; . user's input CNZ DO$CRLF ; . POP PSW ; . RET ; and return PROMPT$MODE: DB 0 ; Prompt mode flag PROMPT$DECIMAL: ; ; [E6.11] ; ; Prompt the user for a decimal input of up to 5 digits. The Z flag ; indicates the termination character: Z = ESC key, rurce. INX D ; Increment pointer to dest. DCR B ; Decrement counter. JNZ M$NEXT$CHR ; Loop back if we're not done. POP H ; Restore registers POP D ; . POP B ; . POP PSW ; . RET ; No more to do -- return. PROMPT: ; ; [E1.30] ; ; Prompt the user or the command line for input. ; ; Two entry points are provided: ; PROMPT standard entry, CRLF after chr from user ; PROMPT$NOLF special entry, No CRLF after chr from user ; ; Entry: ; DE = pointer to prompt string ; HL = poiesults may not ; be valid; NZ = RETURN key, results valid. ; ; Entry: ; DE = pointer to prompt string ; ; Exit: ; DE = Value entered by the user, 0-0FFFFH ; A = Low byte of value entered by the user, 0-0FFH ; ; Z = ESC key pressed, ignore results ; NZ = RETURN key pressed, results valid ; ; Modifies: ; A, PSW ; MVI A,01H ; Set CRLF after entry DB JR,E$PR$DEC-$-1 ; Jump to entry point PROMPT$DEC$NOLF: MVI A,00H ; Set no CRLF after entry E$PR$DEC: STA PROMPT$D$MODE ; Save prompt mnter to list of valid chars (terminated with 00H) ; ; Exit: ; A = char from the user ; B = position of this character (0, 1, 2, ... n) ; ; Z = char was the escape key ; NZ = char was not the escape key ; ; Modifies: ; A, BC ; MVI A,01H ; Set CRLF after chr JMP E$PROMPT ; Jump to entry point PROMPT$NOLF: MVI A,00H ; Set no CRLF after chr E$PROMPT: STA PROMPT$MODE ; Save prompt mode flag LDA CMD$LINE$CHRS ; Are there any characters left from ORA A ; . the command line? JZ P$Dode flag PUSH H ; Save original HL reigster PUSH B ; . and the BC register, too. XRA A ; Clear the current digit pointer MOV C,A ; Setup count in C register LXI H,SCRATCH ; Initialize the scratch pointer NEXT$DIGIT: PUSH B ; Save digit counter PUSH H ; . and string pointer LXI H,DEC$INPUT$OKC ; Get current 'ok' chrs CALL PROMPT$NOLF ; Prompt for digit POP H ; Get string pointer back POP B ; . along with digit counter DB JRZ,ESCRTN-$-1 ; Return if ESC key hit CPI CR ; C heck for return JZ CONVERT$STRING ; Convert string to decimal, if so CPI BS ; Check for backspace JZ BACKUP$DIGIT ; Back up 1 digit, if we can MOV B,A ; Save character for a moment MOV A,C ; Check digit count CPI 5 ; . JC ADD$DIGIT ; 5 digits or less, add to string LXI D,BLOT ; More than 5 digits, blot this one CALL CON$MSG ; . JMP DE$FOR$NEXT ; . ADD$DIGIT: INR C ; Otherwise bump digit count MOV M,B ; Save digit INX H ; Bump digit pointer JMP DE$FOR$NEXT ; Setup DE fERTED: MOV A,B ; Move converted addr to A RET STR$COMP: ; ; [E1.18] ; ; Compare two strings ; ; Entry: ; HL = source ; DE = destination ; B = count ; Exit: ; Z = two strings equal ; M = source < dest ; P = source >= dest ; ; Modifies: all ; PUSH D PUSH H NEXT$COMPARE: MOV C,M INX H LDAX D INX D CMP C JNZ NOT$EQUAL DCR B JNZ NEXT$COMPARE NOT$EQUAL: POP H POP D RET STR$SWAP: ; Swap two strings ; ; [E1.18] ; ; Entry: ; HL = source ; DE =or next prompt BACKUP$DIGIT: MVI A,' ' ; Bump forward to clear digit CALL CON$CHR ; . XRA A ; Are we at the beginning? CMP C ; . JZ DE$FOR$NEXT ; Yes, don't backup MVI A,BS ; Backup to correct position CALL CON$CHR ; . DCX H ; Backup pointer 1 chr DCR C ; Push count 1 back, also DE$FOR$NEXT: LXI D,DEC$INPUT$MSG ; Setup for next prompt JMP NEXT$DIGIT ; . CONVERT$STRING: MVI M,0 ; Mark end of string LXI H,SCRATCH ; Get beginning of string CALL STR$TO$DE ; Convert st destination ; B = count ; Exit: ; data moved ; ; Modifies: all ; MOV C,M LDAX D MOV M,A MOV A,C STAX D INX H INX D DCR B JNZ STR$SWAP RET STR$TO$DE: ; ; [E5.26] ; ; Converts the string pointed to by HL to a number in the DE reg. ; The conversion will continue until the first non-numeric chr ; found. ; ; NOTE: a test for register overflow is not made. If HL points ; to a string whose numerical value is greater than 65535 (64K), ; inaccurate results will occur. ; ring to DE register MVI A,0FFH ; Insure NZ flag ORA A ; . MOV A,E ; Move low byte to A PUSH PSW ; Check mode flag to see if a LDA PROMPT$D$MODE ; . CR+LF should be sent after ORA A ; . the user's input CNZ DO$CRLF ; . POP PSW ; . ESCRTN: POP B ; Get old BC register back POP H ; . and original HL, as well. RET ; and return with result in HL & A DEC$INPUT$MSG: DB '$' ; Decimal input message DEC$INPUT$OKC: DB '0123456789' ; Decimal input 'ok' chrs DB BS,CR,0 ; . B ; Entry: ; HL = ptr to string ; ; Exit: ; DE = value of string in HL ; HL = next character to process ; ; Modifies: ; DE, HL ; ; PUSH PSW ; We need the A register PUSH B ; . and the BC register LXI D,0 ; Clear totals STN$NEXT$CHR: PUSH D ; Save results so far . . . POP B ; . MOV A,M ; Get chr CPI '0' ; Less than '0'? DB JRC,NO$DIG-$-1 ; Yes, finished CPI '9'+1 ; Greater than '9' DB JRNC,NO$DIG-$-1 ; Yes, finished SUI '0' ; No, convert to 0-9 MOV E,A ; Save thiLOT: DB BS,' ',BS,'$' ; Blot out digit SCRATCH: DB ' ',0 ; Max 5 digits PROMPT$D$MODE: DB 0 ; CRLF flag RET$TO$CONT: ; ; [E2.19] ; ; Prompts and waits for the RETURN key to be pressed. ; ; Entry: ; none ; ; Exit: ; Display message and wait for a RETURN key. ; ; Modifies: ; all ; LXI D,RTC$MSG ; Press RETURN to continue ... CALL CENTER$OUTPUT ; . LXI D,NO$MSG ; . LXI H,RTC$OKC ; . CALL PROMPT ; . [ CR ] RET RTC$MSG: DB 'Press the RETURN key to continue ... ' NO$MSGs digit MVI D,0 ; . PUSH H ; Save ptr to input string LXI H,0 ; Multiply previous by 10 DAD B ; x1 DAD H ; x2 DAD H ; x4 DAD B ; x5 DAD H ; x10 DAD D ; Add in new digit XCHG ; Save results back to DE POP H ; Get input string ptr back INX H ; Bump to next chr JMP STN$NEXT$CHR ; Go back for another digit NO$DIG: POP B ; Get old BC reg back POP PSW ; . and old A reg as well. RET ; Return with results in DE. TO$UPPER: ; ; [E1.08] ; ; Convert the charac: DB '$' RTC$OKC: DB CR,0 ; end of RET$TO$CONT routine SCSI$TO$BIN: ; ; [DC.27] ; ; Converts SCSI address to binary 0-7 ; ; Entry: ; A = SCSI address to convert ; ; Exit: ; A = converted address (0-7) [0FFH = error] ; ; Modifies: B ; MVI B,0FFH ; Set up B register for possible error ORA A ; If A=0, error JZ SCSI$CONVERTED ; . SCSI$NEXT$BIT: INR B ; Increment count RRC ; Shift address right 1 bit JNC SCSI$NEXT$BIT ; Bit shifted to cary means we're done SCSI$CONVter in A to upper case. ; ; Entry: ; A = character to convert ; ; Exit: ; A = upper case character (if alpha) ; ; Modifies: ; A ; CPI 'z'+1 ; Convert to upper case JP UPPER$ALREADY ; . CPI 'a' ; . JM UPPER$ALREADY ; . ANI 5FH ; . UPPER$ALREADY: RET ; and return WAIT: ; Wait A ms ; ; Entry: ; milliseconds in A ; ; Exit: ; time waited ; ; Modifies: A ; PUSH PSW MVI A,221 WAIT$2: DCR A JNZ WAIT$2 POP PSW DCR A JNZ WAIT RET * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * * Data area . . . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ; Replicated BIOS to make direct calls easier . . . LB$BIOS$TBL: LB$WBOOT DS 3 ; Warm boot LB$CONST DS 3 ; Console status LB$CONIN DS 3 ; Console input LB$CONOUT DS 3 ; Console output LB$LISTOUT DS 3 ; List output LB$PUNCH DS 3 ; Punch output LB$READER DS 3 ; Reader input LB$HOMDSK DS 3 ; Home disk (move to track 00) LB$SELDSK DS 3 ; Select disk drive L,CR,LF DB 'SCSI address bit code ' S$ADDR$CODE DB 'xx (hex).',CR,LF DB '(See your hard disk controller ' DB 'manual for details).',CR,LF,LF,'$' AGAIN$MSG DB CR,LF,'Do another (Y/N)? ','$' AGAIN$OKC DB 'NY',CR,0 NEW$CTRL DB CR,LF,'Cannot format -- you have an ' DB 'unknown or unsupported hard disk ' DB 'controller.',CR,LF,LF,'$' OLDSP: DS 2 ; old stack pointer STACK: EQU OLDSP+64 ; 31-level stack INBUF: EQU STACK+1 ; Command line input buffer INBUFL: EQU 0 ; Input buffer lB$SETTRK DS 3 ; Select track number LB$SETSEC DS 3 ; Select sector number LB$SETDMA DS 3 ; Set DMA address LB$DSKREAD DS 3 ; Disk read LB$DSKWRITE DS 3 ; Disk write LB$LISTST DS 3 ; List status LB$SECTRN DS 3 ; Sector translate routine ; AMPRO-specific BIOS calls LB$GETNXT DS 3 ; Get bios ver & next tbl address LB$GETEDSK DS 3 ; Get pointer to E-disk storage LB$IOINIT DS 3 ; Set new I/O parameters LB$SCSIDRV DS 3 ; SCSI direct driver LB$LEN EQU $-LB$WBOOT ; Length of bios table LB$XTBL: ; 'ength OBPLUG: EQU INBUF+INBUFL ; Start of outbuf ('$') OUTBUF: EQU OBPLUG+1 ; Output buffer OBUFL: EQU PWIDTH ; Output buffer length HEAP: EQU OUTBUF+OBUFL ; Next available area for data E5BUF: EQU HEAP END rmat -- you have an ' DB 'unknown or unsupported hard disk ' DB 'controller.',CR,LF,LF,'$' OLDSP: DS 2 ; old stack pointer STACK: EQU OLDSP+64 ; 31-level stack INBUF: EQU STACK+1 ; Command line input buffer INBUFL: EQU 0 ; Input buffer lExtra' table definitions ... LB$SWAP$DRV DS 3 ; Swap two logical drives LB$GET$WDP DS 3 ; Set/get win drive parameters LB$PHYTAB DS 3 ; Set/get phytab access LB$GET$LDTE DS 3 ; Get logical device table entry LB$RESERVED DS 3 ; Reserved entry LB$XLEN EQU $-LB$XTBL ; Length of extra table BIOS$VERSION DB 0 ; Bios version number YOU$ARE$ABT DB 'You are about to format the following ' DB 'drive with an interleave of (' INTL$ECHO DB '1):',CR,LF,LF,'$' RTF$MSG DB CR,LF DB 'Press to format, ' DB ' to start over, ' DB ' to quit: ','$' RTF$OKC DB CR,0 FMT$START$MSG DB CR,LF,LF,'Formatting . . . ','$' CLR$DIR$MSG: DB CR,'Setting drive data to 0E5H . . . ','$' FMT$OK$MSG DB CR,'Drive formatted -- no errors. ' DB 'Be sure to use FINDBAD on each of the CP/M ' DB 'letters listed above to find any bad sectors ' DB 'in the drive you just formatted.',CR,LF DB 'Example: FINDBAD F:; FINDBAD G:',CR,LF,LF,'$' FMT$ERR$MSG DB 'Drive NOT FORMATTED due to * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * HInit.asm Version 1 * * * * Copyright (C) 1984,1985 Ampro Computers, Inc. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ; Assemble with asm.com or equivalent. ; All Z80 opcodes are defined with DB or DW statements. ; Revision history: ; ; Ver Date Who Description ; --- ----- --- -------------------------------------------- ; 1.2 EA.22 RJB Changed space algori error(s).' DB CR,LF,LF,'$' NO$RESPONSE DB CR,'FORMAT ERROR: your controller ' DB 'is not responding. Possible causes:',CR,LF,LF DB TAB,'Controller set to wrong SCSI address',CR,LF DB TAB,'Controller malfunctioning or not connected',CR,LF DB TAB,'SCSI host adapter malfunctioning',CR,LF DB TAB,'Cables (SCSI to controller or controller ' DB 'to drive) are bad',CR,LF,LF,'$' FORMAT$ERROR DB CR,'FORMAT ERROR: Error code ' ERR$CODE DB 'xx (hex), SCSI command ' ERR$CMD DB 'xx (hex).'thm to be based on the ; # of tracks required to avoid allocating an ; extra block, therby overwriting the next ; partition. ; ; 1.1 E8.22 RJB Corrected calculation of lowest available ; byte when Z3ENV is available. ; ; 1.0 E7.02 RJB Hard disk init for PHYTAB, DPH, DPB, and ; drive initialization, if necessary. ; Program version, and current version date VERS EQU 12 ; Current version THIS$MONTH EQU 10 ; Today's month THIS$DAY EQU 22 ; . day THIS$YEAR EQU 85 ; .   year INT$REV EQU 99 ; Internal revision number ; TRUE and FALSE are defined here NO EQU 0 FALSE EQU 0 YES EQU NOT FALSE TRUE EQU NOT FALSE ; Operating characteristics MIN$VERSION EQU 31 ; Minimum bios version allowed CMD$LINE$OK EQU TRUE ; Allow command line input? RTN$VIA$WB EQU TRUE ; Return to CP/M via warm boot? INTERNAL EQU FALSE ; Internal (unreleased) revision? ; Include diagnostic messages? TEST EQU NO ; TEST diagnostics? ; Current screen width and outp) EOF EQU 'Z'-'@' ; Ctrl-Z (CP/M End-of-file) ESC EQU 1BH ; Ctrl-[ (Escape) EXP EQU 21H ; (Exclamation point) EOS EQU '$' ; (CP/M End-of-string) DEL EQU 7FH ; (Delete) ; bdos equates BDOS EQU 5 ; bdos entry ; * * * * * * * * ; ; The code starts here ... ; ; * * * * * * * * ORG 0100H JMP START SCREEN$WIDTH: DB SWIDTH-1 ; 1 less than actual # SLOW$TERM: DB 10 ; Delay (ms) for slow term CMD$LINE$CHRS: DB 0 ; # of cmd line chrs left CMD$LINE$PTR: DW 0 ; Ptr ut buffer width SWIDTH EQU 80 ; Screen width PWIDTH EQU 132 ; Output buffer width ; Z-80 opcode equates (reversed so we can use a DW to enter them) LDIR80 EQU 0B0EDH ; LDIR (0edh,0b0h) CPIR80 EQU 0B1EDH ; CPIR (0edh,0b1h) INIR80 EQU 0B2EDH ; INIR (0edh,0b2h) OTIR80 EQU 0B3EDH ; OTIR (0edh,0b3h) SBCD80 EQU 043EDH ; SBCD (0edh,043h) LBCD80 EQU 04BEDH ; LBCD (0edh,04bh) SDED80 EQU 053EDH ; SDED (0edh,053h) LDED80 EQU 05BEDH ; LDED (0edh,05bh) SSPD80 EQU 073EDH ; SSPD (to next cmd line chr ZCPR3$TYPE: DB 1 ; External ZCPR3 ENV ZCPR3$PTR: DW 0FE00H ; Pointer to ZCPR3 ENV NAME$MSG: DB 0DH,'Ampro ' NAME: DB 'Hard Disk System Initialization'; <-- Insert name here DB 00H,' Utility',CR,LF ; (Zero marks end-of-name) DB 'Copyright (C) 1985 AMPRO Computers, Inc.',CR,LF DB 'Version ',VERS/10+'0','.',VERS MOD 10+'0' IF INTERNAL ; Display internal revision number? DB 'x',INT$REV/10+'0',INT$REV MOD 10+'0' DB ' [',THIS$YEAR-80+'@' DB THIS$MONTH+'0'+((THIS$MONT0edh,073h) LSPD80 EQU 07BEDH ; LSPD (0edh,07bh) SIXD80 EQU 022DDH ; SIXD (0ddh,022h) LIXD80 EQU 02ADDH ; LIXD (0ddh,02ah) SIYD80 EQU 022FDH ; SIYD (0fdh,022h) LIYD80 EQU 02AFDH ; LIYD (0fdh,02ah) ; Bit SET/RESET/TEST Z-80 opcode equates (use DB to enter) ; Example: SET 7,D would be DB BIT,BSET+B7+ZD BIT EQU 0CBH ; Bit prefix BTST EQU 040H ; Bit test BRES EQU 080H ; Bit reset BSET EQU 0C0H ; Bit set B0 EQU 000H ; Bit 0 B1 EQU 008H ; Bit 1 B2 EQU 010H ; Bit 2 BH/10)*7) DB '.',THIS$DAY/10+'0',THIS$DAY MOD 10+'0',']' ENDIF DB CR,LF,LF,'$',CR HELP$MSG: DB 'Usage: HINIT scsiaddr ctrlr lun ' DB '[cyl,head,rwc,wpc, steprate] ' DB 'cpmletter partition [,cpmletter partition] ...' DB CR,LF,LF BIOS$PLUS: DB 'This program requires AMPRO bios version ' DB MIN$VERSION/10+'0','.',MIN$VERSION MOD 10+'0' DB ' or later.',CR,LF,'$' DB CR,' ',CR,EOF SIGNON$MSG: DB 'The Hard Disk System Initialization utility ' DB 'prepares your Ampro 3.1+ bios ' D3 EQU 018H ; Bit 3 B4 EQU 020H ; Bit 4 B5 EQU 028H ; Bit 5 B6 EQU 030H ; Bit 6 B7 EQU 038H ; Bit 7 ZB EQU 000H ; B Reg ZC EQU 001H ; C Reg ZD EQU 002H ; D Reg ZE EQU 003H ; E Reg ZH EQU 004H ; H Reg ZL EQU 005H ; L Reg ZM EQU 006H ; M Reg ZA EQU 007H ; A Reg ; Jump relative opcode equates (use DB to enter) ; Example: JR AGAIN would be DB JR,AGAIN-$-1 AND 255 DJNZ EQU 010H ; DJNZ addr JR EQU 018H ; JR addr JRNZ EQU 020H ; JR NZ,addr JRZ EQU 028H ; JB 'to access a hard disk unit. The following ' DB 'information is required to initialize the bios ' DB 'for your hard disk unit:',CR,LF,LF DB TAB,'Controller: SCSI address' DB TAB,'* Drive: starting RWC cylinder',CR,LF DB TAB,'Controller: type/model* ' DB TAB,'* Drive: starting WPC cylinder',CR,LF DB TAB,'Drive: logical unit number' DB TAB,'* Drive: step rate code',CR,LF DB TAB,'* Drive: number of cylinders' DB TAB,'CP/M drive letter',CR,LF DB TAB,'* Drive: number of heads' DB TAB,'R Z,addr JRNC EQU 030H ; JR NC,addr JRC EQU 038H ; JR C,addr ; IX and IY prefixes (use DB to enter) IX EQU 0DDH ; IX prefix IY EQU 0FDH ; IY prefix ; Character equates CTRLC EQU 'C'-'@' ; Ctrl-C (ETX) BS EQU 'H'-'@' ; Ctrl-H (Backspace) TAB EQU 'I'-'@' ; Ctrl-I (Tab) LF EQU 'J'-'@' ; Ctrl-J (Line feed) FF EQU 'L'-'@' ; Ctrl-L (Form feed, new pg) CR EQU 'M'-'@' ; Ctrl-M (Carriage return) NAK EQU 'U'-'@' ; Ctrl-U (Negative ack) CAN EQU 'X'-'@' ; Ctrl-X (CancelSize (Kb) of each partition',CR,LF,LF DB '(*) Only required on non-"SCSI generic" ' DB 'Hard disk controllers',CR,LF,LF,LF DB 'NOTE: Your drive''s total formatted ' DB 'capacity in Kbytes is:',CR,LF,TAB DB '(CYLS-1) * HEADS * SECTORS per TRACK * 0.5',CR,LF DB 'where SECTORS per TRACK depends on your controller ' DB 'and is usually 17 or 18.',CR,LF,LF,LF DB '$' ; ; Initialize the command line input pointer. ; START: IF CMD$LINE$OK ; If we want to allow cmd line input, LXI H,0080H  ; . Save the command line. LXI D,INBUF ; . . LXI B,128 ; . . DW LDIR80 ; . . LXI H,INBUF ; . Set up ptrs and count of chrs MOV A,M ; . . STA CMD$LINE$CHRS ; . Save count of characters, INX H ; . Bump line ptr, SHLD CMD$LINE$PTR ; . . and save ptr to cmd line ENDIF IF NOT CMD$LINE$OK ; If no command line input allowed, MVI A,0 ; . Clear the count of characters, STA CMD$LINE$CHRS ; . . LXI H,0 ; . but set the ptr up anyway. SHLD CMD$LINE$PTR ; . . ENDIF L LB$HD$INFO ; Get bottom of ALV storage CALL GET$HL$PTR ; . SHLD HDC$ALV ; . ; ; Compute the DPH base ; LHLD 1 ; Get bios base from wboot vector MVI L,80H ; Assume DPH starts at BIOS+80H SHLD DPH$BASE ; Save it. ; ; Check the version of the bios against the minimum version allowed. ; If the bios is not at least the minimum, display an error message ; and exit to the operating system. ; CHECK$B$VERS: LDA BIOS$VERSION ; Get bios version # CPI MIN$VERSION ; Check against minimum ve IF NOT RTN$VIA$WB ; If we're returning without warm boot, DW SSPD80,STACK ; . then save old stack ptr. ENDIF ; ; Stuff the stack with our pointer and get the bios JMP table. ; LXI SP,STACK ; Set up the stack pointer CALL GET$BIOS$VERS ; Copy the jmp tbl to a local area ; ; Special first-time initialization goes here . . . ; ; Find the lowest Z3 address of the RCP, IOP, FCP, or NDR. The CL, ; ENV, SH, PATH, MSG, FCB, STK, and WHL are all assumed to be either ; in low memory (belorsion DB JRNC,MINBIOS-$-1; At least minimum version . . . LXI D,BIOS$PLUS ; Not minimum, display error message CALL JUSTIFY ; . JMP ALL$DONE ; and exit. ; ; Perform any initialization particular to each version of the Ampro ; bios, if necessary. ; MINBIOS: LDA BIOS$VERSION ; Get current bios version CPI 30 ; Is it version 3? DB JRC,CHECK2-$-1 ; No, check for version 2 ; Bios version 3 init code ... CHECK2: CPI 20 ; Is it version 2? DB JRC,TOPMENU-$-1 ; No, assume versiw 0080H) or above the loadable packages. The ; address found becomes the upper limit to the hard disk allocation ; vectors. If no Z3 environment descriptor is found, the top of ; memory (0FFFFH) is returned as the upper limit to the hard disk ; allocation vectors. LHLD ZCPR3$PTR ; Get pointer to Z3 stuff ... INX H ; Bump to where 'Z3ENV' literal INX H ; . is supposed to be INX H ; . LXI D,Z3$CHECK$V ; Make sure this is a Z3 env MVI B,5 ; . descriptor CHECK$NEXT$Z3: LDAX D ; . on 1 ; Bios version 2 init code ... ; ; This is where you can jump to start the program over. ; TOPMENU: LXI SP,STACK ; Stuff SP with our stack. ; ; Display the name and signon message ; IF CMD$LINE$OK ; If cmd line input is possible LDA CMD$LINE$CHRS ; Check for any input and skip ORA A ; . the initial messages if any DB JRNZ,CLRHD-$-1 ; . chrs in the cmd line. ENDIF CALL CLEAR$SCREEN ; Clear the screen LXI D,NAME$MSG ; Display the name, version, etc. CALL CENTE CMP M ; . DB JRNZ,NO$Z3-$-1 ; Not equal, no Z3 env INX D ; . INX H ; . DB DJNZ,CHECK$NEXT$Z3-$-1 and 255 LXI B,4 ; Bump HL to RCP DAD B ; . LXI D,0FD00H ; Check the next four Z3 elements MVI B,4 ; . (RCP, IOP, FCP, and NDR). keep NEXT$PACKAGE: ; . the smallest non-zero value as PUSH H ; . the upper limit to the hard CALL GET$HL$PTR ; . disk allocation vectors. We MOV A,L ; . assume that we are using at CMP H ; . least 0FD00H and up, as that DB JRZ,SKIP0-$-1R$OUTPUT ; . LXI D,SIGNON$MSG ; and the initial message CALL JUSTIFY ; . CLRHD: LXI D,CLEAR$HDC$MSG ; Clear prior hdc assignments? LXI H,CLEAR$HDC$OKC ; . CALL PROMPT ; . JZ ALL$DONE ; . (end if the ESC key pressed). CPI 'Y' ; . CZ CLEAR$HDC ; . AGAIN: ; ; Program main loop . . . ; IF CMD$LINE$OK ; If cmd line input is possible LDA CMD$LINE$CHRS ; Check for any input and skip ORA A ; . the current status messages DB JRNZ,NOSTAT-$-1 ; . if any chrs in the cmd line. E ; . is the standard Ampro Z3 env. CALL CMP$HL$DE ; . DB JRNC,SKIP0-$-1 ; . XCHG ; . SKIP0: POP H ; . INX H ; . INX H ; . INX H ; . DB DJNZ,NEXT$PACKAGE-$-1 and 255 XCHG ; Save lowest byte found DB JR,SAVE$Z3-$-1 ; . Z3$CHECK$V: DB 'Z3ENV' ; Z3 environment check vector NO$Z3: LXI H,0FFFFH ; Indicate the last available byte SAVE$Z3: ; . is the top of memory, and fall SHLD LAST$BYTE ; . throught to check bios version. ; ; Get the base of ALV storage ; CALNDIF CALL SHOW$STAT ; Show "current drive" + partitions NOSTAT: LXI D,MAIN$MENU$MSG ; Get main choices LXI H,MAIN$MENU$OKC ; . CALL PROMPT ; . JZ ALL$DONE ; . MOV A,B ; . DCR A ; . LXI H,MAIN$MENU$EXE ; . JMP GO$TABLE ; . MAIN$MENU$EXE: DW DEFINE$CDRIVE ; Define "Current Drive" DW DEFINE$PART ; Define parition on "Current Drive" DEFINE$CDRIVE LXI D,SCSI$ADDR$MSG ; Get SCSI address LXI H,SCSI$ADDR$OKC ; . CALL PROMPT ; . JZ ALL$DONE ; . ESC -- finished STA CD$ID  ; . (Save ID in msg) MOV A,B ; . DCR A ; . CALL BIN$TO$SCSI ; . STA SCSI$ADDR ; . LXI D,HD$CTRL$MSG ; Get HD controller name LXI H,HD$CTRL$OKC ; . CALL PROMPT ; . JZ DEFINE$CDRIVE ; . ESC -- top of this section STA CD$TYPE ; . (Save type in msg) MOV A,B ; . DCR A ; . STA HD$CTRL ; . LXI D,DRV$LUN$MSG ; Get drive logical unit # LXI H,DRV$LUN$OKC ; . CALL PROMPT ; . JZ DEFINE$CDRIVE ; . ESC -- back up 1 section STA CD$LUN ; . (Save logical unit # in msg) MOV A,; . DPOK: LXI D,CPM$LTR$MSG ; Get CP/M drive letter DB JR,GET$PART-$-1 ; . GET$PART$2: ; . LXI D,CPM$LTR$MSG2 ; . GET$PART: ; . LXI H,CPM$LTR$OKC ; . CALL PROMPT ; . JZ AGAIN ; . ESC -- next physical drive MOV A,B ; . ADI 4 ; . (bump 1-11 to 5-15) STA CPM$LTR ; . ADI 'A' ; Put current CP/M drive letter STA CPM$LTR$ECHO ; . in prompt STA DRIVE$INST ; . and install message GET$PART$AGAIN: LXI D,PARTITION$MSG ; Get partition size (Kb) CALL PROMPT$DEC$NOLF ; . DB B ; . DCR A ; Convert 0-3 to SCSI lun RRC ; . RRC ; . RRC ; . ANI 1110$0000B ; . mask all but bits 7-6-5 STA DRV$LUN ; . XRA A ; Clear step rate value, in case STA STEP$RATE ; . we've got a non-Xebec HDC LDA HD$CTRL ; Check for Xebec HDC CPI NON$GENERIC ; If generic, then no need to DB JRC,DCDONE-$-1 ; . get drive info ... LXI D,DRV$CYLS$MSG ; Get # of cylinders CALL PROMPT$DECIMAL ; . JZ DEFINE$CDRIVE ; . ESC -- top of this section LXI H,DRV$CYLS ; . MOV JRZ,GET$PART$2-$-1 and 255 ; Go back for more MOV E,A ; . ORA D ; . DB JRZ,GET$PART$AGAIN-$-1 and 255 ; Zero, try again DW SDED80,PARTITION; Save partition value CALL ADD$PARTITION ; Add another drive partition JMP AGAIN ; . ALL$DONE: ; Exit the program ... IF RTN$VIA$WB ; If we're returning via warm boot, JMP LB$WBOOT ; . then JUMP! ENDIF IF NOT RTN$VIA$WB ; Otherwise, DW LSPD80,STACK ; . Get old stack ptr back. RET ; . and return to cp/m ENDIF * * * * * * * * M,D ; . INX H ; . MOV M,E ; . LXI D,DRV$HEAD$MSG ; Get # of heads CALL PROMPT$DECIMAL ; . JZ DEFINE$CDRIVE ; . ESC -- top of this section STA DRV$HEAD ; . LXI D,DRV$RWC$MSG ; Get reduced write current cylinder CALL PROMPT$DECIMAL ; . JZ DEFINE$CDRIVE ; . ESC -- top of this section LXI H,DRV$RWC ; . MOV M,D ; . INX H ; . MOV M,E ; . LXI D,DRV$WPC$MSG ; Get write precomp cylinder CALL PROMPT$DECIMAL ; . JZ DEFINE$CDRIVE ; . ESC -- top of this section LXI H,DRV$WPC* * * * * * * * * * * * * * * * * * * * * * * * Routines specific to this program . . . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * CLEAR$HDC: CALL LB$HD$INFO ; Get pointer to HD info PUSH H ; Bump to absolute start INX H ; . INX H ; . MOV E,M ; Get absolute start vector INX H ; . MOV D,M ; . POP H ; Plug in current HD info MOV M,E ; . INX H ; . MOV M,D ; . XRA A ; Clear all "driver 3" entries NXTDVR: PUSH PSW ; CALL LB$GET ; . MOV M,D ; . INX H ; . MOV M,E ; . LDA HD$CTRL ; Check for Xebec HDC CPI NON$GENERIC ; . JNZ DTC LXI D,XEB$STEP$MSG ; Get step rate mode value LXI H,XEB$STEP$OKC ; . CALL PROMPT ; . JZ DEFINE$CDRIVE ; . ESC -- top of this section MOV A,B ; . DCR A ; . STA STEP$RATE ; . JMP DCDONE ; . DTC: LXI D,DTC$STEP$MSG LXI H,DTC$STEP$OKC CALL PROMPT JZ DEFINE$CDRIVE MOV A,B DCR A STA STEP$RATE DCDONE: CALL INIT$HDC ; Initialize HDC for this drive ... $LOGICAL ; MOV A,M ; CPI 03H ; Driver 3? DB JRNZ,BUMPD-$-1 ; No, skip it MVI M,0 ; Yes, clear it BUMPD POP PSW ; Bump to next unit INR A ; . CPI 16 ; done? DB JRNC,NXTDVR-$-1 and 255 ; No, do another CALL LB$HD$INFO ; Reset pointer to ALV CALL GET$HL$PTR ; . SHLD HDC$ALV ; . LXI D,HDC$CLR$DONE ; Tell 'em we cleared prior assignments CALL CENTER$OUTPUT ; . RET SHOW$STAT: RET DO$SCSI: INX H LDA DRV$LUN ORA M MOV M,A DCX H LDA SCSI$ADDR CA ; . (ok or error msg returned in DE) LXI H,2 ; Setup initial reserved tracks value SHLD RESERVED ; . MVI A,0FFH ; Mark drive STA CDRIVE$OK ; . CALL CENTER$OUTPUT ; Display either error or ok msg JMP AGAIN ; Go back for more DEFINE$PART: LDA CDRIVE$OK ; Check for drive defined ORA A ; . DB JRNZ,DPOK-$-1 ; Drive defined -- continue LXI D,NO$DRIVE$MSG ; No drive defined -- display message LXI H,NO$DRIVE$OKC ; . and return to main menu. CALL PROMPT ; . JMP AGAIN LL LB$SCSIDRV ORA A RET INIT$HDC: LDA HD$CTRL ; Init HDC for this drive LXI H,HDC$INIT$EXE ; . JMP GO$TABLE ; . HDC$INIT$EXE: DW NO$INIT$NEEDED ; Generic SCSI HDC (burst mode) DW INIT$SHUGART ; Generic SCSI HDC (byte mode) DW INIT$XEBEC ; Xebec 1410(A) DW INIT$DTC ; Data Technology 500 Series NO$INIT$NEEDED: LXI D,CD$MSG ; Just set message RET INIT$SHUGART: CALL LB$HD$INFO ; Get HD info pointer LXI D,4 ; Bump to byte/block ptr DAD D ; . CALL GET$HL$PTR ; Get po inter in HL MVI M,1 ; Set byte mode LXI D,CD$MSG ; Set A-OK message RET INIT$XEBEC: LXI D,X$ID ; Where to put the fmt data LXI H,DRV$CYLS ; . LXI B,7 ; . DB 0EDH,0B0H ; . (LDIR) LXI H,X$IC ; Init Drive Characteristics LXI D,X$ID ; . CALL DO$SCSI ; . LXI D,CD$MSG ; Init ok -- load A-OK msg RZ ; LXI D,INIT$ERR$MSG ; Init failed -- load error msg RET X$IC: DB 0CH DB 0,0,0,0,0 X$ID: DB 0,0,0,0,0,0,0,11 INIT$DTC: LHLD DRV$CYLS SHLD D$CYL LDA DRV$RWC+1 STBump to CKV INX H ; . DW LDED80,HDC$ALV ; Get allocation vector to DE MOV M,E ; Save in CKV INX H ; . MOV M,D ; . INX H ; Save in ALV MOV M,E ; . INX H ; . MOV M,D ; . INX H ; . LHLD ALVBYTES ; Add ALV to ALVBYTES DAD D ; . SHLD HDC$ALV ; Save updated ALV base XCHG ; . CALL LB$HD$INFO ; Get pointer in BIOS to ALV base MOV M,E ; Save updated ALV base in BIOS INX H ; . MOV M,D ; . ; ; Update HDC DPB ... ; LHLD HDC$UNIT$DPB ; Get ptr to unit DPB ptr baA D$RWC LDA DRV$HEAD DCR A STA D$HD LDA STEP$RATE MOV E,A MVI D,0 LXI H,D$TRAN$TBL DAD D MOV A,M STA D$STPR XRA A STA STEP$RATE LXI H,D$MSC LXI D,D$MSD CALL DO$SCSI LXI D,CD$MSG ; Init ok -- load A-OK msg RZ ; LXI D,INIT$ERR$MSG ; Init failed -- load error msg RET D$TRAN$TBL: DB 60 DB 4 DB 3 DB 2 DB 1 D$MSC: DB 0C2H,0,0 DB 0,0,0 D$MSD: DB 11 D$STPR: DB 0 DB 0 D$HD: DB 0 D$CYL: DB 0,0 D$RWC: DB 0,0,0 ADD$PARTITION: LHLD PARTITION ; Get pck CALL GET$HL$PTR ; Get actual unit DPB ptr in HL PUSH H ; . LXI B,5 ; Bump to disk size DAD B ; . DW LDED80,NBLOCKS ; Save # of blocks (CP/M wants DCX D ; . [blocks - 1] stored here) MOV M,E ; . INX H ; . MOV M,D ; . POP H ; . LXI B,13 ; Bump to reserved tracks DAD B ; . DW LDED80,RESERVED ; Save reserved tracks MOV M,E ; . INX H ; . MOV M,D ; . LHLD NTRACKS ; Update reserved tracks DAD D ; . SHLD RESERVED ; . ; ; Display "successful" message ; XRartition size (in K) CALL HL$DIV$2 ; Divide K by 8 to get number CALL HL$DIV$2 ; . of tracks in this partition CALL HL$DIV$2 ; . SHLD NTRACKS ; . DAD H ; Multiply tracks by 2 to get # SHLD NBLOCKS ; . of blocks in this partition LHLD NBLOCKS ; Compute the number of ALV bytes LXI B,7 ; . required for this partition DAD B ; . CALL HL$DIV$2 ; . ( the actual formula is: ) CALL HL$DIV$2 ; . ( ALVB = [NBLOCK + 7] / 8 ) CALL HL$DIV$2 ; . SHLD ALVBYTES ; Allocation vector sizA A ; Clear carry LHLD HDC$ALV ; Compute space remaining XCHG ; . LHLD LAST$BYTE ; . DB 0EDH,052H ; SBC HL,DE DCX H ; . XCHG ; Convert to decimal LXI H,SPACE$LEFT$DEC; . CALL DE$TO$HL$DEC ; . LXI D,SPACE$LEFT ; . CALL CENTER$OUTPUT ; . RET * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Misc data and messages . . . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * CLEAR$HDC$MSG: DB 'Do you want to clear the existinge (in bytes) DW LBCD80,HDC$ALV ; Get ALV base in BC DAD B ; Add to HL DW LDED80,LAST$BYTE; Compare to last available byte CALL CMP$HL$DE ; . DB JRC,ENOUGH-$-1 ; Enough room? LXI D,NOT$ENOUGH$RM ; No ... tell 'em. CALL JUSTIFY ; . CALL RET$TO$CONT ; . RET ENOUGH: ; ; Update PHYTAB ... ; LDA CPM$LTR ; Get data for this logical drive CALL LB$GETLOGICAL ; . MVI M,03H ; Set Hard Disk driver INX H ; . MOV A,M ; Get DPH offset ANI 0F0H ; . STA DPHOFS ; . (save in  hard ' DB 'disk assignments (Y/N)? ','$' CLEAR$HDC$OKC: DB 'YN',0 HDC$CLR$DONE: DB '*** Previous hard disk assignments have ' DB 'been cleared ***',CR,LF,'$' MAIN$MENU$MSG: DB CR,LF,LF DB 'Options available:',CR,LF DB TAB,' (D) Define the Current Drive',CR,LF DB TAB,' (A) Add a partition to the ' DB 'Current Drive',CR,LF,LF DB TAB,'(ESC) Exit the program',CR,LF,LF DB 'What next (D/A/ESC)? ','$' MAIN$MENU$OKC: DB 'DA',0 SCSI$ADDR$MSG: DB CR,LF,LF,LF,LF,LF DB 'Definingorder to update DPH) MOV B,A ; Or in step rate code LDA STEP$RATE ; . ANI 00FH ; . ORA B ; . MOV M,A ; Save as DPH/step rate INX H ; . LDA DRV$LUN ; . ORI 0AH ; Set to 512 byte sectors, 4K AU MOV M,A ; . INX H ; . LDA SCSI$ADDR ; Update SCSI address MOV M,A ; . ; ; Update DPH ; LHLD DPH$BASE ; Get DPH base LDA DPHOFS ; Add offset for this unit MOV C,A ; . MVI B,0 ; . DAD B ; . LXI B,0AH ; Add offset to DPB DAD B ; . SHLD HDC$UNIT$DPB ; . INX H ;  the Current Drive ...' DB CR,LF DB '======================================' DB CR,LF DB LF,LF DB 'The Current Drive is the physical hard ' DB 'disk drive you are currently working ' DB 'with. The definition of the drive ' DB 'consists of the SCSI ID and type of ' DB 'the hard disk controller connected to ' DB 'the drive, ' DB 'the logical unit number (LUN) of the ' DB 'drive, and the drive characteristics if ' DB 'you are using a Xebec 1410(A) controller.' DB CR,L  F,LF,'Hard disk controller SCSI ID:' DB CR,LF,'-----------------------------' DB CR,LF,LF DB 'Each hard disk controller must be set ' DB 'to one of the eight SCSI bus ID''s. These ' DB 'ID''s range from zero (0) to seven (7). ' DB 'If you have only one hard disk controller ' DB 'in your system, it''s ID is usually zero ' DB '(0). If you have more than one hard disk ' DB 'controller, make sure they are set to ' DB 'different ID''s.',CR,LF,LF DB 'What is the SCSI ID of the buffered step',CR,LF DB TAB,'6 - 30us buffered step',CR,LF DB TAB,'7 - 15us buffered step',CR,LF DB 'Which step mode (0, 4, 5, 6, or 7)? ','$' XEB$STEP$OKC: DB '0',0FFH,0FFH,0FFH,'4567',0 DTC$STEP$MSG: DB CR,LF DB 'Data Technology 500 Series step mode ' DB '(choose from the ' DB 'following table):',CR,LF DB TAB,'0 - 3ms step',CR,LF DB TAB,'1 - 200us buffered step',CR,LF DB TAB,'2 - 150us buffered step',CR,LF DB TAB,'3 - 100us buffered step',CR,LF DB TAB,'4 - 50us bucurrent drive''s ' DB 'controller (0-7, ESC to quit)? ','$' SCSI$ADDR$OKC: DB '01234567',0 HD$CTRL$MSG: DB CR,LF,'Hard disk controller type:' DB CR,LF,'--------------------------' DB CR,LF,LF DB 'Of the following controllers ...',CR,LF,LF DB TAB,'1 - Generic burst-mode SCSI controller ' DB '(Adaptec ACB4000, Xebec Owl)',CR,LF DB TAB,'2 - Generic byte-mode SCSI controller ' DB '(Shugart 1610-4)',CR,LF DB TAB,'3 - Xebec 1410 or 1410A',CR,LF DB TAB,'4 - Data Technology 500 Serffered step',CR,LF DB 'Which step mode (0, 1, 2, 3, or 4)? ','$' DTC$STEP$OKC: DB '01234',0 NO$DRIVE$MSG: DB 'Please define the Current Drive before ' DB 'adding a partition to it.',CR,LF DB 'Press the RETURN key to continue. ','$' NO$DRIVE$OKC: DB CR,LF,0 CPM$LTR$MSG: DB CR,LF,LF DB 'Add a CP/M partition ' DB 'on the Current Drive ...',CR,LF DB '========================' DB '========================' DB CR,LF CPM$LTR$MSG2: DB CR,LF DB 'CP/M letter to use for this partitiies',CR,LF DB LF DB 'Which one is the Current Drive connected ' DB 'to (1, 2, 3, or 4)? ','$' HD$CTRL$OKC: DB '12' NON$GENERIC: EQU $-HD$CTRL$OKC DB '34',0 DRV$LUN$MSG: DB CR,LF,'Current Drive logical unit number:' DB CR,LF,'----------------------------------' DB CR,LF,LF DB 'Each disk connected to a hard disk ' DB 'controller has a unique number, called ' DB 'the Logical Unit Number, or LUN. ' DB 'The first LUN on a hard disk ' DB 'controller is zero (0), and the numbon ' DB '(F-P, ESC for new Current Drive)? ','$' CPM$LTR$OKC: DB 'FGHIJKLMNOP',0 PARTITION$MSG: DB CR,LF DB 'Size of the ' CPM$LTR$ECHO: DB 'x: partition (in K bytes)? ','$' NOT$ENOUGH$RM: DB CR,LF,'There is not enough space left to ' DB 'install a hard disk partition of the ' DB 'size you indicated.' DB CR,LF,'$' INIT$ERR$MSG: DB '*** Cannot initialize HDC for the Current ' DB 'Drive -- check you disk parameters. ***' DB CR,LF,'$' CD$MSG: DB CR,LF,'*** Current Drive set ers ' DB 'increase at that point to a maximum of ' DB 'seven (7). Most hard disk ' DB 'controllers can support two drives, which ' DB 'means the LUN''s for the controller are ' DB 'either zero (0) or one (1). If only ' DB 'one drive is connected, the logical ' DB 'unit number (LUN) is normally zero (0). ' DB CR,LF,LF DB 'What is the Current Drive''s logical ' DB 'unit number (0-7)? ','$' DRV$LUN$OKC: DB '01234567',0 DRV$CYLS$MSG: DB CR,LF DB 'For the controller you into: SCSI ID [' CD$ID: DB 'x], HDC type: [' CD$TYPE: DB 'x], logical unit [' CD$LUN: DB 'x]. ***',CR,'$' SPACE$LEFT: DB CR,LF,'<<< Drive ' DRIVE$INST: DB 'x: installed -- ' SPACE$LEFT$DEC: DB 'xxxxx bytes of bios buffer area ' DB 'remaining. >>>',CR,'$' SCSI$ADDR: DB 0 HD$CTRL: DB 0 DRV$LUN: DB 0 DRV$CYLS: DW 0 DRV$HEAD: DB 0 DRV$RWC: DW 0 DRV$WPC: DW 0 STEP$RATE: DB 0 CPM$LTR: DB 0 PARTITION: DW 0 LAST$BYTE: DW 0 RESERVED: DW 0 HDC$INIT: DB 0 NBLOCKS: DW 0 NTRACKS: Ddicated, ' DB 'the following additional information on ' DB 'the Current Drive is required:' DB CR,LF,LF DB ' Number of cylinders? ','$' DRV$HEAD$MSG: DB ' Number of heads? ','$' DRV$RWC$MSG: DB 'Starting cylinder for RWC? ','$' DRV$WPC$MSG: DB 'Starting cylinder for WPC? ','$' XEB$STEP$MSG: DB CR,LF DB 'Xebec 1410(A) step mode (choose from the ' DB 'following table):',CR,LF DB TAB,'0 - 3ms step',CR,LF DB TAB,'4 - 200us buffered step',CR,LF DB TAB,'5 - 70us W 0 ALVBYTES: DW 0 DPHOFS: DB 0 HDC$UNIT$DPB: DW 0 HDC$ALV: DW 0 ; HDC CSV & HDC ALV DPH$BASE: DW 0 CDRIVE$OK DB 0 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Library routines . . . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * A$TO$HL$HEX: ; ; [DC.27] ; ; Converts the number in A to the hex digits in HL ; ; Entry: ; A = number to convert ; ; Exit: ; HL = the hex equivalent of the number (L=high, H=low) ; (use shld!  to store the converted number) ; ; Modifies: none ; PUSH PSW ; Save original number RRC ; Get high nybble RRC ; . RRC ; . RRC ; . ANI 0FH ; . CALL A$TO$HEX ; Convert to hex MOV L,A ; Save in L register POP PSW ; Get original number back PUSH PSW ; Save again for later ANI 0FH ; Get low nybble CALL A$TO$HEX ; Convert to hex MOV H,A ; Save in H register POP PSW ; Get original number back RET ; and return A$TO$HEX: ; Convert A to a hex digit CPI 0AH ; ; Stop processing if EOS ('$') JZ CENTER$DONE ; . XCHG ; Put new pointer in DE JMP CENTER$NEXT$LN ; Go & do the next line OUTPUT$DELIM: CALL CON$CHR ; Output delimiter CPI LF ; Wait 10ms if we have a line feed CZ WAIT ; . INX H ; Point to next chr JMP NEXT$DELIM ; and check that one, also CENTER$DONE: POP H ; Restore registers POP B ; . POP PSW ; . RET ; and return CLEAR$SCREEN: ; ; [E1.28] ; ; This routine clears the screen by calling DO$CRLF 26 times. ; ;  If 0-9, we don't need to JM A$TO$HEX$2 ; . add any offset ADI 07H ; Offset for A-F A$TO$HEX$2: ; . ADI 30H ; ASCII bias RET ; and return BIN$TO$SCSI: ; ; [E6.10] ; ; Converts binary 0-7 to SCSI address ; ; Entry: ; A = number to convert (0-7) ; ; Exit: ; A = converted SCSI address [00H = error] ; ; Modifies: B ; ANI 07H ; Mask out all but addrs 0-7 INR A ; Bump A to shift at least one bit MOV B,A ; . and move to the B register XRA A ; Clear A register Entry: ; None ; ; Exit: ; The screen is cleared ; ; Modifies: ; None ; PUSH PSW ; Save just in case MVI A,26 ; 26 CRLF's C$NEXT$LINE: CALL DO$CRLF ; Next line DCR A ; Done? JNZ C$NEXT$LINE ; Nope. POP PSW ; Restore original AF RET ; and return CMP$HL$DE: ; ; [E6.10] ; ; Compare register pair HL with register pair DE. ; ; Entry: ; DE,HL = data to compare ; ; Exit: ; Z: DE = HL, NZ: DE # HL ; C: HL < DE, NC: HL >= DE ; ; Modifies: ; PSW ; MOV A,H ; Compare  STC ; Set carry for shift BIN$NEXT$BIT: RAL ; Shift left one bit DCR B ; Decrement count DB JRNZ,BIN$NEXT$BIT-$-1 and 255 RET ; Z = all done CENTER$OUTPUT: ; ; [DC.20] ; ; Automatically centers the output line(s) based on the line width ; stored in SCREEN$WIDTH. Each line is delimited with CR+LF. This ; routine will return to the caller when it encounters the string ; terminator, '$'. Any additional LF characters after a CR+LF pair ; will be passed through. ; ; Entry: ; Dhigh bytes CMP D ; . RNZ ; If not zero, flags are set MOV A,L ; Compare low bytes CMP E ; . RET ; Return with flags set CON$CHR: ; ; [DC.20] ; ; This routine sends the character in the "A" register to the console ; through the BDOS conout call. ; ; Entry: ; A = character to send ; ; Exit: ; character is sent to the console ; ; Modifies: ; None ; PUSH PSW PUSH B PUSH D PUSH H MVI C,2 MOV E,A CALL BDOS POP H POP D POP B POP PSW RET CON$CHR$AC:E = Pointer to output string(s), terminated with CR+LF. ; ; Exit: ; The output string(s) are sent to the screen ; ; Modifies: DE ; PUSH PSW ; Save registers PUSH B ; . PUSH H ; . CENTER$NEXT$LN: CALL GET$STRLEN ; Get length to next CR or '$' in B MOV A,B ; Check for zero length ORA A ; . JZ NEXT$DELIM ; If so, output the CR, LF, etc. LDA SCREEN$WIDTH ; Compute offset needed to center line STC ; . SBB B ; . (if there are too many chrs, just JC NO$BLANKS ; . print the l ; ; [DC.20] ; ; This routine sends the character in the C register to the console ; the number of times in the A register. ; ; Entry: ; A = Number of times to send character ; C = Character to send ; ; Exit: ; Same ; ; Modifies: ; None ; PUSH PSW ; Save all registers PUSH B ; . PUSH D ; . PUSH H ; . MOV B,A ; Move data to accomodate CP/M MOV A,C ; . NEXT$CHR$OUT: CALL CON$CHR ; Send 1 chr DCR B ; Decrement counter JNZ NEXT$CHR$OUT ; Done? POP H ; Restore all ine as is . . . ) ANI 0FEh ; Clear least significant bit RRC ; . and rotate to divide by two MVI C,' ' ; Output enough blanks to center line CNZ CON$CHR$AC ; . (only if count is non-zero) NO$BLANKS: MOV A,M ; Save CR for later MVI M,'$' ; Plug position with '$' for cp/m CALL CON$MSG ; . print string function MOV M,A ; Restore saved CR NEXT$DELIM: MOV A,M ; Get character CPI CR ; Print it if CR JZ OUTPUT$DELIM ; . CPI LF ; Print it if LF JZ OUTPUT$DELIM ; . CPI EOSregisters POP D ; . POP B ; . POP PSW ; . RET ; and return CONIN$NE$XC: ; ; [E1.28] ; ; Console input, no echo, exit on ctrl-c ; ; Entry: ; none ; ; Exit: ; A = character from console, except for ctrl-c, which causes ; an immediate jump to ALL$DONE ; ; Modifies: ; A ; CALL LB$CONIN CPI CTRLC JZ ALL$DONE RET CON$MSG: ; ; [DC.20] ; ; Console message ; ; Entry: ; DE = pointer to message string, terminated with '$' ; ; Exit: ; message printed on con! sole ; ; Modifies: A, BC ; PUSH PSW ; Save registers PUSH B ; . PUSH D ; . PUSH H ; . MVI C,9 ; BDOS print string command CALL BDOS ; . POP H ; Restore registers POP D ; . POP B ; . POP PSW ; . RET ; and return DE$TO$HL$DEC: ; ; [E6.25] ; ; Convert the 16-bit number in the DE register pair to a 5-digit ; decimal number. Store this number starting in the memory pointed ; to by the HL register pair. Optionally convert leading zeroes to ; blanks. ; ; Call DELF is sent to the screen. ; ; Modifies: ; none ; PUSH PSW ; Save AF MVI A,0Dh ; Send carriage return CALL CON$CHR ; . MVI A,0Ah ; and line feed CALL CON$CHR ; . LDA SLOW$TERM ; Check slow flag ORA A ; . CNZ WAIT ; wait for the s-l-o-w terminals POP PSW ; recover original AF RET ; and return GET$BIOS$VERS: ; ; [E5.24] ; ; Get bios version -- Copies the current BIOS jump tables (starting ; at warm boot) to a local area for ease of utility access. If the ; BIOS is $TO$HL$DEC to convert leading zeroes to blanks ; Call DE$TO$HL$DEC$0 to leave leading zeroes alone ; ; Entry: ; DE = number to convert ; HL = ptr to target memory location ; ; Exit: ; DE,HL unchanged ; ; Modifies: ; PSW ; MVI A,1 ; Set "clear leading 0's" mode DB JR,E$DETOHL-$-1 ; Jump to entry point DE$TO$HL$DEC$0: XRA A ; Set "leave leading 0's alone" mode E$DETOHL: STA DE$TO$HL$MODE ; Save mode flag PUSH B ; Save registers PUSH D ; . PUSH H ; . LXI B,-10000 ; Convert version 2.0 or greater, the secondary jump table is copied ; also. ; ; Entry: ; none ; ; Exit: ; Z = bios 1.0 - 1.4 (floppy only bios) ; NZ = bios 2.0 or greater (floppy & fixed disk bios) ; ; Modifies: All registers ; LHLD 1 ; Get start of bios jump table LXI D,LB$BIOS$TBL ; Move bios to local storage LXI B,LB$LEN ; . (length of bios area) DW LDIR80 ; . (LDIR) MVI A,10 ; Test CP/M version CALL LB$GETNXT ; Get next jump table STA BIOS$VERSION ; Save bios version INX H ; Seeten-thousands digit CALL TODEC ; . LXI B,-1000 ; Convert thousands digit CALL TODEC ; . LXI B,-100 ; Convert hundreds digit CALL TODEC ; . LXI B,-10 ; Convert tens digit CALL TODEC ; . MOV A,E ; Convert ones digit ADI '0' ; . (Leave zero intact) MOV M,A ; . XRA A ; Clear PSW POP H ; Restore registers POP D ; . POP B ; . RET ; and return ... TODEC: MVI A,'0' ; Start with an ASCII zero PUSH H ; Save target string pointer XCHG ; Move number to convert if HL is 0FFFFh MOV A,H ; . ORA L ; . RZ ; If so, then old version DCX H ; Fix HL as it has the table addr LXI D,LB$XTBL ; Move extra table to local storage LXI B,LB$XLEN ; . (length of extra table) DW LDIR80 ; . (LDIR) MVI A,0FFH ; Set NZ to indicate bios ORA A ; ... version 2.1+ RET ; ... and return. BIOS$VERSION: DB 0 ; Current bios version GET$HL$PTR: ; ; [DC.20] ; ; Gets the pointer pointed to by HL and puts it in HL ; ; Entry: ; HL = pointer to put in to HL TODEC1: MOV E,L ; Save a copy of current HL in DE MOV D,H ; . (in case we're done) INR A ; Bump digit DAD B ; Add "negative" BC to HL DB JRC,TODEC1-$-1 and 255 ; Continue while Carry set DCR A ; Get rid of extra bump POP H ; Restore target string pointer MOV M,A ; Save digit CPI '0' ; Is the digit an ascii zero? DB JRNZ,NOZERO-$-1 ; No, Turn off leading 0's flag LDA DE$TO$HL$MODE ; Yes, Check leading 0's flag ORA A ; Convert leading 0's to blanks? DB JRZ,NOBLANK- HL ; ; Exit: ; HL = pointer ; ; Modifies: none ; PUSH PSW ; Save A register MOV A,M ; Get low byte of pointer INX H ; . MOV H,M ; Get high byte of pointer MOV L,A ; Pointer is now together POP PSW ; Restore A register RET ; and return GET$STRLEN ; Searches the string pointed to by HL and returns the string length ; to the next carriage return. The length is returned in B. PUSH D ; Save start of string MVI B,0 ; Clear counter TRY$NEXT$CHR: LDAX D ; Get character$-1 ; No, Leave digit alone MVI M,' ' ; Yes, Change digit to a blank DB JR,NOBLANK-$-1 ; All done for now NOZERO: XRA A ; Turn off leading 0's flag STA DE$TO$HL$MODE ; . NOBLANK: INX H ; Bump digit pointer RET ; and return ... DE$TO$HL$MODE: DB 0 ; "Convert 0's to blanks" flag DO$CRLF: ; ; [DC.27] ; ; This routine sends a carriage return and a line feed to the terminal, ; and then waits 'SLOW$TERM' ms for a slow terminal to catch up. ; ; Entry: ; none ; ; Exit: ; CR +  CPI CR ; Is it CR? JZ EOS$FOUND ; . CPI '$' ; Is it '$'? JZ EOS$FOUND ; . INR B ; No -- increment count and INX D ; . point to the next character JMP TRY$NEXT$CHR ; . EOS$FOUND: POP H ; CR or '$' found, recall orig ptr XCHG ; DE=orginial, HL=current RET ; and return GO$TABLE: ; ; [E2.05] ; ; Jump to a routine based on a table of pointers ; ; Entry: ; A = index into table ; HL = table base address ; ; Exit: ; Routine at (A*2)+HL is executed ; ; Modifies: ; " B, HL ; LXI B,2 ; Compute offset to table of routines CALL INDEX$TABLE ; . CALL GET$HL$PTR ; . PCHL ; Jump to proper routine HL$DIV$2: PUSH PSW XRA A MOV A,H RAR MOV H,A MOV A,L RAR MOV L,A POP PSW RET INDEX$TABLE: ; ; [E1.30] ; ; Computes offset to table given base address, entry length, and entry ; requested. ; ; Entry: ; A = entry # ; BC = table entry length ; HL = base address ; ; Exit: ; HL = address to entry ; ; Modifies: ; A, BC, HL ; ORA  ; Exit: ; The data is sent to the screen. ; ; Modifies: All registers ; MVI A,80H ; Set flush right mode DB JR,E$JUSTIFY-$-1; Jump to routine entry JUSTIFY$RAGGED: MVI A,00H ; Set ragged right mode E$JUSTIFY: STA J$MODE ; Save mode byte MVI A,'$' ; Mark start of buffer STA OUTBUF-1 ; . XCHG ; DE is usually print source J$NEXT$LINE: XRA A ; Clear character counter STA BLANK$LEN ; . MOV B,A ; . LXI D,OUTBUF ; Set up buffer pointer J$CHECK$CHR: MOV A,M ; Get characterA ; Set up flags for first check I$TBL$ADD: RZ ; If A=0, we're done DAD B ; Otherwise add length to base, DCR A ; . decrement counter, JMP I$TBL$ADD ; . and check again. IS$IT$OK: ; ; [E1.28] ; ; Check the character in A against the list of "OK" chrs pointed ; to by HL ; ; Entry: ; A = character to check ; HL = pointer to list of "OK" characters ; ; Exit: ; A = original character if ok, 0ffh if not in list ; B = position of character in list ; ; Modifies: ; BC ; PU CPI CR ; CR? DB JRNZ,J$NO$R-$-1 ; . CALL J$FLUSH$LINE ; . Flush output line, MVI A,CR ; . Output CR, CALL CON$CHR ; . . INX H ; . bump ptr & check next DB JR,J$NEXT$LINE-$-1 and 255 J$NO$R: CPI LF ; LF? DB JRNZ,J$NO$L-$-1 ; . CALL CON$CHR ; . Output LF, INX H ; . bump ptr & check next DB JR,J$CHECK$CHR-$-1 and 255 J$NO$L: CPI FF ; FF? DB JRNZ,J$NO$F-$-1 ; . CALL CLEAR$SCREEN ; . Clear screen, INX H ; . bump ptr & check next DB JR,J$CHECK$CHR-$-1 and 255 SH H ; Save original "OK" pointer MOV C,A ; Save chr to check against MVI B,0 ; Clear counter CPI ESC ; If chr is JZ I$CHR$OK ; . then automatically ok INR B ; . otherwise start counting at 1 I$CHK$NEXT: MOV A,M ; Get chr to check against ORA A ; End of table? JNZ I$NOT$EOT ; No, check chr DCR A ; Decrement to get 0ffh MOV B,A ; Stuff for later move JMP I$CHR$OK ; And exit I$NOT$EOT: CMP C ; Chrs match? JZ I$CHR$OK ; . Yes, return INX H ; . No, bump pointer  J$NO$F: CPI '$' ; End of string? DB JRNZ,J$NO$S-$-1 ; . CALL J$FLUSH$LINE ; . Flush output line, and RET ; . return to caller. J$NO$S: STAX D ; Not a special chr, save in buffer CPI ' ' ; Blank? DB JRNZ,J$NO$B-$-1 ; . No, don't save position SHLD BLANK$POS ; Save position for later XCHG ; and save corresponding position SHLD OUTBUF$BLANK ; . of the blank we just saved XCHG ; . in the output buffer MOV A,B ; . STA BLANK$LEN ; Save current length also J$NO$B: IN INR B ; . . and bump counter JMP I$CHK$NEXT ; . . and check next chr I$CHR$OK: MOV A,B ; Set status based on ORA A ; . position counter MOV A,C ; Get user chr back I$DONE: POP H ; and original "OK" pointer RET ; and return JUSTIFY: ; ; [E6.11] ; ; This routine will send a data stream to the console, with each line ; justified based on the SCREEN$WIDTH value. The stream must terminate ; with the CP/M end of string character ($) and may contain imbedded ; CR,LF pairs to sepR B ; Increment counter INX H ; . and input pointer INX D ; . and output pointer LDA SCREEN$WIDTH ; Compare counter against screen width SUB B ; . JP J$CHECK$CHR ; And continue checking if not past end ; ; Screen width exceeded, send this line to the screen. ; LHLD OUTBUF$BLANK ; Get pos of last blank in output buf MVI M,'$' ; and plug with eos ('$') LDA J$MODE ; Justify right edge only if the ORA A ; . right-justify flag is non-zero CM J$ADD$BLANKS ; . CALL J$SEND$BUFFER arate paragraphs. ; ; NOTE: To insure proper operation, the LF character should only follow ; a CR character or another LF character, as the CR character is used to ; flush the current line without justification. ; ; Two entry points are provided: ; JUSTIFY Justify output, flush right ; JUSTIFY$RAGGED Justify output, ragged right ; ; As of E6.11, the flush right routine was not installed, so either ; entry point will provide the same results. ; ; Entry: ; DE = pointer to line(s) to output ;; Output the line to the screen CALL DO$CRLF ; and a CR / LF LHLD BLANK$POS ; Get pointer to where we left off J$SKIP$BLANKS: INX H ; Bump pointer past blank(s) MOV A,M ; . CPI ' ' ; . DB JRZ,J$SKIP$BLANKS-$-1 and 255 JMP J$NEXT$LINE ; and check next segment J$FLUSH$LINE: ; Flush line when CR or EOS encountered MVI A,'$' ; Plug current position with EOS ($) STAX D ; . CALL J$SEND$BUFFER ; Send this line of data RET ; and return J$ADD$BLANKS: RET ; At a later time, " this routine will ; justify the right margin by inserting ; extra blanks in the output line. J$SEND$BUFFER: MOV A,B ; If line to output is of zero length, ORA A ; . then don't output the line. RZ ; . LXI D,OUTBUF ; Get address of output buffer CALL CON$MSG ; and call our print message routine RET ; return J$MODE DB 0 ; Current right justify mode BLANK$POS DW 0 ; Last blank on this line OUTBUF$BLANK DW 0 ; Last blank in the output buffer BLANK$LEN DB 0 ; Length of lineRNZ,P$NO$D-$-1 ; . MVI A,ESC ; . P$NO$D: CPI '_' ; Underscore = prompt anyway DB JRZ,P$DISP-$-1 ; . CALL TO$UPPER ; Convert the chr to upper case LHLD USER$OKLIST ; Get the user ok chr list CALL IS$IT$OK ; Check the chr against the ok list RP ; If ok, return XRA A ; Otherwise, cancel the cmd line STA CMD$LINE$CHRS ; . buffer and fall through to p$disp P$DISP: CALL JUSTIFY ; and call justify routine P$TRY$AGAIN: CALL CONIN$NE$XC ; Console input, no echo, except ^C CALL TO$ to the blank ; NOTE: OUTBUF is defined to be after the stack and before the heap. ; end of justify$output data area PROMPT: ; ; [E6.14] ; ; Prompt the user or the command line for input. ; ; Two entry points are provided: ; PROMPT standard entry, CRLF after chr from user ; PROMPT$NOLF special entry, No CRLF after chr from user ; ; When the command line is used for input, the following characters ; are translated to new values or new functions: ; ; Character New character or new funUPPER ; Convert the chr to upper case LHLD USER$OKLIST ; Get the user ok chr list CALL IS$IT$OK ; Check the chr against the ok list JM P$TRY$AGAIN ; Not there, try again CNZ CON$CHR ; Display chr we found PUSH PSW ; Check mode flag in case we need LDA PROMPT$MODE ; . to send a CR+LF after the DB BIT,BTST+B0+ZA ; . user's input CNZ DO$CRLF ; . POP PSW ; . RET ; and return PROMPT$MODE: DB 0 ; Prompt mode flag USER$OKLIST: DW 0 ; User 'ok' chr list ; end of PROMPT routine ction ; -------------- ------------------------------------ ; (space) Ignored ; , (comma) key ; . (period) key ; @ (at-sign) repeat existing command line ; _ (underscore) Prompt and get character from user ; ; Entry: ; DE = pointer to prompt string ; HL = pointer to list of valid chars (terminated with 00H) ; ; Exit: ; A = char from the user ; B = position of this character (0, 1, 2, ... n) ; ; Z = char was the escape key ; NZ = char was not the escape key ; ; Modifie PROMPT$DECIMAL: ; ; [E6.11] ; ; Prompt the user for a decimal input of up to 5 digits. The Z flag ; indicates the termination character: Z = ESC key, results may not ; be valid; NZ = RETURN key, results valid. ; ; Entry: ; DE = pointer to prompt string ; ; Exit: ; DE = Value entered by the user, 0-0FFFFH ; A = Low byte of value entered by the user, 0-0FFH ; ; Z = ESC key pressed, ignore results ; NZ = RETURN key pressed, results valid ; ; Modifies: ; A, PSW ; MVI A,01H ; Set CRLFs: ; PSW, BC ; MVI A,01H ; Set CRLF after chr DB JR,E$PROMPT-$-1 ; Jump to entry point PROMPT$NOLF: MVI A,00H ; Set no CRLF after chr E$PROMPT: STA PROMPT$MODE ; Save prompt mode flag SHLD USER$OKLIST ; . and user 'ok' chr list RE$PROMPT: LDA CMD$LINE$CHRS ; Are there any characters left from ORA A ; . the command line? DB JRZ,P$DISP-$-1 ; No, dsp text & get chr from bios DCR A ; Yes, reduce # of chrs by one STA CMD$LINE$CHRS ; . LHLD CMD$LINE$PTR ; Get command line chara after entry DB JR,E$PR$DEC-$-1 ; Jump to entry point PROMPT$DEC$NOLF: MVI A,00H ; Set no CRLF after entry E$PR$DEC: STA PROMPT$D$MODE ; Save prompt mode flag PUSH H ; Save original HL reigster PUSH B ; . and the BC register, too. XRA A ; Clear the current digit pointer MOV C,A ; Setup count in C register LXI H,SCRATCH ; Initialize the scratch pointer NEXT$DIGIT: PUSH B ; Save digit counter PUSH H ; . and string pointer LXI H,DEC$INPUT$OKC ; Get current 'ok' chrs CALL PROcter MOV A,M ; . INX H ; . SHLD CMD$LINE$PTR ; . CPI ' ' ; Ignore spaces DB JRZ,RE$PROMPT-$-1 and 255 CPI '@' ; @ = repeat cmd line DB JRNZ,P$NO$R-$-1 ; . LXI H,INBUF+2 ; Set command line pointer back SHLD CMD$LINE$PTR ; . to the beginning MVI A,07FH ; Set count of chrs to 127 STA CMD$LINE$CHRS ; . (the most it could be) DB JR,RE$PROMPT-$-1 and 255 P$NO$R: CPI ',' ; Change ',' to CR DB JRNZ,P$NO$C-$-1 ; . MVI A,CR ; . P$NO$C: CPI '.' ; Change '.' to ESC DB JMPT$NOLF ; Prompt for digit POP H ; Get string pointer back POP B ; . along with digit counter DB JRZ,ESCRTN-$-1 ; Return if ESC key hit CPI CR ; Check for return JZ CONVERT$STRING ; Convert string to decimal, if so CPI BS ; Check for backspace JZ BACKUP$DIGIT ; Back up 1 digit, if we can MOV B,A ; Save character for a moment MOV A,C ; Check digit count CPI 5 ; . JC ADD$DIGIT ; 5 digits or less, add to string LXI D,BLOT ; More than 5 digits, blot this one CALL CON$MSG ;#  . JMP DE$FOR$NEXT ; . ADD$DIGIT: INR C ; Otherwise bump digit count MOV M,B ; Save digit INX H ; Bump digit pointer JMP DE$FOR$NEXT ; Setup DE for next prompt BACKUP$DIGIT: MVI A,' ' ; Bump forward to clear digit CALL CON$CHR ; . XRA A ; Are we at the beginning? CMP C ; . JZ DE$FOR$NEXT ; Yes, don't backup MVI A,BS ; Backup to correct position CALL CON$CHR ; . DCX H ; Backup pointer 1 chr DCR C ; Push count 1 back, also DE$FOR$NEXT: LXI D,DEC$INPUT$MSG ; Setup  A ; Multiply previous by 10 ADD C ; x1 ADD A ; x2 ADD A ; x4 ADD C ; x5 ADD A ; x10 ADD B ; Add in new digit INX H ; Bump to next chr JMP STA$NEXT$CHR ; Go back for another digit NA$DIG: MOV A,C ; Get totals ORA A ; Set Z/NZ flag POP B ; Get old BC reg back RET ; Return with results in A. STR$TO$DE: ; ; [E5.26] ; ; Converts the string pointed to by HL to a number in the DE reg. ; The conversion will continue until the first non-numeric chr ; found. ; ; NOTfor next prompt JMP NEXT$DIGIT ; . CONVERT$STRING: MVI M,0 ; Mark end of string LXI H,SCRATCH ; Get beginning of string CALL STR$TO$DE ; Convert string to DE register MVI A,0FFH ; Insure NZ flag ORA A ; . MOV A,E ; Move low byte to A PUSH PSW ; Check mode flag to see if a LDA PROMPT$D$MODE ; . CR+LF should be sent after ORA A ; . the user's input CNZ DO$CRLF ; . POP PSW ; . ESCRTN: POP B ; Get old BC register back POP H ; . and original HL, as well. RET ; andE: a test for register overflow is not made. If HL points ; to a string whose numerical value is greater than 65535 (64K), ; inaccurate results will occur. ; ; Entry: ; HL = ptr to string ; ; Exit: ; DE = value of string in HL ; HL = next character to process ; ; Modifies: ; DE, HL ; ; PUSH PSW ; We need the A register PUSH B ; . and the BC register LXI D,0 ; Clear totals STN$NEXT$CHR: PUSH D ; Save results so far . . . POP B ; . MOV A,M ; Get chr CPI '0' ; Less than '0 return with result in HL & A DEC$INPUT$MSG: DB '$' ; Decimal input message DEC$INPUT$OKC: DB '0123456789' ; Decimal input 'ok' chrs DB BS,CR,0 ; . BLOT: DB BS,' ',BS,'$' ; Blot out digit SCRATCH: DB ' ',0 ; Max 5 digits PROMPT$D$MODE: DB 0 ; CRLF flag RET$TO$CONT: ; ; [E2.19] ; ; Prompts and waits for the RETURN key to be pressed. ; ; Entry: ; none ; ; Exit: ; Display message and wait for a RETURN key. ; ; Modifies: ; all ; LXI D,RTC$MSG ; Press RETURN to continue ...'? DB JRC,NO$DIG-$-1 ; Yes, finished CPI '9'+1 ; Greater than '9' DB JRNC,NO$DIG-$-1 ; Yes, finished SUI '0' ; No, convert to 0-9 MOV E,A ; Save this digit MVI D,0 ; . PUSH H ; Save ptr to input string LXI H,0 ; Multiply previous by 10 DAD B ; x1 DAD H ; x2 DAD H ; x4 DAD B ; x5 DAD H ; x10 DAD D ; Add in new digit XCHG ; Save results back to DE POP H ; Get input string ptr back INX H ; Bump to next chr JMP STN$NEXT$CHR ; Go back for another digit NO$DIG CALL CENTER$OUTPUT ; . LXI D,NO$MSG ; . LXI H,RTC$OKC ; . CALL PROMPT ; . [ CR ] RET RTC$MSG: DB 'Press the RETURN key to continue ...' NO$MSG: DB '$' RTC$OKC: DB CR,0 ; end of RET$TO$CONT routine STR$TO$A: ; ; [E5.26] ; ; Converts the string pointed to by HL to a number in the A reg. ; The conversion will continue until the first non-numeric chr ; found. ; ; NOTE: a test for register overflow is not made. If HL points ; to a string whose numerical value is greater than 2: POP B ; Get old BC reg back POP PSW ; . and old A reg as well. RET ; Return with results in DE. TO$UPPER: ; ; [E1.08] ; ; Convert the character in A to upper case. ; ; Entry: ; A = character to convert ; ; Exit: ; A = upper case character (if alpha) ; ; Modifies: ; A ; CPI 'z'+1 ; Convert to upper case JP UPPER$ALREADY ; . CPI 'a' ; . JM UPPER$ALREADY ; . ANI 5FH ; . UPPER$ALREADY: RET ; and return WAIT: ; Wait A ms ; ; Entry: ; milliseconds in A 55, inaccurate ; results will occur. ; ; Entry: ; HL = ptr to string ; ; Exit: ; A = value of string in HL ; HL = next character to process ; ; Modifies: ; A, HL ; PUSH B ; We need the BC register XRA A ; Clear totals STA$NEXT$CHR: MOV C,A ; Save results so far . . . MOV A,M ; Get chr CPI '0' ; Less than '0'? DB JRC,NA$DIG-$-1 ; Yes, finished CPI '9'+1 ; Greater than '9' DB JRNC,NA$DIG-$-1 ; Yes, finished SUI '0' ; No, convert to 0-9 MOV B,A ; Save this digit XRA ; ; Exit: ; time waited ; ; Modifies: A ; PUSH PSW MVI A,221 WAIT$2: DCR A JNZ WAIT$2 POP PSW DCR A JNZ WAIT RET * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Data area (use DS or EQU) * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ; Replicated BIOS to make direct calls easier . . . LB$BIOS$TBL: LB$WBOOT DS 3 ; Warm boot LB$CONST DS 3 ; Console status LB$CONIN DS 3 ; Console input LB$CONOUT DS 3 ; Console output LB$LIS# TOUT DS 3 ; List output LB$PUNCH DS 3 ; Punch output LB$READER DS 3 ; Reader input LB$HOMDSK DS 3 ; Home disk (move to track 00) LB$SELDSK DS 3 ; Select disk drive LB$SETTRK DS 3 ; Select track number LB$SETSEC DS 3 ; Select sector number LB$SETDMA DS 3 ; Set DMA address LB$DSKREAD DS 3 ; Disk read LB$DSKWRITE DS 3 ; Disk write LB$LISTST DS 3 ; List status LB$SECTRN DS 3 ; Sector translate routine ; AMPRO-specific BIOS calls LB$GETNXT DS 3 ; Get bios ver & next tbl address LB$GETEDSK DS 3 ; Get pointer to E-disk storage LB$IOINIT DS 3 ; Set new I/O parameters LB$SCSIDRV DS 3 ; SCSI direct driver LB$LEN EQU $-LB$BIOS$TBL LB$XTBL: ; 'Extra' table definitions ... LB$SWAP$DRV DS 3 ; Swap two logical drives LB$HD$INFO DS 3 ; Get HD pointers LB$PHYTAB DS 3 ; Set/get phytab access LB$GETLOGICAL DS 3 ; Get logical device table entry LB$RESERVED DS 3 ; Reserved entry LB$XLEN EQU $-LB$XTBL DS 64 ; 32-level stack STACK: DS 2 ; Old stack pointer INBUF: DS 128 ; Command line input  of terminal (ADM 3A) DB ' ' ; . DB 'K'-'@' ; . Cursor up DB 'J'-'@' ; . Cursor down DB 'L'-'@' ; . Cursor right DB 'H'-'@' ; . Cursor left DB 00 ; . Clear screen delay DB 00 ; . Cursor motion delay DB 00 ; . Clear to EOL delay DB 1BH,'*',0 ; . (CL) Clear screen string DB 1BH,'=%+ %+ ',0 ; . (CM) Cursor motion string DB 1BH,'T',0 ; . (CE) Clear to EOL string DB 1BH,')',0 ; . (SO) Start hilite string DB 1BH,'(',0 ; . (SE) End hilite string DB 0 ; . (TI) Terminal inibuffer IBUFL: EQU 128 ; Input buffer length OBPLUG: DS 1 ; Start of outbuf ('$') OUTBUF: DS PWIDTH ; Output buffer OBUFL: EQU $-OUTBUF; Output buffer length HEAPPTR: DS 2 ; Pointer to next area HEAPLEN: DS 2 ; Length of next area HEAP: EQU $ ; Start of heap END START ry LB$XLEN EQU $-LB$XTBL DS 64 ; 32-level stack STACK: DS 2 ; Old stack pointer INBUF: DS 128 ; Command line input t string DB 0 ; . (TE) Terminal de-init string DB 0 ; ENVEND: ; End of environment and TCAP descriptors CMDSET: DW Z3CL+4 ; Point to first chr in cmd line buf DB Z3CLS ; Command line buffer size PATH: ; Initial path description DB '$',0 ; . Current drive, user 0 DB '$',15 ; . Current drive, user 15 DB 1,'$' ; . Drive A:, current user DB 1,0 ; . Drive A:, user 0 DB 1,15 ; . Drive A:, user 15 DB 0 ; (end of path) ENDIF ; ZCPR3 data DRIVE$TDATA: ; Drive type data DB 01H,02H,04H,08H DB 02H,00H,00H,00H LAST$SG EQU $ ORG BOOT UNINIT EQU $ MUNACT: DS 1 ; UNALLOCATED COUNT VALUE SEKDSK: DS 1 ;SEEK DISK NUMBER SEKTRK: DS 2 ;SEEK TRACK NUMBER SEKSEC: DS 1 ;SEEK SECTOR NUMBER HSTDSK: DS 1 ;HOST DISK NUMBER HSTTRK: DS 2 ;HOST TRACK NUMBER HSTSEC: DS 1 ;HOST SECTOR NUMBER CPMDSK: DS 1 ;SINGLE DENSITY DSK PARM CPMTRK: DS 2 ; AND TRK CPMSEC: DS 1 ; AND SECTOR SEKHST: DS 1 ;SEEK SHR SECSHF HSTACT: DS 1 ;HOST ACTIVE FLAG HSTWRT: DS 1 ;HOST WRITTEN FLAG UNACNT: DS 1 ;UNALLOC REC CNT UNADSK: DS 1 ;LAST UNALLOC DISK UNATRK: DS 2 ;LAST UNALLOC TRACK UNASEC: DS 1 ;LAST UNALLOC SECTOR CPMSPT: DS 1 ; LOGICAL SECTORS PER TRACK SECMSK: DS 1 ; SECTOR MASK SECSHF: DS 1 ; SECTOR SHIFT INT$RETRIES: DS 1 ; Internal retry counter for RD & WR ERFLAG: DS 1 ;ERROR REPORTING RSFLAG: DS 1 ;READ SECTOR FLAG READOP: DS 1 ;1 IF READ OPERATION WRTYPE: DS 1 ;WRITE OPERATION TYPE DMAADR: DS 2 ;LAST DMA ADDRESS HSTBUF:$  DS 1024 ;HOST BUFFER STATUS: DS 1 MESSAGE: DS 1 CMDPTR DS 2 DATPTR DS 2 LOGDSK DS 1 PHYDRV DS 1 DPHDRV DS 1 PHYTAG DS 2 PHYCMD DS 1 TAGDRV DS 1 TAGPHY DS 1 TAGTYP DS 1 TAGCTL DS 1 SCSI$IO$COUNT DS 1 SCSI$STAT$DAT DS 4 TARGET DS 1 LOGUNIT DS 1 CURDPB DS 2 ; AMPRO BIOS-SPECIFIC STORAGE IDSAVE: DS 6 ;READ ADDRESS BUFFER AREA LTRACK: DS 5 ;LAST FLOPPY TRACK ACCESSED LDISK: DS 1 ;LAST DISK SELECTED TRIES: DS 1 ;NUMBER OF TIMES TO DO IT RWHOST: DS 1 ;LOCAL REDS 0 CSVG: DS 0 CSVH: DS 0 CSVI: DS 0 CSVJ: DS 0 CSVK: DS 0 CSVL: DS 0 CSVM: DS 0 CSVN: DS 0 CSVO: DS 0 CSVP: DS 0 ; * * * * * * * * ; ; Hard disk allocation vector storage ; ; * * * * * * * * HD$ALV EQU (1279/8)+1 ; Maximum 1280 disk blocks ALVF: DS 0 ALVG: DS 0 ALVH: DS 0 ALVI: DS 0 ALVJ: DS 0 ALVK: DS 0 ALVL: DS 0 ALVM: DS 0 ALVN: DS 0 ALVO: DS 0 ALVP: DS 0 HD$CURRENT EQU $ ; Current Bios Buffer Area ptr ENDDATA EQU $ ; Mark the last avail.AD/WRITE FLAG HSTSID: DS 1 ;HOST DISK SIDE SELECT MASK TIMEOUT: DS 1 ;TIMEOUT LOOP COUNTER SECTOR: DS 1 ;TEMPORARY STORAGE CHGDSK: DS 1 ; Flag to tell if we changed drives FRWCMD: DS 1 ; FDC command FRWPTR: DS 2 ; FDC data pointer SAVE$CMDPTR: DS 2 ; SCSI command pointer save area SAVE$DATPTR: DS 2 ; SCSI data pointer save area ; SPECIAL E DISK PARAMETERS - FILLED IN WHEN E DISK IS ; FIRST SELECTED, AND USED IN DEBLOCKING. ESECADJ: DS 1 ;SECTOR NUMBER ADJUST FOR SIDE 1 IF  byte IF ZCPR3 ; RESERVE: EQU 0FD00H ; (0FD00H if ZCPR3) ENDIF ; IF NOT ZCPR3 ; RESERVE: EQU 00000H ; (00000H if no ZCPR3) ENDIF ; FDCSIZE: EQU (MAINEND-BIOS) + (LAST$SG-HDCODE) FDDSIZE: EQU FDATAEND-LAST$SG HDCSIZE: EQU HDCODE-MAINEND HDDSIZE: EQU ENDDATA-FDATAEND ; * * * * * * * * ; ; Show the available free space and the number of sectors needed ; to hold a SYSGEN image of CP/M with this BIOS: ; ; * * * * * * * * FREEMEM: EQU RESERVE-ENDDATA ; Free memory left BUFFKBD KEYBUF: DS BUFFSIZE ; allocate buffer space for 80 character KEYSTOP: EQU $ ; end of buffer address ENDIF DIRBUF: DS 128 ;DIRECTORY ACCESS BUFFER ; * * * * * * * * ; ; Floppy drive directory check vector storage ; ; The length of these vectors allows the use of up to 256 ; directory entries, all of which are checked by BDOS. ; ; Note that these are used for removable floppy media only. ; ; Do not change QPARM to increase the number of directory ; entries without adjustiSGSIZE: EQU (LAST$SG-CCP+127)/128 ; Sysgen size ; Number of sectors -- Must be 50H or less! END BIOS  no ZCPR3) ENDIF ; FDCSIZE: EQU (MAINEND-BIOS) + (LAST$SG-HDCODE) FDDSIZE: EQU FDATAEND-LAST$SG HDCSIZE: EQU HDCODE-MAINEND HDDSIZE: EQU ENDDATA-FDATAEND ; * * * * * * * * ; ; Show the available free space and the number of sectors needed ; to hold a SYSGEN image of CP/M with this BIOS: ; ; * * * * * * * * FREEMEM: EQU RESERVE-ENDDATA ; Free memory left ng the variable FD$CKS. ; ; * * * * * * * * FD$CKS EQU (255/4)+1 ; maximum of 256 directory entries CSVA: DS FD$CKS CSVB: DS FD$CKS CSVC: DS FD$CKS CSVD: DS FD$CKS CSVE: DS FD$CKS ; * * * * * * * * ; ; Floppy drive allocation vector storage ; ; This area is used by BDOS to map all CP/M blocks for the ; target disk drive, and is maintained to indicate which blocks ; on the disk are in use. ; ; The length of the Floppy area is set to 50 Bytes, which is ; enough to handle a 96 TPI DS/DD disk as described in QPARM. ; ; Do not change QPARM to increase storage without adjusting ; the variable FD$ALV. ; ; * * * * * * * * FD$ALV EQU (394/8)+1 ; maximum of 395 disk blocks ALVA: DS FD$ALV ALVB: DS FD$ALV ALVC: DS FD$ALV ALVD: DS FD$ALV ALVE: DS FD$ALV FDATAEND EQU $ HD$VECTORS: EQU $ ; HD check & allocation vectors ; * * * * * * * * ; ; Hard disk directory check vector storage ; ; No Check storage is required - just an address ; ; * * * * * * * * CSVF: $ % % & & ' '