Page 1 of 1

Greetings and Question from Newbie!

Posted: Fri Aug 23, 2024 8:21 am
by Saturn78
Hi there,


after a while I've found time to study my beloved C64 asm, and I've done a setup with Kick Assembly chained with VSCode.

I've tried to study alone, but after I while i think i can declare myself completely stuck and as a newbie I've found this forum for help!

I've already made some sprite with the help of SpritePad, now i'm facing a new challenge: print a charmap!

So i've made a new charmap with CharpadPro, made by 40x8 chars, 320 in total.

I've put up this snippet to print it on screen:

Code: Select all

BasicUpstart2(main) 
#import "screenConstants.asm"
#import "memoryMap.asm" 
 
*=CHARSET_ADDRESS "Charset" 
.import binary "test/FancyMap_CharMapFull - Chars.bin" // 

*=CHARSET_ATTRIB_ADDRESS "Charset Attrib" 
.import binary "test/FancyMap_CharMapFull - CharAttribs_L1.bin" // 

*=HUD_ADDRESS "MAKA_TITLE" 
.import binary "test/FancyMap_CharMapFull - (8bpc, 40x8) Map.bin" // 
 
*=GAME_CODE_ADDRESS "Game Code" 
main: 
    jsr SCREEN_CLEAR

    lda #%11011000   // enable MULTICOLOR - Bit #4: 1 = Multicolor mode on.
    sta $d016
      
     lda #%00011110       
     sta $d018
  
lda #BLUE
sta $d023 //extra color 2

lda #BLACK
sta $d022 //extra color 1
 
lda #RED
sta $d021 //BG color

ldx #00            // Initialize loop counter to 0
ldy #00 //#$192             // Initialize counter for adding $28  //192

jmp loop_start

exit:
    rts
    
loop_start:
    lda loop_counter 
    tax 
    tay
    lda HUD_ADDRESS, x  // Load the char B
    sta $0400, x  // Screen RAM
    tax // Transfer the numer of B to the x-register 
    lda CHARSET_ATTRIB_ADDRESS,x
    sta $d800, y // Color RAM 
    inc loop_counter
    
    jmp loop_start
 
loop_counter: .word 0 //192       // Define a temporary variable (initialized to 0)
loop_row_limit: .byte 0
 

But I probably hit the 255 limit.
With sprites, I've overcome affecting $D010 location, but now I don't know how to do it.

Any advice?

Thanks in advance!

Re: Greetings and Question from Newbie!

Posted: Fri Aug 23, 2024 8:44 am
by JohanFr
Not entirely sure if this is what you are asking about but:

The screen is 1000 bytes, but the X/Y registers can only hold 256 values, so either one need to do a nested loop, or unroll it:

Code: Select all

  ldx #0
copyscreen:
  lda TILEDATA,x
  sta $0400,x
  lda TILEDATA+$100,x
  sta $0500,x
  lda TILEDATA+$200,x
  sta $0600,x
  lda TILEDATA+$300,x
  sta $0700,x
  inx
  bne copyscreen
As you are using Kickassembler, you can take advantage of its script language, making the code more compact:

Code: Select all

  ldx #0
copyscreen:
  .for (var i = 0; i < 4; i++) {
    lda TILEDATA+i*$100,x
    sta $0400+i*$100,x
  }
  inx
  bne copyscreen
  
This will overwrite the sprite pointers at the end of the screen area, so make sure you update those after. Alternatively, do the above for 3 out of the 4 pages, and have a fourth loop not go all 256 bytes.

Same thing for your color ram code.

Re: Greetings and Question from Newbie!

Posted: Fri Aug 23, 2024 8:53 am
by JohanFr
Sorry, forgive my previous answer, answering something you didn't ask. I didn't read properly.

But I would take advantage of the Kickassembler's script-language to do loop unrolling anyway. Something like:

Code: Select all

  ldx #39
loop_start:
  .for (var i = 0; i < NUMBER_OF_ROWS; i++) {
    lda HUD_ADDRESS+i*40, x  // Load the char B
    sta $0400+i*40, x  // Screen RAM
    tay // Transfer the number of B to the y-register
    lda CHARSET_ATTRIB_ADDRESS,y
    sta $d800+i*40, x // Color RAM
  }
  dex
  bpl loop_start

Re: Greetings and Question from Newbie!

Posted: Fri Aug 23, 2024 9:01 am
by BigEd
(Welcome, Saturn78!)

Re: Greetings and Question from Newbie!

Posted: Fri Aug 23, 2024 9:13 am
by JohanFr
If you want the purist approach without using script language, you can use zero page pointers for the screen ram, color ram and the hud, or self modifying code:

Code: Select all

  .const SCREEN=$0400
  .const COLORRAM=$d800
  .const HUD=$XXXX 

  lda #<SCREEN   // Get the low byte of screen address
  sta load_screen  // Update the low byte address of the self modified lda nnnn,x instruction
  lda #>SCREEN  // Get the high byte of the screen address
  sta load_screen+1  // Update the high byte address of the self modified lda nnnn,x instruction
  lda #<HUD
  sta save_hud
  lda #>HUD
  sta save_hud+1
  lda #<COLORRAM
  sta save_colorram
  lda #>COLORRAM
  sta save_colorram+1

  ldy #NUMBER_OF_ROWS
  ldx #39

nextrow:
  sty save_y
nextchar:
  lda load_screen:$0000,x  // Self modified code. Will overwrite the $0000 in the init code above and in the adc code below
  sta save_hud:$0000,x // Self modified code. Will overwrite the $0000 in the init code above and in the adc code below
  tay
  lda CHARSET_ATTRIB_ADDRESS,y
  sta save_colorram:$0000,x // Self modified code. Will overwrite the $0000 in the init code above and in the adc code below
  dex
  bpl nextchar

  clc
  lda load_screen
  adc #40
  sta load_screen
  bcc !+
  inc load_screen+1
  clc

!:
  lda save_hud
  adc #40
  sta save_hud
  bcc !+
  inc save_hud+1
  clc

!:
  lda save_colorram
  adc #40
  sta save_colorram
  bcc !+
  inc save_colorram+1

!:
  ldy save_y:#00
  dey 
  bpl nextrow  
  
EDIT: I guess I ended up using the scriptlanguage for the selfmodifying code anyway. I'll be quiet now :)
EDIT2: Disclaimer, this was written without any assembling whatsoever so the code might be full of bugs. Hopefully it is helpful anyway :)

Re: Greetings and Question from Newbie!

Posted: Fri Aug 23, 2024 10:30 pm
by Saturn78
Here we are: first of all big thanks to JohanFr, it gives lots to study!

I've spent pretty much all day testing, and even if the "zero page" approach is theorically something i've read about, it still won't work, i need further investigations, when i'll be more confident I'll try to explain better my attempt if still won't succeed!

For what concerns the "syntactic sugar" of "KickAss" 8) , well after lots of try and error I made it work with following approach:

Code: Select all

BasicUpstart2(main) 
#import "screenConstants.asm"
#import "memoryMap.asm" 
  
*=CHARSET_ADDRESS "Charset" 
.import binary "test_big/testimura_CharMapBig - Chars.bin"  

*=CHARSET_ATTRIB_ADDRESS "Charset Attrib" 
.import binary "test_big/testimura_CharMapBig - CharAttribs_L1.bin"  

*=HUD_ADDRESS "test_TITLE" 
.import binary "test_big/testimura_CharMapBig - (8bpc, 40x12) Map.bin"  
  
main: 
    jsr SCREEN_CLEAR

    lda #%11011000   // enable MULTICOLOR - Bit #4: 1 = Multicolor mode on.
    sta $d016
      
     lda #%00011110       
     sta $d018
  
lda #BLUE
sta $d023 //extra color 2

lda #BLACK
sta $d022 //extra color 1
 
lda #RED
sta $d021 //BG color

ldx #00            // Initialize loop counter to 0
ldy #00 //#$192             // Initialize counter for adding $28  //192

jmp loop_start

exit:
    rts
    
.var NUMBER_OF_CHARS =  480
 
loop_start:
  .for (var i = 0; i < NUMBER_OF_CHARS; i++) {
 
   lda HUD_ADDRESS + i  // Load the "ith" char 
    sta $0400 + i  // Screen RAM
    tay // Transfer the number of B to the y-register
    lda CHARSET_ATTRIB_ADDRESS,y
    sta $d800 + i // Color RAM  
  } 
  jmp exit 
Attention: when I've noticed that I would be able to overcome 255 limitation I've decided to use a much bigger charMap made of 40 columns x 12 rows, this explains 480 as char number :wink:

With this i got full control of each char printed on screen, but after going back to suggested approach, I'm start thinking that probably i'm not doing this in most efficient way, maybe considering nested loop Cols\Rows or better way to save iterations\logic...

More important than everything is that I feel now i'm in the right place to learn :D

Re: Greetings and Question from Newbie!

Posted: Sat Aug 24, 2024 7:36 am
by JohanFr
Saturn78 wrote:
Here we are: first of all big thanks to JohanFr, it gives lots to study!

I've spent pretty much all day testing, and even if the "zero page" approach is theorically something i've read about, it still won't work, i need further investigations, when i'll be more confident I'll try to explain better my attempt if still won't succeed!

For what concerns the "syntactic sugar" of "KickAss" 8) , well after lots of try and error I made it work with following approach:

Code: Select all

BasicUpstart2(main) 
#import "screenConstants.asm"
#import "memoryMap.asm" 
  
*=CHARSET_ADDRESS "Charset" 
.import binary "test_big/testimura_CharMapBig - Chars.bin"  

*=CHARSET_ATTRIB_ADDRESS "Charset Attrib" 
.import binary "test_big/testimura_CharMapBig - CharAttribs_L1.bin"  

*=HUD_ADDRESS "test_TITLE" 
.import binary "test_big/testimura_CharMapBig - (8bpc, 40x12) Map.bin"  
  
main: 
    jsr SCREEN_CLEAR

    lda #%11011000   // enable MULTICOLOR - Bit #4: 1 = Multicolor mode on.
    sta $d016
      
     lda #%00011110       
     sta $d018
  
lda #BLUE
sta $d023 //extra color 2

lda #BLACK
sta $d022 //extra color 1
 
lda #RED
sta $d021 //BG color

ldx #00            // Initialize loop counter to 0
ldy #00 //#$192             // Initialize counter for adding $28  //192

jmp loop_start

exit:
    rts
    
.var NUMBER_OF_CHARS =  480
 
loop_start:
  .for (var i = 0; i < NUMBER_OF_CHARS; i++) {
 
   lda HUD_ADDRESS + i  // Load the "ith" char 
    sta $0400 + i  // Screen RAM
    tay // Transfer the number of B to the y-register
    lda CHARSET_ATTRIB_ADDRESS,y
    sta $d800 + i // Color RAM  
  } 
  jmp exit 
Attention: when I've noticed that I would be able to overcome 255 limitation I've decided to use a much bigger charMap made of 40 columns x 12 rows, this explains 480 as char number :wink:

With this i got full control of each char printed on screen, but after going back to suggested approach, I'm start thinking that probably i'm not doing this in most efficient way, maybe considering nested loop Cols\Rows or better way to save iterations\logic...

More important than everything is that I feel now i'm in the right place to learn :D
Programming 6502/6510 is fun. Do note that your version takes quite a bit of memory (the code in the loop is essentially duplicated 480 times). It may or may not pose a problem, but just something to be aware of. I usually use a hybrid solution for loop-unrolling (i.e. instead of having two nested loops i have one loop unrolled), which saves me an index-register to use for other things. But it is always a tradeoff between speed, code size and readability.

Re: Greetings and Question from Newbie!

Posted: Sat Aug 24, 2024 6:34 pm
by teamtempest
Be aware that the VIC-II chip in the C64 can only "see" 16Kb of RAM at one time. Aside from the screen color memory at $D800, everything you want to display has to fit into that 16Kb.

You can re-define the character set and point the VIC-II at your re-defined characters. However, a single character set is limited to 256 8x8 glyphs. If you want to display more than that on a single screen, you may have to re-point the VIC-II "on the fly", ie., using raster interrupts. Once the raster reaches whatever line you want to change character sets at, you'll need to re-point the VIC-II to wherever your additional definitions are. And of course change that back once the raster is off the screen, so the top of the screen uses the first 256 characters again.

Re: Greetings and Question from Newbie!

Posted: Sun Aug 25, 2024 4:48 pm
by teamtempest
...although it has belatedly occurred to me that it another way it might be possible to get beyond the 256-glyph limit would be to re-define not what the VIC-II is pointing at, but the glyphs themselves. Whenever the VIC-II is not "looking" at a particular glyph (ie., in the process of copying it to the output stream), that image could be overwritten with another. The source could be anywhere in the C64's memory, just as long as the character set itself was somewhere the VIC-II could "see" it. Any number of glyphs could be accommodated this way. It would probably still require watching the raster beam to be sure it was safe to overwrite any particular glyph.