Lab 2 - 6502 Math Lab
The Mob Programming session emphasizes collaborative learning. Participants work in teams, designating a Driver (who types) and a Presenter (who explains the group's solution). The goal is to collectively understand and implement the 6502 Math Lab tasks.
Steps for Collaboration:
Join a Zoom breakout group and assign roles.
Decide on a code-sharing method (Git, Pastebin, email, or Zoom chat).
Use the 6502 Emulator (http://6502.cdot.systems) to run and test code.
Follow structured coding suggestions where non-Drivers provide coding instructions.
Switch roles periodically to ensure collective learning.
The Setup
The code below moves a 5×5 graphic diagonally across the screen like the series of images above.
The speed can be adjusted with the speed slider.
; ; 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
Bouncing Graphic
- Select a starting location for the graphic where X and Y have different values.
; Set initial position for the graphic (different X and Y values) LDA #$01 ; Set X position to 1 STA XPOS ; Store in XPOS (X position) LDA #$09 ; Set Y position to 9 STA YPOS ; Store in YPOS (Y position)
- 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.
; Set X and Y increments (+1 or -1) LDA #$01 ; Set X increment to +1 (move right) STA XINC ; Store in XINC LDA #$01 ; Set Y increment to +1 (move down) STA YINC ; Store in YINC
- Successively move the graphic by adding the X and Y increments to the graphic's X and Y position.
; Main loop for moving the graphic MAINLOOP: ; Move the graphic by adding the X and Y increments LDA XPOS CLC ADC XINC STA XPOS ; Update X position LDA YPOS CLC ADC YINC STA YPOS ; Update Y position
- 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).
; Check for horizontal bounce (left-right)
LDA XPOS
CMP #$00 ; Check if X position reached 0 (left edge)
BEQ REVERSE_X_LEFT ; If true, reverse direction (move right)
CMP #$1B ; Check if X position reached 27 (right edge)
BEQ REVERSE_X ; If true, reverse direction (move left)
; Check for vertical bounce (top-bottom)
LDA YPOS
CMP #$00 ; Check if Y position reached 0 (top edge)
BEQ REVERSE_Y_DOWN ; If true, reverse direction (move down)
CMP #$1B ; Check if Y position reached 27 (bottom edge)
BEQ REVERSE_Y ; If true, reverse direction (move up)
JMP MAINLOOP ; Continue moving the graphic
; Reverse direction horizontally (left)
REVERSE_X:
DEC XINC ; Reverse X increment (move left)
JMP MAINLOOP
; Reverse direction horizontally (right)
REVERSE_X_LEFT:
INC XINC ; Reverse X increment (move right)
JMP MAINLOOP
; Reverse direction vertically (up)
REVERSE_Y:
DEC YINC ; Reverse Y increment (move up)
JMP MAINLOOP
; Reverse direction vertically (down)
REVERSE_Y_DOWN:
INC YINC ; Reverse Y increment (move down)
JMP MAINLOOP
;; 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 POS_X $20 define POS_Y $21 define INC_X $22 ; X increment (+1 or -1) define INC_Y $23 ; Y increment (+1 or -1) INIT: ; 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 POS_X LDA #$09 STA POS_Y LDA #$01 STA INC_X STA INC_Y ; Main loop for diagonal animation LOOP: ; 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 POS_X ; X position LDY POS_Y ; 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 POS_X LDY POS_Y JSR DRAW LDA INC_X ; Load the value of INC_X BEQ MOVE_LEFT ; If RIGHT is false (INC_X = 0), jump to MOVE_LEFT ; Move right, increment POS_X INC POS_X LDA #27 CMP POS_X ; Check if POS_X reached 27 BEQ REVERSE_X ; If POS_X is 27, reverse direction JMP MOVE_Y ; Otherwise, move to Y-axis logic REVERSE_X: DEC INC_X ; Reverse direction (set INC_X to 0, moving left) JMP MOVE_Y ; Proceed to Y-axis movement MOVE_LEFT: ; Move left, decrement POS_X DEC POS_X LDA #0 CMP POS_X ; Check if POS_X reached 0 BEQ REVERSE_X_LEFT ; If POS_X is 0, reverse direction JMP MOVE_Y ; Otherwise, move to Y-axis logic REVERSE_X_LEFT: INC INC_X ; Reverse direction (set INC_X to 1, moving right) JMP MOVE_Y ; Proceed to Y-axis movement MOVE_Y: LDA INC_Y ; Load INC_Y value (DOWN flag) BEQ MOVE_UP ; If DOWN is false (INC_Y = 0), jump to MOVE_UP ; Move down, increment POS_Y INC POS_Y LDA #27 CMP POS_Y ; Check if POS_Y reached 27 BEQ REVERSE_Y ; If POS_Y is 27, reverse direction JMP LOOP ; Otherwise, return to main loop REVERSE_Y: DEC INC_Y ; Reverse direction (set INC_Y to 0, moving up) JMP LOOP ; Proceed back to main loop MOVE_UP: ; Move up, decrement POS_Y DEC POS_Y LDA #0 CMP POS_Y ; Check if POS_Y reached 0 BEQ REVERSE_Y_DOWN ; If POS_Y is 0, reverse direction JMP LOOP ; Otherwise, return to main loop REVERSE_Y_DOWN: INC INC_Y ; Reverse direction (set INC_Y to 1, moving down) JMP LOOP ; Proceed back to main loop ; Repeat infinitely JMP 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 IMG_PTR $A0 define IMG_PTR_H $A1 define IMG_WIDTH $A2 define IMG_HEIGHT $A3 define SCR_PTR $A4 define SCR_PTR_H $A5 define SCR_X $A6 define SCR_Y $A7 DRAW: ; SAVE THE X AND Y REG VALUES STY SCR_Y STX SCR_X ; GET THE DATA STRUCTURE TAY LDA $0000,Y STA IMG_PTR LDA $0001,Y STA IMG_PTR_H LDA $0002,Y STA IMG_WIDTH LDA $0003,Y STA IMG_HEIGHT ; CALCULATE THE START OF THE IMAGE ON ; SCREEN AND PLACE IN SCR_PTR_H ; ; THIS IS $0200 (START OF SCREEN) + ; SCR_X + SCR_Y * 32 ; ; WE'LL DO THE MULTIPLICATION FIRST ; START BY PLACING SCR_Y INTO SCR_PTR LDA #$00 STA SCR_PTR_H LDA SCR_Y STA SCR_PTR ; NOW DO 5 LEFT SHIFTS TO MULTIPLY BY 32 LDY #$05 ; NUMBER OF SHIFTS MULT: ASL SCR_PTR ; PERFORM 16-BIT LEFT SHIFT ROL SCR_PTR_H DEY BNE MULT ; NOW ADD THE X VALUE LDA SCR_X CLC ADC SCR_PTR STA SCR_PTR LDA #$00 ADC SCR_PTR_H STA SCR_PTR_H ; NOW ADD THE SCREEN BASE ADDRESS OF $0200 ; SINCE THE LOW BYTE IS $00 WE CAN IGNORE IT LDA #$02 CLC ADC SCR_PTR_H STA SCR_PTR_H ; NOTE WE COULD HAVE DONE TWO: INC SCR_PTR_H ; NOW WE HAVE A POINTER TO THE IMAGE IN MEM ; COPY A ROW OF IMAGE DATA COPY_ROW: LDY #$00 ROW_LOOP: LDA (IMG_PTR),Y STA (SCR_PTR),Y INY CPY IMG_WIDTH BNE ROW_LOOP ; NOW WE NEED TO ADVANCE TO THE NEXT ROW ; ADD IMG_WIDTH TO THE IMG_PTR LDA IMG_WIDTH CLC ADC IMG_PTR STA IMG_PTR LDA #$00 ADC IMG_PTR_H STA IMG_PTR_H ; ADD 32 TO THE SCR_PTR LDA #32 CLC ADC SCR_PTR STA SCR_PTR LDA #$00 ADC SCR_PTR_H STA SCR_PTR_H ; DECREMENT THE LINE COUNT AND SEE IF WE'RE ; DONE DEC IMG_HEIGHT BNE COPY_ROW 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
Challenges
- Permit integer values other than -1 and +1 for the X and Y increments (deltas)
We need to allow X_STEP
and Y_STEP
to be any signed integer.
The values in X_STEP
and Y_STEP
are used to update X_COORD
and Y_COORD
, which determine the image's position on the screen.
We then check if the image's current position (X_COORD
and Y_COORD
)
has gone out of bounds.
- 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).
define XSTEP_FRAC $30 ; Fractional part of X increment (0-255)
define XSTEP_INT $31 ; Integer part of X increment (-128 to 127)
define YSTEP_INT $33 ; Integer part of Y increment (-128 to 127)define YSTEP_FRAC $32 ; Fractional part of Y increment (0-255)
- 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.
Working with Assembly Language was both challenging and eye-opening. Unlike high-level languages, it required precise control over hardware operations. I learned how to manipulate data at the byte level, handle signed and unsigned integers, and use conditional jumps for program flow. Debugging was difficult, but it helped me develop a deeper understanding of low-level computing. This lab reinforced my appreciation for modern programming abstractions while highlighting the power of working close to the hardware.
Comments
Post a Comment