1 |
|
/* |
2 |
|
* 1541d64.cpp - 1541 emulation in disk image files (.d64/.x64/zipcode) |
3 |
|
* |
4 |
< |
* Frodo (C) 1994-1997,2002-2003 Christian Bauer |
4 |
> |
* Frodo (C) 1994-1997,2002-2004 Christian Bauer |
5 |
|
* zipcode decoding routines (C) 1993-1997 Marko Mäkelä, Paul David Doherty |
6 |
|
* |
7 |
|
* This program is free software; you can redistribute it and/or modify |
34 |
|
#include "C64.h" |
35 |
|
#include "main.h" |
36 |
|
|
37 |
+ |
#define DEBUG 0 |
38 |
+ |
#include "debug.h" |
39 |
+ |
|
40 |
|
|
41 |
|
// Channel modes (IRC users listen up :-) |
42 |
|
enum { |
115 |
|
* Constructor: Prepare emulation, open image file |
116 |
|
*/ |
117 |
|
|
118 |
< |
D64Drive::D64Drive(IEC *iec, const char *filepath) : Drive(iec), the_file(NULL), bam(ram + 0x700), bam_dirty(false) |
118 |
> |
ImageDrive::ImageDrive(IEC *iec, const char *filepath) : Drive(iec), the_file(NULL), bam(ram + 0x700), bam_dirty(false) |
119 |
|
{ |
120 |
|
for (int i=0; i<18; i++) { |
121 |
|
ch[i].mode = CHMOD_FREE; |
135 |
|
* Destructor |
136 |
|
*/ |
137 |
|
|
138 |
< |
D64Drive::~D64Drive() |
138 |
> |
ImageDrive::~ImageDrive() |
139 |
|
{ |
140 |
|
close_image(); |
141 |
|
} |
145 |
|
* Close the image file |
146 |
|
*/ |
147 |
|
|
148 |
< |
void D64Drive::close_image(void) |
148 |
> |
void ImageDrive::close_image(void) |
149 |
|
{ |
150 |
|
if (the_file) { |
151 |
|
close_all_channels(); |
163 |
|
* Open the image file |
164 |
|
*/ |
165 |
|
|
166 |
< |
bool D64Drive::change_image(const char *path) |
166 |
> |
bool ImageDrive::change_image(const char *path) |
167 |
|
{ |
168 |
|
// Close old image file |
169 |
|
close_image(); |
197 |
|
* Open channel |
198 |
|
*/ |
199 |
|
|
200 |
< |
uint8 D64Drive::Open(int channel, const uint8 *name, int name_len) |
200 |
> |
uint8 ImageDrive::Open(int channel, const uint8 *name, int name_len) |
201 |
|
{ |
202 |
+ |
D(bug("ImageDrive::Open channel %d, file %s\n", channel, name)); |
203 |
+ |
|
204 |
|
set_error(ERR_OK); |
205 |
|
|
206 |
|
// Channel 15: execute file name as command |
231 |
|
* Open file |
232 |
|
*/ |
233 |
|
|
234 |
< |
uint8 D64Drive::open_file(int channel, const uint8 *name, int name_len) |
234 |
> |
uint8 ImageDrive::open_file(int channel, const uint8 *name, int name_len) |
235 |
|
{ |
236 |
|
uint8 plain_name[NAMEBUF_LENGTH]; |
237 |
|
int plain_name_len; |
242 |
|
if (plain_name_len > 16) |
243 |
|
plain_name_len = 16; |
244 |
|
|
245 |
+ |
D(bug(" plain name %s, type %d, mode %d\n", plain_name, type, mode)); |
246 |
+ |
|
247 |
|
// Channel 0 is READ, channel 1 is WRITE |
248 |
|
if (channel == 0 || channel == 1) { |
249 |
|
mode = channel ? FMODE_WRITE : FMODE_READ; |
276 |
|
if (find_first_file(plain_name, plain_name_len, dir_track, dir_sector, entry)) { |
277 |
|
|
278 |
|
// File exists |
279 |
+ |
D(bug(" file exists, dir track %d, sector %d, entry %d\n", dir_track, dir_sector, entry)); |
280 |
|
ch[channel].dir_track = dir_track; |
281 |
|
ch[channel].dir_sector = dir_sector; |
282 |
|
ch[channel].entry = entry; |
342 |
|
} else { |
343 |
|
|
344 |
|
// File doesn't exist |
345 |
+ |
D(bug(" file not found\n")); |
346 |
+ |
|
347 |
|
// Set file type to SEQ if not specified in file name |
348 |
|
if (type == FTYPE_DEL) |
349 |
|
type = FTYPE_SEQ; |
364 |
|
* Open channel for reading from file given track/sector of first block |
365 |
|
*/ |
366 |
|
|
367 |
< |
uint8 D64Drive::open_file_ts(int channel, int track, int sector) |
367 |
> |
uint8 ImageDrive::open_file_ts(int channel, int track, int sector) |
368 |
|
{ |
369 |
+ |
D(bug("open_file_ts track %d, sector %d\n", track, sector)); |
370 |
+ |
|
371 |
|
// Allocate buffer and set channel mode |
372 |
|
int buf = alloc_buffer(-1); |
373 |
|
if (buf == -1) { |
391 |
|
* Create file and open channel for writing to file |
392 |
|
*/ |
393 |
|
|
394 |
< |
uint8 D64Drive::create_file(int channel, const uint8 *name, int name_len, int type, bool overwrite) |
394 |
> |
uint8 ImageDrive::create_file(int channel, const uint8 *name, int name_len, int type, bool overwrite) |
395 |
|
{ |
396 |
+ |
D(bug("create_file %s, type %d\n", name, type)); |
397 |
+ |
|
398 |
|
// Allocate buffer |
399 |
|
int buf = alloc_buffer(-1); |
400 |
|
if (buf == -1) { |
421 |
|
return ST_OK; |
422 |
|
} |
423 |
|
ch[channel].num_blocks = 1; |
424 |
+ |
D(bug(" first data block on track %d, sector %d\n", ch[channel].track, ch[channel].sector)); |
425 |
|
|
426 |
|
// Write directory entry |
427 |
|
memset(de, 0, SIZEOF_DE); |
454 |
|
const char type_char_2[] = "EERSELQG"; |
455 |
|
const char type_char_3[] = "LQGRL???"; |
456 |
|
|
457 |
< |
uint8 D64Drive::open_directory(const uint8 *pattern, int pattern_len) |
457 |
> |
uint8 ImageDrive::open_directory(const uint8 *pattern, int pattern_len) |
458 |
|
{ |
459 |
|
// Special treatment for "$0" |
460 |
|
if (pattern[0] == '0' && pattern_len == 1) { |
609 |
|
* Open channel for direct buffer access |
610 |
|
*/ |
611 |
|
|
612 |
< |
uint8 D64Drive::open_direct(int channel, const uint8 *name) |
612 |
> |
uint8 ImageDrive::open_direct(int channel, const uint8 *name) |
613 |
|
{ |
614 |
|
int buf = -1; |
615 |
|
|
642 |
|
* Close channel |
643 |
|
*/ |
644 |
|
|
645 |
< |
uint8 D64Drive::Close(int channel) |
645 |
> |
uint8 ImageDrive::Close(int channel) |
646 |
|
{ |
647 |
+ |
D(bug("ImageDrive::Close channel %d\n", channel)); |
648 |
+ |
|
649 |
|
switch (ch[channel].mode) { |
650 |
|
case CHMOD_FREE: |
651 |
|
break; |
672 |
|
// Write last data block |
673 |
|
ch[channel].buf[0] = 0; |
674 |
|
ch[channel].buf[1] = ch[channel].buf_len - 1; |
675 |
+ |
D(bug(" writing last data block\n")); |
676 |
|
if (!write_sector(ch[channel].track, ch[channel].sector, ch[channel].buf)) |
677 |
|
goto free; |
678 |
|
|
690 |
|
de[DE_OVR_TRACK] = de[DE_OVR_SECTOR] = 0; |
691 |
|
} |
692 |
|
write_sector(ch[channel].dir_track, ch[channel].dir_sector, dir); |
693 |
+ |
D(bug(" directory entry updated\n")); |
694 |
|
} |
695 |
|
free: free_buffer(ch[channel].buf_num); |
696 |
|
ch[channel].buf = NULL; |
712 |
|
* Close all channels |
713 |
|
*/ |
714 |
|
|
715 |
< |
void D64Drive::close_all_channels() |
715 |
> |
void ImageDrive::close_all_channels() |
716 |
|
{ |
717 |
|
for (int i=0; i<15; i++) |
718 |
|
Close(i); |
727 |
|
* Read from channel |
728 |
|
*/ |
729 |
|
|
730 |
< |
uint8 D64Drive::Read(int channel, uint8 &byte) |
730 |
> |
uint8 ImageDrive::Read(int channel, uint8 &byte) |
731 |
|
{ |
732 |
+ |
// D(bug("ImageDrive::Read channel %d\n", channel)); |
733 |
+ |
|
734 |
|
switch (ch[channel].mode) { |
735 |
|
case CHMOD_FREE: |
736 |
|
if (current_error == ERR_OK) |
756 |
|
|
757 |
|
// Read next block if necessary |
758 |
|
if (ch[channel].buf_len == 0 && ch[channel].buf[0]) { |
759 |
+ |
D(bug(" reading next data block track %d, sector %d\n", ch[channel].buf[0], ch[channel].buf[1])); |
760 |
|
if (!read_sector(ch[channel].buf[0], ch[channel].buf[1], ch[channel].buf)) |
761 |
|
return ST_READ_TIMEOUT; |
762 |
|
ch[channel].buf_ptr = ch[channel].buf + 2; |
795 |
|
* Write byte to channel |
796 |
|
*/ |
797 |
|
|
798 |
< |
uint8 D64Drive::Write(int channel, uint8 byte, bool eoi) |
798 |
> |
uint8 ImageDrive::Write(int channel, uint8 byte, bool eoi) |
799 |
|
{ |
800 |
+ |
// D(bug("ImageDrive::Write channel %d, byte %02x, eoi %d\n", channel, byte, eoi)); |
801 |
+ |
|
802 |
|
switch (ch[channel].mode) { |
803 |
|
case CHMOD_FREE: |
804 |
|
if (current_error == ERR_OK) |
838 |
|
if (!alloc_next_block(track, sector, DATA_INTERLEAVE)) |
839 |
|
return ST_TIMEOUT; |
840 |
|
ch[channel].num_blocks++; |
841 |
+ |
D(bug("next data block on track %d, sector %d\n", track, sector)); |
842 |
|
|
843 |
|
// Write buffer with link to new block |
844 |
|
ch[channel].buf[0] = track; |
872 |
|
* Reset drive |
873 |
|
*/ |
874 |
|
|
875 |
< |
void D64Drive::Reset(void) |
875 |
> |
void ImageDrive::Reset(void) |
876 |
|
{ |
877 |
|
close_all_channels(); |
878 |
|
|
899 |
|
* <- Allocated buffer number or -1 |
900 |
|
*/ |
901 |
|
|
902 |
< |
int D64Drive::alloc_buffer(int want) |
902 |
> |
int ImageDrive::alloc_buffer(int want) |
903 |
|
{ |
904 |
|
if (want == -1) { |
905 |
|
for (want=3; want>=0; want--) |
925 |
|
* Free floppy buffer |
926 |
|
*/ |
927 |
|
|
928 |
< |
void D64Drive::free_buffer(int buf) |
928 |
> |
void ImageDrive::free_buffer(int buf) |
929 |
|
{ |
930 |
|
buf_free[buf] = true; |
931 |
|
} |
954 |
|
return *n == 0xa0 || c == 16; |
955 |
|
} |
956 |
|
|
957 |
< |
bool D64Drive::find_file(const uint8 *pattern, int pattern_len, int &dir_track, int &dir_sector, int &entry, bool cont) |
957 |
> |
bool ImageDrive::find_file(const uint8 *pattern, int pattern_len, int &dir_track, int &dir_sector, int &entry, bool cont) |
958 |
|
{ |
959 |
|
// Counter to prevent cyclic directories from resulting in an infinite loop |
960 |
|
int num_dir_blocks = 0; |
992 |
|
return false; |
993 |
|
} |
994 |
|
|
995 |
< |
bool D64Drive::find_first_file(const uint8 *pattern, int pattern_len, int &dir_track, int &dir_sector, int &entry) |
995 |
> |
bool ImageDrive::find_first_file(const uint8 *pattern, int pattern_len, int &dir_track, int &dir_sector, int &entry) |
996 |
|
{ |
997 |
|
return find_file(pattern, pattern_len, dir_track, dir_sector, entry, false); |
998 |
|
} |
999 |
|
|
1000 |
< |
bool D64Drive::find_next_file(const uint8 *pattern, int pattern_len, int &dir_track, int &dir_sector, int &entry) |
1000 |
> |
bool ImageDrive::find_next_file(const uint8 *pattern, int pattern_len, int &dir_track, int &dir_sector, int &entry) |
1001 |
|
{ |
1002 |
|
return find_file(pattern, pattern_len, dir_track, dir_sector, entry, true); |
1003 |
|
} |
1009 |
|
* The track/sector and entry numbers are returned |
1010 |
|
*/ |
1011 |
|
|
1012 |
< |
bool D64Drive::alloc_dir_entry(int &track, int §or, int &entry) |
1012 |
> |
bool ImageDrive::alloc_dir_entry(int &track, int §or, int &entry) |
1013 |
|
{ |
1014 |
|
// First look for free entry in existing directory blocks |
1015 |
|
dir[DIR_NEXT_TRACK] = DIR_TRACK; |
1020 |
|
|
1021 |
|
uint8 *de = dir + DIR_ENTRIES; |
1022 |
|
for (entry=0; entry<8; entry++, de+=SIZEOF_DE) { |
1023 |
< |
if (de[DE_TYPE] == 0) |
1023 |
> |
if (de[DE_TYPE] == 0) { |
1024 |
> |
D(bug(" allocated entry %d in dir track %d, sector %d\n", entry, track, sector)); |
1025 |
|
return true; |
1026 |
+ |
} |
1027 |
|
} |
1028 |
|
} |
1029 |
|
|
1031 |
|
int last_track = track, last_sector = sector; |
1032 |
|
if (!alloc_next_block(track, sector, DIR_INTERLEAVE)) |
1033 |
|
return false; |
1034 |
+ |
D(bug(" new directory block track %d, sector %d\n", track, sector)); |
1035 |
|
|
1036 |
|
// Write link to new block to last block |
1037 |
|
dir[DIR_NEXT_TRACK] = track; |
1050 |
|
* Test if block is free in BAM (track/sector are not checked for validity) |
1051 |
|
*/ |
1052 |
|
|
1053 |
< |
bool D64Drive::is_block_free(int track, int sector) |
1053 |
> |
bool ImageDrive::is_block_free(int track, int sector) |
1054 |
|
{ |
1055 |
|
uint8 *p = bam + BAM_BITMAP + (track - 1) * 4; |
1056 |
|
int byte = sector / 8 + 1; |
1063 |
|
* Get number of free blocks on a track |
1064 |
|
*/ |
1065 |
|
|
1066 |
< |
int D64Drive::num_free_blocks(int track) |
1066 |
> |
int ImageDrive::num_free_blocks(int track) |
1067 |
|
{ |
1068 |
|
return bam[BAM_BITMAP + (track - 1) * 4]; |
1069 |
|
} |
1089 |
|
* Allocate block in BAM, returns error code |
1090 |
|
*/ |
1091 |
|
|
1092 |
< |
int D64Drive::alloc_block(int track, int sector) |
1092 |
> |
int ImageDrive::alloc_block(int track, int sector) |
1093 |
|
{ |
1094 |
|
if (track < 1 || track > 35 || sector < 0 || sector >= num_sectors[track]) |
1095 |
|
return ERR_ILLEGALTS; |
1102 |
|
if (p[byte] & (1 << bit)) { |
1103 |
|
|
1104 |
|
// Yes, allocate and decrement free block count |
1105 |
+ |
D(bug("allocating block at track %d, sector %d\n", track, sector)); |
1106 |
|
p[byte] &= ~(1 << bit); |
1107 |
|
p[0]--; |
1108 |
|
bam_dirty = true; |
1117 |
|
* Free block in BAM, returns error code |
1118 |
|
*/ |
1119 |
|
|
1120 |
< |
int D64Drive::free_block(int track, int sector) |
1120 |
> |
int ImageDrive::free_block(int track, int sector) |
1121 |
|
{ |
1122 |
|
if (track < 1 || track > 35 || sector < 0 || sector >= num_sectors[track]) |
1123 |
|
return ERR_ILLEGALTS; |
1130 |
|
if (!(p[byte] & (1 << bit))) { |
1131 |
|
|
1132 |
|
// Yes, free and increment free block count |
1133 |
+ |
D(bug("freeing block at track %d, sector %d\n", track, sector)); |
1134 |
|
p[byte] |= (1 << bit); |
1135 |
|
p[0]++; |
1136 |
|
bam_dirty = true; |
1143 |
|
* Allocate chain of data blocks in BAM |
1144 |
|
*/ |
1145 |
|
|
1146 |
< |
bool D64Drive::alloc_block_chain(int track, int sector) |
1146 |
> |
bool ImageDrive::alloc_block_chain(int track, int sector) |
1147 |
|
{ |
1148 |
|
uint8 buf[256]; |
1149 |
|
while (alloc_block(track, sector) == ERR_OK) { |
1160 |
|
* Free chain of data blocks in BAM |
1161 |
|
*/ |
1162 |
|
|
1163 |
< |
bool D64Drive::free_block_chain(int track, int sector) |
1163 |
> |
bool ImageDrive::free_block_chain(int track, int sector) |
1164 |
|
{ |
1165 |
|
uint8 buf[256]; |
1166 |
|
while (free_block(track, sector) == ERR_OK) { |
1180 |
|
* begin |
1181 |
|
*/ |
1182 |
|
|
1183 |
< |
bool D64Drive::alloc_next_block(int &track, int §or, int interleave) |
1183 |
> |
bool ImageDrive::alloc_next_block(int &track, int §or, int interleave) |
1184 |
|
{ |
1185 |
|
// Find track with free blocks |
1186 |
|
bool side_changed = false; |
1330 |
|
} |
1331 |
|
|
1332 |
|
// Read sector and set error message, returns false on error |
1333 |
< |
bool D64Drive::read_sector(int track, int sector, uint8 *buffer) |
1333 |
> |
bool ImageDrive::read_sector(int track, int sector, uint8 *buffer) |
1334 |
|
{ |
1335 |
|
int error = ::read_sector(the_file, desc, track, sector, buffer); |
1336 |
|
if (error) |
1339 |
|
} |
1340 |
|
|
1341 |
|
// Write sector and set error message, returns false on error |
1342 |
< |
bool D64Drive::write_sector(int track, int sector, uint8 *buffer) |
1342 |
> |
bool ImageDrive::write_sector(int track, int sector, uint8 *buffer) |
1343 |
|
{ |
1344 |
|
int error = ::write_sector(the_file, desc, track, sector, buffer); |
1345 |
|
if (error) |
1422 |
|
*/ |
1423 |
|
|
1424 |
|
// BLOCK-READ:channel,0,track,sector |
1425 |
< |
void D64Drive::block_read_cmd(int channel, int track, int sector, bool user_cmd) |
1425 |
> |
void ImageDrive::block_read_cmd(int channel, int track, int sector, bool user_cmd) |
1426 |
|
{ |
1427 |
|
if (channel >= 16 || ch[channel].mode != CHMOD_DIRECT) { |
1428 |
|
set_error(ERR_NOCHANNEL); |
1440 |
|
} |
1441 |
|
|
1442 |
|
// BLOCK-WRITE:channel,0,track,sector |
1443 |
< |
void D64Drive::block_write_cmd(int channel, int track, int sector, bool user_cmd) |
1443 |
> |
void ImageDrive::block_write_cmd(int channel, int track, int sector, bool user_cmd) |
1444 |
|
{ |
1445 |
|
if (write_protected) { |
1446 |
|
set_error(ERR_WRITEPROTECT); |
1461 |
|
} |
1462 |
|
|
1463 |
|
// BLOCK-ALLOCATE:0,track,sector |
1464 |
< |
void D64Drive::block_allocate_cmd(int track, int sector) |
1464 |
> |
void ImageDrive::block_allocate_cmd(int track, int sector) |
1465 |
|
{ |
1466 |
|
int err = alloc_block(track, sector); |
1467 |
|
if (err) { |
1489 |
|
} |
1490 |
|
|
1491 |
|
// BLOCK-FREE:0,track,sector |
1492 |
< |
void D64Drive::block_free_cmd(int track, int sector) |
1492 |
> |
void ImageDrive::block_free_cmd(int track, int sector) |
1493 |
|
{ |
1494 |
|
int err = free_block(track, sector); |
1495 |
|
if (err) |
1497 |
|
} |
1498 |
|
|
1499 |
|
// BUFFER-POINTER:channel,pos |
1500 |
< |
void D64Drive::buffer_pointer_cmd(int channel, int pos) |
1500 |
> |
void ImageDrive::buffer_pointer_cmd(int channel, int pos) |
1501 |
|
{ |
1502 |
|
if (channel >= 16 || ch[channel].mode != CHMOD_DIRECT) { |
1503 |
|
set_error(ERR_NOCHANNEL); |
1508 |
|
} |
1509 |
|
|
1510 |
|
// M-R<adr low><adr high>[<number>] |
1511 |
< |
void D64Drive::mem_read_cmd(uint16 adr, uint8 len) |
1511 |
> |
void ImageDrive::mem_read_cmd(uint16 adr, uint8 len) |
1512 |
|
{ |
1513 |
|
error_len = len; |
1514 |
|
if (adr >= 0x300 && adr < 0x1000) { |
1525 |
|
} |
1526 |
|
|
1527 |
|
// M-W<adr low><adr high><number><data...> |
1528 |
< |
void D64Drive::mem_write_cmd(uint16 adr, uint8 len, uint8 *p) |
1528 |
> |
void ImageDrive::mem_write_cmd(uint16 adr, uint8 len, uint8 *p) |
1529 |
|
{ |
1530 |
|
while (len) { |
1531 |
|
if (adr >= 0x300 && adr < 0x1000) { |
1542 |
|
// COPY:new=file1,file2,... |
1543 |
|
// ^ ^ |
1544 |
|
// new_file old_files |
1545 |
< |
void D64Drive::copy_cmd(const uint8 *new_file, int new_file_len, const uint8 *old_files, int old_files_len) |
1545 |
> |
void ImageDrive::copy_cmd(const uint8 *new_file, int new_file_len, const uint8 *old_files, int old_files_len) |
1546 |
|
{ |
1547 |
|
// Check if destination file is already present |
1548 |
|
int dir_track, dir_sector, entry; |
1605 |
|
// RENAME:new=old |
1606 |
|
// ^ ^ |
1607 |
|
// new_file old_file |
1608 |
< |
void D64Drive::rename_cmd(const uint8 *new_file, int new_file_len, const uint8 *old_file, int old_file_len) |
1608 |
> |
void ImageDrive::rename_cmd(const uint8 *new_file, int new_file_len, const uint8 *old_file, int old_file_len) |
1609 |
|
{ |
1610 |
|
// Check if destination file is already present |
1611 |
|
int dir_track, dir_sector, entry; |
1636 |
|
// SCRATCH:file1,file2,... |
1637 |
|
// ^ |
1638 |
|
// files |
1639 |
< |
void D64Drive::scratch_cmd(const uint8 *files, int files_len) |
1639 |
> |
void ImageDrive::scratch_cmd(const uint8 *files, int files_len) |
1640 |
|
{ |
1641 |
|
// Check for write-protection |
1642 |
|
if (write_protected) { |
1684 |
|
} |
1685 |
|
|
1686 |
|
// INITIALIZE |
1687 |
< |
void D64Drive::initialize_cmd(void) |
1687 |
> |
void ImageDrive::initialize_cmd(void) |
1688 |
|
{ |
1689 |
|
// Close all channels and re-read BAM |
1690 |
|
close_all_channels(); |
1698 |
|
// NEW:name,id |
1699 |
|
// ^ ^ |
1700 |
|
// name comma (or NULL) |
1701 |
< |
void D64Drive::new_cmd(const uint8 *name, int name_len, const uint8 *comma) |
1701 |
> |
void ImageDrive::new_cmd(const uint8 *name, int name_len, const uint8 *comma) |
1702 |
|
{ |
1703 |
|
// Check for write-protection |
1704 |
|
if (write_protected) { |
1735 |
|
} |
1736 |
|
|
1737 |
|
// VALIDATE |
1738 |
< |
void D64Drive::validate_cmd(void) |
1738 |
> |
void ImageDrive::validate_cmd(void) |
1739 |
|
{ |
1740 |
|
// Backup of old BAM in case something goes amiss |
1741 |
|
uint8 old_bam[256]; |