diff --git a/support/atari8bit/atari800.cpp b/support/atari8bit/atari800.cpp index 3b19901..0a0da9b 100644 --- a/support/atari8bit/atari800.cpp +++ b/support/atari8bit/atari800.cpp @@ -261,38 +261,6 @@ static uint16_t get_a800_reg2(uint8_t reg) return r; } -void atari8bit_dma_write(const uint8_t *buf, uint32_t addr, uint32_t len) -{ - user_io_set_index(99); - user_io_set_download(1, addr); - user_io_file_tx_data(buf, len); - user_io_set_download(0); -} - -static void atari800_dma_read(uint8_t *buf, uint32_t addr, uint32_t len) -{ - user_io_set_index(99); - user_io_set_upload(1, addr); - user_io_file_rx_data(buf, len); - user_io_set_upload(0); -} - -void atari8bit_dma_zero(uint32_t addr, uint32_t len) -{ - memset(a8bit_buffer, 0, BUFFER_SIZE); - uint32_t to_write = len > BUFFER_SIZE ? BUFFER_SIZE : len; - - user_io_set_index(99); - user_io_set_download(1, addr); - while(len) - { - user_io_file_tx_data(a8bit_buffer, to_write); - len -= to_write; - to_write = len > BUFFER_SIZE ? BUFFER_SIZE : len; - } - user_io_set_upload(0); -} - static void reboot(uint8_t cold, uint8_t pause) { int i; @@ -346,6 +314,100 @@ static void reboot(uint8_t cold, uint8_t pause) set_a8bit_reg(REG_PAUSE, pause); } +static void check_reset_pause() +{ + uint16_t atari_status1 = get_a8bit_reg(REG_ATARI_STATUS1); + + set_a8bit_reg(REG_PAUSE, atari_status1 & STATUS1_MASK_HALT); + + if (atari_status1 & STATUS1_MASK_SOFTBOOT) + { + reboot(0, 0); + } + else if (atari_status1 & STATUS1_MASK_COLDBOOT) + { + reboot(1, 0); + } +} + +static void atari800_tape_wait() +{ +#ifdef USE_SCHEDULER + int counter = 0; +#endif + while(!(get_a8bit_reg(REG_ATARI_STATUS2) & STATUS2_MASK_TAPE_EMPTY)) + { + check_reset_pause(); +#ifdef USE_SCHEDULER + counter++; + if(counter > 10) + { + counter = 0; + input_poll(0); + scheduler_yield(); + } +#endif + } +} + +static void atari800_tape_enqueue(uint8_t b, uint32_t cnt) +{ +#ifdef USE_SCHEDULER + int counter = 0; +#endif + while(get_a8bit_reg(REG_ATARI_STATUS2) & STATUS2_MASK_TAPE_FULL) + { + check_reset_pause(); +#ifdef USE_SCHEDULER + counter++; + if(counter > 10) + { + counter = 0; + input_poll(0); + scheduler_yield(); + } +#endif + } + EnableIO(); + spi8(A800_TAPE_ENQUEUE); + uint32_t data = b | (cnt << 1); + spi_w((uint16_t)data); + spi_w((uint16_t)(data >> 16)); + DisableIO(); +} + +void atari8bit_dma_write(const uint8_t *buf, uint32_t addr, uint32_t len) +{ + user_io_set_index(99); + user_io_set_download(1, addr); + user_io_file_tx_data(buf, len); + user_io_set_download(0); +} + +static void atari800_dma_read(uint8_t *buf, uint32_t addr, uint32_t len) +{ + user_io_set_index(99); + user_io_set_upload(1, addr); + user_io_file_rx_data(buf, len); + user_io_set_upload(0); +} + +void atari8bit_dma_zero(uint32_t addr, uint32_t len) +{ + memset(a8bit_buffer, 0, BUFFER_SIZE); + uint32_t to_write = len > BUFFER_SIZE ? BUFFER_SIZE : len; + + user_io_set_index(99); + user_io_set_download(1, addr); + while(len) + { + user_io_file_tx_data(a8bit_buffer, to_write); + len -= to_write; + to_write = len > BUFFER_SIZE ? BUFFER_SIZE : len; + } + user_io_set_upload(0); +} + static void uart_init(uint8_t divisor) { set_a8bit_reg(REG_SIO_SETDIV, (divisor << 1) + 1); @@ -363,7 +425,7 @@ static void uart_send(uint8_t data) #endif while(uart_full()) { - usleep(100); + check_reset_pause(); #ifdef USE_SCHEDULER counter++; if(counter > 10) @@ -389,7 +451,7 @@ static uint16_t uart_receive() #endif while(!uart_available()) { - usleep(100); + check_reset_pause(); counter++; #ifdef USE_SCHEDULER if(counter > 10) @@ -789,13 +851,14 @@ static uint64_t get_us(uint64_t offset) static uint8_t check_us(uint64_t time) { long int remaining = time - get_us(0); -#ifdef USE_SCHEDULER if(remaining >= (long int)10000) { + check_reset_pause(); +#ifdef USE_SCHEDULER input_poll(0); scheduler_yield(); - } #endif + } return remaining <= 0; } @@ -1850,9 +1913,300 @@ static void process_command() } } +#define CORE_HZ 28636364 +#define MAX_MS 60000 + +#define cas_header_FUJI 0x494A5546 +#define cas_header_baud 0x64756162 +#define cas_header_data 0x61746164 +#define cas_header_fsk 0x206B7366 +#define cas_header_pwms 0x736D7770 +#define cas_header_pwmc 0x636D7770 +#define cas_header_pwmd 0x646D7770 +#define cas_header_pwml 0x6C6D7770 + +typedef struct { + uint32_t signature; + uint16_t chunk_length; + union { + uint8_t aux_b[2]; + uint16_t aux_w; + } aux; +} __attribute__((packed)) cas_header_t; + +static fileTYPE cas_file = {}; +static cas_header_t cas_header; + +static uint32_t pwm_sample_duration; +static uint32_t cas_sample_duration; +static uint16_t silence_duration; + +static uint32_t cas_offset; +static uint8_t cas_block_turbo; +static uint8_t cas_block_turbo_prev; +static uint16_t cas_block_index; +static uint16_t cas_block_multiple; +static uint8_t cas_fsk_bit; +static uint8_t pwm_bit; +static uint8_t pwm_bit_order; +static int cas_block_pause; + +void atari800_check_osd_key(unsigned short key, int press) +{ + static uint8_t first = 1; + if(cas_file.opened()) + { + if(key == KEY_F11 && press) + { + if(first) + { + first = 0; + ProgressMessage(0, 0, 0, 0); + } + ProgressMessage("CAS Tape", cas_file.name, cas_offset, cas_file.size); + } + } +} + +static uint32_t cas_read_forward(uint32_t offset) { + + uint32_t bytes_read; + + if(!FileSeek(&cas_file, offset, SEEK_SET)) return 0; + + while(1) + { + bytes_read = FileReadAdv(&cas_file, (uint8_t *)&cas_header, sizeof(cas_header_t)); + if(bytes_read != sizeof(cas_header_t)) return 0; + offset += bytes_read; + cas_block_index = 0; + + switch(cas_header.signature) { + + case cas_header_FUJI: + offset += cas_header.chunk_length; + if(!FileSeek(&cas_file, offset, SEEK_SET)) return 0; + break; + + case cas_header_baud: + if(cas_header.chunk_length) return 0; + cas_sample_duration = (CORE_HZ + cas_header.aux.aux_w / 2) / cas_header.aux.aux_w; + break; + + case cas_header_data: + cas_block_turbo_prev = cas_block_turbo; + cas_block_turbo = 0; + silence_duration = cas_header.aux.aux_w; + cas_block_multiple = 1; + goto cas_read_forward_exit; + + case cas_header_fsk: + cas_block_turbo_prev = cas_block_turbo; + cas_block_turbo = 0; + silence_duration = cas_header.aux.aux_w; + cas_block_multiple = 2; + cas_fsk_bit = 0; + goto cas_read_forward_exit; + + case cas_header_pwms: + if(cas_header.chunk_length != 2) + { + offset = 0; + goto cas_read_forward_exit; + } + pwm_bit_order = (cas_header.aux.aux_b[0] >> 2) & 0x1; + cas_header.aux.aux_b[0] &= 0x3; + + if(cas_header.aux.aux_b[0] == 0b01) pwm_bit = 0; + else if(cas_header.aux.aux_b[0] == 0b10) pwm_bit = 1; + else + { + offset = 0; + goto cas_read_forward_exit; + } + pwm_sample_duration = 0; + bytes_read = FileReadAdv(&cas_file, (uint8_t *)&pwm_sample_duration, sizeof(uint16_t)); + if(bytes_read != sizeof(uint16_t)) + { + offset = 0; + goto cas_read_forward_exit; + } + offset += bytes_read; + pwm_sample_duration = (CORE_HZ + pwm_sample_duration / 2) / pwm_sample_duration; + break; + + case cas_header_pwmc: + cas_block_turbo_prev = cas_block_turbo; + cas_block_turbo = 1; + silence_duration = cas_header.aux.aux_w; + cas_block_multiple = 3; + goto cas_read_forward_exit; + + case cas_header_pwmd: + cas_block_turbo_prev = cas_block_turbo; + cas_block_turbo = 1; + cas_block_multiple = 1; + goto cas_read_forward_exit; + + case cas_header_pwml: + cas_block_turbo_prev = cas_block_turbo; + cas_block_turbo = 1; + silence_duration = cas_header.aux.aux_w; + cas_block_multiple = 2; + cas_fsk_bit = pwm_bit; + goto cas_read_forward_exit; + + default: + break; + } + } + +cas_read_forward_exit: + if(cas_block_turbo ^ cas_block_turbo_prev) atari800_tape_enqueue(1, cas_block_turbo ? 0x00000001 : 0x00000000); + return offset; +} + +static void handle_cas() +{ + + if(!(get_a8bit_reg(REG_ATARI_STATUS2) & STATUS2_MASK_TAPE_ACT)) return; + + int to_read = (cas_block_turbo ? 128 : 256) * cas_block_multiple; + if(to_read >= cas_header.chunk_length - cas_block_index) to_read = cas_header.chunk_length - cas_block_index; + + if(!FileSeek(&cas_file, cas_offset, SEEK_SET) || FileReadAdv(&cas_file, a8bit_buffer, to_read) != to_read) + { + FileClose(&cas_file); + return; + } + cas_offset += to_read; + + uint8_t silence_bit = (cas_block_turbo ? pwm_bit : 1); + + while(silence_duration > 0) { + uint16_t silence_block_len = silence_duration; + if(silence_block_len >= MAX_MS) silence_block_len = MAX_MS; + atari800_tape_enqueue(silence_bit, ((CORE_HZ + 500) / 1000) * silence_block_len); + silence_duration -= silence_block_len; + } + + cas_block_index += to_read; + + int bs, be, bd; + uint8_t b; + uint16_t ld; + + for(int i=0; i < to_read; i += cas_block_multiple) { + switch(cas_header.signature) { + + case cas_header_data: + atari800_tape_enqueue(0, cas_sample_duration); + b = a8bit_buffer[i]; + for(int j = 0; j != 8; j++) + { + atari800_tape_enqueue(b & 0x1, cas_sample_duration); + b >>= 1; + } + atari800_tape_enqueue(1, cas_sample_duration); + break; + + case cas_header_fsk: + case cas_header_pwml: + ld = a8bit_buffer[i] | (a8bit_buffer[i+1] << 8); + if(ld) atari800_tape_enqueue(cas_fsk_bit, (cas_block_turbo ? pwm_sample_duration : ((CORE_HZ + 5000)/10000)) * ld); + cas_fsk_bit ^= 1; + break; + + case cas_header_pwmc: + ld = a8bit_buffer[i+1] | (a8bit_buffer[i+2] << 8); + for(int j=0; j < ld; j++) + { + atari800_tape_enqueue(pwm_bit, a8bit_buffer[i] * pwm_sample_duration / 2); + atari800_tape_enqueue(pwm_bit ^ 1, a8bit_buffer[i] * pwm_sample_duration / 2); + } + break; + + case cas_header_pwmd: + b = a8bit_buffer[i]; + if (pwm_bit_order) + { + bs = 7; be = -1; bd = -1; + } + else + { + bs = 0; be = 8; bd = 1; + } + for(int j = bs; j != be; j += bd) + { + uint8_t d = cas_header.aux.aux_b[(b >> j) & 0x1]; + atari800_tape_enqueue(pwm_bit, d * pwm_sample_duration / 2); + atari800_tape_enqueue(pwm_bit ^ 1, d * pwm_sample_duration / 2); + } + default: + break; + } + } + if(cas_offset == cas_file.size) + { + atari800_tape_enqueue(cas_block_turbo ? pwm_bit : 1, 16); + FileClose(&cas_file); + return; + } + else if(cas_block_index == cas_header.chunk_length && cas_offset < cas_file.size) + { + cas_offset = cas_read_forward(cas_offset); + if(!cas_offset) + { + FileClose(&cas_file); + return; + } + else if(cas_header.signature == cas_header_pwmc || cas_header.signature == cas_header_data || silence_duration || cas_block_turbo ^ cas_block_turbo_prev) + { + atari800_tape_enqueue((cas_block_turbo_prev != 0xFF ? cas_block_turbo_prev : cas_block_turbo) ? pwm_bit : 1, 16); + atari800_tape_wait(); + wait_us(cas_block_pause); + } + } +} + void atari800_set_image(int ext_index, int file_index, const char *name) { - if(file_index == 5) // XEX file + if(file_index == 7 || file_index == 8) // CAS file (8 with auto boot) + { + cas_offset = 0; + cas_block_turbo = 0xFF; + cas_block_turbo_prev = 0xFF; + set_a8bit_reg(TAPE_RESET, 1); + set_a8bit_reg(TAPE_RESET, 0); + cas_block_pause = (get_a8bit_reg(REG_ATARI_STATUS2) & STATUS2_MASK_TAPE_SLOW) ? 400000 : 10000; + if(name[0] && FileOpen(&cas_file, name)) + { + cas_sample_duration = (CORE_HZ + 300) / 600; + if(FileReadAdv(&cas_file, (uint8_t *)&cas_header, sizeof(cas_header_t)) == sizeof(cas_header_t) && cas_header.signature == cas_header_FUJI) + { + cas_offset = cas_read_forward(cas_header.chunk_length + sizeof(cas_header_t)); + } + } + + if(!cas_offset) + { + FileClose(&cas_file); + } + else if(file_index == 8) + { + set_a8bit_reg(REG_PAUSE, 1); + set_a8bit_reg(REG_CART1_SELECT, 0); + set_a8bit_reg(REG_CART2_SELECT, 0); + reboot(1, 0); + set_a8bit_reg(REG_OPTION_FORCE, 1); + set_a8bit_reg(REG_START_FORCE, 1); + set_a8bit_reg(REG_OPTION_FORCE, 0); + set_a8bit_reg(REG_START_FORCE, 0); + set_a8bit_reg(REG_SPACE_FORCE, 1); + set_a8bit_reg(REG_SPACE_FORCE, 0); + } + } + else if(file_index == 5) // XEX file { if(name[0] && FileOpen(&xex_file, name)) { @@ -1904,6 +2258,9 @@ void atari800_set_image(int ext_index, int file_index, const char *name) set_a8bit_reg(REG_PAUSE, 1); set_a8bit_reg(REG_CART1_SELECT, 0); set_a8bit_reg(REG_CART2_SELECT, 0); + FileClose(&cas_file); + set_a8bit_reg(TAPE_RESET, 1); + set_a8bit_reg(TAPE_RESET, 0); } set_drive_status(0, name, ext_index); if(name[0]) @@ -2128,20 +2485,10 @@ xex_eof: void atari800_poll() { - uint16_t atari_status1 = get_a8bit_reg(REG_ATARI_STATUS1); - - set_a8bit_reg(REG_PAUSE, atari_status1 & STATUS1_MASK_HALT); - - if (atari_status1 & STATUS1_MASK_SOFTBOOT) - { - reboot(0, 0); - } - else if (atari_status1 & STATUS1_MASK_COLDBOOT) - { - reboot(1, 0); - } + check_reset_pause(); if(xex_file.opened()) handle_xex(); + if(cas_file.opened()) handle_cas(); handle_pbi(); process_command(); } @@ -2184,6 +2531,9 @@ void atari800_reset() FileClose(&drive_infos[i].file); } FileClose(&xex_file); + FileClose(&cas_file); + set_a8bit_reg(TAPE_RESET, 1); + set_a8bit_reg(TAPE_RESET, 0); speed_index = 0; uart_init(speeds[speed_index] + 6); reboot(1, 0); diff --git a/support/atari8bit/atari800.h b/support/atari8bit/atari800.h index d65c7a3..d25f0eb 100644 --- a/support/atari8bit/atari800.h +++ b/support/atari8bit/atari800.h @@ -11,5 +11,6 @@ int atari800_check_cartridge_file(const char* name, unsigned char index); void atari800_open_cartridge_file(const char* name, int match_index); void atari800_open_bios_file(const char* name, unsigned char index); void atari800_set_image(int ext_index, int file_index, const char *name); +void atari800_check_osd_key(unsigned short key, int press); #endif diff --git a/support/atari8bit/atari8bit_defs.h b/support/atari8bit/atari8bit_defs.h index 512919d..e06ea34 100644 --- a/support/atari8bit/atari8bit_defs.h +++ b/support/atari8bit/atari8bit_defs.h @@ -8,6 +8,10 @@ #define STATUS1_MASK_ATX1050 0x8000 #define STATUS2_MASK_SPLASH 0x0800 +#define STATUS2_MASK_TAPE_FULL 0x8000 +#define STATUS2_MASK_TAPE_EMPTY 0x4000 +#define STATUS2_MASK_TAPE_ACT 0x2000 +#define STATUS2_MASK_TAPE_SLOW 0x1000 #define BUFFER_SIZE 8192 @@ -23,6 +27,7 @@ #define A800_GET_REGISTER 0x08 #define A800_SET_REGISTER 0x09 +#define A800_TAPE_ENQUEUE 0x0A #define REG_CART1_SELECT 0x01 #define REG_CART2_SELECT 0x02 @@ -35,6 +40,9 @@ #define REG_XEX_LOADER 0x09 #define REG_SIO_TX 0x0A #define REG_SIO_SETDIV 0x0B +#define TAPE_RESET 0x0C +#define REG_START_FORCE 0x0D +#define REG_SPACE_FORCE 0x0E #define REG_ATARI_STATUS1 0x01 #define REG_ATARI_STATUS2 0x02 diff --git a/user_io.cpp b/user_io.cpp index 89f33e2..0c17d59 100644 --- a/user_io.cpp +++ b/user_io.cpp @@ -3806,6 +3806,11 @@ static void send_keycode(unsigned short key, int press) return; } + if (is_atari800()) + { + atari800_check_osd_key(key, press); + } + if (core_type == CORE_TYPE_8BIT) { uint32_t code = get_ps2_code(key);