User Tools

Site Tools


blog:pushbx:2026:0309_early_march_work_on_ldebug_warplink_ident86_and_patchini

Early March work on lDebug, WarpLink, ident86, and patchini

2026-03-08

This week I uploaded a new branch to the lDebug repo, updating the sources to build with a makefile that assembles multiple object files, then links them using the WarpLink linker.

The bug caused random corruption in the lDebug 386-related patch list. It didn't appear in prior testing because of the minimum amount of EXTDEF entries needed to activate it.

tractest/convlist.pl

  • Allow to disable the usesection detection for lDebug. This requires a NASM revision with the %note directive. If the disable note is found, it is assumed all usesection directives will emit a corresponding note. This is useful because a usesection directive in a disabled conditional assembly branch won't be misdetected as changing the section.

NASM revisions

The older NASM revision that we use is 03490692b0082fe16a1936a5774f4326a55075e6, "2.16.02rc2", of 2023-10-11. It doesn't support the %note directive. In addition, it mistakenly silently ignored a doubled extern label reference (ie, mov dx, 2*N_LTABO-2), assembling it to the scalar value (-2) only without generating any fixups.

On 2026-03-06 I tried out the then most recent git revision, b1909cf39b8028b72cf527ba617a940e329ea05c, "3.02rc2", of 2026-03-04. However, I found that it misassembles instructions like cmp al, 4 to the three-byte machine code 80 F8 04 rather than the two-byte 3C 04 that I expected.

Next I tried an older revision, 9b18e9a9134fc5bb3ed0df631c1012c647d379e2, "3.00rc9", of 2025-09-19. However, it doesn't work to declare a label extern with this revision.

I finally settled on 755593b12844a3189dcab7faf3e9158da0877021, "3.01rc3", of 2025-10-06. This supports the %note directive and everything else needed for building lDebug. It does swap around some instances of multiple prefixes, eg a rep prefix + a32 or o32 + a32. For the doubled extern label reference, it correctly emits the error: invalid operand type.

ident86

The following changes are to the main ident86 tool:

The wlcalc replace switch

Note that the -w switch implies that the file to "edit" should be the original binary that is recreated by the build scriptlet, and the "original" file should be the output of the markwlc.pl script. ident86 will check that the "edit" binary doesn't change from the build. The .tls file needs to be updated to allow the -w run to proceed. (I did this wrongly when actually using this feature, but it is a forgiving error.)

The purpose of the -w switch is to edit the sources of the program (lDebug in this case). It inserts a "relocatedzero +" term into the instruction that generated a wlcalc-affected word or dword field that isn't marked as relocated in the .tls file. We want these fields to be marked as relocated in order for TracList to match these instructions, despite the wlcalc request having changed the literal bytes of the instruction.

The actual use of the ident86 -w switch is documented in the lDebug changeset generated by it:

cp -a ../tmp/ddebugx.big edit.big &&
  ~/proj/ident86/markwlc.pl ../lst/ddebugx.map edit.big --prefix=256
~/proj/ident86/ident86.py ../tmp/ddebugx.big edit.big \
  ../lst/ddebugx.tls ../lst/ddebugx.map \
  -s -o -256 -S -e 2 -x -w -p '^.*\/::' -p '\.lst$::.asm' -n -E \
  -r -b 'make NASM=~/proj/nasm-755593b12844a3189dcab7faf3e9158da0877021/nasm' \
  -c cookie -W; beep

As noted in the changeset message as well, this ran for longer than 60 minutes. (Of course, that time would have been shorter if I had already used the quicker convert listing script.)

Example of multi-line disassembly

Assembling the ddebugx.big image _LINK build of the most recent lDebug revision, f39e3d0d4990 (of 2026-03-08), using first the more recent working NASM revision as in make CONVLIST=~/proj/tractest/clquick.pl NASM=~/proj/nasm-755593b12844a3189dcab7faf3e9158da0877021/nasm and copying the .big, .tls, and .map files to the t subdirectory. Then run touch *.asm and rerun make with the 03490692b0082fe16a1936a5774f4326a55075e6 NASM revision.

Next set up the older ident86:

hg clone ~/webrepos/ident86/
hg -R ident86/ up 8f7e
ln -s ~/proj/ident86/ldebug.com ident86/
ln -s ~/proj/ident86/ldebug.img ident86/
ln -s ~/proj/ident86/extlib.eld ident86/

