OSBYTE 20; Cursor editing mode; Software scroll up/downwards; Exchange four VDU variables; Plot character at graphics cursor; Graphics cursor handling; MODE 7 character conversion; Setup soft character definitions - 872 bytes (5.3%)


§1. Subtract the number of bytes in a row from AX.

 On Entry:
       A = High byte of 16 bit value
       X = Low byte of 16 bit value
.subtractNumberOfBytesInARowFromAX = $ccf8
    PHA                                                 store A
    TXA                                                 A=X
    SEC                                                 set carry for subtraction
    SBC .vduBytesPerCharacterRowLow                     bytes per character row
    TAX                                                 result goes into X
    PLA                                                 recall A
    SBC .vduBytesPerCharacterRowHigh                    bytes per character row
    CMP .vduStartScreenAddressHighByte                  high byte of screen RAM address
.exit6 = $cd06
    RTS                                                 

§2. OSBYTE 20 - 'Explode' soft character definitions.

 This sets how much RAM is reserved for custom ('soft') character definitions. These areas
 of memory are sometimes known as 'font zones'.

 On Entry:
       X   memory assigned          character range   notes
       0   $0C00->$0CFF               $80-$8F         minimum memory assigned ('imploded')
       1   OSHWM->OSHWM+$FF           $A0-$BF         includes allocations above
       2   OSHWM+$100->OSHWM+$1FF     $C0-$DF         includes allocations above
       3   OSHWM+$200->OSHWM+$2FF     $E0-$FF         includes allocations above
       4   OSHWM+$300->OSHWM+$3FF     $20-$3F         includes allocations above
       5   OSHWM+$400->OSHWM+$4FF     $40-$5F         includes allocations above
       6   OSHWM+$500->OSHWM+$5FF     $60-$7F         includes allocations above

 Here OSHWM refers to the value of .defaultOSHWM, which holds the value of OSHWM before any
 font explosions.
.osbyte20EntryPoint = $cd07
    LDA #%00001111                                      
    STA .vduFontFlags                                   characters 160-255 have soft
                                                        character definitions
    LDA #>.softCharacterDefinitions                     page for software character
                                                        definitions
    LDY #.vduFontZoneAddressesHigh7 - .vduFontZoneAddressesHigh1     set loop counter
-
    STA .vduFontZoneAddressesHigh1,Y                    set all font zone addresses (high
                                                        byte) to $0C
    DEY                                                 to indicate the page available
    BPL -                                               for user character definitions

    CPX #7                                              
    BCC +                                               if (X < 7, i.e. parameter is in
                                                        range) then branch
    LDX #6                                              X=6
+
    STX .softCharacterDefinitionsSwitch                 character definition explosion switch
    LDA .defaultOSHWM                                   A = initial OSHWM value, before font
                                                            explosions.
    LDX #0                                              X = 0, loop counter
-
    CPX .softCharacterDefinitionsSwitch                 character definition explosion switch
    BCS +                                               
    LDY .softCharacterRamAllocationTable,X              Y = RAM allocation offset
    STA .vduFontZoneAddressesHigh1,Y                    store in relevant font zone address
    ADC #1                                              add 1 page (carry clear)
    INX                                                 X=X+1
    BNE -                                               if (X is not zero) then branch (loop
                                                        back)
+
    STA .currentOSHWM                                   store new value of PAGE (OSHWM)
    TAY                                                 Y=A
    BEQ .exit6                                          if (zero) then branch (return)

    LDX #.romServiceCallFontImplosionExplosionWarning   } issue ROM service call to let ROMs
                                                        } know of the font explosion and
    JMP .osbyte143EntryPoint                            } to let languages know OSHWM
                                                        } is changing. Y is new PAGE number

§3. moveTextCursorToNextLine.

.moveTextCursorToNextLine = $cd3f
    LDA #2                                              A=2 to check if scrolling disabled
    BIT .vduStatusByte                                  test VDU status byte
    BNE +                                               if (scrolling is disabled) then
                                                        branch
    BVC .exit8                                          if (cursor editing mode is disabled)
                                                        then return
+
    LDA .vduTextWindowBottom                            bottom edge of text window
    BCC +                                               if (carry clear on entry) then branch
    LDA .vduTextWindowTop                               get top of text window
+
    BVS .moveTextCursorToNextLineCursorEditing          if (cursor editing mode enabled)
                                                        then branch
    STA .vduTextCursorYPosition                         set current text line
    PLA                                                 pull return link from stack
    PLA                                                 
    JMP .setCursorSoftwareAndHardwarePosition           set cursor position

