It is currently Tue Nov 13, 2018 11:29 pm

All times are UTC - 7 hours





Post new topic Reply to topic  [ 26 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Mon Oct 15, 2018 7:59 pm 
Offline
User avatar

Joined: Sun Apr 08, 2018 11:45 pm
Posts: 18
Location: Southern California
I'm working on writing some general-purpose math code. I'm starting with 16 bit unsigned integers, and then signed 16 bit, and then either 16 or 24 bit fixed point. I figured I'd breeze through the u16 stuff, but I've hit a few cases that I'm not sure how to handle.

The first thing I realized is that I don't know how to handle constants. Specifically, I don't know how to write code that can tell if a constant is a single byte vs two bytes and then treat it appropriately. Specifically, I mean declaring a constant like this:
Code:
SMALL_CONSTANT = $FF
LARGER_CONSTANT = $0101
MAX_CONSTANT = $FFFF


The second thing I realized is that it's up to me how to handle overflow / underflow when adding or subtracting numbers. My first thought is to make them "wrap correctly" like I would expect from C, but I'm wondering if that is worth it? It's some extra code, and at the end of the day it just generates a different "incorrect" value. I suppose you could also "clamp" them (underflows just become 0, overflows FFFF or whatever) but that doesn't really feel right. I guess this one is a bit of a value judgement, just curious how more experienced people handle it.

Cheers! :D


Top
 Profile  
 
PostPosted: Mon Oct 15, 2018 8:14 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 6948
Location: Canada
In ca65 expressions are evaluated as 32 bits, but will normally be implicitly converted to 8 or 16 bit size when you try to store them in specific places.

LDA $FF generates a LDA ZP instruction.

LDA $FFFF generates a LDA ABS instruction.

For labels it does that kind of selection based on whether the label value is known during that first pass assembly, or if it was known to belong to a zeropage segment.

You can also use address prefixes a: and z: to have the assembler check and enforce that a value fits before generating the instruction, instead of relying on the automated selection.

The < and > operators will take the low 8 bits or the next 8 bits of the expression (both produce an single byte result).


As for overflow, etc. it's generally the same two's complement as you're used to elsewhere, though there's some extra work attached to signed operations that doesn't apply to unsigned on 6502.

I find I'm constantly recommending this document for reference:
http://www.6502.org/tutorials/compare_beyond.html


Last edited by rainwarrior on Tue Oct 16, 2018 10:11 am, edited 1 time in total.

Top
 Profile  
 
PostPosted: Mon Oct 15, 2018 8:54 pm 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 2329
Location: DIGDUG
Specifically, when you write the assembly line, you want to use a: or zp: or < or > to control how that line will assemble.

Lda #<MAX_CONSTANT
ldx #>MAX_CONSTANT

usually if I wanted an address in the zeropage, I would make a label with .res directives in a zeropage segment. Then references of that label will compile in the zeropage.

You mentioned math. What kind of math are you doing? Pointer math, or just adding 2 16 bit numbers?


Edit. Is it z: or zp: for zeropage addressing? I've seen examples of just using the < symbol and can't seem to find the letter symbol.

_________________
nesdoug.com -- blog/tutorial on programming for the NES


Last edited by dougeff on Tue Oct 16, 2018 3:20 am, edited 1 time in total.

Top
 Profile  
 
PostPosted: Mon Oct 15, 2018 10:05 pm 
Offline

Joined: Tue Feb 07, 2017 2:03 am
Posts: 621
You can't write code to tell if a constant is 16 or 8 bit. The 6502 doesn't have anything but 8 bit, and it is up to the programmer to know if they need to perform a 16bit operation or an 8 bit operation.


Top
 Profile  
 
PostPosted: Tue Oct 16, 2018 7:38 am 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 2329
Location: DIGDUG
So, I'm inclined to think it IS z: for forcing zero page. I couldn't find it in the documents, but comments like this...

https://cc65.github.io/mailarchive/2012-05/10300.html

_________________
nesdoug.com -- blog/tutorial on programming for the NES


Top
 Profile  
 
PostPosted: Tue Oct 16, 2018 8:07 am 
Offline

Joined: Tue Feb 07, 2017 2:03 am
Posts: 621
samophlange wrote:
The second thing I realized is that it's up to me how to handle overflow / underflow when adding or subtracting numbers. My first thought is to make them "wrap correctly" like I would expect from C, but I'm wondering if that is worth it? It's some extra code, and at the end of the day it just generates a different "incorrect" value. I suppose you could also "clamp" them (underflows just become 0, overflows FFFF or whatever) but that doesn't really feel right. I guess this one is a bit of a value judgement, just curious how more experienced people handle it.

Cheers! :D


Normally I ensure that my maths won't overflow, If I'm making code that needs more than 16bits then I should use 24bit maths.. however the functions should return 'C' in the correct state so I can detect an overflow and treat it as needed in the calling code.


Top
 Profile  
 
PostPosted: Tue Oct 16, 2018 9:12 am 
Offline
User avatar

Joined: Thu Apr 23, 2009 11:21 pm
Posts: 940
Location: cypress, texas
Oziphantom wrote:
You can't write code to tell if a constant is 16 or 8 bit. The 6502 doesn't have anything but 8 bit, and it is up to the programmer to know if they need to perform a 16bit operation or an 8 bit operation.
I think tokumaru mentioned that he uses
Code:
lda avariable+0
While the +0 is pointless in code operation, it is super helpful when it is only used after the first byte in multibyte variables... makes it really easy to find sections of code using multibyte variables. :)

