Page 1 of 1

Managing X and Y coords in memory best practice

Posted: Mon Jan 08, 2024 1:54 am
by cosmicr
Go easy on me, I'm still learning 65c02 assembly. Okay, so the default screen res with no scaling is 640x480.

You can scale the screen by using the formula

Code: Select all

scale = 128*res / 640
I have been using the common resolution of 320x240x16 colours.

This means that the X values are an array of 320 4-bit values (160 bytes) and the Y is an array of 240 values.

So what is the best practice for storing the X and Y coordinates? It would need 17 bits, (9 for the X, 8 for the Y). I have been using 3 bytes, but it's incredibly annoying to have to do 16-bit maths on the X value every time there is an operation.

It would be great if there was some kind of bit-packing technique or clever shifting trick to allow for using only 2 bytes.

As an example, 8086 (16-bit) assembly has a 20-bit memory address register that only uses 3 bytes (rather than 4), since the high 4 bits set the segment, and the lower 16-bits set the offset. Only a couple of shifting operations needed.

It's obviously not the same, but it would be good to know if someone has already looked at this problem.

Some of the options I have thought of are:
- Reduce the precision of X, to use only 8 bits.
- Reduce the precision of Y, to use 7 bits
- Create two types of "drawing" functions, one where you know X will be less than 256, and another where it will be higher. I think a few older games did this.
- one subroutine, but have it "decide" if X>256 before continuing at the expense of a tiny bit of performance.
- segment the screen into two areas?

Or am I going about this the wrong way altogether?

Re: Managing X and Y coords in memory best practice

Posted: Mon Jan 08, 2024 6:47 am
by TomXP411
This is what macros are for. Just do the work once, then use a macro when you need to repeat it.

So something like

add16: CLC LDA #addend ADC x_coord; add value to low byte of x_coord BCC add16_done inc x_coord+1 ; stash the carry in the high byte of x_coord add16_done:

becomes something like

add16 .macro result, addend CLC LDA #addend ADC result; add value to low byte of x_coord BCC add16_done inc result+1; stash the carry in the high byte of x_coord add16_done: .endmacro

and then you just use it later in your code like
#add16 x_coord, 2

(this is the tass64 syntax. It may be different in other compilers.)

And if you just need to increment a value, it's even simpler:
inc x_coord bne inc16_done inc x_coord+1 inc16_done:

Re: Managing X and Y coords in memory best practice

Posted: Mon Jan 08, 2024 11:55 pm
by cosmicr
Thanks for the reply - unfortunately that's not what I was after, I'm already familiar with macros - in fact they're kind of counterproductive to what I want to achieve.

Sorry I wasn't clear enough. I'm looking for a way to optimise and reduce the amount of memory used, rather than make it easier to code - ie use only 2 bytes for X and Y coordinates. What is the best method for this? Are any of my ideas sound? I presume this is a common problem that has already been solved.

Re: Managing X and Y coords in memory best practice

Posted: Tue Jan 09, 2024 5:48 pm
by TomXP411
The answer is: there is not some kind of bit-packing technique or clever shifting trick to allow for using only 2 bytes. You just have to do the math. Any method of packing (x,y) into a 16-bit value is more complicated than a simple 16 bit add.

If you're just trying to save space, then pack the high bit of 8 X values into a single packed byte.

That basically involves using ROR to get the high bit of X into the Carry flag, then ROL to get it into your pack byte, something like...

CLC
ROR X+1 ;get bit 0 of the high byte of X into the Carry flag
ROL pack_byte ; push the carry bit back in to the pack byte

However, there's no method of packing (320,240) into 2 bytes that isn't hideously more complex than simple 16 bit adds, especially when the high bit if your X coordinate is only 0 or 1.

Re: Managing X and Y coords in memory best practice

Posted: Tue Jan 09, 2024 6:20 pm
by DragWx
Instead of trying to eliminate your X position's 9th bit, try finding a way to repurpose the 7 unused bits of your MSB. For example, if you need flags to store which direction your actor is facing, that's a good place to store them.

Otherwise, if you really want to make your X position 8 bits, you could reduce the size of your playfield to 256x240. On TVs, some of the 320x240 is lost to overscan anyway.

Re: Managing X and Y coords in memory best practice

Posted: Tue Jan 09, 2024 7:22 pm
by Ed Minchau
If you do as DragWx suggests, that's an HSCALE value of $33 to get 256 pixels across the screen. Actually only 255 would be shown, but this means your tile map would only need to be 32 tiles wide.

Otherwise I would just use the 8 most significant bits of the X value. Suppose the high byte is in A and the low byte in X:

LSR A
TXA
ROR A

This puts the 8 most significant bits in A.

Re: Managing X and Y coords in memory best practice

Posted: Wed Jan 10, 2024 2:56 am
by kelli217
I think it might be better to use HSTART and HSTOP for the reduction...

Re: Managing X and Y coords in memory best practice

Posted: Thu Jan 11, 2024 2:48 am
by cosmicr
DragWx wrote: Tue Jan 09, 2024 6:20 pm Instead of trying to eliminate your X position's 9th bit, try finding a way to repurpose the 7 unused bits of your MSB. For example, if you need flags to store which direction your actor is facing, that's a good place to store them.
Thanks that's a good idea - I have a plot pixel macro, I could put the colour in the upper 7 bits (although that would be at the expense of a few extra cycles, so it's a balancing act). I'll have to think about it.

I've been looking at some older source code for VGA MS-DOS games, it looks like most don't worry about using a 16 bit int for the X coordinate. I guess that's because it was at least a 16-bit cpu then.

Re: Managing X and Y coords in memory best practice

Posted: Wed Jan 17, 2024 9:43 pm
by maikaione
I'm not at a computer I can test this on, but here's an idea. Although it seems like a lot of work/cpu time to save 7 bits.

If you imposed a constraint where the x-coord could only increase or decrease by 2 at a time and assume the LSB is always 0, you can free up a bit.

if the true X coord is $0B6 (0000 1011 0110), you could store it as $5B (0101 1011).
If the True coord were 302 ($12E, 0001 0010 1110), you'd store it as $97 (1001 0111)

When you go to send the coordinates to the VERA, do something like this:

Code: Select all

; Assuming the true x coordinate is $B6 so we have $5B stored in XCoord

lda #XCoord			; A = 0101 1011
clc				; Clear the Carry Bit
rol				; 0 < 1011 0110 -- A = $B6
sta $9F23			; Store that in the VERA Data port

lda #0				;lda #0 (Carry flag will still be clear from rol)
bcc :+				; so follow the branch
lda #1				; skip this
:
sta $9F23			; store the next byte of x coord data in the Vera data port. (0 in this case)


and with the other example:

Code: Select all

; Assuming the true x coordinate is $12E so we have $97 stored in XCoord

lda #XCoord			; A = 1001 0111
clc				; Clear the Carry Bit
rol				; 1 < 0010 1110 -- A = $2E
sta $9F23			; Store that in the VERA Data port

lda #0				;lda #0 (Carry flag will still set from the rol)
bcc :+				; so don't branch
lda #1				; load 1 into a
:
sta $9F23			; store the next byte of x coord data in the Vera data port.   (1 in this case)