Custom char issue in 6502

Basic and Machine Language

Moderator: Moderators

User avatar
Daroou
Vic 20 Newbie
Posts: 4
Joined: Mon Jan 18, 2021 5:26 am
Location: UK

Custom char issue in 6502

Post by Daroou »

Hi All,

First post wooo!

I decided to learn some 6502 in my spare time, and opted to try the Vic20 for a couple of reasons.

1. I used to have one when I was 7 (40 *cough* years ago),
2. I'm curious what I can cram into an unexpanded box,
3. It's an elegant and basic machine so I want to be able to focus more on the asm than the feature set.

So this is one of my first programs, to show a custom 'A' on the screen. Works. But I can't access the usual characters by adding #128 or #168 (128+40) to the message byte values, which afaict, I should be able to.

I've been through the whole forum, reading up on character sets, and have looked through a couple of books but these are all in basic, so maybe I've missed some vital equivalent of a POKE statement to set things up properly?

Would really appreciate someone with more experience than I to cast an eye over and see what I've done (or not!). I use ACME assembler.

Bw
D

Code: Select all

;  Char test

* = $1001

; BASIC program to boot the machine language code
   !byte  $0b, $10, $0a, $00, $9e, $34, $31, $30, $39, $00, $00, $00

         jmp   init
init
         strptr = $fa   ; string pointer zp

         ldx   #$ff     ; set custom character range for $1c00
         stx   $9005    ; starts at '@', other chars +128 for usual letters

         lda   #<msg    ; Get msg ptr
         sta   strptr
         lda   #>msg
         sta   strptr+1

         ldy   #0
         jmp   strt

loop
      ;  clc            ; nomatter what I put here doesn't show the ROM letters
      ;  adc   #128     ; I tried 128 & 168 (128+40) 
         jsr   $ffd2    ; print char value in accumulator
         iny

strt
         lda   (strptr),y
         bne   loop        ; loops until byte 0 encountered
         rts

msg      !byte $01, $02, $03, $00 

;new letter a, this works
* = $1c08
letA     !byte $00, $00, $7c, $42, $7e, $42, $42, $0
Last edited by Daroou on Mon Jan 18, 2021 9:58 am, edited 1 time in total.
User avatar
vicist
Vic 20 Afficionado
Posts: 355
Joined: Tue Oct 09, 2012 5:26 am
Location: Sheffield, UK

Re: Custom char issue in 6502

Post by vicist »

Hi Daroou,
welcome to the forum.

$FFD2 prints characters to the screen so 'a' would be 65 not 1.
Adding 128 would only shift the character. The ROM characters are accessed by inverse printing them. Printing chr$(18) before the characters would do the trick.

E.G.
msg !byte $12, $41, $42, $43, $00
User avatar
Daroou
Vic 20 Newbie
Posts: 4
Joined: Mon Jan 18, 2021 5:26 am
Location: UK

Re: Custom char issue in 6502

Post by Daroou »

Thanks, Vicist, you're a star - that's the bit of info I needed!

Looks like I need a list of CHR$ codes but I should be able to source them myself. :D
User avatar
Mike
Herr VC
Posts: 4976
Joined: Wed Dec 01, 2004 1:57 pm
Location: Munich, Germany
Occupation: electrical engineer

Re: Custom char issue in 6502

Post by Mike »

You're confusing screen codes with PETSCII.

PRINT and KERNAL CHROUT ($FFD2) use PETSCII encoding; when you poke values into screen RAM, those are the screen codes - and the latter are where you add 128 to get the reverse codes (and access the normal, non-inverted charset with POKE 36869,255). With PRINT and JSR $FFD2, you'd instead 'bracket' the characters with the {RVS ON} and {RVS OFF} control characters.
Daroou wrote:ooks like I need a list of CHR$ codes but I should be able to source them myself. :D
The tables are in the VIC-20 User's manual. See here: DLH's Commodore Archive - Commodore - Books - VIC: VIC-20 Personal Computing Guide