§4. moveTextCursorToNextLineCursorEditing.

.moveTextCursorToNextLineCursorEditing = $cd59
    PHP                                                 push flags
    CMP .vduTextInputCursorYCoordinate                  Y coordinate of text input cursor
    BEQ .pullAndExit                                    if (A = line count) then branch
                                                        (exit)
    PLP                                                 get back flags
    BCC .incYCoordinate                                 
    DEC .vduTextInputCursorYCoordinate                  Y coordinate of text input cursor
.exit7 = $cd65
    RTS                                                 

§5. incYCoordinate.

.incYCoordinate = $cd66
    INC .vduTextInputCursorYCoordinate                  Y coordinate of text input cursor
    RTS                                                 

§6. Set up a write cursor.

 When entering a line of text, if the user presses a cursor key then two cursors are shown:
 The WRITE cursor is shown at the point of text entry (looks like a solid block character),
 and is the position at which new characters will be input.
 The READ cursor (shown as a flashing underline) is positioned by the cursor keys.
 The COPY key reads the character under the READ cursor and enters it at WRITE cursor.

 Editing finishes when RETURN is pressed.
.setUpWriteCursor = $cd6a
    PHP                                                 save flags
    PHA                                                 save A
    LDY .vduBytesPerCharacter                           
    DEY                                                 Y = bytes per character - 1
    BNE .restoreWriteCursorNonMODE7                     if (not MODE 7) then branch
    LDA .vduMODE7CursorCharacter                        get MODE 7 write character
                                                        cursor character $7F
    STA (.vduWriteCursorScreenAddressLow),Y             store it at top scan line of current
                                                        character
.pullTwiceAndExit = $cd77
    PLA                                                 pull A
.pullAndExit = $cd78
    PLP                                                 pull flags
.exit8 = $cd79
    RTS                                                 

§7. Restore a normal cursor.

.restoreWriteCursor = $cd7a
    PHP                                                 push flags
    PHA                                                 push A
    LDY .vduBytesPerCharacter                           
    DEY                                                 Y = bytes per character - 1
    BNE .restoreWriteCursorNonMODE7                     if (not MODE 7) then branch
    LDA (.vduWriteCursorScreenAddressLow),Y             read from cursor screen address
    STA .vduMODE7CursorCharacter                        store it
    LDA .vduTeletextCharacterForCursor                  MODE 7 write cursor character
    STA (.vduWriteCursorScreenAddressLow),Y             store it in MODE 7 screen address
    JMP .pullTwiceAndExit                               and exit

§8. restoreWriteCursorNonMODE7.

.restoreWriteCursorNonMODE7 = $cd8f
    LDA #$FF                                            A=$FF (cursor byte mask)
    CPY #31                                             check bytes per character
    BNE +                                               if (not MODE 2) then branch
    LDA #$3F                                            A=$3F (cursor byte mask)
+
    STA .vduTempStoreDA                                 store it
-
    LDA (.vduWriteCursorScreenAddressLow),Y             get scan line byte
    EOR .vduTempStoreDA                                 invert it
    STA (.vduWriteCursorScreenAddressLow),Y             store it on scan line
    DEY                                                 decrement scan line counter
    BPL -                                               do it again
    BMI .pullTwiceAndExit                               ALWAYS branch (pull and exit)

§9. Software scroll text window downwards.

.scrollTextWindowDownwards = $cda4
    JSR .checkVerticalTextWindowBoundsAndMoveToLeftColumn   
    LDA .vduTextWindowBottom                            bottom edge
    STA .vduTextCursorYPosition                         current text line
    JSR .setTextCursorScreenAddresses                   set cursor screen addresses
.loopBackScrollDown = $cdb0
    JSR .subtractNumberOfBytesInARowFromAX              subtract bytes per character row
    BCS +                                               
    ADC .vduScreenSizeHighByte                          screen RAM size high byte
+
    STA .vduTempStoreDB                                 store A
    STX .vduTempStoreDA                                 X
    STA .vduTempStoreDC                                 A again
    BCS .noWraparound                                   if (C set; there was no wraparound)
                                                        then branch
-
    JSR .copyCharacterLineOfTextWindow                  copy line to new position
                                                        using (.vduTempStoreDA) for read and
                                                        (.vduWriteCursorScreenAddressLow)
                                                        for write
    JMP +                                               

.noWraparound = $cdc6
    JSR .subtractNumberOfBytesInARowFromAX              subtract bytes per character row
    BCC -                                               if (outside screen RAM) then branch
    JSR .copyCharacterRow                               copy a character row of the text
                                                        window
