Full screen sprite editor - 4578 bytes (27.9%)


§1. Sprite Editor.

editor.png

§2. drawRectangleAroundSpriteInBlack.

.drawRectangleAroundSpriteInBlack = $a808
    LDY #0                                              
    BEQ .drawRectangleAroundSprite                      ALWAYS branch

.drawRectangleAroundSpriteInWhite = $a80c
    LDY #7                                              
    fall through...

§3. Draw rectangle around large sprite in the sprite editor.

 On Entry:
   Y = logical colour to draw in
   .seditCurrentSpriteVisibleWidth:  visible width of current sprite
   .seditCurrentSpriteVisibleHeight: visible height of current sprite
.drawRectangleAroundSprite = $a80e
    LDX #0                                              
    JSR .gcolXY                                         GCOL 0,Y where Y=0 (black) or 7
                                                        (white)

    JSR .seditGetScreenCoordinatesForCurrentSprite      

    JSR .printInlineCounted                             display following message
    !byte 8                                             length of message to print
    !text 25,4                                          PLOT 4 (MOVE)
    !word 15,7                                          PLOT 4,15,7
    !byte 25,5                                          PLOT 5,

    JSR .seditOutputXCoordinate                                X,
    JSR .printInlineCounted                             display following message
    !byte 4                                             length of message to print
    !word 7                                                      7
    !byte 25,5                                          PLOT 5,

    JSR .seditOutputXCoordinate                                X,
    JSR .seditOutputYCoordinate                                  Y
    JSR .printInlineCounted                             display following message
    !byte 4                                             length of message to print
    !text 25,5                                          }
    !word 15                                            } PLOT 5,15,

    JSR .seditOutputYCoordinate                                     Y
    JSR .printInlineCounted                             display following message
    !byte 6                                             length of message to print
    !text 25,5                                          
    !word 15,7                                          PLOT 5,15,7

    RTS                                                 

§4. Get screen coordinates for the visible area of the current sprite.

 Calculates the screen coordinates of the large sprite rectangular area in the editor.

 On Entry:
   .seditCurrentSpriteVisibleWidth:  visible width of current sprite
   .seditCurrentSpriteVisibleHeight: visible height of current sprite

 On Exit:
   .plotPointXLow/High: X screen coordinate
   .plotPointYLow/High: Y screen coordinate
   (Preserves current sprite position)
.seditGetScreenCoordinatesForCurrentSprite = $a849
    LDA .seditCurrentVisibleY                           }
    PHA                                                 } Remember current position in sprite
    LDA .seditCurrentVisibleX                           }
    PHA                                                 }

    LDA .seditCurrentSpriteVisibleHeight                }
    STA .seditCurrentVisibleY                           } currentY = sprite visible height+1
    LDA .seditCurrentSpriteVisibleWidth                 }
    STA .seditCurrentVisibleX                           } currentX = sprite visible width+1
    INC .seditCurrentVisibleX                           }
    INC .seditCurrentVisibleY                           }
    JSR .seditGetScreenCoordinates                      

    PLA                                                 }
    STA .seditCurrentVisibleX                           } Recall current position in sprite
    PLA                                                 }
    STA .seditCurrentVisibleY                           }
    RTS                                                 

§5. seditDrawLargeSpriteRow.

.seditDrawLargeSpriteRow = $a86f
    Set GCOL 0 mode
    LDA #0                                              
    STA .vduForegroundGCOLMode                          

    Column zero
    STA .seditCurrentVisibleX                           

    Get screen coordinates
    JSR .seditGetScreenCoordinates                      

    JSR .calculateCurrentSpriteByte                     

    Column zero
    LDA #0                                              
    STA .seditCurrentVisibleX                           
.seditLoopBytesInRow = $a882
    LDA .vduColourMaskLeft                              
    STA .seditCurrentVisibleByteMask                    store current byte mask
    LDY #0                                              
    LDA (.seditCurrentSpriteByteLow),Y                  load byte from sprite
    STA .vduWorkspaceI                                  store current sprite byte

.seditLoopPixelsInByte = $a88f
    LDA .vduWorkspaceI                                  
    AND .vduColourMaskLeft                              
    STA .vduForegroundGraphicsColour                    

    Create byte of solid foreground colour
    LDX .vduPixelsPerByteMinusOne                       
-
    LSR .vduForegroundGraphicsColour                    
    ORA .vduForegroundGraphicsColour                    
    DEX                                                 
    BNE -                                               

    STA .vduForegroundGraphicsColour                    

    Plot rectangle for the pixel
    LDX #4                                              
    JSR .plotX                                          PLOT 4 (MOVE)
    JSR .seditOutputXCoordinate                         X
    JSR .seditFinishMoveAndDrawLargePixelRectangle      draw rectangle around

    Increment X coordinate, based on current mode
    SEC                                                 
    LDX .vduCurrentScreenMODE                           
    LDA .seditXCoordinateIncrement,X                    
    ADC .plotPointXLow                                  
    STA .plotPointXLow                                  
    BCC +                                               
    INC .plotPointXHigh                                 
+

    Shift to move to next pixel and update byte mask
    ASL .vduWorkspaceI                                  shift byte to next pixel
    LSR .seditCurrentVisibleByteMask                    move mask to next pixel within byte
    BCC .seditLoopPixelsInByte                          

    Move to next sprite byte (decrement sprite byte address)
    LDA .seditCurrentSpriteByteLow                      
    BNE +                                               
    DEC .seditCurrentSpriteByteHigh                     
+
    DEC .seditCurrentSpriteByteLow                      

    Move to next column
    INC .seditCurrentVisibleX                           

    Check if done all of row
    LDA .seditCurrentSpriteVisibleWidth                 
    CMP .seditCurrentVisibleX                           
    BCS .seditLoopBytesInRow                            

    RTS                                                 

§6. seditDrawLargeSpriteColumn.

.seditDrawLargeSpriteColumn = $a8e0
    Set GCOL 0 mode
    LDA #0                                              
    STA .vduForegroundGCOLMode                          

    Row zero
    STA .seditCurrentVisibleY                           

    Get screen coordinates
    JSR .seditGetScreenCoordinates                      

    JSR .calculateCurrentSpriteByte                     

.seditLoopRows = $a8ee
    JSR .getColourByteFromPixelOfSprite                 

    STA .vduForegroundGraphicsColour                    
    JSR .seditDrawLargePixelRectangle                   

    Add increment to Y coordinate
    SEC                                                 
    LDX .vduCurrentScreenMODE                           
    LDA .seditYCoordinateIncrement,X                    
    ADC .plotPointYLow                                  
    STA .plotPointYLow                                  
    BCC +                                               
    INC .plotPointYHigh                                 
+

    Current sprite byte -= width in bytes
    LDY #0                                              
    CLC                                                 
    LDA .seditCurrentSpriteByteLow                      
    SBC (.currentSpriteDefinitionLow),Y                 
    STA .seditCurrentSpriteByteLow                      
    BCS +                                               
    DEC .seditCurrentSpriteByteHigh                     
+

    Increment visible Y
    INC .seditCurrentVisibleY                           
    LDA .seditCurrentSpriteVisibleHeight                
    CMP .seditCurrentVisibleY                           compare current visible Y with
                                                        visible height
    BCS .seditLoopRows                                  if (not done yet) the branch (loop
                                                        back)

    RTS                                                 

§7. seditDrawLargeSprite.

.seditDrawLargeSprite = $a922
    Remember values VUW on stack
    LDA .seditCurrentVisibleY                           
    PHA                                                 
    LDA .seditCurrentVisibleX                           
    PHA                                                 
    LDA .seditCurrentVisibleByteMask                    
    PHA                                                 

    Loop through all visible rows of sprite, drawing each row
    LDA #0                                              
    STA .seditCurrentVisibleY                           
.loopThroughRowsOfSprite = $a933
    JSR .seditDrawLargeSpriteRow                        

    INC .seditCurrentVisibleY                           
    LDA .seditCurrentSpriteVisibleHeight                
    CMP .seditCurrentVisibleY                           
    BCS .loopThroughRowsOfSprite                        

    Restore values VUW from stack
    PLA                                                 
    STA .seditCurrentVisibleByteMask                    
    PLA                                                 
    STA .seditCurrentVisibleX                           
    PLA                                                 
    STA .seditCurrentVisibleY                           
    RTS                                                 

§8. Unplot the cursor, redraw regular and large sprites, then redraw the cursor.

.seditDrawLargeAndRegularSpriteAndUpdateCursor = $a94e
    JSR .drawCircleCursor                               undraw cursor

.seditDrawLargeAndRegularSpriteAndCursor = $a951
    JSR .seditPlotRegularSizeSprite                     

.seditDrawLargeSpriteAndCursor = $a954
    JSR .seditDrawLargeSprite                           
    fall through...

§9. Draw (or undraw) the circular cursor in the sprite editor.

.drawCircleCursor = $a957
    LDX #4                                              
    JSR .plotX                                          PLOT 4 (MOVE)
    JSR .seditGetScreenCoordinates                      

    Get X coordinate of centre of circle to draw
    LDX .vduCurrentScreenMODE                           }
    LDA .seditXCoordinateIncrement,X                    } get size of the square for
                                                        } displaying the current pixel
    LSR                                                 } halve it to get the centre point

    AY += screen X coordinate offset within the current sprite byte
    CLC                                                 }
    ADC .seditScreenXOffsetWithinCurrentByte            }
    LDY .plotPointXHigh                                 
    BCC +                                               increment the high byte if needed
    INY                                                 
+

    AY += plotPointXLow
    CLC                                                 
    ADC .plotPointXLow                                  
    BCC +                                               
    INY                                                 
+

    Send X coordinate (low byte) for MOVE
    JSR .OSWRCH                                         

    Send X coordinate (high byte) for MOVE
    TYA                                                 
    JSR .OSWRCH                                         

    Get Y coordinate of centre of circle to draw
    LDA .seditYCoordinateIncrement,X                    } get size of the square for
                                                        } displaying the current pixel
    LSR                                                 } halve it to get the centre point
    CLC                                                 
    ADC .plotPointYLow                                  
    LDY .plotPointYHigh                                 
    BCC +                                               
    INY                                                 
+

    Send Y coordinate (low and high) for MOVE
    JSR .OSWRCH                                         
    TYA                                                 
    JSR .OSWRCH                                         

    Draw circle
    JSR .printInlineCounted                             display following message
    !byte 9                                             length of message to print
    !byte 18,3,7                                        GCOL 3,7 (EOR, white)
    !text 25,145                                        }
    !word 0, 7                                          } PLOT 145,0,7 (Circle outline)

    JMP .calculateCurrentSpriteByte                     

§10. seditDrawLargePixelRectangle.

.seditDrawLargePixelRectangle = $a9a3
    LDX #4                                              
    JSR .plotX                                          PLOT 4,

    LDA .plotPointXLow                                  }
    CLC                                                 }
    ADC .seditScreenXOffsetWithinCurrentByte            } X is the low byte of the X
                                                        } coordinate
    TAX                                                 }

    LDA .plotPointXHigh                                 }
    ADC #0                                              } Y is the high byte of the X
                                                        } coordinate
    TAY                                                 }

    TXA                                                 }
    JSR .OSWRCH                                         }
    TYA                                                 } output X coordinate
    JSR .OSWRCH                                         }
    fall through...

§11. seditFinishMoveAndDrawLargePixelRectangle.

.seditFinishMoveAndDrawLargePixelRectangle = $a9be
    JSR .seditOutputYCoordinate                         

    LDX #$61                                            
    JSR .plotX                                          PLOT $61 (Rectangle, relative)

    LDX .vduCurrentScreenMODE                           }
    LDA .seditXCoordinateIncrement,X                    }
    JSR .OSWRCH                                         } one X increment amount for the
                                                        } width
    LDA #0                                              }
    JSR .OSWRCH                                         }

    LDA .seditYCoordinateIncrement,X                    }
    JSR .OSWRCH                                         }
    LDA #0                                              } one Y increment amount for the
                                                        } height
    JMP .OSWRCH                                         }

§12. Calculate address within sprite given (X, Y) pixel coordinates.

 On Entry:
   .seditCurrentVisibleX: X pixel within visible area of sprite in editor
   .seditCurrentVisibleY: Y pixel within visible area of sprite in editor
   .seditCurrentX: pixel coordinate of the left edge of visible area of sprite in editor
   .seditCurrentY: pixel coordinate of the bottom edge of visible area of sprite in editor

 On Exit:
   .seditCurrentSpriteByteLow/High: address of current sprite byte
.calculateCurrentSpriteByte = $a9df
    LDY #.spriteHeaderOffsetWidth                       
    LDA (.currentSpriteDefinitionLow),Y                 
    TAX                                                 
    INX                                                 X = width

    Y value
    CLC                                                 
    LDA .seditCurrentVisibleY                           
    ADC .seditCurrentY                                  
    TAY                                                 Y is cursor Y position

    We calculate the offset from the *end* of the sprite data backwards to the current byte
    in the sprite

    JSR .multiply8x8                                    result = X * Y
                                                               = width * Y
                                                               = offset from end of sprite
                                                        backwards to current row of sprite

    result += X offset within the visible region
    CLC                                                 
    LDA .vduTempStoreDE                                 
    ADC .seditCurrentVisibleX                           add the offset within the visible
                                                        region
    LDX .vduTempStoreDF                                 
    BCC +                                               
    INX                                                 
+

    result += current sprite pixel X within visible area
    CLC                                                 
    ADC .seditCurrentX                                  left pixel coordinate of large sprite
    STA .vduTempStoreDE                                 
    BCC +                                               
    INX                                                 
+
    STX .vduTempStoreDF                                 

    [NOTE: instead of the above two additions, calculate .seditCurrentVisibleX +
    .seditCurrentX then add to DE/DF, i.e.:
      LDA .seditCurrentVisibleX
      CLC
      ADC .seditCurrentX
      ADC .vduTempStoreDE
      STA .vduTempStoreDE
      BCC +
      INC .vduTempStoreDF
      +
      ... to save eight bytes
    ]

    AX = start address of next sprite
    CLC                                                 
    LDY #.spriteHeaderOffsetSizeInBytesLow              
    LDA (.currentSpriteDefinitionLow),Y                 
    ADC .currentSpriteDefinitionLow                     add current sprite address (low)
    PHA                                                 
    INY                                                 Y =
                                                        .spriteHeaderOffsetSizeInBytesHigh
    LDA (.currentSpriteDefinitionLow),Y                 
    ADC .currentSpriteDefinitionHigh                    add current sprite address (high)
    TAX                                                 
    PLA                                                 

    Add 5 to sprite address to account for the six byte header of the current sprite,
    minus one because we want to start from the last byte of the current sprite data, not
    the first byte of the next sprite.
    CLC                                                 
    ADC #5                                              
    BCC +                                               
    INX                                                 
+

    .seditCurrentSpriteByteLow/High = next sprite address - result
    (note that result = vduDE/F = the offset backwards from end of current sprite data to
    the current sprite byte)
    SEC                                                 
    SBC .vduTempStoreDE                                 
    STA .seditCurrentSpriteByteLow                      store current sprite byte address
                                                        (low byte)
    TXA                                                 
    SBC .vduTempStoreDF                                 
    STA .seditCurrentSpriteByteHigh                     store current sprite byte address
                                                        (high byte)
    RTS                                                 

§13. seditXCoordinatePowerOf2.

.seditXCoordinatePowerOf2 = $aa26
    MODE  0,   1,   2,   3,   4,   5
    !byte   8,   7,   7, $FF,   8,   8                  indexed by screen MODE (0-5)

