Digital Research
This commit is contained in:
2020-11-06 18:50:37 +01:00
parent 621ed8ccaf
commit 31738079c4
8481 changed files with 1888323 additions and 0 deletions

View File

@@ -0,0 +1,658 @@
nolist
; *****************************
; * Codemacros for 8087 *
; * numeric data processor: *
; ***************************
;
; stack references:
st equ 0 ; stack top (= register 0)
st0 equ 0 ; register 0
st1 equ 1 ; register 1
st2 equ 2 ; register 2
st3 equ 3 ; register 3
st4 equ 4 ; register 4
st5 equ 5 ; register 5
st6 equ 6 ; register 6
st7 equ 7 ; register 7
;
;
codemacro FLD src:Mb
segfix src
db 0d9h
modrm 0,src
endm
codemacro FLD src:Mw
segfix src
db 0ddh
modrm 0,src
endm
codemacro FLD src:Db(0,7)
db 0d9h
dbit 5(18h),3(src(0))
endm
codemacro FLDTR src:M
segfix src
db 0dbh
modrm 5,src
endm
codemacro FST dst:Mb
segfix dst
db 0d9h
modrm 2,dst
endm
codemacro FST dst:Mw
segfix dst
db 0ddh
modrm 2,dst
endm
codemacro FST dst:Db(0,7)
db 0ddh
dbit 5(1ah),3(dst(0))
endm
codemacro FSTP dst:Mb
segfix dst
db 0d9h
modrm 3,dst
endm
codemacro FSTP dst:Mw
segfix dst
db 0ddh
modrm 3,dst
endm
codemacro FSTP dst:Db(0,7)
db 0ddh
dbit 5(1bh),3(dst(0))
endm
codemacro FSTPTR dst:M
segfix dst
db 0dbh
modrm 7,dst
endm
codemacro FXCH
dw 0c9d9h
endm
codemacro FXCH dst:Db(0,7)
db 0d9h
dbit 5(19h),3(dst(0))
endm
codemacro FILD src:Mb
segfix src
db 0dbh
modrm 0,src
endm
codemacro FILDLI src:Mw
segfix src
db 0dfh
modrm 5,src
endm
codemacro FILD src:Mw
segfix src
db 0dfh
modrm 0,src
endm
codemacro FIST dst:Mb
segfix dst
db 0dbh
modrm 2,dst
endm
codemacro FIST dst:Mw
segfix dst
db 0dfh
modrm 2,dst
endm
codemacro FISTP dst:Mb
segfix dst
db 0dbh
modrm 3,dst
endm
codemacro FISTPLI dst:Mw
segfix dst
db 0dfh
modrm 7,dst
endm
codemacro FISTP dst:Mw
segfix dst
db 0dfh
modrm 3,dst
endm
codemacro FBLD src:Mb
segfix src
db 0dfh
modrm 4,src
endm
codemacro FBSTP dst:Mb
segfix dst
db 0dfh
modrm 6,dst
endm
codemacro FADD
dw 0c1d8h
endm
codemacro FADD dst:Db(0),src:Db(0,7)
db 0d8h
dbit 5(18h),3(src(0))
endm
codemacro FADD src:Mb
segfix src
db 0d8h
modrm 0,src
endm
codemacro FADD src:Mw
segfix src
db 0dch
modrm 0,src
endm
codemacro FADD dst:Db(0,7),src:Db(0)
db 0dch
dbit 5(18h),3(dst(0))
endm
codemacro FADDP dst:Db(0,7),src:Db(0)
db 0deh
dbit 5(18h),3(dst(0))
endm
codemacro FIADD src:Mb
segfix src
db 0dah
modrm 0,src
endm
codemacro FIADD src:Mw
segfix src
db 0deh
modrm 0,src
endm
codemacro FSUB
dw 0e1d8h
endm
codemacro FSUB dst:Db(0),src:Db(0,7)
db 0d8h
dbit 5(1ch),3(src(0))
endm
codemacro FSUB src:Mb
segfix src
db 0d8h
modrm 4,src
endm
codemacro FSUB src:Mw
segfix src
db 0dch
modrm 4,src
endm
codemacro FSUB dst:Db(0,7),src:Db(0)
db 0dch
dbit 5(1ch),3(dst(0))
endm
codemacro FSUBP dst:Db(0,7),src:Db(0)
db 0deh
dbit 5(1ch),3(dst(0))
endm
codemacro FISUB src:Mb
segfix src
db 0dah
modrm 4,src
endm
codemacro FISUB src:Mw
segfix src
db 0deh
modrm 4,src
endm
codemacro FSUBR
dw 0e9d8h
endm
codemacro FSUBR dst:Db(0),src:Db(0,7)
db 0d8h
dbit 5(1dh),3(src(0))
endm
codemacro FSUBR src:Mb
segfix src
db 0d8h
modrm 5,src
endm
codemacro FSUBR src:Mw
segfix src
db 0dch
modrm 5,src
endm
codemacro FSUBR dst:Db(0,7),src:Db(0)
db 0dch
dbit 5(1dh),3(dst(0))
endm
codemacro FSUBRP dst:Db(0,7),src:Db(0)
db 0deh
dbit 5(1dh),3(dst(0))
endm
codemacro FISUBR src:Mb
segfix src
db 0dah
modrm 5,src
endm
codemacro FISUBR src:Mw
segfix src
db 0deh
modrm 5,src
endm
codemacro FMUL
dw 0c9d8h
endm
codemacro FMUL dst:Db(0),src:Db(0,7)
db 0d8h
dbit 5(19h),3(src(0))
endm
codemacro FMUL src:Mb
segfix src
db 0d8h
modrm 1,src
endm
codemacro FMUL src:Mw
segfix src
db 0dch
modrm 1,src
endm
codemacro FMUL dst:Db(0,7),src:Db(0)
db 0dch
dbit 5(19h),3(dst(0))
endm
codemacro FMULP dst:Db(0,7),src:Db(0)
db 0deh
dbit 5(19h),3(dst(0))
endm
codemacro FIMUL src:Mb
segfix src
db 0dah
modrm 1,src
endm
codemacro FIMUL src:Mw
segfix src
db 0deh
modrm 1,src
endm
codemacro FDIV
dw 0f1d8h
endm
codemacro FDIV dst:Db(0),src:Db(0,7)
db 0d8h
dbit 5(1eh),3(src(0))
endm
codemacro FDIV src:Mb
segfix src
db 0d8h
modrm 6,src
endm
codemacro FDIV src:Mw
segfix src
db 0dch
modrm 6,src
endm
codemacro FDIV dst:Db(0,7),src:Db(0)
db 0dch
dbit 5(1eh),3(dst(0))
endm
codemacro FDIVP dst:Db(0,7),src:Db(0)
db 0deh
dbit 5(1eh),3(dst(0))
endm
codemacro FIDIV src:Mb
segfix src
db 0dah
modrm 6,src
endm
codemacro FIDIV src:Mw
segfix src
db 0deh
modrm 6,src
endm
codemacro FDIVR
dw 0f9d8h
endm
codemacro FDIVR dst:Db(0),src:Db(0,7)
db 0d8h
dbit 5(1fh),3(src(0))
endm
codemacro FDIVR src:Mb
segfix src
db 0d8h
modrm 7,src
endm
codemacro FDIVR src:Mw
segfix src
db 0dch
modrm 7,src
endm
codemacro FDIVR dst:Db(0,7),src:Db(0)
db 0dch
dbit 5(1fh),3(dst(0))
endm
codemacro FDIVRP dst:Db(0,7),src:Db(0)
db 0deh
dbit 5(1fh),3(dst(0))
endm
codemacro FIDIVR src:Mb
segfix src
db 0dah
modrm 7,src
endm
codemacro FIDIVR src:Mw
segfix src
db 0deh
modrm 7,src
endm
codemacro FSQRT
dw 0fad9h
endm
codemacro FSCALE
dw 0fdd9h
endm
codemacro FPREM
dw 0f8d9h
endm
codemacro FRNDINT
dw 0fcd9h
endm
codemacro FXTRACT
dw 0f4d9h
endm
codemacro FABS
dw 0e1d9h
endm
codemacro FCHS
dw 0e0d9h
endm
codemacro FCOM
dw 0d1d8h
endm
codemacro FCOM src:Db(0,7)
db 0d8h
dbit 5(1ah),3(src(0))
endm
codemacro FCOM src:Mb
segfix src
db 0d8h
modrm 2,src
endm
codemacro FCOM src:Mw
segfix src
db 0dch
modrm 2,src
endm
codemacro FCOMP
dw 0d9d8h
endm
codemacro FCOMP src:Db(0,7)
db 0d8h
dbit 5(1bh),3(src(0))
endm
codemacro FCOMP src:Mb
segfix src
db 0d8h
modrm 3,src
endm
codemacro FCOMP src:Mw
segfix src
db 0dch
modrm 3,src
endm
codemacro FCOMPP
dw 0c9deh
endm
codemacro FICOM src:Mb
segfix src
db 0dah
modrm 2,src
endm
codemacro FICOM src:Mw
segfix src
db 0deh
modrm 2,src
endm
codemacro FICOMP src:Mb
segfix src
db 0dah
modrm 3,src
endm
codemacro FICOMP src:Mw
segfix src
db 0deh
modrm 3,src
endm
codemacro FTST
dw 0e4d9h
endm
codemacro FXAM
dw 0e5d9h
endm
codemacro FPTAN
dw 0f2d9h
endm
codemacro FPATAN
dw 0f3d9h
endm
codemacro F2XM1
dw 0f0d9h
endm
codemacro FYL2X
dw 0f1d9h
endm
codemacro FYL2XP1
dw 0f9d9h
endm
codemacro FLDZ
dw 0eed9h
endm
codemacro FLD1
dw 0e8d9h
endm
codemacro FLDPI
dw 0ebd9h
endm
codemacro FLDL2T
dw 0e9d9h
endm
codemacro FLDL2E
dw 0ead9h
endm
codemacro FLDLG2
dw 0ecd9h
endm
codemacro FLDLN2
dw 0edd9h
endm
codemacro FINIT
dw 0e3dbh
endm
FNINIT equ FINIT
codemacro FDISI
dw 0e1dbh
endm
FNDISI equ FDISI
codemacro FENI
dw 0e0dbh
endm
FNENI equ FENI
codemacro FLDCW src:Mw
segfix src
db 0d9h
modrm 5,src
endm
codemacro FSTCW dst:Mw
segfix dst
db 0d9h
modrm 7,dst
endm
FNSTCW equ FSTCW
codemacro FSTSW dst:Mw
segfix dst
db 0ddh
modrm 7,dst
endm
FNSTSW equ FSTSW
codemacro FCLEX
dw 0e2dbh
endm
FNCLEX equ FCLEX
codemacro FSAVE dst:M
segfix dst
db 0ddh
modrm 6,dst
endm
FNSAVE equ FSAVE
codemacro FRSTOR src:M
segfix src
db 0ddh
modrm 4,src
endm
codemacro FSTENV dst:M
segfix dst
db 0d9h
modrm 6,dst
endm
FNSTENV equ FSTENV
codemacro FLDENV src:M
segfix src
db 0d9h
modrm 4,src
endm
codemacro FINCSTP
dw 0f7d9h
endm
codemacro FDECSTP
dw 0f6d9h
endm
codemacro FFREE dst:Db(0,7)
db 0ddh
dbit 5(18h),3(dst(0))
endm
codemacro FNOP
dw 0d0ddh
endm
FWAIT equ WAIT
list


View File

@@ -0,0 +1,690 @@
title '8086 Disk I/O Drivers'
;*********************************************
;* *
;* Basic Input/Output System (BIOS) for *
;* CP/M-86 Configured for iSBC 86/12 with *
;* the iSBC 204 Floppy Disk Controller *
;* *
;* (Note: this file contains both embedded *
;* tabs and blanks to minimize the list file *
;* width for printing purposes. You may wish*
;* to expand the blanks before performing *
;* major editing.) *
;*********************************************
; Copyright (C) 1980,1981
; Digital Research, Inc.
; Box 579, Pacific Grove
; California, 93950
;
; (Permission is hereby granted to use
; or abstract the following program in
; the implementation of CP/M, MP/M or
; CP/NET for the 8086 or 8088 Micro-
; processor)
true equ -1
false equ not true
;*********************************************
;* *
;* Loader_bios is true if assembling the *
;* LOADER BIOS, otherwise BIOS is for the *
;* CPM.SYS file. Blc_list is true if we *
;* have a serial printer attached to BLC8538 *
;* Bdos_int is interrupt used for earlier *
;* versions. *
;* *
;*********************************************
loader_bios equ false
blc_list equ true
bdos_int equ 224 ;reserved BDOS Interrupt
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
csts equ 0DAh ;i8251 status port
cdata equ 0D8h ; " data port
IF blc_list
;---------------------------------------------
;| |
lsts equ 41h ;2651 No. 0 on BLC8538 status port
ldata equ 40h ; " " " " " data port
blc_reset equ 60h ;reset selected USARTS on BLC8538
;| |
;---------------------------------------------
ENDIF ;blc_list
;*********************************************
;* *
;* Intel iSBC 204 Disk Controller Ports *
;* *
;*********************************************
base204 equ 0a0h ;SBC204 assigned address
fdc_com equ base204+0 ;8271 FDC out command
fdc_stat equ base204+0 ;8271 in status
fdc_parm equ base204+1 ;8271 out parameter
fdc_rslt equ base204+1 ;8271 in result
fdc_rst equ base204+2 ;8271 out reset
dmac_adr equ base204+4 ;8257 DMA base address out
dmac_cont equ base204+5 ;8257 out control
dmac_scan equ base204+6 ;8257 out scan control
dmac_sadr equ base204+7 ;8257 out scan address
dmac_mode equ base204+8 ;8257 out mode
dmac_stat equ base204+8 ;8257 in status
fdc_sel equ base204+9 ;FDC select port (not used)
fdc_segment equ base204+10 ;segment address register
reset_204 equ base204+15 ;reset entire interface
max_retries equ 10 ;max retries on disk i/o
;before perm error
cr equ 0dh ;carriage return
lf equ 0ah ;line feed
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 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
pop ds ;restore the DS register
;*********************************************
;* *
;* National "BLC 8538" Channel 0 for a serial*
;* 9600 baud printer - this board uses 8 Sig-*
;* netics 2651 Usarts which have on-chip baud*
;* rate generators. *
;* *
;*********************************************
mov al,0FFh
out blc_reset,al ;reset all usarts on 8538
mov al,4Eh
out ldata+2,al ;set usart 0 in async 8 bit mode
mov al,3Eh
out ldata+2,al ;set usart 0 to 9600 baud
mov al,37h
out ldata+3,al ;enable Tx/Rx, and set up RTS,DTR
;| |
;---------------------------------------------
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
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
;---------------------------------------------
;| |
int_trap:
cli ;block interrupts
mov ax,cs
mov ds,ax ;get our data segment
mov bx,offset int_trp
call pmsg
hlt ;hardstop
;| |
;---------------------------------------------
ENDIF ;not loader_bios
;*********************************************
;* *
;* CP/M Character I/O Interface Routines *
;* Console is Usart (i8251a) on iSBC 86/12 *
;* at ports D8/DA *
;* *
;*********************************************
CONST: ;console status
in al,csts
and al,2
jz const_ret
or al,255 ;return non-zero if RDA
const_ret:
ret ;Receiver Data Available
CONIN: ;console input
call const
jz CONIN ;wait for RDA
in al,cdata
and al,7fh ;read data and remove parity bit
ret
CONOUT: ;console output
in al,csts
and al,1 ;get console status
jz CONOUT ;wait for TBE
mov al,cl
out cdata,al ;Transmitter Buffer Empty
ret ;then return data
LISTOUT: ;list device output
IF blc_list
;---------------------------------------------
;| |
call LISTST
jz LISTOUT ;wait for printer not busy
mov al,cl
out ldata,al ;send char to TI 810
;| |
;---------------------------------------------
ENDIF ;blc_list
ret
LISTST: ;poll list status
IF blc_list
;---------------------------------------------
;| |
in al,lsts
and al,81h ;look at both TxRDY and DTR
cmp al,81h
jnz zero_ret ;either false, printer is busy
or al,255 ;both true, LPT is ready
;| |
;---------------------------------------------
ENDIF ;blc_list
ret
PUNCH: ;not implemented in this configuration
READER:
mov al,1ah
ret ;return EOF for now
GETIOBF:
mov al,0 ;TTY: for consistency
ret ;IOBYTE not implemented
SETIOBF:
ret ;iobyte not implemented
zero_ret:
and al,0
ret ;return zero in AL and flags
; Routine to get and echo a console character
; and shift it to upper case
uconecho:
call CONIN ;get a console character
push ax
mov cl,al ;save and
call CONOUT
pop ax ;echo to console
cmp al,'a'
jb uret ;less than 'a' is ok
cmp al,'z'
ja uret ;greater than 'z' is ok
sub al,'a'-'A' ;else shift to caps
uret:
ret
; utility subroutine to print messages
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
mov bx,0000h
cmp cl,2 ;this BIOS only supports 2 disks
jnb return ;return w/ 0000 in BX if bad drive
mov al, 80h
cmp cl,0
jne sel1 ;drive 1 if not zero
mov al, 40h ;else drive is 0
sel1: mov sel_mask,al ;save drive select mask
;now, we need disk parameter address
mov ch,0
mov bx,cx ;BX = word(CL)
mov cl,4
shl bx,cl ;multiply drive code * 16
;create offset from Disk Parameter Base
add bx,offset dp_base
return:
ret
HOME: ;move selected disk to home position (Track 0)
mov trk,0 ;set disk i/o to track zero
mov bx,offset hom_com
call execute
jz return ;home drive and return if OK
mov bx,offset bad_hom ;else print
call pmsg ;"Home Error"
jmps home ;and retry
SETTRK: ;set track address given by CX
mov trk,cl ;we only use 8 bits of track address
ret
SETSEC: ;set sector number given by cx
mov sect,cl ;we only use 8 bits of sector address
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: the *
;* Read and Write entry points transfer one *
;* sector of 128 bytes to/from the current *
;* DMA address using the current disk drive *
;* *
;*********************************************
READ:
mov al,12h ;basic read sector command
jmps r_w_common
WRITE:
mov al,0ah ;basic write sector command
r_w_common:
mov bx,offset io_com ;point to command string
mov byte ptr 1[BX],al ;put command into string
; fall into execute and return
execute: ;execute command string.
;[BX] points to length,
; followed by Command byte,
; followed by length-1 parameter bytes
mov last_com,BX ;save command address for retries
outer_retry:
;allow some retrying
mov rtry_cnt,max_retries
retry:
mov BX,last_com
call send_com ;transmit command to i8271
; check status poll
mov BX,last_com
mov al,1[bx] ;get command op code
mov cx,0800h ;mask if it will be "int req"
cmp al,2ch
jb exec_poll ;ok if it is an interrupt type
mov cx,8080h ;else we use "not command busy"
and al,0fh
cmp al,0ch ;unless there isn't
mov al,0
ja exec_exit ; any result
;poll for bits in CH,
exec_poll: ; toggled with bits in CL
in al,fdc_stat ;read status
and al,ch
xor al,cl ; isolate what we want to poll
jz exec_poll ;and loop until it is done
;Operation complete,
in al,fdc_rslt ; see if result code indicates error
and al,1eh
jz exec_exit ;no error, then exit
;some type of error occurred . . .
cmp al,10h
je dr_nrdy ;was it a not ready drive ?
;no,
dr_rdy: ; then we just retry read or write
dec rtry_cnt
jnz retry ; up to 10 times
; retries do not recover from the
; hard error
mov ah,0
mov bx,ax ;make error code 16 bits
mov bx,errtbl[BX]
call pmsg ;print appropriate message
in al,cdata ;flush usart receiver buffer
call uconecho ;read upper case console character
cmp al,'C'
je wboot_l ;cancel
cmp al,'R'
je outer_retry ;retry 10 more times
cmp al,'I'
je z_ret ;ignore error
or al,255 ;set code for permanent error
exec_exit:
ret
dr_nrdy: ;here to wait for drive ready
call test_ready
jnz retry ;if it's ready now we are done
call test_ready
jnz retry ;if not ready twice in row,
mov bx,offset nrdymsg
call pmsg ;"Drive Not Ready"
nrdy01:
call test_ready
jz nrdy01 ;now loop until drive ready
jmps retry ;then go retry without decrement
zret:
and al,0
ret ;return with no error code
wboot_l: ;can't make it w/ a short leap
jmp WBOOT
;*********************************************
;* *
;* The i8271 requires a read status command *
;* to reset a drive-not-ready after the *
;* drive becomes ready *
;* *
;*********************************************
test_ready:
mov dh, 40h ;proper mask if dr 1
test sel_mask,80h
jnz nrdy2
mov dh, 04h ;mask for dr 0 status bit
nrdy2:
mov bx,offset rds_com
call send_com
dr_poll:
in al,fdc_stat ;get status word
test al,80h
jnz dr_poll ;wait for not command busy
in al,fdc_rslt ;get "special result"
test al,dh ;look at bit for this drive
ret ;return status of ready
;*********************************************
;* *
;* Send_com sends a command and parameters *
;* to the i8271: BX addresses parameters. *
;* The DMA controller is also initialized *
;* if this is a read or write *
;* *
;*********************************************
send_com:
in al,fdc_stat
test al,80h ;insure command not busy
jnz send_com ;loop until ready
;see if we have to initialize for a DMA operation
mov al,1[bx] ;get command byte
cmp al,12h
jne write_maybe ;if not a read it could be write
mov cl,40h
jmps init_dma ;is a read command, go set DMA
write_maybe:
cmp al,0ah
jne dma_exit ;leave DMA alone if not read or write
mov cl,80h ;we have write, not read
init_dma:
;we have a read or write operation, setup DMA controller
; (CL contains proper direction bit)
mov al,04h
out dmac_mode,al ;enable dmac
mov al,00
out dmac_cont,al ;send first byte to control port
mov al,cl
out dmac_cont,al ;load direction register
mov ax,dma_adr
out dmac_adr,al ;send low byte of DMA
mov al,ah
out dmac_adr,al ;send high byte
mov ax,dma_seg
out fdc_segment,al ;send low byte of segment address
mov al,ah
out fdc_segment,al ;then high segment address
dma_exit:
mov cl,[BX] ;get count
inc BX
mov al,[BX] ;get command
or al,sel_mask ;merge command and drive code
out fdc_com,al ;send command byte
parm_loop:
dec cl
jz exec_exit ;no (more) parameters, return
inc BX ;point to (next) parameter
parm_poll:
in al,fdc_stat
test al,20h ;test "parameter register full" bit
jnz parm_poll ;idle until parm reg not full
mov al,[BX]
out fdc_parm,al ;send next parameter
jmps parm_loop ;go see if there are more parameters
;*********************************************
;* *
;* Data Areas *
;* *
;*********************************************
data_offset equ offset $
dseg
org data_offset ;contiguous with code segment
IF loader_bios
;---------------------------------------------
;| |
signon db cr,lf,cr,lf
db 'CP/M-86 Version 2.2',cr,lf,0
;| |
;---------------------------------------------
ENDIF ;loader_bios
IF not loader_bios
;---------------------------------------------
;| |
signon db cr,lf,cr,lf
db ' System Generated - 11 Jan 81',cr,lf,0
;| |
;---------------------------------------------
ENDIF ;not loader_bios
bad_hom db cr,lf,'Home Error',cr,lf,0
int_trp db cr,lf,'Interrupt Trap Halt',cr,lf,0
errtbl dw er0,er1,er2,er3
dw er4,er5,er6,er7
dw er8,er9,erA,erB
dw erC,erD,erE,erF
er0 db cr,lf,'Null Error ??',0
er1 equ er0
er2 equ er0
er3 equ er0
er4 db cr,lf,'Clock Error :',0
er5 db cr,lf,'Late DMA :',0
er6 db cr,lf,'ID CRC Error :',0
er7 db cr,lf,'Data CRC Error :',0
er8 db cr,lf,'Drive Not Ready :',0
er9 db cr,lf,'Write Protect :',0
erA db cr,lf,'Trk 00 Not Found :',0
erB db cr,lf,'Write Fault :',0
erC db cr,lf,'Sector Not Found :',0
erD equ er0
erE equ er0
erF equ er0
nrdymsg equ er8
rtry_cnt db 0 ;disk error retry counter
last_com dw 0 ;address of last command string
dma_adr dw 0 ;dma offset stored here
dma_seg dw 0 ;dma segment stored here
sel_mask db 40h ;select mask, 40h or 80h
; Various command strings for i8271
io_com db 3 ;length
rd_wr db 0 ;read/write function code
trk db 0 ;track #
sect db 0 ;sector #
hom_com db 2,29h,0 ;home drive command
rds_com db 1,2ch ;read status command
; System Memory Segment Table
segtable db 2 ;2 segments
dw tpa_seg ;1st seg starts after BIOS
dw tpa_len ;and extends to 08000
dw 2000h ;second is 20000 -
dw 2000h ;3FFFF (128k)
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 0800h - tpa_seg
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 system call vector
rw 2*(bdos_int-1)
bdos_offset rw 1
bdos_segment rw 1
END


