mirror of
https://github.com/SEPPDROID/Digital-Research-Source-Code.git
synced 2025-10-23 16:34:07 +00:00
1225 lines
31 KiB
Plaintext
1225 lines
31 KiB
Plaintext
; Use "gencmd serial86 data[m100]" when producing CMD file
|
||
|
||
; Revisions: 3/30/83 whf: version check, work on PC, print num serial fields
|
||
|
||
; 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
|
||
|
||
; cpm call function codes
|
||
|
||
cpm equ 224 ; cpm interrupt
|
||
cpmtype equ 0h ; version number for CP/M-86
|
||
|
||
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
|
||
|
||
s_bdosver equ 12 ; get OS & BDOS version
|
||
|
||
; 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
|
||
|
||
; check for CP/M-86
|
||
mov cl,s_bdosver ; check OS version
|
||
int cpm
|
||
cmp ah,cpmtype
|
||
je okversion
|
||
mov dx,offset msgver
|
||
call pmsg
|
||
jmp abort
|
||
|
||
okversion:
|
||
|
||
; 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
|
||
cmp al,dskS
|
||
jne okdest
|
||
mov dx,offset msg_sdequal
|
||
call pmsg
|
||
jmp okversion
|
||
okdest:
|
||
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 cpm
|
||
|
||
; 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 cpm
|
||
|
||
; 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 cpm ; 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
|
||
push dx ; remember skew
|
||
or dl,030h ; make it ASCII
|
||
mov cl,c_write
|
||
int cpm
|
||
; check for 0 skew factor
|
||
pop ax ; get skew
|
||
or ax,ax
|
||
je dont_change ; skip question if 0 skew
|
||
; 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
|
||
|
||
; calculate last_trk: whf 2/28/83
|
||
mov bx,1
|
||
mov cl,bsh
|
||
rol bx,cl ; bx=sectors per block
|
||
mov ax,spt ; ax=sectors per track
|
||
div bx
|
||
mov bx,ax ; bx=blocks per track
|
||
mov ax,dsm
|
||
inc ax ; ax=total blocks
|
||
div bx ; ax=total data tracks
|
||
add ax,off ; ax=total tracks
|
||
dec ax ; ax=last_track (0 relative)
|
||
mov last_trk,ax ; save it away
|
||
|
||
mov cl,mc_max ; find 64k or less of free
|
||
mov dx,offset mcb ; memory
|
||
int cpm
|
||
|
||
; 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,serial_cnt ; check if all serial fields
|
||
cmp al,count_num ; were found
|
||
serial_chk:
|
||
je serial_ok ; if not then there is an error
|
||
mov dx,offset error_msg3 ; inform operator of error
|
||
call pmsg
|
||
; output # serial fields
|
||
mov dl,' ' ! mov cl,c_write ! int cpm
|
||
xor ax,ax ! mov al,serial_cnt
|
||
mov bl,100 ! div bl
|
||
push ax ! add al,'0' ! mov dl,al ! mov cl,c_write ! int cpm
|
||
pop ax ! mov al,ah ! xor ah,ah
|
||
mov bl,10 ! div bl
|
||
push ax ! add al,'0' ! mov dl,al ! mov cl,c_write ! int cpm
|
||
pop ax ! mov al,ah
|
||
add al,'0' ! mov dl,al ! mov cl,c_write ! int cpm
|
||
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 cpm
|
||
mov cl,s_bios
|
||
int cpm
|
||
|
||
mov dx,offset set_dma_offset ; set dma offset thru cpm
|
||
mov cl,s_bios
|
||
int cpm
|
||
|
||
mov cl,s_bios ; set read/write func thru cpm
|
||
mov dx,offset io_tab
|
||
int cpm
|
||
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 cpm
|
||
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 cpm
|
||
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 cpm
|
||
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 cpm
|
||
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 cpm
|
||
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 cpm
|
||
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 cpm
|
||
|
||
; print the string whose offset is in dx and is terminated by '$'
|
||
pmsg:
|
||
mov cl,c_writebuf
|
||
int cpm
|
||
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 cpm ; 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 cpm
|
||
pop dx ; get temp save
|
||
push dx
|
||
mov cl,c_write ; write it out
|
||
int cpm
|
||
pop ax
|
||
ret
|
||
|
||
; get a single ASCII number from operator and check for .
|
||
get_1num:
|
||
mov cl,c_read
|
||
int cpm
|
||
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 'SERIAL86 V1.2 Serial No. '
|
||
db 'XXXX-0000-654321'
|
||
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 cr,' ',cr,'$'
|
||
|
||
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 '$'
|
||
|
||
msg_sdequal db bell,cr,lf
|
||
db 'Source and Destination are the same. Please re-enter.'
|
||
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 the correct number of required serial '
|
||
db 'fields.'
|
||
db cr,lf
|
||
db 'Number found was '
|
||
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
|
||
db '$'
|
||
|
||
msgver db cr,lf,lf
|
||
db 'This program requires CP/M-86'
|
||
db cr,lf,lf
|
||
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
|
||
|