.seditMaximumVisibleHeight = $aa2c
    MODE  0,   1,   2,   3,   4,   5
    !byte $0B, $17, $17, $FF, $17, $17                  indexed by screen MODE (0-5)

.seditMaximumVisibleWidth = $aa32
    MODE  0,   1,   2,   3,   4,   5
    !byte $02, $05, $05, $FF, $02, $02                  indexed by screen MODE (0-5)

This is how much we move in Y from one large square in the sprite editor to the next
.seditYCoordinateIncrement = $aa38
    MODE  0,   1,   2,   3,   4,   5
    !byte $3F, $1F, $1F, $FF                            indexed by screen MODE (0-5)
                                                        Overlaps 2 bytes into the next table

This is how much we move in X (minus one) from one large square in the sprite editor to the
next
.seditXCoordinateIncrement = $aa3c
    MODE  0,   1,   2,   3,   4,   5
    !byte $1F, $1F, $3F, $FF, $1F, $3F                  indexed by screen MODE (0-5)

This is the initial OS coordinate offset in X for the first large square in any byte
.seditXCoordinateOffsetForRightmostPixelInByte = $aa42
    MODE  0,   1,   2,   3,   4,   5
    !byte $E0, $60, $40, $FF, $E0, $C0                  indexed by screen MODE (0-5)

§14. Get screen coordinates from sprite coordinates (.seditCurrentX, .seditCurrentY).

 On Exit:
     .plotPointXLow/High = (.seditCurrentX * 256) + 16    for MODE 0,4,5 OR
                         = (.seditCurrentX * 128) + 16    for MODE 1,2

     .plotPointYLow/High = (.seditCurrentY * 64) + 8      for MODE 0 OR
                         = (.seditCurrentY * 32) + 8      for MODE 1,2,4,5
.seditGetScreenCoordinates = $aa48
    LDA #0                                              
    STA .plotPointXHigh                                 high bytes start at zero
    STA .plotPointYHigh                                 

    Loop to multiply by a power of two
    LDX .vduCurrentScreenMODE                           
    LDA .seditXCoordinatePowerOf2,X                     
    TAX                                                 loop counter
    LDA .seditCurrentVisibleX                           coordinate low byte
-
    ASL                                                 double low byte
    ROL .plotPointXHigh                                 double high byte (with carry)
    DEX                                                 
    BNE -                                               loop back

    Add 16
    ADC #16                                             
    STA .plotPointXLow                                  
    LDA .plotPointXHigh                                 
    ADC #0                                              
    STA .plotPointXHigh                                 

    LDX #5                                              }
    LDA .vduCurrentScreenMODE                           }
    BNE +                                               } if (MODE 0) then X = 6 else X = 5
    LDX #6                                              }
+
    Loop to multiply Y coordinate (A, .plotPointY) by a power of two
    i.e. Y coordinate << X
    X is the loop counter
    LDA .seditCurrentVisibleY                           
-
    ASL                                                 }
    ROL .plotPointYHigh                                 } Multiply A (from
                                                        } .seditCurrentVisibleY)
    DEX                                                 } and .plotPointYHigh by 32
                                                        } (non-mode 0)
    BNE -                                               } or 64 (mode 0)

    Add 8 to Y coordinate
    ADC #8                                              } add 8
    STA .plotPointYLow                                  } and store in plotPointYLow/High
    LDA .plotPointYHigh                                 }
    ADC #0                                              }
    STA .plotPointYHigh                                 }
    RTS                                                 

    [NOTE: Could be this for a 3 byte saving
      BCC +
      INC .plotPointYHigh
      +
      RTS
    ]

§15. seditMoveToNextSpriteToDelete.

.seditMoveToNextSpriteToDelete = $aa8f
    JSR .incTempStoreDADB                               move past the COMMA

    LDX .vduTempStoreDA                                 } remcall user input
    LDY .vduTempStoreDB                                 } address into XY
      fall through...

§16. *SDELETE.

.starSDelete = $aa96
    JSR .read8BitNumberIntoA                            

    TXA                                                 }
    PHA                                                 }
    TYA                                                 } push address to rest of string
    PHA                                                 }

    LDA .vduTempStoreDE                                 
    JSR .deleteSpriteInternal                           

    PLA                                                 }
    TAY                                                 } recall address of rest of string
    PLA                                                 }
    TAX                                                 }
    JSR .skipLeadingSpaces                              

    STX .vduTempStoreDA                                 
    STY .vduTempStoreDB                                 
    LDY #0                                              
    LDA (.vduTempStoreDA),Y                             
    CMP #','                                            
    BEQ .seditMoveToNextSpriteToDelete                  if (comma found) then branch (move
                                                        to next sprite to delete)
    RTS                                                 

§17. Delete a sprite.

 On Entry:
   A: sprite number
.deleteSpriteInternal = $aab6
    JSR .findSpriteA                                    get sprite address

    STA .vduTempStoreDA                                 
    STY .vduTempStoreDB                                 
    CPX #0                                              [NOTE: redundant]
    BEQ .return7                                        if sprite not found, branch

    JSR .resetCurrentSpriteAddress                      reset address

    Reduce number of sprites
    SEC                                                 
    LDY #.workspaceOffsetNumberOfSprites                }
    LDA (.privateWorkspaceLow),Y                        } get number of sprites
    SBC #1                                              reduce by one
    STA (.privateWorkspaceLow),Y                        store number of sprites
    DEX                                                 
    BEQ .return7                                        if (no sprites left) then return

    JSR .moveToNextSprite                               tempStoreDC/DD moves on to next
                                                        sprite

    Copy all remaining sprites down to fill the gap
-
    Get size of sprite
    CLC                                                 
    LDY #.spriteHeaderOffsetSizeInBytesLow              }
    LDA (.vduTempStoreDC),Y                             }
    ADC #6                                              } 6 is size of sprite header
    STA .vduTempStoreDE                                 } tempStoreDE/F = byte size of
                                                        } sprite data
    INY                                                 } Y =
                                                        } .spriteHeaderOffsetSizeInBytesHigh
    LDA (.vduTempStoreDC),Y                             }
    ADC #0                                              }
    STA .vduTempStoreDF                                 }

    JSR .blockCopyMemoryIncrementing                    copy sprite data down in memory to
                                                        fill gap

    DEX                                                 
    BNE -                                               

.return7 = $aae9
    RTS                                                 

§18. Add sprite.

 Used in *SGET and *SMERGE

 On Entry:
   .vduTempStoreDC/DD: address of sprite data to add
.addSprite = $aaea
    JSR .getPrivateWorkspaceAddress                     

    LDA .vduTempStoreDC                                 }
    PHA                                                 }
    LDA .vduTempStoreDD                                 } remember sprite address
    PHA                                                 }

    Delete existing sprite
    LDY #.spriteHeaderOffsetSpriteNumber                }
    LDA (.vduTempStoreDC),Y                             } get sprite number
    JSR .deleteSpriteInternal                           

    PLA                                                 }
    STA .vduTempStoreDD                                 }
    PLA                                                 } recall sprite address
    STA .vduTempStoreDC                                 }

    Get size of sprite
    CLC                                                 }
    LDY #.spriteHeaderOffsetSizeInBytesLow              }
    LDA (.vduTempStoreDC),Y                             }
    ADC #6                                              }
    STA .vduTempStoreDE                                 }
    INY                                                 } DE/DF = size in bytes of sprite
    LDA (.vduTempStoreDC),Y                             }
    ADC #0                                              }
    STA .vduTempStoreDF                                 }

    JSR .blockCopyMemoryIncrementing                    copy sprite data

    CLC                                                 
    LDY #.workspaceOffsetNumberOfSprites                }
    LDA (.privateWorkspaceLow),Y                        }
    ADC #1                                              } increment number of sprites
    STA (.privateWorkspaceLow),Y                        }
    RTS                                                 

§19. Redraw regular sized sprite.

 Used as part of reduce vertically and reduce horizontally
.seditClearAndReplotRegularSizeSprite = $ab1d
    JSR .printInlineCounted                             display following message
    !byte 12                                            length of message to print
    !byte 25,4                                          } MOVE 920,200
    !word 920, 200                                      }
    !byte 25,$67                                        rectangle fill in background colour
    !word 1220,800                                      920,200 -> 1220,800
    fall through...

.seditPlotRegularSizeSprite = $ab2d
    JSR .getPrivateWorkspaceAddress                     

    LDY #.workspaceOffsetCurrentSpriteAddressLow        
    LDA .currentSpriteDefinitionLow                     
    STA (.privateWorkspaceLow),Y                        
    INY                                                 
    LDA .currentSpriteDefinitionHigh                    
    STA (.privateWorkspaceLow),Y                        

    Plot sprite
    JSR .printInlineCounted                             display following message
    !byte 19                                            length of message to print
    !byte 24                                            set graphics window
    !word 936, 200                                      left, bottom
    !word 1200, 800                                     right, top
    !byte 18,0,7                                        white graphics foreground colour
    !byte 25, $ed                                       plot sprite
    !word 936, 200                                      at x, y
    !byte 26                                            clear text and graphics windows

    RTS                                                 

§20. Sprite data for new sprite.

 A (1 byte x 1 pixel) MODE 2 sprite with one byte of logical colour zero.
.newSpriteData = $ab53
    Six byte sprite header
    !byte $00                                           width-1
    !byte $00                                           height-1
    !byte $01                                           } size in bytes
    !byte $00                                           }
    !byte $02                                           MODE number
    !byte $00                                           sprite number

    One byte of sprite data for a new sprite
    !byte $00                                           

.notEnoughRoomError2 = $ab5a
    JMP .notEnoughRoomError                             [NOTE: redundant, caller should just
                                                        JMP .notEnoughRoomError]

§21. *SEDIT n[,m].

 Edit sprite n, save as sprite m

 On Entry:
   XY: address of the rest of the command line
.starSEdit = $ab5d
    JSR .read8BitNumberIntoA                            read sprite number to edit

    STA .vduWorkspaceB                                  'edit' sprite number (n)
    STA .vduWorkspaceC                                  'save' sprite number (m)
    JSR .skipLeadingSpaces                              

    STX .vduTempStoreDA                                 
    STY .vduTempStoreDB                                 
    LDY #0                                              
    LDA (.vduTempStoreDA),Y                             
    CMP #','                                            
    BNE .skipSecondNumber                               if no comma, skip ahead

    JSR .incTempStoreDADB                               move past the COMMA

    LDX .vduTempStoreDA                                 }
    LDY .vduTempStoreDB                                 } read the second number
    JSR .read8BitNumberIntoA                            }

    STA .vduWorkspaceC                                  second number, the 'save' sprite
                                                        number (m)
.skipSecondNumber = $ab82
    LDA .vduWorkspaceC                                  'save' sprite number (m)
    CMP .vduWorkspaceB                                  'edit' sprite number (n)
    BEQ +                                               
    JSR .deleteSpriteInternal                           delete 'save' sprite, ready to be
                                                        overwritten
+
    JSR .getPrivateWorkspaceAddress                     
    JSR .getFreeSpace                                   DC/DD = end of used sprite memory
                                                        address

    .vduTempStoreDA/B = .vduTempStoreDC/D (remember end of used sprite memory address)
    LDA .vduTempStoreDC                                 
    STA .vduTempStoreDA                                 
    LDA .vduTempStoreDD                                 
    STA .vduTempStoreDB                                 

    Check we can find the sprite
    LDA .vduWorkspaceB                                  sprite number to find
    JSR .findSpriteA                                    get address of sprite (into
                                                        .vduTempStoreDC/DD)
    BEQ .couldNotFindSpriteToEdit                       

    LDA .vduWorkspaceB                                  'edit' sprite number (n)
    CMP .vduWorkspaceC                                  'save' sprite number (m)
    BNE .editSpriteDifferentFromSaveSprite              if (different) then branch

    Store sprite *index*
    STX .vduWorkspaceD                                  

    SEC                                                 
    LDY #.workspaceOffsetNumberOfSprites                }
    LDA (.privateWorkspaceLow),Y                        } number of sprites -= sprite index
    SBC .vduWorkspaceD                                  } i.e. number of sprites starting at
                                                        } the one being edited
    STA (.privateWorkspaceLow),Y                        }

    .vduTempStoreDE/DF = .vduTempStoreDA/DB - .vduTempStoreDC/DD
                       = end of used sprite memory address - address of current sprite
                       = amount of memory from the end of the current sprite to end of all
    sprite data
    SEC                                                 
    LDA .vduTempStoreDA                                 
    SBC .vduTempStoreDC                                 
    STA .vduTempStoreDE                                 
    LDA .vduTempStoreDB                                 
    SBC .vduTempStoreDD                                 
    STA .vduTempStoreDF                                 

    Edit sprite
    JMP .seditInternal                                  

.couldNotFindSpriteToEdit = $abc8
    LDA #<.newSpriteData                                
    STA .vduTempStoreDC                                 
    LDA #>.newSpriteData                                
    STA .vduTempStoreDD                                 

.editSpriteDifferentFromSaveSprite = $abd0
    LDA .vduTempStoreDF                                 
    PHA                                                 
    LDA .vduTempStoreDE                                 
    PHA                                                 

    Get size of sprite
    CLC                                                 
    LDY #.spriteHeaderOffsetSizeInBytesLow              
    LDA (.vduTempStoreDC),Y                             
    ADC #6                                              
    STA .vduTempStoreDE                                 
    INY                                                 
    LDA (.vduTempStoreDC),Y                             
    ADC #0                                              
    STA .vduTempStoreDF                                 

    Compare sizes of sprites
    PLA                                                 
    CMP .vduTempStoreDE                                 
    PLA                                                 
    SBC .vduTempStoreDF                                 
    BCS .enoughRoom                                     
    JMP .notEnoughRoomError2                            

.enoughRoom = $abf1
    LDA #1                                              
    STA .vduWorkspaceD                                  
    fall through...

§22. Main sprite editor routine.

 Called once the parsing of the *SEDIT command is done and verification passes

 On Entry:
   .vduWorkspaceC: sprite number
   .vduWorkspaceD: sprite index
   .vduTempStoreDC/DD: address of current sprite
   .vduTempStoreDE/DF: length of sprite data
.seditInternal = $abf6

    We copy the existing sprites to the end of sprite memory, leaving the maximum amount
    for the sprite we are about to edit.

    Find how far we can copy the sprite data in memory...
    .vduTempStoreDA/DB = currentSpriteDefinitionLow/High = end of allocated sprite memory -
    length of sprite data
    SEC                                                 
    LDY #.workspaceOffsetSpriteEndPage                  
    LDA #0                                              
    SBC .vduTempStoreDE                                 
    STA .vduTempStoreDA                                 
    STA .currentSpriteDefinitionLow                     
    LDA (.privateWorkspaceLow),Y                        
    SBC .vduTempStoreDF                                 
    STA .vduTempStoreDB                                 
    STA .currentSpriteDefinitionHigh                    

    Copy sprite data to the end of allocated sprite memory, so give the maximum amount
    of memory for editing the current sprite.
    JSR .blockCopyMemoryDecrementing                    copy .vduTempStoreDE/DF bytes from
                                                        tempStoreDC/DD to tempStoreDA/DB

    Store the sprite number
    LDA .vduWorkspaceC                                  sprite number
    LDY #.spriteHeaderOffsetSpriteNumber                
    STA (.currentSpriteDefinitionLow),Y                 store

    JSR .getPrivateWorkspaceAddress                     

    JSR .rememberCurrentSpriteInWorkspace               

    Store MODE in which the sprite was defined
    LDA .vduCurrentScreenMODE                           
    LDY #.spriteHeaderOffsetModeNumber                  
    STA (.currentSpriteDefinitionLow),Y                 

    LDY #.workspaceOffsetOptions                        
    LDA (.privateWorkspaceLow),Y                        
    AND #$FE                                            clear bit 0 of options (pen up)
    STA (.privateWorkspaceLow),Y                        

    Set sprite cursor position to bottom left of sprite
    LDA #0                                              
    STA .seditCurrentX                                  
    STA .seditCurrentY                                  
    STA .seditCurrentVisibleX                           
    STA .seditCurrentVisibleY                           
    STA .seditScreenXOffsetWithinCurrentByte            
    LDA .vduColourMaskLeft                              
    STA .seditCurrentVisibleByteMask                    

    Set white pen
    LDA #7                                              
    STA .seditBackgroundColour                          

    Find the sprite width that is visible on-screen while editing (depends on the current
    MODE).
    LDX .vduCurrentScreenMODE                           
    LDY #.spriteHeaderOffsetWidth                       
    LDA (.currentSpriteDefinitionLow),Y                 
    CMP .seditMaximumVisibleWidth,X                     
    BCC +                                               
    LDA .seditMaximumVisibleWidth,X                     