View File

@@ -0,0 +1,367 @@
title 'Customized Basic I/O System'
;*********************************************
;* *
;* This Customized BIOS adapts CP/M-86 to *
;* the following hardware configuration *
;* Processor: *
;* Brand: *
;* Controller: *
;* *
;* *
;* Programmer: *
;* Revisions : *
;* *
;*********************************************
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
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
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
;---------------------------------------------
;| |
int_trap:
cli ;block interrupts
mov ax,cs
mov ds,ax ;get our data segment
mov bx,offset int_trp
call pmsg
hlt ;hardstop
;| |
;---------------------------------------------
ENDIF ;not loader_bios
;*********************************************
;* *
;* CP/M Character I/O Interface Routines *
;* *
;*********************************************
CONST: ;console status
rs 10 ;(fill-in)
ret
CONIN: ;console input
call CONST
jz CONIN ;wait for RDA
rs 10 ;(fill-in)
ret
CONOUT: ;console output
rs 10 ;(fill-in)
ret ;then return data
LISTOUT: ;list device output
rs 10 ;(fill-in)
ret
LISTST: ;poll list status
rs 10 ;(fill-in)
ret
PUNCH: ;write punch device
rs 10 ;(fill-in)
ret
READER:
rs 10 ;(fill-in)
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 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 trk,0 ;set disk i/o to track zero
rs 10 ;(fill-in)
ret
SETTRK: ;set track address given by CX
mov trk,CX
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:
rs 50 ;fill-in
ret
WRITE:
rs 50 ;(fill-in)
ret
;*********************************************
;* *
;* Data Areas *
;* *
;*********************************************
data_offset equ offset $
dseg
org data_offset ;contiguous with code segment
IOBYTE db 0
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
IF loader_bios
;---------------------------------------------
;| |
signon db cr,lf,cr,lf
db 'CP/M-86 Version 1.0',cr,lf,0
;| |
;---------------------------------------------
ENDIF ;loader_bios
IF not loader_bios
;---------------------------------------------
;| |
signon db cr,lf,cr,lf
db 'System Generated 00/00/00'
db cr,lf,0
;| |
;---------------------------------------------
ENDIF ;not loader_bios
int_trp db cr,lf
db 'Interrupt Trap Halt'
db cr,lf
; System Memory Segment Table
segtable db 2 ;2 segments
dw tpa_seg ;1st seg starts after BIOS
dw tpa_len ;and extends to 08000
dw 2000h ;second is 20000 -
dw 2000h ;3FFFF (128k)
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 0800h - tpa_seg
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 system call vector
rw 2*(bdos_int-1)
bdos_offset rw 1
bdos_segment rw 1
END


View File

@@ -0,0 +1,619 @@
title 'COPYDISK 1 Feb 82'
ver equ 20 ; Version 2.0 -jrp
; COPYDISK duplicates entire diskettes using all of the
; available storage as a multiple track buffer.
; This program must be built with a large extra segment
; as follows:
; ASM86 COPYDISK
; GENCMD COPYDISK EXTRA[M0200,XFF00]
; This allows COPYDISK to utilize all the left-over memory in your
; system as its buffer.
cseg ; code segment
start:
mov si,offset signon_message
call pmsg ; print signon message
next_copy:
; reload local stack for retries.
pushf ; save interrupt flag in stack
pop bx ; put it in bx
cli ; disable interrupts
mov ax,ds ; get our data segment
mov ss,ax ; and use as stack segment
mov sp,offset stack_end ; set stack pointer
push bx ; flags back into stack
popf ; restore interrupt status
mov si,offset source_message ; prompt for source drive
call get_drive ; request drive code
cmp al, cr - 'A' ; see if it was a <cr>
jne next_1 ; no, continue
jmp exit ; yes, we should exit
next_1:
cmp al,drive_cnt ; see if valid drive code
jb save_source ; yes, go save it
drive_err:
mov si,offset bad_drive ; else, print
call pmsg ; a bad drive message
jmp another ; and try to get another
save_source:
mov source,al ; save it as the source drive
add al,'A' ; make ascii drive code
mov source_ascii,al ; save for messages
mov cl,source ; get source drive
mov dl,0 ; force first time selection
call seldsk ; select it
test bx,bx ; insure the drive exists
jz drive_err ; no, fatal
mov si,ES:word ptr 10[bx] ; get pointer to DPB
; create local copy of disk definition
push ds ! push es ; save segments
pop ds ! pop es ; and exchange them
mov di,offset disk_def ; point to local disk def table
mov cx,15 ; diskdef is 15 bytes long
cld ! rep movsb ; copy it
push ds ! push es ; swap back
pop ds ! pop es ; segment registers
mov dx,es:word ptr 00[bx] ; get secttran table address
mov cx,0 ; logical sector zero
call sectran ; find out first sector number
mov base_sector,bx ; save this for track reads
mov si,offset dest_message ; prompt for destination
call get_drive ; get the drive code
cmp al,source ; see if same as source
jne not_same ; no, go see if > max
mov si,offset same_message ; can't have same drive
call pmsg ; so we print message
jmp another ; and go try again
not_same:
cmp al,drive_cnt ; is dest > max?
jnb drive_err ; go print invalid drive error
mov dest,al ; save destination drive
add al,'A' ; make ascii drive code
mov dest_ascii,al ; save for messages
mov cl,dest ; get destination drive
mov dl,0 ; force first time selection
call seldsk ; select destination drive
test bx,bx ; insure drive exists
jz drive_err ; no, go error out
mov di,ES:word ptr 10[bx] ; point to destination DPB
mov si,offset disk_def ; point to source drives def
mov cx,15 ; length of a definition
cld ! rep cmpsb ; compare the definitions
je next_2 ; must be equal.
jmp different_type ; else go print error
next_2:
mov dx,es:word ptr 00[bx] ; get secttran table address
mov cx,0 ; logical sector zero
call sectran ; find out first sector number
cmp bx,base_sector ; make sure its the same
je next_3 ; the same is OK,
jmp different_type ; no, fatal error
next_3: ; determine track capacity
mov ax,spt ; get the sectors per track
mov dl,128 ; length of a sector
mul dl ; get track capacity
mov track_size,ax ; save it
; determine number of tracks on diskette
mov al,blm ; get block mask
inc al ; plus 1 gives sectors/block
mov ah,0 ; make 16 bits
mov dx,0 ; make 32 bits
mul dsm ; total sectors/data area
add ax,spt ; force round up
dec ax ; by adding SPT-1
div spt ; compute number of tracks
add ax,off ; add in operating sys tracks
dec ax ; number of last track
mov max_track,ax ; save value
; compute number of tracks that will
; fit in our data segment
mov ax,extra_length ; get low 16 bits of DS length
mov dl,extra_length_H ; get other 8 bits
mov dh,0 ; zero rest of 32 bit divisor
div track_size ; divide buff_len / track_size
mov NTS,ax ; save this value.
cmp ax,2 ; must be at least 2 tracks
jae adequite_memory ; ok, continue
mov si,offset memory_message ; else print
call pmsg ; error message
aborting: ; here if exiting because of error
mov si,offset abort_message
call pmsg
jmp another
adequite_memory:
mov si,offset ready_message ; insure this is correct
call get_yn ; print request
jc aborting ; if NO, then get new input
mov si,offset copy_message ; else tell user OK.
call pmsg
mov ax,max_track ; get maximum track number
mov base_track,ax ; save as outer loop index
next_block:
mov ax,base_track ; get loop index
cmp ax,0 ; see if we are outside limits
jl next_block_x ; yes, terminate loop
sub ax,nts ; lower by number of tracks per buffer
inc ax ; and adjust by 1.
jns not_neg ; check to make sure its positive
mov ax,0 ; no, zero is floor
not_neg:
mov btr,ax ; save as last track for this copy pass
mov si,offset reading_message ; point to read message
mov ax,offset read ; and proper subroutine
mov cl,source ; select source diskette
call track_block
mov si,offset writing_message ; point to write message
mov ax,offset write ; and proper subroutine
mov cl,dest ; select destination
call track_block
mov si,offset verify_message ; point to verify message
mov ax,offset verify
mov cl,dest ; reselect destination
call track_block
mov ax,base_track
sub ax,nts ; adjust base track by number we copied
mov base_track,ax
jmps next_block
next_block_x: ; here, we are done with a sucessful copy
mov si,offset done_message ; print message
call pmsg ; announcing success
another:
mov si,offset another_message
call getyn ; see if we want another copy
jc exit ; no, go exit
next_copy_v: ; convienient for short jumps
jmp next_copy ; back for another disk copy
exit: ; here, we are exiting
mov si,offset exit_message
call pmsg
mov cl,13
int BDOS
mov cl,0 ; reboot
mov dl,0 ; CP/M-86
int BDOS
different_type:
mov si,offset type_error_message
call pmsg
jmp aborting
track_block:
mov message_pointer,si ; save pointer to message
mov disk_function,ax ; save pointer to read/write
mov selected_disk,cl ; save drive select code
mov dl,1 ; tell bios have selected before
call seldsk ; select diskette drive
mov ax,base_track ; get base track number for this pass
mov trk,ax ; save as loop index
next_track: ; first inner loop...
mov ax,trk ; get the index
cmp ax,btr ; see if less than last
jnl next_4 ; no, continue
jmp next_track_x ; yeah, exit loop
next_4:
mov si,message_pointer
call pmsg ; print "<xxxx> Track "
mov ax,trk ; get the track number
call pdec ; print as decimal
mov si,offset space_message ; then, print some
call pmsg ; spaces to blank any garbage
mov cx,trk ; get desired track address
call settrk ; give to bios
mov cx,0 ; start with logical sector 0
next_sector:
push cx ; save current sector number
mov sector,cx ; save sector number (0 org)
add cx,base_sector ; correct for first sector number
call setsec ; pass to cbios
; compute dma address and segment
mov ax,base_track ; get starting track number
sub ax,trk ; gives relative track of pass
mov dx,0 ; make dword
mul spt ; compute sector of track
add ax,sector ; add in current logical sector
adc dx,0 ; make double precision add
mov cx,128 ; length of a sector
mul cx ; gives base offset as 32 bits
mov dma_offset,ax ; gives base dma offset
mov cl,12 ! shl dx,cl ; move to high nibble
add dx,extra_base ; offset extra segment
mov dma_segment,dx ; save DMA segment
mov cx,dx ; put in argument register
call setdmab ; pass to CBIOS
mov cx,dma_offset ; get the offset again
call setdma ; and pass it to BIOS also
mov ax,sector ; fetch current sector number
inc ax ; add one to it
cmp ax,spt ; see if last sector on track
mov cl,0 ; might be then normal write
jb execute_function ; skip if not last
mov cl,1 ; if last, then treating as
; dir write forces flush
execute_function:
call disk_function ; read/write/verify a sector
test al,al ; check error return code
jz no_disk_error ; see if we got a bad sector
; got fatal disk error
mov si,offset disk_error_message
call pmsg ; print disk error
mov si,message_pointer ; get pointer to read/write
inc si ; skip leading <cr>
call pmsg ; print that
mov ax,trk ; get track number
call pdec ; print it
mov si,offset sector_message ; print ", Sector "
call pmsg
mov ax,sector ; get sector number (0 org)
add ax,base_sector ; correct if 1 origin
call pdec ; print the sector number
mov si,offset continue_message ; see if user wants to ignore
call get_yn ; ask for a Y/N response
jnc next_5 ; yes, continue
jmp aborting ; NO, go ask for new disks
next_5:
call crlf
no_disk_error:
pop cx ; recover sector number
inc cx ; sector = sector + 1
cmp cx,spt ; see if past last sector
jae next_6 ; done, exit
jmp next_sector ; else, continue
next_6:
dec trk ; track = track - 1
jmp next_track ; continue loading buffer
next_track_x:
ret
verify: ; verify a 128 byte sector
mov cx,offset verify_buffer ; point at our 128 byte buffer
call setdma ; make it the dma buffer
mov cx,ds ; get our data segment
call setdmab ; point dma base address
call read ; read 128 byte record
push ax ; save read error in stack
mov si,offset verify_buffer ; point to sector we just read
les di,dword ptr dma_offset ; get pointer to one we wrote
mov cx,128 ; 128 byte record
cld ! rep cmpsb ; compare them
mov al,0 ; no error
je verify_good ; so we are ok
mov al,0FFh ; else, we got compare error
verify_good:
pop dx ; recover read error byte
or al,dl ; merge into return code
ret ; back to caller
; ***********************************
; *
; * BIOS direct entry points
; *
; ***********************************
home:
mov al,8
jmps bios_call
seldsk:
mov al,9
jmps bios_call
settrk:
mov al,10
jmps bios_call
setsec:
mov al,11
jmps bios_call
setdma:
mov al,12
jmps bios_call
setdmab:
mov al,17
jmps bios_call
read:
mov al,13
jmps bios_call
write:
mov al,14
jmps bios_call
sectran:
mov al,16
jmps bios_call
bios_call:
mov bios_function,al
mov bios_CX,cx
mov bios_DX,DX
mov cl,50
mov dx,offset bios_function
int BDOS
ret
; ****************************************
; *
; * Operator interaction subroutines
; *
; ****************************************
get_drive: ; print message and read drive code
call pmsg ; first, print the message
call get_upper ; then get an upper case letter
sub al,'A' ; and normalize to 0...
ret
get_upper: ; get upper case console input w/ echo
call get_char ; get console character
cmp al,'a' ; see if lower case
jb get_upper_x ; below, leave
cmp al,'z' ; insure not > z
ja get_upper_x ; not lower at all
sub al,'a'-'A' ; make upper
get_upper_x:
ret
get_yn: ; print message and get Y or N
push si ; save message pointer
call pmsg ; print the message
call get_upper ; get a upper case letter
pop si ; recover message pointer
cmp al,'Y' ; see if response was 'Y'
je get_yn_x ; yes, return w/ no carry
cmp al,'N' ; see if was 'N'
jne get_yn ; no, invalid. reprompt
stc ; set carry for a NO
get_yn_x:
ret
get_char: ; read a line from CONIN and return first char
mov cl,10 ; function for line in
mov dx,offset line_buff ; point at buffer
int BDOS ; read the line
mov al,line_buff+1 ; get input length
cmp al,1 ; insure single char entered
jb get_char_1 ; no, go
mov al,line_buff+2 ; get the character
ret
get_char_1:
mov al,cr ; null string, return a <CR>
ret
crlf: ; print a <CR><LF>
mov si,offset crlf_message
; fall into pmsg
pmsg: ; print message at [SI] to zero
lods al ; get a character
test al,al ; see if zero terminator
jz pmsg_x ; yes, exit
push si ; save pointer
call conout ; not done, print character
pop si ; recover pointer
jmp pmsg ; and loop
pmsg_x:
ret
conin: ; get character from console into AL
mov cl,6
mov dl,0FFh
int BDOS
test al,al
jz conin
ret
conout: ; output character in AL to console
mov dl,al
mov cl,6
int BDOS
ret
pdec: ; print unsigned 16 bits in AX as decimal
; with zero suppresion
mov cx,0 ; cx is digit counter
pdec_1: ; here to divide out next digit
sub dx,dx ; Zero DX
mov bx,10 ; constant 10
div bx ; quotient to AX, remainder to DX
add dl,'0' ; make remainder ascii digit
push dx ; and stick onto stack
inc cx ; bump digit counter
test ax,ax ; see if any quotient left
jnz pdec_1 ; yes, continue stacking digits
pdec_2:
pop ax ; get a digit from the stack
push cx ; save count
call conout ; print it
pop cx ; restore count
loop pdec_2 ; and continue if more in stack
ret ; done. . .
; ** DATA SEGMENT **
dseg
cr equ 0dh ; ascii carriage return
lf equ 0ah ; ascii line feed
drive_cnt equ 16 ; CP/M currently supports up to 16 drives
BDOS equ 224 ; system call interrupt number
; CP/M-86 Page Zero
code_length rw 1 ; low 16 bits code length
code_length_H rb 1 ; high 8 bits '' ''
code_base rw 1 ; base of the code segment
model_8080 rb 1 ; 8080 memory model flag
data_length rw 1 ; low 16 bits data length
data_length_H rb 1 ; high 8 bits '' ''
data_base rw 1 ; base of the data segment
rs 1 ; not used
extra_length rw 1 ; low 16 bits extra length
extra_length_H rb 1 ; high 8 bits '' ''
extra_base rw 1 ; base of the extra segment
org 005Ch
default_FCB rs 35 ; default File Control Block
org 0080h
default_buffer rs 128 ; default record buffer
org 0100h ; start of user data segment
; MESSAGES
signon_message db cr,lf,'CP/M-86 Full Disk COPY Utility'
db cr,lf,' Version '
db ver/10+'0', '.', (ver mod 10)+'0',cr,lf,0
source_message db cr,lf,cr,lf,'Enter Source Disk Drive (A-D) ? ',0
dest_message db cr,lf,cr,lf,' Destination Disk Drive (A-D) ? ',0
ready_message db cr,lf,cr,lf,'Copying Disk '
source_ascii rb 1
db ': to Disk '
dest_ascii rb 1
db ':',cr,lf
db 'Is this what you want to do (Y/N) ? ',0
memory_message db cr,lf,'Insufficient memory available for copy',0
abort_message db cr,lf,cr,lf,'Copy aborted',cr,lf,0
done_message db cr,lf,'Copy completed.',0
another_message db cr,lf,cr,lf,'Copy another disk (Y/N) ? ',0
copy_message db cr,lf,'Copy started',cr,lf,0
reading_message db cr,' Reading Track ',0
writing_message db cr,' Writing Track ',0
verify_message db cr,'Verifying Track ',0
space_message db ' ',0
bad_drive db cr,lf,'Illegal Diskette Drive',cr,lf,0
same_message db cr,lf,'Source and Destination cannot be the same'
db cr,lf,0
type_error_message db cr,lf,'Source and Destination disks must be'
db cr,lf,'the same type',cr,lf,0
continue_message db cr,lf,'Ignore error (Y/N) ? ',0
sector_message db ', Sector ',0
disk_error_message db cr,lf,'Permanent Error ',0
crlf_message db cr,lf,0
exit_message db cr,lf,'COPY program exiting',cr,lf,0
; VARIABLES
source rb 1 ; source drive select code
dest rb 1 ; destination drive select code
selected_disk rb 1 ; current drive select code
trk rw 1 ; current track number
sector rw 1 ; current sector number (0 origin)
nts rw 1 ; number of tracks per buffer full
base_track rw 1 ; starting track number for this pass
track_size rw 1 ; size of each track in bytes
btr rw 1 ; last track number of this pass
dma_offset rw 1 ; current buffer offset
dma_segment rw 1 ; current buffer segment base
message_pointer rw 1 ; points to appropriate read/write message
disk_function rw 1 ; points to '' '' '' subroutine
base_sector rw 1 ; either 0 or 1 normally
max_track rw 1 ; total number of tracks on disk
line_buff db 80
db 0
rb 80
bios_function rb 1 ; bios function number
bios_cx rw 1 ; first argument for BIOS call
bios_dx rw 1 ; second argument for BIOS call
disk_def rs 0 ; disk definition table gets copied here
spt rw 1 ; 128 byte sectors per track
bsh rb 1 ; block shift factor
blm rb 1 ; block mask
exm rb 1 ; extent mask
dsm rw 1 ; disk size in blocks
drm rw 1 ; directory size
al0 rb 1 ; alloc 0
al1 rb 1 ; alloc 1
cks rw 1 ; checksum size
off rw 1 ; directory offset
rw 128
stack_end rw 0
verify_buffer rs 128 ; sector buffer for verify function
db 0 ; force out data segment
end


View File

