From e748485386f92fe940d63eff43f27723b51ef8a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20J=C3=B8rgensen?= Date: Sat, 30 Apr 2022 09:49:29 +0200 Subject: [PATCH] Improve disk write support --- README.md | 2 +- firmware/commands.h | 12 +- firmware/disk_drive.c | 408 +++++++++++++++++++++++++++++++++++------- firmware/disk_drive.h | 30 +++- firmware/fs_drive.c | 18 +- firmware/loader.c | 11 +- launcher/disk.s | 277 +++++++++++++++++++--------- 7 files changed, 599 insertions(+), 159 deletions(-) diff --git a/README.md b/README.md index d0ba9ef..28a182a 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ Use it at your own risk! Kung Fu Flash will work with the PAL version of the Commodore 64 or Commodore 128. Support for the NTSC version is still considered experimental. -Disk drive emulation is using kernal vectors and will not work with fast loaders or software that uses direct hardware access which a lot of games does. Currently REL files are not supported and there is only limited write support. +Disk drive emulation is using kernal vectors and will not work with fast loaders or software that uses direct hardware access which a lot of games does. Currently REL files are not supported and only a subset of the Commodore DOS commands are supported. ## Thanks Kung Fu Flash was based on or uses other open source projects: diff --git a/firmware/commands.h b/firmware/commands.h index 042e6fa..b1f4c6b 100644 --- a/firmware/commands.h +++ b/firmware/commands.h @@ -52,7 +52,6 @@ typedef enum CMD_MOUNT_DISK, CMD_WAIT_RESET, // Disable screen and wait for reset - CMD_WAIT_SYNC, // Disk commands CMD_NO_DRIVE = 0x10, @@ -60,7 +59,8 @@ typedef enum CMD_NOT_FOUND, CMD_END_OF_FILE, - // SYNC command + // SYNC commands + CMD_WAIT_SYNC = 0x50, CMD_SYNC = 0x55, REPLY_OK = 0x80, @@ -83,11 +83,17 @@ typedef enum // Disk replies REPLY_LOAD = 0x90, REPLY_SAVE, + REPLY_OPEN, REPLY_CLOSE, + REPLY_TALK, REPLY_UNTALK, - REPLY_GET_BYTE, + REPLY_SEND_BYTE, + + REPLY_LISTEN, + REPLY_UNLISTEN, + REPLY_RECEIVE_BYTE } COMMAND_TYPE; typedef enum diff --git a/firmware/disk_drive.c b/firmware/disk_drive.c index 540388b..3f75a84 100644 --- a/firmware/disk_drive.c +++ b/firmware/disk_drive.c @@ -54,7 +54,7 @@ static void put_diskname(u8 **ptr, char* str) str[16] = '"'; str[17] = ' '; - for (u8 i=0; i<23; i++) + for (u32 i=0; i<23; i++) { char c = *str++; if (c == 0xa0) @@ -71,7 +71,7 @@ static void put_filename(u8 **ptr, const char* str) put_u8(ptr, '"'); char end_char = '"'; - for (u8 i=0; i<16; i++) + for (u32 i=0; i<16; i++) { char c = *str++; if (end_char != '"') @@ -123,7 +123,7 @@ static void put_dir_entry(u8 **ptr, D64_DIR_ENTRY *entry) static bool disk_filename_match(D64_DIR_ENTRY *entry, const char *filename) { bool found = true; - for (u8 i=0; i<16; i++) + for (u32 i=0; i<16; i++) { char f = filename[i]; char e = entry->filename[i]; @@ -292,6 +292,16 @@ static bool disk_write_finalize(DISK_CHANNEL *channel) return fs_write_finalize(channel); } +static bool disk_delete_file(DISK_CHANNEL *channel, D64_DIR_ENTRY *entry) +{ + if (dat_file.disk.mode) + { + return d64_delete_file(&channel->d64, entry); + } + + return fs_delete_file(channel, entry); +} + static void disk_put_dir_header(DISK_CHANNEL *channel, u8 **ptr) { put_u16(ptr, dir_start_addr); // start address @@ -378,8 +388,7 @@ static void disk_parse_filename(char *filename, PARSED_FILENAME *parsed) // Scan for wildcard, type and mode while (*f_ptr) { - // Allow comma in 8.3 filenames on SD card - TODO: Can we do better here? - if (*f_ptr == ',' && dat_file.disk.mode) + if (*f_ptr == ',') { *f_ptr++ = 0; // Null terminate filename end_of_filename = true; @@ -475,9 +484,11 @@ static bool disk_open_read(DISK_CHANNEL *channel, const char *filename, if (entry) { disk_open_file_read(channel, entry); + disk_last_error = DISK_STATUS_OK; return true; } + disk_last_error = DISK_STATUS_NOT_FOUND; return false; } @@ -562,19 +573,61 @@ static u8 disk_handle_load_dir(DISK_CHANNEL *channel) u16 prg_size = (ptr - KFF_BUF) - 4; *(u16 *)KFF_BUF = prg_size; + disk_last_error = DISK_STATUS_OK; return CMD_NONE; } +static void disk_receive_data(u8 *buf) +{ + u8 size = c64_receive_byte(); + *buf++ = size; + + while (size--) + { + *buf++ = c64_receive_byte(); + } +} + +static void disk_send_data(u8 *buf) +{ + u8 size = *buf++; + c64_send_byte(size); + + while (size--) + { + c64_send_byte(*buf++); + } +} + static void disk_close_channel(DISK_CHANNEL *channel) { - channel->buf_mode = DISK_BUF_NONE; + if (channel->buf_mode == DISK_BUF_SAVE) + { + c64_send_command(CMD_WAIT_SYNC); + c64_interface(false); + disk_receive_data(channel->buf2); // Save temp data + + if (channel->buf_ptr) + { + disk_write_data(channel, channel->buf, channel->buf_ptr); + } + + disk_write_finalize(channel); + + disk_send_data(channel->buf2); // Send temp data back + c64_interface_sync(); + } + + channel->buf_mode = DISK_BUF_USE; + channel->buf_ptr = 0; channel->buf_len = 0; + channel->buf2_ptr = 0; d64_init(channel->d64.image, &channel->d64); } static void disk_init_all_channels(D64_IMAGE *image, DISK_CHANNEL *channels) { - for (u8 i=0; i<16; i++) + for (u32 i=0; i<16; i++) { DISK_CHANNEL *channel = channels + i; channel->number = i; @@ -624,17 +677,6 @@ static u8 disk_save_file(DISK_CHANNEL *channel, PARSED_FILENAME *parsed, return CMD_NONE; } -static void disk_send_data(u8 *buf) -{ - u8 size = *buf++; - c64_send_byte(size); - - while (size--) - { - c64_send_byte(*buf++); - } -} - static u8 disk_handle_save(DISK_CHANNEL *channel) { char *filename = channel->filename; @@ -650,9 +692,13 @@ static u8 disk_handle_save(DISK_CHANNEL *channel) !d64_is_file_type(existing, parsed.type))) || (!existing && parsed.wildcard)) { + disk_last_error = DISK_STATUS_EXISTS; return CMD_DISK_ERROR; } + disk_receive_data(channel->buf); // Save temp data + + disk_last_error = DISK_STATUS_OK; c64_send_command(CMD_NONE); c64_interface(false); @@ -665,12 +711,96 @@ static u8 disk_handle_save(DISK_CHANNEL *channel) return cmd; } -static bool disk_read_status(DISK_CHANNEL *channel, char *filename) +static u32 disk_write_status(DISK_CHANNEL *channel, u8 status, u8 track, u8 sector) +{ + const char *status_text; + switch (status) + { + case DISK_STATUS_SCRATCHED: + status_text = "FILES SCRATCHED"; + break; + + case DISK_STATUS_NOT_FOUND: + status_text = "FILE NOT FOUND"; + break; + + case DISK_STATUS_EXISTS: + status_text = "FILE EXISTS"; + break; + + case DISK_STATUS_INIT: + status_text = "KUNG FU FLASH V" VERSION; + break; + + case DISK_STATUS_UNSUPPORTED: + return 0; + + default: + status_text = " OK"; + break; + } + + sprint((char *)channel->buf, "%2u,%s,%2u,%2u", + status, status_text, track, sector); + + u32 len = strlen((char *)channel->buf); + return len; +} + +static bool disk_handle_command(DISK_CHANNEL *channel, char *filename) { - const char *status; - if (filename[0] == 'M') // Memory command + u8 status = DISK_STATUS_OK; + u8 track = 0, sector = 0; + + if (!filename[0]) // No command + { + return true; + } + else if (filename[0] == 'M') // Memory command + { + status = DISK_STATUS_UNSUPPORTED; + } + else if (filename[0] == 'S') // Scratch command { - status = ""; // Not supported + // Scan for drive number and start of filename + while (*filename) + { + if (filename[1] == ':') + { + // Check drive number + if (filename[0] >= '1' && filename[0] <= '9') + { + return false; + } + + filename += 2; + break; + } + + filename++; + } + + c64_send_command(CMD_WAIT_SYNC); + c64_interface(false); + disk_receive_data(channel->buf); // Save temp data + + disk_rewind_dir(channel); + + D64_DIR_ENTRY *entry; + while ((entry = disk_read_dir(channel))) + { + if (disk_filename_match(entry, filename) && + disk_delete_file(channel, entry)) + { + disk_rewind_dir(channel); + track++; + } + } + + disk_send_data(channel->buf); // Send temp data back + c64_interface_sync(); + + status = DISK_STATUS_SCRATCHED; } else if (filename[0] == 'C' && filename[1] == 'D') // Directory command { @@ -697,22 +827,22 @@ static bool disk_read_status(DISK_CHANNEL *channel, char *filename) { status = DISK_STATUS_INIT; } + else if (disk_last_error) + { + status = disk_last_error; + } else { - // TODO: Return last error status = DISK_STATUS_OK; } - u8 len; - for (len=0; *status; len++) - { - channel->buf[len] = *status++; - } - channel->buf[len++] = '\r'; + disk_last_error = DISK_STATUS_OK; - channel->buf_len = len; + u32 len = disk_write_status(channel, status, track, sector); + channel->buf[len++] = (u8)'\r'; + channel->buf_len = (u8)len; channel->buf_ptr = 0; - channel->buf_mode = DISK_BUF_USE; + channel->buf2_ptr = 0; return true; } @@ -725,6 +855,7 @@ static u8 disk_handle_open_dir(DISK_CHANNEL *channel) disk_read_data(channel, NULL, 0); if (!disk_bytes_left(channel)) { + disk_last_error = DISK_STATUS_OK; return CMD_END_OF_FILE; } } @@ -742,27 +873,52 @@ static u8 disk_handle_open_dir(DISK_CHANNEL *channel) channel->buf_mode = DISK_BUF_DIR; } + disk_last_error = DISK_STATUS_OK; return CMD_NONE; } -static u8 disk_handle_open_prg(DISK_CHANNEL *channel, char *filename) +static u8 disk_handle_open_write(DISK_CHANNEL *channel, PARSED_FILENAME *parsed) { - PARSED_FILENAME parsed; - disk_parse_filename(filename, &parsed); - - if (parsed.drive || parsed.type == D64_FILE_REL) + D64_DIR_ENTRY *existing = disk_find_file(channel, parsed->name, 0); + if ((existing && (!parsed->overwrite || + !d64_is_file_type(existing, parsed->type))) || + (!existing && parsed->wildcard)) { - return CMD_NO_DRIVE; // Try serial device (if any) + disk_last_error = DISK_STATUS_EXISTS; + return CMD_NONE; } - if (parsed.mode == 'W') + disk_last_error = DISK_STATUS_OK; + c64_send_command(CMD_WAIT_SYNC); + c64_interface(false); + disk_receive_data(channel->buf); // Save temp data + + u8 cmd; + if (disk_create_file(channel, parsed->name, parsed->type, existing)) { - return CMD_DISK_ERROR; // Not supported yet + channel->buf_len = 0; + channel->buf_ptr = 0; + channel->buf_mode = DISK_BUF_SAVE; + cmd = CMD_NONE; + } + else + { + cmd = CMD_DISK_ERROR; } - if (!disk_open_read(channel, parsed.name, parsed.type)) + disk_send_data(channel->buf); // Send temp data back + c64_interface_sync(); + + return cmd; +} + +static u8 disk_handle_open_read(DISK_CHANNEL *channel, PARSED_FILENAME *parsed) +{ + if (!disk_open_read(channel, parsed->name, parsed->type)) { - return CMD_NOT_FOUND; + channel->buf_len = 0; + channel->buf_mode = DISK_BUF_USE; + return CMD_NONE; } disk_read_data(channel, NULL, 0); @@ -774,13 +930,36 @@ static u8 disk_handle_open_prg(DISK_CHANNEL *channel, char *filename) return CMD_NONE; } +static u8 disk_handle_open_prg(DISK_CHANNEL *channel, char *filename) +{ + PARSED_FILENAME parsed; + disk_parse_filename(filename, &parsed); + + if (parsed.drive) + { + return CMD_NO_DRIVE; // Try serial device (if any) + } + + if (parsed.type == D64_FILE_REL) + { + return CMD_DISK_ERROR; // Not supported yet + } + + if (parsed.mode == 'W') + { + return disk_handle_open_write(channel, &parsed); + } + + return disk_handle_open_read(channel, &parsed); +} + static u8 disk_handle_open(DISK_CHANNEL *channel) { char *filename = channel->filename; if (channel->number == 15) // Command channel { - if (!disk_read_status(channel, filename)) + if (!disk_handle_command(channel, filename)) { return CMD_NO_DRIVE; // Try serial device (if any) } @@ -810,23 +989,29 @@ static u8 disk_handle_close(DISK_CHANNEL *channel, DISK_CHANNEL *channels) return CMD_NONE; } -static u8 disk_handle_get_byte(DISK_CHANNEL *channel) +static u8 disk_handle_send_byte(DISK_CHANNEL *channel) { + if (!channel) + { + return CMD_DISK_ERROR; + } + + if (channel->number == 15 && !disk_bytes_left(channel)) + { + disk_handle_command(channel, "-"); // Write disk status to buffer + } + u8 data; - if (!channel || !disk_read_data(channel, &data, 1)) + if (!disk_read_data(channel, &data, 1)) { return CMD_DISK_ERROR; } - *KFF_BUF = data; + *KFF_BUF = data; // Send the byte if (!disk_bytes_left(channel)) { - if (channel->number == 15) - { - disk_read_status(channel, NULL); - } - else if (channel->buf_mode == DISK_BUF_DIR) + if (channel->buf_mode == DISK_BUF_DIR) { u8 *ptr = channel->buf; if (!disk_put_dir_entry(channel, &ptr)) @@ -846,6 +1031,64 @@ static u8 disk_handle_get_byte(DISK_CHANNEL *channel) return CMD_NONE; } +static u8 disk_handle_unlisten(DISK_CHANNEL *channel) +{ + if (!channel || channel->number != 15 || !channel->buf2_ptr) + { + return CMD_NONE; + } + + // Null terminate "filename" + if (channel->buf2[channel->buf2_ptr - 1] == (u8)'\r') + { + channel->buf2[--channel->buf2_ptr] = 0; + } + else + { + channel->buf2[channel->buf2_ptr] = 0; + } + + disk_handle_command(channel, channel->filename); + return CMD_NONE; +} + +static u8 disk_handle_receive_byte(DISK_CHANNEL *channel) +{ + if (!channel) + { + return CMD_DISK_ERROR; + } + + u8 data = c64_receive_byte(); + + if (channel->number == 15) + { + channel->buf2[channel->buf2_ptr++] = data; + return CMD_NONE; + } + + if (channel->buf_mode != DISK_BUF_SAVE) + { + return CMD_DISK_ERROR; + } + + channel->buf[channel->buf_ptr++] = data; + if (channel->buf_ptr) + { + return CMD_NONE; // Buffer not full + } + + c64_send_command(CMD_WAIT_SYNC); + c64_interface(false); + disk_receive_data(channel->buf2); // Save temp data + + disk_write_data(channel, channel->buf, sizeof(channel->buf)); + + disk_send_data(channel->buf2); // Send temp data back + c64_interface_sync(); + return CMD_NONE; +} + static DISK_CHANNEL * disk_receive_channel(DISK_CHANNEL *channels) { u8 channel = c64_receive_byte(); @@ -870,29 +1113,58 @@ static void disk_receive_filename(char *filename) *filename = 0; } -static void disk_receive_data(u8 *buf) +static u8 disk_send_command(u8 cmd, DISK_CHANNEL*channels) { - u8 size = c64_receive_byte(); - *buf++ = size; + c64_set_command(cmd); - while (size--) + u32 led_status = STATUS_LED_OFF; + for (u32 i=0; i<15; i++) { - *buf++ = c64_receive_byte(); + DISK_CHANNEL *channel = channels + i; + if (channel->buf_mode != DISK_BUF_USE || channel->buf_len) + { + // LED on if a file is open + led_status = STATUS_LED_ON; + break; + } } + timer_start_ms(175); + + u8 reply; + while (!c64_get_reply(cmd, &reply)) + { + if (timer_elapsed()) + { + if (disk_last_error > DISK_STATUS_SCRATCHED && + disk_last_error < DISK_STATUS_INIT) + { + // Blink if an error has occurred + led_status = led_status == STATUS_LED_OFF ? + STATUS_LED_ON : STATUS_LED_OFF; + } + + C64_CRT_CONTROL(led_status); + } + } + + return reply; } static void disk_loop(void) { D64_IMAGE *image = &d64_state.image; // Reuse memory from menu - DISK_CHANNEL *channel = NULL, *channels = (DISK_CHANNEL *)(crt_ram_buf + 0x100); + DISK_CHANNEL*channels = (DISK_CHANNEL *)(crt_ram_buf + 0x100); + DISK_CHANNEL *channel, *talk = NULL, *listen = NULL; memset(channels, 0, sizeof(DISK_CHANNEL) * 16); disk_init_all_channels(image, channels); + disk_last_error = DISK_STATUS_INIT; + // BASIC commands to run are placed in dat_buffer u8 cmd = CMD_MOUNT_DISK; while (true) { - u8 reply = c64_send_command(cmd); + u8 reply = disk_send_command(cmd, channels); cmd = CMD_NONE; switch (reply) @@ -912,7 +1184,6 @@ static void disk_loop(void) // Channel 1 will be used as save buffer - just as on 1541 channel = channels + 1; disk_receive_filename(channel->filename); - disk_receive_data(channel->buf); dbg("Got SAVE command for: %s", channel->filename); cmd = disk_handle_save(channel); break; @@ -932,15 +1203,28 @@ static void disk_loop(void) break; case REPLY_TALK: - channel = disk_receive_channel(channels); + talk = disk_receive_channel(channels); break; case REPLY_UNTALK: - channel = NULL; + talk = NULL; + break; + + case REPLY_SEND_BYTE: + cmd = disk_handle_send_byte(talk); + break; + + case REPLY_LISTEN: + listen = disk_receive_channel(channels); + break; + + case REPLY_UNLISTEN: + cmd = disk_handle_unlisten(listen); + listen = NULL; break; - case REPLY_GET_BYTE: - cmd = disk_handle_get_byte(channel); + case REPLY_RECEIVE_BYTE: + cmd = disk_handle_receive_byte(listen); break; default: diff --git a/firmware/disk_drive.h b/firmware/disk_drive.h index 3fb62e8..bdf0a35 100644 --- a/firmware/disk_drive.h +++ b/firmware/disk_drive.h @@ -18,12 +18,20 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#define DISK_STATUS_OK "00, OK,00,00" -#define DISK_STATUS_NOT_FOUND "62,FILE NOT FOUND,00,00" -#define DISK_STATUS_INIT "73,KUNG FU FLASH V" VERSION ",00,00" +static const u16 dir_start_addr = 0x0401; // Start of BASIC (for the PET) +static const u16 dir_link_addr = 0x0101; -const u16 dir_start_addr = 0x0401; // Start of BASIC (for the PET) -const u16 dir_link_addr = 0x0101; +static u8 disk_last_error; + +typedef enum +{ + DISK_STATUS_OK = 00, + DISK_STATUS_SCRATCHED = 01, + DISK_STATUS_NOT_FOUND = 62, + DISK_STATUS_EXISTS = 63, + DISK_STATUS_INIT = 73, + DISK_STATUS_UNSUPPORTED = 0xFF +} DISK_STATUS; typedef struct { @@ -38,8 +46,9 @@ typedef struct typedef enum { DISK_BUF_NONE = 0x00, - DISK_BUF_USE = 0x01, - DISK_BUF_DIR = 0x02 + DISK_BUF_USE, + DISK_BUF_DIR, + DISK_BUF_SAVE } DISK_BUF_MODE; typedef struct @@ -51,8 +60,13 @@ typedef struct u8 buf_ptr; u8 buf[256]; - char filename[256]; + union + { + char filename[256]; + u8 buf2[256]; + }; char *filename_dir; + u8 buf2_ptr; D64 d64; DIR dir; diff --git a/firmware/fs_drive.c b/firmware/fs_drive.c index 0116381..6e5d7dc 100644 --- a/firmware/fs_drive.c +++ b/firmware/fs_drive.c @@ -168,10 +168,16 @@ static bool fs_create_file(DISK_CHANNEL *channel, const char *filename, return file_open(&channel->file, filename, mode); } -static inline size_t fs_write_data(DISK_CHANNEL *channel, u8 *buf, - size_t buf_size) +static size_t fs_write_data(DISK_CHANNEL *channel, u8 *buf, + size_t buf_size) { - return file_write(&channel->file, buf, buf_size); + size_t result = file_write(&channel->file, buf, buf_size); + if (!file_sync(&channel->file)) + { + result = 0; + } + + return result; } static inline bool fs_write_finalize(DISK_CHANNEL *channel) @@ -179,6 +185,12 @@ static inline bool fs_write_finalize(DISK_CHANNEL *channel) return file_close(&channel->file); } +static bool fs_delete_file(DISK_CHANNEL *channel, D64_DIR_ENTRY *entry) +{ + char *filename = fs_get_filename(entry); + return file_delete(filename); +} + static bool fs_dir_up(void) { if (dat_file.disk.mode) diff --git a/firmware/loader.c b/firmware/loader.c index 5524717..ace2158 100644 --- a/firmware/loader.c +++ b/firmware/loader.c @@ -547,14 +547,21 @@ static inline u8 device_number_d64(void) static char * basic_get_filename(FILINFO *file_info) { char *filename = file_info->fname; + bool comma_found = false; size_t len; for (len=0; len<=16 && filename[len]; len++) { - filename[len] = ff_wtoupper(filename[len]); + char c = ff_wtoupper(filename[len]); + filename[len] = c; + + if (c == ',') + { + comma_found = true; + } } - if (len > 16 && file_info->altname[0]) + if ((len > 16 || comma_found) && file_info->altname[0]) { filename = file_info->altname; } diff --git a/launcher/disk.s b/launcher/disk.s index 5e10379..9ebe480 100644 --- a/launcher/disk.s +++ b/launcher/disk.s @@ -30,7 +30,6 @@ ; 6502 Instructions BIT_INSTRUCTION = $2c -LDA_INSTRUCTION = $a9 ; BASIC functions MAIN = $a483 ; Normal BASIC warm start @@ -49,6 +48,7 @@ ICHKIN = $031e ICKOUT = $0320 ICLRCH = $0322 IBASIN = $0324 +IBSOUT = $0326 IGETIN = $032a ICLALL = $032c ILOAD = $0330 @@ -114,6 +114,7 @@ CMD_NO_DRIVE = $10 CMD_DISK_ERROR = $11 CMD_NOT_FOUND = $12 CMD_END_OF_FILE = $13 +CMD_WAIT_SYNC = $50 CMD_SYNC = $55 REPLY_OK = $80 @@ -123,7 +124,10 @@ REPLY_OPEN = $92 REPLY_CLOSE = $93 REPLY_TALK = $94 REPLY_UNTALK = $95 -REPLY_GET_BYTE = $96 +REPLY_SEND_BYTE = $96 +REPLY_LISTEN = $97 +REPLY_UNLISTEN = $98 +REPLY_RECEIVE_BYTE = $99 ; ============================================================================= VECTOR_PAGE = IOPEN & $ff00 @@ -133,14 +137,14 @@ NEW_VECTOR_PAGE = >open_trampoline vectors: .lobytes IOPEN, ICLOSE, ICHKIN .lobytes ICKOUT, ICLRCH, IBASIN - .lobytes IGETIN, ILOAD, ISAVE - .lobytes ICLALL + .lobytes IBSOUT, IGETIN, ILOAD + .lobytes ISAVE, ICLALL old_vectors: .lobytes old_open_vector, old_close_vector, old_chkin_vector .lobytes old_ckout_vector, old_clrch_vector, old_basin_vector - .lobytes old_getin_vector, old_load_vector, old_save_vector - ; No need to store ICLALL + .lobytes old_bsout_vector, old_getin_vector, old_load_vector + .lobytes old_save_vector ; No need to store ICLALL old_vectors_end: OLD_VECTORS_SIZE = old_vectors_end - old_vectors @@ -148,8 +152,8 @@ OLD_VECTORS_SIZE = old_vectors_end - old_vectors new_vectors: .lobytes open_trampoline, close_trampoline, chkin_trampoline .lobytes ckout_trampoline, clrch_trampoline, basin_trampoline - .lobytes getin_trampoline, load_trampoline, save_trampoline - .lobytes clall_trampoline + .lobytes bsout_trampoline, getin_trampoline, load_trampoline + .lobytes save_trampoline, clall_trampoline new_vectors_end: NEW_VECTORS_SIZE = new_vectors_end - new_vectors @@ -242,7 +246,6 @@ open_trampoline: lda SA ; Send secondary address (channel) sta KFF_DATA - ldx FNLEN jsr store_filename_enable jmp kff_open @@ -298,21 +301,14 @@ normal_clrch: ; ----------------------------------------------------------------------------- basin_trampoline: lda DFLTN - bne basin_device ; Not keyboard - -basin_check_input: - lda CRSW ; Replaced with "lda #" when done - bne normal_basin ; Still reading characters - - jsr enable_kff_rom - jmp kff_basin_command ; Print start commands - -basin_device: cmp kff_device_number - bne normal_basin ; Not KFF device + normal_basin_offset = * + 1 + bne do_kff_basin ; will be replaced with normal_basin + do_kff_basin: jsr enable_kff_rom - jmp kff_basin + kff_basin_addr = * + 1 + jmp kff_basin_command ; will be replaced with kff_basin normal_basin_disable: jsr disable_kff_rom @@ -320,6 +316,21 @@ normal_basin: old_basin_vector = * + 1 jmp $ffff +; ----------------------------------------------------------------------------- +bsout_trampoline: + pha + lda DFLTO + cmp kff_device_number + bne normal_bsout ; Not KFF device + + jsr enable_kff_rom + jmp kff_bsout + +normal_bsout: + pla + old_bsout_vector = * + 1 + jmp $ffff + ; ----------------------------------------------------------------------------- getin_trampoline: lda DFLTN @@ -342,9 +353,6 @@ load_trampoline: cmp kff_device_number bne normal_load - ldx FNLEN - beq normal_load ; No filename, load will report error - jsr store_filename_enable jmp kff_load @@ -361,9 +369,6 @@ save_trampoline: cmp kff_device_number bne normal_save - ldx FNLEN - beq normal_save ; No filename, save will report error - jsr store_filename_enable jmp kff_save @@ -375,6 +380,7 @@ normal_save: ; ----------------------------------------------------------------------------- store_filename_enable: + ldx FNLEN stx KFF_DATA ; Send filename Length beq enable_kff_rom @@ -421,7 +427,7 @@ kernal_call: ; ----------------------------------------------------------------------------- .if * > KFF_RAM + KFF_RAM_SIZE - .error "Not enough space for disk_api" + .error "Not enough space for disk_api" .endif .reloc disk_api_end: @@ -473,9 +479,12 @@ kff_open: lda #REPLY_OPEN ; Send reply jsr kff_send_reply beq @open_ok + cmp #CMD_WAIT_SYNC + beq @wait_for_sync cmp #CMD_END_OF_FILE beq @end_of_file +@file_not_found: lda #ERROR_FILE_NOT_FOUND ; File not found error sec bcs @open_done @@ -488,6 +497,11 @@ kff_open: clc @open_done: jmp disable_kff_rom + +@wait_for_sync: + jsr kff_save_ram_wait_sync + beq @open_ok + jmp @file_not_found .endproc ; ============================================================================= @@ -512,8 +526,14 @@ kff_close: lda SA ; Send secondary address (channel) sta KFF_DATA + lda #REPLY_CLOSE ; Send reply jsr kff_send_reply + beq @normal_close + +@wait_for_sync: + jsr kff_save_ram_wait_sync + bne @wait_for_sync @normal_close: lda tmp1 @@ -527,23 +547,21 @@ kff_chkin: txa ; Find logical file (in X) ldx LDTND : dex - bmi normal_chkin ; Not found + bmi @normal_chkin ; Not found cmp LAT,x bne :- sta LA ; Store logical file lda FAT,x ; Lookup device cmp kff_device_number - bne normal_chkin ; Not KFF device + bne @normal_chkin ; Not KFF device -send_talk_cmd: + sta DFLTN ; Set input device sta FA ; Store device - sta DFLTN lda SAT,x ; Lookup secondary address sta SA + sta KFF_DATA ; Send secondary address (channel) - lda SA ; Send secondary address (channel) - sta KFF_DATA lda #REPLY_TALK ; Send reply jsr kff_send_reply @@ -552,14 +570,11 @@ send_talk_cmd: clc ; OK jmp disable_kff_rom -normal_chkin: +@normal_chkin: ldx tmp1 jmp normal_chkin_disable .endproc -; "Export" function -send_talk_cmd = kff_chkin::send_talk_cmd - ; ============================================================================= .proc kff_ckout kff_ckout: @@ -575,7 +590,20 @@ kff_ckout: lda FAT,x ; Lookup device cmp kff_device_number bne @normal_ckout ; Not KFF device - jmp send_talk_cmd + + sta DFLTO ; Set output device + sta FA ; Store device + lda SAT,x ; Lookup secondary address + sta SA + sta KFF_DATA ; Send secondary address (channel) + + lda #REPLY_LISTEN ; Send reply + jsr kff_send_reply + + lda #$00 ; Clear device status + sta STATUS + clc ; OK + jmp disable_kff_rom @normal_ckout: ldx tmp1 @@ -586,12 +614,22 @@ kff_ckout: .proc kff_clrch kff_clrch: lda #$00 ; Keyboard channel - ldx kff_device_number cpx DFLTO ; Check output channel bne @check_input ; Not KFF device sta DFLTO ; Don't send commands to the serial bus + lda #REPLY_UNLISTEN ; Send reply + jsr kff_send_reply + cmp #CMD_WAIT_SYNC + bne @check_input_a + + sty tmp1 + jsr kff_save_ram_wait_sync + ldy tmp1 + +@check_input_a: + lda #$00 ; Keyboard channel @check_input: cpx DFLTN ; Check input channel bne @normal_clrch ; Not KFF device @@ -607,6 +645,12 @@ kff_clrch: ; ============================================================================= .proc kff_basin_command kff_basin_command: + lda DFLTN + bne @not_keyboard + + lda CRSW + bne @normal_basin_disable ; Still reading characters + lda KFF_DATA beq @normal_basin ; No start commands @@ -636,41 +680,80 @@ kff_basin_command: ldx tmp1 @normal_basin: - lda #LDA_INSTRUCTION - sta basin_check_input ; Do not call kff_basin_command again + lda #normal_basin - normal_basin_offset - 1 + sta normal_basin_offset ; Enable KFF device check + lda #kff_basin + sta kff_basin_addr + 1 + +@normal_basin_disable: jmp normal_basin_disable + +@not_keyboard: + cmp kff_device_number + bne @normal_basin_disable ; Not KFF device .endproc -; ============================================================================= +; ----------------------------------------------------------------------------- .proc kff_basin kff_basin: lda STATUS - beq @status_ok - lda #$0d - bne @read_done ; Status not OK - return CR + bne @status_not_ok -@status_ok: - lda #REPLY_GET_BYTE ; Send reply + lda #REPLY_SEND_BYTE ; Send reply jsr kff_send_reply + bne @check_read_error ; Check command - beq @read_ok ; Check command +@read_byte: + lda KFF_DATA ; Get data +@read_done: + clc + jmp disable_kff_rom + +@check_read_error: cmp #CMD_END_OF_FILE - beq @read_end + bne @read_error + + lda #STATUS_END_OF_FILE ; Set status to end of file + sta STATUS + bne @read_byte +@read_error: lda #STATUS_READ_ERROR ; Set device status to read error occurred sta STATUS lda #$00 beq @read_done -@read_end: - lda #STATUS_END_OF_FILE ; Set status to end of file +@status_not_ok: + lda #$0d + bne @read_done ; Status not OK - return CR +.endproc + +; ============================================================================= +.proc kff_bsout +kff_bsout: + pla + sta KFF_DATA ; Send data + + lda #REPLY_RECEIVE_BYTE ; Send reply + jsr kff_send_reply + beq @write_ok ; Check command + cmp #CMD_WAIT_SYNC + bne @write_error + + sty tmp1 + jsr kff_save_ram_wait_sync + ldy tmp1 + jmp @write_ok + +@write_error: + lda #STATUS_SAVE_FILE_EXISTS ; file already exists .byte BIT_INSTRUCTION ; Skip next 2 bytes -@read_ok: +@write_ok: lda #$00 ; Clear status sta STATUS - lda KFF_DATA ; Get data -@read_done: clc jmp disable_kff_rom .endproc @@ -687,6 +770,9 @@ kff_clall: ; ============================================================================= .proc kff_load kff_load: + lda FNLEN + beq @no_filename ; No filename, load will report error + lda VERCK bne @not_found ; TODO: Support verify operation @@ -709,6 +795,9 @@ kff_load: sec jmp disable_kff_rom +@no_filename: + sta KFF_WRITE_LPTR ; Clear KFF write buffer (filename) + sta KFF_WRITE_HPTR @no_drive: jmp normal_load_disable @@ -817,14 +906,15 @@ kff_load: ; ============================================================================= .proc kff_save kff_save: - ldy #KFF_SAVE_RAM_SIZE ; Send KFF_SAVE_RAM for later retrieval - sty KFF_DATA - dey -: lda KFF_SAVE_RAM, y - sta KFF_DATA - dey - bpl :- + lda FNLEN + bne @send_reply ; Has filename + sta KFF_WRITE_LPTR ; Clear KFF write buffer (filename) + sta KFF_WRITE_HPTR + jmp normal_save_disable ; No filename, save will report error + +@send_reply: + jsr kff_save_ram ; Send KFF_SAVE_RAM for later retrieval lda #REPLY_SAVE ; Send reply jsr kff_send_reply beq @save_ok @@ -929,24 +1019,7 @@ kff_save: adc #$00 sta SAH - ldy #KFF_SYNC_RAM_SIZE - 1 ; Copy sync prg to KFF_SAVE_RAM -: lda kff_sync_prg, y - sta KFF_SAVE_RAM, y - dey - bpl :- - - lda #REPLY_OK - jsr wait_for_sync - - ldy KFF_DATA ; Restore KFF_SAVE_RAM - dey -: lda KFF_DATA - sta KFF_SAVE_RAM, y - dey - bpl :- - - lda #REPLY_OK ; Get result - jsr kff_send_reply + jsr kff_wait_for_sync beq @save_no_error lda #STATUS_SAVE_DISK_FULL ; Set status @@ -971,6 +1044,50 @@ kff_send_reply: rts .endproc +; ============================================================================= +.proc kff_save_ram_wait_sync +kff_save_ram_wait_sync: + jsr kff_save_ram + jmp kff_wait_for_sync +.endproc + +; ============================================================================= +.proc kff_save_ram +kff_save_ram: + ldy #KFF_SAVE_RAM_SIZE ; Send KFF_SAVE_RAM + sty KFF_DATA + dey +: lda KFF_SAVE_RAM, y + sta KFF_DATA + dey + bpl :- + rts +.endproc + +; ============================================================================= +.proc kff_wait_for_sync +kff_wait_for_sync: + ldy #KFF_SYNC_RAM_SIZE - 1 ; Copy sync prg to KFF_SAVE_RAM +: lda kff_sync_prg, y + sta KFF_SAVE_RAM, y + dey + bpl :- + + lda #REPLY_OK + jsr wait_for_sync + clc + + ldy KFF_DATA ; Restore KFF_SAVE_RAM + dey +: lda KFF_DATA + sta KFF_SAVE_RAM, y + dey + bpl :- + + lda #REPLY_OK ; Get result + jmp kff_send_reply +.endproc + ; ============================================================================= kff_sync_prg: .org KFF_SAVE_RAM