I'm trying to display error messages like a lot of MS-DOS (4.0+) utilities do. They don't embed messages like "Too many parameters" and "Invalid switch" in the executables, but rather look them up through an interface. So far, my research has led me to Int 2F, AX=122Eh. I've found some documentation on it in a couple of places: http://www.ctyme.com/intr/rb-4414.htm https://www.delorie.com/djgpp/doc/rbinter/id/30/44.html
As I read, it should return the address of a message table, and if the segment is 0001h, I can call it with DL=08h to get a retriever function. This all works well in MS-DOS 6.22. I can make the call to for the initial error type (e.g. 02h - Get parameter error table), then when I know its segment is 0001h, immediately call 08h and get a retriever function, which I can then build a stack for and far return into:
mov ax, 122Eh
mov dx, 0002h
int 2Fh ; Returns table address in ES:DI
mov si, di ; Store the offset as we'll need it for the retriever function
mov dx, es
cmp dx, 0001h
jne parse_table ; Segment != 0001h => This is a data table, should start with FF, etc.
mov dx, 0008h ; Otherwise, we need to get the retriever function
int 2Fh
mov bx, offset return_here
push cs ; Build the return stack
push bx
push es
push di
mov ax, 0002h ; Error code for "Required parameter missing"
mov di, si
retf
return_here:
; Display the error message (length-terminated string)
And it works well. I get the message in ES:DI and I can display it. The problem I have is with MS-DOS 4, and 4.01 specifically. When I make calls for error tables in MS-DOS 4.00, I get the addresses from my initial call (no need to call 08h). When I do the same for MS-DOS 4.01, for DL=00h and DL=02h, I get a phoney 0001 segment, but if I call DL=08h, nothing gets put in ES:DI. The address remains as whatever it was previously, which leads me to believe it's a dud method in DOS 4.
Under notes in the Int 2F, AH=122Eh documentation I've found, it says:
Notes: If the returned segment on a "get" is 0001h, then the offset specifies the offset of the error message table within COMMAND.COM, and the procedure returned by DL=08h should be called.
But the same Notes section goes on to talk about specifics of DOS 5, leading me to believe it might be a DOS 5+ thing.
I tried walking the PSP to get COMMAND.COM's segment myself and the result isn't quite right. For 02h, for instance, here's a dump of what I see initially (obviously incorrect):
Parameter error table: 0001:1536
1536 5D 23 F6 C3 21 75 62 F6 C3 10 74 5D 8A 45 04 1E ]#..!ub...t].E..
1546 57 0E 1F BF 4C 02 8B 5D 02 8B 3D 8E DB 83 FF FF W...L..]..=.....
1556 74 4A 38 45 04 75 EF 8B 5D 23 F6 C3 20 74 E7 80 tJ8E.u..]#.. t..
1566 F3 20 89 5D 23 5F 1F 33 DB 80 CB 20 09 5D 23 2E . .]#_.3... .]#.
1576 80 3E C7 01 01 74 22 2E 80 3E C4 01 02 75 17 1E .>...t"..>...u..
1586 57 50 8A 45 05 8A E0 33 FF 8E DF 86 06 04 05 3A WP.E...3.......:
1596 E0 58 5F 1F 74 03 E8 2F 11 5B 58 C3 F9 5F 1F EB .X_.t../.[X.._..
15A6 F8 B0 08 EB 02 B0 07 F9 C3 B0 0F E9 15 01 2E C6 ................
15B6 06 56 02 02 8B DF E8 2A FF 8A 45 10 2E A2 54 02 .V.....*..E...T.
15C6 E3 E6 F7 45 23 00 02 75 DC 2E 89 0E 58 02 2E 89 ...E#..u....X...
15D6 26 6A 02 8B C2 33 F6 03 D1 83 D6 00 83 7D 0E 00 &j...3.......}..
15E6 74 0C 83 FE 00 75 BA 3B 55 0E 77 B5 EB 11 2E 03 t....u.;U.w.....
15F6 36 8B 0A 3B 75 1D 72 07 77 A7 3B 55 1B 77 A2 2E 6..;u.r.w.;U.w..
1606 8B 16 8B 0A 03 45 17 13 55 19 2E A3 8D 0A 1E 33 .....E..U......3
1616 C0 8E D8 C5 36 78 00 2E 89 36 62 02 2E 8C 1E 64 ....6x...6b....d
1626 02 1F F7 45 23 01 00 75 09 E8 01 FF E8 53 12 E8 ...E#..u.....S..
And here's me trying to fix it by substituting COMMAND.COM's segment directly:
Parameter error table: 1060:1536
1536 53 65 63 74 6F 72 20 6E 6F 74 20 66 6F 75 6E 64 Sector not found
1546 1A 50 72 69 6E 74 65 72 20 6F 75 74 20 6F 66 20 .Printer out of
1556 70 61 70 65 72 20 65 72 72 6F 72 11 57 72 69 74 paper error.Writ
1566 65 20 66 61 75 6C 74 20 65 72 72 6F 72 10 52 65 e fault error.Re
1576 61 64 20 66 61 75 6C 74 20 65 72 72 6F 72 0F 47 ad fault error.G
1586 65 6E 65 72 61 6C 20 66 61 69 6C 75 72 65 11 53 eneral failure.S
1596 68 61 72 69 6E 67 20 76 69 6F 6C 61 74 69 6F 6E haring violation
15A6 0E 4C 6F 63 6B 20 76 69 6F 6C 61 74 69 6F 6E 13 .Lock violation.
15B6 49 6E 76 61 6C 69 64 20 64 69 73 6B 20 63 68 61 Invalid disk cha
15C6 6E 67 65 0F 46 43 42 20 75 6E 61 76 61 69 6C 61 nge.FCB unavaila
15D6 62 6C 65 19 53 79 73 74 65 6D 20 72 65 73 6F 75 ble.System resou
15E6 72 63 65 20 65 78 68 61 75 73 74 65 64 12 43 6F rce exhausted.Co
15F6 64 65 20 70 61 67 65 20 6D 69 73 6D 61 74 63 68 de page mismatch
1606 0C 4F 75 74 20 6F 66 20 69 6E 70 75 74 17 49 6E .Out of input.In
1616 73 75 66 66 69 63 69 65 6E 74 20 64 69 73 6B 20 sufficient disk
1626 73 70 61 63 65 0E 07 8D 3E 50 14 81 C1 E1 01 C3 space...>P......
I get dropped midway into a string table.
Worse, with DL=00h, I get:
Standard error table: 1060:1665
1665 41 4E 44 2E 43 4F 4D 00 50 52 4F 4D 50 54 3D 24 AND.COM.PROMPT=$
1675 70 24 67 00 00 75 69 72 65 64 20 70 61 72 61 6D p$g..uired param
1685 65 74 65 72 20 6D 69 73 73 69 6E 67 0E 49 6E 76 eter missing.Inv
1695 61 6C 69 64 20 73 77 69 74 63 68 0F 49 6E 76 61 alid switch.Inva
16A5 6C 69 64 20 6B 65 79 77 6F 72 64 24 50 61 72 61 lid keywordPara
16B5 6D 65 74 65 72 20 76 61 6C 75 65 20 6E 6F 74 20 meter value not
16C5 69 6E 20 61 6C 6C 6F 77 65 64 20 72 61 6E 67 65 in allowed range
16D5 1B 50 61 72 61 6D 65 74 65 72 20 76 61 6C 75 65 .Parameter value
16E5 20 6E 6F 74 20 61 6C 6C 6F 77 65 5A D0 11 F0 8D not alloweZ....
16F5 61 6D 65 54 50 30 30 00 61 6C 75 CD 20 C0 9F 00 ameTP00.alu. ...
1705 9A F0 FE 1D F0 47 01 60 10 56 01 60 10 BF 05 60 .....G.`.V.`...`
1715 10 60 10 01 03 01 00 02 FF FF FF FF FF FF FF FF .`..............
1725 FF FF FF FF FF FF FF C9 0E C4 FF D0 11 14 00 18 ................
1735 00 D0 11 FF FF FF FF 00 00 00 00 00 00 00 00 00 ................
1745 00 00 00 00 00 00 00 00 00 00 00 CD 21 CB 00 00 ............!...
1755 00 00 00 00 00 00 00 00 20 20 20 20 20 20 20 20 ........
which looks like a message table that was resident, but is now being used for other things. And I know there's a /MSG switch for COMMAND.COM that might change this, but I need to focus on a standard environment.
I had a look at the MS-DOS 4 source code and it doesn't seem like they're doing anything greatly different to get their message tables, albeit everything seems to work for them:
MSGSERV.ASM
XOR CX,CX ;;AN000; Reset to zero
MOV ES,CX ;;AN000;
XOR DI,DI ;;AN000;
MOV AX,DOS_GET_EXT_PARSE_ADD ;;AN000;; 2FH Interface
MOV DL,DOS_GET_EXTENDED ;;AN000;; Where are the Extended errors in COMMAND.COM
INT 2FH ;;AN000;; Private interface
MOV WORD PTR $M_RT.$M_EXT_COMMAND+2,ES ;;AN000;; Move into first avaliable table location
MOV WORD PTR $M_RT.$M_EXT_COMMAND,DI ;;AN000;;
My question is: how can I correctly get an error message like "Too many parameters" from MS-DOS, specifically 4.01? And if my method is completely wrong, what interrupts / calls should I be using instead?