@@ -0,0 +1,366 @@
:0400000300000000F9
:1B000081E92A03E92103E902037F0020202020202020202020202020202020D4
:1B001B81434F505952494748542028432920313938302C204449474954414CA0
:0B003681205245534541524348202091
:1B008B81CDE08CCD8EC5C3B10033D2EBF38AD0B102EBEDB02051E8F4FF59C32C
:1B00A681B00DE8F5FFB00AEBF151E8F3FF5B8A070AC07501C34353E8D8FF5BC5
:1B00C181EBF1BA2708B13BEBC1B93200BA3009EBB9B13AEBB5B10DEBB18AD085
:1B00DC81B10EEBABE8A8FFA2CD08FEC0C3B10FEBF3C606470800BA2708EBF22D
:1B00F781B110E8E4FF7401C3B99B09EB20B111EBD8B112EBD4BA2708EBF3B1C2
:1B01128113EB1DE873FF0AC0C3B114EBF6BA2708EBF7E888FFE9B706B117EB16
:1B012D8104B2FFB120E956FF3C6172063C7B7302245FC3F6060508FF7458F621
:1B014881063909FF740533C0E887FFBA0608E890FF7444A01508FEC8A22608B0
:1B016381BA0608E8B2FF7534BF0A00BE4B08B98000E8C502C606140800FE0E40
:1B017E811508BA0608E871FF7417A03909E84CFFBB0B00E820FFE8320074119C
:1B019981E85600E9BF01E85000B10ABA0900E8E1FEBB0B0033C92E8A0E0A00D4
:1B01B481E30A8A07E87AFF880743E2F6882FC70635090B00C3B10BE8BDFE0A2D
:1B01CF81C07501C3B101E8B3FE0AC0C3B119EB10B133EB0C8B160308E8F5FF9B
:1B01EA81BA8000B11AE999FEF6060508FF7501C3B000A20508E8D8FEBA0608CE
:1B020581E809FFA03909E9CCFEBFDC09BE000BB90600F3A6E302EB01C3F48A01
:1B022081040AC07501C33C207303E9930157BF3B09B90900FCF2AE5FC38A0484
:1B023B813C007501C33C207401C346EBF143C60720E2FAC351E8CCFF5974F26A
:1B025681433C2A7504B03FEB01468807E2EBE8B8FF740346EBF8C3B80000BBF8
:1B027181270803D853C6063A09008B363509E8B7FF8936370974081C40807C15
:1B028C81013A7405A03909EB123C1077043C007705B4FF5BEB39A23A09464621
:1B02A7818807B90800E8A0FFB903003C2E750146E895FF438BFB8BC1B90300B5
:1B02C281FC33C0F3AA893635095BB90B0043803F3F7502FEC4E2F68A040AE42A
:1B02DD81C3BF4409BB0600BE2808B90400F3A6740803F94B75F1B007C38A0480
:1B02F8813C2075F78BC3C38CCAE8DBFEBA4B08E8E3FEC3C70635090B002E801D
:1B0313813E0B000375062EC6060A0000BC3009E83E01EB2C2EC6060A0000BC90
:1B032E813009518AC1B104D2D8240F8AD0A2CE08E8EFFDE892FDA20508E88890
:1B034981FD58240FA23909E887FDE8A9FF2E803E0A0000751AFCBC3009E84111
:1B036481FDE873FE0441E82BFDB03EE826FDE88AFFE8C8FDFCE85FFEA2390916
:1B037F812E803E0B003B74D78B363509E8ABFE74CEE8DAFE752BF6063A09FFF5
:1B039A817403E94503803E280820741AE835FF48BBE20903D803D88B17FFE240
:1B03B581B99009EB03B95C09E9EFFCE8E3FC8B1E37098A073C20740C0AC0741F
:1B03D0810853E8C3FC5B43EBEEB03FE8BAFCE8C5FC2EC6060A0000E974FFE89A
:1B03EB8180FEF6063A09FF75CCBB2808B90B00803F207503B021C38A073C20F2
:1B0406817427432C303C0A73B18AF0F6C5E075AA8AC5B103D2C002C572A00212
:1B042181C5729C02C672988AE8FEC975D4C3803F20758C43FEC975F68AC5C3EE
:1B043C81FCF3A4C3BB4B08B40002C103D88A07C3C606270800A03A090AC074FE
:1B04578124FEC8BB39093A07741BEB1D8A16CE08E8C6FCEB11A03A090AC0740D
:1B04728109FEC8BB39093A077501C3A03909E957FCC606F10901E82700803EF6
:1B048D81F009007406B9AA09E817FCEB15C606F10900E81000803EF009007410
:1B04A88106B9C509E800FCE92F032EC6060A0000C606F00900E8ADFD80FCFF56
:1B04C3817503E9F8FEE881FFBB2808803F20750AB50BC6073F43FECD75F8B29C
:1B04DE810052E829FC7503E8D2FE7503E99B00A0CD08B103D2C88AC8B00AE840
:1B04F98145FFD0D0730EF606F109FF7515C606F00901EB70F606F109FF7407F2
:1B051481C606F00901EB625A8AC2FEC2529090902403507514E87AFB51E8AB8F
:1B052F81FC590441E86AFBB03AE865FBEB08E85EFBB03AE85BFBE856FBB501CC
:1B054A818AC5E8F1FE247F3C20751358503C03750BB009E8E0FE247F3C20740F
:1B05658116B020E835FBFEC580FD0C730A80FD0975D3E824FBEBCE58E849FC20
:1B0580817506E883FBE960FF5AC32EC6060A0000E8DAFC80FC0B7521B9640994
:1B059B81E811FBE89FFBBB0A00FE0F7403E9B3FD43803F597403E9AAFD43893E
:1B05B6811E3509803E2808207467E889FEBA2708E848FBFEC07503E8EAFDEBF1
:1B05D1814C2EC6060A0000E892FC754A803E2808207443E865FEE803FB74E1BE
:1B05EC81E8B7FABBFF0081FB8000720A53E823FB5B751BBB000043BE4A080353
:1B060781F38A043C1A741053E886FAE8B4FB5B7506EBD8FEC87503E9C001E841
:1B06228191FDE845FEE996FD2EC6060A0000E83AFC75F2A03A0950E810FEE86D
:1B063D81CDFA7402EB55BE2708BF3708B91000E8EDFD8B363509E8E3FB3C3DE0
:1B06588174043C5F75354689363509E807FC752B588AE8BB3A098A070AC074E4
:1B067381063AC5882F7519882FC606270800E888FA7408BA2708E89EFAEB9025
:1B068E81E829FDEBF9E8D6FDEB38B96F09E811FAEBECE847FD3C21751AE8FC09
:1B06A981F9E881FA0C303C39760950B031E8DFF9582C0AE8D9F9EB493C1072FE
:1B06C48102EB098AD0803E2808207503E9EDFCA2CE08E857FAE90801B97B0912
:1B06DF81E8CDF9EBA8E827FB803E280820751FA03A090AC07415FEC8A23909B2
:1B06FA81A33109E8CAF90BDB74CCA03909E8D0F9E9D700803E3008207402EBEC
:1B071581BAE833FDBF3008BE0008B90300F3A4BA2708E8C3F97506E83DFDE953
:1B0730818EFCE88EF94074A4891E03088EC3E82BFD8CD88EC0E825FBBB3A0911
:1B074B81538A07A22708B81000E819FB5B8A07A23708C606470800BF5C008E08
:1B076681060308BE2708B92100E8CAFCBE0B00E8C1FA0AC0740EAC0AC07409C6
:1B0781813C207402EBF5E8AFFAB500BF81004EACAA0AC07404FEC5EBF68AC5CB
:1B079C81BF8000AABF0000268B45032EA3FB07268A45052EA2FA07E8F0F8E8CA
:1B07B7812AFAC606CF08002EC6060A00008E0603088E1E0308BB0F00833F00F9
:1B07D28174028E072EFF1EF907BC3009E9B1F8E888FCE886FAA028082C20BB03
:1007ED813A090A077503E968FBE9C7FB00000000B8
:15080082434D4400000000242424202020202053554200000097
:0909308209000001000B00000027
:1B093A8200203D5F2E3A3B3C3E2C444952535553455252454E2054595045451E
:1B095582524120444952204E4F2046494C4500414C4C2028592F4E293F0046D1
:1B097082494C4520455849535453004D454D4F5259204E4F5420415641494C99
:1B098B8241424C450052454144204552524F52000D0A43414E4E4F5420434C6C
:1B09A6824F5345000D0A4E4F4E2D53595354454D2046494C4528532920455818
:1B09C182495354000D0A53595354454D2046494C45285329204558495354001C
:1609DC82000A0000099A9A048A05D2052A06A0068304E40600008B
:0409F682000000007B
:0E0A5082446563656D626572203238203831EC
:00000001FF
:02000085000079
:0D0A60823132206A616E7561727920383200
:02000085000079
:0400000300000A806F
:1B0A8081260FAB156314B0151225FC15B515D315D815DE15EA15F115EC0A06CE
:1B0A9B81209D1F2720A120A720D220EB20F420FD2003210C2115211B212121E1
:1B0AB6812821BA172E2134213D2146215821612167216D217321F715F715C4A0
:1B0AD18121D621F821F10A570F760F800FAC0FCE0FF40F06104410A4101D0EFF
:0B0AEC81B022E9030BBBA024E94A16ED
:1B0B0081000A0000099A1E8CC88ED88F069D2480F905740580F90D7205C606BE
:1B0B1B81B124FF8C16942489269624803E98240174078CC88ED0BC8E248B1EE8
:1B0B368196248E069424268B5F0481E300027401FB8CD85557560E078C062501
:1B0B518123E81A005E5F5D8E062523FA8E1694248B269624C606B124008E1EDF
:1B0B6C819D24CF891699248816102332C0A39B24A22723A21523A21323BB89FA
:1B0B8781215380F929720980E90680F9367201C38AD9B7008ACA03DB2EFFA7CD
:1B0BA281800ABBAB23E819003C037555EB10BBB623EB08BBC223EB03BBBD23EF
:1B0BBD81E80300E96A0353E8D308A0A4240441A2A723B99B23E8CF0859E8CBEA
:1B0BD88108E9AA071E5051C6062723FF880E282332ED8B3699248936A924BF42
:1B0BF381C623893E99248E1E9D24FCF3A459581FC351B121EB0351B124E8CD7A
:1B0C0E81FF59C30650518A0E282332ED8E069D24BEC6238B3EA924893E992470
:1B0C2981FCF3A4595807C3B1148B16DC228E1E2323E805008CC88ED8C32EC66D
:1B0C448106982401CDE0C353BAD722E8F0FF5BC350538B5C072B5C058B4507F2
:1B0C5F812B45053BD8760287F75B58C32BDB2BD2C606E222FFB90800BE0C2286
:1B0C7A81803C007466803C027505C606E22200837C0300743E8B4403A3D7221E
:1B0C9581837C07008B440574038B4407A3D92251B13852E8A0FF5A59803E9BDF
:1B0CB0812400743151B136E891FF59A1D922394405731D894407B138E880FF04
:1B0CCB81EB18035C05837C0700740C035407730ABAF0FFEB05EB5B0354058307
:1B0CE681C609E2900BD274298916D922B137E854FF803E9B24007419B135E822
:1B0D018148FF3B1ED92277D7B137E83DFF8B16D9222BD3E82C008B3E9423A192
:1B0D1C8123238CC93BC17508C6450401FE069323EB12BE0C22B9080038047403
:1B0D37810883C609E2F7B0FFC3B000C3803EE222FF750B8B1E112203DA891E67
:1B0D52811322C3B002E8D4FF837C0700742E8BFEB80300E8C6FF837C0700748D
:1B0D6D8119E8E3FED1EA8BDA035C053B5C0776072B5C0703D3EB03895C070328
:1B0D88815505895507C3B003E89BFF035405895407C3837C07007404034407C9
:1B0DA381C3034405C3C606D62200A1D722803EE222FF750DC606D62201A30FCA
:1B0DBE8122A3D422EB2CBE0C22B90800803C007419803C027503A3D422837C03
:1B0DD9810300750E894403803C097503C60401E8AFFF83C609E2DAC383FD70C9
:1B0DF481750E5306E835FE075BBD0000BE5422C383C510C38BD82500F0B1040E
:1B0E0F81D2ECB104D3E383EB017302FECCC3A19D24A32323A19924A3DC22A1C2
:1B0E2A819424A3E022A19624A3DE228CCAB133E806FEBA0C22B11AE8FEFDE82D
:1B0E4581E9FD803E9B24007403E9CD00E817FE84C075F6E84DFFBA5422B11AA6
:1B0E6081E8DFFDBD7000B90800BB0C2251803F0074248E4703BF00008B4F0141
:1B0E7B81E31951E870FFB90800FCF3A583FF0075078CC280C6108EC259E2E7CE
:1B0E96815983C309E2D08E06D4228C069B24BF0000B93000B000FCF3AABF00DB
:1B0EB18100A0D62226884505B201BE0C22B908003814740783C609E2F7EB30A8
:1B0ECC818B4403268945038B4405837C070074038B4407E826FF268865022652
:1B0EE781891D803EE222FF750C83C7062688650226891DEB0AFEC283C70680D1
:1B0F0281FA0876B5A1DC22A39924C606982400A1E022A39424A1DE22A39624A3
:1B0F1D81C3C7069B24FFFFEBE0A010233C01741FC6060A0000803E93230177BB
:1B0F388105E86801EB0EE8F2018B1E9423E8E900FE0E9323C606982400BB03BA
:1B0F538125E9B7061E8E1E9D248BDA8A078B4F018B57031F5B33DB8AD802C04A
:1B0F6E8102D881C30025FFE38916A2248BCAE8B415C38B1EA224891E25238BAB
:1B0F89811EA024E9B211832E9423058B3E94238B0789058B4702894502C64582
:1B0FA48104008006292301C3E89B01750A803E2A23007473E8ED01756E8B449A
:1B0FBF810239470272038947028B048907EB53E87901755883C604FEC13A0EEB
:1B0FDA812A23734DE8480275F08B440203042B073947027203894702EB2DE804
:1B0FF58153017532E8DD01752DE86B01E88BFFEB1BE84101752083C604FEC166
:1B1010813A0E2A237315E8100275F0E84302E86EFFA02A2388470432C0EB02A7
:1B102B81B0FFA29B24C38B4702A3D9228B07A3D722BBD722E81101EB0EE80324
:1B10468101807F04FF7505E8E300EBCF837F020074D3E86C0275CEE892027438
:1B106181C9E863003C0075C2E8C40275168BFE8AE9E8E60274088B47020145D1
:1B107C8102EBA2E83802EB9DE8D402750C8B0789048B4702014402EB8CE8B710
:1B109781018B0789048B4702894402EBEF33C0A22923A29323B89323A394231F
:1B10B281E881148BF38A0CD0E1D0E1FEC132EDBF2A23FCF3A4C38A0E2923B5D6
:1B10CD8100BF8E238B073905742173178B150355020347023BC2750B8B450293
:1B10E8812B4702894502EB4083EF05E2DCB0FFC38B47023B4502741173F30312
:1B1103810589058B45022B4702894502EB1F8B45FB89058B45FD8945028A45D8
:1B111E81FF88450483EF05E2D1FE0E29238306942305B000C3803E29230074AD
:1B1139810F8B1E9423807F04007505E8EAFEEBEAC3B105E88DFA8B1E9924B18A
:1B115481FFBE2723B0FF803E2923087308837F02007402FEC00AC0C3518B44D7
:1B116F81022B470275098B048907E80B00EB078944020304890759C35156576C
:1B118A81A02A23FEC8A22A232AC1740E8AC832ED03C98BFE83C604FCF3A55FB4
:1B11A5815E59C3525733D2B5FF83C604FEC13A0E2A23730F8B44023BC272EE81
:1B11C0818BD08AE98BFEEBE6B0FF8BF78ACDFEC57402FEC00AC05F5AC35257FD
:1B11DB81BAFFFFB5FF83C604FEC13A0E2A2373168B44022B4702741A72EB3B77
:1B11F681C273E78BD08AE98BFEEBDFB0FF8BF78ACDFEC57402FEC00AC05F5A1E
:1B121181C38B47020307C353E8F6FF8946028BDEE8EEFF8946005BC35583ECED
:1B122C81048BEC51B5FFE8E3FF8B073B04720A8B46003B46027202B5008AC5C3
:1B1247815983C4045D0AC0C3A02A23FE062A23B404F6E48BF08DB42B23C3558B
:1B12628183EC048BEC5251E8ACFF8B073B0475198B46023B460075075951E80F
:1B127D8108FFEB3289048B4702294402EB288B46002B460275098B072B0489C2
:1B1298814402EB178B072B04894402E8A9FF8B460289042B4600F7D8894402E3
:1B12B381595A83C4045DC38B4702034402014502E8C1FEC351525653E8681204
:1B12CE818A2F8BF383EE035BB1FF83C604FEC13ACD7309E845FF75F2B000EB11
:1B12E98102B0FF5E5A590AC0C35583EC048BEC5651BE2723B1FF83C604FEC170
:1B1304813A0E2A23731BE80BFF8B073B0473098B46023B047707EBE33B46000C
:1B131F8173DEB000EB02B0FF595E83C4045D0AC0C35583EC048BECBE2723B1B1
:1B133A81FF83C604FEC13A0E2A23730CE8CFFE8B072B460075EBEB02B0FF83C1
:1B135581C4045D0AC0C35583EC048BECBE2723B1FF83C604FEC13A0E2A23733F
:1B1370810CE8A4FE8B46022B0475EBEB02B0FF83C4045D0AC0C3BBE6228A07C4
:1B138B81C607000AC07525E96C02E8EEFFE80A00721A508AC8E8C00058C33C4A
:1B13A6810D740E3C0A740A3C0974063C0874023C20C3A0E6220AC0754BE84462
:1B13C18102240174F1E838023C13753AE831023C03740332C0C3C606E522008B
:1B13DC81803E932300750BE8200CA09B24A20508EB0EF606AF24FF740F803E57
:1B13F7819323017508C6060A0000E948FBE926FBA2E622B001C3A0E3220AC08D
:1B141281751651E8A0FF5951E8EE015951803EB624007403E8D301598AC1BB86
:1B142D81E5223C7F7416FE073C207310FE0F8A070AC074088AC13C087503FE0A
:1B1448810FC33C0A75FBC60700C38AC1E84EFF730A50B15EE8AEFF5980C9401D
:1B14638180F90975A5B120E8A0FFA0E522240775F4C3E80500B120E88E01B115
:1B147E8108E98901B123E886FFE81000A0E5223A06E42273E1B120E875FFEBC5
:1B149981F0B10DE86EFFB10AE969FF8BF1AC3C2474C941518AC81E161FE8AE16
:1B14B481FF1F59EBEBA0E522A2E4228B1E9924268A0F4353B5005153E8B7FE4F
:1B14CF815B593C0D7503E9C6003C0A74F93C08750E0AED74E6FECDA0E522A27E
:1B14EA81E322EB513C7F750C0AED74D4268A07FECD4BEB7C3C05750C5153E828
:1B15058193FFC606E42200EBBE3C107506F616B624EBB23C1875145BA0E42215
:1B152081BBE522903A07739190FE0FE847FFEBED3C157507E84BFF5BE97EFF35
:1B153B813C12753551E83FFF595B53510AED740F43268A0FFECD5153E8FCFE80
:1B1556815B59EBED53A0E3220AC074102A06E522A2E322E809FFFE0EE32275D3
:1B157181F7EB9843268807FEC551538AC8E8D1FE5B59268A073C038AC5750B83
:1B158C813C0175078CD88EC0E940FE3AC17303E92CFF5B26882FB10D8CD88EC4
:1B15A781C0E962FEE8E7FDEB44E8620FEB3F8AC1FEC07409FEC0754CE84400F0
:1B15C281EB30E83F000AC07503E9E60BE83000EB21E8630FEB1C8BCAE85F0FFF
:1B15DD81C38BCA1E8E1E9D24E8BCFE1FC38E069D24E9C8FEE8C4FDA29B24C37A
:1B15F881B001EBF8BB0F25EB0DBB0925EB08BB0625EB03BB0C25FF1E0022C33E
:1B161381FFD3CBBB0622FF2751B5008BF28BFBFCF3A459C38A0EA424E8ED0E9A
:1B162E8183FB0074508B1783C302891EEC2283C302891EEE2283C302891EF061
:1B1649812283C30287DA891E09238B1EF222891EAD24BBF222B108E8B8FF8B80
:1B16648116F422BBFA22B10FE8ACFFA00023BB1223C607FF0AC07403C6070007
:1B167F81B0FF0AC0C3E8910E33C08B1EEE228907C38B161A23E8900EEB078A2D
:1B169A810E0D23E88A0E0AC074DFBB0422E96FFF8B1EA624B102D3EB891E1AFC
:1B16B58123891E1F23A11A23BA0000F736FA22528BC88B36EE22890C030E0789
:1B16D08123E84A0E598B160923E8540E8BCBE9400EBBFC228A0FA01823D2E817
:1B16EB818AE8B1072A0FA01723D2E002C5C38B1E992483C31003D9803E12235F
:1B1706810074058A1FB700C303D98B1FC3E8CBFF8AC8B500E8DCFF891E1A2302
:1B172181C38B1E1A230BDBC38A0EFC228B1E1A23D3E3891E1C23A0182322069F
:1B173C81FD220AD8891E1A23C38B1E992483C30C8A07C3BA0F0003169924BB03
:1B175781110003DAC3E8F0FF8A07A2182387DA8A07A21623E8D7FFA0FE222293
:1B17728107A21723C3E8D5FFA00F233C02750232C002061823880787DAA01617
:1B178D81238807C3B980008B1EF22232C0020743E2FBC3518A0EA424BB01000A
:1B17A881D3E3590BD9C3A1E8228A0EA424D3E82401C38B0EE822E8DFFF891E31
:1B17C381E8228B160123428B1EEC228917C3E81B00BA090003DA8A07D0D07318
:1B17DE811BBB0A22EB08E8C7FF7411BB0822E92AFE8B1EF222A01E23B40003FC
:1B17F981D8C38B1E992483C30E8A07C3E8F3FFC60700C3E8ECFF0C808807C390
:1B1814818B16A6248B1EEC223B17C3E8F2FF72FA428917C3522BD38BDA5AC345
:1B182F81B1FF8B161F238B1E0523E8ECFF73E051E84FFF8B1E1F23031EF622E8
:1B184A8159FEC1740D3A07740BE8BEFF7306E85FFFC38807C3E8CDFFE819007B
:1B186581B101E833FEEB06E80F00E81EFE8B0EA224E8BA0CBBA024EB088CD94C
:1B188081E8B00CBBF2228B0FE9990C8B16F2228B1EA024B180068E069D24E89B
:1B189B817EFD07C3BBA6248A07433A077502FEC0C3BBFFFF891EA624C38B164C
:1B18B68101238B1EA62443891EA624E864FF7302EBE4A0A624240351B105D252
:1B18D181E059A21E230AC0752651E8CCFDE88BFF59E94CFF8BD180E107FEC176
:1B18EC818AE9B103D3EA8B1EF82203DA8A078ACDD2C08BD1C352E8E0FF24FE08
:1B190781590AC1518ACED2C8880759C3E8D9FE83C31051B1115AFEC97501C3B0
:1B19228152803E122300740851538A0FB500EB07FEC9518B0F43530BC9740BE9
:1B193D818B1EFF223BD97203E8B9FF5B4359EBCF8B1EFF22B103D3EB438BCB95
:1B1958818B1EF822C6070043E2FA8B1603238B1EF8228917E815FD8B1EEC2269
:1B197381C7070300E832FFB1FFE835FFE81DFF743BE868FE803FE574EEA0A5D6
:1B198E81243A07750C438A072C247505FEC8A29B24B101E86FFFE878FEEBD1F0
:1B19A981A00E23E945FC518A2EFE22F6D522CD22C52AC1241F59C38B1E99242D
:1B19C481891E2123C6060E23FF880EA824C3E8EBFFE8D4FEE8A9FCC6069A23D9
:1B19DF8100B100E8CFFEE8B7FE7503E980008B1621238BF2AC3CE5740752E8A4
:1B19FA8118FE5A73EBE8EDFD8A0EA824B5000AC974388BF2AC3C3F742980FD5B
:1B1A15810D742480FD0C74082A07247F7533EB17518A0FE884FF599FF6069A2A
:1B1A308123FF7545C6069723009E759F4243FEC5FEC9EBC4C6060E2300A0A605
:1B1A4B81242403A29B248AE8FEC5C30AED7530F607FF752BF6069723FF7424D6
:1B1A6681C6069A23FFEBCFE83CFEB0FF8AE8FEC5E97BFB9E750E43803F0075A5
:1B1A8181088B36A62489369823E94EFFE854FDB10CE83DFFE807FE74BBE83434
:1B1A9C81FDE84FFDC607E5B100E86BFEE8B4FDE82DFFEBE58BD10BC9740C4953
:1B1AB7815251E829FED0D87318595A3B16FF22731A4251528BCAE815FED0D81F
:1B1AD28173045A59EBDAD0D0FEC0E82BFE5B5AC30BC975CCBB0000C3B100B2AC
:1B1AED812052B5008B16992403D1E8F5FC59E81DFBE8A9FBE95BFDE8DDFCB193
:1B1B08810CE8C6FE8B1E99248A0783C3108807E885FD7501C3E8B1FCB110B202
:1B1B23810CE8C7FFE8B1FEEBEBB10CE8A1FEE86BFD74E6B100B20CE8B1FFE822
:1B1B3E819BFEEBEFB10FE88BFEE855FD74D0E8F6FB5053E89BFC87DA8B1E994B
:1B1B598124B12052E8BBFAE8A9FC5ABB0C0003DA8A0FBB0F0003DA8A2F5B58D5
:1B1B748188073A0F8AC57406B0007202B0808B1E9924BA0F0003DA8807C383FF
:1B1B8F813F0075FA8BF2AD8907C333C0A29B24A3A624E80AFC75F2E852FC241F
:1B1BAA818075EBB10FE820FEE8EAFC74E1B91000E832FC03D987DA8B1E992454
:1B1BC58103D9B110803E122300741B8A070AC08BF2AC750288070AC075068A0C
:1B1BE081078BFAFCAA3A077547EB16E8A0FF87DAE89BFF87DA8BF28B043B0720
:1B1BFB8175334243FEC94243FEC975C2B9ECFF03D987DA03D98BF2AC3A07723E
:1B1C1681118807B9030003D987DA03D98A078BFAFCAAC6060B23FFE9CEFEFE55
:1B1C31810E9B24C3E8ACFBFF369924C7069924E722B101E88BFDE855FC8F0683
:1B1C4C81992474E48B169924BB0F0003DAB11132C0880743FEC975F9BB0D005F
:1B1C678103DA8807E8B1FBE879FEE998FB32C0A20B23E81DFFE820FC74B38B8A
:1B1C82811E992483C30C8A07FEC0241F8807740F8AE8A0FE2222C522060B2386
:1B1C9D81740DEB2683C302FE078A07240F7426B10FE821FDE8EBFB7511A00CA8
:1B1CB88123FEC07415E875FFE8DCFB740DEB03E882FEE88FFA32C0E922F9E8E5
:1B1CD38123F9E934FBC6060F2301C6060C23FFE877FAA018233A061623721811
:1B1CEE813C807528E87FFFE857FA33C0A318238807803E9B24007514E80AFA10
:1B1D0981E816FA740CE818FAE8A6F9E879F9E95DFAE9DBF8C6060F2301C60624
:1B1D24810C2300E8BAFA8B1E9924E8A3FAE828FAA018233C807203E9BAF8E8D4
:1B1D3F81D2F9E8DEF9B1007548E896F9A2112333C90AC074088AC849E89FF966
:1B1D5A818BCBE851FD0BDB7505B002E98CF8891E1A2387DA8B1E992483C310E1
:1B1D7581803E122300A01123B400740603D88817EB0703D803D8891743B10225
:1B1D9081803E9B24007401C3880E0D23E88AF9803E0F23027545803E0D230235
:1B1DAB81753EFF361A238B3EF22233C0B94000FCF3ABE8BEFA8B1E1C23891EE5
:1B1DC6811A23E8EFF8E8CBF88B1E1A23C6060D23008AC38A36FD2222C6433A57
:1B1DE181C675E08F061A23E887FAE8CCF8E8A8F8A01823BB16233A07720688CC
:1B1DFC8107FE07B10250E8F6F9247F8807583C7F751E803E0F23017517E85FC9
:1B1E1781F9E859FEBB9B248A070AC07505FEC8A21823C60700E948F9C6060F33
:1B1E32812300518B169924BB210003DA8A07247F508A07D0D0438A07D0D0243C
:1B1E4D811F8AC88A07D0D8D0D8D0D8D0D8240F8AE858438A1F0ADBB3067569F2
:1B1E6881BB200003DA8807BB0C0003DA8AC12A07750DBB0E0003DA8AC52A07CF
:1B1E8381247F743C5152E80DFD5A59B303A09B24FEC07432BB0C0003DA880F74
:1B1E9E81BB0E0003DA882FE89AFCA09B24FEC075145951B304FEC17412E87B1E
:1B1EB981FDB305A09B24FEC074065932C0E92BF753E82EF9C607C05B598AC3FB
:1B1ED481A29B24E932F9B1FFE850FF7503E8F9FDC3B100E845FF7503E833FE8F
:1B1EEF81C387DA03DA8A0FB500BB0C0003DA8A27B000D1E880E40F03C8BB0E43
:1B1F0A810003DA8A07B410F6E45002E89C5B8AC35B0AC32401C3B10CE8ADFA55
:1B1F25818B1E9924BA210003DA538837438837438837E865F97422E8B0F8BAC6
:1B1F40810F00E8ABFF5B538AD08BC12B078AC21A47027205885702890FE87FD8
:1B1F5B81FAEBD95BC38B1E9924BA2000E886FFBB210003DA890F884702C38BF1
:1B1F76811EEA228A0EA424D3CB5387DAE8A2F65B7503E88BF6D0DB72678B0E1A
:1B1F9181EA22E80AF8891EEA22E9B0F9A01023A2A424EBD0E85CECEB03E85C1F
:1B1FAC81EC8B1E9924C606962300F647088074098067087FC606962360C60661
:1B1FC7811323FF8B1E99248A07241FFEC8A210233C1E7312A0A424A214238ACA
:1B1FE28107A2152324E08807E8B0FFA0A5248B1E99240807C3803E962360756B
:1B1FFD81078B1E9924C60700C3F7060222FFFF75048C0E022233DB891EE82236
:1B201881891EEA228A1EB724881EA424E94EFFE87BFFE8D8F7803EA52400743E
:1B2033810BC7069923FE00C6069723FFE800FBE83C00F6069723FF7501C3C63F
:1B204E8106972300813E9923FE0074F28B1E982380E3FC4B891EA624C6069676
:1B20698123608B1E9924C60700B10FE849F9E861F9E8CDFAE80100C3E81BF8A3
:1B20848174C65B803E96236075128B1E99248A470A24807507A29623B0FFEB77
:1B209F817EC3E801FFE9F2FAE85AEB8B1E9D24891EAB24B1008B1E9924803FC4
:1B20BA813F7410E885F6803F3F7403E83DF7E8E2FEB10FE802F9EB168B1EAB13
:1B20D58124891E9D248B1EA924891E9924E8C0FEE8F3F8E9A0F7E8B7FEE89CEF
:1B20F081F9E9B5F8E8AEFEE8FDFEE9DBFBE8A5FEE91AFCE89FFEE8FCF6E92994
:1B210B81FBE896FEE8F2F9E994F88B1EEA22EB26A0A424E9D3F48916A024E94F
:1B2126814AF78B1EF822EB138B1EE822EB0DE86EFEE8F2F9E96CF88B1EF4223D
:1B214181891E9B24C3A010233CFF7505A0A524EBCC241FA2A524C3E84FFEE8A3
:1B215C8199FEE979FDE846FEE97EFDE840FEE9B3FDE899EAE9EDFDA19924F70F
:1B217781D050BBEA222307890758BBE82223078907C3803E13230074248B1E5C
:1B2192819924803E9623607504804F0880C60700A015230AC0740B8807A0141C
:1B21AD8123A21023E8E9FD803E2723FF7503E853EAA19B248BD8C3E8E3FDC618
:1B21C881060F2302B100E863FC75F0E94CFB1EB98000BF0B008B36A0248E1E62
:1621E381A224AC0AC07403AAE2F8B0802AC1A20A001FE93AEDC375
:02000086000078
:0C22008213160000A40BB00BB50BBA0B38
:1B22D48200000000000000000000000000000000000000E50000000000000088
:1B22EF8200000000000000000000000000000000000000000000000000000052
:1B230A8200000000000000000000000000000000000000000000000000000036
:1B2325820000000000000000000000000000000000000000000000000000001B
:1B23408200000000000000000000000000000000000000000000000000000000
:10235B8200000000000000000000000000000000F0
:1B239382000000000000000042646F7320457272204F6E20203A20244261643A
:1823AE8220536563746F722453656C6563742446696C6520522F4F24C9
:0B2494820000000000000000000000BB
:1B24A0820000000000000000000000000000000000000000000000000000009F
:0224BB8200009D
:0124C8820091
:0224E082500028
:0124FA82005F
:00000001FF
:0400000300000521D3
:03052181E91C054C
:0F0A4081803EE0243C7203E9DAFA2401E9D5FA19
:00000001FF
:180A00823132204A616E75617279203139383200000000000012018276
:00000001FF
:04000003000021E6F2
:0521E681AA0AC0740289
:0421F1811FA20A009E
:00000001FF
:0400000300000A30BF
:0F0A3081A12323A39D24C7069B24FFFFE9C704AD
:030F1E81E90FFB5C
:00000001FF
:0400000300000B7777
:020B778133C008
:00000001FF
:04000003000018964B
:041896818E06A22473
:00000001FF
:0400000300000E23C8
:060E238106908916DC2215
:040F1A81E903FB90DB
:050A2081A3962407C329
:00000001FF
:0400000300000A509F
:0C0A50817207C7069B24FFFFC3E93B012E
:030B9481E9B9FE3D
:00000001FF
:0400000300000A00EF
:1B0A00812EC606982401502E8A2627232EA0132350CDE0582EA213232E8826F0
:050A1B81272358C39060
:090C4281E9BBFD90909090909027
:00000001FF


