Macro/Function Pseudo Instructions Everyone Should Use?

Programming the 6502 microprocessor and its relatives in assembly and other languages.
load81
Posts: 71
Joined: 16 Nov 2018

Macro/Function Pseudo Instructions Everyone Should Use?

Post by load81 »

When you write code for decently large projects in 6502 assembly (any dialect) what sorts of macros and functions are a "must" in your mind?

Personally, I created a simple not pseudo-instruction as an alias for eor #$ff as a purely stylistic preference. I just find it easier to read. Additionally, nand and nor followed. I'm not trying to go "macro crazy" or anything, but I have thought about different functions macros I could experiment with besides 1-2 instruction aliases.

Because 6502 is load/store based, I've played around with a few variations on the on "copy #counter $[source], $[target]" theme. I'm sure everyone else has too.

I thought about implementing a pair of multiply and divide instructions. But, that's not as simple as it sounds because of differing memory and performance characteristics as several articles made clear while I was researching the topic.

I've also tried to figure out how to (functionally) nop an odd number of cycles without resorting to an illegal opcode. I haven't come up with anything yet. I'm sure it will hit me eventually and seem "obvious" in hindsight.
User avatar
GARTHWILSON
Forum Moderator
Posts: 8775
Joined: 30 Aug 2002
Location: Southern California
Contact:

Re: Macro/Function Pseudo Instructions Everyone Should Use?

Post by GARTHWILSON »

We've had some good macro topics here in the past:

assembler macros!
desirable assembler features
Passing a variable as an argument to a macro
ANN: HXA0.190 teamtempest's (Anton Treuenfels') topic on program structures in his HXA 65c02/816 assembler.  It was an honor that he copied my structures here and implemented them in the assembler he provides.
A sensible macro engine  In this one, enso proposes and explores the idea of a macro pre-processor that could then be used with various assemblers including ones with no macro capability.
And mentioned on the forum by HansO, is "Macross 6502, an assembler for people who hate assembly language," a very C-like macro assembler for the 6502 (NMOS only).  (Note the two s's in "Macross.")

I use macros to form high-level program flow-control structures, and have an article about it, with source code, at http://wilsonminesco.com/StructureMacros/, and extended examples in the last 40% of the page at http://wilsonminesco.com/multitask/index.html, and more on how the macros do their job at http://wilsonminesco.com/stacks/pgmstruc.html .
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
barrym95838
Posts: 2056
Joined: 30 Jun 2013
Location: Sacramento, CA, USA

Re: Macro/Function Pseudo Instructions Everyone Should Use?

Post by barrym95838 »

Code: Select all

     jmp  $+3
takes three cycles, and

Code: Select all

     php
     plp
takes seven.
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)
Chromatix
Posts: 1462
Joined: 21 May 2018

Re: Macro/Function Pseudo Instructions Everyone Should Use?

Post by Chromatix »

BRA *+2 also takes three cycles (taken branch) and only two bytes. There is never a page-crossing penalty because the offset is zero.

BVC *+2 : BVS *+2 always takes 5 cycles (one taken branch, one fall-through), and works on NMOS as well as CMOS. You could use any other pair of complementary branches too.

On both NMOS and CMOS, but *not* the '816, opcode $44 is an undocumented 2-byte, 3-cycle NOP; it performs a zero-page access but does nothing with the result. On the '816 it is MVN, so be careful.
Last edited by Chromatix on Fri Jun 19, 2020 6:02 am, edited 1 time in total.
User avatar
barrym95838
Posts: 2056
Joined: 30 Jun 2013
Location: Sacramento, CA, USA

Re: Macro/Function Pseudo Instructions Everyone Should Use?

Post by barrym95838 »

Chromatix wrote:
BVC *+2 : BVS *+2 always takes 5 cycles ...
I like it!
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)
White Flame
Posts: 704
Joined: 24 Jul 2012

Re: Macro/Function Pseudo Instructions Everyone Should Use?

Post by White Flame »

If you want to delay for a runtime-determined number of cycles, I've used this:

