Extracting data from DOS 2.0/2.5 disks
In earlier posts I explained how data is organized on a DOS 2.0/2.5 disk, but didn't provide any code specific to the directories and files (other than link_data). This post remedies that, in part to fill in those gaps, and in part because I'm going to fix some disks that require a bit more effort than just copying a sector or two from another disk.
Let's start with a helper function
sub toascii { my ($val) = @_; $val =~ tr/\x00-\x1f\x60\x7b\x7d\x7e\x80-\x9a\x9c-\x9f\xff/./; $val =~ tr/\x7f\x9b\xa0-\xfe/\t\n\x20-\x7e/; $val; }
This routine will convert some ATASCII text to ASCII text. It translates unprintable characters to periods, inverse characters to their non-inverse equivalents, and 0x7F and 0x9B to tab and carriage return.
OK, now let's write a function that dumps a DOS directory.
sub read_dos_dir { my ($file, $sector) = @_; my $disk = read_disk($file); $sector = 361 if !defined $sector; my $dirData = ''; $dirData .= substr($disk->{buff}, (sector_offset($disk, $_))[0], 0x80) for ($sector .. $sector + ; for (my $i = 0; $i < 64; $i++) { my $dirEntry = substr($dirData, $i * 16, 16); my ($status, $sectorCount, $firstSector) = unpack "Cvv", substr($dirEntry, 0, 5); last if $status == 0; my $name = toascii(substr($dirEntry, 5)); printf "%02d %02x %03d %03d %s\n", $i, $status, $sectorCount, $firstSector, $name; } }
Recall that the directory is in sectors 361 to 368, so this routine first reads in those sectors. Actually, it reads in 8 sectors from anywhere on the disk, since MYDOS can locates sub-directory information in other locations and we may want to reuse this code in the future. It then reads each 16-byte entry, getting the start, sector count, first sector and name (which it converts to ascii, thus the helper) and printing them. It stops when it gets to an entry that has a status byte set to zero.
Note that this routine works even with double density disks, by ignoring the 2nd half of every sector. sector_offset returns the size of the sectors, but we ignore that and always use 0x80 = 128.
OK, now let's see if we can extract a file from a DOS disk:
sub read_dos_file { my ($file, $sector, $ascii) = @_; $ascii = 0 if !defined $ascii; my $disk = read_disk($file); my $out = ''; for (;;) { my ($pos, $sectorSize) = sector_offset($disk, $sector); my $sectorData = substr($disk->{buff}, $pos, $sectorSize); my ($nextSector, $nBytes) = link_data($sectorData); $out .= substr($sectorData, 0, $nBytes); $sector = $nextSector & 0x3ff; last if $sector == 0; } $out = toascii($out) if $ascii; write_file('out.bin', $out); }
In this routine, you provide the first sector of the file you want to extract. It then follows the sector links until the next sector is 0. It then writes the file out, optionally converting it to ascii first if requested.
Now to hook this up to our tool, we add the calls into our main subroutine as follows:
read_dos_dir(@_) if $opt eq '-dir'; read_dos_file(@_) if $opt eq '-file';
And voila, atr.pl has some rudimentary DOS inspection abilities. You can exercise them on Bellcom Disk #1 like so.
C:> atr.pl -dir 001.atr 00 62 037 004 DOS SYS 01 62 042 041 DUP SYS 02 62 012 083 AUTORUN SYS 03 62 016 095 HELP DOC 04 62 132 111 ENTPRIS1BAS 05 62 235 243 ENTPRIS2BAS 06 62 047 487 STARTREKDOC 07 62 184 534 STARTREKBAS C:> atr.pl -file 001.atr 95 1 C:> head out.bin - B E L L C O M - [Public Domain Library] BOX 1043, PETERBOROUGH ONTARIO, CANADA K9J 7A5 ----------------------- ATARI MENU INSTRUCTIONS -----------------------
That's enough for today, I've attached the current scripts to this post for anyone who wants to use them without constructing them from the snippets in the posts.
- 1
0 Comments
Recommended Comments
There are no comments to display.