SmileyDude Posted September 4, 2020 Share Posted September 4, 2020 (edited) Hello - Does anyone know if it’s possible in dasm to have a macro reserve space for a string in another segment than the segment the code is being assembled in? For example, I want to have a macro like this: MACRO PRINT lda #<.string ldx #>.string jsr printString jmp .skip .string: .byte {1},$00 .skip: ENDM but without needing to do that jump at the end to skip over the string. Instead, I want to put all the strings together in a single place and let the macro handle the details so I can just say: PRINT "Hello World!" and it works correctly. The above macro does work, but it ends up wasting 3 bytes for that jump. Which is why I want to just push all the strings into a separate segment after my code. Thanks! Edited September 4, 2020 by SmileyDude typo Quote Link to comment Share on other sites More sharing options...
+selgus Posted September 4, 2020 Share Posted September 4, 2020 A different implementation for PRINTing to the screen in 6502, is to not pass the address of the string data to the printing function in the index registers, but to place that data after the "jsr" call to your printing function. Then in your printing function, you can pull the return address off of the stack and use it as a pointer to the string data you wish to print. Usually you end that print data with a NULL, so you can check if you are at the end of the string yet. Lastly you can then use that pointer that is now at the end of your string, as the return address for your "rts" (by pushing it back onto the stack and doing an "rts"). Quote Link to comment Share on other sites More sharing options...
SmileyDude Posted September 4, 2020 Author Share Posted September 4, 2020 That actually was an interesting suggestion. I think I had heard of that years ago and then forgot about it. I just spent a little time re-working my print method to do this and it works perfectly. Thanks! That said, it would still be interesting to hear if there is a way to use segments that way in dasm. I’m sure I’ll wind up wishing for it again one day. Quote Link to comment Share on other sites More sharing options...
Kevin McGrath Posted September 8, 2020 Share Posted September 8, 2020 (edited) Yes, you should be able to switch segments within the macro, and then define the space for your data, and then switch back into your code segment. Here's an example: SEG.U STRINGS ORG Strings_Org ... ; SEG_PRINT "Hello World" ; Store a string, with the first byte containing the string length in bytes MAC SEG_PRINT .length EQU [.lastChr - .firstChr] IF [.length == 0] ECHO "String '", {1}, "' is zero bytes long!" ELSE IF [.length > 255] ECHO "String '", {1}, "' is longer than 255 bytes!" ELSE brk DC.B <.storedLen DC.B >.storedLen ENDIF ENDIF SEG.U STRINGS .storedLen DC.B <[.length] .firstChr DC.B {1} .lastChr EQU . SEG.U CODE ENDM ... SEG CODE ORG $F000 ; Standard Atari 4K cartridge, no bank-switching needed ... SEG_PRINT "Hello World" ... Strings_Org EQU . ;======================================-======================================= ECHO ([$FFFC-.]d), "bytes available for 4KB cartridge" ;======================================-======================================= ; BANK SWITCHING ; Reserve $FFFA - $FFFB (normally the 6502 NMI vector) for bank-switching ;======================================-======================================= ; RESET AND IRQ/BRK VECTORS ORG $FFFC DC.W ColdBoot ; Reset DC.W BRKInst ; IRQ / BRK So in the above example, I've got a primary CODE segment, and at the end of that segment, but still within the CODE segment I've defined the STRINGS segment origin referenced at the top of the assembly code. Notice the macro, which is called from within the CODE segment, switches to the STRINGS segment, defined the data it wants to store there, and then switches back to the CODE segment for the remaining assembly code. As a side note; you can save even more ROM bytes by using the BRK instruction and placing your string's address after the break. If you want to get fancy, you can have a byte after the BRK that defines what thing you want to do, kind of like a system call, so the code would go BRK, function byte, low address byte, high address byte, and get away with a four byte print string call. Or if you just want to use it for string printing, just store your whole string after the BRK instruction like selgus mentions above and save two bytes by not using a JSR. Keep in mind that return address for the BRK on the stack isn't the next instruction immediately after the BRK, but the one after that (so the 6502 sees BRK as a two byte instruction, even though it's only one). Edit: Fixed code formatting Edited September 8, 2020 by Kevin McGrath 1 Quote Link to comment Share on other sites More sharing options...
+Andrew Davie Posted September 8, 2020 Share Posted September 8, 2020 8 hours ago, Kevin McGrath said: Yes, you should be able to switch segments within the macro, and then define the space for your data, and then switch back into your code segment. Here's an example: SEG.U STRINGS ORG Strings_Org ... ; SEG_PRINT "Hello World" ; Store a string, with the first byte containing the string length in bytes MAC SEG_PRINT .length EQU [.lastChr - .firstChr] IF [.length == 0] ECHO "String '", {1}, "' is zero bytes long!" ELSE IF [.length > 255] ECHO "String '", {1}, "' is longer than 255 bytes!" ELSE brk DC.B <.storedLen DC.B >.storedLen ENDIF ENDIF SEG.U STRINGS .storedLen DC.B <[.length] .firstChr DC.B {1} .lastChr EQU . SEG.U CODE ENDM ... SEG CODE ORG $F000 ; Standard Atari 4K cartridge, no bank-switching needed ... SEG_PRINT "Hello World" ... Strings_Org EQU . ;======================================-======================================= ECHO ([$FFFC-.]d), "bytes available for 4KB cartridge" ;======================================-======================================= ; BANK SWITCHING ; Reserve $FFFA - $FFFB (normally the 6502 NMI vector) for bank-switching ;======================================-======================================= ; RESET AND IRQ/BRK VECTORS ORG $FFFC DC.W ColdBoot ; Reset DC.W BRKInst ; IRQ / BRK So in the above example, I've got a primary CODE segment, and at the end of that segment, but still within the CODE segment I've defined the STRINGS segment origin referenced at the top of the assembly code. Notice the macro, which is called from within the CODE segment, switches to the STRINGS segment, defined the data it wants to store there, and then switches back to the CODE segment for the remaining assembly code. As a side note; you can save even more ROM bytes by using the BRK instruction and placing your string's address after the break. If you want to get fancy, you can have a byte after the BRK that defines what thing you want to do, kind of like a system call, so the code would go BRK, function byte, low address byte, high address byte, and get away with a four byte print string call. Or if you just want to use it for string printing, just store your whole string after the BRK instruction like selgus mentions above and save two bytes by not using a JSR. Keep in mind that return address for the BRK on the stack isn't the next instruction immediately after the BRK, but the one after that (so the 6502 sees BRK as a two byte instruction, even though it's only one). Edit: Fixed code formatting Great answer. This belongs in the new manual. Would you mind if I included it? 1 Quote Link to comment Share on other sites More sharing options...
Kevin McGrath Posted September 8, 2020 Share Posted September 8, 2020 5 hours ago, Andrew Davie said: Great answer. This belongs in the new manual. Would you mind if I included it? Not at all. Thanks! Quote Link to comment Share on other sites More sharing options...
+Andrew Davie Posted September 9, 2020 Share Posted September 9, 2020 18 hours ago, Kevin McGrath said: Not at all. Thanks! In preparation for that, I found that it doesn't/won't work. At least, I'm having trouble with it - the local labels for example, are reused and thus have incorrect values later in the assembly. I may be misunderstanding it - do you have a working-code example? Quote Link to comment Share on other sites More sharing options...
Kevin McGrath Posted September 9, 2020 Share Posted September 9, 2020 6 hours ago, Andrew Davie said: In preparation for that, I found that it doesn't/won't work. At least, I'm having trouble with it - the local labels for example, are reused and thus have incorrect values later in the assembly. I may be misunderstanding it - do you have a working-code example? Yipes! It looks like I don’t have a working example, though I’m not sure why it’s not working. I’ve attached my 2K ROM test case. If you change the SEG.U for the STRING segment definition into a SEG, you’ll get this error: seg_print.asm (92): error: Origin Reverse-indexed. But leaving it as an undefined segment (SEG.U) appears to allow it to assemble. If you look at the listing file, it looks like the strings are being assigned space and being put into the correct area of the ROM, but a dump of the binary file shows that they are indeed not: 8 f800 00 brk 9 f801 1b DC.B <.storedLen 10 f802 f8 DC.B >.storedLen 11 f803 ENDIF 12 f803 ENDIF 13 Uf81b SEG.U STRINGS String stored at $f81b 14 Uf81b ECHO "String stored at", . 15 Uf81b 03 .storedLen DC.B <[.length] 16 Uf81c 4f 6e 65 .firstChr DC.B "One" 17 Uf81c f8 1f .lastChr EQU . 18 f803 SEG CODE ... 8 f811 00 brk 9 f812 1f DC.B <.storedLen 10 f813 f8 DC.B >.storedLen 11 f814 ENDIF 12 f814 ENDIF 13 Uf81f SEG.U STRINGS String stored at $f81f 14 Uf81f ECHO "String stored at", . 15 Uf81f 03 .storedLen DC.B <[.length] 16 Uf820 54 77 6f .firstChr DC.B "Two" 17 Uf820 f8 23 .lastChr EQU . 18 f814 SEG CODE ... 103 f815 42 65 66 6f* DC.B "Before" 104 f81b Strings_Org: $f81b 105 f81b ECHO "Strings_Org:", . 106 f81b 107 f81b f8 1b Strings_Org EQU . 108 f81b 109 f81b 41 66 74 65* DC.B "After" Above, you can see the DC.B statements within the listing getting assigned space (Uf81b and Ufb1f), but then in the binary file: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00000000 00 1B F8 78 D8 A2 00 8A A8 CA 9A 48 D0 FB 4C 00 ...x....¨..H..L. 00000010 F8 00 1F F8 40 42 65 66 6F 72 65 41 66 74 65 72 ....@BeforeAfter 00000020 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................ 00000030 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................ You can see there's nothing between "Before" and "After". Perhaps I should post this as a bug on the Dasm GitHub site? It feels like you should be able to switch segments within a macro, and the listing file sure makes it seem like space is being allocated and assigned to the string bytes, but the final emitted bytes do not contain the strings from the macros. It does go through five passes, which seems like it's trying to figure something out, but not quiet getting there in the end. I'm assembling the attached test file using: dasm.exe seg_print.asm -f3 -v0 -Lseg_print.lst -sseg_print.sym -oseg_print.bin seg_print.asm Quote Link to comment Share on other sites More sharing options...
+Andrew Davie Posted September 9, 2020 Share Posted September 9, 2020 30 minutes ago, Kevin McGrath said: Yipes! It looks like I don’t have a working example, though I’m not sure why it’s not working. I’ve attached my 2K ROM test case. If you change the SEG.U for the STRING segment definition into a SEG, you’ll get this error: seg_print.asm (92): error: Origin Reverse-indexed. But leaving it as an undefined segment (SEG.U) appears to allow it to assemble. If you look at the listing file, it looks like the strings are being assigned space and being put into the correct area of the ROM, but a dump of the binary file shows that they are indeed not: 8 f800 00 brk 9 f801 1b DC.B <.storedLen 10 f802 f8 DC.B >.storedLen 11 f803 ENDIF 12 f803 ENDIF 13 Uf81b SEG.U STRINGS String stored at $f81b 14 Uf81b ECHO "String stored at", . 15 Uf81b 03 .storedLen DC.B <[.length] 16 Uf81c 4f 6e 65 .firstChr DC.B "One" 17 Uf81c f8 1f .lastChr EQU . 18 f803 SEG CODE ... 8 f811 00 brk 9 f812 1f DC.B <.storedLen 10 f813 f8 DC.B >.storedLen 11 f814 ENDIF 12 f814 ENDIF 13 Uf81f SEG.U STRINGS String stored at $f81f 14 Uf81f ECHO "String stored at", . 15 Uf81f 03 .storedLen DC.B <[.length] 16 Uf820 54 77 6f .firstChr DC.B "Two" 17 Uf820 f8 23 .lastChr EQU . 18 f814 SEG CODE ... 103 f815 42 65 66 6f* DC.B "Before" 104 f81b Strings_Org: $f81b 105 f81b ECHO "Strings_Org:", . 106 f81b 107 f81b f8 1b Strings_Org EQU . 108 f81b 109 f81b 41 66 74 65* DC.B "After" Above, you can see the DC.B statements within the listing getting assigned space (Uf81b and Ufb1f), but then in the binary file: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00000000 00 1B F8 78 D8 A2 00 8A A8 CA 9A 48 D0 FB 4C 00 ...x....¨..H..L. 00000010 F8 00 1F F8 40 42 65 66 6F 72 65 41 66 74 65 72 ....@BeforeAfter 00000020 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................ 00000030 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................ You can see there's nothing between "Before" and "After". Perhaps I should post this as a bug on the Dasm GitHub site? It feels like you should be able to switch segments within a macro, and the listing file sure makes it seem like space is being allocated and assigned to the string bytes, but the final emitted bytes do not contain the strings from the macros. It does go through five passes, which seems like it's trying to figure something out, but not quiet getting there in the end. I'm assembling the attached test file using: dasm.exe seg_print.asm -f3 -v0 -Lseg_print.lst -sseg_print.sym -oseg_print.bin seg_print.asm 3.66 kB · 1 download I'm not sure what's going on yet. I wonder if it's related to... Specifically, although you're assembling with -f3, the error you are getting looks like it's -f0 restrictions coming into play. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.