... oh, and welcome to Denial! :mrgreen:
User avatar
AndyH
Vic 20 Afficionado
Posts: 410
Joined: Thu Jun 17, 2004 5:51 am
Website: https://www.hewco.uk
Location: UK
Occupation: Developer

Re: Custom char issue in 6502

Post by AndyH »

Welcome back to the Vic 20, you've picked the right (and a fine) machine to do it with. No bias here, just fact. ;)

There is so much to discover and less hinderence by hardware design of later models. The Vic has its own quirks of course but that's what makes it fun.

BTW, if you just want to put characters on the screen you can use sta from $1e00 on the unexpanded Vic rather than call CHROUT.
--
AndyH
HEWCO | Vic 20 blog
User avatar
Daroou
Vic 20 Newbie
Posts: 4
Joined: Mon Jan 18, 2021 5:26 am
Location: UK

Re: Custom char issue in 6502

Post by Daroou »

Thanks for the welcome Mike and Andy! Yes it's been many years since I owned one of these, worked my way through C64, ST, and Amiga until arriving at the not-much-fun-but-sort-of-necessary PC. :)
You're confusing screen codes with PETSCII.
So, if I divert from the kernal routines I'll have to switch to char codes directly?
If you just want to put characters on the screen you can use sta from $1e00 on the unexpanded Vic rather than call CHROUT.
Hrm. I'm using CHROUT for quickness posting strings to the screen, are there any advantages to using sta? If I do that, I'll have to keep track where I am in screen RAM and identify each character's position won't I?

D.
User avatar
AndyH
Vic 20 Afficionado
Posts: 410
Joined: Thu Jun 17, 2004 5:51 am
Website: https://www.hewco.uk
Location: UK
Occupation: Developer

Re: Custom char issue in 6502

Post by AndyH »

Many ways you can do it, but based on the code I see above you could do something like this (not tested):

Code: Select all

        strptr = $fa   ; string pointer zp
        scrptr = $fc   ; screen pointer zp

... stuff ...

        ; set address in pointer
        lda #$00
        sta scrptr
        lda #$1e
        sta scrptr+1

... more stuf ...

         ldy #0
loop
         lda   (strptr),y
         sta   (scrptr),y
         iny
         cpy #10  ; length of your string
         bne   loop 
I tend to dip my toe in 6502 assembler when needed, as I mostly use Turbo Rascal, so I'm sure someone can suggest a more optimised way to do the loop.
--
AndyH
HEWCO | Vic 20 blog
User avatar
Mike
Herr VC
Posts: 4976
Joined: Wed Dec 01, 2004 1:57 pm
Location: Munich, Germany
Occupation: electrical engineer

Re: Custom char issue in 6502

Post by Mike »

Daroou wrote:I'm using CHROUT for quickness posting strings to the screen, are there any advantages to using sta?
CHROUT puts quite some overhead for maintaining the cursor position and logical vs. physical screenlines. It also keeps colour RAM in sync. If you write directly to screen RAM this is nearly two orders of magnitude faster, but you have to keep in mind you will have to update the corresponding position in colour RAM as well - after a screen clear, the KERNAL defaults all colour RAM cells to white(!), irrespective of the current background or foreground colour.

I many cases using CHROUT is not time critical though, and I use both methods in my own programs where applicable. You might take a look at my VICMON primer for examples. There, my m/l port of "Killer Comet" also features user defined characters. :)
User avatar
chysn
Vic 20 Scientist
Posts: 1204
Joined: Tue Oct 22, 2019 12:36 pm
Website: http://www.beigemaze.com
Location: Michigan, USA
Occupation: Software Dev Manager

Re: Custom char issue in 6502

Post by chysn »

Mike wrote: Thu Jan 21, 2021 4:23 amyou have to keep in mind you will have to update the corresponding position in colour RAM as well - after a screen clear, the KERNAL defaults all colour RAM cells to white(!), irrespective of the current background or foreground colour.
This was a terrible way to save one byte in the BASIC ROM!

A good way to sync characters and colors is by adding the difference between the high bytes of the color and character addresses to a set of zero page pointers. So for unexpanded VIC, this is $9600 - $1e00 = $7800.

In my current game, I'm syncing colors and characters with something like this:

Code: Select all

ldx #$00
sta (CH_PTR,x)
and #$07
tax
lda #$78
clc
adc CH_PTR+1
sta CO_PTR+1
lda CH_PTR
sta CO_PTR
txa
ldx #$00
sta (CO_PTR,x)
You may notice that the actual color code comes from the lowest three bits of the character code (and #$07)! So when I designed my character set, I also considered what colors my characters were going to start at, allowing the character code itself to pull double-duty. This was a nice savings for a game that wound up using every single byte.
VIC-20 Projects: wAx Assembler, TRBo: Turtle RescueBot, Helix Colony, Sub Med, Trolley Problem, Dungeon of Dance, ZEPTOPOLIS, MIDI KERNAL, The Archivist, Ed for Prophet-5

WIP: MIDIcast BASIC extension

he/him/his
wimoos
Vic 20 Afficionado
Posts: 352
Joined: Tue Apr 14, 2009 8:15 am
Website: http://wimbasic.webs.com
Location: Netherlands
Occupation: farmer

Re: Custom char issue in 6502

Post by wimoos »

In the kernal, the following code is used. Why not use that ?

Code: Select all

;***********************************************************************************;
;
; calculate pointers to screen lines colour RAM

LAB_EA6E
	JSR	LAB_EAB2		; calculate pointer to current screen line colour RAM
	LDA	LAB_AC		; get next screen line pointer low byte
	STA	LAB_AE		; save next screen line colour RAM pointer low byte
	LDA	LAB_AD		; get next screen line pointer high byte
	AND	#$03			; mask 0000 00xx, line memory page
	ORA	#$94			; set  1001 01xx, colour memory page
	STA	LAB_AF		; save next screen line colour RAM pointer high byte
	RTS

and

Code: Select all

;***********************************************************************************;
;
; calculate pointer to colour RAM

LAB_EAB2
	LDA	LAB_D1		; get current screen line pointer low byte
	STA	LAB_F3		; save pointer to colour RAM low byte
	LDA	LAB_D2		; get current screen line pointer high byte
	AND	#$03			; mask 0000 00xx, line memory page
	ORA	#$94			; set  1001 01xx, colour memory page
	STA	LAB_F4		; save pointer to colour RAM high byte
	RTS
VICE; selfwritten 65asmgen; tasm; maintainer of WimBasic
User avatar
Daroou
Vic 20 Newbie
Posts: 4
Joined: Mon Jan 18, 2021 5:26 am
Location: UK

Re: Custom char issue in 6502

Post by Daroou »

Wow go easy on me I only just started 6502! :D

Lots to consider here, I shall digest. Thanks very much for all your input!
User avatar
chysn
Vic 20 Scientist
Posts: 1204
Joined: Tue Oct 22, 2019 12:36 pm
Website: http://www.beigemaze.com
Location: Michigan, USA
Occupation: Software Dev Manager

Re: Custom char issue in 6502

Post by chysn »

wimoos wrote: Fri Jan 22, 2021 3:11 am

Code: Select all

	LDA	LAB_D2		; get current screen line pointer high byte
	AND	#$03			; mask 0000 00xx, line memory page
	ORA	#$94			; set  1001 01xx, colour memory page
	STA	LAB_F4		; save pointer to colour RAM high byte
See, this is why I love Denial. Wimoos, you've just changed my life. I've been doing arithmetic like a schmuck this whole time!

Code: Select all

; Set Character and Color
; Write character in A to screen memory specified by ZP pointer
; Set corresponding color memory to color Y
; (Unexpanded VIC)
SetChCo: ldx #$00
         sta (ZP,x)
         jsr flip
         tya
         sta (ZP,x)
flip:    lda ZP+1
         eor #$88
         sta ZP+1
         rts
Edit: I made the routine above more general by setting color in Y
Last edited by chysn on Sun Jan 24, 2021 9:45 am, edited 2 times in total.
VIC-20 Projects: wAx Assembler, TRBo: Turtle RescueBot, Helix Colony, Sub Med, Trolley Problem, Dungeon of Dance, ZEPTOPOLIS, MIDI KERNAL, The Archivist, Ed for Prophet-5

WIP: MIDIcast BASIC extension

he/him/his
User avatar
Mike
Herr VC
Posts: 4976
Joined: Wed Dec 01, 2004 1:57 pm
Location: Munich, Germany
Occupation: electrical engineer

Re: Custom char issue in 6502

Post by Mike »

chysn wrote:This was a terrible way to save one byte in the BASIC ROM!
More like "Why would anybody want to use something other than white as background and let's make sure people have to define the colour RAM value explicitly" ... ;)

