I'm starting to get into 6502 assembly and working through the 64 Bites seasons. I'm on Season 2 episode 17 loops but kinda stuck on the second exercise question. The question is...
Modify program below using add_words_imm macro so that:
- The first row is filled with the letter X.
- The first column is filled with the letter X.
:BasicUpstart2(main)
main:
ldx #0
lda #24
loop:
sta $0400
// TODO: modify program here
inx
cpx #40
bne loop
rts
.macro add_words_imm(a, b, result) {
clc
.for(var byte = 0; byte < 2; byte++) {
lda a + byte
adc #extract_byte(b, byte)
sta result + byte
}
}
.function extract_byte(value, byte_id) {
.var bits = byte_id * 8
.eval value = value >> bits
.return value & 255
}
For the first part of the question, my first thought would be to just do the following.
:BasicUpstart2(main)
main:
ldx #0
lda #24
loop:
sta $0400,x
inx
cpx #40
bne loop
rts
Maybe I'm thinking too literally about the code snippet in the question but my assumption is that the existing code stays and I just add my code where it says "// TODO: modify program here".
That being said I'm struggling to see how to use the macro to fill the top row with X's.
Also, I'm using Kick Assembler and its scripting language to compile and build the .prg/.sym files.
To fill the first row with X
's, you already have a loop; as you noted in your question you just need to change the memory address into which you're writing:
:BasicUpstart2(main)
main:
ldx #0
lda #24
loop:
sta $0400,X
inx
cpx #40
bne loop
rts
The add_words_imm
macro is useful (-ish) for filling in column one because we need to increment a value by 40 (the number of screen columns) a number of times, and the value would overflow a simple 8 bit register.
There's probably a more elegant way of writing this, but this works:
:BasicUpstart2(main)
.pc = $c000
main:
ldx #0
lda #24
fill_row_1:
sta $0400,X
inx
cpx #40
bne fill_row_1
ldy #0
ldx #24
// Load screen base address ($0400) into location $fb
lda #$0
sta $fb
lda #$04
sta $fc
// Load character "X" into A
lda #24
fill_column_1:
// Save A because add_words_imm changes it
pha
// Increment target address by 40 characters
add_words_imm($fb, 40, $fb)
// Restore A
pla
// Write character to screen location
sta ($fb),y
dex
bne fill_column_1
rts
.macro add_words_imm(a, b, result) {
clc
.for(var byte = 0; byte < 2; byte++) {
lda a + byte
adc #extract_byte(b, byte)
sta result + byte
}
}
.function extract_byte(value, byte_id) {
.var bits = byte_id * 8
.eval value = value >> bits
.return value & 255
}
We store the target address in a zero-page location ($fb
) and then use add_words_imm
to increment this value each time through the loop. We set Y
to zero (and never change it) so we can use $fb
as the address of our next character.
We use X
as the loop index and loop 24 times; once for each screen line.
For the record and future readers, if I were writing this myself I wouldn't bother with those subroutines and I would instead write:
:BasicUpstart2(main)
.pc = $c000
main:
ldx #0
lda #24
fill_row_1:
sta $0400,X
inx
cpx #40
bne fill_row_1
lda #24
ldy #6
ldx #0
clc
fill_column_1:
sta $0400,X
sta $04f0,X
sta $05e0,X
sta $06d0,X
pha
txa
adc #40
tax
pla
dey
bne fill_column_1
rts
This divides the screen into four blocks of six lines each and fills the first column of these blocks together.