Welcome to My 6502 Programming Journey

Explore my labs and projects.

Lab: Introduction to Mob Programming

In this lab, we want to introduce Mob Programming. It’s another form of Pair Programming. It means a group of people working on a single piece of code collaboratively. The task was to modify the 6502 Assembly Program that moves a 5x5 graphic diagonally around the screen. We assemble and then run the code on http://6502.cdot.systems. The goal for this lab is to make it bounce off the edges and also challenge it by changing the x and y to make it move differently.

1. Setup

We start by opening http://6502.cdot.systems and pasting the following code:


; draw-image-subroutine.6502
; Moves a 5x5 graphic 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

MAINLOOP:
  ; Set pointer to the image
  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 $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 (omitted for brevity)
; 5x5 pixel images (omitted for brevity)
      

2. Testing the Code

Now we need to click on the assembly button and run. We also have the option to change the speed from the top part. The Speed button controls the execution speed of the emulator. Basically, it adjusts how fast the program runs.

3. Different Values for X and Y

By modifying the following piece of code:


; Set initial position X=10, Y=5
  LDA #10
  STA XPOS
  LDA #5
  STA YPOS
      

Now with this change, the graphic will start from (10, 5) and move diagonally as the X and Y values are incremented in the MAINLOOP, same as before.

4. Setting Movement and Direction with Increment Values

To implement an X increment and Y increment that can be -1 or +1, we use single bytes to represent them. For example:

We can define two new zero-page variables to hold the X and Y increments:


; Zero-page variables
define XPOS $20      ; Current X position
define YPOS $21      ; Current Y position
define XINC $22      ; X increment (can be $01 or $FF)
define YINC $23      ; Y increment (can be $01 or $FF)
      

Then we can update the code to set these increments at the start of the program and use them to change the XPOS and YPOS in the MAINLOOP.


; Set initial increments (XINC = +1, YINC = -1)
  LDA #$01      ; +1
  STA XINC
  LDA #$FF      ; -1
  STA YINC
      

If we test the code now, we don’t see any difference. However, by updating the position using the increment right after drawing the blank graphic to clear the old image and before continuing for 29 frames of animation, using the following instructions:


LDA XPOS
  CLC
  ADC XINC
  STA XPOS

  LDA YPOS
  CLC
  ADC YINC
  STA YPOS
      

The ADD with carry instruction handles the signed arithmetic.

We can always customize the movement by changing the value of XINC and YINC to create different movement patterns. Now, the starting position and direction of the movement have been changed as follows:

Example of Movement

If XINC = $01 and YINC = $FF, the graphic will move as follows:

Frame XPOS YPOS Direction
1 10 5 Start at (10, 5)
2 11 4 Right and up
3 12 3 Right and up
4 13 2 Right and up

5. Graphic Bounce

To be honest, I couldn’t figure out how to make the graphic bounce, and I was still working on it to make it happen. However, my teammate from week 3, Kaung Khant Zaw, did it. I’ll break it down into steps so you and I can understand what’s happening:

Zero-page Variables for Position and Direction

The XPOS and YPOS store the current position, but we need to define XINC and YINC to make it easy to access in the 6502 CPU.

Question: What are XINC and YINC?

They store the direction of the movements.

  • XINC = 1 moves right
  • XINC = -1 or #FF moves left
  • YINC = 1 moves down
  • YINC = -1 moves up

define XPOS    $20      ; Current X position
define YPOS    $21      ; Current Y position
define XINC    $22      ; X increment (-1 or +1)
define YINC    $23      ; Y increment (-1 or +1)
      

Initialization in Start

Question: What are the differences between XPOS, YPOS and XINC, YINC?

Key Difference Snippet What It Controls Example Values
STA XPOS, STA YPOS Initial Position Where it starts XPOS = 0, YPOS = 0 (Top-left)
STA XINC, STA YINC Movement Direction Where it moves XINC = 1, YINC = 1 (Moves right & down)

