tricks to avoid imm v abs/zp addressing bugs in ca65?

Programming the 6502 microprocessor and its relatives in assembly and other languages.
User avatar
Proxy
Posts: 746
Joined: 03 Aug 2018
Location: Germany

Re: tricks to avoid imm v abs/zp addressing bugs in ca65?

Post by Proxy »

Tbh I personally don't think you should be using raw hex addresses in the first place. Always use symbols, makes the code more readable, allows you to change addresses without modifying hundreds of lines of code, and can also help in this case to distinguish between addressing modes.

Just compare:

Code: Select all

LDA #$6F
STA $6F00
With this:

Code: Select all

variable = $6F00

LDA #$6F
STA variable
Though ideally even specific immediate values should be symbols.
User avatar
GARTHWILSON
Forum Moderator
Posts: 8773
Joined: 30 Aug 2002
Location: Southern California
Contact:

Re: tricks to avoid imm v abs/zp addressing bugs in ca65?

Post by GARTHWILSON »

In the cases of hardware addresses of I/O ICs, you'll usually want to just give the base address of the IC, for example $6000, and then make each register be the base address plus the offset.  If you change your address decoding, you'll only need to change the one line telling the base address.  You could have for example,

Code: Select all

;                           +----------------------+
;                           |   General Equates    |
;                           +----------------------+

ACIA:       EQU  $9000    ; The base address of the 6551 Asynchronous Communications Interface Adapter is $9000.
ACIA_DATA:  EQU  ACIA+0   ; Its data I/O register is at $9000.
ACIA_STAT:  EQU  ACIA+1   ; Its  status  register is at $9001.
ACIA_COMM:  EQU  ACIA+2   ; Its command  register is at $9002. 
ACIA_CTRL:  EQU  ACIA+3   ; Its control  register is at $9003.

VIA:        EQU  $A000    ; The base address of the 6522 Versatile Interface Adapter is $A000.
PB:         EQU  VIA      ; Its port B is at that address.
PA:         EQU  VIA+1    ; Its port A is at address $A001.
DDRB:       EQU  VIA+2    ; Its data-direction register for port B is at $A002.
DDRA:       EQU  VIA+3    ; Its data-direction register for port A is at $A003.
T2CL:       EQU  VIA+8    ; Its timer-2 counter's low  byte is at $A008.
T2CH:       EQU  VIA+9    ; Its timer-2 counter's high byte is at $A009.
SR:         EQU  VIA+10   ; The shift register is at $A00A.
ACR:        EQU  VIA+11   ; The auxiliary  control register is at $A00B.
PCR:        EQU  VIA+12   ; The peripheral control register is at $A00C.
IFR:        EQU  VIA+13   ; The interrupt  flag  register is at $A00D.
IER:        EQU  VIA+14   ; The interrupt enable register is at $A00E.

(Note that I did not assign anything for addresses $A004 through $A007, which was because I didn't use those registers in the project I copied this from.  Note also that the addresses are in hex, with the dollar sign to tell the assembler that, but the offsets are in decimal.  You can leave it in hexadecimal all the time if you like, with an assembler directive like RADIX HEX (although it might be different on yours—you'll have to check the manual), but then if you want to use a decimal number, you might have to express it something like D'231' since D231 and 231D are valid hex numbers.  It's probably best to leave it in decimal and specify hex with the leading "$" or trailing "H".

Also, let the assembler assign the variable addresses.  As long as they fit in the allotted space (like ZP, or a portion of it), the programmer shouldn't care what they are.  Just give them names that are meaningful to humans, and let the assembler substitute-in the appropriate address.

Another thing I do is use macros to shorten the code, reduce errors, make it more readable, more maintainable, etc.  So for the above example

Code: Select all

   LDA  #$6F
   STA  variable

I might have instead,

Code: Select all

   PUT  $6F, in, variable
The # is not needed because that's already what PUT is about.  If I wanted to copy the contents of ZP address $6F, I'd use COPY instead of PUT:

Code: Select all

   COPY  $6F, to, variable
