Back to home page

DOS ain't dead

Forum index page

Log in | Register

Back to the board
Thread view  Mix view  Order
bencollver

Homepage

31.12.2025, 23:25
(edited by bencollver, 01.01.2026, 16:47)
 

Watcom ASM + C puzzle, variable has conflicting addresses (Developers)

I am working on porting Paul Vojta's calvin editor from Turbo-C and A86 to Watcom. I am currently troubleshooting a puzzle related to the asm sources. The file vv.asm has a symbol named "activpg" for one byte that is initialized to 0

            public  activpg
    ...
    activpg db      0                ;active display page
    crspos  dw      450h, 0          ;offset, segment of cursor location
    scaninf db      7, 0             ;bottom scan line and (3=mono, 0=cga)
    vidseg  dw      0b800h           ;segment of video card
    old24   dw      0, 0             ;old INT 24 address
    ...
            mov bx, offset activpg
            mov bh, byte ptr [activpg]


I linked a large memory model executable v.exe

In the v.map memory map, the Segments section includes:

    Segment                Class         Group          Address          Size
    =======                =====         =====          =======          ====
    ...
    _NULL                  CODE          DGROUP         10fc:0000        00000020


And the Memory Map section includes:


    Address        Symbol
    =======        ======
   
    Module: VV.OBJ(C:\CALSRC24\vv.asm)
   
    10fc:0c4c      activpg


I opened v.exe in the Watcom Debugger.

I stepped through the program into ex_version(), into the first botputs(), and into writes(). After stepping through the instruction "mov bx, offset activpg", the CPU registers window shows BX is 0C4C and DS is 3461. Inspecting the memory at 3461:0C4C shows the expected byte 00.

    Memory (0x3461:0x0000)
    0x0C40: 9A 65 23 09 00 00 00 00 00 00 00 00 00 50 04 00
    0x0C50: 00 07 00 00 B8 00 00 00 00 00 03 06 00 06 0B 00


After stepping through the instruction "mov bh, byte ptr [activpg]", the CPU registers window shows BX is 644C. I expected BX to be 004C.

Looking at Code->Assembly i see:

            mov bx, offset activpg
          mov    bx, 0C4C
            mov bh, byte ptr [activpg]
          mov    bh, byte ptr 027C


Inspecting the memory at 3461:027C shows the unexpected byte 64.

Why would Watcom think "activpg" is at two different addresses? (3461:0C4C versus 3461:027C)

jadoxa

Homepage E-mail

Queensland, Australia,
01.01.2026, 02:06

@ bencollver

Watcom ASM + C puzzle, variable has conflicting addresses

> Module: VV.OBJ(C:\CALSRC24\vv.asm)
>
> 10fb:0c7e      activpg
>
> [...]
>
> Why would Watcom think "activpg" is at two different addresses? (3461:0C4C
> versus 3461:027C)

Isn't that three addresses? The map has it at an even address, not a multiple-of-4. But yes, it's very weird that consecutive instructions are using a different address. Can you look at the object file, does that have the two addresses?

Another question is: since the address is in bx, why not just use [bx]?

Rugxulo

Homepage

Usono,
01.01.2026, 03:44

@ jadoxa

Watcom ASM + C puzzle, variable has conflicting addresses

> Can you look at the object file, does that have the two addresses?

Yes, try using wdis on the A86 .obj file to compare.

Japheth

Homepage

Germany (South),
01.01.2026, 09:01

@ bencollver

Watcom ASM + C puzzle, variable has conflicting addresses

> I linked a large memory model executable v.exe

> _NULL CODE DGROUP 10fb:0000

> Why would Watcom think "activpg" is at two different addresses? (3461:0C4C
> versus 3461:027C)

It's almost certainly a memory model conflict.

In the large memory model, code sections aren't part of DGROUP.

More analysis isn't possible, since you didn't provide a full map file.
Wasm doesn't support to create a listing file, but - as Rugxulo suggested - wdis may be used for a disassembly listing.

---
MS-DOS forever!