View File

@@ -0,0 +1,349 @@
;*****************************************************
;* *
;* Sector Blocking / Deblocking *
;* *
;* This algorithm is a direct translation of the *
;* CP/M-80 Version, and is included here for refer- *
;* ence purposes only. The file DEBLOCK.LIB is in- *
;* cluded on your CP/M-86 disk, and should be used *
;* for actual applications. You may wish to contact *
;* Digital Research for notices of updates. *
;* *
;*****************************************************
;
;*****************************************************
;* *
;* CP/M to host disk constants *
;* *
;* (This example is setup for CP/M block size of 16K *
;* with a host sector size of 512 bytes, and 12 sec- *
;* tors per track. Blksiz, hstsiz, hstspt, hstblk *
;* and secshf may change for different hardware.) *
;*****************************************************
una equ byte ptr [BX] ;name for byte at BX
;
blksiz equ 16384 ;CP/M allocation size
hstsiz equ 512 ;host disk sector size
hstspt equ 12 ;host disk sectors/trk
hstblk equ hstsiz/128 ;CP/M sects/host buff
;
;*****************************************************
;* *
;* secshf is log2(hstblk), and is listed below for *
;* values of hstsiz up to 2048. *
;* *
;* hstsiz hstblk secshf *
;* 256 2 1 *
;* 512 4 2 *
;* 1024 8 3 *
;* 2048 16 4 *
;* *
;*****************************************************
secshf equ 2 ;log2(hstblk)
cpmspt equ hstblk * hstspt ;CP/M sectors/track
secmsk equ hstblk-1 ;sector mask
;
;*****************************************************
;* *
;* BDOS constants on entry to write *
;* *
;*****************************************************
wrall equ 0 ;write to allocated
wrdir equ 1 ;write to directory
wrual equ 2 ;write to unallocated
;
;*****************************************************
;* *
;* The BIOS entry points given below show the *
;* code which is relevant to deblocking only. *
;* *
;*****************************************************
seldsk:
;select disk
;is this the first activation of the drive?
test DL,1 ;lsb = 0?
jnz selset
;this is the first activation, clear host buff
mov hstact,0
mov unacnt,0
selset:
mov al,cl ! cbw ;put in AX
mov sekdsk,al ;seek disk number
mov cl,4 ! shl al,cl ;times 16
add ax,offset dpbase
mov bx,ax
ret
;
home:
;home the selected disk
mov al,hstwrt ;check for pending write
test al,al
jnz homed
mov hstact,0 ;clear host active flag
homed:
mov cx,0 ;now, set track zero
; (continue HOME routine)
ret
;
settrk:
;set track given by registers CX
mov sektrk,CX ;track to seek
ret
;
setsec:
;set sector given by register cl
mov seksec,cl ;sector to seek
ret
;
setdma:
;set dma address given by CX
mov dma_off,CX
ret
;
setdmab:
;set segment address given by CX
mov dma_seg,CX
ret
;
sectran:
;translate sector number CX with table at [DX]
test DX,DX ;test for hard skewed
jz notran ;(blocked must be hard skewed)
mov BX,CX
add BX,DX
mov BL,[BX]
ret
no_tran:
;hard skewed disk, physical = logical sector
mov BX,CX
ret
;
read:
;read the selected CP/M sector
mov unacnt,0 ;clear unallocated counter
mov readop,1 ;read operation
mov rsflag,1 ;must read data
mov wrtype,wrual ;treat as unalloc
jmp rwoper ;to perform the read
;
write:
;write the selected CP/M sector
mov readop,0 ;write operation
mov wrtype,cl
cmp cl,wrual ;write unallocated?
jnz chkuna ;check for unalloc
;
; write to unallocated, set parameters
;
mov unacnt,(blksiz/128) ;next unalloc recs
mov al,sekdsk ;disk to seek
mov unadsk,al ;unadsk = sekdsk
mov ax,sektrk
mov unatrk,ax ;unatrk = sektrk
mov al,seksec
mov unasec,al ;unasec = seksec
;
chkuna:
;check for write to unallocated sector
;
mov bx,offset unacnt ;point "UNA" at UNACNT
mov al,una ! test al,al ;any unalloc remain?
jz alloc ;skip if not
;
; more unallocated records remain
dec al ;unacnt = unacnt-1
mov una,al
mov al,sekdsk ;same disk?
mov BX,offset unadsk
cmp al,una ;sekdsk = unadsk?
jnz alloc ;skip if not
;
; disks are the same
mov AX, unatrk
cmp AX, sektrk
jnz alloc ;skip if not
;
; tracks are the same
mov al,seksec ;same sector?
;
mov BX,offset unasec ;point una at unasec
;
cmp al,una ;seksec = unasec?
jnz alloc ;skip if not
;
; match, move to next sector for future ref
inc una ;unasec = unasec+1
mov al,una ;end of track?
cmp al,cpmspt ;count CP/M sectors
jb noovf ;skip if below
;
; overflow to next track
mov una,0 ;unasec = 0
inc unatrk ;unatrk=unatrk+1
;
noovf:
;match found, mark as unnecessary read
mov rsflag,0 ;rsflag = 0
jmps rwoper ;to perform the write
;
alloc:
;not an unallocated record, requires pre-read
mov unacnt,0 ;unacnt = 0
mov rsflag,1 ;rsflag = 1
;drop through to rwoper
;
;*****************************************************
;* *
;* Common code for READ and WRITE follows *
;* *
;*****************************************************
rwoper:
;enter here to perform the read/write
mov erflag,0 ;no errors (yet)
mov al, seksec ;compute host sector
mov cl, secshf
shr al,cl
mov sekhst,al ;host sector to seek
;
; active host sector?
mov al,1
xchg al,hstact ;always becomes 1
test al,al ;was it already?
jz filhst ;fill host if not
;
; host buffer active, same as seek buffer?
mov al,sekdsk
cmp al,hstdsk ;sekdsk = hstdsk?
jnz nomatch
;
; same disk, same track?
mov ax,hsttrk
cmp ax,sektrk ;host track same as seek track
jnz nomatch
;
; same disk, same track, same buffer?
mov al,sekhst
cmp al,hstsec ;sekhst = hstsec?
jz match ;skip if match
nomatch:
;proper disk, but not correct sector
mov al, hstwrt
test al,al ;"dirty" buffer ?
jz filhst ;no, don't need to write
call writehst ;yes, clear host buff
; (check errors here)
;
filhst:
;may have to fill the host buffer
mov al,sekdsk ! mov hstdsk,al
mov ax,sektrk ! mov hsttrk,ax
mov al,sekhst ! mov hstsec,al
mov al,rsflag
test al,al ;need to read?
jz filhst1
;
call readhst ;yes, if 1
; (check errors here)
;
filhst1:
mov hstwrt,0 ;no pending write
;
match:
;copy data to or from buffer depending on "readop"
mov al,seksec ;mask buffer number
and ax,secmsk ;least signif bits are masked
mov cl, 7 ! shl ax,cl ;shift left 7 (* 128 = 2**7)
;
; ax has relative host buffer offset
;
add ax,offset hstbuf ;ax has buffer address
mov si,ax ;put in source index register
mov di,dma_off ;user buffer is dest if readop
;
push DS ! push ES ;save segment registers
;
mov ES,dma_seg ;set destseg to the users seg
;SI/DI and DS/ES is swapped
;if write op
mov cx,128/2 ;length of move in words
mov al,readop
test al,al ;which way?
jnz rwmove ;skip if read
;
; write operation, mark and switch direction
mov hstwrt,1 ;hstwrt = 1 (dirty buffer now)
xchg si,di ;source/dest index swap
mov ax,DS
mov ES,ax
mov DS,dma_seg ;setup DS,ES for write
;
rwmove:
cld ! rep movs AX,AX ;move as 16 bit words
pop ES ! pop DS ;restore segment registers
;
; data has been moved to/from host buffer
cmp wrtype,wrdir ;write type to directory?
mov al,erflag ;in case of errors
jnz return_rw ;no further processing
;
; clear host buffer for directory write
test al,al ;errors?
jnz return_rw ;skip if so
mov hstwrt,0 ;buffer written
call writehst
mov al,erflag
return_rw:
ret
;
;*****************************************************
;* *
;* WRITEHST performs the physical write to the host *
;* disk, while READHST reads the physical disk. *
;* *
;*****************************************************
writehst:
ret
;
readhst:
ret
;
;*****************************************************
;* *
;* Use the GENDEF utility to create disk def tables *
;* *
;*****************************************************
dpbase equ offset $
; disk parameter tables go here
;
;*****************************************************
;* *
;* Uninitialized RAM areas follow, including the *
;* areas created by the GENDEF utility listed above. *
;* *
;*****************************************************
sek_dsk rb 1 ;seek disk number
sek_trk rw 1 ;seek track number
sek_sec rb 1 ;seek sector number
;
hst_dsk rb 1 ;host disk number
hst_trk rw 1 ;host track number
hst_sec rb 1 ;host sector number
;
sek_hst rb 1 ;seek shr secshf
hst_act rb 1 ;host active flag
hst_wrt rb 1 ;host written flag
;
una_cnt rb 1 ;unalloc rec cnt
una_dsk rb 1 ;last unalloc disk
una_trk rw 1 ;last unalloc track
una_sec rb 1 ;last unalloc sector
;
erflag rb 1 ;error reporting
rsflag rb 1 ;read sector flag
readop rb 1 ;1 if read operation
wrtype rb 1 ;write operation type
dma_seg rw 1 ;last dma segment
dma_off rw 1 ;last dma offset
hstbuf rb hstsiz ;host buffer
end


View File

