Jump to content

Recommended Posts

For some years I have been using a macro which checks if code or data crosses a page boundary. It worked very well, but sometimes too well. Then it failed in an early assembler pass, where it would have succeeded in the final one.

 

Today Andrew came up with a brilliant idea: Define a label inside the macro in case of success only. And use that label later on. The assembler will return with an error if he cannot resolve the label in the final pass.

 

For convenience I added the success check into my macro too. Here is the updated macro:

_ERR SET 0

  MAC CHECKPAGE ; {address}
    LIST OFF              ; keep the list file clean
    IF (>(.-1)) != >{1}
      ECHO ""
      ECHO "ERROR: different pages! (", {1}, ",", .-1, ")"
      ECHO ""
;      ERR                ; previously this aborted the assembly
_ERR SET _ERR + 1         ; count number of errors (useful for extra output)
    ELSE
CHECKPAGE_{1}             ; define in case of success
    ENDIF
$CHECK SET CHECKPAGE_{1}  ; check success
    LIST ON
  ENDM

Usage:

Table
  .byte 1, 2, 3, 4,...
  CHECKPAGE Table

If Table crosses a page boundary the assembler will error: 

CHECKPAGE_Table ????(r )

 

  • Like 1

Share this post


Link to post
Share on other sites

Update: After I switched to the latest DASM version, the error is gone and DASM returns "complete". Any idea how to fix this? Is this a DASM problem?

Share this post


Link to post
Share on other sites

If I use the version that's in github master, it seems to work for me. (I uncommented your commented ERR statement) Here's my output if I use an ORG of $10ff...

 

/usr/local/bin/dasm scratch.asm -f3 -sscratch.symbol.txt -lscratch.listing.txt  -I. -I../includes -oscratch.bin
 
 ERROR: different pages! ( $10ff , $1102 )
 

scratch.asm (27): error: ERR pseudo-op encountered.
 

and my output if I use an ORG of $1000...

 

/usr/local/bin/dasm scratch.asm -f3 -sscratch.symbol.txt -lscratch.listing.txt  -I. -I../includes -oscratch.bin

Complete.

 

That seems to be what you intended.

Share this post


Link to post
Share on other sites

The "Complete" is the problem. The assembly should still fail with ERR being commented.

Share this post


Link to post
Share on other sites

Oh, I see. A failed SET for symbol that's actually unused in your assembly, is no longer a reason to abort an assembly. This was a reasonable (IMO) change that was needed to allow assembly to shift between passes.

 

Share this post


Link to post
Share on other sites

Any idea how I can force the assembly to fail?

Share this post


Link to post
Share on other sites

It's a bit kludgy, but you could spend a byte and use it in your assembly somewhere...

 .byte (CHECKPAGE_Table | CHECKPAGE_Foo | CHECKPAGE_bar | ... )

 

Not sure, beyond that.

Share this post


Link to post
Share on other sites

What does $CHECK do?

 

I changed that line to:

	IFNCONST CHECKPAGE_{1}
	  ECHO "ERROR: page crossing (",{1},",",.-1,")"
	  ERR
	ENDIF

(and removed the ECHOs from the other IF block) which seems to work, but I’m not certain it solves the original problem with using ERR directly, and I’m also not certain it works with a developmental dasm. I’m using 2.20.13, which I believe is the latest release.

Share this post


Link to post
Share on other sites
Posted (edited)

I found that (.-1) is useful when I’m confirming some block is on a single page, but when I’m confirming 2 different blocks are on the same page, I just want (.). So I split it into two separate macros. Here’s what I have now (I changed some names in my preference):

 

        mac samehighimpl ; {addr1,addr2}
        list localoff
        if >{1} == >{2}
SAMEHIGH_{1}_{2}_EXISTS ; defined if high bytes are equal
        endif
        ifnconst SAMEHIGH_{1}_{2}_EXISTS
          echo "ERROR: high byte difference (",{1},",",{2},")"
          err
        endif
        endm

        mac prevsamehighas ; {addr}