IMO, that's the same mindset that resulted in the Y2K problem. Not the need to save memory in first place, but people simply took the leading "19" century for granted. Witness the kind of programs that output "19100" when the year 2000 was reached. :shock:

BTW, the Rev. 1 KERNAL of the C64 also defaults to white. People got slighty upset when the Rev. 2 then defaulted to background, rendering many displays invisible (the programs relied on the white default). In Rev. 3, CBM then made the foreground colour the default - which surely is a sensible choice. But then, people again 'forgot' the practice to sync the colour RAM cells, and there are still C64s with Rev. 2 KERNALs around ...

Really cool would have been a Rev. 4 KERNAL that initialised the colour RAM to random values. ;) That would have taught people to do the Right Thing™.
In my current game, I'm syncing colors and characters with something like this: [...]
I'd also use two ZP pointers for this, fetched from 2 tables containing the low- and highbytes of the screen lines. The address mode (),Y then accesses column Y within each line. Only call 'SetRow' as needed:

Code: Select all

.SetRow
 LDA line_lo,X
 STA zp_scrn
 STA zp_cram
 CLC
 LDA line_hi,X
 STA zp_scrn+1
 ADC #(38400-7680) DIV 256
 STA zp_cram+1
 RTS

 [...]
 STA (zp_scrn),Y

 [...]
 STA (zp_cram),Y
This inverses the roles of X and Y somewhat (X: row, Y: column), but that's no show stopper. Replace "38400" and "7680" with any useful DEFINEs to highlight their use as magic constants. ;)

With the pointers in ZP set for row X and Y being the running columns, that's a single STA now for both text and colour RAM.

Consequently, I keep those KERNAL routines at $EA6E and $EAB2 out of this. First, they're fixed to the 22 column layout. Second, I don't like to throw wrenches into the gearbox as the KERNAL uses all those ZP addresses to maintain the current cursor position - with any conceivable and unintended side-effects to standard KERNAL I/O. Last, I'd likely have to copy the X,Y cursor position into those given ZP addresses first, which isn't necessary if own code can work directly on the values it keeps in workspace.


Edit: for tile-based output, I once gave another example here: Tile draw code optimization help. That routine actually uses the 'natural' ordering of X and Y, adds in the X register while calculating the origin of the tile on screen and then uses Y to quickly add all necessary offsets:

Code: Select all