+
    LDA .vduTempStoreDC                                 set write pointer from read pointer
    LDX .vduTempStoreDA                                 
    STA .vduWriteCursorScreenAddressHigh                
    STX .vduWriteCursorScreenAddressLow                 
    DEC .vduTempStoreDE                                 decrement window height
    BNE .loopBackScrollDown                             if (not zero) then branch (loop back)

.exchangeTextCursorPositionWithWorkspaceAB = $cdda
    LDX #.vduWorkspaceA - .vduVariablesStart            point to workspace

    LDY #.vduTextCursorXPosition - .vduVariablesStart   point to text column/line
    fall through...

§10. exchangeTwoVDUBytes.

.exchangeTwoVDUBytes = $cdde
    LDA #2                                              number of bytes to swap
    BNE .exchangeABytes                                 ALWAYS branch. Exchange
                                                        (.vduWorkspaceA/B)+Y with
                                                        (.workspaceA/B)+X

§11. exchangeGraphicsCursorWithOldPosition.

.exchangeGraphicsCursorWithOldPosition = $cde2
    LDX #.vduGraphicsCursorPixelsXLow - .vduVariablesStart     point to graphics cursor
.exchangeOldGraphicsCursorPositionWithVariableX = $cde4
    LDY #.vduOldGraphicsCursorPixelsXLow - .vduVariablesStart  point to last graphics cursor
    fall through...

§12. exchangeFourBytes.

.exchangeFourBytes = $cde6
    LDA #4                                              A = 4
    fall through...

§13. exchangeABytes.

.exchangeABytes = $cde8
    STA .vduTempStoreDA                                 store it as loop counter
-
    LDA .vduVariablesStart,X                            get byte
    PHA                                                 store it
    LDA .vduVariablesStart,Y                            get byte pointed to by Y
    STA .vduVariablesStart,X                            put it in 300+X
    PLA                                                 get back A
    STA .vduVariablesStart,Y                            put it in 300+Y
    INX                                                 increment pointers
    INY                                                 
    DEC .vduTempStoreDA                                 decrement loop counter
    BNE -                                               if (not zero) then branch (loop back
                                                        and do it again)
    RTS                                                 

§14. Software scroll text window upwards.

.scrollTextWindowUpwards = $cdff
    JSR .checkVerticalTextWindowBoundsAndMoveToLeftColumn   
    LDY .vduTextWindowTop                               top of text window
    STY .vduTextCursorYPosition                         current text line
    JSR .setTextCursorScreenAddresses                   set cursor screen addresses
.loopBackScrollUp = $ce0b
    JSR .addNumberOfBytesInACharacterRowToAX            add bytes per character row
    BPL +                                               
    SEC                                                 
    SBC .vduScreenSizeHighByte                          screen RAM size high byte
+
    STA .vduTempStoreDB                                 (.vduTempStoreDA)=X/A
    STX .vduTempStoreDA                                 
    STA .vduTempStoreDC                                 .vduTempStoreDC=A
    BCC +                                               
-
    JSR .copyCharacterLineOfTextWindow                  copy line to new position
                                                        using (.vduTempStoreDA) for read
                                                        and
                                                        (.vduWriteCursorScreenAddressLow)
                                                        for write
    JMP .finishScroll                                   

+
    JSR .addNumberOfBytesInACharacterRowToAX            add bytes per char. row
    BMI -                                               if (outside screen RAM) then branch
    JSR .copyCharacterRow                               copy a character row of the text
                                                        window
.finishScroll = $ce2a
    LDA .vduTempStoreDC                                 
    LDX .vduTempStoreDA                                 
    STA .vduWriteCursorScreenAddressHigh                
    STX .vduWriteCursorScreenAddressLow                 
    DEC .vduTempStoreDE                                 decrement window height
    BNE .loopBackScrollUp                               if (not zero) then branch (loop back)
    BEQ .exchangeTextCursorPositionWithWorkspaceAB      ALWAYS branch - swap text cursor
                                                        position with workspace

§15. Copy bytes of one character row of the text window.

 On Entry:
       .vduTempStoreDA/DB                   is address to copy from
       .vduWriteCursorScreenAddressLow/High is the destination address
       .vduTextWindowWidthInBytesLow/High   is number of bytes to copy
.copyCharacterRow = $ce38
    LDX .vduTextWindowWidthInBytesHigh                  text window width high (bytes)
    BEQ +                                               if (no more than 256 bytes to copy)
                                                        then branch

    LDY #0                                              Y=0 to set loop counter
