Menu

[Z80] Manually placing parts of a file at different memory locations

Help
Ronaldo
2016-02-16
2016-10-16
  • Ronaldo

    Ronaldo - 2016-02-16

    Hi,

    I am interested on developing a way to manually locate parts of the code at a given memory location. The idea is giving a programmer an easy way to control memory placement of some parts of its code. Therefore, I came up with this:

    #include <stdio.h>
    
    //////////////////////////////////////////////////////////
    // PIECE OF CODE THAT WE WANT TO PLACE AT 0x6000
    //////////////////////////////////////////////////////////
    
    // Force following code to be absolutely placed at 0x6000
    void dummy_absolute_0x6000 (void) __naked {
      __asm
        .area _0x6000_ (ABS)
        .org 0x6000
      __endasm;
    }
    
    unsigned char* const str1 = "Hello!\r\n";
    unsigned char* const str2 = "This string is at 0x6000 area.\r\n";
    
    void printThings() {
       printf(str1);
       printf(str2);
    }
    
    //////////////////////////////////////////////////////////
    // PIECE OF CODE TO BE ALLOCATED BY THE LINKER (RELATIVE)
    //////////////////////////////////////////////////////////
    
    // Declare following code to be relocatable
    void dummy_relocatable_1 (void) __naked {
      __asm
        .area _CSEG (REL, CON)
      __endasm;
    }
    
    unsigned char* const str3 = "This is relatively located\r\n";
    
    void main(void) {
       printThings();
       printf(str3);
       while (1);
    }
    

    As you can see, the idea was to add some dummy functions that help including assembler directives to locate partes of the code. I know it is kind of a hack, but it seemed to work at first. Previous C code gets compiled into this assembly code:

    ;--------------------------------------------------------
    ; File Created by SDCC : free open source ANSI-C Compiler
    ; Version 3.5.5 #9498 (Linux)
    ;--------------------------------------------------------
        .module main
        .optsdcc -mz80
    
    ;--------------------------------------------------------
    ; Public variables in this module
    ;--------------------------------------------------------
        .globl _main
        .globl _printThings
        .globl _printf
        .globl _str3
        .globl _str2
        .globl _str1
    ;--------------------------------------------------------
    ; special function registers
    ;--------------------------------------------------------
    ;--------------------------------------------------------
    ; ram data
    ;--------------------------------------------------------
        .area _DATA
    ;--------------------------------------------------------
    ; ram data
    ;--------------------------------------------------------
        .area _INITIALIZED
    ;--------------------------------------------------------
    ; absolute external ram data
    ;--------------------------------------------------------
        .area _DABS (ABS)
    ;--------------------------------------------------------
    ; global & static initialisations
    ;--------------------------------------------------------
        .area _HOME
        .area _GSINIT
        .area _GSFINAL
        .area _GSINIT
    ;--------------------------------------------------------
    ; Home
    ;--------------------------------------------------------
        .area _HOME
        .area _HOME
    ;--------------------------------------------------------
    ; code
    ;--------------------------------------------------------
        .area _CODE
    ;   ---------------------------------
    ; Function dummy_absolute_0x6000
    ; ---------------------------------
    _dummy_absolute_0x6000::
        .area _0x6000_ (ABS) 
        .org 0x6000 
    ;   ---------------------------------
    ; Function printThings
    ; ---------------------------------
    _printThings::
        ld  hl,(_str1)
        push    hl
        call    _printf
        pop af
        ld  hl,(_str2)
        push    hl
        call    _printf
        pop af
        ret
    _str1:              ;;; <<<< --- Why are strings put below printThings 
        .dw __str_0       ;;;         instead of above?
    _str2:
        .dw __str_1
    __str_0:
        .ascii "Hello!"
        .db 0x0D
        .db 0x0A
        .db 0x00
    __str_1:
        .ascii "This string is at 0x6000 area."
        .db 0x0D
        .db 0x0A
        .db 0x00
    ;   ---------------------------------
    ; Function dummy_relocatable_1
    ; ---------------------------------
    _dummy_relocatable_1::
        .area _CSEG (REL, CON) 
    ;   ---------------------------------
    ; Function main
    ; ---------------------------------
    _main::
        call    _printThings
        ld  hl,(_str3)
        push    hl
        call    _printf
        pop af
    00102$:
        jr  00102$
    _str3:
        .dw __str_2
    __str_2:
        .ascii "This is relatively located"
        .db 0x0D
        .db 0x0A
        .db 0x00
        .area _CODE
        .area _INITIALIZER
        .area _CABS (ABS)
    

    While it seems to work, there is actually a problem. Compiler rearranges functions and arrays as it considers. In this case, things are still almost in-place, but minimum changes impede this approach to work, like in this second version of the code:

    #include <stdio.h>
    
    //////////////////////////////////////////////////////////
    // PIECE OF CODE THAT WE WANT TO PLACE AT 0x6000
    //////////////////////////////////////////////////////////
    
    // Force following code to be absolutely placed at 0x6000
    void dummy_absolute_0x6000 (void) __naked {
      __asm
        .area _0x6000_ (ABS)
        .org 0x6000
      __endasm;
    }
    
    extern unsigned char* const str1;
    extern unsigned char* const str2;
    
    void printThings() {
       printf(str1);
       printf(str2);
    }
    
    unsigned char* const str1 = "Hello!\r\n";
    unsigned char* const str2 = "This string is at 0x6000 area.\r\n";
    
    //////////////////////////////////////////////////////////
    // PIECE OF CODE TO BE ALLOCATED BY THE LINKER (RELATIVE)
    //////////////////////////////////////////////////////////
    
    // Declare following code to be relocatable
    void dummy_relocatable_1 (void) __naked {
      __asm
        .area _CSEG (REL, CON)
      __endasm;
    }
    
    unsigned char* const str3 = "This is relatively located\r\n";
    
    void main(void) {
       printThings();
       printf(str3);
       while (1);
    }
    

    With this minimum change (str1 and str2 are defined after printThings instead of previously), generated code puts str1 and str2 below main definition, leaving them in the relative area, instead of the absolute area, as wanted:

    ;   ---------------------------------
    ; Function dummy_absolute_0x6000
    ; ---------------------------------
    _dummy_absolute_0x6000::
        .area _0x6000_ (ABS) 
        .org 0x6000 
    ;   ---------------------------------
    ; Function printThings
    ; ---------------------------------
    _printThings::
        ld  hl,(_str1)
        push    hl
        call    _printf
        pop af
        ld  hl,(_str2)
        push    hl
        call    _printf
        pop af
        ret
    ;   ---------------------------------
    ; Function dummy_relocatable_1
    ; ---------------------------------
    _dummy_relocatable_1::
        .area _CSEG (REL, CON) 
    ;   ---------------------------------
    ; Function main
    ; ---------------------------------
    _main::
        call    _printThings
        ld  hl,(_str3)
        push    hl
        call    _printf
        pop af
    00102$:
        jr  00102$
    _str1:             ;;; <<<<---- Strings str1 and str2 have crossed defined
        .dw __str_0      ;;;          areas because of their rearrangement during
    _str2:             ;;;          compilation.
        .dw __str_1
    _str3:
        .dw __str_2
    __str_0:
        .ascii "Hello!"
        .db 0x0D
        .db 0x0A
        .db 0x00
    __str_1:
        .ascii "This string is at 0x6000 area."
        .db 0x0D
        .db 0x0A
        .db 0x00
    __str_2:
        .ascii "This is relatively located"
        .db 0x0D
        .db 0x0A
        .db 0x00
        .area _CODE
        .area _INITIALIZER
        .area _CABS (ABS)
    

    Sometimes, the problem is even worse. Next example defines only an array in a separate file, trying to locate it at 0x6000, using this "hack-method":

    #include "dummy.h" // This is only to force syntax highlighting
    
    //////////////////////////////////////////////////////////
    // PIECE OF CODE THAT WE WANT TO PLACE AT 0x6000
    //////////////////////////////////////////////////////////
    
    // Force following code to be absolutely placed at 0x6000
    void dummy_absolute_0x6000 (void) __naked {
      __asm
        .area _0x6000_ (ABS)
        .org 0x6000
      __endasm;
    }
    
    unsigned char* const array[5] = {1, 2, 3, 4, 5};
    

    But the result includes an unexpected switch to _CODE area, just below dummy function:

    ;--------------------------------------------------------
    ; File Created by SDCC : free open source ANSI-C Compiler
    ; Version 3.5.5 #9498 (Linux)
    ;--------------------------------------------------------
        .module main
        .optsdcc -mz80
    
    ;--------------------------------------------------------
    ; Public variables in this module
    ;--------------------------------------------------------
        .globl _array
    ;--------------------------------------------------------
    ; special function registers
    ;--------------------------------------------------------
    ;--------------------------------------------------------
    ; ram data
    ;--------------------------------------------------------
        .area _DATA
    ;--------------------------------------------------------
    ; ram data
    ;--------------------------------------------------------
        .area _INITIALIZED
    ;--------------------------------------------------------
    ; absolute external ram data
    ;--------------------------------------------------------
        .area _DABS (ABS)
    ;--------------------------------------------------------
    ; global & static initialisations
    ;--------------------------------------------------------
        .area _HOME
        .area _GSINIT
        .area _GSFINAL
        .area _GSINIT
    ;--------------------------------------------------------
    ; Home
    ;--------------------------------------------------------
        .area _HOME
        .area _HOME
    ;--------------------------------------------------------
    ; code
    ;--------------------------------------------------------
        .area _CODE
    ;   ---------------------------------
    ; Function dummy_absolute_0x6000
    ; ---------------------------------
    _dummy_absolute_0x6000::
        .area _0x6000_ (ABS) 
        .org 0x6000 
        .area _CODE       ;;; <<<< --- Why this change to _CODE area?
    _array:
        .dw #0x0001
        .dw #0x0002
        .dw #0x0003
        .dw #0x0004
        .dw #0x0005
        .area _INITIALIZER
        .area _CABS (ABS)
    

    So, the questions are:
    * Is there any way to make compiler generate code in the same order it finds it on the source file?
    * Is there any other proper way or hack to force some code areas to be absolutelly located?
    * Why does compiler rearrange code?

    I already know the existence of the __at keyword, but it only applies to data (not functions / code) and would be less practical that just using a macro at the start of a section, for instance. Moreover, in the Z80 implementation, using __at keyword at different code files does not produce expected results, as some elements get 'absolutelly' located with respect to where the file is placed in the code, and not with respect to memory, as it is expected.

    Thank you very much in advance :).

     

    Last edit: Ronaldo 2016-02-16
  • Philipp Klaus Krause

    Are you aware of the SDCC options

    --codeseg
    --constseg
    

    and linker options for segment placement?

    Philipp

     
  • Ronaldo

    Ronaldo - 2016-10-16

    Thanks Philipp, and apologizes for taking so long to reply (did not receive any email notification or similar).

    Yes, I am aware of --codeseg and --constseg, but that's not what I wanted to do. I'm creating a library for other programmers and wanted to give them control over code placement without changing compiler options (directly from their code).

    I found a "hack" solution for the problem. If I use 2 consecutive dummy functions like this:

    // Force following code to be absolutely placed at 0x6000
    void dummy_absolute_0x6000_catcher (void) {}
    void dummy_absolute_0x6000 (void) __naked {
      __asm
        .area _0x6000_ (ABS)
        .org 0x6000
      __endasm;
    }
    

    The first one generates a label and a RET and acts as "catcher" in the sense that it makes all previous data definitions go after the catcher but previous to the new area and org definitions. This adds 1 byte to the binary per use of this method, but works. I've encapsulated this solution into macros, and that gives other coders access to this functionality directly from their code.

    It's kind of a hack, not "neat" but it works and gives an interesting functionality :).

    Thank you for your help.

     

Log in to post a comment.