Lab 02 - Math Lab

 Lab 02 - Math Lab

This lab helped me understand boundary checking to control the direction of a moving graphic. Managing pixel placement and position updates is crucial to ensure the graphic behaves as expected, bouncing off the edges of the bitmapped screen in both directions.

Below is the original code that served as the foundation for this task:

; 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


Let's do a deep dive into the changes!

Variables and Initialization Updates

New variables were added to control the movement direction; XINC helps with ensuring graphic bounces horizontally when it hits left/right edges, while YINC controls the up/down movement when graphic hits the edges.

define XPOS  $20  
define YPOS  $21   
define XINC  $22  - new variable added!
define YINC  $23  - new variable added!

Used the instruction LDA #$01 to load and store in both XINC and YINC, ensuring that both start at 1. These could be used for reverse movement. 

Previously: 

  LDA #$00
  STA XPOS
  STA YPOS
Changes:

LDA #$01 - loads #$01 into accumulator from memory
STA XINC - stores 1 into memory (starts +1 due to increment)
STA YINC - stores 1 into memory

Updated INC XPOS and INC YPOS with XINC or YINC since XPOS and YPOS caused the graphic to never bounce.

Previously:

  INC XPOS
  INC YPOS
Changes:

For X Update:

LDA XPOS
CLC - clears flag; initializes flag to 0
ADC XINC - usually used after clearing flag, adds memory to accumulator with carry
STA XPOS

For Y Update:

LDA YPOS
CLC - clears flag; initializes flag to 0
ADC YINC  - usually used after clearing flag, adds memory to accumulator with carry
STA YPOS

Boundary Checks to Control Movement

There was no available logic to reverse the movement which is essential to make the graphic bounce in a reverse direction once it hits the edges. 

Left/Right Edges - Reverse Movement:

X_MOV logic: 

LDA XINC 
EOR #$FF - "Exclusive OR" Memory with Accumulator; transfers memory to the adder and stores result to accumulator
CLC - clears flag; initializes flag to 0
ADC #$01 - usually used after clearing flag, adds memory to accumulator with carry
STA XINC 

Used X_MOV:

CMP #$00 - compares 0 with XPOS 
BEQ X_MOV - if flag is 0, condition follows which is X_MOV
CMP #$1B - checks if XPOS hits the max width
BEQ X_MOV - takes conditional branch when carry flag is on

Y_MOV logic:

LDA YINC
EOR #$FF - "Exclusive OR" Memory with Accumulator; transfers memory to the adder and stores result to accumulator
CLC - clears flag; initializes flag to 0
ADC #$01 - usually used after clearing flag, adds memory to accumulator with carry
STA YINC

JMP MAINLOOP

Used Y_MOV:

 CMP #$00 - compares 0 with XPOS 
 BEQ Y_MOV - if flag is 0, condition follows which is X_MOV
 CMP #$1B - checks if XPOS hits the max width
 BCS Y_MOV  - takes conditional branch when carry flag is on
 JMP MAINLOOP

Last step I almost overlooked! 

Looking at the movement, I realized it just keeps moving diagonally as it started at 0. To see the reverse movement, I had to start XPOS and YPOS at 5 and 8, respectively instead of having it start at the top leftmost edge.

LDA #$05 
STA XPOS
LDA #$07 
Start Y at 7

Try it out yourself! 

Copy and paste this code into a 6502 emulator! 

define XPOS  $20  
define YPOS  $21   
define XINC  $22  
define YINC  $23  

START:

  LDA #$05
  STA $12      
  STA $13      

  LDA #$05
  STA XPOS
  LDA #$08
  STA YPOS

  LDA #$01
  STA XINC
  STA YINC

MAINLOOP:

  LDA #<G_O
  STA $10
  LDA #>G_O
  STA $11

  LDA #$10  
  LDX XPOS  
  LDY YPOS 
  JSR DRAW 

  LDY #$00
  LDX #$50
DELAY:
  DEY
  BNE DELAY
  DEX
  BNE DELAY

  LDA #<G_BLANK
  STA $10
  LDA #>G_BLANK
  STA $11

  LDA #$10
  LDX XPOS
  LDY YPOS
  JSR DRAW

  LDA XPOS
  CLC
  ADC XINC
  STA XPOS

  CMP #$00
  BEQ X_MOV
  CMP #$1B 
  BCS X_MOV
  JMP CHECK_Y

X_MOV:
  LDA XINC
  EOR #$FF
  CLC
  ADC #$01
  STA XINC

CHECK_Y:
  LDA YPOS
  CLC
  ADC YINC
  STA YPOS

  CMP #$00
  BEQ Y_MOV
  CMP #$1B
  BCS Y_MOV
  JMP MAINLOOP

Y_MOV:
  LDA YINC
  EOR #$FF
  CLC
  ADC #$01
  STA YINC

  JMP MAINLOOP

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:
  STY SCRY
  STX SCRX

  TAY
  LDA $0000,Y
  STA IMGPTR
  LDA $0001,Y
  STA IMGPTRH
  LDA $0002,Y
  STA IMGWIDTH
  LDA $0003,Y
  STA IMGHEIGHT
  LDA #$00
  STA SCRPTRH
  LDA SCRY
  STA SCRPTR
  LDY #$05    
MULT:
  ASL SCRPTR  
  ROL SCRPTRH
  DEY
  BNE MULT
  LDA SCRX
  CLC
  ADC SCRPTR
  STA SCRPTR
  LDA #$00
  ADC SCRPTRH
  STA SCRPTRH
  LDA #$02
  CLC
  ADC SCRPTRH
  STA SCRPTRH

COPYROW:
  LDY #$00
ROWLOOP:
  LDA (IMGPTR),Y
  STA (SCRPTR),Y
  INY
  CPY IMGWIDTH
  BNE ROWLOOP

  LDA IMGWIDTH
  CLC
  ADC IMGPTR
  STA IMGPTR
  LDA #$00
  ADC IMGPTRH
  STA IMGPTRH
 
  LDA #32
  CLC
  ADC SCRPTR
  STA SCRPTR
  LDA #$00
  ADC SCRPTRH
  STA SCRPTRH

  DEC IMGHEIGHT
  BNE COPYROW

  RTS

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

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

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

Conclusion:

This lab was very challenging to me especially when I have just started learning the concept of talking to a machine, let alone involving instructions that control a graphic's movement. For someone who enjoys high-level coding, I believe this experience encouraged me to open my mind to learning Assembly Language. I am so thrilled to gain better knowledge in low-level programming which absolutely worthwhile!

Comments

Popular posts from this blog

BATCH 3 | Project Stage 2, Part 3 - Cloned Functions Comparison and Reflection

Lab 01 - Experiments, Calculating Performance and Modifying Code

BATCH 4 | Clone-Prune Analysis Code Pass On Both Architectures