: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]ExtendedOpcodes.Press) 60000 CYCLE Left Rotate 61000 DIR Disable interrupts 61001 EIR Enable interrupts 61002 BRI Branch and Return from interrupt 61003 RCLK Read Clock 61004 SIO Start I/O 61005 BLT Block Transfer 61006 BLKS Block Store 61007 *SIT Start Interval Timer - NOP 61010 JMPRAM Jump to RAM - only works if AC1 = 420 (enters Mesa) 61011 RDRAM Read RAM - Returns -1 61012 WRTRAM Write RAM - NOP 61013 DIRS Disable interrupts and skip if on 61014 VERS Version - AC0 _ 40000C 61015 *DREAD AC0_$AC3; AC1_$(AC3 xor 1) (Alto 2 n.i.) 61016 *DWRITE $AC3_AC0; $(AC3 xor 1)_AC1 (Alto 2 n.i.) 61017 *DEXCH Double-word exchange (Alto 2 n.i.) 61020 MUL Unsigned Multiply 61021 DIV Unsigned Divide 61022 WPRINTER Printer_AC0 (Alto 2 DIAGNOSE1 n.i.) 61023 RPRINTER AC0_Printer (Alto 2 DIAGNOSE2 n.i.) 61024 BITBLT Bit Block Transfer 61025 XMLDA Loads AC0 from the AC1th alternate bank location 61026 XMSTA Stores 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 61027 DSPRATE Accepts new end-of-field fill in AC0 and returns old value in AC0. 213b selects 60 Hz, 11b is normal 77 Hz. 61030 --- 61031 --- 61032 DSPWID AC0 = -1 makes full display width available; = 0 reverts to standard Alto-size picture. Width returned in AC0. 61033 MEMCFG Returns no. of 400b-word pages of storage in AC0, number of 64k-word banks of VM in AC1. 61034 POWOFF Power off machine 61035 CSUM Compute ethernet-style checksum 61036 LOADRAM load microstore from memory and optional start 61037 SDP SetDefaultPartition 61040 IOB Input (Dorado only) 61041 IOB Output (Dorado only) 61042 Halt (n.i.) 61043 Turn on PC sampling (Dorado only) 61044 General Input (Dorado only) 61045 General Output (Dorado only) 64400 JSRII Jump to subroutine, double indirect, pc relative 65000 JSRIS Jump to subroutine, double indirect, ac2 relative 67000 CONVERT Scan conversion of characters NOTE: 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 0 NWRDS -- number of words per scanline (< 200b) word 1 dba -- minus dest bit addr mod 20b ac3: pointer to word xh of the character descriptor block Character Descriptor Block: word 0: xh-1 bit map for character word xh: xw -- (2*width) + 1, or (2*pseudochar) if extension required word xh+1: hd,,xh -- (scan lines to skip),,(height of bit map) % Convertp: 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: AC0 address of first source word - 1 (BLT), or data to be stored (BLKS) AC1 address of last destination word (= destination+word count-1) AC2 unused AC3 negative word count % :IF[neFastBltBlks]; **************************** *Fast versions moving quadwords *Average timing for BLT's longer than 8 words ~= 100 + 7.25*nwords cycles, *assuming random alignment of source and destination and non-overlap case. 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: 1 test loop count for done 2 test next mpr bit (AC1[17]), RSh AC1 2a if 1: AC0 _ AC0 + T 3a RSh AC0, or'ing 100000 into AC0 if add carried 2b if 0: RSh AC0 4a,3b or 100000 into AC1 if shifted a 1 out of AC0 % xoMul: T _ AC2, LoadPage[neMulPage], At[xoTab0,10]; *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];e6(1792)\31360f1 6f0 43f1 2f0