Jump to content
IGNORED

Dasm strings and macros


SmileyDude

Recommended Posts

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 by SmileyDude
typo
Link to comment
Share on other sites

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").

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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 by Kevin McGrath
  • Like 1
Link to comment
Share on other sites

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?

 

  • Like 1
Link to comment
Share on other sites

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?

 

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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...

 

320816891_ScreenShot2020-09-10at6_41_31am.thumb.png.38beb57775e35fcd20f82c0949303fdb.png

Specifically, although you're assembling with -f3, the error you are getting looks like it's -f0 restrictions coming into play.

 

 

 

Link to comment
Share on other sites

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.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...