And it's complicated by the fact that the NEC & M100 ROM entry points are *almost* compatible, most of the time. ;) These discrepancies seemed to bite software vendors who, back in the day, made certain assumptions, and did a minimum of testing...
A common library sounds like a great idea. On Thu, Oct 15, 2026, 5:17 PM John R. Hogerhuis <[email protected]> wrote: > I think other software leaves the RAM file system messed up to. > Interacting with the RAM filesystem is extremely complicated. We should > probably all be using some reusable libraries for interacting with it. If > you wing it, you are very likely to get it wrong. > > -- John. > > > On Thu, Oct 15, 2026 at 5:07 PM Gary Weber <[email protected]> wrote: > >> I forgot to mention there's a third bug that's fixed in TS-DOS 4.20 for >> the NEC. >> >> *Bug #3: SAVE"0:PROG.BA <http://PROG.BA>" after a RUN can save GOTO and >> GOSUB targets as RAM addresses instead of line numbers* >> >> With DOS-ON, you RUN a program, SAVE"0:PROG.BA", and later LOAD"0:PROG.BA" >> back. Most of the program is fine, but some GOTO, GOSUB or THEN line >> numbers are wrong. LIST shows things like GOTO 12849 for lines that don't >> exist, and RUN jumps to the wrong place. Fix the lines, run the program to >> test it, save it again, and the next LOAD has them wrong again. If you've >> ever had .BA files on your TPDD that seemed to "corrupt themselves", this >> may be why. >> >> It's in every NEC TS-DOS image I have, starting with TS-DOS 4.00. The >> SAVE code was byte-for-byte identical in 4.00 and 4.10, before this fix. >> >> So what happened here is, NEC BASIC has a speed-up that the Model 100's >> BASIC doesn't. In tokenized BASIC a line-number reference after GOTO, >> GOSUB, THEN and so on is stored as a 0Eh byte followed by the 2-byte line >> number. The first time such a statement runs, the NEC BASIC looks the line >> up and then overwrites those 3 bytes in your program with a 0Dh byte and >> the line's RAM address, so the next time it can jump straight there without >> searching: >> >> 0C38 CC 0760h ; FINDL2 / FINDLN: find the target line >> 0C3B CNC 075Dh >> 0C3E JNC 0C4Eh ; not found: ?UL >> 0C41 DCX B ; BC = the line's address - 1 >> 0C42 MVI A,0Dh >> 0C44 STA FACDh ; PTRFLG: "some line numbers have been turned into >> addresses" >> ... >> 0C48 CALL 173Ch ; write 0D lo hi over 0E lo hi, right in the >> program text >> >> An address is only good while the program stays where it is in RAM, so >> the system ROM turns every 0Dh address back into a 0Eh line number (routine >> 16A8h) before anything that could move or keep the program. That includes >> its own SAVE to RAM (25F6h), the MENU (5652h calls 2337h, which calls >> 16A8h) and typing in or changing a program line (068Fh calls 1745h). >> >> TS-DOS's DOS-ON SAVE hook doesn't do that. It relinks the program and >> sends it to the drive exactly as it sits in RAM: >> >> 453B POP PSW >> 453C CPI 09h ; our device? >> 453E JNZ 00A5h >> 4541 LXI B,4142h ; "BA" >> 4544 CALL 450Eh >> 4547 JNZ 48D2h >> 454A CALL 4CB6h ; RST 1 / DW 0714h = LINKER >> 454D LHLD F45Dh ; TXTTAB: start of the program >> ... >> 455D CALL 411Fh ; send it to the drive, 0Dh addresses and all >> >> So if you RUN a program and then SAVE"0:PROG.BA", every GOTO or GOSUB >> that ran is saved as an address. On LOAD"0:PROG.BA" the program usually >> lands at a different address, because the program area starts right after >> your RAM files (see Bug #1). Those saved addresses now point into the >> middle of some other line. LIST follows the address and prints whatever 2 >> bytes it finds there as the "line number". The "12849" above is 3231h: the >> two text bytes "12". RUN jumps to the stale address and carries on from >> whatever is there. >> >> My fix, in TS-DOS 4.20 stand-alone and SARDOS 1.80: the SAVE hook now >> calls a 6-byte stub that turns the addresses back into line numbers first, >> the same way the system ROM's own SAVE does, and then runs LINKER as before: >> >> 454A CALL 44F3h ; was CALL 4CB6h >> ... >> 44F3 RST 1 >> 44F4 DW 16A8h ; system ROM: every 0Dh address back to a 0Eh line >> number >> 44F6 JMP 4CB6h ; then LINKER, as before >> >> 44F3h is the free space left behind when the Bug #2 helper moved to 4FF0h. >> >> Is a .BA saved from the TS-DOS menu screen (F1 Save) affected? No. You >> can only get to TS-DOS's file screen through the MENU, and the MENU >> converts the program back to line numbers on the way in. I checked this on >> the emulator: a RAM .BA run from BASIC held addresses until it went back to >> the MENU, and then held line numbers again. A plain SAVE to RAM is fine >> too. Only SAVE"0:PROG.BA" from BASIC with DOS-ON is affected. >> >> Also, just like the other bugs I mentioned before, the M100's TS-DOS >> doesn't have these problems. ANd the reason is the Model 100's BASIC never >> turns line numbers into addresses: GOTO looks the line up by number every >> time, so there is nothing to undo before saving. >> >> Why it stayed hidden so long: it takes all three of these together: >> 1. the program ran (or at least its GOTOs or GOSUBs did) since its lines >> were last typed in or changed, >> 2. it was saved with SAVE"0:" before you went back to the MENU or changed >> a line, and >> 3. it was loaded back at a different address than it was saved from. >> Run, test and save is about the most natural thing to do, and point 3 >> happens whenever the RAM files in front of the program are different. If >> they're the same, the stale addresses happen to still be right, just like >> with Bug #1, so the bug only shows up sometimes, which makes it look like >> random corruption. >> >> Already have a .BA on disk that was saved this way? The fix stops new bad >> saves, but it doesn't repair old files on LOAD. LOAD it, retype the >> GOTO/GOSUB lines that LIST shows with impossible line numbers, and SAVE it >> again with TS-DOS 4.20 or SARDOS 1.80, which you can find in the NEC >> Option ROM images 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> >> on Web8201. >> >> If you're stuck on an older TS-DOS for now, you stil can get around it: >> just before SAVE"0:PROG.BA", re-enter any program line (retype it, or >> LIST it, move the cursor onto it and press ENTER), or go to the MENU and >> back into BASIC. Either one converts the addresses back to line numbers. >> (LIST doesn't, because it just follows the addresses.) >> >> Anyway, that's three bugs that have existed in TS-DOS for the NEC since >> its inception, finally fixed in 4.20. >> >> >> On Wed, Oct 14, 2026 at 8:45 PM Gary Weber <[email protected]> wrote: >> >>> 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" works from BASIC. >>> 1) If you have "DOS-ON" and do a LOAD"0: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" >>> 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" 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" to >>> your TPDD drive or server, clear RAM again and then load up another >>> program, i.e. LOAD"0:HELLO.BA" and SAVE"HELLO.BA" to your RAM >>> directory, go back to BASIC and LOAD"0: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" 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] >>> >>
