home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.barnyard.co.uk
/
2015.02.ftp.barnyard.co.uk.tar
/
ftp.barnyard.co.uk
/
cpm
/
walnut-creek-CDROM
/
CPM
/
BDSC
/
BDSC-1
/
CCC.ASM
< prev
next >
Wrap
Assembly Source File
|
2000-06-30
|
25KB
|
1,192 lines
;
; CCC.ASM (C.CCC) v1.45 11/22/81
;
; NOTE: If you are running under MP/M II, be sure to set the MPM2
; equate to 1.
;
; This is the BDS C run-time package. Normally, it resides at
; the start of the TPA (at address BASE+100h, where BASE is either
; 0000h or 4200h depending on CP/M implementation.) The code
; generated by the compiler ALWAYS sits immediately after the end of
; this run-time package code.
;
; Equate statements in CAPITAL letters may be customized by the
; user in order to change a) the origin of the run-time package,
; and b) the origin of the run-time RAM area. If you will be
; generating code to run in a non-CP/M environment, set the CPM
; equate to zero and make sure to set the ORIGIN, RAM and
; EXITAD equates to fit your custom run-time configuration.
;
; The "lxi sp,0" instruction at the start is replaced by the sequence:
;
; lhld base+6
; sphl
;
; by CLINK at link time, unless the -t option is used with CLINK,
; in which case the "lxi sp" remains there and the value used to
; initialize the SP is the argument given to the "-t" option.
;
page 76
title 'BDS C Run-Time Module (c.ccc) v1.45 11/22/81'
CPM: EQU 1 ;True if to be run under CP/M or MP/M
MPM2: EQU 0 ;True ONLY if running under MP/M II
DMAVIO: EQU 1 ;True if using DMA video library routines and
;need parameters initialized
IF CPM
base: equ 0 ;start of ram in system (either 0 or 4200h for CP/M)
bdos: equ base+5 ;rest of these used by CP/M-based configurations.
tpa: equ base+100h
nfcbs: equ 8 ;maximum # of files open at one time
tbuff: equ base+80h
origin: equ tpa
exitad: equ base ;warm boot location
ENDIF
IF NOT CPM ;fill in the appropriate values...
ORIGIN: EQU NEWBASE ;Address at which programs are to run
RAM: EQU WHATEVER ;Read-write memory area for non-CP/M configurations
; (defaults to immediately after C.CCC under CP/M)
EXITAD: EQU WHENDONE ;where to go when done executing
ENDIF
;
; The location of the jump vectors and utility routines must remain
; constant relative to the beginning of this run-time module.
;
; Do NOT change ANYTHING between here and the start of the
; "init" routine!!!!!!!!
;
org origin
lxi sp,0 ;this is changed by CLINK to lhld base+5h
nop ;this first is usually turned into sphl by CLINK
nop! nop ;Simple initialization or patches may be
nop! nop! nop ;inserted here, but better to do all that
nop! nop! nop ;in the "init" routine
call init ;do ARGC & ARGV processing, plus misc. initializations
call main ;go crunch!!!!
jmp vexit ;close open files and reboot
extrns: ds 2 ;set by CLINK to external data base address
cccsiz: dw main-origin ;size of this code (for use by CLINK)
codend: ds 2 ;set by CLINK to (last addr of code + 1)
freram: ds 2 ;set by CLINK to (last addr of externals + 1)
;
; Jump vectors to some file i/o utility routines:
;
error: jmp verror ;loads -1 into HL and returns
exit: jmp vexit ;close all open files and reboot
IF CPM
close: jmp vclose ;close a file
setfcb: jmp vsetfcb ;set up fcb at HL given filename at DE
fgfd: jmp vfgfd ;return C set if file fd in A not open
fgfcb: jmp vfgfcb ;compute address of internal fcb for fd in A
ENDIF
IF NOT CPM ;if not under CP/M, file I/O routines
close: jmp verror ;are not used.
setfcb: jmp verror
fgfd: jmp verror
fgfcb: jmp verror
ENDIF
ds 16 ;reserved
IF CPM
setfcb3: mov m,a ;this is a patch from the "vsetfcb" routine,
inx h ;which causes the random record bytes of the
mov m,a ;fcb being initialized to be zeroed. (Former
inx h ;versions had a "ds 30" above, so this keeps
mov m,a ;all the addresses consistent between this
pop d ;and earlier 1.4's)
pop b
ret
patchnm: call setnm ;another patch from "vsetfcb"
jmp setnm3
ENDIF
IF NOT CPM
ds 14 ;keep addresses the same for non-CP/M implementations
ENDIF
;
; The following routines fetch a variable value from either
; the local stack frame or the external area, given the relative
; offset of the datum required immediately following the call;
; for the "long displacement" routines, the offset must be 16 bits,
; for the "short displacement" routines, the offset must be 8 bits.
;
;
; long-displacement, double-byte external indirection:
;
; format: call ldei ; get 16-bit value in HL
; dw offset_from_extrns ; >= 256
;
ldei: pop h ;get address of offset
mov e,m ;put offset in DE
inx h
mov d,m
inx h
push h ;save return address
lhld extrns ;add offset to external area base
dad d
mov a,m ;and get the value into HL
inx h
mov h,m
mov l,a
ret
;
; short-displacement, double-byte external indirection:
;
; format: call sdei ; get 16-bit value in L
; db offset_from_extrns ; < 256
;
sdei: pop h
mov e,m
inx h
push h
mvi d,0
lhld extrns
dad d
mov a,m
inx h
mov h,m
mov l,a
ret
;
; long-displacement, single-byte external indirection:
;
; format: call lsei ; get 8-bit value in L
; dw offset_from_extrns ; >= 256
;
lsei: pop h
mov e,m
inx h
mov d,m
inx h
push h
lhld extrns
dad d
mov l,m
ret
;
; short-displacement, single-byte external indirection:
;
; format: call ssei ; get 8-bit value in L
; db offset_from_externs ; < 256
;
ssei: pop h
mov e,m
inx h
push h
mvi d,0
lhld extrns
dad d
mov l,m
ret
;
; long-displacement, double-byte local indirection:
;
; format: call ldli ; get 16-bit value in HL
; dw offset_from_BC ; >= 256
;
ldli: pop h
mov e,m
inx h
mov d,m
inx h
push h
xchg
dad b
mov a,m
inx h
mov h,m
mov l,a
ret
;
; short-displacement, double-byte local indirection:
;
; format: call sdli ; get 16-bit value in HL
; db offset_from_BC ; < 256
;
sdli: pop h
mov e,m
inx h
push h
xchg
mvi h,0
dad b
mov a,m
inx h
mov h,m
mov l,a
ret
;
; Flag conversion routines:
;
pzinh: lxi h,1 ;return HL = true if Z set
rz
dcx h
ret
pnzinh: lxi h,0 ;return HL = false if Z set
rz
inx h
ret
pcinh: lxi h,1 ;return HL = true if C set
rc
dcx h
ret
pncinh: lxi h,0 ;return HL = false if C set
rc
inx h
ret
ppinh: lxi h,1 ;return HL = true if P (plus) flag set
rp
dcx h
ret
pminh: lxi h,1 ;return HL = true if M (minus) flag set
rm
dcx h
ret
pzind: lxi d,1 ;return DE = true if Z set
rz
dcx d
ret
pnzind: lxi d,0 ;return DE = false if Z set
rz
inx d
ret
pcind: lxi d,1 ;return DE = true if C set
rc
dcx d
ret
pncind: lxi d,0 ;return DE = false if C set
rc
inx d
ret
ppind: lxi d,1 ;return DE = true if P (plus) flag set
rp
dcx d
ret
pmind: lxi d,1 ;return DE = true if M (minus) flag set
rm
dcx d
ret
;
; Relational operator routines: take args in DE and HL,
; and return a flag bit either set or reset.
;
; ==, >, < :
;
eqwel: mov a,l ;return Z if HL == DE, else NZ
cmp e
rnz ;if L <> E, then HL <> DE
mov a,h ;else HL == DE only if H == D
cmp d
ret
blau: xchg ;return C if HL < DE, unsigned
albu: mov a,d ;return C if DE < HL, unsigned
cmp h
rnz ;if D <> H, C is set correctly
mov a,e ;else compare E with L
cmp l
ret
bgau: xchg ;return C if HL > DE, unsigned
agbu: mov a,h ;return C if DE > HL, unsigned
cmp d
rnz ;if H <> D, C is set correctly
mov a,l ;else compare L with E
cmp e
ret
blas: xchg ;return C if HL < DE, signed
albs: mov a,h ;return C if DE < HL, signed
xra d
jp albu ;if same sign, do unsigned compare
mov a,d
ora a
rp ;else return NC if DE is positive and HL is negative
stc ;else set carry, since DE is negative and HL is pos.
ret
bgas: xchg ;return C if HL > DE, signed
agbs: mov a,h ;return C if DE > HL, signed
xra d
jp agbu ;if same sign, go do unsigned compare
mov a,h
ora a
rp ;else return NC is HL is positive and DE is negative
stc
ret ;else return C, since HL is neg and DE is pos
;
; Multiplicative operators: *, /, and %:
;
smod: mov a,d ;signed MOD routine: return (DE % HL) in HL
push psw ;save high bit of DE as sign of result
call tstn ;get absolute value of args
xchg
call tstn
xchg
call usmod ;do unsigned mod
pop psw ;was DE negative?
ora a ;if not,
rp ; all done
mov a,h ;else make result negative
cma
mov h,a
mov a,l
cma
mov l,a
inx h
ret
nop ;maintain address compatibility with some
nop ; pre-release v1.4's.
usmod: mov a,h ;unsigned MOD: return (DE % HL) in HL
ora l
rz
push d
push h
call usdiv
pop d
call usmul
mov a,h
cma
mov h,a
mov a,l
cma
mov l,a
inx h
pop d
dad d
ret
smul: xra a ;signed multiply: return (DE * HL) in HL
sta tmp
call tstn
xchg
call tstn
call usmul
smul2: lda tmp
rar
rnc
mov a,h
cma
mov h,a
mov a,l
cma
mov l,a
inx h
ret
tstn: mov a,h
ora a
rp
cma
mov h,a
mov a,l
cma
mov l,a
inx h
lda tmp
inr a
sta tmp
ret
usmul: push b ;unsigned multiply: return (DE * HL) in HL
call usm2
pop b
ret
usm2: mov b,h
mov c,l
lxi h,0
usm3: mov a,b
ora c
rz
mov a,b
rar
mov b,a
mov a,c
rar
mov c,a
jnc usm4
dad d
usm4: xchg
dad h
xchg
jmp usm3
usdiv: mov a,h ;unsigned divide: return (DE / HL) in HL
ora l ;return 0 if HL is 0
rz
push b
call usd1
mov h,b
mov l,c
pop b
ret
usd1: mvi b,1
usd2: mov a,h
ora a
jm usd3
dad h
inr b
jmp usd2
usd3: xchg
usd4: mov a,b
lxi b,0
usd5: push psw
usd6: call cmphd
jc usd7
inx b
push d
mov a,d
cma
mov d,a
mov a,e
cma
mov e,a
inx d
dad d
pop d
usd7: xra a
mov a,d
rar
mov d,a
mov a,e
rar
mov e,a
pop psw
dcr a
rz
push psw
mov a,c
ral
mov c,a
mov a,b
ral
mov b,a
jmp usd6
sdiv: xra a ;signed divide: return (DE / HL) in HL
sta tmp
call tstn
xchg
call tstn
xchg
call usdiv
jmp smul2
cmphd: mov a,h ;this returns C if HL < DE
cmp d ; (unsigned compare only used
rc ; within C.CCC, not from C)
rnz
mov a,l
cmp e
ret
;
; Shift operators << and >>:
;
sderbl: xchg ;shift DE right by L bits
shlrbe: inr e ;shift HL right by E bits
shrbe2: dcr e
rz
xra a
mov a,h
rar
mov h,a
mov a,l
rar
mov l,a
jmp shrbe2
sdelbl: xchg ;shift DE left by L bits
shllbe: inr e ;shift HL left by E bits
shlbe2: dcr e
rz
dad h
jmp shlbe2
;
; Routines to 2's complement HL and DE:
;
cmh: mov a,h
cma
mov h,a
mov a,l
cma
mov l,a
inx h
ret
cmd: mov a,d
cma
mov d,a
mov a,e
cma
mov e,a
inx d
ret
;
; The following routines yank a formal parameter value off the stack
; and place it in both HL and A (low byte), assuming the caller
; hasn't done anything to its stack pointer since IT was called.
;
; The mnemonics are "Move Arg #n To HL",
; where arg #1 is the third thing on the stack (where the first
; and second things are, respectively, the return address of the
; routine making the call to here, and the previous return
; address to the routine which actually pushed the args on the
; stack.) Thus, a call to "ma1toh" would return with the first
; passed parameter in HL and A; "ma2toh" would return the second,
; etc. Note that if the caller has pushed [n] items on the stack
; before calling "ma [x] toh", then the [x-n]th formal parameter
; value will be returned, not the [x]th.
;
ma1toh: lxi h,4 ;get first arg
ma0toh: dad sp
mov a,m
inx h
mov h,m
mov l,a
ret
ma2toh: lxi h,6 ;get 2nd arg
jmp ma0toh
ma3toh: lxi h,8 ;get 3rd arg
jmp ma0toh
ma4toh: lxi h,10 ;get 4th arg
jmp ma0toh
ma5toh: lxi h,12 ;get 5th arg
jmp ma0toh
ma6toh: lxi h,14 ;get 6th arg
jmp ma0toh
ma7toh: lxi h,16 ;get 7th arg
jmp ma0toh
;
; This routine takes the first 7 args on the stack
; and places them contiguously at the "args" ram area.
; This allows a library routine to make one call to arghak
; and henceforth have all it's args available directly
; through lhld's instead of having to hack the stack as it
; grows and shrinks. Note that arghak should be called as the
; VERY FIRST THING a function does, before even pushing BC.
;
arghak: lxi d,args ;destination for block move in DE
lxi h,4 ;pass over two return address
dad sp ;source for block move in HL
push b ;save BC
mvi b,14 ;countdown in B
arghk2: mov a,m ;copy loop
stax d
inx h
inx d
dcr b
jnz arghk2
pop b ;restore BC
ret
;
; UP TO THIS POINT, ABSOLUTELY NO CHANGES SHOULD EVER BE MADE
; TO THIS SOURCE FILE (except for customizing the EQU statements
; at the beginning of the file).
;
;
; This routine is called first to do argc & argv processing (if
; running under CP/M) and some odds and ends initializations:
;
init: pop h ;store return address
shld tmp2 ; somewhere safe for the time being
IF CPM
lxi h,arglst-2 ;set the "argv" that the C main program
ENDIF
IF NOT CPM
lxi h,0
ENDIF
push h ; will get.
;Initialize storage allocation pointers:
lhld freram ;get address after end of externals
shld allocp ;store at allocation pointer (for "sbrk.")
lxi h,1000 ;default safety space between stack and
shld alocmx ; highest allocatable address in memory
; (for use by "sbrk".).
;Initialize random seed:
lxi h,59dch ;let's stick something wierd into the
shld rseed ;first 16 bits of the random-number seed
;Initialize I/O hack locations:
mvi a,0dbh ;"in" op, for "in xx; ret" subroutine
sta iohack
mvi a,0d3h ;"out" op for "out xx; ret" subroutine
sta iohack+3
mvi a,0c9h ;"ret" for above sobroutines
sta iohack+2 ;the port number is filled in by the
sta iohack+5 ;"inp" and "outp" library routines.
;Initialize DMA video parameters:
IF DMAVIO ;if we're using DMA video routines,
lxi h,0cc00h ;set up default values (may be changed
shld pbase ;to whatever suits). Video board address,
lxi h,16
shld xsize ;# of lines,
lxi h,64
shld ysize ;# of columns,
lxi h,1024
shld psize ;and total # of characters on screen
ENDIF
IF CPM ;under CP/M: clear console, process ARGC & ARGV:
mvi c,11 ;interrogate console status to see if there
call bdos ; happens to be a stray character there...
ora a ;(used to be `ani 1'...they tell me this works
nop ; better for certain bizarre CP/M-"like" systems)
jz initzz
mvi c,1 ;if input present, clear it
call bdos
initzz: lxi h,tbuff ;if arguments given, process them.
lxi d,comlin ;get ready to copy command line
mov b,m ;first get length of it from loc. base+80h
inx h
mov a,b
ora a ;if no arguments, don't parse for argv
jnz initl
lxi d,1 ;set argc to 1 in such a case.
jmp i5
initl: mov a,m ;ok, there are arguments. parse...
stax d ;first copy command line to comlin
inx h
inx d
dcr b
jnz initl
xra a ;place zero following line
stax d
lxi h,comlin ;now compute pointers to each arg
lxi d,1 ;arg count
lxi b,arglst ;where pointers will all go
xra a ;clear "in a string" flag
sta tmp1
i2: mov a,m ;between args...
inx h
cpi ' '
jz i2
ora a
jz i5 ;if null byte, done with list
cpi '"'
jnz i2a ;quote?
sta tmp1 ;yes. set "in a string" flag
jmp i2b
i2a: dcx h
i2b: mov a,l ;ok, HL is a pointer to the start
stax b ;of an arg string. store it.
inx b
mov a,h
stax b
inx b
inx d ;bump arg count
i3: mov a,m
inx h ;pass over text of this arg
ora a ;if at end, all done
jz i5
push b ;if tmp1 set, in a string
mov b,a ; (so we have to ignore spaces)
lda tmp1
ora a
mov a,b
pop b
jz i3a
cpi '"' ;we are in a string.
jnz i3 ;check for terminating quote
xra a ;if found, reset "in string" flag
sta tmp1
dcx h
mov m,a ;and stick a zero byte after the string
inx h ;and go on to next arg
i3a: cpi ' ' ;now find the space between args
jnz i3
dcx h ;found it. stick in a zero byte
mvi m,0
inx h
jmp i2 ;and go on to next arg
i5: push d ;all done finding args. Set argc.
mvi b,nfcbs ;now initialize all the file info
lxi h,fdt ;(just zero the fd table)
i6: mvi m,0
inx h
dcr b
jnz i6
ENDIF
IF NOT CPM ;if not under CP/M, force ARGC value
lxi h,1 ; of one.
push h
ENDIF
xra a
sta ungetl ;clear the push-back byte
sta lastc ;and last character byte
lhld tmp2
pchl ;all done initializing.
;
; General purpose error value return routine:
;
verror: lxi h,-1 ;general error handler...just
ret ;returns -1 in HL
;
; Here are file I/O handling routines, only needed under CP/M:
;
;
; Close any open files and reboot:
;
vexit:
IF CPM ;if under CP/M, close all open files
mvi a,7+nfcbs ;start with largest possible fd
exit1: push psw ;and scan all fd's for open files
call vfgfd ;is file whose fd is in A open?
jc exit2 ;if not, go on to next fd
mov l,a ;else close the associated file
mvi h,0
push h
call vclose
pop h
exit2: pop psw
dcr a ;and go on to next one
cpi 7
jnz exit1
ENDIF
jmp exitad ;done closing; now reboot CP/M or whatever.
;
; Close the file whose fd is 1st arg:
;
IF CPM ;here comes a lot of CP/M stuff...
vclose: call setdma ;library function just jumps here.
call ma1toh ;get fd in A
call vfgfd ;see if it is open
jc verror ;if not, complain
mov a,m
ani 4
IF NOT MPM2 ;if not MP/M, and
jz close2 ;the file isn't open for write, don't bother to close
ENDIF
IF MPM2 ;always close all files under MP/M
nop
nop
nop
ENDIF
push h ;save fd table entry addr
call ma2toh ;move arg1 to A
push b
call vfgfcb ;get the appropriate fcb address
xchg ;put it in DE
mvi c,16 ;get BDOS function # for close
call bdos ;and do it!
pop b
pop h
close2: mvi m,0 ;close logically
cpi 255 ;if 255 comes back, we got problems
lxi h,0
rnz ;return 0 if OK
dcx h ;return -1 on error
ret
;
; Determine status of file whose fd is in A...if the file
; is not open, return C flag set, else clear C flag:
vfgfd: call setdma
mov d,a
sui 8
rc ;if fd < 8, error
cpi nfcbs
cmc ;don't allow too big an fd either
rc
push d
mov e,a ;OK, we have a value in range. Now
mvi d,0 ; see if the file is open or not
lxi h,fdt
dad d
mov a,m
ani 1 ;bit 0 is high if file is open
stc
pop d
mov a,d
rz ;return C set if not open
cmc
ret ;else reset C and return
;
; Set up a CP/M file control block at HL with the file whose
; simple null-terminated name is pointed to by DE:
; Format for filename must be: "[white space][d:]filename.ext"
;
vsetfcb: call setdma ;set up an fcb at HL for filename at DE
push b
call igwsp ;ignore blanks and tabs
mvi b,8
push h
inx d
ldax d
dcx d
cpi ':' ;default disk byte value is 0
mvi a,0 ; (for currently logged disk)
jnz setf1
ldax d ;oh oh...we have a disk designator
call mapuc ;make it upper case
sui '@' ;and fudge it a bit
inx d
inx d
setf1: mov m,a
inx h
call patchnm ;now set filename and pad with blanks
ldax d
cpi '.' ;and if an extension is given,
jnz setfcb2
inx d
setfcb2 mvi b,3 ;set the extension and pad with blanks
call setnm
xra a ;and zero the appropriate fields of the fcb
mov m,a
lxi d,20
dad d
mov m,a
inx h
jmp setfcb3 ;finish up elsewhere to keep addresses consistent
;with prior releases
;
; This routine copes up to B characters from memory at DE to
; memory at HL and pads with blanks on the right:
;
setnm: push b
setnm1: ldax d
cpi '*' ;wild card?
mvi a,'?' ;if so, pad with ? characters
jz pad2
setnm2: ldax d
call legfc ;next char legal filename char?
jc pad ;if not, go pad for total of B characters
mov m,a ;else store
inx h
inx d
dcr b
jnz setnm1 ;and go for more if B not yet zero
pop b
setnm3: ldax d ;skip rest of filename if B chars already found
call legfc
rc
inx d
jmp setnm3
pad: mvi a,' ' ;pad with B blanks
pad2: mov m,a ;pad with B instances of char in A
inx h
dcr b
jnz pad2
pop b
ret
;
; Test if char in A is legal character to be in a filename:
;
legfc: call mapuc
cpi '.' ; '.' is illegal in a filename or extension
stc
rz
cpi ':' ;so is ':'
stc
rz
cpi 7fh ;delete is no good
stc
rz
cpi '!' ;if less than exclamation pt, not legal char
ret ;else good enough
;
; Map character in A to upper case if it is lower case:
;
mapuc: cpi 'a'
rc
cpi 'z'+1
rnc
sui 32 ;if lower case, map to upper
ret
;
; Ignore blanks and tabs at text pointed to by DE:
;
igwsp: dcx d
igwsp1: inx d
ldax d
cpi ' '
jz igwsp1
cpi 9
jz igwsp1
ret
;
; This routine does one of two things, depending
; on the value passed in A.
;
; If A is zero, then it finds a free file slot
; (if possible), else returns C set.
;
; If A is non-zero, then it returns the address
; of the fcb corresponding to an open file whose
; fd happens to be the value in A, or C set if there
; is no file associated with fd.
;
vfgfcb: push b
call setdma
ora a ;look for free slot?
mov c,a
jnz fgfc2 ;if not, go away
mvi b,nfcbs ;yes. do it...
lxi d,fdt
lxi h,fcbt
mvi c,8
fgfc1: ldax d
ani 1
mov a,c
jnz fgfc1a ;found free slot?
pop b ;yes. all done.
ret
fgfc1a: push d
lxi d,36 ;fcb length to accommodate random I/O
dad d
pop d
inx d
inr c
dcr b
jnz fgfc1
fgfc1b: stc
pop b
ret ;return C if no more free slots
fgfc2: call vfgfd ;compute fcb address for fd in A:
jc fgfc1b ;return C if file isn't open
sui 8
mov l,a ;put (fd-8) in HL
mvi h,0
dad h ;double it
dad h ;4*a
mov d,h ;save 4*a in DE
mov e,l
dad h ;8*a
dad h ;16*a
dad h ;32*a
dad d ;36*a
xchg ;put 36*a in DE
lxi h,fcbt ;add to base of table
dad d ;result in HL
mov a,c ;and return original fd in A
pop b
ret
setdma: push d ;just a preventative measure,
push b ;since the default I/O buffer
push psw ;tends to magically change
push h ;around by itself when left
mvi c,26 ;in CP/M's hands !!
lxi d,tbuff
call bdos
pop h
pop psw
pop b
pop d
ret
ENDIF ;end of CP/M-related file I/O routines
IF NOT CPM
main: equ $ ;where main program resides when not under CP/M
;(under CP/M, the data area comes first)
ENDIF
;
; Ram area:
;
IF NOT CPM ;if not under CP/M, use custom ram area address
org ram
ENDIF
room: ds 30 ;room for random stuff
pbase: ds 2 ;screen-DMA address
ysize: ds 2 ;screen width
xsize: ds 2 ;screen height
psize: ds 2 ;screen length
rseed: ds 8 ;the random generator seed
args: ds 14 ;"arghak" puts args passed on stack here.
iohack: ds 6 ;room for I/O subroutines for use by "inp"
;and "outp" library routines
allocp: ds 2 ;pointer to free storage for use by "sbrk" func
alocmx: ds 2 ;highest location to be made available to the
;storage allocator
tmp: equ room ;this is misc. garbage space
tmp1: equ room+1
tmp2: equ room+2
tmp2a: equ room+4
ungetl: equ room+6 ;where characters are "ungotten"
lastc: equ room+7 ;last char typed
;
;--------------------------------------------------------------------------
; The following data areas are needed only if running under CP/M:
;
IF CPM
;
; The fcb table (fcbt): 36 bytes per file control block
;
fcbt: ds 36*nfcbs ;reserve room for fcb's (extra byte for IMDOS)
;
; The fd table: one byte per file specifying r/w/open as follows:
; bit 0 is high if open, low if closed
; bit 1 is high if open for read
; bit 2 is high if open for write
; (both b1 and b2 may be high)
;
fdt: ds nfcbs ;one byte per fcb tells if it is active, r/w, etc.
;
; The command line is copied here by init:
;
comlin: ds 131 ;copy of the command line pointed to by entries
;in arglst
;
; This is where "init" places the array of argument pointers:
;
arglst: ds 60 ;the "argv" paramater points here (well,
;actually to 2 bytes before arglst). Thus,
;up to 30 parameters may be passed to "main"
ENDIF ;(enough for you, Andy?)
;
; End of CP/M-only data area
;---------------------------------------------------------------------------
IF CPM
main: equ $ ;where "main" program will be loaded under CP/M
ENDIF
end