@@ -0,0 +1,134 @@
:02000085000079
:02000085000079
:040000030000038076
:1B03808131053F07F705440712129007490767076C0772077E078507EC034376
:1B039B810EDA0D640EDE0EE40E0F0F130F140F1D0F1E0F1F0F200F260F2C0FF8
:1B03B681330F4C09390F3F0F400F460F470F480F490F4A0F4B0F8B078B078777
:1B03D1810F880F890FF10332053B0545054605460546054605460546053005A6
:0B03EC81B022E99703BBA011E94A0B86
:1B0400810000000000001E8CC88ED88F069D1180F905740580F90D7205C60685
:1B041B81B111FF8C16941189269611803E98110174078CC88ED0BC59118B1E83
:1B04368196118E069411268B5F0481E300027401FB8CD85557560E078C065DF6
:1B04518110E81A005E5F5D8E065D10FA8E1694118B269611C606B111008E1E0D
:1B046C819D11CF891699118816481032C0A39B11A25F10A24D10A24B10BB4CE3
:1B0487810F5380F929720980E90680F9367201C38AD9B7008ACA03DB2EFFA7E6
:1B04A2818003BB7610E819003C037555EB10BB8110EB08BB8D10EB03BB88101D
:1B04BD81E80300E9700053E86701A0A4110441A27210B96610E8630159E85FE3
:1B04D88101E96B001E5051C6065F10FF880E601032ED8B3699118936A911BF72
:1B04F3819110893E99118E1E9D11FCF3A459581FC351B121EB0351B124E8CDEF
:1B050E81FF59C30650518A0E601032ED8E069D11BE91108B3EA911893E9911D3
:1B052981FCF3A4595807C3C3C3C3C606981100E9C80C8916A2118BCAE8EF0C23
:1B054481C3C3C3BB1E108A07C607000AC07525E93F02E8EEFFE80A00721A505A
:1B055F818AC8E8930058C33C0D740E3C0A740A3C0974063C0874023C20C3A051
:1B057A811E100AC0751EE81702240174F1E80B023C13750DE804023C03740365
:1B05958132C0C3E998FFA21E10B001C3A01B100AC0751651E8CDFF5951E8EEAC
:1B05B081015951803EB611007403E8D301598AC1BB1D103C7F7416FE073C201A
:1B05CB817310FE0F8A070AC074088AC13C087503FE0FC33C0A75FBC60700C310
:1B05E6818AC1E87BFF730A50B15EE8AEFF5980C94080F90975A5B120E8A0FF85
:1B060181A01D10240775F4C3E80500B120E88E01B108E98901B123E886FFE8AF
:1B061C811000A01D103A061C1073E1B120E875FFEBF0B10DE86EFFB10AE9697D
:1B063781FF8BF1AC3C2474C941518AC81E161FE8AEFF1F59EBEBA01D10A21CBE
:1B065281108B1E9911268A0F4353B5005153E8E4FE5B593C0D7503E9C6003CD1
:1B066D810A74F93C08750E0AED74E6FECDA01D10A21B10EB513C7F750C0AED8E
:1B06888174D4268A07FECD4BEB7C3C05750C5153E893FFC6061C1000EBBE3C9D
:1B06A381107506F616B611EBB23C1875145BA01C10BB1D10903A07739190FE71
:1B06BE810FE847FFEBED3C157507E84BFF5BE97EFF3C12753551E83FFF595BA8
:1B06D98153510AED740F43268A0FFECD5153E8FCFE5B59EBED53A01B100AC0A0
:1B06F48174102A061D10A21B10E809FFFE0E1B1075F7EB9843268807FEC5519A
:1B070F81538AC8E8D1FE5B59268A073C038AC5750B3C0175078CD88EC0E96DB8
:1B072A81FE3AC17303E92CFF5B26882FB10D8CD88EC0E962FEE814FEEB44E8AE
:1B074581CE0AEB3F8AC1FEC07409FEC0754CE84400EB30E83F000AC07503E978
:1B0760811508E83000EB21E8CF0AEB1C8BCAE8CB0AC38BCA1E8E1E9D11E8BCAE
:1B077B81FE1FC38E069D11E9C8FEE8F1FDA29B11C3B001EBF8BB0F12EB0DBB07
:1B0796810912EB08BB0612EB03BB0C12FFD3C3FFD3CBBB0610FF2751B5008B65
:1B07B181F28BFBFCF3A459C38A0EA411E85B0A83FB0074508B1783C302891E18
:1B07CC81241083C302891E261083C302891E281083C30287DA891E41108B1EC7
:1B07E7812A10891EAD11BB2A10B108E8B8FF8B162C10BB3210B10FE8ACFFA0BD
:1B0802813810BB4A10C607FF0AC07403C60700B0FF0AC0C3E8FF0933C08B1E5B
:1B081D8126108907C38B165210E8FE09EB078A0E4510E8F8090AC074DFBB0420
:1B08388110E96FFF8B1EA611B102D3EB891E5210891E5710A15210BA0000F721
:1B085381363210528BC88B362610890C030E3F10E8B809598B164110E8C20959
:1B086E818BCBE9AE09BB34108A0FA05010D2E88AE8B1072A0FA04F10D2E00290
:1B088981C5C38B1E991183C31003D9803E4A100074058A1FB700C303D98B1F8C
:1B08A481C3E8CBFF8AC8B500E8DCFF891E5210C38B1E52100BDBC38A0E34101D
:1B08BF818B1E5210D3E3891E5410A05010220635100AD8891E5210C38B1E9974
:1B08DA811183C30C8A07C3BA0F0003169911BB110003DAC3E8F0FF8A07A25079
:1B08F5811087DA8A07A24E10E8D7FFA036102207A24F10C3E8D5FFA047103CE5
:1B09108102750232C002065010880787DAA04E108807C3B980008B1E2A1032EA
:1B092B81C0020743E2FBC3518A0EA411BB0100D3E3590BD9C3A120108A0EA467
:1B09468111D3E82401C38B0E2010E8DFFF891E20108B163910428B1E24108969
:1B09618117C3E81B00BA090003DA8A07D0D0731BBB0A10EB08E8C7FF7411BB08
:1B097C810810E92AFE8B1E2A10A05610B40003D8C38B1E991183C30E8A07C380
:1B099781E8F3FFC60700C3E8ECFF0C808807C38B16A6118B1E24103B17C3E877
:1B09B281F2FF72FA428917C3522BD38BDA5AC3B1FF8B1657108B1E3D10E8EC53
:1B09CD81FF73E051E84FFF8B1E5710031E2E1059FEC1740D3A07740BE8BEFF48
:1B09E8817306E85FFFC38807C3E8CDFFE81900B101E833FEEB06E80F00E81E31
:1B0A0381FE8B0EA211E82808BBA011EB088CD9E81E08BB2A108B0FE907088B11
:1B0A1E81162A108B1EA011B180068E069D11E87EFD07C3BBA6118A07433A0765
:1B0A39817502FEC0C3BBFFFF891EA611C38B1639108B1EA61143891EA611E87C
:1B0A548164FF7302EBE4A0A611240351B105D2E059A256100AC0750B51E8CC78
:1B0A6F81FDE88BFF59E94CFFC3E806FF83C31051B1115AFEC97501C352803E6C
:1B0A8A814A1000740851538A0FB500EB07FEC9518B0F43530BC974088B1E379E
:1B0AA581103BD972005B4359EBD28B1E3710B103D3EB438BCB8B1E3010C607BA
:1B0AC0810043E2FA8B163B108B1E30108917E845FD8B1E2410C7070300E862E4
:1B0ADB81FFB1FFE865FFE84DFF743BE898FE803FE574EEA0A5113A07750C43C2
:1B0AF6818A072C247505FEC8A29B11B101E872FFE8A8FEEBD1A04610E977FC4E
:1B0B1181518A2E3610F6D522CD22C52AC1241F59C38B1E9911891E5910C606DF
:1B0B2C814610FF880EA811C3E8EBFFE804FFE8D9FCC606651000B100E8FFFE75
:1B0B4781E8E7FE7503E980008B1659108BF2AC3CE5740752E848FE5A73EBE875
:1B0B62811DFE8A0EA811B5000AC974388BF2AC3C3F742980FD0D742480FD0C6B
:1B0B7D8174082A07247F7533EB17518A0FE884FF599FF6066510FF7545C6069F
:1B0B98816210009E759F4243FEC5FEC9EBC4C606461000A0A6112403A29B11F1
:1B0BB3818AE8FEC5C30AED7530F607FF752BF6066210FF7424C6066510FFEB46
:1B0BCE81CFE86CFEB0FF8AE8FEC5E9ADFB9E750E43803F0075088B36A6118954
:1B0BE981366310E94EFFB100B22052B5008B16991103D1E882FD59E8AAFBE8B3
:1B0C048136FCE9E8FDC3B10CE825FFE81FFE74F5B100B20CE8D8FFE81FFFEB3B
:1B0C1F81EFB10FE80FFFE809FE74DFE8AAFC5053E84FFD87DA8B1E9911B12068
:1B0C3A8152E86FFBE85DFD5ABB0C0003DA8A0FBB0F0003DA8A2F5B5888073AC5
:1B0C55810F8AC57406B0007202B0808B1E9911BA0F0003DA8807C3833F007555
:1B0C7081FA8BF2AD8907C333C0A29B11A3A611E8BEFC75F2E806FD248075EBDE
:1B0C8B81B10FE8A4FEE89EFD74E1B91000E8E6FC03D987DA8B1E991103D9B1FB
:1B0CA68110803E4A1000741B8A070AC08BF2AC750288070AC075068A078BFA16
:1B0CC181FCAA3A077547EB16E8A0FF87DAE89BFF87DA8BF28B043B07753342F0
:1B0CDC8143FEC94243FEC975C2B9ECFF03D987DA03D98BF2AC3A0772118807B6
:1B0CF781B9030003D987DA03D98A078BFAFCAAC6064310FFE9F5FEFE0E9B111E
:1B0D1281C332C0A24310E85CFFE813FD74F28B1E991183C30C8A07FEC0241FC3
:1B0D2D818807740F8AE8A0361022C522064310740DEB1C83C302FE078A0724D4
:1B0D48810F741CB10FE8E4FDE8DEFC7507A04410FEC0740BE8CBFEE88CFB3226
:1B0D6381C0E921FAE822FAE931FCC606471001C6064410FFE874FBA050103A42
:1B0D7E81064E1072183C807528E889FFE854FB33C0A350108807803E9B1100FC
:1B0D99817514E807FBE813FB740CE815FBE8A3FAE876FAE95AFBE9DAF98B1E5D
:1B0DB48122108A0EA411D3CB5387DAE8F7F95B7503E8E0F9D0DB72678B0E2227
:1B0DCF8110E85FFB891E2210E9D5FCA04810A2A411EBD0E81FF7EB03E81FF7AF
:1B0DEA818B1E9911C606611000F647088074098067087FC606611060C6064B79
:1B0E058110FF8B1E99118A07241FFEC8A248103C1E7312A0A411A24C108A0798
:1B0E2081A24D1024E08807E8B0FFA0A5118B1E99110807C3803E611060750787
:1B0E3B818B1E9911C60700C3F7060210FFFF75048C0E021033DB891E20108998
:1B0E56811E22108A1EB711881EA411E94EFFE87BFFE82DFB803EA51100740B4A
:1B0E7181C7066410FE00C6066210FFE8A1FDE83C00F6066210FF7501C3C6064D
:1B0E8C81621000813E6410FE0074F28B1E631080E3FC4B891EA611C606611060
:1B0EA781608B1E9911C60700B10FE86EFCE886FCE86EFDE80100C3E870FB74ED
:1B0EC281C65B803E61106075128B1E99118A470A24807507A26110B0FFEB4C16
:1B0EDD81C3E801FFE993FDE81DF68B1E9D11891EAB11B1008B1E9911803F3F9E
:1B0EF8817410E8DAF9803F3F7403E892FAE8E2FEB10FE827FCEB01C3E90AFB06
:1B0F1381C3E8CBFEE81AFFE950FEC3C3C38B1E2210EB1BA0A411E95CF889163B
:1B0F2E81A011E9D1FA8B1E3010EB088B1E2010EB02C3C3891E9B11C3C3C3C33B
:1B0F4981C3C3C3803E4B100074248B1E9911803E6110607504804F0880C60793
:1B0F648100A04D100AC0740B8807A04C10A24810E863FE803E5F10FF7503E851
:0C0F7F8190F5A19B118BD8C3C3C3C3C3E1
:02000086000078
:1B100082A5070000A404B004B504BA04000000000000000000000000000000D4
:1B101B8200000000E50000000000000000000000000000000000000000000053
:1B1036820000000000000000000000000000000000000000000000000000001D
:1B10518200000000000000000000000000000000000000000042646F73204515
:1B106C827272204F6E20203A202442616420536563746F722453656C6563744D
:0A1087822446696C6520522F4F2425
:0B1194820000000000000000000000CE
:1B11A082000000000000000000000000000000000000000000000000000000B2
:0211BB820000B0
:0111C88200A4
:0211E08250003B
:0111FA820072
:00000001FF
000000000000000000000000000D4
:1B101B8200000000E50000000

View File

@@ -0,0 +1,690 @@
title '8086 Disk I/O Drivers'
;*********************************************
;* *
;* Basic Input/Output System (BIOS) for *
;* CP/M-86 Configured for iSBC 86/12 with *
;* the iSBC 204 Floppy Disk Controller *
;* *
;* (Note: this file contains both embedded *
;* tabs and blanks to minimize the list file *
;* width for printing purposes. You may wish*
;* to expand the blanks before performing *
;* major editing.) *
;*********************************************
; Copyright (C) 1980,1981
; Digital Research, Inc.
; Box 579, Pacific Grove
; California, 93950
;
; (Permission is hereby granted to use
; or abstract the following program in
; the implementation of CP/M, MP/M or
; CP/NET for the 8086 or 8088 Micro-
; processor)
true equ -1
false equ not true
;*********************************************
;* *
;* Loader_bios is true if assembling the *
;* LOADER BIOS, otherwise BIOS is for the *
;* CPM.SYS file. Blc_list is true if we *
;* have a serial printer attached to BLC8538 *
;* Bdos_int is interrupt used for earlier *
;* versions. *
;* *
;*********************************************
loader_bios equ true
blc_list equ true
bdos_int equ 224 ;reserved BDOS Interrupt
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
csts equ 0DAh ;i8251 status port
cdata equ 0D8h ; " data port
IF blc_list
;---------------------------------------------
;| |
lsts equ 41h ;2651 No. 0 on BLC8538 status port
ldata equ 40h ; " " " " " data port
blc_reset equ 60h ;reset selected USARTS on BLC8538
;| |
;---------------------------------------------
ENDIF ;blc_list
;*********************************************
;* *
;* Intel iSBC 204 Disk Controller Ports *
;* *
;*********************************************
base204 equ 0a0h ;SBC204 assigned address
fdc_com equ base204+0 ;8271 FDC out command
fdc_stat equ base204+0 ;8271 in status
fdc_parm equ base204+1 ;8271 out parameter
fdc_rslt equ base204+1 ;8271 in result
fdc_rst equ base204+2 ;8271 out reset
dmac_adr equ base204+4 ;8257 DMA base address out
dmac_cont equ base204+5 ;8257 out control
dmac_scan equ base204+6 ;8257 out scan control
dmac_sadr equ base204+7 ;8257 out scan address
dmac_mode equ base204+8 ;8257 out mode
dmac_stat equ base204+8 ;8257 in status
fdc_sel equ base204+9 ;FDC select port (not used)
fdc_segment equ base204+10 ;segment address register
reset_204 equ base204+15 ;reset entire interface
max_retries equ 10 ;max retries on disk i/o
;before perm error
cr equ 0dh ;carriage return
lf equ 0ah ;line feed
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 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
pop ds ;restore the DS register
;*********************************************
;* *
;* National "BLC 8538" Channel 0 for a serial*
;* 9600 baud printer - this board uses 8 Sig-*
;* netics 2651 Usarts which have on-chip baud*
;* rate generators. *
;* *
;*********************************************
mov al,0FFh
out blc_reset,al ;reset all usarts on 8538
mov al,4Eh
out ldata+2,al ;set usart 0 in async 8 bit mode
mov al,3Eh
out ldata+2,al ;set usart 0 to 9600 baud
mov al,37h
out ldata+3,al ;enable Tx/Rx, and set up RTS,DTR
;| |
;---------------------------------------------
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
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
;---------------------------------------------
;| |
int_trap:
cli ;block interrupts
mov ax,cs
mov ds,ax ;get our data segment
mov bx,offset int_trp
call pmsg
hlt ;hardstop
;| |
;---------------------------------------------
ENDIF ;not loader_bios
;*********************************************
;* *
;* CP/M Character I/O Interface Routines *
;* Console is Usart (i8251a) on iSBC 86/12 *
;* at ports D8/DA *
;* *
;*********************************************
CONST: ;console status
in al,csts
and al,2
jz const_ret
or al,255 ;return non-zero if RDA
const_ret:
ret ;Receiver Data Available
CONIN: ;console input
call const
jz CONIN ;wait for RDA
in al,cdata
and al,7fh ;read data and remove parity bit
ret
CONOUT: ;console output
in al,csts
and al,1 ;get console status
jz CONOUT ;wait for TBE
mov al,cl
out cdata,al ;Transmitter Buffer Empty
ret ;then return data
LISTOUT: ;list device output
IF blc_list
;---------------------------------------------
;| |
call LISTST
jz LISTOUT ;wait for printer not busy
mov al,cl
out ldata,al ;send char to TI 810
;| |
;---------------------------------------------
ENDIF ;blc_list
ret
LISTST: ;poll list status
IF blc_list
;---------------------------------------------
;| |
in al,lsts
and al,81h ;look at both TxRDY and DTR
cmp al,81h
jnz zero_ret ;either false, printer is busy
or al,255 ;both true, LPT is ready
;| |
;---------------------------------------------
ENDIF ;blc_list
ret
PUNCH: ;not implemented in this configuration
READER:
mov al,1ah
ret ;return EOF for now
GETIOBF:
mov al,0 ;TTY: for consistency
ret ;IOBYTE not implemented
SETIOBF:
ret ;iobyte not implemented
zero_ret:
and al,0
ret ;return zero in AL and flags
; Routine to get and echo a console character
; and shift it to upper case
uconecho:
call CONIN ;get a console character
push ax
mov cl,al ;save and
call CONOUT
pop ax ;echo to console
cmp al,'a'
jb uret ;less than 'a' is ok
cmp al,'z'
ja uret ;greater than 'z' is ok
sub al,'a'-'A' ;else shift to caps
uret:
ret
; utility subroutine to print messages
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
mov bx,0000h
cmp cl,2 ;this BIOS only supports 2 disks
jnb return ;return w/ 0000 in BX if bad drive
mov al, 80h
cmp cl,0
jne sel1 ;drive 1 if not zero
mov al, 40h ;else drive is 0
sel1: mov sel_mask,al ;save drive select mask
;now, we need disk parameter address
mov ch,0
mov bx,cx ;BX = word(CL)
mov cl,4
shl bx,cl ;multiply drive code * 16
;create offset from Disk Parameter Base
add bx,offset dp_base
return:
ret
HOME: ;move selected disk to home position (Track 0)
mov trk,0 ;set disk i/o to track zero
mov bx,offset hom_com
call execute
jz return ;home drive and return if OK
mov bx,offset bad_hom ;else print
call pmsg ;"Home Error"
jmps home ;and retry
SETTRK: ;set track address given by CX
mov trk,cl ;we only use 8 bits of track address
ret
SETSEC: ;set sector number given by cx
mov sect,cl ;we only use 8 bits of sector address
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: the *
;* Read and Write entry points transfer one *
;* sector of 128 bytes to/from the current *
;* DMA address using the current disk drive *
;* *
;*********************************************
READ:
mov al,12h ;basic read sector command
jmps r_w_common
WRITE:
mov al,0ah ;basic write sector command
r_w_common:
mov bx,offset io_com ;point to command string
mov byte ptr 1[BX],al ;put command into string
; fall into execute and return
execute: ;execute command string.
;[BX] points to length,
; followed by Command byte,
; followed by length-1 parameter bytes
mov last_com,BX ;save command address for retries
outer_retry:
;allow some retrying
mov rtry_cnt,max_retries
retry:
mov BX,last_com
call send_com ;transmit command to i8271
; check status poll
mov BX,last_com
mov al,1[bx] ;get command op code
mov cx,0800h ;mask if it will be "int req"
cmp al,2ch
jb exec_poll ;ok if it is an interrupt type
mov cx,8080h ;else we use "not command busy"
and al,0fh
cmp al,0ch ;unless there isn't
mov al,0
ja exec_exit ; any result
;poll for bits in CH,
exec_poll: ; toggled with bits in CL
in al,fdc_stat ;read status
and al,ch
xor al,cl ; isolate what we want to poll
jz exec_poll ;and loop until it is done
;Operation complete,
in al,fdc_rslt ; see if result code indicates error
and al,1eh
jz exec_exit ;no error, then exit
;some type of error occurred . . .
cmp al,10h
je dr_nrdy ;was it a not ready drive ?
;no,
dr_rdy: ; then we just retry read or write
dec rtry_cnt
jnz retry ; up to 10 times
; retries do not recover from the
; hard error
mov ah,0
mov bx,ax ;make error code 16 bits
mov bx,errtbl[BX]
call pmsg ;print appropriate message
in al,cdata ;flush usart receiver buffer
call uconecho ;read upper case console character
cmp al,'C'
je wboot_l ;cancel
cmp al,'R'
je outer_retry ;retry 10 more times
cmp al,'I'
je z_ret ;ignore error
or al,255 ;set code for permanent error
exec_exit:
ret
dr_nrdy: ;here to wait for drive ready
call test_ready
jnz retry ;if it's ready now we are done
call test_ready
jnz retry ;if not ready twice in row,
mov bx,offset nrdymsg
call pmsg ;"Drive Not Ready"
nrdy01:
call test_ready
jz nrdy01 ;now loop until drive ready
jmps retry ;then go retry without decrement
zret:
and al,0
ret ;return with no error code
wboot_l: ;can't make it w/ a short leap
jmp WBOOT
;*********************************************
;* *
;* The i8271 requires a read status command *
;* to reset a drive-not-ready after the *
;* drive becomes ready *
;* *
;*********************************************
test_ready:
mov dh, 40h ;proper mask if dr 1
test sel_mask,80h
jnz nrdy2
mov dh, 04h ;mask for dr 0 status bit
nrdy2:
mov bx,offset rds_com
call send_com
dr_poll:
in al,fdc_stat ;get status word
test al,80h
jnz dr_poll ;wait for not command busy
in al,fdc_rslt ;get "special result"
test al,dh ;look at bit for this drive
ret ;return status of ready
;*********************************************
;* *
;* Send_com sends a command and parameters *
;* to the i8271: BX addresses parameters. *
;* The DMA controller is also initialized *
;* if this is a read or write *
;* *
;*********************************************
send_com:
in al,fdc_stat
test al,80h ;insure command not busy
jnz send_com ;loop until ready
;see if we have to initialize for a DMA operation
mov al,1[bx] ;get command byte
cmp al,12h
jne write_maybe ;if not a read it could be write
mov cl,40h
jmps init_dma ;is a read command, go set DMA
write_maybe:
cmp al,0ah
jne dma_exit ;leave DMA alone if not read or write
mov cl,80h ;we have write, not read
init_dma:
;we have a read or write operation, setup DMA controller
; (CL contains proper direction bit)
mov al,04h
out dmac_mode,al ;enable dmac
mov al,00
out dmac_cont,al ;send first byte to control port
mov al,cl
out dmac_cont,al ;load direction register
mov ax,dma_adr
out dmac_adr,al ;send low byte of DMA
mov al,ah
out dmac_adr,al ;send high byte
mov ax,dma_seg
out fdc_segment,al ;send low byte of segment address
mov al,ah
out fdc_segment,al ;then high segment address
dma_exit:
mov cl,[BX] ;get count
inc BX
mov al,[BX] ;get command
or al,sel_mask ;merge command and drive code
out fdc_com,al ;send command byte
parm_loop:
dec cl
jz exec_exit ;no (more) parameters, return
inc BX ;point to (next) parameter
parm_poll:
in al,fdc_stat
test al,20h ;test "parameter register full" bit
jnz parm_poll ;idle until parm reg not full
mov al,[BX]
out fdc_parm,al ;send next parameter
jmps parm_loop ;go see if there are more parameters
;*********************************************
;* *
;* Data Areas *
;* *
;*********************************************
data_offset equ offset $
dseg
org data_offset ;contiguous with code segment
IF loader_bios
;---------------------------------------------
;| |
signon db cr,lf,cr,lf
db 'CP/M-86 Version 1.1',cr,lf,0
;| |
;---------------------------------------------
ENDIF ;loader_bios
IF not loader_bios
;---------------------------------------------
;| |
signon db cr,lf,cr,lf
db ' System Generated - 11 Jan 81',cr,lf,0
;| |
;---------------------------------------------
ENDIF ;not loader_bios
bad_hom db cr,lf,'Home Error',cr,lf,0
int_trp db cr,lf,'Interrupt Trap Halt',cr,lf,0
errtbl dw er0,er1,er2,er3
dw er4,er5,er6,er7
dw er8,er9,erA,erB
dw erC,erD,erE,erF
er0 db cr,lf,'Null Error ??',0
er1 equ er0
er2 equ er0
er3 equ er0
er4 db cr,lf,'Clock Error :',0
er5 db cr,lf,'Late DMA :',0
er6 db cr,lf,'ID CRC Error :',0
er7 db cr,lf,'Data CRC Error :',0
er8 db cr,lf,'Drive Not Ready :',0
er9 db cr,lf,'Write Protect :',0
erA db cr,lf,'Trk 00 Not Found :',0
erB db cr,lf,'Write Fault :',0
erC db cr,lf,'Sector Not Found :',0
erD equ er0
erE equ er0
erF equ er0
nrdymsg equ er8
rtry_cnt db 0 ;disk error retry counter
last_com dw 0 ;address of last command string
dma_adr dw 0 ;dma offset stored here
dma_seg dw 0 ;dma segment stored here
sel_mask db 40h ;select mask, 40h or 80h
; Various command strings for i8271
io_com db 3 ;length
rd_wr db 0 ;read/write function code
trk db 0 ;track #
sect db 0 ;sector #
hom_com db 2,29h,0 ;home drive command
rds_com db 1,2ch ;read status command
; System Memory Segment Table
segtable db 2 ;2 segments
dw tpa_seg ;1st seg starts after BIOS
dw tpa_len ;and extends to 08000
dw 2000h ;second is 20000 -
dw 2000h ;3FFFF (128k)
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 0800h - tpa_seg
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 system call vector
rw 2*(bdos_int-1)
bdos_offset rw 1
bdos_segment rw 1
END


View File