In variable declaration using asm6's
Code:
avariable .dsb 2
asmallervariable .dsb 1
it's easy to view that avariable is 16bit, especially when looking at your .lst file... if you are in the middle of the file, and using Windows, just press Ctrl+Home to instantly reach the very first character of the file. Your variable declarations should be near the top. To get back to where I was just have to enter the appropriate 16bit hex code in the Find box of my text editor.

After doing those a bunch it's really easy to know which variables are 8bit and which are 16bit, for me at least. :) Hope this helps. :)


edit: sta avariable+2 would store the accumulator's value into asmallervariable's memory location.


Top
 Profile  
 
PostPosted: Tue Oct 16, 2018 10:14 am 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 6948
Location: Canada
dougeff wrote:
Is it z: or zp: for zeropage addressing? I've seen examples of just using the < symbol and can't seem to find the letter symbol.

I've seen lots of people use < to force zero page addressing, but I don't recommend doing it that way.

z: will force it but also do a range check to make sure the value really fits in 8 bits. < will just silently discard the upper bits (hope they're zero).

So... if you know the value is less than 256, < is equivalent, but a habit of using z: will protect you against error. (e.g. what if you move a variable from ZP to elsewhere later on? with < you have to seek out all uses of it to fix them, with z: they'll produce an error for you.)


Yeah, I think the ca65 documentation neglects to document address prefixes, unfortunately. Someone might submit a ticket (or pull request) about that...


Top
 Profile  
 
PostPosted: Tue Oct 16, 2018 5:54 pm 
Offline
User avatar

Joined: Tue Jun 24, 2008 8:38 pm
Posts: 2036
Location: Fukuoka, Japan
rainwarrior wrote:
I've seen lots of people use < to force zero page addressing, but I don't recommend doing it that way.


I saw that often in older code but the latest ca65 spit warning every time it saw them so I decided to remove such reference in the code samples I borrowed. I found them confusing too since it's the same operator used to extract low/hight byte so maybe this is why it was more or less removed for zp?.


Top
 Profile  
 
PostPosted: Tue Oct 16, 2018 10:21 pm 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 3676
Location: Mountain View, CA
I'm more inclined to say that forcing ZP addressing through syntax semantics is the backwards approach to take -- instead, default to ZP and forcing absolute/16-bit addressing through semantics. Getting a bit off-topic though.

I agree with unregistered's feelings about data byte definition sizes though; 1 vs. 2 makes it pretty obvious. But then again I'm also the "nut" who does the whole
Code:
var1 = $00
var2 = var1+1   ; byte
var3 = var2+1   ; byte
var4 = var3+2   ; word
var5 = var4+1   ; byte

...thing, depending on what I'm writing (*much*more common with disassembled works). *shrug* As covered in some other threads, every person has their own way of doing stuff based on whatever their experience is (both experience with things throughout 65xxx development, as well as experience level itself). I think there's pros and cons to pretty much every method out there, so I try not to harp too much on how others do it. :)

As for < being used to force a ZP address, ex. lda <$12 -- the < operator is doing the exact same thing there as it would be for "extracting the low vs. high byte" for immediates, ex. lda #<somevar. There's nothing special about it with regards to immediates vs. addresses. It's going to pick the low byte of the effective address.


Top
 Profile  
 
PostPosted: Tue Oct 16, 2018 11:22 pm 
Offline

Joined: Tue Feb 07, 2017 2:03 am
Posts: 621
Why are people talking about forcing ZP? I know this is NesDev but this is a giant leap in a single bound. He states and gives examples of CONSTANTS not VARIABLES.


Top
 Profile  
 
PostPosted: Wed Oct 17, 2018 1:00 am 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 6948
Location: Canada
koitsu wrote:
I'm more inclined to say that forcing ZP addressing through syntax semantics is the backwards approach to take -- instead, default to ZP and forcing absolute/16-bit addressing through semantics.

O_o? What 6502 assembler defaults to ZP and requires some explicit mechanism to use 16-bit addressing? I don't really understand how that would even be implemented.


Top
 Profile  
 
PostPosted: Wed Oct 17, 2018 1:09 am 
Offline

Joined: Tue Feb 07, 2017 2:03 am
Posts: 621
I think, and personally I'm also of the option as to why these even needs to exist. He means when you do

*= $02
ZPTemp .byte ?

and then I do

LDA ZPTemp -> A5 02

The assembler just put in a ZP address, I don't need to force it to put in a ZP address, it just puts it in by default. However should I for timing of space reasons NEED a abs version I then do

LDA@w ZPTemp -> AD 02 00

So it defaults to ZP, and I override to ABS when needed. Rather than assume everything is ABS and ZP when somebody explicitly says ZP, to me that is backwards.


Top
 Profile  
 
PostPosted: Wed Oct 17, 2018 10:00 am 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 6948
Location: Canada
NESASM does everything ABS unless a < explicitly indicates ZP, which I would agree is wrong, but I think "backwards" is not the right way to describe this. Making everything ZP unless explicitly marked ABS would be equally wrong.

Actually I think it would be even more wrong. At least ZP instructions will still function the same way but slower if incorrectly assumed as ABS. The opposite is not true. You're now mandating annotations of probably most memory accesses in a program just for it to function?? That's what I mean that I don't understand how this would be implemented.


We're talking about ca65 though. ca65 will reduce anything with known 8-bit sized value to ZP, and assume ABS wherever it is unknown (or known to be larger), but either assumption can be explicitly overridden by z: or a: if needed (generally only rarely). This seems almost ideal to me, except for the fact that ca65 is single pass, so anything you want to auto-assume ZP has to be declared as ZP before the instruction in some way, of which there are many options:
  • Using an explicit value.
  • Placed in a segment that is marked zeropage.
  • < or > operator result is correctly assumed to be byte sized.
  • Imported as a zeropage symbol (.importzp)
  • A z: prefix on use.

In my experience this produces the "right" thing with minimal fuss, as long as you keep the single pass concept in mind when working. With conventions and habits that remember to declare variables above their use, the issue of selecting operand size almost entirely disappears, and the z: and a: prefixes give you safe ways to override it with range checking that will tell you if you made a mistake!


(I don't offhand know how ASM6 does things, and its documentation doesn't seem to explicitly state what it does.)


Anyhow, sorry this seems to be quite a digression. The discussion was talking about appropriate practice for using < or z: etc. because the question was how does ca65 know about sizes of values. What a theoretical assembler should do is kind of a completely different problem.


Top
 Profile  
 
PostPosted: Wed Oct 17, 2018 10:37 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10961
Location: Rio de Janeiro - Brazil
rainwarrior wrote:
(I don't offhand know how ASM6 does things, and its documentation doesn't seem to explicitly state what it does.)

ASM6 uses ZP addressing whenever possible (i.e. address < $0100), and since it's multi-pass, labels don't need to be known before they're used to be ultimately treated as ZP. What ASM6 lacks is a way to force absolute addressing.

Did anyone really suggest ZP addressing being the default, even for addresses above $00FF? I thought they were just ZP addressing should be there default for addresses below $0100, as is the case in most assemblers, NESASM being the notable exception.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 26 posts ]  Go to page 1, 2  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 3 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group