+
    STA .seditCurrentSpriteVisibleWidth                 

    Find the sprite height that is visible on-screen while editing (depends on the current
    MODE).
    INY                                                 
    LDA (.currentSpriteDefinitionLow),Y                 sprite height
    CMP .seditMaximumVisibleHeight,X                    
    BCC +                                               
    LDA .seditMaximumVisibleHeight,X                    
+
    STA .seditCurrentSpriteVisibleHeight                

    JSR .printInlineCounted                             display following message
    !byte 15                                            length of message to print
    !byte 4                                             VDU 4 (write text at the text cursor)
    !byte 26                                            VDU 26 (reset text and graphics
                                                        windows)
    !byte 17,128                                        VDU 17,128 (select black as
                                                        background colour)
    !byte 12                                            VDU 12 (clear screen)
    !byte 23,1,0,0,0,0,0,0,0,0                          VDU 23,1,0,0,0,0,0,0,0,0 (turn off
                                                        cursor)

    LDA #4                                              } *FX 4,2
    LDX #2                                              } disable cursor editing (edit keys
                                                        } act as soft keys)
    JSR .OSBYTE                                         }

    LDA #$E1                                            }
    LDX #$90                                            } *FX 225,$90,0
    LDY #0                                              } expand function keys as $90 upwards
    JSR .OSBYTE                                         }

    LDA #$E2                                            }
    LDX #$80                                            } *FX 226,$80,0
    LDY #0                                              } expand SHIFT+function keys as $80
                                                        } upwards
    JSR .OSBYTE                                         }

    LDA #$E3                                            }
    LDX #$A0                                            } *FX 227,$A0,0
    LDY #0                                              } expand CTRL+function keys as $A0
                                                        } upwards
    JSR .OSBYTE                                         }

    JSR .seditPrintModeMessage                          print "Mode "
    JSR .drawRectangleAroundSpriteInWhite               
    JSR .seditDrawLargeAndRegularSpriteAndCursor        

.editingLoop = $aca2
    JSR .seditPrintModeMessage                          update current status

    LDA #$0F                                            }
    TAX                                                 } Flush keyboard buffer
    JSR .OSBYTE                                         }
    JSR .OSRDCH                                         wait for key press
    BCS .exitEditing                                    carry set on ESCAPE
    CMP #$0D                                            
    BNE +                                               if (not RETURN key) then branch

    Handle RETURN key
    JSR .seditSetSquareColourA                          
    JMP .editingLoop                                    

+
    Check for DELETE key
    CMP #$7F                                            
    BNE +                                               if (not DELETE key) then branch

    Handle DELETE key
    JSR .seditDeleteSquare                              
    JMP .editingLoop                                    

+
    CMP #'0'                                            
    BCC .editingLoop                                    if (A < '0') then branch (loop back)

    CMP #'9'+1                                          
    BCC .editHandleDigit                                if (A <= '9') then branch

    CMP #'A'                                            
    BCC .editingLoop                                    if (A < 'A') then branch

    CMP #'F'+1                                          
    BCC .editHandleHexDigit                             if (A <= 'F') then branch

    CMP #'a'                                            
    BCC .editingLoop                                    if (A < 'a') then branch

    CMP #'f'+1                                          
    BCS .editContinueKeyChecks                          if (A >= 'f'+1) then branch

    SBC #$1F                                            convert a-f to A-F (upper case)
.editHandleHexDigit = $acde
    SEC                                                 
    SBC #7                                              adjust value of hex digit A-F
.editHandleDigit = $ace1
    SEC                                                 
    SBC #'0'                                            convert hex digit to value 0-15
    STA .seditBackgroundColour                          set background colour
    JMP .editingLoop                                    

.editContinueKeyChecks = $acea
    CMP #$80                                            
    BCC .editingLoop                                    if (A < $80) then branch

    CMP #$B0                                            
    BCS .editingLoop                                    if (A >= $B0) then branch

    Handle function key press
    ASL                                                 
    TAX                                                 
    JSR .seditCallEditingFunction                       
    JMP .editingLoop                                    

§23. Exit the sprite editor.

.exitEditing = $acfa
    LDY #.spriteHeaderOffsetSpriteNumber                
    LDA (.currentSpriteDefinitionLow),Y                 
    PHA                                                 remember workspace[sprite number]

    LDA .currentSpriteDefinitionLow                     
    STA .vduTempStoreDC                                 
    LDA .currentSpriteDefinitionHigh                    
    STA .vduTempStoreDD                                 

    JSR .deselectCurrentSprite                          

    LDX .vduWorkspaceD                                  number of sprites
    JSR .addSpriteLoop                                  

    PLA                                                 recall sprite number
    JSR .starSChooseInternal                            

    LDA #$7C                                            
    JSR .OSBYTE                                         *FX 124 (Reset ESCAPE flag)

    LDA #$E1                                            
    LDX #1                                              
    LDY #0                                              
    JSR .OSBYTE                                         *FX 225,1,0 (Function keys)

    LDA #$E2                                            
    LDX #$80                                            
    LDY #0                                              
    JSR .OSBYTE                                         *FX 226,128,0 (SHIFT function keys)

    LDA #$E3                                            
    LDX #$90                                            
    LDY #0                                              
    JSR .OSBYTE                                         *FX 227,144,0 (CTRL function keys)

    LDA #4                                              
    JSR .OSWRCH                                         VDU 4

    LDA #4                                              
    LDX #0                                              
    LDY #0                                              
    JMP .OSBYTE                                         *FX 4,0,0 (cursor key editing)

§24. Call the specific editing function.

 On Entry:
   X is offset within table of function address
.seditCallEditingFunction = $ad42
    lookup routine address
    LDA .seditRoutineTable,X                            
    STA .vduTempStoreDA                                 
    LDA .seditRoutineTable + 1,X                        
    STA .vduTempStoreDB                                 

    jump to routine
    JMP (.vduTempStoreDA)                               

§25. Sprite editor commands (function keys, COPY key, and cursor keys).

.seditRoutineTable = $ad4f
    !word .return                                       128 = SHIFT + f0
    !word .return                                       129 = SHIFT + f1
    !word .return                                       130 = SHIFT + f2
    !word .seditReduceVertically                        131 = SHIFT + f3
    !word .seditReduceHorizontally                      132 = SHIFT + f4
    !word .seditDeleteRow                               133 = SHIFT + f5
    !word .seditDeleteColumn                            134 = SHIFT + f6
    !word .return                                       135 = SHIFT + f7
    !word .return                                       136 = SHIFT + f8
    !word .return                                       137 = SHIFT + f9
    !word .return                                       138 =
    !word .return                                       139 = SHIFT + COPY
    !word .seditShiftCursorLeft                         140 = SHIFT + cursor LEFT
    !word .seditShiftCursorRight                        141 = SHIFT + cursor RIGHT
    !word .seditShiftCursorDown                         142 = SHIFT + cursor DOWN
    !word .seditShiftCursorUp                           143 = SHIFT + cursor UP

    !word .seditPenUpDown                               144 = f0
    !word .seditFloodRow                                145 = f1
    !word .seditFloodColumn                             146 = f2
    !word .seditExtendVertically                        147 = f3
    !word .seditExtendHorizontally                      148 = f4
    !word .seditInsertRow                               149 = f5
    !word .seditInsertColumn                            150 = f6
    !word .seditMirrorVertically                        151 = f7
    !word .seditMirrorHorizontally                      152 = f8
    !word .return                                       153 = f9
    !word .return                                       154 =
    !word .seditCopy                                    155 = COPY
    !word .seditCursorLeft                              156 = cursor LEFT
    !word .seditCursorRight                             157 = cursor RIGHT
    !word .seditCursorDown                              158 = cursor DOWN
    !word .seditCursorUp                                159 = cursor UP

    !word .return                                       160 = CTRL + f0
    !word .return                                       161 = CTRL + f1
    !word .return                                       162 = CTRL + f2
    !word .return                                       163 = CTRL + f3
    !word .return                                       164 = CTRL + f4
    !word .return                                       165 = CTRL + f5
    !word .return                                       166 = CTRL + f6
    !word .return                                       167 = CTRL + f7
    !word .return                                       168 = CTRL + f8
    !word .return                                       169 = CTRL + f9
    !word .return                                       170 =
    !word .return                                       171 = CTRL + COPY
    !word .seditCtrlCursorLeft                          172 = CTRL + cursor LEFT
    !word .seditCtrlCursorRight                         173 = CTRL + cursor RIGHT
    !word .seditCtrlCursorDown                          174 = CTRL + cursor DOWN
    !word .seditCtrlCursorUp                            175 = CTRL + cursor UP

§26. Delete a square shown in the large sprite.

.seditDeleteSquare = $adaf
    LDA .seditBackgroundColour                          
    PHA                                                 push old background colour
    LDA #0                                              background colour black
    BEQ .seditCopyInternal                              ALWAYS branch

§27. Copy key: set pixel to white.

.seditCopy = $adb7
    LDA .seditBackgroundColour                          
    PHA                                                 push old background colour
    LDA #7                                              background colour white

.seditCopyInternal = $adbd
    STA .seditBackgroundColour                          
    JSR .seditSetSquareColourA                          
    PLA                                                 
    STA .seditBackgroundColour                          
.return6 = $adc7
    RTS                                                 

§28. Check for pen down then set pixel.

.seditSetPixelIfPenDown = $adc8
    LDY #.workspaceOffsetOptions                        
    LDA (.privateWorkspaceLow),Y                        
    LSR                                                 
    BCC .return6                                        if pen up, return

.seditSetSquareColourA = $adcf
    JSR .drawCircleCursor                               undraw cursor
    JSR .writeSpritePixel                               
    JSR .seditDrawLargePixelRectangle                   
    JSR .seditPlotRegularSizeSprite                     
    JMP .drawCircleCursor                               draw cursor

§29. Write pixel into sprite.

.writeSpritePixel = $adde
    LDX #0                                              
    LDY .seditBackgroundColour                          
    JSR .gcolXY                                         set background colour

    LDA .vduForegroundGraphicsColour                    foreground colour
    AND .seditCurrentVisibleByteMask                    apply mask for current pixel
    STA .vduTempStoreDA                                 store pixel colour

    Update pixel in sprite
    LDY #0                                              
    LDA (.seditCurrentSpriteByteLow),Y                  
    ORA .seditCurrentVisibleByteMask                    
    EOR .seditCurrentVisibleByteMask                    clear bits from byte mask
    ORA .vduTempStoreDA                                 set bits for pixel colour
    STA (.seditCurrentSpriteByteLow),Y                  
.return = $adfc
    RTS                                                 

§30. Copy visible area rectangle left or right.

 On Entry:
   X: 0=move left
      1=move right
   Y: X coordinate to move from
.moveVisibleAreaLeftOrRight = $adfd
    LDA .seditCurrentVisibleX                           }
    PHA                                                 }
    LDA .seditCurrentVisibleY                           } remember current position
    PHA                                                 }
    LDA .seditCurrentVisibleByteMask                    }
    PHA                                                 }

    TXA                                                 }
    PHA                                                 }
    TYA                                                 } remember X,Y
    PHA                                                 }

    LDY #0                                              
    JSR .moveXY                                         source rectangle one corner

    PLA                                                 }
    TAX                                                 } X=original Y value (0=move left or
                                                        } 1=move right)
    LDY .seditCurrentSpriteVisibleHeight                
    INY                                                 Y = sprite height
    JSR .moveXMinus1YMinus1                             source rectangle other corner

    PLA                                                 } A=original X value
    PHA                                                 }
    EOR #1                                              
    TAX                                                 X ^= 1
    LDY #0                                              destination point (X,Y)
    JSR .seditCopyRectangleAbsolute                     

    PLA                                                 }
    TAX                                                 } X=original X value
    BEQ +                                               

    LDX .seditCurrentSpriteVisibleWidth                 
+
    STX .seditCurrentVisibleX                           
    LDA .seditScreenXOffsetWithinCurrentByte            
    PHA                                                 
    LDA #0                                              
    STA .seditScreenXOffsetWithinCurrentByte            
    LDA .vduColourMaskLeft                              
    STA .seditCurrentVisibleByteMask                    

-
    JSR .seditDrawLargeSpriteColumn                     

    SEC                                                 
    LDX .vduCurrentScreenMODE                           
    LDA .seditXCoordinateIncrement,X                    
    ADC .seditScreenXOffsetWithinCurrentByte            
    STA .seditScreenXOffsetWithinCurrentByte            

    LSR .seditCurrentVisibleByteMask                    
    BCC -                                               

    PLA                                                 
    STA .seditScreenXOffsetWithinCurrentByte            
    JMP .restoreUVWAndDrawCursor                        

§31. Copy visible area rectangle up or down.

 On Entry:
   X: 0=move down
      1=move up
   Y: Y coordinate to move down from
.moveVisibleAreaUpOrDown = $ae5a
    LDA .seditCurrentVisibleX                           }
    PHA                                                 }
    LDA .seditCurrentVisibleY                           } remember current position
    PHA                                                 }
    LDA .seditCurrentVisibleByteMask                    }
    PHA                                                 }

    TXA                                                 }
    PHA                                                 } remember X
    LDX .seditCurrentSpriteVisibleWidth                 
    INX                                                 X=width
    JSR .moveXMinus1YMinus1                             source rectangle (top right)

    PLA                                                 }
    PHA                                                 } recall X
    TAY                                                 Y = original X (0=move down or
                                                        1=move up)
    LDX #0                                              
    JSR .moveXY                                         source rectangle (bottom left)

    PLA                                                 }
    PHA                                                 } recall X (0=move down or 1=move up)
    EOR #1                                              
    TAY                                                 Y = X^1 (reversed: 1=move down or
                                                        0=move up)
    LDX #0                                              destination point (X,Y)
    JSR .seditCopyRectangleAbsolute                     

    PLA                                                 recall X (0=move down or 1=move up)
    TAY                                                 
    BEQ +                                               if move down, then cursor Y = 0
    LDY .seditCurrentSpriteVisibleHeight                              else cursor Y =
                                                        visible height
+
    STY .seditCurrentVisibleY                           
    JSR .seditDrawLargeSpriteRow                        draw last row that was left behind
                                                        by the move

.restoreUVWAndDrawCursor = $ae8e
    PLA                                                 }
    STA .seditCurrentVisibleByteMask                    }
    PLA                                                 } recall current position
    STA .seditCurrentVisibleY                           }
    PLA                                                 }
    STA .seditCurrentVisibleX                           }
    JMP .drawCircleCursor                               draw cursor