@@ -0,0 +1,41 @@
:0400000300001200E7
:1B120081E93C00E96100E96100E96700E96E00E97600E98800E98500E9D000FB
:1B121B81E9AC00E9DF00E9E100E9EA00E9F500E9F600E96200E9D700E9E000AC
:1B123681E9E200E96400E964008CC88ED08ED88EC0BCA916FC1EB800008ED89E
:1B125181C706800306048C0E82031FBB1514E85A00B100E99CEDE99FEDE4DAED
:1B126C81240274020CFFC3E8F4FF74FBE4D8247FC3E4DA240174FA8AC1E6D8B6
:1B128781C3E8070074FB8AC1E640C3E44124813C81750A0CFFC3B01AC3B00065
:1B12A281C3C32400C3E8C9FF508AC8E8CDFF583C6172063C7A77022C20C38A08
:1B12BD810784C074288AC8E8B6FF43EBF2BB000080F9027318B08080F90075C0
:1B12D88102B040A22E15B5008BD9B104D3E381C34115C3C606311500BB3315AD
:1B12F381E8350074F2BB2F14E8BEFFEBEB880E3115C3880E3215C38BD903DAE3
:1B130E818A1FC3890E2A15C3890E2C15C3BB3815C3B012EB02B00ABB2F1588E8
:1B1329814701891E2815C60627150A8B1E2815E889008B1E28158A4701B90022
:1B134481083C2C720BB98080240F3C0CB0007736E4A022C532C174F8E4A1241C
:1B135F811E74283C107425FE0E271575C8B4008BD88B9F5614E845FFE4D8E853
:1B137A812BFF3C4374253C5274AB3C49741A0CFFC3E81A0075A4E81500759FDB
:1B139581BBC714E821FFE80A0074FBEB922400C3E9BFFEB640F6062E15807589
:1B13B08102B604BB3615E80B00E4A0A88075FAE4A184C6C3E4A0A88075FA8A9A
:1B13CB8147013C127504B140EB063C0A7520B180B004E6A8B000E6A58AC1E6DB
:1B13E681A5A12A15E6A48AC4E6A4A12C15E6AA8AC4E6AA8A0F438A070A062E89
:1414018115E6A0FEC9748243E4A0A82075FA8A07E6A1EBEF0E
:1B1415820D0A0D0A43502F4D2D38362056657273696F6E20312E310D0A000D88
:1B1430820A486F6D65204572726F720D0A000D0A496E746572727570742054F3
:1B144B827261702048616C740D0A00761476147614761486149614A314B41416
:1B146682C714DB14ED14021512157614761476140D0A4E756C6C204572726FD8
:1B14818272203F3F000D0A436C6F636B204572726F72203A000D0A4C6174659A
:1B149C8220444D41203A000D0A494420435243204572726F72203A000D0A44EC
:1B14B78261746120435243204572726F72203A000D0A4472697665204E6F7484
:1B14D282205265616479203A000D0A57726974652050726F74656374203A0091
:1B14ED820D0A54726B203030204E6F7420466F756E64203A000D0A5772697416
:1B15088265204661756C74203A000D0A536563746F72204E6F7420466F756EDB
:1B15238264203A00000000000000004003000000022900012C02AB01550600C9
:1B153E8220002070150000000000008A15611529160A16701500000000000052
:1B1559828A156115581639161A00030700F2003F00C0001000020001070D13D4
:1615748219050B111703090F1502080E141A060C1218040A1016A8
:0116688200FF
:0116A98200BE
:02000086000078
:00000001FF
2205265616479203A000D0A57726974652050726F74656374203A0091
:1B14ED820D0A54726B203030204E6F742046

View File

@@ -0,0 +1,508 @@
title 'LDCOPY Ver 1.1 Oct 81'
vers equ 10
; Copyright, (C) 1981
; Digital Research, Inc.
; P.O. Box 579
; Pacific Grove, CA 93950
; LDCOPY is used to generate and propagate bootable CP/M-86 systems.
; It copies a CP/M-86 Loader image (either from a disk file, or from
; the system tracks of an existing CP/M-86 system diskette) to a new
; diskette.
; The following equates will require modification for different
; diskette formats. For convienence, they are only referenced
; by a pair of data bytes near the end of the data segment.
; In addition, the skew table in the data area will need modification.
sdspt equ 26 ; number of sectors per track
sdlt equ 2 ; " of tracks for loader
; **************************
; *
; * global equates
; *
; **************************
bdosint equ 224 ;BDOS interrupt number
false equ 0
true equ not false
cr equ 0dh ;carriage return
lf equ 0ah ;line feed
sdndisks equ 16 ; maximum number of drives
sdsecsiz equ 128 ; size of each sector
tpa equ 100h
fcbaddr equ 5ch
fcbname equ fcbaddr + 1 ;1st char of file name
fcbtype equ fcbaddr + 9 ;file type
fcbcr equ fcbaddr + 32 ;current record
cseg
jmp start
db ' COPYRIGHT (C) 1981, DIGITAL RESEARCH '
Rs 100H ; Emergency patch area
; ***********************
; *
; * program begins here
; *
; ***********************
start:
mov ax,ds
mov es,ax
mov dx,offset signon
call outmsg
cmp byte ptr .fcbname,' '
jz gl ;check for file given on command line
call getfile ;read ldr from file
jmp ps
gl: call getldr ;read ldr from 1st 2 tracks
ps: call putldr ;put ldr on " " "
jmp reboot
; ***************************
; *
; * getfile: get the file specified in command tail into ram
; *
; ***************************
getfile:
cmp byte ptr .fcbtype,' '
jnz opn
mov byte ptr .fcbtype, 'C'
mov byte ptr .fcbtype + 1,'M'
mov byte ptr .fcbtype + 2,'D'
opn:
mov dx,fcbaddr ;try to open it
call open
inc al ;255 becomes 00
jnz rdok ;ok to read if not 255
mov dx,offset nofile
call crmsg ;file not present, error and reboot
jmp reboot
rdok:
mov byte ptr .fcbcr,0 ;current record = 0
mov dx,offset loadp ;base of buffer
mov cx,maxsecs ;sector count
rdinp:
push cx
push dx ;ready for dma
call bdma ;bdos dma function
mov dx,fcbaddr ;ready for read
call dread
pop dx ;recall dma address
pop cx ;recall sector count
cmp al,1 ;check for eof
jz don
dec cx ;don't read to far
jz don
or al,al ;0 if read ok
jnz badrd
add dx,secsiz ;inc dma by sector size
jmp rdinp
don:
ret
badrd: ;read error encountered in input file
mov dx,offset badfile
call crmsg
jmp reboot
; **************************
; *
; * getldr: get CP/M loader from 1st N tracks
; *
; **************************
getldr:
mov dx,offset askget
call crmsg ;which drive is source on ?
call getchar ;must get from disk - not from memory
mov ah,al ;save ascii char
sub al,'A' ;normalize drive number
cmp al,ndisks ;valid drive?
jb getc ;skip to getc if so
call baddisk ;invalid drive number
jmp getldr ;to try again
getc:
mov gdisk,ah ;ascii drive letter for message
call sel ;to select the drive in al
call crlf
mov dx,offset getmsg
call outmsg ;make sure right drive
call getchar
cmp al,cr ;user mistake, no cr
jnz gboot
call crlf
mov rewr,0 ;set to read for getput
call getput
mov dx,offset done
call outmsg
ret
gboot: jmp reboot ;back to cp/m
; *******************************
; *
; * putldr: put CP/M loader on 1st N tracks
; *
; *******************************
putldr:
mov dx,offset askput
call crmsg ;what drive to put ldr
call getchar
cmp al,cr ;all done if cr
jz pboot ;this is normal program exit point
mov ah,al ;save ascii drive letter
sub al,'A' ;make it a number
cmp al,ndisks
jb putc
call baddisk ;invalid drive name
jmp putldr ;to try again
putc:
mov pdisk,ah ;drive letter in message
call sel ;select dest drive in al
mov dx,offset putmsg
call crmsg ;check with user for ok
call getchar
cmp al,cr ;user mistake if not cr, reboot
jnz pboot
call crlf
mov rewr,1 ;set to write for getput
call getput ;to put loader back on diskette
mov dx,offset done
call outmsg
jmp putldr ;for another put operation
pboot: jmp reboot ;back to cp/m
; ***********************
; *
; * getput: get or put loader (rewr=0 for read, 1 for write)
; * disk is already selected
; *
; ***********************
getput:
mov bx,offset loadp ;load point in ram for cp/m during LDCOPY
mov dmaddr,bx
;clear track to 00
mov track,-1 ;start with track equal -1
rwtrk: ;read or write next track
inc track ;track = track + 1
mov cx,track
cmp cx,nlt ;number of loader tracks = current track ?
jnz nxttrk ;end of this routine > 128 bytes
jmp endrw ;end of read or write
;otherwise notdone, go to next track
nxttrk:
mov cx,track
call trk ;to set track
mov sector,-1 ;counts 0, 1, 2, . . . 25
;sector incremented before read or write
rwsec: ;read or write sector
inc sector ;to next sector
mov bx,sector ;current sector
cmp bx,spt ;sectors per track
jz endtrk
;read or write sector to or from
l_5: ;current dma address
mov si,offset tran
add bx,bx ; double sector number
mov cx,[bx+si] ;xlate to physical sector
push si
push cx
call sec ;set up sector number
pop cx
pop si
mov bx,[si]
sub cx,bx ;tran(sector)-tran(0)
call multsec ;cx * sector size
add cx,dmaddr ;base of dma for this track
;+(tran(sector)-tran(0))*secsiz
call dma ;dma address set from cx
;dma address set, clear retry count
trysec: ;try to read or write current sector
Mov Cl, 00
Mov Ax, Track
Inc Ax
Cmp Ax, Nlt
Jne Normal_Write
Mov Ax, Sector
Inc Ax
Cmp Ax, Spt
Jne Normal_Write
Mov Cl, 01
Normal_Write:
or rewr,0 ;read or write?
jz tryread
;must be write
call write
jmp chkrw ;check for error returns
tryread:
call read
chkrw:
or al,al
jz rwsec
mov dx,offset errmsg
call outmsg
call getchar
cmp al,cr
jne reboot1 ;local jmp then to reboot, >128
;typed a cr, ok to ignore
call crlf
jmp rwsec
endtrk: ;end of track
mov cx,spt ;sectors per track
call multsec ;*secsiz
mov bx,dmaddr ;base dma for this track
add bx,cx
;+ spt * secsiz
mov dmaddr,bx ;ready for next track
jmp rwtrk ;for another track
endrw: ;end of read or write, return to caller
ret
reboot1:jmp reboot ;farther than 128 bytes
; *****************************************
; *
; * utility subroutines
; *
; ****************************************
;
;*****
multsec: ;cx * sector size
push dx ;return value in cx
mov ax,secsiz
mul cx
mov cx,ax
pop dx
ret
;*****
baddisk:
mov dx,offset qdisk ;bad disk name
call crmsg
ret
; **********************
; *
; * bdos subroutines
; *
; **********************
bdos: int bdosint
ret
;function numbers in cl
reset equ 0 ;warm boot
coni equ 1 ;console input
cono equ 2 ;console output
prstr equ 9 ;print string
rconb equ 10 ;read console buffer
self equ 14 ;select disk
openf equ 15 ;disk open
setdmaf equ 26 ;where data will go
dreadf equ 20 ;disk read
biosf equ 50 ;bios call
reboot:
mov al,0
call sel
call crlf
mov cl,reset
mov dl,0 ; release memory
jmp bdos
;****
getchar: ;get an upper case char into al
call getbuf ;use buffered console read
cmp conbuf+1,0 ;just a crlf?
mov al,cr
jz ex
mov al,conbuf+2 ;first char read
cmp al,'a' ;translate to upper case if lower
jb ex
cmp al,'z'
ja ex
and al,5fh ;it is lower, make upper
ex: ret
;****
getbuf:
;read console buffer
mov cl,rconb
mov dx,offset conbuf
jmp bdos
;****
putchar:
;write character from al to console
mov dl,al
mov cl,cono
jmp bdos
;****
crlf: ;send carriage return, line feed
mov al,cr
call putchar
mov al,lf
call putchar
ret
;****
crmsg: ;print message addressed by dx til zero
;with leading crlf
push dx
call crlf
pop dx ;drop thru to outmsg0
;****
outmsg:
mov cl,prstr
jmp bdos ;dx has string addr
;****
bdma: ;dx has address
mov cl,setdmaf
jmp bdos
;****
dread: ;disk read function
mov cl,dreadf
jmp bdos
;****
open: ;file open function
mov cl,openf
jmp bdos
;****
bios:
mov fnum,al ;bios function number
mov bcx,cx
mov bdx,dx
mov dx,offset(bds)
mov cl,biosf
jmp bdos
; ****************************
; *
; * bios utilities
; *
; ****************************
seldsk equ 9 ;wboot+24 for disk select
settrk equ 10 ;wboot+27 for set track function
setsec equ 11 ;wboot+30 for set sector function
setdma equ 12 ;wboot+33 for set dma address
readf equ 13 ;wboot+36 for read function
writf equ 14 ;wboot+39 for write function
sel:
;select disk given by register a
mov cl,al
mov al,seldsk
jmp bios
;****
trk: ;set up track
mov al,settrk ;offset for settrk entry
jmp bios ;gone to settrk
;****
sec: ;set up sector number
mov al,setsec
jmp bios
;****
dma: ;set dma address to value of cx
mov al,setdma
jmp bios
;****
read: ;perform read operation
mov al,readf
jmp bios
;****
write: ;perform write operaton
mov al,writf
jmp bios
; **************************
; *
; * data areas
; *
; **************************
dseg
org 0100h ; skip past page zero
;messages
signon db 'LDCOPY VERS '
db vers/10+'0','.',vers mod 10+'0','$'
askget db 'Source Drive Name $'
getmsg db 'Source On '
gdisk rs 1 ;filled in at get function
db ', Then Type Return$'
askput db 'Destination Drive Name (Or Return To Reboot) $'
putmsg db 'Destination On '
pdisk rs 1 ;filled in at put function
db ', Then Type Return$'
errmsg db 'Permanent Error, Type Return To Ignore$'
done db 'Function Complete$'
qdisk db 'Invalid Drive Name$'
nofile db 'No Source File On Disk$'
badfile db 'Source File Read Error$'
;translate table
tran dw 1,3,5,7,9,11,13,15,17,19,21,23,25
dw 2,4,6,8,10,12,14,16,18,20,22,24,26
;leave room for double density
dw 0,0,0,0,0,0,0,0
dw 0,0,0,0,0,0,0,0
dw 0,0,0,0,0,0,0,0
dw 0,0,0,0,0,0,0,0 ; (64 extra bytes reserved)
; *************************
; *
; * variables
; *
; ************************
bds: ;bios data structure
fnum rb 1 ;storage for bios parameters
bcx rw 1 ;the bdos bios func puts these
bdx rw 1 ;in registers before jumping to bios
nlt dw sdlt ;number of loader tracks
spt dw sdspt ;sectors per track
ndisks db sdndisks ;number of disks
secsiz dw sdsecsiz ;sector size
maxsecs dw sdlt * sdspt ;maximum sectors to read from file
sdisk rb 1 ;selected disk for current operation
track rw 1 ;current track
rewr rb 1 ;read if 0,write if 1
sector rw 1 ;current sector
dmaddr dw 0 ;current dma address
retry rb 1 ;number of tries on this sector
conbuf db 30
rb 32 ;console buffer
;make stack on even address
loadp rs sdsecsiz*sdspt*sdlt
db 0 ; force out last data segement byte
end


View File

@@ -0,0 +1,240 @@
title 'CP/M-86 Loader'
; The CPMLDR consists of this module along with the
; LDRBDOS and LBIOS.
; CPMLDR resides on the first two tracks of a
; CP/M-86 system diskette and is brought into memory
; by the ROM bootstrap loader to load initiate CP/M-86
; It opens the file 'CPM.SYS' using the LDRBDOS and LBIOS
; and then reads it into memory. Finally, a jump to the BIOS
; initialization entry point starts CP/M-86
; The first 128 byte record of the CPM.SYS file is a header
; with the following format:
; ty rb 1 ;seg type (not used here)
; len rw 1 ;length (not used here)
; abs dw ldrstart ;absolute segment address for LOADER
; min rw 1 ;minimum mem (not used here)
; max rw 1 ;max mem needed (not used here)
; (This header record is constructed automatically by the
; GENCMD utility).
; CPMLDR may be read into any segment that does not
; overlap the desired system load segment as it makes
; all memory references using copies of the CS: register
; it is entered with.
false equ 0
true equ not false
cr equ 0dh
lf equ 0ah
lbios_offset equ 1200h ; offset of LBIOS
biosoff equ 2500h ; offset of BIOS from start of CPM.SYS
; this is the entry point into CPM.SYS
bootdrv equ 0 ; boot drive always zero
; 128 bytes of CPM.SYS
lbdosoff equ 406H ;location of LBDOS in LOADER
bdos_int equ 224 ;lbdos interrupt number
; dummy section for interrupt vectors
dseg 0
org 0
abs_zero rw 2*bdosint
bdos_offset rw 1
bdos_segment rw 1
; bdos function numbers
coutf equ 2
pstrf equ 9
seldsk equ 14
openf equ 15
readsf equ 20
dmaf equ 26
dmabf equ 51
;*******************************
;*
;* CPMLDR starts here
;*
;*******************************
cseg
org 0 ; JMPF to here from boot ROM
jmp LBIOS ; allow loader BIOS to
; initialize
start: ; loader BIOS jumps here
xor ax,ax ! mov ds,ax ; temp DS at absolute zero
mov bdos_offset,lbdosoff ; to patch in interrupt table
mov bdos_segment,cs ; offset and segment
mov ax,cs ! mov ss,ax ; make ss, ds, es = cs
mov ds,ax ! mov es,ax
mov sp,offset(stack) ; set up local stack
call initlbdos ;warm up lbdos and lbios
call openfnc ;open CPM.SYS
cmp al,255 ! jne perr ; insure good file
mov dx,offset nofile ! call msg ; no CPM.SYS file
jmp stop ; then halt the machine
perr:
mov dx,cs ! call setdmab
mov dx,offset page1 ! call setdma ;read first page of CPM.SYS
call read
mov ax,word ptr page1+3 ;get abs field from header
mov ldseg,ax ; save it and
mov dx,ax ! call setdmab ; set DMA segment for disk IO
mov dx,offset segment ! call msg ; put memory map on console
mov ax,ldseg ! call phex ; print base system segment
;
mov dx,0 ;offset of CPM in segment
readit:
call setdma ; set DMA offset for
push dx ! call read ; next sector read
cmp al,01H ! je done ; check for EOF
cmp al,0 ! je prerr ; check for good write
mov dx,offset rerr ! call msg ; print READ ERROR message
jmp stop ; hard stop on any error
prerr: pop dx ! add dx,80h ; address for next record
jmp readit
done:
mov dx,offset lenmsg ! call msg ; print length message
pop ax ! dec ax ! call phex ; print last address
call pcrlf ; and a crlf
jmpf bios ; leap to BIOS initialization
;*****************************
;*
;* subroutines
;*
;*****************************
;******
phex: ;print 4 hex characters from ax
mov cx,0404h ; 4 in both CH and CL
lhex:
rol ax,cl ; rotate left 4
push cx ! push ax ; save crucial registers
call pnib ; print hex nibble
pop ax ! pop cx ; restore registers
dec ch ! jnz lhex ; and loop four times
ret
pnib: ;print low nibble in AL as hex char
and al,0fh ! cmp al,9
ja p10 ;above 9 ?
add al,'0' ;digit
jmp prn
p10: add al,'A'-10 ;char a-e
prn: mov dl,al
;******
putchar:
mov cl,coutf
jmp sys_vec
;******
initlbdos:
mov cl,seldsk ! mov dx,bootdrv ; select boot disk
jmp sys_vec
;******
openfnc:
mov cl,openf ! mov dx,offset fcb ; fcb already initialized
mov cl,openf
jmp sys_vec
;********
;
setdma: ;set new dma addr in dx
mov cl,dmaf
jmp sys_vec
;********
;
setdmab: ; set new dma segment base from DX
mov cl,dmabf
jmp sys_vec
;******
;
pcrlf: mov dx,offset crlf ;print carriage return, line feed
;******
;
msg: ;print msg starting at dx until $
mov cl,pstrf ;print string function
jmp sys_vec
;*****
;
read:
mov dx,offset fcb ! mov cl,readsf
; jmp sys_vec
;******
;
sys_vec:
int bdos_int
ret
;******
;
stop: hlt ; hard stop 8086 for error
jmp stop
;********************************
;*
;* DATA AREA
;*
;********************************
nofile db cr,lf,'The File CPM.SYS Not Found On This Disk$'
rerr db cr,lf,'Error In Reading CPM.SYS$'
segment db cr,lf,'Segment Address = $'
lenmsg db cr,lf,' Last Offset = $'
crlf db cr,lf,'$'
; vector for jmpf indirect to start CP/M
bios dd abs_zero ; (dummy value)
org offset bios ; overlay preceding with DW's
biosstart dw biosoff ; first word is BIOS offset
ldseg rw 1 ; second is segment to put CPM.SYS
fcb db 0,'CPM ','SYS',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
org (offset $+1) and 0FFFEh ; even address for stack
rw 32
stack equ offset $
dseg
org stack
page1 rb 128
; dummy section for BIOS init label
org lbios_offset
lbios:
end


View File

@@ -0,0 +1,21 @@
:02000086000078
:0400000300000000F9
:1B000081E9FD1133C08ED8C706800306048C0E82038CC88ED08ED88EC0BCB8C1
:1B001B8101E89200E897003CFF7509BAE100E8A400E9AE008CCAE89400BAB894
:1B00368101E88900E89800A1BB012EA355018BD0E87F00BA2601E881002EA1DD
:1B0051815501E83600BA0000E8670052E875003C0174153C007409BA0B01E8BA
:1B006C816200E96C005A81C28000E9E0FFBA3B01E851005848E80800E846006F
:1B0087812EFF2E5301B90404D3C05150E807005859FECD75F3C3240F3C0977B4
:1B00A281050430E9020004378AD0B102E92900B10EBA0000E92100B10FBA57F0
:1B00BD8101B10FE91700B11AE91200B133E90D00BA5001B109E90500BA570181
:1B00D881B114CDE0C3F4E9FCFF0D0A5468652046696C652043504D2E5359537A
:1B00F381204E6F7420466F756E64204F6E2054686973204469736B240D0A4544
:1B010E8172726F7220496E2052656164696E672043504D2E535953240D0A5324
:1B01298165676D656E742041646472657373203D20240D0A202020204C61737C
:1301448174204F6666736574203D20240D0A240000000050
:02015381002504
:1B0157810043504D20202020205359530000000000000000000000000000008D
:0601728100000000000006
:00000001FF
0
:1B00

View File

