Merge branch 'master' into pc98x1
12
src/86box.c
@@ -1293,6 +1293,8 @@ pc_reset_hard_init(void)
|
||||
* modules that are.
|
||||
*/
|
||||
|
||||
keyboard_init();
|
||||
|
||||
/* Reset the IDE and SCSI presences */
|
||||
other_ide_present = other_scsi_present = 0;
|
||||
|
||||
@@ -1622,6 +1624,8 @@ set_screen_size_monitor(int x, int y, int monitor_index)
|
||||
{
|
||||
int temp_overscan_x = monitors[monitor_index].mon_overscan_x;
|
||||
int temp_overscan_y = monitors[monitor_index].mon_overscan_y;
|
||||
int is_svga = (video_get_type_monitor(monitor_index) == VIDEO_FLAG_TYPE_SPECIAL) ||
|
||||
(video_get_type_monitor(monitor_index) == VIDEO_FLAG_TYPE_8514);
|
||||
double dx;
|
||||
double dy;
|
||||
double dtx;
|
||||
@@ -1655,19 +1659,19 @@ set_screen_size_monitor(int x, int y, int monitor_index)
|
||||
dty = (double) temp_overscan_y;
|
||||
|
||||
/* Account for possible overscan. */
|
||||
if (video_get_type_monitor(monitor_index) != VIDEO_FLAG_TYPE_SPECIAL && (temp_overscan_y == 16)) {
|
||||
if (!is_svga && (temp_overscan_y == 16)) {
|
||||
/* CGA */
|
||||
dy = (((dx - dtx) / 4.0) * 3.0) + dty;
|
||||
} else if (video_get_type_monitor(monitor_index) != VIDEO_FLAG_TYPE_SPECIAL && (temp_overscan_y < 16)) {
|
||||
} else if (!is_svga && (temp_overscan_y < 16)) {
|
||||
/* MDA/Hercules */
|
||||
dy = (x / 4.0) * 3.0;
|
||||
dy = (dx / 4.0) * 3.0;
|
||||
} else {
|
||||
if (enable_overscan) {
|
||||
/* EGA/(S)VGA with overscan */
|
||||
dy = (((dx - dtx) / 4.0) * 3.0) + dty;
|
||||
} else {
|
||||
/* EGA/(S)VGA without overscan */
|
||||
dy = (x / 4.0) * 3.0;
|
||||
dy = (dx / 4.0) * 3.0;
|
||||
}
|
||||
}
|
||||
monitors[monitor_index].mon_unscaled_size_y = (int) dy;
|
||||
|
||||
@@ -1025,8 +1025,10 @@ 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) {
|
||||
device_reset_all(DEVICE_PCI);
|
||||
mem_zero();
|
||||
}
|
||||
|
||||
if (sus_typ & SUS_RESET_CPU)
|
||||
cpu_alt_reset = 0;
|
||||
|
||||
@@ -493,14 +493,28 @@ pipc_reset_hard(void *priv)
|
||||
}
|
||||
dev->ac97_regs[i][0x1c] = 0x01;
|
||||
|
||||
if (i == 0) {
|
||||
dev->ac97_regs[i][0x34] = 0xc0;
|
||||
|
||||
dev->ac97_regs[i][0xc0] = 0x01;
|
||||
dev->ac97_regs[i][0xc1] = 0x00;
|
||||
dev->ac97_regs[i][0xc2] = 0x03;
|
||||
dev->ac97_regs[i][0xc3] = 0x00;
|
||||
|
||||
dev->ac97_regs[i][0xc4] = 0x00;
|
||||
dev->ac97_regs[i][0xc5] = 0x00;
|
||||
dev->ac97_regs[i][0xc6] = 0x00;
|
||||
dev->ac97_regs[i][0xc7] = 0x00;
|
||||
}
|
||||
|
||||
dev->ac97_regs[i][0x3d] = 0x03;
|
||||
|
||||
if (i == 0)
|
||||
if (i == 0) {
|
||||
dev->ac97_regs[i][0x40] = 0x01;
|
||||
|
||||
dev->ac97_regs[i][0x43] = 0x1c;
|
||||
dev->ac97_regs[i][0x48] = 0x01;
|
||||
dev->ac97_regs[i][0x4b] = 0x02;
|
||||
dev->ac97_regs[i][0x43] = 0x1c;
|
||||
dev->ac97_regs[i][0x48] = 0x01;
|
||||
dev->ac97_regs[i][0x4b] = 0x00;
|
||||
}
|
||||
|
||||
pipc_sgd_handlers(dev, i);
|
||||
pipc_codec_handlers(dev, i);
|
||||
@@ -742,10 +756,12 @@ pipc_codec_handlers(pipc_t *dev, uint8_t modem)
|
||||
if (!dev->ac97)
|
||||
return;
|
||||
|
||||
uint32_t base = (dev->ac97_regs[modem][0x1d] << 8);
|
||||
|
||||
if (modem)
|
||||
ac97_via_remap_modem_codec(dev->ac97, dev->ac97_regs[1][0x1d] << 8, dev->ac97_regs[1][0x04] & PCI_COMMAND_IO);
|
||||
ac97_via_remap_modem_codec(dev->ac97, base, dev->ac97_regs[1][0x04] & PCI_COMMAND_IO);
|
||||
else
|
||||
ac97_via_remap_audio_codec(dev->ac97, dev->ac97_regs[0][0x1d] << 8, dev->ac97_regs[0][0x04] & PCI_COMMAND_IO);
|
||||
ac97_via_remap_audio_codec(dev->ac97, base, dev->ac97_regs[0][0x04] & PCI_COMMAND_IO);
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
@@ -1204,7 +1220,7 @@ pipc_write(int func, int addr, uint8_t val, void *priv)
|
||||
|
||||
case 0x77:
|
||||
if ((dev->local >= VIA_PIPC_686A) && (val & 0x10))
|
||||
pclog("PIPC: Warning: Internal I/O APIC enabled.\n");
|
||||
warning("PIPC: Warning: Internal I/O APIC enabled.\n");
|
||||
nvr_via_wp_set(!!(val & 0x04), 0x32, dev->nvr);
|
||||
nvr_via_wp_set(!!(val & 0x02), 0x0d, dev->nvr);
|
||||
break;
|
||||
@@ -1488,36 +1504,39 @@ pipc_write(int func, int addr, uint8_t val, void *priv)
|
||||
break;
|
||||
}
|
||||
} else if (func <= pm_func + 2) { /* AC97 / MC97 */
|
||||
/* Read-only addresses. */
|
||||
if ((addr < 0x4) || ((addr >= 0x6) && (addr < 0x9)) || ((addr >= 0xc) && (addr < 0x11)) || (addr == 0x16) || (addr == 0x17) || (addr == 0x1a) || (addr == 0x1b) || ((addr >= 0x1e) && (addr < 0x2c)) || ((addr >= 0x30) && (addr < 0x34)) || ((addr >= 0x35) && (addr < 0x3c)) || ((addr >= 0x3d) && (addr < 0x41)) || ((addr >= 0x45) && (addr < 0x4a)) || (addr >= 0x4c))
|
||||
return;
|
||||
|
||||
/* Small shortcut. */
|
||||
func = func - pm_func - 1;
|
||||
|
||||
/* Check disable bits and specific read-only addresses for both controllers. */
|
||||
if ((func == 0) && (((addr >= 0x09) && (addr < 0xc)) || (addr == 0x44) || (dev->pci_isa_regs[0x85] & 0x04)))
|
||||
/* Check disable bits. */
|
||||
if ((func == 0) && (dev->pci_isa_regs[0x85] & 0x04))
|
||||
return;
|
||||
|
||||
if ((func == 1) && ((addr == 0x14) || (addr == 0x15) || (addr == 0x18) || (addr == 0x19) || (addr == 0x42) || (addr == 0x43) || (addr == 0x48) || (addr == 0x4a) || (addr == 0x4b) || (dev->pci_isa_regs[0x85] & 0x08)))
|
||||
if ((func == 1) && (dev->pci_isa_regs[0x85] & 0x08))
|
||||
return;
|
||||
|
||||
switch (addr) {
|
||||
case 0x04:
|
||||
dev->ac97_regs[func][addr] = val;
|
||||
dev->ac97_regs[func][addr] = val & 0x01;
|
||||
pipc_sgd_handlers(dev, func);
|
||||
if (func == 0) {
|
||||
pipc_fmnmi_handlers(dev, func);
|
||||
pipc_sb_handlers(dev, func);
|
||||
}
|
||||
pipc_codec_handlers(dev, func);
|
||||
pipc_fmnmi_handlers(dev, func);
|
||||
break;
|
||||
|
||||
case 0x09:
|
||||
case 0x0a:
|
||||
case 0x0b:
|
||||
if (dev->ac97_regs[func][0x44] & 0x20)
|
||||
/* Not writable on audio, only on modem. */
|
||||
if ((func == 1) && (dev->ac97_regs[func][0x44] & 0x20))
|
||||
dev->ac97_regs[func][addr] = val;
|
||||
break;
|
||||
|
||||
case 0x10:
|
||||
/*
|
||||
The lowest 10 bytes are always 0x01, indicating
|
||||
a 256-byte I/O space.
|
||||
*/
|
||||
case 0x11:
|
||||
dev->ac97_regs[func][addr] = val;
|
||||
pipc_sgd_handlers(dev, func);
|
||||
@@ -1525,21 +1544,26 @@ pipc_write(int func, int addr, uint8_t val, void *priv)
|
||||
|
||||
case 0x14:
|
||||
case 0x15:
|
||||
if (addr == 0x14)
|
||||
val = (val & 0xfc) | 1;
|
||||
dev->ac97_regs[func][addr] = val;
|
||||
pipc_fmnmi_handlers(dev, func);
|
||||
/* Not present on modem. */
|
||||
if (func == 0) {
|
||||
if (addr == 0x14)
|
||||
val = (val & 0xfc) | 1;
|
||||
dev->ac97_regs[func][addr] = val;
|
||||
pipc_fmnmi_handlers(dev, func);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x18:
|
||||
case 0x19:
|
||||
if (addr == 0x18)
|
||||
val = (val & 0xfc) | 1;
|
||||
dev->ac97_regs[func][addr] = val;
|
||||
pipc_sb_handlers(dev, func);
|
||||
/* Not present on modem. */
|
||||
if (func == 0) {
|
||||
if (addr == 0x18)
|
||||
val = (val & 0xfc) | 1;
|
||||
dev->ac97_regs[func][addr] = val;
|
||||
pipc_sb_handlers(dev, func);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x1c:
|
||||
case 0x1d:
|
||||
dev->ac97_regs[func][addr] = val;
|
||||
pipc_codec_handlers(dev, func);
|
||||
@@ -1549,39 +1573,84 @@ pipc_write(int func, int addr, uint8_t val, void *priv)
|
||||
case 0x2d:
|
||||
case 0x2e:
|
||||
case 0x2f:
|
||||
if ((func == 0) && (dev->ac97_regs[func][0x42] & 0x20))
|
||||
if (((func == 0) && (dev->ac97_regs[func][0x42] & 0x20)) ||
|
||||
((func == 1) && (dev->ac97_regs[func][0x44] & 0x10)))
|
||||
dev->ac97_regs[func][addr] = val;
|
||||
break;
|
||||
|
||||
case 0x3c:
|
||||
dev->ac97_regs[func][addr] = val & 0x0f;
|
||||
break;
|
||||
|
||||
case 0x41:
|
||||
dev->ac97_regs[func][addr] = val;
|
||||
ac97_via_write_control(dev->ac97, func, val);
|
||||
break;
|
||||
|
||||
case 0x42:
|
||||
case 0x4a:
|
||||
case 0x4b:
|
||||
dev->ac97_regs[0][addr] = dev->ac97_regs[1][addr] = val;
|
||||
gameport_remap(dev->gameport, (dev->ac97_regs[0][0x42] & 0x08) ? ((dev->ac97_regs[0][0x4b] << 8) | (dev->ac97_regs[0][0x4a] & 0xf8)) : 0);
|
||||
if (addr == 0x42)
|
||||
pipc_sb_handlers(dev, func);
|
||||
case 0x4a ... 0x4b:
|
||||
if (func == 0) {
|
||||
dev->ac97_regs[func][addr] = val;
|
||||
gameport_remap(dev->gameport, (dev->ac97_regs[func][0x42] & 0x08) ?
|
||||
((dev->ac97_regs[func][0x4b] << 8) |
|
||||
(dev->ac97_regs[func][0x4a] & 0xf8)) : 0);
|
||||
|
||||
if (addr == 0x42)
|
||||
pipc_sb_handlers(dev, func);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x43:
|
||||
dev->ac97_regs[0][addr] = dev->ac97_regs[1][addr] = val;
|
||||
if (func == 0)
|
||||
dev->ac97_regs[func][addr] = val;
|
||||
break;
|
||||
|
||||
case 0x44:
|
||||
dev->ac97_regs[0][addr] = dev->ac97_regs[1][addr] = val & 0xf0;
|
||||
if (func == 1)
|
||||
dev->ac97_regs[func][addr] = val & 0xf0;
|
||||
break;
|
||||
|
||||
case 0x45:
|
||||
case 0x48:
|
||||
dev->ac97_regs[0][addr] = dev->ac97_regs[1][addr] = val & 0x0f;
|
||||
if (func == 0)
|
||||
dev->ac97_regs[func][addr] = val & 0x0f;
|
||||
break;
|
||||
|
||||
case 0x80:
|
||||
case 0x81:
|
||||
case 0x82:
|
||||
dev->ac97_regs[func][addr] = val;
|
||||
break;
|
||||
case 0x83:
|
||||
dev->ac97_regs[func][addr] = ((dev->ac97_regs[func][addr] & 0x01) |
|
||||
(val & 0xc0)) & ~(val & 0x0a);
|
||||
break;
|
||||
|
||||
case 0x88:
|
||||
case 0x89:
|
||||
dev->ac97_regs[func][addr] = val;
|
||||
break;
|
||||
case 0x8a:
|
||||
case 0x8b:
|
||||
dev->ac97_regs[func][addr] &= ~val;
|
||||
break;
|
||||
|
||||
case 0x8e:
|
||||
case 0x8f:
|
||||
dev->ac97_regs[func][addr] = val;
|
||||
break;
|
||||
|
||||
case 0xc4:
|
||||
if (func == 0)
|
||||
dev->ac97_regs[func][addr] = (dev->ac97_regs[func][addr] & 0x0c) |
|
||||
(val & 0x03);
|
||||
break;
|
||||
case 0xc5:
|
||||
if (func == 0)
|
||||
dev->ac97_regs[func][addr] = (dev->ac97_regs[func][addr] & 0x60) |
|
||||
(val & 0x9f);
|
||||
break;
|
||||
|
||||
default:
|
||||
dev->ac97_regs[func][addr] = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,7 +152,13 @@ ropMOV_b_imm(UNUSED(uint8_t opcode), uint32_t fetchdat, uint32_t op_32, uint32_t
|
||||
{
|
||||
if ((fetchdat & 0xc0) == 0xc0) {
|
||||
STORE_IMM_REG_B(fetchdat & 7, (fetchdat >> 8) & 0xff);
|
||||
} else {
|
||||
}
|
||||
/* TODO: Fix the recompilation of that specific case so it no longer breaks NT 3.x NTVDM. */
|
||||
#ifndef RECOMPILE_MOVB_IMM_MEM_ALWAYS
|
||||
else if (((fetchdat & 0xfc) == 0x80) && (op_32 & 0x200))
|
||||
return 0;
|
||||
#endif
|
||||
else {
|
||||
x86seg *target_seg = FETCH_EA(op_ea_seg, fetchdat, op_ssegs, &op_pc, op_32);
|
||||
uint32_t imm = fastreadb(cs + op_pc + 1);
|
||||
int host_reg = LOAD_REG_IMM(imm);
|
||||
|
||||
@@ -723,7 +723,11 @@ host_x86_MOV8_REG_ABS(codeblock_t *block, int dst_reg, void *p)
|
||||
codegen_addbyte4(block, 0x41, 0x8a, 0x84 | ((dst_reg & 7) << 3), 0x24); /*MOV dst_reg, ram_offset[R12]*/
|
||||
codegen_addlong(block, ram_offset);
|
||||
} else {
|
||||
fatal("host_x86_MOV8_REG_ABS - out of range\n");
|
||||
codegen_alloc_bytes(block, 10);
|
||||
codegen_addbyte2(block, 0x49, 0xb9); /*MOV R9, p*/
|
||||
codegen_addquad(block, (uintptr_t) p);
|
||||
codegen_alloc_bytes(block, 3);
|
||||
codegen_addbyte3(block, 0x41, 0x8a, 0x01 | ((dst_reg & 7) << 3)); /*MOV dst_reg, [R9]*/
|
||||
}
|
||||
}
|
||||
void
|
||||
|
||||
@@ -296,7 +296,13 @@ ropMOV_b_imm(codeblock_t *block, ir_data_t *ir, UNUSED(uint8_t opcode), uint32_t
|
||||
|
||||
imm = fastreadb(cs + op_pc + 1);
|
||||
uop_MOV_IMM(ir, IREG_8(dest_reg), imm);
|
||||
} else {
|
||||
}
|
||||
/* TODO: Fix the recompilation of that specific case so it no longer breaks NT 3.x NTVDM. */
|
||||
#ifndef RECOMPILE_MOVB_IMM_MEM_ALWAYS
|
||||
else if (((fetchdat & 0xfc) == 0x80) && (op_32 & 0x200))
|
||||
return 0;
|
||||
#endif
|
||||
else {
|
||||
uop_MOV_IMM(ir, IREG_oldpc, cpu_state.oldpc);
|
||||
target_seg = codegen_generate_ea(ir, op_ea_seg, fetchdat, op_ssegs, &op_pc, op_32, 0);
|
||||
codegen_check_seg_write(block, ir, target_seg);
|
||||
|
||||
11
src/config.c
@@ -192,12 +192,6 @@ load_general(void)
|
||||
else if (mouse_sensitivity > 2.0)
|
||||
mouse_sensitivity = 2.0;
|
||||
|
||||
p = ini_section_get_string(cat, "iconset", NULL);
|
||||
if (p != NULL)
|
||||
strcpy(icon_set, p);
|
||||
else
|
||||
strcpy(icon_set, "");
|
||||
|
||||
enable_discord = !!ini_section_get_int(cat, "enable_discord", 0);
|
||||
|
||||
open_dir_usr_path = ini_section_get_int(cat, "open_dir_usr_path", 0);
|
||||
@@ -2038,11 +2032,6 @@ save_general(void)
|
||||
ini_section_set_string(cat, "language", buffer);
|
||||
}
|
||||
|
||||
if (!strcmp(icon_set, ""))
|
||||
ini_section_delete_var(cat, "iconset");
|
||||
else
|
||||
ini_section_set_string(cat, "iconset", icon_set);
|
||||
|
||||
if (enable_discord)
|
||||
ini_section_set_int(cat, "enable_discord", enable_discord);
|
||||
else
|
||||
|
||||
@@ -113,7 +113,17 @@ static scconvtbl scconv55_8a[18 + 1] =
|
||||
void
|
||||
keyboard_init(void)
|
||||
{
|
||||
num_lock = 0;
|
||||
caps_lock = 0;
|
||||
scroll_lock = 0;
|
||||
shift = 0;
|
||||
|
||||
memset(recv_key, 0x00, sizeof(recv_key));
|
||||
memset(recv_key_ui, 0x00, sizeof(recv_key));
|
||||
memset(oldkey, 0x00, sizeof(recv_key));
|
||||
#if 0
|
||||
memset(key_delay, 0x00, sizeof(recv_key));
|
||||
#endif
|
||||
|
||||
keyboard_scan = 1;
|
||||
scan_table = NULL;
|
||||
|
||||
@@ -79,6 +79,8 @@ int floppyrate[4];
|
||||
|
||||
int fdc_current[FDC_MAX] = { 0, 0 };
|
||||
|
||||
volatile int fdcinited = 0;
|
||||
|
||||
#ifdef ENABLE_FDC_LOG
|
||||
int fdc_do_log = ENABLE_FDC_LOG;
|
||||
|
||||
@@ -2268,10 +2270,21 @@ fdc_reset(void *priv)
|
||||
fdc_update_rwc(fdc, 1, default_rwc);
|
||||
fdc_update_rwc(fdc, 2, default_rwc);
|
||||
fdc_update_rwc(fdc, 3, default_rwc);
|
||||
fdc_update_drvrate(fdc, 0, 0);
|
||||
fdc_update_drvrate(fdc, 1, 0);
|
||||
fdc_update_drvrate(fdc, 2, 0);
|
||||
fdc_update_drvrate(fdc, 3, 0);
|
||||
/*
|
||||
The OKI IF386SX natively supports the Japanese 1.25 MB floppy format,
|
||||
since it can read such images just fine, it also attempts to use data
|
||||
rate 01 on a 3.5" MB drive (which is the only kind it can physically
|
||||
take, anyway), and rate 01 on a 3.5" MB drive is usually used by 3-mode
|
||||
drives to switch to 360 RPM. Hence why I'm switching DRVDEN to 1, so
|
||||
rate 01 becomes 500 kbps, so on a 3-mode 3.5" drive, 1.25 MB floppies
|
||||
can be read. The side effect is that to read 5.25" 360k drives, you
|
||||
need to use a dual-RPM 5.25" drive - but hey, that finally gets those
|
||||
drives some usage as well.
|
||||
*/
|
||||
fdc_update_drvrate(fdc, 0, !strcmp(machine_get_internal_name(), "if386sx"));
|
||||
fdc_update_drvrate(fdc, 1, !strcmp(machine_get_internal_name(), "if386sx"));
|
||||
fdc_update_drvrate(fdc, 2, !strcmp(machine_get_internal_name(), "if386sx"));
|
||||
fdc_update_drvrate(fdc, 3, !strcmp(machine_get_internal_name(), "if386sx"));
|
||||
fdc_update_drv2en(fdc, 1);
|
||||
fdc_update_rates(fdc);
|
||||
|
||||
@@ -2337,6 +2350,8 @@ fdc_close(void *priv)
|
||||
|
||||
fifo_close(fdc->fifo_p);
|
||||
|
||||
fdcinited = 0;
|
||||
|
||||
free(fdc);
|
||||
}
|
||||
|
||||
@@ -2382,6 +2397,8 @@ fdc_init(const device_t *info)
|
||||
|
||||
fdc_reset(fdc);
|
||||
|
||||
fdcinited = 1;
|
||||
|
||||
return fdc;
|
||||
}
|
||||
|
||||
|
||||
@@ -3452,6 +3452,7 @@ d86f_common_handlers(int drive)
|
||||
drives[drive].poll = d86f_poll;
|
||||
drives[drive].format = d86f_proxy_format;
|
||||
drives[drive].stop = d86f_stop;
|
||||
drives[drive].hole = d86f_hole;
|
||||
}
|
||||
|
||||
int
|
||||
|
||||
@@ -118,7 +118,6 @@ extern int vid_resize; /* (C) allow resizing */
|
||||
extern int invert_display; /* (C) invert the display */
|
||||
extern int suppress_overscan; /* (C) suppress overscans */
|
||||
extern uint32_t lang_id; /* (C) language code identifier */
|
||||
extern char icon_set[256]; /* (C) iconset identifier */
|
||||
extern int scale; /* (C) screen scale factor */
|
||||
extern int dpi_scale; /* (C) DPI scaling of the emulated screen */
|
||||
extern int vid_api; /* (C) video renderer */
|
||||
|
||||
@@ -459,6 +459,7 @@ extern void mem_a20_recalc(void);
|
||||
|
||||
extern void mem_init(void);
|
||||
extern void mem_close(void);
|
||||
extern void mem_zero(void);
|
||||
extern void mem_reset(void);
|
||||
extern void mem_remap_top_ex(int kb, uint32_t start);
|
||||
extern void mem_remap_top_ex_nomid(int kb, uint32_t start);
|
||||
|
||||
@@ -7,23 +7,20 @@
|
||||
#define MASTER_CLOCK 7159090
|
||||
|
||||
typedef struct cms_t {
|
||||
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];
|
||||
#ifdef SAASOUND_H_INCLUDED
|
||||
SAASND saasound;
|
||||
SAASND saasound2;
|
||||
#else
|
||||
void* saasound;
|
||||
void* saasound2;
|
||||
#endif
|
||||
|
||||
uint8_t latched_data;
|
||||
|
||||
int16_t buffer[SOUNDBUFLEN * 2];
|
||||
int16_t buffer[WTBUFLEN * 2];
|
||||
int16_t buffer2[WTBUFLEN * 2];
|
||||
|
||||
int pos;
|
||||
int pos, pos2;
|
||||
} cms_t;
|
||||
|
||||
extern void cms_update(cms_t *cms);
|
||||
|
||||
@@ -18,6 +18,12 @@
|
||||
#ifndef VIDEO_8514A_H
|
||||
#define VIDEO_8514A_H
|
||||
|
||||
#define INT_VSY (1 << 0)
|
||||
#define INT_GE_BSY (1 << 1)
|
||||
#define INT_FIFO_OVR (1 << 2)
|
||||
#define INT_FIFO_EMP (1 << 3)
|
||||
#define INT_MASK 0xf
|
||||
|
||||
typedef struct hwcursor8514_t {
|
||||
int ena;
|
||||
int x;
|
||||
@@ -61,6 +67,7 @@ typedef struct ibm8514_t {
|
||||
uint32_t vram_mask;
|
||||
uint32_t pallook[512];
|
||||
uint32_t bios_addr;
|
||||
uint32_t ma_latch;
|
||||
|
||||
PALETTE vgapal;
|
||||
uint8_t hwcursor_oddeven;
|
||||
@@ -117,6 +124,8 @@ typedef struct ibm8514_t {
|
||||
int y1;
|
||||
int y2;
|
||||
int temp_cnt;
|
||||
int16_t dx_ibm;
|
||||
int16_t dy_ibm;
|
||||
int16_t cx;
|
||||
int16_t cx_back;
|
||||
int16_t cy;
|
||||
@@ -216,10 +225,9 @@ typedef struct ibm8514_t {
|
||||
uint16_t subsys_cntl;
|
||||
uint8_t subsys_stat;
|
||||
|
||||
atomic_int fifo_idx;
|
||||
atomic_int ext_fifo_idx;
|
||||
atomic_int force_busy;
|
||||
atomic_int force_busy2;
|
||||
atomic_int fifo_idx;
|
||||
|
||||
int blitter_busy;
|
||||
uint64_t blitter_time;
|
||||
@@ -235,6 +243,16 @@ typedef struct ibm8514_t {
|
||||
PALETTE _8514pal;
|
||||
|
||||
latch8514_t latch;
|
||||
|
||||
void (*vblank_start)(void *priv);
|
||||
void (*accel_out_fifo)(void *priv, uint16_t port, uint16_t val, int len);
|
||||
void (*update_irqs)(void *priv);
|
||||
|
||||
} ibm8514_t;
|
||||
|
||||
#define IBM_8514A (((dev->local & 0xff) == 0x00) && (dev->extensions == 0x00))
|
||||
#define ATI_8514A_ULTRA (((dev->local & 0xff) == 0x00) && (dev->extensions == 0x01))
|
||||
#define ATI_GRAPHICS_ULTRA ((dev->local & 0xff) == 0x01)
|
||||
#define ATI_MACH32 ((dev->local & 0xff) == 0x02)
|
||||
|
||||
#endif /*VIDEO_8514A_H*/
|
||||
|
||||
@@ -25,6 +25,7 @@ typedef struct mach_t {
|
||||
rom_t bios_rom;
|
||||
rom_t bios_rom2;
|
||||
mem_mapping_t mmio_linear_mapping;
|
||||
mem_mapping_t banked_mapping;
|
||||
|
||||
int mca_bus;
|
||||
int pci_bus;
|
||||
@@ -71,7 +72,13 @@ typedef struct mach_t {
|
||||
uint8_t bank_r;
|
||||
uint16_t shadow_set;
|
||||
uint16_t shadow_cntl;
|
||||
int override_resolution;
|
||||
uint8_t overscan_col_8;
|
||||
uint8_t overscan_b_col_24;
|
||||
uint8_t overscan_g_col_24;
|
||||
uint8_t overscan_r_col_24;
|
||||
uint16_t fifo_test_data[17];
|
||||
int port_len;
|
||||
int crt_resolution;
|
||||
|
||||
struct {
|
||||
uint8_t line_idx;
|
||||
@@ -79,9 +86,9 @@ typedef struct mach_t {
|
||||
uint8_t patt_idx;
|
||||
uint8_t patt_len;
|
||||
uint8_t pix_trans[2];
|
||||
uint8_t eeprom_control;
|
||||
uint8_t alu_bg_fn;
|
||||
uint8_t alu_fg_fn;
|
||||
uint16_t eeprom_control;
|
||||
uint16_t clip_left;
|
||||
uint16_t clip_right;
|
||||
uint16_t clip_top;
|
||||
@@ -92,6 +99,7 @@ typedef struct mach_t {
|
||||
uint16_t src_x_end;
|
||||
uint16_t src_x_start;
|
||||
uint16_t src_x;
|
||||
uint16_t r_src_x;
|
||||
uint16_t src_y;
|
||||
int16_t bres_count;
|
||||
uint16_t clock_sel;
|
||||
@@ -100,6 +108,8 @@ typedef struct mach_t {
|
||||
uint16_t dest_cmp_fn;
|
||||
uint16_t dp_config;
|
||||
uint16_t ext_ge_config;
|
||||
uint16_t crt_offset_lo;
|
||||
uint16_t crt_offset_hi;
|
||||
uint16_t ge_offset_lo;
|
||||
uint16_t ge_offset_hi;
|
||||
uint16_t linedraw_opt;
|
||||
@@ -159,6 +169,7 @@ typedef struct mach_t {
|
||||
} accel;
|
||||
|
||||
atomic_int force_busy;
|
||||
atomic_int fifo_test_idx;
|
||||
} mach_t;
|
||||
|
||||
#endif /*VIDEO_ATI_MACH8_H*/
|
||||
|
||||
@@ -144,10 +144,10 @@ typedef struct ega_t {
|
||||
uint32_t (*remap_func)(struct ega_t *ega, uint32_t in_addr);
|
||||
void (*render)(struct ega_t *svga);
|
||||
|
||||
/*If set then another device is driving the monitor output and the EGA
|
||||
card should not attempt to display anything */
|
||||
void (*render_override)(void *priv);
|
||||
void *priv_parent;
|
||||
/* If set then another device is driving the monitor output and the EGA
|
||||
card should not attempt to display anything. */
|
||||
void (*render_override)(void *priv);
|
||||
void * priv_parent;
|
||||
} ega_t;
|
||||
#endif
|
||||
|
||||
@@ -159,6 +159,7 @@ extern const device_t atiega800p_device;
|
||||
extern const device_t iskra_ega_device;
|
||||
extern const device_t et2000_device;
|
||||
extern const device_t jega_device;
|
||||
extern const device_t jvga_device;
|
||||
#endif
|
||||
|
||||
extern int update_overscan;
|
||||
|
||||
@@ -310,6 +310,11 @@ typedef struct svga_t {
|
||||
void * ext8514;
|
||||
void * clock_gen8514;
|
||||
void * xga;
|
||||
|
||||
/* If set then another device is driving the monitor output and the EGA
|
||||
card should not attempt to display anything. */
|
||||
void (*render_override)(void *priv);
|
||||
void * priv_parent;
|
||||
} svga_t;
|
||||
|
||||
extern void ibm8514_set_poll(svga_t *svga);
|
||||
|
||||
@@ -25,14 +25,19 @@
|
||||
typedef struct vga_t {
|
||||
svga_t svga;
|
||||
|
||||
rom_t bios_rom;
|
||||
rom_t bios_rom;
|
||||
} vga_t;
|
||||
|
||||
extern void vga_out(uint16_t addr, uint8_t val, void *priv);
|
||||
extern void vga_out(uint16_t addr, uint8_t val, void *priv);
|
||||
extern uint8_t vga_in(uint16_t addr, void *priv);
|
||||
|
||||
void vga_disable(void* p);
|
||||
void vga_enable(void* p);
|
||||
int vga_isenabled(void* p);
|
||||
extern void vga_init(const device_t *info, vga_t *vga, int enabled);
|
||||
|
||||
extern void vga_disable(void* p);
|
||||
extern void vga_enable(void* p);
|
||||
|
||||
extern int vga_isenabled(void* p);
|
||||
|
||||
extern video_timings_t timing_vga;
|
||||
|
||||
#endif /*VIDEO_VGA_H*/
|
||||
|
||||
@@ -187,7 +187,7 @@ static const device_config_t ibmat_config[] = {
|
||||
};
|
||||
|
||||
const device_t ibmat_device = {
|
||||
.name = " IBM AT Devices",
|
||||
.name = "IBM AT",
|
||||
.internal_name = "ibmat_device",
|
||||
.flags = 0,
|
||||
.local = 0,
|
||||
@@ -294,7 +294,7 @@ static const device_config_t ibmxt286_config[] = {
|
||||
};
|
||||
|
||||
const device_t ibmxt286_device = {
|
||||
.name = "IBM XT Model 286 Devices",
|
||||
.name = "IBM XT Model 286",
|
||||
.internal_name = "ibmxt286_device",
|
||||
.flags = 0,
|
||||
.local = 0,
|
||||
|
||||
@@ -653,7 +653,9 @@ machine_at_if386sx_init(const machine_t *model)
|
||||
if (bios_only || !ret)
|
||||
return ret;
|
||||
|
||||
machine_at_common_init(model);
|
||||
machine_at_common_init_ex(model, 2);
|
||||
device_add(&amstrad_megapc_nvr_device); /* NVR that is initialized to all 0x00's. */
|
||||
|
||||
device_add(&keyboard_at_phoenix_device);
|
||||
|
||||
device_add(&neat_sx_device);
|
||||
@@ -663,6 +665,12 @@ machine_at_if386sx_init(const machine_t *model)
|
||||
if (fdc_current[0] == FDC_INTERNAL)
|
||||
device_add(&fdc_at_device);
|
||||
|
||||
/*
|
||||
One serial port - on the real hardware IF386AX, it is on the VL 16C451,
|
||||
alognside the bidirectional parallel port.
|
||||
*/
|
||||
device_add_inst(&ns16450_device, 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -691,12 +691,12 @@ static const device_config_t pb450_config[] = {
|
||||
.name = "bios",
|
||||
.description = "BIOS Version",
|
||||
.type = CONFIG_BIOS,
|
||||
.default_string = "pci10a",
|
||||
.default_string = "pb450a",
|
||||
.default_int = 0,
|
||||
.file_filter = "",
|
||||
.spinner = { 0 },
|
||||
.bios = {
|
||||
{ .name = "PCI 1.0A", .internal_name = "pb450" /*"pci10a"*/, .bios_type = BIOS_NORMAL,
|
||||
{ .name = "PCI 1.0A", .internal_name = "pb450a" /*"pci10a"*/, .bios_type = BIOS_NORMAL,
|
||||
.files_no = 1, .local = 0, .size = 131072, .files = { "roms/machines/pb450/OPTI802.bin", "" } },
|
||||
{ .name = "PNP 1.1A", .internal_name = "pnp11a", .bios_type = BIOS_NORMAL,
|
||||
.files_no = 1, .local = 0, .size = 131072, .files = { "roms/machines/pb450/PNP11A.bin", "" } },
|
||||
@@ -708,7 +708,7 @@ static const device_config_t pb450_config[] = {
|
||||
};
|
||||
|
||||
const device_t pb450_device = {
|
||||
.name = "Packard Bell PB450 Devices",
|
||||
.name = "Packard Bell PB450",
|
||||
.internal_name = "pb450_device",
|
||||
.flags = 0,
|
||||
.local = 0,
|
||||
@@ -732,7 +732,7 @@ machine_at_pb450_init(const machine_t *model)
|
||||
return ret;
|
||||
|
||||
device_context(model->device);
|
||||
fn = device_get_bios_file(model->device, device_get_config_bios("bios"), 0);
|
||||
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();
|
||||
|
||||
|
||||
@@ -290,7 +290,7 @@ const device_t ps1_2011_device = {
|
||||
.available = NULL,
|
||||
.speed_changed = NULL,
|
||||
.force_redraw = NULL,
|
||||
.config = &ps1_2011_config[0]
|
||||
.config = ps1_2011_config
|
||||
};
|
||||
|
||||
static void
|
||||
|
||||
@@ -108,7 +108,7 @@ static const device_config_t ibmpc_config[] = {
|
||||
};
|
||||
|
||||
const device_t ibmpc_device = {
|
||||
.name = "IBM PC (1981) Device",
|
||||
.name = "IBM PC (1981)",
|
||||
.internal_name = "ibmpc_device",
|
||||
.flags = 0,
|
||||
.local = 0,
|
||||
@@ -218,7 +218,7 @@ static const device_config_t ibmpc82_config[] = {
|
||||
};
|
||||
|
||||
const device_t ibmpc82_device = {
|
||||
.name = "IBM PC (1982) Devices",
|
||||
.name = "IBM PC (1982)",
|
||||
.internal_name = "ibmpc82_device",
|
||||
.flags = 0,
|
||||
.local = 0,
|
||||
@@ -331,7 +331,7 @@ static const device_config_t ibmxt_config[] = {
|
||||
};
|
||||
|
||||
const device_t ibmxt_device = {
|
||||
.name = "IBM XT (1982) Device",
|
||||
.name = "IBM XT (1982)",
|
||||
.internal_name = "ibmxt_device",
|
||||
.flags = 0,
|
||||
.local = 0,
|
||||
@@ -451,7 +451,7 @@ static const device_config_t ibmxt86_config[] = {
|
||||
};
|
||||
|
||||
const device_t ibmxt86_device = {
|
||||
.name = "IBM XT (1986) Device",
|
||||
.name = "IBM XT (1986)",
|
||||
.internal_name = "ibmxt86_device",
|
||||
.flags = 0,
|
||||
.local = 0,
|
||||
@@ -624,7 +624,7 @@ static const device_config_t jukopc_config[] = {
|
||||
};
|
||||
|
||||
const device_t jukopc_device = {
|
||||
.name = "Juko ST Devices",
|
||||
.name = "Juko ST",
|
||||
.internal_name = "jukopc_device",
|
||||
.flags = 0,
|
||||
.local = 0,
|
||||
@@ -949,7 +949,7 @@ static const device_config_t vendex_config[] = {
|
||||
};
|
||||
|
||||
const device_t vendex_device = {
|
||||
.name = "Vendex 888T Devices",
|
||||
.name = "Vendex 888T",
|
||||
.internal_name = "vendex_device",
|
||||
.flags = 0,
|
||||
.local = 0,
|
||||
|
||||
@@ -154,6 +154,8 @@ machine_xt_laserxt_common_init(const machine_t *model,int is_lxt3)
|
||||
standalone_gameport_type = &gameport_device;
|
||||
|
||||
laserxt_init(is_lxt3);
|
||||
|
||||
device_add(&keyboard_xt_lxt3_device);
|
||||
}
|
||||
|
||||
int
|
||||
@@ -167,8 +169,6 @@ machine_xt_laserxt_init(const machine_t *model)
|
||||
if (bios_only || !ret)
|
||||
return ret;
|
||||
|
||||
device_add(&keyboard_xt_device);
|
||||
|
||||
machine_xt_laserxt_common_init(model, 0);
|
||||
|
||||
return ret;
|
||||
@@ -185,8 +185,6 @@ machine_xt_lxt3_init(const machine_t *model)
|
||||
if (bios_only || !ret)
|
||||
return ret;
|
||||
|
||||
device_add(&keyboard_xt_lxt3_device);
|
||||
|
||||
machine_xt_laserxt_common_init(model, 1);
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -1721,7 +1721,7 @@ const machine_t machines[] = {
|
||||
.available_flag = MACHINE_AVAILABLE,
|
||||
.gpio_acpi_handler = NULL,
|
||||
.cpu = {
|
||||
.package = CPU_PKG_8088,
|
||||
.package = CPU_PKG_8088_VTECH,
|
||||
.block = CPU_BLOCK_NONE,
|
||||
.min_bus = 0,
|
||||
.max_bus = 0,
|
||||
@@ -1762,7 +1762,7 @@ const machine_t machines[] = {
|
||||
.available_flag = MACHINE_AVAILABLE,
|
||||
.gpio_acpi_handler = NULL,
|
||||
.cpu = {
|
||||
.package = CPU_PKG_8088_VTECH,
|
||||
.package = CPU_PKG_8088,
|
||||
.block = CPU_BLOCK_NONE,
|
||||
.min_bus = 0,
|
||||
.max_bus = 0,
|
||||
@@ -6981,46 +6981,6 @@ const machine_t machines[] = {
|
||||
.snd_device = NULL,
|
||||
.net_device = NULL
|
||||
},
|
||||
/* has a Phoenix PLCC Multikey copyrighted 1993, version unknown. */
|
||||
{
|
||||
.name = "[OPTi 895] Packard Bell PB450",
|
||||
.internal_name = "pb450",
|
||||
.type = MACHINE_TYPE_486_S3_PCI,
|
||||
.chipset = MACHINE_CHIPSET_OPTI_895_802G,
|
||||
.init = machine_at_pb450_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_PCI,
|
||||
.flags = MACHINE_SUPER_IO | MACHINE_IDE_DUAL | MACHINE_VIDEO,
|
||||
.ram = {
|
||||
.min = 1024,
|
||||
.max = 65536,
|
||||
.step = 1024
|
||||
},
|
||||
.nvrmask = 255,
|
||||
.kbc_device = NULL,
|
||||
.kbc_p1 = 0xff,
|
||||
.gpio = 0xffffffff,
|
||||
.gpio_acpi = 0xffffffff,
|
||||
.device = &pb450_device,
|
||||
.fdc_device = NULL,
|
||||
.sio_device = NULL,
|
||||
.vid_device = &gd5428_vlb_onboard_device,
|
||||
.snd_device = NULL,
|
||||
.net_device = NULL
|
||||
},
|
||||
/* Uses an NEC 90M002A (UPD82C42C, 8042 clone) with unknown firmware. */
|
||||
{
|
||||
.name = "[SiS 461] Acer V10",
|
||||
@@ -7915,6 +7875,46 @@ const machine_t machines[] = {
|
||||
.snd_device = NULL,
|
||||
.net_device = NULL
|
||||
},
|
||||
/* has a Phoenix PLCC Multikey copyrighted 1993, version unknown. */
|
||||
{
|
||||
.name = "[OPTi 895] Packard Bell PB450",
|
||||
.internal_name = "pb450",
|
||||
.type = MACHINE_TYPE_486_S3_PCI,
|
||||
.chipset = MACHINE_CHIPSET_OPTI_895_802G,
|
||||
.init = machine_at_pb450_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_PCI,
|
||||
.flags = MACHINE_SUPER_IO | MACHINE_IDE_DUAL | MACHINE_VIDEO,
|
||||
.ram = {
|
||||
.min = 1024,
|
||||
.max = 65536,
|
||||
.step = 1024
|
||||
},
|
||||
.nvrmask = 255,
|
||||
.kbc_device = NULL,
|
||||
.kbc_p1 = 0xff,
|
||||
.gpio = 0xffffffff,
|
||||
.gpio_acpi = 0xffffffff,
|
||||
.device = &pb450_device,
|
||||
.fdc_device = NULL,
|
||||
.sio_device = NULL,
|
||||
.vid_device = &gd5428_vlb_onboard_device,
|
||||
.snd_device = NULL,
|
||||
.net_device = NULL
|
||||
},
|
||||
/* This has an AMIKey-2, which is an updated version of type 'H'. */
|
||||
{
|
||||
.name = "[i420EX] ASUS PVI-486AP4",
|
||||
|
||||
@@ -2792,6 +2792,17 @@ mem_init_ram_mapping(mem_mapping_t *mapping, uint32_t base, uint32_t size)
|
||||
mem_add_ram_mapping(mapping, base, size);
|
||||
}
|
||||
|
||||
void
|
||||
mem_zero(void)
|
||||
{
|
||||
#if (!(defined __amd64__ || defined _M_X64 || defined __aarch64__ || defined _M_ARM64))
|
||||
if (mem_size > 1048576)
|
||||
memset(ram2, 0x00, ram2_size + 16);
|
||||
#endif
|
||||
|
||||
memset(ram, 0x00, ram_size + 16);
|
||||
}
|
||||
|
||||
/* Reset the memory state. */
|
||||
void
|
||||
mem_reset(void)
|
||||
@@ -2867,8 +2878,10 @@ mem_reset(void)
|
||||
return;
|
||||
}
|
||||
memset(ram, 0x00, ram_size + 16);
|
||||
#if (!(defined __amd64__ || defined _M_X64 || defined __aarch64__ || defined _M_ARM64))
|
||||
if (mem_size > 1048576)
|
||||
ram2 = &(ram[1 << 30]);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2963,6 +2976,7 @@ mem_reset(void)
|
||||
else if (cpu_16bitbus && is6117 && mem_size > 65408)
|
||||
mem_init_ram_mapping(&ram_high_mapping, 0x100000, (65408 - 1024) * 1024);
|
||||
else {
|
||||
#if (!(defined __amd64__ || defined _M_X64 || defined __aarch64__ || defined _M_ARM64))
|
||||
if (mem_size > 1048576) {
|
||||
mem_init_ram_mapping(&ram_high_mapping, 0x100000, (1048576 - 1024) * 1024);
|
||||
|
||||
@@ -2975,6 +2989,9 @@ mem_reset(void)
|
||||
ram2, MEM_MAPPING_INTERNAL, NULL);
|
||||
} else
|
||||
mem_init_ram_mapping(&ram_high_mapping, 0x100000, (mem_size - 1024) * 1024);
|
||||
#else
|
||||
mem_init_ram_mapping(&ram_high_mapping, 0x100000, (mem_size - 1024) * 1024);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1071,7 +1071,7 @@ nic_init(const device_t *info)
|
||||
mac_oui = (((int) dev->maclocal[0]) << 16);
|
||||
mac_oui |= (((int) dev->maclocal[1]) << 8);
|
||||
mac_oui |= ((int) dev->maclocal[2]);
|
||||
device_set_config_mac("mac", mac);
|
||||
device_set_config_mac("mac_oui", mac_oui);
|
||||
} else {
|
||||
dev->maclocal[0] = (mac_oui >> 16) & 0xff;
|
||||
dev->maclocal[1] = (mac_oui >> 8) & 0xff;
|
||||
|
||||
@@ -419,6 +419,8 @@ pci_trc_reset(uint8_t val)
|
||||
mem_a20_recalc();
|
||||
|
||||
flushmmucache();
|
||||
|
||||
mem_zero();
|
||||
}
|
||||
|
||||
#ifdef USE_DYNAREC
|
||||
|
||||
@@ -194,6 +194,9 @@ add_library(ui STATIC
|
||||
qt_openglshaderconfig.hpp
|
||||
qt_openglshaderconfig.cpp
|
||||
qt_openglshaderconfig.ui
|
||||
|
||||
qt_iconindicators.hpp
|
||||
qt_iconindicators.cpp
|
||||
)
|
||||
|
||||
if(RTMIDI)
|
||||
|
||||
BIN
src/qt/icons/active.ico
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
BIN
src/qt/icons/disabled.ico
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
@@ -35,8 +35,6 @@ qt_nvr_save(void)
|
||||
return nvr_save();
|
||||
}
|
||||
|
||||
char icon_set[256] = ""; /* name of the iconset to be used */
|
||||
|
||||
int
|
||||
plat_vidapi(const char *api)
|
||||
{
|
||||
|
||||
@@ -37,7 +37,7 @@ void
|
||||
HardwareRenderer::resizeGL(int w, int h)
|
||||
{
|
||||
m_context->makeCurrent(this);
|
||||
glViewport(0, 0, qRound(w * devicePixelRatio()), qRound(h * devicePixelRatio()));
|
||||
glViewport(0, 0, qRound(w * devicePixelRatioF()), qRound(h * devicePixelRatioF()));
|
||||
}
|
||||
|
||||
#define PROGRAM_VERTEX_ATTRIBUTE 0
|
||||
@@ -145,7 +145,7 @@ HardwareRenderer::paintGL()
|
||||
QVector<QVector2D> texcoords;
|
||||
QMatrix4x4 mat;
|
||||
mat.setToIdentity();
|
||||
mat.ortho(QRectF(0, 0, (qreal) width(), (qreal) height()));
|
||||
mat.ortho(QRectF(0, 0, (qreal) width() * (qreal) devicePixelRatioF(), (qreal) height() * (qreal) devicePixelRatioF()));
|
||||
verts.push_back(QVector2D((float) destination.x(), (float) destination.y()));
|
||||
verts.push_back(QVector2D((float) destination.x(), (float) destination.y() + (float) destination.height()));
|
||||
verts.push_back(QVector2D((float) destination.x() + (float) destination.width(), (float) destination.y() + (float) destination.height()));
|
||||
@@ -220,14 +220,17 @@ HardwareRenderer::onBlit(int buf_idx, int x, int y, int w, int h)
|
||||
#endif
|
||||
buf_usage[buf_idx].clear();
|
||||
source.setRect(x, y, w, h);
|
||||
if (origSource != source)
|
||||
if (origSource != source) {
|
||||
this->pixelRatio = devicePixelRatioF();
|
||||
onResize(this->width(), this->height());
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
void
|
||||
HardwareRenderer::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
this->pixelRatio = devicePixelRatioF();
|
||||
onResize(width(), height());
|
||||
|
||||
QOpenGLWindow::resizeEvent(event);
|
||||
|
||||
33
src/qt/qt_iconindicators.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#include <QSize>
|
||||
#include <QPainter>
|
||||
#include "qt_iconindicators.hpp"
|
||||
|
||||
QIcon
|
||||
getIndicatorIcon(IconIndicator indicator)
|
||||
{
|
||||
switch (indicator) {
|
||||
case Active:
|
||||
return QIcon(":/settings/qt/icons/active.ico");
|
||||
case Disabled:
|
||||
return QIcon(":/settings/qt/icons/disabled.ico");
|
||||
default:
|
||||
return QIcon();
|
||||
}
|
||||
}
|
||||
|
||||
QPixmap
|
||||
getIconWithIndicator(const QIcon &icon, const QSize &size, QIcon::Mode iconMode, IconIndicator indicator)
|
||||
{
|
||||
auto iconPixmap = icon.pixmap(size, iconMode);
|
||||
|
||||
if (indicator == None)
|
||||
return iconPixmap;
|
||||
|
||||
auto painter = QPainter(&iconPixmap);
|
||||
auto indicatorPixmap = getIndicatorIcon(indicator).pixmap(size);
|
||||
|
||||
painter.drawPixmap(0, 0, indicatorPixmap);
|
||||
painter.end();
|
||||
|
||||
return iconPixmap;
|
||||
}
|
||||
15
src/qt/qt_iconindicators.hpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef QT_ICONINDICATORS_HPP
|
||||
# define QT_INDICATORS_HPP
|
||||
|
||||
#include <QPixmap>
|
||||
#include <QIcon>
|
||||
|
||||
enum IconIndicator {
|
||||
None,
|
||||
Active,
|
||||
Disabled,
|
||||
};
|
||||
|
||||
QPixmap getIconWithIndicator(const QIcon &icon, const QSize &size, QIcon::Mode iconMode, IconIndicator indicator);
|
||||
|
||||
#endif
|
||||
@@ -40,6 +40,8 @@ extern "C" {
|
||||
#include <86box/ui.h>
|
||||
#include <86box/machine_status.h>
|
||||
#include <86box/config.h>
|
||||
|
||||
extern volatile int fdcinited;
|
||||
};
|
||||
|
||||
#include <QIcon>
|
||||
@@ -54,6 +56,7 @@ extern "C" {
|
||||
#include "qt_mainwindow.hpp"
|
||||
#include "qt_soundgain.hpp"
|
||||
#include "qt_progsettings.hpp"
|
||||
#include "qt_iconindicators.hpp"
|
||||
|
||||
#include <array>
|
||||
|
||||
@@ -65,19 +68,24 @@ namespace {
|
||||
struct PixmapSetActive {
|
||||
QPixmap normal;
|
||||
QPixmap active;
|
||||
void load(const QString &basePath);
|
||||
void load(const QIcon &icon);
|
||||
};
|
||||
struct PixmapSetDisabled {
|
||||
QPixmap normal;
|
||||
QPixmap disabled;
|
||||
void load(const QIcon &icon);
|
||||
};
|
||||
struct PixmapSetEmpty {
|
||||
QPixmap normal;
|
||||
QPixmap empty;
|
||||
void load(const QString &basePath);
|
||||
void load(const QIcon &icon);
|
||||
};
|
||||
struct PixmapSetEmptyActive {
|
||||
QPixmap normal;
|
||||
QPixmap active;
|
||||
QPixmap empty;
|
||||
QPixmap empty_active;
|
||||
void load(QString basePath);
|
||||
void load(const QIcon &icon);
|
||||
};
|
||||
struct Pixmaps {
|
||||
PixmapSetEmpty cartridge;
|
||||
@@ -90,7 +98,7 @@ struct Pixmaps {
|
||||
PixmapSetEmptyActive mo;
|
||||
PixmapSetActive hd;
|
||||
PixmapSetEmptyActive net;
|
||||
QPixmap sound, soundMuted;
|
||||
PixmapSetDisabled sound;
|
||||
};
|
||||
|
||||
struct StateActive {
|
||||
@@ -170,30 +178,35 @@ struct StateEmptyActive {
|
||||
};
|
||||
|
||||
static QSize pixmap_size(16, 16);
|
||||
static const QString pixmap_empty = QStringLiteral("_empty");
|
||||
static const QString pixmap_active = QStringLiteral("_active");
|
||||
static const QString pixmap_empty_active = QStringLiteral("_empty_active");
|
||||
|
||||
void
|
||||
PixmapSetEmpty::load(const QString &basePath)
|
||||
PixmapSetEmpty::load(const QIcon &icon)
|
||||
{
|
||||
normal = ProgSettings::loadIcon(basePath.arg(QStringLiteral(""))).pixmap(pixmap_size);
|
||||
empty = ProgSettings::loadIcon(basePath.arg(pixmap_empty)).pixmap(pixmap_size);
|
||||
normal = getIconWithIndicator(icon, pixmap_size, QIcon::Normal, None);
|
||||
empty = getIconWithIndicator(icon, pixmap_size, QIcon::Disabled, None);
|
||||
}
|
||||
|
||||
void
|
||||
PixmapSetActive::load(const QString &basePath)
|
||||
PixmapSetActive::load(const QIcon &icon)
|
||||
{
|
||||
normal = ProgSettings::loadIcon(basePath.arg(QStringLiteral(""))).pixmap(pixmap_size);
|
||||
active = ProgSettings::loadIcon(basePath.arg(pixmap_active)).pixmap(pixmap_size);
|
||||
normal = getIconWithIndicator(icon, pixmap_size, QIcon::Normal, None);
|
||||
active = getIconWithIndicator(icon, pixmap_size, QIcon::Normal, Active);
|
||||
}
|
||||
|
||||
void
|
||||
PixmapSetEmptyActive::load(QString basePath)
|
||||
PixmapSetDisabled::load(const QIcon &icon)
|
||||
{
|
||||
normal = ProgSettings::loadIcon(basePath.arg(QStringLiteral(""))).pixmap(pixmap_size);
|
||||
active = ProgSettings::loadIcon(basePath.arg(pixmap_active)).pixmap(pixmap_size);
|
||||
empty = ProgSettings::loadIcon(basePath.arg(pixmap_empty)).pixmap(pixmap_size);
|
||||
empty_active = ProgSettings::loadIcon(basePath.arg(pixmap_empty_active)).pixmap(pixmap_size);
|
||||
normal = getIconWithIndicator(icon, pixmap_size, QIcon::Normal, None);
|
||||
disabled = getIconWithIndicator(icon, pixmap_size, QIcon::Disabled, Disabled);
|
||||
}
|
||||
|
||||
void
|
||||
PixmapSetEmptyActive::load(const QIcon &icon)
|
||||
{
|
||||
normal = getIconWithIndicator(icon, pixmap_size, QIcon::Normal, None);
|
||||
active = getIconWithIndicator(icon, pixmap_size, QIcon::Normal, Active);
|
||||
empty = getIconWithIndicator(icon, pixmap_size, QIcon::Disabled, None);
|
||||
empty_active = getIconWithIndicator(icon, pixmap_size, QIcon::Disabled, Active);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,21 +215,20 @@ struct MachineStatus::States {
|
||||
|
||||
States(QObject *parent)
|
||||
{
|
||||
pixmaps.cartridge.load("/cartridge%1.ico");
|
||||
pixmaps.cassette.load("/cassette%1.ico");
|
||||
pixmaps.floppy_disabled.normal = ProgSettings::loadIcon(QStringLiteral("/floppy_disabled.ico")).pixmap(pixmap_size);
|
||||
pixmaps.cartridge.load(QIcon(":/settings/qt/icons/cartridge.ico"));
|
||||
pixmaps.cassette.load(QIcon(":/settings/qt/icons/cassette.ico"));
|
||||
pixmaps.floppy_disabled.normal = QIcon(":/settings/qt/icons/floppy_disabled.ico").pixmap(pixmap_size);
|
||||
pixmaps.floppy_disabled.active = pixmaps.floppy_disabled.normal;
|
||||
pixmaps.floppy_disabled.empty = pixmaps.floppy_disabled.normal;
|
||||
pixmaps.floppy_disabled.empty_active = pixmaps.floppy_disabled.normal;
|
||||
pixmaps.floppy_525.load("/floppy_525%1.ico");
|
||||
pixmaps.floppy_35.load("/floppy_35%1.ico");
|
||||
pixmaps.cdrom.load("/cdrom%1.ico");
|
||||
pixmaps.zip.load("/zip%1.ico");
|
||||
pixmaps.mo.load("/mo%1.ico");
|
||||
pixmaps.hd.load("/hard_disk%1.ico");
|
||||
pixmaps.net.load("/network%1.ico");
|
||||
pixmaps.sound = ProgSettings::loadIcon("/sound.ico").pixmap(pixmap_size);
|
||||
pixmaps.soundMuted = ProgSettings::loadIcon("/sound_mute.ico").pixmap(pixmap_size);
|
||||
pixmaps.floppy_525.load(QIcon(":/settings/qt/icons/floppy_525.ico"));
|
||||
pixmaps.floppy_35.load(QIcon(":/settings/qt/icons/floppy_35.ico"));
|
||||
pixmaps.cdrom.load(QIcon(":/settings/qt/icons/cdrom.ico"));
|
||||
pixmaps.zip.load(QIcon(":/settings/qt/icons/zip.ico"));
|
||||
pixmaps.mo.load(QIcon(":/settings/qt/icons/mo.ico"));
|
||||
pixmaps.hd.load(QIcon(":/settings/qt/icons/hard_disk.ico"));
|
||||
pixmaps.net.load(QIcon(":/settings/qt/icons/network.ico"));
|
||||
pixmaps.sound.load(QIcon(":/settings/qt/icons/sound.ico"));
|
||||
|
||||
cartridge[0].pixmaps = &pixmaps.cartridge;
|
||||
cartridge[1].pixmaps = &pixmaps.cartridge;
|
||||
@@ -293,6 +305,9 @@ MachineStatus::hasSCSI()
|
||||
void
|
||||
MachineStatus::iterateFDD(const std::function<void(int)> &cb)
|
||||
{
|
||||
if (!fdcinited)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < FDD_NUM; ++i) {
|
||||
if (fdd_get_type(i) != 0) {
|
||||
cb(i);
|
||||
@@ -510,8 +525,8 @@ MachineStatus::refresh(QStatusBar *sbar)
|
||||
sound_muted ^= 1;
|
||||
config_save();
|
||||
if (d->sound)
|
||||
d->sound->setPixmap(sound_muted ? d->pixmaps.soundMuted : d->pixmaps.sound);
|
||||
|
||||
d->sound->setPixmap(sound_muted ? d->pixmaps.sound.disabled : d->pixmaps.sound.normal);
|
||||
|
||||
muteUnmuteAction->setText(sound_muted ? tr("&Unmute") : tr("&Mute"));
|
||||
});
|
||||
}
|
||||
@@ -694,10 +709,10 @@ MachineStatus::refresh(QStatusBar *sbar)
|
||||
}
|
||||
|
||||
d->sound = std::make_unique<ClickableLabel>();
|
||||
d->sound->setPixmap(sound_muted ? d->pixmaps.soundMuted : d->pixmaps.sound);
|
||||
d->sound->setPixmap(sound_muted ? d->pixmaps.sound.disabled : d->pixmaps.sound.normal);
|
||||
if (muteUnmuteAction)
|
||||
muteUnmuteAction->setText(sound_muted ? tr("&Unmute") : tr("&Mute"));
|
||||
|
||||
|
||||
connect(d->sound.get(), &ClickableLabel::clicked, this, [this](QPoint pos) {
|
||||
this->soundMenu->popup(pos - QPoint(0, this->soundMenu->sizeHint().height()));
|
||||
});
|
||||
|
||||
@@ -94,6 +94,8 @@ extern int qt_nvr_save(void);
|
||||
bool cpu_thread_running = false;
|
||||
}
|
||||
|
||||
#include <locale.h>
|
||||
|
||||
void qt_set_sequence_auto_mnemonic(bool b);
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
@@ -525,6 +527,7 @@ main(int argc, char *argv[])
|
||||
|
||||
QApplication app(argc, argv);
|
||||
QLocale::setDefault(QLocale::C);
|
||||
setlocale(LC_NUMERIC, "C");
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
Q_INIT_RESOURCE(darkstyle);
|
||||
|
||||
@@ -803,7 +803,7 @@ MainWindow::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
//qDebug() << pos().x() + event->size().width();
|
||||
//qDebug() << pos().y() + event->size().height();
|
||||
if (vid_resize == 1)
|
||||
if (vid_resize == 1 || video_fullscreen)
|
||||
return;
|
||||
|
||||
int newX = pos().x();
|
||||
|
||||
@@ -154,10 +154,10 @@ MediaMenu::refresh(QMenu *parentMenu)
|
||||
MachineStatus::iterateCDROM([this, parentMenu](int i) {
|
||||
auto *menu = parentMenu->addMenu("");
|
||||
cdromMutePos = menu->children().count();
|
||||
menu->addAction(ProgSettings::loadIcon("/cdrom_mute.ico"), tr("&Mute"), [this, i]() { cdromMute(i); })->setCheckable(true);
|
||||
menu->addAction(QIcon(":/settings/qt/icons/cdrom_mute.ico"), tr("&Mute"), [this, i]() { cdromMute(i); })->setCheckable(true);
|
||||
menu->addSeparator();
|
||||
menu->addAction(ProgSettings::loadIcon("/cdrom_image.ico"), tr("&Image..."), [this, i]() { cdromMount(i, 0, nullptr); })->setCheckable(false);
|
||||
menu->addAction(ProgSettings::loadIcon("/cdrom_folder.ico"), tr("&Folder..."), [this, i]() { cdromMount(i, 1, nullptr); })->setCheckable(false);
|
||||
menu->addAction(QIcon(":/settings/qt/icons/cdrom_image.ico"), tr("&Image..."), [this, i]() { cdromMount(i, 0, nullptr); })->setCheckable(false);
|
||||
menu->addAction(QIcon(":/settings/qt/icons/cdrom_folder.ico"), tr("&Folder..."), [this, i]() { cdromMount(i, 1, nullptr); })->setCheckable(false);
|
||||
menu->addSeparator();
|
||||
for (int slot = 0; slot < MAX_PREV_IMAGES; slot++) {
|
||||
cdromImageHistoryPos[slot] = menu->children().count();
|
||||
@@ -170,7 +170,7 @@ MediaMenu::refresh(QMenu *parentMenu)
|
||||
for (const auto &letter : driveLetters) {
|
||||
auto drive = QString("%1:\\").arg(letter);
|
||||
if (GetDriveType(drive.toUtf8().constData()) == DRIVE_CDROM)
|
||||
menu->addAction(ProgSettings::loadIcon("/cdrom_host.ico"), tr("Host CD/DVD Drive (%1:)").arg(letter), [this, i, letter] { cdromMount(i, 2, QString(R"(\\.\%1:)").arg(letter)); })->setCheckable(false);
|
||||
menu->addAction(QIcon(":/settings/qt/icons/cdrom_host.ico"), tr("Host CD/DVD Drive (%1:)").arg(letter), [this, i, letter] { cdromMount(i, 2, QString(R"(\\.\%1:)").arg(letter)); })->setCheckable(false);
|
||||
}
|
||||
menu->addSeparator();
|
||||
#endif // Q_OS_WINDOWS
|
||||
@@ -666,7 +666,7 @@ MediaMenu::updateImageHistory(int index, int slot, ui::MediaType type)
|
||||
children = menu->children();
|
||||
imageHistoryUpdatePos = dynamic_cast<QAction *>(children[cdromImageHistoryPos[slot]]);
|
||||
if (fn.left(8) == "ioctl://") {
|
||||
menu_icon = ProgSettings::loadIcon("/cdrom_host.ico");
|
||||
menu_icon = QIcon(":/settings/qt/icons/cdrom_host.ico");
|
||||
#ifdef Q_OS_WINDOWS
|
||||
menu_item_name = tr("Host CD/DVD Drive (%1)").arg(fn.right(2)).toUtf8().constData();
|
||||
#else
|
||||
@@ -674,7 +674,7 @@ MediaMenu::updateImageHistory(int index, int slot, ui::MediaType type)
|
||||
#endif
|
||||
} else {
|
||||
fi.setFile(fn);
|
||||
menu_icon = fi.isDir() ? ProgSettings::loadIcon("/cdrom_folder.ico") : ProgSettings::loadIcon("/cdrom_image.ico");
|
||||
menu_icon = fi.isDir() ? QIcon(":/settings/qt/icons/cdrom_folder.ico") : QIcon(":/settings/qt/icons/cdrom_image.ico");
|
||||
menu_item_name = fn.isEmpty() ? tr("previous image").toUtf8().constData() : fn.toUtf8().constData();
|
||||
}
|
||||
imageHistoryUpdatePos->setIcon(menu_icon);
|
||||
@@ -727,7 +727,7 @@ MediaMenu::cdromUpdateMenu(int i)
|
||||
auto childs = menu->children();
|
||||
|
||||
auto *muteMenu = dynamic_cast<QAction *>(childs[cdromMutePos]);
|
||||
muteMenu->setIcon(ProgSettings::loadIcon((cdrom[i].sound_on == 0) ? "/cdrom_unmute.ico" : "/cdrom_mute.ico"));
|
||||
muteMenu->setIcon(QIcon((cdrom[i].sound_on == 0) ? ":/settings/qt/icons/cdrom_unmute.ico" : ":/settings/qt/icons/cdrom_mute.ico"));
|
||||
muteMenu->setText((cdrom[i].sound_on == 0) ? tr("&Unmute") : tr("&Mute"));
|
||||
|
||||
auto *imageMenu = dynamic_cast<QAction *>(childs[cdromImagePos]);
|
||||
@@ -740,13 +740,13 @@ MediaMenu::cdromUpdateMenu(int i)
|
||||
menu_item_name = tr("Host CD/DVD Drive (%1)").arg(name.right(name.length() - 8));
|
||||
#endif
|
||||
name2 = menu_item_name;
|
||||
menu_icon = ProgSettings::loadIcon("/cdrom_host.ico");
|
||||
menu_icon = QIcon(":/settings/qt/icons/cdrom_host.ico");
|
||||
} else {
|
||||
QFileInfo fi(cdrom[i].image_path);
|
||||
|
||||
menu_item_name = name.isEmpty() ? QString().toUtf8().constData() : name.toUtf8().constData();
|
||||
name2 = name;
|
||||
menu_icon = fi.isDir() ? ProgSettings::loadIcon("/cdrom_folder.ico") : ProgSettings::loadIcon("/cdrom_image.ico");
|
||||
menu_icon = fi.isDir() ? QIcon(":/settings/qt/icons/cdrom_folder.ico") : QIcon(":/settings/qt/icons/cdrom_image.ico");
|
||||
}
|
||||
imageMenu->setIcon(menu_icon);
|
||||
imageMenu->setText(QString::asprintf(tr("Eject %s").toUtf8().constData(), menu_item_name.toUtf8().constData()));
|
||||
|
||||
@@ -1154,14 +1154,15 @@ OpenGLRenderer::onBlit(int buf_idx, int x, int y, int w, int h)
|
||||
|
||||
buf_usage[buf_idx].clear();
|
||||
source.setRect(x, y, w, h);
|
||||
this->pixelRatio = devicePixelRatio();
|
||||
onResize(this->width(), this->height());
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
glw.glViewport(
|
||||
destination.x() * devicePixelRatio(),
|
||||
destination.y() * devicePixelRatio(),
|
||||
destination.width() * devicePixelRatio(),
|
||||
destination.height() * devicePixelRatio());
|
||||
destination.x(),
|
||||
destination.y(),
|
||||
destination.width(),
|
||||
destination.height());
|
||||
#endif
|
||||
|
||||
if (video_framerate == -1)
|
||||
@@ -1187,6 +1188,7 @@ OpenGLRenderer::exposeEvent(QExposeEvent *event)
|
||||
if (!isInitialized)
|
||||
initialize();
|
||||
|
||||
this->pixelRatio = devicePixelRatio();
|
||||
onResize(size().width(), size().height());
|
||||
}
|
||||
|
||||
@@ -1195,6 +1197,7 @@ OpenGLRenderer::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
|
||||
this->pixelRatio = devicePixelRatio();
|
||||
onResize(event->size().width(), event->size().height());
|
||||
|
||||
if (notReady())
|
||||
@@ -1203,10 +1206,10 @@ OpenGLRenderer::resizeEvent(QResizeEvent *event)
|
||||
context->makeCurrent(this);
|
||||
|
||||
glw.glViewport(
|
||||
destination.x() * devicePixelRatio(),
|
||||
destination.y() * devicePixelRatio(),
|
||||
destination.width() * devicePixelRatio(),
|
||||
destination.height() * devicePixelRatio());
|
||||
destination.x(),
|
||||
destination.y(),
|
||||
destination.width(),
|
||||
destination.height());
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1386,10 +1389,10 @@ OpenGLRenderer::render()
|
||||
uint32_t x, y, w, h;
|
||||
} window_rect;
|
||||
|
||||
window_rect.x = destination.x() * devicePixelRatio();
|
||||
window_rect.y = destination.y() * devicePixelRatio();
|
||||
window_rect.w = destination.width() * devicePixelRatio();
|
||||
window_rect.h = destination.height() * devicePixelRatio();
|
||||
window_rect.x = destination.x();
|
||||
window_rect.y = destination.y();
|
||||
window_rect.w = destination.width();
|
||||
window_rect.h = destination.height();
|
||||
|
||||
glw.glBindTexture(GL_TEXTURE_2D, scene_texture.id);
|
||||
scene_texture.min_filter = scene_texture.mag_filter = video_filter_method ? GL_LINEAR : GL_NEAREST;
|
||||
@@ -1652,7 +1655,7 @@ OpenGLRenderer::render()
|
||||
}
|
||||
|
||||
if (monitors[r_monitor_index].mon_screenshots) {
|
||||
int width = destination.width() * devicePixelRatio(), height = destination.height() * devicePixelRatio();
|
||||
int width = destination.width(), height = destination.height();
|
||||
char path[1024];
|
||||
char fn[256];
|
||||
|
||||
|
||||
@@ -36,71 +36,16 @@ extern "C" {
|
||||
#include <86box/rom.h>
|
||||
}
|
||||
|
||||
static QMap<QString, QString> iconset_to_qt;
|
||||
extern MainWindow *main_window;
|
||||
|
||||
ProgSettings::CustomTranslator *ProgSettings::translator = nullptr;
|
||||
QTranslator *ProgSettings::qtTranslator = nullptr;
|
||||
QString
|
||||
ProgSettings::getIconSetPath()
|
||||
{
|
||||
if (iconset_to_qt.isEmpty()) {
|
||||
// Always include default bundled icons
|
||||
iconset_to_qt.insert("", ":/settings/qt/icons");
|
||||
// Walk rom_paths to get the candidates
|
||||
for (rom_path_t *emu_rom_path = &rom_paths; emu_rom_path != nullptr; emu_rom_path = emu_rom_path->next) {
|
||||
// Check for icons subdir in each candidate
|
||||
QDir roms_icons_dir(QString(emu_rom_path->path) + "/icons");
|
||||
if (roms_icons_dir.isReadable()) {
|
||||
auto dirList = roms_icons_dir.entryList(QDir::AllDirs | QDir::Executable | QDir::Readable);
|
||||
for (auto &curIconSet : dirList) {
|
||||
if (curIconSet == "." || curIconSet == "..") {
|
||||
continue;
|
||||
}
|
||||
iconset_to_qt.insert(curIconSet, (roms_icons_dir.canonicalPath() + '/') + curIconSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return iconset_to_qt[icon_set];
|
||||
}
|
||||
|
||||
QIcon
|
||||
ProgSettings::loadIcon(QString file)
|
||||
{
|
||||
(void) getIconSetPath();
|
||||
if (!QFile::exists(iconset_to_qt[icon_set] + file))
|
||||
return QIcon(iconset_to_qt[""] + file);
|
||||
return QIcon(iconset_to_qt[icon_set] + file);
|
||||
}
|
||||
|
||||
ProgSettings::ProgSettings(QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, ui(new Ui::ProgSettings)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
(void) getIconSetPath();
|
||||
ui->comboBox->setItemData(0, "");
|
||||
ui->comboBox->setCurrentIndex(0);
|
||||
for (auto i = iconset_to_qt.begin(); i != iconset_to_qt.end(); i++) {
|
||||
if (i.key() == "")
|
||||
continue;
|
||||
QFile iconfile(i.value() + "/iconinfo.txt");
|
||||
iconfile.open(QFile::ReadOnly);
|
||||
QString friendlyName;
|
||||
QString iconsetinfo(iconfile.readAll());
|
||||
iconfile.close();
|
||||
if (iconsetinfo.isEmpty())
|
||||
friendlyName = i.key();
|
||||
else
|
||||
friendlyName = iconsetinfo.split('\n')[0];
|
||||
ui->comboBox->addItem(friendlyName, i.key());
|
||||
if (strcmp(icon_set, i.key().toUtf8().data()) == 0) {
|
||||
ui->comboBox->setCurrentIndex(ui->comboBox->findData(i.key()));
|
||||
}
|
||||
}
|
||||
ui->comboBox->setItemData(0, '(' + tr("Default") + ')', Qt::DisplayRole);
|
||||
|
||||
ui->comboBoxLanguage->setItemData(0, 0xFFFF);
|
||||
for (auto i = lcid_langcode.begin(); i != lcid_langcode.end(); i++) {
|
||||
if (i.key() == 0xFFFF)
|
||||
@@ -129,7 +74,6 @@ ProgSettings::ProgSettings(QWidget *parent)
|
||||
void
|
||||
ProgSettings::accept()
|
||||
{
|
||||
strcpy(icon_set, ui->comboBox->currentData().toString().toUtf8().data());
|
||||
lang_id = ui->comboBoxLanguage->currentData().toUInt();
|
||||
open_dir_usr_path = ui->openDirUsrPath->isChecked() ? 1 : 0;
|
||||
confirm_exit = ui->checkBoxConfirmExit->isChecked() ? 1 : 0;
|
||||
@@ -161,12 +105,6 @@ ProgSettings::~ProgSettings()
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void
|
||||
ProgSettings::on_pushButton_released()
|
||||
{
|
||||
ui->comboBox->setCurrentIndex(0);
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
/* Return the standard font name on Windows, which is overridden per-language
|
||||
to prevent CJK fonts with embedded bitmaps being chosen as a fallback. */
|
||||
|
||||
@@ -14,8 +14,6 @@ class ProgSettings : public QDialog {
|
||||
public:
|
||||
explicit ProgSettings(QWidget *parent = nullptr);
|
||||
~ProgSettings();
|
||||
static QString getIconSetPath();
|
||||
static QIcon loadIcon(QString file);
|
||||
#ifdef Q_OS_WINDOWS
|
||||
static QString getFontName(uint32_t lcid);
|
||||
#endif
|
||||
@@ -56,7 +54,6 @@ public:
|
||||
protected slots:
|
||||
void accept() override;
|
||||
private slots:
|
||||
void on_pushButton_released();
|
||||
void on_pushButtonLanguage_released();
|
||||
|
||||
void on_horizontalSlider_valueChanged(int value);
|
||||
|
||||
@@ -29,21 +29,14 @@
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SizeConstraint::SetFixedSize</enum>
|
||||
</property>
|
||||
<item row="5" column="1">
|
||||
<item row="2" column="1">
|
||||
<widget class="QPushButton" name="pushButtonLanguage">
|
||||
<property name="text">
|
||||
<string>Default</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Icon set:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<item row="5" column="0">
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
@@ -56,7 +49,7 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QComboBox" name="comboBoxLanguage">
|
||||
<property name="maxVisibleItems">
|
||||
<number>30</number>
|
||||
@@ -68,7 +61,7 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0">
|
||||
<item row="6" column="0">
|
||||
<widget class="QCheckBox" name="openDirUsrPath">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>When selecting media images (CD-ROM, floppy, etc.) the open dialog will start in the same directory as the 86Box configuration file. This setting will likely only make a difference on macOS.</p></body></html></string>
|
||||
@@ -78,14 +71,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QPushButton" name="pushButton">
|
||||
<property name="text">
|
||||
<string>Default</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<item row="2" column="0">
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
@@ -98,14 +84,14 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<item row="5" column="1">
|
||||
<widget class="QPushButton" name="pushButton_2">
|
||||
<property name="text">
|
||||
<string>Default</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="3">
|
||||
<item row="4" column="0" colspan="3">
|
||||
<widget class="QSlider" name="horizontalSlider">
|
||||
<property name="minimum">
|
||||
<number>10</number>
|
||||
@@ -127,21 +113,21 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Language:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="0">
|
||||
<item row="8" column="0">
|
||||
<widget class="QCheckBox" name="checkBoxConfirmSave">
|
||||
<property name="text">
|
||||
<string>Ask for confirmation before saving settings</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="16" column="0" colspan="2">
|
||||
<item row="13" column="0" colspan="2">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
@@ -151,63 +137,35 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
<item row="7" column="0">
|
||||
<widget class="QCheckBox" name="checkBoxMultimediaKeys">
|
||||
<property name="text">
|
||||
<string>Inhibit multimedia keys on Windows</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QComboBox" name="comboBox">
|
||||
<property name="editable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="maxVisibleItems">
|
||||
<number>30</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>(Default)</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Mouse sensitivity:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="13" column="0">
|
||||
<item row="10" column="0">
|
||||
<widget class="QCheckBox" name="checkBoxConfirmHardReset">
|
||||
<property name="text">
|
||||
<string>Ask for confirmation before hard resetting</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="0">
|
||||
<item row="9" column="0">
|
||||
<widget class="QCheckBox" name="checkBoxConfirmExit">
|
||||
<property name="text">
|
||||
<string>Ask for confirmation before quitting</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="14" column="0">
|
||||
<item row="11" column="0">
|
||||
<widget class="QCheckBox" name="checkBoxFullscreenFirst">
|
||||
<property name="text">
|
||||
<string>Display hotkey message when entering full-screen mode</string>
|
||||
|
||||
@@ -59,6 +59,9 @@ RendererCommon::onResize(int width, int height)
|
||||
bool main_max = main_window->isMaximized();
|
||||
bool main_is_max = (main_is_ancestor && main_max == false);
|
||||
|
||||
width = round(pixelRatio * width);
|
||||
height = round(pixelRatio * height);
|
||||
|
||||
if (is_fs && (video_fullscreen_scale_maximized ? (parent_max && main_is_max) : 1))
|
||||
destination.setRect(0, 0, width, height);
|
||||
else {
|
||||
|
||||
@@ -49,5 +49,7 @@ protected:
|
||||
QRect destination;
|
||||
QWidget *parentWidget { nullptr };
|
||||
|
||||
double pixelRatio = 1.0;
|
||||
|
||||
std::vector<std::atomic_flag> buf_usage;
|
||||
};
|
||||
|
||||
@@ -95,7 +95,7 @@ SettingsModel::data(const QModelIndex &index, int role) const
|
||||
case Qt::DisplayRole:
|
||||
return tr(pages.at(index.row()).toUtf8().data());
|
||||
case Qt::DecorationRole:
|
||||
return QIcon(QString("%1/%2.ico").arg(ProgSettings::getIconSetPath(), page_icons[index.row()]));
|
||||
return QIcon(QString(":/settings/qt/icons/%1.ico").arg(page_icons[index.row()]));
|
||||
case Qt::SizeHintRole:
|
||||
return QSize(-1, fontHeight * 2);
|
||||
default:
|
||||
|
||||
@@ -46,11 +46,11 @@ setFloppyType(QAbstractItemModel *model, const QModelIndex &idx, int type)
|
||||
{
|
||||
QIcon icon;
|
||||
if (type == 0)
|
||||
icon = ProgSettings::loadIcon("/floppy_disabled.ico");
|
||||
icon = QIcon(":/settings/qt/icons/floppy_disabled.ico");
|
||||
else if (type >= 1 && type <= 6)
|
||||
icon = ProgSettings::loadIcon("/floppy_525.ico");
|
||||
icon = QIcon(":/settings/qt/icons/floppy_525.ico");
|
||||
else
|
||||
icon = ProgSettings::loadIcon("/floppy_35.ico");
|
||||
icon = QIcon(":/settings/qt/icons/floppy_35.ico");
|
||||
|
||||
model->setData(idx, QObject::tr(fdd_getname(type)));
|
||||
model->setData(idx, type, Qt::UserRole);
|
||||
@@ -64,12 +64,12 @@ setCDROMBus(QAbstractItemModel *model, const QModelIndex &idx, uint8_t bus, uint
|
||||
|
||||
switch (bus) {
|
||||
case CDROM_BUS_DISABLED:
|
||||
icon = ProgSettings::loadIcon("/cdrom_disabled.ico");
|
||||
icon = QIcon(":/settings/qt/icons/cdrom_disabled.ico");
|
||||
break;
|
||||
case CDROM_BUS_ATAPI:
|
||||
case CDROM_BUS_SCSI:
|
||||
case CDROM_BUS_MITSUMI:
|
||||
icon = ProgSettings::loadIcon("/cdrom.ico");
|
||||
icon = QIcon(":/settings/qt/icons/cdrom.ico");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ addRow(QAbstractItemModel *model, hard_disk_t *hd)
|
||||
|
||||
QString busName = Harddrives::BusChannelName(hd->bus_type, hd->channel);
|
||||
model->setData(model->index(row, ColumnBus), busName);
|
||||
model->setData(model->index(row, ColumnBus), ProgSettings::loadIcon("/hard_disk.ico"), Qt::DecorationRole);
|
||||
model->setData(model->index(row, ColumnBus), QIcon(":/settings/qt/icons/hard_disk.ico"), Qt::DecorationRole);
|
||||
model->setData(model->index(row, ColumnBus), hd->bus_type, DataBus);
|
||||
model->setData(model->index(row, ColumnBus), hd->bus_type, DataBusPrevious);
|
||||
model->setData(model->index(row, ColumnBus), hd->channel, DataBusChannel);
|
||||
|
||||
@@ -88,20 +88,31 @@ SettingsMachine::SettingsMachine(QWidget *parent)
|
||||
ui->comboBoxPitMode->setCurrentIndex(-1);
|
||||
ui->comboBoxPitMode->setCurrentIndex(pit_mode + 1);
|
||||
|
||||
int selectedMachineType = 0;
|
||||
auto *machineTypesModel = ui->comboBoxMachineType->model();
|
||||
for (int i = 1; i < MACHINE_TYPE_MAX; ++i) {
|
||||
int j = 0;
|
||||
while (machine_get_internal_name_ex(j) != nullptr) {
|
||||
if (machine_available(j) && (machine_get_type(j) == i)) {
|
||||
int selectedMachineType = 0;
|
||||
auto * machineTypesModel = ui->comboBoxMachineType->model();
|
||||
int i = -1;
|
||||
int j = 0;
|
||||
int cur_j = 0;
|
||||
const void *miname;
|
||||
do {
|
||||
miname = machine_get_internal_name_ex(j);
|
||||
|
||||
if ((miname == nullptr) || (machine_get_type(j) != i)) {
|
||||
if ((i != -1) && (cur_j != 0)) {
|
||||
int row = Models::AddEntry(machineTypesModel, machine_types[i].name, machine_types[i].id);
|
||||
if (machine_types[i].id == machine_get_type(machine))
|
||||
selectedMachineType = row;
|
||||
break;
|
||||
}
|
||||
j++;
|
||||
|
||||
i = machine_get_type(j);
|
||||
cur_j = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (machine_available(j))
|
||||
cur_j++;
|
||||
|
||||
j++;
|
||||
} while (miname != nullptr);
|
||||
|
||||
ui->comboBoxMachineType->setCurrentIndex(-1);
|
||||
ui->comboBoxMachineType->setCurrentIndex(selectedMachineType);
|
||||
|
||||
@@ -137,54 +137,63 @@ SettingsNetwork::onCurrentMachineChanged(int machineId)
|
||||
{
|
||||
this->machineId = machineId;
|
||||
|
||||
int c = 0;
|
||||
int selectedRow = 0;
|
||||
int c = 0;
|
||||
int selectedRow = 0;
|
||||
|
||||
for (int i = 0; i < NET_CARD_MAX; ++i) {
|
||||
auto *cbox = findChild<QComboBox *>(QString("comboBoxNIC%1").arg(i + 1));
|
||||
auto *model = cbox->model();
|
||||
auto removeRows = model->rowCount();
|
||||
c = 0;
|
||||
selectedRow = 0;
|
||||
// Network Card
|
||||
QComboBox * cbox_[NET_CARD_MAX] = { 0 };
|
||||
QAbstractItemModel *models[NET_CARD_MAX] = { 0 };
|
||||
int removeRows_[NET_CARD_MAX] = { 0 };
|
||||
int selectedRows[NET_CARD_MAX] = { 0 };
|
||||
int m_has_net = machine_has_flags(machineId, MACHINE_NIC);
|
||||
|
||||
while (true) {
|
||||
/* Skip "internal" if machine doesn't have it or this is not the primary card. */
|
||||
if ((c == 1) && ((i > 0) || (machine_has_flags(machineId, MACHINE_NIC) == 0))) {
|
||||
c++;
|
||||
continue;
|
||||
}
|
||||
for (uint8_t i = 0; i < NET_CARD_MAX; ++i) {
|
||||
cbox_[i] = findChild<QComboBox *>(QString("comboBoxNIC%1").arg(i + 1));
|
||||
models[i] = cbox_[i]->model();
|
||||
removeRows_[i] = models[i]->rowCount();
|
||||
}
|
||||
|
||||
auto name = DeviceConfig::DeviceName(network_card_getdevice(c), network_card_get_internal_name(c), 1);
|
||||
if (name.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
c = 0;
|
||||
while (true) {
|
||||
const QString name = DeviceConfig::DeviceName(network_card_getdevice(c),
|
||||
network_card_get_internal_name(c), 1);
|
||||
|
||||
if (network_card_available(c) && device_is_valid(network_card_getdevice(c), machineId)) {
|
||||
int row = Models::AddEntry(model, name, c);
|
||||
if (c == net_cards_conf[i].device_num) {
|
||||
selectedRow = row - removeRows;
|
||||
if (name.isEmpty())
|
||||
break;
|
||||
|
||||
if (network_card_available(c)) {
|
||||
if (device_is_valid(network_card_getdevice(c), machineId)) {
|
||||
for (uint8_t i = 0; i < NET_CARD_MAX; ++i) {
|
||||
if ((c != 1) || ((i == 0) && m_has_net)) {
|
||||
int row = Models::AddEntry(models[i], name, c);
|
||||
|
||||
if (c == net_cards_conf[i].device_num)
|
||||
selectedRows[i] = row - removeRows_[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
c++;
|
||||
}
|
||||
|
||||
model->removeRows(0, removeRows);
|
||||
cbox->setEnabled(model->rowCount() > 0);
|
||||
cbox->setCurrentIndex(-1);
|
||||
cbox->setCurrentIndex(selectedRow);
|
||||
c++;
|
||||
}
|
||||
|
||||
cbox = findChild<QComboBox *>(QString("comboBoxNet%1").arg(i + 1));
|
||||
model = cbox->model();
|
||||
removeRows = model->rowCount();
|
||||
for (uint8_t i = 0; i < NET_CARD_MAX; ++i) {
|
||||
models[i]->removeRows(0, removeRows_[i]);
|
||||
cbox_[i]->setEnabled(models[i]->rowCount() > 1);
|
||||
cbox_[i]->setCurrentIndex(-1);
|
||||
cbox_[i]->setCurrentIndex(selectedRows[i]);
|
||||
|
||||
auto cbox = findChild<QComboBox *>(QString("comboBoxNet%1").arg(i + 1));
|
||||
auto model = cbox->model();
|
||||
auto removeRows = model->rowCount();
|
||||
Models::AddEntry(model, tr("Null Driver"), NET_TYPE_NONE);
|
||||
Models::AddEntry(model, "SLiRP", NET_TYPE_SLIRP);
|
||||
|
||||
if (network_ndev > 1) {
|
||||
if (network_ndev > 1)
|
||||
Models::AddEntry(model, "PCap", NET_TYPE_PCAP);
|
||||
}
|
||||
if (network_devmap.has_vde) {
|
||||
|
||||
if (network_devmap.has_vde)
|
||||
Models::AddEntry(model, "VDE", NET_TYPE_VDE);
|
||||
}
|
||||
|
||||
model->removeRows(0, removeRows);
|
||||
cbox->setCurrentIndex(cbox->findData(net_cards_conf[i].net_type));
|
||||
@@ -205,6 +214,7 @@ SettingsNetwork::onCurrentMachineChanged(int machineId)
|
||||
model->removeRows(0, removeRows);
|
||||
cbox->setCurrentIndex(selectedRow);
|
||||
}
|
||||
|
||||
if (net_cards_conf[i].net_type == NET_TYPE_VDE) {
|
||||
QString currentVdeSocket = net_cards_conf[i].host_dev_name;
|
||||
auto editline = findChild<QLineEdit *>(QString("socketVDENIC%1").arg(i+1));
|
||||
|
||||
@@ -83,31 +83,45 @@ SettingsOtherPeripherals::onCurrentMachineChanged(int machineId)
|
||||
ui->comboBoxRTC->setCurrentIndex(selectedRow);
|
||||
ui->pushButtonConfigureRTC->setEnabled((isartc_type != 0) && isartc_has_config(isartc_type) && machineHasIsa);
|
||||
|
||||
for (int c = 0; c < ISAMEM_MAX; c++) {
|
||||
auto *cbox = findChild<QComboBox *>(QString("comboBoxCard%1").arg(c + 1));
|
||||
model = cbox->model();
|
||||
d = 0;
|
||||
selectedRow = 0;
|
||||
while (true) {
|
||||
QString name = DeviceConfig::DeviceName(isamem_get_device(d), isamem_get_internal_name(d), 0);
|
||||
if (name.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
// ISA Memory Expansion Card
|
||||
QComboBox * cbox[ISAMEM_MAX] = { 0 };
|
||||
QAbstractItemModel *models[ISAMEM_MAX] = { 0 };
|
||||
int removeRows_[ISAMEM_MAX] = { 0 };
|
||||
int selectedRows[ISAMEM_MAX] = { 0 };
|
||||
|
||||
if (!device_is_valid(isamem_get_device(d), machineId)) {
|
||||
break;
|
||||
}
|
||||
for (uint8_t c = 0; c < ISAMEM_MAX; ++c) {
|
||||
cbox[c] = findChild<QComboBox *>(QString("comboBoxCard%1").arg(c + 1));
|
||||
models[c] = cbox[c]->model();
|
||||
removeRows_[c] = models[c]->rowCount();
|
||||
}
|
||||
|
||||
int row = Models::AddEntry(model, name, d);
|
||||
if (d == isamem_type[c]) {
|
||||
selectedRow = row;
|
||||
d = 0;
|
||||
while (true) {
|
||||
const QString name = DeviceConfig::DeviceName(isamem_get_device(d),
|
||||
isamem_get_internal_name(d), 0);
|
||||
|
||||
if (name.isEmpty())
|
||||
break;
|
||||
|
||||
if (device_is_valid(isamem_get_device(d), machineId)) {
|
||||
for (uint8_t c = 0; c < ISAMEM_MAX; ++c) {
|
||||
int row = Models::AddEntry(models[c], name, d);
|
||||
|
||||
if (d == isamem_type[c])
|
||||
selectedRows[c] = row - removeRows_[c];
|
||||
}
|
||||
++d;
|
||||
}
|
||||
cbox->setCurrentIndex(-1);
|
||||
cbox->setCurrentIndex(selectedRow);
|
||||
cbox->setEnabled(machineHasIsa);
|
||||
findChild<QPushButton *>(QString("pushButtonConfigureCard%1").arg(c + 1))->setEnabled((isamem_type[c] != 0) && isamem_has_config(isamem_type[c]) && machineHasIsa);
|
||||
|
||||
d++;
|
||||
}
|
||||
|
||||
for (uint8_t c = 0; c < ISAMEM_MAX; ++c) {
|
||||
models[c]->removeRows(0, removeRows_[c]);
|
||||
cbox[c]->setEnabled(models[c]->rowCount() > 1);
|
||||
cbox[c]->setCurrentIndex(-1);
|
||||
cbox[c]->setCurrentIndex(selectedRows[c]);
|
||||
findChild<QPushButton *>(QString("pushButtonConfigureCard%1").arg(c + 1))->setEnabled((isamem_type[c] != 0) &&
|
||||
isamem_has_config(isamem_type[c]) && machineHasIsa);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,11 +46,11 @@ setMOBus(QAbstractItemModel *model, const QModelIndex &idx, uint8_t bus, uint8_t
|
||||
QIcon icon;
|
||||
switch (bus) {
|
||||
case MO_BUS_DISABLED:
|
||||
icon = ProgSettings::loadIcon("/mo_disabled.ico");
|
||||
icon = QIcon(":/settings/qt/icons/mo_disabled.ico");
|
||||
break;
|
||||
case MO_BUS_ATAPI:
|
||||
case MO_BUS_SCSI:
|
||||
icon = ProgSettings::loadIcon("/mo.ico");
|
||||
icon = QIcon(":/settings/qt/icons/mo.ico");
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -81,11 +81,11 @@ setZIPBus(QAbstractItemModel *model, const QModelIndex &idx, uint8_t bus, uint8_
|
||||
QIcon icon;
|
||||
switch (bus) {
|
||||
case ZIP_BUS_DISABLED:
|
||||
icon = ProgSettings::loadIcon("/zip_disabled.ico");
|
||||
icon = QIcon(":/settings/qt/icons/zip_disabled.ico");
|
||||
break;
|
||||
case ZIP_BUS_ATAPI:
|
||||
case ZIP_BUS_SCSI:
|
||||
icon = ProgSettings::loadIcon("/zip.ico");
|
||||
icon = QIcon(":/settings/qt/icons/zip.ico");
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -73,34 +73,49 @@ SettingsPorts::onCurrentMachineChanged(int machineId)
|
||||
{
|
||||
this->machineId = machineId;
|
||||
|
||||
for (int i = 0; i < PARALLEL_MAX; i++) {
|
||||
auto *cbox = findChild<QComboBox *>(QString("comboBoxLpt%1").arg(i + 1));
|
||||
auto *model = cbox->model();
|
||||
const auto removeRows = model->rowCount();
|
||||
int c = 0;
|
||||
int selectedRow = 0;
|
||||
while (true) {
|
||||
const char *lptName = lpt_device_get_name(c);
|
||||
if (lptName == nullptr) {
|
||||
break;
|
||||
}
|
||||
int c = 0;
|
||||
|
||||
int row = Models::AddEntry(model, tr(lptName), c);
|
||||
if (c == lpt_ports[i].device) {
|
||||
selectedRow = row - removeRows;
|
||||
}
|
||||
c++;
|
||||
// LPT Device
|
||||
QComboBox * cbox[PARALLEL_MAX] = { 0 };
|
||||
QAbstractItemModel *models[PARALLEL_MAX] = { 0 };
|
||||
int removeRows_[PARALLEL_MAX] = { 0 };
|
||||
int selectedRows[PARALLEL_MAX] = { 0 };
|
||||
|
||||
for (uint8_t i = 0; i < PARALLEL_MAX; ++i) {
|
||||
cbox[i] = findChild<QComboBox *>(QString("comboBoxLpt%1").arg(i + 1));
|
||||
models[i] = cbox[i]->model();
|
||||
removeRows_[i] = models[i]->rowCount();
|
||||
}
|
||||
|
||||
while (true) {
|
||||
const char *lptName = lpt_device_get_name(c);
|
||||
|
||||
if (lptName == nullptr)
|
||||
break;
|
||||
|
||||
const QString name = tr(lptName);
|
||||
|
||||
for (uint8_t i = 0; i < PARALLEL_MAX; ++i) {
|
||||
int row = Models::AddEntry(models[i], name, c);
|
||||
|
||||
if (c == lpt_ports[i].device)
|
||||
selectedRows[i] = row - removeRows_[i];
|
||||
}
|
||||
model->removeRows(0, removeRows);
|
||||
cbox->setEnabled(model->rowCount() > 0);
|
||||
cbox->setCurrentIndex(-1);
|
||||
cbox->setCurrentIndex(selectedRow);
|
||||
|
||||
c++;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < PARALLEL_MAX; ++i) {
|
||||
models[i]->removeRows(0, removeRows_[i]);
|
||||
cbox[i]->setEnabled(models[i]->rowCount() > 1);
|
||||
cbox[i]->setCurrentIndex(-1);
|
||||
cbox[i]->setCurrentIndex(selectedRows[i]);
|
||||
|
||||
auto *checkBox = findChild<QCheckBox *>(QString("checkBoxParallel%1").arg(i + 1));
|
||||
if (checkBox != NULL)
|
||||
checkBox->setChecked(lpt_ports[i].enabled > 0);
|
||||
if (cbox != NULL)
|
||||
cbox->setEnabled(lpt_ports[i].enabled > 0);
|
||||
if (cbox[i] != NULL)
|
||||
cbox[i]->setEnabled(lpt_ports[i].enabled > 0);
|
||||
}
|
||||
|
||||
for (int i = 0; i < SERIAL_MAX; i++) {
|
||||
|
||||
@@ -73,46 +73,51 @@ SettingsSound::onCurrentMachineChanged(const int machineId)
|
||||
{
|
||||
this->machineId = machineId;
|
||||
|
||||
int c;
|
||||
int selectedRow;
|
||||
int c;
|
||||
int selectedRow;
|
||||
|
||||
// Sound Card
|
||||
QComboBox * cbox[SOUND_CARD_MAX] = { 0 };
|
||||
QAbstractItemModel *models[SOUND_CARD_MAX] = { 0 };
|
||||
int removeRows_[SOUND_CARD_MAX] = { 0 };
|
||||
int selectedRows[SOUND_CARD_MAX] = { 0 };
|
||||
int m_has_snd = machine_has_flags(machineId, MACHINE_SOUND);
|
||||
|
||||
for (uint8_t i = 0; i < SOUND_CARD_MAX; ++i) {
|
||||
QComboBox *cbox = findChild<QComboBox *>(QString("comboBoxSoundCard%1").arg(i + 1));
|
||||
c = 0;
|
||||
auto model = cbox->model();
|
||||
auto removeRows = model->rowCount();
|
||||
selectedRow = 0;
|
||||
cbox[i] = findChild<QComboBox *>(QString("comboBoxSoundCard%1").arg(i + 1));
|
||||
models[i] = cbox[i]->model();
|
||||
removeRows_[i] = models[i]->rowCount();
|
||||
}
|
||||
|
||||
while (true) {
|
||||
/* Skip "internal" if machine doesn't have it or this is not the primary card. */
|
||||
if ((c == 1) && ((i > 0) || (machine_has_flags(machineId, MACHINE_SOUND) == 0))) {
|
||||
c++;
|
||||
continue;
|
||||
}
|
||||
c = 0;
|
||||
while (true) {
|
||||
const QString name = DeviceConfig::DeviceName(sound_card_getdevice(c),
|
||||
sound_card_get_internal_name(c), 1);
|
||||
|
||||
const QString name = DeviceConfig::DeviceName(sound_card_getdevice(c), sound_card_get_internal_name(c), 1);
|
||||
if (name.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
if (name.isEmpty())
|
||||
break;
|
||||
|
||||
if (sound_card_available(c)) {
|
||||
const device_t *sound_dev = sound_card_getdevice(c);
|
||||
if (device_is_valid(sound_dev, machineId)) {
|
||||
int row = Models::AddEntry(model, name, c);
|
||||
if (c == sound_card_current[i]) {
|
||||
selectedRow = row - removeRows;
|
||||
if (sound_card_available(c)) {
|
||||
if (device_is_valid(sound_card_getdevice(c), machineId)) {
|
||||
for (uint8_t i = 0; i < SOUND_CARD_MAX; ++i) {
|
||||
if ((c != 1) || ((i == 0) && m_has_snd)) {
|
||||
int row = Models::AddEntry(models[i], name, c);
|
||||
|
||||
if (c == sound_card_current[i])
|
||||
selectedRows[i] = row - removeRows_[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c++;
|
||||
}
|
||||
|
||||
model->removeRows(0, removeRows);
|
||||
cbox->setEnabled(model->rowCount() > 0);
|
||||
cbox->setCurrentIndex(-1);
|
||||
cbox->setCurrentIndex(selectedRow);
|
||||
c++;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < SOUND_CARD_MAX; ++i) {
|
||||
models[i]->removeRows(0, removeRows_[i]);
|
||||
cbox[i]->setEnabled(models[i]->rowCount() > 1);
|
||||
cbox[i]->setCurrentIndex(-1);
|
||||
cbox[i]->setCurrentIndex(selectedRows[i]);
|
||||
}
|
||||
|
||||
// Midi Out
|
||||
|
||||
@@ -171,35 +171,48 @@ SettingsStorageControllers::onCurrentMachineChanged(int machineId)
|
||||
ui->comboBoxCDInterface->setCurrentIndex(-1);
|
||||
ui->comboBoxCDInterface->setCurrentIndex(selectedRow);
|
||||
|
||||
// SCSI Card
|
||||
QComboBox * cbox[SCSI_CARD_MAX] = { 0 };
|
||||
QAbstractItemModel *models[SCSI_CARD_MAX] = { 0 };
|
||||
int removeRows_[SCSI_CARD_MAX] = { 0 };
|
||||
int selectedRows[SCSI_CARD_MAX] = { 0 };
|
||||
int m_has_scsi = machine_has_flags(machineId, MACHINE_SCSI);
|
||||
|
||||
for (uint8_t i = 0; i < SCSI_CARD_MAX; ++i) {
|
||||
QComboBox *cbox = findChild<QComboBox *>(QString("comboBoxSCSI%1").arg(i + 1));
|
||||
c = 0;
|
||||
model = cbox->model();
|
||||
removeRows = model->rowCount();
|
||||
selectedRow = 0;
|
||||
cbox[i] = findChild<QComboBox *>(QString("comboBoxSCSI%1").arg(i + 1));
|
||||
models[i] = cbox[i]->model();
|
||||
removeRows_[i] = models[i]->rowCount();
|
||||
}
|
||||
|
||||
while (true) {
|
||||
QString name = DeviceConfig::DeviceName(scsi_card_getdevice(c), scsi_card_get_internal_name(c), 1);
|
||||
if (name.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
c = 0;
|
||||
while (true) {
|
||||
const QString name = DeviceConfig::DeviceName(scsi_card_getdevice(c),
|
||||
scsi_card_get_internal_name(c), 1);
|
||||
|
||||
if (scsi_card_available(c)) {
|
||||
const device_t *scsi_dev = scsi_card_getdevice(c);
|
||||
if (device_is_valid(scsi_dev, machineId)) {
|
||||
int row = Models::AddEntry(model, name, c);
|
||||
if (c == scsi_card_current[i]) {
|
||||
selectedRow = row - removeRows;
|
||||
if (name.isEmpty())
|
||||
break;
|
||||
|
||||
if (scsi_card_available(c)) {
|
||||
if (device_is_valid(scsi_card_getdevice(c), machineId)) {
|
||||
for (uint8_t i = 0; i < SCSI_CARD_MAX; ++i) {
|
||||
if ((c != 1) || ((i == 0) && m_has_scsi)) {
|
||||
int row = Models::AddEntry(models[i], name, c);
|
||||
|
||||
if (c == scsi_card_current[i])
|
||||
selectedRows[i] = row - removeRows_[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
c++;
|
||||
}
|
||||
|
||||
model->removeRows(0, removeRows);
|
||||
cbox->setEnabled(model->rowCount() > 0);
|
||||
cbox->setCurrentIndex(-1);
|
||||
cbox->setCurrentIndex(selectedRow);
|
||||
c++;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < SCSI_CARD_MAX; ++i) {
|
||||
models[i]->removeRows(0, removeRows_[i]);
|
||||
cbox[i]->setEnabled(models[i]->rowCount() > 1);
|
||||
cbox[i]->setCurrentIndex(-1);
|
||||
cbox[i]->setCurrentIndex(selectedRows[i]);
|
||||
}
|
||||
|
||||
int is_at = IS_AT(machineId);
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QAbstractItemView>
|
||||
#include <QPixmap>
|
||||
#include <QIcon>
|
||||
#include <QStyleOption>
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
#include <dwmapi.h>
|
||||
@@ -66,3 +69,40 @@ StyleOverride::polish(QWidget *widget)
|
||||
qobject_cast<QComboBox *>(widget)->view()->setMinimumWidth(widget->minimumSizeHint().width());
|
||||
}
|
||||
}
|
||||
|
||||
QPixmap
|
||||
StyleOverride::generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap, const QStyleOption *option) const
|
||||
{
|
||||
if (iconMode != QIcon::Disabled) {
|
||||
return QProxyStyle::generatedIconPixmap(iconMode, pixmap, option);
|
||||
}
|
||||
|
||||
auto image = pixmap.toImage();
|
||||
|
||||
for (int y = 0; y < image.height(); y++) {
|
||||
for (int x = 0; x < image.width(); x++) {
|
||||
// checkerboard transparency
|
||||
if (((x ^ y) & 1) == 0) {
|
||||
image.setPixelColor(x, y, Qt::transparent);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto color = image.pixelColor(x, y);
|
||||
|
||||
// convert to grayscale using the NTSC formula
|
||||
auto avg = 0.0;
|
||||
avg += color.blueF() * 0.114;
|
||||
avg += color.greenF() * 0.587;
|
||||
avg += color.redF() * 0.299;
|
||||
|
||||
color.setRedF(avg);
|
||||
color.setGreenF(avg);
|
||||
color.setBlueF(avg);
|
||||
|
||||
image.setPixelColor(x, y, color);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return QPixmap::fromImage(image);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
#include <QProxyStyle>
|
||||
#include <QWidget>
|
||||
#include <QLayout>
|
||||
#include <QPixmap>
|
||||
#include <QIcon>
|
||||
#include <QStyleOption>
|
||||
|
||||
class StyleOverride : public QProxyStyle {
|
||||
public:
|
||||
@@ -14,6 +17,7 @@ public:
|
||||
QStyleHintReturn *returnData = nullptr) const override;
|
||||
|
||||
void polish(QWidget *widget) override;
|
||||
QPixmap generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap, const QStyleOption *option) const override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -970,17 +970,11 @@ VulkanRenderer2::startNextFrame()
|
||||
m_devFuncs->vkCmdBindVertexBuffers(cb, 0, 1, &m_buf, &vbOffset);
|
||||
|
||||
VkViewport viewport;
|
||||
if (dpi_scale) {
|
||||
viewport.x = destination.x() * m_window->devicePixelRatio();
|
||||
viewport.y = destination.y() * m_window->devicePixelRatio();
|
||||
viewport.width = destination.width() * m_window->devicePixelRatio();
|
||||
viewport.height = destination.height() * m_window->devicePixelRatio();
|
||||
} else {
|
||||
viewport.x = destination.x();
|
||||
viewport.y = destination.y();
|
||||
viewport.width = destination.width();
|
||||
viewport.height = destination.height();
|
||||
}
|
||||
viewport.x = destination.x();
|
||||
viewport.y = destination.y();
|
||||
viewport.width = destination.width();
|
||||
viewport.height = destination.height();
|
||||
|
||||
viewport.minDepth = 0;
|
||||
viewport.maxDepth = 1;
|
||||
m_devFuncs->vkCmdSetViewport(cb, 0, 1, &viewport);
|
||||
|
||||
@@ -846,6 +846,7 @@ VulkanWindowRenderer::createRenderer()
|
||||
void
|
||||
VulkanWindowRenderer::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
this->pixelRatio = devicePixelRatio();
|
||||
onResize(width(), height());
|
||||
|
||||
QVulkanWindow::resizeEvent(event);
|
||||
@@ -868,8 +869,10 @@ VulkanWindowRenderer::onBlit(int buf_idx, int x, int y, int w, int h)
|
||||
if (isExposed())
|
||||
requestUpdate();
|
||||
buf_usage[0].clear();
|
||||
if (origSource != source)
|
||||
if (origSource != source) {
|
||||
this->pixelRatio = devicePixelRatio();
|
||||
onResize(this->width(), this->height());
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t
|
||||
|
||||
@@ -1,16 +1,9 @@
|
||||
<RCC>
|
||||
<qresource prefix="/settings">
|
||||
<file>qt/icons/cartridge.ico</file>
|
||||
<file>qt/icons/cartridge_empty.ico</file>
|
||||
<file>qt/icons/cassette.ico</file>
|
||||
<file>qt/icons/cassette_active.ico</file>
|
||||
<file>qt/icons/cassette_empty.ico</file>
|
||||
<file>qt/icons/cassette_empty_active.ico</file>
|
||||
<file>qt/icons/cdrom.ico</file>
|
||||
<file>qt/icons/cdrom_active.ico</file>
|
||||
<file>qt/icons/cdrom_disabled.ico</file>
|
||||
<file>qt/icons/cdrom_empty.ico</file>
|
||||
<file>qt/icons/cdrom_empty_active.ico</file>
|
||||
<file>qt/icons/cdrom_mute.ico</file>
|
||||
<file>qt/icons/cdrom_unmute.ico</file>
|
||||
<file>qt/icons/cdrom_image.ico</file>
|
||||
@@ -18,38 +11,24 @@
|
||||
<file>qt/icons/cdrom_host.ico</file>
|
||||
<file>qt/icons/display.ico</file>
|
||||
<file>qt/icons/floppy_35.ico</file>
|
||||
<file>qt/icons/floppy_35_active.ico</file>
|
||||
<file>qt/icons/floppy_35_empty.ico</file>
|
||||
<file>qt/icons/floppy_35_empty_active.ico</file>
|
||||
<file>qt/icons/floppy_525.ico</file>
|
||||
<file>qt/icons/floppy_525_active.ico</file>
|
||||
<file>qt/icons/floppy_525_empty.ico</file>
|
||||
<file>qt/icons/floppy_525_empty_active.ico</file>
|
||||
<file>qt/icons/floppy_and_cdrom_drives.ico</file>
|
||||
<file>qt/icons/floppy_disabled.ico</file>
|
||||
<file>qt/icons/hard_disk.ico</file>
|
||||
<file>qt/icons/hard_disk_active.ico</file>
|
||||
<file>qt/icons/input_devices.ico</file>
|
||||
<file>qt/icons/machine.ico</file>
|
||||
<file>qt/icons/mo.ico</file>
|
||||
<file>qt/icons/mo_active.ico</file>
|
||||
<file>qt/icons/mo_disabled.ico</file>
|
||||
<file>qt/icons/mo_empty.ico</file>
|
||||
<file>qt/icons/mo_empty_active.ico</file>
|
||||
<file>qt/icons/network.ico</file>
|
||||
<file>qt/icons/network_active.ico</file>
|
||||
<file>qt/icons/network_empty.ico</file>
|
||||
<file>qt/icons/other_peripherals.ico</file>
|
||||
<file>qt/icons/other_removable_devices.ico</file>
|
||||
<file>qt/icons/ports.ico</file>
|
||||
<file>qt/icons/sound.ico</file>
|
||||
<file>qt/icons/sound_mute.ico</file>
|
||||
<file>qt/icons/storage_controllers.ico</file>
|
||||
<file>qt/icons/zip.ico</file>
|
||||
<file>qt/icons/zip_active.ico</file>
|
||||
<file>qt/icons/zip_disabled.ico</file>
|
||||
<file>qt/icons/zip_empty.ico</file>
|
||||
<file>qt/icons/zip_empty_active.ico</file>
|
||||
<file>qt/icons/active.ico</file>
|
||||
<file>qt/icons/disabled.ico</file>
|
||||
<file>qt/icons/86Box-gray.ico</file>
|
||||
<file>qt/icons/86Box-green.ico</file>
|
||||
<file>qt/icons/86Box-red.ico</file>
|
||||
|
||||
@@ -95,9 +95,6 @@ ncr5380_reset(ncr_t *ncr)
|
||||
|
||||
ncr->timer(ncr->priv, 0.0);
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
scsi_device_reset(&scsi_devices[ncr->bus][i]);
|
||||
|
||||
scsi_bus->state = STATE_IDLE;
|
||||
scsi_bus->clear_req = 0;
|
||||
scsi_bus->wait_complete = 0;
|
||||
|
||||
@@ -29,11 +29,11 @@
|
||||
#define HAVE_STDARG_H
|
||||
#include <86box/86box.h>
|
||||
#include <86box/io.h>
|
||||
#include <86box/timer.h>
|
||||
#include <86box/dma.h>
|
||||
#include <86box/pic.h>
|
||||
#include <86box/mca.h>
|
||||
#include <86box/mem.h>
|
||||
#include <86box/timer.h>
|
||||
#include <86box/rom.h>
|
||||
#include <86box/device.h>
|
||||
#include <86box/nvr.h>
|
||||
@@ -41,6 +41,7 @@
|
||||
#include <86box/scsi.h>
|
||||
#include <86box/scsi_device.h>
|
||||
#include <86box/scsi_ncr5380.h>
|
||||
#include "cpu.h"
|
||||
|
||||
#define LCS6821N_ROM "roms/scsi/ncr5380/Longshine LCS-6821N - BIOS version 1.04.bin"
|
||||
#define COREL_LS2000_ROM "roms/scsi/ncr5380/Corel LS2000 - BIOS ROM - Ver 1.65.bin"
|
||||
@@ -80,6 +81,7 @@ typedef struct ncr53c400_t {
|
||||
int buffer_host_pos;
|
||||
|
||||
int busy;
|
||||
int reset;
|
||||
uint8_t pos_regs[8];
|
||||
|
||||
pc_timer_t timer;
|
||||
@@ -126,6 +128,9 @@ ncr53c400_write(uint32_t addr, uint8_t val, void *priv)
|
||||
|
||||
addr &= 0x3fff;
|
||||
|
||||
if (addr >= 0x3880)
|
||||
ncr53c400_log("%04X:%08X: memio_write(%04x)=%02x\n", CS, cpu_state.pc, addr, val);
|
||||
|
||||
if (addr >= 0x3a00)
|
||||
ncr400->ext_ram[addr - 0x3a00] = val;
|
||||
else {
|
||||
@@ -147,6 +152,8 @@ ncr53c400_write(uint32_t addr, uint8_t val, void *priv)
|
||||
if (ncr400->buffer_host_pos == MIN(128, dev->buffer_length)) {
|
||||
ncr400->status_ctrl |= STATUS_BUFFER_NOT_READY;
|
||||
ncr400->busy = 1;
|
||||
if (ncr400->type != ROM_T130B)
|
||||
timer_on_auto(&ncr400->timer, 1.0);
|
||||
}
|
||||
} else
|
||||
ncr53c400_log("No Write.\n");
|
||||
@@ -155,6 +162,19 @@ ncr53c400_write(uint32_t addr, uint8_t val, void *priv)
|
||||
case 0x3980:
|
||||
switch (addr) {
|
||||
case 0x3980: /* Control */
|
||||
/*Parity bits*/
|
||||
/*This is to avoid RTBios 8.10R BIOS problems with the hard disk and detection.*/
|
||||
/*If the parity bits are set, bit 0 of the 53c400 status port should be set as well.*/
|
||||
/*Required by RTASPI10.SYS otherwise it won't initialize.*/
|
||||
if (val & 0x80) {
|
||||
if (ncr->mode & 0x30) {
|
||||
if (!(ncr->mode & MODE_DMA)) {
|
||||
ncr->mode = 0x00;
|
||||
ncr400->reset = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ncr53c400_log("NCR 53c400 control=%02x, mode=%02x.\n", val, ncr->mode);
|
||||
if ((val & CTRL_DATA_DIR) && !(ncr400->status_ctrl & CTRL_DATA_DIR)) {
|
||||
ncr400->buffer_host_pos = MIN(128, dev->buffer_length);
|
||||
@@ -180,10 +200,7 @@ ncr53c400_write(uint32_t addr, uint8_t val, void *priv)
|
||||
}
|
||||
if ((ncr->mode & MODE_DMA) && (dev->buffer_length > 0)) {
|
||||
memset(ncr400->buffer, 0, MIN(128, dev->buffer_length));
|
||||
if (ncr400->type == ROM_T130B)
|
||||
timer_on_auto(&ncr400->timer, 10.0);
|
||||
else
|
||||
timer_on_auto(&ncr400->timer, scsi_bus->period);
|
||||
timer_on_auto(&ncr400->timer, 10.0);
|
||||
ncr53c400_log("DMA timer on=%02x, callback=%lf, scsi buflen=%d, waitdata=%d, waitcomplete=%d, clearreq=%d, p=%lf enabled=%d.\n",
|
||||
ncr->mode & MODE_MONITOR_BUSY, scsi_device_get_callback(dev), dev->buffer_length, scsi_bus->wait_data, scsi_bus->wait_complete, scsi_bus->clear_req, scsi_bus->period, timer_is_enabled(&ncr400->timer));
|
||||
} else
|
||||
@@ -241,6 +258,20 @@ ncr53c400_read(uint32_t addr, void *priv)
|
||||
|
||||
if (ncr400->buffer_host_pos == MIN(128, dev->buffer_length)) {
|
||||
ncr400->status_ctrl |= STATUS_BUFFER_NOT_READY;
|
||||
if (ncr400->type != ROM_T130B) {
|
||||
if (!ncr400->block_count_loaded) {
|
||||
scsi_bus->tx_mode = PIO_TX_BUS;
|
||||
ncr53c400_log("IO End of read transfer\n");
|
||||
ncr->isr |= STATUS_END_OF_DMA;
|
||||
if (ncr->mode & MODE_ENA_EOP_INT) {
|
||||
ncr53c400_log("NCR read irq\n");
|
||||
ncr5380_irq(ncr, 1);
|
||||
}
|
||||
} else if (!timer_is_enabled(&ncr400->timer)) {
|
||||
ncr53c400_log("Timer re-enabled.\n");
|
||||
timer_on_auto(&ncr400->timer, 1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -252,11 +283,10 @@ ncr53c400_read(uint32_t addr, void *priv)
|
||||
ncr53c400_log("NCR status ctrl read=%02x.\n", ncr400->status_ctrl & STATUS_BUFFER_NOT_READY);
|
||||
if (!ncr400->busy)
|
||||
ret |= STATUS_5380_ACCESSIBLE;
|
||||
if (ncr->mode & 0x30) { /*Parity bits*/
|
||||
if (!(ncr->mode & MODE_DMA)) { /*This is to avoid RTBios 8.10R BIOS problems with the hard disk and detection.*/
|
||||
ret |= 0x01; /*If the parity bits are set, bit 0 of the 53c400 status port should be set as well.*/
|
||||
ncr->mode = 0x00; /*Required by RTASPI10.SYS otherwise it won't initialize.*/
|
||||
}
|
||||
|
||||
if (ncr400->reset) {
|
||||
ncr400->reset = 0;
|
||||
ret |= 0x01;
|
||||
}
|
||||
ncr53c400_log("NCR 53c400 status=%02x.\n", ret);
|
||||
break;
|
||||
@@ -267,7 +297,10 @@ ncr53c400_read(uint32_t addr, void *priv)
|
||||
break;
|
||||
|
||||
case 0x3982: /* switch register read */
|
||||
ret = 0xff;
|
||||
if (ncr->irq != -1) {
|
||||
ret = 0xf8;
|
||||
ret += ncr->irq;
|
||||
}
|
||||
ncr53c400_log("Switches read=%02x.\n", ret);
|
||||
break;
|
||||
|
||||
@@ -282,7 +315,7 @@ ncr53c400_read(uint32_t addr, void *priv)
|
||||
}
|
||||
|
||||
if (addr >= 0x3880)
|
||||
ncr53c400_log("memio_read(%08x)=%02x\n", addr, ret);
|
||||
ncr53c400_log("%04X:%08X: memio_read(%04x)=%02x\n", CS, cpu_state.pc, addr, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -424,11 +457,8 @@ ncr53c400_callback(void *priv)
|
||||
uint8_t status;
|
||||
|
||||
if (scsi_bus->tx_mode != PIO_TX_BUS) {
|
||||
if (ncr400->type == ROM_T130B) {
|
||||
ncr53c400_log("PERIOD T130B DMA=%lf.\n", scsi_bus->period / 225.0);
|
||||
timer_on_auto(&ncr400->timer, scsi_bus->period / 225.0);
|
||||
} else
|
||||
timer_on_auto(&ncr400->timer, 1.0);
|
||||
ncr53c400_log("PERIOD T130B DMA=%lf.\n", scsi_bus->period / 225.0);
|
||||
timer_on_auto(&ncr400->timer, scsi_bus->period / 225.0);
|
||||
}
|
||||
|
||||
if (scsi_bus->data_wait & 1) {
|
||||
@@ -538,14 +568,17 @@ ncr53c400_callback(void *priv)
|
||||
ncr400->block_count = (ncr400->block_count - 1) & 0xff;
|
||||
ncr53c400_log("NCR 53c400 Remaining blocks to be read=%d\n", ncr400->block_count);
|
||||
if (!ncr400->block_count) {
|
||||
scsi_bus->tx_mode = PIO_TX_BUS;
|
||||
ncr400->block_count_loaded = 0;
|
||||
ncr53c400_log("IO End of read transfer\n");
|
||||
ncr->isr |= STATUS_END_OF_DMA;
|
||||
if (ncr->mode & MODE_ENA_EOP_INT) {
|
||||
ncr53c400_log("NCR read irq\n");
|
||||
ncr5380_irq(ncr, 1);
|
||||
}
|
||||
if (ncr400->type == ROM_T130B) {
|
||||
scsi_bus->tx_mode = PIO_TX_BUS;
|
||||
ncr53c400_log("IO End of read transfer\n");
|
||||
ncr->isr |= STATUS_END_OF_DMA;
|
||||
if (ncr->mode & MODE_ENA_EOP_INT) {
|
||||
ncr53c400_log("NCR read irq\n");
|
||||
ncr5380_irq(ncr, 1);
|
||||
}
|
||||
} else
|
||||
timer_on_auto(&ncr400->timer, 1.0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -732,8 +765,17 @@ ncr53c400_init(const device_t *info)
|
||||
|
||||
scsi_bus_set_speed(ncr->bus, 5000000.0);
|
||||
scsi_bus->speed = 0.2;
|
||||
scsi_bus->divider = 2.0;
|
||||
scsi_bus->multi = 1.750;
|
||||
if (ncr400->type == ROM_T130B) {
|
||||
scsi_bus->divider = 2.0;
|
||||
scsi_bus->multi = 1.750;
|
||||
} else {
|
||||
scsi_bus->divider = 1.0;
|
||||
scsi_bus->multi = 1.0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
scsi_device_reset(&scsi_devices[ncr->bus][i]);
|
||||
|
||||
return ncr400;
|
||||
}
|
||||
|
||||
|
||||
@@ -95,9 +95,10 @@ t128_write(uint32_t addr, uint8_t val, void *priv)
|
||||
t128->status, scsi_bus->period, timer_is_enabled(&t128->timer), t128->block_loaded);
|
||||
|
||||
t128->status &= ~0x04;
|
||||
timer_on_auto(&t128->timer, 10.0);
|
||||
timer_on_auto(&t128->timer, 1.0);
|
||||
}
|
||||
}
|
||||
} else
|
||||
t128_log("Write not allowed.\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,20 +137,18 @@ t128_read(uint32_t addr, void *priv)
|
||||
t128_log("T128 Transfer busy read, status=%02x, period=%lf, enabled=%d.\n",
|
||||
t128->status, scsi_bus->period, timer_is_enabled(&t128->timer));
|
||||
|
||||
t128->status &= ~0x04;
|
||||
if (!t128->block_loaded) {
|
||||
ncr->isr |= STATUS_END_OF_DMA;
|
||||
if (ncr->mode & MODE_ENA_EOP_INT) {
|
||||
t128_log("T128 read irq\n");
|
||||
ncr5380_irq(ncr, 1);
|
||||
}
|
||||
t128->status &= ~0x04;
|
||||
scsi_bus->bus_out |= BUS_CD;
|
||||
scsi_bus->tx_mode = PIO_TX_BUS;
|
||||
timer_stop(&t128->timer);
|
||||
} else if (!timer_is_enabled(&t128->timer))
|
||||
timer_on_auto(&t128->timer, 10.0);
|
||||
else
|
||||
t128->status &= ~0x04;
|
||||
timer_on_auto(&t128->timer, 1.0);
|
||||
}
|
||||
} else {
|
||||
/*According to the WinNT DDK sources, just get the status timeout bit from here.*/
|
||||
@@ -522,6 +521,10 @@ t128_init(const device_t *info)
|
||||
scsi_bus->speed = 0.2;
|
||||
scsi_bus->divider = 1.0;
|
||||
scsi_bus->multi = 1.0;
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
scsi_device_reset(&scsi_devices[ncr->bus][i]);
|
||||
|
||||
return t128;
|
||||
}
|
||||
|
||||
|
||||
@@ -180,6 +180,9 @@ 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()
|
||||
|
||||
16
src/sound/saasound/CMakeLists.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
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)
|
||||
203
src/sound/saasound/SAAAmp.cpp
Executable file
@@ -0,0 +1,203 @@
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
44
src/sound/saasound/SAAAmp.h
Executable file
@@ -0,0 +1,44 @@
|
||||
// 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
|
||||
41
src/sound/saasound/SAAConfig.h
Normal file
@@ -0,0 +1,41 @@
|
||||
// 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
|
||||
392
src/sound/saasound/SAADevice.cpp
Normal file
@@ -0,0 +1,392 @@
|
||||
// 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;
|
||||
}
|
||||
69
src/sound/saasound/SAADevice.h
Normal file
@@ -0,0 +1,69 @@
|
||||
// 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
|
||||
380
src/sound/saasound/SAAEnv.cpp
Executable file
@@ -0,0 +1,380 @@
|
||||
// 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();
|
||||
}
|
||||
54
src/sound/saasound/SAAEnv.h
Executable file
@@ -0,0 +1,54 @@
|
||||
// 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
|
||||
287
src/sound/saasound/SAAFreq.cpp
Executable file
@@ -0,0 +1,287 @@
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
141
src/sound/saasound/SAAFreq.dat
Executable file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
// 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
|
||||
72
src/sound/saasound/SAAFreq.h
Executable file
@@ -0,0 +1,72 @@
|
||||
// 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
|
||||
489
src/sound/saasound/SAAImpl.cpp
Normal file
@@ -0,0 +1,489 @@
|
||||
// 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;
|
||||
static double filterout_z1_left_mixed = 0, filterout_z1_right_mixed = 0;
|
||||
|
||||
#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
|
||||
|
||||
*/
|
||||
|
||||
75
src/sound/saasound/SAAImpl.h
Executable file
@@ -0,0 +1,75 @@
|
||||
// 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;
|
||||
#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
|
||||