§32. moveXMinus1YMinus1.

.moveXMinus1YMinus1 = $ae9d
    STX .seditCurrentVisibleX                           
    STY .seditCurrentVisibleY                           
    LDX #4                                              
    JSR .plotX                                          MOVE (X-1,Y-1)

    JSR .seditGetScreenCoordinates                      

    Decrement plotPointX
    LDA .plotPointXLow                                  
    BNE +                                               
    DEC .plotPointXHigh                                 
+
    DEC .plotPointXLow                                  

    Decrement plotPointY
    LDA .plotPointYLow                                  
    BNE +                                               
    DEC .plotPointYHigh                                 
+
    DEC .plotPointYLow                                  

    JMP .outputXYCoordinates                            

§33. MOVE vdu[X],vdu[Y].

.moveXY = $aec4
    STX .seditCurrentVisibleX                           
    LDX #4                                              MOVE plot type
    fall through...

§34. PLOT X,vdu[.seditCurrentVisibleX],vdu[Y].

.plotXY = $aec9
    STY .seditCurrentVisibleY                           
    JSR .plotX                                          

    JSR .seditGetScreenCoordinates                      

.outputXYCoordinates = $aed2
    JSR .seditOutputXCoordinate                         
    JMP .seditOutputYCoordinate                         

§35. Copy a rectangle in the large sprite area.

 On Entry:
   X,Y: new visible cursor position
.seditCopyRectangleAbsolute = $aed8
    STX .seditCurrentVisibleX                           

    Remember current sprite editor position on the stack (6 bytes)
    LDX #5                                              loop counter
-
    LDA .seditCurrentVisibleByteMask,X                  
    PHA                                                 
    DEX                                                 
    BPL -                                               

    LDX #$BE                                            copy rectangle absolute
    JSR .plotXY                                         

    Recall current sprite editor position from the stack (6 bytes)
    LDX #0                                              
    LDY #5                                              loop counter
-
    PLA                                                 
    STA .seditCurrentVisibleByteMask,X                  
    INX                                                 
    DEY                                                 
    BPL -                                               

    RTS                                                 

§36. Move sprite cursor left.

.seditCursorLeft = $aef6
    JSR .seditSetPixelIfPenDown                         if pen down, then draw a pixel in
                                                        the current foreground colour

    LDA .seditCurrentVisibleByteMask                    get pixel mask
    ASL                                                 shift mask for next pixel
    BCS .needToMoveToNewByteLeft                        if (need to move to new byte) then
                                                        branch

    Moving within the current byte
    STA .seditCurrentVisibleByteMask                    

    JSR .drawCircleCursor                               undraw cursor

    Move X coordinate of cursor graphic one square left
    CLC                                                 
    LDX .vduCurrentScreenMODE                           
    LDA .seditScreenXOffsetWithinCurrentByte            
    SBC .seditXCoordinateIncrement,X                    subtract
                                                        
                                                        .seditXCoordinateIncrement[currentMODE]
    STA .seditScreenXOffsetWithinCurrentByte            

    JMP .drawCircleCursor                               draw cursor

.needToMoveToNewByteLeft = $af15
    LDA .seditCurrentVisibleX                           
    BEQ .moveVisibleAreaLeft                            if (cursor is on left edge of
                                                        visible area) then branch

    Move cursor within existing visible area
    JSR .drawCircleCursor                               undraw cursor

    DEC .seditCurrentVisibleX                           move visible cursor left
    LDA .vduColourMaskRight                             } store the rightmost mask value
    STA .seditCurrentVisibleByteMask                    }

    LDX .vduCurrentScreenMODE                           
    LDA .seditXCoordinateOffsetForRightmostPixelInByte,X set initial X offset for circle
    STA .seditScreenXOffsetWithinCurrentByte            

    JMP .drawCircleCursor                               draw cursor

.moveVisibleAreaLeft = $af32
    LDA .seditCurrentX                                  
    BNE +                                               if (not already on left edge of
                                                        sprite) branch

    Already on left edge of sprite, so return
    RTS                                                 
+
    JSR .drawCircleCursor                               undraw cursor
    DEC .seditCurrentX                                  
    LDA .vduColourMaskRight                             } store rightmost mask value
    STA .seditCurrentVisibleByteMask                    }

    LDX .vduCurrentScreenMODE                           
    LDA .seditXCoordinateOffsetForRightmostPixelInByte,X set initial X offset for circle
    STA .seditScreenXOffsetWithinCurrentByte            

    LDX #0                                              
    LDY .seditCurrentSpriteVisibleWidth                 
    JMP .moveVisibleAreaLeftOrRight                     

§37. Move sprite cursor right.

.seditCursorRight = $af55
    JSR .seditSetPixelIfPenDown                         if pen down, then draw a pixel in
                                                        the current foreground colour

    LDA .seditCurrentVisibleByteMask                    get pixel mask
    LSR                                                 shift to next pixel
    BCS .needToMoveToNewByteRight                       if (need to move to new byte) then
                                                        branch

    STA .seditCurrentVisibleByteMask                    
    JSR .drawCircleCursor                               undraw cursor

    Move X coordinate of cursor graphic right
    SEC                                                 
    LDX .vduCurrentScreenMODE                           
    LDA .seditScreenXOffsetWithinCurrentByte            
    ADC .seditXCoordinateIncrement,X                    
    STA .seditScreenXOffsetWithinCurrentByte            

    JMP .drawCircleCursor                               draw cursor

.needToMoveToNewByteRight = $af74
    LDA .seditCurrentVisibleX                           
    CMP .seditCurrentSpriteVisibleWidth                 
    BEQ .moveVisibleAreaRight                           if (cursor is on right edge of
                                                        visible area) then branch

    Move cursor within existing visible area
    JSR .drawCircleCursor                               undraw cursor

    INC .seditCurrentVisibleX                           move visible cursor right
    LDA .vduColourMaskLeft                              } store leftmost mask value
    STA .seditCurrentVisibleByteMask                    }
    LDA #0                                              
    STA .seditScreenXOffsetWithinCurrentByte            zero offset of graphics cursor
    JMP .drawCircleCursor                               draw cursor

.moveVisibleAreaRight = $af90
    CLC                                                 
    ADC .seditCurrentX                                  
    LDY #0                                              
    CMP (.currentSpriteDefinitionLow),Y                 compare with sprite width
    BNE .notOnRightEdge                                 
    RTS                                                 

.notOnRightEdge = $af9b
    JSR .drawCircleCursor                               undraw cursor

    INC .seditCurrentX                                  
    LDA .vduColourMaskLeft                              
    STA .seditCurrentVisibleByteMask                    set mask to leftmost pixel mask
    LDA #0                                              
    STA .seditScreenXOffsetWithinCurrentByte            
    LDX #1                                              
    LDY .seditCurrentSpriteVisibleWidth                 
    INY                                                 
    JMP .moveVisibleAreaLeftOrRight                     

§38. Move sprite cursor down.

.seditCursorDown = $afb5
    JSR .seditSetPixelIfPenDown                         if pen down, then draw a pixel in
                                                        the current foreground colour

    LDA .seditCurrentVisibleY                           
    BEQ .cursorIsAtBottomOfVisibleArea                  if (cursor is at bottom of visible
                                                        area) then branch

    JSR .drawCircleCursor                               undraw cursor

    DEC .seditCurrentVisibleY                           move down
    JMP .drawCircleCursor                               draw cursor

.cursorIsAtBottomOfVisibleArea = $afc6
    LDA .seditCurrentY                                  
    BNE .moveVisibleAreaDown                            if (visible area is not already at
                                                        the bottom of the sprite) then branch
    RTS                                                 

.moveVisibleAreaDown = $afcc
    JSR .drawCircleCursor                               undraw cursor
    DEC .seditCurrentY                                  move visible area down one row

    LDX #0                                              move down
    LDY .seditCurrentSpriteVisibleHeight                
    JMP .moveVisibleAreaUpOrDown                        

§39. Move sprite cursor up.

.seditCursorUp = $afda
    JSR .seditSetPixelIfPenDown                         if pen down, then draw a pixel in
                                                        the current foreground colour

    LDA .seditCurrentVisibleY                           
    CMP .seditCurrentSpriteVisibleHeight                
    BEQ .cursorIsAtTopOfVisibleArea                     if (cursor is at top of visible
                                                        area) then branch

    JSR .drawCircleCursor                               undraw cursor

    INC .seditCurrentVisibleY                           move up
    JMP .drawCircleCursor                               draw cursor

.cursorIsAtTopOfVisibleArea = $afee
    CLC                                                 
    ADC .seditCurrentY                                  
    LDY #.spriteHeaderOffsetHeight                      
    CMP (.currentSpriteDefinitionLow),Y                 
    BNE .moveVisibleAreaUp                              if (visible area is not already at
                                                        the top of the sprite) then branch

    RTS                                                 

.moveVisibleAreaUp = $aff9
    JSR .drawCircleCursor                               undraw cursor
    INC .seditCurrentY                                  move visible area up one row

    LDX #1                                              move up
    LDY .seditCurrentSpriteVisibleHeight                
    INY                                                 
    JMP .moveVisibleAreaUpOrDown                        

§40. Move sprite cursor fully left.

.seditCtrlCursorLeft = $b008
    JSR .drawCircleCursor                               

    LDA #0                                              
    STA .seditCurrentX                                  
    JSR .seditDrawLargeSprite                           
    JMP .seditSetVisibleCursorToLeftEdge                

§41. Shift sprite cursor left.

.seditShiftCursorLeft = $b016
    JSR .drawCircleCursor                               undraw cursor

    SEC                                                 
    LDA .seditCurrentX                                  
    SBC #2                                              
    LDX .seditCurrentX                                  
    BEQ .alreadyOnLeftEdge                              if (X already on left edge) then
                                                        branch

    BCC .closeToLeftEdge                                if (X close to left edge) then branch

    STA .seditCurrentX                                  .seditCurrentX -= 2
    JMP .seditDrawLargeSpriteAndCursor                  

.closeToLeftEdge = $b02c
    PHA                                                 remember new X
    LDA #0                                              
    STA .seditCurrentX                                  set .seditCurrentX = 0
    JSR .seditDrawLargeSprite                           
    PLA                                                 recall new X

.alreadyOnLeftEdge = $b036
    CLC                                                 
    ADC .seditCurrentVisibleX                           
    STA .seditCurrentVisibleX                           
    BPL .seditDrawCursor                                if (non-negative) then branch
                                                        (visible cursor X is in range)

.seditSetVisibleCursorToLeftEdge = $b03f
    LDA #0                                              
    STA .seditCurrentVisibleX                           
    STA .seditScreenXOffsetWithinCurrentByte            
    LDA .vduColourMaskLeft                              
.seditStoreByteMaskAndContinue = $b04a
    STA .seditCurrentVisibleByteMask                    
.seditDrawCursor = $b04d
    JMP .drawCircleCursor                               draw cursor

§42. Move sprite cursor fully right.

.seditCtrlCursorRight = $b050
    JSR .drawCircleCursor                               

    SEC                                                 
    LDY #0                                              
    LDA (.currentSpriteDefinitionLow),Y                 
    SBC .seditCurrentSpriteVisibleWidth                 
    STA .seditCurrentX                                  
    JSR .seditDrawLargeSprite                           

    JMP .finishCursorRight                              

§43. Shift sprite cursor right.

.seditShiftCursorRight = $b064
    JSR .drawCircleCursor                               undraw cursor

    SEC                                                 
    LDY #.spriteHeaderOffsetWidth                       }
    LDA (.currentSpriteDefinitionLow),Y                 }
    SBC .seditCurrentSpriteVisibleWidth                 } X = sprite width - visible width
    TAX                                                 }
    CLC                                                 
    LDA .seditCurrentX                                  }
    ADC #2                                              } add 2 to current X
    CPX .seditCurrentX                                  
    BEQ .alreadyOnRightEdge                             

    STX .seditCurrentX                                  
    CMP .seditCurrentX                                  
    BCS .closeToRightEdge                               

    STA .seditCurrentX                                  
    JMP .seditDrawLargeSpriteAndCursor                  

.closeToRightEdge = $b089
    PHA                                                 remember new X coordinate
    JSR .seditDrawLargeSprite                           
    PLA                                                 recall new X coordinate

.alreadyOnRightEdge = $b08e
    SEC                                                 
    SBC .seditCurrentX                                  
    CLC                                                 
    ADC .seditCurrentVisibleX                           
    STA .seditCurrentVisibleX                           
    CMP .seditCurrentSpriteVisibleWidth                 }
    BEQ .seditDrawCursor                                } if (visible X <= sprite width)
                                                        } then branch (visible cursor X is
                                                        } in range)
    BCC .seditDrawCursor                                }

.finishCursorRight = $b0a0
    LDA .seditCurrentSpriteVisibleWidth                 
    STA .seditCurrentVisibleX                           
    LDX .vduCurrentScreenMODE                           
    LDA .seditXCoordinateOffsetForRightmostPixelInByte,X 
    STA .seditScreenXOffsetWithinCurrentByte            
    LDA .vduColourMaskRight                             
    BNE .seditStoreByteMaskAndContinue                  

§44. Move sprite cursor fully down.

.seditCtrlCursorDown = $b0b4
    JSR .drawCircleCursor                               undraw cursor
    LDA #0                                              
    STA .seditCurrentY                                  
    JSR .seditDrawLargeSprite                           
    JMP .setVisibleYToZeroAndContinue                   

§45. Shift sprite cursor down.

.seditShiftCursorDown = $b0c2
    JSR .drawCircleCursor                               undraw cursor
    SEC                                                 }
    LDA .seditCurrentY                                  }
    SBC #8                                              } currentY -= 8
    LDX .seditCurrentY                                  }
    BEQ .alreadyOnBottomEdge                            
    BCC .closeToBottomEdge                              
    STA .seditCurrentY                                  
    JMP .seditDrawLargeSpriteAndCursor                  

.closeToBottomEdge = $b0d8
    PHA                                                 
    LDA #0                                              
    STA .seditCurrentY                                  set to bottom edge
    JSR .seditDrawLargeSprite                           
    PLA                                                 
.alreadyOnBottomEdge = $b0e2
    CLC                                                 
    ADC .seditCurrentVisibleY                           
    STA .seditCurrentVisibleY                           
    BPL .finishCursorDown                               

.setVisibleYToZeroAndContinue = $b0eb
    LDA #0                                              
    STA .seditCurrentVisibleY                           visible Y = bottom edge
.finishCursorDown = $b0f0
    JMP .drawCircleCursor                               draw cursor

§46. Move sprite cursor fully up.

.seditCtrlCursorUp = $b0f3
    JSR .drawCircleCursor                               
    SEC                                                 
    LDY #.spriteHeaderOffsetHeight                      
    LDA (.currentSpriteDefinitionLow),Y                 
    SBC .seditCurrentSpriteVisibleHeight                
    STA .seditCurrentY                                  
    JSR .seditDrawLargeSprite                           
    JMP .setVisibleYToVisibleHeightAndContinue          

§47. Shift sprite cursor up.

.seditShiftCursorUp = $b107
    JSR .drawCircleCursor                               undraw cursor
    SEC                                                 
    LDY #.spriteHeaderOffsetHeight                      
    LDA (.currentSpriteDefinitionLow),Y                 
    SBC .seditCurrentSpriteVisibleHeight                
    TAX                                                 X=sprite height - visible height
    LDA .seditCurrentY                                  
    ADC #7                                              
    CPX .seditCurrentY                                  
    BEQ .alreadyOnTopEdge                               
    STX .seditCurrentY                                  
    CMP .seditCurrentY                                  
    BCS .closeToTopEdge                                 
    STA .seditCurrentY                                  
    JMP .seditDrawLargeSpriteAndCursor                  

