Learning 6502 assembly

From Wikiversity
Jump to navigation Jump to search

This page by Dan Polansky guides the reader through first steps of learning 6502 assembly. The approach is this: first point the reader to good online sources describing the assembly language, then provide exercises.

We will use Easy 6502 website, which contains a nice introduction together with an online 6502 emulator written in JavaScript. In that environment, one can manipulate a screen of 32 x 32 pixels and 16 colors, starting at $200, one byte per pixel. Furthermore, pseudorandom 8-bit numbers can be obtained from address $fe. One can also get the ASCII code of the last key pressed from $ff. This environment is good to start practicing, although it does not offer ASCII output, which is inconvenient for, say, numerical exercises.

If you never programmed in assembly, the Easy 6502 web site is a good start as a read. The processor is also described in Wikibooks. You may either read Easy 6502 right now, or you can start with exercises and only consult the guide on an as needed basis.

The following exercises are meant for Easy 6502 online emulator. The sequence of exercises is significant, where the latter ones incrementally build on the earlier ones. To learn most from this page, you have to do the exercises yourself rather than merely reading the solutions.

Motivation: Get a feel for assembly programming with a simple processor that was much in use in many home computers (Apple II, Commodore 64, Atari 8-bit, etc.) and appreciate the convenience of high-level languages, C included, and cherish the ingenuity and talent of all those 6502 programmers who created the 8-bit games on very limited machines.

License: All code snippets here are released into the public domain.

Cheat sheet

[edit | edit source]

Here is a quick cheat sheet. Exercises follow in the next sections.

Instructions:

  • LDA, STA, LDX, STX, LDY, STY
  • TAX, TAY, TXA, TYA, TXS, TSX
  • CMP, CPX, CPY
  • BEQ, BNE, BCC, BCS, BPL, BMI, BVC, BVS
  • JMP, JSR, RTS
  • PLA, PHA
  • ADC, SBC, INC, INX, INY, DEC, DEX, DEY
  • CLC, SEC, CLD, SED
  • ASL, LSR, AND, ORA, EOR, BIT, ROL, ROR
  • Etc.

Addressing modes:

  • LDA #$80  ; Acc := $80; called immediate
  • LDA $80  ; Acc := Peek($80)
  • LDA $8000  ; Acc := Peek($8000)
  • LDA ($80), Y  ; Acc := Peek(Deek($80)+Y) ; only for zero page
  • LDA ($80, X)  ; Acc := Peek(Deek($80+X)) ; only for zero page

Numerical literals (exemplified in use with LDA immediate):

  • LDA #170 ;decimal
  • LDA #$AA ;hexadecimal
  • LDA #%10101010 ;binary

Flags of the status register:

  • N: Negative
  • V: Overflow
  • -: ignored
  • B: Break
  • D: Decimal: use BCD
  • I: Interrupt
  • Z: Zero
  • C: Carry

Links:

Limitations of the CPU

[edit | edit source]

To get a better idea about the character of 6502 programming, let us look at its limitations compared e.g. to Motorola 68000 ("68k"):

  • There are only 3 main registers: A, X and Y. By contrast, 68k has 8 data registers and 8 address registers. As a consequence, 6502 programming needs to use memory location for loop indices and other temporary needs much more often.
  • All three main registers are 8-bit. Thus, even such a simple thing as making an x coordinage loop from 0 to 319 (typical for some 8-bit computers using the CPU) cannot be handled by incrementing a single register with no additional work. By contrast, 68k has 32-bit data and address registers. Even 16-bit registers would make a huge difference.
  • Some operations are only possible with the accumulator, not with the index registers X and Y. If one wants to use these operations on, say, X, one can use TXA, then operate, and then TAX, thereby losing the value that was in A.
  • 6502 has no integer multiplication and division instructions, only addition and subtraction. 68k has multiplication.

Small block of pixels without a loop

[edit | edit source]

Assignment: fill a small block of pixels, say, 3 pixels, with colors 0 through 2.

Solution:

LDA #0     ;Load accumulator
STA $200   ;Store accumulator
LDA #1
STA $201
LDA #2
STA $202

