; 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