#include #include #include #include #include #include #define HAVE_STDARG_H #include <86box/86box.h> #include <86box/device.h> #include <86box/io.h> #include "saasound/SAASound.h" #include <86box/snd_cms.h> #include <86box/sound.h> #include <86box/plat_unused.h> void cms_update(cms_t *cms) { if (cms->pos < wavetable_pos_global) { SAASNDGenerateMany(cms->saasound, (unsigned char*)&cms->buffer[cms->pos], wavetable_pos_global - cms->pos); cms->pos = wavetable_pos_global; } if (cms->pos2 < wavetable_pos_global) { SAASNDGenerateMany(cms->saasound2, (unsigned char*)&cms->buffer2[cms->pos2], wavetable_pos_global - cms->pos2); cms->pos2 = wavetable_pos_global; } } void cms_get_buffer(int32_t *buffer, int len, void *priv) { cms_t *cms = (cms_t *) priv; cms_update(cms); for (int c = 0; c < len * 2; c++) buffer[c] += (cms->buffer[c] / 2); cms->pos = 0; } void cms_get_buffer_2(int32_t *buffer, int len, void *priv) { cms_t *cms = (cms_t *) priv; cms_update(cms); for (int c = 0; c < len * 2; c++) buffer[c] += (cms->buffer2[c] / 2); cms->pos2 = 0; } void cms_write(uint16_t addr, uint8_t val, void *priv) { cms_t *cms = (cms_t *) priv; switch (addr & 0xf) { case 0x1: /* SAA #1 Register Select Port */ SAASNDWriteAddress(cms->saasound, val & 31); break; case 0x3: /* SAA #2 Register Select Port */ SAASNDWriteAddress(cms->saasound2, val & 31); break; case 0x0: /* SAA #1 Data Port */ cms_update(cms); SAASNDWriteData(cms->saasound, val); break; case 0x2: /* SAA #2 Data Port */ cms_update(cms); SAASNDWriteData(cms->saasound2, val); break; case 0x6: /* GameBlaster Write Port */ case 0x7: /* GameBlaster Write Port */ cms->latched_data = val; break; default: break; } } uint8_t cms_read(uint16_t addr, void *priv) { const cms_t *cms = (cms_t *) priv; switch (addr & 0xf) { case 0x1: /* SAA #1 Register Select Port */ return SAASNDReadAddress(cms->saasound); case 0x3: /* SAA #2 Register Select Port */ return SAASNDReadAddress(cms->saasound2); case 0x4: /* GameBlaster Read port (Always returns 0x7F) */ return 0x7f; case 0xa: /* GameBlaster Read Port */ case 0xb: /* GameBlaster Read Port */ return cms->latched_data; default: break; } return 0xff; } void * cms_init(UNUSED(const device_t *info)) { cms_t *cms = calloc(1, sizeof(cms_t)); uint16_t addr = device_get_config_hex16("base"); io_sethandler(addr, 0x0010, cms_read, NULL, NULL, cms_write, NULL, NULL, cms); cms->saasound = newSAASND(); SAASNDSetSoundParameters(cms->saasound, SAAP_44100 | SAAP_16BIT | SAAP_NOFILTER | SAAP_STEREO); cms->saasound2 = newSAASND(); SAASNDSetSoundParameters(cms->saasound2, SAAP_44100 | SAAP_16BIT | SAAP_NOFILTER | SAAP_STEREO); wavetable_add_handler(cms_get_buffer, cms); wavetable_add_handler(cms_get_buffer_2, cms); return cms; } void cms_close(void *priv) { cms_t *cms = (cms_t *) priv; deleteSAASND(cms->saasound); deleteSAASND(cms->saasound2); free(cms); } static const device_config_t cms_config[] = { // clang-format off { .name = "base", .description = "Address", .type = CONFIG_HEX16, .default_string = NULL, .default_int = 0x220, .file_filter = NULL, .spinner = { 0 }, .selection = { { .description = "0x210", .value = 0x210 }, { .description = "0x220", .value = 0x220 }, { .description = "0x230", .value = 0x230 }, { .description = "0x240", .value = 0x240 }, { .description = "0x250", .value = 0x250 }, { .description = "0x260", .value = 0x260 }, { .description = "" } }, .bios = { { 0 } } }, { .name = "", .description = "", .type = CONFIG_END } // clang-format on }; const device_t cms_device = { .name = "Creative Music System / Game Blaster", .internal_name = "cms", .flags = DEVICE_ISA, .local = 0, .init = cms_init, .close = cms_close, .reset = NULL, .available = NULL, .speed_changed = NULL, .force_redraw = NULL, .config = cms_config };