commanderx16 / x16-rom

Other
153 stars 43 forks source link

CMDR-DOS: Input corruption when programmatically reading from a directory listing "$" (SA 0) and another file (SA 2) #361

Closed mooinglemur closed 1 year ago

mooinglemur commented 1 year ago

I was writing an assembly language program that programmatically reads a directory list, and before the program reads all of it, opens a file, reads from it, closes it, and then returns to reading the directory listing. This appears to result in some sort of corruption of the directory listing cursor's position.

Moreover, closing and reopening the directory listing at that point results in a corrupt directory read with what should be a clean state. Repeating that one more time seems to come back clean.

I managed to create a BASIC test case for this.

I created this BASIC program

10 H$="0123456789ABCDEF":I=-1
20 PRINT "OPENING DIRECTORY, PRINTING 40 CHARACTERS, AND CLOSING"
30 GOSUB 500
40 GOSUB 700:GOSUB 700
50 CLOSE 1
60 PRINT
70 PRINT "OPENING DIRECTORY, PRINTING 20 CHARACTERS, THEN OPENING A FILE"
80 PRINT "READING ONE CHARACTER, CLOSING, THEN PRINTING 20 MORE CHARACTERS"
90 PRINT "FROM THE DIRECTORY, THEN CLOSING THE DIRECTORY"
100 GOSUB 500
110 GOSUB 700:GOSUB 600:GOSUB 700
120 CLOSE 1
125 PRINT
130 PRINT "OPENING DIRECTORY, PRINTING 40 CHARACTERS, AND CLOSING"
140 GOSUB 500
150 GOSUB 700:GOSUB 700
160 CLOSE 1
165 PRINT
170 PRINT "OPENING DIRECTORY, PRINTING 40 CHARACTERS, AND CLOSING"
180 GOSUB 500
190 GOSUB 700:GOSUB 700
200 CLOSE 1
205 PRINT
210 PRINT "OPENING DIRECTORY, PRINTING 40 CHARACTERS, AND CLOSING"
220 GOSUB 500
230 GOSUB 700:GOSUB 700
240 CLOSE 1
245 PRINT
250 PRINT "OPENING DIRECTORY, THEN OPENING A FILE, READING ONE CHARACTER,"
260 PRINT "CLOSING, THEN PRINTING 20 CHARACTERS FROM THE DIRECTORY, AND CLOSING"
270 GOSUB 500
280 GOSUB 600:GOSUB 700:GOSUB 700
290 CLOSE 1
295 PRINT
300 PRINT "OPENING DIRECTORY, PRINTING 40 CHARACTERS, AND CLOSING"
310 GOSUB 500
320 GOSUB 700:GOSUB 700
330 CLOSE 1
335 PRINT
340 PRINT "OPENING DIRECTORY, PRINTING 40 CHARACTERS, AND CLOSING"
350 GOSUB 500
360 GOSUB 700:GOSUB 700
370 CLOSE 1
499 END
500 OPEN 1,8,0,"$"
510 IF ST<>0 THEN PRINT "ERROR OPENING DIR":GOTO 1000
520 RETURN
600 OPEN 2,8,2,"DIRTEST.PRG"
610 GET#2,P$
620 CLOSE 2
630 RETURN
700 PRINT " ";
710 FOR N=1 TO 20
720 GET#1,C$:X=ASC(C$+CHR$(0))
730 H=INT(X/16):L=X-(16*H)
740 PRINT MID$(H$,H+1,1);MID$(H$,L+1,1);" ";
750 NEXT N
760 PRINT
770 RETURN
1000 CLOSE 1

I saved it as DIRTEST.PRG, and I created an SD card image via the following.


#rm -f sdcard.img
truncate -s 100M sdcard.img
parted -s sdcard.img mklabel msdos mkpart primary fat32 2048s -- -1
mformat -i sdcard.img@@1M -F

mcopy -i sdcard.img@@1M -o -m DIRTEST.PRG ::
mcopy -i sdcard.img@@1M -o -m DIRTEST.PRG ::FILE1
mcopy -i sdcard.img@@1M -o -m DIRTEST.PRG ::FILE2
mcopy -i sdcard.img@@1M -o -m DIRTEST.PRG ::FILE3

x16emu -sdcard sdcard.img -debug -scale 2

This is the output I see

OPENING DIRECTORY, PRINTING 40 CHARACTERS, AND CLOSING
 01 08 01 01 00 00 12 22 4E 4F 20 4E 41 4D 45 20 20 20 20 20 
 20 20 20 20 22 20 46 41 54 33 32 00 01 01 06 00 20 20 20 22 

OPENING DIRECTORY, PRINTING 20 CHARACTERS, THEN OPENING A FILE
READING ONE CHARACTER, CLOSING, THEN PRINTING 20 MORE CHARACTERS
FROM THE DIRECTORY, THEN CLOSING THE DIRECTORY
 01 08 01 01 00 00 12 22 4E 4F 20 4E 41 4D 45 20 20 20 20 20 
 20 20 20 20 22 20 46 41 54 33 32 00 01 01 62 00 4D 42 20 46 

OPENING DIRECTORY, PRINTING 40 CHARACTERS, AND CLOSING
 52 45 45 2E 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 

OPENING DIRECTORY, PRINTING 40 CHARACTERS, AND CLOSING
 01 08 01 01 00 00 12 22 4E 4F 20 4E 41 4D 45 20 20 20 20 20 
 20 20 20 20 22 20 46 41 54 33 32 00 01 01 06 00 20 20 20 22 

OPENING DIRECTORY, PRINTING 40 CHARACTERS, AND CLOSING
 01 08 01 01 00 00 12 22 4E 4F 20 4E 41 4D 45 20 20 20 20 20 
 20 20 20 20 22 20 46 41 54 33 32 00 01 01 06 00 20 20 20 22 

OPENING DIRECTORY, THEN OPENING A FILE, READING ONE CHARACTER,
CLOSING, THEN PRINTING 20 CHARACTERS FROM THE DIRECTORY, AND CLOSING
 01 08 01 01 00 00 12 22 4E 4F 20 4E 41 4D 45 20 20 20 20 20 
 20 20 20 20 22 20 46 41 54 33 32 00 01 01 62 00 4D 42 20 46 

OPENING DIRECTORY, PRINTING 40 CHARACTERS, AND CLOSING
 52 45 45 2E 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 

OPENING DIRECTORY, PRINTING 40 CHARACTERS, AND CLOSING
 01 08 01 01 00 00 12 22 4E 4F 20 4E 41 4D 45 20 20 20 20 20 
 20 20 20 20 22 20 46 41 54 33 32 00 01 01 06 00 20 20 20 22 

READY.

All of these scenarios' outputs should be identical, but they're not.

mooinglemur commented 1 year ago

Oops, line 260 should say 40 characters, not 20 :) Doesn't affect the output though.

mooinglemur commented 1 year ago

Tested against R41 and current masters with identical results

x16-emulator: e10bc7b7dfa20fa73a4bace58c956ef7805779d7 x16-rom: 5d8ba9f36b4c4fb4397b94c7ce273e2d4e5e0110

mooinglemur commented 1 year ago

I think the crux of the issue is that opening a file clobbers the single dirent structure in the fat32 driver. It appears that it even occurred to @mist64 a couple years ago that this would be problematic.

https://github.com/commanderx16/x16-rom/blob/676f4a5744c236eda448cc024c7819f67e8407f9/dos/dir.s#L248-L249

mooinglemur commented 1 year ago

Reopened issue https://github.com/X16Community/x16-rom/issues/19