Skip to content

Instantly share code, notes, and snippets.

@pervognsen
Created June 21, 2020 00:20
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save pervognsen/a80aea02793588fcf771857d64ea98c1 to your computer and use it in GitHub Desktop.
Save pervognsen/a80aea02793588fcf771857d64ea98c1 to your computer and use it in GitHub Desktop.
; https://web.archive.org/web/20190701203222/https://www.pcengines.ch/tp3.htm
; The disassembler was applied to a copy of TP 3.01A downloaded from WinWorld.
; I postprocessed the disassembly with a script to clean up spacing and column alignment.
; *** TURBO PASCAL version 3.01 A source code
; ***
; *** commented by Pascal Dornier
; *** all rights reserved
; "***
cseg $100 ; "COM file...
;
; I/O port equates
;
timerfrq= $0042 ; timer: frequency
timercmd= $0043 ; timer: command
timerflg= $0061 ; "PPI: sound enable
;
; interrupt numbers
;
bioscrt = $0010 ; BIOS: CRT driver
bioskbd = $0016 ; BIOS: KBD driver
msdos = $0021 ; "MS-DOS entry
;
; variable definitions
;
attnrm = $0000 ; attribute NormVideo
attlow = $0001 ; attribute LowVideo
att2 = $0002 ; attribute #2 (used by editor)
att3 = $0003 ; attribute #3
txwinx1 = $0004 ; upper left edge current window
txwiny1 = $0005
scrmod = $0006 ; video mode
coltxt = $0007 ; color or text ?
attcur = $0008 ; current attribute
grmod = $0009 ; graphics mode
grwinx1 = $000A ; graphics window: upper left edge
grwinx2 = $000C
grwiny1 = $000E ; bottom right edge
grwiny2 = $0010
delaycnt= $0012 ; number of loop's for 1 ms
collin = $0014 ; line color
lnxdir = $0016 ; X direction
lnydir = $0018 ; Y direction
lnxpos = $001A ; X position
lnypos = $001C ; Y position
lndda = $001E ; DDA register
grback = $0020 ; "graphics: background color
; heap pointer
hpstrt = $0022 ; beginning of heap
hpstrt1 = $0024
hpdstpt = $0026 ; ^ destination pointer
hpdstpt1= $0028
hpesize = $002A ; size of entry to delete
hpesize1= $002C
hplast = $002E ; ^ previous entry
hplast1 = $0030
hpmerg = $0032 ; length of entry to be merged
hpmerg1 = $0034 ; "with next entry
coninbuf= $0036 ; CON input buffer
pnbuf0 = $00B5
pnbuf = $00B6 ; buffer for filename etc.
pnbuf1 = $00B7
pnbuf2 = $00B8
pnbuf3 = $00B9
pndiratt= $00CB ; for Dir: file attribute
pndirnm = $00D4 ; name of file
pndirpad= $00E4 ; ^ end for formatting
pnbufend= $0135 ; "end of buffer
; I/O vectors
vkbdstat= $0136 ; KBD stat (ConStPtr)
vkbdget = $0138 ; KBD get (ConInPtr)
vconput = $013A ; CON put (ConOutPtr)
vprnput = $013C ; PRN put (LstOutPtr)
vauxput = $013E ; AUX put (AuxOutPtr)
vauxget = $0140 ; AUX get (AuxInPtr)
vusrput = $0142 ; USR put (UsrOutPtr)
vusrget = $0144 ; "USR get (UsrInPtr)
; file entries for std files
filcon = $0146 ; CON, TRM
filkbd = $014A ; KBD
fillst = $014E ; LST
filaux = $0152 ; AUX
filusr = $0156 ; USR
stdin = $015A ; MS-DOS input file
stdinfl = $015C ; flag
stdinof = $015E ; ^ buffer
stdinsz = $0160 ; buffer size
stdout = $0166 ; MS-DOS output file
stdoutfl= $0168 ; flag
stdoutof= $016A ; ^ buffer
stdoutsz= $016C ; "buffer size
modeflg = $0172 ; option flag - see initmem
spval = $0174 ; SP on initialisation
turbocs = $0176 ; CS for return to Turbo
turbods = $0178 ; DS for return to Turbo
filtabpt= $017A ; ^ into list of open files
filemax = $017C ; max number of open files
verror = $017E ; ErrorPtr
errnum = $0180 ; error number
conbufln= $0181 ; BufLen (for ReadLn)
conbufpt= $0182 ; ^ into input buffer
conbfend= $0184 ; end of input buffer
errpos = $0186 ; return address ->
errpos2 = $0188 ; position of error
hptop = $018A ; HeapPtr: ^ end of heap
hptop1 = $018C
svintv = $018E ; buffer for vector:
svintv1 = $0190 ; DIV/0 interrupt
lastkey = $0192 ; code of last key pressed
cbreak = $0194 ; CBreak
ovrpnbuf= $0196 ; "buffer for overlay filename
fmtfield= $01E6 ; formatting: field size
fmttype = $01E7 ; type of conversion
fmtsdst = $01E8 ; ^ dest string (STR)
fmtsdst1= $01EA
fmtvdst = $01EC ; ^ dest var (VAL)
fmtvdst1= $01EE
fmtpdst = $01F0 ; ^ error var (VAL)
fmtpdst1= $01F2
recvbuf = $01F4 ; "real buffer: number to convert
errio = $01FA ; flag: return because of error
rndseed = $01FC ; random seed: last random number
rndseed1= $01FE
strdstln= $0200 ; string operations: max. length of result
strpos = $0202 ; Pos
strnum = $0204 ; Num
strtrgt = $0206 ; ^ dest string
strtrgt1= $0208
strobj = $020A ; ^ object string
strobj1 = $020C ; "
remul11 = $020E ; real arithmetics
remul11a= $020F ; first number * /
remul12 = $0210
remul13 = $0212
remul21b= $0213
remul21 = $0214 ; second number * /
remul21a= $0215
remul22 = $0216
remul23 = $0218
retrc1 = $021A ; number for Sqrt, ArcTan, polynomials
retrc2 = $021C
retrc3 = $021E
resign = $0220 ; sign for + -
resave = $0221
remant = $0222 ; mantissa + -
remant1 = $0223 ; "
cvdecexp= $0224 ; real -> ASCII: decimal exponent
cvexpcnt= $0225 ; ASCII -> real: counter for exponent
cvoutbuf= $0226 ; output buffer for result
currfil = $0232 ; ^ current file var
currfil1= $0234
filfunc = $0238 ; function code file operation
filerr = $0239 ; error code, if failure
prnum = $023A ; real: number to print
filetab = $0240 ; "table of open files (file handles)
; kernel variables
freemem = $0260 ; free memory (paragraphs)
stackseg= $0262 ; stack segment on start
stackpt = $0264 ; stack pointer
destseg = $0266 ; segment of compiled program
codesize= $0268 ; code size (paragraphs)
datasize= $026A ; data size (paragraphs)
minstksz= $026C ; min size stack + heap
mincssz = $026E ; min size CS
mindssz = $0270 ; min size DS
minhpsz = $0272 ; min size free heap
maxhpsz = $0274 ; max size free heap
txbeg = $0276 ; ^ beginning of text
txend = $0278 ; ^ end of text
txmemend= $027A ; ^ end of text memory
txerrpos= $027C ; ^ error in text
vfilbig = $027E ; vector: file too big
vnewfil = $0280 ; vector: file not found
txcomp = $0282 ; 0 = text not translated
cpmode = $0283 ; (0 memory, 1 find error, 2 COM, 3 CHN)
cperr = $0284 ; compiler's error number
txchg = $0285 ; 0 = text not changed
defdrv = $0286 ; number of default drive
scrpn = $0287 ; buffer for filename processing
scrpnend= $02C6 ; end of buffer
workpn = $02C7 ; filename work file
mainpn = $0307 ; filename main file
mainflg = $0347 ; 0 = work file used
msgflg = $0348 ; 0 = error messages not read
codedest= $0349 ; (1 = memory, 2 = COM, 3 = CHN)
parmlin = $034A ; buffer: input line for program
knumbuf = $0386 ; number output buffer
curatt = $038E ; "number current video attribute
; editor variables
srend = $0390 ; ^ block end for search
srbeg = $0392 ; ^ block beg for search
bkbeg = $0394 ; ^ block beg
bkend = $0396 ; ^ block end
lnupper = $0398 ; ^ line above
disbeg = $039A ; ^ beginning of text displayed
oldpos = $039C ; old position
eolpos = $039E ; ^ end of line
srpos = $03A0 ; current search pos
srcnt = $03A2 ; search counter
horscr = $03A4 ; horizontal scrolling
attflg = $03A5 ; (1 = block beg, 2 = block end in curr line)
phrow = $03A6 ; cursor position
phcol = $03A7
statobs = $03A8 ; 0 = invalid status line
edcol = $03A9 ; column
attchg = $03AA ; FF = attribute changes in curr line
sropt = $03AB ; options for search
; 1=entire words, 2=replace without query
; 4=upper = lower, 8=global
; 10=backwards 20=L (?)
srmode = $03AC ; 0=search, FF=search and replace
editflg = $03AD ; ? never referenced !
bkhide = $03AE ; FF = invisible block
scrfl1 = $03AF ; FF = short redisplay
statera = $03B0 ; # chars to delete in status line
oldlen = $03B1 ; old length curr line
dislin = $03B2 ; redisplay from line ...
overflg = $03B3 ; 0 = overwrite, FF = insert
indntflg= $03B4 ; 0 = normal, FF = indent
disflg = $03B6 ; FF = don't display
scrfl2 = $03B7 ; redisplay: 0 = small displacement
bkbegl = $03BA ; ^ block beg in curr line
bkendl = $03BC ; ^ block end in curr line
edpos = $03BE ; ^ current line
lnpos = $03C0 ; pos in line buffer
posfifo = $03C2 ; old position for ^QP
pfifosrc= $03C5
qppos = $03C6
qppos1 = $03C8
pfifodst= $03C9 ; (for block transfer)
cmdbuf = $03CA ; buffer for command entry
cmdbuf1 = $03CB
srword = $03CE ; max length of search word
srword1 = $03CF ; length
srword2 = $03D0 ; string
srrepl = $03EF ; replace word
srrepl1 = $03F0
srrepl2 = $03F1
stopt = $0410 ; options for search / replace
sropt1 = $0411
fnbuf = $041D ; buffer for filename entry
fnbuf2 = $041F
fnbufend= $0460
nbkbeg = $0461 ; block transfer destination
nbk = $0463 ; block
sepptr = $0465 ; ^ word separator table
explen = $0467 ; expected length
curpast = $0469 ; 1 = cursor past end of line
line0 = $046B
line = $046C ; buffer: current line
lineend0= $04E9
lineend1= $04EA
lineend = $04EB
dmabuf = $04EE ; buffer for redisplay
scrseg = $0590 ; segment of video memory
scrrow = $0592 ; video row
scrbad = $0593 ; "FF = snowy screen
; compiler variables
spsav = $0594 ; SP at start
symtop = $0596 ; current end of symbol table
symtop2 = $0598 ; end of symbol table
ptcbeg = $059A ; ^ beg symbol table, patch list
tyfence = $059C ; limit for undefined pointer types
fence = $059E ; limit for search on new definition
pc = $05A0 ; PC of emitted code
dc = $05A2 ; data offset of emitted code
varspc = $05A4 ; space used on stack
cdptr = $05A6 ; ^ into code buffer
cdbufpt = $05A8 ; PC of beg code buffer
cdbegpt = $05AA ; ^ code buffer
cdfoff = $05AC ; buffer's offset in file
cdprcoff= $05AD ; offset for overlay
cdfoff1 = $05AE
lincnt = $05B0 ; line counter
cdinval = $05B2 ; <> 0: invalid code
recnum = $05B3 ; number of current record
reccnt = $05B4 ; record counter
scalcnt = $05B5 ; enumeration type counter
srcend = $05B6 ; <> 0: end of source reached
lexnest = $05B7 ; lexical nesting
flgpshax= $05B8 ; <> 0: save AX on stack
flgpshes= $05B9 ; <> 0: save ES on stack
flgpshdi= $05BA ; <> 0: save DI on stack
usrint = $05BB ; <> 0: user interrupt used
ovrcnt = $05BC ; overlay counter
inclflg = $05BD ; FF = include file used
cmaxfil = $05BF ; max number of open files
cinpsize= $05C1 ; size of std input buffer
coutsize= $05C3 ; size of std output buffer
direct = $05C5 ; compiler directives:
; 1 = I/O check
; 2 = range check
; 4 = I/O mode (CON or TRM)
; 8 = value for CBreak
; 10 = user interrupt
; 20 = stack check
; 40 = type check
; 80 = device check
destpn = $05C7 ; object filename
destpne = $0603
dstfile = $0607 ; object file handle
ptctop = $0609 ; current end of patch list
ptcend = $060B ; limit for patch list
srcptr = $060D ; ^ into text
srclnbeg= $060F ; ^ beg of source line
chptr = $0611 ; ^ into line buffer
; variable entry 1:
indflg = $0615 ; <> 0: indexed var
indptflg= $0616 ; <> 0: indirect via pointer
varseg = $0617 ; segment (FF=DS, FE=CS, FD=ES, else:
; lexical nesting
varofs = $0618 ; variable offset
vartp = $061A ; type
varctp = $061C ; element type
varnest = $061D ; <>0: record #, FF: undef pointer
lower = $061E ; lower bound or ^ index type
upper = $0620 ; upper bound or ^ element type
varsize = $0622 ; variable size
parm1end= $0624
parm2 = $0625 ; variable entry 2
var2ctp = $062C ; type
lower2 = $062E ; lower bound
upper2 = $0630 ; upper bound
var2size= $0632 ; size
maxsize = $0634 ; max size for variant records
uncrlink= $0636 ; link for uncrunch list
flgvar = $0638 ; F = VAR parameter
vrecflg = $0639 ; number of record variant
procfnc = $063A ; procedure or function
ovrproc = $063B ; <>0: overlay procedure
absflg = $0647 ; FF = Absolute variable
var3ofs = $0648 ; offset
var3seg = $064A ; segment
ovrlen = $064C ; max length of overlay procedures in a file
creal1 = $064E ; buffer for real constant
creal2 = $0650
creal3 = $0652
cresign = $0653
stklev = $0654 ; stack use (WITH, FOR, display)
casectp = $0655 ; CASE element type
direcsv = $0656 ; directives at beg of statement
brnchop = $0658 ; branch op on comparision
rdlnflg = $0659 ; flag: Read / ReadLn
forptr = $065A ; pointer for FOR: TO / DOWNTO
withnest= $065C ; WITH nesting
inlinflg= $065D ; inline flag
withtab = $065E ; WITH table: record number, segment
withtab1= $0660 ; offset, FFFF = indexed
functp = $069E ; ^ result type
comptp = $06A0 ; ^ type for comparison
oldpc = $06A2 ; PC before translation of this atom
negflg = $06A4 ; negation flag
exres = $06A6 ; integer constant
cxbuf = $06A8 ; buffer for CX
direcin = $06AA ; directives before include
semiflg = $06AC ; flag for missing error
wordflg = $06AD ; type of word read
typept = $06AE ; ^ type
sympos = $06B0 ; ^ into symbol table
wrdend = $06B2 ; end of word read
retbuf = $06B4 ; buffer for return address
inclpn = $06B6 ; include filename
incfile = $06F6 ; include file handle
bufpt = $06F8 ; include pointer
bufend = $06FA ; end of include buffer
srclnbg = $06FC ; beg of line in buffer
frelpos = $06FE ; relative position in file
ptcbuf1 = $0700 ; buffer for patch
wordbuf = $0702 ; buffer for word, filename, string...
wrdbuf1 = $0703
inclbuf = $0782 ; include buffer
txstrt = $0802 ; "beg of text space / error messages
JMP start ; "jump to main code
B $90,$90,$CD,$AB
B "Copyright (C) 1985 BORLAND Inc"
B $02,$04,$00
W ecmd1 ; ptrs for TINST.COM
B $00
W errpath
B $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
B $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
B $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
displstrB $14,"Default display mod"
B $65
txwinx2 B $50 ; lower edge of text window
txwiny2 B $19
B $01,$FF,$FF
attmono B $0F,$07,$07,$70 ; monochrome attributes
attbwgr B $0F,$07,$07,$70 ; mono graphic atts
attcolgrB $0E,$07,$07,$4F ; "color graphic atts
putstr CS: ; put attribute string
MOV.B AH,[BX] ; length
OR.B AH,AH
STC
JZ puts2 ; nothing to put
putstrl INC BX ; next char
CS:
MOV.B AL,[BX] ; get it
PUSH AX ; save cnt
CALL conput ; put CRT
POP AX
DEC.B AH ; another one ?
JNZ putstrl ; :yes
CLC
puts2 RET ; "
meascnt W $0000
measdoneB $00
vtimer W $0000 ; "space for timer int vector
vtimer1 W $0000
msspeed MOV delaycnt,#$006E ; measure CPU speed
CS: ; measurement value
MOV.B measdone,#$00 ; flag:not done
MOV SI,#timvec ; ptr to timer INT
ES:
MOV AX,[SI]
CS:
MOV vtimer,AX ; save it
ES:
MOV AX,[SI]$02
CS:
MOV vtimer1,AX
CLI
ES:
MOV [SI],#msint1
ES:
MOV [SI]$02,CS
STI ; INT ok again
JMP.b mssploop ; "measurement loop
msint1 PUSH DS ; first INT entry
PUSH AX
XOR AX,AX
MOV DS,AX
MOV timvec,#msint2 ; set second vector
CS:
MOV meascnt,AX ; clear counter
POP AX
POP DS
CS:
JMPF [vtimer] ; "do timer routine
msint2 CS: ; second INT entry
MOV.B measdone,#$FF ; set flag:done
CS:
JMPF [vtimer] ; "do timer routine
mssploopCALL delay1ms ; wait one step
CS:
INC meascnt ; count those steps
CS:
CMP.B measdone,#$FF ; done ?
JNZ mssploop ; no, continue counting
CS:
MOV AX,vtimer1 ; restore timer INT vec
CLI
ES:
MOV [SI]$02,AX
CS:
MOV AX,vtimer
ES:
MOV [SI],AX
STI ; INT ok again
CS:
MOV AX,meascnt ; number of steps done
ADD AX,AX ; *2
MOV delaycnt,AX ; -> result
RET ; "
delaybx MOV AX,BX ; Delay(BX)
xdelay MOV CX,AX ; DELAY(AX)
JCXZ delay2 ; :no delay
delay1 CALL delay1ms ; delay 1ms
LOOP delay1
delay2 RET ; "
delay1msPUSH CX ; Delay 1 ms
MOV CX,delaycnt ; get count for 1ms
delay3 LOOP delay3 ; do it
POP CX ; restore cnt
RET ; "
xclrscr PUSH BP ; CLRSCR
MOV AH,#$0F ; get screen stat
INT bioscrt
POP BP
CMP.B AL,scrmod ; = current screen mode ?
JZ clrscr1 ; :ok
MOV AL,scrmod ; no: do set it
JMP xtxtmode ; '
clrscr1 PUSH BP
MOV AX,#$0600 ; scroll window up, 0 lines
MOV.B BH,attcur
MOV CX,txwinx1 ; upper left edge
CS:
MOV DX,txwinx2 ; lower right edge
DEC.B DH
DEC.B DL
INT bioscrt ; clear window
MOV AH,#$02 ; set cursor position
MOV DX,txwinx1 ; upper left edge
XOR.B BH,BH
INT bioscrt ; do it
POP BP
RET ; "
xdellinePUSH BX ; DelLine
PUSH CX
PUSH DX
PUSH BP
CALL getcpos ; get cursor pos
MOV AH,#$06 ; scroll window up
doscrollMOV AL,#$01 ; one line
MOV.B BH,attcur
MOV.B CH,DH
MOV.B CL,txwinx1 ; upper left edge
CS:
MOV DX,txwinx2 ; lower right of window
DEC.B DH
DEC.B DL
CMP.B CH,DH ; same ?
JNZ dellin1 ; :no
XOR.B AL,AL ; no lines
dellin1 INT bioscrt ; do it
POP BP
POP DX
POP CX
POP BX
RET ; "
xinslinePUSH BX ; InsLine
insline2PUSH CX
PUSH DX
PUSH BP
CALL getcpos ; get cursor pos
MOV AH,#$07 ; scroll window down
JMP doscroll ; "as above
xlowvid PUSH AX ; LowVideo
MOV AL,attlow ; set low attribute
MOV attcur,AL
POP AX
RET ; "
xnormvidPUSH AX ; NormVideo, HighVideo
MOV AL,attnrm ; set normal attribute
MOV attcur,AL
POP AX
RET ; "
getcpos MOV AH,#$03 ; get cursor position
XOR.B BH,BH ; page 0
INT bioscrt ; do it
RET ; "
xclreol PUSH BX ; ClrEol
PUSH CX
PUSH DX
PUSH BP
CALL getcpos ; get cursor pos
MOV AX,#$0600 ; clear window
MOV.B BH,attcur
MOV CX,DX ; pos -> upper left
CS:
MOV.B DL,txwinx2 ; X-position
DEC.B DL ; Y-pos from current pos
INT bioscrt ; do it
POP BP
POP DX
POP CX
POP BX
RET ; "
xcrtinitCALL xnosound ; CrtInit
CS: ; Screen mode
MOV AL,scrmodch ; defined with TINST
CMP AL,#$FF
JNZ xtxtmode ; do change it
PUSH BP
MOV AH,#$0F ; get current screen mode
INT bioscrt
POP BP
xtxtmodeMOV.B txwinx1,#$00 ; TextMode
MOV.B txwiny1,#$00 ; set window
MOV.B grmod,#$FF ; no graphics mode
CMP AL,#$07 ; monochrome ?
MOV BH,#$50
MOV BL,#$00
MOV SI,#attmono ; ptr to screen attributes
JZ txtmd3 ; :yes, mono
MOV SI,#attcolgr
CMP AL,#$02 ; BW80 ?
JZ txtmd2 ; :yes
CMP AL,#$04
JB txtmd1 ; :C80, C40, BW40
MOV AL,#$03
txtmd1 MOV BL,#$FF
CMP AL,#$03 ; C80 ?
JZ txtmd3 ; :yes
MOV BH,#$28
CMP AL,#$01 ; C40 ?
JZ txtmd3 ; :yes
XOR.B AL,AL ; monochrome
MOV BL,#$00
txtmd2 MOV SI,#attbwgr
txtmd3 MOV scrmod,AL ; set screen mode
MOV.B coltxt,BL ; color or mono
CS:
MOV.B txwinx2,BH ; set screen size
CS:
MOV AX,[SI] ; get screen attributes
MOV attnrm,AX
CS:
MOV AX,[SI]$02
MOV att2,AX
PUSH BP
MOV AH,#$0F ; get screen status
INT bioscrt
CMP.B AL,scrmod ; correct mode ?
JZ txtmd4 ; :yes
MOV AL,scrmod
XOR.B AH,AH ; set screen mode
INT bioscrt
txtmd4 POP BP
JMP xnormvid ; "
xcrtexitRET ; "CrtExit
setcpos PUSH AX ; Set cursor position
PUSH BX ; DL=row
PUSH CX ; DH=col
PUSH DX ; pos relative to curr window
PUSH SI
PUSH DI
PUSH BP
PUSHF
XCHG.B DL,DH
ADD DX,txwinx1 ; add window pos
CS:
CMP.B DH,txwiny2 ; over edge of screen ?
JNB setcbad ; :yes
CS:
CMP.B DL,txwinx2
JNB setcbad ; :yes
MOV AH,#$02 ; set cursor
XOR.B BH,BH ; page 0
INT bioscrt
setcbad POPF
POP BP
POP DI
POP SI
POP DX
POP CX
POP BX
POP AX
RET ; "
xupcase JMP upcase ; "UpCase
xwherex CALL getcpos ; WhereX
MOV.B AL,DL ; get cursor pos
SUB.B AL,txwinx1 ; col-window col
INC.B AL ; +1
XOR.B AH,AH
RET ; "
xwherey CALL getcpos ; WhereY
MOV.B AL,DH ; get cursor pos
SUB.B AL,txwiny1 ; row-window row
INC.B AL ; +1
XOR.B AH,AH
RET ; "
xwindow POP BX ; Window
CMP AL,#$19 ; Row 2 - limit to 25
JA window1 ; :too much
CS:
MOV txwiny2,AL ; set it
window1 POP AX ; Col 2
CMP AL,#$50 ; limit to 80
JA window2 ; :too much
CS:
MOV txwinx2,AL ; set it
window2 POP AX ; Row 1
CS:
CMP.B AL,txwiny2 ; > row 2 ?
JNB window3 ; : no good
DEC.B AL
MOV txwiny1,AL ; set it
window3 POP AX ; Col 1
CS:
CMP.B AL,txwinx2 ; > col 2
JNB window4 ; :no good
DEC.B AL
MOV txwinx1,AL ; set it
window4 JMP BX ; "return
xtxtcol AND AL,#$1F ; TextColor
TEST AL,#$10 ; Blink ?
JZ txtcol1 ; :no
AND AL,#$0F
OR AL,#$80 ; set blink flag
txtcol1 AND.B attcur,#$70
OR.B attcur,AL ; set new attribute
RET ; "
xtxtbg AND AL,#$07 ; TextBackground
MOV CL,#$04
SHL.B AL,CL
AND.B attcur,#$8F
OR.B attcur,AL ; set attribute
RET ; "
setgmod PUSH BP ; Set graphic mode
MOV grwinx2,AX ; X-size
MOV grwinx1,#$0000 ; clear window
MOV grwiny1,#$0000
MOV grwiny2,#$00C7 ; Y-size = 199
MOV AL,grmod
XOR.B AH,AH
INT bioscrt ; set graph mode
XOR BX,BX
MOV.B grback,BL ; clear background
MOV AH,#$0B ; set palette -> backgrnd
INT bioscrt
INC.B BH ; set foregrnd
MOV AH,#$0B
INT bioscrt ; set palette
POP BP
RET ; "
xgrcolmdMOV.B grmod,#$04 ; GraphColorMode
setgmod2MOV AX,#$013F ; X-size = 319
JMP setgmod ; 'set it
xgrmode MOV.B grmod,#$05 ; GraphMode
JMP setgmod2 ; "set it
xhires MOV.B grmod,#$06 ; Hires
MOV AX,#$027F ; X-size = 639
CALL setgmod ; set it
MOV AX,#$000F ; set background
JMP.b xhirscol ; "
xgrbg AND AL,#$0F ; GraphBackground
MOV.B AH,grback ; get old color
AND.B AH,#$10
OR.B AL,AH
MOV grback,AL ; store it
setbg PUSH BP ; set graph background
XOR.B BH,BH
MOV.B BL,grback
MOV AH,#$0B ; set palette
INT bioscrt ; do it
POP BP
RET ; "
xpalettePUSH BP ; set palette AL
MOV.B BL,grback ; get current color
AND.B BL,#$EF ; clear palette
MOV AH,#$02
CMP.B grmod,#$04 ; color graphics ?
JZ setpal1 ; yes
MOV AH,#$01
setpal1 CMP.B AL,AH
JB setpal2
SUB.B AL,AH ; upper bit: part of backgnd
OR.B BL,#$10
setpal2 MOV.B grback,BL ; set it
MOV BH,#$01 ; set palette
MOV.B BL,AL ; palette number
MOV AH,#$0B
INT bioscrt ; do it
POP BP
JMP setbg ; "set backgnd color
xhirscolPUSH BP ; HiresColor
MOV BX,AX ; as background
MOV AH,#$0B ; set palette
INT bioscrt ; do it
POP BP
RET ; "
xgrwindwPOP BX ; GraphWindow
MOV CX,#$027F ; max. X = 639
CMP.B grmod,#$06 ; Hires ?
JZ gwind1 ; :yes
MOV CX,#$013F ; max. X = 319
gwind1 CMP AX,#$00C7 ; Y2 > 199
JA gwind2 ; :no good
MOV grwiny2,AX ; set Y2
gwind2 POP AX ; X2
CMP AX,CX ; > max X ?
JA gwind3 ; :no good
MOV grwinx2,AX ; set X2
gwind3 POP AX ; Y1
CMP AX,grwiny2 ; >= Y2 ?
JNB gwind4 ; :no good
MOV grwiny1,AX ; set Y1
gwind4 POP AX ; X1
CMP AX,grwinx2 ; >= X2 ?
JNB gwind5 ; :no good
MOV grwinx1,AX ; set X1
gwind5 JMP BX ; "return
xplot POP BX ; Plot
POP DX ; Y-pos
POP CX ; X-pos
PUSH BX ; restore return addr
MOV AH,#$0C ; set point, AL is color
doplot OR CX,CX ; test X-pos (do clipping)
JS noplot ; negative: clip it
ADD CX,grwinx1 ; add window offset
CMP CX,grwinx2 ; outside window ?
JA noplot ; yes: clip it
OR DX,DX ; test Y-pos
JS noplot ; negative: clip it
ADD DX,grwiny1 ; add window offset
CMP DX,grwiny2 ; outside window ?
JA noplot ; yes: clip it
PUSH BP
INT bioscrt ; do plot
POP BP
noplot RET ; "
xdraw MOV AH,#$0C ; Line. AL is color
MOV collin,AX ; set color, command:plot
POP DI ; return addr
POP AX ; Y2
POP DX ; X2
POP BX ; Y1
MOV lnypos,BX ; store it
CALL getdelta ; calculate direction
MOV lnydir,CX ; store it
CALL iabs ; ABS(AX)
XCHG AX,DX ; DX:=Y-distance YD
POP BX ; X1
PUSH DI ; restore ret addr
MOV lnxpos,BX ; store X1
CALL getdelta ; calculate direction
MOV lnxdir,CX ; store it
CALL iabs ; ABS(AX)
MOV BX,AX ; BX:=X-distance XD
CMP BX,DX ; XD <= YD ?
JLE lnyline ; yes:Y-oriented line
MOV AX,DX ; X-oriented line
ADD AX,AX
SUB AX,BX
MOV lndda,AX ; DDA:=YD+YD-XD
MOV CX,BX ; XD -> count
INC CX
lnxloop CALL lnplot ; plot point with clipping
MOV AX,lndda ; test DDA
OR AX,AX
JLE lnxnostp ; :no step
ADD AX,DX ; DDA:=DDA+YD+YD-XD-XD
ADD AX,DX
SUB AX,BX
SUB AX,BX
MOV lndda,AX
MOV AX,lnydir ; Ypos:=Ypos+Ydir
ADD lnypos,AX
JMP.b lnxcont ; '
lnxnostpADD AX,DX ; DDA:=DDA+YD+YD
ADD AX,DX
MOV lndda,AX
lnxcont MOV AX,lnxdir ; Xpos:=Xpos+Xdir
ADD lnxpos,AX
LOOP lnxloop ; another step
RET ; '
lnyline MOV AX,BX ; Y-oriented line
ADD AX,AX
SUB AX,DX
MOV lndda,AX ; DDA:=XD+XD-YD
MOV CX,DX ; YD -> count
INC CX
lnyloop CALL lnplot ; plot point with clipping
MOV AX,lndda ; test DDA
OR AX,AX
JLE lnynostp ; :no step
ADD AX,BX ; DDA:=DDA+XD+XD-YD-YD
ADD AX,BX
SUB AX,DX
SUB AX,DX
MOV lndda,AX
MOV AX,lnxdir ; Xpos:=Xpos+Xdir
ADD lnxpos,AX
JMP.b lnycont ; '
lnynostpADD AX,BX ; DDA:=DDA+XD+XD
ADD AX,BX
MOV lndda,AX
lnycont MOV AX,lnydir ; Ypos:=Ypos+Ydir
ADD lnypos,AX
LOOP lnyloop ; :another one
RET ; "
getdeltaXOR CX,CX ; calculate direction -> CX
SUB AX,BX ; compare them
JZ deltazer ; equal: CX=0
JS deltaneg ; :negative
INC CX ; positive: CX=1
RET ; '
deltanegDEC CX ; negative: CX=-1
deltazerRET ; "
lnplot PUSH CX ; Set line point with clipping
PUSH DX ; save count, YD
MOV AX,collin ; color, command
MOV CX,lnxpos ; Xpos
MOV DX,lnypos ; Ypos
CALL doplot ; plot that point
POP DX
POP CX
RET ; "
xsound MOV BX,AX ; Sound
MOV AX,#$34DD ; for frequency calculation
MOV DX,#$0012
CMP DX,BX ; frequency < 12 Hz ?
JNB sounddis ; :might damage speaker
DIV BX ; calculate timer value
MOV BX,AX
IN AL,timerflg ; sound on ?
TEST AL,#$03
JNZ soundon ; :yes
OR AL,#$03 ; switch it on
OUT timerflg,AL
MOV AL,#$B6 ; set frequency
OUT timercmd,AL
soundon MOV.B AL,BL ; set freq lo
OUT timerfrq,AL
MOV.B AL,BH ; set freq hi
OUT timerfrq,AL
sounddisRET ; "
xnosoundIN AL,timerflg ; NoSound
AND AL,#$FC ; switch it off
OUT timerflg,AL
RET ; "
xgetmem XCHG AX,CX ; GetMem, AX=length requested
POP BX ; ret addr
POP DI ; dest ptr
JMP.b new1 ; "like New
hpconv PUSH AX ; change representation:
PUSH CX ; AX=ofs 0..15, BX=seg
MOV CL,#$04
SHR AX,CL
ADD BX,AX ; seg:=seg+ofs DIV 16
POP CX
POP AX
AND AX,#$000F ; ofs:=ofs MOD 16
RET ; "
hpcmp CMP BX,DX ; Compare BX:AX with DX:CX
JNZ hpcmp1
CMP AX,CX
hpcmp1 RET ; "
hpadd ADD AX,CX ; BX:AX + DX:CX -> BX:AX
ADD BX,DX
JMP hpconv ; "change representation
hplen ES: ; Get length of entry
MOV AX,[DI]$04
ES:
MOV BX,[DI]$06
PUSH AX
OR AX,BX ; test if end of heap = 0
POP AX
RET ; "
xnew POP BX ; New
new1 POP ES ; dest ptr
PUSH BX ; restore ret
MOV hpdstpt,DI ; save addr dest ptr
MOV hpdstpt1,ES
MOV AX,CX ; number of bytes wanted
ADD AX,#$0007 ; +7
MOV BX,#$1000 ; carry ?
JB new2 ; :yes
XOR BX,BX ; no carry
new2 AND AL,#$F8 ; space allocated is n*8
CALL hpconv ; convert to seg,ofs
MOV CX,AX ; needed size
MOV DX,BX
MOV hplast,#$0022 ; last entry
MOV hplast1,DS
LES DI,hpstrt ; get start of heap
newseek CALL hplen ; get len of entry
JZ newend ; :end of heap
CALL hpcmp ; sufficient size ?
JNB newfnd ; :large enough
MOV hplast,DI ; remember last entry
MOV hplast1,ES
ES: ; get next entry
LES DI,[DI]
JMP newseek ; 'continue searching
newfnd CALL newdest ; store ES:DI to dest ptr
JZ newfits ; equal - nothing to crunch
SUB AX,CX ; calculate superfluous mem
SBB BX,DX
AND AX,#$000F
JMP.b newrem ; 'mark the remainder as free
newfits ES: ; get next entry
LES DI,[DI]
JMP.b newlink ; 'set link to it
newend CALL newdest
MOV AX,DI
MOV BX,ES
CALL hpadd ; sum up sizes
MOV hptop,AX ; new heap top
MOV hptop1,BX
PUSH CX ; save size of entry
PUSH DX
MOV CX,AX ; test if heap overflow
MOV DX,BX
MOV AX,SP ; heap top - stack top
MOV BX,SS
SUB BX,#$0E
CALL hpconv ; convert sp
XOR AX,AX
CALL hpcmp ; compare them
POP DX
POP CX
JA newmemok ; :ok
JMP chkstk1 ; 'Error FF - memory overflow
newmemokXOR AX,AX ; mark next entry
XOR BX,BX ; as end of heap
newrem PUSH BX ; save length
PUSH AX
ES: ; save ptr to next entry
PUSH [DI]$02
ES:
PUSH [DI]
MOV AX,DI ; try to put together with
MOV BX,ES ; next entry
CALL hpadd ; calc end of this entry
MOV DI,AX ; new top
MOV ES,BX
ES: ; now produce that entry
POP [DI] ; link
ES:
POP [DI]$02
ES:
POP [DI]$04 ; length
ES:
POP [DI]$06
newlink PUSH ES ; save ES
PUSH ES
LES SI,hplast ; store ES:DI in last
ES: ; entry as link to next entry
MOV [SI],DI
ES:
POP [SI]$02
POP ES ; restore ES
RET ; "
newdest PUSH ES ; store ES:DI in dest ptr
PUSH ES
LES SI,hpdstpt
ES:
MOV [SI],DI
ES:
POP [SI]$02
POP ES
RET ; "
xfreememXCHG AX,CX ; FreeMem
POP BX ; ret addr
POP DI ; get ptr
JMP.b disp1 ; 'like Dispose
xdisposePOP BX ; Dispose
disp1 POP ES ; get dest ptr
PUSH BX ; restore ret
MOV AX,CX ; number of bytes
ES:
MOV CX,[DI] ; get value of ptr
ES:
MOV DX,[DI]$02
ADD AX,#$0007 ; size+7
MOV BX,#$1000 ; carry ?
JB disp2 ; :yes
XOR BX,BX ; no carry
disp2 AND AL,#$F8
CALL hpconv
MOV hpesize,AX ; save size of
MOV hpesize1,BX ; entry to erase
LES DI,hpstrt ; start at the beginning
MOV AX,DI
MOV BX,ES
CALL hpcmp ; = entry to erase ?
JNB dispdel ; :no free space before
dispseekES: ; get link
MOV AX,[DI]
ES:
MOV BX,[DI]$02
CALL hpcmp ; compare with our entry
JNB dispfnd
MOV DI,AX ; go to next entry
MOV ES,BX
JMP dispseek ; 'continue
dispfnd PUSH ES ; ES:DI last entry
MOV SI,CX ; DX:CX searched one
MOV ES,DX ; AX:BX current entry
PUSH hpesize1
PUSH hpesize
ES: ; store link in disposed entry
MOV [SI],AX
ES:
MOV [SI]$02,BX
ES: ; store length
POP [SI]$04
ES:
POP [SI]$06
POP ES ; restore ES
ES:
MOV [DI],CX ; store addr in last entry
ES:
MOV [DI]$02,DX
ES:
MOV AX,[DI]$04 ; get length of this entry
ES:
MOV BX,[DI]$06
CALL dispmerg ; try to put them together
JZ dispdone ; :end of heap
ES: ; get addr of next one
LES DI,[DI]
dispdoneES: ; get length of this entry
MOV AX,[DI]$04
ES:
MOV BX,[DI]$06
ES: ; get link of this entry
MOV CX,[DI]
ES:
MOV DX,[DI]$02
JMP.b dispmerg ; 'try to put them together
dispdel MOV hpstrt,CX ; link to next entry
MOV hpstrt1,DX
MOV DI,CX ; ptr to this entry
MOV ES,DX
ES: ; store link to next one
MOV [DI],AX
ES:
MOV [DI]$02,BX
MOV CX,AX ; link for collect
MOV DX,BX
MOV AX,hpesize ; store its len
MOV BX,hpesize1
ES:
MOV [DI]$04,AX
ES:
MOV [DI]$06,BX
; try to merge contiguous entries - thus reducing
; fragmentation of the heap.
dispmergMOV hpmerg,AX ; length of this entry
MOV hpmerg1,BX
ADD AX,DI ; + its address
MOV BX,ES
ADD BX,hpmerg1
CALL hpconv ; convert
CALL hpcmp ; = next entry ?
JNZ disprt ; no:cannot put them together
MOV AX,hptop ; = heap top ?
MOV BX,hptop1
CALL hpcmp
JZ disptop ; yes:set new, lower heap top
PUSH ES ; save ES
MOV SI,CX ; addr of next entry
MOV ES,DX
ES: ; get link to next entry
MOV AX,[SI]
ES:
MOV BX,[SI]$02
ES: ; get length
MOV CX,[SI]$04
ES:
MOV DX,[SI]$06
POP ES ; restore ES
ES: ; store link
MOV [DI],AX
ES:
MOV [DI]$02,BX
MOV AX,hpmerg ; get length
MOV BX,hpmerg1
CALL hpadd ; add them
ES: ; store new length
MOV [DI]$04,AX
ES:
MOV [DI]$06,BX
XOR AX,AX
RET ; '
disptop MOV hptop,DI ; set new heap top
MOV hptop1,ES
PUSH DI ; clear 8 bytes
XOR AX,AX ; = marker for end of heap
CLD
MOV CX,#$0004
REPZ
STOS
POP DI ; restore
XOR AX,AX
disprt RET ; "
xmemavl XOR CX,CX ; MemAvail
XOR DX,DX ; clear sum
XOR SI,SI
LES DI,hpstrt ; get heap start
memav1 CALL hplen ; get length
JZ memav2 ; :end of heap
CALL hpaddcmp ; sum it up, test size
ES: ; get next entry
LES DI,[DI]
JMP memav1 ; 'continue
memav2 MOV AX,SP ; last entry: free space
MOV BX,SS ; between stack and heap
SUB BX,#$10 ; leave some space
CALL hpconv ; convert
XOR AX,AX ; clear offset
SUB BX,hptop1 ; subtract heap top
JB memav3 ; :nothing left
CALL hpaddcmp ; sum it up
memav3 MOV AX,DX ; space left (paragraphs)
RET ; "
hpaddcmpCMP SI,BX ; is it the largest one ?
JNB hpac2 ; :no
MOV SI,BX ; yes, remember its size
hpac2 CALL hpadd ; sum it up
MOV CX,AX ; remember sum
MOV DX,BX
RET ; "
xmaxavl CALL xmemavl ; MaxAvail: do MemAvail
MOV AX,SI ; get largest size
RET ; "
xmark POP BX ; Mark
POP ES ; get ptr
MOV AX,hptop ; Heap top -> ptr
ES:
MOV [DI],AX
MOV DX,hptop1
ES:
MOV [DI]$02,DX
JMP BX ; "return
xreleasePOP BX ; Release
POP ES ; ptr addr
ES: ; get pointer
LES DI,[DI]
MOV hptop,DI ; -> heap top
MOV hpstrt,DI ; -> heap start
MOV hptop1,ES
MOV hpstrt1,ES
XOR AX,AX ; clear 8 bytes in entry
LES DI,hpstrt ; = marker for end of heap
MOV CX,#$0004
CLD
REPZ
STOS
JMP BX ; "return
kbdstat CMP.B lastkey,#$00 ; Get key stat
MOV AL,#$FF ; something there ?
JNZ kbdst2 ; :true
MOV AH,#$01 ; test kbd stat
INT bioskbd
MOV AL,#$00 ; false
JZ kbdst2 ; :nothing available
DEC.B AL ; true
kbdst2 AND AX,#$0001
RET $0001 ; "
kbdget MOV AL,lastkey ; Get KBD char
MOV.B lastkey,#$00 ; clear last key code
OR.B AL,AL
JNZ kbdg3 ; :was full - take it
XOR.B AH,AH ; get key
INT bioskbd
OR.B AL,AL ; test it
JNZ kbdg2 ; :normal char
MOV.B lastkey,AH ; store scan code
MOV AL,#$1B ; return ESC
OR.B AH,AH
JNZ kbdg3
MOV AL,#$03 ; Break !
kbdg2 CMP.B cbreak,#$01 ; Break allowed ?
JNZ kbdg3 ; :no
CMP AL,#$03 ; Break ?
JNZ kbdg3 ; :no
JMP brkmsg ; '
kbdg3 XOR.B AH,AH ; clear hi byte
RET $0001 ; "
crtput POP AX ; Print char to screen
POP DX ; char to be printed
PUSH AX ; restore ret
PUSH DX ; save regs
PUSH BP
PUSH DX
CALL getcpos ; get cursor pos
POP AX ; char to be printed
CMP AL,#$0D ; Carriage Return ?
JNZ crtlf ; :no
MOV.B DL,txwinx1 ; go to left margin
JMP.b crtsetps ; 'set new pos
crtlf CMP AL,#$0A ; Line Feed ?
JNZ crtbs ; :no
INC.B DH ; add 1 to line
CS:
CMP.B DH,txwiny2 ; >= bottom ?
JB crtsetps ; :no, set pos
JMP.b crtscrol ; 'scroll up
crtbs CMP AL,#$08 ; Backspace ?
JNZ crtbell ; :no
CMP.B DL,txwinx1 ; at the left ?
JZ crtsetps ; yes, no change
DEC.B DL ; go back
JMP.b crtsetps ; 'set cursor pos
crtbell CMP AL,#$07 ; Bell ?
JNZ crtchar ; :no
MOV AH,#$0E ; write char
XOR.B BH,BH
INT bioscrt
JMP.b crttest ; 'no pos change
crtchar PUSH DX ; remember position - put char
MOV AH,#$09 ; write char
XOR.B BH,BH ; screen page 0
MOV CX,#$0001 ; 1 char
MOV.B BL,attcur ; current attribute
INT bioscrt ; do it
POP DX ; restore pos
INC.B DL ; go right one char
CS:
CMP.B DL,txwinx2 ; = right margin ?
JB crtsetps ; :no, set new pos
MOV.B DL,txwinx1 ; go to the left
INC.B DH ; next line
CS:
CMP.B DH,txwiny2 ; = bottom ?
JB crtsetps ; no: set pos
crtscrolDEC.B DH ; scroll up
PUSH DX ; save pos
MOV AX,#$0601 ; scroll up one line
MOV.B BH,attcur ; att for empty line
MOV CX,txwinx1 ; position upper left
CS:
MOV DX,txwinx2 ; position lower right
DEC.B DH
DEC.B DL
INT bioscrt ; do it
POP DX
crtsetpsMOV AH,#$02 ; set cursor pos
XOR.B BH,BH ; screen 0
INT bioscrt ; do it
crttest POP BP
CMP.B cbreak,#$01 ; test for break ?
JNZ crtnobrk ; :no
DEC SP
CALL kbdstat ; get key stat
JZ crtnobrk ; :nothing
DEC SP
CALL kbdget ; get key
CMP AL,#$13 ; ^S ?
JNZ crtnobrk ; :no
DEC SP
CALL kbdget ; get key
crtnobrkPOP AX ; restore char
RET ; "
lstput POP AX ; put to LST
POP DX ; get char
PUSH AX ; restore ret
MOV AH,#$05 ; operation
JMP.b dos ; 'do MS-DOS
NOP ; Courtesy of LINK
auxput POP AX ; put to AUX
POP DX ; get char
PUSH AX ; restore ret
MOV AH,#$04 ; operation
JMP.b dos ; 'do MS-DOS
NOP
auxget MOV AH,#$03 ; Get from AUX
CALL dos ; do MS-DOS
XOR.B AH,AH ; clear hi byte
RET $0001 ; '
dos CMP.B AH,#$3D ; do MS-DOS-operation
JZ openfil ; :Open file
CMP.B AH,#$3C
JZ openfil ; :Create file
CMP.B AH,#$3E
JZ closfil ; :Close file
CMP.B AH,#$80
JZ closeall ; :Close all files
dodos PUSH BP ; save this
INT msdos
POP BP ; restore it
RET ; '
openfil PUSH SI ; put file into table of
PUSH CX ; open files, open/create it
MOV SI,filtabpt ; start of file table
MOV CX,filemax
openlp CMP [SI],#$00 ; empty ?
JZ opendoit ; :yes
INC SI
INC SI
LOOP openlp ; another one
POP CX ; restore regs
POP SI
MOV AX,#$0004 ; Error: file not open
STC
RET ; '
opendoitPOP CX ; get offset to filename
PUSH DS ; save DS
PUSH ES ; ES -> DS
POP DS
CALL dodos ; open file
POP DS ; restore DS
JB openerr ; :error
MOV [SI],AX ; file handle -> file list
openerr POP SI ; restore
RET ; "
closfil PUSH CX ; Close file
PUSH SI
MOV SI,filtabpt ; start of file table
MOV CX,filemax
closlp CMP [SI],BX ; is it this file ?
JNZ clsother ; :no
MOV [SI],#$0000 ; clear its entry
clsotherINC SI ; next one
INC SI
LOOP closlp ; :not yet done
POP SI ; restore regs
POP CX
JMP dodos ; 'do it
closeallMOV SI,filtabpt ; Close all files
MOV CX,filemax
clall1 MOV BX,[SI] ; test all file handles
OR BX,BX
JZ clall2 ; :not open
MOV AH,#$3E ; close file
CALL dodos ; do it
MOV [SI],#$0000 ; store a 0: closed
clall2 INC SI ; next one
INC SI
LOOP clall1 ; :continue
RET ; "
initio XOR AX,AX ; Init files, I/O
initioflMOV modeflg,AX ; clear flag
MOV DI,#filetab ; set pt to file tab
MOV filtabpt,DI
MOV filemax,CX ; CX=max number of open files
XOR AX,AX ; clear file list
PUSH DS ; DS -> ES
POP ES
CLD
REPZ
STOS
MOV ES,AX ; segment 0:
ES:
MOV verrhnd,#errhndl ; set vector:
ES: ; error handler
MOV verrhnd1,CS
CALL msspeed ; measure CPU speed
reinit1 MOV.B cbreak,#$00 ; no test for break
reinit2 MOV SI,#inittab ; transfer vectors into table
MOV DI,#vkbdstat
PUSH DS ; DS -> ES
POP ES
PUSH CS ; CS -> DS
POP DS
MOV CX,#$001E ; count
CLD
REPZ ; move it
MOVS
PUSH ES ; restore DS
POP DS
XOR AX,AX ; clear vars
MOV lastkey,AX ; no key stored
MOV errnum,AL ; no error
MOV conbufpt,AX ; no con buf
MOV conbfend,AX
MOV.B conbufln,#$7E
MOV.B coninbuf,#$0D ; mark end of buffer
RET ; "
inittab W kbdstat,kbdget,crtput,lstput,auxput,auxget,crtput,kbdget
W $FFFF,$00C1,$FFFF,$0082,$FFFF,$0043,$FFFF,$00C4,$FFFF
W $00C5,$FFFF,$00C1,$0000,$0000,$0000,$0000,$FFFF,$00C1
W $0000,$0000,$0000,$0000 ; "
errhndl IRET ; "Error handler
conput PUSH BX ; put to CON
PUSH CX ; save registers
PUSH DX
PUSH DI
PUSH SI
XOR.B AH,AH
PUSH AX ; char to put
CALL [vconput] ; put it
conret POP SI ; restore regs
POP DI
POP DX
POP CX
POP BX
RET ; "
keyget PUSH BX ; Get from KBD
PUSH CX ; save regs
PUSH DX
PUSH DI
PUSH SI
DEC SP
CALL [vkbdget] ; do it
JMP conret ; "restore
prints PUSH BP ; Print inline string
MOV BP,SP
XCHG BX,[BP]$02 ; return addr -> BX
prsl CS:
MOV.B AL,[BX] ; get char
INC BX ; go to next one
OR.B AL,AL ; test it
JZ prsend ; 0:end
CALL conput ; put to CRT
JMP prsl ; 'continue
prsend XCHG BX,[BP]$02 ; restore ret to pos after text
POP BP
RET ; "
xwritelnCALL prints ; Writeln CRT
B $0D,$0A,$00 ; print string: CR,LF
RET ; "
upcase CMP AL,#$61 ; UpCase. < a ?
JB upcas1 ; :not lower case
CMP AL,#$7A ; > z ?
JA upcas1 ; yes:not lower case
SUB AL,#$20 ; change to upper
upcas1 RET ; "
whexwordPUSH AX ; Write Hex number: save it
MOV.B AL,AH ; do hi byte
CALL whexbyte ; do it
POP AX ; restore lo byte
whexbytePUSH AX ; write hex byte: save low nibble
ROR.B AL,1 ; get hi nibble
ROR.B AL,1
ROR.B AL,1
ROR.B AL,1
CALL whexnib ; do digit
POP AX ; get lo nibble
whexnib AND AL,#$0F ; mask it
ADD AL,#$90 ; convert to hex - tricky
DAA
ADC AL,#$40
DAA
JMP conput ; "now print it
limstindOR.B AH,AH ; limit string index
JZ limst1 ; :ok
STC ; hi <> 0: error
MOV AX,#$0000 ; clear it
JS limst1 ; negative:0
DEC.B AL ; +:255
limst1 RET ; "
; Init Memory - with inline parms:
; + 0:mode flag
; 1=direct mode 2=no device checking
; 4=test for break 8=set breakpoint interrupt
; + 2:CS for returning to Turbo
; + 4:DS for returning to Turbo
; + 6:CS size
; + 8:DS size
; + A:heap, stack size
; + C:max. heap, stack size
; + E:max. number of open files
; +10:size of std input buffer
; +12:size of std output buffer
initmem CALL readvers ; Init memory - with inline parms
POP SI ; ret addr
MOV AX,CS ; calculate segments
CS:
ADD AX,[SI]$06 ; +CS size
CS:
ADD AX,[SI]$08 ; +DS size
CS:
ADD AX,[SI]$0A ; +heap, stack size
CS:
CMP AX,availmem ; > available memory ?
JBE memin1 ; :no
JMP memerr ; 'Not enough memory
memin1 MOV BX,CS ; CS + CS size -> DS
CS:
ADD BX,[SI]$06
MOV DS,BX
CS: ; DS + DS size -> beg of free mem
ADD BX,[SI]$08
CS:
MOV DX,availmem
SUB DX,BX ; how much is left ?
CS:
CMP DX,[SI]$0C ; > max size ?
JB memin2 ; :no
CS:
MOV DX,[SI]$0C ; limit to max size
memin2 MOV DI,DX ; DI: heap size
MOV AX,#$FFFE ; value for full stack
SUB DX,#$1000 ; 64 K free ?
JNB memin3 ; :yes
MOV AX,DX ; calculate stack size -> AX
ADD AX,#$1000
MOV CL,#$04
SHL AX,CL
XOR DX,DX ; at beg of heap segment
memin3 ADD DX,BX ; + heap segment
MOV SS,DX ; -> SS
MOV SP,AX ; set SP
MOV spval,AX ; remember SP
XOR AX,AX ; clear heap:
MOV hptop,AX ; ptrs to beg of free memory
MOV hptop1,BX
MOV hpstrt,AX
MOV hpstrt1,BX
PUSH DI ; save heap size
LES DI,hpstrt ; init heap:
MOV CX,#$0004 ; mark heap top
CLD
REPZ
STOS
POP DI ; restore heap size
CS: ; return excess memory ?
TEST [SI],#$0001
JNZ memin4 ; direct mode:no
MOV AX,CS ; CS -> ES
MOV ES,AX
ADD BX,DI ; Heap pos + Heap size
SUB BX,AX ; -> top of used memory
MOV AH,#$4A ; change memory blocks
CALL dos ; do MS-DOS
memin4 CS: ; set variables
MOV AX,[SI]$02 ; CS for return
MOV turbocs,AX
CS:
MOV AX,[SI]$04 ; DS for return
MOV turbods,AX
CS:
MOV AX,[SI] ; main flag
CS:
MOV CX,[SI]$0E ; max file count
PUSH CX
PUSH SI
CALL initiofl ; init files, I/O
POP SI
POP CX
MOV DI,#filetab ; get end addr
ADD DI,CX ; of file tab
ADD DI,CX
MOV stdinof,DI ; buffer addr std input
CS:
MOV AX,[SI]$10 ; set std in buffer
MOV stdinsz,AX
ADD DI,AX
OR AX,AX
JZ memin5 ; :no std in file
MOV stdin,#$0000 ; set handle: std in
MOV.B stdinfl,#$00 ; clear flag
memin5 MOV stdoutof,DI ; buffer addr std output
CS:
MOV AX,[SI]$12 ; set std out buffer
MOV stdoutsz,AX
OR AX,AX
JZ memin6 ; :no std out file
MOV stdout,#$0001 ; set handle: std out
MOV.B stdoutfl,#$00 ; clear flag:not open
memin6 ADD SI,#$14 ; set return addr
PUSH SI
XOR AX,AX ; 0 -> ES
MOV ES,AX
ES: ; save div / 0 interrupt
MOV AX,div0vec
MOV svintv,AX
ES:
MOV AX,availmem ; div0vec1
MOV svintv1,AX
ES: ; now set own routine
MOV div0vec,#div0err
ES:
MOV availmem,CS ; div0vec1
TEST modeflg,#$0008 ; set breakpoint int ?
JZ memin7 ; :no
ES:
MOV int3vec,#brkint
ES:
MOV int3vec1,CS
memin7 TEST modeflg,#$0004 ; test for ^C, ^S ?
JZ memin8 ; :no
MOV.B cbreak,#$01 ; set flag
memin8 MOV verror,#$10D0 ; set error vec
XOR AX,AX
MOV errpos2,AX ; clear error pos
MOV ovrpnbuf,AL ; clear ovrlay pathname
MOV CX,stdinsz ; open std in for read
PUSH DS
MOV DI,#stdin
CALL xresettx
MOV CX,stdoutsz ; open std out for write
PUSH DS
MOV DI,#stdout
CALL xrewrttx
MOV.B errio,#$00 ; clear flag
CALL xcrtinit ; CrtInit
RET ; "
readversMOV AH,#$30 ; Read Version Number
CALL dos ; do MS-DOS
OR.B AL,AL ; Version 0 ?
JZ verserr ; yes:error
RET ; '
verserr MOV DX,#msgdos
JMP.b writerr ; '
memerr MOV DX,#msgmem
writerr PUSH CS ; CS -> DS
POP DS
MOV AH,#$09 ; print string
CALL dos ; do MS-DOS
MOV DX,#msgend
MOV AH,#$09 ; print string
CALL dos ; MS-DOS
MOV AH,#$00 ; exit program
CALL dos ; "do MS-DOS
msgmem B "Not enough memory$"
msgdos B "Incorrect DOS version$"
msgend B $0D,$0A,"Program aborted ; "
progend PUSH AX ; End of program
PUSH DS ; AX=0:normal
MOV DI,#stdin ; close std input
CALL xclosetx
PUSH DS
MOV DI,#stdout ; close std output
CALL xclosetx
XOR AX,AX ; restore div / 0 int
MOV ES,AX
MOV AX,svintv
ES:
MOV div0vec,AX
MOV AX,svintv1
ES:
MOV availmem,AX ; div0int1
POP AX ; end flag
TEST modeflg,#$0001 ; direct mode ?
JNZ turbort ; yes: return to Turbo
MOV AH,#$4C ; end process
CALL dos ; 'return to MS-DOS
turbort MOV AH,#$80 ; close all files
CALL dos
PUSH turbocs ; set return addr
MOV AX,#turboret
PUSH AX
PUSH DS ; DS -> ES
POP ES
MOV DS,turbods
RETF ; "return to Turbo
; Uncrunch program
; This routine makes space for overlays, which are not stored
; in the main code file. It makes use of an inline data structure:
; + 0:length of the block to move
; + 2:destination addr of this block
; This makes a list which continues at the end of each block.
uncrunchPOP BX ; return addr
CS:
MOV AX,[BX] ; get block length
OR AX,AX ; 0 ?
JZ uncrdone ; yes:end of list
PUSH DS ; save DS
PUSH CS ; CS -> DS
POP DS
PUSH CS ; CS -> ES
POP ES ; push list on stack
XOR DX,DX ; count blocks
uncrlistMOV AX,[BX] ; get word
OR AX,AX ; end of list ?
JZ uncrstrt ; :yes
PUSH BX ; push offset
ADD BX,AX ; add offset
INC DX ; count entry
JMP uncrlist ; 'continue
uncrstrtMOV CX,BX ; last ptr
POP BX ; get ptr from stack
MOV SI,BX ; +4 -> beginning of block
ADD SI,#$04
MOV DI,[BX]$02 ; get destination addr
CMP SI,DI ; the same ?
JZ uncrno
SUB CX,SI ; block length
ADD SI,CX ; ptrs to last byte in block
ADD DI,CX
DEC SI
DEC DI
STD ; move backwards
REPZ
MOVS.B
uncrno DEC DX ; another block ?
JNZ uncrstrt ; :yes
MOV [BX],#$0000 ; clear the list: uncrunched
POP DS ; restore DS
uncrdoneADD BX,#$04 ; skip inline data
JMP BX ; "return
; Overlay header. Inline parameters:
; + 0:currently valid procedure (ptr into file)
; + 2:name of the overlay file
; + F:start of the procedure code
rdover POP SI ; get return addr
CS: ; DX = file offset of proc wanted
CMP DX,[SI] ; is it already there ?
JNZ ovrread ; :no
ovrcall ADD SI,#$0F ; skip filename
JMP SI ; 'jump into procedure
ovrread PUSH AX ; AX = overlay length
PUSH DX ; save overlay pos
PUSH SI ; save dest addr
MOV DI,#ovrpnbuf ; scan overlay pathname
XOR.B AL,AL ; clear char buffer
ovrscan MOV.B AH,AL ; buffer:last char
MOV.B AL,[DI] ; get char
OR.B AL,AL ; end of name ?
JZ ovrpne ; :yes
INC DI ; next char
JMP ovrscan ; 'continue scanning
ovrpne PUSH DI ; save end pos
OR.B AH,AH ; test last char
JZ ovrnob ; :no path specified
CMP.B AH,#$3A ; : ?
JZ ovrnob ; :yes
CMP.B AH,#$5C ; \ ?
JZ ovrnob ; :yes
MOV.B [DI],#$5C ; store a \ at the end
INC DI
ovrnob INC SI ; set to beg of filename
INC SI
ovrcopy CS: ; get char from filename
MOV.B AL,[SI]
MOV.B [DI],AL ; store in pathname
INC SI
INC DI ; next char
OR.B AL,AL ; end ?
JNZ ovrcopy ; no: continue
MOV AX,#$3D00 ; open file
MOV DX,#ovrpnbuf ; pathname
PUSH DS ; DS -> ES
POP ES
CALL dos ; do it
MOV BX,AX ; file handle
POP DI ; restore parms
POP SI
POP DX
MOV.B [DI],#$00 ; restore overlay pathname
JB ovrerr ; :error
CS: ; store pos as current pos
MOV [SI],DX ; in the overlay header
MOV AX,#$4200 ; seek absolute
XOR.B CH,CH ; pos = DX*256
MOV.B CL,DH
MOV.B DH,DL
XOR.B DL,DL
CALL dos ; do it
POP CX ; restore length
JB ovrerr ; :error
MOV AH,#$3F ; read byte block
LEA DX,[SI]$0F ; destination addr
PUSH DS ; save DS
PUSH CS ; CS -> DS
POP DS
CALL dos ; do it
POP DS ; restore DS
JB ovrerr ; :error
MOV AH,#$3E ; close file
CALL dos
JMP ovrcall ; 'jump into procedure
ovrerr MOV DL,#$F0 ; error: Overlay not found
PUSH SI ; return addr
JMP runerrrt ; "
xovrpathPOP BX ; OvrPath
CALL getpn ; convert string -> ASCIIZ
PUSH BX ; restore ret
MOV SI,#pnbuf ; copy into overlay pathname
MOV DI,#ovrpnbuf
PUSH DS ; DS -> ES
POP ES
MOV CX,#$0020 ; 64 bytes
CLD
REPZ
MOVS
RET ; "
xkeypresDEC SP ; KeyPressed
CALL [vkbdstat] ; check status
RET ; "
xgotoxy POP BX ; GotoXY
POP CX ; column
PUSH BX ; restore ret
MOV.B DL,AL ; row-1
MOV.B DH,CL ; column-1
DEC.B DL ; (Turbo uses origin 1,1)
DEC.B DH
JMP setcpos ; "set position
xparmstrMOV DX,AX ; ParamStr: # wanted string
OR DX,DX ; 0 ?
JZ prmstr1
CALL prmcnt1 ; search in parm line
XCHG AX,BX ; string length -> AX
prmstr1 POP BX ; return addr
SUB SP,AX ; allot space for string
DEC SP
MOV DI,SP ; string dest
PUSH DS ; save DS
PUSH CS ; CS -> DS
POP DS
PUSH SS ; SS -> ES
POP ES
CLD ; move string
STOS.B ; store length
XCHG AX,CX ; -> count
REPZ ; move string
MOVS.B
POP DS ; restore DS
JMP BX ; "return
xparmcntXOR DX,DX ; ParamCount: count string entries
prmcnt1 MOV DI,#paramlin ; parameter line
CS:
MOV.B CL,[DI] ; length of param line
XOR.B CH,CH
INC DI ; beginning of string
XOR BX,BX ; clear parm cnt
prmcsep JCXZ prmcst ; :end of line
CS: ; search char <> tab, space
MOV.B AL,[DI] ; read char
CMP AL,#$20 ; space ?
JZ prmcsep2 ; :continue
CMP AL,#$09 ; tab ?
JNZ prmcst ; no:start of parm
prmcsep2INC DI ; next char
DEC CX ; count length
JMP prmcsep ; 'continue
prmcst MOV SI,DI ; start of parm string
prmcstl JCXZ prmcste ; end of line
CS: ; search char = tab, space
MOV.B AL,[DI] ; get char
CMP AL,#$20 ; space ?
JZ prmcste ; yes: end of parm
CMP AL,#$09 ; tab ?
JZ prmcste ; yes: end of parm
INC DI ; next char
DEC CX ; count length
JMP prmcstl ; 'continue searching
prmcste MOV AX,DI ; end position - beg position
SUB AX,SI
JZ prmcrt ; =0:forget it
INC BX ; count parm
DEC DX ; search another parm ?
JNZ prmcsep ; :yes
prmcrt XCHG AX,BX ; return count, string length
RET ; "
xstrint MOV.B fmtfield,CL ; Str(Integer): max length
MOV fmtsdst,DI ; dest ofs
POP BX ; ret addr
POP fmtsdst1 ; dest seg
POP CX ; format
POP AX ; number
PUSH BX ; restore ret
PUSH CX ; save format
MOV BX,#pnbuf ; dest buffer
CALL intasc ; Integer -> ASCII
JMP.b str1 ; 'store in string
xstrrealMOV.B fmtfield,CL ; Str(Real): max length
MOV fmtsdst,DI ; dest ofs
POP BX ; ret addr
POP fmtsdst1 ; dest seg
POP DX ; format 2
POP AX ; format 1
MOV DI,#recvbuf ; buffer for real number
POP [DI] ; pop real number
POP [DI]$02
POP [DI]$04
PUSH BX ; restore ret
PUSH AX ; format 1
XCHG AX,CX ; format 2 -> AX
MOV BX,#pnbuf ; dest buffer
CALL fmtreal ; Real -> ASCII
str1 POP CX ; format
LES DI,fmtsdst ; ptr to dest string
PUSH DI ; save begin pos
MOV.B DL,fmtfield ; max. field size
XOR.B DH,DH
XCHG AX,BX ; endposition - buffer pos
SUB AX,#pnbuf ; -> length
SUB CX,AX ; max len - len
JBE strcpy0 ; :too long
strpad INC DI ; pad with spaces
ES:
MOV.B [DI],#$20
INC.B DH ; count len
CMP.B DH,DL ; = max len ?
JZ strdone ; yes:done
LOOP strpad ; continue padding
strcpy0 XCHG AX,CX ; num len -> CX
MOV BX,#pnbuf ; source ptr
strcopy MOV.B AL,[BX] ; get char
INC BX ; ptr to next
INC DI
ES:
MOV.B [DI],AL ; store in string
INC.B DH ; pos = max len ?
CMP.B DH,DL
JZ strdone ; yes: end it
LOOP strcopy ; continue
strdone POP DI ; restore dest ptr
ES:
MOV.B [DI],DH ; store length
RET ; "
xvalint XOR.B AL,AL ; Val(Integer)
JMP.b val1 ; '
xvalrealMOV AL,#$01 ; Val(Real)
val1 MOV fmttype,AL ; store type
MOV fmtpdst,DI ; pos ofs
POP BX ; ret addr
POP fmtpdst1 ; pos seg
POP fmtvdst ; dest ofs
POP fmtvdst1 ; dest seg
CALL getstz ; get string from stack
PUSH BX ; restore ret
XOR AX,AX ; no error
MOV BX,#pnbuf
CMP.B [BX],AL ; null string ?
JZ valrt ; :set pos, no change to dest
CMP.B fmttype,AL ; integer ?
JNZ val2 ; :no
CALL ascint ; ASCIIZ -> Integer
JB valerr ; :error
LES DI,fmtvdst ; dest ptr
ES:
MOV [DI],AX ; store result
JMP.b valend ; 'get error pos
val2 MOV DI,#recvbuf ; real dest
CALL ascreal ; ASCIIZ -> Real
JB valerr ; :error
MOV SI,DI
LES DI,fmtvdst ; move real into
CLD ; dest var
MOVS
MOVS
MOVS
valend XOR AX,AX ; no error
CMP.B [BX],AL ; end of buffer reached ?
JZ valrt ; :yes
valerr XCHG AX,BX ; calculate error pos
SUB AX,#pnbuf0
valrt LES DI,fmtpdst ; store error pos
ES:
MOV [DI],AX
RET ; "
getpn MOV CX,#$0040 ; get string from stack
JMP.b gstz1 ; 'max 64 chars
getstz MOV CX,#$007F ; max 127 chars
gstz1 MOV DI,#pnbuf ; dest buffer (redundant!)
POP AX ; ret addr
MOV SI,SP ; ptr to string
SS:
MOV.B DL,[SI] ; get length
XOR.B DH,DH
CMP CX,DX ; > max length ?
JBE gstz2 ; yes, limit it
MOV CX,DX ; take this length
gstz2 INC DX
INC SI ; skip length
MOV DI,#pnbuf ; dest ptr
PUSH DS ; DS -> ES
POP ES
PUSH SS ; SS -> DS
POP DS
CLD
REPZ ; move string into buffer
MOVS.B
PUSH ES ; restore DS
POP DS
MOV.B [DI],#$00 ; store a 0 at the end
ADD SP,DX ; remove string from stack
JMP AX ; "return
xrndmizeMOV AH,#$2C ; Randomize: Get time
CALL dos
MOV rndseed1,CX ; store as random seed
MOV rndseed,DX
RET ; "
xmovevarPOP BX ; Move var - CX = length
MOV DX,DS ; save DS
MOV SI,DI ; source ofs
POP DS ; source seg
POP DI ; dest ofs
POP ES ; dest seg
CLD ; move var
REPZ
MOVS.B
MOV DS,DX ; restore DS
JMP BX ; "return
xblkparmPOP BX ; Copy var -> stack
MOV DX,DS ; save DS
MOV SI,DI ; source ofs
POP DS ; source seg
SUB SP,CX ; make space - CX = len
MOV DI,SP ; dest ptr
PUSH SS ; SS -> ES
POP ES
CLD ; move it
REPZ
MOVS.B
MOV DS,DX ; restore DS
JMP BX ; "return
xfillchrPOP BX ; FillChar
POP CX ; number of bytes
POP DI ; array ofs
POP ES ; array seg
CLD ; fill it - char in AL
REPZ
STOS.B
JMP BX ; "return
xmove XCHG AX,CX ; Move: Count -> CX
MOV DX,DS ; save DS
POP BX ; ret addr
POP DI ; dest ofs
POP ES ; dest seg
POP SI ; source ofs
POP DS ; source seg
CLD ; forward move
CMP SI,DI ; source > dest ?
JNB domove ; yes: move forward
ADD SI,CX ; set ptr to end of block
ADD DI,CX
DEC SI
DEC DI
STD ; move backwards
domove REPZ ; do it
MOVS.B
MOV DS,DX ; restore DS
JMP BX ; "return
xsetregsPOP BX ; Get parms for MsDos, Intr
POP AX ; record seg
PUSH BP ; save regs
PUSH DS
PUSH AX ; save record addr
PUSH DI
PUSH BX ; restore ret
MOV SI,DI ; pointer -> source
MOV DS,AX ; source segment
CLD
LODS ; AX value -> stack
PUSH AX
LODS ; BX value
MOV BX,AX
LODS ; CX value
MOV CX,AX
LODS ; DX value
MOV DX,AX
LODS ; BP value
MOV BP,AX
LODS ; SI value -> stack
PUSH AX
LODS ; DI value
MOV DI,AX
LODS ; DS value
PUSH AX
LODS ; ES value
MOV ES,AX
POP DS ; get DS,SI,AX from stack
POP SI
POP AX
RET ; "
xgetregsPUSHF ; store register values
PUSH ES
PUSH DI
PUSH BP
MOV BP,SP ; stack index
LES DI,[BP]$0A ; get record ptr
CLD ; store AX
STOS
MOV AX,BX ; store BX
STOS
MOV AX,CX ; store CX
STOS
MOV AX,DX ; store DX
STOS
POP AX ; store BP (from stack)
STOS
MOV AX,SI ; store SI
STOS
POP AX ; store DI (from stack)
STOS
MOV AX,DS ; store DS
STOS
POP AX ; store ES (from stack)
STOS
POP AX ; store flags (from stack)
STOS
POP BX ; return addr
ADD SP,#$04 ; clear stack
POP DS ; restore DS,BP
POP BP
JMP BX ; "return
xindchk CMP AX,CX ; array index check
JNB chklim1 ; AX>=limit CX:error
RET ; '
chklim1 MOV DL,#$90 ; Index out of range
JMP.b runerrrt ; "
NOP ; Range check CX<=AX<=DX
xrngchk CMP AX,CX
JL chkrng1 ; not enough:error
CMP AX,DX
JG chkrng1 ; too much:error
RET ; '
chkrng1 MOV DL,#$91 ; Scalar or subrange
JMP.b runerrrt ; "out of range
NOP ; Stack check
xchkstk MOV AX,SP ; CX=space required
SUB AX,CX ; SP-CX
JB chkstk1 ; :error
CMP AX,#$0200 ; getting tight ?
JB chkstk1 ; :yes
MOV CL,#$04 ; convert to segment
SHR AX,CL
MOV CX,SS
ADD AX,CX
CMP AX,hptop1 ; compare with heap top
JB chkstk1 ; below:error
RET ; '
chkstk1 MOV DL,#$FF ; heap / stack collision
JMP.b runerrrt ; "
NOP ; Breakpoint interrupt
brkint POP BX ; return addr
POP AX ; AX
POPF ; Flags
PUSH BX ; restore ret
OR.B cbreak,#$02 ; set flag: int test
DEC SP
CALL kbdstat ; Key pressed ?
JZ brkno ; :no
DEC SP
CALL kbdget ; get that key
brkno AND.B cbreak,#$01 ; restore flag
CMP AL,#$03 ; ^C ?
JZ brkbrk ; :yes
RET ; '
brkbrk POP errpos ; get return addr
ADD errpos,#$02 ; adjust for length of INT3
brkmsg MOV DX,#$0001 ; error: break
JMP.b error ; "
xiores XOR AX,AX ; IOResult: clear AX
XCHG.B AL,errnum ; read error, clear it
RET ; "
xiochk CMP.B errnum,#$00 ; check for I/O-error
JNZ chkioerr
RET ; '
chkioerrMOV.B DL,errnum ; get error number
MOV DH,#$01 ; I/O-error
JMP.b error ; 'display message
div0err POP BX ; Div/0 interrupt
POP AX ; take stuff from stack
POPF
PUSH BX ; restore ret
MOV DL,#$04 ; div / 0
runerrrtPOP errpos ; error pos: return addr
runerr MOV DH,#$02 ; runtime error
error PUSH DX ; save error number
CALL reinit1 ; reinit files, I/O
POP DX ; error number
MOV AX,errpos
SUB AX,#$0003
XCHG AX,errpos2 ; store pos of call
OR AX,AX ; break ?
JNZ errbrk ; :yes
PUSH DX ; save error number
PUSH DX
PUSH errpos2
CALL [verror] ; error handler
POP DX ; restore err number
errbrk CMP.B DH,#$01 ; Break ?
JNB errrunio ; :no
CALL prints
B "^C",$0D,$0A,"User Break",$00
JMP.b errwpos ; '
errrunioMOV.B errio,#$FF ; set flag: real error
JA errrun ; :runtime error
CALL prints
B $0D,$0A,"I/O",$00
JMP.b errerr ; '
errrun CALL prints
B $0D,$0A,"Run-time",$00
errerr CALL prints
B " error ",$00
MOV.B AL,DL ; write error number
CALL whexbyte ; (hex)
errwpos CALL prints
B ", PC=",$00
MOV AX,errpos2 ; display error pos
CALL whexword
CALL prints
B $0D,$0A,"Program aborted",$0D,$0A,$00
MOV AL,#$01 ; Program end: error
JMP progend ; "
RET $0004 ; "Error handler
iabs OR AX,AX ; Abs(Integer):test sign
JNS iabspos ; :positive, zero
NEG AX ; negate it
iabspos RET ; "
irandom PUSH AX ; Random(Integer):save limit
CALL dorandom ; do random
POP BX ; get limit
SHR AX,1 ; div 2
CWD
DIV BX ; do modulo
XCHG AX,DX ; remainder -> AX
RET ; "
dorandomMOV BX,rndseed1 ; do random
MOV CX,rndseed ; get seed
PUSH BX ; save it
PUSH CX
MOV.B AL,BH ; permutate it
MOV.B BH,BL
MOV.B BL,CH
MOV.B CH,CL
XOR.B CL,CL
RCR.B AL,1
RCR BX,1
RCR CX,1
POP AX ; old seed
ADD CX,AX ; add it
POP AX
ADC BX,AX
MOV AX,#$62E9 ; add constant
ADD CX,AX
MOV AX,#$3619
ADC BX,AX
MOV rndseed1,BX ; new seed
MOV rndseed,CX
MOV AX,BX ; result
RET ; "
intasc OR AX,AX ; Integer -> ASCII
JNS iapos ; :positive number
NEG AX ; negate it
MOV.B [BX],#$2D ; store a -
INC BX
iapos XOR.B CH,CH ; flag for leading zeroes
MOV DX,#$2710 ; digit 10000
CALL iadigit
MOV DX,#$03E8 ; digit 1000
CALL iadigit
MOV DX,#$0064 ; digit 100
CALL iadigit
MOV DL,#$0A ; digit 10
CALL iadigit
MOV.B CL,AL ; do it direct
JMP.b iadput ; 'last digit
iadigit XOR.B CL,CL ; clear digit
iadsub INC.B CL ; do successive subtraction
SUB AX,DX ; (faster than DIV)
JNB iadsub ; :continue
ADD AX,DX ; restore remainder
INC.B CH ; flag:now print zeroes
DEC.B CL ; dec number
JNZ iadput ; :ok, non-zero
DEC.B CH ; clear flag
JZ iadnoput
iadput ADD.B CL,#$30 ; convert to ASCII
MOV.B [BX],CL ; store
INC BX
iadnoputRET ; "
asccard XOR AX,AX ; Read integer number:clear result
CMP.B [BX],#$24 ; $ ?
MOV DX,#$000A ; base 10
JNZ acdec ; no:ok
MOV DL,#$10 ; base 16
acloop INC BX ; go to next char
acdec PUSH AX ; save previous result
MOV.B AL,[BX] ; get char
CALL upcase
MOV.B CL,AL ; save char
POP AX ; restore result
SUB.B CL,#$30 ; digit < 0 ?
JB acend ; :yes, end of number
CMP.B CL,#$0A ; > 9 ?
JB acdigok ; no:ok
CMP.B DL,#$10 ; base = 16 ?
JNZ acend ; no:end
SUB.B CL,#$07 ; adjust hex
CMP.B CL,#$0A ; < A ?
JB acend ; :end
CMP.B CL,#$10 ; > F ?
JNB acend ; :end
acdigok PUSH DX ; save base
MUL DX ; do multiplication
POP DX ; restore base
JB acret ; :overflow
XOR.B CH,CH ; clear hi byte
ADD AX,CX ; add digit to result
JNB acloop ; :no overflow, continue
JMP.b acret ; 'end it - overflow
acend CMP.B DL,#$10 ; base 16 ?
JZ acret ; :yes
MOV CX,AX ; CX:=number*2
ADD CX,CX
acret RET ; "
ascint MOV.B CL,[BX] ; read integer:test sign
CMP.B CL,#$2D ; - ?
JNZ aipos ; :no
INC BX ; go to next char
aipos PUSH CX ; save sign
CALL asccard ; read integer
POP CX ; restore sign
JB aichk ; :might be wrong
CMP.B CL,#$2D ; - ?
JNZ ainoneg ; :no
NEG AX ; negate it
ainoneg CLC ; no error
RET ; '
aichk CMP AX,#$8000 ; $8000 ?
JNZ aierr ; no:really error
CMP.B CL,#$2D ; negative ?
JNZ aierr ; no:overflow
RET ; 'yes:ok
aierr STC ; return error
RET ; "
strload POP BX ; get string var -> stack
POP ES ; source seg
MOV SI,DI ; source ofs
ES: ; get length
MOV.B CL,[SI]
XOR.B CH,CH ; -> count
INC CX ; copy length, too
SUB SP,CX ; make space on stack
MOV DI,SP ; dest: new stack top
PUSH DS ; save DS
PUSH ES ; ES -> DS
POP DS
PUSH SS ; SS -> ES
POP ES
CLD ; move string to stack
REPZ
MOVS.B
POP DS ; restore DS
JMP BX ; "return
xstrcn POP SI ; inline string -> stack
CS: ; get length
MOV.B CL,[SI]
XOR.B CH,CH ; -> count
INC CX ; copy length, too
SUB SP,CX ; make space on stack
MOV DI,SP ; dest: new stack top
PUSH DS ; save DS
PUSH CS ; CS -> DS
POP DS
PUSH SS ; SS -> ES
POP ES
CLD ; move string to stack
REPZ ; puts SI to end of string
MOVS.B
POP DS ; restore DS
JMP SI ; "return
strstorePOP DX ; store string from stack
MOV.B AL,CL ; max. length dest
MOV BX,SP ; stack base
SS: ; get length of source string
MOV.B CL,[BX]
XOR.B CH,CH
ADD BX,CX ; add to stack base
INC BX ; +1
SS:
LES DI,[BX] ; get dest ptr
MOV SI,SP ; source ptr:stack
CMP.B CL,AL ; too long ?
JBE stslenok ; :yes
MOV.B CL,AL ; take real length
SS:
MOV.B [SI],AL ; store length on stack
stslenokINC CX ; count
PUSH DS ; save DS
PUSH SS ; SS -> DS
POP DS
CLD ; move string
REPZ
MOVS.B
POP DS ; restore DS
LEA SP,[BX]$04 ; remove string, ptr
JMP DX ; "return
xldarrchPOP BX ; store string on stack
POP ES ; source seg
MOV SI,DI ; source ofs
XOR.B CH,CH ; CL=length
SUB SP,CX ; make space on stack
DEC SP
MOV DI,SP ; dest addr
SS:
MOV.B [DI],CL ; store length
INC DI
PUSH DS ; save DS
PUSH ES ; ES -> DS
POP DS
PUSH SS ; SS -> ES
POP ES
CLD ; move to stack
REPZ
MOVS.B
POP DS ; restore DS
JMP BX ; "return
xstrparmPOP BX ; String insert / delete
XOR.B CH,CH ; CL=length
MOV SI,SP ; string pos
SS:
MOV.B AL,[SI] ; get length
XOR.B AH,AH
SUB AX,CX ; compare length
MOV DI,SI ; dest ptr
ADD DI,AX ; + difference
OR AX,AX ; test direction
JZ strret ; :same length
JNS strins ; :make it shorter
MOV SP,DI ; new stack top
SS: ; insert - make string longer
MOV.B CL,[SI] ; get length
INC CX ; +1 -> count
PUSH DS ; save DS
PUSH SS ; SS -> DS
POP DS
PUSH SS ; SS -> ES
POP ES
CLD ; move it
REPZ
MOVS.B
POP DS ; restore DS
JMP.b strret ; 'return
strins SS: ; delete - shorten string
MOV.B [SI],CL ; store new length
ADD DI,CX ; go to end of string
ADD SI,CX
INC CX ; count+1
PUSH DS ; save DS
PUSH SS ; SS -> DS
POP DS
PUSH SS ; SS -> ES
POP ES
STD ; move it up
REPZ
MOVS.B
POP DS ; restore DS
INC DI ; beginning of string
MOV SP,DI ; = new stack top
strret JMP BX ; "return
csteq CALL cmpstr ; Compare strings =
MOV AX,#$0001
JZ csteq1 ; equal:true
DEC AX ; false
csteq1 OR AX,AX ; set flags
RET ; "
cstne CALL cmpstr ; Compare strings <>
MOV AX,#$0001
JNZ cstne1 ; not equal:true
DEC AX
cstne1 OR AX,AX
RET ; "
cstge CALL cmpstr ; Compare strings >=
MOV AX,#$0001
JNB cstge1 ; larger or equal:true
DEC AX
cstge1 OR AX,AX
RET ; "
cstle CALL cmpstr ; Compare strings <=
MOV AX,#$0001
JBE cstle1 ; less or equal:true
DEC AX
cstle1 OR AX,AX
RET ; "
cstg CALL cmpstr ; Compare strings >
MOV AX,#$0001
JA cstg1 ; larger:true
DEC AX
cstg1 OR AX,AX
RET ; "
cstl CALL cmpstr ; Compare strings <
MOV AX,#$0001
JB cstl1 ; less:true
DEC AX
cstl1 OR AX,AX
RET ; "
cmpstr MOV DI,SP ; Compare strings
ADD DI,#$04 ; ignore 2 ret addr
SS:
MOV.B CL,[DI] ; get len second string
XOR.B CH,CH ; clr hi byte
INC DI ; ptr to beg of string
MOV SI,DI ; calc pos of first string
ADD SI,CX ; add len
SS:
MOV.B DL,[SI] ; get len first string
XOR.B DH,DH ; clr hi byte
INC SI
MOV BX,SI ; calc end pos of string
ADD BX,DX ; for removing it
MOV.B AL,CL ; second len
MOV.B AH,DL ; first len
CMP CX,DX ; compare them
JBE csshrt ; :CX already shorter
XCHG CX,DX ; shorter len -> CX
csshrt OR CX,CX ; null string ?
JZ csnull ; :yes
PUSH DS ; save DS
PUSH SS ; SS -> ES
POP ES
PUSH SS ; SS -> DS
POP DS
CLD ; compare strings on stack
REPZ
CMPS.B
POP DS ; restore DS
JNZ csnoteq ; :not equal - flags are set
csnull CMP.B AH,AL ; compare len
csnoteq POP DX ; return addr
POP CX ; return addr caller
MOV SP,BX ; remove strings from stack
PUSH CX ; restore ret caller
JMP DX ; "return
xconcat POP errpos ; Concat: get return addr
MOV DI,SP ; pos second string
SS:
MOV.B DL,[DI] ; len2
XOR.B DH,DH
MOV SI,DI ; go to start of first string
INC SI
ADD SI,DX
SS:
MOV.B CL,[SI] ; len1
ADD.B DL,CL ; len1+len2 too long ?
JB concerr ; yes:error
SS:
MOV.B [SI],DL ; store new len
XOR.B CH,CH ; put first string in front
SUB DI,CX ; of second
MOV SP,DI ; get space on stack
INC CX ; len1+1 len2 will be overwritten
PUSH DS ; save DS
PUSH SI ; save SI
PUSH SS ; SS -> ES
POP ES
PUSH SS ; SS -> DS
POP DS
CLD ; move first string
REPZ
MOVS.B
MOV DI,SI ; end addr of string 1
POP SI ; end pos of concat string
DEC SI
DEC DI
MOV CX,DX ; resulting length + 1
INC CX
STD
REPZ ; copy it up
MOVS.B
POP DS ; restore DS
INC DI
MOV SP,DI ; remove garbage from stack
JMP [errpos] ; 'return
concerr MOV DL,#$10 ; String too long
JMP runerr ; "
xcopy POP errpos ; Copy: get return addr
CALL limstind ; limit index (length)
MOV CX,AX ; -> CX
POP AX ; string pos
CALL chkstind ; check string index
DEC AX ; pos in string
MOV SI,SP ; addr of string
SS:
MOV.B DL,[SI] ; get length
XOR.B DH,DH
MOV DI,SP ; pos of dest
ADD DI,DX ; + length
SUB DX,AX ; length > pos ?
JBE copnull ; no - return null string
ADD SI,AX ; begin pos of substring
CMP DX,CX ; length > num ?
JBE copend ; :yes, sub goes to end
ADD SI,CX ; pos end of substring
MOV DX,CX ; new length
PUSH DS ; save DS
PUSH SS ; SS -> ES
POP ES
PUSH SS ; SS -> DS
POP DS
STD ; move string
REPZ
MOVS.B
POP DS ; restore DS
JMP.b copmovd ; 'return result
copnull XOR DX,DX ; length = 0
copmovd XCHG SI,DI ; point to beg of new string
copend SS:
MOV.B [SI],DL ; store length
MOV SP,SI ; remove the rest
JMP [errpos] ; "return
xlength POP BX ; Length
MOV DI,SP ; pos of string
SS:
MOV.B AL,[DI] ; get length
XOR.B AH,AH ; clr hi byte
ADD SP,AX ; remove string from stack
INC SP
JMP BX ; "return
xpos POP errpos ; Pos (search substring)
MOV DI,SP ; pos of string
SS:
MOV.B DL,[DI] ; length of
XOR.B DH,DH
INC DI ; begin pos of string
MOV SI,DI ; DI:string scanned
ADD SI,DX ; go to beg of pattern str
SS:
MOV.B CL,[SI] ; get pattern len
XOR.B CH,CH
INC SI ; SI:pattern string
MOV BX,SI ; calc stack end pos
ADD BX,CX ; to remove strings
XOR AX,AX ; find position
SUB DX,CX ; DX:length difference
JB posend ; pattern too long: not found
INC AX ; pos 1
OR CX,CX ; pattern = null ?
JZ posend ; yes: found
INC DX ; number of compares
PUSH DS ; save DS
PUSH SS ; SS -> ES
POP ES
PUSH SS ; SS -> DS
POP DS
CLD ; forward compare
posloop PUSH CX ; save parms
PUSH DI
PUSH SI
REPZ ; compare it
CMPS.B
POP SI ; restore parms
POP DI
POP CX
JZ posdone ; :found
INC AX ; next pos
INC DI
DEC DX ; another search ?
JNZ posloop ; :yes
XOR AX,AX ; not found = 0
posdone POP DS ; restore DS
posend MOV SP,BX ; remove strings
JMP [errpos] ; "return
xinsert MOV.B strdstln,CL ; Insert: max. dest len
MOV strpos,AX ; pos
POP BX ; return addr
POP strtrgt ; target string ptr
POP strtrgt1
MOV strobj,SP ; object string - on stack
MOV strobj1,SS
PUSH BX ; restore ret
LES DI,strtrgt ; target string
PUSH ES ; save ptr for storing result
PUSH DI
PUSH ES ; get target string
CALL strload ; -> stack
MOV AX,#$0001 ; Copy(target,1,pos-1)
PUSH AX
MOV AX,strpos
DEC AX
CALL xcopy ; do it
LES DI,strobj ; get obj string
PUSH ES
CALL strload ; -> stack
CALL xconcat ; concat strings
LES DI,strtrgt ; get target string
PUSH ES
CALL strload ; -> stack
PUSH strpos ; Copy(target,pos,255)
MOV AX,#$00FF
CALL xcopy ; do it
CALL xconcat ; concat strings
MOV.B CL,strdstln ; max. length
CALL strstore ; store string
JMP xlength ; "remove strings
xdelete MOV strnum,AX ; Delete: number of chars
POP BX ; ret addr
POP strpos ; pos in string
POP strtrgt ; target string
POP strtrgt1
PUSH BX ; restore ret
LES DI,strtrgt ; target string
PUSH ES ; save ptr for storing result
PUSH DI
PUSH ES ; get target
CALL strload ; -> stack
MOV AX,#$0001 ; Copy(target,1,pos-1)
PUSH AX
MOV AX,strpos
DEC AX
CALL xcopy ; do it
MOV AX,strpos
ADD AX,strnum ; pos+num
OR.B AH,AH ; test it
JNZ delnorem ; too big: nothing left
LES DI,strtrgt ; get target
PUSH ES
CALL strload ; -> stack
PUSH AX ; Copy(target,pos+num,255)
MOV AX,#$00FF
CALL xcopy ; do it
CALL xconcat ; concat strings
delnoremMOV CL,#$FF ; max len - never a problem
CALL strstore ; store string
RET ; "
xstrch POP BX ; String -> char
POP AX ; get string
DEC.B AL ; test length
JNZ stcherr ; <> 1:error
XCHG.B AL,AH ; char -> AL
JMP BX ; 'return
stcherr MOV errpos,BX ; store error position
MOV DL,#$10 ; String too long
JMP runerr ; "
xchstr MOV SI,SP ; string -> substring
SS: ; (1 char)
MOV.B BL,[SI]$02 ; length of string
XOR.B BH,BH
SS:
MOV AX,[BX_SI]$03 ; get char
MOV.B AH,AL ; char
MOV AL,#$01 ; length = 1
SS:
MOV [BX_SI]$03,AX ; store in string
RET ; "
POP BX ; return string as function result
ADD SP,DX ; forget DX bytes on stack
MOV SI,SP ; pos of string
SS:
MOV.B AL,[SI] ; get its length
CMP.B AL,CL ; = CL (expected length) ?
JZ retstrt ; yes: done
XOR.B AH,AH
ADD SI,AX ; ptr to end of string
MOV DI,SP ; destination (must be longer)
XOR.B CH,CH
ADD DI,CX ; end of destination
XCHG AX,CX ; real length -> count
INC CX ; copy length, too
PUSH DS ; save DS
PUSH SS ; SS -> DS
POP DS
PUSH SS ; SS -> ES
POP ES
STD ; move backwards
REPZ
MOVS.B
POP DS ; restore DS
INC DI
MOV SP,DI ; new stack top
retstrt JMP BX ; "return
chkstindOR.B AH,AH ; Check string index
JNZ stinderr ; > 255:error
OR.B AL,AL
JZ stinderr ; 0:error
RET ; '
stinderrMOV DL,#$11 ; Invalid string index
JMP runerr ; "
; Get set. CL=bytes used, CH=bytes empty at beginning
xldset POP BX ; ret addr
POP DX ; source seg
MOV SI,DI ; source ofs
SUB SP,#$20 ; make space on stack
MOV DI,SP ; dest ptr
PUSH CX ; save crunch byte
PUSH SS ; SS -> ES
POP ES
CLD ; forward
OR.B CH,CH ; empty bytes at beginning
JZ sld2 ; :none
XOR.B AL,AL ; store zeroes
sldclr1 STOS.B
DEC.B CH
JNZ sldclr1 ; :another
sld2 PUSH DS ; save DS
MOV DS,DX ; DX -> DS
REPZ
MOVS.B ; move set
POP DS ; restore DS
POP CX ; restore crunch byte
MOV AH,#$20 ; calculate empty bytes
SUB.B AH,CH ; at end
SUB.B AH,CL
JZ sld3 ; :none
XOR.B AL,AL ; store zeroes
sldclr2 STOS.B
DEC.B AH
JNZ sldclr2 ; :another one
sld3 JMP BX ; "return
sldemptyPOP BX ; Make empty set
SUB SP,#$20 ; 32 bytes on stack
MOV DI,SP ; dest addr
PUSH SS ; SS -> ES
POP ES
MOV CX,#$0010 ; do 32 bytes
XOR AX,AX ; fill with zeroes
CLD
REPZ
STOS
JMP BX ; "return
setincl CALL setindex ; Include element: calc index
SS:
OR.B [BX],AL ; include it
RET ; "
setinrngXCHG AX,CX ; Include range in set
POP BX ; ret addr
POP AX ; lower upper is in CX
PUSH BX ; restore ret
SUB.B CL,AL ; upper < lower ?
JB srngnil
XOR.B CH,CH ; upper-lower -> count
INC CX
MOV.B AH,CL ; save count
CALL setindex ; calc set index (lower)
MOV.B CL,AH ; restore count
srngloopSS: ; include element in set
OR.B [BX],AL
SHL.B AL,1 ; for next bit
JNB srngbit ; :ok
INC BX ; go to next byte
MOV AL,#$01
srngbit LOOP srngloop ; :another element
srngnil RET ; "
setsto MOV SI,SP ; store set. CX as with load
INC SI
INC SI ; source addr
SS:
MOV DI,[SI]$20 ; get dest ofs
SS:
MOV ES,[SI]$22 ; dest seg
MOV.B DL,CH ; # empty bytes
XOR.B DH,DH
ADD SI,DX ; add to source addr
XOR.B CH,CH ; CL = # bytes used
PUSH DS ; save DS
PUSH SS ; SS -> DS
POP DS
CLD ; move used bytes
REPZ
MOVS.B
POP DS ; restore DS
RET $0024 ; "remove set, pointer
xsetparmPOP BX ; Put set as procedure parm
MOV.B DL,CH ; bytes empty at beg
XOR.B DH,DH
XOR.B CH,CH ; bytes used
MOV SI,SP ; source addr
ADD SI,DX ; calc end pos
ADD SI,CX
MOV DI,SP ; dest addr
ADD DI,#$20
CMP SI,DI ; = end pos ?
JZ putsetrt
DEC SI ; go back one byte
DEC DI
PUSH DS ; save DS
PUSH SS ; SS -> ES
POP ES
PUSH SS ; SS -> DS
POP DS ; set is stored as compressed
STD ; local var
REPZ
MOVS.B ; do move it
POP DS ; restore DS
INC DI
MOV SP,DI ; remove empty space
putsetrtJMP BX ; "return
seteq MOV AX,#$0001 ; Set comparison =
JMP.b setcmp ; '
setne XOR AX,AX ; Set comparison <>
setcmp CALL setptrs ; set parms
REPZ ; compare sets
CMPS
MOV DS,DX ; restore DS
JZ setceq ; :equal
XOR AX,#$0001 ; invert result
setceq OR AX,AX ; test result
RET $0040 ; "remove sets from stack
setge XOR AX,AX ; Set comparison >=
JMP.b setcmp2 ; '
setle MOV AX,#$0001 ; Set comparison <=
setcmp2 CALL setptrs ; set parms
DEC AX ; 'test type of comparison
JNZ setc2l ; :<=
XCHG DI,SI ; swap set1, set2
setc2l LODS ; get byte from set2
OR AX,[DI] ; include set1
SCAS ; compare with set1
JNZ setc2g ; :not the same
LOOP setc2l ; :continue
MOV AX,#$0001 ; true
JMP.b setc2rt ; 'end it
setc2g XOR AX,AX ; false
setc2rt MOV DS,DX ; restore DS
OR AX,AX ; test result
RET $0040 ; "remove sets from stack
sunion CALL setptrs ; Set union: set parms
sunion1 LODS ; get byte set 2
OR AX,[DI] ; include set 1
STOS ; store in set 1
LOOP sunion1 ; :continue
MOV DS,DX ; restore DS
RET $0020 ; "remove set 2 from stack
sdiff CALL setptrs ; Set difference: set parms
sdiff1 LODS ; get byte set 2
NOT AX ; invert it
AND AX,[DI] ; take it from set 1
STOS ; store in set 1
LOOP sdiff1 ; :continue
MOV DS,DX ; restore DS
RET $0020 ; "remove set 2 from stack
sinter CALL setptrs ; Set intersection: set parms
sinter1 LODS ; get byte set 2
AND AX,[DI] ; intersect set 1
STOS ; store in set 1
LOOP sinter1 ; :continue
MOV DS,DX ; restore DS
RET $0020 ; "remove set 2 from stack
xsetin MOV BX,SP ; Set IN operation
SS: ; pos of set
MOV AX,[BX]$22 ; get parm
OR.B AH,AH ; > 255 ?
JZ setintst ; no: ok
XOR AX,AX ; false
JMP.b setnotin ; 'return result
setintstCALL setindex ; calc set index
SS:
AND.B AL,[BX] ; test set element
MOV AX,#$0000 ; false
JZ setnotin ; not set: false
INC AX ; true
setnotinOR AX,AX ; test result
RET $0022 ; "remove set, parm
setindexMOV.B BL,AL ; Calculate set index, mask
XOR.B BH,BH ; BX = bit number
MOV CL,#$03 ; bit number DIV 8
SHR BX,CL
ADD BX,#$04 ; +SP+4 -> set ptr
ADD BX,SP
MOV.B CL,AL ; bit number MOD 8
AND.B CL,#$07
MOV AL,#$01 ; create bit mask
SHL.B AL,CL
RET ; "
setptrs MOV SI,SP ; Set parms for set ops
ADD SI,#$04 ; ptr set 2
MOV DI,SP ; skip two ret addrs
ADD DI,#$24 ; ptr set 1
MOV DX,DS ; save DS
PUSH SS ; SS -> ES
POP ES
PUSH SS ; SS -> DS
POP DS
MOV CX,#$0010 ; count: 32 bytes
CLD
RET ; "
ptreq CMP AX,BX ; Compare pointers DX:AX = CX:BX
MOV AX,#$0000 ; false
JNZ ptreqno ; :not equal
CMP DX,CX
JNZ ptreqno ; :not equal
INC AX ; true
ptreqno OR AX,AX ; set flags
RET ; "
ptrne CMP AX,BX ; Compare pointers <>
MOV AX,#$0001 ; true
JNZ ptrne1 ; not equal: true
CMP DX,CX
JNZ ptrne1 ; not equal: true
DEC AX ; false
ptrne1 OR AX,AX ; set flags
RET ; "
; *** Real Operations ***
; #1 #2 register usage
; AX CX LSB, exponent
; BX SI mantissa
; DX DI MSB, sign
resub MOV resign,#$8000 ; Do real subtraction
JMP.b ra1 ; '
readd MOV resign,#$0000 ; Do real addition
ra1 OR.B CL,CL ; second = 0 ?
JZ raret ; :yes, done
XOR DI,resign ; change sign2, if sub
OR.B AL,AL ; first = 0 ?
JNZ ranotriv ; :no
raretn2 MOV AX,CX ; second -> result
MOV BX,SI
MOV DX,DI
raret RET ; '
ranotrivCMP.B AL,CL ; compare exponents
JBE ranoswap ; AL <= CL !
XCHG AX,CX ; otherwhise swap numbers
XCHG BX,SI
XCHG DX,DI
ranoswapMOV.B cvdecexp,CL ; save exp2
SUB.B CL,AL ; exponent difference
CMP.B CL,#$28 ; first number too small ?
JB ranoundr ; :no
MOV.B CL,cvdecexp ; restore exp2
JMP raretn2 ; 'second -> result
ranoundrMOV resign,DI ; save MSB 2
AND.B resave,#$80 ; mask out mantissa
MOV remant,DI ; save MSB 2
XOR.B remant1,DH
OR DI,#$8000 ; remove sign
OR.B DH,#$80 ; remove sign
raadj16lCMP.B CL,#$10 ; shift first in 16-bit-steps
JB raadj8 ; :done
MOV.B AH,BH ; do it.
MOV BX,DX ; first num is shifted to make
XOR DX,DX ; exp1 = exp2
SUB.B CL,#$10
JMP raadj16l ; 'try again
raadj8 CMP.B CL,#$08 ; shift first in 8-bit-steps
JB raadj8l ; :done
MOV.B AH,BL ; do it
MOV.B BL,BH
MOV.B BH,DL
MOV.B DL,DH
XOR.B DH,DH
SUB.B CL,#$08 ; count down difference
raadj8l OR.B CL,CL ; test difference
JZ raadjend ; :adjustment done
raadj1l SHR DX,1 ; shift right in 1-bit-steps
RCR BX,1
RCR.B AH,1
DEC.B CL
JNZ raadj1l ; :continue
raadjendMOV AL,cvdecexp ; get exp2
TEST.B remant1,#$80 ; test sign
JNZ radosub ; :negative
ADD.B AH,CH ; add mantissa
ADC BX,SI
ADC DX,DI
JNB rasign ; :ok
RCR DX,1 ; do normalization
RCR BX,1
RCR.B AH,1
INC.B AL ; inc exponent
JNZ rasign ; :ok
STC ; overflow error
RET ; '
radosub XCHG.B AH,CH ; exchange numbers
XCHG BX,SI
XCHG DX,DI
SUB.B AH,CH ; subtract numbers
SBB BX,SI
SBB DX,DI
JNB ranoneg ; :no underflow
XOR.B resave,#$80 ; change sign
NOT.B AH ; negate mantissa
NOT BX ; = inverted mantissa + 1
NOT DX
ADD.B AH,#$01
ADC BX,#$00
ADC DX,#$00
ranoneg MOV CL,#$05 ; normalize number
ranrm8l OR.B DH,DH ; upper byte empty ?
JNZ ranrm1l ; :no
MOV.B DH,DL ; shift left 8 bits
MOV.B DL,BH
MOV.B BH,BL
MOV.B BL,AH
XOR.B AH,AH
SUB AL,#$08 ; sub 8 from exp
JB razero ; :underflow, return 0
DEC.B CL ; count down bytes
JNZ ranrm8l ; :another one
JMP.b razero ; 'underflow
ranrm1l TEST.B DH,#$80 ; Mantissa MSB must be 1
JNZ rasign ; :yes, done
SHL.B AH,1 ; shift left 1 bit
RCL BX,1
RCL DX,1
DEC.B AL ; sub 1 from exp
JNZ ranrm1l ; :ok
razero XOR AX,AX ; underflow, return zero
XOR BX,BX
XOR DX,DX
RET ; '
rasign AND.B DH,#$7F ; clear MSB mantissa
XOR.B DH,resave ; set correct sign
RET ; "
remult OR.B CL,CL ; Real multiplication: second=0 ?
JZ rmzero ; yes: return zero
OR.B AL,AL ; first=0 ?
JZ rmret ; yes: done
ADD.B AL,CL ; add exponents
CALL testexp ; test exponent
MOV remul11,AX ; save first number
MOV remul12,BX
MOV remul13,DX
XOR.B AH,AH ; clear result
XOR BX,BX
XOR DX,DX
MOV DI,#remul21 ; ptr to second mantissa
MOV CL,#$05 ; do 5 bytes
rmbyt INC DI ; get byte from second mantissa
MOV.B CH,[DI]
OR.B CH,CH ; 0 ?
JNZ rmdomul ; no: do multiplication
MOV.B AH,BL ; just shift result
MOV.B BL,BH ; 8 bits right
MOV.B BH,DL
MOV.B DL,DH
XOR.B DH,DH
JMP.b rmnxtbyt ; 'next step
rmdomul MOV SI,#$0008 ; do 8 bits
rmmul RCR.B CH,1 ; get bit
JNB rmbit0 ; :not set
ADD.B AH,remul11a ; add mantissa 1 to result
ADC BX,remul12
ADC DX,remul13
rmbit0 RCR DX,1 ; shift result 1 bit right
RCR BX,1
RCR.B AH,1
DEC SI ; another bit ?
JNZ rmmul ; :yes
rmnxtbytDEC.B CL ; another byte ?
JNZ rmbyt ; :yes
XCHG AX,CX ; save AX, CL
LAHF ; save flags
TEST.B DH,#$80 ; already normalized ?
JNZ rmnoadj ; :yes
SAHF ; restore flags
RCL.B CH,1 ; shift 1 bit left
RCL BX,1
RCL DX,1
OR.B CL,CL ; check exp
JZ rmnoadj ; :underflow
DEC.B CL ; sub 1 from exp
rmnoadj XCHG AX,CX ; restore AX, CL
XOR.B DH,resave ; set sign
OR.B AL,AL ; test exponent
JNZ rmret ; :ok
rmzero XOR AX,AX ; return zero
XOR BX,BX
XOR DX,DX
rmret RET ; "
rediv OR.B AL,AL ; Real division
JZ rmret ; first=0:done
SUB.B AL,CL ; sub exponents
CMC
CALL testexp ; test exponent
MOV remul11,AL ; save exponent
MOV DI,#remul21b ; ptr to dest
MOV CL,#$05 ; 5 bytes
MOV SI,#$0008 ; 8 bits
rdloop CMP DX,remul23 ; compare with num2
JNZ rdcmp
CMP BX,remul22
JNZ rdcmp
CMP.B AH,remul21a
rdcmp JB rdshft ; below:no subtraction
SUB.B AH,remul21a ; do subtraction
SBB BX,remul22
SBB DX,remul23
rdshft CMC ; invert carry
RCL.B CH,1 ; shift into result
DEC SI ; another bit ?
JNZ rdbit ; :yes
MOV.B [DI],CH ; store result byte
DEC.B CL ; another byte ?
JZ rdlast ; :no
DEC DI ; next one
MOV SI,#$0008 ; 8 bits again
rdbit SHL.B AH,1 ; shift left mantissa
RCL BX,1
RCL DX,1
JNB rdloop ; :normal step
SUB.B AH,remul21a ; carry: no comparison necessary
SBB BX,remul22 ; do subtraction
SBB DX,remul23
CLC
JMP rdshft ; 'shift in result
rdlast SHL.B AH,1 ; do last shift
RCL BX,1
RCL DX,1
JB rdshft2 ; :ok
CMP DX,remul23 ; test last step
JNZ rdcmp2
CMP BX,remul22
JNZ rdcmp2
CMP.B AH,remul21a
rdcmp2 CMC
rdshft2 MOV CX,remul11 ; get result
MOV BX,remul12
MOV DX,remul13
LAHF ; save flags
TEST.B DH,#$80 ; normalized ?
JNZ rdnrm ; :yes
SAHF ; get flags
RCL.B CH,1 ; shift left mantissa
RCL BX,1
RCL DX,1
JMP.b rdok ; 'get sign, test exponent
rdnrm INC.B CL ; inc exponent
JNZ rdok ; :ok
STC ; overflow error
RET ; '
rdok JMP rmnoadj ; "set sign, test exponent
testexp JB texovr ; test exponent: overflow ?
ADD AL,#$80 ; set offset again
JB texok ; :ok
POP BX ; forget return addr
XOR AX,AX ; return zero: underflow
XOR BX,BX
XOR DX,DX
RET ; '
texovr ADD AL,#$80 ; set offset again
JNB texok ; :no error
POP BX ; forget return addr
STC ; overflow error
RET ; '
texok MOV remul21,CX ; save LSB2
MOV CX,DX ; get sign
XOR CX,DI
NOT.B CH ; invert it
AND.B CH,#$80 ; mask sign
MOV.B resave,CH ; save sign
OR.B DH,#$80 ; set mantissa MSB
OR DI,#$8000
MOV remul22,SI ; store mantissa 2
MOV remul23,DI
RET ; "
readd2 PUSH DI ; add, keep second number
PUSH SI
PUSH CX
CALL readd ; do addition
POP CX
POP SI
POP DI
RET ; "
resub2 PUSH DI ; subtract, keep second number
PUSH SI
PUSH CX
CALL resub ; do subtraction
POP CX
POP SI
POP DI
RET ; "
remult2 PUSH DI ; multiply, keep second number
PUSH SI
PUSH CX
CALL remult ; do multiplication
POP CX
POP SI
POP DI
RET ; "
rediv2 PUSH DI ; divide, keep second number
PUSH SI
PUSH CX
CALL rediv ; do division
POP CX
POP SI
POP DI
RET ; "
recmp PUSH DX ; Real comparison
XOR DX,DI ; compare signs
POP DX ; restore
JNS rcsign ; :signs are the same
PUSH DX ; different signs: get flag
RCL DX,1
POP DX
RET ; '
rcsign TEST.B DH,#$80 ; sign ?
JZ rcnum ; :positive
CALL rcnum ; compare
JZ rcdiff ; equal: don't invert
CMC ; invert flags
RET ; '
rcnum CMP.B AL,CL ; compare exponents
JNZ rcdiff
OR.B AL,AL ; zero ?
JZ rcdiff ; yes:equal
CMP DX,DI ; compare mantissa MSB
JNZ rcdiff
CMP BX,SI
JNZ rcdiff
CMP.B AH,CH ; compare mantissa LSB
rcdiff RET ; "
intreal OR AX,AX ; Int(Integer) -> Real
JNZ irnot0 ; :not zero
XOR BX,BX ; return zero
XOR DX,DX
RET ; '
irnot0 MOV.B BH,AH ; put sign
MOV DX,AX
OR DX,DX ; test sign
JNS irpos ; :positive
NEG DX ; negate number
irpos MOV AX,#$0090 ; exp, cleared LSB mantissa
OR.B DH,DH ; test high byte
JNZ ir16bits ; :not zero
MOV AL,#$88 ; speed it up...
XCHG.B DL,DH ; shift 8 bits
ir16bitsOR DX,DX ; test mantissa MSB
JS irdone ; :ok, normalized
irnrm DEC.B AL ; count down exponent
SHL DX,1 ; shift it left
JNS irnrm ; :continue
irdone OR.B BH,BH ; negative ?
JS irpos2 ; :yes
AND.B DH,#$7F ; set positive flag
irpos2 XOR BX,BX ; clear rest of mantissa
RET ; "
reint CMP AL,#$A8 ; Int(Real) - inefficient !
JNB riret ; exponent too big - done
MOV CX,AX ; save number
MOV SI,BX
MOV DI,DX
XOR.B AH,AH ; clear mask
XOR BX,BX
XOR DX,DX
SUB.B CL,#$80 ; exponent-offset
JBE rizero ; :underflow, return zero
rim16l CMP.B CL,#$10 ; shift 16 bit ?
JB rim8 ; :done
MOV.B AH,BH ; shift right 16 bit
MOV BX,DX
MOV DX,#$FFFF ; put into mask
SUB.B CL,#$10 ; count down
JMP rim16l ; 'try again
rim8 CMP.B CL,#$08 ; shift 8 bit ?
JB rim1 ; :done
MOV.B AH,BL ; shift right 8 bit
MOV.B BL,BH
MOV.B BH,DL
MOV.B DL,DH
MOV DH,#$FF ; put into mask
SUB.B CL,#$08 ; count down
rim1 OR.B CL,CL ; shift single bits ?
JZ riand ; :done
rim1l STC ; shift in a 1
RCR DX,1 ; shift right 1 bit
RCR BX,1
RCR.B AH,1
DEC.B CL ; count down
JNZ rim1l ; :continue shifting
riand AND DX,DI ; get result:
AND BX,SI ; mask away the fraction
AND.B AH,CH
riret RET ; '
rizero XOR.B AL,AL ; return zero
RET ; "
refrac PUSH DX ; Frac - inefficient !
PUSH BX ; save original number
PUSH AX
CALL reint ; Int
MOV CX,AX ; result -> second number
MOV SI,BX
MOV DI,DX
POP AX ; restore number
POP BX
POP DX
JMP resub ; "do subtraction
xldreal POP BX ; Load real number onto stack
POP ES ; ptr
ES:
PUSH [DI]$04 ; push that number
ES:
PUSH [DI]$02
ES:
PUSH [DI]
JMP BX ; "return
xrealcn POP BX ; Get real constant (inline)
CS: ; get return addr
PUSH [BX]$04 ; push that number
CS:
PUSH [BX]$02
CS:
PUSH [BX]
ADD BX,#$06 ; skip constant
JMP BX ; "return
xstorealPOP BX ; Store real number
POP AX ; get number
POP CX
POP DX
POP DI ; get dest ptr
POP ES
ES: ; store number
MOV [DI],AX
ES:
MOV [DI]$02,CX
ES:
MOV [DI]$04,DX
JMP BX ; "return
xadd POP errpos ; Add real numbers
POP CX ; second number
POP SI
POP DI
POP AX ; first number
POP BX
POP DX
CALL readd ; do addition
retest JB reovrerr ; :error
repush PUSH DX ; store result on stack
PUSH BX
PUSH AX
JMP [errpos] ; 'return
reovrerrMOV DL,#$01 ; Floating point overflow
JMP runerr ; "
xsub POP errpos ; Subtract real numbers
POP CX ; second number
POP SI
POP DI
POP AX ; first number
POP BX
POP DX
CALL resub ; do subtraction
JMP retest ; "put result, return
xmul POP errpos ; Multiply real numbers
POP CX ; second number
POP SI
POP DI
POP AX ; first number
POP BX
POP DX
xmul2 CALL remult ; do multiplication
JMP retest ; "put result, return
xdiv POP errpos ; Divide real numbers
POP CX ; second number
POP SI
POP DI
POP AX ; first number
POP BX
POP DX
OR.B CL,CL ; second = 0 ?
JZ xdiverr ; yes: div / 0 error
CALL rediv ; divide
JMP retest ; 'put result, return
xdiverr MOV DL,#$02 ; Division by zero attempted
JMP runerr ; "
xneg MOV BX,SP ; Neg real: get addr of number
SS:
CMP.B [BX]$02,#$00 ; zero ?
JZ xnegzer ; :don't negate
SS:
XOR.B [BX]$07,#$80 ; invert sign
xnegzer RET ; "
xabs MOV BX,SP ; Abs real: get addr of number
SS:
AND.B [BX]$07,#$7F ; make positive
RET ; "
realeq POP errpos ; Real =
POP CX ; second number
POP SI
POP DI
POP AX ; first number
POP BX
POP DX
CALL recmp ; compare real
PUSH errpos ; restore ret
MOV AX,#$0001 ; true
JZ realeq1 ; equal: true
DEC AX ; false
realeq1 OR AX,AX ; set flags
RET ; "
realne POP errpos ; Real <>
POP CX ; second number
POP SI
POP DI
POP AX ; first number
POP BX
POP DX
CALL recmp ; compare real
PUSH errpos ; restore ret
MOV AX,#$0001 ; true
JNZ realne1 ; not equal: true
DEC AX ; false
realne1 OR AX,AX ; set flags
RET ; "
realge POP errpos ; Real >=
POP CX ; second number
POP SI
POP DI
POP AX ; first number
POP BX
POP DX
CALL recmp ; compare real
PUSH errpos ; restore ret
MOV AX,#$0001 ; true
JNB realge1 ; larger or equal: true
DEC AX ; false
realge1 OR AX,AX ; set flags
RET ; "
realle POP errpos ; Real <=
POP CX ; second number
POP SI
POP DI
POP AX ; first number
POP BX
POP DX
CALL recmp ; compare real
PUSH errpos ; restore ret
MOV AX,#$0001 ; true
JBE realle1 ; smaller or equal: true
DEC AX ; false
realle1 OR AX,AX ; set flags
RET ; "
realg POP errpos ; Real >
POP CX ; second number
POP SI
POP DI
POP AX ; first number
POP BX
POP DX
CALL recmp ; compare real
PUSH errpos ; restore ret
MOV AX,#$0001 ; true
JA realg1 ; larger: true
DEC AX ; false
realg1 OR AX,AX ; set flags
RET ; "
reall POP errpos ; Real <
POP CX ; second number
POP SI
POP DI
POP AX ; first number
POP BX
POP DX
CALL recmp ; compare real
PUSH errpos ; restore ret
MOV AX,#$0001 ; true
JB reall1 ; smaller: true
DEC AX ; false
reall1 OR AX,AX ; set flags
RET ; "
xsqr POP errpos ; Sqr real: save ret addr
POP AX ; get number
POP BX
POP DX
MOV CX,AX ; -> second number
MOV SI,BX
MOV DI,DX
JMP xmul2 ; "do real multiplication
xint POP errpos ; Int real: save ret addr
POP AX ; get number
POP BX
POP DX
CALL reint ; do Int
JMP repush ; "push result
xfrac POP errpos ; Frac real: save ret addr
POP AX ; get number
POP BX
POP DX
CALL refrac ; do Frac
JMP repush ; "push result
xrandom CALL dorandom ; Random real
MOV DX,#$0080 ; original exponent
MOV AL,#$20 ; max. count
rrndnrm TEST.B BH,#$80 ; normalized ?
JNZ rrndok ; :yes
SHL CX,1 ; shift left 1 bit
RCL BX,1
DEC.B DL ; count down exponent
DEC.B AL ; another bit ?
JNZ rrndnrm ; :yes
XOR.B DL,DL ; return zero
rrndok AND.B BH,#$7F ; clear mantissa MSB
POP AX ; ret addr
PUSH BX ; push result
PUSH CX
PUSH DX
JMP AX ; "return
xround MOV CH,#$FF ; Round
JMP.b trunc1 ; '
xtrunc XOR.B CH,CH ; Trunc
trunc1 POP BX ; ret addr
POP AX ; get number
POP DX
POP DX
PUSH BX ; restore ret
trunc2 XCHG AX,DX ; put exp
MOV CL,#$8F ; subtract exponent
SUB.B CL,DL ; too big ?
JB truncerr ; :overflow error
CMP.B CL,#$0F ; too many shifts ?
JA trunczer ; yes: 0
INC.B CL
MOV.B BH,AH ; save sign
OR.B AH,#$80 ; set mantissa MSB
SHR AX,CL ; shift right
JNB trunc3 ; :nothing to round
OR.B CH,CH ; round up ?
JZ trunc3 ; :no
INC AX ; round it up
JS truncerr ; overflow: error
trunc3 TEST.B BH,#$80 ; sign ?
JZ truncrt ; :no
NEG AX ; negate it
truncrt RET ; '
trunczerXOR AX,AX ; return 0
RET ; '
truncerrMOV DL,#$92 ; Out of integer range
JMP runerrrt ; "
xintrealCALL intreal ; Integer -> Real
POP CX ; get ret
PUSH DX ; push number
PUSH BX
PUSH AX
JMP CX ; "return
xintre2 POP errpos ; Int -> Real
POP CX ; keep second number
POP SI
POP DI
POP AX ; integer number
CALL intreal ; do conversion
PUSH DX ; push result
PUSH BX
PUSH AX
PUSH DI ; push second number
PUSH SI
PUSH CX
JMP [errpos] ; "return
; Square root. Algorithm: Newton
; x[n+1]:=0.5*(x[n]+c/x[n]) -> Sqrt(c)
xsqrt POP errpos ; Sqrt: save ret addr
POP AX ; get number
POP BX
POP DX
MOV CX,AX ; -> second number
MOV SI,BX
MOV DI,DX
OR.B AL,AL ; zero ?
JZ sqrtdone ; yes: return
TEST.B DH,#$80 ; test sign
JNZ sqrterr ; negative: error
MOV retrc1,AX ; store number
MOV retrc2,BX
MOV retrc3,DX
ADD.B CL,#$80 ; make a guess for the
SAR.B CL,1 ; exponent
ADD.B CL,#$80
MOV.B AL,CL
SUB AL,#$14 ; end condition: difference
MOV cvexpcnt,AL ; smaller than this
sqrtloopMOV AX,retrc1 ; get original number
MOV BX,retrc2
MOV DX,retrc3
CALL rediv2 ; real division /x[n]
CALL readd2 ; real addition +x[n]
DEC.B AL ; * 0.5 = dec exponent
PUSH DX ; save x[n+1]
PUSH BX
PUSH AX ; test: end reached ?
CALL resub ; real subtraction
CMP.B AL,cvexpcnt ; test exponent
POP CX ; restore x[n+1]
POP SI
POP DI
JNB sqrtloop ; :not yet done
sqrtdonePUSH DI ; return result
PUSH SI
PUSH CX
JMP [errpos] ; 'return
sqrterr MOV DL,#$03 ; Sqrt argument error
JMP runerr ; "
; *** Transcendental functions ***
; The algorithms used can be looked up in any formulary (!!!)
xcos POP errpos ; Cos: save ret addr
POP CX ; get number
POP SI
POP DI
MOV AX,#$2181 ; -pi/2
MOV BX,#$DAA2
MOV DX,#$490F
CALL resub ; real subtraction
JMP.b sin1 ; '
xsin POP errpos ; Sin: save ret addr
POP AX ; get number
POP BX
POP DX
sin1 CMP AL,#$6C ; small number ?
JB sinpush ; yes: return immediately
MOV CX,#$2183 ; 2*pi
MOV SI,#$DAA2 ; bring into range 0..2*pi
MOV DI,#$490F
PUSH DX ; save sign
AND.B DH,#$7F ; make positive
CALL recmp ; real comparison
POP DX ; restore sign
JB sinl2pi ; :ok
CALL rediv2 ; real division, keep 2*pi
PUSH DI ; save 2*pi
PUSH SI
PUSH CX
CALL refrac ; Frac
POP CX ; restore 2*pi
POP SI
POP DI
CALL remult2 ; real mult, keep 2*pi
sinl2pi TEST.B DH,#$80 ; negative ?
JZ sinpos ; :no
CALL readd2 ; add 2*pi
sinpos DEC.B CL ; change to pi
CALL recmp ; real comparison
PUSHF ; save result
JB sinlpi ; :below
CALL resub2 ; subtract pi
sinlpi DEC.B CL ; change to pi/2
CALL recmp ; real comparison
JB sinlpi2 ; :less
INC.B CL
OR.B DH,#$80 ; add -pi
CALL readd
sinlpi2 CMP AL,#$6C ; small number ?
JB sinsmall ; yes:return
MOV DI,#fltsin ; pointer to constants
MOV CX,#$0007 ; 7 numbers
CALL poly1 ; do polynome
sinsmallPOPF ; flag: >pi
JB sinpush ; :ok
OR.B AL,AL ; neg, if not zero
JZ sinpush ; :zero
XOR.B DH,#$80 ; negate
sinpush JMP repush ; "push result
fltsin W $9D58,$9F39,$D73F ; -7.6471637318E-13 -1/15! constants for sin,cos
W $4360,$309D,$3092 ; 1.6059043837E-10 1/13!
W $AA67,$283F,$D732 ; -2.5052103056E-08 -1/11!
W $B66E,$1D2A,$38EF ; 2.7557319224E-06 1/ 9!
W $0D74,$00D0,$D00D ; -1.9841269841E-04 -1/ 7!
W $887A,$8888,$0888 ; 8.3333333333E-03 1/ 5!
W $AB7E,$AAAA,$AAAA ; -1.6666666667E-01 "-1/ 3!
xln POP errpos ; Ln (Logarithm)
POP AX ; get number
POP BX
POP DX
OR.B AL,AL ; zero ?
JZ lnerr ; yes:error
TEST.B DH,#$80 ; negative ?
JZ lnok ; :no
lnerr MOV DL,#$04 ; Ln argument error
JMP runerr ; '
lnok MOV.B CH,AH ; sign, LSB
MOV CL,#$81 ; bring into range 1..2
SUB.B AL,CL ; modify exponent
CBW
PUSH AX ; integer: exponent
XCHG AX,CX ; set new exponent
MOV CX,#$FB80 ; multiply *Sqrt(2)/2
MOV SI,#$F333
MOV DI,#$3504
CALL remult ; multiplication
MOV CX,AX ; first -> second number
MOV SI,BX
MOV DI,DX
MOV AX,#$0081 ; first number = 1
XOR BX,BX
XOR DX,DX
CALL readd2 ; addition +1
PUSH DX ; save result
PUSH BX
PUSH AX
MOV AX,#$0081 ; first = -1
XOR BX,BX
MOV DX,#$8000
CALL readd ; addition -1
POP CX ; restore result
POP SI
POP DI
CALL rediv ; division (c-1)/(c+1)
MOV DI,#fltln ; pointer to constants
MOV CX,#$0006 ; 6 numbers
CALL poly1 ; do polynome
INC.B AL ; result *2
MOV CX,#$D27F ; + ln(sqrt(2))
MOV SI,#$17F7
MOV DI,#$3172
CALL readd ; addition
POP CX ; get int exponent
PUSH DX ; save result
PUSH BX
PUSH AX
XCHG AX,CX ; exponent
CALL intreal ; -> real
MOV CX,#$D280 ; * ln(2)
MOV SI,#$17F7
MOV DI,#$3172
CALL remult ; multiplication
POP CX ; restore result
POP SI
POP DI
CALL readd ; add
CMP AL,#$67 ; do a cosmetic round-off (!!!)
JNB lnround ; :no
XOR AX,AX ; return zero
XOR BX,BX
XOR DX,DX
lnround JMP repush ; "push result
fltln W $8A7D,$D89D,$1D89 ; 7.6923076923E-02 1/13 Constants for Ln
W $E97D,$8BA2,$3A2E ; 9.0909090909E-02 1/11
W $8E7D,$38E3,$638E ; 1.1111111111E-01 1/ 9
W $497E,$2492,$1249 ; 1.4285714286E-01 1/ 7
W $CD7E,$CCCC,$4CCC ; 2.0000000000E-01 1/ 5
W $AB7F,$AAAA,$2AAA ; 3.3333333333E-01 "1/ 3
xexp POP errpos ; Exp: get return addr
POP AX ; get number
POP BX
POP DX
TEST.B DH,#$80 ; negative ?
PUSHF ; remember flag
AND.B DH,#$7F ; make it positive
MOV CX,#$D280 ; /ln(2)
MOV SI,#$17F7
MOV DI,#$3172 ; (would be faster to use mult !)
CALL rediv ; division
CMP AL,#$88 ; too much ?
JNB experr ; :yes, overflow
PUSH DX ; save number
PUSH BX
PUSH AX
INC.B AL ; * 2
MOV CH,#$FF
CALL trunc2 ; do Round
POP CX ; restore number
POP SI
POP DI
PUSH AX ; save integer part
CALL intreal ; convert -> real
OR.B AL,AL ; zero ?
JZ expzer ; :yes
DEC.B AL ; / 2
expzer XCHG AX,CX ; swap them
XCHG BX,SI
XCHG DX,DI
CALL resub ; subtraction -> frac
MOV DI,#fltexp ; ptr to constants
MOV CX,#$0008 ; 8 numbers
CALL poly2 ; do polynome
POP CX ; get exponent
SHR CX,1 ; / 2
JNB expeven ; :even
PUSH CX ; save it
MOV CX,#$FB81 ; * Sqrt(2)
MOV SI,#$F333
MOV DI,#$3504
CALL remult ; multiplication
POP CX ; restore exponent
expeven ADD.B AL,CL ; add exponents
JB experr ; :overflow error
POPF ; restore sign
JZ exppush ; pos: store result
MOV CX,AX ; negative: do 1/x -> x
MOV SI,BX ; first -> second
MOV DI,DX
MOV AX,#$0081 ; first = 1
XOR BX,BX
XOR DX,DX
CALL rediv ; division
exppush JMP repush ; 'push result
experr POP AX ; clear stack
MOV DL,#$01 ; Floating point overflow
JMP runerr ; "
fltexp W $2E6D,$111D,$3160 ; 1.3215486790E-06 ln(2)**8/8! Constants for Exp
W $4670,$FE2C,$7FE5 ; 1.5252733804E-05 ln(2)**7/7!
W $3674,$897C,$2184 ; 1.5403530393E-04 ln(2)**6/6!
W $5377,$FF3C,$2EC3 ; 1.3333558146E-03 ln(2)**5/5!
W $D27A,$5B7D,$1D95 ; 9.6181291076E-03 ln(2)**4/4!
W $257C,$46B8,$6358 ; 5.5504108665E-02 ln(2)**3/3!
W $167E,$EFFC,$75FD ; 2.4022650696E-01 ln(2)**2/2!
W $D280,$17F7,$3172 ; 6.9314718056E-01 "ln(2)
xarctan POP errpos ; ArcTan: get ret addr
POP AX ; get number
POP BX
POP DX
OR.B AL,AL ; zero ?
JZ exppush ; yes: return
XOR CX,CX ; clear neg flag
TEST.B DH,#$80 ; negative ?
JZ atnpos ; :no
INC CX ; set neg flag
AND.B DH,#$7F ; make positive
atnpos PUSH CX ; save neg flag
MOV CX,#$0081 ; second = 1
XOR SI,SI
XOR DI,DI
CALL recmp ; real comparison
JB atnsmall ; :x < 1
XCHG AX,CX ; swap numbers
XCHG BX,SI
XCHG DX,DI
CALL rediv ; division x -> 1/x
POP CX ; restore neg flag
INC CX ; set bits: large number
INC CX
PUSH CX ; push it again
atnsmallMOV CX,#$4A7E ; > 0.1371 ?
MOV SI,#$E98E
MOV DI,#$0C6F
CALL recmp ; real comparison
JNB atnrng ; :yes
CALL atnpoly ; do polynome
JMP.b atndone ; 'complete result
atnrng MOV DI,#fltatnrg ; pointer into table
MOV CX,#$0002
atnrlp PUSH CX ; save index
PUSH DI ; save ptr
CS: ; test different ranges
MOV CX,[DI] ; get table entry
CS:
MOV SI,[DI]$02
CS:
MOV DI,[DI]$04
CALL recmp ; real comparison
POP DI ; restore
POP CX
JB atnrfnd ; :smaller than that
ADD DI,#$12 ; go to next entry
LOOP atnrlp ; :another one
SUB DI,#$06 ; save space in table
atnrfnd ADD DI,#$06 ; go to associated number
MOV retrc1,AX ; save number
MOV retrc2,BX
MOV retrc3,DX
PUSH DI ; save ptr
CS: ; get number from table
MOV CX,[DI]
CS:
MOV SI,[DI]$02
CS:
MOV DI,[DI]$04
CALL resub2 ; subtraction x-t
PUSH DX ; save number
PUSH BX
PUSH AX
MOV AX,retrc1 ; restore x
MOV BX,retrc2
MOV DX,retrc3
CALL remult ; x*t
MOV CX,#$0081 ; second = 1
XOR SI,SI
XOR DI,DI
CALL readd ; addition
MOV CX,AX ; first -> second
MOV SI,BX
MOV DI,DX
POP AX ; restore x-t
POP BX
POP DX
CALL rediv ; division -> (x-t)/(x*t+1)
CALL atnpoly ; do polynome
POP DI ; restore ptr
ADD DI,#$06
CS:
MOV CX,[DI] ; get number
CS:
MOV SI,[DI]$02
CS:
MOV DI,[DI]$04
CALL readd ; add it
atndone POP CX ; get flag
TEST.B CL,#$02 ; done 1/x ?
JZ atnnorcp ; :no
PUSH CX ; save flag
MOV CX,AX ; first -> second
MOV SI,BX
MOV DI,DX
MOV AX,#$2181 ; first = pi/2
MOV BX,#$DAA2
MOV DX,#$490F
CALL resub ; subtraction pi/2-y
POP CX ; restore flag
atnnorcpTEST.B CL,#$01 ; negative ?
JZ atnpos2 ; :no
OR.B DH,#$80 ; set sign
atnpos2 JMP repush ; "push result
fltatnrgW $E77F,$CCCF,$5413 ; 4.1421356237E-01 1. tan(22.5) range table
W $F67F,$A2F4,$0930 ; 2.6794919243E-01 tan(15) for ArcTan
W $6A7F,$91C1,$060A ; 2.6179938780E-01 15
W $B580,$8A9E,$446F ; 7.6732698798E-01 2. tan(37.5)
W $8280,$3A2C,$13CD ; 5.7735026919E-01 tan(30)
W $6A80,$91C1,$060A ; 5.2359877560E-01 30
W $0081,$0000,$0000 ; 1.0000000000E+00 3. tan(45)
W $2180,$DAA2,$490F ; 7.8539816340E-01 " 45
fltatn W $E87D,$8BA2,$BA2E ; -9.0909090909E-02 -1/11 constants for ArcTan
W $8E7D,$38E3,$638E ; 1.1111111111E-01 1/ 9
W $497E,$2492,$9249 ; -1.4285714286E-01 -1/ 7
W $CD7E,$CCCC,$4CCC ; 2.0000000000E-01 1/ 5
W $AB7F,$AAAA,$AAAA ; -3.3333333333E-01 " 1/ 3
atnpoly MOV DI,#fltatn ; ptr to ArcTan constants
MOV CX,#$0005 ; 5 of them
poly1 PUSH DX ; do polynome: store number
PUSH BX ; y:=x+c*x**3+b*x**5+a*x**7...
PUSH AX
PUSH CX ; save cnt
PUSH DI ; save ptr
MOV CX,AX ; first -> second
MOV SI,BX
MOV DI,DX
CALL remult ; multiplication -> square
POP DI ; restore cnt,ptr
POP CX
CALL poly2 ; do polynome 2
POP CX ; get number -> second
POP SI
POP DI
JMP remult ; "multiplication
poly2 MOV retrc1,AX ; do polynome 2: store number
MOV retrc2,BX ; y:=1+c*x+b*x**2+a*x**3...
MOV retrc3,DX
CS: ; get number from table
MOV AX,[DI]
CS:
MOV BX,[DI]$02
CS:
MOV DX,[DI]$04
PUSH CX ; save ptr, cnt
PUSH DI
JMP.b polystrt ; 'start it
polyloopPUSH CX ; save ptr, cnt
PUSH DI
CS:
MOV CX,[DI] ; get number from table
CS:
MOV SI,[DI]$02
CS:
MOV DI,[DI]$04
CALL readd ; add it
polystrtMOV CX,retrc1 ; get x -> second
MOV SI,retrc2
MOV DI,retrc3
CALL remult ; multiplication *x
POP DI ; restore ptr, cnt
POP CX
ADD DI,#$06 ; go to next number
LOOP polyloop ; :another one
MOV CX,#$0081 ; second = 1
XOR SI,SI
XOR DI,DI
JMP readd ; "add 1
fmtreal PUSH BX ; Format real number
CMP DX,#$19 ; number of fraction chars
JB frnolim2 ; < 25:ok
MOV AX,CX ; field width
CALL limstind ; limit it
MOV DL,#$07
TEST.B [DI]$05,#$80 ; negative (DI:ptr to num)
JZ frpos ; :no
INC.B DL ; one char more
frpos SUB.B AL,DL
JNB frnound ; :no underflow
XOR.B AL,AL ; negative: make zero
frnound CMP AL,#$09 ; limit to 9
JB frnolim
MOV AL,#$09
frnolim INC.B AL ; -> number of fraction chars
MOV.B DL,AL
MOV.B DH,AL
frnolim2PUSH DX ; save # fraction chars
CALL realdec ; convert to decimal
POP DX ; restore
MOV.B AL,DL
INC.B AL ; number of mantissa chars
OR.B DH,DH
JNZ frround
ADD.B AL,CL
JNS frlimprc
MOV.B cvoutbuf,#$00 ; mark end of buffer
JMP.b frfmtit ; '
frlimprcCMP AL,#$0C ; max 11 digits shown
JB frround ; :ok
MOV AL,#$0B ; limit it
frround CALL decround ; round up number
frfmtit POP BX ; dest ptr
MOV SI,#cvoutbuf ; source: dec buffer
TEST.B CH,#$80 ; test sign
JZ frpos2 ; :no
MOV AL,#$2D ; put a -
CALL frdigsto
frpos2 MOV.B CH,CL ; exponent: number of int digs
OR.B DH,DH ; size of number
JZ frzer ; :zero
MOV CH,#$00
frzer OR.B CH,CH ; neg exponent ?
JNS frint ; :no
CALL frdig0 ; store 0 - no int part
JMP.b frfrac0 ; 'do fraction part
frint CALL frdig ; put char from source
DEC.B CH ; another one ?
JNS frint ; :yes
frfrac0 OR.B DL,DL ; test fraction len
JZ frexp ; :nothing
MOV AL,#$2E ; put a .
CALL frdigsto
frfill0 INC.B CH ; put zeroes as necessary
JZ frfrac ; :exponent ok
CALL frdig0 ; put a 0
DEC.B DL ; field filled ?
JNZ frfill0 ; :no
frfrac DEC.B DL ; another fraction digit ?
JS frexp ; :no
CALL frdig ; do digit
JMP frfrac ; 'next one
frexp OR.B DH,DH ; do exponent ?
JNZ frputexp ; :yes
RET ; '
frputexpMOV AL,#$45 ; put an E
CALL frdigsto
MOV AL,#$2B ; +
OR.B CL,CL ; test sign
JNS frposexp ; :positive
NEG.B CL ; negate exponent
MOV AL,#$2D ; -
frposexpCALL frdigsto ; store sign
MOV AL,#$2F ; do DIV 10 / MOD 10
frexpsubINC.B AL ; count up digit
SUB.B CL,#$0A ; successive subtraction
JNB frexpsub ; :continue
CALL frdigsto ; put digit
ADD.B CL,#$3A ; restore second digit
MOV.B AL,CL ; put digit
JMP.b frdigsto ; "
frdig MOV.B AL,[SI] ; get digit from source
OR.B AL,AL ; end ?
JZ frdig0 ; :yes
INC SI ; ptr to next char
JMP.b frdigsto ; 'store digit
frdig0 MOV AL,#$30 ; store a 0
frdigstoMOV.B [BX],AL ; store digit
INC BX ; ptr to next dest char
RET ; "
realdec MOV AX,[DI] ; Real -> Decimal
MOV BX,[DI]$02 ; get real number (in CS !)
MOV DX,[DI]$04
OR.B AL,AL ; zero ?
JNZ rdno0 ; :no
MOV SI,#cvoutbuf ; fill buffer with zeroes
rdfill MOV [SI],#$3030
INC SI
INC SI
CMP SI,#currfil ; end reached ?
JNZ rdfill ; :no
MOV CX,#$0000 ; exponent 0
RET ; '
rdno0 MOV.B CH,DH ; get sign
AND.B DH,#$7F ; clear sign in mantissa
PUSH AX ; save exp
PUSH DX ; save mantissa
SUB AL,#$80 ; exponent
CBW
MOV DX,#$004D ; * 77
IMUL DX ; 77/256 is about ln(2)/ln(10) !
ADD AX,#$0005 ; +5
MOV.B CL,AH ; this is approx. dec exponent
POP DX ; restore
POP AX
CMP.B CL,#$D9 ; correct error
JNZ rdnocomp ; :ok
INC.B CL
rdnocompPUSH CX ; save exponent, sign
NEG.B CL ; negate exponent
CALL realrang ; bring into range
POP CX ; restore
CMP AL,#$81 ; exp ok ?
JNB rdnoadj ; :yes
CALL mult10 ; mult * 10
DEC.B CL ; count down dec exp
rdnoadj PUSH CX ; save exp, sign
OR.B DH,#$80 ; set mantissa MSB
MOV CL,#$84 ; offset for exponent
SUB.B CL,AL
MOV AL,#$00 ; LSB = 0
JZ rdnosh ; :no shift
rdshift SHR DX,1 ; convert to 6-byte-card
RCR BX,1
RCR AX,1
DEC.B CL ; shift again ?
JNZ rdshift ; :yes
rdnosh MOV SI,#cvoutbuf ; ptr to output buffer
rdconv MOV.B CH,DH ; get upper 4 bits
MOV CL,#$04
SHR.B CH,CL
ADD.B CH,#$30 ; -> digit
MOV.B [SI],CH ; store digit
AND.B DH,#$0F ; clear that digit
PUSH DX ; card * 10 -> card
PUSH BX ; save number
PUSH AX
SHL AX,1 ; card * 4
RCL BX,1
RCL DX,1
SHL AX,1
RCL BX,1
RCL DX,1
POP CX ; + card
ADD AX,CX
POP CX
ADC BX,CX
POP CX
ADC DX,CX
SHL AX,1 ; * 2 = card * 10
RCL BX,1
RCL DX,1
INC SI ; next buffer pos
CMP SI,#currfil ; done ?
JNZ rdconv ; :no
POP CX ; restore exp, sign
RET ; "
decroundXOR.B AH,AH ; Round up decimal number
MOV BX,#cvoutbuf ; ptr to buffer
ADD BX,AX ; add digit index
CMP.B [BX],#$35 ; 5 ?
MOV.B [BX],#$00 ; store 0: end mark
JB drdone ; below: no round up
drloop DEC.B AL ; go back one digit
JS drincexp ; :beg of buffer reached
DEC BX ; go back one pos
INC.B [BX] ; inc that digit
CMP.B [BX],#$3A ; carry ?
JB drdone ; no: done
MOV.B [BX],#$00 ; mark as end
JMP drloop ; 'continue
drincexpMOV.B [BX],#$31 ; store a 1
MOV.B [BX]$01,#$00 ; end mark
INC.B CL ; inc exponent
drdone RET ; "
ascreal MOV.B CL,[BX] ; String -> Real
CMP.B CL,#$2D ; - ?
JNZ arpos ; :no
INC BX ; next char
arpos PUSH CX ; save sign
CALL ascreal2 ; convert unsigned real
POP CX ; restore sign
JB arerr ; :error
CMP.B CL,#$2D ; - ?
JNZ arpos1 ; :no
CMP.B [DI],#$00 ; result = 0 ?
JZ arpos1
XOR.B [DI]$05,#$80 ; negate it
arpos1 CLC ; no error
arerr RET ; "
ascreal2MOV SI,BX ; source ptr
XOR AX,AX ; ASCII -> unsigned real
XOR BX,BX ; clear result
XOR DX,DX
XOR CX,CX
MOV.B cvexpcnt,#$00 ; exponent
arloop MOV.B CL,[SI] ; get char
CMP.B CL,#$61 ; lower case ?
JB arupc ; :no
CMP.B CL,#$7A
JA arupc ; :no
SUB.B CL,#$20 ; convert to upper case
arupc CALL ardigit2 ; do a digit
JB arnodig ; :no digit
CALL mult10 ; result * 10
JB arover ; :overflow
PUSH DI ; save ptrs
PUSH SI
PUSH CX
PUSH DX ; save result
PUSH BX
PUSH AX
MOV.B AL,CL ; get digit
XOR.B AH,AH ; clear hi byte
CALL intreal ; convert to real
POP CX ; restore result
POP SI
POP DI
CALL readd ; add digit to result
POP CX ; restore ptrs
POP SI
POP DI
TEST.B CH,#$40 ; after decimal point ?
JZ arnext ; :yes
DEC.B cvexpcnt ; count down exponent
JMP.b arnext ; 'next digit
arnodig CMP.B CL,#$2E ; decimal point ?
JNZ arexp ; :no, test exponent
TEST.B CH,#$40 ; . already done ?
STC ; error
JNZ arover ; yes: error
OR.B CH,#$40 ; set flag
arnext INC SI ; next char
JMP arloop ; 'continue
arover MOV BX,SI ; pointer: end pos
RET ; '
arexp CMP.B CL,#$45 ; E ?
MOV.B CL,cvexpcnt ; exponent
JNZ arexp3 ; :no
CALL realrng ; bring into range
JB arover ; :error
INC SI ; go to next char
MOV.B CL,[SI] ; get it
CMP.B CL,#$2B ; + ?
JZ arposexp ; :yes
CMP.B CL,#$2D ; - ?
JNZ arexp2 ; no: digit
OR.B CH,#$20 ; set flag: neg exponent
arposexpINC SI ; next char
arexp2 CALL ardigit ; do digit
JB arover ; :error
PUSH AX ; save
MOV.B AL,CL ; first digit
INC SI ; next char
CALL ardigit ; do digit
JB aronedig ; :no second digit
MOV.B AH,AL ; AL*10 -> AL
SHL.B AL,1
SHL.B AL,1
ADD.B AL,AH
SHL.B AL,1
ADD.B AL,CL
INC SI ; next char
aronedigMOV.B CL,AL ; new exponent
POP AX ; restore
TEST.B CH,#$20 ; negative exponent ?
JZ arexp3 ; :no
NEG.B CL ; negate it
arexp3 CALL realrng ; real number*10**exp
MOV [DI],AX ; store the result
MOV [DI]$02,BX
MOV [DI]$04,DX
JMP arover ; "set pointer to end
realrng CMP.B CL,#$DA ; outside a reasonable range ?
JL rrerr ; :yes
CMP.B CL,#$26
JG rrerr ; :yes
PUSH CX ; save pointers
PUSH SI
PUSH DI
CALL realrang ; bring into range
POP DI
POP SI
POP CX
RET ; '
rrerr STC ; error
RET ; "
ardigit MOV.B CL,[SI] ; get digit
ardigit2CMP.B CL,#$30 ; < 0 ?
JB ardret ; :yes
CMP.B CL,#$3A ; > 9 ?
CMC ; change to error flag
JB ardret ; :yes
SUB.B CL,#$30 ; convert to number
ardret RET ; "
realrangPUSH DX ; bring into range:
PUSH BX ; number*10**CL
PUSH AX ; save number
MOV.B cvdecexp,CL ; save exponent
OR.B CL,CL ; negative ?
JNS rrpos ; :no
NEG.B CL ; make it positive
rrpos MOV.B BL,CL ; (exponent DIV 4)*6
AND.B BL,#$FC
MOV.B BH,BL
SHR.B BL,1
ADD.B BL,BH ; -> pointer into table
XOR.B BH,BH ; clear hi
LEA DI,[BX]fltdec ; factor table
CS:
MOV AX,[DI] ; get factor
CS:
MOV BX,[DI]$02
CS:
MOV DX,[DI]$04
AND.B CL,#$03 ; exponent MOD 4
JZ rrdone ; :ok
rrmul CALL mult10 ; do successive multiplications
DEC.B CL ; another ?
JNZ rrmul ; :yes
rrdone MOV CX,AX ; factor -> second
MOV SI,BX
MOV DI,DX
POP AX ; restore number
POP BX
POP DX
TEST.B cvdecexp,#$80 ; positive exponent ?
JNZ rrdiv ; :no
JMP remult ; 'multiply num*factor
rrdiv JMP rediv ; "divide num/factor
fltdec W $0081,$0000,$0000 ; 1.0000000000E+00 decimal factor table
W $008E,$0000,$1C40 ; 1.0000000000E+04
W $009B,$2000,$3EBC ; 1.0000000000E+08
W $00A8,$A510,$68D4 ; 1.0000000000E+12
W $04B6,$C9BF,$0E1B ; 1.0000000000E+16
W $ACC3,$EBC5,$2D78 ; 1.0000000000E+20
W $CDD0,$1BCE,$53C2 ; 1.0000000000E+24
W $F9DE,$3978,$013F ; 1.0000000000E+28
W $2BEB,$ADA8,$1DC5 ; 1.0000000000E+32
W $C9F8,$CE7B,$4097 ; 1.0000000000E+36 "
mult10 OR.B AL,AL ; Real multiplication * 10
JNZ m10not0 ; :not zero
RET ; 'zero - return
m10not0 OR.B DH,#$80 ; set mantissa MSB
PUSH CX ; save CX
PUSH DX ; save number
PUSH BX
PUSH AX
SHR DX,1 ; mantissa / 4
RCR BX,1
RCR.B AH,1
SHR DX,1
RCR BX,1
RCR.B AH,1
POP CX ; add mantissa
ADD.B AH,CH ; why no ADC (rounding !) ?
POP CX
ADC BX,CX
POP CX
ADC DX,CX
POP CX ; restore CX
JNB m10nrm
RCR DX,1 ; shift right
RCR BX,1
RCR.B AH,1
INC.B AL ; exponent+1
JNZ m10nrm ; :ok
STC ; overflow...
RET ; '
m10nrm AND.B DH,#$7F ; make positive
ADD AL,#$03 ; exponent+3
RET ; "
realcardPOP SI ; Real -> long cardinal
POP DI ; 2 ret addrs
POP DX ; get real
POP CX
POP BX
PUSH DI ; restore rets
PUSH SI
TEST.B BH,#$80 ; negative ?
JNZ rczero ; yes:return zero
OR.B BH,#$80 ; set mantissa MSB
MOV AL,#$A0 ; exponent offset
SUB.B AL,DL ; calc number of shifts
JB rcover ; :too much
CMP AL,#$20 ; too small ?
JNB rczero ; :return zero
rcdenormOR.B AL,AL ; another shift ?
JZ rcdone ; :done
SHR BX,1 ; shift right: denormalize
RCR CX,1
DEC.B AL
JMP rcdenorm ; 'next one
rcdone MOV AX,CX ; return number in DX:AX
MOV DX,BX
RET ; '
rczero XOR AX,AX ; return zero
XOR DX,DX
RET ; '
rcover MOV AX,#$FFFF ; return maxcard
MOV DX,#$FFFF
RET ; "
cardrealMOV BX,DX ; long cardinal -> real
MOV CX,AX
OR AX,DX ; 0 ?
JZ crzero ; yes: return 0
MOV DX,#$00A0 ; exponent, LSB mantissa
crnorm TEST.B BH,#$80 ; normalized ?
JNZ crstore ; :yes
SHL CX,1 ; shift left
RCL BX,1
DEC.B DL ; count down exponent
JMP crnorm ; 'continue
crstore AND.B BH,#$7F ; make positive
crzero POP AX ; return addr
PUSH BX ; push result
PUSH CX
PUSH DX
JMP AX ; "return
xassgntxMOV AL,#$01 ; text file
doassignMOV filfunc,AL ; do assign: store func code
POP BX ; ret addr
CALL getpn ; string -> ASCIIZ
POP DI ; ptr to file var
POP ES
PUSH BX ; restore ret
MOV AX,ES ; file var in DS ?
MOV DX,DS
CMP AX,DX
JNZ asnostd
CMP DI,#stdout ; std in / out file ?
JBE asgnerr ; :yes, error
asnostd PUSH DI ; save var ofs
MOV SI,#pnbuf ; path name buffer
LEA DI,[DI]$0C ; path name in file var
MOV CX,#$0020 ; copy 64 chars
CLD
REPZ
MOVS ; do it
POP DI ; restore file var ofs
CALL devtest ; test if device
JNB asnodev ; :yes
MOV AL,#$00 ; flag: not open
MOV BX,#$FFFF ; no handle
asnodev ES:
MOV [DI],BX ; store file handle
CMP.B filfunc,#$00 ; text file ?
JZ asnotxt ; :no
ES:
MOV.B [DI]$02,AL ; set file flag
LEA AX,[DI]$4C ; set buffer ofs
ES:
MOV [DI]$04,AX ; store in file var
RET ; '
asnotxt ES: ; set file var:
MOV [DI]$02,#$0000 ; record length
RET ; '
asgnerr MOV.B errnum,#$22 ; Assign to std files
RET ; "not allowed
xresettxXOR.B AL,AL ; Reset text file
JMP.b opentxt ; '
xrewrttxMOV AL,#$01 ; Rewrite text file
JMP.b opentxt ; '
xappndtxMOV AL,#$02 ; Append text file
opentxt MOV filfunc,AL ; store function code
POP errpos ; get ret addr
POP ES ; file var ptr
PUSH errpos ; restore ret
ES:
MOV.B AL,[DI]$02 ; test flag
AND AL,#$0F ; device ?
JZ otnodev ; :no
ES:
AND.B [DI]$02,#$DF ; clear flag: char preread
otret RET ; '
otnodev ES:
MOV [DI]$06,CX ; store buffer size
CALL close1 ; close file
CMP.B errnum,#$00 ; error ?
JNZ otret ; yes:ret
CALL openfile ; do open file
CMP.B errnum,#$00 ; error ?
JNZ otret ; yes:ret
TEST modeflg,#$0002 ; do device checking ?
JZ otnodev2 ; :no
MOV AX,#$4400 ; test device status
ES:
MOV BX,[DI] ; get file handle
CALL dos ; do it
TEST DX,#$0080 ; device ?
JZ otnodev2 ; :no
ES:
MOV [DI]$06,#$0001 ; buffer len: 1 char
otnodev2CMP.B filfunc,#$01 ; read ?
JNB otappend ; :no
ES:
MOV.B [DI]$02,#$80 ; open for input
ES:
MOV BX,[DI]$04 ; buffer pos
ES:
MOV [DI]$08,BX ; -> buffer ptr
ES:
MOV [DI]$0A,BX ; -> buffer end
RET ; '
otappendJZ prepout ; write: prepare for output
MOV AX,#$4202 ; seek relative to EOF
ES:
MOV BX,[DI] ; file handle
XOR CX,CX ; offset 0
XOR DX,DX
CALL dos ; do it: get file length
ES:
MOV CX,[DI]$06 ; buffer size
CMP CX,#$0080 ; < 128 ?
JB otsmall ; yes: ok
MOV CX,#$0080 ; go back up to 128 bytes
otsmall SUB AX,CX ; sub from file pos
SBB DX,#$00
JNB otnotbeg ; :ok
ADD AX,CX ; beyond beg of file -
MOV CX,AX ; go to beg of file
XOR AX,AX
XOR DX,DX
otnotbegPUSH CX ; save char count
MOV CX,DX ; dest pos
MOV DX,AX
MOV AX,#$4200 ; seek absolute
ES:
MOV BX,[DI] ; file handle
CALL dos ; do seek
CALL gbrdbuf ; read from buffer
POP DX ; counter
NEG DX
ES:
MOV SI,[DI]$08 ; buffer ptr
otsearchES:
CMP.B [SI],#$1A ; search ^Z
JZ oteof ; :found
INC SI ; next char
INC DX
JNZ otsearch ; :continue
JMP.b prepout ; 'EOF not found
oteof MOV AX,#$4202 ; seek from end
ES: ; DX = offset from end
MOV BX,[DI] ; file handle
MOV CX,#$FFFF ; backwards
CALL dos ; do seek
prepout ES: ; prepare for subsequent output
MOV.B [DI]$02,#$40 ; open for output
ES:
MOV AX,[DI]$04 ; buffer offset
ES:
MOV [DI]$08,AX ; -> buffer ptr
ES:
ADD AX,[DI]$06 ; + buffer size
ES:
MOV [DI]$0A,AX ; -> buffer end
poret RET ; "
xtrunctxPOP errpos ; Truncate text file
POP ES ; file var ptr
PUSH errpos ; restore ret
ES:
CMP.B [DI]$02,#$80 ; open for input ?
JNZ poret ; :no - ret
ES:
MOV DX,[DI]$08 ; buffer ptr
ES:
SUB DX,[DI]$0A ; - buffer end
JZ trend ; equal: ok
MOV AX,#$4201 ; seek relative
ES:
MOV BX,[DI] ; file handle
MOV CX,#$FFFF ; backwards
CALL dos ; do it
trend MOV AH,#$40 ; write
ES:
MOV BX,[DI] ; file handle
XOR CX,CX ; len = 0 -> truncate
CALL dos ; do it
JMP prepout ; "prepare for output
xflush POP errpos ; Flush
POP ES ; file var ptr
PUSH errpos ; restore ret
flush ES:
CMP.B [DI]$02,#$40 ; output file ?
JNZ flushret ; no: ret
JMP pbflush ; 'flush buffer
flushretRET ; "
xclosetxPOP errpos ; Close text file
POP ES ; get file var pt
PUSH errpos ; restore ret
close1 ES:
MOV.B AL,[DI]$02 ; get flags
AND AL,#$0F ; device ?
JNZ closeret ; yes: ret
CALL flush ; flush the buffer
ES:
MOV.B [DI]$02,#$00 ; clear flag
close2 ES:
MOV BX,[DI] ; file handle
CMP BX,#$02 ; standard file ?
JBE closeret ; :ret
CMP BX,#-$01 ; not open ?
JZ closeret ; yes:ret
ES:
MOV [DI],#$FFFF ; clear file handle
MOV AH,#$3E ; close file
CALL dos ; do it
JNB closeret ; :ok, no error
MOV.B errnum,#$FF ; file disappeared
closeretRET ; "
devtest MOV CX,#$0009 ; Test filename for device
MOV BX,#devtable
devloop PUSH CX ; save cnt, ptr
PUSH BX
MOV SI,#pnbuf ; file name
MOV CX,#$0003 ; 3 chars
devcloopMOV.B AL,[SI] ; get char
CALL upcase ; ignore upper / lower
CS:
CMP.B AL,[BX] ; compare
JZ devnextc ; :ok
POP BX ; restore cnt, ptr
POP CX
ADD BX,#$06 ; next device
LOOP devloop ; another one ?
devnone STC ; not found
RET ; '
devnextcINC SI ; next char
INC BX
LOOP devcloop ; :another char
POP CX ; remove
POP CX
CMP.B [SI],#$3A ; next char = : ?
JNZ devnone ; :no device
CS:
MOV.B AL,[BX] ; get flag
CS:
MOV BX,[BX]$01 ; get file handle
RET ; "
devtableB "CON" ; Device table
B $C1,$FF,$FF ; input, output, dev 1
B "TRM"
B $C1,$FF,$FF ; input, output, dev 1
B "KBD"
B $82,$FF,$FF ; input, dev 2
B "LST"
B $43,$FF,$FF ; output, dev 3
B "AUX"
B $C4,$FF,$FF ; input, output, dev 4
B "USR"
B $C5,$FF,$FF ; input, output, dev 5
B "INP" ; std MS-DOS input-file
B $00,$00,$00 ; not open, handle 0
B "OUT" ; std MS-DOS output-file
B $00,$01,$00 ; not open, handle 1
B "ERR" ; std MS-DOS error file
B $00,$02,$00 ; "not open, handle 2
openfileES: ; do open file
CMP [DI],#-$01 ; handle <> $ffff ?
JNZ opret ; yes: already open
MOV AX,#$3D02 ; open for input / output
MOV DL,#$01 ; error number
TEST.B filfunc,#$01 ; create file ?
JZ opnotnew ; :no
MOV AH,#$3C ; create
XOR CX,CX ; clear attribute
MOV DL,#$F1 ; error (dir full)
opnotnewPUSH DX ; save error number
LEA DX,[DI]$0C ; ptr to path name
CALL dos ; do it
POP DX ; restore error number
JB operr ; :error
ES:
MOV [DI],AX ; store file handle
RET ; '
operr MOV.B errnum,DL ; store error number
CMP AL,#$04 ; too many open files ?
JNZ opret ; :no
MOV.B errnum,#$F3 ; set that error
opret RET ; "
xstdin POP errpos ; Set standard input
MOV currfil,#stdin ; ptr to file var
MOV currfil1,DS
JMP [errpos] ; "return - error pos set
xrdfil POP errpos ; prepare for read
POP ES ; get file var ptr
MOV currfil,DI ; -> current file
MOV currfil1,ES
ES:
TEST.B [DI]$02,#$80 ; open for input ?
JNZ prret ; :yes
MOV.B errnum,#$02 ; error: not open for input
prret JMP [errpos] ; "return
xstdout POP errpos ; Set standard output
MOV currfil,#stdout ; ptr to file var
MOV currfil1,DS
JMP [errpos] ; "return
xwrfil POP errpos ; prepare for write
POP ES ; get file var ptr
MOV currfil,DI ; -> current file
MOV currfil1,ES
ES:
TEST.B [DI]$02,#$40 ; open for output ?
JNZ pwret ; :no
MOV.B errnum,#$03 ; error: not open for output
pwret JMP [errpos] ; "return
xrd MOV AL,#$FF ; Readln string
JMP.b readst ; '
xrdln XOR.B AL,AL ; Read string
readst POP errpos ; get ret addr
MOV currfil,#stdin ; from std input
MOV currfil1,DS
AND.B stdinfl,#$DF ; clear flag: char read
PUSH ES ; save dest var ptr
PUSH DI
PUSH AX ; save flag
CALL rdedit ; read with editing
POP AX ; restore flag
OR.B AL,AL ; readln ?
JZ readst2 ; :no
CALL xwriteln ; do WriteLn
readst2 POP DI ; restore dest var ptr
POP ES
JMP [errpos] ; "return
rdedit XOR.B DH,DH ; Read line with editing: clr flag
rdedit2 MOV.B CH,conbufln ; buffer length
CMP.B CH,#$7E ; too big ?
JB relimlen ; :no
MOV CH,#$7E ; limit to 127 chars
relimlenMOV.B conbufln,#$7E ; 127 chars again
MOV BX,#coninbuf
MOV conbufpt,BX ; set input ptr
rezero XOR.B CL,CL ; ptr into line
reloop CALL keyget ; get char
MOV DL,#$01 ; flag: one char
CMP AL,#$08 ; BS ?
JZ rebs ; :yes
CMP AL,#$7F ; Delete ?
JZ rebs ; :yes
CMP AL,#$04 ; ^D ?
JZ recall ; yes, recall char from buffer
DEC.B DL ; flag: all chars
CMP AL,#$18 ; ^X ?
JZ rebs ; yes:erase input line
CMP AL,#$1B ; ESC ?
JZ rebs ; yes:erase input line
CMP AL,#$12 ; ^R ?
JZ recall ; yes:recall last input line
CMP AL,#$1A ; ^Z ?
JZ reeof ; :yes
CMP AL,#$0D ; CR ?
JZ recr ; :yes
CMP AL,#$20 ; other control char ?
JB reloop ; yes: ignore
CMP.B CL,CH ; end of buffer reached ?
JZ reloop
MOV.B AH,[BX] ; get old char
MOV.B [BX],AL ; store new char
INC.B CL ; new pos
INC BX
CMP.B AH,#$20 ; was it old end of buffer ?
JNB renotend ; :no
MOV.B [BX],AH ; mark it again
renotendCALL xputch ; display new char
JMP reloop ; 'next key
rebs DEC.B CL ; go back one char
JS rezero ; :beg of line
CALL prints ; go back one char
B $08," ",$08,$00
DEC BX ; go back
DEC.B DL ; another char ?
JNZ rebs ; :yes
JMP reloop ; 'next key
recall MOV.B AL,[BX] ; recall from buffer
CMP AL,#$20 ; end of buffer
JB reloop ; :yes
CALL xputch ; display that char
INC.B CL ; next one
INC BX
DEC.B DL ; another one ?
JNZ recall ; :yes
JMP reloop ; 'next key
reeof OR.B DH,DH ; test flag
JZ reloop ; :no EOF allowed
JMP.b resto ; 'store it
recr OR.B DH,DH ; test flag
JNZ reend ; :no ^Z needed
resto MOV.B [BX],#$1A ; store ^Z
JMP.b reend2 ; 'end it
reend CALL xwriteln ; WriteLn
MOV [BX],#$0A0D ; store CR,LF at end
INC BX ; 2 chars added
reend2 INC BX ; 1 char added
MOV conbfend,BX ; store end pointer
RET ; "
xputch MOV.B AH,cbreak ; Put char: save break flag
MOV.B cbreak,#$00 ; break allowed
PUSH AX ; save flag
CALL conput ; print char
POP AX ; restore flag
MOV.B cbreak,AH
RET ; "
getbyte LES DI,currfil ; Get byte from current file
getbyte2CMP.B errnum,#$00 ; error ?
JNZ gbeof ; yes: return ^Z
ES:
MOV.B AL,[DI]$02 ; test flags:
TEST AL,#$20 ; char pre-read ?
JNZ gbfrbuf ; yes: return it
AND AL,#$0F ; device ?
JNZ gbdev ; :yes
ES:
MOV BX,[DI]$08 ; buffer pointer
ES:
CMP BX,[DI]$0A ; = buffer end ?
JB gbnotend ; :no
CALL gbrdbuf ; read buffer
ES:
MOV BX,[DI]$08 ; buffer ptr
gbnotendES:
MOV.B AL,[BX] ; get byte
INC BX ; advance ptr
ES:
MOV [DI]$08,BX ; store ptr
JMP.b gbbuf2 ; 'remember that char
gbdev PUSH ES ; save file ptr
PUSH DI ; test devices
CMP AL,#$01 ; CON ?
JNZ gbkbd ; :no
MOV BX,conbufpt ; get from CON input buffer
CMP BX,conbfend ; end of buffer reached ?
JB gbget ; no: return char
MOV.B DH,AL ; ^Z allowed
CALL rdedit2 ; read line
MOV BX,conbufpt ; ptr to char
gbget MOV.B AL,[BX] ; get char
INC BX ; advance ptr
MOV conbufpt,BX ; update ptr
JMP.b gbbuf ; 'done
gbkbd CMP AL,#$02 ; KBD ?
JNZ gbaux ; :no
DEC SP ; (function:char)
CALL [vkbdget] ; get char
JMP.b gbbuf ; 'return it
gbaux CMP AL,#$04 ; AUX ?
JNZ gbusr ; :no
DEC SP
CALL [vauxget] ; get char
JMP.b gbbuf ; 'return it
gbusr DEC SP ; USR
CALL [vusrget] ; get char
gbbuf POP DI ; restore file var ptr
POP ES
gbbuf2 ES:
MOV.B [DI]$03,AL ; store char in buffer
ES:
OR.B [DI]$02,#$20 ; set flag: char pre-read
RET ; '
gbfrbuf ES:
MOV.B AL,[DI]$03 ; get char from buffer
RET ; '
gbeof MOV AL,#$1A ; return ^Z = EOF
RET ; '
gbrdbuf MOV AH,#$3F ; read buffer
ES:
MOV BX,[DI] ; file handle
ES:
MOV CX,[DI]$06 ; buffer size
ES:
MOV DX,[DI]$04 ; buffer offset
PUSH DS ; save DS
PUSH ES ; ES -> DS
POP DS
CALL dos ; do it
POP DS ; restore DS
JNB gbnoerr ; :no error
XOR AX,AX ; nothing read
gbnoerr ES:
MOV BX,[DI]$04 ; buffer offset
OR AX,AX ; anything read ?
JNZ gbmakeof ; :yes
ES:
MOV.B [BX],#$1A ; store a ^Z
INC AX ; 1 char read
gbmakeofES:
MOV [DI]$08,BX ; -> buffer pointer
ADD BX,AX ; + number chars read
ES:
MOV [DI]$0A,BX ; -> buffer end
RET ; "
readnum PUSH ES ; Read number
PUSH DI ; save ptr
MOV BX,#pnbuf ; buffer ptr
rnspace PUSH BX ; save
CALL getbyte ; get char
POP BX ; restore ptr
CMP AL,#$1A ; ^Z ?
JZ rnend ; yes: end
ES:
AND.B [DI]$02,#$DF ; clear flag: char pre-read
CMP AL,#$20 ; space / control ?
JBE rnspace ; yes: ignore
rndig MOV.B [BX],AL ; store that char
INC BX ; advance ptr
CMP BX,#pnbufend ; end reached ?
JZ rnend ; :yes
PUSH BX ; save ptr
CALL getbyte ; get char
POP BX ; restore ptr
CMP AL,#$20 ; space / control ?
JBE rnend ; yes: end
ES:
AND.B [DI]$02,#$DF ; clear flag: char pre-read
JMP rndig ; 'next digit
rnend MOV.B [BX],#$00 ; store a 0 at the end
MOV BX,#pnbuf ; nothing entered ?
CMP.B [BX],#$00
POP DI ; restore ptr
POP ES
RET ; "
chknum JB cnerr ; Check numeric format
CMP.B [BX],#$00 ; end reached ?
JZ cnret ; yes: ok
cnerr MOV.B errnum,#$10 ; Error in numeric format
STC
cnret RET ; "
xrdchar PUSH DI ; Read char: save ptr
CALL getbyte ; get char
ES:
AND.B [DI]$02,#$DF ; clear flag: char pre-read
POP DI ; var ofs
POP BX ; ret addr
POP ES ; var seg
ES:
MOV.B [DI],AL ; store char
JMP BX ; "return
xrdbyte CLC ; Read byte
JMP.b rdi1 ; '
xrdint STC ; Read integer
rdi1 POP BX ; ret addr
POP ES ; dest seg
PUSH BX ; restore ret
PUSHF ; save flag
CALL readnum ; read number.
JZ rdierr ; :nothing entered
CALL ascint ; convert to integer
CALL chknum ; check numeric format
JB rdierr ; :error
POPF ; restore flag
JNB rdibyt ; :byte
ES:
MOV [DI],AX ; store integer
RET ; '
rdibyt ES:
MOV.B [DI],AL ; store byte
RET ; '
rdierr POPF ; remove flag
RET ; "error - don't change var
xrdreal POP BX ; Read real
POP ES ; dest seg
PUSH BX ; restore ret
CALL readnum ; read number
JZ rdrret ; :nothing entered
PUSH DI ; save dest ptr
PUSH ES
MOV DI,#prnum ; dest var
CALL ascreal ; convert to real
MOV SI,DI ; -> source ptr
POP ES ; restore dest ptr
POP DI
CALL chknum ; check numeric format
JB rdrret ; :error
CLD ; store real number
MOVS
MOVS
MOVS
rdrret RET ; "
xrdstr POP BX ; Read string var
POP ES ; dest seg
PUSH BX ; restore ret
XOR BX,BX ; clear length of string
XOR.B CH,CH ; count. CL=max len
rdsloop PUSH ES ; save dest ptr
PUSH DI
PUSH BX ; save len, cnt
PUSH CX
CALL getbyte ; get char
POP CX ; restore
POP BX
CMP AL,#$0D ; CR ?
JZ rdsend ; :end
CMP AL,#$1A ; ^Z ?
JZ rdsend ; :end
ES:
AND.B [DI]$02,#$DF ; clear flag: char pre-read
POP DI ; restore dest ptr
POP ES
INC BX ; count len
ES:
MOV.B [BX_DI],AL ; store char
LOOP rdsloop ; :another char
JMP.b rdslen ; 'store length
rdsend POP DI ; restore dest ptr
POP ES
rdslen ES:
MOV.B [DI],BL ; store length
RET ; "
xrdarrchPOP BX ; Read array of char
POP ES ; dest ptr
PUSH BX ; restore ret
XOR.B CH,CH ; CL=max len
rdacloopPUSH ES ; save dest ptr
PUSH DI
PUSH CX ; save cnt
CALL getbyte ; get a char
POP CX ; restore cnt
CLD
CMP AL,#$0D ; CR ?
JZ rdacend ; yes: end
CMP AL,#$1A ; ^Z ?
JZ rdacend ; :yes
ES:
AND.B [DI]$02,#$DF ; clear flag: char pre-read
POP DI ; restore dest ptr
POP ES
STOS.B ; store char
LOOP rdacloop ; :another char
RET ; '
rdacend POP DI ; restore dest ptr
POP ES
MOV AL,#$20 ; pad with spaces
REPZ
STOS.B
RET ; "
xreadln CALL getbyte ; Readln: get char
CMP AL,#$1A ; ^Z ?
JZ rdlnret ; yes: done
ES:
AND.B [DI]$02,#$DF ; clear flag: char pre-read
CMP AL,#$0A ; LF ?
JZ rdlnret ; yes: done
CMP AL,#$0D ; CR ?
JNZ xreadln ; no: continue
CALL getbyte ; get next char
CMP AL,#$0A ; LF ?
JNZ rdlnret ; no: forget it
ES:
AND.B [DI]$02,#$DF ; clear flag: char pre-read
rdlnret RET ; "
putbyte LES DI,currfil ; put char to current file
CMP.B errnum,#$00 ; error ?
JNZ pbret ; yes: no output
ES:
MOV.B CL,[DI]$02 ; get flag
AND.B CL,#$0F ; device ?
JNZ pbdev ; :yes
ES:
MOV BX,[DI]$08 ; get buffer pointer
ES:
MOV.B [BX],AL ; store char
INC BX ; advance pointer
ES:
MOV [DI]$08,BX ; update pointer
ES:
CMP BX,[DI]$0A ; = buffer end ?
JZ pbflush ; yes: do flush
RET ; '
pbdev PUSH AX ; put char as parameter
CMP.B CL,#$01 ; CON ?
JZ pbcon ; :yes
CMP.B CL,#$03 ; LST ?
JZ pblst ; :yes
CMP.B CL,#$04 ; AUX ?
JZ pbaux ; :yes
CALL [vusrput] ; USR out
RET ; '
pbcon CALL [vconput] ; CON out
RET ; '
pblst CALL [vprnput] ; LST out
RET ; '
pbaux CALL [vauxput] ; AUX out
pbret RET ; '
pbflush ES: ; flush output buffer
MOV CX,[DI]$08 ; count=pointer-offset
ES:
SUB CX,[DI]$04
JZ pbok ; :nothing to write
MOV AH,#$40 ; write byte block
ES:
MOV BX,[DI] ; file handle
ES:
MOV DX,[DI]$04 ; buffer offset
ES:
MOV [DI]$08,DX ; reset buffer pointer
PUSH DS ; save DS
PUSH ES ; ES -> DS
POP DS
CALL dos ; do it
POP DS ; restore DS
JB pberr ; :error
CMP AX,CX ; length = expected ?
JZ pbok ; :yes
pberr MOV.B errnum,#$F0 ; Disk write error
pbok RET ; "
xwrchar OR AX,AX ; Write char
JZ wchdoit ; AX=formatting parm: nothing
CALL limstind ; limit it
CMP AL,#$01 ; <= 1 char ?
JBE wchdoit
XCHG AX,CX ; -> count
DEC CX ; -1 for char itself
wchpad MOV AL,#$20 ; put spaces
PUSH CX ; save count
CALL putbyte ; put it
POP CX ; restore count
LOOP wchpad ; :continue
wchdoit POP BX ; ret addr
POP AX ; get char
PUSH BX ; restore ret
JMP putbyte ; "put that char
xwrint XCHG AX,CX ; Write integer: format parm
POP BX ; return addr
POP AX ; get number
PUSH BX ; restore ret
PUSH CX ; save format parm
MOV BX,#pnbuf ; dest buffer
CALL intasc ; Integer -> ASCII
wintbuf POP AX ; restore format parm
CALL limstind ; limit it
SUB BX,#pnbuf ; calc length
SUB AX,BX ; length of spaces ?
JBE wintdoit ; :no space left
XCHG AX,CX ; -> count
PUSH BX ; save number length
wintpad MOV AL,#$20 ; put spaces
PUSH CX ; save count
CALL putbyte ; put it
POP CX ; restore count
LOOP wintpad ; :another space
POP BX ; restore number length
wintdoitMOV CX,BX ; -> count
MOV BX,#pnbuf ; buffer ptr
wintloopMOV.B AL,[BX] ; get char
PUSH BX ; save ptr, cnt
PUSH CX
CALL putbyte ; put it
POP CX ; restore
POP BX
INC BX ; next char
LOOP wintloop ; :again
RET ; "
xwrreal XCHG AX,DX ; Write real: second format parm
POP BX ; ret addr
POP CX ; first format parm
MOV DI,#prnum ; get number from stack
POP [DI]
POP [DI]$02
POP [DI]$04
PUSH BX ; restore ret
PUSH CX ; push format parm
MOV BX,#pnbuf ; dest buffer
CALL fmtreal ; Real -> ASCII
JMP wintbuf ; "write buffer
xwrbool POP BX ; write boolean
POP CX ; get boolean
PUSH BX ; restore ret
MOV DI,#sttrue ; (true)
OR CX,CX ; test boolean
JNZ wbotrue ; :true
MOV DI,#stfalse ; (false)
wbotrue PUSH CS ; str segment
CALL strload ; load string
CALL xwrtstr ; write string
RET ; "
sttrue B $04,"TRUE"
stfalse B $05,"FALSE" ; "
xwrtstr CALL limstind ; Write string: format parm
MOV BX,SP ; pos of string
INC BX ; skip ret addr
INC BX
SS:
SUB.B AL,[BX] ; field len - string len
JBE wstdoit ; :too much - no padding
MOV.B CL,AL ; -> count
XOR.B CH,CH
PUSH BX ; save string pos
wstpad MOV AL,#$20 ; put spaces
PUSH CX ; save count
CALL putbyte ; put it
POP CX ; restore count
LOOP wstpad ; :another space
POP BX ; restore string pos
wstdoit SS:
MOV.B CL,[BX] ; get length
XOR.B CH,CH ; -> count
INC BX ; point to first char
OR CX,CX ; nothing to write ?
JZ wstnil ; :yes
wstloop SS:
MOV.B AL,[BX] ; get char
PUSH BX ; save ptr, cnt
PUSH CX
CALL putbyte ; put char
POP CX ; restore
POP BX
INC BX ; next char
LOOP wstloop ; :another
wstnil POP DX ; ret addr
MOV SP,BX ; remove string from stack
JMP DX ; "return
xwrtinl POP BX ; Write inline string
CS:
MOV.B CL,[BX] ; get length
XOR.B CH,CH ; -> count
INC BX ; point to first char
JCXZ wstinil ; :null string
wstiloopCS:
MOV.B AL,[BX] ; get char
PUSH BX ; save
PUSH CX
CALL putbyte ; put char
POP CX
POP BX ; restore
INC BX ; next char
LOOP wstiloop ; :another
wstinil JMP BX ; "return - text skipped
xwrln MOV AL,#$0D ; WriteLn
CALL putbyte ; put CR
MOV AL,#$0A
JMP putbyte ; "put LF
xseekeolMOV DX,#$010D ; SeekEOLN
JMP.b seox1 ; '
xeoln MOV DX,#$000D ; EOLN
JMP.b seox1 ; '
xseekeofMOV DX,#$011A ; SEEKEOF
JMP.b seox1 ; '
xeoftx MOV DX,#$001A ; EOF
seox1 POP errpos ; get return addr
POP ES ; file var seg
PUSH errpos ; restore ret
ES:
TEST.B [DI]$02,#$80 ; open for input ?
JZ sefalse ; no: false
seloop PUSH DX ; save flag
CALL getbyte2 ; get char from file
POP DX ; restore flag
CMP.B AL,DL ; = char searched ?
JZ setrue ; yes: true
CMP AL,#$1A ; EOF ?
JZ setrue ; yes: true
CMP AL,#$20 ; space / control ?
JA sefalse ; no: false
OR.B DH,DH ; seek it ?
JZ sefalse ; :no, false
ES:
AND.B [DI]$02,#$DF ; clear flag: char pre-read
JMP seloop ; 'search it
setrue XOR AX,AX ; return true
INC AX ; setting flags
RET ; '
sefalse XOR AX,AX ; return false
RET ; "
xassign XOR.B AL,AL ; Assign: set function code
JMP doassign ; "do assign
xresettyXOR.B AL,AL ; Reset typed
JMP.b openty1 ; '
xrewrttyMOV AL,#$01 ; Rewrite typed
openty1 MOV filfunc,AL ; store function code
POP errpos ; get return addr
POP ES ; file var ptr
PUSH errpos ; restore ret
PUSH CX ; save record length
CALL closty1 ; clear record len, close file
POP CX ; remove for error exit
CMP.B errnum,#$00 ; error ?
JNZ openty2 ; :yes
PUSH CX
CALL openfile ; open that file
POP CX ; record len
CMP.B errnum,#$00 ; error ?
JNZ openty2 ; :yes
ES:
MOV [DI]$02,CX ; store record length
openty2 RET ; "
xtruncatPOP errpos ; Truncate typed, untyped
POP ES ; file var ptr
PUSH errpos ; restore ret
MOV AH,#$40 ; write
ES:
MOV BX,[DI] ; file handle
XOR CX,CX ; length = 0 -> truncate
JMP dos ; "do it
xflushtyRET $0002 ; "Flush typed, untyped
xclosetyPOP errpos ; Close typed, untyped
POP ES ; file var ptr
PUSH errpos ; restore ret
closty1 ES:
MOV [DI]$02,#$0000 ; record len = 0
JMP close2 ; "close it
xfilsel POP errpos ; Select file
POP ES ; file var ptr
MOV currfil,DI ; -> current file
MOV currfil1,ES
ES:
CMP [DI]$02,#$00 ; record len = 0 ?
JNZ selret ; no: ok
MOV.B errnum,#$04 ; File not open
selret JMP [errpos] ; "ret
xrdvar MOV filfunc,#$993F ; Read from typed file
JMP.b filrw ; 'read, unexpected EOF
xwrvar MOV filfunc,#$F040 ; Write to typed file
filrw POP BX ; write, disk write error
POP SI ; var seg
PUSH BX ; restore ret
CMP.B errnum,#$00 ; error ?
JNZ filrwret ; yes: no operation
MOV DX,DI ; save DI
LES DI,currfil ; current file var
MOV.B AH,filfunc ; save function, error code
ES:
MOV BX,[DI] ; file handle
ES:
MOV CX,[DI]$02 ; record length
PUSH DS ; save DS
MOV DS,SI ; ptr var
CALL dos ; do it
POP DS ; restore DS
JB filrwerr ; :error
CMP AX,CX ; length = expected ?
JZ filrwret ; :yes
CMP.B filfunc,#$3F ; read ?
JNZ filrwerr ; no: error
OR AX,AX ; test count
JZ filrwerr ; nothing done: error
ES:
MOV CX,[DI]$02 ; record length
MOV DI,DX ; restore dest ofs
ADD DI,AX ; add count read
MOV ES,SI ; dest seg
SUB CX,AX ; count to fill
XOR AX,AX ; pad with zeroes
CLD
REPZ ; do fill
STOS.B
RET ; '
filrwerrMOV AL,filerr ; error code -> error
MOV errnum,AL
filrwretRET ; "
xseek XOR DX,DX ; Seek: clear high word
seek1 POP errpos ; set error pos
POP DI ; get file var ptr
POP ES
PUSH errpos ; restore ret
ES:
MOV CX,[DI]$02 ; get record length
CALL cardmul ; * pos -> DX:AX
MOV CX,DX ; -> CX:DX
MOV DX,AX
MOV AX,#$4200 ; seek absolute
ES:
MOV BX,[DI] ; file handle
PUSH CX ; save pos wanted
PUSH DX
CALL dos ; do it
POP CX ; restore pos wanted
POP BX
JB seekerr ; :error
CMP AX,CX ; pos = expected ?
JNZ seekerr ; :no
CMP DX,BX
JZ seekret ; :yes
seekerr MOV.B errnum,#$91 ; Seek beyond EOF
seekret RET ; "
xlngseekCALL realcard ; LongSeek: convert real
JMP seek1 ; "do it
xeofty POP BX ; EOF typed, untyped
POP ES ; get file var ptr
PUSH BX ; restore ret
MOV AX,#$4406 ; get unit status
ES:
MOV BX,[DI] ; file handle
CALL dos ; do it
OR.B AL,AL ; test result
MOV AX,#$0000 ; false
JNZ eoffalse ; :ok
INC AX ; true
eoffalseOR AX,AX ; set flags
RET ; "
xfileposPOP BX ; FilePos: ret addr
POP ES ; file var ptr
PUSH BX ; restore ret
filpos1 MOV AX,#$4201 ; seek relative
ES:
MOV BX,[DI] ; file handle
XOR CX,CX ; no offset
XOR DX,DX
CALL dos ; get position
filpos2 ES:
MOV CX,[DI]$02 ; record length
JMP.b carddiv ; "pos / reclen -> filepos
NOP ; LongFilePos
xlfilposPOP BX ; return addr
POP ES ; file var ptr
PUSH BX ; restore ret
CALL filpos1 ; do it
JMP cardreal ; "result -> real
xfilesizPOP BX ; FileSize: ret addr
POP ES ; file var ptr
PUSH BX ; restore ret
filsiz1 MOV AX,#$4201 ; seek relative
ES:
MOV BX,[DI] ; file handle
XOR CX,CX ; no offset
XOR DX,DX
CALL dos ; get current pos
PUSH AX ; save it
PUSH DX
MOV AX,#$4202 ; seek from end
ES:
MOV BX,[DI] ; file handle
XOR CX,CX ; no offset
XOR DX,DX
CALL dos ; get end position
POP CX ; restore current pos
POP BX
PUSH AX ; save end pos
PUSH DX
MOV DX,BX ; restore file pos
MOV AX,#$4200 ; seek absolute
ES:
MOV BX,[DI] ; file handle
CALL dos ; go back to old pos
POP DX ; end position
POP AX
ES:
MOV CX,[DI]$02 ; (pos+reclen-1)
DEC CX
ADD AX,CX
ADC DX,#$00
JMP filpos2 ; "/ reclen -> position
xlfilsizPOP BX ; LongFileSize: ret addr
POP ES ; file var ptr
PUSH BX ; restore ret
CALL filsiz1 ; do it
JMP cardreal ; "result -> real
carddiv CMP CX,#$01 ; long cardinal division
JZ cdret ; / 1: done
MOV SI,CX ; DX:AX / CX -> DX:AX
XOR BX,BX ; clear result
MOV CX,#$0021 ; 32 bits
cdloop RCL BX,1 ; shift in result
SBB BX,SI ; try subtraction
JNB cdbit1 ; :ok
ADD BX,SI ; restore it
STC
cdbit1 CMC ; make flag to shift in
RCL AX,1 ; shift in result
RCL DX,1
LOOP cdloop ; :another bit
cdret RET ; "
cardmul MOV BX,AX ; long card mul
MOV AX,DX ; DX:AX * CX -> DX:AX
MUL CX ; higher word
XCHG AX,BX ; save it
MUL CX ; lower word
ADD DX,BX ; add higher result
RET ; "
xresetunXCHG AX,CX ; Reset untyped: record length
POP BX ; ret addr
POP DI ; file var ofs
PUSH BX ; restore ret
JMP xresetty ; "now like typed file
xrewrtunXCHG AX,CX ; Rewrite untyped: record length
POP BX ; ret addr
POP DI ; file var ofs
PUSH BX ; restore ret
JMP xrewrtty ; "now like typed file
xblkrd MOV filfunc,#$993F ; BlockRead
JMP.b blrw1 ; 'read, unexpected EOF
xblkwr MOV filfunc,#$F040 ; BlockWrite
blrw1 POP errpos ; write, disk write error
POP DX ; var seg
POP SI ; var ofs
POP DI ; file var ptr
POP ES
PUSH AX ; save length
CALL blrw ; do it
POP CX ; restore len
CMP.B errnum,#$00 ; error ?
JNZ blrw1ok ; yes: return
CMP AX,CX ; length = expected
JZ blrw1ok ; :yes
MOV AL,filerr ; error code -> error
MOV errnum,AL
blrw1ok JMP [errpos] ; "return
xblkrdrdMOV filfunc,#$993F ; BlockRead with result var
JMP.b blrwres ; '
xblkwrrsMOV filfunc,#$F040 ; BlockWrite with result var
blrwres POP errpos ; get error pos
MOV CX,DI ; result var ptr
POP BX
POP AX ; length
POP DX ; var seg
POP SI ; var ofs
POP DI ; file var ptr
POP ES
PUSH BX ; save result var ptr
PUSH CX
CALL blrw ; do it
POP DI ; restore result var ptr
POP ES
ES:
MOV [DI],AX ; store record count
JMP [errpos] ; "return
blrw ES: ; do Block-R/W
CMP [DI]$02,#$00 ; record length = 0 ?
JZ blrwerr ; yes: file not open
ES:
CMP [DI]$02,#$01 ; record len = 1 ?
JZ blrwbyt ; :no calculation
PUSH DX ; save
ES:
MUL [DI]$02 ; count * record length
POP DX ; restore
blrwbyt XCHG AX,CX ; length -> CX
MOV.B AH,filfunc ; function code
ES:
MOV BX,[DI] ; file handle
PUSH DS ; save DS
MOV DS,SI ; var seg
CALL dos ; do file operation
POP DS ; restore DS
JNB blrwok ; :ok
MOV AL,filerr ; error code -> error
MOV errnum,AL
XOR AX,AX ; nothing read / written
blrwok ES:
MOV CX,[DI]$02 ; record length
CMP CX,#$01 ; = 1 ?
JZ blrwret ; yes: no division
MOV DI,DX ; offset + count done
ADD DI,AX
XOR DX,DX ; clear hi
DIV CX ; count done / record len
OR DX,DX ; test remainder
JZ blrwret ; :ok
CMP.B filfunc,#$3F ; read ?
JNZ blrwret ; no: end it
PUSH AX ; save count done
SUB CX,DX ; fill up the rest
MOV ES,SI ; var seg
XOR AX,AX ; with zeroes
CLD
REPZ ; do it
STOS.B
POP AX ; restore result
INC AX ; +1
blrwret RET ; '
blrwerr MOV.B errnum,#$04 ; File not open
RET ; "
xerase POP errpos ; Erase
POP ES ; file var ptr
PUSH errpos ; restore ret
MOV AH,#$41 ; delete file
LEA DX,[DI]$0C ; ptr to filename
PUSH DS ; save DS
PUSH ES ; ES -> DS
POP DS
CALL dos ; do it
POP DS ; restore DS
JNB eraret ; :no error
eraerr MOV.B errnum,#$01 ; file not found
eraret RET ; "
xrename POP errpos ; Rename
CALL getpn ; convert string -> ASCIIZ
POP DI ; file var ptr
POP ES
PUSH errpos ; restore ret
MOV AH,#$56 ; Rename file
LEA DX,[DI]$0C ; current file name
PUSH DI ; save addr
MOV DI,#pnbuf ; filename ptr
PUSH DS ; ES <-> DS
PUSH ES
POP DS
POP ES
CALL dos ; do rename
PUSH DS ; ES <-> DS
PUSH ES
POP DS
POP ES
POP DI ; restore addr
JB eraerr ; :error
MOV SI,#pnbuf ; copy new name into file var
LEA DI,[DI]$0C ; file var offset
MOV CX,#$0020 ; 64 bytes
CLD
REPZ
MOVS ; copy it
RET ; "
xchdir POP errpos ; ChDir: get error pos
CALL getpn ; get path name
PUSH errpos ; restore ret
MOV AX,pnbuf ; get drive #
OR.B AL,AL ; end of name ?
JZ chdirret ; yes: return
CMP.B AH,#$3A ; : ?
JNZ chdir1 ; no: no drive specified
CALL upcase ; upcase drive number
SUB AL,#$41 ; - A
JB eraerr ; <:error
CMP AL,#$0F ; max. O
JNB eraerr ; :error.
; Please note that MS-DOS now allows more drives !!!
MOV AH,#$0E ; set default drive
MOV.B DL,AL ; drive number
CALL dos ; set it
CMP.B pnbuf2,#$00 ; pathname ?
JZ chdirret ; :none given
chdir1 MOV AH,#$3B ; change dir
chdir2 MOV DX,#pnbuf ; pointer to pathname
CALL dos ; set it
JB eraerr ; :error
chdirretRET ; "
xmkdir MOV BH,#$39 ; MkDir
JMP.b rmdir1 ; '
xrmdir MOV BH,#$3A ; RmDir
rmdir1 POP errpos ; return addr -> error pos
CALL getpn ; get path name
PUSH errpos ; restore ret
MOV.B AH,BH ; get function code
JMP chdir2 ; "do it
xgetdir POP errpos ; GetDir: CL=max. string len
POP ES ; dest string
POP AX ; drive number
PUSH errpos ; restore ret
OR.B AL,AL ; default drive ?
JNZ gdnotdef ; :no
MOV AH,#$19 ; get default drive number
CALL dos
INC.B AL ; +1
gdnotdefMOV.B DL,AL ; drive number
ADD AL,#$40 ; -> drive name
MOV pnbuf,AL ; store in buffer
MOV pnbuf1,#$5C3A ; store :\
MOV AH,#$47 ; read current access path
MOV SI,#pnbuf3 ; dest ptr
CALL dos ; do it
JNB gddone ; :ok
MOV.B [SI],#$00 ; no path - mark end
gddone MOV SI,#pnbuf ; pointer to path name
XOR BX,BX ; clear len counter
gdloop MOV.B AL,[SI] ; get char
OR.B AL,AL ; end ?
JZ gdend ; :yes
INC SI ; next char
INC BX ; count length
ES:
MOV.B [BX_DI],AL ; store in dest string
DEC.B CL ; space available ?
JNZ gdloop ; :yes
gdend ES: ; store length
MOV.B [DI],BL
RET ; "
xexecuteMOV BX,#$2C7C ; Execute
JMP.b chain1
xchain XOR BX,BX ; Chain: load all of it
chain1 POP errpos ; get ret addr
POP ES ; file var ptr
PUSH errpos ; restore ret
TEST modeflg,#$0001 ; direct mode ?
JNZ cherr2 ; yes: error
PUSH BX ; save begin offset
MOV AX,#$3D00 ; open file
LEA DX,[DI]$0C ; ptr to filename
CALL dos ; do it
POP DX ; begin offset
JB cherr1 ; :error
MOV BX,AX
MOV AX,#$4200 ; seek absolute
XOR CX,CX ; clr hi word
CALL dos ; do seek
JB cherr1 ; :error
PUSH DS ; save DS
PUSH CS ; CS -> DS
POP DS
MOV AH,#$3F ; read byte block
MOV CX,#$FFFF ; as much as possible
MOV DX,#start ; destination
CALL dos ; do it
POP DS ; restore DS
MOV AH,#$3E ; close file
CALL dos
MOV SP,spval ; restore SP
CALL reinit2 ; reinit files, I/O
MOV verror,#$10D0 ; set break vector
JMP start2 ; 'start - no memory init
cherr1 MOV DL,#$01 ; File not found
JMP operr ; '
cherr2 MOV.B errnum,#$21 ; Not allowed in direct mode
RET ; "
start JMP.b kstart ; 'skip message
B "Licensed Material. Program Property of B"
B "ORLAND." ; '
kstart CALL readvers ; read version number
MOV AX,#$9C06 ; (program size+15)/16
MOV CL,#$04
SHR AX,CL
MOV DX,CS ; +CS -> DS
ADD DX,AX
MOV DS,DX
CS:
MOV AX,availmem ; top of memory
SUB AX,DX
MOV freemem,AX ; max memory size
MOV BX,#$FFFE ; stack pointer
SUB AX,#$1000 ; mem - 64K
JNB kmembig ; :ok
MOV BX,freemem ; max memory size
MOV CL,#$04 ; small memory: SS at end of DS
SHL BX,CL ; calc SP
XOR AX,AX ; no segment offset
kmembig ADD AX,DX ; add to DS
MOV stackseg,AX ; -> stack segment
MOV stackpt,BX ; -> stack pointer
MOV AX,freemem ; max memory size
SUB AX,#$0040 ; - 64 paragraphs
CMP AX,#$1000 ; = 64K ?
JB kmemlim ; :less
MOV AX,#$1000 ; limit to 64K
kmemlim MOV CL,#$04
SHL AX,CL
DEC AX
MOV txmemend,AX ; store -> end of text space
XOR AX,AX
kmeminitMOV SS,stackseg ; set stack segment
MOV SP,stackpt ; set stack pointer
PUSH AX ; save flag
MOV CX,#$0010 ; 16 files
CALL initio ; init files, I/O
POP AX ; get flag
OR AX,AX ; test it
PUSH AX ; save it again
JNZ kcrtinit ; :set
CALL xcrtinit ; CrtInit
kcrtinitCALL klowvid ; LowVideo
CALL knrmvid ; NormVideo
POP AX ; get flag
OR AX,AX ; test it
JZ kinitvar ; :init
JMP kmainlp ; 'go to main loop
kinitvarMOV AX,#txstrt ; init vars
MOV txbeg,AX ; beg of text
MOV txend,AX ; end of text
XOR AX,AX ; clear vars
MOV mincssz,AX ; min CS-size = 0
MOV mindssz,AX ; min DS-size = 0
MOV minhpsz,#$0400 ; min free heap = 16K
MOV maxhpsz,#$A000 ; max free heap = 640K
MOV workpn,AL ; clear work filename
MOV mainpn,AL ; clear main filename
MOV parmlin,AL ; clear param line
MOV errio,AL ; clear error
MOV.B codedest,#$01 ; code destination: memory
CALL inited ; init variables
MOV AH,#$19 ; get default drive
CALL dos
MOV defdrv,AL ; store it
CALL xclrscr ; ClrScr
CALL prints ; write string
B "---------------------------------------",$0D
B $0A,"TURBO Pascal system ",$00
CALL klowvid ; do LowVideo
CALL prints ; write string
B "Version 3.01A",$00
CALL knrmvid ; do HighVideo
CALL prints ; write string
B $0D,$0A," ",$00
CALL klowvid
CALL prints
B "PC-DOS",$00
CALL knrmvid
CALL prints
B $0D,$0A,$0A,$00
CALL klowvid
CALL prints
B "Copyright (C) 1983,84,85",$00
CALL knrmvid
CALL prints
B " BORLAND Inc.",$0D,$0A,"-----------------------"
B "----------------",$0D,$0A,$0A,$00
MOV BX,#displstr ; write display type
CALL putstr
CALL prints
B $0D,$0A,$0A,$0A,$0A,"Include error messages",$00
CALL yornln ; Y or N ?
MOV msgflg,AL ; store flag
JZ kclr ; :no
CALL kreaderr ; read error messages
kclr CALL kclrtxt ; clear text
CALL kmenu ; write menu
kmainlp MOV SP,stackpt ; main loop: restore SP
MOV BX,#kmainlp ; set a return addr
PUSH BX
CMP.B errio,#$00 ; error ?
JZ kgetcmd ; :no
JMP kdofind ; 'search error
kgetcmd CALL printatt ; write string with attributes
B $8D,$8A,$BE,$00 ; CR,LF,>
CALL knrmvid
CALL keyget ; get command
CALL upcase ; convert to upper case
PUSH AX ; save it
CALL xwriteln ; WriteLn
POP AX ; restore command
MOV SI,#kcmdtab ; ptr to command table
CALL srchcmd ; search command
JB kmenu ; not found: write menu
CS:
JMP [SI]$01 ; "execute command
kreaddefCLD ; Transfer string into CON buffer
MOV DI,#coninbuf ; dest ptr
PUSH DS ; DS -> ES
POP ES
kreaddl LODS.B ; SI: source ptr
STOS.B ; copy string
OR.B AL,AL ; test for end
JNZ kreaddl ; :no
kreadln CALL prints ; Readln: write string
B ": ",$00
CALL rdedit ; read with editing
MOV BX,#coninbuf ; buffer ptr
CALL xwriteln ; WriteLn
krdln1 MOV.B AL,[BX] ; get char from buffer
CMP AL,#$1A ; ^Z ?
JZ krdlnret ; yes - end
CMP AL,#$20 ; space ?
JNZ krdlnret ; :no, ret
INC BX ; skip spaces
JMP krdln1 ; 'continue scanning
krdlnretRET ; "BX now points into buffer
krdend CMP.B [BX],#$1A ; Test end of line
RET ; "
kmenu CALL knrmvid ; Write menu
CALL xclrscr ; ClrScr
CALL printatt
B $CC,"ogged drive: ",$80,$00
MOV AH,#$19 ; get default drive
CALL dos
ADD AL,#$41 ; convert to ASCII
CALL conput ; write its name
CALL printatt
B $0D,$0A,$C1,"ctive directory: ",$DC,$00
MOV AH,#$47 ; get active directory
XOR.B DL,DL ; default drive
MOV SI,#pnbuf ; dest buffer
CALL dos ; do it: get pathname
CALL printpn ; write that pathname
CALL printatt
B $0D,$0A,$0A,$D7,"ork file: ",$80,$00
CALL kwworkpn ; write current filename
CALL printatt
B $0D,$0A,$CD,"ain file: ",$80,$00
MOV SI,#mainpn ; main filename
CALL printpn ; write it
CALL printatt ; write command menu
B $0D,$0A,$0A,$C5,"dit ",$C3,"ompile ",$D2,"un ",$D3
B "ave",$0D,$0A,$0A,$C4,"ir ",$D1,"uit compiler ",$CF
B "ptions",$0D,$0A,$0A,"Text: ",$00
MOV AX,txend ; end of text
SUB AX,txbeg ; - beg of text
CALL kbytes ; print number
CALL prints
B "Free: ",$00
MOV AX,txmemend ; top of text space
SUB AX,txend ; - end of text
kbytes CALL knumax ; print number
CALL prints
B " bytes",$0D,$0A,$00
RET ; "
kcdest DEC.B AL ; show code direction: this one ?
JNZ kcdestno ; count it down: no
CALL printatt
B "compile -> ",$00
RET ; '
kcdestnoCALL printatt ; clear field
B " ",$00
RET ; "
optmenu MOV AX,#optmenu ; Option-menu
PUSH AX ; put return addr
optmenu2CALL xclrscr ; ClrScr
MOV AL,codedest ; code destination
CALL kcdest ; show it
CALL printatt
B $CD,"emory",$0D,$0A,$00
CALL kcdest ; show it
CALL printatt
B $C3,"om-file",$0D,$0A,$00
CALL kcdest ; show it
CALL printatt
B "c",$C8,"n-file",$0D,$0A,$0A,$0A,$00
CMP.B codedest,#$02 ; to COM-file ?
JZ optmcom ; :yes
JMP optmparm ; '
optmcom CALL printatt ; display memory information
B "minimum c",$CF,"de segment size: ",$00
CALL knrmvid
MOV AX,mincssz ; min CS size
CALL whexword ; write hex
CALL printatt
B " (max ",$00
MOV AX,#$2D8B ; -(runtime size+15)/16
MOV CL,#$04
SHR AX,CL
NEG AX
ADD AX,#$1000 ; + 64K
CALL kpara ; write paragraphs
CALL printatt
B ")",$0D,$0A,"minimum ",$C4,"ata segment size: ",$00
CALL knrmvid
MOV AX,mindssz ; min DS size
CALL whexword ; write hex
CALL printatt
B " (max ",$00
MOV AX,#$024F ; -(min vars used+15)/16
MOV CL,#$04
SHR AX,CL
NEG AX
ADD AX,#$1000 ; + 64K
CALL kpara ; write paragraphs
CALL printatt
B ")",$0D,$0A,"m",$C9,"nimum free dynamic memory: ",$00
CALL knrmvid
MOV AX,minhpsz ; min free heap
CALL whexword ; write hex
CALL klowvid
CALL kparastr ; write paragraphs
CALL printatt
B $0D,$0A,"m",$C1,"ximum free dynamic memory: ",$00
CALL knrmvid
MOV AX,maxhpsz ; max free heap
CALL whexword ; write hex
CALL klowvid
CALL kparastr ; write paragraphs
JMP.b optmget
optmparmCALL printatt
B "command line ",$D0,"arameters: ",$00
CALL knrmvid
MOV SI,#parmlin ; display command line
CALL kwstrsi
optmget CALL printatt
B $0D,$0A,$0A,$0A,$C6,"ind run-time error ",$D1,"uit",$0D
B $0A,$0A,$BE,$00
CALL knrmvid
CALL keyget ; get command
CALL upcase ; convert to upper case
PUSH AX ; save it
CALL xwriteln ; WriteLn
POP AX ; restore cmd
MOV SI,#ocmdtab ; ptr to command table
CALL srchcmd ; search command
JNB optexe ; :ok
JMP optmenu2 ; 'not found - repeat
optexe CS:
JMP [SI]$01 ; "execute command
yornln CALL yorn ; Y or N ?
PUSHF ; save result
PUSH AX
CALL xwriteln ; WriteLn
POP AX ; restore result
POPF
RET ; "
yorn CALL prints ; Y or N ?
B " (Y/N)? ",$00
yornlp CALL keyget ; get char
CALL upcase ; convert to upper
CMP AL,#$59 ; Y ?
JZ yornok ; :yes
CMP AL,#$4E ; N ?
JNZ yornlp ; no: loop back
yornok PUSH AX ; save key
CALL conput ; display choice
POP AX ; restore it
SUB AL,#$4E ; set flags:
RET ; "0=no
waitesc CALL prints ; Wait for ESC
B ". Press <ESC>",$00
waitesclCALL keyget ; get char
CMP AL,#$1B ; ESC ?
JNZ waitescl ; :no, wait
RET ; "
srchcmd CS: ; Search command in table
CMP.B AL,[SI] ; compare it
JZ srcmdfnd ; :found
ADD SI,#$03 ; go to next entry
CS:
CMP.B [SI],#$00 ; end of table ?
JNZ srchcmd ; :no
STC ; not found !
srcmdfndRET ; "
kcmdtab B "L" ; main command table
W klogged
B "A"
W kactdir
B "W"
W kwork
B "M"
W kmain
B "E"
W editor
B "C"
W kcomp
B "R"
W krun
B "S"
W ksave
B "D"
W kdir
B "O"
W optmenu
B "Q"
W kquit
B $00 ; "end of table
B "M" ; option command table
W kmem
B "C"
W kcom
B "H"
W kchn
B "O"
W kcs
B "D"
W kds
B "I"
W kss
B "A"
W kmss
B "P"
W kparm
B "F"
W kfind
B "Q"
W optquit
B $00 ; "end of table
optquit POP AX ; Quit submenu
JMP kmenu ; "jump to main menu
; pathname of message file (installed by TINST)
errpath B "TURBO.MSG",$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
B $00,$00,$00,$00,$00,$00, ; "
extpas B $04,".PAS" ; table of file extensions
extcom B $04,".COM"
extbak B $04,".BAK"
extchn B $04,".CHN" ; "
fnpath B $04,"\*.*" ; search parameters
fnany B $03,"*.*" ; "
kmain CALL prints ; M:Main file name
B $0D,$0A,"Main file name",$00
CALL knocode ; flag: text not compiled
MOV SI,#mainpn ; pointer to pathname
CALL kreaddef ; Enter new name
MOV.B mainpn,#$00 ; mark end
JZ kmret ; :nothing read
CALL kpasext ; parse filename
MOV DI,#mainpn ; dest buffer
CALL fnscdi ; copy to main file name
kmret RET ; "
; W:Work file name
kwork MOV vnewfil,#flnew ; set error vec: new file
CALL ksvchng ; save, if changed
CALL prints
B $0D,$0A,"Work file name",$00
MOV SI,#workpn ; ptr work file name
CALL kreaddef ; Enter new name
MOV.B workpn,#$00 ; mark end
JNZ kwok ; :something read
CALL kclrtxt ; clear text
JMP kmainlp ; 'back to main menu
kwok CALL kpasext ; parse filename
MOV DI,#workpn ; copy to work file name
CALL fnscdi
JMP.b kw2 ; 'now load it
kwload MOV vnewfil,#flescclr ; set error vec: not found
kw2 MOV vfilbig,#flbigclr ; set error vec: too big
CALL kclrtxt ; clear text
MOV SI,#workpn ; work file name
kwload2 MOV BX,txbeg ; beg of text
PUSH BX ; save parms
PUSH SI
CALL clred ; clear editor vars
POP SI
POP BX ; restore
MOV DX,BX ; begin addr
MOV CX,txmemend ; end of space
CALL kload ; load file
INC BX
MOV txend,BX ; set end of text
RET ; "
kload PUSH DX ; Load file: save start pos
PUSH CX ; save end pos
CALL fnsisc ; copy pathname[SI]
CALL prints
B $0D,$0A,"Loading ",$00
CALL kwscrpn ; write pathname
MOV AX,#$3D00 ; open file
MOV DX,#scrpn ; current pathname
PUSH DS ; DS -> ES
POP ES
CALL dos ; do it
MOV BX,AX ; file handle
POP CX ; restore parms
POP DX
JB flntfnd ; :error
kload2 SUB CX,DX ; calc max length
MOV AH,#$3F ; read byte block
CALL dos ; do it
JB flntfnd ; :error
CMP AX,CX ; length = expected ?
JZ fltoobig ; yes: too big !
XCHG AX,CX ; get length done
MOV DI,DX ; end addr
PUSH DS ; DS -> ES
POP ES
MOV AL,#$1A ; search ^Z in the block
CLD
REPNZ
SCAS.B
JNZ klnoeof ; :not found
DEC DI ; go back one byte
klnoeof MOV AH,#$3E ; close file
CALL dos
DEC DI
MOV BX,DI ; end pos of text read
RET ; "
fltoobigCALL klnoeof ; file too large: close file
MOV BX,DX ; end of text space
INC BX ; +1
JMP [vfilbig] ; "handle error
flntfnd MOV BX,DX ; file not found
DEC BX ; end addr
JMP [vnewfil] ; "handle error
flesc CALL xwriteln ; File not found - ESC
CALL vidattr3 ; attribute #3
CALL prints
B "File not found",$00
flesc2 CALL waitesc ; wait for ESC
JMP knrmvid ; "HighVideo
flescclrCALL flesc ; file not found - ESC
JMP.b flclr ; "clear work file name
flnew CMP AL,#$03 ; New file: test error code
JZ fldir ; :invalid dir
CALL xwriteln ; WriteLn
CALL vidattr2 ; attribute #2
CALL prints
B "New File",$00
CALL knrmvid
PUSH BX ; save end pos
MOV BX,#$07D0 ; wait 2 s
CALL delaybx
POP BX ; restore
RET ; '
fldir CALL xwriteln ; WriteLn
CALL vidattr3 ; Attribute #3
CALL prints
B "Invalid directory",$00
JMP.b flclr ; "clear work file name
flbig CALL xwriteln ; File too big
CALL vidattr3
CALL prints
B "File too big",$00
MOV BX,txend ; end of text-1 -> BX
DEC BX
JMP flesc2 ; "wait for ESC
flbigclrCALL flbig ; file too big
flclr MOV.B workpn,#$00 ; clear work file name
JMP kmainlp ; "main loop
ksvchng XOR AX,AX ; Save if modified
INC AX ; set flag
JMP.b ksv2 ; '
ksvnoaskXOR AX,AX
ksv2 CMP.B txchg,#$00 ; text changed ?
JZ ksvret ; :no - ret
OR AX,AX ; test flag
JZ ksave
CALL prints
B "Workfile ",$00
CALL kwworkpn ; write path name
CALL prints
B " not saved. Save",$00
MOV.B txchg,#$00 ; clear change flag
CALL yornln ; Y or N ?
JNZ ksave ; :yes
ksvret RET ; '
ksave CALL kgetfn ; S:Save file - get filename
MOV.B txchg,#$00 ; clear change flag
MOV SI,#workpn ; ptr work file name
CALL fnsisc ; copy it
CALL prints
B $0D,$0A,"Saving ",$00
CALL kwworkpn ; write path name
MOV DI,#pnbuf
CALL fnscdi ; copy filename into buffer
MOV SI,#extbak ; extension .BAK
CALL kext ; modify file name
MOV AH,#$41 ; delete old .BAK file
MOV DX,#scrpn ; scratch file name
CALL dos ; do it
MOV AH,#$56 ; rename file
MOV DX,#pnbuf ; current filename
MOV DI,#scrpn ; new filename
CALL dos ; do it: rename to .BAK
MOV AH,#$3C ; create new file
XOR CX,CX ; no attribute
MOV DX,#pnbuf ; current filename
PUSH DS ; DS -> ES
POP ES
CALL dos ; do create
JB ksren ; :error
XCHG AX,BX ; file handle
MOV DX,txbeg ; beg -> offset
MOV CX,txend ; end of text
MOV DI,CX
MOV.B [DI],#$1A ; store ^Z at the end
SUB CX,DX ; calc length
INC CX ; +1
MOV AH,#$40 ; write byte block
CALL dos ; do it
JB kserr ; :error
CMP AX,CX ; length = expected ?
JNZ kserr ; :no
JMP klnoeof ; 'close that file
kserr MOV.B txchg,#$FF ; set changed flag
CALL klnoeof ; close that file
MOV AH,#$41 ; erase new file
MOV DX,#pnbuf
CALL dos
ksren MOV AH,#$56 ; rename it back to .PAS
MOV DX,#scrpn ; .BAK
MOV DI,#pnbuf ; -> .PAS
CALL dos ; do it
CALL xwriteln ; WriteLn
CALL ksfull ; write message
JMP kmainlp ; "return to main menu
ksfull CALL vidattr3 ; File not saved
CALL prints
B "Disk full or invalid directory path",$00
CALL waitesc ; wait for ESC
JMP knrmvid ; "
kdir CALL prints ; D:Directory
B "Dir mask",$00
CALL kreadln ; enter dir mask
MOV CX,#$000D ; *, ? allowed
CALL kparse2 ; parse filename
CALL krdend ; line end ?
MOV AH,#$1A ; set DMA transfer addr
MOV DX,#pnbuf ; buffer
CALL dos
MOV SI,#scrpn ; test pathname
kdtest MOV.B AL,[SI] ; get char
INC SI ; go to next
CMP AL,#$2A ; * ?
JZ kddoit ; :yes
CMP AL,#$3F ; ?
JZ kddoit ; :yes
OR.B AL,AL ; end of string ?
JNZ kdtest ; :no
MOV.B AL,[SI]-$02 ; get last char
MOV SI,#fnany ; ptr to *.*
CMP AL,#$3A ; : ?
JZ kdwrit ; :yes
CMP AL,#$5C ; \ ?
JZ kdwrit ; :yes
MOV AH,#$4E ; search first dir entry
MOV CX,#$0010 ; attribute
MOV DX,#scrpn ; pathname
CALL dos ; do it
JB kddoit ; :error
TEST.B pndiratt,#$10 ; attribute ok ?
JZ kddoit ; yes, take it
MOV SI,#fnpath ; ptr to \*.*
kdwrit CALL kext2
kddoit CALL prints
B "Directory of ",$00
CALL kwscrpn ; write path name
CALL xwriteln ; WriteLn
MOV AH,#$4E ; get first entry
MOV CX,#$0010 ; attribute
MOV DX,#scrpn ; path name
CALL dos ; do it
JB kderr ; :error
XOR.B DL,DL ; clear flag
kddis OR.B DL,DL ; test flag
JNZ kdnocr ; :not first entry in line
CS:
MOV AL,txwinx2 ; line length / 16
XOR.B AH,AH
MOV DL,#$10
DIV.B DL
MOV.B DL,AL
kdnocr MOV SI,#pndirnm ; ptr to filename
TEST.B [SI]-$09,#$10 ; test attribute
JZ kdnodir ; :ok
CMP.B [SI],#$2E ; directory ?
JZ kdnext
CALL klowvid ; LowVideo
kdnodir CALL printpn ; write name
CALL knrmvid ; NormVideo
DEC.B DL ; another one in this line ?
JZ kdnextln ; :no
MOV CX,#pndirpad ; end for padding
SUB CX,SI ; calc length
kdpad MOV AL,#$20 ; pad with spaces
CALL conput ; write space
LOOP kdpad ; :another one
JMP.b kdnext ; 'next dir entry
kdnextlnCALL xwriteln ; WriteLn
kdnext MOV AH,#$4F ; get next entry
PUSH DX ; save tab
CALL dos ; get it
POP DX ; restore tab
JNB kddis ; :ok, write it
OR.B DL,DL ; (redundant !)
JMP.b kdend ; 'end it
kderr CALL xwriteln ; WriteLn
CALL vidattr2
CALL prints
B "No files",$00
CALL knrmvid
MOV DL,#$01 ; do CR
kdend OR.B DL,DL ; test tab flag
JZ kdend2 ; :no CR
CALL xwriteln ; WriteLn
kdend2 CALL xwriteln ; WriteLn
MOV.B DL,scrpn ; get drive number
SUB.B DL,#$40 ; convert
MOV AH,#$36 ; get disk free space
CALL dos
CMP AX,#$FFFF ; invalid drive ?
JZ kdret ; :yes
MUL CX ; calc bytes/block
OR DX,DX ; many ?
JNZ kdret ; :yes
MUL BX ; * number free blocks
MOV CL,#$0A ; DIV 1024
SHR AX,CL
MOV CL,#$06
SHL DX,CL
ADD AX,DX
CALL knum1 ; write number
CALL prints
B "k bytes free",$00
kdret RET ; "
klogged CALL prints ; L:Logged drive
B "New drive",$00
CALL kreadln ; Read line
MOV.B AL,[BX] ; get char from buffer
CMP AL,#$1A ; EOF ?
JNZ kldef ; :no
MOV AH,#$19 ; get default drive
CALL dos
JMP.b kldef2 ; 'ok
kldef CALL upcase ; convert it
SUB AL,#$41 ; < A ?
JB klret ; :wrong
CMP AL,#$0F ; > O ?
JA klret ; :wrong
kldef2 PUSH AX ; save it
MOV AH,#$0D ; reset disk system
CALL dos
POP DX ; restore drive
MOV AH,#$0E ; set default drive
CALL dos
klret RET ; "
kactdir CALL prints ; A:Active directory
B "New directory",$00
CALL kreadln ; read string
JZ karet ; nothing: ret
MOV SI,BX ; source
MOV DX,#pnbuf ; dest: std pathname
MOV DI,DX
MOV CX,#$0040 ; 64 bytes
PUSH DS ; DS -> ES
POP ES
CLD ; copy it
REPZ
MOVS.B
MOV DI,DX ; restore ptr to beg
MOV AL,#$1A ; search ^Z
MOV CX,#$0040
REPNZ
SCAS.B
JNZ kaerr ; not found: invalid
MOV.B [DI]-$01,#$00 ; mark end with 0
MOV AH,#$3B ; change directory
CALL dos
OR.B AL,AL ; error ?
JZ karet ; :no
kaerr MOV AL,#$07 ; Bell
CALL conput ; write it
karet RET ; "
kspace CALL prints ; Display space information
B "Code: ",$00
MOV BX,#$2D8B ; (code size-runtime length)/16
MOV CL,#$04
SHR BX,CL
MOV AX,codesize ; (already in paras)
SUB AX,BX
AND AX,#$0FFF
CALL ksize ; write number
CALL prints
B ", ",$00
MOV AX,#$1000 ; (64K-code size)/16
SUB AX,codesize
AND AX,#$0FFF
CALL kpara ; write para
CALL prints
B " free",$0D,$0A,"Data: ",$00
MOV BX,#$024F ; (Data size-std vars)/16
MOV CL,#$04
SHR BX,CL
MOV AX,datasize ; (already paras)
SUB AX,BX
AND AX,#$0FFF
CALL ksize ; write number
CALL prints
B ", ",$00
MOV AX,#$1000 ; (64K-data size)/16
SUB AX,datasize
AND AX,#$0FFF
CALL kpara ; write para
CALL prints
B " free",$0D,$0A,$00
CMP.B codedest,#$01 ; code destination ?
JZ kspmem ; :memory
CMP.B codedest,#$02
JZ kspcom ; :COM-File
RET ; '
kspmem CALL prints
B "Stack/Heap: ",$00
MOV AX,minstksz ; display min stack size
CALL ksize ; write number
JMP xwriteln ; 'WriteLn
kspcom CALL prints
B "Stack/Heap: ",$00
MOV AX,minhpsz ; min heap/stack size
CALL ksize ; write number
CALL prints
B " (minimum)",$0D,$0A," ",$00
MOV AX,maxhpsz ; max heap/stack size
CALL ksize ; write number
CALL prints
B " (maximum)",$0D,$0A,$00
RET ; "
ksize PUSH AX ; write number hex and dec
CALL kpara ; write para
CALL prints
B " (",$00
POP AX ; restore number
XOR DX,DX ; clear hi
MOV CX,#$0004
ksize2 SHL AX,1 ; * 16
RCL DX,1
LOOP ksize2 ; :another shift
MOV BX,AX ; number
MOV CX,#$0006 ; field length
CALL knum ; write DX:BX
CALL prints
B " bytes)",$00
RET ; "
kpara CALL whexword ; Write para: hex number
kparastrCALL prints
B " paragraphs",$00
RET ; "
CALL kpara ; WriteLn para
JMP xwriteln ; "
krdhex XOR AX,AX ; Read hex
krxlp PUSH AX ; save number
MOV.B AL,[BX] ; get char
CALL upcase ; UpCase
MOV.B CH,AL ; -> CH
POP AX ; restore number
SUB.B CH,#$30 ; ASCII-translation
JB krxend ; :< 0 - end
CMP.B CH,#$0A ; > 9 ?
JB krxok ; no: ok
SUB.B CH,#$07 ; A..F
CMP.B CH,#$0A ; < A ?
JB krxend ; yes: end
CMP.B CH,#$10 ; > F ?
JNB krxend ; yes: end
krxok MOV CL,#$04 ; number * 16
SHL AX,CL
OR.B AL,CH ; add digit
INC BX ; next char
JMP krxlp ; 'continue
krxend RET ; "
kfind CALL prints ; F:Find Error
B "Enter PC",$00
CALL kreadln ; ReadLn
JNZ kfind2 ; :ok
RET ; 'nothing entered
kfind2 CALL krdhex ; read hex
MOV errpos2,AX ; store error position
CALL knocode ; set flag: not compiled
kdofind CALL kgetfile ; get file, if necessary
MOV.B cpmode,#$01 ; set flag: searching
CALL prints
B $0D,$0A,"Searching",$00
JMP.b kcnofile ; "do it...
kcomp CALL kgetfile ; C:Compile - get file
CALL kdestfil ; set dest file
CALL prints
B $0D,$0A,"Compiling",$00
CMP.B cpmode,#$00 ; to memory ?
JZ kcnofile ; :yes
CALL prints
B " --> ",$00
MOV SI,#scrpn ; display dest path name
CALL printpn
kcnofileCALL xwriteln ; WriteLn
CALL turbo ; Call compiler
CMP.B cpmode,#$02 ; COM/CHN produced ?
JB kcnoerr ; :no
CMP.B cperr,#$00 ; error ?
JZ kcnoerr ; :no
CALL kdestfil ; set dest file
MOV AH,#$41 ; delete it
MOV DX,#destpn
CALL dos
kcnoerr CMP.B cperr,#$CA ; Compilation aborted ?
JNZ kcnoabrt ; :no
CALL xwriteln ; WriteLn
CALL xwriteln ; WriteLn
CALL vidattr2
CALL prints
B "Compilation aborted",$00
JMP kmainlp ; 'return to main menu
kcnoabrtCALL prints
B " lines",$0D,$0A,$0A,$00
CMP.B cperr,#$00 ; error ?
JNZ kctest ; :yes
CMP.B cpmode,#$01 ; find error pos ?
JZ kcnotfnd ; :yes
CALL kspace ; display space info
RET ; '
kcnotfndCALL vidattr3 ; error pos not found
CALL prints
B "Run-time error position not found",$00
CALL knrmvid
JMP.b kwlnret ; 'return to main
kctest CMP.B cperr,#$C9 ; I/O-error ?
JNZ kcfound ; :no
CALL ksfull ; write message
kwlnret CALL xwriteln ; WriteLn
JMP kmainlp ; 'return to main menu
kcfound CMP.B cperr,#$C8 ; Error pos found ?
JNZ kcerror ; :no
CALL prints
B "Run-time error position found",$00
JMP kcwend ; 'jump to this position
kcerror CALL vidattr3 ; Error message
CALL prints
B "Error ",$00
XOR AX,AX ; get error number
MOV AL,cperr
CALL knum1 ; write it
TEST.B msgflg,#$FF ; error msgs included ?
JZ kcwend ; :no
MOV BX,#txstrt ; pointer to messages
kcmsr MOV.B AL,[BX] ; get char
CMP AL,#$1A ; ^Z ?
JZ kcwend ; yes: not found
CMP AL,#$20 ; control char ?
JB kcmsr2 ; :yes
SUB AL,#$30 ; get digit
MOV.B AH,AL ; * 10
ADD.B AL,AL
ADD.B AL,AL
ADD.B AL,AH
ADD.B AL,AL
INC BX
ADD.B AL,[BX] ; get second digit
SUB AL,#$30 ; convert
INC BX ; next char
CMP.B AL,cperr ; = error number ?
JZ kcwmsg ; :yes
kcmsr2 MOV.B AL,[BX] ; get char
INC BX ; next one
CMP AL,#$0D ; end of line ?
JNZ kcmsr2 ; no: search it
INC BX ; next char: skip LF
JMP kcmsr ; 'continue searching
kcwmsg CALL prints ; Display error message
B ": ",$00
kcwlp MOV.B AL,[BX] ; get char
CMP AL,#$0D ; CR ?
JZ kcwend ; yes: done
CMP AL,#$20 ; control char ?
JNB kcwchar ; :no - write it
CMP AL,#$1A ; ^Z ?
JZ kcwend ; yes: done
MOV SI,#txstrt ; pointer to messages
kcsubsr MOV.B AL,[SI] ; get char
INC SI ; go to next
CMP AL,#$20 ; control char ?
JNB kcwsub ; no: search end of line
CMP.B AL,[BX] ; is it the right one ?
JNZ kcwsub ; :no, next line
kcsubsr2MOV.B AL,[SI] ; get char - write sub-message
CMP AL,#$0D ; CR ?
JZ kcwnext ; :end
CALL conput ; write it
INC SI ; next one
JMP kcsubsr2 ; 'continue
kcwsub MOV.B AL,[SI] ; search end of line
INC SI ; next char
CMP AL,#$0D ; CR ?
JNZ kcwsub ; :no
INC SI ; skip LF
JMP kcsubsr ; 'continue searching
kcwchar CALL conput ; display char
kcwnext INC BX ; go to next
JMP kcwlp ; 'continue
kcwend MOV.B mainflg,#$00 ; work file used
CMP.B inclflg,#$00 ; error in include file ?
JZ kcnoincl ; :no
MOV AL,#$2E
CALL conput ; write char
CALL knrmvid
CALL ksvnoask
MOV SI,#inclpn ; copy include file name
MOV DI,#workpn ; into work file name
CALL fncopy ; do it
CALL kwload ; load that file
CALL xwriteln ; WriteLn
CALL vidattr3
CALL prints
B "Error found in above include file",$00
JMP.b kcwait ; 'go into editor
kcnoinclCMP.B mainpn,#$00 ; main file exists ?
JZ kcwait ; :no
MOV SI,#mainpn ; copy main into
MOV DI,#workpn ; work file name
CALL fncopy ; do it
kcwait CALL waitesc ; wait for ESC
MOV BX,txerrpos ; get error pos in text
DEC BX
JMP editor2 ; "jump into editor
kmem MOV.B codedest,#$01 ; M:memory
RET ; "set flag
kcom MOV.B codedest,#$02 ; C:COM file
RET ; "set flag
kchn MOV.B codedest,#$03 ; H:CHN file
RET ; "set flag
kcs CALL prints ; O:CS size
B "Minimum Code Segment size",$00
CALL kreadln ; read it
MOV AX,#$0000 ; default
JZ kcsdef ; :nothing entered
CALL krdhex ; read hex
MOV BX,#$2D8B ; add runtime size
MOV CL,#$04 ; convert to paras
SHR BX,CL
ADD AX,BX
kcsdef MOV mincssz,AX ; store min CS
RET ; "
kds CALL prints ; D:DS size
B "Minimum Data Segment size",$00
CALL kreadln ; read it
MOV AX,#$0000 ; default
JZ kdsdef ; nothing entered: set it
CALL krdhex ; read hex
MOV BX,#$024F ; add std vars size
MOV CL,#$04
SHR BX,CL ; convert to paras
ADD AX,BX
kdsdef MOV mindssz,AX ; store min DS
RET ; "
kss CALL prints ; S:SS size
B "Minimum Stack Segment size",$00
CALL kreadln ; read it
MOV AX,#$0400 ; default: 16K
JZ kssdef ; nothing entered: set it
CALL krdhex ; read hex
kssdef MOV minhpsz,AX ; store min SS
RET ; "
kmss CALL prints ; A:Max. SS size
B "Maximum Stack Segment size",$00
CALL kreadln ; read it
MOV AX,#$A000 ; default: 640K
JZ kmssdef ; nothing entered: set it
CALL krdhex ; read hex
kmssdef MOV maxhpsz,AX ; store max SS
RET ; "
kparm CALL prints ; P:Parameter line
B "Parameters",$00
MOV SI,#parmlin ; ptr to buffer
CALL kreaddef ; Read line with default
MOV DI,#parmlin ; copy it
MOV CX,#$003B ; count
kparml MOV.B AL,[BX] ; get char
CMP AL,#$1A ; ^Z ?
JZ kparme ; yes: end
MOV.B [DI],AL ; store in buffer
INC BX ; next char
INC DI
LOOP kparml ; :another char
kparme MOV.B [DI],#$00 ; mark end of param line
RET ; "
kgetfileCMP.B workpn,#$00 ; load file, if necessary
JNZ kget1 ; :work file defined
CALL kgetfn ; get work file name
kget1 CMP.B mainpn,#$00 ; main file ?
kget2 MOV SI,#workpn
JNZ kget3 ; :work file exists
CALL kgetfn ; copy work -> main file name
MOV AL,#$00 ; use work file
JMP.b kget4 ; '
kget3 CALL kcmpfn ; work = main ?
JZ kget2 ; :yes
CALL ksvnoask ; save without question
MOV vfilbig,#flbigclr ; set error vectors
MOV vnewfil,#flescclr
MOV SI,#mainpn ; main file
CALL kwload2 ; load it
MOV AL,#$FF ; set flag: main file
kget4 MOV mainflg,AL ; store flag
MOV.B errio,#$00 ; clear error flag
RET ; "
kdestfilMOV SI,#mainpn ; Set dest file name
CMP.B [SI],#$00 ; main file name ok ?
JNZ kdsmain ; not null - ok
MOV SI,#workpn ; use work file name
kdsmain CALL fnsisc ; copy to scratch file name
MOV SI,#extcom ; set extension .COM
MOV AL,codedest
MOV.B cpmode,#$00 ; set flag: memory
DEC.B AL ; memory ?
JZ kdsmem ; :yes
MOV.B cpmode,#$02 ; flag: file
DEC.B AL ; COM ?
JZ kdsmem ; :yes
MOV.B cpmode,#$03 ; flag: CHN
MOV SI,#extchn ; set extension .CHN
kdsmem CALL kext ; modify file name
MOV DI,#destpn ; -> dest file name
CALL fnscdi ; copy it
RET ; "
krun CMP.B txcomp,#$00 ; R:Run
JNZ krok ; :already compiled
CALL kcomp ; compile it
krok CMP.B codedest,#$01 ; memory: ok
JZ krmem ; yes: do it
CMP.B codedest,#$02 ; COM-file ?
JNZ krchn
CALL xwriteln ; WriteLn
CALL vidattr2
CALL prints
B "Please Quit TURBO to run .COM file",$00
krchn JMP kmainlp ; 'return to menu
krmem MOV SI,#parmlin ; copy command line
MOV DI,#paramlin ; to start of CS
MOV ES,destseg ; seg of compiled program
XOR BX,BX ; clear offset
krparml MOV.B AL,[SI] ; get char
CMP AL,#$00 ; end ?
JZ krparme ; :yes
INC SI ; next char
INC BX
ES:
MOV.B [BX_DI],AL ; store that char
JMP krparml ; 'continue
krparme ES:
MOV.B [BX_DI]$01,#$0D ; store CR at the end
ES:
MOV.B [DI],BL ; store length
CALL prints
B $0D,$0A,"Running",$0D,$0A,$00
PUSH destseg ; CS of program
MOV AX,#$0100 ; start of program
PUSH AX
RETF ; 'start program
turboretES: ; return point from program
MOV AL,errio ; error ?
MOV errio,AL
ES:
MOV AX,errpos2 ; get error position
MOV errpos2,AX
MOV AX,#$FFFF ; flag
JMP kmeminit ; "reinit
kclrtxt MOV BX,txbeg ; Clear text
MOV txend,BX ; text beg -> text end
MOV.B txchg,#$00 ; text not changed
MOV.B mainflg,#$00 ; work file
knocode MOV.B txcomp,#$00 ; text not compiled
RET ; "
kquit CALL ksvchng ; Q:Quit - save changes
CALL xcrtexit ; CrtExit
MOV AH,#$4C ; end of process
JMP dos ; "return to MS-DOS
kgetfn CMP.B workpn,#$00 ; Get current file name
JNZ kgf2 ; :work file
CMP.B mainpn,#$00 ; main file ?
JNZ kgfmain ; :yes
CALL kwork ; set work file name
JMP.b kgf2 ; '
kgfmain MOV SI,#mainpn ; main -> work file name
MOV DI,#workpn
CALL fncopy
MOV.B mainflg,#$FF ; flag: main file
CALL kgetfn ; get filename
kgf2 CMP.B mainflg,#$00 ; main file ?
JZ kgfret ; :no
CALL ksvnoask ; save without question
CALL kwload ; load that file
kgfret RET ; "
kcmpfn MOV SI,#workpn ; main = work file ?
MOV DI,#mainpn
kcmpl MOV.B AL,[SI] ; get char
CMP.B AL,[DI] ; compare it
JNZ kcmpnoeq ; :not equal
OR.B AL,AL ; end ?
JZ kcmpret ; :yes
INC SI ; next char
INC DI
JMP kcmpl ; 'continue
kcmpnoeqMOV AL,#$01 ; set flag: false
kcmpret RET ; "
kreaderrMOV AX,#txstrt ; Read error message file
MOV txbeg,AX ; set text beg
MOV vnewfil,#flesc ; set error vectors
MOV vfilbig,#flbig
MOV SI,#errpath ; ptr to pathname
MOV DI,#scrpn ; scratch pathname
PUSH DI ; save ptr
PUSH DS ; DS -> ES
POP ES
PUSH CS ; CS -> DS
POP DS
CALL fncopy1 ; copy filename
PUSH ES ; ES -> DS
POP DS
POP SI ; restore ptr
CALL kwload2 ; load file
MOV BX,txend ; store its end position
MOV.B [BX],#$1A ; store ^Z at the end
INC BX ; next char
MOV txbeg,BX ; -> new text beg
RET ; "
kwstrsi PUSH BX ; write string [SI]
MOV BX,#prpnret ; no change
JMP.b prpn2 ; '
printpn PUSH BX ; write pathname: save BX
MOV BX,#upcase ; vector: UpCase
prpn2 CLD
LODS.B ; get char
OR.B AL,AL ; end ?
JZ prpne ; :no
CALL BX ; modify char
CALL conput ; write it
JMP prpn2 ; 'continue
prpne POP BX ; restore
prpnret RET ; "
knum1 XOR CX,CX ; write number - unformatted
CALL knum2 ; do it
RET ; "
knumax MOV CX,#$0005 ; write number - 5 digits
XOR DX,DX ; clear hi word
JMP.b knum2 ; '
knum2 XOR DX,DX ; write number DX:BX
knum PUSH SI ; save regs
PUSH DI
PUSH ES
PUSH DS ; DS -> ES
POP ES
PUSH CS ; CS -> DS
POP DS
MOV DI,#knumbuf ; dest buffer
MOV BX,AX ; number to write
PUSH CX ; save field size
CLD
XOR CX,CX ; format flag (leading zeroes)
MOV SI,#kndectab ; ptr decimal table
MOV AH,#$07 ; 7 digits
kndig XOR.B AL,AL ; clear digit
kndiglp INC.B AL ; count up
SUB BX,[SI] ; do successive subtraction
SBB DX,[SI]$02
JNB kndiglp ; :continue
ADD BX,[SI] ; restore number
ADC DX,[SI]$02
ADD SI,#$04 ; next table entry
DEC.B AL ; restore digit
JNZ knnot0 ; :not zero
OR CX,CX ; test flag
JZ knnoput
knnot0 ADD AL,#$30 ; convert to ASCII
STOS.B ; store in dest buffer
INC CX ; set flag
knnoput DEC.B AH ; another digit ?
JNZ kndig ; :yes
OR CX,CX ; digits done ?
JNZ knnot00 ; :yes
MOV AL,#$30 ; put a 0
STOS.B
INC CX ; digit count
knnot00 XOR AX,AX ; write a zero at the end
STOS.B
POP AX ; restore field size
PUSH ES ; ES -> DS
POP DS
SUB AX,CX ; fill up with spaces ?
JBE knnopad ; :no
INC CX ; space count
DEC DI
MOV SI,DI ; make space in buffer
ADD DI,AX
STD
REPZ ; move it up
MOVS.B
MOV CX,AX ; now fill with spaces
MOV AL,#$20
REPZ
STOS.B
knnopad MOV SI,#knumbuf ; buffer ptr
CALL kwstrsi ; write string
POP ES ; restore regs
POP DI
POP SI
RET ; "
kndectabW $4240,$000F ; 1000000 decimal table
W $86A0,$0001 ; 100000
W $2710,$0000 ; 10000
W $03E8,$0000 ; 1000
W $0064,$0000 ; 100
W $000A,$0000 ; 10
W $0001,$0000 ; " 1
kparse MOV CX,#$000F ; parse filename: no wildcards allowed
kparse2 MOV DI,#scrpn ; dest: scratch file name
MOV AX,[BX] ; get 2 chars: drive spec
CALL upcase ; UpCase
CMP AL,#$20 ; control char ?
JB kpdefdrv ; :yes
CMP.B AH,#$3A ; : ?
JNZ kpdefdrv ; :no
INC BX ; next char
INC BX
JMP.b kpdrv ; 'set drive
kpdefdrvMOV AH,#$19 ; get default drive
CALL dos
ADD AL,#$41 ; -> ASCII
MOV AH,#$3A
kpdrv MOV [DI],AX ; store drive spec
INC DI
INC DI
OR.B CH,CH ; test path ?
JNZ kpendpn2 ; :no
CMP.B [BX],#$5C ; \ ?
JZ kpendpn2 ; :yes
MOV.B [DI],#$5C ; store \
INC DI
MOV AH,#$47 ; get current path
MOV.B DL,AL ; drive number
SUB.B DL,#$40
MOV SI,DI ; destination
CALL dos ; do it
JB kpendpn2 ; :error
CMP.B [DI],#$00 ; search end of path
JZ kpendpn2 ; :found
kpendsr MOV.B AL,[DI] ; get char
OR.B AL,AL ; end ?
JZ kpendpn ; :found
INC DI ; next char
CMP DI,#scrpnend ; end of buffer ?
JB kpendsr ; :not yet
JMP.b kpend ; 'end it
kpendpn MOV.B [DI],#$5C ; store \
INC DI ; next char
CMP DI,#scrpnend ; end of buffer ?
; :yes
JZ kpend
kpendpn2XOR.B CH,CH ; clear flag
PUSH CS ; CS -> ES
POP ES
CLD
kpchklp MOV.B AL,[BX] ; get char
CMP AL,#$20 ; control / space ?
JBE kpend ; :yes
PUSH CX ; save cnt, dest ptr
PUSH DI
MOV DI,#kpinval ; test: illegal char ?
REPNZ ; CL is count for this search
SCAS.B ; search char in table
POP DI ; restore
POP CX
JZ kpend ; found: end it
INC BX ; next char
MOV.B [DI],AL ; store it
INC DI ; next char in dest
CMP DI,#scrpnend ; end of buffer ?
JB kpchklp ; :no
kpend MOV.B [DI],#$00 ; mark end
RET ; "
kpinval B $22,"+,/ ; <=>[]{|}*?" "table of illegal chars
kpasext MOV SI,#extpas ; pointer: .PAS
kextdef PUSH SI ; save extension ptr
CALL kparse ; parse filename
POP SI ; restore ext ptr
MOV DL,#$01 ; only set if no ext
JMP.b kx1 ; 'do it
kext2 MOV DL,#$02
JMP.b kx1 ; '
kext XOR.B DL,DL ; set file extension
kx1 MOV DI,#insline2 ; ptr scratch file name
XOR CX,CX ; clr dot pos
kxsrdot MOV.B AL,[DI] ; get char
OR.B AL,AL ; end ?
JZ kxend ; :yes
INC DI ; next one
CMP.B DL,#$02 ; search dot ?
JZ kxsrdot ; :no
CMP AL,#$2E
JNZ kxnodot ; :no
MOV CX,DI ; store pos of dot
kxnodot CMP AL,#$5C ; \ ?
JNZ kxsrdot ; :no
XOR CX,CX ; clear dot pos
JMP kxsrdot ; 'continue
kxend OR CX,CX ; test dot pos
JZ kxnodotf ; :not set
OR.B DL,DL ; set extension ?
JNZ kxret ; :no
MOV DI,CX ; dot position
DEC DI ; go back
kxnodotfCS: ; set extension
MOV.B CL,[SI] ; length of extension
XOR.B CH,CH ; -> count
JCXZ kxexte ; :nothing to do
kxextl CMP DI,#scrpnend ; buffer end ?
JZ kxexte ; :yes
INC SI ; next pos
CS:
MOV.B AL,[SI] ; get ext char
MOV.B [DI],AL ; store it
INC DI ; next one
LOOP kxextl ; :another char
kxexte MOV.B [DI],#$00 ; mark end
kxret RET ; "
fnscdi MOV SI,#scrpn ; copy scratch -> [DI]
JMP.b fncopy ; '
fnsisc MOV DI,#scrpn ; copy [SI] -> scratch
fncopy PUSH DS ; DS -> ES
POP ES
fncopy1 MOV CX,#$0020 ; copy 64 bytes
CLD
REPZ
MOVS ; do it
RET ; "
kwscrpn MOV SI,#scrpn ; write scratch file name
JMP printpn ; "
kwworkpnMOV SI,#workpn ; write work file name
JMP printpn ; "
kdworkfnMOV SI,#workpn ; Display work file name
PUSH DS ; DS -> ES
POP ES
CLD
LODS.B ; get char
CALL upcase ; UpCase
CALL conput ; write it (drive)
LODS.B ; get char
CALL conput ; write it (:)
MOV DI,SI ; search end of filename
XOR.B AL,AL
MOV CX,#$FFFF ; any length
CLD
REPNZ
SCAS.B ; search it
STD ; backward search !
MOV AL,#$5C ; now search \
MOV CX,#$FFFF ; any length
DEC DI
REPNZ
SCAS.B ; do it
MOV SI,DI ; position of file name
INC SI ; without dir path
INC SI
JMP printpn ; "write it
printattPUSH BP ; Print string with highlighting
MOV BP,SP ; string: inline, 0=end
XCHG BX,[BP]$02 ; get return addr
PUSH AX ; save
PUSHF
pralp CS: ; get char
MOV.B AL,[BX]
INC BX ; go to next
OR.B AL,AL ; end ?
JZ praend ; :yes
CMP AL,#$80 ; highlighted ?
JNB prahi ; :yes
CALL klowvid ; LowVideo
JMP.b pralo ; 'write it
prahi CALL knrmvid ; NormVideo
AND AL,#$7F ; mask out bit 7
JZ pralp ; zero: end
pralo CALL conput ; write it
JMP pralp ; 'next char
praend POPF ; restore
POP AX
XCHG BX,[BP]$02 ; restore ret
POP BP
RET ; "
knrmvid CMP.B curatt,#$00 ; NormVideo
JZ knrmret ; already set: ret
MOV.B curatt,#$00 ; set flag
JMP xnormvid ; 'do it
knrmret RET ; "
klowvid CMP.B curatt,#$01 ; LowVideo
JZ knrmret ; :already set
MOV.B curatt,#$01 ; set it
JMP xlowvid ; "
vidattr3CMP.B curatt,#$03 ; Attribute #3
JZ knrmret ; :already set
MOV.B curatt,#$03 ; set it
PUSH AX ; save
MOV AL,att3 ; set that attribute
MOV attcur,AL
POP AX ; restore
RET ; "
vidattr2CMP.B curatt,#$02 ; Attribute #2
JZ knrmret ; :already set
MOV.B curatt,#$02 ; set it
PUSH AX ; save
MOV AL,att2 ; set that attribute
MOV attcur,AL
POP AX ; restore
RET ; "
movebk MOV SI,BX ; Block transfer [BX]->[DX]
MOV DI,DX ; forward = delete
PUSH DS ; DS -> ES
POP ES
CLD
REPZ ; CX=count
MOVS.B ; do it
MOV BX,SI ; point to block end
MOV DX,DI
RET ; "
movebkb MOV SI,BX ; Block transfer [BX]->[DX]
MOV DI,DX ; backward = insert
PUSH DS ; DS -> ES
POP ES ; BX,DX point to block END !
STD
REPZ
MOVS.B ; do it
CLD ; reset dir flag
MOV BX,SI ; point to block beg
MOV DX,DI
RET ; "
inited XOR AX,AX ; Init editor vars
MOV editflg,AL
MOV DI,#srend ; 390..3B0:=0
MOV CX,#$0021
PUSH DS ; DS -> ES
POP ES
CLD ; fill
REPZ
STOS.B
INC AX ; 3B1..3B2:=1
MOV DI,#oldlen ; old len, redisplay from
MOV CX,#$0002
REPZ
STOS.B
INC AX ; Words 3BA..3BD:=2
MOV DI,#bkbegl ; block pos in buffer
MOV CX,#$0002
REPZ
STOS
MOV AX,#$FFFF ; 3B3..3B8:=FF
MOV DI,#overflg ; set flags:
MOV CX,#$0006 ; insert, indent, display
REPZ ; redisplay
STOS.B
MOV AX,#line ; Words 3BE..3C8:=046C
MOV DI,#edpos ; current pos, pos in line buffer
MOV CX,#$0006 ; pos FIFO = beg of line buffer
REPZ
STOS
MOV AL,#$00 ; 3CA..45F:=0
MOV DI,#cmdbuf ; command entry buffer, search,
MOV CX,#$0096 ; replace, option, filename
REPZ
STOS.B
MOV.B cmdbuf,#$03 ; command buf: 3 chars
MOV.B srword,#$1E ; search word: 30 chars
MOV.B srrepl,#$1E ; replce word: 30 chars
MOV.B stopt,#$0A ; option word: 10 chars
MOV.B fnbuf,#$40 ; file name: 64 chars
MOV.B fnbufend,#$1A ; end file name buffer
MOV lineend,#$0A0D ; end line input buffer
MOV AL,#$00 ; no flicker
MOV DX,#$B000 ; mono screen segment
CMP.B scrmod,#$07 ; mode = 7 ?
JZ inited1 ; yes: mono
CS:
MOV AL,scrflick ; get flicker flag
MOV DH,#$B8 ; set segment
inited1 MOV scrbad,AL ; store flicker flag
MOV scrseg,DX ; store segment
RET ; "
clred MOV bkbeg,BX ; Clear editor vars
MOV bkend,BX ; set to beg of text = BX
MOV edpos,BX
MOV posfifo,BX
MOV qppos,BX
MOV disbeg,BX
RET ; "
ekd CALL eflush ; flush changes
CS:
MOV.B DL,txwiny2 ; bottom-1
DEC.B DL
MOV DH,#$00
CALL setcpos ; set cursor pos
CALL knrmvid ; NormVideo
DEC txend ; end of text
MOV BX,txend
MOV.B [BX],#$1A ; store ^Z at the end
JMP kmainlp ; "return to main menu
editor CALL kgetfn ; E:Editor - get file name
MOV BX,#$FFFF ; position flag
editor2 PUSH BX ; save pos
MOV BX,txend ; end of text
MOV [BX],#$0A0D ; store CR,LF there
INC txend
MOV.B statobs,#$00 ; status line obsolete
MOV.B dislin,#$01 ; redisplay all
MOV sepptr,#eseptab ; pos of word separator tab
CALL knrmvid ; NormVideo
CALL xclrscr ; ClrScr
POP BX ; relative text pos
ADD BX,txbeg ; +text beg+1
INC BX
CALL efsetpos ; set new pos
edmain CALL eredispl ; redisplay
MOV.B CL,statera ; clear status line
OR.B CL,CL ; anything to erase ?
JZ ednoera ; :no
MOV BX,#$0000 ; cursor pos: home
CALL esetcur
CALL esetlow ; LowVideo
MOV AL,#$20 ; space
edera CALL ewritch ; write char
DEC.B CL ; another ?
JNZ edera ; :yes
MOV.B statera,CL ; clear erase flag
ednoera CALL estat ; display status line
CALL edproc ; get command
JNB edchg ; :no command - enter char
JZ edmain ; :not found
CMP.B DH,#$7F ; MSB vector = 1 ?
JB ednochg ; :no
MOV.B txchg,#$FF ; set flag: text changed
MOV.B txcomp,#$00 ; code invalid
AND.B DH,#$7F ; mask vector
ednochg MOV BX,#edmain ; store return addr
PUSH BX ; -> main loop
PUSH DX
MOV BX,#pfifosrc ; do position FIFO
MOV DX,#pfifodst
MOV CX,#$0008 ; 8 bytes
JMP movebkb ; 'move back, jump to command
eprefix CALL eddiscmd ; ^P:Control char prefix - display
CALL keyget ; get char
JMP.b edput ; 'enter this char
edchg MOV txchg,AL ; flag: text changed
MOV.B txcomp,#$00 ; code invalid
edput MOV BX,lnpos ; pos in line
CMP BX,#lineend0 ; line full ?
JNB edmain ; yes: ignore char
CMP.B overflg,#$00 ; overwrite ?
JZ edover ; :yes
PUSH AX ; save it
CALL einsch ; make space for char
POP AX ; restore it
edover MOV.B [BX],AL ; store char
INC BX ; cursor right
PUSH BX
CALL eredlin ; redisplay line
POP BX
MOV lnpos,BX ; new pos in line buffer
CALL erepos ; reposition in line
JMP edmain ; "main loop
edproc CALL keyget ; Get command: get key
CMP AL,#$7F ; Delete ?
JZ edpdel ; :yes
CMP AL,#$20 ; control char ?
JNB edpret ; no: no command
edpdel MOV BX,#cmdbuf ; pointer to cmd buf
MOV.B [BX],#$01 ; store length
INC BX
MOV.B [BX],AL ; store char
edploop PUSH BX ; save pos in buf
MOV BX,#cmdbuf ; ptr cmd buf
MOV SI,#ecmd1 ; ptr first cmd table
MOV CH,#$FF ; must be equal
CALL edsrcmd ; search in table
POP BX ; restore pos
OR.B AL,AL ; test result
JNZ edpfnd ; :found
PUSH BX ; save again
MOV BX,#cmdbuf ; ptr cmd buf
MOV SI,#ecmd2 ; ptr second cmd table
MOV CH,#$1F ; second char without ctrl ok
CALL edsrcmd ; search in table
POP BX ; restore pos
OR.B AL,AL ; test result
STC
JZ edpret ; :not found
edpfnd DEC.B AL
JZ edpcont ; :need another key
MOV BX,#ejmptab ; ptr jump table
ADD BX,CX ; +2*cmd number
ADD BX,CX
CS:
MOV DX,[BX] ; get jump addr
STC ; ok
edpret RET ; '
edpcont CALL eddiscmd ; display command char
PUSHF ; save stat
CALL keyget ; get key
INC.B cmdbuf ; inc lenght cmd buf
INC BX ; next pos
MOV.B [BX],AL ; store char
POPF ; restore stat: function key ?
JNZ edploop ; :continue
CALL eddiscmd ; display
JMP edploop ; "try it again
eddiscmdCALL ekbdstat ; display command sequence
JNZ edpret ; late - ret
PUSH BX ; save pos
XOR BX,BX ; clear char count
MOV.B statera,BL ; number of chars to erase
CALL esetcur ; set position: home
MOV BX,#cmdbuf ; ptr cmd buf
MOV.B AL,[BX] ; get cmd length
eddislp PUSH AX ; save len
INC BX ; next char
MOV.B AL,[BX] ; get char
ADD.B statera,#$02 ; 2 chars to erase
CALL edcntput ; put char
POP AX ; restore len
DEC.B AL ; another char ?
JNZ eddislp ; :yes
POP BX ; restore
RET ; "
edcntputPUSH AX ; Display control code - save char
CALL esetnrm ; set attribute
POP AX ; restore char
CMP AL,#$20 ; control ?
JB edcntl ; :yes
JMP ewritch ; 'write char
edcntl PUSH AX ; save char
PUSH AX
MOV AL,#$5E ; write ^
CALL ewritch
POP AX ; restore char
ADD AL,#$40 ; -> normal char
CALL ewritch ; write it
POP AX ; restore char
RET ; "
edsrcmd MOV CL,#$FF ; Search command in table
PUSH CX ; save cmd comparison mask
PUSH BX ; command buf ptr
edsclp POP BX ; restore
POP CX
CS:
MOV.B AL,[SI] ; get char from table
INC SI ; next pos
OR.B AL,AL ; end of table ?
JZ edscret ; :yes
INC.B CL ; command number
PUSH CX ; save
PUSH BX
MOV.B CL,[BX] ; get command len
INC BX ; go to first char
SUB.B AL,CL ; >= table len ?
JNB edsc1 ; :yes
ADD.B CL,AL ; skip this entry
JMP.b edscnxt1 ; 'try next one
edsc1 LAHF ; save flags
PUSH AX ; save table entry length
edsccmp CS:
MOV.B AL,[SI] ; char from table
SUB.B AL,[BX] ; compare them
AND.B AL,CH ; mask difference
JNZ edscnext ; :not equal - next one
INC SI ; next char
INC BX
DEC.B CL ; = length of cmd ?
JNZ edsccmp ; no: compare
POP AX ; restore flag, table entry length
SAHF
POP BX ; restore
POP CX
MOV CH,#$00 ; mode: none
MOV AL,#$FF ; found
JZ edscret ; :found
MOV AL,#$01 ; need another char
edscret RET ; '
edscnextPOP AX ; restore length
ADD.B AL,CL ; skip entry
MOV.B CL,AL
edscnxt1MOV CH,#$00 ; clr hi
ADD SI,CX ; add to ptr
JMP edsclp ; "try next one
estat CALL ekbdstat ; Display status line: Get KBD stat
JNZ edscret ; late: ret
CALL klowvid ; LowVideo
CMP.B statobs,#$FF ; obsolete ?
JZ estnrm ; no:just display pos
MOV.B statobs,#$FF ; clear flag
MOV BX,#$0000 ; screen position
MOV oldpos,BX ; remember cursor pos
MOV.B edcol,BL ; column
CALL esetcur ; set cursor pos
CALL eclrlin ; clear line
CS:
CMP.B txwinx2,#$38 ; screen width sufficient ?
JB estshrt ; :no
MOV BX,#$2A00 ; set cursor pos
CALL esetcur
CALL kdworkfn ; display filename
estshrt MOV BX,#$0600 ; cursor pos
CALL ewritpos ; put string
B "Line ",$00
MOV BX,#$1000 ; cursor pos
CALL ewritpos ; put string
B "Col ",$00
MOV BX,#$1800 ; cursor pos
CMP.B overflg,#$00 ; Overwrite ?
JZ estover ; :yes
CALL ewritpos ; put string
B "Insert ",$00
JMP.b estins ; '
NOP
estover CALL ewritpos ; put string
B "Overwrite ",$00
estins CMP.B indntflg,#$00 ; Indent ?
JZ estnrm ; :no
CALL ewrits ; put string
B "Indent",$00
estnrm MOV AL,horscr ; calc column
ADD.B AL,phcol
INC.B AL
CMP.B AL,edcol ; = old col ?
JZ estnocol
MOV edcol,AL ; set that column
MOV BX,#$1400 ; cursor pos
CALL esetcur ; set it
CALL klowvid ; LowVideo
MOV.B BL,AL ; number -> BX
MOV BH,#$00 ; clr hi
MOV AL,#$03 ; 3 chars
CALL ednum ; display number
estnocolMOV BX,edpos ; current pos = old pos ?
CMP BX,oldpos
JNZ estrow ; no: display line number
JMP eposcur ; 'restore cursor pos
estrow CALL eposcur ; restore cursor pos
CALL ekbdstat ; Get KBD stat
JNZ estend
MOV DI,txbeg ; beg of text
MOV CX,edpos
MOV DX,#$0001 ; line number
SUB CX,DI ; calc count
JZ estrow2 ; :done
estcnt CLD ; forward search
PUSH DS ; DS -> ES
POP ES
estlf MOV AL,#$0A ; search LF
REPNZ
SCAS.B
JNZ estrow2 ; :not found, done
INC DX ; count it
OR.B DL,DL ; 256 lines counted ?
JNZ estlf ; :no, continue
CALL ekbdstat ; Get KBD stat
JZ estcnt ; :nothing entered
JMP.b estend
estrow2 MOV BX,#$0B00 ; cursor pos
PUSH DX ; save line number
CALL esetcur ; set cursor pos
CALL klowvid ; LowVideo
POP BX ; restore number
MOV AL,#$05 ; 5 digits
CALL ednum ; write it
MOV BX,edpos ; current pos -> old pos
MOV oldpos,BX
estend JMP eposcur ; "restore cursor pos
ednum PUSH AX ; Write number: save field length
MOV CH,#$00 ; clear flag
CALL edcvt ; convert number
POP AX ; restore length
ADD.B AL,CH ; counter
JZ ednret ; :done
; counter
MOV.B CH,AL
MOV AL,#$20 ; pad with spaces
ednpad CALL ewritch ; put it
DEC.B CH ; another ?
JNZ ednpad ; :yes
ednret RET ; "
edcvt CMP BX,#$00 ; number -> decimal
MOV AL,#$30
JZ edcvno0 ; 0: store a 0
MOV DX,#$2710 ; digit 10000
CALL edcvdig
MOV DX,#$03E8 ; digit 1000
CALL edcvdig
MOV DX,#$0064 ; digit 100
CALL edcvdig
MOV DX,#$000A ; digit 10
CALL edcvdig
MOV DX,#$0001 ; digit 1
edcvdig XOR.B AL,AL ; clear digit
edcvdlp SUB BX,DX ; do successive subtraction
JB edcvput ; :done
INC.B AL ; count digit
JMP edcvdlp ; '
edcvput ADD BX,DX ; restore number
ADD AL,#$30 ; convert to ASCII
CMP AL,#$30 ; 0 ?
JNZ edcvno0 ; :no
OR.B CH,CH ; is it a leading zero ?
JZ ednret
edcvno0 DEC.B CH ; set flag
JMP ewritch ; "write char
edreadstCALL prints ; Input from status line
B ": ",$00
MOV BX,DX ; destination ptr
MOV SI,DX
INC BX ; point to string
MOV.B DH,[BX] ; get old length
MOV.B [BX],#$00 ; clr old length
INC BX ; go to string
erloop MOV.B disflg,#$00 ; flag: editing stat line
PUSH DX ; save regs
PUSH BX
PUSH SI
CALL edproc ; process key
POP SI
POP BX
POP DX
MOV.B disflg,#$FF ; restore flag
JNB erput ; no command: put char
JNZ ercr ; :valid command
MOV AL,cmdbuf1 ; get command
CALL etstint ; test for interrupt
JMP erloop ; 'loop back
ercr MOV.B AL,CL ; command code
CMP AL,#$00 ; CR ?
JNZ errt ; :no
MOV.B [BX],#$1A ; store ^Z at the end
RET ; 'return
errt CMP AL,#$03 ; Character right ?
JNZ errecall ; :no
MOV.B AL,[SI]$01 ; get length
CMP.B AL,DH ; = pos ?
JNB erloop
INC.B [SI]$01 ; go right
JMP.b errest ; 'get char from old value
errecallCMP AL,#$05 ; word right ?
JNZ erclr ; :no
errec1 MOV.B AL,[SI]$01 ; length = old len ?
CMP.B AL,DH
JZ erloop ; yes: loop
MOV.B AL,[BX] ; get char
CALL edcntput ; write it
INC BX ; next one
INC.B [SI]$01 ; inc len
JMP errec1 ; 'until end
erclr CMP AL,#$04 ; Word left ?
JNZ erprefix ; :no
erclrl CALL erdelc ; delete char
JNZ erclrl ; :ok, continue
JMP erloop ; 'end reached, loop
erprefixCMP AL,#$2D ; Control char prefix ?
JNZ erdel ; :no
CALL keyget ; get a key
JMP.b erput ; 'put it
erdel CMP AL,#$1B ; Test delete codes
JZ erdel1 ; :delete left
CMP AL,#$1C
JZ erdel1 ; :del left
CMP AL,#$01
JZ erdel1 ; :char left
CMP AL,#$02
JNZ erinv ; :no delete
erdel1 CALL erdelc ; delete char
JMP erloop ; 'loop back
erinv CMP.B cmdbuf1,#$12 ; word right ?
JZ errec1 ; :yes
JMP erloop ; 'loop back
erput MOV.B DL,AL ; save char
MOV.B AL,[SI]$01 ; current len
CMP.B AL,[SI] ; < max len ?
JB erput2 ; :ok
JMP erloop ; 'loop back
erput2 INC.B [SI]$01 ; inc length
MOV.B [BX],DL ; store char
errest MOV.B AL,[BX] ; get char
INC BX ; go to next
CALL edcntput ; display it
MOV.B AL,[SI]$01 ; get current len
CMP.B AL,DH ; = old len ?
JNB ergend ; :no
JMP erloop ; 'loop back
ergend MOV.B DH,[SI]$01 ; get pos
JMP erloop ; 'loop back
erdelc MOV.B AL,[SI]$01 ; Delete char: get len
OR.B AL,AL ; 0 ?
JZ erdelret ; yes: nothing to delete
DEC.B [SI]$01 ; dec length
DEC BX ; go back one char
CMP.B [BX],#$20 ; was it a control char ?
JNB erdelcc ; :no
CALL erdelcc ; delete two chars
erdelcc CALL prints ; delete one char
B $08," ",$08,$00
MOV AL,#$FF ; set flag
OR.B AL,AL
erdelretRET ; "
efind MOV.B srmode,#$00 ; ^QF:Find - set mode flag
CALL eqffind ; get find word
CALL eqfopt ; get option string
JMP.b efnd1 ; "do it
eqffind CALL eclrstat ; Get find word: clear stat line
CALL ewrits ; write string
B "Find",$00
MOV DX,#srword ; dest: find word
JMP edreadst ; "read string
eqfrepl CALL eclrs2 ; Get repl word: clear stat line
CALL ewrits ; write string
B "Replace with",$00
MOV DX,#srrepl ; dest var
JMP edreadst ; "read string
eqfopt CALL eclrs2 ; Get Options: clear stat line
CALL ewrits ; write string
B "Options",$00
MOV DX,#stopt ; dest var
CALL edreadst ; read string
CS:
MOV.B BH,txwinx2 ; end of screen
DEC.B BH
MOV BL,#$00 ; beg of line
JMP esetcur ; "set cursor pos
ereplaceMOV.B srmode,#$FF ; ^QA:Search & Replace
CALL eqffind ; get find word
CALL eqfrepl ; get replacement word
CALL eqfopt ; get option string
JMP.b efnd1 ; "do it
erepeat CALL eddiscmd ; ^L:Repeat last find
efnd1 CALL eflush ; write back current line
CALL esrcend ; end of line buffer
INC BX ; +1
MOV DX,lnpos ; or current pos in line
CALL emin
MOV DX,#line ; - beg of buffer
SUB BX,DX
MOV DX,edpos ; + current pos
ADD BX,DX
MOV srpos,BX ; -> search pos
MOV srcnt,#$0000 ; line count
MOV CX,txend ; text end-1
DEC CX
MOV srend,CX ; -> search end
MOV CX,txbeg ; text beg -> search beg
MOV srbeg,CX
MOV BX,#sropt1 ; ptr option string
MOV.B CH,[BX] ; get length
MOV.B sropt,#$00 ; clear option flag
OR.B CH,CH ; empty ?
JNZ efndcnt ; :no
JMP.b efndopte ; 'no options set
NOP ; Process option line
efndcnt INC BX ; next char
MOV.B AL,[BX] ; get char
CMP AL,#$30 ; valid digit ?
JB efndopt ; :no
CMP AL,#$3A ; > 9 ?
JNB efndopt ; :no digit
SUB AL,#$30 ; -> number. Get line count
CBW
PUSH AX ; save digit
MOV AL,#$0A ; current count * 10
IMUL srcnt
POP DX ; + digit
ADD AX,DX
MOV srcnt,AX ; -> line count
JMP.b efndo2 ; 'continue
efndopt CALL upcase ; UpCase
CMP AL,#$57 ; W ?
JNZ efndu ; :no
OR.B sropt,#$01 ; flag: whole words
efndu CMP AL,#$55 ; U ?
JNZ efndn ; :no
OR.B sropt,#$04 ; flag: ignore upper/lower
efndn CMP AL,#$4E ; N ?
JNZ efndg ; :no
OR.B sropt,#$02 ; flag: replace without asking
efndg CMP AL,#$47 ; G ?
JNZ efndb ; :no
OR.B sropt,#$08 ; flag: global
efndb CMP AL,#$42 ; B ?
JNZ efndl ; :no
OR.B sropt,#$10 ; flag: backwards
efndl CMP AL,#$4C ; L ?
JNZ efndo2 ; :no
OR.B sropt,#$20 ; flag: search block
AND.B sropt,#$F7 ; clear global
MOV CX,bkend ; search block, only
MOV srend,CX ; block end -> search end
MOV CX,bkbeg ; block beg -> search beg
MOV srbeg,CX
efndo2 DEC.B CH ; another option char ?
JNZ efndcnt ; :yes
efndopteCMP srcnt,#$01 ; at least one line to do
JA efndcnok ; :ok
MOV srcnt,#$0001
efndcnokMOV BX,srbeg ; begin limit
MOV AL,sropt ; option
TEST AL,#$10 ; backwards ?
JZ efndfwd ; :no
MOV BX,srend ; end limit
efndfwd TEST AL,#$28 ; block & global ?
JNZ efndrep ; :no
MOV BX,srpos ; current pos
efndrep MOV srpos,BX ; store search pos
TEST.B sropt,#$10 ; backwards ?
JNZ efndbkw ; :yes
CMP BX,srend ; = search limit ?
JB efndsrc ; :no, continue
JMP efndexit ; 'done
efndbkw DEC BX ; go back
CMP BX,srbeg ; = search limit ?
JNB efndsrc ; :no
JMP efndexit ; 'end it
efndsrc MOV DX,#srword2 ; ptr searched word
MOV AL,srword1 ; get length
MOV.B CH,AL
TEST.B sropt,#$10 ; backwards ?
JZ efndfw2 ; :no
DEC.B AL ; go to its end
ADD.B AL,DL
MOV.B DL,AL
JNB efndfw2
INC.B DH
efndfw2 TEST.B sropt,#$01 ; whole words ?
JZ efndall1 ; :no
PUSH DX ; save
PUSH BX
CALL edbeg ; at the beginning ?
MOV.B AL,[BX] ; get char
POP BX ; restore
POP DX
JB efndall1 ; :at beg
CALL edfnchar ; is it alphanum ?
JB efndpos ; :yes - continue searching
efndall1OR.B CH,CH ; length = 0 ?
JZ efndnil ; :found
efndcmp CALL edcmp ; compare chars
JNZ efndpos ; :not the same
DEC.B CH ; another char ?
JNZ efndnch ; :yes
efndnil TEST.B sropt,#$01 ; whole words ?
JZ efndall2 ; :no
PUSH BX ; save
CALL edend ; end ?
MOV.B AL,[BX] ; get char
POP BX ; restore
JB efndall2 ; :at the end
CALL edfnchar ; alphanum ?
JB efndpos ; yes: not valid
efndall2TEST.B sropt,#$10 ; backwards ?
JNZ efndbw2 ; :yes
INC BX ; end of text ?
CMP BX,txend
efndbw2 CMP.B srmode,#$00 ; search mode ?
JZ efndfnd ; :find
CALL edoreplc ; replace it
efndfnd TEST.B sropt,#$28 ; block global ?
JZ efndnxt ; :yes
JMP efndrep ; 'continue
efndnxt DEC srcnt ; another line to process ?
JZ efndend ; :no
JMP efndrep ; 'continue
efndend JMP.b efsetpos ; 'set new pos
NOP
efndnch PUSH DX ; save pos
CALL edend ; at the end ?
POP DX ; restore
JB efndexit ; :yes
TEST.B sropt,#$10 ; backwards ?
JZ efndfw3 ; :no
DEC DX ; go back
JMP efndcmp ; '
efndfw3 INC DX ; next char
JMP efndcmp ; 'continue
efndpos MOV BX,srpos ; get current search pos
CALL edend ; at the end ?
JB efndexit ; :yes
JMP efndrep ; 'continue
efndexitCALL edrange ; end search: limit new pos
CALL efsetpos ; set pos
TEST.B sropt,#$28 ; global block ?
JNZ espret ; :no
CALL eclrstat ; clear status line
CALL ewriterr ; write string
B "Search string not found",$00
JMP waitesc ; "wait for ESC
efsetposCALL esetpos ; set position
JMP erstlin ; "restore line
esetpos MOV DX,txend ; Set pos
DEC DX ; text end - 1
CMP BX,DX ; = current ?
JB esp2 ; :below
XCHG BX,DX ; limit it
esp2 PUSH BX ; save
PUSH BX
CALL esrcbeg ; search beg of that line
MOV edpos,BX ; -> current pos
XCHG BX,DX ; -> DX
POP BX ; restore
SUB BX,DX ; calc pos in line
MOV DX,#line ; + buffer offset
ADD BX,DX
MOV lnpos,BX ; -> pos in line buffer
CALL erepos ; reposition in current line
CALL eseldisp ; do selective rewrite
POP BX ; restore pos
espret RET ; "
edrange MOV DX,txbeg ; bring BX into range:
CALL emin ; >= text beg,
MOV BX,txend
DEC BX
JMP emin ; "< text end
edfncharCMP AL,#$30 ; char in alphanum ?
JB edfnch2 ; <0: no
CMP AL,#$3A
JB edfnchrt ; 0..9: yes
CMP AL,#$41
JB edfnch2 ; <A: no
CMP AL,#$5B
JB edfnchrt ; A..Z: yes
CMP AL,#$61
JB edfnch2 ; <a: no
CMP AL,#$7B
JB edfnchrt ; a..z: yes
edfnch2 OR.B AL,AL ; test flag
RET ; "
edbeg TEST.B sropt,#$10 ; At the beginning ?
JZ edend2 ; :forward
edbeg2 INC BX
CMP BX,srend ; = search limit end ?
CMC
edfnchrtRET ; '
edend TEST.B sropt,#$10 ; end reached ?
JZ edbeg2 ; :forward
edend2 DEC BX
CMP BX,srbeg ; = search limit beg ?
RET ; "
edcmp XCHG DX,BX ; Compare chars
MOV.B AL,[BX] ; get char in search string
XCHG DX,BX
CMP AL,#$01 ; wild card ?
JZ edfnchrt ; yes: ok
CMP.B AL,[BX] ; compare it
JZ edfnchrt ; yes: ok
TEST.B sropt,#$04 ; ignore upper/lower ?
JZ edcmp2 ; :no, just compare
CALL edfnchar ; is it a valid char ?
JNB edcmp2 ; :no
XOR.B AL,[BX] ; mask different bits
AND AL,#$DF
RET ; '
edcmp2 CMP.B AL,[BX] ; compare it
RET ; "
edoreplcPUSH BX ; Do replace
CALL ekbdstat ; get KBD stat
JZ erpdis ; nothing: display it
TEST.B sropt,#$02 ; replace without asking ?
JNZ erpdoit ; :yes
erpdis CALL efsetpos ; set new position
CALL eredispl ; redisplay
TEST.B sropt,#$02 ; replace without asking ?
JNZ erpdoit ; :yes
CALL eclrs2 ; clear status line
CALL printatt ; write string
B "Replace (",$D9,"/",$CE,"): ",$00
erpblnk MOV.B BL,phrow ; Blink cursor: alternate
MOV.B BH,phcol ; status line / cursor pos
CALL esetcur ; set cursor pos
MOV CX,#$0004 ; do 4 times
erpbw1 CALL ekbdstat ; get KBD stat
JNZ erpbw2 ; :key pressed
MOV BX,#$0064 ; wait 100 ms
PUSH CX ; save count
CALL delaybx ; Delay
POP CX ; restore
LOOP erpbw1 ; :another
MOV BX,#$0F00 ; pos:status line
CALL esetcur ; set cursor pos
MOV CX,#$0004 ; 4 times
erpblnk2CALL ekbdstat ; get KBD stat
JNZ erpbw2 ; :key pressed
MOV BX,#$0064 ; wait 100 ms
PUSH CX ; save cnt
CALL delaybx ; Delay
POP CX ; restore cnt
LOOP erpblnk2 ; :another
JMP erpblnk ; 'blink again
erpbw2 CALL keyget ; get char
CALL etstint ; test interrupt
CALL upcase ; UpCase
CMP AL,#$59 ; Y ?
JZ erpdoit ; :yes, do it
CMP AL,#$19 ; ^Y ?
JNZ erpbk
erpdoit MOV.B txchg,#$FF ; set flags: text changed
MOV.B txcomp,#$00 ; code invalid
MOV.B CL,srrepl1 ; length of replacement
MOV CH,#$00
POP BX ; restore pos
PUSH BX
PUSH CX ; save
MOV AL,srword1 ; length of word found
SUB.B AL,CL ; - length of replacement
MOV.B CL,AL ; -> CL
LAHF ; save flags
PUSH AX
JNB erp2 ; do sign extension
DEC.B CH
erp2 TEST.B sropt,#$10 ; backwards ?
JNZ erpbw ; :yes
MOV BX,srpos ; beg of that word
erpbw POP AX ; flag
SAHF
PUSH BX
JZ erpsame ; : no difference
CALL echgsize ; change len
erpsame POP DX ; restore
POP CX
MOV.B AL,CH ; length = 0 ?
OR.B AL,CL
JZ erpnil
MOV BX,#srrepl2 ; move in replacement word
CALL movebk
erpnil CALL ekbdstat ; get KBD stat
JZ erpok ; :nothing
CALL eredall ; redisplay all
JMP.b erpchg ; '
erpok PUSH DX ; save pos
CALL erstlin ; restore line
CALL eredall ; flag: redisplay
POP DX ; restore pos
erpchg TEST.B sropt,#$10 ; backwards ?
JNZ erpbk ; :yes
POP BX ; remove
XCHG DX,BX ; set to end of word
RET ; '
erpbk POP BX ; restore beg of word
erpret RET ; "
ekw CMP.B bkhide,#$FF ; ^KW:Write block to disk
JZ erpret ; block hidden: ret
CALL eflush ; rewrite line
CALL erstlin ; restore line
MOV AX,bkbeg ; block begin > block end ?
CMP AX,bkend
JNB erpret ; yes: ret
CALL etstblk ; test block
CALL erstlin ; restore line
ekwlp CALL eclrstat ; clr status line
CALL ewrits ; write string
B "Write block to file",$00
CALL ekwfile ; get filename
JZ erpret ; nothing entered: ret
CALL kpasext ; default extension .PAS
MOV AX,#$3D00 ; open file
MOV DX,#scrpn ; name ptr
PUSH DS ; DS -> ES
POP ES
CALL dos
MOV BX,AX ; file handle
JB ekwnew
MOV AH,#$3E ; close file
CALL dos
CALL eclrs2 ; clr status line
CALL ewrits
B "Overwrite old ",$00
MOV SI,#scrpn ; write filename
CALL printpn
CALL yorn ; Y or N ?
JZ ekwlp ; no: get new filename
ekwnew MOV AH,#$3C ; create file
MOV DX,#scrpn ; name ptr
XOR CX,CX ; no attribute
PUSH DS ; DS -> ES
POP ES
CALL dos
JNB ekwopen ; :ok
CALL eclrs2 ; clr status line
CALL ewriterr ; write message
B "Unable to create ",$00
MOV SI,#scrpn ; write filename
CALL printpn
CALL waitesc ; wait for ESC
JMP ekwlp ; 'get new filename
ekwopen PUSH AX ; save file handle
CALL eclrs2 ; clr status line
POP BX ; restore
MOV DX,bkbeg ; block begin
MOV SI,bkend ; block end
MOV.B AL,[SI] ; get char at block end
PUSH AX ; save it
PUSH SI ; save its addr
MOV.B [SI],#$1A ; store a ^Z there
MOV CX,SI ; count = block end - block beg
SUB CX,DX
CMP BX,#$04 ; write to device ?
JBE ekwdev ; :yes
INC CX ; file: write ^Z, too
ekwdev MOV AH,#$40 ; write byte block
CALL dos
JB ekwfull ; :error
SUB CX,AX ; length = expected ?
JZ ekwdone ; :yes
DEC CX ; 1 byte difference ?
JNZ ekwfull ; no: error
ekwdone CALL ekwclose ; close file
JNB ekwok ; :ok
CALL ewriterr ; write message
B "Error closing file",$00
CALL waitesc ; wait for ESC
ekwok POP SI ; restore char at block end
POP AX
MOV.B [SI],AL
RET ; '
ekwfull CALL ekwclose ; close file
MOV AH,#$41 ; delete it
MOV DX,#scrpn ; name ptr
CALL dos
CALL ewriterr ; write message
B "Disk full",$00
CALL waitesc ; wait for ESC
JMP ekwok ; 'restore block end
ekwcloseMOV AH,#$3E ; close file
JMP dos ; "
ekwfile MOV DX,#fnbuf ; get filename
CALL edreadst ; read it
MOV BX,#fnbuf2 ; first char
CMP.B [BX],#$1A ; = ^Z ?
ekrret RET ; "yes: nothing entered
ekr CALL eclrstat ; ^KR:Read block from disk
CALL ewrits ; write string
B "Read block from file",$00
CALL ekwfile ; get file name
JZ ekrret ; nothing entered: ret
CALL kpasext ; default extension .PAS
MOV AX,#$3D00 ; open file
MOV DX,#scrpn ; name ptr
PUSH DS ; DS -> ES
POP ES
CALL dos ; do it
XCHG AX,BX ; file handle
JNB ekrfound ; :ok
CALL eclrs2 ; clr status line
CALL ewriterr ; write message
B "File ",$00
MOV SI,#scrpn ; write filename
CALL printpn
CALL prints ; write string
B " not found",$00
CALL waitesc ; wait for ESC
JMP ekr ; 'get another filename
ekrfoundPUSH BX ; save file handle
MOV.B bkhide,#$00 ; flag: block not hidden
CALL etstblk ; test block
MOV BX,txend ; text end
MOV DX,txmemend ; text space end
MOV CX,#$00FE ; + some space free
ADD BX,CX
SUB BX,DX
PUSH BX ; save free space
MOV CX,BX
MOV BX,nbkbeg ; new block beg
STC
CALL echgsize ; make space
POP AX ; restore free space
POP BX ; restore file handle
MOV DX,nbkbeg ; new block beg
MOV CX,DX ; -> offset
SUB CX,AX
MOV explen,CX ; save end free space
MOV vfilbig,#ebigfil ; set error vec
CALL kload2 ; load block
INC BX ; end of block read
MOV bkend,BX ; store as new block end
XCHG DX,BX ; -> DX
MOV BX,nbkbeg ; new block beg
MOV bkbeg,BX ; -> block beg
ekrremovMOV BX,explen ; end free space
SUB BX,DX ; -block end
MOV CX,BX ; -> count
JMP.b ekvremov ; 'remove unneeded space
MOV DX,nbkbeg ; File too big:
CALL ekrremov ; remove space made free
JMP etsterr ; "error: out of space
eblkmov CALL etstblk ; ^KV:Move block - test block
JNB ekvrest
CALL ecopyblk ; copy it
MOV BX,nbkbeg ; new block beg
MOV DX,bkbeg ; -> block beg
MOV bkbeg,BX
ADD BX,CX ; + count
MOV bkend,BX ; -> block end
ekvremovXCHG BX,DX ; old block beg -> BX
CLC
CALL echgsize ; remove old block
MOV BX,bkbeg ; block begin
CALL efsetpos ; set position
JMP erewrall ; 'rewrite all
ekvrest JMP erstlin ; "restore line
eblkcpy CALL etstblk ; ^KC:Copy block - test block
JNB ekvrest
CALL ecopyblk ; do copy it
MOV BX,nbkbeg ; new block beg
MOV bkbeg,BX ; -> block beg
ADD BX,CX ; + count
MOV bkend,BX ; -> block end
CALL eseldisp ; selective redisplay
CALL erstlin ; restore line
JMP erewrall ; "rewrite all
etstblk CMP.B bkhide,#$00 ; Test block
JZ etb2 ; :block not hidden
CLC
RET ; '
etb2 CALL esrcend ; search end of input line
INC BX ; +1
MOV DX,lnpos ; pos in input line
PUSH DX ; save it
CALL emin ; take smaller of them
XCHG BX,DX ; -> BX
CALL efl2 ; write back to memory
POP BX ; restore pos in input line
MOV DX,#line ; calc relative pos in input line
SUB BX,DX
MOV DX,edpos ; +pos of current line
ADD BX,DX
MOV nbkbeg,BX ; -> new block beg
PUSH BX ; save
MOV DX,bkbeg ; block beg+1
INC DX
CMP BX,DX ; destination inside block ?
MOV DX,bkend ; block end
JB etbbeg ; :no
CMP BX,DX
JNB etbbeg ; :no
OR.B AL,AL ; dest is in old block...
JMP.b etb3 ; 'end it
etbbeg MOV BX,bkbeg ; block beg
SUB BX,DX ; -block end
MOV nbk,BX ; -> - block len
MOV CX,BX ; -> count
etb3 POP BX ; restore destination
RET ; "
ecopyblkCALL echgsize ; Copy block: make space for it
MOV CX,nbk ; get count
NEG CX ; (is - block len)
MOV DX,nbkbeg ; block destination
MOV BX,bkbeg ; block source
PUSH CX ; save count
CALL movebk ; move it
POP CX ; restore count
RET ; "
eblkdel CALL eflush ; ^KY:Delete block - rewrite line
CMP.B bkhide,#$00 ; block hidden ?
JNZ ekyrest
MOV BX,bkbeg ; block beg
CALL esrcbeg ; search beg of line
MOV edpos,BX ; -> current pos
MOV BX,posfifo ; update pos FIFO
MOV DX,bkbeg ; inside block ?
INC DX
CMP BX,DX
JB ekynochg ; :no
MOV DX,bkend
CMP BX,DX
JNB ekynochg ; :no
MOV BX,edpos ; current pos
MOV posfifo,BX ; -> FIFO
ekynochgMOV BX,bkend ; block end-block beg
MOV DX,bkbeg
SUB BX,DX ; calc count
JNB ekyblkok ; :ok
ekyrest JMP erstlin ; 'restore line
ekyblkokMOV CX,BX ; -> count
XCHG BX,DX ; swap addrs
PUSH BX ; save them
PUSH CX
PUSHF
CALL eseldisp ; do selective rewrite
POPF
POP CX ; restore
POP BX
CALL echgsize ; delete block
MOV BX,edpos ; current pos
MOV bkbeg,BX ; -> block beg
MOV bkend,BX ; -> block end
CALL erstlin ; restore line
JMP.b erewrall ; "rewrite all
NOP ; ^KH:hide / unhide block
ekh NOT.B bkhide ; toggle flag: block hidden
JMP.b erewrall ; "rewrite all
NOP ; Mark block end
emarkendMOV BX,lnpos ; pos in line
MOV bkendl,BX ; -> block end in buffer
MOV BX,edpos ; line pos
MOV bkend,BX ; -> block end
TEST.B attflg,#$02 ; block marked in this line ?
LAHF
OR.B attflg,#$02 ; set that flag
emrend2 CMP.B bkhide,#$FF ; block hidden ?
MOV.B bkhide,#$00 ; make it visible
JZ erewrall ; hidden: rewrite all
SAHF
JZ erewrall ; no block marked in line: all
JMP.b erewrlin ; "rewrite line, only
NOP ; Mark block beg
emarkbegMOV BX,lnpos ; get pos in line
MOV bkbegl,BX ; -> block beg in line buffer
MOV BX,edpos ; current pos
MOV bkbeg,BX ; -> block beg
TEST.B attflg,#$01 ; block beg in this line ?
LAHF
OR.B attflg,#$01 ; set flag
JMP emrend2 ; "as above
ejbkbeg CALL eflush ; ^QB:to beginning of block
MOV BX,bkbeg ; block beg
JMP efsetpos ; "set new position
ejbkend CALL eflush ; ^QK:to end of block
MOV BX,bkend ; block end
JMP efsetpos ; "set new position
erewrlinMOV BH,#$00 ; Rewrite line
CALL eposrow ; pos cursor to beg of line
MOV BX,#line ; pos of current line
MOV.B attchg,#$FF ; critical line
CALL edmalin ; display it
MOV.B attchg,#$00 ; normal again
RET ; "
erewrallCALL erewrlin ; rewrite line
JMP eredall ; "then rewrite all
ejtxend CALL eflush ; ^QC:to end of text
MOV BX,txend ; text end
JMP efsetpos ; "set new position
ejlnbeg MOV BX,#line ; ^QS:to beg of line
MOV lnpos,BX ; buffer beg -> pos in line
JMP erepos ; "reposition in line
ejlnend CALL esrcend ; ^QD:to end of line
INC BX ; search end of line+1
MOV DX,#lineend ; too much ?
CMP BX,DX
JB ele2 ; :no
MOV BX,#lineend1 ; limit it
ele2 MOV lnpos,BX ; set new pos in line
JMP erepos ; "reposition in line
etogovr MOV.B statobs,#$00 ; ^V:insert mode on/off
NOT.B overflg ; toggle flag
RET ; "status line obsolete
etogind MOV.B statobs,#$00 ; ^QI:indent mode on/off
NOT.B indntflg ; toggle flag
ednrt RET ; "status line obsolete
eposcur MOV.B BH,phcol ; put cursor to current pos
eposrow MOV.B BL,phrow
JMP esetcur ; "set cursor pos
edn MOV BX,edpos ; ^X:line down
CALL elindn ; go down one line
JB ednrt
CALL eflush ; rewrite line
MOV BX,edpos ; now really go down
CALL elindn
edn2 MOV edpos,BX ; store new pos
MOV.B scrfl2,#$00 ; flag: short update
MOV.B scrfl1,#$FF
CALL eseldisp ; do selective redisplay
MOV.B scrfl1,#$00 ; clear flag
JMP erstlin ; "restore line
eup MOV BX,edpos ; ^E:line up
CALL elinup ; go up one line
JB ednrt
PUSH BX ; save pos
CALL eflush ; rewrite line
POP BX ; now go there
JMP edn2 ; "set it
escrup MOV AX,disbeg ; ^W:scroll up
CMP AX,txbeg ; display beg = text beg ?
JZ eptret ; yes: ret
CALL eflush ; rewrite line
XOR CX,CX ; count pos on screen
MOV BX,edpos ; current pos
esulp CMP BX,disbeg ; = display beg ?
JZ esu2 ; yes: do it
CALL elinup ; go up one line
INC CX ; count it
JMP esulp ; '
esu2 XCHG BX,edpos ; current pos = top of screen
CS:
MOV AL,txwiny2 ; line count - 3
SUB AL,#$03
CMP.B CL,AL ; = count ?
JNZ esu3 ; no: ok
CALL elinup ; go up one line
esu3 PUSH BX ; save current pos
MOV BX,edpos ; display beg
CALL elinup ; go up one line
CALL edn2 ; display it
POP BX ; restore current pos
esu4 JMP edn2 ; "set new pos
escrdn CALL eflush ; ^Z:scroll down
PUSH edpos ; save current pos
MOV BX,disbeg ; start from display beg
XOR CX,CX
CS:
MOV.B CL,txwiny2 ; line count-2 -> CX
SUB.B CL,#$02
esdlp CALL elindn ; go down one line
LOOP esdlp ; do it again
PUSHF
CALL edn2 ; set new pos
POPF
POP BX ; restore old pos
JB esu4 ; :set new pos
CMP BX,disbeg ; >= display beg ?
JNB esu4 ; :set new pos
CALL elindn ; go down one line
JMP esu4 ; 'set new pos
eptret RET ; "
epagtop MOV BX,disbeg ; ^QE:to page top
CMP BX,edpos ; display beg = current line ?
JZ eptret ; yes: done
PUSH BX ; save this pos
CALL eflush ; rewrite line
POP BX ; display beg -> new pos
JMP esu4 ; "set that pos
epagbot CALL eflush ; ^QX:to bottom of page
MOV BX,disbeg ; display beg
XOR CX,CX ; line count-3 -> CX
CS:
MOV.B CL,txwiny2
SUB.B CL,#$03
epblp CALL elindn ; go down one line
LOOP epblp ; :again
JMP esu4 ; "set new pos
epagdn CALL eflush ; ^C:page down - rewrite line
CS:
MOV AL,txwiny2 ; line count - 2
SUB AL,#$02
MOV.B CH,AL ; -> CL, CH
MOV.B CL,AL
MOV BX,disbeg ; display beg
epdlp CALL elindn ; go down one line
DEC.B CH
JNZ epdlp ; :another one
MOV disbeg,BX ; store new display beg
MOV BX,edpos ; current pos
epdl2 CALL elindn ; go down one line
LOOP epdl2 ; :again
epd2 MOV edpos,BX ; set new pos
CALL eseldisp ; redisplay
CALL eredall ; redisplay all
JMP erstlin ; "restore line
epagup CALL eflush ; ^R:page up - rewrite line
CS:
MOV AL,txwiny2 ; line count - 2
SUB AL,#$02
MOV.B CH,AL ; -> CL, CH
MOV.B CL,AL
MOV BX,disbeg ; display beg
epulp CALL elinup ; go up one line
DEC.B CH
JNZ epulp ; :another
MOV disbeg,BX ; store new display beg
MOV BX,edpos ; current pos
epul2 CALL elinup ; go up one line
LOOP epul2 ; :another
JMP epd2 ; "set new pos, redisplay
etxtbeg MOV BX,disbeg ; ^QR:to beg of text
MOV DX,txbeg ; text beg = display beg ?
CMP BX,DX
JZ etsb2 ; :yes
CALL eredall ; no:redisplay all
etsb2 CALL eflush ; rewrite line
MOV BX,txbeg ; text beg
MOV edpos,BX ; -> current pos
MOV disbeg,BX ; -> display beg
CALL eseldisp ; do selective redisplay
CALL erstlin ; restore line
MOV BX,#line ; go to beg of line
MOV lnpos,BX ; pos in line
JMP erepos ; "reposition in line
ecr CMP.B overflg,#$00 ; CR:carriage return
JNZ ecrins ; :insert mode
CALL edn ; go down one line
JMP ejlnbeg ; 'go to beg of line
ecrins MOV.B txchg,#$FF ; set flags: text changed
MOV.B txcomp,#$00
MOV BX,phrow ; current cursor pos
INC.B BL
CALL esetcur ; set cursor to beg of line
CALL elinbrk ; insert line break
MOV BX,edpos
PUSH BX ; save current pos
CALL erstlin ; restore line
CALL eflush ; rewrite line
POP BX ; restore current pos
CALL elindn ; go down one line
MOV edpos,BX ; -> current pos
MOV BX,#line ; go to beg of line
CALL eredisp ; redisplay
CALL eposcur ; set new position
CMP.B indntflg,#$00 ; indent ?
JZ elftret ; :no
CALL etabup ; up one line from curr pos
JB elftret ; :no
MOV SI,#esepspc ; separator: spaces only
CALL etslp ; 'go word right
JNB elftret
JMP etab ; "do tab
eredisp MOV lnpos,BX ; store pos in buffer
CALL erepos ; reposition in line
CALL eseldisp ; selective redisplay
JMP erstlin ; "restore line
elinins CALL elinbrk ; ^N:insert line - insert line break
MOV.B BL,phrow ; row + 1
INC.B BL
XOR.B BH,BH ; col = 0
CALL esetcur ; set cursor pos
MOV BX,edpos ; current pos
CALL elindn ; go down one line
CALL edmalin ; display line
JMP erstlin ; "restore line
elinbrk CALL eflush ; Insert line break: rewrite line
CALL xinsline ; InsLine
CALL esrcend ; search end of line + 1
INC BX
MOV DX,lnpos ; pos in buffer
CALL emin
MOV DX,#line ; relative pos in line
SUB BX,DX
elb2 XCHG BX,DX ; -> DX
MOV BX,edpos ; current pos
ADD BX,DX ; + relative pos
PUSH BX ; save pos of line break
STC ; insert two bytes
MOV CX,#$FFFE
CALL echgsize ; do it
POP BX ; restore pos
MOV [BX],#$0A0D ; store CR, LF there
INC BX
elftret RET ; "
eleft MOV BX,lnpos ; ^S, ^H:character left
DEC BX ; pos in line - 1
CMP BX,#line ; already at the beg ?
JB elftret
eleft2 MOV lnpos,BX ; set new pos
JMP erepos ; "reposition in line
eright MOV BX,lnpos ; ^D:character right
INC BX ; pos in line + 1
CMP BX,#lineend0 ; = end of buffer ?
JB eleft2 ; below: ok, set it
RET ; "
elstpos CALL eflush ; ^QP:to last position
MOV BX,qppos ; get pos from FIFO
CALL esrcbeg ; search beg of line
MOV edpos,BX ; -> current pos
MOV BX,qppos1 ; get display beg from FIFO
JMP eredisp ; "redisplay
emrkwrd CALL ewrdrt ; ^T:mark word - word right
CALL ewrdlft ; word left
MOV BX,lnpos ; pos in line buffer
emwlp CALL etstsep ; char = separator ?
JB emwend ; yes: word end found
INC BX ; next char
CMP BX,#lineend0 ; end of line ?
JB emwlp ; :no, continue
emwend MOV lnpos,BX ; -> current pos
CALL emarkend ; mark block end
CALL ewrdlft ; go left one word
JMP emarkbeg ; "mark block beg
ewlup MOV BX,edpos ; get current pos
CALL elinup ; go up one line
JB ewloldp
PUSH BX ; save pos
CALL eflush ; rewrite line
POP BX
MOV edpos,BX ; set new pos
MOV.B scrfl2,#$00 ; set flag: short updating
CALL eseldisp ; selective redisplay
CALL erstlin ; restore line
CALL esrcend ; search end of line
JMP.b ewlle ; 'store position
ewrdlft MOV BX,lnpos ; ^A:word left
ewllp DEC BX ; pos in buffer-1:search char<>sep
CMP BX,#line ; = line beg ?
JB ewlup ; yes: go up one line
CALL etstsep ; char = separator ?
JB ewllp ; yes: continue searching
ewll2 DEC BX ; go back: search separator
CMP BX,#$046C ; = line beg ?
JB ewlle ; yes: end it
CALL etstsep ; char = separator ?
JNB ewll2 ; :no
ewlle INC BX ; go to left of word
ewlpos MOV lnpos,BX ; store pos in line
ewloldp MOV BX,lnpos ; get pos
JMP erepos ; "reposition in line
ewrdrt CALL esrcend ; ^F:word right
MOV AX,BX ; end of line -> AX
MOV BX,lnpos ; pos in line
SUB AX,BX ; calc count
MOV AL,#$00
JNB ewr1 ; :ok
INC.B AL
ewr1 MOV curpast,AL ; flag: cursor past end of line
ewrlp DEC BX
ewrl2 INC BX ; next char
CMP BX,#lineend0 ; end of line ?
JB ewrtst ; :no
ewrl3 MOV BX,edpos ; current pos
CALL elindn ; go down one line
JB etabret
CALL eflush ; rewrite line
MOV BX,edpos ; current pos
CALL elindn ; go down
MOV edpos,BX ; new pos
MOV.B scrfl2,#$00 ; short update
CALL eseldisp ; redisplay
CALL erstlin ; restore line
MOV BX,#$046C ; go to beg of line
MOV lnpos,BX
CALL etstsep ; char = separator ?
JB ewrlp ; :yes
JMP erepos ; 'set that pos
ewrtst CALL etstsep ; char = separator ?
JNB ewrl2 ; :no
ewrlp2 INC BX ; next char
CMP BX,#lineend0 ; end of line ?
JB ewrtst2 ; :no
CMP.B curpast,#$00 ; cursor past end of line ?
JNZ ewrl3 ; :yes
CALL esrcend ; search end of line + 1
INC BX
JMP ewlpos ; 'set that pos
ewrtst2 CALL etstsep ; char = separator ?
JB ewrlp2 ; :yes
JMP ewlpos ; "set that pos
etabup MOV BX,edpos ; current pos
CALL elinup ; go up one line
etabret RET ; "
etab CALL etabup ; ^I:Tab - go up one line
JB etabret
MOV AL,phrow ; save phys row
PUSH AX
MOV BX,lnpos ; pos in line
MOV lnupper,BX ; store pos of upper line
MOV.B disflg,#$00 ; normal mode
CALL eflush ; rewrite line
MOV BX,edpos ; current pos
PUSH BX ; save it
CALL elinup ; go up one line
MOV edpos,BX ; store pos
CALL erstlin ; restore line
PUSH sepptr ; save ptr
MOV sepptr,#esepspc ; set sep ptr: spaces only
CALL ewrdrt ; word right
POP sepptr ; restore sep ptr
POP edpos ; restore current pos
POP AX
MOV phrow,AL ; restore phys row
CALL erstlin ; restore line
MOV.B disflg,#$FF ; flag: short update
CMP.B overflg,#$FF ; insert ?
JZ etabins ; :yes
JMP erewrlin ; 'rewrite line
etabins MOV BX,lnpos ; pos in line
MOV DX,lnupper ; pos in upper line
SBB BX,DX ; difference
JBE etabret ; not found: ret
XCHG BX,DX ; -> count
etabinslPUSH DX ; save count
CALL einsch ; insert a char
MOV.B [BX],#$20 ; space
POP DX ; restore count
DEC.B DL ; another ?
JNZ etabinsl ; :yes
JMP erewrlin ; "rewrite line
edeleol MOV BX,lnpos ; ^QY:delete to end of line
PUSH BX ; save pos in line
CALL etstbk ; test block markers in line
POP BX ; restore pos
PUSH BX
MOV DX,#lineend1 ; end position
edelp MOV.B [BX],#$20 ; fill up with spaces
CMP BX,DX ; end reached ?
JZ edeend ; :yes
INC BX ; next char
JMP edelp ; '
edeend POP BX ; restore pos in line
JMP eredlin ; "redisplay line
edellin MOV BX,#line ; ^Y:delete line
MOV lnpos,BX ; to line beg
CALL erepos ; reposition in line
CALL edeleol ; delete to end of line
CALL eflush ; rewrite line
MOV BX,edpos ; current pos
PUSH BX ; save it
PUSH BX
CALL elindn ; go down one line
POP DX ; old pos
JB edlend
SUB BX,DX ; difference
MOV CX,BX ; -> count
POP BX ; restore current pos
JNZ edb3 ; :erase the rest
RET ; '
edlend POP BX ; remove from stack
JMP erstlin ; "restore line
edlinbrkCALL efl2 ; del line break: rewrite to BX
MOV BX,edpos ; current pos
CALL elindn ; go down one line
JNB edb2 ; :ok
JMP erstlin ; 'restore line
edb2 DEC BX ; go back two bytes
DEC BX
MOV CX,#$0002 ; delete 2 bytes
OR.B AL,AL ; clear carry
edb3 CALL echgsize ; do it
CALL xdelline ; DelLine
CS:
MOV AL,txwiny2 ; get line cnt - 1
DEC.B AL
CALL edsplin ; redisplay last line
JMP erstlin ; "restore line
edelwrd CALL esrcend ; ^T:delete right word
MOV DX,lnpos ; pos in buffer
CMP BX,DX ; > end ?
XCHG BX,DX
JB edlinbrk ; :delete line break
MOV.B AL,[BX] ; get char
CMP AL,#$20 ; space ?
JZ edelspc ; :delete spaces
CALL etstsep ; separator ?
JB edel2 ; :end it
edwlp CALL edelch ; delete char
CALL etstsep ; separator ?
JB edrest ; :yes, end
JMP edwlp ; "another char
edwend MOV BX,edpos ; current pos
CALL elinup ; up one line
JB edwret
CALL eup ; line up
CALL ejlnend ; go to line end
JMP edelwrd ; 'delete word
edwret RET ; '
edelrt MOV BX,lnpos ; ^G:delete right
JMP.b edel2 ; '
edellft MOV BX,lnpos ; DEL:delete left
DEC BX ; go back one char
CMP BX,#line ; beg of line ?
JB edwend ; yes: go to line above
MOV lnpos,BX ; store new pos
edel2 CALL edelch ; delete char
edrest CALL erepos ; reposition on line
JMP eredlin ; "redisplay line
edelspc CALL edelch ; Delete spaces
MOV.B AL,[BX] ; get char
CMP AL,#$20 ; space ?
JZ edelspc ; :delete it
JMP edrest ; "redisplay
eredisplCALL eposcur ; Redisplay: set cursor pos
CALL edisbdn ; go down from display beg
edisp1 CALL ekbdstat ; get KBD stat
JNZ edisp2
CALL edsp2 ; redisplay
JNB edisp1 ; :continue
edisp2 JMP eposcur ; "set cursor pos
edodisplCALL eposcur ; Redisplay: set cursor pos
CALL edisbdn ; go down from display beg
eddlp CALL edsp2 ; redisplay
JNB eddlp ; :continue
JMP eposcur ; "set cursor pos
edisbdn MOV.B CL,dislin ; go down from display beg
edisbdn2XOR.B CH,CH ; count: first line to display
MOV BX,disbeg ; display beg
DEC CX ; count down
JZ eddnil ; :end it
eddn CALL elindn ; go down one line
JB eddend
LOOP eddn ; :another line
eddnil RET ; '
eddend MOV BX,txend ; go to text end
RET ; "
edodisp CALL elindn ; do redisplay: go down
JNB edsp2 ; :ok
MOV BX,txend ; go to text beg
edsp2 MOV AL,dislin ; display from where ?
CS:
CMP.B AL,txwiny2 ; = line cnt ?
JNB edspret
INC.B dislin ; go down one more line
CMP.B AL,phrow ; = phys row ?
JZ edodisp ; yes: go down
edsp3 MOV scrrow,AL ; current line
CMP BX,txend ; text end reached ?
JNB edsp4 ; :yes
CALL edmalin ; display line
CLC ; not yet end
RET ; '
edsp4 CALL esetnrm ; NormVideo
CALL eclrlin ; clear line
CLC ; not yet end
RET ; '
edspret STC ; end it
RET ; "
edsplin PUSH AX ; redisplay one line
MOV.B CL,AL ; line number
CALL edisbdn2 ; go down to this line
POP AX ; restore
JMP edsp3 ; "redisplay that line
esrcbeg MOV AL,#$0A ; search beg of line: LF
esblp DEC BX ; go back
CMP BX,txbeg ; text beg ?
JZ esbend ; yes: end it
JB esb1 ; below: next char
CMP.B AL,[BX] ; test char: LF ?
JNZ esblp ; no: continue searching
esb1 INC BX ; next char
esbend RET ; "
eskipcr CMP AL,#$0D ; Skip CR
JNZ escret ; no: ret
MOV.B AL,[BX] ; get char
INC BX ; next char
CMP BX,txend ; text end ?
JB eskipcr ; no: continue
escret RET ; "
eclrlin CS: ; Clear line
MOV.B CH,txwinx2 ; col count-1
DEC.B CH
JMP eclrln ; "clear line
escrdma PUSH DS ; Prepare screen DMA
POP ES ; DS -> ES
MOV DI,#dmabuf ; ptr to DMA buffer
CLD
XOR.B AH,AH ; AL -> CX = pos in line
MOV CX,AX
MOV SI,AX ; AL*2 -> SI
ADD SI,SI
CS:
MOV AL,txwinx2 ; column count * 2
ADD AX,AX
MOV BP,AX ; -> BP
SUB BP,SI ; - horizontal offset
MUL.B scrrow ; line length * current row
ADD BP,AX ; + row position
RET ; "
escrdma2INC.B AL ; column + 1
CALL escrdma ; prepare DMA
DEC CX ; char cnt-1
JMP.b edlcrit ; "do it
edmalin CS: ; Display line
MOV AL,txwinx2 ; column count
CALL escrdma ; prepare screen DMA
DEC CX ; count down
MOV.B AH,horscr ; horizontal scroll
OR.B AH,AH ; =0 ?
JZ edlcrit
edl1 CALL esetatt ; set attribute
MOV.B AL,[BX] ; get char
INC BX ; next char
CMP BX,txend ; text end ?
JNB edletx ; :yes
CALL eskipcr ; skip CR
CMP AL,#$0A ; LF ?
JZ edletx ; :yes
DEC.B AH ; another char ?
JNZ edl1 ; :yes
edlcrit CMP.B attchg,#$00 ; critical line
JZ edlatt ; :no
PUSH BX ; save pos
CALL esrcend ; search end of line
MOV AX,lnpos ; pos in line = end of line ?
CMP AX,BX
JBE edllim ; :ok
MOV BX,AX ; limit it
edllim INC BX
MOV eolpos,BX ; pos: end of line
POP BX ; restore pos
CALL esetatt ; set attribute
CMP BX,eolpos ; = pos EOL ?
JNB edletx ; :yes
edlatt CALL esetatt ; set attribute
CMP BX,eolpos ; end of line ?
JZ edletx ; yes: end
MOV.B AL,[BX] ; get char
INC BX ; next
CMP BX,txend ; text end ?
JA edletx ; yes: end
CMP AL,#$0D ; CR ?
JZ edllp ; yes: forget it
CMP AL,#$20 ; control char ?
JNB edlnrm ; :no, normal
ADD AL,#$40 ; convert to normal
PUSH AX ; save it
CALL etogatt ; set attribute
POP AX ; restore
edlnrm MOV.B AH,attcur ; current attribute
STOS ; store in buffer
LOOP edlatt ; :another char
edllp MOV.B AL,[BX] ; get char
INC BX
CMP BX,txend ; text end ?
JA edletx ; :yes
CMP AL,#$0A ; LF ?
JNZ edllp ; :not yet
edletx INC CX ; one more byte
MOV AL,#$20 ; fill up with spaces
MOV.B AH,attcur ; current attribute
REPZ
STOS
CMP.B disflg,#$00 ; display it ?
JZ edlret ; :no
MOV CX,SI ; byte number / 2 -> words
SHR CX,1
MOV DI,BP ; destination ofs
MOV ES,scrseg ; screen segment
MOV SI,#dmabuf ; buffer addr
CMP.B scrbad,#$FF
JZ edlbad ; :yes
REPZ ; just move it into screen memory
MOVS
edlret RET ; '
edlbad MOV DX,#$03DA ; test port
edlblp LODS ; get word
MOV BP,AX ; save it
edlbw1 IN AL,DX ; get status
RCR.B AL,1
JB edlbw1 ; :wait
CLI ; no INT allowed
edlbw2 IN AL,DX ; get status
RCR.B AL,1
JNB edlbw2 ; :wait
XCHG AX,BP ; restore char
STOS ; store it
STI ; clear interrupt
LOOP edlblp ; :another char
edlbret RET ; "
esetatt CMP.B bkhide,#$FF ; Set attribute
JZ esetnrm ; :block hidden
CMP.B attchg,#$FF ; block beg/end in this line ?
JZ esetblk ; :no
CMP BX,bkbeg ; block begin ?
JB esetnrm ; :no, normal
CMP BX,bkend ; block end ?
JNB esetnrm ; above: normal
JMP.b eset2 ; 'in block !
esetblk CMP BX,bkbegl ; = pos block beg in buffer ?
JB esetnrm ; :no, normal
CMP BX,bkendl ; > pos block end in buffer ?
JNB esetnrm ; yes: normal
eset2 CMP.B disflg,#$00 ; display it ?
JZ edlbret ; :no
JMP vidattr2 ; 'Attribute #2
esetnrm CMP.B disflg,#$00 ; display it ?
JZ edlbret ; :no
JMP knrmvid ; "NormVideo
esetlow CMP.B disflg,#$00 ; display it ?
JZ edlbret ; :no
JMP klowvid ; "LowVideo
etogatt CMP.B curatt,#$01 ; low set ?
JZ esetnrm ; yes:NormVideo
JMP esetlow ; "LowVideo
eclrln CMP.B disflg,#$00 ; Clear line
JZ edlbret
PUSH AX ; save
PUSH DX
MOV.B AL,CH ; line number+1
INC.B AL
CALL escrdma ; prepare screen DMA
DEC CX ; length-1
CALL edletx ; erase to end of line
POP DX ; restore
POP AX
RET ; "
escrolupMOV BX,#$0001 ; Scroll up
CALL esetcur ; set cursor pos: top line
JMP xdelline ; "DelLine
erstlin MOV BX,edpos ; ^QL:restore line
MOV DX,#$0000 ; clear block pos in buf:
MOV bkbegl,DX ; block beg
MOV bkendl,DX ; block end
MOV CH,#$7F ; up to 127 chars
MOV SI,#line ; dest: line buffer
MOV.B attflg,#$00 ; flag: no block beg / end
erllp MOV.B AL,[BX] ; get char
CMP BX,bkbeg ; block beg ?
JNZ erlnbeg ; :no
MOV bkbegl,SI ; pos block beg in line
OR.B attflg,#$01 ; block beg in this line
erlnbeg CMP BX,bkend ; block end ?
JNZ erlnend ; :no
MOV bkendl,SI ; pos block end in line
OR.B attflg,#$02 ; block end in this line
erlnend CMP AL,#$0D ; CR ?
JNZ erlnext ; no:next char
MOV.B [SI],#$20 ; store a space
INC SI ; next
DEC.B CH ; count down
JZ elonglin ; :line too long
INC BX ; next char
CMP BX,txend ; end of text ?
JNB erllp ; :no, continue
erlend CMP BX,bkend ; block active in this line ?
JNB erlbeg ; :no
PUSH BX ; save pos
MOV BX,#$FFFF ; flag: block ends after
MOV bkendl,BX ; this line
POP BX
erlbeg CMP BX,bkbeg ; block beg ?
JNB erllen
MOV BX,#$FFFF ; flag: block starts before this
MOV bkbegl,BX ; line
erllen MOV AL,#$7F ; test line length
SUB.B AL,CH
MOV oldlen,AL ; store old length
erlpad MOV.B [SI],#$20 ; fill up with spaces
INC SI
DEC.B CH
JNZ erlpad ; :again
MOV BX,lnpos ; pos in buffer
CALL erepos ; reposition in line
CMP.B scrfl2,#$00 ; small move only ?
MOV.B scrfl2,#$FF ; reset flag
JZ erldisp ; :yes
JMP erewrlin ; 'redisplay line
erldisp MOV AL,dislin ; redisplay from ...
DEC.B AL
CMP.B AL,phrow ; = phys row ?
JNB eseret ; above: ret
JMP erewrlin ; 'rewrite line
erlnext CMP AL,#$0A ; LF ?
JZ erlend ; yes: end it
MOV.B [SI],AL ; store char
INC SI ; go to next
DEC.B CH ; too much ?
JNZ erletx ; :no
JMP.b elonglin ; 'error: long line
erletx INC BX ; end of text ?
CMP BX,txend
JNB erlend ; yes: end it
JMP erllp ; "continue
elonglinCALL eclrstat ; Insert line break - line too long
CALL ewriterr ; write message
B "Line too long - CR inserted",$00
CALL waitesc ; wait for ESC
MOV BX,#$007D ; offset
CALL elb2 ; insert line break
CALL erstlin ; restore line
JMP erewrall ; "rewrite all
eclrstatCALL edodispl ; Clear status line
eclrs2 MOV.B statobs,#$00 ; status line destroyes
MOV BX,#$0000 ; set cursor pos
CALL esetcur
CALL knrmvid ; NormVideo
CALL eclrlin ; clear line
MOV BX,#$0000 ; set cursor pos
CALL esetcur
JMP klowvid ; "LowVideo
esrcend MOV AL,#$20 ; Search end of input line
MOV BX,#lineend1 ; end pos
MOV DX,#line0 ; start pos
eselp CMP.B AL,[BX] ; space ?
JNZ eseret ; no: end found
DEC BX ; search backwards
CMP BX,DX ; beg reached ?
JNZ eselp ; no:continue
eseret RET ; "
elimpos MOV DX,#lineend ; BX>end of line ?
CMP BX,DX
JNB eseret ; :ret
MOV DX,CX
JMP emin ; "min(BX,DX)->BX
eflush CALL esrcend
INC BX ; search end of line + 1
efl2 MOV CX,BX ; end position
MOV BX,bkbegl ; pos block beg in buf
CALL elimpos ; limit to end position
MOV bkbegl,BX
MOV BX,bkendl ; pos block beg in buf
CALL elimpos ; limit to end position
MOV bkendl,BX
MOV BX,CX ; restore end position
INC BX ; +1
MOV DX,#line ; - beg position
SUB BX,DX
PUSH BX ; -> length
MOV AL,oldlen ; old length - new length
SUB.B AL,BL
MOV.B CL,AL ; -> count
MOV CH,#$00
JNB eflpos ; :ok
MOV CH,#$FF ; negative
eflpos MOV BX,edpos ; current pos
JZ eflsame ; :no size change
CALL echgsize ; expand / shrink text
eflsame POP CX ; restore length
MOV SI,edpos ; destination pos
MOV BX,#line ; buffer pos
MOV.B CH,CL ; count
OR.B CH,CH ; test it
JZ eflend ; 0:nothing to do
efllp MOV.B AL,[BX] ; get char
CMP BX,bkbegl ; = block beg ?
JNZ eflnbeg ; :no
MOV bkbeg,SI ; set block beg
eflnbeg CMP BX,bkendl ; = block end ?
JNZ eflnend ; :no
MOV bkend,SI ; set block end
eflnend MOV.B [SI],AL ; store char
INC BX ; next char
INC SI
DEC.B CH ; another ?
JNZ efllp ; :yes
DEC SI ; go back one char
eflend MOV.B [SI],#$0D ; store CR at end
RET ; "
etstmem MOV BX,txmemend ; Test if memory full
SUB BX,DX ; end text space - new text end
JB etsterr ; :error
MOV CX,#$00FE ; 254 bytes left ?
SUB BX,CX
JNB etstret ; or more: ret
ADD BX,CX ; restore
PUSH BX
CALL eclrs2 ; clear status line
POP BX ; number free bytes
MOV CH,#$00
CALL ewriterr ; write message
B "WARNING: ",$00
CALL edcvt ; display number
CALL ewriterr ; write message
B " byte(s) left",$00
CALL waitesc ; wait for ESC
etstret RET ; '
etsterr CALL eclrstat ; clear status line
CALL ewriterr ; write message
B "ERROR: Out of space",$00
CALL waitesc ; wait for ESC
JMP edmain ; "return to main loop
etstint CMP AL,#$15 ; Test for interrupt
JNZ etstret ; :not ^U - ret
CALL eclrstat ; clear status line
CALL ewriterr ; write message
B "*** INTERRUPTED",$00
CALL waitesc ; wait for ESC
JMP edmain ; "go to main loop
echgsizePUSH BX ; Shrink or expand text
PUSH CX ; save pos,cnt
JB edinsert ; :insert, negative count
JMP edshrink ; 'delete, positive count
edinsertMOV DX,txend ; save text end
PUSH DX
PUSH DX
XCHG DX,BX ; text end - pos
SUB BX,DX
MOV BP,BX ; -> BP
POP BX ; text end
PUSH BP ; save (text end - pos)
SUB BX,CX ; text end - count
JNB etsterr ; :error - out of space
MOV DX,BX ; -> BX
PUSH DX ; save new text end
CALL etstmem ; test space free
POP DX ; restore new text end
POP CX ; restore count+1
INC CX
POP BX ; text end
MOV txend,DX ; set new text end
OR CX,CX ; test count
JZ edins2 ; 0:no move to do
CALL movebkb ; move block backwards
edins2 POP CX ; restore difference
POP BX ; pos
XCHG BX,DX
MOV BX,bkbeg ; update pointers:
CALL edupdate ; block beg
MOV bkbeg,BX
MOV BX,bkend
CALL edupdate ; block end
MOV bkend,BX
MOV BX,disbeg
CALL edupdate ; display beg
MOV disbeg,BX
MOV BX,edpos
CALL edupdate ; pos current line
MOV edpos,BX
MOV BX,posfifo
CALL edupdate ; position FIFO
MOV posfifo,BX
MOV BX,qppos
CALL edupdate ; pos FIFO 2
MOV qppos,BX
MOV BX,srend
CALL edupdate ; search limit end
MOV srend,BX
MOV BX,srbeg
CALL edupdate ; search limit beg
MOV srbeg,BX
RET ; "
edupdateCMP BX,DX ; update pointer: >pos of change ?
JBE edupret ; below/equal: no change
SUB BX,CX ; change it
edupret RET ; "
edshrinkPUSH BX ; Shrink text: save pos
ADD BX,CX ; + count
PUSH BX ; save
MOV DX,txend ; text end+1
INC DX
XCHG DX,BX ; -pos-count
SUB BX,DX
MOV CX,BX ; -> move count
POP BX ; restore source
POP DX ; restore dest
OR CX,CX ; test counter
JZ edshnil ; :nothing to move
CALL movebk ; move it - delete
DEC DX ; end pos-1
edshnil MOV txend,DX ; set new end pos
JMP edins2 ; "update pointers
etstbk PUSH BX ; Test block markers in line buffer
MOV DX,bkbegl ; pos block beg in buffer
CALL emin ; min(BX,DX)->BX
TEST.B attflg,#$01 ; block beg in this line ?
JZ etbnbeg ; :no
MOV bkbegl,BX ; set it
etbnbeg POP BX ; restore
TEST.B attflg,#$02 ; block end in this line ?
JZ etbnend ; :no
MOV DX,bkendl ; update it
CALL emin
MOV bkendl,BX
etbnend RET ; "
erepos MOV DX,#line ; Reposition in current line
CS: ; ptr line beg
MOV AL,txwinx2 ; column count - 1 -> CL
DEC.B AL
MOV.B CL,AL
SUB BX,DX ; relative pos -> BX
MOV.B AL,BL
SUB.B AL,horscr ; pos < hor scroll ?
JB erephscr ; yes: underflow
CMP.B AL,CL ; outside displayed window ?
JB erepcol ; no: ok
SUB.B AL,CL ; calc diff+1 (overflow)
INC.B AL
ADD.B AL,horscr ; add to horizontal scroll
MOV horscr,AL
CS:
MOV AL,txwinx2 ; col cnt-2 -> phys col
DEC.B AL
DEC.B AL
MOV phcol,AL
JMP erewrall ; 'redisplay all
erepcol MOV phcol,AL ; store phys col
erepret RET ; '
erephscrADD.B AL,horscr ; add to horizontal scroll
MOV horscr,AL
MOV.B phcol,#$00 ; phys col 0
JMP erewrall ; "rewrite all
eseldispCMP.B disflg,#$00 ; Do selective rewrite
JZ erepret
MOV BX,disbeg ; display beg
MOV AX,txbeg ; text beg
CMP AX,BX ; the same ?
JBE esd2 ; :yes
MOV disbeg,AX ; store to display beg
MOV BX,AX
esd2 MOV CX,#$0001 ; line cnt
MOV DX,edpos ; current pos
CMP BX,DX ; = display beg ?
JNZ esd3 ; :no
JMP esd9 ; 'yes: done
esd3 JB esddn ; below: search down
esd4 MOV DX,edpos ; current pos
CMP BX,DX ; = ?
JZ esd5
CALL elinup ; go up one line
INC CX ; count it
JMP esd4 ; '
esd5 MOV disbeg,BX ; store display beg
MOV.B phrow,#$01 ; phys row
MOV.B scrfl2,#$FF ; set flag
XOR AX,AX
CS:
MOV AL,txwiny2 ; row cnt - 1
SUB AX,#$0001
CMP AX,CX ; = line ?
JB esdredsp ; below: ok
DEC CX ; go back one line
MOV BX,#$0001 ; set cursor pos:
CALL esetcur ; below status line
DEC CX ; test it
PUSHF
INC CX ; restore
esdinsl CALL xinsline ; InsLine
LOOP esdinsl ; :again
POPF ; one only ?
JNZ esdredsp ; no: do redisplay
RET ; '
esdredspJMP eredall ; 'redisplay all
esddn CMP BX,edpos ; >= current pos
JNB esd6 ; :not yet
CALL elindn ; go down one line
INC CX ; count
JMP esddn ; '
esd6 OR.B CH,CH ; clear count
JNZ esd10 ; :much
CS:
MOV AL,txwiny2 ; line count-1 -> DL
DEC.B AL
MOV.B DL,AL
MOV.B AL,CL ; counter-line cnt+1 -> DH
SUB.B AL,DL
MOV.B DH,AL
INC.B DH
JB esd9 ; :ok
DEC.B DH ; test it
JNZ esd7
CMP.B scrfl1,#$FF ; test flag
JNZ esd7 ; :normal
JMP.b esd11 ; 'keep it short
NOP
esd7 INC.B DH
SUB.B AL,DL
JNB esd10
MOV AL,dislin ; redisplay from
SUB.B AL,DH
JBE esd10
MOV dislin,AL ; set it
MOV BX,disbeg ; display beg
MOV.B CH,DH
PUSH DX
esdscrl CALL elindn ; go down one line
PUSH BX ; save pos
CALL escrolup ; scroll up
POP BX ; restore
DEC.B CH ; count down
JNZ esdscrl ; :another
MOV disbeg,BX ; store display beg
POP DX
esd8 DEC.B DL
MOV.B phrow,DL ; phys row
RET ; '
esd9 MOV.B phrow,CL
RET ; '
esd10 MOV BX,disbeg
DEC CX
CS: ; line cnt - 3 -> DL
MOV AL,txwiny2
SUB AL,#$03
MOV.B DL,AL
MOV.B AL,CL
SUB.B AL,DL
MOV.B CL,AL
JNB esddn2
DEC.B CH
esddn2 CALL elindn ; go down one line
LOOP esddn2 ; :again
MOV disbeg,BX ; store display beg
CALL eredall ; redisplay all
MOV.B scrfl2,#$FF ; set flag: long update
JMP eseldisp ; 'do it again
esd11 CALL esd8 ; set phys row
MOV AL,dislin ; redisplay from
CS:
CMP.B AL,txwiny2 ; = line cnt ?
JZ esd12 ; :yes
DEC.B AL
JZ esd12
MOV dislin,AL ; set: redisplay from
esd12 MOV BX,disbeg ; display beg
CALL elindn ; go down one line
MOV disbeg,BX ; -> new display beg
CALL escrolup ; scroll up
CS:
MOV AL,txwiny2 ; line cnt - 1
DEC.B AL
JMP edsplin ; "redisplay one line
etstsep MOV SI,sepptr ; Char = separator ?
etslp CS: ; get ptr to table
MOV.B AL,[SI] ; get char from table
OR.B AL,AL ; table end ?
JZ etsno ; :yes
CMP.B AL,[BX] ; = text char ?
JZ etsyes ; :yes
INC SI ; try next char
JMP etslp ; '
etsyes STC ; found it !
etsno MOV DX,SI ; DX=pos in table
RET ; "
LAHF ; redisplay current line
PUSH AX ; save
MOV AL,phrow ; phys row
CMP.B AL,dislin ; >= redisplay from ?
JNB erc2 ; :ok
MOV dislin,AL ; redisplay current line, too !
erc2 POP AX ; restore
SAHF
RET ; "
eredall MOV.B dislin,#$01 ; set flag: redisplay all
RET ; "
eblkupdtXCHG BX,DX ; Update blocks
TEST.B attflg,#$01 ; block beg in this line ?
JZ ebunobk ; :no
MOV BX,bkbegl ; pos block in buffer
CMP BX,DX ; < BX ?
JB ebunobk ; :ok
ADD BX,CX ; add count
MOV bkbegl,BX ; -> update
ebunobk TEST.B attflg,#$02 ; block end in this line ?
JZ ebunochg ; :no
MOV BX,bkendl ; pos block end in buffer
CMP BX,DX ; < BX ?
JB ebunochg ; :ok
ADD BX,CX ; add count
MOV bkendl,BX ; -> update
ebunochgXCHG BX,DX ; restore BX
RET ; "
edelch PUSH BX ; delete char: save pos
MOV CX,#$FFFF ; 1 char back
CALL eblkupdt ; update blocks
XCHG BX,DX ; pos -> DX
MOV BX,#lineend1 ; buffer end
SUB BX,DX ; calculate count
JZ edelcnil ; :nothing to move
MOV CX,BX ; count -> CX
MOV BX,DX ; pos -> BX
INC BX ; source: +1
CALL movebk ; move it
edelcnilMOV BX,#lineend1 ; store a space at the end
MOV.B [BX],#$20
POP BX ; restore pos
RET ; "
emin CMP BX,DX ; min(BX,DX) -> BX
JB emin2 ; max(BX,DX) -> DX
XCHG BX,DX
emin2 RET ; "
eredlin CALL eposcur ; redisplay line: set cursor pos
CS:
MOV AL,txwinx2 ; col cnt - 1
DEC.B AL
SUB.B AL,phcol ; - phys col
MOV BX,lnpos ; pos in line buffer
MOV.B attchg,#$FF ; attribute change in this line
CALL escrdma2 ; redisplay this line
MOV.B attchg,#$00 ; clear attribute flag
RET ; "
elindn MOV BP,CX ; go down one line
MOV CX,txend ; text end
MOV DI,BX ; current pos
SUB CX,DI ; text end-pos -> count
JBE eld2 ; :too much
MOV AX,DS ; DS -> ES
MOV ES,AX
CLD ; search LF
MOV AL,#$0A
REPNZ
SCAS.B
JNZ eld2 ; :not found
MOV CX,BP ; restore CX
MOV BX,DI ; pos of line break
RET ; '
eld2 MOV CX,BP ; restore CX
STC ; flag: not done
RET ; "
elinup MOV BP,BX ; go up one line
MOV AL,#$0A ; search LF
MOV DI,txbeg ; text beg = limit
DEC BX ; go back one char
elulp DEC BX ; go back
CMP BX,DI ; = end ?
JBE eluend ; :yes
CMP.B [BX],AL ; LF ?
JNZ elulp ; :no, try again
INC BX ; go to beg of line
eluend JB eluerr ; :not found
RET ; '
eluerr MOV BX,BP ; restore pos
RET ; "
einsch PUSH BX ; insert char in line
MOV CX,#$0001 ; 1 char
CALL eblkupdt ; update blocks
MOV DX,#lineend1 ; buffer end
XCHG BX,DX ; calc count
SUB BX,DX
DEC BX
MOV CX,BX
MOV DX,#lineend0 ; buffer end
MOV BX,DX
DEC BX
MOV.B AL,CL ; test length
OR.B AL,CH ; did they translate this automatically
JZ einscnil ; from Z80-code ??!!
PUSH DX ; save pos
CALL movebkb ; insert
POP BX ; restore pos
MOV.B [BX],#$20 ; store a space
einscnilPOP BX ; restore pos
RET ; "
ewritposCALL esetcur ; Write string + pos cursor
ewrits CALL klowvid ; LowVideo
JMP prints ; "write string
ewriterrCALL vidattr3 ; Attribute #3
JMP prints ; "write string: error message
ewritch CMP.B disflg,#$FF ; editing status
JNZ ewritret ; :ret
JMP conput ; "write it
ekbdstatPUSH AX ; Get KBD stat
PUSH BX
PUSH CX
PUSH DX
DEC SP
CALL [vkbdstat] ; do it
POP DX
POP CX
POP BX
POP AX
ewritretRET ; "
esetcur CMP.B disflg,#$00 ; set cursor pos
JZ escrt
MOV.B scrrow,BL ; line
XCHG BX,DX
CALL setcpos ; set it
XCHG BX,DX ; restore
escrt RET ; "
; first command table (installed with TINST)
ecmd1 B $01,$0D,$02,$1B,$4B,$01,$FF,$02,$1B,$4D,$02,$1B,$73,$02
B $1B,$74,$02,$1B,$48,$02,$1B,$50,$01,$FF,$01,$FF,$02,$1B
B $49,$02,$1B,$51,$02,$1B,$47,$02,$1B,$4F,$02,$1B,$77,$02
B $1B,$75,$02,$1B,$84,$02,$1B,$76,$01,$FF,$01,$FF,$01,$FF
B $02,$1B,$52,$01,$FF,$01,$FF,$01,$FF,$01,$FF,$02,$1B,$53
B $01,$08,$01,$FF,$02,$1B,$41,$02,$1B,$42,$01,$FF,$01,$FF
B $01,$FF,$01,$FF,$01,$FF,$01,$FF,$01,$FF,$01,$FF,$01,$FF
B $01,$FF,$01,$FF,$01,$FF,$01,$FF,$01,$FF,$01,$FF,$00,$00
B $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
B $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
B $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
B $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
B $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
B $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
B $00,$00,$00,$00 ; "
; second command line (preinstalled)
ecmd2 B $01,$0D,$01,$13,$01,$08,$01,$04,$01,$01,$01,$06,$01,$05
B $01,$18,$01,$17,$01,$1A,$01,$12,$01,$03,$02,$11,$13,$02
B $11,$04,$02,$11,$05,$02,$11,$18,$02,$11,$12,$02,$11,$03
B $02,$11,$02,$02,$11,$0B,$02,$11,$10,$01,$16,$01,$0E,$01
B $19,$02,$11,$19,$01,$14,$01,$07,$01,$7F,$01,$FF,$02,$0B
B $02,$02,$0B,$0B,$02,$0B,$14,$02,$0B,$08,$02,$0B,$03,$02
B $0B,$16,$02,$0B,$19,$02,$0B,$12,$02,$0B,$17,$02,$0B,$04
B $01,$09,$02,$11,$09,$02,$11,$0C,$02,$11,$06,$02,$11,$01
B $01,$0C,$01,$10,$00 ; "
eseptab B "<>,[].*+-/$:=(){}^#\'"
esepspc B " ",$00 ; "
; Jump table for editor commands
; Labels ending with 2 have the MSB set. On these commands
; the text is marked as changed. The commands are in the order
; given in the chapter "Editing Command Installation" of the
; Turbo manual.
ejmptab W ecr,eleft,eleft,eright,ewrdlft,ewrdrt,eup,edn,escrup,escrdn
W epagup,epagdn,ejlnbeg,ejlnend,epagtop,epagbot,etxtbeg
W ejtxend,ejbkbeg,ejbkend,elstpos,etogovr,elinins2,edellin2
W edeleol2,edelwrd2,edelrt2,edellft2,edellft2,emarkbeg,emarkend
W emrkwrd,ekh,eblkcpy2,eblkmov2,eblkdel2,eblkrd2,ekw,ekd
W etab2,etogind,erstlin2,e ; "
; *** Compiler ***
turbo MOV spsav,SP ; save stack pointer
CMP.B cpmode,#$02 ; to COM or CHN ?
JB turmem ; :no
CMP.B txcomp,#$00 ; is it already compiled ?
JZ turmem ; :no
CMP.B cdinval,#$00 ; is it invalid ?
JNZ turmem ; :yes
CALL opendest ; open dest file
CALL cvmemdsk ; change to disk code
JMP.b turret ; 'no error
turmem CALL inittur ; init variables
CMP.B cpmode,#$00 ; to memory ?
JNZ turfil ; :no
CALL copyrt ; make space, copy run-time lib
JMP.b turmem2 ; '
turfil CMP.B cpmode,#$02 ; COM / CHN ?
JB turmem2 ; :no
CALL opendest ; open dest file
turmem2 CALL compile ; compile program
CMP.B cpmode,#$00 ; memory ?
JNZ turfil2 ; :no
MOV.B txcomp,#$FF ; set flag: compiled
JMP.b turret ; '
turfil2 CMP.B cpmode,#$02 ; COM / CHN ?
JB turret ; :no
CALL codflush ; flush code buffer
turret XOR.B AL,AL
JMP errexit ; "return: no error
inittur MOV.B txcomp,#$00 ; Init variables: not compiled
MOV.B cdinval,#$00 ; code invalid
MOV BX,txend ; store ^Z at source end
MOV.B [BX],#$1A
MOV CL,#$04 ; (size/16)+1+DS
SHR BX,CL
INC BX
MOV AX,DS
ADD AX,BX
MOV destseg,AX ; -> code dest segment
MOV AX,freemem ; free memory - size
SUB AX,BX
MOV minstksz,AX ; -> minimum stack size
MOV DI,stackpt ; stack pointer - 1024
SUB DI,#$0400
MOV ptcend,DI ; -> end of patch list space
SUB DI,#$0400 ; - 1024
MOV ptctop,DI ; -> top of patch list
MOV ptcbeg,DI ; -> beg of patch list
DEC DI ; destination for var table
MOV SI,#stdvars ; copy std vars into symbol table
MOV CX,SS ; SS -> ES
MOV ES,CX
PUSH DS ; save DS
MOV CX,CS ; CS -> DS
MOV DS,CX
MOV CX,#$036A ; count
STD
REPZ
MOVS.B
POP DS ; restore DS
INC DI
MOV symtop,DI ; -> top of symtab
MOV symtop2,DI
MOV fence,DI ; -> current var fence
MOV BX,#varpatch ; patch in ptrs to type defs
MOV CX,#$0019
itpatch CS:
MOV BP,[BX] ; pointer
ADD [BP_DI],DI ; + offset
INC BX
INC BX ; next one
LOOP itpatch ; :another one
MOV AX,txbeg ; text beg
MOV srcptr,AX ; -> src ptr
MOV DI,#pnbuf ; line buffer
MOV chptr,DI ; -> char ptr
MOV.B [DI],#$00 ; clear line
XOR AX,AX ; clear vars:
MOV recnum,AL ; record nesting
MOV reccnt,AL
MOV lexnest,AL ; lexical nesting
MOV srcend,AL ; not end of source
MOV flgpshax,AL ; no PUSH AX
MOV flgpshes,AL ; no PUSH ES
MOV flgpshdi,AL ; no PUSH DI
MOV usrint,AL ; user int not used
MOV ovrcnt,AL ; no overlays used
MOV inclflg,AL ; no include
MOV cdptr,AX ; code ptr
MOV cdbufpt,AX ; code pos in buffer
MOV cdbegpt,AX ; beg of code buffer
MOV cdfoff,AX ; current file offset
MOV cdfoff1,AX
MOV lincnt,AX ; line counter
MOV cinpsize,AX ; std in buffer size
MOV coutsize,AX ; std out buffer size
MOV cmaxfil,#$0010 ; max 16 open files
MOV.B scalcnt,#$0D ; counter for scalar types
MOV direct,#$00ED ; set compiler directive
CALL disline ; display line, test brk
MOV pc,#$2D7C ; PC: start of code
MOV dc,#$0240 ; DC: start of file list
CMP.B cpmode,#$02 ; COM ?
JZ itcom ; :yes
JA itret ; CHN:ret
MOV cdptr,#$2D7C ; code pos of buffer
RET ; '
itcom MOV cdptr,#$2C7C ; code ptr
MOV cdbufpt,#$2C7C ; code pos of buffer
itret RET ; "
copyrt MOV AX,#start ; Test for memory overflow
MOV cdptr,AX
MOV CL,#$04 ; (../16)+1
SHR AX,CL
INC AX
ADD AX,destseg ; + dest segment
MOV BX,symtop ; (symtop/16)+SS
SHR BX,CL
MOV CX,SS
ADD BX,CX
CMP AX,BX ; compare them
CALL errnb ; 99:compiler overflow
B $63
XOR SI,SI ; clear addr
XOR DI,DI
MOV ES,destseg ; dest seg
PUSH DS ; save DS
PUSH CS ; CS -> DS
POP DS
MOV CX,#start ; copy runtime library into code
CLD
REPZ
MOVS.B
POP DS ; restore DS
RET ; "
opendestMOV AH,#$3C ; Open dest file
XOR CX,CX ; no attribute
MOV DX,#destpn ; dest name ptr
PUSH DS ; DS -> ES
POP ES
CALL dos ; create it
CALL errb
B $C9
MOV dstfile,AX ; store file handle
CMP.B cpmode,#$02 ; COM file ?
JNZ odret ; :no
MOV BX,AX ; file handle
MOV AH,#$40 ; write byte block
MOV CX,#$2C7C ; size of runtime code
MOV DX,#$0100 ; offset
PUSH DS ; save DS
PUSH CS ; CS -> DS
POP DS
CALL dos ; write runtime library
POP DS ; restore DS
JB oderr ; :error
CMP AX,CX ; length = expected ?
JNZ oderr ; no: error
odret RET ; '
oderr CALL err ; C9:dest file not written
B $C9 ; "
cvmemdskPUSH DS ; Change mem code to disk code
PUSH DS ; save DS
POP ES ; DS -> ES
MOV DS,destseg ; code dest segment
MOV SI,#memparm ; ptr to memory parameters
MOV DI,#pnbuf ; dest: buffer
MOV CX,#$0007 ; 7 words
CLD
REPZ
MOVS ; move it into buffer
PUSH DS ; DS -> ES
POP ES
POP DS ; restore DS
MOV DI,#memparm ; ptr to memory parameters
ES:
AND [DI],#$FFFE ; clear direct mode
MOV AX,codesize ; code size > min CS size ?
CMP AX,mincssz
JNB cvdcs ; :ok
MOV AX,mincssz ; at least min CS size
cvdcs ES:
MOV [DI]$06,AX ; store CS size
MOV AX,datasize ; data size > min DS size ?
CMP AX,mindssz
JNB cvdds ; :ok
MOV AX,mindssz ; at least min DS size
cvdds ES:
MOV [DI]$08,AX ; store DS size
MOV AX,minhpsz ; min free heap
ES:
MOV [DI]$0A,AX ; -> heap, stack size
CMP AX,maxhpsz ; > max free heap ?
JNB cvdhp ; :yes
MOV AX,maxhpsz ; take that size
cvdhp ES:
MOV [DI]$0C,AX ; store max heap,stack size
MOV AH,#$40 ; Write code to disk
MOV BX,dstfile ; dest file handle
MOV CX,cdptr ; code end
MOV DX,#start ; offset: end of run-time
SUB CX,DX ; calc length
PUSH DS ; save DS
PUSH ES ; ES -> DS
POP DS
CALL dos ; write it
POP DS ; restore DS
PUSH AX ; save results
PUSH CX
PUSHF
MOV SI,#pnbuf ; restore memory code
MOV DI,#memparm
MOV ES,destseg
MOV CX,#$0007 ; 7 words
CLD
REPZ ; do it
MOVS
POPF ; restore results
POP CX
POP AX
JB cvderr ; :error
CMP AX,CX ; length = expected ?
JNZ cvderr ; :no
RET ; '
cvderr CALL err ; C9:file error
B $C9 ; "
compile CALL skip ; Compile program: get word
CALL ctoken ; PROGRAM ?
W tkprog
JNZ comnoprm ; :no
CALL dummysym ; read dummy symbol
CALL cbrack1 ; ( ?
JNZ comprmen ; :no
comprmlpCALL dummysym ; read dummy symbol
CALL ccomma ; , ?
JZ comprmlp ; yes: repeat
CALL ebrack2 ; ! )
comprmenCALL esemi ; !
comnoprmMOV AX,#initmem ; * CALL initmem
CALL ecall ; emit CALL
XOR AX,AX ; set mode flag
CMP.B cpmode,#$00 ; to memory ?
JNZ comcom ; :no
OR AX,#$0001 ; flag: direct mode
comcom TEST direct,#$0080 ; device checking ?
JZ comdevck ; :no
OR AX,#$0002 ; set flag
comdevckTEST direct,#$0008 ; test ^C and ^S ?
JZ comctcs ; :no
OR AX,#$0004 ; set flag
comctcs PUSH AX ; save flag
PUSH cdptr ; save code ptr
CALL eword ; emit mode flag
MOV AX,CS ; emit Turbo CS
CALL eword
MOV AX,DS ; emit Turbo DS
CALL eword
PUSH cdptr ; save code ptr
CALL eword ; emit 4 dummy words:
CALL eword ; memory size is not yet known
CALL eword
CALL eword
MOV AX,cmaxfil ; max number of open files
ADD dc,AX ; add to data counter
ADD dc,AX ; (file handle list)
CALL eword ; emit file count
MOV AX,cinpsize ; std in buf size
ADD dc,AX ; add to DC
CALL eword ; emit
MOV AX,coutsize ; std out buf size
ADD dc,AX ; add to DC
CALL eword ; emit
CALL ecode ; * MOV BP,SP
B $02,$8B,$EC
MOV varspc,#$0000 ; clear var space used
CALL emcrunch ; emit overlay uncrunch
CALL defpart ; declaration part
CALL progpart ; program part
MOV DI,chptr
CMP.B [DI],#$2E
CALL errnz
B $0A
CALL ecode ; * XOR AX,AX
B $02,$33,$C0
MOV AX,#progend ; * CALL progend
CALL ecall
CALL ptcrunch ; end overlay crunch list
MOV AX,pc ; (PC+15)/16
ADD AX,#$000F
MOV CL,#$04
SHR AX,CL
MOV codesize,AX ; -> CS size
MOV AX,dc ; (DC+15)/16
ADD AX,#$000F
MOV CL,#$04
SHR AX,CL
MOV datasize,AX ; -> DS size
POP BX ; code ptr: memory sizes
MOV AX,codesize ; CS size > min CS size ?
CMP AX,mincssz
JNB comcs ; :ok
MOV AX,mincssz ; at least min CS size
comcs CALL patch ; patch in
INC BX
INC BX
MOV AX,datasize ; DS size > min DS size ?
CMP AX,mindssz
JNB comds ; :ok
MOV AX,mindssz ; at least min DS size
comds CALL patch ; patch in
INC BX
INC BX
MOV AX,minhpsz ; min free heap
CMP.B cpmode,#$02 ; COM / CHN ?
JNB comfile ; :yes
MOV AX,minstksz ; min stack size
SUB AX,codesize ; - code size - data size
SUB AX,datasize
MOV minstksz,AX ; -> min stack size
comfile PUSH AX ; save it
CALL patch ; patch in
POP AX ; restore
INC BX
INC BX
CMP AX,maxhpsz ; > max free heap size ?
JNB comhp ; :ok
MOV AX,maxhpsz ; at least max free heap
comhp CALL patch ; patch in
POP BX ; code ptr mode flag
POP AX ; flag
CMP.B usrint,#$00 ; user interrupt used ?
JZ comnobrk ; :no
OR AX,#$0008 ; set flag: user interrupt
comnobrkCALL patch ; patch in
JMP ptcflush ; "patch code in file
dummysymPUSH symtop ; read dummy symbol
CALL rdsym ; read symbol
POP symtop ; restore symtob: forget symbol read
RET ; "
chkovrflPUSH AX ; Test for overflow
PUSH BX
PUSH CX ; save regs
CMP.B cpmode,#$00 ; to memory ?
JNZ ckvfil ; :no
MOV AX,pc ; (PC/16)+1
MOV CL,#$04
SHR AX,CL
INC AX
MOV BX,dc ; +(DC/16)+64
MOV CL,#$04
SHR BX,CL
ADD BX,AX
ADD BX,#$40
CMP BX,minstksz ; >= free memory ?
CALL errnb ; 98:memory overflow
B $62
JMP.b ckvmem ; 'ok
ckvfil MOV AX,cdptr ; code ptr
SUB AX,cdbufpt ; -code pos of buffer
ADD AX,cdbegpt ; +beg of code buffer
MOV CL,#$04 ; convert to paragraphs
SHR AX,CL
INC AX
ckvmem ADD AX,destseg ; + dest segment
ADD AX,#$0020 ; + spare
MOV BX,symtop ; (symtab top/16)+SS
SHR BX,CL
MOV CX,SS
ADD BX,CX
CMP AX,BX ; crash ?
JB ckvok ; :no
PUSH DX ; save
CALL codflush ; flush code buffer
CALL errb ; 99:compiler overflow
B $63
POP DX ; restore
ckvok POP CX ; restore regs
POP BX
POP AX
RET ; "
alpha CMP AL,#$41 ; valid symbol char ?
JB alpharet ; :no
CMP AL,#$5B ; A..Z ?
CMC
JNB alpharet ; :ok
CMP AL,#$5F ; _ ?
JZ alpharet ; yes: ok
CMP AL,#$61 ; a..z ?
JB alpharet ; :no
CMP AL,#$7B
CMC
alpharetRET ; "
alphanumCALL alpha ; valid alphanumeric char ?
JNB alnret ; :ok
number CMP AL,#$30 ; 0..9 ?
JB alnret ; :no
CMP AL,#$3A
CMC
alnret RET ; "
perrb MOV chptr,DI ; Error if below: store error pos
errb JNB skiperr ; :no error
JMP.b err ; '
MOV chptr,DI ; Error if not below
errnb JB skiperr ; :no error
JMP.b err ; '
perrz MOV chptr,DI ; Error if equal
errz JNZ skiperr ; :no error
JMP.b err ; '
perrnz MOV chptr,DI ; Error if not equal
errnz JNZ err ; :error
skiperr PUSH BP ; no error: skip inline parameter
MOV BP,SP
INC [BP]$02
POP BP
RET ; '
perr MOV chptr,DI ; store error position
err POP BX ; Error: get return addr
CS:
MOV.B AL,[BX] ; get error number (inline)
errexit MOV cperr,AL ; store it
CALL disline ; write line number
OR.B AL,AL ; test error
JZ exitcom ; :no error
MOV AX,chptr ; calculate error position
SUB AX,#pnbuf ; pos in buffer - buffer beg
CMP.B inclflg,#$00 ; include file ?
JNZ errfil ; :yes
SUB AX,txbeg ; - text beg
ADD AX,srclnbeg ; + beginning of source line
JMP.b errmem ; '
errfil ADD AX,srclnbg ; pos of line begin
errmem MOV txerrpos,AX ; store relative error pos
exitcom MOV AH,#$80 ; close all files
CALL dos
MOV SP,spsav ; restore stack pointer
RET ; "return to user-interface
defpart PUSH cdptr ; Definition part: save code pointer
CALL ejump ; * JMP ....
PUSH pc ; save PC
defloop CALL ckey ; search keyword
B $01 ; offset between keywords
W tklabel ; keyword pointer
CALL errnz ; not found ?
B $0C ; 12:BEGIN expected
deflab CMP AL,#$01 ; LABEL ?
JNZ defconst ; :no
CALL label ; do label
JMP defloop ; 'next def
defconstCMP AL,#$02 ; CONST ?
JNZ deftype ; :no
CALL const ; do const
JMP deflab ; 'next def, already searched
deftype CMP AL,#$03 ; TYPE ?
JNZ defvar ; :no
CALL type ; do type
JMP deflab ; 'next def
defvar CMP AL,#$04 ; VAR ?
JNZ defover ; :no
CALL var ; do var
JMP deflab ; 'next def
defover CMP AL,#$07 ; OVERLAY ?
JNZ defproc ; :no
CALL overlay ; do overlay
JMP defloop ; 'next def
defproc CMP AL,#$08 ; BEGIN ?
JZ defend ; :yes
MOV.B ovrproc,#$00 ; flag: not overlay procedure
CALL procfunc ; do procedure / function def
JMP defloop ; 'next def
defend CALL resforw ; resolve forward definitions
POP CX ; restore beginning PC
MOV AX,pc ; new PC
SUB AX,CX ; calc offset
POP BX ; code ptr of beginning
INC BX ; +1: offset
JMP patch ; "patch it
emcrunchMOV AX,#uncrunch ; emit code for overlay uncrunch
CALL ecall ; * CALL uncrunch
MOV AX,cdptr ; code ptr
MOV uncrlink,AX ; -> link
CALL eword ; emit it (dummy)
MOV AX,pc ; PC+2
INC AX
INC AX
JMP eword ; "emit it
ptcrunchMOV BX,uncrlink ; do overlay uncrunch list
MOV AX,cdptr ; code ptr
SUB AX,BX ; - pos old link
CALL patch ; patch offset
XOR AX,AX ; emit a zero:
JMP eword ; "current end of the list
label MOV AX,#$0100 ; label definition
CALL symword ; store word in symtab
MOV DI,chptr ; current pos
MOV.B AL,[DI] ; get char
CALL alphanum ; in alphanum ?
CALL rdsym0 ; get sym - numbers allowed
MOV.B AH,lexnest ; lexical nesting
MOV AL,#$FF ; flag: unresolved
CALL symword ; store word
CALL symword ; store dummy: offset
CALL symoffs ; write symtab offset
CALL ccomma ; , ?
JZ label ; yes: do another definition
JMP esemi ; "!
const PUSH symtop ; Constant definition
XOR AX,AX ; tag: invisible
CALL symword ; store in symtab
CALL rdsym ; get symbol name
CALL cequal ; = ?
JNZ cnstruct ; no: structured constant
CALL rdconst ; read constant
MOV.B AL,CL ; type
CALL symbyte ; store in symtab
CMP.B CL,#$09 ; real ?
JNZ cnstr ; :no
MOV AX,creal3 ; store real number in symtab
CALL symword ; 6 bytes
MOV AX,creal2
CALL symword
MOV AX,creal1
CALL symword
JMP.b cnput ; 'do end of entry
cnstr CMP.B CL,#$08 ; string ?
JNZ cnint ; :no
MOV BX,#wordbuf ; buffer pointer
MOV.B AL,CH ; get length
INC.B CH ; counter
cnstlp CALL symbyte ; store byte in symtab
MOV.B AL,[BX] ; get next char
INC BX
DEC.B CH ; another ?
JNZ cnstlp ; :yes
JMP.b cnput ; 'do end of entry
cnint XCHG AX,BX ; integer constant: result -> AX
CALL symword ; store in symtab
cnput CALL symoffs ; write symtab offset
MOV AL,#$02 ; set tag: normal constant
JMP.b cnput2 ; 'do it
cnstructCALL ecolon ; ! : (structured constant)
PUSH symtop ; save symtab pos
CALL symword ; store dummy type
MOV AX,pc ; store offset in symtab
CALL symword
MOV AX,#$FE00 ; segment CS
CALL symword
CALL symoffs ; write symtab offset
CALL rdtype ; get type
POP BP ; restore symtab pos
MOV AX,vartp ; store type ptr
MOV [BP]-$02,AX
CALL eequal ; ! =
CALL structcn ; read structured constant
MOV AL,#$04 ; tag: structured constant
cnput2 POP BP ; restore symtab pos
MOV.B [BP]-$01,AL ; set tag byte: entry type
CALL esemi ; !
CALL ckey ; search keyword: definitions
B $01
W tklabel
JZ cnrt ; :found, exit
JMP const ; 'do another const definition
cnrt RET ; "
structcnMOV AL,varctp ; read structured constant
CMP AL,#$04 ; test component type
JB scnok ; :ok
CMP AL,#$08 ; file / pointer ?
JNB scnok ; :no
CALL err ; 61:Files and pointers
B $3D ; 'are not allowed here
scnok CMP AL,#$01 ; array ?
JNZ scnrec ; :no
CALL pushe1 ; save entry to stack
MOV BP,upper ; index type
CALL getparm ; get parameters
MOV AX,upper2 ; upper bound - lower bound + 1
SUB AX,lower2
INC AX
PUSH AX ; save count
MOV BP,lower ; component type pointer
CALL getvprm2 ; get parms
POP CX ; restore count
CMP.B varctp,#$0C ; array of char ?
JNZ scnarray ; :no
OR.B CH,CH ; test count
JNZ scnarray ; :more than 256
CALL cbrack1 ; ( ?
JNZ scnarrch ; no: defined by string constant
JMP.b scnarrlp ; '
scnarrayCALL ebrack1 ; ! (
scnarrlpPUSH CX ; save count
CALL structcn ; read structured constant (recursive !)
POP CX ; restore count
DEC CX ; count down
JCXZ scnaend ; :done
CALL ecomma ; ! ,
JMP scnarrlp ; 'do next entry
scnarrchPUSH CX ; save count
CALL rdstrcn ; read constant
POP DX ; restore expected length
CMP.B CH,DL ; = length read ?
CALL errnz ; 50:String const length does not
B $32 ; match type
CALL estr2 ; emit string (without length byte)
JMP.b scnaend2 ; 'done
scnaend CALL ebrack2 ; ! )
scnaend2CALL pope1 ; restore entry
RET ; '
scnrec CMP AL,#$02 ; record ?
JNZ scset ; :no
CALL pushe1 ; save entry to stack
CALL ebrack1 ; ! (
MOV.B CL,varnest ; record nesting level
PUSH varsize ; save component size
XOR AX,AX ; offset in record: size done
scnrlp PUSH CX ; save nesting, size
PUSH AX
MOV CH,#$04 ; search variable
CALL search
CALL errnz ; 41:Unknown ID or syntax error
B $29
CALL getvprm ; get parms
POP AX ; restore size
CMP AX,varofs ; = offset of record sub-var ?
CALL errnz ; 69:Invalid ordering of fields
B $45
ADD AX,varsize ; add size to offset
PUSH AX ; save it
CALL ecolon ; ! :
CALL structcn ; read structured constant
CALL csemi ; , ?
POP AX ; restore size, nesting level
POP CX
JZ scnrlp ; yes: continue
PUSH AX ; save size
CALL ebrack2 ; ! )
POP AX ; current size
POP CX ; component size of record type
SUB CX,AX ; compare them
JZ scnrok ; :ok
scnrfillXOR.B AL,AL ; emit zeroes to fill
CALL ebyte
LOOP scnrfill ; :another
scnrok CALL pope1 ; restore entry from stack
RET ; '
scset CMP AL,#$03 ; set ?
JNZ scstr ; :no
CALL pushe1 ; save entry to stack
PUSH varsize ; save component size
MOV BP,lower ; type ptr
CALL getvprm2 ; get type parms
CALL esqr1 ; ! [
MOV DI,#wordbuf ; buffer ptr
PUSH DS ; DS,DI: dest ptr
PUSH DI
CALL sldempty ; make empty set (on stack)
CALL csqr2 ; ] ?
JZ scssto ; :yes
scslp CALL rdscalar ; get scalar element
PUSH AX ; save
CALL ctoken
W tk2dot
JNZ scsincl ; :no
CALL rdscalar ; get scalar element
CALL setinrng ; include range in set
JMP.b scsrng ; '
scsincl POP AX ; restore scalar element
CALL setincl ; include element in set
scsrng CALL ccomma ; , ?
JZ scslp ; yes: continue
CALL esqr2 ; ! ]
scssto MOV CX,#$0020 ; 32 bytes
CALL setsto ; store set into buffer
MOV BX,lower ; crunch set constant:
MOV CL,#$03 ; (lower bound)/8
SHR BX,CL
ADD BX,#wordbuf ; + buffer offset
POP CX ; restore component size
scsemit MOV.B AL,[BX] ; get byte
CALL ebyte ; emit it
INC BX ; next one
LOOP scsemit ; :another
CALL pope1 ; restore entry from stack
RET ; '
scstr CMP AL,#$08 ; string ?
JNZ screal ; :no
CALL rdstrcn ; read string constant
MOV.B CL,varsize ; component size - 1
DEC.B CL
SUB.B CL,CH ; >= actual size ?
JNB scstok ; :ok
ADD.B CL,CH ; limit to max size
MOV.B CH,CL
XOR.B CL,CL ; nothing to fill up
scstok CALL estring ; emit string
OR.B CL,CL ; fill up ?
JZ scstret ; :no
scstfillXOR.B AL,AL ; emit zeroes to reserve space
CALL ebyte
DEC.B CL ; another ?
JNZ scstfill ; :yes
scstret RET ; '
screal CMP AL,#$09 ; real ?
JNZ scint ; :no
CALL rdnumcn ; get numeric constant
CMP.B CL,#$09 ; real ?
JZ scrok ; :ok
CMP.B CL,#$0A ; integer ?
CALL errnz ; 25:Integer or real const expected
B $19
XCHG AX,BX ; get result
CALL intreal ; convert to real
MOV creal1,AX ; store it in buffer
MOV creal2,BX
MOV creal3,DX
scrok MOV AX,creal1 ; emit resulting real number
CALL eword
MOV AX,creal2
CALL eword
MOV AX,creal3
JMP eword ; 'done
scint CALL rdscalar ; integer: get scalar element
CMP varsize,#$01 ; component size = 1 ?
JZ scbyte ; :yes
JMP eword ; 'emit word
scbyte JMP ebyte ; "emit byte
rdscalarCALL rdnumcn ; get scalar element
CMP.B CL,varctp ; = component type ?
CALL errnz ; 44:Type mismatch
B $2C
XCHG AX,BX ; result -> AX
CMP AX,lower ; < lower bound ?
JL rscalerr ; :yes
CMP AX,upper ; > upper bound ?
JG rscalerr ; :yes
RET ; '
rscalerrCALL err ; 45:Constant out of range
B $2D ; "
type PUSH symtop ; Type definition
typelp PUSH symtop ; save symtab top
MOV AX,#$0000 ; tag: invisible
CALL symword ; store in symtab
CALL rdsym ; get symbol
PUSH symtop ; save symtab top
CALL symword ; store dummy
CALL symoffs ; write symtab offset
CALL eequal ; ! =
CALL rdtype ; get type
POP BP ; restore pos
MOV AX,vartp ; store type ptr
MOV [BP]-$02,AX
POP BP ; pos of tag
MOV.B [BP]-$01,#$03 ; set tag: type definition
CALL esemi ; !
CALL ckey ; search keyword: definitions
B $01
W tklabel
JNZ typelp ; not found: another type def
POP tyfence ; old symtab top -> type fence
PUSH AX ; save next element
CALL resptr ; fill in pointer types
POP AX ; restore next element
RET ; "
var CALL vardef ; Var definition: define var
CALL esemi ; !
CALL ckey ; search keyword: definitions
B $01
W tklabel
JNZ var ; not found: another var
RET ; "
overlay MOV.B cdinval,#$FF ; Overlay: always recompile
MOV DI,#destpn ; get filename
XOR DX,DX ; end pos
ovnm1 MOV.B AL,[DI] ; get char
OR.B AL,AL ; end ?
JZ ovnmend ; :yes
CMP AL,#$2E ; . ?
JNZ ovnm2 ; :no
MOV DX,DI ; remember end pos
ovnm2 INC DI ; next char
CMP AL,#$5C ; \ ?
JNZ ovnm1 ; no: loop back
MOV BX,DI ; set beg position
XOR DX,DX ; clear end position
JMP ovnm1 ; 'loop
ovnmend OR DX,DX ; end position set ?
JZ ovnm3 ; :no
MOV DI,DX ; set it
ovnm3 CMP DI,#destpne ; too much ?
CALL errnb ; 92:Unable to create overlay file
B $5C
MOV AL,ovrcnt ; overlay counter
XOR.B AH,AH ; clear high byte
INC.B ovrcnt ; count up
MOV [DI],#$302E
MOV CL,#$0A ; get 10-digit
DIV.B CL
ADD AX,#$3030 ; -> ASCII
MOV [DI]$02,AX ; store number
MOV.B [DI]$04,#$00 ; mark end
MOV AX,#rdover ; * CALL readovr
CALL ecall
MOV AX,#$FFFF ; invalid overlay in mem
CALL eword ; emit word
MOV CX,#$000D ; 13 bytes
ovnmem MOV.B AL,[BX] ; emit file name
CALL ebyte
INC BX
LOOP ovnmem ; :another
CALL ptcflush ; patch code in file
CALL codflush ; flush code buffer
MOV AL,cpmode ; save code destination
PUSH AX
PUSH dstfile ; save dest file handle
PUSH cdptr ; save code ptr
PUSH cdbufpt ; save code pos buf beg
PUSH cdbegpt ; save beg of buf
PUSH cdfoff ; save current offset in file
PUSH cdfoff1
PUSH uncrlink ; save uncrunch link
PUSH ovrlen ; save max length of overlay part
CMP.B cpmode,#$00 ; compile to memory ?
JNZ ovfil ; :no
MOV.B cpmode,#$02 ; set: to COM
ovfil MOV AX,cdptr ; code ptr - code pos of buffer
SUB AX,cdbufpt ; add to beg of code buffer
ADD cdbegpt,AX
XOR AX,AX ; length = 0
MOV ovrlen,AX
MOV cdfoff,AX ; no offset in file
MOV cdfoff1,AX
CMP.B cpmode,#$01 ; find error ?
JZ ovloop
MOV AH,#$3C ; create file
XOR CX,CX ; no attribute
MOV DX,#destpn ; name ptr
PUSH DS ; DS -> ES
POP ES
CALL dos ; open it
CALL errb ; 92:Unable to create overlay file
B $5C
MOV dstfile,AX ; store dest file handle
ovloop XOR AX,AX
MOV cdptr,AX ; clear code ptr
MOV cdbufpt,AX ; code pos of buffer
CALL ckey ; search key word
B $01 ; (procedure or function)
W tkproc
CALL errnz ; 16:PROC or FUNC expected
B $10
PUSH pc ; save PC
PUSH cdptr ; save code ptr
PUSH AX ; save keyword
CALL emcrunch ; emit overlay uncrunch code
POP AX ; restore keyword
MOV.B ovrproc,#$FF ; set flag: in overlay
CALL procfunc ; do proc/func
CALL ptcrunch ; end overlay uncrunch list
CALL ptcflush ; patch code in file
POP CX ; restore code ptr, PC
POP DX
PUSH BP ; save
ovfill MOV AX,cdptr ; new - old code ptr
SUB AX,CX
OR.B AL,AL ; emit zeroes, until length
JZ ovnofill ; a multiple of 256
XOR.B AL,AL
CALL ebyte
JMP ovfill ; '
ovnofillPOP BP ; restore
MOV [BP]-$0E,AX ; store code pos in proc def
ADD cdfoff,AX ; add to pos in file
ADC cdfoff1,#$00
MOV AX,pc ; PC - old PC
SUB AX,DX
MOV pc,DX ; restore old PC
CMP AX,ovrlen ; >= previous procedures ?
JB ovshort ; :no
MOV ovrlen,AX ; set as max length
ovshort CALL codflush ; flush code buffer
CALL ctoken ; OVERLAY ?
W tkover
JZ ovloop ; yes: repeat
CMP.B cpmode,#$01 ; find error ?
JZ ovfind ; yes: no file
MOV AH,#$3E ; close file
MOV BX,dstfile ; file handle
CALL dos
ovfind MOV DX,ovrlen ; get maximum length
POP ovrlen ; restore variables
POP uncrlink ; same as above
POP cdfoff1
POP cdfoff
POP cdbegpt
POP cdbufpt
POP cdptr
POP dstfile
POP AX
MOV cpmode,AL ; restore code destination
ADD DX,pc ; max length + PC
MOV BX,uncrlink ; uncrunch link
MOV AX,cdptr ; code ptr -> uncrunch link
MOV uncrlink,AX
SUB AX,BX ; code pos - link
CALL patch ; patch link
CALL eword ; emit word
MOV AX,DX ; max length + PC
CALL eword ; emit word
MOV pc,DX ; set new PC
RET ; "
procfuncMOV procfnc,AL ; define proc / func: set flag
MOV.B CH,AL ; -> type
XOR.B CL,CL
PUSH CX ; save type
CALL srchvar ; search
POP AX
JNZ prfnew ; not found: new definition
JMP prffwd ; 'complete forward definition
prfnew CALL symword ; store tag word in symbol table
CALL rdsym ; get symbol
PUSH fence ; save current var fence
MOV AX,symtop2 ; current sym top
MOV fence,AX ; -> new var fence
PUSH symtop ; save symtab pos
SUB symtop,#$10 ; make space
CALL chkovrfl ; test overflow
MOV BX,#$0004 ; size of stack frame
XOR CX,CX ; parameter counter
CALL cbrack1 ; ( ?
JNZ prfnil ; no:no parms
prfloop PUSH BX ; save
PUSH CX
PUSH symtop ; save symtab pos
SUB symtop,#$04 ; make space
CALL chkovrfl ; test overflow
CALL ctoken ; VAR ?
W tkvar
MOV CX,#$0000 ; normal: 0
JNZ prfcnt ; no: normal
DEC.B CH ; flag: FF00
prfcnt PUSH CX ; save flag
CALL rdsym ; read symbol
POP CX ; restore type
INC.B CL ; count vars of same type
CALL ccomma ; , ?
JZ prfcnt ; yes: repeat
PUSH CX ; save count
OR.B CH,CH ; VAR-parameter ?
JNZ prfnotyp ; :yes
CALL ecolon ; ! :
JMP.b prftype ; '
prfnotypCALL ccolon ; : ?
JZ prftype ; yes: ok
MOV AX,ptcbeg ; bottom of symtab
SUB AX,#$000E ; point to untyped var
MOV vartp,AX ; -> type ptr
JMP.b prfnot2 ; '
prftype MOV.B flgvar,CH ; set VAR flag
CALL testtp ; get type
CALL tstscal ; limit component size
prfnot2 POP CX ; restore type, count
POP BP ; restore symtab pos
MOV AX,vartp ; type ptr
MOV [BP]-$02,AX ; -> type
MOV [BP]-$04,CX ; store count
MOV AX,varsize ; component size
OR.B CH,CH ; VAR-parameter ?
JZ prfnovar ; :no
MOV AX,#$0004 ; size 4: pointer
prfnovarXOR.B CH,CH ; clear hi
MUL CX ; count * component size
POP CX
POP BX ; restore
ADD BX,AX ; add to size of stack frame
INC.B CL ; count entries
CALL csemi ; semicolon ?
JZ prfloop ; yes: another parm
CALL ebrack2 ; ! )
prfnil CALL symoffs ; write symtab offset
CMP.B procfnc,#$06 ; function ?
JNZ prfprc1 ; :no
CALL ecolon ; ! :
PUSH BX ; save parm count,
PUSH CX ; stack frame size
MOV.B flgvar,#$00 ; clear flag: normal var
CALL testtp ; get type
POP CX ; restore
POP BX
CMP.B varctp,#$08 ; legal type ?
JNB prfresok ; :no
CMP.B varctp,#$04 ; pointer ?
CALL errnz ; 48:Invalid result type
B $30
prfresokPOP BP ; symtab ptr
PUSH BP
MOV AX,vartp ; get type ptr
MOV [BP]-$02,AX ; store it
MOV [BP]-$04,BX ; store stack frame size
MOV.B AH,lexnest ; lexical nesting level
INC.B AH ; +1
MOV AL,#$FF
MOV [BP]-$06,AX ; store segment
ADD BX,varsize ; add to size of stack frame
prfprc1 CALL esemi ; !
POP BP ; restore symtab ptr
POP AX ; restore var fence
MOV fence,AX
MOV AX,pc ; get PC
CMP.B ovrproc,#$00 ; overlay procedure ?
JZ prfnoovr ; :no
SUB AX,#$0019 ; entry code...
prfnoovrMOV [BP]-$08,AX ; store position
MOV [BP]-$0A,BX ; store stack frame size
XOR.B CH,CH ; store param count
MOV [BP]-$10,CX
MOV AX,cdprcoff ; position in overlay file
MOV [BP]-$0C,AX
MOV [BP]-$0E,#$0000 ; no forward
CMP.B ovrproc,#$00 ; overlay procedure ?
JNZ prfovr ; :yes
CALL ctoken ; FORWARD ?
W tkforwrd
JNZ prfnofwd ; :no
MOV AX,cdptr ; remember code position
MOV [BP]-$0C,AX ; for patching
DEC.B [BP]-$0F ; set flag: forward def
CALL ejump ; emit jump
JMP esemi ; '!
prfnofwdCALL ctoken ; EXTERNAL ?
W tkext
JNZ prfovr ; :no
JMP rdextnal ; 'do external procedure
prffwd CMP.B [BP]-$0F,#$00 ; complete forward definition
CALL errz ; test flag. Defined:
B $2B ; 43:Duplicate ID or label
CMP.B ovrproc,#$00 ; overlay procedure ?
CALL errnz ; yes:
B $4C ; 76:Overlays cannot be forwarded
CALL skipdi ; skip spaces
CALL esemi ; !
MOV.B [BP]-$0F,#$00 ; clear forward flag
MOV BX,[BP]-$0C ; get addr of forward jump
INC BX
MOV AX,pc ; PC-proc pos-3 -> offset
SUB AX,[BP]-$08
SUB AX,#$0003
CALL patch ; patch it in
prfovr PUSH varspc ; save memory usage
PUSH fence ; save fence
MOV AX,symtop ; symtab top
MOV fence,AX ; -> current fence
PUSH BP ; save pos in symtab
MOV AX,[BP]-$0A ; get stack frame size
CMP.B procfnc,#$05 ; procedure ?
JZ prfprc2 ; :yes
MOV BX,[BP]-$02 ; subtract size of result
SS:
SUB AX,[BX]-$02
prfprc2 MOV varspc,AX ; -> memory usage
INC.B lexnest ; inc nesting
MOV.B [BP]-$06,#$00
MOV.B CL,[BP]-$10 ; parameter count
MOV BX,BP ; pos
SUB BX,#$10
prfsto OR.B CL,CL ; all parms done ?
JZ prfentry ; :yes
PUSH CX ; save count
SS:
MOV BP,[BX]-$02 ; get variable pointer
PUSH BX ; save current pos
MOV vartp,BP ; set type ptr
CALL getvprm2 ; get var parms
POP BX ; restore pos
SS:
MOV CX,[BX]-$04 ; get type
MOV.B flgvar,CH
XOR.B CH,CH
PUSH CX ; save type
PUSH symtop ; save symtab top
SUB BX,#$04 ; go down
prfsto2 MOV BP,symtop ; symtab top
DEC BP
DEC BP
MOV [BP]$00,#$0400 ; store: var
DEC BX
DEC BP
SS:
MOV.B DL,[BX] ; get length
MOV.B [BP]$00,DL ; store length
prfsto3 DEC BX ; go down
DEC BP
SS:
MOV.B AL,[BX] ; get char
MOV.B [BP]$00,AL ; store it
DEC.B DL ; another ?
JNZ prfsto3 ; :yes
SUB BP,#$06 ; go down
MOV symtop,BP ; set new symtab top
CALL symoffs ; write offset
LOOP prfsto2 ; :another var
POP BP ; restore pos
POP CX ; restore count
PUSH BX
CALL rdvnrm ; store offset, segment
CALL tstscal ; scalar var: 2 bytes on stack
CALL vardef2 ; do var definitions
POP BX ; restore pos, cnt
POP CX
DEC.B CL ; another ?
JMP prfsto ; '
prfentryCALL ecode ; emit stack frame code
B $01,$55 ; * PUSH BP
CMP.B lexnest,#$01 ; lexical nesting = 1 ?
JNZ prflong ; :no
CALL ecode
B $02,$8B,$EC ; * MOV BP,SP
JMP.b prfshort ; '
prflong CALL ecode ; complicated - do display
B $02,$8B,$C4 ; * MOV AX,SP
XOR.B CH,CH
MOV.B CL,lexnest ; lexical nesting -> count
DEC.B CL
prfcopy CALL ecode ; * PUSH [BP+..]
B $02,$FF,$76
DEC.B CH ; count down two bytes
DEC.B CH
MOV.B AL,CH ; offset into display
CALL ebyte ; emit offset
DEC.B CL ; another level ?
JNZ prfcopy ; :no
CALL ecode
B $02,$8B,$E8 ; * MOV BP,AX
prfshortCALL ecode ; now push current display !
B $01,$55 ; * PUSH BP
MOV AL,lexnest ; lexical nesting * 2
XOR.B AH,AH
ADD AX,AX
NEG AX
MOV varspc,AX ; -> stack usage
PUSH AX
MOV AL,procfnc ; proc or func ?
PUSH AX ; save flag
CALL defpart ; do definition part
POP AX ; restore... (defs may be recursive)
MOV procfnc,AL
POP AX
SUB AX,varspc ; mem usage - current
CALL allotstk ; make space on stack
POP BP ; restore symtab ptr
PUSH BP
PUSH [BP]-$0A ; store memory size
MOV AX,varspc ; memory usage
NEG AX
ADD [BP]-$0A,AX ; add to mem size
CALL errb ; 98:Memory overflow
B $62
CALL progpart ; do program part
POP AX ; restore memory size - 4
SUB AX,#$0004
CMP.B procfnc,#$06 ; function ?
JNZ prfret ; :no
POP BP ; restore symtab pos
PUSH BP
PUSH AX ; save mem size
CALL getvprm ; get var parms
MOV.B indflg,#$00 ; flag: not indexed
POP AX ; mem size
CMP.B varctp,#$09 ; component type = real ?
JNZ prfstr ; :no
SUB AX,#$0006 ; 6 bytes on stack
JMP.b prfret ; '
prfstr CMP.B varctp,#$08 ; string ?
JNZ prfelse ; :no
SUB AX,varsize ; - component size -> pos on stack
CALL emovdxi ; * MOV DX,..
MOV AX,varsize ; component size: max length - 1
DEC AX
MOV.B AH,AL ; max string length
MOV AL,#$B1 ; * MOV CL,..
CALL eword
CALL ecode ; * MOV SP,BP
B $03,$8B,$E5,$5D ; * POP BP
MOV AX,#retstr ; * JMP retstr
CALL ejump
JMP.b prfret2 ; '
prfelse PUSH AX ; save pos
CALL eload ; load var
POP AX ; restore
CMP.B varctp,#$0B ; boolean ?
JNZ prfret ; :no
CALL ecode ; * OR AX,AX (set flags)
B $02,$0B,$C0
prfret CALL ecode ; * MOV SP,BP
B $03,$8B,$E5,$5D ; * POP BP
OR AX,AX ; stack frame ?
JNZ prfretn ; :yes
CALL ecode ; none:
B $01,$C3 ; * RET
JMP.b prfret2 ; '
prfretn CALL ecode ; remove stack frame
B $01,$C2 ; * RET ....
CALL eword ; emit stack frame size
prfret2 CALL esemi ; !
POP BP ; restore symtab pos
DEC.B [BP]-$06 ; clear flag
DEC.B lexnest ; restore lexical nesting
MOV AX,fence ; fence -> symtab top
MOV symtop,AX ; = remove all local vars
MOV symtop2,AX
POP fence ; restore fence
POP varspc ; restore mem usage
RET ; "
rdextnalPUSH BP ; read external proc
MOV CX,#$0500 ; tag: procedure
CALL search
JZ rdxold ; : found
MOV CX,#$0600 ; tag: function
CALL search
JNZ rdxnew ; :not found
rdxold PUSH [BP]-$08 ; get offset
CALL esqr1 ; ! [
CALL rdintcn ; get integer constant
CALL esqr2 ; ! ]
POP AX ; offset + number
ADD AX,BX
JMP.b rdxsto ; '-> offset of this proc
rdxnew PUSH pc ; save PC
CALL rdstrcn ; read string constant
MOV.B BL,CH ; length
XOR.B BH,BH
MOV.B [BX]wordbuf,#$00 ; store a 0 at the end
MOV BX,#wordbuf ; name ptr
MOV SI,#extcom ; extension .COM
CALL kextdef ; parse filename
MOV AX,#$3D00 ; open file
MOV DX,#scrpn ; name ptr
PUSH DS ; DS -> ES
POP ES
CALL dos ; open it
CALL errb ; 90:File not found
B $5A
MOV BX,AX ; file handle
rdxloop MOV AH,#$3F ; read from file
MOV CX,#$0080 ; 128 bytes
MOV DX,#wordbuf ; dest buffer
CALL dos ; do it
CALL errb ; 90:File not found
B $5A
XCHG AX,CX ; length read -> CX
JCXZ rdxend ; 0: end
MOV SI,#wordbuf ; source ptr
rdxcopy MOV.B AL,[SI] ; get byte from external file
CALL ebyte ; and emit it
INC SI ; next one
LOOP rdxcopy ; :again
JMP rdxloop ; 'try another block
rdxend MOV AH,#$3E ; close file
CALL dos
POP AX ; restore offset
rdxsto POP BP ; restore symtab pos
MOV [BP]-$08,AX ; store offset
JMP esemi ; "!
tstscal CMP.B varctp,#$0A ; scalar var ?
JB tsc2 ; :no
MOV varsize,#$0002 ; on stack at least 2 bytes
tsc2 RET ; "
resforw MOV BP,symtop ; Resolve forward definitions
rfwloop CMP BP,fence ; = var fence ?
JZ rfwret ; yes: end
ADD BP,[BP]$00 ; go to next entry
CMP.B [BP]-$01,#$06 ; function ?
JZ rfwfunc ; :yes
CMP.B [BP]-$01,#$05 ; procedure ?
JNZ rfwloop ; :no, next one
rfwfunc MOV BX,BP ; entry ptr
SUB BX,#$03
SS:
MOV.B AL,[BX] ; length of name
XOR.B AH,AH
SUB BX,AX ; go down
SS:
CMP.B [BX]-$0F,#$00 ; defined ?
CALL errnz ; no:
B $49 ; 73:Undefined FORWARD procedure
JMP rfwloop ; 'next one
rfwret RET ; "
vardef MOV.B flgvar,#$00 ; define var: clear VAR-flag
CALL rdvarlst ; get variable list
PUSH CX ; save counter
PUSH BP ; save symtab ptr
CALL ecolon ; ! :
CALL rdvartp ; get type, test absolute
POP BP ; restore
POP CX
vardef2 MOV DX,varsize ; component size
CMP.B flgvar,#$00 ; VAR-parameter ?
JZ vdvar ; :no
MOV DX,#$0004 ; yes: pointer !
vdvar MOV AX,var3ofs ; var offset
CMP.B absflg,#$00 ; absolute ?
JNZ vdstore ; :yes
CMP.B recnum,#$00 ; record nesting level ?
JNZ vdrec ; :in record
CMP.B lexnest,#$00 ; inside procedure ?
JNZ vdstk ; :yes
MOV AX,dc ; DC -> offset
ADD dc,DX ; add size to DC
CALL errb ; overflow:
B $62 ; 98:Memory overflow
JMP.b vdstore ; '
vdstk SUB varspc,DX ; go down (stack !)
CALL errb ; overflow ?
B $62 ; 98:Memory overflow
MOV AX,varspc ; get offset
JMP.b vdstore ; '
vdrec MOV AX,varspc ; get offset
ADD varspc,DX ; add to var space
CALL errb ; overflow ?
B $62 ; 98:memory overflow
vdstore SUB BP,#$03 ; go down in var list
MOV.B BL,[BP]$00 ; get length
XOR.B BH,BH
SUB BP,BX ; go down
MOV [BP]-$04,AX ; store offset
MOV AX,var3seg ; store segment
MOV [BP]-$06,AX
MOV AX,vartp ; store type ptr
MOV [BP]-$02,AX
SUB BP,#$08 ; make space
LOOP vdvar ; another definition ?
JMP chkovrfl ; "test for overflow
rdvarlstPUSH symtop ; get variable list
XOR CX,CX ; clear counter
rdvloop PUSH CX ; save
MOV AH,#$04 ; tag: var, record nesting
MOV AL,recnum
CALL symword ; store in symtab
CALL rdsym ; store name in symtab
SUB symtop,#$06 ; reserve some space
CALL symoffs ; write symtab offset
POP CX ; restore counters
INC CX
CALL ccomma ; , ?
JZ rdvloop ; yes: next var
POP BP ; restore symtab pos
RET ; "(beginning of list)
rdvartp PUSH symtop ; get var type, test ABSOLUTE
CALL rdtype ; read type
POP tyfence ; symtab top -> type fence
CALL resptr ; fill in pointer types
CALL ctoken ; ABSOLUTE ?
W tkabs
JNZ rdvnrm ; :no
MOV.B absflg,#$FF ; set flag
CMP.B recnum,#$00 ; in record ?
CALL errnz ; yes:
B $4B ; 75:Illegal use of ABSOLUTE
MOV CX,#$0400 ; search var
CALL search
JNZ rdvabs ; :not found
MOV AX,[BP]-$06 ; get offset
MOV DX,[BP]-$04 ; get segment
JMP.b rdvofs ; 'store parms
rdvabs CALL rdconst ; read constant -> segment
JNZ rdvseg ; :no good
CALL testint ; test type: integer
PUSH BX ; save segment
CALL ecolon ; ! :
CALL rdintcn ; get integer constant
MOV DX,pc ; get PC: pos of pointer
XCHG AX,BX
CALL eword ; emit result = offset
POP AX ; emit segment
CALL eword
MOV AX,#$FEFF ; flag: CS indirect
JMP.b rdvofs ; '
rdvseg CALL ctoken ; DSEG ?
W tkdseg
MOV AX,#$FF00 ; flag: DS
JZ rdvseg2 ; :yes
CALL ctoken ; CSEG ?
W tkcseg
MOV AX,#$FE00 ; flag: CS
JZ rdvseg2 ; :yes
JMP snerror ; 'Unknown ID or syntax error
rdvseg2 PUSH AX ; save segment flag
CALL ecolon ; ! :
CALL rdintcn ; get integer constant
POP AX ; restore segment
MOV DX,BX ; offset -> DX
rdvofs MOV var3ofs,DX
JMP.b rdvsto ; '
rdvnrm MOV.B absflg,#$00 ; clear absolute flag
MOV AL,flgvar ; VAR-parameter ?
MOV.B AH,lexnest ; lexical nesting
OR.B AH,AH ; inside proc ?
JNZ rdvofs ; :yes, use stack segment
MOV AH,#$FF ; data segment
rdvsto MOV var3seg,AX ; store segment
RET ; "
testtp CALL srchtype ; Test file type: search type
JZ ttpsto ; :found
CALL ctoken ; TEXT ?
W tktext
JNZ ttpnotxt ; :no
CALL textstd ; get ptr to that type
JMP.b ttpsto ; '
ttpnotxtCALL ctoken ; FILE ?
W tkfile
CALL errnz ; no:
B $24 ; 36:Type ID expected
CALL filuntp ; point to typed/untyped file
ttpsto CMP.B flgvar,#$00 ; VAR-parameter ?
JNZ rdvnrm ; yes: ok
CMP.B varctp,#$05 ; typed file ?
JB rdvnrm ; :below
CMP.B varctp,#$07 ; above file ?
JA rdvnrm ; yes: ok
CALL err
B $43 ; "67:Files must be VAR parameters
resptr MOV BP,symtop ; Fill in pointer types
rptlp CMP BP,tyfence ; = type fence ?
JZ rptret ; yes: done
ADD BP,[BP]$00 ; go to next var
CMP.B [BP]-$01,#$08 ; subtype ?
JNZ rptlp ; no: next one
MOV.B [BP]-$01,#$00 ; make it invisible
CMP.B [BP]-$0A,#$04 ; pointer ?
JNZ rptlp ; no: next one
CMP.B [BP]-$09,#$00 ; filled in ?
JZ rptlp ; :yes
MOV.B [BP]-$09,#$00 ; flag: filled in
MOV BX,[BP]-$08 ; get pos of type name
SS:
MOV.B DL,[BX]-$01 ; name length
XOR.B DH,DH
INC DX ; -> count
PUSH BP ; save pos
MOV BP,symtop ; search from top
rptsrch CMP BP,ptcbeg ; bottom of symbol table ?
JZ rpterr ; yes: error
ADD BP,[BP]$00 ; go to next var
CMP.B [BP]-$01,#$03 ; type ?
JNZ rptsrch ; :no, search next
MOV SI,BP ; pointer
DEC SI
DEC SI
MOV DI,BX ; pos of searched string
MOV CX,DX ; len -> count
rptcmp DEC SI ; go back
DEC DI
SS:
MOV.B AL,[SI] ; compare chars
SS:
CMP.B AL,[DI]
JNZ rptsrch ; :not the right one
LOOP rptcmp ; :test another char
POP BP ; restore symtab pos
SS:
MOV AX,[SI]-$02 ; get type pointer
MOV [BP]-$08,AX ; store into pointer entry
JMP rptlp ; 'next one
rptret RET ; '
rpterr CALL err ; 42:Undefd ptr type in preceding
B $2A ; "type defs
rdtype CALL srchtype ; get type: search type ID
JZ rdtpret ; found: ret
CALL ctoken ; PACKED ?
W tkpacked ; (ignored)
CALL array ; array ?
JZ rdtpret ; :done
CALL record ; record ?
JZ rdtpret ; :done
CALL set ; set ?
JZ rdtpret ; :done
CALL pointer ; pointer ?
JZ rdtpret ; :done
CALL file ; file ?
JZ rdtpret ; :done
CALL text ; text ?
JZ rdtpret ; :done
CALL string ; string ?
JZ rdtpret ; :done
CALL scalar ; scalar list ?
JZ rdtpret ; :done
CALL subrange ; subrange ?
JZ rdtpret ; :done
CALL err ; 36:Type identifier expected
B $24 ; '
rdtpret RET ; "
srchtypeMOV CX,#$0300 ; search type
CALL search
JNZ srtret ; :not found - ret
MOV BP,[BP]-$02 ; type pointer
stdtype MOV vartp,BP ; store it
CALL getvprm2 ; get type parms
XOR AX,AX ; ok
srtret RET ; "
array CALL ctoken ; ARRAY ?
W tkarray
JNZ arret ; :no
CALL esqr1 ; ! [
XOR CX,CX ; clear dimension count
arrlp PUSH CX ; save
CALL rdscaltp ; get scalar type
POP CX
PUSH vartp ; save type pointer: index type
MOV AX,upper ; upper bound - lower bound
SUB AX,lower
INC AX ; + 1
CALL errz ; 98:Memory overflow
B $62
PUSH AX ; save component count
INC CX ; count dimensions
CALL ccomma ; , ?
JZ arrlp ; yes: another dimension
PUSH CX ; save dim count
CALL esqr2 ; ! ]
CALL expof ; ! OF
CALL rdtype ; get type
POP CX ; dim count
arrlp2 MOV AX,vartp ; type ptr
MOV lower,AX ; -> type
MOV AX,varsize ; component size
POP BX ; * component count
MUL BX
CALL errb ; too much ?
B $62 ; 98:Memory overflow
MOV varsize,AX ; -> component size
POP AX ; index type
MOV upper,AX ; store it
MOV.B varctp,#$01 ; tag: array
CALL stotype ; store type
LOOP arrlp2 ; :another dimension
arret RET ; "
record CALL ctoken ; RECORD ?
W tkrec
JNZ recret ; no: ret
MOV AL,vrecflg ; save variant rec nesting
PUSH AX
MOV AL,recnum ; save rec nesting
PUSH AX
INC.B reccnt ; one more level
MOV AL,reccnt ; record counter
MOV recnum,AL ; -> record number
PUSH varspc ; save space used
PUSH maxsize ; save max size of variant rec
MOV varspc,#$0000 ; clear them
MOV maxsize,#$0000
MOV.B vrecflg,#$00 ; no variant record
CALL recdef ; do record definition
MOV AX,maxsize ; max component size
MOV varsize,AX ; -> variable size
POP maxsize ; restore vars
POP varspc
MOV AL,recnum ; record number
MOV varnest,AL ; -> subtype
POP AX
MOV recnum,AL
POP AX
MOV vrecflg,AL
MOV.B varctp,#$02 ; type: record
CALL stotype ; store type
recret RET ; "
recdef CALL rectest ; do record def: test end
JZ rcdret ; yes: ret
CALL ctoken ; CASE ?
W tkcase
JZ rcdvrec ; :yes
CALL vardef ; define variables
MOV AX,varspc ; memory used
CMP AX,maxsize ; >= max size ?
JB rcdsmall ; below: forget
MOV maxsize,AX ; store as new max size
rcdsmallCALL csemi ; semicolon ?
JZ recdef ; yes: loop back
JMP.b rcde ; 'end it
rcdvrec CALL srchtype ; variant record: search type
JZ rcdnotag ; :found
CALL vardef ; define var: tag field
rcdnotagCALL expof ; ! OF
rcdvlp CALL rectest ; test for end
JZ rcdret ; yes: ret
PUSH varspc ; save mem used
rcdtag CALL rdnumcn ; get constant
CALL ccomma ; , ?
JZ rcdtag ; :another constant
CALL ecolon ; ! :
CALL ebrack1 ; ! (
MOV AL,vrecflg ; save variant rec flag
PUSH AX
MOV.B vrecflg,#$FF ; set it
CALL recdef ; do type definition list
POP AX ; restore flag
MOV vrecflg,AL
POP varspc ; restore memory used
CALL csemi ; semicolon ?
JZ rcdvlp ; yes: another
rcde CMP.B vrecflg,#$00 ; variant record ?
JZ rcde1 ; :no
JMP ebrack2 ; '! )
rcde1 CALL ctoken ; END ?
W tkend
CALL errnz ; no:
B $0E ; 14:END expected
rcdret RET ; "
rectest CMP.B vrecflg,#$00 ; test end
JZ rectest2 ; :normal rec
JMP cbrack2 ; 'in variant rec: ) ?
rectest2CALL ctoken ; END ?
W tkend
RET ; "
set CALL ctoken ; SET ?
W tkset
JNZ setret ; no: ret
CALL expof ; ! OF
CALL rdscaltp ; get scalar type
MOV AX,upper ; upper bound
MOV BX,lower ; lower bound
MOV.B CL,AH ; one of them > 255 ?
OR.B CL,BH
CALL errnz ; yes:
B $46 ; 70:Set base type out of range
MOV CL,#$03 ; calculate component size
SHR AX,CL ; (upper/8)-(lower/8)+1
MOV CL,#$03
SHR BX,CL
SUB AX,BX
INC AX
MOV varsize,AX ; -> component size
MOV AX,vartp ; type ptr
MOV lower,AX ; -> type
MOV.B varctp,#$03 ; tag: set
CALL stotype ; store type
setret RET ; "
pointer CALL cptr ; pointer: prelim def
JNZ ptrret ; ptr ? no: ret
MOV AX,#$0000 ; tag: invisible
CALL symword ; store in symtab
PUSH symtop ; save pos
CALL rdsymnew ; get name of ptr
CALL symoffs ; write symtab offset
POP lower ; restore pos of prelim offset
MOV.B varctp,#$04 ; tag: pointer
MOV.B varnest,#$FF ; flag: not fully defined
MOV varsize,#$0004 ; size: 4 bytes
CALL stotype ; store type
ptrret RET ; "
file CALL ctoken ; FILE ?
W tkfile
JNZ fileret ; no: ret
CALL ctoken ; OF ?
W tkof
JNZ filuntp ; no: untyped file
CALL rdtype ; read type
CMP.B varctp,#$05 ; component type = file ?
JB file2 ; :no
CMP.B varctp,#$07
JA file2 ; :no
CALL err ; yes:
B $44 ; '68:File components may not be files
file2 MOV AX,vartp ; type ptr
MOV lower,AX ; store it
MOV.B varctp,#$05 ; typed file
MOV varsize,#$004C ; set size
CALL stotype ; store type
fileret RET ; '
filuntp MOV BP,ptcbeg ; untyped file
B $81,$ED,$02,$00 ; point to it
JMP stdtype ; "standard type
text CALL ctoken ; TEXT ?
W tktext
JNZ textret ; no:ret
CALL csqr1 ; [ ?
JNZ textstd ; no: standard text file
CALL rdintcn ; get integer constant
OR BX,BX ; buffer size = 0 ?
CALL errz ; yes:
B $2D ; 45:Constant out of range
CALL esqr2 ; ! ]
ADD BX,#$4C ; add size of file var
MOV varsize,BX ; -> var size
MOV.B varctp,#$06 ; text file
CALL stotype ; store type
textret RET ; '
textstd MOV BP,ptcbeg ; set ptr to std TEXT file
B $81,$ED,$3E,$00
JMP stdtype ; 'standard type
string CALL ctoken ; STRING ?
W tkstr
JNZ strrt ; no: ret
CALL esqr1 ; ! [
CALL rdintcn ; get integer constant
OR.B BH,BH ; > 255 ?
CALL errnz ; yes:
B $31 ; 49:Invalid string length
OR.B BL,BL ; length = 0 ?
CALL errz ; yes:
B $31 ; 49:Invalid string length
CALL esqr2 ; ! ]
INC BX ; len+1 (for length byte)
MOV varsize,BX ; -> component size
MOV.B varctp,#$08 ; tag: string
CALL stotype ; store type
strrt RET ; "
scalar CALL cbrack1 ; do scalar list
JNZ scalret ; ( ? no: ret
MOV BX,#$FFFF ; init counter
scallp PUSH BX
MOV AX,#$0200 ; tag: const
CALL symword ; store tag word
CALL rdsym ; read symbol
MOV AL,scalcnt ; number of scalar type
CALL symbyte ; store (elementary type)
POP AX ; restore counter
INC AX ; inc it
PUSH AX ; save it again
CALL symword ; store value of that const
CALL symoffs ; write symtab offset
CALL ccomma ; , ?
POP BX
JZ scallp ; yes: another element
CALL ebrack2 ; ! )
MOV.B CL,scalcnt ; number of this type
INC.B scalcnt ; count scalar types
XOR DX,DX ; clear lower bound
scalsto MOV.B varctp,CL ; store component type
MOV upper,BX ; store upper bound
MOV lower,DX ; store lower bound
OR.B DH,BH ; byte possible ?
MOV AX,#$0001 ; size = 1
JZ scalbyt ; yes: ok
INC AX ; size = 2
scalbyt MOV varsize,AX ; store component size
CALL stotype ; store type
scalret RET ; "
subrangeCALL rdconst ; do subrange: read constant
JNZ subret ; no good: ret
PUSH CX ; save type, result
PUSH BX
CMP.B CL,#$0A ; scalar ?
CALL errb ; no:
B $33 ; 51:Invalid subrange base type
CALL ctoken ; .. ?
W tk2dot
CALL errnz ; no:
B $0B ; 11: .. expected
CALL rdnumcn ; get constant
POP DX ; restore component type
POP AX ; restore lower bound
CMP.B CL,AL ; same component type ?
CALL errnz ; no:
B $2C ; 44:Type mismatch
CMP BX,DX ; upper >= lower ?
JGE scalsto ; yes: ok
CALL err ; 52:Lower bound > upper bound
B $34 ; '
subret RET ; "
rdscaltpCALL subrange ; get scalar type: do subrange
JZ rdscret ; done: ret
CALL scalar ; do scalar list
JZ rdscret ; done: ret
CALL srchtype ; search type
CALL errnz ; complex ?
B $1E ; 30:Simple type expected
CMP.B varctp,#$0A ; scalar ?
JNB rdscret ; :yes
CALL err ; 30:Simple type expected
B $1E ; '
rdscret RET ; "
progpartMOV.B stklev,#$00 ; Program part
MOV.B withnest,#$00 ; no with nesting
CALL block ; do block
PUSH pc ; save PC: jump to end of exit codes
CALL ejump ; emit jump
MOV BP,symtop2
prpatch CMP BP,symtop ; = actual symtab top ?
JZ prpend ; yes: end it
MOV BX,[BP]-$02 ; get dest addr
OR BX,BX ; exit ?
JZ prpexit ; :yes
SS:
MOV AX,[BX]-$02 ; get dest offset (from label)
SS:
MOV.B CH,[BX]-$04 ; label defined ?
CMP.B CH,#$FF ; no:
CALL errz ; 40:Undefined label
B $28
JMP.b prpgoto ; 'patch GOTO
prpexit POP AX ; get PC of end jump
PUSH AX ; = dest
MOV CH,#$00 ; level: 0
prpgoto MOV BX,[BP]-$04 ; get addr of jump
INC BX ; +1: point to offset
MOV.B CL,[BP]-$05 ; stack level-dest stack level
SUB.B CL,CH
JNZ prpremov ; not zero: remove from stack
CALL ptcjmp ; patch jump
JMP.b prpnxt ; '
prpremovCALL errb ; trying to jump into a WITH/FOR ?
B $47 ; 71:Invalid GOTO
PUSH AX ; save PC of end jump
CALL ptcjmppc ; patch jump to current pos
prpremlpCALL epopax ; * POP AX
DEC.B CL ; remove another var from stack ?
JNZ prpremlp ; :no
POP AX ; restore PC of end jump
CALL ejump ; emit jump to end jump
prpnxt SUB BP,#$05 ; go to next entry
JMP prpatch ; 'next one
prpend POP BX ; restore PC of end jump
INC BX ; point to offset
JMP ptcjmppc ; "patch jump
block CALL statemnt ; do block: do statement
CALL ctoken ; END ?
W tkend
JZ blkret ; yes: done
CALL esemi2 ; !
JMP block ; 'next statement
blkret RET ; "
statemntMOV.B semiflg,#$FF ; Do statement
MOV AX,direct ; get compiler directives
MOV direcsv,AX ; use copy during statement
TEST direcsv,#$0010 ; user interrupt ?
JZ stmnoint ; :no
MOV.B usrint,#$FF ; set flag: used
CALL ecode ; * INT 3
B $01,$CC
stmnointCALL ckey ; search keyword - code level
B $02
W tkbegin
JZ stmstd2 ; :found
CALL rdvar ; search var
JNZ stmproc ; :not found
JMP cassign ; 'do assignment
stmproc MOV CX,#$0500 ; search procedure
CALL search
JNZ stmlabel ; :not found
JMP cproc ; 'do procedure
stmlabelMOV CX,#$0100 ; search label
CALL search
JZ clabel ; :found
MOV CX,#$0600 ; search function
CALL search
JNZ stmstd ; :not found
JMP assgnvar ; 'do function: assign return var
stmstd CALL ckey ; search keyword: std procedures
B $02
W stdprocs
JZ stmstd2 ; :found
RET ; '
stmstd2 CS: ; jump to procedure compilation
JMP [BX] ; "routine
clabel CALL ecolon ; ! : - do label
MOV AL,lexnest ; lexical level
CMP.B AL,[BP]-$03 ; = that of label ?
CALL errnz ; no:
B $48 ; 72:Label not within current block
CMP.B [BP]-$04,#$FF ; label already defined ?
CALL errnz ; 43:Duplicate ID or label
B $2B
MOV AL,stklev ; store stack level
MOV.B [BP]-$04,AL ; in label entry
MOV AX,pc ; store offset
MOV [BP]-$02,AX
JMP statemnt ; "do statement
if CALL excond ; IF: evaluate condition
MOV AL,brnchop ; branch opcode
MOV AH,#$03 ; offset
CALL eword ; emit branch
PUSH pc ; save pos of jump
CALL ejump ; emit jump
CALL ctoken ; THEN ?
W tkthen
CALL errnz ; no:
B $11 ; 17:THEN expected
CALL statemnt ; do statement
CALL ctoken ; ELSE ?
W tkelse
JNZ ifnoelse ; :no
POP BX ; get pos of THEN-jump
PUSH pc ; save pos of second jump
CALL ejump ; emit second jump
INC BX ; point to offset
CALL ptcjmppc ; patch jump addr: to ELSE-part
CALL statemnt ; do statement
ifnoelsePOP BX ; restore pos
INC BX ; point to offset
JMP ptcjmppc ; "patch jump offset
while PUSH pc ; WHILE: save loop beg addr
CALL excond ; evaluate condition
MOV AL,brnchop ; branch opcode
MOV AH,#$03 ; offset
CALL eword ; emit branch
PUSH pc ; save pos of jump to end
CALL ejump ; emit it
CALL ctoken ; DO ? Bug: not checked
W tkdo ; (try it !!)
CALL statemnt ; do statement
POP BX ; pos of jump
POP AX ; pos of loop beg
CALL ejump ; emit jump to loop beg
INC BX ; point to offset
JMP ptcjmppc ; "patch jump to loop end
repeat PUSH pc ; REPEAT: save loop beg addr
reploop CALL statemnt ; do statement
CALL ctoken ; UNTIL ?
W tkuntil
JZ repend ; yes: end it
CALL esemi2 ; !
JMP reploop ; '
repend CALL excond ; evaluate condition
MOV AL,brnchop ; branch opcode
MOV AH,#$03 ; offset
CALL eword ; emit branch
POP AX ; restore pos: loop beg
JMP ejump ; "emit jump to loop beg
for MOV CX,#$0400 ; FOR
CALL search ; search loop var
CALL errnz ; not found:
B $29 ; 41:Unknown ID or syntax error
CALL getvprm ; get var parms
MOV.B indflg,#$00 ; not indexed
CMP.B indptflg,#$00 ; var indirect ?
JNZ forerr ; yes: error
CMP.B varctp,#$0A ; scalar ?
JNB forstrt ; :yes, ok
forerr CALL err ; 30:Simple type expected
B $1E ; '
forstrt CALL pushe1 ; push var entry
MOV AL,varctp ; save component type
PUSH AX
CALL eassign ; ! :=
CALL exprax ; expression -> AX
CALL epushax ; * PUSH AX
POP AX ; component type
PUSH AX
CMP.B AL,CL ; = type read ?
CALL errnz ; no:
B $2C ; 44:Type mismatch
CALL ckey ; TO or DOWNTO ?
B $04
W tkto
CALL errnz ; not found:
B $12 ; 18:TO or DOWNTO expected
MOV forptr,BX ; store direction pointer
CALL exprax ; expression -> AX
POP AX ; test component type
CMP.B AL,CL
CALL errnz ; 44:Type mismatch
B $2C
CALL ctoken ; DO ?
W tkdo
CALL errnz ; no:
B $0D ; 13:DO expected
CALL pope1 ; restore entry from stack
CALL ecode ; * POP CX
B $04,$59,$91,$2B,$C8 ; * XCHG CX,AX
MOV BX,forptr ; * SUB CX,AX
CS: ; get flag: pointer to table entry
MOV.B AL,[BX] ; get branch
CALL ebyte ; emit it
CALL ecode ; * branch offset
B $04,$03,$E9,$00,$00 ; * JUMP ....
PUSH pc ; save jump pos
CS:
MOV.B AL,[BX]$01 ; emit INC/DEC
CALL ebyte
CALL estore2 ; store var
PUSH pc ; save PC
CALL ecode ; * PUSH CX
B $01,$51
CALL pushe1 ; save var entry
PUSH forptr ; save for-ptr
INC.B stklev ; space used on stack (counter!)
CALL statemnt ; do statement
DEC.B stklev ; remove from stack
POP forptr ; restore FOR-ptr
CALL pope1 ; restore var entry
CALL ecode ; * POP CX
B $01,$59 ; (get counter var from stack)
MOV BX,forptr
CS:
MOV.B AL,[BX]$02 ; INC/DEC CX
CALL ebyte ; emit
PUSH pc ; save PC
CALL eword ; emit (dummy) branch to loop end
CS:
MOV.B DH,[BX]$03 ; INC or DEC var ?
MOV DL,#$FF ; word opcode
CMP varsize,#$01 ; test component size
JA forword ; :word
MOV DL,#$FE ; byte obcode
forword CALL einstr ; emit INC loop var
POP BX ; restore PC
POP AX ; restore PC: beg of loop
CALL ejump ; emit jump to loop beg
MOV AX,pc ; PC-dest-2
SUB AX,BX
DEC AX
DEC AX
MOV.B AH,AL ; -> offset
MOV AL,#$74 ; JZ
CALL ptcjmp2 ; patch branch: to loop end
POP BX ; restore addr: loop beg-2
DEC BX
DEC BX
JMP ptcjmppc ; "patch jump to loop end
case CALL exscal ; CASE: get scalar expression
MOV.B casectp,CL ; store CASE-type
CALL expof ; ! OF
XOR CX,CX ; clear counter: main
PUSH CX
caselp1 XOR CX,CX ; clear counter: sub
caselp2 PUSH CX ; save it
CALL cmpbound ; get element / lower bound
CALL ctoken ; .. ?
W tk2dot
MOV DL,#$74 ; JZ
JNZ casenrng ; :no
CALL ecode ; * JL +05
B $02,$7C,$05
CALL cmpbound ; get upper bound
MOV DL,#$7E ; JG
POP CX ; count: range done
INC.B CH ; count space used
PUSH CX
casenrngPOP CX ; restore counter
INC.B CH ; count space used
INC.B CL ; count labels
PUSH DX ; save opcode
PUSH pc ; save pos
CALL eword ; emit branch op
CALL ccomma ; , ?
JNZ caselab ; :no
CMP.B CH,#$14 ; branch distance ok ?
JB caselp2 ; :yes
CALL ecode ; * JMP +02
B $02,$EB,$02 ; make a hip
XOR.B CH,CH ; clear space used
casehip POP BX ; get branch pos
POP DX ; get opcode
MOV AX,pc ; PC-pos-2
SUB AX,BX
DEC AX
DEC AX
MOV.B AH,AL ; -> offset
MOV.B AL,DL ; branch opcode
CALL ptcjmp2 ; patch in branch
LOOP casehip ; :another one
MOV DL,#$EB ; JMP
PUSH DX ; remember opcode
PUSH pc ; & position
CALL eword ; emit it (dummy)
MOV CX,#$0101 ; space counter
JMP caselp2 ; 'continue
caselab CALL ecolon ; ! :
CALL ejump ; emit jump to next case label
XOR.B CH,CH ; clear space counter
caseres POP BX ; fill in branches - as above !
POP DX
MOV AX,pc
SUB AX,BX
DEC AX
DEC AX
MOV.B AH,AL
MOV.B AL,DL
CALL ptcjmp2 ; patch it
LOOP caseres ; :another
POP CX ; restore main counter
PUSH pc ; save current pos
INC CX ; count case labels
PUSH CX ; save again
MOV AL,casectp ; save type
PUSH AX ; (CASE may be nested !)
CALL statemnt ; do statement
POP AX ; restore type
MOV casectp,AL
CALL csemi ; semicolon ?
MOV DL,#$FF ; set flag
JZ casesemi ; :yes
XOR.B DL,DL ; clr flag
casesemiPUSH DX ; save it
CALL ctoken ; END ?
W tkend
POP DX ; restore flag
JZ caseres2 ; :yes
CALL ejump ; emit jump to end of CASE
POP CX ; counter
POP BX ; pos of last comparison
PUSH pc ; save pos of jump to CASE-end
PUSH CX ; save counter
DEC BX ; pos-2
DEC BX
CALL ptcjmppc ; patch jump
PUSH DX ; flag
CALL ctoken ; ELSE ?
W tkelse
POP DX ; 'flag
JZ caseelse ; :yes
OR.B DL,DL ; test flag
JZ caseend ; :no semicolon
JMP caselp1 ; 'case loop
caseend CMP.B semiflg,#$00 ; flag for semi error
CALL errz ; cleared:
B $0E ; 14:END expected
CALL err
B $29 ; "41:Unknown ID or syntax error
caseelseCALL statemnt ; ELSE-part: do statement
CALL ctoken ; END ?
W tkend
JZ caseres2 ; :yes
CALL esemi2 ; !
JMP caseelse ; '
caseres2POP CX ; number of jumps
caseres3POP BX ; addr of jump-2
DEC BX
DEC BX ; patch jumps to end of CASE
CALL ptcjmppc
LOOP caseres3 ; :another
RET ; "
cmpboundCALL rdnumcn ; get bound: read num constant
CMP.B CL,casectp ; correct type ?
CALL errnz ; 46:Constant and CASE selector type
B $2E ; does not match
MOV AL,#$3D ; * CMP AX,i
CALL ebyte
XCHG AX,BX ; emit constant: bound
JMP eword ; "
goto MOV CX,#$0100 ; do GOTO
CALL search ; search label
CALL errnz ; not found:
B $28 ; 40:Undefined label
MOV.B AL,[BP]-$03 ; lexical level
CMP.B AL,lexnest ; = current ?
CALL errnz ; no:
B $48 ; 72:Label not within current block
MOV AX,BP ; symtab-ptr of label
exit CALL symword ; store in symtab
MOV AX,pc ; store PC
CALL symword
MOV AL,stklev ; store stack level
CALL symbyte
JMP ejump ; "emit jump - resolved at block end
with MOV AL,withnest ; WITH: nesting level
MOV.B AH,stklev ; stack level
PUSH AX ; save them
withlp CMP.B withnest,#$10 ; too much ?
CALL errz ; yes:
B $61
CALL rdvar ; get var
CALL errnz ; not found:
B $29 ; 41:Unknown ID or syntax error
CMP.B varctp,#$02 ; type = record ?
CALL errnz ; no:
B $1D ; 29:Record variable expected
CMP.B indflg,#$00 ; indexed ?
JNZ withindx ; :yes
MOV.B AH,varseg ; segment and offset
MOV DX,varofs ; already known
JMP.b withsto ; 'store in WITH-buffer
withindxCALL varptr2 ; get var ptr
CALL epushdi ; * PUSH DI
ADD.B stklev,#$02 ; 4 bytes on stack
MOV.B AH,stklev ; get stack level
MOV DX,#$FFFF ; dummy pos: variable
withsto MOV.B BL,withnest
XOR.B BH,BH ; nesting level*4 -> BX
SHL BX,1 ; offset into WITH-buffer
SHL BX,1
MOV AL,varnest ; store type, position
MOV [BX]withtab,AX ; -> WITH-buffer
MOV [BX]withtab1,DX
INC.B withnest ; inc nesting level
CALL ccomma ; , ?
JZ withlp ; yes: repeat
CALL ctoken ; DO ?
W tkdo
CALL errnz ; no:
B $0D ; 13:DO expected
CALL statemnt ; do statement
MOV.B CL,stklev ; stack usage
POP AX ; restore stack usage, nesting level
MOV withnest,AL
MOV.B stklev,AH
SUB.B CL,AH ; remove from stack ?
JZ withret ; :nothing to remove
MOV AX,#$C483 ; * ADD SP,...
CALL eword
MOV.B AL,CL ; var count * 2
SHL.B AL,1
CALL ebyte ; emit byte
withret RET ; "
inline CALL ebrack1 ; ! ( - Inline
inllp MOV.B inlinflg,#$02 ; flag: byte mode
MOV AL,#$3E
CALL chkal ; > ?
JZ inltp ; :yes
MOV.B inlinflg,#$01 ; word mode
MOV AL,#$3C ; < ?
CALL chkal
JZ inltp ; :yes
MOV.B inlinflg,#$00 ; normal mode
inltp XOR BX,BX ; clear number
XOR CX,CX ; clear neg flag
inlexlp PUSH BX ; save
PUSH CX
CALL rdconst ; read num constant
JNZ inlnocn ; :no good
CMP.B CL,#$0A ; type = integer ?
CALL errnz ; no:
B $16 ; 22:Integer constant expected
XCHG AX,BX ; result -> AX
JMP.b inlatom ; '
inlnocn CMP.B inlinflg,#$00 ; normal mode ?
JNZ inlpc ; :no
MOV.B inlinflg,#$02 ; set word mode
inlpc MOV AL,#$2A ; * ?
CALL chkal
JNZ inlvar ; :no
MOV AX,pc ; PC -> result
JMP.b inlatom ; '
inlvar MOV CX,#$0400 ; search var
CALL search
JNZ inlproc ; :not found
CALL getvprm ; get var parms
MOV AX,varofs ; get var offset
JMP.b inlatom ; '
inlproc MOV CX,#$0500 ; search proc
CALL search
JZ inlproc2 ; :found
MOV CX,#$0600 ; search func
CALL search
CALL errnz ; not found:
B $4A ; 74:Inline error
inlproc2MOV AX,[BP]-$08 ; get offset
inlatom POP CX ; restore
POP BX
JCXZ inlnoneg ; :no negation
NEG AX ; negate result
inlnonegADD BX,AX ; add to number
XOR CX,CX ; clear neg flag
MOV AL,#$2B ; + ?
CALL chkal
JZ inlexlp ; :yes
DEC CX ; set neg flag
MOV AL,#$2D ; - ?
CALL chkal
JZ inlexlp ; :yes
XCHG AX,BX ; else: end of expression
CMP.B inlinflg,#$01 ; byte mode ?
JA inlword ; :word mode
JZ inlbyte ; :byte mode
OR.B AH,AH ; normal mode: result > 255 ?
JNZ inlword ; :yes
inlbyte CALL ebyte ; emit byte
JMP.b inlchk ; '
inlword CALL eword ; emit word
inlchk MOV AL,#$2F ; / ?
CALL chkal
JNZ inlend ; no: end it
JMP inllp ; 'loop back: next expression
inlend CALL ebrack2 ; ! )
RET ; "
cproc CALL estkchk ; procedure call: stack checking
cproc2 PUSH BP ; save symtab pos
MOV.B CL,[BP]-$10 ; parameter count
SUB BP,#$10 ; go to parameter
OR.B CL,CL ; any parms ?
JNZ cprparms ; :yes
JMP cprnoprm ; 'none - end it
cprparmsCALL ebrack1 ; ! (
cprlp1 PUSH CX ; parameter loop
PUSH BP ; save counter, symtab pos
MOV CX,[BP]-$04 ; parm count same type
MOV BP,[BP]-$02 ; type ptr
CALL getvprm2 ; get type parms
cprlp2 PUSH CX ; save counter
OR.B CH,CH ; VAR-parameter ?
JNZ cprvar ; :yes
CMP.B varctp,#$03 ; set ?
JB cprcpl ; :below, complex var
CALL exprsave ; get expression -> AX/stack
CALL typechk ; type checking, conversions
CALL erngchk ; range check
CMP.B varctp,#$0A ; scalar ?
JNB cprscal ; :yes
CMP.B varctp,#$04 ; ptr ?
JNZ cprstr ; :no
CALL ecode ; * PUSH DX
B $01,$52
cprscal CALL epushax ; * PUSH AX
JMP.b cprnext ; 'next parm
cprstr CMP.B varctp,#$08 ; string ?
JNZ cprset ; :no
MOV.B AH,varsize ; get max length
DEC.B AH
MOV AL,#$B1
CALL eword ; * MOV CL,max_len
MOV AX,#xstrparm ; adapt string size
CALL ecall ; * CALL xstrparm
JMP.b cprnext ; 'next parm
cprset CMP.B varctp,#$03 ; set ?
JNZ cprnext ; no:next parm
CALL esetfac ; calc set crunch factor
MOV AX,#xsetparm ; adapt set size
CALL ecall ; * CALL xsetparm
JMP.b cprnext ; 'next parm
cprcpl CALL pushe1 ; save var entry: complex var
CALL fullvar ; do full var
CALL errnz ; no good:
B $29 ; 41:Unknown ID or syntax error
CALL varptr2 ; get var ptr
MOV AX,varsize ; component size
CALL emovcxi ; * MOV CX,compo_size
MOV AX,#xblkparm ; copy complex var to stack
CALL ecall ; * CALL xblkparm
JMP.b cprchk ; 'check type
cprvar CALL pushe1 ; save entry - VAR-parameter
CALL varptr ; get var ptr
CALL epushdi ; * PUSH DI
cprchk CALL copye2 ; copy entry from stack
CMP.B var2ctp,#$00 ; type = 0 ?
JZ cpruntyp ; yes: untyped
CALL tchkstrc ; type checking struct vars
cpruntypCALL pope1 ; restore entry
cprnext POP CX ; restore counter
DEC.B CL ; another parm of same type ?
JZ cprskip ; :no
CALL ecomma ; ! ,
JMP cprlp2 ; 'loop back - same type
cprskip POP BP ; restore symtab pos
SUB BP,#$04 ; go to next parm
MOV.B AL,[BP]$00 ; get cnt
cprsklp DEC BP
MOV.B CL,[BP]$00 ; skip var entries
XOR.B CH,CH
SUB BP,CX ; skip name
DEC.B AL ; count down
JNZ cprsklp ; :another
POP CX ; restore count
DEC.B CL ; another parameter ?
JZ cprend ; :no
CALL ecomma ; ! ,
JMP cprlp1 ; 'loop back
cprend CALL ebrack2 ; ! )
cprnoprmPOP BP ; symtab pos
MOV AX,[BP]-$0E ; overlay ?
OR AX,AX
JZ cprnoovr ; :no
CALL emovaxi ; * MOV AX,proc_len
MOV AX,[BP]-$0C ; get file offset
CALL emovdxi ; * MOV DX,file_offs
cprnoovrMOV AX,[BP]-$08 ; get proc offset
JMP ecall ; "* CALL proc
estkchk TEST direcsv,#$0020 ; Stack checking ?
JZ estkret ; :switched off
MOV AX,[BP]-$0A ; get space needed
CALL emovcxi ; * MOV CX,space_needed
MOV AX,#xchkstk
CALL ecall ; * CALL xchkstk
estkret RET ; "
cassign CMP.B varctp,#$00 ; Do assignment: untyped ?
JZ asnerr ; yes: error
CMP.B varctp,#$05 ; file var ?
JB asnscal ; :no
CMP.B varctp,#$07
JA asnscal ; :no
asnerr CALL err ; files cannot be assigned !
B $36 ; '54:Illegal assignment
asnscal CMP.B varctp,#$0A ; scalar ?
JNB asnscal2 ; :yes
CMP.B varctp,#$04 ; pointer ?
JNZ asnvar ; :no
asnscal2CMP.B varseg,#$FD ; segment = ES ?
JNZ asnnoseg ; :no
MOV.B flgpshes,#$01 ; set flag: PUSH ES
asnnosegCMP.B indflg,#$00 ; indexed ?
JZ asn2 ; :no
MOV.B flgpshdi,#$01 ; set flag: PUSH DI
JMP.b asn2 ; '
asnvar CALL varptr2 ; get var ptr
CALL epushdi ; * PUSH DI
asn2 CALL eassign ; ! :=
CMP.B varctp,#$03 ; set ?
JB asncpl ; :no, complex type
CALL pushe1 ; save dest var entry
PUSH pc ; save PC
CALL expr ; evaluate expression
POP AX ; old = new PC ?
SUB AX,pc
MOV flgpshes,#$0000 ; clear flag: PUSH ES
PUSH AX ; save difference
CALL expload ; get expression, ready for store
POP AX
CALL pope1 ; restore dest var entry
PUSH AX ; save diff
CALL typechk ; type checking, conversions
POP AX
CMP.B varctp,#$0A ; scalar ?
JNB asnscal3 ; :yes
CMP.B varctp,#$04 ; pointer ?
JNZ asnstore ; :no
asnscal3OR AX,AX ; test diff
JZ asnstore ; none: ok
CMP.B indflg,#$00 ; indexed ?
JZ asnnoind ; :no
CALL ecode ; * POP DI - restore ptr
B $01,$5F
asnnoindCMP.B varseg,#$FD ; segment = ES ?
JNZ asnstore ; :no
CALL ecode ; * POP ES
B $01,$07
asnstoreJMP estore ; 'Store var
asncpl CALL pushe1 ; store dest var entry
CALL fullvar ; do full var
CALL errnz ; no good:
B $29 ; 41:Unknown ID or syntax error
CALL varptr2 ; get var ptr
CALL pope2 ; restore dest var entry
CALL tchkstrc ; type checking - struct vars
MOV AX,var2size ; component size
CALL emovcxi ; * MOV CX,comp_size
MOV AX,#xmovevar
JMP ecall ; '* CALL xmovevar
typechk CMP.B varctp,#$09 ; Type checking, conversions
JNZ tckstr ; :no real
CMP.B CL,#$0A ; now integer ?
JNZ tckchk ; :no
MOV AX,#xintreal ; conversion int -> real
CALL ecall ; * CALL xintreal
MOV CL,#$09 ; now real
JMP.b tckchk ; '
tckstr CMP.B varctp,#$08 ; string expected ?
JNZ tckch ; :no
CMP.B CL,#$0C ; result = char ?
JNZ tckchk ; :no
CALL ecode ; * MOV AH,AL
B $05,$8A,$E0,$B0,$01,$50 ; * MOV AL,01
MOV CL,#$08 ; * PUSH AX
JMP.b tckchk ; 'now string
tckch CMP.B varctp,#$0C ; char expected ?
JNZ tckchk ; :no
CMP.B CL,#$08 ; result = string ?
JNZ tckchk ; :no
MOV AX,#xstrch ; convert to char
CALL ecall ; * CALL xstrch
MOV CL,#$0C ; now char
tckchk CMP.B CL,varctp ; type = expected ?
JNZ tckerr ; no: error
CMP.B CL,#$03 ; set ?
JNZ tckptr ; :no
OR.B CH,CH ; constant ?
JZ tckret ; yes: ret
MOV BP,lower ; same base type ?
CMP.B CH,[BP]-$08
JZ tckret ; yes: ret
tckerr CALL err ; 44:Type mismatch
B $2C ; '
tckptr CMP.B CL,#$04 ; pointer ?
JNZ tckret ; no: ret
MOV AX,functp ; type pointer
OR AX,AX ; untyped ?
JZ tckret ; yes: ret
CMP AX,lower ; compare types
JNZ tckerr ; different: error
tckret RET ; "
tchkstrcMOV AL,varctp ; Type checking struct vars
CMP AL,#$00 ; type expected: untyped ?
JZ tcserr ; yes: type mismatch
MOV DL,#$BF ; check everything
CMP AL,#$0A ; scalar ?
JNB tcschk ; yes: check it
CMP AL,#$08 ; string ?
JNZ tcsnostr ; :no
MOV DL,#$80 ; check type only
TEST direcsv,#$0040 ; string checking ?
JZ tcschk ; :no
tcsnostrMOV DL,#$83 ; check type, component size
CMP AL,#$06 ; text, untyped file ?
JNB tcschk ; :check it
MOV DL,#$B3
CMP AL,#$03 ; set ?
JNB tcschk ; :check it
MOV DL,#$C3 ; check type, rec nest, size
CMP AL,#$02 ; record ?
JNB tcschk ; :check it
CMP upper,#$00 ; second type
MOV DL,#$BF ; check everything
JNZ tcschk ; :check it
MOV BP,upper2 ; second type
CMP.B [BP]-$08,#$0A ; scalar index ?
JNZ tcserr ; no: type mismatch
MOV DL,#$B3 ; check type, lower, upper, size
tcschk MOV SI,#varctp ; check buffers
MOV DI,#var2ctp
MOV CX,#$0008 ; check 8 bytes
tcschklpROL.B DL,1 ; test this byte ?
JNB tcsnochk ; :no
MOV.B AL,[SI] ; compare
CMP.B AL,[DI]
JNZ tcserr ; different: error
tcsnochkINC SI ; next byte
INC DI
LOOP tcschklp ; :another
RET ; '
tcserr CALL err ; 44:Type mismatch
B $2C ; "
assgnvarCALL getvprm ; get var parms
CMP.B indptflg,#$00 ; indirect ?
CALL errnz ; yes:
B $29 ; 41:Unknown ID or syntax error
CALL rdvarind ; do indexing
JMP cassign ; "do assignment
passign CALL brfilvar ; ASSIGN: get file var
MOV AX,#xassign ; normal Assign
CMP.B CL,#$06 ; text file ?
JNZ pasgn2 ; :no
MOV AX,#xassgntx ; assign text file
CALL pasgn2 ; get params
JMP.b pfilchk ; 'do checking
pasgn2 PUSH AX ; save call addr
CALL epushdi ; * PUSH DI
CALL ecomma ; ! ,
CALL exstr ; get string expr
POP AX ; restore call addr
pemit PUSH AX ; save call addr
CALL ebrack2 ; ! )
POP AX ; call addr
JMP ecall ; "emit call
prename CALL brfilvar ; RENAME: get file var
MOV AX,#xrename
CALL pasgn2 ; get string, emit call
JMP.b pfilchk ; "do checking
perase CALL brfilvar ; ERASE: get file var
MOV AX,#xerase
JMP.b pfilend ; 'put it
pchain MOV AX,#xchain ; CHAIN
JMP.b pexec2 ; 'put it
pexecuteMOV AX,#xexecute ; EXECUTE
pexec2 PUSH AX ; save addr
CALL brfilvar ; get file var
pexec3 POP AX ; restore addr
pfilend CALL pemit ; test ), emit call
pfilchk JMP efilchk ; "do checking
pseek CALL brfilvar ; SEEK, LONGSEEK: get file var
CMP.B CL,#$06 ; text file ?
CALL errz ; yes:
B $3F ; 63:Textfiles are not allowed here
CALL epushdi ; * PUSH DI
CALL ecomma ; ! ,
CALL exnum ; get num expr
MOV AX,#xseek
CMP.B CL,#$0A ; integer ?
JZ pseekint ; :yes
MOV AX,#xlngseek ; real...
pseekintJMP pfilend ; "test ), emit call, check
pappend CALL brfilvar ; APPEND: get file var
CMP.B CL,#$06 ; text file ?
CALL errnz ; no:
B $23 ; 35:Textfile expected
MOV AX,varsize ; component size
SUB AX,#$004C ; sub size of file var
CALL emovcxi ; * MOV CX,buf_size
MOV AX,#xappndtx
JMP pfilend ; "test ), emit call, check
preset MOV BX,#vreset ; RESET
JMP.b prw2 ; 'ptr into vector table
prewriteMOV BX,#vrewrite ; REWRITE
prw2 PUSH BX ; save ptr
CALL brfilvar ; get file var
CMP.B varctp,#$05 ; typed file ?
JNZ prwtext ; :no
MOV BP,lower ; type ptr
CALL getparm ; get var parms
MOV AX,var2size ; component size
CALL emovcxi ; * MOV CX,compo_size
JMP.b prwemit ; '
prwtext CMP.B varctp,#$06 ; text file ?
JNZ prwuntp ; :no
MOV AX,varsize ; var size-base size
SUB AX,#$004C
CALL emovcxi ; * MOV CX,buf_size
JMP.b prwemit ; '
prwuntp CALL epushdi ; * PUSH DI - untyped file
CALL ccomma ; , ?
JNZ prwuntp2 ; :no
CALL pushe1 ; save var entry
CALL exint ; get int expr: block size
CALL pope1 ; restore var entry
JMP.b prwemit ; '
prwuntp2MOV AX,#$0080 ; std blk size
CALL emovaxi ; * MOV AX,0080
prwemit POP BX ; restore table index
JMP.b pclose3 ; '
ptruncatMOV BX,#vtruncat ; TRUNCATE
JMP.b pclose2 ; '
pflush MOV BX,#vflush ; FLUSH
JMP.b pclose2 ; '
pclose MOV BX,#vclose ; CLOSE
pclose2 PUSH BX ; save table index
CALL brfilvar ; get file var
POP BX ; restore index
pclose3 CALL ebrack2 ; ! )
CALL ecallio ; emit call
JMP pfilchk ; "do checking
ecallio MOV AL,varctp ; emit call for I/O
SUB AL,#$05 ; (component type-5)*2
XOR.B AH,AH
ADD AX,AX
ADD BX,AX ; + BX -> pointer
CS:
MOV AX,[BX] ; get vector
JMP ecall ; "emit call
vreset W xresetty,xresettx,xreset ; vector table for I/O
vrewriteW xrewrtty,xrewrttx,xrewrtun
vclose W xclosety,xclosetx,xclosety
vflush W xflushty,xflush,xflushty
vtruncatW xtruncat,xtrunctx,xtrunc ; "
brfilvarCALL ebrack1 ; ! (
CALL rdfilvar ; get file var
JZ brfilret ; ok:ret
CALL err
B $15 ; '21:File variable expected
brfilretRET ; "
rdfilvarCALL rdvar ; get file var: get var
JZ rdfil2 ; :found
STC ; not found
RET ; '
rdfil2 CMP.B varctp,#$05 ; file var ?
JB rdfilnf ; :no
CMP.B varctp,#$07
JA rdfilnf ; :no
CALL varptr2 ; get var ptr
MOV.B CL,varctp ; get type
XOR AX,AX ; ok
rdfilnf CLC
RET ; "
preadln MOV AL,#$FF ; READLN: set flag
JMP.b prd1 ; '
pread XOR.B AL,AL ; READ
prd1 MOV rdlnflg,AL ; store flag
CALL cbrack1 ; ( ?
JZ prdfil ; :yes
CALL setinp ; set input path
JMP prdrdln ; 'do checking
prdfil CALL rdfilvar ; get file var
JB prderr ; :error
JNZ prdsetin ; no file var: use std
CMP.B CL,#$05 ; typed file ?
JNZ prdtext ; :no
JMP prdtyped ; 'do read typed file
prdtext CMP.B CL,#$06 ; text file ?
CALL errnz ; no:
B $41 ; 65:Untyped files are not allowed here
MOV AX,#xrdfil ; (prepare for input)
CALL ecall ; * CALL xrdfil
JMP prdnxt ; '
prderr CALL err ; 41:Unknown ID or syntax error
B $29 ; '
prdsetinCALL setinp ; set input path
JMP.b prdlp2 ; '
prdloop CALL rdvar ; parameter loop: read var
CALL errnz ; not found:
B $29 ; 41:Unknown ID or syntax error
prdlp2 CALL varptr2 ; get var ptr
MOV.B CL,varctp ; component type
CMP.B CL,#$01 ; array ?
JZ prdarrch ; yes: read as byte block
CMP.B CL,#$08 ; file, ptr, set, record ?
JB prderrtp ; :not allowed
CMP.B CL,#$0B ; boolean ?
JZ prderrtp ; :not allowed
CMP.B CL,#$0D ; scalar ?
JB prdstr ; below: ok
prderrtpCALL err ; 66:I/O not allowed here
B $42 ; '
prdarrchMOV BP,lower ; type ptr - test array of char
CMP.B [BP]-$08,#$0C ; component type = char ?
JNZ prderrtp ; no:error
MOV BP,upper ; index type = integer ?
CMP.B [BP]-$08,#$0A
JNZ prderrtp ; no:error
MOV AX,varsize ; component size
OR.B AH,AH ; > 255 ?
JNZ prderrtp ; yes:error
MOV.B AH,AL ; * MOV CL,len
MOV AL,#$B1
CALL eword
MOV AX,#xrdarrch ; (read array of char)
JMP.b prdemit ; '
prdstr CMP.B CL,#$08 ; string ?
JNZ prdnum ; :no
MOV.B AH,varsize ; max len-1
DEC.B AH
MOV AL,#$B1
CALL eword ; * MOV CL,max_len
MOV AX,#xrdstr ; (read string)
JMP.b prdemit ; '
prdnum MOV AX,#xrdreal ; (read real)
CMP.B CL,#$09 ; real ?
JZ prdemit ; :yes
MOV AX,#xrdchar ; (read char)
CMP.B CL,#$0C ; char ?
JZ prdemit ; :yes
MOV AX,#xrdint ; (read integer)
CMP varsize,#$01 ; word ?
JA prdemit ; :yes
MOV AX,#xrdbyte ; (read byte)
prdemit CALL ecall ; emit call
prdnxt CALL ccomma ; another var ?
JNZ prdend ; no: end it
JMP prdloop ; 'parameter loop
prdend CALL ebrack2 ; ! )
prdrdln MOV AX,#xreadln ; (readln)
prdln CMP.B rdlnflg,#$00 ; ReadLn ?
JZ efilchk ; :no
CALL ecall ; emit call
efilchk TEST direcsv,#$0001 ; do I/O-checking ?
JZ prdret ; :no
MOV AX,#xiochk ; (I/O-check)
CALL ecall
prdret RET ; "
setinp MOV AX,#xstdin ; set input path: std input
CMP cinpsize,#$00 ; buffer > 0 ?
JNZ setinprt ; yes: do it
TEST direcsv,#$0004 ; test I/O-mode
JZ setinprt ; :MS-DOS std input
MOV AX,#xrdln ; (read with editing)
CMP.B rdlnflg,#$00 ; ReadLn ?
JZ setinprt ; :no
MOV.B rdlnflg,#$00 ; clear flag
MOV AX,#xrd ; (Readln with editing)
setinprtJMP ecall ; "emit call
pwritelnMOV AL,#$FF ; WRITELN
JMP.b pwr1 ; '
pwrite XOR.B AL,AL ; WRITE
pwr1 MOV rdlnflg,AL ; set flag
CALL cbrack1 ; ( ?
JZ pwrfil ; yes: ok
MOV AX,#xstdout ; set std output
CALL ecall ; emit call
JMP pwrend2 ; 'end it
pwrfil CALL rdfilvar ; get file var
JB pwrstd2 ; :none
JNZ pwrstd ; no file var
CMP.B CL,#$05 ; typed file ?
JNZ pwrtext ; :no
JMP pwrtyped ; 'write typed file
pwrtext CMP.B CL,#$06 ; text file ?
CALL errnz ; no:
B $41 ; 65:Untyped files are not allowed here
MOV AX,#xwrfil ; (prepare for write)
CALL ecall ; * CALL xwrfil
JMP pwrnext ; '
pwrstd MOV AX,#xstdout ; set std output
CALL ecall ; * CALL xstdout
CALL expvar ; expression (var pre-read)
JMP.b pwrstr ; 'check type
pwrstd2 MOV AX,#xstdout ; set std output
CALL ecall ; * CALL xstdout
pwrloop CALL constel ; get const element
JNZ pwrexpv ; :no, try expression
CMP.B CL,#$08 ; string ?
JNZ pwrexpc ; :no
MOV DI,chptr ; check current char
CMP.B [DI],#$2C ; , ?
JZ pwrinlin ; :yes
CMP.B [DI],#$29 ; ) ?
JNZ pwrexpc ; no: normal
pwrinlinMOV AX,#xwrtinl ; (write inline string)
CALL ecall ; * CALL xwrtinl
CALL estring ; emit string
JMP.b pwrnext ; 'get next element
NOP
pwrexpc CALL expconst ; expression (const pre-read)
JMP.b pwrstr ; 'check type
pwrexpv CALL exprax ; expression (var pre-read)
pwrstr CMP.B CL,#$08 ; string ?
JB pwrerrtp ; below:illegal
CMP.B CL,#$0D ; scalar ?
JB pwrint ; below: ok
pwrerrtpCALL err ; 66:I/O not allowed here
B $42 ; '
pwrint CMP.B CL,#$0A ; integer ?
JB pwrnoint ; below: real, string
CALL epushax ; * PUSH AX - stack it
pwrnointCALL ccolon ; : ?
JNZ pwrdef ; no:default format
PUSH CX ; save type
CALL exint ; get int expression
POP CX
CMP.B CL,#$09 ; real ?
JNZ pwrchk ; no: no second parm
CALL ccolon ; : ?
JNZ pwrdef3 ; no: use default
CALL epushax ; * PUSH AX
PUSH CX ; save type
CALL exint ; get int expression
POP CX
JMP.b pwrchk ; 'continue
pwrdef XOR AX,AX ; standard format
CMP.B CL,#$09 ; real ?
JNZ pwrdef2 ; :no
MOV AL,#$12 ; default: 18 chars
pwrdef2 CALL emovaxi ; * MOV AX,field_len
CMP.B CL,#$09 ; real ?
JNZ pwrchk ; :no
pwrdef3 CALL epushax ; * PUSH AX
MOV AX,#$FFFF
CALL emovaxi ; * MOV AX,#FFFF
pwrchk MOV AX,#xwrtstr ; (write string)
CMP.B CL,#$08 ; string ?
JZ pwremit ; :yes
MOV AX,#xwrreal ; (write real)
CMP.B CL,#$09 ; real ?
JZ pwremit ; :yes
MOV AX,#xwrint ; (write integer)
CMP.B CL,#$0A ; integer ?
JZ pwremit ; :yes
MOV AX,#xwrbool ; (write boolean)
CMP.B CL,#$0B ; boolean ?
JZ pwremit ; :yes
MOV AX,#xwrchar ; (write char)
pwremit CALL ecall ; emit call
pwrnext CALL ccomma ; , ?
JNZ pwrend ; no: end it
JMP pwrloop ; 'do param loop
pwrend CALL ebrack2 ; ! )
pwrend2 MOV AX,#xwrln ; (writeln)
JMP prdln ; "like read/readln
prdtypedMOV AX,#xrdvar ; Read typed file
JMP.b pwt2 ; '(read byte block)
pwrtypedMOV AX,#xwrvar ; Write typed file
pwt2 CMP.B rdlnflg,#$00 ; ReadLn/WriteLn ?
CALL errnz ; yes:
B $23 ; 35:Textfile expected
PUSH AX ; save addr
MOV AX,#xfilsel ; (select file)
CALL ecall ; * CALL xfilsel
MOV BP,lower ; type ptr
CALL getparm ; get type parms
pwtloop CALL ccomma ; , ?
JNZ pwtend ; no: end it
CALL pushe2 ; push entry
CALL varptr ; get var ptr
CALL pope2 ; pop entry
CALL tchkstrc ; check type struct vars
POP AX ; get routine addr
PUSH AX
CALL ecall ; * CALL xwrvar / xrdvar
JMP pwtloop ; 'parameter loop
pwtend POP AX ; remove
CALL ebrack2 ; ! )
JMP efilchk ; "do checking
pblockrdMOV AX,#xblkrdrd ; BLOCKREAD
MOV BX,#xblkrd ; with / without res var
JMP.b pbl1 ; '
pblockwrMOV AX,#xblkwrrs ; BLOCKWRITE
MOV BX,#xblkwr
pbl1 PUSH AX ; save addr
PUSH BX
CALL brfilvar ; get file var
CMP.B CL,#$07 ; untyped ?
CALL errnz ; no:
B $25 ; 37:Untyped file expected
CALL epushdi ; * PUSH DI
CALL ecomma ; ! ,
CALL varptr ; get var ptr (src/dst var)
CALL epushdi ; * PUSH DI
CALL ecomma ; ! ,
CALL exint ; get int expr (count)
CALL ccomma ; , ?
POP BX ; restore addr
POP AX
JZ pblres ; yes: with result var
PUSH BX ; without result var
JMP.b pblend ; '
pblres PUSH AX ; save routine addr
CALL epushax ; * PUSH AX (save count)
CALL varptr ; get res var ptr
CMP.B varctp,#$0A ; integer ?
JNZ pblerr ; no: error
CMP varsize,#$02 ; word ?
JZ pblend ; yes: ok
pblerr CALL err ; 24:Integer variable expected
B $18 ; '
pblend JMP pexec3 ; "emit call, check end
pchdir MOV AX,#xchdir ; CHDIR
JMP.b prmd2 ; '
pmkdir MOV AX,#xmkdir ; MKDIR
JMP.b prmd2 ; '
prmdir MOV AX,#xrmdir ; RMDIR
prmd2 PUSH AX ; save addr
CALL ebrack1 ; ! (
CALL exstr ; get string expr
JMP pexec3 ; "complete it
pgetdir CALL ebrack1 ; ! ( : GETDIR
CALL exint ; get int expr
CALL epushax ; * PUSH AX
CALL ecomma ; ! ,
CALL rdstrvar ; get string var
MOV.B AH,varsize ; max len-1
DEC.B AH
MOV AL,#$B1
CALL eword ; * MOV CL,max_len
MOV AX,#xgetdir
JMP pemit ; "end it
povrpathCALL ebrack1 ; ! ( : OVRPATH
CALL exstr ; get string expr
MOV AX,#xovrpath
JMP pemit ; "end it
pdelete CALL ebrack1 ; ! ( : DELETE
CALL rdstrvar ; get string var
CALL epushdi ; * PUSH DI
CALL ecomma ; ! ,
CALL exint ; get int expr
CALL epushax ; * PUSH AX
CALL ecomma ; ! ,
CALL exint ; get int expr
MOV AX,#xdelete
JMP pemit ; "end it
pinsert CALL ebrack1 ; ! ( : INSERT
CALL exstr ; get string expr
CALL ecomma ; ! ,
CALL rdstrvar ; get string var
CALL epushdi ; * PUSH DI
MOV.B AH,varsize ; component size - 1
DEC.B AH
MOV AL,#$B1
PUSH AX ; save it
CALL ecomma ; ! ,
CALL exint ; get integer expr
POP AX ; restore
CALL eword ; * MOV CL,max_len
MOV AX,#xinsert
JMP pemit ; "end it
rdstrvarCALL varptr ; get string var: get var ptr
CMP.B varctp,#$08 ; string ?
CALL errnz ; no:
B $22 ; 34:String variable expected
RET ; "
pstr CALL ebrack1 ; ! ( : STR
CALL exnum ; get num expr
CMP.B CL,#$0A ; integer ?
JNZ pstnoint ; real: is already on stack !
CALL epushax ; * PUSH AX
pstnointCALL ccolon ; : ?
JNZ pstdef1 ; :no formatting
PUSH CX ; save type
CALL exint ; get int expr
CALL epushax ; * PUSH AX
POP CX ; restore type
CMP.B CL,#$0A ; integer ?
JZ pstnxt ; yes: no second parm
CALL ccolon ; : ?
JNZ pstdef3 ; no: get default
PUSH CX ; save type
CALL exint ; get int expr
CALL epushax ; * PUSH AX
POP CX ; restore type
JMP.b pstnxt ; '
pstdef1 XOR AX,AX ; set default
CMP.B CL,#$0A ; integer ?
JZ pstdef2 ; :yes
MOV AL,#$12 ; real: 18 chars
pstdef2 CALL emovaxi ; * MOV AX,i
CALL epushax ; * PUSH AX
CMP.B CL,#$0A ; integer ?
JZ pstnxt ; :yes
pstdef3 MOV AX,#$FFFF ; default for second parm:
CALL emovaxi ; * MOV AX,#FFFF
CALL epushax ; * PUSH AX
pstnxt CALL ecomma ; ! ,
PUSH CX ; save type
CALL rdstrvar ; get string var
MOV.B AH,varsize ; component size-1
DEC.B AH
MOV AL,#$B1
CALL eword ; * MOV CL,max_len
POP CX ; restore type
MOV AX,#xstrint ; (str integer)
CMP.B CL,#$0A ; integer ?
JZ pstemit ; :yes
MOV AX,#xstrreal ; (str real)
pstemit JMP pemit ; "emit call, check )
pval CALL ebrack1 ; ! ( : VAL
CALL exstr ; get string expression
CALL ecomma ; ! ,
CALL varptr ; get dest var ptr
MOV.B CL,varctp ; test type
CMP.B CL,#$0A ; integer ?
JZ pvalint ; :yes
CMP.B CL,#$09 ; real
JZ pvalreal ; :yes
CALL err ; 27:Integer or real var expected
B $1B ; '
pvalint CMP varsize,#$02 ; test component size
JNZ pvalerr ; byte: error
pvalrealPUSH CX ; save type
CALL epushdi ; * PUSH DI
CALL ecomma ; ! ,
CALL varptr ; get status var ptr
CMP.B varctp,#$0A ; integer ?
JNZ pvalerr ; no: error
CMP varsize,#$02 ; byte ?
JZ pvalok ; word:ok
pvalerr CALL err ; 24:Int var expected
B $18 ; '
pvalok POP CX ; restore type
MOV AX,#xvalint ; (val integer)
CMP.B CL,#$0A ; integer ?
JZ pstemit ; :yes
MOV AX,#xvalreal ; (val real)
JMP pstemit ; "emit call, check )
pgotoxy CALL ebrack1 ; ! ( : GOTOXY
CALL exint ; get integer expr
CALL epushax ; * PUSH AX
MOV AX,#xgotoxy
pgxy2 PUSH AX ; save addr
CALL ecomma ; ! ,
CALL exint ; get integer expr
POP AX ; restore addr
JMP pemit ; "emit call, check )
prndmizeMOV AX,#xrndmize ; RANDOMIZE
JMP ecall ; "emit call
pnew MOV AX,#xnew ; NEW
pnew2 PUSH AX ; save addr
CALL ebrack1 ; ! (
CALL varptr ; get var ptr
CMP.B varctp,#$04 ; pointer var ?
JNZ prelerr ; no: error
MOV BP,lower ; get type ptr
CALL getparm ; get type parms
MOV AX,var2size ; get its component size
CALL emovcxi ; * MOV CX,compo_size
POP AX ; restore addr
JMP pemit ; "emit call, check )
pgetmem MOV AX,#xgetmem ; GETMEM
pgm2 PUSH AX ; save addr
CALL ebrack1 ; ! (
CALL varptr ; get var ptr
CMP.B varctp,#$04 ; pointer ?
JNZ prelerr ; no: error
CALL epushdi ; * PUSH DI
CALL ecomma ; ! ,
CALL exint ; get int expr
POP AX ; restore addr
JMP pemit ; "emit call, check )
pdisposeMOV AX,#xdispose ; DISPOSE
JMP pnew2 ; "like New
pfreememMOV AX,#xfreemem ; FREEMEM
JMP pgm2 ; "like Getmem
pmark MOV AX,#xmark ; MARK
JMP.b prel2 ; 'like Release
preleaseMOV AX,#xrelease ; RELEASE
prel2 PUSH AX ; save addr
CALL ebrack1 ; ! (
CALL varptr ; get var ptr
POP AX ; restore addr
CMP.B varctp,#$04 ; pointer ?
JNZ prelerr ; no: error
JMP pemit ; 'emit call, check )
prelerr CALL err ; 28:Pointer var expected
B $1C ; "
pmove CALL ebrack1 ; ! ( : MOVE
CALL varptr ; get src var ptr
CALL epushdi ; * PUSH DI
CALL ecomma ; ! ,
CALL varptr ; get dest var ptr
CALL epushdi ; * PUSH DI
MOV AX,#xmove
JMP pgxy2 ; "get int expr, end it
pfillchrCALL ebrack1 ; ! ( : FILLCHAR
CALL varptr ; get dest var ptr
CALL epushdi ; * PUSH DI
CALL ecomma ; ! ,
CALL exint ; get int expr
CALL epushax ; * PUSH AX
CALL ecomma ; ! ,
CALL exscal ; get scalar expr
MOV AX,#xfillchr
JMP pemit ; "end it
pexit XOR AX,AX ; EXIT: clear flag
JMP exit ; "do it
phalt CALL cbrack1 ; HALT: ( ?
JNZ phlt2 ; no: normal
CALL exint ; get int expr
CALL ebrack2 ; ! )
JMP.b phlt3 ; '
phlt2 CALL ecode ; * XOR AX,AX
B $02,$33,$C0
phlt3 MOV AX,#progend
JMP ejump ; "emit jump
pmsdos CALL ebrack1 ; ! ( : MSDOS
MOV BX,#$0021 ; int number 21
PUSH BX ; save it
JMP.b pint2 ; 'like INTR
pintr CALL ebrack1 ; ! ( : INTR
CALL rdintcn ; read integer constant
PUSH BX ; save it
CALL ecomma ; ! ,
pint2 CALL varptr ; get var ptr
MOV AX,#xsetregs ; (set registers from rec)
CALL ecall ; * CALL xsetregs
POP AX ; interrupt number
MOV.B AH,AL
MOV AL,#$CD ; * INT nn
CALL eword
MOV AX,#xgetregs ; (get registers back)
JMP pemit ; "emit call, check )
pportw MOV AL,#$EF ; PORTW: opcode OUT
JMP.b pprt2 ; '
pport MOV AL,#$EE ; PORT: opcode OUTB
pprt2 PUSH AX ; save opcode
CALL esqr1 ; ! [
CALL exint ; get int expr
CALL esqr2 ; ! ]
CALL epushax ; * PUSH AX (port number)
CALL eassign ; ! :=
CALL exint ; get int expr
CALL ecode ; * POP DX
B $01,$5A
POP AX ; restore opcode
JMP ebyte ; "emit OUT [DX],AX
pcrtinitMOV AX,#xcrtinit ; CRTINIT
JMP.b pdelemit ; '
pcrtexitMOV AX,#xcrtexit ; CRTEXIT
JMP.b pdelemit ; '
pclrscr MOV AX,#xclrscr ; CLRSCR
JMP.b pdelemit ; '
pclreol MOV AX,#xclreol ; CLREOL
JMP.b pdelemit ; '
pnrmvid MOV AX,#xnormvid ; NORMVIDEO, HIGHVIDEO
JMP.b pdelemit ; '
plowvid MOV AX,#xlowvid ; LOWVIDEO
JMP.b pdelemit ; '
pinslineMOV AX,#xinsline ; INSLINE
JMP.b pdelemit ; '
pdellineMOV AX,#xdelline ; DELLINE
pdelemitJMP ecall ; "emit call
pdelay MOV AX,#xdelay ; DELAY
pdel2 PUSH AX ; save addr
CALL ebrack1 ; ! (
CALL exint ; get int expr
POP AX ; restore addr
JMP pemit ; "emit, check )
pwindow MOV AX,#xwindow ; WINDOW
pwind4 PUSH AX ; save addr
CALL ebrack1 ; ! (
pwind3 CALL exint ; get int expr
CALL epushax ; * PUSH AX
CALL ecomma ; ! ,
pwind2 CALL exint ; get int expr
CALL epushax ; * PUSH AX
CALL ecomma ; ! ,
CALL exint ; get int expr
CALL epushax ; * PUSH AX
POP AX ; restore addr
JMP pgxy2 ; "get int, end it
ptextcolMOV AX,#xtxtcol ; TEXTCOLOR
JMP pdel2 ; "(int)
ptextbg MOV AX,#xtxtbg ; TEXTBACKGROUND
JMP pdel2 ; "(int)
pgraphbgMOV AX,#xgrbg ; GRAPHBACKGROUND
JMP pdel2 ; "(int)
ppaletteMOV AX,#xpalette ; PALETTE
JMP pdel2 ; "(int)
phirscolMOV AX,#xhirscol ; HIRESCOLOR
JMP pdel2 ; "(int)
pgrcolmdMOV AX,#xgrcolmd ; GRAPHCOLORMODE
pgremit JMP ecall ; 'emit call
pgrmode MOV AX,#xgrmode ; GRAPHMODE
JMP pgremit ; 'emit call
phires MOV AX,#xhires ; HIRES
JMP pgremit ; "emit call
ptxtmodeCALL cbrack1 ; TEXTMODE
JZ ptxtmd2 ; ( ? yes: get mode
MOV AX,#$00FF
CALL emovaxi ; * MOV AX,#00FF
JMP.b ptxtdef ; '
ptxtmd2 CALL exint ; get int expr
CALL ebrack2 ; ! )
ptxtdef MOV AX,#xtxtmode
JMP ecall ; "emit call
pgrwind MOV AX,#xgrwindw ; GRAPHWINDOW
JMP pwind4 ; "like window
pplot CALL ebrack1 ; ! ( : PLOT
MOV AX,#xplot
PUSH AX ; save it
JMP pwind2 ; "like window - only 3 parms
pdraw CALL ebrack1 ; ! ( : DRAW
CALL exint ; get int expr
CALL epushax ; * PUSH AX
CALL ecomma ; ! ,
MOV AX,#xdraw
PUSH AX ; save addr
JMP pwind3 ; "now like window
psound MOV AX,#xsound ; SOUND
JMP pdel2 ; "(int)
pnosoundMOV AX,#xnosound ; NOSOUND
JMP ecall ; "emit call
exprsaveCALL pushe1 ; push entry
CALL exprax ; read expression
CALL pope1 ; restore entry
exintrt RET ; "
exint CALL exprax ; get integer expression: rd expr
CMP.B CL,#$0A ; integer ?
JZ exintrt ; yes:ret
CALL err ; 23:Integer expression expected
B $17 ; "
exnum CALL exprax ; get numeric expression: rd expr
CMP.B CL,#$0A ; integer ?
JZ exintrt ; yes: ret
CMP.B CL,#$09 ; real ?
JZ exintrt ; yes: ret
CALL err ; 26:Int or real expression expected
B $1A ; "
exscal CALL exprax ; get scalar expression: rd expr
chksimplCMP.B CL,#$0A ; scalar ?
JNB exscret ; :ok
CMP.B CL,#$08 ; string ?
CALL errnz ; no:
B $1F ; 31:Simple expression expected
MOV AX,#xstrch ; (string -> char)
CALL ecall ; * CALL xstrch
MOV CX,#$020C ; type: char in AX
exscret RET ; "
exstr CALL expr ; get string expression: do expr
CMP.B CL,#$08 ; string ?
JZ exstrret ; :yes
CMP.B CL,#$0C ; char ?
CALL errnz ; no:
B $21 ; 33:String expression expected
OR.B CH,CH ; constant ?
JNZ ecstrch ; :no
MOV AX,exres ; get result
MOV.B AH,AL ; char
MOV AL,#$01 ; length
CALL emovaxi ; * MOV AX,#...
JMP.b exstrch2 ; '
ecstrch CALL loadatom ; get atom
CALL ecode ; * MOV AH,AL
B $04,$8A,$E0,$B0,$01 ; * MOV AL,#01
exstrch2CALL epushax ; * PUSH AX
MOV CL,#$08 ; type = string
exstrretRET ; "
excond CALL expr ; evaluate boolean expr: do expr
CMP.B CL,#$0B ; boolean ?
CALL errnz ; no:
B $14 ; 20:Boolean expression expected
CALL loadatom ; get atom
CMP.B CH,#$04 ; comparision outside ?
JZ excondrt ; yes: ok
MOV AL,#$75 ; store branch op: JNZ
MOV brnchop,AL
CMP.B CH,#$03 ; flags set ?
JZ excondrt ; :yes
CALL ecode ; * OR AX,AX
B $02,$0B,$C0
excondrtRET ; "
exprax CALL expr ; read expression
expload CMP.B CL,#$0A ; scalar ?
JB excondrt ; below: ret
CALL loadatom ; get atom
JMP flgbool ; "flags -> boolean
expvar MOV AX,#expload ; do expression - var pre-read
PUSH AX ; set up return stack
MOV AX,#expr2
PUSH AX
MOV AX,#add2
PUSH AX
MOV AX,#mul2
PUSH AX
JMP cvar2 ; "start with var
expconstMOV AX,#expload ; do expression - const pre-read
PUSH AX ; set up return stack
MOV AX,#expr2
PUSH AX
MOV AX,#add2
PUSH AX
MOV AX,#mul2
PUSH AX
JMP cconst2 ; "start with const
expr CALL addlevel ; Comparison level: do add level
expr2 PUSH CX ; save type
CALL ckey ; check comparisons
B $02
W tkcmp
POP CX ; restore type
JZ cmp1 ; found: do comparison
RET ; '
cmp1 INC.B AL ; IN ?
JZ cmpin ; :yes
PUSH BX ; save op ptr, type
PUSH CX
CALL pushres ; save entry
PUSH pc ; save PC
PUSH functp ; save type ptr
CALL addlevel ; addition level
POP comptp ; restore type ptr
POP oldpc ; restore PC
POP DX ; restore type
CALL typeconv ; do type conversion
POP BX ; restore op ptr
CMP.B CL,#$0A ; scalar ?
JB cmpdiff ; :no
CS:
MOV.B AL,[BX]$01 ; set branch opcode
MOV brnchop,AL ; (from comparison table)
MOV BX,#cmpcode ; emit parms for comparison
CALL ecalc ; emit instruction
MOV CX,#$040B ; return type: boolean, flags set
RET ; '
cmpdiff CS: ; real, string, set, ptr comparisons
MOV.B BL,[BX] ; get offset in cmp table
XOR.B BH,BH ; -> table index
CMP.B CL,#$09 ; real ?
JZ cmpfnd ; :yes
INC BX ; +2
INC BX
CMP.B CL,#$08 ; string ?
JZ cmpfnd ; :yes
INC BX ; +2
INC BX
CMP.B CL,#$03 ; set ?
JZ cmpfnd ; :yes
INC BX ; +2 (pointer)
INC BX
cmpfnd CS:
MOV AX,[BX]vcompare ; get vector from table
OR AX,AX ; test it
CALL errz ; 47:Operand type(s) do
B $2F ; not match operator
JMP.b cmpemit ; 'end it
cmpin CALL loadatom ; IN: get atom
CALL epushax ; * PUSH AX
PUSH CX ; save type
CALL addlevel ; do addition level
POP DX ; restore type
CMP.B CL,#$03 ; set ?
CALL errnz ; 47:Operand type(s) do
B $2F ; not match operator
OR.B CH,CH ; constant ?
JZ cmpconst ; :yes
CMP.B DL,CH ; compare types
CALL errnz ; different base type:
B $2C ; 44:Type mismatch
cmpconstMOV AX,#xsetin
cmpemit CALL ecall ; * CALL xsetin
MOV CX,#$030B ; boolean, flags set
RET ; "
vcompareW realeq,csteq,seteq,ptreq ; = vector table for comparisons
W realne,cstne,setne,ptrne ; <>
W realge,cstge,setge ; >=
W $0000
W realle,cstle,setle ; <=
W $0000
W realg,cstg ; >
W $0000,$0000
W reall,cstl ; <
W $0000,$0000 ; "
addlevelCALL mullevel ; Addition level: do mult level
add2 PUSH CX ; save type
CALL ckey ; check add ops
B $05
W tkadd
POP CX ; restore type
JZ addptr ; :found
RET ; '
addptr CMP.B CL,#$04 ; pointer ?
CALL errz ; yes:
B $2F ; 47:Operand type(s) does not match op
PUSH BX ; save op ptr
PUSH CX ; save type
CALL pushres ; save first result
PUSH pc ; save PC
CALL mullevel ; mult level
CMP.B CL,#$0C ; char ?
JNZ add3 ; :no
OR.B CH,CH ; constant ?
JNZ addch ; :no
MOV AX,exres ; get resulting char
MOV.B AH,AL
MOV AL,#$01 ; convert to string
CALL emovaxi ; * MOV AX,#..
JMP.b addch2 ; '
addch CALL loadatom ; load second result
CALL ecode ; * MOV AH,AL
B $04,$8A,$E0,$B0,$01 ; * MOV AL,#01
addch2 CALL epushax ; * PUSH AX
MOV CL,#$08 ; convert to string
add3 POP oldpc ; restore PC
POP DX ; type first part
CALL typeconv ; do type conversions
POP BX ; restore op ptr
CS:
CMP.B [BX],#$02 ; OR, XOR ?
JNB addbool ; :yes
MOV AX,#sunion ; (set +)
MOV DX,#sdiff ; (set -)
CMP.B CL,#$03 ; set ?
JZ addset ; :yes
MOV AX,#xadd ; (add real)
MOV DX,#xsub ; (sub real)
CMP.B CL,#$09 ; real ?
JNZ addstr ; :no
addset CS:
CMP.B [BX],#$00 ; add ?
JZ addadd ; :yes
XCHG AX,DX ; get subtract vector
addadd CALL ecall ; emit call
JMP add2 ; 'loop back
addstr CMP.B CL,#$08 ; string ?
JNZ addscal ; :no
CS:
CMP.B [BX],#$00 ; add ?
CALL errnz ; no:
B $2F ; 47:Operand type(s) does not match op
MOV AX,#xconcat ; (concat strings)
JMP addadd ; 'emit call, loop back
addbool CMP.B CL,#$0B ; boolean ?
JZ addsc2 ; :yes
addscal CMP.B CL,#$0A ; scalar ?
CALL errnz ; no:
B $2F ; 47:Operand type(s) does not match op
addsc2 CALL ecalc ; emit instructions
JMP add2 ; "loop back
mullevelCALL neglevel ; Multiplication level: do unary level
mul2 PUSH CX ; save result type
CALL ckey ; check multiplication ops
B $05
W tkmul
POP CX ; restore type
JZ mulptr ; :found
RET ; '
mulptr CMP.B CL,#$04 ; pointer ?
CALL errz ; yes:
B $2F ; 47:Operand type(s) does not match op
PUSH BX ; save op ptr, type
PUSH CX
CALL pushres ; emit push first value
PUSH pc
CALL neglevel ; do unary level
POP oldpc ; restore old PC
POP DX ; type first op
POP BX ; op ptr
PUSH BX ; save it again
CS:
CMP.B [BX],#$01 ; division ?
JNZ mulnodiv ; :no
CMP.B CL,#$0A ; second = integer ?
JNZ mulnodiv ; :no
CALL loadatom ; load result
MOV AX,#xintreal ; convert to real
CALL ecall ; * CALL xintreal
MOV CL,#$09 ; now real
mulnodivCALL typeconv ; do type conversions
POP BX ; op ptr
CS:
CMP.B [BX],#$01 ; division ?
JA muland ; higher: no reals, sets
JZ muldiv ; :division
MOV AX,#xmul ; (multiply real)
CMP.B CL,#$09 ; real ?
JZ mulreal ; :yes
MOV AX,#sinter ; (set *)
CMP.B CL,#$03 ; set ?
JNZ muland ; no: go out
mulreal CALL ecall ; emit call
JMP mul2 ; 'loop back
muldiv MOV AX,#xdiv ; (divide real)
CMP.B CL,#$09 ; real ?
JZ mulreal ; :yes
CALL err ; 47:Operand type(s) does not match op
B $2F ; '
muland CS: ; AND ?
CMP.B [BX],#$02
JNZ mulscal ; :no
CMP.B CL,#$0B ; boolean ?
JZ mulemit ; :yes
mulscal CMP.B CL,#$0A ; scalar ?
CALL errnz ; no:
B $2F ; 47:Operand type(s) does not match op
mulemit CALL ecalc ; emit arithmetic op
JMP mul2 ; "loop back
neglevelCALL ctoken ; Unary level
W tknot ; NOT ?
JNZ negneg ; :no
CALL negneg ; do negation level
CALL flgbool ; flags -> boolean
CMP.B CL,#$0A ; integer ?
JZ negnot ; :ok
CMP.B CL,#$0B ; boolean ?
CALL errnz ; no:
B $2F ; 47:Operand type(s) does not match op
CALL loadatom ; load value
CALL ecode ; * XOR AL,#01
B $02,$34,$01
MOV CH,#$03 ; flags set...
RET ; '
negnot CALL loadatom ; get value
CALL ecode ; * NOT AX
B $02,$F7,$D0
RET ; '
negneg PUSH negflg ; do negation: save flag
CALL testsign ; test sign
MOV negflg,DX ; store it
CALL atom ; do atom
MOV DX,negflg ; get neg flag
CALL testnum ; test type
JZ negend ; :ok
CMP.B CL,#$0A ; integer ?
JNZ negreal ; :no
CALL loadatom ; get value
CALL ecode ; * NEG AX
B $02,$F7,$D8
JMP.b negend ; '
negreal MOV AX,#xneg ; (negate real)
CALL ecall
negend POP negflg ; restore neg flag
RET ; "
pushres CALL flgbool ; store result on stack
CMP.B CL,#$0A ; integer ?
JB pushptr ; :no
CALL loadatom ; get value
MOV.B flgpshax,#$01 ; set flag: PUSH AX
RET ; '
pushptr CMP.B CL,#$04 ; pointer ?
JNZ pushret ; :no
CALL ecode ; * PUSH DX
B $02,$52,$50 ; * PUSH AX
pushret RET ; "
typeconvCALL flgbool ; Do type conversions
MOV.B flgpshax,#$00 ; no PUSH AX
CMP.B DL,#$09 ; first = real ?
JNZ tcvstr ; :no
CMP.B CL,#$0A ; second = integer ?
JNZ tcvreal ; :no
CALL loadatom ; get value
MOV AX,#xintreal
CALL ecall ; * CALL xintreal
MOV CL,#$09 ; -> real
JMP.b tcvreal ; '
tcvstr CMP.B DL,#$08 ; first = string ?
JNZ tcvreal ; :no
CMP.B CL,#$0C ; second = char ?
JNZ tcvreal ; :no
OR.B CH,CH ; constant ?
JNZ tcvchstr ; :no
MOV AX,exres ; get char
MOV.B AH,AL ; convert to string
MOV AL,#$01
CALL emovaxi ; * MOV AX,#i
JMP.b tcvchst2 ; '
tcvchstrCALL loadatom ; get value
CALL ecode ; * MOV AH,AL (convert to string)
B $04,$8A,$E0,$B0,$01 ; * MOV AL,#01
tcvchst2CALL epushax ; * PUSH AX
MOV CL,#$08 ; -> string
tcvreal CMP.B CL,#$09 ; second = real ?
JNZ tcvchst1 ; :no
CMP.B DL,#$0A ; first = integer ?
JNZ tcvint ; :no
MOV AX,#xintre2 ; convert first number
CALL ecall
MOV DL,#$09 ; first -> real
JMP.b tcvint ; '
tcvchst1CMP.B CL,#$08 ; second = string ?
JNZ tcvint ; :no
CMP.B DL,#$0C ; first = char ?
JNZ tcvint ; :no
MOV AX,#xchstr ; first -> string
CALL ecall ; * CALL xchstr1
MOV DL,#$08 ; -> string
tcvint CMP.B CL,#$0A ; second = integer ?
JB tcvnoint ; :no
CMP.B CH,#$02 ; calculated ?
JNZ tcvpop ; :no
CALL ecode ; * POP CX
B $01,$59
JMP.b tcvchk ; '
tcvpop MOV AX,pc ; PC = old PC ?
CMP AX,oldpc
JZ tcvchk ; :yes
CALL epopax ; * POP AX
JMP.b tcvchk ; '
tcvnointCMP.B CL,#$04 ; pointer ?
JNZ tcvchk ; :no
CALL ecode ; * POP BX
B $02,$5B,$59 ; * POP DX
tcvchk CMP.B CL,DL ; first = second ?
CALL errnz ; no:
B $2C ; 44:Type mismatch
CMP.B CL,#$03 ; set ?
JNZ tcvset ; :no
CMP.B CH,DH ; same base types !
JZ tcvret ; :ok
OR.B DH,DH ; constant 1 ?
JZ tcvret ; yes: ret
OR.B CH,CH ; constant 2 ?
MOV.B CH,DH ; copy base type
JZ tcvret ; yes: ret
CALL err ; 44:Type mismatch
B $2C ; '
tcvset CMP.B CL,#$04 ; pointer ?
JNZ tcvret ; :no
CMP functp,#$00 ; untyped ?
JZ tcvret ; yes: ret
CMP comptp,#$00 ; untyped ?
JZ tcvret ; yes: ret
MOV AX,functp ; get type ptr
CMP AX,comptp ; compare
JZ tcvret ; equal: ok
CALL err ; 44:Type mismatch
B $2C ; '
tcvret RET ; "
loadatomOR.B CH,CH ; load value -> AX
JNZ latvar ; :no constant
MOV AX,exres ; get result
CALL emovaxi ; * MOV AX,#result
JMP.b latres ; '
latvar CMP.B CH,#$01 ; var ?
JNZ latret ; no: ret - already in AX
PUSH CX ; save
PUSH DX
CALL eload ; load var
POP DX
POP CX
latres MOV CH,#$02 ; flag: in AX
latret RET ; "
flgbool CMP.B CL,#$0B ; Flags -> boolean
JNZ flgbret ; :no boolean
CMP.B CH,#$03 ; flags set ?
JB flgbret ; :constant, var, AX
JZ flgbres ; flags set: ok
CALL ecode ; convert
B $03,$B8,$01,$00 ; * MOV AX,#0001
MOV AL,brnchop
CALL ebyte ; emit branch op
CALL ecode ; * offset
B $02,$01,$48 ; * DEC AX
flgbres MOV CH,#$02 ; flag: result in AX
flgbret RET ; "
ecalc PUSH CX ; Emit arithmetic operation
OR.B CH,CH ; second = const ?
JNZ ecvar ; :no
CS:
TEST.B [BX]$04,#$02 ; is there a const form ?
JNZ ecnocn ; :no
CS:
MOV.B AL,[BX]$01 ; get its opcode
CALL ebyte ; emit it
MOV AX,exres ; emit the result
CALL eword ; as immediate value
JMP.b ecxchg2 ; 'done
ecnocn MOV AX,exres ; take immediate
CALL emovcxi ; * MOV CX,#result
JMP.b ecnoxchg ; '
ecvar CMP.B CH,#$01 ; variable ?
JNZ eccalc ; :no
CS:
TEST.B [BX]$04,#$04 ; is there a var form ?
JNZ ecnovar ; :no
CS:
TEST.B [BX]$04,#$08 ; CWD needed ?
JZ eccwd1 ; :no
CALL ecode
B $01,$99 ; * CWD
eccwd1 CS:
MOV DX,[BX]$02 ; base opcode
AND.B DH,#$38 ; convert to AX,var
CALL einstr ; emit instruction
JMP.b ecxchg2 ; 'done
ecnovar MOV DX,#$088B ; * MOV CX,...
CALL einstr ; emit instruction
JMP.b ecnoxchg ; '
eccalc CS: ; second number in AX
TEST.B [BX]$04,#$01 ; XCHG needed ?
JZ ecnoxchg ; :no
CALL ecode
B $01,$91 ; * XCHG CX,AX
ecnoxchgCS:
TEST.B [BX]$04,#$08 ; CWD needed ?
JZ eccwd2 ; :no
CALL ecode
B $01,$99 ; * CWD
eccwd2 CS:
MOV DX,[BX]$02 ; base opcode
CALL emitdx ; emit it
ecxchg2 CS:
TEST.B [BX]$04,#$10 ; second XCHG needed ?
JZ ecres ; :no
CALL ecode
B $01,$92 ; * XCHG DX,AX (for MOD)
ecres POP CX ; restore type
MOV CH,#$02 ; result in AX
RET ; "
atom CALL cvar ; Do atom: do var
JZ atomret ; :done
CALL cconst ; do constant
JZ atomret ; :done
CALL cfunc ; do function
JZ atomret ; :done
CALL cset ; do set
JZ atomret ; :done
CALL cparens ; do parentheses
JZ atomret ; :done
CALL cstdfn ; do standard functions
JZ atomret ; :done
CALL ctype ; do type conversions
JZ atomret ; :done
CALL cnil ; test for NIL
CALL errnz ; not found:
B $29 ; 41:Unknown ID or syntax error
atomret RET ; "
cvar CALL rdvar ; get var
JNZ atomret ; no:ret
cvar2 MOV AL,varctp ; component type:
CMP AL,#$0A ; integer ?
JB cvareal ; :below
MOV CH,#$01 ; var
CMP varsize,#$01 ; byte ?
JA cvaint ; :no, word
CALL eload ; load var
MOV CH,#$02 ; now in AX
cvaint MOV.B CL,varctp ; get component type
JMP.b cvaok ; 'ok
cvareal CMP AL,#$09 ; real ?
JZ cvaload ; :yes
CMP AL,#$08 ; string ?
JZ cvaload ; :yes
CMP AL,#$03 ; set ?
JZ cvaload ; :yes
CMP AL,#$04 ; pointer ?
JNZ cvaarrch ; :no
cvaload CALL eload ; load var
MOV BP,lower ; type ptr
MOV functp,BP
MOV.B CL,varctp ; component type
CMP.B CL,#$03 ; set ?
JNZ cvaok ; :no, ok
MOV.B CH,[BP]-$08 ; get base type
JMP.b cvaok ; 'ok
cvaarrchCMP AL,#$01 ; array ?
CALL errnz ; no:
B $3E ; 62:Struct vars are not allowed here
CALL varptr2 ; get var ptr
MOV BP,lower ; type ptr
CMP.B [BP]-$08,#$0C ; array of char ?
CALL errnz ; no:
B $3E ; 62:Struct vars are not allowed here
MOV BP,upper ; index type
CMP.B [BP]-$08,#$0A ; = scalar ?
CALL errnz ; no:
B $3E ; 62:Struct vars are not allowed here
MOV AX,varsize ; component size
OR.B AH,AH ; > 255 ?
CALL errnz ; yes:
B $3E ; 62:Struct vars are not allowed here
MOV.B AH,AL
MOV AL,#$B1
CALL eword ; * MOV CL,#len
MOV AX,#xldarrch ; (load array of char)
CALL ecall ; * CALL xldarrch
MOV CL,#$08
cvaok XOR AX,AX ; ok
RET ; "
cconst CALL constel ; get constant element
JNZ ccnret ; no good: ret
MOV DX,negflg ; negation flag
CALL donegate ; do negation
MOV negflg,#$0000 ; clear neg flag
cconst2 CMP.B CL,#$09 ; real ?
JNZ ccnstr ; :no
MOV AX,#xrealcn ; (load real constant)
CALL ecall ; * CALL xrealcn
MOV AX,creal1 ; emit real constant
CALL eword ; as inline code
MOV AX,creal2
CALL eword
MOV AX,creal3
CALL eword
JMP.b ccnok ; 'ok
ccnstr CMP.B CL,#$08 ; string ?
JNZ ccnscal ; :no
MOV AX,#xstrcn ; (get inline string)
CALL ecall ; * CALL xstrcn
PUSH CX ; save
CALL estring ; emit string
POP CX
JMP.b ccnok ; 'ok
ccnscal MOV exres,BX ; store result
XOR.B CH,CH ; type: constant
ccnok XOR AX,AX ; ok
ccnret RET ; "
cfunc MOV CX,#$0600 ; Do function
CALL search ; search it
JNZ cfnret ; not found: ret
CALL estkchk ; emit stack check
MOV BX,[BP]-$02 ; result type ptr
SS:
MOV AX,[BX]-$02 ; result type size
CALL allotstk ; allot stack space for result
CALL cproc2 ; call function (like procedure)
CALL getvprm ; get var parms
MOV AX,lower ; result type ptr
MOV functp,AX
MOV.B CL,varctp ; component type
MOV CH,#$02 ; result in AX / on stack
CMP.B CL,#$0B ; boolean ?
JNZ cfnok ; :no
MOV CH,#$03 ; flags set
cfnok XOR AX,AX ; ok
cfnret RET ; "
allotstkCMP AX,#$0002 ; Allocate space on stack
JA astksub ; :offset > 2
XCHG AX,CX ; offset -> CX
JCXZ cfnret ; :nothing to allot
astkdec CALL ecode ; * DEC SP
B $01,$4C
LOOP astkdec ; :another byte
RET ; '
astksub CALL offslen ; short or long offset
MOV DX,#$EC83 ; short
JZ astkshrt ; :short
MOV DL,#$81 ; long
astkshrtPUSH AX ; save AX
CALL emitdx ; * SUB SP,#....
POP AX ; restore
TEST.B DL,#$02 ; short ?
JZ astklong ; :no
JMP ebyte ; 'emit byte offset
astklongJMP eword ; "emit word offset
cset CALL csqr1 ; Do set: [ ?
JNZ csetret ; no:ret
MOV AX,#sldempty ; (make empty set)
CALL ecall ; * CALL sldempty
CALL csqr2 ; ] ?
MOV CX,#$0003 ; type: untyped set
JZ csetret ; :yes
csetlp PUSH CX ; save type
CALL exscal ; get scalar expression
MOV.B AL,CL ; type read
POP CX ; restore type
OR.B CH,CH ; typed set ?
JNZ csetcn ; :yes
MOV.B CH,AL ; set type from const read
csetcn CMP.B AL,CH ; compare base type
CALL errnz ; not equal:
B $2C ; 44:Type mismatch
PUSH CX ; save type
CALL ctoken ; .. ?
W tk2dot
MOV AX,#setincl ; (include element in set)
JNZ csetincl ; :no
CALL epushax ; * PUSH AX
CALL exscal ; get scalar expr
MOV.B AL,CL ; result type
POP CX
PUSH CX
CMP.B AL,CH ; = type of set ?
CALL errnz ; no:
B $2C ; 44:Type mismatch
MOV AX,#setinrng ; (include range in set)
csetinclCALL ecall ; emit call
POP CX ; restore type
CALL ccomma ; , ?
JZ csetlp ; yes: loop back
CALL esqr2 ; ! ]
XOR AX,AX ; ok
csetret RET ; "
cparens CALL cbrack1 ; Do parentheses: ( ?
JNZ cparret ; no:ret
CALL expr ; get expression
CALL ebrack2 ; ! )
XOR AX,AX ; ok
cparret RET ; "
ctype MOV CX,#$0300 ; Do type conversions
CALL search ; search type
JNZ ctpret ; not found: ret
MOV BP,[BP]-$02 ; get type ptr
MOV.B AL,[BP]-$08 ; get component type
CMP AL,#$0A ; scalar ?
CALL errb ; no:
B $1E ; 30:Simple type expected
PUSH AX ; save type
CALL ebrack1 ; ! (
CALL expr ; get expression
CALL ebrack2 ; ! )
CALL chksimpl ; check simple type
POP AX ; restore type
MOV.B CL,AL ; convert to type wanted
XOR AX,AX ; ok
ctpret RET ; "
cnil CALL ctoken ; test for NIL
W tknil
JNZ cnilret ; no:ret
CALL ecode ; * XOR AX,AX
B $04,$33,$C0,$33,$D2 ; * XOR DX,DX
MOV CL,#$04 ; type: pointer
XOR AX,AX ; ok
MOV functp,AX ; untyped ptr
cnilret RET ; "
cstdfn CALL ckey ; Do standard functions
B $02
W stdfuncs
JNZ csfret ; not found: ret
CS:
CALL [BX] ; call compiler routine
MOV CH,#$02 ; flag: result in AX
CMP.B CL,#$0B ; boolean ?
JNZ csfok ; :no
MOV CH,#$03 ; flags set
csfok XOR AX,AX ; ok
csfret RET ; "
fsqr CALL getnum ; SQR (num)
CMP.B CL,#$0A ; integer ?
JNZ fsqr2 ; :no
CALL ecode
B $02,$F7,$E8 ; * IMUL AX
RET ; '
fsqr2 MOV AX,#xsqr ; (sqr real)
fsqremitJMP ecall ; "emit call
fabs CALL getnum ; ABS (num)
MOV AX,#iabs ; (abs integer)
CMP.B CL,#$0A ; integer ?
JZ fsqremit ; yes: emit call
MOV AX,#xabs ; (abs real)
JMP fsqremit ; "
fsqrt MOV AX,#xsqrt ; SQRT
JMP.b freal ; '
fsin MOV AX,#xsin ; SIN
JMP.b freal ; '
fcos MOV AX,#xcos ; COS
JMP.b freal ; '
farctan MOV AX,#xarctan ; ARCTAN
JMP.b freal ; '
fln MOV AX,#xln ; LN
JMP.b freal ; '
fexp MOV AX,#xexp ; EXP
JMP.b freal ; '
fint MOV AX,#xint ; INT
JMP.b freal ; '
ffrac MOV AX,#xfrac ; FRAC
freal PUSH AX ; save function addr
CALL getnum ; (num)
CMP.B CL,#$09 ; real ?
JZ freal2 ; :yes
MOV AX,#xintreal ; (integer -> real)
CALL ecall
MOV CL,#$09 ; now: real
freal2 POP AX ; restore addr
JMP ecall ; "emit call
ftrunc MOV AX,#xtrunc ; TRUNC
JMP.b frnd2 ; '
fround MOV AX,#xround ; ROUND
frnd2 PUSH AX ; save addr
CALL getnum ; (num)
POP AX ; restore
CMP.B CL,#$0A ; integer ?
JNZ frndemit ; :no
RET ; 'int: do nothing
frndemitMOV CL,#$0A ; convert to integer
JMP ecall ; "emit call
fsucc MOV AL,#$40 ; SUCC: INC AX
JMP.b fpred2 ; '
fpred MOV AL,#$48 ; PRED: DEC AX
fpred2 PUSH AX ; save opcode
CALL getscal ; (scal)
POP AX ; restore op
JMP ebyte ; "emit it
flo CALL getint ; LO: (int)
CALL ecode
B $02,$32,$E4 ; * XOR AH,AH
RET ; "
fhi CALL getint ; HI: (int)
CALL ecode ; * MOV AL,AH
B $04,$8A,$C4,$32,$E4 ; * XOR AH,AH
RET ; "
fswap CALL getint ; SWAP: (int)
CALL ecode
B $02,$86,$C4 ; * XCHG AH,AL
RET ; "
fodd CALL getint ; ODD: (int)
CALL ecode
B $03,$25,$01,$00 ; * AND AX,#0001
MOV CL,#$0B ; boolean result
RET ; "
fkeypresMOV AX,#xkeypres ; KEYPRESSED
MOV CL,#$0B ; result: boolean
JMP ecall ; "emit call
ford CALL getscal ; ORD: (scalar)
MOV CL,#$0A ; result: integer
RET ; "
fchr CALL getint ; CHR: (int)
MOV CL,#$0C ; result: char
RET ; "
flength CALL ebrack1 ; ! ( : LENGTH
MOV AX,#xlength
flen2 PUSH AX ; save addr
CALL exstr ; get string expression
CALL ebrack2 ; ! )
POP AX ; restore addr
JMP frndemit ; "int result, emit call
fpos CALL ebrack1 ; ! ( : POS
CALL exstr ; get string expression
CALL ecomma ; ! ,
MOV AX,#xpos
JMP flen2 ; "get second string expr
fcopy CALL ebrack1 ; ! ( : COPY
CALL exstr ; get string expr
CALL ecomma ; ! ,
CALL exint ; get int expr
CALL ecomma ; ! ,
CALL epushax ; * PUSH AX
CALL exint ; get int expr
MOV AX,#xcopy
CALL ecall ; emit call
fcopemitCALL ebrack2 ; ! )
MOV CL,#$08 ; result: string
RET ; "
fconcat CALL ebrack1 ; ! ( : CONCAT
CALL exstr ; get string expr
fconlp CALL ccomma ; , ?
JNZ fcopemit ; no:end it
CALL exstr ; get string expr
MOV AX,#xconcat
CALL ecall ; emit call
JMP fconlp ; "another string ?
fparmcntMOV AX,#xparmcnt ; PARAMCOUNT
JMP frndemit ; "result: integer
fparmstrCALL getint ; PARAMSTR: (int)
MOV AX,#xparmstr
MOV CL,#$08 ; result: string
JMP ecall ; "emit call
frandom CALL cbrack1 ; RANDOM: ( ?
MOV AX,#xrandom ; (random real)
MOV CL,#$09 ; result: real
JNZ frndreal ; no (: take real
CALL exint ; get int expr
CALL ebrack2 ; ! )
MOV AX,#irandom ; (random int)
femitintMOV CL,#$0A ; result: integer
frndrealJMP ecall ; "emit call
fiores MOV AX,#xiores ; IORESULT
JMP femitint ; "-> integer
feof CALL getfil ; EOF: (file)
MOV BX,#veof ; ptr to vector table
CALL ecallio ; emit call for I/O
femitbooMOV CL,#$0B ; result: boolean
RET ; "
fseekeofMOV AX,#xseekeof ; SEEKEOF
JMP.b feo2 ; '
fseekeolMOV AX,#xseekeol ; SEEKEOLN
JMP.b feo2 ; '
feoln MOV AX,#xeoln ; EOLN
feo2 PUSH AX ; save addr
CALL getfil ; (file)
CMP.B CL,#$06 ; text file ?
CALL errnz ; no:
B $23 ; 35:Textfile expected
POP AX ; restore addr
CALL ecall ; emit call
JMP femitboo ; "result: boolean
ffilpos MOV AX,#xfilepos ; FILEPOS
JMP.b ffil2 ; '
ffilsizeMOV AX,#xfilesiz ; FILESIZE
ffil2 PUSH AX ; save addr
CALL getfil ; (file)
POP AX ; addr
CMP.B CL,#$06 ; text file ?
CALL errz ; yes:
B $3F ; 63:Textfiles are not allowed here
JMP femitint ; "int result, emit call
flfilposMOV AX,#xlfilpos ; LONGFILEPOS
JMP.b flf2 ; '
flfilsizMOV AX,#xlfilsiz ; LONGFILESIZE
flf2 PUSH AX ; save addr
CALL getfil ; (file)
POP AX ; addr
CMP.B CL,#$06 ; text file ?
CALL errz ; yes:
B $3F ; 63:Textfiles are not allowed here
MOV CL,#$09 ; result: real
JMP ecall ; "emit call
fmemavl MOV AX,#xmemavl ; MEMAVAIL
JMP femitint ; "int result, emit call
fmaxavl MOV AX,#xmaxavl ; MAXAVAIL
JMP femitint ; "int result, emit call
faddr CALL ebrack1 ; ! ( : ADDR
CALL varptr ; get var ptr
CALL ecode ; * POP DX
B $02,$5A,$97 ; * XCHG DI,AX
fretptr CALL ebrack2 ; ! )
MOV CL,#$04 ; result: pointer
MOV functp,#$0000 ; untyped
RET ; "
fptr CALL ebrack1 ; ! ( : PTR
CALL exint ; get int expr
CALL ecomma ; ! ,
CALL epushax ; * PUSH AX
CALL exint ; get int expr
CALL ecode ; * POP DX (segment)
B $01,$5A
JMP fretptr ; "expect ), pointer
fofs CALL ebrack1 ; ! ( : OFS
MOV CX,#$0500 ; search procedure
CALL search
JZ fofsproc ; :found
MOV CX,#$0600 ; search function
CALL search
JNZ fofsvar ; :not found
fofsprocMOV AX,[BP]-$08 ; get offset
fofsimmeCALL emovaxi ; * MOV AX,#proc_offset
JMP.b fofsint ; 'type: integer
fofsvar CALL rdvar ; get var
CALL errnz ; not found:
B $29 ; 41:Unknown ID or syntax error
CALL loadoffs ; get offset
CALL ecode ; * XCHG DI,AX
B $01,$97 ; (offset -> AX)
fofsint CALL ebrack2 ; ! )
MOV CL,#$0A ; result: integer
RET ; "
fseg CALL ebrack1 ; ! ( : SEG
CALL rdvar ; get var
CALL errnz ; not found:
B $29 ; 41:Unknown ID or syntax error
MOV AL,#$8C ; (MOV AX,DS)
MOV AH,#$D8
CMP.B varseg,#$FF ; DS ?
JZ fsegemit ; yes: ok
MOV AH,#$C0 ; (MOV AX,ES)
CMP.B varseg,#$FD ; ES ?
JZ fsegemit ; yes: ok
MOV AH,#$D0 ; (MOV AX,SS)
JB fsegemit ; below: ok
MOV AH,#$C8 ; (MOV AX,CS)
fsegemitCALL eword ; emit word
JMP fofsint ; "expect ), integer
fsizeof CALL ebrack1 ; ! ( : SIZEOF
MOV CX,#$0300 ; search type
CALL search
JNZ fszvar ; :not found
MOV BP,[BP]-$02 ; type ptr
MOV AX,[BP]-$02 ; get component size
CALL emovaxi ; * MOV AX,#compo_size
JMP fofsimme ; '(Bug: redundant load !)
fszvar CALL rdvar ; get var
CALL errnz ; not found:
B $29 ; 41:Unknown ID or syntax error
MOV AX,varsize ; get component size
JMP fofsimme ; "emit MOV AX,#.., integer
fdseg CALL ecode ; DSEG
B $02,$8C,$D8 ; * MOV AX,DS
JMP.b fsseg2 ; 'int result
fcseg CALL ecode ; CSEG
B $02,$8C,$C8 ; * MOV AX,CS
JMP.b fsseg2 ; 'int result
fsseg CALL ecode ; SSEG
B $02,$8C,$D0 ; * MOV AX,SS
fsseg2 MOV CL,#$0A ; integer
RET ; "
fportw MOV AL,#$ED ; PORTW
JMP.b fprt2 ; '(IN AX,[DX])
fport MOV AL,#$EC ; PORT (INB AL,[DX])
fprt2 PUSH AX ; save opcode
CALL esqr1 ; ! [
CALL exint ; get int expr
CALL esqr2 ; ! ]
CALL ecode
B $01,$92 ; * XCHG DX,AX (port number)
POP AX ; restore opcode
CALL ebyte ; emit it
CMP AL,#$ED ; integer ?
JZ fsseg2 ; :yes
CALL ecode ; byte:
B $02,$32,$E4 ; * XOR AH,AH
JMP fsseg2 ; "int result
fupcase CALL getscal ; UPCASE: (scalar)
MOV CL,#$0C ; result: char
MOV AX,#xupcase
JMP ecall ; "emit call
fwherex MOV AX,#xwherex ; WHEREX
JMP femitint ; "int result, emit call
fwherey MOV AX,#xwherey ; WHEREY
JMP femitint ; "int result, emit call
veof W xeofty,xeoftx,xeofty ; "EOF vector table
getint CALL ebrack1 ; ! ( : get (int)
CALL exint ; get int expr
JMP ebrack2 ; "! )
getnum CALL ebrack1 ; ! ( : get (num)
CALL exnum ; get num expr
JMP ebrack2 ; "! )
getscal CALL ebrack1 ; ! ( : get (scalar)
CALL exscal ; get scalar expr
JMP ebrack2 ; "! )
getfil CALL cbrack1 ; get (file): ( ?
JNZ gtfstd ; :no
CALL rdfilvar ; get file var
JNB gtf2 ; :ok
CALL err ; 41:Unknown ID or syntax error
B $29 ; '
gtf2 CALL errnz ; not file var:
B $15 ; 21:File var expected
JMP ebrack2 ; '! )
gtfstd CALL ecode ; set ptr to std input file
B $04,$BF,$5A,$01,$1E ; * MOV DI,#stdin
MOV CL,#$06 ; * PUSH DS
MOV.B varctp,CL ; type: text file
RET ; "
fullvar CALL rdconst ; Do full var: read constant
JNZ rdvar ; no good: read var
CMP.B CL,#$08 ; string constant ?
CALL errnz ; no:
B $3C ; 60:Constants are not allowed here
MOV AL,#$EB ; JMP
MOV.B AH,CH ; offset: length of string
CALL eword ; emit branch around string constant
MOV AX,pc ; save current pos
MOV varofs,AX ; -> offset of var
MOV.B indflg,#$00 ; not indexed
MOV.B varseg,#$FE ; segment CS
MOV.B varctp,#$01 ; type: array
MOV AX,ptcbeg ; std type: char
SUB AX,#$0062
MOV lower,AX ; -> type pointer
XOR AX,AX ; no index type
MOV upper,AX
MOV.B AL,CH ; length -> component size
MOV varsize,AX
CALL estr2 ; emit array of char
XOR AX,AX ; ok
RET ; '
rdvar CALL indwith ; do WITH-indexing
JZ rdvarlp ; :done
MOV CX,#$0400 ; search var
CALL search
JNZ rvamem ; not found: try MEM
CALL getvprm ; get var parms
rdvarindMOV.B indflg,#$00 ; flag: not indexed
CMP.B indptflg,#$00 ; indirect by ptr ?
JZ rdvarlp ; :no
CALL indptrld ; get ptr to var
rdvarlp CALL indarray ; do array indexing
JZ rdvarlp ; done: loop
CALL indrec ; do record indexing
JZ rdvarlp ; done: loop
CALL indptr ; do pointer indexing
JZ rdvarlp ; done: loop
CALL indstr ; do string indexing
XOR AX,AX ; ok
RET ; '
rvamem CALL ckey ; search keyword:
B $01 ; MEM / MEMW
W tkmem
JNZ rvaret ; not found: ret
CS:
MOV.B AL,[BX] ; get component size
PUSH AX ; save it
CALL esqr1 ; ! [
CALL exint ; get int expr
CALL epushax ; * PUSH AX (segment)
CALL ecolon ; ! :
CALL exint ; get int expr
CALL esqr2 ; ! ]
CALL ecode ; * XCHG DI,AX
B $02,$97,$07 ; * POP ES
POP AX ; get length flag
XOR.B AH,AH ; clr hi
MOV varsize,AX ; -> component size
MOV.B varctp,#$0A ; type integer
CMP AL,#$01 ; byte ?
JNZ rvamemw ; :no
MOV lower,#$0000 ; set lower, upper bound
MOV upper,#$00FF
JMP.b rvamemb ; '
rvamemw MOV lower,#$8000 ; set lower, upper bound
MOV upper,#$7FFF
rvamemb MOV varofs,#$0000 ; var offset: none
MOV.B indflg,#$FF ; indexed
MOV.B varseg,#$FD ; segment ES
XOR AX,AX ; ok
rvaret RET ; "
indwith MOV.B BL,withnest ; Do WITH indexing
XOR.B BH,BH ; with nesting -> count
iwloop DEC BX ; count down
JNS iw1 ; :another level to test
RET ; '
iw1 PUSH BX ; save counter
SHL BX,1 ; * 4
SHL BX,1 ; -> pointer into with-table
MOV CX,[BX]withtab ; type
PUSH CX
PUSH [BX]withtab1 ; position
MOV CH,#$04 ; search var (record num)
CALL search
POP AX ; restore pos, type, counter
POP CX
POP BX
JNZ iwloop ; not found: do next level
MOV.B indflg,#$00 ; flag: not indexed
CMP AX,#$FFFF ; indexed WITH ?
JZ iwvar ; :yes
MOV.B varseg,CH ; get offset from with-table
MOV varofs,AX
JMP.b iwconst ; 'do record indexing
iwvar PUSH BP ; save symtab pos
MOV.B BL,CH ; get stack offset
SHL BX,1
MOV AX,varspc ; stack usage
SUB AX,BX ; - position
MOV varofs,AX ; -> offset
MOV AL,lexnest ; lexical nesting
MOV varseg,AL ; -> var segment
CALL indptrld ; get pointer to var
POP BP ; restore symtab ptr
iwconst JMP iraddofs ; "do record indexing
indstr CMP.B varctp,#$08 ; Do string indexing
JNZ iaret ; no string: ret
CALL csqr1 ; [ ?
JNZ iaret ; no: ret
MOV AX,ptcbeg ; point to char type
SUB AX,#$0062
MOV lower,AX ; -> type ptr
JMP.b ialoop ; "get index
indarrayCMP.B varctp,#$01 ; Do array indexing
JNZ iaret ; no array: ret
CALL csqr1 ; [ ?
JZ ialoop ; yes: do it
iaret RET ; '
ialoop MOV AX,flgpshes ; flag: emit PUSH ES
CMP.B varseg,#$FD ; segment ES ?
JNZ ialp2 ; :no
MOV.B flgpshes,#$01 ; set flag
ialp2 CMP.B indflg,#$00 ; indexed ?
JZ ianoind ; :no
MOV.B flgpshdi,#$01 ; set flag: emit PUSH DI
ianoind CALL pushe1 ; push entry
PUSH pc ; save PC, flag
PUSH AX
CALL expr ; evaluate expression
POP AX ; restore
CMP flgpshes,#$00 ; flag = 0 ?
JZ iarest ; :yes
MOV flgpshes,AX ; restore flag
iarest POP AX ; old PC
SUB AX,pc ; anything emitted ?
OR.B CH,CH ; constant ?
JZ ianosto ; :yes
PUSH AX ; save
PUSH CX
CALL expload ; load result
POP CX
POP AX
ianosto CALL pope1 ; restore entry
OR AX,AX ; test flag
JZ ianopop ; :not set
CMP.B indflg,#$00 ; indexed ?
JZ ianoind2 ; :no
CALL ecode ; * POP DI
B $01,$5F
ianoind2CMP.B varseg,#$FD ; segment ES ?
JNZ ianopop ; :no
CALL ecode ; * POP ES
B $01,$07
ianopop CMP.B varctp,#$08 ; string ?
JNZ ianostr ; :no
MOV.B var2ctp,#$0A ; component type = scalar
MOV lower2,#$0000 ; lower bound
MOV AX,varsize ; component size - 1
DEC AX
MOV upper2,AX ; -> upper bound
JMP.b iastr ; '
ianostr MOV BP,upper ; index type ptr
CALL getparm ; get type parms
iastr MOV BP,lower ; type ptr
CALL getvprm2 ; get type parms
CMP.B CL,var2ctp ; type = index type ?
CALL errnz ; no:
B $2C ; 44:Type mismatch
OR.B CH,CH ; constant index ?
JNZ iavarind ; :no
MOV AX,exres ; get result
CMP AX,lower2 ; < lower bound ?
JL iaerr ; yes: error
CMP AX,upper2 ; > upper bound ?
JLE iacnind ; no: ok
iaerr CALL err ; 45:Constant out of range
B $2D ; '
iacnind SUB AX,lower2 ; subtract lower bound
MOV CX,varsize ; * component size
MUL CX
ADD varofs,AX ; add to var offset
JMP iaind ; 'store it
iavarindMOV AX,lower2 ; lower bound
TEST direcsv,#$0002 ; range check ?
JNZ iarngind ; :yes
MOV CX,varsize ; lower bound * component size
MUL CX
SUB varofs,AX ; subtract from var offset
JMP.b iacnind2 ; '-> faster array indexing
iarngindOR AX,AX ; subtract lower bound from index
JZ iaoff2 ; 0:done
CMP AX,#$0001 ; 1 ?
JNZ iaoff ; :no
CALL ecode
B $01,$48 ; * DEC AX
JMP.b iaoff2 ; '
iaoff CALL ecode
B $01,$2D ; * SUB AX,#lower_bound
CALL eword
iaoff2 MOV AX,upper2 ; upper bound-lower bound+1
SUB AX,lower2
INC AX
CALL emovcxi ; * MOV CX,#ind_count
MOV AX,#xindchk ; (range check)
CALL ecall ; * CALL xindchk
iacnind2MOV AX,varsize ; index * component size
CMP AX,#$0001 ; component size = 1 ?
JZ iacsend ; yes: ok
CMP AX,#$0002 ; = 2 ?
JNZ iacs4 ; :no
CALL ecode
B $02,$D1,$E0 ; * SHL AX,1
JMP.b iacsend ; '
iacs4 CMP AX,#$0004 ; = 4 ?
JNZ iacs6 ; :no
CALL ecode ; * SHL AX,1
B $04,$D1,$E0,$D1,$E0 ; * SHL AX,1
JMP.b iacsend ; '
iacs6 CMP AX,#$0006 ; = 6 ?
JNZ iacs ; :no
CALL ecode ; * SHL AX,1
B $08,$D1,$E0,$8B,$C8 ; * MOV CX,AX
B $D1,$E0,$03,$C1 ; * SHL AX,1
JMP.b iacsend ; '* ADD AX,CX
iacs CALL emovcxi ; * MOV CX,#compo_size
CALL ecode
B $02,$F7,$E1 ; * MUL CX
iacsend CMP.B indflg,#$00 ; already indexed ?
JZ ianotind ; :no
CALL ecode ; add to previous index
B $02,$03,$F8 ; * ADD DI,AX
JMP.b iaind ; '
ianotindCALL ecode
B $01,$97 ; * XCHG DI,AX
MOV.B indflg,#$FF ; flag: indexed
iaind CMP.B varctp,#$01 ; component type = array ?
JNZ iaend ; no: end it
CALL ccomma ; , ?
JNZ iaend ; no: end it
JMP ialoop ; 'get next index
iaend CALL esqr2 ; ! ]
XOR AX,AX ; ok
RET ; "
indrec CMP.B varctp,#$02 ; Do record indexing
JNZ irret ; no record: ret
CALL cdot ; . ?
JNZ irret ; no: ret
MOV.B CL,varnest ; record nesting level
MOV CH,#$04 ; var
CALL search ; search subvar
CALL errnz ; not found:
B $29 ; 41:Unknown ID or syntax error
iraddofsPUSH indptflg ; save base parms
PUSH varofs
CALL getvprm ; get var parms
POP AX ; restore offset
ADD varofs,AX ; add to new offset
POP indptflg ; restore indirection ptr
XOR AX,AX ; ok
irret RET ; "
indptr CMP.B varctp,#$04 ; Do pointer indexing
JNZ iptret ; no pointer: ret
CALL cptr ; ^ ?
JNZ iptret ; no: ret
MOV BP,lower ; type ptr
CALL getvprm2 ; get type parms
indptrldMOV DX,#$38C4 ; * LES DI,pointer_var
CALL einstr ; emit instruction
MOV varofs,#$0000 ; offset = 0
MOV.B varseg,#$FD ; segment = ES
MOV.B indflg,#$FF ; indexed
XOR AX,AX ; ok
iptret RET ; "
einstr MOV AL,varseg ; Emit instruction with addressing
CMP AL,#$FD ; ES ?
JB eistk ; :stack segment
CALL esegment ; emit segment prefix
CMP.B indflg,#$00 ; indexed ?
JNZ eiind ; :yes
MOV CH,#$06 ; mode: direct
OR.B DH,CH
CALL emitdx ; emit DX
MOV AX,varofs ; get offset
CALL eword ; emit offset
JMP.b eiret ; 'ret
eiind MOV AX,varofs ; get offset
OR AX,AX ; test offset
MOV CH,#$05 ; [DI]
JZ eiindofs ; :yes
CALL offslen ; short or long offset ?
MOV CH,#$45 ; short
JZ eiindofs ; :ok
MOV CH,#$85 ; long offset
eiindofsOR.B DH,CH ; set addressing mode
CALL emitdx ; emit opcode
TEST.B DH,#$C0 ; offset ?
JZ eiret ; no: ret
JMP.b eioffs ; 'emit offset
eistk CMP.B AL,lexnest ; in current procedure ?
MOV CX,#$0306 ; [BP]offs / [BP+DI]offs
JZ eistk2 ; :yes
CALL ecode ; * MOV BX,[BP]lex_level
B $02,$8B,$5E ; (get ptr from display)
ADD.B AL,AL ; - nesting level * 2
NEG.B AL
CALL ebyte ; emit offset
MOV AL,#$36 ; * SS:
CALL ebyte
MOV CX,#$0107 ; [BX]offs / [BX+DI]offs
eistk2 CMP.B indflg,#$00 ; indexed ?
JZ eistk3 ; :no
MOV.B CL,CH ; use [B.+DI]
eistk3 MOV AX,varofs ; get offset
CALL offslen ; short or long ?
MOV CH,#$40 ; short
JZ eistksh ; :ok
MOV CH,#$80 ; long
eistksh OR.B DH,CL ; set addressing mode
OR.B DH,CH
CALL emitdx ; emit opcode
eioffs MOV AX,varofs ; get offset
TEST.B DH,#$40 ; long ?
JNZ eioffbyt ; :no
CALL eword ; emit word offset
JMP.b eiret ; '
eioffbytCALL ebyte ; emit byte offset
eiret RET ; "
offslen OR.B AL,AL ; Short or long offset ?
JS offneg ; :negative
OR.B AH,AH ; high byte must be zero
RET ; '
offneg CMP.B AH,#$FF ; high byte must be FF
RET ; "
esegmentCMP.B varseg,#$FF ; emit segment prefix
JZ eseges ; DS:no prefix needed
CMP.B varseg,#$FD ; ES ?
MOV AL,#$26 ; (ES:)
JZ esegret ; :yes
MOV AL,#$2E ; (CS:)
esegret CALL ebyte ; emit prefix
eseges RET ; "
loadoffsCMP.B varseg,#$FD ; Get offset
JB ldostk ; :stack segment
MOV AX,varofs ; var offset
CMP.B indflg,#$00 ; indexed ?
JNZ ldoind ; :yes
JMP emovdii ; '* MOV DI,#offset
ldoind OR AX,AX ; test offset
JZ eseges ; 0:ret
CALL offslen ; short or long offset ?
MOV DX,#$C783 ; (ADD DI,#byte)
JZ ldobyt ; :short
MOV DL,#$81 ; (ADD DI,#word)
ldobyt PUSH AX ; save offset
CALL emitdx ; emit operation
POP AX ; offset
TEST.B DL,#$02 ; short ?
JZ ldoword ; :no
JMP ebyte ; 'emit byte offset
ldoword JMP eword ; 'emit word offset
ldostk MOV DX,#$388D ; (LEA DI,..)
JMP einstr ; "get effective addr -> DI
varptr CALL rdvar ; Get var ptr: get var
CALL errnz ; not found:
B $29 ; 41:Unknown ID or syntax error
varptr2 CALL loadoffs ; get offset -> DI
MOV AL,#$1E ; (PUSH DS) push offset
CMP.B varseg,#$FF ; DS ?
JZ varptret ; :yes
MOV AL,#$06 ; (PUSH ES)
CMP.B varseg,#$FD ; ES ?
JZ varptret ; :yes
MOV AL,#$16 ; (PUSH SS)
JB varptret ; :yes
MOV AL,#$0E ; (PUSH CS)
varptretJMP ebyte ; "emit op
eload CMP.B varctp,#$0A ; Load var
JB eldptr ; :no scalar
CMP.B varseg,#$FD ; ES ?
JB eldstk ; :SS: no shortcuts
CMP.B indflg,#$00 ; indexed ?
JZ elddir ; :no
eldstk MOV DL,#$8B ; (MOV)
CMP varsize,#$01 ; byte ?
JA eldword ; :no
MOV DL,#$8A ; (MOVB)
eldword MOV DH,#$00 ; MOV AX,...
CALL einstr ; emit instruction
JMP.b eldpbyt ; 'byte: clr hi byte
elddir CALL esegment ; emit segment prefix
MOV AL,#$A1 ; (MOV AX,var)
CMP varsize,#$01 ; byte ?
JA elddbyt ; :no
MOV AL,#$A0 ; (MOVB AL,var)
elddbyt CALL ebyte ; emit operation
MOV AX,varofs ; emit offset
CALL eword
eldpbyt CMP varsize,#$01 ; byte ?
JA eldret ; :no
CALL ecode
B $02,$32,$E4 ; * XOR AL,AL
eldret RET ; '
eldptr CMP.B varctp,#$04 ; load other types
JNZ eldreal ; :no ptr
MOV DX,#$00C4 ; LES AX,ptr_var
CALL einstr ; emit instruction
CALL ecode
B $02,$8C,$C2 ; * MOV DX,ES
RET ; '
eldreal CALL varptr2 ; get ptr -> DI,stack
MOV AX,#xldreal ; (load real)
CMP.B varctp,#$09 ; real ?
JZ eldemit ; :yes
MOV AX,#strload ; (load string)
CMP.B varctp,#$08 ; string ?
JZ eldemit ; :yes
CALL esetfac ; emit set crunch factor
MOV AX,#xldset ; (load set)
eldemit JMP ecall ; "emit call
estore CALL erngchk ; Store var: emit range check
estore2 CMP.B varctp,#$0A ; scalar ?
JB estcpl ; :no
eststo CMP.B varseg,#$FD ; SS ?
JB eststk ; yes: no shortcuts
CMP.B indflg,#$00 ; indexed ?
JZ estdir ; no: optimize
eststk MOV DL,#$89 ; (MOV dest_var,AX)
CMP varsize,#$01 ; byte ?
JA estword ; :no
MOV DL,#$88 ; (MOVB dest_var,AL)
estword MOV DH,#$00 ; mode
JMP einstr ; 'emit instruction
estdir CALL esegment ; emit segment prefix
MOV AL,#$A3 ; (MOV var,AX)
CMP varsize,#$01 ; byte ?
JA estdbyt ; :no
MOV AL,#$A2 ; (MOVB var,AL)
estdbyt CALL ebyte ; emit opcode
MOV AX,varofs ; emit var offset
JMP eword ; '
estptr CALL eststo ; store ptr: emit store AX
ADD varofs,#$02 ; offs second word
MOV DX,#$1089 ; emit store DX
JMP einstr ; 'emit instruction
estcpl CMP.B varctp,#$04 ; pointer ?
JZ estptr ; :yes
MOV AX,#xstoreal ; (store real)
CMP.B varctp,#$09 ; real ?
JZ estemit ; :yes
CMP.B varctp,#$08 ; string ?
JNZ estset ; :no
MOV.B AH,varsize ; get string length
DEC.B AH
MOV AL,#$B1
CALL eword ; * MOV CL,max_len
MOV AX,#strstore ; (store string)
JMP.b estemit ; 'emit call
estset CALL esetfac ; set crunch factor
MOV AX,#setsto ; (store set)
estemit JMP ecall ; "emit call
esetfac MOV BP,lower ; calculate set crunch factor
CALL getparm ; get parms of base type
MOV AL,varsize ; component size
MOV.B AH,lower2 ; lower bound/8
MOV CL,#$03
SHR.B AH,CL
JMP emovcxi ; "* MOV CX,#crunch
erngchk CMP.B varctp,#$0A ; Emit range check
JB erngret ; no scalar: ret
TEST direcsv,#$0002 ; range checking on ?
JZ erngret ; :no
MOV AX,lower ; lower bound-1 = upper bound ?
DEC AX
CMP AX,upper
JZ erngret ; yes: no check
INC AX ; restore lower
CALL emovcxi ; * MOV CX,#lower_bound
MOV AX,upper ; upper bound
CALL emovdxi ; * MOV DX,#upper_bound
MOV AX,#xrngchk ; (range check)
CALL ecall ; emit call
erngret RET ; "
rdnumcn CALL rdconst ; read numeric constant
snerror CALL errnz ; no good:
B $29 ; 41:Unknown ID or syntax error
RET ; "
rdintcn CALL rdnumcn ; get integer constant
testint CMP.B CL,#$0A ; integer ?
CALL errnz ; no:
B $16 ; 22:Integer constant expected
RET ; "
rdstrcn CALL rdnumcn ; get string constant
CMP.B CL,#$08 ; string ?
JZ rdstrret ; yes: ok
CMP.B CL,#$0C ; char ?
CALL errnz ; no:
B $20 ; 32:String constant expected
MOV CL,#$08 ; convert to string
rdstrretRET ; "
rdconst CALL testsign ; Read constant: test sign
PUSH DX ; save it
CALL constel ; get constant element
POP DX ; restore sign
JZ donegate ; :ok
OR DX,DX ; didn't work
CALL errnz ; 25:Int or real const expected
B $19
DEC DX ; ok
rdcnret RET ; '
donegateCALL testnum ; test type
JZ rdcnret ; :ret - no negation
CMP.B CL,#$09 ; real ?
JNZ rdcnnegi ; :no
CMP.B creal1,#$00 ; 0 ?
JZ rdcnok ; yes: don't negate
XOR.B cresign,#$80 ; negate it
JMP.b rdcnok ; 'ok
rdcnnegiNEG BX ; negate result
rdcnok XOR DX,DX ; ok
RET ; "
testsignMOV DI,chptr ; Test sign:test char
MOV DX,#$FFFF ; flag: negative
CMP.B [DI],#$2D ; - ?
JZ tsminus ; :yes
INC DX ; clear neg flag
CMP.B [DI],#$2B ; + ?
JNZ tsret ; no: don't skip char
INC DX ; set + flag
tsminus INC DI ; skip that char
CALL skipdi ; read next word
tsret RET ; "
testnum OR DX,DX ; Test type
JZ tnret ; :no negation - ok
CMP.B CL,#$0A ; integer ?
JZ tndec ; :ok
CMP.B CL,#$09 ; real ?
CALL errnz ; no:
B $19 ; 25:Int or real constant expected
tndec DEC DX ; clear plus flag
tnret RET ; "
constel CALL immecn ; Get const element: immediate const
JNZ clconst ; :no
RET ; '
clconst MOV CX,#$0200 ; search constant
CALL search
JNZ clret ; :not found
MOV.B CL,[BP]-$01 ; get component type
CMP.B CL,#$0A ; scalar ?
JB clreal ; :no
MOV BX,[BP]-$03 ; get value
JMP.b clok ; 'ok
clreal CMP.B CL,#$09 ; real ?
JNZ clstring ; :no
MOV AX,[BP]-$07 ; get real const
MOV creal1,AX ; store in buffer
MOV AX,[BP]-$05
MOV creal2,AX
MOV AX,[BP]-$03
MOV creal3,AX
JMP.b clok ; 'ok
clstringMOV.B CH,[BP]-$02 ; string: get length
MOV.B DL,CH
MOV BX,#wordbuf ; dest buffer
clstrlp OR.B DL,DL ; test len
JZ clok ; :done
DEC BP ; stored backwards !
MOV.B AL,[BP]-$02 ; get char
MOV.B [BX],AL ; copy into buffer
INC BX ; next pos
DEC.B DL ; another char ?
JMP clstrlp ; '
clok XOR DX,DX ; ok
clret RET ; "
immecn MOV DI,chptr ; get immediate constant
MOV.B AL,[DI] ; current char
CMP AL,#$27 ; ' ?
JZ icstr ; yes: string const
CMP AL,#$5E ; ^ ?
JZ icstr ; yes: string const
CMP AL,#$23 ; # ?
JNZ icnum ; no: numeric const
icstr MOV BX,#wordbuf ; dest buffer
MOV CH,#$00 ; length = 0
icsloop MOV.B AL,[DI] ; current char
CMP AL,#$5E ; ^ ?
JZ icscntrl ; :yes
CMP AL,#$23 ; # ?
JZ icsnum ; :yes
CMP AL,#$27 ; ' ?
JNZ icsend ; no: end of string const
icslp2 INC DI ; next char
MOV.B AL,[DI] ; get char
OR.B AL,AL ; line end ?
CALL perrz ; yes:
B $37 ; 55:String constant exceeds line
CMP AL,#$27 ; ' ?
JNZ icssto ; :no
INC DI ; test next char: may be ''
CMP.B [DI],#$27 ; ' ?
JNZ icsloop ; no: end of string
icssto MOV.B [BX],AL ; store in buffer
INC BX ; next pos
INC.B CH ; count length
JMP icslp2 ; 'continue
icscntrlINC DI ; next char: do ^char
MOV.B AL,[DI] ; get it
CALL upcase ; UpCase
OR.B AL,AL ; 0 ?
CALL perrz ; yes:
B $37 ; 55:String constant exceeds line
XOR AL,#$40 ; make it a control char
INC DI ; next char
icssto2 MOV.B [BX],AL ; store in buffer
INC BX
INC.B CH ; count length
JMP icsloop ; 'get next element
icsnum INC DI ; read #char
PUSH BX ; save dest ptr, counter
PUSH CX
MOV BX,DI ; pos of number
CALL asccard ; read integer
MOV DI,BX ; set new position
POP CX ; restore
POP BX
CALL perrb ; error:
B $38 ; 56:Error in int constant
JMP icssto2 ; 'store it, continue
icsend MOV CL,#$08 ; string
CMP.B CH,#$01 ; length = 1 ?
JNZ icend ; :no
MOV.B BL,wordbuf ; get char
XOR.B BH,BH ; clear high byte
MOV CL,#$0C ; char
icend JMP skipdi ; 'get next word
icnum MOV BX,DI ; numeric constant: beg pos
CMP AL,#$24 ; $ ?
JZ icnint ; yes: integer constant
CALL number ; char in 0..9 ?
JNB icnscan ; :yes
XOR AX,AX ; set flag: invalid
DEC AX
RET ; '
icnscan INC DI ; next char
MOV.B AL,[DI] ; get it
CALL number ; in 0..9 ?
JNB icnscan ; yes: loop
CALL upcase ; UpCase
CMP AL,#$45 ; E ?
JZ icnreal ; yes: real constant
CMP AL,#$2E ; . ?
JNZ icnint ; no: integer constant
INC DI ; test next char: .. ?
MOV.B AL,[DI]
CMP AL,#$2E ; . ?
JZ icnint ; :yes, integer constant
CMP AL,#$29 ; ) ? (.) = ])
JZ icnint ; yes: integer constant
icnreal MOV DI,#creal1 ; dest buffer
CALL ascreal2 ; read real number
MOV DI,BX ; set new position
CALL perrb ; error:
B $39 ; 57:Error in real const
MOV CL,#$09 ; real
JMP icend ; 'end it
icnint CALL asccard ; read integer const
MOV DI,BX ; set new pos
MOV BX,AX ; get result
CALL perrb ; ok ? no:
B $38 ; 56:Error in integer constant
MOV CL,#$0A ; integer
JMP icend ; "end it
ecode PUSH AX ; Emit inline code
PUSH BP ; (stored as inline string)
MOV BP,SP ; stack frame
XCHG BX,[BP]$04 ; get return addr
CS:
MOV.B AH,[BX] ; get length
INC BX ; next byte
ecodelp CS: ; get byte
MOV.B AL,[BX]
CALL ebyte ; emit it
INC BX ; next one
DEC.B AH
JNZ ecodelp ; :another
XCHG BX,[BP]$04 ; restore ret, BX
POP BP ; restore
POP AX
RET ; "
estring MOV.B AL,CH ; Emit string, CH=length
CALL ebyte ; emit length
estr2 MOV BX,#wordbuf ; buffer ptr
estrlp OR.B CH,CH ; test length
JZ estrret ; :null string
MOV.B AL,[BX] ; get char
CALL ebyte ; emit it
INC BX ; next one
DEC.B CH
JMP estrlp ; '
estrret RET ; "
epushax CALL ecode ; * PUSH AX
B $01,$50
RET ; "
epopax CALL ecode ; * POP AX
B $01,$58
RET ; "
epushdi CALL ecode ; * PUSH DI
B $01,$57
RET ; "
emovaxi CALL ecode ; * MOV AX,#..
B $01,$B8
JMP.b eword ; "emit immediate value in AX
CALL ecode ; * MOV BX,#..
B $01,$BB
JMP.b eword ; "emit immediate
emovcxi CALL ecode ; * MOV CX,#..
B $01,$B9
JMP.b eword ; "emit immediate
emovdxi CALL ecode ; * MOV DX,#..
B $01,$BA
JMP.b eword ; "emit immediate
emovdii CALL ecode ; * MOV DI,#..
B $01,$BF
JMP.b eword ; "emit immediate
ecall CALL ecode ; * CALL ..
B $01,$E8
JMP.b ejmp2 ; 'emit offset
ejump CALL ecode ; * JMP ..
B $01,$E9
ejmp2 SUB AX,pc ; dest-PC-2 -> offset
DEC AX
DEC AX
JMP.b eword ; "emit offset word
emitdx MOV AX,DX ; emit DX
eword CALL ebyte ; emit AX
PUSH AX ; save it
MOV.B AL,AH ; emit high byte
CALL ebyte ; emit byte
POP AX ; restore
RET ; "
ebyte CMP.B flgpshax,#$00 ; Emit byte in AL
JZ ebes ; :no PUSH AX
PUSH AX ; save
MOV AL,#$50 ; * PUSH AX
CALL ebemit
POP AX
MOV.B flgpshax,#$00 ; reset flag
ebes CMP.B flgpshes,#$00 ; emit PUSH ES ?
JZ ebdi ; :no
PUSH AX ; save
MOV AL,#$06 ; * PUSH ES
CALL ebemit
POP AX ; restore
MOV.B flgpshes,#$00 ; reset flag
ebdi CMP.B flgpshdi,#$00 ; emit PUSH DI ?
JZ ebemit ; :no
PUSH AX ; save
MOV AL,#$57 ; * PUSH DI
CALL ebemit
POP AX ; restore
MOV.B flgpshdi,#$00 ; reset flag
ebemit PUSH BX ; save
CMP.B cpmode,#$01 ; find runtime error ?
JZ ebfind ; :yes
MOV BX,cdptr ; code ptr-code pos of buffer
SUB BX,cdbufpt
ADD BX,cdbegpt ; + beg of code buffer
MOV ES,destseg ; dest segment
ES:
MOV.B [BX],AL ; store byte
JMP.b ebend ; '
ebfind MOV BX,pc ; PC = error pos ?
CMP BX,errpos2
JZ ebfound ; :yes - found !
ebend INC cdptr ; inc code pointers
INC pc
MOV BX,pc ; get PC
INC.B BH ; overflow ?
JZ eberr ; :yes
OR.B BL,BL ; 256 bytes done ?
JNZ ebnochk ; :no
CALL chkovrfl ; test for overflow
ebnochk POP BX ; restore BX
RET ; '
eberr CALL err ; 98:Memory overflow
B $62 ; '
ebfound CALL err ; C8:Error position found
B $C8 ; "
codflushCMP.B cpmode,#$02 ; Flush code buffer
JB cfret ; not to file:ret
MOV CX,cdptr ; code pointer
SUB CX,cdbufpt ; = code pos of buffer ?
STC
JZ cfret ; yes: nothing to flush
MOV AH,#$40 ; write byte block
MOV BX,dstfile ; dest file handle
MOV DX,cdbegpt ; beginning of code buffer
PUSH DS ; save DS
MOV DS,destseg ; dest segment
CALL dos ; write buffer
POP DS ; restore DS
JB cferr ; :error
CMP AX,CX ; length = expected ?
JZ cfok ; :yes
cferr CALL err ; C9:File error
B $C9 ; '
cfok MOV AX,cdptr ; code ptr -> code pos of buffer
MOV cdbufpt,AX
cfret RET ; "
ptcjmppcMOV AX,pc ; Patch jump address
ptcjmp SUB AX,BX ; dest (AX)-src (BX)-2
DEC AX
DEC AX
ptcjmp2 PUSH BX ; save dest
SUB BX,pc ; dest-PC+code ptr
ADD BX,cdptr
CALL patch ; patch it
POP BX ; restore
RET ; "
patch CMP.B cpmode,#$01 ; Patch word AX at pos BX
JZ ptret ; find error:ret
CMP BX,cdbufpt ; >= code pos of buffer ?
JB ptlist ; no: put it into patch list
PUSH BX ; save pos
SUB BX,cdbufpt ; -code pos of buffer
ADD BX,cdbegpt ; +beg of code buffer
MOV ES,destseg ; dest segment
ES:
MOV [BX],AX ; patch it in memory
POP BX ; restore
ptret RET ; '
ptlist PUSH CX ; save regs
PUSH DI ; patch list is sorted to
PUSH SI ; minimize disk accesses
PUSH SS ; SS -> ES
POP ES
MOV SI,ptcbeg ; start of patch list
MOV DI,ptctop ; top of patch list
ptsearchCMP SI,DI ; at the end ?
JZ ptstore ; yes: put it there
SS:
CMP BX,[SI] ; put it here ?
JB ptins ; :yes
ADD SI,#$04 ; go to next entry
JMP ptsearch ; 'continue searching
ptins MOV CX,DI ; calculate count to shift
SUB CX,SI
MOV SI,DI ; end position
ADD DI,#$04 ; -> end pos+4
DEC DI
DEC SI
PUSH DS ; save DS
PUSH SS ; SS -> DS
POP DS
STD ; make space for new entry
REPZ
MOVS.B
POP DS ; restore DS
INC SI ; point to dest pos
ptstore SS: ; store in patch list:
MOV [SI],BX ; address
SS:
MOV [SI]$02,AX ; value to be patched
ADD ptctop,#$04 ; add 4 to patch top
MOV CX,ptctop ; patch list top = end of
CMP CX,ptcend ; patch list space ?
JNZ ptnofl ; :no
CALL ptcflush ; flush patch list
ptnofl POP SI ; restore registers
POP DI
POP CX
RET ; "
ptcflushCMP.B cpmode,#$02 ; Patch code in file
JB ptfret ; no file - ret
PUSH AX ; save regs
PUSH BX
PUSH CX
PUSH DX
PUSH BP
MOV AX,#$4201 ; seek relative
MOV BX,dstfile ; dest file handle
XOR CX,CX ; get current pos in file
XOR DX,DX
CALL dos
PUSH AX ; save it
PUSH DX
MOV BP,ptcbeg ; start of patch list
ptflp CMP BP,ptctop ; end reached ?
JZ ptfend ; :yes
MOV AX,#$4200 ; seek absolute
MOV BX,dstfile ; dest file handle
MOV DX,cdfoff ; file offset
MOV CX,cdfoff1
ADD DX,[BP]$00 ; + patch addr
ADC CX,#$00 ; carry
CALL dos ; do seek
MOV AX,[BP]$02 ; get value to patch
MOV ptcbuf1,AX ; store in buffer
MOV AH,#$40 ; write patch value
MOV BX,dstfile ; dest file handle
MOV CX,#$0002 ; 2 bytes
MOV DX,#ptcbuf1 ; buffer ofs
CALL dos
CALL errb
B $C9 ; C):File error
ADD BP,#$04 ; next patch list entry
JMP ptflp ; '
ptfend MOV AX,ptcbeg ; clear patch list:
MOV ptctop,AX ; beg -> top
MOV AX,#$4200 ; seek absolute
MOV BX,dstfile ; dest file handle
POP CX ; restore current pos
POP DX
CALL dos ; set it again
POP BP ; restore regs
POP DX
POP CX
POP BX
POP AX
ptfret RET ; "
pushe2 MOV SI,#parm2 ; save entry 2 on stack
JMP.b pshe1 ; '
pushe1 MOV SI,#indflg ; save entry 1 on stack
pshe1 POP retbuf ; ret addr
MOV cxbuf,CX ; save CX
MOV CX,SS ; SS -> ES
MOV ES,CX
MOV CX,#$000F ; 15 bytes
SUB SP,CX ; make space on stack
MOV DI,SP ; dest: stack
CLD
REPZ
MOVS.B ; move it
psheend MOV CX,cxbuf ; restore CX
JMP [retbuf] ; "return
pope2 MOV DI,#parm2 ; restore entry 2 from stack
JMP.b pope ; '
pope1 MOV DI,#indflg ; restore entry 1 from stack
pope POP retbuf ; ret addr
MOV cxbuf,CX ; save CX
MOV SI,SP ; SP -> source
MOV CX,DS ; DS -> ES
MOV ES,CX
MOV CX,SS ; SS -> DS
MOV DS,CX
MOV CX,#$000F ; 15 bytes
CLD
REPZ
MOVS.B ; move it
MOV CX,ES ; restore DS
MOV DS,CX
MOV SP,SI ; remove entry from stack
JMP psheend ; "return
copye2 MOV DI,#parm2 ; copy entry 2 from stack
JMP.b cpe ; '
MOV DI,#indflg ; copy entry 1 from stack
cpe PUSH CX ; save CX
MOV SI,SP ; source: on stack
ADD SI,#$04 ; skip ret addr, CX
MOV CX,DS ; DS -> ES
MOV ES,CX
MOV CX,SS ; SS -> DS
MOV DS,CX
MOV CX,#$000F ; 15 bytes
CLD
REPZ
MOVS.B ; copy it
MOV CX,ES ; restore DS
MOV DS,CX
POP CX ; restore CX
RET ; "
symbyte MOV BP,symtop ; store AL in symtab
DEC BP ; down one byte
MOV.B [BP]$00,AL ; store it
MOV symtop,BP ; set new symtab top
RET ; "
symword MOV BP,symtop ; store AX in symtab
DEC BP ; down two bytes
DEC BP
MOV [BP]$00,AX ; store it
MOV symtop,BP ; set new symtab top
RET ; "
symoffs MOV AX,symtop2 ; Write symtab offset
SUB AX,symtop ; last entry-current+2
ADD AX,#$0002
CALL symword ; store offset
MOV AX,symtop ; symtab top ->
MOV symtop2,AX ; symtab top at beg of definition
JMP chkovrfl ; "test size
stotype MOV AX,#$0800 ; store type
CALL symword ; store tag: subtype
MOV AX,symtop ; symtab top
MOV vartp,AX ; -> type ptr
MOV BX,#parm1end ; source buffer
MOV BP,symtop ; destination
stotlp DEC BX ; go down
DEC BX ; write size, upper bound,
DEC BP ; lower bound, component type
DEC BP
MOV AX,[BX] ; get word
MOV [BP]$00,AX ; store it
CMP BX,#varctp ; end ?
JNZ stotlp ; :not yet
MOV symtop,BP ; set new top
CALL symoffs ; write offset
XOR AX,AX ; ok
RET ; "
getparm MOV BX,#maxsize ; Get var parms -> entry 2
JMP.b gvp2 ; '
getvprm MOV AX,[BP]-$06 ; Get var parms
MOV indptflg,AX ; var segment
MOV AX,[BP]-$04 ; var offset
MOV varofs,AX
MOV BP,[BP]-$02 ; type pointer
getvprm2MOV BX,#parm1end ; to entry 1
gvp2 PUSH CX ; save
MOV CX,#$0004 ; 8 bytes
gvplp DEC BP ; go down
DEC BP ; copy size, upper & lower bound,
DEC BX ; component type
DEC BX
MOV AX,[BP]$00 ; copy entry -> buffer
MOV [BX],AX
LOOP gvplp ; :another
POP CX ; restore
RET ; "
rdsymnewMOV AX,#$0100 ; 'Read symbol
JMP.b rsy1 ; 'no numbers, verify in symtab
rdsym0 MOV AX,#$0001 ; numbers allowed
JMP.b rsy1 ; '
rdsym XOR AX,AX ; no numbers
rsy1 PUSH AX ; save flag
CALL rdword ; read word
POP AX ; restore flag
OR.B AL,AL ; numbers allowed ?
MOV AL,wrdbuf1 ; first char ?
JZ rsynonum ; :no
CALL number ; 0..9 ?
JNB rsynum ; yes: ok
rsynonumCALL alpha ; valid char ?
CALL errb ; no:
B $3A ; 58:Illegal char in ID
rsynum OR.B AH,AH ; verify in symbol table ?
JNZ rsynotst ; :no
CALL dupvar ; test if duplicate
rsynotstCALL dupkey ; test if keyword
MOV BP,symtop ; destination
MOV.B BL,wordbuf ; word length
XOR.B BH,BH ; -> count
DEC BP ; go down
MOV.B [BP]$00,BL ; store length
rsysto DEC BP ; go down
MOV.B AL,[BX]wordbuf ; get char from buffer
MOV.B [BP]$00,AL ; store in symtab
DEC BX ; go back
JNZ rsysto ; :another char
MOV symtop,BP ; set new symtab top
rsynext MOV DI,wrdend ; go to end of word
JMP skipdi ; "get next word
rdword CMP.B wordflg,#$FF ; read word: word available ?
JNZ rdwret ; yes: ret
XOR BX,BX
MOV DI,chptr ; char ptr: source
MOV.B AL,[DI] ; get char
CALL alphanum ; char in alphanum ?
JB rdwother ; :no
rdwlp CMP.B BL,#$7F ; end of buffer ?
JZ rdwfull ; :yes - don't store
CMP AL,#$61 ; do UpCase
JB rdwupper ; :no
SUB AL,#$20
rdwupperINC BX ; go to next pos
MOV.B [BX]wordbuf,AL ; store char in buffer
rdwfull INC DI ; next char from source
MOV.B AL,[DI] ; get it
CALL alphanum ; in alphanum ?
JNB rdwlp ; yes: loop back
JMP.b rdwend ; 'no: end
rdwotherINC DI ; go to next pos
INC BX
MOV.B [BX]wordbuf,AL ; store char
MOV.B AL,[DI] ; next char
CMP AL,#$2E ; . ?
JZ rdwchar2 ; :yes
CMP AL,#$3D ; = ?
JZ rdwchar2 ; :yes
CMP AL,#$3E ; > ?
JNZ rdwend ; :no
rdwchar2INC DI ; store that char
INC BX
MOV.B [BX]wordbuf,AL
rdwend MOV.B wordbuf,BL ; store word length
MOV wrdend,DI ; store end addr of word
MOV.B wordflg,#$FE ; word available
rdwret RET ; "
search CALL srchall ; search whole symbol table
JNZ rdwret ; not found:ret
JMP skipdi ; "get next word
srchvar MOV BX,fence ; current var fence
JMP.b src1 ; '(BX=search limit)
srchall MOV BX,ptcbeg ; start of symtab
src1 MOV DX,CX ; type wanted
CMP.B DL,wordflg ; = type of current word ?
JZ srcsame ; :yes
PUSH BX ; save fence
CALL rdword ; read word
POP BX ; restore
MOV.B wordflg,DL ; store type wanted
CALL srchsym ; search in symbol table
JB srcnofnd ; :not found
MOV typept,AL ; store type returned
MOV sympos,BP ; store pos in symbol table
srcsame MOV BP,sympos ; get symtab pos
MOV DI,wrdend ; get end pos of word
MOV CX,DX ; type -> CX
CMP.B CH,typept ; compare type
RET ; '
srcnofndXOR AX,AX ; type returned: none
MOV typept,AL
DEC AX ; not found
RET ; "
srchsym MOV.B CL,wordbuf ; word length
XOR.B CH,CH ; -> count
MOV BP,symtop2 ; symtab position
PUSH SS ; SS -> ES
POP ES
CLD ; forward search
srchloopCMP BP,BX ; fence reached ?
JZ srsynf ; yes: not found
ADD BP,[BP]$00 ; add offset
MOV.B AL,[BP]-$01 ; get tag byte
OR.B AL,AL ; 0 ?
JZ srchloop ; yes: invisible entry
CMP AL,#$08 ; subtype ?
JZ srchloop ; yes: invisible
MOV AX,[BP]-$03 ; string length
CMP.B AL,CL ; = searched ?
JNZ srchloop ; :no
CMP.B AH,DL ; = wanted type ?
JNZ srchloop ; :no
MOV DI,BP ; calculate string position
SUB DI,#$03
SUB DI,CX ; - count
MOV SI,#wrdbuf1 ; word ptr
MOV AX,CX ; save count
REPZ ; do comparison
CMPS.B
XCHG AX,CX ; restore count
JNZ srchloop ; not the same: continue
MOV.B AL,[BP]-$01 ; get type
SUB BP,#$03 ; set ptr to beg of string
SUB BP,CX ; = beg of entry
CLC ; found !
RET ; '
srsynf STC ; not found
RET ; "
ckey CALL rdword ; check keyword: read word
POP BX ; return addr
CS:
MOV.B DL,[BX] ; get offset between words
CS:
MOV DI,[BX]$01 ; get pointer
ADD BX,#$03 ; skip inline parms
PUSH BX ; restore ret addr
ckey2 XOR.B DH,DH ; distance between keywords
MOV BX,#wordbuf ; word ptr
PUSH CS ; CS -> ES
POP ES
CLD ; forward search
ckloop CS:
MOV.B CL,[DI] ; get length
XOR.B CH,CH
JCXZ cknf ; nothing - end of list
INC CX ; -> count
MOV SI,BX ; source
REPZ
CMPS.B ; compare words
JZ ckfound ; :equal
ADD DI,CX ; add remaining count
ADD DI,DX ; add offset
JMP ckloop ; 'try next keyword
cknf DEC CX ; flag: not found
RET ; '
ckfound MOV BX,DI ; position in keyword table
CALL rsynext ; get next word
CS:
MOV AX,[BX] ; get word from keyword table
ckret RET ; "
ctoken CALL rdword ; Check keyword: read word
POP BX ; return address
CS:
MOV DI,[BX] ; inline parm: pointer
INC BX ; skip inline parm
INC BX
PUSH BX ; restore ret
MOV SI,#wordbuf ; word ptr
MOV.B CL,[SI] ; get length of keyword
XOR.B CH,CH ; -> count
INC CX
PUSH CS ; CS -> ES
POP ES
CLD ; compare words
REPZ
CMPS.B
JNZ ckret ; :not the same - ret
JMP rsynext ; "ok, get next word
dupkey MOV BX,#keytable ; new ID = keyword ?
dkloop CS: ; pointer: list of keyword areas
MOV.B DL,[BX] ; get offset
CMP.B DL,#$FF ; end of table ?
JZ dkret ; :yes
PUSH BX ; save pointer
CS:
MOV DI,[BX]$01 ; get pointer into table
CALL ckey2 ; check it
POP BX ; restore pointer
CALL errz ; yes:
B $35 ; 53:Reserved word
INC BX ; next entry
INC BX
INC BX
JMP dkloop ; 'do next one
dkret RET ; "
dupvar MOV BX,fence ; Duplicate ID ?
MOV.B DL,recnum ; expected type
CALL srchsym ; search in symbol table
JB dkret ; :ret
CALL err ; 43:Duplicate ID or label
B $2B ; "
csqr1 MOV DI,chptr ; Check [, (.
CMP.B [DI],#$5B ; [ ?
JZ chkskip ; yes: skip it
CMP.B [DI],#$28 ; ( ?
JNZ chkret ; no: ret
INC DI ; next char
CMP.B [DI],#$2E ; . ?
JMP.b chkcskip ; 'check it
csqr2 MOV DI,chptr ; Check ], .)
CMP.B [DI],#$5D ; ] ?
JZ chkskip ; yes: skip it
CMP.B [DI],#$2E ; . ?
JNZ chkret ; no: ret
INC DI ; next char
CMP.B [DI],#$29 ; ) ?
JMP.b chkcskip ; 'check it
ccolon MOV AL,#$3A ; : ?
JMP.b chkal ; 'check it
csemi MOV AL,#$3B ; semicolon ?
JMP.b chkal ; 'check it
ccomma MOV AL,#$2C ; , ?
JMP.b chkal ; 'check it
cdot MOV AL,#$2E ; . ?
JMP.b chkal ; 'check it
cbrack1 MOV AL,#$28 ; ( ?
JMP.b chkal ; 'check it
cbrack2 MOV AL,#$29 ; ) ?
JMP.b chkal ; 'check it
cequal MOV AL,#$3D ; = ?
JMP.b chkal ; 'check it
cptr MOV AL,#$5E ; ^ ?
chkal MOV DI,chptr ; current char = AL ?
CMP.B AL,[DI] ; compare
chkcskipJNZ chkret ; no: ret
chkskip INC DI ; skip it
JMP.b skipdi ; 'read next word
NOP
chkret RET ; "
esqr1 CALL csqr1 ; expect [: check [
CALL errnz ; no:
B $08 ; 8:[ expected
RET ; "
esqr2 CALL csqr2 ; expect ]: check ]
CALL errnz ; no:
B $09 ; 9:] expected
RET ; "
ecolon CALL ccolon ; expect :
CALL errnz ; no:
B $02 ; 2:':' expected
ecolret RET ; "
esemi CALL csemi ; expect semicolon
JZ ecolret ; yes: ret
esemierrCALL err ; 1:Semicolon expected
B $01 ; '
esemi2 CALL csemi ; expect semicolon
JZ ecolret ; yes: ret
CMP.B semiflg,#$00 ; flag set ?
JZ esemierr ; yes: error 1
CALL err ; 41:Unknown ID or syntax error
B $29 ; "
ecomma CALL ccomma ; expect ,
CALL errnz ; no:
B $03 ; 3:',' expected
RET ; "
ebrack1 CALL cbrack1 ; expect (
CALL errnz ; no:
B $04 ; 4:'(' expected
RET ; "
ebrack2 CALL cbrack2 ; expect )
CALL errnz ; no:
B $05 ; 5:')' expected
RET ; "
eequal CALL cequal ; expect =
CALL errnz ; no:
B $06 ; 6:'=' expected
RET ; "
eassign CALL ctoken ; expect :=
W tkassign ; check token
CALL errnz ; no:
B $07 ; 7:':=' expected
RET ; "
expof CALL ctoken ; expect OF
W tkof ; check token
CALL errnz ; no:
B $0F ; 15:OF expected
RET ; "
; This routine skips spaces until next word is reached.
skip MOV DI,chptr ; get next word: get char ptr
skipdi MOV.B semiflg,#$00 ; flag for semicolon error
MOV.B wordflg,#$FF ; search flag
skloop MOV AX,[DI] ; get char (two, actually)
CMP AL,#$20 ; space, control ?
JBE skspace ; yes: skip spaces
CMP AL,#$7B ; '{' ?
JZ skcom2 ; yes: comment
CMP AX,#$2A28 ; '(*' ?
JZ skcom ; yes: comment
MOV chptr,DI ; set char pointer
XOR AX,AX ; ok
RET ; '
skspace CALL getchar ; skip spaces: get char
JMP skloop ; 'loop back
skcom CALL getchar ; Comment: get char
skcom2 PUSH DX ; save
MOV.B DL,[DI] ; comment type
CMP.B [DI]$01,#$24 ; $ ?
JZ cdirec ; yes: compiler directive
skcomlp CALL getchar ; get char
skcom3 MOV AX,[DI] ; test two chars
CMP.B DL,#$2A ; '(*' ?
JNZ skcomcur ; :no
CMP AX,#$292A ; now '*)' ?
JNZ skcomlp ; no: loop back
CALL getchar ; end of comment - get char
JMP.b skcomend ; 'continue scanning
skcomcurCMP AL,#$7D ; '}' ?
JNZ skcomlp ; no: loop back
skcomendPOP DX ; restore
JMP skspace ; "continue scanning
cdirec PUSH BX ; Compiler directive: save regs
PUSH CX
PUSH DX
CALL getchar ; get char
cdlop CALL getchar ; get char
MOV.B AL,[DI] ; get directive
CALL upcase ; UpCase
CMP AL,#$49 ; I ? I/O-error handling, Include
MOV DX,#$0001 ; flag
JZ cdplus ; :yes +-
CMP AL,#$52 ; R ? Range checking
MOV DX,#$0002
JZ cdplus ; :yes +-
CMP AL,#$42 ; B ? I/O-mode (CON or TRM)
MOV DX,#$0004
JZ cdplus ; :yes +-
CMP AL,#$43 ; C ? Control C and S
MOV DX,#$0008
JZ cdplus ; :yes +-
CMP AL,#$55 ; U ? User Interrupt
MOV DX,#$0010
JZ cdplus ; :yes +-
CMP AL,#$4B ; K ? Stack check
MOV DX,#$0020
JZ cdplus ; :yes +-
CMP AL,#$56 ; V ? Type checking
MOV DX,#$0040
JZ cdplus ; :yes +-
CMP AL,#$44 ; D ? Device checking
MOV DX,#$0080
JZ cdplus ; :yes +-
CMP AL,#$47 ; G ? Input file buffer size
MOV BX,#cinpsize ; dest var: buffer size
JZ cdnum ; :yes #
CMP AL,#$50 ; P ? Output file buffer size
MOV BX,#coutsize ; dest var: buffer size
JZ cdnum ; :yes #
CMP AL,#$46 ; F ? Max number open files
MOV BX,#cmaxfil ; dest var: max files
JZ cdnum ; :yes #
XOR DX,DX ; no flag
CMP AL,#$41 ; A ? Absolute code
JZ cdplus ; :yes +-
CMP AL,#$57 ; W ? WITH nesting
JZ cdignore ; :yes #
CMP AL,#$58 ; X ? Array optimization
JZ cdplus ; :yes +-
CMP AL,#$4F ; O ?
JZ cdignore ; :yes #
cderr CALL perr ; remember pos
B $5D ; '93:Invalid compiler directive
cdend POP DX ; restore regs
POP CX
POP BX
JMP skcom3 ; 'continue scanning comment
cdplus CALL getchar ; +/- directive: get char
MOV.B AL,[DI] ; read it
XOR CX,CX ; clear flag
CMP AL,#$2B ; '+' ?
JZ cdset ; :yes
DEC CX ; set flag
CMP AL,#$2D ; '-' ?
JZ cdset ; :yes
CMP DX,#$01 ; include ?
JZ cdinclsk ; yes: do it
JMP cderr ; 'Invalid compiler directive
cdset MOV AX,direct ; old directives
XOR AX,CX ; invert if reset bit
OR AX,DX ; set / reset bit
XOR AX,CX ; invert if reset bit
MOV direct,AX ; set new directive
CALL getchar ; get char
cdnext MOV.B AL,[DI] ; ',' ?
CMP AL,#$2C
JNZ cdend ; no: end directive
JMP cdlop ; 'yes: loop back
cdignoreCALL getchar ; Ignore directive: get char
MOV.B AL,[DI]
CALL alphanum ; valid char ?
JNB cdignore ; yes: continue
JMP cdnext ; 'another directive ?
cdnum CALL getchar ; Get number: get char
PUSH BX ; save dest ptr
MOV BX,DI ; position
CALL asccard ; read integer
MOV DI,BX ; new position
POP BX ; dest ptr
JB cderr ; :error
OR AX,AX ; 0 ?
JZ cderr ; yes: error
MOV [BX],AX ; store result
JMP cdnext ; 'another directive ?
cdinclskCMP.B [DI],#$20 ; Set include file: space ?
JNZ cdincl ; no: do it
CALL getchar ; get char
JMP cdinclsk ; 'skip spaces
cdincl CMP.B inclflg,#$00 ; include file active ?
CALL errnz ; yes:
B $60 ; 96:Illegal nesting of include files
MOV BX,DI ; position
CALL kpasext ; parse filename, default .PAS
PUSH BX ; save pos
MOV DI,#inclpn ; copy path name
CALL fnscdi ; -> include filename buffer
POP DI ; restore pos
MOV AX,#$3D00 ; open file
MOV DX,#inclpn ; name ptr
PUSH DS ; DS -> ES
POP ES
CALL dos ; open it
CALL errb ; 90:File not found
B $5A
MOV incfile,AX ; store file handle
MOV AX,direct ; save compiler directives
MOV direcin,AX
XOR AX,AX ; clear vars:
MOV bufpt,AX ; buffer ptr
MOV bufend,AX ; buffer end
MOV frelpos,AX ; relative pos in file
MOV.B inclflg,#$FF ; set include flag
CALL disline ; display line number
JMP cdend ; "restore regs, continue scanning
getchar MOV.B AL,[DI] ; Get char
INC DI ; go to next one
OR.B AL,AL ; end of line ?
JZ getln ; :yes
RET ; '
getln PUSH BX ; save regs
PUSH CX
PUSH DX
CMP.B srcend,#$00 ; last line ?
CALL perrnz ; yes:
B $5B ; 91:Unexpected end of source
INC lincnt ; count lines
MOV DI,#pnbuf ; destination: line buffer
MOV CX,#$007F ; up to 127 chars
CMP.B inclflg,#$00 ; from include file ?
JNZ fileline ; :yes
MOV BX,srcptr ; pointer into text
MOV srclnbeg,BX ; -> beginning of source line
memline MOV.B AL,[BX] ; get char
CMP AL,#$1A ; ^Z ?
JZ memeof ; yes: mark end of source
INC BX ; next char
CMP AL,#$0D ; CR ?
JZ memlf ; yes: end of line
MOV.B [DI],AL ; store in buffer
INC DI ; next pos
LOOP memline ; :another char
JMP.b memsto ; 'end it
memlf CMP.B [BX],#$0A ; LF ?
JNZ memsto ; no: forget it
INC BX ; yes: skip it
JMP.b memsto ; 'set new pos
memeof MOV srcend,AL ; set flag: end of source
memsto MOV srcptr,BX ; store new pos in buffer
JMP.b filesto ; 'mark end of line
filelineMOV BX,bufpt ; buffer pointer
MOV AX,frelpos ; relative pos in file
ADD AX,BX ; + buffer offset
SUB AX,bufend ; - buffer end
MOV srclnbg,AX ; -> pos of line begin
fileloopCALL getincl ; get char from include file
CMP AL,#$1A ; ^Z ?
JZ fileeof ; :yes, close include
INC BX ; next char
CMP AL,#$0D ; CR ?
JZ filelf ; yes: end of line
MOV.B [DI],AL ; store char in buffer
INC DI ; next pos
LOOP fileloop ; :another char
JMP.b filelf2 ; 'end it
filelf CALL getincl ; get char from include file
CMP AL,#$0A ; LF ?
JNZ filelf2 ; no: forget it
INC BX ; skip
filelf2 MOV bufpt,BX ; store buffer ptr
JMP.b filesto ; 'mark end of line
fileeof MOV.B inclflg,#$00 ; clear include flag
MOV AX,direcin ; restore directives to
MOV direct,AX ; state before include
MOV AH,#$3E ; close
MOV BX,incfile ; file handle
CALL dos
filesto MOV.B [DI],#$00 ; store a 0 at the end
MOV DI,#pnbuf ; go to beg of buffer
CALL disline1 ; display line number
POP DX ; restore regs
POP CX
POP BX
RET ; "
getincl CMP BX,bufend ; Get char from include file
JB ginend ; :not yet end of buffer
PUSH CX ; save
MOV AH,#$3F ; read byte block
MOV BX,incfile ; file handle
MOV CX,#$0080 ; 128 bytes
MOV DX,#inclbuf ; buffer ptr
MOV SI,#$0080 ; ?
CALL dos ; read buffer
POP CX ; restore
JNB ginok ; :ok
XOR AX,AX ; nothing read
ginok MOV BX,#inclbuf ; start of buffer
OR AX,AX ; anything read ?
JNZ ginnoeof ; :yes
MOV.B [BX],#$1A ; store ^Z
INC AX ; 1 char
ginnoeofADD frelpos,AX ; relative pos in file
ADD AX,BX ; pos of buffer end
MOV bufend,AX
ginend MOV.B AL,[BX] ; get char
RET ; "
disline1TEST.B lincnt,#$0F ; Display line number
JZ disline ; :once every 16 lines
RET ; '
disline PUSH AX ; save regs
PUSH BX
PUSH CX
PUSH DX
MOV AL,#$0D ; write CR
CALL conput
CMP.B inclflg,#$00 ; include ?
JZ dismem ; :no
MOV AL,#$49 ; I
JMP.b disincl ; '
dismem MOV AL,#$20 ; space
disincl CALL conput ; write it
MOV AL,#$20 ; write space
CALL conput
MOV AX,lincnt ; get line number
CALL knum1 ; display it
CALL xkeypres ; Keypressed
OR.B AL,AL ; test flag
JZ disret ; :no
CALL prints ; write string
B " *** Abort compilation",$00
CALL yorn ; Y or N ?
JZ disok ; no: continue
CALL err ; CA:Compilation aborted
B $CA ; '
disok MOV CX,#$0020 ; clear 32 bytes
disera CALL prints ; write BS space BS
B $08," ",$08,$00
LOOP disera ; :another
disret POP DX ; restore regs
POP CX
POP BX
POP AX
RET ; "
; *** Start of Tables ***
; Standard definitions
W $000E,$02FC
B "INTEGER",$07,$00,$03
W $000B,$0308
B "CHAR",$04,$00,$03
W $000B,$0314
B "REAL",$04,$00,$03
W $000E,$0320
B "BOOLEAN",$07,$00,$03
W $000B,$0338
B "BYTE",$04,$00,$03
W $000C,$0001
B $0B,"TRUE",$04,$00,$02
W $000D,$0000
B $0B,"FALSE",$05,$00,$02
W $000E,$7FFF
B $0A,"MAXINT",$06,$00,$02
W $000D,$0000
B $0A,"BLACK",$05,$00,$02
W $000C,$0001
B $0A,"BLUE",$04,$00,$02
B $0D,$00,$02,$00
B $0A,"GREEN",$05,$00,$02
W $000C,$0003
B $0A,"CYAN",$04,$00,$02
W $000B,$0004
B $0A,"RED",$03,$00,$02
W $000F,$0005
B $0A,"MAGENTA",$07,$00,$02
W $000D,$0006
B $0A,"BROWN",$05,$00,$02
W $0011,$0007
B $0A,"LIGHTGRAY",$09,$00,$02
W $0010,$0008
B $0A,"DARKGRAY",$08,$00,$02
W $0011,$0009
B $0A,"LIGHTBLUE",$09,$00,$02
W $0012,$000A
B $0A,"LIGHTGREEN",$0A,$00,$02
W $0011,$000B
B $0A,"LIGHTCYAN",$09,$00,$02
W $0010,$000C
B $0A,"LIGHTRED",$08,$00,$02
W $0014,$000D
B $0A,"LIGHTMAGENTA",$0C,$00,$02
W $000E,$000E
B $0A,"YELLOW",$06,$00,$02
W $000D,$000F
B $0A,"WHITE",$05,$00,$02
W $000D,$0010
B $0A,"BLINK",$05,$00,$02
W $000C,$0000
B $0A,"BW40",$04,$00,$02
W $000C,$0002
B $0A,"BW80",$04,$00,$02
W $000B,$0001
B $0A,"C40",$03,$00,$02
W $000B,$0003
B $0A,"C80",$03,$00,$02
W $000E
W $2182,$DAA2,$490F ; 3.1415926536E+00
B $09,"PI",$02,$00,$02
W $0011,$FF00
W conbufln
W $0338
B "BUFLEN",$06,$00,$04
W $0012,$FF00
W hptop
W $0344
B "HEAPPTR",$07,$00,$04
W $0011,$FF00
W stdout
W $032C
B "OUTPUT",$06,$00,$04
W $0010,$FF00
W stdin
W $032C
B "INPUT",$05,$00,$04
W $000E,$FF00
W filcon
W $032C
B "CON",$03,$00,$04
W $000E,$FF00
W filcon
W $032C
B "TRM",$03,$00,$04
W $000E,$FF00
W filkbd
W $032C
B "KBD",$03,$00,$04
W $000E,$FF00
W fillst
W $032C
B "LST",$03,$00,$04
W $000E,$FF00
W filaux
W $032C
B "AUX",$03,$00,$04
W $000E,$FF00
W filusr
W $032C
B "USR",$03,$00,$04
W $0011,$FF00
W cbreak
W $0320
B "CBREAK",$06,$00,$04
W $0013,$FF00
W vkbdstat
W $02FC
B "CONSTPTR",$08,$00,$04
W $0013,$FF00
W vkbdget
W $02FC
B "CONINPTR",$08,$00,$04
W $0014,$FF00
W vconput
W $02FC
B "CONOUTPTR",$09,$00,$04
W $0014,$FF00
W vprnput
W $02FC
B "LSTOUTPTR",$09,$00,$04
W $0014,$FF00
W vauxput
W $02FC
B "AUXOUTPTR",$09,$00,$04
W $0013,$FF00
W vauxget
W $02FC
B "AUXINPTR",$08,$00,$04
W $0014,$FF00
W vusrput
W $02FC
B "USROUTPTR",$09,$00,$04
W $0013,$FF00
W vusrget
W $02FC
B "USRINPTR",$08,$00,$04
W $0013,$FF00
W verror
W $02FC
B "ERRORPTR",$08,$00,$04 ; "
; Standard types
; link type lower upper size tag: invisible
B $0C,$00,$0A,$00,$00,$80, ; integer
B $0C,$00,$0C,$00,$00,$00, ; char
B $0C,$00,$09,$00,$00,$00, ; real
B $0C,$00,$0B,$00,$00,$00, ; boolean
B $0C,$00,$06,$00,$00,$00, ; text file
B $0C,$00,$0A,$00,$00,$00, ; byte
B $0C,$00,$04,$00,$00,$00, ; pointer
B $0C,$00,$08,$00,$00,$00, ; string
B $0C,$00,$00,$00,$00,$00, ; untyped file
B $0C,$00,$07,$00,$00,$00, ; "typed file
; Patch table to patch in pointers to type entries
; Offsets relative to 9277
varpatchW $0002,$0010,$001B,$0026,$0034,$01A0,$01B1,$01C3,$01D4
W $01E4,$01F2,$0200,$020E,$021C,$022A,$0249,$025C,$026F
W $0283,$0297,$02AB,$02BE,$02D2,$02E5,$0238
; "
keytableB $00 ; Pointers into keyword table
W tkprog ; (offset between entries, pointer)
B $01
W tklabel
B $02
W tkbegin
B $04
W tkto
B $05
W tkmul2
B $05
W tkadd2
B $02
W tkcmp2
B $FF ; "end of table
tkprog B $07,"PROGRAM" ; Keyword table
tkend B $03,"END"
tkforwrdB $07,"FORWARD"
tkext B $08,"EXTERNAL"
tkpackedB $06,"PACKED"
tkarray B $05,"ARRAY"
tkfile B $04,"FILE"
tkset B $03,"SET"
tkrec B $06,"RECORD"
tkstr B $06,"STRING"
tkof B $02,"OF"
tkabs B $08,"ABSOLUTE"
tkthen B $04,"THEN"
tkelse B $04,"ELSE"
tkdo B $02,"DO"
tkuntil B $05,"UNTIL"
tknot B $03,"NOT"
tknil B $03,"NIL",$00 ; '
tktext B $04,"TEXT" ; other reserved words
tk2dot B $02,".."
tkassignB $02,":=" ; '
tklabel B $05,"LABEL",$01 ; definition part
B $05,"CONST",$02 ; byte at the end = token
B $04,"TYPE",$03
tkvar B $03,"VAR",$04
B $05,"BEGIN",$08
tkover B $07,"OVERLAY",$07
tkproc B $09,"PROCEDURE",$05
B $08,"FUNCTION",$06,$00 ; '
tkbegin B $05,"BEGIN" ; program part
W block ; word = vector to compiler routine
B $02,"IF"
W if
B $05,"WHILE"
W while
B $06,"REPEAT"
W repeat
B $03,"FOR"
W for
tkcase B $04,"CASE"
W case
B $04,"GOTO"
W goto
B $04,"WITH"
W with
B $06,"INLINE"
W inline
B $00 ; '
tkto B $02,"TO" ; keywords used with FOR
B $7D,$41,$49,$00 ; JGE / INC CX / DEC CX
B $06,"DOWNTO"
B $7E,$49,$41,$08 ; JNG / DEC CX / INC CX
B $00 ; '
tkmem B $03,"MEM",$01 ; special arrays
B $04,"MEMW",$02 ; (not reserved)
B $00 ; '
; Code descriptors: parameters for the code generator
; +0:operation #
; +1:opcode immediate form
; +2:standard opcode
; +4:option bits
; 1:XCHG CX,AX needed
; 2:no immediate form available
; 4:no var form available
; 8:CWD needed
; " 10:XCHG DX,AX needed at end
tkmul B $01,"*" ; Multiplication ops
B $00,$00,$F7,$E9,$02 ; no imme, IMUL CX
B $01,"/"
B $01,$00,$00,$00,$00 ; real only !
tkmul2 B $03,"AND" ; AND AX,CX
B $02,$25,$23,$C1,$00
B $03,"MOD" ; IDIV CX
B $03,$00,$F7,$F9,$1B ; no imme, both XCHG, CWD
B $03,"DIV" ; IDIV CX
B $04,$00,$F7,$F9,$0B ; no imme, XCHG CX,AX, CWD
B $03,"SHL" ; SHL AX,CL
B $05,$00,$D3,$E0,$07 ; no imme, no var, XCHG CX,AX
B $03,"SHR" ; SHR AX,CL
B $06,$00,$D3,$E8,$07 ; no imme, no var, XCHG CX,AX
B $00 ; '
tkadd B $01,"+" ; Addition ops
B $00,$05,$03,$C1,$00 ; ADD AX,CX
B $01,"-" ; SUB AX,CX
B $01,$2D,$2B,$C1,$01 ; XCHG CX,AX
tkadd2 B $02,"OR" ; OR AX,CX
B $02,$0D,$0B,$C1,$00
B $03,"XOR" ; XOR AX,CX
B $03,$35,$33,$C1,$00
B $00 ; '
tkcmp B $01,"=" ; Comparison ops
B $00,$74 ; table offset, branch op: JZ
B $02,"<>"
B $08,$75 ; JNZ
B $02,">="
B $10,$7D ; JGE
B $02,"<="
B $18,$7E ; JNG
B $01,">"
B $20,$7F ; JG
B $01,"<"
B $28,$7C ; JL
tkcmp2 B $02,"IN"
B $FF,$00 ; special
B $00 ; '
cmpcode B $00,$3D,$3B,$C1,$01 ; "CMP AX,CX XCHG CX,AX
stdprocsB $07,"WRITELN" ; Standard procedures
W pwriteln ; vector to COMPILER routine
B $05,"WRITE"
W pwrite
B $06,"READLN"
W preadln
B $04,"READ"
W pread
B $06,"DELETE"
W pdelete
B $06,"INSERT"
W pinsert
B $06,"GOTOXY"
W pgotoxy
B $06,"ASSIGN"
W passign
B $05,"RESET"
W preset
B $07,"REWRITE"
W prewrite
B $06,"APPEND"
W pappend
B $05,"CLOSE"
W pclose
B $05,"ERASE"
W perase
B $06,"RENAME"
W prename
B $04,"SEEK"
W pseek
B $08,"LONGSEEK"
W pseek
B $03,"NEW"
W pnew
B $04,"MARK"
W pmark
B $07,"RELEASE"
W prelease
B $06,"GETMEM"
W pgetmem
B $07,"DISPOSE"
W pdispose
B $07,"FREEMEM"
W pfreemem
B $03,"STR"
W pstr
B $03,"VAL"
W pval
B $09,"BLOCKREAD"
W pblockrd
B $0A,"BLOCKWRITE"
W pblockwr
B $05,"CHDIR"
W pchdir
B $05,"MKDIR"
W pmkdir
B $05,"RMDIR"
W prmdir
B $06,"GETDIR"
W pgetdir
B $07,"OVRPATH"
W povrpath
B $09,"RANDOMIZE"
W prndmize
B $04,"MOVE"
W pmove
B $08,"FILLCHAR"
W pfillchr
B $04,"EXIT"
W pexit
B $04,"HALT"
W phalt
B $05,"PORTW"
W pportw
B $04,"PORT"
W pport
B $05,"FLUSH"
W pflush
B $08,"TRUNCATE"
W ptruncat
B $07,"EXECUTE"
W pexecute
B $05,"CHAIN"
W pchain
B $04,"INTR"
W pintr
B $05,"MSDOS"
W pmsdos
B $07,"CRTINIT"
W pcrtinit
B $07,"CRTEXIT"
W pcrtexit
B $06,"CLRSCR"
W pclrscr
B $06,"CLREOL"
W pclreol
B $09,"HIGHVIDEO"
W pnrmvid
B $09,"NORMVIDEO"
W pnrmvid
B $08,"LOWVIDEO"
W plowvid
B $07,"INSLINE"
W pinsline
B $07,"DELLINE"
W pdelline
B $05,"DELAY"
W pdelay
B $06,"WINDOW"
W pwindow
B $09,"TEXTCOLOR"
W ptextcol
B $0E,"TEXTBACKGROUND"
W ptextbg
B $0E,"GRAPHCOLORMODE"
W pgrcolmd
B $09,"GRAPHMODE"
W pgrmode
B $05,"HIRES"
W phires
B $08,"TEXTMODE"
W ptxtmode
B $0F,"GRAPHBACKGROUND"
W pgraphbg
B $07,"PALETTE"
W ppalette
B $0A,"HIRESCOLOR"
W phirscol
B $0B,"GRAPHWINDOW"
W pgrwind
B $04,"PLOT"
W pplot
B $04,"DRAW"
W pdraw
B $05,"SOUND"
W psound
B $07,"NOSOUND"
W pnosound
B $00 ; "
stdfuncsB $03,"CHR" ; Standard functions
W fchr ; vector to COMPILER routine
B $03,"ORD"
W ford
B $04,"COPY"
W fcopy
B $06,"LENGTH"
W flength
B $03,"POS"
W fpos
B $06,"CONCAT"
W fconcat
B $04,"SUCC"
W fsucc
B $04,"PRED"
W fpred
B $06,"UPCASE"
W fupcase
B $05,"TRUNC"
W ftrunc
B $05,"ROUND"
W fround
B $03,"ODD"
W fodd
B $03,"ABS"
W fabs
B $03,"SQR"
W fsqr
B $04,"SQRT"
W fsqrt
B $03,"SIN"
W fsin
B $03,"COS"
W fcos
B $06,"ARCTAN"
W farctan
B $02,"LN"
W fln
B $03,"EXP"
W fexp
B $06,"RANDOM"
W frandom
B $03,"INT"
W fint
B $04,"FRAC"
W ffrac
B $0A,"PARAMCOUNT"
W fparmcnt
B $08,"PARAMSTR"
W fparmstr
B $02,"LO"
W flo
B $02,"HI"
W fhi
B $04,"SWAP"
W fswap
B $08,"IORESULT"
W fiores
B $03,"EOF"
W feof
B $04,"EOLN"
W feoln
B $07,"SEEKEOF"
W fseekeof
B $08,"SEEKEOLN"
W fseekeol
B $08,"FILESIZE"
W ffilsize
B $0C,"LONGFILESIZE"
W flfilsiz
B $07,"FILEPOS"
W ffilpos
B $0B,"LONGFILEPOS"
W flfilpos
B $0A,"KEYPRESSED"
W fkeypres
B $08,"MAXAVAIL"
W fmaxavl
B $08,"MEMAVAIL"
W fmemavl
B $05,"PORTW"
W fportw
B $04,"PORT"
W fport
B $04,"ADDR"
W faddr
B $03,"PTR"
W fptr
B $03,"OFS"
W fofs
B $03,"SEG"
W fseg
B $06,"SIZEOF"
W fsizeof
tkdseg B $04,"DSEG"
W fdseg
tkcseg B $04,"CSEG"
W fcseg
B $04,"SSEG"
W fsseg
B $06,"WHEREX"
W fwherex
B $06,"WHEREY"
W fwherey ; "
; *** End of Turbo 3.0 ***
B $00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment