CL65 doesn't adjust addresses when using .align

291 views Asked by At

I'm trying write an asm program that uses the .align directive to ensure data doesn't cross page boundaries.

However, while the data is in the correct place in memory, the compiled code doesn't not use the correct address.

According to the docs ( https://www.cc65.org/doc/ld65-5.html )

If an alignment is requested, the linker will add enough space to the output file, so that the new segment starts at an address that is dividable by the given number without a remainder. All addresses are adjusted accordingly. To fill the unused space, bytes of zero are used, or, if the memory area has a "fillval" attribute, that value.

Its this adjustment that doesn't appear to be happening.

To reproduce I have the following config file: (Note 'align' on the DATA256 segment)

# Assembly configuration for R38

FEATURES {
    STARTADDRESS: default = $0801;
}
SYMBOLS {
    __LOADADDR__: type = import;
# Putting "-u __EXEHDR__" on cl65's command line will add a BASIC RUN stub to your program.
#    __EXEHDR__:   type = import;
    __HIMEM__:    type = weak, value = $9F00;
}
MEMORY {
    ZP:       file = "", start = $0022,  size = $0080 - $0022, define = yes;
    ZP2:      file = "", start = $00A9,  size = $0100 - $00A9;
    LOADADDR: file = %O, start = %S - 2, size = $0002;
    MAIN:     file = %O, start = %S,     size = __HIMEM__ - %S;
}
SEGMENTS {
    ZEROPAGE: load = ZP,       type = zp;
    EXTZP:    load = ZP2,      type = zp, optional = yes; # OK if BASIC functions not used
    LOADADDR: load = LOADADDR, type = ro;
    EXEHDR:   load = MAIN,     type = ro, optional = yes;
    CODE:     load = MAIN,     type = ro;
    LOWCODE:  load = MAIN,     type = ro, optional = yes;
    RODATA:   load = MAIN,     type = ro;
    DATA:     load = MAIN,     type = rw;
    DATA256:  load = MAIN,     type = rw, align = $100;
    DATA4k:   load = MAIN,     type = rw, align = $1000;
    BSS:      load = MAIN,     type = bss,                define = yes;
}
FEATURES {
    CONDES: type    = constructor,
            label   = __CONSTRUCTOR_TABLE__,
            count   = __CONSTRUCTOR_COUNT__,
            segment = ONCE;
    CONDES: type    = destructor,
            label   = __DESTRUCTOR_TABLE__,
            count   = __DESTRUCTOR_COUNT__,
            segment = RODATA;
    CONDES: type    = interruptor,
            label   = __INTERRUPTOR_TABLE__,
            count   = __INTERRUPTOR_COUNT__,
            segment = RODATA,
            import  = __CALLIRQ__;
}

With the asm as

   .org $0801                  ; Assembled code should start at $0801
                                ; (where BASIC programs start)
                                ; The real program starts at $0810 = 2064

; 10 SYS 2064
    .byte $0C, $08              ; $080C - pointer to next line of BASIC code
    .byte $0A, $00              ; 2-byte line number ($000A = 10)
    .byte $9E                   ; SYS BASIC token
    .byte $20                   ; [space]
    .byte $32, $30, $36, $34    ; $32="2",$30="0",$36="6",$34="4"
    .byte $00                   ; End of Line
    .byte $00, $00              ; This is address $080C containing
                                ; 2-byte pointer to next line of BASIC code
                                ; ($0000 = end of program)
    .byte $00, $00              ; Padding so code starts at $0810
    cld

    stp
    lda testdata
    rts


.segment "DATA256"
testdata:
    .byte $01, $02, $03, $04

Built using the following commandline:

cl65 --verbose -o build.prg --cpu 65c02 -t cx16 -C asm.cfg -Ln labels.txt -m map.txt -T main.asm

This is the compiled .prg. You can see the 'lda testdata' reads from $0816, which is not an aligned address. The padding to $01, $02, $03 shows there is alignment of the data.

enter image description here

This is confirmed in the debugger.

enter image description here

What am I doing wrong? Or is this a bug in the linker?

1

There are 1 answers

1
Greg King On BEST ANSWER

Don't use the .org directive. The configure file sets that address: STARTADDRESS: default = $0801.

Also, that directive tells the assembler to tell the linker, "don't relocate the following code". That prevents the .segment directive from doing what we expect it to do.