What we have learned: we can use LDA and STA to manipulate memory. There is the immediate addressing mode indicated using the hash character (#) and the other addressing mode. The string character ($) indicates the value is hexadecimal; it is decimal without it.

Block of pixels using a loop

[edit | edit source]

Assignment: fill a 256-long block of pixels (spanning mutliple lines on the 32x32 screen) with color 1.

Solution:

LDA #1
LDY #0
LOOP:
STA $200, Y ;Indexed addressing with Y; store to $200+Y
INY
BNE LOOP ;Branch on not equal (here implicitly not equal to zero)

What we have learned:

  • The increment instruction affects the zeor flag.

Fill screen with vertical stripes

[edit | edit source]

Assignment: fill the 32x32 screen with vertical color stripes. Which colors are to be used is left unspecified.

Hint: we may build on the ideas from previous exercises. We can no longer do with using Y as index register alone since we need to cover 32x32 = 4 * 256 bytes.

Solution:

LDA #00
STA $80
LDA #02
STA $81
LDA #1
LDY #0
LDX #4    ;Four pages to process
LOOP:
TYA
STA ($80), Y
INY
BNE LOOP
INC $81
DEX
BNE LOOP

What we have learned:

  • We can use the indirect addressing indexed with Y for the purpose. The base address needs to be stored at zero page ($0-$FF). We picked $80 arbitrarily. We note that 6502 uses little endian addressing: the lower byte is stored at $80 and the upper byte in $81 rather than the other way around.
  • There is TYA (A := Y) and similar for X and the other way around.

Fill screen with horizontal stripes

[edit | edit source]

Assignment: Fill screen with horizontal stripes.

Solution:

LDA #00
STA $80
LDA #02
STA $81
LDA #0
LDY #0
LDX #4
LOOP:
STA ($80), Y
STA $82 ;Temporary color store
INY
TYA
AND #$1F
BNE KEEPCOLOR
INC $82
KEEPCOLOR:
LDA $82
CPY #0
BNE LOOP
INC $81
DEX
BNE LOOP

What we have learned: We need to use additional memory to store values given how few registers we have.

Fill screen with random color pixels

[edit | edit source]

Assignment: fill the 32x32 screen with random color pixels. This is a minor variation on filling the screen with a fixed color.

Hint: obtain a random byte from address $fe.

LDA #00
STA $80
LDA #02
STA $81
LDY #0
LDX #4
LOOP:
LDA $FE
STA ($80), Y
INY
BNE LOOP
INC $81
DEX
BNE LOOP

Plot a secondary diagonal

[edit | edit source]

Assignment: plot a secondary diagonal using color 1. That is, start at upper righthand corner and end at lower lefthand corner. (Plotting the main diagonal is a bit easier and is left out.)

Solution:

LDA #00
STA $80
LDA #02
STA $81
LDA #1
LDY #31
LDX #32   ;Pixel count
LOOP:
LDA #1
STA ($80), Y
TYA
CLC
ADC #31
TAY
BCC SKIP ;Branch on carry clear
INC $81
SKIP:
DEX
BNE LOOP

What we have learned: we cannot do addition on Y, only on A. The carry bit set by ADC is not erased by TAY.

Random pixels in a rectangle

[edit | edit source]

Assignment: bombard a rectangle (e.g. 20 x 25 pixels) starting at the top left corner with random pixels. Thus, pseudorandomly generate pixel x and y coordinates in the appropriate ranges and plot pixel with color 1 at that location, doing so repeatedly. Have the rectangle width and height parametrized.

Solution:

LDA #20  ;Rectangle width
STA $82
LDA #25  ;Rectangle height
STA $83
LDX #0   ;Pixel count $100
PIXELLOOP:
LDA #0   ;$80-81 := screen address
STA $80
LDA #2
STA $81
RNDX:
LDA $FE  ;x := random in [0, xmax-1]
AND #$1F
CMP $82
BCS RNDX
STA $84
RNDY:
LDA $FE  ;y := random in [0, ymax-1]
AND #$1F
CMP $83
BCS RNDY
ASL ;Multiply by 32 with carry into $81
ASL
ASL
ASL
BCC SKIP1
INC $81
INC $81
CLC
SKIP1:
ASL
BCC SKIP2
INC $81
SKIP2:
CLC
ADC $84
BCC PLOT
INC $81
PLOT:
TAY
LDA #1
STA ($80), Y
DEX
BNE PIXELLOOP

What we have learned:

  • How to compare 8-bit numbers for less than or less than or equal using unsigned 8-bit integer semantics.
  • How to multiply by a power of 2 (here, 32, the screen width) via ASL and using carry bit to reflect the operation in the upper byte.

Links:

Keyboard controlled pixels

[edit | edit source]

Assignment: read key events and when a key is pressed, place random color to pixel that is offset from the screen beginning by the ASCII code of the key pressed. Read the ASCII code of the last key (or key combination, e.g. "a" with shift) from $ff. When no key is being pressed, keep setting random color to pixel with offset zero.

Solution:

LOOP:
LDY $ff ;Last key pressed
LDA $fe ;Random byte
STA $200, Y
CPY #0
BEQ LOOP
LDY #0
STY $FF ;Erase last key pressed or else it will stick
BEQ LOOP

What we have learned: How to read key events and erase last key pressed. How the keys map to codes, visually; thus, e.g. "a" is $61, and other lowercase letters alphabetically follow.

Keyboard-controlled moving dot

[edit | edit source]

Assignment: implement a keyboard-controlled moving dot representing a person, represented as a single pixel of color 1 (white). Use color 5 (green) to first fill the screen as a background. Mark the screen boundary using color 8 (brown). As the player moves around, leave empty space (color 0, black) behind. Do not allow the player to enter the screen boundary filled with brown. Use keys A, S, D, and W to control the player.

Note: This was inspired by the snake game at Easy 6502. Like in the snake, one has to figure out how to receive keyboard events. However, it is simpler than the snake, not requiring keeping track of the locations of the snake body.

Solution:

; ---- Memory variables
; $80-81: screen address
; $82-83: player position as pixel address on the screen
; $84-85: temporary copy of the above to be modified and conditionally copied to the actual position
; ---- Fill screen with color 5, green
LDA #00
STA $80
LDA #02
STA $81
LDA #5
LDY #0
LDX #4    ;Four pages to process
LOOP1:
STA ($80), Y
INY
BNE LOOP1
INC $81
DEX
BNE LOOP1
; ---- Plot boundary with color 8, brown
LDA #02
STA $81
LDA #8
LDY #0
LDX #32
LOOP2:
STA ($80), Y
INY
DEX
BNE LOOP2
;
LDA #$E0 ; Let $80 start at $200 + 31 * 32 = 5E0
STA $80
LDA #$05
STA $81
LDA #8
LDY #0
LDX #32
LOOP3:
STA ($80), Y
INY
DEX
BNE LOOP3
;
LDA #00
STA $80
LDA #02
STA $81
LDY #0
LDX #32 ;Pixel count
LOOP4:
LDA #8
STA ($80), Y
TYA
CLC
ADC #32
TAY
BCC SKIP
INC $81
SKIP:
DEX
BNE LOOP4
;
LDA #00
STA $80
LDA #02
STA $81
LDY #31
LDX #32 ;Pixel count
LOOP5:
LDA #8
STA ($80), Y
TYA
CLC
ADC #32
TAY
BCC SKIP2
INC $81
SKIP2:
DEX
BNE LOOP5
; ---- Player control and key event loop
LDA #$EF
STA $82   ;Player position as pixel address; $200 + 15 * 32 + 15
STA $84
LDA #$03
STA $83
STA $85
;
LOOP6:
LDA $84
STA $82
LDA $85
STA $83
;
LOOP7:
LDA #1
LDY #0
STA ($82), Y
;
LDY $FF   ;Last key pressed
CPY #$61  ;"a", left
BNE TESTD
LDA #0
STA $FF
LDA $82
STA $84
LDA $83
STA $85
DEC $84
LDA $84
CMP #$FF
BNE SKIP3
DEC $85
SKIP3:
LDY #0
LDA ($84), Y
CMP #8 ;brown
BEQ LOOP7
LDA #0
STA ($82), Y
CLC
BCC LOOP6
;
TESTD:
LDY $FF   ;Last key pressed
CPY #$64  ;"d", right
BNE TESTW
LDA #0
STA $FF
LDA $82
STA $84
LDA $83
STA $85
INC $84
LDA $84
CMP #0
BNE SKIP4
INC $85
SKIP4:
LDY #0
LDA ($84), Y
CMP #8 ;brown
BEQ LOOP7
LDA #0
STA ($82), Y
CLC
BCC LOOP6
;
TESTW:
LDY $FF   ;Last key pressed
CPY #$77  ;"w", up
BNE TESTS
LDA #0
STA $FF
LDA $82
STA $84
LDA $83
STA $85
LDA $84
SEC
SBC #32
STA $84
BCS SKIP5
DEC $85
SKIP5:
LDY #0
LDA ($84), Y
CMP #8 ;brown
BEQ LOOP7C
LDA #0
STA ($82), Y
JMP LOOP6
LOOP7C:
JMP LOOP7
;
TESTS:
LDY $FF   ;Last key pressed
CPY #$73  ;"s", down
BNE LOOP7C
LDA #0
STA $FF
LDA $82
STA $84
LDA $83
STA $85
LDA $84
CLC
ADC #32
STA $84
BCC SKIP7
INC $85
SKIP7:
LDY #0
LDA ($84), Y
CMP #8 ;brown
BEQ LOOP7C
LDA #0
STA ($82), Y
JMP LOOP6

What we have learned:

  • Far jumps are not possible using relative branch instructions Bxx; one has to use e.g. JMP.
  • To subtract the value V using SBC #V, one has to make sure the carry flag is set (via SEC) rather than cleared (via CLC).

Plot concentric colored squares

[edit | edit source]

Assignment: plot concentric square outlines in different colors, 16 of them so they completely fill the screen. Create a generic plot pixel routine for the purpose, passing the color and the x and y coordinates in registers.

Solution:

; ---- Main
; ----- Part 1
LDA #0 ; i: outer loop index
LOOP1:
TAX
TAY
JSR SUB32MINATO83 ; ADDR($83) := 32 - A
LOOP2:
JSR PLOTPIX ; The color is the same as the outer loop index
INX
CPX $83
BNE LOOP2
CLC
ADC #1
CMP #16
BNE LOOP1
; ----- Part 2
LDA #0 ; i: outer loop index
LOOP3:
TAX
JSR SUB32MINATO83 ; ADDR($83) := 32 - A
LDY $83
DEY
LOOP4:
JSR PLOTPIX ; The color is the same as the outer loop index
INX
CPX $83
BNE LOOP4
CLC
ADC #1
CMP #16
BNE LOOP3
; ----- Part 3
LDA #0 ; i: outer loop index
LOOP5:
TAX
TAY
JSR SUB32MINATO83 ; ADDR($83) := 32 - A
LOOP6:
JSR PLOTPIX ; The color is the same as the outer loop index
INY
CPY $83
BNE LOOP6
CLC
ADC #1
CMP #16
BNE LOOP5
; ----- Part 4
LDA #0 ; i: outer loop index
LOOP7:
TAY
JSR SUB32MINATO83 ; ADDR($83) := 32 - A
LDX $83
DEX
LOOP8:
JSR PLOTPIX ; The color is the same as the outer loop index
INY
CPY $83
BNE LOOP8
CLC
ADC #1
CMP #16
BNE LOOP7
BRK
; ---- ADDR($83) := 32 - A
SUB32MINATO83:
PHA   
STA $83
LDA #32
SEC
SBC $83
STA $83
PLA
RTS
; ---- Plot color in A at X, Y coord
; Uses $80-82
PLOTPIX:
STA $82 ;Store color for later use
PHA ;Save regs
TXA
PHA
TYA
PHA
LDA #0  ;$80-81 := screen address
STA $80
LDA #2
STA $81
TXA
CLC
ADC $80
STA $80
BCC PLSKIP1
INC $81
PLSKIP1:
TYA
ASL ;Multiply by 32 with carry into $81
ASL
ASL
ASL
BCC PLSKIP2
INC $81
INC $81
CLC
PLSKIP2:
ASL
BCC PLSKIP3
INC $81
PLSKIP3:
TAY
LDA $82
STA ($80), Y
PLA ; Restore regs
TAY
PLA
TAX
PLA
RTS

What we have learned: we created a subroutine that uses registers for argument passing (not stack) and stores the registers on stack.

Fill the screen without BNE and BEQ

[edit | edit source]

Assigment: fill the screen with a fixed color without using BNE and BEQ branch instructions. Hint: you can use BPL and BMI. Purpose: get acquainted with BPL and BMI.

A solution:

LDA #00
STA $80
LDA #02
STA $81
LDA #1    ;White color
LDY #0
LDX #3    ;Four pages to process
LOOP1:
STA ($80), Y
INY
BPL LOOP1 ; If 0 <= Y <= $7F, branch
LOOP2:
STA ($80), Y
INY
BMI LOOP2 ; If $80 <= Y <= $FF, branch
INC $81
DEX
BPL LOOP1

Fill the screen with a checkered pattern

[edit | edit source]

Assignment 1: fill the screen with two alternating colors 0 and 1 such that the alternation is both horizontal and vertical. Thus, create a checkered pattern where the squares have the size of 1 pixel.

A solution:

LDA #00   ;Screen start
STA $80
LDA #02
STA $81
LDA #1
LDY #0
LDX #4    ;Four pages to process
LOOP:
STA ($80), Y
EOR #1
INY
PHA
TYA
AND #$1F
BNE SKIP
PLA
EOR #1
PHA
SKIP:
PLA
CPY #0
BNE LOOP
INC $81
DEX
BNE LOOP

Assignment 2 (a variation of assignment 1): let these be two pseudorandomly chosen colors rather than 0 and 1, that is, colors obtained from the pseudorandom source at $FE.

A solution:

LDA $FE
AND #$1F
STA $82
COLOR2:
LDA $FE
AND #$1F
CMP $82
BEQ COLOR2
STA $83
;
LDA #00
STA $80
LDA #02
STA $81
;
LDA $82
LDY #0
LDX #4    ;Four pages to process
LOOP:
LDA $82
STA ($80), Y
INY
LDA $83
STA ($80), Y
INY
TYA
AND #$1F
BNE SKIP
; Swap $82 and $83
TYA
PHA
LDA $82
LDY $83
STA $83
STY $82
PLA
TAY
;
SKIP:
CPY #0
BNE LOOP
INC $81
DEX
BNE LOOP

Assignment 3: Implement assignment 2 with the difference that the square size is 2 instead of 1. Left as an exercise for the reader without solution (very easy modification of the solution for assignment 2).

Assignment 4: Implement assignment 2 with the difference that the square size is 4 instead of 1. Also very easy.

Assignment 5: Implement assignment 2 with the difference that the square size is 5 instead of 1 and some of the squares are truncated. Since 5 does not divide 32 (the screen size), this one is more difficult. Left without solution so far.

Assignment 6: Implement assignment 2 with the difference that the square size is picked as a pseudorandom number between 1 and 16. Depending on the square size, some of the squares may be truncated. This one is more difficult. Left without solution so far.

Swap two bytes using only accumulator

[edit | edit source]

Assignment: swap two bytes located e.g. at $80 and $81 using only the accumulator; do not use X and Y and do not use stack.

A solution:

; Set up a test
LDA $FE ; Random
STA $80
STA $82
LDA $FE ; Random
STA $81
STA $83
; Swap $80 and $81 using EOR
LDA $80
EOR $81
STA $80
EOR $81
STA $81
EOR $80
STA $80
; Verify
LDA $82
CMP $81
BNE BAD
LDA $83
CMP $80
BNE BAD
LDA #5   ; Green
STA $200
BRK
BAD:
LDA #8   ; Reddish
STA $200

Visualize bit patterns on screen

[edit | edit source]

Assignment: write a routine to visualize the content of a byte on the screen as an 8-pixel pattern corresponding to the bit pattern in the byte, using two colors 0 and non-0 (black and some non-black color). Thus, treat the byte as if it was part of the screen buffer of the high-resolution mode on Atari 800 XL and some other 8-bit computers. Figure out a test (not necessarily a complete one) for the byte visualization routine, e.g. by showing the value of #%10101010 that is successively shifted to the right.

Point: practice detection of the state of individual bits within bytes. Moreover, practice design of tests covering at least part of the input space. Furthermore, using this routine, we can visualize the results of various assembly operations on screen.

A solution:

; ---- Test byte visualization
LDA #%10101010
LDX #1
LDY #0
JSR VISUALIZEBYTESHIFTED
LDA #%11110000
LDX #3
LDY #8
JSR VISUALIZEBYTESHIFTED
LDA #%11001100
LDX #5
LDY #16
JSR VISUALIZEBYTESHIFTED
LDA #%10011001
LDX #7
LDY #24
JSR VISUALIZEBYTESHIFTED
BRK
; ---- Visualize a bit pattern successively shifted to the right
; Impacts 8x8 pixels.
; In: Y: initial byte offset; A: byte to visualize; X color for bit value 1
; Uses $84 and $85; modifies X and Y
VISUALIZEBYTESHIFTED:
STY $84   ;Screen offset
STA $85   ;Byte to visualize
VLOOP1:
LDA $85
LDY $84
JSR VISUALIZEBYTE
LSR $85
LDA $84
CLC
ADC #$20
STA $84
BCC VLOOP1
RTS
; ---- Visualize byte using pixels
; In: Acc: byte to visualize; Y: screen offset; X: bit 1 color (bit 0 is black)
; Use $80 as tmp; modify A and Y
VISUALIZEBYTE:
STA $80
LDA #$80
VBLOOP:
BIT $80
PHA
BNE VBSKIP1
LDA #0
BEQ VBSKIP2
VBSKIP1:
TXA
VBSKIP2:
STA $200, Y
PLA
INY
LSR
BNE VBLOOP
RTS

A variation: modify the above solution to allow plotting on any part of the screen rather than just the top quarter. Expand the test to make full use of the screen.

A solution:

; ---- Test byte visualization
LDA #%10101010
LDX #1
LDY #0
JSR VISUALIZEBYTESHIFTED
LDA #%11110000
LDX #3
LDY #1
JSR VISUALIZEBYTESHIFTED
LDA #%11001100
LDX #5
LDY #2
JSR VISUALIZEBYTESHIFTED
LDA #%10011001
LDX #7
LDY #3
JSR VISUALIZEBYTESHIFTED
LDA #%10000000
LDX #8
LDY #32
JSR VISUALIZEBYTESHIFTED
LDA #%10100000
LDX #10
LDY #33
JSR VISUALIZEBYTESHIFTED
LDA #%10101000
LDX #12
LDY #34
JSR VISUALIZEBYTESHIFTED
LDA #%10100100
LDX #13
LDY #35
JSR VISUALIZEBYTESHIFTED
LDA #%11000000
LDX #14
LDY #64
JSR VISUALIZEBYTESHIFTED
LDA #%11010000
LDX #15
LDY #65
JSR VISUALIZEBYTESHIFTED
LDA #%11001000
LDX #4
LDY #66
JSR VISUALIZEBYTESHIFTED
LDA #%11000100
LDX #6
LDY #67
JSR VISUALIZEBYTESHIFTED
LDA #%11111111
LDX #1
LDY #96
JSR VISUALIZEBYTESHIFTED
LDA #%10111111
LDX #3
LDY #97
JSR VISUALIZEBYTESHIFTED
LDA #%10011111
LDX #5
LDY #98
JSR VISUALIZEBYTESHIFTED
LDA #%10010111
LDX #7
LDY #99
JSR VISUALIZEBYTESHIFTED
BRK
; ---- Visualize a bit pattern successively shifted to the right
; Impacts 8x8 pixels.
; In: Y: initial byte offset * 8; A: byte to visualize; X color for bit value 1
; Uses $84-86 directly and $80-82 indirectly; modifies X and Y
VISUALIZEBYTESHIFTED:
STA $85   ;Byte to visualize
STY $84   ;Screen offset * 8
LDA #8
STA $86   ;Loop index
VLOOP1:
LDA $85
LDY $84
JSR VISUALIZEBYTE
LSR $85
LDA $84
CLC
ADC #$4
STA $84
DEC $86
BNE VLOOP1
RTS
; ---- Visualize byte using pixels
; In: Acc: byte to visualize; Y: screen offset * 8; X: bit 1 color (bit 0 is black)
; Use $80-82 as tmp; modify A and Y
VISUALIZEBYTE:
; Set up screen base
STA $82
LDA #00
STA $80
LDA #02
STA $81
TYA
ASL
ASL
BCC VBSKIP0
INC $81
INC $81
VBSKIP0:
ASL
BCC VBSKIP3
INC $81
VBSKIP3:
TAY
;
LDA #$80
VBLOOP:
BIT $82
PHA
BNE VBSKIP1
LDA #0
BEQ VBSKIP2
VBSKIP1:
TXA
VBSKIP2:
STA ($80), Y
PLA
INY
LSR
BNE VBLOOP
RTS

A variation: use the above solution's VISUALIZEBYTE routine to plot all byte values from 0 to 127, thereby filling the screen with them. Use different colors for different 8 pixel wide columns. This demonstrates correct function of VISUALIZEBYTE for half of the input space.

A solution:

LDA #0
STA $83
LDX #1
LOOP:
LDA $83
TAY
JSR VISUALIZEBYTE
INX
INX
INX
INX
INC $83
BPL LOOP
BRK
; ---- Visualize byte using pixels
; In: Acc: byte to visualize; Y: screen offset * 8; X: bit 1 color (bit 0 is black)
; Use $80-82 as tmp; modify A and Y
VISUALIZEBYTE:
; Set up screen base
STA $82
LDA #00
STA $80
LDA #02
STA $81
TYA
ASL
ASL
BCC VBSKIP0
INC $81
INC $81
VBSKIP0:
ASL
BCC VBSKIP3
INC $81
VBSKIP3:
TAY
;
LDA #$80
VBLOOP:
BIT $82
PHA
BNE VBSKIP1
LDA #0
BEQ VBSKIP2
VBSKIP1:
TXA
VBSKIP2:
STA ($80), Y
PLA
INY
LSR
BNE VBLOOP
RTS

Scrolling the screen

[edit | edit source]

Assignment: implement a routine for scrolling the screen to the right by one pixel, leaving the leftmost pixels just scrolled away black. Test it by filling the screen with pseudorandom pixels and scrolling the content by 32 pixels, thereby removing it from the screen.

A solution:

; ---- Test
; ----- Fill screen
LDA #0
STA $80
LDA #2
STA $81
LDX #4
LDY #0
TLOOP:
LDA $FE
STA ($80),Y
INY
BNE TLOOP
INC $81
DEX
BNE TLOOP
; ----- Scroll
LDA #32
STA $84
TLOOP2:
JSR SCROLLSCREENRIGHT
DEC $84
BNE TLOOP2
BRK
; ---- Scroll right by 1 pixel
; Uses $80-83
SCROLLSCREENRIGHT:
LDA #0
STA $80
LDA #2
STA $81
LDA #32
STA $82 ; Line count
LDA #31
STA $83 ; On-screen offset
SLOOP1:
LDX #31 ; Col count
SLOOP2:
DEC $83
LDY $83
LDA ($80),Y
INY
STA ($80),Y
DEX
BNE SLOOP2
LDA #0
DEY
STA ($80),Y
LDA $83
CLC
ADC #63 ;+32+31
BCC SSKIP
INC $81
SSKIP:
STA $83
DEC $82
BNE SLOOP1
RTS

Fibonacci sequence

[edit | edit source]

Assignment: calculate Fibonacci sequence using 16-bit integer arithmetic. (Variations of the assignment follow below.)

A solution:

LDX #22  ;Fibonacci number index
LDA #1
STA $80  ;$80-81: n1
STA $82  ;$82-83: n2
LDA #0
STA $81
STA $83
CPX #1
BEQ DONE
DEX
LOOP:
; n3 = n1 + n2; $84-85: n3
LDA $80
CLC
ADC $82
STA $84
LDA $81
ADC $83
STA $85
BCS OVERFLOW
; n1 = n2; n2 = n3
LDA $82
STA $80
LDA $83
STA $81
LDA $84
STA $82
LDA $85
STA $83
DEX
BNE LOOP
DONE:
LDA #5 ; Green
STA $200
; The number is in n1, $80-81.
BRK
OVERFLOW:
LDA #8 ; Reddish
STA $200

What we have learned: the carry flag's interaction with ADC makes 16-bit addition trivial.

A variation: modify the above to be 32-bit and to put the Fibonacci numbers on screen as bit patterns, one number per line. For the putting on screen, you can use routine VISUALIZEBYTE from a previous exercise.

A solution:

LDX #32  ;Fibonacci number index; if greater than 32, visualization will wrap
LDA #1
STA $84  ;$84-87: n1
STA $88  ;$88-8B: n2
LDA #0
STA $85
STA $86
STA $87
STA $89
STA $8A
STA $8B
LDY #0 ; Screen offset * 8
CPX #1
BEQ DONE
DEX
LOOP:
JSR VISUALIZEN1
; n3 = n1 + n2; $8C-8F: n3
LDA $84
CLC
ADC $88
STA $8C
LDA $85
ADC $89
STA $8D
LDA $86
ADC $8A
STA $8E
LDA $87
ADC $8B
STA $8F
BCS OVERFLOW
; n1 = n2
LDA $88
STA $84
LDA $89
STA $85
LDA $8A
STA $86
LDA $8B
STA $87
; n2 = n3
LDA $8C
STA $88
LDA $8D
STA $89
LDA $8E
STA $8A
LDA $8F
STA $8B
DEX
BNE LOOP
DONE:
JSR VISUALIZEN1
LDA #5 ; Green
STA $200
; The number is in n1, $84-87.
BRK
OVERFLOW:
LDA #8 ; Reddish
STA $200
BRK
; ---- Visualize n1
; In: Y: screen offset * 8; n1? $84-87
; Increments Y by 4
; Preserves X
VISUALIZEN1:
TXA
PHA
LDX #1
LDA $87
JSR VISUALIZEBYTE
LDA $86
INY
JSR VISUALIZEBYTE
LDA $85
INY
JSR VISUALIZEBYTE
LDA $84
INY
JSR VISUALIZEBYTE
INY
PLA
TAX
RTS
; ---- Visualize byte using pixels
; In: Acc: byte to visualize; Y: screen offset * 8; X: bit 1 color (bit 0 is black)
; Use $80-83 as tmp; modify A; preserve Y
VISUALIZEBYTE:
STY $83
STA $82
; Set up screen base
LDA #00
STA $80
LDA #02
STA $81
TYA
ASL
ASL
BCC VBSKIP0
INC $81
INC $81
VBSKIP0:
ASL
BCC VBSKIP3
INC $81
VBSKIP3:
TAY
;
LDA #$80
VBLOOP:
BIT $82
PHA
BNE VBSKIP1
LDA #0
BEQ VBSKIP2
VBSKIP1:
TXA
VBSKIP2:
STA ($80), Y
PLA
INY
LSR
BNE VBLOOP
LDY $83
RTS

What we have learned: the carry flag's interaction with ADC makes addition trivial for any fixed number of bytes representing an integer.

A variation: modify the above to have the number of bytes used to represent an integer parametrized, to up to 255 bytes. Do not visualize the result. Slightly more challenging.

A solution:

;$80-81: n1 address
;$82-83: n2 address
;$84-85: n3 address
;$86: int size in bytes
;$88-89: Fibonacci number index; must not be 1
LDA #0
STA $88
LDA #1
STA $89
; Set int size
LDA #$20
STA $86
; Set addresses
LDA #0   ; DOKE $80, $1000
STA $80
LDA #$10
STA $81
LDA $80
CLC
ADC $86 
STA $82
LDA $81
ADC #0
STA $83
LDA $82
CLC
ADC $86
STA $84
LDA $83
ADC #0
STA $85
; Zero n1, n2 and n3
LDA #0
LDY #0
ZLOOP:
STA ($80),Y
STA ($82),Y
STA ($84),Y
INY
CPY $86
BNE ZLOOP
; Set n1 and n2 to 1
LDA #1
LDY #0
STA ($80),Y
STA ($82),Y
; Reduce $88-89 by 2
LDA $88
SEC
SBC #2
STA $88
LDA $89
SBC #0
STA $89
MAINLOOP:
; n3 = n1 + n2;
LDY #0
LDX $86
CLC
PLUSLOOP:
LDA ($80),Y
ADC ($82),Y
STA ($84),Y
INY
DEX
BNE PLUSLOOP
; n1 = n2
LDY #0
LOOPA1:
LDA ($82),Y
STA ($80),Y
INY
CPY $86
BNE LOOPA1
; n2 = n3
LDY #0
LOOPA2:
LDA ($84),Y
STA ($82),Y
INY
CPY $86
BNE LOOPA2
; Decrement $88-89
LDA $88
SEC
SBC #1
STA $88
LDA $89
SBC #0
STA $89
CMP #$FF
BNE MAINLOOP
; The result is in n1, starting at $1000.

What we have learned:

  • Short memory copy.
  • Addition of many-byte integers.
  • Decrement of a 16-bit integer.

Floating-point arithmetic

[edit | edit source]

Implementing floating-point arithmetic on 6502 is not for beginners. If one is interested in a solution, can peruse e.g. 6502.org linked below.

Links:

Online assemblers

[edit | edit source]

One can asseble the 6502 assembly source code using online assemblers:

The Masswerk assembler is too sensitive in that it does not compile "LDA ($80), Y" because of the space after the comma. Moreover, it assembles "LDA ($80),X", which is invalid, into "LDA $80,X". Easy 6502 seems better in those two regards.

6502 source code listings

[edit | edit source]

Some 6502 source code listings, from which one can learn (copyrighted!):

[edit | edit source]