comparison sms.c @ 2528:90a40be940f7

Implement read-only SC-3000 cassette support
author Michael Pavone <pavone@retrodev.com>
date Mon, 25 Nov 2024 22:26:45 -0800
parents 8cf7cadc17ee
children a6687a6fb69d
comparison
equal deleted inserted replaced
2524:25e40370e0e4 2528:90a40be940f7
16 #define z80_handle_code_write(...) 16 #define z80_handle_code_write(...)
17 #else 17 #else
18 #define Z80_CYCLE current_cycle 18 #define Z80_CYCLE current_cycle
19 #define Z80_OPTS options 19 #define Z80_OPTS options
20 #endif 20 #endif
21
22 enum {
23 TAPE_NONE,
24 TAPE_STOPPED,
25 TAPE_PLAYING,
26 TAPE_RECORDING
27 };
21 28
22 static void *memory_io_write(uint32_t location, void *vcontext, uint8_t value) 29 static void *memory_io_write(uint32_t location, void *vcontext, uint8_t value)
23 { 30 {
24 z80_context *z80 = vcontext; 31 z80_context *z80 = vcontext;
25 sms_context *sms = z80->system; 32 sms_context *sms = z80->system;
132 sms_context *sms = ppi->system; 139 sms_context *sms = ppi->system;
133 sms->kb_mux = data & 0x7; 140 sms->kb_mux = data & 0x7;
134 } 141 }
135 } 142 }
136 143
144 static void cassette_run(sms_context *sms, uint32_t cycle)
145 {
146 if (!sms->cassette) {
147 return;
148 }
149 if (cycle > sms->cassette_cycle) {
150 uint64_t diff = cycle - sms->cassette_cycle;
151 diff *= sms->cassette_wave.sample_rate;
152 diff /= sms->normal_clock;
153 if (sms->cassette_state == TAPE_PLAYING) {
154 uint64_t bytes_per_sample = sms->cassette_wave.num_channels * sms->cassette_wave.bits_per_sample / 8;
155 uint64_t offset = diff * bytes_per_sample + sms->cassette_offset;
156 if (offset > UINT32_MAX || offset > sms->cassette->size - bytes_per_sample) {
157 sms->cassette_offset = sms->cassette->size - bytes_per_sample;
158 } else {
159 sms->cassette_offset = offset;
160 }
161 static uint32_t last_displayed_seconds;
162 uint32_t seconds = (sms->cassette_offset - (sms->cassette_wave.format_header.size + offsetof(wave_header, audio_format))) / (bytes_per_sample * sms->cassette_wave.sample_rate);
163 if (seconds != last_displayed_seconds) {
164 last_displayed_seconds = seconds;
165 printf("Cassette: %02d:%02d\n", seconds / 60, seconds % 60);
166 }
167 }
168 diff *= sms->normal_clock;
169 diff /= sms->cassette_wave.sample_rate;
170 sms->cassette_cycle += diff;
171 }
172 }
173
174 static uint8_t cassette_read(sms_context *sms, uint32_t cycle)
175 {
176 cassette_run(sms, cycle);
177 if (sms->cassette_state != TAPE_PLAYING) {
178 return 0;
179 }
180 int64_t sample = 0;
181 for (uint16_t i = 0; i < sms->cassette_wave.num_channels; i++)
182 {
183 if (sms->cassette_wave.audio_format == 3) {
184 if (sms->cassette_wave.bits_per_sample == 64) {
185 sample += 32767.0 * ((double *)(((char *)sms->cassette->buffer) + sms->cassette_offset))[i];
186 } else if (sms->cassette_wave.bits_per_sample == 32) {
187 sample += 32767.0f * ((float *)(((char *)sms->cassette->buffer) + sms->cassette_offset))[i];
188 }
189 } else if (sms->cassette_wave.audio_format == 1) {
190 if (sms->cassette_wave.bits_per_sample == 32) {
191 sample += ((int32_t *)(((char *)sms->cassette->buffer) + sms->cassette_offset))[i];
192 } else if (sms->cassette_wave.bits_per_sample == 16) {
193 sample += ((int16_t *)(((char *)sms->cassette->buffer) + sms->cassette_offset))[i];
194 } else if (sms->cassette_wave.bits_per_sample == 8) {
195 sample += ((uint8_t *)sms->cassette->buffer)[sms->cassette_offset + i] - 0x80;
196 }
197 }
198 }
199 uint32_t bytes_per_sample = sms->cassette_wave.num_channels * sms->cassette_wave.bits_per_sample / 8;
200 if (sms->cassette_offset == sms->cassette->size - bytes_per_sample) {
201 sms->cassette_state = TAPE_STOPPED;
202 puts("Cassette reached end of file, playback stoped");
203 }
204 return sample > 0 ? 0x80 : 0;
205 }
206
207 static void cassette_action(system_header *header, uint8_t action)
208 {
209 sms_context *sms = (sms_context*)header;
210 if (!sms->cassette) {
211 return;
212 }
213 cassette_run(sms, sms->z80->Z80_CYCLE);
214 switch(action)
215 {
216 case CASSETTE_PLAY:
217 sms->cassette_state = TAPE_PLAYING;
218 puts("Cassette playback started");
219 break;
220 case CASSETTE_RECORD:
221 break;
222 case CASSETTE_STOP:
223 sms->cassette_state = TAPE_STOPPED;
224 puts("Cassette playback stoped");
225 break;
226 case CASSETTE_REWIND:
227 sms->cassette_offset = sms->cassette_wave.format_header.size + offsetof(wave_header, audio_format);
228 break;
229 }
230 }
231
137 static uint8_t i8255_input_poll(i8255 *ppi, uint32_t cycle, uint32_t port) 232 static uint8_t i8255_input_poll(i8255 *ppi, uint32_t cycle, uint32_t port)
138 { 233 {
139 if (port > 1) { 234 if (port > 1) {
140 return 0xFF; 235 return 0xFF;
141 } 236 }
142 sms_context *sms = ppi->system; 237 sms_context *sms = ppi->system;
143 if (sms->kb_mux == 7) { 238 if (sms->kb_mux == 7) {
144 if (port) { 239 if (port) {
145 //TODO: cassette-in
146 //TODO: printer port BUSY/FAULT 240 //TODO: printer port BUSY/FAULT
147 uint8_t port_b = io_data_read(sms->io.ports+1, cycle); 241 uint8_t port_b = io_data_read(sms->io.ports+1, cycle);
148 return (port_b >> 2 & 0xF) | 0x10; 242 return (port_b >> 2 & 0xF) | 0x10 | cassette_read(sms, cycle);
149 } else { 243 } else {
150 uint8_t port_a = io_data_read(sms->io.ports, cycle); 244 uint8_t port_a = io_data_read(sms->io.ports, cycle);
151 uint8_t port_b = io_data_read(sms->io.ports+1, cycle); 245 uint8_t port_b = io_data_read(sms->io.ports+1, cycle);
152 return (port_a & 0x3F) | (port_b << 6); 246 return (port_a & 0x3F) | (port_b << 6);
153 } 247 }
154 } 248 }
155 //TODO: keyboard matrix ghosting 249 //TODO: keyboard matrix ghosting
156 if (port) { 250 if (port) {
157 //TODO: cassette-in
158 //TODO: printer port BUSY/FAULT 251 //TODO: printer port BUSY/FAULT
159 return (sms->keystate[sms->kb_mux] >> 8) | 0x10; 252 return (sms->keystate[sms->kb_mux] >> 8) | 0x10 | cassette_read(sms, cycle);
160 } 253 }
161 return sms->keystate[sms->kb_mux]; 254 return sms->keystate[sms->kb_mux];
162 } 255 }
163 256
164 static void update_mem_map(uint32_t location, sms_context *sms, uint8_t value) 257 static void update_mem_map(uint32_t location, sms_context *sms, uint8_t value)
602 z80_clear_reset(sms->z80, sms->z80->Z80_CYCLE + 128*15); 695 z80_clear_reset(sms->z80, sms->z80->Z80_CYCLE + 128*15);
603 } 696 }
604 target_cycle = sms->z80->Z80_CYCLE; 697 target_cycle = sms->z80->Z80_CYCLE;
605 vdp_run_context(sms->vdp, target_cycle); 698 vdp_run_context(sms->vdp, target_cycle);
606 psg_run(sms->psg, target_cycle); 699 psg_run(sms->psg, target_cycle);
700 cassette_run(sms, target_cycle);
607 701
608 if (system->save_state) { 702 if (system->save_state) {
609 while (!sms->z80->pc) { 703 while (!sms->z80->pc) {
610 //advance Z80 to an instruction boundary 704 //advance Z80 to an instruction boundary
611 z80_run(sms->z80, sms->z80->Z80_CYCLE + 1); 705 z80_run(sms->z80, sms->z80->Z80_CYCLE + 1);
620 io_adjust_cycles(sms->io.ports, sms->z80->Z80_CYCLE, adjust); 714 io_adjust_cycles(sms->io.ports, sms->z80->Z80_CYCLE, adjust);
621 io_adjust_cycles(sms->io.ports+1, sms->z80->Z80_CYCLE, adjust); 715 io_adjust_cycles(sms->io.ports+1, sms->z80->Z80_CYCLE, adjust);
622 z80_adjust_cycles(sms->z80, adjust); 716 z80_adjust_cycles(sms->z80, adjust);
623 vdp_adjust_cycles(sms->vdp, adjust); 717 vdp_adjust_cycles(sms->vdp, adjust);
624 sms->psg->cycles -= adjust; 718 sms->psg->cycles -= adjust;
719 sms->cassette_cycle -= adjust;
625 target_cycle -= adjust; 720 target_cycle -= adjust;
626 } 721 }
627 } 722 }
628 if (sms->header.force_release || render_should_release_on_exit()) { 723 if (sms->header.force_release || render_should_release_on_exit()) {
629 bindings_release_capture(); 724 bindings_release_capture();
884 } 979 }
885 } 980 }
886 #endif 981 #endif
887 } 982 }
888 983
984 void load_cassette(sms_context *sms, system_media *media)
985 {
986 sms->cassette = NULL;
987 sms->cassette_state = TAPE_NONE;
988 memcpy(&sms->cassette_wave, media->buffer, offsetof(wave_header, data_header));
989 if (memcmp(sms->cassette_wave.chunk.format, "WAVE", 4)) {
990 return;
991 }
992 if (sms->cassette_wave.chunk.size < offsetof(wave_header, data_header)) {
993 return;
994 }
995 if (memcmp(sms->cassette_wave.format_header.id, "fmt ", 4)) {
996 return;
997 }
998 if (sms->cassette_wave.format_header.size < offsetof(wave_header, data_header) - offsetof(wave_header, audio_format)) {
999 return;
1000 }
1001 if (sms->cassette_wave.bits_per_sample != 8 && sms->cassette_wave.bits_per_sample != 16) {
1002 return;
1003 }
1004 uint32_t data_sub_chunk = sms->cassette_wave.format_header.size + offsetof(wave_header, audio_format);
1005 if (data_sub_chunk > media->size || media->size - data_sub_chunk < sizeof(riff_sub_chunk)) {
1006 return;
1007 }
1008 memcpy(&sms->cassette_wave.data_header, ((uint8_t *)media->buffer) + data_sub_chunk, sizeof(riff_sub_chunk));
1009 sms->cassette_state = TAPE_STOPPED;
1010 sms->cassette_offset = data_sub_chunk;
1011 sms->cassette = media;
1012 }
1013
889 sms_context *alloc_configure_sms(system_media *media, uint32_t opts, uint8_t force_region) 1014 sms_context *alloc_configure_sms(system_media *media, uint32_t opts, uint8_t force_region)
890 { 1015 {
891 sms_context *sms = calloc(1, sizeof(sms_context)); 1016 sms_context *sms = calloc(1, sizeof(sms_context));
892 tern_node *rom_db = get_rom_db(); 1017 tern_node *rom_db = get_rom_db();
893 const memmap_chunk base_map[] = { 1018 const memmap_chunk base_map[] = {
945 if (is_gamegear) { 1070 if (is_gamegear) {
946 init_z80_opts(zopts, sms->header.info.map, sms->header.info.map_chunks, io_gg, 6, 15, 0xFF); 1071 init_z80_opts(zopts, sms->header.info.map, sms->header.info.map_chunks, io_gg, 6, 15, 0xFF);
947 sms->start_button_region = 0xC0; 1072 sms->start_button_region = 0xC0;
948 } else if (is_sc3000) { 1073 } else if (is_sc3000) {
949 sms->keystate = calloc(sizeof(uint16_t), 7); 1074 sms->keystate = calloc(sizeof(uint16_t), 7);
950 memset(sms->keystate, 0xFF, sizeof(uint16_t) * 7); 1075 for (int i = 0; i < 7; i++)
1076 {
1077 sms->keystate[i] = 0xFFF;
1078 }
951 sms->i8255 = calloc(1, sizeof(i8255)); 1079 sms->i8255 = calloc(1, sizeof(i8255));
952 i8255_init(sms->i8255, i8255_output_updated, i8255_input_poll); 1080 i8255_init(sms->i8255, i8255_output_updated, i8255_input_poll);
953 sms->i8255->system = sms; 1081 sms->i8255->system = sms;
954 sms->kb_mux = 7; 1082 sms->kb_mux = 7;
955 init_z80_opts(zopts, sms->header.info.map, sms->header.info.map_chunks, io_sc, 7, 15, 0xFF); 1083 init_z80_opts(zopts, sms->header.info.map, sms->header.info.map_chunks, io_sc, 7, 15, 0xFF);
956 } else { 1084 } else {
957 init_z80_opts(zopts, sms->header.info.map, sms->header.info.map_chunks, io_map, 4, 15, 0xFF); 1085 init_z80_opts(zopts, sms->header.info.map, sms->header.info.map_chunks, io_map, 4, 15, 0xFF);
1086 }
1087 if (is_sc3000 && media->chain) {
1088 load_cassette(sms, media->chain);
958 } 1089 }
959 sms->z80 = init_z80_context(zopts); 1090 sms->z80 = init_z80_context(zopts);
960 sms->z80->system = sms; 1091 sms->z80->system = sms;
961 sms->z80->Z80_OPTS->gen.debug_cmd_handler = debug_commands; 1092 sms->z80->Z80_OPTS->gen.debug_cmd_handler = debug_commands;
962 1093
1017 sms->header.keyboard_up = keyboard_up; 1148 sms->header.keyboard_up = keyboard_up;
1018 sms->header.config_updated = config_updated; 1149 sms->header.config_updated = config_updated;
1019 sms->header.serialize = serialize; 1150 sms->header.serialize = serialize;
1020 sms->header.deserialize = deserialize; 1151 sms->header.deserialize = deserialize;
1021 sms->header.toggle_debug_view = toggle_debug_view; 1152 sms->header.toggle_debug_view = toggle_debug_view;
1153 sms->header.cassette_action = cassette_action;
1022 sms->header.type = SYSTEM_SMS; 1154 sms->header.type = SYSTEM_SMS;
1023 1155
1024 return sms; 1156 return sms;
1025 } 1157 }