LAB 2

Introduction:

In this lab, I will work with 6502 assembly language to perform how graphics are displayed and manipulated on a bitmapped screen. We provided a program that included a subroutine, which takes a rectangular image and places it at specified X and Y coordinates on the screen. The code calculates the correct screen memory address for the image using the position and dimensions provided, then copies the image data row by row to the screen memory. I began by testing this program by assembling and running it, ensuring it functioned correctly while observing how the DRAW subroutine works. After that I modified it to create a "bouncing graphic" animation. I started by setting an initial position for the graphic and defined increments for X and Y movement. The program then updated the graphic's position by adding these increments to its X and Y coordinates. When the graphic reached the screen's edges, the code reversed the direction by flipping the movement increment, creating a bouncing effect. 

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

; 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

The image diagonally across the screen:



Bouncing Graphic:

Our first task is to select a starting location for the graphic where X and Y have different values. Instead of starting with the initial position X = Y = 0, we will begin with the X position = 1 and Y position = 9:

LDA #$01
STA XPOS
LDA #$09
STA YPOS

The next task is to set a X increment that is -1 or +1 and Y increment that is -1 or 1:

define XINC $22  ; X increment
define YINC $23  ; Y increment
; Set increments to +1 
LDA #$01
STA XINC 
STA YINC

Then we have to move the graphic by increasing its X and Y positions using the specified X and Y increments and make the graphic bounce when it hits the edge of the bitmapped screen:
LDA XINC           ; Load the value of XINC
BEQ MOV_LEFT       ; If RIGHT is false (XINC = 0), jump to MOV_LEFT

; Move right, increment XPOS
INC XPOS           
LDA #27
CMP XPOS           ; Check if XPOS reached 27
BEQ REVERSE_X      ; If XPOS is 27, reverse direction
JMP MOV_Y          ; Otherwise, move to Y-axis logic

REVERSE_X:
DEC XINC           ; Reverse direction (set XINC to 0, moving left)
JMP MOV_Y          ; Proceed to Y-axis movement

MOV_LEFT:
; Move left, decrement XPOS
DEC XPOS           
LDA #0
CMP XPOS           ; Check if XPOS reached 0
BEQ REVERSE_X_LEFT ; If XPOS is 0, reverse direction
JMP MOV_Y          ; Otherwise, move to Y-axis logic

REVERSE_X_LEFT:
INC XINC           ; Reverse direction (set XINC to 1, moving right)
JMP MOV_Y          ; Proceed to Y-axis movement

MOV_Y:
LDA YINC           ; Load YINC value (DOWN flag)
BEQ MOV_UP         ; If DOWN is false (YINC = 0), jump to MOV_UP

; Move down, increment YPOS
INC YPOS           
LDA #27
CMP YPOS           ; Check if YPOS reached 27
BEQ REVERSE_Y      ; If YPOS is 27, reverse direction
JMP MAINLOOP       ; Otherwise, return to main loop

REVERSE_Y:
DEC YINC           ; Reverse direction (set YINC to 0, moving up)
JMP MAINLOOP       ; Proceed back to main loop

MOV_UP:
; Move up, decrement YPOS
DEC YPOS           
LDA #0
CMP YPOS           ; Check if YPOS reached 0
BEQ REVERSE_Y_DOWN ; If YPOS is 0, reverse direction
JMP MAINLOOP       ; Otherwise, return to main loop

REVERSE_Y_DOWN:
INC YINC           ; Reverse direction (set YINC to 1, moving down)
JMP MAINLOOP       ; Proceed back to main loop

; Repeat infinitely
JMP MAINLOOP
Entire 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:"
;

; 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 the value of XINC
BEQ MOV_LEFT       ; If RIGHT is false (XINC = 0), jump to MOV_LEFT

; Move right, increment XPOS
INC XPOS           
LDA #27
CMP XPOS           ; Check if XPOS reached 27
BEQ REVERSE_X      ; If XPOS is 27, reverse direction
JMP MOV_Y          ; Otherwise, move to Y-axis logic

REVERSE_X:
DEC XINC           ; Reverse direction (set XINC to 0, moving left)
JMP MOV_Y          ; Proceed to Y-axis movement

MOV_LEFT:
; Move left, decrement XPOS
DEC XPOS           
LDA #0
CMP XPOS           ; Check if XPOS reached 0
BEQ REVERSE_X_LEFT ; If XPOS is 0, reverse direction
JMP MOV_Y          ; Otherwise, move to Y-axis logic

REVERSE_X_LEFT:
INC XINC           ; Reverse direction (set XINC to 1, moving right)
JMP MOV_Y          ; Proceed to Y-axis movement

MOV_Y:
LDA YINC           ; Load YINC value (DOWN flag)
BEQ MOV_UP         ; If DOWN is false (YINC = 0), jump to MOV_UP

; Move down, increment YPOS
INC YPOS           
LDA #27
CMP YPOS           ; Check if YPOS reached 27
BEQ REVERSE_Y      ; If YPOS is 27, reverse direction
JMP MAINLOOP       ; Otherwise, return to main loop

REVERSE_Y:
DEC YINC           ; Reverse direction (set YINC to 0, moving up)
JMP MAINLOOP       ; Proceed back to main loop

MOV_UP:
; Move up, decrement YPOS
DEC YPOS           
LDA #0
CMP YPOS           ; Check if YPOS reached 0
BEQ REVERSE_Y_DOWN ; If YPOS is 0, reverse direction
JMP MAINLOOP       ; Otherwise, return to main loop

REVERSE_Y_DOWN:
INC YINC           ; Reverse direction (set YINC to 1, moving down)
JMP MAINLOOP       ; Proceed back to main loop

; Repeat infinitely
JMP MAINLOOP

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

Challege:

Permit integer values other than -1 and +1 for the X and Y increments (deltas):

We need to allow XINC and YINC to be any signed integer.And the values in XINC and YINC are used to update XPOS and YPOS, which determine the image's position on the screen,and then check if the image's current position (XPOS and YPOS) has gone out of bounds.

ADC XINC
ADC YINC

Permit fractional values for the X and Y increments:
lowest byte is fractional and the highest byte is integer:
define XINC_LOW  $22   ; Fractional part of X increment (0-255)
define XINC_HIGH $23   ; Integer part of X increment (-128 to 127)

define YINC_LOW  $24   ; Fractional part of Y increment (0-255)
define YINC_HIGH $25   ; Integer part of Y increment (-128 to 127)
For each frame, we first add the integer part of the increment to the position. Then, add the fractional part of the increment to the position. 

Change the color each time it bounces:
I didn't successfully implement it yet, but the concept is to make the image bounce around the screen and change color each time it hits a wall. The idea is to check when the image reaches the edges (left, right, top, or bottom) and reverse its direction. Every time the image bounces, we change its color by cycling through a set of colors. The image will keep bouncing while its color changes each time it hits an edge.

Conclusion:

In this lab, I worked with 6502 assembly language to learn how to display and manipulate graphics on a bitmapped screen. I tested a provided program that displayed an image and then modified it to create an animation. By adjusting the graphic's position and using increments for X and Y, I made it move diagonally. When the graphic reached the edges of the screen, I reversed its direction by changing the increments, creating a bouncing effect.Through this lab, I also learned some important assembly commands like CLD (clear decimal mode), ADC (add with carry), and CMP (compare), which helped me understand how to manipulate data and control program flow.

Comments

Popular posts from this blog

PROJECT STAGE2

PROJECT STAGE1

LAB 4