Menu

#59 Unexpected need for --long-address

1.56.2625
closed-invalid
nobody
None
5
2022-11-05
2021-07-31
No

I don't know if this is a bug or Working As Intended. Test file generates eight 8KB segments that all start at $8000, for a total of 64KB:

        .cpu "6502"


*       =       $8000
        .fill   $2000,$00

        .logical $8000
        .fill   $2000,$01
        .here

        .logical $8000
        .fill   $2000,$02
        .here

        .logical $8000
        .fill   $2000,$03
        .here

        .logical $8000
        .fill   $2000,$04
        .here

        .logical $8000
        .fill   $2000,$05
        .here

        .logical $8000
        .fill   $2000,$06
        .here

        .logical $8000
        .fill   $2000,$07
        .here

Assembling with "--nostart --long-address -Wall" I get a 65536-byte output file with everything looking as expected. Without "--long-address" I get a warning:

Test.S:19:23: warning: compile offset overflow [-Wwrap-mem]
         .fill   $2000,$04

and a 65536-byte output file with blocks 04/05/06/07 followed by blocks 00/01/02/03.

If I change "$2000" to "$200" in the code it all works fine, so I assume this has something to do with how much code is being generated rather than how many logical blocks there are. However, a simple test with 65536 NOP instructions (but no ".logical" statements) works fine.

So, two questions:
(1) is this the expected behavior?
(2) under what circumstances do I need to add "--long-address" to the argument list?

Tested with V1.56.2625 and 1.54.1900.

Discussion

  • Soci/Singular

    Soci/Singular - 2021-08-01

    The problem with the above code is that it starts with a * = $8000. Use the .logical $8000 / .here just like for the other 7.

    The reason is that * =$8000 does move both the program counter and the image offset to $8000. I think you only wanted to move the program counter but not the image offset.

    With the code above it starts assembling at the half of the 64KiB image and then wraps around to the beginning so it'll be like: 04, 05, 06, 07, 00, 01, 02, 03.

    It does work with $200 as with that the whole thing becomes less than 32 KiB and so there won't be any wrap around.

    Also by default (without using flat output) the first 32 KiB of the image which was skipped is not saved. So the result looks good even if it was started in the middle.

    When you've selected a 16 MiB image format with --long-address then there's no wrap around in the image. It starts at 32 KiB and stops at 96 KiB. However the PC had still an overflow half way through so there's a warning about that.

    So if it's a 8 x 8 KiB banked image then I'd suggest using the following 8 times:

    .logical $8000
    nop                  ; some code
    .fill $a000 - *, $ff ; fill up unused space, will fail on bank overflow
    .here
    

    No need to set " * " at the beginning as that moves the image offset as well and that needs to stay at 0.

    The PC is set for each bank to $8000 with the .logical/.here.

    The .fill at the end makes sure to pad out any remaining space until $a000 where the 8 KiB bank ends. Without this the next bank will not align to a 8 KiB boundary in the image.

    One could put "* = $0000, * = $2000, * = $4000 ..." in front of each .logical/.here block for alignment however the .fill construct will automatically fail in case the code in the bank got too long.

     
  • Soci/Singular

    Soci/Singular - 2021-08-01
    • status: open --> pending-invalid
     
  • Andy McFadden

    Andy McFadden - 2021-08-01

    Ah. So the assembler is effectively emulating a system loader that populates memory with the assembled output, and then grabs a memory dump of the affected range.

    Seems like I need two output modes, "loadable" and "streaming". "Loadable" is something that could be loaded into a 64K RAM area, so to qualify (start_address + total_length) must be <= 65536 to avoid wrap-around. "Loadable" generated sources begin with "* = addr" and handle logical address changes in the usual way.

    If start+len > 65536, then I need to switch to "streaming" mode. Sources can omit "* = 0" and just wrap each segment in .logical/.here.

    And if len > 65536, I need to be in "streaming" mode and set --long-address.

    The ultimate goal is to generate source code that looks the way people expect it to look, so I do want to start with "* = addr" when it makes sense to do so. In the "loadable" case I can also test for my PRG optimization: if the first two bytes match the start address, I don't generate a data statement for them, and omit --nostart from the args so the assembler generates them.

    Thanks for the info!

     
  • Andy McFadden

    Andy McFadden - 2021-11-15

    Having two different "output modes" was the correct answer, for both 64tass and ACME.

    FWIW, 64tass is one of the few assemblers that can handle Metroid (128KB) as a single source file: https://6502disassembly.com/nes-metroid/

    You're welcome to close this.

     
  • Soci/Singular

    Soci/Singular - 2022-11-05
    • status: pending-invalid --> closed-invalid
     

Log in to post a comment.

MongoDB Logo MongoDB