-
    LDA (.vduTempStoreDA),Y                             } copy 256 bytes
    STA (.vduWriteCursorScreenAddressLow),Y             }
    INY                                                 }
    BNE -                                               } loop till Y=0 again
    INC .vduWriteCursorScreenAddressHigh                increment high bytes
    INC .vduTempStoreDB                                 
    DEX                                                 decrement window width
    BNE -                                               if (not done yet) then branch (loop
                                                        back)
+
    LDY .vduTextWindowWidthInBytesLow                   text window width low (bytes)
    BEQ +                                               if (text window is zero bytes wide)
                                                        then branch (return)
-
    DEY                                                 Y=Y-1
    LDA (.vduTempStoreDA),Y                             copy Y bytes
    STA (.vduWriteCursorScreenAddressLow),Y             
    TYA                                                 A=Y
    BNE -                                               if (not done yet) then branch (loop
                                                        back)
+
    RTS                                                 

§16. checkVerticalTextWindowBoundsAndMoveToLeftColumn.

.checkVerticalTextWindowBoundsAndMoveToLeftColumn = $ce5b
    JSR .exchangeTextCursorPositionWithWorkspaceAB      exchange text cursor position with
                                                        workspaceAB
    SEC                                                 set carry
    LDA .vduTextWindowBottom                            bottom edge
    SBC .vduTextWindowTop                               top of text window
    STA .vduTempStoreDE                                 store height of text area
    BNE .setTextCursorToLeftHandColumn                  set text column to left hand column
    PLA                                                 get back return address (to exit
                                                        caller routine early)
    PLA                                                 
    JMP .exchangeTextCursorPositionWithWorkspaceAB      exchange text cursor position with
                                                        workspaceAB

§17. setTextCursorToLeftHandColumn.

.setTextCursorToLeftHandColumn = $ce6e
    LDA .vduTextWindowLeft                              text window left
    BPL .storeTextCursorXSetCarryAndReturn              ALWAYS branch (set X window position)

§18. copyCharacterLineOfTextWindow.

.copyCharacterLineOfTextWindow = $ce73
    LDA .vduTempStoreDA                                 get back A
    PHA                                                 push A
    SEC                                                 set carry
    LDA .vduTextWindowRight                             text window right
    SBC .vduTextWindowLeft                              text window left
    STA .vduTempStoreDF                                 store text window width
--
    LDY .vduBytesPerCharacter                           bytes per character to set loop
                                                        counter
    DEY                                                 copy loop
-
    LDA (.vduTempStoreDA),Y                             source: copy from .vduTempStoreDA/DB
    STA (.vduWriteCursorScreenAddressLow),Y             destination: store to screen
    DEY                                                 
    BPL -                                               keep copying for one character
    LDX #2                                              X=2 loop counter
-
    CLC                                                 clear carry
    LDA .vduWriteCursorScreenAddressLow,X               
    ADC .vduBytesPerCharacter                           move screen pointer on by bytes per
                                                        character
    STA .vduWriteCursorScreenAddressLow,X               and (when X=0) do the same with
                                                        .vduTempStoreDA/DB
    LDA .vduWriteCursorScreenAddressHigh,X              
    ADC #0                                              
    BPL +                                               if (this remains in screen RAM OK)
                                                        then branch
    SEC                                                 wrap around screen
    SBC .vduScreenSizeHighByte                          take off screen RAM size high byte
+
    STA .vduWriteCursorScreenAddressHigh,X              store high byte
    DEX                                                 X = X - 2
    DEX                                                 
    BEQ -                                               if (X = 0) then branch (loop back
                                                        to adjust second set of pointers)
    DEC .vduTempStoreDF                                 decrement text window width counter
    BPL --                                              if (still +ve) then branch (loop
                                                        back and do it all again)
    PLA                                                 get back A
    STA .vduTempStoreDA                                 and store it
    RTS                                                 

§19. clearOneLine.

.clearOneLine = $ceac
    LDA .vduTextCursorXPosition                         text column
    PHA                                                 save it
    JSR .setTextCursorToLeftHandColumn                  set text column to left hand column
    JSR .setTextCursorScreenAddresses                   set cursor screen addresses
    SEC                                                 set carry
    LDA .vduTextWindowRight                             text window right
    SBC .vduTextWindowLeft                              text window left
    STA .vduTempStoreDC                                 as window width
--
    LDA .vduBackgroundTextColour                        background text colour
    LDY .vduBytesPerCharacter                           bytes per character