.closeToTopEdge = $b12b
    PHA                                                 remember new Y coordinate
    JSR .seditDrawLargeSprite                           
    PLA                                                 recall new Y coordinate
.alreadyOnTopEdge = $b130
    SEC                                                 
    SBC .seditCurrentY                                  
    CLC                                                 
    ADC .seditCurrentVisibleY                           
    STA .seditCurrentVisibleY                           set new visible Y
    CMP .seditCurrentSpriteVisibleHeight                
    BEQ .finishCursorUp                                 
    BCC .finishCursorUp                                 

.setVisibleYToVisibleHeightAndContinue = $b142
    LDA .seditCurrentSpriteVisibleHeight                
    STA .seditCurrentVisibleY                           
.finishCursorUp = $b148
    JMP .drawCircleCursor                               

§48. Deselect current sprite.

 On Exit:
   Preserves Y
.deselectCurrentSprite = $b14b
    TYA                                                 }
    PHA                                                 } remember Y

    LDY #.workspaceOffsetChosenSpriteIndex              
    LDA (.privateWorkspaceLow),Y                        
    STA .vduWorkspaceD                                  store previously selected sprite
                                                        index
    LDA #0                                              zero means no sprite selected
    STA (.privateWorkspaceLow),Y                        
    PLA                                                 } recall Y
    TAY                                                 }
    RTS                                                 

§49. Record the current sprite details in the private workspace.

.rememberCurrentSpriteInWorkspace = $b15b
    Remember address in workspace
    LDY #.workspaceOffsetChosenSpriteAddressLow         }
    LDA .currentSpriteDefinitionLow                     }
    STA (.privateWorkspaceLow),Y                        } workspace[chosen sprite address] =
                                                        } .currentSpriteDefinition
    INY                                                 }
    LDA .currentSpriteDefinitionHigh                    }
    STA (.privateWorkspaceLow),Y                        }

    Remember chosen sprite index in workspace
    INY                                                 Y=#.workspaceOffsetChosenSpriteIndex
    LDA .vduWorkspaceD                                  
    STA (.privateWorkspaceLow),Y                        workspace[chosen sprite index] =
                                                        .vduWorkspaceD
    RTS                                                 

§50. Extend the sprite to insert a new row.

.seditExtendVertically = $b16d
    LDY #.spriteHeaderOffsetHeight                      
    LDA (.currentSpriteDefinitionLow),Y                 get sprite height-1
    CMP #254                                            
    BCS .return25                                       if (height>=255) then branch (return)

    DEY                                                 Y=0
    JSR .seditExtendSpriteInDirectionY                  
    BCS .return25                                       if (cant extend)

    LDY #.spriteHeaderOffsetWidth                       
    LDA (.currentSpriteDefinitionLow),Y                 
    TAX                                                 
    INX                                                 X=sprite width
    TYA                                                 Y=0
.seditNewRowLoop = $b182
    STA (.vduTempStoreDA),Y                             }
    INY                                                 } store zero bytes in the new row
    DEX                                                 }
    BNE .seditNewRowLoop                                }

    LDX .vduCurrentScreenMODE                           
    LDA .seditCurrentSpriteVisibleHeight                
    CMP .seditMaximumVisibleHeight,X                    
    BCS .seditVisibleHeightIsAsBigAsItCanBe             

    JSR .drawRectangleAroundSpriteInBlack               remove current border (draw in black)
    INC .seditCurrentSpriteVisibleHeight                increment visible height
    JSR .drawRectangleAroundSpriteInWhite               draw new border (draw in white)

.seditVisibleHeightIsAsBigAsItCanBe = $b19c
    CLC                                                 
.return25 = $b19d
    RTS                                                 

§51. Rectangle fill in large sprite area.

.seditRectangleFillToCurrentXY = $b19e
    JSR .seditGetScreenCoordinatesForCurrentSprite      
    LDX #$67                                            }
    JSR .plotX                                          } PLOT $67,X,Y (rectangle fill)
    JSR .seditOutputXCoordinate                         }
    JSR .seditOutputYCoordinate                         }
    JMP .drawRectangleAroundSpriteInWhite               

§52. Extend the sprite to insert a new column.

.seditExtendHorizontally = $b1af
    LDY #.spriteHeaderOffsetWidth                       
    LDA (.currentSpriteDefinitionLow),Y                 get sprite width-1
    CMP #$FE                                            
    BCS .return14                                       if (width >=255) then branch

    INY                                                 Y=#.spriteHeaderOffsetHeight
    JSR .seditExtendSpriteInDirectionY                  
    BCS .return14                                       

    Copy sprite bytes across each row
    LDY #.spriteHeaderOffsetHeight                      
    LDA (.currentSpriteDefinitionLow),Y                 
    TAX                                                 
    INX                                                 X=height (loop counter)
    DEY                                                 Y = 0
.copyOuterLoop = $b1c4
    TYA                                                 
    STA (.vduTempStoreDA),Y                             
    JSR .incTempStoreDADB                               

    TXA                                                 
    PHA                                                 push loop counter
    LDA (.currentSpriteDefinitionLow),Y                 
    TAX                                                 

    Copy X bytes from .vduTempStoreDC/D to .vduTempStoreDA/B
.copyLoop = $b1cf
    LDA (.vduTempStoreDC),Y                             
    STA (.vduTempStoreDA),Y                             
    JSR .incTempStoreDCDD                               
    JSR .incTempStoreDADB                               
    DEX                                                 
    BNE .copyLoop                                       

    PLA                                                 get loop counter
    TAX                                                 

    DEX                                                 
    BNE .copyOuterLoop                                  loop back until height bytes are
                                                        copied

    Finish up
    LDX .vduCurrentScreenMODE                           
    LDA .seditCurrentSpriteVisibleWidth                 
    CMP .seditMaximumVisibleWidth,X                     
    BCS .seditVisibleWidthIsAsBigAsItCanBe              

    JSR .drawRectangleAroundSpriteInBlack               remove current border (draw in black)
    INC .seditCurrentSpriteVisibleWidth                 increment visible width
    JSR .drawRectangleAroundSpriteInWhite               draw new border (draw in white)

.seditVisibleWidthIsAsBigAsItCanBe = $b1f5
    CLC                                                 
.return14 = $b1f6
    RTS                                                 

§53. Remove a row from the sprite.

 On Exit:
   Carry flag: set if full large and small sprite redraw needed
.seditReduceVertically = $b1f7
    SEC                                                 
    LDY #.spriteHeaderOffsetHeight                      
    LDA (.currentSpriteDefinitionLow),Y                 get sprite height - 1
    BEQ .return26                                       if (sprite height == 1) then branch

    DEY                                                 Y=0 (remove a row)
    JSR .seditReduceInDirectionY                        

    JSR .drawCircleCursor                               undraw cursor
    JSR .seditClearAndReplotRegularSizeSprite           

    SEC                                                 
    LDY #.spriteHeaderOffsetHeight                      
    LDA (.currentSpriteDefinitionLow),Y                 
    SBC .seditCurrentY                                  
    CMP .seditCurrentVisibleY                           
    BCS .seditDontReduceCurrentVisibleY                 

    DEC .seditCurrentVisibleY                           reduce visibleY by one
.seditDontReduceCurrentVisibleY = $b218
    CMP .seditCurrentSpriteVisibleHeight                
    BCS .seditDrawCursorAndPreserveCarry                

    LDA .seditCurrentY                                  
    BNE .reduceCurrentY                                 

    DEC .seditCurrentSpriteVisibleHeight                

    Clear rectangular area
    JSR .printInlineCounted                             display following message
    !byte 6                                             length of message to print
    !byte 25,4                                          
    !word 0,800                                         PLOT 4,0,800
    JSR .seditRectangleFillToCurrentXY                  

    SEC                                                 
    BCS .seditDrawCursorAndPreserveCarry                ALWAYS branch

.reduceCurrentY = $b235
    DEC .seditCurrentY                                  
    INC .seditCurrentVisibleY                           
    JSR .seditDrawLargeSprite                           
    CLC                                                 
.seditDrawCursorAndPreserveCarry = $b23f
    PHP                                                 remember carry
    JSR .drawCircleCursor                               draw cursor
    PLP                                                 recall carry
.return26 = $b244
    RTS                                                 

§54. Remove a column from the sprite.

.seditReduceHorizontally = $b245
    LDY #.spriteHeaderOffsetWidth                       
    LDA (.currentSpriteDefinitionLow),Y                 get sprite width-1
    BEQ .return26                                       if (sprite width == 1) then branch
                                                        (return)

    LDA .currentSpriteDefinitionLow                     }
    STA .vduTempStoreDC                                 } .vduTempStoreDC/DD = the current
                                                        } sprite definition address
    LDA .currentSpriteDefinitionHigh                    }
    STA .vduTempStoreDD                                 }

    JSR .moveToNextSprite                               .vduTempStoreDC/DD = next sprite
                                                        address minus one
    JSR .decTempStoreDCDD                                                  = address of last
                                                        byte of current sprite

    LDA .vduTempStoreDC                                 }
    STA .vduTempStoreDA                                 } .vduTempStoreDA/DB = address of
                                                        } last byte of current sprite
    LDA .vduTempStoreDD                                 }
    STA .vduTempStoreDB                                 }

    Copy sprite data to the end of sprite
    the source and destination addresses start out the same, but at the end of each row
    we reduce the source pointer by one byte, removing a column of bytes from the data
    LDY #.spriteHeaderOffsetHeight                      
    LDA (.currentSpriteDefinitionLow),Y                 get sprite height-1
    TAX                                                 
    INX                                                 X=sprite height (loop counter)
    LDY #0                                              [NOTE: Redundant]
.seditReduceLoop = $b269
    TXA                                                 
    PHA                                                 remember sprite height remaining
    LDY #.spriteHeaderOffsetWidth                       Y=0
    LDA (.currentSpriteDefinitionLow),Y                 
    TAX                                                 X = sprite width-1 (inner loop
                                                        counter)
.seditCopyRowOfSpriteLoop = $b270
    LDA (.vduTempStoreDC),Y                             
    STA (.vduTempStoreDA),Y                             
    JSR .decTempStoreDCDD                               
    JSR .decTempStoreDADB                               
    DEX                                                 decrement loop counter
    BNE .seditCopyRowOfSpriteLoop                       if more to do then loop back

    JSR .decTempStoreDCDD                               remove one byte from each row

    PLA                                                 }
    TAX                                                 } recall sprite height remaining and
                                                        } decrement
    DEX                                                 }
    BNE .seditReduceLoop                                loop back if more to do

    LDY #1                                              Y=1 (remove a column)
    JSR .seditReduceInDirectionY                        actually remove the column

    JSR .drawCircleCursor                               remove cursor

    JSR .seditClearAndReplotRegularSizeSprite           redraw small sprite

    SEC                                                 
    LDY #.spriteHeaderOffsetWidth                       
    LDA (.currentSpriteDefinitionLow),Y                 get sprite width-1
    SBC .seditCurrentX                                  take off the current X position
    PHA                                                 remember how many columns are to the
                                                        right of the current X
    CMP .seditCurrentVisibleX                           
    BCS .seditSkipDecrementingVisibleX                  

    Update the visible X and other associated variables
    DEC .seditCurrentVisibleX                           
    LDA .vduColourMaskRight                             
    STA .seditCurrentVisibleByteMask                    
    LDX .vduCurrentScreenMODE                           
    LDA .seditXCoordinateOffsetForRightmostPixelInByte,X 
    STA .seditScreenXOffsetWithinCurrentByte            
.seditSkipDecrementingVisibleX = $b2b0
    PLA                                                 recall how many columns are to the
                                                        right of the current X
    CMP .seditCurrentSpriteVisibleWidth                 
    BCS .seditFinishUp                                  

    LDA .seditCurrentX                                  
    BNE .seditSkipDecrementingVisibleWidth              

    DEC .seditCurrentSpriteVisibleWidth                 

    Blank out old column
    JSR .printInlineCounted                             display following message
    !byte 6                                             length of message to print
    !text 25,4                                          
    !word 900,0                                         PLOT 4,900,0

    JSR .seditRectangleFillToCurrentXY                  
    JMP .seditFinishUp                                  

.seditSkipDecrementingVisibleWidth = $b2ce
    DEC .seditCurrentX                                  
    INC .seditCurrentVisibleX                           
    JSR .seditDrawLargeSprite                           

.seditFinishUp = $b2d7
    JMP .drawCircleCursor                               

§55. Extend a sprite.

 On Entry:
   Y: 0 means extend by a column; 1 means extend by a row
.seditExtendSpriteInDirectionY = $b2da
    JSR .getPrivateWorkspaceAddress                     }
    JSR .deselectCurrentSprite                          } NOTE: These calls all preserve Y
    JSR .getFreeSpace                                   }

    TYA                                                 
    TAX                                                 remember Y in X

    CLC                                                 }
    LDA .currentSpriteDefinitionLow                     }
    SBC (.currentSpriteDefinitionLow),Y                 }
    STA .vduTempStoreDA                                 } .vduTempStoreDA/DB = current
                                                        } sprite address - sprite width or
                                                        } height
    LDA .currentSpriteDefinitionHigh                    }
    SBC #0                                              }
    STA .vduTempStoreDB                                 }

    LDA .vduTempStoreDA                                 }
    CMP .vduTempStoreDC                                 } if (end of sprite memory <
                                                        } tempStoreDA/DB) then branch (no
                                                        } room to extend sprite)
    LDA .vduTempStoreDB                                 }
    SBC .vduTempStoreDD                                 }
    BCC .noRoomToExtendSprite                           }

    SEC                                                 }
    LDA (.currentSpriteDefinitionLow),Y                 } get sprite width or height
    LDY #.spriteHeaderOffsetSizeInBytesLow              } add size of sprite
    ADC (.currentSpriteDefinitionLow),Y                 } to give our new size
    STA (.currentSpriteDefinitionLow),Y                 }
    INY                                                 }
                                                        } Y=#.spriteHeaderOffsetSizeInBytesHigh
    LDA #0                                              }
    ADC (.currentSpriteDefinitionLow),Y                 }
    STA (.currentSpriteDefinitionLow),Y                 }

    TXA                                                 recall original Y
    EOR #1                                              flip to opposite direction: 1 to 0
                                                        and 0 to 1
    TAY                                                 
    CLC                                                 }
    LDA #1                                              } also add the length in the
                                                        } opposite direction (ie. the sprite
                                                        } height or width)?
    ADC (.currentSpriteDefinitionLow),Y                 }
    STA (.currentSpriteDefinitionLow),Y                 }

    Shuffle addresses around:
    
      tempStoreDC/DD = current sprite address
      current sprite address = tempStoreDA/DB
    LDY #1                                              
.seditShuffleAddressesLoop = $b319
    LDA .currentSpriteDefinitionLow,Y                   
    STA .vduTempStoreDC,Y                               
    LDA .vduTempStoreDA,Y                               
    STA .currentSpriteDefinitionLow,Y                   
    DEY                                                 
    BPL .seditShuffleAddressesLoop                      

    Copy six bytes of memory from .vduTempStoreDC/DD to .vduTempStoreDA/DB
    i.e. this copies the sprite header
    LDA #6                                              
    STA .vduTempStoreDE                                 
    LDA #0                                              
    STA .vduTempStoreDF                                 
    JSR .blockCopyMemoryIncrementing                    copy from .vduTempStoreDC/DD to
                                                        .vduTempStoreDA/DB

    JSR .rememberCurrentSpriteInWorkspace               

    CLC                                                 
    RTS                                                 

