From 37eb2cb35944b59f58a3649c9715b79705584da7 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Sat, 27 Sep 2025 16:31:11 -0300 Subject: [PATCH 1/8] AD1848: Use the correct algorithm for CS423x ADPCM Still has level and DC offset issues, but actual use cases are lacking (all validation was done with ALSA's adpcm conversion plugin) --- src/include/86box/snd_ad1848.h | 6 ++-- src/sound/snd_ad1848.c | 66 +++++++++++++++++++++++----------- 2 files changed, 48 insertions(+), 24 deletions(-) diff --git a/src/include/86box/snd_ad1848.h b/src/include/86box/snd_ad1848.h index 4ad6f1709..435b2aac4 100644 --- a/src/include/86box/snd_ad1848.h +++ b/src/include/86box/snd_ad1848.h @@ -66,10 +66,10 @@ typedef struct ad1848_t { uint8_t enable : 1; uint8_t irq : 4; uint8_t dma : 3; - uint8_t adpcm_ref; - int8_t adpcm_step; + int adpcm_predictor[2]; + int16_t adpcm_step_index[2]; int freq; - int adpcm_data; + uint8_t adpcm_data; int adpcm_pos; uint8_t dma_ff; diff --git a/src/sound/snd_ad1848.c b/src/sound/snd_ad1848.c index 94520070e..6c32b1892 100644 --- a/src/sound/snd_ad1848.c +++ b/src/sound/snd_ad1848.c @@ -56,9 +56,22 @@ ad1848_log(const char *fmt, ...) static int ad1848_vols_7bits[128]; static double ad1848_vols_5bits_aux_gain[32]; -/* Borrowed from snd_sb_dsp */ -extern int8_t scaleMap4[64]; -extern uint8_t adjustMap4[64]; +/* Borrowed from ffmpeg. */ +static const int8_t adpcm_index_table[16] = { + -1, -1, -1, -1, 2, 4, 6, 8, + -1, -1, -1, -1, 2, 4, 6, 8 +}; +static const int16_t adpcm_step_table[89] = { + 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, + 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, + 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, + 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, + 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, + 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, + 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 +}; void ad1848_setirq(ad1848_t *ad1848, int irq) @@ -311,6 +324,8 @@ ad1848_write(uint16_t addr, uint8_t val, void *priv) case 9: if (!ad1848->enable && (val & 0x41) == 0x01) { ad1848->adpcm_pos = 0; + ad1848->adpcm_predictor[0] = ad1848->adpcm_predictor[1] = 0; + ad1848->adpcm_step_index[0] = ad1848->adpcm_step_index[1] = 0; ad1848->dma_ff = 0; if (ad1848->timer_latch) timer_set_delay_u64(&ad1848->timer_count, ad1848->timer_latch); @@ -358,6 +373,11 @@ ad1848_write(uint16_t addr, uint8_t val, void *priv) } break; + case 17: + if (val & 0x08) + ad1848->adpcm_predictor[0] = ad1848->adpcm_predictor[1] = 0; + break; + case 18 ... 19: if (ad1848->type >= AD1848_TYPE_CS4236B) { if (ad1848->type >= AD1848_TYPE_CS4235) { @@ -622,29 +642,33 @@ ad1848_dma_channel_read(ad1848_t *ad1848, int channel) } static int16_t -ad1848_process_adpcm(ad1848_t *ad1848) +ad1848_process_adpcm(ad1848_t *ad1848, int channel) { int temp; if (ad1848->adpcm_pos++ & 1) { - temp = (ad1848->adpcm_data & 0x0f) + ad1848->adpcm_step; + temp = ad1848->adpcm_data >> 4; } else { - ad1848->adpcm_data = (int) (ad1848_dma_channel_read(ad1848, ad1848->dma) & 0xffff); - temp = (ad1848->adpcm_data >> 4) + ad1848->adpcm_step; + ad1848->adpcm_data = ad1848_dma_channel_read(ad1848, ad1848->dma); + temp = ad1848->adpcm_data & 0x0f; } - if (temp < 0) - temp = 0; - else if (temp > 63) - temp = 63; - ad1848->adpcm_ref += scaleMap4[temp]; - if (ad1848->adpcm_ref > 0xff) - ad1848->adpcm_ref = 0xff; - else if (ad1848->adpcm_ref < 0x00) - ad1848->adpcm_ref = 0x00; + int step = adpcm_step_table[ad1848->adpcm_step_index[channel]]; + int step_index = ad1848->adpcm_step_index[channel] + adpcm_index_table[temp]; + if (step_index < 0) + step_index = 0; + else if (step_index > 88) + step_index = 88; - ad1848->adpcm_step = (int8_t) ((ad1848->adpcm_step + adjustMap4[temp]) & 0xff); + int diff = ((2 * (temp & 7) + 1) * step) >> 3; + int predictor = ad1848->adpcm_predictor[channel] + ((temp & 8) ? -diff : diff); + if (predictor < -32768) + predictor = -32768; + else if (predictor > 32767) + predictor = 32767; + ad1848->adpcm_predictor[channel] = predictor; + ad1848->adpcm_step_index[channel] = step_index; - return (int16_t) ((ad1848->adpcm_ref ^ 0x80) << 8); + return (int16_t) predictor; } static void @@ -705,12 +729,12 @@ ad1848_poll(void *priv) /* 0x80 and 0x90 reserved */ case 0xa0: /* Mono, 4-bit ADPCM */ - ad1848->out_l = ad1848->out_r = ad1848_process_adpcm(ad1848); + ad1848->out_l = ad1848->out_r = ad1848_process_adpcm(ad1848, 0); break; case 0xb0: /* Stereo, 4-bit ADPCM */ - ad1848->out_l = ad1848_process_adpcm(ad1848); - ad1848->out_r = ad1848_process_adpcm(ad1848); + ad1848->out_l = ad1848_process_adpcm(ad1848, 0); + ad1848->out_r = ad1848_process_adpcm(ad1848, 1); break; case 0xc0: /* Mono, 16-bit PCM big endian */ From 4a6327da68f575d556ca29f7916cc2aa4333b76c Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Sun, 28 Sep 2025 01:58:07 +0600 Subject: [PATCH 2/8] S3 ViRGE: Move sign bit 1 bit further to the right for K2 scaler registers (#6243) * Fix sign position of DDA accumulator registers * S3 ViRGE: Move sign bit 1 bit further to the right for K1/K2 scaler registers * K1 scales are 13 bits --- src/video/vid_s3_virge.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/video/vid_s3_virge.c b/src/video/vid_s3_virge.c index ac96aedef..8a139793c 100644 --- a/src/video/vid_s3_virge.c +++ b/src/video/vid_s3_virge.c @@ -1967,9 +1967,9 @@ s3_virge_mmio_write_l(uint32_t addr, uint32_t val, void *priv) if (val & 0x800) virge->streams.k1_horiz_scale |= ~0x7ff; - virge->streams.k2_horiz_scale = (val >> 16) & 0x7ff; - if ((val >> 16) & 0x800) - virge->streams.k2_horiz_scale |= ~0x7ff; + virge->streams.k2_horiz_scale = (val >> 16) & 0x3ff; + if ((val >> 16) & 0x400) + virge->streams.k2_horiz_scale |= ~0x3ff; svga_recalctimings(svga); svga->fullchange = changeframecount; @@ -2025,9 +2025,9 @@ s3_virge_mmio_write_l(uint32_t addr, uint32_t val, void *priv) virge->streams.k1_vert_scale |= ~0x7ff; break; case 0x81e4: - virge->streams.k2_vert_scale = val & 0x7ff; - if (val & 0x800) - virge->streams.k2_vert_scale |= ~0x7ff; + virge->streams.k2_vert_scale = val & 0x3ff; + if (val & 0x400) + virge->streams.k2_vert_scale |= ~0x3ff; break; case 0x81e8: virge->streams.dda_vert_accumulator = val & 0x7ff; From 6ca6afedd55c4df7c4963c32393e3a3ac2eaab35 Mon Sep 17 00:00:00 2001 From: OBattler Date: Sun, 28 Sep 2025 00:44:01 +0200 Subject: [PATCH 3/8] AdLib Gold: Everything now outputs to the same 48k source in order to avoid noise caused by the YM7128's resampling which is currently tailored to that frequency. --- src/include/86box/filters.h | 142 +++++++++++++------------- src/include/86box/snd_opl.h | 5 + src/include/86box/snd_opl_nuked.h | 4 +- src/include/86box/snd_ym7128.h | 12 +-- src/sound/snd_adlibgold.c | 160 ++++++------------------------ src/sound/snd_opl.c | 70 +++++++------ src/sound/snd_opl_nuked.c | 108 ++++++++++++++------ src/sound/snd_opl_ymfm.cpp | 87 ++++++++-------- src/sound/snd_ym7128.c | 24 ++--- 9 files changed, 291 insertions(+), 321 deletions(-) diff --git a/src/include/86box/filters.h b/src/include/86box/filters.h index 3e20fa653..572137ef0 100644 --- a/src/include/86box/filters.h +++ b/src/include/86box/filters.h @@ -5,7 +5,7 @@ /* fc=150Hz */ static inline float -adgold_highpass_iir(int c, int i, float NewSample) +adgold_highpass_iir(int i, float NewSample) { float ACoef[NCoef + 1] = { 0.98657437157334349000, @@ -19,76 +19,6 @@ adgold_highpass_iir(int c, int i, float NewSample) 0.97261396931534050000 }; - static float y[2][2][NCoef + 1]; /* output samples */ - static float x[2][2][NCoef + 1]; /* input samples */ - int n; - - /* shift the old samples */ - for (n = NCoef; n > 0; n--) { - x[c][i][n] = x[c][i][n - 1]; - y[c][i][n] = y[c][i][n - 1]; - } - - /* Calculate the new output */ - x[c][i][0] = NewSample; - y[c][i][0] = ACoef[0] * x[c][i][0]; - for (n = 1; n <= NCoef; n++) - y[c][i][0] += ACoef[n] * x[c][i][n] - BCoef[n] * y[c][i][n]; - - return y[c][i][0]; -} - -/* fc=150Hz */ -static inline float -adgold_lowpass_iir(int c, int i, float NewSample) -{ - float ACoef[NCoef + 1] = { - 0.00009159473951071446, - 0.00018318947902142891, - 0.00009159473951071446 - }; - - float BCoef[NCoef + 1] = { - 1.00000000000000000000, - -1.97223372919526560000, - 0.97261396931306277000 - }; - - static float y[2][2][NCoef + 1]; /* output samples */ - static float x[2][2][NCoef + 1]; /* input samples */ - int n; - - /* shift the old samples */ - for (n = NCoef; n > 0; n--) { - x[c][i][n] = x[c][i][n - 1]; - y[c][i][n] = y[c][i][n - 1]; - } - - /* Calculate the new output */ - x[c][i][0] = NewSample; - y[c][i][0] = ACoef[0] * x[c][i][0]; - for (n = 1; n <= NCoef; n++) - y[c][i][0] += ACoef[n] * x[c][i][n] - BCoef[n] * y[c][i][n]; - - return y[c][i][0]; -} - -/* fc=56Hz */ -static inline float -adgold_pseudo_stereo_iir(int i, float NewSample) -{ - float ACoef[NCoef + 1] = { - 0.00001409030866231767, - 0.00002818061732463533, - 0.00001409030866231767 - }; - - float BCoef[NCoef + 1] = { - 1.00000000000000000000, - -1.98733021473466760000, - 0.98738361004063568000 - }; - static float y[2][NCoef + 1]; /* output samples */ static float x[2][NCoef + 1]; /* input samples */ int n; @@ -108,6 +38,76 @@ adgold_pseudo_stereo_iir(int i, float NewSample) return y[i][0]; } +/* fc=150Hz */ +static inline float +adgold_lowpass_iir(int i, float NewSample) +{ + float ACoef[NCoef + 1] = { + 0.00009159473951071446, + 0.00018318947902142891, + 0.00009159473951071446 + }; + + float BCoef[NCoef + 1] = { + 1.00000000000000000000, + -1.97223372919526560000, + 0.97261396931306277000 + }; + + static float y[2][NCoef + 1]; /* output samples */ + static float x[2][NCoef + 1]; /* input samples */ + int n; + + /* shift the old samples */ + for (n = NCoef; n > 0; n--) { + x[i][n] = x[i][n - 1]; + y[i][n] = y[i][n - 1]; + } + + /* Calculate the new output */ + x[i][0] = NewSample; + y[i][0] = ACoef[0] * x[i][0]; + for (n = 1; n <= NCoef; n++) + y[i][0] += ACoef[n] * x[i][n] - BCoef[n] * y[i][n]; + + return y[i][0]; +} + +/* fc=56Hz */ +static inline float +adgold_pseudo_stereo_iir(float NewSample) +{ + float ACoef[NCoef + 1] = { + 0.00001409030866231767, + 0.00002818061732463533, + 0.00001409030866231767 + }; + + float BCoef[NCoef + 1] = { + 1.00000000000000000000, + -1.98733021473466760000, + 0.98738361004063568000 + }; + + static float y[NCoef + 1]; /* output samples */ + static float x[NCoef + 1]; /* input samples */ + int n; + + /* shift the old samples */ + for (n = NCoef; n > 0; n--) { + x[n] = x[n - 1]; + y[n] = y[n - 1]; + } + + /* Calculate the new output */ + x[0] = NewSample; + y[0] = ACoef[0] * x[0]; + for (n = 1; n <= NCoef; n++) + y[0] += ACoef[n] * x[n] - BCoef[n] * y[n]; + + return y[0]; +} + /* fc=3.2kHz - probably incorrect */ static inline float dss_iir(float NewSample) diff --git a/src/include/86box/snd_opl.h b/src/include/86box/snd_opl.h index 62c62e965..fbc66a1c2 100644 --- a/src/include/86box/snd_opl.h +++ b/src/include/86box/snd_opl.h @@ -49,6 +49,9 @@ enum fm_type { FM_MAX = 26 }; +#define FM_TYPE_MASK 255 +#define FM_FORCE_48K 256 + enum fm_driver { FM_DRV_NUKED = 0, FM_DRV_YMFM = 1, @@ -65,9 +68,11 @@ typedef struct fm_drv_t { void (*generate)(void *priv, int32_t *data, uint32_t num_samples); /* daughterboard only. */ } fm_drv_t; +extern uint8_t fm_driver_get_ex(int chip_id, fm_drv_t *drv, int is_48k); extern uint8_t fm_driver_get(int chip_id, fm_drv_t *drv); extern const fm_drv_t nuked_opl_drv; +extern const fm_drv_t nuked_opl_drv_48k; extern const fm_drv_t ymfm_drv; extern const fm_drv_t esfmu_opl_drv; extern const fm_drv_t ymfm_opl2board_drv; diff --git a/src/include/86box/snd_opl_nuked.h b/src/include/86box/snd_opl_nuked.h index 0b203fe31..9fdbe16c4 100644 --- a/src/include/86box/snd_opl_nuked.h +++ b/src/include/86box/snd_opl_nuked.h @@ -147,7 +147,7 @@ struct _opl3_chip { typedef struct { opl3_chip opl; int8_t flags; - int8_t pad; + int8_t is_48k; uint16_t port; uint8_t status; @@ -159,6 +159,8 @@ typedef struct { int pos; int32_t buffer[MUSICBUFLEN * 2]; + + int32_t *(*update)(void *priv); } nuked_drv_t; enum { diff --git a/src/include/86box/snd_ym7128.h b/src/include/86box/snd_ym7128.h index f0657425f..a0796b1fa 100644 --- a/src/include/86box/snd_ym7128.h +++ b/src/include/86box/snd_ym7128.h @@ -19,18 +19,18 @@ typedef struct ym7128_t { int c1; int t[9]; - int16_t filter_dat[2]; - int16_t prev_l[2]; - int16_t prev_r[2]; + int16_t filter_dat; + int16_t prev_l; + int16_t prev_r; - int16_t delay_buffer[2][2400]; - int delay_pos[2]; + int16_t delay_buffer[2400]; + int delay_pos; int16_t last_samp; } ym7128_t; void ym7128_init(ym7128_t *ym7128); void ym7128_write(ym7128_t *ym7128, uint8_t val); -void ym7128_apply(ym7128_t *ym7128, int16_t *buffer, int i, int len); +void ym7128_apply(ym7128_t *ym7128, int16_t *buffer, int len); #endif /*SOUND_YM7128_H*/ diff --git a/src/sound/snd_adlibgold.c b/src/sound/snd_adlibgold.c index f8c89c554..87243e8d9 100644 --- a/src/sound/snd_adlibgold.c +++ b/src/sound/snd_adlibgold.c @@ -82,8 +82,6 @@ typedef struct adgold_t { int treble; int bass; - int16_t samp_buffer[SOUNDBUFLEN * 2]; - int16_t opl_buffer[MUSICBUFLEN * 2]; int16_t mma_buffer[2][SOUNDBUFLEN]; int pos; @@ -779,30 +777,37 @@ static void adgold_get_buffer(int32_t *buffer, int len, void *priv) { adgold_t *adgold = (adgold_t *) priv; + int16_t *adgold_buffer = malloc(sizeof(int16_t) * len * 2); + if (adgold_buffer == NULL) + fatal("adgold_buffer = NULL"); + int c; + int32_t *opl_buf = adgold->opl.update(adgold->opl.priv); adgold_update(adgold); for (c = 0; c < len * 2; c += 2) { - adgold->samp_buffer[c] = ((adgold->mma_buffer[0][c >> 1] * adgold->samp_vol_l) >> 7) / 4; - adgold->samp_buffer[c + 1] = ((adgold->mma_buffer[1][c >> 1] * adgold->samp_vol_r) >> 7) / 4; + adgold_buffer[c] = ((opl_buf[c] * adgold->fm_vol_l) >> 7) / 2; + adgold_buffer[c] += ((adgold->mma_buffer[0][c >> 1] * adgold->samp_vol_l) >> 7) / 4; + adgold_buffer[c + 1] = ((opl_buf[c + 1] * adgold->fm_vol_r) >> 7) / 2; + adgold_buffer[c + 1] += ((adgold->mma_buffer[1][c >> 1] * adgold->samp_vol_r) >> 7) / 4; } if (adgold->surround_enabled) - ym7128_apply(&adgold->ym7128, adgold->samp_buffer, 0, len); + ym7128_apply(&adgold->ym7128, adgold_buffer, len); switch (adgold->adgold_38x_regs[0x8] & 6) { case 0: for (c = 0; c < len * 2; c++) - adgold->samp_buffer[c] = 0; + adgold_buffer[c] = 0; break; case 2: /*Left channel only*/ for (c = 0; c < len * 2; c += 2) - adgold->samp_buffer[c + 1] = adgold->samp_buffer[c]; + adgold_buffer[c + 1] = adgold_buffer[c]; break; case 4: /*Right channel only*/ for (c = 0; c < len * 2; c += 2) - adgold->samp_buffer[c] = adgold->samp_buffer[c + 1]; + adgold_buffer[c] = adgold_buffer[c + 1]; break; case 6: /*Left and right channels*/ break; @@ -814,7 +819,7 @@ adgold_get_buffer(int32_t *buffer, int len, void *priv) switch (adgold->adgold_38x_regs[0x8] & 0x18) { case 0x00: /*Forced mono*/ for (c = 0; c < len * 2; c += 2) - adgold->samp_buffer[c] = adgold->samp_buffer[c + 1] = ((int32_t) adgold->samp_buffer[c] + (int32_t) adgold->samp_buffer[c + 1]) / 2; + adgold_buffer[c] = adgold_buffer[c + 1] = ((int32_t) adgold_buffer[c] + (int32_t) adgold_buffer[c + 1]) / 2; break; case 0x08: /*Linear stereo*/ break; @@ -822,17 +827,17 @@ adgold_get_buffer(int32_t *buffer, int len, void *priv) /*Filter left channel, leave right channel unchanged*/ /*Filter cutoff is largely a guess*/ for (c = 0; c < len * 2; c += 2) - adgold->samp_buffer[c] += adgold_pseudo_stereo_iir(0, adgold->samp_buffer[c]); + adgold_buffer[c] += adgold_pseudo_stereo_iir(adgold_buffer[c]); break; case 0x18: /*Spatial stereo*/ /*Quite probably wrong, I only have the diagram in the TDA8425 datasheet and a very vague understanding of how op-amps work to go on*/ for (c = 0; c < len * 2; c += 2) { - int16_t l = adgold->samp_buffer[c]; - int16_t r = adgold->samp_buffer[c + 1]; + int16_t l = adgold_buffer[c]; + int16_t r = adgold_buffer[c + 1]; - adgold->samp_buffer[c] += (r / 3) + ((l * 2) / 3); - adgold->samp_buffer[c + 1] += (l / 3) + ((r * 2) / 3); + adgold_buffer[c] += (r / 3) + ((l * 2) / 3); + adgold_buffer[c + 1] += (l / 3) + ((r * 2) / 3); } break; @@ -846,9 +851,9 @@ adgold_get_buffer(int32_t *buffer, int len, void *priv) int32_t highpass; /*Output is deliberately halved to avoid clipping*/ - temp = ((int32_t) adgold->samp_buffer[c] * adgold->vol_l) >> 17; - lowpass = adgold_lowpass_iir(0, 0, temp); - highpass = adgold_highpass_iir(0, 0, temp); + temp = ((int32_t) adgold_buffer[c] * adgold->vol_l) >> 17; + lowpass = adgold_lowpass_iir(0, temp); + highpass = adgold_highpass_iir(0, temp); if (adgold->bass > 6) temp += (lowpass * bass_attenuation[adgold->bass]) >> 14; else if (adgold->bass < 6) @@ -863,118 +868,9 @@ adgold_get_buffer(int32_t *buffer, int len, void *priv) temp = 32767; buffer[c] += temp; - temp = ((int32_t) adgold->samp_buffer[c + 1] * adgold->vol_r) >> 17; - lowpass = adgold_lowpass_iir(0, 1, temp); - highpass = adgold_highpass_iir(0, 1, temp); - if (adgold->bass > 6) - temp += (lowpass * bass_attenuation[adgold->bass]) >> 14; - else if (adgold->bass < 6) - temp = highpass + ((temp * bass_cut[adgold->bass]) >> 14); - if (adgold->treble > 6) - temp += (highpass * treble_attenuation[adgold->treble]) >> 14; - else if (adgold->treble < 6) - temp = lowpass + ((temp * treble_cut[adgold->treble]) >> 14); - if (temp < -32768) - temp = -32768; - if (temp > 32767) - temp = 32767; - buffer[c + 1] += temp; - } - - adgold->pos = 0; -} - -static void -adgold_get_music_buffer(int32_t *buffer, int len, void *priv) -{ - adgold_t *adgold = (adgold_t *) priv; - int c; - - const int32_t *opl_buf = adgold->opl.update(adgold->opl.priv); - - for (c = 0; c < len * 2; c += 2) { - adgold->opl_buffer[c] = ((opl_buf[c] * adgold->fm_vol_l) >> 7) / 2; - adgold->opl_buffer[c + 1] = ((opl_buf[c + 1] * adgold->fm_vol_r) >> 7) / 2; - } - - if (adgold->surround_enabled) - ym7128_apply(&adgold->ym7128, adgold->opl_buffer, 1, len); - - switch (adgold->adgold_38x_regs[0x8] & 6) { - case 0: - for (c = 0; c < len * 2; c++) - adgold->opl_buffer[c] = 0; - break; - case 2: /*Left channel only*/ - for (c = 0; c < len * 2; c += 2) - adgold->opl_buffer[c + 1] = adgold->opl_buffer[c]; - break; - case 4: /*Right channel only*/ - for (c = 0; c < len * 2; c += 2) - adgold->opl_buffer[c] = adgold->opl_buffer[c + 1]; - break; - case 6: /*Left and right channels*/ - break; - - default: - break; - } - - switch (adgold->adgold_38x_regs[0x8] & 0x18) { - case 0x00: /*Forced mono*/ - for (c = 0; c < len * 2; c += 2) - adgold->opl_buffer[c] = adgold->opl_buffer[c + 1] = ((int32_t) adgold->opl_buffer[c] + (int32_t) adgold->opl_buffer[c + 1]) / 2; - break; - case 0x08: /*Linear stereo*/ - break; - case 0x10: /*Pseudo stereo*/ - /*Filter left channel, leave right channel unchanged*/ - /*Filter cutoff is largely a guess*/ - for (c = 0; c < len * 2; c += 2) - adgold->opl_buffer[c] += adgold_pseudo_stereo_iir(1, adgold->opl_buffer[c]); - break; - case 0x18: /*Spatial stereo*/ - /*Quite probably wrong, I only have the diagram in the TDA8425 datasheet - and a very vague understanding of how op-amps work to go on*/ - for (c = 0; c < len * 2; c += 2) { - int16_t l = adgold->opl_buffer[c]; - int16_t r = adgold->opl_buffer[c + 1]; - - adgold->opl_buffer[c] += (r / 3) + ((l * 2) / 3); - adgold->opl_buffer[c + 1] += (l / 3) + ((r * 2) / 3); - } - break; - - default: - break; - } - - for (c = 0; c < len * 2; c += 2) { - int32_t temp; - int32_t lowpass; - int32_t highpass; - - /*Output is deliberately halved to avoid clipping*/ - temp = ((int32_t) adgold->opl_buffer[c] * adgold->vol_l) >> 17; - lowpass = adgold_lowpass_iir(1, 0, temp); - highpass = adgold_highpass_iir(1, 0, temp); - if (adgold->bass > 6) - temp += (lowpass * bass_attenuation[adgold->bass]) >> 14; - else if (adgold->bass < 6) - temp = highpass + ((temp * bass_cut[adgold->bass]) >> 14); - if (adgold->treble > 6) - temp += (highpass * treble_attenuation[adgold->treble]) >> 14; - else if (adgold->treble < 6) - temp = lowpass + ((temp * treble_cut[adgold->treble]) >> 14); - if (temp < -32768) - temp = -32768; - if (temp > 32767) - temp = 32767; - buffer[c] += temp; - - temp = ((int32_t) adgold->opl_buffer[c + 1] * adgold->vol_r) >> 17; - lowpass = adgold_lowpass_iir(1, 1, temp); - highpass = adgold_highpass_iir(1, 1, temp); + temp = ((int32_t) adgold_buffer[c + 1] * adgold->vol_r) >> 17; + lowpass = adgold_lowpass_iir(1, temp); + highpass = adgold_highpass_iir(1, temp); if (adgold->bass > 6) temp += (lowpass * bass_attenuation[adgold->bass]) >> 14; else if (adgold->bass < 6) @@ -991,6 +887,9 @@ adgold_get_music_buffer(int32_t *buffer, int len, void *priv) } adgold->opl.reset_buffer(adgold->opl.priv); + adgold->pos = 0; + + free(adgold_buffer); } static void @@ -1058,7 +957,7 @@ adgold_init(UNUSED(const device_t *info)) adgold->surround_enabled = device_get_config_int("surround"); adgold->gameport_enabled = device_get_config_int("gameport"); - fm_driver_get(FM_YMF289B, &adgold->opl); + fm_driver_get_ex(FM_YMF262, &adgold->opl, 1); if (adgold->surround_enabled) ym7128_init(&adgold->ym7128); @@ -1149,7 +1048,6 @@ adgold_init(UNUSED(const device_t *info)) timer_add(&adgold->adgold_mma_timer_count, adgold_timer_poll, adgold, 1); sound_add_handler(adgold_get_buffer, adgold); - music_add_handler(adgold_get_music_buffer, adgold); sound_set_cd_audio_filter(adgold_filter_cd_audio, adgold); diff --git a/src/sound/snd_opl.c b/src/sound/snd_opl.c index 1ee687f1e..6bbd634a7 100644 --- a/src/sound/snd_opl.c +++ b/src/sound/snd_opl.c @@ -36,150 +36,152 @@ static uint32_t fm_dev_inst[FM_DRV_MAX][FM_MAX]; uint8_t -fm_driver_get(int chip_id, fm_drv_t *drv) +fm_driver_get_ex(int chip_id, fm_drv_t *drv, int is_48k) { + void *flag_48k = is_48k ? ((void *) FM_FORCE_48K) : NULL; + switch (chip_id) { case FM_YM2149: /* SSG */ *drv = ymfm_drv; - drv->priv = device_add_inst(&ym2149_ymfm_device, fm_dev_inst[fm_driver][chip_id]++); + drv->priv = device_add_inst_params(&ym2149_ymfm_device, fm_dev_inst[fm_driver][chip_id]++, flag_48k); break; case FM_YM3526: /* OPL */ *drv = ymfm_drv; - drv->priv = device_add_inst(&ym3526_ymfm_device, fm_dev_inst[fm_driver][chip_id]++); + drv->priv = device_add_inst_params(&ym3526_ymfm_device, fm_dev_inst[fm_driver][chip_id]++, flag_48k); break; case FM_Y8950: /* MSX-Audio (OPL with ADPCM) */ *drv = ymfm_drv; - drv->priv = device_add_inst(&y8950_ymfm_device, fm_dev_inst[fm_driver][chip_id]++); + drv->priv = device_add_inst_params(&y8950_ymfm_device, fm_dev_inst[fm_driver][chip_id]++, flag_48k); break; case FM_YM3812: /* OPL2 */ if (fm_driver == FM_DRV_NUKED) { - *drv = nuked_opl_drv; - drv->priv = device_add_inst(&ym3812_nuked_device, fm_dev_inst[fm_driver][chip_id]++); + *drv = is_48k ? nuked_opl_drv_48k : nuked_opl_drv; + drv->priv = device_add_inst_params(&ym3812_nuked_device, fm_dev_inst[fm_driver][chip_id]++, flag_48k); } else { *drv = ymfm_drv; - drv->priv = device_add_inst(&ym3812_ymfm_device, fm_dev_inst[fm_driver][chip_id]++); + drv->priv = device_add_inst_params(&ym3812_ymfm_device, fm_dev_inst[fm_driver][chip_id]++, flag_48k); } break; case FM_YMF262: /* OPL3 */ if (fm_driver == FM_DRV_NUKED) { - *drv = nuked_opl_drv; - drv->priv = device_add_inst(&ymf262_nuked_device, fm_dev_inst[fm_driver][chip_id]++); + *drv = is_48k ? nuked_opl_drv_48k : nuked_opl_drv; + drv->priv = device_add_inst_params(&ymf262_nuked_device, fm_dev_inst[fm_driver][chip_id]++, flag_48k); } else { *drv = ymfm_drv; - drv->priv = device_add_inst(&ymf262_ymfm_device, fm_dev_inst[fm_driver][chip_id]++); + drv->priv = device_add_inst_params(&ymf262_ymfm_device, fm_dev_inst[fm_driver][chip_id]++, flag_48k); } break; case FM_YMF289B: /* OPL3-L */ *drv = ymfm_drv; - drv->priv = device_add_inst(&ymf289b_ymfm_device, fm_dev_inst[fm_driver][chip_id]++); + drv->priv = device_add_inst_params(&ymf289b_ymfm_device, fm_dev_inst[fm_driver][chip_id]++, flag_48k); break; case FM_YMF278B: /* OPL4 */ *drv = ymfm_drv; - drv->priv = device_add_inst(&ymf278b_ymfm_device, fm_dev_inst[fm_driver][chip_id]++); + drv->priv = device_add_inst_params(&ymf278b_ymfm_device, fm_dev_inst[fm_driver][chip_id]++, flag_48k); break; case FM_YM2413: /* OPLL */ *drv = ymfm_drv; - drv->priv = device_add_inst(&ym2413_ymfm_device, fm_dev_inst[fm_driver][chip_id]++); + drv->priv = device_add_inst_params(&ym2413_ymfm_device, fm_dev_inst[fm_driver][chip_id]++, flag_48k); break; case FM_YM2423: /* OPLL-X */ *drv = ymfm_drv; - drv->priv = device_add_inst(&ym2423_ymfm_device, fm_dev_inst[fm_driver][chip_id]++); + drv->priv = device_add_inst_params(&ym2423_ymfm_device, fm_dev_inst[fm_driver][chip_id]++, flag_48k); break; case FM_YMF281: /* OPLLP */ *drv = ymfm_drv; - drv->priv = device_add_inst(&ymf281_ymfm_device, fm_dev_inst[fm_driver][chip_id]++); + drv->priv = device_add_inst_params(&ymf281_ymfm_device, fm_dev_inst[fm_driver][chip_id]++, flag_48k); break; case FM_DS1001: /* Konami VRC7 MMC */ *drv = ymfm_drv; - drv->priv = device_add_inst(&ds1001_ymfm_device, fm_dev_inst[fm_driver][chip_id]++); + drv->priv = device_add_inst_params(&ds1001_ymfm_device, fm_dev_inst[fm_driver][chip_id]++, flag_48k); break; case FM_YM2151: /* OPM */ *drv = ymfm_drv; - drv->priv = device_add_inst(&ym2151_ymfm_device, fm_dev_inst[fm_driver][chip_id]++); + drv->priv = device_add_inst_params(&ym2151_ymfm_device, fm_dev_inst[fm_driver][chip_id]++, flag_48k); break; case FM_YM2203: /* OPN */ *drv = ymfm_drv; - drv->priv = device_add_inst(&ym2203_ymfm_device, fm_dev_inst[fm_driver][chip_id]++); + drv->priv = device_add_inst_params(&ym2203_ymfm_device, fm_dev_inst[fm_driver][chip_id]++, flag_48k); break; case FM_YM2608: /* OPNA */ *drv = ymfm_drv; - drv->priv = device_add_inst(&ym2608_ymfm_device, fm_dev_inst[fm_driver][chip_id]++); + drv->priv = device_add_inst_params(&ym2608_ymfm_device, fm_dev_inst[fm_driver][chip_id]++, flag_48k); break; case FM_YMF288: /* OPN3L */ *drv = ymfm_drv; - drv->priv = device_add_inst(&ymf288_ymfm_device, fm_dev_inst[fm_driver][chip_id]++); + drv->priv = device_add_inst_params(&ymf288_ymfm_device, fm_dev_inst[fm_driver][chip_id]++, flag_48k); break; case FM_YM2610: /* OPNB */ *drv = ymfm_drv; - drv->priv = device_add_inst(&ym2610_ymfm_device, fm_dev_inst[fm_driver][chip_id]++); + drv->priv = device_add_inst_params(&ym2610_ymfm_device, fm_dev_inst[fm_driver][chip_id]++, flag_48k); break; case FM_YM2610B: /* OPNB2 */ *drv = ymfm_drv; - drv->priv = device_add_inst(&ym2610b_ymfm_device, fm_dev_inst[fm_driver][chip_id]++); + drv->priv = device_add_inst_params(&ym2610b_ymfm_device, fm_dev_inst[fm_driver][chip_id]++, flag_48k); break; case FM_YM2612: /* OPN2 */ *drv = ymfm_drv; - drv->priv = device_add_inst(&ym2612_ymfm_device, fm_dev_inst[fm_driver][chip_id]++); + drv->priv = device_add_inst_params(&ym2612_ymfm_device, fm_dev_inst[fm_driver][chip_id]++, flag_48k); break; case FM_YM3438: /* OPN2C */ *drv = ymfm_drv; - drv->priv = device_add_inst(&ym3438_ymfm_device, fm_dev_inst[fm_driver][chip_id]++); + drv->priv = device_add_inst_params(&ym3438_ymfm_device, fm_dev_inst[fm_driver][chip_id]++, flag_48k); break; case FM_YMF276: /* OPN2L */ *drv = ymfm_drv; - drv->priv = device_add_inst(&ymf276_ymfm_device, fm_dev_inst[fm_driver][chip_id]++); + drv->priv = device_add_inst_params(&ymf276_ymfm_device, fm_dev_inst[fm_driver][chip_id]++, flag_48k); break; case FM_YM2164: /* OPP */ *drv = ymfm_drv; - drv->priv = device_add_inst(&ym2164_ymfm_device, fm_dev_inst[fm_driver][chip_id]++); + drv->priv = device_add_inst_params(&ym2164_ymfm_device, fm_dev_inst[fm_driver][chip_id]++, flag_48k); break; case FM_YM3806: /* OPQ */ *drv = ymfm_drv; - drv->priv = device_add_inst(&ym3806_ymfm_device, fm_dev_inst[fm_driver][chip_id]++); + drv->priv = device_add_inst_params(&ym3806_ymfm_device, fm_dev_inst[fm_driver][chip_id]++, flag_48k); break; #if 0 case FM_YMF271: /* OPX */ *drv = ymfm_drv; - drv->priv = device_add_inst(&ymf271_ymfm_device, fm_dev_inst[fm_driver][chip_id]++); + drv->priv = device_add_inst_params(&ymf271_ymfm_device, fm_dev_inst[fm_driver][chip_id]++, flag_48k); break; #endif case FM_YM2414: /* OPZ */ *drv = ymfm_drv; - drv->priv = device_add_inst(&ym2414_ymfm_device, fm_dev_inst[fm_driver][chip_id]++); + drv->priv = device_add_inst_params(&ym2414_ymfm_device, fm_dev_inst[fm_driver][chip_id]++, flag_48k); break; case FM_ESFM: *drv = esfmu_opl_drv; - drv->priv = device_add_inst(&esfm_esfmu_device, fm_dev_inst[fm_driver][chip_id]++); + drv->priv = device_add_inst_params(&esfm_esfmu_device, fm_dev_inst[fm_driver][chip_id]++, flag_48k); break; #ifdef USE_LIBSERIALPORT case FM_OPL2BOARD: *drv = ymfm_opl2board_drv; - drv->priv = device_add_inst(&ym_opl2board_device, fm_dev_inst[fm_driver][chip_id]++); + drv->priv = device_add_inst_params(&ym_opl2board_device, fm_dev_inst[fm_driver][chip_id]++, flag_48k); break; #endif @@ -189,3 +191,9 @@ fm_driver_get(int chip_id, fm_drv_t *drv) return 1; }; + +uint8_t +fm_driver_get(int chip_id, fm_drv_t *drv) +{ + return fm_driver_get_ex(chip_id, drv, 0); +} diff --git a/src/sound/snd_opl_nuked.c b/src/sound/snd_opl_nuked.c index 60f5ed2a6..a69b2e758 100644 --- a/src/sound/snd_opl_nuked.c +++ b/src/sound/snd_opl_nuked.c @@ -1460,6 +1460,15 @@ OPL3_GenerateStream(opl3_chip *chip, int32_t *sndptr, uint32_t numsamples) } } +void +OPL3_GenerateResampledStream(opl3_chip *chip, int32_t *sndptr, uint32_t numsamples) +{ + for (uint_fast32_t i = 0; i < numsamples; i++) { + OPL3_GenerateResampled(chip, sndptr); + sndptr += 2; + } +} + static void nuked_timer_tick(nuked_drv_t *dev, int tmr) { @@ -1525,32 +1534,6 @@ nuked_drv_set_do_cycles(void *priv, int8_t do_cycles) dev->flags &= ~FLAG_CYCLES; } -static void * -nuked_drv_init(const device_t *info) -{ - nuked_drv_t *dev = (nuked_drv_t *) calloc(1, sizeof(nuked_drv_t)); - dev->flags = FLAG_CYCLES; - if (info->local == FM_YMF262) - dev->flags |= FLAG_OPL3; - else - dev->status = 0x06; - - /* Initialize the NukedOPL object. */ - OPL3_Reset(&dev->opl, FREQ_49716); - - timer_add(&dev->timers[0], nuked_timer_1, dev, 0); - timer_add(&dev->timers[1], nuked_timer_2, dev, 0); - - return dev; -} - -static void -nuked_drv_close(void *priv) -{ - nuked_drv_t *dev = (nuked_drv_t *) priv; - free(dev); -} - static int32_t * nuked_drv_update(void *priv) { @@ -1560,8 +1543,8 @@ nuked_drv_update(void *priv) return dev->buffer; OPL3_GenerateStream(&dev->opl, - &dev->buffer[dev->pos * 2], - music_pos_global - dev->pos); + &dev->buffer[dev->pos * 2], + music_pos_global - dev->pos); for (; dev->pos < music_pos_global; dev->pos++) { dev->buffer[dev->pos * 2] /= 2; @@ -1571,6 +1554,26 @@ nuked_drv_update(void *priv) return dev->buffer; } +static int32_t * +nuked_drv_update_48k(void *priv) +{ + nuked_drv_t *dev = (nuked_drv_t *) priv; + + if (dev->pos >= sound_pos_global) + return dev->buffer; + + OPL3_GenerateResampledStream(&dev->opl, + &dev->buffer[dev->pos * 2], + sound_pos_global - dev->pos); + + for (; dev->pos < sound_pos_global; dev->pos++) { + dev->buffer[dev->pos * 2] /= 2; + dev->buffer[(dev->pos * 2) + 1] /= 2; + } + + return dev->buffer; +} + static uint8_t nuked_drv_read(uint16_t port, void *priv) { @@ -1579,7 +1582,7 @@ nuked_drv_read(uint16_t port, void *priv) if (dev->flags & FLAG_CYCLES) cycles -= ((int) (isa_timing * 8)); - nuked_drv_update(dev); + dev->update(dev); uint8_t ret = 0xff; @@ -1598,7 +1601,8 @@ static void nuked_drv_write(uint16_t port, uint8_t val, void *priv) { nuked_drv_t *dev = (nuked_drv_t *) priv; - nuked_drv_update(dev); + + dev->update(dev); if ((port & 0x0001) == 0x0001) { OPL3_WriteRegBuffered(&dev->opl, dev->port, val); @@ -1649,6 +1653,40 @@ nuked_drv_reset_buffer(void *priv) dev->pos = 0; } +static void +nuked_drv_close(void *priv) +{ + nuked_drv_t *dev = (nuked_drv_t *) priv; + free(dev); +} + +static void * +nuked_drv_init(const device_t *info) +{ + nuked_drv_t *dev = (nuked_drv_t *) calloc(1, sizeof(nuked_drv_t)); + dev->flags = FLAG_CYCLES; + if ((info->local & FM_TYPE_MASK) == FM_YMF262) + dev->flags |= FLAG_OPL3; + else + dev->status = 0x06; + + dev->is_48k = !!(info->local & FM_FORCE_48K); + + /* Initialize the NukedOPL object. */ + if (dev->is_48k) { + dev->update = nuked_drv_update_48k; + OPL3_Reset(&dev->opl, FREQ_48000); + } else { + dev->update = nuked_drv_update; + OPL3_Reset(&dev->opl, FREQ_49716); + } + + timer_add(&dev->timers[0], nuked_timer_1, dev, 0); + timer_add(&dev->timers[1], nuked_timer_2, dev, 0); + + return dev; +} + const device_t ym3812_nuked_device = { .name = "Yamaha YM3812 OPL2 (NUKED)", .internal_name = "ym3812_nuked", @@ -1686,3 +1724,13 @@ const fm_drv_t nuked_opl_drv = { .priv = NULL, .generate = NULL, }; + +const fm_drv_t nuked_opl_drv_48k = { + .read = &nuked_drv_read, + .write = &nuked_drv_write, + .update = &nuked_drv_update_48k, + .reset_buffer = &nuked_drv_reset_buffer, + .set_do_cycles = &nuked_drv_set_do_cycles, + .priv = NULL, + .generate = NULL, +}; diff --git a/src/sound/snd_opl_ymfm.cpp b/src/sound/snd_opl_ymfm.cpp index 815b6cb4b..37363aa74 100644 --- a/src/sound/snd_opl_ymfm.cpp +++ b/src/sound/snd_opl_ymfm.cpp @@ -56,11 +56,12 @@ enum { class YMFMChipBase { public: - YMFMChipBase(UNUSED(uint32_t clock), fm_type type, uint32_t samplerate) + YMFMChipBase(UNUSED(uint32_t clock), fm_type type, uint32_t samplerate, int is_48k) : m_buf_pos(0) , m_flags(0) , m_type(type) , m_samplerate(samplerate) + , m_48k(is_48k) { memset(m_buffer, 0, sizeof(m_buffer)); } @@ -74,11 +75,13 @@ public: void set_do_cycles(int8_t do_cycles) { do_cycles ? m_flags |= FLAG_CYCLES : m_flags &= ~FLAG_CYCLES; } int32_t *buffer() const { return (int32_t *) m_buffer; } void reset_buffer() { m_buf_pos = 0; } + int is_48k() const { return m_48k; } virtual uint32_t sample_rate() const = 0; virtual void write(uint16_t addr, uint8_t data) = 0; virtual void generate(int32_t *data, uint32_t num_samples) = 0; + virtual void generate_resampled(int32_t *data, uint32_t num_samples) = 0; virtual int32_t *update() = 0; virtual uint8_t read(uint16_t addr) = 0; virtual void set_clock(uint32_t clock) = 0; @@ -90,17 +93,19 @@ protected: int8_t m_flags; fm_type m_type; uint32_t m_samplerate; + int m_48k; }; template class YMFMChip : public YMFMChipBase, public ymfm::ymfm_interface { public: - YMFMChip(uint32_t clock, fm_type type, uint32_t samplerate) - : YMFMChipBase(clock, type, samplerate) + YMFMChip(uint32_t clock, fm_type type, uint32_t samplerate, int m_48k) + : YMFMChipBase(clock, type, samplerate, m_48k) , m_chip(*this) , m_clock(clock) , m_samplerate(samplerate) , m_samplecnt(0) + , m_48k(0) { memset(m_samples, 0, sizeof(m_samples)); memset(m_oldsamples, 0, sizeof(m_oldsamples)); @@ -109,7 +114,10 @@ public: m_subtract[0] = 80.0; m_subtract[1] = 320.0; m_type = type; - m_buf_pos_global = (samplerate == FREQ_49716) ? &music_pos_global : &wavetable_pos_global; + if (m_48k) + m_buf_pos_global = &sound_pos_global; + else + m_buf_pos_global = (samplerate == FREQ_49716) ? &music_pos_global : &wavetable_pos_global; if (m_type == FM_YMF278B) { if (rom_load_linear("roms/sound/yamaha/yrw801.rom", 0, 0x200000, 0, m_yrw801) == 0) { @@ -176,14 +184,8 @@ public: } } -#if 0 virtual void generate_resampled(int32_t *data, uint32_t num_samples) override { - if ((m_samplerate == FREQ_49716) || (m_samplerate == FREQ_44100)) { - generate(data, num_samples); - return; - } - for (uint32_t i = 0; i < num_samples; i++) { while (m_samplecnt >= m_rateratio) { m_oldsamples[0] = m_samples[0]; @@ -217,14 +219,16 @@ public: m_samplecnt += 1 << RSM_FRAC; } } -#endif virtual int32_t *update() override { if (m_buf_pos >= *m_buf_pos_global) return m_buffer; - generate(&m_buffer[m_buf_pos * 2], *m_buf_pos_global - m_buf_pos); + if (m_48k) + generate_resampled(&m_buffer[m_buf_pos * 2], *m_buf_pos_global - m_buf_pos); + else + generate(&m_buffer[m_buf_pos * 2], *m_buf_pos_global - m_buf_pos); for (; m_buf_pos < *m_buf_pos_global; m_buf_pos++) { m_buffer[m_buf_pos * 2] /= 2; @@ -287,6 +291,8 @@ private: int32_t m_samplecnt; int32_t m_oldsamples[2]; int32_t m_samples[2]; + + int m_48k; }; extern "C" { @@ -325,127 +331,128 @@ static void * ymfm_drv_init(const device_t *info) { YMFMChipBase *fm; + int is_48k = !!(info->local & FM_FORCE_48K); - switch (info->local) { + switch (info->local & FM_TYPE_MASK) { case FM_YM2149: /* OPL */ // TODO: Check rates and frequency - fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YM2149, FREQ_49716); + fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YM2149, FREQ_49716, is_48k); break; case FM_YM3526: /* OPL */ // TODO: Check rates and frequency - fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YM3526, FREQ_49716); + fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YM3526, FREQ_49716, is_48k); break; case FM_Y8950: /* MSX-Audio (OPL with ADPCM) */ // TODO: Check rates and frequency - fm = (YMFMChipBase *) new YMFMChip(14318181, FM_Y8950, FREQ_49716); + fm = (YMFMChipBase *) new YMFMChip(14318181, FM_Y8950, FREQ_49716, is_48k); break; default: case FM_YM3812: /* OPL2 */ - fm = (YMFMChipBase *) new YMFMChip(3579545, FM_YM3812, FREQ_49716); + fm = (YMFMChipBase *) new YMFMChip(3579545, FM_YM3812, FREQ_49716, is_48k); break; case FM_YMF262: /* OPL3 */ - fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YMF262, FREQ_49716); + fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YMF262, FREQ_49716, is_48k); break; case FM_YMF289B: /* OPL3-L */ /* According to the datasheet, we should be using 33868800, but YMFM appears to cheat and does it using the same values as the YMF262. */ - fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YMF289B, FREQ_49716); + fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YMF289B, FREQ_49716, is_48k); break; case FM_YMF278B: /* OPL4 */ - fm = (YMFMChipBase *) new YMFMChip(33868800, FM_YMF278B, FREQ_44100); + fm = (YMFMChipBase *) new YMFMChip(33868800, FM_YMF278B, FREQ_44100, is_48k); break; case FM_YM2413: /* OPLL */ // TODO: Check rates and frequency - fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YM2413, FREQ_49716); + fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YM2413, FREQ_49716, is_48k); break; case FM_YM2423: /* OPLL-X */ // TODO: Check rates and frequency - fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YM2423, FREQ_49716); + fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YM2423, FREQ_49716, is_48k); break; case FM_YMF281: /* OPLLP */ // TODO: Check rates and frequency - fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YMF281, FREQ_49716); + fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YMF281, FREQ_49716, is_48k); break; case FM_DS1001: /* Konami VRC7 MMC */ // TODO: Check rates and frequency - fm = (YMFMChipBase *) new YMFMChip(14318181, FM_DS1001, FREQ_49716); + fm = (YMFMChipBase *) new YMFMChip(14318181, FM_DS1001, FREQ_49716, is_48k); break; case FM_YM2151: /* OPM */ // TODO: Check rates and frequency - fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YM2151, FREQ_49716); + fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YM2151, FREQ_49716, is_48k); break; case FM_YM2203: /* OPN */ // TODO: Check rates and frequency - fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YM2203, FREQ_49716); + fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YM2203, FREQ_49716, is_48k); break; case FM_YM2608: /* OPNA */ // TODO: Check rates and frequency - fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YM2608, FREQ_49716); + fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YM2608, FREQ_49716, is_48k); break; case FM_YMF288: /* OPN3L */ // TODO: Check rates and frequency - fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YMF288, FREQ_49716); + fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YMF288, FREQ_49716, is_48k); break; case FM_YM2610: /* OPNB */ // TODO: Check rates and frequency - fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YM2610, FREQ_49716); + fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YM2610, FREQ_49716, is_48k); break; case FM_YM2610B: /* OPNB2 */ // TODO: Check rates and frequency - fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YM2610B, FREQ_49716); + fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YM2610B, FREQ_49716, is_48k); break; case FM_YM2612: /* OPN2 */ // TODO: Check rates and frequency - fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YM2612, FREQ_49716); + fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YM2612, FREQ_49716, is_48k); break; case FM_YM3438: /* OPN2C */ // TODO: Check rates and frequency - fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YM3438, FREQ_49716); + fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YM3438, FREQ_49716, is_48k); break; case FM_YMF276: /* OPN2L */ // TODO: Check function call, rates and frequency - fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YMF276, FREQ_49716); + fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YMF276, FREQ_49716, is_48k); break; case FM_YM2164: /* OPP */ // TODO: Check rates and frequency - fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YM2164, FREQ_49716); + fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YM2164, FREQ_49716, is_48k); break; case FM_YM3806: /* OPQ */ // TODO: Check rates and frequency - fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YM3806, FREQ_49716); + fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YM3806, FREQ_49716, is_48k); break; #if 0 case FM_YMF271: /* OPX */ // TODO: Check rates and frequency - fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YMF271, FREQ_49716); + fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YMF271, FREQ_49716, is_48k); break; #endif case FM_YM2414: /* OPZ */ // TODO: Check rates and frequency - fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YM2414, FREQ_49716); + fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YM2414, FREQ_49716, is_48k); break; } @@ -523,8 +530,10 @@ ymfm_drv_generate(void *priv, int32_t *data, uint32_t num_samples) { YMFMChipBase *drv = (YMFMChipBase *) priv; - // drv->generate_resampled(data, num_samples); - drv->generate(data, num_samples); + if (drv->is_48k()) + drv->generate_resampled(data, num_samples); + else + drv->generate(data, num_samples); } const device_t ym2149_ymfm_device = { diff --git a/src/sound/snd_ym7128.c b/src/sound/snd_ym7128.c index 08b33f414..59e5691e9 100644 --- a/src/sound/snd_ym7128.c +++ b/src/sound/snd_ym7128.c @@ -111,10 +111,10 @@ ym7128_write(ym7128_t *ym7128, uint8_t val) ym7128->a0 = new_a0; } -#define GET_DELAY_SAMPLE(ym7128, offset) (((ym7128->delay_pos[i] - offset) < 0) ? ym7128->delay_buffer[i][(ym7128->delay_pos[i] - offset) + 2400] : ym7128->delay_buffer[i][ym7128->delay_pos[i] - offset]) +#define GET_DELAY_SAMPLE(ym7128, offset) (((ym7128->delay_pos - offset) < 0) ? ym7128->delay_buffer[(ym7128->delay_pos - offset) + 2400] : ym7128->delay_buffer[ym7128->delay_pos - offset]) void -ym7128_apply(ym7128_t *ym7128, int16_t *buffer, int i, int len) +ym7128_apply(ym7128_t *ym7128, int16_t *buffer, int len) { for (int c = 0; c < len * 2; c += 4) { /*YM7128 samples a mono stream at ~24 kHz, so downsample*/ @@ -125,13 +125,13 @@ ym7128_apply(ym7128_t *ym7128, int16_t *buffer, int i, int len) int32_t samp_r = 0; filter_temp = GET_DELAY_SAMPLE(ym7128, ym7128->t[0]); - filter_out = ((filter_temp * ym7128->c0) >> 11) + ((ym7128->filter_dat[i] * ym7128->c1) >> 11); + filter_out = ((filter_temp * ym7128->c0) >> 11) + ((ym7128->filter_dat * ym7128->c1) >> 11); filter_out = (filter_out * ym7128->vc) >> 16; samp = (samp * ym7128->vm) >> 16; samp += filter_out; - ym7128->delay_buffer[i][ym7128->delay_pos[i]] = samp; + ym7128->delay_buffer[ym7128->delay_pos] = samp; for (uint8_t d = 0; d < 8; d++) { samp_l += (GET_DELAY_SAMPLE(ym7128, ym7128->t[d + 1]) * ym7128->gl[d]) >> 16; @@ -141,17 +141,17 @@ ym7128_apply(ym7128_t *ym7128, int16_t *buffer, int i, int len) samp_l = (samp_l * ym7128->vl * 2) >> 16; samp_r = (samp_r * ym7128->vr * 2) >> 16; - buffer[c] += (samp_l + (int32_t) ym7128->prev_l[i]) / 2; - buffer[c + 1] += (samp_r + (int32_t) ym7128->prev_r[i]) / 2; + buffer[c] += (samp_l + (int32_t) ym7128->prev_l) / 2; + buffer[c + 1] += (samp_r + (int32_t) ym7128->prev_r) / 2; buffer[c + 2] += samp_l; buffer[c + 3] += samp_r; - ym7128->delay_pos[i]++; - if (ym7128->delay_pos[i] >= 2400) - ym7128->delay_pos[i] = 0; + ym7128->delay_pos++; + if (ym7128->delay_pos >= 2400) + ym7128->delay_pos = 0; - ym7128->filter_dat[i] = filter_temp; - ym7128->prev_l[i] = samp_l; - ym7128->prev_r[i] = samp_r; + ym7128->filter_dat = filter_temp; + ym7128->prev_l = samp_l; + ym7128->prev_r = samp_r; } } From 91652ab4c70506e41dc3ab15ef1ea5f8280ca460 Mon Sep 17 00:00:00 2001 From: OBattler Date: Sun, 28 Sep 2025 04:06:39 +0200 Subject: [PATCH 4/8] IDE: Revert the previous ATAPI shadow fix and fix it better now by simply forcibly returning 0x0000 on reading the cylinder number, fixes the actual ATAPI drive's signature being overwritten with 0x0000, fixes OS/2 Warp 3.0 and AN430TX BIOS. --- src/disk/hdc_ide.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/disk/hdc_ide.c b/src/disk/hdc_ide.c index e9ee7083f..105cdb3a0 100644 --- a/src/disk/hdc_ide.c +++ b/src/disk/hdc_ide.c @@ -810,7 +810,7 @@ ide_set_signature(ide_t *ide) ide->tf->sector = 1; ide->tf->head = 0; ide->tf->secount = 1; - ide->tf->cylinder = (ide->type == IDE_ATAPI_SHADOW) ? 0x0000 : ide_signatures[ide->type & ~IDE_SHADOW]; + ide->tf->cylinder = ide_signatures[ide->type & ~IDE_SHADOW]; if (ide->type == IDE_HDD) ide->drive = 0; @@ -1579,7 +1579,7 @@ ide_reset_registers(ide_t *ide) ide->tf->atastat = DRDY_STAT | DSC_STAT; ide->tf->error = 1; ide->tf->secount = 1; - ide->tf->cylinder = (ide->type == IDE_ATAPI_SHADOW) ? 0x0000 : ide_signatures[ide->type & ~IDE_SHADOW]; + ide->tf->cylinder = ide_signatures[ide->type & ~IDE_SHADOW]; ide->tf->sector = 1; ide->tf->head = 0; @@ -1676,6 +1676,7 @@ ide_writeb(uint16_t addr, uint8_t val, void *priv) break; case 0x6: /* Drive/Head */ + pclog("ch = %i, current = %i\n", ch, ((val >> 4) & 1) + (ide->board << 1)); if (ch != ((val >> 4) & 1) + (ide->board << 1)) { if (!ide->reset && !ide_other->reset && ide->irqstat) { ide_irq_lower(ide); @@ -1684,6 +1685,7 @@ ide_writeb(uint16_t addr, uint8_t val, void *priv) ide_boards[ide->board]->cur_dev = ((val >> 4) & 1) + (ide->board << 1); ch = ide_boards[ide->board]->cur_dev; + pclog("Current device: %i, ch = %i\n", ide_boards[ide->board]->cur_dev, ch); ide = ide_drives[ch]; ide->selected = 1; @@ -2098,6 +2100,8 @@ ide_readb(uint16_t addr, void *priv) case 0x4: /* Cylinder low */ if (ide->type == IDE_NONE) ret = 0x7f; + else if (ide->type == IDE_ATAPI_SHADOW) + ret = 0x00; else ret = ide->tf->cylinder & 0xff; #if defined(ENABLE_IDE_LOG) && (ENABLE_IDE_LOG == 2) @@ -2109,6 +2113,8 @@ ide_readb(uint16_t addr, void *priv) case 0x5: /* Cylinder high */ if (ide->type == IDE_NONE) ret = 0x7f; + else if (ide->type == IDE_ATAPI_SHADOW) + ret = 0x00; else ret = ide->tf->cylinder >> 8; #if defined(ENABLE_IDE_LOG) && (ENABLE_IDE_LOG == 2) From 2e0a152b3b135c76ded3781178f1e3e23eaacd26 Mon Sep 17 00:00:00 2001 From: OBattler Date: Sun, 28 Sep 2025 04:08:07 +0200 Subject: [PATCH 5/8] IDE: Remove two excess logging lines. --- src/disk/hdc_ide.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/disk/hdc_ide.c b/src/disk/hdc_ide.c index 105cdb3a0..566f255e9 100644 --- a/src/disk/hdc_ide.c +++ b/src/disk/hdc_ide.c @@ -1676,7 +1676,6 @@ ide_writeb(uint16_t addr, uint8_t val, void *priv) break; case 0x6: /* Drive/Head */ - pclog("ch = %i, current = %i\n", ch, ((val >> 4) & 1) + (ide->board << 1)); if (ch != ((val >> 4) & 1) + (ide->board << 1)) { if (!ide->reset && !ide_other->reset && ide->irqstat) { ide_irq_lower(ide); @@ -1685,7 +1684,6 @@ ide_writeb(uint16_t addr, uint8_t val, void *priv) ide_boards[ide->board]->cur_dev = ((val >> 4) & 1) + (ide->board << 1); ch = ide_boards[ide->board]->cur_dev; - pclog("Current device: %i, ch = %i\n", ide_boards[ide->board]->cur_dev, ch); ide = ide_drives[ch]; ide->selected = 1; From 4d35eee630f6c9885bce8cc90a5294417e3dd328 Mon Sep 17 00:00:00 2001 From: OBattler Date: Mon, 29 Sep 2025 00:15:40 +0200 Subject: [PATCH 6/8] FDD: When a drive is set to turbo mode, keep it silent and bring back the fast seeks. --- src/floppy/fdc.c | 10 +++++++++ src/floppy/fdd.c | 50 +++++++++++++++++++++++------------------- src/floppy/fdd_audio.c | 6 ++--- 3 files changed, 40 insertions(+), 26 deletions(-) diff --git a/src/floppy/fdc.c b/src/floppy/fdc.c index 44adc569b..71a03bac2 100644 --- a/src/floppy/fdc.c +++ b/src/floppy/fdc.c @@ -1940,6 +1940,16 @@ fdc_callback(void *priv) case 0x0f: /*Seek*/ fdc->st0 = 0x20 | (fdc->params[0] & 3); fdc->stat = 0x10 | (1 << fdc->rw_drive); + if (fdd_get_turbo(1 << fdc->rw_drive)) { + if (fdc->flags & FDC_FLAG_PCJR) { + fdc->fintr = 1; + fdc->interrupt = -4; + timer_set_delay_u64(&fdc->timer, 1024 * TIMER_USEC); + } else { + fdc->interrupt = -3; + fdc_callback(fdc); + } + } // Interrupts and callbacks in the fdd callback function return; case 0x10: /*Version*/ diff --git a/src/floppy/fdd.c b/src/floppy/fdd.c index 212317f49..d4d9d4476 100644 --- a/src/floppy/fdd.c +++ b/src/floppy/fdd.c @@ -385,31 +385,35 @@ fdd_seek(int drive, int track_diff) fdd_changed[drive] = 0; - /* Trigger appropriate audio for track movements */ - int actual_track_diff = abs(old_track - fdd[drive].track); - if (actual_track_diff == 1) { - /* Single track movement */ - fdd_audio_play_single_track_step(drive, old_track, fdd[drive].track); - } else if (actual_track_diff > 1) { - /* Multi-track seek */ - fdd_audio_play_multi_track_seek(drive, old_track, fdd[drive].track); - } - - if (old_track + track_diff < 0) { + if (fdd[drive].turbo) fdd_do_seek(drive, fdd[drive].track); - return; + else { + /* Trigger appropriate audio for track movements */ + int actual_track_diff = abs(old_track - fdd[drive].track); + if (actual_track_diff == 1) { + /* Single track movement */ + fdd_audio_play_single_track_step(drive, old_track, fdd[drive].track); + } else if (actual_track_diff > 1) { + /* Multi-track seek */ + fdd_audio_play_multi_track_seek(drive, old_track, fdd[drive].track); + } + + if (old_track + track_diff < 0) { + fdd_do_seek(drive, fdd[drive].track); + return; + } + + fdd_seek_in_progress[drive] = 1; + + if (!fdd_seek_timer[drive].callback) { + timer_add(&(fdd_seek_timer[drive]), fdd_seek_complete_callback, &drives[drive], 0); + } + + double initial_seek_time = FDC_FLAG_PCJR & fdd_fdc->flags ? 40000.0 : 15000.0; + double track_seek_time = FDC_FLAG_PCJR & fdd_fdc->flags ? 10000.0 : 6000.0; + uint64_t seek_time_us = (initial_seek_time + (abs(actual_track_diff) * track_seek_time)) * TIMER_USEC; + timer_set_delay_u64(&fdd_seek_timer[drive], seek_time_us); } - - fdd_seek_in_progress[drive] = 1; - - if (!fdd_seek_timer[drive].callback) { - timer_add(&(fdd_seek_timer[drive]), fdd_seek_complete_callback, &drives[drive], 0); - } - - double initial_seek_time = FDC_FLAG_PCJR & fdd_fdc->flags ? 40000.0 : 15000.0; - double track_seek_time = FDC_FLAG_PCJR & fdd_fdc->flags ? 10000.0 : 6000.0; - uint64_t seek_time_us = (initial_seek_time + (abs(actual_track_diff) * track_seek_time)) * TIMER_USEC; - timer_set_delay_u64(&fdd_seek_timer[drive], seek_time_us); } int diff --git a/src/floppy/fdd_audio.c b/src/floppy/fdd_audio.c index 409ddfe8c..afa78002f 100644 --- a/src/floppy/fdd_audio.c +++ b/src/floppy/fdd_audio.c @@ -386,7 +386,7 @@ fdd_audio_close(void) void fdd_audio_set_motor_enable(int drive, int motor_enable) { - if (!fdd_sounds_enabled) + if (!fdd_sounds_enabled || fdd_get_turbo(drive)) return; drive_audio_samples_t *samples = get_drive_samples(drive); @@ -420,7 +420,7 @@ fdd_audio_set_motor_enable(int drive, int motor_enable) void fdd_audio_play_single_track_step(int drive, int from_track, int to_track) { - if (!fdd_sounds_enabled) + if (!fdd_sounds_enabled || fdd_get_turbo(drive)) return; if (drive < 0 || drive >= FDD_NUM) @@ -435,7 +435,7 @@ fdd_audio_play_single_track_step(int drive, int from_track, int to_track) void fdd_audio_play_multi_track_seek(int drive, int from_track, int to_track) { - if (!fdd_sounds_enabled) + if (!fdd_sounds_enabled || fdd_get_turbo(drive)) return; if (drive < 0 || drive >= FDD_NUM) From 5c9fa029bfae0391014a0507d32cda6e42f58db5 Mon Sep 17 00:00:00 2001 From: win2kgamer <47463859+win2kgamer@users.noreply.github.com> Date: Sun, 28 Sep 2025 21:48:33 -0500 Subject: [PATCH 7/8] Implement the VLSI SuperCore/Wildcat chipsets (#6247) * Add the VLSI SuperCore and Wildcat chipsets * Disable logging and minor cleanups * Add the AST Bravo MS P/90 (Rattler) * Add the AT&T Globalyst 620 (NCR 3248) * Add the DEC Celebris 5xx * Add the DFI G586VPM Rev C * Add the Packard Bell PB600 * Fix southbridge PCI Command Register writes * Block the Cyrix 6x86 on incompatible machines * Rename the AT&T Globalyst 620 to include the names of the NCR counterparts * Add the Zeos Pantera Wildcat * Add RZ-1001 variant of the RZ-1000 PCI IDE controller and made the Zeos Pantera Wildcat use it * Add the Micronics M54Si * Update machine_table.c * Re-add new machines to machine table * Update machine inits to use new KBC device names * Use correct machine init method as NVRAM init is done by the chipset code * Use a Phoenix KBC for the AST Bravo since the BIOS calls command D5h to show the KBC revision * Update KBC comments in machine table * Update VLSI 59x chipset comments * Update machine inits for new super I/O code * Reorganize machines and update comments * AST readout device actually has multiple indexed registers * Implement the AST Bravo MS ECP DMA configuration * Implement jumpered/hardwired ECP DMA for the remaining machines * Fix ECP DMA on the AST Bravo MS * Move the DEC Celebris to the Socket 4/5 category * Implement SMI I/O port read, fixes APM init on AT&T, Micronics and Zeos machines * Convert AST readout device to new logging system * Update KBC init to new method * Cleanups --- src/chipset/CMakeLists.txt | 1 + src/chipset/vl82c59x.c | 644 ++++++++++++++++++++++++++++++++ src/device/CMakeLists.txt | 2 + src/device/ast_nvr.c | 180 +++++++++ src/device/ast_readout.c | 198 ++++++++++ src/device/phoenix_486_jumper.c | 30 ++ src/disk/hdc_ide_cmd640.c | 15 + src/disk/hdc_ide_rz1000.c | 20 +- src/include/86box/chipset.h | 8 + src/include/86box/hdc.h | 2 + src/include/86box/machine.h | 17 + src/machine/m_at_socket4_5.c | 32 ++ src/machine/m_at_socket5.c | 162 ++++++++ src/machine/m_at_socket7_3v.c | 33 ++ src/machine/machine_table.c | 320 ++++++++++++++++ 15 files changed, 1663 insertions(+), 1 deletion(-) create mode 100644 src/chipset/vl82c59x.c create mode 100644 src/device/ast_nvr.c create mode 100644 src/device/ast_readout.c diff --git a/src/chipset/CMakeLists.txt b/src/chipset/CMakeLists.txt index 9ccba8dc6..6147a2ccf 100644 --- a/src/chipset/CMakeLists.txt +++ b/src/chipset/CMakeLists.txt @@ -92,5 +92,6 @@ add_library(chipset OBJECT via_apollo.c via_pipc.c vl82c480.c + vl82c59x.c wd76c10.c ) diff --git a/src/chipset/vl82c59x.c b/src/chipset/vl82c59x.c new file mode 100644 index 000000000..0ed6bb0db --- /dev/null +++ b/src/chipset/vl82c59x.c @@ -0,0 +1,644 @@ +/* + * 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. + * + * Implementation of the VLSI SuperCore and Wildcat chipsets. + * + * + * + * Authors: Miran Grca, + * win2kgamer + * + * Copyright 2020-2025 Miran Grca. + * Copyright 2025 win2kgamer + */ + +#ifdef ENABLE_VL82C59X_LOG +#include +#endif + +#include +#include +#include +#include +#include +#define HAVE_STDARG_H +#include <86box/86box.h> +#include "cpu.h" +#include <86box/device.h> +#include <86box/io.h> +#include <86box/timer.h> +#include <86box/apm.h> +#include <86box/machine.h> +#include <86box/pic.h> +#include <86box/pit.h> +#include <86box/pit_fast.h> +#include <86box/plat_unused.h> +#include <86box/mem.h> +#include <86box/nvr.h> +#include <86box/smram.h> +#include <86box/pci.h> +#include <86box/port_92.h> +#include <86box/spd.h> +#include <86box/keyboard.h> +#include <86box/chipset.h> +#include <86box/log.h> + +#ifdef ENABLE_VL82C59X_LOG +int vl82c59x_do_log = ENABLE_VL82C59X_LOG; + +static void +vl82c59x_log(void *priv, const char *fmt, ...) +{ + if (vl82c59x_do_log) { + va_list ap; + va_start(ap, fmt); + log_out(priv, fmt, ap); + va_end(ap); + } +} +#else +# define vl82c59x_log(fmt, ...) +#endif + +typedef struct vl82c59x_t { + uint8_t nb_slot; + uint8_t sb_slot; + uint8_t type; + uint8_t is_compaq; + + uint8_t pci_conf[256]; + uint8_t pci_conf_sb[256]; + + uint16_t pmio; + uint8_t pmio_set; + uint8_t pmreg; + + smram_t *smram[4]; + port_92_t *port_92; + nvr_t *nvr; + + void * log; /* New logging system */ +} vl82c59x_t; + +static int +vl82c59x_shflags(uint8_t access) +{ + int ret = MEM_READ_EXTANY | MEM_WRITE_EXTANY; + + switch (access) { + default: + case 0x00: + ret = MEM_READ_EXTANY | MEM_WRITE_EXTANY; + break; + case 0x01: + ret = MEM_READ_EXTANY | MEM_WRITE_INTERNAL; + break; + case 0x02: + ret = MEM_READ_INTERNAL | MEM_WRITE_EXTANY; + break; + case 0x03: + ret = MEM_READ_INTERNAL | MEM_WRITE_INTERNAL; + break; + } + + return ret; +} + +static void +vl82c59x_recalc(vl82c59x_t *dev) +{ + uint32_t base; + uint8_t access; + + shadowbios = 0; + shadowbios_write = 0; + + for (uint8_t i = 0; i < 4; i++) { + for (uint8_t j = 0; j < 8; j += 2) { + base = 0x000c0000 + (i << 16) + (j << 13); + access = (dev->pci_conf[0x66 + i] >> j) & 3; + mem_set_mem_state_both(base, 0x4000, vl82c59x_shflags(access)); + shadowbios |= ((base >= 0xe0000) && (access & 0x02)); + shadowbios_write |= ((base >= 0xe0000) && (access & 0x01)); + } + } + + flushmmucache(); +} + +static void +vl82c59x_smram(vl82c59x_t *dev) +{ + smram_disable_all(); + + /* A/B region SMRAM seems to not be controlled by 591 reg 0x7C/SMRAM enable */ + /* Dell Dimension BIOS breaks if A0000 region is controlled by SMRAM enable */ + if (dev->pci_conf[0x64] & 0x55) { + smram_enable(dev->smram[1], 0x000a0000, 0x000a0000, 0x10000, dev->pci_conf[0x64] & 0xAA, dev->pci_conf[0x64] & 0x55); + } + if (dev->pci_conf[0x65] & 0x55) { + smram_enable(dev->smram[2], 0x000b0000, 0x000b0000, 0x10000, dev->pci_conf[0x65] & 0xAA, dev->pci_conf[0x65] & 0x55); + } + + /* Handle E region SMRAM */ + if (dev->pci_conf[0x7C] & 0x80) { + if (dev->pci_conf[0x68] & 0x05) { + smram_enable(dev->smram[3], 0x000e0000, 0x000e0000, 0x8000, dev->pci_conf[0x68] & 0x0A, dev->pci_conf[0x68] & 0x05); + } + if (dev->pci_conf[0x68] & 0x50) { + smram_enable(dev->smram[4], 0x000e8000, 0x000e8000, 0x8000, dev->pci_conf[0x68] & 0xA0, dev->pci_conf[0x68] & 0x50); + } + } + + flushmmucache(); +} + +static void +vl82c59x_pm_write(uint16_t addr, uint8_t val, void *priv) +{ + vl82c59x_t *dev = (vl82c59x_t *) priv; + + vl82c59x_log(dev->log, "VL82c593 SMI I/O: [W] (%04X) = %02X\n", addr, val); + + /* Verify SMI Global Enable and Software SMI Enable are set */ + if ((dev->pci_conf_sb[0x6D] & 0x80) && (dev->pci_conf_sb[0x60] & 0x80)) { + dev->pci_conf_sb[0x61] = 0x80; + dev->pmreg = val; + smi_raise(); + } + +} + +static uint8_t +vl82c59x_pm_read(uint16_t addr, void *priv) +{ + vl82c59x_t *dev = (vl82c59x_t *) priv; + uint8_t ret = 0x00; + + ret = dev->pmreg; + vl82c59x_log(dev->log, "VL82c593 SMI I/O: [R] (%04X) = %02X\n", addr, ret); + + return ret; +} + +static void +vl82c59x_set_pm_io(void *priv) +{ + vl82c59x_t *dev = (vl82c59x_t *) priv; + uint8_t highbyte = dev->pci_conf_sb[0x62]; + uint8_t lowbyte = dev->pci_conf_sb[0x63]; + + /* Check for existing I/O mapping and remove it */ + if (dev->pmio_set == 1) { + vl82c59x_log(dev->log, "VL82c59x: Removing SMI IO handler for %04X\n", dev->pmio); + io_removehandler(dev->pmio, 0x0001, vl82c59x_pm_read, NULL, NULL, vl82c59x_pm_write, NULL, NULL, dev); + dev->pmio_set = 0; + } + + if ((highbyte != 0x00) | (lowbyte != 0x00)) { + dev->pmio = ((highbyte << 8) + lowbyte); + vl82c59x_log(dev->log, "VL82c59x: Adding SMI IO handler for %04X\n", dev->pmio); + io_sethandler(dev->pmio, 0x0001, vl82c59x_pm_read, NULL, NULL, vl82c59x_pm_write, NULL, NULL, dev); + dev->pmio_set = 1; + } + +} + +static void +vl82c59x_write(int func, int addr, uint8_t val, void *priv) +{ + vl82c59x_t *dev = (vl82c59x_t *) priv; + + vl82c59x_log(dev->log, "[%04X:%08X] VL82c591: [W] (%02X, %02X) = %02X\n", CS, cpu_state.pc, func, addr, val); + + if (func == 0x00) + switch (addr) { + case 0x04: + case 0x05: /* PCI Command Register */ + dev->pci_conf[addr] = val; + break; + case 0x54: /* Cache Control Register 1 */ + dev->pci_conf[addr] = val; + cpu_cache_ext_enabled = (val & 0xc0); + cpu_update_waitstates(); + break; + case 0x55: /* Cache Control Register 2 */ + dev->pci_conf[addr] = val; + cpu_cache_int_enabled = (val & 0x40); + cpu_update_waitstates(); + break; + case 0x58: /* RAMCFG0 */ + case 0x59: /* RAMCFG1 */ + dev->pci_conf[addr] = val; + break; + case 0x5A: /* Wildcat EDO RAM control */ + if (dev->type == 0x01) { + dev->pci_conf[addr] = val; + } + break; + case 0x5C: /* RAMCTL0 */ + case 0x5D: /* RAMCTL1 */ + case 0x5E: /* RAMCTL2 */ + case 0x5F: + case 0x60: + case 0x62: + /* Apricot XEN-PC Ruby/Jade BIOS requires bit 2 to be set or */ + /* CMOS setup hangs on subsequent runs after NVRAM is initialized */ + dev->pci_conf[addr] = val; + break; + case 0x64: /* A-B SMRAM regs */ + case 0x65: + dev->pci_conf[addr] = val; + vl82c59x_smram(dev); + break; + case 0x66: /* Shadow RAM */ + case 0x67: + case 0x68: + case 0x69: + dev->pci_conf[addr] = val; + vl82c59x_recalc(dev); + vl82c59x_smram(dev); + break; + case 0x6C: /* L2 Cacheability registers */ + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x74: /* Suspected PMRA registers */ + case 0x75: + case 0x76: + case 0x78: + case 0x79: + case 0x7A: + dev->pci_conf[addr] = val; + break; + case 0x7C: /* MISCSSET, bit 7 is SMRAM enable (for the E region) */ + /* io.c logging shows BIOSes setting Bit 7 here */ + dev->pci_conf[addr] = val; + vl82c59x_smram(dev); + break; + case 0x7D: /* Unknown but seems Wildcat-specific, Zeos and PB600 BIOSes hang if bit 3 is writable */ + if (dev->type == 0x01) { + dev->pci_conf[addr] = val & 0xf7; + } + break; + default: + if (addr > 0x3F) + vl82c59x_log(dev->log, "VL82c591: Unknown reg [W] (%02X, %02X) = %02X\n", func, addr, val); + break; + } + +} + +static uint8_t +vl82c59x_read(int func, int addr, void *priv) +{ + const vl82c59x_t *dev = (vl82c59x_t *) priv; + uint8_t ret = 0xff; + + if (func == 0x00) { + switch (addr) { + default: + ret = dev->pci_conf[addr]; + break; + } + } + + vl82c59x_log(dev->log, "[%04X:%08X] VL82c591: [R] (%02X, %02X) = %02X\n", CS, cpu_state.pc, func, addr, ret); + + return ret; +} + +static void +vl82c59x_sb_write(int func, int addr, uint8_t val, void *priv) +{ + vl82c59x_t *dev = (vl82c59x_t *) priv; + uint8_t irq; + const uint8_t irq_array[8] = { 3, 5, 9, 10, 11, 12, 14, 15 }; + + vl82c59x_log(dev->log, "[%04X:%08X] VL82c593: [W] (%02X, %02X) = %02X\n", CS, cpu_state.pc, func, addr, val); + + if (func == 0x00) + switch (addr) { + case 0x04: + case 0x05: /* PCI Command Register */ + dev->pci_conf_sb[addr] = val; + break; + case 0x50: /* MISCSETC */ + case 0x51: /* MISCSETB */ + case 0x52: /* MISCSETA */ + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + /* Has at least one GPIO bit. Compaq Presario 700/900 586 BIOS */ + /* uses bit 2 as an output to set the onboard ES688's base I/O */ + /* address. Bit 2 cleared = 220, bit 2 set = 240 */ + case 0x5C: /* Interrupt Assertion Level Register */ + case 0x5D: + dev->pci_conf_sb[addr] = val; + break; + case 0x60: /* SMI Enable Register */ + dev->pci_conf_sb[addr] = val; + break; + case 0x61: /* SMI Status Register */ + dev->pci_conf_sb[addr] = 0x00; + break; + case 0x62: /* SMI I/O port high byte */ + case 0x63: /* SMI I/O port low byte */ + dev->pci_conf_sb[addr] = val; + vl82c59x_set_pm_io(dev); + break; + case 0x64: /* System Event Enable Register 1 */ + dev->pci_conf_sb[addr] = val; + break; + case 0x65: /* System Event Status Register 1 */ + dev->pci_conf_sb[addr] = 0x00; + break; + case 0x66: /* System Event Enable Register 2 */ + dev->pci_conf_sb[addr] = val; + break; + case 0x67: /* System Event Status Register 2 */ + dev->pci_conf_sb[addr] = 0x00; + break; + case 0x68: /* System Event Enable Register 3 */ + dev->pci_conf_sb[addr] = val; + break; + case 0x69: /* System Event Status Register 3 */ + dev->pci_conf_sb[addr] = 0x00; + break; + case 0x6A: /* PCI Activity Control Register */ + dev->pci_conf_sb[addr] = val & 0x0f; /* Top 4 bits are Read/Clear */ + break; + case 0x6B: /* Programmable I/O Range Register High Byte */ + dev->pci_conf_sb[addr] = val; + break; + case 0x6C: /* Programmable I/O Range Register Low Byte */ + dev->pci_conf_sb[addr] = val; + break; + case 0x6D: /* System Event Control Register/SMI Global Enable */ + dev->pci_conf_sb[addr] = val; + break; + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: /* GPIO */ + /* Compaq Presario and Prolinea use bits 6-4 for setting ECP DMA */ + /* 011 (0x03) = DMA 3 (Default) */ + /* 100 (0x04) = DMA 0 */ + /* 111 (0x07) = DMA disabled */ + case 0x73: /* GPIO */ + dev->pci_conf_sb[addr] = val; + break; + case 0x74: /* PCI Interrupt Connection Register (PCIINT0/1) */ + dev->pci_conf_sb[addr] = val; + irq = irq_array[val & 0x07]; + pci_set_irq_routing(PCI_INTA, (irq != 0) ? irq : PCI_IRQ_DISABLED); + irq = irq_array[(val & 0x70) >> 4]; + pci_set_irq_routing(PCI_INTB, (irq != 0) ? irq : PCI_IRQ_DISABLED); + break; + case 0x75: /* PCI Interrupt Connection Register (PCIINT2/3) */ + dev->pci_conf_sb[addr] = val; + irq = irq_array[val & 0x07]; + pci_set_irq_routing(PCI_INTC, (irq != 0) ? irq : PCI_IRQ_DISABLED); + irq = irq_array[(val & 0x70) >> 4]; + pci_set_irq_routing(PCI_INTD, (irq != 0) ? irq : PCI_IRQ_DISABLED); + break; + case 0x76: /* PCI Interrupt Connection Register (ISA/PCIINT) */ + dev->pci_conf_sb[addr] = val; + break; + case 0x77: + case 0x78: + dev->pci_conf_sb[addr] = val; + break; + default: + if (addr > 0x3F) + vl82c59x_log(dev->log, "VL82c593: Unknown reg [W] (%02X, %02X) = %02X\n", func, addr, val); + break; + } + +} + +static uint8_t +vl82c59x_sb_read(int func, int addr, void *priv) +{ + const vl82c59x_t *dev = (vl82c59x_t *) priv; + uint8_t ret = 0xff; + + if (func == 0x00) + switch (addr) { + case 0x69: /* Lower two bits are a CPU speed readout per Compaq's Prolinea E series TRG */ + /* Per the Prolinea TRG bits 5/3/1 of 593 reg 0x73 must be set to 1 to read the jumpers */ + if (dev->is_compaq && (dev->pci_conf_sb[0x73] & 0x2A)) { + /* Set bit 2 to 1 as this is required for the Prolinea E to be properly identified + in Compaq Computer Setup. */ + ret = (dev->pci_conf_sb[addr] | 0x04); + if (cpu_busspeed <= 50000000) + ret = (ret & 0xfd); /* 50MHz: Bit 1 = 0 */ + else + ret = (ret | 0x02); /* 60MHz: Bit 1 = 1 */ + + if (cpu_dmulti <= 1.5) + ret = (ret | 0x01); /* 1.5x mult: Bit 0 = 1 */ + else + ret = (ret & 0xfe); /* 2.0x mult: Bit 0 = 0 */ + } else { + ret = dev->pci_conf_sb[addr]; + } + break; + default: + ret = dev->pci_conf_sb[addr]; + break; + } + + vl82c59x_log(dev->log, "[%04X:%08X] VL82c593: [R] (%02X, %02X) = %02X\n", CS, cpu_state.pc, func, addr, ret); + + return ret; + +} + +static void +vl82c59x_reset(void *priv) +{ + vl82c59x_t *dev = (vl82c59x_t *) priv; + + /* Northbridge (VLSI VL82c591) */ + dev->pci_conf[0x00] = 0x04; + dev->pci_conf[0x01] = 0x10; + switch (dev->type) { + case 0: /* SuperCore */ + dev->pci_conf[0x02] = 0x05; + dev->pci_conf[0x03] = 0x00; + break; + case 1: /* Wildcat */ + dev->pci_conf[0x02] = 0x07; + dev->pci_conf[0x03] = 0x00; + break; + } + dev->pci_conf[0x08] = 0x00; + dev->pci_conf[0x09] = 0x00; + dev->pci_conf[0x0a] = 0x00; + dev->pci_conf[0x0b] = 0x06; + + /* Southbridge (VLSI VL82c593) */ + dev->pci_conf_sb[0x00] = 0x04; + dev->pci_conf_sb[0x01] = 0x10; + switch (dev->type) { + case 0: /* SuperCore */ + dev->pci_conf_sb[0x02] = 0x06; + dev->pci_conf_sb[0x03] = 0x00; + break; + case 1: /* Wildcat */ + dev->pci_conf_sb[0x02] = 0x08; + dev->pci_conf_sb[0x03] = 0x00; + break; + } + dev->pci_conf_sb[0x08] = 0x00; + dev->pci_conf_sb[0x09] = 0x00; + dev->pci_conf_sb[0x0a] = 0x01; + dev->pci_conf_sb[0x0b] = 0x06; + + /* Unsure on which register configures this (if any), per Compaq's + * Pentium-based Presario 700/900 Series and Prolinea E Series Desktop + * Technical Reference Guides the ISA bus runs at 8MHz while the + * Zeos Pantera Wildcat user manual says that the ISA bus runs at + * 7.5MHz on 90MHz (60MHz bus) systems and 8.25MHz on 100MHz (66MHz bus) + * systems. + */ + if (cpu_busspeed > 50000000) + cpu_set_isa_pci_div(4); + else + cpu_set_isa_pci_div(3); + + pci_set_irq_routing(PCI_INTA, PCI_IRQ_DISABLED); + pci_set_irq_routing(PCI_INTB, PCI_IRQ_DISABLED); + pci_set_irq_routing(PCI_INTC, PCI_IRQ_DISABLED); + pci_set_irq_routing(PCI_INTD, PCI_IRQ_DISABLED); + + vl82c59x_smram(dev); + + /* Reset SMI IO port */ + dev->pmio = 0x0000; + dev->pmio_set = 0; + + cpu_cache_int_enabled = 1; + cpu_cache_ext_enabled = 1; + cpu_update_waitstates(); +} + +static void +vl82c59x_close(void *priv) +{ + vl82c59x_t *dev = (vl82c59x_t *) priv; + + smram_del(dev->smram[1]); + smram_del(dev->smram[2]); + smram_del(dev->smram[3]); + smram_del(dev->smram[4]); + + if (dev->log != NULL) { + log_close(dev->log); + dev->log = NULL; + } + + free(dev); +} + +static void * +vl82c59x_init(UNUSED(const device_t *info)) +{ + vl82c59x_t *dev = (vl82c59x_t *) calloc(1, sizeof(vl82c59x_t)); + + dev->type = (info->local & 0x0f); + + dev->is_compaq = (info->local >> 4); + + dev->log = log_open("VL82c59x"); + + /* VL82c591 (Northbridge) */ + pci_add_card(PCI_ADD_NORTHBRIDGE, vl82c59x_read, vl82c59x_write, dev, &dev->nb_slot); + + /* VL82c593 (Southbridge) */ + pci_add_card(PCI_ADD_SOUTHBRIDGE, vl82c59x_sb_read, vl82c59x_sb_write, dev, &dev->sb_slot); + + dev->port_92 = device_add(&port_92_device); + + /* NVR */ + dev->nvr = device_add(&at_nvr_device); + + dev->smram[1] = smram_add(); + dev->smram[2] = smram_add(); + dev->smram[3] = smram_add(); + dev->smram[4] = smram_add(); + + vl82c59x_reset(dev); + + return dev; +} + +const device_t vl82c59x_device = { + .name = "VLSI VL82c59x (SuperCore)", + .internal_name = "vl82c59x", + .flags = DEVICE_PCI, + .local = 0, + .init = vl82c59x_init, + .close = vl82c59x_close, + .reset = vl82c59x_reset, + .available = NULL, + .speed_changed = NULL, + .force_redraw = NULL, + .config = NULL +}; + +const device_t vl82c59x_compaq_device = { + .name = "VLSI VL82c59x (SuperCore with Compaq readout)", + .internal_name = "vl82c59x_compaq", + .flags = DEVICE_PCI, + .local = 0x10, + .init = vl82c59x_init, + .close = vl82c59x_close, + .reset = vl82c59x_reset, + .available = NULL, + .speed_changed = NULL, + .force_redraw = NULL, + .config = NULL +}; + +const device_t vl82c59x_wildcat_device = { + .name = "VLSI VL82c59x (Wildcat)", + .internal_name = "vl82c59x_wildcat", + .flags = DEVICE_PCI, + .local = 1, + .init = vl82c59x_init, + .close = vl82c59x_close, + .reset = vl82c59x_reset, + .available = NULL, + .speed_changed = NULL, + .force_redraw = NULL, + .config = NULL +}; + +const device_t vl82c59x_wildcat_compaq_device = { + .name = "VLSI VL82c59x (Wildcat with Compaq readout)", + .internal_name = "vl82c59x_wildcat_compaq", + .flags = DEVICE_PCI, + .local = 0x11, + .init = vl82c59x_init, + .close = vl82c59x_close, + .reset = vl82c59x_reset, + .available = NULL, + .speed_changed = NULL, + .force_redraw = NULL, + .config = NULL +}; diff --git a/src/device/CMakeLists.txt b/src/device/CMakeLists.txt index dcad64149..47330955a 100644 --- a/src/device/CMakeLists.txt +++ b/src/device/CMakeLists.txt @@ -18,6 +18,8 @@ add_library(dev OBJECT access_bus.c + ast_nvr.c + ast_readout.c bugger.c cartridge.c cassette.c diff --git a/src/device/ast_nvr.c b/src/device/ast_nvr.c new file mode 100644 index 000000000..21585dbe3 --- /dev/null +++ b/src/device/ast_nvr.c @@ -0,0 +1,180 @@ +/* + * 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. + * + * Implementation of the AST Bravo MS secondary NVR + * + * + * + * Authors: win2kgamer + * + * Copyright 2025 win2kgamer. + */ + +#ifdef ENABLE_AST_NVR_LOG +#include +#endif + +#include +#include +#include +#include +#include +#define HAVE_STDARG_H +#include <86box/86box.h> +#include <86box/machine.h> +#include <86box/device.h> +#include <86box/io.h> +#include <86box/mem.h> +#include <86box/timer.h> +#include <86box/nvr.h> +#include <86box/rom.h> +#include <86box/log.h> + +#ifdef ENABLE_AST_NVR_LOG +int ast_nvr_do_log = ENABLE_AST_NVR_LOG; + +static void +ast_nvr_log(void *priv, const char *fmt, ...) +{ + if (ast_nvr_do_log) { + va_list ap; + va_start(ap, fmt); + log_out(priv, fmt, ap); + va_end(ap); + } +} +#else +# define ast_nvr_log(fmt, ...) +#endif + +typedef struct ast_nvr_t { + int addr; + int bank; + + uint8_t *ram; + int size; + + char *fn; + + void * log; // New logging system +} ast_nvr_t; + +static uint8_t +ast_nvr_read(uint16_t port, void *priv) +{ + ast_nvr_t *nvr = (ast_nvr_t *) priv; + uint8_t ret = 0xff; + + switch (port) { + case 0x800 ... 0x8FF: + nvr->addr = ((nvr->bank << 8) + (port - 0x800)); + ret = nvr->ram[nvr->addr]; + break; + default: + break; + } + + ast_nvr_log(nvr->log, "AST NVR Read [%02X:%02X] = %02X\n", nvr->bank, port, ret); + + return ret; +} + +static void +ast_nvr_write(uint16_t port, uint8_t val, void *priv) +{ + ast_nvr_t *nvr = (ast_nvr_t *) priv; + + ast_nvr_log(nvr->log, "AST NVR Write [%02X:%02X] = %02X\n", nvr->bank, port, val); + + switch (port) { + case 0x800 ... 0x8FF: + nvr->addr = ((nvr->bank << 8) + (port - 0x800)); + nvr->ram[nvr->addr] = val; + break; + case 0xC00: + nvr->bank = val; + default: + break; + } + +} + +static void * +ast_nvr_init(const device_t *info) +{ + ast_nvr_t *nvr; + FILE *fp = NULL; + int c; + + nvr = (ast_nvr_t *) calloc(1, sizeof(ast_nvr_t)); + memset(nvr, 0x00, sizeof(ast_nvr_t)); + + nvr->log = log_open("ASTNVR"); + + nvr->size = 8192; + + /* Set up the NVR file's name */ + c = strlen(machine_get_internal_name()) + 9; + nvr->fn = (char *) calloc(1, (c + 1)); + sprintf(nvr->fn, "%s_sec.nvr", machine_get_internal_name()); + + io_sethandler(0x0800, 0x100, + ast_nvr_read, NULL, NULL, ast_nvr_write, NULL, NULL, nvr); + io_sethandler(0x0C00, 0x01, + ast_nvr_read, NULL, NULL, ast_nvr_write, NULL, NULL, nvr); + + fp = nvr_fopen(nvr->fn, "rb"); + + nvr->ram = (uint8_t *) calloc(1, nvr->size); + memset(nvr->ram, 0xff, nvr->size); + if (fp != NULL) { + if (fread(nvr->ram, 1, nvr->size, fp) != nvr->size) + fatal("ast_nvr_init(): Error reading EEPROM data\n"); + fclose(fp); + } + + return nvr; +} + +static void +ast_nvr_close (void *priv) +{ + ast_nvr_t *nvr = (ast_nvr_t *) priv; + FILE *fp = NULL; + + fp = nvr_fopen(nvr->fn, "wb"); + + if (fp != NULL) { + (void) fwrite(nvr->ram, nvr->size, 1, fp); + fclose(fp); + } + + if (nvr->ram != NULL) + free(nvr->ram); + + if (nvr->log != NULL) { + log_close(nvr->log); + nvr->log = NULL; + } + + free(nvr); +} + +const device_t ast_nvr_device = { + .name = "AST Secondary NVRAM for Bravo MS", + .internal_name = "ast_nvr", + .flags = 0, + .local = 0, + .init = ast_nvr_init, + .close = ast_nvr_close, + .reset = NULL, + .available = NULL, + .speed_changed = NULL, + .force_redraw = NULL, + .config = NULL +}; diff --git a/src/device/ast_readout.c b/src/device/ast_readout.c new file mode 100644 index 000000000..e583ae34d --- /dev/null +++ b/src/device/ast_readout.c @@ -0,0 +1,198 @@ +/* + * 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. + * + * Implementation of the AST Bravo MS jumper readout. + * + * + * + * Authors: win2kgamer + * + * Copyright 2025 win2kgamer + */ + +#ifdef ENABLE_AST_READOUT_LOG +#include +#endif + +#include +#include +#include +#include +#include +#define HAVE_STDARG_H +#include <86box/86box.h> +#include "cpu.h" +#include <86box/timer.h> +#include <86box/io.h> +#include <86box/device.h> +#include <86box/chipset.h> +#include <86box/plat_unused.h> +#include <86box/lpt.h> +#include <86box/machine.h> +#include <86box/log.h> + +/* + The AST readout device has multiple indexed registers that handle + jumper readout, software ECP DMA configuration and other unknown functions. + + Register 0x00: + Bits 6-4 = ECP DMA configuration + 010 (0x02) = DMA 0 + 101 (0x05) = DMA 1 + 111 (0x07) = DMA 3 + + Register 0x03: + Bit 7 = Force flash + Bit 6 = Password disable + Bit 5 = Mono/Color primary video (0=Color/1=Mono) + Bit 4 = Setup disable (0=Enable Setup/1=Disable Setup) + Bit 3 = Enable onboard video (0=Enable/1=Disable) + Bit 2 = ???? + Bit 1 = ???? + Bit 0 = ???? +*/ + +typedef struct ast_readout_t { + uint8_t index; + uint8_t jumper[4]; + + void * log; // New logging system +} ast_readout_t; + +#ifdef ENABLE_AST_READOUT_LOG +int ast_readout_do_log = ENABLE_AST_READOUT_LOG; + +static void +ast_readout_log(void *priv, const char *fmt, ...) +{ + if (ast_readout_do_log) { + va_list ap; + va_start(ap, fmt); + log_out(priv, fmt, ap); + va_end(ap); + } +} +#else +# define ast_readout_log(fmt, ...) +#endif + +static void +ast_readout_write(uint16_t port, uint8_t val, void *priv) +{ + ast_readout_t *dev = (ast_readout_t *) priv; + switch (port) { + case 0xE0: + ast_readout_log(dev->log, "[%04X:%08X] AST Bravo Readout: Set Index %02X\n", CS, cpu_state.pc, val); + dev->index = val; + break; + case 0xE1: + ast_readout_log(dev->log, "[%04X:%08X] AST Bravo Readout: Write %02X:%02X\n", CS, cpu_state.pc, dev->index, val); + if ((dev->index == 0x00) && (!strcmp(machine_get_internal_name(), "bravoms586"))) { + uint8_t dmaval = ((val >> 4) & 0x07); + dev->jumper[dev->index] = val; + switch (dmaval) { + case 0x02: + ast_readout_log(dev->log, "ECP DMA set to 0\n"); + lpt1_dma(0); + break; + case 0x05: + ast_readout_log(dev->log, "ECP DMA set to 1\n"); + lpt1_dma(1); + break; + case 0x07: + ast_readout_log(dev->log, "ECP DMA set to 3\n"); + lpt1_dma(3); + break; + default: + ast_readout_log(dev->log, "Unknown ECP DMA!\n"); + break; + } + } else if (dev->index == 0x03) { + dev->jumper[dev->index] = (val & 0x07); + if (gfxcard[0] != 0x01) + dev->jumper[dev->index] |= 0x08; + } + else + dev->jumper[dev->index] = val; + break; + default: + break; + } +} + +static uint8_t +ast_readout_read(uint16_t port, void *priv) +{ + const ast_readout_t *dev = (ast_readout_t *) priv; + uint8_t ret = 0xff; + + switch (port) { + case 0xE0: + ast_readout_log(dev->log, "[%04X:%08X] AST Bravo Readout: Read Index %02X\n", CS, cpu_state.pc, dev->index); + ret = dev->index; + break; + case 0xE1: + ast_readout_log(dev->log, "[%04X:%08X] AST Bravo Readout: Read %02X:%02X\n", CS, cpu_state.pc, dev->index, dev->jumper[dev->index]); + ret = dev->jumper[dev->index]; + break; + default: + break; + } + return ret; +} + +static void +ast_readout_reset(void *priv) +{ + ast_readout_t *dev = (ast_readout_t *) priv; + + dev->jumper[0x03] = 0x06; + if (gfxcard[0] != 0x01) + dev->jumper[0x03] |= 0x08; +} + +static void +ast_readout_close(void *priv) +{ + ast_readout_t *dev = (ast_readout_t *) priv; + + if (dev->log != NULL) { + log_close(dev->log); + dev->log = NULL; + } + + free(dev); +} + +static void * +ast_readout_init(const device_t *info) +{ + ast_readout_t *dev = (ast_readout_t *) calloc(1, sizeof(ast_readout_t)); + + dev->log = log_open("AST Readout"); + + ast_readout_reset(dev); + + io_sethandler(0x00E0, 0x0002, ast_readout_read, NULL, NULL, ast_readout_write, NULL, NULL, dev); + + return dev; +} + +const device_t ast_readout_device = { + .name = "AST Bravo MS Readout", + .internal_name = "ast_readout", + .flags = 0, + .local = 0, + .init = ast_readout_init, + .close = ast_readout_close, + .reset = ast_readout_reset, + .available = NULL, + .speed_changed = NULL, + .force_redraw = NULL, + .config = NULL +}; diff --git a/src/device/phoenix_486_jumper.c b/src/device/phoenix_486_jumper.c index 1b6270984..ed4349082 100644 --- a/src/device/phoenix_486_jumper.c +++ b/src/device/phoenix_486_jumper.c @@ -40,6 +40,18 @@ Bit 0 = ????. */ +/* + PB600 bit meanings: + Bit 7 = ???? (if 1 BIOS throws beep codes and won't POST) + Bit 6 = Super I/O chip: 1 = disabled, 0 = enabled + Bit 5 = ???? + Bit 4 = ???? + Bit 3 = ???? + Bit 2 = ???? + Bit 1 = Quick Boot: 1 = normal boot, 0 = quick boot/skip POST + Bit 0 = ???? +*/ + typedef struct phoenix_486_jumper_t { uint8_t type; uint8_t jumper; @@ -70,6 +82,8 @@ phoenix_486_jumper_write(UNUSED(uint16_t addr), uint8_t val, void *priv) phoenix_486_jumper_log("Phoenix 486 Jumper: Write %02x\n", val); if (dev->type == 1) dev->jumper = val & 0xbf; + else if (dev->type == 2) /* PB600 */ + dev->jumper = ((val & 0xbf) | 0x02); else dev->jumper = val; } @@ -90,6 +104,8 @@ phoenix_486_jumper_reset(void *priv) if (dev->type == 1) dev->jumper = 0x00; + else if (dev->type == 2) /* PB600 */ + dev->jumper = 0x02; else { dev->jumper = 0x9f; if (gfxcard[0] != 0x01) @@ -146,3 +162,17 @@ const device_t phoenix_486_jumper_pci_device = { .force_redraw = NULL, .config = NULL }; + +const device_t phoenix_486_jumper_pci_pb600_device = { + .name = "Phoenix 486 Jumper Readout (PB600)", + .internal_name = "phoenix_486_jumper_pci_pb600", + .flags = 0, + .local = 2, + .init = phoenix_486_jumper_init, + .close = phoenix_486_jumper_close, + .reset = phoenix_486_jumper_reset, + .available = NULL, + .speed_changed = NULL, + .force_redraw = NULL, + .config = NULL +}; diff --git a/src/disk/hdc_ide_cmd640.c b/src/disk/hdc_ide_cmd640.c index dbdf32dcf..ea2a195d9 100644 --- a/src/disk/hdc_ide_cmd640.c +++ b/src/disk/hdc_ide_cmd640.c @@ -694,3 +694,18 @@ const device_t ide_cmd640_pci_single_channel_sec_device = { .force_redraw = NULL, .config = NULL }; + +const device_t ide_cmd640_pci_single_channel_legacy_only_device = { + .name = "CMD PCI-0640B PCI (Legacy Mode Only)", + .internal_name = "ide_cmd640_pci_single_channel_legacy_only", + .flags = DEVICE_PCI, + .local = 0x20000, + .init = cmd640_init, + .close = cmd640_close, + .reset = cmd640_reset, + .available = NULL, + .speed_changed = NULL, + .force_redraw = NULL, + .config = NULL +}; + diff --git a/src/disk/hdc_ide_rz1000.c b/src/disk/hdc_ide_rz1000.c index 6b7aa68e8..56568cc2c 100644 --- a/src/disk/hdc_ide_rz1000.c +++ b/src/disk/hdc_ide_rz1000.c @@ -51,6 +51,7 @@ typedef struct rz1000_t { int irq_mode[2]; int irq_pin; int irq_line; + uint8_t type; } rz1000_t; static int next_id = 0; @@ -197,9 +198,12 @@ rz1000_reset(void *priv) rz1000_log("dev->local = %08X\n", dev->local); + dev->type = ((dev->local >> 8) & 0x01); + rz1000_log("dev->type = %04X\n", dev->type); + dev->regs[0x00] = 0x42; /* PC Technology */ dev->regs[0x01] = 0x10; - dev->regs[0x02] = 0x00; /* RZ-1000 */ + dev->regs[0x02] = dev->type; /* RZ-1000/RZ-1001 */ dev->regs[0x03] = 0x10; dev->regs[0x04] = 0x00; dev->regs[0x07] = 0x02; /* DEVSEL timing: 01 medium */ @@ -296,3 +300,17 @@ const device_t ide_rz1000_pci_single_channel_device = { .force_redraw = NULL, .config = NULL }; + +const device_t ide_rz1001_pci_device = { + .name = "PC Technology RZ-1001 PCI", + .internal_name = "ide_rz1001_pci", + .flags = DEVICE_PCI, + .local = 0x60100, + .init = rz1000_init, + .close = rz1000_close, + .reset = rz1000_reset, + .available = NULL, + .speed_changed = NULL, + .force_redraw = NULL, + .config = NULL +}; diff --git a/src/include/86box/chipset.h b/src/include/86box/chipset.h index 9cd037d1f..66ae082ef 100644 --- a/src/include/86box/chipset.h +++ b/src/include/86box/chipset.h @@ -214,6 +214,10 @@ extern const device_t via_vt8231_device; /* VLSI */ extern const device_t vl82c480_device; extern const device_t vl82c486_device; +extern const device_t vl82c59x_device; +extern const device_t vl82c59x_compaq_device; +extern const device_t vl82c59x_wildcat_device; +extern const device_t vl82c59x_wildcat_compaq_device; extern const device_t vlsi_scamp_device; /* WD */ @@ -228,6 +232,10 @@ extern const device_t nec_mate_unk_device; extern const device_t phoenix_486_jumper_device; extern const device_t phoenix_486_jumper_pci_device; +extern const device_t phoenix_486_jumper_pci_pb600_device; + +extern const device_t ast_readout_device; +extern const device_t ast_nvr_device; extern const device_t radisys_config_device; diff --git a/src/include/86box/hdc.h b/src/include/86box/hdc.h index 214ed84e5..91a4c68cd 100644 --- a/src/include/86box/hdc.h +++ b/src/include/86box/hdc.h @@ -76,6 +76,7 @@ extern const device_t ide_cmd640_pci_device; /* CMD PCI-640B extern const device_t ide_cmd640_pci_legacy_only_device; /* CMD PCI-640B PCI (Legacy Mode Only) */ extern const device_t ide_cmd640_pci_single_channel_device; /* CMD PCI-640B PCI (Only primary channel) */ extern const device_t ide_cmd640_pci_single_channel_sec_device; /* CMD PCI-640B PCI (Only secondary channel) */ +extern const device_t ide_cmd640_pci_single_channel_legacy_only_device; /* CMD PCI-640B PCI (Legacy Mode Only/Only primary channel) */ extern const device_t ide_cmd646_device; /* CMD PCI-646 */ extern const device_t ide_cmd646_legacy_only_device; /* CMD PCI-646 (Legacy Mode Only) */ extern const device_t ide_cmd646_single_channel_device; /* CMD PCI-646 (Only primary channel) */ @@ -89,6 +90,7 @@ extern const device_t ide_opti611_vlb_sec_device; /* OPTi 82c611/6 extern const device_t ide_rz1000_pci_device; /* PC Technology RZ-1000 PCI */ extern const device_t ide_rz1000_pci_single_channel_device; /* PC Technology RZ-1000 PCI (Only primary channel) */ +extern const device_t ide_rz1001_pci_device; /* PC Technology RZ-1001 PCI */ extern const device_t ide_um8673f_device; /* UMC UM8673F */ extern const device_t ide_um8886af_device; /* UMC UM8886AF */ diff --git a/src/include/86box/machine.h b/src/include/86box/machine.h index b88d8f492..00562d455 100644 --- a/src/include/86box/machine.h +++ b/src/include/86box/machine.h @@ -301,6 +301,8 @@ enum { MACHINE_CHIPSET_VLSI_VL82C480, MACHINE_CHIPSET_VLSI_VL82C481, MACHINE_CHIPSET_VLSI_VL82C486, + MACHINE_CHIPSET_VLSI_SUPERCORE, + MACHINE_CHIPSET_VLSI_WILDCAT, MACHINE_CHIPSET_WD76C10, MACHINE_CHIPSET_ZYMOS_POACH, MACHINE_CHIPSET_MAX @@ -890,6 +892,9 @@ extern int machine_at_ecs50x_init(const machine_t *); /* OPTi 597 */ extern int machine_at_pci56001_init(const machine_t *); +/* VLSI SuperCore */ +extern int machine_at_celebris5xx_init(const machine_t *); + /* m_at_socket5.c */ /* i430NX */ extern int machine_at_p54np4_init(const machine_t *); @@ -930,6 +935,15 @@ extern int machine_at_torino_init(const machine_t *); /* UMC 889x */ extern int machine_at_hot539_init(const machine_t *); +/* VLSI SuperCore */ +extern int machine_at_bravoms586_init(const machine_t *); +extern int machine_at_g586vpmc_init(const machine_t *); +extern int machine_at_m54si_init(const machine_t *); +extern int machine_at_pb600_init(const machine_t *); + +/* VLSI Wildcat */ +extern int machine_at_globalyst620_init(const machine_t *); + /* m_at_socket7_3v.c */ /* i430FX */ #ifdef EMU_DEVICE_H @@ -976,6 +990,9 @@ extern int machine_at_ap5s_init(const machine_t *); extern int machine_at_pc140_6260_init(const machine_t *); extern int machine_at_ms5124_init(const machine_t *); +/* VLSI Wildcat */ +extern int machine_at_zeoswildcat_init(const machine_t *); + /* m_at_socket7.c */ /* i430HX */ extern int machine_at_acerm3a_init(const machine_t *); diff --git a/src/machine/m_at_socket4_5.c b/src/machine/m_at_socket4_5.c index 368f8397f..ccaf06050 100644 --- a/src/machine/m_at_socket4_5.c +++ b/src/machine/m_at_socket4_5.c @@ -70,3 +70,35 @@ machine_at_pci56001_init(const machine_t *model) return ret; } + +/* VLSI SuperCore */ +int +machine_at_celebris5xx_init(const machine_t *model) +{ + int ret; + + ret = bios_load_linear("roms/machines/celebris5xx/CELEBRIS.ROM", + 0x000e0000, 131072, 0); + + if (bios_only || !ret) + return ret; + + machine_at_common_init_ex(model, 2); + + pci_init(PCI_CONFIG_TYPE_1); + pci_register_slot(0x00, PCI_CARD_NORTHBRIDGE, 0, 0, 0, 0); + pci_register_slot(0x01, PCI_CARD_SOUTHBRIDGE, 0, 0, 0, 0); + pci_register_slot(0x08, PCI_CARD_IDE, 4, 1, 2, 3); /* Onboard */ + pci_register_slot(0x09, PCI_CARD_VIDEO, 4, 1, 2, 3); /* Onboard */ + pci_register_slot(0x0B, PCI_CARD_NORMAL, 1, 3, 2, 1); /* Slot 01 */ + pci_register_slot(0x0C, PCI_CARD_NORMAL, 2, 1, 3, 2); /* Slot 02 */ + + device_add(&vl82c59x_device); + device_add(&intel_flash_bxt_device); + device_add_params(machine_get_kbc_device(machine), (void *) model->kbc_params); + device_add_params(&fdc37c6xx_device, (void *) FDC37C665); + device_add(&ide_cmd640_pci_device); + if (gfxcard[0] == VID_INTERNAL) + device_add(machine_get_vid_device(machine)); + +} diff --git a/src/machine/m_at_socket5.c b/src/machine/m_at_socket5.c index cb3803e0d..c61f4c8bb 100644 --- a/src/machine/m_at_socket5.c +++ b/src/machine/m_at_socket5.c @@ -765,3 +765,165 @@ machine_at_hot539_init(const machine_t *model) return ret; } + +/* VLSI SuperCore */ +int +machine_at_bravoms586_init(const machine_t *model) +{ + int ret; + + ret = bios_load_linear("roms/machines/bravoms586/asttest.bin", + 0x000e0000, 131072, 0); + + if (bios_only || !ret) + return ret; + + machine_at_common_init_ex(model, 2); + + pci_init(PCI_CONFIG_TYPE_1); + pci_register_slot(0x00, PCI_CARD_NORTHBRIDGE, 0, 0, 0, 0); + pci_register_slot(0x01, PCI_CARD_SOUTHBRIDGE, 0, 0, 0, 0); + pci_register_slot(0x06, PCI_CARD_IDE, 2, 0, 0, 0); + pci_register_slot(0x08, PCI_CARD_VIDEO, 4, 0, 0, 0); + pci_register_slot(0x09, PCI_CARD_NORMAL, 1, 2, 3, 4); + pci_register_slot(0x0A, PCI_CARD_NORMAL, 2, 3, 4, 1); + pci_register_slot(0x0B, PCI_CARD_NORMAL, 3, 4, 1, 2); + pci_register_slot(0x0C, PCI_CARD_NORMAL, 4, 1, 2, 3); + + device_add(&vl82c59x_device); + device_add(&intel_flash_bxt_device); + device_add_params(machine_get_kbc_device(machine), (void *) model->kbc_params); + device_add_params(&fdc37c6xx_device, (void *) (FDC37C665 | FDC37C6XX_IDE_SEC)); + device_add(&ide_cmd640_pci_single_channel_device); + if (gfxcard[0] == VID_INTERNAL) + device_add(machine_get_vid_device(machine)); + device_add(&ast_readout_device); /* AST custom jumper readout */ + device_add(&ast_nvr_device); /* AST custom secondary NVR device */ + + return ret; +} + +int +machine_at_g586vpmc_init(const machine_t *model) +{ + int ret; + + ret = bios_load_linear("roms/machines/g586vpmc/Vpm_c3.bin", + 0x000e0000, 131072, 0); + + if (bios_only || !ret) + return ret; + + machine_at_common_init_ex(model, 2); + + pci_init(PCI_CONFIG_TYPE_1); + pci_register_slot(0x00, PCI_CARD_NORTHBRIDGE, 0, 0, 0, 0); + pci_register_slot(0x01, PCI_CARD_SOUTHBRIDGE, 0, 0, 0, 0); + pci_register_slot(0x02, PCI_CARD_NORMAL, 1, 2, 3, 4); + pci_register_slot(0x04, PCI_CARD_NORMAL, 2, 3, 4, 1); + pci_register_slot(0x06, PCI_CARD_NORMAL, 3, 4, 1, 2); + pci_register_slot(0x08, PCI_CARD_NORMAL, 4, 1, 2, 3); + pci_register_slot(0x0A, PCI_CARD_IDE, 0, 0, 0, 0); + device_add(&vl82c59x_device); + device_add(&sst_flash_29ee010_device); + device_add_params(machine_get_kbc_device(machine), (void *) model->kbc_params); + device_add_params(&pc873xx_device, (void *) (PC87332 | PCX730X_398)); + device_add(&ide_cmd646_device); + return ret; +} + +int +machine_at_m54si_init(const machine_t *model) +{ + int ret; + + ret = bios_load_linear("roms/machines/m54si/M54SI.03", + 0x000e0000, 131072, 0); + + if (bios_only || !ret) + return ret; + + machine_at_common_init_ex(model, 2); + + pci_init(PCI_CONFIG_TYPE_1); + pci_register_slot(0x00, PCI_CARD_NORTHBRIDGE, 0, 0, 0, 0); + pci_register_slot(0x01, PCI_CARD_SOUTHBRIDGE, 0, 0, 0, 0); + pci_register_slot(0x0D, PCI_CARD_IDE, 0, 0, 0, 0); /* Onboard device */ + pci_register_slot(0x10, PCI_CARD_NORMAL, 1, 2, 3, 4); + pci_register_slot(0x11, PCI_CARD_NORMAL, 2, 3, 4, 1); + pci_register_slot(0x12, PCI_CARD_NORMAL, 3, 4, 1, 2); + /* Slots are a guess since this BIOS won't work with pcireg */ + device_add(&vl82c59x_device); + device_add(&intel_flash_bxt_device); + device_add_params(machine_get_kbc_device(machine), (void *) model->kbc_params); + device_add_params(&fdc37c6xx_device, (void *) (FDC37C665 | FDC37C6XX_IDE_SEC)); + device_add(&ide_cmd640_pci_single_channel_device); + + return ret; +} + +int +machine_at_pb600_init(const machine_t *model) +{ + int ret; + + ret = bios_load_linear("roms/machines/pb600/BIOS.ROM", + 0x000e0000, 131072, 0); + + if (bios_only || !ret) + return ret; + + machine_at_common_init_ex(model, 2); + + pci_init(PCI_CONFIG_TYPE_1); + pci_register_slot(0x00, PCI_CARD_NORTHBRIDGE, 0, 0, 0, 0); + pci_register_slot(0x06, PCI_CARD_SOUTHBRIDGE, 0, 0, 0, 0); + pci_register_slot(0x0A, PCI_CARD_VIDEO, 4, 0, 0, 0); + pci_register_slot(0x0D, PCI_CARD_IDE, 4, 0, 0, 0); + pci_register_slot(0x0B, PCI_CARD_NORMAL, 3, 4, 1, 2); + pci_register_slot(0x11, PCI_CARD_NORMAL, 1, 2, 3, 4); + pci_register_slot(0x13, PCI_CARD_NORMAL, 2, 3, 4, 1); + device_add(&vl82c59x_device); + device_add(&intel_flash_bxt_device); + device_add_params(machine_get_kbc_device(machine), (void *) model->kbc_params); + device_add_params(&fdc37c6xx_device, (void *) FDC37C665); + device_add(&phoenix_486_jumper_pci_pb600_device); + device_add(&ide_cmd640_pci_device); + if (gfxcard[0] == VID_INTERNAL) + device_add(machine_get_vid_device(machine)); + + return ret; +} + +/* VLSI Wildcat */ +int +machine_at_globalyst620_init(const machine_t *model) +{ + int ret; + + ret = bios_load_linear("roms/machines/globalyst620/p107.bin", + 0x000e0000, 131072, 0); + + if (bios_only || !ret) + return ret; + + machine_at_common_init_ex(model, 2); + + pci_init(PCI_CONFIG_TYPE_1); + pci_register_slot(0x00, PCI_CARD_NORTHBRIDGE, 0, 0, 0, 0); + pci_register_slot(0x01, PCI_CARD_SOUTHBRIDGE, 0, 0, 0, 0); + pci_register_slot(0x0F, PCI_CARD_VIDEO, 0, 0, 0, 0); /* Onboard device */ + pci_register_slot(0x10, PCI_CARD_IDE, 0, 0, 0, 0); /* Onboard device */ + pci_register_slot(0x11, PCI_CARD_NORMAL, 1, 2, 3, 4); /* Slot 04 */ + pci_register_slot(0x12, PCI_CARD_NORMAL, 2, 3, 4, 1); /* Slot 05 */ + pci_register_slot(0x13, PCI_CARD_NORMAL, 3, 4, 1, 2); /* Slot 06 */ + device_add(&vl82c59x_wildcat_device); + device_add(&intel_flash_bxt_device); + device_add_params(machine_get_kbc_device(machine), (void *) model->kbc_params); + device_add(&ide_cmd640_pci_single_channel_legacy_only_device); + device_add_params(&fdc37c6xx_device, (void *) (FDC37C665 | FDC37C6XX_IDE_SEC)); + if (gfxcard[0] == VID_INTERNAL) + device_add(machine_get_vid_device(machine)); + + return ret; +} diff --git a/src/machine/m_at_socket7_3v.c b/src/machine/m_at_socket7_3v.c index f289b2275..068782db7 100644 --- a/src/machine/m_at_socket7_3v.c +++ b/src/machine/m_at_socket7_3v.c @@ -1020,3 +1020,36 @@ machine_at_ms5124_init(const machine_t *model) return ret; } + +/* VLSI Wildcat */ +int +machine_at_zeoswildcat_init(const machine_t *model) +{ + int ret; + + ret = bios_load_linear("roms/machines/zeoswildcat/003606.BIN", + 0x000e0000, 131072, 0); + + if (bios_only || !ret) + return ret; + + machine_at_common_init_ex(model, 2); + + pci_init(PCI_CONFIG_TYPE_1); + pci_register_slot(0x00, PCI_CARD_NORTHBRIDGE, 0, 0, 0, 0); + pci_register_slot(0x01, PCI_CARD_SOUTHBRIDGE, 0, 0, 0, 0); + pci_register_slot(0x0D, PCI_CARD_IDE, 1, 2, 0, 0); /* Onboard device */ + pci_register_slot(0x0E, PCI_CARD_SCSI, 1, 0, 0, 0); /* Onboard device */ + pci_register_slot(0x0F, PCI_CARD_NETWORK, 1, 0, 0, 0); /* Onboard device */ + pci_register_slot(0x11, PCI_CARD_NORMAL, 1, 2, 3, 4); /* Slot 03 */ + pci_register_slot(0x12, PCI_CARD_NORMAL, 4, 2, 3, 1); /* Slot 04 */ + pci_register_slot(0x13, PCI_CARD_NORMAL, 3, 4, 1, 2); /* Slot 05 */ + /* Per the machine's manual there was an option for AMD SCSI and/or LAN controllers */ + device_add(&vl82c59x_wildcat_device); + device_add(&intel_flash_bxt_device); + device_add_params(machine_get_kbc_device(machine), (void *) model->kbc_params); + device_add_params(&fdc37c6xx_device, (void *) FDC37C665); + device_add(&ide_rz1001_pci_device); + + return ret; +} diff --git a/src/machine/machine_table.c b/src/machine/machine_table.c index 8c8dc6b91..9fa4ab81b 100644 --- a/src/machine/machine_table.c +++ b/src/machine/machine_table.c @@ -163,6 +163,8 @@ const machine_filter_t machine_chipsets[] = { { "VLSI VL82C480", MACHINE_CHIPSET_VLSI_VL82C480 }, { "VLSI VL82C481", MACHINE_CHIPSET_VLSI_VL82C481 }, { "VLSI VL82C486", MACHINE_CHIPSET_VLSI_VL82C486 }, + { "VLSI SuperCore", MACHINE_CHIPSET_VLSI_SUPERCORE }, + { "VLSI Wildcat", MACHINE_CHIPSET_VLSI_WILDCAT }, { "WD76C10", MACHINE_CHIPSET_WD76C10 } }; @@ -11939,6 +11941,51 @@ const machine_t machines[] = { .snd_device = NULL, .net_device = NULL }, + /* VLSI SuperCore */ + /* This has Phoenix KBC firmware. */ + { + .name = "[VLSI SuperCore] DEC Celebris 5xx", + .internal_name = "celebris5xx", + .type = MACHINE_TYPE_SOCKET4_5, + .chipset = MACHINE_CHIPSET_VLSI_SUPERCORE, + .init = machine_at_celebris5xx_init, + .p1_handler = machine_generic_p1_handler, + .gpio_handler = NULL, + .available_flag = MACHINE_AVAILABLE, + .gpio_acpi_handler = NULL, + .cpu = { + .package = CPU_PKG_SOCKET4 | CPU_PKG_SOCKET5_7, + .block = CPU_BLOCK(CPU_Cx6x86), + .min_bus = 50000000, + .max_bus = 66666667, + .min_voltage = 3520, + .max_voltage = 5000, + .min_multi = 1.0, + .max_multi = 2.0 + }, + .bus_flags = MACHINE_PS2_PCI, + .flags = MACHINE_IDE_DUAL | MACHINE_APM | MACHINE_VIDEO, + .ram = { + .min = 4096, + .max = 131072, + .step = 4096 + }, + .nvrmask = 127, + .jumpered_ecp_dma = MACHINE_DMA_3, + .default_jumpered_ecp_dma = 3, + .kbc_device = &kbc_at_device, + .kbc_params = KBC_VEN_PHOENIX | 0x00021400, /* Guess */ + .kbc_p1 = 0x00000cf0, + .gpio = 0xffffffff, + .gpio_acpi = 0xffffffff, + .device = NULL, + .kbd_device = NULL, + .fdc_device = NULL, + .sio_device = NULL, + .vid_device = &s3_phoenix_vision864_pci_device, + .snd_device = NULL, + .net_device = NULL + }, /* Socket 5 machines */ /* 430NX */ @@ -12879,6 +12926,232 @@ const machine_t machines[] = { .net_device = NULL }, + /* VLSI SuperCore */ + /* This has AST KBC firmware, likely a Phoenix variant since the BIOS */ + /* calls KBC command D5h to read the KBC revision. */ + { + .name = "[VLSI SuperCore] AST Bravo MS P/90", + .internal_name = "bravoms586", + .type = MACHINE_TYPE_SOCKET5, + .chipset = MACHINE_CHIPSET_VLSI_SUPERCORE, + .init = machine_at_bravoms586_init, + .p1_handler = machine_generic_p1_handler, + .gpio_handler = NULL, + .available_flag = MACHINE_AVAILABLE, + .gpio_acpi_handler = NULL, + .cpu = { + .package = CPU_PKG_SOCKET5_7, + .block = CPU_BLOCK(CPU_Cx6x86), + .min_bus = 50000000, + .max_bus = 66666667, + .min_voltage = 3520, + .max_voltage = 3520, + .min_multi = 1.5, + .max_multi = 2.0 + }, + .bus_flags = MACHINE_PS2_PCI, + .flags = MACHINE_IDE_DUAL | MACHINE_APM | MACHINE_VIDEO, + .ram = { + .min = 4096, + .max = 131072, + .step = 4096 + }, + .nvrmask = 127, + .jumpered_ecp_dma = MACHINE_DMA_USE_CONFIG, + .default_jumpered_ecp_dma = -1, + .kbc_device = &kbc_at_device, + .kbc_params = KBC_VEN_PHOENIX | 0x00021400, /* Guess */ + .kbc_p1 = 0x00000cf0, + .gpio = 0xffffffff, + .gpio_acpi = 0xffffffff, + .device = NULL, + .kbd_device = NULL, + .fdc_device = NULL, + .sio_device = NULL, + .vid_device = &gd5434_onboard_pci_device, + .snd_device = NULL, + .net_device = NULL + }, + /* Has a VIA KBC chip */ + { + .name = "[VLSI SuperCore] DFI G586VPM Rev C", + .internal_name = "g586vpmc", + .type = MACHINE_TYPE_SOCKET5, + .chipset = MACHINE_CHIPSET_VLSI_SUPERCORE, + .init = machine_at_g586vpmc_init, + .p1_handler = machine_generic_p1_handler, + .gpio_handler = NULL, + .available_flag = MACHINE_AVAILABLE, + .gpio_acpi_handler = NULL, + .cpu = { + .package = CPU_PKG_SOCKET5_7, + .block = CPU_BLOCK_NONE, + .min_bus = 50000000, + .max_bus = 66666667, + .min_voltage = 3520, + .max_voltage = 3520, + .min_multi = 1.5, + .max_multi = 2.0 + }, + .bus_flags = MACHINE_PS2_PCI, + .flags = MACHINE_IDE_DUAL | MACHINE_APM, + .ram = { + .min = 4096, + .max = 262144, + .step = 4096 + }, + .nvrmask = 127, + .jumpered_ecp_dma = MACHINE_DMA_1 | MACHINE_DMA_3, + .default_jumpered_ecp_dma = 1, + .kbc_device = &kbc_at_device, + .kbc_params = KBC_VEN_VIA | 0x00424600, /* Guess */ + .kbc_p1 = 0x00000cf0, + .gpio = 0xffffffff, + .gpio_acpi = 0xffffffff, + .device = NULL, + .kbd_device = NULL, + .fdc_device = NULL, + .sio_device = NULL, + .vid_device = NULL, + .snd_device = NULL, + .net_device = NULL + }, + /* KBC firmware is unknown. No commands outside of the base PS/2 */ + /* KBC command set are used. */ + { + .name = "[VLSI SuperCore] Micronics M54Si", + .internal_name = "m54si", + .type = MACHINE_TYPE_SOCKET5, + .chipset = MACHINE_CHIPSET_VLSI_SUPERCORE, + .init = machine_at_m54si_init, + .p1_handler = machine_generic_p1_handler, + .gpio_handler = NULL, + .available_flag = MACHINE_AVAILABLE, + .gpio_acpi_handler = NULL, + .cpu = { + .package = CPU_PKG_SOCKET5_7, + .block = CPU_BLOCK(CPU_Cx6x86), + .min_bus = 50000000, + .max_bus = 66666667, + .min_voltage = 3520, + .max_voltage = 3520, + .min_multi = 1.5, + .max_multi = 2.0 + }, + .bus_flags = MACHINE_PS2_PCI, + .flags = MACHINE_IDE_DUAL | MACHINE_APM, + .ram = { + .min = 4096, + .max = 131072, + .step = 4096 + }, + .nvrmask = 127, + .jumpered_ecp_dma = MACHINE_DMA_DISABLED | MACHINE_DMA_1 | MACHINE_DMA_3, + .default_jumpered_ecp_dma = 4, + .kbc_device = &kbc_at_device, + .kbc_params = KBC_VEN_PHOENIX | 0x00021400, /* Guess */ + .kbc_p1 = 0x00000cf0, + .gpio = 0xffffffff, + .gpio_acpi = 0xffffffff, + .device = NULL, + .kbd_device = NULL, + .fdc_device = NULL, + .sio_device = NULL, + .vid_device = NULL, + .snd_device = NULL, + .net_device = NULL + }, + /* This has Phoenix KBC firmware. */ + { + .name = "[VLSI SuperCore] Packard Bell PB600", + .internal_name = "pb600", + .type = MACHINE_TYPE_SOCKET5, + .chipset = MACHINE_CHIPSET_VLSI_SUPERCORE, + .init = machine_at_pb600_init, + .p1_handler = machine_generic_p1_handler, + .gpio_handler = NULL, + .available_flag = MACHINE_AVAILABLE, + .gpio_acpi_handler = NULL, + .cpu = { + .package = CPU_PKG_SOCKET5_7, + .block = CPU_BLOCK_NONE, + .min_bus = 50000000, + .max_bus = 66666667, + .min_voltage = 3520, + .max_voltage = 3520, + .min_multi = 1.5, + .max_multi = 2.0 + }, + .bus_flags = MACHINE_PS2_PCI, + .flags = MACHINE_IDE_DUAL | MACHINE_APM | MACHINE_VIDEO, + .ram = { + .min = 8192, + .max = 139264, + .step = 4096 + }, + .nvrmask = 127, + .jumpered_ecp_dma = MACHINE_DMA_1 | MACHINE_DMA_3, + .default_jumpered_ecp_dma = 3, + .kbc_device = &kbc_at_device, + .kbc_params = KBC_VEN_PHOENIX | 0x00012900, /* Guess */ + .kbc_p1 = 0x00000cf0, + .gpio = 0xffffffff, + .gpio_acpi = 0xffffffff, + .device = NULL, + .kbd_device = NULL, + .fdc_device = NULL, + .sio_device = NULL, + .vid_device = &gd5430_onboard_pci_device, + .snd_device = NULL, + .net_device = NULL + }, + + /* VLSI Wildcat */ + /* This has Phoenix KBC firmware. */ + { + .name = "[VLSI Wildcat] AT&T Globalyst 620/630 (NCR 3248/3348)", + .internal_name = "globalyst620", + .type = MACHINE_TYPE_SOCKET5, + .chipset = MACHINE_CHIPSET_VLSI_WILDCAT, + .init = machine_at_globalyst620_init, + .p1_handler = machine_generic_p1_handler, + .gpio_handler = NULL, + .available_flag = MACHINE_AVAILABLE, + .gpio_acpi_handler = NULL, + .cpu = { + .package = CPU_PKG_SOCKET5_7, + .block = CPU_BLOCK(CPU_Cx6x86), + .min_bus = 50000000, + .max_bus = 66666667, + .min_voltage = 3520, + .max_voltage = 3520, + .min_multi = 1.5, + .max_multi = 2.0 + }, + .bus_flags = MACHINE_PS2_PCI, + .flags = MACHINE_IDE_DUAL | MACHINE_APM | MACHINE_VIDEO, + .ram = { + .min = 4096, + .max = 196608, + .step = 4096 + }, + .nvrmask = 127, + .jumpered_ecp_dma = MACHINE_DMA_3, + .default_jumpered_ecp_dma = 3, + .kbc_device = &kbc_at_device, + .kbc_params = KBC_VEN_PHOENIX | 0x00012900, /* Guess */ + .kbc_p1 = 0x00000cf0, + .gpio = 0xffffffff, + .gpio_acpi = 0xffffffff, + .device = NULL, + .kbd_device = NULL, + .fdc_device = NULL, + .sio_device = NULL, + .vid_device = &s3_phoenix_trio64_onboard_pci_device, + .snd_device = NULL, + .net_device = NULL + }, + /* Socket 7 (Single Voltage) machines */ /* 430FX */ /* This has an AMIKey-2, which is type 'H'. @@ -13736,6 +14009,53 @@ const machine_t machines[] = { .net_device = NULL }, + /* VLSI Wildcat */ + /* KBC firmware is unknown. No PS/2 port is present and no commands outside */ + /* of the base AT KBC command set are used. */ + { + .name = "[VLSI Wildcat] Zeos Pantera Wildcat", + .internal_name = "zeoswildcat", + .type = MACHINE_TYPE_SOCKET7_3V, + .chipset = MACHINE_CHIPSET_VLSI_WILDCAT, + .init = machine_at_zeoswildcat_init, + .p1_handler = machine_generic_p1_handler, + .gpio_handler = NULL, + .available_flag = MACHINE_AVAILABLE, + .gpio_acpi_handler = NULL, + .cpu = { + .package = CPU_PKG_SOCKET5_7, + .block = CPU_BLOCK(CPU_Cx6x86, CPU_PENTIUMMMX), + .min_bus = 50000000, + .max_bus = 66666667, + .min_voltage = 3520, + .max_voltage = 3520, + .min_multi = 1.5, + .max_multi = 2.5 + }, + .bus_flags = MACHINE_PCI, + .flags = MACHINE_IDE_DUAL | MACHINE_APM, + .ram = { + .min = 4096, + .max = 393216, + .step = 4096 + }, + .nvrmask = 127, + .jumpered_ecp_dma = MACHINE_DMA_3, + .default_jumpered_ecp_dma = 3, + .kbc_device = &kbc_at_device, + .kbc_params = 0x00000000, + .kbc_p1 = 0x000004f0, + .gpio = 0xffffffff, + .gpio_acpi = 0xffffffff, + .device = NULL, + .kbd_device = NULL, + .fdc_device = NULL, + .sio_device = NULL, + .vid_device = NULL, + .snd_device = NULL, + .net_device = NULL + }, + /* Socket 7 (Dual Voltage) machines */ /* ALi ALADDiN IV+ */ /* Has the ALi M1543 southbridge with on-chip KBC. */ From 4fb6e3aaacdcb17a104397b2722887a0aa6381ae Mon Sep 17 00:00:00 2001 From: win2kgamer <47463859+win2kgamer@users.noreply.github.com> Date: Sun, 28 Sep 2025 22:56:46 -0500 Subject: [PATCH 8/8] Fix warnings in vl82c59x.c and m_at_socket4_5.c (#6249) --- src/chipset/vl82c59x.c | 12 ++++++------ src/machine/m_at_socket4_5.c | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/chipset/vl82c59x.c b/src/chipset/vl82c59x.c index 0ed6bb0db..468978718 100644 --- a/src/chipset/vl82c59x.c +++ b/src/chipset/vl82c59x.c @@ -139,19 +139,19 @@ vl82c59x_smram(vl82c59x_t *dev) /* A/B region SMRAM seems to not be controlled by 591 reg 0x7C/SMRAM enable */ /* Dell Dimension BIOS breaks if A0000 region is controlled by SMRAM enable */ if (dev->pci_conf[0x64] & 0x55) { - smram_enable(dev->smram[1], 0x000a0000, 0x000a0000, 0x10000, dev->pci_conf[0x64] & 0xAA, dev->pci_conf[0x64] & 0x55); + smram_enable(dev->smram[0], 0x000a0000, 0x000a0000, 0x10000, dev->pci_conf[0x64] & 0xAA, dev->pci_conf[0x64] & 0x55); } if (dev->pci_conf[0x65] & 0x55) { - smram_enable(dev->smram[2], 0x000b0000, 0x000b0000, 0x10000, dev->pci_conf[0x65] & 0xAA, dev->pci_conf[0x65] & 0x55); + smram_enable(dev->smram[1], 0x000b0000, 0x000b0000, 0x10000, dev->pci_conf[0x65] & 0xAA, dev->pci_conf[0x65] & 0x55); } /* Handle E region SMRAM */ if (dev->pci_conf[0x7C] & 0x80) { if (dev->pci_conf[0x68] & 0x05) { - smram_enable(dev->smram[3], 0x000e0000, 0x000e0000, 0x8000, dev->pci_conf[0x68] & 0x0A, dev->pci_conf[0x68] & 0x05); + smram_enable(dev->smram[2], 0x000e0000, 0x000e0000, 0x8000, dev->pci_conf[0x68] & 0x0A, dev->pci_conf[0x68] & 0x05); } if (dev->pci_conf[0x68] & 0x50) { - smram_enable(dev->smram[4], 0x000e8000, 0x000e8000, 0x8000, dev->pci_conf[0x68] & 0xA0, dev->pci_conf[0x68] & 0x50); + smram_enable(dev->smram[3], 0x000e8000, 0x000e8000, 0x8000, dev->pci_conf[0x68] & 0xA0, dev->pci_conf[0x68] & 0x50); } } @@ -542,10 +542,10 @@ vl82c59x_close(void *priv) { vl82c59x_t *dev = (vl82c59x_t *) priv; + smram_del(dev->smram[0]); smram_del(dev->smram[1]); smram_del(dev->smram[2]); smram_del(dev->smram[3]); - smram_del(dev->smram[4]); if (dev->log != NULL) { log_close(dev->log); @@ -577,10 +577,10 @@ vl82c59x_init(UNUSED(const device_t *info)) /* NVR */ dev->nvr = device_add(&at_nvr_device); + dev->smram[0] = smram_add(); dev->smram[1] = smram_add(); dev->smram[2] = smram_add(); dev->smram[3] = smram_add(); - dev->smram[4] = smram_add(); vl82c59x_reset(dev); diff --git a/src/machine/m_at_socket4_5.c b/src/machine/m_at_socket4_5.c index ccaf06050..f5b7ae87c 100644 --- a/src/machine/m_at_socket4_5.c +++ b/src/machine/m_at_socket4_5.c @@ -101,4 +101,5 @@ machine_at_celebris5xx_init(const machine_t *model) if (gfxcard[0] == VID_INTERNAL) device_add(machine_get_vid_device(machine)); + return ret; }