Lab1 6502 Assembly Language
Optimizing Assembly: Insights from Lab 1
Introduction
Lab 1 introduced me to the fundamentals of 6502 Assembly programming. It was my first real experience diving into low-level code, and honestly, it was a mix of confusion and excitement. The goal of the lab was to analyze an existing assembly program, understand how it works, and then try making changes to improve or modify what it does. This blog post walks through my personal journey with the lab—from running the original code to experimenting with new instructions—and what I learned along the way.
Running the Bitmap Code
We kicked things off with a basic assembly program designed to fill the emulator’s display with a single solid color. The code sets a pointer in memory to start writing pixel data and loops through, placing the same color on each pixel. Here’s a quick breakdown of what happens:
The program sets up a pointer to location $0200 (the start of the bitmap).
It sets the color to yellow with LDA #$07.
Then it uses a loop to write this color to every pixel, one at a time, across the screen.
When I ran it, the emulator filled up completely with yellow, and it felt good to see the result match the expected outcome. It confirmed the code was working properly and helped me understand how memory is used for graphics at such a low level.
Performance Optimization
After understanding what the base code did, we explored ways to make it better or do more interesting things with it. The main focus was on reducing redundant instructions and experimenting with how the bitmap could be manipulated differently.
1. Changing the Color
Our first small change was switching the display from yellow to light blue. This was as simple as changing the line LDA #$07 (yellow) to LDA #$0E (light blue). I liked this one because it was straightforward but still gave me a sense of control over the visual output.
2. A Different Color for Each Page
Next, we got a bit fancier. Instead of using the same color across the entire screen, I made each “page” (a quarter of the display) use a different color. To do this, I added ADC #$02 after each full-page loop to change the color in the accumulator. The effect was really cool—it looked like the screen was split into colorful blocks stacked vertically. This helped me understand how values change as the program moves through memory.
3. Random Colors for Each Pixel
This part was probably my favorite. By loading a pseudo-random value into the accumulator using LDA $FE inside the loop, I got a full-screen display of totally random colors. The chaos was beautiful, and it was a great example of how code interacts with memory and the emulator’s color palette.
Experiments
Now this is where things got interesting. We started experimenting with specific assembly instructions to see how they change the display. Here's what I learned:
Using TYA Before STA ($40),Y
I added the TYA instruction just before STA ($40),Y. What it did was copy the Y register value into the accumulator, and then store that value to memory. Since Y was already being incremented with INY, the effect was that each column of pixels got a different color. The screen filled with colorful vertical lines—32 of them, because the Y register cycles through 0–255 and wraps around after 256 steps.
Adding LSR After TYA
Next, I added a LSR instruction after TYA. This shifts all the bits in the accumulator one place to the right, effectively halving the color value. The result was thicker vertical lines, and the number of unique colors dropped to 16. It looked like fewer but wider columns.
Adding Multiple LSR Instructions
I repeated the test with two, three, four, and even five LSR instructions. Each time, the colors became fewer and the lines thicker. It was like compressing the display visually. With each added LSR, I saw less detail in the output, and it really drove home how bit-shifting changes data.
Replacing with ASL Instructions
Then I flipped the script and used ASL instead. This shifts bits to the left, which had the opposite effect of LSR. The colors shifted and started looking darker—eventually turning to black. Since ASL multiplies the value, I could see how it quickly ran out of valid color values and just defaulted to zero (which is black in this palette). It gave me a better understanding of how bit manipulation can either build up or break down values.
Playing Around with Multiple INY Instructions
Lastly, I tested what happens when you use multiple INY instructions in a row. Since INY increments the index used for placing colors in memory, stacking a few of them skips pixels. For example:
One INY: smooth line-by-line fill.
Two INYs: every second pixel is colored.
Three or more: it starts looking glitchy or skips around in memory.
With more INYs, the pattern got weirder. Sometimes parts of the screen stayed black, or colors repeated in unusual ways. I learned that too many skips make the screen fill inconsistently, and it helped me visualize how looping works with memory addresses.
Challenges
Drawing a Different Color for the Center Pixels
This challenge was a fun mix of logic and trial-and-error. The goal was to fill the entire display with one color, but leave the middle four pixels in a different color. Sounds simple at first—but in Assembly, nothing is ever that straightforward!
To start, I used the same setup as before to fill the whole screen with one solid color. I picked light blue (#$0E) again just to keep things consistent. But the tricky part was figuring out where the center of the screen is in memory.
Each "page" in memory corresponds to 256 pixels, and the display spans from memory location $0200 to $05FF, which is a total of 1024 bytes or pixels. So the midpoint lands right around the 512th pixel. That means the four middle pixels are roughly at:
$0400
$0401
$0402
$0403
So what I did was run the usual loop to fill the entire display with the base color (light blue). Then after that, I directly loaded a new color into the accumulator (I chose red #$02), and manually stored it into those four memory locations.
Here’s what that part of the code looked like:
assembly
CopyEdit
; fill screen with light blue
lda #$0E
ldx #$00
loop:
sta $0200,x
inx
bne loop
; second page
lda #$0E
ldx #$00
loop2:
sta $0300,x
inx
bne loop2
; third page
lda #$0E
ldx #$00
loop3:
sta $0400,x
inx
bne loop3
; fourth page
lda #$0E
ldx #$00
loop4:
sta $0500,x
inx
bne loop4
; set middle pixels to red
lda #$02
sta $0400
sta $0401
sta $0402
sta $0403
And boom—done! The screen fills up with blue, but right in the center, there’s a tiny red square of four pixels. It may not look super fancy, but figuring out where exactly to poke the memory took some thinking, and honestly, it felt pretty satisfying when it worked.
Conclusion & Reflection
This lab really stretched my brain. I’ve never worked with assembly before, and I won’t lie—it was tough to wrap my head around some of these concepts. But after playing with the code, seeing how each instruction impacts the screen, and making sense of how registers and memory interact, I feel way more confident.
Assembly programming is all about precision. Every instruction counts. It’s a different mindset compared to higher-level languages, but now I get why people find it so fascinating. It’s like solving a puzzle with just a handful of tools.
By the end of this lab, I had a better understanding of how the CPU talks to memory, how graphics are rendered pixel by pixel, and how you can manipulate visuals with just a few lines of code. It was a challenging start—but definitely a rewarding one.
Comments
Post a Comment