Merge branch '86Box:master' into master

This commit is contained in:
starfrost
2025-05-22 23:36:37 +01:00
committed by GitHub
70 changed files with 2119 additions and 3837 deletions

View File

@@ -36,6 +36,7 @@
#include <86box/pit.h>
#include <86box/apm.h>
#include <86box/acpi.h>
#include <86box/dma.h>
#include <86box/machine.h>
#include <86box/i2c.h>
#include <86box/video.h>
@@ -1025,8 +1026,13 @@ acpi_reg_write_common_regs(UNUSED(int size), uint16_t addr, uint8_t val, void *p
nvr_reg_write(0x000f, 0xff, dev->nvr);
}
if (sus_typ & SUS_RESET_PCI)
if (sus_typ & SUS_RESET_PCI) {
/* DMA is part of the southbridge so it responds to PCI reset. */
dma_reset();
dma_set_at(1);
device_reset_all(DEVICE_PCI);
}
if (sus_typ & SUS_RESET_CPU)
cpu_alt_reset = 0;

View File

@@ -155,6 +155,7 @@ piix_ide_handlers(piix_t *dev, int bus)
uint16_t side;
if (bus & 0x01) {
piix_log("Disabling primary IDE...\n");
ide_pri_disable();
if (dev->type == 5) {
@@ -170,11 +171,14 @@ piix_ide_handlers(piix_t *dev, int bus)
ide_set_side(0, side);
}
if ((dev->regs[1][0x04] & 0x01) && (dev->regs[1][0x41] & 0x80))
if ((dev->regs[1][0x04] & 0x01) && (dev->regs[1][0x41] & 0x80)) {
piix_log("Enabling primary IDE...\n");
ide_pri_enable();
}
}
if (bus & 0x02) {
piix_log("Disabling secondary IDE...\n");
ide_sec_disable();
if (dev->type == 5) {
@@ -190,8 +194,10 @@ piix_ide_handlers(piix_t *dev, int bus)
ide_set_side(1, side);
}
if ((dev->regs[1][0x04] & 0x01) && (dev->regs[1][0x43] & 0x80))
if ((dev->regs[1][0x04] & 0x01) && (dev->regs[1][0x43] & 0x80)) {
piix_log("Enabling secondary IDE...\n");
ide_sec_enable();
}
}
}
@@ -465,6 +471,13 @@ piix_write(int func, int addr, uint8_t val, void *priv)
uint8_t *fregs;
uint16_t base;
/* Dell OptiPlex Gn+ shows that register 02:FF is aliased in 01:FF. */
if ((dev->type == 4) && (func == 1) && (addr == 0xff))
func = 2;
if ((func == 1) || (addr == 0xf8) || (addr == 0xf9))
piix_log("[W] %02X:%02X = %02X\n", func, addr, val);
/* Return on unsupported function. */
if (dev->max_func > 0) {
if (func > dev->max_func)
@@ -736,6 +749,8 @@ piix_write(int func, int addr, uint8_t val, void *priv)
fregs[addr] = val;
break;
case 0xb0:
if (val & 0x10)
warning("Write %02X to B0\n", val);
if (dev->type == 4)
fregs[addr] = (fregs[addr] & 0x8c) | (val & 0x73);
else if (dev->type == 5)
@@ -745,6 +760,8 @@ piix_write(int func, int addr, uint8_t val, void *priv)
alt_access = !!(val & 0x20);
break;
case 0xb1:
if (val & 0x18)
warning("Write %02X to B1\n", val);
if (dev->type > 3)
fregs[addr] = val & 0xdf;
break;
@@ -923,6 +940,12 @@ piix_write(int func, int addr, uint8_t val, void *priv)
if (dev->type > 4)
fregs[addr] = val;
break;
case 0xf8:
case 0xf9:
/* Undocumented! */
if (dev->type == 4)
fregs[addr] = val;
break;
default:
break;
}
@@ -1169,6 +1192,10 @@ piix_read(int func, int addr, void *priv)
uint8_t ret = 0xff;
const uint8_t *fregs;
/* Dell OptiPlex Gn+ shows that register 02:FF is aliased in 01:FF. */
if ((dev->type == 4) && (func == 1) && (addr == 0xff))
func = 2;
if ((dev->type == 3) && (func == 2) && (dev->max_func == 1) && (addr >= 0x40))
ret = 0x00;
@@ -1199,7 +1226,7 @@ piix_reset_hard(piix_t *dev)
sff_set_slot(dev->bm[1], dev->pci_slot);
sff_set_irq_pin(dev->bm[1], PCI_INTA);
sff_set_irq_line(dev->bm[1], 14);
sff_set_irq_line(dev->bm[1], 15);
sff_set_irq_mode(dev->bm[1], IRQ_MODE_LEGACY);
}
@@ -1315,6 +1342,10 @@ piix_reset_hard(piix_t *dev)
fregs[0x45] = 0x55;
fregs[0x46] = 0x01;
}
if (dev->type == 4) {
fregs[0xf8] = 0x30;
fregs[0xf9] = 0x0f;
}
if ((dev->type == 1) && (dev->rev == 2))
dev->max_func = 0; /* It starts with IDE disabled, then enables it. */
else
@@ -1678,7 +1709,7 @@ const device_t piix4_device = {
.name = "Intel 82371AB/EB (PIIX4/PIIX4E)",
.internal_name = "piix4",
.flags = DEVICE_PCI,
.local = 0x71100004,
.local = 0x71100014,
.init = piix_init,
.close = piix_close,
.reset = piix_reset,

View File

@@ -15,6 +15,7 @@
*
* Copyright 2019-2020 Miran Grca.
*/
#include <math.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
@@ -631,6 +632,41 @@ sis_85c4xx_out(uint16_t port, uint8_t val, void *priv)
sis_85c4xx_recalcremap(dev);
break;
case 0x10:
if (dev->reg_base == 0x50) {
double bus_clk;
switch (val & 0xe0) {
default:
case 0x00:
bus_clk = 7159091.0;
break;
case 0x02:
bus_clk = cpu_busspeed / 10.0;
break;
case 0x04:
bus_clk = cpu_busspeed / 8.0;
break;
case 0x06:
bus_clk = cpu_busspeed / 6.0;
break;
case 0x80:
bus_clk = cpu_busspeed / 5.0;
break;
case 0xa0:
bus_clk = cpu_busspeed / 4.0;
break;
case 0xc0:
bus_clk = cpu_busspeed / 3.0;
break;
case 0xe0:
bus_clk = cpu_busspeed / 2.0;
break;
}
cpu_set_isa_speed((int) round(bus_clk));
}
break;
case 0x13:
if (dev->is_471 && (valxor & 0xf0)) {
smram_disable(dev->smram);
@@ -813,6 +849,9 @@ sis_85c4xx_reset(void *priv)
dev->force_flush = 1;
sis_85c4xx_recalcmapping(dev);
if (dev->reg_base == 0x50)
cpu_set_isa_speed((int) round(7159091.0));
}
static void

View File

@@ -132,7 +132,8 @@ vl82c480_write(uint16_t addr, uint8_t val, void *priv)
break;
case 0x02: case 0x03:
dev->regs[dev->idx] = val;
if (!strcmp(machine_get_internal_name(), "martin"))
if (!strcmp(machine_get_internal_name(), "martin") ||
!strcmp(machine_get_internal_name(), "prolineamt"))
vl82c480_recalc_banks(dev);
break;
case 0x04:
@@ -140,8 +141,6 @@ vl82c480_write(uint16_t addr, uint8_t val, void *priv)
dev->regs[dev->idx] = (dev->regs[dev->idx] & 0x08) | (val & 0xf7);
else
dev->regs[dev->idx] = val;
if (!strcmp(machine_get_internal_name(), "martin"))
dev->regs[dev->idx] &= 0x1f;
break;
case 0x05:
dev->regs[dev->idx] = (dev->regs[dev->idx] & 0x10) | (val & 0xef);
@@ -221,6 +220,9 @@ vl82c480_init(const device_t *info)
vl82c480_t *dev = (vl82c480_t *) calloc(1, sizeof(vl82c480_t));
uint32_t sizes[8] = { 0, 0, 1024, 2048, 4096, 8192, 16384, 32768 };
uint32_t ms = mem_size;
uint8_t min_i = !strcmp(machine_get_internal_name(), "prolineamt") ? 1 : 0;
uint8_t min_j = !strcmp(machine_get_internal_name(), "prolineamt") ? 4 : 2;
uint8_t max_j = !strcmp(machine_get_internal_name(), "prolineamt") ? 8 : 7;
dev->regs[0x00] = info->local;
dev->regs[0x01] = 0xff;
@@ -231,8 +233,16 @@ vl82c480_init(const device_t *info)
dev->regs[0x07] = 0x21;
dev->regs[0x08] = 0x38;
for (uint8_t i = 0; i < 4; i++) {
for (uint8_t j = 2; j < 7; j++) {
if (!strcmp(machine_get_internal_name(), "prolineamt")) {
dev->banks[0] = 4096;
/* Bank 0 is ignored if 64 MB is installed. */
if (ms != 65536)
ms -= 4096;
}
if (ms > 0) for (uint8_t i = min_i; i < 4; i++) {
for (uint8_t j = min_j; j < max_j; j++) {
if (ms >= sizes[j])
dev->banks[i] = sizes[j];
else

View File

@@ -818,6 +818,7 @@ codegen_generate_call(uint8_t opcode, OpFn op, uint32_t fetchdat, uint32_t new_p
int over = 0;
int pc_off = 0;
int test_modrm = 1;
int in_lock = 0;
int c;
uint32_t op87 = 0x00000000;
@@ -956,6 +957,7 @@ codegen_generate_call(uint8_t opcode, OpFn op, uint32_t fetchdat, uint32_t new_p
break;
case 0xf0: /*LOCK*/
in_lock = 0;
break;
case 0xf2: /*REPNE*/
@@ -1013,6 +1015,9 @@ generate_call:
STORE_IMM_ADDR_L((uintptr_t) &x87_op, op87);
}
if (in_lock && ((opcode == 0x90) || (opcode == 0xec)))
goto codegen_skip;
if (recomp_op_table && recomp_op_table[(opcode | op_32) & 0x1ff]) {
uint32_t new_pc = recomp_op_table[(opcode | op_32) & 0x1ff](opcode, fetchdat, op_32, op_pc, block);
if (new_pc) {
@@ -1040,7 +1045,13 @@ generate_call:
}
}
op = op_table[((opcode >> opcode_shift) | op_32) & opcode_mask];
codegen_skip:
if (in_lock && ((opcode == 0x90) || (opcode == 0xec)))
/* This is always ILLEGAL. */
op = x86_dynarec_opcodes_3DNOW[0xff];
else
op = op_table[((opcode >> opcode_shift) | op_32) & opcode_mask];
if (op_ssegs != last_ssegs) {
last_ssegs = op_ssegs;
addbyte(0xC6); /*MOVB $0,(ssegs)*/

View File

@@ -1857,6 +1857,7 @@ codegen_generate_call(uint8_t opcode, OpFn op, uint32_t fetchdat, uint32_t new_p
int over = 0;
int pc_off = 0;
int test_modrm = 1;
int in_lock = 0;
int c;
uint32_t op87 = 0x00000000;
@@ -1996,6 +1997,7 @@ codegen_generate_call(uint8_t opcode, OpFn op, uint32_t fetchdat, uint32_t new_p
break;
case 0xf0: /*LOCK*/
in_lock = 1;
break;
case 0xf2: /*REPNE*/
@@ -2054,6 +2056,9 @@ generate_call:
STORE_IMM_ADDR_L((uintptr_t) &x87_op, op87);
}
if (in_lock && ((opcode == 0x90) || (opcode == 0xec)))
goto codegen_skip;
if (recomp_op_table && recomp_op_table[(opcode | op_32) & 0x1ff]) {
uint32_t new_pc = recomp_op_table[(opcode | op_32) & 0x1ff](opcode, fetchdat, op_32, op_pc, block);
if (new_pc) {
@@ -2080,7 +2085,13 @@ generate_call:
}
}
op = op_table[((opcode >> opcode_shift) | op_32) & opcode_mask];
codegen_skip:
if (in_lock && ((opcode == 0x90) || (opcode == 0xec)))
/* This is always ILLEGAL. */
op = x86_dynarec_opcodes_3DNOW[0xff];
else
op = op_table[((opcode >> opcode_shift) | op_32) & opcode_mask];
if (op_ssegs != last_ssegs) {
last_ssegs = op_ssegs;

View File

@@ -395,6 +395,7 @@ codegen_generate_call(uint8_t opcode, OpFn op, uint32_t fetchdat, uint32_t new_p
int over = 0;
int test_modrm = 1;
int pc_off = 0;
int in_lock = 0;
uint32_t next_pc = 0;
uint16_t op87 = 0x0000;
#ifdef DEBUG_EXTRA
@@ -556,6 +557,7 @@ codegen_generate_call(uint8_t opcode, OpFn op, uint32_t fetchdat, uint32_t new_p
break;
case 0xf0: /*LOCK*/
in_lock = 1;
break;
case 0xf2: /*REPNE*/
@@ -675,6 +677,9 @@ generate_call:
goto codegen_skip;
#endif
if (in_lock && ((opcode == 0x90) || (opcode == 0xec)))
goto codegen_skip;
if (recomp_op_table && recomp_op_table[(opcode | op_32) & recomp_opcode_mask]) {
uint32_t new_pc = recomp_op_table[(opcode | op_32) & recomp_opcode_mask](block, ir, opcode, fetchdat, op_32, op_pc);
if (new_pc) {
@@ -692,13 +697,17 @@ generate_call:
}
}
// codegen_skip:
codegen_skip:
if ((op_table == x86_dynarec_opcodes_REPNE || op_table == x86_dynarec_opcodes_REPE) && !op_table[opcode | op_32]) {
op_table = x86_dynarec_opcodes;
recomp_op_table = recomp_opcodes;
}
op = op_table[((opcode >> opcode_shift) | op_32) & opcode_mask];
if (in_lock && ((opcode == 0x90) || (opcode == 0xec)))
/* This is always ILLEGAL. */
op = x86_dynarec_opcodes_3DNOW[0xff];
else
op = op_table[((opcode >> opcode_shift) | op_32) & opcode_mask];
if (!test_modrm || (op_table == x86_dynarec_opcodes && opcode_modrm[opcode]) || (op_table == x86_dynarec_opcodes_0f && opcode_0f_modrm[opcode]) || (op_table == x86_dynarec_opcodes_3DNOW)) {
int stack_offset = 0;

View File

@@ -781,14 +781,9 @@ host_x86_MOV32_REG_ABS(codeblock_t *block, int dst_reg, void *p)
codegen_addbyte4(block, 0x41, 0x8b, 0x84 | ((dst_reg & 7) << 3), 0x24); /*MOV dst_reg, ram_offset[R12]*/
codegen_addlong(block, ram_offset);
} else if ((ram_offset < -2147483648LL) || (ram_offset > 2147483647LL) || !(block->flags & CODEBLOCK_NO_IMMEDIATES)) {
// fatal("host_x86_MOV32_REG_ABS - out of range\n");
// void *q = p;
//uint32_t *r = NULL;
// *r = 5; /* Crash deliberately. */
codegen_alloc_bytes(block, 8);
codegen_alloc_bytes(block, 13);
codegen_addbyte2(block, 0x49, 0xb9); /*MOV r9,(uintptr_t) p*/
codegen_addquad(block, (uintptr_t) p);
codegen_alloc_bytes(block, 3);
codegen_addbyte3(block, 0x41, 0x8b, 0x01 | ((dst_reg & 7) << 3)); /*MOV dst_reg, [R9]*/
} else {
fatal("host_x86_MOV32_REG_ABS - RAM offset = %016" PRIX64 " (p - ram = %016" PRIX64 ")\n", ram_offset, (uintptr_t) p - (uintptr_t) ram);

View File

@@ -753,7 +753,7 @@ opLOCK(uint32_t fetchdat)
return 0;
cpu_state.pc++;
ILLEGAL_ON((fetchdat & 0xff) == 0x90);
ILLEGAL_ON(((fetchdat & 0xff) == 0x90) || ((fetchdat & 0xff) == 0xec));
CLOCK_CYCLES(4);
PREFETCH_PREFIX();

View File

@@ -145,6 +145,11 @@ typedef struct atkbc_t {
/* Internal FIFO for the purpose of commands with multi-byte output. */
uint8_t key_ctrl_queue[64];
uint8_t handler_enable[2];
uint16_t base_addr[2];
uint16_t irq[2];
uint32_t flags;
/* Main timers. */
@@ -157,8 +162,13 @@ typedef struct atkbc_t {
/* Local copies of the pointers to both ports for easier swapping (AMI '5' MegaKey). */
kbc_at_port_t *ports[2];
uint8_t (*write60_ven)(void *priv, uint8_t val);
uint8_t (*write64_ven)(void *priv, uint8_t val);
struct {
uint8_t (*read)(uint16_t port, void *priv);
void (*write)(uint16_t port, uint8_t val, void *priv);
} handlers[2];
uint8_t (*write_cmd_data_ven)(void *priv, uint8_t val);
uint8_t (*write_cmd_ven)(void *priv, uint8_t val);
} atkbc_t;
/* Keyboard controller ports. */
@@ -167,8 +177,6 @@ kbc_at_port_t *kbc_at_ports[2] = { NULL, NULL };
static uint8_t kbc_ami_revision = '8';
static uint8_t kbc_award_revision = 0x42;
static uint8_t kbc_handler_set = 0;
static void (*kbc_at_do_poll)(atkbc_t *dev);
/* Non-translated to translated scan codes. */
@@ -362,12 +370,19 @@ kbc_do_irq(atkbc_t *dev)
if (dev->do_irq) {
/* WARNING: On PS/2, all IRQ's are level-triggered, but the IBM PS/2 KBC firmware is explicitly
written to pulse its P2 IRQ bits, so they should be kept as as edge-triggered here. */
picint_common(1 << 1, 0, 0, NULL);
picint_common(1 << 12, 0, 0, NULL);
if (dev->channel >= 2)
picint_common(1 << 12, 0, 1, NULL);
else
picint_common(1 << 1, 0, 1, NULL);
if (dev->irq[0] != 0xffff)
picint_common(1 << dev->irq[0], 0, 0, NULL);
if (dev->irq[1] != 0xffff)
picint_common(1 << dev->irq[1], 0, 0, NULL);
if (dev->channel >= 2) {
if (dev->irq[1] != 0xffff)
picint_common(1 << dev->irq[1], 0, 1, NULL);
} else {
if (dev->irq[0] != 0xffff)
picint_common(1 << dev->irq[0], 0, 1, NULL);
}
dev->do_irq = 0;
}
@@ -404,7 +419,9 @@ kbc_send_to_ob(atkbc_t *dev, uint8_t val, uint8_t channel, uint8_t stat_hi)
} else if (dev->mem[0x20] & 0x01)
kbc_set_do_irq(dev, channel);
} else if (dev->mem[0x20] & 0x01)
picintlevel(1 << 1, &dev->irq_state); /* AT KBC: IRQ 1 is level-triggered because it is tied to OBF. */
/* AT KBC: IRQ 1 is level-triggered because it is tied to OBF. */
if (dev->irq[0] != 0xffff)
picintlevel(1 << dev->irq[0], &dev->irq_state);
#ifdef WRONG_CONDITION
if ((dev->channel > 0) || dev->is_asic || (kbc_ven == KBC_VEN_IBM_PS1) || (kbc_ven == KBC_VEN_IBM))
@@ -784,10 +801,12 @@ write_p2(atkbc_t *dev, uint8_t val)
/* PS/2: Handle IRQ's. */
if (dev->misc_flags & FLAG_PS2) {
/* IRQ 12 */
picint_common(1 << 12, 0, val & 0x20, NULL);
if (dev->irq[1] != 0xffff)
picint_common(1 << dev->irq[1], 0, val & 0x20, NULL);
/* IRQ 1 */
picint_common(1 << 1, 0, val & 0x10, NULL);
if (dev->irq[0] != 0xffff)
picint_common(1 << dev->irq[0], 0, val & 0x10, NULL);
}
#endif
@@ -932,7 +951,7 @@ pulse_poll(void *priv)
}
static uint8_t
write64_generic(void *priv, uint8_t val)
write_cmd_generic(void *priv, uint8_t val)
{
atkbc_t *dev = (atkbc_t *) priv;
uint8_t current_drive;
@@ -1088,12 +1107,41 @@ write64_generic(void *priv, uint8_t val)
/* (B0 or F0) | (0x04 or 0x0c) */
kbc_delay_to_ob(dev, dev->p1 | fixed_bits, 0, 0x00);
} else if (((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_1) && ((dev->flags & KBC_TYPE_MASK) < KBC_TYPE_GREEN)) {
/* (B0 or F0) | (0x08 or 0x0c) */
uint8_t p1_out = ((dev->p1 | fixed_bits) & 0xf0) |
(((dev->flags & KBC_VEN_MASK) == KBC_VEN_ACER) ? 0x08 : 0x0c);
if (!strcmp(machine_get_internal_name(), "alfredo"))
p1_out &= 0xef;
kbc_delay_to_ob(dev, p1_out, 0, 0x00);
if (!strcmp(machine_get_internal_name(), "dell466np")) {
/*
Dell 466/NP:
- Bit 2: Keyboard fuse (must be set);
- Bit 4: Password disable jumper (must be clear);
- Bit 5: Manufacturing jumper (must be set);
*/
uint8_t p1 = 0x24;
kbc_delay_to_ob(dev, p1, 0, 0x00);
} else if (!strcmp(machine_get_internal_name(), "optiplex_gxl")) {
/*
Dell OptiPlex GXL/GXM:
- Bit 3: Password disable jumper (must be clear);
- Bit 4: Keyboard fuse (must be set);
- Bit 5: Manufacturing jumper (must be set);
*/
uint8_t p1 = 0x30;
kbc_delay_to_ob(dev, p1, 0, 0x00);
} else if (!strcmp(machine_get_internal_name(), "dellplato") || !strcmp(machine_get_internal_name(), "dellhannibalp")) {
/*
Dell Dimension XPS Pxxx & Pxxxa/Mxxxa:
- Bit 3: Password disable jumper (must be clear);
- Bit 4: Clear CMOS jumper (must be set);
*/
uint8_t p1 = 0x10;
kbc_delay_to_ob(dev, p1, 0, 0x00);
} else {
/* (B0 or F0) | (0x08 or 0x0c) */
uint8_t p1_out = ((dev->p1 | fixed_bits) & 0xf0) |
(((dev->flags & KBC_VEN_MASK) == KBC_VEN_ACER) ? 0x08 : 0x0c);
if (!strcmp(machine_get_internal_name(), "alfredo"))
p1_out &= 0xef;
kbc_delay_to_ob(dev, p1_out, 0, 0x00);
}
} else if (kbc_ven == KBC_VEN_COMPAQ)
kbc_delay_to_ob(dev, dev->p1 | (hasfpu ? 0x00 : 0x04), 0, 0x00);
else
@@ -1149,7 +1197,7 @@ write64_generic(void *priv, uint8_t val)
}
static uint8_t
write60_ami(void *priv, uint8_t val)
write_cmd_data_ami(void *priv, uint8_t val)
{
atkbc_t *dev = (atkbc_t *) priv;
@@ -1219,7 +1267,7 @@ kbc_at_set_ps2(void *priv, const uint8_t ps2)
}
static uint8_t
write64_ami(void *priv, uint8_t val)
write_cmd_ami(void *priv, uint8_t val)
{
atkbc_t *dev = (atkbc_t *) priv;
uint8_t kbc_ven = dev->flags & KBC_VEN_MASK;
@@ -1424,11 +1472,11 @@ write64_ami(void *priv, uint8_t val)
break;
}
return write64_generic(dev, val);
return write_cmd_generic(dev, val);
}
static uint8_t
write60_phoenix(void *priv, uint8_t val)
write_cmd_data_phoenix(void *priv, uint8_t val)
{
atkbc_t *dev = (atkbc_t *) priv;
@@ -1501,7 +1549,7 @@ write60_phoenix(void *priv, uint8_t val)
}
static uint8_t
write64_phoenix(void *priv, uint8_t val)
write_cmd_phoenix(void *priv, uint8_t val)
{
atkbc_t *dev = (atkbc_t *) priv;
@@ -1647,11 +1695,11 @@ write64_phoenix(void *priv, uint8_t val)
break;
}
return write64_generic(dev, val);
return write_cmd_generic(dev, val);
}
static uint8_t
write64_siemens(void *priv, uint8_t val)
write_cmd_siemens(void *priv, uint8_t val)
{
atkbc_t *dev = (atkbc_t *) priv;
@@ -1680,11 +1728,11 @@ write64_siemens(void *priv, uint8_t val)
break;
}
return write64_ami(dev, val);
return write_cmd_ami(dev, val);
}
static uint8_t
write60_quadtel(void *priv, UNUSED(uint8_t val))
write_cmd_data_quadtel(void *priv, UNUSED(uint8_t val))
{
const atkbc_t *dev = (atkbc_t *) priv;
@@ -1701,7 +1749,7 @@ write60_quadtel(void *priv, UNUSED(uint8_t val))
}
static uint8_t
write64_olivetti(void *priv, uint8_t val)
write_cmd_olivetti(void *priv, uint8_t val)
{
atkbc_t *dev = (atkbc_t *) priv;
@@ -1722,11 +1770,11 @@ write64_olivetti(void *priv, uint8_t val)
break;
}
return write64_generic(dev, val);
return write_cmd_generic(dev, val);
}
static uint8_t
write64_quadtel(void *priv, uint8_t val)
write_cmd_quadtel(void *priv, uint8_t val)
{
atkbc_t *dev = (atkbc_t *) priv;
@@ -1745,11 +1793,11 @@ write64_quadtel(void *priv, uint8_t val)
break;
}
return write64_generic(dev, val);
return write_cmd_generic(dev, val);
}
static uint8_t
write60_toshiba(void *priv, uint8_t val)
write_cmd_data_toshiba(void *priv, uint8_t val)
{
const atkbc_t *dev = (atkbc_t *) priv;
@@ -1767,7 +1815,7 @@ write60_toshiba(void *priv, uint8_t val)
}
static uint8_t
write64_toshiba(void *priv, uint8_t val)
write_cmd_toshiba(void *priv, uint8_t val)
{
atkbc_t *dev = (atkbc_t *) priv;
@@ -1856,7 +1904,7 @@ write64_toshiba(void *priv, uint8_t val)
break;
}
return write64_generic(dev, val);
return write_cmd_generic(dev, val);
}
static void
@@ -1900,8 +1948,10 @@ kbc_at_process_cmd(void *priv)
/* TODO: Proper P1 implementation, with OR and AND flags in the machine table. */
dev->p1 = dev->p1 & 0xff;
write_p2(dev, 0x4b);
picintc(0x1000);
picintc(0x0002);
if (dev->irq[1] != 0xffff)
picintc(1 << dev->irq[1]);
if (dev->irq[0] != 0xffff)
picintc(1 << dev->irq[0]);
}
dev->status = (dev->status & 0x0f) | 0x60;
@@ -1920,7 +1970,8 @@ kbc_at_process_cmd(void *priv)
/* TODO: Proper P1 implementation, with OR and AND flags in the machine table. */
dev->p1 = dev->p1 & 0xff;
write_p2(dev, 0xcf);
picintclevel(0x0002, &dev->irq_state);
if (dev->irq[0] != 0xffff)
picintclevel(1 << dev->irq[0], &dev->irq_state);
dev->irq_state = 0;
}
@@ -2035,8 +2086,8 @@ kbc_at_process_cmd(void *priv)
* that. Otherwise, or if that handler fails,
* log a bad command.
*/
if (dev->write64_ven)
bad = dev->write64_ven(dev, dev->ib);
if (dev->write_cmd_ven)
bad = dev->write_cmd_ven(dev, dev->ib);
kbc_at_log(bad ? "ATkbc: bad controller command %02X\n" : "", dev->ib);
}
@@ -2122,8 +2173,8 @@ kbc_at_process_cmd(void *priv)
* it returns an error, log a bad
* controller command.
*/
if (dev->write60_ven)
bad = dev->write60_ven(dev, dev->ib);
if (dev->write_cmd_data_ven)
bad = dev->write_cmd_data_ven(dev, dev->ib);
if (bad) {
kbc_at_log("ATkbc: bad controller command %02x data %02x\n", dev->command, dev->ib);
@@ -2133,7 +2184,7 @@ kbc_at_process_cmd(void *priv)
}
static void
kbc_at_write(uint16_t port, uint8_t val, void *priv)
kbc_at_port_1_write(uint16_t port, uint8_t val, void *priv)
{
atkbc_t *dev = (atkbc_t *) priv;
uint8_t kbc_ven = dev->flags & KBC_VEN_MASK;
@@ -2141,83 +2192,89 @@ kbc_at_write(uint16_t port, uint8_t val, void *priv)
kbc_at_log("ATkbc: [%04X:%08X] write(%04X) = %02X\n", CS, cpu_state.pc, port, val);
switch (port) {
case 0x60:
dev->status &= ~STAT_CD;
if (fast_a20 && dev->wantdata && (dev->command == 0xd1)) {
kbc_at_log("ATkbc: write P2\n");
dev->status &= ~STAT_CD;
/* Fast A20 - ignore all other bits. */
write_p2_fast_a20(dev, (dev->p2 & 0xfd) | (val & 0x02));
if (fast_a20 && dev->wantdata && (dev->command == 0xd1)) {
kbc_at_log("ATkbc: write P2\n");
dev->wantdata = 0;
dev->state = STATE_MAIN_IBF;
/* Fast A20 - ignore all other bits. */
write_p2_fast_a20(dev, (dev->p2 & 0xfd) | (val & 0x02));
/*
Explicitly clear IBF so that any preceding
command is not executed.
*/
dev->status &= ~STAT_IFULL;
return;
}
break;
dev->wantdata = 0;
dev->state = STATE_MAIN_IBF;
case 0x64:
dev->status |= STAT_CD;
if (fast_a20 && (val == 0xd1)) {
kbc_at_log("ATkbc: write P2\n");
dev->wantdata = 1;
dev->state = STATE_KBC_PARAM;
dev->command = 0xd1;
/*
Explicitly clear IBF so that any preceding
command is not executed.
*/
dev->status &= ~STAT_IFULL;
return;
}
/*
Explicitly clear IBF so that any preceding
command is not executed.
*/
dev->status &= ~STAT_IFULL;
return;
} else if (fast_reset && ((val & 0xf0) == 0xf0)) {
pulse_output(dev, val & 0x0f);
dev->ib = val;
dev->status |= STAT_IFULL;
}
dev->state = STATE_MAIN_IBF;
static void
kbc_at_port_2_write(uint16_t port, uint8_t val, void *priv)
{
atkbc_t *dev = (atkbc_t *) priv;
uint8_t kbc_ven = dev->flags & KBC_VEN_MASK;
uint8_t fast_a20 = (kbc_ven != KBC_VEN_SIEMENS);
/*
Explicitly clear IBF so that any preceding
command is not executed.
*/
dev->status &= ~STAT_IFULL;
return;
} else if (val == 0xad) {
/* Fast track it because of the Bochs BIOS. */
kbc_at_log("ATkbc: disable keyboard\n");
set_enable_kbd(dev, 0);
kbc_at_log("ATkbc: [%04X:%08X] write(%04X) = %02X\n", CS, cpu_state.pc, port, val);
dev->state = STATE_MAIN_IBF;
dev->status |= STAT_CD;
/*
Explicitly clear IBF so that any preceding
command is not executed.
*/
dev->status &= ~STAT_IFULL;
return;
} else if (val == 0xae) {
/* Fast track it because of the LG MultiNet. */
kbc_at_log("ATkbc: enable keyboard\n");
set_enable_kbd(dev, 1);
if (fast_a20 && (val == 0xd1)) {
kbc_at_log("ATkbc: write P2\n");
dev->wantdata = 1;
dev->state = STATE_KBC_PARAM;
dev->command = 0xd1;
dev->state = STATE_MAIN_IBF;
/*
Explicitly clear IBF so that any preceding
command is not executed.
*/
dev->status &= ~STAT_IFULL;
return;
} else if (fast_reset && ((val & 0xf0) == 0xf0)) {
pulse_output(dev, val & 0x0f);
/*
Explicitly clear IBF so that any preceding
command is not executed.
*/
dev->status &= ~STAT_IFULL;
return;
}
break;
dev->state = STATE_MAIN_IBF;
default:
break;
/*
Explicitly clear IBF so that any preceding
command is not executed.
*/
dev->status &= ~STAT_IFULL;
return;
} else if (val == 0xad) {
/* Fast track it because of the Bochs BIOS. */
kbc_at_log("ATkbc: disable keyboard\n");
set_enable_kbd(dev, 0);
dev->state = STATE_MAIN_IBF;
/*
Explicitly clear IBF so that any preceding
command is not executed.
*/
dev->status &= ~STAT_IFULL;
return;
} else if (val == 0xae) {
/* Fast track it because of the LG MultiNet. */
kbc_at_log("ATkbc: enable keyboard\n");
set_enable_kbd(dev, 1);
dev->state = STATE_MAIN_IBF;
/*
Explicitly clear IBF so that any preceding
command is not executed.
*/
dev->status &= ~STAT_IFULL;
return;
}
dev->ib = val;
@@ -2225,7 +2282,7 @@ kbc_at_write(uint16_t port, uint8_t val, void *priv)
}
static uint8_t
kbc_at_read(uint16_t port, void *priv)
kbc_at_port_1_read(uint16_t port, void *priv)
{
atkbc_t *dev = (atkbc_t *) priv;
uint8_t ret = 0xff;
@@ -2233,26 +2290,32 @@ kbc_at_read(uint16_t port, void *priv)
if ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_1)
cycles -= ISA_CYCLES(8);
switch (port) {
case 0x60:
ret = dev->ob;
dev->status &= ~STAT_OFULL;
/* TODO: IRQ is only tied to OBF on the AT KBC, on the PS/2 KBC, it is controlled by a P2 bit.
This also means that in AT mode, the IRQ is level-triggered. */
if (!(dev->misc_flags & FLAG_PS2))
picintclevel(1 << 1, &dev->irq_state);
if ((strstr(machine_get_internal_name(), "pb41") != NULL) && (cpu_override_dynarec == 1))
cpu_override_dynarec = 0;
break;
ret = dev->ob;
dev->status &= ~STAT_OFULL;
/*
TODO: IRQ is only tied to OBF on the AT KBC, on the PS/2 KBC, it is controlled by a P2 bit.
This also means that in AT mode, the IRQ is level-triggered.
*/
if (!(dev->misc_flags & FLAG_PS2) && (dev->irq[0] != 0xffff))
picintclevel(1 << dev->irq[0], &dev->irq_state);
if ((strstr(machine_get_internal_name(), "pb41") != NULL) && (cpu_override_dynarec == 1))
cpu_override_dynarec = 0;
case 0x64:
ret = dev->status;
break;
kbc_at_log("ATkbc: [%04X:%08X] read (%04X) = %02X\n", CS, cpu_state.pc, port, ret);
default:
kbc_at_log("ATkbc: read(%04x) invalid!\n",port);
break;
}
return ret;
}
static uint8_t
kbc_at_port_2_read(uint16_t port, void *priv)
{
atkbc_t *dev = (atkbc_t *) priv;
uint8_t ret = 0xff;
if ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_1)
cycles -= ISA_CYCLES(8);
ret = dev->status;
kbc_at_log("ATkbc: [%04X:%08X] read (%04X) = %02X\n", CS, cpu_state.pc, port, ret);
@@ -2291,11 +2354,14 @@ kbc_at_reset(void *priv)
if ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_1) {
dev->misc_flags |= FLAG_PS2;
kbc_at_do_poll = kbc_at_poll_ps2;
picintc(0x1000);
picintc(0x0002);
if (dev->irq[1] != 0xffff)
picintc(1 << dev->irq[1]);
if (dev->irq[0] != 0xffff)
picintc(1 << dev->irq[0]);
} else {
kbc_at_do_poll = kbc_at_poll_at;
picintclevel(0x0002, &dev->irq_state);
if (dev->irq[0] != 0xffff)
picintclevel(1 << dev->irq[0], &dev->irq_state);
dev->irq_state = 0;
}
@@ -2338,19 +2404,44 @@ kbc_at_close(void *priv)
}
void
kbc_at_handler(int set, void *priv)
kbc_at_port_handler(int num, int set, uint16_t port, void *priv)
{
if (kbc_handler_set) {
io_removehandler(0x0060, 1, kbc_at_read, NULL, NULL, kbc_at_write, NULL, NULL, priv);
io_removehandler(0x0064, 1, kbc_at_read, NULL, NULL, kbc_at_write, NULL, NULL, priv);
atkbc_t *dev = (atkbc_t *) priv;
if (dev->handler_enable[num] && (dev->base_addr[num] != 0x0000))
io_removehandler(dev->base_addr[num], 1,
dev->handlers[num].read, NULL, NULL,
dev->handlers[num].write, NULL, NULL, priv);
dev->handler_enable[num] = set;
dev->base_addr[num] = port;
if (dev->handler_enable[num] && (dev->base_addr[num] != 0x0000))
io_sethandler(dev->base_addr[num], 1,
dev->handlers[num].read, NULL, NULL,
dev->handlers[num].write, NULL, NULL, priv);
}
void
kbc_at_handler(int set, uint16_t port, void *priv)
{
kbc_at_port_handler(0, set, port, priv);
kbc_at_port_handler(1, set, port + 0x0004, priv);
}
void
kbc_at_set_irq(int num, uint16_t irq, void *priv)
{
atkbc_t *dev = (atkbc_t *) priv;
if (dev->irq[num] != 0xffff) {
if ((num == 0) && ((dev->flags & KBC_TYPE_MASK) < KBC_TYPE_PS2_1))
picintclevel(1 << dev->irq[num], &dev->irq_state);
else
picintc(1 << dev->irq[num]);
}
kbc_handler_set = set;
if (kbc_handler_set) {
io_sethandler(0x0060, 1, kbc_at_read, NULL, NULL, kbc_at_write, NULL, NULL, priv);
io_sethandler(0x0064, 1, kbc_at_read, NULL, NULL, kbc_at_write, NULL, NULL, priv);
}
dev->irq[num] = irq;
}
static void *
@@ -2371,16 +2462,21 @@ kbc_at_init(const device_t *info)
if (info->flags & DEVICE_PCI)
dev->misc_flags |= FLAG_PCI;
kbc_handler_set = 0;
kbc_at_handler(1, dev);
dev->handlers[0].read = kbc_at_port_1_read;
dev->handlers[0].write = kbc_at_port_1_write;
dev->handlers[1].read = kbc_at_port_2_read;
dev->handlers[1].write = kbc_at_port_2_write;
dev->irq[0] = 1;
dev->irq[1] = 12;
timer_add(&dev->kbc_poll_timer, kbc_at_poll, dev, 1);
timer_add(&dev->pulse_cb, pulse_poll, dev, 0);
timer_add(&dev->kbc_dev_poll_timer, kbc_at_dev_poll, dev, 1);
dev->write60_ven = NULL;
dev->write64_ven = NULL;
dev->write_cmd_data_ven = NULL;
dev->write_cmd_ven = NULL;
kbc_ami_revision = '8';
kbc_award_revision = 0x42;
@@ -2389,8 +2485,8 @@ kbc_at_init(const device_t *info)
case KBC_VEN_SIEMENS:
kbc_ami_revision = '8';
kbc_award_revision = 0x42;
dev->write60_ven = write60_ami;
dev->write64_ven = write64_siemens;
dev->write_cmd_data_ven = write_cmd_data_ami;
dev->write_cmd_ven = write_cmd_siemens;
break;
case KBC_VEN_ACER:
@@ -2399,24 +2495,24 @@ kbc_at_init(const device_t *info)
case KBC_VEN_IBM_PS1:
case KBC_VEN_IBM:
case KBC_VEN_COMPAQ:
dev->write64_ven = write64_generic;
dev->write_cmd_ven = write_cmd_generic;
break;
case KBC_VEN_OLIVETTI:
dev->write64_ven = write64_olivetti;
dev->write_cmd_ven = write_cmd_olivetti;
break;
case KBC_VEN_ALI:
kbc_ami_revision = 'F';
kbc_award_revision = 0x43;
dev->write60_ven = write60_ami;
dev->write64_ven = write64_ami;
dev->write_cmd_data_ven = write_cmd_data_ami;
dev->write_cmd_ven = write_cmd_ami;
break;
case KBC_VEN_TRIGEM_AMI:
kbc_ami_revision = 'Z';
dev->write60_ven = write60_ami;
dev->write64_ven = write64_ami;
dev->write_cmd_data_ven = write_cmd_data_ami;
dev->write_cmd_ven = write_cmd_ami;
break;
case KBC_VEN_AMI:
@@ -2439,23 +2535,23 @@ kbc_at_init(const device_t *info)
else
kbc_ami_revision = 'F';
dev->write60_ven = write60_ami;
dev->write64_ven = write64_ami;
dev->write_cmd_data_ven = write_cmd_data_ami;
dev->write_cmd_ven = write_cmd_ami;
break;
case KBC_VEN_PHOENIX:
dev->write60_ven = write60_phoenix;
dev->write64_ven = write64_phoenix;
dev->write_cmd_data_ven = write_cmd_data_phoenix;
dev->write_cmd_ven = write_cmd_phoenix;
break;
case KBC_VEN_QUADTEL:
dev->write60_ven = write60_quadtel;
dev->write64_ven = write64_quadtel;
dev->write_cmd_data_ven = write_cmd_data_quadtel;
dev->write_cmd_ven = write_cmd_quadtel;
break;
case KBC_VEN_TOSHIBA:
dev->write60_ven = write60_toshiba;
dev->write64_ven = write64_toshiba;
dev->write_cmd_data_ven = write_cmd_data_toshiba;
dev->write_cmd_ven = write_cmd_toshiba;
break;
default:
@@ -2481,6 +2577,8 @@ kbc_at_init(const device_t *info)
fast_reset = 0x00;
kbc_at_handler(1, 0x0060, dev);
return dev;
}

