Introduction
Welcome to the second installment of our journey into the fascinating realm of 6502 assembly language programming. In this lab, we will delve into the intricacies of this low-level language, starting with an examination of the performance and optimization of a code snippet that fills the emulator's bitmapped display with a vibrant yellow hue.
Our primary focus will be on a snippet of code that accomplishes the task of painting the screen yellow. However, we won't stop at mere execution; we'll dissect the code, understand its inner workings, and experiment with ways to make it more efficient.
We'll calculate execution times based on a clock speed of 1 MHz and determine the memory usage for the program code and associated pointers or variables. Our ultimate goal is to devise the fastest version of this program, aiming for more than double the speed of the original.
But that's not all. We'll also modify the code to fill the display with different colors, challenge ourselves with optional experiments, and even tackle some advanced challenges. If you're ready to embark on this coding adventure, grab your keyboard, and let's dive into the world of 6502 assembly language.
For this Lab I am going to use the emulator from this website : https://skilldrick.github.io/easy6502/
As the emulator given by our professor was not working(may be some server error)
Without further ado, let's begin our exploration of assembly language programming!
Bitmap Code
The below code fills the bitmapped display with yellow color:
LDA #$00 ; set a pointer in memory location $40 to point to $0200
STA $40 ; ... low byte ($00) goes in address $40
LDA #$02
STA $41 ; ... high byte ($02) goes into address $41
LDA #$07 ; colour number
LDY #$00 ; set index to 0
loop: STA ($40),y ; set pixel colour at the address (pointer)+Y
INY ; increment index
BNE loop ; continue until done the page (256 pixels)
INC $41 ; increment the page
LDX $41 ; get the current page number
CPX #$06 ; compare with 6
BNE loop ; continue until done all pages
like this:
This code is pretty straightforward. We start by storing the value of the first pixel in memory addresses $40 and $41. Then, we iterate through each pixel on a page, setting its color. Once we finish one page, we move to the next, and we do this for a total of four pages. Memory locations $200 to $5FF map to the screen pixels, and we start from $0200 and keep incrementing until $05FF.
Let's calculate the performance of this code.
Calculating Performance
To calculate the performance of this code we can count the total number of cylcles and byte each instruction will take and then add them up to get total cycles and bytes.
So this is the cycle count and byte I have calculated for each instructions:
|
| Bytes | Cycles | Cycle Count | Alt Cycles | Alt count | Total | |
|
|
|
|
|
|
|
| |
| lda #$00 | 2 | 2 | 1 |
|
| 2 | |
| sta $40 | 2 | 3 | 1 |
|
| 3 | |
| lda #$02 | 2 | 2 | 1 |
|
| 2 | |
| sta $41 | 2 | 3 | 1 |
|
| 3 | |
|
|
|
|
|
|
| 0 | |
| lda #$07 | 2 | 2 | 1 |
|
| 2 | |
|
|
|
|
|
|
| 0 | |
| ldy #$00 | 2 | 2 | 1 |
|
| 2 | |
|
|
|
|
|
|
| 0 | |
loop: | sta ($40),y | 3 | 6 | 1024 |
|
| 6144 | |
|
|
|
|
|
|
| 0 | |
| iny | 1 | 2 | 1024 |
|
| 2048 | |
| bne loop | 2 | 3 | 1020 | 2 | 4 | 3068 | |
|
|
|
|
|
|
| 0 | |
| inc $41 | 2 | 5 | 4 |
|
| 20 | |
| ldx $41 | 2 | 3 | 4 |
|
| 12 | |
| cpx #$06 | 2 | 2 | 4 |
|
| 8 | |
| bne loop | 2 | 3 | 3 | 2 | 1 | 11 | |
|
| 26 |
|
|
| Total: | 11325 | cycles |
CPU Speed: | 1 | MHz | ||||||
uS per clock | 1 |
| ||||||
Time: | 11325 | uS | ||||||
| 11.325 | mS | ||||||
| 0.011325 | S |
So, the total time it takes to execute this program is 0.011325 seconds.
Total program code size: 26 bytes
Memory locations $40 and $41 are used for pointers and variables, each taking up 1 byte.
Total for pointers and variables: 2 bytes
So, the total memory usage for your program (assuming it's stored in RAM) is:
Program code size + Pointers and Variables = 26 bytes + 2 bytes = 28 bytes
Modifying the Code
Lets Modify our code to print blue color
To do that we just need to change the color number from 07 to 06
LDA #$00 ; set a pointer in memory location $40 to point to $0200
STA $40 ; ... low byte ($00) goes in address $40
LDA #$02
STA $41 ; ... high byte ($02) goes into address $41
LDA #$06 ; colour number <-- here we change it to 06
LDY #$00 ; set index to 0
loop: STA ($40),y ; set pixel colour at the address (pointer)+Y
INY ; increment index
BNE loop ; continue until done the page (256 pixels)
INC $41 ; increment the page
LDX $41 ; get the current page number
CPX #$06 ; compare with 6
BNE loop ; continue until done all pages
Our output would we like this:
Lets modify our code to fill the display with a different colour on each page .
To achive that we can just change the value of color.
In my code I have achieved that by adding 01 value in the accumulate after one page color has printed
So, in the begining the value was #$06, after one loop it becomes #$07 then #$08 and then last #$09.
LDA #$00 ; set a pointer in memory location $40 to point to $0200
STA $40 ; ... low byte ($00) goes in address $40
LDA #$02
STA $41 ; ... high byte ($02) goes into address $41
LDA #$06 ; colour number
LDY #$00 ; set index to 0
loop: STA ($40),y ; set pixel colour at the address (pointer)+Y
INY ; increment index
BNE loop ; continue until done the page (256 pixels)
INC $41 ; increment the page
LDX $41 ; get the current page number
CLC ; clear any carry
ADC #$01 ; add value 01 in accumulator
CPX #$06 ; compare with 6
BNE loop ; continue until done all pages
Our output is going to look like this:
Lets modify our code to make it little faster.
To do that we have to lower the number of instructions we are executing one of the possible solution is loading the x register at the beginning and then just comparing the highest byte with the x register. lets see the code:
LDA #$00 ; set a pointer in memory location $40 to point to $0200
STA $40 ; ... low byte ($00) goes in address $40
LDA #$02
STA $41 ; ... high byte ($02) goes into address $41
LDA #$06 ; colour number
LDX #$06 ; load x register with 06
LDY #$00 ; set index to 0
loop: STA ($40),y ; set pixel colour at the address (pointer)+Y
INY ; increment index
BNE loop ; continue until done the page (256 pixels)
INC $41 ; increment the page
CPX $41 ; compare X register with higher byte after increament the higher byte
BNE loop ; continue until done all pages
by doing this we have 3 less instructions but that is not enough. Think of some ways by which we can execute less and less instructions and achieve the same result.
Lets experiment with our code a bit:
Experiments
- If we add this instruction after the
loop:
label and before thesta ($40),y
instruction:tya
like this:
LDA #$00 ; set a pointer in memory location $40 to point to $0200
STA $40 ; ... low byte ($00) goes in address $40
LDA #$02
STA $41 ; ... high byte ($02) goes into address $41
LDA #$07 ; colour number -- even if we remove this line same thing --
LDY #$00 ; set index to 0
loop:
tya ; transfer y to accumulator <--- this is the new line added
STA ($40),y ; set pixel colour at the address (pointer)+Y
INY ; increment index
BNE loop ; continue until done the page (256 pixels)
INC $41 ; increment the page
LDX $41 ; get the current page number
CPX #$06 ; compare with 6
BNE loop ; continue until done all pages
our output is going to look like this:
We can observe from the above out that it is printing all the 16 colors in line consecutively at each pixel.
It is very clear from the code that with this instruction tya
we are transfering the value of y into the accumulator.
Moreover that instruction is inside the loop and after transfering the value from the loop it is increased by one and then again transfered into accumulator. So the loop started with 0 and will run all the way to f and our bitmapped dispaly is 32 pixel wide so it will repeat the colors. So, that's why we are able to see all the color lines.
Conclusion
Congratulations on completing the second installment of our journey into 6502 assembly language programming! In this lab, we have learned how to calculate the performance and memory usage of assembly code, modified the code to fill the screen with a different color, and explored optional experiments and advanced challenges to further enhance your skills.
Assembly language programming can be challenging but incredibly rewarding. The more you experiment and practice, the more proficient you'll become. Whether you're interested in retro game development, demoscene productions, or low-level systems programming, assembly language is a valuable skill to have in your toolkit.
Keep exploring, learning, and coding, and don't hesitate to reach out if you have any questions or need assistance on your assembly language journey. Happy coding!