From aec37c382159b1eb44ee6493e43d3cb0b7cc6d74 Mon Sep 17 00:00:00 2001 From: Stefano Baldo Date: Fri, 28 Aug 2026 12:16:29 -0300 Subject: [PATCH] Do not mistake a line number for the end of file marker LOAD tested every byte it copied for 1ah, including the two bytes of each line number, so a line whose number's low byte is 1ah ended the load and everything after it was lost without an error. Line numbers congruent to 26 modulo 256 are affected: 26, 282, 538, 794, 1050, and so on. This is issue #5. The copy loop no longer tests for the marker. It cannot usefully do so: at the moment it sees 1ah it has no idea whether that byte is a line number's low half or the terminator, and telling those apart needs the previous line number, which the loop cannot carry because the BDOS call between records destroys the registers. The end of the program is found afterwards instead, by walking the lines that were read. Only at a line's first byte can 1ah be the marker, and even there it is only the marker when the two bytes do not read as a line number greater than the one before it: line numbers ascend, and the NUL padding that follows a saved program's marker does not. One residual remains, and it is in the format rather than in this code: if the bytes after the marker happen to read as an ascending line number followed by a carriage return, the walk takes them for a line. A file saved by Tasty Basic pads with NUL and cannot do that. Measured before and after, on the CP/M build, with these files: file before after expected 100 / 1050 / 1100, as issue #5 1 3 3 10 / 26 / 30, the other degenerate 1 3 3 examples/TICTAC.BAS, assembled 94 117 117 examples/tictac.tba, as shipped 94 117 117 a line 1050 inside the last record 13 15 15 a program with no such number 3 3 3 The shipped tictac.tba is worth a word: the file is complete, all 117 lines of it. It only ever loaded as 94, which is why the game fails at line 1020 on a GOTO 1050 that was never brought into memory. Nothing needs regenerating. The ROMWBW build is unaffected and assembles byte for byte identical; this file is only included for CP/M. --- src/cpmio.asm | 52 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/src/cpmio.asm b/src/cpmio.asm index fdef0be..0693b16 100644 --- a/src/cpmio.asm +++ b/src/cpmio.asm @@ -52,6 +52,7 @@ FCBRC .equ FCB+15 ; file's record count (0 to 128) FCBCR .equ FCB+32 ; current (next) record FCBLN .equ FCB+33 ; FCB length FTYPE .db "TBA" ; tasty basic file type +lastnum .dw 0 ; last line number the loader read haschar: push bc @@ -98,8 +99,6 @@ lo1: ld hl,(textunfilled) lo2: ld a,(de) ; get char from buffer - cp 1ah ; is it EOF? - jr z,lo3 ; yes, all done ld (hl),a ; copy char to text area inc hl ; and update pointers inc de @@ -107,7 +106,56 @@ lo2: dec b ; end of record? jr z,lo1 ; yes, so try next record jr lo2 ; no, copy next char +; The whole file is in memory now. Walk the lines to find where the program +; ends: only at a line's first byte can 1ah be the end of file marker, and +; even there it is only the marker when the two bytes do not read as a line +; number after the one before it. Line numbers ascend; what follows the +; marker does not. lo3: + ld de,(textunfilled) ; de -> past the last byte read + ld hl,0 + ld (lastnum),hl ; no line read yet + ld hl,textbegin +lo4: + push hl ; hl -> a line's first byte + or a + sbc hl,de + pop hl + jr nc,lo7 ; nothing left: no marker in the file + ld a,(hl) + cp EOF + jr nz,lo5 ; not the marker, so a line: read it + push hl ; the marker, where a line could begin: + ld a,(hl) ; does it read as the next number? + inc hl + ld h,(hl) + ld l,a + ld bc,(lastnum) + or a + sbc hl,bc + pop hl + jr z,lo7 ; not greater than the last: the end + jr c,lo7 +lo5: + ld a,(hl) ; this line's number is the one the + ld (lastnum),a ; next one has to beat + inc hl + ld a,(hl) + ld (lastnum+1),a + inc hl +lo6: + push hl + or a + sbc hl,de + pop hl + jr nc,lo7 ; ran off the end mid line + ld a,(hl) + inc hl + cp cr ; a line ends at its first cr + jr nz,lo6 + jr lo4 +lo7: + ld (textunfilled),hl jp rstart save: call fname ; ** save **