Files
Digital-Research-Source-Code/CPM OPERATING SYSTEMS/CPM 86/CONCURRENT/CCPM-86 2.0 SOURCE/serializ/serial16.a86
Sepp J Morris 31738079c4 Upload
Digital Research
2020-11-06 18:50:37 +01:00

1164 lines
29 KiB
Plaintext

; Use "gencmd serial data[m100]" when producing CMD file
; print control constants
cr equ 00Dh ; carriage return
lf equ 00Ah ; line feed
bell equ 007h ; self-explanatory
bs equ 008h ; backspace
vt equ 00Bh ; vertical tab
; ccpm call function codes
ccpm equ 224 ; ccpm interrupt
c_read equ 1 ; request single character from console
c_write equ 2 ; send single character to console
c_writebuf equ 9 ; print string terminated by '$'
drv_dpb equ 31 ; get DBP offset and segment base
drv_resetall equ 13 ; reset disk system
drv_set equ 14 ; select disk drive reg DL=disk #
; (0=A,1=B,etc.)
mc_max equ 53 ; find available memory space
; bdos function codes for direct BIOS calls
s_bios equ 50 ; direct BIOS call function code
read_code equ 13 ; read a 128 byte sector
write_code equ 14 ; write a 128 byte sector
; program constants
blank equ 0E5h ; data which an empty (blank)
; sector will contain
false equ -1
true equ 0
cseg
start:
; init serial_number
mov cx,length ser_def
mov al,'0'
mov bx,offset serial_number
initlop10:
mov byte ptr[bx],al
inc bx
loop initlop10
; give sign on
call clear ; clear the monitor screen
mov dx,offset msg0 ; print sign on message
call pmsg
; prompt operator for source and destination drives
mov dx,offset msg12 ; send source message to
call pmsg ; console
call get_char ; get disk code
sub al,'A' ; correct it
mov dskS,al ; store code for source disk
mov dx,offset msg13 ; send destination message
call pmsg ; to console
call get_char ; get disk code
sub al,'A' ; correct it
mov dskD,al ; store code for dest disk
; prompt operator for source disk
mov dx,offset msg14 ; send message
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.
mov cl,drv_resetall ; reset disks
int ccpm
; 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.
mov al,dskS ; select source disk
mov disk,al
mov cl,s_bios
mov dx,offset sel_dsk_tab
int ccpm
; when the "select disk" function is performed using a direct BIOS call
; the offset of the DPH (in system's extra segment) is returned in the
; BX register. the first word of the DPH is the offset of the sector
; translation table for the system.
mov xlt_offset,bx ; save offset of system's
; sector translation table
; move DPB into program data area
cld
mov cl,drv_dpb ; get system extra segment
int ccpm ; value and offset of DPB
push es ; save ES value
mov si,bx ; init source pointer
mov es,data_base
mov di,offset DPB
pop ds ; get system extra seg into DS
mov cx,15 ; init byte counter
rep movsb
push ds ; save system extra seg value
push es ; restore DS value
pop ds
; get system sector translation table into data segment of program
pop es ; recover extra seg value
mov bx,xlt_offset ; get offset of trans table
mov si,es:word ptr[bx] ; into SI
test si,si ; if SI = 0 then system doesn't
jnz has_table ; have a translation table
mov bx,offset xlt_tab ; which means that I'll have
mov cx,spt ; to init "xlt_tab" myself
mov al,0
initlop0:
mov byte ptr[bx],al
inc al
inc bx
loop initlop0
mov al,1 ; set skew factor = 1
jmps init_addr_tab
has_table:
push es ; save extra seg again
mov bx,xlt_offset ; get offset of trans table
mov di,offset xlt_tab ; DI = offset of program sector
; translation table
mov cx,spt ; use sectors/track as
; counter value
mov es,data_base ; init ES value
pop ds ; init DS
rep movsb
push es ; restore old DS value
pop ds
; make sure that serial numbers that cross sector boundries will be
; found
init_addr_tab:
sub ax,ax ; init address offset
mov si,offset xlt_tab ; SI -> xlt-tab
mov cx,spt ; init counter
sub dh,dh ; DH = 0
initlop9:
mov bx,offset addr_tab
mov dl,byte ptr[si] ; get sector number
inc si ; move pointer
sub dl,xlt_tab ; subtract 1st sector number
add dl,dl ; multiply by 2
add bx,dx ; BX -> correct table entry
mov word ptr[bx],ax ; put address into table
add ax,128 ; AX = AX+bytes/sectorz
loop initlop9
; 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.
mov bx,offset xlt_tab ; BX -> xlt_tab
xltlop0:
mov al,byte ptr[bx] ; AL = table entry
inc bx ; BX -> next byte in table
inc al
cmp al,byte ptr[bx] ; see if the two numbers are in
je xltlop0 ; the right order
sub bx,offset xlt_tab ; if they aren't then calculate
; how long the sequence was
mov spb,bx ; and save the value
; calculate skew of system sector translation table
mov al,xlt_tab[bx] ; get byte at xlt_tab+spb
sub al,xlt_tab ; and subtract byte at xlt_tab
mov bx,spb ; now divide by sectors/block
sub ah,ah ; prepare of division
div bl
mov skew,al
; tell operator about skew
skew_prompt:
push ax ; save system skew
call clear ; clear screen
mov dx,offset msg21 ; send message
call pmsg
pop dx ; recover skew
or dl,030h ; make it ASCII
mov cl,c_write
int ccpm
; ask operator if he/she would like to change the sector skew
mov dx,offset msg16
call pmsg
call get_char ; get Y/N response
cmp al,'N' ; check for No
je dont_change
; ask for new skew factor
mov dx,offset msg17
call pmsg
call get_1num
mov skew,al
; make new sector translation table using skew factor supplied by
; operator
mov si,offset xlt_tab ; BX is pointer into xlt_tab
mov bl,byte ptr[si] ; init beginning sector number
mov bh,bl
mov dl,sptbyte ; calculate sectors/track +
add dl,bl ; first sector number
mov cl,skew ; init loop counter
sub ch,ch
xltlop1:
push cx ; save loop counter
xltlop2:
mov cx,spb ; init sectors/block counter
push bx ; save sector # registers
xltlop3:
mov byte ptr[si],bh ; put sector number into table
inc si ; increment pointer
inc bh ; increment sector number
loop xltlop3
mov ax,spb ; calculate new starting sector
mov cl,skew
mul cl
pop bx ; recover sector # registers
add bh,al ; add skew*spt to starting
; sector number
cmp bh,dl ; compare new starting sector #
; to first sector # + spt
jb xltlop2
add bl,spbbyte ; add sectors/block to 1st
; starting sector number
mov bh,bl ; init starting sector
; number of the next sector
; block
pop cx ; recover loop counter
loop xltlop1
dont_change:
; ok, now calculate many how bytes there are on a track.
; bytes/track = sectors/track * logical sector size
mov ax,spt
mov bx,128
mul bx
mov bpt,ax ; save value
mov cl,mc_max ; find 64k or less of free
mov dx,offset mcb ; memory
int ccpm
; check the disk for blank tracks starting at last track. blank
; tracks won't be copied
mov dx,offset msg1 ; tell operator about search
call pmsg
cld ; set direction flag forward
mov dx,offset msg7 ; send 'Reading' message
call pmsg
mov cx,last_trk ; initialize track counter
scan_loop:
push cx ; save track counter
mov track,cl ; get current track
mov al,cl ; make track number ascii
call make_ascii
mov dx,offset ascii_trk ; print it
call pmsg
mov dma_addr,0 ; initialize dma offset
call seek ; seek to track
call read ; read a track
mov al,blank ; init AL with the character
; which is found in blank
; (empty) sectors
mov es,m_base ; init segment reg
mov di,0 ; offset is set to zero
mov cx,bpt ; how many bytes to scan
repe scasb ; scan until ZF=1 or CX=0
jne data_found ; if ZF=1 then have found data
call erase_trk ; erase old track message
pop cx ; otherwise scan another track
loop scan_loop ; if CX<>0
; the diskette is blank if program flow reaches this point
mov dx,offset error_msg0
call pmsg ; inform operator that the
call abort ; source disk is blank and
; abort operation
data_found:
pop cx ; if data is found then save
mov last_trk,cx ; the number of the non-blank
; track
; 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 needed.
mov ax,m_length ; calculate number of bytes
mov bx,16 ; available to be used for
mul bx ; a buffer area
mov bx,bpt
div bx
mov cx,last_trk ; CX will contain number of
sub cx,off ; tracks to be copied
cmp ax,cx ; if block1 track count<lst_trk
jl over0 ; then get block2 track count
mov B1_trk_cnt,cx ; last_trk, block_cnt=
mov block_cnt,1 ; 1 and jump to new_disk
jmps over07
over0:
dec ax
mov B1_trk_cnt,ax
sub dx,dx
mov bx,ax ; bx holds divisor
mov ax,cx ; hold dividend in division
; operation
div bx ; do division
mov block_cnt,ax ; move result to memory
mov B2_trk_cnt,dx ; remainder = block2 track
; count
over07:
; prompt operator for number of serial fields on source disk
call get_cnt
cmp al,0 ; if count = 0 then don't ask
jz over09 ; for a 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 ; clear monitor screen
mov dx,offset msg18 ; ask question
call pmsg
mov ver_freq,0 ; init verification frequency
; value
call get_char ; get yes/no answer
cmp al,'N' ; check for no
je over01 ; if no then dont verify
; ask operator for frequency of verification
mov dx,offset msg19 ; send message
call pmsg
call get_2num ; get response from operator
mov ver_freq,al ; save value
over01:
; clear monitor screen
call clear
mov dsk_cnt,0 ; init diskette counter
; this portion of code does the copying and serializing
new_disk:
cld ; move current serial number
mov es,data_base ; into insert serial message
mov cx,length ser_def
mov si,offset serial_number
mov di,offset serial_1
rep movsb
mov dx,offset msg3 ; prompt operator for
call pmsg_wait ; destination diskette
; home both disk drives
call selectD
call home
call selectS
call home
mov ax,off ; don't copy reserved tracks
mov trackS,al
mov trackD,al
mov ax,B1_trk_cnt ; init track counter
mov track_cnt,ax
mov tog0,0 ; decides whether to use
; B1_trk_cnt or B2_trk_cnt
mov serial_cnt,0 ; used to check if all the
; serial fields were found
mov dx,offset msg7 ; send 'Reading' message
call pmsg
call seekS ; read in a track so that
mov dma_addr,0 ; there is always an extra
call read ; one in memory
call erase_msg ; erase "Reading" message
mov cx,block_cnt ; init block counter
create_dsk:
push cx ; save block counter
mov cx,bpt ; intialize dma offset
mov dma_addr,cx
call selectS ; select source disk
mov dx,offset msg7 ; send 'Reading' message
call pmsg
mov cx,track_cnt ; init track counter
lop0:
push cx ; save track counter
call seekS ; seek track on source drive
call read ; read a track
call erase_trk ; erase old track message
pop cx ; recover track counter
loop lop0
call erase_msg ; erase "Reading" message
call serialize ; insert current serial number
; into all serial fields in
; this block
mov dma_addr,0 ; same as above but write to
; destination drive
call selectD ; select destination drive
mov dx,offset msg8 ; send "Writing" message to
call pmsg ; console
mov cx,track_cnt
lop1:
push cx
call seekD
call write
call erase_trk ; erase old track message
pop cx
loop lop1
call erase_msg ; erase "Writing" message
; move extra track (read and searched but not written) from end of
; buffer to the beginning of the buffer
cld ; clear direction flag
mov cx,bpt ; init counter
mov ax,cx ; calculate starting address
mov bx,B1_trk_cnt ; of data to be moved
mul bx
mov si,ax
mov di,0 ; where block is to be moved
mov ax,m_base ; init segment registers
push ds ; save old values
push es
mov ds,ax ; move in new values
mov es,ax
rep movsb ; do the move
pop es ; recover values of segment
pop ds ; registers
pop cx ; recover block counter
loop create_dsk ; do again if not last block
cmp tog0,1 ; check if we have reached this
; point before
je done ; if yes then we are done
mov tog0,1 ; no, so toggle tog0
mov ax,B2_trk_cnt ; if B2_trk_cnt=0
cmp ax,0 ; then we are done
je done
mov track_cnt,ax ; else do above again but with
mov cx,1 ; B2_trk_cnt = track count
jmp create_dsk
done:
mov dx,offset msg8 ; send "Writing" message
call pmsg ; to console
call seekD ; write last track to dest
call write ; drive
mov al,count_num ; check if all serial fields
cmp al,serial_cnt ; were found
je serial_ok ; if not then there is an error
mov dx,offset error_msg3 ; inform operator of error
call pmsg
call abort ; abort operation
serial_ok:
call erase_msg ; erase "Writing" message
inc dsk_cnt ; increment diskette counter
mov ax,dsk_cntword ; get number of diskettes done
mov cx,ver_freqword ; get verification frequency
jcxz dont_verify ; don't verify if CX = 0
div cl ; see if it's time to verify
test ah,ah ; if DX = 0 then it's time
jnz dont_verify
; do the verification
mov dx,offset msg20 ; send "Verifying" message
call pmsg
mov cx,last_trk ; calculate number of tracks to
mov ax,off ; compare
sub cx,ax
inc cx ; correct result
mov trackS,al
mov trackD,al
verlop0:
push cx ; save track counter
call selectD ; select dest disk
call seekD ; seek to track on dest disk
mov dma_addr,0 ; init offset of 1st buffer
call read ; read a track from source disk
call erase_trk ; erase old track message
call selectS ; select source drive
call seekS ; seek to current track
mov cx,bpt ; init 2nd buffer offset
push cx ; temp save
mov dma_addr,cx
call read ; read a track from dest disk
call erase_trk ; erase old track message
pop cx ; recover track data length
; into byte counter
sub si,si ; init 1st pointer
mov di,cx ; init 2nd pointer
verlop1:
push ds ; save value of data seg reg
mov ax,m_base ; get extra seg reg value
mov es,ax ; init ES
mov ds,ax ; init DS
repe cmpsb ; verify
pop ds ; recover data seg reg value
je verlop2 ; if ZF = 0 then verification
; failed. inform operator
dec di ; correct 2nd buffer pointer
push si ; save pointer to 1st buffer
push cx ; save byte pointer
mov si,offset ser_def ; SI -> to ser_def
mov cx,length ser_def ; init counter
repe cmpsb ; do compare
jne verlop3
pop cx ; recover byte counter
sub cx,length ser_def-1 ; correct it
pop si ; recover 1st buffer pointer
add si,length ser_def-1 ; correct it
jmps verlop1
; verification failed so inform operator to try it again
verlop3:
call clear
mov dx,offset error_msg7
call pmsg
jmp new_disk
verlop2:
pop cx ; recover track counter
loop verlop0
dont_verify:
call clear ; clear monitor screen
cld ; insert current serial number
mov cx,length ser_def ; into remove message
mov es,data_base
mov si,offset serial_number
mov di,offset serial_2
rep movsb
mov dx,offset msg4 ; prompt operator to remove
call pmsg ; copy
call inc_serial ; increment current serial
; number
jmp new_disk ; do another disk
; read a track from the selected disk
read:
mov io_tab,read_code
jmps track_io
; write a track to the selected disk drive
write:
mov io_tab,write_code
jmps track_io
; do track input or output on selected disk drive
track_io:
mov cx,4 ; initialize io retry counter
io_retry:
push cx ; save retry counter
call track_setup ; init dma base address
mov sector,0 ; start with sector #0
mov cx,spt ; init sector counter
io_lop:
push cx ; save sector counter
call sectran ; perform sector translation
mov dx,offset set_sector ; set sector thru ccpm
mov cl,s_bios
int ccpm
mov dx,offset set_dma_offset ; set dma offset thru ccpm
mov cl,s_bios
int ccpm
mov cl,s_bios ; set read/write func thru ccpm
mov dx,offset io_tab
int ccpm
test al,al ; io error if not zero
jnz io_error ; tell operator about io error
inc sector ; next sector to read
pop cx ; recover sector counter
loop io_lop
pop cx ; clean up stack
mov ax,bpt ; init dma_addr for next
add dma_addr,ax ; track
ret
io_error:
pop cx ; clean up stack
call home ; home disk
pop cx ; recover retry counter
loop io_retry
ret
track_setup:
mov ax,m_base ; initialize dma base address
mov dma_base,ax
mov cl,s_bios
mov dx,offset set_dma_base
int ccpm
ret
; do logical to physical sector translation
sectran:
sub ah,ah
mov al,sector
mov bx,offset xlt_tab
xlat xlt_tab
mov tsector,al
sub ah,ah
sub al,xlt_tab
add al,al
mov bx,offset addr_tab
add bx,ax
mov ax,word ptr[bx]
add ax,dma_addr
mov dma_offset,ax
ret
; home the selected drive
home:
mov track,0
jmps seek
; seek to the selected track on the source drive
seekS:
mov al,trackS
inc trackS
jmps display_trk
; seek to the selected track on the destination drive
seekD:
mov al,trackD
inc trackD
display_trk:
mov track,al
call make_ascii ; make track # ascii
mov dx,offset ascii_trk ; send track number to
call pmsg ; console
; seek to the selected track on the selected drive
; "track" = desired track #
seek:
mov cl,s_bios
mov dx,offset seek_trk_tab
int ccpm
ret
; select source disk
selectS:
mov dl,dskS
jmps select
; select destination disk
selectD:
mov dl,dskD
; select a disk drive for future operations
select:
mov cl,drv_set
int ccpm
ret
; get starting serial number from operator
get_serial:
mov dx,offset msg2 ; send prompt message
call pmsg ; to console
mov dx,offset con_buf0 ; point to console input buffer
mov cl,10 ; CP/M read console buffer
int ccpm
cmp buf0_len,length ser_def ; check for correct number
; of digits
jne bad_serial
ret
bad_serial:
mov error_msg2,length ser_def
or error_msg2,030h ; make it ASCII
mov dx,offset error_msg1
call pmsg
jmps get_serial
; 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:
mov dx,offset msg6 ; send prompt message
call pmsg ; to console
call get_2num ; get a number (0-99)
; from operator
mov count_num,al ; save number
ret
; input two ASCII numbers into buffer 1 and translate them into a
; binary number which is returned in the AL register
get_2num:
mov dx,offset con_buf1 ; get address of buffer
mov cl,10
int ccpm
cmp buf1_len,2 ; can't have more than
jbe input_ok0 ; two digits 0-99
input_err0:
mov dx,offset error_msg6 ; tell operator about
call pmsg ; error and try again
jmp get_2num
input_ok0:
cmp buf1_len,0 ; need at least one
je input_err0 ; digit. tell operator
; if there isn't
mov bx,offset input_num ; bx -> input buffer
mov cl,buf1_len ; (cl) = digit count
sub ch,ch ; zero upper half of cx
add bx,cx ; make bx point to
dec bx ; units digit of input
mov dh,byte ptr[bx] ; get units digit
and dh,00Fh ; mask off upper nybble
loop tens ; if (cx) <> 2 then get
; tens digit
jmp got_it ; only had one digit
tens:
dec bx ; make bx -> to tens
; digit
mov dl,byte ptr[bx] ; get tens digit
and dl,00Fh ; mask off upper nybble
mov al,10 ; load multiplier
mul dl ; do multiply
add dh,al ; add tens to units
got_it:
mov al,dh ; save number
ret
serialize: ; locate all occurrances of serial numbers in buffer
cld ; set direction forward
mov es,m_base ; point ES: to beginning of buffer
mov di,0
mov ax,bpt
mov bx,track_cnt
inc bx ; remember that an extra track was read
mul bx
mov cx,ax
next_serial:
mov si,offset ser_def ; point at pattern
lodsb ; get first char of pattern into AL
repne scasb ; look for next occurance of AL
jne no_match ; if not match, then we are done
push di ; save scan pointer
push cx ; save count
mov cx,(length ser_def)-1 ; residual length of pattern
repe cmpsb
pop cx ; recover scan count
pop di ; recover scan pointer
jne next_serial ; no, rest didn't match
push cx ; save scan pointer
dec di ; move di back to start of field
mov si,offset serial_number ; si points to current serial
; number
mov cx,length ser_def ; counter for number of bytes to move
rep movsb
pop cx ; recover scan pointer
inc serial_cnt
jmp next_serial
no_match:
ret
; increment the ASCII serial number
inc_serial:
mov cx,length ser_def ; length of serial number
mov bx,offset serial_number ; bx points to start of string
add bx,(length ser_def)-1 ; bx now points to end of string
addlop:
push cx ; save length of pointer
mov al,byte ptr[bx] ; get byte of serial number
inc al ; add 1 to it
daa ; decimal adjust it
lahf ; check to see if there was a
and ah,10h ; carry from the lower half of
mov cl,4 ; the al register
shl ah,cl
jc fix ; if yes then increment the next
; byte of the serial number
mov byte ptr[bx],al ; else save byte and stop
pop cx ; clean up stack
ret
fix:
and al,0fh ; make high nybble of byte
or al,30h ; equal to "3" again
mov byte ptr[bx],al ; save byte back in serial
; number
dec bx ; point at next higher byte
pop cx ; recover length of serial
; number
loop addlop ; do again if CX<>0
ret
; translate the 8 bit digit in AL to ASCII into "ascii_trk"
make_ascii:
mov dx,' ' ; fill "ascii_trk" with blanks
mov ascii_trk,dx
mov ascii_trk+2,dx
mov bx,offset ascii_trk+3 ; BX -> "ascii_trk"+3
mov dl,10 ; init divisor
makelop1:
sub ah,ah ; make AH zero
div dl ; and divide by 10
add ah,'0' ; make remainder ascii
mov byte ptr[bx],ah ; store it in "ascii_trk"
dec bx ; move pointer to next location
test al,al ; see if quotient zero yet
jnz makelop1 ; no, got more digits
ret
; print the message at [DX] and wait for RETURN key
pmsg_wait:
call pmsg ; print the message
wait_1:
mov cl,c_read ; CP/M Console Input
int ccpm
cmp al,'C'-'@' ; see if control-C
je abort ; terminate program
cmp al,cr ; see if ASCII carriage return
jne wait_1 ; no, try again
ret ; back to caller
; abort the program
abort:
mov dx,offset abort_msg
call pmsg_wait
mov cl,0 ; CP/M reboot function
int ccpm
; print the string whose offset is in dx and is terminated by '$'
pmsg:
mov cl,c_writebuf
int ccpm
ret
; clears the monitor screen
clear:
cmp clear_on,false ; see if clear screen enable is
je no_clear ; off
mov dx,offset clear_msg
call pmsg
no_clear:
ret
; get ASCII input from operator and translate to upper case
get_char:
mov cl,c_read ; request single character
int ccpm ; from console
cmp al,'C'-'@' ; check for control-C
je abort ; abort operation if yes
and al,05Fh ; xlate lower to upper chase
; echo choice back to operator
echo_choice:
push ax ; temp save
mov dl,bs ; write over last input
mov cl,c_write
int ccpm
pop dx ; get temp save
push dx
mov cl,c_write ; write it out
int ccpm
pop ax
ret
; get a single ASCII number from operator and check for .
get_1num:
mov cl,c_read
int ccpm
cmp al,'C'-'@' ; check for control-C
je abort
and al,00Fh
ret
; this code erases the "Reading" and "Writing" messages from the
; monitor screen
erase_msg:
mov dx,offset msg10
call pmsg
ret
; this code erases the old track numbers from the monitor screen
erase_trk:
mov dx,offset msg11
call pmsg
ret
dseg
org 0000h
; Page Zero Definitions
code_length rw 1 ; low 16 bits code segment length
code_length_h rb 1 ; high 8 bits code segment length
code_base rw 1 ; base paragraph of code segment
model_8080 rb 1 ; 8080 model byte
data_length rw 1 ; low 16 bits data segment length
data_length_h rb 1 ; high 8 bits data segment length
data_base rw 1 ; base paragraph of data segment
rs 1 ; << reserved >>
extra_length rw 1 ; low 16 bits extra segment length
extra_length_h rb 1 ; high 8 bits extra segment length
extra_base rw 1 ; base paragraph of extra segment
org 0100h
seek_trk_tab db 10 ; seek track function table
track rb 4
sel_dsk_tab db 9 ; select disk drive
disk rb 4
set_dma_base db 17 ; set dma base address function table
dma_base rw 2
set_dma_offset db 12 ; set dma offset address function table
dma_offset rw 2
set_sector db 11 ; set sector function table
tsector rb 4 ; holds translated sector #
sector rb 1 ; holds untranslated sector #
io_tab rb 1 ; select read/write function table
rw 2
bpt rw 1 ; how many bytes per track
mcb rw 0 ; memory control block
m_base rw 1 ; base address (segment address)
m_length dw 0FFFh ; desired length of memory area
m_ext rw 1
ver_freqword rw 0
ver_freq db 0,0 ; holds verification frequency value
dsk_cntword rw 0
dsk_cnt db 0,0 ; number of copies made so far
B1_trk_cnt rw 1
B2_trk_cnt rw 1
block_cnt rw 1
track_cnt rw 1
tog0 rb 1
serial_cnt rb 1
count_num rs 1 ; holds number of serial fields
; to expect on source disk
dskS db 0
dskD db 1
skew rb 1 ; reserve storage for skew factor
spb rw 0 ; sectors/block
spbbyte rb 2
DPB rb 0 ; Disk Parameter Block
spt rw 0 ; sector per track
sptbyte rb 2
bsh rb 1 ; block shift factor
blm rb 1 ; block mask
exm rb 1 ; extent mask
dsm rw 1 ; total storage capacity of drive
drm rw 1 ; number of directory entries
al0 rb 1
al1 rb 1
cks rw 1
off rw 1 ; number of reserved tracks
xlt_offset rw 1 ; offset of system translate table
xlt_tab rb 128 ; will contain sector translation table
addr_tab rw 128 ; will contain sector address offsets
; from beginning of track address in
; disk data buffer
dma_addr rw 1 ; will hold dma offset by track
trackS rb 1
trackD rb 1
clear_on db true
last_trk dw 76
ser_def db '654321'
msg0 db cr,lf,lf
db '-------------------------'
db '-------------------------'
db '-'
db cr,lf
db 'SERIAL16 V1.0 Serial No. '
db 'XXXX-0000-999999'
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-86 based systems'
db '$'
msg2 db cr,lf,lf
db 'Enter starting serial number '
db '$'
msg6 db bell,cr,lf,lf
db 'Enter number of serial fields on source disk'
db ' ? '
db '$'
msg7 db 'Reading Track '
db '$'
msg8 db 'Writing Track '
db '$'
msg9 rs 0
ascii_trk rw 2
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 '$'
error_msg1 db cr,lf,lf
db 'Serial number must be '
error_msg2 rb 1
db ' digits long. Try again !!! '
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 '$'
msg14 db cr,lf,lf
db 'Insert Source Disk '
db 'and type "Return" to continue '
db cr,lf,lf
db '$'
msg3 db cr,lf,lf
db 'Insert '
serial_1 rb length ser_def
db ' into destination drive'
db cr,lf
db 'then type "Return" to continue'
db cr,lf,lf
db '$'
msg4 db bell,cr,lf,lf
db 'Remove '
serial_2 rb length ser_def
db ' from destination drive'
db cr,lf,lf
db '$'
abort_msg db cr,lf,cr,lf
db cr,lf,'*** Serialization terminated ***'
db cr,lf
db cr,lf,'Replace disk in Drive A:'
db cr,lf,' then type ENTER to reboot ','$'
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,lf
db '1 = every disk is verified'
db cr,lf
db '2 = every 2nd 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 '$'
error_msg3 db bell,cr,lf,lf
db 'Did not find all of the required serial '
db 'fields'
db cr,lf
db '$'
error_msg6 db cr,lf,lf
db 'Wrong. Need one or two digits'
db '$'
error_msg7 db bell,cr,lf,lf
db '**** Verification failed ****'
db cr,lf,lf
db 'Try it again.'
db cr,lf
db '$'
error_msg0 db bell,cr,lf,lf
db ' The Source Disk is BLANK '
db cr,lf
db '$'
msg1 db cr,lf,lf
db 'Checking for blank tracks'
db cr,lf,lf
db '$'
clear_msg 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,lf,lf,lf,lf,lf
db 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 '$'
con_buf0 db 10 ; buffer used to read
buf0_len rb 1 ; serial number
serial_number rb length ser_def
rb 10-1-length ser_def
con_buf1 db 5 ; another console input buffer
buf1_len rb 1
input_num rb 5
db 0 ; force out end of data segment