Then run it: ./ident86/ident86.py -s t/ddebugx.big ../tmp/ddebugx.big t/ddebugx.tls t/ddebugx.map -o -256

(We're using the newer NASM's .tls file because that fixes the usesection directives in not taken conditional branches.)

After a moment, the run comes across the multi-line disassembly:

...
005DDC  first:67 != second:66
005DDD  first:66 != second:67
005DEB  first:67 != second:66
005DEC  first:66 != second:67
005DDC up to below 005DFC, first=005DDC last=005DEC
Repeating due to malformed line 1 "4B17:0100 6766C74530AC9C00"
Repeating due to malformed line 1 "4B17:0100 6766C74530AC9C00"

Finally, after repeating this error several times, the program throws an exception "LogicError: Repetition limit reached". Following the chain of the instruction: 005DDCh plus the .big file origin of 100h is 005EDCh. Running the following:

~/proj/tractest/sortmap.pl t/ddebugx.map –skip-empty

We find the relevant segpart:

...
LDEBUG_DATA_ENTRY DATAGROUP      s=05D70h l=0088h a=16  ddebugx.obj/pmdata
LDEBUG_DATA_ENTRY DATAGROUP      s=05DF8h l=01F0h a=4   ddebugx.obj/pmentry
LDEBUG_DATA_ENTRY DATAGROUP      s=05FE8h l=0181h a=2   ddebugx.obj/pminit
...

So we open t/ddebugx.tls (the correct, more recent NASM's .tls file) and skip to where it says "pmentry.lst", then skip to "5EDC ". This points to the following instruction: mov dword [ ebp + fr32_eip ], debuggerexception

Opening the .big image in the debugger allows us to look at the disassembly:

$ dos "ldebug /f /e t\\ddebugx.big"
About to Execute : ldebug /f /e t\ddebugx.big
lDebug: Warning, unknown filename extension specified!
lDebug (2026-02-01)
-u 5EDC
2B5F:5EDC 6766C74530AC9C00
          00                mov     dword [30+ebp], 00009CAC
2B5F:5EE5 660FB706600C      movzx   eax, word [0C60]
2B5F:5EEB 676689453C        mov     [3C+ebp], eax
2B5F:5EF0 67806539FE        and     byte [39+ebp], FE
2B5F:5EF5 EB42              jmp     5F39
2B5F:5EF7 C606600202        mov     byte [0260], 02
-
2B5F:5EFC 800ED90108        or      byte [01D9], 08
2B5F:5F01 6766C74530DF6500
          00                mov     dword [30+ebp], 000065DF
2B5F:5F0A 678C4D34          mov     [34+ebp], cs
2B5F:5F0E 67806539FC        and     byte [39+ebp], FC
2B5F:5F13 6766C7453CA80E00
          00                mov     dword [3C+ebp], 00000EA8
-

So the offending instruction is a32 o32 opcode modrm disp8 imm32, for a total of 9 bytes, just shy of fitting into the expected single-line disassembly. (As mentioned for the NASM revisions, the two prefixes are reversed between these two builds.)

patchini

  • Initial commit of a NASM/8086/DOS port of patchqry. The intention is to provide a replacement for the patchqry (and eventually, patldos) tool when it is to be run on a DOS system. The assembly language port easily needs a fraction of the disk space of the gcc-ia16 built C language program. The C program will be retained as it can be used for other platforms as well.
  • In patldos.c fix a comment missing a closing parenthesis.
  • Fix to allow a 16-bit number in patldos.c (for SCANMODE). Bug caused by a prior changeset (on 2026-01-16) which extracted the function parsenumber. This also adds the bytes parameter to the function in both patchqry.c and patldos.c (always a constant 1 in patchqry).
  • Add ?bytes parameter to the assembly language function parsenumber. Also use the lret mmacro instead of a retn instruction, this causes the function to discard its parameters from the stack upon returning.
  • In patldos.c do not re-open the file upon looping. (Incomplete: The call to patchqry code may re-open the file once again.)
  • Initial commit of patldos.asm, the NASM 8086 DOS port of the patldos.c program. Doesn't integrate the patchqry.asm tool yet.

Apart from all the boring bits of the ports, I had to get the DOS application command line into a format that resembles an argv array expected by the C code, that is a list of ASCIZ strings. In both programs, an argv loop runs after the processing of the kernel pathname, and rewrites the command line to be a list of ASCIZ strings terminated by an empty string. In patldos.asm there is an additional function called cut, which creates a single ASCIZ string out of one parameter early on.

lDebug

The link branch contains updates to allow building the .big image either using the original mak.sh script (as a single-file assembly language stream) or using a makefile that assembles multiple object files and links them to create the .big image.

Overview of some specific changes (not all):

nasm -f obj expr.asm -I ../../lmacros/ 2>&1 |
  perl -ne '
    if (/error: symbol .(\S+)'"'"' not defined/) {
      print "$1\n";
    }' | LC_ALL=C sort | LC_ALL=C uniq | perl -e '
      my $prefix = " extern ";
      my $counter = 1;
      my $length = 0;
      while (<<>>) {
        chomp;
        if (not ($counter % 6) or ($length + 2 + length($_) > 71)) {
          $length = 0;
          $counter = 1;
          $prefix = "\n extern ";
        };
        print "$prefix$_";
        $length += length("$prefix$_");
        $prefix = ", ";
        $counter += 1;
      };
      print "\n";'
make 2>&1 | perl -ne '
    if (/error: symbol .(\S+)'"'"' not defined/) {
      print "$1\n";
    }' | LC_ALL=C sort | LC_ALL=C uniq | tee -a temp.txt | perl -e '
      my $prefix = "\n extern ";
      my $counter = 1;
      my $length = 0;
      while (<<>>) {
        chomp;
        if (not ($counter % 6) or ($length + 2 + length($_) > 71)) {
          $length = 0;
          $counter = 1;
          $prefix = "\n extern ";
        };
        print "$prefix$_";
        $length += length("$prefix$_");
        $prefix = ", ";
        $counter += 1;
      };
      print "\n";' >> temp.mac
  • In wllist and wlcalc mmacros, include a filename in the distinguishing string to avoid cross-file collisions caused by using the same namespace with the NASM macro-local counter resetting for each file.
  • Duplicate some equates (eg). NASM won't generate a definition in the object file when trying to define a global label in terms of an equate that references an extern label. (The fact that it silently fails is concerning.)
  • Create a .tls file using convlist.pl
  • Byte-align most sections by default (in sections.mac), relying on NASM sectalign to use larger alignment only as needed. debug.asm, as the first object module passed to the linker, paragraph-aligns all sections.
  • Use the WarpLink r19 itoa wlcalc feature to expand the /Y= switch maximum value in the command help message.
  • Fix boot message segment being placed too early so some of it was overwritten by datastack section.
  • In uu.asm replace some doubling extern label references by wlcalc. A newer NASM revision than our default detected these as an error. The older revision silently ignored the references, generating wrong code.
  • Add relocatedzero to many wlcalc operations otherwise not marked as relocated in the .tls file. This used the new -w switch to ident86 so as to do the required edits (minus the global/extern directives for relocatedzero).
  • Make DDebug build with _DUALCODE enabled, this adds a new wllist patch list for the immediate far calls from one code section to the other. _DUALCODENEARDUAL thunks are duplicated into every object file needing them (about 50 errors of duplicate thunks if they're created with global labels). wllist mmacro accepts a filename parameter to avoid uses of the mmacro generating "debug.mac" as the filename.
  • Emit thunk extern labels. This is purely to help enumerating the thunks created. To avoid dd.asm included files generating multiple thunks to the same function, the NASM command passes a SOURCEFILENAME define on the command line, which is used to generate the thunks. (This may be re-used later for wlcalc and wllist so as to avoid FILE that may contain path separators.)

The .big image generated by the makefile can be used by the original mak scripts by passing the variable use_build_compress_only=1 to the script.

The object files assemble very quickly. The linker needs about 6 seconds to run. Running both the linker and convlist.pl to generate the .tls file needs nearly 30 seconds, not much faster than the single-file build. I am considering optimisations to convlist.pl to speed it up. (ETA: Now landed in clquick.pl, I'll write about it in the next post.)

You could leave a comment if you were logged in.
blog/pushbx/2026/0309_early_march_work_on_ldebug_warplink_ident86_and_patchini.txt · Last modified: by ecm