MAINLOOP Logic

  1. Updating the X Position:
    
    ; Check X direction
    LDA XINC
    CMP #$01
    BEQ INC_XPOS
    ; If XINC = $FF (-1), decrement XPOS
    LDA XPOS
    SEC
    SBC #$01
    STA XPOS
    JMP CHECK_X_BOUNDARY
    
    INC_XPOS:
    ; If XINC = +1, increment XPOS
    INC XPOS
              
  2. Checking the X Boundaries:
    
    CHECK_X_BOUNDARY:
    LDA XPOS
    CMP #$1B  ; 27 (screen width - image width)
    BCC NO_CHANGE_X
    ; If XPOS >= 27, reverse XINC to -1
    LDA #$FF
    STA XINC
    JMP UPDATE_Y
    
    NO_CHANGE_X:
    ; Check if XPOS <= 0
    LDA XPOS
    CMP #$00
    BNE UPDATE_Y
    ; If XPOS <= 0, reverse XINC to +1
    LDA #$01
    STA XINC
              
  3. Updating the Y Position:
    
    UPDATE_Y:
    ; Check Y direction
    LDA YINC
    CMP #$01
    BEQ INC_YPOS
    ; If YINC = $FF (-1), decrement YPOS
    LDA YPOS
    SEC
    SBC #$01
    STA YPOS
    JMP CHECK_Y_BOUNDARY
    
    INC_YPOS:
    ; If YINC = +1, increment YPOS
    INC YPOS
              
  4. Checking the Y Boundaries:
    
    CHECK_Y_BOUNDARY:
    LDA YPOS
    CMP #$1B
    BCC NO_CHANGE_Y
    ; If YPOS >= 27, reverse YINC to -1
    LDA #$FF
    STA YINC
    JMP MAINLOOP
    
    NO_CHANGE_Y:
    ; Check if YPOS <= 0
    LDA YPOS
    CMP #$00
    BNE MAINLOOP
    ; If YPOS <= 0, reverse YINC to +1
    LDA #$01
    STA YINC
    
    JMP MAINLOOP
              

By repeating the logic with the JMP MAINLOOP instruction, the graphic moves diagonally and bounces off the edges.

Full Code Example

Here is the full code. You can give it a try:


;
; draw-image-subroutine.bouncing.6502
;
; This routine places an arbitrary 
; rectangular image on the screen at given
; coordinates and makes it bounce within 
; the screen boundaries.
;
; 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 and bounces it off the edges

; Zero-page variables
define XPOS    $20      ; Current X position
define YPOS    $21      ; Current Y position
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
    LDA #$0A      ; 10 in decimal
    STA XPOS
    LDA #$05      ; 5 in decimal
    STA YPOS

    ; Initialize increments: XINC = +1, YINC = +1
    LDA #$01      ; +1 represented as $01
    STA XINC
    STA YINC

MAINLOOP:

    ; Set pointer to the image
    ; Use G_O or G_X as desired
    ; The syntax #LABEL returns the high byte of LABEL

    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 $11

    ; Draw the blank graphic to clear the old image
    LDA #$10      ; LOCATION OF DATA STRUCTURE
    LDX XPOS
    LDY YPOS
    JSR DRAW

    ; Update position based on increments
    ; Check X direction
    LDA XINC
    CMP #$01
    BEQ INC_XPOS
    ; If XINC = $FF (-1), decrement XPOS
    LDA XPOS
    SEC
    SBC #$01
    STA XPOS
    JMP CHECK_X_BOUNDARY

INC_XPOS:
    ; If XINC = +1, increment XPOS
    INC XPOS

CHECK_X_BOUNDARY:
    ; Check if XPOS has hit screen boundaries
    ; Screen width is 32 pixels and image width is 5
    ; Max XPOS = 32 - 5 = 27 ($1B)
    LDA XPOS
    CMP #$1B
    BCC NO_CHANGE_X
    ; If XPOS >= 27, reverse XINC to -1
    LDA #$FF
    STA XINC
    JMP UPDATE_Y

NO_CHANGE_X:
    ; Check if XPOS <= 0
    LDA XPOS
    CMP #$00
    BNE UPDATE_Y
    ; If XPOS <= 0, reverse XINC to +1
    LDA #$01
    STA XINC

UPDATE_Y:
    ; Check Y direction
    LDA YINC
    CMP #$01
    BEQ INC_YPOS
    ; If YINC = $FF (-1), decrement YPOS
    LDA YPOS
    SEC
    SBC #$01
    STA YPOS
    JMP CHECK_Y_BOUNDARY

INC_YPOS:
    ; If YINC = +1, increment YPOS
    INC YPOS

CHECK_Y_BOUNDARY:
    ; Check if YPOS has hit screen boundaries
    LDA YPOS
    CMP #$1B
    BCC NO_CHANGE_Y
    ; If YPOS >= 27, reverse YINC to -1
    LDA #$FF
    STA YINC
    JMP MAINLOOP

NO_CHANGE_Y:
    ; Check if YPOS <= 0
    LDA YPOS
    CMP #$00
    BNE MAINLOOP
    ; If YPOS <= 0, reverse YINC to +1
    LDA #$01
    STA YINC

    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
      

6. Result

As we can see, we finally figured out step by step how to make the bouncing 5×5 graphic move diagonally. I see this as a big achievement. Although I didn’t do it all on my own, that’s what mob programming is all about—teamwork, right? This lab basically taught us how to manage memory efficiently by using zero-page variables and how to handle basic graphics using assembly programming and loops.

You've visited this page 1 times.