SPO600 Lab2 (Pt.2)-- challenges 1 and 2

In Part 1, I have modified the code of 6502 processor so that an logo can bounce within the screen. 

Challenges


Now, we have a few challenges. We would like to modified the code so that it can meet the following requirement:

1. Permit integer value other than -1 and +1 for the X and Y increments (logo velocity).
2. Permit fractional value for the X and Y increments (eg. +1.5 or -0.75)
3. Perturb the ball's position or velocity (To be done next blog)
4. Change the graphic image or colour each time it bounces (To be done next blog)

Challenge 1. 

Problems analysis and solutions

Assumption: the velocity cannot be larger than the screen size, i.e. the ball will not bounce more than once within one frame. 

Problem of Challenge 1:
Since the current algorithm is just checking whether the logo is on the edge to decide whether we should reverse the velocity to perform a bounce. However, it only works when the velocity is 1. If the velocity is 2 and the displacement of the ball from the edge is 1, bounce will not be performed and could cause error.

solution for challenge 1:
Instead of only checking the current position of the logo, we can calculate the expected location after movement to see if it excess the boundary. If so, recalculate the location and reverse the velocity.

code:
; 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:"

; Zero-page variables
define XPOS $20
define YPOS $21
define X_V $22
define Y_V $23
define XNEXT $24
define YNEXT $25

START:
; Set up the width and height elements of the data structure
  LDA #$05
  STA $12       ; IMAGE WIDTH
  STA $13       ; IMAGE HEIGHT

; randomize initial position within 0 to 3
  LDA $fe
  AND #$03
  STA XPOS
  LDA $fe
  AND #$03
  STA YPOS
; randomize initial velocity within 1 to 3
  LDA $fe
  AND #$03
  ORA #$01
  STA X_V
  LDA $fe
  AND #$03
  ORA #$01
  STA Y_V

; 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_X
  STA $10
  LDA #>G_X
  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
  JMP MOVE
TEMP: JMP MAINLOOP  
  ; Increment the predicted position
MOVE:  LDA X_V
  CLC
  ADC XPOS
  STA XNEXT
  LDA Y_V
  CLC
  ADC YPOS
  STA YNEXT

X_LEFTCHECK:  ;check if X reach left edge
  LDA XNEXT
  BMI X_BOUNCE_L 
 
;check if X excess right edge
  LDA XNEXT
  CMP #$1b
  BCS X_BOUNCE_R
  JMP Y_TOPCHECK

X_BOUNCE_R: ;bounce on right edge
  LDA #$00
  SEC
  SBC X_V
  STA X_V
  LDA XNEXT
  SEC
  SBC #$1b
  STA XNEXT
  LDA #$1b
  SEC
  SBC XNEXT
  STA XNEXT
  JMP Y_TOPCHECK

X_BOUNCE_L:  ;bounce on left edge
  LDA #$00
  SEC
  SBC X_V
  STA X_V
  LDA #$00
  SEC
  SBC XNEXT
  STA XNEXT

;check if Y reach TOP edge
Y_TOPCHECK:  LDA YNEXT
  BMI Y_BOUNCE_T

  ;check if Y reach Bottom edge
Y_BOTCHECK:  LDA YNEXT
  CMP #$1b
  BCS Y_BOUNCE_B
  JMP END_CHECK

Y_BOUNCE_T:
  LDA #$00
  SEC
  SBC Y_V
  STA Y_V
  LDA #$00
  SEC
  SBC YNEXT
  STA YNEXT
  JMP END_CHECK
Y_BOUNCE_B:
  LDA #$00
  SEC
  SBC Y_V
  STA Y_V
  LDA YNEXT
  SEC
  SBC #$1b
  STA YNEXT
  LDA #$1b
  SEC
  SBC YNEXT
  STA YNEXT
  
END_CHECK:

  LDA XNEXT
  STA XPOS
  LDA YNEXT
  STA YPOS

  JMP TEMP ; Repeat infinitely

; ==========================================
;
; 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
red highlighted part: randomized initial velocity and position
yellow highlighted part: calculated the predicted position
green highlighted code: check if the predicted position out of boundary. if so, recalculate the position and reverse the velocity
blue highlighted part: save the predicted position to position



output:



Challenge 2.

 Permit fractional value for the X and Y increments (eg. +1.5 or -0.75)

Problems analysis and solutions

This is quite simple given that in challenge 1, we fix the program so that it have functionality to check if the predict position is out of boundary. To solve this problem, we use 2 bytes of memory to store velocity and position in each direction. 2 byte value's addition and subtraction may take some time, but generally it should be easy.

code:
; 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:"

; Zero-page variables
define XPOSL $20
define XPOSH $21
define YPOSL $22
define YPOSH $23

