April 23, 2016
Hi everyone,

As I'm trying to make DMC working for a 16bit x86 (80186) non PC hardware, I started to make a crt for that target, but once I started to make real program, I got problem with how segments are handled by DMC.

DMC seems to create a full bunch of segments which are good:

_TEXT for code
_DATA for inited RW data
CONST for constant data ?
_BSS for non inited RW data ?

I'm using a memory model which is close to the small and tiny model, but CS, DS and SS are different, as the code is in ROM, data are in a limite memory space (I can't copy the whole executable there, and at the end there could be more than 64K of data and it would be paginated, but that's another problem) and the stack have it's own area.

My first issue is that I don't understand how DMC put data into each segments, and I can't find a way to make sure it does what I want.

It seems to put everything into _DATA, it honour the __cs, but the CONST segment stay empty whatever the code I put.

Here is an example:

int non_init_var;             /* Should be in _BSS    */
int inited_var = 2;           /* Should be in _DATA   */
const int constant_var = 53;  /* Should be in CONST   */

int main(int argc, char *argv[])
   const int const_local_var = 20;              /* Should be in TEXT or CONST */
   static int static_local_var;                 /* Should be in _BSS          */
   static int static_inited_local_var = 5;      /* Should be in _DATA         */
   int local_var = 42;                          /* Should be on stack         */
   printf("Hello World!");    /* Text should be in CONST or in TEXT as it is implicitely const here. */

   /* Just to make sure all the variable are used and are not stripped */
   non_init_var = 29;
   local_var = non_init_var + inited_var + constant_var + const_local_var + static_local_var + static_inited_local_var;
   return local_var;

After building with

dmc -c -a1 -NL test.c

I got this annotated disassembly output from the freeware version of IDA Pro 5:
FLAT:0000 ;
FLAT:0000 ; +-------------------------------------------------------------------------+
FLAT:0000 ; ¦     This file is generated by The Interactive Disassembler (IDA)        ¦
FLAT:0000 ; ¦     Copyright (c) 2010 by Hex-Rays SA, <support@hex-rays.com>           ¦
FLAT:0000 ; ¦                      Licensed to: Freeware version  
FLAT:0000 ; +-------------------------------------------------------------------------+
FLAT:0000 ;
FLAT:0000 ; Input MD5   : F86FF1C59D493A04AE64BE3441AE11FD
FLAT:0000 ; File Name   : C:\users\crossover\Desktop\My Mac Desktop\AC2016\AC2016\test.obj
FLAT:0000 ; Format      : Object Module Format (OMF/Microsoft)
FLAT:0000 ; Module name      : test.c
FLAT:0000 ; MS parameters    :
FLAT:0000 ; Debug info type  : CodeView
FLAT:0000                 .386
FLAT:0000                 .model flat
FLAT:0000 ; ---------------------------------------------------------------------------
FLAT:0000 ; Segment type: Group
FLAT:0000 FLAT            group
extn00:0001 ; Near data, 4 bytes
extn00:0001 ; ---------------------------------------------------------------------------
extn00:0001 ; Segment type: Externs
extn00:0001 ; extn00
extn00:0001                 extrn _non_init_var:byte:4 ; DATA XREF: _main+1Cw
extn00:0005                 extrn __acrtused_con:far
extn01:0006 ; ---------------------------------------------------------------------------
extn01:0006 ; Segment type: Externs
extn01:0006 ; extn01
extn01:0006 ; int printf(const char *,...)
extn01:0006                 extrn _printf:near      ; CODE XREF: _main+12p
_TEXT:00000007 ; ---------------------------------------------------------------------------
_TEXT:00000007 ; Segment type: Pure code
_TEXT:00000007 _TEXT           segment dword public 'CODE' use32
_TEXT:00000007                 assume cs:_TEXT
_TEXT:00000007                 ;org 7
_TEXT:00000007                 assume es:nothing, ss:nothing, ds:nothing, fs:nothing, gs:nothing
_TEXT:00000007 ; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ S U B R O U T I N E ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦
_TEXT:00000007 ; Attributes: bp-based frame
_TEXT:00000007                 public _main
_TEXT:00000007 _main           proc near
_TEXT:00000007 local_var       = dword ptr -4
_TEXT:00000007                 enter   4, 0
_TEXT:0000000B                 push    ebx
_TEXT:0000000C                 mov     eax, 20
_TEXT:00000011                 mov     [ebp+local_var], eax
_TEXT:00000014                 push    offset aHelloWorld ; "Hello World!"
_TEXT:00000019                 call    _printf
_TEXT:0000001E                 mov     ecx, 29
_TEXT:00000023                 mov     dword ptr ds:_non_init_var, ecx
_TEXT:00000029                 inc     ds:static_local_var
_TEXT:0000002F                 mov     edx, ds:_inited_var
_TEXT:00000035                 lea     ebx, [ecx+edx]
_TEXT:00000038                 add     ebx, ds:_constant_var
_TEXT:0000003E                 add     ebx, [ebp+local_var]
_TEXT:00000041                 add     ebx, ds:static_local_var
_TEXT:00000047                 add     ebx, ds:static_inited_local_var
_TEXT:0000004D                 mov     eax, ebx
_TEXT:0000004F                 add     esp, 4
_TEXT:00000052                 pop     ebx
_TEXT:00000053                 leave
_TEXT:00000054                 retn
_TEXT:00000054 _main           endp
_TEXT:00000054 _TEXT           ends
_DATA:00000005 ; ---------------------------------------------------------------------------
_DATA:00000005 ; Segment type: Pure data
_DATA:00000005 _DATA           segment dword public 'DATA' use32
_DATA:00000005                 assume cs:_DATA
_DATA:00000005                 ;org 5
_DATA:00000005                 public _inited_var
_DATA:00000005 _inited_var     dd 2                    ; DATA XREF: _main+28r
_DATA:00000009                 public _constant_var
_DATA:00000009 _constant_var   dd 53                   ; DATA XREF: _main+31r
_DATA:0000000D static_inited_local_var dd 5            ; DATA XREF: _main+40r
_DATA:00000011 ; char aHelloWorld[]
_DATA:00000011 aHelloWorld     db 'Hello World!',0     ; DATA XREF: _main+Do
_DATA:00000011 _DATA           ends
CONST:0000000E ; ---------------------------------------------------------------------------
CONST:0000000E ; Segment type: Zero-length
CONST:0000000E CONST           segment dword public 'CONST' use32
CONST:0000000E CONST           ends
_BSS:0000000F ; ---------------------------------------------------------------------------
_BSS:0000000F ; Segment type: Uninitialized
_BSS:0000000F _BSS            segment dword public 'BSS' use32
_BSS:0000000F                 assume cs:_BSS
_BSS:0000000F                 ;org 0Fh
_BSS:0000000F                 assume es:nothing, ss:nothing, ds:nothing, fs:nothing, gs:nothing
_BSS:0000000F static_local_var dd ?                   ; DATA XREF: _main+22w
_BSS:0000000F                                         ; _main+3Ar
_BSS:0000000F _BSS            ends
_BSS:0000000F                 end

