: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
*60000CYCLELeft Rotate
*61000DIRDisable interrupts
*61001EIREnable interrupts
*61002BRIBranch and Return from interrupt
*61003RCLKRead Clock
*61004SIOStart I/O
*61005BLTBlock Transfer
*61006BLKSBlock Store
*61007**SITStart Interval Timer - Returns -1
*61010JMPRAMJump to RAM - only works if AC1 = 420 (enters Mesa)
*61011RDRAMRead RAM - Returns -1
*61012WRTRAMWrite RAM - NOP
*61013DIRSDisable interrupts and skip if on
*61014VERSVersion - AC0 ← 40000C
*61015**DREADDouble-word read (altoII only)
*61016**DWRITEDouble-word write (altoII only)
*61017**DEXCHDouble-word exchange (altoII only)
*61020MULUnsigned Multiply
*61021DIVUnsigned Divide
*61022WMDS(IME instruction replacing AltoII DIAGNOSE1)
*61023**DIAGNOSE2Diagnostic (altoII only)
*61024BITBLTBit Block Transfer
*61036LOADRAMimplementation pending
*61037SDPSetDefaultPartition
*64400JSRIIJump to subroutine, double indirect, pc relative
*65000JSRISJump to subroutine, double indirect, ac2 relative
*67000CONVERTScan 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 0NWRDS -- number of words per scanline (< 400c)
word 1dba -- minus dest bit addr mod 20c
ac3:pointer to word xh of the character descriptor block
Character Descriptor Block:
words 0:xh-1bit 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:
AC0address of first source word - 1 (BLT), or data to be stored (BLKS)
AC1address of last destination word (= destination+word count-1)
AC2unused
AC3negative 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:
*1test loop count for done
*2test next mpr bit (AC1[17]), Rsh AC1
*2aif 1:AC0 ← AC0 + T
*3aRsh AC0, or’ing 100000 into AC0 if add carried
*2bif 0:Rsh AC0
*4a,3bor 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];