SAMEHIGHBASE set .-1 ; evaluate here so SAMEHIGH_{1}_{2}_EXISTS will work
        samehighimpl SAMEHIGHBASE,{1}
        endm

        mac nextsamehighas ; {addr}
SAMEHIGHBASE set . ; evaluate here so SAMEHIGH_{1}_{2}_EXISTS will work
        samehighimpl SAMEHIGHBASE,{1}
        endm

 

Edited by bizarrostormy
  • Like 1

Share this post


Link to post
Share on other sites
Posted (edited)

Now, how to get it to work for local labels?

 

EDIT: also, the label doesn’t substitute how I want it to. I want it to contain the addresses. Instead {1} expands to SAMEHIGHBASE, and {2} expands to the label (not the address) of the original argument. The implication is it does not support multiple calls for the same label. In some sense, that’s not necessary: of all the addresses that are supposed to be on the same page, the only ones that really matter are the first and the last. But this macro exists to ensure that future revisions don’t break old assumptions, so I would prefer to be able to put the macro everywhere in case one of those future revisions moves things.

Edited by bizarrostormy

Share this post


Link to post
Share on other sites

Not sure why, but as of now, macros do not accept local labels. 

Share this post


Link to post
Share on other sites
On 7/22/2020 at 1:51 AM, Thomas Jentzsch said:

Not sure why, but as of now, macros do not accept local labels. 

 

I didn’t realize this at the time, but macros apparently have their own namespace, as implied by this line from the documentation:

Quote

You should always use LOCAL labels (.name) inside macros which you use more than once.

which also implies the rationale.

 

The fact that macro calls do not implicitly evaluate their arguments is a major limitation. Tcl technically does the same thing but provides several ways to substitute.

Share this post


Link to post
Share on other sites
Posted (edited)

Just following that suggestion improves the code:

 

        mac samehigh8impl ; {addr1,addr2}
        list localoff
        if >{1} == >{2}
.SAMEHIGH8_EXISTS ; defined if high bytes are equal
        endif
        ifnconst .SAMEHIGH8_EXISTS
          echo "ERROR: high byte difference (",{1},",",{2},")"
          err
        endif
        endm

        mac prevsamehigh8as ; {addr}
SAMEHIGH8BASE set .-1 ; prevent error in samehigh8impl
        samehigh8impl SAMEHIGH8BASE,{1}
        endm

        mac nextsamehigh8as ; {addr}
SAMEHIGH8BASE set . ; prevent error in samehigh8impl
        samehigh8impl SAMEHIGH8BASE,{1}
        endm

 

Still doesn’t work for local labels but does now allow multiple calls with the same label.

 

EDIT: one reason I changed the name from “checkpage” to “samehigh” is because I also have places that need two addresses to have the same low byte. With “samehigh” it is very clear what to call the low-byte equivalent.

 

Edited by bizarrostormy

Share this post


Link to post
Share on other sites
Posted (edited)

It’s best to bracket all parameter references used as subexpressions, in case someone passes an argument containing a low-precedence operator:

 

EDIT: I posted code that doesn’t work. Will try again later.

 

EDIT: the gist was to enclose {1} and {2} in brackets, like:

        if >[{1}] == >[{2}]

But that doesn’t work: dasm rejects >[.] and >[.-1], both directly and when passed as a macro argument and then accessed via symbol.

 

EDIT: This seems to work, even when passing . or .-1:

        mac samehigh8impl ; {addr1,addr2}
        list localoff
        if >{1} == >{2} ; dasm currently rejects [.] in some cases
.SAMEHIGH8_EXISTS ; defined if high bytes are equal
        endif
        ifnconst .SAMEHIGH8_EXISTS
          echo "ERROR: high byte difference (",{1},",",{2},")"
          err
        endif
        endm

        mac prevsamehigh8as ; {addr}
SAMEHIGH8BASE set .-1 ; prevent error in samehigh8impl
        samehigh8impl SAMEHIGH8BASE,[{1}]
        endm

        mac nextsamehigh8as ; {addr}
SAMEHIGH8BASE set . ; prevent error in samehigh8impl
        samehigh8impl SAMEHIGH8BASE,[{1}]
        endm

 

