bencollver

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

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

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

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

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

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

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]](http://0x0.st/P-wj.png)
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

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

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

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

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

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

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

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

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

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

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

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

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] |