Switched the OPL and NukedOPL code to VARCem's reworked (and cleaner) versions.
This commit is contained in:
@@ -1,222 +1,284 @@
|
||||
/* Copyright holders: Sarah Walker, SA1988
|
||||
see COPYING for more details
|
||||
*/
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Interface to the actual OPL emulator.
|
||||
*
|
||||
* TODO: Finish re-working this into a device_t, which requires a
|
||||
* poll-like function for "update" so the sound card can call
|
||||
* that and get a buffer-full of sample data.
|
||||
*
|
||||
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
|
||||
* Miran Grca, <mgrca8@gmail.com>
|
||||
* TheCollector1995, <mariogplayer@gmail.com>
|
||||
* Sarah Walker, <tommowalker@tommowalker.co.uk>
|
||||
*
|
||||
* Copyright 2017-2020 Fred N. van Kempen.
|
||||
* Copyright 2016-2019 Miran Grca.
|
||||
* Copyright 2008-2018 Sarah Walker.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <wchar.h>
|
||||
#define dbglog sound_card_log
|
||||
#include <86box/86box.h>
|
||||
#include <86box/timer.h>
|
||||
#include "cpu.h"
|
||||
#include <86box/io.h>
|
||||
#include <86box/timer.h>
|
||||
#include <86box/sound.h>
|
||||
#include <86box/snd_opl.h>
|
||||
#include <86box/snd_opl_backend.h>
|
||||
#include <86box/snd_opl_nuked.h>
|
||||
|
||||
|
||||
/*Interfaces between 86Box and the actual OPL emulator*/
|
||||
enum {
|
||||
STATUS_TIMER_1 = 0x40,
|
||||
STATUS_TIMER_2 = 0x20,
|
||||
STATUS_TIMER_ALL = 0x80
|
||||
};
|
||||
|
||||
enum {
|
||||
CTRL_IRQ_RESET = 0x80,
|
||||
CTRL_TIMER1_MASK = 0x40,
|
||||
CTRL_TIMER2_MASK = 0x20,
|
||||
CTRL_TIMER2_CTRL = 0x02,
|
||||
CTRL_TIMER1_CTRL = 0x01
|
||||
};
|
||||
|
||||
|
||||
uint8_t
|
||||
opl2_read(uint16_t a, void *priv)
|
||||
static void
|
||||
status_update(opl_t *dev)
|
||||
{
|
||||
opl_t *opl = (opl_t *)priv;
|
||||
|
||||
sub_cycles((int) (isa_timing * 8));
|
||||
opl2_update2(opl);
|
||||
|
||||
return opl_read(0, a);
|
||||
if (dev->status & (STATUS_TIMER_1 | STATUS_TIMER_2) & dev->status_mask)
|
||||
dev->status |= STATUS_TIMER_ALL;
|
||||
else
|
||||
dev->status &= ~STATUS_TIMER_ALL;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
opl2_write(uint16_t a, uint8_t v, void *priv)
|
||||
static void
|
||||
timer_set(opl_t *dev, int timer, uint64_t period)
|
||||
{
|
||||
opl_t *opl = (opl_t *)priv;
|
||||
|
||||
opl2_update2(opl);
|
||||
opl_write(0, a, v);
|
||||
opl_write(1, a, v);
|
||||
timer_on_auto(&dev->timers[timer], ((double) period) * 20.0);
|
||||
}
|
||||
|
||||
|
||||
uint8_t
|
||||
opl2_l_read(uint16_t a, void *priv)
|
||||
static void
|
||||
timer_over(opl_t *dev, int tmr)
|
||||
{
|
||||
opl_t *opl = (opl_t *)priv;
|
||||
if (tmr) {
|
||||
dev->status |= STATUS_TIMER_2;
|
||||
timer_set(dev, 1, dev->timer[1] * 16);
|
||||
} else {
|
||||
dev->status |= STATUS_TIMER_1;
|
||||
timer_set(dev, 0, dev->timer[0] * 4);
|
||||
}
|
||||
|
||||
sub_cycles((int)(isa_timing * 8));
|
||||
opl2_update2(opl);
|
||||
|
||||
return opl_read(0, a);
|
||||
status_update(dev);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
opl2_l_write(uint16_t a, uint8_t v, void *priv)
|
||||
static void
|
||||
timer_1(void *priv)
|
||||
{
|
||||
opl_t *opl = (opl_t *)priv;
|
||||
opl_t *dev = (opl_t *)priv;
|
||||
|
||||
opl2_update2(opl);
|
||||
opl_write(0, a, v);
|
||||
timer_over(dev, 0);
|
||||
}
|
||||
|
||||
|
||||
uint8_t
|
||||
opl2_r_read(uint16_t a, void *priv)
|
||||
static void
|
||||
timer_2(void *priv)
|
||||
{
|
||||
opl_t *opl = (opl_t *)priv;
|
||||
opl_t *dev = (opl_t *)priv;
|
||||
|
||||
sub_cycles((int)(isa_timing * 8));
|
||||
opl2_update2(opl);
|
||||
|
||||
return opl_read(1, a);
|
||||
timer_over(dev, 1);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
opl2_r_write(uint16_t a, uint8_t v, void *priv)
|
||||
static uint8_t
|
||||
opl_read(opl_t *dev, uint16_t port)
|
||||
{
|
||||
opl_t *opl = (opl_t *)priv;
|
||||
if (! (port & 1))
|
||||
return((dev->status & dev->status_mask) | (dev->is_opl3 ? 0x00 : 0x06));
|
||||
|
||||
opl2_update2(opl);
|
||||
opl_write(1, a, v);
|
||||
if (dev->is_opl3 && ((port & 0x03) == 0x03))
|
||||
return(0x00);
|
||||
|
||||
return(dev->is_opl3 ? 0x00 : 0xff);
|
||||
}
|
||||
|
||||
|
||||
uint8_t
|
||||
opl3_read(uint16_t a, void *priv)
|
||||
static void
|
||||
opl_write(opl_t *dev, uint16_t port, uint8_t val)
|
||||
{
|
||||
opl_t *opl = (opl_t *)priv;
|
||||
if (! (port & 1)) {
|
||||
dev->port = nuked_write_addr(dev->opl, port, val) & 0x01ff;
|
||||
|
||||
sub_cycles((int)(isa_timing * 8));
|
||||
opl3_update2(opl);
|
||||
if (! dev->is_opl3)
|
||||
dev->port &= 0x00ff;
|
||||
|
||||
return opl_read(0, a);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
nuked_write_reg_buffered(dev->opl, dev->port, val);
|
||||
|
||||
void
|
||||
opl3_write(uint16_t a, uint8_t v, void *priv)
|
||||
{
|
||||
opl_t *opl = (opl_t *)priv;
|
||||
|
||||
opl3_update2(opl);
|
||||
opl_write(0, a, v);
|
||||
}
|
||||
switch (dev->port) {
|
||||
case 0x02: // timer 1
|
||||
dev->timer[0] = 256 - val;
|
||||
break;
|
||||
|
||||
case 0x03: // timer 2
|
||||
dev->timer[1] = 256 - val;
|
||||
break;
|
||||
|
||||
void
|
||||
opl2_update2(opl_t *opl)
|
||||
{
|
||||
if (opl->pos < sound_pos_global) {
|
||||
opl2_update(0, &opl->buffer[opl->pos << 1], sound_pos_global - opl->pos);
|
||||
opl2_update(1, &opl->buffer2[opl->pos << 1], sound_pos_global - opl->pos);
|
||||
for (; opl->pos < sound_pos_global; opl->pos++) {
|
||||
opl->buffer[(opl->pos << 1) + 1] = opl->buffer2[(opl->pos << 1) + 1];
|
||||
opl->buffer[opl->pos << 1] = (opl->buffer[opl->pos << 1] / 2);
|
||||
opl->buffer[(opl->pos << 1) + 1] = (opl->buffer[(opl->pos << 1) + 1] / 2);
|
||||
}
|
||||
case 0x04: // timer control
|
||||
if (val & CTRL_IRQ_RESET) {
|
||||
dev->status &= ~(STATUS_TIMER_1 | STATUS_TIMER_2);
|
||||
status_update(dev);
|
||||
return;
|
||||
}
|
||||
if ((val ^ dev->timer_ctrl) & CTRL_TIMER1_CTRL) {
|
||||
if (val & CTRL_TIMER1_CTRL)
|
||||
timer_set(dev, 0, dev->timer[0] * 4);
|
||||
else
|
||||
timer_set(dev, 0, 0);
|
||||
}
|
||||
if ((val ^ dev->timer_ctrl) & CTRL_TIMER2_CTRL) {
|
||||
if (val & CTRL_TIMER2_CTRL)
|
||||
timer_set(dev, 1, dev->timer[1] * 16);
|
||||
else
|
||||
timer_set(dev, 1, 0);
|
||||
}
|
||||
dev->status_mask = (~val & (CTRL_TIMER1_MASK | CTRL_TIMER2_MASK)) | 0x80;
|
||||
dev->timer_ctrl = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
opl3_update2(opl_t *opl)
|
||||
static void
|
||||
opl_init(opl_t *dev, int is_opl3)
|
||||
{
|
||||
if (opl->pos < sound_pos_global) {
|
||||
opl3_update(0, &opl->buffer[(opl->pos << 1)], sound_pos_global - opl->pos);
|
||||
for (; opl->pos < sound_pos_global; opl->pos++) {
|
||||
opl->buffer[opl->pos << 1] = (opl->buffer[opl->pos << 1] / 2);
|
||||
opl->buffer[(opl->pos << 1) + 1] = (opl->buffer[(opl->pos << 1) + 1] / 2);
|
||||
}
|
||||
memset(dev, 0x00, sizeof(opl_t));
|
||||
dev->is_opl3 = is_opl3;
|
||||
|
||||
/* Create a NukedOPL object. */
|
||||
dev->opl = nuked_init(48000);
|
||||
|
||||
timer_add(&dev->timers[0], timer_1, dev, 0);
|
||||
timer_add(&dev->timers[1], timer_2, dev, 0);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
opl_close(opl_t *dev)
|
||||
{
|
||||
/* Release the NukedOPL object. */
|
||||
if (dev->opl) {
|
||||
nuked_close(dev->opl);
|
||||
dev->opl = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ym3812_timer_set_0(void *param, int timer, uint64_t period)
|
||||
uint8_t
|
||||
opl2_read(uint16_t port, void *priv)
|
||||
{
|
||||
opl_t *opl = (opl_t *)param;
|
||||
opl_t *dev = (opl_t *)priv;
|
||||
|
||||
if (period)
|
||||
timer_set_delay_u64(&opl->timers[0][timer], period * TIMER_USEC * 20);
|
||||
else
|
||||
timer_disable(&opl->timers[0][timer]);
|
||||
cycles -= ISA_CYCLES(8);
|
||||
|
||||
opl2_update(dev);
|
||||
|
||||
return(opl_read(dev, port));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ym3812_timer_set_1(void *param, int timer, uint64_t period)
|
||||
opl2_write(uint16_t port, uint8_t val, void *priv)
|
||||
{
|
||||
opl_t *opl = (opl_t *)param;
|
||||
opl_t *dev = (opl_t *)priv;
|
||||
|
||||
if (period)
|
||||
timer_set_delay_u64(&opl->timers[1][timer], period * TIMER_USEC * 20);
|
||||
else
|
||||
timer_disable(&opl->timers[1][timer]);
|
||||
}
|
||||
opl2_update(dev);
|
||||
|
||||
|
||||
void
|
||||
ymf262_timer_set(void *param, int timer, uint64_t period)
|
||||
{
|
||||
opl_t *opl = (opl_t *)param;
|
||||
|
||||
if (period)
|
||||
timer_set_delay_u64(&opl->timers[0][timer], period * TIMER_USEC * 20);
|
||||
else
|
||||
timer_disable(&opl->timers[0][timer]);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
opl_timer_callback00(void *p)
|
||||
{
|
||||
opl_timer_over(0, 0);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
opl_timer_callback01(void *p)
|
||||
{
|
||||
opl_timer_over(0, 1);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
opl_timer_callback10(void *p)
|
||||
{
|
||||
opl_timer_over(1, 0);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
opl_timer_callback11(void *p)
|
||||
{
|
||||
opl_timer_over(1, 1);
|
||||
opl_write(dev, port, val);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
opl2_init(opl_t *opl)
|
||||
opl2_init(opl_t *dev)
|
||||
{
|
||||
opl_init(ym3812_timer_set_0, opl, 0, 0);
|
||||
opl_init(ym3812_timer_set_1, opl, 1, 0);
|
||||
|
||||
timer_add(&opl->timers[0][0], opl_timer_callback00, (void *)opl, 0);
|
||||
timer_add(&opl->timers[0][1], opl_timer_callback01, (void *)opl, 0);
|
||||
timer_add(&opl->timers[1][0], opl_timer_callback10, (void *)opl, 0);
|
||||
timer_add(&opl->timers[1][1], opl_timer_callback11, (void *)opl, 0);
|
||||
opl_init(dev, 0);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
opl3_init(opl_t *opl)
|
||||
opl2_update(opl_t *dev)
|
||||
{
|
||||
opl_init(ymf262_timer_set, opl, 0, 1);
|
||||
if (dev->pos >= sound_pos_global)
|
||||
return;
|
||||
|
||||
timer_add(&opl->timers[0][0], opl_timer_callback00, (void *)opl, 0);
|
||||
timer_add(&opl->timers[0][1], opl_timer_callback01, (void *)opl, 0);
|
||||
nuked_generate_stream(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] = dev->buffer[dev->pos * 2];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint8_t
|
||||
opl3_read(uint16_t port, void *priv)
|
||||
{
|
||||
opl_t *dev = (opl_t *)priv;
|
||||
|
||||
cycles -= ISA_CYCLES(8);
|
||||
|
||||
opl3_update(dev);
|
||||
|
||||
return(opl_read(dev, port));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
opl3_write(uint16_t port, uint8_t val, void *priv)
|
||||
{
|
||||
opl_t *dev = (opl_t *)priv;
|
||||
|
||||
opl3_update(dev);
|
||||
|
||||
opl_write(dev, port, val);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
opl3_init(opl_t *dev)
|
||||
{
|
||||
opl_init(dev, 1);
|
||||
}
|
||||
|
||||
|
||||
/* API to sound interface. */
|
||||
void
|
||||
opl3_update(opl_t *dev)
|
||||
{
|
||||
if (dev->pos >= sound_pos_global)
|
||||
return;
|
||||
|
||||
nuked_generate_stream(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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user