; The fence. ; ; A guarded range is a fence rather than a wall. Any program may raise one and any ; program may lower it, so nobody is ever told no. What it stops is walking into ; something by accident, which is the failure that costs an afternoon: without it, code ; that gets walked over is found much later, when the wreckage is finally executed and ; the evidence is long gone. ; ; The handler reads the controller's destination registers to say where the refused write ; was aimed. That is the whole point of catching it at the instruction that did it: the ; address is still sitting there to be read. ; ; The addresses here are absolute rather than labels, so that this says the same thing no ; matter what else ends up in the Data Segment. Data Memory is 64K and this program uses ; a hundred bytes of it, so 0x0200 is empty ground. ; ; Correct output is: ; outside ok a write just below the fence went through ; caught 0200 a write inside it was refused, and the handler says where ; caught 01FC a blit that merely clipped it was refused whole ; read ok reading inside the fence is allowed: it guards writing ; lowered ok after GuardOff the same write goes through ; fenced 05 the bank table shows bank 1 present and fenced #Include print.asm #Program start: ; Fence off 0x0200 to 0x020F in Data Memory. INIA 0d1 OUTA 0xEB ; GuardBank = 1 INIA 0x02 OUTA 0xEC RSTA OUTA 0xED ; GuardStart = 0x0200 INIA 0x02 OUTA 0xEE INIA 0x0F OUTA 0xEF ; GuardEnd = 0x020F INIA 0x10 OUTA 0xE8 ; GuardOn ; Just below the fence is ordinary ground. INIA 0x01 OUTA 0xE4 INIA 0xFF OUTA 0xE5 ; Dest = 0x01FF INIA 0d1 OUTA 0xE3 ; DestBank = 1 INIA 0x41 OUTA 0xE9 SETD.0 OutsideOk CALL say ; Inside it is not. INIA 0x02 OUTA 0xE4 RSTA OUTA 0xE5 ; Dest = 0x0200 INIA 0x42 OUTA 0xE9 ; Refused. ; A blit from 0x01FC for eight bytes runs to 0x0203, so it clips the fence. It is ; refused whole rather than doing the four bytes that would have fitted. INIA 0x01 OUTA 0xE4 INIA 0xFC OUTA 0xE5 ; Dest = 0x01FC INIA 0d1 OUTA 0xE0 RSTA OUTA 0xE1 OUTA 0xE2 ; Source = bank 1, 0x0000 OUTA 0xE6 INIA 0d8 OUTA 0xE7 ; Length = 8 INIA 0x01 OUTA 0xE8 ; Refused. ; Reading inside a fence is allowed. A debugger has to be able to see what is being ; protected. INIA 0d1 OUTA 0xE0 INIA 0x02 OUTA 0xE1 RSTA OUTA 0xE2 ; Source = 0x0200 INA 0xE9 SETD.0 ReadOk CALL say ; Lower it, and the write that was refused goes through. INIA 0x11 OUTA 0xE8 ; GuardOff INIA 0x02 OUTA 0xE4 RSTA OUTA 0xE5 INIA 0x43 OUTA 0xE9 SETD.0 LoweredOk CALL say ; Raise it again, and read the flags back out of the bank table, where a fence is ; published along with everything else about a bank. INIA 0x10 OUTA 0xE8 INIA 0d2 OUTA 0xE0 RSTA OUTA 0xE1 INIA 0d8 OUTA 0xE2 ; Bank 1's record begins at 8. SETD.0 Fenced CALL printString CALL blankSpace INA 0xE9 CALL printByteHex CALL lineFeed HALT ; Says where the refused write was aimed, by reading the controller's own registers. guardHandler: SETD.0 Caught CALL printString CALL blankSpace INA 0xE4 ; DestHigh CALL printByteHex INA 0xE5 ; DestLow CALL printByteHex CALL lineFeed ; Step past the refused instruction. Every write here is an opcode and a port. MVSD.0 DPUP.0 0d14 LDA.0 CCF INIB 0d2 ADD MVQA STA.0 BRC stepCarried RETI stepCarried: DECD.0 LDA.0 INCA STA.0 RETI say: CALL printString CALL lineFeed RET #Data OutsideOk: "outside ok" Caught: "caught" ReadOk: "read ok" LoweredOk: "lowered ok" Fenced: "fenced" #Vectors GuardViolation guardHandler