Nice fixes!
This jogged my memory about a bug on the M100 version of TS-DOS is that it will let you try to LOAD a directory which of course promptly locks up the computer. I have not tested this on an 8201 to see if it acts the same. Jeff From: M100 <[email protected]> On Behalf Of Gary Weber Sent: Wednesday, October 14, 2026 10:45 PM To: [email protected] Subject: [M100] The Story of 40 year old bugs in TS-DOS Believe it or not, there are some TS-DOS NEC-specific bugs from 1986. These have been there since the original NEC port that Traveling Software did of their Model 100 TS-DOS. They relate to how DOS-ON LOAD"0:PROG.BA <http://PROG.BA> " works from BASIC. 1) If you have "DOS-ON" and do a LOAD"0:PROG.BA <http://PROG.BA> ", its line pointers are messed up and you have to return to the main menu and then back to BASIC to fix. 2) If you're in any other bank other than bank #1, LOAD"0:PROG.BA <http://PROG.BA> " simply hangs. You always have to be in RAM bank #1 with TS-DOS 4.10 or earlier. Both of these bugs are in the TS-DOS 4.10 stand-alone option ROM, as well as the one embedded in SARDOS. I'm putting out a new TS-DOS 4.20 option ROM image as well as SARDOS 1.80 with fixes, and they're available here <https://www.web8201.net/default.asp?content=/Files/body.asp%3Fname=NEC%20Option%20ROM%20Images%26bknm=Back%20to%20Web8201%20Files%26bk=NEC-filedirlist.asp%26url=ROMS> . If you want the nitty-gritty details, keep reading! Bug #1: Any .BA that gets loaded from disk with LOAD"0:PROG.BA <http://PROG.BA> " keeps stale line links Symptom: Wwith DOS-ON, LOAD"0:PROG.BA <http://PROG.BA> " followed by LIST shows only the first line and then garbage, or RUN fails. Going to the MENU and back into BASIC "fixes" it. Why: Every line of tokenized BASIC starts with a 2-byte link, the absolute RAM address of the next line. A .BA file keeps the links from the address it was saved at. If it loads anywhere else, BASIC must rebuild them with the system ROM's LINKER routine. The BASIC program area starts right after the RAM files, so any difference in the files present moves it. The normal native ROM LOAD calls LINKER, and so does entering BASIC from the MENU, which is why the round trip hides the problem. Now, if it just so happens that the .BA you are loading was the *first* .BA in RAM when it was saved to disk, and you're reloading it as the very first .BA in a freshly cleared RAM bank, the pointers will still be correct and you won't experience the problem. But if you already have some .BA programs in memory when you do this LOAD, the pointers will be off and you will see this issue. (Want to try reproduce it? Use TS-DOS 4.00 or 4.10, clear your RAM bank, create a small .BA program, SAVE"0:PROG.BA <http://PROG.BA> " to your TPDD drive or server, clear RAM again and then load up another program, i.e. LOAD"0:HELLO.BA <http://HELLO.BA> " and SAVE"HELLO.BA <http://HELLO.BA> " to your RAM directory, go back to BASIC and LOAD"0:PROG.BA <http://PROG.BA> " from your TPDD device that you had created earlier. Then do a LIST. Kaboom!) Okay, so what's going on here? In the original code, TS-DOS receives the file straight into the program area. Then it hands back to the system ROM's own LOAD code to finish: 44E8 CALL 4B1Bh ; restore the serial settings 44EB XRA A 44EC LXI H,27B8h ; system ROM LOAD tail... 44EF PUSH H 44F0 JMP 00A5h ; bank out and "return" into the system ROM at 27B8h The NEC system ROM's LOAD tail code in ROM: 27AC CALL 0714h ; LINKER <- skipped ... 27B5 CALL 280Bh ; NEC only: give back the extra space reserved for the load 27B8 XRA A ; <- TS-DOS comes back in here TS-DOS re-enters at 27B8h, past both LINKER and the space-trimming call. Skipping the trim is on purpose, because TS-DOS has already fixed the end-of-program pointer itself (44DA–44E5h), and doing it twice would corrupt it. Skipping LINKER is the side effect: nothing ever rebuilds the links. My fix is in TS-DOS 4.20 stand-alone, and SARDOS 1.80: the call to restore the serial settings now goes through a 6-byte stub in unused option ROM space, and the stub runs LINKER too: 44E8 CALL 4CCEh ; was CALL 4B1Bh ... 4CCE CALL 4B1Bh ; restore the serial settings, like it was already doing 4CD1 JMP 4CB6h ; TS-DOS's existing system call: RST 1 / DW 0714h / RET = LINKER LINKER runs on the new program, then control returns to 44EBh and the original hand-back carries on unchanged. And so.... Why didn't the Model 100 TS-DOS code have this bug?? The Model 100's TS-DOS ends its loader the same way (XRA A / LXI H,23F5h / PUSH H / JMP 00A6h), but the Model 100 ROM's LOAD tail is in a different order: 23F5 JNZ CLOADERR ; <- TS-DOS returns here (A = 0, so no error) ... 2403 CALL 05F0h ; LINKER: still ahead, so it runs On the Model 100, LINKER comes after the point where TS-DOS returns, so the program is always relinked. The NEC ROM loads differently, it first reserves extra space for the incoming program and gives back the excess afterwards (27B5h). It calls LINKER before that trim. When Traveling Software ported TS-DOS, the return point had to come after the trim, to avoid fixing the end pointer twice. On the NEC, that spot is also past LINKER. It stayed hidden for years because on a freshly set-up machine the program area usually starts at the same address (8017h, after the TS-DOS menu entry). Most .BA files were saved from there, so their links happened to be right. Bug #2: Disk LOAD"0:FILE.BA <http://FILE.BA> " hangs from RAM bank #2 or #3 on the NEC PC-8201A On the PC-8201A, port A1h is one latch for two things: bits 0–1 select the ROM at 0000–7FFFh (system or option ROM), and bits 2–3 select which RAM bank appears at 8000–FFFFh. After a disk .BA LOAD (LOAD"0:FILE.BA <http://FILE.BA> " with DOS-ON), TS-DOS has to call the system ROM's CLRPTR routine (22EEh) to reset BASIC's pointers. CLRPTR can't go through TS-DOS's normal bank-call path, so TS-DOS copies a small helper into RAM at FC8Ah and runs it there: 44FF LXI H,44F3h ; copy the 12-byte helper... LXI D,FC8Ah ; ...into RAM LXI B,000Ch CALL MOVE JMP FC8Ah ; and run it FC8A XRA A ; A = 00 OUT A1h ; system ROM in -- but RAM bank bits = 00 too! CALL 22EEh ; CLRPTR CALL F991h ; bank TS-DOS back in (restores the right RAM bank) JMP 44CCh The bug: XRA A / OUT A1h doesn't only switch to the system ROM. It also selects RAM bank #1. From bank #1 nothing goes wrong. But this helper runs from RAM, in the very area the bank switch replaces. From bank #2 or #3, the CPU's next instruction fetch, its stack, and CLRPTR itself all suddenly act on bank #1's memory. Bank #1's BASIC pointers get reset, the return address comes from the wrong bank, and the machine crashes into bank #1. The same 12 bytes are in TS-DOS 4.0 (1986), 4.1 and 4.2, and in every SARDOS. It went unnoticed for about 40 years because almost everyone works in bank #1. The fix: switch only the ROM and keep the current RAM bank, the same way TS-DOS's own bank-out routine does it: FC8A IN A0h ; read the current bank latch ANI 0Ch ; keep the RAM-bank bits; ROM bits 00 = system ROM OUT A1h ; switch the ROM, stay in our RAM bank CALL 22EEh ; CLRPTR CALL F991h ; bank TS-DOS back in JMP 44CCh The helper grew from 12 to 15 bytes, so it moved to free TS-DOS/SARDOS ROM space at 4FF0h, and the copy at 44FFh now copies 15 bytes from there. The usual option-ROM trick won't work here: write the latch, read bank #1's saved bank number at F3DBh, then write the latch again. That only works in code running from ROM. In this helper, which runs from RAM, the instruction after the first OUT would already be fetched from bank #1. --- Bottom line: On your NEC, if you're using the TS-DOS 4.10 or earlier option ROM, you should switch to using TS-DOS 4.20. Same for SARDOS 1.73 or earlier: switch to 1.80. Both are available in the ROMs download area <https://www.web8201.net/default.asp?content=/Files/body.asp%3Fname=NEC%20Option%20ROM%20Images%26bknm=Back%20to%20Web8201%20Files%26bk=NEC-filedirlist.asp%26url=ROMS> up on Web8201. -- Gary Weber [email protected] <mailto:[email protected]>
