*----------------------------------------------------------- Title[Block.mc...November 8, 1982 9:29 AM...Taft]; * Block Transfers (PrincOps, chapter 8), except BITBLT *----------------------------------------------------------- % CONTENTS, by order of occurence Word boundary block transfers BLT Block Transfer BLTL Block Transfer Long BLTLR Block Transfer Long Reversed BLTC Block Transfer Code BLTCL Block Transfer Code Long CKSUM Checksum Block comparisons BLEL Block Equal Long BLECL Block Equal Code Long Byte boundary block transfers BYTBLT Byte Block Transfer (not implemented) BYTBLTR Byte Block Transfer Reversed (not implemented) Bit boundary block transfers BITBLT Bit Block Transfer (in BitBlt.mc) TXTBLT Text Block Transfer (not implemented) % TopLevel; *----------------------------------------------------------- IFUR[BLT, 1, MemBase[BR34]]; * Block Transfer * DO * dest: POINTER _ Pop[]; count: CARDINAL _ Pop[]; source: POINTER _ Pop[]; * IF count=0 THEN EXIT; * StoreMds[dest]^ _ FetchMds[source]^; * Push[source+1]; Push[count-1]; Push[dest+1]; * IF InterruptPending[] THEN {PC _ savedPC; EXIT}; * ENDLOOP; *----------------------------------------------------------- * Set up both BR34 and BR35 as copies of MDS T_ A0, BRHi_ MDSHi; T_ Stack&-2, BRLo_ T; RTemp0_ T, FlipMemBase; * RTemp0_ dest T_ A0, BRHi_ MDSHi; BRLo_ T, Branch[BLTCommon]; *----------------------------------------------------------- IFUR[BLTL, 1, MemBase[BR34]]; * Block Transfer Long * DO * dest: LONG POINTER _ PopLong[]; count: CARDINAL _ Pop[]; * source: LONG POINTER _ PopLong[]; * IF count=0 THEN EXIT; * Store[dest]^ _ Fetch[source]^; * PushLong[source+1]; Push[count-1]; PushLong[dest+1]; * IF InterruptPending[] THEN {PC _ savedPC; EXIT}; * ENDLOOP; *----------------------------------------------------------- BRHi_ Stack&-1; * BR34_ dest BRLo_ Stack&-1; T_ Stack&-2, FlipMemBase; * T_ count BRLo_ Stack&+1; * BR35_ source BRHi_ Stack&+3; RTemp0_ A0; * Offsets zero relative to BRs RTemp1_ A0, Call[BLTSetupTransfer]; * See comments under BLTSetupTransfer for how this loop works. * At top of loop, StkP addresses high word of dest. Subroutine; BLTLLoop: StkP-2, CoReturn; T_ (Stack&-2)-(Q_ T), * T_ words done this time, Q_ words remaining Branch[BLTLDone, ALU=0]; Stack&+1_ (Stack&+1)+T; * Advance source pointer on stack Stack&+1_ A_ Stack&+1, XorSavedCarry; Stack&+1_ Q; * Update count on stack Stack&+1_ (Stack&+1)+T; * Advance dest pointer on stack Stack_ A_ Stack, XorSavedCarry, Branch[BLTLLoop]; TopLevel; BLTLDone: StkP-1, NextOpcode; *----------------------------------------------------------- NewOp; ESCEntry[BLTLR]; * Block Transfer Long Reversed * DO * dest: LONG POINTER _ PopLong[]; count: CARDINAL _ Pop[]; * source: LONG POINTER _ PopLong[]; * IF count=0 THEN EXIT; * Store[dest+count-1]^ _ Fetch[source+count-1]^; * PushLong[source]; Push[count-1]; PushLong[dest]; * IF InterruptPending[] THEN {PC _ savedPC; EXIT}; * ENDLOOP; *----------------------------------------------------------- * This is implemented in-line and is not optimized for high transfer rate. * Making the common code handle BLTs in both directions would make BLTSetupTransfer * a lot more complicated and slow down the other (presumably more common) cases. MemBase_ BR34, Call[BRHiGetsStackPop]; * BR34_ dest BRLo_ Stack&-1; T_ (Stack&-1)-1, FlipMemBase, * T_ count-1 Call[BRHiGetsStackPop]; * BR35_ source BRLo_ Stack&+2; BLTLRLoop: Stack_ (Fetch_ T)+1, FlipMemBase; PD_ T, Branch[BLTLRInterrupt, Reschedule]; BLTLRNoInterrupt: T_ (Store_ T)-1, DBuf_ MD, FlipMemBase, Branch[BLTLRLoop, ALU#0]; T_ MD, StkP-3, NextOpcode; * Here if an interrupt is (possibly) pending. * The intermediate state (just the count) is on the stack, * so it is OK to exit to the interrupt handler at this point. BLTLRInterrupt: T_ MD, Call[InterruptLongRunningOpcode]; T_ (Stack)-1, Branch[BLTLRNoInterrupt]; * Interrupt not pending after all *----------------------------------------------------------- IFUR[BLTC, 1, MemBase[CB]]; * Block Transfer Code * DO * dest: POINTER _ Pop[]; count: CARDINAL _ Pop[]; source: CARDINAL _ Pop[]; * IF count=0 THEN EXIT; * StoreMds[dest]^ _ ReadCode[source]; * Push[source+1]; Push[count-1]; Push[dest+1]; * IF InterruptPending[] THEN {PC _ savedPC; EXIT}; * ENDLOOP; *----------------------------------------------------------- * We will use MDS and CB as dest and source BRs, since they are an even-odd pair. RTemp0_ Stack&-2; * dest BLTCommon: * BLT enters here RTemp1_ Stack&+1; * source T_ Stack&+1, Call[BLTSetupTransfer]; * T_ count * See comments under BLTSetupTransfer for how this loop works. * At top of loop, StkP addresses dest. Subroutine; BLTLoop: CoReturn; * Transfer one munch Q_ RTemp0, Branch[BLTDone, ALU=0]; Stack&-1_ Q; * Put intermediate state on stack Stack&-1_ T; T_ RTemp1; Stack&+2_ T, Branch[BLTLoop]; TopLevel; BLTDone: StkP-3, NextOpcode; *----------------------------------------------------------- IFUR[BLTCL, 1, MemBase[BR34]]; * Block Transfer Code Long * DO * dest: LONG POINTER _ PopLong[]; count: CARDINAL _ Pop[]; * source: CARDINAL _ Pop[]; * IF count=0 THEN EXIT; * Store[dest]^ _ ReadCode[source]; * Push[source+1]; Push[count-1]; PushLong[dest+1]; * IF InterruptPending[] THEN {PC _ savedPC; EXIT}; * ENDLOOP; *----------------------------------------------------------- * Load dest into BR34 and set up BR35 as copy of CB BRHi_ Stack&-1; * BR34_ dest BRLo_ Stack&-2; T_ Stack&+1, MemBase_ CB; * T_ source RTemp1_ T, DummyRef_ 0S; * RTemp1_ source, read CB RTemp0_ A0, MemBase_ BR34, * RTemp0_ dest offset of zero Call[BRGetsVA]; * BR34_ CB T_ Stack&+2, Call[BLTSetupTransfer]; * T_ count * See comments under BLTSetupTransfer for how this loop works. * At top of loop, StkP addresses high word of dest. Subroutine; BLTCLLoop: StkP-2, CoReturn; T_ (Stack&-1)-(Q_ T), * T_ words done this time, Q_ words remaining Branch[BLTCLDone, ALU=0]; Stack&+1_ (Stack&+1)+T; * Advance source pointer on stack Stack&+1_ Q; * Update count on stack Stack&+1_ (Stack&+1)+T; * Advance dest pointer on stack Stack_ A_ Stack, XorSavedCarry, Branch[BLTCLLoop]; TopLevel; BLTCLDone: StkP-2, NextOpcode; *----------------------------------------------------------- BLTSetupTransfer: * Subroutine to handle the major work of various flavors of Block Transfer. * Does PreFetches and deals properly with interrupts and page faults. * Arranged as a coroutine pair with the caller, and coreturns once per munch * to permit the caller to copy intermediate state back onto the stack. * Thus, this routine need not know how the BLT arguments are arranged on the stack. * The code is careful to touch all required memory data before altering * the intermediate state so as to work correctly in the face of page faults. * This routine uses a pair of base registers of the caller's choosing for the * bases of the source and destination blocks; they must be an even-odd pair. * Calling sequence: * ; * ; * ; * RTemp0_ destination word address (base-relative); * RTemp1_ source word address (base-relative); * T_ word count; * MemBase_ source BR * Call[BLTSetupTransfer]; * Returns with MemBase = source BR * Subroutine; * -- This is very important -- * Loop: CoReturn; * Returns with MemBase = source BR, * RTemp0, RTemp1, and T updated, * Branch[Done, ALU=0]; * ALU = T = remaining count * ; * Branch[Loop]; *----------------------------------------------------------- Subroutine; * Initially call here with word count in T. RTemp2_ T, FlipMemBase; * Dest BR * See whether the destination address = source address +1. * The regular BLT inner loop does not handle that case correctly. DummyRef_ RTemp0, FlipMemBase, T_ MD, * Compute destination VA Branch[BLTCountZero, ALU=0]; * Branch if nothing to do RTemp3_ VALo; T_ (RTemp1)+1, Q_ VAHi; DummyRef_ T, T_ Q; * Compute source VA+1 RTemp3_ (RTemp3) XOR (VALo); * Check for resulting VAs equal T_ T XOR (VAHi); RTemp3_ (RTemp3) OR T, CoReturn; * RTemp3_ 0 iff dest = source+1 * On first coreturn, transfer ((RTemp2-1) mod 20b)+1 words. T_ (RTemp2)-1; PD_ RTemp3; * Recall which way to do the BLT T_ T AND (17C), Branch[BlkSMunchEntry, ALU=0]; * BLTSetupTransfer (cont'd) * T = number of words -1 for this transfer; MemBase = source BR. * Before starting the transfer, touch the last word of the source * and destination blocks (and store into the destination), to force * any faults that would occur in mid-transfer to happen now. * Need not also touch the first word, since a fault on it will * abort the loop before it has done anything permanent. BLTMunchEntry: T_ (RTemp1)+(Q_ T), Branch[BLTInterrupt, Reschedule]; BLTNoInterrupt: RTemp3_ (Fetch_ T)+(20C); * Fetch last source word PreFetch_ RTemp3, FlipMemBase; T_ (RTemp0)+Q; RTemp3_ (Fetch_ T)+(20C); * Fetch last destination word PreFetch_ RTemp3, RTemp3_ MD; RTemp2_ (RTemp2)-(Cnt_ Q)-1; * Update word count Store_ T, DBuf_ RTemp3, FlipMemBase; *Dirty last destination word RTemp1_ (Fetch_ RTemp1)+1, Branch[BLTMunchExit, Cnt=0&-1]; BLTWordLoop: * Inner loop: 2 instructions per word transferred. RTemp1_ (Fetch_ RTemp1)+1, T_ MD, FlipMemBase; RTemp0_ (Store_ RTemp0)+1, DBuf_ T, FlipMemBase, Branch[BLTWordLoop, Cnt#0&-1]; BLTMunchExit: FlipMemBase; * Dest BR RTemp0_ (Store_ RTemp0)+1, DBuf_ MD, FlipMemBase; T_ RTemp2, CoReturn; * On each subsequent coreturn, transfer 20b words. T_ 17C, Branch[BLTMunchEntry]; * Here if an interrupt is (possibly) pending. * The caller has just finished putting the intermediate state on the stack, * so it is OK to exit to the interrupt handler at this point. TopLevel; BLTInterrupt: RTemp3_ Link, Call[InterruptLongRunningOpcode]; Link_ RTemp3; Subroutine; T_ (RTemp1)+Q, Branch[BLTNoInterrupt]; * Interrupt not pending after all * BLTSetupTransfer (cont'd) * Here to handle the "destination = source+1" case. * This is implemented as a simple replication of the first source word * throughout the destination block. * T = number of words -1 for this transfer; MemBase = source BR. * First, do PreFetches for the next transfer. * (Need not touch block beforehand, since this case of BLT is idempotent.) BlkSMunchEntry: RTemp1_ T_ (Fetch_ RTemp1)+(Q_ T)+1, * Updated source ptr = last dest Branch[BlkSInterrupt, Reschedule]; BlkSNoInterrupt: RTemp3_ T+(20C), T_ MD; * T_ source word to be replicated PreFetch_ RTemp3, FlipMemBase; RTemp2_ (RTemp2)-(Cnt_ Q)-1; * Update word count BlkSWordLoop: * Inner loop: 1 instruction per word transferred. RTemp0_ (Store_ RTemp0)+1, DBuf_ T, Branch[BlkSWordLoop, Cnt#0&-1]; BlkSMunchExit: T_ RTemp2, B_ MD, FlipMemBase, CoReturn; * On each subsequent coreturn, transfer 20b words. T_ 17C, Branch[BlkSMunchEntry]; * Here if word count = 0 on entry. Force caller to quit immediately. BLTCountZero: T_ A0, CoReturn; Branch[.-1]; * Here if an interrupt is (possibly) pending. * The caller has just finished putting the intermediate state on the stack, * so it is OK to exit to the interrupt handler at this point. TopLevel; BlkSInterrupt: RTemp3_ Link, T_ MD, Call[InterruptLongRunningOpcode]; Link_ RTemp3; Subroutine; T_ RTemp1, Branch[BlkSNoInterrupt]; * Interrupt not pending after all *----------------------------------------------------------- BRGetsVA: * Subroutine to facilitate copying one base register to another. * Typical call: * MemBase_
; * DummyRef_ 0S; * MemBase_
, Call[BRgetsVA]; * Clobbers T *----------------------------------------------------------- Subroutine; T_ VAHi; BRHi_ T; T_ VALo; BRLo_ T, Return; TopLevel; *----------------------------------------------------------- NewOp; ESCEntry[CKSUM], * Checksum * cksum: CARDINAL; * DO * count: CARDINAL _ Pop[]; source: LONG POINTER _ PopLong[]; cksum _ Pop[]; * IF count=0 THEN EXIT; * Push[Checksum[s, Fetch[p]^]; PushLong[source+1]; PushLong[count-1]; * IF InterruptPending[] THEN {PC _ savedPC; EXIT}; * ENDLOOP; * Push[cksum]; *----------------------------------------------------------- StkP-1; RTemp0_ A0, MemBase_ LPtr, Call[BRHiGetsStackPop]; * LPtr_ source BRLo_ Stack&+2; * Come here once per munch. RTemp0 contains LPtr-relative address. * StkP addresses count (words remaining). * On the first iteration, checksum ((count-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&-3)-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]; CSNoInterrupt: T_ (Fetch_ T)+(20C); PreFetch_ T, T_ Stack&+3, Stack&+3_ MD; * T_ cksum * All possible faults have happened by this point. Stack&-2_ (Stack&-2)-(Cnt_ Q)-1; * Update word count RTemp0_ (Fetch_ RTemp0)+1; Stack&+1_ (Stack&+1)+Q+1; * Update long pointer on stack Stack&+1_ A_ Stack&+1, XorSavedCarry; * Inner loop: 3 instructions per word. T = cksum, StkP adresses count. CSWordLoop: T_ T+MD, StkP-3, Branch[CSWordExit, Cnt=0&-1]; RTemp0_ (Fetch_ RTemp0)+1, Branch[.+2, Carry]; CSAddNoCarry: * ALU=0 iff came from CSWordExit Stack&+3_ T_ T LCY 1, DblBranch[CSWordLoop, CSMunch, ALU#0]; CSAddCarry: Stack&+3_ T_ (T+1) LCY 1, DblBranch[CSWordLoop, CSMunch, ALU#0]; CSWordExit: PD_ A0, DblBranch[CSAddCarry, CSAddNoCarry, Carry]; * Here when count=0. If result is -0, change it to +0. StkP addresses cksum. CSDone: PD_ (Stack)+1; * Carry iff sum=177777 Stack_ A_ Stack, XorSavedCarry, NextOpcode; CSInterrupt: T_ MD, Call[InterruptLongRunningOpcode]; T_ (RTemp0)+Q, Branch[CSNoInterrupt]; *----------------------------------------------------------- NewOp; ESCEntry[BLEL]; * Block Equal Long * DO * ptr1: LONG POINTER _ PopLong[]; count: CARDINAL _ Pop[]; * ptr2: LONG POINTER _ PopLong[]; * IF count=0 THEN {Push[TRUE]; EXIT}; * IF Fetch[ptr1]^ # Fetch[ptr2]^ THEN {Push[FALSE]; EXIT}; * PushLong[ptr2+1]; Push[count-1]; PushLong[ptr1+1]; * IF InterruptPending[] THEN {PC _ savedPC; EXIT}; * ENDLOOP; *----------------------------------------------------------- StkP-3, MemBase_ BR35, Call[BRHiGetsStackPop]; * BR35_ ptr2 RTemp0_ TIOA&StkP; * StkP for result BRLo_ Stack&+2, Branch[BLECommon]; *----------------------------------------------------------- NewOp; ESCEntry[BLECL]; * Block Equal Code Long * DO * ptr: LONG POINTER _ PopLong[]; count: CARDINAL _ Pop[]; * offset: CARDINAL _ Pop[]; * IF count=0 THEN {Push[TRUE]; EXIT}; * IF Fetch[ptr]^ # ReadCode[offset] THEN {Push[FALSE]; EXIT}; * Push[offset+1]; Push[count-1]; PushLong[ptr+1]; * IF InterruptPending[] THEN {PC _ savedPC; EXIT}; * ENDLOOP; *----------------------------------------------------------- StkP-3, MemBase_ CB; * Set up BR35 as copy of CB DummyRef_ Stack, T_ MD; RTemp0_ TIOA&StkP; * StkP for result StkP+1, MemBase_ BR35, Call[BRGetsVA]; BLECommon: T_ (Stack&+2)-1, MemBase_ BR34, * T_ count-1 Call[BRHiGetsStackPop]; * BR34_ ptr1 PD_ A0, BRLo_ Stack&-1, Branch[BLEDone, Carry']; * Branch if count was initially zero * The block equal loop is not optimized for high speed, * since in normal use the blocks being compared are typically quite small. * This implementation does the compare in descending order of addresses * so as not to need to increment the pointers. BLELoop: Stack_ (Fetch_ T)+1, FlipMemBase, Branch[BLERetFalse, ALU#0]; T_ (Fetch_ T)-1, RTemp1_ MD, FlipMemBase, Branch[BLEInterrupt, Reschedule]; BLENoInterrupt: PD_ (RTemp1)#MD, Branch[BLELoop, Carry]; BLEDone: StkP_ RTemp0, Branch[.+2, ALU=0]; * Set StkP for result Stack_ A0, NextOpcode; * Return false Stack_ 1C, NextOpcode; * Return true BLERetFalse: PD_ T-T-1, Branch[BLEDone]; * Here if an interrupt is (possibly) pending. * The intermediate state (just the count) is on the stack, * so it is OK to exit to the interrupt handler at this point. BLEInterrupt: T_ MD, Call[InterruptLongRunningOpcode]; T_ (Stack)-1, Branch[BLENoInterrupt]; * Interrupt not pending after all *----------------------------------------------------------- * Unimplemented opcodes *----------------------------------------------------------- ESCOpcodeUnimpl[BYTBLT]; * Byte Block Transfer ESCOpcodeUnimpl[BYTBLTR]; * Byte Block Transfer Reversed ESCOpcodeUnimpl[TXTBLT]; * Text Block Transfer