-
    DEY                                                 Y=Y-1 decrementing loop counter
    STA (.vduWriteCursorScreenAddressLow),Y             store background colour at this
                                                        point on screen
    BNE -                                               if (Y is not zero) then branch
                                                        (loop back)
    TXA                                                 A=X
    CLC                                                 clear carry to add
    ADC .vduBytesPerCharacter                           bytes per character
    TAX                                                 X=A restoring it
    LDA .vduWriteCursorScreenAddressHigh                get high byte
    ADC #0                                              add carry if any
    BPL +                                               if (+ve) then branch
    SEC                                                 wrap around
    SBC .vduScreenSizeHighByte                          screen RAM size high byte
+
    STX .vduWriteCursorScreenAddressLow                 restore values
    STA .vduWriteCursorScreenAddressHigh                
    DEC .vduTempStoreDC                                 decrement window width
    BPL --                                              and if not 0 do it all again
    PLA                                                 get back A

.storeTextCursorXSetCarryAndReturn = $cee3
    STA .vduTextCursorXPosition                         restore text column
.setCarryAndReturn = $cee6
    SEC                                                 set carry
    RTS                                                 

§20. validatePositionAndSetupScreenAddress.

.validatePositionAndSetupScreenAddress = $cee8
    LDX .vduTextCursorXPosition                         get text cursor X position
    CPX .vduTextWindowLeft                              test against left edge of text window
    BMI .setCarryAndReturn                              if (less than left edge) then branch
                                                        (return with carry set)
    CPX .vduTextWindowRight                             test against right edge of text
                                                        window
    BEQ +                                               if (equal to right edge) then branch
                                                        (thats OK)
    BPL .setCarryAndReturn                              if (greater than right edge) then
                                                        branch (return with carry set)
+
    LDX .vduTextCursorYPosition                         current text line
    CPX .vduTextWindowTop                               test against top edge of text window
    BMI .setCarryAndReturn                              if (less than top edge) then branch
                                                        (return with carry set)
    CPX .vduTextWindowBottom                            test against bottom edge of text
                                                        window
    BEQ .setTextCursorScreenAddresses                   if (equal to bottom edge) then
                                                        branch (ok to set cursor screen
                                                        addresses)
    BPL .setCarryAndReturn                              if (greater than bottom edge) then
                                                        branch (return with carry set)
    fall through...

§21. Set text cursor screen addresses from X,Y position.

 Let X,Y be the text cursor position (in absolute character cells from top left corner)

     MODE 0: address = screen_start + Y*640 + X* 8
     MODE 1: address = screen_start + Y*640 + X*16
     MODE 2: address = screen_start + Y*640 + X*32
     MODE 3: address = screen_start + Y*640 + X* 8
     MODE 4: address = screen_start + Y*320 + X* 8
     MODE 5: address = screen_start + Y*320 + X*16
     MODE 6: address = screen_start + Y*320 + X* 8
     MODE 7: address = screen_start + Y* 40 + X

 Given the text cursor X,Y position, use the multiplication table and other characteristic
 values for the current MODE to calculate the address on screen.

 On Entry:
       .vduTextCursorX/YPosition stores the X,Y coordinates of the text cursor

 On Exit:
       Carry clear
       Stores the result in .vduTextCursorCRTCAddressLow/High, then wraps the address back on
       screen if beyond the bottom of the screen. Store the new wrapped result in
       .vduWriteCursorScreenAddressLow/High.
.setTextCursorScreenAddresses = $cf06
    LDA .vduTextCursorYPosition                         current text line
    ASL                                                 multiply by two to get table offset
    TAY                                                 Y=A
    LDA (.vduMultiplicationTableLow),Y                  get CRTC multiplication table pointer
    STA .vduWriteCursorScreenAddressHigh                .vduWriteCursorScreenAddressHigh=A
    INY                                                 Y=Y+1
    LDA #2                                              A=2
    AND .vduCurrentScreenMODEGroup                      AND with MODE group:
                                                          0 = 20k (MODE 0,1,2)
                                                          1 = 16k (MODE 3)
                                                          2 = 10k (MODE 4,5)
                                                          3 = 8k (MODE 6)
                                                          4 = 1k (MODE 7)
    PHP                                                 save flags
    LDA (.vduMultiplicationTableLow),Y                  get CRTC multiplication table pointer
    PLP                                                 pull flags
    BEQ +                                               branch if MODE 0,1,2,3 or 7
    LSR .vduWriteCursorScreenAddressHigh                MODE 4,5,6: Halve value from
                                                        multiplication table (high and low
                                                        bytes)
    ROR                                                 A = A / 2 + (128*carry)
+
    ADC .vduScreenTopLeftAddressLow                     add start of screen (low)
    STA .vduWriteCursorScreenAddressLow                 store
    LDA .vduWriteCursorScreenAddressHigh                get offset from start of screen
                                                        (high)
    ADC .vduScreenTopLeftAddressHigh                    add start of screen (high)
    TAY                                                 store in Y
    LDA .vduTextCursorXPosition                         text column
    LDX .vduBytesPerCharacter                           bytes per character
    DEX                                                 X=X-1
    BEQ .mode7Cursor                                    if (in MODE 7) then branch
    CPX #15                                             is it mode 1 or mode 5? (four colour
                                                        modes = 16 bytes per character)
    BEQ .mode1or5Cursor                                 if (mode 1 or 5) then branch (with
                                                        carry set)
    BCC .mode0346Cursor                                 if (mode 0,3,4, or 6) then branch
                                                        (with carry clear)
    ASL                                                 A=A*16 if entered here (MODE 2)
.mode1or5Cursor = $cf39
    ASL                                                 A=A*8 if entered here
.mode0346Cursor = $cf3a
    ASL                                                 A=A*4 if entered here
    ASL                                                 
    BCC .skipIncs                                       if (carry clear) then branch
    INY                                                 Y=Y+2
    INY                                                 Y is the high byte of the address
.skipIncs = $cf40
    ASL                                                 A=A*2
    BCC .skipInc                                        if (carry clear) branch (to add to
                                                        .vduWriteCursorScreenAddressLow)
    INY                                                 Y=Y+1
.mode7Cursor = $cf44
    CLC                                                 clear carry
.skipInc = $cf45
    ADC .vduWriteCursorScreenAddressLow                 add to
                                                        .vduWriteCursorScreenAddressLow
    STA .vduWriteCursorScreenAddressLow                 and store it
    STA .vduTextCursorCRTCAddressLow                    text cursor CRTC address
    TAX                                                 X=A
    TYA                                                 A=Y
    ADC #0                                              add carry if set
    STA .vduTextCursorCRTCAddressHigh                   store the text cursor CRTC address
                                                        (before any wraparound)
    BPL +                                               if (not negative) then branch
    SEC                                                 wrap around...
    SBC .vduScreenSizeHighByte                          ...subtract the screen size (high
                                                        byte)
+
    STA .vduWriteCursorScreenAddressHigh                store in high byte
    CLC                                                 clear carry
    RTS                                                 

§22. Display a character at the current graphics cursor.

.plotACharacterAtGraphicsCursor = $cf5d
    LDX .vduForegroundGraphicsColour                    foreground graphics colour
    LDY .vduForegroundGCOLMode                          foreground graphics GCOL mode
.plotACharacterWithXYAsGraphicsColourAndGCOLMode = $cf63
    JSR .setGraphicsColourMaskXY                        set graphics byte mask in
                                                        .vduGraphicsColourByteOR/EOR
    JSR .copyGraphicsCursorPixelPositionToWorkspaceABCD copy graphics cursor in pixels to
                                                        workspace (.vduWorkspaceA-D)
    LDY #0                                              Y=0
.displayNextRowOfCharacter = $cf6b
    STY .vduTempStoreDC                                 .vduTempStoreDC=Y
    LDY .vduTempStoreDC                                 Y=.vduTempStoreDC [redundant
                                                        instruction]
    LDA (.vduTempStoreDE),Y                             get pattern byte
    BEQ .characterPatternZero                           if (A=0) then branch
    STA .vduTempStoreDD                                 .vduTempStoreDD = 1 bit character
                                                        pattern byte
-
    BPL +                                               if (pattern's top bit is clear) then
                                                        branch (nothing to draw)
    JSR .plotAPixelInACharacter                         display a pixel
+
    INC .vduGraphicsCursorPixelsXLow                    current horizontal graphics cursor
    BNE +                                               
    INC .vduGraphicsCursorPixelsXHigh                   current horizontal graphics cursor
+
    ASL .vduTempStoreDD                                 shift the bit pattern left to draw
                                                        next pixel
    BNE -                                               if (there's still something left to
                                                        draw) then branch (loop back)
.characterPatternZero = $cf86
    LDX #.vduWorkspaceA - .vduVariablesStart            source is workspace
    LDY #.vduGraphicsCursorPixelsXLow - .vduVariablesStart
                                                        destination is horizontal graphics
                                                        cursor pixel position
    JSR .copyTwoBytesWithinVDUVariables                 copy two bytes
    LDY .vduGraphicsCursorPixelsYLow                    }
    BNE +                                               }
    DEC .vduGraphicsCursorPixelsYHigh                   } decrement graphics cursor Y pixel
                                                        } position
