; Runtimeml.asm
; L. Stewart, D. Swinehart, February 23, 1983 12:49 PM
; November 8, 1982 1:55 PM, make statics global
; November 13, 1982 5:23 PM, ReadTmr
; December 8, 1982 4:09 PM, delete stack limit enable
; December 21, 1982 12:52 PM, delete PUBLIC for wdc
; January 21, 1983 1:28 PM, restore PUBLIC for wdc
; February 23, 1983 10:31 AM, move wdc to *IntML, SSLimit
; April 30, 1983 10:58 AM, add more pro and epilogs
C←CODE SEGMENT
$INCLUDE(Lark.d)
C←DATA SEGMENT
←stackLimit DW 0
←remainder DW 0
C←DATA ENDS
ASSUME CS:C←CODE, DS:C←DATA, ES:C←DATA
; at the time a c procedure is run, BP holds the frame pointer
; [BP] is the previous frame pointer
; [BP+2] is the pc to return to
; The last procedure argument is passed in BX
; The second to the last argument, if there is one, is passed in CX
; Earlier arguments, if they exist, are PUSHed in order first to
; last before the call.
; The last pushed argument is in [BP+4]
; MoveBlock(dest, source, count)
; int *dest, *source, count;
; [BP+4], CX, BX
; Moves count words from source to dest
←MoveBlock PROC NEAR
PUSH BP
MOV BP,SP
←MoveBl:
MOV DI,[BP+4] ; get dest
MOV SI,CX ; get source
MOV CX,BX ; move count to CX
CLD
REP MOVSW
POP BP
RET
←MoveBlock ENDP
; Marshall(Swab, dest, source, count)
; bool Swab;
; int *dest, *source, count;
; Moves count words from source to dest; if Swab, swaps bytes;
←Marshall PROC NEAR
PUSH BP
MOV BP, SP
MOV AX,[BP+6] ; get Swab
OR AX, AX
JZ ←MoveBl ; no Swab, just a MoveBlock()
MOV DI,[BP+4] ; dest
MOV SI,CX ; source
MOV CX,BX ; count to loop counter
CLD
←mlp:
LODSW
XCHG AH, AL ; swap bytes
STOSW
LOOP ←mlp ; zowee.
POP BP
RET
←Marshall ENDP
; ByteBlt(dest, source, count)
; char *dest, *source;
; int count;
; [BP+4], CX, BX
; Moves count bytes from source to dest
←ByteBlt PROC NEAR
PUSH BP
MOV BP,SP
MOV DI,[BP+4] ; get dest
MOV SI,CX ; get source
MOV CX,BX ; move count to CX
CLD
REP MOVSB
POP BP
RET
←ByteBlt ENDP
; SetBlock(dest, length, value)
; int *dest, length, value;
; set length words at dest to value
←SetBlock PROC NEAR
PUSH BP
MOV BP,SP
MOV DI,[BP+4] ; get dest
; length already in CX
MOV AX,BX ; move value to AX
CLD
REP STOSW
POP BP
RET
←SetBlock ENDP
; Zero(dest, length)
; int *dest, length;
; set length words at dest to zero
←Zero PROC NEAR
PUSH BP
MOV BP,SP
MOV DI,CX ; get dest
MOV CX,BX ; get length to CX
XOR AX,AX ; move value to AX
CLD
REP STOSW
POP BP
RET
←Zero ENDP
; Move2(dest, source)
; int dest[2], source[2];
; move two words
←Move2 PROC NEAR
PUSH BP
MOV BP,SP
MOV DI,CX ; get dest
MOV SI,BX ; get source
CLD
MOVSW
MOVSW
POP BP
RET
←Move2 ENDP
; Min(x, y)
; int x, y;
; return smaller of two integers
←Min PROC NEAR
PUSH BP
MOV BP,SP
CMP BX,CX
JLE minok
MOV BX,CX
minok:
POP BP
RET
←Min ENDP
; Max(x, y)
; int x, y;
; return larger of two integers
←Max PROC NEAR
PUSH BP
MOV BP,SP
CMP BX,CX
JGE maxok
MOV BX,CX
maxok:
POP BP
RET
←Max ENDP
; UMax(x, y)
; unsigned x, y;
; return larger of two unsigned numbers
←UMax PROC NEAR
PUSH BP
MOV BP,SP
CMP BX,CX
JAE umaxok
MOV BX,CX
umaxok:
POP BP
RET
←UMax ENDP
; Usc(x, y)
; unsigned x, y;
; return -1 if a<b, 0 if a=b, 1 if a>b
←Usc PROC NEAR
PUSH BP
MOV BP,SP
CMP CX,BX
JE usceq
JA uscgr
MOV BX,-1
JMP uscret
uscgr:
MOV BX,1
JMP uscret
usceq:
XOR BX,BX
uscret:
POP BP
RET
←Usc ENDP
; int DoubleIncrement(px, y)
; int *px, y;
; add a 16 bit integer to a 32 bit integer
←DoubleIncrement PROC NEAR
PUSH BP
MOV BP,SP
MOV SI,CX ; SI ← px, BX has y
ADD [SI],BX
ADC [SI+2],0
MOV BX,[SI] ; return ls word
POP BP
RET
←DoubleIncrement ENDP
; int DoubleDifference(px, py)
; int *px, *py;
; subtract two 32 bit integers
←DoubleDifference PROC NEAR
PUSH BP
MOV BP,SP
MOV SI,CX ; SI ← px, BX has py
MOV AX,[BX] ; CX,,AX ← y
MOV CX,[BX+2]
SUB [SI],AX ; subtract to memory
SBB [SI+2],CX
MOV BX,[SI] ; return ls word
POP BP
RET
←DoubleDifference ENDP
; int DoubleInc(px, y)
; struct num *px
; int y;
; add a 16 bit integer to a 32 bit Swabbed integer
←DoubleInc PROC NEAR
PUSH BP
MOV BP,SP
MOV SI,CX ; SI ← px, BX has y
MOV AX,[SI] ; CX,,AX ← x
MOV CX,[SI+2]
XCHG AH,AL ; convert x to long int
XCHG CH,CL
ADD AX,BX
ADC CX,0
MOV BX,AX ; return the ls word
XCHG AH,AL ; convert x to long num
XCHG CH,CL
MOV [SI],AX ; x ← CX,,AX
MOV [SI+2],CX
POP BP
RET
←DoubleInc ENDP
; int DoubleDiff(px, py)
; struct num *px, *py
; subtract two 32 bit Swabbed integers
←DoubleDiff PROC NEAR
PUSH BP
MOV BP,SP
MOV SI,CX ; SI ← px
MOV DI,BX ; DI ← py
MOV AX,[SI] ; BX,,AX ← x
MOV BX,[SI+2]
XCHG AH,AL ; convert x to long int
XCHG BH,BL
MOV CX,[DI] ; DX,,CX ← y
MOV DX,[DI+2]
XCHG CH,CL ; convert y to long int
XCHG DH,DL
SUB AX,CX
SBB BX,DX
XCHG BH,BL
MOV [SI+2],BX
MOV BX,AX ; save ls word
XCHG AH,AL
MOV [SI],AX
POP BP
RET
←DoubleDiff ENDP
; DoubleEq(px, py)
; struct int px[2], py[2]
; compare two 32 bit quantities
←DoubleEq PROC NEAR
PUSH BP
MOV BP,SP
MOV SI,CX ; SI ← px
MOV AX,[SI]
CMP AX,[BX]
JNE deqfail
MOV AX,[SI+2]
CMP AX,[BX+2]
JNE deqfail
MOV BX,-1
POP BP
RET
deqfail:
XOR BX,BX
POP BP
RET
←DoubleEq ENDP
; MultEq(px, py, length)
; struct int *px, *py, length
; compare two length word quantities
←MultEq PROC NEAR
PUSH BP
MOV BP,SP
MOV SI,[BP+4] ; SI ← px
MOV DI,CX ; DI ← py
MOV CX,BX ; CX ← length
CLD
REPE CMPSW
JNE meqfail
MOV BX,-1
POP BP
RET
meqfail:
XOR BX,BX
POP BP
RET
←MultEq ENDP
; at the time a c procedure is run, BP holds the frame pointer
; [BP] is the previous frame pointer
; [BP+2] is the pc to return to
; The last procedure argument is passed in BX
; The second to the last argument, if there is one, is passed in CX
; Earlier arguments, if they exist, are PUSHed in order first to
; last before the call.
; We use BP as the "frame pointer"
; f = MyFrame();
; return the value of BP in BX
←MyFrame PROC NEAR
MOV BX,BP
RET
←MyFrame ENDP
; cf = CallersFrame(f)
; f can be the value returned by MyFrame or CallersFrame
←CallersFrame PROC NEAR
MOV BX,[BX]
RET
←CallersFrame ENDP
; ReturnFrom(frame);
; frame had better be on the stack!
; ReturnFrom(MyFrame()) is the same as a return
←ReturnFrom PROC NEAR
MOV SP,BX
POP BP
RET
←ReturnFrom ENDP
; pc = ReturnLoc(frame);
; aquire the return pc of frame frame
←ReturnLoc PROC NEAR
MOV BX,[BX+2]
RET
←ReturnLoc ENDP
; ReturnTo(frfr, tofr, topc, retval);
; return value retval to location topc in frame tofr, pretending you
; had been executing in frame frfr
; environment on entry:
; frfr
; tofr
; SP -> retadr to use if returning from call to ReturnTo
; CX = topc
; BX = retval
; on entry, SP points to a junk return address. after POPing
; the junk, SP will point to frfr.
; tofr is in CX on entry and topc is in BX
←ReturnTo PROC NEAR
POP BP ; pop garbage return address
POP BP ; set up tofr
POP AX ; get frfr
ADD AX,4 ; pop 'tofr' and 'topc'
MOV SP,AX ; restore SP to the
; value it had before the call
JMP CX ; resume execution
←ReturnTo ENDP
; suppose you want to save enough information to later pretend to return
; value xyz from a procedure called foo. foo will have to do some work:
;
; int frfr, tofr, topc; /* static ! */
;
; foo(foo's arguments) {
; frfr = MyFrame();
; tofr = CallersFrame(frfr);
; topc = ReturnLoc(frfr);
; whatever...
; };
;
; later:
; ReturnTo(frfr, tofr, topc, xyz)
;
; The overall problem is that while there is a known amount of space
; between a calling frame and a callee frame, one doesn't know how much
; of it is the caller's locals and how much is the callee's arguments
; Call0(proc)
; int *proc();
←Call0 PROC NEAR
OR BX,BX
JZ dontCall
JMP BX
dontCall:
RET
←Call0 ENDP
; Call1(proc, arg)
; int *proc(), arg;
←Call1 PROC NEAR
OR CX,CX
JZ dontCall
JMP CX
←Call1 ENDP
; Call2(proc, arg1, arg2)
; int *proc(), arg1, arg2;
←Call2 PROC NEAR
MOV SI,SP
MOV AX,[SI+4] ; get proc, don't disturb stack
OR AX,AX
JZ dontCall
JMP AX
←Call2 ENDP
; Apply(argv, proc, nargs)
; int argv[];
; int *proc();
; int nargs; length of argv
;
; BP -> base of callers sframe
; callers locals
; argv (A)
; SP -> callers return address
; CX = proc
; BX = nargs
; we want
; (A) argv[0]
; ...
; argv[n-3]
; SP -> callers return address
; CX = argv[n-2]
; BX = argv[n-1]
; BP undisturbed
←Apply PROC NEAR
PUSH BP
MOV BP,SP
CLD
MOV SI,[BP+4] ; get argv
MOV DI,CX ; save proc
MOV CX,BX ; put nargs in CX
OR CX,CX
JZ noargs
DEC CX
JZ onearg
DEC CX
JZ twoarg
apple:
LODSW ; get argv[i] into AX
PUSH AX
LOOP apple
twoarg:
LODSW ; get argv[n-2]
MOV CX,AX
onearg:
LODSW
MOV BX,AX
noargs:
CALL DI ; go to called procedure
MOV SP,BP
POP BP
RET
←Apply ENDP
; int Ugt(a, b) unsigned compare a and b
←Ugt PROC NEAR
PUSH BP
MOV BP,SP
CMP CX,BX
JA ugtt
XOR BX,BX
POP BP
RET
ugtt:
MOV BX,1
POP BP
RET
←Ugt ENDP
; int UDiv(a, b) unsigned divide a by b
←UDiv PROC NEAR
PUSH BP
MOV BP,SP
MOV AX,CX
XOR DX,DX
DIV BX
MOV ←remainder,DX
MOV BX,AX
POP BP
RET
←UDiv ENDP
; int DoubleUDiv(a, b)
; int a[2], b;
; divide (long) a by b
←DoubleUDiv PROC NEAR
PUSH BP
MOV BP,SP
MOV SI,CX
MOV AX,[SI]
MOV DX,[2+SI]
DIV BX
MOV ←remainder,DX
MOV BX,AX
POP BP
RET
←DoubleUDiv ENDP
; int URem(a, b) unsigned remainder of division a by b
←URem PROC NEAR
PUSH BP
MOV BP,SP
MOV AX,CX
XOR DX,DX
DIV BX
MOV ←remainder,DX
MOV BX,DX
POP BP
RET
←URem ENDP
; int GetRem() return remainder of recent operation
←GetRem PROC NEAR
MOV BX, ←remainder
RET
←GetRem ENDP
; call
; Fetch from address in BX and return in BX and AX
←FetchW PROC NEAR
MOV BX,[BX]
MOV AX,BX
RET
←FetchW ENDP
; Store CX to address in BX
←StoreW PROC NEAR
MOV [BX],CX
RET
←StoreW ENDP
; Fetch byte from address in BX and return in BL and AL
←FetchB PROC NEAR
MOV BL,[BX]
XOR BH,BH
MOV AX,BX
RET
←FetchB ENDP
; Store CL to address in BX
←StoreB PROC NEAR
MOV [BX],CL
RET
←StoreB ENDP
; output BL to port CX
←OutByte PROC NEAR
MOV DX,CX
MOV AL,BL
OUT DX,AL
RET
←OutByte ENDP
; input from port BX to BL and AL
←InByte PROC NEAR
MOV DX,BX
IN AL,DX
MOV BL,AL
RET
←InByte ENDP
;PortStr(p)
; char *p;
; {
; int port;
; int count;
; while(*p != 0x00ff) {
; port = *p++;
; count = *p++;
; while (count--) {
; OutByte(port, *p++);
; };
; };
; };
; enters with BX pointer to port string
←PortStr LABEL NEAR
PUSH BP
MOV BP,SP
XOR AH,AH
CLD
MOV SI,BX
X16:
LODSB
CMP AL,0FFH
JNE X17
POP BP
RET
X17:
MOV DX,AX
LODSB
MOV CX,AX
X18:
LODSB
OUT DX,AL
LOOP X18
JMP X16
; swap bytes in BX
←Swab PROC NEAR
XCHG BH,BL
RET
←Swab ENDP
; read timer, returns millisecond timer in BX
; call is ReadTmr()
←ReadTmr PROC NEAR
MOV SI,clklo
MOV BX,[SI]
RET
←ReadTmr ENDP
; set timer, address of timer in BX, interval in CX
; call is SetTmr(interval, &timer)
; interval is in 1000ths of a second
←SetTmr PROC NEAR
MOV SI,clklo
ADD CX,[SI]
MOV WORD PTR [BX],CX
RET
←SetTmr ENDP
; has timer expired? Address of timer in BX
; returns BX=1 if so, BX=0 if not
; call is if (TmrExp(&timer)) { ... }
←TmrExp PROC NEAR
MOV AX,WORD PTR [BX]
XOR BX,BX
MOV SI,clklo
CMP AX,[SI] ; expired will leave sign bit set
JS teret
RET
teret:
INC BX
RET
←TmrExp ENDP
; check stack pointer against ←stackLimit
StkChk PROC NEAR
CMP SP,←stackLimit
JBE stackov
RET
stackov:
PUSH BP
MOV BP,SP
MOV BX,08004H ; allocator class error
CALL ←CallDebugger
MOV SP,BP
POP BP
RET
StkChk ENDP
; Call debugger (as many args as you want)
←CallDebugger PROC NEAR
PUSH BP
MOV BP,SP
INT 5
POP BP
RET
←CallDebugger ENDP
; Causes a hardware boot by shutting down the watchdog timer
←Boot PROC NEAR
PUSH BP
MOV BP,SP
CLI
boota:
JMP boota
POP BP
RET
←Boot ENDP
; Set ←stackLimit
←SSLimit PROC NEAR
MOV ←stackLimit,BX
RET
←SSLimit ENDP
; called with pointer to Pup in BX
←CheckSum PROC NEAR
PUSH BP
MOV BP,SP
MOV AX,[BX] ; get length
XCHG AH,AL ; swab
INC AX ; round up
SAR AX, 1 ; words
DEC AX ; except checksum
XOR DX,DX
MOV SI,BX
MOV CX,AX
CLD
ckl:
LODSW
ADD DX,AX
ADC DX,0
ROL DX,1
LOOP ckl
MOV BX,DX
CMP BX,0FFFFH
JNE ckret
XOR BX,BX
ckret:
POP BP
RET
←CheckSum ENDP
PUBLIC ←stackLimit
PUBLIC ←MoveBlock
PUBLIC ←Marshall
PUBLIC ←ByteBlt
PUBLIC ←SetBlock
PUBLIC ←Zero
PUBLIC ←Move2
PUBLIC ←Min
PUBLIC ←Max
PUBLIC ←UMax
PUBLIC ←Usc
PUBLIC ←DoubleIncrement
PUBLIC ←DoubleDifference
PUBLIC ←DoubleInc
PUBLIC ←DoubleDiff
PUBLIC ←DoubleEq
PUBLIC ←MultEq
PUBLIC ←MyFrame
PUBLIC ←CallersFrame
PUBLIC ←ReturnFrom
PUBLIC ←ReturnLoc
PUBLIC ←ReturnTo
PUBLIC ←Call0
PUBLIC ←Call1
PUBLIC ←Call2
PUBLIC ←Apply
PUBLIC ←Ugt
PUBLIC ←UDiv
PUBLIC ←DoubleUDiv
PUBLIC ←URem
PUBLIC ←GetRem
PUBLIC ←FetchW
PUBLIC ←StoreW
PUBLIC ←FetchB
PUBLIC ←StoreB
PUBLIC ←OutByte
PUBLIC ←InByte
PUBLIC ←PortStr
PUBLIC ←Swab
PUBLIC ←ReadTmr
PUBLIC ←SetTmr
PUBLIC ←TmrExp
PUBLIC StkChk
PUBLIC ←CallDebugger
PUBLIC ←Boot
PUBLIC ←SSLimit
PUBLIC ←CheckSum
C←CODE ENDS
END