:TITLE[Alto];*Alto/Nova emulator
*Ed Fiala 10 September 1982
%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[neBCPLI340]; *******************************
xoRefill:PFetch4[PCB,IBuf,4], GoToP[neRefx], At[xoBase,377];
:ENDIF; ****************************************
neRefill:PFetch4[PCB,IBuf,4], GoToP[neRefx], 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;
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;
*BlksLp jumps to intSub.
intSub:LU ← NWW, Skip[R>=0];*Skip if interrupts enabled
PCB ← (PCB) and not (3C), Return;
*BLT/BLKS enter interrupts with DblGoTo[intEnt,int0Ret,ALU#0];
PCB ← (PCB) and not (3C), Skip[ALU#0];*Skip if int requests
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);
*xoCSum enters at intEnt1.
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. Absolute
*placement is for overlays.
neTask1st:Call[neRet], At[OpStart];*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 ← RZero;
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 ← RZero;
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 ← RZero;
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 ← (RZero) + 1, UseCOutAsCIn, DblGoTo[aoNN,aoNZ,ALU#0];*Nosh, 0
LU ← (RZero) - 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 ← (RZero) - 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 ← (RZero) + 1, UseCOutAsCIn, DblGoTo[aoSN,aoSZ,ALU#0];*Swap, 0
LU ← (RZero) - 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];
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], 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];
aoF0r1:T ← Stack ← (Stack) or (100000C), GoTo[cZrN];
aoF1r1:Dispatch[PCF[IBuf],2,1], DblGoTo[cNrNLd,cNrLd,R Odd];
:ENDIF; ****************************************
aoFC1:DblGoTo[cNrN,cNrZ,ALU#0];*Odd
aoFC0:DblGoTo[cZrN,cZrZ,ALU#0];*Even
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 ([Indigo]<DoradoDocs>ExtendedOpcodes.Press)
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 xor 1) (Alto 2 n.i.)
61016*DWRITE$AC3←AC0; $(AC3 xor 1)←AC1 (Alto 2 n.i.)
61017*DEXCHDouble-word exchange (Alto 2 n.i.)
61020MULUnsigned Multiply
61021DIVUnsigned Divide
61022WPRINTER Printer←AC0 (Alto 2 DIAGNOSE1 n.i.)
61023RPRINTER AC0←Printer (Alto 2 DIAGNOSE2 n.i.)
61024BITBLTBit Block Transfer
61025XMLDALoads AC0 from the AC1th alternate bank location
61026XMSTAStores AC0 into the AC1th alternate bank location
VM 177740+n[12:13d] are the normal bank for task n
VM 177740+n[14:15d] are the alternate bank for task n
61027DSPRATEAccepts new end-of-field fill in AC0 and returns old value in
AC0. 213b selects 60 Hz, 11b is normal 77 Hz.
61030---
61031---
61032DSPWIDAC0 = -1 makes full display width available; = 0 reverts to
standard Alto-size picture. Width returned in AC0.
61033MEMCFGReturns no. of 400b-word pages of storage in AC0, number of
64k-word banks of VM in AC1.
61034POWOFFPower off machine
61035CSUMCompute ethernet-style checksum
61036LOADRAMload microstore from memory and optional start
61037SDPSetDefaultPartition
61040IOB Input (Dorado only)
61041IOB Output (Dorado only)
61042Halt(n.i.)
61043Turn on PC sampling (Dorado only)
61044General Input (Dorado only)
61045General Output (Dorado only)
64400JSRIIJump to subroutine, double indirect, pc relative
65000JSRISJump to subroutine, double indirect, ac2 relative
67000CONVERTScan conversion of characters
NOTE: operations with * are not implemented, and will trap if executed when
the xoTraps switch in GlobalDefs is 1 or will jump to undefined locations
in the microstore if xoTraps is 0.
Want WRS232 and RRS232 opcodes?
%
*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];
T ← 17C, GoTo[.+3,ALU#0];
T ← (LSh[AC1,4]) or T;*Cycle by AC1 if count=0
PCF[IBuf] ← T, Skip;
PCF[IBuf] ← (LSh[PCF[IBuf],4]) or T;
CycleControl ← CNextData[IBuf];*DBX←cycle count, MWX←17b
xoAC0RF:
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 (upper left corner) minus NWRDS
ac2+ Disp points to two word block:
word 0NWRDS -- number of words per scanline (< 200b)
word 1dba -- minus dest bit addr mod 20b
ac3:pointer to word xh of the character descriptor block
Character Descriptor Block:
word 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 ← (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 ← 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
**I think this mi and the NextInst; PStore4 by Mesa are the only mi subject
**to the Output/Output/PStore4 gotcha; if so, might be profitable to enforce
**Output/Output/PStore4 gotcha by requiring some 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;
PCF[IBuf], LoadPage[nePage], DblGoToP[xoEIRz,xoBRIy,R Odd];
*EIR must test for interrupts NOW. We simulate JMP .+1
xoEIRz:T ← (PCF.word) + 1, GoToP[PCJmp1];
xoBRIy:T ← (R400) or (100C), GoToP[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], GoTo[intSub];
: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], GoTo[intSub];
: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
*Timing xoDiv to exit = 19 + (9/quotient 1) + (12/quotient 0) cycles.
xoDiv:LoadPage[neDivPage], T ← AC2, At[xoTab1,10];*61021b
LU ← (AC0) - T, GoToP[.+1];
OnPage[neDivPage];
RTemp ← 16C, GoTo[.+3,Carry’];
LoadPage[nePage];
xoDivX: RTemp, CSkipData, DblGoTo[neTask1st,neTaskSkp,R Odd];
T ← 31C;
T ← AC2, SALUF ← T;*SALUF ← A+A+1
RTemp1 ← (Zero) - T;*Save minus divisor
*1st bit shifted in is "don’t care" (shifted out on the final step).
AC1 ← (AC1) SALUFOP T, Call[xoDvE];
%Shift the high dividend left 1 while subtracting the divisor and adding in
the bit shifted out of the low dividend; +1 compensates for -1 inserted at
end of loop, and this circumlocution makes the carry correct for the problem
divisors 1 and 177777b.
%
AC0 ← (LSh[AC0,1]) + T + 1, Skip[R>=0];
*Shift the low dividend while bringing in the quotient bit
AC1 ← (AC1) SALUFOP T, GoTo[xoDvSb];
AC1 ← (AC1) SALUFOP T, UseCOutAsCIn, GoTo[xoDvSb,Carry];
*Subtract failed. We would like to use the fact that
*((Divd+Divs) lshift 1) - Divs = (Divd lshift 1) + Divs
*I.e., we would like to add rather than subtract on the next step, but
*the carries get screwed up.
T ← AC2, FreezeResult;
AC0 ← (AC0) + T, FreezeResult;
*Subtract succeeded.
xoDvSb:RTemp ← (RTemp) - 1, FreezeResult, Skip[R>=0];
LoadPage[nePage], GoTo[xoDivX];
*Subtract divisor from high dividend on 1st/next step while adding in bit
*shifted out of low dividend
xoDvE:T ← (RTemp1) - 1, UseCOutAsCIn, Return;
%Obsolete divide.
RTemp ← 17C, GoTo[.+3,Carry’];
LoadPage[nePage];
xoDivX: RTemp, CSkipData, DblGoTo[neTask1st,neTaskSkp,R Odd];
T ← 31C;
*1st bit shifted into AC1 is "don’t care" because AC1 will be shifted an
*extra time at the end.
SALUF ← T, T ← AC2, Call[xoDvA1];
*Loop time 11 to 12 cycles/bit.
RTemp ← (RSh[RTemp,1]) - 1, GoTo[xoDvCryT,R Even];
AC0 ← (AC0) - T, Skip[ALU>=0];*Subtract divisor
AC1 ← (AC1) SALUFOP T, GoTo[xoDvX0];
xoDvA1:AC1 ← (AC1) SALUFOP T, GoTo[xoDvSb];*Force quotient bit to 1
xoDvCryT:
AC0 ← (AC0) - T, GoTo[xoDvA0,ALU>=0];*Subtract divisor
AC1 ← (AC1) SALUFOP T, UseCOutAsCIn, Skip[Carry];
AC0 ← (AC0) + T;
xoDvX0: RTemp ← (RTemp) + 1, LoadPage[nePage], GoTo[xoDivX];*Skip exit
xoDvA0:AC1 ← (AC1) SALUFOP T, UseCOutAsCIn, Skip[Carry];*Successful?
AC0 ← (AC0) + T, FreezeResult;*No--undo
xoDvSb:AC0 ← (AC0) SALUFOP T, UseCOutAsCIn;
RTemp ← (RTemp) SALUFOP T, UseCOutAsCIn, Return;
%
xoPrinterGetsAC0:
Printer ← AC0, GoTo[xoWRTRAM], At[xoTab0,11];*61022b
xoAC0GetsPrinter:
T ← Printer, GoTo[TtoAC0CSR], At[xoTab1,11];*61023b
**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];
:IF[AltoXMMode]; *******************************
xoXMLDA:
T ← (RZero) or not (37C), GoTo[xoABSU], At[xoTab1,12];*61025b
xoXMSTA:
T ← (RZero) or not (37C), GoTo[xoABSU], At[xoTab0,13];*61026b
xoABSU:PFetch1[MDS,LPhi];
T ← AC1, Task;
LP ← T;
T ← LPhi ← LdF[LPhi,16,2];
LPhi ← (LSh[LPhi,10]) or T;
PCF[IBuf], LoadPage[nePage], Skip[R Odd];
PStore1[LP,AC0,0], GoToP[neCS1st];
PFetch1[LP,AC0,0], GoToP[neCS1st];
:ELSEIF[xoTraps]; ******************************
LoadPage[nePage], GoTo[xoUnIm], At[xoTab1,12];*61025b
LoadPage[nePage], GoTo[xoUnIm], At[xoTab0,13];*61026b
:ENDIF; ****************************************
*Exchange AC0 with the end-of-field fill parameter for the display.
*11b is normal (77d Hz.); 213b is for videotaping (60 Hz.). This works
*only for the LF monitor. Code is completed in Display.Mc.
xoFrameRate:
T ← AC0, GoTo[vFrameRate], At[xoTab1,13];*61027b
xoDspWid:*Put on DisplayPage to allow variant implementations
LoadPageExternal[DisplayPage], At[xoTab0,15];*61032b
CSkipData, GoToExternal[dpWidthLoc];
xoMemCfg:
RTemp ← IP[xPageCount]C, At[xoTab1,15];*61033b
StkP ← RTemp;
AC1 ← 100C;*2↑22-word VM = 2↑6 * 2↑16 = 100b banks
T ← Stack;
TtoAC0CSR:
AC0 ← T, CSkipData, Return;
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 ← RZero, 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
xoLRJ:T ← AC0, LoadPage[neLRJPage], At[xoTab0,17];*61036b
LP ← T, CSkipData;
OnPage[neLRJPage];
T ← MDShi;
LPhi ← T;
*Setup PCB for continuation after SoftGo.
T ← PCF.Word;
PCB ← (PCB) + 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 ← (RZero) + (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 Disk.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 - LSh 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];*RSh count > 17b
*Go if RSh count = 1 to 17b.
brLShNeg1:CycleControl ← AC1, CSkipData, GoTo[xoAC0RF,ALU>=0];
AC0 ← 0C, Return;*RSh count = 0, LSh count < -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 ← RZero, 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]; *******************************
%This code seems to be pinned to nePage by the following: (1) the SkipData
at brGetFrame+2; (2) the various CSkipData/SkipData’s which might refill
IBuf (because only nePage has buffer refill at 377b of the page);
(3) The various GoTo[neCS1st]’s.
%
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;*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;*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;
PStore1[AC2,DMA,0], Call[brRTemp1ToT];*Save old frame ptr at [AC2]
PStore1[AC2,AC0,4], Call[brRTemp1ToT];*Save arg1
PFetch1[MDS,AC0];*AC0 ← [[oldAC2+1]] = nargs passed
*PCF now points at the odd byte preceding the jsr @StArgs word
*Point PCF at the even byte of the opcode after jsr @StArgs and refill the
*buffer if necessary.
PCF ← PCF[SkipPCF0], GoTo[.+3,R>=0];
PFetch4[PCB,IBuf,4];
PCB ← (PCB) + (4C);
PStore1[AC2,AC1,5];*Even; Save arg2
T ← (AC0) - (3C), Call[brSA];
brGFlp:PStore1[AC2,xBuf];
RTemp ← (RTemp) - 1;
***This is one of (many) LoadPage usages that prevents LogSE from being used
***because the PStore1 above might cause a fault if LogSE is true.
T ← (RZero) + T + 1, LoadPage[nePage], Skip[ALU>=0];
GoToP[neTask1st];*Exit
PFetch1[DMA,xBuf,1], GoToP[.+1];
OnPage[nePage];
DMA ← (DMA) + 1, Return;*Loop
OnPage[brGarbPage1];
brRTemp1ToT:
T ← RTemp1, Return;
brSA:RTemp ← T, LoadPage[nePage], Skip[ALU>=0];*RTemp ← args to move
GoToP[neTask1st];*<=2 args--exit
T ← (xBuf) + (3C), GoToP[.+3,ALU=0];*Even; skip if exactly 3 args
OnPage[nePage];
PFetch1[DMA,xBuf];*Odd; >3 args
DMA ← T;*Bypass kludge
T ← 6C, Return;*Exactly 3 args
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[Alto];