Page 2 of 3

Re: Is there a better way to INX 4 times?

Posted: Mon Jul 24, 2017 9:52 am
by tokumaru
Yeah, you definitely have to mind the ends of the table if you plan on having values wrapping around. Complete freedom will require you to have 2 tables back to back (512 bytes total) and use the lower table for adding and the upper table for subtracting.

BTW, another advantage of a $00-$ff table is being able to perform operations between the accumulator and the index registers, such as ADC, SBC, AND, ORA, EOR... (e.g. A + X = ADC ByteTable, x).

Re: Is there a better way to INX 4 times?

Posted: Mon Jul 24, 2017 10:56 am
by FrankenGraphics
You could also make the table smaller if you know your Y and X transfers in your TYX/TXY substitutes are always going to be smaller than the table size. Making macros out of TYX, TXY, and the table inclusion, you could set your assembler to output an error if the param exceeds the limit of the table size you've chosen.

Re: Is there a better way to INX 4 times?

Posted: Mon Jul 24, 2017 12:03 pm
by mantanz
Wow thanks for all the info, guys!

Re: Is there a better way to INX 4 times?

Posted: Mon Jul 24, 2017 1:19 pm
by Garth
Just calling it "four_count," are you sure you'll know what that is if you come back to it in a couple of years? I would call it something INX_4X to make it more clear what it's doing, and then if you want to tell the reason or significance, put that in the comment to the right.

Re: Is there a better way to INX 4 times?

Posted: Mon Jul 24, 2017 1:24 pm
by mantanz
Garth wrote:Just calling it "four_count," are you sure you'll know what that is if you come back to it in a couple of years? I would call it something INX_4X to make it more clear what it's doing, and then if you want to tell the reason or significance, put that in the comment to the right.
I commented it :) But yeah I think your name is better.

Re: Is there a better way to INX 4 times?

Posted: Mon Jul 24, 2017 1:41 pm
by FrankenGraphics
I think garths' comment on the comment ( :lol: ) reflected on what is often viewed as good vs bad commenting practice

Bad practice:
Describe what the macro literally does. This adds clutter, which will make important comments harder to find.
For example, you can just aswell read the content of the macro (inx inx inx inx) aswell as the current comment and get the same info: 4x inx. Moreover, if the name reflects it, you can get that info from the code calling the macro, too, which would make such a comment reduntant both in your macro file and your main source file/s.

Good practice:
Comment what it is supposed to do, its reason for being there, what you mean to achieve.
If the goal of the block is too basic/evident, omit comment.

Best practice (IMO - sometimes a bit overkill):
Same as good, but also add a description what arguments do, what they expect, possible caveats using this macro, and what registers/flags are affected by the macro, if not evident. Mostly needed in more complex macros.

Re: Is there a better way to INX 4 times?

Posted: Mon Jul 24, 2017 1:46 pm
by mantanz
Yeah, excellent points. I'll clean it all up when I get home. At the moment I'm still struggling to get the code working when in an external asm file. Works when in main file... odd. lol.

Re: Is there a better way to INX 4 times?

Posted: Mon Jul 24, 2017 2:29 pm
by FrankenGraphics
Hm.. It sounds like your assembler assumes another path for your .included files than you'd expect. NESASM3 users might want to chime in on that.

Re: Is there a better way to INX 4 times?

Posted: Mon Jul 24, 2017 2:34 pm
by mantanz
FrankenGraphics wrote:Hm.. It sounds like your assembler assumes another path for your .included files than you'd expect. NESASM3 users might want to chime in on that.
I checked the docs, paths are correct and no issues assembling (which I did get when I named the file incorrectly) so I think it's my code. I'll have another go tonight.

Re: Is there a better way to INX 4 times?

Posted: Mon Jul 24, 2017 11:38 pm
by Oziphantom
FrankenGraphics wrote:I think garths' comment on the comment ( :lol: ) reflected on what is often viewed as good vs bad commenting practice

Bad practice:
Describe what the macro literally does. This adds clutter, which will make important comments harder to find.
For example, you can just aswell read the content of the macro (inx inx inx inx) aswell as the current comment and get the same info: 4x inx. Moreover, if the name reflects it, you can get that info from the code calling the macro, too, which would make such a comment reduntant both in your macro file and your main source file/s.

Good practice:
Comment what it is supposed to do, its reason for being there, what you mean to achieve.
If the goal of the block is too basic/evident, omit comment.

