Applecorn/mainmem.shr.s

2017 lines
71 KiB
ArmAsm

* MAINMEM.SHR.S
* (c) Bobbi 2022 GPLv3
*
* Routines for drawing bitmapped text and graphics in SHR mode
* on Apple IIGS (640x200 4 colour, or 320x200 16 colour.)
*
* This code is in main memory only to save space in aux LC.
*
******************************************************************************
* Data in bank $E1
******************************************************************************
SHRFONTXPLD EQU $A000 ; Explode SHR font to $E1:A000
******************************************************************************
* 21 bytes of persistent storage, also accessed by mainmem code
* TODO: Move to SHRZP maybe
SHRPIXELS DB $00 ; Main memory copy of VDUPIXELS
SHRVDUQ DS 16 ; Main memory copy of VDUQ
SHRGFXFGMASK DB $00 ; Foreground colour mask
SHRGFXFGMSK2 DB $00 ; Copy of foreground colour mask
SHRGFXBGMASK DB $00 ; Background colour mask
SHRGFXACTION DB $00 ; GCOL action for point plotting
* These are all persistent locals (14 bytes of ZP)
SHRXPIXEL EQU SHRZP+0 ; Prev point in screen coords (word)
SHRYPIXEL EQU SHRZP+2 ; Prev point in screen coords (word)
SHRWINLFT EQU SHRZP+4 ; Gfx win - left (0-639) (word)
SHRWINRGT EQU SHRZP+6 ; Gfx win - right (0-639) (word)
SHRWINTOP EQU SHRZP+8 ; Gfx win - top (0-199) (word)
SHRWINBTM EQU SHRZP+10 ; Gfx win - bottom (0-199) (word)
* Colours in the following order.
* For 16 colour modes ...
* BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, ...
* For 4 colour modes ...
* BLACK, RED, YELLOW, WHITE
* GB 0R
PALETTE320 DB $00, $00 ; BLACK
DB $00, $0F ; RED
DB $F0, $00 ; GREEN
DB $F0, $0F ; YELLOW
DB $0F, $00 ; BLUE
DB $0F, $0F ; MAGENTA
DB $FF, $00 ; CYAN
DB $FF, $0F ; WHITE
DB $44, $04 ; Dark grey
DB $88, $0F ; RED (light)
DB $F8, $08 ; GREEN (light)
DB $F8, $0F ; YELLOW (light)
DB $8F, $08 ; BLUE (light)
DB $8F, $0F ; MAGENTA (light)
DB $FF, $08 ; CYAN (light)
DB $AA, $0A ; Light grey
PALETTE640 DB $00, $00 ; BLACK
DB $00, $0F ; RED
DB $F0, $0F ; YELLOW
DB $FF, $0F ; WHITE
DB $00, $00 ; BLACK
DB $00, $0F ; RED
DB $F0, $0F ; YELLOW
DB $F8, $0F ; WHITE
DB $00, $00 ; BLACK
DB $00, $0F ; RED
DB $F0, $0F ; YELLOW
DB $FF, $0F ; WHITE
DB $00, $00 ; BLACK
DB $00, $0F ; RED
DB $F0, $0F ; YELLOW
DB $FF, $0F ; WHITE
* Explode font to generate SHRFONTXPLD table
* This is 2 bytes x 8 rows for each character in 640 mode
* or 4 bytes x 8 rows for each character in 320 mode
SHRXPLDFONT >>> ENTMAIN
LDA #<SHRFONTXPLD ; Use A3L/H to point to ..
STA A3L ; .. start of table to write
LDA #>SHRFONTXPLD
STA A3H
LDA #$E1 ; Memory bank $E1
STA A4L
LDA #32 ; First char number
:L1 JSR SHRXPLDCHAR ; Explode char A
INC A
CMP #128 ; 96 chars in FONT8
BNE :L1
JSR SHRCLR24 ; Clear row 24
>>> XF2AUX,SHRV22RET
* Explode one character to location pointed to by A3L
* On entry: A - character to explode
SHRXPLDCHAR PHA
SEC
SBC #32
STA A1L ; A*8 -> A1L/H
STZ A1H
ASL A1L
ROL A1H
ASL A1L
ROL A1H
ASL A1L
ROL A1H
CLC ; FONT8+A*8 -> A1L/H
LDA A1L
ADC #<FONT8
STA A1L
LDA A1H
ADC #>FONT8
STA A1H
LDY #$00 ; First row of char
:L1 LDA (A1L),Y ; Load row of font
JSR SHRXPLDROW
INY ; Next row of font
CPY #$08 ; Last row?
BNE :L1
PLA
RTS
* Explode one pixel row of user defined graphics char
SHRUSERCHAR >>> ENTMAIN
LDA #<SHRFONTXPLD ; Use A3L/H to point to ..
STA A3L ; .. start of table to write
LDA #>SHRFONTXPLD
STA A3H
LDA #$E1 ; Bank $E1
STA A4L
LDA SHRVDUQ+0 ; Character number
CMP #32 ; < 32? Then bail out
BCC :DONE
SEC ; Otherwise, subtract 32
SBC #32
TAY
LDA #16 ; Bytes/char in 640 mode
LDX SHRPIXELS ; Pixels per byte
CPX #$02 ; 2 is 320-mode (MODE 1)
BNE :S0
LDA #32 ; Bytes/char in 320 mode
:S0 STA :INCREMENT
:L0 CPY #$00
BEQ :S1
CLC
LDA A3L
ADC :INCREMENT
STA A3L
LDA A3H
ADC #$00
STA A3H
DEY
BRA :L0
:S1 LDY #$00
:L1 LDA SHRVDUQ+1,Y ; Row of pixels
JSR SHRXPLDROW
INY
CPY #$08 ; Last row?
BNE :L1
:DONE >>> XF2AUX,VDUXXRET
:INCREMENT DB $00
* Explode one row of pixels. Used by SHRXPLDCHAR & SHRUSERCHAR
* On entry: A contains row of font data
SHRXPLDROW LDX SHRPIXELS ; Pixels per byte
CPX #$02 ; 2 is 320-mode (MODE 1)
BNE :S1
JSR SHRCHAR320
BRA :S2
:S1 JSR SHRCHAR640
:S2 LDX SHRPIXELS ; Pixels per byte
CPX #$02 ; 2 is 320-mode (MODE 1)
BNE :S3
CLC ; 320 mode: add 4 to A3L
LDA A3L
ADC #$04
STA A3L
LDA A3H
ADC #$00
STA A3H
BRA :S4
:S3 CLC ; 640 mode: add 2 to A3L
LDA A3L
ADC #$02
STA A3L
LDA A3H
ADC #$00
STA A3H
:S4 RTS
* Clear text row 24 (0-based index)
SHRCLR24 CLC ; 65816 native mode
XCE
REP #$30 ; 16 bit M & X
MX %00 ; Tell Merlin
LDX #$00
LDA #$00
:L1 STAL $E19800,X
INX
INX
CPX #$0500
BNE :L1
SEC ; 65816 emulation mode
XCE
MX %11 ; Tell Merlin
RTS
* Explode one pixel row of font in 320 mode
* 4 bytes per char, 4 bits per pixel
* On entry: A contains row of font data
SHRCHAR320 PHY ; Preserve Y
LDY #$00 ; Dest byte index
:L0 STZ A2L
LDX #$00 ; Source bit index
:L1 ASL ; MS bit -> C
PHP ; Preserve C
ROL A2L ; C -> LS bit
PLP ; Recover C
PHP
ROL A2L ; C -> LS bit
PLP ; Recover C
PHP
ROL A2L ; C -> LS bit
PLP ; Recover C
ROL A2L ; C -> LS bit
INX
CPX #$02 ; Processed two bits of font?
BNE :L1
PHA ; Preserve partially shifted font
LDA A2L
STA [A3L],Y
PLA ; Recover partially shifted font
INY
CPY #$04 ; Done 4 bytes?
BNE :L0
PLY ; Recover Y
RTS
* Explode one pixel row of font in 640 mode
* 2 bytes per char, 2 bits per pixel
* On entry: A contains row of font data
SHRCHAR640 PHY ; Preserve Y
LDY #$00 ; Dest byte index
:L0 STZ A2L
LDX #$00 ; Source bit index
:L1 ASL ; MS bit -> C
PHP ; Preserve C
ROL A2L ; C -> LS bit
PLP ; Recover C
ROL A2L ; C -> LS bit
INX
CPX #$04
BNE :L1
PHA ; Preserve partially shifted font
LDA A2L
STA [A3L],Y
PLA ; Recover partially shifted font
INY
CPY #$02 ; Done 2 bytes?
BNE :L0
PLY ; Recover Y
RTS
* Handle plotting & unplotting cursors
* On entry: character in A, flags in Y
* pointer to screen address in SHRVDUQ+0..1
SHRCURSM >>> ENTMAIN
PHY ; Preserve flags
PHA ; Preserve character
LDA SHRVDUQ+0 ; Copy pointer to A3L/H
STA A3L
LDA SHRVDUQ+1
STA A3H
LDA #$E1 ; Bank $E1
STA A4L
LDA SHRPIXELS ; Pixels per byte
CMP #$02 ; 2 is 320-mode (MODE 1)
BNE :MODE0
LDA #$04 ; 4 bytes in 320 mode
LDX #$71 ; White/red
BRA :S1
:MODE0 LDA #$02 ; 2 bytes in 640 mode
LDX #%11011101 ; White/red/white/red
:S1 STA :BYTES ; Bytes per char
STX :CURSBYTE
LDA A3L ; LSB
CLC
ADC #<$460 ; $460 is seven rows
STA A3L
LDA A3H ; MSB
ADC #>$460 ; $460 is seven rows
STA A3H
LDY #$00
LDX #$00
PLA ; Recover character
PLP ; Recover flags
BVC :S2 ; VC: Write cursor
INX ; Advance to 2nd half of :SAVEBYTES
INX
INX
INX
:S2 BCC :CURSOROFF ; CC: Remove cursor
:CURSORON
LDAL [A3L],Y ; See if cursor shown
CMP :CURSBYTE
BEQ :DONE ; Cursor shown already, skip
:L1 LDAL [A3L],Y
STA :SAVEBYTES,X ; Preserve bytes under cursor
LDA :CURSBYTE ; Byte of cursor data
STAL [A3L],Y
INX
INY
CPY :BYTES
BNE :L1
>>> XF2AUX,SHRCURSRET
:CURSOROFF
LDAL [A3L],Y ; See if cursor shown
CMP :CURSBYTE
BNE :DONE ; Cursor not shown, skip
:L2 LDA :SAVEBYTES,X ; Restore bytes under cursor
STAL [A3L],Y
INX
INY
CPY :BYTES
BNE :L2
:DONE >>> XF2AUX,SHRCURSRET
:BYTES DB $00 ; 2 for 640-mode, 4 for 320-mode
:CURSBYTE DB $00 ; Cursor byte for mode
:SAVEBYTES DS 8 ; Bytes under cursors
* VDU5 plot char at graphics cursor position
SHRVDU5CH >>> ENTMAIN
CLC ; 65816 native mode
XCE
REP #$30 ; 16 bit M & X
MX %00 ; Tell Merlin
AND #$00FF
STA A1L ; A*16 -> A1L/H
ASL A1L
ASL A1L
ASL A1L
ASL A1L
LDA SHRPIXELS ; Pixels per byte
AND #$00FF
CMP #$02 ; 2 is 320-mode (MODE 1)
BNE :MODE0
LDA #$04 ; 4 bytes per row in MODE 1
ASL A1L ; A*32 -> A1L/H in MODE 1
BRA :S0
:MODE0 LDA #$02 ; 2 bytes per row in MODE 0
:S0 STA :BYTES
CLC ; Add SHRFONTXPLD to A1L/H
LDA A1L
ADC #SHRFONTXPLD
STA A1L
LDA SHRYPIXEL ; y coordinate
SEC
SBC #8 ; Height of this row
CMP SHRWINBTM
BMI :NEWPAGE
LDA SHRYPIXEL
CMP SHRWINTOP
BEQ :S1
BPL :NEWPAGE
:S1 LDA SHRXPIXEL ; x coordinate
CMP SHRWINLFT
BMI :NEWPAGE
CMP SHRWINRGT
BEQ :S2
BPL :NEWPAGE
BRA :S2
:NEWPAGE LDA SHRWINTOP
STA SHRYPIXEL
LDA SHRWINLFT
STA SHRXPIXEL
:S2 SEP #$30 ; 8 bit M & X
MX %11 ; Tell Merlin
LDX SHRYPIXEL ; Screen row (Y-coord)
LDA SHRROWSL,X ; Look up addr (LS byte)
STA A3L ; Stash in A3L
LDA SHRROWSH,X ; Look up addr (MS byte)
STA A3H ; Stash in A3H
LDA #$E1 ; Bank $E1
STA A4L
REP #$30 ; 16 bit M & X
MX %00 ; Tell Merlin
LDX SHRXPIXEL ; Screen col (X-coord)
STX A2L
LSR A2L ; Divide by 2
LDA SHRPIXELS
AND #$00FF
CMP #$02
BEQ :M1 ; MODE 1
LSR A2L ; Divide by 2 again
:M1 LDX A1L ; Index into exploded font
STZ :ROWCTR
:L0
PHX
LDY #$00
STZ :PIXBUF+2 ; Clear bytes 3,4 of shift buf
STZ :PIXBUF+4 ; Clear bytes 5,6 of shift buf
:LOOP LDAL $E10000,X ; Read a word of exploded font
STA :PIXBUF,Y ; Store word to shift buffer
INX
INX
INY
INY
CPY :BYTES
BNE :LOOP
LDA SHRXPIXEL
JSR SHRSHIFT ; Shift :PIXBUF to the right
LDY A2L ; Index into row of pixels
STZ :COLCTR
LDX #$00
INC :BYTES
:L1 LDA :PIXBUF,X ; Read word of exploded font
PHX
SEP #$30 ; 8 bit M & X
MX %11 ; Tell Merlin
JSR SHRPLOTBYTE
REP #$30 ; 16 bit M & X
MX %00 ; Tell Merlin
PLX
INX ; Next byte of font
INY ; Next byte on screen
INC :COLCTR
LDA :COLCTR
CMP :BYTES ; Bytes per row
BNE :L1
DEC :BYTES
PLA ; Restore saved X -> A
CLC ; Add bytes per row
ADC :BYTES
TAX ; Back to X
LDA A3L ; Increment A3L/H to next row
CLC
ADC #$A0
STA A3L
LDA A3H
ADC #$00
STA A3H
INC :ROWCTR
LDA :ROWCTR
CMP #$08 ; 8 rows
BNE :L0
:DONE SEC ; 65816 emulation mode
XCE
MX %11 ; Tell Merlin
>>> XF2AUX,SHRPRCH320RET
* Zero page
:COLCTR EQU TMPZP+0
:ROWCTR EQU TMPZP+2
:BYTES EQU TMPZP+4 ; Bytes per char row
:PIXBUF EQU TMPZP+6 ; Shift buffer (6 bytes)
* Shifts one character row of pixels to the right
* Called in 65816 native mode, 16 bit M & X
* On entry: A - x-coordinate of char
SHRSHIFT MX %00 ; Tell merlin we are 16 bit M&X
PHA
LDA SHRPIXELS ; Pixels per byte
AND #$00FF
CMP #$02 ; 2 is 320-mode (MODE 1)
BNE :MODE0
PLA
AND #$0001 ; Bits to shift in MODE 1
ASL
PHA
:MODE0 PLA
AND #$0003 ; Bits to shift in MODE 0
PHA
LDA :PIXBUF ; Put bytes in big-endian order
XBA
STA :PIXBUF
LDA :PIXBUF+2
XBA
STA :PIXBUF+2
LDA :PIXBUF+4
XBA
STA :PIXBUF+4
PLA
:L1 CMP #$0000
BEQ :S1
LSR :PIXBUF ; Shift :PIXBUF to the right 1 bit
ROR :PIXBUF+2
ROR :PIXBUF+4
LSR :PIXBUF ; Shift right again
ROR :PIXBUF+2
ROR :PIXBUF+4
DEC A
BRA :L1
:S1 LDA :PIXBUF ; Put bytes back in little-endian
XBA
STA :PIXBUF
LDA :PIXBUF+2
XBA
STA :PIXBUF+2
LDA :PIXBUF+4
XBA
STA :PIXBUF+4
RTS
MX %11
* Handle cursor left in VDU5 mode
SHRVDU08 >>> ENTMAIN
CLC ; 65816 native mode
XCE
REP #$30 ; 16 bit M & X
MX %00 ; Tell Merlin
LDA SHRXPIXEL
SEC
SBC #$08 ; Move to previous column
CMP SHRWINLFT
BMI :PREVLINE ; x-pos < SHRWINLFT
STA SHRXPIXEL
BRA :DONE
:PREVLINE LDA SHRYPIXEL
CLC ; Add 8 rows (go up)
ADC #$08
CMP SHRWINTOP
BCS :HOME ; y-pos >= SHRWINTOP
STA SHRYPIXEL
LDA SHRWINRGT
SEC
SBC #$07
STA SHRXPIXEL
BRA :DONE
:HOME LDA SHRWINTOP
STA SHRYPIXEL
LDA SHRWINLFT
STA SHRXPIXEL
:DONE SEC ; 65816 emulation mode
XCE
MX %11 ; Tell Merlin
>>> XF2AUX,VDUXXRET
* Handle cursor right in VDU5 mode
SHRVDU09 >>> ENTMAIN
CLC ; 65816 native mode
XCE
REP #$30 ; 16 bit M & X
MX %00 ; Tell Merlin
LDA SHRXPIXEL
CLC
ADC #$08 ; Advance to next column
CMP SHRWINRGT
BCS :NEWLINE ; x-pos >= SHRWINRGT
STA SHRXPIXEL
BRA :DONE
:NEWLINE LDA SHRWINLFT
STA SHRXPIXEL
JSR SHRVDU5LF
:DONE SEC ; 65816 emulation mode
XCE
MX %11 ; Tell Merlin
>>> XF2AUX,VDUXXRET
* Handle cursor down / linefeed in VDU5 mode
SHRVDU10 >>> ENTMAIN
CLC ; 65816 native mode
XCE
REP #$30 ; 16 bit M & X
MX %00 ; Tell Merlin
JSR SHRVDU5LF
:DONE SEC ; 65816 emulation mode
XCE
MX %11 ; Tell Merlin
>>> XF2AUX,VDUXXRET
* Handle cursor up in VDU5 mode
SHRVDU11 >>> ENTMAIN
CLC ; 65816 native mode
XCE
REP #$30 ; 16 bit M & X
MX %00 ; Tell Merlin
LDA SHRYPIXEL
CLC
ADC #$08 ; Height of row of text
CMP SHRWINTOP
BCS :TOP ; y-pos >= SHRWINTOP
STA SHRYPIXEL
BRA :DONE
:TOP LDA SHRWINTOP
STA SHRYPIXEL
:DONE SEC ; 65816 emulation mode
XCE
MX %11 ; Tell Merlin
>>> XF2AUX,VDUXXRET
* Handle linefeed in VDU5 mode - does the actual work
* Called in 65816 native mode, 16 bit M & X
SHRVDU5LF MX %00 ; Tell Merlin
LDA SHRYPIXEL
SEC
SBC #16 ; Height of this+next row
CMP SHRWINBTM
BCC :NEWPAGE ; Less than 16 rows left
LDA SHRYPIXEL
SEC
SBC #$08
STA SHRYPIXEL
BRA :DONE
:NEWPAGE LDA SHRWINTOP
STA SHRYPIXEL
:DONE RTS
MX %11 ; 8 bit again
* Handle carriage return in VDU5 mode
SHRVDU13 >>> ENTMAIN
CLC ; 65816 native mode
XCE
REP #$30 ; 16 bit M & X
MX %00 ; Tell Merlin
LDA SHRWINLFT
STA SHRXPIXEL
:DONE SEC ; 65816 emulation mode
XCE
MX %11 ; Tell Merlin
>>> XF2AUX,VDUXXRET
* Handle erasing char for VDU127 in VDU5 mode
* Called after SHRVDU08 has already backspaced cursor
SHRVDU127 >>> ENTMAIN
LDX SHRYPIXEL ; Screen row (Y-coord)
LDA SHRROWSL,X ; Look up addr (LS byte)
STA A3L ; Stash in A3L
LDA SHRROWSH,X ; Look up addr (MS byte)
STA A3H ; Stash in A3H
LDA #$E1 ; Bank $E1
STA A4L
LDX SHRPIXELS
CPX #$02
BNE :MODE0
CLC ; 65816 native mode
XCE
REP #$30 ; 16 bit M & X
MX %00 ; Tell Merlin
LDA SHRXPIXEL ; Screen col (X-coord)
LSR A ; Divide by 2
BRA :MODE1
:MODE0 CLC ; 65816 native mode
XCE
REP #$30 ; 16 bit M & X
MX %00 ; Tell Merlin
LDA SHRXPIXEL ; Screen col (X-coord)
LSR A ; Divide by 2
LSR A ; Divide by 4
TAY
LDX #$00
SEP #$30 ; 8 bit M & X
MX %11 ; Tell Merlin
:L1 LDA SHRGFXBGMASK
STAL [A3L],Y
INY
STAL [A3L],Y
DEY
JSR SHRNXTROWM ; Advance A3L/H to next pixel row
INX
CPX #$08 ; Erased all 8 rows?
BNE :L1
BRA :DONE
:MODE1 MX %00 ; Tell Merlin it's 16 bit M&X
TAY
LDX #$00
SEP #$30 ; 8 bit M & X
MX %11 ; Tell Merlin
:L2 LDA SHRGFXBGMASK
STAL [A3L],Y
INY
STAL [A3L],Y
INY
STAL [A3L],Y
INY
STAL [A3L],Y
DEY
DEY
DEY
JSR SHRNXTROWM ; Advance A3L/H to next pixel row
INX
CPX #$08 ; Erased all 8 rows?
BNE :L2
:DONE SEC ; 65816 emulation mode
XCE
>>> XF2AUX,VDUXXRET
* Advance A3L/H to next pixel row on screen
SHRNXTROWM LDA A3L ; Advance A3L/H to next row
CLC
ADC #160
STA A3L
LDA A3H
ADC #$00
STA A3H
RTS
* Plot actions: PLOT k,x,y
* k is in SHRVDUQ+4
* x is in SHRVDUQ+5,SHRVDUQ+6
* y is in SHRVDUQ+7,SHRVDUQ+8
*
* Plot actions:
* $00+x - move/draw lines Where x: 0 - Move relative
* $40+x - plot point 1 - Draw relative FG
* [$50+x - fill triangle] 2 - Draw relative Inv FG
* [$60+x - fill rectangle] 3 - Draw relative BG
* [$90+x - draw circle] 4 - Move absolute
* [$98+x - fill circle] 5 - Draw abs FG
* 6 - Draw abs Inv FG
* 7 - Draw abs BG
* Note: abs/rel handled in auxmem.vdu.s
* TODO: No triangle filling or other fancy ops yet
SHRPLOT >>> ENTMAIN
>>> SHRCOORD ; Convert coordinates
LDA A1L ; Preserve converted x
PHA
LDA A1H
PHA
LDA A2L ; Preserve converted y
PHA
LDA A2H
PHA
LDA SHRVDUQ+4 ; k
AND #$03
CMP #$00 ; Bits 0,1 clear -> just move
BEQ :S2
JSR SHRPLOTCOL ; Handle colour selection
LDA SHRVDUQ+4 ; k
AND #$F0 ; Keep MS nybble
CMP #$00 ; Move or draw line
BNE :S1
JSR SHRLINE
BRA :S2
:S1 CMP #$40 ; Plot point
BNE :BAIL ; Other? Bail out
CLC ; 65816 native mode
XCE
SEP #$30 ; 8 bit M & X
MX %11 ; Tell Merlin
JSR SHRPOINT
SEC ; 65816 emulation mode
XCE
MX %11 ; Tell Merlin
BRA :S2
:S2 PLA ; Store prev pt in screen coords
STA SHRYPIXEL+1
PLA
STA SHRYPIXEL+0
PLA
STA SHRXPIXEL+1
PLA
STA SHRXPIXEL+0
:DONE >>> XF2AUX,GFXPLOTRET
:BAIL PLA
PLA
PLA
PLA
LDA SHRGFXFGMSK2 ; Restore original FG colour
STA SHRGFXFGMASK
BRA :DONE
* Handle colour selection for PLOT
SHRPLOTCOL LDA SHRGFXFGMASK ; Preserve FG colour
STA SHRGFXFGMSK2
LDA SHRVDUQ+4 ; k
AND #$03
CMP #$02 ; Inverse fFG
BNE :S1
LDA SHRGFXFGMASK ; Load FG mask
EOR #$FF ; Negate / invert
INC A
STA SHRGFXFGMASK ; Overwrite GF mask
BRA :DONE
:S1 CMP #$03 ; BG
BNE :DONE
LDA SHRGFXBGMASK ; Load BG mask
STA SHRGFXFGMASK ; Overwrite FG mask
:DONE RTS
* Plot a point
* Called in 65816 native mode, 8 bit M & X
* On entry: A1L/H x-coordinate, A2L/H y-coordinate
SHRPOINT REP #$30 ; 16 bit M & X
MX %00 ; Tell Merlin
LDA A2L ; y coordinate
CMP SHRWINBTM
BMI :OUT
CMP SHRWINTOP
BEQ :S1
BPL :OUT
:S1 LDA A1L ; x coordinate
CMP SHRWINLFT
BMI :OUT
CMP SHRWINRGT
BEQ SHRPOINT2
BPL :OUT
BRA SHRPOINT2
:OUT SEP #$30 ; 8 bit M & X
MX %11 ; Tell Merlin
RTS
SHRPOINT2 SEP #$30 ; 8 bit M & X
MX %11 ; Tell Merlin
LDX A2L ; Screen row (Y-coord)
LDA SHRROWSL,X ; Look up addr (LS byte)
STA A3L ; Stash in A3L
LDA SHRROWSH,X ; Look up addr (MS byte)
STA A3H ; Stash in A3H
LDA #$E1 ; Bank $E1
STA A4L
LDX A1L ; Store X-coord for later
LSR A1H ; Divide by 2
ROR A1L
LDA SHRPIXELS ; Pixels per byte
CMP #$02 ; 2 is 320-mode (MODE 1)
BNE :MODE0
LDY A1L ; Index into row of pixels
TXA
AND #$01 ; Keep LSB bit only
TAX ; Index into :BITS320
LDA :BITS320,X ; Get bit pattern for pixel to set
BRA SHRPLOTBYTE
:MODE0 LSR A1H ; Divide X-coord by 2 again
ROR A1L
LDY A1L ; Index into row of pixels
TXA
AND #$03 ; Keep LSB two bits only
TAX ; Index into :BITS640
LDA :BITS640,X ; Get bit pattern for pixel to set
SHRPLOTBYTE PHA
LDA SHRGFXACTION ; GCOL action
AND #$0007 ; Avoid table overflows
ASL
TAX
PLA ; Recover bit pattern
JMP (:PLOTTBL, X) ; Jump using jump table
:BITS320 DB %11110000 ; Bit patterns for pixel ..
DB %00001111 ; .. within byte
:BITS640 DB %11000000 ; Bit patterns for pixel ..
DB %00110000 ; .. within byte
DB %00001100
DB %00000011
:PLOTTBL DW SHRPLOTSET ; Jump table for GCOL actions
DW SHRPLOTOR
DW SHRPLOTAND
DW SHRPLOTXOR
DW SHRPLOTNOT
DW SHRPLOTNOP
DW SHRPLOTCLR
DW SHRPLOTNOP
* Plot the specified colour (GCOL action 0)
* Pixel bit pattern in A
SHRPLOTSET TAX ; Keep copy of bit pattern
EOR #$FF ; Invert bits
AND [A3L],Y ; Load existing byte, clearing pixel
STA A1L
TXA ; Get bit pattern back
AND SHRGFXFGMASK ; Mask to set colour
ORA A1L ; OR into existing byte
STA [A3L],Y ; Write to screen
RTS
* OR with colour on screen (GCOL action 1)
* Pixel bit pattern in A
SHRPLOTOR AND SHRGFXFGMASK ; Mask to set colour
ORA [A3L],Y ; OR into existing byte
STA [A3L],Y ; Write to screen
RTS
* AND with colour on screen (GCOL action 2)
* Pixel bit pattern in A
SHRPLOTAND TAX ; Keep copy of bit pattern
AND [A3L],Y ; Mask bits to work on
STA A1L
TXA ; Get bit pattern back
AND SHRGFXFGMASK ; Mask to set colour
AND A1L ; AND with screen data
STA A1L
TXA ; Get bit pattern back
EOR #$FF ; Invert
AND [A3L],Y ; Mask remaining bits
ORA A1L ; Combine
STA [A3L],Y ; Write to screen
RTS
* XOR with colour on screen (GCOL action 3)
* Pixel bit pattern in A
SHRPLOTXOR AND SHRGFXFGMASK ; Mask to set colour
EOR [A3L],Y ; EOR into existing byte
STA [A3L],Y ; Write to screen
RTS
* NOT colour on screen (GCOL action 4)
* Pixel bit pattern in A
SHRPLOTNOT TAX ; Keep copy of bit pattern
STX A1L
LDA [A3L],Y ; Load existing byte
EOR #$FF ; Negate / invert existing byte
AND A1L ; Mask with bit pattern
STA A1L
TXA ; Get bit pattern back
EOR #$FF ; Invert bits
AND [A3L],Y ; Mask remaining bits
ORA A1L ; Combine
STA [A3L],Y ; Write to screen
RTS
* NO-OP (GCOL action 5)
* Pixel bit pattern in A
SHRPLOTNOP RTS
* Clear (GCOL action 6)
* Pixel bit pattern in A, and also at top of stack
SHRPLOTCLR EOR #$FF ; Invert bits
AND [A3L],Y ; Load existing byte, clearing pixel
STA [A3L],Y ; Write to screen
RTS
* Bresenham line drawing algorithm, entry point
* x0 is in SHRXPIXEL+0,SHRPIXEL+1
* y0 is in SHRYPIXEL
* x1 in A1L,A1H
* y1 in A2L
* Called in emulation mode.
* Uses TMPZP+0,+1
SHRLINE LDA A2L ; y1
SEC
SBC SHRYPIXEL ; Subtract y0
BPL :S1 ; Skip if +ve
EOR #$FF ; Negate if -ve
INC A
:S1 STA TMPZP+0 ; abs(y1 - y0)
STZ TMPZP+1 ; Pad to 16 bit
CLC ; 65816 native mode
XCE
REP #$30 ; 16 bit M & X
MX %00 ; Tell Merlin
LDA A1L ; Load x1 (A1L,A1H)
SEC
SBC SHRXPIXEL ; Subtract x0
BPL :S2 ; Skip if +ve
EOR #$FFFF ; Negate if -ve
INC A
:S2 CMP TMPZP ; Cmp abs(x1 - x0) w/ abs(y1 - y0)
BCC :YDOM ; abs(x1 - x0) < abs(y1 - y0)
:XDOM LDA SHRXPIXEL ; x0
CMP A1L ; x1
BPL :X1 ; x0 >= x1
JMP SHRLINELO ; x0 < x1
:X1 JSR SHRLINESWAP ; Swap parms
JMP SHRLINELO
:YDOM LDA SHRYPIXEL ; y0
CMP A2L ; y1
BPL :Y1 ; y0 >= y1
JMP SHRLINEHI ; y0 < y1
:Y1 JSR SHRLINESWAP ; Swap parms
JMP SHRLINEHI
* Swap (x0, y0) and (x1, y1)
* Called in 65816 native mode, 16 bit M &X
* Uses TMPZP+0,+1
SHRLINESWAP LDA SHRXPIXEL ; x0
STA TMPZP
LDA A1L ; x1
STA SHRXPIXEL
LDA TMPZP
STA A1L
LDA SHRYPIXEL ; y0
STA TMPZP
LDA A2L ; y1
STA SHRYPIXEL
LDA TMPZP
STA A2L
RTS
* Plot x-dominant line (shallow gradient)
* Called in 65816 native mode, 16 bit M & X. Returns in emulation mode.
SHRLINELO MX %00 ; Tell merlin 16 bit M & X
LDA A1L ; x1
STA :LIM ; We re-use A1L/H later
SEC
SBC SHRXPIXEL ; Subtract x0
STA :DX
LDA A2L ; y1
SEC
SBC SHRYPIXEL ; Subtract y0
STA :DY
LDA #$0001
STA :YI ; yi = 1
LDA :DY
BPL :S1 ; Skip if dy = 0
LDA #$FFFF
STA :YI ; yi = -1
EOR :DY ; Negate dy
INC A
STA :DY ; dy = -dy
:S1 TAY ; dy
ASL ; 2 * dy
STA :DY ; DY now (2 * dy)
SEC
SBC :DX ; (2 * dy) - dx
STA :D ; D = (2 * dy) - dx
LDA SHRYPIXEL ; y0
STA A2L ; y = y0 (re-using A2L/H)
TYA
SEC
SBC :DX
ASL
STA :DX ; DX now (2 * (dy - dx)
LDX SHRXPIXEL ; x = x0
:L1 STX A1L ; Store x-coord for SHRPOINT
PHX
SEP #$30 ; 8 bit M & X
MX %11 ; Tell Merlin
JSR SHRPOINT ; x in A1L/H, y in A2L
REP #$31 ; 16 bit M & X, CLC
MX %00 ; Tell Merlin
PLX
LDA :D
BMI :S2 ; D < 0
ADC :DX
STA :D ; D = D + (2 * (dy - dx))
LDA A2L ; y
CLC ; (Required)
ADC :YI
STA A2L ; y = y + yi
BRA :S3
:S2 ; CLC ; Already CC
ADC :DY
STA :D ; D = D + 2 * dy
:S3 INX
CPX :LIM ; Compare with x1
BCC :L1
SEC ; 65816 emulation mode
XCE
MX %11 ; Tell Merlin
RTS
* Zero page
:DX EQU TMPZP+0 ; dx initially, then (2 * (dy - dx))
:DY EQU TMPZP+2 ; dy initially, then (2 * dy)
:YI EQU TMPZP+4 ; +1 or -1
:D EQU TMPZP+6 ; D
:LIM EQU TMPZP+8 ; x1 gets stashed here
* Plot y-dominant line (steep gradient)
* Called in 65816 native mode, 16 bit M & X. Returns in emulation mode.
SHRLINEHI MX %00 ; Tell Merlin 16 bit M & X
LDA A1L ; x1
SEC
SBC SHRXPIXEL ; Subtract x0
STA :DX
LDA A2L ; y1
STA :LIM ; We re-use A1L/H later
SEC
SBC SHRYPIXEL ; Subtract y0
STA :DY
LDA #$0001
STA :XI ; xi = 1
LDA :DX
BPL :S1 ; Skip if dx = 0
LDA #$FFFF
STA :XI ; xi = -1
EOR :DX ; Negate dx
INC A
STA :DX ; dx = -dx
:S1 TAX ; dx
ASL ; 2 * dx
STA :DX ; DX now (2 * dx)
SEC
SBC :DY ; (2 * dx) - dy
STA :D ; D = (2 * dx) - dy
LDA SHRXPIXEL ; x0
STA :X ; x = x0
TXA
SEC
SBC :DY
ASL
STA :DY ; DY now (2 * (dx - dy)
LDX SHRYPIXEL ; y = y0
:L1 LDA :X
STA A1L ; Store x-coord for SHRPOINT
STX A2L ; Store y-coord for SHRPOINT
PHX
SEP #$30 ; 8 bit M & X
MX %11 ; Tell Merlin
JSR SHRPOINT ; x in A1L/H, y in A2L
REP #$31 ; 16 bit M & X, CLC
MX %00 ; Tell Merlin
PLX
LDA :D
BMI :S2 ; D < 0
ADC :DY
STA :D ; D = D + (2 * (dx - dy))
LDA :X ; x
CLC ; (Required)
ADC :XI
STA :X ; x = x + xi
BRA :S3
:S2 ; CLC ; Already CC
ADC :DX
STA :D ; D = D + 2 * dx
:S3 INX
CPX :LIM ; Compare with y1
BCC :L1
SEC ; 65816 emulation mode
XCE
MX %11 ; Tell Merlin
* PLP ; Resume normal service
RTS
* Zero page
:X EQU TMPZP+0
:DX EQU TMPZP+2 ; dx initially, then (2 * dx)
:DY EQU TMPZP+4 ; dy initially, then (2 * (dx - dy)))
:XI EQU TMPZP+6 ; +1 or -1
:D EQU TMPZP+8 ; D
:LIM EQU TMPZP+10 ; x1 gets stashed here
* Macro to convert high-resolution screen coordinates
* from 1280x1024 to 640x200 or 320x200
* On return: X-coordinate in A1L/H, Y-coordinate in A2L (A2H=0)
SHRCOORD MAC
CLC ; 65816 native mode
XCE
REP #$30 ; 16 bit M & X
MX %00 ; Tell Merlin
* X-coordinate in SHRVDUQ+5,+6 1280/2=640 or 1280/4=320
LDA SHRVDUQ+5
ASL ; Sign bit -> C
ROR SHRVDUQ+5 ; Signed divide /2
LDA SHRPIXELS ; Pixels per byte
AND #$00FF ; Mask
CMP #$02 ; 2 is 320-mode (MODE 1)
BNE SHRCOORDM0
LDA SHRVDUQ+5
ASL ; Sign bit -> C
ROR SHRVDUQ+5 ; Signed divide /2
SHRCOORDM0 LDA SHRVDUQ+5
STA A1L ; Result in A1L/H
* Y-coordinate in SHRVDUQ+7,+8 1024*25/128=200
LDA SHRVDUQ+7
BMI SHRCOORDNEG
ASL ; *2
ADC SHRVDUQ+7 ; *3
ASL ; *6
ASL ; *12
ASL ; *24
ADC SHRVDUQ+7 ; *25
* Clever speedup trick thanks to Kent Dickey @ A2Infinitum
* now we have valid data in acc[15:7], and we want to shift right 7 bits to
* get acc[8:0] as the valid bits. If we left shift one bit and xba,
* we get acc[7:0] in the proper bits, so we just have to bring the bit we
* just shifted out back
* See: https://apple2infinitum.slack.com/archives/CA8AT5886/p1628877444215300
* for code on how to shift left 7 bits
ASL ;
AND #$FF00 ; Mask bits
ADC #0 ; Add in carry (9th bit)
XBA ; Clever trick: fewer shifts
STA A2L ; Into A2L/H
SEC ; Back to emulation mode
XCE
MX %11 ; Tell Merlin
BRA SHRCOORDEND
SHRCOORDNEG MX %00 ; Tell Merlin we are 16 bit
EOR #$FFFF ; Negate
INC A
ASL ; *2
ADC SHRVDUQ+7 ; *3
ASL ; *6
ASL ; *12
ASL ; *24
ADC SHRVDUQ+7 ; *25
* Clever speedup trick thanks to Kent Dickey @ A2Infinitum
* now we have valid data in acc[15:7], and we want to shift right 7 bits to
* get acc[8:0] as the valid bits. If we left shift one bit and xba,
* we get acc[7:0] in the proper bits, so we just have to bring the bit we
* just shifted out back
* See: https://apple2infinitum.slack.com/archives/CA8AT5886/p1628877444215300
* for code on how to shift left 7 bits
ASL ;
AND #$FF00 ; Mask bits
ADC #0 ; Add in carry (9th bit)
XBA ; Clever trick: fewer shifts
EOR #$FFFF ; Negate
INC A
STA A2L ; Into A2L/H
SEC ; Back to emulation mode
XCE
MX %11 ; Tell Merlin
* PLP ; Normal service resumed
SHRCOORDEND EOM
* Another coordinate transform, used by VDU25
* Same as SHRCOORD above, except it is entered in native, 16 bit M & X mode
* Assumes positive coordinates.
* On entry: X is offset into SHRVDUQ to find coordinate
* On return: Coverted coordinats in A1L/H, A2L/H
SHRCOORD2 MX $00 ; Tell Merlin it's 16 bit
* X-coordinate in SHRVDUQ+5,+6 1280/2=640
LSR SHRVDUQ,X ; Unsigned divide /2
LDA SHRPIXELS ; Pixels per byte
AND #$00FF ; Mask
CMP #$02 ; 2 is 320-mode (MODE 1)
BNE SHRCOORD2M0
LSR SHRVDUQ,X ; Unsigned divide /2 again
SHRCOORD2M0 LDA SHRVDUQ,X
STA A1L ; Result in A1L/H
* Y-coordinate in SHRVDUQ+7,+8 1024*25/128=200
LDA SHRVDUQ+2,X
ASL ; *2
ADC SHRVDUQ+2,X ; *3
ASL ; *6
ASL ; *12
ASL ; *24
ADC SHRVDUQ+2,X ; *25
ASL ;
AND #$FF00 ; Mask bits
ADC #0 ; Add in carry (9th bit)
XBA ; Clever trick: fewer shifts
STA A2L ; Into A2L/H
RTS
MX %11 ; Following code is 8 bit again
* Clear the graphics window
SHRVDU16 >>> ENTMAIN
CLC ; 816 native mode
XCE
REP #$30 ; 16 bit M & X
MX %00 ; Tell Merlin
INC SHRWINTOP
INC SHRWINRGT
LDX SHRWINBTM
LDA SHRPIXELS
AND #$00FF
CMP #$02
BNE :M0 ; MODE0
LDA SHRWINLFT
LSR A ; Divide left by 2
INC A ; Treat left column specially
STA :LEFTLIM
LDA SHRWINRGT
LSR A ; Divide right by 2
STA :RIGHTLIM
BRA :S0
:M0 LDA SHRWINLFT
LSR A ; Divide left by 4
LSR A
INC A ; Treat left column specially
STA :LEFTLIM
LDA SHRWINRGT
LSR A ; Divide right by 4
LSR A
STA :RIGHTLIM
BRA :S0
:S0 SEP #$30 ; 8 bit M & X
MX %11 ; Tell Merlin
:L1 LDY :LEFTLIM
LDA SHRROWSL,X ; Look up addr (LS byte)
STA A3L ; Stash in A3L
LDA SHRROWSH,X ; Look up addr (MS byte)
STA A3H ; Stash in A3H
LDA #$E1 ; Bank $E1
STA A4L
LDA SHRGFXBGMASK
:L2 CPY :RIGHTLIM
BCS :S1
STA [A3L],Y
INY
CPY :RIGHTLIM
BRA :L2
:S1 INX
CPX SHRWINTOP
BNE :L1
LDA SHRPIXELS
CMP #$02
BNE :MODE0
LDA SHRWINRGT
AND #$01
TAX
LDA :RIGHT320,X ; Bits to set
JSR SHRVDU16V ; Handle right edge
LDY :LEFTLIM
DEY ; Handle leftmost byte
LDA SHRWINLFT
AND #$01
TAX
LDA :LEFT320,X ; Bits to set
JSR SHRVDU16V ; Handle left edge
BRA :DONE
:MODE0 LDA SHRWINRGT
AND #$03
TAX
LDA :RIGHT640,X ; Bits to set
JSR SHRVDU16V ; Handle right edge
LDY :LEFTLIM
DEY ; Handle leftmost byte
LDA SHRWINLFT
AND #$03
TAX
LDA :LEFT640,X ; Bits to set
JSR SHRVDU16V ; Handle left edge
:DONE REP #$30 ; 16 bit M & X
MX %00 ; Tell Merlin
DEC SHRWINTOP
DEC SHRWINRGT
SEC ; Back to 6502 emu mode
XCE
MX %11 ; Tell Merlin
>>> XF2AUX,SHRCLRRET
:LEFT320 DB %11111111
DB %00001111
:LEFT640 DB %11111111
DB %00111111
DB %00001111
DB %00000011
:RIGHT320 DB %00000000
DB %11110000
:RIGHT640 DB %00000000
DB %11000000
DB %11110000
DB %11111100
* Zero page
:LEFTLIM EQU TMPZP+0 ; 2 bytes of ZP
:RIGHTLIM EQU TMPZP+2 ; 2 bytes of ZP
* Helper routine to draw vertical lines
* Draw line from A1L,SHRWINBTM to A1L,SHRWINBTM in BG colour
* Called in 65816 native mode, 8 bit M & X
* On entry: Y - byte offset into row, A - bit pattern to set
SHRVDU16V PHA
LDX SHRWINBTM
:L1 LDA SHRROWSL,X ; Look up addr (LS byte)
STA A3L ; Stash in A3L
LDA SHRROWSH,X ; Look up addr (MS byte)
STA A3H ; Stash in A3H
LDA #$E1 ; Bank $E1
STA A4L
PLA
PHA
EOR #$FF ; Invert bits
AND [A3L],Y ; Load existing byte, clearing pixel
STA A1L
PLA
PHA
AND SHRGFXBGMASK ; Mask to set colour
ORA A1L ; OR into existing byte
STA [A3L],Y ; Write to screen
INX
CPX SHRWINTOP
BNE :L1
PLA
RTS
* Validate graphics window parms & store if okay
* First 8 bytes of SHRVDUQ: left, bottom, right, top
SHRVDU24 >>> ENTMAIN
CLC ; 65816 native mode
XCE
REP #$30 ; 16 bit M & X
MX %00 ; Tell Merlin
LDA SHRVDUQ+4 ; Right
CMP SHRVDUQ+0 ; Left
BCC :BAD ; right<left
CMP #1280 ; width
BCS :BAD ; right>=width
LDA SHRVDUQ+6 ; Top
CMP SHRVDUQ+2 ; Bottom
BCC :BAD ; top<bottom
CMP #1024 ; height
BCS :BAD ; top>=height
LDX #$00 ; Start of SHRVDUQ
JSR SHRCOORD2 ; Convert left, bottom
LDA A1L ; left converted
STA SHRWINLFT
LDA A2L ; bottom converted
STA SHRWINBTM
LDX #$04 ; 4 byte offset
JSR SHRCOORD2 ; Convert right, top
LDA A1L ; right converted
STA SHRWINRGT
LDA A2L ; top converted
STA SHRWINTOP
SEC ; 65816 emulation mode
XCE
>>> XF2AUX,VDU24RET
:BAD SEC ; 65816 emulation mode
XCE
>>> XF2AUX,VDUXXRET
* Reset graphics window
* Initialize other locals (called on MODE)
SHRVDU26 >>> ENTMAIN
STZ SHRWINLFT+0 ; Graphics window
STZ SHRWINLFT+1
STZ SHRWINBTM+0
STZ SHRWINBTM+1
LDY SHRPIXELS
CPY #$02
BNE :MODE0
LDA #<319
STA SHRWINRGT+0
LDA #>319
STA SHRWINRGT+1
BRA :S1
:MODE0 LDA #<639
STA SHRWINRGT+0
LDA #>639
STA SHRWINRGT+1
:S1 LDA #<199
STA SHRWINTOP+0
LDA #>199
STA SHRWINTOP+1
STZ SHRXPIXEL+0 ; Other locals
STZ SHRXPIXEL+1
STZ SHRYPIXEL+0
STZ SHRYPIXEL+1
>>> XF2AUX,VDU26RET
* Set up default palette
SHRDEFPALM >>> ENTMAIN
LDY #00 ; Palette offset for 320 mode
LDA SHRPIXELS ; Pixels per byte
CMP #$02 ; 2 is 320-mode (MODE 1)
BEQ :S1
LDY #32 ; Palette offset for 640 mode
:S1 LDX #$00
:L1 LDA PALETTE320,Y ; Offset in Y computed above
STAL $E19E00,X ; Palettes begin at $9E00 in $E1
INX
INY
CPX #32 ; 32 bytes in palette
BNE :L1
>>> XF2AUX,SHRDEFPALRET
* Assign a 'physical' colour from the 16 colour palette to a
* 'logical' colour for the current mode
* On entry: A=logical colour, Y=physical colour
SHRPALCHANGE >>> ENTMAIN
TAX
TYA
AND #%00011110 ; Has already been shifted
TAY
LDA SHRPIXELS ; Pixels per byte
CMP #$02 ; 2 is 320-mode (MODE 1)
BEQ :MODE320
TXA
AND #%00000110 ; Has already been shifted
TAX
LDA PALETTE320,Y ; Byte 1 of physical colour
STAL $E19E00,X ; Store in logical slot (4 copies)
STAL $E19E00+8,X
STAL $E19E00+16,X
STAL $E19E00+24,X
LDA PALETTE320+1,Y ; Byte 2 of physical colour
STAL $E19E00+1,X ; Store in logical slot (4 copies)
STAL $E19E00+9,X
STAL $E19E00+17,X
STAL $E19E00+25,X
BRA :DONE
:MODE320 TXA
AND #%00011110 ; Has already been shifted
TAX
LDA PALETTE320,Y ; Byte 1 of physical colour
STAL $E19E00,X ; Store in logical slot
LDA PALETTE320+1,Y ; Byte 2 of physical colour
STAL $E19E00+1,X ; Store in logical slot
:DONE >>> XF2AUX,VDUXXRET
* Assign a custom RGB colour to a 'logical' colour
* On entry: A=GB components, Y=R component, SHRVDUQ=logical colour
SHRPALCUSTOM >>> ENTMAIN
LDX SHRVDUQ
PHA ; Preserve GB components
LDA SHRPIXELS ; Pixels per byte
CMP #$02 ; 2 is 320-mode (MODE 1)
BEQ :MODE320
TXA
AND #%00000110 ; Has already been shifted
TAX
PLA ; Recover GB components
STAL $E19E00,X ; Store in logical slot (4 copies)
STAL $E19E00+8,X
STAL $E19E00+16,X
STAL $E19E00+24,X
TYA ; R component
STAL $E19E00+1,X ; Store in logical slot (4 copies)
STAL $E19E00+9,X
STAL $E19E00+17,X
STAL $E19E00+25,X
BRA :DONE
:MODE320 TXA
AND #%00011110 ; Has already been shifted
TAX
PLA ; Recover GB components
STAL $E19E00,X ; Store in logical slot
TYA ; R component
STAL $E19E00+1,X ; Store in logical slot
:DONE >>> XF2AUX,VDUXXRET
* Table of addresses of SHR rows (in reverse order)
SHRROWSL DB <$9c60
DB <$9bc0
DB <$9b20
DB <$9a80
DB <$99e0
DB <$9940
DB <$98a0
DB <$9800
DB <$9760
DB <$96c0
DB <$9620
DB <$9580
DB <$94e0
DB <$9440
DB <$93a0
DB <$9300
DB <$9260
DB <$91c0
DB <$9120
DB <$9080
DB <$8fe0
DB <$8f40
DB <$8ea0
DB <$8e00
DB <$8d60
DB <$8cc0
DB <$8c20
DB <$8b80
DB <$8ae0
DB <$8a40
DB <$89a0
DB <$8900
DB <$8860
DB <$87c0
DB <$8720
DB <$8680
DB <$85e0
DB <$8540
DB <$84a0
DB <$8400
DB <$8360
DB <$82c0
DB <$8220
DB <$8180
DB <$80e0
DB <$8040
DB <$7fa0
DB <$7f00
DB <$7e60
DB <$7dc0
DB <$7d20
DB <$7c80
DB <$7be0
DB <$7b40
DB <$7aa0
DB <$7a00
DB <$7960
DB <$78c0
DB <$7820
DB <$7780
DB <$76e0
DB <$7640
DB <$75a0
DB <$7500
DB <$7460
DB <$73c0
DB <$7320
DB <$7280
DB <$71e0
DB <$7140
DB <$70a0
DB <$7000
DB <$6f60
DB <$6ec0
DB <$6e20
DB <$6d80
DB <$6ce0
DB <$6c40
DB <$6ba0
DB <$6b00
DB <$6a60
DB <$69c0
DB <$6920
DB <$6880
DB <$67e0
DB <$6740
DB <$66a0
DB <$6600
DB <$6560
DB <$64c0
DB <$6420
DB <$6380
DB <$62e0
DB <$6240
DB <$61a0
DB <$6100
DB <$6060
DB <$5fc0
DB <$5f20
DB <$5e80
DB <$5de0
DB <$5d40
DB <$5ca0
DB <$5c00
DB <$5b60
DB <$5ac0
DB <$5a20
DB <$5980
DB <$58e0
DB <$5840
DB <$57a0
DB <$5700
DB <$5660
DB <$55c0
DB <$5520
DB <$5480
DB <$53e0
DB <$5340
DB <$52a0
DB <$5200
DB <$5160
DB <$50c0
DB <$5020
DB <$4f80
DB <$4ee0
DB <$4e40
DB <$4da0
DB <$4d00
DB <$4c60
DB <$4bc0
DB <$4b20
DB <$4a80
DB <$49e0
DB <$4940
DB <$48a0
DB <$4800
DB <$4760
DB <$46c0
DB <$4620
DB <$4580
DB <$44e0
DB <$4440
DB <$43a0
DB <$4300
DB <$4260
DB <$41c0
DB <$4120
DB <$4080
DB <$3fe0
DB <$3f40
DB <$3ea0
DB <$3e00
DB <$3d60
DB <$3cc0
DB <$3c20
DB <$3b80
DB <$3ae0
DB <$3a40
DB <$39a0
DB <$3900
DB <$3860
DB <$37c0
DB <$3720
DB <$3680
DB <$35e0
DB <$3540
DB <$34a0
DB <$3400
DB <$3360
DB <$32c0
DB <$3220
DB <$3180
DB <$30e0
DB <$3040
DB <$2fa0
DB <$2f00
DB <$2e60
DB <$2dc0
DB <$2d20
DB <$2c80
DB <$2be0
DB <$2b40
DB <$2aa0
DB <$2a00
DB <$2960
DB <$28c0
DB <$2820
DB <$2780
DB <$26e0
DB <$2640
DB <$25a0
DB <$2500
DB <$2460
DB <$23c0
DB <$2320
DB <$2280
DB <$21e0
DB <$2140
DB <$20a0
DB <$2000
SHRROWSH DB >$9c60
DB >$9bc0
DB >$9b20
DB >$9a80
DB >$99e0
DB >$9940
DB >$98a0
DB >$9800
DB >$9760
DB >$96c0
DB >$9620
DB >$9580
DB >$94e0
DB >$9440
DB >$93a0
DB >$9300
DB >$9260
DB >$91c0
DB >$9120
DB >$9080
DB >$8fe0
DB >$8f40
DB >$8ea0
DB >$8e00
DB >$8d60
DB >$8cc0
DB >$8c20
DB >$8b80
DB >$8ae0
DB >$8a40
DB >$89a0
DB >$8900
DB >$8860
DB >$87c0
DB >$8720
DB >$8680
DB >$85e0
DB >$8540
DB >$84a0
DB >$8400
DB >$8360
DB >$82c0
DB >$8220
DB >$8180
DB >$80e0
DB >$8040
DB >$7fa0
DB >$7f00
DB >$7e60
DB >$7dc0
DB >$7d20
DB >$7c80
DB >$7be0
DB >$7b40
DB >$7aa0
DB >$7a00
DB >$7960
DB >$78c0
DB >$7820
DB >$7780
DB >$76e0
DB >$7640
DB >$75a0
DB >$7500
DB >$7460
DB >$73c0
DB >$7320
DB >$7280
DB >$71e0
DB >$7140
DB >$70a0
DB >$7000
DB >$6f60
DB >$6ec0
DB >$6e20
DB >$6d80
DB >$6ce0
DB >$6c40
DB >$6ba0
DB >$6b00
DB >$6a60
DB >$69c0
DB >$6920
DB >$6880
DB >$67e0
DB >$6740
DB >$66a0
DB >$6600
DB >$6560
DB >$64c0
DB >$6420
DB >$6380
DB >$62e0
DB >$6240
DB >$61a0
DB >$6100
DB >$6060
DB >$5fc0
DB >$5f20
DB >$5e80
DB >$5de0
DB >$5d40
DB >$5ca0
DB >$5c00
DB >$5b60
DB >$5ac0
DB >$5a20
DB >$5980
DB >$58e0
DB >$5840
DB >$57a0
DB >$5700
DB >$5660
DB >$55c0
DB >$5520
DB >$5480
DB >$53e0
DB >$5340
DB >$52a0
DB >$5200
DB >$5160
DB >$50c0
DB >$5020
DB >$4f80
DB >$4ee0
DB >$4e40
DB >$4da0
DB >$4d00
DB >$4c60
DB >$4bc0
DB >$4b20
DB >$4a80
DB >$49e0
DB >$4940
DB >$48a0
DB >$4800
DB >$4760
DB >$46c0
DB >$4620
DB >$4580
DB >$44e0
DB >$4440
DB >$43a0
DB >$4300
DB >$4260
DB >$41c0
DB >$4120
DB >$4080
DB >$3fe0
DB >$3f40
DB >$3ea0
DB >$3e00
DB >$3d60
DB >$3cc0
DB >$3c20
DB >$3b80
DB >$3ae0
DB >$3a40
DB >$39a0
DB >$3900
DB >$3860
DB >$37c0
DB >$3720
DB >$3680
DB >$35e0
DB >$3540
DB >$34a0
DB >$3400
DB >$3360
DB >$32c0
DB >$3220
DB >$3180
DB >$30e0
DB >$3040
DB >$2fa0
DB >$2f00
DB >$2e60
DB >$2dc0
DB >$2d20
DB >$2c80
DB >$2be0
DB >$2b40
DB >$2aa0
DB >$2a00
DB >$2960
DB >$28c0
DB >$2820
DB >$2780
DB >$26e0
DB >$2640
DB >$25a0
DB >$2500
DB >$2460
DB >$23c0
DB >$2320
DB >$2280
DB >$21e0
DB >$2140
DB >$20a0
DB >$2000