PGM MLIB86 REL ********************************************************** * * * (C) Copyright 1980 Network Development Group * * Rutherford Laboratory * * * ********************************************************** ********************************************************** * * * This is the assembly language library for TRIPOS on an * * INTEL 8086 system. * * * * Author Graham Adams April 1980 * * Modified to use separate code segment August 1980 * * Maximum code version August 1981 * * * ********************************************************** * system constants LIBWORD EQU 23456 marks library routines SECWORD EQU 12345 marks BCPL sections * * device bits and addresses * COKRDY EQU 2 console keyboard ready bit COPRDY EQU 1 console printer ready bit COKPCSW EQU #DE console status register COKPIOW EQU #DC console i/o register * * Coroutine stack symbols * * 0 link to next coroutine C.CLLR EQU 2 caller coroutine C.SEND EQU 4 stack end C.RESP EQU 6 resumption ptr C.FUNC EQU 8 function C.RTRN EQU 10 return link for STOP * * Root node items CRNTSK EQU #506 CBLKLIST EQU #528 * Global Vector symbols * G.GETCOB EQU 3*2 G.PUTCOB EQU 4*2 G.FETCHC EQU 5*2 G.STOREC EQU 6*2 G.GETCOD EQU 7*2 G.FRCOD EQU 9*2 G.RES2 EQU 10*2 RESULT2 for errors etc. G.RC EQU 11*2 RETURNCODE for STOP G.SBASE EQU 12*2 Current stack base * * Kernel Primitives * G.FVEC EQU 30*2 FREEVEC G.GVEC EQU 29*2 GETVEC * * Machine code library routines * G.APT EQU 20*2 APTOVEC G.CALCO EQU 25*2 CALLCO G.COWAIT EQU 26*2 COWAIT G.CRCO EQU 23*2 CREATECO G.DELCO EQU 24*2 DELETECO G.GBYTE EQU 15*2 GETBYTE G.LEVEL EQU 17*2 LEVEL G.LJUMP EQU 18*2 LONGJUMP G.MULDIV EQU 19*2 MULDIV G.PBYTE EQU 16*2 PUTBYTE G.RESCO EQU 27*2 RESUMECO G.SARDCH EQU 21*2 SARDCH G.SAWRCH EQU 22*2 SAWRCH G.STOP EQU 2*2 STOP * * Section header * MLIB DW MLBEND-MLIB/2 Section length in words DW SECWORD * DB 17,"MLIB 11-Aug-81" ********************************************************** * * * PUTBYTE(S,N,C) * * S,STRING: N,BYTE POSITION: C,BYTE * * * ********************************************************** DW LIBWORD DB 7,"putbyte" PB1 SHL BX string address ADD BX,AX char address MOV (BX),CL stuff it RETS ********************************************************** * * * LEVEL() RETURNS ACTIVATION LEVEL * * * ********************************************************** DW LIBWORD DB 7,"level " LEVEL MOV BX,BP RETS ********************************************************** * * * STOP(ARG) * * * * This function returns from the current coroutine or * * task, setting RETURNCODE and passing the argument ARG. * * * ********************************************************** DW LIBWORD DB 7,"stop " STOP ADD SP,!4 discard return address MOV G.RC(DI),BX set return code MOV BP,G.SBASE(DI) find stack base SHL BP CMP C.CLLR(BP),!0 find caller JS STP1 root stack := -1 JMP CRC2 return from coroutine STP1 JIS C.RTRN(BP) return to task deactivation ********************************************************** * * * LONGJUMP(P, L) JUMP TO DIFFERENT ACTIVATION LEVEL * * * ********************************************************** DW LIBWORD DB 7,"longjum" LONGJ MOV BP,BX ADD SP,!4 keep m/c stack correct MOV SI,AX ready for jump JIS (SI) ********************************************************** * * * APTOVEC(G, N) * * * ********************************************************** DB 7,"aptovec" APTOVEC ADD BP,DX std BCPL entry MOV 4(BP),DX POP (BP) POP 2(BP) MOV 6(BP),SI MOV 8(BP),BX MOV 10(BP),AX save args. MOV DX,AX n INC DX SHL DX 2*(N+1) LEA CX,12(BP) vector address SHR CX -> BCPL ADD DX,!14 MOV SI,BX entry point MOV BX,CX CIS (SI) JMP BRET ********************************************************** * * * MULDIV(A, B, C) * * Computes 32 bit result of A*B then divides it by C. * * Result of call is the quotient, remainder is placed * * in RESULT2. * * * ********************************************************** DW LIBWORD DB 7,"muldiv " MULDIV IMUL BX dx:ax := ax * bx IDIV CX ax := dx:ax/cx; dx := dx:ax REM cx MOV G.RES2(DI),DX MOV BX,AX RETS * * Outch and Inch are used by Sawrch and Sardch * OUTCH AND AL,!#7F CMP AL,!#A JNE ONLF MOV AL,!#D for *N do cr then lf CALL OUTCH MOV AL,!#A line feed CALL ONLF XOR AL,AL idle CALL ONLF CALL ONLF CALL ONLF MOV AL,!#A RET ONLF PUSH AX OUT1 IN #DE TEST AL,!1 JE OUT1 POP AX OUT #DC data RET * * INCH IN #DE TEST AL,!2 JE INCH IN #DC AND AL,!#7F CMP AL,!#D if cr then change to *N JNE INCR MOV AL,!#A INCR CALL OUTCH RET ********************************************************** * * * GETBYTE(S,N) * * S,STRING: N,BYTE POSITION * * * ********************************************************** DW LIBWORD DB 7,"getbyte" GB1 SHL BX string address ADD BX,AX char address MOV BL,(BX) get character XOR BH,BH RETS ********************************************************** * * * FETCHCODE(ADDRESS,SEGMENT) * * ADDRESS = MC ADDRESS OF WORD IN SEGMENT * * * ********************************************************** DW LIBWORD DB 7,"FETCHCO" FETCHC CLI protect ES MOV ES,AX MOV BX,(BX)[ES] STI RETS ********************************************************** * * * STORECODE(ADDRESS, SEGMENT, WORD) * * STORES WORD AT MC ADDRESS IN SEGMENT * * * ********************************************************** DW LIBWORD DB 7,"STORECO" STOREC CLI protect ES MOV ES,AX MOV (BX),CX[ES] STI RETS ********************************************************** * * * GETCODEBYTE(S,SEG,N) * * S,MC STRING IN SEGMENT,SEG = SEGMENT,N = BYTE POSITION * * * ********************************************************** DW LIBWORD DB 7,"GETCODE" GETCO ADD BX,CX no protection against overflowing seg CLI protect ES MOV ES,AX MOV BL,(BX)[ES] STI XOR BH,BH RETS ********************************************************** * * * PUTCODEBYTE(S, SEG, N, C) * * S=MC STRING, SEG=SEGMENT, N=BYTE POSITION IN STRING, * * C=BYTE VALUE * * * ********************************************************** DW LIBWORD DB 7,"PUTCODE" PUTCO ADD BP,DX std. BCPL entry MOV 4(BP),DX POP (BP) POP 2(BP) * ADD BX,CX MOV CL,14(BP) CLI MOV ES,AX MOV (BX),CL[ES] STI * JMP BRET ************************************************************ * * * GETCODEVEC(UPPERBOUND) * * * * This function is BCPL callable. * * It returns the mc address of a vector with at least * * the given upper bound. (In fact, the upperbound is * * rounded up to the next even number.) The words at offset * * -2 of the vector contain the length of the store block * * and should not be touched by the user. * * The vector is taken from the code area store chain * * Runs with interrupts disabled, but reenables and disables* * them each time round the search loop, so that they will * * not be locked out for the whole of a lengthy search * * If no global vector has been set up then DI must be zero.* * The ls. 16 bits of the address are returned in RESULT2. * * The normal result contains the segment register contents * * unless an error has occurred in which case the result is * * -1 (not 0 as for GETVEC). * * * ************************************************************ DW LIBWORD DB 7,"getcode" * GETCOD EQU $ std. entry sequence ADD BP,DX MOV 4(BP),DX POP (BP) POP 2(BP) * INC BX true vector size JG GG1 JMP GVC7 error GG1 INC BX one word of count OR BX,!1 make odd INC BX other word of count SHL BX size requested in bytes MOV 8(BP),BX save CLI disable interrupts * GVCRTY MOV SI,CRNTSK MOV GVTSK,SI record caller MOV AX,CBLKLIST ls. of mc address MOV BX,CBLKLIST+2 seg for mc address XOR DX,DX MOV CX,!12 get 12 bits from seg in DX GG1A SHR BX RCR DX LOOP GG1A ADD AX,DX now correct ls. 16 bits ADC BX,!0 and ms. 4 bits MOV 10(BP),BX AX and BX now contain 20 bit mc address ROR BX ROR BX ROR BX ROR BX MOV GVES,BX * GVC1 XCHG AX,SI XCHG 10(BP),BX GVC1A STI enable interrupts * * At this point interrupts are enabled. This is because the * search for a suitable free block may be long. If any * other task is run and calls GETCODEVEC then GVTSK will be * reset and the search must start again at the beginning of * the block list in case it has been updated meanwhile. CLI disable interrupts again MOV AX,GVTSK CMP AX,CRNTSK any other callers? JNE GVCRTY restart the search * MOV ES,GVES MOV CX,(SI)[ES] get 19-bit count MOV DX,2(SI)[ES] CMP DX,!7 bizarre? JLE GG2 JMP ERRSTOR GG2 OR CX,CX JNE G1B non-zero OR DX,DX JNE G1B JMP GVC7 count field is zero * G1B TEST CX,!1 free? JNE GVC2 yes SHL CX -> byte count RCL DX * Find next count. ADD SI,CX ADC BX,DX MOV CX,BX ROR CX ROR CX ROR CX ROR CX MOV GVES,CX set up ES JMP GVC1A * GVC2 MOV AX,SI save MC address of free block MOV 10(BP),BX GVC3 DEC CX count-1, ignore free bit SHL CX get '32 bit' count in bytes RCL DX ADD SI,CX move to next block ADC BX,DX MOV CX,BX construct ES ROR CX ROR CX ROR CX ROR CX MOV ES,CX MOV CX,(SI)[ES] MOV DX,2(SI)[ES] MOV 12(BP),!0 OR 12(BP),CX OR 12(BP),DX JE GVC4 CMP DX,!7 bizarre? JLE GG3 JMP ERRSTOR loop? GG3 TEST CX,!1 JNE GVC3 jump if block free * * Now 8(BP) = size request in bytes * AX,10(BP) ls and ms of start of free area * SI,BX ls and ms of end of free area * GVC4 EQU $ * Find size of free area. MOV CX,SI MOV DX,BX SUB CX,AX SBB DX,10(BP) * Swap pointers. XCHG AX,SI XCHG 10(BP),BX MOV ES,GVES * Amalgamate blocks. MOV (SI),CX[ES] MOV 2(SI),DX[ES] SHR 2(SI)[ES] RCR (SI)[ES] INC (SI)[ES] mark as free * Split block by subtracting request SUB CX,8(BP) SBB DX,!0 JNS GG4 JMP GVC1 can't be done GG4 MOV 12(BP),!0 OR 12(BP),CX OR 12(BP),DX JE GVC5 exact fit SUB AX,CX find upper part and calc. size SBB 10(BP),DX SHR DX -> BCPL RCR CX INC CX mark as free XCHG AX,BX MOV 12(BP),SI temp store SI MOV SI,10(BP) use to calc. segment ROR SI ROR SI ROR SI ROR SI MOV ES,SI MOV SI,12(BP) get back value MOV (BX),CX[ES] plant size MOV 2(BX),DX[ES] XCHG BX,AX * GVC5 MOV AX,8(BP) get request SHR AX -> BCPL MOV CX,BX ROR CX ROR CX ROR CX ROR CX MOV ES,CX MOV (SI),AX[ES] plant size MOV 2(SI),!0[ES] * Returns seg as result * and ls. part in RESULT2. MOV DX,SI MOV CX,!12 GVC5A SHL SI RCL BX LOOP GVC5A AND DX,!#F ADD DX,!4 skip count (and ensure offset >=4) MOV G.RES2(DI),DX STI enable interrupts JMP BRET * GVC7 OR DI,DI if no glob. vec then JE GVC8 can't set RESULT2 MOV G.RES2(DI),!103 insufficient free store GVC8 MOV BX,!-1 -1 means error return STI JMP BRET * ERRSTOR MOV CX,!197 free store corrupt INT 34 JMP GVCRTY **************************************************************** * * * FREECODEVEC(OFF, SEG) * * * * This BCPL callable routine frees the vector, which should * * have been obtained by GETCODEVEC. It aborts the task if an * * error is detected. * * * **************************************************************** DW LIBWORD DB 7,"freecod" FREECOD EQU $ FVEC CLI MOV ES,AX TEST -4(BX),!1[ES] free? (n.b. BX always >= 4) JNE FVC1 error INC -4(BX)[ES] mark free RETS FVC1 MOV CX,!199 INT 34 RETS ********************************************************** * * * SARDCH() * * Stand alone rdch routine * * * ********************************************************** DW LIBWORD DB 7,"sardch " RD1 CALL INCH XOR BH,BH MOV BL,AL RETS ********************************************************** * * * SAWRCH() * * Stand alone wrch routine * * * ********************************************************** DW LIBWORD DB 7,"sawrch " WR1 MOV AX,BX CALL OUTCH RETS ********************************************************** * * * CREATECO(FN, STSIZE) * * * * This function creates a coroutine with body FN, stack * * size STSIZE and returns a pointer to its stack base * * * ********************************************************** DB 7,"createc" CREATEC ADD BP,DX std BCPL entry MOV 4(BP),DX POP (BP) POP 2(BP) MOV 6(BP),SI MOV 8(BP),BX MOV 10(BP),AX save arguments MOV BX,AX MOV DX,!12 MOV SI,G.GVEC(DI) call getvec CIS (SI) * OR BX,BX any store? JNE CRCA1 JMP BRET no, return result 0 CRCA1 MOV SI,BX BCPL new stack in BX SHL SI MC new stack in SI MOV CX,10(BP) size in CX INC CX CRC1 MOV (SI),!0 INC SI INC SI LOOP CRC1 * MOV SI,G.SBASE(DI) get current corot. MOV DX,BX save new BCPL stack SHL BX MOV C.CLLR(BX),SI set caller SHL SI MC current MOV CX,(SI) MOV (BX),CX MOV (SI),DX link in new stack MOV C.RESP(SI),BP save resumption ptr MOV CX,8(BP) function MOV C.FUNC(BX),CX to new stack MOV CX,10(BP) size ADD CX,DX BCPL new stack end SUB CX,!50 MOV C.SEND(BX),CX MOV G.SBASE(DI),DX MOV BP,BX MOV BX,DX return correct result CRC2 MOV DX,!C.RTRN PUSH CS mimic CIS CALL COWAIT MOV DX,!C.RTRN MOV SI,C.FUNC(BP) CIS (SI) JMP CRC2 repeat ********************************************************** * * * DELETECO(CPTR) * * This routine deletes the coroutine represented by CPTR * * * ********************************************************** DB 7,"deletec" DELETEC ADD BP,DX std BCPL entry MOV 4(BP),DX POP (BP) POP 2(BP) MOV 6(BP),SI MOV 8(BP),BX save cptr * MOV SI,BX SHL SI MC coptr CMP C.CLLR(SI),!0 active? JNE COERR yes, error MOV SI,G.SBASE(DI) corot. chain * descend to root coroutine DC1 MOV DX,SI SHL SI MOV SI,C.CLLR(SI) OR SI,SI JG DC1 loop till root * locate corot. DX holds root the first time MOV SI,DX DC2 SHL SI MOV DX,SI MOV SI,(SI) OR SI,SI JE COERR end of chain CMP SI,BX found? JNE DC2 * now unlink SHL SI MOV CX,(SI) MOV SI,DX MOV (SI),CX * free the store MOV DX,!12 MOV SI,G.FVEC(DI) CIS (SI) B BRET * * error in coroutine call etc COERR MOV CX,!195 INT 34 abort the task * BCPL fn return BRET MOV SI,4(BP) SUB BP,SI JIS (BP,SI) ********************************************************** * * * CALLCO(CPTR, ARG) * * * * This function calls the coroutine represented by CPTR, * * passing the argument ARG. * * * ********************************************************** DB 7,"callco " CALLCO ADD BP,DX MOV 4(BP),DX POP (BP) POP 2(BP) MOV 6(BP),SI MOV DX,BX save new corot SHL BX MC new corot ptr MOV CX,BX save MC ptr CMP C.CLLR(BX),!0 already active? JNE COERR MOV SI,G.SBASE(DI) MOV C.CLLR(BX),SI activate new coroutine SHL SI MC old corot ptr COENT MOV G.SBASE(DI),DX MOV BX,AX arg in BX COSWP MOV C.RESP(SI),BP save resumption ptr MOV SI,CX MOV BP,C.RESP(SI) new stack B BRET ********************************************************** * * * RESUMECO(CPTR, ARG) * * * * This function deactivates the current coroutine and * * resumes the coroutine represented by CPTR, passing the * * argument ARG. * * * ********************************************************** DB 7,"resumec" RESUME ADD BP,DX MOV 4(BP),DX POP (BP) POP 2(BP) MOV 6(BP),SI MOV DX,BX SHL BX MC new corot ptr MOV CX,BX MOV SI,G.SBASE(DI) SHL SI MC old corot ptr CMP SI,BX new=old? JE COENT yes - bypass checks CMP C.CLLR(BX),!0 new already active? JNE COERR yes - error CMP C.CLLR(SI),!0 old=root? JS COERR yes - error PUSH C.CLLR(SI) POP C.CLLR(BX) activate new MOV C.CLLR(SI),!0 deactivate old B COENT ********************************************************** * * * COWAIT(ARG) * * * * This function returns from the current coroutine * * passing the argument ARG. * * * ********************************************************** DB 7,"cowait " COWAIT ADD BP,DX MOV 4(BP),DX POP (BP) POP 2(BP) MOV 6(BP),SI MOV SI,G.SBASE(DI) SHL SI MC old corot ptr MOV CX,C.CLLR(SI) get caller OR CX,CX JNS COW1 JMP COERR | -1 => root node COW1 MOV G.SBASE(DI),CX stackbase SHL CX MC new corot ptr MOV C.CLLR(SI),!0 deactivate old coroutine B COSWP * * * Globals to be initialized * * EVEN DW 0 DW G.GETCOB/2 DW GETCOI-MLIB DW G.PUTCOB/2 DW PUTCOI-MLIB DW G.FETCHC/2 DW FETCHCI-MLIB DW G.STOREC/2 DW STORECI-MLIB DW G.GETCOD/2 DW GETCODI-MLIB DW G.FRCOD/2 DW FREECODI-MLIB DW G.APT/2 DW APTOVECI-MLIB DW G.LEVEL/2 DW LEVELI-MLIB DW G.LJUMP/2 DW LONGJI-MLIB DW G.MULDIV/2 DW MULDIVI-MLIB DW G.GBYTE/2 DW GB1I-MLIB DW G.PBYTE/2 DW PB1I-MLIB DW G.SARDCH/2 DW RD1I-MLIB DW G.SAWRCH/2 DW WR1I-MLIB DW G.CRCO/2 DW CREATECI-MLIB DW G.DELCO/2 DW DELETECI-MLIB DW G.CALCO/2 DW CALLCOI-MLIB DW G.RESCO/2 DW RESUMEI-MLIB DW G.COWAIT/2 DW COWAITI-MLIB DW G.STOP/2 DW STOPI-MLIB DW 99 highest referenced global MLBEND EQU $ * * DSEG DMLIB DW DMLBEND-DMLIB/2 GVTSK DW 0 GVES DW 0 * GETCOI DW GETCO DW 0 PUTCOI DW PUTCO DW 0 FETCHCI DW FETCHC DW 0 STORECI DW STOREC DW 0 GETCODI DW GETCOD DW 0 FREECODI DW FREECOD DW 0 APTOVECI DW APTOVEC DW 0 LEVELI DW LEVEL DW 0 LONGJI DW LONGJ DW 0 MULDIVI DW MULDIV DW 0 GB1I DW GB1 DW 0 PB1I DW PB1 DW 0 RD1I DW RD1 DW 0 WR1I DW WR1 DW 0 CREATECI DW CREATEC DW 0 DELETECI DW DELETEC DW 0 CALLCOI DW CALLCO DW 0 RESUMEI DW RESUME DW 0 COWAITI DW COWAIT DW 0 STOPI DW STOP DW 0 DMLBEND END