6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sun Nov 24, 2024 7:18 pm

All times are UTC




Post new topic Reply to topic  [ 26 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Sun Nov 05, 2023 7:39 am 
Offline

Joined: Sun Jul 11, 2021 9:12 am
Posts: 155
Hi guys,

I am presently playing with native mode on a W65C816, but am soon running in to issues with 16 bit numbers. I am using the CA65 compiler.

It throws the following error, so it seems the compiler still wants an 8 bit number.

Quote:
Error: Range error (48896 not in [0..255])

Here's the snippet of code I am focusing on, with the last line being the offending one.

Code:
   clc                  ; Enable native 16 bit mode
   xce

   rep #%00110000         ; 16 bit A (bit5), X & Y (bit4) registers
      
   lda #$BF00

The command to compile is being instructed to build for the 65816, and the build is indeed happy with the XCE and REP commands.
Quote:
_ca65 --cpu 65816 --feature labels_without_colons main.asm

Any idea's of what I am missing would be greatly appreciated.


Top
 Profile  
Reply with quote  
PostPosted: Sun Nov 05, 2023 7:58 am 
Offline

Joined: Sun Jul 11, 2021 9:12 am
Posts: 155
Seems to be a compiler issue, as the following works at storing a 16 bit value to RAM perfectly.

Code:
   .byte $A9
   .byte $BF
   .byte $00


Top
 Profile  
Reply with quote  
PostPosted: Sun Nov 05, 2023 8:19 am 
Offline

Joined: Sun Jul 11, 2021 9:12 am
Posts: 155
Hmmm. Looks like you have to pepper .A8, .A16, etc.. throughout your code when you switch between 8 & 16 bit address mode.

Oh well. It works now I guess.


Top
 Profile  
Reply with quote  
PostPosted: Sun Nov 05, 2023 9:47 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10986
Location: England
I think that's normal and unavoidable: the assembler needs to know the machine mode. Bear in mind the place(s) where the machine mode is changed might be quite distant from the place(s) where the sizes of registers is important.


Top
 Profile  
Reply with quote  
PostPosted: Sun Nov 05, 2023 11:01 am 
Offline
User avatar

Joined: Fri Aug 03, 2018 8:52 am
Posts: 746
Location: Germany
yea that's just how ca65 works, it doesn't automaitcally track the m/x flags, Direct Page, Data Bank, or Program Bank values. (which would be a nice to have feature, but it seems pretty complicated to implement, as you basically need to track program execution while assembling)

you need to use the directives to tell it what the state the m/x flags are. it's not as annoying as it sounds since i highly recommend using macros to set the Register widths anyways instead of using raw REP/SEP instructions

here are the ones i use:

Code:
.macro   accu8
   SEP #%00100000
   .A8
.endmacro

.macro   accu16
   REP #%00100000
   .A16
.endmacro

.macro   index8
   SEP #%00010000
   .I8
.endmacro

.macro   index16
   REP #%00010000
   .I16
.endmacro

.macro   ai8
   SEP #%00110000
   .A8
   .I8
.endmacro

.macro   ai16
   REP #%00110000
   .A16
   .I16
.endmacro


though sometimes it's still necessary to directly place the .Ax and .Ix directives, for example i recommend placing them at the start of each function so that the assembler works correctly in case you place some other function above it that uses different register widths.

also the assembler is pretty stingy with operand sizes, since there are no directives to tell it the current Data Bank or Direct Page you have to manually cut addresses down to the right size if you want it to use absolute or direct page addressing modes.
for example:

Code:
some_var1 = $FF00
some_var2 = $06C000

.A16
LDA #$FF00
TCD                         ; Direct Page is now $FF00
accu8
LDA z:some_var1             ; Forced Direct Page Addressing, causes Range error because $FF00 > 255
LDA z:<some_var1            ; "<" returns the Low Byte of the given value, making this work
LDA #$06
PHA
PLB                         ; Data Bank is now $06
LDA a:some_var2             ; Forced Absolute Addressing, casues Range error because $06C000 > 65535
LDA a:.LOWORD(some_var2)    ; ".LOWORD" is pretty self explanatory, not as ellegant as "<" or ">" though, but it works


you can check out ca65's docs on more of these pseudo functions: https://www.cc65.org/doc/ca65-10.html


Top
 Profile  
Reply with quote  
PostPosted: Sun Nov 05, 2023 5:11 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8546
Location: Southern California
I use the Cross-32 (C32) assembler, and instead of having to tell it LONGI ON etc., it just gives 16-bit unless you preface the operand with "<", like LDX #<$20 which lays down A2 20, not A2 20 00.  It makes the code more compact, and, I think, keeps you more mindful of the size you're working in at the moment.  I have the links to get C32 in my links page, but the suppliers are out of commission now on it, so I'm trying to find out if I can distribute it myself.  I'm not out to make any money; I just want to keep a good thing going.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
PostPosted: Sun Nov 05, 2023 5:36 pm 
Offline
User avatar

Joined: Fri Aug 03, 2018 8:52 am
Posts: 746
Location: Germany
on a similar note, Calypsi C's assembler also doesn't require size directives, though it's the opposite of C32 because it defaults to 8-bits and you specify 16-bit immediates by using ##.

so for example "LDA #$10" assembles to "A9 10" while "LDA ##$10" assembles to "A9 10 00".

plus you get a functional C compiler with it so that's nice.


Top
 Profile  
Reply with quote  
PostPosted: Sun Nov 05, 2023 11:15 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8514
Location: Midwestern USA
J64C wrote:
Hmmm. Looks like you have to pepper .A8, .A16, etc.. throughout your code when you switch between 8 & 16 bit address mode.

Minor pedantic note.

65C816 register sizes are not “modes.”  The 816 has two operating modes: emulation and native.  Also note that all addresses emitted by the 816 are 24 bits, whether in emulation or native mode.  It is possible while in emulation mode to access extended memory, i.e., memory beyond $00FFFF.

What you are referring to as “8 & 16 bit address mode” are register sizes or “widths,” as determined by the state of the m (accumulator/memory) and x (index) bits in the status register (SR).  m and x are immutably set when the 816 is operating in emulation mode.

The ca65 assembler has a .smart pseudo-op that causes the assembler to try to track REP and SEP instructions that affect m and x.  This feature should, in theory, always result in correct assembly of immediate-mode-addressing operands.  However, .smart can’t track changes caused by pulling SR from the stack, so it is easy to trip up the assembler and have the wrong-sized operand assembled with immediate-mode-addressing instructions.

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Top
 Profile  
Reply with quote  
PostPosted: Sun Nov 05, 2023 11:29 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8514
Location: Midwestern USA
Proxy wrote:
on a similar note, Calypsi C's assembler also doesn't require size directives, though it's the opposite of C32 because it defaults to 8-bits and you specify 16-bit immediates by using ##.

so for example "LDA #$10" assembles to "A9 10" while "LDA ##$10" assembles to "A9 10 00".

In the Kowalski assembler, as greatly improved by Daryl Rictor, an instruction such as LDA !#$10 will do the same.  I prefer the !# syntax over Calypsi’s ## due to the latter being too easy to accidentally enter during source editing.  !# requires distinctly different keystrokes, making it less likely a typo will introduce a bug into the program.

If an immediate-mode-addressing operand exceeds 8 bits, the Kowalski assembler will, by default, assembly it as a 16-bit quantity.

Daryl also added the capability of telling the assembler how large an address parameter should be during assembly time.  The address is prefixed by \x, where x is 1, 2 or 3 bytes.  The ability to affect the addressing mode this way on a per instruction basis is more flexible than having to use pseudo-ops.

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Top
 Profile  
Reply with quote  
PostPosted: Mon Nov 06, 2023 5:15 am 
Offline
User avatar

Joined: Fri Aug 03, 2018 8:52 am
Posts: 746
Location: Germany
BigDumbDinosaur wrote:
In the Kowalski assembler, as greatly improved by Daryl Rictor, an instruction such as LDA !#$10 will do the same. I prefer the !# syntax over Calypsi’s ## due to the latter being too easy to accidentally enter during source editing. !# requires distinctly different keystrokes, making it less likely a typo will introduce a bug into the program.

true i guess, but when you make mistakes like that it shouldn't be that difficult to spot if you use comments and such to note what registers should be what width every now and then.

BigDumbDinosaur wrote:
The ca65 assembler has a .smart pseudo-op that causes the assembler to try to track REP and SEP instructions that affect m and x.

oops, forgot that exists. but also note that it doesn't make the assembler follow program flow, so even when you're not using any PHP/PLP instructions it could still easily be wrong if you purely rely on .smart due to the order in which the assembler does it's work.

BigDumbDinosaur wrote:
65C816 register sizes are not “modes.” The 816 has two operating modes: emulation and native

now i'm interested, why not? i'd say a mode is defined as something that changes the way some subset of instructions are fetched and/or executed. all of those line up with the register widths as they effect almost every memory accessing instruction.

the 6502/65C02 already has 2 modes, the D flag for either Binary or Decimal Addition/Subtraction. it's even called "Decimal Mode".
so even when you exclude the register widths, the 65816 would have atleast 4 operating modes: Emulation Binary, Emulation Decimal, Native Binary, and Native Decimal.
Bruce Clark's page on the 65816 includes the register widths as modes, so he's saying the CPU has a total of 10 different modes: http://www.6502.org/tutorials/65c816opcodes.html#6.10.3


Top
 Profile  
Reply with quote  
PostPosted: Mon Nov 06, 2023 6:57 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8546
Location: Southern California
Proxy wrote:
BigDumbDinosaur wrote:
65C816 register sizes are not “modes.” The 816 has two operating modes: emulation and native

now i'm interested, why not? i'd say a mode is defined as something that changes the way some subset of instructions are fetched and/or executed. all of those line up with the register widths as they effect almost every memory accessing instruction.

I'd say it's because changing the register size does not change the way instructions behave, only the size of the data operated on.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
PostPosted: Mon Nov 06, 2023 8:26 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8514
Location: Midwestern USA
Proxy wrote:
BigDumbDinosaur wrote:
In the Kowalski assembler, as greatly improved by Daryl Rictor, an instruction such as LDA !#$10 will do the same. I prefer the !# syntax over Calypsi’s ## due to the latter being too easy to accidentally enter during source editing. !# requires distinctly different keystrokes, making it less likely a typo will introduce a bug into the program.

true i guess, but when you make mistakes like that it shouldn't be that difficult to spot if you use comments and such to note what registers should be what width every now and then.

I agree commentary helps to reconcile the code with the thoughts that put it there.  However, it won’t ferret out typos until after you’ve assembled the program, put it on the target system and have crashed the machine due to a mismatch between a register’s width and the expected data.  Been there, done that many times!  :oops:  The commentary always seems helpful after the crash-and-burn has occurred.  :D

My opinion is using ## to tell the assembler to assemble a 16-bit operand was not a good choice, and opens the door to creating an insidious bug.  That is one of a number of reasons I don’t use the ca65 assembler.

Quote:
BigDumbDinosaur wrote:
The ca65 assembler has a .smart pseudo-op that causes the assembler to try to track REP and SEP instructions that affect m and x.

oops, forgot that exists. but also note that it doesn't make the assembler follow program flow, so even when you're not using any PHP/PLP instructions it could still easily be wrong if you purely rely on .smart due to the order in which the assembler does it's work.

I so much as said so.  :wink:  Decades of experience in writing assembly language programs has taught me to be cautious about letting the assembler do too much thinking for me.  The ca65 assembler, unfortunately, can be made to do exactly that and quickly lead the unwary code jockey down a deep rabbit hole.

Quote:
BigDumbDinosaur wrote:
65C816 register sizes are not “modes.” The 816 has two operating modes: emulation and native

now i'm interested, why not? i'd say a mode is defined as something that changes the way some subset of instructions are fetched and/or executed. all of those line up with the register widths as they effect almost every memory accessing instruction.

As Garth noted, register widths have no effect on how instructions behave.  Also, register widths have no effect on how the 816 loads instruction operands that are addresses or relative offsets.

When operating in native mode, the 816 always processes 16-bit quantities without regard to register widths, a fundamental characteristic resulting from having a 16-bit ALU.  As Garth also noted, register width determines whether a byte or a word will be fetched from or stored to memory, transferred to or from the ALU, copied from register to register (with exceptions, e.g., TSC) and how an instruction will affect the status register.

Quote:
the 6502/65C02 already has 2 modes, the D flag for either Binary or Decimal Addition/Subtraction. it's even called "Decimal Mode".

Those are computation modes that technically are not modes at all, since they only affect the result computed by two instructions.  Addition or subtraction is internally carried out on binary quantities and then if the d bit is set, a “decimal adjust” operation is carried out in the ALU on the computed value before it is transferred back to the accumulator.¹  You can see this behavior by running the following program (works on the 816 in either mode).

Code:
;BCD addition on binary values
;
         sed
         clc
         lda #$0e              ;not a valid BCD value
         adc #$0f              ;also not a valid BCD value
         cld
         brk

The result will be a nonsense value caused by the decimal adjust step.  A little thinking about what the sum would have been without decimal adjust will make it apparent that addition was on binary values.

Quote:
Bruce Clark's page on the 65816 includes the register widths as modes, so he's saying the CPU has a total of 10 different modes

I completely disagree with Bruce on that and in fact, think that description of the 816 is potentially confusing to someone new to the processor.  Such confusion may well scare off that newbie with the thought that the 816 is “too complicated.”

I’ll reiterate: the 816 has two modes or, if you will, “personalities:” emulation and native.

————————————————————————————————————————————
¹The decimal adjust circuitry in the 6502 design is one of Bill Mensch’s many patents.

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Top
 Profile  
Reply with quote  
PostPosted: Mon Nov 06, 2023 9:23 am 
Offline

Joined: Sun Jul 11, 2021 9:12 am
Posts: 155
GARTHWILSON wrote:
Proxy wrote:
BigDumbDinosaur wrote:
65C816 register sizes are not “modes.” The 816 has two operating modes: emulation and native

now i'm interested, why not? i'd say a mode is defined as something that changes the way some subset of instructions are fetched and/or executed. all of those line up with the register widths as they effect almost every memory accessing instruction.

I'd say it's because changing the register size does not change the way instructions behave, only the size of the data operated on.


But changing the width does change how the instructions behave, including the clock cycles required to achieve the task. And the fact that you have to switch processor flags along with it.

If they had used a different opcode for the 8 and 16 bit operations, then I’d agree that there is no ‘mode’ change. But the fact they used the same opcodes and you have to prewarn the CPU what types you are dealing with certainly seems like a mode change to me.


Top
 Profile  
Reply with quote  
PostPosted: Mon Nov 06, 2023 10:20 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8546
Location: Southern California
It's really just a matter of doing the same thing, in the same way, just on a different size of data.  Flag results are derived the same way too.  The number of clock cycles will change even with things like if you do a direct-page addressing mode when the direct page is not page-aligned.  Similarly, the '02 takes an extra cycle for branches that cross a page boundary, or index results that cross a page boundary.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
PostPosted: Mon Nov 06, 2023 10:31 am 
Offline

Joined: Tue Sep 03, 2002 12:58 pm
Posts: 336
Proxy wrote:
BigDumbDinosaur wrote:
65C816 register sizes are not “modes.” The 816 has two operating modes: emulation and native


now i'm interested, why not? i'd say a mode is defined as something that changes the way some subset of instructions are fetched and/or executed. all of those line up with the register widths as they effect almost every memory accessing instruction.


WDC appear to agree with you. BDD is the only person I've noticed expressing an opinion in the opposite direction.

65816 datasheet, section 2.8:
Quote:
The Decimal (D), IRQ Disable (I), Memory/Accumulator (M), and Index (X) bits are used as mode select flags

They use the same language for E.

I would dispute that register widths have no effect on how instructions behave. Some instructions change length. Most of them change behaviour: ADC is either doing an 8 bit or 16 bit add, one or two bytes in memory will be written, indexed addressing modes are using either 8 bits or 16 bits of the index. It's a bigger change than decimal mode, and we've been calling that decimal mode forever.

I suspect the confusion comes from trying to see the whole processor being in a single mode at a time. That's not how it works, and it's not a helpful way to look at it. The processor is made (conceptually, at least) from a number of sub-systems, and some of these have mode flags that control their behaviour. They are mostly independent choices affecting single sub-systems, but there is some overlap in the case of E (which is an ugly mess).


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

All times are UTC


Who is online

Users browsing this forum: No registered users and 5 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: