Merge pull request #3823 from 86Box/version/4.1

Merge Version/4.1 into master since we have decided to just go straight for 4.1
This commit is contained in:
Miran Grča
2023-11-11 16:12:16 +01:00
committed by GitHub
45 changed files with 8883 additions and 571 deletions

View File

@@ -35,7 +35,7 @@ if(MUNT_EXTERNAL)
endif()
project(86Box
VERSION 4.0.2
VERSION 4.1
DESCRIPTION "Emulator of x86-based systems"
HOMEPAGE_URL "https://86box.net"
LANGUAGES C CXX)

2
debian/changelog vendored
View File

@@ -1,4 +1,4 @@
86box (4.0.2) UNRELEASED; urgency=medium
86box (4.1) UNRELEASED; urgency=medium
* Bump release.

View File

@@ -1625,7 +1625,10 @@ acpi_reset(void *priv)
acpi_t *dev = (acpi_t *) priv;
memset(&dev->regs, 0x00, sizeof(acpi_regs_t));
dev->regs.gpireg[0] = 0xff;
/* PC Chips M773:
- Bit 3: 80-conductor cable on unknown IDE channel (active low)
- Bit 1: 80-conductor cable on unknown IDE channel (active low) */
dev->regs.gpireg[0] = !strcmp(machine_get_internal_name(), "m773") ? 0xf5 : 0xff;
dev->regs.gpireg[1] = 0xff;
/* A-Trend ATC7020BXII:
- Bit 3: 80-conductor cable on secondary IDE channel (active low)

View File

@@ -2370,9 +2370,14 @@ cpu_CPUID(void)
EBX = ECX = 0;
EDX = CPUID_FPU | CPUID_VME | CPUID_PSE | CPUID_TSC | CPUID_MSR | CPUID_PAE | CPUID_MCE | CPUID_CMPXCHG8B | CPUID_MTRR | CPUID_PGE | CPUID_MCA | CPUID_SEP | CPUID_CMOV;
} else if (EAX == 2) {
EAX = 0x00000001;
EAX = 0x03020101; /* Instruction TLB: 4 KB pages, 4-way set associative, 32 entries
Instruction TLB: 4 MB pages, fully associative, 2 entries
Data TLB: 4 KB pages, 4-way set associative, 64 entries */
EBX = ECX = 0;
EDX = 0x00000000;
EDX = 0x06040a42; /* 2nd-level cache: 256 KB, 4-way set associative, 32-byte line size
1st-level data cache: 8 KB, 2-way set associative, 32-byte line size
Data TLB: 4 MB pages, 4-way set associative, 8 entries
1st-level instruction cache:8 KB, 4-way set associative, 32-byte line size */
} else
EAX = EBX = ECX = EDX = 0;
break;
@@ -2388,9 +2393,14 @@ cpu_CPUID(void)
EBX = ECX = 0;
EDX = CPUID_FPU | CPUID_VME | CPUID_PSE | CPUID_TSC | CPUID_MSR | CPUID_PAE | CPUID_MCE | CPUID_CMPXCHG8B | CPUID_MMX | CPUID_MTRR | CPUID_PGE | CPUID_MCA | CPUID_SEP | CPUID_CMOV;
} else if (EAX == 2) {
EAX = 0x00000001;
EAX = 0x03020101; /* Instruction TLB: 4 KB pages, 4-way set associative, 32 entries
Instruction TLB: 4 MB pages, fully associative, 2 entries
Data TLB: 4 KB pages, 4-way set associative, 64 entries */
EBX = ECX = 0;
EDX = 0x00000000;
EDX = 0x0c040843; /* 2nd-level cache: 512 KB, 4-way set associative, 32-byte line size
1st-level data cache: 16 KB, 4-way set associative, 32-byte line size
Data TLB: 4 MB pages, 4-way set associative, 8 entries
1st-level instruction cache: 16 KB, 4-way set associative, 32-byte line size */
} else
EAX = EBX = ECX = EDX = 0;
break;
@@ -2406,9 +2416,22 @@ cpu_CPUID(void)
EBX = ECX = 0;
EDX = CPUID_FPU | CPUID_VME | CPUID_PSE | CPUID_TSC | CPUID_MSR | CPUID_PAE | CPUID_MCE | CPUID_CMPXCHG8B | CPUID_MMX | CPUID_MTRR | CPUID_PGE | CPUID_MCA | CPUID_SEP | CPUID_FXSR | CPUID_CMOV;
} else if (EAX == 2) {
EAX = 0x00000001;
EAX = 0x03020101; /* Instruction TLB: 4 KB pages, 4-way set associative, 32 entries
Instruction TLB: 4 MB pages, fully associative, 2 entries
Data TLB: 4 KB pages, 4-way set associative, 64 entries */
EBX = ECX = 0;
EDX = 0x00000000;
if (cpu_f->package == CPU_PKG_SLOT2) /* Pentium II Xeon Drake */
EDX = 0x0c040844; /* 2nd-level cache: 1 MB, 4-way set associative, 32-byte line size
1st-level data cache: 16 KB, 4-way set associative, 32-byte line size
Data TLB: 4 MB pages, 4-way set associative, 8 entries
1st-level instruction cache: 16 KB, 4-way set associative, 32-byte line size */
else if (!strncmp(cpu_f->internal_name, "celeron", 7)) { /* Celeron */
if (CPUID >= 0x660) /* Mendocino */
EDX = 0x0c040841; /* 2nd-level cache: 128 KB, 4-way set associative, 32-byte line size */
else /* Covington */
EDX = 0x0c040840; /* No 2nd-level cache */
} else /* Pentium II Deschutes and OverDrive */
EDX = 0x0c040843; /* 2nd-level cache: 512 KB, 4-way set associative, 32-byte line size */
} else
EAX = EBX = ECX = EDX = 0;
break;

View File

