*SGET; *SSAVE; *SLOAD; *SMERGE; *SCHOOSE; *SRENUMBER - 1018 bytes (6.2%)
- §1. *SGET n
- §2. noSpriteMemoryErrorLocal
- §3. sgetRectOutOfBounds
- §4. sgetInternal
- §5. noRoomToGetSpriteError
- §6. noSpritesError
- §7. *SSAVE
- §8. *SLOAD
- §9. *SMERGE
- §10. cantOpenFileError
- §11. Reads a sprite file
- §12. notEnoughRoomError
- §13. Prepare the filing system by setting short messages on a cassette / ROM based filing system
- §14. *SCHOOSE n
- §15. spriteDoesntExistError
- §16. starSChooseInternal
- §17. *SRENUMBER n,m
- §18. missingCommaError
- §19. spriteNumbersAreEqualError
Executes 'VDU 23,27,1,n,0,0,0,0,0,0' to capture a rectangle of the screen to sprite n On Entry: XY: address of the rest of the command line
.starSGet = $a40e JSR .read8BitNumberIntoA PHA JSR .printInlineCounted display following message !byte 3 length of message to print !text 23,27,1 get sprite from screen PLA JSR .OSWRCH sprite to get JSR .printInlineCounted display following message !byte 6 length of message to print !text 0,0,0,0,0,0 finish VDU stream RTS
.noSpriteMemoryErrorLocal = $a428 JMP .noSpriteMemoryError
.sgetRectOutOfBounds = $a42b PLA RTS
.sgetInternal = $a42d PHA LDY #.workspaceOffsetSpritePages LDA (.privateWorkspaceLow),Y BEQ .noSpriteMemoryErrorLocal Sort current graphics cursor and old graphics cursor points LDX #.vduOldGraphicsCursorPixelsXLow - .vduVariablesStart JSR .sortCoordinatesAsBottomLeftAndTopRight set .vduWorkspaceAB,CD to bottom left point, and .vduWorkspaceEF,GH to top right LDX #.vduWorkspaceA - .vduVariablesStart JSR .checkPointXIsWithinGraphicsWindow BEQ .sgetBottomLeftPointWithinBounds AND #%00001010 BNE .sgetRectOutOfBounds if (bottom left point of rectangle is right or above graphics window) then branch (exit) LDA .vduTempStoreDA PHA AND #1 BEQ + if (bottom left point of rect is not left of graphics window) then branch LDX #.vduGraphicsWindowPixelsLeftLow - .vduVariablesStart } copy left edge of graphics LDY #.vduWorkspaceA - .vduVariablesStart } window into current workspace } position JSR .copyTwoBytesWithinVDUVariables } i.e. clip to left edge + PLA AND #4 BEQ .sgetBottomLeftPointWithinBounds if (bottom left point is not below the graphics window) then branch LDX #.vduGraphicsWindowPixelsBottomLow - .vduVariablesStart } copy bottom edge of } graphics LDY #.vduWorkspaceC - .vduVariablesStart } window into current workspace } position JSR .copyTwoBytesWithinVDUVariables } i.e. clip to bottom edge .sgetBottomLeftPointWithinBounds = $a45e LDX #.vduWorkspaceE - .vduVariablesStart JSR .checkPointXIsWithinGraphicsWindow BEQ .sgetAllWithinBounds if (top right point is in bounds) AND #5 BNE .sgetRectOutOfBounds if (top right point of rectangle is left or below graphics window) then branch LDA .vduTempStoreDA PHA AND #2 BEQ + if (top right point of rect is not right of the graphics window) then branch LDX #.vduGraphicsWindowPixelsRightLow - .vduVariablesStart } copy right edge of graphics LDY #.vduWorkspaceE - .vduVariablesStart } window into current workspace } position JSR .copyTwoBytesWithinVDUVariables } i.e. clip to right edge + PLA AND #8 BEQ .sgetAllWithinBounds if (top right point of rect is not above the graphics window) then branch LDX #.vduGraphicsWindowPixelsTopLow - .vduVariablesStart } copy top edge of graphics LDY #.vduWorkspaceG - .vduVariablesStart } window into current workspace } position JSR .copyTwoBytesWithinVDUVariables } i.e. clip to top edge .sgetAllWithinBounds = $a483 LDX #.vduWorkspaceE - .vduVariablesStart JSR .setScreenAddress set screen address based on top right point of rectangle .vduWorkspaceAB = width of rect SEC } LDA .vduWorkspaceE } SBC .vduWorkspaceA } STA .vduWorkspaceA } AB = EF - AB = width of rect LDA .vduWorkspaceF } SBC .vduWorkspaceB } STA .vduWorkspaceB } Loop to convert AB into a byte width rather than width in pixels LDA .vduPixelsPerByteMinusOne - LSR .vduWorkspaceB ROR .vduWorkspaceA .vduWorkspaceAB /= 2 LSR BNE - At this point .vduWorkspaceB is zero, because our sprite is less than 256 pixels wide. We reuse .vduWorkspaceB below. Calculate the width and height of the sprite we are going to create X=A+1 width of sprite B=G-C Y=G-C+1 height of sprite If Y=0 Y=255:B-=1 LDX .vduWorkspaceA INX X = .vduWorkspaceA + 1 SEC LDA .vduWorkspaceG } SBC .vduWorkspaceC } B = G - C STA .vduWorkspaceB } TAY INY BNE + DEY DEC .vduWorkspaceB + Calculate width * height to see how much memory we need JSR .multiply8x8 DE/F = X*Y Store result in .vduWorkspaceCD CLC [NOTE: redundant, CLC not used] LDA .vduTempStoreDE STA .vduWorkspaceC LDA .vduTempStoreDF STA .vduWorkspaceD JSR .getFreeSpace Now .vduTempStoreDE/DF holds the free space in bytes PLA PHA JSR .findSpriteA BEQ .skipExistingSprite Add size of existing sprite to free space CLC LDY #.spriteHeaderOffsetSizeInBytesLow LDA (.vduTempStoreDC),Y ADC .vduTempStoreDE STA .vduTempStoreDE INY Y = .spriteHeaderOffsetSizeInBytesHigh LDA (.vduTempStoreDC),Y ADC .vduTempStoreDF STA .vduTempStoreDF Add 6 to free space (for the old sprite header data) LDA #6 } ADC .vduTempStoreDE } STA .vduTempStoreDE } [NOTE: don't add six only to } subtract it again, just skip } forward, i.e.: JMP +] BCC .skipExistingSprite } INC .vduTempStoreDF } .skipExistingSprite = $a4ef Subtract 6 from the free space (for the new sprite header data) SEC LDA .vduTempStoreDE SBC #6 BCS + DEC .vduTempStoreDF + Check we have enough room for the new sprite if (free space >= size of new sprite) CMP .vduWorkspaceC LDA .vduTempStoreDF SBC .vduWorkspaceD BMI .noRoomToGetSpriteError Delete the existing sprite PLA PHA JSR .deleteSpriteInternal tempStoreDC/D = DA/B = start of sprite data DA/B += 6 for the sprite header data CLC LDA .vduTempStoreDA STA .vduTempStoreDC ADC #6 STA .vduTempStoreDA LDA .vduTempStoreDB STA .vduTempStoreDD BCC + INC .vduTempStoreDB + Store sprite number LDY #.spriteHeaderOffsetSpriteNumber PLA PHA STA (.vduTempStoreDC),Y Store current mode DEY Y=.spriteHeaderOffsetModeNumber LDA .vduCurrentScreenMODE STA (.vduTempStoreDC),Y Store width and height (4 bytes) DEY .sgetStoreWidthAndHeight = $a525 LDA .vduWorkspaceA,Y STA (.vduTempStoreDC),Y DEY BPL .sgetStoreWidthAndHeight INC .vduWorkspaceA width INC .vduWorkspaceB height (loop counter) Loop over each row .sgetLoopOverRows = $a533 LDX .vduWorkspaceA width Remember current screen address LDA .vduScreenAddressOfGraphicsCursorCellLow PHA LDA .vduScreenAddressOfGraphicsCursorCellHigh PHA .sgetLoopOverBytesInRow = $a53c LDY .vduGraphicsCursorVerticalOffsetInCell A=byte from screen !if (MACHINE = BBC_B) | (MACHINE = ELECTRON) { LDA (.vduScreenAddressOfGraphicsCursorCellLow),Y read direct from screen memory } else if MACHINE = BBC_B_PLUS { LDA .vduTempStoreDA stash value at $DA which subroutine call will corrupt PHA store on stack JSR .checkPixelIsBackgroundColourFast read byte from screen EOR .vduBackgroundGraphicsColour undo the unwanted eor from subroutine call TAY } PLA } restore $DA, preserving A STA .vduTempStoreDA } TYA } } else { +unknown_machine } LDY #0 STA (.vduTempStoreDA),Y store byte Move sprite address on one byte INC .vduTempStoreDA BNE + INC .vduTempStoreDB + Call OS routine to move graphics cursor left SEC JSR .moveGraphicsCursorAddressTotheLeftAndUpdateMask DEX byte width-=1 BNE .sgetLoopOverBytesInRow Restore the screen address PLA STA .vduScreenAddressOfGraphicsCursorCellHigh PLA STA .vduScreenAddressOfGraphicsCursorCellLow Move graphics cursor down one row INC .vduGraphicsCursorVerticalOffsetInCell LDY .vduGraphicsCursorVerticalOffsetInCell CPY #8 BCC + JSR .plotSpriteMoveDownOneCell STY .vduGraphicsCursorVerticalOffsetInCell + DEC .vduWorkspaceB reduce loop counter BNE .sgetLoopOverRows JSR .addSprite add the sprite PLA JMP .starSChooseInternal choose the new sprite
.noRoomToGetSpriteError = $a574 JSR .generateError !byte $82 error number !text "No room to get sprite",0 error message
.noSpritesError = $a58e JSR .generateError !byte $83 error number !text "No Sprites",0 error message
On Entry: XY is the rest of the command line
.starSSave = $a59d JSR .prepareFilingSystem Store address of filename TYA LDY #.workspaceOffsetFilenameHigh STA (.privateWorkspaceLow),Y TXA DEY Y = .workspaceOffsetFilenameLow STA (.privateWorkspaceLow),Y Check we have at least one sprite LDY #.workspaceOffsetNumberOfSprites LDA (.privateWorkspaceLow),Y BEQ .noSpritesError if (no sprites) then branch (error) JSR .getFreeSpace .vduTempStoreDE/DF = free space in bytes vduTempStoreDA/B = start of sprite memory LDY #.workspaceOffsetSpriteStartPage LDA (.privateWorkspaceLow),Y STA .vduTempStoreDB LDA #0 STA .vduTempStoreDA Store number of sprites LDY #.workspaceOffsetNumberOfSprites } LDA (.privateWorkspaceLow),Y } get number of sprites LDY #2 STA (.vduTempStoreDA),Y store number of sprites Store length of sprite data LDY #0 SEC LDA .vduTempStoreDC STA (.vduTempStoreDA),Y INY LDA .vduTempStoreDD SBC .vduTempStoreDB subtract start of sprite memory STA (.vduTempStoreDA),Y Store $FF in the two high bytes of start address and all of the end address. i.e. six bytes from .workspaceOffsetStartAddressMid2 LDY #.workspaceOffsetStartAddressMid2 + 5 offset from sart of private workspace LDX #6 LDA #$FF - STA (.privateWorkspaceLow),Y DEY DEX BNE - Store start address (lower two bytes) LDY #.workspaceOffsetStartAddressLow LDA #0 low byte zero STA (.privateWorkspaceLow),Y INY LDA .vduTempStoreDB high byte of start of workspace memory STA (.privateWorkspaceLow),Y Store end address (2 bytes more than start) LDY #.workspaceOffsetEndAddressMid1 STA (.privateWorkspaceLow),Y high byte of start of workspace memory DEY LDA #2 low byte of start of workspace memory (two). STA (.privateWorkspaceLow),Y LDX #.workspaceOffsetParamBlock } LDY .privateWorkspaceHigh } LDA #0 } Save two byte file? JSR .OSFILE } JSR .getPrivateWorkspaceAddress .privateWorkspaceLow/High = private workspace address JSR .getFreeSpace .vduTempStoreDE/DF = free space in bytes vduTempStoreDA/B = start of sprite memory LDY #.workspaceOffsetSpriteStartPage LDA (.privateWorkspaceLow),Y STA .vduTempStoreDB LDA #0 STA .vduTempStoreDA Store $FF in two high bytes of start address and all of end address. i.e. six bytes from .workspaceOffsetStartAddressMid2 LDY #.workspaceOffsetStartAddressMid2+5 [NOTE: duplicate of above, could be a function instead to save bytes] LDX #6 LDA #$FF - STA (.privateWorkspaceLow),Y DEY DEX BNE - Store start address (start of sprite memory) LDY #.workspaceOffsetStartAddressLow LDA .vduTempStoreDA STA (.privateWorkspaceLow),Y INY Y=$5E LDA .vduTempStoreDB LDY #.workspaceOffsetStartAddressMid1 [NOTE: Redundant: Y is already set to $5E] STA (.privateWorkspaceLow),Y Store end address LDY #.workspaceOffsetEndAddressLow LDA .vduTempStoreDC STA (.privateWorkspaceLow),Y INY Y = .workspaceOffsetEndAddressMid1 LDA .vduTempStoreDD STA (.privateWorkspaceLow),Y LDX #.workspaceOffsetParamBlock } LDY .privateWorkspaceHigh } LDA #0 } Save file proper JMP .OSFILE }
On Entry: XY is the rest of the command line
.starSLoad = $a638 JSR .prepareFilingSystem STX .vduTempStoreDA store filename address STY .vduTempStoreDB vduTempStoreDC/D = start of sprite data LDY #.workspaceOffsetSpriteStartPage LDA (.privateWorkspaceLow),Y STA .vduTempStoreDD LDA #0 STA .vduTempStoreDC JSR .readSpriteFile BCS + if (failed to load) then branch LDY #.workspaceOffsetNumberOfSprites } STA (.privateWorkspaceLow),Y } store number of sprites JMP .resetCurrentSpriteAddress + RTS
On Entry: XY is the rest of the command line
.starSMerge = $a656 JSR .prepareFilingSystem STX .vduTempStoreDA STY .vduTempStoreDB store filename address JSR .getFreeSpace JSR .readSpriteFile BCS .return15 if (error reading file) then branch TAX X = number of sprites vduTempStoreDC/D += 3 (add 3 to sprite address) CLC LDA .vduTempStoreDC ADC #3 STA .vduTempStoreDC BCC .addSpriteLoop INC .vduTempStoreDD Add each sprite .addSpriteLoop = $a671 TXA PHA JSR .addSprite PLA TAX DEX BNE .addSpriteLoop .return15 = $a67b RTS
.cantOpenFileError = $a67c JSR .generateError !byte $85 error number [NOTE: should be $84] !text "Can't open file",0 error message
On Entry: .vduTempStoreDA/DB - filename address .vduTempStoreDC/DD - load address On Exit: Carry: clear on success A: number of sprites after reading the file vduTempStoreDC/D: end of sprites address
.readSpriteFile = $a690 Store filename address to parameter block in private workspace LDY #.workspaceOffsetFilenameLow } LDA .vduTempStoreDA } STA (.privateWorkspaceLow),Y } set filename address INY } i.e. LDA .vduTempStoreDB } } workspace[.workspaceOffsetFilenameLow] = .vduTempStoreDA = filename address low STA (.privateWorkspaceLow),Y } } workspace[.workspaceOffsetFilenameHigh] = .vduTempStoreDB = filename address high Store load address to parameter block in private workspace INY } Y=#.workspaceOffsetLoadAddressLow LDA .vduTempStoreDC } STA (.privateWorkspaceLow),Y } INY } LDA .vduTempStoreDD } set load address STA (.privateWorkspaceLow),Y } INY } i.e. LDA #$FF } workspace[load address low] = } .vduTempStoreDC (load address low) STA (.privateWorkspaceLow),Y } workspace[load address mid1] = } .vduTempStoreDD (load address high) INY } workspace[load address mid2] = $FF STA (.privateWorkspaceLow),Y } workspace[load address high] = $FF We zero this next byte, meaning 'load the file at the load address given' INY } Y=#.workspaceOffsetExecAddressLow LDA #0 } STA (.privateWorkspaceLow),Y } } workspace[.workspaceOffsetExecAddressLow] = 0 Store load address LDY #.workspaceOffsetEndOfSpritesAddressLow LDA .vduTempStoreDC STA (.privateWorkspaceLow),Y workspace[end of sprites low] = tempStoreDC INY LDA .vduTempStoreDD STA (.privateWorkspaceLow),Y workspace[end of sprite high] = tempStoreDD Open file for reading LDX .vduTempStoreDA } LDY .vduTempStoreDB } Pointer to filename LDA #$40 JSR .OSFIND open file for reading CMP #0 BEQ .cantOpenFileError if (open file fails) then branch Read two bytes (the size) onto the stack TAY Y = file handle JSR .OSBGET get first byte PHA push first byte of file JSR .OSBGET get second byte PHA push second byte Close file LDA #0 JSR .OSFIND close file JSR .getPrivateWorkspaceAddress Recall first two bytes from stack (size) PLA } STA .vduTempStoreDD } .vduTempStoreDC/DD = first two } bytes from file (size) PLA } STA .vduTempStoreDC } Add size to current end of sprites address CLC } LDY #.workspaceOffsetEndOfSpritesAddressLow } LDA (.privateWorkspaceLow),Y } ADC .vduTempStoreDC } STA .vduTempStoreDC } .vduTempStoreDC/DD += } workspace[$65/$66] INY } Y = } } .workspaceOffsetEndOfSpritesAddressHigh LDA (.privateWorkspaceLow),Y } ADC .vduTempStoreDD } STA .vduTempStoreDD } Check for enough room LDY #.workspaceOffsetSpriteEndPage LDA #0 CMP .vduTempStoreDC LDA (.privateWorkspaceLow),Y workspace[.workspaceOffsetSpriteEndPage] - .vduTempStoreDC/DD SBC .vduTempStoreDD BCC .notEnoughRoomError if (workspace[.workspaceOffsetSpriteEndPage] < .vduTempStoreDC/DD) then branch Load file LDX #.workspaceOffsetParamBlock } LDY .privateWorkspaceHigh } address of parameter block LDA #$FF load named file JSR .OSFILE JSR .getPrivateWorkspaceAddress vduTempStoreDC/D = end of sprites address LDY #.workspaceOffsetEndOfSpritesAddressLow } LDA (.privateWorkspaceLow),Y } STA .vduTempStoreDC } INY } LDA (.privateWorkspaceLow),Y } .vduTempStoreDC/DD = } } workspace[.workspaceOffsetEndOfSpritesAddressLow/High] STA .vduTempStoreDD } Get number of sprites LDY #2 } LDA (.vduTempStoreDC),Y } get number of sprites CLC } RTS
.notEnoughRoomError = $a71a JSR .generateError !byte $85 error number !text "Not enough room",0 error message
§13. Prepare the filing system by setting short messages on a cassette / ROM based filing system.
and get the workspace address
.prepareFilingSystem = $a72e TXA } PHA } TYA } store X,Y PHA } LDA #0 } TAY } OSARGS 0,168,0 LDX #$A8 } JSR .OSARGS } return filing system number in A AND #$FC BNE .restoreXY if (not cassette or ROM filing system) then branch LDA #$8B } LDX #1 } *FX 139,1,1 LDY #1 } Equivalent to *OPT 1,1 JSR .OSBYTE } (give short messages) .restoreXY = $a747 PLA TAY PLA TAX JMP .getPrivateWorkspaceAddress
On Entry: XY is the rest of the command line
.starSChoose = $a74e JSR .read8BitNumberIntoA .sChooseA = $a751 JSR .starSChooseInternal BEQ .spriteDoesntExistError RTS
.spriteDoesntExistError = $a757 JSR .generateError !byte $86 error number !text "Sprite doesn't exist",0 error message
.starSChooseInternal = $a770 PHA LDY #.workspaceOffsetSpritePages LDA (.privateWorkspaceLow),Y BEQ .schooseError if no sprite space, then branch (error) PLA JSR .findSpriteA BEQ .dontStoreSpriteAddress if sprite not found, then skip storing the address Record the sprite address LDY #.workspaceOffsetCurrentSpriteAddressLow STA (.privateWorkspaceLow),Y INY LDA .vduTempStoreDD STA (.privateWorkspaceLow),Y CPX #0 RTS .dontStoreSpriteAddress = $a789 LDY #.workspaceOffsetCurrentSpriteAddressLow } TXA } STA (.privateWorkspaceLow),Y } zero the current sprite address INY } STA (.privateWorkspaceLow),Y } CPX #0 RTS .schooseError = $a794 JMP .noSpriteMemoryError
On Entry: XY is the rest of the command line
.starSRenumber = $a797 JSR .read8BitNumberIntoA read first number n PHA push n JSR .skipLeadingSpaces STX .vduTempStoreDE } remember address of remainder of } command STY .vduTempStoreDF } PLA pull n PHA push n JSR .findSpriteA get sprite n address BEQ .spriteNotFound if not found then branch Skip comma LDY #0 LDA (.vduTempStoreDE),Y CMP #',' BNE .missingCommaError Move on to next character LDX .vduTempStoreDE LDY .vduTempStoreDF INX BNE + INY + JSR .read8BitNumberIntoA read second number m PLA pull n PHA push n CMP .vduTempStoreDE BEQ .spriteNumbersAreEqualError if (n == m) then branch LDA .vduTempStoreDE get m PHA push m JSR .deleteSpriteInternal PLA get m STA .vduTempStoreDE store m PLA pull n JSR .findSpriteA get sprite address for sprite n LDY #.spriteHeaderOffsetSpriteNumber LDA .vduTempStoreDE get sprite number m STA (.vduTempStoreDC),Y store sprite number m RTS .spriteNotFound = $a7d6 JMP .spriteDoesntExistError
.missingCommaError = $a7d9 JSR .generateError !byte $87 error number !text "Missing comma",0 error message
§19. spriteNumbersAreEqualError.
.spriteNumbersAreEqualError = $a7eb JSR .generateError !byte $88 error number !text "Sprite numbers are equal",0 error message