define XNEXTL $24
define XNEXTH $25
define YNEXTL $26
define YNEXTH $27

define X_VL $28
define X_VH $29
define Y_VL $30
define Y_VH $31


START:
; Set up the width and height elements of the data structure
  LDA #$05
  STA $12       ; IMAGE WIDTH
  STA $13       ; IMAGE HEIGHT

; initial position
  LDA #$00
  STA XPOSL
  LDA #$01
  STA XPOSH
  LDA #$00
  STA YPOSL
  LDA #$03
  STA YPOSH
; initial velocity x = 1.5, y =-0.75 
  LDA #$01
  STA X_VH
  LDA #$80
  STA X_VL
  LDA #$40
  STA Y_VL
  LDA #$ff
  STA Y_VH

; 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_X
  STA $10
  LDA #>G_X
  STA $11

  ; Place the image on the screen
  LDA #$10  ; Address in zeropage of the data structure
  LDX XPOSH  ; X position
  LDY YPOSH  ; 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 XPOSH
  LDY YPOSH
  JSR DRAW
  JMP MOVE
TEMP: JMP MAINLOOP  
  ; Increment the predicted position
MOVE:  LDA X_VL
  CLC
  ADC XPOSL
  STA XNEXTL
  LDA X_VH
  ADC XPOSH
  STA XNEXTH
  LDA Y_VL
  CLC
  ADC YPOSL
  STA YNEXTL
  LDA Y_VH
  ADC YPOSH
  STA YNEXTH

X_LEFTCHECK:  ;check if X reach left edge
  LDA XNEXTH
  BMI X_BOUNCE_L 
 
;check if X excess right edge
  LDA XNEXTH
  CMP #$1b
  BCS X_BOUNCE_R
  JMP Y_TOPCHECK

X_BOUNCE_R: ;bounce on right edge
  LDA #$00
  SEC
  SBC X_VL
  STA X_VL
  LDA #$00
  SBC X_VH
  STA X_VH

  LDA XNEXTH
  SEC
  SBC #$1b
  STA XNEXTH
  LDA $00
  SEC
  SBC XNEXTL
  STA XNEXTL
  LDA #$1b
  SBC XNEXTH
  STA XNEXTH
  JMP Y_TOPCHECK

X_BOUNCE_L:  ;bounce on left edge

  LDA #$00
  SEC
  SBC X_VL
  STA X_VL
  LDA #$00
  SBC X_VH
  STA X_VH

  LDA #$00
  SEC
  SBC XNEXTL
  STA XNEXTL
  LDA #$00
  SBC XNEXTH
  STA XNEXTH

;check if Y reach TOP edge
Y_TOPCHECK:  LDA YNEXTH
  BMI Y_BOUNCE_T

  ;check if Y reach Bottom edge
Y_BOTCHECK:  LDA YNEXTH
  CMP #$1b
  BCS Y_BOUNCE_B
  JMP END_CHECK

Y_BOUNCE_T:
  LDA #$00 ;reverse velocity
  SEC
  SBC Y_VL
  STA Y_VL
  LDA #$00
  SBC Y_VH
  STA Y_VH
;reverse position
  LDA #$00
  SEC
  SBC YNEXTL
  STA YNEXTL
  LDA #$00
  SBC YNEXTH
  STA YNEXTH
  
  JMP END_CHECK
Y_BOUNCE_B:
  LDA #$00
  SEC
  SBC Y_VL
  STA Y_VL
  LDA #$00
  SBC Y_VH
  STA Y_VH

 LDA YNEXTH
  SEC
  SBC #$1b
  STA YNEXTH
  LDA $00
  SEC
  SBC YNEXTL
  STA YNEXTL
  LDA #$1b
  SBC YNEXTH
  STA YNEXTH
  
END_CHECK:

  LDA XNEXTH
  STA XPOSH
  LDA XNEXTL
  STA XPOSL
  LDA YNEXTH
  STA YPOSH
  LDA YNEXTL
  STA YPOSL

  JMP TEMP ; Repeat infinitely

; ==========================================
;
; 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

yellow highlighted part: initial value with high-bytes and low-bytes
blue highlighted part: 2-bytes addition and subtraction are performed in the move and bounce functions
red highlighted part: store both the high and low bytes of the predicted and adjusted positions.

Ouptut:

Challenges 3 and 4 will be completed in next blog



Comments

Popular posts from this blog

SPO600 Project Stage 1 (Pt.1) - Create a GCC Pass

SPO600 Project Stage 2 (Pt.1) - GCC pass locating clone function

SPO600 Project Stage 2 (Pt.2) - GCC pass locating clone function -modified version