Learning 6502 assembly
This resource includes primary and/or secondary research. Learn more about original research at Wikiversity. |
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
editHere 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:
- 6502 Instruction Set, masswerk.at
- 6502 Family CPU Reference by Michael Steil, pagetable.com
- NMOS 6502 Opcodes by John Pickens et al., 6502.org
- 6502 Assembly, wikibooks.org -- contains some suspect material, e.g. "INC $F001,Y", which does not exist
Limitations of the CPU
editTo 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
editAssignment: 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
editAssignment: 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
editAssignment: 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
editAssignment: 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
editAssignment: 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
editAssignment: 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
editAssignment: 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:
- Tutorials: Compare Instructions, 6502.org
Keyboard controlled pixels
editAssignment: 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
editAssignment: 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
editAssignment: 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
editAssigment: 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
editAssignment 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
editAssignment: 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
editAssignment: 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
editAssignment: 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
editAssignment: 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
editImplementing 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:
- Source Code Repository, 6502.org
Online assemblers
editOne can asseble the 6502 assembly source code using online assemblers:
- Easy 6502, skilldrick.github.io
- virtual 6502 Assembler, masswerk.at
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
editSome 6502 source code listings, from which one can learn (copyrighted!):
- Source Code Repository, 6502.org
- jmechner/Prince-of-Persia-Apple-II, github.com
- Arcade Games: Source Code Collection | Michael R. Cook -- not all items listed are in 6502
- 6502disassembly.com by Andy McFadden
External links
edit- Easy 6502, skilldrick.github.io
- MOS Technology 6502, wikipedia.org
- 6502 Assembly, wikibooks.org
- 6502.org, 6502.org
- Category:6502 Assembly, rosettacode.org
- 6502 Instruction Set, masswerk.at
- virtual 6502 Assembler, masswerk.at
- Topic 6502, language assembly, github.com
- Programming the 6502 by Rodnay Zaks, archive.org
- 6502 assembly language programming by Lance A. Leventhal, archive.org
- MCS6500 MICROPROCESSOR, Preliminary data sheet, 1976, archive.6502.org
- 65CE02 MICROPROCESSOR, archive.6502.org -- for a particular version