I came up with a cunning plan for the attribute renderer, and I think it's as good as it's going to get - the code weighs-in at 59 bytes, and needs 216 cycles to apply all three attribute styles to the associated glyph pair, which is actually quite a lot better than a few other techniques I tried. I miscalculated the impact of attribute-rendering when I talked about it last time, though - I estimated it might add 250 cycles to the total line-drawing time, but forgot that those 250 cycles (216, as it's turned-out) were per attribute byte, not per line. So where I embarrassingly claimed a projected cycle total still below 10,000 I was hopelessly wrong - even with this logic about as fast as I can make it, any line with an attribute set anywhere on it is going to blow that target almost immediately. I've profiled a fully-loaded line with all 40 characters assigned with all three attributes, and it demands around 13,000 cycles - far too heavy a load for the CPU to run twice in an IRQ.
But this is really about as quick as it can be, I think - so I added a little two-bit toggle which represents a user-controlled and system-controlled double-refresh-inhibitor. The idea is that the ISR calls drawdrty twice per IRQ unless either the user has set a bit explicitly (to tell the OS not to spend more than half the IRQ time drawing lines) or if the first line drawn in that IRQ frame has attributes set on it - so the ISR will always refresh the entire screen in 1/4-second except if the user says not to, or if any line takes longer than about 9500 cycles to draw (in which case refreshing the screen will take up to but no more than the 1/2-second required to draw all 25 lines over 25 IRQs). This works very well, and means the system never blows the IRQ frame-time target because it self-limits on attribute-loaded lines. Equally, if user code needs to run with as much headroom as possible and doesn't care if the screen-refresh isn't quite as quick, it can set the other limiter bit which imposes the same restriction. Here's the attribute render code in its' final form:
.getattr
TAX ; [2] stash attribute byte in .X
LDA _REFRESH ; [3] ZP get auto-refresh byte
ORA #$80 ; [2] set b7 for auto-refresh inhibit
STA _REFRESH ; [3] ZP set auto-refresh byte
TXA ; [2] get attribute byte from .X
AND #000011 ; [2] mask underline bits
BEQ .strkthru ; [2/3] skip underline if not set
TAY ; [2] move index to .Y
LDA _GLYPHDAT+7 ; [3] ZP get glyph underline data byte
ORA _CHARDATA-1,Y ; [5] apply underline overlay
STA _GLYPHDAT+7 ; [3] ZP set glyph underline data byte
.strkthru
TXA ; [2] get attribute byte from .X
AND #110000 ; [2] mask strike-through bits
BEQ .inverse ; [2/3] skip strike-through if not set
LSR ; [2] divide by 16 for overlay offset
LSR ; [2]
LSR ; [2]
LSR ; [2]
TAY ; [2] move index to .Y
LDA _GLYPHDAT+3 ; [3] ZP get glyph strike-through data byte
ORA _CHARDATA-1,Y ; [5] apply strike-through overlay
STA _GLYPHDAT+3 ; [3] ZP set glyph strike-through data byte
.inverse
TXA ; [2] get attribute byte from .X
AND #001100 ; [2] mask inverse-mode bits
BEQ .drawbmp ; [2/3] skip inverse-mode if not set
LSR ; [2] divide by 4 for overlay offset
LSR ; [2]
TAY ; [2] move index to .Y
LDX #$07 ; [2] glyph byte index
.setinv
LDA _GLYPHDAT,X ; [4] ZP get glyph data byte
EOR _CHARDATA-1,Y ; [5] apply inverse overlay
STA _GLYPHDAT,X ; [4] ZP set glyph data byte
DEX ; [2] decrement index
BPL .setinv ; [3/2] loop for next byte
BMI .drawbmp ; [3/3] always branch
You'll notice I have used a little trick for the glyph attribute masks, by defining the first glyph data byte in the Character Generator at $8000 as mask data. This glyph is never displayed, because it's the first one and ASCII codes 0-31 represent control codes rather than displayable characters - so I don't need to waste cycles explicitly setting mask values, as I can just point the code at those first three glyph data bytes and use those.
So we now have a workable, efficient text-rendering engine, and the next thing to do is write the little bit of code that will give us a cursor representing the typing-point.
No comments:
Post a Comment