mirror of
https://github.com/SEPPDROID/Digital-Research-Source-Code.git
synced 2025-10-26 18:04:07 +00:00
Upload
Digital Research
This commit is contained in:
786
CONTRIBUTIONS/cpm1.4/bldccp/OS2CCP.ASM
Normal file
786
CONTRIBUTIONS/cpm1.4/bldccp/OS2CCP.ASM
Normal file
@@ -0,0 +1,786 @@
|
||||
title 'console command processor (CCP), ver 2.0'
|
||||
; assembly language version of the CP/M console command processor
|
||||
;
|
||||
; version 1.4
|
||||
;
|
||||
; Copyright (c) 1976, 1977, 1978
|
||||
; Digital Research
|
||||
; Box 579, Pacific Grove,
|
||||
; California, 93950
|
||||
;
|
||||
false equ 0000h
|
||||
true equ not false
|
||||
testing equ false ;true if debugging
|
||||
;
|
||||
;
|
||||
if testing
|
||||
org 3400h
|
||||
bdosl equ $+800h ;bdos location
|
||||
else
|
||||
org 000h
|
||||
bdosl equ $+800h ;bdos location
|
||||
endif
|
||||
tran equ 100h
|
||||
tranm equ $
|
||||
ccploc equ $
|
||||
;
|
||||
; ********************************************************
|
||||
; * Base of CCP contains the following code/data *
|
||||
; * ccp: jmp ccpstart (start with command) *
|
||||
; * jmp ccpclear (start, clear command) *
|
||||
; * ccp+6 127 (max command length) *
|
||||
; * ccp+7 comlen (command length = 00) *
|
||||
; * ccp+8 ' ... ' (16 blanks) *
|
||||
; ********************************************************
|
||||
; * Normal entry is at ccp, where the command line given *
|
||||
; * at ccp+8 is executed automatically (normally a null *
|
||||
; * command with comlen = 00). An initializing program *
|
||||
; * can be automatically loaded by storing the command *
|
||||
; * at ccp+8, with the command length at ccp+7. In this *
|
||||
; * case, the ccp executes the command before prompting *
|
||||
; * the console for input. Note that the command is exe-*
|
||||
; * cuted on both warm and cold starts. When the command*
|
||||
; * line is initialized, a jump to "jmp ccpclear" dis- *
|
||||
; * ables the automatic command execution. *
|
||||
; ********************************************************
|
||||
;
|
||||
jmp ccpstart ;start ccp with possible initial command
|
||||
jmp ccpclear ;clear the command buffer
|
||||
maxlen: db 127 ;max buffer length
|
||||
comlen: db 0 ;command length (filled in by dos)
|
||||
; (command executed initially if comlen non zero)
|
||||
combuf:
|
||||
db ' ' ;8 character fill
|
||||
db ' ' ;8 character fill
|
||||
db 'COPYRIGHT (C) 1978, DIGITAL RESEARCH '; 38
|
||||
ds 128-($-combuf)
|
||||
; total buffer length is 128 characters
|
||||
comaddr:dw combuf ;address of next to char to scan
|
||||
staddr: ds 2 ;starting address of current fillfcb request
|
||||
;
|
||||
diska equ 0004h ;disk address for current disk
|
||||
bdos equ 0005h ;primary bdos entry point
|
||||
buff equ 0080h ;default buffer
|
||||
fcb equ 005ch ;default file control block
|
||||
;
|
||||
rcharf equ 1 ;read character function
|
||||
pcharf equ 2 ;print character function
|
||||
pbuff equ 9 ;print buffer function
|
||||
rbuff equ 10 ;read buffer function
|
||||
breakf equ 11 ;break key function
|
||||
liftf equ 12 ;lift head function (no operation)
|
||||
initf equ 13 ;initialize bdos function
|
||||
self equ 14 ;select disk function
|
||||
openf equ 15 ;open file function
|
||||
closef equ 16 ;close file function
|
||||
searf equ 17 ;search for file function
|
||||
searnf equ 18 ;search for next file function
|
||||
delf equ 19 ;delete file function
|
||||
dreadf equ 20 ;disk read function
|
||||
dwritf equ 21 ;disk write function
|
||||
makef equ 22 ;file make function
|
||||
renf equ 23 ;rename file function
|
||||
logf equ 24 ;return login vector
|
||||
cself equ 25 ;return currently selected drive number
|
||||
dmaf equ 26 ;set dma address
|
||||
;
|
||||
; special fcb flags
|
||||
rofile equ 9 ;read only file
|
||||
sysfile equ 10 ;system file flag
|
||||
;
|
||||
; special characters
|
||||
cr equ 13 ;carriage return
|
||||
lf equ 10 ;line feed
|
||||
la equ 5fh ;left arrow
|
||||
eofile equ 1ah ;end of file
|
||||
;
|
||||
; utility procedures
|
||||
printchar:
|
||||
mov e,a! mvi c,pcharf! jmp bdos
|
||||
;
|
||||
printbc:
|
||||
;print character, but save b,c registers
|
||||
push b! call printchar! pop b! ret
|
||||
;
|
||||
crlf:
|
||||
mvi a,cr! call printchar
|
||||
mvi a,lf! jmp printchar
|
||||
;
|
||||
;
|
||||
print: ;print string starting at b,c until next 00 entry
|
||||
push b! call crlf! pop h ;now print the string
|
||||
prin0: mov a,m! ora a! rz ;stop on 00
|
||||
inx h! push h ;ready for next
|
||||
call printchar! pop h ;character printed
|
||||
jmp prin0 ;for another character
|
||||
;
|
||||
initialize:
|
||||
mvi c,initf! jmp bdos
|
||||
;
|
||||
select:
|
||||
mov e,a! mvi c,self! jmp bdos
|
||||
;
|
||||
;
|
||||
open: ;open the file given by d,e
|
||||
mvi c, openf! call bdos! sta dcnt! inr a! ret
|
||||
;
|
||||
openc: ;open comfcb
|
||||
xra a! sta comrec ;clear next record to read
|
||||
lxi d,comfcb! jmp open
|
||||
;
|
||||
close: ;close the file given by d,e
|
||||
mvi c,closef! call bdos! sta dcnt! inr a! ret
|
||||
;
|
||||
search: ;search for the file given by d,e
|
||||
mvi c,searf! call bdos! sta dcnt! inr a! ret
|
||||
;
|
||||
searchn:
|
||||
;search for the next occurrence of the file given by d,e
|
||||
mvi c,searnf! call bdos! sta dcnt! inr a! ret
|
||||
;
|
||||
searchcom:
|
||||
;search for comfcb file
|
||||
lxi d,comfcb! jmp search
|
||||
;
|
||||
delete: ;delete the file given by d,e
|
||||
mvi c,delf! jmp bdos
|
||||
;
|
||||
;
|
||||
diskread:
|
||||
;read the next record from the file given by d,e
|
||||
mvi c,dreadf! call bdos! ora a! ret
|
||||
;
|
||||
diskreadc:
|
||||
;read the comfcb file
|
||||
lxi d,comfcb! jmp diskread
|
||||
;
|
||||
diskwrite:
|
||||
;write the next record to the file given by d,e
|
||||
mvi c,dwritf! call bdos! ora a! ret
|
||||
;
|
||||
make: ;create the file given by d,e
|
||||
mvi c,makef! call bdos! sta dcnt! ret
|
||||
;
|
||||
renam: ;rename the file given by d,e
|
||||
mvi c,renf! jmp bdos
|
||||
;
|
||||
;
|
||||
translate:
|
||||
;translate character in register A to upper case
|
||||
cpi 61h! rc ;return if below lower case a
|
||||
cpi 7bh! rnc ;return if above lower case z
|
||||
ani 5fh! ret ;translated to upper case
|
||||
;
|
||||
readcom:
|
||||
;read the next command into the command buffer
|
||||
;check for submit file
|
||||
lda submit! ora a! jz nosub
|
||||
;scanning a submit file
|
||||
;change drives to open and read the file
|
||||
lda cdisk! ora a! mvi a,0! cnz select
|
||||
;have to open again in case xsub present
|
||||
lda subrc! dcr a ;read last record(s) first
|
||||
sta subcr ;current record to read
|
||||
lxi d,subfcb! call diskread ;end of file if last record
|
||||
jnz nosub
|
||||
;disk read is ok, transfer to combuf
|
||||
lxi d,comlen! lxi h,buff! mvi b,128! call move0
|
||||
;line is transferred, close the file with a
|
||||
;deleted record
|
||||
lxi h,subrc ; mvi m,0 ;clear fwflag
|
||||
dcr m ;one less record
|
||||
lxi d,subfcb! call close! jz nosub
|
||||
;close went ok, return to original drive
|
||||
lda cdisk! ora a! cnz select
|
||||
;print to the 00
|
||||
lxi h,combuf! call prin0
|
||||
call break$key! jz noread
|
||||
call del$sub! jmp ccp ;break key depressed
|
||||
;
|
||||
nosub: ;no submit file! call del$sub
|
||||
;translate to upper case, store zero at end
|
||||
mvi c,rbuff! lxi d,maxlen! call bdos
|
||||
noread: ;enter here from submit file
|
||||
;set the last character to zero for later scans
|
||||
lxi h,comlen! mov b,m ;length is in b
|
||||
readcom0: inx h! mov a,b! ora a ;end of scan?
|
||||
jz readcom1! mov a,m ;get character and translate
|
||||
call translate! mov m,a! dcr b! jmp readcom0
|
||||
;
|
||||
readcom1: ;end of scan, h,l address end of command
|
||||
mov m,a ;store a zero
|
||||
lxi h,combuf! shld comaddr ;ready to scan to zero
|
||||
ret
|
||||
;
|
||||
break$key:
|
||||
;check for a character ready at the console
|
||||
mvi c,breakf! call bdos
|
||||
ora a! rz
|
||||
mvi c,rcharf! call bdos ;character cleared
|
||||
ora a! ret
|
||||
;
|
||||
clifter:
|
||||
mvi c,liftf! jmp bdos
|
||||
;
|
||||
cselect:
|
||||
;get the currently selected drive number to reg-A
|
||||
mvi c,cself! jmp bdos
|
||||
;
|
||||
cpyblock:
|
||||
ldax d! mov m,a! inx d! inx h
|
||||
dcr c! jnz cpyblock! ret
|
||||
;
|
||||
setdma:
|
||||
;set dma address to d,e
|
||||
mvi c,dmaf! jmp bdos
|
||||
;
|
||||
del$sub:
|
||||
;delete the submit file, and set submit flag to false
|
||||
lxi h,submit! mov a,m! ora a! rz ;return if no sub file
|
||||
mvi m,0 ;submit flag is set to false
|
||||
xra a! call select ;on drive a to erase file
|
||||
lxi d,subfcb! call delete
|
||||
lda cdisk! jmp select ;back to original drive
|
||||
;
|
||||
serialize:
|
||||
;check serialization
|
||||
lxi d,serial! lxi h,bdosl! mvi b,6 ;check six bytes
|
||||
ser0: ldax d! cmp m! jnz badserial
|
||||
inx d! inx h! dcr b! jnz ser0
|
||||
ret ;serial number is ok
|
||||
;
|
||||
comerr:
|
||||
;error in command string starting at position
|
||||
;'staddr' and ending with first delimiter
|
||||
call crlf ;space to next line
|
||||
lhld staddr ;h,l address first to print
|
||||
comerr0: ;print characters until blank or zero
|
||||
mov a,m! cpi ' '! jz comerr1; not blank
|
||||
ora a! jz comerr1; not zero, so print it
|
||||
push h! call printchar! pop h! inx h
|
||||
jmp comerr0; for another character
|
||||
comerr1: ;print question mark,and delete sub file
|
||||
mvi a,'?'! call printchar
|
||||
call crlf! call del$sub
|
||||
jmp ccp ;restart with next command
|
||||
;
|
||||
; fcb scan and fill subroutine (entry is at fillfcb below)
|
||||
;fill the comfcb, indexed by A (0 or 16)
|
||||
;subroutines
|
||||
delim: ;look for a delimiter
|
||||
ldax d! ora a! rz ;not the last element
|
||||
cpi ' '! jc comerr ;non graphic
|
||||
rz ;treat blank as delimiter
|
||||
cpi '='! rz
|
||||
cpi la! rz ;left arrow
|
||||
cpi '.'! rz
|
||||
cpi ':'! rz
|
||||
cpi ';'! rz
|
||||
cpi '<'! rz
|
||||
cpi '>'! rz
|
||||
ret ;delimiter not found
|
||||
;
|
||||
deblank: ;deblank the input line
|
||||
ldax d! ora a! rz ;treat end of line as blank
|
||||
cpi ' '! rnz! inx d! jmp deblank
|
||||
;
|
||||
addh: ;add a to h,l
|
||||
add l! mov l,a! rnc
|
||||
inr h! ret
|
||||
;
|
||||
fillfcb0:
|
||||
;equivalent to fillfcb(0)
|
||||
mvi a,0
|
||||
;
|
||||
fillfcb:
|
||||
lxi h,comfcb! call addh! push h! push h ;fcb rescanned at end
|
||||
xra a! sta sdisk ;clear selected disk (in case A:...)
|
||||
lhld comaddr! xchg ;command address in d,e
|
||||
call deblank ;to first non-blank character
|
||||
xchg! shld staddr ;in case of errors
|
||||
xchg! pop h ;d,e has command, h,l has fcb address
|
||||
;look for preceding file name A: B: ...
|
||||
ldax d! ora a! jz setcur0 ;use current disk if empty command
|
||||
sbi 'A'-1! mov b,a ;disk name held in b if : follows
|
||||
inx d! ldax d! cpi ':'! jz setdsk ;set disk name if :
|
||||
;
|
||||
setcur: ;set current disk
|
||||
dcx d ;back to first character of command
|
||||
setcur0:
|
||||
lda cdisk! mov m,a! jmp setname
|
||||
;
|
||||
setdsk: ;set disk to name in register b
|
||||
mov a,b! sta sdisk ;mark as disk selected
|
||||
mov m,b! inx d ;past the :
|
||||
;
|
||||
setname: ;set the file name field
|
||||
mvi b,8 ;file name length (max)
|
||||
setnam0: call delim! jz padname ;not a delimiter
|
||||
inx h! cpi '*'! jnz setnam1 ;must be ?'s
|
||||
mvi m,'?'! jmp setnam2 ;to dec count
|
||||
;
|
||||
setnam1: mov m,a ;store character to fcb! inx d
|
||||
setnam2: dcr b ;count down length! jnz setnam0
|
||||
;
|
||||
;end of name, truncate remainder
|
||||
trname: call delim! jz setty ;set type field if delimiter
|
||||
inx d! jmp trname
|
||||
;
|
||||
padname: inx h! mvi m,' '! dcr b! jnz padname
|
||||
;
|
||||
setty: ;set the type field
|
||||
mvi b,3! cpi '.'! jnz padty ;skip the type field if no .
|
||||
inx d ;past the ., to the file type field
|
||||
setty0: ;set the field from the command buffer
|
||||
call delim! jz padty! inx h! cpi '*'! jnz setty1
|
||||
mvi m,'?' ;since * specified! jmp setty2
|
||||
;
|
||||
setty1: ;not a *, so copy to type field
|
||||
mov m,a! inx d
|
||||
setty2: ;decrement count and go again
|
||||
dcr b! jnz setty0
|
||||
;
|
||||
;end of type field, truncate
|
||||
trtyp: ;truncate type field
|
||||
call delim! jz efill! inx d! jmp trtyp
|
||||
;
|
||||
padty: ;pad the type field with blanks
|
||||
inx h! mvi m,' '! dcr b! jnz padty
|
||||
;
|
||||
efill: ;end of the filename/filetype fill, save command address
|
||||
;fill the remaining fields for the fcb
|
||||
mvi b,3
|
||||
efill0: inx h! mvi m,0! dcr b! jnz efill0
|
||||
xchg! shld comaddr ;set new starting point
|
||||
;
|
||||
;recover the start address of the fcb and count ?'s
|
||||
pop h! lxi b,11 ;b=0, c=8+3
|
||||
scnq: inx h! mov a,m! cpi '?'! jnz scnq0
|
||||
;? found, count it in b! inr b
|
||||
scnq0: dcr c! jnz scnq
|
||||
;
|
||||
;number of ?'s in c, move to a and return with flags set
|
||||
mov a,b! ora a! ret
|
||||
;
|
||||
intvec:
|
||||
;intrinsic function names (all are four characters)
|
||||
db 'DIR '
|
||||
db 'ERA '
|
||||
db 'TYPE'
|
||||
db 'SAVE'
|
||||
db 'REN '
|
||||
intlen equ ($-intvec)/4 ;intrinsic function length
|
||||
serial: ;db 0,0,0,0,0,0
|
||||
db 14h,0eh,0,0,7h,06dh
|
||||
;
|
||||
;
|
||||
intrinsic:
|
||||
;look for intrinsic functions (comfcb has been filled)
|
||||
lxi h,intvec! mvi c,0 ;c counts intrinsics as scanned
|
||||
intrin0: mov a,c! cpi intlen ;done with scan?! rnc
|
||||
;no, more to scan
|
||||
lxi d,comfcb+1 ;beginning of name
|
||||
mvi b,4 ;length of match is in b
|
||||
intrin1: ldax d! cmp m ;match?
|
||||
jnz intrin2 ;skip if no match
|
||||
inx d! inx h! dcr b
|
||||
jnz intrin1 ;loop while matching
|
||||
;
|
||||
;complete match on name, check for blank in fcb
|
||||
ldax d! cpi ' '! jnz intrin3 ;otherwise matched
|
||||
mov a,c! ret ;with intrinsic number in a
|
||||
;
|
||||
intrin2: ;mismatch, move to end of intrinsic
|
||||
inx h! dcr b! jnz intrin2
|
||||
;
|
||||
intrin3: ;try next intrinsic
|
||||
inr c ;to next intrinsic number
|
||||
jmp intrin0 ;for another round
|
||||
intrin4:
|
||||
ora a! jnz intrin5! lxi d,subfcb! call open
|
||||
jz intrin5
|
||||
mvi a, 0ffh! jmp intrin6
|
||||
intrin5:
|
||||
xra a
|
||||
intrin6:
|
||||
sta submit
|
||||
ret
|
||||
;
|
||||
ccpclear:
|
||||
;clear the command buffer
|
||||
xra a
|
||||
sta comlen
|
||||
;drop through to start ccp
|
||||
ccpstart:
|
||||
;enter here from boot loader
|
||||
lxi sp,stack! push b ;save initial disk number
|
||||
;(high order 4bits=user code, low 4bits=disk#)
|
||||
push b! call initialize! pop b
|
||||
push psw! mov a,c! call select
|
||||
pop psw! pop b
|
||||
inr a! ora c
|
||||
call intrin4 ;proper disk is selected, now check sub files
|
||||
;check for initial command
|
||||
lda comlen! ora a! jnz ccp0 ;assume typed already
|
||||
;
|
||||
ccp:
|
||||
;enter here on each command or error condition
|
||||
lxi sp,stack
|
||||
call crlf ;print d> prompt, where d is disk name
|
||||
call cselect ;get current disk number
|
||||
adi 'A'! call printchar
|
||||
mvi a,'>'! call printchar
|
||||
call readcom ;command buffer filled
|
||||
ccp0: ;(enter here from initialization with command full)
|
||||
lxi d,buff! call setdma ;default dma address at buff
|
||||
call cselect! sta cdisk ;current disk number saved
|
||||
call fillfcb0 ;command fcb filled
|
||||
cnz comerr ;the name cannot be an ambiguous reference
|
||||
lda sdisk! ora a! jnz userfunc
|
||||
;check for an intrinsic function
|
||||
call intrinsic
|
||||
lxi h,jmptab ;index is in the accumulator
|
||||
mov e,a! mvi d,0! dad d! dad d ;index in d,e
|
||||
mov a,m! inx h! mov h,m! mov l,a! pchl
|
||||
;pc changes to the proper intrinsic or user function
|
||||
jmptab:
|
||||
dw direct ;directory search
|
||||
dw erase ;file erase
|
||||
dw type ;type file
|
||||
dw save ;save memory image
|
||||
dw rename ;file rename
|
||||
dw userfunc;user-defined function
|
||||
badserial:
|
||||
lxi h,di or (hlt shl 8)
|
||||
shld ccploc! lxi h,ccploc! pchl
|
||||
;
|
||||
;
|
||||
;utility subroutines for intrinsic handlers
|
||||
readerr:
|
||||
;print the read error message
|
||||
lxi b,rdmsg! jmp print
|
||||
rdmsg: db 'READ ERROR',0
|
||||
;
|
||||
nofile:
|
||||
;print no file message
|
||||
lxi b,nofmsg! jmp print
|
||||
nofmsg: db 'NOT FOUND',0
|
||||
;
|
||||
getnumber: ;read a number from the command line
|
||||
;convert the byte value in comfcb to binary
|
||||
lxi h,comfcb+1! lxi b,11 ;(b=0, c=11)
|
||||
;value accumulated in b, c counts name length to zero
|
||||
conv0: mov a,m! cpi ' '! jz conv1
|
||||
;more to scan, convert char to binary and add
|
||||
inx h! sui '0'! cpi 10! jnc comerr ;valid?
|
||||
mov d,a ;save value! mov a,b ;mult by 10
|
||||
ani 1110$0000b! jnz comerr
|
||||
mov a,b ;recover value
|
||||
rlc! rlc! rlc ;*8
|
||||
add b! jc comerr
|
||||
add b! jc comerr ;*8+*2 = *10
|
||||
add d! jc comerr ;+digit
|
||||
mov b,a! dcr c! jnz conv0 ;for another digit
|
||||
ret
|
||||
conv1: ;end of digits, check for all blanks
|
||||
mov a,m! cpi ' '! jnz comerr ;blanks?
|
||||
inx h! dcr c! jnz conv1
|
||||
mov a,b ;recover value! ret
|
||||
;
|
||||
movename:
|
||||
;move 3 characters from h,l to d,e addresses
|
||||
mvi b,3
|
||||
move0: mov a,m! stax d! inx h! inx d
|
||||
dcr b! jnz move0
|
||||
ret
|
||||
;
|
||||
addhcf: ;buff + a + c to h,l followed by fetch
|
||||
lxi h,buff! add c! call addh! mov a,m! ret
|
||||
;
|
||||
setdisk:
|
||||
;change disks for this command, if requested
|
||||
xra a! sta comfcb ;clear disk name from fcb
|
||||
setdisk1:
|
||||
lda sdisk! ora a! rz ;no action if not specified
|
||||
dcr a! lxi h,cdisk! cmp m! rz ;already selected
|
||||
jmp select
|
||||
;
|
||||
resetdisk:
|
||||
;return to original disk after command
|
||||
lda sdisk! ora a! rz ;no action if not selected
|
||||
dcr a! lxi h,cdisk! cmp m! rz ;same disk
|
||||
lda cdisk! jmp select
|
||||
;
|
||||
;individual intrinsics follow
|
||||
direct:
|
||||
;directory search
|
||||
call fillfcb0 ;comfcb gets file name
|
||||
call setdisk ;change disk drives if requested
|
||||
lxi h,comfcb+1! mov a,m ;may be empty request
|
||||
cpi ' '! jnz dir1 ;skip fill of ??? if not blank
|
||||
;set comfcb to all ??? for current disk
|
||||
mvi b,11 ;length of fill ????????.???
|
||||
dir0: mvi m,'?'! inx h! dcr b! jnz dir0
|
||||
;not a blank request, must be in comfcb
|
||||
dir1:
|
||||
call searchcom ;first one has been found
|
||||
cz nofile ;not found message
|
||||
dir2: jz endir
|
||||
;found, but may be system file
|
||||
call crlf! call cselect
|
||||
;current disk in A
|
||||
adi 'A'! call printchar
|
||||
mvi a,':'! call printchar
|
||||
mvi a,' '! call printchar
|
||||
dirhdr1:
|
||||
lda dcnt! rrc! rrc! rrc! ani 60h! mov c,a
|
||||
;compute position of name in buffer
|
||||
mvi b,1 ;start with first character of name
|
||||
dir3: mov a,b! call addhcf ;buff+a+c fetched
|
||||
cpi ' '! jnz dir4 ;check for blank type
|
||||
mvi a,9! call addhcf ;first char of type
|
||||
mvi a,' '! cmp m! jz dir5
|
||||
dir4:
|
||||
call printbc ;char printed
|
||||
inr b! mov a,b! cpi 12! jnc dir5
|
||||
;check for break between names
|
||||
cpi 9! jnz dir3 ;for another char
|
||||
;print a blank between names
|
||||
mvi a,' '! call printbc! jmp dir3
|
||||
;
|
||||
dir5: ;end of current entry
|
||||
dir6: call break$key ;check for interrupt at keyboard
|
||||
jnz endir ;abort directory search
|
||||
call searchn! jmp dir2 ;for another entry
|
||||
endir: ;end of directory scan
|
||||
jmp retcom
|
||||
;
|
||||
;
|
||||
erase:
|
||||
call fillfcb0 ;cannot be all ???'s
|
||||
cpi 11
|
||||
jnz getout
|
||||
;erasing all of the disk
|
||||
lxi b,ermsg! call print!
|
||||
call readcom
|
||||
lxi h,comlen! dcr m! jnz ccp ;bad input
|
||||
inx h! mov a,m! cpi 'Y'! jnz ccp
|
||||
;ok, erase the entire diskette
|
||||
inx h! shld comaddr ;otherwise error at retcom
|
||||
erasefile:
|
||||
lxi h, comfcb! mvi m,'?'
|
||||
call setdisk1
|
||||
lxi d,comfcb! call delete
|
||||
call initialize! jmp retcom
|
||||
getout:
|
||||
call setdisk
|
||||
lxi d, comfcb! call delete
|
||||
jmp retcom
|
||||
;
|
||||
ermsg: db 'ALL FILES (Y/N)?',0
|
||||
;
|
||||
type:
|
||||
call fillfcb0! jnz comerr ;don't allow ?'s in file name
|
||||
call setdisk! call openc ;open the file
|
||||
jz typerr ;zero flag indicates not found
|
||||
;file opened, read 'til eof
|
||||
call crlf! lxi h,bptr! mvi m,255 ;read first buffer
|
||||
type0: ;loop on bptr
|
||||
lxi h,bptr! mov a,m! cpi 128 ;end buffer
|
||||
jc type1! push h ;carry if 0,1,...,127
|
||||
;read another buffer full
|
||||
call diskreadc! pop h ;recover address of bptr
|
||||
jnz typeof ;hard end of file
|
||||
xra a! mov m,a ;bptr = 0
|
||||
type1: ;read character at bptr and print
|
||||
inr m ;bptr = bptr + 1
|
||||
lxi h,buff! call addh ;h,l addresses char
|
||||
mov a,m! cpi eofile! jz retcom
|
||||
call printchar
|
||||
call break$key! jnz retcom ;abort if break
|
||||
jmp type0 ;for another character
|
||||
;
|
||||
typeof: ;end of file, check for errors
|
||||
dcr a! jz retcom
|
||||
call readerr
|
||||
typerr: call resetdisk! jmp comerr
|
||||
;
|
||||
save:
|
||||
call fillfcb0! lda sdisk! ora a! jnz comerr
|
||||
call getnumber; value to register a
|
||||
push psw ;save it for later
|
||||
;
|
||||
;should be followed by a file to save the memory image
|
||||
call fillfcb0
|
||||
jnz comerr ;cannot be ambiguous
|
||||
call setdisk ;may be a disk change
|
||||
lxi d,comfcb! push d! call delete ;existing file removed
|
||||
pop d! call make ;create a new file on disk
|
||||
call openc! jz save1
|
||||
pop psw ;#pages to write is in a, change to #sectors
|
||||
mov l,a! mvi h,0! dad h!
|
||||
lxi d,tran ;h,l is sector count, d,e is load address
|
||||
save0: ;check for sector count zero
|
||||
mov a,h! ora l! jz save2 ;may be completed
|
||||
dcx h ;sector count = sector count - 1
|
||||
push h ;save it for next time around
|
||||
lxi h,128! dad d! push h ;next dma address saved
|
||||
call setdma ;current dma address set
|
||||
lxi d,comfcb! call diskwrite
|
||||
pop d! pop h ;dma address, sector count
|
||||
jnz save1 ;may be disk full case
|
||||
jmp save0 ;for another sector
|
||||
;
|
||||
save1: ;end of dump, close the file
|
||||
lxi b, fullmsg! call print
|
||||
save2:
|
||||
lxi d,comfcb! call close
|
||||
jnz retsave ;for another command
|
||||
saverr: ;must be full or read only disk
|
||||
lxi b,closemsg! call print
|
||||
retsave:
|
||||
jmp retcom
|
||||
fullmsg: db 'NO SPACE',0
|
||||
;
|
||||
closemsg: db 'CANNOT CLOSE',0
|
||||
;
|
||||
rename:
|
||||
;rename a file on a specific disk
|
||||
call fillfcb0! jnz comerr ;must be unambiguous
|
||||
lda sdisk! push psw ;save for later compare
|
||||
call setdisk ;disk selected
|
||||
call searchcom ;is new name already there?
|
||||
jnz renerr3
|
||||
;file doesn't exist, move to second half of fcb
|
||||
lxi h,comfcb! lxi d,comfcb+16! mvi b,16! call move0
|
||||
;check for = or left arrow
|
||||
lhld comaddr! xchg! call deblank
|
||||
cpi '='! jz ren1 ;ok if =
|
||||
cpi la! jnz renerr2
|
||||
ren1: xchg! inx h! shld comaddr ;past delimiter
|
||||
;proper delimiter found
|
||||
call fillfcb0! jnz renerr2
|
||||
;check for drive conflict
|
||||
pop psw! mov b,a ;previous drive number
|
||||
lxi h,sdisk! mov a,m! ora a! jz ren2
|
||||
;drive name was specified. same one?
|
||||
cmp b! mov m,b! jnz renerr2
|
||||
ren2: mov m,b ;store the name in case drives switched
|
||||
xra a! sta comfcb! call searchcom ;is old file there?
|
||||
jz renerr1
|
||||
;
|
||||
;everything is ok, rename the file
|
||||
lxi d,comfcb! call renam
|
||||
jmp retcom
|
||||
;
|
||||
renerr1:; no file on disk
|
||||
call nofile! jmp retcom
|
||||
renerr2:; ambigous reference/name conflict
|
||||
call resetdisk! jmp comerr
|
||||
renerr3:; file already exists
|
||||
lxi b,renmsg! call print! jmp retcom
|
||||
renmsg: db 'FILE EXISTS',0
|
||||
;
|
||||
;
|
||||
userfunc:
|
||||
call serialize ;check serialization
|
||||
;load user function and set up for execution
|
||||
lda comfcb+1! cpi ' '! jnz user0
|
||||
;no file name, but may be disk switch
|
||||
lda sdisk! ora a! jz endcom ;no disk name if 0
|
||||
dcr a! sta cdisk! ;call setdiska ;set user/disk
|
||||
sta diska
|
||||
call select! jmp endcom
|
||||
user0: ;file name is present
|
||||
lxi d,comfcb+9! ldax d! cpi ' '! jnz comerr ;type ' '
|
||||
push d! call setdisk! pop d! lxi h,comtype ;.com
|
||||
call movename ;file type is set to .com
|
||||
call openc! jz userer
|
||||
;file opened properly, read it into memory
|
||||
lxi h,tran ;transient program base
|
||||
load0: push h ;save dma address
|
||||
xchg! call setdma
|
||||
lxi d,comfcb! call diskread! jnz load1
|
||||
;sector loaded, set new dma address and compare
|
||||
pop h! lxi d,128! dad d
|
||||
lxi d,tranm ;has the load overflowed?
|
||||
mov a,l! sub e! mov a,h! sbb d! jnc loaderr
|
||||
jmp load0 ;for another sector
|
||||
;
|
||||
load1: pop h! dcr a! jnz loaderr ;end file is 1
|
||||
call resetdisk ;back to original disk
|
||||
call fillfcb0! lxi h,sdisk! push h
|
||||
mov a,m! sta comfcb ;drive number set
|
||||
mvi a,16! call fillfcb ;move entire fcb to memory
|
||||
pop h! mov a,m! sta comfcb+16
|
||||
xra a! sta comrec ;record number set to zero
|
||||
lxi d,fcb! lxi h,comfcb! mvi b,33! call move0
|
||||
;move command line to buff
|
||||
lxi h,combuf
|
||||
bmove0: mov a,m! ora a! jz bmove1! cpi ' '! jz bmove1
|
||||
inx h! jmp bmove0 ;for another scan
|
||||
;first blank position found
|
||||
bmove1: mvi b,0! lxi d,buff+1! ;ready for the move
|
||||
bmove2: mov a,m! stax d! ora a! jz bmove3
|
||||
;more to move
|
||||
inr b! inx h! inx d! jmp bmove2
|
||||
bmove3: ;b has character count
|
||||
mov a,b! sta buff
|
||||
call crlf
|
||||
;now go to the loaded program
|
||||
lda cdisk! sta diska! call clifter! lxi d, buff
|
||||
call setdma ;user code saved
|
||||
;low memory diska contains user code
|
||||
call tran ;gone to the loaded program
|
||||
lxi sp,stack ;may come back here
|
||||
lda cdisk! call select
|
||||
jmp ccp
|
||||
;
|
||||
userer: ;arrive here on command error
|
||||
call resetdisk! jmp comerr
|
||||
;
|
||||
loaderr:;cannot load the program
|
||||
lxi b,loadmsg! call print
|
||||
jmp retcom
|
||||
loadmsg: db 'LOAD ERROR',0
|
||||
comtype: db 'COM' ;for com files
|
||||
;
|
||||
;
|
||||
retcom: ;reset disk before end of command check
|
||||
call resetdisk
|
||||
;
|
||||
endcom: ;end of intrinsic command
|
||||
call fillfcb0 ;to check for garbage at end of line
|
||||
lda comfcb+1! sui ' '! lxi h,sdisk! ora m
|
||||
;0 in accumulator if no disk selected, and blank fcb
|
||||
jnz comerr
|
||||
jmp ccp
|
||||
;
|
||||
;
|
||||
;
|
||||
; data areas
|
||||
ds 16 ;8 level stack
|
||||
stack:
|
||||
;
|
||||
; 'submit' file control block
|
||||
submit: db 0 ;00 if no submit file, ff if submitting
|
||||
subfcb: db 0,'$$$ ' ;file name is $$$
|
||||
db 'SUB',0,0 ;file type is sub
|
||||
submod: db 0 ;module number
|
||||
subrc: ds 1 ;record count filed
|
||||
ds 16 ;disk map
|
||||
subcr: ds 1 ;current record to read
|
||||
;
|
||||
; command file control block
|
||||
comfcb: ds 32 ;fields filled in later
|
||||
comrec: ds 1 ;current record to read/write
|
||||
dcnt: ds 1 ;disk directory count (used for error codes)
|
||||
cdisk: ds 1 ;current disk
|
||||
sdisk: ds 1 ;selected disk for current operation
|
||||
;none=0, a=1, b=2 ...
|
||||
bptr: ds 1 ;buffer pointer
|
||||
|
||||
ds 0eh
|
||||
ds 38
|
||||
|
||||
end ccploc
|
||||
BIN
CONTRIBUTIONS/cpm1.4/doc/Reconstruct CPM 1.4.doc
Normal file
BIN
CONTRIBUTIONS/cpm1.4/doc/Reconstruct CPM 1.4.doc
Normal file
Binary file not shown.
1
CONTRIBUTIONS/cpm1.4/readme.md
Normal file
1
CONTRIBUTIONS/cpm1.4/readme.md
Normal file
@@ -0,0 +1 @@
|
||||
Make CPM 1.4 v 0.1: by Ernie Price. Make CPM 1.4 is a howto on reconstucting the CPM Version 1.4 CCP and BDOS from recovered and regenerated sources.
|
||||
5
CONTRIBUTIONS/cpm1.4/tgtbin/dbg.bat
Normal file
5
CONTRIBUTIONS/cpm1.4/tgtbin/dbg.bat
Normal file
@@ -0,0 +1,5 @@
|
||||
rcx
|
||||
800
|
||||
nccp.bin
|
||||
wa00
|
||||
q
|
||||
BIN
CONTRIBUTIONS/cpm1.4/tools/EXTRACT.EXE
Normal file
BIN
CONTRIBUTIONS/cpm1.4/tools/EXTRACT.EXE
Normal file
Binary file not shown.
BIN
CONTRIBUTIONS/cpm1.4/tools/HEXCON.EXE
Normal file
BIN
CONTRIBUTIONS/cpm1.4/tools/HEXCON.EXE
Normal file
Binary file not shown.
253
CONTRIBUTIONS/cpm1.4/tools/extract.c
Normal file
253
CONTRIBUTIONS/cpm1.4/tools/extract.c
Normal file
@@ -0,0 +1,253 @@
|
||||
/* #include <conio.h> */
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
struct fcb // also directory entry
|
||||
{
|
||||
unsigned char user; // not used in CP/M 1.4
|
||||
unsigned char name[8]; // file name
|
||||
unsigned char type[3]; // file extension
|
||||
unsigned char ext; // extent number
|
||||
unsigned char notused[2];
|
||||
unsigned char rc; // record count in this extent
|
||||
unsigned char dm[16]; // single byte extent numbers
|
||||
};
|
||||
|
||||
#define DIR_ENT_SZ 32 // size of one directory entry
|
||||
#define SECT_SZ 128 // disk sector size
|
||||
#define SPB 8 // sectors per block
|
||||
#define DIR_BLOCKS 2 // 2 blocks reserved for directory
|
||||
#define SPT 26 // sectors per track
|
||||
#define RESERVED_TRACKS 2 // for cold boot, ccp and cpm
|
||||
|
||||
#define DIR_SECTORS (SPB * DIR_BLOCKS) // sectors for directory
|
||||
#define DIR_LOCN (SPT * RESERVED_TRACKS) // offset of directory
|
||||
#define DIR_ENT_PER_SEC (SECT_SZ / DIR_ENT_SZ) // directory entries per sector
|
||||
|
||||
unsigned char buff1[SPT * SECT_SZ]; // for de-interleaving
|
||||
unsigned char buff2[SPT * SECT_SZ]; // "
|
||||
|
||||
unsigned char fsec [SECT_SZ];
|
||||
char fname [20]; // file name for current extent
|
||||
char fname2[20]; // currently open output file name, if any
|
||||
|
||||
FILE *in, // raw image file
|
||||
*out; // extracted CP/M file
|
||||
|
||||
unsigned char exm[1024]; // map of processed blocks
|
||||
|
||||
// sector interleave table
|
||||
unsigned char sectran[] = {
|
||||
"\x01\x07\x0d\x13\x19\x05\x0b\x11"
|
||||
"\x17\x03\x09\x0f\x15\x02\x08\x0e"
|
||||
"\x14\x1a\x06\x0c\x12\x18\x04\x0a"
|
||||
"\x10\x16\x00\x00\x00\x00\x00\x00"};
|
||||
|
||||
char temp[] = "junkimg.jnk"; // name of de-interleaved raw image file
|
||||
|
||||
/**************************************************
|
||||
|
||||
Read the input image file in gulps of 26 sectors, reorder using the
|
||||
interleace table, and save the to the de-interleaved file
|
||||
|
||||
*/
|
||||
void RemoveInterleave
|
||||
(
|
||||
char *fn1, // ram image file name
|
||||
char * fn2, // reordered image file name
|
||||
int flag // not used
|
||||
)
|
||||
{
|
||||
int x;
|
||||
|
||||
in = fopen(fn1, "rb");
|
||||
out = fopen(fn2, "wb");
|
||||
|
||||
while (fread(buff1, 1, sizeof(buff1), in)) // read a 'track'
|
||||
{
|
||||
for (x = 0; x < SPT; x++) // shuffle
|
||||
{
|
||||
memcpy(&buff2[x * SECT_SZ], &buff1[(sectran[x] - 1) * SECT_SZ], SECT_SZ);
|
||||
}
|
||||
fwrite(buff2, 1, sizeof(buff2), out);
|
||||
}
|
||||
fcloseall();
|
||||
}
|
||||
|
||||
/**************************************************
|
||||
|
||||
Copy a file name or extension until a space character is found
|
||||
or limit is reached.
|
||||
|
||||
*/
|
||||
void copyuntil
|
||||
(
|
||||
char *dst, // destination for copy
|
||||
char *src, // source for copy
|
||||
int count // max copy length
|
||||
)
|
||||
{
|
||||
while (*src != ' ' && count--)
|
||||
{
|
||||
*dst++ = *src++;
|
||||
}
|
||||
*dst = 0;
|
||||
}
|
||||
|
||||
/**************************************************
|
||||
|
||||
Convert an 8,3 filename into name.ext format
|
||||
|
||||
*/
|
||||
void MakeFilename
|
||||
(
|
||||
char *fname, // destination for file name
|
||||
struct fcb *tfcb // pointer to fcb (directory entry)
|
||||
)
|
||||
{
|
||||
copyuntil(fname, tfcb->name, 8); // copy name part
|
||||
|
||||
if (tfcb->type[0] != ' ') // if extension is non-blank
|
||||
{
|
||||
strcat(fname, "."); // add a dot
|
||||
copyuntil(fname + strlen(fname), tfcb->type, 3);
|
||||
}
|
||||
}
|
||||
/**************************************************
|
||||
|
||||
Read a logical CP/M sector from the image file. Sector is a logical
|
||||
number based at the directory root. The reserved sectors(tracks)
|
||||
are added.
|
||||
|
||||
*/
|
||||
void ReadSector
|
||||
(
|
||||
unsigned char *where, // destination for read
|
||||
int sectorno // logical sector number
|
||||
)
|
||||
{
|
||||
fseek(in, (long)(DIR_LOCN + sectorno) * (long)SECT_SZ, SEEK_SET);
|
||||
fread(where, 1, SECT_SZ, in);
|
||||
}
|
||||
|
||||
/**************************************************
|
||||
|
||||
Construct the file name and copy all records in the specified extent into the
|
||||
output file. If the file name differs from the previously processed file name
|
||||
close the previous file and open a new one with the current name.
|
||||
Since extents are psorecces in sorted order, including the extent number, this
|
||||
results in recovered files.
|
||||
|
||||
|
||||
*/
|
||||
void CopyExtentToFile
|
||||
(
|
||||
struct fcb *tfcb, // pointer to fcb (directory entry)
|
||||
int deleted // flag indicating a deleted extent
|
||||
)
|
||||
{
|
||||
int x, // general counter
|
||||
thisSect, // computed logical sector number
|
||||
said; // flag for printing
|
||||
static int extent; // logical extent number
|
||||
|
||||
MakeFilename(fname, tfcb); // extract filename
|
||||
if (strcmp(fname2, fname)) // is it different from before?
|
||||
{
|
||||
if (out != NULL)
|
||||
{
|
||||
fclose(out);
|
||||
}
|
||||
out = fopen(fname, "wb"); // create new output file
|
||||
strcpy(fname2, fname);
|
||||
printf("Extracting %sfile %s\n", deleted ? "deleted " : "", fname);
|
||||
extent = -1;
|
||||
}
|
||||
extent++;
|
||||
|
||||
//printf("%15s", fname);
|
||||
for (x = 0; x < tfcb->rc; x++) // fetch record count sectors and save
|
||||
{
|
||||
if (!(x % SPB))
|
||||
{
|
||||
//printf(" %02x", tfcb->dm[x / SPB]);
|
||||
}
|
||||
if ((exm[tfcb->dm[x / SPB]] != 0) && !said)
|
||||
{
|
||||
said = 1;
|
||||
printf(" extent %d has blocks used elsewhere\n");
|
||||
}
|
||||
exm[tfcb->dm[x / SPB]] = 1; // mark in block map
|
||||
|
||||
// convert logical sector to physical
|
||||
thisSect = (tfcb->dm[x / SPB] * SPB) + (x % SPB);
|
||||
|
||||
ReadSector(fsec, thisSect); // load the file sector
|
||||
fwrite(fsec, 1, SECT_SZ, out); // store in new file
|
||||
}
|
||||
//printf("\n");
|
||||
}
|
||||
|
||||
/**************************************************
|
||||
|
||||
Compare routine for qsort of directory
|
||||
|
||||
*/
|
||||
int compare
|
||||
(
|
||||
void *a, // pointer to key field 'a'
|
||||
void *b // pointer to key field 'b'
|
||||
)
|
||||
{
|
||||
return memcmp(a, b, 16); // compare keys
|
||||
}
|
||||
|
||||
/**************************************************
|
||||
*/
|
||||
main
|
||||
(
|
||||
int argc, // count of command line arguments
|
||||
char **argv // pointers to arguments
|
||||
)
|
||||
{
|
||||
int x;
|
||||
struct fcb *tfcb = (struct fcb *)buff1;
|
||||
|
||||
//printf("Dir locn %x\n", DIR_LOCN * SECT_SZ);
|
||||
|
||||
if (argc < 2)
|
||||
{
|
||||
printf("File name required for compare\n\n");
|
||||
printf("Correct usage is:\n");
|
||||
printf(" extract <filename>\n\n");
|
||||
printf(" where <filename> is a diskette image file\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
RemoveInterleave(argv[1], temp, 0);
|
||||
|
||||
in = fopen(temp, "rb");
|
||||
|
||||
// load the directory into memory
|
||||
for (x = 0; x < DIR_SECTORS; x++)
|
||||
{
|
||||
ReadSector(buff1 + (x * SECT_SZ), x);
|
||||
}
|
||||
|
||||
// sort the directory in [user], name and extent order
|
||||
qsort(buff1, DIR_SECTORS, DIR_ENT_SZ, compare);
|
||||
|
||||
// process the sorted list
|
||||
for (x = 0; x < (DIR_SECTORS * DIR_ENT_PER_SEC); x++)
|
||||
{
|
||||
if ((tfcb->name[0] != 0xe5) && (tfcb->name != 0))
|
||||
{
|
||||
CopyExtentToFile(tfcb, tfcb->user == 0xe5);
|
||||
}
|
||||
tfcb++;
|
||||
}
|
||||
fcloseall();
|
||||
remove(temp);
|
||||
}
|
||||
3
CONTRIBUTIONS/cpm1.4/tools/getbdos.bat
Normal file
3
CONTRIBUTIONS/cpm1.4/tools/getbdos.bat
Normal file
@@ -0,0 +1,3 @@
|
||||
@echo off
|
||||
rem extract the BDOS image from cpm.com
|
||||
call ..\tools\getcpm ..\binaries\cpm.com bdos.bin 1200 d00
|
||||
4
CONTRIBUTIONS/cpm1.4/tools/getccp.bat
Normal file
4
CONTRIBUTIONS/cpm1.4/tools/getccp.bat
Normal file
@@ -0,0 +1,4 @@
|
||||
@echo off
|
||||
rem extract the CCP image from cpm.com
|
||||
call ..\tools\getcpm ..\binaries\cpm.com ccp.bin a00 800
|
||||
|
||||
10
CONTRIBUTIONS/cpm1.4/tools/getcpm.bat
Normal file
10
CONTRIBUTIONS/cpm1.4/tools/getcpm.bat
Normal file
@@ -0,0 +1,10 @@
|
||||
@echo off
|
||||
rem #1 = inputfn, #2 outputfn #3 = addr, #4 = lth
|
||||
rem build a script for debug
|
||||
echo rcx>dbg.bat
|
||||
echo %4>>dbg.bat
|
||||
echo n%2>>dbg.bat
|
||||
echo w%3>>dbg.bat
|
||||
echo q>>dbg.bat
|
||||
call debug %1 <dbg.bat
|
||||
|
||||
176
CONTRIBUTIONS/cpm1.4/tools/hexcon.c
Normal file
176
CONTRIBUTIONS/cpm1.4/tools/hexcon.c
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
|
||||
Intel hex to binary converter Converts the specified file to binary
|
||||
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <conio.h>
|
||||
#include <assert.h>
|
||||
|
||||
typedef struct image_t
|
||||
{
|
||||
unsigned lowaddress, highaddress;
|
||||
} image_t;
|
||||
|
||||
//void LoadHexFile(char *fn, unsigned char *where, image_t *im);
|
||||
|
||||
|
||||
#define LINE_SIZE 500 // max size of input hex line
|
||||
#define MAX_IMG_SZ 0x4000
|
||||
|
||||
// Intel hex-file record types
|
||||
#define RT_DATA 0
|
||||
#define RT_EOF 1
|
||||
|
||||
static int hexsum;
|
||||
static unsigned char *lyne;
|
||||
|
||||
/******************************************************************************
|
||||
|
||||
Get a hexadecimal nybble from the input stream. Increment the pointer.
|
||||
|
||||
*/
|
||||
static int getnib(char **bt)
|
||||
{
|
||||
int x = **bt - '0';
|
||||
|
||||
if (x > 9)
|
||||
{
|
||||
x -= 7;
|
||||
}
|
||||
(*bt)++;
|
||||
return x;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
|
||||
Get a single unsigned byte (2 nibbles) from the input stream. Increment the
|
||||
pointer and add the fetched byte to the line checksum
|
||||
|
||||
*/
|
||||
static unsigned long gethex(char **bt)
|
||||
{
|
||||
int x;
|
||||
|
||||
x = getnib(bt) << 4;
|
||||
x |= getnib(bt);
|
||||
hexsum += x;
|
||||
return x;
|
||||
}
|
||||
/******************************************************************************
|
||||
|
||||
Get an unsigned short from the input stream, The pointer and the line checksum
|
||||
are maintained.
|
||||
|
||||
*/
|
||||
static unsigned long getshort(char **bt)
|
||||
{
|
||||
unsigned short ret;
|
||||
|
||||
ret = gethex(bt) << 8;
|
||||
ret |= gethex(bt);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
|
||||
Load the specified Intel format hex file into memory as a binary
|
||||
image. Returns the file loaded in 'prog' with the base and limit filled in
|
||||
as im->lowaddress and im->highaddress respectively.
|
||||
|
||||
*/
|
||||
void LoadHexFile(char *fn, unsigned char *prog, image_t *im, unsigned limit)
|
||||
{
|
||||
char *p1; // pointer to hex input text
|
||||
FILE *ff = fopen(fn, "rb");
|
||||
int lth; // line length byte
|
||||
unsigned address, // current line address
|
||||
offset; // 0 offset of first byte
|
||||
int rt; // record type
|
||||
unsigned char kh; // fetched byte from hex file
|
||||
|
||||
printf("Converting %s\n", fn);
|
||||
|
||||
lyne = (unsigned char*)malloc(LINE_SIZE);
|
||||
|
||||
im->lowaddress = 0xffff;
|
||||
im->highaddress = 0;
|
||||
|
||||
while (fgets(p1 = lyne, LINE_SIZE, ff))
|
||||
{
|
||||
if (*p1++ != ':') // all good Intel hex file have this
|
||||
{
|
||||
break;
|
||||
}
|
||||
hexsum = 0;
|
||||
lth = gethex(&p1); // fetch length of line
|
||||
address = getshort(&p1); // fetch target address
|
||||
|
||||
if ((rt = gethex(&p1)) == RT_EOF) // fetch record type
|
||||
{
|
||||
break; // end of file
|
||||
}
|
||||
switch(rt)
|
||||
{
|
||||
default:
|
||||
printf("Invalid record type %02x\n", rt);
|
||||
getch();
|
||||
break;
|
||||
|
||||
case RT_DATA: // data bytes
|
||||
if (address < im->lowaddress)
|
||||
{
|
||||
assert(im->highaddress == 0);
|
||||
im->lowaddress = address;
|
||||
offset = address;
|
||||
}
|
||||
|
||||
while (lth--)
|
||||
{
|
||||
kh = (unsigned char)gethex(&p1);
|
||||
if ((address - offset) > limit)
|
||||
{
|
||||
printf("Hex file too large\n");
|
||||
getch();
|
||||
exit(0);
|
||||
}
|
||||
*(prog + address++ - offset) = kh;
|
||||
}
|
||||
if (address > im->highaddress)
|
||||
{
|
||||
im->highaddress = address;
|
||||
}
|
||||
break;
|
||||
}
|
||||
kh = (unsigned char)gethex(&p1);
|
||||
if (hexsum & 0xff)
|
||||
{
|
||||
printf("Hex conversion error\n");
|
||||
getch();
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
free(lyne);
|
||||
fclose(ff);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
|
||||
Convert the specified Intel format hex file <argv[1]> into memory as a binary
|
||||
image. Saves the binary image as <argv[2]>.
|
||||
|
||||
*/
|
||||
void main(int argc, char **argv)
|
||||
{
|
||||
image_t img;
|
||||
FILE *out;
|
||||
|
||||
char *where = (char*)malloc(MAX_IMG_SZ);
|
||||
|
||||
memset(where, 0, MAX_IMG_SZ);
|
||||
LoadHexFile(argv[1], where, &img, MAX_IMG_SZ);
|
||||
out = fopen(argv[2], "wb");
|
||||
fwrite(where, 1, img.highaddress - img.lowaddress, out);
|
||||
fclose(out);
|
||||
}
|
||||
17
CONTRIBUTIONS/cpm1.4/tools/makebdos.bat
Normal file
17
CONTRIBUTIONS/cpm1.4/tools/makebdos.bat
Normal file
@@ -0,0 +1,17 @@
|
||||
set :F0:=..\isis\plm80
|
||||
set :F1:=..\isis\utils
|
||||
set :F2:=.
|
||||
set :F3:=..\isis\asm80
|
||||
..\isis\utils\isis :F0:plm80 :F2:bdos.plm
|
||||
..\isis\utils\isis :F3:asm80 :F2:bdosi.src
|
||||
..\isis\utils\isis :F1:link :F2:bdosi.obj,:F2:bdos.obj,:F1:plm80.lib to :F2:bdos.mod
|
||||
del bdosi.obj
|
||||
del bdos.obj
|
||||
..\isis\utils\isis :F1:locate :F2:bdos.mod code(0800h) stacksize(0)
|
||||
del bdos.mod
|
||||
..\isis\utils\isis :F1:objhex :F2:bdos to :F2:bdos.hex
|
||||
del bdos
|
||||
..\tools\hexcon bdos.hex bdos.abs
|
||||
del bdos.hex
|
||||
|
||||
|
||||
7
CONTRIBUTIONS/cpm1.4/tools/makeccp.bat
Normal file
7
CONTRIBUTIONS/cpm1.4/tools/makeccp.bat
Normal file
@@ -0,0 +1,7 @@
|
||||
@echo off
|
||||
set savepath=%path%
|
||||
set path=..\tools2;%path%
|
||||
rmac os2ccp
|
||||
link os2ccp[l0]
|
||||
set path=%savepath%
|
||||
set savepath=
|
||||
Reference in New Issue
Block a user