bencollver

Homepage

01.01.2026, 16:35

@ Japheth

Watcom ASM + C puzzle, variable has conflicting addresses

Thank you for your helpful replies!


jadoxa:

You are right that it was three addresses. I referred to the wrong map file. See the correct map below [1]. It matches the address in my original post.

I only put the address in bx for the purpose of debugging. I realize that i could use [bx] but i believe this problem affects other parts of the code too, so i would like to find the root cause.


rugxulo:

See wdis output below [2].

Here is the section from the wdis output that corresponds to my original post:

            mov     bx, offset activpg
    0199    BB 06 00                  mov         bx,offset DGROUP:activpg
   
    ;       mov     bx,[bp+16]  ;bl = attribute
            mov     bh,byte ptr [activpg]       ;bh = active display page
    019C    8A 3E 06 00               mov         bh,byte ptr activpg


The original program used A86 and the compact memory model. Mine uses wasm and the large memory model.


Japeth:

I am not sure whether i did this correctly, but i assembled the object with the following command:

    wasm -q -0 -bt=dos -ml -d1 vv.asm

I created calvin.lib using the following commands:

    del calvin.lib
    wlib -q calvin.lib +ex.obj +vu.obj +vl.obj +vs.obj +vm.obj +vc.obj
    wlib -q calvin.lib +va.obj +vk.obj +vv.obj +sys3.obj +compat.obj


And created the executable and map using the following command:

    wcl -q -l=dos -ml -d2 -fe=v.exe -fm=v.map wildargv.obj main.obj calvin.lib


[1] v.map

https://termbin.com/4pl7

[2] wdis -l=vv.lis -s vv.obj

https://termbin.com/j11y

Japheth

Homepage

Germany (South),
01.01.2026, 18:57

@ bencollver

Watcom ASM + C puzzle, variable has conflicting addresses

Your map file looks good and the memory models for C and ASM are also ok.

As you already have verified, register DS is correctly setup to DGROUP.

The line in the assembly window:


          mov    bh, byte ptr 027C


suggests that "someone" has modified the code segment, changing offset 0xC4C to 0x27C.

I'm not sure if WD supports write watchpoints - that would surely help here. But first I'd verify that on program entry the interesting code part is still correct, that is, the line "mov bh, [activpg]" is encoded 8A 3E 4C 0C.

---
MS-DOS forever!

bencollver

Homepage

01.01.2026, 21:07

@ Japheth

Watcom ASM + C puzzle, variable has conflicting addresses

> I'm not sure if WD supports write watchpoints - that would surely help
> here. But first I'd verify that on program entry the interesting code part
> is still correct, that is, the line "mov bh, [activpg]" is encoded 8A 3E 4C
> 0C.

See below for a screenshot of the Assembly window on program entry, plus the Memory window. The line "mov bh, [activpg]" is encoded as 8A 3E 7C 02.

[image]

I am still a Watcom newbie, so i don't grok this yet. Looking at the memory map:

The _NULL segment address is 10fc:0000 (10fc0), segment size 0x0020, so the end address is 10fc:0020 (10fe0)

The _DATA segment address is 1199:0006 (11996), segment size 0x09b2, so the end address is 1199:09b8 (12348)

The activpg symbol address is 10fc:0c4c (11c0c), which is outside of _NULL and inside of _DATA

Doing some arithmetic:

10fc:0c4c (11c0c) - 1199:0006 (11996) = 0x0276

If i discard the offset part of the _DATA segment address, then it results in the unexpected value:

10fc:0c4c (11c0c) - 1199:0000 (11990) = 0x027C

But these are just numbers to me, i am not grokking it yet.

boeckmann

Aachen, Germany,
01.01.2026, 21:09

@ bencollver

Watcom ASM + C puzzle, variable has conflicting addresses

> Why would Watcom think "activpg" is at two different addresses? (3461:0C4C
> versus 3461:027C)

Comparing 0x0C4C-0x027C=0x09d0 with the map file indicates that there is some _DATA segment related offset being used while this should be a DGROUP related offset.