Code: Select all

delayJumpVectorInit:
 cmp #$c9 ; 25 | 24 cycles
 cmp #$c9 ; 23 | 22 cycles
 cmp #$c9 ; 21 | 20 cycles
 cmp #$c9 ; 19 | 18 cycles
 cmp #$c9 ; 17 | 16 cycles
 cmp #$c9 ; 15 | 14 cycles
 cmp #$c9 ; 13 | 12 cycles
 cmp #$c9 ; 11 | 10 cycles
 cmp #$c9 ;  9 |  8 cycles
 cmp #$c9 ;  7 |  6 cycles
 cmp #$c9 ;  5 |  4 cycles
 bit $ea  ;  3 |  2 cycles
Starting with the entry point of the last $EA taking 2 cycles (NOP), each consecutively prior entry address takes 1 more clock cycle. C9 is CMP #imm.
johnwbyrd
Posts: 89
Joined: 01 May 2017

Re: Macro/Function Pseudo Instructions Everyone Should Use?

Post by johnwbyrd »

Since Bill Gates was writing 6502 code, it's been conventional to have a series of pseudo opcodes in the form JEQ, JNE, JCC, JCS, JVC, JVS, JMI, and JPL.

These pseudo-ops compile exactly to their B?? counterparts, unless the jump target is more than +/-128 bytes away, in which case they compile to a combination of the opposite branch type plus a jump.

So, a "JNE target" instruction can compile to:

BNE target

Or it can compile to:

BEQ branchnottaken
JMP target
.branchnottaken

Depending on the distance from the current program counter to target.
User avatar
Agumander
Posts: 129
Joined: 17 Jul 2018
Location: Long Island, NY
Contact:

Re: Macro/Function Pseudo Instructions Everyone Should Use?

Post by Agumander »

One assembler feature I've been wondering exists anywhere is using "{" as label to the next instance of "}", with the ability to nest them.
(It could be some other pair of demarcating characters, those are just the first I think of as a C guy)
This would save me from having to come up with unique label names, or avoid bugs that happen if i miscount bytes when writing BEQ *+n
Had this happen on my current project when I forgot that one of my named memory locations wasn't actually zero page. Oops!
BillG
Posts: 710
Joined: 12 Mar 2020
Location: North Tejas

Re: Macro/Function Pseudo Instructions Everyone Should Use?

Post by BillG »

Agumander wrote:
One assembler feature I've been wondering exists anywhere is using "{" as label to the next instance of "}", with the ability to nest them.
(It could be some other pair of demarcating characters, those are just the first I think of as a C guy)
This would save me from having to come up with unique label names, or avoid bugs that happen if i miscount bytes when writing BEQ *+n
Had this happen on my current project when I forgot that one of my named memory locations wasn't actually zero page. Oops!
Personally, I like local labels. I talk about it here: viewtopic.php?p=76604#p76604
User avatar
Druzyek
Posts: 367
Joined: 12 May 2014
Contact:

Re: Macro/Function Pseudo Instructions Everyone Should Use?

Post by Druzyek »

Quote:
Because 6502 is load/store based, I've played around with a few variations on the on "copy #counter $[source], $[target]" theme. I'm sure everyone else has too.
I find that kind of macro really useful. It took some effort to get it working right, but this saved me a lot of time:

Code: Select all

BYTE foo
WORD bar,baz

MOV #$1234,bar

MOV bar,foo

MOV foo,baz

;After expansion:
LDA #$34
STA bar,X
LDA #$12
STA bar+1,X

LDA bar,X
STA foo,X

LDA foo,X
STA baz,X
STZ baz+1,X
The key is that the macro remembers whether a variable is 8 or 16 bits and outputs the correct assembly even when the source and destination are different sizes. Type awareness is really handy for other macros as well.
User avatar
GARTHWILSON
Forum Moderator
Posts: 8775
Joined: 30 Aug 2002
Location: Southern California
Contact:

Re: Macro/Function Pseudo Instructions Everyone Should Use?

