(lab2)6502 Math Lab
Setup
1. Have the Driver open the 6502 Emulator at http://6502.cdot.systems in another tab or window, keeping this lab open.
Important: The emulator does not save your work automatically. Remember to periodically save it to a file (copy-and-paste the code or use the Save
button to create local files). Recommendation: save your files to a directory, and use git to manage that directory.
Initial Code
2. The following code moves a 5×5 graphic diagonally across the screen:
; ; draw-image-subroutine.6502 ; ; This is a routine that can place an arbitrary ; rectangular image on to the screen at given ; coordinates. ; ; Chris Tyler 2024-09-17 ; Licensed under GPLv2+ ; ; ; The subroutine is below starting at the ; label "DRAW:" ; ; Test code for our subroutine ; Moves an image diagonally across the screen ; Zero-page variables define XPOS $20 define YPOS $21 START: ; Set up the width and height elements of the data structure LDA #$05 STA $12 ; IMAGE WIDTH STA $13 ; IMAGE HEIGHT ; Set initial position X=Y=0 LDA #$00 STA XPOS STA YPOS ; Main loop for diagonal animation MAINLOOP: ; Set pointer to the image ; Use G_O or G_X as desired ; The syntax #<LABEL returns the low byte of LABEL ; The syntax #>LABEL returns the high byte of LABEL LDA #<G_O STA $10 LDA #>G_O STA $11 ; Place the image on the screen LDA #$10 ; Address in zeropage of the data structure LDX XPOS ; X position LDY YPOS ; Y position JSR DRAW ; Call the subroutine ; Delay to show the image LDY #$00 LDX #$50 DELAY: DEY BNE DELAY DEX BNE DELAY ; Set pointer to the blank graphic LDA #<G_BLANK STA $10 LDA #>G_BLANK STA $11 ; Draw the blank graphic to clear the old image LDA #$10 ; LOCATION OF DATA STRUCTURE LDX XPOS LDY YPOS JSR DRAW ; Increment the position INC XPOS INC YPOS ; Continue for 29 frames of animation LDA #28 CMP XPOS BNE MAINLOOP ; Repeat infinitely JMP START ; ========================================== ; ; DRAW :: Subroutine to draw an image on ; the bitmapped display ; ; Entry conditions: ; A - location in zero page of: ; a pointer to the image (2 bytes) ; followed by the image width (1 byte) ; followed by the image height (1 byte) ; X - horizontal location to put the image ; Y - vertical location to put the image ; ; Exit conditions: ; All registers are undefined ; ; Zero-page memory locations define IMGPTR $A0 define IMGPTRH $A1 define IMGWIDTH $A2 define IMGHEIGHT $A3 define SCRPTR $A4 define SCRPTRH $A5 define SCRX $A6 define SCRY $A7 DRAW: ; SAVE THE X AND Y REG VALUES STY SCRY STX SCRX ; GET THE DATA STRUCTURE TAY LDA $0000,Y STA IMGPTR LDA $0001,Y STA IMGPTRH LDA $0002,Y STA IMGWIDTH LDA $0003,Y STA IMGHEIGHT ; CALCULATE THE START OF THE IMAGE ON ; SCREEN AND PLACE IN SCRPTRH ; ; THIS IS $0200 (START OF SCREEN) + ; SCRX + SCRY * 32 ; ; WE'LL DO THE MULTIPLICATION FIRST ; START BY PLACING SCRY INTO SCRPTR LDA #$00 STA SCRPTRH LDA SCRY STA SCRPTR ; NOW DO 5 LEFT SHIFTS TO MULTIPLY BY 32 LDY #$05 ; NUMBER OF SHIFTS MULT: ASL SCRPTR ; PERFORM 16-BIT LEFT SHIFT ROL SCRPTRH DEY BNE MULT ; NOW ADD THE X VALUE LDA SCRX CLC ADC SCRPTR STA SCRPTR LDA #$00 ADC SCRPTRH STA SCRPTRH ; NOW ADD THE SCREEN BASE ADDRESS OF $0200 ; SINCE THE LOW BYTE IS $00 WE CAN IGNORE IT LDA #$02 CLC ADC SCRPTRH STA SCRPTRH ; NOTE WE COULD HAVE DONE TWO: INC SCRPTRH ; NOW WE HAVE A POINTER TO THE IMAGE IN MEM ; COPY A ROW OF IMAGE DATA COPYROW: LDY #$00 ROWLOOP: LDA (IMGPTR),Y STA (SCRPTR),Y INY CPY IMGWIDTH BNE ROWLOOP ; NOW WE NEED TO ADVANCE TO THE NEXT ROW ; ADD IMGWIDTH TO THE IMGPTR LDA IMGWIDTH CLC ADC IMGPTR STA IMGPTR LDA #$00 ADC IMGPTRH STA IMGPTRH ; ADD 32 TO THE SCRPTR LDA #32 CLC ADC SCRPTR STA SCRPTR LDA #$00 ADC SCRPTRH STA SCRPTRH ; DECREMENT THE LINE COUNT AND SEE IF WE'RE ; DONE DEC IMGHEIGHT BNE COPYROW RTS ; ========================================== ; 5x5 pixel images ; Image of a blue "O" on black background G_O: DCB $00,$0e,$0e,$0e,$00 DCB $0e,$00,$00,$00,$0e DCB $0e,$00,$00,$00,$0e DCB $0e,$00,$00,$00,$0e DCB $00,$0e,$0e,$0e,$00 ; Image of a yellow "X" on a black background G_X: DCB $07,$00,$00,$00,$07 DCB $00,$07,$00,$07,$00 DCB $00,$00,$07,$00,$00 DCB $00,$07,$00,$07,$00 DCB $07,$00,$00,$00,$07 ; Image of a black square G_BLANK: DCB $00,$00,$00,$00,$00 DCB $00,$00,$00,$00,$00 DCB $00,$00,$00,$00,$00 DCB $00,$00,$00,$00,$00 DCB $00,$00,$00,$00,$00
3. Test the code by pressing the Assemble button, then the Run button. You can adjust the speed slider as needed. If the there are any errors assembling (compiling) the code, they will appear in the message area at the bottom of the page. Make sure the code is running correctly and that you understands how it works. Don't be afraid to experiment!
run result:
Bouncing Graphic
4. Select a starting location for the graphic where X and Y have different values.
LDA #$01
STA XPOS
LDA #$09
STA YPOS
5. Select an X increment that is -1 or +1, and a Y increment that is -1 or +1. You can choose to use either a signed byte or some other representation to hold these values.
define XINC $22
define YINC $23
LDA #$01 ; Set increments to +1
STA XINC
LDA #$FF
STA YINC
6. Successively move the graphic by adding the X and Y increments to the graphic's X and Y position.
LDA XINC
BEQ MOV_LEFT
INC XPOS
LDA #27
CMP XPOS ;Check if YPOS reaches the right boundary (27)
BEQ REVERSE_X
JMP MOV_Y
LDA YINC
BEQ MOV_UP
INC YPOS
LDA #27
CMP YPOS ;Check if YPOS reaches the lower boundary (27)
BEQ REVERSE_Y
JMP MAINLOOP
7. Make the graphic bounce when it hits the edge of the bitmapped screen, both vertically (when it hits the top/bottom) and horizontally (when it hits the left/right edge).
;; draw-image-subroutine.6502;; This is a routine that can place an arbitrary; rectangular image on to the screen at given; coordinates.;; Chris Tyler 2024-09-17; Licensed under GPLv2+;;; The subroutine is below starting at the; label "DRAW:";; Test code for our subroutine; Moves an image diagonally across the screen; Zero-page variablesdefine XPOS $20define YPOS $21define XINC $22 ; X increment (+1 or -1)define YINC $23 ; Y increment (+1 or -1)START:; Set up the width and height elements of the data structureLDA #$05STA $12 ; IMAGE WIDTHSTA $13 ; IMAGE HEIGHT; Set initial position X=Y=0LDA #$01STA XPOSLDA #$09STA YPOSLDA #$01STA XINCSTA YINC; Main loop for diagonal animationMAINLOOP:; Set pointer to the image; Use G_O or G_X as desired; The syntax #<LABEL returns the low byte of LABEL; The syntax #>LABEL returns the high byte of LABELLDA #<G_OSTA $10LDA #>G_OSTA $11; Place the image on the screenLDA #$10 ; Address in zeropage of the data structureLDX XPOS ; X positionLDY YPOS ; Y positionJSR DRAW ; Call the subroutine; Delay to show the imageLDY #$00LDX #$50DELAY:DEYBNE DELAYDEXBNE DELAY; Set pointer to the blank graphicLDA #<G_BLANKSTA $10LDA #>G_BLANKSTA $11; Draw the blank graphic to clear the old imageLDA #$10 ; LOCATION OF DATA STRUCTURELDX XPOSLDY YPOSJSR DRAWLDA XINC ; Load X direction (1 for right, -1 for left)BEQ MOV_LEFT ; If XINC = 0 (moving left), jump to MOV_LEFT; Move right, increment XPOSINC XPOS ; Increase X positionLDA #27 ; Set the right boundaryCMP XPOS ; Compare XPOS with the boundaryBEQ REVERSE_X ; If at the boundary, reverse directionJMP MOV_Y ; Otherwise, move to Y-axis logicREVERSE_X:DEC XINC ; Reverse direction to left (XINC = -1)JMP MOV_Y ; Move to Y-axis logicMOV_LEFT:; Move left, decrement XPOSDEC XPOS ; Decrease X positionLDA #0 ; Set the left boundaryCMP XPOS ; Compare XPOS with the boundaryBEQ REVERSE_X_LEFT ; If at the boundary, reverse directionJMP MOV_Y ; Otherwise, move to Y-axis logicREVERSE_X_LEFT:INC XINC ; Reverse direction to right (XINC = 1)JMP MOV_Y ; Move to Y-axis logicMOV_Y:LDA YINC ; Load Y direction (1 for down, -1 for up)BEQ MOV_UP ; If YINC = 0 (moving up), jump to MOV_UP; Move down, increment YPOSINC YPOS ; Increase Y positionLDA #27 ; Set the bottom boundaryCMP YPOS ; Compare YPOS with the boundaryBEQ REVERSE_Y ; If at the boundary, reverse directionJMP MAINLOOP ; Otherwise, return to main loopREVERSE_Y:DEC YINC ; Reverse direction to up (YINC = -1)JMP MAINLOOP ; Return to main loopMOV_UP:; Move up, decrement YPOSDEC YPOS ; Decrease Y positionLDA #0 ; Set the top boundaryCMP YPOS ; Compare YPOS with the boundaryBEQ REVERSE_Y_DOWN ; If at the boundary, reverse directionJMP MAINLOOP ; Otherwise, return to main loopREVERSE_Y_DOWN:INC YINC ; Reverse direction to down (YINC = 1)JMP MAINLOOP ; Return to main loop; Repeat infinitelyJMP MAINLOOP ; Jump back to the main loop; ==========================================;; DRAW :: Subroutine to draw an image on; the bitmapped display;; Entry conditions:; A - location in zero page of:; a pointer to the image (2 bytes); followed by the image width (1 byte); followed by the image height (1 byte); X - horizontal location to put the image; Y - vertical location to put the image;; Exit conditions:; All registers are undefined;; Zero-page memory locationsdefine IMGPTR $A0define IMGPTRH $A1define IMGWIDTH $A2define IMGHEIGHT $A3define SCRPTR $A4define SCRPTRH $A5define SCRX $A6define SCRY $A7DRAW:; SAVE THE X AND Y REG VALUESSTY SCRYSTX SCRX; GET THE DATA STRUCTURETAYLDA $0000,YSTA IMGPTRLDA $0001,YSTA IMGPTRHLDA $0002,YSTA IMGWIDTHLDA $0003,YSTA IMGHEIGHT; CALCULATE THE START OF THE IMAGE ON; SCREEN AND PLACE IN SCRPTRH;; THIS IS $0200 (START OF SCREEN) +; SCRX + SCRY * 32;; WE'LL DO THE MULTIPLICATION FIRST; START BY PLACING SCRY INTO SCRPTRLDA #$00STA SCRPTRHLDA SCRYSTA SCRPTR; NOW DO 5 LEFT SHIFTS TO MULTIPLY BY 32LDY #$05 ; NUMBER OF SHIFTSMULT:ASL SCRPTR ; PERFORM 16-BIT LEFT SHIFTROL SCRPTRHDEYBNE MULT; NOW ADD THE X VALUELDA SCRXCLCADC SCRPTRSTA SCRPTRLDA #$00ADC SCRPTRHSTA SCRPTRH; NOW ADD THE SCREEN BASE ADDRESS OF $0200; SINCE THE LOW BYTE IS $00 WE CAN IGNORE ITLDA #$02CLCADC SCRPTRHSTA SCRPTRH; NOTE WE COULD HAVE DONE TWO: INC SCRPTRH; NOW WE HAVE A POINTER TO THE IMAGE IN MEM; COPY A ROW OF IMAGE DATACOPYROW:LDY #$00ROWLOOP:LDA (IMGPTR),YSTA (SCRPTR),YINYCPY IMGWIDTHBNE ROWLOOP; NOW WE NEED TO ADVANCE TO THE NEXT ROW; ADD IMGWIDTH TO THE IMGPTRLDA IMGWIDTHCLCADC IMGPTRSTA IMGPTRLDA #$00ADC IMGPTRHSTA IMGPTRH; ADD 32 TO THE SCRPTRLDA #32CLCADC SCRPTRSTA SCRPTRLDA #$00ADC SCRPTRHSTA SCRPTRH; DECREMENT THE LINE COUNT AND SEE IF WE'RE; DONEDEC IMGHEIGHTBNE COPYROWRTS; ==========================================; 5x5 pixel images; Image of a blue "O" on black backgroundG_O:DCB $00,$0e,$0e,$0e,$00DCB $0e,$00,$00,$00,$0eDCB $0e,$00,$00,$00,$0eDCB $0e,$00,$00,$00,$0eDCB $00,$0e,$0e,$0e,$00; Image of a yellow "X" on a black backgroundG_X:DCB $07,$00,$00,$00,$07DCB $00,$07,$00,$07,$00DCB $00,$00,$07,$00,$00DCB $00,$07,$00,$07,$00DCB $07,$00,$00,$00,$07; Image of a black squareG_BLANK:DCB $00,$00,$00,$00,$00DCB $00,$00,$00,$00,$00DCB $00,$00,$00,$00,$00DCB $00,$00,$00,$00,$00DCB $00,$00,$00,$00,$00
run result:
Challenges (Optional, Recommended)
Here are some challenges for further experimentation if you're interested:
- Permit integer values other than -1 and +1 for the X and Y increments (deltas).
- Permit fractional values for the X and Y increments (e.g., +1.5 or -0.75). One way of doing this would be to use 16-bit (2-byte) X and Y increment values (deltas), where the lowest byte is fractional and the highest byte is integer (i.e., the radix point is between the two bytes).
- Perturb (subtly disrupt or randomize) the ball's position or bounce so that it is not overly predictable. (Remember that there's a pseudo-random number generator available in memory location $00FE).
- Change the graphic image or colour each time it bounces.
Comments
Post a Comment