{File name Block.mc Description: Mesa Block Transfer opcodes Author: Jim Frandeen Created: January 30, 1981 Last edited Fiala 29-Apr-86 13:25:18 Copyright (C) 1981, 1982, 1983, 1984, 1985, 1986 by Xerox Corporation. All rights reserved. Fiala 17-Apr-86 10:51:02 Commented out Checksum opcode (moved to CedarB0.mc and CedarMisc.mc with performance improvements). Fiala, BJackson 4-Jun-85 18:30:57: Fix Checksum for count=0 Frandeen March 23, 1981 2:46 PM: Update comments and try out new coding conventions. Frandeen March 16, 1981 11:42 AM: Add code to handle Checksum Misc opcode. Frandeen February 25, 1981 3:02 PM: Major rewrite. The goddamn BlockEqual instructions are not at all like the BlockTransfer instructions. The code offset is on the top of the stack. Frandeen February 24, 1981 10:50 PM: Fix the Zero Byte Page Carry Gotcha. Frandeen February 21, 1981 3:23 AM: Major rewrite. BlockEqual is a two-byte opcode. Frandeen February 17, 1981 11:10 PM: Change to check for zero count BEFORE we do any maps. Unfortunately, we get address fault otherwise. } {This module handles BlockTransfer and BlockEqual instructions. For the Block instructions, L2 is used to call SaveBlockRegs. It is also used to distinguish the opcodes as follows: Inst. L2 code long Block Transfer BLT 3 0 0 1 1 BLTC B 1 0 1 1 BLEQ 2 0 0 1 0 BLEQC A 1 0 1 0 BLTL 7 0 1 1 1 BLEQL 6 0 1 1 0 BLEQCL E 1 1 1 0 Checksum 4 0 1 0 0 After L2Disp: BRANCH[NotBlockTransferInstruction, BlockTransferInstruction, 0E], BRANCH[ChecksumInstruction, BlockInstruction, 0D], BRANCH[ShortInstruction, LongInstruction, 0B], BRANCH[ZeroBase, CodeBase, 7], } Set[Transfer,0E]; Set[Block,0D]; Set[Long,0B]; Set[CodeBase,7]; { SUBROUTINES } {****************************************************************************************** PopCount subroutine L2Disp is pending on call. ******************************************************************************************} MacroDef[PopCountRet,at[#1,10,PopCountRet]]; {Return depending on the two middle bits of L2:} Set[PopShortBlock,0B]; {x01x = 0B = short Block} Set[PopLongBlock,0F]; {x11x = 0F = long Block} Set[PopChecksum,0D]; {x10x = 0D = checksum} Set[PopBLEQC,9]; {x00x = 9 = BLEQC (called with no disp)} PopCount: TOS ← STK, pop, DISP4[PopCountRet,9], c*; {****************************************************************************************** Checksum TOS = uStack5 = sourceHi STK = uStack4 = = sourceLow STK-1 = uStack3 = count STK-2 = uStack2 = sum We set up source and dest to be the same thing because this works out easiest when mapping. ******************************************************************************************} Checksum: at[7,10,Misc0n] L ← ~ErrnIBnStkp, ULsave ← L, L2 ← Checksum, c1; rhT {destHi} ← uStack5 {sourceHi}, CALL[SaveBlockRegs], c2; {SaveBlockRegs subroutine here c3-c3} at[Checksum,10,SavebbRegsRet] Rx {sourceLow} ← uStack4 {sourceLow}, c1; TOS ← uStack3 {count}, ZeroBr, L0 ← BLEQfini, c2; TT ← rhTT {sourceHi} ← uStack5 {sourceHi}, {Next line} BRANCH[BlockEqualCommon, blNullChecksum], c3; blNullChecksum: Noop, c1; {this is a useless click, and should never happen} Noop, c2; TOS ← 1, c3; Rx ← uStack2, GOTO[blChecksumCountZero], c1; {fake out blChecksumCountZero when invoked with count zero}; { BLOCK EQUAL INSTRUCTIONS } {****************************************************************************************** BLEQL Block Equal Long (sBLTE = KFCB 56B) TOS = srcHi STK = srcLow STK-1 = count STK-2 = destHi STK-3 = destLow We will treat this just like BlockTransferLong. This means that if we update the stack because we are interrupted, we will come back in with the operands reversed in the stack. ******************************************************************************************} @BLEQL: at[6,8,BlockEqual] L ← ~ErrnIBnStkp, ULsave ← L, L2 ← BLEQL, c1; rhT ← TOS {destHi} LRot0, CALL[SaveBlockRegs], c2; {SaveBlockRegs subroutine here c3-c3} at[BLEQL,10,SavebbRegsRet] T ← STK {destLow}, pop {count}, L2Disp, CALL[PopCount], c1; {TOS ← STK, pop {srcHi}, GOTO[BLongSourceCommon], c2;} {****************************************************************************************** BLEQCL Block Equal Code Long TOS = offset of source from code STK = count STK-1 = destHi STK-2 = destLow ******************************************************************************************} @BLEQCL: at[0,8,BlockEqual] L ← ~ErrnIBnStkp, ULsave ← L, L2 ← BLEQCL, c1; Rx ← TOS {code offset}, CALL[SaveBlockRegs], c2; {SaveBlockRegs subroutine here c3-c3} at[BLEQCL,10,SavebbRegsRet] TOS ← STK {count}, pop {destHigh}, c1; rhT ← STK {destHigh}, pop{destLow}, {Continue next line} GOTO[BlockEqualCodeCommon], c2; {****************************************************************************************** BLEQ Block Equal (sBLTE = KFCB 52B) TOS = srcLow STK = count STK-1 = destLow We will treat this just like BlockTransfer. This means that if we update the stack because we are interrupted, we will come back in with the operands reversed in the stack. ******************************************************************************************} @BLEQ: at[2,8,BlockEqual], L ← ~ErrnIBnStkp, ULsave ← L, L2 ← BLEQ, c1; TT ← rhTT {srcHi} ← UvMDS,CALL[SaveBlockRegs], c2; {SaveBlockRegs subroutine here c3-c3} rhT ← UvMDS {dstHi}, {Continue next line} at[BLEQ,10,SavebbRegsRet] push {destLow, now in stack}, GOTO[blShortSourceCommon], c1; {****************************************************************************************** BLEQC Block Equal Code (sBLTE = KFCB 54B) TOS = offset of source from codebase STK = count STK-1 = destLow ******************************************************************************************} @BLEQC: at[4,8,BlockEqual] L ← ~ErrnIBnStkp, ULsave ← L, L2 ← BLEQC, c1; Rx ← TOS {code offset}, CALL[SaveBlockRegs], c2; {SaveBlockRegs subroutine here c3-c3} at[BLEQC,10,SavebbRegsRet] rhT ← UvMDS {dstHi}, {0 disp} CALL[PopCount], c1; {TOS ← STK {count}, pop {destLow}, c2;} BlockEqualCodeCommon: PopCountRet[PopBLEQC] TT ← rhTT ← UvChigh {srcHi}, c3; BlockEqualCommon: {Come here from Checksum. Stack pointer is pointing to sourceLow} T ← STK {destLow}, pop{next/count if Checksum}, GOTO[blTestCount], c1; { BLOCK TRANSFER INSTRUCTIONS } {****************************************************************************************** BLTL Block Transfer Long TOS = destHi STK = destLow STK-1 = count STK-2 = srcHi STK-3 = srcLow ******************************************************************************************} @BLTL: opcode[353'b] L ← ~ErrnIBnStkp, ULsave ← L, L2 ← BLTL, c1; rhT ← TOS {destHi} LRot0, CALL[SaveBlockRegs], c2; {SaveBlockRegs subroutine here c3-c3} at[BLTL,10,SavebbRegsRet] T ← STK {destLow}, pop {count}, L2Disp, CALL[PopCount], c1; {TOS ← STK {count}, pop{srcHi}, c2;} blLongSourceCommon: PopCountRet[PopLongBlock] TT ← rhTT ← STK {srcHi}, pop {srcLow}, GOTO[blPopSourceLow] c3; {****************************************************************************************** BLTC Block Transfer Code TOS = destLow STK = count STK-1 = offset of source from codebase ******************************************************************************************} @BLTC: opcode[354'b] L ← ~ErrnIBnStkp, ULsave ← L, L2 ← BLTC, c1; TT ← rhTT {srcHi} ← UvChigh {dstHi}, CALL[SaveBlockRegs], c2; {SaveBlockRegs subroutine here c3-c3} rhT ← UvMDS {dstHi}, {continue next line} at[BLTC,10,SavebbRegsRet] push {destLow, now in stack}, GOTO[blShortSourceCommon], c1; {****************************************************************************************** BLT Block Transfer TOS = destLow STK = count STK-1 = srcLow ******************************************************************************************} @BLT: opcode[352'b] L ← ~ErrnIBnStkp, ULsave ← L, L2 ← BLT, c1; TT ← rhTT {srcHi} ← UvMDS, CALL[SaveBlockRegs], c2; {SaveBlockRegs subroutine here c3-c3} at[BLT,10,SavebbRegsRet] rhT ← UvMDS {dstHi}, push {destLow, now in stack}, c1; blShortSourceCommon: T ← STK {destLow}, pop {count}, L2Disp, CALL[PopCount], c2; {TOS ← STK {count}, pop {srcLow}, c3;} blPopSourceLow: PopCountRet[PopShortBlock] Rx ← STK {srcLow}, pop {next}, c1; blTestCount: [] ← TOS {count}, ZeroBr, CANCELBR[$,1], c2; blTestOffset: L2Disp, BRANCH[$,blNullCount], c3; blSaveStackP: uStackPSave ← L, BRANCH[blZeroOffset,blCodeOffset, CodeBase], c1; blZeroOffset: Q ← 0, GOTO[blSaveOffset], c2; blCodeOffset: Q ← UvC, c2; blSaveOffset: uSourceOffset ← Q, L1 ← L1w.Restore, GOTO[blMapSource], c3; blNullCount: {L2Disp is pending. Load L0 to return to BLTransferDone or BLCompareDone. Put one in TOS for TRUE in case this is BlockEqual. It doesn't matter which Restore entry we use so long as it is 3 cycles and we don't try to restore rhL. } TOS ← 1 {TRUE}, L0 ← BLEQfini, {continue next line} BRANCH[RestoreBlockRegsC3Initial, RestoreBlockRegsC3, Transfer], c1; {Restore here c2-c1}; blTransferDone: at[BLTfini,10,RestoreCallers] TOS ← STK, pop, GOTO[blDone], c2; blCompareDone: {1 (TRUE) or 0 (FALSE) is in TOS.} blChecksumDone: {sum is in TOS.} at[BLEQfini,10,RestoreCallers] {BlockCompare and Checksum are two-btye instructions.} PC ← PC + PC16, c2; blDone: Xbus ← uPCCross, XRefBr, c3; PC ← PC + PC16, BRANCH[IBDispOnly,blSetInt], c1; blSetInt: MesaIntRq, GOTO[NoWakeups {in Refill}], c2; {NoWakeups: GOTO[IgnoreInt] ,c3;} {IgnoreInt: Noop ,c1;} {IBDispOnly: IBDisp, GOTO[DISPNIonly] ,c2;} {****************************************************************************************** Map source and dest virtual addresses Entry State: Registers have been saved by SaveBlockRegs and must be restored. All arguments have been removed from the stack except for the case of Checksum. In the case of Checksum, we must set Stack pointer to 1 before we exit. L1 = L1w.Restore: return address from RLMapFix or WLMapFix in case of trap. rhTT = TT = source virtual address high Rx = source virtual address low rhT = dest virtual address high T = dest virtual address low Q = source offset (0 if not Code operation, else UvC) TOS = count ******************************************************************************************} blMapSource: {Map source real address into rhG, G. Test map for referenced. If not referenced, update the map since we are about to read it. We want to end up with the source virtual address in rhTT,,Q in case of a trap on source. Save rhTT (source high) and Rx (source low) so that we can restore it later. Set up L0 to return to BLMapDest if we call RLMapFix.} Map ← Q ← [rhTT, Rx + Q], c1; blMapSourcex: uSourceHighSave ← TT, PC ← Q, L0 ← L0.BLSource, c2; G ← rhG ← MD, XRefBr, uSourceLowSave ← Rx, c3; blMapDest: at[L0.BLSource,10,RMapFixCaller] {Map dest real address into rhRx, Rx. We want to end up with the source virtual address in rhTT,,Q in case of a trap on dest. Return here from RLMapFix if we needed to set the referenced bit. Set up L0 to return to BLTestDestDirty if BlockTransfer or BLTestDestReferenced if BlockEqual.} Map ← [rhT, T], L2Disp, BRANCH[blSetSourceReferenced, blSourceMapOK], c1; blSourceMapOK: Q ← rhT, L0 ← L0.BLDestR, {Continue next line} BRANCH[blMapDestRead, blMapDestWrite, Transfer], c2; blMapDestWrite: {Come here for BlockTransfer to map the dest address for write. If not dirty, we will have to fix the map since we are about to write it.} rhRx ← Rx ← MD, XDirtyDisp, c3; blTestDestDirty: at[L0.BLDest,10,WMapFixCaller] {Return here from WLMapFix after we set dirty bit.} rhTT ← Q LRot0, BRANCH[blSetDestDirty, blDestDirty, 1], c1; blSetSourceReferenced: {Come here if source map must be fixed for referenced. Q = virtual sourceLow. rhTT = virtual sourceHi. Set Rx to map data. L0 = L0.BLSource to return to BLMapDest above after we set referenced bit. L1 = L1w.restore = L1r.Restore to return to BLReadTrap in case of trap.} Rx ← G, CANCELBR[RLMapFix,0F], c2; blSetDestDirty: {Come here if dest map must be fixed for dirty. Rx = map data. rhTT = virtual destHi. Set Q to virtual destLow. L0 = L0.BLDest for return to BLTestDestDirty after we set dirty bit. L1 = L1w.restore = L1r.restore to return to BLWriteTrap in case of trap.} Q ← T, CALL[WLMapFix], c2; blDestDirty: {Restore while we have a spare cycle in case we get to Update.} TT ← rhTT ← uSourceHighSave, GOTO[blStart], c2; blMapDestRead: {Come here if Block Equal. Test dest for referenced. If not referenced, we will have to fix the map since we are about to read it. } rhRx ← Rx ← MD, XRefBr, c3; blTestDestReferenced: at[L0.BLDestR,10,RMapFixCaller] {Return here from RLMapFix after we set dest referenced.} rhTT ← Q LRot0, {Continue next line} BRANCH[blSetDestReferenced, blDestReferenced], c1; blSetDestReferenced: {Come here if dest map must be fixed for referenced. Rx = map data. rhTT = virtual destHi. Set Q to virtual destLow. L0 = L0.BLDest for return to blTestDestReferenced after we set dirty bit. L1 = L1w.restore = L1r.Restore to return to blWriteTrap in case of trap.} Q ← T, CALL[RLMapFix], c2; blDestReferenced: {Restore while we have a spare cycle in case we get to Update.} TT ← rhTT ← uSourceHighSave, GOTO[blStart], c2; blReadTrap: at[L1r.Restore,10,RFixForTrap] {Return here from RLMapFix in case of trap on either source or dest (if Block Equal).} stackP ← uStackPSave, {Continue next line} L0 ← restore.trap, GOTO[blRestoreBlockRegs], c1; blWriteTrap: at[L1w.Restore,10,WFixForTrap] {Return here from WLMapFix in case of trap on dest.} stackP ← uStackPSave, L0 ← restore.trap, c1; blRestoreBlockRegs: {StackP has been restored from uStackPSave. Restore all other registers. Q = virtual address low. TT = virtual address high.} uFaultParm0 ← Q, push, CALL[RestoreBlockRegsC1], c2; {Restore here. c3-c3} at[restore.trap,10,RestoreCallers] {Restore TOS so VArhTT can save it again} TOS ← STK, GOTO[VArhTT], c1; {****************************************************************************************** Block inner loop Entry State: L1 = L1w.Restore: return address from RLMapFix or WLMapFix in case of trap. rhTT = source virtual address high uSourceHighSave = source virtual address High PC = source virtual address low uSourceLowSave = source virtual address low rhG,,G = source real address high rhT = dest virtual address high T = dest virtual address low rhRx,,Rx = dest real address high TOS = count Registers used: Q = words transferred L = source word read ******************************************************************************************} blStart: L {words transferred} ← 0, c3; {Note that it is necessary to do the first memory reference separately. Unfortunately, we cannot use Q to add to the virtual address each time through the loop. When the low byte of the virtual address is zero, we will never get a page cross branch. This is the Zero Byte Page Cross Gotcha. } MAR ← G ← [rhG, PC+0], c1; L2Disp, c2; PC ← MD {source}, L2Disp, BRANCH[blFirstChecksumWord, blFirstDest, Block], c3; blFirstDest: MAR ← Rx ← [rhRx, T + 0], {Continue next line} {Set L0 to return to BLTransferDone or BLCompareDone.} L0 ← BLEQfini, BRANCH[blReadNextDest, blWriteNextDest, Transfer], c1; {Read next source word: high 10 bits from rhG,,G, low 8 bits from G + 1.} blReadSource: MAR ← G ← [rhG, G + 1], MesaIntBr, CANCELBR[$,0F], c1; L2Disp, DISP2[blSourceCheck], c2; PC ← MD {source}, L2Disp, {next line} at[0,4,blSourceCheck] BRANCH[blNextChecksumWord, blNextDest, Block], c3; at[2,4,blSourceCheck] Noop, CANCELBR[blUpdate,0F], {source page cross} c3; at[1,4,blSourceCheck] Noop, CANCELBR[blUpdate,0F], {interrupt} c3; at[3,4,blSourceCheck] Noop, CANCELBR[blUpdate,0F], {source page cross and interrupt} c3; blNextDest: {Send real address of dest: high 10 bits from rhRx,,Rx, low 8 bits from Rx + 1.} MAR ← Rx ← [rhRx, Rx + 1], {Continue next line} BRANCH[blReadNextDest, blWriteNextDest, Transfer], c1; blWriteNextDest: {Come here if BlockTransfer to store the source word in the dest address.} MDR {dest} ← PC {source}, TOS {count} ← TOS - 1, ZeroBr, {continue next line} DISP2[blDestWriteCheck], c2; blIncrementWordsTransferred: L {words transferred} ← L + 1, {Continue next line} at[0,4,blDestWriteCheck] L2Disp, BRANCH[blReadSource, blCountZero], c3; at[2,4,blDestWriteCheck] TOS {count} ← TOS + 1, CANCELBR[blUpdate,1], c3; blReadNextDest: {Come here if BlockEqual to compare the source word to the dest word.} Noop, BRANCH[blNoDestReadPageCross, blDestReadPageCross,1], c2; blDestReadPageCross: Noop, GOTO[blUpdate], {Dest page cross} c3; blNoDestReadPageCross: PC ← MD {source} xor PC {dest}, c3; [] ← PC, NZeroBr, c1; blDecrementCount: TOS {count} ← TOS - 1, ZeroBr, {Continue next line} BRANCH[blIncrementWordsTransferred, blFalse], c2; blFalse: TOS ← 0 {false}, CANCELBR[RestoreBlockRegsC1,1], c3; {RestoreBlockRegs here. Return to blCompareDone. c1-c1} blCountZero: TOS ← 1 {TRUE}, {next line} BRANCH[blChecksumCountZero, RestoreBlockRegsC3, Block], c1; {Return to blTransferDone or blCompareDone. c2-c1} blChecksumCountZero: [] ← Rx xor ~0, NZeroBr, c2; {If sum is all ones, change it to zero.} stackP ← TOS {1}, TOS ← uStack2 {sum}, {next line} BRANCH[blChecksumAllOnes,RestoreBlockRegsC1], c3; {Restore here. Return to blCompareOrChecksumDone. c1-c1} blChecksumAllOnes: TOS {sum} ← 0, CALL[RestoreBlockRegsC3], c1; {Restore here. Return to blChecksumDone. c2-c1} {****************************************************************************************** ChecksumWord The Pup checksum is a 16-bit, one's complement, add-and-cycle checksum computed over the 16-bit words in the block. The checksum is initialized to zero and computed by repeated one's complement addition and left cycle. If the result is the ones-complement value "minus one" (177777B), it is converted to zero. 177777B is specifically defined to mean that the Pup carries no checksum. ******************************************************************************************} blFirstChecksumWord: PC {next word to add to sum} ← PC LRot1, CANCELBR[blChecksum,0F], c1; blNextChecksumWord: PC {next word to add to sum} ← PC LRot1, CANCELBR[blChecksum,0F], c1; blChecksum: Rx {sum} ← uStack2 LRot1, L0 ← BLEQfini, c2; Rx {sum} ← Rx + PC {next word}, CarryBr, c3; blTestChecksumCarry: uStack2 ← Rx {sum}, BRANCH[blDecrementCount, blChecksumCarry], c1; blChecksumCarry: Rx {sum} ← Rx + 1, c2; Noop, GOTO[blTestChecksumCarry], c3; {****************************************************************************************** blUpdate Come here if we got a source or dest page cross or an interrupt request. L1 = L1w.Restore: return address from RLMapFix or WLMapFix in case of trap. TT = rhTT =uSourceHighSave source virtual address high uSourceLowSave = source virtual address low uSourceOffset = 0 if not code, UvC if code operation rhT = dest virtual address high T = dest virtual address low TOS = count L = number of words transferred uStackPSave = stack pointer at beginning of operation. ******************************************************************************************} blUpdate: T {new destLow} ← T {destLow} + L {words transferred}, {Continue next line} CarryBr, CANCELBR[$,0F], c1; blTestDestCarry: Rx ← uSourceLowSave {srcLow}, {Continue next line} BRANCH[blNoDestCarry, blDestCarry], c2; blDestCarry: Q ← rhT {destHi} + 1, LOOPHOLE[byteTiming], c3; rhT ← Q LRot0, GOTO[blTestDestCarry], {delay 1 cycle} c1; blNoDestCarry: Rx {sourceLow} ← Rx {sourceLow} + L {words transferred}, CarryBr, c3; blTestSourceCarry: L ← ~ErrnIBnStkp, {Continue next line} L2Disp, BRANCH[blUpdateStack, blSourceCarry], c1; blSourceCarry: TT ← TT {sourceHigh} + 1, CANCELBR[$,0F], c2; rhTT ← TT LRot0, GOTO[blTestSourceCarry], {delay 1 cycle} c3; {****************************************************************************************** If we must interrupt, we must save the state in the stack. This is messy because BlockEqual and BlockTransfer are currently different. Current State: TT = rhTT = source virtual address high Rx = source virtual address low rhT = dest virtual address high T = dest virtual address low TOS = count stack pointer points to next. This is the way the stack should look when we are done: BLEQL BLEQCL BLEQ and BLEQC TOS = srcHi TOS = srcLow TOS = srcLow STK = srcLow STK = count STK = count STK-1 = count STK-1 = destHi STK-1 = destLow STK-2 = destHi STK-2 = destLow STK-3 = destLow BLTL BLTC and BLT TOS = destHi TOS = destLow STK = destLow STK = count STK-1 = count STK-1 = srcLow STK-2 = srcHi STK-3 = srcLow We treat BLEQ just like BLT, and we treat BLEQL just like BLTL. This means that the operands will be reversed in the stack. ******************************************************************************************} blUpdateStack: L2Disp, Q ← rhT {destHigh}, push {destLow/sourceLow}, {Continue next line} DISP4[blStackFormat,0C], c2; blSourceFirst: {BlockTransfer} STK ← Rx {sourceLow}, push {srcHi/count}, {Continue next line} at[0F,10,blStackFormat] BRANCH[blShortSourceFirst,blLongSourceFirst, Long], c3; blShortSourceFirst: L2Disp, GOTO[blStoreCount], c1; blLongSourceFirst: STK ← TT {sourceHi}, push {count}, {Continue next line} L2Disp, GOTO[blStoreCount], c1; blDestFirst: {BlockEqual} STK ← T {destLow}, push {destHi/count}, {Continue next line} at[0E,10,blStackFormat] BRANCH[blShortDestFirst,blLongDestFirst, Long], c3; blShortDestFirst: L2Disp, GOTO[blStoreCount], c1; blLongDestFirst: STK ← Q {destHi}, push {count}, L2Disp, GOTO[blStoreCount], c1; blUpdateChecksum: at[0C,10,blStackFormat] {Two extra words here to handle checksum case.} stackP ← 3 {count}, CANCELBR[$,Checksum], c3; L2Disp, c1; blStoreCount: STK ← TOS {count}, push {sourceLow/destLow}, {continue next line} L2Disp, BRANCH[blSourceLast, blDestLast, Transfer], c2; blSourceLast: {BlockEqual or Checksum} STK ← Rx {sourceLow}, push {srcHi/next}, {Continue next line} L2Disp, BRANCH[blShortSourceLast,blLongSourceLast, Long], c3; blLongSourceLast: MesaIntBr, BRANCH[blStoreLongSourceHiLast, blTestInterrupt, CodeBase], c1; blStoreLongSourceHiLast: {Two extra words here to handle Block Equal Code Long. Not too bad.} STK ← TT {sourceHi}, CANCELBR[$,1], c2; Noop, c3; blShortSourceLast: MesaIntBr , CANCELBR[blTestInterrupt,0F], c1; blDestLast: {BlockTransfer} STK ← T {destLow}, push {destHi/next}, {Continue next line} BRANCH[blShortDestLast,blLongDestLast, Long], c3; blShortDestLast: MesaIntBr, GOTO[blTestInterrupt], c1; blLongDestLast: STK ← Q {destHi}, MesaIntBr, c1; blTestInterrupt: {We came through here because the interrupt request was set or because we crossed a page boundary. If the interrupt request is not on, go back to start on the next page.} Q {sourceOffset} ← uSourceOffset, {Continue next line} ClrIntErr, BRANCH[blNoInterrupt, blInterrupt], c2; blNoInterrupt: {Restore the stack pointer the way it was when all arguments were removed.} stackP ← L, GOTO[blMapSource], c3; blInterrupt: [] ← uWP {wakeups pending}, ZeroBr, c3; {Restore the stack pointer the way it was when all arguments were removed. We must have the original stack pointer in L when we get to TestCount.} stackP ← L, L ← uStackPSave, {next line} BRANCH[blTestWakeupsDisabled, blTestCount {none pending}], c1; blTestWakeupsDisabled: [] ← uWDC {wakeup disable count}, L0 ← restore.int, NZeroBr, c2; Noop, BRANCH[blDoInt {not disabled}, blMapSource {disabled}], c3; blDoInt: stackP ← uStackPSave, CALL[RestoreBlockRegsC1], c1; {RestoreBlockRegs here. Return to bbInit: c2-c2}; {RestoreRRegsAndRhG here. Return to bbInit: c1-c2}; {Rx ← pInt, push, c3, at[restore.int,10,RestoreCallers]}; {TOS ← STK, pop, GOTO[SaveRegs], c1;}