; IFSResUtila.asm -- IFS resident utility stuff ; Copyright Xerox Corporation 1979, 1980, 1981 ; Last modified November 14, 1981 12:19 PM by Taft .ent TFSSwatProc .ent MulDiv .ent DoubleUsc .ent IfsPupChecksum .ent DoMove .ent Mul .ent Div .ent Div32x16 .ent LoadXM .ent StoreXM .ent DoubleBlt .ent CallWithArgVec .ent SysAllocate .ent SysFree .ent GetBit .ent SetBit .bext sysZone .srel TFSSwatProc: .TFSSwatProc MulDiv: .MulDiv DoubleUsc: .DoubleUsc IfsPupChecksum: .IfsPupChecksum DoMove: .DoMove Mul: .Mul Div: .Div Div32x16: .Div32x16 LoadXM: .LoadXM StoreXM: .StoreXM DoubleBlt: .DoubleBlt CallWithArgVec: .CallWithArgVec SysAllocate: .SysAllocate SysFree: .SysFree GetBit: .GetBit SetBit: .SetBit .nrel ; Procedure called by Swat before swapping us out (code = 0) ; and after swapping us back in (code = -1) .TFSSwatProc: snz 0 0 ; entering Swat? kwait: inc 0 0 snr jmp 1 3 lda 1 @.KBLK ; yes, wait for no Trident commands pending sz 1 1 jmp kwait inc 1 1 szr ; wait a while longer for things to settle jmp .-1 jmp 1 3 .KBLK: 640 ; MulDiv(a, b, c) ; returns the unsigned value (a*b)/c .MulDiv: sta 3 1 2 mov 2 3 mov 0 2 mkzero 0 0 mul lda 2 3 3 div #77400 mov 1 0 mov 3 2 jmp done ; DoubleUsc(lvA, lvB, nWords; numargs na) = -1|0|1 ; Returns: -1 if A < B ; 0 if A = B ; 1 if A > B ; lvA and lvB are the addresses of the nWords-word operands ; nWords defaults to 2. .DoubleUsc: sta 0 1 2 ; lvA sta 1 2 2 ; lvB lda 0 0 3 ; number of arguments lda 1 c2 sne 0 1 sta 1 3 2 ; default nWords to 2 uscLp: lda 0 @1 2 ; A word lda 1 @2 2 ; B word sleu 0 1 jmp gr ; A > B sub 1 0 szr jmp ls ; A < B isz 1 2 ; lvA isz 2 2 ; lvB dsz 3 2 ; decrement and test count jmp uscLp jmp 1 3 ; all words equal, return zero gr: mkone 0 0 skp ls: mkminusone 0 0 jmp 1 3 c2: 2 ; IfsPupChecksum(pup) ; Microcoded replacement for PupChecksum procedure in PupAl1a.asm .IfsPupChecksum: sta 3 1 2 mov 0 1 ; address to ac1 for microcode mov 0 3 lda 3 0 3 ; get pup length (bytes) neg 3 3 ; compute # words exclusive of checksum comzr 3 3 ; = (# bytes -1)/2 mkzero 0 0 ; init checksum #63000 ; call microcode jmp done ; DoMove -- tail of Dvec operation (see IfsDvec.bcpl for explanation) .DoMove: lda 3 3 2 add 2 3 lda 2 3 3 lda 3 4 3 blt inc 1 0 jmp done ; Mul(addend, multiplicand, multiplier, lvResult) ; Gives direct access to the "mul" instruction. ; Computes addend + (multiplicand*multiplier) and puts the ; 32-bit unsigned result in @lvResult. .Mul: sta 3 1 2 mov 2 3 ; put stack pointer here lda 2 3 3 ; get extra args word add 3 2 ; compute absolute pointer sta 2 3 3 ; put it back lda 2 3 2 ; get 3rd arg (multiplier) mul ; ac0 + (ac1*ac2) -> (ac0, ac1) mov 3 2 ; restore stack pointer lda 3 3 2 ; get extra args pointer lda 3 4 3 ; get lvResult sta 0 0 3 ; store result sta 1 1 3 jmp done ; Div(lvDividend, divisor, lvRemainder) = quotient ; Gives direct access to the "div" instruction. ; @lvDividend is the 32-bit unsigned dividend. ; Computes (@lvDividend)/divisor, puts the remainder in @lvRemainder, ; and returns the quotient. .Div: sta 3 1 2 sta 1 2 2 ; save divisor mov 0 3 ; lvDividend lda 0 0 3 ; Get dividend into ac0 and ac1 lda 1 1 3 mov 2 3 ; preserve stack pointer lda 2 2 3 ; get divisor div ; (ac0, ac1)/ac2, ac0←remainder, ac1←quotient #77400 mov 3 2 ; restore stack pointer sta 0 @3 2 ; @lvRemainder ← remainder mov 1 0 ; return quotient jmp done ; Div32x16(lvDividend, divisor) ; Does an unsigned division of the 32-bit number at @lvDividend by the ; 16-bit divisor, stores the quotient back in @lvDividend, and returns ; the remainder. .Div32x16: sta 3 1 2 mov 2 3 ; preserve stack pointer sta 0 2 3 ; save lvDividend mov 1 2 ; divisor mkzero 0 0 lda 1 @2 3 ; get high dividend div ; ac0←remainder, ac1←quotient #77400 sta 1 @2 3 ; store high quotient isz 2 3 lda 1 @2 3 ; get low dividend div ; ac0←remainder, ac1←quotient #77400 sta 1 @2 3 ; store low quotient mov 3 2 ; restore stack pointer done: lda 3 1 2 jmp 1 3 ; Extended memory support. ; LoadXM(bank, adr) ; Returns the value at adr in bank .LoadXM: #63420 ; XMLoad: AC0 ← (@AC1 in bank AC0) jmp 1 3 ; StoreXM(bank, adr, value) ; Stores value into adr in bank .StoreXM: sta 3 1 2 lda 3 3 2 #63421 ; XMStore: (@AC1 in bank AC0) ← AC3 jmp done ; DoubleBlt(dest, source, count, banks) ; Block-transfers double-words, possibly from one bank to another. ; dest = start of destination block. *** Must be even *** ; source = start of source block. *** Must be even *** ; count = number of words to transfer. *** Must be even *** ; banks = (destBank lshift 2) + sourceBank, where destBank and sourceBank ; are the destination and source bank numbers. ; The previous contents of the emulator bank register are saved and restored. ; *** Not reentrant *** .DoubleBlt: sta 3 1 2 lda 3 3 2 ; Frame offset of extra args add 2 3 ; Make absolute pointer sta 0 2 2 ; Save dest and source sta 1 3 2 lda 0 4 3 ; ac0← banks lda 1 .dblblt ; Construct DoubleBlt instruction add 0 1 sta 1 inst ; Put it down in front of us lda 0 2 2 ; Recover dest and source lda 1 3 2 lda 3 3 3 ; ac3← count movzr 3 3 ; Microcode wants count/2 inst: 0 ; The instruction jmp done .dblblt: #63400 ; DoubleBlt trap opcode ; CallWithArgVec(proc, lvArgs, numArgs) = result ; Calls proc with arguments lvArgs!0 through lvArgs!(numArgs-1), ; and returns the result returned by proc. proc = 4 lvArgs = 5 numArgs = 6 retInst = 7 .CallWithArgVec: sta 3 1 2 jsr @370 retInst+1 jsr @367 lda 3 lvArgs 2 lda 0 numArgs 2 lda 1 c3 se 0 1 ; exactly 3 args? adc 2 3 skp ; no, temp3 ← lvArgs-frame-1 lda 3 2 3 ; yes, temp3 ← 3rd arg sta 3 3 2 lda 3 lvArgs 2 ; build calling sequence in frame lda 0 cJsrProc sta 0 lvArgs 2 lda 0 cReturn sta 0 retInst 2 lda 0 0 3 ; first 2 args lda 1 1 3 jmp lvArgs 2 ; jump to calling sequence ; calling sequence in frame: ; jsr @proc 2 ; numArgs ; jsr @366 ; bcpl return cJsrProc: jsr @proc 2 cReturn: jsr @366 c3: 3 ; SysAllocate(nWords) = Allocate(sysZone, nWords) ; SysFree(block) = Free(sysZone, block) ; Only the 2-argument forms of Allocate and Free are supported. ; Note: this code depends on the fact that GetFrame always stores ; its first two arguments, regardless of the number of arguments ; specified in the call. .dmr ObjCall = 65400 .SysAllocate: mov 0 1 lda 0 @lvSysZone ObjCall 0 ; jmp ac0!0 = ZN.Allocate .SysFree: mov 0 1 lda 0 @lvSysZone ObjCall 1 ; jmp ac0!1 = ZN.Free lvSysZone: sysZone ; GetBit(lvBitArray, bitNumber) = value ; Reads a bit from a bit array. The base of the array is lvBitArray ; and the index is bitNumber. Returns the result as a BCPL boolean; ; i.e., a one is returned as -1 (BCPL true). .GetBit: sta 3 1 2 mov 0 3 ; lvBitArray movzr 1 0 ; bitNumber movzr 0 0 movzr 0 0 movzr 0 0 add 0 3 ; lvBitArray + bitNumber/16 lda 0 0 3 ; word from array cycle 0 ; left-cycle bitNumber rem 16 movl# 0 0 snc ; test result now in sign mkzero 0 0 skp ; false mkminusone 0 0 ; true jmp done ; SetBit(lvBitArray, bitNumber, value) ; Writes a bit into a bit array. The base of the array is lvBitArray ; and the index is bitNumber. The bit written is zero if value is ; zero, one if value is nonzero. .SetBit: sta 3 1 2 movzr 1 3 ; bitNumber movzr 3 3 movzr 3 3 movzr 3 3 add 3 0 ; lvBitArray + bitNumber/16 sta 0 2 2 neg 1 1 ; negate bitNumber for right shift adczr 0 0 ; 77777B cycle 0 ; left-cycle bitNumber rem 16 lda 1 @2 2 ; word from array and 0 1 ; turn off the bit lda 3 3 2 ; get value mov# 3 3 szr adc 0 1 ; nonzero, turn on the bit sta 1 @2 2 ; store back into array jmp done .end