(although the $6F address should also have a name that's meaningful to humans).

For a 16-bit variable, it could be something like,

Code: Select all

   PUT2  $6F14, in, variable

which is only one line instead of four.  The macro definition has conditional assembly such that if the two bytes are the same, the value in only loaded once; and if a byte is 00, STZ is used, avoiding the LDA #0.  If the accumulator is not available but X is, you could have something like

Code: Select all

   PUT  $6F, in, variable, using_X

I feel myself wanting to delve further into the use of macros to make the errors less likely; but I've probably gone off-topic far enough already.

Welcome gilhad.  Do what works for you; but those colors are awfully hard for me to read.
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?
User avatar
gilhad
Posts: 85
Joined: 26 Jan 2024
Location: Prague; Czech Republic; Europe; Earth
Contact:

Re: tricks to avoid imm v abs/zp addressing bugs in ca65?

Post by gilhad »

GARTHWILSON wrote:
Welcome gilhad.  Do what works for you; but those colors are awfully hard for me to read.
Thank you, GARTHWILSON. I have read many of your pages, and they were very inspiring for me, leading me here. I will write a longer introduction later in the appropriate section when I have more time.

The colors are just my personal settings and serve me well, but I may change them a little after some time of use. I've included them here as an illustration of the principle: using different colors for different addressing modes helps spot missing or misplaced '#' easily. I assume everyone has their preferred colors (bolds, inverse, or whatever else) and will set their preferred editor to their liking.

I have read about not posting poorly colored schematics and preferentially using black and white, as it is a common ground. I will use BW for my schematics as well. The previous illustration was just to show that there is a less intensively used tool that may help if used creatively. :D
User avatar
BigEd
Posts: 11463
Joined: 11 Dec 2008
Location: England
Contact:

Re: tricks to avoid imm v abs/zp addressing bugs in ca65?

Post by BigEd »

Indeed, welcome! I like the idea of getting the editor to help, by one form or another of syntax highlighting. I can imagine bold or inverse, or even italic, might help.
User avatar
BigDumbDinosaur
Posts: 9425
Joined: 28 May 2009
Location: Midwestern USA (JB Pritzker’s dystopia)
Contact:

Re: tricks to avoid imm v abs/zp addressing bugs in ca65?

Post by BigDumbDinosaur »

Amplifying what Garth posted (which was an expansion of something I had earlier posted), most of the time I let the assembler do the work for me by not getting too specific on things such as defining run-time variables.  Also, as Garth noted, macros can be your friend in helping you to avoid silly errors.

For example, I use a macro called res (reserve) to allocate run-time variable space, e.g., res s_timet, which allocates space for a binary sequential time variable.  res s_timet never goofs, but typing *=*+6 to allocate variable space opens the door to an error that may be difficult to catch if I fat-finger the numeric keypad during code entry.  Furthermore, if I mistype s_timet, the assembler will complain and refuse to assemble the program statement.

In another example, in much of my software, parameter-passing into called functions is through the stack.  Some functions may require as many as five parameters, a mixture of pointers and constants, on the stack, something that is ripe for entry errors.  Use of macros helps to make sure a stack frame is properly organized and populated.

As does Garth, I set a base address for any hardware device, e.g., a UART, and then refer to registers with a usually-decimal index from the base address.  The base address plus index scheme essentially guarantees addressing errors will be avoided.  See attached for how I do it with the 28L92 dual UART (DUART).  This file also refers to the vQUART (virtual quadruple UART) scheme I use, which during assembly, makes the four UARTs in the system appear to be parts of a single device.

registers.asm
NXP 28L92 Register “Map”
(4.94 KiB) Downloaded 116 times

In my POC unit’s serial I/O driver, the above scheme is used to build the physical address tables that get loaded into direct page during POST.  Those tables are structured so I can take advantage of that totally useless (<dp>,X) addressing mode.  :D

Once again, I will say that the best defense against errors, such as forgetting # in front of an immediate-mode-addressing operand, is diligence and proofreading.  Tools such as color syntax highlighting may help, but ultimately, being meticulous is the best defense against editing-induced errors.

gilhad wrote:
(I use Linux Gentoo, edit nearly everything in Vim, have a half-transparent picture in the background, and use tabs for indenting, shown as blue "|----" marks.)

First off, welcome to 6502-land.

I do all my editing and assembling in the Kowalski simulator, which supports colored syntax highlighting in the editor.  I only use a couple of colors, and mostly just to differentiate comments from “live text.”  That is useful when I comment out some code during testing—the code stands out in the editor.  Being partially color-blind (tritanomaly), I generally stick to colors that have high contrast and are in the lower end of the spectrum.

Although I do take advantage of vim’s syntax highlighting (great for identifying code blocks with unbalanced curly braces or square brackets—I write quite a bit in BASH’s native tongue, as well as in PHP), I have it mostly set up in two colors via my .vimrc file.  Some of the default color combinations in vim with syntax highlighting turned on are eye-watering and in a particular case, blue on the default black background, the text is completely invisible.

I also can’t read color schematics because certain color combinations, especially green or yellow on a light-colored background, are invisible to me.  Plain old black-on-white works every time!  :D
x86?  We ain't got no x86.  We don't NEED no stinking x86!
Rich
Posts: 4
Joined: 14 Oct 2024
Location: Sandy Ridge, NC

Re: tricks to avoid imm v abs/zp addressing bugs in ca65?

Post by Rich »

pdragon wrote:
By far my most common mistake writing assembly is neglecting a # in front of a literal (lda ff) or named constant (lda MAGIC_NUMBER),
so I end up with a memory reference instead of the intended immediate value.

Any tricks for getting the assembler or linker to help me detect these problems? For example when I write a literal value I almost never mean to refer to a memory location (I would always give it a symbolic name). Or if I could generate a list of all referenced memory locations (vs a named symbol map) I might see that MAGIC_NUMBER and ff shouldn't appear there.

Thanks!
Sorry I'm late to the party!
For many years monochromatic monitors were all we had. My solution was to think of "LDA #$52" as "Load A with number hex fifty-two" and translate "(" as "from" or "to" as needed.

HTH
jgharston
Posts: 181
Joined: 22 Feb 2004

Re: tricks to avoid imm v abs/zp addressing bugs in caa6?

Post by jgharston »

BillG wrote:
Zilog made the same mistake with the Z80.
Instead of

Code: Select all

    lda     Label
    mvi     A,Value
they used

Code: Select all

    ld      A,(Label)
    ld      A,Value
though you will get few Z80 fans to admit this is a problem...it is not as bad because () is more visible than a single #
I would say the Z80 syntax *fixes* the problem not replicates it. Memory (and I/O) references are always (), non-memory references are always not-().

(ok, one instruction, there's JP (HL) which maybe should actually be JP HL.)
John West
Posts: 383
Joined: 03 Sep 2002

Re: tricks to avoid imm v abs/zp addressing bugs in caa6?

Post by John West »

jgharston wrote:
I would say the Z80 syntax *fixes* the problem not replicates it.
I wouldn't say there's a problem as such - the 6502 way only seems to cause problems to people who encountered other assembly languages first. Using # for immediates makes sense to me because I started with the 6502, and I've always been surprised that people forget to use it.

If you want a problem, I give you Intel. If I'm reading the ASM86 Language Reference Manual correctly,

Code: Select all

    var1 equ 1234
    var2 dw 5678

    mov ax, var1
    mov bx, var2
is a load immediate followed by a load from memory.
barnacle
Posts: 1831
Joined: 19 Jan 2004
Location: Potsdam, DE
Contact:

Re: tricks to avoid imm v abs/zp addressing bugs in ca65?

Post by barnacle »

That's got to be an assembler construct. Those should either both load an immediate value, or both load from memory. Which is probably why x86 code scatters LEA instructions like salami slices on a pizza.

Neil
John West
Posts: 383
Joined: 03 Sep 2002

Re: tricks to avoid imm v abs/zp addressing bugs in ca65?

Post by John West »

Well yes, it's all assembler constructs. The 6502 world could have been using LDA 12 for immediate and LDA (12) for zero page if they'd chosen a Z80-like syntax. Or maybe LDAI 12 if they'd followed the 8080. The processor ends up with different opcodes either way. We only think of A9 and A5 as closely related because we call both 'LDA'. In another world, we might think of A9 and A2 as the closely related ones - they differ only in the register they affect.

This was Intel's "strongly typed assembler" idea. They had a bit of a thing about shoving high level language concepts into places they didn't really belong back in the late 70s.
Post Reply