§56. noRoomToExtendSprite.

.noRoomToExtendSprite = $b338
    JSR .rememberCurrentSpriteInWorkspace               

    JSR .printInlineCounted                             display following message
    !byte 9                                             length of message to print
    !text $07,"No room",$0D                             

    LDA #$0F                                            
    TAX                                                 
    JSR .OSBYTE                                         *FX 15,15 (Flush keyboard)

    LDA #$81                                            
    LDX #$96                                            
    LDY #0                                              
    JSR .OSBYTE                                         read key for 150ms

    JSR .printInlineCounted                             display following message
    !byte 7                                             length of message to print
    !text "       "                                     

    SEC                                                 
    RTS                                                 

§57. Reduce a sprite.

.seditReduceInDirectionY = $b364
    JSR .deselectCurrentSprite                          

    TYA                                                 
    TAX                                                 
    CLC                                                 
    LDA (.currentSpriteDefinitionLow),Y                 
    STA .vduTempStoreDA                                 
    LDY #2                                              
    LDA (.currentSpriteDefinitionLow),Y                 
    SBC .vduTempStoreDA                                 
    STA (.currentSpriteDefinitionLow),Y                 
    INY                                                 
    LDA (.currentSpriteDefinitionLow),Y                 
    SBC #0                                              
    STA (.currentSpriteDefinitionLow),Y                 
    SEC                                                 
    LDA .currentSpriteDefinitionLow                     
    STA .vduTempStoreDC                                 
    ADC .vduTempStoreDA                                 
    STA .vduTempStoreDA                                 
    STA .currentSpriteDefinitionLow                     
    LDA .currentSpriteDefinitionHigh                    
    STA .vduTempStoreDD                                 
    ADC #0                                              
    STA .vduTempStoreDB                                 
    STA .currentSpriteDefinitionHigh                    
    LDA #6                                              
    STA .vduTempStoreDE                                 
    LDA #0                                              
    STA .vduTempStoreDF                                 
    JSR .blockCopyMemoryDecrementing                    

    TXA                                                 
    EOR #1                                              
    TAY                                                 Y=X^1
    SEC                                                 
    LDA (.currentSpriteDefinitionLow),Y                 
    SBC #1                                              decrement
    STA (.currentSpriteDefinitionLow),Y                 
    JMP .rememberCurrentSpriteInWorkspace               

§58. Toggle pen up / down.

.seditPenUpDown = $b3ab
    LDY #.workspaceOffsetOptions                        
    LDA (.privateWorkspaceLow),Y                        
    EOR #1                                              flip bit 0 of options
    STA (.privateWorkspaceLow),Y                        
    RTS                                                 

§59. Flood one column of sprite.

.seditFloodColumn = $b3b4
    JSR .drawCircleCursor                               undraw current cursor

    LDA .seditCurrentVisibleY                           
    PHA                                                 remember current visible Y

    CLC                                                 
    LDA .seditCurrentY                                  get sprite pixel Y for bottom left
                                                        of visible area of sprite
    ADC .seditCurrentVisibleY                           add sprite pixel Y offset within the
                                                        visible area
    STA .seditCurrentVisibleY                           store current Y sprite pixel

    PHA                                                 remember it
    LDA .seditCurrentSpriteByteLow                      }
    PHA                                                 } remember address of current sprite
                                                        } data
    LDA .seditCurrentSpriteByteHigh                     }
    PHA                                                 }
    JSR .getColourByteFromPixelOfSprite                 

    JSR .writeSpritePixel                               

.floodColumnUpLoop = $b3d2
    LDY #.spriteHeaderOffsetHeight                      
    LDA .seditCurrentVisibleY                           
    CMP (.currentSpriteDefinitionLow),Y                 check if we have reached the height
                                                        of the sprite
    LDY #.spriteHeaderOffsetWidth                       
    BCS .floodColumnDown                                

    INC .seditCurrentVisibleY                           

    Subtract width from sprite data address
    CLC                                                 
    LDA .seditCurrentSpriteByteLow                      
    SBC (.currentSpriteDefinitionLow),Y                 
    STA .seditCurrentSpriteByteLow                      
    BCS +                                               
    DEC .seditCurrentSpriteByteHigh                     
+

    JSR .writeSpritePixelIfItsBackgroundColour          
    BCC .floodColumnUpLoop                              

.floodColumnDown = $b3f0
    PLA                                                 }
    STA .seditCurrentSpriteByteHigh                     } recall original sprite address
    PLA                                                 } and Y sprite pixel
    STA .seditCurrentSpriteByteLow                      }
    PLA                                                 }
    STA .seditCurrentVisibleY                           }

.floodColumnDownLoop = $b3fa
    LDY #0                                              
    LDA .seditCurrentVisibleY                           
    BEQ .floodColumnFinishUp                            

    DEC .seditCurrentVisibleY                           

    Add width to sprite data address
    SEC                                                 
    LDA .seditCurrentSpriteByteLow                      
    ADC (.currentSpriteDefinitionLow),Y                 
    STA .seditCurrentSpriteByteLow                      
    BCC +                                               
    INC .seditCurrentSpriteByteHigh                     
+

    JSR .writeSpritePixelIfItsBackgroundColour          
    BCC .floodColumnDownLoop                            

.floodColumnFinishUp = $b414
    JSR .seditPlotRegularSizeSprite                     plot the regular size sprite
    JSR .seditDrawLargeSpriteColumn                     draw new large sprite column

    PLA                                                 
    STA .seditCurrentVisibleY                           
    JMP .drawCircleCursor                               draw current cursor

§60. Flood one row of sprite.

.seditFloodRow = $b421
    JSR .drawCircleCursor                               undraw current cursor

    Remember current visible X and byte mask (twice)
    LDA .seditCurrentVisibleX                           
    PHA                                                 
    LDA .seditCurrentVisibleByteMask                    
    PHA                                                 
    PHA                                                 

    CLC                                                 
    LDA .seditCurrentX                                  get sprite pixel X for bottom left
                                                        of visible area of sprite
    ADC .seditCurrentVisibleX                           add sprite pixel X offset within the
                                                        visible area
    STA .seditCurrentVisibleX                           store current X sprite pixel

    PHA                                                 remember it
    LDA .seditCurrentSpriteByteLow                      }
    PHA                                                 } remember address of current sprite
                                                        } data
    LDA .seditCurrentSpriteByteHigh                     }
    PHA                                                 }
    JSR .getColourByteFromPixelOfSprite                 

    JSR .writeSpritePixel                               

.floodRowRightLoop = $b444
    LSR .seditCurrentVisibleByteMask                    
    BCC .writePixel1                                    

    Start next byte going
    LDA .vduColourMaskLeft                              
    STA .seditCurrentVisibleByteMask                    
    LDY #.spriteHeaderOffsetWidth                       
    LDA .seditCurrentVisibleX                           
    CMP (.currentSpriteDefinitionLow),Y                 if (reached right edge of sprite)
                                                        then branch
    BCS .floodRowLeft                                   

    INC .seditCurrentVisibleX                           

    Decrement sprite byte (sprite bytes are stored right to left)
    LDA .seditCurrentSpriteByteLow                      
    BNE +                                               
    DEC .seditCurrentSpriteByteHigh                     
+
    DEC .seditCurrentSpriteByteLow                      

.writePixel1 = $b463
    JSR .writeSpritePixelIfItsBackgroundColour          
    BCC .floodRowRightLoop                              

.floodRowLeft = $b468
    PLA                                                 }
    STA .seditCurrentSpriteByteHigh                     }
    PLA                                                 }
    STA .seditCurrentSpriteByteLow                      } restore original values after
                                                        } filling right
    PLA                                                 }
    STA .seditCurrentVisibleX                           }
    PLA                                                 }
    STA .seditCurrentVisibleByteMask                    }
.floodRowLeftLoop = $b476
    ASL .seditCurrentVisibleByteMask                    
    BCC .writePixel2                                    

    LDA .vduColourMaskRight                             
    STA .seditCurrentVisibleByteMask                    
    LDY #0                                              
    LDA .seditCurrentVisibleX                           
    BEQ .floodRowFinishUp                               if (at left edge of sprite) then
                                                        branch

    Move left one pixel
    DEC .seditCurrentVisibleX                           

    Add one to sprite byte address (sprites are stored right to left)
    INC .seditCurrentSpriteByteLow                      
    BNE .writePixel2                                    
    INC .seditCurrentSpriteByteHigh                     

.writePixel2 = $b491
    JSR .writeSpritePixelIfItsBackgroundColour          
    BCC .floodRowLeftLoop                               

.floodRowFinishUp = $b496
    JSR .seditPlotRegularSizeSprite                     plot the regular size sprite
    JSR .seditDrawLargeSpriteRow                        draw new large sprite row

    PLA                                                 }
    STA .seditCurrentVisibleByteMask                    } restore byte mask and X position
    PLA                                                 }
    STA .seditCurrentVisibleX                           }
    JMP .drawCircleCursor                               draw current cursor

§61. Get the current pixel colour from a sprite returned as a solid colour byte.

.getColourByteFromPixelOfSprite = $b4a7
    LDY #0                                              
    LDA (.seditCurrentSpriteByteLow),Y                  sprite byte
    AND .seditCurrentVisibleByteMask                    mask off what isn't the current pixel
    STA .vduWorkspaceA                                  

    Shift pixel value up to the top bits
    LDA .seditCurrentVisibleByteMask                    
    BMI +                                               

.updateMaskLoop = $b4b6
    ASL .vduWorkspaceA                                  
    ASL                                                 shift mask value up too
    BPL .updateMaskLoop                                 

    Shift pixel down the byte, making each pixel the same colour, so we end up with
    a byte of solid colour based on the pixel from the sprite
+
    LDX .vduPixelsPerByteMinusOne                       
    LDA .vduWorkspaceA                                  
.updateSpriteByteLoop = $b4c2
    LSR                                                 
    ORA .vduWorkspaceA                                  
    DEX                                                 
    BNE .updateSpriteByteLoop                           

    STA .vduWorkspaceA                                  
    RTS                                                 

§62. writeSpritePixelIfItsBackgroundColour.

.writeSpritePixelIfItsBackgroundColour = $b4cd
    LDY #0                                              
    LDA (.seditCurrentSpriteByteLow),Y                  read sprite byte
    EOR .vduWorkspaceA                                  EOR with background colour
    AND .seditCurrentVisibleByteMask                    apply mask
    SEC                                                 
    BNE .return19                                       if (not background colour) then
                                                        branch (return)

    Sprite pixel is background colour, write new pixel there
    JSR .writeSpritePixel                               

    CLC                                                 
.return19 = $b4de
    RTS                                                 

§63. Insert row.

.seditInsertRow = $b4df
    JSR .seditExtendVertically                          
    BCS .return27                                       if (no room to extend) then branch
                                                        (return)

    CLC                                                 }
    LDA .currentSpriteDefinitionLow                     }
    ADC #5                                              }
    STA .vduTempStoreDC                                 } tempStoreDC/DD = current sprite
                                                        } address + 5
    LDA .currentSpriteDefinitionHigh                    }
    ADC #0                                              }
    STA .vduTempStoreDD                                 }

    LDY #.spriteHeaderOffsetHeight                      }
    SEC                                                 }
    LDA (.currentSpriteDefinitionLow),Y                 }
    SBC .seditCurrentY                                  } X = (sprite height - current
                                                        } position)
    SEC                                                 } i.e. the number of rows above the
                                                        } current row
    SBC .seditCurrentVisibleY                           }
    DEY                                                 } Y = 0
    TAX                                                 }
.seditInsertRowLoop = $b4ff
    LDA .vduTempStoreDC                                 
    STA .vduTempStoreDA                                 
    SEC                                                 
    LDA (.currentSpriteDefinitionLow),Y                 sprite width-1
    TAY                                                 
    INY                                                 Y=sprite width (loop counter)
    ADC .vduTempStoreDC                                 }
    STA .vduTempStoreDC                                 } .vduTempStoreDC/DD = source
    LDA .vduTempStoreDD                                 } .vduTempStoreDA/DB = destination
    STA .vduTempStoreDB                                 }
    BCC .seditCopyRowLoop                               }
    INC .vduTempStoreDD                                 }
.seditCopyRowLoop = $b514
    LDA (.vduTempStoreDC),Y                             
    STA (.vduTempStoreDA),Y                             
    DEY                                                 
    BNE .seditCopyRowLoop                               

    DEX                                                 
    BNE .seditInsertRowLoop                             

    Store zeroes in the newly inserted row
    Y=0 here
    LDA (.currentSpriteDefinitionLow),Y                 get sprite width-1
    TAY                                                 
    INY                                                 Y=sprite width
.seditClearNewRowLoop = $b522
    LDA #0                                              
    STA (.vduTempStoreDC),Y                             
    DEY                                                 
    BNE .seditClearNewRowLoop                           

    JMP .seditDrawLargeAndRegularSpriteAndUpdateCursor  

.return27 = $b52c
    RTS                                                 

§64. Delete row.

.seditDeleteRow = $b52d
    LDY #.spriteHeaderOffsetHeight                      
    LDA (.currentSpriteDefinitionLow),Y                 get sprite height-1
    BEQ .return28                                       if (sprite height == 1) then branch
                                                        (return)

    SEC                                                 }
    SBC .seditCurrentY                                  }
    SEC                                                 } Y = sprite height - current
                                                        } position
    SBC .seditCurrentVisibleY                           }
    BEQ .deleteRowAndUpdate                             if (removing the top row) then branch

    Need to copy data
    TAX                                                 X = number of rows to move (loop
                                                        counter)
    DEY                                                 Y = #.spriteHeaderOffsetWidth
    LDA (.currentSpriteDefinitionLow),Y                 get sprite width-1
    TAY                                                 
    INY                                                 Y=sprite width
    JSR .multiply8x8                                    .vduTempStoreDE/DF = (number of rows
                                                        to move) * (sprite width)

    tempStoreDC/DD = current sprite definition + number of bytes to move
    CLC                                                 
    LDA .currentSpriteDefinitionLow                     
    ADC .vduTempStoreDE                                 
    STA .vduTempStoreDC                                 
    LDA .currentSpriteDefinitionHigh                    
    ADC .vduTempStoreDF                                 
    STA .vduTempStoreDD                                 
    LDA .vduTempStoreDC                                 
    ADC #5                                              
    STA .vduTempStoreDC                                 
    BCC +                                               
    INC .vduTempStoreDD                                 
+

    Copy bytes from tempStoreDC/DD to tempStroeDA/DB
    LDY #.spriteHeaderOffsetWidth                       
.copySpriteDataForDeleteRow = $b55f
    CLC                                                 }
    LDA .vduTempStoreDC                                 }
    STA .vduTempStoreDA                                 }
                                                        } .vduTempStoreDA/DB=.vduTempStoreDC/DD
    SBC (.currentSpriteDefinitionLow),Y                 } .vduTempStoreDC/DD=-width
    STA .vduTempStoreDC                                 }
    LDA .vduTempStoreDD                                 }
    STA .vduTempStoreDB                                 }
    SBC #0                                              }
    STA .vduTempStoreDD                                 }

    LDA (.currentSpriteDefinitionLow),Y                 
    TAY                                                 
    INY                                                 Y = sprite width (loop counter)
-
    LDA (.vduTempStoreDC),Y                             copy sprite data for row
    STA (.vduTempStoreDA),Y                             
    DEY                                                 
    BNE -                                               
    DEX                                                 
    BNE .copySpriteDataForDeleteRow                     

