;----------------------------------------------------------------- ; MesacROM.Mu - Jumps, Load/Store, Read/Write, Binary/Unary/Stack Operators ; Last modified by Levin - November 5, 1979 4:34 PM ;----------------------------------------------------------------- ;----------------------------------------------------------------- ; J u m p s ;----------------------------------------------------------------- ; The following requirements are assumed: ; 1) J2-J9, JB are usable (in that order) as subroutine ; returns (by JEQx and JNEx). ; 2) since J2-J9 and JB are opcode entry points, ; they must meet requirements set by opcode dispatch. ;----------------------------------------------------------------- ; Jn - jump PC-relative ;----------------------------------------------------------------- !1,2,JnA,Jbranchf; J2: L←ONE, :JnA; J3: L←2, :JnA; J4: L←3, :JnA; J5: L←4, :JnA; J6: L←5, :JnA; J7: L←6, :JnA; J8: L←7, :JnA; J9: L←10, :JnA; JnA: L←M-1, :Jbranchf; A-aligned - adjust distance ;----------------------------------------------------------------- ; JB - jump PC-relative by alpha, assuming: ; JB is A-aligned ; Note: JEQB and JNEB come here with branch (1) pending ;----------------------------------------------------------------- !1,1,JBx; shake JEQB/JNEB branch !1,1,Jbranch; must be odd (shakes IR← below) JB: T←ib, :JBx; JBx: L←400 OR T; ←DISP will do sign extension IR←M; 400 above causes branch (1) L←DISP-1, :Jbranch; L: ib (sign extended) - 1 ;----------------------------------------------------------------- ; JW - jump PC-relative by alphabeta, assuming: ; if JW is A-aligned, B byte is irrelevant ; alpha in B byte, beta in A byte of word after JW ;----------------------------------------------------------------- JW: IR←sr1, :FetchAB; returns to JWr JWr: L←ALLONES+T, :Jbranch; L: alphabeta-1 ;----------------------------------------------------------------- ; Jump destination determination ; L has (signed) distance from even byte of word addressed by mpc+1 ;----------------------------------------------------------------- !1,2,Jforward,Jbackward; !1,2,Jeven,Jodd; Jbranch: T←0+1, SH<0; dispatch fwd/bkwd target Jbranchf: SINK←M, BUSODD, TASK, :Jforward; dispatch even/odd target Jforward: temp←L RSH 1, :Jeven; stash positive word offset Jbackward: temp←L MRSH 1, :Jeven; stash negative word offset Jeven: T←temp+1, :NOOP; fetch and execute even byte Jodd: T←temp+1, :nextXB; fetch and execute odd byte ;----------------------------------------------------------------- ; JZEQB - if TOS (popped) = 0, jump PC-relative by alpha, assuming: ; stack has precisely one element ; JZEQB is A-aligned (also ensures no pending branch at entry) ;----------------------------------------------------------------- !1,2,Jcz,Jco; JZEQB: SINK←stk0, BUS=0; test TOS = 0 L←stkp-1, TASK, :Jcz; ;----------------------------------------------------------------- ; JZNEB - if TOS (popped) ~= 0, jump PC-relative by alpha, assuming: ; stack has precisely one element ; JZNEB is A-aligned (also ensures no pending branch at entry) ;----------------------------------------------------------------- !1,2,JZNEBne,JZNEBeq; JZNEB: SINK←stk0, BUS=0; test TOS = 0 L←stkp-1, TASK, :JZNEBne; JZNEBne: stkp←L, :JB; branch, pick up alpha JZNEBeq: stkp←L, :nextA; no branch, alignment => nextA ;----------------------------------------------------------------- ; JEQn - if TOS (popped) = TOS (popped), jump PC-relative by n, assuming: ; stack has precisely two elements ;----------------------------------------------------------------- !1,2,JEQnB,JEQnA; !7,1,JEQNEcom; shake IR← dispatch JEQ2: IR←sr0, L←T, :JEQnB; returns to J2 JEQ3: IR←sr1, L←T, :JEQnB; returns to J3 JEQ4: IR←sr2, L←T, :JEQnB; returns to J4 JEQ5: IR←sr3, L←T, :JEQnB; returns to J5 JEQ6: IR←sr4, L←T, :JEQnB; returns to J6 JEQ7: IR←sr5, L←T, :JEQnB; returns to J7 JEQ8: IR←sr6, L←T, :JEQnB; returns to J8 JEQ9: IR←sr7, L←T, :JEQnB; returns to J9 ;----------------------------------------------------------------- ; JEQB - if TOS (popped) = TOS (popped), jump PC-relative by alpha, assuming: ; stack has precisely two elements ; JEQB is A-aligned (also ensures no pending branch at entry) ;----------------------------------------------------------------- JEQB: IR←sr10, :JEQnA; returns to JB ;----------------------------------------------------------------- ; JEQ common code ;----------------------------------------------------------------- !1,2,JEQcom,JNEcom; return points from JEQNEcom JEQnB: temp←L RSH 1, L←T, :JEQNEcom; temp:0, L:1 (for JEQNEcom) JEQnA: temp←L, L←T, :JEQNEcom; temp:1, L:1 (for JEQNEcom) !1,2,JEQne,JEQeq; JEQcom: L←stkp-T-1, :JEQne; L: old stkp - 2 JEQne: SINK←temp, BUS, TASK, :Setstkp; no jump, reset stkp JEQeq: stkp←L, IDISP, :JEQNExxx; jump, set stkp, then dispatch ; ; JEQ/JNE common code ; ; !7,1,JEQNEcom; appears above with JEQn ; !1,2,JEQcom,JNEcom; appears above with JEQB JEQNEcom: T←stk1; L←stk0-T, SH=0; dispatch EQ/NE T←0+1, SH=0, :JEQcom; test outcome and return JEQNExxx: SINK←temp, BUS, :J2; even/odd dispatch ;----------------------------------------------------------------- ; JNEn - if TOS (popped) ~= TOS (popped), jump PC-relative by n, assuming: ; stack has precisely two elements ;----------------------------------------------------------------- !1,2,JNEnB,JNEnA; JNE2: IR←sr0, L←T, :JNEnB; returns to J2 JNE3: IR←sr1, L←T, :JNEnB; returns to J3 JNE4: IR←sr2, L←T, :JNEnB; returns to J4 JNE5: IR←sr3, L←T, :JNEnB; returns to J5 JNE6: IR←sr4, L←T, :JNEnB; returns to J6 JNE7: IR←sr5, L←T, :JNEnB; returns to J7 JNE8: IR←sr6, L←T, :JNEnB; returns to J8 JNE9: IR←sr7, L←T, :JNEnB; returns to J9 ;----------------------------------------------------------------- ; JNEB - if TOS (popped) = TOS (popped), jump PC-relative by alpha, assuming: ; stack has precisely two elements ; JNEB is A-aligned (also ensures no pending branch at entry) ;----------------------------------------------------------------- JNEB: IR←sr10, :JNEnA; returns to JB ;----------------------------------------------------------------- ; JNE common code ;----------------------------------------------------------------- JNEnB: temp←L RSH 1, L←0, :JEQNEcom; temp:0, L:0 JNEnA: temp←L, L←0, :JEQNEcom; temp:1, L:0 !1,2,JNEne,JNEeq; JNEcom: L←stkp-T-1, :JNEne; L: old stkp - 2 JNEne: stkp←L, IDISP, :JEQNExxx; jump, set stkp, then dispatch JNEeq: SINK←temp, BUS, TASK, :Setstkp; no jump, reset stkp ;----------------------------------------------------------------- ; JrB - for r in {L,LE,G,GE,UL,ULE,UG,UGE} ; if TOS (popped) r TOS (popped), jump PC-relative by alpha, assuming: ; stack has precisely two elements ; JrB is A-aligned (also ensures no pending branch at entry) ;----------------------------------------------------------------- ; The values loaded into IR are not returns but encoded actions: ; Bit 12: 0 => branch if carry zero ; 1 => branch if carry one (mask value: 10) ; Bit 15: 0 => perform add-complement before testing carry ; 1 => perform subtract before testing carry (mask value: 1) ; (These values were chosen because of the masks available for use with ←DISP ; in the existing constants ROM. Note that IR← causes no dispatch.) JLB: IR←10, :Jscale; adc, branch if carry one JLEB: IR←11, :Jscale; sub, branch if carry one JGB: IR←ONE, :Jscale; sub, branch if carry zero JGEB: IR←0, :Jscale; adc, branch if carry zero JULB: IR←10, :Jnoscale; adc, branch if carry one JULEB: IR←11, :Jnoscale; sub, branch if carry one JUGB: IR←ONE, :Jnoscale; sub, branch if carry zero JUGEB: IR←0, :Jnoscale; adc, branch if carry zero ;----------------------------------------------------------------- ; Comparison "subroutine": ;----------------------------------------------------------------- !1,2,Jadc,Jsub; ; !1,2,Jcz,Jco; appears above with JZEQB !1,2,Jnobz,Jbz; !1,2,Jbo,Jnobo; Jscale: T←77777, :Jadjust; Jnoscale: T←ALLONES, :Jadjust; Jadjust: L←stk1+T+1; L:stk1 + (0 or 100000) temp←L; SINK←DISP, BUSODD; dispatch ADC/SUB T←stk0+T+1, :Jadc; Jadc: L←temp-T-1, :Jcommon; perform add complement Jsub: L←temp-T, :Jcommon; perform subtract Jcommon: T←ONE; warning: not T←0+1 L←stkp-T-1, ALUCY; test ADC/SUB outcome SINK←DISP, SINK←lgm10, BUS=0, TASK, :Jcz; dispatch on encoded bit 12 Jcz: stkp←L, :Jnobz; carry is zero (stkp←stkp-2) Jco: stkp←L, :Jbo; carry is one (stkp←stkp-2) Jnobz: L←mpc+1, TASK, :nextAput; no jump, alignment=>nextAa Jbz: T←ib, :JBx; jump Jbo: T←ib, :JBx; jump Jnobo: L←mpc+1, TASK, :nextAput; no jump, alignment=>nextAa ;----------------------------------------------------------------- ; JIW - see Principles of Operation for description ; assumes: ; stack contains precisely two elements ; if JIW is A-aligned, B byte is irrelevant ; alpha in B byte, beta in A byte of word after JIW ;----------------------------------------------------------------- !1,2,JIuge,JIul; !1,1,JIWx; JIW: L←stkp-T-1, TASK, :JIWx; stkp←stkp-2 JIWx: stkp←L; T←stk0; L←XMAR←mpc+1; load alphabeta mpc←L; L←stk1-T-1; do unsigned compare ALUCY; T←MD, :JIuge; JIuge: L←mpc+1, TASK, :nextAput; out of bounds - to 'nextA' JIul: L←cp+T, TASK; (removing this TASK saves a taskhole←L; word, but leaves a run of T←taskhole; 15 instructions) XMAR←stk0+T; fetch <<cp>+alphabeta+X> NOP; L←MD-1, :Jbranch; L: offset ;----------------------------------------------------------------- ; L o a d s ;----------------------------------------------------------------- ; Note: These instructions keep track of their parity ;----------------------------------------------------------------- ; LLn - push <<lp>+n> ; Note: LL3 must be odd! ;----------------------------------------------------------------- ; Note: lp is offset by 2, hence the adjustments below LL0: MAR←lp-T-1, :pushMD; LL1: MAR←lp-1, :pushMD; LL2: MAR←lp, :pushMD; LL3: MAR←lp+T, :pushMD; LL4: MAR←lp+T+1, :pushMD; LL5: T←3, SH=0, :LL3; pick up ball 1 LL6: T←4, SH=0, :LL3; pick up ball 1 LL7: T←5, SH=0, :LL3; pick up ball 1 ;----------------------------------------------------------------- ; LLB - push <<lp>+alpha> ;----------------------------------------------------------------- LLB: IR←sr4, :Getalpha; returns to LLBr LLBr: T←nlpoffset+T+1, SH=0, :LL3; undiddle lp, pick up ball 1 ;----------------------------------------------------------------- ; LLDB - push <<lp>+alpha>, push <<lp>+alpha+1> ; LLDB is A-aligned (also ensures no pending branch at entry) ;----------------------------------------------------------------- LLDB: T←lp, :LDcommon; LDcommon: T←nlpoffset+T+1, :Dpush; ;----------------------------------------------------------------- ; LGn - push <<gp>+n> ; Note: LG2 must be odd! ;----------------------------------------------------------------- ; Note: gp is offset by 1, hence the adjustments below LG0: MAR←gp-1, :pushMD; LG1: MAR←gp, :pushMD; LG2: MAR←gp+T, :pushMD; LG3: MAR←gp+T+1, :pushMD; LG4: T←3, SH=0, :LG2; pick up ball 1 LG5: T←4, SH=0, :LG2; pick up ball 1 LG6: T←5, SH=0, :LG2; pick up ball 1 LG7: T←6, SH=0, :LG2; pick up ball 1 ;----------------------------------------------------------------- ; LGB - push <<gp>+alpha> ;----------------------------------------------------------------- LGB: IR←sr5, :Getalpha; returns to LGBr LGBr: T←ngpoffset+T+1, SH=0, :LG2; undiddle gp, pick up ball 1 ;----------------------------------------------------------------- ; LGDB - push <<gp>+alpha>, push <<gp>+alpha+1> ; LGDB is A-aligned (also ensures no pending branch at entry) ;----------------------------------------------------------------- LGDB: T←gp+T+1, :LDcommon; T: gp-gpoffset+lpoffset ;----------------------------------------------------------------- ; LIn - push n ;----------------------------------------------------------------- !1,2,LI0xB,LI0xA; keep ball 1 in air ; Note: all BUS dispatches use old stkp value, not incremented one LI0: L←stkp+1, BUS, :LI0xB; LI1: L←stkp+1, BUS, :pushT1B; LI2: T←2, :pushTB; LI3: T←3, :pushTB; LI4: T←4, :pushTB; LI5: T←5, :pushTB; LI6: T←6, :pushTB; LI0xB: stkp←L, L←0, TASK, :push0; LI0xA: stkp←L, BUS=0, L←0, TASK, :push0; BUS=0 keeps branch pending ;----------------------------------------------------------------- ; LIN1 - push -1 ;----------------------------------------------------------------- LIN1: T←ALLONES, :pushTB; ;----------------------------------------------------------------- ; LINI - push 100000 ;----------------------------------------------------------------- LINI: T←100000, :pushTB; ;----------------------------------------------------------------- ; LIB - push alpha ;----------------------------------------------------------------- LIB: IR←sr2, :Getalpha; returns to pushTB ; Note: pushT1B will handle ; any pending branch ;----------------------------------------------------------------- ; LINB - push (alpha OR 377B8) ;----------------------------------------------------------------- LINB: IR←sr26, :Getalpha; returns to LINBr LINBr: T←177400 OR T, :pushTB; ;----------------------------------------------------------------- ; LIW - push alphabeta, assuming: ; if LIW is A-aligned, B byte is irrelevant ; alpha in B byte, beta in A byte of word after LIW ;----------------------------------------------------------------- LIW: IR←msr0, :FetchAB; returns to LIWr LIWr: L←stkp+1, BUS, :pushT1A; duplicates pushTA, but ; because of overlapping ; return points, we ; can't use it ;----------------------------------------------------------------- ; S t o r e s ;----------------------------------------------------------------- ;----------------------------------------------------------------- ; SLn - <<lp>+n>←TOS (popped) ; Note: SL3 is odd! ;----------------------------------------------------------------- ; Note: lp is offset by 2, hence the adjustments below SL0: MAR←lp-T-1, :StoreB; SL1: MAR←lp-1, :StoreB; SL2: MAR←lp, :StoreB; SL3: MAR←lp+T, :StoreB; SL4: MAR←lp+T+1, :StoreB; SL5: T←3, SH=0, :SL3; SL6: T←4, SH=0, :SL3; SL7: T←5, SH=0, :SL3; ;----------------------------------------------------------------- ; SLB - <<lp>+alpha>←TOS (popped) ;----------------------------------------------------------------- SLB: IR←sr6, :Getalpha; returns to SLBr SLBr: T←nlpoffset+T+1, SH=0, :SL3; undiddle lp, pick up ball 1 ;----------------------------------------------------------------- ; SLDB - <<lp>+alpha+1>←TOS (popped), <<lp>+alpha>←TOS (popped), assuming: ; SLDB is A-aligned (also ensures no pending branch at entry) ;----------------------------------------------------------------- SLDB: T←lp, :SDcommon; SDcommon: T←nlpoffset+T+1, :Dpop; ;----------------------------------------------------------------- ; SGn - <<gp>+n>←TOS (popped) ; Note: SG2 must be odd! ;----------------------------------------------------------------- ; Note: gp is offset by 1, hence the adjustments below SG0: MAR←gp-1, :StoreB; SG1: MAR←gp, :StoreB; SG2: MAR←gp+T, :StoreB; SG3: MAR←gp+T+1, :StoreB; ;----------------------------------------------------------------- ; SGB - <<gp>+alpha>←TOS (popped) ;----------------------------------------------------------------- SGB: IR←sr7, :Getalpha; returns to SGBr SGBr: T←ngpoffset+T+1, SH=0, :SG2; undiddle gp, pick up ball 1 ;----------------------------------------------------------------- ; SGDB - <<gp>+alpha+1>←TOS (popped), <<gp>+alpha>←TOS (popped), assuming: ; SGDB is A-aligned (also ensures no pending branch at entry) ;----------------------------------------------------------------- SGDB: T←gp+T+1, :SDcommon; T: gp-gpoffset+lpoffset ;----------------------------------------------------------------- ; P u t s ;----------------------------------------------------------------- ;----------------------------------------------------------------- ; PLn - <<lp>+n>←TOS (stack is not popped) ;----------------------------------------------------------------- !1,1,PLcommon; drop ball 1 ; Note: lp is offset by 2, hence the adjustments below PL0: MAR←lp-T-1, SH=0, :PLcommon; pick up ball 1 PL1: MAR←lp-1, SH=0, :PLcommon; PL2: MAR←lp, SH=0, :PLcommon; PL3: MAR←lp+T, SH=0, :PLcommon; PLcommon: L←stkp, BUS, :StoreBa; don't decrement stkp ;----------------------------------------------------------------- ; B i n a r y o p e r a t i o n s ;----------------------------------------------------------------- ; Warning! Before altering this list, be certain you understand the additional addressing ; requirements imposed on some of these return locations! However, it is safe to add new ; return points at the end of the list. !37,40,ADDr,SUBr,ANDr,ORr,XORr,MULr,DIVr,LDIVr,SHIFTr,EXCHr,RSTRr,WSTRr,WSBr,WS0r,WSFr,WFr, WSDBrb,WFSrb,BNDCKr,RWBLrb,WBLrb,,,,,,,,,,,; ;----------------------------------------------------------------- ; Binary operations common code ; Entry conditions: ; Both IR and T hold return number. (More precisely, entry at ; 'BincomB' requires return number in IR, entry at 'BincomA' requires ; return number in T.) ; Exit conditions: ; left operand in L (M), right operand in T ; stkp positioned for subsequent push (i.e. points at left operand) ; dispatch pending (for push0) on return ; if entry occurred at BincomA, IR has been modified so ; that mACSOURCE will produce 1 ;----------------------------------------------------------------- ; dispatches on stkp-1, so Binpop1 = 1 mod 20B !17,20,Binpop,Binpop1,Binpop2,Binpop3,Binpop4,Binpop5,Binpop6,Binpop7,,,,,,,,; !1,2,BincomB,BincomA; !4,1,Bincomx; shake IR← in BincomA BincomB: L←T←stkp-1, :Bincomx; value for dispatch into Binpop Bincomx: stkp←L, L←T; L←M-1, BUS, TASK; L:value for push dispatch Bincomd: temp2←L, :Binpop; stash briefly BincomA: L←2000 OR T; make mACSOURCE produce 1 Binpop: IR←M, :BincomB; Binpop1: T←stk1; L←stk0, :Binend; Binpop2: T←stk2; L←stk1, :Binend; Binpop3: T←stk3; L←stk2, :Binend; Binpop4: T←stk4; L←stk3, :Binend; Binpop5: T←stk5; L←stk4, :Binend; Binpop6: T←stk6; L←stk5, :Binend; Binpop7: T←stk7; L←stk6, :Binend; Binend: SINK←DISP, BUS; perform return dispatch SINK←temp2, BUS, :ADDr; perform push dispatch ;----------------------------------------------------------------- ; ADD - replace <TOS> with sum of top two stack elements ;----------------------------------------------------------------- ADD: IR←T←ret0, :BincomB; ADDr: L←M+T, mACSOURCE, TASK, :push0; M addressing unaffected ;----------------------------------------------------------------- ; ADD01 - replace stk0 with <stk0>+<stk1> ;----------------------------------------------------------------- !1,1,ADD01x; drop ball 1 ADD01: T←stk1-1, :ADD01x; ADD01x: T←stk0+T+1, SH=0; pick up ball 1 L←stkp-1, :pushT1B; no dispatch => to push0 ;----------------------------------------------------------------- ; SUB - replace <TOS> with difference of top two stack elements ;----------------------------------------------------------------- SUB: IR←T←ret1, :BincomB; SUBr: L←M-T, mACSOURCE, TASK, :push0; M addressing unaffected ;----------------------------------------------------------------- ; AND - replace <TOS> with AND of top two stack elements ;----------------------------------------------------------------- AND: IR←T←ret2, :BincomB; ANDr: L←M AND T, mACSOURCE, TASK, :push0; M addressing unaffected ;----------------------------------------------------------------- ; OR - replace <TOS> with OR of top two stack elements ;----------------------------------------------------------------- OR: IR←T←ret3, :BincomB; ORr: L←M OR T, mACSOURCE, TASK, :push0; M addressing unaffected ;----------------------------------------------------------------- ; XOR - replace <TOS> with XOR of top two stack elements ;----------------------------------------------------------------- XOR: IR←T←ret4, :BincomB; XORr: L←M XOR T, mACSOURCE, TASK, :push0; M addressing unaffected ;----------------------------------------------------------------- ; MUL - replace <TOS> with product of top two stack elements ; high-order bits of product recoverable by PUSH ;----------------------------------------------------------------- !7,1,MULDIVcoma; shakes stack dispatch !1,2,GoROMMUL,GoROMDIV; !7,2,MULx,DIVx; also shakes bus dispatch MUL: IR←T←ret5, :BincomB; MULr: AC1←L, L←T, :MULDIVcoma; stash multiplicand MULDIVcoma: AC2←L, L←0, :MULx; stash multiplier or divisor MULx: AC0←L, T←0, :MULDIVcomb; AC0←0 keeps ROM happy DIVx: AC0←L, T←0+1, BUS=0, :MULDIVcomb; BUS=0 => GoROMDIV MULDIVcomb: L←MULDIVretloc-T-1, SWMODE, :GoROMMUL; prepare return address GoROMMUL: PC←L, :ROMMUL; go to ROM multiply GoROMDIV: PC←L, :ROMDIV; go to ROM divide MULDIVret: :MULDIVret1; No divide - someday a trap ; perhaps, but garbage now. MULDIVret1: T←AC1; Normal return L←stkp+1; L←T, SINK←M, BUS; T←AC0, :dpush; Note! not a subroutine ; call, but a direct ; dispatch. ;----------------------------------------------------------------- ; DIV - push quotient of top two stack elements (popped) ; remainder recoverable by PUSH ;----------------------------------------------------------------- DIV: IR←T←ret6, :BincomB; DIVr: AC1←L, L←T, BUS=0, :MULDIVcoma; BUS=0 => DIVx ;----------------------------------------------------------------- ; LDIV - push quotient of <TOS-1>,,<TOS-2>/<TOS> (all popped) ; remainder recoverable by PUSH ;----------------------------------------------------------------- LDIV: IR←sr27, :Popsub; get divisor LDIVf: AC2←L; stash it IR←T←ret7, :BincomB; L:low bits, T:high bits LDIVr: AC1←L, L←T, IR←0, :DIVx; stash low part of dividend ; and ensure mACSOURCE of 0. ;----------------------------------------------------------------- ; SHIFT - replace <TOS> with <TOS-1> shifted by <TOS> ; <TOS> > 0 => left shift, <TOS> < 0 => right shift ;----------------------------------------------------------------- !7,1,SHIFTx; shakes stack dispatch !1,2,Lshift,Rshift; !1,2,DoShift,Shiftdone; !1,2,DoRight,DoLeft; !1,1,Shiftdonex; SHIFT: IR←T←ret10, :BincomB; SHIFTr: temp←L, L←T, TASK, :SHIFTx; L: value, T: count SHIFTx: count←L; L←T←count; L←0-T, SH<0; L: -count, T: count IR←sr1, :Lshift; IR← causes no branch Lshift: L←37 AND T, TASK, :Shiftcom; mask to reasonable size Rshift: T←37, IR←37; equivalent to IR←msr0 L←M AND T, TASK, :Shiftcom; mask to reasonable size Shiftcom: count←L, :Shiftloop; Shiftloop: L←count-1, BUS=0; test for completion count←L, IDISP, :DoShift; DoShift: L←temp, TASK, :DoRight; DoRight: temp←L RSH 1, :Shiftloop; DoLeft: temp←L LSH 1, :Shiftloop; Shiftdone: SINK←temp2, BUS, :Shiftdonex; dispatch to push result Shiftdonex: L←temp, TASK, :push0; ;----------------------------------------------------------------- ; D o u b l e - P r e c i s i o n A r i t h m e t i c ;----------------------------------------------------------------- ;----------------------------------------------------------------- ; DADD - add two double-word quantities, assuming: ; stack contains precisely 4 elements ;----------------------------------------------------------------- !1,1,DoRamDoubles; shake B/A dispatch DADD: L←4, SWMODE, :DoRamDoubles; drop ball 1 DoRamDoubles: SINK←M, BUS, TASK, :ramOverflow; go to overflow code in RAM ;----------------------------------------------------------------- ; DSUB - subtract two double-word quantities, assuming: ; stack contains precisely 4 elements ;----------------------------------------------------------------- DSUB: L←5, SWMODE, :DoRamDoubles; drop ball 1 ;----------------------------------------------------------------- ; DCOMP - compare two long integers, assuming: ; stack contains precisely 4 elements ; result left on stack is -1, 0, or +1 (single-precision) ; (i.e. result = sign(stk1,,stk0 DSUB stk3,,stk2) ) ;----------------------------------------------------------------- DCOMP: L←6, SWMODE, :DoRamDoubles; drop ball 1 ;----------------------------------------------------------------- ; DUCOMP - compare two long cardinals, assuming: ; stack contains precisely 4 elements ; result left on stack is -1, 0, or +1 (single-precision) ; (i.e. result = sign(stk1,,stk0 DSUB stk3,,stk2) ) ;----------------------------------------------------------------- DUCOMP: L←7, SWMODE, :DoRamDoubles; drop ball 1 ;----------------------------------------------------------------- ; R a n g e C h e c k i n g ;----------------------------------------------------------------- ;----------------------------------------------------------------- ; NILCK - check TOS for NIL (0), trap if so ;----------------------------------------------------------------- !1,2,InRange,OutOfRange; NILCK: L←ret17, :Xpopsub; returns to NILCKr NILCKr: T←ONE, SH=0, :NILCKpush; test TOS=0 NILCKpush: L←stkp+T, :InRange; InRange: SINK←ib, BUS=0, TASK, :Setstkp; pick up ball 1 OutOfRange: T←sBoundsFaultm1+T+1, :KFCr; T:SD index; go trap ;----------------------------------------------------------------- ; BNDCK - check subrange inclusion ; if TOS-1 ~IN [0..TOS) then trap (test is unsigned) ; only TOS is popped off ;----------------------------------------------------------------- !7,1,BNDCKx; shake push dispatch BNDCK: IR←T←ret22, :BincomB; returns to BNDCKr BNDCKr: L←M-T, :BNDCKx; L: value, T: limit BNDCKx: T←0, ALUCY, :NILCKpush; ;----------------------------------------------------------------- ; R e a d s ;----------------------------------------------------------------- ; Note: RBr must be odd! ;----------------------------------------------------------------- ; Rn - TOS←<<TOS>+n> ;----------------------------------------------------------------- R0: T←0, SH=0, :RBr; R1: T←ONE, SH=0, :RBr; R2: T←2, SH=0, :RBr; R3: T←3, SH=0, :RBr; R4: T←4, SH=0, :RBr; ;----------------------------------------------------------------- ; RB - TOS←<<TOS>+alpha>, assuming: ;----------------------------------------------------------------- !1,2,ReadB,ReadA; keep ball 1 in air RB: IR←sr15, :Getalpha; returns to RBr RBr: L←stkp-1, BUS, :ReadB; ReadB: stkp←L, :MAStkT; to pushMD ReadA: stkp←L, BUS=0, :MAStkT; to pushMDA ;----------------------------------------------------------------- ; RDB - temp←<TOS>+alpha, push <<temp>>, push <<temp>+1>, assuming: ; RDB is A-aligned (also ensures no pending branch at entry) ;----------------------------------------------------------------- RDB: IR←sr30, :Popsub; returns to Dpush ;----------------------------------------------------------------- ; RD0 - temp←<TOS>, push <<temp>>, push <<temp>+1> ;----------------------------------------------------------------- RD0: IR←sr32, :Popsub; returns to RD0r RD0r: L←0, :Dpusha; ;----------------------------------------------------------------- ; RILP - push <<<lp>+alpha[0-3]>+alpha[4-7]> ;----------------------------------------------------------------- RILP: L←ret0, :Splitalpha; get two 4-bit values RILPr: T←lp, :RIPcom; T:address of local 2 ;----------------------------------------------------------------- ; RIGP - push <<<gp>+alpha[0-3]>+alpha[4-7]> ;----------------------------------------------------------------- !3,1,IPcom; shake IR← at WILPr RIGP: L←ret1, :Splitalpha; get two 4-bit values RIGPr: T←gp+1, :RIPcom; T:address of global 2 RIPcom: IR←msr0, :IPcom; set up return to pushMD IPcom: T←-3+T+1; T:address of local or global 0 MAR←lefthalf+T; start memory cycle L←righthalf; IPcomx: T←MD, IDISP; T:local/global value MAR←M+T, :pushMD; start fetch/store ;----------------------------------------------------------------- ; RIL0 - push <<<lp>>> ;----------------------------------------------------------------- !1,2,RILxB,RILxA; RIL0: MAR←lp-T-1, :RILxB; fetch local 0 RILxB: IR←msr0, L←0, :IPcomx; to pushMD RILxA: IR←sr1, L←sr1 AND T, :IPcomx; to pushMDA, L←0(!) ;----------------------------------------------------------------- ; RXLP - TOS←<<TOS>+<<lp>+alpha[0-3]>+alpha[4-7]> ;----------------------------------------------------------------- RXLP: L←ret3, :Splitalpha; will return to RXLPra RXLPra: IR←sr34, :Popsub; fetch TOS RXLPrb: L←righthalf+T, TASK; L:TOS+alpha[4-7] righthalf←L, :RILPr; now act like RILP ;----------------------------------------------------------------- ; W r i t e s ;----------------------------------------------------------------- ;----------------------------------------------------------------- ; Wn - <<TOS> (popped)+n>←<TOS> (popped) ;----------------------------------------------------------------- !1,2,WnB,WnA; keep ball 1 in air W0: T←0, :WnB; W1: T←ONE, :WnB; W2: T←2, :WnB; WnB: IR←sr2, :Wsub; returns to StoreB WnA: IR←sr3, :Wsub; returns to StoreA ;----------------------------------------------------------------- ; Write subroutine: ;----------------------------------------------------------------- !7,1,Wsubx; shake IR← dispatch Wsub: L←stkp-1, BUS, :Wsubx; Wsubx: stkp←L, IDISP, :MAStkT; ;----------------------------------------------------------------- ; WB - <<TOS> (popped)+alpha>←<TOS-1> (popped) ;----------------------------------------------------------------- WB: IR←sr16, :Getalpha; returns to WBr WBr: :WnB; branch may be pending ;----------------------------------------------------------------- ; WSB - act like WB but with stack values reversed, assuming: ; WSB is A-aligned (also ensures no pending branch at entry) ;----------------------------------------------------------------- !7,1,WSBx; shake stack dispatch WSB: IR←T←ret14, :BincomA; alignment requires BincomA WSBr: T←M, L←T, :WSBx; WSBx: MAR←ib+T, :WScom; WScom: temp←L; WScoma: L←stkp-1; MD←temp; mACSOURCE, TASK, :Setstkp; ;----------------------------------------------------------------- ; WS0 - act like WSB but with alpha value of zero ;----------------------------------------------------------------- !7,1,WS0x; shake stack dispatch WS0: IR←T←ret15, :BincomB; WS0r: T←M, L←T, :WS0x; WS0x: MAR←T, :WScom; ;----------------------------------------------------------------- ; WILP - <<lp>+alpha[0-3]>+alpha[4-7] ← <TOS> (popped) ;----------------------------------------------------------------- WILP: L←ret2, :Splitalpha; get halves of alpha WILPr: IR←sr2; IPcom will exit to StoreB T←lp, :IPcom; prepare to undiddle ;----------------------------------------------------------------- ; WXLP - <TOS>+<<lp>+alpha[0-3]>+alpha[4-7] ← <TOS-1> (both popped) ;----------------------------------------------------------------- WXLP: L←ret4, :Splitalpha; get halves of alpha WXLPra: IR←sr35, :Popsub; fetch TOS WXLPrb: L←righthalf+T, TASK; L:TOS+alpha[4-7] righthalf←L, :WILPr; now act like WILP ;----------------------------------------------------------------- ; WDB - temp←alpha+<TOS> (popped), pop into <temp>+1 and <temp>, assuming: ; WDB is A-aligned (also ensures no pending branch at entry) ;----------------------------------------------------------------- WDB: IR←sr31, :Popsub; returns to Dpop ;----------------------------------------------------------------- ; WD0 - temp←<TOS> (popped), pop into <temp>+1 and <temp> ;----------------------------------------------------------------- WD0: L←ret6, TASK, :Xpopsub; returns to WD0r WD0r: L←0, :Dpopa; ;----------------------------------------------------------------- ; WSDB - like WDB but with address below data words, assuming: ; WSDB is A-aligned (also ensures no pending branch at entry) ;----------------------------------------------------------------- !7,1,WSDBx; WSDB: IR←sr24, :Popsub; get low data word WSDBra: saveret←L; stash it briefly IR←T←ret20, :BincomA; alignment requires BincomA WSDBrb: T←M, L←T, :WSDBx; L:high data, T:address WSDBx: MAR←T←ib+T+1; start store of low data word temp←L, L←T; temp:high data temp2←L, TASK; temp2:updated address MD←saveret; stash low data word MAR←temp2-1, :WScoma; start store of high data word ;----------------------------------------------------------------- ; L o n g P o i n t e r o p e r a t i o n s ;----------------------------------------------------------------- !1,1,RWBLcom; drop ball 1 ;----------------------------------------------------------------- ; RBL - like RB, but uses a long pointer ;----------------------------------------------------------------- RBL: L←M AND NOT T, T←M, SH=0, :RWBLcom; L: ret0, T: L at entry ;----------------------------------------------------------------- ; WBL - like WB, but uses a long pointer ;----------------------------------------------------------------- WBL: L←T, T←M, SH=0, :RWBLcom; L: ret1, T: L at entry ; ; Common long pointer code ; !1,2,RWBLcomB,RWBLcomA; !1,1,RWBLxa; drop ball 1 !7,1,RWBLxb; shake stkp dispatch !7,1,WBLx; shake stkp dispatch !3,4,RBLra,WBLra,WBLrc,; !3,4,RWBLdone,RBLdone,,WBLdone; RWBLcom: entry←L, L←T, :RWBLcomB; stash return, restore L RWBLcomB: IR←sr37, :Getalpha; RWBLcomA: IR←sr37, :GetalphaA; RWBLra: IR←ret23, L←T, :RWBLxa; L: alpha byte RWBLxa: alpha←L, :BincomB; stash alpha, get long pointer RWBLrb: MAR←BankReg, :RWBLxb; fetch bank register RWBLxb: L←T, T←M; T: low half, L: high half temp←L; temp: high pointer L←alpha+T; L: low pointer+alpha T←MD; T: bank register to save MAR←BankReg; reaccess bank register frame←L, L←T; frame: pointer taskhole←L, TASK; taskhole: old bank register MD←temp, :WBLx; set new alternate bank value WBLx: XMAR←frame; start memory access L←entry+1, BUS; dispatch RBL/WBL entry←L, L←T, :RBLra; (L←T for WBLrc only) RBLra: T←MD, :RWBLtail; T: data from memory WBLra: IR←ret24, :BincomB; returns to WBLrb WBLrb: T←M, :WBLx; T: data to write WBLrc: MD←M, :RWBLtail; stash data in memory RWBLtail: MAR←BankReg; SINK←entry, BUS; dispatch return RWBLdone: MD←taskhole, :RWBLdone; restore bank register RBLdone: L←temp2+1, BUS, :pushT1B; temp2: original stkp-2 WBLdone: L←temp2, TASK, :Setstkp; temp2: original stkp-3 ;----------------------------------------------------------------- ; U n a r y o p e r a t i o n s ;----------------------------------------------------------------- ; XMESA Note: Untail is wired down by a pre-def in MesaROM.mu ;----------------------------------------------------------------- ; INC - TOS ← <TOS>+1 ;----------------------------------------------------------------- INC: IR←sr14, :Popsub; INCr: T←0+T+1, :pushTB; ;----------------------------------------------------------------- ; NEG - TOS ← -<TOS> ;----------------------------------------------------------------- NEG: L←ret11, TASK, :Xpopsub; NEGr: L←0-T, :Untail; ;----------------------------------------------------------------- ; DBL - TOS ← 2*<TOS> ;----------------------------------------------------------------- DBL: IR←sr25, :Popsub; DBLr: L←M+T, :Untail; ;----------------------------------------------------------------- ; Unary operation common code ;----------------------------------------------------------------- Untail: T←M, :pushTB; ;----------------------------------------------------------------- ; S t a c k a n d M i s c e l l a n e o u s O p e r a t i o n s ;----------------------------------------------------------------- ;----------------------------------------------------------------- ; PUSH - add 1 to stack pointer ;----------------------------------------------------------------- !1,1,PUSHx; PUSH: L←stkp+1, BUS, :PUSHx; BUS checks for overflow PUSHx: SINK←ib, BUS=0, TASK, :Setstkp; pick up ball 1 ;----------------------------------------------------------------- ; POP - subtract 1 from stack pointer ;----------------------------------------------------------------- POP: L←stkp-1, SH=0, TASK, :Setstkp; L=0 <=> branch 1 pending ; need not check stkp=0 ;----------------------------------------------------------------- ; DUP - temp←<TOS> (popped), push <temp>, push <temp> ;----------------------------------------------------------------- !1,1,DUPx; DUP: IR←sr2, :DUPx; returns to pushTB DUPx: L←stkp, BUS, TASK, :Popsuba; don't pop stack ;----------------------------------------------------------------- ; EXCH - exchange top two stack elements ;----------------------------------------------------------------- !1,1,EXCHx; drop ball 1 EXCH: IR←ret11, :EXCHx; EXCHx: L←stkp-1; dispatch on stkp-1 L←M+1, BUS, TASK, :Bincomd; set temp2←stkp EXCHr: T←M, L←T, :dpush; Note: dispatch using temp2 ;----------------------------------------------------------------- ; LADRB - push alpha+lp (undiddled) ;----------------------------------------------------------------- !1,1,LADRBx; shake branch from Getalpha LADRB: IR←sr10, :Getalpha; returns to LADRBr LADRBr: T←nlpoffset+T+1, :LADRBx; LADRBx: L←lp+T, :Untail; ;----------------------------------------------------------------- ; GADRB - push alpha+gp (undiddled) ;----------------------------------------------------------------- !1,1,GADRBx; shake branch from Getalpha GADRB: IR←sr11, :Getalpha; returns to GADRBr GADRBr: T←ngpoffset+T+1, :GADRBx; GADRBx: L←gp+T, :Untail; ;----------------------------------------------------------------- ; S t r i n g O p e r a t i o n s ;----------------------------------------------------------------- !7,1,STRsub; shake stack dispatch !1,2,STRsubA,STRsubB; !1,2,RSTRrx,WSTRrx; STRsub: L←stkp-1; update stack pointer stkp←L; L←ib+T; compute index and offset SINK←M, BUSODD, TASK; count←L RSH 1, :STRsubA; STRsubA: L←177400, :STRsubcom; left byte STRsubB: L←377, :STRsubcom; right byte STRsubcom: T←temp; get string address MAR←count+T; start fetch of word T←M; move mask to more useful place SINK←DISP, BUSODD; dispatch to caller mask←L, SH<0, :RSTRrx; dispatch B/A, mask for WSTR ;----------------------------------------------------------------- ; RSTR - push byte of string using base (<TOS-1>) and index (<TOS>) ; assumes RSTR is A-aligned (no pending branch at entry) ;----------------------------------------------------------------- !1,2,RSTRB,RSTRA; RSTR: IR←T←ret12, :BincomB; RSTRr: temp←L, :STRsub; stash string base address RSTRrx: L←MD AND T, TASK, :RSTRB; isolate good bits RSTRB: temp←L, :RSTRcom; RSTRA: temp←L LCY 8, :RSTRcom; right-justify byte RSTRcom: T←temp, :pushTA; go push result byte ;----------------------------------------------------------------- ; WSTR - pop <TOS-2> into string byte using base (<TOS-1>) and index (<TOS>) ; assumes WSTR is A-aligned (no pending branch at entry) ;----------------------------------------------------------------- !1,2,WSTRB,WSTRA; WSTR: IR←T←ret13, :BincomB; WSTRr: temp←L, :STRsub; stash string base WSTRrx: L←MD AND NOT T, :WSTRB; isolate good bits WSTRB: temp2←L, L←ret0, TASK, :Xpopsub; stash them, return to WSTRrB WSTRA: temp2←L, L←ret0+1, TASK, :Xpopsub; stash them, return to WSTRrA WSTRrA: taskhole←L LCY 8; move new data to odd byte T←taskhole, :WSTRrB; WSTRrB: T←mask.T; L←temp2 OR T; T←temp; retrieve string address MAR←count+T; TASK; MD←M, :nextA; ;----------------------------------------------------------------- ; F i e l d I n s t r u c t i o n s ;----------------------------------------------------------------- ; temp2 is coded as follows: ; 0 - RF, RFS ; 1 - WF, WSF, WFS ; 2 - RFC %1,3,2,RFrr,WFrr; returns from Fieldsub !7,1,Fieldsub; shakes stack dispatch ; !7,1,WFr; (required by WSFr) is implicit in ret17 (!) ;----------------------------------------------------------------- ; RF - push field specified by beta in word at <TOS> (popped) + alpha ; if RF is A-aligned, B byte is irrelevant ; alpha in B byte, beta in A byte of word after RF ;----------------------------------------------------------------- RF: IR←sr12, :Popsub; RFr: L←ret0, :Fieldsub; RFrr: T←mask.T, :pushTA; alignment requires pushTA ;----------------------------------------------------------------- ; WF - pop data in <TOS-1> into field specified by beta in word at <TOS> (popped) + alpha ; if WF is A-aligned, B byte is irrelevant ; alpha in B byte, beta in A byte of word after WF ;----------------------------------------------------------------- ; !1,2,WFnzct,WFret; - see location-specific definitions WF: IR←T←ret17, :BincomB; L:new data, T:address WFr: newfield←L, L←ret0+1, :Fieldsub; (actually, L←ret1) WFrr: T←mask; L←M AND NOT T; set old field bits to zero temp←L; stash result T←newfield.T; save new field bits L←temp OR T, TASK; merge old and new CYCOUT←L; stash briefly T←index, BUS=0; get position, test for zero L←WFretloc, :WFnzct; get return address from ROM WFnzct: PC←L; stash return L←20-T, SWMODE; L:remaining count to cycle T←CYCOUT, :RAMCYCX; go cycle remaining amount WFret: MAR←frame; start memory L←stkp-1; pop remaining word MD←CYCOUT, TASK, :JZNEBeq; stash data, go update stkp ;----------------------------------------------------------------- ; WSF - like WF, but with top two stack elements reversed ; if WSF is A-aligned, B byte is irrelevant ; alpha in B byte, beta in A byte of word after WSF ;----------------------------------------------------------------- WSF: IR←T←ret16, :BincomB; L:address, T:new data WSFr: L←T, T←M, :WFr; ;----------------------------------------------------------------- ; RFS - like RF, but with a word containing alpha and beta on top of stack ; if RFS is A-aligned, B byte is irrelevant ;----------------------------------------------------------------- RFS: L←ret12, TASK, :Xpopsub; get alpha and beta RFSra: temp←L; stash for WFSa L←ret13, TASK, :Xpopsub; T:address RFSrb: L←ret0, BUS=0, :Fieldsub; returns quickly to WFSa ;----------------------------------------------------------------- ; WFS - like WF, but with a word containing alpha and beta on top of stack ; if WFS is A-aligned, B byte is irrelevant ;----------------------------------------------------------------- !1,2,Fieldsuba,WFSa; WFS: L←ret14, TASK, :Xpopsub; get alpha and beta WFSra: temp←L; stash temporarily IR←T←ret21, :BincomB; L:new data, T:address WFSrb: newfield←L, L←ret0+1, BUS=0, :Fieldsub; returns quickly to WFSa WFSa: frame←L; stash address T←177400; to separate alpha and beta L←temp AND T, T←temp, :Getalphab; L:alpha, T:both ; returns to Fieldra ;----------------------------------------------------------------- ; RFC - like RF, but uses <cp>+<alpha>+<TOS> as address ; if RFC is A-aligned, B byte is irrelevant ; alpha in B byte, beta in A byte of word after RF ;----------------------------------------------------------------- RFC: L←ret16, TASK, :Xpopsub; get index into code segment RFCr: L←cp+T; T←M; T:address L←ret2, :Fieldsub; returns to RFrr ;----------------------------------------------------------------- ; Field instructions common code ; Entry conditions: ; L holds return offset ; T holds base address ; Exit conditions: ; mask: right-justified mask ; frame: updated address, including alpha ; index: left cycles needed to right-justify field [0-15] ; L,T: data word from location <frame> cycled left <index> bits ;----------------------------------------------------------------- %2,3,1,NotCodeSeg,IsCodeSeg; Fieldsub: temp2←L, L←T, IR←msr0, TASK, :Fieldsuba; stash return Fieldsuba: frame←L, :GetalphaA; stash base address ; T: beta, ib: alpha Fieldra: L←ret5; saveret←L, :Splitcomr; get two halves of beta Fieldrb: T←righthalf; index for MASKTAB MAR←MASKTAB+T; start fetch of mask T←lefthalf+T+1; L:left-cycle count L←17 AND T; mask to 4 bits index←L; stash position L←MD, TASK; L:mask for caller's use mask←L; stash mask SINK←temp2, BUS; temp2=2 <=> RFC T←frame, :NotCodeSeg; get base address NotCodeSeg: L←MAR←ib+T, :StashFieldLoc; add alpha IsCodeSeg: XMAR←ib+T, :DoCycle; add alpha StashFieldLoc: frame←L, :DoCycle; stash updated address for WF DoCycle: L←Fieldretloc; return location from RAMCYCX PC←L; T←MD, SWMODE; data word into T for cycle L←index, :RAMCYCX; count to cycle, go do it Fieldrc: SINK←temp2, BUSODD; return dispatch L←T←CYCOUT, :RFrr; cycled data word in L and T