As WASM generates group related offsets when encountering something like this:

dgroup group data
data segment
x   db 2
    mov  bh, byte ptr [y]
data ends
end


as shown in the following decoded OMF fixup record (decoded with OMFDUMP, the important part is the "frame method"):

9c FIXUPP16      6 bytes, checksum 7F (valid)
   FIXUP  segment-relative, type 1 (16-bit offset)
          record offset 0005
          frame method F1 (GRPDEF) index 0001 'dgroup'
          target method T4 (SEGDEF) index 0001 'data'


this is indeed puzzling. Can you upload the .OBJ file? Then I can verify via OMFDUMP if the corresponding fixup in the OBJ is correct.

bencollver

Homepage

01.01.2026, 21:32

@ boeckmann

Watcom ASM + C puzzle, variable has conflicting addresses

> this is indeed puzzling. Can you upload the .OBJ file? Then I can verify
> via OMFDUMP if the corresponding fixup in the OBJ is correct.

Here is a .zip archive containing vv.asm and vv.obj:

http://0x0.st/P-wO.zip

Japheth

Homepage

Germany (South),
01.01.2026, 21:51

@ bencollver

Watcom ASM + C puzzle, variable has conflicting addresses

> Here is a .zip archive containing vv.asm and vv.obj:

In assembly, you'll have to tell the assembler in what group the segments are supposed to be. In your case, _DATA must be in DGROUP, the Masm syntax for this is:

DGROUP group _DATA

---
MS-DOS forever!

boeckmann

Aachen, Germany,
01.01.2026, 21:56

@ bencollver

Watcom ASM + C puzzle, variable has conflicting addresses

> > this is indeed puzzling. Can you upload the .OBJ file? Then I can verify
> > via OMFDUMP if the corresponding fixup in the OBJ is correct.
>
> Here is a .zip archive containing vv.asm and vv.obj:
>
> http://0x0.st/P-wO.zip

I inspected the .OBJ file. The fixups are wrong, as they are segment offsets:

    FIXUP  segment-relative, type 1 (16-bit offset)
          record offset 0094
          frame method F5 (TARGET index)
          target method T4 (SEGDEF) index 0002 '_DATA'


There are two ways to fix this: The MASM way is to ASSUME:

    assume ds:DGROUP,es:DGROUP

Put this in front or right after the start of the _TEXT segment. This should instruct WASM to spit out DGROUP related memory addresses. Be careful not to break other code locations.

You may also explicitly indicate that you are referencing DGROUP:

    mov bh,DGROUP:activpg

boeckmann

Aachen, Germany,
01.01.2026, 21:58

@ boeckmann

Watcom ASM + C puzzle, variable has conflicting addresses

> > > this is indeed puzzling. Can you upload the .OBJ file? Then I can
> verify
> > > via OMFDUMP if the corresponding fixup in the OBJ is correct.
> >
> > Here is a .zip archive containing vv.asm and vv.obj:
> >
> > http://0x0.st/P-wO.zip
>
> I inspected the .OBJ file. The fixups are wrong, as they are segment
> offsets:
>
>     FIXUP  segment-relative, type 1 (16-bit offset)
> record offset 0094
> frame method F5 (TARGET index)
> target method T4 (SEGDEF) index 0002 '_DATA'

>
> There are two ways to fix this: The MASM way is to ASSUME:
>
>     assume ds:DGROUP,es:DGROUP
>
> Put this in front or right after the start of the _TEXT segment. This
> should instruct WASM to spit out DGROUP related memory addresses. Be
> careful not to break other code locations.
>
> You may also explicitly indicate that you are referencing DGROUP:
>
>     mov bh,DGROUP:activpg

And as Japheth pointed out a DGOUP definition is missing:

    DGROUP group _DATA

bencollver

Homepage

01.01.2026, 22:54

@ boeckmann

Watcom ASM + C puzzle, variable has conflicting addresses