.deleteRowAndUpdate = $b57e
    JSR .seditReduceVertically                          
    BCC .return28                                       
    JMP .seditDrawLargeAndRegularSpriteAndUpdateCursor  

.return28 = $b586
    RTS                                                 

§65. Print the following message.

 First byte of following data is a count of the characters

 X and Y are preserved
.printInlineCounted = $b587
    PLA                                                 }
    STA .vduTempStoreDA                                 }
    PLA                                                 } get address of following data from
                                                        } stack
    STA .vduTempStoreDB                                 } (minus one)

    TXA                                                 }
    PHA                                                 }
    TYA                                                 } Remember current XY
    PHA                                                 }
    JSR .incTempStoreDADB                               increment pointer to point at data
                                                        itself

    LDY #0                                              
    LDA (.vduTempStoreDA),Y                             
    TAX                                                 count of number of bytes
    JSR .incTempStoreDADB                               

.printCharacterLoop = $b59c
    LDA .vduTempStoreDA                                 }
    PHA                                                 }
    LDA .vduTempStoreDB                                 } remember tempStoreDA/DB
    PHA                                                 }

    LDA (.vduTempStoreDA),Y                             load character
    JSR .OSWRCH                                         print character

    PLA                                                 }
    STA .vduTempStoreDB                                 }
    PLA                                                 } restore tempStoreDA/DB
    STA .vduTempStoreDA                                 }
    JSR .incTempStoreDADB                               move to next character

    DEX                                                 decrement counter
    BNE .printCharacterLoop                             if (not finished) then branch

    PLA                                                 }
    TAY                                                 }
    PLA                                                 } Restore XY
    TAX                                                 }
    JMP (.vduTempStoreDA)                               return to following instruction

§66. Insert column.

.seditInsertColumn = $b5ba
    JSR .createMaskByteForPixelsToTheRight              
    STA .vduTempStoreDC                                 

    LDY #.spriteHeaderOffsetHeight                      
    LDA (.currentSpriteDefinitionLow),Y                 
    TAX                                                 
    INX                                                 X=sprite height
    CLC                                                 
    LDA .currentSpriteDefinitionLow                     }
    ADC #5                                              }
    STA .vduTempStoreDA                                 } tempStoreDA/DB = sprite address + 5
    LDA .currentSpriteDefinitionHigh                    }
    ADC #0                                              }
    STA .vduTempStoreDB                                 }

    SEC                                                 }
    LDY #.spriteHeaderOffsetWidth                       }
    LDA (.currentSpriteDefinitionLow),Y                 }
    SBC .seditCurrentX                                  } .vduTempStoreDF = width - current
                                                        } position
    SEC                                                 }                 = number of bytes
                                                        } to the right of the current
                                                        } position
    SBC .seditCurrentVisibleX                           }
    STA .vduTempStoreDF                                 }
    INC .vduTempStoreDF                                 }
.insertColumnLoop = $b5e2
    TXA                                                 } remember X (the sprite height
                                                        } remaining, the loop counter) on
                                                        } the stack
    PHA                                                 }

    LDY .vduTempStoreDF                                 
    LDA (.vduTempStoreDA),Y                             get sprite byte
    PHA                                                 remember it on the stack
    ORA .vduTempStoreDC                                 OR with mask of pixels to the right
    EOR .vduTempStoreDC                                 EOR to get the pixels to the left
                                                        only
    STA .vduTempStoreDE                                 store result

    LDA (.vduTempStoreDA),Y                             get sprite byte again
    AND .vduTempStoreDC                                 
    ORA .vduColourMaskRight                             
    EOR .vduColourMaskRight                             
    LSR                                                 shift byte right to move the pixels
    ORA .vduTempStoreDE                                 add contribution from previous byte
    STA (.vduTempStoreDA),Y                             store back in sprite byte
    PLA                                                 recall sprite byte
    AND .vduColourMaskRight                             

    Shift A left by pixels per byte-1
    LDX .vduPixelsPerByteMinusOne                       
-
    ASL                                                 
    DEX                                                 
    BNE -                                               

    STA .vduTempStoreDE                                 
    DEY                                                 
    BEQ .seditInsertColumnGotoNextRow                   if (end of row) then branch

.insertColumnLoop2 = $b60e
    LDA (.vduTempStoreDA),Y                             read sprite byte
    PHA                                                 
    ORA .vduColourMaskRight                             
    EOR .vduColourMaskRight                             
    LSR                                                 
    ORA .vduTempStoreDE                                 
    STA (.vduTempStoreDA),Y                             store back in sprite byte
    PLA                                                 recall original sprite byte
    AND .vduColourMaskRight                             

    Shift A left by pixels per byte-1
    LDX .vduPixelsPerByteMinusOne                       
-
    ASL                                                 
    DEX                                                 
    BNE -                                               

    STA .vduTempStoreDE                                 store pixel from the previous byte
    DEY                                                 
    BNE .insertColumnLoop2                              

.seditInsertColumnGotoNextRow = $b62c
    LDY #.spriteHeaderOffsetWidth                       }
    SEC                                                 }
    LDA .vduTempStoreDA                                 } add sprite width to tempStoreDA/DB
    ADC (.currentSpriteDefinitionLow),Y                 }
    STA .vduTempStoreDA                                 }
    LDA .vduTempStoreDB                                 }
    ADC #0                                              }
    STA .vduTempStoreDB                                 }

    PLA                                                 } Recall loop counter X
    TAX                                                 }
    DEX                                                 
    BNE .insertColumnLoop                               

    JMP .seditDrawLargeAndRegularSpriteAndUpdateCursor  

§67. Delete column.

.seditDeleteColumn = $b643
    JSR .createMaskByteForPixelsToTheRight              
    STA .vduTempStoreDC                                 

    LDY #1                                              
    LDA (.currentSpriteDefinitionLow),Y                 get sprite height-1
    TAX                                                 
    INX                                                 X=sprite height

    tempStoreDA/DB = sprite address + 6
    CLC                                                 
    LDA .currentSpriteDefinitionLow                     
    ADC #6                                              
    STA .vduTempStoreDA                                 
    LDA .currentSpriteDefinitionHigh                    
    ADC #0                                              
    STA .vduTempStoreDB                                 

    SEC                                                 
    LDY #.spriteHeaderOffsetWidth                       
    LDA (.currentSpriteDefinitionLow),Y                 get sprite width-1
    SBC .seditCurrentX                                  subtract current position
    SEC                                                 
    SBC .seditCurrentVisibleX                           
    STA .vduTempStoreDF                                 .vduTempStoreDF = sprite width -
                                                        current position
                                                        i.e. number of columns to move

.deleteColumnLoop = $b669
    TXA                                                 }
    PHA                                                 } store loop counter (number of
                                                        } columns remaining to move)
    LDA #0                                              
    STA .vduTempStoreDE                                 .vduTempStoreDE holds the pixel from
                                                        previous byte
    LDY .vduTempStoreDF                                 
    BEQ .finishUpMovingRow                              

    LDY #0                                              
.moveRowLoop = $b675
    LDA (.vduTempStoreDA),Y                             get sprite byte
    PHA                                                 remember sprite byte
    ORA .vduColourMaskLeft                              
    EOR .vduColourMaskLeft                              
    ASL                                                 shift byte
    ORA .vduTempStoreDE                                 
    STA (.vduTempStoreDA),Y                             store sprite byte
    PLA                                                 recall original sprite byte
    AND .vduColourMaskLeft                              

    Shift A right by pixels per byte-1
    LDX .vduPixelsPerByteMinusOne                       
-
    LSR                                                 
    DEX                                                 
    BNE -                                               

    STA .vduTempStoreDE                                 store pixel from the previous byte
    INY                                                 
    CPY .vduTempStoreDF                                 
    BNE .moveRowLoop                                    

.finishUpMovingRow = $b695
    Move last byte of row
    LDA (.vduTempStoreDA),Y                             
    ORA .vduTempStoreDC                                 
    EOR .vduTempStoreDC                                 
    ORA .vduTempStoreDE                                 
    STA .vduTempStoreDE                                 
    LDA (.vduTempStoreDA),Y                             
    AND .vduTempStoreDC                                 
    ORA .seditCurrentVisibleByteMask                    
    EOR .seditCurrentVisibleByteMask                    
    ASL                                                 
    ORA .vduTempStoreDE                                 
    STA (.vduTempStoreDA),Y                             

    Add width to current address
    LDY #.spriteHeaderOffsetWidth                       }
    SEC                                                 }
    LDA .vduTempStoreDA                                 }
    ADC (.currentSpriteDefinitionLow),Y                 }
    STA .vduTempStoreDA                                 } .vduTempStoreDA/DB += width
    LDA .vduTempStoreDB                                 }
    ADC #0                                              }
    STA .vduTempStoreDB                                 }

    PLA                                                 }  Recall loop counter (number of
                                                        } columns remaining to move)
    TAX                                                 }
    DEX                                                 decrement loop counter
    BNE .deleteColumnLoop                               loop back if not yet done

    JMP .seditDrawLargeAndRegularSpriteAndUpdateCursor  

§68. createMaskByteForPixelsToTheRight.

.createMaskByteForPixelsToTheRight = $b6c5
    LDA .seditCurrentVisibleByteMask                    
    STA .vduTempStoreDC                                 store current pixel mask
-
    ORA .vduTempStoreDC                                 } OR in the new value
    STA .vduTempStoreDC                                 }
    LSR                                                 shift right
    BCC -                                               loop until a set pixel is found

    LDA .vduTempStoreDC                                 
    RTS                                                 

§69. Reverse the pixels in the byte.

 Used when mirroring vertically
.reversePixelsInByteA = $b6d4
    LDY #0                                              
    STY .vduTempStoreDE                                 

    LDY .vduPixelsPerByteMinusOne                       loop counter
-
    ASL .vduTempStoreDE                                 
    PHA                                                 remember A
    AND .vduColourMaskRight                             
    ORA .vduTempStoreDE                                 
    STA .vduTempStoreDE                                 
    PLA                                                 recall A
    LSR                                                 shift A right
    DEY                                                 
    BPL -                                               loop back until done

    LDA .vduTempStoreDE                                 
    RTS                                                 

§70. Mirror sprite vertically.

.seditMirrorVertically = $b6ed
    CLC                                                 
    LDA .currentSpriteDefinitionLow                     
    ADC #5                                              
    STA .vduTempStoreDA                                 
    LDA .currentSpriteDefinitionHigh                    
    ADC #0                                              
    STA .vduTempStoreDB                                 

    LDY #.spriteHeaderOffsetHeight                      
    LDA (.currentSpriteDefinitionLow),Y                 
    CLC                                                 
    ADC #1                                              
    TAX                                                 
.mirrorVerticallyLoop = $b702
    CLC                                                 }
    LDY #.spriteHeaderOffsetWidth                       }
    LDA (.currentSpriteDefinitionLow),Y                 } A = sprite width - 1
    ADC #2                                              } A = sprite width + 1
    STA .vduTempStoreDD                                 }
    LSR                                                 }
    STA .vduTempStoreDC                                 }
    INC .vduTempStoreDD                                 } .vduTempStoreDC = (sprite width +
                                                        } 1)/2]
                                                                          = byte left of
                                                        centre point
                                                                          = destination
                                                        offset
    LSR .vduTempStoreDD                                 } .vduTempStoreDD = (sprite width +
                                                        } 2)/2
                                                                          = byte right of
                                                        centre point
                                                                          = source offset

.mirrorRowLoop = $b712
    Swap two bytes, reversing the pixels in each byte also
    LDY .vduTempStoreDC                                 
    LDA (.vduTempStoreDA),Y                             
    JSR .reversePixelsInByteA                           
    PHA                                                 remember byte

    LDY .vduTempStoreDD                                 
    LDA (.vduTempStoreDA),Y                             read source byte
    JSR .reversePixelsInByteA                           reverse it

    LDY .vduTempStoreDC                                 
    STA (.vduTempStoreDA),Y                             store in destination

    PLA                                                 recall byte
    LDY .vduTempStoreDD                                 
    STA (.vduTempStoreDA),Y                             store in source

    INC .vduTempStoreDD                                 
    DEC .vduTempStoreDC                                 
    BNE .mirrorRowLoop                                  

    Add width to the current address
    SEC                                                 carry set, add one more than usual
    LDY #.spriteHeaderOffsetWidth                       
    LDA .vduTempStoreDA                                 
    ADC (.currentSpriteDefinitionLow),Y                 add sprite width-1
    STA .vduTempStoreDA                                 
    BCC +                                               
    INC .vduTempStoreDB                                 
+

    DEX                                                 decrement loop counter
    BNE .mirrorVerticallyLoop                           loop back until done

    JMP .seditDrawLargeAndRegularSpriteAndUpdateCursor  

§71. Mirror sprite horizontally.

.seditMirrorHorizontally = $b743
    CLC                                                 }
    LDA .currentSpriteDefinitionLow                     }
    STA .vduTempStoreDC                                 }
    ADC #5                                              } .vduTempStoreDC/DD = sprite
                                                        } definition address
    STA .vduTempStoreDA                                 } .vduTempStoreDA/DB = sprite
                                                        } definition address + 5
    LDA .currentSpriteDefinitionHigh                    }
    STA .vduTempStoreDD                                 }
    ADC #0                                              }
    STA .vduTempStoreDB                                 }

    JSR .moveToNextSprite                               move address to start of next sprite
    JSR .decTempStoreDCDD                               move address back one to be the last
                                                        byte of the current sprite

    .vduTempStoreDC/DD -= sprite width
    CLC                                                 carry clear, subtract one more than
                                                        normal
    LDY #.spriteHeaderOffsetWidth                       
    LDA .vduTempStoreDC                                 
    SBC (.currentSpriteDefinitionLow),Y                 
    STA .vduTempStoreDC                                 
    BCS +                                               
    DEC .vduTempStoreDD                                 
+

    LDY #.spriteHeaderOffsetHeight                      
    LDA (.currentSpriteDefinitionLow),Y                 A = sprite height-1
    BEQ .skipMirrorVertically                           if (sprite has height 1) then branch

    CLC                                                 
    ADC #1                                              
    LSR                                                 
    TAX                                                 X = sprite height / 2 (loop counter)
.mirrorHorizontalMainLoop = $b772
    LDY #.spriteHeaderOffsetWidth                       
    LDA (.currentSpriteDefinitionLow),Y                 
    TAY                                                 
    INY                                                 Y = sprite width (loop counter)

    Swap row in top half of sprite with row in bottom half of sprite
.mirrorRowHorizontalLoop = $b778
    LDA (.vduTempStoreDA),Y                             
    PHA                                                 remember byte from top half
    LDA (.vduTempStoreDC),Y                             
    STA (.vduTempStoreDA),Y                             byte in top half = byte in bottom
                                                        half
    PLA                                                 recall original byte from top half
    STA (.vduTempStoreDC),Y                             store in bottom half
    DEY                                                 decrement loop counter
    BNE .mirrorRowHorizontalLoop                        loop back until done

    Add sprite width to top half of sprite address
    SEC                                                 
    LDY #.spriteHeaderOffsetWidth                       
    LDA .vduTempStoreDA                                 
    ADC (.currentSpriteDefinitionLow),Y                 
    STA .vduTempStoreDA                                 
    BCC +                                               
    INC .vduTempStoreDB                                 
+

    Subtract sprite width from bottom half address
    CLC                                                 
    LDA .vduTempStoreDC                                 
    SBC (.currentSpriteDefinitionLow),Y                 
    STA .vduTempStoreDC                                 
    BCS +                                               
    DEC .vduTempStoreDD                                 
+
    DEX                                                 loop counter
    BNE .mirrorHorizontalMainLoop                       

