6502 Loops and Macros with Kick Assembler

141 Views Asked by At

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:

  1. The first row is filled with the letter X.
  2. 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.

1

There are 1 best solutions below

1
On BEST ANSWER

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.