(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 variables
define XPOS $20
define YPOS $21
define 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 structure
  LDA #$05
  STA $12       ; IMAGE WIDTH
  STA $13       ; IMAGE HEIGHT

; Set initial position X=Y=0
  LDA #$01
  STA XPOS
  LDA #$09
  STA YPOS
  LDA #$01
  STA XINC 
  STA YINC

; 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


LDA 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 XPOS
INC XPOS           ; Increase X position
LDA #27            ; Set the right boundary
CMP XPOS           ; Compare XPOS with the boundary
BEQ REVERSE_X      ; If at the boundary, reverse direction
JMP MOV_Y          ; Otherwise, move to Y-axis logic

REVERSE_X:
DEC XINC           ; Reverse direction to left (XINC = -1)
JMP MOV_Y          ; Move to Y-axis logic

MOV_LEFT:
; Move left, decrement XPOS
DEC XPOS           ; Decrease X position
LDA #0             ; Set the left boundary
CMP XPOS           ; Compare XPOS with the boundary
BEQ REVERSE_X_LEFT ; If at the boundary, reverse direction
JMP MOV_Y          ; Otherwise, move to Y-axis logic

REVERSE_X_LEFT:
INC XINC           ; Reverse direction to right (XINC = 1)
JMP MOV_Y          ; Move to Y-axis logic

MOV_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 YPOS
INC YPOS           ; Increase Y position
LDA #27            ; Set the bottom boundary
CMP YPOS           ; Compare YPOS with the boundary
BEQ REVERSE_Y      ; If at the boundary, reverse direction
JMP MAINLOOP       ; Otherwise, return to main loop

REVERSE_Y:
DEC YINC           ; Reverse direction to up (YINC = -1)
JMP MAINLOOP       ; Return to main loop

MOV_UP:
; Move up, decrement YPOS
DEC YPOS           ; Decrease Y position
LDA #0             ; Set the top boundary
CMP YPOS           ; Compare YPOS with the boundary
BEQ REVERSE_Y_DOWN ; If at the boundary, reverse direction
JMP MAINLOOP       ; Otherwise, return to main loop

REVERSE_Y_DOWN:
INC YINC           ; Reverse direction to down (YINC = 1)
JMP MAINLOOP       ; Return to main loop

; Repeat infinitely
JMP 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 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

run result:




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.

My experience:

By implementing this lab , i feel like have a better understanding about the execution process. 
The movement direction is determined by XINC and YINC, which decide whether the graphic moves left/right or up/down. The values #01 and #FF (representing +1 and -1) efficiently control the movement direction. The program detects when the graphic reaches the screen boundaries (XPOS and YPOS hitting 0 or 27) and reverses its movement by modifying XINC and YINC, ensuring a smooth bouncing effect.


Comments

Popular posts from this blog

(lab1)6502 Assembly Language Lab

(Lab4) GCC Build Lab