Files
Sepp J Morris 31738079c4 Upload
Digital Research
2020-11-06 18:50:37 +01:00

1321 lines
32 KiB
NASM

bdos equ 00005h ; BDOS entry address
jump$table equ 00001h ; address of address of CP/M
; jump table
TOM equ 00006h ; address of address of top
; of memory
cr equ 00Dh ; carriage return
lf equ 00Ah ; line feed
vt equ 00Bh ; vertical tab
ht equ 009h ; horizontal tab
bs equ 008h ; backspace
bell equ 007h ; sound bell
blank equ 0E5h ; if whole sector contains this
; for data then sector is
; considered empty or blank
; BDOS function codes
inputf equ 00Ah ; input from console into
; buffer until 'CR' is sensed
printf equ 009h ; print to console from buffer
; until '$' is sensed
char$in equ 001h ; input single character from
; console
char$out equ 002h ; send single character to
; console
reset$dsk equ 00Dh ; reset disk system
reboot equ 000h ; system reboot (warm) function
; more program constants
ser$def$len equ 6 ; length of serial field
org 0100h
jmp start
wboot ds 3 ; jump to warm boot routine
conin ds 3 ; read character from console
conout ds 3 ; send character to console
const ds 3 ; check for console char ready
list ds 3 ; send character to list device
punch ds 3 ; send character to punch
reader ds 3 ; get character from reader
home ds 3 ; move to track 00 on selected
; drive
seldsk ds 3 ; select disk drive
; reg C=disk # (0=A,1=B,etc.)
settrkf ds 3 ; select track function
; reg BC=track #
setsecf ds 3 ; select sector function
; reg C = sector #
setdma ds 3 ; set dma address function
; reg BC=address
readf ds 3 ; read disk at select track
; and sector, into memory
writef ds 3 ; write to disk from memory
; at dma address to selected
; track and sector
listst ds 3 ; return list status
sectran ds 3 ; sector translate routine
start:
lxi sp,stack ; init stack pointer
; fill program jump table with proper information
lhld jump$table ; get starting address
; of system jump table
lxi d,wboot ; get address of our jump
; table
mvi b,3*16 ; init byte counter
call mov$blk ; fill our jump table
; initialize ser$num to zero
lxi h,ser$num
mvi b,ser$def$len
initlop07:
mvi m,'0'
inx h
dcr b
jnz initlop07
; give sign on
call clear ; clear the monitor
lxi d,msg0 ; get address of char string
call pmsg ; print string
; prompt operator for source and destination drives
lxi d,msg12 ; send source message to
call pmsg ; console
call get$char ; get disk code
sui 'A' ; correct it
sta dskS ; store code of source disk
lxi d,msg13 ; send destination message
call pmsg ; to console
call get$char ; get disk code
sui 'A' ; correct it
sta dskD ; store code of dest disk
; prompt operator for source disk
lxi d,msg14 ; get message address
call pmsg$wait
; the reset function clears out any previous information pertaining
; to format, sector size, density, etc. and the next time a disk is
; selected, those values will be reinitialized.
mvi c,reset$dsk
call bdos
; when a disk is selected CP/M initializes the Disk Parameter Header
; and the Disk Parameter Block to conform with the format of the
; selected disk
call selectS ; select source disk
; when the "select disk" function is performed the address of the DPH
; (Disk Parameter Header) is returned in the HL register pair. the
; 1st word in the DPH is the address of the system's sector
; translation table.
mov a,m ; get low byte of table address
sta xlt$offset ; save it
inx h ; get high byte of address
mov a,m
sta xlt$offset+1 ; save it
; the DPH also contains the address of the DPB (Disk Parameter Block).
lxi d,9 ; the DPB address is 10 bytes
dad d ; 10 bytes in DPH
mov a,m ; get lower half of address
sta DPB$addr ; store it
inx h ; get upper half of address
mov a,m
sta DPB$addr+1 ; store it
; move DPB (Disk Parameter Block) into program data area
lxi d,DPB ; DE points to program DPB
lhld DPB$addr ; HL points to system DPB
mvi b,15 ; B is a counter
call mov$blk ; do the move
; get system sector translation table into data segment of program
lhld xlt$offset ; get address of trans table
mov a,h ; if HL = 0 then system doesn't
ora l ; have a translation table
jnz has$table ; which means that I'll have
lda spt ; to init "xlt$tab" myself
mov c,a
mov a,0
lxi h,xlt$tab
initlop0:
mov m,a
inr a
inx h
dcr c
jnz initlop0
mov a,1 ; set skew factor = 1
jmp skew$prompt
has$table:
lhld xlt$offset ; get address of system's
xchg ; translation table
lxi h,xlt$tab ; DE -> system table
; HL -> program table
lda spt ; init counter
mov c,a
initlop1:
ldax d ; get a byte
mov m,a ; store it
inx d ; move pointers
inx h
dcr c ; decrement counter
jnz initlop1 ; loop if C <> 0
; depending on the system, each physical sector on a diskette can
; be made up of a number of logical sectors (128 bytes long). the
; following code analyses the system sector translate table to
; determine the number of logical sectors in a physical sector.
lxi h,xlt$tab
mvi c,0 ; init counter
xltlop0:
inr c ; increment counter
mov a,m
inr a
inx h ; HL -> next byte in table
cmp m ; see if the two numbers are in
jz xltlop0 ; the right order
mov a,c ; save the sectors/block value
sta spb
; calculate skew of system sector translation table
mov a,m ; get byte at xlt$tab+spb
lxi h,xlt$tab
sub m
call divide ; divide A by C
; tell operator about skew
skew$prompt:
push b ; save system skew
call clear ; blank monitor screen
lxi d,msg21 ; send message
call pmsg
pop psw ; recover skew
ori 030h ; make it ASCII
mov e,a ; send it console
mvi c,char$out
call bdos
; ask operator if he/she would like to change the sector skew
lxi d,msg16
call pmsg
call get$char
cpi 'N'
jz dont$change
; ask for new skew factor
lxi d,msg17
call pmsg
call get$1num
sta skew
; make new sector translation table using skew factor supplied by
; operator
lxi h,xlt$tab ; HL -> "xlt$tab"
mov b,m ; init beginning sector number
mov c,b
lda skew ; get skew factor to init
push psw ; loop counter
lda spt ; get sectors/track + beginning
add c ; sector # into reg D
mov d,a
xltlop1:
push b ; save loop counter and 1st
; sector #
lda spb ; init sectors/block counter
mov e,a
mov a,b ; get current starting sector
; of block into reg A
xltlop2:
mov m,a ; put sector number into table
inx h ; increment pointer
inr a ; increment sector number
dcr e ; decrement spb counter
jnz xltlop2
lda spb ; calcualte new starting sector
mov c,a
lda skew
call mult
pop b ; recover starting sector #
push b ; and save it again
add b ; A = spb*skew+current sector
; number
cmp d ; see if new sector # > spt
jz xltlop3 ; go to xltlop3 if 0
jnc xltlop3
pop b ; recover starting sector #
; and skew then move new
mov b,a ; starting sector # into B
jmp xltlop1 ; do it again
xltlop3:
pop b ; recover current and starting
; sector #s
lda spb ; get sectors/block
add c ; add it to starting sector #
mov b,a ; init both sector registers
mov c,a
pop psw ; recover block counter
dcr a
jnz xltlop1
dont$change:
; ok, now calculate how many bytes there are on a track.
; bytes/track = sectors/track * logical sector size.
lda spt ; A=sectors/track
mvi d,0 ; init DE=sectors/track
mov e,a
lxi h,0 ; init HL=0
mvi b,128 ; init addition counter
multlop0:
dad d ; HL=HL+DE
dcr b
jnz multlop0
shld bpt
; search for blank tracks starting with last$trk
; the portion of the source disk which is empty (blank) isn't copied
lxi d,msg1 ; tell operator about search
call pmsg
lxi d,msg7 ; send "Reading" message
call pmsg
lda last$trk ; get value of 'last$track'
mvi b,0 ; B=0
mov c,a ; C=A init track counter
scan$loop:
push b ; save track counter
call settrkf ; set track number
pop b ; get track number into reg A
push b
mov a,c ; make track number ASCII
call make$ascii
lxi d,ascii$trk ; print it
call pmsg
lxi h,trk$buff ; init dma address
shld dma$base
push h ; temp save HL (dma address)
call read ; read a track into 'trk$buff'
; check to see if the track that was just read is blank
pop d ; recover dma address
lhld bpt ; get byte counter
xchg ; HL -> "trk$buff"
; DE = byte counter
blank$scan:
push d ; save byte counter
mov a,m ; A=(HL)
cpi blank ; if A<>blank then track isn't
jnz data$found ; empty
inx h ; HL points to next byte
pop d ; recover byte counter
dcx d ; decrement byte counter
mov a,d ; see DE = 0
ora e
jnz blank$scan ; do again if DE <> 0
call erase$trk ; erase old track number
pop b ; recover track counter
dcr c ; decrement counter
jnz scan$loop ; do again if C <> 0
; the diskette is blank if program flow reaches this point
lxi d,error$msg0 ; inform operator that source
call pmsg ; diskette is blank and
call abort ; abort operation
; 'last$trk' will reflect which track was found to contain data
data$found:
pop b ; clear stack
pop b ; recover track counter
mov a,c ; move track count to reg A
sta last$trk ; save track count
; the information on the source disk will more than likely need to be
; paged into memory. the following code calculates how many pages
; or blocks will be need.
; calculate amount of memory available to be used for a buffer area
; to hold the track data
lxi d,trk$buff
lhld TOM ; get end of TPA address
mov a,l ; now find the difference
sub e
mov l,a
mov a,h
sbb d
mov h,a ; HL = HL-DE
; calculate number of tracks that will fit in that space
xra a ; clear carry flag
lda spt ; reg A = sectors/track
rar ; divide SPT by 2
mov c,a ; reg C = SPT/2
mov a,h ; reg A = available mem/256
call divide ; B = A/C
dcr b ; leave room for extra track
; you probably realize that I'am going to need two page sizes
; (tracks/page) to read in the diskette.
; example:
; let's say that after scaning the diskette for blank tracks and
; taking into account reserved tracks which won't be copied, we
; find that there are 33 tracks with data. next we calculate that
; there is enough spare memory in our system to hold 9 tracks of
; data from the disk (page size=9). that means that we can read
; 3 pages of 9 tracks each and then 1 page of 6 tracks (33=3*9+1*6).
; that's is why there are two track/block variables, "B1$cnt" and
; "B2$cnt".
lda OFF ; get number of reserved tracks
mov c,a ; C = A
lda last$trk ; get last track with data
sub c ; A = # of tracks with data
cmp b ; if last$trk<b1$cnt then
jp over0 ; b1$cnt=last$trk
sta b1$cnt
mvi b,1 ; will only need 1 page
sub a ; and no block2 count
jmp b2$too
over0:
push psw ; temp save last$trk
mov a,b ; save "B1$cnt"
sta B1$cnt
pop psw ; recover "last$trk"
mov c,b ; prepare for division
call divide ; calcualte how many "B1$cnt"
; pages there are
b2$too:
sta b2$cnt ; block2 track count=remainder
mov a,b ; save block count
sta block$cnt
; prompt operator for total number of serial numbers on disk
call get$cnt
cpi 0 ; if count = 0 then don't ask
jz over09 ; for starting serial number
; prompt operator for starting serial number
call get$serial
over09:
; ask operator if he/she wants to verify the copy against the source
; disk and if so, how often
call clear
lxi d,msg18 ; ask question
call pmsg
sub a ; init verification frequency
sta ver$freq ; value
call get$char ; get Y/N answer
cpi 'N' ; check for No
jz over1
; ask operator for frequency of verification
lxi d,msg19 ; send message
call pmsg
call get$2num
sta ver$freq ; save value
over1:
; clear monitor screen
call clear
sub a ; init diskette counter
sta dsk$cnt
; copy and serialization portion of program
new$disk:
lxi h,ser$num ; move current serial number
lxi d,serial1 ; into 'Insert' message
mvi b,ser$def$len
call mov$blk
lxi d,msg3 ; prompt operator for
call pmsg$wait ; destination diskette
; home both disk drives
call selectD ; select destination drive
call home ; home it
call selectS ; select source drive
call home ; home it
sub a ; init serial field counter
sta ser$cnt
sub a ; decides whether to use
sta tog0 ; b1$cnt or b2$cnt
lda b1$cnt ; init track counter
sta trk$cnt
lda OFF ; get number of reserved tracks
sta trackS ; init source disk track
; register
sta trackD ; init destination disk
; track register
lxi d,msg7 ; send "Reading" message
call pmsg
call seekS ; seek
lxi h,trk$buff ; init dma address and read in
shld dma$base ; 1st track so that there is
call read ; always a extra one in memory
; memory
call erase$msg ; erase "Reading" message
lda block$cnt ; get block counter
mov b,a
create$dsk:
push b ; save block counter
call selectS ; select source drive
lhld bpt ; calculate dma address
lxi d,trk$buff
dad d
shld dma$base ; save dma address
lxi d,msg7 ; send 'Reading' message
call pmsg
lda trk$cnt ; init track counter
mov b,a
lop0:
push b ; save track counters
call seekS ; set track to read
call read ; read track
call erase$trk ; erase old track number
call track$setup ; move dma base address
pop b ; recover counter
dcr b
jnz lop0
call erase$msg ; erase "Reading" message
call serialize ; insert serial number
call selectD ; select destination drive
lxi h,trk$buff ; init dma address
shld dma$base
lxi d,msg8 ; send 'Writing message
call pmsg
lda trk$cnt ; init track counter
mov b,a
lop1:
push b ; save track counter
call seekD ; set track to write
call write ; write track
call erase$trk ; erase old track number
call track$setup ; move dma base address
pop b ; recover track counter
dcr b
jnz lop1
call erase$msg ; erase "Writing message
; move extra track (read and searched but not written) to beginning
; of buffer
lhld dma$base ; get address of unwritten
; track
xchg ; DE=HL, DE-> beginning of
; extra track
lhld bpt ; get length of track
push h ; BC=HL
pop b
lxi h,trk$buff ; HL-> beginning of buffer
movlop5:
ldax d ; get byte
mov m,a ; move it
inx h ; increment pointers
inx d
dcx b ; decrement byte counter
mov a,b ; am I done ?
ora c
jnz movlop5
pop b ; recover block counter
dcr b ; decrement counter
jnz create$dsk ; do again if B<>0
lda tog0 ; check toggle
cpi 1
jz done ; if tog0=1 then the copy
; process is done
mvi a,1 ; if tog0<>1 then need to
sta tog0 ; finish copy with
lda b2$cnt ; trk$cnt=b2$cnt
cpi 0 ; but if b2$cnt=0 then
jz done ; we are done
sta trk$cnt
mvi b,1 ; reint block counter
jmp create$dsk ; and finish copying
done:
lxi d,msg8 ; send 'Writing message
call pmsg
call seekD ; write the extra track that
call write ; was read in beginning
lda ser$cnt ; check if all the serial
mov b,a ; fields were found
lda ser$num$cnt
cmp b
jz serial$ok
lxi d,error$msg3 ; inform operator of error
call pmsg
call abort ; abort operation
serial$ok:
call erase$msg ; erase "Writing" message
lxi h,dsk$cnt ; get and increment diskette
inr m ; counter
mov b,m
push b ; save it on stack
lda ver$freq ; get verification frequency
cpi 0 ; see if "ver$freq" = 0
jz dont$verify ; don't verify if yes
mov c,a ; "ver$freq" is divisor
pop psw ; "dsk$cnt" is quotient
call divide ; do division
cpi 0 ; if A<>0 then don't verify
jnz dont$verify
; do the verification
lxi d,msg20 ; send "Verifying" message
call pmsg
lda off ; calculate # of tracks to
sta trackS ; verify
sta trackD
mov b,a
lda last$trk
sub b
inr a
mov c,a ; init counter
verlop0:
push b ; save track counter
call selectD ; select dest disk
call seekD ; seek to current track
lxi h,trk$buff ; init dma address
shld dma$base
call read ; read a track from dest drive
call erase$trk ; erase old track message
call selectS ; select source disk
call seekS ; seek to current track
lxi h,trk$buff ; init dma address
push h ; save pointer to 1st buffer
xchg
lhld bpt
push h ; save bytes/track value
dad d
push h ; save pointer to 2nd buffer
shld dma$base
call read ; read track from source disk
call erase$trk ; erase old track message
pop d ; recover pointer to 2nd buffer
pop b ; recover bytes/track value
pop h ; recover pointer to 1st buffer
verlop1:
ldax d ; get byte from dest buffer
cmp m ; compare it against byte
; from source buffer
jnz verlop3 ; jump if not equal
inx d ; increment pointers
inx h
dcx b ; decrement byte counter and
mov a,b ; check for BC = 0
ora c
jnz verlop1
verlop2:
pop b ; recover track counter
dcr c ; decrement counter
jnz verlop0
jmp dont$verify
verlop3:
mvi a,ser$def$len ; get length of serial
; definition
push h ; save dest pointer
lxi h,ser$def ; HL -> serial definition
verlop4:
sta cnter ; save value of counter
ldax d ; get byte from source buffer
cmp m ; see it equals byte from
jnz ver$error ; serial definition
inx d ; increment pointers
inx h
dcx b ; decrement buffer byte counter
mov a,b ; and check for BC = 0
ora c
jz verlop2
lda cnter ; get counter
dcr a ; decrement byte counter
jnz verlop4 ; do again if C <> 0
pop h ; recover dest pointer
push b ; save buffer byte counter
lxi b,ser$def$len ; correct it
dad b
pop b ; recover byte counter
jmp verlop1
; found a verification error so tell operator and try again
ver$error:
call clear ; clear monitor screen
lxi d,error$msg7
call pmsg
jmp new$disk
dont$verify:
lxi h,ser$num ; insert serial number into
lxi d,serial2 ; 'Remove' message
mvi b,ser$def$len
call mov$blk
call clear ; clear monitor screen
lxi d,msg4 ; send 'Remove' message to
call pmsg ; send to console
call inc$serial ; increment serial number
jmp new$disk ; do it again
error:
lxi d,errormsg3 ; inform operator of error
call pmsg$wait ; send message to console
jmp wboot ; abort program
track$setup:
lhld bpt
xchg
lhld dma$base
dad d
shld dma$base
ret
inc$serial:
mvi b,ser$def$len ; B = length of serial number
lxi h,ser$num+ser$def$len-1 ; HL points to end of serial
; number
addlop0:
push b ; save byte counter
mov a,m ; get byte of serial number
inr a ; increment byte
daa ; decimal adjust result
push psw ; need to check AC (auxiliary
pop b ; carry) flag to see if there
mov a,c ; was a carry from the low
ani 010h ; nybble to the high nybble
jnz fix ; increment the next byte if
; there was a carry
mov m,b ; else save serial number byte
pop b ; and stop
ret
fix:
mov a,b ; recover serial number byte
ani 00Fh ; mask off upper nybble
ori 030h ; make high nybble '3' again
mov m,a ; store byte
dcx h ; point to next byte
pop b ; recover byte counter
dcr b ; decrement byte counter
jnz addlop0 ; do again if B<>0
ret
; search for all occurances of 'ser$def' and insert serial number
serialize:
lhld dma$base ; get address of end of buffer
dcx h ; HL->end of trk$buff
mov a,m ; save last byte in trk$buff
push psw
push h ; save address of byte
mvi m,0 ; put 00h at end of buffer
inx h ; HL -> end of "trk$buff" + 1
xchg ; DE = HL
lxi h,trk$buff ; HL points to trk$buff
serlop0:
lda ser$def ; get 1st digit of ser$def
; into the source register
cmp m ; compare source with (HL)
jz serlop2 ; if equal then check further
inx h ; increment HL
mov a,h ; am I done ?
cmp d
jnz serlop0
mov a,l
cmp e
jnz serlop0
pop h ; restore last byte of trk$buff
pop psw
mov m,a
ret
serlop2:
push d ; save pointer to end of buffer
push h ; save 'trk$buff' pointer
inx h ; HL points to next byte
lxi d,ser$def+1 ; DE points to 'ser$def'+1
mvi b,ser$def$len-1 ; B = length 'ser$def'-1
serlop3:
ldax d ; get next byte of 'ser$def'
cmp m ; compare with where HL points
jnz not$ser$field ; if not equal stop compare
inx h ; increment HL
inx d ; increment DE
dcr b ; decrement byte counter
jnz serlop3 ; do again if B<>0
; found a serial field so increment field counter
lxi h,ser$cnt
inr m
; n move current serial number into track data
pop h ; recover 'trk$buff' pointer
lxi d,ser$num ; DE points to current serial
; number
mvi b,ser$def$len ; init byte counter
serlop4:
ldax d ; get serial number byte
mov m,a ; move to where HL points
inx d ; increment DE
inx h ; increment HL
dcr b ; decrement byte counter
jnz serlop4 ; if B<>0 do again
pop d ; recover end of buffer pointer
jmp serlop0 ; continue with serialization
not$ser$field:
pop h ; recover trk$buff pointer
inx h ; HL points to next byte
pop d ; recover end of buffer
; pointer
jmp serlop0 ; continue with serialization
; read a track from disk (track and disk drive already selected)
read:
lda spt ; source = sectors/track
mov b,a ; B=A loop counter
mvi c,0 ; init sector counter to 0
rtrklop0:
push b ; save loop counter on stack
call setsec ; set sector to read
call setdma ; set dma address
call readf ; read at selected track
; and sector
cpi 000h ; check for read error
jnz rd$error ; if yes then tell operator
pop b ; recover loop and sector
; counters
inr c ; increment sector counter
dcr b
jnz rtrklop0 ; do again if B<>0
ret
rd$error:
lxi d,errormsg4 ; prompt operator about error
call pmsg
call abort ; abort operation
; write a track to disk (track and disk already selected)
write:
lda spt ; source = sectors/track
mov b,a ; B=A
mvi c,0 ; init sector counter
wtrklop0:
push b ; save loop and sector counters
call setsec ; set sector thru BDOS
call setdma ; set dma address thru BDOS
call writef ; write sector to disk
cpi 000h ; check for write error
jnz wr$error ; if yes then tell operator
pop b ; recover loop and sector
; counters
inr c ; increment sector counter
dcr b ; decrement loop counter
jnz wtrklop0 ; do again if B<>0
ret
wr$error:
lxi d,errormsg5 ; prompt operator about error
call pmsg
call abort ; abort operation
; do sector translation and set sector
setsec:
mvi b,0 ; B=0
lxi h,xlt$tab ; HL -> translation table
dad b ; HL points to translated
; sector number
mov c,m ; C = translated sector number
push b
call setsecf ; set sector thru BDOS
pop b
mov a,c
lxi h,xlt$tab
sub m
add a
mov c,a
lxi h,addr$tab
dad b
mov c,m
inx h
mov b,m
lhld dma$base
dad b
push h
pop b
ret
; move block (HL) to block (DE) with length of B
mov$blk:
mov a,m
stax d
inx d
inx h
dcr b
jnz mov$blk
ret
; select source disk
selectS:
lda dskS
jmp select
; select destination disk
selectD:
lda dskD
select:
mov c,a
call seldsk
ret
; select track on source disk
seekS:
lxi h,trackS
jmp seek
; select track on destination disk
seekD:
lxi h,trackD
seek:
mov c,m
inr m
mvi b,0
push b
mov a,c
call make$ascii
lxi d,ascii$trk
call pmsg
pop b ; recover track number
call settrkf
ret
; print string on console
pmsg:
mvi c,printf
call bdos
ret
; print string on console and wait for carriage return
pmsg$wait:
call pmsg
waitlop:
mvi c,char$in
call bdos
cpi 'C'-'@' ; check for control-C
jz abort ; abort if yes
cpi cr
jnz waitlop
ret
; abort the program
abort:
lxi d,abort$msg
call pmsg$wait
mvi c,reboot ; BDOS system reboot function
call bdos
; reads in starting serial number from console
get$serial:
lxi d,msg2 ; DE = operator prompt address
call pmsg ; send it to console
mvi c,inputf ; C = input string BDOS code
lxi d,con$buff0 ; DE = address of console buff
call bdos ; BDOS call
lda buff0len ; check to see if got right
cpi ser$def$len ; number of digits
rz ; yes so can return
mvi a,ser$def$len ; give operator correct
ori 030h ; number of digits to enter
sta errormsg2
lxi d,errormsg1 ; tell operator about error
call pmsg
jmp get$serial ; do again
; get from the operator how many serial fields are on the source
; diskette. this is a safeguard to help prevent the wrong disk
; from being used.
get$cnt:
lxi d,msg6 ; send prompt to console
call pmsg
call get$2num ; get a number (0-99) from
; operator
sta ser$num$cnt ; save number
ret
; input two ASCII numbers into buffer 1 and translate them into a
; binary number which is returned in the A register.
get$2num:
mvi c,inputf ; tell bdos to get input
lxi d,con$buff1 ; DE=address of input buffer
call bdos
lda buff1len ; how many characters were read
cpi 3 ; check for more than two
jm input$ok0 ; if two or less then OK
input$err0:
lxi d,errormsg6 ; tell operator about problem
call pmsg
jmp get$2num ; try again
input$ok0:
cpi 0 ; need at least one digit.
jz input$err0 ; tell operator if not
mov c,a ; going to make HL->end of
mvi b,0 ; input string
lxi h,count$num
dad b
dcx h
mov a,m ; get units digit
ani 00Fh ; mask off upper nybble of
; units digit
dcr c ; decrement digit counter
jz units ; if C=0 then only had one
; digit so can exit
push psw ; save units digit
dcx h ; make HL -> next digit
mov a,m ; get tens digit
ani 00Fh ; mask off upper nybble
mvi c,10 ; init multiplier
call mult ; A = A*C
pop b ; get units digit back
add b ; add tens and units digits
units:
ret
; get a single ASCII number from operator and check for control-C
get$1num:
mvi c,char$in
call bdos
cpi 'C'-'@'
jz abort
ani 00Fh
ret
; this code erases the "Reading" and "Writing" messages from the
; monitor screen
erase$msg:
lxi d,msg10
call pmsg
ret
; this code erases the old track numbers from the monitor screen
erase$trk:
lxi d,msg11
call pmsg
ret
; clear monitor screen
clear:
lxi d,clear$scn ; get address of char string
call pmsg ; call print routine
ret
; translate the 8 bit number in A to ASCII into "ascii$trk"
make$ascii:
lxi h,' ' ; fill "ascii$trk" with blanks
shld ascii$trk
shld ascii$trk+2
lxi h,ascii$trk+3 ; HL -> "ascii$trk"+3
makelop0:
mvi c,10 ; init divisor
call divide ; B=A/C remainder in A
adi '0' ; make remainder ASCII
mov m,a ; store it in "ascii$trk"
dcx h ; move pointer to next location
mov a,b ; get qoutient into A
cpi 0 ; is qoutient zero yet ?
jnz makelop0 ; no, have more digits
ret
; get ASCII input from operator and translate it to upper case
get$char:
mvi c,char$in ; init BDOS call
call bdos
cpi 'C'-'@' ; check for control-C
jz abort ; abort if yes
ani 05Fh ; xlate lower to upper case
; echo choice back to operator
echo$choice:
push psw ; temp save
mvi e,bs ; backspace over last input
mvi c,char$out ; send backspace to console
call bdos
pop psw ; recover old value
push psw ; and save it again
mov e,a ; send character to console
mvi c,char$out
call bdos
pop psw ; restore old value
ret
; divide number in register A by number in register C and return
; quotent in register B with remainder in register A.
divide:
mvi b,0 ; set result result reg = 0
divlop0:
cmp c ; if C>A then quit
rc
sub c ; A = A-C
inr b ; increment result register
jmp divlop0
ret
; multiply register A by register C and return result in register A
; assume that A*C will be <= 255
mult:
mov b,a
sub a ; zero out result register
dcr c ; check for C = 0
jm dont$mult
inr c
multlop1:
add b
dcr c
jnz multlop1
dont$mult:
ret
; **** Data Area **** ;
cnter ds 1 ; will contain a counter value
sector ds 1 ; sector number held here
tog0 ds 1
trackS db 76 ; source disk track register
trackD db 76 ; destination disk track reg
dskS ds 1 ; code for source disk
dskD ds 1 ; code for destination disk
trk$cnt ds 1 ; how many tracks in block to
; read or write
ver$freq ds 1 ; holds verification frequency
dsk$cnt ds 1 ; number of copies nade so far
skew ds 1 ; storage for skew factor
spb ds 1 ; sectors/ block storage
xlt$tab ds 128 ; sector translation table
addr$tab dw 0000h,0680h,0480h,0B00h,0280h,0900h,0080h
dw 0700h,0500h,0B80h,0300h,0980h,0100h,0780h
dw 0580h,0C00h,0380h,0A00h,0180h,0800h,0600h
dw 0C80h,0400h,0A80h,0200h,0880h
bpt ds 2 ; number of bytes/track
b1$cnt ds 1 ; number of tracks in 1st block
b2$cnt ds 1 ; number of tracks in 2nd block
block$cnt ds 1 ; how blocks of b1$cnt size
ser$def db '654321' ; serial field on master disk
con$buff0 db 10 ; console input buffer
buff0len ds 1
ser$num ds 10
con$buff1 db 5 ; console input buffer
buff1len ds 1
count$num ds 5
ser$num$cnt ds 1 ; how many serial fields
; are there on the source disk
ser$cnt ds 1 ; counter for number of serial
; fields
last$trk db 76 ; be sure this is right for
; your system
dma$base ds 2 ; save area for current dma
; base address
dma$addr ds 2 ; save area for current dma
; address
clear$scn db cr,lf,lf,lf,lf,lf,lf,lf,lf,lf,lf,lf,lf,lf,lf
db lf,lf,lf,lf,lf,lf,lf,lf,lf,lf
db vt,vt,vt,vt,vt,vt,vt,vt,vt,vt,vt,vt,vt,vt,vt
db vt,vt,vt,vt,vt,vt,vt,vt,vt
db '$'
msg0 db cr,lf,lf
db '-------------------------'
db '-------------------------'
db '-'
db cr,lf
db 'SERIAL8 V1.0 Serial No. '
db ' SER-0000-000002'
db cr,lf
db 'Copyright (C) 1982'
db cr,lf
db 'Digital Research, Inc. '
db 'All Rights Reserved'
db cr,lf
db '-------------------------'
db '-------------------------'
db '-'
db cr,lf,lf
db 'Diskette Generation Utility',cr,lf
db 'for CP/M-80 based systems'
db '$'
msg14 db cr,lf,lf
db 'Insert Source Disk '
db 'and type "Return" to continue'
db cr,lf,lf
db '$'
msg1 db cr,lf,lf
db 'Checking for blank tracks'
db cr,lf,lf
db '$'
msg2 db cr,lf,lf
db 'Enter starting serial number '
db '$'
msg3 db cr,lf,lf
db 'Insert '
serial1 ds ser$def$len
db ' into destination drive'
db cr,lf
db ' and type "Return" to continue'
db cr,lf,lf
db '$'
msg4 db bell,cr,lf,lf
db 'Remove '
serial2 ds ser$def$len
db ' from destination drive'
db cr,lf,lf
db '$'
msg6 db bell,cr,lf,lf
db 'Enter number of serial fields on'
db ' source disk '
db '$'
msg7 db 'Reading Track '
db '$'
msg8 db 'Writing Track '
db '$'
msg9 ds 0
ascii$trk db ' '
db ' '
db '$'
msg10 db bs,bs,bs,bs,bs,bs,bs,bs,bs,bs
db bs,bs,bs,bs,bs,bs
msg11 db bs,bs,bs,bs,bs
db '$'
msg12 db cr,lf,lf,lf
db 'Source Drive is (A,B,etc) ? '
db '$'
msg13 db cr,lf,lf
db 'Destination Drive is (A,B,etc) ? '
db '$'
msg16 db cr,lf,lf
db 'Would you like to change the skew factor'
db ' (Y/N) ? '
db '$'
msg17 db cr,lf,lf
db 'Enter skew factor (1-9) ? '
db '$'
msg18 db cr,lf,lf
db 'Do you want to perform copy verification'
db ' (Y/N) ? '
db '$'
msg19 db cr,lf,lf
db '1 = every disk is verified'
db cr,lf
db '2 = every other disk is verified'
db cr,lf
db '3 = every 3rd disk is verified'
db cr,lf,'"',cr,lf,'"',cr,lf,'"',cr,lf
db '99 = every 99th disk is verified'
db cr,lf,lf
db 'How often do you want the verification to'
db ' occur (1-99) ? '
db '$'
msg20 db 'Verifying Track '
db '$'
msg21 db cr,lf,lf
db 'The current sector skew is '
db '$'
errormsg0 db bell,cr,lf,lf
db 'Source Diskette is blank'
db cr,lf,lf
db '$'
errormsg1 db cr,lf,lf
db 'Serial number must be '
errormsg2 ds 1
db ' digits long. Try again.'
db cr,lf
db '$'
errormsg3 db bell,cr,lf,lf
db 'Did not find all of the required serial '
db 'fields'
db cr,lf
db '$'
errormsg4 db bell,cr,lf,lf,lf
db '****** A read error has occurred ******'
db cr,lf
db '$'
errormsg5 db bell,cr,lf,lf,lf
db '****** A write error has occurred ******'
db cr,lf
db '$'
errormsg6 db cr,lf,lf
db 'Need one or two digits. Try again.'
db '$'
error$msg7 db bell,cr,lf,lf
db '**** Verification failed ****'
db cr,lf
db '$'
abort$msg db cr,lf,lf,lf
db '*** Serialization terminated ***'
db cr,lf,lf
db 'Replace system disk in Drive A:'
db cr,lf
db 'then type "Return" to reboot'
db '$'
xlt$offset ds 2 ; address of sector translation
;table
dpb$addr ds 2 ; address of dpb
; Disk Parameter Block
dpb ds 0 ; DPB will be moved here
spt ds 2 ; sectors/track
ds 11 ; not important
off ds 1 ; number of reserved tracks
ds 1 ; not important
ds 64 ; stack area
stack ds 0
trk$buff ds 0 ; track buffer
end