@@ -0,0 +1,288 @@
;
;****************************************************
;* *
;* Sample Random Access Program for CP/M-86 *
;* *
;****************************************************
;
; BDOS Functions
;
coninp equ 1 ;console input function
conout equ 2 ;console output function
pstring equ 9 ;print string until '$'
rstring equ 10 ;read console buffer
version equ 12 ;return version number
openf equ 15 ;file open function
closef equ 16 ;close function
makef equ 22 ;make file function
readr equ 33 ;read random
writer equ 34 ;write random
;
; Equates for non graphic characters
cr equ 0dh ;carriage return
lf equ 0ah ;line feed
;
;
; load SP, ready file for random access
;
cseg
pushf ;push flags in CCP stack
pop ax ;save flags in AX
cli ;disable interrupts
mov bx,ds ;set SS register to base of DATA group
mov ss,bx ;set SS, SP with interrupts disabled
mov sp,offset stack ; for 80888
push ax ;restore the flags
popf
;
; CP/M-86 initial release returns the file
; system version number of 2.2: check is
; shown below for illustration purposes.
;
mov cl,version
call bdos
cmp al,20h ;version 2.0 or later?
jnb versok
; bad version, message and go back
mov dx,offset badver
call print
jmp abort
;
versok:
; correct version for random access
mov cl,openf ;open default fct
mov dx,offset fcb
call bdos
inc al ;err 255 becomes zero
jnz ready
;
; cannot open file, so create it
mov cl,makef
mov dx,offset fcb
call bdos
inc al ;err 255 becomes zero
jnz ready
;
; cannot create file, directory full
mov dx,offset nospace
call print
jmp abort ;back to ccp
;
; loop back to "ready" after each command
;
ready:
; file is ready for processing
;
call readcom ;read next command
mov ranrec,dx ;store input record#
mov ranovf,0h ;clear high byte if set
cmp al,'Q' ;quit?
jnz notq
;
; quit processing, close file
mov cl,closef
mov dx,offset fcb
call bdos
inc al ;err 255 becomes 0
jz error ;error message, retry
jmps abort ;back to ccp
;
;
; end of quit command, process write
;
;
notq:
; not the quit command, random write?
cmp al,'W'
jnz notw
;
; this is a random write, fill buffer until cr
mov dx,offset datmsg
call print ;data prompt
mov cx,127 ;up to 127 characters
mov bx,offset buff ;destination
rloop: ;read next character to buff
push cx ;save loop conntrol
push bx ;next destination
call getchr ;character to AL
pop bx ;restore destination
pop cx ;restore counter
cmp al,cr ;end of line?
jz erloop
; not end, store character
mov byte ptr [bx],al
inc bx ;next to fill
loop rloop ;decrement cx ..loop if not 0
erloop:
; end of read loop, store 00
mov byte ptr [bx],0h
;
; write the record to selected record number
mov cl,writer
mov dx,offset fcb
call bdos
or al,al ;error code zero?
jz ready ;for another record
jmps error ;message if not
;
;
;
; end of write command, process read
;
;
notw:
; not a write command, read record?
cmp al,'R'
jz ranread
jmps error ;skip if not
;
; read random record
ranread:
mov cl,readr
mov dx,offset fcb
call bdos
or al,al ;return code 00?
jz readok
jmps error
;
; read was successful, write to console
readok:
call crlf ;new line
mov cx,128 ;max 128 characters
mov si,offset buff ;next to get
wloop:
lods al ;next character
and al,07fh ;mask parity
jnz wloop1
jmp ready ;for another command if 00
wloop1:
push cx ;save counter
push si ;save next to get
cmp al,' ' ;graphic?
jb skipw ;skip output if not graphic
call putchr ;output character
skipw:
pop si
pop cx
loop wloop ;decrement CX and check for 00
jmp ready
;
;
; end of read command, all errors end-up here
;
;
error:
mov dx,offset errmsg
call print
jmp ready
;
; BDOS entry subroutine
bdos:
int 224 ;entry to BDOS if by INT 224
ret
;
abort: ;return to CCP
Mov Dl, 0
mov cl,0
call bdos ;use function 0 to end execution
;
; utility subroutines for console i/o
;
getchr:
;read next console character to a
mov cl,coninp
call bdos
ret
;
putchr:
;write character from a to console
mov cl,conout
mov dl,al ;character to send
call bdos ;send character
ret
;
crlf:
;send carriage return line feed
mov al,cr ;carriage return
call putchr
mov al,lf ;line feed
call putchr
ret
;
print:
;print the buffer addressed by dx until $
push dx
call crlf
pop dx ;new line
mov cl,pstring
call bdos ;print the string
ret
;
readcom:
;read the next command line to the conbuf
mov dx,offset prompt
call print ;command?
mov cl,rstring
mov dx,offset conbuf
call bdos ;read command line
; command line is present, scan it
mov ax,0 ;start with 0000
mov bx,offset conlin
readc: mov dl,[bx] ;next command character
inc bx ;to next command position
mov dh,0 ;zero high byte for add
or dl,dl ;check for end of command
jnz getnum
ret
; not zero, numeric?
getnum:
sub dl,'0'
cmp dl,10 ;carry if numeric
jnb endrd
mov cl,10
mul cl ;multipy accumulator by 10
add ax,dx ;+digit
jmps readc ;for another char
endrd:
; end of read, restore value in a and return value in bx
mov dx,ax ;return value in DX
mov al,-1[bx]
cmp al,'a' ;check for lower case
jnb transl
ret
transl: and al,5fH ;translate to upper case
ret
;
;
; Template for Page 0 of Data Group
; Contains default FCB and DMA buffer
;
dseg
org 05ch
fcb rb 33 ;default file control block
ranrec rw 1 ;random record position
ranovf rb 1 ;high order (overflow) byte
buff rb 128 ;default DMA buffer
;
; string data area for console messages
badver db 'sorry, you need cp/m version 2$'
nospace db 'no directory space$'
datmsg db 'type data: $'
errmsg db 'error, try again.$'
prompt db 'next command? $'
;
;
; fixed and variable data area
;
conbuf db conlen ;length of console buffer
consiz rs 1 ;resulting size after read
conlin rs 32 ;length 32 buffer
conlen equ offset $ - offset consiz
;
rs 31 ;16 level stack
stack rb 1
db 0 ;end byte for GENCMD
end
x,ax ;return value in DX
mov al,-1[bx]
cmp

View File

@@ -0,0 +1 @@
This folder contains a couple of original DRI CP/M-86 1.1 sources in Assembly language, including the ROM bootstrap and BIOS for the iSBC86/12 with Intel SBC 204 floppy controller.

View File

@@ -0,0 +1,443 @@
title 'SBC86/12 w/ SBC204 Bootstrap EPROM'
;
; ROM bootstrap for CP/M-86 on an iSBC86/12
; with the
; Intel SBC 204 Floppy Disk Controller
;
; Copyright (C) 1980,1981
; Digital Research, Inc.
; Box 579, Pacific Grove
; California, 93950
;
;********************************************
;* This is the BOOT ROM which is initiated *
;* by a system reset. First, the ROM moves *
;* a copy of its data area to RAM at loca- *
;* tion 00000H, then initializes the segment*
;* registers and the stack pointer. The *
;* various peripheral interface chips on the*
;* SBC 86/12 are initialized. The 8251 *
;* serial interface is configured for a 9600*
;* baud asynchronous terminal, and the in- *
;* terrupt controller is setup for inter- *
;* rupts 10H-17H (vectors at 00040H-0005FH) *
;* and edge-triggered auto-EOI (end of in- *
;* terrupt) mode with all interrupt levels *
;* masked-off. Next, the SBC 204 Diskette *
;* controller is initialized, and track 1 *
;* sector 1 is read to determine the target *
;* paragraph address for LOADER. Finally, *
;* the LOADER on track 0 sectors 2-26 and *
;* track 1 sectors 1-26 is read into the *
;* target address. Control then transfers *
;* to LOADER. This program resides in two *
;* 2716 EPROM's (2K each) at location *
;* 0FF000H on the SBC 86/12 CPU board. ROM *
;* 0 contains the even memory locations, and*
;* ROM 1 contains the odd addresses. BOOT *
;* ROM uses RAM between 00000H and 000FFH *
;* (absolute) for a scratch area, along with*
;* the sector 1 buffer. *
;********************************************
true equ 0ffh
false equ not true
;
debug equ true
;debug = true indicates bootstrap is in same roms
;with SBC 957 "Execution Vehicle" monitor
;at FE00:0 instead of FF00:0
;
cr equ 13
lf equ 10
;
; disk ports and commands
;
base204 equ 0a0h
fdccom equ base204+0
fdcstat equ base204+0
fdcparm equ base204+1
fdcrslt equ base204+1
fdcrst equ base204+2
dmacadr equ base204+4
dmaccont equ base204+5
dmacscan equ base204+6
dmacsadr equ base204+7
dmacmode equ base204+8
dmacstat equ base204+8
fdcsel equ base204+9
fdcsegment equ base204+10
reset204 equ base204+15
;
;actual console baud rate
baud_rate equ 9600
;value for 8253 baud counter
baud equ 768/(baud_rate/100)
;
csts equ 0DAh ;i8251 status port
cdata equ 0D8h ; " data port
;
tch0 equ 0D0h ;8253 PIC channel 0 port
tch1 equ tch0+2 ;ch 1 port
tch2 equ tch0+4 ;ch 2 port
tcmd equ tch0+6 ;8253 command port
;
icp1 equ 0C0h ;8259a port 0
icp2 equ 0C2h ;8259a port 1
;
;
IF NOT DEBUG
ROMSEG EQU 0FF00H ;normal
ENDIF
;
IF DEBUG ;share prom with SBC 957
ROMSEG EQU 0FE00H
ENDIF
;
;
; This long jump prom'd in by hand
; cseg 0ffffh ;reset goes to here (ffff0h)
; JMPF BOTTOM ;boot is at bottom of PROM
; EA 00 00 00 FF ;cs = bottom of prom (ff000h)
; ip = 0
; EVEN PROM ODD PROM
; 7F8 - EA 7F8 - 00
; 7F9 - 00 7F9 - 00
; 7FA - FF ;this is not done if special = true
;
cseg romseg
;
;First, move our data area into RAM at 0000:0200
;
mov ax,cs
mov ds,ax ;point DS to CS for source
mov SI,drombegin ;start of data
mov DI,offset ram_start ;offset of destination
mov ax,0
mov es,ax ;destination segment is 0000
mov CX,data_length ;how much to move in bytes
rep movs al,al ;move out of eprom a byte at a time
;
mov ax,0
mov ds,ax ;data segment now in RAM
mov ss,ax
mov sp,stack_offset ;Initialize stack segment/pointer
cld ;clear the direction flag
;
IF NOT DEBUG
;
;Now, initialize the console USART and baud rate
;
mov al,0Eh
out csts,al ;give 8251 dummy mode
mov al,40h
out csts,al ;reset 8251 to accept mode
mov al,4Eh
out csts,al ;normal 8 bit asynch mode, * 16
mov al,37h
out csts,al ;enable Tx & Rx
mov al,0B6h
out tcmd,al ;8253 ch.2 square wave mode
mov ax,baud
out tch2,al ;low of the baud rate
mov al,ah
out tch2,al ;high of the baud rate
;
ENDIF
;
;Setup the 8259 Programmable Interrupt Controller
;
mov al,13h
out icp1,al ;8259a ICW 1 8086 mode
mov al,10h
out icp2,al ;8259a ICW 2 vector @ 40-5F
mov al,1Fh
out icp2,al ;8259a ICW 4 auto EOI master
mov al,0FFh
out icp2,al ;8259a OCW 1 mask all levels off
;
;Reset and initialize the iSBC 204 Diskette Interface
;
restart: ;also come back here on fatal errors
out reset204,AL ;reset iSBC 204 logic and
mov AL,1
out fdcrst,AL ;give 8271 FDC
mov al,0
out fdcrst,AL ; a reset command
mov BX,offset specs1
CALL sendcom ;program
mov BX,offset specs2
CALL sendcom ; Shugart SA-800 drive
mov BX,offset specs3
call sendcom ; characteristics
homer: mov BX,offset home
CALL execute ;home drive 0
;
mov bx,sector1 ;offset for first sector DMA
mov ax,0
mov es,ax ;segment " " " "
call setup_dma
;
mov bx,offset read0
call execute ;get T0 S1
;
mov es,ABS
mov bx,0 ;get loader load address
call setup_dma ;setup DMA to read loader
;
mov bx,offset read1
call execute ;read track 0
mov bx,offset read2
call execute ;read track 1
;
mov leap_segment,ES
; setup far jump vector
mov leap_offset,0
;
; enter LOADER
jmpf dword ptr leap_offset
;
pmsg:
mov cl,[BX]
test cl,cl
jz return
call conout
inc BX
jmp pmsg
;
conout:
in al,csts
test al,1
jz conout
mov al,cl
out cdata,al
ret
;
conin:
in al,csts
test al,2
jz conin
in al,cdata
and al,7Fh
ret
;
;
;
execute: ;execute command string @ [BX]
;<BX> points to length,
;followed by Command byte
;followed by length-1 parameter bytes
;
mov lastcom,BX ;remember what it was
retry: ;retry if not ready drive
call sendcom ;execute the command
;now, let's see what type
;of status poll was necessary
;for that command type . . .
mov BX,lastcom ;point to command string
mov AL,1[BX] ;get command op code
and AL,3fh ;drop drive code bits
mov CX,0800h ;mask if it will be "int req"
cmp AL,2ch ;see if interrupt type
jb execpoll
mov CX,8080h ;else we use "not command busy"
and AL,0fh ;unless . . .
cmp AL,0ch ;there isn't
mov AL,0
ja return ;any result at all
;
execpoll: ;poll for bit in b, toggled with c
in AL,FDCSTAT
and AL,CH
xor AL,CL ! JZ execpoll
;
in AL,fdcrslt ;get result register
and AL,1eh ;look only at result type & code
jz return ;zero means it was a good operation
;
cmp al,10h
jne fatal ;if other than "Not Ready", stop
;
mov bx,offset rdstat
call sendcom ;perform read status command
rd_poll:
in al,fdc_stat
test al,80h ;wait for command not busy
jnz rd_poll
mov bx,last_com ;recover last attempted command
jmp retry ;and try it over again
;
fatal: ; fatal error
mov ah,0
mov bx,ax ;make 16 bits
mov bx,errtbl[BX]
; print appropriate error message
call pmsg
call conin ;wait for key strike
pop ax ;discard unused item
jmp restart ;then start all over
;
return:
RET ;return from EXECUTE
;
setupdma:
mov AL,04h
out dmacmode,AL ;enable dmac
mov al,0
out dmaccont,AL ;set first (dummy) byte to
mov AL,40h
out dmaccont,AL ;force read data mode
mov AX,ES
out fdcsegment,AL
mov AL,AH
out fdcsegment,AL
mov AX,BX
out dmacadr,AL
mov AL,AH
out dmacadr,AL
RET
;
;
;
sendcom: ;routine to send a command string to SBC204
in AL,fdcstat
and AL,80h
jnz sendcom ;insure command not busy
mov CL,[BX] ;get count
inc BX
mov al,[BX] ;point to and fetch command byte
out fdccom,AL ;send command
parmloop:
dec CL
jz return ;see if any (more) parameters
inc BX ;point to next parameter
parmpoll:
in AL,fdcstat
and AL,20h
jnz parmpoll ;loop until parm not full
mov AL,[BX]
out fdcparm,AL ;output next parameter
jmp parmloop ;go see about another
;
;
; Image of data to be moved to RAM
;
drombegin equ offset $
;
clastcom dw 0000h ;last command
;
creadstring db 3 ;length
db 52h ;read function code for drive 0
db 0 ;track #
db 1 ;sector #
;
creadtrk0 db 4
db 53h ;read multiple
db 0 ;track 0
db 2 ;sectors 2
db 25 ;through 26
;
creadtrk1 db 4
db 53h
db 1 ;track 1
db 1 ;sectors 1
db 26 ;through 26
;
chome0 db 2,69h,0
crdstat0 db 1,6ch
cspecs1 db 5,35h,0dh
db 08h,08h,0e9h
cspecs2 db 5,35h,10h
db 255,255,255
cspecs3 db 5,35h,18h
db 255,255,255
;
cerrtbl dw offset er0
dw offset er1
dw offset er2
dw offset er3
dw offset er4
dw offset er5
dw offset er6
dw offset er7
dw offset er8
dw offset er9
dw offset erA
dw offset erB
dw offset erC
dw offset erD
dw offset erE
dw offset erF
;
Cer0 db cr,lf,'Null Error ??',0
Cer1 equ cer0
Cer2 equ cer0
Cer3 equ cer0
Cer4 db cr,lf,'Clock Error',0
Cer5 db cr,lf,'Late DMA',0
Cer6 db cr,lf,'ID CRC Error',0
Cer7 db cr,lf,'Data CRC Error',0
Cer8 db cr,lf,'Drive Not Ready',0
Cer9 db cr,lf,'Write Protect',0
CerA db cr,lf,'Trk 00 Not Found',0
CerB db cr,lf,'Write Fault',0
CerC db cr,lf,'Sector Not Found',0
CerD equ cer0
CerE equ cer0
CerF equ cer0
;
dromend equ offset $
;
data_length equ dromend-drombegin
;
; reserve space in RAM for data area
; (no hex records generated here)
;
dseg 0
org 0200h
;
ram_start equ $
lastcom rw 1 ;last command
read0 rb 4 ;read track 0 sector 1
read1 rb 5 ;read T0 S2-26
read2 rb 5 ;read T1 S1-26
home rb 3 ;home drive 0
rdstat rb 2 ;read status
specs1 rb 6
specs2 rb 6
specs3 rb 6
errtbl rw 16
er0 rb length cer0 ;16
er1 equ er0
er2 equ er0
er3 equ er0
er4 rb length cer4 ;14
er5 rb length cer5 ;11
er6 rb length cer6 ;15
er7 rb length cer7 ;17
er8 rb length cer8 ;18
er9 rb length cer9 ;16
erA rb length cerA ;19
erB rb length cerB ;14
erC rb length cerC ;19
erD equ er0
erE equ er0
erF equ er0
;
leap_offset rw 1
leap_segment rw 1
;
;
rw 32 ;local stack
stack_offset equ offset $;stack from here down
;
; T0 S1 read in here
sector1 equ offset $
;
Ty rb 1
Len rw 1
Abs rw 1 ;ABS is all we care about
Min rw 1
Max rw 1
end


View File

@@ -0,0 +1,5 @@
disks 2
diskdef 0,1,26,6,1024,243,64,64,2
diskdef 1,0
endef


View File

@@ -0,0 +1,65 @@
; DISKS 2
dpbase equ $ ;Base of Disk Parameter Blocks
dpe0 dw xlt0,0000h ;Translate Table
dw 0000h,0000h ;Scratch Area
dw dirbuf,dpb0 ;Dir Buff, Parm Block
dw csv0,alv0 ;Check, Alloc Vectors
dpe1 dw xlt1,0000h ;Translate Table
dw 0000h,0000h ;Scratch Area
dw dirbuf,dpb1 ;Dir Buff, Parm Block
dw csv1,alv1 ;Check, Alloc Vectors
; DISKDEF 0,1,26,6,1024,243,64,64,2
;
; 1944: 128 Byte Record Capacity
; 243: Kilobyte Drive Capacity
; 64: 32 Byte Directory Entries
; 64: Checked Directory Entries
; 128: Records / Extent
; 8: Records / Block
; 26: Sectors / Track
; 2: Reserved Tracks
; 6: Sector Skew Factor
;
dpb0 equ offset $ ;Disk Parameter Block
dw 26 ;Sectors Per Track
db 3 ;Block Shift
db 7 ;Block Mask
db 0 ;Extnt Mask
dw 242 ;Disk Size - 1
dw 63 ;Directory Max
db 192 ;Alloc0
db 0 ;Alloc1
dw 16 ;Check Size
dw 2 ;Offset
xlt0 equ offset $ ;Translate Table
db 1,7,13,19
db 25,5,11,17
db 23,3,9,15
db 21,2,8,14
db 20,26,6,12
db 18,24,4,10
db 16,22
als0 equ 31 ;Allocation Vector Size
css0 equ 16 ;Check Vector Size
; DISKDEF 1,0
;
; Disk 1 is the same as Disk 0
;
dpb1 equ dpb0 ;Equivalent Parameters
als1 equ als0 ;Same Allocation Vector Size
css1 equ css0 ;Same Checksum Vector Size
xlt1 equ xlt0 ;Same Translate Table
; ENDEF
;
; Uninitialized Scratch Memory Follows:
;
begdat equ offset $ ;Start of Scratch Area
dirbuf rs 128 ;Directory Buffer
alv0 rs als0 ;Alloc Vector
csv0 rs css0 ;Check Vector
alv1 rs als1 ;Alloc Vector
csv1 rs css1 ;Check Vector
enddat equ offset $ ;End of Scratch Area
datsiz equ offset $-begdat ;Size of Scratch Area
db 0 ;Marks End of Module


View File