> > There are two ways to fix this: The MASM way is to ASSUME:
> >
> >     assume ds:DGROUP,es:DGROUP
>
> And as Japheth pointed out a DGROUP definition is missing:
>
>     DGROUP group _DATA

Thank you Japheth and boeckmann!

I notice that in the original source, va.asm has:

    DGROUP group _DATA

and also:

            assume  cs:_TEXT, ds:DGROUP, ss:DGROUP

None of the other asm sources had these.

I added them to vv.asm and now i get the expected offset for symbol activpg.

boeckmann

Aachen, Germany,
01.01.2026, 23:26

@ bencollver

Watcom ASM + C puzzle, variable has conflicting addresses

> and also:
>
>             assume  cs:_TEXT, ds:DGROUP, ss:DGROUP
>
> None of the other asm sources had these.
>
> I added them to vv.asm and now i get the expected offset for symbol
> activpg.

Great! While the GROUP definition it strictly neccessary, depending on the exact assembler and version, the ASSUME is not. A86 seems to do its job without, as does WASM. JWASM (and probably MASM 6+??) however throws an errors like

Error A2089: Cannot access label through segment registers: activpg

without the assume. I think that's a good thing, as it helps preventing the sort of errors you encountered. So perhaps you think about "upgrading" from WASM to JWASM.

samwdpckr

02.01.2026, 08:50

@ bencollver

Watcom ASM + C puzzle, variable has conflicting addresses

mov bx, offset symbol_name loads the BX register with the address of the symbol, which is an immediate number.

mov bh, byte ptr [symbol_name] uses the symbol as a pointer and copies the contents of that memory address into the BH register.

mov bh, byte ptr symbol_name copies the value from the symbol's own memory address into the BH register.

When you are writing assembly, the symbol also probably resides in the code segment, so what you want to do is loading a 8-bit register with the value of that symbol, what you probably want to do is

mov bh, byte ptr cs:symbol_name

In any case you should always make sure that the segment registers point into correct paragraphs. But sometimes it's just more efficient to do a segment override instead of loading the segment registers with new values.

In large memory model, if the symbol is in the DGROUP segment, you first have to load a segment register (either DS or ES, or GS/FS if you use a 386+) with the paragraph address where DGROUP starts before you can access the symbol. Be sure to also save and restore the value of the segment register before modifying it.

Accessing data where DS points to does not need a segment override, but other segment registers need it. In other words, DS is the default segment.

bencollver

Homepage

02.01.2026, 17:42

@ boeckmann

Watcom ASM + C puzzle, variable has conflicting addresses

> JWASM (and probably MASM 6+??) however throws an errors like
>
> Error A2089: Cannot access label through segment registers:
> activpg
>
> without the assume. I think that's a good thing, as it helps preventing the
> sort of errors you encountered. So perhaps you think about "upgrading" from
> WASM to JWASM.

Thanks for the suggestion. I will try JWASM.

bencollver

Homepage

03.01.2026, 18:09

@ bencollver

Watcom ASM + C puzzle, variable has conflicting addresses

> Thanks for the suggestion. I will try JWASM.

This morning i tried JWASM. Everything worked fine except i had to change one line in vv.asm.

OLD:

        es lodsb

NEW:

    IFDEF __JWASM__
        lodsb es:0
    ELSE
        es lodsb
    ENDIF


I figured this out after reading

https://www.boeckmann.io/articles/rasmporting.html

This morning i troubleshat why writing to 0000:0450 (AKA 0040:0050) didn't change the cursor position.

Turns out it DOES change the cursor position, just not within the Watcom debugger. I guess WD.EXE uses DOS4GW and that disables some BIOS functionality. I will need to spend some time with DEBUG.COM

ecm

Homepage E-mail

Düsseldorf, Germany,
03.01.2026, 21:23

@ bencollver

Watcom ASM + C puzzle, variable has conflicting addresses

>
> Turns out it DOES change the cursor position, just not within the Watcom
> debugger. I guess WD.EXE uses DOS4GW and that disables some BIOS
> functionality. I will need to spend some time with DEBUG.COM