Best practice (IMO - sometimes a bit overkill):
Same as good, but also add a description what arguments do, what they expect, possible caveats using this macro, and what registers/flags are affected by the macro, if not evident. Mostly needed in more complex macros.
I disagree with these statements, well... wish to expand upon...
There are 3 uses for macros.
1.) Long complicated thing that I do often enough and want to tidy as its a black box and I just don't care. For example

Code: Select all

;@name Clear Interupts
;@desc disables NMI on CIA, IRQs on CIA and VIC, sei
;@parent IRQ
ClearInterupts .macro
	sei
	lda #$7f
	sta $dc0d		 ;turn off all types of cia irq/nmi.
	sta $dd0d
	lda $dc0d
	lda $dd0d
	lda #$ff
	sta $D019
	lda #$00
	sta $D01a
	sta $dc0e
	sta $dc0f
	sta $dd0e
	sta $dd0f
	lda $d01e
	lda $d01f
.endm
;@end
I do it once per project, maybe twice, fire and forget. Describing what this does exactly would be silly and long and not important.
2.) Macro to replace this missing thing, which should be named as on the tin, for example

Code: Select all

;@name STore Zero
;@param address
;@desc writes 0 to address 
;@parent STA
STZ .macro
	lda #0 
	sta \1
.endm
;@end

;@name ADd with Carry offset by X Word
;@param address
;@param address
;@desc address@w + (address@w),x will clear carry 
;@parent ADC
ADC_X_W .macro
	clc
	lda \1
	adc \2,x
	sta \1
	lda (\1)+1
	adc (\2)+1,x
	sta (\1)+1
.endm
;@end
This makes the code tighter, and stops my eyes getting lost in boiler plate, while allowing me to re target to say a 65816 latter and make use of its built in abilities, whilst keeping the code easy to read and allowing me to see what is used and trashed where. And 3.) take something that is custom to the situation and make it easier to read, and hence form a "MACRO Language", For Example

Code: Select all

MOVE_NEXT_ENT .macro
    inx
    cpx #kMaxNumEnts
    .if abs(*-EntLoop) < 127 
    bne EntLoop
    .else
    beq +
    jmp EntLoop
+  
    .fi
.endm
The problem with the 3rd case is it magically requires X to be the thing it needs to be, and after you have taken a break from coding and you come back to your game, or even if you have been spending time on the Title screen logic and not touch an ent for a while, then you come back make a change and boom, bug. Why because you forgot that magic macro needs X to be Current Ent still. And after a while you will open up your macro file and find the macro and then see what went wrong. A safer way would be to make it

Code: Select all

MOVE_NEXT_ENT .macro
    inc ENT_NUM
    ldx ENT_NUM
    cpx #kMaxNumEnts
    .if abs(*-EntLoop) < 127 
    bne EntLoop
    .else
    beq +
    jmp EntLoop
+  
    .fi
.endm
so now it just works, less efficient, but shouldn't have any issues unless you magically hope that X is not changed after the macro is "not taken". Lets make this macro

Code: Select all

inxCpxEq .macro
    inx
    cpx \1
    .if abs(*-\2) < 127 
    bne \2
    .else
    beq +
    jmp \2
+  
    .fi
So before I had

Code: Select all

  ... do things
  MOVE_NEXT_ENT
 .. other things
Now I have

Code: Select all

   ... do things
  inxCpxEq #kMaxNumEnts,EntLoop
 ... other things
the 2nd case you can instantly see that it A.) uses X and relies upon X having a specific value, B.) see exactly what it compares against and C.) see exactly where it jumps to, without having to track down some other macro file, which you won't keep in alphabetical order, then look it up and be OH.. as you brain will see the text MOVE_NEXT_ENT and think, well that gets the next ent, so so problems with it, and gloss over it.

C has the pre-processor and it was used for much evil, all subsequent languages have done their best to clobber the #defines see C#, Java et al . Speak with an old time C programmer and they will tend to go Roy Batty on you ;) Macros are mermaids, they will sing to you... type less code... be more readable and you won't need to add comments.... come deeper... add more... see how nice it looks... and then when you are in the middle of the ocean, they will turn and eat you. So they are advanced, and should only be attempted by people who know what they are doing, have strong discipline and are set in their ways. The guide is if you use a MACRO language only use macros, once you start inlining normal code with the macro code you start to open your self to pain.

Re: Is there a better way to INX 4 times?

Posted: Tue Jul 25, 2017 1:22 am
by FrankenGraphics
A macro can have more uses than that. For example, you can set target platform in your config or at the top of main and your macros can adjust accordingly, automatically replacing a 6502 workaround with a more efficient 65816 opcode for you at assembly time without needing to search and replace.

But yeah i agree, it's a balance. You shouldn't obfuscate code just for the sake of it.

Macros not being in alphabetical order is a non-argument, though. Ctrl-F is always faster/more convenient than browsing.

Re: Is there a better way to INX 4 times?

Posted: Tue Jul 25, 2017 1:25 am
by mantanz
You guys are breaking my 41-year-old brain. LOL

Re: Is there a better way to INX 4 times?

Posted: Tue Jul 25, 2017 1:57 am
by Oziphantom
FrankenGraphics wrote:A macro can have more uses than that. For example, you can set target platform in your config or at the top of main and your macros can adjust accordingly, automatically replacing a 6502 workaround with a more efficient 65816 opcode for you at assembly time without needing to search and replace.

But yeah i agree, it's a balance. You shouldn't obfuscate code just for the sake of it.

Macros not being in alphabetical order is a non-argument, though. Ctrl-F is always faster/more convenient than browsing.
see point 2 while allowing me to re target to say a 65816 later and make use of its built in abilities ;)

Re: Is there a better way to INX 4 times?

Posted: Tue Jul 25, 2017 3:04 am
by FrankenGraphics
Unless i'm missing something (again then), substituting an opcode from another processor, and autoreplacement based on conditional assembly are two different uses though (even if the latter must necessarily include the first). Like letting the macro conditionally select between branch always, and clc+bcs, or even just bcs if we know the carry flag is clear, based on what processor you have defined in your config. your if directives in the macro does the rest for you.

Re: Is there a better way to INX 4 times?

Posted: Tue Jul 25, 2017 4:16 am
by Oziphantom
Possibly my use of "missing thing" is throwing you, by thing I mean anything you want to do easily, not instruction from CPU Z, my first example being STZ doesn't help the matter ;)

Conditional Assembly doesn't need Macros, it is its own thing. For example taking the STZ example you could just do

Code: Select all

    ...some code
    .if CPU=="6502"
    lda #0
    sta ADDR
    .elsif CPU=="65816"
    stz ADDR
    .fi
    ...more code...
Of which you would use a Macro to make it neater, so you might be tempted to do

Code: Select all

.macro STZ .if CPU=="6502"
    lda #0
    sta \1
    .elsif CPU=="65816"
    stz \1
    .fi

and thus

    ...some code...
   #STZ ADDR
   ...more code....
I don't recommend this, if you have a more complete assembler like TASS64 which has .weak then you can mitigate the issues but in most assemblers you can't. If you put the above in a file, then you must include and correctly define a CPU variable or else you will get assembly issues, and then when you add a new flag to them, you then have to update all your old code and so on. I feel and recommend you separate your conditional and macros such that you have

Code: Select all

6502.mac
STZ .macro 
    lda #0
    sta \1
.endm
and
65816.mac
STZ .macro
    stz \1
.endm

Then in your code file do 
.if CPU=="6502"
   .include "6502.mac"
.elsif CPU=="65816"
  .include "65816.mac"
.else
  .error "CPU not set please choose 6502 or 65816"
.fi
However I also don't recommend you do this at all... replacing STZ in a macro with the intention of targeting both a NES and SNES will lead you to pain, consider the following snippet

Code: Select all

lda #$80
sta someFlag
#STZ numSprites
sta currEnt
sta ScreenColour
On the NES this will set someFlag to $80 and numSprites, currEnt and ScreenColour to $00
On the SNES this will set someFlag to $80, numSprites to $00 and currEnt and ScreenColour to $80

The correct way to do it is, if you're NES first and want the NES outcome,

Code: Select all

lda #$80
sta someFlag
#STZ numSprites
#STZ currEnt
#STZ ScreenColour
But then that will do a bunch of redundant LDA #0s. If you make code on the SNES and then try to back port it, it will nail you the other way. Beware of mermaids ;) Something like the BRA example you give is safer, while the ADC_X_W case is safe logically ( it will eat more bytes and more clocks on a 6502, but affects the same flags and has the same outcome if I replace it with a 16bit 65816 versions and just do LDA ADC STA at 16 bits.