:TITLE[Nova]; *Alto/Nova emulator *Last edited: 28 April 1980 by Fiala %Naming conventions (not universally followed): Labels are preceded by one of four prefixes: "ne," "ao," "xo," and "br." Opcode execution begins on nePage at the mi labeled "ne1st" or on one of several duplicates of this opcode. Memory reference opcodes use only nePage; A-group opcodes branch off to aoPage; opcodes in the extended instruction set branch off to xoPage; BCPL runtime opcodes are divided into three groups: JSR 300-337 are on brJsrPage, JSR @340-357 on br340Page, and JSR @360-377 on nePage. The mi at location 1 (BufferRefillTrap) is 'LoadPage[0], Goto[377]', which sends control to location 377 on the page that did the (aborted) NextInst/NextData. We would like the mi at 0 to be a PFetch4, but alas, DF2 addressing doesn't work, since H2 is not loaded in the cycle following an aborted mi (so the displacement won't be loaded). Buffer refill occurs every four non-jump opcodes and requires 16 cycles (4 through the PFetch4 + 12 after PFetch4) or 4.0 cycles/opcode; JMP and JSR independently refill the buffer, and the time for that is attributed to them. LDA@ and STA@ explicitly check for buffer refill during the dead time of the indirect fetch, so that IBuf will be loaded by the end of the opcode; STA also checks for refill prior to its PStore1 because MC1 is busy for 15 cycles afterwards and buffer refill would be very slow. % :IF[neBCPLI340]; xoRefill: PFetch4[PC,IBuf,4], GotoP[neRefx], At[xoBase,377]; :ENDIF; neRefill: PFetch4[PC,IBuf,4], GotoP[.+1], At[neBase,377]; *PC contains a multiple of 4, and the low two bits of the PC are in PCF. OnPage[0]; neRefx: PCF _ RZero; PC _ (PC) + (4C), Return; *Start Alto/Nova emulator. Initialize bases and constants, then start at PC. StartNova: Carry _ 0C; R177400 _ 177400C, Task; ROne _ 1C; RTwo _ 2C; * RThree _ 3C; *RThree coincident with rpAC2 rwpAC0 _ IP[AC0]C; rwpAC1 _ IP[AC1]C; rwpAC2 _ IP[AC2]C; rwpAC3 _ IP[AC3]C, Task; *rpACx contains a pointer to ACx-1 (i.e., (ACx & 360)+((ACx-1) & 17) *since StkP counts mod 20b. rpAC0 _ OR[AND[IP[AC0],360],AND[SUB[IP[AC0],1],17]]C; rpAC1 _ OR[AND[IP[AC1],360],AND[SUB[IP[AC1],1],17]]C; T _ MDShi, LoadPage[neFixBPage]; rpAC2 _ OR[AND[IP[AC2],360],AND[SUB[IP[AC2],1],17]]C, CallP[FixNBases]; T _ PC, LoadPage[nePage]; rpAC3 _ OR[AND[IP[AC3],360],AND[SUB[IP[AC3],1],17]]C, GotoP[brJmpPz]; OnPage[neFixBPage]; FixNBases: DMAhi _ T; PChi _ T; AC0hi _ T; AC1hi _ T; AC2hi _ T; AC3hi _ T, Return; %Interrupts are checked for only on opcodes that jump or compute indefinitely such as BLT/BLKS. We task (i.e., Return) every 41 cycles by requiring each opcode to Return within 41 cycles of starting and within 0 cycles of exiting, where JsrFin1+1 or one of its duplicates is the 1st mi. % OnPage[nePage]; neMemI: LU _ (Dispatch[PCF[IBuf],5,4]) or not T, Disp[.+1]; *Timing = 6 cycles to here *Main Instruction Dispatch for Memory Reference instructions PCF[IBuf] _ T _ PCF[IBuf] and T, FreezeResult, Disp[JmpJsr], At[OpTab,0]; *jmp PCF[IBuf] _ T _ PCF[IBuf] and T, Disp[JmpJsr], At[OpTab,1]; *jsr T _ PCF[IBuf] and T, Disp[IszDsz], At[OpTab,2]; *isz T _ PCF[IBuf] and T, FreezeResult, Disp[IszDsz], At[OpTab,3]; *dsz *rp's point to (reg-1) mod 20b because PFetch's do pushes. lu _ StkP _ rpAC0, Disp[LdaSta], At[OpTab,4]; *lda 0 lu _ StkP _ rpAC1, Disp[LdaSta], At[OpTab,5]; *lda 1 lu _ StkP _ rpAC2, Disp[LdaSta], At[OpTab,6]; *lda 2 lu _ StkP _ rpAC3, Disp[LdaSta], At[OpTab,7]; *lda 3 lu _ (StkP _ rwpAC0) or not T, Disp[LdaSta], At[OpTab,10]; *sta 0 lu _ (StkP _ rwpAC1) or not T, Disp[LdaSta], At[OpTab,11]; *sta 1 lu _ (StkP _ rwpAC2) or not T, Disp[LdaSta], At[OpTab,12]; *sta 2 lu _ (StkP _ rwpAC3) or not T, Disp[LdaSta], At[OpTab,13]; *sta 3 LoadPage[xoPage], Disp[Cycle], At[OpTab,14]; *60000-63777 T _ PCF[IBuf] or not T, Disp[JSRIIp], At[OpTab,15]; *64000-67777 T _ (PCF.word) + 1, Goto[IOUnIm], At[OpTab,16]; *70000-73777 T _ (PCF.word) + 1, Goto[IOUnIm], At[OpTab,17]; *74000-77777 *Save the current pc + 1 in memory location 527b and jump to location pointed *to by location 530b + inst[3,7] IOUnIm: PC _ (PC) + T; PCF[IBuf] _ (LdF[PCF[IBuf],3,5]) + 1; T _ (R400) or (127C), Goto[intXit]; *Dispatch for Jmp/Jsr instructions: *T contains PCF[IBuf] and 377. *Timing: JMP = 24, JSR = 33, JMP@ = 38, JSR@ = 40; *+2 if negative eff. addr; +2 if PC-relative addressing *Non-indirect Jmp/Jsr JmpJsr: PFetch4[MDS,IBuf], DblGoto[JmpFin,JsrFin,Alu<0], At[JmpJsrTab,0]; *page 0 :IF[neBCPL300]; LU _ PCF[IBuf] - (300C), DblGoto[brJmpPz,brJsrPz,Alu<0], At[JmpJsrTab,1]; *pg 0 :ELSE; PFetch4[MDS,IBuf], DblGoto[JmpFin,JsrFin,Alu<0], At[JmpJsrTab,1]; *pg 0 :ENDIF; T _ (PCF.word)+T, DblGoto[PCJmp1,PCJsr1,Alu<0], At[JmpJsrTab,2]; *pc T _ (R177400) or T, FreezeResult, Goto[.-1], At[JmpJsrTab,3]; *-pc PFetch4[AC2,IBuf], DblGoto[JmpFin,JsrFin,Alu<0], At[JmpJsrTab,4]; *ac2 T _ (R177400) or T, FreezeResult, Goto[.-1], At[JmpJsrTab,5]; *-ac2 PFetch4[AC3,IBuf], DblGoto[JmpFin,JsrFin,Alu<0], At[JmpJsrTab,6]; *ac3 T _ (R177400) or T, FreezeResult, Goto[.-1], At[JmpJsrTab,7]; *-ac3 *Indirect Jmp/Jsr PFetch1[MDS,RTemp], DblGoto[JmpIndFin,JsrIndFin,Alu<0], At[JmpJsrTab,10]; *@pg 0 :IF[neBCPLI340]; LU _ PCF[IBuf] - (340C), DblGoto[JmpPzInd,JsrPzInd,Alu<0], At[JmpJsrTab,11]; *@pg 0 :ELSEIF[neBCPLI360]; LU _ PCF[IBuf] - (360C), DblGoto[JmpPzInd,JsrPzInd,Alu<0], At[JmpJsrTab,11]; *@pg 0 :ELSE; PFetch1[MDS,RTemp], DblGoto[JmpIndFin,JsrIndFin,Alu<0], At[JmpJsrTab,11]; *@pg 0 :ENDIF; T _ PCF[RZero] + T, FreezeResult, Goto[PCIndJmpJsr], At[JmpJsrTab,12]; *pc T _ (R177400) or T, FreezeResult, Goto[.-1], At[JmpJsrTab,13]; *-pc PFetch1[AC2,RTemp], DblGoto[JmpIndFin,JsrIndFin,Alu<0], At[JmpJsrTab,14]; *ac2 T _ (R177400) or T, FreezeResult, Goto[.-1], At[JmpJsrTab,15]; *-ac2 PFetch1[AC3,RTemp], DblGoto[JmpIndFin,JsrIndFin,Alu<0], At[JmpJsrTab,16]; *ac3 T _ (R177400) or T, FreezeResult, Goto[.-1], At[JmpJsrTab,17]; *-ac3 PCtoAC3: T _ (PCF.word)+T+1; AC3 _ T, Return; *Entry from EIR, brBranch PCJmp1: PFetch4[PC,IBuf], Goto[JmpFin]; *Odd PCJsr1: PFetch4[PC,IBuf]; *Even JsrFin: MNBR _ PC, PC _ T _ T, NoRegILockOK; *Even; bypass kludge T _ MNBR, Call[PCtoAC3]; *PC_new PC*2, AC3_old PC+1 T _ PC; PC _ (PC) + T, Goto[JsrFin1]; PCIndJmpJsr: PFetch1[PC,RTemp], DblGoto[JmpIndFin,JsrIndFin,Alu<0]; JsrIndFin: T _ PC, Call[PCtoAC3]; *Even; entry from JSRII/JSRIS JmpIndFin: T _ RTemp; *Odd *Entry from StartNova, brReturn, brBranch brJmpPz: PFetch4[MDS,IBuf]; *Entry here on BitBlt interrupt or exit, LRJ continue. JmpFin: T _ PC _ T, Task, At[JmpFinLoc]; *Odd; bypass kludge PC _ (PC) + T; JsrFin1: LU _ NWW, LoadPage[neIntPage0], Call[JmpFin3]; *Return here to start next inst with PCF pointing at odd byte. *Many opcodes finish by returning (which accomplishes the tasking requirement) ne1st: CSkipData, T _ AllOnes; *T _ 377 ne2nd: Dispatch[PCF[IBuf],1,4], DblGoto[neRegI,neMemI,R<0]; neRet: Return; JmpPzInd: PFetch1[MDS,RTemp], Goto[JmpIndFin]; JsrPzIF: PFetch1[MDS,RTemp], Goto[JsrIndFin]; JmpFin3: PCF _ PC, PC _ T, NoRegILockOK, SkipP[Alu#0]; *Skip if possible int OnPage[neIntPage0]; PC _ (PC) and not (3C), Return; LU _ NWW, Skip[R>=0]; *Skip if interrupts enabled *BlksLp enters here with LU _ NWW, DblGoto[intDis,intEna,R<0]; intDis: PC _ (PC) and not (3C), Return; intEna: PC _ (PC) and not (3C), Skip[Alu#0]; *Skip if int requests *BLT/BLKS enter interrupts with DblGoto[intEnt,int0Ret,Alu#0] int0Ret: Return; *Begin an interrupt unless the interrupting device is inactive. *Worst case timing from here to Return is 27 cycles. intEnt: T _ (R400) + (52C); PFetch2[MDS,WW]; *Odd; fetch WW and ACTIVE DMA _ T, LoadPage[neIntPage1]; T _ NWW, GotoP[.+1]; OnPage[neIntPage1]; WW _ T _ (WW) or T; ACTIVE _ T _ (ACTIVE) and T; *ACTIVE now holds active int req's NWW _ T _ (Zero) - T, Skip[Alu#0]; intPS2: PStore1[DMA,WW,0], Return; *Return with NWW .eq. 0 *Start an interrupt T _ (ACTIVE) and T, Task; *Odd; ACTIVE & -ACTIVE = right-most 1 WW _ (WW) and not T; PCF[IBuf] _ 1C, Call[intPS2]; *RTemp1 will contain interrupt level NWW _ 100000C, Call[intT8]; *Disable interrupts *loop to get number of the highest priority interrupt intT8: ACTIVE _ Rsh[ACTIVE,1], Goto[intT9,R odd]; PCF[IBuf] _ PCF[IBuf] + 1, Return; *enter int routine - save other interrupts in WW (452) intT9: T _ PCF.word; *recover the PC PC _ (PC) or T, LoadPage[nePage]; T _ (R400) or (100C), GotoP[.+1]; OnPage[nePage]; *Also enter here from IOUnIm intXit: PStore1[MDS,PC], Call[neRet]; *save PC at 500b T _ PCF[IBuf] + T, Goto[JmpPzInd]; *T _ address of new PC *Dispatch for Isz/Dsz instructions: *These opcodes are executed so rarely that speed is of little importance. *T contains PCF[IBuf] and 377 *Non-indirect Isz/Dsz IszDsz: PFetch1[MDS,RTemp], DblGoto[Dsz1,Isz1,Alu<0], At[IszDszTab,0]; *pg 0 PFetch1[MDS,RTemp], DblGoto[Dsz1,Isz1,Alu<0], At[IszDszTab,1]; *pg 0 T _ PCF[RZero] + T, FreezeResult, Goto[PCDszIsz], At[IszDszTab,2]; *pc T _ (R177400) or T, FreezeResult, Goto[.-1], At[IszDszTab,3]; *-pc PFetch1[AC2,RTemp], DblGoto[Dsz1,Isz1,Alu<0], At[IszDszTab,4]; *ac2 T _ (R177400) or T, FreezeResult, Goto[.-1], At[IszDszTab,5]; *-ac2 PFetch1[AC3,RTemp], DblGoto[Dsz1,Isz1,Alu<0], At[IszDszTab,6]; *ac3 T _ (R177400) or T, FreezeResult, Goto[.-1], At[IszDszTab,7]; *-ac3 *Indirect Isz/Dsz PFetch1[MDS,RTemp], FreezeResult, Goto[IndDszIsz], At[IszDszTab,10]; PFetch1[MDS,RTemp], FreezeResult, Goto[IndDszIsz], At[IszDszTab,11]; T _ PCF[RZero] + T, FreezeResult, Goto[PCIndDszIsz], At[IszDszTab,12]; T _ (R177400) or T, FreezeResult, Goto[.-1], At[IszDszTab,13]; PFetch1[AC2,RTemp], FreezeResult, Goto[IndDszIsz], At[IszDszTab,14]; T _ (R177400) or T, FreezeResult, Goto[.-1], At[IszDszTab,15]; PFetch1[AC3,RTemp], FreezeResult, Goto[IndDszIsz], At[IszDszTab,16]; T _ (R177400) or T, FreezeResult, Goto[.-1], At[IszDszTab,17]; PCDszIsz: PFetch1[PC,RTemp], DblGoto[Dsz1,Isz1,Alu<0]; Isz1: T _ T, Task; *Even; save efadr with bypass kludge RTemp _ (RTemp) + (2C); Dsz1: T _ T, CSkipData, Call[neRet]; *Odd; save efadr with bypass kludge RTemp _ (RTemp) - 1; PStore1[MDS,RTemp], DblGoto[neTask1st,neTaskSkp,Alu#0]; PCIndDszIsz: PFetch1[PC,RTemp], FreezeResult; IndDszIsz: T _ RTemp, FreezeResult, Goto[IszDsz]; neTaskSkp: SkipData, Call[.+1]; *Even; may cause refill (fake call) neCS1st: CSkipData, Call[neRet]; *Skip even byte of opcode (no refill) CSkipData, T _ AllOnes, Goto[ne2nd]; *Opcodes that compute for too long to return to JmpFin+2 exit at *neTask1st to task immediately before starting the next opcode. neTask1st: Call[neRet]; *Odd CSkipData, T _ AllOnes, Goto[ne2nd]; *Average timing: LDA = 17, STA = 19.25, LDA@ = 30.25, STA@ = 30.75; *+ 2 if PC-relative *In addition, an A-group opcode referencing the AC of an immediately preceding *LDA/LDA@ will be slowed by 2 cycles; a LDA, LDA@, or STA@ following a *STA/STA@ will be slowed by 4 cycles (but slowed by 0 after buffer refill or *by only 2 if PC-relative); a JSR, JMP, JSR@, or JMP@ will be slowed by *4 (positive efadr) or 6 (negative efadr) cycles (but by only 0 or 2 cycles *if buffer refill occurred). **Maximum time to Return = 32 cycles on PC-rel STA@ barring error-correction. *Non-indirect Lda/Sta (CNextData's obtain odd byte of opcode = efadr) LdaSta: T _ CNextData[IBuf], DblGoto[PzSta,PzLda,Alu<0], At[LdaStaTab,0]; *pg 0 T _ CNextData[IBuf], DblGoto[PzSta,PzLda,Alu<0], At[LdaStaTab,1]; *pg 0 T _ PCF[RZero], FreezeResult, Goto[PCLdaSta], At[LdaStaTab,2]; *pc T _ PCF[RZero] or not T, FreezeResult, Goto[PCLdaSta], At[LdaStaTab,3]; *-pc T _ CNextData[IBuf], DblGoto[AC2Sta,AC2Lda,Alu<0], At[LdaStaTab,4]; *ac2 T _ CNextData[IBuf] or not T, DblGoto[AC2Sta,AC2Lda,Alu<0], At[LdaStaTab,5]; *-ac2 T _ CNextData[IBuf], DblGoto[AC3Sta,AC3Lda,Alu<0], At[LdaStaTab,6]; *ac3 T _ CNextData[IBuf] or not T, DblGoto[AC3Sta,AC3Lda,Alu<0], At[LdaStaTab,7]; *-ac3 *Indirect Lda/Sta T _ CNextData[IBuf], FreezeResult, Goto[PzLdaStaInd], At[LdaStaTab,10]; *@pg 0 T _ CNextData[IBuf], FreezeResult, Goto[PzLdaStaInd], At[LdaStaTab,11]; *@pg 0 T _ PCF[RZero], FreezeResult, Goto[PCLdaStaInd], At[LdaStaTab,12]; *@pc T _ PCF[RZero] or not T, FreezeResult, Goto[PCLdaStaInd], At[LdaStaTab,13]; *@-pc T _ CNextData[IBuf], FreezeResult, Goto[AC2LdaStaInd], At[LdaStaTab,14]; *@ac2 T _ CNextData[IBuf] or not T, FreezeResult, Goto[AC2LdaStaInd], At[LdaStaTab,15]; *@-ac2 T _ CNextData[IBuf], FreezeResult, Goto[AC3LdaStaInd], At[LdaStaTab,16]; *@ac3 T _ CNextData[IBuf] or not T, FreezeResult, Goto[AC3LdaStaInd], At[LdaStaTab,17]; *@-ac3 *The check for buffer refill costs 2 cycles 3/4 of the time, when refill *doesn't occur but saves 14 cycles 1/4 of the time when refill occurs, *for an average saving of 2 cycles/STA. StaIFF: PFetch4[PC,IBuf,4]; PCF _ RZero; PC _ (PC) + (4C); PzStaF: PStore1[MDS,Stack], Return; PCSta: T _ (PC) + T, DblGoto[StaIFF,PzStaF,BPCChk]; *Odd AC2Sta: T _ (AC2) + T, DblGoto[StaIFF,PzStaF,BPCChk]; *Odd AC3Sta: T _ (AC3) + T, DblGoto[StaIFF,PzStaF,BPCChk]; *Odd PzSta: DblGoto[StaIFF,PzStaF,BPCChk]; *Odd PCLdaSta: T _ CNextData[IBuf] + T, DblGoto[PCSta,PCLda,Alu<0]; *Refill check saves 3.5 cycles/LDA@ LdaIndFin: Goto[LdaIndF1,BPCChk']; *Even PFetch4[PC,IBuf,4]; *Odd PCF _ RZero; PC _ (PC) + (4C); LdaIndF1: T _ RTemp; *Even PzLda: PFetch1[MDS,Stack], Return; *Even PCLda: PFetch1[PC,Stack], Return; *Even AC2Lda: PFetch1[AC2,Stack], Return; *Even AC3Lda: PFetch1[AC3,Stack], Return; *Even *Refill check saves 5.75 cycles/STA@. StaIndFin: Goto[StaIndF1,BPCChk']; *Odd PFetch4[PC,IBuf,4]; *Odd PCF _ RZero; PC _ (PC) + (4C); StaIndF1: T _ RTemp, Goto[PzStaF]; *Even PCLdaStaInd: T _ CNextData[IBuf] + T, FreezeResult; PFetch1[PC,RTemp], DblGoto[StaIndFin,LdaIndFin,Alu<0]; PzLdaStaInd: PFetch1[MDS,RTemp], DblGoto[StaIndFin,LdaIndFin,Alu<0]; AC2LdaStaInd: PFetch1[AC2,RTemp], DblGoto[StaIndFin,LdaIndFin,Alu<0]; AC3LdaStaInd: PFetch1[AC3,RTemp], DblGoto[StaIndFin,LdaIndFin,Alu<0]; *Dispatch table for register-register instructions. 16-way Dispatch on *SrcAc,,DestAc is pending. *Average timing: 28 to 32 cycles on logical; 30 to 38 cycles on arithmetic. *Opcodes that skip require an additional 7.25 cycles. **Max time to Return is 41 cycles on opcodes that skip and refill buffer. neRegI: PCF[IBuf] _ Rcy[PCF[IBuf],3], Disp[.+1]; StkP _ rwpAC0, Goto[neSrc0], At[RegOpTab,0]; *SrcAC = 0, DestAC = 0 StkP _ rwpAC1, Goto[neSrc0], At[RegOpTab,1]; *SrcAC = 0, DestAC = 1 StkP _ rwpAC2, Goto[neSrc0], At[RegOpTab,2]; *SrcAC = 0, DestAC = 2 StkP _ rwpAC3, Goto[neSrc0], At[RegOpTab,3]; *SrcAC = 0, DestAC = 3 StkP _ rwpAC0, Goto[neSrc1], At[RegOpTab,4]; *SrcAC = 1, DestAC = 0 StkP _ rwpAC1, Goto[neSrc1], At[RegOpTab,5]; *SrcAC = 1, DestAC = 1 StkP _ rwpAC2, Goto[neSrc1], At[RegOpTab,6]; *SrcAC = 1, DestAC = 2 StkP _ rwpAC3, Goto[neSrc1], At[RegOpTab,7]; *SrcAC = 1, DestAC = 3 StkP _ rwpAC0, Goto[neSrc2], At[RegOpTab,10]; *SrcAC = 2, DestAC = 0 StkP _ rwpAC1, Goto[neSrc2], At[RegOpTab,11]; *SrcAC = 2, DestAC = 1 StkP _ rwpAC2, Goto[neSrc2], At[RegOpTab,12]; *SrcAC = 2, DestAC = 2 StkP _ rwpAC3, Goto[neSrc2], At[RegOpTab,13]; *SrcAC = 2, DestAC = 3 StkP _ rwpAC0, Goto[neSrc3], At[RegOpTab,14]; *SrcAC = 3, DestAC = 0 StkP _ rwpAC1, Goto[neSrc3], At[RegOpTab,15]; *SrcAC = 3, DestAC = 1 StkP _ rwpAC2, Goto[neSrc3], At[RegOpTab,16]; *SrcAC = 3, DestAC = 2 StkP _ rwpAC3, Goto[neSrc3], At[RegOpTab,17]; *SrcAC = 3, DestAC = 3 neSrc0: T _ AC0, LoadPage[aoPage], Goto[neFnD]; neSrc1: T _ AC1, LoadPage[aoPage], Goto[neFnD]; neSrc2: T _ AC2, LoadPage[aoPage], Goto[neFnD]; neSrc3: T _ AC3, LoadPage[aoPage], Goto[neFnD]; :IF[neFastAGroup]; neFnD: Dispatch[PCF[IBuf],10,3], GotoP[.+1]; *Function field OnPage[aoPage]; Dispatch[PCF[IBuf],13,4], Disp[aoFNC]; *Shift and carry fields %Put the function result in both T and the smashable temporary below the destination AC. Disp into either aoSHC (logical) or aoSHCc (arithmetic); arithmetic operations complement the incoming carry if the ALU carry-out is 1. % aoFNC: T _ Stack&-1 _ (Zero) xnor T, Disp[aoSHC], At[aoFunc,0]; *com T _ Stack&-1 _ (Zero) - T, Disp[aoSHCc], At[aoFunc,1]; *neg Stack&-1 _ T, Disp[aoSHC], At[aoFunc,2]; *mov T _ Stack&-1 _ (Zero) + T +1, Disp[aoSHCc], At[aoFunc,3]; *inc T _ Stack&-1 _ (Stack&-1) - T -1, Disp[aoSHCc], At[aoFunc,4]; *adc T _ Stack&-1 _ (Stack&-1) - T, Disp[aoSHCc], At[aoFunc,5]; *sub T _ Stack&-1 _ (Stack&-1) + T, Disp[aoSHCc], At[aoFunc,6]; *add T _ Stack&-1 _ (Stack&-1) and T, Disp[aoSHC], At[aoFunc,7]; *and *We use Carry = 177777 for carry-in = 1, Carry = 0 for carry-in = 0 aoSHC: LU _ Carry, DblGoto[aoNN,aoNZ,Alu#0], At[aoSH0,0]; *Nosh, Carry DblGoto[cZrN,cZrZ,Alu#0], At[aoSH0,1]; *Nosh, 0 DblGoto[cNrN,cNrZ,Alu#0], At[aoSH0,2]; *Nosh, 1 LU _ (Carry) xnor (0C), DblGoto[aoNN,aoNZ,Alu#0], At[aoSH0,3]; *Nosh, Carry' Carry, DblGoto[aoL1,aoL0,R<0], At[aoSH0,4]; *Lsh, Carry T _ Lsh[Stack,1], DblGoto[aoFC1,aoFC0,R<0], At[aoSH0,5]; *Lsh, 0 T _ (Lsh[Stack,1])+1, DblGoto[cNrNa,cZrNa,R<0], At[aoSH0,6]; *Lsh, 1 Carry, DblGoto[aoL1a,aoL0a,R>=0], At[aoSH0,7]; *Lsh, Carry' Carry, T _ 100000C, DblGoto[aoR1,aoR0,R<0], At[aoSH0,10]; *Rsh, Carry T _ Rsh[Stack,1], DblGoto[aoFC1,aoFC0,R Odd], At[aoSH0,11]; *Rsh, 0 T _ Stack _ Rcy[Stack,1], DblGoto[aoRO,aoRE,R Odd], At[aoSH0,12]; *Rsh, 1 Carry, T _ 100000C, DblGoto[aoR1a,aoR0a,R>=0], At[aoSH0,13]; *Rsh, Carry' LU _ Carry, DblGoto[aoSN,aoSZ,Alu#0], At[aoSH0,14]; *Swap, Carry T _ Rcy[Stack,10], DblGoto[cZrN,cZrZ,Alu#0], At[aoSH0,15]; *Swap, 0 T _ Rcy[Stack,10], DblGoto[cNrN,cNrZ,Alu#0], At[aoSH0,16]; *Swap, 1 LU _ (Carry) xnor (0C), DblGoto[aoSN,aoSZ,Alu#0], At[aoSH0,17]; *Swap, Carry' *Arithmetic ALU operations use this table. *The Rsh-Carry' and Swap-Carry' table entries are never executed by the *Executive, Bravo, or FTP. aoSHCc: LU _ (Carry)+1, UseCOutAsCIn, DblGoto[aoNN,aoNZ,Alu#0], At[aoSH1,0]; *Nosh, Carry LU _ (RZero)+1, UseCOutAsCIn, DblGoto[aoNN,aoNZ,Alu#0], At[aoSH1,1]; *Nosh, 0 LU _ (AllOnes)+1, UseCOutAsCIn, DblGoto[aoNN,aoNZ,Alu#0], At[aoSH1,2]; *Nosh, 1 T _ Carry, FreezeResult, Goto[aoNX], At[aoSH1,3]; *Nosh, Carry' T _ (Carry)+1, UseCOutAsCIn, DblGoto[aoLS,aoLA,R<0], At[aoSH1,4]; *Lsh, Carry T _ (Stack)+T, UseCOutAsCIn, DblGoto[aoFC1,aoFC0,R<0], At[aoSH1,5]; *Lsh, 0 T _ (AllOnes)+1, UseCOutAsCIn, Goto[aoLS], At[aoSH1,6]; *Lsh, 1 LU _ (Carry)+1, UseCOutAsCIn, Goto[aocL], At[aoSH1,7]; *Lsh, Carry' LU _ (Carry)+1, UseCOutAsCIn, Goto[aocR], At[aoSH1,10]; *Rsh, Carry T _ 100000C, DblGoto[aoR1,aoR0,Carry], At[aoSH1,11]; *Rsh, 0 T _ 100000C, DblGoto[aoR1a,aoR0a,Carry'], At[aoSH1,12]; *Rsh, 1 LU _ (Carry)+1, UseCOutAsCIn, Goto[aocRa], At[aoSH1,13]; *Rsh, Carry' LU _ (Carry)+1, UseCOutAsCIn, DblGoto[aoSN,aoSZ,Alu#0], At[aoSH1,14]; *Swap, Carry LU _ (RZero)+1, UseCOutAsCIn, DblGoto[aoSN,aoSZ,Alu#0], At[aoSH1,15]; *Swap, 0 LU _ (AllOnes)+1, UseCOutAsCIn, DblGoto[aoSN,aoSZ,Alu#0], At[aoSH1,16]; *Swap, 1 T _ Carry, FreezeResult, Goto[aoSX], At[aoSH1,17]; *Swap, Carry' aoNN: T _ Stack, DblGoto[cNrNa,cZrNa,Alu#0]; *Odd aoNZ: T _ Stack, DblGoto[cNrZa,cZrZa,Alu#0]; *Even aoSN: T _ Rcy[Stack,10], DblGoto[cNrNa,cZrNa,Alu#0]; *Odd aoSZ: T _ Rcy[Stack,10], DblGoto[cNrZa,cZrZa,Alu#0]; *Even aoL1: T _ (Lsh[Stack,1])+1, DblGoto[cNrNa,cZrNa,R<0]; aoL0: T _ Lsh[Stack,1], DblGoto[aoFC1,aoFC0,R<0]; aoL1a: T _ (Lsh[Stack,1])+1, DblGoto[cNrNa,cZrNa,R<0]; aoL0a: T _ Lsh[Stack,1], DblGoto[aoFC1,aoFC0,R<0]; aoFC1: DblGoto[cNrN,cNrZ,Alu#0]; *Odd aoFC0: DblGoto[cZrN,cZrZ,Alu#0]; *Even aoRO: Dispatch[PCF[IBuf],2,1], DblGoto[cNrNLd,cNrLd,R Odd]; aoRE: T _ (Stack) or (100000C), Goto[cZrN]; aoR1: T _ (Rsh[Stack,1]) or T, DblGoto[cNrNa,cZrNa,R Odd]; aoR0: T _ Rsh[Stack,1], DblGoto[aoFC1,aoFC0,R Odd]; aoR1a: T _ (Rsh[Stack,1]) or T, DblGoto[cNrNa,cZrNa,R Odd]; aoR0a: T _ Rsh[Stack,1], DblGoto[aoFC1,aoFC0,R Odd]; aoNX: LU _ (RZero)-T-1, UseCOutAsCIn, DblGoto[aoNN,aoNZ,Alu#0]; aoLS: T _ (Lsh[Stack,1]) - T, DblGoto[aoFC1,aoFC0,R<0]; aoLA: T _ (Lsh[Stack,1]) + T, DblGoto[aoFC1,aoFC0,R<0]; aocL: DblGoto[aoL1a,aoL0a,Alu=0]; aocR: T _ 100000C, DblGoto[aoR1,aoR0,Alu#0]; aocRa: T _ 100000C, DblGoto[aoR1a,aoR0a,Alu=0]; aoSX: LU _ (RZero)-T-1, UseCOutAsCIn, DblGoto[aoSN,aoSZ,Alu#0]; cNrZa: Dispatch[PCF[IBuf],0,3], DblGoto[cNrNLd,cNrLd,R Odd]; cZrZa: Dispatch[PCF[IBuf],0,3], DblGoto[cZrNLd,cZrLd,R Odd]; :ELSE; neFnD: Dispatch[PCF[IBuf],15,2], GotoP[.+1]; *Carry field OnPage[aoPage]; Dispatch[PCF[IBuf],10,3], Disp[aoCRY]; *Function field aoCRY: LU _ Carry, Disp[aoFNC], At[aoCY0,0]; *cin _ Carry LU _ 0C, Disp[aoFNC], At[aoCY0,1]; *cin _ 0 LU _ 100000C, Disp[aoFNC], At[aoCY0,2]; *cin _ 1 LU _ (Carry) xnor (0C), Disp[aoFNC], At[aoCY0,3]; *cin _ Carry' %Put the function result in both T and the smashable temporary below the destination AC. Disp into either neSHC (logical) or neSHCc (arithmetic); arithmetic operations complement the incoming carry if the ALU carry-out is 1. % aoFNC: T _ Stack&-1 _ (Zero) xnor T, DblGoto[aoNTC1,aoNTC0,Alu<0], At[aoFunc,0]; *com T _ Stack&-1 _ (Zero) - T, DblGoto[aoTC1,aoTC0,Alu<0], At[aoFunc,1]; *neg Stack&-1 _ T, DblGoto[aoNTC1,aoNTC0,Alu<0], At[aoFunc,2]; *mov T _ Stack&-1 _ (Zero) + T +1, DblGoto[aoTC1,aoTC0,Alu<0], At[aoFunc,3]; *inc T _ Stack&-1 _ (Stack&-1) - T -1, DblGoto[aoTC1,aoTC0,Alu<0], At[aoFunc,4]; *adc T _ Stack&-1 _ (Stack&-1) - T, DblGoto[aoTC1,aoTC0,Alu<0], At[aoFunc,5]; *sub T _ Stack&-1 _ (Stack&-1) + T, DblGoto[aoTC1,aoTC0,Alu<0], At[aoFunc,6]; *add T _ Stack&-1 _ (Stack&-1) and T, DblGoto[aoNTC1,aoNTC0,Alu<0], At[aoFunc,7]; *and *Alu carry out has no effect on carryin--dispatch on shift field aoNTC0: Dispatch[PCF[IBuf],13,2], Goto[aoCo0]; aoNTC1: Dispatch[PCF[IBuf],13,2], Goto[aoCo1]; *Alu carry complements carryin--dispatch on shift field aoTC0: Dispatch[PCF[IBuf],13,2], DblGoto[aoCo1,aoCo0,Carry]; *carryin = 0 aoTC1: Dispatch[PCF[IBuf],13,2], DblGoto[aoCo0x,aoCo1x,Carry]; *carryin = 1 aoCo0: LU _ Stack, Disp[aoSH00]; aoCo1: LU _ Stack, Disp[aoSH10]; aoCo0x: LU _ Stack, Disp[aoSH00]; aoCo1x: LU _ Stack, Disp[aoSH10]; *Shift dispatch for final carry = 0 aoSH00: DblGoto[cZrZ,cZrN,Alu=0], At[aoSH0,0]; T _ Stack _ Lsh[Stack,1], DblGoto[aoFC1,aoFC0,R<0], At[aoSH0,1]; T _ Stack _ Rsh[Stack,1], DblGoto[aoFC1,aoFC0,R Odd], At[aoSH0,2]; T _ Stack _ Lcy[Stack,10], DblGoto[cZrZ,cZrN,Alu=0], At[aoSH0,3]; aoSH10: DblGoto[cNrZ,cNrN,Alu=0], At[aoSH1,0]; T _ Stack _ (Lsh[Stack,1]) + 1, DblGoto[cNrNa,cZrNa,R<0], At[aoSH1,1]; T _ Stack _ Rcy[Stack,1], DblGoto[aoF1r1,aoF0r1,R Odd], At[aoSH1,2]; T _ Stack _ Lcy[Stack,10], DblGoto[cNrZ,cNrN,Alu=0], At[aoSH1,3]; aoFC0: DblGoto[cZrZ,cZrN,Alu=0]; aoFC1: DblGoto[cNrZ,cNrN,Alu=0]; aoF0r1: T _ (Stack) or (100000C), Goto[cZrN]; aoF1r1: Dispatch[PCF[IBuf],2,1], DblGoto[cNrNLd,cNrLd,R Odd]; :ENDIF; cZrNa: Dispatch[PCF[IBuf],1,2], DblGoto[cZrNLd,cZrLd,R Odd]; cNrNa: Dispatch[PCF[IBuf],2,1], DblGoto[cNrNLd,cNrLd,R Odd]; cZrZ: Dispatch[PCF[IBuf],0,3], DblGoto[cZrNLd,cZrLd,R Odd]; cZrN: Dispatch[PCF[IBuf],1,2], DblGoto[cZrNLd,cZrLd,R Odd]; cNrZ: Dispatch[PCF[IBuf],0,3], DblGoto[cNrNLd,cNrLd,R Odd]; cNrN: Dispatch[PCF[IBuf],2,1], DblGoto[cNrNLd,cNrLd,R Odd]; *Either table ok cZrNLd: Stack&-1, Disp[.+2]; *No load cZrLd: Carry _ 0C, Disp[.+1]; *Load Stack&+1 _ T, CSkipData, Return, At[aoXitT0,0]; *-- Stack&+1 _ T, CSkipData, Goto[aoSkip], At[aoXitT0,1]; *Skp Stack&+1 _ T, CSkipData, Goto[aoSkip], At[aoXitT0,2]; *szc Stack&+1 _ T, CSkipData, Return, At[aoXitT0,3]; *snc Stack&+1 _ T, CSkipData, Goto[aoSkip], At[aoXitT0,4]; *szr Stack&+1 _ T, CSkipData, Return, At[aoXitT0,5]; *snr Stack&+1 _ T, CSkipData, Goto[aoSkip], At[aoXitT0,6]; *sez Stack&+1 _ T, CSkipData, Return, At[aoXitT0,7]; *sbn cNrNLd: Stack&-1, Disp[.+2]; *No load cNrLd: Carry _ (Carry) or not (0C), Disp[.+1]; *Load Stack&+1 _ T, CSkipData, Return, At[aoXitT1,0]; Stack&+1 _ T, CSkipData, Goto[aoSkip], At[aoXitT1,1]; Stack&+1 _ T, CSkipData, Return, At[aoXitT1,2]; Stack&+1 _ T, CSkipData, Goto[aoSkip], At[aoXitT1,3]; Stack&+1 _ T, CSkipData, Goto[aoSkip], At[aoXitT1,4]; Stack&+1 _ T, CSkipData, Return, At[aoXitT1,5]; Stack&+1 _ T, CSkipData, Goto[aoSkip], At[aoXitT1,6]; Stack&+1 _ T, CSkipData, Return, At[aoXitT1,7]; %Doing refill this way rather averages .25 cycles/opcode faster than with: CSkipData, Task; CSkipData; T _ AllOnes, CSkipData, Goto[ne2nd]; and allows the A-group mi to be on a different page from ne2nd and the memory reference mi; also worst case time to Return is 9 cycles compared to 10 cycles for the trap. Skips would average 2 cycles/opcode faster if the CSkipData in the exit table could be moved earlier, so that the DblGoto at aoSkip could be incorporated in the exit mi. % aoSkip: Skip[BPCChk]; CSkipData, Goto[WRTRAM]; *Cannot cause refill PFetch4[PC,IBuf,4]; *Refill buffer and skip PCF _ RTwo; PC _ (PC) + (4C), Return; *Alto augmented instruction set *60000 CYCLE Left Rotate *61000 DIR Disable interrupts *61001 EIR Enable interrupts *61002 BRI Branch and Return from interrupt *61003 RCLK Read Clock *61004 SIO Start I/O *61005 BLT Block Transfer *61006 BLKS Block Store *61007 **SIT Start Interval Timer - Returns -1 *61010 JMPRAM Jump to RAM - only works if AC1 = 420 (enters Mesa) *61011 RDRAM Read RAM - Returns -1 *61012 WRTRAM Write RAM - NOP *61013 DIRS Disable interrupts and skip if on *61014 VERS Version - AC0 _ 40000C *61015 **DREAD Double-word read (altoII only) *61016 **DWRITE Double-word write (altoII only) *61017 **DEXCH Double-word exchange (altoII only) *61020 MUL Unsigned Multiply *61021 DIV Unsigned Divide *61022 WMDS (IME instruction replacing AltoII DIAGNOSE1) *61023 **DIAGNOSE2 Diagnostic (altoII only) *61024 BITBLT Bit Block Transfer *61036 LOADRAM implementation pending *61037 SDP SetDefaultPartition *64400 JSRII Jump to subroutine, double indirect, pc relative *65000 JSRIS Jump to subroutine, double indirect, ac2 relative *67000 CONVERT Scan conversion of characters *NOTE: instructions with ** are not implemented, and will bomb out if executed *Opcode 60000 - Cycle *Left cycle AC0 by inst[12-15d], or by AC1 if count is 0. Cycle: PCF[IBuf] and (17C), GotoP[.+1], At[IO0Tab,0]; *Test cycle count = 0 OnPage[xoPage]; PCF[IBuf] _ Lsh[PCF[IBuf],4], Goto[.+3,Alu#0]; T _ Lsh[AC1,4]; *cycle by AC1 if count=0 PCF[IBuf] _ T; PCF[IBuf] _ (PCF[IBuf]) or (17C); CycleControl _ CNextData[IBuf]; *DBX_cycle count, MWX_17b AC0 _ RF[AC0], Return; *Return to next opcode *Opcodes 61000 - 61377 xoDisp: Dispatch[PCF[IBuf],13,4], DblGotoP[NoParA,NoParB,R Even], At[IO0Tab,4]; OnPage[xoPage]; *StkP must point at AC1 for BitBlt NoParA: StkP _ rwpAC1, Disp[DIR]; *Timing 12 thru here NoParB: StkP _ rwpAC1, Disp[EIR]; *Timing 13 thru here *JSRII - 64400, 64600 *JSR double indirect, PC relative JSRIIp: T _ PCF[IBuf] and (377C), At[IO1Tab,2]; *EfAddr positive JSRIIn: T _ (PCF.word) + T, At[IO1Tab,3]; *EfAddr negative PFetch1[PC,RTemp], Call[neRet]; JSRIS: T _ RTemp, Goto[JsrPzIF]; *JSRIS - 65000, 65200 *JSR double indirect, AC2 relative JSRISp: T _ PCF[IBuf] and (377C), At[IO1Tab,4]; *EfAddr positive JSRISn: PFetch1[AC2,RTemp], Goto[JSRIS], At[IO1Tab,5]; *EfAddr negative %CONVERT - Opcode 67000, 67200 Registers: ac0: destination word address minus NWRDS ac2 + Disp points to two word block: word 0 NWRDS -- number of words per scanline (< 400c) word 1 dba -- minus dest bit addr mod 20c ac3: pointer to word xh of the character descriptor block Character Descriptor Block: words 0: xh-1 bit map for character word xh: xw -- (2*width) + 1, or (2*pseudochar) if extension required word xh+1: hd,,xh -- (scan lines to skip),,(height of bit map) % Convertp: T _ PCF[IBuf] and (377C), At[IO1Tab,14]; Convertn: PFetch1[AC2,RTemp1], At[IO1Tab,15]; *fetch NWRDS T _ (RZero) + T + 1, LoadPage[ConvertPage1]; PFetch1[MDS,AC1], GotoP[.+1]; OnPage[ConvertPage1]; PFetch1[AC3,RTemp,0]; *RTemp_self-relative ptr to xw T _ AC0, Task; DMA _ T, CSkipData; *setup for later, advance PC T _ (RTemp) - 1; *self-relative ptr to xw - 1 AC3 _ (AC3) + T, Task; *point AC3 to start of the block RTemp _ 16C; PFetch1[AC3,xnXH,2]; *fetch hd,,xh AC1 _ T _ (AC1) and (17C); *mask dba RTemp _ (RTemp) - T, Call[.+3]; *DMA _ DMA + (hd*xnNWRDS) *Point DMA to the first dest word xnXH _ (xnXH) + (177400C); DMA _ (DMA) + T, Skip[ALU<0]; cvRT1toT: T _ RTemp1, Return; xnXH _ T _ (LDF[xnXH,10,10]) - 1; *also decrement xh T _ (AC3) - T, LoadPage[ConvertPage2]; SMA _ T, GotoP[.+1]; OnPage[ConvertPage2]; xnCVLOOP: PFetch1[MDS,AC3], Task; *fetch source XBI _ pXBuf; xnXH _ (xnXH) - 1, Goto[xnCNVEND,R<0]; *test count PFetch4[DMA,xBuf,0]; *fetch dest T _ LdF[DMA,16,2]; *use StkP to index xBuf XBI _ (XBI) + T, LoadPage[ConvertPage3]; StkP _ XBI, CallP[neCVor]; CycleControl _ RTemp, Skip[R>=0]; Goto[xnCVX]; *odd; source exhausted T _ WFA[AC3]; *even; source contribution to 2nd dest word, done if 0 lu _ LdF[SStkP&NStkP,16,2], Goto[xnCVX,Alu=0]; Stack&+1, Goto[doSingleWord,Alu=0]; *Go if not still in quadword Stack _ (Stack) or T, Goto[xnCVX]; *odd; OR the second Dest word doSingleWord: PFetch1[DMA,XBI,1]; *even; fetch 2nd dest word XBI _ (XBI) or T, LoadPage[ConvertPage1]; *so XBI will be written PStore1[DMA,XBI,1], Call[cvRT1toT]; *UGH xnCVX: PStore4[DMA,xBuf,0]; *even; store buffer T _ RTemp1; *get NWRDS DMA _ (DMA) + T; SMA _ T _ (SMA) + 1, Goto[xnCVLOOP]; xnCNVEND: LU _ AC3, LoadPage[nePage], Goto[.+2,R Odd]; AC3 _ Rsh[AC3,1], GotoP[neTask1st]; AC3 _ Rsh[AC3,1], GotoP[neTaskSkp]; OnPage[ConvertPage3]; neCVor: CycleControl _ AC1; T _ RF[AC3]; *source contribution to first dest word Stack _ (Stack) or T, Return; *Extended Opcodes with no Displacement or parameter OnPage[xoPage]; DIR: NWW _ (NWW) or (100000C), Goto[WRTRAM], At[xoTab0,0]; *dir - 61000 EIR: T _ (R400) or (52C), Goto[EIRcom], At[xoTab1,0]; *eir - 61001 EIRcom: PFetch1[MDS,WW], Task; NWW _ (NWW) and not (100000C); T _ (WW) and not (100000C); NWW _ (NWW) or T, LoadPage[nePage]; PCF[IBuf], DblGotoP[EIRz,BRIy,R Odd]; OnPage[nePage]; *EIR must test for interrupts NOW. We simulate JMP .+1 EIRz: T _ (PCF.word) + 1, Goto[PCJmp1]; BRIy: T _ (R400) or (100C), Goto[JmpPzInd]; *fetch PC from location 500 BRI: T _ (R400) or (52C), Goto[EIRcom], At[xoTab0,1] ; *bri - 61002 RCLK: LoadPage[XMiscPage], At[xoTab1,1] ; *rclk - 61003 T _ (R400) or (30C), CallP[MXRClk]; AC1 _ T; T _ RTemp, Goto[RCLK1]; SIO: T _ AC0, LoadPage[EEPage], At[xoTab0,2]; *sio RTemp1 _ 0C, GotoP[EESIO]; *RTemp1=0 means Return to Nova %BLT and BLKS: AC0 address of first source word - 1 (BLT), or data to be stored (BLKS) AC1 address of last destination word (= destination+word count-1) AC2 unused AC3 negative word count % :IF[neFastBltBlks]; *Fast versions moving quadwords *Average timing for BLT's longer than 8 words ~= 100 + 7.25*nwords cycles, *assuming random alignment of source and destination and non-overlap case. BLT: T _ AC0, At[xoTab1,2]; T _ (AC3) - T, Task; *T _ - word count - source + 1 AC1 _ (AC1) + T; *AC1 _ destination-source (AC1) and not (3C); *Move the words one-at-a-time if destination address is 0 to 3 larger than *the source address; restore AC1. T _ AC1 _ (AC1) - T, LoadPage[neIntPage0], Skip[Alu#0]; AC3 _ (AC3) + (4C), GotoP[Blt1W]; *Even AC3 _ (AC3) + T, GotoP[.+1]; *AC3 _ destination-1 OnPage[neIntPage0]; *Dispatch on two low bits of destination-1 to transfer single words until *destination is quadaligned. Dispatch[AC3,16,2]; *Odd AC3 _ (AC3) - T, Disp[.+1]; *Restore AC3 AC0 _ T _ (AC0) + 1, Call[BltLp], At[BltTab,0]; *01 AC0 _ T _ (AC0) + 1, Call[BltLp], At[BltTab,1]; *10 AC0 _ T _ (AC0) + 1, Call[BltLp], At[BltTab,2]; *11 *Destination quadaligned; setup loop. LU _ AC3, Call[BltZT], At[BltTab,3]; *00 *Loop begins here with AC3 .ne. 0 *Check for .gr. 4 words to transfer (i.e., AC3 # -4, -3, -2, or -1) *Check for .ge. 4 is better but slows inner loop by 5 cycles. AC3 _ (AC3) + (4C); *Dispatch on low two bits of Source-1 to determine optimal fetch Dispatch[AC0,16,2], Goto[Blt1W,Carry]; T _ (AC1) - (3C), Disp[.+1]; *Even PFetch4[AC0,xBuf,1], Goto[Blt4S], At[BltFTab,3]; *00 *Smashes wBuf3 PFetch4[AC0,wBuf3,0], At[BltFTab,0]; *01 PFetch1[AC0,xBuf3,4], Goto[Blt4S]; PFetch2[AC0,xBuf,1], At[BltFTab,1]; *10 PFetch2[AC0,xBuf2,3], Goto[Blt4S]; *Smashes yBuf PFetch4[AC0,xBuf1,2], At[BltFTab,2]; *11 PFetch1[AC0,xBuf,1], Goto[Blt4S]; Blt4S: AC0 _ (AC0) + (4C), Goto[Blks4S]; Blt1W: AC3 _ (AC3) - (4C), Call[.+1]; AC0 _ T _ (AC0) + 1; BltLp: PFetch1[MDS,xBuf], Goto[BlksLp]; BltZT: Skip[Alu#0]; LoadPage[nePage], Goto[BltXit]; Return; *Average timing ~= 112 + 2.5*nwords cycles for nwords > 8 BLKS: T _ AC0, LoadPage[neBlksPage0], At[xoTab0,3]; xBuf _ T, GotoP[.+1]; OnPage[neBlksPage0]; *Remoted for page packing xBuf1 _ T; xBuf2 _ T; xBuf3 _ T, Task; T _ AC1; AC3 _ (AC3) + T, LoadPage[neIntPage0]; *AC3 _ destination-1 *Dispatch on two low bits of destination-1 to transfer single words until *destination is quadaligned. Dispatch[AC3,16,2], GotoP[.+1]; OnPage[neIntPage0]; AC3 _ (AC3) - T, Disp[.+1]; LU _ AC3, Call[BlksLx], At[BlksTab,0]; *01 LU _ AC3, Call[BlksLx], At[BlksTab,1]; *10 LU _ AC3, Call[BlksLx], At[BlksTab,2]; *11 *Quadaligned; setup loop--ensure word count .ne. 0 LU _ AC3, Call[BltZT], At[BlksTab,3]; *00 *Loop here while .gr. 4 words to do Blks4: AC3 _ (AC3) + (4C); T _ (AC1) - (3C), Goto[Blks1W,Carry]; Blks4S: LU _ NWW, Skip[R>=0]; *Even PStore4[AC3,xBuf], Return; PStore4[AC3,xBuf], DblGoto[intEnt,int0Ret,Alu#0]; *Store one word in xBuf, check for done, check for interrupts, and loop Blks1W: AC3 _ (AC3) - (4C), Call[BlksLx]; *Odd BlksLp: LU _ AC3; BlksLx: T _ AC3 _ (AC3) + 1, Skip[Alu#0]; AC3 _ (AC3) - 1, LoadPage[nePage], Goto[BltXit]; PStore1[AC1,xBuf]; LU _ NWW, DblGoto[intDis,intEna,R<0]; :ELSE; *Slower, smaller versions using single-word transfers *Average timing is about 32 + 19*nwords cycles. BLT: AC0 _ T _ (AC0) + 1, LoadPage[nePage], At[xoTab1,2]; Call[neRet]; *For tasking PFetch1[MDS,xBuf], Call[BlksLp]; AC0 _ T _ (AC0) + 1, Goto[.-1]; *Average timing ~= 30 + 17*nwords cycles BLKS: T _ AC0, LoadPage[nePage], At[xoTab0,3]; xBuf _ T, Call[neRet]; *For tasking BlksLp: LU _ AC3, LoadPage[neIntPage0]; T _ AC3 _ (AC3) + 1, SkipP[Alu#0]; OnPage[neIntPage0]; AC3 _ (AC3) - 1, LoadPage[nePage], Goto[BltXit]; PStore1[AC1,xBuf]; LU _ NWW, DblGoto[intDis,intEna,R<0]; :ENDIF; BltXit: CSkipData, GotoP[neTask1st]; JMPRAM: T _ 20C, At[xoTab0,4] ; *jmpram - 61010 LU _ (RHMask[AC1]) xor T; T _ AC0, Skip[Alu=0] ; BreakPoint, Return; *Should never get here LoadPage[7]; GotoP[MStart]; RDRAM: AC0 _ (Zero) - 1, At[xoTab1,4]; *rdram - 61011 WRTRAM: CSkipData, Return, At[xoTab0,5]; *wrtram - 61012 (nop) DIRS: CSkipData, At[xoTab1,5]; LoadPage[nePage]; NWW _ (NWW) or (100000C), DblGoto[neTask1st,neTaskSkp,R<0]; VERS: AC0 _ (40000c), Goto[WRTRAM], At[xoTab0,6] ; *vers - 61014 *AC0,,AC1 _ (AC1 * AC2) + (AC0) *Steps: *1 test loop count for done *2 test next mpr bit (AC1[17]), Rsh AC1 *2a if 1: AC0 _ AC0 + T *3a Rsh AC0, or'ing 100000 into AC0 if add carried *2b if 0: Rsh AC0 *4a,3b or 100000 into AC1 if shifted a 1 out of AC0 xoMul: T _ AC2, LoadPage[neMulPage], At[xoTab0,10]; RTemp _ 16C, GotoP[.+1]; *setup loop OnPage[neMulPage]; Call[xoMul1]; RTemp _ (RTemp) - 1, Goto[.+3,R>=0]; LoadPage[nePage]; CSkipData, GotoP[neTask1st]; *Loop time: 8 to 9 cycles on multiplier zeroes, 13 to 15 on multiplier ones xoMul1: AC1 _ Rsh[AC1,1], Skip[R Odd]; AC0 _ Rsh[AC0,1], DblGoto[xoMula,xoMulb,R Odd]; AC0 _ (AC0) + T; AC0 _ Rcy[AC0,1], Skip[Carry]; AC0 _ (AC0) and not (100000C), DblGoto[xoMula,xoMulb,Alu<0]; AC0 _ (AC0) or (100000C), DblGoto[xoMula,xoMulb,Alu<0]; xoMula: AC1 _ (AC1) or (100000C), Return; xoMulb: Return; *AC0,,AC1/AC2. Quotient in AC1, remainder in AC0 xoDiv: LoadPage[neDivPage], T _ AC2, At[xoTab1,10]; LU _ (AC0) - T, GotoP[.+1]; OnPage[neDivPage]; RTemp _ 16C, Goto[.+3,Carry']; RTemp _ (RTemp) + 1, LoadPage[nePage]; *RTemp odd--no-skip xoDivX: RTemp, CSkipData, DblGoto[neTask1st,neTaskSkp,R Odd]; T _ 31C; SAluf _ T; *SAluf _ A+A+1 T _ (AC2) + (0C); *T _ divisor, carry _ 0 AC1 _ (AC1) SAlufOp T, UseCOutAsCIn, Call[xoDivE]; *Loop time: 13 to 15 cycles/bit LU _ RTemp1; AC0 _ (AC0) - T, Skip[Alu>=0]; AC1 _ (AC1) SAlufOp T, Goto[xoDivL]; *No carry test--quot _ 1 AC1 _ (AC1) SAlufOp T, UseCOutAsCIn, Skip[Carry]; *Subtract ok? AC0 _ (AC0) + T, FreezeResult; *No--undo xoDivL: RTemp _ (RTemp) - 1, FreezeResult, Skip[R>=0]; LoadPage[nePage], Goto[xoDivX]; *RTemp even--skip xoDivE: AC0 _ (AC0) SAlufOp T, UseCOutAsCIn, Skip[R<0]; RTemp1 _ Zero, Return; RTemp1 _ (RTemp1) or not (0C), Return; *AC0,,AC1/AC2. Quotient in AC1, remainder in AC0 %DIV: LoadPage[MulDivPage], T _ AC2, At[xoTab1,10]; CallP[Divide], LU _ (AC0) - T; *div LoadPage[nePage]; LU _ neCNT, CSkipData, DblGoto[neTask1st,neTaskSkp,R Odd]; OnPage[MulDivPage]; Divide: DblGoto[noDiv,doDiv,carry], neCNT _ 17C; noDiv: Return; *neCNT odd means no skip doDiv: UseCTask; T _ APCTask&APC; RTemp1 _ T; *save link T _ AC2, Call[UDIVenter]; lu _ Rcy[RTemp,1]; *loop Returns to here AC0 _ (AC0) - T, DblGoto[UDIV1t,UDIV1f,ALU<0]; *test flag UDIV1t: AC1 _ (Lsh[AC1,1]) + 1, DblGoto[UDIV3t,UDIV3f,R<0]; *no need to test carry, quot _ 1 UDIV1f: DblGoto[UDIV2t,UDIV2f,carry]; *subtract ok, test carry UDIV2t: AC1 _ (Lsh[AC1,1]) + 1, DblGoto[UDIV3t,UDIV3f,R<0]; *put a one in the quotient UDIV2f: AC0 _ (AC0) + T; *no carry - undo subtraction UDIVenter: AC1 _ (Lsh[AC1,1]), DblGoto[UDIV3t,UDIV3f,R<0]; *put a Zero in the quotient UDIV3t: neCNT _ (MNBR _ neCNT) - 1, Skip[R>=0]; APC&APCTask _ RTemp1, Goto[UDIV4f]; AC0 _ (Lsh[AC0,1]) + 1, DblGoto[UDIV4t,UDIV4f,R<0]; UDIV3f: neCNT _ (MNBR _ neCNT) - 1, Skip[R>=0]; APC&APCTask _ RTemp1, Goto[UDIV4f]; AC0 _ (Lsh[AC0,1]), DblGoto[UDIV4t,UDIV4f,R<0]; UDIV4t: RTemp _ (RTemp) or (1C), Return; *set flag UDIV4f: RTemp _ (RTemp) and not (1C), Return; *clear flag % AWMDS: T _ AC0, LoadPage[neFixBPage], At[xoTab0,11]; T _ (Lsh[AC0,10]) or T, CallP[FixNBases]; LoadPage[XMiscPage], MDShi _ T; CallP[SetIOBases]; *In MesaX LoadPage[nePage]; xoExit: CSkipData, GotoP[neTask1st]; *Enter bbBitBLT with StkP pointing at AC1 (icom), pointer to BitBlt table in *AC2, and (Cycle&PCXF) and not (100000C) in T. ABITBLT: LoadPage[bbPage], At[xoTab0,12]; T _ (Cycle&PCXF) and not (100000C), GotoP[bbBitBLT]; xoLRJ: T _ AC0, LoadPage[neLRJPage], At[xoTab0,17]; LP _ T, GotoP[.+1]; OnPage[neLRJPage]; *AC1 odd: turn off tasking, do inline storage refresh; load control store, * jump to starting address in the end item of the RAM image. *AC1 even: normal tasking; ignore starting address, continue with next item. T _ (AC1) or (100000C); T _ (Rsh[AllOnes,17]) xor T; RTemp1 _ T; *RTemp1<0 = call from Alto emulator, odd = resume *at JmpFin, even = jump to starting address T _ (Rsh[AllOnes,17]) and T; xfTemp1 _ T; RTemp _ FFaultAdd; StkP _ RTemp, Task; Stack _ (Stack) and not (1C); T _ MDShi, LoadPageExternal[LRJPage]; LPhi _ T, GotoExternal[LRJStart]; SDP: LoadPage[XMiscPage], At[xoTab1,17]; T _ AC0, CallP[MXPar]; *In MesaX RCLK1: AC0 _ T, LoadPage[nePage], Goto[xoExit]; :IF[neBCPL300]; OnPage[nePage]; *Arrive at brJsrPz on a JSR 200 to 377 with Efadr - 300 through the ALU. brJsrPz: LU _ PCF[IBuf] - (333C), Skip[Alu>=0]; *Even PFetch4[MDS,IBuf], Goto[JsrFin]; *.ls. 300 LoadPage[brJsrPage], Skip[Alu<0]; LoadPage[nePage], GotoP[brJsrExit]; *.ge. 333 Dispatch[PCF[IBuf],13,4], DblGotoP[br300Odd,br300Even,R Odd]; OnPage[brJsrPage]; brJsrExit: PFetch4[MDS,IBuf], GotoP[JsrFin]; brJsrSNQ0: LoadPage[nePage]; PFetch1[AC1,RTemp,0], GotoP[brSNQ0jsr]; brJsrSNQ1: LoadPage[nePage]; PFetch1[AC0,RTemp,0], GotoP[brSNQ1jsr]; br300Odd: CSkipData, Disp[.+1]; AC1 _ Rsh[AC1,6], Return, At[Bcpl300Odd,0]; *301 AC1 _ Rsh[AC1,5], Return, At[Bcpl300Odd,1]; *303 AC1 _ Rsh[AC1,4], Return, At[Bcpl300Odd,2]; *305 AC1 _ Rsh[AC1,3], Return, At[Bcpl300Odd,3]; *307 AC1 _ Rsh[AC1,2], Return, At[Bcpl300Odd,4]; *311 AC1 _ Rsh[AC1,1], Return, At[Bcpl300Odd,5]; *313 T _ AC0 _ Lsh[AC0,7], Goto[brJsrSNQ0], At[Bcpl300Odd,6]; *315 T _ AC0 _ Lsh[AC0,6], Goto[brJsrSNQ0], At[Bcpl300Odd,7]; *317 T _ AC0 _ Lsh[AC0,5], Goto[brJsrSNQ0], At[Bcpl300Odd,10]; *321 T _ AC0 _ Lsh[AC0,4], Goto[brJsrSNQ0], At[Bcpl300Odd,11]; *323 T _ AC0 _ Lsh[AC0,3], Goto[brJsrSNQ0], At[Bcpl300Odd,12]; *325 T _ AC0 _ Lsh[AC0,2], Goto[brJsrSNQ0], At[Bcpl300Odd,13]; *327 T _ AC0 _ Lsh[AC0,1], Goto[brJsrSNQ0], At[Bcpl300Odd,14]; *331 br300Even: CSkipData, Disp[.+1]; AC0 _ Rsh[AC0,6], Return, At[Bcpl300Even,0]; *300 AC0 _ Rsh[AC0,5], Return, At[Bcpl300Even,1]; *302 AC0 _ Rsh[AC0,4], Return, At[Bcpl300Even,2]; *304 AC0 _ Rsh[AC0,3], Return, At[Bcpl300Even,3]; *306 AC0 _ Rsh[AC0,2], Return, At[Bcpl300Even,4]; *310 AC0 _ Rsh[AC0,1], Return, At[Bcpl300Even,5]; *312 Return, At[Bcpl300Even,6]; *314 (nop) T _ AC1 _ Lsh[AC1,7], Goto[brJsrSNQ1], At[Bcpl300Even,7]; *316 T _ AC1 _ Lsh[AC1,6], Goto[brJsrSNQ1], At[Bcpl300Even,10]; *320 T _ AC1 _ Lsh[AC1,5], Goto[brJsrSNQ1], At[Bcpl300Even,11]; *322 T _ AC1 _ Lsh[AC1,4], Goto[brJsrSNQ1], At[Bcpl300Even,12]; *324 T _ AC1 _ Lsh[AC1,3], Goto[brJsrSNQ1], At[Bcpl300Even,13]; *326 T _ AC1 _ Lsh[AC1,2], Goto[brJsrSNQ1], At[Bcpl300Even,14]; *330 T _ AC1 _ Lsh[AC1,1], Goto[brJsrSNQ1], At[Bcpl300Even,15]; *332 :ENDIF; :IF[neBCPLI340]; OnPage[nePage]; *BCPL Runtime: JSR @ 340-357 br340Disp: Dispatch[PCF[IBuf],14,4], GotoP[.+1]; OnPage[br340Page]; T _ AC1, Disp[.+1]; AC0 _ (AC0) or T, Goto[WRTRAM], At[neBR340,0]; *AC0_AC0 or AC1 AC0 _ (AC0) xor T, Goto[WRTRAM], At[neBR340,1]; *AC0_AC0 xor AC1 AC0 _ (AC0) xnor T, Goto[WRTRAM], At[neBR340,2]; *AC0_AC0 xnor AC1 T _ PCF[IBuf], LoadPage[nePage], Goto[br340NI], At[neBR340,3]; *Mult T _ PCF[IBuf], LoadPage[nePage], Goto[br340NI], At[neBR340,4]; *DivRem T _ PCF[IBuf], LoadPage[nePage], Goto[br340NI], At[neBR340,5]; *DivRem T _ (AC1) - (20C), DblGoto[brLshNeg,brLshPos,Alu<0], At[neBR340,6]; *AC0 _ AC0 lsh AC1 T _ (AC1) - (20C), DblGoto[brRshNeg,brRshPos,Alu<0], At[neBR340,7]; *AC0 _ AC0 rsh AC1 CSkipData, Goto[brBranch], At[neBR340,10]; *Switchon branch CSkipData, Goto[brLookup], At[neBR340,11]; *Switchon lookup T _ PCF[IBuf], LoadPage[nePage], Goto[br340NI], At[neBR340,12]; *Util (swats) T _ PCF[IBuf], LoadPage[nePage], Goto[br340NI], At[neBR340,13]; *Finish T _ PCF[IBuf], LoadPage[nePage], Goto[br340NI], At[neBR340,14]; *Abort CSkipData, Goto[brLongJump], At[neBR340,15]; *Long jump T _ PCF[IBuf], LoadPage[nePage], Goto[br340NI], At[neBR340,16]; *GetLV (swats) T _ PCF[IBuf], LoadPage[nePage], Goto[br340NI], At[neBR340,17]; *MulPlus br340NI: PFetch1[MDS,RTemp], GotoP[JsrIndFin]; *These don't contribute significantly to performance. brLshNeg: AC1 _ (AC1) + (17C), Goto[brLshNeg1]; *AC1 _ 17b - rsh count brLshPos: AC1 _ (Zero) - T - 1; *AC1 _ 17b - lshift count brRshNeg1: CycleControl _ AC1, CSkipData, Skip[Alu>=0]; AC0 _ 0C, Return; *Lsh count > 17b AC0 _ WFA[AC0], Return; *Lsh count = 0 to 17b brRshNeg: AC1 _ (AC1) + (17C), Goto[brRshNeg1]; brRshPos: AC1 _ (Zero) - T - 1, Skip[Alu<0]; *AC1 _ 17b - rsh count AC0 _ 0C, Goto[WRTRAM]; *Rshift count > 17b brLshNeg1: CycleControl _ AC1, CSkipData, Skip[Alu>=0]; AC0 _ 0C, Return; *Rsh count = 0, lsh count < -17b AC0 _ RF[AC0], Return; *Rsh count = 1 to 17b br340AC0toT: T _ AC0, Return; %Branch: "switchon" implemented by dispatch. jsr @350 ; with case value in AC0 value of last case number of cases lastTarget-. ... firstTarget-. continue here if out of range, AC0 unchanged % brBranch: CSkipData, Call[br340AC0toT]; *Point PCF at odd byte of last case value *T _ last case value - switchon value T _ PCF[IBuf] - T; CSkipData, Skip[Carry]; SkipData, Call[brBrn1]; *.gr. last case; fake call SkipData; *Point PCF at odd byte of no. cases; fake call *LU _ no. cases - (last case value - switchon value) - 1 = *LU _ switchon value - (last case value - no. cases + 1) = *LU _ switchon value - first case value LU _ PCF[IBuf] - T - 1; T _ (PCF.word) + T + 1, Goto[brBrn1,Carry']; PFetch1[PC,RTemp]; *Fetch self-rel ptr to new address T _ T, LoadPage[nePage]; *Bypass kludge T _ (RTemp) + T, GotoP[brJmpPz]; brBrn1: T _ PCF[IBuf] + 1, LoadPage[nePage]; brLk1: T _ PCF[RZero] + T, GotoP[PCJmp1]; *Jump to out-of-range %Switchon "lookup"; calling sequence as follows: jsr @351 ; with case value in AC0 number of cases case value 1 target1-. ... case value n targetn-. continue here if not found, AC0 unchanged. % brLookup: SkipData; *Point PCF at odd byte of no. cases; may refill T _ PCF[IBuf] + 1; RTemp _ T, CSkipData, Call[br340AC0toT]; *Have PCF pointing at even byte of case value j, value being looked up in T CSkipData; *Point PCF at odd byte of case value j; may refill RTemp _ (RTemp) - 1; LU _ PCF[IBuf] - T, Skip[Alu#0]; T _ RZero, LoadPage[nePage], Goto[brLk1]; CSkipData, Goto[aoSkip,Alu#0]; *Point PCF at even byte of targetj %Long jump; calling sequence: jsr @355 target-. % brLongJump: SkipData; T _ PCF[IBuf], LoadPage[nePage], Goto[brLk1]; :ENDIF; :IF[neBCPLI360]; OnPage[nePage]; *Arrive at JsrPzInd on Jsr @ 200-377; have EfAdr in T and in PCF[IBuf]; :IF[neBCPLI340]; *LU _ PCF[IBuf] - (340C) is pending; check for BCPL runtime JsrPzInd: LU _ PCF[IBuf] - (360C), Skip[Alu>=0]; PFetch1[MDS,RTemp], Goto[JsrIndFin]; *.ls. 340 LU _ PCF[IBuf] - (371C), Skip[Alu>=0]; LoadPage[br340Page], Goto[br340Disp]; *340-357 :ELSE; *LU _ PCF[IBuf] - (360C) is pending; check for BCPL runtime JsrPzInd: LU _ PCF[IBuf] - (371C), Skip[Alu>=0]; PFetch1[MDS,RTemp], Goto[JsrIndFin]; :ENDIF; Dispatch[PCF[IBuf],14,4], Goto[JsrPzIF,Alu>=0]; CSkipData, Disp[brJsrI360]; *360-370 brJsrI360: PFetch1[AC1,RTemp,0], Goto[brSNQ0], At[neBR360,0]; *SNQ0 PFetch1[AC0,RTemp,0], Goto[brSNQ1], At[neBR360,1]; *SNQ1 T _ (AC1) and (100000C), Goto[brLY01], At[neBR360,2]; *LY01 T _ (AC0) and (100000C), Goto[brLY10], At[neBR360,3]; *LY10 PFetch1[AC2,RTemp,3], Goto[brSY01], At[neBR360,4]; *SY01 PFetch1[AC2,RTemp,3], Goto[brSY10], At[neBR360,5]; *SY10 PFetch1[AC2,AC2,0], Goto[brReturn], At[neBR360,6]; *Return T _ PCF[IBuf], Goto[JsrPzIF], At[neBR360,7]; *StArgs (never xct) %GetFrame/StArgs is usually in the context: sta 3 1 2 jsr @GetFrame Frame size jsr @StArgs and is equivalent to the following pseudo-program: oldAC2 _ AC2; T _ AC2 - [PC+1] - 2; if [335] le T then swat (normal runtime does the swat) AC2 _ T; sta 0 4 2 ; Save arg1 sta 1 5 2 ; Save arg2 sta oldAC2 0 2 ; Save old frame ptr AC0 _ [[oldAC2+1]] ; AC0 _ nargs passed if AC0 < 3 then dblskip exit Temp _ [oldAC2+3] ; Temp _ arg3/frame rel ptr to extra args if AC0 eq 3 then sta Temp 6 2 else Move AC0-2 words from oldAC2 + Temp + 3 to AC2 + 6. dblskip exit % brGetFrame: T _ 335C, Call[brFetToRTemp], At[neBR360,10]; *RTemp _ stack limit T _ AC2; *DMA _ oldAC2 DMA _ T, SkipData, Call[.+1]; *Point PCF at odd byte of frame size (fake call) PFetch1[AC2,RTemp1,1]; *RTemp1 _ [oldAC2+1] *Allocated frame must be 2 larger for ancient BCPL programs T _ PCF[IBuf] + 1, LoadPage[brGarbPage0]; *T _ frame size + 1 T _ (AC2) - T - 1, CallP[brGF1]; *T _ AC2 - frame size - 2 = new frame ptr CSkipData, Call[brCSret]; *Point PCF at even byte of next opcode PStore1[AC2,AC0,4], Task; *Save arg1 T _ RTemp1; PFetch1[MDS,AC0], Goto[.+3,BPCChk']; *AC0 _ [[oldAC2+1]] = nargs passed LoadPage[0]; PFetch4[PC,IBuf,4], Call[neRefx]; PStore1[AC2,AC1,5]; *Even; Save arg2 T _ (AC0) - (3C); RTemp _ T, Skip[Alu>=0]; *RTemp _ args to move Goto[neTask1st]; *<=2 args--exit T _ (xBuf) + (3C), Skip[Alu=0]; *Even; skip if exactly 3 args PFetch1[DMA,xBuf]; *Odd; >3 args DMA _ T; *Even; bypass kludge if it matters T _ 6C, Call[neRet]; brGFlp: PStore1[AC2,xBuf]; RTemp _ (RTemp) - 1; T _ (Zero) + T + 1, Skip[Alu>=0]; Goto[neTask1st]; *Exit PFetch1[DMA,xBuf,1]; DMA _ (DMA) + 1, Return; OnPage[brGarbPage0]; brGF1: LU _ (RTemp) - T - 1; *LU _ stklim - new frame pointer *xBuf _ arg3/frel ptr to xargs PFetch1[DMA,xBuf,3], Goto[brGFok,Carry']; PC _ (PC) - 1, LoadPage[nePage]; T _ 370C, GotoP[JsrPzIF]; *Stack ovf--let software do it brGFok: AC2 _ T, CSkipData; *PCF now points at even byte of Jsr @StArgs PStore1[AC2,DMA,0], Return; *Save old frame ptr at [AC2] OnPage[nePage]; brCSret: CSkipData, Return; brSNQa: T _ PCF[IBuf], LoadPage[brGarbPage2]; *T _ mask RTemp _ (RTemp) and not T, GotoP[.+1]; OnPage[brGarbPage2]; T _ (RTemp1) and T; RTemp _ (RTemp) or T, Return; OnPage[nePage]; *SNQ0 executes @AC1 _ (@AC1 & not mask) or (AC0 & mask) where the mask *is [PC+1] and continues at PC+2. brSNQ0: T _ AC0; brSNQ0jsr: *Enter here from JSR 300-332 opcodes also RTemp1 _ T, CSkipData, Call[brSNQa]; *May refill IBuf PStore1[AC1,RTemp,0], Goto[neCS1st]; *SNQ1 is like SNQ0 with AC0 and AC1 interchanged. brSNQ1: T _ AC1; brSNQ1jsr: *Enter here from JSR 300-332 opcodes also RTemp1 _ T, CSkipData, Call[brSNQa]; PStore1[AC0,RTemp,0], Goto[neCS1st]; *Return right-justified in AC0 the AC1th byte from the array pointed to by *AC0 (Note: AC1 may be negative). brLY01: T _ (Rsh[AC1,1]) + T, Goto[.+3,R Odd]; PFetch1[AC0,AC0]; AC0 _ LdF[AC0,0,10], Return; PFetch1[AC0,AC0]; AC0 _ LdF[AC0,10,10], Return; *Like LY01 with AC0 and AC1 interchanged brLY10: T _ (Rsh[AC0,1]) + T, Goto[.+3,R Odd]; PFetch1[AC1,AC1]; AC1 _ LdF[AC1,0,10], Return; PFetch1[AC1,AC1]; AC1 _ LdF[AC1,10,10], Return; brFetToRTemp: PFetch1[MDS,RTemp], Return; brTtoDMA: DMA _ T, Return; *Bypass kludge brSYe: T _ Lsh[RTemp,10]; RTemp1 _ (RHMask[RTemp1]) or T; brSYx: PStore1[DMA,RTemp1,0], Goto[neTask1st]; brSYo: T _ RHMask[RTemp]; RTemp1 _ (LHMask[RTemp1]) or T, Goto[brSYx]; *Store the byte at AC2!3 into the AC1th byte of the array pointed to by *AC0 (Note: AC1 may be negative). brSY01: T _ (AC1) and (100000C); T _ (Rsh[AC1,1]) + T; PFetch1[AC0,RTemp1], Call[brTtoDMA]; AC1, DblGoto[brSYo,brSYe,R Odd]; *Like SY01 with AC0 and AC1 interchanged. brSY10: T _ (AC0) and (100000C); T _ (Rsh[AC0,1]) + T; PFetch1[AC1,RTemp1], Call[brTtoDMA]; AC0, DblGoto[brSYo,brSYe,R Odd]; *Return: AC2 _ AC2!0; PC _ (AC2!1)+1 brReturn: T _ (AC2) + 1, Call[brFetToRTemp]; T _ (RTemp) + 1, Goto[brJmpPz]; :ENDIF; :END[Nova];e6(1792)\30668f1 6f0