View File

@@ -30,11 +30,13 @@
#include "cpu.h"
uint8_t postcard_codes[POSTCARDS_NUM];
char postcard_diags[5] = { 0 };
static uint16_t postcard_port;
static uint8_t postcard_written[POSTCARDS_NUM];
static uint8_t postcard_ports_num = 1;
static uint8_t postcard_prev_codes[POSTCARDS_NUM];
static char postcard_prev_diags[5] = { 0 };
#define UISTR_LEN 32
static char postcard_str[UISTR_LEN]; /* UI output string */
@@ -97,6 +99,22 @@ postcard_setui(void)
ps[1][0], ps[1][1], ps[1][2], ps[1][3]);
break;
}
} else if (strstr(machines[machine].name, " Dell ")) {
char dell_diags[10] = { 0 };
if (!postcard_written[1])
snprintf(dell_diags, sizeof(dell_diags), "---- ----");
else if (postcard_written[1] == 1)
snprintf(dell_diags, sizeof(dell_diags), "%s ----", postcard_diags);
else
snprintf(dell_diags, sizeof(dell_diags), "%s %s", postcard_diags, postcard_prev_diags);
if (!postcard_written[0])
snprintf(postcard_str, sizeof(postcard_str), "POST: -- -- %s", dell_diags);
else if (postcard_written[0] == 1)
snprintf(postcard_str, sizeof(postcard_str), "POST: %02X -- %s", postcard_codes[0], dell_diags);
else
snprintf(postcard_str, sizeof(postcard_str), "POST: %02X %02X %s", postcard_codes[0], postcard_prev_codes[0], dell_diags);
} else {
if (!postcard_written[0])
snprintf(postcard_str, sizeof(postcard_str), "POST: -- --");
@@ -122,6 +140,9 @@ postcard_reset(void)
memset(postcard_codes, 0x00, POSTCARDS_NUM * sizeof(uint8_t));
memset(postcard_prev_codes, 0x00, POSTCARDS_NUM * sizeof(uint8_t));
memset(postcard_diags, 0x00, 5 * sizeof(char));
memset(postcard_prev_diags, 0x00, 5 * sizeof(char));
postcard_setui();
}
@@ -140,6 +161,35 @@ postcard_write(uint16_t port, uint8_t val, UNUSED(void *priv))
postcard_setui();
}
static int
postcard_cmp_diags(uint32_t val)
{
int ret = 0;
char *pv = (char *) &val;
for (int i = 0; i < 4; i++)
ret = ret || (pv[i] != postcard_diags[3 - i]);
return ret;
}
static void
postcard_writel(uint16_t port, uint32_t val, UNUSED(void *priv))
{
char *pv = (char *) &val;
if (postcard_written[1] && !postcard_cmp_diags(val))
return;
*(uint32_t *) postcard_prev_diags = *(uint32_t *) postcard_diags;
for (int i = 0; i < 4; i++)
postcard_diags[i] = pv[3 - i];
if (postcard_written[1] < 2)
postcard_written[1]++;
postcard_setui();
}
static void *
postcard_init(UNUSED(const device_t *info))
{
@@ -173,6 +223,10 @@ postcard_init(UNUSED(const device_t *info))
io_sethandler(postcard_port, postcard_ports_num,
NULL, NULL, NULL, postcard_write, NULL, NULL, NULL);
if (strstr(machines[machine].name, " Dell "))
io_sethandler(0x00e0, 0x0001,
NULL, NULL, NULL, NULL, NULL, postcard_writel, NULL);
return postcard_write;
}

View File

@@ -272,7 +272,10 @@ rz1000_init(const device_t *info)
dev->channels = ((info->local & 0x60000) >> 17) & 0x03;
device_add(&ide_pci_2ch_device);
if (dev->channels & 0x02)
device_add(&ide_pci_2ch_device);
else
device_add(&ide_pci_device);
if (info->local & 0x80000)
pci_add_card(PCI_ADD_NORMAL, rz1000_pci_read, rz1000_pci_write, dev, &dev->pci_slot);

View File

@@ -856,7 +856,7 @@ dma16_read(uint16_t addr, UNUSED(void *priv))
break;
}
dma_log("dma16_read(%08X) = %02X\n", port, ret);
dma_log("dma16_read(%08X) = %02X\n", addr, ret);
return ret;
}

View File

@@ -377,6 +377,15 @@ fdc_set_power_down(fdc_t *fdc, uint8_t power_down)
fdc->power_down = power_down;
}
void
fdc_toggle_flag(fdc_t *fdc, int flag, int on)
{
if (on)
fdc->flags |= flag;
else
fdc->flags &= ~flag;
}
void
fdc_update_max_track(fdc_t *fdc, int max_track)
{
@@ -469,9 +478,11 @@ fdc_update_drv2en(fdc_t *fdc, int drv2en)
void
fdc_update_rate(fdc_t *fdc, int drive)
{
if (((fdc->rwc[drive] == 1) || (fdc->rwc[drive] == 2)) && fdc->enh_mode)
if (((fdc->rwc[drive] == 1) || (fdc->rwc[drive] == 2)) &&
fdc->enh_mode && !(fdc->flags & FDC_FLAG_SMC661))
fdc->bit_rate = 500;
else if ((fdc->rwc[drive] == 3) && fdc->enh_mode)
else if ((fdc->rwc[drive] == 3) && fdc->enh_mode &&
!(fdc->flags & FDC_FLAG_SMC661))
fdc->bit_rate = 250;
else switch (fdc->rate) {
default:
@@ -535,7 +546,7 @@ fdc_get_bitcell_period(fdc_t *fdc)
static int
fdc_get_densel(fdc_t *fdc, int drive)
{
if (fdc->enh_mode) {
if (fdc->enh_mode && !(fdc->flags & FDC_FLAG_SMC661)) {
switch (fdc->rwc[drive]) {
case 1:
case 3:
@@ -770,8 +781,13 @@ fdc_write(uint16_t addr, uint8_t val, void *priv)
return;
case 3: /* TDR */
if (fdc->enh_mode) {
drive = real_drive(fdc, fdc->dor & 3);
fdc_update_rwc(fdc, drive, (val & 0x30) >> 4);
if (fdc->flags & FDC_FLAG_SMC661) {
fdc_set_swap(fdc, !!(val & 0x20));
fdc_update_densel_force(fdc, (val & 0x18) >> 3);
} else {
drive = real_drive(fdc, fdc->dor & 3);
fdc_update_rwc(fdc, drive, (val & 0x30) >> 4);
}
}
/* Bit 2: FIFO test mode (PS/55 5550-S,T only. Undocumented)
The Power-on Self Test of PS/55 writes and verifies 8 bytes of FIFO buffer through I/O 3F5h.
@@ -1391,6 +1407,8 @@ fdc_read(uint16_t addr, void *priv)
/* PS/55 POST throws an error and halt if ret = 1 or 2, somehow. */
} else if (!fdc->enh_mode)
ret = 0x20;
else if (fdc->flags & FDC_FLAG_SMC661)
ret = (fdc->densel_force << 3) | ((!!fdc->swap) << 5) | (fdc->media_id << 6);
else
ret = (fdc->rwc[drive] << 4) | (fdc->media_id << 6);
break;
@@ -1475,7 +1493,7 @@ fdc_read(uint16_t addr, void *priv)
fdc->step = 0;
break;
default:
ret = 0xFF;
ret = 0xff;
}
fdc_log("[%04X:%08X] Read FDC %04X %02X [%i:%02X]\n", CS, cpu_state.pc, addr, ret, drive, fdc->dor & (0x10 << drive));
return ret;
@@ -2226,9 +2244,13 @@ fdc_set_base(fdc_t *fdc, int base)
{
int super_io = (fdc->flags & FDC_FLAG_SUPERIO);
if (base == 0x0000) {
fdc->base_address = base;
return;
}
if (fdc->flags & FDC_FLAG_NSC) {
io_sethandler(base + 2, 0x0001, fdc_read, NULL, NULL, fdc_write, NULL, NULL, fdc);
io_sethandler(base + 4, 0x0002, fdc_read, NULL, NULL, fdc_write, NULL, NULL, fdc);
io_sethandler(base + 2, 0x0004, fdc_read, NULL, NULL, fdc_write, NULL, NULL, fdc);
io_sethandler(base + 7, 0x0001, fdc_read, NULL, NULL, fdc_write, NULL, NULL, fdc);
} else {
if ((fdc->flags & FDC_FLAG_AT) || (fdc->flags & FDC_FLAG_AMSTRAD)) {
@@ -2257,10 +2279,12 @@ fdc_remove(fdc_t *fdc)
{
int super_io = (fdc->flags & FDC_FLAG_SUPERIO);
if (fdc->base_address == 0x0000)
return;
fdc_log("FDC Removed (%04X)\n", fdc->base_address);
if (fdc->flags & FDC_FLAG_NSC) {
io_removehandler(fdc->base_address + 2, 0x0001, fdc_read, NULL, NULL, fdc_write, NULL, NULL, fdc);
io_removehandler(fdc->base_address + 4, 0x0002, fdc_read, NULL, NULL, fdc_write, NULL, NULL, fdc);
io_removehandler(fdc->base_address + 2, 0x0004, fdc_read, NULL, NULL, fdc_write, NULL, NULL, fdc);
io_removehandler(fdc->base_address + 7, 0x0001, fdc_read, NULL, NULL, fdc_write, NULL, NULL, fdc);
} else {
if ((fdc->flags & FDC_FLAG_AT) || (fdc->flags & FDC_FLAG_AMSTRAD)) {
@@ -2644,6 +2668,20 @@ const device_t fdc_at_actlow_device = {
.config = NULL
};
const device_t fdc_at_smc_661_device = {
.name = "PC/AT Floppy Drive Controller (SM(s)C FDC37C661/2)",
.internal_name = "fdc_at_smc",
.flags = 0,
.local = FDC_FLAG_AT | FDC_FLAG_SUPERIO | FDC_FLAG_SMC661,
.init = fdc_init,
.close = fdc_close,
.reset = fdc_reset,
.available = NULL,
.speed_changed = NULL,
.force_redraw = NULL,
.config = NULL
};
const device_t fdc_at_smc_device = {
.name = "PC/AT Floppy Drive Controller (SM(s)C FDC37Cxxx)",
.internal_name = "fdc_at_smc",

View File

@@ -38,26 +38,27 @@
#define FDC_QUATERNARY_IRQ 6
#define FDC_QUATERNARY_DMA 2
#define FDC_FLAG_PCJR 0x01 /* PCjr */
#define FDC_FLAG_DISKCHG_ACTLOW 0x02 /* Amstrad, PS/1, PS/2 ISA */
#define FDC_FLAG_AT 0x04 /* AT+, PS/x */
#define FDC_FLAG_PS2 0x08 /* PS/1, PS/2 ISA */
#define FDC_FLAG_PS2_MCA 0x10 /* PS/2 MCA */
#define FDC_FLAG_SUPERIO 0x20 /* Super I/O chips */
#define FDC_FLAG_START_RWC_1 0x40 /* W83877F, W83977F */
#define FDC_FLAG_MORE_TRACKS 0x80 /* W83877F, W83977F, PC87306, PC87309 */
#define FDC_FLAG_NSC 0x100 /* PC87306, PC87309 */
#define FDC_FLAG_TOSHIBA 0x200 /* T1000, T1200 */
#define FDC_FLAG_AMSTRAD 0x400 /* Non-AT Amstrad machines */
#define FDC_FLAG_UMC 0x800 /* UMC UM8398 */
#define FDC_FLAG_ALI 0x1000 /* ALi M512x / M1543C */
#define FDC_FLAG_NO_DSR_RESET 0x2000 /* Has no DSR reset */
#define FDC_FLAG_DENSEL_INVERT 0x4000 /* Invert DENSEL polarity */
#define FDC_FLAG_FINTR 0x8000 /* Raise FINTR on data command finish */
#define FDC_FLAG_NEC 0x10000 /* Is NEC upd765-compatible */
#define FDC_FLAG_SEC 0x20000 /* Is Secondary */
#define FDC_FLAG_TER 0x40000 /* Is Tertiary */
#define FDC_FLAG_QUA 0x80000 /* Is Quaternary */
#define FDC_FLAG_PCJR 0x01 /* PCjr */
#define FDC_FLAG_DISKCHG_ACTLOW 0x02 /* Amstrad, PS/1, PS/2 ISA */
#define FDC_FLAG_AT 0x04 /* AT+, PS/x */
#define FDC_FLAG_PS2 0x08 /* PS/1, PS/2 ISA */
#define FDC_FLAG_PS2_MCA 0x10 /* PS/2 MCA */
#define FDC_FLAG_SUPERIO 0x20 /* Super I/O chips */
#define FDC_FLAG_START_RWC_1 0x40 /* W83877F, W83977F */
#define FDC_FLAG_MORE_TRACKS 0x80 /* W83877F, W83977F, PC87306, PC87309 */
#define FDC_FLAG_NSC 0x100 /* PC87306, PC87309 */
#define FDC_FLAG_TOSHIBA 0x200 /* T1000, T1200 */
#define FDC_FLAG_AMSTRAD 0x400 /* Non-AT Amstrad machines */
#define FDC_FLAG_UMC 0x800 /* UMC UM8398 */
#define FDC_FLAG_ALI 0x1000 /* ALi M512x / M1543C */
#define FDC_FLAG_NO_DSR_RESET 0x2000 /* Has no DSR reset */
#define FDC_FLAG_DENSEL_INVERT 0x4000 /* Invert DENSEL polarity */
#define FDC_FLAG_FINTR 0x8000 /* Raise FINTR on data command finish */
#define FDC_FLAG_NEC 0x10000 /* Is NEC upd765-compatible */
#define FDC_FLAG_SEC 0x20000 /* Is Secondary */
#define FDC_FLAG_TER 0x40000 /* Is Tertiary */
#define FDC_FLAG_QUA 0x80000 /* Is Quaternary */
#define FDC_FLAG_SMC661 0x100000 /* SM(s)C FDC37C661 - different TDR enhanced mode */
typedef struct fdc_t {
uint8_t dor;
@@ -182,6 +183,7 @@ extern uint8_t fdc_get_densel_polarity(fdc_t *fdc);
extern void fdc_update_densel_force(fdc_t *fdc, int densel_force);
extern void fdc_update_drvrate(fdc_t *fdc, int drive, int drvrate);
extern void fdc_update_drv2en(fdc_t *fdc, int drv2en);
extern void fdc_toggle_flag(fdc_t *fdc, int flag, int on);
extern void fdc_noidam(fdc_t *fdc);
extern void fdc_nosector(fdc_t *fdc);
@@ -260,6 +262,7 @@ extern const device_t fdc_at_sec_device;
extern const device_t fdc_at_ter_device;
extern const device_t fdc_at_qua_device;
extern const device_t fdc_at_actlow_device;
extern const device_t fdc_at_smc_661_device;
extern const device_t fdc_at_smc_device;
extern const device_t fdc_at_ali_device;
extern const device_t fdc_at_winbond_device;

View File

@@ -289,7 +289,9 @@ extern uint8_t kbc_at_read_p(void *priv, uint8_t port, uint8_t mask);
extern void kbc_at_write_p(void *priv, uint8_t port, uint8_t mask, uint8_t val);
extern void kbc_at_set_fast_reset(uint8_t new_fast_reset);
extern void kbc_at_handler(int set, void *priv);
extern void kbc_at_port_handler(int num, int set, uint16_t port, void *priv);
extern void kbc_at_handler(int set, uint16_t port, void *priv);
extern void kbc_at_set_irq(int num, uint16_t irq, void *priv);
extern void kbc_at_dev_queue_reset(atkbc_dev_t *dev, uint8_t reset_main);
extern uint8_t kbc_at_dev_queue_pos(atkbc_dev_t *dev, uint8_t main);

View File

@@ -532,6 +532,8 @@ extern int machine_at_cs4031_init(const machine_t *);
extern int machine_at_pb410a_init(const machine_t *);
extern int machine_at_decpclpv_init(const machine_t *);
extern int machine_at_dell466np_init(const machine_t *);
extern int machine_at_acerv10_init(const machine_t *);
extern int machine_at_acera1g_init(const machine_t *);
@@ -670,6 +672,7 @@ extern int machine_at_tek932_init(const machine_t *);
extern int machine_at_acerv30_init(const machine_t *);
extern int machine_at_apollo_init(const machine_t *);
extern int machine_at_optiplex_gxl_init(const machine_t *);
extern int machine_at_zappa_init(const machine_t *);
extern int machine_at_powermatev_init(const machine_t *);
extern int machine_at_hawk_init(const machine_t *);
@@ -749,6 +752,7 @@ extern int machine_at_gw2kte_init(const machine_t *);
extern int machine_at_ma23c_init(const machine_t *);
extern int machine_at_nupro592_init(const machine_t *);
extern int machine_at_tx97_init(const machine_t *);
extern int machine_at_optiplex_gn_init(const machine_t *);
#ifdef USE_AN430TX
extern int machine_at_an430tx_init(const machine_t *);
#endif /* USE_AN430TX */
@@ -818,8 +822,8 @@ extern int machine_at_p65up5_cpknd_init(const machine_t *);
extern int machine_at_kn97_init(const machine_t *);
extern int machine_at_lx6_init(const machine_t *);
extern int machine_at_optiplex_gxa_init(const machine_t *);
extern int machine_at_spitfire_init(const machine_t *);
extern int machine_at_ma30d_init(const machine_t *);
extern int machine_at_p6i440e2_init(const machine_t *);

View File

@@ -92,13 +92,23 @@ extern const device_t pc87332_398_ide_device;
extern const device_t pc87332_398_ide_sec_device;
extern const device_t pc87332_398_ide_fdcon_device;
#define PCX7307_PC87307 0x00c0
#define PCX7307_PC97307 0x00cf
#define PC87309_PC87309 0x00e0
#define PCX730X_CHIP_ID 0x00ff
#define PCX730X_AMI 0x0200 /* AMI '5' Megakey KBC firmware. */
#define PCX730X_PHOENIX_42 0x0500 /* Phoenix Multikey/42 1.37 KBC firmware. */
#define PCX730X_PHOENIX_42I 0x0700 /* Phoenix Multikey/42i 4.16 KBC firmware. */
#define PCX730X_KBC 0x0f00
#define PCX730X_15C 0x2000
extern const device_t pc87307_device;
extern const device_t pc87307_15c_device;
extern const device_t pc87307_both_device;
extern const device_t pc97307_device;
extern const device_t pc87309_device;
extern const device_t pc87309_15c_device;
/* LG Prime */
extern const device_t prime3b_device;

View File

@@ -7,20 +7,23 @@
#define MASTER_CLOCK 7159090
typedef struct cms_t {
#ifdef SAASOUND_H_INCLUDED
SAASND saasound;
SAASND saasound2;
#else
void* saasound;
void* saasound2;
#endif
int addrs[2];
uint8_t regs[2][32];
uint16_t latch[2][6];
int freq[2][6];
float count[2][6];
int vol[2][6][2];
int stat[2][6];
uint16_t noise[2][2];
uint16_t noisefreq[2][2];
int noisecount[2][2];
int noisetype[2][2];
uint8_t latched_data;
int16_t buffer[WTBUFLEN * 2];
int16_t buffer2[WTBUFLEN * 2];
int16_t buffer[SOUNDBUFLEN * 2];
int pos, pos2;
int pos;
} cms_t;
extern void cms_update(cms_t *cms);

View File

@@ -365,6 +365,7 @@ extern const device_t gd5428_boca_isa_device;
extern const device_t gd5428_mca_device;
extern const device_t gd5426_mca_device;
extern const device_t gd5428_onboard_device;
extern const device_t gd5428_onboard_vlb_device;
extern const device_t gd5429_isa_device;
extern const device_t gd5429_vlb_device;
extern const device_t gd5430_diamond_speedstar_pro_se_a8_vlb_device;

View File

@@ -445,10 +445,10 @@ outb(uint16_t port, uint8_t val)
}
}
if (!found) {
if (!found || (port == 0x84)) {
cycles -= io_delay;
#ifdef USE_DYNAREC
if (cpu_use_dynarec && ((port == 0xeb) || (port == 0xed)))
if (cpu_use_dynarec && ((port == 0x84) || (port == 0xeb) || (port == 0xed)))
update_tsc();
#endif
}

View File

@@ -229,9 +229,9 @@ void
lpt_port_setup(int i, uint16_t port)
{
if (lpt_ports[i].enabled) {
if (lpt_ports[i].addr != 0xffff)
if ((lpt_ports[i].addr != 0xffff) && (lpt_ports[i].addr != 0x0000))
io_removehandler(lpt_ports[i].addr, 0x0003, lpt_read, NULL, NULL, lpt_write, NULL, NULL, &lpt_ports[i]);
if (port != 0xffff)
if ((port != 0xffff) && (port != 0x0000))
io_sethandler(port, 0x0003, lpt_read, NULL, NULL, lpt_write, NULL, NULL, &lpt_ports[i]);
lpt_ports[i].addr = port;
} else

View File

@@ -396,7 +396,7 @@ machine_at_pb410a_init(const machine_t *model)
device_add(&phoenix_486_jumper_device);
if (gfxcard[0] == VID_INTERNAL)
device_add(&ht216_32_pb410a_device);
device_add(machine_get_vid_device(machine));
return ret;
}
@@ -545,16 +545,47 @@ machine_at_decpclpv_init(const machine_t *model)
device_add(&sis_85c461_device);
if (gfxcard[0] == VID_INTERNAL)
device_add(&s3_86c805_onboard_vlb_device);
device_add(machine_get_vid_device(machine));
device_add(&keyboard_ps2_phoenix_pci_device);
/* TODO: Phoenix MultiKey KBC */
device_add(&keyboard_ps2_ami_pci_device);
device_add(&ide_isa_2ch_device);
device_add(&fdc37c663_ide_device);
return ret;
}
int
machine_at_dell466np_init(const machine_t *model)
{
int ret;
ret = bios_load_linear("roms/machines/dell466np/466np.bin",
0x000c0000, 262144, 0);
if (bios_only || !ret)
return ret;
machine_at_common_init(model);
device_add(&sis_85c461_device);
if (gfxcard[0] == VID_INTERNAL)
device_add(machine_get_vid_device(machine));
else {
for (uint16_t i = 0; i < 32768; i++)
rom[i] = mem_readb_phys(0x000c0000 + i);
}
mem_mapping_set_addr(&bios_mapping, 0x0c0000, 0x40000);
mem_mapping_set_exec(&bios_mapping, rom);
device_add(&keyboard_ps2_phoenix_pci_device);
device_add(&ide_isa_device);
device_add(&fdc37c661_ide_device);
return ret;
}
static void
machine_at_ali1429_common_init(const machine_t *model, int is_green)
{
@@ -1696,9 +1727,6 @@ machine_at_sb486pv_init(const machine_t *model)
device_context_restore();
machine_at_common_init(model);
// machine_at_common_init_ex(model, 2);
// device_add(&amstrad_megapc_nvr_device);
device_add(&ide_pci_device);
pci_init(PCI_CONFIG_TYPE_2);

View File

@@ -150,6 +150,39 @@ machine_at_lx6_init(const machine_t *model)
return ret;
}
int
machine_at_optiplex_gxa_init(const machine_t *model)
{
int ret;
ret = bios_load_linear("roms/machines/optiplex_gxa/DELL.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, 1, 2, 3, 4);
pci_register_slot(0x0D, PCI_CARD_NORMAL, 2, 1, 3, 4);
pci_register_slot(0x0E, PCI_CARD_NORMAL, 3, 4, 2, 1);
pci_register_slot(0x11, PCI_CARD_NETWORK, 4, 0, 0, 0);
pci_register_slot(0x07, PCI_CARD_SOUTHBRIDGE, 0, 0, 0, 4);
pci_register_slot(0x01, PCI_CARD_AGPBRIDGE, 1, 2, 3, 4);
if (sound_card_current[0] == SOUND_INTERNAL)
device_add(machine_get_snd_device(machine));
device_add(&i440lx_device);
device_add(&piix4_device);
device_add_params(&pc87307_device, (void *) (PCX730X_PHOENIX_42 | PCX7307_PC87307));
device_add(&intel_flash_bxt_device);
spd_register(SPD_TYPE_SDRAM, 0x7, 256);
return ret;
}
int
machine_at_spitfire_init(const machine_t *model)
{
@@ -584,8 +617,7 @@ machine_at_s1846_init(const machine_t *model)
pci_register_slot(0x01, PCI_CARD_AGPBRIDGE, 1, 2, 3, 4);
device_add(&i440bx_device);
device_add(&piix4e_device);
device_add(&pc87309_device);
device_add(&keyboard_ps2_ami_pci_device);
device_add_params(&pc87309_device, (void *) (PCX730X_AMI | PC87309_PC87309));
device_add(&intel_flash_bxt_device);
spd_register(SPD_TYPE_SDRAM, 0x7, 256);

View File

@@ -140,8 +140,7 @@ machine_at_fw6400gx_init(const machine_t *model)
device_add(&i440gx_device);
device_add(&piix4e_device);
device_add(&keyboard_ps2_ami_pci_device);
device_add(&pc87309_15c_device);
device_add_params(&pc87309_device, (void *) (PCX730X_15C | PCX730X_AMI | PC87309_PC87309));
device_add(ics9xxx_get(ICS9250_08));
device_add(&sst_flash_29ee020_device);
spd_register(SPD_TYPE_SDRAM, 0xF, 512);

View File

@@ -40,6 +40,7 @@
#include <86box/sio.h>
#include <86box/video.h>
#include <86box/machine.h>
#include <86box/sound.h>
int
machine_at_plato_init(const machine_t *model)
@@ -236,6 +237,42 @@ machine_at_apollo_init(const machine_t *model)
return ret;
}
int
machine_at_optiplex_gxl_init(const machine_t *model)
{
int ret;
ret = bios_load_linear("roms/machines/optiplex_gxl/DELL.ROM",
0x000e0000, 131072, 0);
if (bios_only || !ret)
return ret;
machine_at_common_init_ex(model, 2);
device_add(&amstrad_megapc_nvr_device);
pci_init(PCI_CONFIG_TYPE_1);
pci_register_slot(0x00, PCI_CARD_NORTHBRIDGE, 1, 2, 3, 4);
pci_register_slot(0x0C, PCI_CARD_NORMAL, 1, 2, 3, 4);
pci_register_slot(0x0D, PCI_CARD_NORMAL, 3, 4, 2, 1);
pci_register_slot(0x10, PCI_CARD_VIDEO, 0, 0, 0, 0);
pci_register_slot(0x07, PCI_CARD_SOUTHBRIDGE, 0, 0, 0, 0);
if (gfxcard[0] == VID_INTERNAL)
device_add(machine_get_vid_device(machine));
if (sound_card_current[0] == SOUND_INTERNAL)
machine_snd = device_add(machine_get_snd_device(machine));
device_add(&keyboard_ps2_phoenix_pci_device);
device_add(&i430fx_device);
device_add(&piix_device);
device_add(&pc87332_device);
device_add(&intel_flash_bxt_device);
return ret;
}
static void
machine_at_zappa_gpio_init(void)
{

View File

@@ -969,6 +969,42 @@ machine_at_tx97_init(const machine_t *model)
return ret;
}
int
machine_at_optiplex_gn_init(const machine_t *model)
{
int ret;
ret = bios_load_linear("roms/machines/optiplex_gn/DELL.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, 1, 2, 3, 4);
pci_register_slot(0x0E, PCI_CARD_NORMAL, 3, 4, 2, 1);
pci_register_slot(0x0D, PCI_CARD_NORMAL, 2, 1, 3, 4);
pci_register_slot(0x10, PCI_CARD_VIDEO, 4, 0, 0, 0); /* Trio64V2/GX, temporarily Trio64V2/DX is given */
pci_register_slot(0x11, PCI_CARD_NETWORK, 4, 0, 0, 0); /* 3C905, not yet emulated */
pci_register_slot(0x07, PCI_CARD_SOUTHBRIDGE, 0, 0, 0, 4);
if (gfxcard[0] == VID_INTERNAL)
device_add(machine_get_vid_device(machine));
if ((sound_card_current[0] == SOUND_INTERNAL) && machine_get_snd_device(machine)->available())
machine_snd = device_add(machine_get_snd_device(machine));
device_add(&i430tx_device);
device_add(&piix4_device);
device_add_params(&pc87307_device, (void *) (PCX730X_PHOENIX_42 | PCX7307_PC87307));
device_add(&intel_flash_bxt_device);
spd_register(SPD_TYPE_SDRAM, 0x3, 128);
return ret;
}
#ifdef USE_AN430TX
int
machine_at_an430tx_init(const machine_t *model)
@@ -1006,8 +1042,7 @@ machine_at_an430tx_init(const machine_t *model)
pci_register_slot(0x10, PCI_CARD_NORMAL, 4, 1, 2, 3);
device_add(&i430tx_device);
device_add(&piix4_device);
device_add(&keyboard_ps2_ami_pci_device);
device_add(&pc87307_both_device);
device_add_params(&pc87307_device, (void *) (PCX730X_PHOENIX_42I | PCX7307_PC97307));
device_add(&intel_flash_bxt_ami_device);
spd_register(SPD_TYPE_SDRAM, 0x3, 128);

View File

@@ -706,17 +706,59 @@ machine_at_gw2kma_init(const machine_t *model)
return ret;
}
static const device_config_t ap5s_config[] = {
// clang-format off
{
.name = "bios",
.description = "BIOS Version",
.type = CONFIG_BIOS,
.default_string = "ap5s",
.default_int = 0,
.file_filter = "",
.spinner = { 0 },
.bios = {
{ .name = "04/22/96 1.20 4.50PG", .internal_name = "ap5s_450pg", .bios_type = BIOS_NORMAL,
.files_no = 1, .local = 0, .size = 131072, .files = { "roms/machines/ap5s/ap5s120.bin", "" } },
{ .name = "11/13/96 1.50 4.51PG", .internal_name = "ap5s", .bios_type = BIOS_NORMAL,
.files_no = 1, .local = 0, .size = 131072, .files = { "roms/machines/ap5s/AP5S150.BIN", "" } },
{ .name = "06/25/97 1.60 4.51PG", .internal_name = "ap5s_latest", .bios_type = BIOS_NORMAL,
.files_no = 1, .local = 0, .size = 131072, .files = { "roms/machines/ap5s/ap5s160.bin", "" } },
{ .files_no = 0 }
},
},
{ .name = "", .description = "", .type = CONFIG_END }
// clang-format on
};
const device_t ap5s_device = {
.name = "AOpen AP5S",
.internal_name = "ap5s_device",
.flags = 0,
.local = 0,
.init = NULL,
.close = NULL,
.reset = NULL,
.available = NULL,
.speed_changed = NULL,
.force_redraw = NULL,
.config = ap5s_config
};
int
machine_at_ap5s_init(const machine_t *model)
{
int ret;
int ret = 0;
const char* fn;
ret = bios_load_linear("roms/machines/ap5s/AP5S150.BIN",
0x000e0000, 131072, 0);
if (bios_only || !ret)
/* No ROMs available */
if (!device_available(model->device))
return ret;
device_context(model->device);
fn = device_get_bios_file(machine_get_device(machine), device_get_config_bios("bios"), 0);
ret = bios_load_linear(fn, 0x000e0000, 131072, 0);
device_context_restore();
machine_at_common_init_ex(model, 2);
pci_init(PCI_CONFIG_TYPE_1 | FLAG_TRC_CONTROLS_CPURST);

View File

@@ -250,8 +250,7 @@ machine_at_vs440fx_init(const machine_t *model)
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_params(&pc87307_device, (void *) (PCX730X_AMI | PCX7307_PC87307));
device_add(&intel_flash_bxt_ami_device);
@@ -287,8 +286,7 @@ machine_at_gw2kvenus_init(const machine_t *model)
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_params(&pc87307_device, (void *) (PCX730X_AMI | PCX7307_PC87307));
device_add(&intel_flash_bxt_ami_device);
@@ -324,8 +322,7 @@ machine_at_ap440fx_init(const machine_t *model)
pci_register_slot(0x07, PCI_CARD_SOUTHBRIDGE, 0, 0, 0, 4);
device_add(&i440fx_device);
device_add(&piix3_device);
device_add(&keyboard_ps2_ami_pci_device);
device_add(&pc87307_device);
device_add_params(&pc87307_device, (void *) (PCX730X_AMI | PCX7307_PC87307));
device_add(&intel_flash_bxt_ami_device);
if (sound_card_current[0] == SOUND_INTERNAL)

View File

@@ -70,6 +70,7 @@ extern const device_t jukopc_device;
extern const device_t vendex_device;
extern const device_t c5sbm2_device;
extern const device_t sb486pv_device;
extern const device_t ap5s_device;
const machine_filter_t machine_types[] = {
{ "None", MACHINE_TYPE_NONE },
@@ -6646,7 +6647,7 @@ const machine_t machines[] = {
.device = NULL,
.fdc_device = NULL,
.sio_device = NULL,
.vid_device = NULL,
.vid_device = &ht216_32_pb410a_device,
.snd_device = NULL,
.net_device = NULL
},
@@ -6934,6 +6935,46 @@ const machine_t machines[] = {
.snd_device = NULL,
.net_device = NULL
},
/* Uses a ???? KBC. */
{
.name = "[SiS 461] Dell 466/NP",
.internal_name = "dell466np",
.type = MACHINE_TYPE_486_S2,
.chipset = MACHINE_CHIPSET_SIS_461,
.init = machine_at_dell466np_init,
.p1_handler = NULL,
.gpio_handler = NULL,
.available_flag = MACHINE_AVAILABLE,
.gpio_acpi_handler = NULL,
.cpu = {
.package = CPU_PKG_SOCKET3,
.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_PS2,
.flags = MACHINE_IDE | MACHINE_VIDEO | MACHINE_APM,
.ram = {
.min = 1024,
.max = 32768,
.step = 1024
},
.nvrmask = 127,
.kbc_device = NULL,
.kbc_p1 = 0xff,
.gpio = 0xffffffff,
.gpio_acpi = 0xffffffff,
.device = NULL,
.fdc_device = NULL,
.sio_device = NULL,
.vid_device = &gd5428_onboard_vlb_device,
.snd_device = NULL,
.net_device = NULL
},
/* The BIOS does not send any non-standard keyboard controller commands and wants
a PS/2 mouse, so it's an IBM PS/2 KBC (Type 1) firmware. */
{
@@ -7816,10 +7857,10 @@ const machine_t machines[] = {
.kbc_p1 = 0xff,
.gpio = 0xffffffff,
.gpio_acpi = 0xffffffff,
.device = &tgui9440_onboard_pci_device,
.device = NULL,
.fdc_device = NULL,
.sio_device = NULL,
.vid_device = NULL,
.vid_device = &tgui9440_onboard_pci_device,
.snd_device = NULL,
.net_device = NULL
},
@@ -10512,6 +10553,46 @@ const machine_t machines[] = {
.snd_device = NULL,
.net_device = NULL
},
/* Has a National Semiconductor PC87332VLJ Super I/O with AMIKey 'F' KBC firmware. */
{
.name = "[i430FX] Dell OptiPlex GXL/GXM",
.internal_name = "optiplex_gxl",
.type = MACHINE_TYPE_SOCKET5,
.chipset = MACHINE_CHIPSET_INTEL_430FX,
.init = machine_at_optiplex_gxl_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_Cx6x86),
.min_bus = 60000000,
.max_bus = 66666667,
.min_voltage = 3380,
.max_voltage = 3520,
.min_multi = 1.5,
.max_multi = 3.0
},
.bus_flags = MACHINE_PS2_PCI,
.flags = MACHINE_IDE_DUAL | MACHINE_VIDEO | MACHINE_SOUND | MACHINE_APM, /* Video: S3 Trio64V+ (86C765), Sound: Creative ViBRA 16S (CT2504), Network: 3Com ETHERLINK III (3C509B) */
.ram = {
.min = 8192,
.max = 131072,
.step = 8192
},
.nvrmask = 127,
.kbc_device = NULL,
.kbc_p1 = 0xff,
.gpio = 0xffffffff,
.gpio_acpi = 0xffffffff,
.device = NULL,
.fdc_device = NULL,
.sio_device = NULL,
.vid_device = &s3_phoenix_trio64vplus_onboard_pci_device,
.snd_device = &sb_vibra16s_onboard_device,
.net_device = NULL /* not yet emulated */
},
/* According to tests from real hardware: This has AMI MegaKey KBC firmware on the
PC87306 Super I/O chip, command 0xA1 returns '5'.
Command 0xA0 copyright string: (C)1994 AMI . */
@@ -11780,7 +11861,7 @@ const machine_t machines[] = {
.kbc_p1 = 0xff,
.gpio = 0xffffffff,
.gpio_acpi = 0xffffffff,
.device = NULL,
.device = &ap5s_device,
.fdc_device = NULL,
.sio_device = NULL,
.vid_device = NULL,
@@ -13062,6 +13143,56 @@ const machine_t machines[] = {
.snd_device = NULL,
.net_device = NULL
},
/*
According to Dell specifications, it can have either National Semiconductor
PC87307 or PC87309 Super I/O. All known instances have the former, although
other similar Dells of the era have pinouts for accompanying either so this
likely also does.
The KBC is either an AMI '5' MegaKey, Phoenix MultiKey/42 1.37, or Phoenix
MultiKey/42i 4.16.
*/
{
.name = "[i430TX] Dell OptiPlex GN+",
.internal_name = "optiplex_gn",
.type = MACHINE_TYPE_SOCKET7,
.chipset = MACHINE_CHIPSET_INTEL_430TX,
.init = machine_at_optiplex_gn_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 = 2500,
.max_voltage = 3520,
.min_multi = 1.5,
.max_multi = 3.5
},
.bus_flags = MACHINE_PS2_PCI,
/* Video: S3 86C785 (Trio64V2/GX), ethernet: 3C905. */
.flags = MACHINE_IDE_DUAL | MACHINE_APM | MACHINE_ACPI | MACHINE_VIDEO | MACHINE_SOUND,
.ram = {
.min = 8192,
.max = 262144,
.step = 8192
},
.nvrmask = 255,
.kbc_device = NULL,
.kbc_p1 = 0xff,
.gpio = 0xffffffff,
.gpio_acpi = 0xffffffff,
.device = NULL,
.fdc_device = NULL,
.sio_device = NULL,
/* Stop-gap measure until the Trio64V2/GX is emulated, as both use the same VBIOS. */
.vid_device = &s3_trio64v2_dx_onboard_pci_device,
.snd_device = &sb_vibra16xv_onboard_device,
.net_device = NULL
},
/* [TEST] Has AMI Megakey '5' KBC firmware on the SM(S)C FDC37C67x Super I/O chip. */
{
.name = "[i430TX] Gateway E-1000",
@@ -15039,6 +15170,47 @@ const machine_t machines[] = {
.snd_device = NULL,
.net_device = NULL
},
/* Has a National Semiconductor PC87307 Super I/O with on-chip KBC, which has one of these
firmwares: AMI '5' MegaKey, Phoenix MultiKey/42 1.37, or Phoenix MultiKey/42i 4.16. */
{
.name = "[i440LX] Dell OptiPlex GXa",
.internal_name = "optiplex_gxa",
.type = MACHINE_TYPE_SLOT1,
.chipset = MACHINE_CHIPSET_INTEL_440LX,
.init = machine_at_optiplex_gxa_init,
.p1_handler = NULL,
.gpio_handler = NULL,
.available_flag = MACHINE_AVAILABLE,
.gpio_acpi_handler = NULL,
.cpu = {
.package = CPU_PKG_SLOT1,
.block = CPU_BLOCK(CPU_PENTIUMPRO, CPU_CYRIX3S),
.min_bus = 66666667,
.max_bus = 66666667,
.min_voltage = 1800,
.max_voltage = 3500,
.min_multi = 1.5,
.max_multi = 5.0
},
.bus_flags = MACHINE_PS2_PCI | MACHINE_BUS_USB,
.flags = MACHINE_IDE_DUAL | MACHINE_SOUND | MACHINE_APM | MACHINE_ACPI | MACHINE_USB, /* Video: ATi 3D Rage Pro, Network: 3Com 3C905, Sound: Crystal CS4236B */
.ram = {
.min = 8192,
.max = 786432,
.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, /* not yet emulated */
.snd_device = &cs4236b_device,
.net_device = NULL /* not yet emulated */
},
/* Has a SM(S)C FDC37C935 Super I/O chip with on-chip KBC with Phoenix
MultiKey/42 (version 1.38) KBC firmware. */
{

View File

@@ -2549,6 +2549,12 @@ rtl8139_io_writeb(uint32_t addr, uint8_t val, void *priv)
break;
case RxConfig:
rtl8139_log("RxConfig write(b) val=0x%02x\n", val);
rtl8139_RxConfig_write(s,
(rtl8139_RxConfig_read(s) & 0xFFFFFF00) | val);
break;
default:
rtl8139_log("not implemented write(b) addr=0x%x val=0x%02x\n", addr, val);
break;

View File

@@ -1107,9 +1107,10 @@ nvr_at_init(const device_t *info)
case 1: /* standard AT */
case 5: /* AMI WinBIOS 1994 */
case 6: /* AMI BIOS 1995 */
if ((info->local & 0x1f) == 0x11)
if ((info->local & 0x1f) == 0x11) {
local->flags |= FLAG_PIIX4;
else {
local->def = 0x00;
} else {
local->def = 0x00;
if ((info->local & 0x1f) == 0x15)
local->flags |= FLAG_AMI_1994_HACK;

View File

@@ -1503,6 +1503,7 @@ OpenGLRenderer::render()
/* loop through each pass */
for (i = 0; i < shader->num_passes; ++i) {
bool resetFiltering = false;
struct shader_pass *pass = &shader->passes[i];
memcpy(pass->state.input_size, input->state.output_size, 2 * sizeof(GLfloat));
@@ -1524,8 +1525,14 @@ OpenGLRenderer::render()
glw.glBindFramebuffer(GL_FRAMEBUFFER, pass->fbo.id);
glw.glViewport(0, 0, pass->state.output_size[0], pass->state.output_size[1]);
} else
} else {
resetFiltering = true;
glw.glBindTexture(GL_TEXTURE_2D, input->fbo.texture.id);
glw.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, video_filter_method ? GL_LINEAR : GL_NEAREST);
glw.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, video_filter_method ? GL_LINEAR : GL_NEAREST);
glw.glBindTexture(GL_TEXTURE_2D, 0);
glw.glViewport(window_rect.x, window_rect.y, window_rect.w, window_rect.h);
}
glw.glClearColor(0, 0, 0, 1);
glw.glClear(GL_COLOR_BUFFER_BIT);
@@ -1570,6 +1577,13 @@ OpenGLRenderer::render()
glw.glBindTexture(GL_TEXTURE_2D, 0);
}
if (resetFiltering) {
glw.glBindTexture(GL_TEXTURE_2D, input->fbo.texture.id);
glw.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, input->fbo.texture.min_filter);
glw.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, input->fbo.texture.mag_filter);
glw.glBindTexture(GL_TEXTURE_2D, 0);
}
input = pass;
}
@@ -1637,6 +1651,11 @@ OpenGLRenderer::render()
pass->state.output_texture_size[j] = next_pow2(pass->state.output_size[j]);
}
glw.glBindTexture(GL_TEXTURE_2D, input->fbo.texture.id);
glw.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, video_filter_method ? GL_LINEAR : GL_NEAREST);
glw.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, video_filter_method ? GL_LINEAR : GL_NEAREST);
glw.glBindTexture(GL_TEXTURE_2D, 0);
glw.glViewport(window_rect.x, window_rect.y, window_rect.w, window_rect.h);
glw.glClearColor(0, 0, 0, 1);

View File

@@ -61,6 +61,11 @@ struct mouseinputdata {
static mouseinputdata mousedata;
extern MainWindow *main_window;
#ifdef Q_OS_WINDOWS
HWND rw_hwnd;
#endif
RendererStack::RendererStack(QWidget *parent, int monitor_index)
: QStackedWidget(parent)
, ui(new Ui::RendererStack)
@@ -251,7 +256,9 @@ RendererStack::mouseMoveEvent(QMouseEvent *event)
leaveEvent((QEvent *) event);
ignoreNextMouseEvent--;
}
#if !defined _WIN32
QCursor::setPos(mapToGlobal(QPoint(width() / 2, height() / 2)));
#endif
ignoreNextMouseEvent = 2;
oldPos = event->pos();
#endif
@@ -405,8 +412,15 @@ RendererStack::createRenderer(Renderer renderer)
}
#endif
}
if (current.get() == nullptr)
if (current.get() == nullptr) {
#ifdef Q_OS_WINDOWS
rw_hwnd = NULL;
#endif
return;
}
#ifdef Q_OS_WINDOWS
rw_hwnd = (HWND) this->winId();
#endif
current->setFocusPolicy(Qt::NoFocus);
current->setFocusProxy(this);
current->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
@@ -649,6 +663,14 @@ RendererStack::setFocusRenderer()
void
RendererStack::onResize(int width, int height)
{
#ifdef Q_OS_WINDOWS
if (mouse_capture) {
RECT rect;
if (GetWindowRect((HWND)this->winId(), &rect)) {
ClipCursor(&rect);
}
}
#endif
if (rendererWindow) {
rendererWindow->r_monitor_index = m_monitor_index;
rendererWindow->onResize(width, height);

View File

@@ -23,6 +23,7 @@
#include <QMessageBox>
#include <QStatusBar>
#include <QApplication>
#include "qt_mainwindow.hpp"
#include "qt_machinestatus.hpp"
@@ -122,6 +123,10 @@ plat_resize(int w, int h, int monitor_index)
main_window->resizeContents(w, h);
}
#if defined _WIN32
extern HWND rw_hwnd;
#endif
void
plat_mouse_capture(int on)
{
@@ -129,6 +134,26 @@ plat_mouse_capture(int on)
return;
main_window->setMouseCapture(on > 0 ? true : false);
#if defined _WIN32
if (on) {
QCursor cursor(Qt::BlankCursor);
QApplication::setOverrideCursor(cursor);
QApplication::changeOverrideCursor(cursor);
RECT rect;
GetWindowRect(rw_hwnd, &rect);
ClipCursor(&rect);
} else {
ClipCursor(NULL);
QApplication::restoreOverrideCursor();
}
#endif
}
int

View File

@@ -395,7 +395,7 @@ WindowsRawInputFilter::mouse_handle(PRAWINPUT raw)
mouse_scale(delta_x, delta_y);
HWND wnd = (HWND)window->winId();
/* HWND wnd = (HWND)window->winId();
RECT rect;
@@ -404,5 +404,5 @@ WindowsRawInputFilter::mouse_handle(PRAWINPUT raw)
int left = rect.left + (rect.right - rect.left) / 2;
int top = rect.top + (rect.bottom - rect.top) / 2;
SetCursorPos(left, top);
SetCursorPos(left, top); */
}

View File

@@ -314,7 +314,10 @@ fdc37c6xx_init(const device_t *info)
{
fdc37c6xx_t *dev = (fdc37c6xx_t *) calloc(1, sizeof(fdc37c6xx_t));
dev->fdc = device_add(&fdc_at_smc_device);
if (dev->chip_id >= 0x63)
dev->fdc = device_add(&fdc_at_smc_device);
else
dev->fdc = device_add(&fdc_at_smc_661_device);
dev->chip_id = info->local & 0xff;
dev->has_ide = (info->local >> 8) & 0xff;

View File

@@ -894,7 +894,7 @@ fdc37c93x_kbc_handler(fdc37c93x_t *dev)
dev->kbc_base = local_enable ? 0x0060 : 0x0000;
if (dev->kbc_base != old_base)
kbc_at_handler(local_enable, dev->kbc);
kbc_at_handler(local_enable, dev->kbc_base, dev->kbc);
}
static void

File diff suppressed because it is too large Load Diff

View File

@@ -8,56 +8,97 @@
*
* Emulation of the NatSemi PC87309 Super I/O chip.
*
*
*
* Authors: Miran Grca, <mgrca8@gmail.com>
*
* Copyright 2020 Miran Grca.
* Copyright 2020-2025 Miran Grca.
*/
#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/io.h>
#include <86box/timer.h>
#include <86box/device.h>
#include <86box/lpt.h>
#include <86box/machine.h>
#include <86box/mem.h>
#include <86box/nvr.h>
#include <86box/pci.h>
#include <86box/rom.h>
#include <86box/serial.h>
#include <86box/hdc.h>
#include <86box/hdc_ide.h>
#include <86box/fdd.h>
#include <86box/fdc.h>
#include <86box/keyboard.h>
#include <86box/sio.h>
#include <86box/plat_fallthrough.h>
#include "cpu.h"
typedef struct pc87309_t {
uint8_t id;
uint8_t baddr;
uint8_t pm_idx;
uint8_t regs[48];
uint8_t ld_regs[256][208];
uint8_t pm[8];
uint16_t superio_base;
uint16_t pm_base;
int cur_reg;
void *kbc;
fdc_t *fdc;
serial_t *uart[2];
} pc87309_t;
static void fdc_handler(pc87309_t *dev);
static void lpt1_handler(pc87309_t *dev);
static void serial_handler(pc87309_t *dev, int uart);
enum {
LD_FDC = 0,
LD_LPT,
LD_UART2,
LD_UART1,
LD_PM,
LD_KBD,
LD_MOUSE
} pc87309_ld_t;
#define LD_MIN LD_FDC
#define LD_MAX LD_MOUSE
static void fdc_handler(pc87309_t *dev);
static void lpt1_handler(pc87309_t *dev);
static void serial_handler(pc87309_t *dev, int uart);
static void kbc_handler(pc87309_t *dev);
static void pc87309_write(uint16_t port, uint8_t val, void *priv);
static uint8_t pc87309_read(uint16_t port, void *priv);
#ifdef ENABLE_PC87309_LOG
int pc87309_do_log = ENABLE_PC87309_LOG;
static void
pc87309_log(const char *fmt, ...)
{
va_list ap;
if (pc87309_do_log) {
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
# define pc87309_log(fmt, ...)
#endif
static void
pc87309_pm_write(uint16_t port, uint8_t val, void *priv)
{
pc87309_t *dev = (pc87309_t *) priv;
if (port & 1) {
if (port & 1)
dev->pm[dev->pm_idx] = val;
else {
dev->pm_idx = val & 0x07;
switch (dev->pm_idx) {
case 0x00:
@@ -70,8 +111,7 @@ pc87309_pm_write(uint16_t port, uint8_t val, void *priv)
default:
break;
}
} else
dev->pm_idx = val & 0x07;
}
}
uint8_t
@@ -104,20 +144,49 @@ pc87309_pm_init(pc87309_t *dev, uint16_t addr)
pc87309_pm_read, NULL, NULL, pc87309_pm_write, NULL, NULL, dev);
}
static void
kbc_handler(pc87309_t *dev)
{
uint8_t active = (dev->ld_regs[LD_KBD][0x00] & 0x01) &&
(dev->pm[0x00] & 0x01);
uint8_t active_2 = dev->ld_regs[LD_MOUSE][0x00] & 0x01;
uint8_t irq = (dev->ld_regs[LD_KBD][0x40] & 0x0f);
uint8_t irq_2 = (dev->ld_regs[LD_MOUSE][0x40] & 0x0f);
uint16_t addr = (dev->ld_regs[LD_KBD][0x30] << 8) |
dev->ld_regs[LD_KBD][0x31];
uint16_t addr_2 = (dev->ld_regs[LD_KBD][0x32] << 8) |
dev->ld_regs[LD_KBD][0x33];
pc87309_log("%02X, %02X, %02X, %02X, %04X, %04X\n",
active, active_2, irq, irq_2, addr, addr_2);
if (addr <= 0xfff8) {
pc87309_log("Enabling KBC #1 on %04X...\n", addr);
kbc_at_port_handler(0, active, addr, dev->kbc);
}
if (addr_2 <= 0xfff8) {
pc87309_log("Enabling KBC #2 on %04X...\n", addr_2);
kbc_at_port_handler(1, active, addr_2, dev->kbc);
}
kbc_at_set_irq(0, active ? irq : 0xffff, dev->kbc);
kbc_at_set_irq(1, (active && active_2) ? irq_2 : 0xffff, dev->kbc);
}
static void
fdc_handler(pc87309_t *dev)
{
uint8_t irq;
uint8_t active;
uint16_t addr;
fdc_remove(dev->fdc);
active = (dev->ld_regs[0x00][0x00] & 0x01) && (dev->pm[0x00] & 0x08);
addr = ((dev->ld_regs[0x00][0x30] << 8) | dev->ld_regs[0x00][0x31]) - 0x0002;
irq = (dev->ld_regs[0x00][0x40] & 0x0f);
uint8_t active = (dev->ld_regs[LD_FDC][0x00] & 0x01) &&
(dev->pm[0x00] & 0x08);
uint8_t irq = (dev->ld_regs[LD_FDC][0x40] & 0x0f);
uint16_t addr = ((dev->ld_regs[LD_FDC][0x30] << 8) |
dev->ld_regs[LD_FDC][0x31]) & 0xfff8;
if (active) {
if (active && (addr <= 0xfff8)) {
pc87309_log("Enabling FDC on %04X, IRQ %i...\n", addr, irq);
fdc_set_base(dev->fdc, addr);
fdc_set_irq(dev->fdc, irq);
}
@@ -126,221 +195,351 @@ fdc_handler(pc87309_t *dev)
static void
lpt1_handler(pc87309_t *dev)
{
uint8_t irq;
uint8_t active;
uint16_t addr;
uint8_t active = (dev->ld_regs[LD_LPT][0x00] & 0x01) &&
(dev->pm[0x00] & 0x10);
uint8_t irq = (dev->ld_regs[LD_LPT][0x40] & 0x0f);
uint16_t addr = (dev->ld_regs[LD_LPT][0x30] << 8) |
dev->ld_regs[LD_LPT][0x31];
lpt1_remove();
active = (dev->ld_regs[0x01][0x00] & 0x01) && (dev->pm[0x00] & 0x10);
addr = (dev->ld_regs[0x01][0x30] << 8) | dev->ld_regs[0x01][0x31];
irq = (dev->ld_regs[0x01][0x40] & 0x0f);
if (active) {
if (active && (addr <= 0xfffc)) {
pc87309_log("Enabling LPT1 on %04X...\n", addr);
lpt1_setup(addr);
lpt1_irq(irq);
}
} else
lpt1_setup(0xffff);
lpt1_irq(irq);
}
static void
serial_handler(pc87309_t *dev, int uart)
{
uint8_t irq;
uint8_t active;
uint16_t addr;
serial_remove(dev->uart[uart]);
active = (dev->ld_regs[0x03 - uart][0x00] & 0x01) && (dev->pm[0x00] & (1 << (6 - uart)));
addr = (dev->ld_regs[0x03 - uart][0x30] << 8) | dev->ld_regs[0x03 - uart][0x31];
irq = (dev->ld_regs[0x03 - uart][0x40] & 0x0f);
uint8_t active = (dev->ld_regs[LD_UART1 - uart][0x00] & 0x01) &&
(dev->pm[0x00] & (1 << (6 - uart)));
uint8_t irq = (dev->ld_regs[LD_UART1 - uart][0x40] & 0x0f);
uint16_t addr = (dev->ld_regs[LD_UART1 - uart][0x30] << 8) |
dev->ld_regs[LD_UART1 - uart][0x31];
if (active)
if (active && (addr <= 0xfff8)) {
pc87309_log("Enabling COM%i on %04X...\n", uart + 1, addr);
serial_setup(dev->uart[uart], addr, irq);
} else
serial_setup(dev->uart[uart], 0x0000, irq);
}
static void
pm_handler(pc87309_t *dev)
{
uint8_t active;
uint16_t addr;
pc87309_pm_remove(dev);
active = (dev->ld_regs[0x04][0x00] & 0x01);
addr = (dev->ld_regs[0x04][0x30] << 8) | dev->ld_regs[0x04][0x31];
uint8_t active = (dev->ld_regs[LD_PM][0x00] & 0x01);
uint16_t addr = (dev->ld_regs[LD_PM][0x30] << 8) |
dev->ld_regs[LD_PM][0x31];
if (active)
if (active) {
pc87309_log("Enabling power management on %04X...\n", addr);
pc87309_pm_init(dev, addr);
}
}
static void
superio_handler(pc87309_t *dev)
{
if (dev->superio_base != 0x0000)
io_removehandler(dev->superio_base, 0x0002,
pc87309_read, NULL, NULL,
pc87309_write, NULL, NULL, dev);
switch (dev->regs[0x21] & 0x0b) {
default:
dev->superio_base = 0x0000;
break;
case 0x02:
case 0x08: case 0x0a:
dev->superio_base = 0x015c;
break;
case 0x03:
case 0x09: case 0x0b:
dev->superio_base = 0x002e;
break;
}
if (dev->superio_base != 0x0000) {
pc87309_log("Enabling Super I/O on %04X...\n", dev->superio_base);
io_sethandler(dev->superio_base, 0x0002,
pc87309_read, NULL, NULL,
pc87309_write, NULL, NULL, dev);
}
}
static void
pc87309_write(uint16_t port, uint8_t val, void *priv)
{
pc87309_t *dev = (pc87309_t *) priv;
uint8_t index;
index = (port & 1) ? 0 : 1;
pc87309_t *dev = (pc87309_t *) priv;
uint8_t ld = dev->regs[0x07];
uint8_t reg = dev->cur_reg - 0x30;
uint8_t index = (port & 1) ? 0 : 1;
uint8_t old = dev->regs[dev->cur_reg];
if (index) {
dev->cur_reg = val;
return;
} else {
#ifdef ENABLE_PC87309_LOG
if (dev->cur_reg >= 0x30)
pc87309_log("[%04X:%08X] [W] (%04X) %02X:%02X = %02X\n",
CS, cpu_state.pc, port, ld, dev->cur_reg, val);
else
pc87309_log("[%04X:%08X] [W] (%04X) %02X = %02X\n",
CS, cpu_state.pc, port, dev->cur_reg, val);
#endif
switch (dev->cur_reg) {
case 0x00:
case 0x02:
case 0x03:
case 0x06:
case 0x07:
case 0x21:
case 0x02: case 0x03:
case 0x06: case 0x07:
dev->regs[dev->cur_reg] = val;
break;
case 0x21:
dev->regs[dev->cur_reg] = val;
fdc_toggle_flag(dev->fdc, FDC_FLAG_PS2_MCA, !(val & 0x04));
superio_handler(dev);
break;
case 0x22:
dev->regs[dev->cur_reg] = val & 0x7f;
dev->regs[dev->cur_reg] = val;
break;
default:
if (dev->cur_reg >= 0x30) {
if ((dev->regs[0x07] != 0x06) || !(dev->regs[0x21] & 0x10))
dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val;
}
if (dev->cur_reg >= 0x30)
old = dev->ld_regs[ld][reg];
break;
}
}
switch (dev->cur_reg) {
case 0x30:
dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0x01;
switch (dev->regs[0x07]) {
case 0x00:
switch (ld) {
default:
break;
case LD_KBD: case LD_MOUSE:
dev->ld_regs[ld][reg] = val;
kbc_handler(dev);
break;
case LD_FDC:
dev->ld_regs[ld][reg] = val;
fdc_handler(dev);
break;
case 0x01:
case LD_LPT:
dev->ld_regs[ld][reg] = val;
lpt1_handler(dev);
break;
case 0x02:
case LD_UART2:
dev->ld_regs[ld][reg] = val;
serial_handler(dev, 1);
break;
case 0x03:
case LD_UART1:
dev->ld_regs[ld][reg] = val;
serial_handler(dev, 0);
break;
case 0x04:
case LD_PM:
dev->ld_regs[ld][reg] = val;
pm_handler(dev);
break;
default:
break;
}
break;
/* I/O Range Check. */
case 0x31:
switch (ld) {
default:
break;
case LD_MIN ... LD_MAX:
if (ld != LD_MOUSE)
dev->ld_regs[ld][reg] = val;
break;
}
break;
/* Base Address 0 MSB. */
case 0x60:
case 0x62:
dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0x07;
if (dev->cur_reg == 0x62)
break;
switch (dev->regs[0x07]) {
case 0x00:
switch (ld) {
default:
break;
case LD_KBD:
dev->ld_regs[ld][reg] = val;
kbc_handler(dev);
break;
case LD_FDC:
dev->ld_regs[ld][reg] = val;
fdc_handler(dev);
break;
case 0x01:
case LD_LPT:
dev->ld_regs[ld][reg] = (old & 0xfc) | (val & 0x03);
lpt1_handler(dev);
break;
case 0x02:
case LD_UART2:
dev->ld_regs[ld][reg] = val;
serial_handler(dev, 1);
break;
case 0x03:
case LD_UART1:
dev->ld_regs[ld][reg] = val;
serial_handler(dev, 0);
break;
case 0x04:
case LD_PM:
dev->ld_regs[ld][reg] = val;
pm_handler(dev);
break;
default:
break;
}
break;
case 0x63:
if (dev->regs[0x07] == 0x06)
dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = (val & 0xf8) | 0x04;
break;
/* Base Address 0 LSB. */
case 0x61:
switch (dev->regs[0x07]) {
case 0x00:
dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = (val & 0xfa) | 0x02;
switch (ld) {
default:
break;
case LD_KBD:
dev->ld_regs[ld][reg] = (old & 0x04) | (val & 0xfb);
kbc_handler(dev);
break;
case LD_FDC:
dev->ld_regs[ld][reg] = (old & 0x07) | (val & 0xf8);
fdc_handler(dev);
break;
case 0x01:
dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0xfc;
case LD_LPT:
dev->ld_regs[ld][reg] = (old & 0x03) | (val & 0xfc);
lpt1_handler(dev);
break;
case 0x02:
dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0xf8;
case LD_UART2:
dev->ld_regs[ld][reg] = (old & 0x07) | (val & 0xf8);
serial_handler(dev, 1);
break;
case 0x03:
dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0xf8;
case LD_UART1:
dev->ld_regs[ld][reg] = (old & 0x07) | (val & 0xf8);
serial_handler(dev, 0);
break;
case 0x04:
dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0xfe;
case LD_PM:
dev->ld_regs[ld][reg] = (old & 0x01) | (val & 0xfe);
pm_handler(dev);
break;
case 0x06:
dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0xf8;
break;
default:
break;
}
break;
/* Base Address 1 MSB (undocumented for Logical Device 7). */
case 0x62:
switch (ld) {
default:
break;
case LD_KBD:
dev->ld_regs[ld][reg] = val;
kbc_handler(dev);
break;
}
break;
/* Base Address 1 LSB (undocumented for Logical Device 7). */
case 0x63:
switch (ld) {
default:
break;
case LD_KBD:
dev->ld_regs[ld][reg] = (old & 0x04) | (val & 0xfb);
kbc_handler(dev);
break;
}
break;
/* Interrupt Select. */
case 0x70:
case 0x74:
case 0x75:
switch (dev->regs[0x07]) {
case 0x00:
switch (ld) {
default:
break;
case LD_KBD: case LD_MOUSE:
dev->ld_regs[ld][reg] = val;
kbc_handler(dev);
break;
case LD_FDC:
dev->ld_regs[ld][reg] = val;
fdc_handler(dev);
break;
case 0x01:
case LD_LPT:
dev->ld_regs[ld][reg] = val;
lpt1_handler(dev);
break;
case 0x02:
case LD_UART2:
dev->ld_regs[ld][reg] = val;
serial_handler(dev, 1);
break;
case 0x03:
case LD_UART1:
dev->ld_regs[ld][reg] = val;
serial_handler(dev, 0);
break;
case 0x04:
pm_handler(dev);
break;
default:
break;
}
break;
/* Interrupt Type. */
case 0x71:
switch (ld) {
default:
break;
case LD_MIN ... LD_MAX:
if ((ld == LD_KBD) || (ld == LD_MOUSE))
dev->ld_regs[ld][reg] = (old & 0xfc) | (val & 0x03);
else
dev->ld_regs[ld][reg] = (old & 0xfd) | (val & 0x02);
break;
}
break;
/* DMA Channel Select 0. */
case 0x74:
switch (ld) {
default:
break;
case LD_FDC:
dev->ld_regs[ld][reg] = val;
fdc_handler(dev);
break;
case LD_LPT:
dev->ld_regs[ld][reg] = val;
lpt1_handler(dev);
break;
case LD_UART2:
dev->ld_regs[ld][reg] = val;
break;
}
break;
/* DMA Channel Select 1. */
case 0x75:
switch (ld) {
default:
break;
case LD_UART2:
dev->ld_regs[ld][reg] = val;
break;
}
break;
/* Configuration Register 0. */
case 0xf0:
switch (dev->regs[0x07]) {
case 0x00:
dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0xe1;
switch (ld) {
default:
break;
case LD_KBD:
dev->ld_regs[ld][reg] = val;
break;
case LD_FDC:
dev->ld_regs[ld][reg] = val;
fdc_update_densel_polarity(dev->fdc, (val & 0x20) ? 1 : 0);
fdc_update_enh_mode(dev->fdc, (val & 0x40) ? 1 : 0);
break;
case 0x01:
dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0xf3;
case LD_LPT:
dev->ld_regs[ld][reg] = val;
lpt1_handler(dev);
break;
case 0x02:
case 0x03:
dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0x87;
break;
case 0x06:
dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0xc1;
break;
default:
case LD_UART2: case LD_UART1:
dev->ld_regs[ld][reg] = val;
break;
}
break;
/* Configuration Register 1. */
case 0xf1:
if (dev->regs[0x07] == 0x00)
dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0x0f;
switch (ld) {
default:
break;
case LD_FDC:
dev->ld_regs[ld][reg] = val;
break;
}
break;
default:
@@ -348,97 +547,102 @@ pc87309_write(uint16_t port, uint8_t val, void *priv)
}
}
uint8_t
static uint8_t
pc87309_read(uint16_t port, void *priv)
{
const pc87309_t *dev = (pc87309_t *) priv;
uint8_t ret = 0xff;
uint8_t index;
index = (port & 1) ? 0 : 1;
const pc87309_t *dev = (pc87309_t *) priv;
uint8_t ld = dev->regs[0x07];
uint8_t reg = dev->cur_reg - 0x30;
uint8_t index = (port & 1) ? 0 : 1;
uint8_t ret = 0xff;
if (index)
ret = dev->cur_reg & 0x1f;
ret = dev->cur_reg;
else {
if (dev->cur_reg >= 0x30)
ret = dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30];
ret = dev->ld_regs[ld][reg];
/* Write-only registers. */
else if ((dev->cur_reg == 0x00) ||
(dev->cur_reg == 0x02) || (dev->cur_reg == 0x03))
ret = 0x00;
else
ret = dev->regs[dev->cur_reg];
#ifdef ENABLE_PC87309_LOG
if (dev->cur_reg >= 0x30)
pc87309_log("[%04X:%08X] [R] (%04X) %02X:%02X = %02X\n",
CS, cpu_state.pc, port, ld, dev->cur_reg, ret);
else
pc87309_log("[%04X:%08X] [R] (%04X) %02X = %02X\n",
CS, cpu_state.pc, port, dev->cur_reg, ret);
#endif
}
return ret;
}
void
pc87309_reset(pc87309_t *dev)
pc87309_reset(void *priv)
{
pc87309_t *dev = (pc87309_t *) priv;
memset(dev->regs, 0x00, 0x30);
for (uint16_t i = 0; i < 256; i++)
memset(dev->ld_regs[i], 0x00, 0xd0);
memset(dev->pm, 0x00, 0x08);
dev->regs[0x20] = dev->id;
dev->regs[0x21] = 0x04;
dev->regs[0x21] = 0x04 | dev->baddr;
dev->ld_regs[0x00][0x01] = 0x01;
dev->ld_regs[0x00][0x30] = 0x03;
dev->ld_regs[0x00][0x31] = 0xf2;
dev->ld_regs[0x00][0x40] = 0x06;
dev->ld_regs[0x00][0x41] = 0x03;
dev->ld_regs[0x00][0x44] = 0x02;
dev->ld_regs[0x00][0x45] = 0x04;
dev->ld_regs[0x00][0xc0] = 0x02;
dev->ld_regs[LD_KBD ][0x00] = 0x01;
dev->ld_regs[LD_KBD ][0x31] = 0x60;
dev->ld_regs[LD_KBD ][0x33] = 0x64;
dev->ld_regs[LD_KBD ][0x40] = 0x01;
dev->ld_regs[LD_KBD ][0x41] = 0x02;
dev->ld_regs[LD_KBD ][0x44] = 0x04;
dev->ld_regs[LD_KBD ][0x45] = 0x04;
dev->ld_regs[LD_KBD ][0xc0] = 0x40;
dev->ld_regs[0x01][0x30] = 0x02;
dev->ld_regs[0x01][0x31] = 0x78;
dev->ld_regs[0x01][0x40] = 0x07;
dev->ld_regs[0x01][0x44] = 0x04;
dev->ld_regs[0x01][0x45] = 0x04;
dev->ld_regs[0x01][0xc0] = 0xf2;
dev->ld_regs[LD_MOUSE][0x40] = 0x0c;
dev->ld_regs[LD_MOUSE][0x41] = 0x02;
dev->ld_regs[LD_MOUSE][0x44] = 0x04;
dev->ld_regs[LD_MOUSE][0x45] = 0x04;
dev->ld_regs[0x02][0x30] = 0x02;
dev->ld_regs[0x02][0x31] = 0xf8;
dev->ld_regs[0x02][0x40] = 0x03;
dev->ld_regs[0x02][0x41] = 0x03;
dev->ld_regs[0x02][0x44] = 0x04;
dev->ld_regs[0x02][0x45] = 0x04;
dev->ld_regs[0x02][0xc0] = 0x02;
dev->ld_regs[LD_FDC ][0x01] = 0x01;
dev->ld_regs[LD_FDC ][0x30] = 0x03;
dev->ld_regs[LD_FDC ][0x31] = 0xf0;
dev->ld_regs[LD_FDC ][0x32] = 0x03;
dev->ld_regs[LD_FDC ][0x33] = 0xf7;
dev->ld_regs[LD_FDC ][0x40] = 0x06;
dev->ld_regs[LD_FDC ][0x41] = 0x03;
dev->ld_regs[LD_FDC ][0x44] = 0x02;
dev->ld_regs[LD_FDC ][0x45] = 0x04;
dev->ld_regs[LD_FDC ][0xc0] = 0x02;
dev->ld_regs[0x03][0x30] = 0x03;
dev->ld_regs[0x03][0x31] = 0xf8;
dev->ld_regs[0x03][0x40] = 0x04;
dev->ld_regs[0x03][0x41] = 0x03;
dev->ld_regs[0x03][0x44] = 0x04;
dev->ld_regs[0x03][0x45] = 0x04;
dev->ld_regs[0x03][0xc0] = 0x02;
dev->ld_regs[LD_LPT ][0x30] = 0x02;
dev->ld_regs[LD_LPT ][0x31] = 0x78;
dev->ld_regs[LD_LPT ][0x40] = 0x07;
dev->ld_regs[LD_LPT ][0x44] = 0x04;
dev->ld_regs[LD_LPT ][0x45] = 0x04;
dev->ld_regs[LD_LPT ][0xc0] = 0xf2;
dev->ld_regs[0x04][0x44] = 0x04;
dev->ld_regs[0x04][0x45] = 0x04;
dev->ld_regs[LD_UART2][0x30] = 0x02;
dev->ld_regs[LD_UART2][0x31] = 0xf8;
dev->ld_regs[LD_UART2][0x40] = 0x03;
dev->ld_regs[LD_UART2][0x41] = 0x03;
dev->ld_regs[LD_UART2][0x44] = 0x04;
dev->ld_regs[LD_UART2][0x45] = 0x04;
dev->ld_regs[LD_UART2][0xc0] = 0x02;
dev->ld_regs[0x05][0x40] = 0x0c;
dev->ld_regs[0x05][0x41] = 0x02;
dev->ld_regs[0x05][0x44] = 0x04;
dev->ld_regs[0x05][0x45] = 0x04;
dev->ld_regs[LD_UART1][0x30] = 0x03;
dev->ld_regs[LD_UART1][0x31] = 0xf8;
dev->ld_regs[LD_UART1][0x40] = 0x04;
dev->ld_regs[LD_UART1][0x41] = 0x03;
dev->ld_regs[LD_UART1][0x44] = 0x04;
dev->ld_regs[LD_UART1][0x45] = 0x04;
dev->ld_regs[LD_UART1][0xc0] = 0x02;
dev->ld_regs[0x06][0x01] = 0x01;
dev->ld_regs[0x06][0x31] = 0x60;
dev->ld_regs[0x06][0x33] = 0x64;
dev->ld_regs[0x06][0x40] = 0x01;
dev->ld_regs[0x06][0x41] = 0x02;
dev->ld_regs[0x06][0x44] = 0x04;
dev->ld_regs[0x06][0x45] = 0x04;
dev->ld_regs[0x06][0xc0] = 0x40;
dev->regs[0x00] = 0x0B;
dev->regs[0x01] = 0x01;
dev->regs[0x03] = 0x01;
dev->regs[0x05] = 0x0D;
dev->regs[0x08] = 0x70;
dev->regs[0x09] = 0xC0;
dev->regs[0x0b] = 0x80;
dev->regs[0x0f] = 0x1E;
dev->regs[0x12] = 0x30;
dev->regs[0x19] = 0xEF;
dev->ld_regs[LD_PM ][0x44] = 0x04;
dev->ld_regs[LD_PM ][0x45] = 0x04;
dev->pm[0] = 0x79;
dev->pm[4] = 0x0e;
@@ -449,10 +653,16 @@ pc87309_reset(pc87309_t *dev)
0 = 360 rpm @ 500 kbps for 3.5"
1 = Default, 300 rpm @ 500, 300, 250, 1000 kbps for 3.5"
*/
lpt1_remove();
serial_remove(dev->uart[0]);
serial_remove(dev->uart[1]);
fdc_toggle_flag(dev->fdc, FDC_FLAG_PS2_MCA, 0);
fdc_reset(dev->fdc);
kbc_handler(dev);
fdc_handler(dev);
lpt1_handler(dev);
serial_handler(dev, 0);
serial_handler(dev, 1);
pm_handler(dev);
superio_handler(dev);
}
static void
@@ -475,16 +685,27 @@ pc87309_init(const device_t *info)
dev->uart[0] = device_add_inst(&ns16550_device, 1);
dev->uart[1] = device_add_inst(&ns16550_device, 2);
pc87309_reset(dev);
if (info->local & 0x100) {
io_sethandler(0x15c, 0x0002,
pc87309_read, NULL, NULL, pc87309_write, NULL, NULL, dev);
} else {
io_sethandler(0x02e, 0x0002,
pc87309_read, NULL, NULL, pc87309_write, NULL, NULL, dev);
switch (info->local & PCX730X_KBC) {
default:
case PCX730X_AMI:
dev->kbc = device_add(&keyboard_ps2_intel_ami_pci_device);
break;
/* Optiplex! */
case PCX730X_PHOENIX_42:
dev->kbc = device_add(&keyboard_ps2_phoenix_device);
break;
case PCX730X_PHOENIX_42I:
dev->kbc = device_add(&keyboard_ps2_phoenix_pci_device);
break;
}
if (info->local & PCX730X_15C)
dev->baddr = 0x0a;
else
dev->baddr = 0x0b;
pc87309_reset(dev);
return dev;
}
@@ -492,24 +713,10 @@ const device_t pc87309_device = {
.name = "National Semiconductor PC87309 Super I/O",
.internal_name = "pc87309",
.flags = 0,
.local = 0xe0,
.local = 0,
.init = pc87309_init,
.close = pc87309_close,
.reset = NULL,
.available = NULL,
.speed_changed = NULL,
.force_redraw = NULL,
.config = NULL
};
const device_t pc87309_15c_device = {
.name = "National Semiconductor PC87309 Super I/O (Port 15Ch)",
.internal_name = "pc87309_15c",
.flags = 0,
.local = 0x1e0,
.init = pc87309_init,
.close = pc87309_close,
.reset = NULL,
.reset = pc87309_reset,
.available = NULL,
.speed_changed = NULL,
.force_redraw = NULL,

View File

@@ -180,9 +180,6 @@ endif()
add_subdirectory(ymfm)
target_link_libraries(86Box ymfm)
add_subdirectory(saasound)
target_link_libraries(86Box saasound)
if(GUSMAX)
target_compile_definitions(snd PRIVATE USE_GUSMAX)
endif()

View File

@@ -1,16 +0,0 @@
add_library(saasound OBJECT
SAAAmp.cpp
SAAAmp.h
SAADevice.cpp
SAADevice.h
SAAEnv.cpp
SAAEnv.h
SAAFreq.cpp
SAAFreq.h
SAAImpl.cpp
SAAImpl.h
SAANoise.cpp
SAANoise.h
SAASndC.cpp
SAASndC.h
SAASound.cpp)

View File

@@ -1,203 +0,0 @@
// Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
//
// SAAAmp.cpp: implementation of the CSAAAmp class.
// This class handles Tone/Noise mixing, Envelope application and
// amplification.
//
//////////////////////////////////////////////////////////////////////
#include "SAASound.h"
#include "types.h"
#include "SAANoise.h"
#include "SAAEnv.h"
#include "SAAFreq.h"
#include "SAAAmp.h"
#include "defns.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CSAAAmp::CSAAAmp(CSAAFreq * const ToneGenerator, const CSAANoise * const NoiseGenerator, const CSAAEnv * const EnvGenerator)
:
m_pcConnectedToneGenerator(ToneGenerator),
m_pcConnectedNoiseGenerator(NoiseGenerator),
m_pcConnectedEnvGenerator(EnvGenerator),
m_bUseEnvelope(EnvGenerator != NULL)
{
leftlevel = 0;
leftlevela0x0e = 0;
rightlevel = 0;
rightlevela0x0e = 0;
m_nMixMode = 0;
m_bMute=true;
m_bSync = false;
m_nOutputIntermediate=0;
last_level_byte=0;
SetAmpLevel(0x00);
}
CSAAAmp::~CSAAAmp()
{
// Nothing to do
}
void CSAAAmp::SetAmpLevel(BYTE level_byte)
{
// if level unchanged since last call then do nothing
if (level_byte != last_level_byte)
{
last_level_byte = level_byte;
leftlevel = level_byte & 0x0f;
leftlevela0x0e = leftlevel & 0x0e;
rightlevel = (level_byte >> 4) & 0x0f;
rightlevela0x0e = rightlevel & 0x0e;
}
}
void CSAAAmp::SetToneMixer(BYTE bEnabled)
{
if (bEnabled == 0)
{
// clear mixer bit
m_nMixMode &= ~(0x01);
}
else
{
// set mixer bit
m_nMixMode |= 0x01;
}
}
void CSAAAmp::SetNoiseMixer(BYTE bEnabled)
{
if (bEnabled == 0)
{
m_nMixMode &= ~(0x02);
}
else
{
m_nMixMode |= 0x02;
}
}
void CSAAAmp::Mute(bool bMute)
{
// m_bMute refers to the GLOBAL mute setting (register 28 bit 0)
// NOT the per-channel mixer settings !!
m_bMute = bMute;
}
void CSAAAmp::Sync(bool bSync)
{
// m_bSync refers to the GLOBAL sync setting (register 28 bit 1)
m_bSync = bSync;
}
void CSAAAmp::Tick(void)
{
// updates m_nOutputIntermediate to 0, 1 or 2
//
// connected oscillator always ticks (this isn't really connected to the amp)
int level = m_pcConnectedToneGenerator->Tick();
switch (m_nMixMode)
{
case 0:
// no tone or noise for this channel
m_nOutputIntermediate = 0;
break;
case 1:
// tone only for this channel
m_nOutputIntermediate = level * 2;
// NOTE: ConnectedToneGenerator returns either 0 or 1
break;
case 2:
// noise only for this channel
m_nOutputIntermediate = m_pcConnectedNoiseGenerator->Level() * 2;
// NOTE: ConnectedNoiseGenerator()->Level() returns either 0 or 1
break;
case 3:
// tone+noise for this channel ... mixing algorithm :
// tone noise output
// 0 0 0
// 1 0 2
// 0 1 0
// 1 1 1
// = 2 * tone - 1 * (tone & noise)
// = tone * (2 - noise)
m_nOutputIntermediate = level * (2 - m_pcConnectedNoiseGenerator->Level());
break;
}
// intermediate is between 0 and 2
}
inline int CSAAAmp::EffectiveAmplitude(int amp, int env) const
{
// Return the effective amplitude of the low-pass-filtered result of the logical
// AND of the amplitude PDM and envelope PDM patterns. This is a more accurate
// evaluation of the SAA than simply returning amp * env , based on how the SAA
// implements pulse-density modulation.
static const int pdm[16][16] = {
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,2,2,2,2,2,2,2,2,4,4,4,4},
{0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8},
{0,1,1,2,4,5,5,6,6,7,7,8,10,11,11,12},
{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15},
{0,1,2,3,6,7,8,9,10,11,12,13,16,17,18,19},
{0,2,3,5,6,8,9,11,12,14,15,17,18,20,21,23},
{0,2,3,5,8,10,11,13,14,16,17,19,22,24,25,27},
{0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30},
{0,2,4,6,10,12,14,16,18,20,22,24,28,30,32,34},
{0,3,5,8,10,13,15,18,20,23,25,28,30,33,35,38},
{0,3,5,8,12,15,17,20,22,25,27,30,34,37,39,42},
{0,3,6,9,12,15,18,21,24,27,30,33,36,39,42,45},
{0,3,6,9,14,17,20,23,26,29,32,35,40,43,46,49},
{0,4,7,11,14,18,21,25,28,32,35,39,42,46,49,53},
{0,4,7,11,16,20,23,27,30,34,37,41,46,50,53,57}
};
return(pdm[amp][env] * 4);
}
void CSAAAmp::TickAndOutputStereo(unsigned int & left, unsigned int & right)
{
// This returns a value between 0 and 480 inclusive.
// This represents the full dynamic range of one output mixer (tone, or noise+tone, at full volume,
// without envelopes enabled). Note that, with envelopes enabled, the actual dynamic range
// is reduced on-chip to just over 88% of this (424), so the "loudest" output requires disabling envs.
// NB for 6 channels at full volume, with simple additive mixing, you would see a combined
// output of 2880, and a multiplier of 11 (=31680) fits comfortably within 16-bit signed output range.
if (m_bSync)
{
// TODO check this
left = right = 0;
return;
}
// first, do the Tick:
Tick();
// now calculate the returned amplitude for this sample:
////////////////////////////////////////////////////////
if (m_bMute)
{
left = right = 0;
}
else if (m_bUseEnvelope && m_pcConnectedEnvGenerator->IsActive())
{
left = EffectiveAmplitude(m_pcConnectedEnvGenerator->LeftLevel(), leftlevela0x0e) * (2 - m_nOutputIntermediate);
right = EffectiveAmplitude(m_pcConnectedEnvGenerator->RightLevel(), rightlevela0x0e) * (2 - m_nOutputIntermediate);
}
else
{
left = leftlevel * m_nOutputIntermediate * 16;
right = rightlevel * m_nOutputIntermediate * 16;
}
}

View File

@@ -1,44 +0,0 @@
// Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
//
// SAAAmp.h: interface for the CSAAAmp class.
// This class handles Tone/Noise mixing, Envelope application and
// amplification.
//
//////////////////////////////////////////////////////////////////////
#ifndef SAAAMP_H_INCLUDED
#define SAAAMP_H_INCLUDED
class CSAAAmp
{
private:
int leftlevel;
int leftlevela0x0e;
int rightlevel;
int rightlevela0x0e;
int m_nOutputIntermediate;
unsigned int m_nMixMode;
CSAAFreq * const m_pcConnectedToneGenerator; // not const because amp calls ->Tick()
const CSAANoise * const m_pcConnectedNoiseGenerator;
const CSAAEnv * const m_pcConnectedEnvGenerator;
const bool m_bUseEnvelope;
mutable bool m_bMute;
mutable bool m_bSync;
mutable BYTE last_level_byte;
int EffectiveAmplitude(int amp, int env) const;
public:
CSAAAmp(CSAAFreq * const ToneGenerator, const CSAANoise * const NoiseGenerator, const CSAAEnv * const EnvGenerator);
~CSAAAmp();
void SetAmpLevel(BYTE level_byte); // really just a BYTE
void SetToneMixer(BYTE bEnabled);
void SetNoiseMixer(BYTE bEnabled);
void Mute(bool bMute);
void Sync(bool bSync);
void Tick(void);
void TickAndOutputStereo(unsigned int & left, unsigned int & right);
};
#endif // SAAAMP_H_INCLUDED

View File

@@ -1,41 +0,0 @@
// Part of SAASound copyright 2020 Dave Hooper <dave@beermex.com>
//
// SAAConfig.h: configuration file handler class
//
//////////////////////////////////////////////////////////////////////
#include "defns.h"
#ifdef USE_CONFIG_FILE
#ifndef SAA_CONFIG_H_INCLUDED
#define SAA_CONFIG_H_INCLUDED
#define INI_READONLY
#define INI_ANSIONLY /*nb not really 'ANSI', this just forces all read/write to use 8-bit char*/
#include "minIni/minIni.h"
class SAAConfig
{
private:
minIni m_minIni;
bool m_bHasReadConfig;
public:
bool m_bGenerateRegisterLogs;
bool m_bGeneratePcmLogs;
bool m_bGeneratePcmSeparateChannels;
t_string m_strRegisterLogPath;
t_string m_strPcmOutputPath;
unsigned int m_nOversample;
bool m_bHighpass;
double m_nBoost;
SAAConfig();
void ReadConfig();
t_string getChannelPcmOutputPath(int);
};
#endif // SAA_CONFIG_H_INCLUDED
#endif // USE_CONFIG_FILE

View File

@@ -1,392 +0,0 @@
// Part of SAASound copyright 2020 Dave Hooper <dave@beermex.com>
//
// SAADevice.cpp: connecting the subcomponents of the SAA1099 together.
// This class handles device inputs and outputs (clocking, data and
// address bus, and simulated output)
//
//////////////////////////////////////////////////////////////////////
#include "SAASound.h"
#include "types.h"
#include "SAAEnv.h"
#include "SAANoise.h"
#include "SAAFreq.h"
#include "SAAAmp.h"
#include "SAASound.h"
#include "SAAImpl.h"
#include "defns.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CSAADevice::CSAADevice()
:
m_nCurrentSaaReg(0),
m_bOutputEnabled(false),
m_bSync(false),
m_bHighpass(true),
m_nOversample(0),
m_Noise0(0xffffffff),
m_Noise1(0xffffffff),
m_Env0(),
m_Env1(),
m_Osc0(&m_Noise0, NULL),
m_Osc1(NULL, &m_Env0),
m_Osc2(NULL, NULL),
m_Osc3(&m_Noise1, NULL),
m_Osc4(NULL, &m_Env1),
m_Osc5(NULL, NULL),
m_Amp0(&m_Osc0, &m_Noise0, NULL),
m_Amp1(&m_Osc1, &m_Noise0, NULL),
m_Amp2(&m_Osc2, &m_Noise0, &m_Env0),
m_Amp3(&m_Osc3, &m_Noise1, NULL),
m_Amp4(&m_Osc4, &m_Noise1, NULL),
m_Amp5(&m_Osc5, &m_Noise1, &m_Env1)
{
// Create and link up the objects that make up the emulator
Noise[0] = &m_Noise0;
Noise[1] = &m_Noise1;
Env[0] = &m_Env0;
Env[1] = &m_Env1;
// Create oscillators (tone generators) and link to noise generators and
// envelope controllers
Osc[0] = &m_Osc0;
Osc[1] = &m_Osc1;
Osc[2] = &m_Osc2;
Osc[3] = &m_Osc3;
Osc[4] = &m_Osc4;
Osc[5] = &m_Osc5;
// Create amplification/mixing stages and link to appropriate oscillators,
// noise generators and envelope controllers
Amp[0] = &m_Amp0;
Amp[1] = &m_Amp1;
Amp[2] = &m_Amp2;
Amp[3] = &m_Amp3;
Amp[4] = &m_Amp4;
Amp[5] = &m_Amp5;
_SetClockRate(EXTERNAL_CLK_HZ);
_SetOversample(DEFAULT_OVERSAMPLE);
}
CSAADevice::~CSAADevice()
{
}
//////////////////////////////////////////////////////////////////////
// CSAASound members
//////////////////////////////////////////////////////////////////////
void CSAADevice::_SetClockRate(unsigned int nClockRate)
{
m_Osc0._SetClockRate(nClockRate);
m_Osc1._SetClockRate(nClockRate);
m_Osc2._SetClockRate(nClockRate);
m_Osc3._SetClockRate(nClockRate);
m_Osc4._SetClockRate(nClockRate);
m_Osc5._SetClockRate(nClockRate);
m_Noise0._SetClockRate(nClockRate);
m_Noise1._SetClockRate(nClockRate);
}
void CSAADevice::_SetSampleRate(unsigned int nSampleRate)
{
m_Osc0._SetSampleRate(nSampleRate);
m_Osc1._SetSampleRate(nSampleRate);
m_Osc2._SetSampleRate(nSampleRate);
m_Osc3._SetSampleRate(nSampleRate);
m_Osc4._SetSampleRate(nSampleRate);
m_Osc5._SetSampleRate(nSampleRate);
m_Noise0._SetSampleRate(nSampleRate);
m_Noise1._SetSampleRate(nSampleRate);
}
void CSAADevice::_SetOversample(unsigned int nOversample)
{
if (((int) nOversample) != m_nOversample)
{
m_nOversample = nOversample;
m_Osc0._SetOversample(nOversample);
m_Osc1._SetOversample(nOversample);
m_Osc2._SetOversample(nOversample);
m_Osc3._SetOversample(nOversample);
m_Osc4._SetOversample(nOversample);
m_Osc5._SetOversample(nOversample);
m_Noise0._SetOversample(nOversample);
m_Noise1._SetOversample(nOversample);
}
}
void CSAADevice::_WriteData(BYTE nData)
{
#if defined(DEBUG) || defined(DEBUGSAA)
m_Reg[m_nCurrentSaaReg] = nData;
#endif
// route nData to the appropriate place
switch (m_nCurrentSaaReg)
{
// Amplitude data (==> Amp)
case 0:
m_Amp0.SetAmpLevel(nData);
break;
case 1:
m_Amp1.SetAmpLevel(nData);
break;
case 2:
m_Amp2.SetAmpLevel(nData);
break;
case 3:
m_Amp3.SetAmpLevel(nData);
break;
case 4:
m_Amp4.SetAmpLevel(nData);
break;
case 5:
m_Amp5.SetAmpLevel(nData);
break;
// Freq data (==> Osc)
case 8:
m_Osc0.SetFreqOffset(nData);
break;
case 9:
m_Osc1.SetFreqOffset(nData);
break;
case 10:
m_Osc2.SetFreqOffset(nData);
break;
case 11:
m_Osc3.SetFreqOffset(nData);
break;
case 12:
m_Osc4.SetFreqOffset(nData);
break;
case 13:
m_Osc5.SetFreqOffset(nData);
break;
// Freq octave data (==> Osc) for channels 0,1
case 16:
m_Osc0.SetFreqOctave(nData & 0x07);
m_Osc1.SetFreqOctave((nData >> 4) & 0x07);
break;
// Freq octave data (==> Osc) for channels 2,3
case 17:
m_Osc2.SetFreqOctave(nData & 0x07);
m_Osc3.SetFreqOctave((nData >> 4) & 0x07);
break;
// Freq octave data (==> Osc) for channels 4,5
case 18:
m_Osc4.SetFreqOctave(nData & 0x07);
m_Osc5.SetFreqOctave((nData >> 4) & 0x07);
break;
// Tone mixer control (==> Amp)
case 20:
m_Amp0.SetToneMixer(nData & 0x01);
m_Amp1.SetToneMixer(nData & 0x02);
m_Amp2.SetToneMixer(nData & 0x04);
m_Amp3.SetToneMixer(nData & 0x08);
m_Amp4.SetToneMixer(nData & 0x10);
m_Amp5.SetToneMixer(nData & 0x20);
break;
// Noise mixer control (==> Amp)
case 21:
m_Amp0.SetNoiseMixer(nData & 0x01);
m_Amp1.SetNoiseMixer(nData & 0x02);
m_Amp2.SetNoiseMixer(nData & 0x04);
m_Amp3.SetNoiseMixer(nData & 0x08);
m_Amp4.SetNoiseMixer(nData & 0x10);
m_Amp5.SetNoiseMixer(nData & 0x20);
break;
// Noise frequency/source control (==> Noise)
case 22:
m_Noise0.SetSource(nData & 0x03);
m_Noise1.SetSource((nData >> 4) & 0x03);
break;
// Envelope control data (==> Env) for envelope controller #0
case 24:
m_Env0.SetEnvControl(nData);
break;
// Envelope control data (==> Env) for envelope controller #1
case 25:
m_Env1.SetEnvControl(nData);
break;
// Global enable and reset (sync) controls
case 28:
{
// Reset (sync) bit
bool bSync = bool(nData & 0x02);
if (bSync != m_bSync)
{
// Sync all devices
// This amounts to telling them all to reset to a
// known state, which is also a state that doesn't change
// (i.e. no audio output, although there are some exceptions)
// bSync=true => all devices are sync (aka reset);
// bSync=false => all devices are allowed to run and generate changing output
m_Osc0.Sync(bSync);
m_Osc1.Sync(bSync);
m_Osc2.Sync(bSync);
m_Osc3.Sync(bSync);
m_Osc4.Sync(bSync);
m_Osc5.Sync(bSync);
m_Noise0.Sync(bSync);
m_Noise1.Sync(bSync);
m_Amp0.Sync(bSync);
m_Amp1.Sync(bSync);
m_Amp2.Sync(bSync);
m_Amp3.Sync(bSync);
m_Amp4.Sync(bSync);
m_Amp5.Sync(bSync);
m_bSync = bSync;
}
// Global mute bit
bool bOutputEnabled = bool(nData & 0x01);
if (bOutputEnabled != m_bOutputEnabled)
{
// unmute all amps - sound 'enabled'
m_Amp0.Mute(!bOutputEnabled);
m_Amp1.Mute(!bOutputEnabled);
m_Amp2.Mute(!bOutputEnabled);
m_Amp3.Mute(!bOutputEnabled);
m_Amp4.Mute(!bOutputEnabled);
m_Amp5.Mute(!bOutputEnabled);
m_bOutputEnabled = bOutputEnabled;
}
}
break;
default:
// anything else means data is being written to a register
// that is not used within the SAA-1099 architecture
// hence, we ignore it.
{}
}
}
void CSAADevice::_WriteAddress(BYTE nReg)
{
m_nCurrentSaaReg = nReg & 31;
if (m_nCurrentSaaReg == 24)
{
m_Env0.ExternalClock();
}
else if (m_nCurrentSaaReg == 25)
{
m_Env1.ExternalClock();
}
}
#if 1
BYTE CSAADevice::_ReadAddress(void)
{
// Not a real hardware function of the SAA-1099, which is write-only
// However, this is used by SAAImpl to generate debug logs (if enabled)
return(m_nCurrentSaaReg);
}
#endif
#if defined(DEBUG)
BYTE CSAADevice::_ReadData(void)
{
// Not a real hardware function of the SAA-1099, which is write-only
// This is only compiled for Debug builds
return(m_Reg[m_nCurrentSaaReg]);
}
#endif
void CSAADevice::_TickAndOutputStereo(unsigned int& left_mixed, unsigned int& right_mixed)
{
unsigned int temp_left, temp_right;
unsigned int accum_left = 0, accum_right = 0;
for (int i = 1 << m_nOversample; i > 0; i--)
{
m_Noise0.Tick();
m_Noise1.Tick();
m_Amp0.TickAndOutputStereo(temp_left, temp_right);
accum_left += temp_left;
accum_right += temp_right;
m_Amp1.TickAndOutputStereo(temp_left, temp_right);
accum_left += temp_left;
accum_right += temp_right;
m_Amp2.TickAndOutputStereo(temp_left, temp_right);
accum_left += temp_left;
accum_right += temp_right;
m_Amp3.TickAndOutputStereo(temp_left, temp_right);
accum_left += temp_left;
accum_right += temp_right;
m_Amp4.TickAndOutputStereo(temp_left, temp_right);
accum_left += temp_left;
accum_right += temp_right;
m_Amp5.TickAndOutputStereo(temp_left, temp_right);
accum_left += temp_left;
accum_right += temp_right;
}
left_mixed = accum_left;
right_mixed = accum_right;
}
void CSAADevice::_TickAndOutputSeparate(unsigned int& left_mixed, unsigned int& right_mixed,
unsigned int& left0, unsigned int& right0,
unsigned int& left1, unsigned int& right1,
unsigned int& left2, unsigned int& right2,
unsigned int& left3, unsigned int& right3,
unsigned int& left4, unsigned int& right4,
unsigned int& left5, unsigned int& right5
)
{
unsigned int temp_left, temp_right;
unsigned int accum_left = 0, accum_right = 0;
left0 = left1 = left2 = left3 = left4 = left5 = 0;
right0 = right1 = right2 = right3 = right4 = right5 = 0;
for (int i = 1 << m_nOversample; i > 0; i--)
{
m_Noise0.Tick();
m_Noise1.Tick();
m_Amp0.TickAndOutputStereo(temp_left, temp_right);
left0 += temp_left;
right0 += temp_right;
accum_left += temp_left;
accum_right += temp_right;
m_Amp1.TickAndOutputStereo(temp_left, temp_right);
left1 += temp_left;
right1 += temp_right;
accum_left += temp_left;
accum_right += temp_right;
m_Amp2.TickAndOutputStereo(temp_left, temp_right);
left2 += temp_left;
right2 += temp_right;
accum_left += temp_left;
accum_right += temp_right;
m_Amp3.TickAndOutputStereo(temp_left, temp_right);
left3 += temp_left;
right3 += temp_right;
accum_left += temp_left;
accum_right += temp_right;
m_Amp4.TickAndOutputStereo(temp_left, temp_right);
left4 += temp_left;
right4 += temp_right;
accum_left += temp_left;
accum_right += temp_right;
m_Amp5.TickAndOutputStereo(temp_left, temp_right);
left5 += temp_left;
right5 += temp_right;
accum_left += temp_left;
accum_right += temp_right;
}
left_mixed = accum_left;
right_mixed = accum_right;
}

View File

@@ -1,69 +0,0 @@
// Part of SAASound copyright 2020 Dave Hooper <dave@beermex.com>
//
// SAADevice.h: connecting the subcomponents of the SAA1099 together.
// This class handles device inputs and outputs (clocking, data and
// address bus, and simulated output)
//
//////////////////////////////////////////////////////////////////////
#ifndef SAADEVICE_H_INCLUDED
#define SAADEVICE_H_INCLUDED
#include "SAASound.h"
#include "SAANoise.h"
#include "SAAEnv.h"
#include "SAAFreq.h"
#include "SAAAmp.h"
class CSAADevice
{
private:
int m_nCurrentSaaReg;
bool m_bOutputEnabled;
bool m_bSync;
bool m_bHighpass;
int m_nOversample;
CSAANoise m_Noise0, m_Noise1;
CSAAEnv m_Env0, m_Env1;
CSAAFreq m_Osc0, m_Osc1, m_Osc2, m_Osc3, m_Osc4, m_Osc5;
CSAAAmp m_Amp0, m_Amp1, m_Amp2, m_Amp3, m_Amp4, m_Amp5;
CSAANoise* Noise[2];
CSAAEnv* Env[2];
CSAAFreq* Osc[6];
CSAAAmp* Amp[6];
#if defined(DEBUG) || defined(DEBUGSAA)
BYTE m_Reg[32];
#endif
public:
CSAADevice();
~CSAADevice();
void _WriteAddress(BYTE nReg);
void _WriteData(BYTE nData);
#if 1
BYTE _ReadAddress(void);
#endif
#if defined(DEBUG)
BYTE _ReadData(void);
#endif
void _SetClockRate(unsigned int nClockRate);
void _SetSampleRate(unsigned int nSampleRate);
void _SetOversample(unsigned int nOversample);
void _TickAndOutputStereo(unsigned int& left_mixed, unsigned int& right_mixed);
void _TickAndOutputSeparate(unsigned int& left_mixed, unsigned int& right_mixed,
unsigned int& left0, unsigned int& right0,
unsigned int& left1, unsigned int& right1,
unsigned int& left2, unsigned int& right2,
unsigned int& left3, unsigned int& right3,
unsigned int& left4, unsigned int& right4,
unsigned int& left5, unsigned int& right5
);
};
#endif // SAADEVICE_H_INCLUDED

View File

@@ -1,380 +0,0 @@
// Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
//
// SAAEnv.cpp: implementation of the CSAAEnv class.
//
//////////////////////////////////////////////////////////////////////
#include "SAASound.h"
#include "types.h"
#include "SAAEnv.h"
//////////////////////////////////////////////////////////////////////
// Static member initialisation
//////////////////////////////////////////////////////////////////////
const ENVDATA CSAAEnv::cs_EnvData[8] =
{
{1,false, { {{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}},
{{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}}},
{1,true, { {{15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15},{15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15}},
{{14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14},{14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14}}}},
{1,false, { {{15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}},
{{14,14,12,12,10,10,8,8,6,6,4,4,2,2,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}}},
{1,true, { {{15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}},
{{14,14,12,12,10,10,8,8,6,6,4,4,2,2,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}}},
{2,false, { {{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}, {15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0}},
{{0,0,2,2,4,4,6,6,8,8,10,10,12,12,14,14}, {14,14,12,12,10,10,8,8,6,6,4,4,2,2,0,0}}}},
{2,true, { {{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}, {15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0}},
{{0,0,2,2,4,4,6,6,8,8,10,10,12,12,14,14}, {14,14,12,12,10,10,8,8,6,6,4,4,2,2,0,0}}}},
{1,false, { {{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}},
{{0,0,2,2,4,4,6,6,8,8,10,10,12,12,14,14}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}}},
{1,true, { {{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}},
{{0,0,2,2,4,4,6,6,8,8,10,10,12,12,14,14}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}}}
};
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CSAAEnv::CSAAEnv()
:
m_bEnabled(false),
m_nPhase(0),
m_nPhasePosition(0),
m_bEnvelopeEnded(true),
m_nResolution(1),
m_bNewData(false),
m_nNextData(0)
{
// initialise itself with the value 'zero'
SetEnvControl(0);
}
CSAAEnv::~CSAAEnv()
{
// Nothing to do
}
void CSAAEnv::InternalClock(void)
{
// will only do something if envelope clock mode is set to internal
// and the env control is enabled
if (m_bEnabled && (!m_bClockExternally)) Tick();
}
void CSAAEnv::ExternalClock(void)
{
// will only do something if envelope clock mode is set to external
// and the env control is enabled
if (m_bClockExternally && m_bEnabled) Tick();
}
void CSAAEnv::SetEnvControl(int nData)
{
// process immediate stuff first:
// start with the Enabled flag. if env is disabled,
// there's not much to do
bool bEnabled = ((nData & 0x80)==0x80);
if (!bEnabled && !m_bEnabled)
return;
m_bEnabled = bEnabled;
if (!m_bEnabled)
{
// env control was enabled, and now disabled
// Any subsequent env control changes are immediate.
m_bEnvelopeEnded = true;
return;
}
// Resolution (3bit/4bit) is also immediately processed
int new_resolution = ((nData & 0x10) == 0x10) ? 2 : 1;
// NOTE: undocumented behaviour when changing resolution mid-waveform
// Empirically, the following matches observations:
// * When ticking the env generator with 4-bit resolution, the position += 1
// * When ticking the env generator with 3-bit resolution, the position += 2
// * When changing between 4-bit resolution and 3-bit resolution
// without ticking the env generator, the position is unchanged
// (although, effectively, the LSB is ignored. Purely as an implementation
// detail, I'm implementing this as clearing the LSB ie LSB=0; see next point)
// * When changing between 3-bit resolution and 4-bit resolution
// without ticking the env generator, the position LSB is set to 1
// See test case: envext_34b
//
if (m_nResolution == 1 && new_resolution == 2)
{
// change from 4-bit to 3-bit
m_nPhasePosition &= 0xe;
}
else if (m_nResolution == 2 && new_resolution == 1)
{
// change from 3-bit to 4-bit
m_nPhasePosition |= 0x1;
}
m_nResolution = new_resolution;
// now buffered stuff: but only if it's ok to, and only if the
// envgenerator is not disabled. otherwise it just stays buffered until
// the Tick() function sets m_bEnvelopeEnded to true and realises there is
// already some new data waiting
if (m_bEnvelopeEnded)
{
SetNewEnvData(nData); // also does the SetLevels() call for us.
m_bNewData=false;
}
else
{
// since the 'next resolution' changes arrive unbuffered, we
// may need to change the current level because of this:
SetLevels();
// store current new data, and set the newdata flag:
m_bNewData = true;
m_nNextData = nData;
}
}
int CSAAEnv::LeftLevel(void) const
{
return m_nLeftLevel;
}
int CSAAEnv::RightLevel(void) const
{
return m_nRightLevel;
}
inline void CSAAEnv::Tick(void)
{
// if disabled, do nothing
if (!m_bEnabled) // m_bEnabled is set directly, not buffered, so this is ok
{
// for sanity, reset stuff:
m_bEnvelopeEnded = true;
m_nPhase = 0;
m_nPhasePosition = 0;
return;
}
// else : m_bEnabled
if (m_bEnvelopeEnded)
{
// do nothing
// (specifically, don't change the values of m_bEnvelopeEnded,
// m_nPhase and m_nPhasePosition, as these will still be needed
// by SetLevels() should it be called again)
return;
}
// else : !m_bEnvelopeEnded
// Continue playing the same envelope ...
// increments the phaseposition within an envelope.
// also handles looping and resolution appropriately.
// Changes the level of the envelope accordingly
// through calling SetLevels() . This must be called after making
// any changes that will affect the output levels of the env controller!!
// SetLevels also handles left-right channel inverting
// increment phase position
m_nPhasePosition += m_nResolution;
// if this means we've gone past 16 (the end of a phase)
// then change phase, and if necessary, loop
// Refer to datasheet for meanings of (3) and (4) in following text
// w.r.t SAA1099 envelopes
// Note that we will always reach position (3) or (4), even if we keep toggling
// resolution from 4-bit to 3-bit and back, because the counter will always wrap to 0.
// In fact it's quite elegant:
// No matter how you increment and toggle and increment and toggle, the counter
// will at some point be either 0xe (either 4-bit mode or 3-bit mode) or 0xf (4-bit mode only).
// Depending on the mode, even if you change the mode, the next increment,
// or the one after it, will then take it to 0.
// 0xe + 2 (3bit mode) => 0x0
// 0xe + 1 (4bit mode) => 0xf
// 0xf + 1 (4bit mode) => 0x0
// 0xe -> (toggle 3bit mode to 4bit mode) => 0xf
// 0xe -> (toggle 4bit mode to 3bit mode) => 0xe
// 0xf -> (toggle 4bit mode to 3bit mode) => 0xe
//
// but there is a subtlety (of course), which is that any changes at point (3)
// can take place immediately you hit point (3), but changes at point (4) are actually
// only acted upon when the counter transitions from 0xe (or 0xf) to 0x0 (which also
// means that, for these looping envelopes, which are the ones that have a point(4),
// immediately after the counter wrapping to 0x0, a write to the env data register will
// NOT set the waveform and will NOT reset the phase/phaseposition (even though it
// will still let you toggle the 4bit/3bit mode, which will change the phaseposition LSB!)
// See test case: envext_34c
bool bProcessNewDataIfAvailable = false;
if (m_nPhasePosition >= 16)
{
m_nPhase++;
// if we should loop, then do so - and we've reached position (4)
// otherwise, if we shouldn't loop,
// then we've reached position (3) and so we say that
// we're ok for new data.
if (m_nPhase == m_nNumberOfPhases)
{
// at position (3) or (4)
if (!m_bLooping)
{
// position (3) only
// note that it seems that the sustain level is ALWAYS zero
// in the case of non-looping waveforms
m_bEnvelopeEnded = true;
bProcessNewDataIfAvailable = true;
}
else
{
// position (4) only
// note that any data already latched is ONLY acted upon
// at THIS point. If (after this Tick has completed) any new
// env data is written, it will NOT be acted upon, until
// we get back to position (4) again.
// this is why m_bEnvelopeEnded (which affects the behaviour
// of the SetEnvControl method) is FALSE here.
// See test case: envext_34c (as noted earlier)
m_bEnvelopeEnded = false;
// set phase pointer to start of envelope for loop
// and reset m_nPhasePosition
m_nPhase=0;
m_nPhasePosition -= 16;
bProcessNewDataIfAvailable = true;
}
}
else // (m_nPhase < m_nNumberOfPhases)
{
// not at position (3) or (4) ...
// (i.e., we're in the middle of an envelope with
// more than one phase. Specifically, we're in
// the middle of envelope 4 or 5 - the
// triangle envelopes - but that's not important)
// any commands sent to this envelope controller
// will be buffered. Set the flag to indicate this.
m_bEnvelopeEnded = false;
m_nPhasePosition -= 16;
}
}
else // (m_nPhasePosition < 16)
{
// still within the same phase;
// but, importantly, we are no longer at the start of the phase ...
// so new data cannot be acted on immediately, and must
// be buffered
m_bEnvelopeEnded = false;
// Phase and PhasePosition have already been updated.
// SetLevels() will need to be called to actually calculate
// the output 'level' of this envelope controller
}
// if we have new (buffered) data, now is the time to act on it
if (m_bNewData && bProcessNewDataIfAvailable)
{
m_bNewData = false;
SetNewEnvData(m_nNextData);
}
else
{
// ok, we didn't have any new buffered date to act on,
// so we just call SetLevels() to calculate the output level
// for whatever the current envelope is
SetLevels();
}
}
inline void CSAAEnv::SetLevels(void)
{
// sets m_nLeftLevel
// Also sets m_nRightLevel in terms of m_nLeftLevel
// and m_bInvertRightChannel
// m_nResolution: 1 means 4-bit resolution; 2 means 3-bit resolution. Resolution of envelope waveform.
// Note that this is handled 'immediately', and doesn't wait for synchronisation of
// the envelope waveform (this is important, see test case EnvExt_imm)
// It is therefore possible to switch between 4-bit and 3-bit resolution in the middle of
// an envelope waveform. if you are at an 'odd' phase position, you would be able to hear
// the difference. if you are at an 'even' phase position, the volume level for 4-bit
// and 3-bit would be the same.
// NOTE: additional test cases are required.
switch (m_nResolution)
{
case 1: // 4 bit res waveforms
default:
{
// special case: if envelope is not a looping one, and we're at the end
// then our level should be zero (all of the non-looping waveforms have
// a sustain level of zero):
if (m_bEnvelopeEnded && !m_bLooping)
m_nLeftLevel = 0;
else
m_nLeftLevel = m_pEnvData->nLevels[0][m_nPhase][m_nPhasePosition];
if (m_bInvertRightChannel)
m_nRightLevel = 15-m_nLeftLevel;
else
m_nRightLevel = m_nLeftLevel;
break;
}
case 2: // 3 bit res waveforms
{
// special case: if envelope is not a looping one, and we're at the end
// then our level should be zero (all of the non-looping waveforms have
// a sustain level of zero):
if (m_bEnvelopeEnded && !m_bLooping)
m_nLeftLevel = 0;
else
m_nLeftLevel = m_pEnvData->nLevels[1][m_nPhase][m_nPhasePosition];
if (m_bInvertRightChannel)
m_nRightLevel = 14-m_nLeftLevel;
else
m_nRightLevel = m_nLeftLevel;
break;
}
}
}
inline void CSAAEnv::SetNewEnvData(int nData)
{
// loads envgenerator's registers according to the bits set
// in nData
m_nPhase = 0;
m_nPhasePosition = 0;
m_pEnvData = &(cs_EnvData[(nData >> 1) & 0x07]);
m_bInvertRightChannel = ((nData & 0x01) == 0x01);
m_bClockExternally = ((nData & 0x20) == 0x20);
m_nNumberOfPhases = m_pEnvData->nNumberOfPhases;
m_bLooping = m_pEnvData->bLooping;
m_nResolution = (((nData & 0x10)==0x10) ? 2 : 1);
m_bEnabled = ((nData & 0x80) == 0x80);
if (m_bEnabled)
{
m_bEnvelopeEnded = false;
// is this right?
// YES. See test case EnvExt_34c (setting data multiple times
// when at a point (3) resets the waveform so you're no longer
// at a point (3).
}
else
{
// DISABLED - so set stuff accordingly
m_bEnvelopeEnded = true;
m_nPhase = 0;
m_nPhasePosition = 0;
}
SetLevels();
}

View File

@@ -1,54 +0,0 @@
// Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
//
// SAAEnv.h: interface for the CSAAEnv class.
//
//////////////////////////////////////////////////////////////////////
#ifndef SAAENV_H_INCLUDED
#define SAAENV_H_INCLUDED
class CSAAEnv
{
private:
int m_nLeftLevel, m_nRightLevel;
ENVDATA const * m_pEnvData;
bool m_bEnabled;
bool m_bInvertRightChannel;
BYTE m_nPhase;
BYTE m_nPhasePosition;
bool m_bEnvelopeEnded;
char m_nPhaseAdd[2];
char m_nCurrentPhaseAdd;
bool m_bLooping;
char m_nNumberOfPhases;
char m_nResolution;
char m_nInitialLevel;
bool m_bNewData;
BYTE m_nNextData;
bool m_bClockExternally;
static const ENVDATA cs_EnvData[8];
void Tick(void);
void SetLevels(void);
void SetNewEnvData(int nData);
public:
CSAAEnv();
~CSAAEnv();
void InternalClock(void);
void ExternalClock(void);
void SetEnvControl(int nData); // really just a BYTE
int LeftLevel(void) const;
int RightLevel(void) const;
bool IsActive(void) const;
};
inline bool CSAAEnv::IsActive(void) const
{
return m_bEnabled;
}
#endif // SAAENV_H_INCLUDED

View File

@@ -1,287 +0,0 @@
// Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
//
// SAAFreq.cpp: implementation of the CSAAFreq class.
// only 7-bit fractional accuracy on oscillator periods. I may consider fixing that.
//
//////////////////////////////////////////////////////////////////////
#include "SAASound.h"
#include "types.h"
#include "SAANoise.h"
#include "SAAEnv.h"
#include "SAAFreq.h"
#include "defns.h"
#ifdef SAAFREQ_FIXED_CLOCKRATE
// 'load in' the data for the static frequency lookup table
// precomputed for a fixed clockrate
// See: tools/freqdat.py
const unsigned long CSAAFreq::m_FreqTable[2048] = {
#include "SAAFreq.dat"
};
#else
unsigned long CSAAFreq::m_FreqTable[2048];
unsigned long CSAAFreq::m_nClockRate = 0;
#endif // SAAFREQ_FIXED_CLOCKRATE
const int INITIAL_LEVEL = 1;
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CSAAFreq::CSAAFreq(CSAANoise * const NoiseGenerator, CSAAEnv * const EnvGenerator)
:
m_nCounter(0),
m_nAdd(0),
m_nCounter_low(0),
m_nOversample(0),
m_nCounterLimit_low(1),
m_nLevel(INITIAL_LEVEL),
m_nCurrentOffset(0),
m_nCurrentOctave(0),
m_nNextOffset(0),
m_nNextOctave(0),
m_bIgnoreOffsetData(false),
m_bNewData(false),
m_bSync(false),
m_nSampleRate(SAMPLE_RATE_HZ),
m_pcConnectedNoiseGenerator(NoiseGenerator),
m_pcConnectedEnvGenerator(EnvGenerator),
m_nConnectedMode((NoiseGenerator == NULL) ? ((EnvGenerator == NULL) ? 0 : 1) : 2)
{
_SetClockRate(EXTERNAL_CLK_HZ);
SetAdd(); // current octave, current offset
}
CSAAFreq::~CSAAFreq()
{
// Nothing to do
}
void CSAAFreq::SetFreqOffset(BYTE nOffset)
{
// nOffset between 0 and 255
if (!m_bSync)
{
m_nNextOffset = nOffset;
m_bNewData=true;
if (m_nNextOctave==m_nCurrentOctave)
{
// According to Philips, if you send the SAA-1099
// new Octave data and then new Offset data in that
// order, on the next half-cycle of the current frequency
// generator, ONLY the octave data is acted upon.
// The offset data will be acted upon next time.
// ?? TEST CASE : if you set the octave and then the offset
// but the octave you set it to is the same one it already was.
// Will this ignore the offset data?
// Do you get the same behaviour if you set offset THEN octave
// even if you set octave to the same value it was before?
m_bIgnoreOffsetData=true;
}
}
else
{
// updates straightaway if m_bSync
m_bNewData=false;
m_bIgnoreOffsetData = false;
m_nCurrentOffset = nOffset;
m_nNextOffset = nOffset;
m_nCurrentOctave = m_nNextOctave;
SetAdd();
}
}
void CSAAFreq::SetFreqOctave(BYTE nOctave)
{
// nOctave between 0 and 7
if (!m_bSync)
{
m_nNextOctave = nOctave;
m_bNewData=true;
m_bIgnoreOffsetData = false;
}
else
{
// updates straightaway if m_bSync
m_bNewData=false;
m_bIgnoreOffsetData = false;
m_nCurrentOctave = nOctave;
m_nNextOctave = nOctave;
m_nCurrentOffset = m_nNextOffset;
SetAdd();
}
}
void CSAAFreq::UpdateOctaveOffsetData(void)
{
// loads the buffered new octave and new offset data into the current registers
// and sets up the new frequency for this frequency generator (i.e. sets up m_nAdd)
// - called during Sync, and called when waveform half-cycle completes
// How the SAA-1099 really treats new data:
// if only new octave data is present,
// then set new period based on just the octave data
// Otherwise, if only new offset data is present,
// then set new period based on just the offset data
// Otherwise, if new octave data is present, and new offset data is present,
// and the offset data was set BEFORE the octave data,
// then set new period based on both the octave and offset data
// Else, if the offset data came AFTER the new octave data
// then set new period based on JUST THE OCTAVE DATA, and continue
// signalling the offset data as 'new', so it will be acted upon
// next half-cycle
//
// Weird, I know. But that's how it works. Philips even documented as much.
if (!m_bNewData)
{
// optimise for the most common case! No new data!
return;
}
m_nCurrentOctave=m_nNextOctave;
if (!m_bIgnoreOffsetData)
{
m_nCurrentOffset=m_nNextOffset;
m_bNewData=false;
}
m_bIgnoreOffsetData=false;
SetAdd();
}
void CSAAFreq::_SetSampleRate(unsigned int nSampleRate)
{
m_nSampleRate = nSampleRate;
}
void CSAAFreq::_SetOversample(unsigned int oversample)
{
// oversample is a power of 2 i.e.
// if oversample == 2 then 4x oversample
// if oversample == 6 then 64x oversample
if (oversample < m_nOversample)
{
m_nCounter_low <<= (m_nOversample - oversample);
}
else
{
m_nCounter_low >>= (oversample - m_nOversample);
}
m_nCounterLimit_low = 1<<oversample;
m_nOversample = oversample;
}
#ifdef SAAFREQ_FIXED_CLOCKRATE
void CSAAFreq::_SetClockRate(int nClockRate)
{
// if SAAFREQ clock rate is hardcoded, then we don't support dynamically
// adjusting the SAA clock rate, so this is a no-op
}
#else
void CSAAFreq::_SetClockRate(int nClockRate)
{
// initialise the frequency table based on the SAA clockrate
// Each item in m_FreqTable corresponds to the frequency calculated by
// the standard formula (15625 << octave) / (511 - offset)
// then multiplied by 8192 (and represented as a long integer value).
// We are therefore using 12 bits (i.e. 2^12 = 4096) as fractional part.
// The reason we multiply by 8192, not 4096, is that we use this as a counter
// to toggle the oscillator state, so we need to count half-waves (i.e. twice
// the frequency)
//
// Finally, note that the standard formula corresponds to a 8MHz base clock
// so we rescale the final result by the ratio nClockRate/8000000
if (((unsigned long) nClockRate) != m_nClockRate)
{
m_nClockRate = nClockRate;
int ix = 0;
for (int nOctave = 0; nOctave < 8; nOctave++)
for (int nOffset = 0; nOffset < 256; nOffset++)
m_FreqTable[ix++] = (unsigned long)((8192.0 * 15625.0 * double(1 << nOctave) * (double(nClockRate) / 8000000.0)) / (511.0 - double(nOffset)));
}
}
#endif
int CSAAFreq::Tick(void)
{
// set to the absolute level (0 or 1)
if (m_bSync)
return 1;
m_nCounter += m_nAdd;
while (m_nCounter >= (m_nSampleRate<<12))
{
m_nCounter -= (m_nSampleRate<<12);
m_nCounter_low++;
if (m_nCounter_low >= m_nCounterLimit_low)
{
// period elapsed for (at least) one half-cycle of
// current frequency
m_nCounter_low = 0;
// flip state - from 0 to 1 or vice versa
m_nLevel = 1 - m_nLevel;
// trigger any connected devices
switch (m_nConnectedMode)
{
case 1:
// env trigger
m_pcConnectedEnvGenerator->InternalClock();
break;
case 2:
// noise trigger
m_pcConnectedNoiseGenerator->Trigger();
break;
default:
// do nothing
break;
}
// get new frequency (set period length m_nAdd) if new data is waiting:
UpdateOctaveOffsetData();
}
}
return m_nLevel;
}
void CSAAFreq::SetAdd(void)
{
// nOctave between 0 and 7; nOffset between 0 and 255
// Used to be:
// m_nAdd = (15625 << nOctave) / (511 - nOffset);
// Now just table lookup:
m_nAdd = m_FreqTable[m_nCurrentOctave<<8 | m_nCurrentOffset];
}
void CSAAFreq::Sync(bool bSync)
{
m_bSync = bSync;
// update straightaway if m_bSync
if (m_bSync)
{
m_nCounter = 0;
m_nCounter_low = 0;
// this seems to need to be required to make the Fred59 SPACE DEMO audio work correctly
m_nLevel = INITIAL_LEVEL;
m_nCurrentOctave=m_nNextOctave;
m_nCurrentOffset=m_nNextOffset;
SetAdd();
}
}

View File

@@ -1,141 +0,0 @@
/*
// Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
//
// Precalculated oscillator frequency period steps
// Higher scaling for better accuracy.
//
// After construction, it's important to SetSampleRate before
// trying to use the generator.
// (Just because the CSAANoise object has a default samplerate
// doesn't mean you should rely on it)
//
//////////////////////////////////////////////////////////////////////
*/
250489 , 250980 , 251473 , 251969 , 252465 , 252964 , 253465 , 253968 , 254473 , 254980 , 255489 , 256000 , 256513 , 257028 , 257545 , 258065 ,
258586 , 259109 , 259635 , 260163 , 260692 , 261224 , 261759 , 262295 , 262834 , 263374 , 263918 , 264463 , 265010 , 265560 , 266112 , 266667 ,
267223 , 267782 , 268344 , 268908 , 269474 , 270042 , 270613 , 271186 , 271762 , 272340 , 272921 , 273504 , 274090 , 274678 , 275269 , 275862 ,
276458 , 277056 , 277657 , 278261 , 278867 , 279476 , 280088 , 280702 , 281319 , 281938 , 282561 , 283186 , 283814 , 284444 , 285078 , 285714 ,
286353 , 286996 , 287640 , 288288 , 288939 , 289593 , 290249 , 290909 , 291572 , 292237 , 292906 , 293578 , 294253 , 294931 , 295612 , 296296 ,
296984 , 297674 , 298368 , 299065 , 299766 , 300469 , 301176 , 301887 , 302600 , 303318 , 304038 , 304762 , 305489 , 306220 , 306954 , 307692 ,
308434 , 309179 , 309927 , 310680 , 311436 , 312195 , 312958 , 313725 , 314496 , 315271 , 316049 , 316832 , 317618 , 318408 , 319202 , 320000 ,
320802 , 321608 , 322418 , 323232 , 324051 , 324873 , 325700 , 326531 , 327366 , 328205 , 329049 , 329897 , 330749 , 331606 , 332468 , 333333 ,
334204 , 335079 , 335958 , 336842 , 337731 , 338624 , 339523 , 340426 , 341333 , 342246 , 343164 , 344086 , 345013 , 345946 , 346883 , 347826 ,
348774 , 349727 , 350685 , 351648 , 352617 , 353591 , 354571 , 355556 , 356546 , 357542 , 358543 , 359551 , 360563 , 361582 , 362606 , 363636 ,
364672 , 365714 , 366762 , 367816 , 368876 , 369942 , 371014 , 372093 , 373178 , 374269 , 375367 , 376471 , 377581 , 378698 , 379822 , 380952 ,
382090 , 383234 , 384384 , 385542 , 386707 , 387879 , 389058 , 390244 , 391437 , 392638 , 393846 , 395062 , 396285 , 397516 , 398754 , 400000 ,
401254 , 402516 , 403785 , 405063 , 406349 , 407643 , 408946 , 410256 , 411576 , 412903 , 414239 , 415584 , 416938 , 418301 , 419672 , 421053 ,
422442 , 423841 , 425249 , 426667 , 428094 , 429530 , 430976 , 432432 , 433898 , 435374 , 436860 , 438356 , 439863 , 441379 , 442907 , 444444 ,
445993 , 447552 , 449123 , 450704 , 452297 , 453901 , 455516 , 457143 , 458781 , 460432 , 462094 , 463768 , 465455 , 467153 , 468864 , 470588 ,
472325 , 474074 , 475836 , 477612 , 479401 , 481203 , 483019 , 484848 , 486692 , 488550 , 490421 , 492308 , 494208 , 496124 , 498054 , 500000 ,
500978 , 501961 , 502947 , 503937 , 504931 , 505929 , 506931 , 507937 , 508946 , 509960 , 510978 , 512000 , 513026 , 514056 , 515091 , 516129 ,
517172 , 518219 , 519270 , 520325 , 521385 , 522449 , 523517 , 524590 , 525667 , 526749 , 527835 , 528926 , 530021 , 531120 , 532225 , 533333 ,
534447 , 535565 , 536688 , 537815 , 538947 , 540084 , 541226 , 542373 , 543524 , 544681 , 545842 , 547009 , 548180 , 549356 , 550538 , 551724 ,
552916 , 554113 , 555315 , 556522 , 557734 , 558952 , 560175 , 561404 , 562637 , 563877 , 565121 , 566372 , 567627 , 568889 , 570156 , 571429 ,
572707 , 573991 , 575281 , 576577 , 577878 , 579186 , 580499 , 581818 , 583144 , 584475 , 585812 , 587156 , 588506 , 589862 , 591224 , 592593 ,
593968 , 595349 , 596737 , 598131 , 599532 , 600939 , 602353 , 603774 , 605201 , 606635 , 608076 , 609524 , 610979 , 612440 , 613909 , 615385 ,
616867 , 618357 , 619855 , 621359 , 622871 , 624390 , 625917 , 627451 , 628993 , 630542 , 632099 , 633663 , 635236 , 636816 , 638404 , 640000 ,
641604 , 643216 , 644836 , 646465 , 648101 , 649746 , 651399 , 653061 , 654731 , 656410 , 658098 , 659794 , 661499 , 663212 , 664935 , 666667 ,
668407 , 670157 , 671916 , 673684 , 675462 , 677249 , 679045 , 680851 , 682667 , 684492 , 686327 , 688172 , 690027 , 691892 , 693767 , 695652 ,
697548 , 699454 , 701370 , 703297 , 705234 , 707182 , 709141 , 711111 , 713092 , 715084 , 717087 , 719101 , 721127 , 723164 , 725212 , 727273 ,
729345 , 731429 , 733524 , 735632 , 737752 , 739884 , 742029 , 744186 , 746356 , 748538 , 750733 , 752941 , 755162 , 757396 , 759644 , 761905 ,
764179 , 766467 , 768769 , 771084 , 773414 , 775758 , 778116 , 780488 , 782875 , 785276 , 787692 , 790123 , 792570 , 795031 , 797508 , 800000 ,
802508 , 805031 , 807571 , 810127 , 812698 , 815287 , 817891 , 820513 , 823151 , 825806 , 828479 , 831169 , 833876 , 836601 , 839344 , 842105 ,
844884 , 847682 , 850498 , 853333 , 856187 , 859060 , 861953 , 864865 , 867797 , 870748 , 873720 , 876712 , 879725 , 882759 , 885813 , 888889 ,
891986 , 895105 , 898246 , 901408 , 904594 , 907801 , 911032 , 914286 , 917563 , 920863 , 924188 , 927536 , 930909 , 934307 , 937729 , 941176 ,
944649 , 948148 , 951673 , 955224 , 958801 , 962406 , 966038 , 969697 , 973384 , 977099 , 980843 , 984615 , 988417 , 992248 , 996109 , 1000000 ,
1001957 , 1003922 , 1005894 , 1007874 , 1009862 , 1011858 , 1013861 , 1015873 , 1017893 , 1019920 , 1021956 , 1024000 , 1026052 , 1028112 , 1030181 , 1032258 ,
1034343 , 1036437 , 1038540 , 1040650 , 1042770 , 1044898 , 1047035 , 1049180 , 1051335 , 1053498 , 1055670 , 1057851 , 1060041 , 1062241 , 1064449 , 1066667 ,
1068894 , 1071130 , 1073375 , 1075630 , 1077895 , 1080169 , 1082452 , 1084746 , 1087049 , 1089362 , 1091684 , 1094017 , 1096360 , 1098712 , 1101075 , 1103448 ,
1105832 , 1108225 , 1110629 , 1113043 , 1115468 , 1117904 , 1120350 , 1122807 , 1125275 , 1127753 , 1130243 , 1132743 , 1135255 , 1137778 , 1140312 , 1142857 ,
1145414 , 1147982 , 1150562 , 1153153 , 1155756 , 1158371 , 1160998 , 1163636 , 1166287 , 1168950 , 1171625 , 1174312 , 1177011 , 1179724 , 1182448 , 1185185 ,
1187935 , 1190698 , 1193473 , 1196262 , 1199063 , 1201878 , 1204706 , 1207547 , 1210402 , 1213270 , 1216152 , 1219048 , 1221957 , 1224880 , 1227818 , 1230769 ,
1233735 , 1236715 , 1239709 , 1242718 , 1245742 , 1248780 , 1251834 , 1254902 , 1257985 , 1261084 , 1264198 , 1267327 , 1270471 , 1273632 , 1276808 , 1280000 ,
1283208 , 1286432 , 1289673 , 1292929 , 1296203 , 1299492 , 1302799 , 1306122 , 1309463 , 1312821 , 1316195 , 1319588 , 1322997 , 1326425 , 1329870 , 1333333 ,
1336815 , 1340314 , 1343832 , 1347368 , 1350923 , 1354497 , 1358090 , 1361702 , 1365333 , 1368984 , 1372654 , 1376344 , 1380054 , 1383784 , 1387534 , 1391304 ,
1395095 , 1398907 , 1402740 , 1406593 , 1410468 , 1414365 , 1418283 , 1422222 , 1426184 , 1430168 , 1434174 , 1438202 , 1442254 , 1446328 , 1450425 , 1454545 ,
1458689 , 1462857 , 1467049 , 1471264 , 1475504 , 1479769 , 1484058 , 1488372 , 1492711 , 1497076 , 1501466 , 1505882 , 1510324 , 1514793 , 1519288 , 1523810 ,
1528358 , 1532934 , 1537538 , 1542169 , 1546828 , 1551515 , 1556231 , 1560976 , 1565749 , 1570552 , 1575385 , 1580247 , 1585139 , 1590062 , 1595016 , 1600000 ,
1605016 , 1610063 , 1615142 , 1620253 , 1625397 , 1630573 , 1635783 , 1641026 , 1646302 , 1651613 , 1656958 , 1662338 , 1667752 , 1673203 , 1678689 , 1684211 ,
1689769 , 1695364 , 1700997 , 1706667 , 1712375 , 1718121 , 1723906 , 1729730 , 1735593 , 1741497 , 1747440 , 1753425 , 1759450 , 1765517 , 1771626 , 1777778 ,
1783972 , 1790210 , 1796491 , 1802817 , 1809187 , 1815603 , 1822064 , 1828571 , 1835125 , 1841727 , 1848375 , 1855072 , 1861818 , 1868613 , 1875458 , 1882353 ,
1889299 , 1896296 , 1903346 , 1910448 , 1917603 , 1924812 , 1932075 , 1939394 , 1946768 , 1954198 , 1961686 , 1969231 , 1976834 , 1984496 , 1992218 , 2000000 ,
2003914 , 2007843 , 2011788 , 2015748 , 2019724 , 2023715 , 2027723 , 2031746 , 2035785 , 2039841 , 2043912 , 2048000 , 2052104 , 2056225 , 2060362 , 2064516 ,
2068687 , 2072874 , 2077079 , 2081301 , 2085540 , 2089796 , 2094070 , 2098361 , 2102669 , 2106996 , 2111340 , 2115702 , 2120083 , 2124481 , 2128898 , 2133333 ,
2137787 , 2142259 , 2146751 , 2151261 , 2155789 , 2160338 , 2164905 , 2169492 , 2174098 , 2178723 , 2183369 , 2188034 , 2192719 , 2197425 , 2202151 , 2206897 ,
2211663 , 2216450 , 2221258 , 2226087 , 2230937 , 2235808 , 2240700 , 2245614 , 2250549 , 2255507 , 2260486 , 2265487 , 2270510 , 2275556 , 2280624 , 2285714 ,
2290828 , 2295964 , 2301124 , 2306306 , 2311512 , 2316742 , 2321995 , 2327273 , 2332574 , 2337900 , 2343249 , 2348624 , 2354023 , 2359447 , 2364896 , 2370370 ,
2375870 , 2381395 , 2386946 , 2392523 , 2398126 , 2403756 , 2409412 , 2415094 , 2420804 , 2426540 , 2432304 , 2438095 , 2443914 , 2449761 , 2455635 , 2461538 ,
2467470 , 2473430 , 2479419 , 2485437 , 2491484 , 2497561 , 2503667 , 2509804 , 2515971 , 2522167 , 2528395 , 2534653 , 2540943 , 2547264 , 2553616 , 2560000 ,
2566416 , 2572864 , 2579345 , 2585859 , 2592405 , 2598985 , 2605598 , 2612245 , 2618926 , 2625641 , 2632391 , 2639175 , 2645995 , 2652850 , 2659740 , 2666667 ,
2673629 , 2680628 , 2687664 , 2694737 , 2701847 , 2708995 , 2716180 , 2723404 , 2730667 , 2737968 , 2745308 , 2752688 , 2760108 , 2767568 , 2775068 , 2782609 ,
2790191 , 2797814 , 2805479 , 2813187 , 2820937 , 2828729 , 2836565 , 2844444 , 2852368 , 2860335 , 2868347 , 2876404 , 2884507 , 2892655 , 2900850 , 2909091 ,
2917379 , 2925714 , 2934097 , 2942529 , 2951009 , 2959538 , 2968116 , 2976744 , 2985423 , 2994152 , 3002933 , 3011765 , 3020649 , 3029586 , 3038576 , 3047619 ,
3056716 , 3065868 , 3075075 , 3084337 , 3093656 , 3103030 , 3112462 , 3121951 , 3131498 , 3141104 , 3150769 , 3160494 , 3170279 , 3180124 , 3190031 , 3200000 ,
3210031 , 3220126 , 3230284 , 3240506 , 3250794 , 3261146 , 3271565 , 3282051 , 3292605 , 3303226 , 3313916 , 3324675 , 3335505 , 3346405 , 3357377 , 3368421 ,
3379538 , 3390728 , 3401993 , 3413333 , 3424749 , 3436242 , 3447811 , 3459459 , 3471186 , 3482993 , 3494881 , 3506849 , 3518900 , 3531034 , 3543253 , 3555556 ,
3567944 , 3580420 , 3592982 , 3605634 , 3618375 , 3631206 , 3644128 , 3657143 , 3670251 , 3683453 , 3696751 , 3710145 , 3723636 , 3737226 , 3750916 , 3764706 ,
3778598 , 3792593 , 3806691 , 3820896 , 3835206 , 3849624 , 3864151 , 3878788 , 3893536 , 3908397 , 3923372 , 3938462 , 3953668 , 3968992 , 3984436 , 4000000 ,
4007828 , 4015686 , 4023576 , 4031496 , 4039448 , 4047431 , 4055446 , 4063492 , 4071571 , 4079681 , 4087824 , 4096000 , 4104208 , 4112450 , 4120724 , 4129032 ,
4137374 , 4145749 , 4154158 , 4162602 , 4171079 , 4179592 , 4188139 , 4196721 , 4205339 , 4213992 , 4222680 , 4231405 , 4240166 , 4248963 , 4257796 , 4266667 ,
4275574 , 4284519 , 4293501 , 4302521 , 4311579 , 4320675 , 4329810 , 4338983 , 4348195 , 4357447 , 4366738 , 4376068 , 4385439 , 4394850 , 4404301 , 4413793 ,
4423326 , 4432900 , 4442516 , 4452174 , 4461874 , 4471616 , 4481400 , 4491228 , 4501099 , 4511013 , 4520971 , 4530973 , 4541020 , 4551111 , 4561247 , 4571429 ,
4581655 , 4591928 , 4602247 , 4612613 , 4623025 , 4633484 , 4643991 , 4654545 , 4665148 , 4675799 , 4686499 , 4697248 , 4708046 , 4718894 , 4729792 , 4740741 ,
4751740 , 4762791 , 4773893 , 4785047 , 4796253 , 4807512 , 4818824 , 4830189 , 4841608 , 4853081 , 4864608 , 4876190 , 4887828 , 4899522 , 4911271 , 4923077 ,
4934940 , 4946860 , 4958838 , 4970874 , 4982968 , 4995122 , 5007335 , 5019608 , 5031941 , 5044335 , 5056790 , 5069307 , 5081886 , 5094527 , 5107232 , 5120000 ,
5132832 , 5145729 , 5158690 , 5171717 , 5184810 , 5197970 , 5211196 , 5224490 , 5237852 , 5251282 , 5264781 , 5278351 , 5291990 , 5305699 , 5319481 , 5333333 ,
5347258 , 5361257 , 5375328 , 5389474 , 5403694 , 5417989 , 5432361 , 5446809 , 5461333 , 5475936 , 5490617 , 5505376 , 5520216 , 5535135 , 5550136 , 5565217 ,
5580381 , 5595628 , 5610959 , 5626374 , 5641873 , 5657459 , 5673130 , 5688889 , 5704735 , 5720670 , 5736695 , 5752809 , 5769014 , 5785311 , 5801700 , 5818182 ,
5834758 , 5851429 , 5868195 , 5885057 , 5902017 , 5919075 , 5936232 , 5953488 , 5970845 , 5988304 , 6005865 , 6023529 , 6041298 , 6059172 , 6077151 , 6095238 ,
6113433 , 6131737 , 6150150 , 6168675 , 6187311 , 6206061 , 6224924 , 6243902 , 6262997 , 6282209 , 6301538 , 6320988 , 6340557 , 6360248 , 6380062 , 6400000 ,
6420063 , 6440252 , 6460568 , 6481013 , 6501587 , 6522293 , 6543131 , 6564103 , 6585209 , 6606452 , 6627832 , 6649351 , 6671010 , 6692810 , 6714754 , 6736842 ,
6759076 , 6781457 , 6803987 , 6826667 , 6849498 , 6872483 , 6895623 , 6918919 , 6942373 , 6965986 , 6989761 , 7013699 , 7037801 , 7062069 , 7086505 , 7111111 ,
7135889 , 7160839 , 7185965 , 7211268 , 7236749 , 7262411 , 7288256 , 7314286 , 7340502 , 7366906 , 7393502 , 7420290 , 7447273 , 7474453 , 7501832 , 7529412 ,
7557196 , 7585185 , 7613383 , 7641791 , 7670412 , 7699248 , 7728302 , 7757576 , 7787072 , 7816794 , 7846743 , 7876923 , 7907336 , 7937984 , 7968872 , 8000000 ,
8015656 , 8031373 , 8047151 , 8062992 , 8078895 , 8094862 , 8110891 , 8126984 , 8143141 , 8159363 , 8175649 , 8192000 , 8208417 , 8224900 , 8241449 , 8258065 ,
8274747 , 8291498 , 8308316 , 8325203 , 8342159 , 8359184 , 8376278 , 8393443 , 8410678 , 8427984 , 8445361 , 8462810 , 8480331 , 8497925 , 8515593 , 8533333 ,
8551148 , 8569038 , 8587002 , 8605042 , 8623158 , 8641350 , 8659619 , 8677966 , 8696391 , 8714894 , 8733475 , 8752137 , 8770878 , 8789700 , 8808602 , 8827586 ,
8846652 , 8865801 , 8885033 , 8904348 , 8923747 , 8943231 , 8962801 , 8982456 , 9002198 , 9022026 , 9041943 , 9061947 , 9082040 , 9102222 , 9122494 , 9142857 ,
9163311 , 9183857 , 9204494 , 9225225 , 9246050 , 9266968 , 9287982 , 9309091 , 9330296 , 9351598 , 9372998 , 9394495 , 9416092 , 9437788 , 9459584 , 9481481 ,
9503480 , 9525581 , 9547786 , 9570093 , 9592506 , 9615023 , 9637647 , 9660377 , 9683215 , 9706161 , 9729216 , 9752381 , 9775656 , 9799043 , 9822542 , 9846154 ,
9869880 , 9893720 , 9917676 , 9941748 , 9965937 , 9990244 , 10014670 , 10039216 , 10063882 , 10088670 , 10113580 , 10138614 , 10163772 , 10189055 , 10214464 , 10240000 ,
10265664 , 10291457 , 10317380 , 10343434 , 10369620 , 10395939 , 10422392 , 10448980 , 10475703 , 10502564 , 10529563 , 10556701 , 10583979 , 10611399 , 10638961 , 10666667 ,
10694517 , 10722513 , 10750656 , 10778947 , 10807388 , 10835979 , 10864721 , 10893617 , 10922667 , 10951872 , 10981233 , 11010753 , 11040431 , 11070270 , 11100271 , 11130435 ,
11160763 , 11191257 , 11221918 , 11252747 , 11283747 , 11314917 , 11346260 , 11377778 , 11409471 , 11441341 , 11473389 , 11505618 , 11538028 , 11570621 , 11603399 , 11636364 ,
11669516 , 11702857 , 11736390 , 11770115 , 11804035 , 11838150 , 11872464 , 11906977 , 11941691 , 11976608 , 12011730 , 12047059 , 12082596 , 12118343 , 12154303 , 12190476 ,
12226866 , 12263473 , 12300300 , 12337349 , 12374622 , 12412121 , 12449848 , 12487805 , 12525994 , 12564417 , 12603077 , 12641975 , 12681115 , 12720497 , 12760125 , 12800000 ,
12840125 , 12880503 , 12921136 , 12962025 , 13003175 , 13044586 , 13086262 , 13128205 , 13170418 , 13212903 , 13255663 , 13298701 , 13342020 , 13385621 , 13429508 , 13473684 ,
13518152 , 13562914 , 13607973 , 13653333 , 13698997 , 13744966 , 13791246 , 13837838 , 13884746 , 13931973 , 13979522 , 14027397 , 14075601 , 14124138 , 14173010 , 14222222 ,
14271777 , 14321678 , 14371930 , 14422535 , 14473498 , 14524823 , 14576512 , 14628571 , 14681004 , 14733813 , 14787004 , 14840580 , 14894545 , 14948905 , 15003663 , 15058824 ,
15114391 , 15170370 , 15226766 , 15283582 , 15340824 , 15398496 , 15456604 , 15515152 , 15574144 , 15633588 , 15693487 , 15753846 , 15814672 , 15875969 , 15937743 , 16000000 ,
16031311 , 16062745 , 16094303 , 16125984 , 16157791 , 16189723 , 16221782 , 16253968 , 16286282 , 16318725 , 16351297 , 16384000 , 16416834 , 16449799 , 16482897 , 16516129 ,
16549495 , 16582996 , 16616633 , 16650407 , 16684318 , 16718367 , 16752556 , 16786885 , 16821355 , 16855967 , 16890722 , 16925620 , 16960663 , 16995851 , 17031185 , 17066667 ,
17102296 , 17138075 , 17174004 , 17210084 , 17246316 , 17282700 , 17319239 , 17355932 , 17392781 , 17429787 , 17466951 , 17504274 , 17541756 , 17579399 , 17617204 , 17655172 ,
17693305 , 17731602 , 17770065 , 17808696 , 17847495 , 17886463 , 17925602 , 17964912 , 18004396 , 18044053 , 18083885 , 18123894 , 18164080 , 18204444 , 18244989 , 18285714 ,
18326622 , 18367713 , 18408989 , 18450450 , 18492099 , 18533937 , 18575964 , 18618182 , 18660592 , 18703196 , 18745995 , 18788991 , 18832184 , 18875576 , 18919169 , 18962963 ,
19006961 , 19051163 , 19095571 , 19140187 , 19185012 , 19230047 , 19275294 , 19320755 , 19366430 , 19412322 , 19458432 , 19504762 , 19551313 , 19598086 , 19645084 , 19692308 ,
19739759 , 19787440 , 19835351 , 19883495 , 19931873 , 19980488 , 20029340 , 20078431 , 20127764 , 20177340 , 20227160 , 20277228 , 20327543 , 20378109 , 20428928 , 20480000 ,
20531328 , 20582915 , 20634761 , 20686869 , 20739241 , 20791878 , 20844784 , 20897959 , 20951407 , 21005128 , 21059126 , 21113402 , 21167959 , 21222798 , 21277922 , 21333333 ,
21389034 , 21445026 , 21501312 , 21557895 , 21614776 , 21671958 , 21729443 , 21787234 , 21845333 , 21903743 , 21962466 , 22021505 , 22080863 , 22140541 , 22200542 , 22260870 ,
22321526 , 22382514 , 22443836 , 22505495 , 22567493 , 22629834 , 22692521 , 22755556 , 22818942 , 22882682 , 22946779 , 23011236 , 23076056 , 23141243 , 23206799 , 23272727 ,
23339031 , 23405714 , 23472779 , 23540230 , 23608069 , 23676301 , 23744928 , 23813953 , 23883382 , 23953216 , 24023460 , 24094118 , 24165192 , 24236686 , 24308605 , 24380952 ,
24453731 , 24526946 , 24600601 , 24674699 , 24749245 , 24824242 , 24899696 , 24975610 , 25051988 , 25128834 , 25206154 , 25283951 , 25362229 , 25440994 , 25520249 , 25600000 ,
25680251 , 25761006 , 25842271 , 25924051 , 26006349 , 26089172 , 26172524 , 26256410 , 26340836 , 26425806 , 26511327 , 26597403 , 26684039 , 26771242 , 26859016 , 26947368 ,
27036304 , 27125828 , 27215947 , 27306667 , 27397993 , 27489933 , 27582492 , 27675676 , 27769492 , 27863946 , 27959044 , 28054795 , 28151203 , 28248276 , 28346021 , 28444444 ,
28543554 , 28643357 , 28743860 , 28845070 , 28946996 , 29049645 , 29153025 , 29257143 , 29362007 , 29467626 , 29574007 , 29681159 , 29789091 , 29897810 , 30007326 , 30117647 ,
30228782 , 30340741 , 30453532 , 30567164 , 30681648 , 30796992 , 30913208 , 31030303 , 31148289 , 31267176 , 31386973 , 31507692 , 31629344 , 31751938 , 31875486 , 32000000 ,
32062622 , 32125490 , 32188605 , 32251969 , 32315582 , 32379447 , 32443564 , 32507937 , 32572565 , 32637450 , 32702595 , 32768000 , 32833667 , 32899598 , 32965795 , 33032258 ,
33098990 , 33165992 , 33233266 , 33300813 , 33368635 , 33436735 , 33505112 , 33573770 , 33642710 , 33711934 , 33781443 , 33851240 , 33921325 , 33991701 , 34062370 , 34133333 ,
34204593 , 34276151 , 34348008 , 34420168 , 34492632 , 34565401 , 34638478 , 34711864 , 34785563 , 34859574 , 34933902 , 35008547 , 35083512 , 35158798 , 35234409 , 35310345 ,
35386609 , 35463203 , 35540130 , 35617391 , 35694989 , 35772926 , 35851204 , 35929825 , 36008791 , 36088106 , 36167770 , 36247788 , 36328160 , 36408889 , 36489978 , 36571429 ,
36653244 , 36735426 , 36817978 , 36900901 , 36984199 , 37067873 , 37151927 , 37236364 , 37321185 , 37406393 , 37491991 , 37577982 , 37664368 , 37751152 , 37838337 , 37925926 ,
38013921 , 38102326 , 38191142 , 38280374 , 38370023 , 38460094 , 38550588 , 38641509 , 38732861 , 38824645 , 38916865 , 39009524 , 39102625 , 39196172 , 39290168 , 39384615 ,
39479518 , 39574879 , 39670702 , 39766990 , 39863747 , 39960976 , 40058680 , 40156863 , 40255528 , 40354680 , 40454321 , 40554455 , 40655087 , 40756219 , 40857855 , 40960000 ,
41062657 , 41165829 , 41269521 , 41373737 , 41478481 , 41583756 , 41689567 , 41795918 , 41902813 , 42010256 , 42118252 , 42226804 , 42335917 , 42445596 , 42555844 , 42666667 ,
42778068 , 42890052 , 43002625 , 43115789 , 43229551 , 43343915 , 43458886 , 43574468 , 43690667 , 43807487 , 43924933 , 44043011 , 44161725 , 44281081 , 44401084 , 44521739 ,
44643052 , 44765027 , 44887671 , 45010989 , 45134986 , 45259669 , 45385042 , 45511111 , 45637883 , 45765363 , 45893557 , 46022472 , 46152113 , 46282486 , 46413598 , 46545455 ,
46678063 , 46811429 , 46945559 , 47080460 , 47216138 , 47352601 , 47489855 , 47627907 , 47766764 , 47906433 , 48046921 , 48188235 , 48330383 , 48473373 , 48617211 , 48761905 ,
48907463 , 49053892 , 49201201 , 49349398 , 49498489 , 49648485 , 49799392 , 49951220 , 50103976 , 50257669 , 50412308 , 50567901 , 50724458 , 50881988 , 51040498 , 51200000 ,
51360502 , 51522013 , 51684543 , 51848101 , 52012698 , 52178344 , 52345048 , 52512821 , 52681672 , 52851613 , 53022654 , 53194805 , 53368078 , 53542484 , 53718033 , 53894737 ,
54072607 , 54251656 , 54431894 , 54613333 , 54795987 , 54979866 , 55164983 , 55351351 , 55538983 , 55727891 , 55918089 , 56109589 , 56302405 , 56496552 , 56692042 , 56888889 ,
57087108 , 57286713 , 57487719 , 57690141 , 57893993 , 58099291 , 58306050 , 58514286 , 58724014 , 58935252 , 59148014 , 59362319 , 59578182 , 59795620 , 60014652 , 60235294 ,
60457565 , 60681481 , 60907063 , 61134328 , 61363296 , 61593985 , 61826415 , 62060606 , 62296578 , 62534351 , 62773946 , 63015385 , 63258687 , 63503876 , 63750973 , 64000000

View File

@@ -1,72 +0,0 @@
// Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
//
// SAAFreq.h: interface for the CSAAFreq class.
// Note about Samplerates: 0=44100, 1=22050; 2=11025
//
//////////////////////////////////////////////////////////////////////
#ifndef SAAFREQ_H_INCLUDE
#define SAAFREQ_H_INCLUDE
#include "defns.h"
class CSAAFreq
{
private:
#ifdef SAAFREQ_FIXED_CLOCKRATE
// 'load in' the data for the static frequency lookup table
// precomputed for a fixed clockrate
// See: tools/freqdat.py
const static unsigned long m_FreqTable[2048];
#else
// we'll calculate the frequency lookup table at runtime.
static unsigned long m_FreqTable[2048];
static unsigned long m_nClockRate;
#endif
unsigned long m_nCounter;
unsigned long m_nAdd;
unsigned long m_nCounter_low;
unsigned int m_nOversample;
unsigned long m_nCounterLimit_low;
int m_nLevel;
int m_nCurrentOffset;
int m_nCurrentOctave;
int m_nNextOffset;
int m_nNextOctave;
bool m_bIgnoreOffsetData;
bool m_bNewData;
bool m_bSync;
unsigned long m_nSampleRate;
CSAANoise * const m_pcConnectedNoiseGenerator;
CSAAEnv * const m_pcConnectedEnvGenerator;
const int m_nConnectedMode; // 0 = nothing; 1 = envgenerator; 2 = noisegenerator
void UpdateOctaveOffsetData(void);
void SetAdd(void);
public:
CSAAFreq(CSAANoise * const pcNoiseGenerator, CSAAEnv * const pcEnvGenerator);
~CSAAFreq();
void SetFreqOffset(BYTE nOffset);
void SetFreqOctave(BYTE nOctave);
void _SetSampleRate(unsigned int nSampleRate);
void _SetOversample(unsigned int oversample);
void _SetClockRate(int nClockRate);
void Sync(bool bSync);
int Tick(void);
int Level(void) const;
};
inline int CSAAFreq::Level(void) const
{
if (m_bSync)
return 1;
return m_nLevel;
}
#endif // SAAFREQ_H_INCLUDE

View File

@@ -1,488 +0,0 @@
// Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
//
// SAAImpl.cpp: implementation of the CSAASound class.
// the bones of the 'virtual SAA-1099' emulation
//
// the actual sound generation is carried out in the other classes;
// this class provides the output stage and the external interface only
//
//////////////////////////////////////////////////////////////////////
#include "SAASound.h"
#include "types.h"
#include "SAAImpl.h"
#include "defns.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CSAASoundInternal::CSAASoundInternal()
:
m_chip(),
m_uParam(0),
m_uParamRate(0),
m_nClockRate(EXTERNAL_CLK_HZ),
m_nSampleRate(SAMPLE_RATE_HZ),
m_nOversample(DEFAULT_OVERSAMPLE),
#if defined(DEBUGSAA) || defined(USE_CONFIG_FILE)
m_bHighpass(false),
m_nDebugSample(0)
#else
m_bHighpass(false)
#endif
{
#ifdef USE_CONFIG_FILE
m_Config.ReadConfig();
#endif
#if defined(DEBUGSAA)
m_dbgfile.open(_T(DEBUG_SAA_REGISTER_LOG), std::ios_base::out);
m_pcmfile.open(_T(DEBUG_SAA_PCM_LOG), std::ios_base::out | std::ios_base::binary);
#elif defined(USE_CONFIG_FILE)
if (m_Config.m_bGenerateRegisterLogs)
m_dbgfile.open(m_Config.m_strRegisterLogPath, std::ios_base::out);
if (m_Config.m_bGeneratePcmLogs)
m_pcmfile.open(m_Config.m_strPcmOutputPath, std::ios_base::out | std::ios_base::binary);
if (m_Config.m_bGeneratePcmLogs && m_Config.m_bGeneratePcmSeparateChannels)
{
for (int i = 0; i < 6; i++)
{
m_channel_pcmfile[i].open(m_Config.getChannelPcmOutputPath(i), std::ios_base::out | std::ios_base::binary);
}
}
#endif
// set parameters
// TODO support defaults and overrides from config file
// m_chip.SetSoundParameters(SAAP_FILTER | SAAP_11025 | SAAP_8BIT | SAAP_MONO);
// reset the virtual SAA
// m_chip.Clear();
m_chip._SetClockRate(m_nClockRate);
m_chip._SetOversample(m_nOversample);
}
CSAASoundInternal::~CSAASoundInternal()
{
//
}
//////////////////////////////////////////////////////////////////////
// CSAASound members
//////////////////////////////////////////////////////////////////////
void CSAASoundInternal::SetClockRate(unsigned int nClockRate)
{
m_nClockRate = nClockRate;
m_chip._SetClockRate(m_nClockRate);
}
void CSAASoundInternal::Clear(void)
{
// reinitialises virtual SAA:
// sets reg 28 to 0x02; - sync and disabled
// sets regs 00-31 (except 28) to 0x00;
// sets reg 28 to 0x00;
// sets current reg to 0
WriteAddressData(28,2);
for (int i=31; i>=0; i--)
{
if (i!=28) WriteAddressData(i,0);
}
WriteAddressData(28,0);
WriteAddress(0);
}
void CSAASoundInternal::WriteData(BYTE nData)
{
// originated from an OUT 255,d call
m_chip._WriteData(nData);
#if defined(DEBUGSAA) || defined(USE_CONFIG_FILE)
#ifdef USE_CONFIG_FILE
if (m_Config.m_bGenerateRegisterLogs)
{
#endif
m_dbgfile << m_nDebugSample << " " << (int)m_chip._ReadAddress() << ":" << (int)nData << std::endl;
#ifdef USE_CONFIG_FILE
}
#endif
#endif
}
void CSAASoundInternal::WriteAddress(BYTE nReg)
{
// originated from an OUT 511,r call
m_chip._WriteAddress(nReg);
#if defined(DEBUGSAA) || defined(USE_CONFIG_FILE)
#ifdef USE_CONFIG_FILE
if (m_Config.m_bGenerateRegisterLogs)
{
#endif
m_dbgfile << m_nDebugSample << " " << (int)nReg << ":";
if (nReg==24)
{
m_dbgfile << "<!ENVO!>";
}
else if (nReg==25)
{
m_dbgfile << "<!ENV1!>";
}
m_dbgfile << std::endl;
#ifdef USE_CONFIG_FILE
}
#endif
#endif
}
void CSAASoundInternal::WriteAddressData(BYTE nReg, BYTE nData)
{
// performs WriteAddress(nReg) followed by WriteData(nData)
m_chip._WriteAddress(nReg);
m_chip._WriteData(nData);
}
#if 1
BYTE CSAASoundInternal::ReadAddress(void)
{
// Not a real hardware function of the SAA-1099, which is write-only
return(m_chip._ReadAddress());
}
#else
BYTE CSAASoundInternal::ReadAddress(void)
{
// Not a real hardware function of the SAA-1099, which is write-only
return(0);
}
#endif
void CSAASoundInternal::SetSoundParameters(SAAPARAM uParam)
{
// set samplerate properties from uParam (deprecated but still supported)
unsigned int nSampleRate = m_nSampleRate;
switch (uParam & SAAP_MASK_SAMPLERATE)
{
case SAAP_44100:
nSampleRate = 44100;
m_uParamRate = (m_uParamRate & ~SAAP_MASK_SAMPLERATE) | SAAP_44100;
break;
case SAAP_22050:
nSampleRate = 22050;
m_uParamRate = (m_uParamRate & ~SAAP_MASK_SAMPLERATE) | SAAP_22050;
break;
case SAAP_11025:
nSampleRate = 11025;
m_uParamRate = (m_uParamRate & ~SAAP_MASK_SAMPLERATE) | SAAP_11025;
break;
case 0:// change nothing!
default:
break;
}
if (nSampleRate != m_nSampleRate)
{
m_nSampleRate = nSampleRate;
m_chip._SetSampleRate(m_nSampleRate);
}
// set filter properties from uParam
m_uParam = (m_uParam & ~SAAP_MASK_FILTER) | (uParam & SAAP_MASK_FILTER);
m_bHighpass=true;
}
void CSAASoundInternal::SetSampleRate(unsigned int nSampleRate)
{
if (nSampleRate != m_nSampleRate)
{
m_nSampleRate = nSampleRate;
m_chip._SetSampleRate(m_nSampleRate);
}
}
void CSAASoundInternal::SetOversample(unsigned int nOversample)
{
if (nOversample != m_nOversample)
{
m_nOversample = nOversample;
m_chip._SetOversample(m_nOversample);
}
}
SAAPARAM CSAASoundInternal::GetCurrentSoundParameters(void)
{
return m_uParam | m_uParamRate;
}
unsigned short CSAASoundInternal::GetCurrentBytesPerSample(void)
{
// 16 bit stereo => 4 bytes per sample
return 4;
}
/*static*/ unsigned short CSAASound::GetBytesPerSample(SAAPARAM uParam)
{
// 16 bit stereo => 4 bytes per sample
switch (uParam & (SAAP_MASK_CHANNELS | SAAP_MASK_BITDEPTH))
{
case SAAP_STEREO | SAAP_16BIT:
return 4;
default:
return 0;
}
}
unsigned long CSAASoundInternal::GetCurrentSampleRate(void)
{
return CSAASound::GetSampleRate(m_uParamRate);
}
/*static*/ unsigned long CSAASound::GetSampleRate(SAAPARAM uParam) // static member function
{
switch (uParam & SAAP_MASK_SAMPLERATE)
{
case SAAP_11025:
return 11025;
case SAAP_22050:
return 22050;
case SAAP_44100:
return 44100;
default:
return 0;
}
}
#if defined(USE_CONFIG_FILE) || (defined(DEFAULT_BOOST) && DEFAULT_BOOST>1)
#define DO_BOOST
#endif
void scale_for_output(unsigned int left_input, unsigned int right_input,
double oversample_scalar, bool highpass, double boost,
double& filterout_z1_left, double& filterout_z1_right,
BYTE* &pBuffer)
{
double float_left = (double)left_input;
double float_right = (double)right_input;
float_left /= oversample_scalar;
float_right /= oversample_scalar;
// scale output into good range
float_left *= DEFAULT_UNBOOSTED_MULTIPLIER;
float_right *= DEFAULT_UNBOOSTED_MULTIPLIER;
if (highpass)
{
/* cutoff = 5 Hz (say)
const double b1 = exp(-2.0 * M_PI * (Fc/Fs))
const double a0 = 1.0 - b1;
*/
const double b1 = 0.99928787;
const double a0 = 1.0 - b1;
filterout_z1_left = float_left * a0 + filterout_z1_left * b1;
filterout_z1_right = float_right * a0 + filterout_z1_right * b1;
float_left -= filterout_z1_left;
float_right -= filterout_z1_right;
}
// multiply by boost, if defined
#if defined(DO_BOOST)
float_left *= boost;
float_right *= boost;
#endif
// convert to 16-bit signed range with hard clipping
signed short left_output = (signed short)(float_left > 32767 ? 32767 : float_left < -32768 ? -32768 : float_left);
signed short right_output = (signed short)(float_right > 32767 ? 32767 : float_right < -32768 ? -32768 : float_right);
*pBuffer++ = left_output & 0x00ff;
*pBuffer++ = (left_output >> 8) & 0x00ff;
*pBuffer++ = right_output & 0x00ff;
*pBuffer++ = (right_output >> 8) & 0x00ff;
}
void CSAASoundInternal::GenerateMany(BYTE* pBuffer, unsigned long nSamples)
{
unsigned int left_mixed, right_mixed;
#if defined(DEBUGSAA) || defined(USE_CONFIG_FILE)
BYTE* pBufferStart = pBuffer;
unsigned long nTotalSamples = nSamples;
#endif
#if defined(DO_BOOST)
#if defined(USE_CONFIG_FILE)
double nBoost = m_Config.m_nBoost;
#else
double nBoost = DEFAULT_BOOST;
#endif
#else
double nBoost = 1.0;
#endif
double oversample = double(1 << m_nOversample);
#if defined(USE_CONFIG_FILE)
static double filterout_z1_left_0 = 0, filterout_z1_right_0 = 0;
static double filterout_z1_left_1 = 0, filterout_z1_right_1 = 0;
static double filterout_z1_left_2 = 0, filterout_z1_right_2 = 0;
static double filterout_z1_left_3 = 0, filterout_z1_right_3 = 0;
static double filterout_z1_left_4 = 0, filterout_z1_right_4 = 0;
static double filterout_z1_left_5 = 0, filterout_z1_right_5 = 0;
if (m_Config.m_bGeneratePcmLogs && m_Config.m_bGeneratePcmSeparateChannels)
{
unsigned int left0, right0, left1, right1, left2, right2, left3, right3, left4, right4, left5, right5;
BYTE* pChannelBufferPtr[6] = { m_pChannelBuffer[0], m_pChannelBuffer[1], m_pChannelBuffer[2], m_pChannelBuffer[3], m_pChannelBuffer[4], m_pChannelBuffer[5] };
while (nSamples--)
{
m_chip._TickAndOutputSeparate(left_mixed, right_mixed,
left0, right0,
left1, right1,
left2, right2,
left3, right3,
left4, right4,
left5, right5);
scale_for_output(left_mixed, right_mixed, oversample, m_bHighpass, nBoost, filterout_z1_left_mixed, filterout_z1_right_mixed, pBuffer);
// and the separate channels
scale_for_output(left0, right0, oversample, m_bHighpass, nBoost, filterout_z1_left_0, filterout_z1_right_0, pChannelBufferPtr[0]);
scale_for_output(left1, right1, oversample, m_bHighpass, nBoost, filterout_z1_left_1, filterout_z1_right_1, pChannelBufferPtr[1]);
scale_for_output(left2, right2, oversample, m_bHighpass, nBoost, filterout_z1_left_2, filterout_z1_right_2, pChannelBufferPtr[2]);
scale_for_output(left3, right3, oversample, m_bHighpass, nBoost, filterout_z1_left_3, filterout_z1_right_3, pChannelBufferPtr[3]);
scale_for_output(left4, right4, oversample, m_bHighpass, nBoost, filterout_z1_left_4, filterout_z1_right_4, pChannelBufferPtr[4]);
scale_for_output(left5, right5, oversample, m_bHighpass, nBoost, filterout_z1_left_5, filterout_z1_right_5, pChannelBufferPtr[5]);
// flush channel output PCM buffers when full
if (pChannelBufferPtr[0] >= m_pChannelBuffer[0] + CHANNEL_BUFFER_SIZE)
{
for (int i = 0; i < 6; i++)
{
m_channel_pcmfile[i].write((const char*)m_pChannelBuffer[i], CHANNEL_BUFFER_SIZE);
pChannelBufferPtr[i] = m_pChannelBuffer[i];
}
}
}
// flush remaining channel PCM output data
if (pChannelBufferPtr[0] >= m_pChannelBuffer[0])
{
for (int i = 0; i < 6; i++)
{
m_channel_pcmfile[i].write((const char*)m_pChannelBuffer[i], pChannelBufferPtr[i]-m_pChannelBuffer[i]);
}
}
}
else
{
#endif
while (nSamples--)
{
m_chip._TickAndOutputStereo(left_mixed, right_mixed);
scale_for_output(left_mixed, right_mixed, oversample, m_bHighpass, nBoost, filterout_z1_left_mixed, filterout_z1_right_mixed, pBuffer);
}
#if defined(USE_CONFIG_FILE)
}
#endif
#if defined(DEBUGSAA) || defined(USE_CONFIG_FILE)
#ifdef USE_CONFIG_FILE
if (m_Config.m_bGeneratePcmLogs)
{
#endif
m_pcmfile.write((const char *)pBufferStart, nTotalSamples * (unsigned long)GetCurrentBytesPerSample());
m_nDebugSample += nTotalSamples;
#ifdef USE_CONFIG_FILE
}
#endif
#endif
}
///////////////////////////////////////////////////////
LPCSAASOUND SAAAPI CreateCSAASound(void)
{
return (new CSAASoundInternal);
}
void SAAAPI DestroyCSAASound(LPCSAASOUND object)
{
delete (object);
}
/* thoughts on lowpass filtering as part of oversampling.
I tried this and really it didn't seem to make a lot of (audible) difference.
// lowpass oversample filter adds complexity and not particularly audibly better than simple averaging.
// use_lowpass_oversample_filter_average_output adds an additional averaging step to the output of the oversample
// filter. this seems critical, because without this, the raw output of the lowpass filter is full of aliases
// If use_lowpass_oversample_filter is False, then the _average_output flag is ignored.
// Default, use_lowpass_oversample_filter is False, it sounds just fine really.
//#define USE_LOWPASS_OVERSAMPLE_FILTER
#undef USE_LOWPASS_OVERSAMPLE_FILTER
//#define USE_LOWPASS_OVERSAMPLE_FILTER_AVERAGE_OUTPUT
#undef USE_LOWPASS_OVERSAMPLE_FILTER_AVERAGE_OUTPUT
#ifdef USE_LOWPASS_OVERSAMPLE_FILTER
static double oversample_lp_filterout_z1_left_stages[10] = { 0,0,0,0,0,0,0,0,0,0 };
static double oversample_lp_filterout_z1_right_stages[10] = { 0,0,0,0,0,0,0,0,0,0 };
double averaged_filterout_left = 0.0, averaged_filterout_right = 0.0;
const int nStages = 10;
for (int i = 0; i < 1 << m_nOversample; i++)
{
Noise[0]->Tick();
Noise[1]->Tick();
f_left = f_right = 0;
for (int c = 0; c < 6; c++)
{
Amp[c]->TickAndOutputStereo(temp_left, temp_right);
f_left += (double)temp_left;
f_right += (double)temp_right;
}
// apply lowpass here.
// HACK: ASSUME m_nOversample is 64 (I was experimenting only using the 64x oversample anyway)
// therefore Fs = 44100*64
// let's set Fc = 10kHz
// so Fc/Fs = 0.00354308390022675736961451247166
// const double b1 = exp(-2.0 * M_PI * (Fc/Fs))
// const double a0 = 1.0 - b1;
// const double b1 = 0.9779841137335348363722276130195;
const double b1 = 0.977;
const double a0 = 1.0 - b1;
oversample_lp_filterout_z1_left_stages[0] = f_left * a0 + oversample_lp_filterout_z1_left_stages[0] * b1;
for (int stage = 1; stage < nStages; stage++)
oversample_lp_filterout_z1_left_stages[stage] = oversample_lp_filterout_z1_left_stages[stage - 1] * a0 + oversample_lp_filterout_z1_left_stages[stage] * b1;
oversample_lp_filterout_z1_right_stages[0] = f_right * a0 + oversample_lp_filterout_z1_right_stages[0] * b1;
for (int stage = 1; stage < nStages; stage++)
oversample_lp_filterout_z1_right_stages[stage] = oversample_lp_filterout_z1_right_stages[stage - 1] * a0 + oversample_lp_filterout_z1_right_stages[stage] * b1;
#ifdef USE_LOWPASS_OVERSAMPLE_FILTER_AVERAGE_OUTPUT
averaged_filterout_left += oversample_lp_filterout_4z1_left;
averaged_filterout_right += oversample_lp_filterout_4z1_right;
#endif
}
// by the end of this loop we will have computed the oversample lowpass filter m_nOversample times
// and yielded exactly ONE sample output.
#ifdef USE_LOWPASS_OVERSAMPLE_FILTER_AVERAGE_OUTPUT
f_left = averaged_filterout_left / (1 << m_nOversample);
f_right = averaged_filterout_right / (1 << m_nOversample);
#else
f_left = oversample_lp_filterout_z1_left_stages[nStages - 1];
f_right = oversample_lp_filterout_z1_right_stages[nStages - 1];
#endif
#else
// do the simple 1/N averaging which is easier and sounds good enough
#endif
*/

View File

@@ -1,77 +0,0 @@
// Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
//
// This is the internal implementation (header file) of the SAASound object.
// This is done so that the external interface to the object always stays the same
// (SAASound.h) even though the internal object can change
// .. Meaning future releases don't require relinking everyone elses code against
// the updated saasound stuff
//
//////////////////////////////////////////////////////////////////////
#ifndef SAAIMPL_H_INCLUDED
#define SAAIMPL_H_INCLUDED
#include "SAASound.h"
#include "SAADevice.h"
#ifdef USE_CONFIG_FILE
#include "SAAConfig.h"
#endif
#if defined(DEBUGSAA) || defined(USE_CONFIG_FILE)
#include <ios>
#include <iostream>
#include <fstream>
#if defined(USE_CONFIG_FILE)
const int CHANNEL_BUFFER_SIZE=1024;
#endif
#endif
class CSAASoundInternal : public CSAASound
{
private:
CSAADevice m_chip;
int m_uParam, m_uParamRate;
unsigned int m_nClockRate;
unsigned int m_nSampleRate;
unsigned int m_nOversample;
bool m_bHighpass;
double filterout_z1_left_mixed = 0;
double filterout_z1_right_mixed = 0;
#ifdef USE_CONFIG_FILE
SAAConfig m_Config;
#endif
#if defined(DEBUGSAA) || defined(USE_CONFIG_FILE)
unsigned long m_nDebugSample;
std::ofstream m_dbgfile, m_pcmfile;
#if defined(USE_CONFIG_FILE)
std::ofstream m_channel_pcmfile[6];
BYTE m_pChannelBuffer[6][CHANNEL_BUFFER_SIZE];
#endif
#endif
public:
CSAASoundInternal();
~CSAASoundInternal();
void SetClockRate(unsigned int nClockRate);
void SetSampleRate(unsigned int nClockRate);
void SetOversample(unsigned int nOversample);
void SetSoundParameters(SAAPARAM uParam);
void WriteAddress(BYTE nReg);
void WriteData(BYTE nData);
void WriteAddressData(BYTE nReg, BYTE nData);
BYTE ReadAddress(void);
void Clear(void);
SAAPARAM GetCurrentSoundParameters(void);
unsigned long GetCurrentSampleRate(void);
static unsigned long GetSampleRate(SAAPARAM uParam);
unsigned short GetCurrentBytesPerSample(void);
static unsigned short GetBytesPerSample(SAAPARAM uParam);
void GenerateMany(BYTE * pBuffer, unsigned long nSamples);
};
#endif // SAAIMPL_H_INCLUDED

View File

@@ -1,180 +0,0 @@
// Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
//
// SAANoise.cpp: implementation of the CSAANoise class.
// One noise generator
//
// After construction, it's important to SetSampleRate before
// trying to use the generator.
// (Just because the CSAANoise object has a default samplerate
// doesn't mean you should rely on it)
//
//////////////////////////////////////////////////////////////////////
#include "SAASound.h"
#include "types.h"
#include "SAANoise.h"
#include "defns.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CSAANoise::CSAANoise()
:
m_nCounter(0),
m_nCounter_low(0),
m_nOversample(0),
m_nCounterLimit_low(1),
m_bSync(false),
m_nSampleRate(SAMPLE_RATE_HZ),
m_nSourceMode(0),
m_nRand(1)
{
_SetClockRate(EXTERNAL_CLK_HZ);
m_nAdd = m_nAddBase;
}
CSAANoise::CSAANoise(unsigned long seed)
:
m_nCounter(0),
m_nCounter_low(0),
m_nOversample(0),
m_nCounterLimit_low(1),
m_bSync(false),
m_nSampleRate(SAMPLE_RATE_HZ),
m_nSourceMode(0),
m_nRand(seed)
{
_SetClockRate(EXTERNAL_CLK_HZ);
m_nAdd = m_nAddBase;
}
CSAANoise::~CSAANoise()
{
// Nothing to do
}
void CSAANoise::_SetClockRate(int nClockRate)
{
// at 8MHz the clock rate is 31.250kHZ
// This is simply the clock rate divided by 256 i.e. 2^8
// We then shift this by 2^12 (like the Freq) for better
// period accuracy. So that's the same as shifting by (12-8)
m_nAddBase = nClockRate << (12 - 8);
}
void CSAANoise::Seed(unsigned long seed)
{
m_nRand = seed;
}
void CSAANoise::SetSource(int nSource)
{
m_nSourceMode = nSource;
m_nAdd = m_nAddBase >> m_nSourceMode;
}
void CSAANoise::Trigger(void)
{
// Trigger only does anything useful when we're
// clocking from the frequency generator - i.e
// if bUseFreqGen = true (i.e. SourceMode = 3)
// So if we're clocking from the noise generator
// clock (ie, SourceMode = 0, 1 or 2) then do nothing
// No point actually checking m_bSync here ... because if sync is true,
// then frequency generators won't actually be generating Trigger pulses
// so we wouldn't even get here!
// EXCEPT - cool edge case: if sync is set, then actually the Noise Generator
// is triggered on EVERY CLOCK PULSE (i.e. 8MHz noise). So indeed it is correct
// to not check for sync here. NEEDS TEST CASE.
if (m_nSourceMode == 3)
{
ChangeLevel();
}
}
void CSAANoise::Tick(void)
{
// Tick only does anything useful when we're
// clocking from the noise generator clock
// (ie, SourceMode = 0, 1 or 2)
// So, if SourceMode = 3 (ie, we're clocking from a
// frequency generator ==> bUseFreqGen = true)
// then do nothing
if ( (!m_bSync) && (m_nSourceMode!=3) )
{
m_nCounter += m_nAdd;
while (m_nCounter >= (m_nSampleRate<<12))
{
m_nCounter -= (m_nSampleRate<<12);
m_nCounter_low++;
if (m_nCounter_low >= m_nCounterLimit_low)
{
m_nCounter_low = 0;
ChangeLevel();
}
}
}
}
void CSAANoise::Sync(bool bSync)
{
if (bSync)
{
m_nCounter = 0;
m_nCounter_low = 0;
}
m_bSync = bSync;
}
void CSAANoise::_SetSampleRate(int nSampleRate)
{
m_nSampleRate = nSampleRate;
}
void CSAANoise::_SetOversample(unsigned int oversample)
{
// oversample is a power of 2 i.e.
// if oversample == 2 then 4x oversample
// if oversample == 6 then 64x oversample
if (oversample < m_nOversample)
{
m_nCounter_low <<= (m_nOversample - oversample);
}
else
{
m_nCounter_low >>= (oversample - m_nOversample);
}
m_nCounterLimit_low = 1<<oversample;
m_nOversample = oversample;
}
inline void CSAANoise::ChangeLevel(void)
{
/*
https://www.vogons.org/viewtopic.php?f=9&t=51695
SAA1099P noise generator as documented by Jepael
18-bit Galois LFSR
Feedback polynomial = x^18 + x^11 + x^1
Period = 2^18-1 = 262143 bits
Verified to match recorded noise from my SAA1099P
*/
if (m_nRand & 1)
{
m_nRand = (m_nRand >> 1) ^ 0x20400;
}
else
{
m_nRand >>= 1;
}
}

View File

@@ -1,54 +0,0 @@
// Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
//
// SAANoise.h: interface for the CSAANoise class.
//
//////////////////////////////////////////////////////////////////////
#ifndef SAANOISE_H_INCLUDED
#define SAANOISE_H_INCLUDED
class CSAANoise
{
private:
unsigned long m_nCounter;
unsigned long m_nAdd;
unsigned long m_nCounter_low;
unsigned int m_nOversample;
unsigned long m_nCounterLimit_low;
bool m_bSync; // see description of "SYNC" bit of register 28
unsigned long m_nSampleRate; // = 44100 when RateMode=0, for example
int m_nSourceMode;
unsigned long m_nAddBase; // nAdd for 31.25 kHz noise at 44.1 kHz samplerate
// pseudo-random number generator
unsigned long m_nRand;
void ChangeLevel(void);
public:
CSAANoise();
CSAANoise(unsigned long seed);
~CSAANoise();
void SetSource(int nSource);
void Trigger(void);
void _SetSampleRate(int nSampleRate);
void _SetOversample(unsigned int oversample);
void _SetClockRate(int nClockRate);
void Seed(unsigned long seed);
void Tick(void);
int Level(void) const;
void Sync(bool bSync);
};
inline int CSAANoise::Level(void) const
{
// returns 0 or 1
return (m_nRand & 0x00000001);
}
#endif // SAANOISE_H_INCLUDED

View File

@@ -1,100 +0,0 @@
// Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
//
// Thanks to this file (and associated header file) you can now
// use CSAASound from within a standard 'C' program
//
//////////////////////////////////////////////////////////////////////
#include "SAASound.h"
#include "types.h"
#include "SAAEnv.h"
#include "SAANoise.h"
#include "SAAFreq.h"
#include "SAAAmp.h"
#include "SAASound.h"
#include "SAAImpl.h"
SAASND SAAAPI newSAASND(void)
{
return (SAASND)(new CSAASoundInternal());
}
void SAAAPI deleteSAASND(SAASND object)
{
delete (LPCSAASOUND)(object);
}
void SAAAPI SAASNDSetClockRate(SAASND object, unsigned int nClockRate)
{
((LPCSAASOUND)(object))->SetClockRate(nClockRate);
}
void SAAAPI SAASNDSetSoundParameters(SAASND object, SAAPARAM uParam)
{
((LPCSAASOUND)(object))->SetSoundParameters(uParam);
}
void SAAAPI SAASNDWriteAddress(SAASND object, BYTE nReg)
{
((LPCSAASOUND)(object))->WriteAddress(nReg);
}
void SAAAPI SAASNDWriteData(SAASND object, BYTE nData)
{
((LPCSAASOUND)(object))->WriteData(nData);
}
void SAAAPI SAASNDWriteAddressData(SAASND object, BYTE nReg, BYTE nData)
{
((LPCSAASOUND)(object))->WriteAddressData(nReg, nData);
}
void SAAAPI SAASNDClear(SAASND object)
{
((LPCSAASOUND)(object))->Clear();
}
SAAPARAM SAAAPI SAASNDGetCurrentSoundParameters(SAASND object)
{
return ((LPCSAASOUND)(object))->GetCurrentSoundParameters();
}
unsigned short SAAAPI SAASNDGetCurrentBytesPerSample(SAASND object)
{
return ((LPCSAASOUND)(object))->GetCurrentBytesPerSample();
}
unsigned short SAAAPI SAASNDGetBytesPerSample(SAAPARAM uParam)
{
return CSAASound::GetBytesPerSample(uParam);
}
unsigned long SAAAPI SAASNDGetCurrentSampleRate(SAASND object)
{
return ((LPCSAASOUND)(object))->GetCurrentSampleRate();
}
unsigned long SAAAPI SAASNDGetSampleRate(SAAPARAM uParam)
{
return CSAASound::GetSampleRate(uParam);
}
void SAAAPI SAASNDGenerateMany(SAASND object, BYTE * pBuffer, unsigned long nSamples)
{
((LPCSAASOUND)(object))->GenerateMany(pBuffer, nSamples);
}
void SAAAPI SAASNDSetSampleRate(SAASND object, unsigned int nSampleRate)
{
return ((LPCSAASOUND)(object))->SetSampleRate(nSampleRate);
}
void SAAAPI SAASNDSetOversample(SAASND object, unsigned int nOversample)
{
return ((LPCSAASOUND)(object))->SetOversample(nOversample);
}
BYTE SAAAPI SAASNDReadAddress(SAASND object)
{
return ((LPCSAASOUND)(object))->ReadAddress();
}

View File

@@ -1,102 +0,0 @@
// Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
//
// **********
// * PUBLIC *
// **********
//
// SAASndC.h: "C-style" interface for the CSAASound class.
//
//////////////////////////////////////////////////////////////////////
#ifndef SAASNDC_H_INCLUDED
#define SAASNDC_H_INCLUDED
#ifdef _MSC_VER
#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
#endif
#ifndef SAASOUND_H_INCLUDED
// Parameters for use with SetSoundParameters, for example,
// SetSoundParameters(SAAP_NOFILTER | SAAP_44100 | SAAP_16BIT | SAAP_STEREO);
#define SAAP_FILTER_HIGHPASS_SIMPLE 0x00000400
#define SAAP_FILTER_OVERSAMPLE64x 0x00000300
#define SAAP_FILTER_OVERSAMPLE2x 0x00000200
#define SAAP_FILTER SAAP_FILTER_OVERSAMPLE2x
#define SAAP_NOFILTER 0x00000100
#define SAAP_44100 0x00000030
#define SAAP_22050 0x00000020
#define SAAP_11025 0x00000010
#define SAAP_16BIT 0x0000000c
#define SAAP_8BIT 0x00000004
#define SAAP_STEREO 0x00000003
#define SAAP_MONO 0x00000001
// Bitmasks for use with GetCurrentSoundParameters, for example,
// unsigned long CurrentSampleRateParameter = GetCurrentSoundParameters()
#define SAAP_MASK_FILTER 0x00000f00
#define SAAP_MASK_FILTER_HIGHPASS 0x00000c00
#define SAAP_MASK_FILTER_OVERSAMPLE 0x00000300
#define SAAP_MASK_SAMPLERATE 0x000000030
#define SAAP_MASK_BITDEPTH 0x0000000c
#define SAAP_MASK_CHANNELS 0x00000003
typedef unsigned long SAAPARAM;
#ifndef BYTE
#define BYTE unsigned char
#endif
#ifdef WIN32
#ifndef WINAPI
#define WINAPI __stdcall
#endif
#define EXTAPI __declspec(dllexport) WINAPI
#else // Win32
#ifndef WINAPI
#define WINAPI /**/
#endif
#define EXTAPI /**/
#endif // Win32
#endif // SAASOUND_H_INCLUDED
typedef void * SAASND;
// the following are implemented as calls, etc, to a class.
#ifdef __cplusplus
extern "C" {
#endif
SAASND EXTAPI newSAASND(void);
void EXTAPI deleteSAASND(SAASND object);
void EXTAPI SAASNDSetSoundParameters(SAASND object, SAAPARAM uParam);
void EXTAPI SAASNDWriteAddress(SAASND object, BYTE nReg);
void EXTAPI SAASNDWriteData(SAASND object, BYTE nData);
void EXTAPI SAASNDWriteAddressData(SAASND object, BYTE nReg, BYTE nData);
void EXTAPI SAASNDClear(SAASND object);
BYTE EXTAPI SAASNDReadAddress(SAASND object);
SAAPARAM EXTAPI SAASNDGetCurrentSoundParameters(SAASND object);
unsigned short EXTAPI SAASNDGetCurrentBytesPerSample(SAASND object);
unsigned short EXTAPI SAASNDGetBytesPerSample(SAAPARAM uParam);
unsigned long EXTAPI SAASNDGetCurrentSampleRate(SAASND object);
unsigned long EXTAPI SAASNDGetSampleRate(SAAPARAM uParam);
void EXTAPI SAASNDGenerateMany(SAASND object, BYTE * pBuffer, unsigned long nSamples);
void EXTAPI SAASNDSetClockRate(SAASND object, unsigned int nClockRate);
void EXTAPI SAASNDSetSampleRate(SAASND object, unsigned int nSampleRate);
void EXTAPI SAASNDSetOversample(SAASND object, unsigned int nOversample);
#ifdef __cplusplus
}; // extern "C"
#endif
#endif // SAASNDC_H_INCLUDED

View File

@@ -1,13 +0,0 @@
// Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
//
// SAASound.cpp - dummy function
//
//////////////////////////////////////////////////////////////////////
#include <stdio.h>
// Provide something so the compiler doesn't optimise us out of existance
int SomeFunction ()
{
return 42;
}

View File

@@ -1,130 +0,0 @@
// Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
//
// SAASound.h: interface for the CSAASound class.
//
// This corresponds to the public (exported) DLL interface, so all
// APIs and client factory methods belong here.
//
// Compatibility notes : the intention is for this to be fully backwards
// compatible across minor and patch versions. Any backwards breaking changes
// should be reflected as a major version increment. New functionality can be added
// in minor versions so long as backwards compatiblity is maintained
//
// Version 3.3.0 (4th Dec 2018)
//
//////////////////////////////////////////////////////////////////////
#ifndef SAASOUND_H_INCLUDED
#define SAASOUND_H_INCLUDED
// define this if you want to output diagnostic text and PCM files
//#define DEBUGSAA
// Parameters for use with SetSoundParameters, for example,
// SetSoundParameters(SAAP_NOFILTER | SAAP_44100 | SAA_16BIT | SAA_STEREO);
// SAAP_FILTER_HIGHPASS_SIMPLE can be ORd with SAAP_FILTER_OVERSAMPLE64x/2x
#define SAAP_FILTER_HIGHPASS_SIMPLE 0x00000400
#define SAAP_FILTER_OVERSAMPLE64x 0x00000300
#define SAAP_FILTER_OVERSAMPLE2x 0x00000200
#define SAAP_FILTER SAAP_FILTER_OVERSAMPLE2x
#define SAAP_NOFILTER 0x00000100
#define SAAP_44100 0x00000030
#define SAAP_22050 0x00000020
#define SAAP_11025 0x00000010
#define SAAP_16BIT 0x0000000c
#define SAAP_8BIT 0x00000004
#define SAAP_STEREO 0x00000003
#define SAAP_MONO 0x00000001
// Bitmasks for use with GetCurrentSoundParameters, for example,
// unsigned long CurrentSampleRateParameter = GetCurrentSoundParameters()
#define SAAP_MASK_FILTER 0x00000f00
#define SAAP_MASK_FILTER_HIGHPASS 0x00000c00
#define SAAP_MASK_FILTER_OVERSAMPLE 0x00000300
#define SAAP_MASK_SAMPLERATE 0x000000030
#define SAAP_MASK_BITDEPTH 0x0000000c
#define SAAP_MASK_CHANNELS 0x00000003
typedef unsigned long SAAPARAM;
#ifndef BYTE
#define BYTE unsigned char
#endif
#ifdef _WIN32
#define SAAAPI _stdcall
#else
#define SAAAPI
#endif
#ifdef __cplusplus
class CSAASound
{
public:
virtual ~CSAASound() { }
virtual void SetSoundParameters (SAAPARAM uParam) = 0;
virtual void WriteAddress (BYTE nReg) = 0;
virtual void WriteData (BYTE nData) = 0;
virtual void WriteAddressData (BYTE nReg, BYTE nData) = 0;
virtual void Clear () = 0;
virtual BYTE ReadAddress () = 0;
virtual SAAPARAM GetCurrentSoundParameters () = 0;
virtual unsigned long GetCurrentSampleRate () = 0;
static unsigned long GetSampleRate (SAAPARAM uParam);
virtual unsigned short GetCurrentBytesPerSample () = 0;
static unsigned short GetBytesPerSample (SAAPARAM uParam);
virtual void GenerateMany (BYTE * pBuffer, unsigned long nSamples) = 0;
virtual void SetClockRate(unsigned int nClockRate) = 0;
virtual void SetSampleRate(unsigned int nSampleRate) = 0;
virtual void SetOversample(unsigned int nOversample) = 0;
};
typedef class CSAASound * LPCSAASOUND;
LPCSAASOUND SAAAPI CreateCSAASound(void);
void SAAAPI DestroyCSAASound(LPCSAASOUND object);
#endif // __cplusplus
#ifdef __cplusplus
extern "C" {
#endif
typedef void * SAASND;
// "C-style" interface for the CSAASound class
SAASND SAAAPI newSAASND(void);
void SAAAPI deleteSAASND(SAASND object);
void SAAAPI SAASNDSetSoundParameters(SAASND object, SAAPARAM uParam);
void SAAAPI SAASNDWriteAddress(SAASND object, BYTE nReg);
void SAAAPI SAASNDWriteData(SAASND object, BYTE nData);
void SAAAPI SAASNDWriteAddressData(SAASND object, BYTE nReg, BYTE nData);
void SAAAPI SAASNDClear(SAASND object);
SAAPARAM SAAAPI SAASNDGetCurrentSoundParameters(SAASND object);
unsigned short SAAAPI SAASNDGetCurrentBytesPerSample(SAASND object);
unsigned short SAAAPI SAASNDGetBytesPerSample(SAAPARAM uParam);
unsigned long SAAAPI SAASNDGetCurrentSampleRate(SAASND object);
unsigned long SAAAPI SAASNDGetSampleRate(SAAPARAM uParam);
void SAAAPI SAASNDGenerateMany(SAASND object, BYTE * pBuffer, unsigned long nSamples);
void SAAAPI SAASNDSetClockRate(SAASND object, unsigned int nClockRate);
void SAAAPI SAASNDSetSampleRate(SAASND object, unsigned int nSampleRate);
void SAAAPI SAASNDSetOversample(SAASND object, unsigned int nOversample);
BYTE SAAAPI SAASNDReadAddress(SAASND object);
#ifdef __cplusplus
}; // extern "C"
#endif
#endif // SAASOUND_H_INCLUDED

View File

@@ -1,59 +0,0 @@
// Part of SAASound copyright 2020 Dave Hooper <dave@beermex.com>
//
// defns.h: compile-time configuration parameters
//
//////////////////////////////////////////////////////////////////////
#ifndef DEFNS_H_INCLUDED
#define DEFNS_H_INCLUDED
#define HAVE_CONFIG_H
#ifdef HAVE_CONFIG_H
// using CMAKE
#include "saasound_cmake_config.h"
#else
// initial default SAA1099 crystal clock rate in HZ (can be changed subsequently by calling SetClockRate)
#define EXTERNAL_CLK_HZ 8000000
// define SAAFREQ_FIXED_CLOCKRATE if the above external clock rate is the only supported clock rate
// i.e. only support a single compile-time clock rate (=> this also prevents using the SetClockRate method)
#undef SAAFREQ_FIXED_CLOCKRATE
// #define SAAFREQ_FIXED_CLOCKRATE
// initial default sample rate (audio samplerate)
#define SAMPLE_RATE_HZ 44100
// initial default oversample (audio quality) recommend 0<=oversample<=6
#define DEFAULT_OVERSAMPLE 6
// Whether to dump out a log of all register and value changes and raw output pcm
//#define DEBUGSAA
#undef DEBUGSAA
// the (default) names of the register output and pcm output log files.
// If you're using a config file, you can change these (or, if you enable
// debugging via the config file settings, but leave the filenames unspecified,
// it will use these defaults)
#define DEBUG_SAA_REGISTER_LOG "debugsaa.txt"
#define DEBUG_SAA_PCM_LOG "debugsaa.pcm"
// Whether to include support for these debug logs via config file (only making
// sense if USE_CONFIG_FILE is also defined)
// Whether to support a startup configuration file that is parsed at load time
// #undef USE_CONFIG_FILE
#define USE_CONFIG_FILE
// and if so, what is its location
#ifdef USE_CONFIG_FILE
#define CONFIG_FILE_PATH "SAASound.cfg"
#endif // USE_CONFIG_FILE
#define DEFAULT_UNBOOSTED_MULTIPLIER 11.35
#define DEFAULT_BOOST 1
#endif // HAVE_CONFIG_H
#endif // DEFNS_H_INCLUDED

View File

@@ -1,15 +0,0 @@
//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by SAASound.rc
//
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 101
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1000
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

View File

@@ -1,14 +0,0 @@
#pragma once
#define EXTERNAL_CLK_HZ 7159090
/* #undef SAAFREQ_FIXED_CLOCKRATE */
#define SAMPLE_RATE_HZ 44100
#define DEFAULT_OVERSAMPLE 6
#define DEFAULT_UNBOOSTED_MULTIPLIER 11.3
#define DEFAULT_BOOST 1
/* #undef DEBUGSAA */
#define DEBUG_SAA_REGISTER_LOG "debugsaa.txt"
#define DEBUG_SAA_PCM_LOG "debugsaa.pcm"
/* #undef USE_CONFIG_FILE */
#define CONFIG_FILE_PATH "SAASound.cfg"

View File

@@ -1,34 +0,0 @@
// Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
//
// handy typedefs
//
//////////////////////////////////////////////////////////////////////
#ifndef TYPES_H_INCLUDED
#define TYPES_H_INCLUDED
#if defined(__i386__) || defined(WIN32) || \
(defined(__alpha__) || defined(__alpha)) || \
defined(__arm__) || \
(defined(__mips__) && defined(__MIPSEL__))
#else
#define __BIG_ENDIAN
#endif
#ifndef NULL
#define NULL 0
#endif
typedef struct
{
int nNumberOfPhases;
bool bLooping;
int nLevels[2][2][16]; // [Resolution][Phase][Withinphase]
} ENVDATA;
#ifdef WIN32
extern "C" void _stdcall OutputDebugStringA (char*);
#endif
#endif

View File

@@ -8,7 +8,6 @@
#include <86box/86box.h>
#include <86box/device.h>
#include <86box/io.h>
#include "saasound/SAASound.h"
#include <86box/snd_cms.h>
#include <86box/sound.h>
#include <86box/plat_unused.h>
@@ -16,13 +15,62 @@
void
cms_update(cms_t *cms)
{
if (cms->pos < wavetable_pos_global) {
SAASNDGenerateMany(cms->saasound, (unsigned char*)&cms->buffer[cms->pos], wavetable_pos_global - cms->pos);
cms->pos = wavetable_pos_global;
}
if (cms->pos2 < wavetable_pos_global) {
SAASNDGenerateMany(cms->saasound2, (unsigned char*)&cms->buffer2[cms->pos2], wavetable_pos_global - cms->pos2);
cms->pos2 = wavetable_pos_global;
for (; cms->pos < sound_pos_global; cms->pos++) {
int16_t out_l = 0;
int16_t out_r = 0;
for (uint8_t c = 0; c < 4; c++) {
switch (cms->noisetype[c >> 1][c & 1]) {
case 0:
cms->noisefreq[c >> 1][c & 1] = MASTER_CLOCK / 256;
break;
case 1:
cms->noisefreq[c >> 1][c & 1] = MASTER_CLOCK / 512;
break;
case 2:
cms->noisefreq[c >> 1][c & 1] = MASTER_CLOCK / 1024;
break;
case 3:
cms->noisefreq[c >> 1][c & 1] = cms->freq[c >> 1][(c & 1) * 3];
break;
default:
break;
}
}
for (uint8_t c = 0; c < 2; c++) {
if (cms->regs[c][0x1C] & 1) {
for (uint8_t d = 0; d < 6; d++) {
if (cms->regs[c][0x14] & (1 << d)) {
if (cms->stat[c][d])
out_l += (cms->vol[c][d][0] * 90);
if (cms->stat[c][d])
out_r += (cms->vol[c][d][1] * 90);
cms->count[c][d] += cms->freq[c][d];
if (cms->count[c][d] >= 24000) {
cms->count[c][d] -= 24000;
cms->stat[c][d] ^= 1;
}
} else if (cms->regs[c][0x15] & (1 << d)) {
if (cms->noise[c][d / 3] & 1)
out_l += (cms->vol[c][d][0] * 90);
if (cms->noise[c][d / 3] & 1)
out_r += (cms->vol[c][d][0] * 90);
}
}
for (uint8_t d = 0; d < 2; d++) {
cms->noisecount[c][d] += cms->noisefreq[c][d];
while (cms->noisecount[c][d] >= 24000) {
cms->noisecount[c][d] -= 24000;
cms->noise[c][d] <<= 1;
if (!(((cms->noise[c][d] & 0x4000) >> 8) ^ (cms->noise[c][d] & 0x40)))
cms->noise[c][d] |= 1;
}
}
}
}
cms->buffer[cms->pos << 1] = out_l;
cms->buffer[(cms->pos << 1) + 1] = out_r;
}
}
@@ -34,44 +82,68 @@ cms_get_buffer(int32_t *buffer, int len, void *priv)
cms_update(cms);
for (int c = 0; c < len * 2; c++)
buffer[c] += (cms->buffer[c] / 2);
buffer[c] += cms->buffer[c];
cms->pos = 0;
}
void
cms_get_buffer_2(int32_t *buffer, int len, void *priv)
{
cms_t *cms = (cms_t *) priv;
cms_update(cms);
for (int c = 0; c < len * 2; c++)
buffer[c] += (cms->buffer2[c] / 2);
cms->pos2 = 0;
}
void
cms_write(uint16_t addr, uint8_t val, void *priv)
{
cms_t *cms = (cms_t *) priv;
int voice;
int chip = (addr & 2) >> 1;
switch (addr & 0xf) {
case 0x1: /* SAA #1 Register Select Port */
SAASNDWriteAddress(cms->saasound, val & 31);
cms->addrs[0] = val & 31;
break;
case 0x3: /* SAA #2 Register Select Port */
SAASNDWriteAddress(cms->saasound2, val & 31);
cms->addrs[1] = val & 31;
break;
case 0x0: /* SAA #1 Data Port */
cms_update(cms);
SAASNDWriteData(cms->saasound, val);
break;
case 0x2: /* SAA #2 Data Port */
cms_update(cms);
SAASNDWriteData(cms->saasound2, val);
cms->regs[chip][cms->addrs[chip] & 31] = val;
switch (cms->addrs[chip] & 31) {
case 0x00:
case 0x01:
case 0x02: /*Volume*/
case 0x03:
case 0x04:
case 0x05:
voice = cms->addrs[chip] & 7;
cms->vol[chip][voice][0] = val & 0xf;
cms->vol[chip][voice][1] = val >> 4;
break;
case 0x08:
case 0x09:
case 0x0A: /*Frequency*/
case 0x0B:
case 0x0C:
case 0x0D:
voice = cms->addrs[chip] & 7;
cms->latch[chip][voice] = (cms->latch[chip][voice] & 0x700) | val;
cms->freq[chip][voice] = (MASTER_CLOCK / 512 << (cms->latch[chip][voice] >> 8)) / (511 - (cms->latch[chip][voice] & 255));
break;
case 0x10:
case 0x11:
case 0x12: /*Octave*/
voice = (cms->addrs[chip] & 3) << 1;
cms->latch[chip][voice] = (cms->latch[chip][voice] & 0xFF) | ((val & 7) << 8);
cms->latch[chip][voice + 1] = (cms->latch[chip][voice + 1] & 0xFF) | ((val & 0x70) << 4);
cms->freq[chip][voice] = (MASTER_CLOCK / 512 << (cms->latch[chip][voice] >> 8)) / (511 - (cms->latch[chip][voice] & 255));
cms->freq[chip][voice + 1] = (MASTER_CLOCK / 512 << (cms->latch[chip][voice + 1] >> 8)) / (511 - (cms->latch[chip][voice + 1] & 255));
break;
case 0x16: /*Noise*/
cms->noisetype[chip][0] = val & 3;
cms->noisetype[chip][1] = (val >> 4) & 3;
break;
default:
break;
}
break;
case 0x6: /* GameBlaster Write Port */
@@ -91,9 +163,9 @@ cms_read(uint16_t addr, void *priv)
switch (addr & 0xf) {
case 0x1: /* SAA #1 Register Select Port */
return SAASNDReadAddress(cms->saasound);
return cms->addrs[0];
case 0x3: /* SAA #2 Register Select Port */
return SAASNDReadAddress(cms->saasound2);
return cms->addrs[1];
case 0x4: /* GameBlaster Read port (Always returns 0x7F) */
return 0x7f;
case 0xa: /* GameBlaster Read Port */
@@ -113,12 +185,7 @@ cms_init(UNUSED(const device_t *info))
uint16_t addr = device_get_config_hex16("base");
io_sethandler(addr, 0x0010, cms_read, NULL, NULL, cms_write, NULL, NULL, cms);
cms->saasound = newSAASND();
SAASNDSetSoundParameters(cms->saasound, SAAP_44100 | SAAP_16BIT | SAAP_NOFILTER | SAAP_STEREO);
cms->saasound2 = newSAASND();
SAASNDSetSoundParameters(cms->saasound2, SAAP_44100 | SAAP_16BIT | SAAP_NOFILTER | SAAP_STEREO);
wavetable_add_handler(cms_get_buffer, cms);
wavetable_add_handler(cms_get_buffer_2, cms);
sound_add_handler(cms_get_buffer, cms);
return cms;
}
@@ -127,9 +194,6 @@ cms_close(void *priv)
{
cms_t *cms = (cms_t *) priv;
deleteSAASND(cms->saasound);
deleteSAASND(cms->saasound2);
free(cms);
}

View File

@@ -41,7 +41,6 @@
#include <86box/sound.h>
#include "cpu.h"
#include <86box/timer.h>
#include "saasound/SAASound.h"
#include <86box/snd_sb.h>
#include <86box/plat_unused.h>
@@ -146,42 +145,6 @@ sb_log(const char *fmt, ...)
# define sb_log(fmt, ...)
#endif
void
sb_cms_get_buffer(int32_t *buffer, int len, void *priv)
{
sb_t *sb = (sb_t *) priv;
cms_update(&sb->cms);
for (int c = 0; c < len * 2; c++) {
if (sb->mixer_enabled) {
buffer[c] += sb->cms.buffer[c] * sb->mixer_sb2.fm;
}
else
buffer[c] += sb->cms.buffer[c];
}
sb->cms.pos = 0;
}
void
sb_cms_get_buffer_2(int32_t *buffer, int len, void *priv)
{
sb_t *sb = (sb_t *) priv;
cms_update(&sb->cms);
for (int c = 0; c < len * 2; c++) {
if (sb->mixer_enabled) {
buffer[c] += sb->cms.buffer2[c] * sb->mixer_sb2.fm;
}
else
buffer[c] += sb->cms.buffer2[c];
}
sb->cms.pos2 = 0;
}
/* SB 1, 1.5, MCV, and 2 do not have a mixer, so signal is hardwired. */
static void
sb_get_buffer_sb2(int32_t *buffer, int len, void *priv)
@@ -192,10 +155,23 @@ sb_get_buffer_sb2(int32_t *buffer, int len, void *priv)
sb_dsp_update(&sb->dsp);
if (sb->cms_enabled)
cms_update(&sb->cms);
for (int c = 0; c < len * 2; c += 2) {
double out_l = 0.0;
double out_r = 0.0;
if (sb->cms_enabled) {
out_l += sb->cms.buffer[c];
out_r += sb->cms.buffer[c + 1];
}
if (sb->cms_enabled && sb->mixer_enabled) {
out_l *= mixer->fm;
out_r *= mixer->fm;
}
/* TODO: Recording: I assume it has direct mic and line in like SB2.
It is unclear from the docs if it has a filter, but it probably does. */
/* TODO: Recording: Mic and line In with AGC. */
@@ -216,6 +192,9 @@ sb_get_buffer_sb2(int32_t *buffer, int len, void *priv)
}
sb->dsp.pos = 0;
if (sb->cms_enabled)
sb->cms.pos = 0;
}
static void
@@ -2911,13 +2890,6 @@ sb_init(UNUSED(const device_t *info))
cms_read, NULL, NULL,
cms_write, NULL, NULL,
&sb->cms);
sb->cms.saasound = newSAASND();
SAASNDSetSoundParameters(sb->cms.saasound, SAAP_44100 | SAAP_16BIT | SAAP_NOFILTER | SAAP_STEREO);
sb->cms.saasound2 = newSAASND();
SAASNDSetSoundParameters(sb->cms.saasound2, SAAP_44100 | SAAP_16BIT | SAAP_NOFILTER | SAAP_STEREO);
wavetable_add_handler(sb_cms_get_buffer, sb);
wavetable_add_handler(sb_cms_get_buffer_2, sb);
}
if (mixer_addr > 0x000) {
@@ -4072,11 +4044,6 @@ sb_close(void *priv)
sb_t *sb = (sb_t *) priv;
sb_dsp_close(&sb->dsp);
if (sb->cms_enabled) {
deleteSAASND(sb->cms.saasound);
deleteSAASND(sb->cms.saasound2);
}
free(sb);
}

View File

@@ -2510,6 +2510,12 @@ mach_in(uint16_t addr, void *priv)
case 0xa9:
temp = svga->vc & 0xff;
break;
case 0xaa:
if (ATI_GRAPHICS_ULTRA)
temp = 0x06;
else
temp = 0x00;
break;
case 0xb0:
temp = mach->regs[0xb0] | 0x80;
temp &= ~0x18;
@@ -2699,14 +2705,12 @@ mach_set_resolution(mach_t *mach, svga_t *svga)
dev->vdisp += 2;
dev->v_total = dev->v_total_reg + 1;
if (dev->interlace)
dev->v_total >>= 1;
dev->v_syncstart = dev->v_sync_start + 1;
if (dev->interlace)
dev->v_syncstart >>= 1;
if (ATI_8514A_ULTRA) {
mach_log("VSYNCSTART=%d, VTOTAL=%d, interlace=%02x, vdisp=%d.\n", dev->v_syncstart, dev->v_total, dev->interlace, dev->vdisp);
if (!ATI_MACH32) {
if ((mach->accel.clock_sel & 0x01) &&
!(dev->accel.advfunc_cntl & 0x01))
ret = 2;
@@ -3233,7 +3237,7 @@ mach_accel_out_fifo(mach_t *mach, svga_t *svga, ibm8514_t *dev, uint16_t port, u
if (len == 1) {
if ((mach->accel.clock_sel & 0x01) || (!(mach->accel.clock_sel & 0x01) && (mach->shadow_set & 0x03))) { /*For 8514/A mode, take the shadow sets into account.*/
if (!(mach->shadow_cntl & 0x20)) {
WRITE8(port, dev->v_disp, val);
WRITE8(port, dev->v_disp, val >> 8);
dev->v_disp &= 0x1fff;
}
}
@@ -3267,7 +3271,7 @@ mach_accel_out_fifo(mach_t *mach, svga_t *svga, ibm8514_t *dev, uint16_t port, u
if (len == 1) {
if ((mach->accel.clock_sel & 0x01) || (!(mach->accel.clock_sel & 0x01) && (mach->shadow_set & 0x03))) { /*For 8514/A mode, take the shadow sets into account.*/
if (!(mach->shadow_cntl & 0x10)) {
WRITE8(port, dev->v_sync_start, val);
WRITE8(port, dev->v_sync_start, val >> 8);
dev->v_sync_start &= 0x1fff;
}
}

View File

@@ -4273,7 +4273,10 @@ gd54xx_init(const device_t *info)
break;
case CIRRUS_ID_CLGD5428:
if (info->local & 0x100)
if (info->local & 0x200) {
romfn = NULL;
gd54xx->has_bios = 0;
} else if (info->local & 0x100)
if (gd54xx->vlb)
romfn = BIOS_GD5428_DIAMOND_B1_VLB_PATH;
else {
@@ -4750,26 +4753,6 @@ static const device_config_t gd5426_config[] = {
{ .name = "", .description = "", .type = CONFIG_END }
};
static const device_config_t gd5428_onboard_config[] = {
{
.name = "memory",
.description = "Memory size",
.type = CONFIG_SELECTION,
.default_string = NULL,
.default_int = 2048,
.file_filter = NULL,
.spinner = { 0 },
.selection = {
{ .description = "512 KB", .value = 512 },
{ .description = "1 MB", .value = 1024 },
{ .description = "2 MB", .value = 2048 },
{ .description = "" }
},
.bios = { { 0 } }
},
{ .name = "", .description = "", .type = CONFIG_END }
};
static const device_config_t gd5429_config[] = {
{
.name = "memory",
@@ -5176,7 +5159,7 @@ const device_t gd5428_onboard_device = {
.available = gd5428_isa_available,
.speed_changed = gd54xx_speed_changed,
.force_redraw = gd54xx_force_redraw,
.config = gd5428_onboard_config
.config = gd5426_config
};
const device_t gd5428_vlb_onboard_device = {
@@ -5190,7 +5173,21 @@ const device_t gd5428_vlb_onboard_device = {
.available = NULL,
.speed_changed = gd54xx_speed_changed,
.force_redraw = gd54xx_force_redraw,
.config = gd5428_onboard_config
.config = gd5426_config
};
const device_t gd5428_onboard_vlb_device = {
.name = "Cirrus Logic GD5428 (VLB) (On-Board) (Dell)",
.internal_name = "cl_gd5428_onboard_vlb",
.flags = DEVICE_VLB,
.local = CIRRUS_ID_CLGD5428 | 0x200,
.init = gd54xx_init,
.close = gd54xx_close,
.reset = gd54xx_reset,
.available = NULL,
.speed_changed = gd54xx_speed_changed,
.force_redraw = gd54xx_force_redraw,
.config = gd542x_config
};
const device_t gd5429_isa_device = {

View File

@@ -8042,7 +8042,7 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, voi
s3->accel.sy = s3->accel.maj_axis_pcnt;
if ((s3->bpp == 0) && s3->color_16bit) {
s3->accel.rd_mask_16bit_check = ((rd_mask & 0xff00) != 0xff00);
s3->accel.rd_mask_16bit_check = ((rd_mask & 0xff00) != 0xff00) && rd_mask;
if (s3->accel.rd_mask_16bit_check) {
if ((s3->accel.cur_x_overflow & 0xc00) == 0xc00)
s3->accel.start = 1;
@@ -8353,7 +8353,7 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, voi
s3->accel.dest = dstbase + s3->accel.cy * s3->width;
if ((s3->bpp == 0) && s3->color_16bit) {
s3->accel.rd_mask_16bit_check = ((rd_mask & 0xff00) != 0xff00);
s3->accel.rd_mask_16bit_check = ((rd_mask & 0xff00) != 0xff00) && rd_mask;
if (s3->accel.rd_mask_16bit_check) {
if (s3->accel.cmd == 0x41b3) {
if (frgd_mix == 0) {
@@ -8478,9 +8478,10 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, voi
if ((s3_cpu_src(s3)) && !(s3->accel.cmd & 0x200)) {
s3_log("FIXME: S3 911/924 15/16bpp documentation needed.\n");
} else {
if (!cpu_input && (s3->accel.cur_x & 0x400))
if (!cpu_input && (s3->accel.cur_x & 0x400)) {
s3_log("No Input.\n");
break;
else if (cpu_input && (s3->accel.cmd == 0x53b3) && (s3->accel.cur_x & 0x400))
} else if (cpu_input && (s3->accel.cmd == 0x53b3) && (s3->accel.cur_x & 0x400))
break;
}
}
@@ -8756,7 +8757,7 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, voi
s3->accel.cy = s3->accel.cur_y & 0xfff;
if ((s3->bpp == 0) && s3->color_16bit) {
s3->accel.rd_mask_16bit_check = ((rd_mask & 0xff00) != 0xff00);
s3->accel.rd_mask_16bit_check = ((rd_mask & 0xff00) != 0xff00) && rd_mask;
if (s3->accel.rd_mask_16bit_check) {
if (!(clip_r & 0x400))
s3->accel.start = 1;
@@ -8805,7 +8806,7 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, voi
s3_log("CMDFULL=%04x, FRGDSEL=%x, BKGDSEL=%x, FRGDMIX=%02x, BKGDMIX=%02x, MASKCHECK=%x, RDMASK=%04x, MINUS=%d, WRTMASK=%04X, MIX=%04x, CX=%d, CY=%d, DX=%d, DY=%d, SX=%d, SY=%d, PIXCNTL=%02x, 16BITCOLOR=%x, RDCHECK=%x, CLIPL=%d, CLIPR=%d, OVERFLOW=%d, pitch=%d.\n", s3->accel.cmd, frgd_mix, bkgd_mix, s3->accel.frgd_mix & 0x0f, s3->accel.bkgd_mix & 0x0f, s3->accel.rd_mask_16bit_check, rd_mask, s3->accel.minus, wrt_mask, mix_dat & 0xffff, s3->accel.cx, s3->accel.cy, s3->accel.dx, s3->accel.dy, s3->accel.sx, s3->accel.sy, s3->accel.multifunc[0x0a] & 0xc4, s3->accel.color_16bit_check, s3->accel.rd_mask_16bit_check, clip_l, clip_r, (s3->accel.destx_overflow & 0xc00) == 0xc00, s3->width);
if (!cpu_input && (frgd_mix == 3) && !vram_mask && !(s3->accel.multifunc[0xe] & 0x100) && ((s3->accel.cmd & 0xa0) == 0xa0) && ((s3->accel.frgd_mix & 0xf) == 7) && ((s3->accel.bkgd_mix & 0xf) == 7)) {
pclog("Special BitBLT.\n");
s3_log("Special BitBLT.\n");
while (1) {
if ((s3->accel.dx >= clip_l) && (s3->accel.dx <= clip_r) && (s3->accel.dy >= clip_t) && (s3->accel.dy <= clip_b)) {
READ(s3->accel.src + s3->accel.cx - s3->accel.minus, src_dat);

View File

@@ -125,6 +125,7 @@ typedef struct tgui_t {
uint8_t rop;
uint32_t flags;
uint8_t pattern[0x80];
uint8_t pattern_32bpp[0x100];
int command;
int offset;
uint16_t ger22;
@@ -142,6 +143,7 @@ typedef struct tgui_t {
uint32_t pattern_8[8 * 8];
uint32_t pattern_16[8 * 8];
uint32_t pattern_32[8 * 8];
int pattern_32_idx;
} accel;
uint8_t copy_latch[16]; /*TGUI9400CXi only*/
@@ -756,7 +758,7 @@ tgui_recalctimings(svga_t *svga)
if (svga->vdisp == 1020)
svga->vdisp += 2;
if ((tgui->oldctrl2 & 0x10) || (svga->crtc[0x2a] & 0x40))
if (tgui->oldctrl2 & 0x10)
svga->ma_latch <<= 1;
svga->lowres = !(svga->crtc[0x2a] & 0x40);
@@ -2280,6 +2282,8 @@ tgui_accel_command(int count, uint32_t cpu_dat, tgui_t *tgui)
if (count == -1)
tgui->accel.x = tgui->accel.y = 0;
tgui->accel.pattern_32_idx = 0;
if (tgui->accel.flags & TGUI_SOLIDFILL) {
for (y = 0; y < 8; y++) {
for (x = 0; x < 8; x++) {
@@ -2298,22 +2302,21 @@ tgui_accel_command(int count, uint32_t cpu_dat, tgui_t *tgui)
if (tgui->accel.bpp == 0) {
for (y = 0; y < 8; y++) {
for (x = 0; x < 8; x++) {
tgui->accel.pattern_8[(y * 8) + (7 - x)] = tgui->accel.pattern[x + y * 8];
tgui->accel.pattern_8[(y * 8) + x] = tgui->accel.pattern[x + y * 8];
}
}
pattern_data = tgui->accel.pattern_8;
} else if (tgui->accel.bpp == 1) {
for (y = 0; y < 8; y++) {
for (x = 0; x < 8; x++) {
tgui->accel.pattern_16[(y * 8) + (7 - x)] = tgui->accel.pattern[x * 2 + y * 16] | (tgui->accel.pattern[x * 2 + y * 16 + 1] << 8);
tgui->accel.pattern_16[(y * 8) + x] = tgui->accel.pattern[x * 2 + y * 16] | (tgui->accel.pattern[x * 2 + y * 16 + 1] << 8);
}
}
pattern_data = tgui->accel.pattern_16;
} else {
for (y = 0; y < 4; y++) {
for (y = 0; y < 8; y++) {
for (x = 0; x < 8; x++) {
tgui->accel.pattern_32[(y * 8) + (7 - x)] = tgui->accel.pattern[x * 4 + y * 32] | (tgui->accel.pattern[x * 4 + y * 32 + 1] << 8) | (tgui->accel.pattern[x * 4 + y * 32 + 2] << 16) | (tgui->accel.pattern[x * 4 + y * 32 + 3] << 24);
tgui->accel.pattern_32[((y + 4) * 8) + (7 - x)] = tgui->accel.pattern[x * 4 + y * 32] | (tgui->accel.pattern[x * 4 + y * 32 + 1] << 8) | (tgui->accel.pattern[x * 4 + y * 32 + 2] << 16) | (tgui->accel.pattern[x * 4 + y * 32 + 3] << 24);
tgui->accel.pattern_32[(y * 8) + x] = tgui->accel.pattern_32bpp[x * 4 + y * 32] | (tgui->accel.pattern_32bpp[x * 4 + y * 32 + 1] << 8) | (tgui->accel.pattern_32bpp[x * 4 + y * 32 + 2] << 16) | (tgui->accel.pattern_32bpp[x * 4 + y * 32 + 3] << 24);
}
}
pattern_data = tgui->accel.pattern_32;
@@ -2396,6 +2399,7 @@ tgui_accel_command(int count, uint32_t cpu_dat, tgui_t *tgui)
count -= 3;
}
READ(tgui->accel.dst, dst_dat);
pat_dat = pattern_data[((tgui->accel.pat_y & 7) * 8) + (tgui->accel.pat_x & 7)];
@@ -3192,6 +3196,8 @@ tgui_accel_out(uint16_t addr, uint8_t val, void *priv)
case 0x21fe:
case 0x21ff:
tgui->accel.pattern[addr & 0x7f] = val;
tgui->accel.pattern_32bpp[tgui->accel.pattern_32_idx] = val;
tgui->accel.pattern_32_idx = (tgui->accel.pattern_32_idx + 1) & 0xff;
break;
default: