Add Mindscape Music Board

Ported from PCem
This commit is contained in:
Jasmine Iwanek
2025-05-25 19:21:33 -04:00
parent fb8ef19c1e
commit 853830f8bb
9 changed files with 833 additions and 0 deletions

View File

@@ -0,0 +1,42 @@
/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Mindscape Music Board emulation.
*
* Authors: Roy Baer, <https://pcem-emulator.co.uk/>
* Jasmine Iwanek, <jriwanek@gmail.com>
*
* Copyright 2025 Roy Baer.
* Copyright 2025 Jasmine Iwanek.
*/
#ifndef _SOUND_SND_MMB_H_
#define _SOUND_SND_MMB_H_
#define MMB_FREQ FREQ_48000
/* NOTE:
* The constant clock rate is a deviation from the real hardware which has
* the design flaw that the clock rate is always half the ISA bus clock.
*/
#define MMB_CLOCK 2386364
typedef struct ay_3_891x_s {
uint8_t index;
uint8_t regs[16];
struct ayumi chip;
} ay_3_891x_t;
typedef struct mmb_s {
ay_3_891x_t first;
ay_3_891x_t second;
int16_t buffer[SOUNDBUFLEN * 2];
int pos;
} mmb_t;
#endif /* _SOUND_SND_MMB_H_ */

View File

@@ -205,6 +205,9 @@ extern const device_t ps1snd_device;
extern const device_t ssi2001_device;
extern const device_t entertainer_device;
/* Mindscape Music Board */
extern const device_t mmb_device;
/* Pro Audio Spectrum Plus, 16, and 16D */
extern const device_t pasplus_device;
extern const device_t pas16_device;

View File

@@ -41,6 +41,7 @@ add_library(snd OBJECT
snd_sb.c
snd_sb_dsp.c
snd_emu8k.c
snd_mmb.c
snd_mpu401.c
snd_pas16.c
snd_sn76489.c
@@ -177,6 +178,9 @@ if(MUNT)
endif()
endif()
add_subdirectory(ayumi)
target_link_libraries(86Box ayumi)
add_subdirectory(ymfm)
target_link_libraries(86Box ymfm)

View File

@@ -0,0 +1,14 @@
#
# 86Box A hypervisor and IBM PC system emulator that specializes in
# running old operating systems and software designed for IBM
# PC systems and compatibles from 1981 through fairly recent
# system designs based on the PCI bus.
#
# This file is part of the 86Box distribution.
#
# CMake build script.
#
add_library(ayumi STATIC
ayumi.c
)

21
src/sound/ayumi/LICENSE Normal file
View File

@@ -0,0 +1,21 @@
The MIT License
Copyright (c) Peter Sovietov, http://sovietov.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

338
src/sound/ayumi/ayumi.c Normal file
View File

@@ -0,0 +1,338 @@
/* Author: Peter Sovietov */
#include <string.h>
#include <math.h>
#include "ayumi.h"
static const double AY_dac_table[] = {
0.0, 0.0,
0.00999465934234, 0.00999465934234,
0.0144502937362, 0.0144502937362,
0.0210574502174, 0.0210574502174,
0.0307011520562, 0.0307011520562,
0.0455481803616, 0.0455481803616,
0.0644998855573, 0.0644998855573,
0.107362478065, 0.107362478065,
0.126588845655, 0.126588845655,
0.20498970016, 0.20498970016,
0.292210269322, 0.292210269322,
0.372838941024, 0.372838941024,
0.492530708782, 0.492530708782,
0.635324635691, 0.635324635691,
0.805584802014, 0.805584802014,
1.0, 1.0
};
static const double YM_dac_table[] = {
0.0, 0.0,
0.00465400167849, 0.00772106507973,
0.0109559777218, 0.0139620050355,
0.0169985503929, 0.0200198367285,
0.024368657969, 0.029694056611,
0.0350652323186, 0.0403906309606,
0.0485389486534, 0.0583352407111,
0.0680552376593, 0.0777752346075,
0.0925154497597, 0.111085679408,
0.129747463188, 0.148485542077,
0.17666895552, 0.211551079576,
0.246387426566, 0.281101701381,
0.333730067903, 0.400427252613,
0.467383840696, 0.53443198291,
0.635172045472, 0.75800717174,
0.879926756695, 1.0
};
static void reset_segment(struct ayumi* ay);
static int update_tone(struct ayumi* ay, int index) {
struct tone_channel* ch = &ay->channels[index];
ch->tone_counter += 1;
if (ch->tone_counter >= ch->tone_period) {
ch->tone_counter = 0;
ch->tone ^= 1;
}
return ch->tone;
}
static int update_noise(struct ayumi* ay) {
int bit0x3;
ay->noise_counter += 1;
if (ay->noise_counter >= (ay->noise_period << 1)) {
ay->noise_counter = 0;
bit0x3 = ((ay->noise ^ (ay->noise >> 3)) & 1);
ay->noise = (ay->noise >> 1) | (bit0x3 << 16);
}
return ay->noise & 1;
}
static void slide_up(struct ayumi* ay) {
ay->envelope += 1;
if (ay->envelope > 31) {
ay->envelope_segment ^= 1;
reset_segment(ay);
}
}
static void slide_down(struct ayumi* ay) {
ay->envelope -= 1;
if (ay->envelope < 0) {
ay->envelope_segment ^= 1;
reset_segment(ay);
}
}
static void hold_top(struct ayumi* ay) {
(void) ay;
}
static void hold_bottom(struct ayumi* ay) {
(void) ay;
}
static void (* const Envelopes[][2])(struct ayumi*) = {
{slide_down, hold_bottom},
{slide_down, hold_bottom},
{slide_down, hold_bottom},
{slide_down, hold_bottom},
{slide_up, hold_bottom},
{slide_up, hold_bottom},
{slide_up, hold_bottom},
{slide_up, hold_bottom},
{slide_down, slide_down},
{slide_down, hold_bottom},
{slide_down, slide_up},
{slide_down, hold_top},
{slide_up, slide_up},
{slide_up, hold_top},
{slide_up, slide_down},
{slide_up, hold_bottom}
};
static void reset_segment(struct ayumi* ay) {
if (Envelopes[ay->envelope_shape][ay->envelope_segment] == slide_down
|| Envelopes[ay->envelope_shape][ay->envelope_segment] == hold_top) {
ay->envelope = 31;
return;
}
ay->envelope = 0;
}
int update_envelope(struct ayumi* ay) {
ay->envelope_counter += 1;
if (ay->envelope_counter >= ay->envelope_period) {
ay->envelope_counter = 0;
Envelopes[ay->envelope_shape][ay->envelope_segment](ay);
}
return ay->envelope;
}
static void update_mixer(struct ayumi* ay) {
int i;
int out;
int noise = update_noise(ay);
int envelope = update_envelope(ay);
ay->left = 0;
ay->right = 0;
for (i = 0; i < TONE_CHANNELS; i += 1) {
out = (update_tone(ay, i) | ay->channels[i].t_off) & (noise | ay->channels[i].n_off);
out *= ay->channels[i].e_on ? envelope : ay->channels[i].volume * 2 + 1;
ay->left += ay->dac_table[out] * ay->channels[i].pan_left;
ay->right += ay->dac_table[out] * ay->channels[i].pan_right;
}
}
int ayumi_configure(struct ayumi* ay, int is_ym, double clock_rate, int sr) {
int i;
memset(ay, 0, sizeof(struct ayumi));
ay->step = clock_rate / (sr * 8 * DECIMATE_FACTOR);
ay->dac_table = is_ym ? YM_dac_table : AY_dac_table;
ay->noise = 1;
ayumi_set_envelope(ay, 1);
for (i = 0; i < TONE_CHANNELS; i += 1) {
ayumi_set_tone(ay, i, 1);
}
return ay->step < 1;
}
void ayumi_set_pan(struct ayumi* ay, int index, double pan, int is_eqp) {
if (is_eqp) {
ay->channels[index].pan_left = sqrt(1 - pan);
ay->channels[index].pan_right = sqrt(pan);
} else {
ay->channels[index].pan_left = 1 - pan;
ay->channels[index].pan_right = pan;
}
}
void ayumi_set_tone(struct ayumi* ay, int index, int period) {
period &= 0xfff;
ay->channels[index].tone_period = (period == 0) | period;
}
void ayumi_set_noise(struct ayumi* ay, int period) {
period &= 0x1f;
ay->noise_period = (period == 0) | period;
}
void ayumi_set_mixer(struct ayumi* ay, int index, int t_off, int n_off, int e_on) {
ay->channels[index].t_off = t_off & 1;
ay->channels[index].n_off = n_off & 1;
ay->channels[index].e_on = e_on;
}
void ayumi_set_volume(struct ayumi* ay, int index, int volume) {
ay->channels[index].volume = volume & 0xf;
}
void ayumi_set_envelope(struct ayumi* ay, int period) {
period &= 0xffff;
ay->envelope_period = (period == 0) | period;
}
void ayumi_set_envelope_shape(struct ayumi* ay, int shape) {
ay->envelope_shape = shape & 0xf;
ay->envelope_counter = 0;
ay->envelope_segment = 0;
reset_segment(ay);
}
static double decimate(double* x) {
double y = -0.0000046183113992051936 * (x[1] + x[191]) +
-0.00001117761640887225 * (x[2] + x[190]) +
-0.000018610264502005432 * (x[3] + x[189]) +
-0.000025134586135631012 * (x[4] + x[188]) +
-0.000028494281690666197 * (x[5] + x[187]) +
-0.000026396828793275159 * (x[6] + x[186]) +
-0.000017094212558802156 * (x[7] + x[185]) +
0.000023798193576966866 * (x[9] + x[183]) +
0.000051281160242202183 * (x[10] + x[182]) +
0.00007762197826243427 * (x[11] + x[181]) +
0.000096759426664120416 * (x[12] + x[180]) +
0.00010240229300393402 * (x[13] + x[179]) +
0.000089344614218077106 * (x[14] + x[178]) +
0.000054875700118949183 * (x[15] + x[177]) +
-0.000069839082210680165 * (x[17] + x[175]) +
-0.0001447966132360757 * (x[18] + x[174]) +
-0.00021158452917708308 * (x[19] + x[173]) +
-0.00025535069106550544 * (x[20] + x[172]) +
-0.00026228714374322104 * (x[21] + x[171]) +
-0.00022258805927027799 * (x[22] + x[170]) +
-0.00013323230495695704 * (x[23] + x[169]) +
0.00016182578767055206 * (x[25] + x[167]) +
0.00032846175385096581 * (x[26] + x[166]) +
0.00047045611576184863 * (x[27] + x[165]) +
0.00055713851457530944 * (x[28] + x[164]) +
0.00056212565121518726 * (x[29] + x[163]) +
0.00046901918553962478 * (x[30] + x[162]) +
0.00027624866838952986 * (x[31] + x[161]) +
-0.00032564179486838622 * (x[33] + x[159]) +
-0.00065182310286710388 * (x[34] + x[158]) +
-0.00092127787309319298 * (x[35] + x[157]) +
-0.0010772534348943575 * (x[36] + x[156]) +
-0.0010737727700273478 * (x[37] + x[155]) +
-0.00088556645390392634 * (x[38] + x[154]) +
-0.00051581896090765534 * (x[39] + x[153]) +
0.00059548767193795277 * (x[41] + x[151]) +
0.0011803558710661009 * (x[42] + x[150]) +
0.0016527320270369871 * (x[43] + x[149]) +
0.0019152679330965555 * (x[44] + x[148]) +
0.0018927324805381538 * (x[45] + x[147]) +
0.0015481870327877937 * (x[46] + x[146]) +
0.00089470695834941306 * (x[47] + x[145]) +
-0.0010178225878206125 * (x[49] + x[143]) +
-0.0020037400552054292 * (x[50] + x[142]) +
-0.0027874356824117317 * (x[51] + x[141]) +
-0.003210329988021943 * (x[52] + x[140]) +
-0.0031540624117984395 * (x[53] + x[139]) +
-0.0025657163651900345 * (x[54] + x[138]) +
-0.0014750752642111449 * (x[55] + x[137]) +
0.0016624165446378462 * (x[57] + x[135]) +
0.0032591192839069179 * (x[58] + x[134]) +
0.0045165685815867747 * (x[59] + x[133]) +
0.0051838984346123896 * (x[60] + x[132]) +
0.0050774264697459933 * (x[61] + x[131]) +
0.0041192521414141585 * (x[62] + x[130]) +
0.0023628575417966491 * (x[63] + x[129]) +
-0.0026543507866759182 * (x[65] + x[127]) +
-0.0051990251084333425 * (x[66] + x[126]) +
-0.0072020238234656924 * (x[67] + x[125]) +
-0.0082672928192007358 * (x[68] + x[124]) +
-0.0081033739572956287 * (x[69] + x[123]) +
-0.006583111539570221 * (x[70] + x[122]) +
-0.0037839040415292386 * (x[71] + x[121]) +
0.0042781252851152507 * (x[73] + x[119]) +
0.0084176358598320178 * (x[74] + x[118]) +
0.01172566057463055 * (x[75] + x[117]) +
0.013550476647788672 * (x[76] + x[116]) +
0.013388189369997496 * (x[77] + x[115]) +
0.010979501242341259 * (x[78] + x[114]) +
0.006381274941685413 * (x[79] + x[113]) +
-0.007421229604153888 * (x[81] + x[111]) +
-0.01486456304340213 * (x[82] + x[110]) +
-0.021143584622178104 * (x[83] + x[109]) +
-0.02504275058758609 * (x[84] + x[108]) +
-0.025473530942547201 * (x[85] + x[107]) +
-0.021627310017882196 * (x[86] + x[106]) +
-0.013104323383225543 * (x[87] + x[105]) +
0.017065133989980476 * (x[89] + x[103]) +
0.036978919264451952 * (x[90] + x[102]) +
0.05823318062093958 * (x[91] + x[101]) +
0.079072012081405949 * (x[92] + x[100]) +
0.097675998716952317 * (x[93] + x[99]) +
0.11236045936950932 * (x[94] + x[98]) +
0.12176343577287731 * (x[95] + x[97]) +
0.125 * x[96];
memcpy(&x[FIR_SIZE - DECIMATE_FACTOR], x, DECIMATE_FACTOR * sizeof(double));
return y;
}
void ayumi_process(struct ayumi* ay) {
int i;
double y1;
double* c_left = ay->interpolator_left.c;
double* y_left = ay->interpolator_left.y;
double* c_right = ay->interpolator_right.c;
double* y_right = ay->interpolator_right.y;
double* fir_left = &ay->fir_left[FIR_SIZE - ay->fir_index * DECIMATE_FACTOR];
double* fir_right = &ay->fir_right[FIR_SIZE - ay->fir_index * DECIMATE_FACTOR];
ay->fir_index = (ay->fir_index + 1) % (FIR_SIZE / DECIMATE_FACTOR - 1);
for (i = DECIMATE_FACTOR - 1; i >= 0; i -= 1) {
ay->x += ay->step;
if (ay->x >= 1) {
ay->x -= 1;
y_left[0] = y_left[1];
y_left[1] = y_left[2];
y_left[2] = y_left[3];
y_right[0] = y_right[1];
y_right[1] = y_right[2];
y_right[2] = y_right[3];
update_mixer(ay);
y_left[3] = ay->left;
y_right[3] = ay->right;
y1 = y_left[2] - y_left[0];
c_left[0] = 0.5 * y_left[1] + 0.25 * (y_left[0] + y_left[2]);
c_left[1] = 0.5 * y1;
c_left[2] = 0.25 * (y_left[3] - y_left[1] - y1);
y1 = y_right[2] - y_right[0];
c_right[0] = 0.5 * y_right[1] + 0.25 * (y_right[0] + y_right[2]);
c_right[1] = 0.5 * y1;
c_right[2] = 0.25 * (y_right[3] - y_right[1] - y1);
}
fir_left[i] = (c_left[2] * ay->x + c_left[1]) * ay->x + c_left[0];
fir_right[i] = (c_right[2] * ay->x + c_right[1]) * ay->x + c_right[0];
}
ay->left = decimate(fir_left);
ay->right = decimate(fir_right);
}
static double dc_filter(struct dc_filter* dc, int index, double x) {
dc->sum += -dc->delay[index] + x;
dc->delay[index] = x;
return x - dc->sum / DC_FILTER_SIZE;
}
void ayumi_remove_dc(struct ayumi* ay) {
ay->left = dc_filter(&ay->dc_left, ay->dc_index, ay->left);
ay->right = dc_filter(&ay->dc_right, ay->dc_index, ay->right);
ay->dc_index = (ay->dc_index + 1) & (DC_FILTER_SIZE - 1);
}

71
src/sound/ayumi/ayumi.h Normal file
View File

@@ -0,0 +1,71 @@
/* Author: Peter Sovietov */
#ifndef AYUMI_H
#define AYUMI_H
enum {
TONE_CHANNELS = 3,
DECIMATE_FACTOR = 8,
FIR_SIZE = 192,
DC_FILTER_SIZE = 1024
};
struct tone_channel {
int tone_period;
int tone_counter;
int tone;
int t_off;
int n_off;
int e_on;
int volume;
double pan_left;
double pan_right;
};
struct interpolator {
double c[4];
double y[4];
};
struct dc_filter {
double sum;
double delay[DC_FILTER_SIZE];
};
struct ayumi {
struct tone_channel channels[TONE_CHANNELS];
int noise_period;
int noise_counter;
int noise;
int envelope_counter;
int envelope_period;
int envelope_shape;
int envelope_segment;
int envelope;
const double* dac_table;
double step;
double x;
struct interpolator interpolator_left;
struct interpolator interpolator_right;
double fir_left[FIR_SIZE * 2];
double fir_right[FIR_SIZE * 2];
int fir_index;
struct dc_filter dc_left;
struct dc_filter dc_right;
int dc_index;
double left;
double right;
};
int ayumi_configure(struct ayumi* ay, int is_ym, double clock_rate, int sr);
void ayumi_set_pan(struct ayumi* ay, int index, double pan, int is_eqp);
void ayumi_set_tone(struct ayumi* ay, int index, int period);
void ayumi_set_noise(struct ayumi* ay, int period);
void ayumi_set_mixer(struct ayumi* ay, int index, int t_off, int n_off, int e_on);
void ayumi_set_volume(struct ayumi* ay, int index, int volume);
void ayumi_set_envelope(struct ayumi* ay, int period);
void ayumi_set_envelope_shape(struct ayumi* ay, int shape);
void ayumi_process(struct ayumi* ay);
void ayumi_remove_dc(struct ayumi* ay);
#endif

339
src/sound/snd_mmb.c Normal file
View File

@@ -0,0 +1,339 @@
/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Mindscape Music Board emulation.
*
* Authors: Roy Baer, <https://pcem-emulator.co.uk/>
* Jasmine Iwanek, <jriwanek@gmail.com>
*
* Copyright 2025 Roy Baer.
* Copyright 2025 Jasmine Iwanek.
*/
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define HAVE_STDARG_H
#include <86box/86box.h>
#include <86box/device.h>
#include <86box/io.h>
#include <86box/sound.h>
//#i nclude "cpu.h"
#include "ayumi/ayumi.h"
#include <86box/snd_mmb.h>
#include <86box/plat_unused.h>
#ifdef ENABLE_MMB_LOG
int mmb_do_log = ENABLE_MMB_LOG;
static void
mmb_log(const char *fmt, ...)
{
va_list ap;
if (mmb_do_log) {
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
# define mmb_log(fmt, ...)
#endif
void
mmb_update(mmb_t *mmb)
{
for (; mmb->pos < sound_pos_global; mmb->pos++) {
ayumi_process(&mmb->first.chip);
ayumi_process(&mmb->second.chip);
ayumi_remove_dc(&mmb->first.chip);
ayumi_remove_dc(&mmb->second.chip);
mmb->buffer[mmb->pos << 1] = (mmb->first.chip.left + mmb->second.chip.left) * 16000;
mmb->buffer[(mmb->pos << 1) + 1] = (mmb->first.chip.right + mmb->second.chip.right) * 16000;
}
}
void
mmb_get_buffer(int32_t *buffer, int len, void *priv)
{
mmb_t *mmb = (mmb_t *) priv;
mmb_update(mmb);
for (int c = 0; c < len * 2; c++)
buffer[c] += mmb->buffer[c];
mmb->pos = 0;
}
void
mmb_write(uint16_t addr, uint8_t val, void *priv)
{
mmb_t *mmb = (mmb_t *) priv;
mmb_update(mmb);
mmb_log("mmb_write(%04X): activity now: %02X\n", addr, val);
switch (addr & 3) {
case 0:
mmb->first.index = val;
break;
case 2:
mmb->second.index = val;
break;
case 1:
case 3:
{
ay_3_891x_t *ay = ((addr & 2) == 0) ? &mmb->first : &mmb->second;
switch (ay->index) {
case 0:
ay->regs[0] = val;
ayumi_set_tone(&ay->chip, 0, (ay->regs[1] << 8) | ay->regs[0]);
break;
case 1:
ay->regs[1] = val & 0xf;
ayumi_set_tone(&ay->chip, 0, (ay->regs[1] << 8) | ay->regs[0]);
break;
case 2:
ay->regs[2] = val;
ayumi_set_tone(&ay->chip, 1, (ay->regs[3] << 8) | ay->regs[2]);
break;
case 3:
ay->regs[3] = val & 0xf;
ayumi_set_tone(&ay->chip, 1, (ay->regs[3] << 8) | ay->regs[2]);
break;
case 4:
ay->regs[4] = val;
ayumi_set_tone(&ay->chip, 2, (ay->regs[5] << 8) | ay->regs[4]);
break;
case 5:
ay->regs[5] = val & 0xf;
ayumi_set_tone(&ay->chip, 2, (ay->regs[5] << 8) | ay->regs[4]);
break;
case 6:
ay->regs[6] = val & 0x1f;
ayumi_set_noise(&ay->chip, ay->regs[6]);
break;
case 7:
ay->regs[7] = val;
ayumi_set_mixer(&ay->chip, 0, val & 1, (val >> 3) & 1, (ay->regs[8] >> 4) & 1);
ayumi_set_mixer(&ay->chip, 1, (val >> 1) & 1, (val >> 4) & 1, (ay->regs[9] >> 4) & 1);
ayumi_set_mixer(&ay->chip, 2, (val >> 2) & 1, (val >> 5) & 1, (ay->regs[10] >> 4) & 1);
break;
case 8:
ay->regs[8] = val;
ayumi_set_volume(&ay->chip, 0, val & 0xf);
ayumi_set_mixer(&ay->chip, 0, ay->regs[7] & 1, (ay->regs[7] >> 3) & 1, (val >> 4) & 1);
break;
case 9:
ay->regs[9] = val;
ayumi_set_volume(&ay->chip, 1, val & 0xf);
ayumi_set_mixer(&ay->chip, 1, (ay->regs[7] >> 1) & 1, (ay->regs[7] >> 4) & 1, (val >> 4) & 1);
break;
case 10:
ay->regs[10] = val;
ayumi_set_volume(&ay->chip, 2, val & 0xf);
ayumi_set_mixer(&ay->chip, 2, (ay->regs[7] >> 2) & 1, (ay->regs[7] >> 5) & 1, (val >> 4) & 1);
break;
case 11:
ay->regs[11] = val;
ayumi_set_envelope(&ay->chip, (ay->regs[12] >> 8) | ay->regs[11]);
break;
case 12:
ay->regs[12] = val;
ayumi_set_envelope(&ay->chip, (ay->regs[12] >> 8) | ay->regs[11]);
break;
case 13:
ay->regs[13] = val;
ayumi_set_envelope_shape(&ay->chip, val & 0xf);
break;
case 14:
ay->regs[14] = val;
break;
case 15:
ay->regs[15] = val;
break;
default:
break;
}
break;
}
default:
break;
}
}
uint8_t
mmb_read(uint16_t addr, void *priv)
{
mmb_t *mmb = (mmb_t *) priv;
ay_3_891x_t *ay = ((addr & 2) == 0) ? &mmb->first : &mmb->second;
uint8_t ret = 0;
switch (ay->index) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
case 12:
case 13:
ret = ay->regs[ay->index];
break;
case 14:
if (ay->regs[7] & 0x40)
ret = ay->regs[14];
break;
case 15:
if (ay->regs[7] & 0x80)
ret = ay->regs[15];
break;
default:
break;
}
mmb_log("mmb_read(%04X): activity now: %02X\n", addr, ret);
return ret;
}
void *
mmb_init(UNUSED(const device_t *info))
{
mmb_t *mmb = calloc(1, sizeof(mmb_t));
# if 0
uint16_t addr = (device_get_config_int("addr96") << 6) | (device_get_config_int("addr52") << 2);
#else
uint16_t addr = 0x300;
#endif
sound_add_handler(mmb_get_buffer, mmb);
ayumi_configure(&mmb->first.chip, 0, MMB_CLOCK, MMB_FREQ);
ayumi_configure(&mmb->second.chip, 0, MMB_CLOCK, MMB_FREQ);
for (uint8_t i = 0; i < 3; i++) {
ayumi_set_pan(&mmb->first.chip, i, 0.5, 1);
ayumi_set_pan(&mmb->second.chip, i, 0.5, 1);
}
io_sethandler(addr, 0x0004,
mmb_read, NULL, NULL,
mmb_write, NULL, NULL,
mmb);
return mmb;
}
void
mmb_close(void *priv)
{
mmb_t *mmb = (mmb_t *) priv;
free(mmb);
}
// clang-format off
#if 0
static device_config_t mmb_config[] = {
{
.name = "addr96",
.description = "Base address A9...A6",
.type = CONFIG_SELECTION,
.default_string = NULL,
.default_int = 12,
.file_filter = NULL,
.spinner = { 0 },
.selection = {
{ .description = "0000", .value = 0 },
{ .description = "0001", .value = 1 },
{ .description = "0010", .value = 2 },
{ .description = "0011", .value = 3 },
{ .description = "0100", .value = 4 },
{ .description = "0101", .value = 5 },
{ .description = "0110", .value = 6 },
{ .description = "0111", .value = 7 },
{ .description = "1000", .value = 8 },
{ .description = "1001", .value = 9 },
{ .description = "1010", .value = 10 },
{ .description = "1011", .value = 11 },
{ .description = "1100", .value = 12 },
{ .description = "1101", .value = 13 },
{ .description = "1110", .value = 14 },
{ .description = "1111", .value = 15 },
{ .description = "" }
},
.bios = { { 0 } }
},
{
.name = "addr52",
.description = "Base address A5...A2",
.type = CONFIG_SELECTION,
.default_string = NULL,
.default_int = 0,
.file_filter = NULL,
.spinner = { 0 },
.selection = {
{ .description = "0000", .value = 0 },
{ .description = "0001", .value = 1 },
{ .description = "0010", .value = 2 },
{ .description = "0011", .value = 3 },
{ .description = "0100", .value = 4 },
{ .description = "0101", .value = 5 },
{ .description = "0110", .value = 6 },
{ .description = "0111", .value = 7 },
{ .description = "1000", .value = 8 },
{ .description = "1001", .value = 9 },
{ .description = "1010", .value = 10 },
{ .description = "1011", .value = 11 },
{ .description = "1100", .value = 12 },
{ .description = "1101", .value = 13 },
{ .description = "1110", .value = 14 },
{ .description = "1111", .value = 15 },
{ .description = "" }
},
.bios = { { 0 } }
},
{ .type = CONFIG_END }
};
#endif
// clang-format on
const device_t mmb_device = {
.name = "Mindscape Music Board",
.internal_name = "mmb",
.flags = DEVICE_ISA,
.local = 0,
.init = mmb_init,
.close = mmb_close,
.reset = NULL,
.available = NULL,
.speed_changed = NULL,
.force_redraw = NULL,
#if 0
.config = mmb_config
#else
.config = NULL
#endif
};

View File

@@ -137,6 +137,7 @@ static const SOUND_CARD sound_cards[] = {
{ &sb_vibra16xv_device },
{ &ssi2001_device },
{ &entertainer_device },
{ &mmb_device },
{ &pasplus_device },
{ &pas16_device },
{ &pas16d_device },