What I see here is that the CONST segment is absolutely empty, especially with all things that should be treated as constant (because they are explicitly or implicitly declared as constant, the text given in parameter to printf should be constant for exemple)

and two variable defined in this C file, but not initialised instead of going into the BSS are put as extern? That clearly not what should be expected.

I've run this with 32bit x86 output, but using all memory model for 16bit DOS does the same.

The fact that on some system and that some linker may merge CONST and DATA segments is possible, as on some system everything is in RAM anyway, but that should not be the C compiler that do such a thing.

I should only copy the non constant variables into the RW DATA RAM, and not all the constant one that could be really big on some project.
Is this a bug in DMC?
Is this something done on purpose? If yes, how can I force it to use the CONST segment for everything which is constant, and make sure it does not create extern for variable that are clearly defined in the current file.

By the way, I also have a few thing I don't understand with the optlink:
I've created a third group to represent a specific area in memory:

section PRAM      class=IRAM location=00000h
            resb  0200h ; Put at byte 200h
_tickL:     resw    1   ; Low word of tick counter
_tickH:     resw    1   ; High word of tick counter

and optlink seems to have merged DGROUP with IRGROUP where I should not expect this at all as in one C file that use _tickL/_tickH, instead of referencing them as a far pointer (ds is not set to this section) it just merge that section with the DGROUP, which is not the behaviour I expected.

Similar problem, I've tried to use the trick to get the size of the RW data by putting a pointer at the beginning of the DGROUP, and use the ENDDATA section to put an end pointer and use them to copy the rw data into the memory, but doing that as when the assembler build the CRT, it does not now where DRGOUP is about to be put in the resulting file, and create a relocation marker to say "I still don't know where this is to be put" but then, when linking optlink does not replace that marker with the real value so I end with a file with relocation, which is not something I want as I'm currently using exe2com (updated to work on 64bit computer) which does not want relocatable EXEs.

Is there is a way to copy the DGROUP into memory without generating a relocation?