.skipMirrorVertically = $b7a0
    JMP .seditDrawLargeAndRegularSpriteAndUpdateCursor  

§72. incTempStoreDADB.

.incTempStoreDADB = $b7a3
    INC .vduTempStoreDA                                 
    BNE .return2                                        
    INC .vduTempStoreDB                                 
.return2 = $b7a9
    RTS                                                 

§73. incTempStoreDCDD.

.incTempStoreDCDD = $b7aa
    INC .vduTempStoreDC                                 
    BNE .return2                                        
    INC .vduTempStoreDD                                 
    RTS                                                 

§74. decTempStoreDADB.

.decTempStoreDADB = $b7b1
    LDA .vduTempStoreDA                                 
    BNE +                                               
    DEC .vduTempStoreDB                                 
+
    DEC .vduTempStoreDA                                 
    RTS                                                 

§75. decTempStoreDCDD.

.decTempStoreDCDD = $b7ba
    LDA .vduTempStoreDC                                 
    BNE +                                               
    DEC .vduTempStoreDD                                 
+
    DEC .vduTempStoreDC                                 
    RTS                                                 

§76. Find the address of the given sprite.

 On Entry:
   A = sprite number to find

 On Exit:
   (A,Y): vduTempStoreDC/DD = start of sprite
   X: Sprite index (in the range 1 to number of sprites), or 0 for not found
   Z Set and X = 0 if we ran out of sprites
.findSpriteA = $b7c3
    PHA                                                 remember sprite number

    address = start of sprite page + 3
    LDY #.workspaceOffsetSpriteStartPage                
    LDA (.privateWorkspaceLow),Y                        
    STA .vduTempStoreDD                                 .vduTempStoreDD = sprite start page
    LDA #3                                              
    STA .vduTempStoreDC                                 .vduTempStoreDC = 3

    LDY #.workspaceOffsetNumberOfSprites                
    LDA (.privateWorkspaceLow),Y                        
    TAX                                                 X = number of sprites = loop counter
    BEQ +                                               

-
    LDY #5                                              
    PLA                                                 
    PHA                                                 recall and remember sprite number
    CMP (.vduTempStoreDC),Y                             have we reached the sprite we want?
    BEQ +                                               if (yes) then branch
    JSR .moveToNextSprite                               move .vduTempStoreDC/D to next sprite
    DEX                                                 
    BNE -                                               if (X != 0) then branch (loop back)

+
    PLA                                                 recall and discard the sprite number
                                                        now
    LDA .vduTempStoreDC                                 }
    LDY .vduTempStoreDD                                 } get address in AY
    CPX #0                                              did we reach the end of the sprite
                                                        list?
    RTS                                                 

§77. Move address to the start of the next sprite.

 On Entry:
       .vduTempStoreDC/DD points to a sprite
 On Exit:
       .vduTempStoreDC/DD points to the next sprite
       Preserves X
.moveToNextSprite = $b7eb
    CLC                                                 }
    LDY #2                                              }
    LDA (.vduTempStoreDC),Y                             }
    ADC .vduTempStoreDC                                 }
    PHA                                                 }
    INY                                                 }
    LDA (.vduTempStoreDC),Y                             }
    ADC .vduTempStoreDD                                 }
    STA .vduTempStoreDD                                 } .vduTempStoreDC/DD += size of
                                                        } sprite + 6
    PLA                                                 }
    ADC #6                                              } six is the size of the sprite
                                                        } header data
    STA .vduTempStoreDC                                 }
    BCC +                                               }
    INC .vduTempStoreDD                                 }
+
    RTS                                                 

§78. Get the amount of free space remaining in the sprite data.

 On Exit:
   .vduTempStoreDE/DF holds the free space in bytes
   .vduTempStoreDC/DD holds the end of used sprite memory address
   Preserves A,X,Y
.getFreeSpace = $b804
    PHA                                                 }
    TXA                                                 }
    PHA                                                 } store A,X,Y
    TYA                                                 }
    PHA                                                 }

    LDY #.workspaceOffsetSpriteStartPage                }
    LDA (.privateWorkspaceLow),Y                        }
    STA .vduTempStoreDD                                 } .vduTempStoreDC/DD = sprite page
                                                        } start + 3
    LDA #3                                              }                    = initial space
                                                        } used
    STA .vduTempStoreDC                                 }

    LDY #.workspaceOffsetNumberOfSprites                
    LDA (.privateWorkspaceLow),Y                        get number of sprites
    BEQ +                                               if (no sprites) then branch
    TAX                                                 X = number of sprites = loop counter

-
    JSR .moveToNextSprite                               
    DEX                                                 
    BNE -                                               if (not done yet) then branch back

+
    SEC                                                 
    LDY #.workspaceOffsetSpriteEndPage                  
    LDA #0                                              
    SBC .vduTempStoreDC                                 
    STA .vduTempStoreDE                                 
    LDA (.privateWorkspaceLow),Y                        free space = total space - space used
    SBC .vduTempStoreDD                                 
    STA .vduTempStoreDF                                 

    PLA                                                 }
    TAY                                                 }
    PLA                                                 } recall A,X,Y
    TAX                                                 }
    PLA                                                 }
    RTS                                                 

§79. Skips past any spaces on the command line.

 On Entry:
   (X,Y): address of the current position in the command line

 On Exit:
   (X,Y): address of the rest of the command line
.skipLeadingSpaces = $b835
    STX .privateWorkspaceLow                            }
    STY .privateWorkspaceHigh                           } store pointer to remainder of user
                                                        } input string
    LDY #0                                              
.skipSpaces = $b83b
    LDA (.privateWorkspaceLow),Y                        
    CMP #' '                                            
    BNE .nonSpace                                       
    INC .privateWorkspaceLow                            
    BNE .skipSpaces                                     
    INC .privateWorkspaceHigh                           
    JMP .skipSpaces                                     

.nonSpace = $b84a
    LDX .privateWorkspaceLow                            }
    LDY .privateWorkspaceHigh                           } set XY to the remainder of the
                                                        } user input string
    JMP .getPrivateWorkspaceAddress                     get proper workspace address

§80. Read 8 bit number from user input string into A.

 On Exit:
       XY = address of remainder of user input string
       A = .vduTempStoreDE = 8 bit value read
.read8BitNumberIntoA = $b851
    JSR .skipLeadingSpaces                              

    STX .privateWorkspaceLow                            } workspace address is set to current
    STY .privateWorkspaceHigh                           } position in user input string
    LDY #0                                              
    STY .vduTempStoreDE                                 result = 0
    LDA (.privateWorkspaceLow),Y                        get next character
    SEC                                                 
    SBC #'0'                                            convert ASCII digit to value 0-9
    CMP #10                                             
    BCS .badNumberError                                 if (10 or more) then branch (bad
                                                        number)

-
    Multiply the existing result by 10
    TAX                                                 X = digit
    ASL .vduTempStoreDE                                 } .vduTempStoreDE *= 2
    BCS .badNumberError                                 }
    LDA .vduTempStoreDE                                 }
    ASL                                                 }
    BCS .badNumberError                                 }
    ASL                                                 } A = .vduTempStoreDE * 4
    BCS .badNumberError                                 }
    ADC .vduTempStoreDE                                 } A += .vduTempStoreDE
    STA .vduTempStoreDE                                 } .vduTempStoreDE = A =
                                                        } .vduTempStoreDE * 10
    BCS .badNumberError                                 }

    Add the digit in X to the result
    TXA                                                 }
    ADC .vduTempStoreDE                                 } .vduTempStoreDE += digit
    STA .vduTempStoreDE                                 }

    Branch if overflow occurs
    BCS .badNumberError                                 

    Move to next digit
    INC .privateWorkspaceLow                            
    BNE +                                               
    INC .privateWorkspaceHigh                           
+
    Read the next digit
    LDA (.privateWorkspaceLow),Y                        
    SEC                                                 
    SBC #'0'                                            
    CMP #10                                             
    BCC -                                               branch back if there is another digit

    Get result in A, and XY is the address of the rest of the command
    LDA .vduTempStoreDE                                 
    LDX .privateWorkspaceLow                            
    LDY .privateWorkspaceHigh                           
    JMP .getPrivateWorkspaceAddress                     

§81. badNumberError.

.badNumberError = $b897
    JSR .generateError                                  
    !byte $89                                           error number
    !text "Bad number",0                                error message

§82. PLOT X,.

 On Entry:
   X: plot number
.plotX = $b8a6
    LDA #$19                                            }
    JSR .OSWRCH                                         }
    TXA                                                 } VDU 25,X, = PLOT X,
    JMP .OSWRCH                                         }

§83. VDU plotPointX;.

.seditOutputXCoordinate = $b8af
    LDA .plotPointXLow                                  
    JSR .OSWRCH                                         
    LDA .plotPointXHigh                                 
    JMP .OSWRCH                                         

§84. VDU plotPointY;.

.seditOutputYCoordinate = $b8bb
    LDA .plotPointYLow                                  
    JSR .OSWRCH                                         
    LDA .plotPointYHigh                                 
    JMP .OSWRCH                                         

§85. GCOL X,Y.

 On Entry:
   X: GCOL mode
   Y: GCOL logical colour
.gcolXY = $b8c7
    LDA #$12                                            }
    JSR .OSWRCH                                         }
    TXA                                                 }
    JSR .OSWRCH                                         } VDU 18,X,Y = GCOL X,Y
    TYA                                                 }
    JMP .OSWRCH                                         }

§86. 8 bit unsigned multiply, 16 bit result.

 On Entry:
   X = multiplicand
   Y = multiplier
 On Exit:
   vduTempStoreDE/DF: product (16 bit)
.multiply8x8 = $b8d4
    LDA #0                                              
    STA .vduTempStoreDF                                 clear high byte of product
    TYA                                                 
    LSR                                                 shift multiplier and first bit into
                                                        carry
    STA .vduTempStoreDE                                 store multiplier as low byte of
                                                        product
    LDY #8                                              loop counter
.multiply8x8Loop = $b8de
    BCC +                                               skip if bit clear

    CLC                                                 add multiplicand (=X) to high byte
                                                        of product
    TXA                                                 
    ADC .vduTempStoreDF                                 
    STA .vduTempStoreDF                                 
+
    ROR .vduTempStoreDF                                 shift product
    ROR .vduTempStoreDE                                 
    DEY                                                 
    BNE .multiply8x8Loop                                loop 8 times

    RTS                                                 

§87. Print sprite details (in editor).

.seditPrintModeMessage = $b8ee
    JSR .printInlineCounted                             
    !byte 6                                             length of message to print
    !text .charHomeCursor, "Mode "                      
    LDA .vduCurrentScreenMODE                           }
    JSR .printNumberA                                   } Print current screen MOE

    JSR .printInlineCounted                             
    !byte 9                                             length of message to print
    !text .charCR,.charLF,"Sprite "                     
    LDY #5                                              
    LDA (.currentSpriteDefinitionLow),Y                 
    JSR .printNumberA                                   

    JSR .printInlineCounted                             
    !byte 4                                             length of message to print
    !text .charMoveCursor,11,1,"["                      PRINT;TAB(11,1);"[";
    CLC                                                 
    LDY #0                                              
    LDA (.currentSpriteDefinitionLow),Y                 
    ADC #1                                              
    JSR .printNumberA                                   

    LDA #','                                            
    JSR .OSWRCH                                         

    CLC                                                 
    LDY #1                                              
    LDA (.currentSpriteDefinitionLow),Y                 
    ADC #1                                              
    JSR .printNumberA                                   

    JSR .printInlineCounted                             
    !byte 5                                             length of message to print
    !text "] ",.charMoveCursor,0,2                      PRINT;"]";TAB(0,2);

    LDY #.workspaceOffsetOptions                        
    LDA (.privateWorkspaceLow),Y                        
    LSR                                                 
    BCC +                                               if (bit 0 of options clear) then
                                                        branch

    LDA #'D'                                            pen is DOWN
    BNE ++                                              

+
    LDA #'U'                                            pen is UP
++
    JSR .OSWRCH                                         

    JSR .printInlineCounted                             
    !byte 2                                             length of message to print
    !text " ",.charDefineTextColour                     

    CLC                                                 
    LDA .seditBackgroundColour                          
    ADC #$80                                            
    JSR .OSWRCH                                         set text background colour

    JSR .printInlineCounted                             
    !byte 7                                             length of message to print
    !text ' '                                           space
    !text .charDefineTextColour,$80                     change background colour to black
    !text .charMoveCursor,11,2,"("                      PRINT;TAB(11,2);"(";

    CLC                                                 
    LDA .seditCurrentVisibleX                           
    ADC .seditCurrentX                                  
    JSR .printNumberA                                   print cursor X position

    LDA #','                                            
    JSR .OSWRCH                                         PRINT;",";

    CLC                                                 
    LDA .seditCurrentVisibleY                           
    ADC .seditCurrentY                                  
    JSR .printNumberA                                   print cursor Y position

    JSR .printInlineCounted                             
    !byte 6                                             length of message to print
    !text ")  ",.charMoveCursor,0,4                     PRINT;TAB(0,4);
    RTS                                                 

§88. Print the 8 bit number in A.

.printNumberA = $b98a
    LDX #0                                              
    fall through...

§89. Print the 16 bit number in XA.

.printNumberXA = $b98c
    LDY #0                                              
    STY .tempNumberPrintedFlag                          'have we printed a digit yet?' flag
    STA .tempNumberToPrintLow                           number (low)
    STX .tempNumberToPrintHigh                          number (high)
    ORA .tempNumberToPrintHigh                          
    BEQ .printDigitY                                    if (number is zero) then branch

    LDX #0                                              pointer to table of powers of ten
                                                        X is twice the number of digits
                                                        handled so far
.digitLoop = $b99a
    LDY #0                                              counter for current digit
.subtractionLoop = $b99c
    INY                                                 
    LDA .tempNumberToPrintLow                           }
    SEC                                                 }
    SBC .powersOfTenTable,X                             }
    STA .tempNumberToPrintLow                           } A8/A9 -= power of ten for current
                                                        } digit
    LDA .tempNumberToPrintHigh                          }
    SBC .powersOfTenTable + 1,X                         }
    STA .tempNumberToPrintHigh                          }
    BCC .doneSubtraction                                }
    JMP .subtractionLoop                                

.printDigitY = $b9b1
    TYA                                                 }
    CLC                                                 } convert to ASCII digit
    ADC #$30                                            }
    JMP .OSWRCH                                         print digit

.doneSubtraction = $b9b8
    LDA .tempNumberToPrintLow                           }
    CLC                                                 }
    ADC .powersOfTenTable,X                             }
    STA .tempNumberToPrintLow                           } add power of ten for current digit
    LDA .tempNumberToPrintHigh                          }
    ADC .powersOfTenTable + 1,X                         }
    STA .tempNumberToPrintHigh                          }
    DEY                                                 
    BNE +                                               if (digit is not zero) then branch

    LDA .tempNumberPrintedFlag                          
    BEQ .moveToNextDigit                                if (we have not printed a digit yet)
                                                        then branch

+
    LDA #$FF                                            
    STA .tempNumberPrintedFlag                          mark flag to say that a digit is
                                                        printed
    JSR .printDigitY                                    print the digit

.moveToNextDigit = $b9d5
    INX                                                 
    INX                                                 
    TXA                                                 
    CMP #10                                             
    BEQ +                                               
    JMP .digitLoop                                      
+
    RTS                                                 

§90. powersOfTenTable.

.powersOfTenTable = $b9e0
    !word 10000                                         
    !word 1000                                          
    !word 100                                           
    !word 10                                            
    !word 1