There was INX and INY, why no INA?

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
There was INX and INY, why no INA?
by on (#195601)
You know how in the 6502 instruction set, you do INX and INY to increment the X and Y registers, but why did you have to do INC A instead of having a different command?
Re: There was INX and INY, why no INA?
by on (#195602)
Because SEC/ADC #0 isn't that much bigger.
Re: There was INX and INY, why no INA?
by on (#195603)
Nothing on a chip comes for free, in terms of cost and space and energy used and heat produced, so CPU designers need to think carefully about what features they include. This was doubly true at the time the 6502 was designed, when integrated circuit technology was nowhere near advanced as it is now and they had far fewer resources available.

So: conceptually, the accumulator A is the place where you do math, and X and Y are index registers for counting and pointing and stuff and the like. Incrementing/decrementing counters and indexes are common enough operations (for things like looping through arrays) that the designers of the 6502 chose to use some silicon on adding instructions to do that. But they figured that you're not doing math in the accumulator that just involves adding 1 all that often, and when you do you can just do ADC #1, so they opted not to include any extra circuitry for an INC A instruction. Now, that's a tiny bit slower and a tiny bit bigger than INC A would be, but the 6502 designers figured it was still a worthwhile trade-off.

Note that later chips in the 65xx family *did* include INC A, because at that point technology had advanced and they had more room on the chip to work with.
Re: There was INX and INY, why no INA?
by on (#195605)
There's a lot of asymmetries in the instruction set. If you look at available addressing modes there's a ton of weird inconsistencies there.

For example, there's an INC abs, X but no INC abs, Y.

As for the "why", it must have seemed cheaper or more convenient to implement the hardware without it. Explaining in more detail that that would require a lot of reverse engineering and speculation, though.
Re: There was INX and INY, why no INA?
by on (#195606)
If you work with the 6502 long enough you start to get the hang of which instructions exist and which don't, but I for example still have look up the reference to make sure something I need exists when designing complex algorithms. I usually stay on the safe side and assume that not all addressing modes are available for all operations, so when I look up an instruction like ORA I end up being positively surprised.

EDIT: If you're really bothered by the absence of certain opcodes, you can always create macros to simulate them. This will make your source code look more like the way you wanted it to be, but the performance and the final binary will obviously be the same as if you typed out the individual instructions that simulate your invented ones.
Re: There was INX and INY, why no INA?
by on (#195607)
The 65c02 does have INA (plus DEA, PHX, PHY, PLX, PLY, STZ, BRA, BIT#, and other goodies). The original 6502 was designed by hand though, and the masks were laid out by hand, similar to designing a PC board without CAD, which I imagine was largely why the things added later in the 65c02 didn't make it into the original. On 6502.org, we've had a lot of discussions over the years about extra instructions that would be nice. Of course, the 65816 did do a nice job of doing all that while putting up with the requirement that it still be able to execute 6502 code; but otherwise remember that for a given wafer technology, a more-complex instruction set also means more-complex instruction-decoding circuitry, reducing the maximum clock rate. Any possible slowing needed to be more than offset by the benefit of having whatever new instruction is being considered, and how frequently it would be used. I've also wondered about the process to evaluate the usefulness of proposed new instructions. You would kind of have to write a lot of software to see how it works out, but OTOH you wouldn't want to spend a lot of time on something there's no silicon for yet, or even cycle-accurate simulators. Chicken and egg situation.
Re: There was INX and INY, why no INA?
by on (#195609)
What the heck? There's INC. It does the same thing as INA would.
Re: There was INX and INY, why no INA?
by on (#195611)
INC only works on memory, not on the accumulator.
Re: There was INX and INY, why no INA?
by on (#195614)
tokumaru wrote:
INC only works on memory, not on the accumulator.

The 65C02 added an accumulator version of the instruction. (Presuming psycopathicteen is familiar with it via SNES.)
Re: There was INX and INY, why no INA?
by on (#195616)
Yeah, I was just letting him know that things are different with the 6502.
Re: There was INX and INY, why no INA?
by on (#195618)
I always liked how the Minecraft mod Redpower CPU, based on later entries in the 6502 family, implemented STP as "halt and catch fire".
Re: There was INX and INY, why no INA?
by on (#195621)
It would be kinda interesting to sit down with the decode ROMs and figure out what novel sensible instructions could have been fit in the same 130x21bit decode ROM
Re: There was INX and INY, why no INA?
by on (#195624)
I'd be happy with an ADD/SUB that didn't add the carry. I mean, CMP and AXS exist, so why can't ADD/SUB exist?
Re: There was INX and INY, why no INA?
by on (#195633)
pubby wrote:
I'd be happy with an ADD/SUB that didn't add the carry. I mean, CMP and AXS exist, so why can't ADD/SUB exist?


Code:
.macro ADD arg
CLC
ADC #arg
.endmacro

.macro SUB arg
SEC
SBC #arg
.endmacro
Re: There was INX and INY, why no INA?
by on (#195634)
That's only one addressing mode though, and offers no speed improvements.
Re: There was INX and INY, why no INA?
by on (#195635)
tokumaru wrote:
That's only one addressing mode though, and offers no speed improvements.


Got any other bright ideas for him (pubby)?

EDIT: This is not to come as any way offensive, Just trying to let others (including me) find other ideas!
Re: There was INX and INY, why no INA?
by on (#195642)
From ca65's generic macpack:
Code:
; add - Add without carry
.macro  add     Arg1, Arg2
        clc
        .if .paramcount = 2
                adc     Arg1, Arg2
        .else
                adc     Arg1
        .endif
.endmacro

; sub - subtract without borrow
.macro  sub     Arg1, Arg2
        sec
        .if .paramcount = 2
                sbc     Arg1, Arg2
        .else
                sbc     Arg1
        .endif
.endmacro

You nailed it as best as it seems possible for other assemblers.
Re: There was INX and INY, why no INA?
by on (#195650)
Hamtaro126 wrote:
Got any other bright ideas for him (pubby)?

There's nothing that can be done about speed, which's why I prefer to not use a macro like this and instead optimize the CLC/SEC out whenever the logic allows (for example, in multiplication and division routines you usually know the state of the carry before adding or subtracting).

As for the addressing modes...

nicklausw wrote:
You nailed it as best as it seems possible for other assemblers.

Yeah, AFAIK only ca65 is flexible enough to handle multiple addressing modes in macros like this.
Re: There was INX and INY, why no INA?
by on (#195678)
64TASS will let you do a lot of things, you can use default params to set the extra args to known things for example.
I have one that gives me an auto size length for STZ, so if I define a word and STZ the word it will clear both bytes. If I declare an int it will clear all 4 etc
Code:
STZ .macro
   lda #0
   .proff
   ;.if type(\1) == bits
   .if IsParamDirectAddress(\1)
   .pron
   sta \1
   .proff
   .else
   .pron
   sta (\1)+range(size(\1))
   .proff
   .endif
   .pron
.endm
so
Code:
myVar .byte ?
myWordVar  .word ?
myWordVarHL .dunion HLWord

#STZ myVar = lda #0 sta myVar
#STZ myWordVar = lda #0 sta myWordVar sta myWordVar+1
#STZ myWordVarHL = lda #0 sta myWordVarHL sta myWordVarHL +1
#STZ myWordVarHL.lo = lda #0 sta myWordVarHL.lo

where HLWord is defined as
HLWord .union
 .word ?
 .struct
    lo .byte ?
    hi .byte ?
 .ends
.endu
You can also make whole functions like so
Code:
dfont    .function f       
      .proff      
i      := f
      .for n = 0, n < len(f)/8, n = n + 1      
r      := bits(0)            
         .for m = 0, m < 8, m = m + 1      
r            ..= (i[0] == '#') ? %1 : %0            
i          := i[1:]            
            .if i == ''             
               .break            
            .endif            
         .next            
         .pron            
         .byte r            
         .proff                  
      .next      
      .pron      
.endf

dfontM    .function f       
      .proff      
i      := f
      .for n = 0, n < len(f)/4, n = n + 1      
r      := bits(0)            
         .for m = 0, m < 4, m = m + 1      
             .if (i[0] == '#') 
            r ..= %10
         .elsif (i[0] == '@')
            r ..= %01
         .elsif (i[0] == '*')
            r ..= %11
         .else
            r ..= %00
         .endif             
i          := i[1:]            
            .if i == ''             
               .break            
            .endif            
         .next            
         .pron            
         .byte r            
         .proff                  
      .next      
      .pron      
.endf   
these allow me to do things like
.dfont "##_##___"
.dfont "##_##___"
.dfont "#####___"
.dfont "##_##___"
.dfont "##_##___"
and it will convert it to
%11011000
%11011000
%11111000
%11011000
%11011000
The 2nd one lets me specify multicolour graphics
Code:
ILIW .macro
   .proff   
   .if \2 == x
   .pron
      ldy \1.lo,\2
      sty \3.lo
      ldy \1.hi,\2
      sty \3.hi
   .proff      
   .elsif \2 == y
   .pron
      ldx \1.lo,\2
      stx \3.lo
      ldx \1.hi,\2
      stx \3.hi
   .else
   .pron
      .cerror "invalid index input"
   .endif
.endm
lets me specify ,x or ,y on the params list.
You can also get it to do array of structs to struct of arrays conversions for you
Code:
sorted  = sort([(60, irq1), (50, irq2)])
lines   .byte sorted[:, 0] ; 50, 60
irqs    .addr sorted[:, 1] ; irq2, irq1


However if one really wants scripting power KICKASS is the insane one

It has a optimiser that you can enable which will point out most of the things you can skip, so if you want to break the macros out into full code to save a clc or sec or A is already 0 etc it will give you a nice list.
Re: There was INX and INY, why no INA?
by on (#195741)
lidnariq wrote:
Because SEC/ADC #0 isn't that much bigger.


Curious why you'd chose SEC/ADC#0 over CLC/ADC#1?
I get that they do the same, but the latter just seems to give a much better visual on what you are actually trying to do.
Re: There was INX and INY, why no INA?
by on (#195750)
Because "INX" can only add one, and likewise the Carry bit only can add one.

Not saying it's a great reason.