-
BASIC Makes This Hard
05/12/2022 at 10:56 • 0 commentsTrying to go from a typed in file name as a string like "TEST.BAS" into "TEST BAS" to find the file in the directory. The string functions didn't work as expected.
DIM A$(11) - Allocates string space for the output string
Inserting characters didn't work. Code to test:
6000 REM STRING STUFF 6010 DIM A$(8) 6020 PRINT A$ 6030 PRINT LEN(A$) 6040 A$(1)="X" 6050 PRINT A$ 6060 PRINT LEN(A$)
Produces no string. This should be relatively easy to solve in assembly so the problem can be ignored for the moment by just typing in the file name with spaces to pad the file name to 8 characters and 3 character extension.
Working on the BASIC code to find a file offset from a file name. Let's look through the directory for "STARS BAS" which is on the disk. String matching does work.
7000 A$="TEST BAS" 7010 B$="TEST BAS" 7020 IF A$=B$ THEN PRINT "OK-MATCH" 7030 IF A$<>B$ THEN PRINT "MATCH ERROR" 7040 B$="TEST2 BAS" 7050 IF A$<>B$ THEN PRINT "OK-DOES NOT MATCH" 7060 IF A$=B$ THEN PRINT "DOES NOT MATCH ERROR"
Produces OK messages. But how can the file name be extracted into a string without the first method? This simple function does not work as expected.
7000 A$="TEST BAS" 7010 PRINT A$(1)
Can the individual characters be converted to numbers and compared?
MID$ does seem to work. This function pulls out characters and turns them into their numeric equivalent.
7000 A$="TEST BAS" 7010 C$=MID$(A$,1,1) 7020 PRINT ASC(C$) 7030 C$=MID$(A$,2,1) 7040 PRINT ASC(C$)
This produces 84,69 as expected. The reverse function works so it could be used one character at a time.
7000 A$="TEST BAS" 7010 C$=MID$(A$,1,1) 7020 PRINT ASC(C$) 7030 C$=MID$(A$,2,1) 7040 N=ASC(C$) 7050 PRINT N 7060 PRINT CHR$(N)
This turns the second character back into the string value which can be used to compare to the value in the buffer. This code matches like it should.
6000 A$="TEST BAS" 6010 DIM B(20) 6020 V$="T" 6030 B(5)=ASC(V$) 6040 C$=MID$(A$,1,1) 6050 X=ASC(C$) 6060 IF X=B(5) THEN PRINT "OK-MATCHES"
So the method is to pull the character out of the buffer, convert the character to ASCII number then compare it to the value of the string. This is painful but works. Here's the code.
265 Q$="STARS BAS" 6010 CM=1 6020 FOR I=1 TO 10 6030 SE=ASC(MID$(Q$,I,1)) 6040 IF MA(DF+I-1)<>SE THEN CM=0 6050 NEXT I 6060 IF CM=1 THEN PRINT "FILE FOUND" 6190 RETURN
This returns:
SPLAT.BAS 4942 CLUSTER OFFSET 150 FILE FOUND STARS.BAS 1590 CLUSTER OFFSET 151 STARTR~1.BAS 8481 CLUSTER OFFSET 152
For some unknown reason typing in 151 does not work. Smaller numbers are OK. Looking for WAR returned 112 which did get the correct file. Added code to prompt fpor a file name and if that file name was found in the directory then print the first block of the file. Name still has to be entered with spaces but this same method could work for comparisons.
-
Print the file cluster offset
05/11/2022 at 17:22 • 0 commentsBack to the Directory Entry Structure table:
The offset to the cluster is at locations 20-21 and 26-27. The code to support this is:
4550 PRINT " ";:PRINT ((MA(DF+30)*65536)+(MA(DF+29)*256)+MA(DF+28)); 4560 AC=(65536*MA(DF+21))+(256*MA(DF+27))+MA(DF+26) 4570 PRINT "CLUSTER OFFSET";:PRINT AC
The result looks like this now.
*** DIRECTORY START *** SYSTEM~1. 0 CLUSTER OFFSET 3 HOCKEY.BAS 8377 CLUSTER OFFSET 5 HORSER~1.BAS 3014 CLUSTER OFFSET 6 HURKLE.BAS 1390 CLUSTER OFFSET 7 KINEMA.BAS 813 CLUSTER OFFSET 8 KING.BAS 8950 CLUSTER OFFSET 9 LABYRI~1.BAS 4988 CLUSTER OFFSET 10 LETTER.BAS 935 CLUSTER OFFSET 11 LIFE2.BAS 2298 CLUSTER OFFSET 12 ...
It should now be possible to replace the cluster offset with a value that gets typed in by the user. Wrote the code and is worked. Top part is:
270 GOSUB 5200:REM PRINT DIRECTORY 280 INPUT "FILE OFFSET";FS 360 GOSUB 5000:REM PRINT FILE AT 9
The file listing dumps the first block of the file. For QUBIC.BAS:
FILE OFFSET? 129 FILE OFFSET SECTOR 32704 000 35 30 20 50 52 49 4E 54 20 43 48 52 24 28 32 36 50 PRINT CHR$(26 010 29 3A 57 49 44 54 48 20 38 30 0D 0A 31 30 30 20 ):WIDTH 80..100 020 50 52 49 4E 54 20 54 41 42 28 33 33 29 3B 22 51 PRINT TAB(33);"Q 030 55 42 49 43 22 3A 50 52 49 4E 54 0D 0A 31 31 30 UBIC":PRINT..110 040 20 50 52 49 4E 54 20 54 41 42 28 31 35 29 3B 22 PRINT TAB(15);" 050 43 52 45 41 54 49 56 45 20 43 4F 4D 50 55 54 49 CREATIVE COMPUTI 060 4E 47 20 20 4D 4F 52 52 49 53 54 4F 57 4E 2C 20 NG MORRISTOWN, 070 4E 45 57 20 4A 45 52 53 45 59 22 0D 0A 31 32 30 NEW JERSEY"..120 080 20 50 52 49 4E 54 3A 50 52 49 4E 54 3A 50 52 49 PRINT:PRINT:PRI 090 4E 54 0D 0A 32 31 30 20 50 52 49 4E 54 20 22 44 NT..210 PRINT "D 0A0 4F 20 59 4F 55 20 57 41 4E 54 20 49 4E 53 54 52 O YOU WANT INSTR 0B0 55 43 54 49 4F 4E 53 22 3B 0D 0A 32 32 30 20 49 UCTIONS";..220 I 0C0 4E 50 55 54 20 43 24 0D 0A 32 33 30 20 49 46 20 NPUT C$..230 IF 0D0 4C 45 46 54 24 28 43 24 2C 31 29 3D 22 4E 22 20 LEFT$(C$,1)="N" 0E0 54 48 45 4E 20 33 31 35 0D 0A 32 34 30 20 49 46 THEN 315..240 IF 0F0 20 4C 45 46 54 24 28 43 24 2C 31 29 3D 22 59 22 LEFT$(C$,1)="Y" 100 20 54 48 45 4E 20 32 36 35 0D 0A 32 35 30 20 50 THEN 265..250 P 110 52 49 4E 54 20 22 49 4E 43 4F 52 52 45 43 54 20 RINT "INCORRECT 120 41 4E 53 57 45 52 2E 20 20 50 4C 45 41 53 45 20 ANSWER. PLEASE 130 54 59 50 45 20 27 59 45 53 27 20 4F 52 20 27 4E TYPE 'YES' OR 'N 140 4F 27 22 3B 0D 0A 32 36 30 20 47 4F 54 4F 20 32 O'";..260 GOTO 2 150 32 30 0D 0A 32 36 35 20 50 52 49 4E 54 0D 0A 32 20..265 PRINT..2 160 37 30 20 50 52 49 4E 54 20 22 54 48 45 20 47 41 70 PRINT "THE GA 170 4D 45 20 49 53 20 54 49 43 2D 54 41 43 2D 54 4F ME IS TIC-TAC-TO 180 45 20 49 4E 20 41 20 34 20 58 20 34 20 58 20 34 E IN A 4 X 4 X 4 190 20 43 55 42 45 2E 22 0D 0A 32 38 30 20 50 52 49 CUBE."..280 PRI 1A0 4E 54 20 22 45 41 43 48 20 4D 4F 56 45 20 49 53 NT "EACH MOVE IS 1B0 20 49 4E 44 49 43 41 54 45 44 20 42 59 20 41 20 INDICATED BY A 1C0 33 20 44 49 47 49 54 20 4E 55 4D 42 45 52 2C 20 3 DIGIT NUMBER, 1D0 57 49 54 48 20 45 41 43 48 22 0D 0A 32 39 30 20 WITH EACH"..290 1E0 50 52 49 4E 54 20 22 44 49 47 49 54 20 42 45 54 PRINT "DIGIT BET 1F0 57 45 45 4E 20 31 20 41 4E 44 20 34 20 49 4E 43 WEEN 1 AND 4 INC
One complication of loading BASIC programs is that BASIC is tokenized. There's a few ways to handle this.
- Do the tokenization and convert ASCII code to tokenized code at load time
- Load initially over serial port and save the file to the SD card as tokenized
- Intercept the BASIC LOAD/SAVE calls
- Could this be done at the I/O routine level? Or would BASIC need to be re-built? Hints are in here.
- Put the SD card code into an IOP16 core and emulate the serial port
- Advantage is this could be ported to any design - not just the OSI UK101
Thinking about other ways.
Loading C1P machine code should be much easier. The format is pretty simple:
000 2E 30 34 30 30 2F 41 32 0D 44 45 0D 41 39 0D 30 .0400/A2.DE.A9.0 010 34 0D 32 30 0D 33 45 0D 30 34 0D 32 30 0D 33 34 4.20.3E.04.20.34 020 0D 30 34 0D 41 39 0D 30 30 0D 38 44 0D 42 46 0D .04.A9.00.8D.BF. 030 30 34 0D 41 44 0D 42 46 0D 30 34 0D 32 30 0D 35 04.AD.BF.04.20.5 040 35 0D 30 34 0D 41 39 0D 32 30 0D 32 30 0D 45 45 5.04.A9.20.20.EE 050 0D 46 46 0D 41 44 0D 42 46 0D 30 34 0D 43 39 0D .FF.AD.BF.04.C9. 060 36 34 0D 46 30 0D 30 36 0D 45 45 0D 42 46 0D 30 64.F0.06.EE.BF.0 070 34 0D 34 43 0D 30 46 0D 30 34 0D 41 32 0D 33 36 4.4C.0F.04.A2.36 080 0D 41 39 0D 30 35 0D 32 30 0D 33 45 0D 30 34 0D .A9.05.20.3E.04. 090 32 30 0D 45 42 0D 46 46 0D 36 43 0D 46 43 0D 46 20.EB.FF.6C.FC.F 0A0 46 0D 41 32 0D 43 31 0D 41 39 0D 30 34 0D 32 30 F.A2.C1.A9.04.20
- . signals an address follows
- 0400 start loading code address
- / data entry mode
- AA - data
- 0D -next data - could possibly be CR and LF?
At the very least some facility for file names will need to be added to the LOAD/SAVE commands.
-
Small Improvement to Directory Listing
05/11/2022 at 12:14 • 0 commentsAdd spaces to the directory listing to pad out to the size. New code is:
4600 REM 4610 DF=0:REM START AT FIRST ENTRY 4620 IF MA(DF+2)<>0 THEN GOSUB 4400:REM PRINT CURRENT DIR ENTRY 4630 DF=DF+32:REM ADVANCE TO NEXT DIR ENTRY 4640 IF DF<512 GOTO 4620:REM BLOCKS ARE 512 BYTES 4660 RETURN 4800 REM POKE LBA, CHECK STAT, READ BLOCK TO MA() 4810 GOSUB 3800:REM POKE LBA VALS 4820 GOSUB 2200:REM CHECK CARD INIT STATUS 4830 GOSUB 2400:REM READ IN BLOCK 4840 RETURN
Result is better
*** DIRECTORY *** SYSTEM~1. 0 HOCKEY.BAS 8377 HORSER~1.BAS 3014 HURKLE.BAS 1390 KINEMA.BAS 813 KING.BAS 8950 LABYRI~1.BAS 4988
-
Find the next Directory Block
05/11/2022 at 11:25 • 0 commentsThe previous log properly parsed the first directory block but it did not go through all the directory blocks. Where is the next directory block? A hint is here.
For FAT32, the root directory can be of variable size and is a cluster chain, just like any other directory is. The first cluster of the root directory on a FAT32 volume is stored in BPB_RootClus.
Each entry is:
Picking one entry in the middle:
080 48 4F 43 4B 45 59 20 20 42 41 53 20 00 23 BD 8C HOCKEY BAS .#.. 090 A9 54 A9 54 00 00 F5 91 4D 54 05 00 B9 20 00 00 .T.T....MT... ..
- Offset 0-10 = Short File Name - "HOCKEY BAS"
- Offset 11 = 0x20 - ATTR_ARCHIVE (see below)
- Offset 12 = 0x00 NT_Reserved - is 0 like it should be
- Offset 13 - 0x23 = Millisecond timestamp - ignore
- Offset 14-19 = BD 8C A9 54 A9 54 - Not sure what this is but there's stuff in there and it's different for each entry
- Offset 20-21 = 00 00 - High word of entry's first cluster
- Offset 22-23 = F5 91 - Last Time
- Offset 24-25 = 4D 54 - Last Date
- Offset 27-28 = 05 00 - Low word of entry's first cluster
- Offset 28-31 = B9 20 00 00 - File size
For example HOCKEY.BAS is at offset 0x00 00 00 05.
There's no chain element in this entry.
Note about the ATTR_ARCHIVE - This attribute supports backup utilities. This bit is set by the FAT file system driver when a file is created, renamed, or written to. Backup utilities may use this attribute to indicate which files on the volume have been modified since the last time that a backup was performed.
The first character in the Short File name has special meaning.
- If DIR_Name[0] == 0xE5, then the directory entry is free (there is no file or directory name in this entry).
- If DIR_Name[0] == 0x00, then the directory entry is free (same as for 0xE5), and there are no allocated directory entries after this one (all of the DIR_Name[0] bytes in all of the entries after this one are also set to 0).
The special 0 value, rather than the 0xE5 value, indicates to FAT file system driver code that the rest of the entries in this directory do not need to be examined because they are all free.
Nothing There
There's nothing in the directory block that tells where the next block is located. Backing up a level to the FAT32 FSInfo Sector Structure.
The root directory seems to be in consecutive block. Added code to read blocks until a block that starts with the first item equal to 0x00.
Result is:
*** DIRECTORY START *** SYSTEM~1. 0 HOCKEY.BAS 8377 HORSER~1.BAS 3014 HURKLE.BAS 1390 KINEMA.BAS 813 ... [CLIPPED HERE ... STARTR~1.BAS 8481 STOCK.BAS 7440 SUPERS~1.BAS 20045 SUPERS~2.BAS 5960 SWING.BAS 1151 SYNONYM.BAS 1981 T1000.BAS 32 TANKFORT.BAS 3456 *** DIRECTORY END ***
The code is:
5200 REM READ/PRINT ROOT DIRECTORY 5210 BO=0 5220 PRINT "*** DIRECTORY START ***" 5230 L0=(DS+BO) AND 255:L1=(DS+BO)/256:L2=(DS+BO)/65536 5240 GOSUB 4800:REM READ BLOCK 5250 IF MA(0)=0 THEN GOTO 5300 5260 REM GOSUB 3000:REM DUMP ARRAY 5270 GOSUB 4600:REM PRINT DIRECTORY 5280 BO=BO+1 5290 GOTO 5230 5300 PRINT "*** DIRECTORY END ***" 5310 RETURN
It works but I'm not sure if it is a robust result since I am assuming that the directory is in consecutive blocks. Moving on for the moment.
I think the answer is in Rebuilding FAT cluster chains.
By now you know from the previous entries that this structure in the FAT was zeroed out when the file was deleted and that the file system uses the data contained in these 16 bit cluster entries to map out the file's location on disk. Without this map, who knows where the file lies. To rebuild the map, we have to populate the 16 bit cluster entries with cluster addresses where the data resides on disk. We saw in the previous entry that the FAT Directory Entry told us the file began in cluster two. We know from the way a file system optimizes writes that it will put the file down contiguously if it can and in this instance we have enough available cluster entries in the FAT to accommodate our file. Let's see the corrected FAT:
Aside from the fact that I've masked out the values we're not interested in seeing, you can now see the 16-bit values (also known as words) that make up each cluster entry in the FAT. Each pair of bytes (03 00, 04 00, 05 00...) represents a cluster on the disk, and inside each word is the next cluster address on the disk where the file continues. Previously, when the file was deleted, each word contained 00 00, 00 00, etc. We know from the FAT Directory Entry that our file started in cluster two. The 16 bit value in the FAT entry for cluster two is 0x0300, the 16 bit value in the FAT entry for cluster three is 0x0400, the 16 bit value in the FAT entry for cluster four is 0x0500 and so on. If we convert these values to decimal we get three, four, five and so on. So, cluster two is the start of the file, the FAT Directory Entry tells us this, to see where the file continues, we look at cluster two's entry in the FAT itself.
It would be nice to build a table of file names and offsets.
-
Parse the Directory Better
05/11/2022 at 10:55 • 0 commentsThe previous logs found its way to the directory on an SD card when the first LBA contained a partition table. It was able to "roughly" read in the directory but had trouble with proper parsing when the directory had long file names.
This log is a quick solution to that issue. If successful, this should produce a clean listing of the part of the directory in the first LBA.
Directory entries are 32-bytes per entry. Here's the first directory LBA seperated into 32-byte chunks..
DIR SECTOR # 24576 96 0 READING LBA 24576 000 42 20 00 49 00 6E 00 66 00 6F 00 0F 00 72 72 00 B .I.n.f.o...rr. 010 6D 00 61 00 74 00 69 00 6F 00 00 00 6E 00 00 00 m.a.t.i.o...n... 020 01 53 00 79 00 73 00 74 00 65 00 0F 00 72 6D 00 .S.y.s.t.e...rm. 030 20 00 56 00 6F 00 6C 00 75 00 00 00 6D 00 65 00 .V.o.l.u...m.e. 040 53 59 53 54 45 4D 7E 31 20 20 20 16 00 99 98 8C SYSTEM~1 ..... 050 A9 54 A9 54 00 00 99 8C A9 54 03 00 00 00 00 00 .T.T.....T...... 060 41 48 00 6F 00 63 00 6B 00 65 00 0F 00 69 79 00 AH.o.c.k.e...iy. 070 2E 00 62 00 61 00 73 00 00 00 00 00 FF FF FF FF ..b.a.s......... 080 48 4F 43 4B 45 59 20 20 42 41 53 20 00 23 BD 8C HOCKEY BAS .#.. 090 A9 54 A9 54 00 00 F5 91 4D 54 05 00 B9 20 00 00 .T.T....MT... .. 0A0 41 48 00 6F 00 72 00 73 00 65 00 0F 00 63 52 00 AH.o.r.s.e...cR. 0B0 61 00 63 00 65 00 2E 00 62 00 00 00 61 00 73 00 a.c.e...b...a.s. 0C0 48 4F 52 53 45 52 7E 31 42 41 53 20 00 4B BD 8C HORSER~1BAS .K.. 0D0 A9 54 A9 54 00 00 01 92 4D 54 06 00 C6 0B 00 00 .T.T....MT...... 0E0 41 48 00 75 00 72 00 6B 00 6C 00 0F 00 39 65 00 AH.u.r.k.l...9e. 0F0 2E 00 62 00 61 00 73 00 00 00 00 00 FF FF FF FF ..b.a.s......... 100 48 55 52 4B 4C 45 20 20 42 41 53 20 00 52 BD 8C HURKLE BAS .R.. 110 A9 54 A9 54 00 00 F7 54 F1 52 07 00 6E 05 00 00 .T.T...T.R..n... 120 41 4B 00 69 00 6E 00 65 00 6D 00 0F 00 17 61 00 AK.i.n.e.m....a. 130 2E 00 62 00 61 00 73 00 00 00 00 00 FF FF FF FF ..b.a.s......... 140 4B 49 4E 45 4D 41 20 20 42 41 53 20 00 55 BD 8C KINEMA BAS .U.. 150 A9 54 A9 54 00 00 F7 54 F1 52 08 00 2D 03 00 00 .T.T...T.R..-... 160 41 4B 00 69 00 6E 00 67 00 2E 00 0F 00 DD 62 00 AK.i.n.g......b. 170 61 00 73 00 00 00 FF FF FF FF 00 00 FF FF FF FF a.s............. 180 4B 49 4E 47 20 20 20 20 42 41 53 20 00 58 BD 8C KING BAS .X.. 190 A9 54 A9 54 00 00 F7 54 F1 52 09 00 F6 22 00 00 .T.T...T.R...".. 1A0 42 69 00 63 00 00 00 FF FF FF FF 0F 00 A4 FF FF Bi.c............ 1B0 FF FF FF FF FF FF FF FF FF FF 00 00 FF FF FF FF ................ 1C0 01 6C 00 61 00 62 00 79 00 72 00 0F 00 A4 69 00 .l.a.b.y.r....i. 1D0 6E 00 74 00 68 00 2E 00 62 00 00 00 61 00 73 00 n.t.h...b...a.s. 1E0 4C 41 42 59 52 49 7E 31 42 41 53 20 00 5E BD 8C LABYRI~1BAS .^.. 1F0 A9 54 A9 54 00 00 F8 54 F1 52 0A 00 7C 13 00 00 .T.T...T.R..|...
A quick filter of each of the entries could be to look at the third byte. If the third byte is 0x00 then skip the record.
BASIC Code
The BASIC code is in GitHub here. So far, the code is able to read in the first directory block but it does a poor job of parsing the directory. Let's improve the directory code by looking at 32 byte chunks. The previous code stepped forward in 64 byte chunks but really should step in 32 byte chunks.
Here's the previous BASIC code:
4400 REM PRINT DIR ENTRY 4410 FOR I=0 TO 7 4420 IF MA(DF+I)<>32 THEN PRINT CHR$(MA(DF+I)); 4430 NEXT I 4440 PRINT "."; 4450 FOR I=8 TO 10 4460 IF MA(DF+I)<> 32 THEN PRINT CHR$(MA(DF+I)); 4470 NEXT I 4480 REM PRINT 4490 PRINT " ";:PRINT ((MA(DF+30)*65536)+(MA(DF+29)*256)+MA(DF+28)) 4500 RETURN 4600 PRINT "*** DIRECTORY ***" 4610 DF=128:REM DIR OFFSET 4620 GOSUB 4400:REM PRINT CURRENT DIR ENTRY 4630 DF=DF+64 4640 IF DF<512 GOTO 4620 4650 FF=DS+((9-2)*SC):REM MANUALLY ADDING FILE OFFSET 4660 L0=FF AND 255:L1=FF/256:L2=0:L3=0 4670 GOSUB 3800:REM POKE LBA VALS 4680 GOSUB 2200:REM CHECK CARD INIT STATUS 4690 GOSUB 2400:REM READ IN BLOCK 4700 GOSUB 3000:REM DUMP ARRAY 4710 RETURN
Instead of starting at 128,. start at 0. Then, look at the third byte. If it's not 0x00 print the entry. After doing that small change the directory listing looks good:
*** DIRECTORY *** SYSTEM~1. 0 HOCKEY.BAS 8377 HORSER~1.BAS 3014 HURKLE.BAS 1390 KINEMA.BAS 813 KING.BAS 8950 LABYRI~1.BAS 4988 READING LBA 25216
The size of the block is now displayed and longer file names are in 8.3 format. Also, the SySTEM~1 entry is shown.
The new directory parsing code is:
4600 PRINT "*** DIRECTORY ***" 4610 DF=0:REM START AT FIRST ENTRY 4620 IF MA(DF+2)<>0 THEN GOSUB 4400:REM PRINT CURRENT DIR ENTRY 4630 DF=DF+32 4640 IF DF<512 GOTO 4620 4650 FF=DS+((9-2)*SC):REM MANUALLY ADDING FILE OFFSET 4660 L0=FF AND 255:L1=FF/256:L2=0:L3=0 4670 GOSUB 3800:REM POKE LBA VALS 4680 GOSUB 2200:REM CHECK CARD INIT STATUS 4690 GOSUB 2400:REM READ IN BLOCK 4700 GOSUB 3000:REM DUMP ARRAY 4710 RETURN
Next - figure out how to find the next directory block. I think they are somehow chained?
-
Directory Stuff
05/10/2022 at 13:33 • 0 commentsI put a bunch of files on the SD card so the directory spans across multiple blocks.
It might be fun to have a directory listing. The front of the first directory block has:
000 42 20 00 49 00 6E 00 66 00 6F 00 0F 00 72 72 00 B .I.n.f.o...rr. 010 6D 00 61 00 74 00 69 00 6F 00 00 00 6E 00 00 00 m.a.t.i.o...n... 020 01 53 00 79 00 73 00 74 00 65 00 0F 00 72 6D 00 .S.y.s.t.e...rm. 030 20 00 56 00 6F 00 6C 00 75 00 00 00 6D 00 65 00 .V.o.l.u...m.e. 040 53 59 53 54 45 4D 7E 31 20 20 20 16 00 99 98 8C SYSTEM~1 ..... 050 A9 54 A9 54 00 00 99 8C A9 54 03 00 00 00 00 00 .T.T.....T...... 060 41 48 00 6F 00 63 00 6B 00 65 00 0F 00 69 79 00 AH.o.c.k.e...iy. 070 2E 00 62 00 61 00 73 00 00 00 00 00 FF FF FF FF ..b.a.s......... 080 48 4F 43 4B 45 59 20 20 42 41 53 20 00 23 BD 8C HOCKEY BAS .#.. 090 A9 54 A9 54 00 00 F5 91 4D 54 05 00 B9 20 00 00 .T.T....MT... .. 0A0 41 48 00 6F 00 72 00 73 00 65 00 0F 00 63 52 00 AH.o.r.s.e...cR. 0B0 61 00 63 00 65 00 2E 00 62 00 00 00 61 00 73 00 a.c.e...b...a.s.
I'm going to ignore the stuff from 0x000-0x04F since it looks like volume stuff.
Directory Entries
The Microsoft Extensible Firmware Initiative FAT32 File System Specification provides additional information on the fields in the directory.
Looking a 0x060-0x09F looks like a single file's directory entry using 64 bytes. The first part from 0x060 to 0x07F looks like the file name with each character separated by a NULL character. The byte at 0x060 has looks different from the previous values since it has a value of 0x41. The 8.3 plain text file name is at 0x080-0x0x87. Spaces fill out shorter file The extension is at 0x088-0x8A.
It should be easy to print out the directory. Let's give it a shot!
The plain text starts at offset 0x080. Adding a BASIC function to print a single file name. The block is in the MA() array. DF is the offset to the first file directory field.
4400 REM PRINT DIR ENTRY 4410 FOR I=0 TO 7 4420 IF MA(DF+I)<>32 THEN PRINT CHR$(MA(DF+I)); 4430 NEXT I 4440 PRINT "."; 4450 FOR I=8 TO 10 4460 IF MA(DF+I)<> 32 THEN PRINT CHR$(MA(DF+I)); 4470 NEXT I 4480 PRINT 4490 RETURN
The BASIC calling function to work through each directory entry is:
360 DF=128:REM DIR OFFSET 370 GOSUB 4400 380 DF=DF+64 390 IF DF<512 GOTO 370
The result is:
HORSER~1.BAS HURKLE.BAS KINEMA.BAS KING.BAS laby.r
The last entry has issues that need to be figured out. The raw data is:
160 41 4B 00 69 00 6E 00 67 00 2E 00 0F 00 DD 62 00 AK.i.n.g......b. 170 61 00 73 00 00 00 FF FF FF FF 00 00 FF FF FF FF a.s............. 180 4B 49 4E 47 20 20 20 20 42 41 53 20 00 58 BD 8C KING BAS .X.. 190 A9 54 A9 54 00 00 F7 54 F1 52 09 00 F6 22 00 00 .T.T...T.R...".. 1A0 42 69 00 63 00 00 00 FF FF FF FF 0F 00 A4 FF FF Bi.c............ 1B0 FF FF FF FF FF FF FF FF FF FF 00 00 FF FF FF FF ................ 1C0 01 6C 00 61 00 62 00 79 00 72 00 0F 00 A4 69 00 .l.a.b.y.r....i. 1D0 6E 00 74 00 68 00 2E 00 62 00 00 00 61 00 73 00 n.t.h...b...a.s. 1E0 4C 41 42 59 52 49 7E 31 42 41 53 20 00 5E BD 8C LABYRI~1BAS .^.. 1F0 A9 54 A9 54 00 00 F8 54 F1 52 0A 00 7C 13 00 00 .T.T...T.R..|...
The byte offset at 0x1A0 does not have 0x41. This entry also has a different size. It does not have 64 bytes so the final file name was not caught. That's likely because the file name is: labyrinth.basic . Let's ignore long file names for now.
For each directory entry, the fields are
In the FDISK.EXE example above:
- 0-10 = "FDISK EXE ".
- 11 = 0x20: archive?
- 26-27 = Starting cluster - 0x0020
- 28-31 = Filesize - 0x00007317 (29463 bytes)
For KING.BAS:
180 4B 49 4E 47 20 20 20 20 42 41 53 20 00 58 BD 8C KING BAS .X.. 190 A9 54 A9 54 00 00 F7 54 F1 52 09 00 F6 22 00 00 .T.T...T.R..."..
- 0-10 = "KING BAS"
- 11 = 0x20: archive?
- 26-27 = Starting cluster - 0x0009
- 28-31 = Filesize - 0x000022f6 (8950)
The file size matches the File Properties:
Reading in the file
Added BASIC code to read in first LBA of KING.BAS file.
Need the BPB_SecPerClus value from the MBR:
13 BPB_SecPerClus Number of sectors per cluster (1) Must be one of 1, 2, 4, 8, 16, 32, 64, 128. A cluster should have at most 32768 bytes. In rare cases 65536 is OK. <0x40> = 642 sectors per clusters
BASIC code added:
4200 REM PRINT DIR SECTOR VALUES 4203 SC=MA(13):REM SECTORS PER CLUSTER 4205 PRINT "SECTORS PER CLUSTER";:PRINT SC 4210 B0=MA(14):B1=MA(15)
Returns:
SECTORS PER CLUSTER 64
KING.BAS LBA has Starting cluster value of 0x0009.
Call read block code with:
400 FF=DS+((9-2)*SC):REM MANUALLY ADDING FILE OFFSET 410 L0=FF AND 255:L1=FF/256:L2=0:L3=0 420 GOSUB 3800:REM POKE LBA VALS 430 GOSUB 2200:REM CHECK CARD INIT STATUS 440 GOSUB 2400:REM READ IN BLOCK 450 GOSUB 3000:REM DUMP ARRAY
Hard coded line 400 "9" value from KING.BAS file offset
Result matches the file:
READING LBA 25216 000 31 20 50 52 49 4E 54 20 54 41 42 28 33 34 29 3B 1 PRINT TAB(34); 010 22 4B 49 4E 47 22 0D 0A 32 20 50 52 49 4E 54 20 "KING"..2 PRINT 020 54 41 42 28 31 35 29 3B 22 43 52 45 41 54 49 56 TAB(15);"CREATIV 030 45 20 43 4F 4D 50 55 54 49 4E 47 20 20 4D 4F 52 E COMPUTING MOR 040 52 49 53 54 4F 57 4E 2C 20 4E 45 57 20 4A 45 52 RISTOWN, NEW JER 050 53 45 59 22 0D 0A 33 20 50 52 49 4E 54 3A 50 52 SEY"..3 PRINT:PR 060 49 4E 54 3A 50 52 49 4E 54 0D 0A 34 20 50 52 49 INT:PRINT..4 PRI 070 4E 54 20 22 44 4F 20 59 4F 55 20 57 41 4E 54 20 NT "DO YOU WANT 080 49 4E 53 54 52 55 43 54 49 4F 4E 53 22 3B 0D 0A INSTRUCTIONS";.. 090 35 20 49 4E 50 55 54 20 5A 24 0D 0A 36 20 4E 35 5 INPUT Z$..6 N5 0A0 3D 38 0D 0A 31 30 20 49 46 20 4C 45 46 54 24 28 =8..10 IF LEFT$( 0B0 5A 24 2C 31 29 3D 22 4E 22 20 54 48 45 4E 20 34 Z$,1)="N" THEN 4 0C0 37 0D 0A 31 31 20 49 46 20 5A 24 3D 22 41 47 41 7..11 IF Z$="AGA 0D0 49 4E 22 20 54 48 45 4E 20 31 39 36 30 0D 0A 31 IN" THEN 1960..1 0E0 32 20 50 52 49 4E 54 3A 50 52 49 4E 54 3A 50 52 2 PRINT:PRINT:PR 0F0 49 4E 54 0D 0A 32 30 20 50 52 49 4E 54 20 22 43 INT..20 PRINT "C 100 4F 4E 47 52 41 54 55 4C 41 54 49 4F 4E 53 21 20 ONGRATULATIONS! 110 59 4F 55 27 56 45 20 4A 55 53 54 20 42 45 45 4E YOU'VE JUST BEEN 120 20 45 4C 45 43 54 45 44 20 50 52 45 4D 49 45 52 ELECTED PREMIER 130 20 4F 46 20 53 45 54 41 54 53 22 0D 0A 32 32 20 OF SETATS"..22 140 50 52 49 4E 54 20 22 44 45 54 49 4E 55 2C 20 41 PRINT "DETINU, A 150 20 53 4D 41 4C 4C 20 43 4F 4D 4D 55 4E 49 53 54 SMALL COMMUNIST 160 20 49 53 4C 41 4E 44 20 33 30 20 42 59 20 37 30 ISLAND 30 BY 70 170 20 4D 49 4C 45 53 20 4C 4F 4E 47 2E 20 59 4F 55 MILES LONG. YOU 180 52 22 0D 0A 32 34 20 50 52 49 4E 54 20 22 4A 4F R"..24 PRINT "JO 190 42 20 49 53 20 54 4F 20 44 45 43 49 44 45 20 55 B IS TO DECIDE U 1A0 50 4F 4E 20 54 48 45 20 43 4F 4E 54 52 59 27 53 PON THE CONTRY'S 1B0 20 42 55 44 47 45 54 20 41 4E 44 20 44 49 53 54 BUDGET AND DIST 1C0 52 49 42 55 54 45 22 0D 0A 32 36 20 50 52 49 4E RIBUTE"..26 PRIN 1D0 54 20 22 4D 4F 4E 45 59 20 54 4F 20 59 4F 55 52 T "MONEY TO YOUR 1E0 20 43 4F 55 4E 54 52 59 4D 45 4E 20 46 52 4F 4D COUNTRYMEN FROM 1F0 20 54 48 45 20 43 4F 4D 4D 55 4E 41 4C 20 54 52 THE COMMUNAL TR
Some Simplifying Assumptions
Here's a few:
- Not a general purpose solution
- Only handles FAT32 formatted SD cards
- Only handles SD cards where the first block just has a partition table
- Not folders, everything is in the root folder
- Only handles 8.3 file names
- SDHC SD cards
- Larger SD cards
Next Steps
- Do a better job of parsing the directory by skipping the long file names
- Read in the entire directory
- Enter a file name to load
- Load the file
-
​Different SD card MBR formats
05/09/2022 at 22:31 • 0 commentsI tried a couple of other cards and found that they have different MBR formats with no header. I noted this in a previous log. There's an offset at 0x01BE that points to the MBR. My theory is this is true for "bigger" cards. I think the reason I could get it to work on the other card is that it is a small card. I think that bigger cards contain this offset to the MBR and smaller cards don't.
Why Big Cards?
I think the answer is that larger cards have a lot of "wasted" space at the start. There's really no point in supporting smaller cards. I tried to buy some 8GB SD cards and they are getting hard to find and expensive. 32 Gb seems to be the sweet spot at the moment. Of course, all of the existing software in the world for the OSI could easily fit onto one of these SD cards, so why not? 64 GB cards are formatted as exFAT and older devices like cameras don't support exFAT so there's likely to be some market "life" left in 32GB cards.
INIT STATUS - OK LBA0 ? 0 LBA1 ? 0 LBA2 ? 0 000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 130 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 150 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 170 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 190 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 1A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 1B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 82 ................ 1C0 03 00 0C FE FF FF 00 20 00 00 00 04 B7 03 00 00 ....... ........ 1D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 1E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 1F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA ..............U.
Is the first sector Partition Table?
A comment on this page helped.
...the four entry partition table stars at 0x1BE. Each entry is 16 bytes and in this case it's pretty clear only one is filled...
Also, on this FAT32 Structure Information - MBR, FAT32 Boot Sector Introduction page:
...The Master Boot Record is the same for pretty much all Operating Systems. It is located on the first Sector of the Hard Drive, at Cylinder 0, Head 0, Sector 1. It is the first piece of code that your computer runs after it has checked all of your hardware (POST) and turned control of loading software over the hard drive. It also contains the partition table, which defines the different sections of your hard drive...
Also see Hard Drive Partition. Partition Table - NTFS.com
provides information how to determine if LBA 0 is a partition table or not.
Byte Offset Field Length Sample Value Meaning 00 BYTE 0x80 Boot Indicator. Indicates whether the partition is the system partition. Legal values are: 00 = Do not use for booting. 80 = System partition. For that example:
00 03 33 00 06 02 E2 CA EF 00 00 00 11 EB 0E 00 00 - bootable 03 33 00 - CHS of start (ignore cos LBA used these days) 06 - partition type (DOS 3.31+ 16 bit FAT) 02 E2 CA - CHS of end (ignore) EF 00 00 00 - start LBA is 0x000000EF (sector 239) 11 EB 0E 00 - end LBA is 0x000EEB11 (sector 977,681)
My 32GB card has:
00 82 03 00 0C FE FF FF 00 20 00 00 00 04 B7 03 ADDR OFF VALS 01BE 0 00 - bootable 01BF 1 82 03 00 - CHS (ignore) 01C2 4 0C - partition type WIN95 OSR2 FAT32, LBA-mapped 01C3 5 FE FF FF - CHS of end (ignore) 01C6 8 00 20 00 00 - start LBA is 0x00002000 01CA 12 00 04 B7 03 - end LBA is 0x03B70400
Added addresses to last few rows. Fields are:
Offset (bytes) Len Description 0x00 1 Status or physical drive (bit 7 set is for active or bootable, old MBRs only accept 0x80, 0x00 means inactive, and 0x01–0x7F stand for invalid)[c] 0x01 3 CHS address of first absolute sector in partition.[d] The format is described by three bytes, see the next three rows. 0x01 1 h7–0 head[e] x x x x x x x x 0x02 1 c9–8 s5–0 sector in bits 5–0; bits 7–6 are high bits of cylinder[e] x x x x x x x x 0x03 1 c7–0 bits 7–0 of cylinder[e] x x x x x x x x 0x04 1 Partition type[15] 0x05 3 CHS address of last absolute sector in partition.[d] The format is described by 3 bytes, see the next 3 rows. 0x05 1 h7–0 head[e] x x x x x x x x 0x06 1 c9–8 s5–0 sector in bits 5–0; bits 7–6 are high bits of cylinder[e] x x x x x x x x 0x07 1 c7–0 bits 7–0 of cylinder x x x x x x x x 0x08 4 LBA of first absolute sector in the partition[f] 0x0C 4 Number of sectors in partition[f] 0x002000 is LBA0-2 - 0,32,0
INIT STATUS - OK LBA0 ? 0 LBA1 ? 32 LBA2 ? 0 000 EB 00 90 20 20 20 20 20 20 20 20 00 02 40 92 04 ... ..@.. 010 02 00 00 00 00 F8 00 00 3F 00 FF 00 00 20 00 00 ........?.... .. 020 00 04 B7 03 B7 1D 00 00 00 00 00 00 02 00 00 00 ................ 030 01 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 040 80 00 29 39 62 35 30 4E 4F 20 4E 41 4D 45 20 20 ..)9b50NO NAME 050 20 20 46 41 54 33 32 20 20 20 00 00 00 00 00 00 FAT32 ...... 060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 130 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 150 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 170 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 190 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 1A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 1B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 1C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 1D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 1E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 1F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA ..............U.
FAT32 shows up there. Automated BASIC code is:
100 REM OSISDOS READ DIR FROM SD CARD IN BASIC 110 SAVE:REM TURN ON LOGGING TO SERIAL PORT 120 REM SDCARD BASE ADDRESS = $F010 (61456 DEC) 130 DT=61456:ST=61457:A0=61458:A1=61459:A2=61460:REM SD INTERFACE REGISTER ADDRESSES 140 DIM MA(512):REM RESERVE ARRAY SPACE FOR SD CARD DATA 150 GOSUB 2200:REM CHECK CARD INIT STATUS 160 REM READ FIRST LBA 170 L0=0:L1=0:L2=0 180 GOSUB 3800:REM POKE LBA VALS 190 GOSUB 2200:REM CHECK CARD INIT STATUS 200 GOSUB 2400:REM READ IN BLOCK 210 GOSUB 3000:REM DUMP ARRAY 220 GOSUB 3600:REM PRINT FIRST SECTOR KEY VALUES 230 L0=MA(454):L1=MA(455):L2=MA(456) 240 GOSUB 3800:REM POKE LBA VALS 250 GOSUB 2200:REM CHECK CARD INIT STATUS 260 GOSUB 2400:REM READ IN BLOCK 270 PRINT "FIRST BLOCK" 280 GOSUB 3000:REM DUMP ARRAY 290 GOSUB 4000:REM PRINT KEY VALUES IN FIRST BLOCK 1990 END 2200 REM CHECK CARD INIT STATUS 2210 IF PEEK(ST) = 128 THEN RETURN 2220 PRINT "BAD INIT STATUS" 2230 PRINT PEEK(ST) 2240 END 2400 REM READ BLOCK TO ARRAY 2410 POKE ST,0 2420 REM WAIT FOR READ VALUE READY 2430 FOR MO=0 TO 511 2440 SV=PEEK(ST) 2450 IF SV <> 224 GOTO 2440 2460 MA(MO)=PEEK(DT) 2470 NEXT MO 2480 RETURN 3000 REM DUMP TO SCREEN 3010 FOR I=0 TO 31 3020 DV=I 3030 GOSUB 3200 3040 PRINT "0 "; 3050 SA=(I*16) 3060 FOR J=0 TO 15 3070 DV=MA(SA+J) 3080 GOSUB 3200 3090 PRINT " "; 3100 NEXT J 3110 GOSUB 3400 3120 NEXT I 3130 RETURN 3200 REM PRINT DECIMAL NUMBER AS TWO HEX DIGITS 3210 NB=(DV AND 240)/16 3220 GOSUB 3300 3230 NB=DV AND 15 3240 GOSUB 3300 3250 RETURN 3300 REM PRINT NIBBLE 3310 IF NB>9 GOTO 3350 3320 VL=NB+48 3330 PRINT CHR$(VL); 3340 RETURN 3350 REM CHARACTER IS LETTER A-F 3360 VL=NB+55 3370 PRINT CHR$(VL); 3380 RETURN 3400 REM PRINT THE ASCII TABLE TO THE RIGHT 3410 PRINT " "; 3420 FOR OF=SA TO (SA+15) 3430 IF MA(OF)<32 THEN PRINT "."; 3450 IF MA(OF)>126 THEN PRINT "."; 3460 IF MA(OF)>31 AND MA(OF)<127 THEN PRINT CHR$(MA(OF)); 3470 NEXT OF 3480 PRINT 3490 RETURN 3600 REM PRINT FIRST SECTOR KEY VALUES 3610 PRINT "FIRST SECTOR KEY VALUES" 3620 PRINT "LBA0";MA(454) 3630 PRINT "LBA1";MA(455) 3640 PRINT "LBA2";MA(456) 3650 PRINT "LBA3";MA(457) 3660 RETURN 3800 REM POKE LBA VALUES 3810 POKE A0,L0 3820 POKE A1,L1 3830 POKE A2,L2 3840 RETURN 4000 REM PRINT RESERVED SECTOR KEY VALUES 4010 PRINT "ID S/B FAT32, IS: "; 4020 FOR I=82 TO 86 4030 PRINT CHR$(MA(I)); 4040 NEXT I 4050 PRINT 4100 RETURN
Output is:
000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 130 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 150 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 170 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 190 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 1A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 1B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 82 ................ 1C0 03 00 0C FE FF FF 00 20 00 00 00 04 B7 03 00 00 ....... ........ 1D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 1E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 1F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA ..............U. FIRST SECTOR KEY VALUES LBA0 0 LBA1 32 LBA2 0 LBA3 0 FIRST BLOCK 000 EB 00 90 20 20 20 20 20 20 20 20 00 02 40 92 04 ... ..@.. 010 02 00 00 00 00 F8 00 00 3F 00 FF 00 00 20 00 00 ........?.... .. 020 00 04 B7 03 B7 1D 00 00 00 00 00 00 02 00 00 00 ................ 030 01 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 040 80 00 29 39 62 35 30 4E 4F 20 4E 41 4D 45 20 20 ..)9b50NO NAME 050 20 20 46 41 54 33 32 20 20 20 00 00 00 00 00 00 FAT32 ...... 060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 130 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 150 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 170 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 190 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 1A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 1B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 1C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 1D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 1E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 1F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA ..............U. ID S/B FAT32, IS: FAT32
Looks good! Now to pick apart the block to get to the directory using the information from this log entry.
Previous Example
FirstDataSector = BPB_RsvdSecCnt + (BPB_NumFATs * BPB_FATSz32) + RootDirSectors;
= 2106 + (2 * 15331)
= 32,768
Bytes Content 14-15 BPB_RsvdSecCnt Number of reserved sectors (1) FAT12 and FAT16 use 1. FAT32 uses 32. <0x083a> = 2106 decimal 16 BPB_NumFATs Number of FAT copies <0x02> 36-39 BPB_FATSz32 This field is the FAT32 32-bit count of sectors occupied by ONE FAT. Sectors per FAT <0x00003BE3> = 15331 dec
Getting to the Directory
Need to also add in the offset to the block found above. Doing that the directory can be found:
BPRsvdSecCnt 1170 BPNumFATs 2 BPFATSz32 7607 DIR SECTOR 24576 96 0 000 42 20 00 49 00 6E 00 66 00 6F 00 0F 00 72 72 00 B .I.n.f.o...rr. 010 6D 00 61 00 74 00 69 00 6F 00 00 00 6E 00 00 00 m.a.t.i.o...n... 020 01 53 00 79 00 73 00 74 00 65 00 0F 00 72 6D 00 .S.y.s.t.e...rm. 030 20 00 56 00 6F 00 6C 00 75 00 00 00 6D 00 65 00 .V.o.l.u...m.e. 040 53 59 53 54 45 4D 7E 31 20 20 20 16 00 99 98 8C SYSTEM~1 ..... 050 A9 54 A9 54 00 00 99 8C A9 54 03 00 00 00 00 00 .T.T.....T...... 060 41 48 00 6F 00 63 00 6B 00 65 00 0F 00 69 79 00 AH.o.c.k.e...iy. 070 2E 00 62 00 61 00 73 00 00 00 00 00 FF FF FF FF ..b.a.s......... 080 48 4F 43 4B 45 59 20 20 42 41 53 20 00 23 BD 8C HOCKEY BAS .#.. 090 A9 54 A9 54 00 00 F5 91 4D 54 05 00 B9 20 00 00 .T.T....MT... .. 0A0 41 48 00 6F 00 72 00 73 00 65 00 0F 00 63 52 00 AH.o.r.s.e...cR. 0B0 61 00 63 00 65 00 2E 00 62 00 00 00 61 00 73 00 a.c.e...b...a.s. 0C0 48 4F 52 53 45 52 7E 31 42 41 53 20 00 4B BD 8C HORSER~1BAS .K.. 0D0 A9 54 A9 54 00 00 01 92 4D 54 06 00 C6 0B 00 00 .T.T....MT...... 0E0 41 48 00 75 00 72 00 6B 00 6C 00 0F 00 39 65 00 AH.u.r.k.l...9e. 0F0 2E 00 62 00 61 00 73 00 00 00 00 00 FF FF FF FF ..b.a.s......... 100 48 55 52 4B 4C 45 20 20 42 41 53 20 00 52 BD 8C HURKLE BAS .R.. 110 A9 54 A9 54 00 00 F7 54 F1 52 07 00 6E 05 00 00 .T.T...T.R..n... 120 41 4B 00 69 00 6E 00 65 00 6D 00 0F 00 17 61 00 AK.i.n.e.m....a. 130 2E 00 62 00 61 00 73 00 00 00 00 00 FF FF FF FF ..b.a.s......... 140 4B 49 4E 45 4D 41 20 20 42 41 53 20 00 55 BD 8C KINEMA BAS .U.. 150 A9 54 A9 54 00 00 F7 54 F1 52 08 00 2D 03 00 00 .T.T...T.R..-... 160 41 4B 00 69 00 6E 00 67 00 2E 00 0F 00 DD 62 00 AK.i.n.g......b. 170 61 00 73 00 00 00 FF FF FF FF 00 00 FF FF FF FF a.s............. 180 4B 49 4E 47 20 20 20 20 42 41 53 20 00 58 BD 8C KING BAS .X.. 190 A9 54 A9 54 00 00 F7 54 F1 52 09 00 F6 22 00 00 .T.T...T.R...".. 1A0 42 69 00 63 00 00 00 FF FF FF FF 0F 00 A4 FF FF Bi.c............ 1B0 FF FF FF FF FF FF FF FF FF FF 00 00 FF FF FF FF ................ 1C0 01 6C 00 61 00 62 00 79 00 72 00 0F 00 A4 69 00 .l.a.b.y.r....i. 1D0 6E 00 74 00 68 00 2E 00 62 00 00 00 61 00 73 00 n.t.h...b...a.s. 1E0 4C 41 42 59 52 49 7E 31 42 41 53 20 00 5E BD 8C LABYRI~1BAS .^.. 1F0 A9 54 A9 54 00 00 F8 54 F1 52 0A 00 7C 13 00 00 .T.T...T.R..|...
The BASIC code to get to the directory is then:
100 REM OSISDOS READ DIR FROM SD CARD IN BASIC 110 SAVE:REM TURN ON LOGGING TO SERIAL PORT 120 REM SDCARD BASE ADDRESS = $F010 (61456 DEC) 130 DT=61456:ST=61457:A0=61458:A1=61459:A2=61460:REM SD INTERFACE REGISTER ADDRESSES 140 DIM MA(512):REM RESERVE ARRAY SPACE FOR SD CARD DATA 150 GOSUB 2200:REM CHECK CARD INIT STATUS 160 REM READ FIRST LBA 170 L0=0:L1=0:L2=0 180 GOSUB 3800:REM POKE LBA VALS 190 GOSUB 2200:REM CHECK CARD INIT STATUS 200 GOSUB 2400:REM READ IN BLOCK 210 REM GOSUB 3000:REM DUMP ARRAY 220 REM GOSUB 3600:REM PRINT FIRST SECTOR KEY VALUES 230 L0=MA(454):L1=MA(455):L2=MA(456) 232 FS=((L1*256)+L0) 235 PRINT "FIRST SECTOR LBA ";:PRINT FS 240 GOSUB 3800:REM POKE LBA VALS 250 GOSUB 2200:REM CHECK CARD INIT STATUS 260 GOSUB 2400:REM READ IN BLOCK 270 PRINT "FIRST BLOCK" 280 GOSUB 3000:REM DUMP ARRAY 290 REM GOSUB 4000:REM PRINT RESERVED SECTOR KEY VALUES 300 GOSUB 4200:REM CALCULATE DIR SECTOR VALUES 310 L0=DS AND 255:L1=DS/256:L2=0:L3=0 320 GOSUB 3800:REM POKE LBA VALS 330 GOSUB 2200:REM CHECK CARD INIT STATUS 340 GOSUB 2400:REM READ IN BLOCK 350 GOSUB 3000:REM DUMP ARRAY 1990 END 2200 REM CHECK CARD INIT STATUS 2210 IF PEEK(ST) = 128 THEN RETURN 2220 PRINT "BAD INIT STATUS" 2230 PRINT PEEK(ST) 2240 END 2400 REM READ BLOCK TO ARRAY 2410 POKE ST,0 2420 REM WAIT FOR READ VALUE READY 2430 FOR MO=0 TO 511 2440 SV=PEEK(ST) 2450 IF SV <> 224 GOTO 2440 2460 MA(MO)=PEEK(DT) 2470 NEXT MO 2480 RETURN 3000 REM DUMP TO SCREEN 3010 FOR I=0 TO 31 3020 DV=I 3030 GOSUB 3200 3040 PRINT "0 "; 3050 SA=(I*16) 3060 FOR J=0 TO 15 3070 DV=MA(SA+J) 3080 GOSUB 3200 3090 PRINT " "; 3100 NEXT J 3110 GOSUB 3400 3120 NEXT I 3130 RETURN 3200 REM PRINT DECIMAL NUMBER AS TWO HEX DIGITS 3210 NB=(DV AND 240)/16 3220 GOSUB 3300 3230 NB=DV AND 15 3240 GOSUB 3300 3250 RETURN 3300 REM PRINT NIBBLE 3310 IF NB>9 GOTO 3350 3320 VL=NB+48 3330 PRINT CHR$(VL); 3340 RETURN 3350 REM CHARACTER IS LETTER A-F 3360 VL=NB+55 3370 PRINT CHR$(VL); 3380 RETURN 3400 REM PRINT THE ASCII TABLE TO THE RIGHT 3410 PRINT " "; 3420 FOR OF=SA TO (SA+15) 3430 IF MA(OF)<32 THEN PRINT "."; 3450 IF MA(OF)>126 THEN PRINT "."; 3460 IF MA(OF)>31 AND MA(OF)<127 THEN PRINT CHR$(MA(OF)); 3470 NEXT OF 3480 PRINT 3490 RETURN 3600 REM PRINT FIRST SECTOR KEY VALUES 3610 PRINT "FIRST SECTOR KEY VALUES" 3620 PRINT "LBA0";MA(454) 3630 PRINT "LBA1";MA(455) 3640 PRINT "LBA2";MA(456) 3650 PRINT "LBA3";MA(457) 3660 RETURN 3800 REM POKE LBA VALUES 3810 POKE A0,L0 3820 POKE A1,L1 3830 POKE A2,L2 3840 RETURN 4000 REM PRINT RESERVED SECTOR KEY VALUES 4010 PRINT "ID S/B FAT32, IS: "; 4020 FOR I=82 TO 86 4030 PRINT CHR$(MA(I)); 4040 NEXT I 4050 PRINT 4100 RETURN 4200 REM PRINT DIR SECTOR VALUES 4210 B0=MA(14):B1=MA(15) 4220 NF=MA(16) 4230 S0=MA(36):S1=MA(37):S2=MA(38):S3=MA(39) 4240 RS=((256*B1)+B0) 4250 SZ=((256*S1)+S0) 4260 PRINT "BPB_RsvdSecCnt ";:PRINT RS 4270 PRINT "BPB_NumFATs ";:PRINT NF 4280 PRINT "BPB_FATSz32 ";:PRINT SZ 4290 DS=FS+RS+(NF*SZ) 4300 PRINT "DIR SECTOR ";:PRINT DS; 4310 PRINT (DS/256);:PRINT (DS AND 255) 4390 RETURN
I've parsed directories before in this log.
-
DOS/65
08/30/2020 at 17:36 • 0 commentsIt looks as if someone has done a lot of the heavy lifting towards making an operating system for the 6502. The project is DOS/65 by Richard A. Leary.
This could save a lot of work. Richard chose to have a lot of compatibility with CP/M and I've got a lot of time already invested with the Multicomp CP/M systems.
Not sure how tough it will be to port the DOS/65 system but it has to be a lot less than writing an OS from scratch.