;********************************************* ;this file is included in M4LDBIOS or M4CBIOS ;to generate a complete BIOS for M4LOADER ;or CPM.SYS respectively ;************************************************ ;* * ;* M-Four Basic I/O System (M4BIOS) * ;* * ;* Version: 21 Date: 24 May 1983 * ;* * ;* For CP/M version 1.1 * ;* * ;************************************************ ;********************************************* ;* * ;* Loader_bios is true if assembling the * ;* LOADER BIOS, otherwise BIOS is for the * ;* CPM.SYS file. * ;* * ;********************************************* IF NOT cal keybd_int_typ equ 65h ;keyboard int. (parallel) ENDIF IF cal keybd_int_typ equ 60h ENDIF bdos_int equ 224 ;reserved BDOS interrupt dpblen equ 24 xon equ 11h xoff equ 13h cntrl_Z equ 26 flop_flags equ .45aah ;only used by special version flop_step_rate equ .45adh ;ditto 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 bdos_ofst equ 0406h ;stripped BDOS entry IF NOT mpm ccp_offset equ 0003h ;base of CPMLOADER ENDIF IF mpm ccp_offset equ 0103h ;base of MPMLDR ENDIF ;| | ;--------------------------------------------- ENDIF ;loader_bios ;********************************************* ;* * ;* Dummy Data Section * ;* * ;********************************************* dseg 0 ;absolute low memory org 0 ;(interrupt vectors) first equ $ int0_offset rw 1 int0_segment rw 1 org 4 * 4 int4_offset rw 1 int4_segment rw 1 org 61h * 4 crt_int_offset rw 1 crt_int_segment rw 1 org 64h * 4 clock_int_offset rw 1 org keybd_int_typ * 4 keybd_int_offset rw 1 org 67h * 4 printer_int_offset rw 1 org bdos_int * 4 bdos_int_offset rw 1 bdos_int_segment rw 1 org 0400h msdos_jmp rb 128 debug_wrksp rb 256 prom_wrksp rb 128 nvram rb 128 keytab rb 512 cseg ;******************************************************************** ; equates / locations in cpm to be patched/used at run time ; to run cpm 80 com files. ccp_file equ .0827h ; filename to be loaded ccp_file_type equ .0831h ; filetype of ^ ccp_file_rec equ .0847h ; record count of ^ ccp_file_ext equ .0833h ; extent number of ^ com_patch equ .0727h ; address in ccp to be patched to force ; jump to bios routine to check for .com ; file if not .cmd file. com_desc equ .000ah ; location of command line descriptor com_line equ .000bh ; location of command line cpm 86, as input ccp_parse_patch equ .07a0h ; here is put a temporary ret to end ccp parse ; routine org 00edh ccp_open: ; routine to open a file org 46ch ccp_cleanup: ; cleanup routine to be called before running ; cpm80, or if allocation of z80 bank/ load ; of cpm80 file fail. org 3deh ccp_exit: ; back to cpm86 command level org 732h ccp_cmd: ; back to load .CMD file org 738h ccp_parse: ; routine to parse command line to fcb1/fcb2 ; + dma buffer org 072ch ccp_error: ; routine to give cpm86 command error ;*************************************************************************** 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 GETIOB ;Return I/O map byte (IOBYTE) JMP SETIOB ;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 int 3 ; DEBUG mov ax,cs ;we entered with a JMPF so use mov ds,ax ;CS: as the initial value of DS:, mov es,ax ;ES: ;use local stack during initialization mov ss,ax mov sp,offset stkbase IF loader_bios ;--------------------------------------------- ;| | push bp ;tells which boot disc ;| | ;--------------------------------------------- ENDIF IF not loader_bios ;--------------------------------------------- ;| | ; This is a BIOS for the CPM.SYS file. mov ax,cs mov kstat_ptr_seg,ax ; bios is segment relocatable mov iobyte_ptr_seg,ax mov cl,4 shl ax,cl add fn_expand_ptr,ax ; so bios is segment relocatable ; establish if 128 or 256 k system push es mov ax,1000h mov es,ax mov di,0ffffh ; write to top of 128k,55 mov es:byte ptr [di],55h mov ax,3000h mov es,ax mov es:byte ptr [di],00 ; write to top of 256,0 mov ax,1000h mov es,ax cmp es:byte ptr [di],55h ; see if 128 top changed pop es ; top 128 mirrored to bottom if absent je big_sys ; must be 256 k system sub seg_len,2000h mov si,offset size_txt_small ; amend signon mov di,offset size_txt mov cx,3 cld rep movs al,al ; replace 256k with 128k at sign-on big_sys: ; Print firmware revision number push ds mov ax,0fc00h mov ds,ax mov al,byte ptr .3fffh pop ds aam add ax,3030h xchg al,ah mov word ptr firm_rev,ax ;Give sign-on message mov bx,offset signon ;print sign on message call pmsg ; Patch cpm to include routine to test for .com files mov byte ptr com_patch,0e9h ;jump instruction mov word ptr com_patch+1,offset cmd_chk - (offset com_patch + 3) ;| | ;---------------------------------------- ENDIF ;not loader_bios ;If Winchester present then find CP/M partitions callf get_switch ; test al,bit2 ;8" drives? ; jz not_8_in ; mov buffer_size,8 * 1024 ;ensure buffer big enough ;not_8_in: test al,bit3 jz no_winch mov dx,1 ;read sector 1 mov cx,1 mov bx,offset part_buf mov al,20h callf hdsk_all jz hd_read_ok mov cl,true ;indicate hard disc access call print_error mov bx,offset part_err_msg call pmsg jmps $ hd_read_ok: mov si,offset part_buf mov di,offset dpb_7 call set_size mov si,offset part_buf + 2 mov di,offset dpb_8 call set_size no_winch: xor bx,bx push ds ;save the DS register mov ds,bx IF NOT loader_bios ;------------------------------------------------ ;| | push es mov es,bx mov int0_segment,cs mov int0_offset,offset div_trap mov si,offset int4_offset mov word ptr [si],offset int_trap mov 2[si],cs mov cx,512 - 10 lea di,4[si] pushf push crt_int_offset push crt_int_segment cld cli rep movsw pop crt_int_segment pop crt_int_offset popf pop es mov clock_int_offset,offset clock_int mov keybd_int_offset,offset keybd_int mov printer_int_offset,offset printer_int ;| | ;------------------------------------------------ ENDIF ;not loader_bios ; BDOS offset to proper interrupt mov bdos_int_offset,bdos_ofst mov bdos_int_segment,cs ;for loader bios pop ds ;restore the DS register IF (NOT cal) AND (NOT loader_bios) ;------------------------------------------------ ;| | ;clear type-ahead buffer key_buf_test: mov dl,0 callf sio_in_stat jz key_buf_clear ; if no chars in buffer callf sio_in jmps key_buf_test key_buf_clear: mov al,5 out kybd_cntl,al ; enable keyboard irq cli in al,pic_imr and al,not bit5 ; enable its level out pic_imr,al sti ;| | ;------------------------------------------------ ENDIF ;(not cal) and (not loader_bios) IF cal AND (NOT loader_bios) ;------------------------------------------------ ;| | mov dx,sio_0b_stat mov al,12h out dx,al mov al,0 out dx,al ;set up dart vector mov al,11h out dx,al mov al,1ch out dx,al ;status affects vector & enable rx interrupt cli in al,pic_imr and al,not bit0 out pic_imr,al sti ;| | ;------------------------------------------------ ENDIF ;cal and (not loader_bios) ; set up dph's & dpb's callf get_switch mov ah,al and al,1111b cmp al,0101b ; double sided 8", no Winchester ? mov al,ah jne ds8 ; if no ;If d.s. 8 inch and no Winchester, test for double sided disc actually present in al,kybd_stat test al,bit3 mov al,ah jz ds8 ;8" ds there and al,not bit0 ;8" ss there ds8: IF NOT loader_bios and ax,1fh ENDIF IF loader_bios pop bp cmp bp,offset hdsk_all mov dx,07h ;mask all but 1st floppy jne not_hdsk_boot mov dx,08h ;mask all but Winchester not_hdsk_boot: and ax,dx ENDIF mov bx,offset avail_tab scan_sys: cmp word ptr [bx],-1 je not_recog cmp [bx],ax je sys_recog add bx,22 jmps scan_sys not_recog: mov bx,offset unrecd_tx ;unrecognised system type call pmsg jmps $ ;hard stop sys_recog: lea si,2[bx] mov dx,offset bdos_scr ; mov dx,offset hst_buf ; add dx,buffer_size ;get address if 1st free byte mov bx,offset dph_a mov di,offset dpb_a xor cl,cl set_hdr_loop: call set_header add si,4 ;length of AVAIL_TAB entry add di,dpblen ;length of DPB add bx,16 ;length of DPH inc cl cmp cl,5 jb set_hdr_loop ; dx = offset of first free byte add dx,15 ;round up to paragraph mov cl,4 shr dx,cl ;cvt to paragraph mov ax,cs add ax,dx mov seg_start,ax ;store 1st free para in seg table sub seg_len,ax ;and update free space IF NOT loader_bios ;------------------------------------------------ ;| | push es ; clear keyboard table xor ax,ax mov es,ax mov di,offset keytab mov cx,length keytab mov al,8dh rep stos al ; Get address of system data area mov cl,49 int 224 mov word ptr sysdat_offset,bx mov word ptr sysdat_segment,es ; Enable clock interrupt (if clock present) mov dx,010ah in al,dx or al,0fh ;max period (500 ms) out dx,al inc dx mov al,01000110b out dx,al ;24h,enable interrupt cli in al,pic_imr and al,not bit4 out pic_imr,al sti pop es ;| | ;------------------------------------------------ ENDIF mov cl,0 ;default to dr A: on coldstart jmp ccp ;jump to cold start entry of CCP ;Routine to set up drive parameter header set_header: xor ax,ax mov [bx],ax mov 2[bx],ax mov 4[bx],ax mov 6[bx],ax push si mov si,[si] test si,si jz null_header test byte ptr 15[si],bit3 ;single density ? jz set_hdr_1 mov [bx],offset xlt26 ;if yes set_hdr_1: mov 8[bx],offset dirbuf mov 10[bx],di mov 12[bx],dx add dx,11[si] mov 14[bx],dx mov ax,5[si] ;get disk size add ax,15 ;to round up push cx mov cl,3 shr ax,cl ;divide by 8 to get alloc vec size add dx,ax push di mov cx,dpblen ;max. dpb length cld rep movs al,al pop di pop cx pop si mov ax,2[si] mov 18[di],al ;set drive number IF NOT loader_bios ;---------------------------------------- ;| | call print_drive ;| | ;---------------------------------------- ENDIF ;not loader_bios ret null_header: mov word ptr 10[bx],0 pop si ret ; Routine to set size of hard disc partition in DPB ; Enter with SI pointing to partition table and DI to DPB set_size: push si push di callf hdsk_size ;sectors/track to CL ; mov ax,512 ; mov ch,0 ; mul cx ;get required buffer size ; cmp ax,buffer_size ; jbe no_set_bufsize ; mov buffer_size,ax ;no_set_bufsize: pop di pop si mov 17[di],cl ;store sectors/track mov ax,cx shl ax,1 shl ax,1 ;get CP/M sectors/track mov [di],ax mov ax,[si] ;get track offset of partition mul cx ;convert to sector mov 20[di],ax ;save sector offset mov ax,2[si] ;start of next partition sub ax,[si] ;get partition size dec ax ;less 1 reserved track js no_part mul cx ;get no of 512b sectors mov cl,4 shr ax,cl ;convert 512b sectors to 8K blocks dec ax ;get last available block no. mov 5[di],ax ;store number of free blocks test ah,ah ;DSM > 255? jnz exm_3_ok mov byte ptr 4[di],7 ;reset EXM from 3 to 7 exm_3_ok: ret no_part: mov bx,offset no_part_msg call pmsg jmps $ IF NOT loader_bios ;---------------------------------------- ;| | ; print drive name and type ; cl= drive name (A..), di= associated dpb print_drive: push bx push cx push si push di add cl,'A' call conout mov cl,':' call conout mov cl,' ' call conout pop di push di mov bx,offset win_tx test dpb_flags[di],bit4 ;see if disk is floppy or winch jnz chk4 ;skip if winchester test dpb_phy_disk[di],bit0 mov cl,'L' jz chk1 mov cl,'R' chk1: call conout mov bx,offset floptx call pmsg pop di push di mov al,dpb_flags[di] ;get flags IF NOT cal ;nested IF ;---------------------------------------- ;| | mov cl,'5' ;print disk size test al,bit2 jz boot5 mov cl,'8' boot5: push ax call conout ;print 5 or 8 mov bx,offset driv_tx call pmsg pop ax ;| | ;---------------------------------------- ENDIF test al,bit0 mov bx,offset single_tx jz chk2 mov bx,offset double_tx chk2: push ax call pmsg mov bx,offset sides_tx call pmsg pop ax test al,bit1 mov bx,offset quad_tx jnz chk3 test al,bit3 mov bx,offset double_tx jz chk3 mov bx,offset single_tx chk3: call pmsg mov bx,offset density_tx chk4: call pmsg pop di pop si pop cx pop bx ret ; from print_drive ; default divide overflow trap div_trap: mov bx,offset divide_error_tx jmps int_trap_1 ; trap for other uninitialised interrupts int_trap: mov bx,offset int_trap_tx int_trap_1: sti call pmsg mov cl,0 mov dl,0 int 224 ;exit to ccp ; Level 7 interrupt routine (8259A cleanup) printer_int: mov cs:ax_save_7,ax mov al,67h out pic_icw,al mov ax,cs:ax_save_7 iret ; Real time clock interrupt (frequency 2 Hz) clock_int: mov cs:ss_save,ss mov cs:sp_save,sp mov cs:clock_stack,ax mov ax,cs mov ss,ax mov sp,offset clock_stack sti push bx push cx push dx push si push di push bp push ds push es mov ds,ax ;AX still holds copy of CS mov dx,010ch in al,dx ; clear interrupt call gettim ; get the time /date jnz time_not_ok les di,dword ptr sysdat_offset lea di,32[di] ;point to TOD field mov ch,'/' mov al,month call dec2 mov al,day_no call dec2 mov ch,',' mov al,yr add al,80 ;year is from 1980 call dec2 mov ch,':' mov al,byte ptr hour call dec2 mov al,byte ptr minute call dec2 mov ch,',' mov al,byte ptr second call dec2 time_not_ok: cli mov al,64h out pic_icw,al ;eoi to PIC pop es pop ds pop bp pop di pop si pop dx pop cx pop bx pop ax cli mov ss,cs:ss_save mov sp,cs:sp_save iret ; Routine to get date and time from clock ; Returns Z if ok gettim: mov cx,1500 ; timeout loop mov dx,100h+10 ; adress clk_wait: in al,dx test al,10000000b ;bit7 loopnz clk_wait jz clk_ok or al,0ffh ; clock u/s: return al = ff ret clk_ok: mov dx,100h mov di,offset clock_temp read_time: ; loop round reading clock registers in al,dx mov [di],al inc di inc dx cmp dx,100h+10 jne read_time ; Test data for validity cmp second,59 ; check all clock inputs ja error ; 0 - 59 ok cmp minute,59 ja error ; 0 - 59 ok cmp hour,23 ja error ; 0 - 23 ok cmp day_no,1 jb error cmp day_no,31 ja error ; 1 - 31 ok cmp month,1 jb error cmp month,12 ja error ; 1 - 12 ok cmp yr,99 ja error ; 0 - 99 ok (1980 - 2079) xor ax,ax ret error: or ax,1 ; 1 is duf date return ret ; Routine to convert AL to 2-character ASCII number ; and store at [ES:DI] followed by character in CH ; DI is incremented by 3 dec2: cld mov ah,0 mov cl,10 div cl add al,'0' stos al mov al,ah add al,'0' stos al mov al,ch stos al ret ;| | ;------------------------------------------- ENDIF WBOOT: mov ax,cs mov ds,ax mov es,ax mov ss,ax mov sp,offset stkbase ;to prevent re-use of stack IF not loader_bios ;------------------------------------------------ ;| | call z80_abort ; cleanup z80 bank if needed ; write out host buffer if necessary cmp write_req,0 je no_flush call rw_command ;can't do much about errors here mov write_req,0 no_flush: ; No need to deselect host buffer - this is better done in SELDSK ;| | ;------------------------------------------------ ENDIF jmp ccp+6 ;direct entry to CCP at command level ;********************************************* ;* * ;* CP/M Character I/O Interface Routines * ;* * ;********************************************* ; ;IOBYTE descriptions and actions (what this bios does): ; ; Values for M-Four: ; ;CONSOLE FIELD (bits 0 & 1) ; 0 - Port 1 (TTY:) ; 1 - Monitor (CRT:) ; 2 - Port 2 (BAT:) ; 3 - Port 3 (UC1:) ; ;READER FIELD (bits 2 & 3) ; 0 - Port 1 (TTY:) ; 1 - Port 2 (PTR:) ; 2 - Port 3 (UR1:) ; 3 - Port 4 (UR2:) ; ;PUNCH FIELD (bits 4 & 5) ; 0 - Port 1 (TTY:) ; 1 - Port 2 (PUN:) ; 2 - Port 3 (UP1:) ; 3 - Port 4 (UP2:) ; ;LIST FIELD (bits 6 & 7) ; 0 - Port 1 (TTY:) ; 1 - Monitor (CRT:) ; 2 - Centronics (LPT:) ; 3 - Port 2 (UL1:) ; Values for Cal-PC: ; ;CONSOLE FIELD (bits 0 & 1) ; 0 - Port 2 (TTY:) ; 1 - Monitor (CRT:) ; 2 - Port 3 (BAT:) ; 3 - Port 2 (UC1:) ; ;READER FIELD (bits 2 & 3) ; 0 - Port 2 (TTY:) ; 1 - Port 3 (PTR:) ; 2 - Port 4 (UR1:) ; 3 - Null (UR2:) ; ;PUNCH FIELD (bits 4 & 5) ; 0 - Port 2 (TTY:) ; 1 - Port 3 (PUN:) ; 2 - Port 4 (UP1:) ; 3 - Null (UP2:) ; ;LIST FIELD (bits 6 & 7) ; 0 - Port 2 (TTY:) ; 1 - Monitor (CRT:) ; 2 - Centronics (LPT:) ; 3 - Port 3 (UL1:) ; Loader bios does not use IOBYTE or interrupts IF NOT loader_bios ;using nested IFs here ;------------------------------------------------ ;| | IF NOT cal console_tab db 1, 0, 2, 3 list_tab db 1, 0, 5, 2 ENDIF IF cal console_tab db 2, 0, 3, 2 list_tab db 2, 0, 5, 3 ENDIF ; service keyboard int keybd_int: mov cs:saved_ax,ax mov cs:saved_ds,ds mov cs:saved_bx,bx IF NOT cal ;---------------------------------------- ;| | in al,kybd_data ;| | ;---------------------------------------- ENDIF IF cal ;---------------------------------------- ;| | in al,sio_int_vec cmp al,04h je rec_chr_int cmp al,06h je rec_chr_int int 68h ;user interrupt jmps sio_int_eoi rec_chr_int: in al,sio0b_data ;| | ;---------------------------------------- ENDIF mov bx,cs mov ds,bx mov bx,kbuf_put_ptr inc bx cmp bx,offset kbuf_end jne no_putp_ovf mov bx,offset kbuf_start no_putp_ovf: cmp bx,kbuf_get_ptr je kbuf_full xchg bx,kbuf_put_ptr mov [bx],al lds bx,kstat_ptr mov byte ptr [bx],-1 kbuf_full: IF NOT cal ;---------------------------------------- ;| | mov al,65h out pic_icw,al ;| | ;---------------------------------------- ENDIF IF cal ;---------------------------------------- ;| | sio_int_ret: mov al,38h out sio0a_stat,al ;eio to DART sio_int_eoi: mov al,60h out pic_icw,al ;| | ;---------------------------------------- ENDIF mov bx,cs:saved_bx mov ds,cs:saved_ds mov ax,cs:saved_ax iret ;Console status routine ; Input: none ; Output: IF ready THEN AL:=0FFH ; ELSE AL:=00 CONST: push bx push ds lds bx,iobyte_ptr mov al,[bx] pop ds and al,11b ;get console bits from iobyte cmp al,01b jz fkybdst ;Here if (IOBYTE & 11b) <> 01b mov bx,offset console_tab xlat al push dx mov dl,al callf sio_in_stat pop dx mov al,0 jz const_out or al,-1 const_out: pop bx ret ;Here if (IOBYTE & 11b) = 01b fkybdst: push ds lds bx,kstat_ptr mov al,byte ptr [bx] pop ds pop bx ret ; Console input routine ; Input: none ; Output: AL:=char AND 07FH CONIN: push ds push bx lds bx,iobyte_ptr mov al,[bx] pop bx pop ds and al,11b ;get CONSOLE bits from IOBYTE cmp al,01b jz fkybdin ;Here if CONSOLE field <> 1 push bx mov bx,offset console_tab xlat al pop bx push dx mov dl,al callf sio_in pop dx and al,not bit7 ;mask parity bit ret ;Here if CONSOLE field = 01b ;Decode keyboard function keys fkybdin: push bx cmp fn_expand_flag,false ;jump if key not being decoded je no_dec_in_prog dec_in_prog: push ds xor bx,bx mov ds,bx ;DS := 0 mov bx,cs: fn_expand_ptr ;BX := pointer fn_decode: inc bx mov al,[bx] ;get character from bottom segment mov cs: fn_expand_ptr,bx ;store new pointer of char just got fn_decode2: pop ds test al,bit7 ;end of this key's data ? jz fkey_don ;return if not mov fn_expand_flag,false ;else reset fkybdin_out: call set_kstat fkey_don: pop bx and al,not bit7 ;and return ret no_dec_in_prog: wait_for_key: mov bx,kbuf_get_ptr cmp bx,kbuf_put_ptr jz wait_for_key mov al,[bx] inc bx cmp bx,offset kbuf_end jne no_getp_ovf mov bx,offset kbuf_start no_getp_ovf: mov kbuf_get_ptr,bx IF NOT cal ;---------------------------------------- ;| | test al,bit7 ;function key ? jz fkybdin_out ;return if not ;| | ;---------------------------------------- ENDIF IF cal ;---------------------------------------- ;| | mov bx,offset fn_key_tab mov ah,80h ;map function keys to 80h upwards fn_key_chk: cmp al,[bx] jz do_decode cmp byte ptr [bx],-1 jz fkybdin_out inc ah inc bx jmps fn_key_chk do_decode: mov al,ah ;| | ;---------------------------------------- ENDIF push ds push cx xor cx,cx mov ds,cx ;else DS := 0 mov bx,offset keytab -1 ;start at KEYTAB in bottom segment mov cx,length keytab ;for length KEYTAB chars and al,not bit7 ;mask function bit jz lookup_end ;skip if first function key mov ah,al ;else AH := fn. key no. fn_key_lookup: inc bx test byte ptr [bx],bit7 loopz fn_key_lookup jz fn_overflow dec ah jnz fn_key_lookup lookup_end: pop cx not cs: fn_expand_flag ;was false, now true. jmps fn_decode ;go get char from memory & return fn_overflow: pop cx mov al,bit7 ;here if past end of fn_table jmps fn_decode2 IF cal ;---------------------------------------- ;| | fn_key_tab db 80h,81h,82h,83h,84h,85h,86h,87h,88h db 8ah,8bh db 0b7h,0b9h db 0aah,0b1h,0b3h db 0b0h db -1 ;| | ;---------------------------------------- ENDIF ;Console output routine ; Input: CL:=char ; Output: none CONOUT: push bx push ds lds bx,iobyte_ptr mov al,[bx] pop ds conout0: and al,11b cmp al,1 jz mon_out ;Here if console field <> 1 mov bx,offset console_tab xlat al push dx mov dl,al sio_out_wait: call cpm_sio_out_stat ;don't use PROM as early ones don't test CTS or DTR jnz sio_out_wait mov al,cl callf sio_out ;PROM ok here pop dx pop bx ret ; Routine to test if SIO ready to transmit ; Like PROM routine but tests CTS and DTR ; Returns opposite flags, i.e. Z = ready to transmit cpm_sio_out_stat: push dx push bx mov bx,offset port_table -1 xchg ax,dx xlat al pop bx xchg ax,dx mov dh,0 mov al,10h out dx,al ;flush CTS/DSR fifo in al,dx and al,00101100b ;test for CTS, DSR and transmit buf empty cmp al,00101100b pop dx ret port_table db sio0b_stat, sio0a_stat, sio1b_stat, sio1a_stat ;Here if CONSOLE field = 1 mon_out: mov al,cl push dx mov dl,0 callf sio_out ;Send byte to screen pop dx pop bx ret ; List output routine ; Input: CL:=char ; Output: none LISTOUT: push ds lds bx,iobyte_ptr mov al,[bx] pop ds rol al,1 rol al,1 and al,11b cmp al,2 jz print_out ;Output to printer if 2 ; Here if output to VDU or serial port mov bx,offset list_tab xlat al mov dl,al list_wait: call list_stat jnz list_wait mov al,cl callf sio_out ret ; Here if output to Centronics port print_out: mov al,cl ;get data callf prntr_out ret ; Routine to get list device status for port DL ; Return Z if ready for output ; XON, XOFF recognised list_stat: cmp dl,0 jz list_stat_ret ;monitor always ready more_stat: callf sio_in_stat ;test for XON, XOFF jz test_stat ;if no received character callf sio_in and al,7fh cmp al,xon jz setx cmp al,xoff jne more_stat setx: mov xonxoff,al jmps more_stat test_stat: cmp xonxoff,xon jne list_stat_ret ;if XOFF received last call cpm_sio_out_stat ;test buffer empty, CTS, DCD list_stat_ret: ret ;List status routine ; Input: none ; Output: IF ready THEN AL:=0FFH ; ELSE AL:=00 LISTST: push ds lds bx,iobyte_ptr mov al,[bx] pop ds rol al,1 rol al,1 and al,11b cmp al,2 jz print_stat ;Jump if printer mov bx,offset list_tab xlat al mov dl,al call list_stat mov al,0 jnz listst_ret mov al,-1 listst_ret: ret ;Here if LIST field = 2 print_stat: callf prntr_st mov al,0 jz listst_ret_2 mov al,-1 listst_ret_2: ret ;Punch output routine ; Input: CL:=char ; Output: none PUNCH: push ds lds bx,iobyte_ptr mov al,[bx] pop ds ror al,1 ror al,1 ror al,1 ror al,1 and al,11 inc ax IF cal inc ax cmp al,4 ja punch_ret ENDIF mov dl,al mov al,cl callf sio_out punch_ret: ret ;Reader input routine ; Input: none ; Output: AL:=char AND 07FH READER: push ds lds bx,iobyte_ptr mov al,[bx] pop ds ror al,1 ror al,1 and al,11b inc ax IF cal inc ax cmp al,4 jbe rdr_1 mov al,cntrl_Z ret rdr_1: ENDIF mov dl,al callf sio_in and al,07fh ret ;| | ;------------------------------------------------ ENDIF ;not loader_bios IF loader_bios ;------------------------------------------------ ;| | CONST: push dx mov dl,0 callf sio_in_stat pop dx mov al,0 jz const_ret mov al,0ffh const_ret: ret CONIN: push dx mov dl,0 callf sio_in pop dx test al,bit7 jnz conin ;ignore function keys ret CONOUT: mov al,cl push dx mov dl,0 callf sio_out ;Send byte to screen pop dx LISTOUT: LISTST: PUNCH: READER: ret ;| | ;------------------------------------------------ ENDIF ;loader_bios ;Get IOBYTE ; Input: none ; Output: AL:=IOBYTE GETIOB: push ds lds bx,iobyte_ptr mov al,[bx] pop ds ret ;Set IOBYTE ; Input: CL:=IOBYTE ; Output: none SETIOB: push ds lds bx,iobyte_ptr mov al,[bx] mov [bx],cl pop ds xor al,cl test al,bit6+bit7 ;see if printer assignment changed jz no_set_xon mov xonxoff,xon no_set_xon: ret ;********************************************* ;* * ;* Disk Input/Output Routines * ;* * ;********************************************* SELDSK: ;select disk given by register CL test dl,bit0 ;1st time selected? jnz seldsk_1 ;if no cmp cl,hst_disk ;same drive as sector in buffer? jne seldsk_1 ;if no mov hst_disk,-1 ;deselect buffer IF NOT loader_bios ;---------------------------------------- ;| | cmp write_req,0 ;check no write was pending je seldsk_1 ; Error - write pending and disc may have been changed mov write_req,0 push cx mov bx,offset write_pending_tx call pmsg pop cx ;| | ;---------------------------------------- ENDIF seldsk_1: xor bx,bx ;ready for error return cmp cl,5 ;n beyond max disks? jnb return ;return if so mov al,cl ;save required disk mov req_disk,al ;store disk_no in M4 memory set_prm: cbw ;ah := 0 mov cl,4 ;multiply by 16 (length of dp_block) shl ax,cl add ax,offset dph_A ;add base of dpbase mov bx,ax ;bx := .dph mov ax,10[bx] ;get address of dpb test ax,ax ;drive exists ? jne return ;if yes mov bx,ax ;no, return 0 return: ret HOME: ;move selected disk to home position (Track 0) xor cx,cx ;set disk i/o to track zero ;drop through to SETTRK SETTRK: ;set track address given by CX mov req_trk,cx ret SETSEC: ;set sector number given by CL mov req_sect,cl ret SECTRAN: ;translate sector CX using table at [DX] mov bx,cx test dx,dx jz no_tran add bx,dx ;add sector to tran table address mov bl,[bx] ;get logical sector no_tran: 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 al,false jmps disc_tfr WRITE: mov write_type,cl ;save write information mov al,true disc_tfr: mov write_flag,al ;save read or write flag ; IF (hst_disc = req_disc) & (hst_trk = req_trk) ; THEN no read required mov al,hst_disk cmp al,req_disk jne rd_req mov ax,hst_trk cmp ax,req_trk je do_tfr rd_req: cmp write_req,0 ;skip if write not required je rd_req2 call rw_command mov write_req,0 jnz j_wr_err rd_req2: mov ax,req_trk ;hst_trk := req_trk mov hst_trk,ax mov al,req_disk ;hst_disc := req_disc mov hst_disk,al mov ah,0 ;reset m4prom in case CPM has read discs mov cl,4 shl ax,cl ;ax := ax * 16 add ax,offset dph_A mov bx,ax ;save start of header table mov bx,10[bx] ;get start of parameter block mov hst_dpb,bx mov al,dpb_flags[bx] ;get flags mov cx,dpb_ret_sidleng[bx] ;get retries + sideleng mov si,dpb_max_sectors[bx] ;get max_sectors mov bp,dpb_secsiz[bx] ;get sec_size test al,bit4 ;Winchester? jnz no_set ;if so test al,bit1 ;quad density? jnz do_set ;if yes xchg al,ah callf get_switch xchg al,ah test ah,bit1 ;quad density drives? jz do_set ;if no or al,bit4 ;read alternate tracks do_set: callf flop_set IF special ;---------------------------------------- ;| | mov si,0fc00h ;PROM segment push ds mov ds,si mov byte ptr flop_flags,al ;ensure 8"/5" set mov byte ptr flop_step_rate,3 ;set slow step rate in case 5" pop ds ;| | ;---------------------------------------- ENDIF no_set: call rw_command ;read since write_req = 0 j_wr_err: jnz rd_err do_tfr: mov al,req_sect mov ah,0 xchg al,ah shr ax,1 ;ax := req_sect * 128 add ax,offset hst_buff mov si,ax mov di,dma_adr mov cx,128 /2 push ds push es mov es,dma_seg cmp byte ptr write_flag,false je rw_move ;skip if read xchg si,di push es push ds pop es pop ds rw_move: cld rep movsw pop es pop ds cmp write_flag,false ;return if (write_flag = false) je rw_out_ok cmp write_req,2 jnb no_wr_inc mov si,hst_dpb mov al,dpb_secsiz_msb[si] ;al := 0,1,2,4 cmp al,4 jb flip4to3 dec ax ;if 4, al := 3 flip4to3: mov cl,al mov al,req_sect ;divide req_sect shr al,cl ;... by 1,2,4 or 8 cmp write_req,0 ;first write? je set_rw_sec ;if yes cmp al,hst_sect ;same sector as before? je no_wr_inc ;if yes ; fall thru to increment write_req if no set_rw_sec: mov hst_sect,al ;and store inc write_req no_wr_inc: cmp byte ptr write_type,1 ;write to directory ? jne rw_out_ok call rw_command mov write_req,0 jnz wr_err rw_out_ok: mov al,0 ret rd_err: wr_err: mov hst_disk,-1 ;deselect host buffer mov al,1 ret ; Routine to do actual read/write transfers ; Does a read if write_req is 0, otherwise a write ; Parameters used are hst_track, hst_sect, hst_dpb ; Error Correction will take place in this routine rw_command: mov si,hst_dpb ; Check here for SS/DS same as before IF (NOT special) AND (NOT cal) ;------------------------------------------------ ;| | test dpb_flags[si],bit4 jnz no_test ;don't check if winnie callf get_switch and al,101b cmp al,101b jne no_test ; dont check if not ds 8" cli in al,aux_cmd ; select side 0 and al,not bit6 ; to ensure can go ready out aux_cmd,al ; if single sided sti rwait: mov al,dpb_phy_disk[si] mov ah,0 xor dx,dx callf flopall ; select drive test ah,bit7 jnz rwait ; wait till ready in al,kybd_stat mov cl,3 shr al,cl xor al,dpb_flags[si] test al,1 jnz test_ok mov bx,offset change_msg call pmsg or al,-1 ret ;| | ;------------------------------------------------ ENDIF no_test: test_ok: rw_retry: mov si,hst_dpb ;in case here from retry mov cl,dpb_sidleng[si] ;1 physical track to read mov ch,0 mov ax,hst_trk mul cx mov dx,ax ;logsec := trk * (sects per trk) mov bx,offset hst_buff ; Check here for only 1 write needed cmp write_req,1 ;IF only 1 write jne whole_trk mov cx,1 ;THEN just do 1 sector mov al,hst_sect cbw add dx,ax ;remembering to update log sector no. push dx mul dpb_secsiz[si] ;corrupts dx pop dx add bx,ax ;update buffer address whole_trk: mov al,dpb_phy_disk[si] and al,0fh or al,20h ;read cmp write_req,0 je is_read xor al,50h XOR 20h ;write + verify is_read: test dpb_flags[si],bit4 jnz rw_winch callf flopall mov cl,false jnz first_err ret rw_winch: add dx,dpb_part_offset[si] ;add partition offset callf hdsk_all mov cl,true jnz first_err ret first_err: ;code here to give error, wait for key ;and repeat or exit with error ;CL is true if hard disc call print_err ;print error type here repeat: call flush mov bx,offset retry_msg call pmsg call conin ;get response push ax mov cl,al ;echo it call conout mov cl,cr ;print call conout pop ax and al,5fh ;check for Y/N cmp al,'Y' jne repeat1 mov bx,offset crlf call pmsg jmps rw_retry ;retry if "Y" repeat1: cmp al,'N' jne repeat IF NOT loader_bios ;---------------------------------------- ;| | call z80_abort ;| | ;---------------------------------------- ENDIF or al,0ffh ret ; Print first part of disc error message ; Called from INIT and PRINT_ERR ; CL is true if was a hard disc error print_error: push ax push cx mov bx,offset crlf call pmsg pop cx pop ax push ax and al,0f8h ;mask off retry count mov bx,offset floppy_error_tab cmp cl,true jne error_scan ;if floppy mov bx,offset hd_error_tab_1 cmp al,78h jnz error_scan ;if hard disc handshake error mov bx,offset hd_error_tab_2 mov al,ah ;controller gave error code and al,3fh error_scan: cmp al,[bx] jz got_error cmp byte ptr [bx], -1 jz got_error add bx,3 jmps error_scan got_error: mov bx,1[bx] ;get address of error text call pmsg mov bx,offset err_msg1 ;print ' Error (' call pmsg pop ax call print_ax_hex mov bx,offset err_msg2 ;print 'H) ' call pmsg ret ; Print full error message ; CL is true if hard disc print_err: call print_error cmp write_req,0 mov bx,offset write_msg jnz writing mov bx,offset read_msg writing: call pmsg ;print 'writ' or 'read' mov bx,offset drive_msg ;print 'ing drive ' call pmsg mov al,hst_disk add al,'A' mov cl,al call conout ret flush: call const test al,al jz end_flush call conin jmp flush end_flush: preturn: ret ; Print message at CS:BX until zero terminator found pmsg: mov cl,cs:[bx] inc bx test cl,cl jz preturn call conout jmp pmsg print_ax_hex: push ax mov al,ah call print_al_hex pop ax print_al_hex: push ax mov cl,4 shr al,cl call print_al_nib pop ax print_al_nib: and al,15 add al,'0' cmp al,'9' jbe pnib1 add al,'A'-'0'-10 pnib1: mov cl,al call conout ret ;*** CP/M entry codes for disk r/w ** wr_all equ 0 ;write allocated wr_dir equ 1 ;write directory - forced physical write wr_ual equ 2 ;write unallocated change_msg db bell,cr,lf,'Disc has wrong number of sides',0 err_msg1 db ' Error (',0 err_msg2 db 'H) ',0 retry_msg db cr,lf,'Retry (Y/N) ?',0 read_msg db 'read',0 write_msg db 'writ',0 drive_msg db 'ing drive ',0 floppy_error_tab: db 10h ! dw err1 db 20h ! dw err2 db 30h ! dw err3 db 38h ! dw err4 db 40h ! dw err5 db 50h ! dw err6 db 60h ! dw err7 db 70h ! dw err8 db 80h ! dw err9 db 90h ! dw err10 db 0a0h ! dw err11 db 0b0h ! dw err3 db -1 ! dw err0 hd_error_tab_1: db 08h ! dw err12 db 10h ! dw err12 db 18h ! dw err12 db 20h ! dw err12 db 28h ! dw err12 db 48h ! dw err13 db 70h ! dw err8 db 80h ! dw err9 db -1 ! dw err0 hd_error_tab_2: db 01h ! dw err14 db 02h ! dw err15 db 03h ! dw err16 db 04h ! dw err17 db 06h ! dw err4 db 10h ! dw err2 db 11h ! dw err7 db 12h ! dw err18 db 14h ! dw err5 db 15h ! dw err1 db 19h ! dw err19 db -1 ! dw err0 err1 db 'Seek',0 err2 db 'ID CRC',0 err3 db 'Write Protect',0 err4 db 'No Trk 00',0 err5 db 'Record Not Found',0 err6 db 'Lost Data',0 err7 db 'Data CRC',0 err8 db 'DMA',0 err9 db 'Busy',0 err10 db 'Sector out of range',0 err11 db 'Bank Cross',0 err12 db 'Handshake',0 err13 db 'Error Enquiry',0 err14 db 'No Index',0 err15 db 'No Seek Complete',0 err16 db 'Write Fault',0 err17 db 'Drive Not Ready',0 err18 db 'No Address Mark',0 err19 db 'Bad Track Flag',0 err0 db 'Unknown',0 crlf db cr,lf,0 IF NOT loader_bios ;------------------------------------------------ ;| | ; Code to run 8-bit CP/M applications ; Enter here when CCP is about to look for .CMD file cmd_chk: push dx call z80_abort pop dx call ccp_open ; this replaces code patched je com_chk ; if .CMD file not found jmp ccp_cmd ; back to ccp to load .CMD file com_chk: ; check for SAVE command mov si,offset ccp_file+1 mov di,offset save_tx mov cx,5 cld repe cmps al,al je save_0 jmp com_chk_0 ; SAVE command - save contents of z80 bank save_0: call parse_tail mov ds,mcb_base ; get bank mov si,offset zfcb_1+1 ; ready to get no of pages xor ah,ah save_1: mov al,[si] sub al,'0' jb save_error cmp al,9 ja save_error inc si add al,ah cmp byte ptr [si],' ' je save_2 cmp al,20 ja save_error mov cl,10 mul cl mov ah,al jmps save_1 save_2: mov cs:save_pages,al ; create save file mov dx,offset zfcb_2 push dx mov cl,19 ;delete old file int 224 pop dx mov cl,22 int 224 ;make file test al,al js save_error mov bx,offset zfcb_2 mov byte ptr 12[bx],0 ;initialise extent no. mov byte ptr 32[bx],0 ;initialise current record mov dx,ds mov cl,51 int 224 ;set DMA segment mov dx,0100h ;initial DMA offset mov cl,cs:save_pages mov ch,0 add cx,cx save_3: push cx push dx mov cl,26 int 224 ;set DMA offset mov dx,offset zfcb_2 mov cl,21 int 224 ;write sequential pop dx pop cx test al,al jne save_error add dx,080h loop save_3 ; close file mov dx,offset zfcb_2 mov cl,16 int 224 ; end of SAVE mov bx,offset save_ok_tx jmps end_of_save save_error: mov bx,offset save_error_tx end_of_save: mov ax,cs mov ds,ax mov es,ax call pmsg jmp back_2_ccp com_chk_0: mov ccp_file_type,'MO' mov dx,offset ccp_file call ccp_open ; open .com file jne com_chk_1 jmp ccp_error ; not found com_chk_1: mov bx,offset crlf call pmsg cmp z80_flag,0 jne got_bank ; if have mem from last time mov mcb_base,1000h ; get a bank to run cpm in get_bank: mov dx,offset mcb_base mov cl,38h push es int 224 ; attempt to allocate memory pop es test al,al jz got_bank add mcb_base,1000h ; failed to get bank, try next cmp mcb_base,4000h ; but not past 4 jne get_bank mov bx,offset no_bank_message call pmsg call ccp_cleanup jmp back_2_ccp ; go back to cpm86 got_bank: mov z80_flag,1 ; memory allocated,not executing yet mov byte ptr ccp_file_rec,0 ; zero record count + extent in cpm80 mov byte ptr ccp_file_ext,0 ; fcb to be loaded mov cl,33h ; set dma segment mov dx,mcb_base ; to z80 bank int 224 mov dx,0100h ; to 80h red: push dx mov cl,1ah ; set dma int 224 mov cl,14h mov dx,offset ccp_file int 224 ; read .com file sequentially pop dx cmp al,01 je eof ; got to end add dx,128 ; increment dma address cmp dx,0f800h ; make sure less than 64k read jb red mov bx,offset com_2_big call pmsg call ccp_cleanup jmp dealloc_exit ; de-allocate & exit eof: mov ax,mcb_base ; z80 bank cli ; disable keybd int mov kstat_ptr_seg,ax mov kstat_ptr_off,offset kstat_80 ;offset of kstat 80 (z80 bank) mov iobyte_ptr_seg,ax mov iobyte_ptr_off,offset iobyte_80 sti mov z80_flag,2 call set_kstat call ccp_cleanup ; reset default disc mov cl,19h int 224 ; get current(default) disc mov def_drive_tmp,al ; to Z80 call parse_tail mov bx,offset copy_table call copy ; copy all to Z80 bank mov cl,1ah ; set dma address mov dx,080h int 224 mov ax,mcb_base mov cl,4 shr ah,cl mov al,ah out 30h,al ; loop on function call ; Wait for function to process push es mov z80_flag,3 ; z80 now executing poll: mov es,mcb_base mov es:fncode,0 out 38h,al ;start Z80 mode nop mov bl,es:byte ptr fncode mov bh,0 call word ptr jumptab[bx] ; Process function jmps poll ; return here at end of z80 prog end_cpm_80: pop es mov z80_flag,2 call z80_abort mov cl,0 mov dl,1 int 224 ; warm boot : normal end dealloc_exit: call z80_abort ; exit here if program load failed back_2_ccp: jmp ccp_exit ; exit here if alllocation failed ; Use ccp code to process command line tail parse_tail: mov al,byte ptr ccp_parse_patch ; save patch contents push ax mov byte ptr ccp_parse_patch,0c3h ; insert ret mov bx,mcb_base ; set bx = cpm80 bank call ccp_parse ; parse command line ; to fcb1/2 in 80 bank pop ax mov byte ptr ccp_parse_patch,al ; un-patch it. mov byte ptr com_desc,0 ; reset descriptor ret ; copy copies according to a table at [bx]. ; each entry is 3 words long. ; word 1 = source offset (in this segment, i.e. 0100h) ; word 2 = destination offset (in z80 segment) ; word 3 = number of bytes to copy (0 = end of table) copy: push es ;save es mov ax,mcb_base ; get the z80 bank mov es,ax ; to es cld more_copy: mov si,[bx] ; from in si mov di,2[bx] ; to in di mov cx,4[bx] ; no words cmp cx,0 ; end of table? jz end_copy rep movs al,al ; move add bx,6 jmps more_copy end_copy: pop es ret set_kstat_ptr: cli mov kstat_ptr_off,offset kstat_86 mov kstat_ptr_seg,cs sti call set_kstat ret ; Abort Z80 if it is running and deallocate its memory z80_abort: cmp z80_flag,3 ; z80 running ? jne z80_abort_1 ; if no dec z80_flag push ds mov ds,mcb_base abort_lp_2: mov byte ptr z80_nmi,jump mov word ptr z80_nmi+1,0 mov al,1*2 ; clear bit 1 to pulse NMI low out ptr_flag,al mov fncode,0 mov al,1*2+1 out ptr_flag,al ; reset NMI high abort_loop: out 38h,al ; enable z80 cmp fncode,0 jz abort_loop cmp fncode,4 ; wait for warm boot request jne abort_lp_2 pop ds z80_abort_1: cmp z80_flag,2 jne z80_abort_2 dec z80_flag call set_kstat_ptr mov iobyte_ptr_seg,cs mov iobyte_ptr_off,offset iobyte push es mov es,mcb_base mov cl,es:iobyte_80 pop es call setiob z80_abort_2: cmp z80_flag,1 jne z80_abort_ret dec z80_flag mov mcb_ext,0 mov cl,39h mov dx,offset mcb_base push es int 224 pop es z80_abort_ret: ret ; Routine to set kstat ; All registers except flags preserved set_kstat: push ax push bx push ds lds bx,kstat_ptr cli mov al,cs:byte ptr kbuf_get_ptr xor al,cs:byte ptr kbuf_put_ptr jz kbuf_empty mov al,-1 kbuf_empty: mov byte ptr [bx],al sti pop ds pop bx pop ax ret no_bank_message db 'No bank available to run CP/M-80',cr,lf,0 com_2_big db '.COM file too large !',cr,lf,0 ;Order of parms in list: ; Flags - only stored, not loaded ; AX ; BX ; CX ; DX ; SI ; DI ; BP ; DS ; ES not loaded so we know where Z80 is running ESC8088: push ds ;these need to be saved push es mov ax,es:desav mov esc_addr,ax mov word ptr esc_addr+2,es mov bx,es:bcsav mov ax,es:2[bx] push es:word ptr 4[bx] mov cx,es:6[bx] mov dx,es:8[bx] mov si,es:10[bx] mov di,es:12[bx] mov bp,es:14[bx] mov ds,es:16[bx] pop bx push es callf cs:dword ptr esc_addr pop es push bx mov bx,cs:mcb_base mov es,bx mov bx,es:bcsav pushf pop es:word ptr [bx] ;flags mov es:2[bx],ax mov es:6[bx],cx mov es:8[bx],dx mov es:10[bx],si mov es:12[bx],di mov es:14[bx],bp mov es:16[bx],ds pop es:word ptr 4[bx] ;bx pop es pop ds exitint: ret zerror: mov bx,offset duf_bios_call call pmsg jmp zwboot duf_bios_call db cr,lf,'Unsupported direct bios call',cr,lf,0 zconst: call const ; get console status test al,al jz zconst1 ; 0 = no char mov al,0ffh ;-86 = 01 for char: change to ff for -80 zconst1: mov es:asav,al ret zconin: call conin mov es:asav,al ret zconout: mov cl,es:csav jmp conout zlist: mov cl,es:csav jmp listout zpunch: mov cl,es:csav jmp punch zreader: call reader mov es:asav,al ret zlistst: call listst mov es:asav,al ret RDKTAB: push ds xor si,si mov ds,si ; table is in seg 0 mov si,offset keytab mov di,es:hlsav mov cx,512 /2 cld rep movs ax,ax pop ds ret WRKTAB: push es ;save regs push ds xor si,si mov ds,si mov di,offset keytab mov si,es:hlsav push es ;xchg es,ds push ds pop es pop ds mov cx,512 /2 cld rep movs ax,ax pop ds ;restore regs pop es ret WRFONT1: mov di,1000h jmp wrfnt WRFONT2: mov di,1800h wrfnt: push es push ds mov si,es:hlsav push es ;xchg es,ds push ds pop es pop ds mov cx,0fc00h mov es,cx mov bx,8 ;8 passes wt_v_end: in al,crt_stat test al,bit5 jnz wt_v_end wt_v_start: in al,crt_stat test al,bit5 jz wt_v_start mov cx,1024 /8 ;1/8 of 2K in words rep movs ax,ax dec bx jnz wt_v_end mov al,31 out crt_addr,al pop ds pop es ret zwboot: pop bx jmp end_cpm_80 PORTIN: ;read from port C , data into reg A mov dl,es:csav mov dh,0 in al,dx mov es:asav,al ret PORTOUT: ;write to port C , data in reg B mov ax,es:bcsav mov dl,al mov dh,0 mov al,ah out dx,al ret zbdos: mov cl,es:csav mov dx,es:desav cmp cl,6 jz zbdos6 cmp cl,27 jz zbdos27 cmp cl,31 jz zbdos31 zbdos0: push ds push es pop ds int 224 push ds pop es pop ds zbdos_end: mov es:hlsav,ax ret zbdos6: cmp dl,0fdh ; console input (like MPM-II) ? jz zbdos6a cmp dl,0feh ; console status ? jz zbdos6b cmp dl,0ffh ; console input/status request? jz zbdos6c mov cl,dl call conout ret zbdos6c: call const test al,al jz zbdos_end ; if no character available zbdos6a: call conin jmps zbdos_end zbdos6b: call const jmps zbdos_end zbdos27: mov di,offset zalloc mov ax,256 ;largest alloc vector is 512 bytes jmps zbdos31a zbdos31: mov di,offset mfcb_a mov ax,8 ;count of bytes zbdos31a: push di ;save destination offset push ax ;save count push es int 224 mov ax,es pop es pop cx ;count to cx pop di push ds mov ds,ax mov si,bx mov ax,di cld rep movs ax,ax pop ds jmps zbdos_end bdos6: mov cl,6 bdos: push es int 224 pop es ret ;| | ;------------------------------------------------ ENDIF ;not loader_bios ;*********************************************************************** ; Data areas data_offset equ offset $ dseg org data_offset IF NOT loader_bios ;--------------------------------------------- ;| | divide_error_tx db cr,lf,'*** Divide overflow - execution terminated',cr,lf,0 int_trap_tx db cr,lf,'*** Uninitialised interrupt - execution terminated',cr,lf,0 save_tx db 'SAVE ' save_ok_tx db cr,lf,'Save complete.',0 save_error_tx db cr,lf,'Error in SAVE command or disc full.',0 writepending_tx db cr,lf,'*** Error - disc logged in has write pending!',cr,lf,0 submit_txt db 'submit autoexec',cr+bit7 ;| | ;--------------------------------------------- ENDIF ;not loader_bios unrecd_tx db cr,lf,'System not recognized',0 ;*** Variables here can be accessed by utilities, using BIOS GETSEGT call *** ;*** System Memory Segment Table *** segtable db 1 ;1 active segment out of 8 maximum seg_start dw 0 ;1st seg starts after BIOS seg_len dw 4000h ;and extends to 40000h (256K) rw 2*7 ;reserve space for 7 more regions xlt26: db 0,6,12,18,24,4,10,16,22,2,8,14,20 db 1,7,13,19,25,5,11,17,23,3,9,15,21 ;*** M4 specific variables - values inserted depend on configuration *** ;*** Disk parameter headers - values inserted depend on configuration *** ;*** Header format : xlt, scr, scr, scr, dirbuf, dpb, csv, alv *** DPBASE EQU $ ;Base of disk parameter table ;Header here for drive A DPH_A DW 0,0,0,0, offset dirbuf , offset dpb_A ,0,0 ;Header here for drive B DPH_B DW 0,0,0,0, offset dirbuf , offset dpb_B ,0,0 ;Header here for drive C DPH_C DW 0,0,0,0, offset dirbuf , offset dpb_C ,0,0 ;Header here for drive D DPH_D DW 0,0,0,0, offset dirbuf , offset dpb_D ,0,0 dph_e dw 0,0,0,0, offset dirbuf, offset dpb_e, 0,0 IF not loader_bios ;--------------------------------------------------- ;| | ; Data to be copied to z80 bank prior to running cpm80 programs copy_table dw offset iobyte ; from offset in ds dw offset iobyte_80 ; to offset in z80 bank dw 1 ; number of words to copy dw offset def_drive_tmp dw offset def_drive dw 1 dw offset mbios ; copy z80 code dw offset bios_80 dw offset z80_end_bios - offset mbios dw offset tmp_jmp ; set up temp jump dw offset bios_jmp dw 3 dw offset bdos_jmp_80 dw offset bdos_jmp dw 3 dw offset mbdos dw offset bdos_80 dw offset z80_end_bdos - offset mbdos dw 0 ; Hard stopper block dw 0 dw 0 def_drive_tmp db 0 tmp_jmp db jump ; first time at cpm loc 0 dw offset put_bios_jmp - offset mbios + offset bios_80 bdos_jmp_80 db jump dw offset bdos_80 esc_addr rw 2 ; all 'zerror' entries are illegal direct bios calls jumptab: dw exitint ;exit after an interrupt dw zerror ;this will be endcpm function call dw zwboot ;arrive here for warm boot dw zconst ;check for console char ready dw zconin ;read console character in ;10 dw zconout ;write console character out dw zlist ;write listing character out dw zpunch ;punch dw zreader ;read reader dw zerror ;move to track 00 ;20 dw zerror ;select disc driv dw zerror ;set track number dw zerror ;set sector number (eg 1-8) dw zerror ;set dma address dw zerror ;read selected secto ;30 dw zerror ;write selected sector dw zlistst ;return list status dw zerror ;sector translate subroutine dw exitint ;dummy XIOS functions for MP/M compatability dw exitint ;40 dw exitint dw exitint dw exitint dw exitint dw exitint ;50 dw exitint dw exitint dw exitint dw exitint dw exitint ;60 dw exitint dw zwboot ;end CP/M function call dw portin ;input character from port C dw portout ;output char in B to port C dw rdktab ;read 512 bytes of keytab to (HL) ;70 dw wrktab ;write 512 bytes from (HL) to keytab dw esc8088 ;8088 escape routine at (HL:DE), parms at (BC) dw wrfont1 ;write 2048 bytes at (HL) to font ram 1 dw wrfont2 ;write 2048 bytes at (HL) to font ram 2 dw exitint ;set/reset drive logging ;80 dw zbdos dw zerror dw exitint dw exitint dw exitint ;90 dw exitint ;only got limited amount of space dw exitint ;so only allow max of 48 functions dw exitint ;ie code 0-94 ; ; Below is the skeletal BIOS which will be moved to Z80 bank ; This BIOS will transfer function calls back to the 8088 ; jump equ 0c3h lda equ 3ah sta equ 32h lhld equ 2ah shld equ 22h movac equ 79h movca equ 4fh movad equ 7ah movhb equ 60h movlc equ 69h movlm equ 6eh movbh equ 44h moval equ 7dh lxib equ 01h lxisp equ 31h lxih equ 21h dadb equ 09h mvia equ 3eh mvih equ 26h ani equ 0e6h orae equ 0b3h jrz equ 28h rz equ 0c8h exdehl equ 0ebh pushh equ 0e5h oraa equ 0b7h jumpz equ 0cah retun equ 0c9h znop equ 00h mbios: db jump ;Z80 jmp dw (offset mcboot - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mwboot - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mconst - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mconin - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mconout - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mlist - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mpunch - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mreader - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mhome - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mseldsk - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset msettrk - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset msetsec - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset msetdma - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mread - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mwrite - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mlistst - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset msectran - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mempty - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mempty - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mempty - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mempty - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mempty - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mempty - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mempty - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mempty - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mempty - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mempty - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mempty - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mempty - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mempty - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mwboot - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mportin - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mportout - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mrdktab - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mwrktab - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mesc8088 - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mwrfont1 - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mwrfont2 - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mempty - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mempty - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mempty - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mempty - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mempty - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mempty - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mempty - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mempty - offset mbios) + offset bios_80 db jump ;Z80 jmp dw (offset mempty - offset mbios) + offset bios_80 mcboot: mwboot: db mvia,4 mwboot1: db sta dw fncode db 0dbh,50h,0 db jump dw 0000 mconst: db lda ;Z80 ld a,(iobyte) dw offset iobyte db ani,3 ;Z80 and a,3 db jrz,4 ;Z80 jr z,$+4 db lda ;Z80 ld a,(kstat_80) dw offset kstat_80 db retun ;Z80 ret db mvia,6 ;Z80 ld a,6 db sta ;Z80 ld (fncode),a dw fncode db 0dbh,50h,0 ;Z80 in a,(50h) ; nop db lda ;Z80 ld a,(asav) dw asav db retun ;Z80 ret mconin: db mvia,8 ;Z80 ld a,8 db sta ;Z80 ld (fncode),a dw fncode db 0dbh,50h,0 ;Z80 in a,(50h) ; nop db lda ;Z80 ld a,(asav) dw asav db retun ;Z80 ret mconout: db movac ;Z80 ld a,c db sta ;Z80 ld csav,a dw csav db mvia,10 ;Z80 ld a,10 db sta ;Z80 ld (fncode),a dw fncode db 0dbh,50h,0 ;Z80 in a,(50h) ; nop db retun ;Z80 ret mlist: db movac ;Z80 ld a,c db sta ;Z80 ld csav,a dw csav db mvia,12 ;Z80 ld a,12 db sta ;Z80 ld (fncode),a dw fncode db 0dbh,50h,0 ;Z80 in a,(50h) ; nop db retun ;Z80 ret mpunch: db movac ;Z80 ld a,c db sta ;Z80 ld csav,a dw csav db mvia,14 ;Z80 ld a,14 db sta ;Z80 ld (fncode),a dw fncode db 0dbh,50h,0 ;Z80 in a,(50h) ; nop db retun ;Z80 ret mreader: db mvia,16 ;Z80 ld a,16 db sta ;Z80 ld (fncode),a dw fncode db 0dbh,50h,0 ;Z80 in a,(50h) ; nop db lda ;Z80 ld a,(asav) dw asav db retun ;Z80 ret mhome: mseldsk: msettrk: msetsec: msetdma: mread: mwrite: msectran: db mvia,62 db jump dw (offset mwboot1 - offset mbios) + offset bios_80 mportin: db movac db sta dw csav db mvia,64 db sta dw fncode db 0dbh,50h,0 db lda dw asav db retun mportout: db 0edh,043h ; ld (bcsav),bc dw bcsav db mvia,66 db sta dw fncode db 0dbh,50h,0 db retun mrdktab: db shld dw hlsav db mvia,68 db sta dw fncode db 0dbh,50h,0 db retun mwrktab: db shld dw hlsav db mvia,70 db sta dw fncode db 0dbh,50h,0 db retun mlistst: db mvia,32 ;Z80 ld a,32 db sta ;Z80 ld (fncode),a dw fncode db 0dbh,50h,0 ;Z80 in a,(50h) ; nop db lda ;Z80 ld a,(asav) dw asav db retun ;Z80 ret mesc8088: db shld ;Z80 ld (hlsav),hl ;segment dw hlsav db 0edh,053h ;Z80 ld (desav),de ;offset dw desav db 0edh,043h ;Z80 ld (bcsav),bc ;parm list dw bcsav db mvia,72 ;Z80 ld a,72 db sta ;Z80 ld (fncode),a dw fncode db 0dbh,50h,0 ;Z80 in a,(50h) ; nop db 0edh,04bh ;Z80 ld bc,(bcsav) ;parm list dw bcsav db lda ;Z80 ld a,(asav) ;status dw asav db retun ;Z80 ret mwrfont1: db shld ;Z80 ld (hlsav),hl dw hlsav db mvia,74 ;Z80 ld a,74 db sta ;Z80 ld (fncode),a dw fncode db 0dbh,50h,0 ;Z80 in a,(50h) ; nop db retun ;Z80 ret mwrfont2: db shld ;Z80 ld (hlsav),hl dw hlsav db mvia,76 ;Z80 ld a,76 db sta ;Z80 ld (fncode),a dw fncode db 0dbh,50h,0 ;Z80 in a,(50h) ; nop db retun ;Z80 ret mempty: db retun ;Z80 ret put_bios_jmp: db lxih dw offset bios_80 + 3 db shld dw 0001h db lxisp ; set stack to top of TPA dw offset z80_stack db pushh db jump dw 0100h ; go execute program z80_end_bios equ $ mbdos: db movac db oraa db jumpz dw 0000 db sta dw csav db 0edh,053h ; ld(desav),de dw desav db mvia,80 ; ld a,80 db sta dw fncode db 0dbh,50h,0 db lhld dw hlsav db movbh db moval db retun z80_end_bdos equ $ ;| | ;------------------------------------------------ ENDIF ;************************************************************ ; initialised data space mcb_base dw 1000h ; memory control block for z80 dw 1000h ; length 64k mcb_ext dw 0 IOBYTE db 95h ; CON:=CRT:, LST:=LPT: iobyte_ptr_off dw offset iobyte iobyte_ptr_seg rw 1 ;filled in iobyte_ptr equ dword ptr iobyte_ptr_off xonxoff db xon dma_adr dw 0 ; DMA offset from DS dma_seg dw 0 ; DMA Base Segment hst_disk db -1 hst_sect db 0 hst_trk dw 0 hst_dpb dw 0 req_disk db 0 req_sect db 0 req_trk dw 0 req_ofs db 0 write_flag db 0 write_type db 0 write_req db 0 ;buffer_size dw 5 * 1024 ;5" value IF not loader_bios ;---------------------------------------- ;| | fn_expand_flag db true fn_expand_ptr dw offset submit_txt - 1 ;gets modified by INIT kstat_86 db true kstat_ptr_off dw offset kstat_86 kstat_ptr_seg rw 1 kstat_ptr equ dword ptr kstat_ptr_off saved_ax rw 1 saved_ds rw 1 saved_bx rw 1 ;used by keyboard interrupt ax_save_7 rw 1 ;used by level 7 interrupt kbuf_get_ptr dw offset kbuf_start kbuf_put_ptr dw offset kbuf_start z80_flag db 0 ; 0 if 8088, 1 if z80 allocated,2 if running save_pages rb 1 ; number of pages to SAVE ;| | ;---------------------------------------- ENDIF ;************* end of initialised data temp_data equ offset $ ; data here is only needed at system startup ; so is overwritten by disc buffer etc. IF loader_bios ;------------------------------------------------ ;| | avail_tab: dw 0h, dpb_0,0, 0,0, 0,0, 0,0, 0,0 ;5" ss dd dw 1h, dpb_1,0, 0,0, 0,0, 0,0, 0,0 ;5" ds dd dw 2h, dpb_2,0, 0,0, 0,0, 0,0, 0,0 ;5" ss qd dw 3h, dpb_3,0, 0,0, 0,0, 0,0, 0,0 ;5" ds qd dw 4h, dpb_4,0, 0,0, 0,0, 0,0, 0,0 ;8" ss dd dw 5h, dpb_5,0, 0,0, 0,0, 0,0, 0,0 ;8" ds dd dw 8h, dpb_7,80h, 0,0, 0,0, 0,0, 0,0 ;Winchester dw -1 ;| | ;------------------------------------------------ ENDIF IF (NOT loader_bios) AND (NOT special) ;------------------------------------------------ ;| | avail_tab: dw 10h, dpb_0,0, dpb_0,1, 0,0, 0,0, 0,0 ;5" ss/dd + 5"ss/dd dw 11h, dpb_1,0, dpb_1,1, dpb_0,1, 0,0, 0,0 ;5 ds dd + 5 ds dd dw 12h, dpb_2,0, dpb_2,1, dpb_0,1, 0,0, 0,0 ;5 ss qd + 5 ss qd dw 13h, dpb_3,0, dpb_3,1, dpb_2,1, dpb_1,1, dpb_0,1 ;5 ds qd + 5 ds qd dw 14h, dpb_4,0, dpb_4,1, dpb_6,1, 0,0, 0,0 ;8 ss dd + 8 ss dd dw 15h, dpb_5,0, dpb_5,1, dpb_4,1, dpb_6,1, 0,0 ;8 ds dd + 8 ds dd dw 09h, dpb_7,80h, dpb_8,80h, dpb_1,0, dpb_0,0, 0,0 ;5 ds dd + w dw 0bh, dpb_7,80h, dpb_8,80h, dpb_3,0, dpb_1,0, dpb_0,0 ;5 ds qd + w dw 0dh, dpb_7,80h, dpb_8,80h, dpb_5,0, dpb_4,0, dpb_6,0 ;8 ds dd + w dw -1 ; stopper ;| | ;------------------------------------------------ ENDIF IF (NOT loader_bios) AND special ;------------------------------------------------ | avail_tab: dw 15h, dpb_5,0, dpb_1,1, 0,0, 0,0, 0,0 dw -1 ;| | ;------------------------------------------------ ENDIF ; Extended disc parameter block layout dpb_flags equ byte ptr 15 ;flags dpb_ret_sidleng equ word ptr 16 ;retries and sectors/track dpb_sidleng equ byte ptr 17 ;sectors/track dpb_phy_disk equ byte ptr 18 ;drive number dpb_max_sectors equ word ptr 20 ;total sectors on floppy disc dpb_part_offset equ word ptr 20 ;hard disc partition offset dpb_secsiz equ word ptr 22 ;bytes per sector dpb_secsiz_msb equ byte ptr 23 ;msb of above ; Disc parameter blocks ; Single Sided, Double Density 5" drives (SA 400L) DPB_0: ;Block allocation size = 1024 (BLS) DW 40 ;CP/M Sectors per track (SPT) DB 03 ;Block shift factor (BSH) DB 07 ;Block mask (BLM) DB 00 ;Null mask (EXM) DW 189 ;Disk size - 1 (DSM) DW 063 ;Directory max (DRM) DB 11000000b ;Alloc 0 (AL0) DB 0 ;Alloc 1 (AL1) DW 16 ;Check size (CKS) DW 2 ;Track offset (OFF) db 0000b ;Flags - 5" SS db 10 ;10 retries db 5 ;no. of sectors per side of trk db 0 ;space for drive no. db 0 ;spare dw 40*5 ;total sectors per disc dw 1024 ;sector size ;Double Sided, Double Density 5" drives (SA 450L) DPB_1: ;Block allocation size = 2048 (BLS) DW 40 ;CP/M Sectors per track (SPT) DB 04 ;Block shift factor (BSH) DB 15 ;Block mask (BLM) DB 01 ;Null mask (EXM) DW 194 ;Disk size - 1 (DSM) DW 127 ;Directory max (DRM) DB 11000000b ;Alloc 0 (AL0) DB 0 ;Alloc 1 (AL1) DW 32 ;Check size (CKS) DW 2 ;Track offset (OFF) db 0001b ;Flags - 5" DS db 10 ;10 retries db 5 ;no. of sectors per side of trk db 0 ;drive no. db 0 ;spare dw 40*5*2 ;total sectors per disc dw 1024 ;sector size ;Single Sided, Quad Density 5" drives (SA 410) DPB_2: ;Block allocation size = 2048 (BLS) DW 40 ;CP/M Sectors per track (SPT) DB 04 ;Block shift factor (BSH) DB 15 ;Block mask (BLM) DB 01 ;Null mask (EXM) DW 194 ;Disk size - 1 (DSM) DW 127 ;Directory max (DRM) DB 11000000b ;Alloc 0 (AL0) DB 0 ;Alloc 1 (AL1) DW 32 ;Check size (CKS) DW 2 ;Track offset (OFF) db 0010b ;Flags - 5" SS quad db 10 ;10 retries db 5 ;no. of sectors per side of trk db 0 ;drive no. db 0 ;spare dw 80*5 ;total sectors per disc dw 1024 ;sector size ;Double Sided, Quad Density 5" drives (SA 460) DPB_3: ;Block allocation size = 2048 (BLS) DW 40 ;CP/M Sectors per track (SPT) DB 04 ;Block shift factor (BSH) DB 15 ;Block mask (BLM) DB 00 ;Null mask (EXM) DW 394 ;Disk size - 1 (DSM) DW 255 ;Directory max (DRM) DB 11110000b ;Alloc 0 (AL0) DB 0 ;Alloc 1 (AL1) DW 64 ;Check size (CKS) DW 2 ;Track offset (OFF) db 0011b ;Flags - 5" DS quad db 10 ;10 retries db 5 ;no. of sectors per side of trk db 0 ;drive no. db 0 ;spare dw 80*5*2 ;total sectors per disc dw 1024 ;sector size ;Single Sided, Double Density 8" drives (SA 800) DPB_4: ;Block allocation size = 2048 (BLS) DW 64 ;CP/M Sectors per track (SPT) DB 04 ;Block shift factor (BSH) DB 15 ;Block mask (BLM) DB 00 ;Null mask (EXM) DW 303 ;Disk size - 1 (DSM) DW 127 ;Directory max (DRM) DB 11000000b ;Alloc 0 (AL0) DB 0 ;Alloc 1 (AL1) DW 32 ;Check size (CKS) DW 1 ;Track offset (OFF) db 0100b ;Flags - 8" SS db 10 ;10 retries db 8 ;no. of sectors per side of trk db 0 ;drive no. db 0 ;spare dw 77*8 ;total sectors per disc dw 1024 ;sector size ;Double Sided, Double Density 8" drives (SA 850) DPB_5: ;Block allocation size = 2048 (BLS) DW 64 ;CP/M Sectors per track (SPT) DB 04 ;Block shift factor (BSH) DB 15 ;Block mask (BLM) DB 00 ;Null mask (EXM) DW 611 ;Disk size - 1 (DSM) DW 255 ;Directory max (DRM) DB 11110000b ;Alloc 0 (AL0) DB 0 ;Alloc 1 (AL1) DW 64 ;Check size (CKS) DW 1 ;Track offset (OFF) db 0101b ;Flags - 8" DS db 10 ;10 retries db 8 ;no. of sectors per side of trk db 0 ;drive no. db 0 ;spare dw 77*8*2 ;total sectors per disc dw 1024 ;sector size ;Single Sided, Single Density 8" drives (SA 800) DPB_6: ;Block allocation size = 1024 (BLS) DW 26 ;CP/M Sectors per track (SPT) DB 03 ;Block shift factor (BSH) DB 07 ;Block mask (BLM) DB 00 ;Null mask (EXM) DW 242 ;Disk size - 1 (DSM) DW 063 ;Directory max (DRM) DB 11110000b ;Alloc 0 (AL0) DB 0 ;Alloc 1 (AL1) DW 16 ;Check size (CKS) DW 2 ;Track offset (OFF) db 1100b ;Flags - 8" SS SDen db 10 ;10 retries db 26 ;no. of sectors per side of trk db 0 ;drive no. db 0 ;spare dw 77*26 ;total sectors per disc dw 128 ;sector size ;Winnie, partition A DPB_7: ;Block allocation size = 8192 (BLS) DW 17*4 ;CP/M Sectors per track (SPT) DB 06 ;Block shift factor (BSH) DB 63 ;Block mask (BLM) DB 03 ;Extent mask, mod. by set_size (EXM) DW 0 ;Disk size (filled in) (DSM) DW 511 ;Directory max (DRM) DB 11000000b ;Alloc 0 (AL0) DB 0 ;Alloc 1 (AL1) DW 0 ;Check size (CKS) DW 1 ;Track offset (OFF) db 10000b ;Flags - Winchester db 10 ;10 retries db 17 ;no. of sectors per side of trk db 0 ;drive no. (filled in later) db 0 ;spare dw 0 ;partition offset (filled in) dw 512 ;sector size DPB_8: ;Block allocation size = 8192 (BLS) DW 17*4 ;CP/M Sectors per track (SPT) DB 06 ;Block shift factor (BSH) DB 63 ;Block mask (BLM) DB 03 ;Extent mask, mod. by set_size (EXM) DW 0 ;Disk size (filled in) (DSM) DW 511 ;Directory max (DRM) DB 11000000b ;Alloc 0 (AL0) DB 0 ;Alloc 1 (AL1) DW 0 ;Check size (CKS) DW 1 ;Track offset (OFF) db 10000b ;Flags - Winchester db 10 ;10 retries db 17 ;no. of sectors per side of trk db 0 ;drive no. (filled in later) db 0 ;spare dw 0 ;partition offset (filled in) dw 512 ;sector size IF NOT loader_bios ;---------------------------------------- ;| | signon db esc,'E','CP/M-86/80 Version 1.1, ' db 'Level 21 (24/05/83)',cr,lf ;| | ;---------------------------------------- ENDIF IF (NOT loader_bios) AND (NOT cal) ;---------------------------------------- ;| | db 'LSI Computers Ltd, Woking',cr,lf size_txt db '256K System M-Four' ;| | ;---------------------------------------- ENDIF IF (NOT loader_bios) AND cal ;---------------------------------------- ;| | size_txt db '256K CAL Personal Computer' ;| | ;---------------------------------------- ENDIF IF NOT loader_bios ;---------------------------------------- ;| | db ', Firmware Level ' firm_rev db '00',cr,lf,lf db 'Available drives :-',cr,lf,0 win_tx db 'Winchester',cr,lf,0 flop_tx db '.H. drive, ',0 driv_tx db '" ',0 single_tx db 'single',0 double_tx db 'double',0 quad_tx db 'quad',0 sides_tx db ' sided, ',0 density_tx db ' density',cr,lf,0 size_txt_small db '128' ;| | ;---------------------------------------- ENDIF part_err_msg db 'reading Winchester partition table',0 no_part_msg db 'Winchester disc has no CP/M partition.',0 part_buf rb 512 ;for reading partition table ; end of temporary data org temp_data ; uninitialised data space dirbuf rb 128 ;overwritten by temp_data hst_buff rb 19 * 512 ;overwritten by temp_data rw 32 ;local stack for init. & warm boot stkbase equ $ ;*** Disk parameter blocks *** DPB_A rb dpblen DPB_B rb dpblen DPB_C rb dpblen DPB_D rb dpblen dpb_e rb dpblen ; Keyboard buffer kbuf_start rb 32 kbuf_end equ $ ; Workspace used by clock interrupt clock_temp: ;data read from clock to here (image of clock registers) second rb 1 ;seconds 0 0 - 60 rb 1 ; 1 minute rb 1 ;minutes 2 0 - 60 rb 1 ; 3 hour rb 1 ;hour 4 0 - 24 rb 1 ; 5 rb 1 ; 6 day_no rb 1 ;day 7 1 - 31 month rb 1 ;month 8 1 - 12 yr rb 1 ;year 9 0 - 99 (from 1980) rb 1 ; 10 sysdat_offset rw 1 sysdat_segment rw 1 ;must come after sysdat_offset ss_save rw 1 sp_save rw 1 rw 30 ;space for clock stack, allowing for interrupts clock_stack rw 1 ;must be at end of stack space ; End of clock workspace bdos_scr rs 1 ;******** end of everything ;********************************************************** IF NOT loader_bios ;---------------------------------------- ;| | ; Map of skeletal cpm 80 bios to be put in Z80 bank org 0 bios_jmp rs 3 iobyte_80 rs 1 def_drive rs 1 bdos_jmp rs 3 org 5ch zfcb_1 rs 36 org 66h z80_nmi rs 3 org 6ch zfcb_2 rs 36 org 80h dma_buf rs 80h prog_80 equ $ org 0fb00h z80_stack equ $ org 0fb06h bdos_80 equ $ org 0fc00h z_alloc rs 512 ;allocation vector org 0fe00h bios_80 equ $ org 0ffe0h mfcb_a rb 16 asav rb 1 csav rb 1 bcsav rw 1 esav rb 1 desav rw 1 lsav rb 1 hlsav rw 1 kstat_80 rb 1 org 0fffeh fncode rw 2 ;| | ;------------------------------------------------ ENDIF ; not loader bios ; End of M4BIOS.ASM file END