;=============================================================== ; ; Prom Based CP/M and ISIS 4.0 Bios, August 9, 1981 ; ; Version 1.0 ; ; (For Tarbell Disk Controller and Dynabyte Video) ; ;=============================================================== ; ; This module contains the input/output routines used by ISIS II ; and a CP/M cold boot loader. ; true equ -1 ; define value of true false equ not true ; define value of false debug equ false ; set true if setup assembly for ; ram based debug at 32 memory size ; stprat equ 2 ; rate 1=6ms, 2=10ms, 3=20ms hlab equ 8 ; 8 for hd ld at beg of seek ; ; define disk controller base addresses for operations ; disk equ 0f8h ; disk base address dcom equ disk ; disk command port dstat equ disk ; disk status port track equ disk+1 ; disk track port sectp equ disk+2 ; disk sector port ddata equ disk+3 ; disk data port wait equ disk+4 ; disk wait port dcont equ disk+4 ; disk control port ; ; define console and printer interface parameter equates ; cstat equ 00h ; console status port cdata equ 01h ; console data port lstat equ 42h ; list status port ldata equ 43h ; list data port ckbr equ 00000001b ; keyboard ready bit cptr equ 10000000b ; crt out ready bit lptr equ 00000001b ; printer output ready bit ; ; define stack top location ; stack equ 100h ; top of stack ; ; define ASCII character codes that are referenced ; cr equ 0dh ; ASCII cariage return lf equ 0ah ; ASCII linefeed ; ; define ISIS and CP/M storage locations and constants ; iobyte equ 0003h ; INTEL iobyte initio equ 0006h ; initial I/O config memtop equ 0004h ; location ISIS stores the top of ram ; if debug monitor equ 08800h ; monitor start memsiz equ 32 ; ram memory size in decimal kilo bytes endif ; debug ; if not debug monitor equ 0f800h ; monitor start memsiz equ 62 ; ram memory size in decimal kilo bytes endif ; not debug ; memmax equ ((memsiz*1024)/256)-1 ; memory map area date equ 0715h ; date verh equ 10h ; version 1.0 ; org 0ffffh ; db 0 ; set version number of monitor ; ; start of CP/M and ISIS boot load point routine ; org monitor ; origin of this program ; jmp start0 ; reset entry point jmp conin ; console input jmp reader ; reader input jmp conout ; console output jmp punch ; punch output jmp list ; list output jmp const ; console input status jmp iochk ; I/O system status jmp ioset ; set I/O configuration jmp memchk ; compute size of memory jmp dummy ; define user I/O entry points jmp dummy dw date ; date stamp for monitor rom jmp dummy ; upp input jmp dummy ; upp output jmp dummy ; upp status db verh ; version stamp for monitor rom db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ; dummy copyright jmp dummy ; ioccom entry point jmp dummy ; ioc output jmp stat78 ; disk controller 1 status jmp stat88 ; disk controller 2 status jmp cmmd78 ; disk controller 1 jmp cmmd88 ; disk controller 2 jmp rtyp78 ; disk controller 1 result type jmp rtyp88 ; disk controller 2 result type jmp rbyt78 ; disk controller 1 result byte jmp rbyt88 ; disk controller 2 result byte ; ; disk command to controller that should be at 88h ; hl points to address of I/O parameter block ; cmmd88: mvi a,2 ; mark this is for controller 2 jmp cmmdent ; go do common I/O command ; ; ; disk command to controller that should be at 78h ; hl points to address of I/O parameter block ; cmmd78: xra a ; mark this is for controller 1 cmmdent:shld iopb ; save pointer to iopb lxi h,0 ; make hl 0 for dad dad sp shld ostack ; save old stack pointer lxi sp,newstk ; set up new stack push d ; save d and e push b ; save b and c mov c,a ; drive select bit 1 lxi h,iodone ; make return address push h ; put it on stack ; ; copy iopb into scratch area ; lxi d,ioblk ; point to ioblk destination lhld iopb ; point to source mvi b,7 ; number of bytes to move ibkmov: mov a,m ; get a byte stax d ; store a byte inx h ; bump pointers inx d dcr b ; decrement count jnz ibkmov ; loop if not done ; ; select disk drive specified in iopb ; lda dskins ; get disk instruction rrc rrc rrc rrc ; drive select bits to 1 and 2 ani 1 ; select bit 0 ora c ; select bit 1 call seldsk ; select drive ; ; test if selected drive is ready, error if not ready ; in dstat ; read status rlc ; look at drive ready bit jc rdyerr ; error if not ready ; ; execute disk instruction from iopb ; lda dskins ; get disk instruction ani 7 ; look at opcode only cpi 4 jz iread ; goto INTEL read cpi 6 jz iwrite ; goto INTEL write cpi 7 jz iwrite ; do deleted write like normal write cpi 5 jz iverf ; goto INTEL verify cpi 1 jz seek ; goto INTEL seek cpi 2 jz ifrmat ; INTEL format cpi 3 jz home ; do INTEL home xra a ; nop, ret result byte=0 ret ; ; disk I/O complete restore regs and restore sp and return ; disk controller simulator exit point ; iodone: sta resbyt ; save disk status pop b ; restore regs pop d lhld ostack sphl ; restore stack lhld iopb ret ; return to ISIS ; ; user entry point to check the status of the I/O byte ; iochk: lda iobyte ; get status byte ret ; ; user entry point to change the I/O byte to a new value ; ioset: mov a,c ; put new value into the memory sta iobyte ret ; ; user entry point to get the value of the top of available memory ; memchk: lda memtop+1 ; msbyt of top address of memory dcr a ; change one page less to permit ; monitor to use 256 bytes of ram mov b,a ; msbyt in b reg mvi a,0c0h ; ab points to base of user stack ret ; in 2'nd from top page of ram ; ; dummy I/O routine ; dummy: xra a ret ; dummy routine ; ; routine to simulate getting ready status of the single density ; INTEL disk controller hardware ; stat78: equ $ ; stat88: mvi a,0bh ; show always ready ret ; ; routine to imitate return of result type byte from the disk ; controller hardware ; rtyp78: equ $ ; rtyp88: xra a ret ; ; routine to imitate reading of the result byte from the disk ; controller hardware code was made by the disk operation ; command routines and stored at resbyt in the top page of ram ; rbyt78: equ $ ; rbyt88: lda resbyt ; get result byte ret ; ; here to setup start of command inquiry loop to boot either ; a CP/M system disk of up to 62k size or to load an ISIS 40 ; system disk that has been patched for disk I/O through this ; prom based i/c-bios software ; start0: lxi sp,stack ; set stack pointer ; ; put jump to monitor at zero ; mvi a,0c3h ; put jump to monitor at 0 sta 0000h lxi h,monitor shld 0001h mvi a,01h ; console is crt sta iobyte sta initio ; ; calculate top of memory and store at "memtop" for the ISIS ; system operation ; lxi h,03f00h ; start point for test pag: inr h ; set to next page jz fnd ; stop if at end mov a,m ; get memory byte cma ; complement it mov m,a ; put comp in memory cmp m ; did it go in ok cma ; invert back mov m,a ; restore original jz pag ; loop to next 256 bytes if it changed fnd: dcx h ; back up a page mov a,h cpi memmax ; less than f700h jc x11 ; if yes then use this value ; fnd1: mvi h,memmax ; otherwise set memtop to 0f7ffh ; x11: shld memtop ; set ISIS memory top address ; ; command check loop for monitor operation select ; iboot: lxi h,stmsg ; point to start msg call pmsg ; print message iboot1: call conin ; wait for response push psw ; echo operator response mov c,a call conout pop psw ani 05fh ; convert input to upper case cpi 'C' ; check if CP/M boot jz cpmboot cpi 'I' ; or if ISIS boot jnz iboot ; if neither then prompt again ; ; print ISIS prompt message and then load disk on input of a "cr" ; isisboot: ; lxi h,isisms ; print ISIS load message call pmsg call conin ; get ready status cpi cr ; see if a cr jnz iboot ; start all over if not ISIS boot lxi h,crlf ; print carriage return linefeed call pmsg ; ; force interrupt ; mvi a,0d0h ; force interrupt command out dcom in dstat ; clear interrupt ; ; setup scratch area ; lxi h,0ffffh shld trtab ; preset track table shld trtab+2 mvi a,4 ; set up initial out of range disk sta diskno xra a call seldsk ; select drive 0 ; ; boot in ISISt0 to location 3000 and execute ; lxi h,bootpb ; point to boot parameter block call cmmd78 ; read in disk ora a jz 3000h ; if no errors goto ISIS lxi h,bmsg call pmsg ; print boot error message jmp iboot ; if error then go try for another command ; ; iopb for booting ISISt0 in to 3000h ; bootpb: db 80h ; iocw, no update bit set db 04h ; I/O instruction, read disk 0 db 26 ; read 26 sectors db 0 ; track 0 db 1 ; sector 1 dw 3000h ; load address ; ; print CP/M prompt message and then load disk on input of a "cr" ; cpmboot:lxi h,cpmms ; print CP/M load message call pmsg call conin ; get ready status cpi cr ; see if a cr jnz iboot ; start all over if not CP/M boot lxi h,crlf ; print carriage return linefeed call pmsg ; ; force interrupt ; mvi a,0d0h ; force interrupt command out dcom in dstat ; clear interrupt ; ; setup scratch area ; lxi h,0ffffh shld trtab ; preset track table shld trtab+2 mvi a,4 ; set up initial out of range disk sta diskno xra a call seldsk ; select drive 0 ; ; boot normal CP/M track zero sector 1 into ram at zero and ; jump to it to get in rest of CP/M ; lxi h,cpmpb ; point to boot parameter block call cmmd78 ; read in disk ora a jz 0000h ; if no errors goto CP/M loader lxi h,bmsg call pmsg ; print boot error message jmp iboot ; if error then go try for another command ; ; iopb for booting CP/M track 0 sector 1 into 0000h ; cpmpb: db 80h ; iocw, no update bit set db 04h ; I/O instruction, read disk 0 db 01 ; read one sector db 0 ; track 0 db 1 ; sector 1 dw 0000h ; load address ; ; hardware interface I/O routines for ISIS bios software ; ; check console input status ; const: in cstat ; read console status ani ckbr mvi a,0 ; set a=0 rnz ; return if not ready with a=0 cma ret ; ; read a character from console ; conin: in cstat ; read console status ani ckbr ; if not ready jnz conin ; keep waiting in cdata ; read a character ani 7fh ; make most sig bit=0 ret ; ; write a character to the console device ; conout: in cstat ; read console status ani cptr ; if not ready, jnz conout ; keep waiting mov a,c ; get character out cdata ; print it ret ; ; write a character to the list device ; list: in lstat ; read list port status ani lptr ; if not ready, jz list ; then keep waiting mov a,c ; get character out ldata ; print it ret ; ; select disk number according to register a ; seldsk: lxi h,diskno ; get addr of old disk no cmp m ; new=old? mov c,a ; c=new drive jz seld1 ; ; if changing to a new drive perform a seek to the same track ; this is an undocumented procedure which unloads the head ; in track ; get the present track out ddata ; put in 1771 data reg mvi a,10h ; seek command out dcom ; seek same track in wait ; ; get old track number from 1771 and put into table ; mov e,m ; e=old disk no mvi d,0 ; clear d for dad lxi h,trtab ; point to table dad d ; index into table in track ; read 1771 track reg mov m,a ; put into table ; ; get new drive track number from table and put in 1771 ; mov e,c ; e=new disk no mvi d,0 ; clear d for dad lxi h,trtab ; point to table dad d ; index into table mov a,m ; get new track number out track ; update 1771 track reg ; ; update diskno and select new drive ; seld1: mov a,c ; update old disk number sta diskno cma add a ; put bits 1 and 2 at 4 and 5 add a add a add a ori 2 sta latch ; update latch out dcont ; select drive ; ; if table entry for this drive is ff then this drive is not logged ; and we must do a recalibrate before returning from seldsk ; mov a,m ; get track from table inr a ; if 0ffh then recalibrate rnz ; ; move disk to track zero ; home: mvi a,stprat+hlab ; restore command out dcom ; to controller in wait ; wait for restore complete in dstat ; read status ani 91h ; look at these bits rz ; return good mvi a,00000100b ; seek error ret ; return bad ; ; seek track specified in iopb ; seek: lda itrack ; get track # from iopb cpi 77 ; more than 77 ? jnc adrerr ; error if more mov b,a ; b=destination track in track ; a= present track no mov c,a ; c= present track also cmp b ; dest=present ?? rz ; return if same ; ; this routine is to allow time for the drive ; tunnel erase to terminate before moving the ; head the delay is approx 700 micro-sec @ ; 4 mhz cpu time, and double this for 2 mhz cpu's ; push psw mvi a,208 ; delay count busy1: dcr a ; count=count-1 jnz busy1 ; loop til count=0 pop psw mov a,b ; a=destination track out ddata ; track to data register busy: in dstat ; read disk status rrc ; look at bit 0 jc busy ; wait till not busy mvi a,10h+stprat+hlab ; get step rate and head load out dcom ; do seek in wait ; wait for seek done in dstat ; read controller status ani 91h ; look at these bits rz ; return good mvi a,00000100b ; seek error ret ; return bad ; ; hdld-get head-load bit if required ; hdld: in dstat ; read 1771 status ani 20h ; look at hl bit jz hdldy ; load if not loaded xra a ; otherwise, a=0 ret ; return from hdld hdldy: mvi a,4 ; set bit to load head ret ; return from hdld ; ; write sector or sectors specified in iopb ; iwrite: call seek ; check params and seek track rnz ; return if seek error mvi c,20h ; set (c) to write code jmp ddio ; go do common I/O ; ; verify sector or sectors specified in iopb ; iverf: call seek ; check parms and perform seek rnz ; return if seek error mvi c,080h ; set verify function bit jmp ddio ; go do common I/O ; ; read sector or sectors specified in iopb ; iread: call seek ; check params and seek track rnz ; return if seek error mvi c,0 ; set read function code and ; fall through to common I/O ; ; do disk common operations ; ddio: lhld dmaadd ; get dma address in h and l mvi a,0d0h out dcom ; force interupt lda sect ; get iopb sector ani 1fh ; strip select bit cpi 27 ; more than 26 ? jnc adrerr ; error if more out sectp ; sector to 1771 call hdld ; head loaded ? adi 88h push b ; save function byte in (c) push psw ; strip possible verify bit off (c) mov a,c ani 07fh mov c,a pop psw ora c ; read/write out dcom ; command to 1771 pop b mov a,c ; get read/write cpi 020h ; check for write jz wloop ; if 020h then write cpi 080h ; check for verify jz vloop ; if 080h then verify ; must be read if not write/verify ; ; read loop ; else read ; rloop: in wait ; wait for drq or intrq ora a ; set flags jp rdone ; if p then was intrq, done in ddata ; otherwise drq, get byte poke: mov m,a ; put into memory inx h ; bump memory pointer jmp rloop ; continue reading ; ; read exit ; rdone: in dstat ; get status ani 9dh ; look at these error bits jnz dskerr ; fall through to next record check ; ; next record of multiple record check ; nxtrec: lxi h,numrec ; point to numrec in iopb dcr m ; numrec=numrec-1 rz ; return if numrec=0 lhld dmaadd ; else get dma address lxi d,128 dad d ; hl=hl+128 shld dmaadd ; update dma address lxi h,sect ; point to sector # in iopb inr m ; sect=sect+1 mov a,m cpi 27 ; greater than 26 ?? jc ddio ; continue if range ok jmp adrerr ; otherwise error ; ; verify loop ; vloop: in wait ; wait for drq or intrq ora a ; set flags jp vdone ; if p then was intrq and done in ddata ; otherwise drq so get data ; we wont use data if a verify op inx h ; do pointer anyway jmp vloop ; go for next byte ; ; verify exit ; vdone: in dstat ; get operation status ani 09dh ; mask appropiate bits jnz dskerr ; go to error tree if I/O error jmp nxtrec ; see if more records to do ; ; write loop ; wloop: in wait ; wait for drq or intrq ora a ; set flags jp wdone ; if p then was intrq, done mov a,m ; get a byte from memory out ddata ; send to 1771 inx h ; bump memory pointer jmp wloop ; continue writing ; ; write done ; wdone: in dstat ; read status ani 0fdh ; look at these error bits jz nxtrec ; see if more records ; ; disk I/O error sorting tree that converts 1771 error codes into ; those required to emulate an INTEL disk controller ; dskerr: cpi 10000000b ; test for not ready jz rdyerr cpi 01000000b ; test for write protect jz wprot cpi 00000100b ; test for lost data jz losdat cpi 00011000b ; test for id crc error jz idcrc cpi 00010000b ; test for record not found jz idcrc ; lets say all else is crc crcerr: mvi a,00000010b ; make code for crc error ret ; wprot: mvi a,00100000b ; make code for write protect ret ; rdyerr: mvi a,10000000b ; make code for not ready ret ; losdat: mvi a,00010000b ; make code for lost data ret ; idcrc: mvi a,00001010b ; make code for id crc ret ; adrerr: mvi a,00001000b ; make code for adress error ret ; ; format a track-this routine here due to fact that INTEL controller ; directly supports a track format operation for the "idisk" command ; ifrmat: in dstat ; read disk status ani 01000000b jnz wprot ; error if write protected call seek ; seek desired track lda chword ; get channel word ani 01000000b ; look at format sequence jnz random ; go setup table for random ; ; sequential format-fill table with fill characters and sequential ; sector numbers fill byte pointed to by dmaadd ; seq: lhld dmaadd ; point to fill byte mov d,m ; put fill byte into (d) mvi a,01h ; start sector number lxi h,fmttab ; point to format table seqlp: mov m,a ; put sector into table inx h mov m,d ; put fill char into the table inx h inr a ; increment sector number cpi 27 ; table all filled yet? jnz seqlp jmp seq1 ; go start format ; ; random format-fill table with sector and fill bytes that was ; built by caller in the iopb pointed data buffer ; random: lhld dmaadd ; point to callers table mvi b,52 ; number of entries to move across lxi d,fmttab ; point to our table randlp: mov a,m ; get users byte stax d ; put into our table inx h ; do pointer adjust for loop inx d dcr b ; moved all bytes yet? jnz randlp seq1: lxi h,fmttab ; point to format order table lda itrack ; get track number mov c,a ; put it in c mvi e,26 ; set max # sectors mvi b,40 ; gap 4 preindex 40 bytes of 0ffh mvi a,0f4h ; load track write command out dcom ; issue track write ; ; write preindex fill ; preind: in wait ; wait for drq ora a ; set flags jp fmterr ; jmp out if error mvi a,0ffh ; byte=0ffh out ddata ; write it on disk dcr b ; count=count-1 jnz preind ; loop til count=0 mvi b,6 ; count=6 prein1: in wait ; wait for drq ora a ; set flags jp fmterr ; jmp out if error xra a ; byte=0 out ddata ; write it on disk dcr b ; count=count-1 jnz prein1 ; loop til count=0 ; ; write address mark on track ; in wait ; wait for drq ora a ; set flags jp fmterr ; jmp out if error mvi a,0fch ; load address mark out ddata ; write it on disk ; ; post index gap ; postgap:mvi b,26 ; count=26 postid: in wait ; wait for drq ora a ; set flags jp fmterr ; jmp out if error mvi a,0ffh ; byte=0ffh out ddata ; write it on disk dcr b ; count=count-1 jnz postid ; loop til count=0 ; ; pre id section ; asect: mvi b,6 ; count=6 sector: in wait ; wait for drq ora a ; set flags jp fmterr ; jmp out if error xra a ; byte=0 out ddata ; write it on track dcr b ; count=count-1 jnz sector ; loop til count=0 ; ; write id address mark ; in wait ; wait for drq ora a ; set flags jp fmterr ; if error jmp out mvi a,0feh ; get address mark out ddata ; write it on disk ; ; write track number on disk ; in wait ; wait for drq ora a ; set flags jp fmterr ; jmp out if error mov a,c ; get track number out ddata ; write it on disk ; ; write one byte of 00 ; in wait ; wait for drq ora a ; set flags jp fmterr ; jmp out if error xra a ; byte=0 out ddata ; write it on disk ; ; write sector # on disk ; in wait ; wait for drq ora a ; set flags jp fmterr ; jmp out if error mov a,m ; get sector # out ddata ; write it on disk ; ; increment table pointer to next byte to point to sector fill byte ; inx h ; point to fill byte ; ; one more byte 0 ; in wait ; wait for drq ora a ; set flags jp fmterr ; jmp out if error xra a ; byte=0 out ddata ; write it on disk inr d ; bump sector count ; ; write 2 crc's on this sector ; in wait ; wait for drq ora a ; set flags jp fmterr ; jmp out if error mvi a,0f7h ; get crc pattern out ddata ; write it on disk ; ; get sector fill byte to (d) and inx table pointer for next sector ; mov d,m ; get fill byte inx h ; point to next sector number ; ; pre data 17 bytes 00 ; mvi b,11 ; count=11 predat: in wait ; wait for drq ora a ; set flags jp fmterr ; jmp out if error mvi a,0ffh ; byte=0ffh out ddata ; write it on disk dcr b ; count=count-1 jnz predat ; loop til count=0 mvi b,6 ; count=6 preda1: in wait ; wait for drq ora a ; set flags jp fmterr ; jmp out if error xra a ; byte=0 out ddata ; write it to disk dcr b ; count=count-1 jnz preda1 ; loop til count=0 ; ; data address mark ; in wait ; wait for drq ora a ; set flags jp fmterr ; jmp out if error mvi a,0fbh ; get data address mark out ddata ; write it on disk ; ; fill data field with data from table ; mvi b,128 ; count=128 dfill: in wait ; wait for drq ora a ; set flags jp fmterr ; jmp out if error mov a,d ; get fill byte out ddata ; write it on disk dcr b ; count=count-1 jnz dfill ; loop til count=0 ; ; write crc's ; in wait ; wait till drq ora a ; set flags jp fmterr ; jmp out if error mvi a,0f7h ; get crc byte out ddata ; write it on disk ; ; end of sector fill ; dcr e ; sectorcount=sectorcount-1 jz endtrk ; jmp if sectorcount=0 datgap: in wait ; wait for drq ora a ; set flags jp fmterr ; jmp out if error mvi a,0ffh ; byte=0ffh out ddata ; write it on disk jmp postgap ; do next sector ; ; do track and sector house keeping ; endtrk: in wait ; wait for drq or intrq ora a ; set flags jp fdone ; jmp out if error mvi a,0ffh ; load a with 0ffh out ddata ; write it on disk jmp endtrk ; do until intrq ; ; format error sort routine ; fdone: in dstat ; read status ora a ; test for flag rz ; return if no errors fmterr: in dstat jmp dskerr ; error if not zero ; ; read a character from reader device ; reader: mvi a,1ah ; always return an eof char ; punch: ret ; punch is a do nothing output ; ; print data pointed to by h and l until a zero is found ; pmsg: mov a,m ; get character ora a ; set flags rz ; ret if char is a zero mov c,a ; put char in "c" call conout ; print character inx h ; bump message pointer jmp pmsg ; do another character ; ; bios messages ; stmsg: db cr,lf db 'ISIS 4.0 Bios, Ver.1.0 implemented by Kelly Smith' db cr,lf db 'Use "I" to boot ISIS disk or "C" to boot CP/M disk' db cr,lf,cr,lf,0 isisms: db cr,lf,cr,lf,'ISIS disk in drive A:, type return ',0 cpmms: db cr,lf,cr,lf,'CP/M disk in drive A:, type return ',0 ; crlf: db cr,lf,0 ; bmsg: db cr,lf,'+++Disk Boot Error+++',0 ; ; bios scratch area placed at top of ram in system uses max of 256 ; bytes in top ram page ; org memmax*256 ; set for top page of ram ; ; trtab: ds 5 ; track table diskno: ds 1 ; current disk number latch: ds 1 ; current latch code resbyt: ds 1 ; result byte ostack: ds 2 ; old stack storage iopb: ds 2 ; pointer to iopb ; ; copy of i/o parameter block ; ioblk: equ $ chword: ds 1 ; channel byte dskins: ds 1 ; disk instruction numrec: ds 1 ; number of records to do itrack: ds 1 ; track address sect: ds 1 ; current sector address dmaadd: ds 2 ; current r/w address fmttab: ds 52 ; storage for format table ds 20 ; stack space newstk: ds 1 ; end