static void dump_result(nameinfo_t *result) { printf("CMD=%s\n", command_to_name(result->cmd)); printf("DRIVE=%c\n", result->drive == NAMEINFO_UNUSED_DRIVE ? '-' : (result->drive == NAMEINFO_UNDEF_DRIVE ? '*' : (result->drive == NAMEINFO_LAST_DRIVE ? 'L' : result->drive + 0x30))); printf("DRIVENAME='%s'\n", result->drivename ? (char*) result->drivename : nullstring); printf("NAME='%s' (%d)\n", result->name ? (char*)result->name : nullstring, result->namelen); printf("ACCESS=%c\n", result->access ? result->access : '-'); printf("TYPE=%c", result->type ? result->type : '-'); debug_putcrlf(); printf("DRIVE2=%c\n", result->file[0].drive == NAMEINFO_UNUSED_DRIVE ? '-' : (result->file[0].drive == NAMEINFO_UNDEF_DRIVE ? '*' : (result->file[0].drive == NAMEINFO_LAST_DRIVE ? 'L' : result->file[0].drive + 0x30))); printf("DRIVENAME2='%s'\n", result->file[0].drivename ? (char*) result->file[0].drivename : nullstring); printf("NAME2='%s' (%d)\n", result->file[0].name ? (char*)result->file[0].name : nullstring, result->file[0].namelen); printf("RECLEN=%d\n", result->recordlen); debug_flush(); }
// opens the file, registers an error code in command->error if necessary // If the open was successful, setup a channel for the given channel number // (note: explicitely not secondary device number, as IEEE and IEC in parallel // use overlapping numbers, so they may be shifted or similar to avoid clashes) // // The command buffer is used as transmit buffer, so it must not be overwritten // until the open has been sent. // // note that if it returns a value <0 on error, it has to have the error message // set appropriately. // int8_t file_open(uint8_t channel_no, bus_t *bus, errormsg_t *errormsg, void (*callback)(int8_t errnum, uint8_t *rxdata), uint8_t openflag) { assert_not_null(bus, "file_open: bus is null"); cmd_t *command = &(bus->command); rtconfig_t *rtconf = &(bus->rtconf); #ifdef DEBUG_FILE debug_printf("OPEN FILE: FOR CHAN: %d WITH NAME: '%s', OPENFLAG=%d\n", channel_no, (char*)&(command->command_buffer), openflag); #endif // note: in a preemtive env, the following would have to be protected // to be atomic as we modify static variables parse_filename(command, &nameinfo, (openflag & OPENFLAG_LOAD) ? PARSEHINT_LOAD : 0); #ifdef DEBUG_FILE debug_printf(" PARSE -> ACCESS=%c, TYPE=%c\n", nameinfo.access, nameinfo.type); #endif // drive handling needed for error message drive if (nameinfo.drive == NAMEINFO_LAST_DRIVE) { nameinfo.drive = rtconf->last_used_drive; } else if (nameinfo.drive == NAMEINFO_UNUSED_DRIVE) { // TODO: match CBM behavior nameinfo.drive = rtconf->last_used_drive; } int8_t errdrive = rtconf->errmsg_with_drive ? nameinfo.drive : -1; // post-parse if (nameinfo.cmd != CMD_NONE && nameinfo.cmd != CMD_DIR && nameinfo.cmd != CMD_OVERWRITE) { // command name during open // this is in fact ignored by CBM DOS as checked with VICE's true drive emulation debug_printf("NO CORRECT CMD: %s\n", command_to_name(nameinfo.cmd)); nameinfo.cmd = 0; } if (nameinfo.type != 0 && nameinfo.type != 'S' && nameinfo.type != 'P' && nameinfo.type != 'U' && nameinfo.type != 'L') { // not set, or set as not sequential and not program debug_puts("UNKOWN FILE TYPE: "); debug_putc(nameinfo.type); debug_putcrlf(); set_error_tsd(errormsg, CBM_ERROR_FILE_TYPE_MISMATCH, 0, 0, errdrive); return -1; } if (nameinfo.access != 0 && nameinfo.access != 'W' && nameinfo.access != 'R' && nameinfo.access != 'A' && nameinfo.access != 'X') { debug_puts("UNKNOWN FILE ACCESS TYPE "); debug_putc(nameinfo.access); debug_putcrlf(); // not set, or set as not read, write, or append, or r/w ('X') set_error_tsd(errormsg, CBM_ERROR_SYNTAX_UNKNOWN, 0, 0, errdrive); return -1; } if (nameinfo.cmd == CMD_DIR && (nameinfo.access != 0 && nameinfo.access != 'R')) { // trying to write to a directory debug_puts("WRITE TO DIRECTORY!"); debug_putcrlf(); set_error_tsd(errormsg, CBM_ERROR_FILE_EXISTS, 0, 0, errdrive); return -1; } uint8_t type = FS_OPEN_RD; // file access default if (nameinfo.access == 0) { if (openflag == OPENFLAG_LOAD) { nameinfo.access = 'R'; } else if (openflag == OPENFLAG_SAVE) { nameinfo.access = 'W'; } } // file type defaults if (nameinfo.type == 0) { // do we create a file (access=='W')? if (nameinfo.access == 'W') { // write (like save) if (openflag == OPENFLAG_SAVE) { nameinfo.type = 'P'; } else { nameinfo.type = 'S'; } } if (nameinfo.access == 'R') { if (openflag == OPENFLAG_LOAD) { // on load, 'P' is the default nameinfo.type = 'P'; } } } if (nameinfo.access == 'X') { // trying to open up a R/W channel debug_puts("OPENING UP A R/W CHANNEL!"); debug_putcrlf(); type = FS_OPEN_RW; } if (nameinfo.name[0] == '#') { // trying to open up a direct channel // Note: needs to be supported for D64 support with U1/U2/... // Note: '#' is still blocking on read! debug_puts("OPENING UP A DIRECT CHANNEL!"); debug_putcrlf(); type = FS_OPEN_DIRECT; } // if ",W" or secondary address is one, i.e. save if (nameinfo.access == 'W') { type = FS_OPEN_WR; } if (nameinfo.access == 'A') { type = FS_OPEN_AP; } if (nameinfo.cmd == CMD_DIR) { if (openflag & OPENFLAG_LOAD) { type = FS_OPEN_DR; } else { type = FS_OPEN_RD; } } else if (nameinfo.cmd == CMD_OVERWRITE) { type = FS_OPEN_OW; } #ifdef DEBUG_FILE debug_printf("NAME='%s' (%d)\n", nameinfo.name, nameinfo.namelen); debug_printf("ACCESS=%c\n", nameinfo.access); debug_printf("CMD=%d\n", nameinfo.cmd); debug_flush(); #endif return file_submit_call(channel_no, type, command->command_buffer, errormsg, rtconf, callback, 0); }
uint8_t file_submit_call(uint8_t channel_no, uint8_t type, uint8_t *cmd_buffer, errormsg_t *errormsg, rtconfig_t *rtconf, void (*callback)(int8_t errnum, uint8_t *rxdata), uint8_t iscmd) { assert_not_null(errormsg, "file_submit_call: errormsg is null"); assert_not_null(rtconf, "file_submit_call: rtconf is null"); // check for default drive (here is the place to set the last used one) if (nameinfo.drive == NAMEINFO_LAST_DRIVE) { nameinfo.drive = rtconf->last_used_drive; } else if (nameinfo.drive == NAMEINFO_UNUSED_DRIVE) { // TODO: match CBM behavior nameinfo.drive = rtconf->last_used_drive; } else if (nameinfo.drive < MAX_DRIVES) { // only save real drive numbers as last used default rtconf->last_used_drive = nameinfo.drive; } // if second name does not have a drive, use drive from first, // but only if it is defined if (nameinfo.file[0].drive == NAMEINFO_UNUSED_DRIVE && nameinfo.drive != NAMEINFO_UNDEF_DRIVE) { nameinfo.file[0].drive = nameinfo.drive; } // here is the place to plug in other file system providers, // like SD-Card, or even an outgoing IEC or IEEE, to convert between // the two bus systems. This is done depending on the drive number // and managed with the ASSIGN call. //provider_t *provider = &serial_provider; endpoint_t *endpoint = NULL; if (type == FS_OPEN_DIRECT) { debug_printf("Getting direct endpoint provider for channel %d\n", channel_no); endpoint = direct_provider(); } else { endpoint = provider_lookup(nameinfo.drive, (char*) nameinfo.drivename); } // convert from bus' PETSCII to provider // currently only up to the first zero byte is converted, options like file type // are still ASCII only // in the future the bus may have an own conversion option... cconv_converter(CHARSET_PETSCII, endpoint->provider->charset(endpoint->provdata)) ((char*)nameinfo.name, strlen((char*)nameinfo.name), (char*)nameinfo.name, strlen((char*)nameinfo.name)); for (uint8_t i=0 ; i < nameinfo.num_files ; ++i) { if (nameinfo.file[i].name != NULL) { cconv_converter(CHARSET_PETSCII, endpoint->provider->charset(endpoint->provdata)) ((char*)nameinfo.file[i].name, strlen((char*)nameinfo.file[i].name), (char*)nameinfo.file[i].name, strlen((char*)nameinfo.file[i].name)); } } if (type == FS_MOVE && nameinfo.file[0].drive != NAMEINFO_UNUSED_DRIVE // then use ep from first drive anyway && nameinfo.file[0].drive != nameinfo.drive) { // no need to check if the same // two-name command(s) with possibly different drive numbers endpoint_t *endpoint2 = provider_lookup(nameinfo.file[0].drive, (char*) nameinfo.file[0].name); if (endpoint2 != endpoint) { debug_printf("ILLEGAL DRIVE COMBINATION: %d vs. %d\n", nameinfo.drive+0x30, nameinfo.file[0].drive+0x30); set_error_tsd(errormsg, CBM_ERROR_DRIVE_NOT_READY, 0, 0, nameinfo.drive); return -1; } } // check the validity of the drive (note that in general provider_lookup // returns a default provider - serial-over-USB to the PC, which then // may do further checks if (endpoint == NULL) { debug_puts("ILLEGAL DRIVE: "); debug_putc(0x30+nameinfo.drive); debug_putcrlf(); set_error_tsd(errormsg, CBM_ERROR_DRIVE_NOT_READY, 0, 0, nameinfo.drive); return -1; } provider_t *provider = endpoint->provider; // find open slot //int8_t slot = -1; open_t *activeslot = NULL; for (uint8_t i = 0; i < MAX_ACTIVE_OPEN; i++) { if (active[i].channel_no < 0) { //slot = i; activeslot = (open_t*) &active[i]; break; } } //if (slot < 0) { if (activeslot == NULL) { debug_puts("NO OPEN SLOT FOR OPEN!"); debug_putcrlf(); set_error_tsd(errormsg, CBM_ERROR_NO_CHANNEL, 0, 0, nameinfo.drive); return -1; } activeslot->endpoint = endpoint; uint8_t len = assemble_filename_packet(cmd_buffer, &nameinfo); #ifdef DEBUG_FILE debug_printf("LEN AFTER ASSEMBLE=%d\n", len); #endif packet_init(&activeslot->txbuf, len, cmd_buffer); // store pointer to runtime config in packet // used by providers running on the device activeslot->txbuf.rtc = rtconf; packet_set_filled(&activeslot->txbuf, channel_no, type, len); if (!iscmd) { // only for file opens // note: we need the provider for the dir converter, // so we can only do it in here. // open channel uint8_t writetype = WTYPE_READONLY; if (type == FS_OPEN_WR || type == FS_OPEN_AP || type == FS_OPEN_OW) { writetype = WTYPE_WRITEONLY; } else if (type == FS_OPEN_RW) { writetype = WTYPE_READWRITE; } if (nameinfo.options & NAMEOPT_NONBLOCKING) { writetype |= WTYPE_NONBLOCKING; } int8_t (*converter)(void *, packet_t*, uint8_t) = (type == FS_OPEN_DR) ? (provider->directory_converter) : NULL; // TODO: if provider->channel_* are not NULL, we should probably not allocate a channel // but that would break the FILE OPEN detection here. channel_t *channel = channel_find(channel_no); if (channel != NULL) { // clean up channel_close(channel_no); // Note: it seems possible to open the same channel multiple times // on a direct file if (type != FS_OPEN_DIRECT) { debug_puts("FILE OPEN ERROR"); debug_putcrlf(); set_error_tsd(errormsg, CBM_ERROR_NO_CHANNEL, 0, 0, nameinfo.drive); return -1; } } int8_t e = channel_open(channel_no, writetype, endpoint, converter, nameinfo.drive); if (e < 0) { debug_puts("E="); debug_puthex(e); debug_putcrlf(); set_error_tsd(errormsg, CBM_ERROR_NO_CHANNEL, 0, 0, nameinfo.drive); return -1; } } activeslot->callback = callback; // no more error here, just the submit. // so callers can be sure if this function returns <0, they do not need // to close the channel, as it has not been opened // If this function returns 0, a callback must be received and handled, // and the channel is already opened. activeslot->channel_no = channel_no; // prepare response buffer packet_init(&activeslot->rxbuf, OPEN_RX_DATA_LEN, activeslot->rxdata); provider->submit_call(endpoint->provdata, channel_no, &activeslot->txbuf, &activeslot->rxbuf, _file_open_callback); return 0; }