; C Runtime

; L. Stewart, April 7, 1982  5:32 PM

C←CODE SEGMENT

C←DATA	SEGMENT

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.

; 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)  calls proc with 0 arguments

←call0	PROC	NEAR
	JMP	BX		; resume execution
←call0	ENDP

; call1(proc, arg)  calls proc with 1 argument

←call1	PROC	NEAR
	JMP	CX		; resume execution
←call1	ENDP

; calln(argn-1, argn, arg1, arg2, ..., argn-2, proc, nargs)
; calls proc with arguments arg1, ..., argn

←calln	PROC	NEAR
	MOV	AX,CX		; proc
	SAL	BX,1		; word index
	ADD	BX,SP
	MOV	CX,[BX]
	MOV	BX,[BX-2]
	JMP	AX		; resume execution
←calln	ENDP

; int ugt(a, b)  unsigned compare a and b

←ugt	PROC	NEAR
	CMP	CX,BX
	JA	ugtt
	XOR	BX,BX
	RET
ugtt:
	MOV	BX,1
	RET
←ugt	ENDP

; int udiv(a, b)  unsigned divide a by b

←udiv	PROC	NEAR
	MOV	AX,CX
	XOR	DX,DX
	DIV	BX
	MOV	BX,AX
	RET
←udiv	ENDP

; int urem(a, b)  unsigned remainder of division a by b

←urem	PROC	NEAR
	MOV	AX,CX
	XOR	DX,DX
	DIV	BX
	MOV	BX,DX
	RET
←urem	ENDP

; call
;   blt(src, dest, count)
;    char *src, *dest;
;    int count;
; on entry, count is in BX, dest is in CX, src is on stack
; just above return address

; moves count bytes from src to dest

; assumes all segment registers are 0

←blt	PROC	NEAR
	PUSH	BP
	MOV	BP,SP
	MOV	SI,[BP+4]
	MOV	DI,CX
	MOV	CX,BX
	CLD
	REP MOVSB
	POP	BP
	RET
←blt	ENDP

; 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
   XOR   AH,AH
   CLD
   MOV   SI,BX
X16:
   LODSB
   CMP   AL,0FFH
   JNE   X17
   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

; SysHalt()
←SysHalt   PROC   NEAR
	HLT
	JMP	←SysHalt
	RET
←SysHalt   ENDP

PUBLIC  ←myframe
PUBLIC  ←callersframe
PUBLIC  ←returnfrom
PUBLIC	←returnloc
PUBLIC  ←returnto
PUBLIC  ←call0
PUBLIC	←call1
PUBLIC  ←calln
PUBLIC  ←ugt
PUBLIC	←udiv
PUBLIC  ←urem
PUBLIC  ←blt
PUBLIC  ←fetchw
PUBLIC  ←storew
PUBLIC  ←fetchb
PUBLIC  ←storeb
PUBLIC  ←outbyte
PUBLIC  ←inbyte
PUBLIC  ←portstr
PUBLIC  ←swab
PUBLIC  ←SysHalt

C←CODE ENDS
	END