Post by GARTHWILSON »

Agumander wrote:
One assembler feature I've been wondering exists anywhere is using "{" as label to the next instance of "}", with the ability to nest them.
(It could be some other pair of demarcating characters, those are just the first I think of as a C guy)
This would save me from having to come up with unique label names, or avoid bugs that happen if i miscount bytes when writing BEQ *+n
Had this happen on my current project when I forgot that one of my named memory locations wasn't actually zero page. Oops!

That's what my article on using macros to form nestable program flow control structures is about, at http://wilsonminesco.com/StructureMacros/ .  I much prefer the natural-language characteristic over curly braces, and it should be implementable on any macro assembler with very few modifications needed to make it work.  There are a few extended examples of the macro usage in the last 40% of the page at http://wilsonminesco.com/multitask/, and further explanation of the macros' inner workings in the relevant chapter of the 6502 stacks treatise, at http://wilsonminesco.com/stacks/pgmstruc.html .

Andrew Jacobs (forum name "BitWise") has published his free AS65 assembler that has a similar thing built in, at http://www.obelisk.me.uk/dev65/as65.html .  Also, Anton Treuenfels (forum name "teamtempest") has his HXA assembler he wrote incorporating my program structures, at https://web.archive.org/web/20190208204 ... .net/~hxa/ .
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
BitWise
In Memoriam
Posts: 996
Joined: 02 Mar 2004
Location: Berkshire, UK
Contact:

Re: Macro/Function Pseudo Instructions Everyone Should Use?

Post by BitWise »

Who needs local labels when you have structure?

Code: Select all

                             ; Process the string pointered to be Y extracting any initial line number and
                             ; replacing any keywords with token codes.

                             TokenizeLine:
C0:00BE  201201            :                 jsr     SkipSpaces      ; Skip any spaces
C0:00C1  206300            :                 jsr     IsDigit         ; Found a line number?
C0:00C4  08                :                 php
C0:00C5  9042              :                 if cs
C0:00C7  6404              :                  stz    LINENO+0        ; Zero the line number
C0:00C9  6405              :                  stz    LINENO+1
                                              repeat
C0:00CB  290F              :                   and   #$0f            ; Extract digit value
C0:00CD  18                :                   clc                   ; .. and add into line number
C0:00CE  6504              :                   adc   LINENO+0
C0:00D0  8504              :                   sta   LINENO+0
C0:00D2  9004              :                   if cs
C0:00D4  E605              :                    inc  LINENO+1
C0:00D6  F036              :                    beq  .Failed
                                               endif

C0:00D8  B90000            :                   lda   !0,y            ; Another digit?
C0:00DB  C8                :                   iny
C0:00DC  206300            :                   jsr   IsDigit
C0:00DF  9028              :                   break cc              ; No. done
C0:00E1  48                :                   pha                   ; Yes, save it

C0:00E2  A505              :                   lda   LINENO+1        ; Save x1
C0:00E4  48                :                   pha
C0:00E5  A504              :                   lda   LINENO+0
C0:00E7  48                :                   pha
C0:00E8  0604              :                   asl   LINENO+0        ; x2
C0:00EA  2605              :                   rol   LINENO+1
C0:00EC  B020              :                   bcs   .Failed
C0:00EE  0604              :                   asl   LINENO+0        ; x4
C0:00F0  2605              :                   rol   LINENO+1
C0:00F2  B01A              :                   bcs   .Failed
C0:00F4  68                :                   pla                   ; Add x1 to x4 => x5
C0:00F5  6504              :                   adc   LINENO+0
C0:00F7  8504              :                   sta   LINENO+0
C0:00F9  68                :                   pla
C0:00FA  6505              :                   adc   LINENO+1
C0:00FC  8505              :                   sta   LINENO+1
C0:00FE  B00E              :                   bcs   .Failed
C0:0100  0604              :                   asl   LINENO+0        ; x10
C0:0102  2605              :                   rol   LINENO+1
C0:0104  B008              :                   bcs   .Failed

