Something else for today: Because the Düssel-Café was closed for carnival last Sunday, today I want to list and discuss the ldebug.sld startup files that I have been using recently or not so recently. Plus, the scriptlet I use to enable booting different files in dosemu2 without having to move around a lot of files nor to re-configure dosemu2. This scriptlet allows to boot into lDebug's debugger command prompt rather than chainloading the FreeDOS kernel immediately.
The following is an ldebug.sld startup file from my diskm710.img (MS-DOS v7.10 boot diskette) image, stored in my local (not web accessible) ~/test/20240224 and ~/test/20250130 directories.
:applicationstartup install getinput, biosoutput, rhighlight, rh, quietinstall, quickrun ext extlib.eld extname install ext extlib.eld path install ext extlib.eld quit install ext extlib.eld aformat install @; 26A1:0335 B02D mov al, 2D @; 26A1:0337 F606E10101 test byte [01E1], 01 @s dcode1seg:0 l 1000 B0 '-' F6 06 @if (src != 1) then goto :eof @r byte [srs:sro + 1] = #'^'
This enables the GETINPUT, BIOSOUTPUT, RHIGHLIGHT, RH mode, quiet install,
and QUICKRUN nouns. Then it installs some Extensions for lDebug.
Finally, it searches the debugger code segment (dcode1seg
)
for a sequence that is used to set the default debugger prompt.
If the sequence is found exactly once,
the dash text byte is replaced by a caret.
This is the ldebug.sld file in the ~/proj/ldebug/bin/ subdirectory:
:bootstartup ; Welcome to lDebug! r dco or= 4000 e 3F:0 EA r dword [3F:1] := ri16p r dword [0:16*4] := 3F*10000 ; boot protocol ldos lddebug.com // r DSPVI := 0C; r DSPVM := 2#0001_0000; r DSPVP := 3F8; r dco or:= 4000 bp at ptr ri16p boot protocol freedos cmdline=1 kernel.sys // r g r f CY goto :eof @:applicationstartup @:devicestartup ; Welcome to lDebug! @if (dauxbuflen <= 9 * #1024) then goto :noaux h as bytes dauxbuflen @:noaux @r dco or= 800 @ext aformat.eld install @ext extname.eld install
On boot, this file enables serial I/O early. Then it writes a placeholder interrupt handler at address 003F0h which just far jumps to the next interrupt 16h handler, then sets the vector to run this. A commented out boot command would load lDDebug with serial settings for connecting to the COM1 port rather than the default COM2. Then a permanent breakpoint is set on the (new) interrupt 16h handler's entrypoint. After this, FreeDOS is booted from kernel.sys with an empty (but present) kernel command line. The registers are dumped, the kernel is run until the debugger check, and the debugger presence is indicated by setting CY (Carry Flag set). Then the boot startup section is done.
On application or device driver startup, a welcome message is displayed. If the auxiliary buffer is larger than 9 KiB then its size is displayed using the H AS BYTES command.
Finally, GETINPUT is enabled (using the Debugger Common Options flag 800h) and the AFORMAT Extension for lDebug as well as the EXTNAME Extension for lDebug are installed. These are convenience ELDs.
The lddebug.sld file and the lcdebug.sld file contain exactly the same as the application/device startup of ldebug.sld except for the different names in the welcome message.
This is the file that I use in my ~/.dosemu/drive_c/ directory, used when lDebug is loaded in dosemu2. The file rxdos.com
is a symlink to the current ldebug.com build in the ~/proj/ldebug/bin/ subdirectory. This name is preferred by dosemu2 over FreeDOS's kernel.sys, and uses the RxDOS.3 load protocol. (This protocol is equivalent to the lDOS load protocol, originally defined for bootable lDebug.)
I use the following scriptlet to start dosemu2 in such a way as to enable access to the debugger terminal at boot time:
echo foo > ~/.dosemu/drive_c/ldbgterm.tmp; (sleep 4; rm ~/.dosemu/drive_c/ldbgterm.tmp) & ~/proj/patchini/patchdeb ~/proj/edrdos/repo/bin/edrdos.com +15 -2; ~/proj/patchini/patchdeb ~/proj/msdos4.hg/hg/bin/ldos.com +0xF -2; cp -at . ~/proj/msdos4.hg/hg/bin/ldos.com ~/proj/ldebug/bin/extlib.eld; ~/local.new/bin/dosemu -I "serial { com 2 device /tmp/vptty-dos }" -I "floppy { device ../../proj/ldebug/bin/diskette.img }" -dumb -td -kt -D+9x; stty sane
The same scriptlet again but with linebreaks inserted:
echo foo > ~/.dosemu/drive_c/ldbgterm.tmp; (sleep 4; rm ~/.dosemu/drive_c/ldbgterm.tmp) & ~/proj/patchini/patchdeb ~/proj/edrdos/repo/bin/edrdos.com +15 -2; ~/proj/patchini/patchdeb ~/proj/msdos4.hg/hg/bin/ldos.com +0xF -2; cp -at . ~/proj/msdos4.hg/hg/bin/ldos.com ~/proj/ldebug/bin/extlib.eld; ~/local.new/bin/dosemu -I "serial { com 2 device /tmp/vptty-dos }" \ -I "floppy { device ../../proj/ldebug/bin/diskette.img }" -dumb -td -kt -D+9x; stty sane
echo
at the beginning serves as a sentinel. The file must exist and be not empty to be detected by the IF EXISTS Y ldp/ldbgterm.tmp
command later.rm
command in a subshell serve to reset the sentinel file so it won't be detected any longer by other dosemu2 processes starting up. This was the first step at automatic cleanup to avoid interfering with other users.patchdeb
uses configure lDOS flavour kernels to set the lCFG option flags "Check debugger", "Check only valid", and "Check only IISP", and clear "Assume debugger present".cp
updates the lDOS kernel file and the extlib.eld Extensions Library Extension for lDebug file, placed in the root directory of the dosemu2 C: drive.-dumb -td -kt
are used to run my preferred terminal-only mode of dosemu2.-D+9x
is for debugging dosemu2's XMS services.stty sane
helps fix the terminal in some cases.Here the file in full:
:bootstartup ; Welcome to lDebug! @r ysf |= 4000 if not exists y ldp/ldbgseri.tmp then goto :loopend r v0 0 :loop if (v0 >= 8) then goto :loopend r v1 = word [40:v0] if not (v1) then goto :loopnext r dspvp = v1 r v2 = 0 if (v1 == 2F8) then r v2 = 3 if (v1 == 3F8) then r v2 = 4 if not (v2) then goto :loopnext r dspvi = 8 + v2 r dspvm = 1 << v2 r dspvp . r dco |= 4000 if (dco & 4000) then goto :end :loopnext r v0 += 2 goto :loop @:applicationstartup @:devicestartup @install getinput @install quietinstall @ext extlib.eld quit.eld install @install quickrun @goto :eof :loopend if exists y ldp/ldbgterm.tmp then goto :end :runfreedos if not exists y ldp/fdkernel.sys then goto :endnowait boot protocol=freedos ldp/fdkernel.sys ; sleep 5 seconds if (rc == 0 && ioi == 0) then q :end install quietinstall ext extlib.eld printf install printf "Press any key to enter debugger terminal!\x07\r\n" r v0 = #10 * #18 :wait r v0 -= 1 if (ioi != 0) then goto :terminal printf "\r%2u.%02u" (v0 / #18) (v0 * #100 / #18 % #100) sleep 1 ticks if (v0) then goto :wait printf "\rTime is up\r\n" goto :runfreedos :terminal if (byte ioi == 20) then r v0 = iok printf "\rTimer stopped\r\n" ext extlib.eld set run /e vldfoo=printf uninstall ext extlib.eld set run vldfoo= :endnowait rv ;install quietinstall ext extlib.eld alias.eld install alias add fred boot protocol freedos ldp/fdkernel.sys alias add edr boot protocol ldos ldp/edrdos.com ; alias add ldos boot protocol ldos ldp/lmsdos.com // config ldos.ini alias add ldos boot protocol ldos . // . alias add ldosr boot protocol ldos . // append devicehigh srdxms.sys alias add ldoshigh boot protocol ldos ldp/lmsdos.com // config ldoshigh.ini alias add oldldos boot protocol ldos oldldos/lmsdos.com // config ldos.ini alias add ms5 boot protocol msdos6 ldp/io5.sys alias add tepe tp cccccc while cs != 70 silent 1 alias add seri install serial timer alias add dmm dm word [ri31s ?? ri31s :: ri27s:24] alias add denv d word [es:bx]:0 alias add zap if (byte [cs:ip - 1] == CC) then e cs:ip - 1 90 alias add strat dosstrat force alias add voone r v0 := ext extlib.eld dm.eld install ext extlib.eld dosstrat.eld install rc.abort ; rc.replace ldos; g; r f cy; tp FFFFFF while cs == 70 silent 1; r v0 = cs; tp FFFFFF while cs == v0 silent 1 ; rc.replace ms5; g; g C13E:0219; t; e es:bx + 3 as words 100; e es:bx + E as words 0, DFFF - v0 ; rc.replace ldoshigh; g; r f CY; tp fffff while cs < 4000 silent 1 rc.replace ldos; g; r f CY; tp fffff while cs < 4000 silent 1 @ext extlib.eld quit.eld install @install quickrun
:bootstartup ; Welcome to lDebug!
A little advertisement. The commented line is displayed as debugger input, as by default the debugger displays every script command as it is read.
@r ysf |= 4000
Disable display of subsequent script command debugger input.
if not exists y ldp/ldbgseri.tmp then goto :loopend r v0 0 :loop if (v0 >= 8) then goto :loopend r v1 = word [40:v0] if not (v1) then goto :loopnext r dspvp = v1 r v2 = 0 if (v1 == 2F8) then r v2 = 3 if (v1 == 3F8) then r v2 = 4 if not (v2) then goto :loopnext r dspvi = 8 + v2 r dspvm = 1 << v2 r dspvp . r dco |= 4000 if (dco & 4000) then goto :end :loopnext r v0 += 2 goto :loop
This code, enabled by another sentinel file called ldbgseri.tmp
,
checks for the first installed serial port and sets up
the debugger's serial settings accordingly.
By setting the DCO (Debugger Common Options) flag 4000h the loop enables the serial port I/O. If this command returns with the same flag cleared again, then the timeout of the KEEP prompt was reached and thus this port might be unusable. So the loop continues then.
@:applicationstartup @:devicestartup @install getinput @install quietinstall @ext extlib.eld quit.eld install @install quickrun @goto :eof
GETINPUT is useful when running on DOS, so that instead of the default interrupt 21h function 0Ah line editor the debugger uses its own line editor. This comes with better editing and history recall.
QUIETINSTALL quietens the installation messages of Extensions for lDebug.
quit.eld adds the QUIT command, which tries to shut down the machine. Combining it with installing QUICKRUN means that the QUIT command will not require a RUN keyword to actually run.
The GOTO causes the Script for lDebug file to be closed as if the end was reached.
:loopend if exists y ldp/ldbgterm.tmp then goto :end :runfreedos if not exists y ldp/fdkernel.sys then goto :endnowait boot protocol=freedos ldp/fdkernel.sys ; sleep 5 seconds if (rc == 0 && ioi == 0) then q
If the sentinel exists, go to the :END label. This label is named a little confusingly, as it is merely the end of the automatic loading, not the end of the Script for lDebug. Probably chosen due to historical reasons.
If the sentinel doesn't exist, immediately try to load the FreeDOS kernel. If no error occurred (and no keypress is buffered yet) then run the Q command to uninstall the resident debugger and start executing the currently loaded debuggee. In this case, the FreeDOS kernel.
If the load of the kernel fails, fall through to the :END label.
:end install quietinstall ext extlib.eld printf install
Installing the print formatted Extension for lDebug once allows to rerun it without having to load the extlib.eld off the disk repeatedly.
printf "Press any key to enter debugger terminal!\x07\r\n"
Display a message prompting the user to enter a keypress. The beep was added for good measure to remind the user that input is expected.
r v0 = #10 * #18 :wait r v0 -= 1 if (ioi != 0) then goto :terminal printf "\r%2u.%02u" (v0 / #18) (v0 * #100 / #18 % #100) sleep 1 ticks if (v0) then goto :wait printf "\rTime is up\r\n" goto :runfreedos
This loop is the next step in avoiding
interfering with other dosemu2 users on the same server.
(Chiefly, the automatic build cronjobs.)
It sets up a time out of roughly 10 seconds (thus #10 * #18
ticks).
Every tick, the IOI (Input Output check for Input variable) is queried.
If a keypress is detected, then the command to go to the :TERMINAL label is run.
After each check, printf.eld is used to display a running timer with centiseconds precision. (Because we deal in ticks, the precision is higher than the actual granularity of the loop condition.) The display uses a leading Carriage Return to overwrite the same line repeatedly rather than append the text after it.
After the display, the debugger's SLEEP command is used to delay for a duration of approximately one tick. This needn't be too exact, we just want to achieve a maximum loop duration of about 10 seconds.
Finally, if the time is up then FreeDOS is booted as if the sentinel file never was detected to begin with. This is the crucial part for not interfering: Even if another dosemu2 process finds the sentinel, absent any input it will delay for 10 seconds before booting as usual.
:terminal if (byte ioi == 20) then r v0 = iok printf "\rTimer stopped\r\n" ext extlib.eld set run /e vldfoo=printf uninstall ext extlib.eld set run vldfoo= :endnowait
This is the code that runs upon keypress detection. First, if the keypress is an ASCII blank then it is read using the IOK (Input Output eat Keypress variable). A message indicating that the timer has been stopped is displayed.
Then, the resident printf.eld is uninstalled.
Instead of directly running printf uninstall
this uses
the SET Extension for lDebug's SET /E command to uninstall printf.eld.
This is done purely to hide the uninstall message.
The second use of set.eld resets the variable used by the first use.
rv
Runs the RV command, displaying some debugger variables as well as the current mode. This is always Virtual 86 Mode within the boot process of dosemu2.
;install quietinstall ext extlib.eld alias.eld install
Install the ALIAS Extension for lDebug to allow adding aliases.
alias add fred boot protocol freedos ldp/fdkernel.sys
Help to boot FreeDOS with a short command.
alias add edr boot protocol ldos ldp/edrdos.com
Same for Enhanced DR-DOS, the lDOS flavour. Hence the lDOS load protocol and the edrdos.com filename.
; alias add ldos boot protocol ldos ldp/lmsdos.com // config ldos.ini
Commented out alias for running lMS-DOS from its old filename lmsdos.com and explicitly specifying the ldos.ini configuration file. As of 2025 March this file is the default ALTCONFIG filename, so there is no more need to override the CONFIG filename like this.
alias add ldos boot protocol ldos . // .
Alias for running lDOS, using the default name ldos.com and no second filename.
The final dot is
a no-op command passed to the kernel,
which was needed to allow appending a command line
when using an alias
before the debugger patch
that allows to append a command line using an initial semicolon.
The expected use was to specify dot or slashslash as the second filename,
then a blank and then the command line contents.
However, when adding an alias the alias Extension for lDebug
strips any trailing blanks off the end of the alias content.
(I'm noticing that just ldos append foo
rather than ldos; append foo
works just fine. Oh well.)
alias add ldosr boot protocol ldos . // append devicehigh srdxms.sys
Alias to run lDOS but append a configuration line to load the SRDISK XMS block device.
alias add ldoshigh boot protocol ldos ldp/lmsdos.com // config ldoshigh.ini
Obsolete alias to run lMS-DOS with a different CONFIG file.
alias add oldldos boot protocol ldos oldldos/lmsdos.com // config ldos.ini
Alias to run another lDOS revision, stored in the oldldos subdirectory. Yes, the debugger can load kernels from within subdirectories.
alias add ms5 boot protocol msdos6 ldp/io5.sys
Alias to load a (slightly patched) msbio file for MS-DOS v5.00, using the MS-DOS v6 load protocol. (This appears to be an exact match for MS-DOS v5.00, but in the case of MS-DOS v4.00 the "non-contiguous IBMBIO loader (msload)" aka initial loader only works if its io.sys starts in cluster 2, the first data cluster.)
alias add tepe tp cccccc while cs != 70 silent 1
Alias to run a TP (Trace/Proceed past string operations) command with a high repetition count (effectively, "forever") but a condition to continue only while CS is not equal to 70h. The silent keyword with a count of 1 indicates to only display the register dumps once the control flow returns to the debugger terminal, and to only display the very last register dump lest it clutter the screen. This alias helps to skip past the lDOS iniload, lDOS inicomp, and lDOS drkernpl stages of an lDOS flavoured kernel file and return to the debugger command prompt once the drbio/msbio file is entered.
alias add seri install serial timer
This alias is a shortcut to enabling the serial port I/O and for good measure the timer interrupt 8 hook. The latter enables us to use the serial Double Control-C input to break into the debugger regardless what is running in the debuggee.
alias add dmm dm word [ri31s ?? ri31s :: ri27s:24]
This alias runs the DM command, either internal or the command handled by the residently installed DM Extension for lDebug. It gets the MCB to start from using either RI31S (int 31h vector segment) if nonzero or RI27S (int 27h vector segment), and the offset 24h. On most DOS systems, the List of Lists starts at DOSDATA:26h and the first MCB is stored just before this, so the DMM alias will find the current chain regardless of where it starts.
(DR-DOS, EDR-DOS, and lDOS store the DOSDATA segment in the int 31h segment. Current FreeDOS happens to also have this value equal the DOSDATA segment, albeit it won't ever relocate that segment. Reading the int 31h segment is faster than querying the DOSDATA segment using the interrupt 2Fh function 1203h.)
alias add denv d word [es:bx]:0
This alias retrieves the environment segment stored in an int 21h function 4Bh parameter block, and dumps the beginning of this segment.
alias add zap if (byte [cs:ip - 1] == CC) then e cs:ip - 1 90
This alias is for deleting an unexpected breakpoint interrupt after it breaks control to the debugger. This is useful to avoid noise from repeatedly hitting an uninteresting breakpoint.
alias add strat dosstrat force
This alias runs the resident command of the DOSSTRAT Extension for lDebug with the FORCE keyword to run an int 21h call even while "InDOS". This is required when the debugger is bootloaded, as it will always be considered InDOS then.
alias add voone r v0 :=
Alias to shortcut set user variable V00 to a new value.
ext extlib.eld dm.eld install ext extlib.eld dosstrat.eld install
Install residently the dm.eld and the dosstrat.eld.
rc.abort
Running this command causes the debugger to abort its reading from the RC command line buffer. This is needed to re-define the contents of the RC buffer while running a script called from that same buffer.
; rc.replace ldos; g; r f cy; tp FFFFFF while cs == 70 silent 1; r v0 = cs; tp FFFFFF while cs == v0 silent 1
Commented out RC buffer contents to:
ldos
alias)Presumably this was used to quickly get to the relocated SYSINIT segment, which prior to 2025 March would require bypassing the code running in segment 70h (msbio init) and then the code running in another segment (start of sysinit before relocation).
; rc.replace ms5; g; g C13E:0219; t; e es:bx + 3 as words 100; e es:bx + E as words 0, DFFF - v0
Don't recall what exactly this was for, beyond booting MS-DOS v5 and modifying some data structures (MCBs? device driver request packet?).
; rc.replace ldoshigh; g; r f CY; tp fffff while cs < 4000 silent 1
Use the ldoshigh
alias. Go to debugger check. Set debugger present flag.
Then, go until CS is >= 4000h, a better check for running until
the relocated SYSINIT gains control.
rc.replace ldos; g; r f CY; tp fffff while cs < 4000 silent 1
Same as prior, but using the ldos
alias instead.
@ext extlib.eld quit.eld install @install quickrun
As above, install the QUIT command from quit.eld and enable to quickly run it without a RUN parameter.
This file is used as ldebug.sld in my qemu disktest setup, which I use to test primarily lDOS (the one based on lMS-DOS) and Enhanced DR-DOS (the lDOS flavour). Most of these commands are found in the dosemu2 ldebug.sld file as well.
:bootstartup ;install quietinstall ext extlib.eld alias.eld install alias add fred boot protocol freedos fda alias add edr boot protocol ldos fda/edrdos.com ; alias add ldos boot protocol ldos fda/lmsdos.com ; alias add ldos boot protocol ldos alias add ldos boot protocol ldos . . alias add tepe tp cccccc while cs != 70 silent 1 alias add seri install serial timer alias add dmm dm word [ri31s:24] alias add denv d word [es:bx]:0 alias add zap if (byte [cs:ip - 1] == CC) then e cs:ip - 1 90 ext extlib.eld dm.eld install rc.abort ; rc.replace ldos; g; r f cy; tp FFFFFF while cs == 70 silent 1; r v0 = cs; tp FFFFFF while cs == v0 silent 1 ; rc.replace ldos; g; r f CY; g ptr ri13p; g ptr [ss:sp]; r ah 2; tp FFFFF while cs < 4000 silent 1; g 6629:2F45 ; rc.replace ldos; g; r f CY; g ptr ri13p; g ptr [ss:sp]; r ah 2; tp FFFFF while cs < 4000 silent 1 ; rc.replace ldos; tp FFFFFF while cs != 70 silent 1; r word [0:413] = #71; g; r f CY rc.replace ldos; tp FFFFFF while cs != 70 silent 1; r word [0:413] = #45; g; r f CY @ext quit.eld install @install quickrun boot protocol freedos segment=54 minpara=1 testzero.bin @:applicationstartup @:devicestartup @?version @install getinput
alias add ldos boot protocol ldos . .
This alias uses another way of specifying the default ldos.com filename as the first file and the default none file as the second file.
; rc.replace ldos; g; r f CY; g ptr ri13p; g ptr [ss:sp]; r ah 2; tp FFFFF while cs < 4000 silent 1; g 6629:2F45
This boots lDOS, goes to the debugger check, and indicates debugger present. This setup is required to get a writable interrupt 13h handler entrypoint, which we need for setting a breakpoint on it. Next, it runs until the (new) interrupt 13h handler is called. Then it runs until this interrupt call returns to the caller, stored in the 16:16 far pointer on top of the stack on entry. Then it modifies AH to equal 2 (meaning a diskette with change-line support as return from interrupt 13h function 15h). Subsequently it runs until the relocated SYSINIT is entered. Finally, it runs to one particular spot in SYSINIT that I forgot about what is it specifically.
; rc.replace ldos; tp FFFFFF while cs != 70 silent 1; r word [0:413] = #71; g; r f CY
This boots lDOS, runs until the lDOS iniload, inicomp, and drkernpl stages have finished, and then modifies the ROM-BIOS Data Area memory size field to indicate that there are only 71 kilo-binary bytes available. After that it goes to the debugger check and sets CY to indicate debugger present. This alias was used to check for behaviour in low-memory execution.
boot protocol freedos segment=54 minpara=1 testzero.bin
Not sure what this was about.
@?version
Display the lDebug version.