diff --git a/src/SDL_win32_main.c b/src/SDL_win32_main.c new file mode 100644 index 0000000..1d858c6 --- /dev/null +++ b/src/SDL_win32_main.c @@ -0,0 +1,3 @@ +/* This file should be replaced with the one taken from SDL-1.2 source tree. */ +typedef int make_iso_compilers_happy; +#error Replace this file (SDL_win32_main.c) with the one from SDL-1.2 source tree. diff --git a/src/aclocal.m4 b/src/aclocal.m4 index f08963d..4ce9220 100644 --- a/src/aclocal.m4 +++ b/src/aclocal.m4 @@ -262,6 +262,7 @@ dnl Check for a working version of liba8cas that is of the right version. AC_LANG_SAVE AC_LANG_C + if test "x$cross_compiling" = "xno"; then AC_RUN_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ @@ -275,7 +276,10 @@ dnl Check for a working version of liba8cas that is of the right version. a8cas_found=no] ) AC_LANG_RESTORE - + else + AC_MSG_RESULT([skipping because we are cross compiling, assuming OK.]) + a8cas_found=yes + fi dnl Now that we know that we have the right version, let's see if we have the library and not just the headers. AC_CHECK_LIB([a8cas], [A8CAS_version],, [ifelse([$3], , [AC_MSG_ERROR(No linkable liba8cas was found.)]) @@ -289,7 +293,6 @@ dnl Check for a working version of liba8cas that is of the right version. export LD_LIBRARY_PATH fi - if test "x$a8cas_found" = "xyes" ; then ifelse([$2], , :, [$2]) else diff --git a/src/antic.c b/src/antic.c index b24c473..133355a 100644 --- a/src/antic.c +++ b/src/antic.c @@ -33,6 +33,7 @@ #include "memory.h" #include "platform.h" #include "pokey.h" +#include "pia.h" #include "util.h" #if !defined(BASIC) && !defined(CURSES_BASIC) #include "input.h" @@ -2867,6 +2868,7 @@ void ANTIC_Frame(int draw_display) ANTIC_ypos = 0; do { POKEY_Scanline(); /* check and generate IRQ */ + PIA_Scanline(); /* check and generate IRQ */ OVERSCREEN_LINE; } while (ANTIC_ypos < 8); @@ -2884,6 +2886,7 @@ void ANTIC_Frame(int draw_display) } POKEY_Scanline(); /* check and generate IRQ */ + PIA_Scanline(); /* check and generate IRQ */ pmg_dma(); #ifdef USE_CURSES @@ -3177,6 +3180,7 @@ void ANTIC_Frame(int draw_display) /* TODO: cycle-exact overscreen lines */ POKEY_Scanline(); /* check and generate IRQ */ + PIA_Scanline(); /* check and generate IRQ */ CPU_GO(ANTIC_NMIST_C); ANTIC_NMIST = 0x5f; /* Set VBLANK */ if (ANTIC_NMIEN & 0x40) { @@ -3188,6 +3192,7 @@ void ANTIC_Frame(int draw_display) do { POKEY_Scanline(); /* check and generate IRQ */ + PIA_Scanline(); /* check and generate IRQ */ OVERSCREEN_LINE; } while (ANTIC_ypos < Atari800_tv_mode); ANTIC_ypos = 0; /* just for monitor.c */ diff --git a/src/atari.c b/src/atari.c index 040de2c..5c234fd 100644 --- a/src/atari.c +++ b/src/atari.c @@ -1184,6 +1184,7 @@ static void basic_frame(void) ANTIC_ypos = 0; do { POKEY_Scanline(); /* check and generate IRQ */ + PIA_Scanline(); /* check and generate IRQ */ BASIC_LINE; } while (ANTIC_ypos < 8); @@ -1191,12 +1192,14 @@ static void basic_frame(void) /* scanlines 8 - 247 */ do { POKEY_Scanline(); /* check and generate IRQ */ + PIA_Scanline(); /* check and generate IRQ */ basic_antic_scanline(); BASIC_LINE; } while (ANTIC_ypos < 248); /* scanline 248 */ POKEY_Scanline(); /* check and generate IRQ */ + PIA_Scanline(); /* check and generate IRQ */ CPU_GO(ANTIC_NMIST_C); ANTIC_NMIST = 0x5f; /* Set VBLANK */ if (ANTIC_NMIEN & 0x40) { @@ -1208,6 +1211,7 @@ static void basic_frame(void) /* scanlines 249 - 261(311) */ do { POKEY_Scanline(); /* check and generate IRQ */ + PIA_Scanline(); /* check and generate IRQ */ BASIC_LINE; } while (ANTIC_ypos < Atari800_tv_mode); } diff --git a/src/cassette.c b/src/cassette.c index d238702..8b92db8 100644 --- a/src/cassette.c +++ b/src/cassette.c @@ -67,6 +67,12 @@ static int cassette_gapdelay = 0; /* in ms, includes leader and all gaps */ static int cassette_motor = 0; +/* Holds number of ticks since a motor was turned off. + Motor is off initialy + ~20 ms looks ok*/ +#define tail_max_length_ticks 35000 +static unsigned int ticks_since_motor_off = tail_max_length_ticks; + int CASSETTE_hold_start_on_reboot = 0; int CASSETTE_hold_start = 0; int CASSETTE_press_space = 0; @@ -76,20 +82,32 @@ static int eof_of_tape = 0; static unsigned int prev_cpu_clock; + #if HAVE_LIBA8CAS int CASSETTE_position_in_blocks; static int current_signal; -static int cassette_samplerate; +unsigned int CASSETTE_data_samplerate = 44100; /* initialize to minimal value */ enum CASSETTE_TURBO_TYPE_t CASSETTE_turbo_type = CASSETTE_TURBO_NONE; static char const * const turbo_type_cfg_strings[CASSETTE_TURBO_TYPE_SIZE] = { "NONE", "AST-XC12", + "AST-1010", + "ATT", + "UM", "BLIZZARD", "HARD", + "TURBOROM", + "2000STICK", + "2000F", + "6000", + "CS2000A", + "CS2000B", + "TAPEWIZ", + "SUPERTURBO", + "AUTOTURBO", "MANUAL", - "MANUAL-REV" }; enum TURBO_CONTROL_t { @@ -97,13 +115,32 @@ enum TURBO_CONTROL_t { TURBO_CONTROL_MANUAL, TURBO_CONTROL_DATA_OUT, TURBO_CONTROL_COMMAND, - TURBO_CONTROL_COMMAND_MOTOR + TURBO_CONTROL_COMMAND_MOTOR, + TURBO_CONTROL_PORTA_5 }; static enum TURBO_CONTROL_t turbo_control; -static int turbo_descend_is_1; + +enum TURBO_READ_t { + TURBO_READ_DATA_IN, + TURBO_READ_PIA_PROCEED, + TURBO_READ_PIA_INTERRUPT +}; +static enum TURBO_READ_t turbo_read; + +enum TURBO_WRITE_t { + TURBO_WRITE_NONE, + TURBO_WRITE_COMMAND, + TURBO_WRITE_DATA_OUT, + TURBO_WRITE_PORTA_6, + TURBO_WRITE_TWO_TONE /* bit 3 of SKCTL affects data-out line */ +}; +static enum TURBO_WRITE_t turbo_write; + int CASSETTE_turbo_state = 0; static int file_turbo_state = 0; int CASSETTE_turbo_tolerance = CASSETTE_DEFAULT_TURBO_TOLERANCE; +int CASSETTE_turbo_invert_polarity_read = 0; +int CASSETTE_turbo_invert_polarity_write = 0; #ifdef SYNCHRONIZED_SOUND int CASSETTE_play_audio = 1; @@ -145,27 +182,44 @@ static void UpdateFlags(void) static void UpdateTurboState(void) { if (CASSETTE_turbo_state != file_turbo_state && - cassette_file != NULL && - !CASSETTE_record) { - IMG_TAPE_SetPWMDecode(cassette_file, CASSETTE_turbo_state); - file_turbo_state = CASSETTE_turbo_state; + cassette_file != NULL) { + if (!CASSETTE_record) + IMG_TAPE_SetPWMDecode(cassette_file, CASSETTE_turbo_state); + else + IMG_TAPE_SetPWMEncode(cassette_file, CASSETTE_turbo_state); + file_turbo_state = CASSETTE_turbo_state; } + /* TODO: tape motor state update */ } static void ResetTurboState(void) { - switch (turbo_control) { - case TURBO_CONTROL_DATA_OUT: - CASSETTE_turbo_state = !POKEY_serial_data_output; - break; - case TURBO_CONTROL_COMMAND: - CASSETTE_turbo_state = !(PIA_PBCTL & 0x08); - break; - case TURBO_CONTROL_COMMAND_MOTOR: - CASSETTE_turbo_state = !(PIA_PBCTL & 0x08) && cassette_motor; - break; - default: - break; + if (CASSETTE_record && turbo_control != TURBO_CONTROL_NONE) + if (POKEY_AUDF[POKEY_CHAN1] == 0x5 && POKEY_AUDF[POKEY_CHAN2] == 0x7 && (POKEY_SKCTL & 0x28) == 0x28) + CASSETTE_turbo_state = 0; /* Looks like FSK recording */ + else + CASSETTE_turbo_state = 1; /* There is no switch when recording */ + else { + switch (turbo_control) { + case TURBO_CONTROL_DATA_OUT: + CASSETTE_turbo_state = !POKEY_serial_data_output; + break; + case TURBO_CONTROL_COMMAND: + CASSETTE_turbo_state = !(PIA_PBCTL & 0x08); + break; + case TURBO_CONTROL_COMMAND_MOTOR: + CASSETTE_turbo_state = !(PIA_PBCTL & 0x08) && cassette_motor; + break; + case TURBO_CONTROL_PORTA_5: + CASSETTE_turbo_state = ! (((PIA_PORTA | PIA_PORTA_mask) & 0x20) >> 5); + break; + default: + break; + } + if (CASSETTE_turbo_type == CASSETTE_TURBO_6000) { + CASSETTE_turbo_state = 1; /*There is no switch in Turbo 6000*/ + file_turbo_state = 0; /* workaround to force update */ + } } UpdateTurboState(); } @@ -202,12 +256,18 @@ int CASSETTE_ReadConfig(char *string, char *ptr) CASSETTE_write_protect = value; } #if HAVE_LIBA8CAS + else if (strcmp(string, "CASSETTE_DATA_SAMPLERATE") == 0) + return (CASSETTE_data_samplerate = Util_sscandec(ptr)) != -1; else if (strcmp(string, "CASSETTE_TURBO_TYPE") == 0) { int i = CFG_MatchTextParameter(ptr, turbo_type_cfg_strings, CASSETTE_TURBO_TYPE_SIZE); if (i < 0) return FALSE; CASSETTE_SetTurboType(i); } + else if (strcmp(string, "CASSETTE_TURBO_INVERT_POLARITY_READ") == 0) + return (CASSETTE_turbo_invert_polarity_read = Util_sscanbool(ptr)) != -1; + else if (strcmp(string, "CASSETTE_TURBO_INVERT_POLARITY_WRITE") == 0) + return (CASSETTE_turbo_invert_polarity_write = Util_sscanbool(ptr)) != -1; #ifdef SYNCHRONIZED_SOUND else if (strcmp(string, "CASSETTE_PLAY_AUDIO") == 0) return (CASSETTE_play_audio = Util_sscanbool(ptr)) != -1; @@ -231,11 +291,14 @@ void CASSETTE_WriteConfig(FILE *fp) fprintf(fp, "CASSETTE_WRITE_PROTECT=%d\n", CASSETTE_write_protect); #if HAVE_LIBA8CAS fprintf(fp, "CASSETTE_TURBO_TYPE=%s\n", turbo_type_cfg_strings[CASSETTE_turbo_type]); + fprintf(fp, "CASSETTE_DATA_SAMPLERATE=%d\n", CASSETTE_data_samplerate); #ifdef SYNCHRONIZED_SOUND fprintf(fp, "CASSETTE_PLAY_AUDIO=%d\n", CASSETTE_play_audio); fprintf(fp, "CASSETTE_AUDIO_VOLUME=%u\n", CASSETTE_audio_volume); fprintf(fp, "CASSETTE_CROSSTALK_VOLUME=%u\n", CASSETTE_crosstalk_volume); #endif /* SYNCHRONIZED_SOUND */ + fprintf(fp, "CASSETTE_TURBO_INVERT_POLARITY_READ=%d\n", CASSETTE_turbo_invert_polarity_read); + fprintf(fp, "CASSETTE_TURBO_INVERT_POLARITY_WRITE=%d\n", CASSETTE_turbo_invert_polarity_write); #endif /* HAVE_LIBA8CAS */ } @@ -294,7 +357,7 @@ int CASSETTE_Initialise(int *argc, char *argv[]) Log_print("\t-boottape Insert cassette image and boot it"); Log_print("\t-tape-readonly Mark the attached cassette image as read-only"); #if HAVE_LIBA8CAS - Log_print("\t-tape-turbo none|ast-xc12|blizzard|hard|manual|manual-rev"); + Log_print("\t-tape-turbo none|ast-xc12|ast-1010|att|um|blizzard|hard|turborom|2000stick|2000f|6000|cs2000a|cs2000b|tapewiz|cssuperturbo|autoturbo|manual"); Log_print("\t Choose tape recorder's turbo modification"); #endif /* HAVE_LIBA8CAS */ } @@ -370,7 +433,13 @@ int CASSETTE_Insert(const char *filename) cassette_gapdelay = 0; #if HAVE_LIBA8CAS - cassette_samplerate = IMG_TAPE_GetSamplerate(cassette_file); + CASSETTE_data_samplerate = IMG_TAPE_GetSamplerate(cassette_file); + if (CASSETTE_data_samplerate == 0) /* to be fixed in the future at least in some cases */ +#ifdef SYNCHRONIZED_SOUND + CASSETTE_data_samplerate = audio.samplerate; /* default */ +#else /* SYNCHRONIZED_SOUND */ + CASSETTE_data_samplerate = 44100; /* default */ +#endif /* SYNCHRONIZED_SOUND */ CASSETTE_position_in_blocks = IMG_TAPE_PositionInBlocks(cassette_file); file_turbo_state = 0; ResetTurboState(); @@ -397,12 +466,27 @@ void CASSETTE_Remove(void) } CASSETTE_status = CASSETTE_STATUS_NONE; CASSETTE_description[0] = '\0'; +#if HAVE_LIBA8CAS +#ifdef SYNCHRONIZED_SOUND + CASSETTE_data_samplerate = audio.samplerate; +#else + CASSETTE_data_samplerate = 44100; +#endif /* SYNCHRONIZED_SOUND */ +#endif /* HAVE_LIBA8CAS */ UpdateFlags(); } static int CreateBlankImage(IMG_TAPE_format_t format, char const *filename, char const *description) { - IMG_TAPE_t *file = IMG_TAPE_Create(filename, format, description); + IMG_TAPE_t *file; + +#if HAVE_LIBA8CAS && defined(SYNCHRONIZED_SOUND) + /* samplerate (btw: bit depth too) must be set before audio file is created. + can't be changed afterwards */ + file = IMG_TAPE_Create(filename, format, description, CASSETTE_data_samplerate); +#else + file = IMG_TAPE_Create(filename, format, description); +#endif if (file == NULL) return FALSE; @@ -420,7 +504,7 @@ static int CreateBlankImage(IMG_TAPE_format_t format, char const *filename, char UpdateFlags(); #if HAVE_LIBA8CAS - cassette_samplerate = IMG_TAPE_GetSamplerate(cassette_file); + CASSETTE_data_samplerate = IMG_TAPE_GetSamplerate(cassette_file); CASSETTE_position_in_blocks = IMG_TAPE_PositionInBlocks(cassette_file); file_turbo_state = 0; ResetTurboState(); @@ -428,7 +512,7 @@ static int CreateBlankImage(IMG_TAPE_format_t format, char const *filename, char #ifdef SYNCHRONIZED_SOUND audio.sample_pos = 0.0; audio.samples_left_in_signal = 0; - IMG_TAPE_SetAudio(cassette_file, audio.samplerate, (audio.bit16 ? 16 : 8)); + IMG_TAPE_SetAudio(cassette_file, audio.samplerate, (audio.bit16 ? 16 : 8)); /* This does NOT affect audio file after it is cretated */ UpdateCrosstalkVolume(); #endif /* SYNCHRONIZED_SOUND */ #endif /* HAVE_LIBA8CAS */ @@ -442,6 +526,12 @@ int CASSETTE_CreateCAS(const char *filename, char const *description) } #if HAVE_LIBA8CAS + +int CASSETTE_CreateHEX(char const *filename, char *description) +{ + return CreateBlankImage(IMG_TAPE_FORMAT_HEX, filename, description); +} + int CASSETTE_CreateWAV(char const *filename) { return CreateBlankImage(IMG_TAPE_FORMAT_WAV, filename, NULL); @@ -463,7 +553,7 @@ unsigned int CASSETTE_GetPosition(void) return IMG_TAPE_GetPosition(cassette_file) + 1; #if HAVE_LIBA8CAS else - return IMG_TAPE_GetPosition(cassette_file) / cassette_samplerate; + return IMG_TAPE_GetPosition(cassette_file) / CASSETTE_data_samplerate; #endif /* HAVE_LIBA8CAS */ } @@ -477,7 +567,7 @@ unsigned int CASSETTE_GetSize(void) return IMG_TAPE_GetSize(cassette_file); #if HAVE_LIBA8CAS else - return IMG_TAPE_GetSize(cassette_file) / cassette_samplerate; + return IMG_TAPE_GetSize(cassette_file) / CASSETTE_data_samplerate; #endif /* HAVE_LIBA8CAS */ } @@ -492,7 +582,7 @@ void CASSETTE_Seek(unsigned int position) IMG_TAPE_Seek(cassette_file, position); #if HAVE_LIBA8CAS } else - IMG_TAPE_Seek(cassette_file, position * cassette_samplerate); + IMG_TAPE_Seek(cassette_file, position * CASSETTE_data_samplerate); #endif /* HAVE_LIBA8CAS */ event_time_left = 0.0; eof_of_tape = 0; @@ -513,7 +603,7 @@ int CASSETTE_IOLineStatus(void) if (!ESC_enable_sio_patch) CASSETTE_AddScanLine(); #if HAVE_LIBA8CAS - if (CASSETTE_turbo_state && turbo_descend_is_1) + if (CASSETTE_turbo_state && CASSETTE_turbo_invert_polarity_read) return !(int)current_signal; else return (int)current_signal; @@ -536,6 +626,8 @@ void CASSETTE_PutByte(int byte) void CASSETTE_TapeMotor(int onoff) { if (cassette_motor != onoff) { + if (!onoff && CASSETTE_record) /* turning off after recording */ + ticks_since_motor_off = 0; if (CASSETTE_record && CASSETTE_writable) /* Recording disabled, flush the tape */ IMG_TAPE_Flush(cassette_file); @@ -553,6 +645,9 @@ int CASSETTE_ToggleWriteProtect(void) return FALSE; CASSETTE_write_protect = !CASSETTE_write_protect; UpdateFlags(); +#if HAVE_LIBA8CAS + UpdateTurboState(); +#endif /* HAVE_LIBA8CAS */ return TRUE; } @@ -585,36 +680,84 @@ void CASSETTE_ToggleTurboState(void) void CASSETTE_SetTurboType(enum CASSETTE_TURBO_TYPE_t type) { CASSETTE_turbo_type = type; - + turbo_read = TURBO_READ_DATA_IN; switch (type) { case CASSETTE_TURBO_AST_XC12: + case CASSETTE_TURBO_AST_1010: + case CASSETTE_TURBO_ATT: + case CASSETTE_TURBO_UM: turbo_control = TURBO_CONTROL_COMMAND; - turbo_descend_is_1 = 0; + turbo_write = TURBO_WRITE_COMMAND; break; case CASSETTE_TURBO_BLIZZARD: turbo_control = TURBO_CONTROL_DATA_OUT; - turbo_descend_is_1 = 0; + turbo_write = TURBO_WRITE_DATA_OUT; break; case CASSETTE_TURBO_HARD: /* PWM circuit is enabled by asserting COMMAND and turning on the motor. */ turbo_control = TURBO_CONTROL_COMMAND_MOTOR; - turbo_descend_is_1 = 0; + turbo_write = TURBO_WRITE_TWO_TONE; break; - case CASSETTE_TURBO_MANUAL: + case CASSETTE_TURBO_TURBOROM: + turbo_control = TURBO_CONTROL_COMMAND; + turbo_write = TURBO_WRITE_COMMAND; + break; + case CASSETTE_TURBO_2000_STICK: + turbo_control = TURBO_CONTROL_PORTA_5; + turbo_write = TURBO_WRITE_PORTA_6; + break; + case CASSETTE_TURBO_2000F: + turbo_control = TURBO_CONTROL_COMMAND; + turbo_write = TURBO_WRITE_DATA_OUT; + break; + case CASSETTE_TURBO_6000: turbo_control = TURBO_CONTROL_MANUAL; - turbo_descend_is_1 = 0; + turbo_write = TURBO_WRITE_DATA_OUT; + turbo_read = TURBO_READ_PIA_PROCEED; + break; + case CASSETTE_TURBO_CS2000_A: + turbo_control = TURBO_CONTROL_COMMAND; + turbo_write = TURBO_WRITE_DATA_OUT; + break; + case CASSETTE_TURBO_CS2000_B: + turbo_control = TURBO_CONTROL_COMMAND; + turbo_write = TURBO_WRITE_TWO_TONE; + break; + case CASSETTE_TURBO_TAPEWIZ: + turbo_control = TURBO_CONTROL_COMMAND; + turbo_write = TURBO_WRITE_DATA_OUT; + turbo_read = TURBO_READ_PIA_INTERRUPT; + break; + case CASSETTE_TURBO_SUPERTURBO: + turbo_control = TURBO_CONTROL_COMMAND; + turbo_write = TURBO_WRITE_TWO_TONE; break; - case CASSETTE_TURBO_MANUAL_REV: + case CASSETTE_TURBO_AUTOTURBO: + turbo_control = TURBO_CONTROL_COMMAND; + turbo_write = TURBO_WRITE_TWO_TONE; + break; + case CASSETTE_TURBO_MANUAL: turbo_control = TURBO_CONTROL_MANUAL; - turbo_descend_is_1 = 1; + turbo_write = TURBO_WRITE_DATA_OUT; break; default: turbo_control = TURBO_CONTROL_NONE; + turbo_write = TURBO_WRITE_NONE; } CASSETTE_turbo_state = 0; ResetTurboState(); } +void CASSETTE_ToggleTurboPolarityRead(void) +{ + CASSETTE_turbo_invert_polarity_read = !CASSETTE_turbo_invert_polarity_read; +} + +void CASSETTE_ToggleTurboPolarityWrite(void) +{ + CASSETTE_turbo_invert_polarity_write = !CASSETTE_turbo_invert_polarity_write; +} + void CASSETTE_SetTurboTolerance(int value) { CASSETTE_turbo_tolerance = value; @@ -694,27 +837,62 @@ static void WriteSilence(int num_samples) static void CassetteWriteAudio(int num_ticks) { int num_samples = 0; + unsigned int audio_samples = 0; + unsigned int predicted_audio_samples; + int curr_sig; + unsigned int ticks_per_frame = Atari800_tv_mode*114; + double CASSETTE_samples_per_frame = (double)CASSETTE_data_samplerate/(Atari800_tv_mode == Atari800_TV_PAL ? Atari800_FPS_PAL : Atari800_FPS_NTSC); + double CASSETTE_ticks_per_sample = (double)ticks_per_frame / CASSETTE_samples_per_frame; + double samplerate_ratio = (double)CASSETTE_data_samplerate / audio.samplerate; event_time_left -= num_ticks; if (event_time_left < 0.0) { - num_samples = ceil((-event_time_left) / SYNCSOUND_ticks_per_sample); - event_time_left += num_samples * SYNCSOUND_ticks_per_sample; + num_samples = ceil((-event_time_left) / CASSETTE_ticks_per_sample); + event_time_left += num_samples * CASSETTE_ticks_per_sample; } - if ((audio.bit16 ? (audio.write_pos + num_samples) << 1 : audio.write_pos + num_samples) > audio.buf_size) { + + predicted_audio_samples = ceil(num_samples / samplerate_ratio); + if ((audio.bit16 ? (audio.write_pos + predicted_audio_samples) << 1 : audio.write_pos + predicted_audio_samples) > audio.buf_size) { /* Log_print("Cassette audio buffer overload: %f", event_time_left);*/ audio.write_pos = 0; } /* Log_print("caswrite: num_samples=%i", num_samples);*/ - if (CASSETTE_writable && num_samples > 0) { + if (CASSETTE_writable || ticks_since_motor_off < tail_max_length_ticks) { /* Log_print("addscanline: numsamples=%i, signal=%u, tick=%i", num_samples, POKEY_serial_data_output, ANTIC_CPU_CLOCK);*/ - if (!IMG_TAPE_WriteAudio(cassette_file, num_samples, POKEY_serial_data_output, audio.buffer + (audio.bit16 ? audio.write_pos << 1 : audio.write_pos))) { - WriteSilence(num_samples); + if (CASSETTE_turbo_state) { + switch(turbo_write) { + case TURBO_WRITE_DATA_OUT: + curr_sig = POKEY_serial_data_output; /* bit 7 skctl: 1 gives 0V, 0 gives 5V */ + break; + case TURBO_WRITE_COMMAND: + curr_sig = !PIA_CB2; /* opposite to bit 7 of SKCTL: 1 gives 5V, 0 gives 0V */ + break; + case TURBO_WRITE_PORTA_6: + curr_sig = ((PIA_PORTA | PIA_PORTA_mask) & 0x40) >> 6; + break; + case TURBO_WRITE_TWO_TONE: + curr_sig = (POKEY_SKCTL & 0x08) >> 3; /* like bit 7: 1 gives ~1.9V, 0 gives 5V */ + break; + default: + curr_sig = POKEY_serial_data_output; + } + if (CASSETTE_turbo_invert_polarity_write) + curr_sig = !curr_sig; + } + else + curr_sig = POKEY_serial_data_output; + + if (!IMG_TAPE_WriteWithAudio(cassette_file, num_samples, curr_sig, + audio.buffer + (audio.bit16 ? audio.write_pos << 1 : audio.write_pos), &audio_samples)) { + WriteSilence(predicted_audio_samples); return; } - audio.write_pos += num_samples; + audio.write_pos += audio_samples; + if (!CASSETTE_writable) + ticks_since_motor_off += num_ticks; } else { - WriteSilence(num_samples); + WriteSilence(predicted_audio_samples); } } @@ -744,7 +922,7 @@ static void CassetteReadAudio(int num_ticks) } { - unsigned int length; + double length; if (!IMG_TAPE_ReadWithAudio(cassette_file, &length, ¤t_signal, &audio.samples_left_in_signal)) { /* Ignore the eventual error, assume it is the end of file */ eof_of_tape = 1; @@ -812,6 +990,8 @@ void CASSETTE_InitAudio(unsigned int samplerate, unsigned int n_pokeys, int b16) IMG_TAPE_SetAudio(cassette_file, samplerate, (b16 ? 16 : 8)); cassette_write_func = &CassetteWriteAudio; cassette_read_func = &CassetteReadAudio; + if (CASSETTE_status == CASSETTE_STATUS_NONE) /* only if cassette is not inserted */ + CASSETTE_data_samplerate = audio.samplerate; /* initialize to audio frequency */ } void CASSETTE_UpdateSound(void *sndbuffer, int sndn) @@ -861,11 +1041,38 @@ void CASSETTE_UpdateSound(void *sndbuffer, int sndn) static void CassetteWrite(int num_ticks) { +#if HAVE_LIBA8CAS + int curr_sig; + + if (CASSETTE_turbo_state) { + switch(turbo_write) { + case TURBO_WRITE_DATA_OUT: + curr_sig = POKEY_serial_data_output; /* bit 7 skctl: 1 gives 0V, 0 gives 5V */ + break; + case TURBO_WRITE_COMMAND: + curr_sig = !PIA_CB2; /* opposite to bit 7 of SKCTL: 1 gives 5V, 0 gives 0V */ + break; + case TURBO_WRITE_PORTA_6: + curr_sig = ((PIA_PORTA | PIA_PORTA_mask) & 0x40) >> 6; + break; + case TURBO_WRITE_TWO_TONE: + curr_sig = (POKEY_SKCTL & 0x08) >> 3; /* like bit 7: 1 gives ~1.9V, 0 gives 5V */ + break; + default: + curr_sig = POKEY_serial_data_output; + } + if (CASSETTE_turbo_invert_polarity_write) + curr_sig = !curr_sig; + } + else + curr_sig = POKEY_serial_data_output; +#endif /* HAVE_LIBA8CAS */ + /* Log_print("caswrite: num_samples=%i", num_samples);*/ if (CASSETTE_writable) { /* Log_print("addscanline: numsamples=%i, signal=%u, tick=%i", num_samples, POKEY_serial_data_output, ANTIC_CPU_CLOCK);*/ #if HAVE_LIBA8CAS - IMG_TAPE_Write(cassette_file, num_ticks, POKEY_serial_data_output); + IMG_TAPE_Write(cassette_file, num_ticks, curr_sig); #else IMG_TAPE_WriteAdvance(cassette_file, num_ticks); #endif /* HAVE_LIBA8CAS */ diff --git a/src/cassette.h b/src/cassette.h index 1ec933d..bd30a68 100644 --- a/src/cassette.h +++ b/src/cassette.h @@ -33,6 +33,7 @@ void CASSETTE_Remove(void); /* Creates a new file in CAS format. DESCRIPTION can be NULL. Returns TRUE on success, FALSE otherwise. */ int CASSETTE_CreateCAS(char const *filename, char const *description); +int CASSETTE_CreateHEX(char const *filename, char *description); #if HAVE_LIBA8CAS int CASSETTE_CreateWAV(char const *filename); int CASSETTE_CreateRaw(char const *filename); @@ -102,10 +103,21 @@ void CASSETTE_UpdateSound(void *sndbuffer, int sndn); enum CASSETTE_TURBO_TYPE_t { CASSETTE_TURBO_NONE = 0, CASSETTE_TURBO_AST_XC12, + CASSETTE_TURBO_AST_1010, + CASSETTE_TURBO_ATT, + CASSETTE_TURBO_UM, CASSETTE_TURBO_BLIZZARD, CASSETTE_TURBO_HARD, + CASSETTE_TURBO_TURBOROM, + CASSETTE_TURBO_2000_STICK, + CASSETTE_TURBO_2000F, + CASSETTE_TURBO_6000, + CASSETTE_TURBO_CS2000_A, + CASSETTE_TURBO_CS2000_B, + CASSETTE_TURBO_TAPEWIZ, + CASSETTE_TURBO_SUPERTURBO, + CASSETTE_TURBO_AUTOTURBO, CASSETTE_TURBO_MANUAL, - CASSETTE_TURBO_MANUAL_REV, /* Number of values in enumerator */ CASSETTE_TURBO_TYPE_SIZE }; @@ -120,6 +132,11 @@ enum { CASSETTE_DEFAULT_TURBO_TOLERANCE = 3, CASSETTE_MAX_TURBO_TOLERANCE = 99 }; +extern int CASSETTE_turbo_invert_polarity_read; +extern int CASSETTE_turbo_invert_polarity_write; + +void CASSETTE_ToggleTurboPolarityRead(void); +void CASSETTE_ToggleTurboPolarityWrite(void); #endif /* HAVE_LIBA8CAS */ @@ -147,4 +164,12 @@ extern int CASSETTE_readable; read-only. */ extern int CASSETTE_writable; +/* Sample rate of data track. */ +extern unsigned int CASSETTE_data_samplerate; + +enum { + CASSETTE_DATA_SAMPLERATE_MIN=44100, /* less is undesired */ + CASSETTE_DATA_SAMPLERATE_MAX=384000 /* more impossible to achieve */ +}; + #endif /* CASSETTE_H_ */ diff --git a/src/configure.ac b/src/configure.ac index da7175a..27e6fc0 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -1012,7 +1012,7 @@ AC_ARG_WITH([a8cas], [AC_HELP_STRING([--with-a8cas], [Use liba8cas for exact tap [with_a8cas=check]) if [[ "$with_a8cas" != no ]]; then - AM_PATH_A8CAS([1.3.0], [with_a8cas=yes], + AM_PATH_A8CAS([1.5.1], [with_a8cas=yes], [if [[ "$with_a8cas" != check ]]; then AC_MSG_FAILURE([unable to use liba8cas - the library was not found.]) fi diff --git a/src/cpu.c b/src/cpu.c index 2a1bdd8..3ccd881 100644 --- a/src/cpu.c +++ b/src/cpu.c @@ -75,7 +75,11 @@ #endif #endif /* BASIC */ #endif /* ASAP */ - +#if HAVE_LIBA8CAS +#include "cassette.h" +#include "pia.h" +#include "pokey.h" +#endif #ifdef FALCON_CPUASM extern UBYTE CPU_IRQ; @@ -563,6 +567,14 @@ void CPU_GO(int limit) while (ANTIC_xpos < ANTIC_xpos_limit) { +#if HAVE_LIBA8CAS + if (CASSETTE_turbo_type == CASSETTE_TURBO_TAPEWIZ && (CASSETTE_readable || CASSETTE_writable)) { + PIA_Scanline(); + POKEY_Decrement_Counters(); + CPUCHECKIRQ; + } +#endif /* HAVE_LIBA8CAS */ + #ifdef MONITOR_BREAKPOINTS breakpoint_return: #endif diff --git a/src/img_tape.h b/src/img_tape.h index 63023c1..a385abe 100644 --- a/src/img_tape.h +++ b/src/img_tape.h @@ -51,13 +51,22 @@ void IMG_TAPE_Close(IMG_TAPE_t *file); typedef enum IMG_TAPE_format_t { IMG_TAPE_FORMAT_CAS, +#if HAVE_LIBA8CAS + IMG_TAPE_FORMAT_HEX, +#endif IMG_TAPE_FORMAT_RAW, IMG_TAPE_FORMAT_WAV } IMG_TAPE_format_t; /* Creates a new writable CAS file at FILENAME, with a given description. Returns pointer to the opened file on success or NULL otherwise. */ -IMG_TAPE_t *IMG_TAPE_Create(char const *filename, IMG_TAPE_format_t format, char const *description); +#if HAVE_LIBA8CAS && defined(SYNCHRONIZED_SOUND) + /* we want to set sample rate of audio file during it's creation, can't be changed afterwards */ + /* TODO (?) bith depth too ? */ + IMG_TAPE_t *IMG_TAPE_Create(char const *filename, IMG_TAPE_format_t format, char const *description, unsigned int samplerate); +#else + IMG_TAPE_t *IMG_TAPE_Create(char const *filename, IMG_TAPE_format_t format, char const *description); +#endif #if HAVE_LIBA8CAS /* length - in CPU cycles */ @@ -92,6 +101,7 @@ int IMG_TAPE_Flush(IMG_TAPE_t *file); #if HAVE_LIBA8CAS void IMG_TAPE_SetPWMDecode(IMG_TAPE_t *file, int value); +void IMG_TAPE_SetPWMEncode(IMG_TAPE_t *file, int value); void IMG_TAPE_SetPWMTolerance(IMG_TAPE_t *file, unsigned short int value); #ifdef SYNCHRONIZED_SOUND void IMG_TAPE_SetCrosstalkVolume(IMG_TAPE_t *file, float value); @@ -111,8 +121,8 @@ void IMG_TAPE_Seek(IMG_TAPE_t *file, unsigned int position); #if HAVE_LIBA8CAS && defined(SYNCHRONIZED_SOUND) void IMG_TAPE_SetAudio(IMG_TAPE_t *file, unsigned int samplerate, unsigned int bits); -int IMG_TAPE_WriteAudio(IMG_TAPE_t *file, unsigned int num_samples, int signal, UBYTE *audio_buffer); -int IMG_TAPE_ReadWithAudio(IMG_TAPE_t *file, unsigned int *length, int *signal, unsigned int *num_samples); +int IMG_TAPE_WriteWithAudio(IMG_TAPE_t *file, unsigned int num_samples, int signal, UBYTE *audio_buffer, unsigned int *audio_samples); +int IMG_TAPE_ReadWithAudio(IMG_TAPE_t *file, double *length, int *signal, unsigned int *num_samples); int IMG_TAPE_ReadAudio(IMG_TAPE_t *file, UBYTE *audio_buffer, unsigned int num_samples); #endif diff --git a/src/img_tape_a8cas.c b/src/img_tape_a8cas.c index 934b80f..e4e682a 100644 --- a/src/img_tape_a8cas.c +++ b/src/img_tape_a8cas.c @@ -34,6 +34,7 @@ /* Index of each entry in this array correspond to a value in IMG_TAPE_format_t. */ static A8CAS_format format_conv[] = { A8CAS_FORMAT_CAS, + A8CAS_FORMAT_HEX, A8CAS_FORMAT_RAW, A8CAS_FORMAT_SNDFILE }; @@ -94,7 +95,6 @@ IMG_TAPE_t *IMG_TAPE_Open(char const *filename, int *writable, int *is_raw, char img = Util_malloc(sizeof(IMG_TAPE_t)); img->file = file; img->samplerate = cas_info.samplerate; - *writable = cas_info.mode == A8CAS_RDWR; *is_raw = cas_info.format == A8CAS_FORMAT_RAW; *description = cas_info.description; @@ -113,14 +113,22 @@ void IMG_TAPE_Close(IMG_TAPE_t *file) free(file); } +#ifdef SYNCHRONIZED_SOUND +IMG_TAPE_t *IMG_TAPE_Create(char const *filename, IMG_TAPE_format_t format, char const *description, unsigned int samplerate) +#else IMG_TAPE_t *IMG_TAPE_Create(char const *filename, IMG_TAPE_format_t format, char const *description) +#endif { A8CAS_info cas_info; IMG_TAPE_t *img; A8CAS_FILE *file; cas_info.format = format_conv[format]; +#ifdef SYNCHRONIZED_SOUND + cas_info.samplerate = samplerate; +#else cas_info.samplerate = 44100; +#endif cas_info.description = description; cas_info.mode = A8CAS_WRRD; if ((file = A8CAS_open(filename, &cas_info)) == NULL || cas_info.mode != A8CAS_WRRD) @@ -174,6 +182,11 @@ void IMG_TAPE_SetPWMDecode(IMG_TAPE_t *file, int value) A8CAS_set_param(file->file, A8CAS_PARAM_PWM_DECODE, &value); } +void IMG_TAPE_SetPWMEncode(IMG_TAPE_t *file, int value) +{ + A8CAS_set_param(file->file, A8CAS_PARAM_PWM_ENCODE, &value); +} + void IMG_TAPE_SetPWMTolerance(IMG_TAPE_t *file, unsigned short int value) { A8CAS_set_param(file->file, A8CAS_PARAM_PWM_TOLERANCE, &value); @@ -213,17 +226,17 @@ void IMG_TAPE_SetAudio(IMG_TAPE_t *file, unsigned int samplerate, unsigned int b file->audio_samplerate = samplerate; } -int IMG_TAPE_WriteAudio(IMG_TAPE_t *file, unsigned int num_samples, int signal, UBYTE *audio_buffer) +int IMG_TAPE_WriteWithAudio(IMG_TAPE_t *file, unsigned int num_samples, int signal, UBYTE *audio_buffer, unsigned int *audio_samples) { A8CAS_signal sig; sig.signal = signal; sig.length = num_samples; - sig.rate = file->audio_samplerate; + sig.rate = file->samplerate; - return A8CAS_write_audio(file->file, &sig, audio_buffer) == 0; + return A8CAS_write_with_audio(file->file, &sig, audio_buffer, audio_samples) == 0; } -int IMG_TAPE_ReadWithAudio(IMG_TAPE_t *file, unsigned int *length, int *signal, unsigned int *num_samples) +int IMG_TAPE_ReadWithAudio(IMG_TAPE_t *file, double *length, int *signal, unsigned int *num_samples) { A8CAS_signal sig; diff --git a/src/pia.c b/src/pia.c index 9c8093b..52375a7 100644 --- a/src/pia.c +++ b/src/pia.c @@ -27,8 +27,10 @@ #include "atari.h" #include "cassette.h" #include "cpu.h" +#include "pokey.h" #include "memory.h" #include "pia.h" +#include "pbi.h" #include "sio.h" #include "pokey.h" #ifdef XEP80_EMULATION @@ -96,6 +98,12 @@ static void set_CB2(int value) if (PIA_CB2 != value) { /* The command line status has changed */ SIO_SwitchCommandFrame(!value); +#if HAVE_LIBA8CAS + if (CASSETTE_turbo_state) { + CASSETTE_AddScanLine(); + } +#endif /*HAVE_LIBA8CAS*/ + } PIA_CB2 = value; } @@ -113,21 +121,95 @@ static void update_PIA_IRQ(void) POKEY_PutByte(POKEY_OFFSET_IRQEN, POKEY_IRQEN); } +#if HAVE_LIBA8CAS +void PIA_TapeMotor(int onoff) +{ + CASSETTE_TapeMotor(onoff); +} +#endif /*HAVE_LIBA8CAS*/ + +static void PIA_CheckProceed(void) +{ + static int prev_input = 1; + static int curr_input = 1; /* better have it always defined */ + +#if HAVE_LIBA8CAS + if (CASSETTE_turbo_type != CASSETTE_TURBO_6000 || !(CASSETTE_readable || CASSETTE_writable)) + return; + curr_input = CASSETTE_IOLineStatus(); +#endif /*HAVE_LIBA8CAS*/ + if (prev_input == 1 && curr_input == 0) { + PIA_PACTL |= 0x80; /* PIA PORTA interrupt request */ + update_PIA_IRQ(); + } + prev_input = curr_input; +} + +static void PIA_CheckInterrupt(void) +{ + static int prev_input = 1; + static int curr_input = 1; /* better have it always defined */ + +#if HAVE_LIBA8CAS + if (CASSETTE_turbo_type != CASSETTE_TURBO_TAPEWIZ || !(CASSETTE_readable || CASSETTE_writable)) + return; + curr_input = CASSETTE_IOLineStatus(); +#endif /*HAVE_LIBA8CAS*/ + + if (prev_input == 1 && curr_input == 0) { + PIA_PBCTL |= 0x80; /* PIA PORTB interrupt request */ + update_PIA_IRQ(); + } + prev_input = curr_input; +} + +#if HAVE_LIBA8CAS +static void PIA_CassetteInputUpdate(void) +{ + int curr_input = CASSETTE_IOLineStatus(); + + /* Turbo 2000 tape recorder connected to 2nd joystick port + sends data on bit 7 */ + PIA_PORT_input[0] &= 0x7f; + PIA_PORT_input[0] |= (UBYTE)(curr_input << 7); +} +#endif /*HAVE_LIBA8CAS*/ + UBYTE PIA_GetByte(UWORD addr, int no_side_effects) { switch (addr & 0x03) { case PIA_OFFSET_PACTL: +#if HAVE_LIBA8CAS + if (CASSETTE_turbo_type == CASSETTE_TURBO_6000 && CASSETTE_readable) { + /* turbo 6000 waits for "Proceed" line status change during read */ + PIA_CheckProceed(); + } +#endif /*HAVE_LIBA8CAS*/ /* read CRA (control register A) */ - return PIA_PACTL; + return PIA_PACTL & 0xbf; case PIA_OFFSET_PBCTL: +#if HAVE_LIBA8CAS + if (CASSETTE_readable || CASSETTE_writable) { + /* Update state of "Interrupt" line, just in case, no known usage */ + PIA_CheckInterrupt(); + } +#endif /*HAVE_LIBA8CAS*/ /* read CRB (control register B) */ - return PIA_PBCTL; + return PIA_PBCTL & 0xbf; case PIA_OFFSET_PORTA: + /*reset IRQ status*/ + PIA_PACTL &= 0x7f; + if ((~POKEY_IRQST & POKEY_IRQEN) == 0 && PBI_IRQ == 0 && (PIA_PBCTL & 0x81) != 0x81) + CPU_IRQ=0; if ((PIA_PACTL & 0x04) == 0) { /* read DDRA (data direction register A) */ return ~PIA_PORTA_mask; } else { +#if HAVE_LIBA8CAS + if (CASSETTE_turbo_type == CASSETTE_TURBO_2000_STICK && CASSETTE_readable) + PIA_CassetteInputUpdate(); +#endif /*HAVE_LIBA8CAS*/ /* read PIBA (peripheral interface buffer A) */ /* also called ORA (output register A) even for reading in data sheet */ if (!no_side_effects) { @@ -152,6 +234,10 @@ UBYTE PIA_GetByte(UWORD addr, int no_side_effects) return PIA_PORT_input[0] & (PIA_PORTA | PIA_PORTA_mask); } case PIA_OFFSET_PORTB: + /*reset IRQ status*/ + PIA_PBCTL &= 0x7f; + if ((~POKEY_IRQST & POKEY_IRQEN) == 0 && PBI_IRQ == 0 && (PIA_PACTL & 0x81) != 0x81) + CPU_IRQ=0; if ((PIA_PBCTL & 0x04) == 0) { /* read DDRA (data direction register B) */ return ~PIA_PORTB_mask; @@ -181,7 +267,13 @@ void PIA_PutByte(UWORD addr, UBYTE byte) case PIA_OFFSET_PACTL: /* write CRA (control register A) */ byte &= 0x3f; /* bits 6 & 7 cannot be written */ - +#if HAVE_LIBA8CAS + if (CASSETTE_turbo_type == CASSETTE_TURBO_2000_STICK) { + if (((PIA_PACTL & 0x04) == 0) && ((byte & 0x04) != 0)) + /* update tape motor state, when role of PORTA changes */ + PIA_TapeMotor((PIA_PORT_input[0] & (PIA_PORTA | PIA_PORTA_mask) & 0x20) ? 0 : 1); + } +#endif /*HAVE_LIBA8CAS*/ PIA_PACTL = ((PIA_PACTL & 0xc0) | byte); /* CA2 control */ switch((byte & 0x38)>>3) { @@ -295,7 +387,22 @@ void PIA_PutByte(UWORD addr, UBYTE byte) XEP80_PutBit(byte); } #endif /* XEP80_EMULATION */ + +#if HAVE_LIBA8CAS + if (CASSETTE_turbo_type == CASSETTE_TURBO_2000_STICK && CASSETTE_writable) { + CASSETTE_AddScanLine(); + } +#endif /*HAVE_LIBA8CAS*/ + PIA_PORTA = byte; /* change from thor */ + +#if HAVE_LIBA8CAS + /* Tape motor control for Turbo 2000 K.S.O */ + if (CASSETTE_turbo_type == CASSETTE_TURBO_2000_STICK) { + PIA_TapeMotor((PIA_PORT_input[0] & (PIA_PORTA | PIA_PORTA_mask) & 0x20) ? 0 : 1); + } +#endif /*HAVE_LIBA8CAS*/ + } #ifndef BASIC INPUT_SelectMultiJoy((PIA_PORTA | PIA_PORTA_mask) >> 4); @@ -330,6 +437,18 @@ void PIA_PutByte(UWORD addr, UBYTE byte) } } +/* PIA_Scanline is created as a part of tape emulation */ + +void PIA_Scanline(void) +{ + PIA_CheckProceed(); + PIA_CheckInterrupt(); +#if HAVE_LIBA8CAS + if (CASSETTE_turbo_type == CASSETTE_TURBO_2000_STICK && CASSETTE_readable) + PIA_CassetteInputUpdate(); +#endif /*HAVE_LIBA8CAS*/ +} + #ifndef BASIC void PIA_StateSave(void) diff --git a/src/pia.h b/src/pia.h index a86dd24..230af80 100644 --- a/src/pia.h +++ b/src/pia.h @@ -20,10 +20,12 @@ extern int PIA_CB2; extern int PIA_IRQ; int PIA_Initialise(int *argc, char *argv[]); +void PIA_Scanline(void); void PIA_Reset(void); UBYTE PIA_GetByte(UWORD addr, int no_side_effects); void PIA_PutByte(UWORD addr, UBYTE byte); void PIA_StateSave(void); void PIA_StateRead(UBYTE version); +void PIA_TapeMotor(int); #endif /* PIA_H_ */ diff --git a/src/pokey.c b/src/pokey.c index eec81ce..c7bae34 100644 --- a/src/pokey.c +++ b/src/pokey.c @@ -61,6 +61,8 @@ #include "votraxsnd.h" #endif +#include /*For UINT_MAX only*/ + #ifdef POKEY_UPDATE void pokey_update(void); #endif @@ -118,6 +120,13 @@ UBYTE POKEY_poly9_lookup[511]; UBYTE POKEY_poly17_lookup[16385]; static ULONG random_scanline_counter; +/*It may change outside of POKEY_Scanline*/ +static int num_serout_ticks = 0; + +#ifndef SOUND_GAIN /* sound gain can be pre-defined in the configure/Makefile */ +#define SOUND_GAIN 4 +#endif + ULONG POKEY_GetRandomCounter(void) { return random_scanline_counter; @@ -128,6 +137,69 @@ void POKEY_SetRandomCounter(ULONG value) random_scanline_counter = value; } +void POKEY_Decrement_Counters() +{ + static unsigned int last_clock = 0; + int delta; + + /*Avoid division by zero*/ + if (ANTIC_CPU_CLOCK == last_clock) return; + + if (ANTIC_CPU_CLOCK > last_clock) + delta = ANTIC_CPU_CLOCK - last_clock; + else /*ANTIC_CPU_CLOCK has wrapped around*/ + delta = UINT_MAX - last_clock + ANTIC_CPU_CLOCK; + + last_clock = ANTIC_CPU_CLOCK; + +/*debug printf("CLOCK %d, last %d, delta %d\n", ANTIC_CPU_CLOCK, last_clock, delta);*/ + + if ((POKEY_DivNIRQ[POKEY_CHAN1] -= delta) < 0 ) { + /* Number of times that the clock ticked since last check time. */ + int mult = (- POKEY_DivNIRQ[POKEY_CHAN1] + delta - 1) / delta; + POKEY_DivNIRQ[POKEY_CHAN1] += mult * POKEY_DivNMax[POKEY_CHAN1]; + if (POKEY_IRQEN & 0x01) { + POKEY_IRQST &= 0xfe; + CPU_GenerateIRQ(); + } + } + + if ((POKEY_DivNIRQ[POKEY_CHAN2] -= delta) < 0 ) { + /* Number of times that the clock ticked since last check time. */ + int mult = (- POKEY_DivNIRQ[POKEY_CHAN2] + delta - 1) / delta; + POKEY_DivNIRQ[POKEY_CHAN2] += mult * POKEY_DivNMax[POKEY_CHAN2]; + if (mult & 1) + timer2_high = !timer2_high; + if (POKEY_IRQEN & 0x02) { + POKEY_IRQST &= 0xfd; + CPU_GenerateIRQ(); + } + /* Determine how many times timer2 switched from low to high. */ + if (timer2_is_serout_clock) + num_serout_ticks += mult / 2 + ((mult & 1) && timer2_high); + } + + /* When in asynchronous mode and waiting for start bit, timer 4 is + locked. ([AltRef] 47). */ + + if (!((POKEY_SKCTL & 0x10) && serin_wait_for_start_bit)) { + if ((POKEY_DivNIRQ[POKEY_CHAN4] -= delta) < 0 ) { + /* Number of times that the clock ticked since last check time. */ + int mult = (- POKEY_DivNIRQ[POKEY_CHAN4] + delta - 1) / delta; + POKEY_DivNIRQ[POKEY_CHAN4] += mult * POKEY_DivNMax[POKEY_CHAN4]; + if (mult & 1) + timer4_high = !timer4_high; + if (POKEY_IRQEN & 0x04) { + POKEY_IRQST &= 0xfb; + CPU_GenerateIRQ(); + } + /* Determine how many times timer4 switched from low to high. */ + if (timer4_is_serout_clock) + num_serout_ticks += mult / 2 + ((mult & 1) && timer4_high); + } + } +} + UBYTE POKEY_GetByte(UWORD addr, int no_side_effects) { UBYTE byte = 0xff; @@ -179,10 +251,19 @@ UBYTE POKEY_GetByte(UWORD addr, int no_side_effects) #endif break; case POKEY_OFFSET_IRQST: +#if HAVE_LIBA8CAS + /* When program checks state of IRQST it may wait for counter IRQ + Update Counters + Added for tape turbo systems (turbo 6000), + but probably it should be always active - when programs checks IRQST it always should be up to date */ + if (CASSETTE_turbo_state && CASSETTE_readable) { + POKEY_Decrement_Counters(); + } +#endif /*HAVE_LIBA8CAS*/ byte = POKEY_IRQST; break; case POKEY_OFFSET_SKSTAT: - byte = POKEY_SKSTAT + (CASSETTE_IOLineStatus() << 4); + byte = (POKEY_SKSTAT & 0xef) | (CASSETTE_IOLineStatus() << 4); #ifdef VOICEBOX if (VOICEBOX_enabled) { byte = POKEY_SKSTAT + (VOTRAXSND_busy << 4); @@ -205,10 +286,6 @@ static int POKEY_siocheck(void) && (POKEY_AUDCTL[0] & 0x28) == 0x28; } -#ifndef SOUND_GAIN /* sound gain can be pre-defined in the configure/Makefile */ -#define SOUND_GAIN 4 -#endif - #ifndef SOUND #define POKEYSND_Update(addr, val, chip, gain) #endif @@ -305,6 +382,14 @@ void POKEY_PutByte(UWORD addr, UBYTE byte) #endif break; case POKEY_OFFSET_STIMER: +#if HAVE_LIBA8CAS + /* Check timer IRQ before timers are reset + this is for sequence used by Turbo Blizzard: + STX STIMER + LDA IRQST + Probably should be here not only for A8CAS */ + POKEY_Decrement_Counters(); +#endif /* HAVE_LIBA8CAS*/ POKEY_DivNIRQ[POKEY_CHAN1] = POKEY_DivNMax[POKEY_CHAN1]; POKEY_DivNIRQ[POKEY_CHAN2] = POKEY_DivNMax[POKEY_CHAN2]; POKEY_DivNIRQ[POKEY_CHAN4] = POKEY_DivNMax[POKEY_CHAN4]; @@ -317,6 +402,12 @@ void POKEY_PutByte(UWORD addr, UBYTE byte) #ifdef VOICEBOX VOICEBOX_SKCTLPutByte(byte); #endif +#if HAVE_LIBA8CAS + if (!ESC_enable_sio_patch && CASSETTE_turbo_state && (CASSETTE_readable || CASSETTE_writable)) { + /* During turbo transmission save previous state first*/ + CASSETTE_AddScanLine(); + } +#endif /*HAVE LIBA8CAS*/ POKEY_SKCTL = byte; { /* Bits 5 & 6 choose the SEROUT clock. ([AltRef] 46) */ @@ -342,6 +433,7 @@ void POKEY_PutByte(UWORD addr, UBYTE byte) POKEY_DELAYED_SERIN_IRQ = 0; /* TODO some other registers probably also should also be reset. */ + } #ifdef DEBUG1 printf("WR: SKCTL = %x, PC = %x\n", byte, CPU_regPC); @@ -488,6 +580,11 @@ static void CassetteUpdateSerin(void) static int shift = 0; static int input_byte = 0; +#if HAVE_LIBA8CAS + /* in cassette turbo mode don't tamper with serin */ + /* But this probably is not right ? */ + if (CASSETTE_turbo_state) return; +#endif /* Log_print("length in ticks %i", length_in_ticks); Log_print("Clock: %d", ANTIC_CPU_CLOCK - prev_ticks);*/ @@ -526,6 +623,7 @@ static void CassetteUpdateSerin(void) return; } + /*************************************************************************** ** Generate POKEY Timer IRQs if required ** ** called on a per-scanline basis, not very precise, but good enough ** @@ -534,7 +632,6 @@ static void CassetteUpdateSerin(void) void POKEY_Scanline(void) { - int num_serout_ticks = 0; #ifdef POKEY_UPDATE pokey_update(); #endif @@ -556,50 +653,7 @@ void POKEY_Scanline(void) random_scanline_counter += ANTIC_LINE_C; - if ((POKEY_DivNIRQ[POKEY_CHAN1] -= ANTIC_LINE_C) < 0 ) { - /* Number of times that the clock ticked during this scanline. */ - int mult = (- POKEY_DivNIRQ[POKEY_CHAN1] + ANTIC_LINE_C - 1) / ANTIC_LINE_C; - POKEY_DivNIRQ[POKEY_CHAN1] += mult * POKEY_DivNMax[POKEY_CHAN1]; - if (POKEY_IRQEN & 0x01) { - POKEY_IRQST &= 0xfe; - CPU_GenerateIRQ(); - } - } - - if ((POKEY_DivNIRQ[POKEY_CHAN2] -= ANTIC_LINE_C) < 0 ) { - /* Number of times that the clock ticked during this scanline. */ - int mult = (- POKEY_DivNIRQ[POKEY_CHAN2] + ANTIC_LINE_C - 1) / ANTIC_LINE_C; - POKEY_DivNIRQ[POKEY_CHAN2] += mult * POKEY_DivNMax[POKEY_CHAN2]; - if (mult & 1) - timer2_high = !timer2_high; - if (POKEY_IRQEN & 0x02) { - POKEY_IRQST &= 0xfd; - CPU_GenerateIRQ(); - } - /* Determine how many times timer2 switched from low to high. */ - if (timer2_is_serout_clock) - num_serout_ticks += mult / 2 + ((mult & 1) && timer2_high); - } - - /* When in asynchronous mode and waiting for start bit, timer 4 is - locked. ([AltRef] 47). */ - - if (!((POKEY_SKCTL & 0x10) && serin_wait_for_start_bit)) { - if ((POKEY_DivNIRQ[POKEY_CHAN4] -= ANTIC_LINE_C) < 0 ) { - /* Number of times that the clock ticked during this scanline. */ - int mult = (- POKEY_DivNIRQ[POKEY_CHAN4] + ANTIC_LINE_C - 1) / ANTIC_LINE_C; - POKEY_DivNIRQ[POKEY_CHAN4] += mult * POKEY_DivNMax[POKEY_CHAN4]; - if (mult & 1) - timer4_high = !timer4_high; - if (POKEY_IRQEN & 0x04) { - POKEY_IRQST &= 0xfb; - CPU_GenerateIRQ(); - } - /* Determine how many times timer4 switched from low to high. */ - if (timer4_is_serout_clock) - num_serout_ticks += mult / 2 + ((mult & 1) && timer4_high); - } - } + POKEY_Decrement_Counters(); while (num_serout_ticks) { num_serout_ticks--; @@ -615,7 +669,7 @@ void POKEY_Scanline(void) serout_updated = 0; /* Disable XMTDONE IRQ and its status bit. */ POKEY_IRQST |= 0x08; - if ((~POKEY_IRQST & POKEY_IRQEN) == 0 && PBI_IRQ == 0) + if ((~POKEY_IRQST & POKEY_IRQEN) == 0 && PBI_IRQ == 0 && (PIA_PACTL & 0x81) != 0x81 && (PIA_PBCTL & 0x81) != 0x81) CPU_IRQ = 0; serout_in_transmission = serout; @@ -695,7 +749,7 @@ void POKEY_Scanline(void) #endif } } - + num_serout_ticks = 0; /* It may change before call to this function, so reset it now */ } /*****************************************************************************/ diff --git a/src/pokey.h b/src/pokey.h index fa529f8..f57c4fe 100644 --- a/src/pokey.h +++ b/src/pokey.h @@ -63,6 +63,7 @@ void POKEY_Frame(void); void POKEY_Scanline(void); void POKEY_StateSave(void); void POKEY_StateRead(void); +void POKEY_Decrement_Counters(void); #endif @@ -119,5 +120,4 @@ extern int POKEY_Base_mult[POKEY_MAXPOKEYS]; /* selects either 64Khz or 15Khz cl extern UBYTE POKEY_poly9_lookup[POKEY_POLY9_SIZE]; extern UBYTE POKEY_poly17_lookup[16385]; - #endif /* POKEY_H_ */ diff --git a/src/ui.c b/src/ui.c index d87e9c5..50a6308 100644 --- a/src/ui.c +++ b/src/ui.c @@ -1346,8 +1346,9 @@ static void MakeBlankTapeMenu(void) #if HAVE_LIBA8CAS static const UI_tMenuItem format_menu_array[] = { UI_MENU_ACTION(0, "CAS"), - UI_MENU_ACTION(1, "WAV"), - UI_MENU_ACTION(2, "raw file"), + UI_MENU_ACTION(1, "HEX"), + UI_MENU_ACTION(2, "WAV"), + UI_MENU_ACTION(3, "raw file"), UI_MENU_END }; #endif @@ -1371,14 +1372,24 @@ static void MakeBlankTapeMenu(void) if (!CASSETTE_CreateCAS(filenm, description)) CantSave(filenm); #if HAVE_LIBA8CAS + if (CASSETTE_turbo_type != CASSETTE_TURBO_NONE) + UI_driver->fMessage("Saving turbo to CAS doesn't work yet!", 1); break; - case 1: /* WAV */ + case 1: /* HEX */ + if (!CASSETTE_CreateHEX(filenm, description)) + CantSave(filenm); + if (CASSETTE_turbo_type != CASSETTE_TURBO_NONE) + UI_driver->fMessage("Saving turbo to HEX doesn't work yet!", 1); + break; + case 2: /* WAV */ if (!CASSETTE_CreateWAV(filenm)) CantSave(filenm); break; - case 2: /* raw file */ + case 3: /* raw file */ if (!CASSETTE_CreateRaw(filenm)) CantSave(filenm); + if (CASSETTE_turbo_type != CASSETTE_TURBO_NONE) + UI_driver->fMessage("Saving turbo to RAW doesn't work yet!", 1); break; default: break; @@ -1417,13 +1428,48 @@ static void TapeManagement(void) static const UI_tMenuItem turbo_type_menu_array[] = { UI_MENU_ACTION(0, "None"), UI_MENU_ACTION(1, "Atari Super Turbo (XC12)"), - UI_MENU_ACTION(2, "Blizzard"), - UI_MENU_ACTION(3, "Hard Turbo"), - UI_MENU_ACTION(4, "Manual switch"), - UI_MENU_ACTION(5, "Manual switch, reverse signal"), + UI_MENU_ACTION(2, "Atari Super Turbo (1010)"), + UI_MENU_ACTION(3, "Atari Turbo Tape (ATT)"), + UI_MENU_ACTION(4, "Unerring Masters (UM)"), + UI_MENU_ACTION(5, "Blizzard"), + UI_MENU_ACTION(6, "Hard Turbo"), + UI_MENU_ACTION(7, "Turbo ROM"), + UI_MENU_ACTION(8, "Turbo 2000 (joy port)"), + UI_MENU_ACTION(9, "Turbo 2000F"), + UI_MENU_ACTION(10, "Turbo 6000"), + UI_MENU_ACTION(11, "Czech. Turbo 2000 (A)"), + UI_MENU_ACTION(12, "Czech. Turbo 2000 (B)"), + UI_MENU_ACTION(13, "Tape-Wiz"), + UI_MENU_ACTION(14, "Czech. Super Turbo"), + UI_MENU_ACTION(15, "Auto Turbo"), + UI_MENU_ACTION(16, "Manual switch"), UI_MENU_END }; -#endif + + static char data_freq_string[11]; /* "Audio freq\0" */ + static const unsigned int freq_values[] = { + 0, /* Audio freq */ + 44100, + 48000, + 96000, + 192000, + 384000 + }; + static const UI_tMenuItem freq_menu_array[] = { +#ifdef SYNCHRONIZED_SOUND + UI_MENU_ACTION(0, "Audio frequency"), +#endif /* SYNCHRONIZED_SOUND */ + UI_MENU_ACTION(1, "44100 Hz (min)"), + UI_MENU_ACTION(2, "48000 Hz"), + UI_MENU_ACTION(3, "96000 Hz"), + UI_MENU_ACTION(4, "192000 Hz"), + UI_MENU_ACTION(5, "384000 Hz (max)"), + UI_MENU_ACTION(6, "custom"), + UI_MENU_END + }; +#endif /* HAVE_LIBA8CAS */ + + static UI_tMenuItem menu_array[] = { UI_MENU_FILESEL_PREFIX_TIP(0, cas_symbol, NULL, NULL), @@ -1433,15 +1479,20 @@ static void TapeManagement(void) UI_MENU_CHECK(2, "Record:"), UI_MENU_SUBMENU(3, "Make blank tape"), #if HAVE_LIBA8CAS + UI_MENU_SUBMENU_SUFFIX(4, " Data track sample rate:", data_freq_string), #ifdef SYNCHRONIZED_SOUND - UI_MENU_SUBMENU_SUFFIX(4, "Audio track volume:", volume_status), - UI_MENU_CHECK(5, " Mute:"), - UI_MENU_SUBMENU_SUFFIX(6, " Crosstalk from data track:", crosstalk_status), + UI_MENU_SUBMENU_SUFFIX(5, "Audio track volume:", volume_status), + UI_MENU_CHECK(6, " Mute:"), + UI_MENU_SUBMENU_SUFFIX(7, " Crosstalk from data track:", crosstalk_status), #endif /* SYNCHRONIZED_SOUND */ - UI_MENU_SUBMENU_SUFFIX(7, "Turbo system:", NULL), - UI_MENU_CHECK(8, "Turbo active:"), - UI_MENU_SUBMENU_SUFFIX(9, "Turbo error tolerance:", tolerance_status), -#endif + UI_MENU_SUBMENU_SUFFIX(8, "Turbo system:", NULL), + UI_MENU_CHECK(9, "Turbo active:"), + UI_MENU_SUBMENU_SUFFIX(10, "Turbo error tolerance:", tolerance_status), + UI_MENU_LABEL(""), + UI_MENU_LABEL("Advanced turbo options:"), + UI_MENU_CHECK(11," Invert polarity during read:"), + UI_MENU_CHECK(12," Invert polarity during write:"), +#endif /* HAVE_LIBA8CAS */ UI_MENU_END }; @@ -1479,16 +1530,22 @@ static void TapeManagement(void) #ifdef SYNCHRONIZED_SOUND snprintf(volume_status, sizeof(volume_status), "%d", CASSETTE_audio_volume); volume_status[sizeof(volume_status) - 1] = '\0'; - SetItemChecked(menu_array, 5, !CASSETTE_play_audio); + SetItemChecked(menu_array, 6, !CASSETTE_play_audio); snprintf(crosstalk_status, sizeof(crosstalk_status), "%d", CASSETTE_crosstalk_volume); crosstalk_status[sizeof(crosstalk_status) - 1] = '\0'; + if (CASSETTE_data_samplerate == Sound_desired.freq) + snprintf(data_freq_string, sizeof(data_freq_string), "%s", "Audio freq"); + else #endif /* SYNCHRONIZED_SOUND */ - FindMenuItem(menu_array, 7)->suffix = turbo_type_menu_array[CASSETTE_turbo_type].item; - - SetItemChecked(menu_array, 8, CASSETTE_turbo_state); + snprintf(data_freq_string, sizeof(data_freq_string), "%i Hz", CASSETTE_data_samplerate); + data_freq_string[sizeof(data_freq_string) -1] = '\0'; + FindMenuItem(menu_array, 8)->suffix = turbo_type_menu_array[CASSETTE_turbo_type].item; + SetItemChecked(menu_array, 9, CASSETTE_turbo_state); snprintf(tolerance_status, sizeof(tolerance_status), "%d", CASSETTE_turbo_tolerance); tolerance_status[sizeof(tolerance_status) - 1] = '\0'; -#endif + SetItemChecked(menu_array, 11, CASSETTE_turbo_invert_polarity_read); + SetItemChecked(menu_array, 12, CASSETTE_turbo_invert_polarity_write); +#endif /* HAVE_LIBA8CAS */ if (CASSETTE_status == CASSETTE_STATUS_NONE) memcpy(position_string, "N/A", 4); @@ -1497,7 +1554,7 @@ static void TapeManagement(void) char const *pos_suffix = (CASSETTE_position_in_blocks ? "blocks" : "secs"); #else static char const *pos_suffix = "blocks"; -#endif +#endif /* HAVE_LIBA8CAS */ if (position > size) snprintf(position_string, sizeof(position_string) - 1, "End/%u %s", size, pos_suffix); else @@ -1576,8 +1633,43 @@ static void TapeManagement(void) MakeBlankTapeMenu(); break; #if HAVE_LIBA8CAS + case 4: if (CASSETTE_status != CASSETTE_STATUS_NONE) + UI_driver->fMessage("Cassette must be ejected first", 1); + else + { + int option4; + int current; + for (current = 0; freq_menu_array[current].retval < 6; ++current) { + /* Find the currently-chosen frequency. */ #ifdef SYNCHRONIZED_SOUND - case 4: + if (CASSETTE_data_samplerate == Sound_desired.freq) + break; +#endif /* SYNCHRONIZED_SOUND */ + if (freq_values[freq_menu_array[current].retval] == CASSETTE_data_samplerate) + break; + } + option4 = UI_driver->fSelect(NULL, UI_SELECT_POPUP, current, freq_menu_array, NULL); + if (option4 == 6) { + snprintf(data_freq_string, sizeof(data_freq_string), "%u", CASSETTE_data_samplerate); /* Remove " Hz" suffix */ + if (UI_driver->fEditString("Enter data track sound frequency", data_freq_string, sizeof(data_freq_string)-3)) { + CASSETTE_data_samplerate = atoi(data_freq_string); + if (CASSETTE_data_samplerate < CASSETTE_DATA_SAMPLERATE_MIN) + CASSETTE_data_samplerate = CASSETTE_DATA_SAMPLERATE_MIN; + if (CASSETTE_data_samplerate > CASSETTE_DATA_SAMPLERATE_MAX) + CASSETTE_data_samplerate = CASSETTE_DATA_SAMPLERATE_MAX; + } + } + else if (option4 != -1) + CASSETTE_data_samplerate = freq_values[option4]; +#ifdef SYNCHRONIZED_SOUND + if (option4 == 0) { + CASSETTE_data_samplerate = Sound_desired.freq; + } +#endif /* SYNCHRONIZED_SOUND */ + } + break; +#ifdef SYNCHRONIZED_SOUND + case 5: switch (seltype) { case UI_USER_DELETE: /* Backspace */ CASSETTE_audio_volume = CASSETTE_DEFAULT_AUDIO_VOLUME; @@ -1586,10 +1678,10 @@ static void TapeManagement(void) CASSETTE_audio_volume = UI_driver->fSelectInt(CASSETTE_audio_volume, 0, CASSETTE_MAX_AUDIO_VOLUME); } break; - case 5: + case 6: CASSETTE_play_audio = !CASSETTE_play_audio; break; - case 6: + case 7: switch (seltype) { case UI_USER_DELETE: /* Backspace */ CASSETTE_crosstalk_volume = CASSETTE_DEFAULT_CROSSTALK_VOLUME; @@ -1600,21 +1692,23 @@ static void TapeManagement(void) } break; #endif /* SYNCHRONIZED_SOUND */ - case 7: { + case 8: { int option2 = UI_driver->fSelect(NULL, UI_SELECT_POPUP, CASSETTE_turbo_type, turbo_type_menu_array, NULL); if (option2 >= 0) CASSETTE_SetTurboType(option2); + if (option2 == 2) /* AST 1010 */ + UI_driver->fMessage("AST v. 1010 needs freq>=162000 Hz !", 1); break; } break; - case 8: - if (CASSETTE_turbo_type == CASSETTE_TURBO_MANUAL || CASSETTE_turbo_type == CASSETTE_TURBO_MANUAL_REV) + case 9: + if (CASSETTE_turbo_type == CASSETTE_TURBO_MANUAL) CASSETTE_ToggleTurboState(); else UI_driver->fMessage("This works only in manual turbo type", 1); break; - case 9: + case 10: switch (seltype) { case UI_USER_DELETE: /* Backspace */ CASSETTE_SetTurboTolerance(CASSETTE_DEFAULT_TURBO_TOLERANCE); @@ -1623,7 +1717,13 @@ static void TapeManagement(void) CASSETTE_SetTurboTolerance(UI_driver->fSelectInt(CASSETTE_turbo_tolerance, 0, CASSETTE_MAX_TURBO_TOLERANCE)); } break; -#endif + case 11: + CASSETTE_ToggleTurboPolarityRead(); + break; + case 12: + CASSETTE_ToggleTurboPolarityWrite(); + break; +#endif /* HAVE_LIBA8CAS */ default: return; }