C0:0106  68                :                   pla                   ; Repeat for next digit
C0:0107  80C2              :                  forever
                                             endif
C0:0109  201101            :                 jsr     TokenizeCmd
C0:010C  28                :                 plp
C0:010D  60                :                 rts

C0:010E  4C1C02            : .Failed:        jmp     SyntaxError     ; Syntax error if line number too big
Ok. So I used one for the error handler.

'816 code with 16-bit index registers when called.
Andrew Jacobs
6502 & PIC Stuff - http://www.obelisk.me.uk/
Cross-Platform 6502/65C02/65816 Macro Assembler - http://www.obelisk.me.uk/dev65/
Open Source Projects - https://github.com/andrew-jacobs
User avatar
65f02
Posts: 79
Joined: 01 Jul 2020
Location: Germany

Re: Macro/Function Pseudo Instructions Everyone Should Use?

Post by 65f02 »

[Edit: Ah, got it. I was apparently blind when first looking at the listing ...]
User avatar
cjs
Posts: 759
Joined: 01 Dec 2018
Location: Tokyo, Japan
Contact:

Re: Macro/Function Pseudo Instructions Everyone Should Use?

Post by cjs »

My most commonly used macro is INCW to increment a word. Re-reading it also reminded me to be careful about the use of labels in macros: in Macroassembler AS a macro can close the scope of local symbols if you're not careful.

Code: Select all

;--------------------------------------------------------------------------
;   INCW macro to increment a word
;
;   We must use only composed or named temporary symbols in macros because
;   even a macro-internal non-temporary symbol will close the current
;   composed/named temporary symbol scope and open a new one. (Nameless +/-
;   temporary symbols also cannot be used because they generate a fresh
;   non-temporary symbol.) The unit test takes care to cover this.

incw        macro addr
            inc addr
            bne .nocarry
            inc addr+1
.nocarry
            endm
One I thought would be quite useful, but actually these days seems less so, is for "automatic" definition of storage in the zero page, called with code like:

Code: Select all

temp1:      zds     1                                                           
temp2:      zds     1                                                           
bufSptr:    zds  2              ; pointer to a buffer                           
bufSlen:    zds  1              ; length of that buffer                         
buf0ptr:    zds  2                                                              
buf0len:    zds  1                 
Curt J. Sampson - github.com/0cjs
Oh-Two
Posts: 7
Joined: 13 Jan 2021

Re: Macro/Function Pseudo Instructions Everyone Should Use?

Post by Oh-Two »

I've downloaded Andrew Jacob's Dev65 assembler, mentioned here. Despite the name, it copes with several 8-bit CPU families, which is what I need, and I really like the look of the structured programming aids. But I can't figure out how to run it!

I know very little about Java, but my understanding is that .java files are source files, compiled by 'full blown' Java / JDK, which creates compiled code (.class files?), which can be run by just the runtime, JRE.

The Dev65 that I've downloaded is packed with .java files, which suggests that they all need compiling. But this is in contrast to the instructions, which imply that the Java runtime (JRE) is sufficient to run it.

In fact, although Dev65 looks to be actively updated (last refresh ~ Oct 2020), the instructions for it may well be quite out of date, and I'm wondering if they still refer to a much older predecessor to it, called 65xx. I've downloaded that as well, and this looks quite different. There is a variety of compiled Java classes in a Zip file, with simple one-line Windows .bat scripts to get the assembler going.

I'm using Windows 10 64-bit, and have already installed the latest JRE. I've successfully done a quick test of the assembler in the much older 65xx package, and that runs OK, producing LST and OBJ files in no time. So I know the JRE is working.

But that older package is for the 65xx family only, and I really want the multi-CPU abilities of Dev65. I know several other assemblers can do that, and if I have to opt for them, then I will. But I'd really like to have a good try with Dev65, and exploit those structured programming tools.

Do I really have to download the 'full blown' Java / JDK (registration required now?) and compile the sources? If so, is there an easy way to compile the whole package in one go? Or have I completely got the wrong end of the stick?
Post Reply