title 'Customized Basic I/O System' ;********************************************* ;* * ;* This Customized BIOS adapts CP/M-86 to * ;* the following hardware configuration * ;* Processor: 8085/8088 Dual Processor * ;* Brand: CompuPro (Godbout) * ;* Controller: iCom 3712 * ;* * ;* * ;* Programmer: Bruce R. Ratoff * ;* Revisions : 04/30/81 20:40 * ;* * ;********************************************* true equ -1 false equ not true cr equ 0dh ;carriage return lf equ 0ah ;line feed ;********************************************* ;* * ;* Loader_bios is true if assembling the * ;* LOADER BIOS, otherwise BIOS is for the * ;* CPM.SYS file. * ;* * ;********************************************* loader_bios equ false bdos_int equ 224 ;reserved BDOS interrupt ;********************************************* ;* * ;* I/O Port Assignments * ;* * ;********************************************* ; ;Diskette interface (iCom 3712) ;Note: Port numbers are "doubled up" because iCom card ; counts on 8080 "address mirror" effect. datai equ 0c0c0h ;data/status input port datao equ 0c1c1h ;data output port cntrl equ 0c0c0h ;command output port ; ;Console interface (IMSAI SIO2-2 port 1) cstat equ 3 ;status cdata equ 2 ;data cimsk equ 2 ;input ready mask comsk equ 1 ;output ready mask ; ;Printer interface (IMSAI SIO2-2 port 2) lstat equ 5 ;status ldata equ 4 ;data lomsk equ 1 ;output ready mask limsk equ 2 ;input ready mask IF not loader_bios ;--------------------------------------------- ;| | bios_code equ 2500h ccp_offset equ 0000h bdos_ofst equ 0B06h ;BDOS entry point ;| | ;--------------------------------------------- ENDIF ;not loader_bios IF loader_bios ;--------------------------------------------- ;| | bios_code equ 1200h ;start of LDBIOS ccp_offset equ 0003h ;base of CPMLOADER bdos_ofst equ 0406h ;stripped BDOS entry ;| | ;--------------------------------------------- ENDIF ;loader_bios cseg org ccpoffset ccp: org bios_code ;********************************************* ;* * ;* BIOS Jump Vector for Individual Routines * ;* * ;********************************************* jmp INIT ;Enter from BOOT ROM or LOADER jmp WBOOT ;Arrive here from BDOS call 0 jmp CONST ;return console keyboard status jmp CONIN ;return console keyboard char jmp CONOUT ;write char to console device jmp LISTOUT ;write character to list device jmp PUNCH ;write character to punch device jmp READER ;return char from reader device jmp HOME ;move to trk 00 on cur sel drive jmp SELDSK ;select disk for next rd/write jmp SETTRK ;set track for next rd/write jmp SETSEC ;set sector for next rd/write jmp SETDMA ;set offset for user buff (DMA) jmp READ ;read a 128 byte sector jmp WRITE ;write a 128 byte sector jmp LISTST ;return list status jmp SECTRAN ;xlate logical->physical sector jmp SETDMAB ;set seg base for buff (DMA) jmp GETSEGT ;return offset of Mem Desc Table jmp GETIOBF ;return I/O map byte (IOBYTE) jmp SETIOBF ;set I/O map byte (IOBYTE) ;********************************************* ;* * ;* INIT Entry Point, Differs for LDBIOS and * ;* BIOS, according to "Loader_Bios" value * ;* * ;********************************************* INIT: ;print signon message and initialize hardware mov ax,cs ;we entered with a JMPF so use mov ss,ax ;CS: as the initial value of SS:, mov ds,ax ;DS:, mov es,ax ;and ES: ;use local stack during initialization mov sp,offset stkbase cld ;set forward direction IF not loader_bios ;--------------------------------------------- ;| | ; This is a BIOS for the CPM.SYS file. ; Setup all interrupt vectors in low ; memory to address trap push ds ;save the DS register mov IOBYTE,0 ;clear IOBYTE mov ax,0 mov ds,ax mov es,ax ;set ES and DS to zero ;setup interrupt 0 to address trap routine mov int0_offset,offset int_trap mov int0_segment,CS mov di,4 mov si,0 ;then propagate mov cx,510 ;trap vector to rep movs ax,ax ;all 256 interrupts ;BDOS offset to proper interrupt mov bdos_offset,bdos_ofst mov int0_offset,offset int0_trap mov int4_offset,offset int4_trap pop ds ;restore the DS register ; (additional CP/M-86 initialization) ;| | ;--------------------------------------------- ENDIF ;not loader_bios IF loader_bios ;--------------------------------------------- ;| | ;This is a BIOS for the LOADER push ds ;save data segment mov ax,0 mov ds,ax ;point to segment zero ;BDOS interrupt offset mov bdos_offset,bdos_ofst mov bdos_segment,CS ;bdos interrupt segment ; (additional LOADER initialization) pop ds ;restore data segment ;| | ;--------------------------------------------- ENDIF ;loader_bios mov bx,offset signon call pmsg ;print signon message mov cl,0 ;default to dr A: on coldstart jmp ccp ;jump to cold start entry of CCP WBOOT: jmp ccp+6 ;direct entry to CCP at command level IF not loader_bios ;--------------------------------------------- ;| | int0_trap: cli mov bx,offset int0_trp jmps int_halt int4_trap: cli mov bx,offset int4_trp jmps int_halt int_trap: cli ;block interrupts mov bx,offset int_trp int_halt: mov ax,cs mov ds,ax ;get our data segment call pmsg pop bx ;get offset pop ax ;print segment push bx ;save offset call PHEX mov cl,':' ;colon call CONOUT pop ax ;print offset call PHEX hlt ;hardstop PHEX: push ax mov al,ah call PHXB ;print upper byte pop ax ;restore to print lower byte PHXB: push ax ;save byte mov cl,4 ;get high nibble shr al,cl ;into low bits call PHXD ;print digit pop ax ;restore byte and al,0fh ;isolate low nibble PHXD: add al,90h ;first half of conversion trick daa add al,40h ;second half of same daa mov cl,al ;now print digit jmps CONOUT ;| | ;--------------------------------------------- ENDIF ;not loader_bios ;********************************************* ;* * ;* CP/M Character I/O Interface Routines * ;* * ;********************************************* CONST: ;console status in al,cstat ;get status byte and al,cimsk ;check input mask jz const1 ;not ready yet...return al=0, ZF=1 or al,0ffh ;ready...return al=0FFh, ZF=0 CONST1: ret CONIN: ;console input call CONST jz CONIN ;wait for RDA in al,cdata;get byte and al,7fh ;strip parity ret CONOUT: ;console output in al,cstat ;get status test al,comsk ;check output bits jz conout ;loop till ready mov al,cl ;setup out cdata,al ;send character ret ;then return data LISTOUT: ;list device output call LISTST ;get output status jz LISTOUT ;wait for TBE mov al,cl ;setup out ldata,al ;send char in al,lstat ;check for handshake received and al,limsk jz LISTOUT2 ;no handshake...exit in al,ldata ;get handshake char and al,7fh ;strip parity cmp al,'S'-40h ;XOFF? jnz LISTOUT2 ;nope mov lstactive,0ffh ;set list active flag LISTOUT2: ret LISTST: ;poll list status in al,lstat ;get status byte and al,lomsk ;test output bits jz LISTST1 ;not ready...exit with al=0, zf=1 mov al,lstactive ;line ready...waiting for XON? not al test al,al jnz LISTST1 ;not waiting...say ready in al,lstat ;check for handshake and al,limsk jz LISTST1 ;not yet...say still busy in al,ldata ;got something... and al,7fh ;strip parity cmp al,'Q'-40h ;is it XON? mov al,0 jnz LISTST1 ;no, return false not al ;ready...exit with al=0ffh, zf=0 mov lstactive,0 ;clear list active flag LISTST1: test al,al ;make sure flags are set ret PUNCH: ;write punch device ret ;is a "bit bucket" READER: mov al,1ah ;is an EOF source ret GETIOBF: mov al,IOBYTE ret SETIOBF: mov IOBYTE,cl ;set iobyte ret ;iobyte not implemented pmsg: mov al,[BX] ;get next char from message test al,al jz return ;if zero return mov CL,AL call CONOUT ;print it inc BX jmps pmsg ;next character and loop ;********************************************* ;* * ;* Disk Input/Output Routines * ;* * ;********************************************* SELDSK: ;select disk given by register CL ndisks equ 2 ;number of disks (up to 16) mov seekfg,0ffh ;set seek flag mov disk,cl ;save disk number mov bx,0000h ;ready for error return cmp cl,ndisks ;n beyond max disks? jnb return ;return if so mov ch,0 ;double(n) mov bx,cx ;bx = n mov cl,4 ;ready for *16 shl bx,cl ;n = n * 16 mov cx,offset dpbase add bx,cx ;dpbase + n * 16 return: ret ;bx = .dph HOME: ;move selected disk to home position (Track 0) mov cx,0 ;set disk i/o to track zero ;**** fall through **** SETTRK: ;set track address given by CX mov trk,CX mov seekfg,0ffh ;set seek flag ret SETSEC: ;set sector number given by cx mov sect,CX ret SECTRAN: ;translate sector CX using table at [DX] mov bx,cx add bx,dx ;add sector to tran table address mov bl,[bx] ;get logical sector ret SETDMA: ;set DMA offset given by CX mov dma_adr,CX ret SETDMAB: ;set DMA segment given by CX mov dma_seg,CX ret ; GETSEGT: ;return address of physical memory table mov bx,offset seg_table ret ;********************************************* ;* * ;* All disk I/O parameters are setup: * ;* DISK is disk number (SELDSK) * ;* TRK is track number (SETTRK) * ;* SECT is sector number (SETSEC) * ;* DMA_ADR is the DMA offset (SETDMA) * ;* DMA_SEG is the DMA segment (SETDMAB)* ;* READ reads the selected sector to the DMA* ;* address, and WRITE writes the data from * ;* the DMA address to the selected sector * ;* (return 00 if successful, 01 if perm err)* ;* * ;********************************************* READ: mov cl,10 ;set retry count READ1: call STUP ;set up unit/track/sector mov al,3 ;send read command call DLOOP mov dx,datai ;set port number in al,dx ;get back status test al,8 ;check CRC flag jz RDOK ;no error...go get data dec cl ;got an error...count retrys jnz READ2 ;some retrys left...continue mov al,1 ;bad news....return error ret READ2: test cl,3 ;time for a re-seek? jpo READ1 ;no, just reread mov seekfg,0ffh ;yes, set seek flag call RESET ;clear errors, home drive jmps READ1 ;try read again RDOK: mov cx,128 ;set byte counter cld ;set forward direction push es ;save extra segment les di,dword ptr dma_adr ;set dest index and segment mov dx,cntrl RDLUP: mov ax,40h ;send "examine read buffer" command out dx,al ;to disk control port in al,dx ;get data byte stos al ;store it, bump pointer and count mov al,41h ;send "step read buffer" command out dx,al ;to controller loop RDLUP ;repeat 128 times pop es ;restore extra segment mov al,0 ;return good status out dx,al ;also put controller in status mode ret WRITE: mov cx,128 ;set 128 byte counter cld ;set forward direction push ds ;save current data segment lds si,dword ptr dma_adr ;set source index WRLUP: lods al ;get next byte mov dx,datao out dx,al ;send to controller mov al,31h ;send "shift write buffer" command mov dx,cntrl out dx,al ;to controller mov al,0 ;remove command out dx,al ;(bit 0 must toggle to be seen) loop WRLUP ;repeat for sector length times pop ds RTRYP: call STUP ;setup for write in al,dx ;check controller status test al,10h ;write protected? jz TRYWR ;no, continue mov bx,offset prtmsg ;say "protected" call ERROR ;and wait for user action jmps RTRYP ;retry if user hits return key TRYWR: mov al,5 ;send write command call DLOOP ;to controller with wait WROK: mov al,0 ;return good status ret ;********************************************* ;* * ;* Disk Utility Routines * ;* * ;********************************************* ; ;print an error message and wait for user response ;if control-c, then abort to cp/m, else return ;to caller and (usually) retry operation ERROR: call PMSG ;print an error message call CONIN ;wait for user response push ax ;save character mov bx,offset crlf ;echo cr, lf call PMSG pop ax ;now look at char cmp al,3 ;control-c? jz ERR1 ;yes, return to cp/m ret ;else retry error'd operation ERR1: mov cl,0 ;tell cp/m user 0, drive A jmp ccp ;bye-bye ;Perform select and possibly seek logic for either a ;read or write operation. STUP: mov al,0bh ;issue "reset errors" command call DLOOP ;to controller with wait mov al,disk ;get drive number mov cl,6 ;prepare to shift into shl ax,cl ;high 2 bits of cmd byte or ax,sect ;put sector number in low bits mov dx,datao out dx,al ;send to controller mov al,21h ;issue "set unit/sector" command call DLOOP mov dx,datai mov cx,100 ;set up delay loop STUP0: mov bx,8000 ;inner delay loop STUP1: in al,dx ;get controller status test al,20h ;check "drive fail" (ready) flag jz STUP2 ;no problem...continue dec bx ;count down inner delay loop jnz STUP1 loop STUP0 ;count down outer delay loop mov bx,offset rdymsg ;timed out...complain call error ;and wait for response jmps STUP ;retry the whole mess STUP2: mov al,0 ;clear seek flag xchg al,seekfg ;and fetch previous value test al,al ;was it set? jnz stup3 ;yes, go do seek or home ret ;no seek needed...exit STUP3: mov ax,trk ;look at track number test al,al ;is it 0? jz RESET ;yes, do a home mov dx,datao out dx,al ;otherwise, set new track mov al,11h ;give "set track" command call DLOOP mov al,9 ;then give "seek" command ;**** fall through **** ; ;This routine issues a controller command and waits for completion DLOOP: mov dx,cntrl out dx,al ;send command mov al,0 ;strobe it off out dx,al LOOP1: in al,dx ;get controller status test al,1 ;check ready bit jnz LOOP1 ;loop till ready ret ;then exit ; ;This routine issues a "clear" command followed by a "home" command RESET: mov al,81h ;send "clear" call DLOOP mov al,0dh ;send "home" jmps DLOOP ;********************************************* ;* * ;* Data Areas * ;* * ;********************************************* data_offset equ offset $ dseg org data_offset ;contiguous with code segment lstactive db 0 ;set if list handshake active IOBYTE db 0 ;i/o assignments (unused at present) seekfg db 0 ;set to 0ffh if next access requires seek disk db 0 ;disk number trk dw 0 ;track number sect dw 0 ;sector number dma_adr dw 0 ;DMA offset from DS dma_seg dw 0 ;DMA Base Segment signon db cr,lf,cr,lf db 'CP/M-86 Version 1.0 for iCom 3712',cr,lf db 'System Generated 04/30/81' db cr,lf,0 int_trp db cr,lf db 'Interrupt Trap Halt at ',0 int0_trp db cr,lf db 'Divide Trap Halt at ',0 int4_trp db cr,lf db 'Overflow Trap Halt at ',0 rdymsg db cr,lf db 'Drive not ready',0 prtmsg db cr,lf db 'Drive write protected',0 crlf db cr,lf,0 ; System Memory Segment Table segtable db 1 ;1 segment dw tpa_seg ;1st seg starts after BIOS dw tpa_len ;and extends to 0ffff include singles.lib ;read in disk definitions loc_stk rw 32 ;local stack for initialization stkbase equ offset $ lastoff equ offset $ tpa_seg equ (lastoff+0400h+15) / 16 tpa_len equ 0fffh - tpa_seg ; 64K less 16 byte reset vector less cp/m size db 0 ;fill last address for GENCMD ;********************************************* ;* * ;* Dummy Data Section * ;* * ;********************************************* dseg 0 ;absolute low memory org 0 ;(interrupt vectors) int0_offset rw 1 int0_segment rw 1 ; pad to overflow trap vector rw 6 int4_offset rw 1 int4_segment rw 1 ; pad to system call vector rw 2*(bdos_int-5) bdos_offset rw 1 bdos_segment rw 1 END