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]

Reply via email to