@@ -2764,6 +2764,7 @@ const cpu_legacy_machine_t cpu_legacy_table[] = {
{ "award286", cputables_286 },
{ "gw286ct", cputables_286 },
{ "gdc212m", cputables_286 },
{ "super286c", cputables_286 },
{ "super286tr", cputables_286 },
{ "spc4200p", cputables_286 },
{ "spc4216p", cputables_286 },

View File

@@ -31,11 +31,13 @@
#define CLAMP(a, min, max) (((a) < (min)) ? (min) : (((a) > (max)) ? (max) : (a)))
/* Formulas and factors derived from Linux's gl518sm.c driver. */
#define GL518SM_RPM_TO_REG(r, d) ((r) ? CLAMP((480000 + (r) * (d) / 2) / (r) * (d), 1, 255) : 0)
/* Formulas and factors derived from Linux's gl518sm.c and gl520sm.c drivers. */
#define GL518SM_RPM_TO_REG(r, d) ((r) ? (480000 / (CLAMP((r), (480000 >> (d)) / 255, (480000 >> (d))) << (d))) : 0)
#define GL518SM_VOLTAGE_TO_REG(v) ((uint8_t) round((v) / 19.0))
#define GL518SM_VDD_TO_REG(v) ((uint8_t) (((v) *4) / 95.0))
#define GL520SM 0x100
typedef struct gl518sm_t {
uint32_t local;
hwm_values_t *values;
@@ -128,18 +130,22 @@ gl518sm_read(gl518sm_t *dev, uint8_t reg)
switch (reg) {
case 0x04: /* temperature */
ret = (dev->values->temperatures[0] + 119) & 0xff;
ret = (dev->values->temperatures[0] + ((dev->local & GL520SM) ? 130 : 119)) & 0xff;
break;
case 0x07: /* fan speeds */
ret = GL518SM_RPM_TO_REG(dev->values->fans[0], 1 << ((dev->regs[0x0f] >> 6) & 0x3)) << 8;
ret |= GL518SM_RPM_TO_REG(dev->values->fans[1], 1 << ((dev->regs[0x0f] >> 4) & 0x3));
ret = GL518SM_RPM_TO_REG(dev->values->fans[0], (dev->regs[0x0f] >> 6) & 0x3) << 8;
ret |= GL518SM_RPM_TO_REG(dev->values->fans[1], (dev->regs[0x0f] >> 4) & 0x3);
break;
case 0x0d: /* VIN3 */
ret = GL518SM_VOLTAGE_TO_REG(dev->values->voltages[2]);
break;
case 0x0e: /* temperature 2 */
ret = (dev->local & GL520SM) ? ((dev->values->temperatures[1] + 130) & 0xff) : dev->regs[reg];
break;
case 0x13: /* VIN2 */
ret = GL518SM_VOLTAGE_TO_REG(dev->values->voltages[1]);
break;
@@ -217,6 +223,11 @@ gl518sm_write(gl518sm_t *dev, uint8_t reg, uint16_t val)
gl518sm_reset(dev);
break;
case 0x0e:
if (dev->local & GL520SM)
return 0;
break;
case 0x0f:
dev->regs[reg] = val & 0xf8;
break;
@@ -238,10 +249,16 @@ gl518sm_reset(gl518sm_t *dev)
{
memset(dev->regs, 0, sizeof(dev->regs));
dev->regs[0x00] = 0x80;
dev->regs[0x01] = 0x80; /* revision 0x80 can read all voltages */
dev->regs[0x05] = 0xc7;
dev->regs[0x06] = 0xc2;
if (dev->local & GL520SM) {
dev->regs[0x00] = 0x20;
dev->regs[0x01] = 0x00;
dev->regs[0x03] = 0x04;
} else {
dev->regs[0x00] = 0x80;
dev->regs[0x01] = 0x80; /* revision 0x80 can read all voltages */
dev->regs[0x05] = 0xc7;
dev->regs[0x06] = 0xc2;
}
dev->regs[0x08] = 0x6464;
dev->regs[0x09] = 0xdac5;
dev->regs[0x0a] = 0xdac5;
@@ -279,7 +296,8 @@ gl518sm_init(const device_t *info)
},
{
/* temperatures */
30 /* usually CPU */
30, /* usually CPU */
30 /* GL520SM only: usually System */
},
{
/* voltages */
@@ -327,3 +345,34 @@ const device_t gl518sm_2d_device = {
.force_redraw = NULL,
.config = NULL
};
/* GL520SM on SMBus address 2Ch */
const device_t gl520sm_2c_device = {
.name = "Genesys Logic GL520SM Hardware Monitor",
.internal_name = "gl520sm_2c",
.flags = DEVICE_ISA,
.local = GL520SM | 0x2c,
.init = gl518sm_init,
.close = gl518sm_close,
.reset = NULL,
{ .available = NULL },
.speed_changed = NULL,
.force_redraw = NULL,
.config = NULL
};
/* GL520SM on SMBus address 2Dh */
const device_t gl520sm_2d_device = {
.name = "Genesys Logic GL520SM Hardware Monitor",
.internal_name = "gl520sm_2d",
.flags = DEVICE_ISA,
.local = GL520SM | 0x2d,
.init = gl518sm_init,
.close = gl518sm_close,
.reset = NULL,
{ .available = NULL },
.speed_changed = NULL,
.force_redraw = NULL,
.config = NULL
};

View File

@@ -29,29 +29,21 @@
#include <86box/plat_unused.h>
#define CHECK_CURRENT_LD() \
if (!dev->current_ld) { \
if (!ld) { \
isapnp_log("ISAPnP: No logical device selected\n"); \
break; \
goto vendor_defined; \
}
#define CHECK_CURRENT_CARD() \
if (1) { \
card = dev->first_card; \
while (card) { \
if (card->enable && (card->state == PNP_STATE_CONFIG)) \
break; \
card = card->next; \
} \
if (!card) { \
isapnp_log("ISAPnP: No card in CONFIG state\n"); \
break; \
} \
#define CHECK_CURRENT_CARD() \
if (!card) { \
isapnp_log("ISAPnP: No card in CONFIG state\n"); \
break; \
}
static const uint8_t pnp_init_key[32] = { 0x6A, 0xB5, 0xDA, 0xED, 0xF6, 0xFB, 0x7D, 0xBE,
0xDF, 0x6F, 0x37, 0x1B, 0x0D, 0x86, 0xC3, 0x61,
0xB0, 0x58, 0x2C, 0x16, 0x8B, 0x45, 0xA2, 0xD1,
0xE8, 0x74, 0x3A, 0x9D, 0xCE, 0xE7, 0x73, 0x39 };
const uint8_t isapnp_init_key[32] = { 0x6A, 0xB5, 0xDA, 0xED, 0xF6, 0xFB, 0x7D, 0xBE,
0xDF, 0x6F, 0x37, 0x1B, 0x0D, 0x86, 0xC3, 0x61,
0xB0, 0x58, 0x2C, 0x16, 0x8B, 0x45, 0xA2, 0xD1,
0xE8, 0x74, 0x3A, 0x9D, 0xCE, 0xE7, 0x73, 0x39 };
static const device_t isapnp_device;
#ifdef ENABLE_ISAPNP_LOG
@@ -95,6 +87,7 @@ typedef struct _isapnp_card_ {
uint8_t enable;
uint8_t state;
uint8_t csn;
uint8_t ld;
uint8_t id_checksum;
uint8_t serial_read;
uint8_t serial_read_pair;
@@ -265,15 +258,13 @@ isapnp_read_rangecheck(UNUSED(uint16_t addr), void *priv)
}
static uint8_t
isapnp_read_data(UNUSED(uint16_t addr), void *priv)
isapnp_read_common(isapnp_t *dev, isapnp_card_t *card, isapnp_device_t *ld, uint8_t reg)
{
isapnp_t *dev = (isapnp_t *) priv;
uint8_t ret = 0xff;
uint8_t bit;
uint8_t next_shift;
isapnp_card_t *card;
uint8_t ret = 0xff;
uint8_t bit;
uint8_t next_shift;
switch (dev->reg) {
switch (reg) {
case 0x01: /* Serial Isolation */
card = dev->first_card;
while (card) {
@@ -342,78 +333,52 @@ isapnp_read_data(UNUSED(uint16_t addr), void *priv)
ret = 0x00;
CHECK_CURRENT_LD();
isapnp_log("ISAPnP: Query LDN for CSN %02X device %02X\n", dev->current_ld_card->csn, dev->current_ld->number);
ret = dev->current_ld->number;
isapnp_log("ISAPnP: Query LDN for CSN %02X device %02X\n", card->csn, ld->number);
ret = ld->number;
break;
case 0x20:
case 0x21:
case 0x22:
case 0x23:
case 0x24:
case 0x25:
case 0x26:
case 0x27:
case 0x28:
case 0x29:
case 0x2a:
case 0x2b:
case 0x2c:
case 0x2d:
case 0x2e:
case 0x2f:
case 0x20 ... 0x2f:
case 0x38 ... 0x3f:
case 0xa9 ... 0xff:
vendor_defined:
CHECK_CURRENT_CARD();
isapnp_log("ISAPnP: Read vendor-defined register %02X from CSN %02X\n", dev->reg, card->csn);
isapnp_log("ISAPnP: Read vendor-defined register %02X from CSN %02X device %02X\n", reg, card->csn, ld ? ld->number : -1);
if (card->read_vendor_reg)
ret = card->read_vendor_reg(0, dev->reg, card->priv);
break;
case 0x38:
case 0x39:
case 0x3a:
case 0x3b:
case 0x3c:
case 0x3d:
case 0x3e:
case 0x3f:
case 0xf0:
case 0xf1:
case 0xf2:
case 0xf3:
case 0xf4:
case 0xf5:
case 0xf6:
case 0xf7:
case 0xf8:
case 0xf9:
case 0xfa:
case 0xfb:
case 0xfc:
case 0xfd:
case 0xfe:
CHECK_CURRENT_LD();
isapnp_log("ISAPnP: Read vendor-defined register %02X from CSN %02X device %02X\n", dev->reg, dev->current_ld_card->csn, dev->current_ld->number);
if (dev->current_ld_card->read_vendor_reg)
ret = dev->current_ld_card->read_vendor_reg(dev->current_ld->number, dev->reg, dev->current_ld_card->priv);
ret = card->read_vendor_reg(ld ? ld->number : -1, reg, card->priv);
break;
default:
if (dev->reg >= 0x30) {
if (reg >= 0x30) {
CHECK_CURRENT_LD();
isapnp_log("ISAPnP: Read register %02X from CSN %02X device %02X\n", dev->reg, dev->current_ld_card->csn, dev->current_ld->number);
ret = dev->current_ld->regs[dev->reg];
isapnp_log("ISAPnP: Read register %02X from CSN %02X device %02X\n", reg, card->csn, ld->number);
ret = ld->regs[reg];
}
break;
}
isapnp_log("ISAPnP: read_data(%02X) = %02X\n", dev->reg, ret);
isapnp_log("ISAPnP: read_common(%02X) = %02X\n", reg, ret);
return ret;
}
static uint8_t
isapnp_read_data(UNUSED(uint16_t addr), void *priv)
{
isapnp_t *dev = (isapnp_t *) priv;
isapnp_card_t *card = dev->first_card;
while (card) {
if (card->enable && (card->state == PNP_STATE_CONFIG))
break;
card = card->next;
}
isapnp_log("ISAPnP: read_data() => ");
return isapnp_read_common(dev, card, dev->current_ld, dev->reg);
}
static void
isapnp_set_read_data(uint16_t addr, isapnp_t *dev)
{
@@ -445,7 +410,7 @@ isapnp_write_addr(UNUSED(uint16_t addr), uint8_t val, void *priv)
if (card->state == PNP_STATE_WAIT_FOR_KEY) { /* checking only the first card should be fine */
/* Check written value against LFSR key. */
if (val == pnp_init_key[dev->key_pos]) {
if (val == isapnp_init_key[dev->key_pos]) {
dev->key_pos++;
if (!dev->key_pos) {
isapnp_log("ISAPnP: Key unlocked, putting cards to SLEEP\n");
@@ -462,17 +427,14 @@ isapnp_write_addr(UNUSED(uint16_t addr), uint8_t val, void *priv)
}
static void
isapnp_write_data(UNUSED(uint16_t addr), uint8_t val, void *priv)
isapnp_write_common(isapnp_t *dev, isapnp_card_t *card, isapnp_device_t *ld, uint8_t reg, uint8_t val)
{
isapnp_t *dev = (isapnp_t *) priv;
isapnp_card_t *card;
isapnp_device_t *ld;
uint16_t io_addr;
uint16_t reset_cards = 0;
uint16_t io_addr;
uint16_t reset_cards = 0;
isapnp_log("ISAPnP: write_data(%02X)\n", val);
isapnp_log("ISAPnP: write_common(%02X, %02X)\n", reg, val);
switch (dev->reg) {
switch (reg) {
case 0x00: /* Set RD_DATA Port */
isapnp_set_read_data((val << 2) | 3, dev);
isapnp_log("ISAPnP: Read data port set to %04X\n", dev->read_data_addr);
@@ -526,7 +488,7 @@ isapnp_write_data(UNUSED(uint16_t addr), uint8_t val, void *priv)
while (card) {
if (card->csn == val) {
card->rom_pos = 0;
card->id_checksum = pnp_init_key[0];
card->id_checksum = isapnp_init_key[0];
if (card->state == PNP_STATE_SLEEP)
card->state = (val == 0) ? PNP_STATE_ISOLATION : PNP_STATE_CONFIG;
} else {
@@ -551,6 +513,7 @@ isapnp_write_data(UNUSED(uint16_t addr), uint8_t val, void *priv)
case 0x07: /* Logical Device Number */
CHECK_CURRENT_CARD();
card->ld = val;
ld = card->first_ld;
while (ld) {
if (ld->number == val) {
@@ -570,10 +533,10 @@ isapnp_write_data(UNUSED(uint16_t addr), uint8_t val, void *priv)
case 0x30: /* Activate */
CHECK_CURRENT_LD();
isapnp_log("ISAPnP: %sctivate CSN %02X device %02X\n", (val & 0x01) ? "A" : "Dea", dev->current_ld_card->csn, dev->current_ld->number);
isapnp_log("ISAPnP: %sctivate CSN %02X device %02X\n", (val & 0x01) ? "A" : "Dea", card->csn, ld->number);
dev->current_ld->regs[dev->reg] = val & 0x01;
isapnp_device_config_changed(dev->current_ld_card, dev->current_ld);
ld->regs[reg] = val & 0x01;
isapnp_device_config_changed(card, ld);
break;
@@ -581,80 +544,39 @@ isapnp_write_data(UNUSED(uint16_t addr), uint8_t val, void *priv)
CHECK_CURRENT_LD();
for (uint8_t i = 0; i < 8; i++) {
if (!dev->current_ld->io_len[i])
if (!ld->io_len[i])
continue;
io_addr = (dev->current_ld->regs[0x60 + (2 * i)] << 8) | dev->current_ld->regs[0x61 + (2 * i)];
if (dev->current_ld->regs[dev->reg] & 0x02)
io_removehandler(io_addr, dev->current_ld->io_len[i], isapnp_read_rangecheck, NULL, NULL, NULL, NULL, NULL, dev->current_ld);
io_addr = (ld->regs[0x60 + (2 * i)] << 8) | ld->regs[0x61 + (2 * i)];
if (ld->regs[reg] & 0x02)
io_removehandler(io_addr, ld->io_len[i], isapnp_read_rangecheck, NULL, NULL, NULL, NULL, NULL, ld);
if (val & 0x02)
io_sethandler(io_addr, dev->current_ld->io_len[i], isapnp_read_rangecheck, NULL, NULL, NULL, NULL, NULL, dev->current_ld);
io_sethandler(io_addr, ld->io_len[i], isapnp_read_rangecheck, NULL, NULL, NULL, NULL, NULL, ld);
}
dev->current_ld->regs[dev->reg] = val & 0x03;
isapnp_device_config_changed(dev->current_ld_card, dev->current_ld);
ld->regs[reg] = val & 0x03;
isapnp_device_config_changed(card, ld);
break;
case 0x20:
case 0x21:
case 0x22:
case 0x23:
case 0x24:
case 0x25:
case 0x26:
case 0x27:
case 0x28:
case 0x29:
case 0x2a:
case 0x2b:
case 0x2c:
case 0x2d:
case 0x2e:
case 0x2f:
case 0x20 ... 0x2f:
case 0x38 ... 0x3f:
case 0xa9 ... 0xff:
vendor_defined:
CHECK_CURRENT_CARD();
isapnp_log("ISAPnP: Write %02X to vendor-defined register %02X on CSN %02X\n", val, dev->reg, card->csn);
isapnp_log("ISAPnP: Write %02X to vendor-defined register %02X on CSN %02X device %02X\n", val, reg, card->csn, ld ? ld->number : -1);
if (card->write_vendor_reg)
card->write_vendor_reg(0, dev->reg, val, card->priv);
break;
case 0x38:
case 0x39:
case 0x3a:
case 0x3b:
case 0x3c:
case 0x3d:
case 0x3e:
case 0x3f:
case 0xf0:
case 0xf1:
case 0xf2:
case 0xf3:
case 0xf4:
case 0xf5:
case 0xf6:
case 0xf7:
case 0xf8:
case 0xf9:
case 0xfa:
case 0xfb:
case 0xfc:
case 0xfd:
case 0xfe:
CHECK_CURRENT_LD();
isapnp_log("ISAPnP: Write %02X to vendor-defined register %02X on CSN %02X device %02X\n", val, dev->reg, dev->current_ld_card->csn, dev->current_ld->number);
if (dev->current_ld_card->write_vendor_reg)
dev->current_ld_card->write_vendor_reg(dev->current_ld->number, dev->reg, val, dev->current_ld_card->priv);
card->write_vendor_reg(ld ? ld->number : -1, reg, val, card->priv);
break;
default:
if (dev->reg >= 0x40) {
if (reg >= 0x40) {
CHECK_CURRENT_LD();
isapnp_log("ISAPnP: Write %02X to register %02X on CSN %02X device %02X\n", val, dev->reg, dev->current_ld_card->csn, dev->current_ld->number);
isapnp_log("ISAPnP: Write %02X to register %02X on CSN %02X device %02X\n", val, reg, card->csn, ld->number);
switch (dev->reg) {
switch (reg) {
case 0x42:
case 0x4a:
case 0x52:
@@ -664,7 +586,7 @@ isapnp_write_data(UNUSED(uint16_t addr), uint8_t val, void *priv)
case 0x94:
case 0xa4:
/* Read-only memory range length / upper limit bit. */
val = (val & 0xfe) | (dev->current_ld->regs[dev->reg] & 0x01);
val = (val & 0xfe) | (ld->regs[reg] & 0x01);
break;
case 0x60:
@@ -676,21 +598,21 @@ isapnp_write_data(UNUSED(uint16_t addr), uint8_t val, void *priv)
case 0x6c:
case 0x6e:
/* Discard upper address bits if this I/O range can only decode 10-bit. */
if (!(dev->current_ld->io_16bit & (1 << ((dev->reg >> 1) & 0x07))))
if (!(ld->io_16bit & (1 << ((reg >> 1) & 0x07))))
val &= 0x03;
break;
case 0x71:
case 0x73:
/* Limit IRQ types to supported ones. */
if ((val & 0x01) && !(dev->current_ld->irq_types & ((dev->reg == 0x71) ? 0x0c : 0xc0))) /* level, not supported = force edge */
if ((val & 0x01) && !(ld->irq_types & ((reg == 0x71) ? 0x0c : 0xc0))) /* level, not supported = force edge */
val &= ~0x01;
else if (!(val & 0x01) && !(dev->current_ld->irq_types & ((dev->reg == 0x71) ? 0x03 : 0x30))) /* edge, not supported = force level */
else if (!(val & 0x01) && !(ld->irq_types & ((reg == 0x71) ? 0x03 : 0x30))) /* edge, not supported = force level */
val |= 0x01;
if ((val & 0x02) && !(dev->current_ld->irq_types & ((dev->reg == 0x71) ? 0x05 : 0x50))) /* high, not supported = force low */
if ((val & 0x02) && !(ld->irq_types & ((reg == 0x71) ? 0x05 : 0x50))) /* high, not supported = force low */
val &= ~0x02;
else if (!(val & 0x02) && !(dev->current_ld->irq_types & ((dev->reg == 0x71) ? 0x0a : 0xa0))) /* low, not supported = force high */
else if (!(val & 0x02) && !(ld->irq_types & ((reg == 0x71) ? 0x0a : 0xa0))) /* low, not supported = force high */
val |= 0x02;
break;
@@ -699,13 +621,31 @@ isapnp_write_data(UNUSED(uint16_t addr), uint8_t val, void *priv)
break;
}
dev->current_ld->regs[dev->reg] = val;
isapnp_device_config_changed(dev->current_ld_card, dev->current_ld);
ld->regs[reg] = val;
isapnp_device_config_changed(card, ld);
}
break;
}
}
static void
isapnp_write_data(UNUSED(uint16_t addr), uint8_t val, void *priv)
{
isapnp_t *dev = (isapnp_t *) priv;
isapnp_card_t *card = NULL;
if (!card) {
card = dev->first_card;
while (card) {
if (card->enable && (card->state == PNP_STATE_CONFIG))
break;
card = card->next;
}
}
isapnp_log("ISAPnP: write_data(%02X) => ", val);
isapnp_write_common(dev, card, dev->current_ld, dev->reg, val);
}
static void *
isapnp_init(UNUSED(const device_t *info))
{
@@ -869,7 +809,7 @@ isapnp_update_card_rom(void *priv, uint8_t *rom, uint16_t rom_size)
}
#ifdef ENABLE_ISAPNP_LOG
isapnp_log(" bytes, %swritable, %sread cacheable, %s, %sshadowable, %sexpansion ROM\n",
isapnp_log(" bytes, %swritable, %sread cacheable, %s, %s, %sshadowable, %sexpansion ROM\n",
(card->rom[i + 3] & 0x01) ? "not " : "",
(card->rom[i + 3] & 0x02) ? "not " : "",
(card->rom[i + 3] & 0x04) ? "upper limit" : "range length",
@@ -1135,6 +1075,32 @@ isapnp_set_csn(void *priv, uint8_t csn)
card->csn_changed(card->csn, card->priv);
}
uint8_t
isapnp_read_reg(void *priv, uint8_t ldn, uint8_t reg)
{
isapnp_card_t *card = (isapnp_card_t *) priv;
isapnp_device_t *ld = card->first_ld;
while (ld) {
if (ld->number == ldn)
break;
ld = ld->next;
}
return isapnp_read_common(device_get_priv(&isapnp_device), card, ld, reg);
}
void
isapnp_write_reg(void *priv, uint8_t ldn, uint8_t reg, uint8_t val)
{
isapnp_card_t *card = (isapnp_card_t *) priv;
isapnp_device_t *ld = card->first_ld;
while (ld) {
if (ld->number == ldn)
break;
ld = ld->next;
}
isapnp_write_common(device_get_priv(&isapnp_device), card, ld, reg, val);
}
void
isapnp_set_device_defaults(void *priv, uint8_t ldn, const isapnp_device_config_t *config)
{

View File

@@ -68,6 +68,8 @@ extern const device_t w83782d_device;
extern const device_t gl518sm_2c_device;
extern const device_t gl518sm_2d_device;
extern const device_t gl520sm_2c_device;
extern const device_t gl520sm_2d_device;
extern const device_t via_vt82c686_hwm_device;

View File

@@ -54,17 +54,21 @@ typedef struct isapnp_device_config_t {
} dma[2];
} isapnp_device_config_t;
void *isapnp_add_card(uint8_t *rom, uint16_t rom_size,
void (*config_changed)(uint8_t ld, isapnp_device_config_t *config, void *priv),
void (*csn_changed)(uint8_t csn, void *priv),
uint8_t (*read_vendor_reg)(uint8_t ld, uint8_t reg, void *priv),
void (*write_vendor_reg)(uint8_t ld, uint8_t reg, uint8_t val, void *priv),
void *priv);
void isapnp_update_card_rom(void *priv, uint8_t *rom, uint16_t rom_size);
void isapnp_enable_card(void *priv, uint8_t enable);
void isapnp_set_csn(void *priv, uint8_t csn);
void isapnp_set_device_defaults(void *priv, uint8_t ldn, const isapnp_device_config_t *config);
void isapnp_reset_card(void *priv);
void isapnp_reset_device(void *priv, uint8_t ld);
extern const uint8_t isapnp_init_key[32];
void *isapnp_add_card(uint8_t *rom, uint16_t rom_size,
void (*config_changed)(uint8_t ld, isapnp_device_config_t *config, void *priv),
void (*csn_changed)(uint8_t csn, void *priv),
uint8_t (*read_vendor_reg)(uint8_t ld, uint8_t reg, void *priv),
void (*write_vendor_reg)(uint8_t ld, uint8_t reg, uint8_t val, void *priv),
void *priv);
void isapnp_update_card_rom(void *priv, uint8_t *rom, uint16_t rom_size);
void isapnp_enable_card(void *priv, uint8_t enable);
void isapnp_set_csn(void *priv, uint8_t csn);
uint8_t isapnp_read_reg(void *priv, uint8_t ldn, uint8_t reg);
void isapnp_write_reg(void *priv, uint8_t ldn, uint8_t reg, uint8_t val);
void isapnp_set_device_defaults(void *priv, uint8_t ldn, const isapnp_device_config_t *config);
void isapnp_reset_card(void *priv);
void isapnp_reset_device(void *priv, uint8_t ld);
#endif /*EMU_ISAPNP_H*/

View File

@@ -446,6 +446,7 @@ extern int machine_at_quadt386sx_init(const machine_t *);
extern int machine_at_award286_init(const machine_t *);
extern int machine_at_gdc212m_init(const machine_t *);
extern int machine_at_gw286ct_init(const machine_t *);
extern int machine_at_super286c_init(const machine_t *);
extern int machine_at_super286tr_init(const machine_t *);
extern int machine_at_spc4200p_init(const machine_t *);
extern int machine_at_spc4216p_init(const machine_t *);
@@ -599,6 +600,7 @@ extern int machine_at_p5sp4_init(const machine_t *);
/* m_at_socket5.c */
extern int machine_at_plato_init(const machine_t *);
extern int machine_at_dellplato_init(const machine_t *);
extern int machine_at_ambradp90_init(const machine_t *);
extern int machine_at_430nx_init(const machine_t *);
@@ -672,6 +674,7 @@ extern int machine_at_tx97_init(const machine_t *);
extern int machine_at_an430tx_init(const machine_t *);
#endif
extern int machine_at_ym430tx_init(const machine_t *);
extern int machine_at_thunderbolt_init(const machine_t *);
extern int machine_at_mb540n_init(const machine_t *);
extern int machine_at_56a5_init(const machine_t *);
extern int machine_at_p5mms98_init(const machine_t *);
@@ -704,6 +707,7 @@ extern int machine_at_aurora_init(const machine_t *);
extern int machine_at_686nx_init(const machine_t *);
extern int machine_at_acerv60n_init(const machine_t *);
extern int machine_at_vs440fx_init(const machine_t *);
extern int machine_at_GW2KVenus_init(const machine_t *);
extern int machine_at_ap440fx_init(const machine_t *);
extern int machine_at_mb600n_init(const machine_t *);
extern int machine_at_8600ttc_init(const machine_t *);
@@ -752,6 +756,7 @@ extern int machine_at_s370slm_init(const machine_t *);
extern int machine_at_cubx_init(const machine_t *);
extern int machine_at_atc7020bxii_init(const machine_t *);
extern int machine_at_m773_init(const machine_t *);
extern int machine_at_ambx133_init(const machine_t *);
extern int machine_at_awo671r_init(const machine_t *);
extern int machine_at_63a1_init(const machine_t *);

View File

@@ -102,6 +102,7 @@ extern void midi_in_sysex(uint8_t *buffer, uint32_t len);
#ifdef EMU_DEVICE_H
extern const device_t rtmidi_output_device;
extern const device_t rtmidi_input_device;
extern const device_t opl4_midi_device;
# ifdef USE_FLUIDSYNTH
extern const device_t fluidsynth_device;
# endif

View File

@@ -0,0 +1,19 @@
struct nmc93cxx_eeprom_t;
typedef struct nmc93cxx_eeprom_t nmc93cxx_eeprom_t;
typedef struct nmc93cxx_eeprom_params_t {
uint16_t nwords;
char *filename;
uint16_t *default_content;
} nmc93cxx_eeprom_params_t;
/* Read from the EEPROM. */
uint16_t nmc93cxx_eeprom_read(nmc93cxx_eeprom_t *eeprom);
/* Write to the EEPROM. */
void nmc93cxx_eeprom_write(nmc93cxx_eeprom_t *eeprom, int eecs, int eesk, int eedi);
/* Get EEPROM data array. */
uint16_t *nmc93cxx_eeprom_data(nmc93cxx_eeprom_t *eeprom);
extern const device_t nmc93cxx_device;

View File

@@ -0,0 +1 @@
extern const device_t rtl8139c_plus_device;

View File

@@ -0,0 +1,2 @@
extern const device_t dec_tulip_device;
extern const device_t dec_tulip_21140_device;

View File

@@ -0,0 +1,101 @@
/*
* RoboPlay for MSX
* Copyright (C) 2020 by RoboSoft Inc.
*
* opl4_defines.h
*
*/
#ifndef __OPL4_DEFINES_H
#define __OPL4_DEFINES_H
/*
* Register numbers
*/
#define OPL4_REG_TEST0 0x00
#define OPL4_REG_TEST1 0x01
#define OPL4_REG_MEMORY_CONFIGURATION 0x02
#define OPL4_MODE_BIT 0x01
#define OPL4_MTYPE_BIT 0x02
#define OPL4_TONE_HEADER_MASK 0x1C
#define OPL4_DEVICE_ID_MASK 0xE0
#define OPL4_REG_MEMORY_ADDRESS_HIGH 0x03
#define OPL4_REG_MEMORY_ADDRESS_MID 0x04
#define OPL4_REG_MEMORY_ADDRESS_LOW 0x05
#define OPL4_REG_MEMORY_DATA 0x06
/*
* Offsets to the register banks for voices. To get the
* register number just add the voice number to the bank offset.
*
* Wave Table Number low bits (0x08 to 0x1F)
*/
#define OPL4_REG_TONE_NUMBER 0x08
/* Wave Table Number high bit, F-Number low bits (0x20 to 0x37) */
#define OPL4_REG_F_NUMBER 0x20
#define OPL4_TONE_NUMBER_BIT8 0x01
#define OPL4_F_NUMBER_LOW_MASK 0xFE
/* F-Number high bits, Octave, Pseudo-Reverb (0x38 to 0x4F) */
#define OPL4_REG_OCTAVE 0x38
#define OPL4_F_NUMBER_HIGH_MASK 0x07
#define OPL4_BLOCK_MASK 0xF0
#define OPL4_PSEUDO_REVERB_BIT 0x08
/* Total Level, Level Direct (0x50 to 0x67) */
#define OPL4_REG_LEVEL 0x50
#define OPL4_TOTAL_LEVEL_MASK 0xFE
#define OPL4_LEVEL_DIRECT_BIT 0x01
/* Key On, Damp, LFO RST, CH, Panpot (0x68 to 0x7F) */
#define OPL4_REG_MISC 0x68
#define OPL4_KEY_ON_BIT 0x80
#define OPL4_DAMP_BIT 0x40
#define OPL4_LFO_RESET_BIT 0x20
#define OPL4_OUTPUT_CHANNEL_BIT 0x10
#define OPL4_PAN_POT_MASK 0x0F
/* LFO, VIB (0x80 to 0x97) */
#define OPL4_REG_LFO_VIBRATO 0x80
#define OPL4_LFO_FREQUENCY_MASK 0x38
#define OPL4_VIBRATO_DEPTH_MASK 0x07
#define OPL4_CHORUS_SEND_MASK 0xC0
/* Attack / Decay 1 rate (0x98 to 0xAF) */
#define OPL4_REG_ATTACK_DECAY1 0x98
#define OPL4_ATTACK_RATE_MASK 0xF0
#define OPL4_DECAY1_RATE_MASK 0x0F
/* Decay level / 2 rate (0xB0 to 0xC7) */
#define OPL4_REG_LEVEL_DECAY2 0xB0
#define OPL4_DECAY_LEVEL_MASK 0xF0
#define OPL4_DECAY2_RATE_MASK 0x0F
/* Release rate / Rate correction (0xC8 to 0xDF) */
#define OPL4_REG_RELEASE_CORRECTION 0xC8
#define OPL4_RELEASE_RATE_MASK 0x0F
#define OPL4_RATE_INTERPOLATION_MASK 0xF0
/* AM (0xE0 to 0xF7) */
#define OPL4_REG_TREMOLO 0xE0
#define OPL4_TREMOLO_DEPTH_MASK 0x07
#define OPL4_REVERB_SEND_MASK 0xE0
/* Mixer */
#define OPL4_REG_MIX_CONTROL_FM 0xF8
#define OPL4_REG_MIX_CONTROL_PCM 0xF9
#define OPL4_MIX_LEFT_MASK 0x07
#define OPL4_MIX_RIGHT_MASK 0x38
#define OPL4_REG_ATC 0xFA
#define OPL4_ATC_BIT 0x01
/* Bits in the OPL4 Status register */
#define OPL4_STATUS_BUSY 0x01
#define OPL4_STATUS_LOAD 0x02
#endif /* __OPL4_DEFINES_H */

View File

@@ -42,6 +42,7 @@ extern const device_t fdc37c935_device;
extern const device_t fdc37m60x_device;
extern const device_t fdc37m60x_370_device;
extern const device_t it8661f_device;
extern const device_t it8671f_device;
extern const device_t i82091aa_device;
extern const device_t i82091aa_398_device;
extern const device_t i82091aa_ide_pri_device;
@@ -71,6 +72,8 @@ extern const device_t ps1_m2133_sio;
extern const device_t sio_detect_device;
#endif
extern const device_t um8669f_device;
extern const device_t um8669f_ide_device;
extern const device_t um8669f_ide_sec_device;
extern const device_t via_vt82c686_sio_device;
extern const device_t w83787f_88h_device;
extern const device_t w83787f_device;

View File

@@ -38,6 +38,7 @@ typedef struct fm_drv_t {
void (*reset_buffer)(void *priv);
void (*set_do_cycles)(void *priv, int8_t do_cycles);
void *priv;
void (*generate)(void *priv, int32_t *data, uint32_t num_samples); /* daughterboard only. */
} fm_drv_t;
extern uint8_t fm_driver_get(int chip_id, fm_drv_t *drv);

View File

@@ -22,12 +22,12 @@
#define EMU_NAME "86Box"
#define EMU_NAME_W LSTR(EMU_NAME)
#define EMU_VERSION "4.0.2"
#define EMU_VERSION "4.1"
#define EMU_VERSION_W LSTR(EMU_VERSION)
#define EMU_VERSION_EX "3.50" /* frozen due to IDE re-detection behavior on Windows */
#define EMU_VERSION_MAJ 4
#define EMU_VERSION_MIN 0
#define EMU_VERSION_PATCH 2
#define EMU_VERSION_MIN 1
#define EMU_VERSION_PATCH 0
#define EMU_BUILD_NUM 0
@@ -42,7 +42,7 @@
#define EMU_ROMS_URL "https://github.com/86Box/roms/releases/latest"
#define EMU_ROMS_URL_W LSTR(EMU_ROMS_URL)
#ifdef RELEASE_BUILD
# define EMU_DOCS_URL "https://86box.readthedocs.io/en/v4.0/"
# define EMU_DOCS_URL "https://86box.readthedocs.io/en/v4.1/"
#else
# define EMU_DOCS_URL "https://86box.readthedocs.io"
#endif

View File

@@ -348,6 +348,29 @@ machine_at_gw286ct_init(const machine_t *model)
return ret;
}
int
machine_at_super286c_init(const machine_t *model)
{
int ret;
ret = bios_load_linear("roms/machines/super286c/hyundai_award286.bin",
0x000f0000, 65536, 0);
if (bios_only || !ret)
return ret;
machine_at_init(model);
device_add(&neat_device);
if (fdc_type == FDC_INTERNAL)
device_add(&fdc_at_device);
device_add(&keyboard_at_ami_device);
return ret;
}
int
machine_at_super286tr_init(const machine_t *model)
{

View File

@@ -247,6 +247,46 @@ machine_at_atc7020bxii_init(const machine_t *model)
return ret;
}
int
machine_at_m773_init(const machine_t *model)
{
int ret;
ret = bios_load_linear("roms/machines/m773/010504s.rom",
0x000c0000, 262144, 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(0x07, PCI_CARD_SOUTHBRIDGE, 1, 2, 3, 4);
pci_register_slot(0x0C, PCI_CARD_SOUND, 4, 3, 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(0x0D, PCI_CARD_NORMAL, 4, 1, 2, 3);
pci_register_slot(0x01, PCI_CARD_AGPBRIDGE, 1, 2, 3, 4);
device_add(&i440bx_device);
device_add(&slc90e66_device);
device_add(&keyboard_ps2_ami_pci_device);
device_add(&it8671f_device);
device_add(&sst_flash_39sf020_device);
spd_register(SPD_TYPE_SDRAM, 0x3, 256);
device_add(&gl520sm_2d_device); /* fans: CPU, Chassis; temperature: System */
hwm_values.temperatures[0] += 2; /* System offset */
hwm_values.temperatures[1] += 2; /* CPU offset */
hwm_values.voltages[0] = 3300; /* Vcore and 3.3V are swapped */
hwm_values.voltages[2] = hwm_get_vcore();
if (sound_card_current[0] == SOUND_INTERNAL)
device_add(&cmi8738_onboard_device);
return ret;
}
int
machine_at_ambx133_init(const machine_t *model)
{

View File

@@ -59,6 +59,25 @@ machine_at_plato_init(const machine_t *model)
return ret;
}
int
machine_at_dellplato_init(const machine_t *model)
{
int ret;
ret = bios_load_linear_combined("roms/machines/dellplato/1016AX1J.bio",
"roms/machines/dellplato/1016AX1J.bi1",
0x1d000, 128);
if (bios_only || !ret)
return ret;
machine_at_premiere_common_init(model, PCI_CAN_SWITCH_TYPE);
device_add(&i430nx_device);
return ret;
}
int
machine_at_ambradp90_init(const machine_t *model)
{

View File

@@ -1197,3 +1197,33 @@ machine_at_ms5164_init(const machine_t *model)
return ret;
}
int
machine_at_thunderbolt_init(const machine_t *model)
{
int ret;
ret = bios_load_linear("roms/machines/thunderbolt/tbolt-01.rom",
0x000c0000, 262144, 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(0x07, PCI_CARD_SOUTHBRIDGE, 0, 1, 2, 3); /* PIIX4 */
pci_register_slot(0x11, PCI_CARD_NORMAL, 0, 1, 2, 3);
pci_register_slot(0x12, PCI_CARD_NORMAL, 1, 2, 3, 0);
pci_register_slot(0x13, PCI_CARD_NORMAL, 2, 3, 0, 1);
pci_register_slot(0x14, PCI_CARD_NORMAL, 3, 0, 1, 2);
device_add(&i430tx_device);
device_add(&piix4_device);
device_add(&keyboard_ps2_ami_pci_device);
device_add(&fdc37c935_device);
device_add(&intel_flash_bxt_device);
spd_register(SPD_TYPE_SDRAM, 0x3, 128);
return ret;
}

View File

@@ -195,6 +195,40 @@ machine_at_vs440fx_init(const machine_t *model)
return ret;
}
int
machine_at_GW2KVenus_init(const machine_t *model)
{
int ret;
ret = bios_load_linear_combined2("roms/machines/GW2KVenus/1011CS1T.BIO",
"roms/machines/GW2KVenus/1011CS1T.BI1",
"roms/machines/GW2KVenus/1011CS1T.BI2",
"roms/machines/GW2KVenus/1011CS1T.BI3",
"roms/machines/GW2KVenus/1011CS1T.RCV",
0x3a000, 128);
if (bios_only || !ret)
return ret;
machine_at_common_init(model);
pci_init(PCI_CONFIG_TYPE_1);
pci_register_slot(0x00, PCI_CARD_NORTHBRIDGE, 0, 0, 0, 0);
pci_register_slot(0x0B, PCI_CARD_NORMAL, 1, 2, 3, 4);
pci_register_slot(0x0F, PCI_CARD_NORMAL, 4, 1, 2, 3);
pci_register_slot(0x11, PCI_CARD_NORMAL, 3, 4, 1, 2);
pci_register_slot(0x13, PCI_CARD_NORMAL, 2, 3, 4, 1);
pci_register_slot(0x07, PCI_CARD_SOUTHBRIDGE, 0, 0, 0, 0);
device_add(&i440fx_device);
device_add(&piix3_device);
device_add(&keyboard_ps2_intel_ami_pci_device);
device_add(&pc87307_device);
device_add(&intel_flash_bxt_ami_device);
return ret;
}
int
machine_at_ap440fx_init(const machine_t *model)
{

View File

@@ -163,7 +163,6 @@ const machine_filter_t machine_chipsets[] = {
};
/* Machines to add before machine freeze:
- PCChips M773 (440BX + SMSC with AMI BIOS);
- TMC Mycomp PCI54ST;
- Zeos Quadtel 486.
@@ -3239,6 +3238,46 @@ const machine_t machines[] = {
.snd_device = NULL,
.net_device = NULL
},
/* has an Award-branded KBC controller */
{
.name = "[NEAT] Hyundai Super-286C",
.internal_name = "super286c",
.type = MACHINE_TYPE_286,
.chipset = MACHINE_CHIPSET_NEAT,
.init = machine_at_super286c_init,
.p1_handler = NULL,
.gpio_handler = NULL,
.available_flag = MACHINE_AVAILABLE,
.gpio_acpi_handler = NULL,
.cpu = {
.package = CPU_PKG_286,
.block = CPU_BLOCK_NONE,
.min_bus = 0,
.max_bus = 0,
.min_voltage = 0,
.max_voltage = 0,
.min_multi = 0,
.max_multi = 0
},
.bus_flags = MACHINE_AT,
.flags = MACHINE_FLAGS_NONE,
.ram = {
.min = 512,
.max = 1024,
.step = 128
},
.nvrmask = 127,
.kbc_device = NULL,
.kbc_p1 = 0xff,
.gpio = 0xffffffff,
.gpio_acpi = 0xffffffff,
.device = NULL,
.fdc_device = NULL,
.sio_device = NULL,
.vid_device = NULL,
.snd_device = NULL,
.net_device = NULL
},
/* Has IBM AT KBC firmware. */
{
.name = "[NEAT] NCR 3302",
@@ -8293,6 +8332,45 @@ const machine_t machines[] = {
.snd_device = NULL,
.net_device = NULL
},
/* Same as Intel Premiere PCI/II, but with a Dell OEM BIOS */
{
.name = "[i430NX] Dell Dimension XPS Pxxx",
.internal_name = "dellplato",
.type = MACHINE_TYPE_SOCKET5,
.chipset = MACHINE_CHIPSET_INTEL_430NX,
.init = machine_at_dellplato_init,
.p1_handler = NULL,
.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 = 1.5
},
.bus_flags = MACHINE_PS2_PCI,
.flags = MACHINE_IDE_DUAL | MACHINE_APM | MACHINE_ACPI,
.ram = {
.min = 2048,
.max = 131072,
.step = 2048
},
.nvrmask = 127,
.kbc_device = NULL,
.kbc_p1 = 0,
.gpio = 0,
.device = NULL,
.fdc_device = NULL,
.sio_device = NULL,
.vid_device = NULL,
.snd_device = NULL,
.net_device = NULL
},
/* This has the Phoenix MultiKey KBC firmware.
This is basically an Intel Premiere/PCI II with a fancier POST screen. */
{
@@ -10628,6 +10706,45 @@ const machine_t machines[] = {
.vid_device = NULL,
.snd_device = NULL,
.net_device = NULL
},
/* PhoenixBIOS 4.0 Rel 6.0 for 430TX, most likely has AMI KBC of some sort. Also has onboard Yamaha YMF701 which can't be emulated yet. */
{
.name = "[i430TX] Micronics Thunderbolt",
.internal_name = "thunderbolt",
.type = MACHINE_TYPE_SOCKET7,
.chipset = MACHINE_CHIPSET_INTEL_430TX,
.init = machine_at_thunderbolt_init,
.p1_handler = NULL,
.gpio_handler = NULL,
.available_flag = MACHINE_AVAILABLE,
.gpio_acpi_handler = NULL,
.cpu = {
.package = CPU_PKG_SOCKET5_7,
.block = CPU_BLOCK(CPU_WINCHIP, CPU_WINCHIP2),
.min_bus = 50000000,
.max_bus = 66666667,
.min_voltage = 2500,
.max_voltage = 3520,
.min_multi = 1.5,
.max_multi = 3.0
},
.bus_flags = MACHINE_PS2_PCI,
.flags = MACHINE_IDE_DUAL | MACHINE_APM | MACHINE_ACPI,
.ram = {
.min = 8192,
.max = 262144,
.step = 8192
},
.nvrmask = 255,
.kbc_device = NULL,
.kbc_p1 = 0,
.gpio = 0,
.device = NULL,
.fdc_device = NULL,
.sio_device = NULL,
.vid_device = NULL,
.snd_device = NULL,
.net_device = NULL
},
/* The BIOS sends KBC command BB and expects it to output a byte, which is AMI KBC behavior. */
{
@@ -11618,6 +11735,45 @@ const machine_t machines[] = {
.snd_device = NULL,
.net_device = NULL
},
/* It's a Intel VS440FX with a Gateway 2000 OEM BIOS */
{
.name = "[i440FX] Gateway 2000 Venus",
.internal_name = "GW2KVenus",
.type = MACHINE_TYPE_SOCKET8,
.chipset = MACHINE_CHIPSET_INTEL_440FX,
.init = machine_at_GW2KVenus_init,
.p1_handler = NULL,
.gpio_handler = NULL,
.available_flag = MACHINE_AVAILABLE,
.gpio_acpi_handler = NULL,
.cpu = {
.package = CPU_PKG_SOCKET8,
.block = CPU_BLOCK_NONE,
.min_bus = 60000000,
.max_bus = 66666667,
.min_voltage = 2100,
.max_voltage = 3500,
.min_multi = 2.0,
.max_multi = 3.5
},
.bus_flags = MACHINE_PS2_PCI,
.flags = MACHINE_IDE_DUAL | MACHINE_APM | MACHINE_ACPI,
.ram = {
.min = 8192,
.max = 524288,
.step = 8192
},
.nvrmask = 127,
.kbc_device = NULL,
.kbc_p1 = 0,
.gpio = 0,
.device = NULL,
.fdc_device = NULL,
.sio_device = NULL,
.vid_device = NULL,
.snd_device = NULL,
.net_device = NULL
},
/* Has the SMC FDC73C935's on-chip KBC with Phoenix MultiKey firmware. */
{
.name = "[i440FX] Micronics M6Mi",
@@ -13046,6 +13202,47 @@ const machine_t machines[] = {
.snd_device = NULL,
.net_device = NULL
},
/* Has an ITE IT8671F Super I/O chip with on-chip KBC with AMIKey-2 KBC
firmware. */
{
.name = "[SMSC VictoryBX-66] PC Chips M773",
.internal_name = "m773",
.type = MACHINE_TYPE_SOCKET370,
.chipset = MACHINE_CHIPSET_SMSC_VICTORYBX_66,
.init = machine_at_m773_init,
.p1_handler = NULL,
.gpio_handler = NULL,
.available_flag = MACHINE_AVAILABLE,
.gpio_acpi_handler = NULL,
.cpu = {
.package = CPU_PKG_SOCKET370,
.block = CPU_BLOCK_NONE,
.min_bus = 66666667,
.max_bus = 133333333,
.min_voltage = 1300,
.max_voltage = 3500,
.min_multi = 1.5,
.max_multi = 8.0
},
.bus_flags = MACHINE_PS2_AGP,
.flags = MACHINE_IDE_DUAL | MACHINE_SOUND | MACHINE_APM | MACHINE_ACPI,
.ram = {
.min = 8192,
.max = 524288,
.step = 8192
},
.nvrmask = 255,
.kbc_device = NULL,
.kbc_p1 = 0xff,
.gpio = 0xffffffff,
.gpio_acpi = 0xffffffff,
.device = NULL,
.fdc_device = NULL,
.sio_device = NULL,
.vid_device = NULL,
.snd_device = &cmi8738_onboard_device,
.net_device = NULL
},
/* VIA Apollo Pro */
/* Has the VIA VT82C586B southbridge with on-chip KBC identical to the VIA

View File

@@ -14,7 +14,8 @@
#
set(net_sources)
list(APPEND net_sources network.c net_pcap.c net_slirp.c net_dp8390.c net_3c501.c
net_3c503.c net_ne2000.c net_pcnet.c net_wd8003.c net_plip.c net_event.c net_null.c)
net_3c503.c net_ne2000.c net_pcnet.c net_wd8003.c net_plip.c net_event.c net_null.c
net_eeprom_nmc93cxx.c net_tulip.c net_rtl8139.c net_l80225.c)
find_package(PkgConfig REQUIRED)
pkg_check_modules(SLIRP REQUIRED IMPORTED_TARGET slirp)

View File

@@ -0,0 +1,293 @@
/*
* 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.
*
* Emulation of National Semiconductors NMC93Cxx EEPROMs.
*
*
* Authors: Cacodemon345
*
* Copyright 2023 Cacodemon345
*/
/* Ported over from QEMU */
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <86box/86box.h>
#include <86box/device.h>
#include <86box/timer.h>
#include <86box/nvr.h>
#include <86box/net_eeprom_nmc93cxx.h>
#include <86box/plat_unused.h>
struct nmc93cxx_eeprom_t {
uint8_t tick;
uint8_t address;
uint8_t command;
uint8_t writable;
uint8_t eecs;
uint8_t eesk;
uint8_t eedo;
uint8_t addrbits;
uint16_t size;
uint16_t data;
char filename[1024];
uint16_t contents[];
};
typedef struct nmc93cxx_eeprom_t nmc93cxx_eeprom_t;
#ifdef ENABLE_NMC93CXX_EEPROM_LOG
int nmc93cxx_eeprom_do_log = ENABLE_NMC93CXX_EEPROM_LOG;
static void
nmc93cxx_eeprom_log(int lvl, const char *fmt, ...)
{
va_list ap;
if (nmc93cxx_eeprom_do_log >= lvl) {
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
# define nmc93cxx_eeprom_log(lvl, fmt, ...)
#endif
static const char *opstring[] = {
"extended", "write", "read", "erase"
};
static void *
nmc93cxx_eeprom_init_params(UNUSED(const device_t *info), void *params)
{
uint16_t nwords = 64;
uint8_t addrbits = 6;
uint8_t filldefault = 1;
nmc93cxx_eeprom_params_t *params_details = (nmc93cxx_eeprom_params_t *) params;
nmc93cxx_eeprom_t *eeprom = NULL;
if (!params)
return NULL;
nwords = params_details->nwords;
switch (nwords) {
case 16:
case 64:
addrbits = 6;
break;
case 128:
case 256:
addrbits = 8;
break;
default:
nwords = 64;
addrbits = 6;
break;
}
eeprom = calloc(1, sizeof(nmc93cxx_eeprom_t) + ((nwords + 1) * 2));
if (!eeprom)
return NULL;
eeprom->size = nwords;
eeprom->addrbits = addrbits;
/* Output DO is tristate, read results in 1. */
eeprom->eedo = 1;
if (params_details->filename) {
FILE *fp = nvr_fopen(params_details->filename, "rb");
strncpy(eeprom->filename, params_details->filename, 1024);
if (fp) {
filldefault = !fread(eeprom->contents, sizeof(uint16_t), nwords, fp);
fclose(fp);
}
}
if (filldefault) {
memcpy(eeprom->contents, params_details->default_content, nwords * sizeof(uint16_t));
}
return eeprom;
}
void
nmc93cxx_eeprom_write(nmc93cxx_eeprom_t *eeprom, int eecs, int eesk, int eedi)
{
uint8_t tick = eeprom->tick;
uint8_t eedo = eeprom->eedo;
uint16_t address = eeprom->address;
uint8_t command = eeprom->command;
nmc93cxx_eeprom_log(1, "CS=%u SK=%u DI=%u DO=%u, tick = %u\n",
eecs, eesk, eedi, eedo, tick);
if (!eeprom->eecs && eecs) {
/* Start chip select cycle. */
nmc93cxx_eeprom_log(1, "Cycle start, waiting for 1st start bit (0)\n");
tick = 0;
command = 0x0;
address = 0x0;
} else if (eeprom->eecs && !eecs) {
/* End chip select cycle. This triggers write / erase. */
if (eeprom->writable) {
uint8_t subcommand = address >> (eeprom->addrbits - 2);
if (command == 0 && subcommand == 2) {
/* Erase all. */
for (address = 0; address < eeprom->size; address++) {
eeprom->contents[address] = 0xffff;
}
} else if (command == 3) {
/* Erase word. */
eeprom->contents[address] = 0xffff;
} else if (tick >= 2 + 2 + eeprom->addrbits + 16) {
if (command == 1) {
/* Write word. */
eeprom->contents[address] &= eeprom->data;
} else if (command == 0 && subcommand == 1) {
/* Write all. */
for (address = 0; address < eeprom->size; address++) {
eeprom->contents[address] &= eeprom->data;
}
}
}
}
/* Output DO is tristate, read results in 1. */
eedo = 1;
} else if (eecs && !eeprom->eesk && eesk) {
/* Raising edge of clock shifts data in. */
if (tick == 0) {
/* Wait for 1st start bit. */
if (eedi == 0) {
nmc93cxx_eeprom_log(1, "Got correct 1st start bit, waiting for 2nd start bit (1)\n");
tick++;
} else {
nmc93cxx_eeprom_log(1, "wrong 1st start bit (is 1, should be 0)\n");
tick = 2;
#if 0
~ assert(!"wrong start bit");
#endif
}
} else if (tick == 1) {
/* Wait for 2nd start bit. */
if (eedi != 0) {
nmc93cxx_eeprom_log(1, "Got correct 2nd start bit, getting command + address\n");
tick++;
} else {
nmc93cxx_eeprom_log(1, "1st start bit is longer than needed\n");
}
} else if (tick < 2 + 2) {
/* Got 2 start bits, transfer 2 opcode bits. */
tick++;
command <<= 1;
if (eedi) {
command += 1;
}
} else if (tick < 2 + 2 + eeprom->addrbits) {
/* Got 2 start bits and 2 opcode bits, transfer all address bits. */
tick++;
address = ((address << 1) | eedi);
if (tick == 2 + 2 + eeprom->addrbits) {
nmc93cxx_eeprom_log(1, "%s command, address = 0x%02x (value 0x%04x)\n",
opstring[command], address, eeprom->contents[address]);
if (command == 2) {
eedo = 0;
}
address = address % eeprom->size;
if (command == 0) {
/* Command code in upper 2 bits of address. */
switch (address >> (eeprom->addrbits - 2)) {
case 0:
nmc93cxx_eeprom_log(1, "write disable command\n");
eeprom->writable = 0;
break;
case 1:
nmc93cxx_eeprom_log(1, "write all command\n");
break;
case 2:
nmc93cxx_eeprom_log(1, "erase all command\n");
break;
case 3:
nmc93cxx_eeprom_log(1, "write enable command\n");
eeprom->writable = 1;
break;
default:
break;
}
} else {
/* Read, write or erase word. */
eeprom->data = eeprom->contents[address];
}
}
} else if (tick < 2 + 2 + eeprom->addrbits + 16) {
/* Transfer 16 data bits. */
tick++;
if (command == 2) {
/* Read word. */
eedo = ((eeprom->data & 0x8000) != 0);
}
eeprom->data <<= 1;
eeprom->data += eedi;
} else {
nmc93cxx_eeprom_log(1, "additional unneeded tick, not processed\n");
}
}
/* Save status of EEPROM. */
eeprom->tick = tick;
eeprom->eecs = eecs;
eeprom->eesk = eesk;
eeprom->eedo = eedo;
eeprom->address = address;
eeprom->command = command;
}
uint16_t
nmc93cxx_eeprom_read(nmc93cxx_eeprom_t *eeprom)
{
/* Return status of pin DO (0 or 1). */
return eeprom->eedo;
}
static void
nmc93cxx_eeprom_close(void *priv)
{
nmc93cxx_eeprom_t *eeprom = (nmc93cxx_eeprom_t *) priv;
FILE *fp = nvr_fopen(eeprom->filename, "wb");
if (fp) {
fwrite(eeprom->contents, 2, eeprom->size, fp);
fclose(fp);
}
free(priv);
}
uint16_t *
nmc93cxx_eeprom_data(nmc93cxx_eeprom_t *eeprom)
{
/* Get EEPROM data array. */
return &eeprom->contents[0];
}
const device_t nmc93cxx_device = {
.name = "National Semiconductor NMC93Cxx",
.internal_name = "nmc93cxx",
.flags = DEVICE_EXTPARAMS,
.local = 0,
.init_ext = nmc93cxx_eeprom_init_params,
.close = nmc93cxx_eeprom_close,
.reset = NULL,
{ .available = NULL },
.speed_changed = NULL,
.force_redraw = NULL,
.config = NULL
};

41
src/network/net_l80225.c Normal file
View File

@@ -0,0 +1,41 @@
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <86box/86box.h>
#include <86box/timer.h>
#include <86box/pci.h>
#include <86box/io.h>
#include <86box/mem.h>
#include <86box/dma.h>
#include <86box/device.h>
#include <86box/thread.h>
#include <86box/network.h>
uint16_t
l80225_mii_readw(uint16_t *regs, uint16_t addr)
{
switch (addr) {
case 0x1:
return 0x782D;
case 0x2:
return 0b10110;
case 0x3:
return 0xF830;
case 0x5:
return 0x41E1;
case 0x18:
return 0xC0;
default:
return regs[addr];
}
return 0;
}
void
l80225_mii_writew(uint16_t *regs, uint16_t addr, uint16_t val)
{
regs[addr] = val;
}

3387
src/network/net_rtl8139.c Normal file

File diff suppressed because it is too large Load Diff

1637
src/network/net_tulip.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -73,6 +73,8 @@
#include <86box/net_pcnet.h>
#include <86box/net_plip.h>
#include <86box/net_wd8003.h>
#include <86box/net_tulip.h>
#include <86box/net_rtl8139.h>
#ifdef _WIN32
# define WIN32_LEAN_AND_MEAN
@@ -131,6 +133,9 @@ static const device_t *net_cards[] = {
&pcnet_am79c970a_device,
&rtl8029as_device,
&pcnet_am79c960_vlb_device,
&dec_tulip_device,
&rtl8139c_plus_device,
&dec_tulip_21140_device,
NULL
};

View File

@@ -15,7 +15,7 @@
add_library(sio OBJECT sio_acc3221.c sio_ali5123.c sio_f82c710.c sio_82091aa.c
sio_fdc37c6xx.c sio_fdc37c67x.c sio_fdc37c669.c sio_fdc37c93x.c sio_fdc37m60x.c
sio_it8661f.c
sio_it86x1f.c
sio_pc87306.c sio_pc87307.c sio_pc87309.c sio_pc87310.c sio_pc87311.c sio_pc87332.c
sio_prime3b.c sio_prime3c.c
sio_w83787f.c sio_w83877f.c sio_w83977f.c sio_um8669f.c

View File

@@ -1,353 +0,0 @@
/*
* 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 ITE IT8661F chipset.
*
* Note: This Super I/O is partially incomplete and intended only for having the intended machine to function
*
* Authors: Tiseno100
*
* Copyright 2021 Tiseno100
*
*/
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#define HAVE_STDARG_H
#include <86box/86box.h>
#include <86box/io.h>
#include <86box/timer.h>
#include <86box/device.h>
#include <86box/lpt.h>
#include <86box/serial.h>
#include <86box/fdd.h>
#include <86box/fdc.h>
#include <86box/fdd_common.h>
#include <86box/sio.h>
#include <86box/plat_unused.h>
#define LDN dev->regs[7]
typedef struct it8661f_t {
fdc_t *fdc_controller;
serial_t *uart[2];
uint8_t index;
uint8_t regs[256];
uint8_t device_regs[6][256];
int unlocked;
int enumerator;
} it8661f_t;
static uint8_t mb_pnp_key[32] = { 0x6a, 0xb5, 0xda, 0xed, 0xf6, 0xfb, 0x7d, 0xbe, 0xdf, 0x6f, 0x37, 0x1b, 0x0d, 0x86, 0xc3, 0x61, 0xb0, 0x58, 0x2c, 0x16, 0x8b, 0x45, 0xa2, 0xd1, 0xe8, 0x74, 0x3a, 0x9d, 0xce, 0xe7, 0x73, 0x39 };
static void it8661f_reset(void *priv);
#ifdef ENABLE_IT8661_LOG
int it8661_do_log = ENABLE_IT8661_LOG;
void
it8661_log(const char *fmt, ...)
{
va_list ap;
if (it8661_do_log) {
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
# define it8661_log(fmt, ...)
#endif
static void
it8661_fdc(uint16_t addr, uint8_t val, it8661f_t *dev)
{
fdc_remove(dev->fdc_controller);
if (((addr == 0x30) && (val & 1)) || (dev->device_regs[0][0x30] & 1)) {
switch (addr) {
case 0x30:
dev->device_regs[0][addr] = val & 1;
break;
case 0x31:
dev->device_regs[0][addr] = val & 3;
if (val & 1)
dev->device_regs[0][addr] |= 0x55;
break;
case 0x60:
case 0x61:
dev->device_regs[0][addr] = val & ((addr == 0x61) ? 0xff : 0xf8);
break;
case 0x70:
dev->device_regs[0][addr] = val & 0x0f;
break;
case 0x74:
dev->device_regs[0][addr] = val & 7;
break;
case 0xf0:
dev->device_regs[0][addr] = val & 0x0f;
break;
default:
break;
}
fdc_set_base(dev->fdc_controller, (dev->device_regs[0][0x60] << 8) | (dev->device_regs[0][0x61]));
fdc_set_irq(dev->fdc_controller, dev->device_regs[0][0x70] & 0x0f);
fdc_set_dma_ch(dev->fdc_controller, dev->device_regs[0][0x74] & 7);
if (dev->device_regs[0][0xf0] & 1)
fdc_writeprotect(dev->fdc_controller);
it8661_log("ITE 8661-FDC: BASE %04x IRQ %02x\n", (dev->device_regs[0][0x60] << 8) | (dev->device_regs[0][0x61]),
dev->device_regs[0][0x70] & 0x0f);
}
}
static void
it8661_serial(int uart, uint16_t addr, uint8_t val, it8661f_t *dev)
{
serial_remove(dev->uart[uart]);
if (((addr == 0x30) && (val & 1)) || (dev->device_regs[1 + uart][0x30] & 1)) {
switch (addr) {
case 0x30:
dev->device_regs[1 + uart][addr] = val & 1;
break;
case 0x60:
case 0x61:
dev->device_regs[1 + uart][addr] = val & ((addr == 0x61) ? 0xff : 0xf8);
break;
case 0x70:
dev->device_regs[1 + uart][addr] = val & 0x0f;
break;
case 0x74:
dev->device_regs[1 + uart][addr] = val & 7;
break;
case 0xf0:
dev->device_regs[1 + uart][addr] = val & 3;
break;
default:
break;
}
serial_setup(dev->uart[uart], (dev->device_regs[1 + uart][0x60] << 8) | (dev->device_regs[1 + uart][0x61]), dev->device_regs[1 + uart][0x70] & 0x0f);
it8661_log("ITE 8661-UART%01x: BASE %04x IRQ %02x\n", 1 + (LDN % 1),
(dev->device_regs[1 + uart][0x60] << 8) | (dev->device_regs[1 + uart][0x61]),
dev->device_regs[1 + uart][0x70] & 0x0f);
}
}
void
it8661_lpt(uint16_t addr, uint8_t val, it8661f_t *dev)
{
lpt1_remove();
if (((addr == 0x30) && (val & 1)) || (dev->device_regs[3][0x30] & 1)) {
switch (addr) {
case 0x30:
dev->device_regs[3][addr] = val & 1;
break;
case 0x60:
case 0x61:
dev->device_regs[3][addr] = val & ((addr == 0x61) ? 0xff : 0xf8);
break;
case 0x70:
dev->device_regs[3][addr] = val & 0x0f;
break;
case 0x74:
dev->device_regs[3][addr] = val & 7;
break;
case 0xf0:
dev->device_regs[3][addr] = val & 3;
break;
default:
break;
}
lpt1_init((dev->device_regs[3][0x60] << 8) | (dev->device_regs[3][0x61]));
lpt1_irq(dev->device_regs[3][0x70] & 0x0f);
it8661_log("ITE 8661-LPT: BASE %04x IRQ %02x\n", (dev->device_regs[3][0x60] << 8) | (dev->device_regs[3][0x61]),
dev->device_regs[3][0x70] & 0x0f);
}
}
void
it8661_ldn(uint16_t addr, uint8_t val, it8661f_t *dev)
{
switch (LDN) {
case 0:
it8661_fdc(addr, val, dev);
break;
case 1:
case 2:
it8661_serial((LDN & 2) - 1, addr, val, dev);
break;
case 3:
it8661_lpt(addr, val, dev);
break;
default:
break;
}
}
static void
it8661f_write(uint16_t addr, uint8_t val, void *priv)
{
it8661f_t *dev = (it8661f_t *) priv;
switch (addr) {
case FDC_SECONDARY_ADDR:
if (!dev->unlocked) {
(val == mb_pnp_key[dev->enumerator]) ? dev->enumerator++ : (dev->enumerator = 0);
if (dev->enumerator == 31) {
dev->unlocked = 1;
it8661_log("ITE8661F: Unlocked!\n");
}
} else
dev->index = val;
break;
case 0x371:
if (dev->unlocked) {
switch (dev->index) {
case 0x02:
dev->regs[dev->index] = val;
if (val & 1)
it8661f_reset(dev);
if (val & 2)
dev->unlocked = 0;
break;
case 0x07:
dev->regs[dev->index] = val;
break;
case 0x22:
dev->regs[dev->index] = val & 0x30;
break;
case 0x23:
dev->regs[dev->index] = val & 0x1f;
break;
default:
it8661_ldn(dev->index, val, dev);
break;
}
}
break;
default:
break;
}
return;
}
static uint8_t
it8661f_read(uint16_t addr, void *priv)
{
const it8661f_t *dev = (it8661f_t *) priv;
it8661_log("IT8661F:\n", addr, dev->regs[dev->index]);
return (addr == 0xa79) ? dev->regs[dev->index] : 0xff;
}
static void
it8661f_reset(void *priv)
{
it8661f_t *dev = (it8661f_t *) priv;
dev->regs[0x20] = 0x86;
dev->regs[0x21] = 0x61;
dev->device_regs[0][0x60] = 3;
dev->device_regs[0][0x61] = 0xf0;
dev->device_regs[0][0x70] = 6;
dev->device_regs[0][0x71] = 2;
dev->device_regs[0][0x74] = 2;
dev->device_regs[1][0x60] = 3;
dev->device_regs[1][0x61] = 0xf8;
dev->device_regs[1][0x70] = 4;
dev->device_regs[1][0x71] = 2;
dev->device_regs[2][0x60] = 2;
dev->device_regs[2][0x61] = 0xf8;
dev->device_regs[2][0x70] = 3;
dev->device_regs[2][0x71] = 2;
dev->device_regs[3][0x60] = 3;
dev->device_regs[3][0x61] = 0x78;
dev->device_regs[3][0x70] = 7;
dev->device_regs[3][0x71] = 2;
dev->device_regs[3][0x74] = 3;
dev->device_regs[3][0xf0] = 3;
}
static void
it8661f_close(void *priv)
{
it8661f_t *dev = (it8661f_t *) priv;
free(dev);
}
static void *
it8661f_init(UNUSED(const device_t *info))
{
it8661f_t *dev = (it8661f_t *) malloc(sizeof(it8661f_t));
memset(dev, 0, sizeof(it8661f_t));
dev->fdc_controller = device_add(&fdc_at_smc_device);
fdc_reset(dev->fdc_controller);
dev->uart[0] = device_add_inst(&ns16550_device, 1);
dev->uart[1] = device_add_inst(&ns16550_device, 2);
io_sethandler(FDC_SECONDARY_ADDR, 0x0002, it8661f_read, NULL, NULL, it8661f_write, NULL, NULL, dev);
dev->enumerator = 0;
dev->unlocked = 0;
it8661f_reset(dev);
return dev;
}
const device_t it8661f_device = {
.name = "ITE IT8661F",
.internal_name = "it8661f",
.flags = 0,
.local = 0,
.init = it8661f_init,
.close = it8661f_close,
.reset = NULL,
{ .available = NULL },
.speed_changed = NULL,
.force_redraw = NULL,
.config = NULL
};

862
src/sio/sio_it86x1f.c Normal file
View File

@@ -0,0 +1,862 @@
/*
* 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.
*
* Emulation of the ITE IT86x1F Super I/O chips.
*
*
*
* Authors: RichardG, <richardg867@gmail.com>
*
* Copyright 2023 RichardG.
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#define HAVE_STDARG_H
#include <86box/86box.h>
#include <86box/device.h>
#include <86box/io.h>
#include <86box/timer.h>
#include <86box/pci.h>
#include <86box/lpt.h>
#include <86box/serial.h>
#include <86box/fdd.h>
#include <86box/fdc.h>
#include <86box/gameport.h>
#include <86box/sio.h>
#include <86box/isapnp.h>
#include <86box/plat_fallthrough.h>
#include <86box/plat_unused.h>
enum {
ITE_IT8661F = 0x8661,
ITE_IT8671F = 0x8681
};
#define CHIP_ID *((uint16_t *) &dev->global_regs[0])
static void it8671f_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv);
static void it8661f_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv);
static const struct {
uint16_t chip_id;
uint16_t unlock_id;
uint8_t gpio_ldn;
/* Fake ROMs to delegate all the logical device register handling over to the ISAPnP subsystem.
The actual ROMs/IDs used by real chips when those are set to ISAPnP mode remain to be seen. */
uint8_t *pnp_rom;
const isapnp_device_config_t *pnp_defaults;
void (*pnp_config_changed)(uint8_t ld, isapnp_device_config_t *config, void *priv);
} it86x1f_models[] = {
{
.chip_id = ITE_IT8661F,
.unlock_id = 0x8661,
.gpio_ldn = 0x05,
.pnp_rom = (uint8_t[]) {
0x26, 0x85, 0x86, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, /* ITE8661, dummy checksum (filled in by isapnp_add_card) */
0x0a, 0x10, 0x10, /* PnP version 1.0, vendor version 1.0 */
0x15, 0x41, 0xd0, 0x07, 0x00, 0x01, /* logical device PNP0700, can participate in boot */
0x23, 0xf8, 0x0f, 0x02, /* IRQ 3/4/5/6/7/8/9/10/11, low true edge sensitive */
0x2a, 0x0f, 0x0c, /* DMA 0/1/2/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */
0x47, 0x01, 0x00, 0x01, 0xf8, 0x0f, 0x08, 0x08, /* I/O 0x100-0xFF8, decodes 16-bit, 8-byte alignment, 8 addresses */
0x15, 0x41, 0xd0, 0x05, 0x01, 0x01, /* logical device PNP0501, can participate in boot */
0x23, 0xf8, 0x0f, 0x02, /* IRQ 3/4/5/6/7/8/9/10/11, low true edge sensitive */
0x47, 0x01, 0x00, 0x01, 0xf8, 0x0f, 0x08, 0x08, /* I/O 0x100-0xFF8, decodes 16-bit, 8-byte alignment, 8 addresses */
0x15, 0x41, 0xd0, 0x05, 0x01, 0x01, /* logical device PNP0501, can participate in boot */
0x23, 0xf8, 0x0f, 0x02, /* IRQ 3/4/5/6/7/8/9/10/11, low true edge sensitive */
0x47, 0x01, 0x00, 0x01, 0xf8, 0x0f, 0x08, 0x08, /* I/O 0x100-0xFF8, decodes 16-bit, 8-byte alignment, 8 addresses */
0x15, 0x41, 0xd0, 0x04, 0x00, 0x01, /* logical device PNP0400, can participate in boot */
0x23, 0xf8, 0x0f, 0x02, /* IRQ 3/4/5/6/7/8/9/10/11, low true edge sensitive */
0x2a, 0x0f, 0x0c, /* DMA 0/1/2/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */
0x47, 0x01, 0x00, 0x01, 0xf8, 0x0f, 0x08, 0x08, /* I/O 0x100-0xFF8, decodes 16-bit, 8-byte alignment, 8 addresses */
0x47, 0x01, 0x00, 0x01, 0xfc, 0x0f, 0x04, 0x04, /* I/O 0x100-0xFFC, decodes 16-bit, 4-byte alignment, 4 addresses */
0x15, 0x41, 0xd0, 0x05, 0x10, 0x01, /* logical device PNP0510, can participate in boot */
0x23, 0xf8, 0x0f, 0x02, /* IRQ 3/4/5/6/7/8/9/10/11, low true edge sensitive */
0x23, 0xf8, 0x0f, 0x02, /* IRQ 3/4/5/6/7/8/9/10/11, low true edge sensitive */
0x2a, 0x0f, 0x0c, /* DMA 0/1/2/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */
0x2a, 0x0f, 0x0c, /* DMA 0/1/2/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */
0x47, 0x01, 0x00, 0x01, 0xf8, 0x0f, 0x08, 0x08, /* I/O 0x100-0xFF8, decodes 16-bit, 8-byte alignment, 8 addresses */
0x47, 0x01, 0x00, 0x01, 0xf8, 0x0f, 0x08, 0x08, /* I/O 0x100-0xFF8, decodes 16-bit, 8-byte alignment, 8 addresses */
0x79, 0x00 /* end tag, dummy checksum (filled in by isapnp_add_card) */
},
.pnp_defaults = (const isapnp_device_config_t[]) {
{
.activate = 0,
.io = { { .base = FDC_PRIMARY_ADDR }, },
.irq = { { .irq = FDC_PRIMARY_IRQ }, },
.dma = { { .dma = FDC_PRIMARY_DMA }, }
}, {
.activate = 0,
.io = { { .base = COM1_ADDR }, },
.irq = { { .irq = COM1_IRQ }, }
}, {
.activate = 0,
.io = { { .base = COM2_ADDR }, },
.irq = { { .irq = COM2_IRQ }, }
}, {
.activate = 0,
.io = { { .base = LPT1_ADDR }, { .base = 0x778 }, },
.irq = { { .irq = LPT1_IRQ }, },
.dma = { { .dma = 3 }, }
}, {
.activate = 0,
.io = { { .base = COM4_ADDR }, { .base = 0x300 }, },
.irq = { { .irq = 10 }, { .irq = 11 }, },
.dma = { { .dma = 1 }, { .dma = 0 }, }
}, {
.activate = -1
}
},
.pnp_config_changed = it8661f_pnp_config_changed
}, {
.chip_id = ITE_IT8671F,
.unlock_id = 0x8680,
.gpio_ldn = 0x07,
.pnp_rom = (uint8_t[]) {
0x26, 0x85, 0x86, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, /* ITE8671, dummy checksum (filled in by isapnp_add_card) */
0x0a, 0x10, 0x10, /* PnP version 1.0, vendor version 1.0 */
0x15, 0x41, 0xd0, 0x07, 0x00, 0x01, /* logical device PNP0700, can participate in boot */
0x23, 0xfa, 0x1f, 0x02, /* IRQ 1/3/4/5/6/7/8/9/10/11/12, low true edge sensitive */
0x2a, 0x0f, 0x0c, /* DMA 0/1/2/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */
0x47, 0x01, 0x00, 0x01, 0xf8, 0x0f, 0x08, 0x08, /* I/O 0x100-0xFF8, decodes 16-bit, 8-byte alignment, 8 addresses */
0x15, 0x41, 0xd0, 0x05, 0x01, 0x01, /* logical device PNP0501, can participate in boot */
0x23, 0xfa, 0x1f, 0x02, /* IRQ 1/3/4/5/6/7/8/9/10/11/12, low true edge sensitive */
0x47, 0x01, 0x00, 0x01, 0xf8, 0x0f, 0x08, 0x08, /* I/O 0x100-0xFF8, decodes 16-bit, 8-byte alignment, 8 addresses */
0x15, 0x41, 0xd0, 0x05, 0x10, 0x01, /* logical device PNP0510, can participate in boot */
0x23, 0xfa, 0x1f, 0x02, /* IRQ 1/3/4/5/6/7/8/9/10/11/12, low true edge sensitive */
0x23, 0xfa, 0x1f, 0x02, /* IRQ 1/3/4/5/6/7/8/9/10/11/12, low true edge sensitive */
0x2a, 0x0f, 0x0c, /* DMA 0/1/2/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */
0x2a, 0x0f, 0x0c, /* DMA 0/1/2/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */
0x47, 0x01, 0x00, 0x01, 0xf8, 0x0f, 0x08, 0x08, /* I/O 0x100-0xFF8, decodes 16-bit, 8-byte alignment, 8 addresses */
0x47, 0x01, 0x00, 0x01, 0xf8, 0x0f, 0x08, 0x08, /* I/O 0x100-0xFF8, decodes 16-bit, 8-byte alignment, 8 addresses */
0x15, 0x41, 0xd0, 0x04, 0x00, 0x01, /* logical device PNP0400, can participate in boot */
0x23, 0xfa, 0x1f, 0x02, /* IRQ 1/3/4/5/6/7/8/9/10/11/12, low true edge sensitive */
0x2a, 0x0f, 0x0c, /* DMA 0/1/2/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */
0x47, 0x01, 0x00, 0x01, 0xf8, 0x0f, 0x08, 0x08, /* I/O 0x100-0xFF8, decodes 16-bit, 8-byte alignment, 8 addresses */
0x47, 0x01, 0x00, 0x01, 0xfc, 0x0f, 0x04, 0x04, /* I/O 0x100-0xFFC, decodes 16-bit, 4-byte alignment, 4 addresses */
0x15, 0x41, 0xd0, 0xff, 0xff, 0x00, /* logical device PNPFFFF (dummy to create APC gap in LDNs) */
0x15, 0x41, 0xd0, 0x03, 0x03, 0x01, /* logical device PNP0303, can participate in boot */
0x23, 0xfa, 0x1f, 0x02, /* IRQ 1/3/4/5/6/7/8/9/10/11/12, low true edge sensitive */
0x47, 0x01, 0x00, 0x00, 0xff, 0x0f, 0x01, 0x01, /* I/O 0x0-0xFFF, decodes 16-bit, 1-byte alignment, 1 address */
0x47, 0x01, 0x00, 0x00, 0xff, 0x0f, 0x01, 0x01, /* I/O 0x0-0xFFF, decodes 16-bit, 1-byte alignment, 1 address */
0x15, 0x41, 0xd0, 0x0f, 0x13, 0x01, /* logical device PNP0F13, can participate in boot */
0x23, 0xfa, 0x1f, 0x02, /* IRQ 1/3/4/5/6/7/8/9/10/11/12, low true edge sensitive */
0x79, 0x00 /* end tag, dummy checksum (filled in by isapnp_add_card) */
},
.pnp_defaults = (const isapnp_device_config_t[]) {
{
.activate = 0,
.io = { { .base = FDC_PRIMARY_ADDR }, },
.irq = { { .irq = FDC_PRIMARY_IRQ }, },
.dma = { { .dma = FDC_PRIMARY_DMA }, }
}, {
.activate = 0,
.io = { { .base = COM1_ADDR }, },
.irq = { { .irq = COM1_IRQ }, }
}, {
.activate = 0,
.io = { { .base = COM2_ADDR }, { .base = 0x300 }, },
.irq = { { .irq = COM2_IRQ }, { .irq = 10 }, },
.dma = { { .dma = 0 }, { .dma = 1 }, }
}, {
.activate = 0,
.io = { { .base = LPT1_ADDR }, { .base = 0x778 }, },
.irq = { { .irq = LPT1_IRQ }, },
.dma = { { .dma = 3 }, }
}, {
.activate = 0
}, {
.activate = 1,
.io = { { .base = 0x60 }, { .base = 0x64 }, },
.irq = { { .irq = 1 }, }
}, {
.activate = 0,
.irq = { { .irq = 12 }, }
}, {
.activate = -1
}
},
.pnp_config_changed = it8671f_pnp_config_changed
}
};
#ifdef ENABLE_IT86X1F_LOG
int it86x1f_do_log = ENABLE_IT86X1F_LOG;
static void
it86x1f_log(const char *fmt, ...)
{
va_list ap;
if (it86x1f_do_log) {
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
# define it86x1f_log(fmt, ...)
#endif
typedef struct it86x1f_t {
uint8_t instance;
uint8_t locked;
uint8_t cur_ldn;
uint8_t cur_reg;
void *pnp_card;
uint8_t global_regs[16]; /* [0x20:0x2f] */
uint8_t ldn_regs[8][16]; /* [0xf0:0xff] */
uint8_t gpio_regs[36]; /* [0x60:0x7f] then [0xe0:0xe3] */
uint8_t gpio_ldn;
uint16_t unlock_id;
uint16_t addr_port;
uint16_t data_port;
uint8_t unlock_val;
uint8_t unlock_pos : 2;
uint8_t key_pos : 5;
fdc_t *fdc;
serial_t *uart[2];
void *gameport;
} it86x1f_t;
static void it86x1f_remap(it86x1f_t *dev, uint16_t addr_port, uint16_t data_port);
static void
it8661f_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv)
{
if (ld > 5) {
it86x1f_log("IT86x1F: Unknown logical device %d\n", ld);
return;
}
it86x1f_t *dev = (it86x1f_t *) priv;
switch (ld) {
case 0:
fdc_remove(dev->fdc);
if (config->activate) {
it86x1f_log("IT86x1F: FDC enabled at port %04X IRQ %d DMA %d\n", config->io[0].base, config->irq[0].irq, (config->dma[0].dma == ISAPNP_DMA_DISABLED) ? -1 : config->dma[0].dma);
if (config->io[0].base != ISAPNP_IO_DISABLED)
fdc_set_base(dev->fdc, config->io[0].base);
fdc_set_irq(dev->fdc, config->irq[0].irq);
fdc_set_dma_ch(dev->fdc, (config->dma[0].dma == ISAPNP_DMA_DISABLED) ? -1 : config->dma[0].dma);
} else {
it86x1f_log("IT86x1F: FDC disabled\n");
}
break;
case 1:
case 2:
serial_remove(dev->uart[ld - 1]);
if (config->activate && (config->io[0].base != ISAPNP_IO_DISABLED)) {
it86x1f_log("IT86x1F: UART %d enabled at port %04X IRQ %d\n", ld - 1, config->io[0].base, config->irq[0].irq);
serial_setup(dev->uart[ld - 1], config->io[0].base, config->irq[0].irq);
} else {
it86x1f_log("IT86x1F: UART %d disabled\n", ld - 1);
}
break;
case 3:
lpt1_remove();
if (config->activate && (config->io[0].base != ISAPNP_IO_DISABLED)) {
it86x1f_log("IT86x1F: LPT enabled at port %04X IRQ %d\n", config->io[0].base, config->irq[0].irq);
lpt1_init(config->io[0].base);
} else {
it86x1f_log("IT86x1F: LPT disabled\n");
}
break;
case 4:
if (config->activate && (config->io[0].base != ISAPNP_IO_DISABLED)) {
it86x1f_log("IT86x1F: IR enabled at ports %04X %04X IRQs %d %d DMAs %d %d\n", config->io[0].base, config->io[1].base, config->irq[0].irq, config->irq[1].irq, (config->dma[0].dma == ISAPNP_DMA_DISABLED) ? -1 : config->dma[0].dma, (config->dma[1].dma == ISAPNP_DMA_DISABLED) ? -1 : config->dma[1].dma);
} else {
it86x1f_log("IT86x1F: IR disabled\n");
}
break;
default:
break;
}
}
static void
it8671f_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv)
{
it86x1f_t *dev = (it86x1f_t *) priv;
switch (ld) {
case 2:
it8661f_pnp_config_changed(4, config, dev); /* just for logging, should change if IR UART is implemented */
fallthrough;
case 0 ... 1:
case 3:
it8661f_pnp_config_changed(ld, config, dev);
break;
case 5:
if (config->activate && (config->io[0].base != ISAPNP_IO_DISABLED) && (config->io[1].base != ISAPNP_IO_DISABLED)) {
it86x1f_log("IT86x1F: KBC enabled at ports %04X %04X IRQ %d\n", config->io[0].base, config->io[1].base, config->irq[0].irq);
} else {
it86x1f_log("IT86x1F: KBC disabled\n");
}
break;
case 6:
if (config->activate) {
it86x1f_log("IT86x1F: KBC mouse enabled at IRQ %d\n", config->irq[0].irq);
} else {
it86x1f_log("IT86x1F: KBC mouse disabled\n");
}
break;
default:
break;
}
}
static uint8_t
it86x1f_pnp_read_vendor_reg(uint8_t ld, uint8_t reg, void *priv)
{
it86x1f_t *dev = (it86x1f_t *) priv;
uint8_t ret = 0xff;
switch (reg) {
case 0x20 ... 0x2f:
ret = dev->global_regs[reg & 0x0f];
break;
case 0x60 ... 0x7f:
if (ld != dev->gpio_ldn)
break;
ret = dev->gpio_regs[reg & 0x1f];
break;
case 0xe0 ... 0xe3:
if (ld != dev->gpio_ldn)
break;
ret = dev->gpio_regs[0x20 | (reg & 0x03)];
break;
case 0xf0 ... 0xff:
if (ld > dev->gpio_ldn)
break;
ret = dev->ldn_regs[ld][reg & 0x0f];
break;
default:
break;
}
it86x1f_log("IT86x1F: read_vendor_reg(%X, %02X) = %02X\n", ld, reg, ret);
return ret;
}
static void
it86x1f_pnp_write_vendor_reg(uint8_t ld, uint8_t reg, uint8_t val, void *priv)
{
it86x1f_t *dev = (it86x1f_t *) priv;
it86x1f_log("IT86x1F: write_vendor_reg(%X, %02X, %02X)\n", ld, reg, val);
switch (reg) {
case 0x22:
if (CHIP_ID == ITE_IT8661F) {
dev->global_regs[reg & 0x0f] = (val & 0x30) | (dev->global_regs[reg & 0x0f] & ~0x30);
uint8_t mcc = (val & 0x30) >> 4;
if (mcc != dev->instance) {
it86x1f_log("IT86x1F: Instance %d unmapping as ID %d was written\n", dev->instance, mcc);
it86x1f_remap(dev, 0, 0);
}
}
break;
case 0x23:
val &= (1 << dev->gpio_ldn) - 1;
dev->global_regs[reg & 0x0f] = val;
if (val)
pclog("IT86x1F: Warning: ISAPnP mode enabled.\n");
break;
case 0x24:
dev->global_regs[reg & 0x0f] = val & ((CHIP_ID == ITE_IT8661F) ? 0x03 : 0x5f);
break;
case 0x25:
val &= (CHIP_ID == ITE_IT8661F) ? 0x1f : 0xf0;
fallthrough;
case 0x26:
if (ld == dev->gpio_ldn)
dev->global_regs[reg & 0x0f] = val;
break;
case 0x2e ... 0x2f:
if ((CHIP_ID == ITE_IT8671F) && (ld == 0xf4))
dev->global_regs[reg & 0x0f] = val;
break;
case 0x60 ... 0x7f:
if (ld != dev->gpio_ldn)
break;
dev->gpio_regs[reg & 0x1f] = val;
break;
case 0xe0 ... 0xe3:
if (ld != dev->gpio_ldn)
break;
dev->gpio_regs[0x20 | (reg & 0x0f)] = val;
break;
case 0xf0 ... 0xff:
/* Translate GPIO LDN to 7 for the switch block. */
uint8_t effective_ldn;
if (ld == dev->gpio_ldn)
effective_ldn = 7;
else if (ld == 7)
effective_ldn = 8; /* dummy */
else
effective_ldn = ld;
switch ((effective_ldn << 8) | reg) {
case 0x0f0:
dev->ldn_regs[ld][reg & 0x0f] = val & 0x0f;
fdc_set_swwp(dev->fdc, !!(val & 0x01));
fdc_set_swap(dev->fdc, !!(val & 0x04));
break;
case 0x1f0:
dev->ldn_regs[ld][reg & 0x0f] = val & 0x03;
break;
case 0x2f0:
dev->ldn_regs[ld][reg & 0x0f] = val & ((CHIP_ID == ITE_IT8661F) ? 0x03 : 0xf3);
break;
case 0x2f1:
if (CHIP_ID == ITE_IT8671F)
dev->ldn_regs[ld][reg & 0x0f] = val & 0xb7;
break;
case 0x3f0:
dev->ldn_regs[ld][reg & 0x0f] = val & 0x07;
break;
case 0x4f0:
if (CHIP_ID == ITE_IT8661F)
val &= 0x3f;
dev->ldn_regs[ld][reg & 0x0f] = val;
break;
case 0x4f1:
if (CHIP_ID == ITE_IT8671F)
dev->ldn_regs[ld][reg & 0x0f] = val & 0x7f;
break;
case 0x4f2:
case 0x4f6:
if (CHIP_ID == ITE_IT8671F)
dev->ldn_regs[ld][reg & 0x0f] = val;
break;
case 0x4f7:
if (CHIP_ID == ITE_IT8671F)
dev->ldn_regs[ld][reg & 0x0f] = val & 0x7f;
break;
case 0x4f8:
if (CHIP_ID == ITE_IT8671F)
dev->ldn_regs[ld][reg & 0x0f] = val & 0x07;
break;
case 0x5f0:
dev->ldn_regs[ld][reg & 0x0f] = val & 0x1f;
break;
case 0x6f0:
if (CHIP_ID == ITE_IT8671F)
dev->ldn_regs[ld][reg & 0x0f] = val & 0x03;
break;
case 0x760:
case 0x762:
case 0x764:
case 0x766:
dev->gpio_regs[reg & 0x1f] = val & 0x0f;
break;
case 0x772:
if (CHIP_ID != ITE_IT8671F)
break;
fallthrough;
case 0x761:
case 0x763:
case 0x765:
case 0x767:
case 0x770:
dev->gpio_regs[reg & 0x1f] = val;
case 0x771:
if (CHIP_ID == ITE_IT8671F)
dev->gpio_regs[reg & 0x1f] = val & 0xde;
break;
case 0x7e0:
if (CHIP_ID == ITE_IT8671F)
dev->gpio_regs[0x20 | (reg & 0x03)] = val & 0xef;
break;
case 0x7e1:
if (CHIP_ID == ITE_IT8671F)
dev->gpio_regs[0x20 | (reg & 0x03)] = val & 0x7f;
break;
case 0x7e3:
if ((CHIP_ID == ITE_IT8671F) && (val & 0x80))
*((uint16_t *) &dev->gpio_regs[0x22]) = 0x0000;
break;
case 0x7fb:
if (CHIP_ID == ITE_IT8671F)
val &= 0x7f;
fallthrough;
case 0x7f0 ... 0x7f5:
dev->ldn_regs[ld][reg & 0x0f] = val;
break;
case 0x7f6:
dev->ldn_regs[ld][reg & 0x0f] = val & ((CHIP_ID == ITE_IT8661F) ? 0x3f : 0xcf);
break;
case 0x7f7:
dev->ldn_regs[ld][reg & 0x0f] = val & ((CHIP_ID == ITE_IT8661F) ? 0x9f : 0xdf);
break;
case 0x7f8 ... 0x7fa:
dev->ldn_regs[ld][reg & 0x0f] = val & ((CHIP_ID == ITE_IT8661F) ? 0x1f : 0x0f);
break;
case 0x7fc:
if (CHIP_ID == ITE_IT8661F)
dev->ldn_regs[ld][reg & 0x0f] = val;
break;
case 0x7ff:
if (CHIP_ID == ITE_IT8671F)
dev->ldn_regs[ld][reg & 0x0f] = val & 0x2f;
break;
default:
break;
}
break;
default:
break;
}
}
static void
it86x1f_write_addr(uint16_t port, uint8_t val, void *priv)
{
it86x1f_t *dev = (it86x1f_t *) priv;
it86x1f_log("IT86x1F: write_addr(%04X, %02X)\n", port, val);
if (dev->locked) {
if (val == isapnp_init_key[dev->key_pos]) {
if (++dev->key_pos == 0) {
it86x1f_log("IT86x1F: Unlocked\n");
dev->locked = 0;
}
} else {
dev->key_pos = 0;
}
} else {
dev->cur_reg = val;
}
}
static void
it86x1f_write_data(uint16_t port, uint8_t val, void *priv)
{
it86x1f_t *dev = (it86x1f_t *) priv;
it86x1f_log("IT86x1F: write_data(%04X, %02X)\n", port, val);
if (dev->locked)
return;
switch (dev->cur_reg) {
case 0x00 ... 0x01:
case 0x03 ... 0x06:
case 0x31:
case 0x71:
case 0x73:
break; /* ISAPnP-only */
case 0x07:
dev->cur_ldn = val;
break;
case 0x02:
if (val & 0x02) {
it86x1f_log("IT86x1F: Locked => ");
dev->locked = 1;
it86x1f_remap(dev, 0, 0);
}
fallthrough;
default:
isapnp_write_reg(dev->pnp_card, dev->cur_ldn, dev->cur_reg, val);
break;
}
}
static uint8_t
it86x1f_read_addr(uint16_t port, void *priv)
{
it86x1f_t *dev = (it86x1f_t *) priv;
uint8_t ret = dev->locked ? 0xff : dev->cur_reg;
it86x1f_log("IT86x1F: read_addr(%04X) = %02X\n", port, ret);
return ret;
}
static uint8_t
it86x1f_read_data(uint16_t port, void *priv)
{
it86x1f_t *dev = (it86x1f_t *) priv;
uint8_t ret = 0xff;
switch (dev->cur_reg) {
case 0x00 ... 0x01:
case 0x03 ... 0x06:
case 0x31:
case 0x71:
case 0x73:
break; /* ISAPnP-only */
case 0x07:
ret = dev->cur_ldn;
break;
default:
ret = isapnp_read_reg(dev->pnp_card, dev->cur_ldn, dev->cur_reg);
break;
}
it86x1f_log("IT86x1F: read_data(%04X) = %02X\n", port, ret);
return ret;
}
static void
it86x1f_remap(it86x1f_t *dev, uint16_t addr_port, uint16_t data_port)
{
if (dev->addr_port)
io_removehandler(dev->addr_port, 1, it86x1f_read_addr, NULL, NULL, it86x1f_write_addr, NULL, NULL, dev);
if (dev->data_port)
io_removehandler(dev->data_port, 1, it86x1f_read_data, NULL, NULL, it86x1f_write_data, NULL, NULL, dev);
it86x1f_log("IT86x1F: remap(%04X, %04X)\n", addr_port, data_port);
dev->addr_port = addr_port;
dev->data_port = data_port;
if (dev->addr_port)
io_sethandler(dev->addr_port, 1, it86x1f_read_addr, NULL, NULL, it86x1f_write_addr, NULL, NULL, dev);
if (dev->data_port)
io_sethandler(dev->data_port, 1, it86x1f_read_data, NULL, NULL, it86x1f_write_data, NULL, NULL, dev);
}
static void
it86x1f_write_unlock(UNUSED(uint16_t port), uint8_t val, void *priv)
{
it86x1f_t *dev = (it86x1f_t *) priv;
it86x1f_log("IT86x1F: write_unlock(%04X, %02X)\n", port, val);
if (!dev->locked)
dev->unlock_pos = 0;
switch (dev->unlock_pos++) {
case 0:
if (val != (dev->unlock_id >> 8))
dev->unlock_pos = 0;
break;
case 1:
if (val != (dev->unlock_id & 0xff))
dev->unlock_pos = 0;
break;
case 2:
if ((val != 0x55) && (val != 0xaa))
dev->unlock_pos = 0;
else
dev->unlock_val = val;
break;
case 3:
switch ((dev->unlock_val << 8) | val) {
case 0x5555:
it86x1f_remap(dev, 0x3f0, 0x3f1);
break;
case 0x55aa:
it86x1f_remap(dev, 0x3bd, 0x3bf);
break;
case 0xaa55:
it86x1f_remap(dev, 0x370, 0x371);
break;
default:
it86x1f_remap(dev, 0, 0);
break;
}
dev->unlock_pos = 0;
break;
}
}
void
it86x1f_reset(it86x1f_t *dev)
{
it86x1f_log("IT86x1F: reset()\n");
fdc_reset(dev->fdc);
serial_remove(dev->uart[0]);
serial_remove(dev->uart[1]);
lpt1_remove();
isapnp_enable_card(dev->pnp_card, ISAPNP_CARD_DISABLE);
dev->locked = 1;
isapnp_reset_card(dev->pnp_card);
}
static void
it86x1f_close(void *priv)
{
it86x1f_t *dev = (it86x1f_t *) priv;
it86x1f_log("IT86x1F: close()\n");
free(dev);
}
static void *
it86x1f_init(UNUSED(const device_t *info))
{
it86x1f_t *dev = (it86x1f_t *) malloc(sizeof(it86x1f_t));
memset(dev, 0, sizeof(it86x1f_t));
uint8_t i;
for (i = 0; i < (sizeof(it86x1f_models) / sizeof(it86x1f_models[0])); i++) {
if (it86x1f_models[i].chip_id == info->local)
break;
}
if (i >= (sizeof(it86x1f_models) / sizeof(it86x1f_models[0]))) {
fatal("IT86x1F: Unknown type %04X selected\n", info->local);
return NULL;
}
it86x1f_log("IT86x1F: init(%04X)\n", info->local);
/* Let the resource data parser figure out the ROM size. */
dev->pnp_card = isapnp_add_card(it86x1f_models[i].pnp_rom, -1, it86x1f_models[i].pnp_config_changed, NULL, it86x1f_pnp_read_vendor_reg, it86x1f_pnp_write_vendor_reg, dev);
for (uint8_t j = 0; it86x1f_models[i].pnp_defaults[j].activate != (uint8_t) -1; j++)
isapnp_set_device_defaults(dev->pnp_card, j, &it86x1f_models[i].pnp_defaults[j]);
dev->fdc = device_add(&fdc_at_smc_device);
dev->uart[0] = device_add_inst(&ns16550_device, 1);
dev->uart[1] = device_add_inst(&ns16550_device, 2);
dev->gameport = gameport_add(&gameport_sio_device);
dev->instance = device_get_instance();
dev->gpio_ldn = it86x1f_models[i].gpio_ldn;
CHIP_ID = it86x1f_models[i].chip_id;
dev->unlock_id = it86x1f_models[i].unlock_id;
io_sethandler(0x279, 1, NULL, NULL, NULL, it86x1f_write_unlock, NULL, NULL, dev);
it86x1f_reset(dev);
return dev;
}
const device_t it8661f_device = {
.name = "ITE IT8661F Super I/O",
.internal_name = "it8661f",
.flags = 0,
.local = ITE_IT8661F,
.init = it86x1f_init,
.close = it86x1f_close,
.reset = NULL,
{ .available = NULL },
.speed_changed = NULL,
.force_redraw = NULL,
.config = NULL
};
const device_t it8671f_device = {
.name = "ITE IT8671F Super I/O",
.internal_name = "it8671f",
.flags = 0,
.local = ITE_IT8671F,
.init = it86x1f_init,
.close = it86x1f_close,
.reset = NULL,
{ .available = NULL },
.speed_changed = NULL,
.force_redraw = NULL,
.config = NULL
};

View File

@@ -18,6 +18,35 @@
* Copyright 2016-2021 Miran Grca.
* Copyright 2021 RichardG.
*/
/*
UMC UM8669F non-PnP register definitions
C0:
[7] Infrared half duplex
[4:3] LPT mode:
00 SPP
01 EPP
10 ECP
11 ECP + EPP
C1:
[7] Enable PnP access
[6:0] Always set regardless of PnP access enabled/disabled
C2:
[6:5] Potentially pin muxing mode: (names from AMI "IR group" setup option)
00 Reserved
01 A (no IDE)
10 B (no IDE)
11 C
[4:3] Infrared mode:
00 Reserved
01 HPSIR
10 ASKIR
11 Disabled
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
@@ -35,8 +64,10 @@
#include <86box/fdd.h>
#include <86box/fdc.h>
#include <86box/gameport.h>
#include <86box/sio.h>
#include <86box/hdc.h>
#include <86box/isapnp.h>
#include <86box/hdc_ide.h>
#include <86box/sio.h>
#include <86box/plat_unused.h>
/* Real chips don't have a PnP ROM and instead rely on the BIOS going in blind.
@@ -63,7 +94,9 @@ static uint8_t um8669f_pnp_rom[] = {
0x22, 0xfa, 0x1f, /* IRQ 1/3/4/5/6/7/8/9/10/11/12 */
0x47, 0x00, 0x00, 0x01, 0xf8, 0x03, 0x08, 0x08, /* I/O 0x100-0x3F8, decodes 10-bit, 8-byte alignment, 8 addresses */
0x15, 0x41, 0xd0, 0xff, 0xff, 0x00, /* logical device PNPFFFF (dummy to create a gap in LDNs) */
0x15, 0x41, 0xd0, 0x06, 0x00, 0x01, /* logical device PNP0600, can participate in boot */
0x22, 0xfa, 0x1f, /* IRQ 1/3/4/5/6/7/8/9/10/11/12 */
0x47, 0x00, 0x00, 0x01, 0xf8, 0x03, 0x08, 0x08, /* I/O 0x100-0x3F8, decodes 10-bit, 8-byte alignment, 8 addresses */
0x15, 0x41, 0xd0, 0xb0, 0x2f, 0x01, /* logical device PNPB02F, can participate in boot */
0x47, 0x00, 0x00, 0x01, 0xf8, 0x03, 0x08, 0x08, /* I/O 0x100-0x3F8, decodes 10-bit, 8-byte alignment, 8 addresses */
@@ -89,7 +122,9 @@ static const isapnp_device_config_t um8669f_pnp_defaults[] = {
.io = { { .base = LPT1_ADDR }, },
.irq = { { .irq = LPT1_IRQ }, }
}, {
.activate = 0
.activate = 0,
.io = { { .base = 0x1f0 }, },
.irq = { { .irq = 14 }, }
}, {
.activate = 0,
.io = { { .base = 0x200 }, }
@@ -116,13 +151,13 @@ um8669f_log(const char *fmt, ...)
typedef struct um8669f_t {
uint8_t locked;
uint8_t cur_reg_108;
uint8_t cur_reg;
void *pnp_card;
uint8_t regs_108[256];
uint8_t regs[3];
fdc_t *fdc;
serial_t *uart[2];
uint8_t ide;
void *gameport;
} um8669f_t;
@@ -179,6 +214,18 @@ um8669f_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *pri
break;
case 4:
if (config->activate && (config->io[0].base != ISAPNP_IO_DISABLED))
um8669f_log("UM8669F: IDE enabled at port %04X IRQ %d\n", config->io[0].base, config->irq[0].irq);
else
um8669f_log("UM8669F: IDE disabled\n");
if (dev->ide < IDE_BUS_MAX) {
config->io[1].base = config->io[0].base + 0x206; /* status port apparently fixed */
ide_pnp_config_changed(0, config, (void *) (int) dev->ide);
}
break;
case 5:
if (config->activate && (config->io[0].base != ISAPNP_IO_DISABLED)) {
um8669f_log("UM8669F: Game port enabled at port %04X\n", config->io[0].base);
@@ -209,11 +256,11 @@ um8669f_write(uint16_t port, uint8_t val, void *priv)
if (val == 0x55)
dev->locked = 1;
else
dev->cur_reg_108 = val;
} else {
dev->regs_108[dev->cur_reg_108] = val;
dev->cur_reg = val;
} else if ((dev->cur_reg >= 0xc0) && (dev->cur_reg <= 0xc2)) {
dev->regs[dev->cur_reg & 3] = val;
if (dev->cur_reg_108 == 0xc1) {
if (dev->cur_reg == 0xc1) {
um8669f_log("UM8669F: ISAPnP %sabled\n", (val & 0x80) ? "en" : "dis");
isapnp_enable_card(dev->pnp_card, (val & 0x80) ? ISAPNP_CARD_FORCE_CONFIG : ISAPNP_CARD_DISABLE);
}
@@ -229,9 +276,9 @@ um8669f_read(uint16_t port, void *priv)
if (!dev->locked) {
if (port == 0x108)
ret = dev->cur_reg_108; /* ??? */
else
ret = dev->regs_108[dev->cur_reg_108];
ret = dev->cur_reg; /* ??? */
else if ((dev->cur_reg >= 0xc0) && (dev->cur_reg <= 0xc2))
ret = dev->regs[dev->cur_reg & 3];
}
um8669f_log("UM8669F: read(%04X) = %02X\n", port, ret);
@@ -252,6 +299,9 @@ um8669f_reset(um8669f_t *dev)
lpt1_remove();
if (dev->ide < IDE_BUS_MAX)
ide_remove_handlers(dev->ide);
isapnp_enable_card(dev->pnp_card, ISAPNP_CARD_DISABLE);
dev->locked = 1;
@@ -270,9 +320,9 @@ um8669f_close(void *priv)
}
static void *
um8669f_init(UNUSED(const device_t *info))
um8669f_init(const device_t *info)
{
um8669f_log("UM8669F: init()\n");
um8669f_log("UM8669F: init(%02X)\n", info->local);
um8669f_t *dev = (um8669f_t *) malloc(sizeof(um8669f_t));
memset(dev, 0, sizeof(um8669f_t));
@@ -286,6 +336,10 @@ um8669f_init(UNUSED(const device_t *info))
dev->uart[0] = device_add_inst(&ns16550_device, 1);
dev->uart[1] = device_add_inst(&ns16550_device, 2);
dev->ide = info->local;
if (dev->ide < IDE_BUS_MAX)
device_add(&ide_isa_device);
dev->gameport = gameport_add(&gameport_sio_device);
io_sethandler(0x0108, 0x0002,
@@ -300,6 +354,20 @@ const device_t um8669f_device = {
.name = "UMC UM8669F Super I/O",
.internal_name = "um8669f",
.flags = 0,
.local = 0xff,
.init = um8669f_init,
.close = um8669f_close,
.reset = NULL,
{ .available = NULL },
.speed_changed = NULL,
.force_redraw = NULL,
.config = NULL
};
const device_t um8669f_ide_device = {
.name = "UMC UM8669F Super I/O (With IDE)",
.internal_name = "um8669f_ide",
.flags = 0,
.local = 0,
.init = um8669f_init,
.close = um8669f_close,
@@ -309,3 +377,17 @@ const device_t um8669f_device = {
.force_redraw = NULL,
.config = NULL
};
const device_t um8669f_ide_sec_device = {
.name = "UMC UM8669F Super I/O (With Secondary IDE)",
.internal_name = "um8669f_ide_sec",
.flags = 0,
.local = 1,
.init = um8669f_init,
.close = um8669f_close,
.reset = NULL,
{ .available = NULL },
.speed_changed = NULL,
.force_redraw = NULL,
.config = NULL
};

View File

@@ -18,7 +18,7 @@ add_library(snd OBJECT sound.c snd_opl.c snd_opl_nuked.c snd_opl_ymfm.cpp snd_re
snd_lpt_dss.c snd_ps1.c snd_adlib.c snd_adlibgold.c snd_ad1848.c snd_audiopci.c
snd_azt2316a.c snd_cms.c snd_cmi8x38.c snd_cs423x.c snd_gus.c snd_sb.c snd_sb_dsp.c
snd_emu8k.c snd_mpu401.c snd_sn76489.c snd_ssi2001.c snd_wss.c snd_ym7128.c
snd_optimc.c)
snd_optimc.c midi_opl4.c midi_opl4_yrw801.c)
if(OPENAL)
if(VCPKG_TOOLCHAIN)

View File

@@ -100,6 +100,7 @@ static const MIDI_OUT_DEVICE devices[] = {
#ifdef USE_RTMIDI
{ &rtmidi_output_device },
#endif
{ &opl4_midi_device },
{ NULL }
// clang-format on
};

732
src/sound/midi_opl4.c Normal file
View File

@@ -0,0 +1,732 @@
// Based off ROBOPLAY's OPL4 MID player code, with some fixes and modifications to make it work well.
#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdatomic.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <86box/86box.h>
#include <86box/device.h>
#include <86box/mem.h>
#include <86box/midi.h>
#include <86box/plat.h>
#include <86box/thread.h>
#include <86box/rom.h>
#include <86box/sound.h>
#include <86box/ui.h>
#include <86box/snd_opl.h>
#include <86box/opl4_defines.h>
#include "yrw801.h"
#define NR_OF_MIDI_CHANNELS 16
#define NR_OF_WAVE_CHANNELS 24
#define DRUM_CHANNEL 9
typedef struct
{
uint8_t instrument;
uint8_t panpot;
uint8_t vibrato;
bool drum_channel;
} MIDI_CHANNEL_DATA;
typedef struct
{
bool is_active;
uint64_t activated;
uint8_t number;
MIDI_CHANNEL_DATA *midi_channel;
uint8_t note;
uint8_t velocity;
const YRW801_WAVE_DATA *wave_data;
uint8_t level_direct;
uint8_t reg_f_number;
uint8_t reg_misc;
uint8_t reg_lfo_vibrato;
} VOICE_DATA;
static const int16_t g_wave_pitch_map[0x600] = {
0x000, 0x000, 0x001, 0x001, 0x002, 0x002, 0x003, 0x003,
0x004, 0x004, 0x005, 0x005, 0x006, 0x006, 0x006, 0x007,
0x007, 0x008, 0x008, 0x009, 0x009, 0x00a, 0x00a, 0x00b,
0x00b, 0x00c, 0x00c, 0x00d, 0x00d, 0x00d, 0x00e, 0x00e,
0x00f, 0x00f, 0x010, 0x010, 0x011, 0x011, 0x012, 0x012,
0x013, 0x013, 0x014, 0x014, 0x015, 0x015, 0x015, 0x016,
0x016, 0x017, 0x017, 0x018, 0x018, 0x019, 0x019, 0x01a,
0x01a, 0x01b, 0x01b, 0x01c, 0x01c, 0x01d, 0x01d, 0x01e,
0x01e, 0x01e, 0x01f, 0x01f, 0x020, 0x020, 0x021, 0x021,
0x022, 0x022, 0x023, 0x023, 0x024, 0x024, 0x025, 0x025,
0x026, 0x026, 0x027, 0x027, 0x028, 0x028, 0x029, 0x029,
0x029, 0x02a, 0x02a, 0x02b, 0x02b, 0x02c, 0x02c, 0x02d,
0x02d, 0x02e, 0x02e, 0x02f, 0x02f, 0x030, 0x030, 0x031,
0x031, 0x032, 0x032, 0x033, 0x033, 0x034, 0x034, 0x035,
0x035, 0x036, 0x036, 0x037, 0x037, 0x038, 0x038, 0x038,
0x039, 0x039, 0x03a, 0x03a, 0x03b, 0x03b, 0x03c, 0x03c,
0x03d, 0x03d, 0x03e, 0x03e, 0x03f, 0x03f, 0x040, 0x040,
0x041, 0x041, 0x042, 0x042, 0x043, 0x043, 0x044, 0x044,
0x045, 0x045, 0x046, 0x046, 0x047, 0x047, 0x048, 0x048,
0x049, 0x049, 0x04a, 0x04a, 0x04b, 0x04b, 0x04c, 0x04c,
0x04d, 0x04d, 0x04e, 0x04e, 0x04f, 0x04f, 0x050, 0x050,
0x051, 0x051, 0x052, 0x052, 0x053, 0x053, 0x054, 0x054,
0x055, 0x055, 0x056, 0x056, 0x057, 0x057, 0x058, 0x058,
0x059, 0x059, 0x05a, 0x05a, 0x05b, 0x05b, 0x05c, 0x05c,
0x05d, 0x05d, 0x05e, 0x05e, 0x05f, 0x05f, 0x060, 0x060,
0x061, 0x061, 0x062, 0x062, 0x063, 0x063, 0x064, 0x064,
0x065, 0x065, 0x066, 0x066, 0x067, 0x067, 0x068, 0x068,
0x069, 0x069, 0x06a, 0x06a, 0x06b, 0x06b, 0x06c, 0x06c,
0x06d, 0x06d, 0x06e, 0x06e, 0x06f, 0x06f, 0x070, 0x071,
0x071, 0x072, 0x072, 0x073, 0x073, 0x074, 0x074, 0x075,
0x075, 0x076, 0x076, 0x077, 0x077, 0x078, 0x078, 0x079,
0x079, 0x07a, 0x07a, 0x07b, 0x07b, 0x07c, 0x07c, 0x07d,
0x07d, 0x07e, 0x07e, 0x07f, 0x07f, 0x080, 0x081, 0x081,
0x082, 0x082, 0x083, 0x083, 0x084, 0x084, 0x085, 0x085,
0x086, 0x086, 0x087, 0x087, 0x088, 0x088, 0x089, 0x089,
0x08a, 0x08a, 0x08b, 0x08b, 0x08c, 0x08d, 0x08d, 0x08e,
0x08e, 0x08f, 0x08f, 0x090, 0x090, 0x091, 0x091, 0x092,
0x092, 0x093, 0x093, 0x094, 0x094, 0x095, 0x096, 0x096,
0x097, 0x097, 0x098, 0x098, 0x099, 0x099, 0x09a, 0x09a,
0x09b, 0x09b, 0x09c, 0x09c, 0x09d, 0x09d, 0x09e, 0x09f,
0x09f, 0x0a0, 0x0a0, 0x0a1, 0x0a1, 0x0a2, 0x0a2, 0x0a3,
0x0a3, 0x0a4, 0x0a4, 0x0a5, 0x0a6, 0x0a6, 0x0a7, 0x0a7,
0x0a8, 0x0a8, 0x0a9, 0x0a9, 0x0aa, 0x0aa, 0x0ab, 0x0ab,
0x0ac, 0x0ad, 0x0ad, 0x0ae, 0x0ae, 0x0af, 0x0af, 0x0b0,
0x0b0, 0x0b1, 0x0b1, 0x0b2, 0x0b2, 0x0b3, 0x0b4, 0x0b4,
0x0b5, 0x0b5, 0x0b6, 0x0b6, 0x0b7, 0x0b7, 0x0b8, 0x0b8,
0x0b9, 0x0ba, 0x0ba, 0x0bb, 0x0bb, 0x0bc, 0x0bc, 0x0bd,
0x0bd, 0x0be, 0x0be, 0x0bf, 0x0c0, 0x0c0, 0x0c1, 0x0c1,
0x0c2, 0x0c2, 0x0c3, 0x0c3, 0x0c4, 0x0c4, 0x0c5, 0x0c6,
0x0c6, 0x0c7, 0x0c7, 0x0c8, 0x0c8, 0x0c9, 0x0c9, 0x0ca,
0x0cb, 0x0cb, 0x0cc, 0x0cc, 0x0cd, 0x0cd, 0x0ce, 0x0ce,
0x0cf, 0x0d0, 0x0d0, 0x0d1, 0x0d1, 0x0d2, 0x0d2, 0x0d3,
0x0d3, 0x0d4, 0x0d5, 0x0d5, 0x0d6, 0x0d6, 0x0d7, 0x0d7,
0x0d8, 0x0d8, 0x0d9, 0x0da, 0x0da, 0x0db, 0x0db, 0x0dc,
0x0dc, 0x0dd, 0x0de, 0x0de, 0x0df, 0x0df, 0x0e0, 0x0e0,
0x0e1, 0x0e1, 0x0e2, 0x0e3, 0x0e3, 0x0e4, 0x0e4, 0x0e5,
0x0e5, 0x0e6, 0x0e7, 0x0e7, 0x0e8, 0x0e8, 0x0e9, 0x0e9,
0x0ea, 0x0eb, 0x0eb, 0x0ec, 0x0ec, 0x0ed, 0x0ed, 0x0ee,
0x0ef, 0x0ef, 0x0f0, 0x0f0, 0x0f1, 0x0f1, 0x0f2, 0x0f3,
0x0f3, 0x0f4, 0x0f4, 0x0f5, 0x0f5, 0x0f6, 0x0f7, 0x0f7,
0x0f8, 0x0f8, 0x0f9, 0x0f9, 0x0fa, 0x0fb, 0x0fb, 0x0fc,
0x0fc, 0x0fd, 0x0fd, 0x0fe, 0x0ff, 0x0ff, 0x100, 0x100,
0x101, 0x101, 0x102, 0x103, 0x103, 0x104, 0x104, 0x105,
0x106, 0x106, 0x107, 0x107, 0x108, 0x108, 0x109, 0x10a,
0x10a, 0x10b, 0x10b, 0x10c, 0x10c, 0x10d, 0x10e, 0x10e,
0x10f, 0x10f, 0x110, 0x111, 0x111, 0x112, 0x112, 0x113,
0x114, 0x114, 0x115, 0x115, 0x116, 0x116, 0x117, 0x118,
0x118, 0x119, 0x119, 0x11a, 0x11b, 0x11b, 0x11c, 0x11c,
0x11d, 0x11e, 0x11e, 0x11f, 0x11f, 0x120, 0x120, 0x121,
0x122, 0x122, 0x123, 0x123, 0x124, 0x125, 0x125, 0x126,
0x126, 0x127, 0x128, 0x128, 0x129, 0x129, 0x12a, 0x12b,
0x12b, 0x12c, 0x12c, 0x12d, 0x12e, 0x12e, 0x12f, 0x12f,
0x130, 0x131, 0x131, 0x132, 0x132, 0x133, 0x134, 0x134,
0x135, 0x135, 0x136, 0x137, 0x137, 0x138, 0x138, 0x139,
0x13a, 0x13a, 0x13b, 0x13b, 0x13c, 0x13d, 0x13d, 0x13e,
0x13e, 0x13f, 0x140, 0x140, 0x141, 0x141, 0x142, 0x143,
0x143, 0x144, 0x144, 0x145, 0x146, 0x146, 0x147, 0x148,
0x148, 0x149, 0x149, 0x14a, 0x14b, 0x14b, 0x14c, 0x14c,
0x14d, 0x14e, 0x14e, 0x14f, 0x14f, 0x150, 0x151, 0x151,
0x152, 0x153, 0x153, 0x154, 0x154, 0x155, 0x156, 0x156,
0x157, 0x157, 0x158, 0x159, 0x159, 0x15a, 0x15b, 0x15b,
0x15c, 0x15c, 0x15d, 0x15e, 0x15e, 0x15f, 0x160, 0x160,
0x161, 0x161, 0x162, 0x163, 0x163, 0x164, 0x165, 0x165,
0x166, 0x166, 0x167, 0x168, 0x168, 0x169, 0x16a, 0x16a,
0x16b, 0x16b, 0x16c, 0x16d, 0x16d, 0x16e, 0x16f, 0x16f,
0x170, 0x170, 0x171, 0x172, 0x172, 0x173, 0x174, 0x174,
0x175, 0x175, 0x176, 0x177, 0x177, 0x178, 0x179, 0x179,
0x17a, 0x17a, 0x17b, 0x17c, 0x17c, 0x17d, 0x17e, 0x17e,
0x17f, 0x180, 0x180, 0x181, 0x181, 0x182, 0x183, 0x183,
0x184, 0x185, 0x185, 0x186, 0x187, 0x187, 0x188, 0x188,
0x189, 0x18a, 0x18a, 0x18b, 0x18c, 0x18c, 0x18d, 0x18e,
0x18e, 0x18f, 0x190, 0x190, 0x191, 0x191, 0x192, 0x193,
0x193, 0x194, 0x195, 0x195, 0x196, 0x197, 0x197, 0x198,
0x199, 0x199, 0x19a, 0x19a, 0x19b, 0x19c, 0x19c, 0x19d,
0x19e, 0x19e, 0x19f, 0x1a0, 0x1a0, 0x1a1, 0x1a2, 0x1a2,
0x1a3, 0x1a4, 0x1a4, 0x1a5, 0x1a6, 0x1a6, 0x1a7, 0x1a8,
0x1a8, 0x1a9, 0x1a9, 0x1aa, 0x1ab, 0x1ab, 0x1ac, 0x1ad,
0x1ad, 0x1ae, 0x1af, 0x1af, 0x1b0, 0x1b1, 0x1b1, 0x1b2,
0x1b3, 0x1b3, 0x1b4, 0x1b5, 0x1b5, 0x1b6, 0x1b7, 0x1b7,
0x1b8, 0x1b9, 0x1b9, 0x1ba, 0x1bb, 0x1bb, 0x1bc, 0x1bd,
0x1bd, 0x1be, 0x1bf, 0x1bf, 0x1c0, 0x1c1, 0x1c1, 0x1c2,
0x1c3, 0x1c3, 0x1c4, 0x1c5, 0x1c5, 0x1c6, 0x1c7, 0x1c7,
0x1c8, 0x1c9, 0x1c9, 0x1ca, 0x1cb, 0x1cb, 0x1cc, 0x1cd,
0x1cd, 0x1ce, 0x1cf, 0x1cf, 0x1d0, 0x1d1, 0x1d1, 0x1d2,
0x1d3, 0x1d3, 0x1d4, 0x1d5, 0x1d5, 0x1d6, 0x1d7, 0x1d7,
0x1d8, 0x1d9, 0x1d9, 0x1da, 0x1db, 0x1db, 0x1dc, 0x1dd,
0x1dd, 0x1de, 0x1df, 0x1df, 0x1e0, 0x1e1, 0x1e1, 0x1e2,
0x1e3, 0x1e4, 0x1e4, 0x1e5, 0x1e6, 0x1e6, 0x1e7, 0x1e8,
0x1e8, 0x1e9, 0x1ea, 0x1ea, 0x1eb, 0x1ec, 0x1ec, 0x1ed,
0x1ee, 0x1ee, 0x1ef, 0x1f0, 0x1f0, 0x1f1, 0x1f2, 0x1f3,
0x1f3, 0x1f4, 0x1f5, 0x1f5, 0x1f6, 0x1f7, 0x1f7, 0x1f8,
0x1f9, 0x1f9, 0x1fa, 0x1fb, 0x1fb, 0x1fc, 0x1fd, 0x1fe,
0x1fe, 0x1ff, 0x200, 0x200, 0x201, 0x202, 0x202, 0x203,
0x204, 0x205, 0x205, 0x206, 0x207, 0x207, 0x208, 0x209,
0x209, 0x20a, 0x20b, 0x20b, 0x20c, 0x20d, 0x20e, 0x20e,
0x20f, 0x210, 0x210, 0x211, 0x212, 0x212, 0x213, 0x214,
0x215, 0x215, 0x216, 0x217, 0x217, 0x218, 0x219, 0x21a,
0x21a, 0x21b, 0x21c, 0x21c, 0x21d, 0x21e, 0x21e, 0x21f,
0x220, 0x221, 0x221, 0x222, 0x223, 0x223, 0x224, 0x225,
0x226, 0x226, 0x227, 0x228, 0x228, 0x229, 0x22a, 0x22b,
0x22b, 0x22c, 0x22d, 0x22d, 0x22e, 0x22f, 0x230, 0x230,
0x231, 0x232, 0x232, 0x233, 0x234, 0x235, 0x235, 0x236,
0x237, 0x237, 0x238, 0x239, 0x23a, 0x23a, 0x23b, 0x23c,
0x23c, 0x23d, 0x23e, 0x23f, 0x23f, 0x240, 0x241, 0x241,
0x242, 0x243, 0x244, 0x244, 0x245, 0x246, 0x247, 0x247,
0x248, 0x249, 0x249, 0x24a, 0x24b, 0x24c, 0x24c, 0x24d,
0x24e, 0x24f, 0x24f, 0x250, 0x251, 0x251, 0x252, 0x253,
0x254, 0x254, 0x255, 0x256, 0x257, 0x257, 0x258, 0x259,
0x259, 0x25a, 0x25b, 0x25c, 0x25c, 0x25d, 0x25e, 0x25f,
0x25f, 0x260, 0x261, 0x262, 0x262, 0x263, 0x264, 0x265,
0x265, 0x266, 0x267, 0x267, 0x268, 0x269, 0x26a, 0x26a,
0x26b, 0x26c, 0x26d, 0x26d, 0x26e, 0x26f, 0x270, 0x270,
0x271, 0x272, 0x273, 0x273, 0x274, 0x275, 0x276, 0x276,
0x277, 0x278, 0x279, 0x279, 0x27a, 0x27b, 0x27c, 0x27c,
0x27d, 0x27e, 0x27f, 0x27f, 0x280, 0x281, 0x282, 0x282,
0x283, 0x284, 0x285, 0x285, 0x286, 0x287, 0x288, 0x288,
0x289, 0x28a, 0x28b, 0x28b, 0x28c, 0x28d, 0x28e, 0x28e,
0x28f, 0x290, 0x291, 0x291, 0x292, 0x293, 0x294, 0x294,
0x295, 0x296, 0x297, 0x298, 0x298, 0x299, 0x29a, 0x29b,
0x29b, 0x29c, 0x29d, 0x29e, 0x29e, 0x29f, 0x2a0, 0x2a1,
0x2a1, 0x2a2, 0x2a3, 0x2a4, 0x2a5, 0x2a5, 0x2a6, 0x2a7,
0x2a8, 0x2a8, 0x2a9, 0x2aa, 0x2ab, 0x2ab, 0x2ac, 0x2ad,
0x2ae, 0x2af, 0x2af, 0x2b0, 0x2b1, 0x2b2, 0x2b2, 0x2b3,
0x2b4, 0x2b5, 0x2b5, 0x2b6, 0x2b7, 0x2b8, 0x2b9, 0x2b9,
0x2ba, 0x2bb, 0x2bc, 0x2bc, 0x2bd, 0x2be, 0x2bf, 0x2c0,
0x2c0, 0x2c1, 0x2c2, 0x2c3, 0x2c4, 0x2c4, 0x2c5, 0x2c6,
0x2c7, 0x2c7, 0x2c8, 0x2c9, 0x2ca, 0x2cb, 0x2cb, 0x2cc,
0x2cd, 0x2ce, 0x2ce, 0x2cf, 0x2d0, 0x2d1, 0x2d2, 0x2d2,
0x2d3, 0x2d4, 0x2d5, 0x2d6, 0x2d6, 0x2d7, 0x2d8, 0x2d9,
0x2da, 0x2da, 0x2db, 0x2dc, 0x2dd, 0x2dd, 0x2de, 0x2df,
0x2e0, 0x2e1, 0x2e1, 0x2e2, 0x2e3, 0x2e4, 0x2e5, 0x2e5,
0x2e6, 0x2e7, 0x2e8, 0x2e9, 0x2e9, 0x2ea, 0x2eb, 0x2ec,
0x2ed, 0x2ed, 0x2ee, 0x2ef, 0x2f0, 0x2f1, 0x2f1, 0x2f2,
0x2f3, 0x2f4, 0x2f5, 0x2f5, 0x2f6, 0x2f7, 0x2f8, 0x2f9,
0x2f9, 0x2fa, 0x2fb, 0x2fc, 0x2fd, 0x2fd, 0x2fe, 0x2ff,
0x300, 0x301, 0x302, 0x302, 0x303, 0x304, 0x305, 0x306,
0x306, 0x307, 0x308, 0x309, 0x30a, 0x30a, 0x30b, 0x30c,
0x30d, 0x30e, 0x30f, 0x30f, 0x310, 0x311, 0x312, 0x313,
0x313, 0x314, 0x315, 0x316, 0x317, 0x318, 0x318, 0x319,
0x31a, 0x31b, 0x31c, 0x31c, 0x31d, 0x31e, 0x31f, 0x320,
0x321, 0x321, 0x322, 0x323, 0x324, 0x325, 0x326, 0x326,
0x327, 0x328, 0x329, 0x32a, 0x32a, 0x32b, 0x32c, 0x32d,
0x32e, 0x32f, 0x32f, 0x330, 0x331, 0x332, 0x333, 0x334,
0x334, 0x335, 0x336, 0x337, 0x338, 0x339, 0x339, 0x33a,
0x33b, 0x33c, 0x33d, 0x33e, 0x33e, 0x33f, 0x340, 0x341,
0x342, 0x343, 0x343, 0x344, 0x345, 0x346, 0x347, 0x348,
0x349, 0x349, 0x34a, 0x34b, 0x34c, 0x34d, 0x34e, 0x34e,
0x34f, 0x350, 0x351, 0x352, 0x353, 0x353, 0x354, 0x355,
0x356, 0x357, 0x358, 0x359, 0x359, 0x35a, 0x35b, 0x35c,
0x35d, 0x35e, 0x35f, 0x35f, 0x360, 0x361, 0x362, 0x363,
0x364, 0x364, 0x365, 0x366, 0x367, 0x368, 0x369, 0x36a,
0x36a, 0x36b, 0x36c, 0x36d, 0x36e, 0x36f, 0x370, 0x370,
0x371, 0x372, 0x373, 0x374, 0x375, 0x376, 0x377, 0x377,
0x378, 0x379, 0x37a, 0x37b, 0x37c, 0x37d, 0x37d, 0x37e,
0x37f, 0x380, 0x381, 0x382, 0x383, 0x383, 0x384, 0x385,
0x386, 0x387, 0x388, 0x389, 0x38a, 0x38a, 0x38b, 0x38c,
0x38d, 0x38e, 0x38f, 0x390, 0x391, 0x391, 0x392, 0x393,
0x394, 0x395, 0x396, 0x397, 0x398, 0x398, 0x399, 0x39a,
0x39b, 0x39c, 0x39d, 0x39e, 0x39f, 0x39f, 0x3a0, 0x3a1,
0x3a2, 0x3a3, 0x3a4, 0x3a5, 0x3a6, 0x3a7, 0x3a7, 0x3a8,
0x3a9, 0x3aa, 0x3ab, 0x3ac, 0x3ad, 0x3ae, 0x3ae, 0x3af,
0x3b0, 0x3b1, 0x3b2, 0x3b3, 0x3b4, 0x3b5, 0x3b6, 0x3b6,
0x3b7, 0x3b8, 0x3b9, 0x3ba, 0x3bb, 0x3bc, 0x3bd, 0x3be,
0x3bf, 0x3bf, 0x3c0, 0x3c1, 0x3c2, 0x3c3, 0x3c4, 0x3c5,
0x3c6, 0x3c7, 0x3c7, 0x3c8, 0x3c9, 0x3ca, 0x3cb, 0x3cc,
0x3cd, 0x3ce, 0x3cf, 0x3d0, 0x3d1, 0x3d1, 0x3d2, 0x3d3,
0x3d4, 0x3d5, 0x3d6, 0x3d7, 0x3d8, 0x3d9, 0x3da, 0x3da,
0x3db, 0x3dc, 0x3dd, 0x3de, 0x3df, 0x3e0, 0x3e1, 0x3e2,
0x3e3, 0x3e4, 0x3e4, 0x3e5, 0x3e6, 0x3e7, 0x3e8, 0x3e9,
0x3ea, 0x3eb, 0x3ec, 0x3ed, 0x3ee, 0x3ef, 0x3ef, 0x3f0,
0x3f1, 0x3f2, 0x3f3, 0x3f4, 0x3f5, 0x3f6, 0x3f7, 0x3f8,
0x3f9, 0x3fa, 0x3fa, 0x3fb, 0x3fc, 0x3fd, 0x3fe, 0x3ff
};
/*
* Attenuation according to GM recommendations, in -0.375 dB units.
* table[v] = 40 * log(v / 127) / -0.375
*/
static const uint8_t g_volume_table[128] = {
255, 224, 192, 173, 160, 150, 141, 134,
128, 122, 117, 113, 109, 105, 102, 99,
96, 93, 90, 88, 85, 83, 81, 79,
77, 75, 73, 71, 70, 68, 67, 65,
64, 62, 61, 59, 58, 57, 56, 54,
53, 52, 51, 50, 49, 48, 47, 46,
45, 44, 43, 42, 41, 40, 39, 39,
38, 37, 36, 35, 34, 34, 33, 32,
31, 31, 30, 29, 29, 28, 27, 27,
26, 25, 25, 24, 24, 23, 22, 22,
21, 21, 20, 19, 19, 18, 18, 17,
17, 16, 16, 15, 15, 14, 14, 13,
13, 12, 12, 11, 11, 10, 10, 9,
9, 9, 8, 8, 7, 7, 6, 6,
6, 5, 5, 4, 4, 4, 3, 3,
2, 2, 2, 1, 1, 0, 0, 0
};
#define BUFFER_SEGMENTS 10
#define RENDER_RATE (48000 / 100)
typedef struct opl4_midi {
fm_drv_t opl4;
MIDI_CHANNEL_DATA midi_channel_data[16];
VOICE_DATA voice_data[24];
int16_t buffer[(48000 / 100) * 2 * BUFFER_SEGMENTS];
float buffer_float[(48000 / 100) * 2 * BUFFER_SEGMENTS];
uint32_t midi_pos;
bool on;
atomic_bool gen_in_progress;
thread_t *thread;
event_t *wait_event;
} opl4_midi_t;
static opl4_midi_t *opl4_midi_cur;
static void
opl4_write_wave_register(const uint8_t reg, const uint8_t value, opl4_midi_t *opl4_midi)
{
opl4_midi->opl4.write(0x380, reg, opl4_midi->opl4.priv);
opl4_midi->opl4.write(0x381, value, opl4_midi->opl4.priv);
}
VOICE_DATA *
get_voice(const YRW801_WAVE_DATA *wave_data, opl4_midi_t *opl4_midi)
{
VOICE_DATA *free_voice = &opl4_midi->voice_data[0];
VOICE_DATA *oldest_voice = &opl4_midi->voice_data[0];
for (uint8_t voice = 0; voice < 24; voice++) {
if (opl4_midi_cur->voice_data[voice].is_active) {
if (opl4_midi_cur->voice_data[voice].activated < oldest_voice->activated)
oldest_voice = &opl4_midi_cur->voice_data[voice];
} else {
if (opl4_midi_cur->voice_data[voice].wave_data == wave_data) {
free_voice = &opl4_midi_cur->voice_data[voice];
break;
}
if (opl4_midi_cur->voice_data[voice].activated < free_voice->activated) {
free_voice = &opl4_midi_cur->voice_data[voice];
break;
}
}
}
/* If no free voice is found, look for one with the same instrument */
if (free_voice->is_active) {
for (uint8_t voice = 0; voice < 24; voice++) {
if (opl4_midi_cur->voice_data[voice].is_active
&& opl4_midi_cur->voice_data[voice].wave_data == wave_data) {
free_voice = &opl4_midi_cur->voice_data[voice];
free_voice->is_active = 0;
free_voice->activated = 0;
free_voice->reg_misc &= ~OPL4_KEY_ON_BIT;
opl4_write_wave_register(OPL4_REG_MISC + free_voice->number, free_voice->reg_misc, opl4_midi);
return free_voice;
}
}
}
/* If still no free voice found, deactivate the 'oldest' */
if (free_voice->is_active) {
free_voice = oldest_voice;
free_voice->is_active = 0;
free_voice->activated = 0;
free_voice->reg_misc &= ~OPL4_KEY_ON_BIT;
opl4_write_wave_register(OPL4_REG_MISC + free_voice->number, free_voice->reg_misc, opl4_midi);
}
return free_voice;
}
static void
update_pan(VOICE_DATA *voice, opl4_midi_t *opl4_midi)
{
int8_t pan = voice->wave_data->panpot;
if (!voice->midi_channel->drum_channel)
pan += voice->midi_channel->panpot;
if (pan < -7)
pan = -7;
else if (pan > 7)
pan = 7;
voice->reg_misc = (voice->reg_misc & ~OPL4_PAN_POT_MASK) | (pan & OPL4_PAN_POT_MASK);
opl4_write_wave_register(OPL4_REG_MISC + voice->number, voice->reg_misc, opl4_midi);
}
void
update_pitch(VOICE_DATA *voice, uint16_t pitch_bend, opl4_midi_t *opl4_midi)
{
int32_t pitch = voice->midi_channel->drum_channel ? 0 : (voice->note - 60) * 128;
pitch = pitch * (int) voice->wave_data->key_scaling / 100;
pitch = pitch + 7680;
pitch += voice->wave_data->pitch_offset;
pitch += pitch_bend * 256 / 0x2000;
if (pitch < 0)
pitch = 0;
else if (pitch > 0x5FFF)
pitch = 0x5FFF;
int8_t octave = pitch / 0x600 - 8;
uint16_t fnumber = g_wave_pitch_map[pitch % 0x600];
opl4_write_wave_register(OPL4_REG_OCTAVE + voice->number, (octave << 4) | ((fnumber >> 7) & 0x07), opl4_midi);
voice->reg_f_number = (voice->reg_f_number & OPL4_TONE_NUMBER_BIT8) | ((fnumber << 1) & OPL4_F_NUMBER_LOW_MASK);
opl4_write_wave_register(OPL4_REG_F_NUMBER + voice->number, voice->reg_f_number, opl4_midi);
}
void
update_volume(VOICE_DATA *voice, opl4_midi_t *opl4_midi)
{
int16_t att = voice->wave_data->tone_attenuate;
att += g_volume_table[voice->velocity];
att = 0x7F - (0x7F - att) * (voice->wave_data->volume_factor) / 0xFE;
att -= 16;
if (att < 0)
att = 0;
else if (att > 0x7E)
att = 0x7E;
opl4_write_wave_register(OPL4_REG_LEVEL + voice->number, (att << 1) | voice->level_direct, opl4_midi);
voice->level_direct = 0;
}
void
note_off(uint8_t note, uint8_t velocity, MIDI_CHANNEL_DATA *midi_channel, opl4_midi_t *opl4_midi)
{
/* Velocity not used */
(void) velocity;
while (opl4_midi->gen_in_progress) { }
for (uint8_t i = 0; i < 24; i++) {
VOICE_DATA *voice = &opl4_midi->voice_data[i];
if (voice->is_active && voice->midi_channel == midi_channel && voice->note == note) {
voice->is_active = false;
voice->activated = 0;
voice->reg_misc &= ~OPL4_KEY_ON_BIT;
opl4_write_wave_register(OPL4_REG_MISC + voice->number, voice->reg_misc, opl4_midi);
}
}
}
void
update_vibrato_depth(VOICE_DATA *voice, opl4_midi_t *opl4_midi)
{
uint16_t depth;
depth = (7 - voice->wave_data->vibrato) * (voice->midi_channel->vibrato & 0x7F);
depth = (depth >> 7) + voice->wave_data->vibrato;
voice->reg_lfo_vibrato &= ~OPL4_VIBRATO_DEPTH_MASK;
voice->reg_lfo_vibrato |= depth & OPL4_VIBRATO_DEPTH_MASK;
opl4_write_wave_register(OPL4_REG_LFO_VIBRATO + voice->number, voice->reg_lfo_vibrato, opl4_midi);
}
void
note_on(uint8_t note, uint8_t velocity, MIDI_CHANNEL_DATA *midi_channel, opl4_midi_t *opl4_midi)
{
const YRW801_REGION_DATA_PTR *region_ptr = &snd_yrw801_regions[0];
const YRW801_WAVE_DATA *wave_data[2];
VOICE_DATA *voice[2];
uint8_t i = 0, voices = 0;
uint32_t j = 0;
while (opl4_midi->gen_in_progress) { }
if (midi_channel->drum_channel)
wave_data[voices++] = &region_ptr[0x80].regions[note - 0x1A].wave_data;
else {
/* Determine the number of voices and voice parameters */
const YRW801_REGION_DATA *region = region_ptr[midi_channel->instrument & 0x7F].regions;
while (i < region_ptr[midi_channel->instrument & 0x7F].count) {
if (note >= region[i].key_min && note <= region[i].key_max) {
wave_data[voices] = &region[i].wave_data;
if (++voices >= 2)
break;
}
i++;
}
}
/* Allocate and initialize needed voices */
for (i = 0; i < voices; i++) {
voice[i] = get_voice(wave_data[i], opl4_midi);
voice[i]->is_active = true;
voice[i]->activated = plat_get_ticks();
voice[i]->midi_channel = midi_channel;
voice[i]->note = note;
voice[i]->velocity = velocity & 0x7F;
}
for (i = 0; i < voices; i++) {
voice[i]->reg_f_number = (wave_data[i]->tone >> 8) & OPL4_TONE_NUMBER_BIT8;
opl4_write_wave_register(OPL4_REG_F_NUMBER + voice[i]->number, voice[i]->reg_f_number, opl4_midi);
bool new_wave = (voice[i]->wave_data != wave_data[i]);
/* Set tone number (triggers header loading) */
if (new_wave) {
opl4_write_wave_register(OPL4_REG_TONE_NUMBER + voice[i]->number, wave_data[i]->tone & 0xFF, opl4_midi);
voice[i]->wave_data = wave_data[i];
}
voice[i]->reg_misc = OPL4_LFO_RESET_BIT;
update_pan(voice[i], opl4_midi);
update_pitch(voice[i], 0, opl4_midi);
voice[i]->level_direct = OPL4_LEVEL_DIRECT_BIT;
update_volume(voice[i], opl4_midi);
if (new_wave) {
/* Set remaining parameters */
opl4_write_wave_register(OPL4_REG_ATTACK_DECAY1 + voice[i]->number, voice[i]->wave_data->reg_attack_decay1, opl4_midi);
opl4_write_wave_register(OPL4_REG_LEVEL_DECAY2 + voice[i]->number, voice[i]->wave_data->reg_level_decay2, opl4_midi);
opl4_write_wave_register(OPL4_REG_RELEASE_CORRECTION + voice[i]->number, voice[i]->wave_data->reg_release_correction, opl4_midi);
opl4_write_wave_register(OPL4_REG_TREMOLO + voice[i]->number, voice[i]->wave_data->reg_tremolo, opl4_midi);
voice[i]->reg_lfo_vibrato = voice[i]->wave_data->reg_lfo_vibrato;
if (!midi_channel->drum_channel)
update_vibrato_depth(voice[i], opl4_midi);
}
}
/* Finally, switch on all voices */
for (i = 0; i < voices; i++) {
voice[i]->reg_misc = (voice[i]->reg_misc & 0x1F) | OPL4_KEY_ON_BIT;
opl4_write_wave_register(OPL4_REG_MISC + voice[i]->number, voice[i]->reg_misc, opl4_midi);
}
}
void
control_change(uint8_t midi_channel, uint8_t id, uint8_t value, opl4_midi_t *opl4_midi)
{
int i = 0;
switch (id) {
case 10:
/* Change stereo panning */
if (midi_channel != DRUM_CHANNEL) {
opl4_midi->midi_channel_data[midi_channel].panpot = (value - 0x40) >> 3;
for (i = 0; i < NR_OF_WAVE_CHANNELS; i++) {
if (opl4_midi->voice_data[i].is_active && opl4_midi->voice_data[i].midi_channel == &opl4_midi->midi_channel_data[midi_channel]) {
update_pan(&opl4_midi->voice_data[i], opl4_midi);
}
}
}
break;
}
}
void
pitch_wheel(uint8_t midi_channel, uint16_t value, opl4_midi_t *opl4_midi)
{
int i = 0;
for (i = 0; i < 24; i++) {
if (opl4_midi->voice_data[i].is_active && opl4_midi->voice_data[i].midi_channel == &opl4_midi->midi_channel_data[midi_channel]) {
update_pitch(&opl4_midi->voice_data[i], value, opl4_midi);
}
}
}
void
channel_pressure(uint8_t midi_channel, uint8_t pressure)
{
(void) midi_channel;
(void) pressure;
}
void
key_pressure(uint8_t midi_channel, uint8_t note, uint8_t pressure)
{
(void) midi_channel;
(void) note;
(void) pressure;
}
void
program_change(uint8_t midi_channel, uint8_t program, opl4_midi_t *opl4_midi)
{
opl4_midi->midi_channel_data[midi_channel].instrument = program;
}
static void
opl4_midi_thread(void *arg)
{
opl4_midi_t *opl4_midi = opl4_midi_cur;
uint32_t i = 0;
uint32_t buf_size = RENDER_RATE * 2;
uint32_t buf_size_segments = buf_size * BUFFER_SEGMENTS;
uint32_t buf_pos = 0;
int32_t buffer[RENDER_RATE * 2];
extern void givealbuffer_midi(void *buf, uint32_t size);
while (opl4_midi->on) {
thread_wait_event(opl4_midi->wait_event, -1);
thread_reset_event(opl4_midi->wait_event);
if (!opl4_midi->on)
break;
atomic_store(&opl4_midi->gen_in_progress, true);
opl4_midi->opl4.generate(opl4_midi->opl4.priv, buffer, RENDER_RATE);
atomic_store(&opl4_midi->gen_in_progress, false);
if (sound_is_float) {
for (i = 0; i < (buf_size / 2); i++) {
opl4_midi->buffer_float[(i + buf_pos) * 2] = buffer[i * 2] / 32768.0;
opl4_midi->buffer_float[((i + buf_pos) * 2) + 1] = buffer[(i * 2) + 1] / 32768.0;
}
buf_pos += buf_size / 2;
if (buf_pos >= (buf_size_segments / 2)) {
givealbuffer_midi(opl4_midi->buffer_float, buf_size_segments);
buf_pos = 0;
}
} else {
for (i = 0; i < (buf_size / 2); i++) {
opl4_midi->buffer[(i + buf_pos) * 2] = buffer[i * 2] & 0xFFFF; /* Outputs are clamped beforehand. */
opl4_midi->buffer[((i + buf_pos) * 2) + 1] = buffer[(i * 2) + 1] & 0xFFFF; /* Outputs are clamped beforehand. */
}
buf_pos += buf_size / 2;
if (buf_pos >= (buf_size_segments / 2)) {
givealbuffer_midi(opl4_midi->buffer, buf_size_segments);
buf_pos = 0;
}
}
}
}
static void
opl4_midi_poll(void)
{
opl4_midi_t *opl4_midi = opl4_midi_cur;
opl4_midi->midi_pos++;
if (opl4_midi->midi_pos == RENDER_RATE) {
opl4_midi->midi_pos = 0;
thread_set_event(opl4_midi->wait_event);
}
}
void
opl4_midi_msg(uint8_t *val)
{
opl4_midi_t *opl4_midi = opl4_midi_cur;
uint32_t msg = *(uint32_t *) (val);
uint8_t data_byte_1 = msg & 0xFF;
uint8_t data_byte_2 = (msg >> 8) & 0xFF;
uint8_t data_byte_3 = (msg >> 16) & 0xFF;
uint8_t midi_channel = data_byte_1 & 0x0F;
uint8_t midi_command = data_byte_1 >> 4;
switch (midi_command) {
case 0x8: // Note OFF
note_off(data_byte_2 & 0x7F, data_byte_3, &opl4_midi->midi_channel_data[midi_channel], opl4_midi);
break;
case 0x9: // Note ON
note_on(data_byte_2 & 0x7F, data_byte_3, &opl4_midi->midi_channel_data[midi_channel], opl4_midi);
break;
case 0xA: // Key after-touch
break;
case 0xB: // Control change
control_change(midi_channel, data_byte_2, data_byte_3, opl4_midi);
break;
case 0xC: // Program change
program_change(midi_channel, data_byte_2 & 0x7F, opl4_midi);
break;
case 0xD: // Channel after-touch
break;
case 0xE: // Pitch wheel
pitch_wheel(midi_channel, ((data_byte_3 <<= 7) | data_byte_2), opl4_midi);
break;
}
}
void
opl4_midi_sysex(uint8_t *data, unsigned int len)
{
}
void *
opl4_init(const device_t *info)
{
midi_device_t *dev;
extern void al_set_midi(int freq, int buf_size);
dev = malloc(sizeof(midi_device_t));
memset(dev, 0, sizeof(midi_device_t));
dev->play_msg = opl4_midi_msg;
dev->play_sysex = opl4_midi_sysex;
dev->poll = opl4_midi_poll;
al_set_midi(48000, 4800);
opl4_midi_cur = calloc(1, sizeof(opl4_midi_t));
fm_driver_get(FM_YMF278B, &opl4_midi_cur->opl4);
opl4_midi_cur->opl4.write(0x38A, 0x05, opl4_midi_cur->opl4.priv);
opl4_midi_cur->opl4.write(0x389, 0x3, opl4_midi_cur->opl4.priv);
midi_out_init(dev);
opl4_midi_cur->on = true;
opl4_midi_cur->midi_channel_data[9].drum_channel = true;
atomic_init(&opl4_midi_cur->gen_in_progress, 0);
for (uint8_t voice = 0; voice < NR_OF_WAVE_CHANNELS; voice++) {
opl4_midi_cur->voice_data[voice].number = voice;
opl4_midi_cur->voice_data[voice].is_active = false;
opl4_midi_cur->voice_data[voice].activated = 0;
opl4_midi_cur->voice_data[voice].midi_channel = NULL;
opl4_midi_cur->voice_data[voice].note = 0;
opl4_midi_cur->voice_data[voice].velocity = 0;
opl4_midi_cur->voice_data[voice].wave_data = NULL;
opl4_midi_cur->voice_data[voice].level_direct = 0;
opl4_midi_cur->voice_data[voice].reg_f_number = 0;
opl4_midi_cur->voice_data[voice].reg_misc = 0;
opl4_midi_cur->voice_data[voice].reg_lfo_vibrato = 0;
}
opl4_midi_cur->wait_event = thread_create_event();
opl4_midi_cur->thread = thread_create(opl4_midi_thread, NULL);
return dev;
}
void
opl4_close(void *p)
{
if (!p)
return;
opl4_midi_cur->on = false;
thread_set_event(opl4_midi_cur->wait_event);
thread_wait(opl4_midi_cur->thread);
free(opl4_midi_cur);
opl4_midi_cur = NULL;
}
static int
opl4_midi_available(void)
{
return rom_present("roms/sound/yamaha/yrw801.rom");
}
const device_t opl4_midi_device = {
.name = "OPL4-ML Daughterboard",
.internal_name = "opl4-ml",
.flags = 0,
.local = 0,
.init = opl4_init,
.close = opl4_close,
.reset = NULL,
{ .available = opl4_midi_available },
.speed_changed = NULL,
.force_redraw = NULL,
.config = NULL
};

1042
src/sound/midi_opl4_yrw801.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1657,4 +1657,5 @@ const fm_drv_t nuked_opl_drv = {
&nuked_drv_reset_buffer,
&nuked_drv_set_do_cycles,
NULL,
NULL,
};

View File

@@ -399,6 +399,13 @@ ymfm_drv_set_do_cycles(void *priv, int8_t do_cycles)
drv->set_do_cycles(do_cycles);
}
static void
ymfm_drv_generate(void *priv, int32_t *data, uint32_t num_samples)
{
YMFMChipBase *drv = (YMFMChipBase *) priv;
drv->generate_resampled(data, num_samples);
}
const device_t ym3812_ymfm_device = {
.name = "Yamaha YM3812 OPL2 (YMFM)",
.internal_name = "ym3812_ymfm",
@@ -462,6 +469,7 @@ const fm_drv_t ymfm_drv {
&ymfm_drv_reset_buffer,
&ymfm_drv_set_do_cycles,
NULL,
ymfm_drv_generate,
};
#ifdef __clang__

45
src/sound/yrw801.h Normal file
View File

@@ -0,0 +1,45 @@
/*
* RoboPlay for MSX
* Copyright (C) 2022 by RoboSoft Inc.
*
* yrw801.h
*/
/* Cacodemon345: Added pointer structs from Linux */
#pragma once
#include <stdint.h>
typedef struct
{
uint16_t tone;
int16_t pitch_offset;
uint8_t key_scaling;
int8_t panpot;
uint8_t vibrato;
uint8_t tone_attenuate;
uint8_t volume_factor;
uint8_t reg_lfo_vibrato;
uint8_t reg_attack_decay1;
uint8_t reg_level_decay2;
uint8_t reg_release_correction;
uint8_t reg_tremolo;
} YRW801_WAVE_DATA;
typedef struct
{
uint8_t key_min;
uint8_t key_max;
YRW801_WAVE_DATA wave_data;
} YRW801_REGION_DATA;
typedef struct
{
int count;
const YRW801_REGION_DATA* regions;
} YRW801_REGION_DATA_PTR;
extern const YRW801_REGION_DATA_PTR snd_yrw801_regions[0x81];

View File

@@ -12,10 +12,10 @@
# After a successful build, you can install the RPMs as follows:
# sudo dnf install RPMS/$(uname -m)/86Box-3* RPMS/noarch/86Box-roms*
%global romver 4.0.1
%global romver 4.1
Name: 86Box
Version: 4.0.2
Version: 4.1
Release: 1%{?dist}
Summary: Classic PC emulator
License: GPLv2+
@@ -121,5 +121,5 @@ popd
%{_datadir}/%{name}/roms
%changelog
* Mon Oct 16 2023 Robert de Rooy <robert.de.rooy[AT]gmail.com> 4.0.2-1
* Mon Oct 16 2023 Robert de Rooy <robert.de.rooy[AT]gmail.com> 4.1-1
- Bump release

View File

@@ -10,7 +10,7 @@
</categories>
<launchable type="desktop-id">net.86box.86Box.desktop</launchable>
<releases>
<release version="4.0.2" date="2023-10-16"/>
<release version="4.1" date="2023-10-16"/>
</releases>
<content_rating type="oars-1.1" />
<description>

View File

@@ -633,7 +633,7 @@ DEVOBJ := bugger.o cartridge.o cassette.o hasp.o hwm.o hwm_lm75.o hwm_lm78.o hwm
SIOOBJ := sio_acc3221.o sio_ali5123.o \
sio_f82c710.o sio_82091aa.o sio_fdc37c6xx.o \
sio_fdc37c67x.o sio_fdc37c669.o sio_fdc37c93x.o sio_fdc37m60x.o \
sio_it8661f.o \
sio_it86x1f.o \
sio_pc87306.o sio_pc87307.o sio_pc87309.o sio_pc87310.o sio_pc87311.o sio_pc87332.o \
sio_prime3b.o sio_prime3c.o \
sio_w83787f.o \
@@ -686,7 +686,11 @@ NETOBJ := network.o \
net_3c503.o net_ne2000.o \
net_pcnet.o net_wd8003.o \
net_plip.o net_event.o \
net_null.o
net_null.o \
net_eeprom_nmc93cxx.o \
net_tulip.o \
net_rtl8139.o \
net_l80225.o
PRINTOBJ := png.o prt_cpmap.o \
prt_escp.o prt_text.o prt_ps.o

View File

@@ -1,6 +1,6 @@
{
"name": "86box",
"version-string": "4.0.2",
"version-string": "4.1",
"homepage": "https://86box.net/",
"documentation": "https://86box.readthedocs.io/",
"license": "GPL-2.0-or-later",