; ***************************** 
; *  Draw 2x2 Tile on screen  * 
; *  A = Tile Number          * 
; *  X = Column 0-20          * 
; *  Y = Row 0-21             * 
; ***************************** 

 STA tile_ptr
 LDA #0
 ASL tile_ptr:ROL A
 ASL tile_ptr:ROL A
 STA tile_ptr+1
 CLC
 LDA #tile_base MOD 256:ADC tile_ptr  :STA tile_ptr
 LDA #tile_base DIV 256:ADC tile_ptr+1:STA tile_ptr+1

 CLC
 TXA   :ADC line_table_lo,Y:STA scrn_ptr  :STA col_ptr
 LDA #0:ADC line_table_hi,Y:STA scrn_ptr+1:ADC #(col_base-scrn_base)DIV256:STA col_ptr+1

 LDY #0:LDA (tile_ptr),Y:        STA (scrn_ptr),Y:LDA #0:STA (col_ptr),Y
 INY   :LDA (tile_ptr),Y:        STA (scrn_ptr),Y:LDA #0:STA (col_ptr),Y
 INY   :LDA (tile_ptr),Y:LDY #22:STA (scrn_ptr),Y:LDA #0:STA (col_ptr),Y
 LDY #3:LDA (tile_ptr),Y:LDY #23:STA (scrn_ptr),Y:LDA #0:STA (col_ptr),Y
 RTS
The 4 instances of "LDA #0:STA (col_ptr),Y" should be replaced by "LDA zp_colour:STA (col_ptr),Y" so arbitrary colours can be set.
MartinC
Vic 20 Drifter
Posts: 34
Joined: Tue Oct 25, 2022 12:18 pm
Website: https://winterfam.co.uk
Location: Kent,uk
Occupation: Author

Re: Custom char issue in 6502

Post by MartinC »

Hi all,

The following code snippet quoted from above:

Code: Select all

; Set Character and Color
; Write character in A to screen memory specified by ZP pointer
; Set corresponding color memory to color Y
; (Unexpanded VIC)
SetChCo: ldx #$00
         sta (ZP,x)
         jsr flip
         tya
         sta (ZP,x)
flip:    lda ZP+1
         eor #$88
         sta ZP+1
         rts
Is what I need to do in a routine I've written. But I have questions...

1) How does the eor #$88 flip the high byte of the ZP pointer to the colour ram? Why $88?
2) After the flip what is ZP now pointing to? It's value hasn't changed, has it?

I'm using sta ($fb),y in my routine to output my character loaded from memory one byte at a time - could I use
the same technique to calculate the colour ram address and set it using say, sta ($f7),y

Thanks for humouring me. I'm studying hard and not finding this language easy :-)
User avatar
chysn
Vic 20 Scientist
Posts: 1204
Joined: Tue Oct 22, 2019 12:36 pm
Website: http://www.beigemaze.com
Location: Michigan, USA
Occupation: Software Dev Manager

Re: Custom char issue in 6502

Post by chysn »

MartinC wrote: Fri Feb 24, 2023 12:44 pm 1) How does the eor #$88 flip the high byte of the ZP pointer to the colour ram? Why $88?
2) After the flip what is ZP now pointing to? It's value hasn't changed, has it?
This is actually really elegant. Look at the result of $1E EOR'd with $88, in binary:

Code: Select all

  0001 1110 $1E (start of screen memory high byte, unexpanded)
⊕ 1000 1000 $88
  -------------
  1001 0110 $96 (result is start of color memory high byte, unexpanded)
So you flip your ZP pointer to corresponding color memory with the JSR, and then on return it sets the color, then runs the flip again by falling through to flip.

Code: Select all

  1001 0110 $96 (start of color memory, unexpanded)
⊕ 1000 1000 $88
  -------------
  0001 1110 $1E (result is start of screen memory, unexpanded)
After two EORs with the same operand, the pointer is returned to its starting value.

The screen address high byte could also be $1F, in which case it goes to $97 and back again.

Incidentally, to answer the question of "Why $88?" more concisely, you can obtain the value for toggling between any two numbers by (you guessed it!) EORing those two numbers.

Code: Select all

lda #$1e
eor #$96 ; A = $88
Last edited by chysn on Sat Feb 25, 2023 11:58 am, edited 1 time in total.
VIC-20 Projects: wAx Assembler, TRBo: Turtle RescueBot, Helix Colony, Sub Med, Trolley Problem, Dungeon of Dance, ZEPTOPOLIS, MIDI KERNAL, The Archivist, Ed for Prophet-5

WIP: MIDIcast BASIC extension

he/him/his
Post Reply