Thursday, 1 March 2012

In Which We Handle RAM Test Failures


We've now got some finely-crafted code which runs across the whole of VIC-20 memory, looking at each byte and deciding if it's working or not. But as I've mentioned previously, there are two distinct failure scenarios - one in which a byte fails inside the 1K or 4K built-in RAM areas, and one in which the byte fails in an expansion area. The former is a disaster, and we'll jump to the .critfail routine shown below; but the latter is something we handle differently, because in that case we just want to mark the block as unavailable and carry on. Check this out:

.testfail
LDA .TESTHI ; [3] ZP get current page number
CMP #$20 ; [2] compare current page against $20 (start of 8K BLK1)
BCS .expfail ; [2/3] current page >= start of 8K BLK1, skip to expansion fail (8K BLK1 / 8K BLK2 / 8K BLK3 / $A000)
CMP #$04 ; [2] compare current page against $04 (start of 3K expansion)
BCC .critfail ; [2/3] current page < start of 3K expansion, critical fail in first 1K
CMP #$10 ; [2] compare current page against $10 (start of main 4K)
BCC .expfail ; [3/2] current page < start of main 4K, skip to expansion fail (3K expansion)
.critfail
LDA #$02 ; [2] if we get here it's a critical failure in first 1K or main 4K block
STA .SCRNCOL ; [4] set VIC register for red border
BNE * ; [3/3] spinloop (VICE spits a JAM exception if we use HLT)
; 21 bytes, 21 / 24 cycles (critical fail) or 8 / 16 cycles (expansion fail)

If the main test loop finds a failure, it jumps into here so that we can find out whether it's a critical failure or not - which is decided simply by looking at the current test address hi-byte in .TESTHI and seeing if it maps into either of the built-in RAM sections below $0400 or between $1000-$2000. If so, the routine drops into the critical failure section where we turn the screen red to tell the user what the problem is, and then spinloop forever (I wanted to use the undocumented HLT instruction here, but VICE gets upset). If it's not a critical section, we carry on...

.expfail
LDX #$00 ; [2] index pointer into table of pages
.nextitem
INX ; [2] increment index
CMP .pagetab,X ; [4] compare failure page against table page
BCC .nextitem ; [3/2] failure page < table page, loop for next entry
LDA .pagetab-1,X ; [4] failure page >= table page, so get next page to test
BEQ .testdone ; [2/3] next page is zero so testing is complete
STA .TESTHI ; [3] ZP save new page for next test cycle
LDA .EXPBITS ; [3] ZP get expansion RAM bitmap
EOR .pagetab+6,X ; [4] turn appropriate bit off (e.g. 011111 ^ 001000 => 010111)
STA .EXPBITS ; [3] ZP save updated bitmap
LDY #$00 ; [2] reset memory test location lo-byte index
BEQ .nextloc ; [3/3] resume testing at new page
; 26 bytes, 86 cycles (worst-case, 3K expansion)

What happens here is moderately cunning; .pagetab is a small table of page numbers (address hi-bytes) which represent address boundaries at which non-critical failures could occur, and we compare the current failure address against each one to see where the failure did actually occur. When we get a match, the next byte down in the table holds the next page at which to resume testing (so we skip the rest of the block which experienced the failure) and we stash that in .TESTHI ready for when we carry on. There's also an even smaller table offset from the first which holds bitmask values corresponding to the expansion bitmap in .EXPBITS, so as we skip the rest of the block we also exclusive-OR that blocks' bit off in the bitmap. If the next page to test is zero, we got to the end of memory, so we exit the whole test loop - otherwise, we branch back to the beginning and start afresh at the new address.

I know. :)

No comments:

Post a Comment