Try lDebug. It mimics the line-oriented interface of the original MS-DOS Debug, but you can write it to a secondary video adapter or to a serial port.

---
l

Japheth

Homepage

Germany (South),
04.01.2026, 05:21

@ boeckmann

Watcom ASM + C puzzle, variable has conflicting addresses

> Great! While the GROUP definition it strictly neccessary, depending on the
> exact assembler and version

It's still not 100% clear why DGROUP has to be defined explicitely in this special case. Because OW WASM's -ml cmdline option should declare the large memory model, similar to Masm's ".model large" directive. And the large memory model is supposed to automatically

1) define group DGROUP and segment _DATA
2) put _DATA into DGROUP
3) add ASSUME DS:DGROUP

---
MS-DOS forever!

rr

Homepage E-mail

Berlin, Germany,
04.01.2026, 11:40

@ Japheth

Watcom ASM + C puzzle, variable has conflicting addresses

> It's still not 100% clear why DGROUP has to be defined explicitely in this
> special case. Because OW WASM's -ml cmdline option should declare the large
> memory model, similar to Masm's ".model large" directive. And the large
> memory model is supposed to automatically
>
> 1) define group DGROUP and segment _DATA
> 2) put _DATA into DGROUP
> 3) add ASSUME DS:DGROUP

Maybe it's time to ask which version of the Watcom toolchain Ben is using.

---
Forum admin

bencollver

Homepage

04.01.2026, 15:31

@ rr

Watcom ASM + C puzzle, variable has conflicting addresses

> Maybe it's time to ask which version of the Watcom toolchain Ben is using.

Open Watcom Linker Version 1.9
Portions Copyright (c) 1985-2002 Sybase, Inc. All Rights Reserved.
Created on:       26/01/01 07:10:22


From:

[1] v.map

https://termbin.com/4pl7

Japheth

Homepage

Germany (South),
04.01.2026, 16:21

@ bencollver

Watcom ASM + C puzzle, variable has conflicting addresses

> Open Watcom Linker Version 1.9
> Portions Copyright (c) 1985-2002 Sybase, Inc. All Rights Reserved.
> Created on:       26/01/01 07:10:22


It's actually a OW Wasm v1.9 bug: cmdline option -ml should be a substitute for assembly directive ".model large", but it's implemented inaccurately. With both alternatives, DGROUP is defined and segment _DATA is a member of DGROUP, but the fixup generation for access of an item in _DATA differs:

- .model large: fixups get a "DGROUP frame" (good)
- cmdline option -ml: fixups get a "SEGMENT _DATA" frame (bad)

This bug can be cured for cmdline option -ml by adding an "ASSUME DS:DGROUP" line.

Btw, this bug has been fixed in OW Wasm v2.0.

---
MS-DOS forever!

bretjohn

Homepage E-mail

Rio Rancho, NM,
08.01.2026, 21:53

@ samwdpckr

Watcom ASM + C puzzle, variable has conflicting addresses

> Accessing data where DS points to does not need a segment override, but
> other segment registers need it. In other words, DS is the default segment.

Actually, this isn't true in all cases. For example, some OpCodes such as certain string-related ones (STOSx, MOVSx, INSx, etc.) use ES by default, and anything which uses BP as part of a pointer uses SS by default.

bencollver

Homepage

16.01.2026, 04:44

@ bencollver

Watcom ASM + C puzzle, variable has conflicting addresses

> IFDEF __JWASM__
> lodsb es:0
> ELSE
> es lodsb
> ENDIF

This worked for jwasm, but wasm didn't do what i expected. It silently discarded the segment override prefix. The source code says "es lodsb" but the wdis output shows an unqualified "lodsb".

The following works correctly in both assemblers.

lods byte ptr es:[si]

Back to the board
Thread view  Mix view  Order
23154 Postings in 2179 Threads, 404 registered users (0 online)
DOS ain't dead | Admin contact
RSS Feed
powered by my little forum