Thomas Jentzsch Posted June 15, 2020 Share Posted June 15, 2020 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 ) 1 Link to comment Share on other sites More sharing options...
Thomas Jentzsch Posted June 15, 2020 Author Share Posted June 15, 2020 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? Link to comment Share on other sites More sharing options...
RevEng Posted June 15, 2020 Share Posted June 15, 2020 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. Link to comment Share on other sites More sharing options...
Thomas Jentzsch Posted June 15, 2020 Author Share Posted June 15, 2020 The "Complete" is the problem. The assembly should still fail with ERR being commented. Link to comment Share on other sites More sharing options...
RevEng Posted June 15, 2020 Share Posted June 15, 2020 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. Link to comment Share on other sites More sharing options...
Thomas Jentzsch Posted June 15, 2020 Author Share Posted June 15, 2020 Any idea how I can force the assembly to fail? Link to comment Share on other sites More sharing options...
RevEng Posted June 15, 2020 Share Posted June 15, 2020 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. Link to comment Share on other sites More sharing options...
+Pat Brady Posted July 22, 2020 Share Posted July 22, 2020 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. Link to comment Share on other sites More sharing options...
+Pat Brady Posted July 22, 2020 Share Posted July 22, 2020 (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 July 22, 2020 by bizarrostormy 1 Link to comment Share on other sites More sharing options...
+Pat Brady Posted July 22, 2020 Share Posted July 22, 2020 (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 July 22, 2020 by bizarrostormy Link to comment Share on other sites More sharing options...
Thomas Jentzsch Posted July 22, 2020 Author Share Posted July 22, 2020 Not sure why, but as of now, macros do not accept local labels. Link to comment Share on other sites More sharing options...
+Pat Brady Posted July 23, 2020 Share Posted July 23, 2020 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. Link to comment Share on other sites More sharing options...
+Pat Brady Posted July 24, 2020 Share Posted July 24, 2020 (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 July 24, 2020 by bizarrostormy Link to comment Share on other sites More sharing options...
+Pat Brady Posted August 8, 2020 Share Posted August 8, 2020 (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 August 8, 2020 by bizarrostormy Link to comment Share on other sites More sharing options...
+Pat Brady Posted October 4, 2020 Share Posted October 4, 2020 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 Link to comment Share on other sites More sharing options...
Thomas Jentzsch Posted October 4, 2020 Author Share Posted October 4, 2020 Very nice. But AFAIK the latest DASM supports 'setstr'. Link to comment Share on other sites More sharing options...
+Pat Brady Posted October 5, 2020 Share Posted October 5, 2020 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. Link to comment Share on other sites More sharing options...
RevEng Posted October 7, 2020 Share Posted October 7, 2020 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. 1 Link to comment Share on other sites More sharing options...
+Pat Brady Posted October 7, 2020 Share Posted October 7, 2020 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. 1 Link to comment Share on other sites More sharing options...
Recommended Posts