Edited by bizarrostormy

Share this post


Link to post
Share on other sites

I often want to check the top nybble, and there might be uses (bankswitching?) to check other ranges.

 

This version can check any number of bits on either the high side or the low side.

 

This also uses no global symbols. The trick to getting rid of them was to assign a local label from the parameter in the leaf macro.

 

        mac samebits ; {numbits, addr1, addr2, mask, side}
        list localoff
.numbits = {1}
.addr1 = {2}
.addr2 = {3}
.mask = {4}
.side = {5} ; current dasm doesn't have setstr

.addr1masked = .addr1 & .mask
.addr2masked = .addr2 & .mask
        if .addr1masked == .addr2masked
.SAMEBITS_EXISTS ; defined if non-masked bits are equal
        endif
        ifnconst .SAMEBITS_EXISTS
          echo "ERROR: ", .side, " ", .numbits, "bits difference (", .addr1, ",", .addr2, ")"
          err
        endif
        endm

        mac samelow ; {numbits, addr1, addr2}
        samebits {1}, {2}, {3}, [1<<[{1}]]-1 & $FFFF, "low"
        endm

        mac samehigh ; {numbits, addr1, addr2}
        samebits {1}, {2}, {3}, ~[[1<<[16-[{1}]]]-1] & $FFFF, "high"
        endm

        mac prevsamelowas ; {numbits, addr}
        samelow [{1}], .-1, [{2}]
        endm

        mac nextsamelowas ; {numbits, addr}
        samelow [{1}], ., [{2}]
        endm

        mac prevsamehighas ; {numbits, addr}
        samehigh [{1}], .-1, [{2}]
        endm

        mac nextsamehighas ; {numbits, addr}
        samehigh [{1}], ., [{2}]
        endm

        mac prevsamehigh8as ; {addr} ; deprecated: use prevsamehighas instead
        prevsamehighas 8, [{1}]
        endm

        mac nextsamehigh8as ; {addr} ; deprecated: use nextsamehighas instead
        nextsamehighas 8, [{1}]
        endm

 

Share this post


Link to post
Share on other sites

Very nice.

 

But AFAIK the latest DASM supports 'setstr'.

Share this post


Link to post
Share on other sites
6 hours ago, Thomas Jentzsch said:

Very nice.

 

But AFAIK the latest DASM supports 'setstr'.

 

Ah. I was not aware of the 2.20.14 release. 2.20.13 does not support 'setstr'. TBH I’m not clear on the difference between 'set' and 'setstr'. The = seems to work fine in this case.

Share this post


Link to post
Share on other sites
On 10/5/2020 at 12:54 AM, bizarrostormy said:

TBH I’m not clear on the difference between 'set' and 'setstr'. The = seems to work fine in this case.

SETSTR makes a string literally from a symbol name. SET evaluates the value of the stuff on the right side of the SET command, and assigns that value to the symbol. Using = is the same as SET, except it's a constant symbol assignment, where you can repeatedly use SET to assign different values to the same symbol. (This is particularly useful when you want to generate look-up-table data instead of using constants, but by no means the only use for SET)

 

This code...

   processor 6502
   org $1000

 MAC testset

myset1 = {1}
myset2 SET {1}
mysetstr SETSTR {1}

 echo "myset1 is",myset1
 echo "myset2 is",myset2
 echo "mysetstr is",mysetstr

 ENDM

mylabel

 testset mylabel

...will result in this output during the assembly...

DASM 2.20.14-SNAPSHOT
/usr/local/bin/dasm scratch.asm -f3 -sscratch.symbol.txt -lscratch.listing.txt  -I. -I../includes -oscratch.bin
 myset1 is $1000
 myset2 is $1000
 mysetstr is mylabel

Complete. (0)

This is the main use case for SETSTR - so you can have a macro report which symbol it was called with.

  • Thanks 1

Share this post


Link to post
Share on other sites

Thanks for the explanation. In my case, the argument is already a quoted string, not a label, so = is correct and my comment about setstr was superfluous.

  • Like 1

Share this post


Link to post
Share on other sites

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...