+                                                       }
    DEC .vduGraphicsCursorPixelsYLow                    }
    LDY .vduTempStoreDC                                 loop counter
    INY                                                 
    CPY #8                                              
    BNE .displayNextRowOfCharacter                      if (Y<8) then branch (loop back)
    LDX #.vduWorkspaceA - .vduVariablesStart            source: workspaceA-D
    LDY #.vduGraphicsCursorPixelsXLow - .vduVariablesStart
                                                        destination: graphics cursor position
    JMP .copyFourBytesWithinVDUVariables                copy to graphics cursor position

§23. graphicsCursorHome.

.graphicsCursorHome = $cfa6
    LDX #.vduGraphicsWindowPixelsTopLow - .vduVariablesStart source bytes (graphics window
                                                             top)
    LDY #.vduGraphicsCursorPixelsYLow - .vduVariablesStart   destination bytes (graphics
                                                             cursor Y)
    JSR .copyTwoBytesWithinVDUVariables                      copy two bytes
    fall through...

§24. setGraphicsCursorToLeftHandColumn.

.setGraphicsCursorToLeftHandColumn = $cfad
    LDX #.vduGraphicsWindowPixelsLeftLow - .vduVariablesStart source: left edge of graphics
                                                              window
    LDY #.vduGraphicsCursorPixelsXLow - .vduVariablesStart    destination: graphics cursor
                                                              X coordinate
    JSR .copyTwoBytesWithinVDUVariables                       copy two bytes
    JMP .convertPixelGraphicsCoordinatesToExternal            set up external coordinates
                                                              for graphics

§25. displayACharacter.

.displayACharacter = $cfb7
    LDX .vduNumberOfLogicalColoursMinusOne              number of logical colours less 1
    BEQ .displayCharacterMODE7                          if (MODE 7) then branch
    JSR .getCharacterDefinitionAddress                  set up character definition pointers
    fall through...

§26. displayACharacterAtAddress.

.displayACharacterAtAddress = $cfbf
    LDX .vduNumberOfLogicalColoursMinusOne              number of logical colours less 1
    LDA .vduStatusByte                                  VDU status byte
    AND #$20                                            and check bit 5 (printing at
                                                        graphics cursor)
    BNE .plotACharacterAtGraphicsCursor                 if (set) then branch
    LDY #7                                              Y=7
    CPX #3                                              
    BEQ .displayACharacter4ColourMODE                   if (X = 3) then branch to handle 4
                                                        colour MODEs
    BCS .displayACharacter16ColourMODE                  if (X > 3) then branch to deal with
                                                        16 colours

    display a character in a 2 colour MODE
-
    LDA (.vduTempStoreDE),Y                             get pattern byte
    ORA .vduTextColourByteOR                            adjust for current foreground and
                                                        background colour
    EOR .vduTextColourByteEOR                           adjust for current foreground and
                                                        background colour
    STA (.vduWriteCursorScreenAddressLow),Y             write to screen
    DEY                                                 Y=Y-1
    BPL -                                               if (still +ve) then branch (loop
                                                        back)
    RTS                                                 

§27. displayCharacterMODE7.

.displayCharacterMODE7 = $cfdc
    LDY #2                                              Y=2, loop counter/index into the
                                                        teletext conversion table
-
    CMP .teletextCharacterConversionTable,Y             compare with teletext conversion
                                                        table
    BEQ .convertMODE7Character                          if (found character to convert) then
                                                        branch
    DEY                                                 Y=Y-1
    BPL -                                               loop back until done
.writeByteToMODE7Screen = $cfe6
    STA (.vduWriteCursorScreenAddressLow,X)             write byte to screen
    RTS                                                 

§28. convertMODE7Character.

.convertMODE7Character = $cfe9
    LDA .teletextCharacterConversionTable + 1,Y         convert with teletext conversion
                                                        table
    BNE .writeByteToMODE7Screen                         ALWAYS branch back to write it

§29. displayACharacter4ColourMODE.

.displayACharacter4ColourMODE = $cfee
    LDA (.vduTempStoreDE),Y                             get pattern byte
    PHA                                                 save it
    LSR                                                 move high nybble to low
    LSR                                                 
    LSR                                                 
    LSR                                                 
    TAX                                                 X=A
    LDA .fourColourMODEByteMaskTable,X                  convert 4 bits of the character
                                                        definition into bytes to write
                                                        to the screen
    ORA .vduTextColourByteOR                            adjust for current foreground and
                                                        background colour
    EOR .vduTextColourByteEOR                           adjust for current foreground and
                                                        background colour
    STA (.vduWriteCursorScreenAddressLow),Y             write to screen
    TYA                                                 A=Y
    CLC                                                 clear carry
    ADC #8                                              add 8 to move screen RAM pointer 8
                                                        bytes
    TAY                                                 Y=A
    PLA                                                 get back A
    AND #%00001111                                      clear high nybble
    TAX                                                 X=A
    LDA .fourColourMODEByteMaskTable,X                  4 colour MODE byte mask look up table
    ORA .vduTextColourByteOR                            adjust for current foreground and
                                                        background colour
    EOR .vduTextColourByteEOR                           adjust for current foreground and
                                                        background colour
    STA (.vduWriteCursorScreenAddressLow),Y             write to screen
    TYA                                                 A=Y
    SBC #8                                              A=A-9
    TAY                                                 Y=A
    BPL .displayACharacter4ColourMODE                   if (not negative) then branch (loop
                                                        back)
.exit9 = $d017
    RTS                                                 

§30. nextPatternByte.

.nextPatternByte = $d018
    TYA                                                 Y=Y-$21
    SBC #$21                                            
    BMI .exit9                                          if (Y is negative) then branch
                                                        (return)
    TAY                                                 A=Y
    fall through...

§31. displayACharacter16ColourMODE.

.displayACharacter16ColourMODE = $d01e
    LDA (.vduTempStoreDE),Y                             get pattern byte
    STA .vduTempStoreDC                                 store it
    SEC                                                 set carry
.iloop = $d023
    LDA #0                                              A=0
    ROL .vduTempStoreDC                                 carry now occupies bit 0 of DC
    BEQ .nextPatternByte                                when DC=0 again branch to deal
                                                        with next pattern byte
    ROL                                                 get bit 7 from .vduTempStoreDC into
                                                        A bit 0
    ASL .vduTempStoreDC                                 rotate again to get second
    ROL                                                 bit into A
    TAX                                                 and store result in X
    LDA .sixteenColourMODEByteMaskTable,X               convert two bits of the character
                                                        definition into bytes to write
                                                        to the screen
    ORA .vduTextColourByteOR                            adjust for current foreground and
                                                        background colour
    EOR .vduTextColourByteEOR                           adjust for current foreground and
                                                        background colour
    STA (.vduWriteCursorScreenAddressLow),Y             and store result
    CLC                                                 }
    TYA                                                 }
    ADC #8                                              } Y=Y+8 moving screen RAM pointer on
                                                        } 8 bytes
    TAY                                                 }
    BCC .iloop                                          branch back to deal with next bit
                                                        pair
    fall through...

§32. Get character definition address.

 On Entry:
       A is the ascii code for a printable character 32-255 (bit pattern abcdefgh)
 On Exit:
       .vduTempStoreDE/F is the address of the character definition either from the default
                      ROM characters or the soft character definitions
.getCharacterDefinitionAddress = $d03e
    ASL                                                 bcdefgh0  C=a
    ROL                                                 cdefgh0a  C=b
    ROL                                                 defgh0ab  C=c
    STA .vduTempStoreDE                                 save this pattern (defgh0ab)
    AND #%00000011                                      000000ab
    ROL                                                 00000abc  C=0
    TAX                                                 X = soft font zone (1 - 7)
    AND #%00000011                                      A = 000000bc (1 - 3)
    ADC #>.characterDefinitions - 1                     A = high byte of character
                                                        definitions ($C0, $C1, $C2)
    TAY                                                 Y = high byte of ROM address of
                                                        character
    LDA .fontMaskTable,X                                get single bit set depending on X
                                                        (1-7)
    BIT .vduFontFlags                                   test font flags to see if there are
                                                        soft character definitions
    BEQ +                                               if (hard character definitions) then
                                                        branch
    LDY .vduFontZoneAddressesHigh1 - 1,X                get high byte from table for soft
                                                        character definitions
+
    STY .vduTempStoreDF                                 store Y in high byte of address
    LDA .vduTempStoreDE                                 get back pattern (defgh0ab)
    AND #%11111000                                      defgh000
    STA .vduTempStoreDE                                 and store it. This is 8 times the
                                                        ASCII character giving the low
                                                        byte of the address
    RTS