; 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