@@ -0,0 +1,829 @@
title 'Track Buffered BIOS - SBC204'
; 5 Jan 82 -jrp
; Copyright (C) 1980,1981
; Digital Research, Inc.
; Box 579, Pacific Grove
; California, 93950
;
; (Permission is hereby granted to use
; or abstract the following program in
; the implementation of CP/M, MP/M or
; CP/NET for the 8086 or 8088 Micro-
; processor)
true equ -1
false equ not true
; Track buffering equates...
host_sectsiz equ 128
host_spt equ 26
host_fsn equ 1
bdos_int equ 224
bios_code equ 2500h
ccp_offset equ 0000h
bdos_ofst equ 0B06h ;BDOS entry point
csts equ 0DAh ;i8251 status port
cdata equ 0D8h ; " data port
lsts equ 41h ;2651 No. 0 on BLC8538 status port
ldata equ 40h ; " " " " " data port
blc_reset equ 60h ;reset selected USARTS on BLC8538
;*********************************************
;* *
;* Intel iSBC 204 Disk Controller Ports *
;* *
;*********************************************
base204 equ 0a0h ;SBC204 assigned address
fdc_com equ base204+0 ;8271 FDC out command
fdc_stat equ base204+0 ;8271 in status
fdc_parm equ base204+1 ;8271 out parameter
fdc_rslt equ base204+1 ;8271 in result
fdc_rst equ base204+2 ;8271 out reset
dmac_adr equ base204+4 ;8257 DMA base address out
dmac_cont equ base204+5 ;8257 out control
dmac_scan equ base204+6 ;8257 out scan control
dmac_sadr equ base204+7 ;8257 out scan address
dmac_mode equ base204+8 ;8257 out mode
dmac_stat equ base204+8 ;8257 in status
fdc_sel equ base204+9 ;FDC select port (not used)
fdc_segment equ base204+10 ;segment address register
reset_204 equ base204+15 ;reset entire interface
max_retries equ 10 ;max retries on disk i/o
;before perm error
cr equ 0dh ;carriage return
lf equ 0ah ;line feed
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)
;print signon message and initialize hardware
INIT:
mov ax,cs ;we entered with a JMPF so use
mov ds,ax ; CS: as the initial value of DS:,
mov es,ax ; and ES:
mov ss,ax ; use local stack during initialization
mov sp,offset stkbase
cld ;set forward direction
push ds ;save the DS register
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
pop ds ;restore the DS register
; Initialize the BLC 8538 printer port
mov al,0FFh
out blc_reset,al ;reset all usarts on 8538
mov al,4Eh
out ldata+2,al ;set usart 0 in async 8 bit mode
mov al,3Eh
out ldata+2,al ;set usart 0 to 9600 baud
mov al,37h
out ldata+3,al ;enable Tx/Rx, and set up RTS,DTR
mov bx,offset signon
call pmsg ;print signon message
call clear_flags ; initialize track buffering
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
int_trap:
cli ;block interrupts
mov ax,cs
mov ds,ax ;get our data segment
mov bx,offset int_trp
call pmsg
hlt ;hardstop
;*********************************************
;* *
;* CP/M Character I/O Interface Routines *
;* Console is Usart (i8251a) on iSBC 86/12 *
;* at ports D8/DA *
;* *
;*********************************************
CONST: ;console status
in al,csts
and al,2
jz const_ret
or al,255 ;return non-zero if RDA
const_ret:
ret ;Receiver Data Available
CONIN: ;console input
call const
jz CONIN ;wait for RDA
in al,cdata
and al,7fh ;read data and remove parity bit
ret
CONOUT: ;console output
in al,csts
and al,1 ;get console status
jz CONOUT ;wait for TBE
mov al,cl
out cdata,al ;Transmitter Buffer Empty
ret ;then return data
LISTOUT: ;list device output
call LISTST
jz LISTOUT ;wait for printer not busy
mov al,cl
out ldata,al ;send char to TI 810
ret
LISTST: ;poll list status
in al,lsts
and al,81h ;look at both TxRDY and DTR
cmp al,81h
jnz zero_ret ;either false, printer is busy
or al,255 ;both true, LPT is ready
ret
PUNCH: ;not implemented in this configuration
READER:
mov al,1ah
ret ;return EOF for now
GETIOBF:
mov al,0 ;TTY: for consistency
ret ;IOBYTE not implemented
SETIOBF:
ret ;iobyte not implemented
zero_ret:
and al,0
ret ;return zero in AL and flags
; Routine to get and echo a console character
; and shift it to upper case
uconecho:
call CONIN ;get a console character
push ax
mov cl,al ;save and
call CONOUT
pop ax ;echo to console
cmp al,'a'
jb uret ;less than 'a' is ok
cmp al,'z'
ja uret ;greater than 'z' is ok
sub al,'a'-'A' ;else shift to caps
uret:
ret
; utility subroutine to print messages
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
mov cpm_disk,cl ; save the selected drive
mov bx,0000h
cmp cl,2 ;this BIOS only supports 2 disks
jnb return ;return w/ 0000 in BX if bad drive
;now, we need disk parameter address
mov ch,0
mov bx,cx ;BX = word(CL)
mov cl,4
shl bx,cl ;multiply drive code * 16
add bx,offset dp_base ;create offset from Disk Parameter Base
return:
ret
home:
test wr_flag,1 ! jnz home1 ; if the buffer is clean,
mov cur_disk,-1 ; insure we read the directory by invalidating
; the track buffer
home1:
mov cx,0 ; home is a settrk zero
settrk:
mov cpm_track,cx ; save track number for next operation
ret
setsec:
mov cpm_sec,cx ; save sector number for next operation
ret
setdma:
mov dma_offset,cx ; save DMA offset address
ret
setdmab:
mov dma_segment,cx ; save DMA segment address
ret
sectran:
mov bx,cx ; Put logical sector into dest. reg.
test dx,dx ; see if table address is zero
jz sectran_exit ; yeah, logical = physical
add bx,dx ; else, we need to fetch the
mov bl,[BX] ; actual sector number from the table
mov bh,0 ; zero high byte for good luck
sectran_exit:
ret
GETSEGT: ;return address of physical memory table
mov bx,offset seg_table
ret
read:
call track_setup
push es ; save the extra segment register
mov si,offset track_buffer ; source segment is systems DS:
add si,ax ; gives the offset into the buffer
les di,dma_longword ; point ES:DI at the users sector
rep movsw ; doit
pop es ; restore the extra segment
sub ax,ax ; make a zero return code
ret
write:
push cx ; save the write mode from the BDOS
call track_setup
push ax ; save buffer offset
push ds ; save the data segment
push es ; save the extra segment
mov bx,ds ! mov es,bx ; destination is our data segment
mov di,offset track_buffer ; destination is in track buffer
add di,ax ; plus appropriate offset
lds si,dma_longword ; source is users DMA address
rep movsw ; move that sector
pop es ; restore the extra segment
pop ds ; and the data segment registers
pop ax ; recover buffer offset
mov cx,host_sectsiz ; setup to divide by host sector size
sub dx,dx ; extend ax to 32 bits
div cx ; find out which host sector we changed
mov bx,ax ; put into index [BX]
mov sec_flags[BX],1 ; set the update flag for that sector
mov wr_flag,1 ; also set the dirty buffer flag
pop cx ; recover BDOS write code
cmp cl,1 ; is this a directory update ?
jne write_return ; no, we may leave dirty records
; in the buffer
call flush_buffer ; we have a directory write, need to
; flush the buffer to insure the
; disks integrity
write_return:
sub ax,ax ; never return BAD SECTOR code
ret
track_setup: ; common code for setting up reads and writes
mov al,cpm_disk ; see if selected disk is
cmp al,cur_disk ; the same as last time
jne wrong_track ; no, we have wrong track
mov ax,cpm_track ; see if desired track is same as
cmp ax,cur_track ; the track in the buffer
je correct_track ; same drive and track, we don't need to read
; Desired operation is on a different track then is in our buffer,
; So it will be nessecary to read in the desired track. First, we
; must check to see if any sectors of the current buffer are dirty.
wrong_track:
call flush_buffer ; write any old records, if nessecary
mov ax,cpm_track ; get desired track number
mov cur_track,ax ; make in new track
mov al,cpm_disk ; get desired disk number
mov cur_disk,al ; make it current drive
mov cur_dma_adr,offset track_buffer
; point dma offset at track buffer
mov cur_sec,host_fsn ; starting from first sector
call track_read ; load the track
correct_track:
mov ax,cpm_sec ; get the cp/m sector number
if (host_fsn ne 0)
sub ax,host_fsn ; correct if we start with sector one
endif
mov cl,7 ; log2(128)
shl ax,cl ; sector times 128 gives offset
mov cx,64 ! cld ; move 64 words forward
ret
flush_buffer:
test wr_flag,1 ; see if we have anything to write
jz no_flush ; no, skip scanning for dirty sectors
mov bx,host_fsn ; start at first host sector
mov cx,host_spt ; for host_spt sectors...
next_sect:
test sec_flags-host_fsn[BX],1 ; see if this sector has been changed
jz not_updated ; no, leave it alone
mov sec_flags-host_fsn[BX],0 ; zero the flag for next time
push bx ; save the registers
push cx
mov cur_sec,bx ; save host sector number
mov ax,host_sectsiz
if (host_fsn ne 0)
sub bx,host_fsn
endif
mul bx ; make track buffer offset
add ax,offset track_buffer ; make direct pointer
mov cur_dma_adr,ax ; save for write routine
call sector_write
pop cx
pop bx
not_updated:
inc bx
loop next_sect
no_flush:
mov wr_flag,0 ; clear the dirty buffer flag
ret
clear_flags: ; Clear all variables associated with the track buffer,
; so next operation will have to read a track.
; This is involves clearing all write flags and setting
; the old drive code to the invalid -1.
push es ; save extra segment
mov cur_disk,-1 ; insure initial pre-read
sub ax,ax ; make a zero
mov wr_flag,al ; clear the dirty buffer flag
mov di,offset sec_flags ; point to the update flag list
mov bx,ds ! mov es,bx ; ES <- DS
mov cx,host_spt ! cld ; set length and direction
rep stosb ; zero the sector update flags
mov cur_dma_seg,ds ; get our segment address
pop es ; recover extra segment
ret
track_READ:
mov io_com,4 ; read track takes 4 byte command
mov io_com+1,13h ; special read track command
jmps r_w_common
sector_WRITE:
mov io_com,3 ; write sector takes 3 byte command
mov io_com+1,0ah ; basic write sector command
r_w_common:
cmp cur_disk,1 ! jne not_first_b ; see if drive B
test b_first_flag,-1 ! jz not_first_b ; and first reference to B
call restore ; then restore drive B
mov b_first_flag,0 ; and clear flag
not_first_b:
mov bx,offset io_com ;point to command string
mov ax,cur_sec ; put sector in
mov sect,al ; iopb as 8 bits
mov ax,cur_track ; same with
mov trk,al ; track . .
; fall into execute and return
execute: ;execute command string.
;[BX] points to length,
; followed by Command byte,
; followed by length-1 parameter bytes
mov last_com,BX ;save command address for retries
outer_retry:
;allow some retrying
mov rtry_cnt,max_retries
retry:
mov BX,last_com
call send_com ;transmit command to i8271
; check status poll
mov BX,last_com
mov al,1[bx] ;get command op code
mov cx,0800h ;mask if it will be "int req"
cmp al,2ch
jb exec_poll ;ok if it is an interrupt type
mov cx,8080h ;else we use "not command busy"
and al,0fh
cmp al,0ch ;unless there isn't
mov al,0
ja exec_exit ; any result
;poll for bits in CH,
exec_poll: ; toggled with bits in CL
in al,fdc_stat ;read status
and al,ch
xor al,cl ; isolate what we want to poll
jz exec_poll ;and loop until it is done
;Operation complete,
in al,fdc_rslt ; see if result code indicates error
and al,1eh
jz exec_exit ;no error, then exit
;some type of error occurred . . .
cmp al,10h
je dr_nrdy ;was it a not ready drive ?
;no,
dr_rdy: ; then we just retry read or write
push ax ; save error code
call restore ; after physically homing this disk
pop ax ; recover error code
dec rtry_cnt
jnz retry ; up to 10 times
; retries do not recover from the
; hard error
mov ah,0
mov bx,ax ;make error code 16 bits
mov bx,errtbl[BX]
call pmsg ;print appropriate message
in al,cdata ;flush usart receiver buffer
call uconecho ;read upper case console character
cmp al,'C'
je wboot_l ;cancel
cmp al,'R'
je outer_retry ;retry 10 more times
cmp al,'I'
je z_ret ;ignore error
or al,255 ;set code for permanent error
exec_exit:
ret
dr_nrdy: ;here to wait for drive ready
call test_ready
jnz retry ;if it's ready now we are done
call test_ready
jnz retry ;if not ready twice in row,
mov bx,offset nrdymsg
call pmsg ;"Drive Not Ready"
nrdy01:
call test_ready
jz nrdy01 ;now loop until drive ready
jmps retry ;then go retry without decrement
zret:
and al,0
ret ;return with no error code
wboot_l: ;can't make it w/ a short leap
jmp WBOOT
;*********************************************
;* *
;* The i8271 requires a read status command *
;* to reset a drive-not-ready after the *
;* drive becomes ready *
;* *
;*********************************************
test_ready:
mov dh, 40h ;proper mask if dr 1
test sel_mask,80h
jnz nrdy2
mov dh, 04h ;mask for dr 0 status bit
nrdy2:
mov bx,offset rds_com
call send_com
dr_poll:
in al,fdc_stat ;get status word
test al,80h
jnz dr_poll ;wait for not command busy
in al,fdc_rslt ;get "special result"
test al,dh ;look at bit for this drive
ret ;return status of ready
restore: ;move selected disk to home position (Track 0)
mov bx,offset hom_com
call execute
jz restore_exit ;home drive and return if OK
mov bx,offset bad_hom ;else print
call pmsg ;"Home Error"
jmps restore ;and retry
restore_exit:
ret
;*********************************************
;* *
;* Send_com sends a command and parameters *
;* to the i8271: BX addresses parameters. *
;* The DMA controller is also initialized *
;* if this is a read or write *
;* *
;*********************************************
send_com:
in al,fdc_stat
test al,80h ;insure command not busy
jnz send_com ;loop until ready
;see if we have to initialize for a DMA operation
mov al,1[bx] ;get command byte
cmp al,13h
jne write_maybe ;if not a read it could be write
mov cl,40h
jmps init_dma ;is a read command, go set DMA
write_maybe:
cmp al,0ah
jne dma_exit ;leave DMA alone if not read or write
mov cl,80h ;we have write, not read
init_dma:
;we have a read or write operation, setup DMA controller
; (CL contains proper direction bit)
mov al,04h
out dmac_mode,al ;enable dmac
mov al,00
out dmac_cont,al ;send first byte to control port
mov al,cl
out dmac_cont,al ;load direction register
mov ax,cur_dma_adr
out dmac_adr,al ;send low byte of DMA
mov al,ah
out dmac_adr,al ;send high byte
mov ax,cur_dma_seg
out fdc_segment,al ;send low byte of segment address
mov al,ah
out fdc_segment,al ;then high segment address
dma_exit:
mov cl,[BX] ;get count
inc BX
mov al, 80h
cmp cur_disk,0
jne sel1 ;drive 1 if not zero
mov al, 40h ;else drive is 0
sel1: mov sel_mask,al ; save select mask
or al,[BX] ;merge command and drive code
out fdc_com,al ;send command byte
parm_loop:
dec cl
jz parm_exit ;no (more) parameters, return
inc BX ;point to (next) parameter
parm_poll:
in al,fdc_stat
test al,20h ;test "parameter register full" bit
jnz parm_poll ;idle until parm reg not full
mov al,[BX]
out fdc_parm,al ;send next parameter
jmps parm_loop ;go see if there are more parameters
parm_exit:
ret
;*********************************************
;* *
;* Data Areas *
;* *
;*********************************************
data_offset equ offset $
dseg
org data_offset ;contiguous with code segment
signon db cr,lf,cr,lf
db ' CP/M-86, 1 Feb 82 ',cr,lf,0
bad_hom db cr,lf,'Home Error',cr,lf,0
int_trp db cr,lf,'Interrupt Trap Halt',cr,lf,0
errtbl dw er0,er1,er2,er3
dw er4,er5,er6,er7
dw er8,er9,erA,erB
dw erC,erD,erE,erF
er0 db cr,lf,'Error Code 0 :',0
er1 db cr,lf,'Error Code 1 :',0
er2 db cr,lf,'Error Code 2 :',0
er3 db cr,lf,'Error Code 3 :',0
er4 db cr,lf,'Clock Error :',0
er5 db cr,lf,'Late DMA :',0
er6 db cr,lf,'ID CRC Error :',0
er7 db cr,lf,'Data CRC Error :',0
er8 db cr,lf,'Drive Not Ready :',0
er9 db cr,lf,'Write Protect :',0
erA db cr,lf,'Track 00 Not Found :',0
erB db cr,lf,'Write Fault :',0
erC db cr,lf,'Sector Not Found :',0
erD db cr,lf,'Error Code D :',0
erE db cr,lf,'Error Code E :',0
erF db cr,lf,'Error Code F :',0
nrdymsg equ er8
b_first_flag db -1
rtry_cnt db 0 ;disk error retry counter
last_com dw 0 ;address of last command string
cur_dma_adr dw 0 ;dma offset stored here
cur_dma_seg dw 0 ;dma segment stored here
sel_mask db 40h ;select mask, 40h or 80h
; Various command strings for i8271
io_com db 3 ;length (changed to 4 to read a track)
rd_wr db 0 ;read/write function code
trk db 0 ;track #
sect db 0 ;sector #
sec_len db 26 ;transfer 26 sectors on a track read
hom_com db 2,29h,0 ;home drive command
rds_com db 1,2ch ;read status command
; Track buffering variables
cpm_disk rb 1
cpm_track rw 1
cpm_sec rw 1
cur_disk rb 1
cur_track rw 1
cur_sec rw 1
dma_offset rw 1
dma_segment rw 1
dma_longword equ dword ptr dma_offset
bdos_wr_code rb 1
wr_flag rb 1
; System Memory Segment Table
segtable db 2 ;2 segments
dw tpa_seg ;1st seg starts after BIOS
dw tpa_len ;and extends to 08000
dw 1000h ;second is 10000 -
dw 1000h ;1FFFF (64k)
; include singles.lib ;read in disk definitions
; DISKS 2
dpbase equ $ ;Base of Disk Parameter Blocks
dpe0 dw xlt0,0000h ;Translate Table
dw 0000h,0000h ;Scratch Area
dw dirbuf,dpb0 ;Dir Buff, Parm Block
dw csv0,alv0 ;Check, Alloc Vectors
dpe1 dw xlt1,0000h ;Translate Table
dw 0000h,0000h ;Scratch Area
dw dirbuf,dpb1 ;Dir Buff, Parm Block
dw csv1,alv1 ;Check, Alloc Vectors
; DISKDEF 0,1,26,6,1024,243,64,64,2
;
; 1944: 128 Byte Record Capacity
; 243: Kilobyte Drive Capacity
; 64: 32 Byte Directory Entries
; 64: Checked Directory Entries
; 128: Records / Extent
; 8: Records / Block
; 26: Sectors / Track
; 2: Reserved Tracks
; 6: Sector Skew Factor
;
dpb0 equ offset $ ;Disk Parameter Block
dw 26 ;Sectors Per Track
db 3 ;Block Shift
db 7 ;Block Mask
db 0 ;Extnt Mask
dw 242 ;Disk Size - 1
dw 63 ;Directory Max
db 192 ;Alloc0
db 0 ;Alloc1
dw 16 ;Check Size
dw 2 ;Offset
xlt0 equ offset $ ;Translate Table
db 1,7,13,19
db 25,5,11,17
db 23,3,9,15
db 21,2,8,14
db 20,26,6,12
db 18,24,4,10
db 16,22
als0 equ (243+7)/8 ;Allocation Vector Size
css0 equ 16 ;Check Vector Size
; DISKDEF 1,0
;
; Disk 1 is the same as Disk 0
;
dpb1 equ dpb0 ;Equivalent Parameters
als1 equ als0 ;Same Allocation Vector Size
css1 equ css0 ;Same Checksum Vector Size
xlt1 equ xlt0 ;Same Translate Table
; ENDEF
;
; Uninitialized Scratch Memory Follows:
;
dirbuf rs 128 ;Directory Buffer
alv0 rs als0 ;Alloc Vector
csv0 rs css0 ;Check Vector
alv1 rs als1 ;Alloc Vector
csv1 rs css1 ;Check Vector
sec_flags rb host_spt
track_buffer rb host_spt*host_sectsiz
loc_stk rw 32 ;local stack for initialization
stkbase equ offset $
lastoff equ offset $
tpa_seg equ ((lastoff+07FFh)/0400h)*40h ; round off to 1K boundary
tpa_len equ 0800h - tpa_seg
db 0 ;fill last address for GENCMD
;*********************************************
;* *
;* Dummy Data Section for Interrupt Vectors *
;* *
;*********************************************
dseg 0 ;absolute low memory
org 0 ;(interrupt vectors)
int0_offset rw 1
int0_segment rw 1
; pad to system call vector
org bdos_int * 4
bdos_offset rw 1
bdos_segment rw 1
END


View File

@@ -0,0 +1,237 @@
; * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
; * *
; * CP/M-86 Accelerator -- Track Buffering Routines *
; * *
; * This module, when installed in a CBIOS, causes *
; * CP/M-86 to perform disk input output on a *
; * track by track basis, rather than sector by *
; * sector. *
; * *
; * This speeds diskette access up, often by a *
; * factor of four or more times. *
; * *
; * The actual disk sectors must be a integral *
; * multiple of 128 bytes, but do not need to be *
; * a power of two multiple unlike the deblocking *
; * algorithms supplied with CP/M-86. *
; * *
; * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
; The following three equates must be set to correspond to the
; actual disk utilized.
host_sectsiz equ 1024 ; bytes per actual (physical) disk sector
host_spt equ 8 ; actual sectors per track
host_fsn equ 1 ; starting sector number (only 0 or 1 allowed)
init:
call clear_flags ; Initialize track buffering
.
.
.
jmp CCP_entry
seldsk:
mov cpm_disk,cl ; save the selected drive
test dl,1 ; check logged-in bit
jnz old_disk ; not first time selected if nz
; here if CP/M is about to login to the drive being
; selected.
old_disk:
mov bl,cpm_disk ! mov bh,0
mov dl,4 ! shl bx,dl ; times 16
add bx,offset dpbase ; gives offset from DPBASE
ret ; back to BDOS
setdma:
mov dma_offset,cx ; save DMA offset address
ret
setdma_seg:
mov dma_segment,cx ; save DMA segment address
ret
home:
test wr_flag,1 ! jnz home1 ; if the buffer is clean,
mov cur_disk,-1 ; insure we read the directory by invalidating
; the track buffer
home1:
mov cx,0 ; home is a settrk zero
settrk:
mov cpm_track,cx ; save track number for next operation
ret
setsec:
mov cpm_sec,cx ; save sector number for next operation
ret
sectran:
mov bx,cx ; Put logical sector into dest. reg.
test dx,dx ; see if table address is zero
jz sectran_exit ; yeah, logical = physical
add bx,dx ; else, we need to fetch the
mov bl,[BX] ; actual sector number from the table
mov bh,0 ; zero high byte for good luck
sectran_exit:
ret
read:
call setup
push es ; save the extra segment register
mov si,offset track_buffer ; source segment is systems DS:
add si,ax ; gives the offset into the buffer
les di,dma_longword ; point ES:DI at the users sector
rep movsw ; doit
pop es ; restore the extra segment
sub ax,ax ; make a zero return code
ret
write:
push cx ; save the write mode from the BDOS
call setup
push ax ; save buffer offset
push ds ; save the data segment
push es ; save the extra segment
mov bx,ds ! mov es,bx ; destination is our data segment
mov di,offset track_buffer ; destination is in track buffer
add di,ax ; plus appropriate offset
lds si,dma_longword ; source is users DMA address
rep movsw ; move that sector
pop es ; restore the extra segment
pop ds ; and the data segment registers
pop ax ; recover buffer offset
mov cx,host_sectsiz ; setup to divide by host sector size
sub dx,dx ; extend ax to 32 bits
div cx ; find out which host sector we changed
mov bx,ax ; put into index [BX]
mov sec_flags[BX],1 ; set the update flag for that sector
mov wr_flag,1 ; also set the dirty buffer flag
pop cx ; recover BDOS write code
cmp cl,1 ; is this a directory update ?
jne return ; no, we may leave dirty records
; in the buffer
call flush_buffer ; we have a directory write, need to
; flush the buffer to insure the
; disks integrity
return:
mov ax,0 ; never return BAD SECTOR code
ret
setup: ; common code for setting up reads and writes
mov al,cpm_disk ; see if selected disk is
cmp al,cur_disk ; the same as last time
jne wrong_track ; no, we have wrong track
mov ax,cpm_track ; see if desired track is same as
cmp ax,cur_track ; the track in the buffer
je correct_track ; same drive and track, we don't need to read
; Desired operation is on a different track then is in our buffer,
; So it will be nessecary to read in the desired track. First, we
; must check to see if any sectors of the current buffer are dirty.
wrong_track:
call flush_buffer ; write any old records, if nessecary
mov ax,cpm_track ; get desired track number
mov cur_track,ax ; make in new track
mov al,cpm_disk ; get desired disk number
mov cur_disk,al ; make it current drive
mov cur_dma,offset track_buffer ; point dma offset at track buffer
mov cur_sec,host_fsn ; starting from first sector
call track_read ; load the track
correct_track:
mov ax,cpm_sec ; get the cp/m sector number
if (host_fsn ne 0)
sub ax,host_fsn ; correct if we start with sector one
endif
mov dl,7 ; log2(128)
shl ax,dl ; sector times 128 gives offset
mov cx,64 ! cld ; move 64 words forward
ret
flush_buffer:
test wr_flag,1 ; see if we have anything to write
jz no_flush ; no, skip scanning for dirty sectors
mov bx,0 ; start at host sector 0
mov cx,host_spt ; for host_spt sectors...
next_sect:
test sec_flags[BX],1 ; see if this sector has been changed
jz not_updated ; no, leave it alone
mov sec_flags[BX],0 ; zero the flag for next time
push bx ; save the registers
push cx
mov cur_sec,bx ; save host sector number
mov ax,host_sectsiz
mul bx ; make track buffer offset
add ax,offset track_buffer ; make direct pointer
mov cur_dma,ax ; save for write routine
call sector_write
pop cx
pop bx
not_updated:
inc bx
loop next_sect
no_flush:
mov wr_flag,0 ; clear the dirty buffer flag
ret
clear_flags: ; Clear all variables associated with the track buffer,
; so next operation will have to read a track.
; This is involves clearing all write flags and setting
; the old drive code to the invalid -1.
mov cur_disk,-1 ; insure initial pre-read
sub ax,ax ; make a zero
mov wr_flag,al ; clear the dirty buffer flag
mov di,offset sec_flags ; point to the update flag list
mov bx,ds ! mov es,bx ; ES <- DS
mov cx,host_spt ! cld ; set length and direction
rep stosb ; zero the sector update flags
ret
track_read:
; read an entire track from the drive "cur_disk",
; the track "cur_track" into "track_buffer".
ret
sector_write:
; write a physical sector to disk "cur_disk",
; track "cur_track", sector "cur_sec" from
; the buffer at DS:"cur_dma".
ret
dseg
cpm_disk rb 1
cpm_track rw 1
cpm_sec rw 1
dma_offset rw 1
dma_segment rw 1
dma_longword equ dword ptr dma_offset
cur_disk rb 1
cur_sec rw 1
cur_track rw 1
cur_dma rw 1
bdos_wr_code rb 1 ; 1 indicates a directory write
wr_flag rb 1 ; bit 0 on indicates we have a dirty buffer
sec_flags rb host_spt ; bit 0 of each byte on indicates
; corresponding host sector has been
; updated and needs writing.
track_buffer rb host_sectsiz * host_spt