*----------------------------------------------------------- Title[DMesaMiscOps.mc...October 26, 1983 12:48 PM...Taft]; * MISC entry point; miscellaneous MISC opcodes -- groups 000, 100, and 240. *----------------------------------------------------------- % CONTENTS, by order of alpha byte value Defined on both Dorado and Dolphin: alpha=0 Associate alpha=1 SetFlags alpha=2 (read ram unimplemented) alpha=3 LoadRam alpha=4 (IOReset unimplemented) alpha=5 Input alpha=6 Output alpha=7 Checksum alpha=10b Set maintenance panel (PrincOps only) alpha=11b ReadClock alpha=12b GenIOin alpha=13b GenIOout (diablo interface) alpha=14b (Xfer Long de-implemented) alpha=15b (TextBlt unimplemented) (was Write MDS for all tasks -- de-implemented) alpha=16b GetFlags alpha=17b Set default disk partition (Alto only) alpha=100b LocalBlkZ alpha=102b LongBlkZ alpha=104b Version (implemented in DMesaVersion.mc) Special operations defined on Dorado only: alpha=240b Zero and Enable Event counters alpha=241b Read Event counters alpha=242b Stop Event counters alpha=243b Set PC Histogram Address alpha=244b Unused alpha=245b Unused alpha=246b Read/write muffler/manifold system alpha=247b Reset Ethernet hardware and tasks (PrincOps only) alpha=250B Unused (used to be Boot; PrincOps only) alpha=251B Get size of real and virtual memory alpha=252B Halt Dorado (for timed power-off) alpha=253B Set display field rate alpha=254b Reset disk hardware and task (PrincOps only) alpha=255b Set interval timer (PrincOps only) alpha=256b Stable Storage block input alpha=257b Stable Storage block output % TopLevel; *----------------------------------------------------------- IFUR[MISC, 2, MDS]; * Do miscellaneous operation alpha *----------------------------------------------------------- T← (2(ID)) LSH 1, Branch[MISCM1]; :IfMEP; T← (2(ID)) LSH 1, Stack← MD, Branch[MISCM1]; T← (2(ID)) LSH 1, StkP+1; :If[AltoMode]; ********** Alto version ********** T← T+1; * Remember entry point 2 :EndIf; ********************************** :EndIf; MISCM1: RTemp1← LDF[T, 4, 6]; BigBDispatch← RTemp1; :If[AltoMode]; ********** Alto version ********** OTPReg← BigBDispatch← T, Branch[MiscDispatch]; :Else; ******** PrincOps version ******** BigBDispatch← T, Branch[MiscDispatch]; :EndIf; ********************************** * Upon dispatch, T contains TOS (if there is one) and StkP has been advanced * in preparation for pushing a result. Routines that do not push a result * must decrement StkP. * Alto mode: OTPReg contains 4*alpha if entry point 0 or 1 was * used, 4*alpha+1 if entry point 2 (used by floating point trap handler). * Spread the entry points 4 apart and put them at odd locations. * This is so that the first instruction can do a Call if desired, and so that * call locations and some conditional branch targets are preserved. * alpha=0, 1, ... 77 => dispatch 1, 5, ... 375 into MiscTable0, * alpha=100, 101, ... 177 => dispatch 1, 5, ... 375 into MiscTable1, etc. *----------------------------------------------------------- * Some definitions for Mesa's record that holds a page number and map flags *----------------------------------------------------------- * MapEntry: TYPE = MACHINE DEPENDENT RECORD [ * unused, wProtect, dirty, ref: BOOLEAN, realPage: [0..7777B]]; MC[MesaF&P.wProtect, 40000]; MC[MesaF&P.dirty, 20000]; MC[MesaF&P.ref, 10000]; MC[MesaF&P.wProtect&dirty, MesaF&P.wProtect, MesaF&P.dirty]; MC[MesaF&P.flagsMask, 70000]; MC[MesaF&P.pageMask, 7777]; * Mesa defines vacant as wProtect and dirty and ref'. * Unfortunately, the Dorado defines vacant as wProtect and dirty, * without regard to ref. * To get around this problem, we take over one of the two high-order * bits of the real page number, which do not participate in storage * addressing unless 256K storage chips are installed (unlikely * during the lifetime of the Dorado). * When Mesa asks us to set the combination wProtect and dirty and ref * (which is NOT vacant by Mesa's definition), we instead set * wProtect and dirty' and ref, and we set the extra bit. * When Mesa asks to read the flags, we or together the hardware dirty * bit and the extra bit. *----------------------------------------------------------- Associate: MiscTable[0], * m: MapEntry ← Pop[]; vp ← Pop[]; Map[vp] ← m; *----------------------------------------------------------- T← MD, StkP-2; T← Stack&+1, Call[SetBRForPage]; T← Stack&-2, Call[WriteMapPage]; * Restart IFU, since FlushPage (called from WriteMapPage) reset it. AssocExit: T← (ID)-(PCX')-1; * T← PCX+IL IFUReset, Branch[SetPCAndJump0]; *----------------------------------------------------------- SetFlags: MiscTable[1], * newM: MapEntry ← Pop[]; vp ← Pop[]; oldM: MapEntry ← Map[vp]; * newM.realPage ← oldM.realPage; Map[vp] ← newM; Push[oldM]; *----------------------------------------------------------- * Read current real page number from map. * Note that all we care about are the page number and the vacant indication, * which cannot be influenced either by concurrent IFU or I/O activity or by * the contents of the cache flags. Therefore it is not necessary to flush * the cache or turn tasking off while reading the map. T← MD, StkP-2; T← Stack&+1, Call[SetBRForPage]; * Returns RTemp0=0 RMap← RTemp0, SCall[TranslateMapEntry]; * Returns map entry in T * +1 return: entry is vacant, just return "vacant" and don't set new flags. Stack-1← T, Branch[AssocExit]; * +2 return: write the map with new flags and old real page number. T← RCY[T, Stack&-1, 14]; * Combine new flags with old page T← LCY[T, T, 14], Call[WriteMapPage]; * The pipe now contains the PREVIOUS contents of the map entry just written. * Tasking is off if came from GetFlags. SetFExit: TaskingOn, Call[TranslateMapEntry]; Stack← T, Branch[AssocExit], DispTable[1, 1, 1]; * Always return here *----------------------------------------------------------- GetFlags: MiscTable[16], * vp ← Pop[]; Push[Map[vp]]; *----------------------------------------------------------- Nop; StkP-1, Call[SetBRForPage]; * Flush this page from the cache so that the map flags are updated. * Note: in principle it should not be necessary to flush twice or to * turn tasking off; but in fact a single flush with tasking on * seems insufficient to capture the dirty bit reliably. Call[FlushPage]; * Returns RTemp0=0, TaskingOff * Read and translate map entry. RMap← RTemp0, Branch[SetFExit]; * Map-related subroutines *----------------------------------------------------------- SetBRForPage: * Enter: T=virtual page * Exit: T unchanged * MemBase=LPtr, LPtr=VA of virtual page * RTemp0=0 *----------------------------------------------------------- Subroutine; MemBase← LPtr; RTemp0← LSH[T, 10]; * set up MemBase and BR BRLo← RTemp0; RTemp0← RSH[T, 10]; BRHi← RTemp0, RTemp0← A0, Return; *----------------------------------------------------------- FlushPage: * Flushes one page from cache with tasking off. This is done to maintain consistency * between cache and real memory when the map is about to change. * Enter: MemBase=LPtr, LPtr=VA of base of page * Exit: RTemp0=0 * Tasking off, IFU reset (to ensure that activity of the IFU and of the * other tasks will not bring new munches into the cache) * Clobbers RTemp0, Cnt *----------------------------------------------------------- Subroutine; * Do the flush twice, once with TaskingOn and again with TaskingOff. * The idea is that all the actual flushing of dirty munches will occur * with TaskingOn; however, it is logically necessary to do it again with * TaskingOff because in the meantime an I/O task might have touched the * page we are flushing, and subsequent Map operations depend on the page being * completely flushed. IFUReset; RTemp0← A0, Cnt← 17S, B← MD; * Assume 20b munches per page RTemp0← Flush← RTemp0, Carry20, Branch[., Cnt#0&-1]; RTemp0← A0, Cnt← 16S; TaskingOff; RTemp0← Flush← RTemp0, Carry20, Branch[., Cnt#0&-1]; Flush← RTemp0, RTemp0← A0, Return; *----------------------------------------------------------- WriteMapPage: * Enter: T = map flags and real page number in Mesa format * MemBase = LPtr, LPtr contains virtual address * Exit: Map written with new value * Pipe contains PREVIOUS contents of map entry * Clobbers T, Q, RTemp0, RTemp1, Cnt *----------------------------------------------------------- Subroutine; * Shift wProtect and dirty bits into position for the hardware, and * test for the combination wProtect & dirty & ref. * Mesa format is B1=wProtect, B2=dirty, B3=ref. * Hardware format for writing is B0=wProtect, B1=dirty (ref can't be set). RTemp1← NOT T, Q← Link; TopLevel; T← T AND (MesaF&P.pageMask); RTemp1← (RTemp1) AND (MesaF&P.flagsMask); RTemp1← (NOT (RTemp1)) LSH 1, Branch[.+3, ALU#0]; * This is the state wProtect & dirty & ref, which the Dorado hardware * can't handle (it would mistakenly interpret it as vacant). * Turn off the dirty bit, and set the sign bit in the real page number, * which we have taken over as a duplicate dirty bit. RTemp1← (RTemp1) AND NOT (LShift[MesaF&P.dirty!, 1]C); T← T OR (100000C); * Note: RTemp1 was carefully masked (above) so as not to select TIOA values * in [10..17], which can screw up the disk controller! TIOA← RTemp1; * Set up flags for Map← Call[FlushPage]; * Returns with TaskingOff; doesn't clobber T * Now write the map entry; T contains the new real page number. Map← 0S, MapBuf← T; * Write map entry (real page & flags) PD← T-T-1, TaskingOn; PD← PRef, Branch[., ALU<0]; * Wait for map reference to finish * Writing the map zeroed the ref bit. If we desire to set ref, do so by * issuing a PreFetch to the page. Note: this does NOT clobber the pipe, * because PreFetch goes to the ring buffer part, not the private emulator entry. PD← (RTemp1) AND (LShift[MesaF&P.ref!, 1]C); T← A0, Link← Q, Branch[.+2, ALU=0]; Subroutine; PreFetch← 0S; TIOA← T, Return; * TIOA=0 required by Mesa emulator *----------------------------------------------------------- TranslateMapEntry: * Translates result of last map reference (Map← or RMap←) to Mesa format. * This procedure first waits for that map reference to finish. * Enter: MemBase = LPtr, LPtr contains virtual address * Call: SCall[TranslateMapEntry] * Exit: Returns to caller+1 if entry is vacant, caller+2 otherwise. * T = map flags and real page number in Mesa format * Clobbers RTemp0 *----------------------------------------------------------- Subroutine; PD← PRef, Global; * Subroutine entry instruction T← NOT (Map'), Branch[.+2, ALU>=0]; * Read previous real page from Pipe PD← PRef, Branch[.-1]; * Convert hardware flags to Mesa format. * The hardware returns the flags as B0=ref, B2=wProtect, B3=dirty, but * Mesa wants to see them in the form B1=WP, B2=dirty, B3=ref. RTemp0← Errors', Branch[.+2, ALU>=0]; * Previous flags (complemented) * Transfer the duplicate dirty bit from the real page to the flags T← T XOR (Or[100000, MesaF&P.dirty!]C), Branch[RealPageInRange]; * Crock for Alto/Mesa and pre-Trinity Pilot: * If real page > 7777B (the highest that can be represented in a MapEntry) * then return Vacant. This compensates for the fact that the software's * initial real memory scan doesn't have an end test! PD← T AND NOT (MesaF&P.pageMask); Branch[RealPageInRange, ALU=0]; :If[Not[AltoMode]]; ******** PrincOps version ******** * For Pilot, actually change the map entry to be vacant. This is so that * references to that page will fault. (Not sure whether this really matters.) RTemp0← 140000C; * wProtect & dirty RTemp0← A0, TIOA← RTemp0; PD← (Map← RTemp0)-1, MapBuf← RTemp0; PD← PRef, Branch[., ALU<0]; TIOA← RTemp0; :EndIf; ********************************** T← (Or[MesaF&P.wProtect!, MesaF&P.dirty!]C), Return; * Say it is vacant * The following instruction zeroes Carry. It must be the last arithmetic * instruction in ReadMapPage. Note: flags are still complemented. RealPageInRange: RTemp0← (A← RTemp0) LSH 1; * Shift wProtect&dirty to Mesa format RTemp0← (RTemp0) AND (MesaF&P.wProtect&dirty), Branch[.+2, ALU<0]; * Branch if not ref * Uncomplement the wProtect and dirty flags, and set ref if appropriate. RTemp0← (RTemp0) XOR (Or[MesaF&P.wProtect&dirty!, MesaF&P.ref!]C), DblBranch[MapVacant, MapNotVacant, ALU=0]; RTemp0← (RTemp0) XOR (MesaF&P.wProtect&dirty), DblBranch[MapVacant, MapNotVacant, ALU=0]; :If[AltoMode]; ********** Alto version ********** * Alto/Mesa wants to see real page = 0 if vacant. MapVacant: T← MesaF&P.wProtect&dirty, Return; * Vacant, return +1 with real page = 0 :Else; ******** PrincOps version ******** * PrincOps real page is undefined if vacant; however, the Cedar Nucleus depends * on being able to read and write the real page number of vacant entries. MapVacant: T← T OR (MesaF&P.wProtect&dirty), Return; * Vacant, return +1 :EndIf; ********************************** MapNotVacant: T← T OR (RTemp0), Return[Carry']; * Not vacant, return +2 TopLevel; *----------------------------------------------------------- InputM: MiscTable[5], * device ← Pop[]; Push[Input[device]]; * Dorado-only feature: if bit 0 of device is on, IOB parity checking is disabled. *----------------------------------------------------------- T← LSH[T, 10], StkP-1; T← A0, TIOA← T, Stack, Branch[.+2, R<0]; Stack← Input, Branch[MesaIOTail]; Stack← InputNoPE, Branch[MesaIOTail]; *----------------------------------------------------------- OutputM: MiscTable[6], * device ← Pop[]; data ← Pop; Output[device, data]; *----------------------------------------------------------- T← LSH[T, 10], StkP-2; T← A0, TIOA← T; Output← Stack&-1; MesaIOTail: TIOA← T, IFUNext0; * Know T=0 here :If[Not[AltoMode]]; ******** PrincOps version ******** *----------------------------------------------------------- SetMaintPanel: MiscTable[10], * maintPanel ← Pop[]; * Also generate an Opcode trap iff there is a trap handler set up, * so the software has a chance to look at the MP code. *----------------------------------------------------------- MemBase← SD; Fetch← Add[sUnimplemented!]S; PD← MD, StkP-2; MaintPanel← T, Branch[.+2, ALU#0]; IFUNext0; T← A0, Branch[OpcodeTrap]; :EndIf; ********************************** *----------------------------------------------------------- RClockM: MiscTable[11], * PushLong[clock]; *----------------------------------------------------------- RBase← RBase[RTClock]; T← RTClock, TaskingOff; * Low part -- read atomically!! Stack&+1← T; T← RTC430, TaskingOn, Branch[PushT]; * High part KnowRBase[RTemp0]; *----------------------------------------------------------- GenIOin: MiscTable[12], * Push[PrinterIn[]]; *----------------------------------------------------------- StackT← NOT (EventCntA'), IFUNext2; *----------------------------------------------------------- GenIOout: MiscTable[13], * PrinterOut[Pop[]]; *----------------------------------------------------------- EventCntB← T, StkP-2, IFUNext0; *----------------------------------------------------------- Checksum: MiscTable[7], * DO * p: LONG POINTER ← PopLong[]; c: CARDINAL ← Pop[]; s: WORD ← Pop[]; * IF c=0 THEN EXIT; * s ← OnesComplementAddAndLeftCycle[s, Fetch[p]↑]; * Push[s]; Push[c-1]; PushLong[p+1]; * IF InterruptPending[] THEN GOTO Suspend; * REPEAT Suspend => PC ← savePC; * ENDLOOP; * IF s=-1 THEN s←0; -- turn ones-complement -0 into +0 * Push[s]; *----------------------------------------------------------- Nop; RTemp0← A0, MemBase← LPtr; BRHi← T, StkP-2; * Pop the long pointer into LPtr BRLo← Stack&-1; * Come here once per munch. RTemp0 contains LPtr-relative address. * StkP addresses c (count of words remaining). * On the first iteration, checksum ((c-1) mod 20b) +1 words; on subsequent * iterations, checksum 20b words. Note that on subsequent iterations, * c mod 20b = 0, so ((c-1) mod 20b) +1 = 20b. CSMunch: T← (Stack&-1)-1; * A-1 generates carry iff A#0 T← T AND (17C), Branch[CSDone, Carry']; * Touch the first and last words to be checksummed in this block, * and issue a PreFetch for the next block. T = word count -1. T← (Fetch← RTemp0)+(Q← T), Branch[CSInterrupt, Reschedule]; T← (Fetch← T)+(20C); PreFetch← T, T← Stack&+1, Stack&+1← MD; * All possible faults have happened by this point. Stack&+1← (Stack&+1)-(Cnt← Q)-1; * Update word count RTemp0← (Fetch← RTemp0)+1; Stack&+1← (Stack&+1)+Q+1; * Update long pointer on stack Stack&-2← A← Stack&-2, XorSavedCarry; * Inner loop: 3 instructions per word. T has partial sum, StkP adresses c. CSWordLoop: T← T+MD, StkP-1, Branch[CSWordExit, Cnt=0&-1]; RTemp0← (Fetch← RTemp0)+1, Branch[.+2, Carry]; CSAddNoCarry: * ALU=0 iff came from CSWordExit Stack&+1← T← T LCY 1, DblBranch[CSWordLoop, CSMunch, ALU#0]; CSAddCarry: Stack&+1← T← (T+1) LCY 1, DblBranch[CSWordLoop, CSMunch, ALU#0]; CSWordExit: PD← A0, DblBranch[CSAddCarry, CSAddNoCarry, Carry]; * Here when c=0. If result is -0, change it to +0. StkP addresses s. CSDone: PD← (Stack)+1; * Carry iff sum=177777 StackT← A← StackT, XorSavedCarry, IFUNext2; CSInterrupt: StkP+3, Branch[BLTInterrupt]; % ********* De-implemented code ********** *----------------------------------------------------------- WMDS: MiscTable[15], * Write MDS * This opcode sets the high bits of MDS. It supports the execution * IME system code on the Dorado. The idea is to provide IME with a * way of switching whole "banks" of memory very quickly. This opcode * touches all the base registers. The Mesa software must assure that * IO devices are quiescent. This opcode affects the way IO devices * work as well as the way the mesa emulator works. * The MESA instruction, WMDS, pops the new MDSBase from the current top * of the Stack. Then it calls the subroutine, SetMDS to do the work. * WMDS does NOT switch the code base. * The NOVA instruction, WMDS, takes the new MDSBase from AC0 and calls * SetMDS to do the work. WMDS switches the code base also. * SetMDS: * ENTER w/ T= new MDS base, RBase=RBase[EmuBRHiReg]. * CLOBBER T, ETemp1, ETemp2, EmuBRHiReg, ALL BRHi * registers and MemBase. *----------------------------------------------------------- StkP-2, RBase← RBase[EmuBRHiReg], Call[SetMDS]; T← (ID)-(PCX')-1, Branch[SetPCAndJump0]; WMDSNova: Nop, At[sd400, 22]; T← Stack, Call[SetMDS]; Branch[AEmuNext]; Subroutine; KnowRBase[EmuBRHiReg]; SetMDS: * We'll compute a new MDS offset for BRHi based upon (newMDSHi-EmuBRHiReg). T← T-(EmuBRHiReg); * EmuBRHiReg← new MDS, T← difference EmuBRHiReg← (EmuBRHiReg)+T; ETemp2← Lshift[36, 10]C; * set MemBase[0..36B],leave CODE alone SetMDSL: MemBase← ETemp2; DummyRef← 0S, B← MD; ETemp1← VAHi; * capture hi order bits of current BR ETemp1← (ETemp1)+T; * this is the new value to use ETemp2← (ETemp2)-(400C); BRHi← ETemp1, Branch[SetMDSL, ALU>=0]; Q← Link; TopLevel; * Call SetDisplayBRHi; it will return. Note Link← overrides implied Call. T← EmuBRHiReg, RBase← RBase[RTemp0]; MDSHi← T, Link← Q, Branch[SetDisplayBRHi]; % ********* End of de-implemented code ********** % ********* De-implemented code ********** *----------------------------------------------------------- XferL: MiscTable[14], * Long Xfer (MDS switch) * StoreMDS[@LocalBase[L].pc]↑ ← PC; * newMDS ← Pop[]; destLink ← Pop[]; Push[L]; Push[LOOPHOLE[MDS, Long].high]; * LOOPHOLE[MDS, Long].high ← newMDS; * XFER[src: L, dst: destLink]; *----------------------------------------------------------- StkP-2; DLink← Stack, Call[SavePCInFrameIL]; * DLink← destination link T← SLink; * SLink=L Stack&+1← T, RBase← RBase[EmuBRHiReg]; * save L in Stack T← EmuBRHiReg; Stack← T, Q← Stack; * Stack← mds, Q← newMDS T← Q, Call[SetMDS]; * switch MDS bases RBase← RBase[RTemp0]; MemBase← MDS, Branch[Xfer]; * Now do Xfer in new MDSbase % ********* End of de-implemented code ********** *----------------------------------------------------------- LoadRamM: MiscTable[3], * Load Ram and jump * flag ← Pop[]; itemArray: LONG POINTER ← PopLong[]; * nextItem: LONG POINTER ← LoadRam[itemArray+1]; * IF (flag MOD 2)=1 THEN {jump to the start address in the new Ram image}; * PushLong[nextItem]; [] ← PopLong[]; -- leave pointer to next Item above TOS *----------------------------------------------------------- StkP-3, RBase← RBase[LRFlag]; LRFlag← NOT T, MemBase← LPTR; * LoadRam reverses sense of flag * Note: software passes (pointer to first item)-1; must skip over it!!! T← (Stack&+1)+1; BRLo← T; T← A← Stack&-1, XorSavedCarry; BRHi← T, Call[LoadRam]; DummyRef← LRItem, B← MD; * Convert ending address to long pointer Stack&+1← VALo; Stack&-2← VAHi, Branch[AssocExit]; * IFUReset and restart IFU :If[AltoMode]; ********** Alto version ********** *----------------------------------------------------------- SetPartitionM: MiscTable[17], * Set default disk partition number * p: Partition ← Pop[]; * IF p=Partition[0] THEN Push[currentPartition] * ELSE IF p IN [Partition[1]..Partition[maxPartition]] * THEN BEGIN currentPartition ← p; Push[-1]; END ELSE Push[0]; *----------------------------------------------------------- PD← T, StkP-1, RBase← RBase[DefaultDisk]; PD← T-(6C), Branch[.+2, ALU#0]; T← DefaultDisk, Branch[PushT]; * Return current default T← T-(Q← T)-1, Branch[.+2, Carry']; T← A0, Branch[PushT]; * Illegal, return 0 DefaultDisk← Q, Branch[PushT]; * Legal, set it and return -1 :EndIf; ********************************** KnowRBase[RTemp0]; * Memory block zeroing opcodes -- added to instruction set for the benefit of Cedar, * but useful in their own right. *** Someday integrate these with the BLT logic in DMesaRW.mc. But for now: *** *----------------------------------------------------------- LocalBlkZM: MiscTable[100], * Local block zero * count: CARDINAL ← Pop[]; * FOR offset DECREASING IN [0..count) DO StoreMDS[L+offset]↑ ← 0; ENDLOOP; * (The implementation must check for interrupts.) *----------------------------------------------------------- T← (ID)+T+1, MemBase← L, StkP-1, * (Offset of local 0)-1 (ID=2 here) Branch[BlkZCommon]; *----------------------------------------------------------- LongBlkZM: MiscTable[102], * Long block zero * count: CARDINAL ← Pop[]; * p: LONG POINTER ← PopLong[]; SP ← SP+2; -- leave long pointer on stack * FOR offset DECREASING IN [0..count) DO Store[p+offset]↑ ← 0; ENDLOOP; * (The implementation must check for interrupts, and may push the intermediate * count back on the stack, but must not disturb the pointer. This is why * the operation is done in descending order of address.) *----------------------------------------------------------- T← T-1, MemBase← LPtr, StkP-2; BRHi← Stack&-1; BRLo← Stack&+2; * T = (base-relative) address of last word of block; Stack = count. BlkZCommon: RTemp1← T-(20C); RTemp0← T, PreFetch← RTemp1; * PreFetch 20b words ahead T← (Stack)-1; T← T AND (17C), Branch[BlkZDone, Carry']; * Branch if count is zero * On first iteration, do ((count-1) mod 20b)+1 words; * on subsequent iterations, do 20b words. * T = (# words to do this iteration)-1. BlkZMunchEntry: RTemp1← (RTemp1)-(20C), Branch[.+2, R<0]; PreFetch← RTemp1; * PreFetch 40b words ahead Cnt← T, Branch[BlkZInterrupt, Reschedule]; RTemp0← (Store← RTemp0)-1, DBuf← 0C, Branch[.+2, Cnt=0&-1]; BlkZWordLoop: RTemp0← (Store← RTemp0)-1, DBuf← 0C, Branch[BlkZWordLoop, Cnt#0&-1]; Stack← (Stack)-T-1, T← MD; * Wait for fault, then update count T← 17C, Branch[BlkZMunchEntry, ALU#0]; BlkZDone: StkP-1, IFUNext0; BlkZInterrupt: Branch[MesaReschedTrap]; * Event counter and PC sampling stuff. * Also see subroutines in Junk.mc MC[EnableEventsAB, 6000]; MC[DisableEventsAB, 0]; *----------------------------------------------------------- StartCountM: MiscTable[240], * Start Event Counters * control ← Pop[]; ZeroEventCounters[]; InsSetOrEvent[control]; *----------------------------------------------------------- StkP-2, RBase← RBase[Events], Call[StartCounters]; IFUNext0; *----------------------------------------------------------- ReadCountM: MiscTable[241], * Read Event Counters * CounterValues: TYPE = MACHINE DEPENDENT RECORD * [ * eventALo, eventAHi1, eventAHi0: CARDINAL, -- event counterA * eventBLo, eventBHi1, eventBHi0: CARDINAL, -- event counterB * ] * p: LONG POINTER TO CounterValues ← PopLong[]; ReadCounters[p]; *----------------------------------------------------------- MemBase← LPtr, StkP-1; BRHi← Stack&-1; BRLo← Stack&-1; T← A0, RBase← RBase[Events], Call[ReadCounters]; IFUNext0; *----------------------------------------------------------- StopCountM: MiscTable[242], * Stop Event Counters * InsSetOrEvent[disableEventsAB]; *----------------------------------------------------------- T← DisableEventsAB, StkP-1; InsSetOrEvent← T, IFUNext0; *----------------------------------------------------------- SetPCHistM: MiscTable[243], * Set PC Sampling Histogram * PCHistogram: TYPE = ARRAY [0..4095] OF LONG CARDINAL; * p: LONG POINTER TO PCHistogram ← PopLong[]; * IF p#NIL THEN EnablePCSampling[p] ELSE DisablePCSampling[]; *----------------------------------------------------------- StkP-2, RBase← RBase[Events]; Q← Stack&-1, Call[SetPCHistAddr]; IFUNext0; :If[Not[AltoMode]]; ******** PrincOps version ******** *----------------------------------------------------------- SetIntervalTimerM: MiscTable[255], * Set Interval Timer time *----------------------------------------------------------- StkP-2, RBase← RBase[WakeupTime]; WakeupTime← T, IFUNext0; :EndIf; ********************************** * Other Dorado-only instructions KnowRBase[RTemp0]; *----------------------------------------------------------- RWMufManM: MiscTable[246], * Read/write muffler/manifold system * arg: RECORD [useDMD: BOOLEAN, unused: [0..7], dMuxAddr: [0..7777B]]; * result: RECORD [dMuxData: BOOLEAN, unused: [0..77777B]]; * arg ← Pop[]; SetDMuxAddress[arg.dMuxAddr]; * IF arg.useDMD THEN UseDMD[]; * result.dMuxData ← DMuxData[]; Push[result]; *----------------------------------------------------------- StkP-1, Call[SetDMuxAddress]; * Takes address and returns data in T Stack, Branch[.+2, R>=0]; UseDMD; StackT← T, IFUNext2; :If[Not[AltoMode]]; ******** PrincOps version ******** *----------------------------------------------------------- ResetEtherM: MiscTable[247], * Reset Ethernet hardware and tasks *----------------------------------------------------------- StkP-1, Branch[ResetEther]; :EndIf; ********************************** *----------------------------------------------------------- GetMemConfM: MiscTable[251], * Get memory configuration * Push[realPages]; Push[virtualBanks]; *----------------------------------------------------------- RBase← RBase[RealPages]; T← RealPages; Stack&+1← T; T← VirtualBanks, Branch[PushT]; *----------------------------------------------------------- HaltM: MiscTable[252], * Halt Dorado, leaving specified value on BMux. * This is useful primarily for power-off: BMux contains time until power-on. * BMux← Pop; Halt[]; *----------------------------------------------------------- StkP-1, TaskingOff; B← StackNoUfl, Breakpoint, Branch[.]; *----------------------------------------------------------- SetDisplayFieldRateM: MiscTable[253], * visibleLines ← Pop[]; topBorder ← Pop[]; verticalSync ← Pop[]; * visibleLines is total number of visible lines, including both borders. * All counts are number of scan lines in the even field. *----------------------------------------------------------- StkP-1, Branch[SetDisplayFieldRate]; :If[Not[AltoMode]]; ******** PrincOps version ******** *----------------------------------------------------------- ResetDiskM: MiscTable[254], * Reset disk hardware and tasks *----------------------------------------------------------- StkP-1, Branch[ResetDisk]; :EndIf; ********************************** * Stable Storage block I/O instructions (using GenIn/Out) KnowRBase[RTemp0]; MC[ssbInput, 100000]; MC[ssbClock, 40000]; MC[ssbPowerOn, 10000]; *----------------------------------------------------------- SSBlockIn: MiscTable[256], * Stable Storage block input * DO * c: CARDINAL ← Pop[]; p: LONG POINTER ← PopLong[]; * IF c=0 THEN EXIT; * GenOut[ssbInput+ssbPowerOn+ssbClock]; GenOut[ssbInput+ssbPowerOn]; * Store[p]↑ ← GenIn[]; * PushLong[p+1]; Push[c-1]; * IF InterruptPending[] THEN {PC ← savedPC; EXIT}; * ENDLOOP; *----------------------------------------------------------- RTemp0← A0; RTemp1← Or[ssbInput!, ssbClock!, ssbPowerOn!]C; RTemp2← Or[ssbInput!, ssbPowerOn!]C, Call[SSBBlockSetup]; * Control returns here for each munch (or partial munch) to process EventCntB← RTemp1; * Send ssbClock=1 for first word EventCntB← RTemp2; * Send ssbClock=0 for first word Nop; * 1-cycle delay required before reading Nop; Nop; Nop; Nop; * The inner loop reads one word from the SSB, issues the clock for the next word, * and exits one word early. This is to ensure adequate delay between sending * the clock and reading the data. SSBInWordLoop: T← NOT (EventCntA'), * Read current word Branch[SSBInWordExit, Cnt=0&-1]; * Exit if this is the last word EventCntB← RTemp1; * Send ssbClock=1 for next word EventCntB← RTemp2; * Send ssbClock=0 for next word Nop; Nop; Nop; Nop; RTemp0← (Store← RTemp0)+1, DBuf← T, * Store current word Branch[SSBInWordLoop]; SSBInWordExit: RTemp0← (Store← RTemp0)+1, DBuf← T, Branch[SSBNextMunch]; *----------------------------------------------------------- SSBlockOut: MiscTable[257], * Stable Storage block output * DO * c: CARDINAL ← Pop[]; p: LONG POINTER ← PopLong[]; word: CARDINAL; * IF c=0 THEN EXIT; * word ← Fetch[p]↑; * GenOut[BITOR[word, ssbClock]]; GenOut[word]; * PushLong[p+1]; Push[c-1]; * IF InterruptPending[] THEN {PC ← savedPC; EXIT}; * ENDLOOP; *----------------------------------------------------------- RTemp0← A0; RTemp1← NOT (ssbClock); T← 7000C; * ShC← [SHA=R, SHB=R, count=16, LMask=1, RMask=16] T← T OR (341C); ShC← T, Call[SSBBlockSetup]; * Control returns here for each munch (or partial munch) to process RTemp0← (Fetch← RTemp0)+1; * Fetch first word of munch T← SHMDBothMasks[RTemp1], * Pick up first word and set ssbClock=1 DblBranch[SSBOutWordLoop, SSBOutWordExit, Cnt#0&-1]; * The inner loop fetches one word ahead and exits one word early. * T contains next word to be sent, OR'ed with ssbClock. SSBOutWordLoop: RTemp0← (Fetch← RTemp0)+1; * Fetch next word T← (RTemp1) AND (EventCntB← T); * Send current word with ssbClock=1, and * set ssbClock to zero T← SHMDBothMasks[RTemp1], * Pick up next word and set ssbClock=1 EventCntB← T, * Send current word with ssbClock=0 DblBranch[SSBOutWordLoop, SSBOutWordExit, Cnt#0&-1]; SSBOutWordExit: T← (RTemp1) AND (EventCntB← T); * Send final word with ssbClock=1 EventCntB← T, Branch[SSBNextMunch]; * Send final word with ssbClock=0 *----------------------------------------------------------- SSBBlockSetup: * Enter: RTemp0 = 0 * STK[StkP-1] = word count * STK[StkP-2],,STK[StkP-3] = base pointer * Exit: Exits opcode if no (more) words to transfer or interrupt pending; otherwise: * MemBase = LPtr, containing base pointer * Cnt = number of words to transfer this iteration -1 * Pointer and count updated on stack to account for that number of words * First and last words touched to ensure no faults * Caller should transfer (Cnt)+1 words and increment RTemp0 by that amount, * then branch to SSBNextMunch. Control will return at the instruction after * the initial call to SSBBlockSetup with parameters set up for another munch. * Clobbers T, Q; uses RTemp6 for return link, which must not be clobbered by caller. *----------------------------------------------------------- Subroutine; RTemp6← Link; TopLevel; MemBase← LPtr, StkP-2; BRHi← Stack&-1; * Pop the long pointer into LPtr BRLo← Stack&+2; * Come here once per munch. RTemp0 contains LPtr-relative address. * StkP addresses c (count of words remaining). * On the first iteration, process ((c-1) mod 20b) +1 words; on subsequent * iterations, process 20b words. Note that on subsequent iterations, * c mod 20b = 0, so ((c-1) mod 20b) +1 = 20b. SSBNextMunch: T← (Stack)-1; * A-1 generates carry iff A#0 T← T AND (17C), Branch[SSBDone, Carry']; * Touch the first and last words to be processed in this block, * and issue a PreFetch for the next block. (Reason for touching words is * to ensure that a fault won't occur in the middle of the word loop * while we are wiggling the GenOut clock and such.) T = word count -1. T← (Fetch← RTemp0)+(Q← T), Branch[SSBInterrupt, Reschedule]; T← (Fetch← T)+(20C); PreFetch← T, T← MD; * All possible faults have happened by this point. Stack&-2← (Stack&-2)-(Cnt← Q)-1; * Update word count Stack&+1← (Stack&+1)+Q+1; * Update long pointer on stack Link← RTemp6; Subroutine; Stack&+1← A← Stack&+1, XorSavedCarry, Return; TopLevel; SSBDone: StkP-3, IFUNext0; SSBInterrupt: Branch[BLTInterrupt];