:TITLE[Alto];*Alto/Nova emulator
*Last edited: 14 April 1981 by Fiala
%Naming conventions (not universally followed): Labels use prefixes
"ne," "ao," "xo," and "br." Opcode execution begins on nePage at "ne1st" or
at one of its duplicates. Memory reference opcodes use only nePage; A-group
opcodes branch off to aoPage; opcodes in the extended instruction set branch
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.
%
*Start Alto/Nova emulator. Initialize registers and start at PCB. NOTE:
*R400, shared with Mesa, is initialized by Initialize.Mc at RegInit1 rather
*than here.
StartNova:*On neStartPage
SkipPCF0 ← 4C, At[neStartLoc];*4 in low 3 bits, 0 in sign
SkipPCF1 ← 26C, Task;*6 in low 3 bits, 0 in sign
SkipPCF2 ← 10C;*0 in low 3 bits, 1 in sign
R177400 ← T ← 177400C;
SkipPCF2 ← (SkipPCF2) + T, LoadPage[neStartPage1];
R177401 ← T ← (Zero) + T + 1;
OnPage[neStartPage1];
R177402 ← T ← (Zero) + T + 1;
SkipPCF3 ← 22C;
SkipPCF3 ← (SkipPCF3) or T, LoadPage[neStartPage2];*2 in low 3 bits, 1 in sign
R177403 ← (Zero) + T + 1;
OnPage[neStartPage2];
:IF[SmallTalkMode]; ****************************
R400 ← 400C;
:ENDIF; ****************************************
Carry ← 0C;
*rwpAC0 ← IP[AC0]C;*coincident with SkipPCF2
*rwpAC1 ← IP[AC1]C;*coincident with SkipPCF1
*rwpAC2 ← IP[AC2]C;*coincident with SkipPCF0
*rwpAC3 ← IP[AC3]C;*coincident with SkipPCF3
*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;
T ← MDShi, LoadPage[neFixBPage];
rpAC1 ← Or[And[IP[AC1],360],And[Sub[IP[AC1],1],17]]C, CallP[FixNBases];
*rpAC2 coincident with R177403
*rpAC2 ← Or[And[IP[AC2],360],And[Sub[IP[AC2],1],17]]C;
T ← PCB, LoadPage[nePage];
rpAC3 ← Or[And[IP[AC3],360],And[Sub[IP[AC3],1],17]]C, GotoP[brJmpPz];
OnPage[neFixBPage];
FixNBases:
DMAhi ← T;
PCBhi ← T;
AC0hi ← T;
AC1hi ← T;
AC2hi ← T;
AC3hi ← T, Return;
%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 every four non-jump opcodes adds 16 cycles or 4.0 cycles/opcode
(6 through PFetch4 + 12 after PFetch4 - 2 for ne1st which would have been
executed anyway); JMP and JSR independently refill the buffer, and are charged
for that refill. LDA@ and STA@ check explicitly for buffer refill during
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.
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 ne1st
or one of its duplicates is the 1st mi.
%
:IF[neBCPLI360]; *******************************
xoRefill:PFetch4[PCB,IBuf,4], GotoP[neRefx], At[xoBase,377];
:ENDIF; ****************************************
neRefill:PFetch4[PCB,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 ← RMZero;
PCB ← (PCB) + (4C), Return;
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. rpACn registers
*may have garbage in left half when coincident with other constants, so must
*mask these.
LU ← (StkP ← rpAC0) and T, Disp[LdaSta], At[OpTab,4];*lda 0
LU ← (StkP ← rpAC1) and T, Disp[LdaSta], At[OpTab,5];*lda 1
LU ← (StkP ← rpAC2) and T, Disp[LdaSta], At[OpTab,6];*lda 2
LU ← (StkP ← rpAC3) and T, 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] and T, Disp[JSRIIp], At[OpTab,15];*64000-67777
*Entries 16 and 17 in this dispatch table are in AltoX.Mc
*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], DispTable[20];*pg 0
:IF[neBCPL300]; ********************************
LU ← PCF[IBuf] - (300C), DblGoto[brJmpPz,brJsrPz,ALU<0];*pg 0
:ELSE; *****************************************
PFetch4[MDS,IBuf], DblGoto[JmpFin,JsrFin,ALU<0];*pg 0
:ENDIF; ****************************************
T ← (PCF.word) + T, DblGoto[PCJmp1,PCJsr1,ALU<0];*pc
T ← PCF[R177400] + T, DblGoto[PCJmp1,PCJsr1,ALU<0];*-pc
PFetch4[AC2,IBuf], DblGoto[JmpFin,JsrFin,ALU<0];*ac2
T ← (R177400) or T, FreezeResult, Goto[.-1];*-ac2
PFetch4[AC3,IBuf], DblGoto[JmpFin,JsrFin,ALU<0];*ac3
T ← (R177400) or T, FreezeResult, Goto[.-1];*-ac3
*Indirect Jmp/Jsr
PFetch1[MDS,RTemp], DblGoto[JmpIndFin,JsrIndFin,ALU<0];*@pg 0
:IF[neBCPLI340]; *******************************
LU ← PCF[IBuf] - (340C), DblGoto[JmpPzInd,JsrPzInd,ALU<0];*@pg 0
:ELSEIF[neBCPLI360]; ***************************
LU ← PCF[IBuf] - (360C), DblGoto[JmpPzInd,JsrPzInd,ALU<0];*@pg 0
:ELSE; *****************************************
PFetch1[MDS,RTemp], DblGoto[JmpIndFin,JsrIndFin,ALU<0];*@pg 0
:ENDIF; ****************************************
T ← (PCF.word) + T, DblGoto[PCIndJmp,PCIndJsr,ALU<0];*pc
T ← PCF[R177400] + T, DblGoto[PCIndJmp,PCIndJsr,ALU<0];*-pc
PFetch1[AC2,RTemp], DblGoto[JmpIndFin,JsrIndFin,ALU<0];*ac2
T ← (R177400) or T, FreezeResult, Goto[.-1];*-ac2
PFetch1[AC3,RTemp], DblGoto[JmpIndFin,JsrIndFin,ALU<0];*ac3
T ← (R177400) or T, FreezeResult, Goto[.-1];*-ac3
PCtoAC3:T ← (PCF.word) + T + 1;
AC3 ← T, Return;
*Entry from EIR, brBranch
PCJmp1:PFetch4[PCB,IBuf], Goto[JmpFin];*Odd
PCJsr1:PFetch4[PCB,IBuf];*Even
JsrFin:MNBR ← PCB, PCB ← T ← T, NoRegILockOK;*Even; bypass kludge
T ← MNBR, Call[PCtoAC3];*PC←new PC*2, AC3←old PC+1
T ← PCB;
PCB ← (PCB) + T, Goto[JsrFin1];
PCIndJmp:PFetch1[PCB,RTemp], Goto[JmpIndFin];
PCIndJsr:PFetch1[PCB,RTemp];
JsrIndFin:T ← PCB, Call[PCtoAC3];*Even; entry from JSRII/JSRIS
JmpIndFin:T ← RTemp;*Odd
*Entry from StartNova, BitBlt interrupt or exit, brReturn, brBranch
brJmpPz:PFetch4[MDS,IBuf];
*Entry here on LRJ continue.
JmpFin:T ← PCB ← T, Task, At[JmpFinLoc];*Odd; bypass kludge
PCB ← (PCB) + T;
JsrFin1:LU ← NWW, LoadPage[neIntPage0], Call[JmpFin3];
*Return here to start next inst with PCF pointing at even byte.
*Many opcodes finish by returning (which accomplishes the tasking requirement)
ne1st:CSkipData, T ← R177400;*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 ← PCB, PCB ← T, NoRegILockOK, SkipP[ALU#0];*Skip if possible int
OnPage[neIntPage0];
PCB ← (PCB) 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: PCB ← (PCB) and not (3C), Return;
intEna:PCB ← (PCB) 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);
intEnt1:
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];*Skip with NWW .eq. -ACTIVE
intPS2: PStore1[DMA,WW,0], Return;*Return with NWW .eq. 0
*Start an interrupt
NWW ← 100000C, Task;*Disable interrupts
T ← (ACTIVE) and T;*Odd; ACTIVE & -ACTIVE = right-most 1
WW ← (WW) and not T;
PCF[IBuf] ← 1C, Call[intPS2];*PCF[IBuf] will contain int level
*loop to get number of the highest priority interrupt
ACTIVE ← Rsh[ACTIVE,1], Skip[R Odd];
PCF[IBuf] ← PCF[IBuf] + 1, Return;
*enter int routine - save other interrupts in WW (452)
T ← PCF.word;*recover the PC
PCB ← (PCB) or T, LoadPage[nePage];
T ← (R400) or (100C), GotoP[.+1];
OnPage[nePage];
*Also enter here from IOUnIm
intXit:PStore1[MDS,PCB], 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], DispTable[20];*pg 0
PFetch1[MDS,RTemp], DblGoto[Dsz1,Isz1,ALU<0];*pg 0
T ← (PCF.word) + T, DblGoto[PCDsz,PCIsz,ALU<0];*pc
T ← PCF[R177400] + T, DblGoto[PCDsz,PCIsz,ALU<0];*-pc
PFetch1[AC2,RTemp], DblGoto[Dsz1,Isz1,ALU<0];*ac2
T ← (R177400) or T, FreezeResult, Goto[.-1];*-ac2
PFetch1[AC3,RTemp], DblGoto[Dsz1,Isz1,ALU<0];*ac3
T ← (R177400) or T, FreezeResult, Goto[.-1];*-ac3
*Indirect Isz/Dsz
PFetch1[MDS,RTemp], FreezeResult, Goto[IndDszIsz];
PFetch1[MDS,RTemp], FreezeResult, Goto[IndDszIsz];
T ← (PCF.word) + T, DblGoto[PCIndDsz,PCIndIsz,ALU<0];
T ← PCF[R177400] + T, FreezeResult, Goto[PCIndDszIsz];
PFetch1[AC2,RTemp], FreezeResult, Goto[IndDszIsz];
T ← (R177400) or T, FreezeResult, Goto[.-1];
PFetch1[AC3,RTemp], FreezeResult, Goto[IndDszIsz];
T ← (R177400) or T, FreezeResult, Goto[.-1];
PCDsz:PFetch1[PCB,RTemp], Goto[Dsz1];
PCIsz:PFetch1[PCB,RTemp];
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];
PCIndDsz:LU ← (Zero) - 1, Skip;
PCIndIsz:LU ← Zero;
PCIndDszIsz:PFetch1[PCB,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 ← R177400, Goto[ne2nd];
*Opcodes that smash TPC 0 by tasking or calling a subroutine exit at
*neTask1st to task immediately before starting the next opcode.
neTask1st:Call[neRet];*Odd
CSkipData, T ← R177400, 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], DispTable[20];*pg 0
T ← CNextData[IBuf], DblGoto[PzSta,PzLda,ALU<0];*pg 0
T ← PCF[R177400] and T, FreezeResult, Goto[PCLdaSta];*pc
T ← PCF[R177400], FreezeResult, Goto[PCLdaSta];*-pc
T ← CNextData[IBuf], DblGoto[AC2Sta,AC2Lda,ALU<0];*ac2
T ← CNextData[IBuf] or not T, DblGoto[AC2Sta,AC2Lda,ALU<0];*-ac2
T ← CNextData[IBuf], DblGoto[AC3Sta,AC3Lda,ALU<0];*ac3
T ← CNextData[IBuf] or not T, DblGoto[AC3Sta,AC3Lda,ALU<0];*-ac3
*Indirect Lda/Sta
T ← CNextData[IBuf], FreezeResult, Goto[PzLdaStaInd];*@pg 0
T ← CNextData[IBuf], FreezeResult, Goto[PzLdaStaInd];*@pg 0
T ← PCF[R177400] and T, FreezeResult, Goto[PCLdaStaInd];*@pc
T ← PCF[R177400], FreezeResult, Goto[PCLdaStaInd];*@-pc
T ← CNextData[IBuf], FreezeResult, Goto[AC2LdaStaInd];*@ac2
T ← CNextData[IBuf] or not T, FreezeResult, Goto[AC2LdaStaInd];*@-ac2
T ← CNextData[IBuf], FreezeResult, Goto[AC3LdaStaInd];*@ac3
T ← CNextData[IBuf] or not T, FreezeResult, Goto[AC3LdaStaInd];*@-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[PCB,IBuf,4];
PCF ← RMZero;
PCB ← (PCB) + (4C);
PzStaF:PStore1[MDS,Stack], Return;
PCSta:T ← (PCB) + 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[PCB,IBuf,4];*Odd
PCF ← RMZero;
PCB ← (PCB) + (4C);
LdaIndF1:T ← RTemp;*Even
PzLda:PFetch1[MDS,Stack], Return;*Even
PCLda:PFetch1[PCB,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[PCB,IBuf,4];*Odd
PCF ← RMZero;
PCB ← (PCB) + (4C);
StaIndF1:T ← RTemp, Goto[PzStaF];*Even
PCLdaStaInd:T ← CNextData[IBuf] + T, FreezeResult;
PFetch1[PCB,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: 29 to 34 cycles on logical; 31 to 36 cycles on arithmetic,
*including 4 cycles/opcode for buffer refill.
*Opcodes that skip average an additional 4.75 cycles.
**Max time to Return is 45 cycles on opcodes that skip and refill buffer.
neRegI:PCF[IBuf] ← Rcy[PCF[IBuf],3], Disp[.+1];
StkP ← rwpAC0, Goto[neSrc0], DispTable[20];*SrcAC = 0, DestAC = 0
StkP ← rwpAC1, Goto[neSrc0];*SrcAC = 0, DestAC = 1
StkP ← rwpAC2, Goto[neSrc0];*SrcAC = 0, DestAC = 2
StkP ← rwpAC3, Goto[neSrc0];*SrcAC = 0, DestAC = 3
StkP ← rwpAC0, Goto[neSrc1];*SrcAC = 1, DestAC = 0
StkP ← rwpAC1, Goto[neSrc1];*SrcAC = 1, DestAC = 1
StkP ← rwpAC2, Goto[neSrc1];*SrcAC = 1, DestAC = 2
StkP ← rwpAC3, Goto[neSrc1];*SrcAC = 1, DestAC = 3
StkP ← rwpAC0, Goto[neSrc2];*SrcAC = 2, DestAC = 0
StkP ← rwpAC1, Goto[neSrc2];*SrcAC = 2, DestAC = 1
StkP ← rwpAC2, Goto[neSrc2];*SrcAC = 2, DestAC = 2
StkP ← rwpAC3, Goto[neSrc2];*SrcAC = 2, DestAC = 3
StkP ← rwpAC0, Goto[neSrc3];*SrcAC = 3, DestAC = 0
StkP ← rwpAC1, Goto[neSrc3];*SrcAC = 3, DestAC = 1
StkP ← rwpAC2, Goto[neSrc3];*SrcAC = 3, DestAC = 2
StkP ← rwpAC3, Goto[neSrc3];*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], DispTable[10];*com
T ← Stack&-1 ← (Zero) - T, Disp[aoSHCc];*neg
Stack&-1 ← T, Disp[aoSHC];*mov
T ← Stack&-1 ← (Zero) + T +1, Disp[aoSHCc];*inc
T ← Stack&-1 ← (Stack&-1) - T -1, Disp[aoSHCc];*adc
T ← Stack&-1 ← (Stack&-1) - T, Disp[aoSHCc];*sub
T ← Stack&-1 ← (Stack&-1) + T, Disp[aoSHCc];*add
T ← Stack&-1 ← (Stack&-1) and T, Disp[aoSHC];*and
*We use Carry = 177777 for carry-in = 1, Carry = 0 for carry-in = 0
aoSHC:LU ← Carry, DblGoto[aoNN,aoNZ,ALU#0], DispTable[20];*Nosh, Carry
DblGoto[cZrN,cZrZ,ALU#0];*Nosh, 0
DblGoto[cNrN,cNrZ,ALU#0];*Nosh, 1
LU ← (Carry) xnor (0C), DblGoto[aoNN,aoNZ,ALU#0];*Nosh, Carry’
Carry, DblGoto[aoL1,aoL0,R<0];*Lsh, Carry
T ← Lsh[Stack,1], DblGoto[aoFC1,aoFC0,R<0];*Lsh, 0
T ← (Lsh[Stack,1])+1, DblGoto[cNrNa,cZrNa,R<0];*Lsh, 1
Carry, DblGoto[aoL1a,aoL0a,R>=0];*Lsh, Carry’
Carry, T ← 100000C, DblGoto[aoR1,aoR0,R<0];*Rsh, Carry
T ← Rsh[Stack,1], DblGoto[aoFC1,aoFC0,R Odd];*Rsh, 0
T ← Stack ← Rcy[Stack,1], DblGoto[aoRO,aoRE,R Odd];*Rsh, 1
Carry, T ← 100000C, DblGoto[aoR1a,aoR0a,R>=0];*Rsh, Carry’
LU ← Carry, DblGoto[aoSN,aoSZ,ALU#0];*Swap, Carry
T ← Rcy[Stack,10], DblGoto[cZrN,cZrZ,ALU#0];*Swap, 0
T ← Rcy[Stack,10], DblGoto[cNrN,cNrZ,ALU#0];*Swap, 1
LU ← (Carry) xnor (0C), DblGoto[aoSN,aoSZ,ALU#0];*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], DispTable[20];*Nosh, Carry
LU ← (RMZero) + 1, UseCOutAsCIn, DblGoto[aoNN,aoNZ,ALU#0];*Nosh, 0
LU ← (RMZero) - 1, UseCOutAsCIn, DblGoto[aoNN,aoNZ,ALU#0];*Nosh, 1
T ← Carry, FreezeResult, Goto[aoNX];*Nosh, Carry’
T ← (Carry) + 1, UseCOutAsCIn, DblGoto[aoLS,aoLA,R<0];*Lsh, Carry
T ← (Stack) + T, UseCOutAsCIn, DblGoto[aoFC1,aoFC0,R<0];*Lsh, 0
T ← (RMZero) - 1, UseCOutAsCIn, Goto[aoLS];*Lsh, 1
LU ← (Carry) + 1, UseCOutAsCIn, Goto[aocL];*Lsh, Carry’
LU ← (Carry) + 1, UseCOutAsCIn, Goto[aocR];*Rsh, Carry
T ← 100000C, DblGoto[aoR1,aoR0,Carry];*Rsh, 0
T ← 100000C, DblGoto[aoR1a,aoR0a,Carry’];*Rsh, 1
LU ← (Carry) + 1, UseCOutAsCIn, Goto[aocRa];*Rsh, Carry’
LU ← (Carry) + 1, UseCOutAsCIn, DblGoto[aoSN,aoSZ,ALU#0];*Swap, Carry
LU ← (RMZero) + 1, UseCOutAsCIn, DblGoto[aoSN,aoSZ,ALU#0];*Swap, 0
LU ← (RMZero) - 1, UseCOutAsCIn, DblGoto[aoSN,aoSZ,ALU#0];*Swap, 1
T ← Carry, FreezeResult, Goto[aoSX];*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 ← (RMZero) - 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 ← (RMZero) - 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], DispTable[4];*cin ← Carry
LU ← 0C, Disp[aoFNC];*cin ← 0
LU ← 100000C, Disp[aoFNC];*cin ← 1
LU ← (Carry) xnor (0C), Disp[aoFNC];*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], DispTable[10];*com
T ← Stack&-1 ← (Zero) - T, DblGoto[aoTC1,aoTC0,ALU<0];*neg
Stack&-1 ← T, DblGoto[aoNTC1,aoNTC0,ALU<0];*mov
T ← Stack&-1 ← (Zero) + T +1, DblGoto[aoTC1,aoTC0,ALU<0];*inc
T ← Stack&-1 ← (Stack&-1) - T -1, DblGoto[aoTC1,aoTC0,ALU<0];*adc
T ← Stack&-1 ← (Stack&-1) - T, DblGoto[aoTC1,aoTC0,ALU<0];*sub
T ← Stack&-1 ← (Stack&-1) + T, DblGoto[aoTC1,aoTC0,ALU<0];*add
T ← Stack&-1 ← (Stack&-1) and T, DblGoto[aoNTC1,aoNTC0,ALU<0];*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], DispTable[4];
T ← Stack ← Lsh[Stack,1], DblGoto[aoFC1,aoFC0,R<0];
T ← Stack ← Rsh[Stack,1], DblGoto[aoFC1,aoFC0,R Odd];
T ← Stack ← Lcy[Stack,10], DblGoto[cZrZ,cZrN,ALU=0];
aoSH10:DblGoto[cNrZ,cNrN,ALU=0], DispTable[4];
T ← Stack ← (Lsh[Stack,1]) + 1, DblGoto[cNrNa,cZrNa,R<0];
T ← Stack ← Rcy[Stack,1], DblGoto[aoF1r1,aoF0r1,R Odd];
T ← Stack ← Lcy[Stack,10], DblGoto[cNrZ,cNrN,ALU=0];
aoFC0:DblGoto[cZrZ,cZrN,ALU=0];
aoFC1:DblGoto[cNrZ,cNrN,ALU=0];
aoF0r1:T ← Stack ← (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
*NOTE: PCF ← PCF[SkipPCF0] will point PCF at the even byte of the word
*after the opcode being skipped.
cZrNLd:Stack&-1, Disp[.+2];*No load
cZrLd:Carry ← 0C, Disp[.+1];*Load
Stack&+1 ← T, CSkipData, Return, DispTable[10];*--
PCF ← PCF[SkipPCF0], DblGoto[aoRfl,aoXit,R<0];*Skp
PCF ← PCF[SkipPCF0], DblGoto[aoRfl,aoXit,R<0];*szc
Stack&+1 ← T, CSkipData, Return;*snc
PCF ← PCF[SkipPCF0], DblGoto[aoRfl,aoXit,R<0];*szr
Stack&+1 ← T, CSkipData, Return;*snr
PCF ← PCF[SkipPCF0], DblGoto[aoRfl,aoXit,R<0];*sez
Stack&+1 ← T, CSkipData, Return;*sbn
cNrNLd:Stack&-1, Disp[.+2];*No load
cNrLd:Carry ← (Carry) or not (0C), Disp[.+1];*Load
Stack&+1 ← T, CSkipData, Return, DispTable[10];
PCF ← PCF[SkipPCF0], DblGoto[aoRfl,aoXit,R<0];
Stack&+1 ← T, CSkipData, Return;
PCF ← PCF[SkipPCF0], DblGoto[aoRfl,aoXit,R<0];
PCF ← PCF[SkipPCF0], DblGoto[aoRfl,aoXit,R<0];
Stack&+1 ← T, CSkipData, Return;
PCF ← PCF[SkipPCF0], DblGoto[aoRfl,aoXit,R<0];
Stack&+1 ← T, CSkipData, Return;
%The straightforward way of doing an A-group skip is as follows:
Stack&+1 ← T, CSkipData;*Can’t cause refill
CSkipData, Task;*Might refill
CSkipData;*Can’t refill
ne1stx:CSkipData, T ← R177400, Goto[ne2nd];*Might refill
This method has the serious drawback that the A-group exit has to be on nePage
(else add another mi); also average timing for this sequence is 10 cycles
compared to 6.75 for the sequence using the SkipPCF registers;
worst case time to return is 5 cycles faster for SkipPCF also.
%
aoRfl:PFetch4[PCB,IBuf,4];
PCB ← (PCB) + (4C);
aoXit:Stack&+1 ← T, 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 - NOP
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*DREADAC0←$AC3; AC1←$(AC3+1) (Alto 2 n.i.)
61016*DWRITE$AC3←AC0; $(AC3+1)←AC1 (Alto 2 n.i.)
61017*DEXCHDouble-word exchange (Alto 2 n.i.)
61020MULUnsigned Multiply
61021DIVUnsigned Divide
61022*WMDS(deimplemented IME instruction, AltoII DIAGNOSE1)
61023*DIAGNOSE2Diagnostic (altoII only)
61024BITBLTBit Block Transfer
61025*XMLDAalternate bank LDA 0,x
61026*XMSTAalternate bank STA 0,x
VM 177740+n[?:?] contains the alternate bank for task n
61027-61033--
61034POWOFFPower off machine
61035CSUMCompute ethernet-style checksum
61036LOADRAMload microstore from memory and optional start
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 - 60177
*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 - 61177
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[xoDIR];*Timing 12 thru here
NoParB:StkP ← rwpAC1, Disp[xoEIR];*Timing 13 thru here
*JSRII - 64400, 64600
*JSR double indirect, PC relative
JSRIIp:T ← (PCF.word) + T, Skip, At[IO1Tab,2];*EfAddr positive
JSRIIn:T ← PCF[R177400] + T, At[IO1Tab,3];*EfAddr negative
PFetch1[PCB,RTemp], Call[neRet];
JSRIS:T ← RTemp, Goto[JsrPzIF];
*JSRIS - 65000, 65200
*JSR double indirect, AC2 relative
JSRISp:PFetch1[AC2,RTemp], Goto[JSRIS], At[IO1Tab,4];*EfAddr positive
JSRISn:T ← PCF[IBuf] or not (377C), Goto[.-1], 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:PFetch1[AC2,RTemp1], Skip, At[IO1Tab,14];
Convertn:T ← PCF[IBuf] or not (377C), Goto[.-1], At[IO1Tab,15];*fetch NWRDS
T ← (RMZero) + 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 ← IP[xBuf]C;
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
**This may be the only PStore4 which is the first mi after a return; if so,
**might be profitable to enforce Output/Output/PStore4 gotcha by requiring
**some of the delay after returning rather than all of it after the O/O.
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];
xoDIR:NWW ← (NWW) or (100000C), Goto[xoWRTRAM], At[xoTab0,0];*61000b
xoEIR:T ← (R400) or (52C), At[xoTab1,0];*61001b
xoBRI1:PFetch1[MDS,WW], Task;
NWW ← (NWW) and not (100000C);
T ← (WW) and not (100000C);
NWW ← (NWW) or T, LoadPage[nePage];
PCF[IBuf], DblGotoP[xoEIRz,xoBRIy,R Odd];
OnPage[nePage];
*EIR must test for interrupts NOW. We simulate JMP .+1
xoEIRz:T ← (PCF.word) + 1, Goto[PCJmp1];
xoBRIy:T ← (R400) or (100C), Goto[JmpPzInd]; *fetch PC from location 500
xoBRI:T ← (R400) or (52C), Goto[xoBRI1], At[xoTab0,1] ;*61002b
xoRCLK:LoadPage[XMiscPage], At[xoTab1,1] ;*61003b
T ← (R400) or (30C), CallP[MXRClk];
AC1 ← T;
T ← RTemp, Goto[RCLK1];
xoSIO:T ← AC0, LoadPage[eePage], At[xoTab0,2];*61004b
CSkipData, GotoP[eeSIO];*returns to next opcode
%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.
xoBLT:T ← AC0, At[xoTab1,2];*61005b
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], DispTable[4];*01
AC0 ← T ← (AC0) + 1, Call[BltLp];*10
AC0 ← T ← (AC0) + 1, Call[BltLp];*11
*Destination quadaligned; setup loop.
LU ← AC3, Call[BltZT];*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
*Smashes wBuf3
PFetch4[AC0,wBuf3,0], NonQuadOK, Goto[Blt4S1], DispTable[4];*01 (smashes wBuf3)
PFetch2[AC0,xBuf,1], Goto[Blt4S2];*10
PFetch4[AC0,xBuf1,2], NonQuadOK, Goto[Blt4S3];*11 (smashes yBuf)
PFetch4[AC0,xBuf,1], Goto[Blt4S];*00 (quadaligned)
Blt4S1:PFetch1[AC0,xBuf3,4], Goto[Blt4S];
Blt4S2:PFetch2[AC0,xBuf2,3], Goto[Blt4S];
Blt4S3: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
xoBLKS:T ← AC0, LoadPage[neBlksPage0], At[xoTab0,3];*61006b
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], DispTable[4];*01
LU ← AC3, Call[BlksLx];*10
LU ← AC3, Call[BlksLx];*11
*Quadaligned; setup loop--ensure word count .ne. 0
LU ← AC3, Call[BltZT];*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.
xoBLT:AC0 ← T ← (AC0) + 1, LoadPage[nePage], At[xoTab1,2];*61005b
Call[neRet];*For tasking
PFetch1[MDS,xBuf], Call[BlksLp];
AC0 ← T ← (AC0) + 1, Goto[.-1];
*Average timing ~= 30 + 17*nwords cycles
xoBLKS:T ← AC0, LoadPage[nePage], At[xoTab0,3];*61006b
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];
xoSIT:CSkipData, Return, At[xoTab1,3];*61007b (nop)
*JMPRAM - 61010 is in AltoX.Mc
xoRDRAM:AC0 ← (Zero) - 1, At[xoTab1,4];*61011b (return -1)
xoWRTRAM:CSkipData, Return, At[xoTab0,5];*61012b (nop)
xoDIRS:CSkipData, At[xoTab1,5];
LoadPage[nePage];
NWW ← (NWW) or (100000C), DblGotoP[neTask1st,neTaskSkp,R<0];
*VERS - 61014 in AltoX.Mc
*DREAD - 61015 not implemented (Alto II only)
*DWRITE - 61016 not implemented
*DEXCH - 61017 not implemented
*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];*61020b
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];*61021b
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 (would be 9 to 10 if no tasking requirement)
**Could make this 20 to 23 cycles/two bits by open-coding one more loop
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;
*61022 - WMDS deimplemented IME opcode, Alto II DIAGNOSE1 not implemented
*61023 - DIAGNOSE2 not implemented, Alto II only
**Enter bbBitBLT with StkP pointing at AC1 (icom*2), pointer to BitBlt table in
*AC2, and (Cycle&PCXF) and not (100000C) in T.
xoBitBlt:LoadPage[bbPage], At[xoTab0,12];*61024b
T ← (Cycle&PCXF) and not (100000C), GotoP[bbBitBLT];
xoPOff:D0Off, Goto[.], At[xoTab0,16];*61034b
*AC0/ checksum (initialize to 0)
*AC1/ block pointer
*AC3/ word count
*Algorithm is CSum ← (CSum+Word) lcy 1 with 1’s complement add
xoCSum:LU ← AC3, LoadPage[neCSPage], At[xoTab1,16];*61035b
PFetch4[AC1,xBuf,0], DblGotoP[aCSbeg,aCSend,ALU#0];
OnPage[neCSPage];
aCSlp:AC1 ← (Zero) + T + 1, Disp[.+1];
T ← Lcy[xBuf,1], Call[aCSwrd], DispTable[4];
T ← Lcy[xBuf1,1], Call[aCSwrd];
T ← Lcy[xBuf2,1], Call[aCSwrd];
T ← Lcy[xBuf3,1], Call[aCSwrd];
PFetch4[AC1,xBuf,0];
aCSbeg:LU ← NWW, Skip[R>=0];*Skip if interrupts enabled
T ← (AC1) or (3C), Skip;
T ← (AC1) or (3C), Skip[ALU#0];
Dispatch[AC1,16,2], Goto[aCSlp];*No int. requests
LoadPage[neIntPage0];
T ← (R400) + (52C), Goto[intEnt1];
aCSwrd:AC3 ← (AC3) - 1;
AC0 ← (Lcy[AC0,1]) + T, Skip[ALU=0];
AC0 ← (AC0) + 1, UseCOutAsCIn, Return;
AC0 ← (AC0) + 1, UseCOutAsCIn;
aCSend:LU ← (AC0) xnor (0C);
T ← RMZero, LoadPage[nePage], Skip[ALU#0];
AC0 ← T, CSkipData, GotoP[neTask1st];*Change -1 result to 0
CSkipData, GotoP[neTask1st];
*AC0 pointer to array in MakeLoaderFile form
**1st 4 mi here would disappear if AC0 were coincident with LP**
xoLRJ:T ← AC0, LoadPage[neLRJPage], At[xoTab0,17];*61036b
LP ← T;
OnPage[neLRJPage];
T ← MDShi;
LPhi ← T;
*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.
*FFault should be even (to crash on MC1/Stk errors), but it is already even,
*so nothing special is required here.
T ← (RMZero) + (100000C) + 1;
T ← (LdF[AC1,17,1]) xor T;
*RTemp1 < 0 = call from Alto emulator
*odd = resume at JmpFin, even = jump to starting address
RTemp1 ← T, LoadPageExternal[LRJPage];
**Low 4 bits of xfTemp1 should be 0 for inline refresh or 1 for normal tasking
xfTemp1 ← T, GotoExternal[LRJStart];
xoSDP:T ← AC0, LoadPage[XMiscPage], At[xoTab1,17];*61037b
RTemp ← T, CallP[MXPar];*In Timer.Mc
RCLK1:AC0 ← T, LoadPage[nePage];
xoExit:CSkipData, GotoP[neTask1st];
: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, DispTable[15,17,0];*301
AC1 ← Rsh[AC1,5], Return;*303
AC1 ← Rsh[AC1,4], Return;*305
AC1 ← Rsh[AC1,3], Return;*307
AC1 ← Rsh[AC1,2], Return;*311
AC1 ← Rsh[AC1,1], Return;*313
T ← AC0 ← Lsh[AC0,7], Goto[brJsrSNQ0];*315
T ← AC0 ← Lsh[AC0,6], Goto[brJsrSNQ0];*317
T ← AC0 ← Lsh[AC0,5], Goto[brJsrSNQ0];*321
T ← AC0 ← Lsh[AC0,4], Goto[brJsrSNQ0];*323
T ← AC0 ← Lsh[AC0,3], Goto[brJsrSNQ0];*325
T ← AC0 ← Lsh[AC0,2], Goto[brJsrSNQ0];*327
T ← AC0 ← Lsh[AC0,1], Goto[brJsrSNQ0];*331
br300Even:
CSkipData, Disp[.+1];
AC0 ← Rsh[AC0,6], Return, DispTable[16,17,0];*300
AC0 ← Rsh[AC0,5], Return;*302
AC0 ← Rsh[AC0,4], Return;*304
AC0 ← Rsh[AC0,3], Return;*306
AC0 ← Rsh[AC0,2], Return;*310
AC0 ← Rsh[AC0,1], Return;*312
Return;*314 (nop)
T ← AC1 ← Lsh[AC1,7], Goto[brJsrSNQ1];*316
T ← AC1 ← Lsh[AC1,6], Goto[brJsrSNQ1];*320
T ← AC1 ← Lsh[AC1,5], Goto[brJsrSNQ1];*322
T ← AC1 ← Lsh[AC1,4], Goto[brJsrSNQ1];*324
T ← AC1 ← Lsh[AC1,3], Goto[brJsrSNQ1];*326
T ← AC1 ← Lsh[AC1,2], Goto[brJsrSNQ1];*330
T ← AC1 ← Lsh[AC1,1], Goto[brJsrSNQ1];*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[xoWRTRAM], DispTable[20];*AC0←AC0 or AC1
AC0 ← (AC0) xor T, Goto[xoWRTRAM];*AC0←AC0 xor AC1
AC0 ← (AC0) xnor T, Goto[xoWRTRAM];*AC0←AC0 xnor AC1
T ← PCF[IBuf], LoadPage[nePage], Goto[br340NI];*Mult
T ← PCF[IBuf], LoadPage[nePage], Goto[br340NI];*DivRem
T ← PCF[IBuf], LoadPage[nePage], Goto[br340NI];*DivRem
T ← (AC1) - (20C), DblGoto[brLshNeg,brLshPos,ALU<0];*AC0 ← AC0 lsh AC1
T ← (AC1) - (20C), DblGoto[brRshNeg,brRshPos,ALU<0];*AC0 ← AC0 rsh AC1
CSkipData, Goto[brBranch];*Switchon branch
CSkipData, Goto[brLookup];*Switchon lookup
T ← PCF[IBuf], LoadPage[nePage], Goto[br340NI]; *Util (swats)
T ← PCF[IBuf], LoadPage[nePage], Goto[br340NI]; *Finish
T ← PCF[IBuf], LoadPage[nePage], Goto[br340NI]; *Abort
CSkipData, Goto[brLongJump];*Long jump
T ← PCF[IBuf], LoadPage[nePage], Goto[br340NI]; *GetLV (swats)
T ← PCF[IBuf], LoadPage[nePage], Goto[br340NI]; *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[xoWRTRAM];*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[PCB,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.word) + 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
brLookLp:
RTemp ← (RTemp) - 1;
LU ← PCF[IBuf] - T, Skip[ALU#0];
T ← RMZero, LoadPage[nePage], Goto[brLk1];
CSkipData, Goto[.+3,ALU=0];*Point PCF at even byte of targetj
CSkipData, Call[xoWRTRAM];
CSkipData, Goto[brLookLp];*Point PCF at odd byte of case value
%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], DispTable[11,17,0];*SNQ0
PFetch1[AC0,RTemp,0], Goto[brSNQ1];*SNQ1
T ← (AC1) and (100000C), Goto[brLY01];*LY01
T ← (AC0) and (100000C), Goto[brLY10];*LY10
PFetch1[AC2,RTemp,3], Goto[brSY01];*SY01
PFetch1[AC2,RTemp,3], Goto[brSY10];*SY10
PFetch1[AC2,AC2,0], Goto[brReturn];*Return
T ← PCF[IBuf], Goto[JsrPzIF];*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];*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[brGarbPage1];*T ← frame size + 1
T ← (AC2) - T - 1, GotoP[.+1];*T ← AC2 - frame size - 2 = new frame ptr
OnPage[brGarbPage1];
LU ← (RTemp) - T - 1;*LU ← stklim - new frame pointer
*xBuf ← arg3/frel ptr to xargs
PFetch1[DMA,xBuf,3], Goto[brGFok,Carry’];
PCB ← (PCB) - 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];*Save old frame ptr at [AC2]
CSkipData, Task;
CSkipData;*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[PCB,IBuf,4], Call[neRefx];
PStore1[AC2,AC1,5];*Even; Save arg2
T ← (AC0) - (3C);
RTemp ← T, LoadPage[nePage], Skip[ALU>=0];*RTemp ← args to move
GotoP[neTask1st];*<=2 args--exit
T ← (xBuf) + (3C), SkipP[ALU=0];*Even; skip if exactly 3 args
OnPage[nePage];
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;
brSNQa:T ← PCF[IBuf], LoadPage[brGarbPage0];*T ← mask
RTemp ← (RTemp) and not T, GotoP[.+1];
OnPage[brGarbPage0];
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;
*Return: AC2 ← AC2!0; PC ← (AC2!1)+1
brReturn:T ← (AC2) + 1, Call[brFetToRTemp];
T ← (RTemp) + 1, Goto[brJmpPz];
*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];
DMA ← T, LoadPage[brGarbPage2];*Bypass kludge
AC1, DblGotoP[brSYo,brSYe,R Odd];
*Like SY01 with AC0 and AC1 interchanged.
brSY10:T ← (AC0) and (100000C);
T ← (Rsh[AC0,1]) + T;
PFetch1[AC1,RTemp1];
DMA ← T, LoadPage[brGarbPage2];
AC0, DblGotoP[brSYo,brSYe,R Odd];
OnPage[brGarbPage2];
brSYe:T ← Lsh[RTemp,10];
RTemp1 ← (RHMask[RTemp1]) or T;
brSYx:PStore1[DMA,RTemp1,0];
Return;
brSYo:T ← RHMask[RTemp];
RTemp1 ← (LHMask[RTemp1]) or T, Goto[brSYx];
:ENDIF; ****************************************
:END[Nova];