From b07570696f6354c4ded84c83e4c1e52d2fca0aea Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Fri, 25 Feb 2022 19:22:45 -0300 Subject: [PATCH 01/62] Initial implementation of C-Media CMI8x38 --- src/include/86box/snd_mpu401.h | 2 + src/include/86box/snd_sb.h | 7 + src/include/86box/sound.h | 5 + src/sound/CMakeLists.txt | 2 +- src/sound/snd_cmi8x38.c | 830 +++++++++++++++++++++++++++++++++ src/sound/snd_mpu401.c | 4 +- src/sound/snd_sb.c | 63 ++- src/sound/sound.c | 2 + src/win/Makefile.mingw | 2 +- 9 files changed, 905 insertions(+), 12 deletions(-) create mode 100644 src/sound/snd_cmi8x38.c diff --git a/src/include/86box/snd_mpu401.h b/src/include/86box/snd_mpu401.h index 8846ee2dd..ca7cf368f 100644 --- a/src/include/86box/snd_mpu401.h +++ b/src/include/86box/snd_mpu401.h @@ -155,6 +155,8 @@ extern const device_t mpu401_mca_device; extern uint8_t MPU401_ReadData(mpu_t *mpu); +extern void mpu401_write(uint16_t addr, uint8_t val, void *priv); +extern uint8_t mpu401_read(uint16_t addr, void *priv); extern void mpu401_setirq(mpu_t *mpu, int irq); extern void mpu401_change_addr(mpu_t *mpu, uint16_t addr); extern void mpu401_init(mpu_t *mpu, uint16_t addr, int irq, int mode, int receive_input); diff --git a/src/include/86box/snd_sb.h b/src/include/86box/snd_sb.h index 90e3e4355..181d08311 100644 --- a/src/include/86box/snd_sb.h +++ b/src/include/86box/snd_sb.h @@ -109,6 +109,8 @@ typedef struct sb_ct1745_mixer_t uint8_t index; uint8_t regs[256]; + + int output_filter; /* for clones */ } sb_ct1745_mixer_t; typedef struct sb_t @@ -137,8 +139,13 @@ extern void sb_ct1345_mixer_write(uint16_t addr, uint8_t val, void *p); extern uint8_t sb_ct1345_mixer_read(uint16_t addr, void *p); extern void sb_ct1345_mixer_reset(sb_t* sb); +extern void sb_ct1745_mixer_write(uint16_t addr, uint8_t val, void *p); +extern uint8_t sb_ct1745_mixer_read(uint16_t addr, void *p); +extern void sb_ct1745_mixer_reset(sb_t* sb); + extern void sb_get_buffer_sbpro(int32_t *buffer, int len, void *p); extern void sbpro_filter_cd_audio(int channel, double *buffer, void *p); +extern void sb16_awe32_filter_cd_audio(int channel, double *buffer, void *p); extern void sb_close(void *p); extern void sb_speed_changed(void *p); diff --git a/src/include/86box/sound.h b/src/include/86box/sound.h index f4a80fd08..c364d25ef 100644 --- a/src/include/86box/sound.h +++ b/src/include/86box/sound.h @@ -120,6 +120,7 @@ extern const device_t sb_pro_mcv_device; extern const device_t sb_pro_compat_device; extern const device_t sb_16_device; extern const device_t sb_16_pnp_device; +extern const device_t sb_16_compat_device; extern const device_t sb_32_pnp_device; extern const device_t sb_awe32_device; extern const device_t sb_awe32_pnp_device; @@ -140,6 +141,10 @@ extern const device_t cs4235_onboard_device; extern const device_t cs4236b_device; extern const device_t cs4237b_device; extern const device_t cs4238b_device; + +/* C-Media CMI8x38 */ +extern const device_t cmi8338_device; +extern const device_t cmi8738_device; #endif #endif /*EMU_SOUND_H*/ diff --git a/src/sound/CMakeLists.txt b/src/sound/CMakeLists.txt index ef753cd6f..d47cec8d6 100644 --- a/src/sound/CMakeLists.txt +++ b/src/sound/CMakeLists.txt @@ -16,7 +16,7 @@ add_library(snd OBJECT sound.c snd_opl.c snd_opl_nuked.c snd_resid.cc midi.c midi_rtmidi.cpp snd_speaker.c snd_pssj.c snd_lpt_dac.c snd_ac97_codec.c snd_ac97_via.c snd_lpt_dss.c snd_ps1.c snd_adlib.c snd_adlibgold.c snd_ad1848.c snd_audiopci.c - snd_azt2316a.c snd_cms.c snd_cs423x.c snd_gus.c snd_sb.c snd_sb_dsp.c + snd_azt2316a.c snd_cms.c snd_cmi8x38.c snd_cs423x.c snd_gus.c snd_sb.c snd_sb_dsp.c snd_emu8k.c snd_mpu401.c snd_sn76489.c snd_ssi2001.c snd_wss.c snd_ym7128.c) if(OPENAL) diff --git a/src/sound/snd_cmi8x38.c b/src/sound/snd_cmi8x38.c new file mode 100644 index 000000000..59d9e36f4 --- /dev/null +++ b/src/sound/snd_cmi8x38.c @@ -0,0 +1,830 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * C-Media CMI8x38 PCI audio controller emulation. + * + * + * + * Authors: RichardG, + * + * Copyright 2022 RichardG. + */ +#include +#include +#include +#include +#include +#define HAVE_STDARG_H +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/io.h> +#include <86box/mem.h> +#include <86box/pic.h> +#include <86box/timer.h> +#include <86box/pci.h> +#include <86box/sound.h> +#include <86box/snd_sb.h> + + +enum { + CMEDIA_CMI8338 = 0x00, + CMEDIA_CMI8738 = 0x11 +}; + +typedef struct { + uint8_t id, reg, always_run, playback_enabled; + struct _cmi8x38_ *dev; + + uint32_t sample_ptr, fifo_pos, fifo_end; + int32_t frame_count_dma, frame_count_fragment, sample_count_out; + uint8_t fifo[32], restart; + + int16_t out_l, out_r; + int vol_l, vol_r, pos; + int32_t buffer[SOUNDBUFLEN * 2]; + uint64_t timer_latch; + + pc_timer_t dma_timer, poll_timer; +} cmi8x38_dma_t; + +typedef struct _cmi8x38_ { + uint16_t io_base; + uint8_t type, pci_regs[256], io_regs[256], mixer_ext_regs[16]; + int slot; + + sb_t *sb; + cmi8x38_dma_t dma[2]; + + int master_vol_l, master_vol_r, cd_vol_l, cd_vol_r; +} cmi8x38_t; + +#define ENABLE_CMI8X38_LOG 1 +#ifdef ENABLE_CMI8X38_LOG +int cmi8x38_do_log = ENABLE_CMI8X38_LOG; + +static void +cmi8x38_log(const char *fmt, ...) +{ + va_list ap; + + if (cmi8x38_do_log) { + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); + } +} +#else +#define cmi8x38_log(fmt, ...) +#endif + +static const double freqs[] = {5512.0, 11025.0, 22050.0, 44100.0, 8000.0, 16000.0, 32000.0, 48000.0}; + + +static void cmi8x38_dma_process(void *priv); +static void cmi8x38_speed_changed(void *priv); + + +static void +cmi8x38_update_irqs(cmi8x38_t *dev) +{ + /* Calculate and use the any interrupt flag. */ + if (*((uint32_t *) &dev->io_regs[0x10]) & 0x0401c003) { + dev->io_regs[0x13] |= 0x80; + pci_set_irq(dev->slot, PCI_INTA); + cmi8x38_log("CMI8x38: Raising IRQ\n"); + } else { + dev->io_regs[0x13] &= ~0x80; + pci_clear_irq(dev->slot, PCI_INTA); + } +} + + +static void +cmi8x38_start_playback(cmi8x38_t *dev, uint8_t val) +{ + uint8_t i; + + i = !(val & 0x01); + if (!dev->dma[0].playback_enabled && i) + timer_advance_u64(&dev->dma[0].poll_timer, dev->dma[0].timer_latch); + dev->dma[0].playback_enabled = i; + + i = !(val & 0x02); + if (!dev->dma[1].playback_enabled && i) + timer_advance_u64(&dev->dma[1].poll_timer, dev->dma[1].timer_latch); + dev->dma[1].playback_enabled = i; +} + + +static uint8_t +cmi8x38_read(uint16_t addr, void *priv) +{ + cmi8x38_t *dev = (cmi8x38_t *) priv; + addr &= 0xff; + uint8_t ret; + + switch (addr) { + case 0x22: + sb_ct1745_mixer_t *mixer = &dev->sb->mixer_sb16; + if (mixer->index >= 0xf0) + ret = dev->mixer_ext_regs[mixer->index & 0x0f]; + else + ret = sb_ct1745_mixer_read(1, dev->sb); + break; + + case 0x23: + ret = sb_ct1745_mixer_read(0, dev->sb); + break; + + case 0x40 ... 0x4f: + if (dev->type == CMEDIA_CMI8338) + goto io_reg; + else + ret = mpu401_read(addr, val, dev->sb->mpu); + break; + + case 0x50 ... 0x5f: + if (dev->type == CMEDIA_CMI8338) + goto io_reg; + else + ret = opl3_read(addr, &dev->sb->opl); + break; + + case 0x80: case 0x88: + ret = dev->dma[(addr & 0x78) >> 3].sample_ptr; + break; + + case 0x81: case 0x89: + ret = dev->dma[(addr & 0x78) >> 3].sample_ptr >> 8; + break; + + case 0x82: case 0x8a: + ret = dev->dma[(addr & 0x78) >> 3].sample_ptr >> 16; + break; + + case 0x83: case 0x8b: + ret = dev->dma[(addr & 0x78) >> 3].sample_ptr >> 24; + break; + + case 0x84: case 0x8c: + ret = dev->dma[(addr & 0x78) >> 3].frame_count_dma; + break; + + case 0x85: case 0x8d: + ret = dev->dma[(addr & 0x78) >> 3].frame_count_dma >> 8; + break; + + case 0x86: case 0x8e: + ret = dev->dma[(addr & 0x78) >> 3].sample_count_out >> 2; + break; + + case 0x87: case 0x8f: + ret = dev->dma[(addr & 0x78) >> 3].sample_count_out >> 10; + break; + + default: +io_reg: ret = dev->io_regs[addr]; + break; + } + + cmi8x38_log("CMI8x38: read(%02X) = %02X\n", addr, ret); + return ret; +} + + +static void +cmi8x38_write(uint16_t addr, uint8_t val, void *priv) +{ + cmi8x38_t *dev = (cmi8x38_t *) priv; + addr &= 0xff; + cmi8x38_log("CMI8x38: write(%02X, %02X)\n", addr, val); + + switch (addr) { + case 0x00: + val &= 0x0f; + + /* Don't care about recording DMA. */ + dev->dma[0].always_run = val & 0x01; + dev->dma[1].always_run = val & 0x02; + + /* Start playback if requested. */ + cmi8x38_start_playback(dev, val); + break; + + case 0x02: + /* Reset DMA channels if requested. */ + if (val & 0x04) + val &= ~0x01; + if (val & 0x08) + val &= ~0x02; + + val &= 0x03; + dev->io_regs[addr] = val; + + /* Start DMA channels if requested. */ + if (val & 0x01) { + cmi8x38_log("CMI8x38: DMA 0 trigger\n"); + dev->dma[0].restart = 1; + cmi8x38_dma_process(&dev->dma[0]); + } + if (val & 0x02) { + cmi8x38_log("CMI8x38: DMA 1 trigger\n"); + dev->dma[1].restart = 1; + cmi8x38_dma_process(&dev->dma[1]); + } + + /* Start playback along with DMA channels. */ + if (val & 0x03) + cmi8x38_start_playback(dev, dev->io_regs[0x00]); + break; + + case 0x05: + dev->io_regs[addr] = val; + cmi8x38_speed_changed(dev); + break; + + case 0x08: + if (dev->type == CMEDIA_CMI8338) + val &= 0x0f; + break; + + case 0x09: +#if 0 /* actual CMI8338 behavior unconfirmed; this register is required for the Windows XP driver which outputs 96K */ + if (dev->type == CMEDIA_CMI8338) + return; +#endif + cmi8x38_speed_changed(dev); + break; + + case 0x0a: case 0x0b: + if (dev->type == CMEDIA_CMI8338) + return; + else + val &= 0xe0; + + if (addr == 0x0a) + dev->pci_regs[0x0d] = (val & 0x80) ? 0x48 : 0x20; /* clearing SETLAT48 is undefined */ + break; + + case 0x0e: + val &= 0x07; + + /* Clear interrupts. */ + dev->io_regs[0x10] &= val | 0xfc; + if (!(val & 0x04)) + dev->io_regs[0x11] &= ~0xc0; + cmi8x38_update_irqs(dev); + break; + + case 0x15: + if (dev->type == CMEDIA_CMI8338) + return; + else + val &= 0xf0; + break; + + case 0x16: + if (dev->type == CMEDIA_CMI8338) + val &= 0xa0; + break; + + case 0x17: + if (dev->type == CMEDIA_CMI8338) { + val &= 0xf3; + + /* Force IRQ if requested. Clearing this bit is undefined. */ + if (val & 0x10) + pci_set_irq(dev->slot, PCI_INTA); + else if ((dev->io_regs[0x17] & 0x10) && !(val & 0x10)) + pci_clear_irq(dev->slot, PCI_INTA); + } + break; + + case 0x18: + if (dev->type == CMEDIA_CMI8338) + val &= 0x0f; + else + val &= 0xdf; + break; + + case 0x19: + if (dev->type == CMEDIA_CMI8338) + return; + else + val &= 0xe0; + break; + + case 0x1a: + val &= 0xfd; + break; + + case 0x1b: + if (dev->type == CMEDIA_CMI8338) + val &= 0xf0; + else + val &= 0xd7; + break; + + case 0x20: + /* ??? */ + break; + + case 0x21: + if (dev->type != CMEDIA_CMI8338) + val &= 0x07; + break; + + case 0x22: + sb_ct1745_mixer_t *mixer = &dev->sb->mixer_sb16; + switch (mixer->index) { + case 0xf0: + if (dev->type == CMEDIA_CMI8338) + val &= 0xfe; + dev->mixer_ext_regs[dev->sb->mixer_sb16.index & 0x0f] = val; + break; + + case 0xf8 ... 0xff: + if (dev->type == CMEDIA_CMI8338) + dev->mixer_ext_regs[dev->sb->mixer_sb16.index & 0x0f] = val; + /* fall-through */ + + case 0xf1 ... 0xf7: + break; + + default: + sb_ct1745_mixer_write(1, val, dev->sb); + + /* Our clone mixer lacks the [3F:47] controls. */ + mixer->input_gain_L = 0; + mixer->input_gain_R = 0; + mixer->output_gain_L = (double) 1.0; + mixer->output_gain_R = (double) 1.0; + mixer->bass_l = 8; + mixer->bass_r = 8; + mixer->treble_l = 8; + mixer->treble_r = 8; + break; + } + return; + + case 0x23: + sb_ct1745_mixer_write(0, val, dev->sb); + return; + + case 0x24: + if (dev->type == CMEDIA_CMI8338) + val &= 0xcf; + break; + + case 0x27: + if (dev->type == CMEDIA_CMI8338) + val &= 0x03; + else + val &= 0x27; + break; + + case 0x40 ... 0x4f: + if (dev->type == CMEDIA_CMI8338) + break; + else + mpu401_write(addr, val, dev->sb->mpu); + return; + + case 0x50 ... 0x5f: + if (dev->type == CMEDIA_CMI8338) + break; + else + opl3_write(addr, val, &dev->sb->opl); + return; + + case 0x92: + if (dev->type == CMEDIA_CMI8338) + return; + else + val &= 0x1f; + break; + + case 0x93: + if (dev->type == CMEDIA_CMI8338) + return; + else + val &= 0x10; + break; + + case 0x04: + case 0x25: case 0x26: + case 0x70: case 0x71: + case 0x80 ... 0x8f: + break; + + default: + return; + } + + dev->io_regs[addr] = val; +} + + +static void +cmi8x38_remap(cmi8x38_t *dev, uint8_t io_msb, uint8_t enable) +{ + if (dev->io_base) + io_removehandler(dev->io_base, 256, cmi8x38_read, NULL, NULL, cmi8x38_write, NULL, NULL, dev); + + if (enable & 0x01) + dev->io_base = io_msb << 8; + else + dev->io_base = 0; + cmi8x38_log("CMI8x38: remap(%04X)\n", dev->io_base); + + if (dev->io_base) + io_sethandler(dev->io_base, 256, cmi8x38_read, NULL, NULL, cmi8x38_write, NULL, NULL, dev); +} + + +static uint8_t +cmi8x38_pci_read(int func, int addr, void *priv) +{ + cmi8x38_t *dev = (cmi8x38_t *) priv; + uint8_t ret = 0xff; + + if (!func) { + ret = dev->pci_regs[addr]; + cmi8x38_log("CMI8x38: pci_read(%02X) = %02X\n", addr, ret); + } + + return ret; +} + + +static void +cmi8x38_pci_write(int func, int addr, uint8_t val, void *priv) +{ + cmi8x38_t *dev = (cmi8x38_t *) priv; + + if (func) + return; + + cmi8x38_log("CMI8x38: pci_write(%02X, %02X)\n", addr, val); + + switch (addr) { + case 0x04: + val &= 0x05; + cmi8x38_remap(dev, dev->pci_regs[0x11], val); + break; + + case 0x05: + val &= 0x01; + break; + + case 0x11: + cmi8x38_remap(dev, val, dev->pci_regs[0x04]); + break; + + case 0x2c: case 0x2d: case 0x2e: case 0x2f: + if (!(dev->io_regs[0x1a] & 0x01)) + return; + break; + + case 0x40: + if (dev->type == CMEDIA_CMI8338) + val &= 0x0f; + else + return; + break; + + case 0x0c: case 0x0d: + case 0x3c: + break; + + default: + return; + } + + dev->pci_regs[addr] = val; +} + + +static void +cmi8x38_update(cmi8x38_t *dev, cmi8x38_dma_t *dma) +{ + sb_ct1745_mixer_t *mixer = &dev->sb->mixer_sb16; + int32_t l = (dma->out_l * mixer->voice_l) * mixer->master_l, + r = (dma->out_r * mixer->voice_r) * mixer->master_r; + + for (; dma->pos < sound_pos_global; dma->pos++) { + dma->buffer[dma->pos*2] = l; + dma->buffer[dma->pos*2 + 1] = r; + } +} + + +static void +cmi8x38_dma_process(void *priv) +{ + cmi8x38_dma_t *dma = (cmi8x38_dma_t *) priv; + cmi8x38_t *dev = dma->dev; + + /* Stop if this DMA channel is not active. */ + uint8_t dma_bit = 0x01 << dma->id; + if (!(dev->io_regs[0x02] & dma_bit)) { + cmi8x38_log("CMI8x38: Stopping DMA %d due to inactive channel (%02X)\n", dma->id, dev->io_regs[0x02]); + return; + } + + /* Schedule next run. */ + timer_on_auto(&dma->dma_timer, 10.0); + + /* Process DMA if it's active, and the FIFO has room or is disabled. */ + uint8_t dma_status = dev->io_regs[0x00] >> dma->id; + if (!(dma_status & 0x04) && (dma->always_run || ((dma->fifo_end - dma->fifo_pos) <= (sizeof(dma->fifo) - 4)))) { + /* Start DMA if requested. */ + if (dma->restart) { + /* Set up base address and counters. + I have no idea how sample_count_out is supposed to work, + nothing consumes it, so it's implemented as an assumption. */ + dma->restart = 0; + dma->sample_ptr = *((uint32_t *) &dev->io_regs[dma->reg]); + dma->frame_count_dma = dma->sample_count_out = *((uint16_t *) &dev->io_regs[dma->reg | 0x4]); + dma->frame_count_fragment = *((uint16_t *) &dev->io_regs[dma->reg | 0x6]); + + cmi8x38_log("CMI8x38: Starting DMA %d at %08X\n", dma->id, dma->sample_ptr); + } + + if (dma_status & 0x01) { + /* Write channel: read data from FIFO. */ + mem_writel_phys(dma->sample_ptr, *((uint32_t *) &dma->fifo[dma->fifo_end & (sizeof(dma->fifo) - 1)])); + } else { + /* Read channel: write data to FIFO. */ + *((uint32_t *) &dma->fifo[dma->fifo_end & (sizeof(dma->fifo) - 1)]) = mem_readl_phys(dma->sample_ptr); + } + dma->fifo_end += 4; + dma->sample_ptr += 4; + + /* Check if the fragment size was reached. */ + if (--dma->frame_count_fragment <= 0) { + cmi8x38_log("CMI8x38: DMA %d fragment size reached at %04X frames left", dma->id, dma->frame_count_dma - 1); + + /* Reset fragment counter. */ + dma->frame_count_fragment = *((uint16_t *) &dev->io_regs[dma->reg | 0x6]); + + /* Fire interrupt if requested. */ + if (dev->io_regs[0x0e] & dma_bit) { + cmi8x38_log(", firing interrupt\n"); + + /* Set channel interrupt flag. */ + dev->io_regs[0x10] |= dma_bit; + + /* Fire interrupt. */ + cmi8x38_update_irqs(dev); + } else { + cmi8x38_log("\n"); + } + } + + /* Check if the buffer's end was reached. */ + if (--dma->frame_count_dma <= 0) { + cmi8x38_log("CMI8x38: DMA %d end reached, restarting\n", dma->id); + + /* Restart DMA on the next run. */ + dma->restart = 1; + } + } +} + + +static void +cmi8x38_poll(void *priv) +{ + cmi8x38_dma_t *dma = (cmi8x38_dma_t *) priv; + cmi8x38_t *dev = dma->dev; + + /* Schedule next run if playback is enabled. */ + if (dev->io_regs[0x00] & (1 << dma->id)) + dma->playback_enabled = 0; + else + timer_advance_u64(&dma->poll_timer, dma->timer_latch); + + /* Update audio buffer. */ + cmi8x38_update(dev, dma); + + /* Feed next sample from the FIFO. */ + switch ((dev->io_regs[0x08] >> (dma->id << 1)) & 0x03) { + case 0x00: /* Mono, 8-bit PCM */ + if ((dma->fifo_end - dma->fifo_pos) >= 1) { + dma->out_l = dma->out_r = (dma->fifo[dma->fifo_pos++ & (sizeof(dma->fifo) - 1)] ^ 0x80) << 8; + dma->sample_count_out--; + return; + } + break; + + case 0x01: /* Stereo, 8-bit PCM */ + if ((dma->fifo_end - dma->fifo_pos) >= 2) { + dma->out_l = (dma->fifo[dma->fifo_pos++ & (sizeof(dma->fifo) - 1)] ^ 0x80) << 8; + dma->out_r = (dma->fifo[dma->fifo_pos++ & (sizeof(dma->fifo) - 1)] ^ 0x80) << 8; + dma->sample_count_out -= 2; + return; + } + break; + + case 0x02: /* Mono, 16-bit PCM */ + if ((dma->fifo_end - dma->fifo_pos) >= 2) { + dma->out_l = dma->out_r = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); + dma->fifo_pos += 2; + dma->sample_count_out -= 2; + return; + } + break; + + case 0x03: /* Stereo, 16-bit PCM */ + if ((dma->fifo_end - dma->fifo_pos) >= 4) { + dma->out_l = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); + dma->fifo_pos += 2; + dma->out_r = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); + dma->fifo_pos += 2; + dma->sample_count_out -= 4; + return; + } + break; + } + + /* Feed silence if the FIFO is empty. */ + dma->out_l = dma->out_r = 0; +} + + +static void +cmi8x38_get_buffer(int32_t *buffer, int len, void *priv) +{ + cmi8x38_t *dev = (cmi8x38_t *) priv; + + /* Update wave playback channels. */ + cmi8x38_update(dev, &dev->dma[0]); + cmi8x38_update(dev, &dev->dma[1]); + + /* Apply wave mute. */ + if (!(dev->io_regs[0x24] & 0x40)) { + /* Fill buffer. */ + for (int c = 0; c < len * 2; c++) { + buffer[c] += dev->dma[0].buffer[c]; + buffer[c] += dev->dma[1].buffer[c]; + } + } + + dev->dma[0].pos = dev->dma[1].pos = 0; +} + + +static void +cmi8x38_speed_changed(void *priv) +{ + cmi8x38_t *dev = (cmi8x38_t *) priv; + double freq; + uint8_t dsr = dev->io_regs[0x09], freqreg = dev->io_regs[0x05] >> 2; + + /* CMI8338 claims the frequency controls are for DAC (playback) and ADC (recording) + respectively, while CMI8738 claims they're for channel 0 and channel 1. The Linux + driver just assumes the latter definition, so that's what we're going to use here. */ + for (int i = 0; i < (sizeof(dev->dma) / sizeof(dev->dma[0])); i++) { + /* More confusion. The Linux driver implies the sample rate doubling + bits take precedence over any configured sample rate. It also + supports 128K with both doubling bits set, which is undocumented. */ + switch (dsr & 0x03) { + case 0x01: freq = 88200.0; break; + case 0x02: freq = 96000.0; break; + case 0x03: freq = 128000.0; break; + default: freq = freqs[freqreg & 0x07]; break; + } + dsr >>= 2; + freqreg >>= 3; + + /* Set period. */ + dev->dma[i].timer_latch = (uint64_t) ((double) TIMER_USEC * (1000000.0 / freq)); + } +} + + +static void +cmi8x38_reset(void *priv) +{ + cmi8x38_t *dev = (cmi8x38_t *) priv; + + /* Reset PCI configuration registers. */ + memset(dev->pci_regs, 0, sizeof(dev->pci_regs)); + dev->pci_regs[0x00] = 0xf6; dev->pci_regs[0x01] = 0x13; + dev->pci_regs[0x02] = dev->type; dev->pci_regs[0x03] = 0x01; + dev->pci_regs[0x06] = (dev->type == CMEDIA_CMI8338) ? 0x80 : 0x10; dev->pci_regs[0x07] = 0x02; + dev->pci_regs[0x08] = 0x10; + dev->pci_regs[0x0a] = 0x01; dev->pci_regs[0x0b] = 0x04; + dev->pci_regs[0x0d] = 0x20; + dev->pci_regs[0x10] = 0x01; + dev->pci_regs[0x2c] = 0xf6; dev->pci_regs[0x2d] = 0x13; + if (dev->type == CMEDIA_CMI8338) { + dev->pci_regs[0x2e] = 0xff; dev->pci_regs[0x2f] = 0xff; + } else { + dev->pci_regs[0x2e] = dev->type; dev->pci_regs[0x2f] = 0x01; + dev->pci_regs[0x34] = 0x40; + } + dev->pci_regs[0x3d] = 0x01; + dev->pci_regs[0x3e] = 0x02; + dev->pci_regs[0x3f] = 0x18; + + /* Reset I/O space registers. */ + memset(dev->io_regs, 0, sizeof(dev->io_regs)); + if (dev->type == CMEDIA_CMI8738) + dev->io_regs[0x0f] = 0x04; /* chip version 039 with 4-channel support */ + + /* Reset DMA channels. */ + for (int i = 0; i < (sizeof(dev->dma) / sizeof(dev->dma[0])); i++) { + dev->dma[i].playback_enabled = 0; + + dev->dma[i].fifo_pos = dev->dma[i].fifo_end = 0; + memset(dev->dma[i].fifo, 0, sizeof(dev->dma[i].fifo)); + } + + /* Reset Sound Blaster 16 mixer. */ + sb_ct1745_mixer_reset(dev->sb); +} + + +static void * +cmi8x38_init(const device_t *info) +{ + cmi8x38_t *dev = malloc(sizeof(cmi8x38_t)); + memset(dev, 0, sizeof(cmi8x38_t)); + + /* Set the chip type. */ + dev->type = info->local; + cmi8x38_log("CMI8x38: init(%02X)\n", dev->type); + + /* Initialize Sound Blaster 16. */ + dev->sb = device_add_inst(&sb_16_compat_device, 1); + dev->sb->opl_enabled = 1; /* let snd_sb.c handle the OPL3 */ + dev->sb->mixer_sb16.output_filter = 0; /* no output filtering */ + + /* Initialize DMA channels. */ + for (int i = 0; i < (sizeof(dev->dma) / sizeof(dev->dma[0])); i++) { + dev->dma[i].id = i; + dev->dma[i].reg = 0x80 + (8 * i); + dev->dma[i].dev = dev; + + timer_add(&dev->dma[i].dma_timer, cmi8x38_dma_process, &dev->dma[i], 0); + timer_add(&dev->dma[i].poll_timer, cmi8x38_poll, &dev->dma[i], 0); + } + cmi8x38_speed_changed(dev); + + /* Initialize playback handler and CD audio filter. */ + sound_add_handler(cmi8x38_get_buffer, dev); + sound_set_cd_audio_filter(sb16_awe32_filter_cd_audio, dev->sb); + + /* Add PCI card. */ + dev->slot = pci_add_card((info->local & 0x100) ? PCI_ADD_SOUND : PCI_ADD_NORMAL, cmi8x38_pci_read, cmi8x38_pci_write, dev); + + /* Perform initial reset. */ + cmi8x38_reset(dev); + + return dev; +} + + +static void +cmi8x38_close(void *priv) +{ + cmi8x38_t *dev = (cmi8x38_t *) priv; + + cmi8x38_log("CMI8x38: close()\n"); + + free(dev); +} + + +const device_t cmi8338_device = +{ + "C-Media CMI8338", + "cmi8338", + DEVICE_PCI, + CMEDIA_CMI8338, + cmi8x38_init, cmi8x38_close, cmi8x38_reset, + { NULL }, + cmi8x38_speed_changed, + NULL, + NULL +}; + +const device_t cmi8738_device = +{ + "C-Media CMI8738", + "cmi8738", + DEVICE_PCI, + CMEDIA_CMI8738, + cmi8x38_init, cmi8x38_close, cmi8x38_reset, + { NULL }, + cmi8x38_speed_changed, + NULL, + NULL +}; diff --git a/src/sound/snd_mpu401.c b/src/sound/snd_mpu401.c index 0922ab21c..57d26c5be 100644 --- a/src/sound/snd_mpu401.c +++ b/src/sound/snd_mpu401.c @@ -1213,7 +1213,7 @@ MPU401_ReadData(mpu_t *mpu) } -static void +void mpu401_write(uint16_t addr, uint8_t val, void *priv) { mpu_t *mpu = (mpu_t *)priv; @@ -1233,7 +1233,7 @@ mpu401_write(uint16_t addr, uint8_t val, void *priv) } -static uint8_t +uint8_t mpu401_read(uint16_t addr, void *priv) { mpu_t *mpu = (mpu_t *)priv; diff --git a/src/sound/snd_sb.c b/src/sound/snd_sb.c index 56e34bbb9..d90af436b 100644 --- a/src/sound/snd_sb.c +++ b/src/sound/snd_sb.c @@ -384,9 +384,14 @@ sb_get_buffer_sb16_awe32(int32_t *buffer, int len, void *p) in_r = (mixer->input_selector_right & INPUT_MIDI_L) ? ((int32_t) out_l) : 0 + (mixer->input_selector_right & INPUT_MIDI_R) ? ((int32_t) out_r) : 0; - /* We divide by 3 to get the volume down to normal. */ - out_l += (low_fir_sb16(0, 0, (double) sb->dsp.buffer[c]) * mixer->voice_l) / 3.0; - out_r += (low_fir_sb16(0, 1, (double) sb->dsp.buffer[c + 1]) * mixer->voice_r) / 3.0; + if (mixer->output_filter) { + /* We divide by 3 to get the volume down to normal. */ + out_l += (low_fir_sb16(0, 0, (double) sb->dsp.buffer[c]) * mixer->voice_l) / 3.0; + out_r += (low_fir_sb16(0, 1, (double) sb->dsp.buffer[c + 1]) * mixer->voice_r) / 3.0; + } else { + out_l += (((double) sb->dsp.buffer[c]) * mixer->voice_l) / 3.0; + out_r += (((double) sb->dsp.buffer[c + 1]) * mixer->voice_r) / 3.0; + } out_l *= mixer->master_l; out_r *= mixer->master_r; @@ -468,7 +473,7 @@ sb_get_buffer_sb16_awe32(int32_t *buffer, int len, void *p) } -static void +void sb16_awe32_filter_cd_audio(int channel, double *buffer, void *p) { sb_t *sb = (sb_t *)p; @@ -481,7 +486,10 @@ sb16_awe32_filter_cd_audio(int channel, double *buffer, void *p) double bass_treble; double output_gain = (channel ? mixer->output_gain_R : mixer->output_gain_L); - c = (low_fir_sb16(1, channel, *buffer) * cd) / 3.0; + if (mixer->output_filter) + c = (low_fir_sb16(1, channel, *buffer) * cd) / 3.0; + else + c = ((*buffer) * cd) / 3.0; c *= master; /* This is not exactly how one does bass/treble controls, but the end result is like it. @@ -691,7 +699,7 @@ sb_ct1345_mixer_reset(sb_t* sb) } -static void +void sb_ct1745_mixer_write(uint16_t addr, uint8_t val, void *p) { sb_t *sb = (sb_t *) p; @@ -862,7 +870,7 @@ sb_ct1745_mixer_write(uint16_t addr, uint8_t val, void *p) } -static uint8_t +uint8_t sb_ct1745_mixer_read(uint16_t addr, void *p) { sb_t *sb = (sb_t *) p; @@ -1013,7 +1021,7 @@ sb_ct1745_mixer_read(uint16_t addr, void *p) } -static void +void sb_ct1745_mixer_reset(sb_t* sb) { sb_ct1745_mixer_write(4, 0, sb); @@ -1655,6 +1663,7 @@ sb_16_init(const device_t *info) } sb->mixer_enabled = 1; + sb->mixer_sb16.output_filter = 1; io_sethandler(addr + 4, 0x0002, sb_ct1745_mixer_read, NULL, NULL, sb_ct1745_mixer_write, NULL, NULL, sb); sound_add_handler(sb_get_buffer_sb16_awe32, sb); @@ -1688,6 +1697,7 @@ sb_16_pnp_init(const device_t *info) sb_ct1745_mixer_reset(sb); sb->mixer_enabled = 1; + sb->mixer_sb16.output_filter = 1; sound_add_handler(sb_get_buffer_sb16_awe32, sb); sound_set_cd_audio_filter(sb16_awe32_filter_cd_audio, sb); @@ -1709,6 +1719,29 @@ sb_16_pnp_init(const device_t *info) } +static void * +sb_16_compat_init(const device_t *info) +{ + sb_t *sb = malloc(sizeof(sb_t)); + memset(sb, 0, sizeof(sb_t)); + + opl3_init(&sb->opl); + + sb_dsp_init(&sb->dsp, SB16, SB_SUBTYPE_DEFAULT, sb); + sb_ct1745_mixer_reset(sb); + + sb->mixer_enabled = 1; + sound_add_handler(sb_get_buffer_sb16_awe32, sb); + + sb->mpu = (mpu_t *) malloc(sizeof(mpu_t)); + memset(sb->mpu, 0, sizeof(mpu_t)); + mpu401_init(sb->mpu, 0, 0, M_UART, 1); + sb_dsp_set_mpu(&sb->dsp, sb->mpu); + + return sb; +} + + static int sb_awe32_available() { @@ -1783,6 +1816,7 @@ sb_awe32_init(const device_t *info) } sb->mixer_enabled = 1; + sb->mixer_sb16.output_filter = 1; io_sethandler(addr + 4, 0x0002, sb_ct1745_mixer_read, NULL, NULL, sb_ct1745_mixer_write, NULL, NULL, sb); sound_add_handler(sb_get_buffer_sb16_awe32, sb); @@ -1820,6 +1854,7 @@ sb_awe32_pnp_init(const device_t *info) sb_ct1745_mixer_reset(sb); sb->mixer_enabled = 1; + sb->mixer_sb16.output_filter = 1; sound_add_handler(sb_get_buffer_sb16_awe32, sb); sound_set_cd_audio_filter(sb16_awe32_filter_cd_audio, sb); @@ -2870,6 +2905,18 @@ const device_t sb_16_pnp_device = sb_16_pnp_config }; +const device_t sb_16_compat_device = +{ + "Sound Blaster 16 (Compatibility)", + "sb16_compat", + DEVICE_ISA | DEVICE_AT, + 0, + sb_16_compat_init, sb_close, NULL, { NULL }, + sb_speed_changed, + NULL, + NULL +}; + const device_t sb_32_pnp_device = { "Sound Blaster 32 PnP", diff --git a/src/sound/sound.c b/src/sound/sound.c index 4beab94ba..2156ec68d 100644 --- a/src/sound/sound.c +++ b/src/sound/sound.c @@ -133,6 +133,8 @@ static const SOUND_CARD sound_cards[] = { &ncr_business_audio_device }, { &sb_mcv_device }, { &sb_pro_mcv_device }, + { &cmi8338_device }, + { &cmi8738_device }, { &es1371_device }, { &ad1881_device }, { &cs4297a_device }, diff --git a/src/win/Makefile.mingw b/src/win/Makefile.mingw index 6c5b92114..f55c7d8df 100644 --- a/src/win/Makefile.mingw +++ b/src/win/Makefile.mingw @@ -657,7 +657,7 @@ SNDOBJ := sound.o \ snd_lpt_dac.o snd_lpt_dss.o \ snd_adlib.o snd_adlibgold.o snd_ad1848.o snd_audiopci.o \ snd_ac97_codec.o snd_ac97_via.o \ - snd_azt2316a.o snd_cs423x.o \ + snd_azt2316a.o snd_cs423x.o snd_cmi8x38.o \ snd_cms.o \ snd_gus.o \ snd_sb.o snd_sb_dsp.o \ From 0a0f3c95a64995a1c1d3d1c75254d3b4748579ba Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Fri, 25 Feb 2022 20:03:18 -0300 Subject: [PATCH 02/62] Add game port to CMI8x38 --- src/sound/snd_cmi8x38.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/sound/snd_cmi8x38.c b/src/sound/snd_cmi8x38.c index 59d9e36f4..afbca960f 100644 --- a/src/sound/snd_cmi8x38.c +++ b/src/sound/snd_cmi8x38.c @@ -58,6 +58,7 @@ typedef struct _cmi8x38_ { int slot; sb_t *sb; + void *gameport; cmi8x38_dma_t dma[2]; int master_vol_l, master_vol_r, cd_vol_l, cd_vol_r; @@ -243,6 +244,11 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv) cmi8x38_start_playback(dev, dev->io_regs[0x00]); break; + case 0x04: + /* Enable or disable the game port. */ + gameport_remap(dev->gameport, (val & 0x02) ? 0x200 : 0); + break; + case 0x05: dev->io_regs[addr] = val; cmi8x38_speed_changed(dev); @@ -335,7 +341,9 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv) break; case 0x21: - if (dev->type != CMEDIA_CMI8338) + if (dev->type == CMEDIA_CMI8338) + val &= 0xf7; + else val &= 0x07; break; @@ -416,7 +424,6 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv) val &= 0x10; break; - case 0x04: case 0x25: case 0x26: case 0x70: case 0x71: case 0x80 ... 0x8f: @@ -782,6 +789,9 @@ cmi8x38_init(const device_t *info) sound_add_handler(cmi8x38_get_buffer, dev); sound_set_cd_audio_filter(sb16_awe32_filter_cd_audio, dev->sb); + /* Initialize game port. */ + dev->gameport = gameport_add(&gameport_pnp_device); + /* Add PCI card. */ dev->slot = pci_add_card((info->local & 0x100) ? PCI_ADD_SOUND : PCI_ADD_NORMAL, cmi8x38_pci_read, cmi8x38_pci_write, dev); From dab4efdd9925fcbdb9ee932729799a374fa5beb5 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Fri, 25 Feb 2022 20:06:21 -0300 Subject: [PATCH 03/62] Fix build --- src/sound/snd_cmi8x38.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sound/snd_cmi8x38.c b/src/sound/snd_cmi8x38.c index afbca960f..206d15fa9 100644 --- a/src/sound/snd_cmi8x38.c +++ b/src/sound/snd_cmi8x38.c @@ -29,6 +29,7 @@ #include <86box/pci.h> #include <86box/sound.h> #include <86box/snd_sb.h> +#include <86box/gameport.h> enum { @@ -146,7 +147,7 @@ cmi8x38_read(uint16_t addr, void *priv) if (dev->type == CMEDIA_CMI8338) goto io_reg; else - ret = mpu401_read(addr, val, dev->sb->mpu); + ret = mpu401_read(addr, dev->sb->mpu); break; case 0x50 ... 0x5f: From 6f6bf999fa249acc198f2ca65e6cebab4c52790e Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Fri, 25 Feb 2022 20:26:21 -0300 Subject: [PATCH 04/62] Add onboard CMI8738 to the ASUS CUV4X-LS --- src/include/86box/sound.h | 2 ++ src/machine/m_at_socket370.c | 3 +++ src/machine/machine_table.c | 2 +- src/sound/snd_cmi8x38.c | 28 +++++++++++++++++++++++++++- 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/include/86box/sound.h b/src/include/86box/sound.h index c364d25ef..16ab290de 100644 --- a/src/include/86box/sound.h +++ b/src/include/86box/sound.h @@ -144,7 +144,9 @@ extern const device_t cs4238b_device; /* C-Media CMI8x38 */ extern const device_t cmi8338_device; +extern const device_t cmi8338_onboard_device; extern const device_t cmi8738_device; +extern const device_t cmi8738_onboard_device; #endif #endif /*EMU_SOUND_H*/ diff --git a/src/machine/m_at_socket370.c b/src/machine/m_at_socket370.c index ae8754f80..1443fb5e5 100644 --- a/src/machine/m_at_socket370.c +++ b/src/machine/m_at_socket370.c @@ -430,6 +430,9 @@ machine_at_cuv4xls_init(const machine_t *model) spd_register(SPD_TYPE_SDRAM, 0xF, 1024); device_add(&as99127f_device); /* fans: Chassis, CPU, Power; temperatures: MB, JTPWR, CPU */ + if (sound_card_current == SOUND_INTERNAL) + device_add(&cmi8738_onboard_device); + return ret; } diff --git a/src/machine/machine_table.c b/src/machine/machine_table.c index 0fc34e930..e1c6aa599 100644 --- a/src/machine/machine_table.c +++ b/src/machine/machine_table.c @@ -913,7 +913,7 @@ const machine_t machines[] = { { "[VIA Apollo Pro 133A] Acorp 6VIA90AP", "6via90ap", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 150000000, 1300, 3500, MACHINE_MULTIPLIER_FIXED, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_BUS_AC97 | MACHINE_IDE_DUAL | MACHINE_SOUND | MACHINE_GAMEPORT, 16384,3145728, 8192, 255, machine_at_6via90ap_init, NULL }, /* Has the VIA VT82C686B southbridge with on-chip KBC identical to the VIA VT82C42N. */ - { "[VIA Apollo Pro 133A] ASUS CUV4X-LS", "cuv4xls", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 150000000, 1300, 3500, 1.5, 8.0, (MACHINE_AGP & ~MACHINE_AT) | MACHINE_BUS_PS2 | MACHINE_BUS_AC97 | MACHINE_IDE_DUAL,16384,4194304, 8192, 255, machine_at_cuv4xls_init, NULL }, + { "[VIA Apollo Pro 133A] ASUS CUV4X-LS", "cuv4xls", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 150000000, 1300, 3500, 1.5, 8.0, (MACHINE_AGP & ~MACHINE_AT) | MACHINE_BUS_PS2 | MACHINE_BUS_AC97 | MACHINE_IDE_DUAL | MACHINE_SOUND, 16384,4194304, 8192, 255, machine_at_cuv4xls_init, NULL }, /* Has a Winbond W83977EF Super I/O chip with on-chip KBC with AMIKey-2 KBC firmware. */ { "[VIA Apollo Pro 133A] BCM GT694VA", "gt694va", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 133333333, 1300, 3500, 1.5, 8.0, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL | MACHINE_SOUND, 16384,3145728, 8192, 255, machine_at_gt694va_init, at_gt694va_get_device }, diff --git a/src/sound/snd_cmi8x38.c b/src/sound/snd_cmi8x38.c index 206d15fa9..1e7f7ff83 100644 --- a/src/sound/snd_cmi8x38.c +++ b/src/sound/snd_cmi8x38.c @@ -767,8 +767,8 @@ cmi8x38_init(const device_t *info) memset(dev, 0, sizeof(cmi8x38_t)); /* Set the chip type. */ + cmi8x38_log("CMI8x38: init(%03X)\n", info->local); dev->type = info->local; - cmi8x38_log("CMI8x38: init(%02X)\n", dev->type); /* Initialize Sound Blaster 16. */ dev->sb = device_add_inst(&sb_16_compat_device, 1); @@ -827,6 +827,19 @@ const device_t cmi8338_device = NULL }; +const device_t cmi8338_onboard_device = +{ + "C-Media CMI8338 (On-Board)", + "cmi8338_onboard", + DEVICE_PCI, + CMEDIA_CMI8338 | 0x100, + cmi8x38_init, cmi8x38_close, cmi8x38_reset, + { NULL }, + cmi8x38_speed_changed, + NULL, + NULL +}; + const device_t cmi8738_device = { "C-Media CMI8738", @@ -839,3 +852,16 @@ const device_t cmi8738_device = NULL, NULL }; + +const device_t cmi8738_onboard_device = +{ + "C-Media CMI8738 (On-Board)", + "cmi8738_onboard", + DEVICE_PCI, + CMEDIA_CMI8738 | 0x100, + cmi8x38_init, cmi8x38_close, cmi8x38_reset, + { NULL }, + cmi8x38_speed_changed, + NULL, + NULL +}; From 60e316cc019e4fc671395599dbed3c9e226a0bba Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Mon, 28 Feb 2022 22:31:44 -0300 Subject: [PATCH 05/62] Add CMI8x38 legacy function remapping --- src/sound/snd_cmi8x38.c | 119 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 111 insertions(+), 8 deletions(-) diff --git a/src/sound/snd_cmi8x38.c b/src/sound/snd_cmi8x38.c index 1e7f7ff83..bd4ea4fe6 100644 --- a/src/sound/snd_cmi8x38.c +++ b/src/sound/snd_cmi8x38.c @@ -54,7 +54,7 @@ typedef struct { } cmi8x38_dma_t; typedef struct _cmi8x38_ { - uint16_t io_base; + uint16_t io_base, sb_base, opl_base, mpu_base; uint8_t type, pci_regs[256], io_regs[256], mixer_ext_regs[16]; int slot; @@ -85,6 +85,7 @@ cmi8x38_log(const char *fmt, ...) #endif static const double freqs[] = {5512.0, 11025.0, 22050.0, 44100.0, 8000.0, 16000.0, 32000.0, 48000.0}; +static const uint16_t opl_ports_cmi8738[] = {0x388, 0x3c8, 0x3e0, 0x3e8}; static void cmi8x38_dma_process(void *priv); @@ -106,6 +107,90 @@ cmi8x38_update_irqs(cmi8x38_t *dev) } +static void +cmi8x38_remap_sb(cmi8x38_t *dev) +{ + if (dev->sb_base) { + io_removehandler(dev->sb_base, 0x0004, opl3_read, NULL, NULL, + opl3_write, NULL, NULL, &dev->sb->opl); + io_removehandler(dev->sb_base + 8, 0x0002, opl3_read, NULL, NULL, + opl3_write, NULL, NULL, &dev->sb->opl); + io_removehandler(dev->sb_base + 4, 0x0002, sb_ct1745_mixer_read, NULL, NULL, + sb_ct1745_mixer_write, NULL, NULL, dev->sb); + + sb_dsp_setaddr(&dev->sb->dsp, 0); + } + + if (dev->io_regs[0x04] & 0x08) { + dev->sb_base = 0x220; + if (dev->type == CMEDIA_CMI8338) + dev->sb_base += (dev->io_regs[0x17] & 0x80) >> 2; + else + dev->sb_base += (dev->io_regs[0x17] & 0x0c) << 3; + } else { + dev->sb_base = 0; + } + cmi8x38_log("CMI8x38: remap_sb(%04X)\n", dev->sb_base); + + if (dev->sb_base) { + io_sethandler(dev->sb_base, 0x0004, opl3_read, NULL, NULL, + opl3_write, NULL, NULL, &dev->sb->opl); + io_sethandler(dev->sb_base + 8, 0x0002, opl3_read, NULL, NULL, + opl3_write, NULL, NULL, &dev->sb->opl); + io_sethandler(dev->sb_base + 4, 0x0002, sb_ct1745_mixer_read, NULL, NULL, + sb_ct1745_mixer_write, NULL, NULL, dev->sb); + + sb_dsp_setaddr(&dev->sb->dsp, dev->sb_base); + } +} + + +static void +cmi8x38_remap_opl(cmi8x38_t *dev) +{ + if (dev->opl_base) { + io_removehandler(dev->opl_base, 0x0004, opl3_read, NULL, NULL, + opl3_write, NULL, NULL, &dev->sb->opl); + } + + if (dev->io_regs[0x04] & 0x08) { + if (dev->type == CMEDIA_CMI8338) + dev->opl_base = 0x388; + else + dev->opl_base = opl_ports_cmi8738[dev->io_regs[0x17] & 0x03]; + } else { + dev->opl_base = 0; + } + cmi8x38_log("CMI8x38: remap_opl(%04X)\n", dev->opl_base); + + if (dev->opl_base) { + io_sethandler(dev->opl_base, 0x0004, opl3_read, NULL, NULL, + opl3_write, NULL, NULL, &dev->sb->opl); + } +} + + +static void +cmi8x38_remap_mpu(cmi8x38_t *dev) +{ + if (dev->mpu_base) + mpu401_change_addr(dev->sb->mpu, 0); + + if (dev->io_regs[0x04] & 0x04) { + if (dev->type == CMEDIA_CMI8338) + dev->mpu_base = 0x300 + ((dev->io_regs[0x17] & 0x60) >> 1); + else + dev->mpu_base = 0x330 - ((dev->io_regs[0x17] & 0x60) >> 1); + } else { + dev->mpu_base = 0; + } + cmi8x38_log("CMI8x38: remap_mpu(%04X)\n", dev->mpu_base); + + if (dev->mpu_base) + mpu401_change_addr(dev->sb->mpu, dev->mpu_base); +} + + static void cmi8x38_start_playback(cmi8x38_t *dev, uint8_t val) { @@ -248,6 +333,12 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv) case 0x04: /* Enable or disable the game port. */ gameport_remap(dev->gameport, (val & 0x02) ? 0x200 : 0); + + /* Enable or disable the legacy devices. */ + dev->io_regs[addr] = val; + cmi8x38_remap_sb(dev); + cmi8x38_remap_opl(dev); + cmi8x38_remap_mpu(dev); break; case 0x05: @@ -310,6 +401,12 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv) else if ((dev->io_regs[0x17] & 0x10) && !(val & 0x10)) pci_clear_irq(dev->slot, PCI_INTA); } + + /* Remap the legacy devices. */ + dev->io_regs[addr] = val; + cmi8x38_remap_sb(dev); + cmi8x38_remap_opl(dev); + cmi8x38_remap_mpu(dev); break; case 0x18: @@ -439,15 +536,12 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv) static void -cmi8x38_remap(cmi8x38_t *dev, uint8_t io_msb, uint8_t enable) +cmi8x38_remap(cmi8x38_t *dev) { if (dev->io_base) io_removehandler(dev->io_base, 256, cmi8x38_read, NULL, NULL, cmi8x38_write, NULL, NULL, dev); - if (enable & 0x01) - dev->io_base = io_msb << 8; - else - dev->io_base = 0; + dev->io_base = (dev->pci_regs[0x04] & 0x01) ? (dev->pci_regs[0x11] << 8) : 0; cmi8x38_log("CMI8x38: remap(%04X)\n", dev->io_base); if (dev->io_base) @@ -483,7 +577,10 @@ cmi8x38_pci_write(int func, int addr, uint8_t val, void *priv) switch (addr) { case 0x04: val &= 0x05; - cmi8x38_remap(dev, dev->pci_regs[0x11], val); + + /* Enable or disable the I/O BAR. */ + dev->pci_regs[addr] = val; + cmi8x38_remap(dev); break; case 0x05: @@ -491,7 +588,9 @@ cmi8x38_pci_write(int func, int addr, uint8_t val, void *priv) break; case 0x11: - cmi8x38_remap(dev, val, dev->pci_regs[0x04]); + /* Remap the I/O BAR. */ + dev->pci_regs[addr] = val; + cmi8x38_remap(dev); break; case 0x2c: case 0x2d: case 0x2e: case 0x2f: @@ -694,6 +793,8 @@ cmi8x38_speed_changed(void *priv) cmi8x38_t *dev = (cmi8x38_t *) priv; double freq; uint8_t dsr = dev->io_regs[0x09], freqreg = dev->io_regs[0x05] >> 2; + char buf[256]; + sprintf(buf, "%02X-%02X", dsr, freqreg); /* CMI8338 claims the frequency controls are for DAC (playback) and ADC (recording) respectively, while CMI8738 claims they're for channel 0 and channel 1. The Linux @@ -708,12 +809,14 @@ cmi8x38_speed_changed(void *priv) case 0x03: freq = 128000.0; break; default: freq = freqs[freqreg & 0x07]; break; } + sprintf(&buf[strlen(buf)], " %d:%X-%X-%.0f", i, dsr & 0x03, freqreg & 0x07, freq); dsr >>= 2; freqreg >>= 3; /* Set period. */ dev->dma[i].timer_latch = (uint64_t) ((double) TIMER_USEC * (1000000.0 / freq)); } + ui_sb_bugui(buf); } From bfcfa04013e3f2fae09bd74aa76efd468b6eec08 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Mon, 28 Feb 2022 22:45:54 -0300 Subject: [PATCH 06/62] Fix CMI8338 MPU/OPL windows being read-write --- src/sound/snd_cmi8x38.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/sound/snd_cmi8x38.c b/src/sound/snd_cmi8x38.c index bd4ea4fe6..86f9e0129 100644 --- a/src/sound/snd_cmi8x38.c +++ b/src/sound/snd_cmi8x38.c @@ -495,16 +495,12 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv) break; case 0x40 ... 0x4f: - if (dev->type == CMEDIA_CMI8338) - break; - else + if (dev->type != CMEDIA_CMI8338) mpu401_write(addr, val, dev->sb->mpu); return; case 0x50 ... 0x5f: - if (dev->type == CMEDIA_CMI8338) - break; - else + if (dev->type != CMEDIA_CMI8338) opl3_write(addr, val, &dev->sb->opl); return; From bd63c3d66a8a76999cd15565d588c66db856eed6 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Tue, 1 Mar 2022 21:42:43 -0300 Subject: [PATCH 07/62] Add multi-channel DMA to CMI8x38 --- src/sound/snd_cmi8x38.c | 122 +++++++++++++++++++++++++++++++--------- 1 file changed, 96 insertions(+), 26 deletions(-) diff --git a/src/sound/snd_cmi8x38.c b/src/sound/snd_cmi8x38.c index 86f9e0129..b74f2dd70 100644 --- a/src/sound/snd_cmi8x38.c +++ b/src/sound/snd_cmi8x38.c @@ -38,17 +38,18 @@ enum { }; typedef struct { - uint8_t id, reg, always_run, playback_enabled; + uint8_t id, reg, always_run, playback_enabled, channels; struct _cmi8x38_ *dev; uint32_t sample_ptr, fifo_pos, fifo_end; int32_t frame_count_dma, frame_count_fragment, sample_count_out; - uint8_t fifo[32], restart; + uint8_t fifo[256], restart; - int16_t out_l, out_r; + int16_t out_fl, out_fr, out_rl, out_rr, out_c, out_lfe; int vol_l, vol_r, pos; int32_t buffer[SOUNDBUFLEN * 2]; uint64_t timer_latch; + double dma_latch; pc_timer_t dma_timer, poll_timer; } cmi8x38_dma_t; @@ -356,6 +357,8 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv) if (dev->type == CMEDIA_CMI8338) return; #endif + /* Update sample rate. */ + dev->io_regs[addr] = val; cmi8x38_speed_changed(dev); break; @@ -365,8 +368,14 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv) else val &= 0xe0; - if (addr == 0x0a) + if (addr == 0x0a) { + /* Set PCI latency timer if requested. */ dev->pci_regs[0x0d] = (val & 0x80) ? 0x48 : 0x20; /* clearing SETLAT48 is undefined */ + } else { + /* Update channel count. */ + dev->io_regs[addr] = val; + cmi8x38_speed_changed(dev); + } break; case 0x0e: @@ -384,6 +393,10 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv) return; else val &= 0xf0; + + /* Update channel count. */ + dev->io_regs[addr] = val; + cmi8x38_speed_changed(dev); break; case 0x16: @@ -617,8 +630,8 @@ static void cmi8x38_update(cmi8x38_t *dev, cmi8x38_dma_t *dma) { sb_ct1745_mixer_t *mixer = &dev->sb->mixer_sb16; - int32_t l = (dma->out_l * mixer->voice_l) * mixer->master_l, - r = (dma->out_r * mixer->voice_r) * mixer->master_r; + int32_t l = (dma->out_fl * mixer->voice_l) * mixer->master_l, + r = (dma->out_fr * mixer->voice_r) * mixer->master_r; for (; dma->pos < sound_pos_global; dma->pos++) { dma->buffer[dma->pos*2] = l; @@ -641,7 +654,7 @@ cmi8x38_dma_process(void *priv) } /* Schedule next run. */ - timer_on_auto(&dma->dma_timer, 10.0); + timer_on_auto(&dma->dma_timer, dma->dma_latch); /* Process DMA if it's active, and the FIFO has room or is disabled. */ uint8_t dma_status = dev->io_regs[0x00] >> dma->id; @@ -720,7 +733,7 @@ cmi8x38_poll(void *priv) switch ((dev->io_regs[0x08] >> (dma->id << 1)) & 0x03) { case 0x00: /* Mono, 8-bit PCM */ if ((dma->fifo_end - dma->fifo_pos) >= 1) { - dma->out_l = dma->out_r = (dma->fifo[dma->fifo_pos++ & (sizeof(dma->fifo) - 1)] ^ 0x80) << 8; + dma->out_fl = dma->out_fr = (dma->fifo[dma->fifo_pos++ & (sizeof(dma->fifo) - 1)] ^ 0x80) << 8; dma->sample_count_out--; return; } @@ -728,8 +741,8 @@ cmi8x38_poll(void *priv) case 0x01: /* Stereo, 8-bit PCM */ if ((dma->fifo_end - dma->fifo_pos) >= 2) { - dma->out_l = (dma->fifo[dma->fifo_pos++ & (sizeof(dma->fifo) - 1)] ^ 0x80) << 8; - dma->out_r = (dma->fifo[dma->fifo_pos++ & (sizeof(dma->fifo) - 1)] ^ 0x80) << 8; + dma->out_fl = (dma->fifo[dma->fifo_pos++ & (sizeof(dma->fifo) - 1)] ^ 0x80) << 8; + dma->out_fr = (dma->fifo[dma->fifo_pos++ & (sizeof(dma->fifo) - 1)] ^ 0x80) << 8; dma->sample_count_out -= 2; return; } @@ -737,7 +750,7 @@ cmi8x38_poll(void *priv) case 0x02: /* Mono, 16-bit PCM */ if ((dma->fifo_end - dma->fifo_pos) >= 2) { - dma->out_l = dma->out_r = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); + dma->out_fl = dma->out_fr = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); dma->fifo_pos += 2; dma->sample_count_out -= 2; return; @@ -745,19 +758,59 @@ cmi8x38_poll(void *priv) break; case 0x03: /* Stereo, 16-bit PCM */ - if ((dma->fifo_end - dma->fifo_pos) >= 4) { - dma->out_l = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); - dma->fifo_pos += 2; - dma->out_r = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); - dma->fifo_pos += 2; - dma->sample_count_out -= 4; - return; + switch (dma->channels) { + case 2: + if ((dma->fifo_end - dma->fifo_pos) >= 4) { + dma->out_fl = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); + dma->fifo_pos += 2; + dma->out_fr = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); + dma->fifo_pos += 2; + dma->out_c = dma->out_lfe = dma->out_rl = dma->out_rr = 0; + dma->sample_count_out -= 4; + return; + } + break; + + case 4: + if ((dma->fifo_end - dma->fifo_pos) >= 8) { + dma->out_fl = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); + dma->fifo_pos += 2; + dma->out_fr = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); + dma->fifo_pos += 2; + dma->out_rl = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); + dma->fifo_pos += 2; + dma->out_rr = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); + dma->fifo_pos += 2; + dma->out_c = dma->out_lfe = 0; + dma->sample_count_out -= 8; + return; + } + break; + + case 6: + if ((dma->fifo_end - dma->fifo_pos) >= 12) { + dma->out_fl = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); + dma->fifo_pos += 2; + dma->out_fr = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); + dma->fifo_pos += 2; + dma->out_c = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); + dma->fifo_pos += 2; + dma->out_lfe = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); + dma->fifo_pos += 2; + dma->out_rl = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); + dma->fifo_pos += 2; + dma->out_rr = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); + dma->fifo_pos += 2; + dma->sample_count_out -= 12; + return; + } + break; } break; } /* Feed silence if the FIFO is empty. */ - dma->out_l = dma->out_r = 0; + dma->out_fl = dma->out_fr = 0; } @@ -788,9 +841,10 @@ cmi8x38_speed_changed(void *priv) { cmi8x38_t *dev = (cmi8x38_t *) priv; double freq; - uint8_t dsr = dev->io_regs[0x09], freqreg = dev->io_regs[0x05] >> 2; + uint8_t dsr = dev->io_regs[0x09], freqreg = dev->io_regs[0x05] >> 2, + chfmt45 = dev->io_regs[0x0b], chfmt6 = dev->io_regs[0x15]; char buf[256]; - sprintf(buf, "%02X-%02X", dsr, freqreg); + sprintf(buf, "%02X-%02X-%02X-%02X", dsr, freqreg, chfmt45, chfmt6); /* CMI8338 claims the frequency controls are for DAC (playback) and ADC (recording) respectively, while CMI8738 claims they're for channel 0 and channel 1. The Linux @@ -805,12 +859,28 @@ cmi8x38_speed_changed(void *priv) case 0x03: freq = 128000.0; break; default: freq = freqs[freqreg & 0x07]; break; } - sprintf(&buf[strlen(buf)], " %d:%X-%X-%.0f", i, dsr & 0x03, freqreg & 0x07, freq); + + /* Set polling timer period. */ + freq = 1000000.0 / freq; + dev->dma[i].timer_latch = (uint64_t) ((double) TIMER_USEC * freq); + + /* Calculate channel count and set DMA timer period. */ + if (dev->type == CMEDIA_CMI8338) { +stereo: dev->dma[i].channels = 2; + } else { + if (chfmt45 & 0x80) + dev->dma[i].channels = (chfmt6 & 0x80) ? 6 : 5; + else if (chfmt45 & 0x20) + dev->dma[i].channels = 4; + else + goto stereo; + } + dev->dma[i].dma_latch = freq / dev->dma[i].channels; /* frequency / approximately(dwords * 2) */ + + /* Shift sample rate configuration registers. */ + sprintf(&buf[strlen(buf)], " %d:%X-%X-%.0f-%dC", i, dsr & 0x03, freqreg & 0x07, 1000000.0 / freq, dev->dma[i].channels); dsr >>= 2; freqreg >>= 3; - - /* Set period. */ - dev->dma[i].timer_latch = (uint64_t) ((double) TIMER_USEC * (1000000.0 / freq)); } ui_sb_bugui(buf); } @@ -867,7 +937,7 @@ cmi8x38_init(const device_t *info) /* Set the chip type. */ cmi8x38_log("CMI8x38: init(%03X)\n", info->local); - dev->type = info->local; + dev->type = info->local & 0xff; /* Initialize Sound Blaster 16. */ dev->sb = device_add_inst(&sb_16_compat_device, 1); From 8633a7c533b6cf695096d8f8539c3d0aa4046cc3 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Sun, 6 Mar 2022 01:30:19 -0300 Subject: [PATCH 08/62] Jenkins: Enable FAudio on Linux --- .ci/AppImageBuilder.yml | 1 + .ci/build.sh | 55 +++++++++++++++++++++++++++-------------- 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/.ci/AppImageBuilder.yml b/.ci/AppImageBuilder.yml index fb2482491..105ef0dc9 100644 --- a/.ci/AppImageBuilder.yml +++ b/.ci/AppImageBuilder.yml @@ -61,6 +61,7 @@ AppDir: - usr/bin - usr/include - usr/lib/*/libasound.so.* + - usr/lib/*.a - usr/lib/cmake - usr/lib/pkgconfig - usr/sbin diff --git a/.ci/build.sh b/.ci/build.sh index 493f42789..04c08d194 100644 --- a/.ci/build.sh +++ b/.ci/build.sh @@ -242,7 +242,7 @@ else # ...and the ones we do want listed. Non-dev packages fill missing spots on the list. libpkgs="" longest_libpkg=0 - for pkg in libc6-dev libstdc++6 libopenal-dev libfreetype6-dev libx11-dev libsdl2-dev libpng-dev librtmidi-dev qtdeclarative5-dev libwayland-dev libevdev-dev libglib2.0-dev libslirp-dev + for pkg in libc6-dev libstdc++6 libopenal-dev libfreetype6-dev libx11-dev libsdl2-dev libpng-dev librtmidi-dev qtdeclarative5-dev libwayland-dev libevdev-dev libglib2.0-dev libslirp-dev libfaudio-dev libaudio-dev libjack-jackd2-dev libpipewire-0.3-dev libsamplerate0-dev libsndio-dev do libpkgs="$libpkgs $pkg:$arch_deb" length=$(echo -n $pkg | sed 's/-dev$//' | sed "s/qtdeclarative/qt/" | wc -c) @@ -298,9 +298,6 @@ EOF # Link against the system libslirp instead of compiling ours. cmake_flags_extra="$cmake_flags_extra -D SLIRP_EXTERNAL=ON" - - # Use OpenAL for Linux builds before FAudio builds are set up. - cmake_flags_extra="$cmake_flags_extra -D OPENAL=ON" fi # Clean workspace. @@ -432,19 +429,41 @@ then else cwd_root=$(pwd) - # Build openal-soft 1.21.1 manually to fix audio issues. This is a temporary - # workaround until a newer version of openal-soft trickles down to Debian repos. - if [ -d "openal-soft-1.21.1" ] + if grep -q "OPENAL:BOOL=ON" build/CMakeCache.txt then - rm -rf openal-soft-1.21.1/build/* + # Build openal-soft 1.21.1 manually to fix audio issues. This is a temporary + # workaround until a newer version of openal-soft trickles down to Debian repos. + if [ -d "openal-soft-1.21.1" ] + then + rm -rf openal-soft-1.21.1/build/* + else + wget -qO - https://github.com/kcat/openal-soft/archive/refs/tags/1.21.1.tar.gz | tar zxf - + fi + cd openal-soft-1.21.1/build + [ -e Makefile ] && make clean + cmake -G "Unix Makefiles" -D "CMAKE_TOOLCHAIN_FILE=$cwd_root/toolchain.cmake" -D "CMAKE_INSTALL_PREFIX=$cwd_root/archive_tmp/usr" .. + make -j$(nproc) install || exit 99 + cd "$cwd_root" + + # Build SDL2 without sound systems. + sdl_ss=OFF else - wget -qO - https://github.com/kcat/openal-soft/archive/refs/tags/1.21.1.tar.gz | tar zxf - + # Build FAudio 22.03 manually to remove the dependency on GStreamer. + if [ -d "FAudio-22.03" ] + then + rm -rf FAudio-22.03/build + else + wget -qO - https://github.com/FNA-XNA/FAudio/archive/refs/tags/22.03.tar.gz | tar zxf - + fi + mkdir FAudio-22.03/build + cd FAudio-22.03/build + cmake -G "Unix Makefiles" -D "CMAKE_TOOLCHAIN_FILE=$cwd_root/toolchain.cmake" -D "CMAKE_INSTALL_PREFIX=$cwd_root/archive_tmp/usr" .. + make -j$(nproc) install || exit 99 + cd "$cwd_root" + + # Build SDL2 with sound systems. + sdl_ss=ON fi - cd openal-soft-1.21.1/build - [ -e Makefile ] && make clean - cmake -G "Unix Makefiles" -D "CMAKE_TOOLCHAIN_FILE=$cwd_root/toolchain.cmake" -D "CMAKE_INSTALL_PREFIX=$cwd_root/archive_tmp/usr" .. - make -j$(nproc) install || exit 99 - cd "$cwd_root" # Build rtmidi without JACK support to remove the dependency on libjack. if [ -d "rtmidi-4.0.0" ] @@ -468,10 +487,10 @@ else rm -rf sdlbuild mkdir sdlbuild cd sdlbuild - cmake -G "Unix Makefiles" -D SDL_DISKAUDIO=OFF -D SDL_DIRECTFB_SHARED=OFF -D SDL_OPENGL=OFF -D SDL_OPENGLES=OFF -D SDL_OSS=OFF -D SDL_ALSA=OFF \ - -D SDL_ALSA_SHARED=OFF -D SDL_JACK=OFF -D SDL_JACK_SHARED=OFF -D SDL_ESD=OFF -D SDL_ESD_SHARED=OFF -D SDL_PIPEWIRE=OFF -D SDL_PIPEWIRE_SHARED=OFF \ - -D SDL_PULSEAUDIO=OFF -D SDL_PULSEAUDIO_SHARED=OFF -D SDL_ARTS=OFF -D SDL_ARTS_SHARED=OFF -D SDL_NAS=OFF -D SDL_NAS_SHARED=OFF -D SDL_SNDIO=OFF \ - -D SDL_SNDIO_SHARED=OFF -D SDL_FUSIONSOUND=OFF -D SDL_FUSIONSOUND_SHARED=OFF -D SDL_LIBSAMPLERATE=OFF -D SDL_LIBSAMPLERATE_SHARED=OFF -D SDL_X11=OFF \ + cmake -G "Unix Makefiles" -D SDL_DISKAUDIO=OFF -D SDL_DIRECTFB_SHARED=OFF -D SDL_OPENGL=OFF -D SDL_OPENGLES=OFF -D SDL_OSS=OFF -D SDL_ALSA=$sdl_ss \ + -D SDL_ALSA_SHARED=$sdl_ss -D SDL_JACK=$sdl_ss -D SDL_JACK_SHARED=$sdl_ss -D SDL_ESD=OFF -D SDL_ESD_SHARED=OFF -D SDL_PIPEWIRE=$sdl_ss -D SDL_PIPEWIRE_SHARED=$sdl_ss \ + -D SDL_PULSEAUDIO=$sdl_ss -D SDL_PULSEAUDIO_SHARED=$sdl_ss -D SDL_ARTS=OFF -D SDL_ARTS_SHARED=OFF -D SDL_NAS=$sdl_ss -D SDL_NAS_SHARED=$sdl_ss -D SDL_SNDIO=$sdl_ss \ + -D SDL_SNDIO_SHARED=$sdl_ss -D SDL_FUSIONSOUND=OFF -D SDL_FUSIONSOUND_SHARED=OFF -D SDL_LIBSAMPLERATE=$sdl_ss -D SDL_LIBSAMPLERATE_SHARED=$sdl_ss -D SDL_X11=OFF \ -D SDL_X11_SHARED=OFF -D SDL_WAYLAND=OFF -D SDL_WAYLAND_SHARED=OFF -D SDL_WAYLAND_LIBDECOR=OFF -D SDL_WAYLAND_LIBDECOR_SHARED=OFF \ -D SDL_WAYLAND_QT_TOUCH=OFF -D SDL_RPI=OFF -D SDL_VIVANTE=OFF -D SDL_VULKAN=OFF -D SDL_KMSDRM=OFF -D SDL_KMSDRM_SHARED=OFF -D SDL_OFFSCREEN=OFF \ -D SDL_HIDAPI_JOYSTICK=ON -D SDL_VIRTUAL_JOYSTICK=ON -D SDL_SHARED=ON -D SDL_STATIC=OFF -S "$cwd_root/SDL2-2.0.20" \ From 9bbf31a2b42b0bb144369b34e1f1d22b6ba209b5 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Sun, 6 Mar 2022 09:39:52 -0300 Subject: [PATCH 09/62] Remove extraneous logging --- src/device/isapnp.c | 2 +- src/sio/sio_um8669f.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/device/isapnp.c b/src/device/isapnp.c index 327c8c581..45752a3ca 100644 --- a/src/device/isapnp.c +++ b/src/device/isapnp.c @@ -53,7 +53,7 @@ static const uint8_t pnp_init_key[32] = { 0x6A, 0xB5, 0xDA, 0xED, 0xF6, 0xFB, 0x 0xE8, 0x74, 0x3A, 0x9D, 0xCE, 0xE7, 0x73, 0x39 }; static const device_t isapnp_device; -#define ENABLE_ISAPNP_LOG 1 + #ifdef ENABLE_ISAPNP_LOG int isapnp_do_log = ENABLE_ISAPNP_LOG; diff --git a/src/sio/sio_um8669f.c b/src/sio/sio_um8669f.c index 8151dac97..aeef3a51e 100644 --- a/src/sio/sio_um8669f.c +++ b/src/sio/sio_um8669f.c @@ -94,7 +94,7 @@ static const isapnp_device_config_t um8669f_pnp_defaults[] = { } }; -#define ENABLE_UM8669F_LOG 1 + #ifdef ENABLE_UM8669F_LOG int um8669f_do_log = ENABLE_UM8669F_LOG; From 219129f97ec9262655711eef0aefcad6b510972b Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Sun, 6 Mar 2022 19:59:14 -0300 Subject: [PATCH 10/62] CMI8x38: Implement Sound Blaster emulation --- .ci/Jenkinsfile | 2 +- src/dma.c | 2 +- src/include/86box/dma.h | 1 + src/include/86box/snd_sb_dsp.h | 17 +- src/io.c | 6 +- src/sound/snd_cmi8x38.c | 465 +++++++++++++++++++++++++++------ src/sound/snd_sb_dsp.c | 150 +++++++---- 7 files changed, 502 insertions(+), 141 deletions(-) diff --git a/.ci/Jenkinsfile b/.ci/Jenkinsfile index d1713bb64..00532dc58 100644 --- a/.ci/Jenkinsfile +++ b/.ci/Jenkinsfile @@ -25,7 +25,7 @@ def osArchs = [ ] def osFlags = [ - 'Windows': '', + 'Windows': '-D QT=ON', 'Linux': '-D QT=ON' ] diff --git a/src/dma.c b/src/dma.c index 876c34356..a995660a5 100644 --- a/src/dma.c +++ b/src/dma.c @@ -35,11 +35,11 @@ dma_t dma[8]; uint8_t dma_e; +uint8_t dma_m; static uint8_t dmaregs[3][16]; static int dma_wp[2]; -static uint8_t dma_m; static uint8_t dma_stat; static uint8_t dma_stat_rq; static uint8_t dma_stat_rq_pc; diff --git a/src/include/86box/dma.h b/src/include/86box/dma.h index fc6a0b388..585d77e95 100644 --- a/src/include/86box/dma.h +++ b/src/include/86box/dma.h @@ -66,6 +66,7 @@ typedef struct { extern dma_t dma[8]; extern uint8_t dma_e; +extern uint8_t dma_m; extern void dma_init(void); diff --git a/src/include/86box/snd_sb_dsp.h b/src/include/86box/snd_sb_dsp.h index eff16373d..aeceb62f1 100644 --- a/src/include/86box/snd_sb_dsp.h +++ b/src/include/86box/snd_sb_dsp.h @@ -20,6 +20,11 @@ typedef struct sb_dsp_t { int sb_16_length, sb_16_format, sb_16_autoinit, sb_16_pause, sb_16_enable, sb_16_autolen, sb_16_output; int sb_16_dmanum; int sb_pausetime; + int (*dma_readb)(void *priv), + (*dma_readw)(void *priv), + (*dma_writeb)(void *priv, uint8_t val), + (*dma_writew)(void *priv, uint16_t val); + void *dma_priv; uint8_t sb_read_data[256]; int sb_read_wp, sb_read_rp; @@ -36,6 +41,8 @@ typedef struct sb_dsp_t { int midi_in_timestamp; int sb_irqnum; + void (*irq_update)(void *priv, int set), + *irq_priv; uint8_t sbe2; int sbe2count; @@ -53,7 +60,7 @@ typedef struct sb_dsp_t { int sbdacpos; - int sbleftright; + int sbleftright, sbleftright_default; int sbreset; uint8_t sbreaddat; @@ -123,4 +130,12 @@ void sb_dsp_set_stereo(sb_dsp_t *dsp, int stereo); void sb_dsp_update(sb_dsp_t *dsp); void sb_update_mask(sb_dsp_t *dsp, int irqm8, int irqm16, int irqm401); +void sb_dsp_irq_attach(sb_dsp_t *dsp, void (*irq_update)(void *priv, int set), void *priv); +void sb_dsp_dma_attach(sb_dsp_t *dsp, + int (*dma_readb)(void *priv), + int (*dma_readw)(void *priv), + int (*dma_writeb)(void *priv, uint8_t val), + int (*dma_writew)(void *priv, uint16_t val), + void *priv); + #endif /* SOUND_SND_SB_DSP_H */ diff --git a/src/io.c b/src/io.c index 9f9df62a4..268305e1a 100644 --- a/src/io.c +++ b/src/io.c @@ -673,7 +673,7 @@ io_trap_remap(void *handle, int enable, uint16_t addr, uint16_t size) trap->base, trap->base + trap->size - 1, trap->enable, addr, addr + size - 1, enable); /* Remove old I/O mapping. */ - if (trap->enable && trap->base && trap->size) { + if (trap->enable && trap->size) { io_removehandler(trap->base, trap->size, io_trap_readb, io_trap_readw, io_trap_readl, io_trap_writeb, io_trap_writew, io_trap_writel, @@ -686,8 +686,8 @@ io_trap_remap(void *handle, int enable, uint16_t addr, uint16_t size) trap->size = size; /* Add new I/O mapping. */ - if (trap->enable && trap->base && trap->size) { - io_sethandler(trap->base, trap->size, + if (trap->enable && trap->size) { + io_sethandler(trap->base, trap->size, io_trap_readb, io_trap_readw, io_trap_readl, io_trap_writeb, io_trap_writew, io_trap_writel, trap); diff --git a/src/sound/snd_cmi8x38.c b/src/sound/snd_cmi8x38.c index 2be5c6bfc..f8623eb96 100644 --- a/src/sound/snd_cmi8x38.c +++ b/src/sound/snd_cmi8x38.c @@ -26,10 +26,13 @@ #include <86box/mem.h> #include <86box/pic.h> #include <86box/timer.h> +#include <86box/dma.h> #include <86box/pci.h> #include <86box/sound.h> #include <86box/snd_sb.h> +#include <86box/snd_sb_dsp.h> #include <86box/gameport.h> +#include <86box/nmi.h> #include <86box/ui.h> @@ -43,6 +46,14 @@ enum { CMEDIA_CMI8738_6CH = 0x080011 /* chip version 055 with 6-channel output */ }; +enum { + TRAP_DMA = 0, + TRAP_PIC, + TRAP_OPL, + TRAP_MPU, + TRAP_MAX +}; + typedef struct { uint8_t id, reg, always_run, playback_enabled, channels; struct _cmi8x38_ *dev; @@ -63,17 +74,21 @@ typedef struct { typedef struct _cmi8x38_ { uint32_t type; uint16_t io_base, sb_base, opl_base, mpu_base; - uint8_t pci_regs[256], io_regs[256], mixer_ext_regs[16]; - int slot; + uint8_t pci_regs[256], io_regs[256]; + int slot, sb_irq; sb_t *sb; - void *gameport; + void *gameport, *io_traps[TRAP_MAX]; + cmi8x38_dma_t dma[2]; + uint16_t tdma_base_addr, tdma_base_count; + uint8_t prev_mask; + int tdma_last_8, tdma_last_16, tdma_mask; int master_vol_l, master_vol_r, cd_vol_l, cd_vol_r; } cmi8x38_t; -#define ENABLE_CMI8X38_LOG 1 + #ifdef ENABLE_CMI8X38_LOG int cmi8x38_do_log = ENABLE_CMI8X38_LOG; @@ -104,7 +119,7 @@ static void cmi8x38_update_irqs(cmi8x38_t *dev) { /* Calculate and use the INTR flag. */ - if (*((uint32_t *) &dev->io_regs[0x10]) & 0x0401c003) { + if ((*((uint32_t *) &dev->io_regs[0x10]) & 0x0401c003) || dev->sb_irq) { dev->io_regs[0x13] |= 0x80; pci_set_irq(dev->slot, PCI_INTA); cmi8x38_log("CMI8x38: Raising IRQ\n"); @@ -135,6 +150,290 @@ cmi8x38_mpu_irq_pending(void *priv) } +static void +cmi8x38_sb_irq_update(void *priv, int set) +{ + cmi8x38_t *dev = (cmi8x38_t *) priv; + dev->sb_irq = set; + cmi8x38_update_irqs(dev); +} + + +static int +cmi8x38_sb_dma_post(cmi8x38_t *dev, uint16_t *addr, uint16_t *count, int channel) +{ + /* Increment address and decrement count. */ + *addr += 1; + *count -= 1; + + /* Handle end of DMA. */ + if (*count == 0xffff) { + if (dma[channel].mode & 0x10) { /* auto-init */ + /* Restart TDMA. */ + *addr = dev->tdma_base_addr; + *count = dev->tdma_base_count; + cmi8x38_log("CMI8x38: Restarting TDMA on DMA %d with addr %08X count %04X\n", channel, (dma[channel].ab & 0xffff0000) | *addr, *count); + } else { + /* Mask TDMA. */ + dev->tdma_mask |= 1 << channel; + } + return DMA_OVER; + } + return 0; +} + + +static int +cmi8x38_sb_dma_readb(void *priv) +{ + cmi8x38_t *dev = (cmi8x38_t *) priv; + + /* Stop if the DMA channel is invalid or if TDMA is masked. */ + int channel = dev->sb->dsp.sb_8_dmanum; + if ((channel < 0) || (dev->tdma_mask & (1 << channel))) + return DMA_NODATA; + + /* Get 16-bit address and count registers. */ + uint16_t *addr = (uint16_t *) &dev->io_regs[0x1c], + *count = (uint16_t *) &dev->io_regs[0x1e]; + + /* Read data. */ + int ret = mem_readb_phys((dma[channel].ab & 0xffff0000) | *addr); + + /* Handle address, count and end. */ + ret |= cmi8x38_sb_dma_post(dev, addr, count, channel); + + return ret; +} + + +static int +cmi8x38_sb_dma_readw(void *priv) +{ + cmi8x38_t *dev = (cmi8x38_t *) priv; + + /* Stop if the DMA channel is invalid or if TDMA is masked. */ + int channel = dev->sb->dsp.sb_16_dmanum; + if ((channel < 0) || (dev->tdma_mask & (1 << channel))) + return DMA_NODATA; + + /* Get 16-bit address and count registers. */ + uint16_t *addr = (uint16_t *) &dev->io_regs[0x1c], + *count = (uint16_t *) &dev->io_regs[0x1e]; + + /* Read data. */ + int ret = mem_readw_phys((dma[channel].ab & 0xfffe0000) | ((*addr) << 1)); + + /* Handle address, count and end. */ + ret |= cmi8x38_sb_dma_post(dev, addr, count, channel); + + return ret; +} + + +static int +cmi8x38_sb_dma_writeb(void *priv, uint8_t val) +{ + cmi8x38_t *dev = (cmi8x38_t *) priv; + + /* Stop if the DMA channel is invalid or if TDMA is masked. */ + int channel = dev->sb->dsp.sb_8_dmanum; + if ((channel < 0) || (dev->tdma_mask & (1 << channel))) + return 1; + + /* Get 16-bit address and count registers. */ + uint16_t *addr = (uint16_t *) &dev->io_regs[0x1c], + *count = (uint16_t *) &dev->io_regs[0x1e]; + + /* Write data. */ + mem_writeb_phys((dma[channel].ab & 0xffff0000) | *addr, val); + + /* Handle address, count and end. */ + cmi8x38_sb_dma_post(dev, addr, count, channel); + + return 0; +} + + +static int +cmi8x38_sb_dma_writew(void *priv, uint16_t val) +{ + cmi8x38_t *dev = (cmi8x38_t *) priv; + + /* Stop if the DMA channel is invalid or if TDMA is masked. */ + int channel = dev->sb->dsp.sb_16_dmanum; + if ((channel < 0) || (dev->tdma_mask & (1 << channel))) + return 1; + + /* Get 16-bit address and count registers. */ + uint16_t *addr = (uint16_t *) &dev->io_regs[0x1c], + *count = (uint16_t *) &dev->io_regs[0x1e]; + + /* Write data. */ + mem_writew_phys((dma[channel].ab & 0xfffe0000) | ((*addr) << 1), val); + + /* Handle address, count and end. */ + cmi8x38_sb_dma_post(dev, addr, count, channel); + + return 0; +} + + +static void +cmi8x38_dma_write(uint16_t addr, uint8_t val, void *priv) +{ + cmi8x38_t *dev = (cmi8x38_t *) priv; + + /* Keep track of the last DMA channel written to. */ + uint8_t channel; + if (addr < 0x08) { + channel = addr >> 1; + dev->tdma_last_8 = channel; + } else { + channel = 4 | ((addr >> 2) & 3); + dev->tdma_last_16 = channel; + } + + /* Stop if not autodetecting. See note on cmi8x38_write(0x27). */ + if (!(dev->io_regs[0x27] & 0x01)) + return; + + /* Write TDMA registers if this is a TDMA channel. */ + if ((channel == dev->sb->dsp.sb_8_dmanum) || (channel == dev->sb->dsp.sb_16_dmanum)) { + /* Write base address and count. */ + uint16_t *addr = (uint16_t *) &dev->io_regs[0x1c], + *count = (uint16_t *) &dev->io_regs[0x1e]; + *addr = dev->tdma_base_addr = dma[channel].ab >> !!(channel & 4); + *count = dev->tdma_base_count = dma[channel].cb; + cmi8x38_log("CMI8x38: Starting TDMA on DMA %d with addr %08X count %04X\n", channel, (dma[channel].ab & 0xffff0000) | *addr, *count); + + /* Set high channel flag. */ + if (channel & 4) + dev->io_regs[0x10] |= 0x20; + else + dev->io_regs[0x10] &= ~0x20; + } +} + +static void +cmi8x38_dma_mask_write(uint16_t addr, uint8_t val, void *priv) +{ + cmi8x38_t *dev = (cmi8x38_t *) priv; + + /* Stop if not autodetecting. See note on cmi8x38_write(0x27). */ + if (!(dev->io_regs[0x27] & 0x01)) + return; + + /* Unmask TDMA on DMA unmasking edge. */ + if ((dev->sb->dsp.sb_8_dmanum >= 0) && (dev->prev_mask & (1 << dev->sb->dsp.sb_8_dmanum)) && !(dma_m & (1 << dev->sb->dsp.sb_8_dmanum))) + dev->tdma_mask &= ~(1 << dev->sb->dsp.sb_8_dmanum); + else if ((dev->sb->dsp.sb_16_dmanum >= 0) && (dev->prev_mask & (1 << dev->sb->dsp.sb_16_dmanum)) && !(dma_m & (1 << dev->sb->dsp.sb_16_dmanum))) + dev->tdma_mask &= ~(1 << dev->sb->dsp.sb_16_dmanum); + dev->prev_mask = dma_m; +} + + +static void +cmi8338_io_trap(int size, uint16_t addr, uint8_t write, uint8_t val, void *priv) +{ + cmi8x38_t *dev = (cmi8x38_t *) priv; + +#ifdef ENABLE_CMI8X38_LOG + if (write) + cmi8x38_log("CMI8x38: cmi8338_io_trap(%04X, %02X)\n", addr, val); + else + cmi8x38_log("CMI8x38: cmi8338_io_trap(%04X)\n", addr); +#endif + + /* Weird offsets, it's best to just treat the register as a big dword. */ + uint32_t *lcs = (uint32_t *) &dev->io_regs[0x14]; + *lcs &= ~0x0003dff0; + *lcs |= (addr & 0x0f) << 14; + if (write) + *lcs |= 0x1000 | (val << 4); + + /* Raise NMI. */ + nmi = 1; +} + + +static uint8_t +cmi8x38_sb_mixer_read(uint16_t addr, void *priv) +{ + cmi8x38_t *dev = (cmi8x38_t *) priv; + sb_ct1745_mixer_t *mixer = &dev->sb->mixer_sb16; + uint8_t ret = sb_ct1745_mixer_read(addr, dev->sb); + + if (addr & 1) { + if ((mixer->index == 0x0e) || (mixer->index >= 0xf0)) + ret = mixer->regs[mixer->index]; + cmi8x38_log("CMI8x38: sb_mixer_read(1, %02X) = %02X\n", mixer->index, ret); + } else { + cmi8x38_log("CMI8x38: sb_mixer_read(0) = %02X\n", ret); + } + + return ret; +} + + +static void +cmi8x38_sb_mixer_write(uint16_t addr, uint8_t val, void *priv) +{ + cmi8x38_t *dev = (cmi8x38_t *) priv; + sb_ct1745_mixer_t *mixer = &dev->sb->mixer_sb16; + + /* Our clone mixer has a few differences. */ + if (addr & 1) { + cmi8x38_log("CMI8x38: sb_mixer_write(1, %02X, %02X)\n", mixer->index, val); + + switch (mixer->index) { + /* Reset interleaved stereo flag for SBPro mode. */ + case 0x00: + mixer->regs[0x0e] = 0x00; + break; + + /* No dynamic IRQ and DMA assignment. */ + case 0x80: case 0x81: + return; + + /* Some extended registers beyond those accepted by the CT1745. */ + case 0xf0: + if (dev->type == CMEDIA_CMI8338) + val &= 0xfe; + mixer->regs[mixer->index] = val; + return; + + case 0xf8 ... 0xff: + if (dev->type == CMEDIA_CMI8338) + mixer->regs[mixer->index] = val; + /* fall-through */ + + case 0xf1 ... 0xf7: + return; + } + + sb_ct1745_mixer_write(addr, val, dev->sb); + + /* No [3F:47] controls. */ + mixer->input_gain_L = 0; + mixer->input_gain_R = 0; + mixer->output_gain_L = (double) 1.0; + mixer->output_gain_R = (double) 1.0; + mixer->bass_l = 8; + mixer->bass_r = 8; + mixer->treble_l = 8; + mixer->treble_r = 8; + + /* Check interleaved stereo flag for SBPro mode. */ + if ((mixer->index == 0x00) || (mixer->index == 0x0e)) + sb_dsp_set_stereo(&dev->sb->dsp, mixer->regs[0x0e] & 2); + } else { + cmi8x38_log("CMI8x38: sb_mixer_write(0, %02X)\n", val); + sb_ct1745_mixer_write(addr, val, dev->sb); + } +} + + static void cmi8x38_remap_sb(cmi8x38_t *dev) { @@ -143,21 +442,19 @@ cmi8x38_remap_sb(cmi8x38_t *dev) opl3_write, NULL, NULL, &dev->sb->opl); io_removehandler(dev->sb_base + 8, 0x0002, opl3_read, NULL, NULL, opl3_write, NULL, NULL, &dev->sb->opl); - io_removehandler(dev->sb_base + 4, 0x0002, sb_ct1745_mixer_read, NULL, NULL, - sb_ct1745_mixer_write, NULL, NULL, dev->sb); + io_removehandler(dev->sb_base + 4, 0x0002, cmi8x38_sb_mixer_read, NULL, NULL, + cmi8x38_sb_mixer_write, NULL, NULL, dev); sb_dsp_setaddr(&dev->sb->dsp, 0); } - if (dev->io_regs[0x04] & 0x08) { - dev->sb_base = 0x220; - if (dev->type == CMEDIA_CMI8338) - dev->sb_base += (dev->io_regs[0x17] & 0x80) >> 2; - else - dev->sb_base += (dev->io_regs[0x17] & 0x0c) << 3; - } else { + dev->sb_base = 0x220; + if (dev->type == CMEDIA_CMI8338) + dev->sb_base += (dev->io_regs[0x17] & 0x80) >> 2; + else + dev->sb_base += (dev->io_regs[0x17] & 0x0c) << 3; + if (!(dev->io_regs[0x04] & 0x08)) dev->sb_base = 0; - } cmi8x38_log("CMI8x38: remap_sb(%04X)\n", dev->sb_base); if (dev->sb_base) { @@ -165,8 +462,8 @@ cmi8x38_remap_sb(cmi8x38_t *dev) opl3_write, NULL, NULL, &dev->sb->opl); io_sethandler(dev->sb_base + 8, 0x0002, opl3_read, NULL, NULL, opl3_write, NULL, NULL, &dev->sb->opl); - io_sethandler(dev->sb_base + 4, 0x0002, sb_ct1745_mixer_read, NULL, NULL, - sb_ct1745_mixer_write, NULL, NULL, dev->sb); + io_sethandler(dev->sb_base + 4, 0x0002, cmi8x38_sb_mixer_read, NULL, NULL, + cmi8x38_sb_mixer_write, NULL, NULL, dev); sb_dsp_setaddr(&dev->sb->dsp, dev->sb_base); } @@ -181,19 +478,16 @@ cmi8x38_remap_opl(cmi8x38_t *dev) opl3_write, NULL, NULL, &dev->sb->opl); } - if (dev->io_regs[0x1a] & 0x08) { - if (dev->type == CMEDIA_CMI8338) - dev->opl_base = 0x388; - else - dev->opl_base = opl_ports_cmi8738[dev->io_regs[0x17] & 0x03]; - } else { + dev->opl_base = (dev->type == CMEDIA_CMI8338) ? 0x388 : opl_ports_cmi8738[dev->io_regs[0x17] & 0x03]; + io_trap_remap(dev->io_traps[TRAP_OPL], dev->io_regs[0x16] & 0x80, dev->opl_base, 4); + if (!(dev->io_regs[0x1a] & 0x08)) dev->opl_base = 0; - } + cmi8x38_log("CMI8x38: remap_opl(%04X)\n", dev->opl_base); if (dev->opl_base) { io_sethandler(dev->opl_base, 0x0004, opl3_read, NULL, NULL, - opl3_write, NULL, NULL, &dev->sb->opl); + opl3_write, NULL, NULL, &dev->sb->opl); } } @@ -204,13 +498,13 @@ cmi8x38_remap_mpu(cmi8x38_t *dev) if (dev->mpu_base) mpu401_change_addr(dev->sb->mpu, 0); - if (dev->io_regs[0x04] & 0x04) { - /* The CMI8338 datasheet's port range of [300:330] is - inaccurate. Drivers expect [330:300] like CMI8738. */ - dev->mpu_base = 0x330 - ((dev->io_regs[0x17] & 0x60) >> 1); - } else { + /* The CMI8338 datasheet's port range of [300:330] is + inaccurate. Drivers expect [330:300] like CMI8738. */ + dev->mpu_base = 0x330 - ((dev->io_regs[0x17] & 0x60) >> 1); + io_trap_remap(dev->io_traps[TRAP_MPU], dev->io_regs[0x16] & 0x20, dev->mpu_base, 2); + if (!(dev->io_regs[0x04] & 0x04)) dev->mpu_base = 0; - } + cmi8x38_log("CMI8x38: remap_mpu(%04X)\n", dev->mpu_base); if (dev->mpu_base) @@ -219,9 +513,9 @@ cmi8x38_remap_mpu(cmi8x38_t *dev) static void -cmi8x38_start_playback(cmi8x38_t *dev, uint8_t val) +cmi8x38_start_playback(cmi8x38_t *dev) { - uint8_t i; + uint8_t i, val = dev->io_regs[0x00]; i = !(val & 0x01); if (!dev->dma[0].playback_enabled && i) @@ -243,16 +537,8 @@ cmi8x38_read(uint16_t addr, void *priv) uint8_t ret; switch (addr) { - case 0x22: - sb_ct1745_mixer_t *mixer = &dev->sb->mixer_sb16; - if (mixer->index >= 0xf0) - ret = dev->mixer_ext_regs[mixer->index & 0x0f]; - else - ret = sb_ct1745_mixer_read(1, dev->sb); - break; - - case 0x23: - ret = sb_ct1745_mixer_read(0, dev->sb); + case 0x22: case 0x23: + ret = cmi8x38_sb_mixer_read(addr ^ 1, dev); break; case 0x40 ... 0x4f: @@ -327,7 +613,8 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv) dev->dma[1].always_run = val & 0x02; /* Start playback if requested. */ - cmi8x38_start_playback(dev, val); + dev->io_regs[addr] = val; + cmi8x38_start_playback(dev); break; case 0x02: @@ -354,7 +641,7 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv) /* Start playback along with DMA channels. */ if (val & 0x03) - cmi8x38_start_playback(dev, dev->io_regs[0x00]); + cmi8x38_start_playback(dev); break; case 0x04: @@ -425,8 +712,14 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv) break; case 0x16: - if (dev->type == CMEDIA_CMI8338) + if (dev->type == CMEDIA_CMI8338) { val &= 0xa0; + + /* Enable or disable I/O traps. */ + dev->io_regs[addr] = val; + cmi8x38_remap_opl(dev); + cmi8x38_remap_mpu(dev); + } break; case 0x17: @@ -438,6 +731,10 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv) pci_set_irq(dev->slot, PCI_INTA); else if ((dev->io_regs[0x17] & 0x10) && !(val & 0x10)) pci_clear_irq(dev->slot, PCI_INTA); + + /* Enable or disable I/O traps. */ + io_trap_remap(dev->io_traps[TRAP_DMA], val & 0x02, 0x0000, 16); + io_trap_remap(dev->io_traps[TRAP_PIC], val & 0x01, 0x0020, 2); } /* Remap the legacy devices. */ @@ -484,43 +781,13 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv) val &= 0xf7; else val &= 0x07; + + /* Enable or disable SBPro channel swapping. */ + dev->sb->dsp.sbleftright_default = !!(val & 0x02); break; - case 0x22: - sb_ct1745_mixer_t *mixer = &dev->sb->mixer_sb16; - switch (mixer->index) { - case 0xf0: - if (dev->type == CMEDIA_CMI8338) - val &= 0xfe; - dev->mixer_ext_regs[dev->sb->mixer_sb16.index & 0x0f] = val; - break; - - case 0xf8 ... 0xff: - if (dev->type == CMEDIA_CMI8338) - dev->mixer_ext_regs[dev->sb->mixer_sb16.index & 0x0f] = val; - /* fall-through */ - - case 0xf1 ... 0xf7: - break; - - default: - sb_ct1745_mixer_write(1, val, dev->sb); - - /* Our clone mixer lacks the [3F:47] controls. */ - mixer->input_gain_L = 0; - mixer->input_gain_R = 0; - mixer->output_gain_L = (double) 1.0; - mixer->output_gain_R = (double) 1.0; - mixer->bass_l = 8; - mixer->bass_r = 8; - mixer->treble_l = 8; - mixer->treble_r = 8; - break; - } - return; - - case 0x23: - sb_ct1745_mixer_write(0, val, dev->sb); + case 0x22: case 0x23: + cmi8x38_sb_mixer_write(addr ^ 1, val, dev); return; case 0x24: @@ -533,6 +800,16 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv) val &= 0x03; else val &= 0x27; + + if (val & 0x01) { + /* Latch last DMA channels that had address/count registers written to. + Nobody knows how this "autodetection" works, but the CMI8338 TSR + disables it before and reenables it after copying the TDMA base/addr + to the 8237 registers corresponding to the 8-bit SB DMA channel. */ + dev->sb->dsp.sb_8_dmanum = dev->tdma_last_8; + if (!(dev->io_regs[0x21] & 0x01)) + dev->sb->dsp.sb_16_dmanum = dev->tdma_last_16; + } break; case 0x40 ... 0x4f: @@ -751,9 +1028,11 @@ cmi8x38_poll(void *priv) int16_t *out_l, *out_r, *out_ol, *out_or; /* o = opposite */ /* Schedule next run if playback is enabled. */ +#if 0 /* temporary */ if (dev->io_regs[0x00] & (1 << dma->id)) dma->playback_enabled = 0; else +#endif timer_advance_u64(&dma->poll_timer, dma->timer_latch); /* Update audio buffer. */ @@ -1000,6 +1279,10 @@ cmi8x38_reset(void *priv) memset(dev->dma[i].fifo, 0, sizeof(dev->dma[i].fifo)); } + /* Reset legacy DMA channel. */ + dev->tdma_last_8 = dev->tdma_last_16 = dev->sb->dsp.sb_8_dmanum = dev->sb->dsp.sb_16_dmanum = -1; + dev->tdma_mask = 0; + /* Reset Sound Blaster 16 mixer. */ sb_ct1745_mixer_reset(dev->sb); } @@ -1023,8 +1306,15 @@ cmi8x38_init(const device_t *info) dev->sb->opl_enabled = 1; /* let snd_sb.c handle the OPL3 */ dev->sb->mixer_sb16.output_filter = 0; /* no output filtering */ - /* Initialize MPU-401 interrupt handler. */ + /* Initialize legacy interrupt and DMA handlers. */ mpu401_irq_attach(dev->sb->mpu, cmi8x38_mpu_irq_update, cmi8x38_mpu_irq_pending, dev); + sb_dsp_irq_attach(&dev->sb->dsp, cmi8x38_sb_irq_update, dev); + sb_dsp_dma_attach(&dev->sb->dsp, cmi8x38_sb_dma_readb, cmi8x38_sb_dma_readw, cmi8x38_sb_dma_writeb, cmi8x38_sb_dma_writew, dev); + dev->sb->dsp.sb_type = SBPRO; + io_sethandler(0x00, 8, NULL, NULL, NULL, cmi8x38_dma_write, NULL, NULL, dev); + io_sethandler(0xc0, 16, NULL, NULL, NULL, cmi8x38_dma_write, NULL, NULL, dev); + io_sethandler(0x08, 8, NULL, NULL, NULL, cmi8x38_dma_mask_write, NULL, NULL, dev); + io_sethandler(0xd0, 16, NULL, NULL, NULL, cmi8x38_dma_mask_write, NULL, NULL, dev); /* Initialize DMA channels. */ for (int i = 0; i < (sizeof(dev->dma) / sizeof(dev->dma[0])); i++) { @@ -1044,6 +1334,14 @@ cmi8x38_init(const device_t *info) /* Initialize game port. */ dev->gameport = gameport_add(&gameport_pnp_device); + /* Initialize I/O traps. */ + if (dev->type == CMEDIA_CMI8338) { + dev->io_traps[TRAP_DMA] = io_trap_add(cmi8338_io_trap, dev); + dev->io_traps[TRAP_PIC] = io_trap_add(cmi8338_io_trap, dev); + dev->io_traps[TRAP_OPL] = io_trap_add(cmi8338_io_trap, dev); + dev->io_traps[TRAP_MPU] = io_trap_add(cmi8338_io_trap, dev); + } + /* Add PCI card. */ dev->slot = pci_add_card((info->local & (1 << 13)) ? PCI_ADD_SOUND : PCI_ADD_NORMAL, cmi8x38_pci_read, cmi8x38_pci_write, dev); @@ -1061,6 +1359,9 @@ cmi8x38_close(void *priv) cmi8x38_log("CMI8x38: close()\n"); + for (int i = 0; i < TRAP_MAX; i++) + io_trap_remove(dev->io_traps[i]); + free(dev); } diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index 59f384e2a..3101f706a 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -169,6 +169,16 @@ recalc_sb16_filter(int c, int playback_freq) low_fir_sb16_coef[c][n] /= gain; } +static void +sb_irq_update_pic(void *priv, int set) +{ + sb_dsp_t *dsp = (sb_dsp_t *) priv; + if (set) + picint(1 << dsp->sb_irqnum); + else + picintc(1 << dsp->sb_irqnum); +} + void sb_update_mask(sb_dsp_t *dsp, int irqm8, int irqm16, int irqm401) { @@ -185,7 +195,7 @@ sb_update_mask(sb_dsp_t *dsp, int irqm8, int irqm16, int irqm401) dsp->sb_irqm401 = irqm401; if (clear) - picintc(1 << dsp->sb_irqnum); + dsp->irq_update(dsp->irq_priv, 0); } void @@ -210,9 +220,9 @@ sb_update_status(sb_dsp_t *dsp, int bit, int set) } if (set && !masked) - picint(1 << dsp->sb_irqnum); + dsp->irq_update(dsp->irq_priv, 1); else if (!set) - picintc(1 << dsp->sb_irqnum); + dsp->irq_update(dsp->irq_priv, 0); } void @@ -281,7 +291,7 @@ sb_dsp_reset(sb_dsp_t *dsp) dsp->record_pos_read = 0; dsp->record_pos_write = SB_DSP_REC_SAFEFTY_MARGIN; - picintc(1 << dsp->sb_irqnum); + dsp->irq_update(dsp->irq_priv, 0); dsp->asp_data_len = 0; } @@ -350,7 +360,7 @@ sb_start_dma(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len) dsp->sb_8_output = 1; if (!timer_is_enabled(&dsp->output_timer)) timer_set_delay_u64(&dsp->output_timer, dsp->sblatcho); - dsp->sbleftright = 0; + dsp->sbleftright = dsp->sbleftright_default; dsp->sbdacpos = 0; } else { dsp->sb_16_length = len; @@ -397,29 +407,31 @@ sb_start_dma_i(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len) } int -sb_8_read_dma(sb_dsp_t *dsp) +sb_8_read_dma(void *priv) { + sb_dsp_t *dsp = (sb_dsp_t *) priv; return dma_channel_read(dsp->sb_8_dmanum); } -void -sb_8_write_dma(sb_dsp_t *dsp, uint8_t val) +int +sb_8_write_dma(void *priv, uint8_t val) { - dma_channel_write(dsp->sb_8_dmanum, val); + sb_dsp_t *dsp = (sb_dsp_t *) priv; + return dma_channel_write(dsp->sb_8_dmanum, val) == DMA_NODATA; } int -sb_16_read_dma(sb_dsp_t *dsp) +sb_16_read_dma(void *priv) { + sb_dsp_t *dsp = (sb_dsp_t *) priv; return dma_channel_read(dsp->sb_16_dmanum); } int -sb_16_write_dma(sb_dsp_t *dsp, uint16_t val) +sb_16_write_dma(void *priv, uint16_t val) { - int ret = dma_channel_write(dsp->sb_16_dmanum, val); - - return (ret == DMA_NODATA); + sb_dsp_t *dsp = (sb_dsp_t *) priv; + return dma_channel_write(dsp->sb_16_dmanum, val) == DMA_NODATA; } void @@ -469,12 +481,12 @@ sb_exec_command(sb_dsp_t *dsp) sb_start_dma(dsp, 1, 0, 0, dsp->sb_data[0] + (dsp->sb_data[1] << 8)); break; case 0x17: /* 2-bit ADPCM output with reference */ - dsp->sbref = sb_8_read_dma(dsp); + dsp->sbref = dsp->dma_readb(dsp->dma_priv); dsp->sbstep = 0; /* Fall through */ case 0x16: /* 2-bit ADPCM output */ sb_start_dma(dsp, 1, 0, ADPCM_2, dsp->sb_data[0] + (dsp->sb_data[1] << 8)); - dsp->sbdat2 = sb_8_read_dma(dsp); + dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); dsp->sb_8_length--; if (dsp->sb_command == 0x17) dsp->sb_8_length--; @@ -486,7 +498,7 @@ sb_exec_command(sb_dsp_t *dsp) case 0x1F: /* 2-bit ADPCM autoinit output */ if (dsp->sb_type >= SB15) { sb_start_dma(dsp, 1, 1, ADPCM_2, dsp->sb_data[0] + (dsp->sb_data[1] << 8)); - dsp->sbdat2 = sb_8_read_dma(dsp); + dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); dsp->sb_8_length--; } break; @@ -581,23 +593,23 @@ sb_exec_command(sb_dsp_t *dsp) dsp->sb_8_autolen = dsp->sb_data[0] + (dsp->sb_data[1] << 8); break; case 0x75: /* 4-bit ADPCM output with reference */ - dsp->sbref = sb_8_read_dma(dsp); + dsp->sbref = dsp->dma_readb(dsp->dma_priv); dsp->sbstep = 0; /* Fall through */ case 0x74: /* 4-bit ADPCM output */ sb_start_dma(dsp, 1, 0, ADPCM_4, dsp->sb_data[0] + (dsp->sb_data[1] << 8)); - dsp->sbdat2 = sb_8_read_dma(dsp); + dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); dsp->sb_8_length--; if (dsp->sb_command == 0x75) dsp->sb_8_length--; break; case 0x77: /* 2.6-bit ADPCM output with reference */ - dsp->sbref = sb_8_read_dma(dsp); + dsp->sbref = dsp->dma_readb(dsp->dma_priv); dsp->sbstep = 0; /* Fall through */ case 0x76: /* 2.6-bit ADPCM output */ sb_start_dma(dsp, 1, 0, ADPCM_26, dsp->sb_data[0] + (dsp->sb_data[1] << 8)); - dsp->sbdat2 = sb_8_read_dma(dsp); + dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); dsp->sb_8_length--; if (dsp->sb_command == 0x77) dsp->sb_8_length--; @@ -605,14 +617,14 @@ sb_exec_command(sb_dsp_t *dsp) case 0x7D: /* 4-bit ADPCM autoinit output */ if (dsp->sb_type >= SB15) { sb_start_dma(dsp, 1, 1, ADPCM_4, dsp->sb_data[0] + (dsp->sb_data[1] << 8)); - dsp->sbdat2 = sb_8_read_dma(dsp); + dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); dsp->sb_8_length--; } break; case 0x7F: /* 2.6-bit ADPCM autoinit output */ if (dsp->sb_type >= SB15) { sb_start_dma(dsp, 1, 1, ADPCM_26, dsp->sb_data[0] + (dsp->sb_data[1] << 8)); - dsp->sbdat2 = sb_8_read_dma(dsp); + dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); dsp->sb_8_length--; } break; @@ -757,7 +769,7 @@ sb_exec_command(sb_dsp_t *dsp) } dsp->sbe2 += sbe2dat[dsp->sbe2count & 3][8]; dsp->sbe2count++; - sb_8_write_dma(dsp, dsp->sbe2); + dsp->dma_writeb(dsp->dma_priv, dsp->sbe2); break; case 0xE3: /* DSP copyright */ if (dsp->sb_type >= SB16) { @@ -1016,7 +1028,7 @@ sb_read(uint16_t a, void *priv) } break; case 0xE: /* Read data ready */ - picintc(1 << dsp->sb_irqnum); + dsp->irq_update(dsp->irq_priv, 0); dsp->sb_irq8 = dsp->sb_irq16 = 0; /* Only bit 7 is defined but aztech diagnostics fail if the others are set. Keep the original behavior to not interfere with what's already working. */ if (IS_AZTECH(dsp)) { @@ -1030,7 +1042,7 @@ sb_read(uint16_t a, void *priv) case 0xF: /* 16-bit ack */ dsp->sb_irq16 = 0; if (!dsp->sb_irq8) - picintc(1 << dsp->sb_irqnum); + dsp->irq_update(dsp->irq_priv, 0); sb_dsp_log("SB 16-bit ACK read 0xFF\n"); ret = 0xff; break; @@ -1108,6 +1120,16 @@ sb_dsp_init(sb_dsp_t *dsp, int type, int subtype, void *parent) dsp->sb_16_dmanum = 5; dsp->mpu = NULL; + dsp->sbleftright_default = 0; + + dsp->irq_update = sb_irq_update_pic; + dsp->irq_priv = dsp; + dsp->dma_readb = sb_8_read_dma; + dsp->dma_readw = sb_16_read_dma; + dsp->dma_writeb = sb_8_write_dma; + dsp->dma_writew = sb_16_write_dma; + dsp->dma_priv = dsp; + sb_doreset(dsp); timer_add(&dsp->output_timer, pollsb, dsp, 0); @@ -1149,6 +1171,28 @@ sb_dsp_set_stereo(sb_dsp_t *dsp, int stereo) dsp->stereo = stereo; } +void +sb_dsp_irq_attach(sb_dsp_t *dsp, void (*irq_update)(void *priv, int set), void *priv) +{ + dsp->irq_update = irq_update; + dsp->irq_priv = priv; +} + +void +sb_dsp_dma_attach(sb_dsp_t *dsp, + int (*dma_readb)(void *priv), + int (*dma_readw)(void *priv), + int (*dma_writeb)(void *priv, uint8_t val), + int (*dma_writew)(void *priv, uint16_t val), + void *priv) +{ + dsp->dma_readb = dma_readb; + dsp->dma_readw = dma_readw; + dsp->dma_writeb = dma_writeb; + dsp->dma_writew = dma_writew; + dsp->dma_priv = priv; +} + void pollsb(void *p) { @@ -1162,7 +1206,7 @@ pollsb(void *p) switch (dsp->sb_8_format) { case 0x00: /* Mono unsigned */ - data[0] = sb_8_read_dma(dsp); + data[0] = dsp->dma_readb(dsp->dma_priv); /* Needed to prevent clicking in Worms, which programs the DSP to auto-init DMA but programs the DMA controller to single cycle */ if (data[0] == DMA_NODATA) @@ -1181,7 +1225,7 @@ pollsb(void *p) dsp->sb_8_length--; break; case 0x10: /* Mono signed */ - data[0] = sb_8_read_dma(dsp); + data[0] = dsp->dma_readb(dsp->dma_priv); if (data[0] == DMA_NODATA) break; dsp->sbdat = data[0] << 8; @@ -1198,8 +1242,8 @@ pollsb(void *p) dsp->sb_8_length--; break; case 0x20: /* Stereo unsigned */ - data[0] = sb_8_read_dma(dsp); - data[1] = sb_8_read_dma(dsp); + data[0] = dsp->dma_readb(dsp->dma_priv); + data[1] = dsp->dma_readb(dsp->dma_priv); if ((data[0] == DMA_NODATA) || (data[1] == DMA_NODATA)) break; dsp->sbdatl = (data[0] ^ 0x80) << 8; @@ -1207,8 +1251,8 @@ pollsb(void *p) dsp->sb_8_length -= 2; break; case 0x30: /* Stereo signed */ - data[0] = sb_8_read_dma(dsp); - data[1] = sb_8_read_dma(dsp); + data[0] = dsp->dma_readb(dsp->dma_priv); + data[1] = dsp->dma_readb(dsp->dma_priv); if ((data[0] == DMA_NODATA) || (data[1] == DMA_NODATA)) break; dsp->sbdatl = data[0] << 8; @@ -1241,7 +1285,7 @@ pollsb(void *p) if (dsp->sbdacpos >= 2) { dsp->sbdacpos = 0; - dsp->sbdat2 = sb_8_read_dma(dsp); + dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); dsp->sb_8_length--; } @@ -1284,7 +1328,7 @@ pollsb(void *p) dsp->sbdacpos++; if (dsp->sbdacpos >= 3) { dsp->sbdacpos = 0; - dsp->sbdat2 = sb_8_read_dma(dsp); + dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); dsp->sb_8_length--; } @@ -1321,7 +1365,7 @@ pollsb(void *p) dsp->sbdacpos++; if (dsp->sbdacpos >= 4) { dsp->sbdacpos = 0; - dsp->sbdat2 = sb_8_read_dma(dsp); + dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); } if ((dsp->sb_type >= SBPRO) && (dsp->sb_type < SB16) && dsp->stereo) { @@ -1352,22 +1396,22 @@ pollsb(void *p) switch (dsp->sb_16_format) { case 0x00: /* Mono unsigned */ - data[0] = sb_16_read_dma(dsp); + data[0] = dsp->dma_readw(dsp->dma_priv); if (data[0] == DMA_NODATA) break; dsp->sbdatl = dsp->sbdatr = data[0] ^ 0x8000; dsp->sb_16_length--; break; case 0x10: /* Mono signed */ - data[0] = sb_16_read_dma(dsp); + data[0] = dsp->dma_readw(dsp->dma_priv); if (data[0] == DMA_NODATA) break; dsp->sbdatl = dsp->sbdatr = data[0]; dsp->sb_16_length--; break; case 0x20: /* Stereo unsigned */ - data[0] = sb_16_read_dma(dsp); - data[1] = sb_16_read_dma(dsp); + data[0] = dsp->dma_readw(dsp->dma_priv); + data[1] = dsp->dma_readw(dsp->dma_priv); if ((data[0] == DMA_NODATA) || (data[1] == DMA_NODATA)) break; dsp->sbdatl = data[0] ^ 0x8000; @@ -1375,8 +1419,8 @@ pollsb(void *p) dsp->sb_16_length -= 2; break; case 0x30: /* Stereo signed */ - data[0] = sb_16_read_dma(dsp); - data[1] = sb_16_read_dma(dsp); + data[0] = dsp->dma_readw(dsp->dma_priv); + data[1] = dsp->dma_readw(dsp->dma_priv); if ((data[0] == DMA_NODATA) || (data[1] == DMA_NODATA)) break; dsp->sbdatl = data[0]; @@ -1418,27 +1462,27 @@ sb_poll_i(void *p) if (dsp->sb_8_enable && !dsp->sb_8_pause && dsp->sb_pausetime < 0 && !dsp->sb_8_output) { switch (dsp->sb_8_format) { case 0x00: /* Mono unsigned As the manual says, only the left channel is recorded */ - sb_8_write_dma(dsp, (dsp->record_buffer[dsp->record_pos_read] >> 8) ^ 0x80); + dsp->dma_writeb(dsp->dma_priv, (dsp->record_buffer[dsp->record_pos_read] >> 8) ^ 0x80); dsp->sb_8_length--; dsp->record_pos_read += 2; dsp->record_pos_read &= 0xFFFF; break; case 0x10: /* Mono signed As the manual says, only the left channel is recorded */ - sb_8_write_dma(dsp, (dsp->record_buffer[dsp->record_pos_read] >> 8)); + dsp->dma_writeb(dsp->dma_priv, (dsp->record_buffer[dsp->record_pos_read] >> 8)); dsp->sb_8_length--; dsp->record_pos_read += 2; dsp->record_pos_read &= 0xFFFF; break; case 0x20: /* Stereo unsigned */ - sb_8_write_dma(dsp, (dsp->record_buffer[dsp->record_pos_read] >> 8) ^ 0x80); - sb_8_write_dma(dsp, (dsp->record_buffer[dsp->record_pos_read + 1] >> 8) ^ 0x80); + dsp->dma_writeb(dsp->dma_priv, (dsp->record_buffer[dsp->record_pos_read] >> 8) ^ 0x80); + dsp->dma_writeb(dsp->dma_priv, (dsp->record_buffer[dsp->record_pos_read + 1] >> 8) ^ 0x80); dsp->sb_8_length -= 2; dsp->record_pos_read += 2; dsp->record_pos_read &= 0xFFFF; break; case 0x30: /* Stereo signed */ - sb_8_write_dma(dsp, (dsp->record_buffer[dsp->record_pos_read] >> 8)); - sb_8_write_dma(dsp, (dsp->record_buffer[dsp->record_pos_read + 1] >> 8)); + dsp->dma_writeb(dsp->dma_priv, (dsp->record_buffer[dsp->record_pos_read] >> 8)); + dsp->dma_writeb(dsp->dma_priv, (dsp->record_buffer[dsp->record_pos_read + 1] >> 8)); dsp->sb_8_length -= 2; dsp->record_pos_read += 2; dsp->record_pos_read &= 0xFFFF; @@ -1459,31 +1503,31 @@ sb_poll_i(void *p) if (dsp->sb_16_enable && !dsp->sb_16_pause && (dsp->sb_pausetime < 0LL) && !dsp->sb_16_output) { switch (dsp->sb_16_format) { case 0x00: /* Unsigned mono. As the manual says, only the left channel is recorded */ - if (sb_16_write_dma(dsp, dsp->record_buffer[dsp->record_pos_read] ^ 0x8000)) + if (dsp->dma_writew(dsp->dma_priv, dsp->record_buffer[dsp->record_pos_read] ^ 0x8000)) return; dsp->sb_16_length--; dsp->record_pos_read += 2; dsp->record_pos_read &= 0xFFFF; break; case 0x10: /* Signed mono. As the manual says, only the left channel is recorded */ - if (sb_16_write_dma(dsp, dsp->record_buffer[dsp->record_pos_read])) + if (dsp->dma_writew(dsp->dma_priv, dsp->record_buffer[dsp->record_pos_read])) return; dsp->sb_16_length--; dsp->record_pos_read += 2; dsp->record_pos_read &= 0xFFFF; break; case 0x20: /* Unsigned stereo */ - if (sb_16_write_dma(dsp, dsp->record_buffer[dsp->record_pos_read] ^ 0x8000)) + if (dsp->dma_writew(dsp->dma_priv, dsp->record_buffer[dsp->record_pos_read] ^ 0x8000)) return; - sb_16_write_dma(dsp, dsp->record_buffer[dsp->record_pos_read + 1] ^ 0x8000); + dsp->dma_writew(dsp->dma_priv, dsp->record_buffer[dsp->record_pos_read + 1] ^ 0x8000); dsp->sb_16_length -= 2; dsp->record_pos_read += 2; dsp->record_pos_read &= 0xFFFF; break; case 0x30: /* Signed stereo */ - if (sb_16_write_dma(dsp, dsp->record_buffer[dsp->record_pos_read])) + if (dsp->dma_writew(dsp->dma_priv, dsp->record_buffer[dsp->record_pos_read])) return; - sb_16_write_dma(dsp, dsp->record_buffer[dsp->record_pos_read + 1]); + dsp->dma_writew(dsp->dma_priv, dsp->record_buffer[dsp->record_pos_read + 1]); dsp->sb_16_length -= 2; dsp->record_pos_read += 2; dsp->record_pos_read &= 0xFFFF; From 6caf39c6ab68445bccd5100225badb6c871e3056 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Sun, 6 Mar 2022 23:10:01 -0300 Subject: [PATCH 11/62] CMI8x38: Fix Sound Blaster emulation on CMI8738 --- src/sound/snd_cmi8x38.c | 78 ++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 36 deletions(-) diff --git a/src/sound/snd_cmi8x38.c b/src/sound/snd_cmi8x38.c index f8623eb96..03f042e4d 100644 --- a/src/sound/snd_cmi8x38.c +++ b/src/sound/snd_cmi8x38.c @@ -83,7 +83,7 @@ typedef struct _cmi8x38_ { cmi8x38_dma_t dma[2]; uint16_t tdma_base_addr, tdma_base_count; uint8_t prev_mask; - int tdma_last_8, tdma_last_16, tdma_mask; + int tdma_8, tdma_16, tdma_mask; int master_vol_l, master_vol_r, cd_vol_l, cd_vol_r; } cmi8x38_t; @@ -166,6 +166,17 @@ cmi8x38_sb_dma_post(cmi8x38_t *dev, uint16_t *addr, uint16_t *count, int channel *addr += 1; *count -= 1; + /* Copy TDMA registers to DMA on CMI8738+. Everything so far suggests that + those chips use PCI bus mastering to directly write to the DMA registers. */ + if (dev->type != CMEDIA_CMI8338) { + if (channel & 4) + dma[channel].ab = (dma[channel].ab & 0xfffe0000) | ((*addr) << 1); + else + dma[channel].ab = (dma[channel].ab & 0xffff0000) | *addr; + dma[channel].ac = dma[channel].ab; + dma[channel].cc = dma[channel].cb = *count; + } + /* Handle end of DMA. */ if (*count == 0xffff) { if (dma[channel].mode & 0x10) { /* auto-init */ @@ -189,7 +200,7 @@ cmi8x38_sb_dma_readb(void *priv) cmi8x38_t *dev = (cmi8x38_t *) priv; /* Stop if the DMA channel is invalid or if TDMA is masked. */ - int channel = dev->sb->dsp.sb_8_dmanum; + int channel = dev->tdma_8; if ((channel < 0) || (dev->tdma_mask & (1 << channel))) return DMA_NODATA; @@ -213,7 +224,7 @@ cmi8x38_sb_dma_readw(void *priv) cmi8x38_t *dev = (cmi8x38_t *) priv; /* Stop if the DMA channel is invalid or if TDMA is masked. */ - int channel = dev->sb->dsp.sb_16_dmanum; + int channel = dev->tdma_16; if ((channel < 0) || (dev->tdma_mask & (1 << channel))) return DMA_NODATA; @@ -237,7 +248,7 @@ cmi8x38_sb_dma_writeb(void *priv, uint8_t val) cmi8x38_t *dev = (cmi8x38_t *) priv; /* Stop if the DMA channel is invalid or if TDMA is masked. */ - int channel = dev->sb->dsp.sb_8_dmanum; + int channel = dev->tdma_8; if ((channel < 0) || (dev->tdma_mask & (1 << channel))) return 1; @@ -261,7 +272,7 @@ cmi8x38_sb_dma_writew(void *priv, uint16_t val) cmi8x38_t *dev = (cmi8x38_t *) priv; /* Stop if the DMA channel is invalid or if TDMA is masked. */ - int channel = dev->sb->dsp.sb_16_dmanum; + int channel = dev->tdma_16; if ((channel < 0) || (dev->tdma_mask & (1 << channel))) return 1; @@ -284,22 +295,17 @@ cmi8x38_dma_write(uint16_t addr, uint8_t val, void *priv) { cmi8x38_t *dev = (cmi8x38_t *) priv; - /* Keep track of the last DMA channel written to. */ - uint8_t channel; - if (addr < 0x08) { - channel = addr >> 1; - dev->tdma_last_8 = channel; - } else { - channel = 4 | ((addr >> 2) & 3); - dev->tdma_last_16 = channel; - } - - /* Stop if not autodetecting. See note on cmi8x38_write(0x27). */ + /* Stop if autodetection is disabled. */ if (!(dev->io_regs[0x27] & 0x01)) return; /* Write TDMA registers if this is a TDMA channel. */ - if ((channel == dev->sb->dsp.sb_8_dmanum) || (channel == dev->sb->dsp.sb_16_dmanum)) { + int channel; + if (addr < 0x08) + channel = addr >> 1; + else + channel = 4 | ((addr >> 2) & 3); + if ((channel == dev->tdma_8) || (channel == dev->tdma_16)) { /* Write base address and count. */ uint16_t *addr = (uint16_t *) &dev->io_regs[0x1c], *count = (uint16_t *) &dev->io_regs[0x1e]; @@ -320,15 +326,15 @@ cmi8x38_dma_mask_write(uint16_t addr, uint8_t val, void *priv) { cmi8x38_t *dev = (cmi8x38_t *) priv; - /* Stop if not autodetecting. See note on cmi8x38_write(0x27). */ + /* Stop if autodetection is disabled. */ if (!(dev->io_regs[0x27] & 0x01)) return; /* Unmask TDMA on DMA unmasking edge. */ - if ((dev->sb->dsp.sb_8_dmanum >= 0) && (dev->prev_mask & (1 << dev->sb->dsp.sb_8_dmanum)) && !(dma_m & (1 << dev->sb->dsp.sb_8_dmanum))) - dev->tdma_mask &= ~(1 << dev->sb->dsp.sb_8_dmanum); - else if ((dev->sb->dsp.sb_16_dmanum >= 0) && (dev->prev_mask & (1 << dev->sb->dsp.sb_16_dmanum)) && !(dma_m & (1 << dev->sb->dsp.sb_16_dmanum))) - dev->tdma_mask &= ~(1 << dev->sb->dsp.sb_16_dmanum); + if ((dev->tdma_8 >= 0) && (dev->prev_mask & (1 << dev->tdma_8)) && !(dma_m & (1 << dev->tdma_8))) + dev->tdma_mask &= ~(1 << dev->tdma_8); + else if ((dev->tdma_16 >= 0) && (dev->prev_mask & (1 << dev->tdma_16)) && !(dma_m & (1 << dev->tdma_16))) + dev->tdma_mask &= ~(1 << dev->tdma_16); dev->prev_mask = dma_m; } @@ -392,8 +398,8 @@ cmi8x38_sb_mixer_write(uint16_t addr, uint8_t val, void *priv) mixer->regs[0x0e] = 0x00; break; - /* No dynamic IRQ and DMA assignment. */ - case 0x80: case 0x81: + /* No dynamic MPU port assignment. */ + case 0x84: return; /* Some extended registers beyond those accepted by the CT1745. */ @@ -427,6 +433,14 @@ cmi8x38_sb_mixer_write(uint16_t addr, uint8_t val, void *priv) /* Check interleaved stereo flag for SBPro mode. */ if ((mixer->index == 0x00) || (mixer->index == 0x0e)) sb_dsp_set_stereo(&dev->sb->dsp, mixer->regs[0x0e] & 2); + + /* Set TDMA channels if autodetection is enabled. */ + if ((dev->io_regs[0x27] & 0x01) && (mixer->index == 0x81)) { + if (dev->tdma_8 == -1) + dev->tdma_8 = dev->sb->dsp.sb_8_dmanum; + if ((dev->sb->dsp.sb_type >= SB16) && (dev->tdma_16 == -1)) + dev->tdma_16 = dev->sb->dsp.sb_16_dmanum; + } } else { cmi8x38_log("CMI8x38: sb_mixer_write(0, %02X)\n", val); sb_ct1745_mixer_write(addr, val, dev->sb); @@ -784,6 +798,9 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv) /* Enable or disable SBPro channel swapping. */ dev->sb->dsp.sbleftright_default = !!(val & 0x02); + + /* Enable or disable SB16 mode. */ + dev->sb->dsp.sb_type = (val & 0x01) ? SBPRO2 : SB16; break; case 0x22: case 0x23: @@ -800,16 +817,6 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv) val &= 0x03; else val &= 0x27; - - if (val & 0x01) { - /* Latch last DMA channels that had address/count registers written to. - Nobody knows how this "autodetection" works, but the CMI8338 TSR - disables it before and reenables it after copying the TDMA base/addr - to the 8237 registers corresponding to the 8-bit SB DMA channel. */ - dev->sb->dsp.sb_8_dmanum = dev->tdma_last_8; - if (!(dev->io_regs[0x21] & 0x01)) - dev->sb->dsp.sb_16_dmanum = dev->tdma_last_16; - } break; case 0x40 ... 0x4f: @@ -1280,7 +1287,7 @@ cmi8x38_reset(void *priv) } /* Reset legacy DMA channel. */ - dev->tdma_last_8 = dev->tdma_last_16 = dev->sb->dsp.sb_8_dmanum = dev->sb->dsp.sb_16_dmanum = -1; + dev->tdma_8 = dev->tdma_16 = -1; dev->tdma_mask = 0; /* Reset Sound Blaster 16 mixer. */ @@ -1310,7 +1317,6 @@ cmi8x38_init(const device_t *info) mpu401_irq_attach(dev->sb->mpu, cmi8x38_mpu_irq_update, cmi8x38_mpu_irq_pending, dev); sb_dsp_irq_attach(&dev->sb->dsp, cmi8x38_sb_irq_update, dev); sb_dsp_dma_attach(&dev->sb->dsp, cmi8x38_sb_dma_readb, cmi8x38_sb_dma_readw, cmi8x38_sb_dma_writeb, cmi8x38_sb_dma_writew, dev); - dev->sb->dsp.sb_type = SBPRO; io_sethandler(0x00, 8, NULL, NULL, NULL, cmi8x38_dma_write, NULL, NULL, dev); io_sethandler(0xc0, 16, NULL, NULL, NULL, cmi8x38_dma_write, NULL, NULL, dev); io_sethandler(0x08, 8, NULL, NULL, NULL, cmi8x38_dma_mask_write, NULL, NULL, dev); From 6ab26aa5670e9b9742525a1922d9fb2b332aa930 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Sun, 6 Mar 2022 23:44:51 -0300 Subject: [PATCH 12/62] CMI8x38: Fix SB emulation in DOS box with VxD driver --- src/sound/snd_cmi8x38.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sound/snd_cmi8x38.c b/src/sound/snd_cmi8x38.c index 03f042e4d..6a3e3541f 100644 --- a/src/sound/snd_cmi8x38.c +++ b/src/sound/snd_cmi8x38.c @@ -1035,11 +1035,9 @@ cmi8x38_poll(void *priv) int16_t *out_l, *out_r, *out_ol, *out_or; /* o = opposite */ /* Schedule next run if playback is enabled. */ -#if 0 /* temporary */ if (dev->io_regs[0x00] & (1 << dma->id)) dma->playback_enabled = 0; else -#endif timer_advance_u64(&dma->poll_timer, dma->timer_latch); /* Update audio buffer. */ @@ -1287,7 +1285,8 @@ cmi8x38_reset(void *priv) } /* Reset legacy DMA channel. */ - dev->tdma_8 = dev->tdma_16 = -1; + dev->tdma_8 = 1; + dev->tdma_16 = 5; dev->tdma_mask = 0; /* Reset Sound Blaster 16 mixer. */ From 57aeaa4413205c22b129463929c57c754cbc1a7c Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Mon, 7 Mar 2022 10:45:10 -0300 Subject: [PATCH 13/62] CMI8x38: Improve CMI8738 variant selection --- src/include/86box/sound.h | 1 + src/sound/snd_cmi8x38.c | 14 +++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/include/86box/sound.h b/src/include/86box/sound.h index b5afed7e6..2696954ef 100644 --- a/src/include/86box/sound.h +++ b/src/include/86box/sound.h @@ -146,6 +146,7 @@ extern const device_t cmi8338_device; extern const device_t cmi8338_onboard_device; extern const device_t cmi8738_device; extern const device_t cmi8738_onboard_device; +extern const device_t cmi8738_6ch_onboard_device; #endif #endif /*EMU_SOUND_H*/ diff --git a/src/sound/snd_cmi8x38.c b/src/sound/snd_cmi8x38.c index 6a3e3541f..9922c59f7 100644 --- a/src/sound/snd_cmi8x38.c +++ b/src/sound/snd_cmi8x38.c @@ -1376,7 +1376,7 @@ static const device_config_t cmi8x38_config[] = { }; static const device_config_t cmi8738_config[] = { - { "six_channel", "MX variant (6-channel)", CONFIG_BINARY, "", 1 }, + { "six_channel", "6CH variant (6-channel)", CONFIG_BINARY, "", 1 }, { "receive_input", "Receive input (MPU-401)", CONFIG_BINARY, "", 1 }, { "", "", -1 } }; @@ -1428,3 +1428,15 @@ const device_t cmi8738_onboard_device = { NULL, cmi8x38_config }; + +const device_t cmi8738_6ch_onboard_device = { + "C-Media CMI8738-6CH (On-Board)", + "cmi8738_6ch_onboard", + DEVICE_PCI, + CMEDIA_CMI8738_6CH | (1 << 13), + cmi8x38_init, cmi8x38_close, cmi8x38_reset, + { NULL }, + cmi8x38_speed_changed, + NULL, + cmi8x38_config +}; From 3f158b5cceec13616e5c9b3ad22fa78991a86ea2 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Mon, 7 Mar 2022 10:52:34 -0300 Subject: [PATCH 14/62] CMI8x38: Improve CMI8338 NMI traps --- src/sound/snd_cmi8x38.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/sound/snd_cmi8x38.c b/src/sound/snd_cmi8x38.c index 9922c59f7..af3645c7e 100644 --- a/src/sound/snd_cmi8x38.c +++ b/src/sound/snd_cmi8x38.c @@ -493,7 +493,7 @@ cmi8x38_remap_opl(cmi8x38_t *dev) } dev->opl_base = (dev->type == CMEDIA_CMI8338) ? 0x388 : opl_ports_cmi8738[dev->io_regs[0x17] & 0x03]; - io_trap_remap(dev->io_traps[TRAP_OPL], dev->io_regs[0x16] & 0x80, dev->opl_base, 4); + io_trap_remap(dev->io_traps[TRAP_OPL], (dev->io_regs[0x04] & 0x01) && (dev->io_regs[0x16] & 0x80), dev->opl_base, 4); if (!(dev->io_regs[0x1a] & 0x08)) dev->opl_base = 0; @@ -515,7 +515,7 @@ cmi8x38_remap_mpu(cmi8x38_t *dev) /* The CMI8338 datasheet's port range of [300:330] is inaccurate. Drivers expect [330:300] like CMI8738. */ dev->mpu_base = 0x330 - ((dev->io_regs[0x17] & 0x60) >> 1); - io_trap_remap(dev->io_traps[TRAP_MPU], dev->io_regs[0x16] & 0x20, dev->mpu_base, 2); + io_trap_remap(dev->io_traps[TRAP_MPU], (dev->io_regs[0x04] & 0x01) && (dev->io_regs[0x16] & 0x20), dev->mpu_base, 2); if (!(dev->io_regs[0x04] & 0x04)) dev->mpu_base = 0; @@ -526,6 +526,16 @@ cmi8x38_remap_mpu(cmi8x38_t *dev) } +static void +cmi8x38_remap_traps(cmi8x38_t *dev) +{ + cmi8x38_remap_opl(dev); + cmi8x38_remap_mpu(dev); + io_trap_remap(dev->io_traps[TRAP_DMA], (dev->io_regs[0x04] & 0x01) && (dev->io_regs[0x17] & 0x02), 0x0000, 16); + io_trap_remap(dev->io_traps[TRAP_PIC], (dev->io_regs[0x04] & 0x01) && (dev->io_regs[0x17] & 0x01), 0x0020, 2); +} + + static void cmi8x38_start_playback(cmi8x38_t *dev) { @@ -665,7 +675,10 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv) /* Enable or disable the legacy devices. */ dev->io_regs[addr] = val; cmi8x38_remap_sb(dev); - cmi8x38_remap_mpu(dev); + /* remap_mpu called by remap_traps */ + + /* Enable or disable I/O traps. */ + cmi8x38_remap_traps(dev); break; case 0x05: @@ -731,8 +744,7 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv) /* Enable or disable I/O traps. */ dev->io_regs[addr] = val; - cmi8x38_remap_opl(dev); - cmi8x38_remap_mpu(dev); + cmi8x38_remap_traps(dev); } break; @@ -747,8 +759,8 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv) pci_clear_irq(dev->slot, PCI_INTA); /* Enable or disable I/O traps. */ - io_trap_remap(dev->io_traps[TRAP_DMA], val & 0x02, 0x0000, 16); - io_trap_remap(dev->io_traps[TRAP_PIC], val & 0x01, 0x0020, 2); + dev->io_regs[addr] = val; + cmi8x38_remap_traps(dev); } /* Remap the legacy devices. */ From 350f2eac9ac58f7702b4149f5ccae60a3dac5411 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Mon, 7 Mar 2022 10:53:18 -0300 Subject: [PATCH 15/62] CMI8x38: Set TDMA channels even after the fact --- src/sound/snd_cmi8x38.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sound/snd_cmi8x38.c b/src/sound/snd_cmi8x38.c index af3645c7e..d28ccf9de 100644 --- a/src/sound/snd_cmi8x38.c +++ b/src/sound/snd_cmi8x38.c @@ -436,9 +436,8 @@ cmi8x38_sb_mixer_write(uint16_t addr, uint8_t val, void *priv) /* Set TDMA channels if autodetection is enabled. */ if ((dev->io_regs[0x27] & 0x01) && (mixer->index == 0x81)) { - if (dev->tdma_8 == -1) - dev->tdma_8 = dev->sb->dsp.sb_8_dmanum; - if ((dev->sb->dsp.sb_type >= SB16) && (dev->tdma_16 == -1)) + dev->tdma_8 = dev->sb->dsp.sb_8_dmanum; + if (dev->sb->dsp.sb_type >= SB16) dev->tdma_16 = dev->sb->dsp.sb_16_dmanum; } } else { From c2346a9ce4933e81d8546dfe55a437c12746196b Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Mon, 7 Mar 2022 11:19:58 -0300 Subject: [PATCH 16/62] Remove redundant SB DSP interleaved stereo checks --- src/sound/snd_sb_dsp.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index 3101f706a..147a1c4e6 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -1212,7 +1212,7 @@ pollsb(void *p) if (data[0] == DMA_NODATA) break; dsp->sbdat = (data[0] ^ 0x80) << 8; - if ((dsp->sb_type >= SBPRO) && (dsp->sb_type < SB16) && dsp->stereo) { + if (dsp->stereo) { sb_dsp_log("pollsb: Mono unsigned, dsp->stereo, %s channel, %04X\n", dsp->sbleftright ? "left" : "right", dsp->sbdat); if (dsp->sbleftright) @@ -1229,7 +1229,7 @@ pollsb(void *p) if (data[0] == DMA_NODATA) break; dsp->sbdat = data[0] << 8; - if ((dsp->sb_type >= SBPRO) && (dsp->sb_type < SB16) && dsp->stereo) { + if (dsp->stereo) { sb_dsp_log("pollsb: Mono signed, dsp->stereo, %s channel, %04X\n", dsp->sbleftright ? "left" : "right", data[0], dsp->sbdat); if (dsp->sbleftright) @@ -1289,7 +1289,7 @@ pollsb(void *p) dsp->sb_8_length--; } - if ((dsp->sb_type >= SBPRO) && (dsp->sb_type < SB16) && dsp->stereo) { + if (dsp->stereo) { sb_dsp_log("pollsb: ADPCM 4, dsp->stereo, %s channel, %04X\n", dsp->sbleftright ? "left" : "right", dsp->sbdat); if (dsp->sbleftright) @@ -1332,7 +1332,7 @@ pollsb(void *p) dsp->sb_8_length--; } - if ((dsp->sb_type >= SBPRO) && (dsp->sb_type < SB16) && dsp->stereo) { + if (dsp->stereo) { sb_dsp_log("pollsb: ADPCM 26, dsp->stereo, %s channel, %04X\n", dsp->sbleftright ? "left" : "right", dsp->sbdat); if (dsp->sbleftright) @@ -1368,7 +1368,7 @@ pollsb(void *p) dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); } - if ((dsp->sb_type >= SBPRO) && (dsp->sb_type < SB16) && dsp->stereo) { + if (dsp->stereo) { sb_dsp_log("pollsb: ADPCM 2, dsp->stereo, %s channel, %04X\n", dsp->sbleftright ? "left" : "right", dsp->sbdat); if (dsp->sbleftright) From b5d0ba8a51cfefc45f1f2ee6322be14d700b6ed4 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Mon, 7 Mar 2022 12:09:47 -0300 Subject: [PATCH 17/62] CMI8x38: Improve TDMA logging --- src/sound/snd_cmi8x38.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/sound/snd_cmi8x38.c b/src/sound/snd_cmi8x38.c index d28ccf9de..072997f46 100644 --- a/src/sound/snd_cmi8x38.c +++ b/src/sound/snd_cmi8x38.c @@ -183,7 +183,10 @@ cmi8x38_sb_dma_post(cmi8x38_t *dev, uint16_t *addr, uint16_t *count, int channel /* Restart TDMA. */ *addr = dev->tdma_base_addr; *count = dev->tdma_base_count; - cmi8x38_log("CMI8x38: Restarting TDMA on DMA %d with addr %08X count %04X\n", channel, (dma[channel].ab & 0xffff0000) | *addr, *count); + cmi8x38_log("CMI8x38: Restarting TDMA on DMA %d with addr %08X count %04X\n", + channel, + (channel & 4) ? ((dma[channel].ab & 0xfffe0000) | ((*addr) << 1)) : ((dma[channel].ab & 0xffff0000) | *addr), + *count); } else { /* Mask TDMA. */ dev->tdma_mask |= 1 << channel; @@ -311,7 +314,10 @@ cmi8x38_dma_write(uint16_t addr, uint8_t val, void *priv) *count = (uint16_t *) &dev->io_regs[0x1e]; *addr = dev->tdma_base_addr = dma[channel].ab >> !!(channel & 4); *count = dev->tdma_base_count = dma[channel].cb; - cmi8x38_log("CMI8x38: Starting TDMA on DMA %d with addr %08X count %04X\n", channel, (dma[channel].ab & 0xffff0000) | *addr, *count); + cmi8x38_log("CMI8x38: Starting TDMA on DMA %d with addr %08X count %04X\n", + channel, + (channel & 4) ? ((dma[channel].ab & 0xfffe0000) | ((*addr) << 1)) : ((dma[channel].ab & 0xffff0000) | *addr), + *count); /* Set high channel flag. */ if (channel & 4) From 67f6525f85a6fc215851036928e89f06b7c2e24a Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Mon, 7 Mar 2022 13:28:31 -0300 Subject: [PATCH 18/62] CMI8x38: Clear I/O mappings on reset, clarify autodetection requirement for TDMA updates --- src/sound/snd_cmi8x38.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/sound/snd_cmi8x38.c b/src/sound/snd_cmi8x38.c index 072997f46..5bfa1299c 100644 --- a/src/sound/snd_cmi8x38.c +++ b/src/sound/snd_cmi8x38.c @@ -298,7 +298,9 @@ cmi8x38_dma_write(uint16_t addr, uint8_t val, void *priv) { cmi8x38_t *dev = (cmi8x38_t *) priv; - /* Stop if autodetection is disabled. */ + /* Stop if DMA channel auto-detection is disabled. This is required for the CMI8338 TSR, + which disables auto-detection while copying the TDMA address/count to the SB DMA channel, + so that those writes don't loop back to the DMA register snoop mechanism implemented here. */ if (!(dev->io_regs[0x27] & 0x01)) return; @@ -332,7 +334,7 @@ cmi8x38_dma_mask_write(uint16_t addr, uint8_t val, void *priv) { cmi8x38_t *dev = (cmi8x38_t *) priv; - /* Stop if autodetection is disabled. */ + /* See comment on dma_write above. */ if (!(dev->io_regs[0x27] & 0x01)) return; @@ -1293,6 +1295,12 @@ cmi8x38_reset(void *priv) dev->io_regs[0x0b] = (dev->type >> 8) & 0x1f; dev->io_regs[0x0f] = dev->type >> 16; + /* Reset I/O mappings. */ + cmi8x38_remap(dev); + cmi8x38_remap_sb(dev); + /* remap_mpu and remap_opl called by remap_traps */ + cmi8x38_remap_traps(dev); + /* Reset DMA channels. */ for (int i = 0; i < (sizeof(dev->dma) / sizeof(dev->dma[0])); i++) { dev->dma[i].playback_enabled = 0; @@ -1333,9 +1341,9 @@ cmi8x38_init(const device_t *info) mpu401_irq_attach(dev->sb->mpu, cmi8x38_mpu_irq_update, cmi8x38_mpu_irq_pending, dev); sb_dsp_irq_attach(&dev->sb->dsp, cmi8x38_sb_irq_update, dev); sb_dsp_dma_attach(&dev->sb->dsp, cmi8x38_sb_dma_readb, cmi8x38_sb_dma_readw, cmi8x38_sb_dma_writeb, cmi8x38_sb_dma_writew, dev); - io_sethandler(0x00, 8, NULL, NULL, NULL, cmi8x38_dma_write, NULL, NULL, dev); - io_sethandler(0xc0, 16, NULL, NULL, NULL, cmi8x38_dma_write, NULL, NULL, dev); - io_sethandler(0x08, 8, NULL, NULL, NULL, cmi8x38_dma_mask_write, NULL, NULL, dev); + io_sethandler(0x00, 8, NULL, NULL, NULL, cmi8x38_dma_write, NULL, NULL, dev); + io_sethandler(0x08, 8, NULL, NULL, NULL, cmi8x38_dma_mask_write, NULL, NULL, dev); + io_sethandler(0xc0, 16, NULL, NULL, NULL, cmi8x38_dma_write, NULL, NULL, dev); io_sethandler(0xd0, 16, NULL, NULL, NULL, cmi8x38_dma_mask_write, NULL, NULL, dev); /* Initialize DMA channels. */ From 07f060bc188621e9e7296bc1892b2d7912e03aae Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Mon, 7 Mar 2022 16:00:39 -0300 Subject: [PATCH 19/62] CS423x: Add untested Mu-Law, A-Law and ADPCM playback --- src/sound/snd_ad1848.c | 105 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 100 insertions(+), 5 deletions(-) diff --git a/src/sound/snd_ad1848.c b/src/sound/snd_ad1848.c index a72e1472b..e1348b7f9 100644 --- a/src/sound/snd_ad1848.c +++ b/src/sound/snd_ad1848.c @@ -37,6 +37,10 @@ static int ad1848_vols_7bits[128]; static double ad1848_vols_5bits_aux_gain[32]; +/* Borrowed from snd_sb_dsp */ +extern int8_t scaleMap4[64]; +extern uint8_t adjustMap4[64]; + void ad1848_setirq(ad1848_t *ad1848, int irq) { @@ -253,7 +257,8 @@ ad1848_write(uint16_t addr, uint8_t val, void *priv) return; case 14: - ad1848->count = ad1848->regs[15] | (val << 8); + ad1848->count = ad1848->regs[15] | (val << 8); + ad1848->adpcm_pos = 0; break; case 17: @@ -414,6 +419,64 @@ ad1848_update(ad1848_t *ad1848) } } +static int16_t +ad1848_process_mulaw(uint8_t byte) +{ + int pos; + int16_t dec; + + dec = (~byte) & 0x7f; + pos = ((dec & 0xf0) >> 4) + 5; + dec = (1 << pos) | ((dec & 0x0f) << (pos - 4)) | (1 << (pos - 5)); + dec = (dec - 33) << 2; + + return (byte & 0x80) ? dec : -dec; +} + +static int16_t +ad1848_process_alaw(uint8_t byte) +{ + int pos; + int16_t dec; + + byte ^= 0x55; + uint8_t sample = byte & 0x7f; + pos = ((sample & 0xf0) >> 4) + 4; + if (pos == 4) + dec = (sample << 4) | 0x08; + else + dec = (1 << (pos + 3)) | ((sample & 0x0f) << (pos - 1)) | (1 << (pos - 2)); + + return (byte & 0x80) ? -dec : dec; +} + +static int16_t +ad1848_process_adpcm(ad1848_t *ad1848) +{ + int temp; + if (ad1848->adpcm_pos) { + temp = (ad1848->adpcm_data & 0x0f) + ad1848->adpcm_step; + } else { + ad1848->adpcm_data = dma_channel_read(ad1848->dma); + temp = (ad1848->adpcm_data >> 4) + ad1848->adpcm_step; + } + ad1848->adpcm_pos ^= 1; + if (temp < 0) + temp = 0; + else if (temp > 63) + temp = 63; + + ad1848->adpcm_ref += scaleMap4[temp]; + if (ad1848->adpcm_ref > 0xff) + ad1848->adpcm_ref = 0xff; + else if (ad1848->adpcm_ref < 0x00) + ad1848->adpcm_ref = 0x00; + + ad1848->adpcm_step = (ad1848->adpcm_step + adjustMap4[temp]) & 0xff; + + return (ad1848->adpcm_ref ^ 0x80) << 8; +} + static void ad1848_poll(void *priv) { @@ -431,12 +494,21 @@ ad1848_poll(void *priv) switch (ad1848->regs[8] & ad1848->fmt_mask) { case 0x00: /* Mono, 8-bit PCM */ - ad1848->out_l = ad1848->out_r = (dma_channel_read(ad1848->dma) ^ 0x80) * 256; + ad1848->out_l = ad1848->out_r = (dma_channel_read(ad1848->dma) ^ 0x80) << 8; break; case 0x10: /* Stereo, 8-bit PCM */ - ad1848->out_l = (dma_channel_read(ad1848->dma) ^ 0x80) * 256; - ad1848->out_r = (dma_channel_read(ad1848->dma) ^ 0x80) * 256; + ad1848->out_l = (dma_channel_read(ad1848->dma) ^ 0x80) << 8; + ad1848->out_r = (dma_channel_read(ad1848->dma) ^ 0x80) << 8; + break; + + case 0x20: /* Mono, 8-bit Mu-Law */ + ad1848->out_l = ad1848->out_r = ad1848_process_mulaw(dma_channel_read(ad1848->dma)); + break; + + case 0x30: /* Stereo, 8-bit Mu-Law */ + ad1848->out_l = ad1848_process_mulaw(dma_channel_read(ad1848->dma)); + ad1848->out_r = ad1848_process_mulaw(dma_channel_read(ad1848->dma)); break; case 0x40: /* Mono, 16-bit PCM little endian */ @@ -451,6 +523,26 @@ ad1848_poll(void *priv) ad1848->out_r = (dma_channel_read(ad1848->dma) << 8) | temp; break; + case 0x60: /* Mono, 8-bit A-Law */ + ad1848->out_l = ad1848->out_r = ad1848_process_alaw(dma_channel_read(ad1848->dma)); + break; + + case 0x70: /* Stereo, 8-bit A-Law */ + ad1848->out_l = ad1848_process_alaw(dma_channel_read(ad1848->dma)); + ad1848->out_r = ad1848_process_alaw(dma_channel_read(ad1848->dma)); + break; + + /* 0x80 and 0x90 reserved */ + + case 0xa0: /* Mono, 4-bit ADPCM */ + ad1848->out_l = ad1848->out_r = ad1848_process_adpcm(ad1848); + break; + + case 0xb0: /* Stereo, 4-bit ADPCM */ + ad1848->out_l = ad1848_process_adpcm(ad1848); + ad1848->out_r = ad1848_process_adpcm(ad1848); + break; + case 0xc0: /* Mono, 16-bit PCM big endian */ temp = dma_channel_read(ad1848->dma); ad1848->out_l = ad1848->out_r = dma_channel_read(ad1848->dma) | (temp << 8); @@ -462,6 +554,8 @@ ad1848_poll(void *priv) temp = dma_channel_read(ad1848->dma); ad1848->out_r = dma_channel_read(ad1848->dma) | (temp << 8); break; + + /* 0xe0 and 0xf0 reserved */ } if (ad1848->regs[6] & 0x80) @@ -475,7 +569,8 @@ ad1848_poll(void *priv) ad1848->out_r = (ad1848->out_r * ad1848_vols_7bits[ad1848->regs[7] & ad1848->wave_vol_mask]) >> 16; if (ad1848->count < 0) { - ad1848->count = ad1848->regs[15] | (ad1848->regs[14] << 8); + ad1848->count = ad1848->regs[15] | (ad1848->regs[14] << 8); + ad1848->adpcm_pos = 0; if (!(ad1848->status & 0x01)) { ad1848->status |= 0x01; ad1848->regs[24] |= 0x10; From 00d4cb5141e9559e9b3a025fd728681d80a7abbb Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Mon, 7 Mar 2022 16:32:06 -0300 Subject: [PATCH 20/62] Add a header file I forgot --- src/include/86box/snd_ad1848.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/include/86box/snd_ad1848.h b/src/include/86box/snd_ad1848.h index c74ba064c..f9f73a60c 100644 --- a/src/include/86box/snd_ad1848.h +++ b/src/include/86box/snd_ad1848.h @@ -41,8 +41,9 @@ typedef struct { int fm_vol_l, fm_vol_r; uint8_t fmt_mask, wave_vol_mask; - uint8_t enable : 1, irq : 4, dma : 3; - int freq; + uint8_t enable : 1, irq : 4, dma : 3, adpcm_ref; + int8_t adpcm_step; + int freq, adpcm_data, adpcm_pos; pc_timer_t timer_count; uint64_t timer_latch; From 9f40c213a6dcbff9e753196873fbb9fa194e0b4f Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Mon, 7 Mar 2022 16:36:13 -0300 Subject: [PATCH 21/62] CMI8x38: Fix SB interrupts in VxD DOS box --- src/sound/snd_cmi8x38.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/sound/snd_cmi8x38.c b/src/sound/snd_cmi8x38.c index 5bfa1299c..f1cfa97f7 100644 --- a/src/sound/snd_cmi8x38.c +++ b/src/sound/snd_cmi8x38.c @@ -75,7 +75,7 @@ typedef struct _cmi8x38_ { uint32_t type; uint16_t io_base, sb_base, opl_base, mpu_base; uint8_t pci_regs[256], io_regs[256]; - int slot, sb_irq; + int slot; sb_t *sb; void *gameport, *io_traps[TRAP_MAX]; @@ -119,7 +119,7 @@ static void cmi8x38_update_irqs(cmi8x38_t *dev) { /* Calculate and use the INTR flag. */ - if ((*((uint32_t *) &dev->io_regs[0x10]) & 0x0401c003) || dev->sb_irq) { + if (*((uint32_t *) &dev->io_regs[0x10]) & 0x0401c003) { dev->io_regs[0x13] |= 0x80; pci_set_irq(dev->slot, PCI_INTA); cmi8x38_log("CMI8x38: Raising IRQ\n"); @@ -154,7 +154,12 @@ static void cmi8x38_sb_irq_update(void *priv, int set) { cmi8x38_t *dev = (cmi8x38_t *) priv; - dev->sb_irq = set; + /* Interrupt flag shared with the first DMA channel. Combining SB and + PCI DMA is undefined; the VxD driver disables SB if PCI is in use. */ + if (set) + dev->io_regs[0x10] |= 0x01; + else + dev->io_regs[0x10] &= ~0x01; cmi8x38_update_irqs(dev); } From 669a6a2e49ec443c7b9cb94369d27490ecebea0f Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Mon, 7 Mar 2022 17:11:35 -0300 Subject: [PATCH 22/62] CS423x: Replace Mu-Law and A-Law functions with more efficient ones --- src/sound/snd_ad1848.c | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/sound/snd_ad1848.c b/src/sound/snd_ad1848.c index e1348b7f9..7a605df22 100644 --- a/src/sound/snd_ad1848.c +++ b/src/sound/snd_ad1848.c @@ -422,32 +422,33 @@ ad1848_update(ad1848_t *ad1848) static int16_t ad1848_process_mulaw(uint8_t byte) { - int pos; - int16_t dec; - - dec = (~byte) & 0x7f; - pos = ((dec & 0xf0) >> 4) + 5; - dec = (1 << pos) | ((dec & 0x0f) << (pos - 4)) | (1 << (pos - 5)); - dec = (dec - 33) << 2; - - return (byte & 0x80) ? dec : -dec; + byte = ~byte; + int16_t dec = ((byte & 0x0f) << 3) + 0x84; + dec <<= (byte & 0x70) >> 4; + return (byte & 0x80) ? (0x84 - dec) : (dec - 0x84); } static int16_t ad1848_process_alaw(uint8_t byte) { - int pos; - int16_t dec; - byte ^= 0x55; - uint8_t sample = byte & 0x7f; - pos = ((sample & 0xf0) >> 4) + 4; - if (pos == 4) - dec = (sample << 4) | 0x08; - else - dec = (1 << (pos + 3)) | ((sample & 0x0f) << (pos - 1)) | (1 << (pos - 2)); + int16_t dec = (byte & 0x0f) << 4; + int seg = (byte & 0x70) >> 4; + switch (seg) { + case 0: + dec += 0x8; + break; - return (byte & 0x80) ? -dec : dec; + case 1: + dec += 0x108; + break; + + default: + dec += 0x108; + dec <<= seg - 1; + break; + } + return (byte & 0x80) ? dec : -dec; } static int16_t From d0cf4bc157499a8fb2d247714be9dfc81591324a Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Mon, 7 Mar 2022 21:12:14 -0300 Subject: [PATCH 23/62] CS423x: Fix ADPCM DMA counter --- src/sound/snd_ad1848.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sound/snd_ad1848.c b/src/sound/snd_ad1848.c index 7a605df22..4d312e1da 100644 --- a/src/sound/snd_ad1848.c +++ b/src/sound/snd_ad1848.c @@ -236,6 +236,7 @@ ad1848_write(uint16_t addr, uint8_t val, void *priv) case 9: if (!ad1848->enable && (val & 0x41) == 0x01) { + ad1848->adpcm_pos = 0; if (ad1848->timer_latch) timer_set_delay_u64(&ad1848->timer_count, ad1848->timer_latch); else @@ -257,8 +258,7 @@ ad1848_write(uint16_t addr, uint8_t val, void *priv) return; case 14: - ad1848->count = ad1848->regs[15] | (val << 8); - ad1848->adpcm_pos = 0; + ad1848->count = ad1848->regs[15] | (val << 8); break; case 17: @@ -455,13 +455,12 @@ static int16_t ad1848_process_adpcm(ad1848_t *ad1848) { int temp; - if (ad1848->adpcm_pos) { + if (ad1848->adpcm_pos++ & 1) { temp = (ad1848->adpcm_data & 0x0f) + ad1848->adpcm_step; } else { ad1848->adpcm_data = dma_channel_read(ad1848->dma); temp = (ad1848->adpcm_data >> 4) + ad1848->adpcm_step; } - ad1848->adpcm_pos ^= 1; if (temp < 0) temp = 0; else if (temp > 63) @@ -580,7 +579,8 @@ ad1848_poll(void *priv) } } - ad1848->count--; + if (!(ad1848->adpcm_pos & 7)) /* ADPCM counts down every 4 bytes */ + ad1848->count--; } else { ad1848->out_l = ad1848->out_r = 0; ad1848->cd_vol_l = ad1848->cd_vol_r = 0; From 00a0a0c9815d2bbb17b0e6a0ea22d2de9caafa6f Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Mon, 7 Mar 2022 21:39:31 -0300 Subject: [PATCH 24/62] CS423x: CS4235 only supports PCM 8 and 16LE formats --- src/sound/snd_ad1848.c | 8 ++++++-- src/sound/snd_cs423x.c | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/sound/snd_ad1848.c b/src/sound/snd_ad1848.c index 4d312e1da..87713712a 100644 --- a/src/sound/snd_ad1848.c +++ b/src/sound/snd_ad1848.c @@ -262,7 +262,8 @@ ad1848_write(uint16_t addr, uint8_t val, void *priv) break; case 17: - if (ad1848->type >= AD1848_TYPE_CS4231) /* enable additional data formats on modes 2 and 3 */ + /* Enable additional data formats on modes 2 and 3 where supported. */ + if ((ad1848->type == AD1848_TYPE_CS4231) || (ad1848->type == AD1848_TYPE_CS4236)) ad1848->fmt_mask = (val & 0x40) ? 0xf0 : 0x70; break; @@ -659,7 +660,10 @@ ad1848_init(ad1848_t *ad1848, uint8_t type) ad1848->out_l = ad1848->out_r = 0; ad1848->fm_vol_l = ad1848->fm_vol_r = 65536; ad1848_updatevolmask(ad1848); - ad1848->fmt_mask = 0x70; + if (type == AD1848_TYPE_CS4235) + ad1848->fmt_mask = 0x50; + else + ad1848->fmt_mask = 0x70; for (c = 0; c < 128; c++) { attenuation = 0.0; diff --git a/src/sound/snd_cs423x.c b/src/sound/snd_cs423x.c index 34d4fb21d..6d4a5ff2e 100644 --- a/src/sound/snd_cs423x.c +++ b/src/sound/snd_cs423x.c @@ -727,7 +727,7 @@ cs423x_init(const device_t *info) case CRYSTAL_CS4237B: case CRYSTAL_CS4238B: /* Same WSS codec and EEPROM structure. */ - dev->ad1848_type = AD1848_TYPE_CS4236; + dev->ad1848_type = (dev->type == CRYSTAL_CS4235) ? AD1848_TYPE_CS4235 : AD1848_TYPE_CS4236; dev->pnp_offset = 0x4013; /* Different Chip Version and ID registers, which shouldn't be reset by ad1848_init */ From 252067de41189654eaac9b5224eac9425a03f3a4 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Thu, 10 Mar 2022 16:55:36 -0300 Subject: [PATCH 25/62] AudioPCI: Add MIDI input option to on-board ES1371 --- src/sound/snd_audiopci.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/sound/snd_audiopci.c b/src/sound/snd_audiopci.c index 23f1fa40c..03e882db8 100644 --- a/src/sound/snd_audiopci.c +++ b/src/sound/snd_audiopci.c @@ -2087,6 +2087,17 @@ static const device_config_t es1371_config[] = { // clang-format on }; +static const device_config_t es1371_onboard_config[] = { +// clang-format off + { + "receive_input", "Receive input (MIDI)", CONFIG_BINARY, "", 1 + }, + { + "", "", -1 + } +// clang-format on +}; + const device_t es1371_device = { "Ensoniq AudioPCI (ES1371)", "es1371", @@ -2112,5 +2123,5 @@ const device_t es1371_onboard_device = { { NULL }, es1371_speed_changed, NULL, - NULL + es1371_onboard_config }; From 79b9b1605de9bfd47b0b9fe29ad6eb51c7119c3a Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Sat, 12 Mar 2022 16:42:45 -0300 Subject: [PATCH 26/62] Jenkins: Add dependency management for MSYS environment --- .ci/build.sh | 123 ++++++++++++++++++++++++++++++++++---- .ci/dependencies_msys.txt | 20 +++++++ .gitignore | 1 + 3 files changed, 133 insertions(+), 11 deletions(-) create mode 100644 .ci/dependencies_msys.txt diff --git a/.ci/build.sh b/.ci/build.sh index 04c08d194..e9f9075a4 100644 --- a/.ci/build.sh +++ b/.ci/build.sh @@ -34,7 +34,7 @@ # # Define common functions. -alias is_windows='[ ! -z "$MSYSTEM" ]' +alias is_windows='[ -n "$MSYSTEM" ]' alias is_mac='uname -s | grep -q Darwin' make_tar() { @@ -62,7 +62,7 @@ make_tar() { fi # Make tar verbose if requested. - [ ! -z "$VERBOSE" ] && local compression_flag="$compression_flag -v" + [ -n "$VERBOSE" ] && local compression_flag="$compression_flag -v" # tar is notorious for having many diverging implementations. For instance, # the flags we use to strip UID/GID metadata can be --owner/group (GNU), @@ -136,7 +136,7 @@ done cmake_flags_extra= # Check if mandatory arguments were specified. -if [ -z "$package_name" -a -z "$tarball_name" ] || [ ! -z "$package_name" -a -z "$arch" ] +if [ -z "$package_name" -a -z "$tarball_name" ] || [ -n "$package_name" -a -z "$arch" ] then echo '[!] Usage: build.sh -b {package_name} {architecture} [-t] [cmake_flags...]' echo ' build.sh -s {source_tarball_name}' @@ -147,7 +147,7 @@ fi cd "$(dirname "$0")/.." # Make source tarball if requested. -if [ ! -z "$tarball_name" ] +if [ -n "$tarball_name" ] then echo [-] Making source tarball [$tarball_name] @@ -211,6 +211,106 @@ then fi echo [-] Using MSYSTEM [$MSYSTEM] + # Update keyring, as the package signing keys sometimes change. + echo [-] Updating package databases and keyring + yes | pacman -Sy --needed msys2-keyring + + # Query installed packages. + pacman -Qe > pacman.txt + + # Download the specified versions of architecture-specific dependencies. + echo -n [-] Downloading dependencies: + pkg_dir="/var/cache/pacman/pkg" + repo_base="https://repo.msys2.org/mingw/$(echo $MSYSTEM | tr '[:upper:]' '[:lower:]')" + pkgs="" + while IFS=" " read pkg version + do + prefixed_pkg="$MINGW_PACKAGE_PREFIX-$pkg" + installed_version=$(grep -E "^$prefixed_pkg " pacman.txt | cut -d " " -f 2) + if [ "$installed_version" != "$version" ] # installed_version will be empty if not installed + then + echo -n " [$pkg" + + # Download package if not already present in the local cache. + pkg_tar="$prefixed_pkg-$version-any.pkg.tar" + if [ -s "$pkg_dir/$pkg_tar.xz" ] + then + pkg_fn="$pkg_tar.xz" + pkg_dest="$pkg_dir/$pkg_fn" + else + pkg_fn="$pkg_tar.zst" + pkg_dest="$pkg_dir/$pkg_fn" + if [ ! -s "$pkg_dest" ] + then + if ! wget -qO "$pkg_dest" "$repo_base/$pkg_fn" + then + rm -f "$pkg_dest" + pkg_fn="$pkg_tar.xz" + pkg_dest="$pkg_dir/$pkg_fn" + wget -qO "$pkg_dest" "$repo_base/$pkg_fn" || rm -f "$pkg_dest" + fi + if [ -s "$pkg_dest" ] + then + wget -qO "$pkg_dest.sig" "$repo_base/$pkg_fn.sig" || rm -f "$pkg_dest.sig" + [ ! -s "$pkg_dest.sig" ] && rm -f "$pkg_dest.sig" + fi + fi + fi + + # Check if the cached package is valid. + if [ -s "$pkg_dest" ] + then + # Add cached zst package. + pkgs="$pkgs $pkg_fn" + else + # Not valid, remove if it exists. + rm -f "$pkg_dest" "$pkg_dest.sig" + echo -n " FAIL" + fi + echo -n "]" + fi + done < <(cat .ci/dependencies_msys.txt | tr -d '\r') + [ -z "$pkgs" ] && echo -n none required + echo + + # Install the downloaded architecture-specific dependencies. + echo [-] Installing dependencies through pacman + if [ -n "$pkgs" ] + then + pushd "$pkg_dir" + yes | pacman -U --needed $pkgs + if [ $? -ne 0 ] + then + # Install packages individually if installing them all together failed. + for pkg in pkgs + do + yes | pacman -U --needed "$pkg" + done + fi + popd + + # Query installed packages again. + pacman -Qe > pacman.txt + fi + + # Install the latest versions for any missing packages (if the specified version couldn't be installed). + pkgs="make git" + while IFS=" " read pkg version + do + prefixed_pkg="$MINGW_PACKAGE_PREFIX-$pkg" + grep -qE "^$prefixed_pkg " pacman.txt || pkgs="$pkgs $prefixed_pkg" + done < <(cat .ci/dependencies_msys.txt | tr -d '\r') + rm -f pacman.txt + yes | pacman -S --needed $pkgs + if [ $? -ne 0 ] + then + # Install packages individually if installing them all together failed. + for pkg in pkgs + do + yes | pacman -S --needed "$pkg" + done + fi + # Point CMake to the toolchain file. cmake_flags_extra="$cmake_flags_extra -D \"CMAKE_TOOLCHAIN_FILE=cmake/$toolchain.cmake\"" elif is_mac @@ -227,7 +327,7 @@ else esac # Establish general dependencies. - pkgs="cmake pkg-config git imagemagick wget p7zip-full wayland-protocols tar gzip" + pkgs="cmake pkg-config git imagemagick wget p7zip-full wayland-protocols tar gzip file" if [ "$(dpkg --print-architecture)" = "$arch_deb" ] then pkgs="$pkgs build-essential" @@ -242,7 +342,7 @@ else # ...and the ones we do want listed. Non-dev packages fill missing spots on the list. libpkgs="" longest_libpkg=0 - for pkg in libc6-dev libstdc++6 libopenal-dev libfreetype6-dev libx11-dev libsdl2-dev libpng-dev librtmidi-dev qtdeclarative5-dev libwayland-dev libevdev-dev libglib2.0-dev libslirp-dev libfaudio-dev libaudio-dev libjack-jackd2-dev libpipewire-0.3-dev libsamplerate0-dev libsndio-dev + for pkg in libc6-dev libstdc++6 libopenal-dev libfreetype6-dev libx11-dev libsdl2-dev libpng-dev librtmidi-dev qtdeclarative5-dev libwayland-dev libevdev-dev libglib2.0-dev libslirp-dev libfaudio-dev libaudio-dev libjack-jackd2-dev libpipewire-0.3-dev libsamplerate0-dev libsndio-dev do libpkgs="$libpkgs $pkg:$arch_deb" length=$(echo -n $pkg | sed 's/-dev$//' | sed "s/qtdeclarative/qt/" | wc -c) @@ -324,16 +424,16 @@ if [ "$CI" = "true" ] then # Backup strategy when running under Jenkins. [ -z "$git_hash" ] && git_hash=$(echo $GIT_COMMIT | cut -c 1-8) -elif [ ! -z "$git_hash" ] +elif [ -n "$git_hash" ] then # Append + to denote a dirty tree. git diff --quiet 2> /dev/null || git_hash="$git_hash+" fi -[ ! -z "$git_hash" ] && cmake_flags_extra="$cmake_flags_extra -D \"EMU_GIT_HASH=$git_hash\"" +[ -n "$git_hash" ] && cmake_flags_extra="$cmake_flags_extra -D \"EMU_GIT_HASH=$git_hash\"" # Add copyright year. year=$(date +%Y) -[ ! -z "$year" ] && cmake_flags_extra="$cmake_flags_extra -D \"EMU_COPYRIGHT_YEAR=$year\"" +[ -n "$year" ] && cmake_flags_extra="$cmake_flags_extra -D \"EMU_COPYRIGHT_YEAR=$year\"" # Run CMake. echo [-] Running CMake with flags [$cmake_flags $cmake_flags_extra] @@ -448,7 +548,8 @@ else # Build SDL2 without sound systems. sdl_ss=OFF else - # Build FAudio 22.03 manually to remove the dependency on GStreamer. + # Build FAudio 22.03 manually to remove the dependency on GStreamer. This is a temporary + # workaround until a newer version of FAudio trickles down to Debian repos. if [ -d "FAudio-22.03" ] then rm -rf FAudio-22.03/build @@ -563,7 +664,7 @@ else project_version=$(grep -oP '#define\s+EMU_VERSION\s+"\K([^"]+)' "build/src/include/$project_lower/version.h" 2> /dev/null) [ -z "$project_version" ] && project_version=unknown build_num=$(grep -oP '#define\s+EMU_BUILD_NUM\s+\K([0-9]+)' "build/src/include/$project_lower/version.h" 2> /dev/null) - [ ! -z "$build_num" -a "$build_num" != "0" ] && project_version="$project_version-b$build_num" + [ -n "$build_num" -a "$build_num" != "0" ] && project_version="$project_version-b$build_num" # Download appimage-builder if necessary. [ ! -e "appimage-builder.AppImage" ] && wget -qO appimage-builder.AppImage \ diff --git a/.ci/dependencies_msys.txt b/.ci/dependencies_msys.txt new file mode 100644 index 000000000..8c85eb90b --- /dev/null +++ b/.ci/dependencies_msys.txt @@ -0,0 +1,20 @@ +zlib 1.2.11-9 +binutils 2.37-4 +headers-git 9.0.0.6357.eac8c38c1-1 +crt-git 9.0.0.6357.eac8c38c1-2 +libwinpthread-git 9.0.0.6357.eac8c38c1-1 +winpthreads-git 9.0.0.6357.eac8c38c1-1 +winstorecompat-git 9.0.0.6357.eac8c38c1-1 +gcc-libs 11.2.0-4 +gcc 11.2.0-4 +libgccjit 11.2.0-4 +tools-git 9.0.0.6357.eac8c38c1-1 +make 4.3-1 +pkgconf 1.8.0-2 +openal 1.21.1-3 +libpng 1.6.37-6 +freetype 2.11.1-1 +SDL2 2.0.18-2 +rtmidi 4.0.0-1 +cmake 3.22.1-1 +qt5-static 5.15.2-4 diff --git a/.gitignore b/.gitignore index f54d57c5b..85bbd51d8 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ Makefile # Build scripts /archive_tmp /static2dll.* +/pacman.txt /VERSION *.zip *.tar From ed792c032192344e0bc03c3afcfdf58b4482d010 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Sat, 12 Mar 2022 16:44:42 -0300 Subject: [PATCH 27/62] Jenkins: Add more Linux libraries for execution in a vacuum --- .ci/AppImageBuilder.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.ci/AppImageBuilder.yml b/.ci/AppImageBuilder.yml index 105ef0dc9..c4a250563 100644 --- a/.ci/AppImageBuilder.yml +++ b/.ci/AppImageBuilder.yml @@ -28,11 +28,11 @@ AppDir: arch: !ENV '${arch_deb}' sources: - sourceline: 'deb http://deb.debian.org/debian bullseye main' - key_url: 'https://ftp-master.debian.org/keys/archive-key-11.asc' + key_url: 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x1f89983e0081fde018f3cc9673a4f27b8dd47936' - sourceline: 'deb http://security.debian.org/debian-security bullseye-security main' - key_url: 'https://ftp-master.debian.org/keys/archive-key-11.asc' + key_url: 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x1f89983e0081fde018f3cc9673a4f27b8dd47936' - sourceline: 'deb http://deb.debian.org/debian bullseye-updates main' - key_url: 'https://ftp-master.debian.org/keys/archive-key-11-security.asc' + key_url: 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0xac530d520f2f3269f5e98313a48449044aad5c5d' include: - libevdev2 - libfluidsynth2 @@ -51,7 +51,12 @@ AppDir: - libsndio7.0 - libwayland-client0 - libx11-6 + - libx11-xcb1 - libxcb1 + - libxcb-render0 + - libxcb-shape0 + - libxcb-shm0 + - libxcb-xfixes0 - zlib1g files: exclude: From a924622216c092c713460596230857fe8a13c8f7 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Sat, 12 Mar 2022 16:45:46 -0300 Subject: [PATCH 28/62] CMI8x38: Some SB emulation fixes --- src/sound/snd_cmi8x38.c | 63 +++++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/src/sound/snd_cmi8x38.c b/src/sound/snd_cmi8x38.c index f1cfa97f7..3e1bb6997 100644 --- a/src/sound/snd_cmi8x38.c +++ b/src/sound/snd_cmi8x38.c @@ -82,8 +82,7 @@ typedef struct _cmi8x38_ { cmi8x38_dma_t dma[2]; uint16_t tdma_base_addr, tdma_base_count; - uint8_t prev_mask; - int tdma_8, tdma_16, tdma_mask; + int tdma_8, tdma_16, tdma_mask, prev_mask; int master_vol_l, master_vol_r, cd_vol_l, cd_vol_r; } cmi8x38_t; @@ -173,7 +172,11 @@ cmi8x38_sb_dma_post(cmi8x38_t *dev, uint16_t *addr, uint16_t *count, int channel /* Copy TDMA registers to DMA on CMI8738+. Everything so far suggests that those chips use PCI bus mastering to directly write to the DMA registers. */ +#if 0 /* TSRs don't set ENWR8237, except for the patched C3DPCI - does that bit have no effect? */ + if ((dev->type != CMEDIA_CMI8338) && (dev->io_regs[0x17] & 0x10)) { +#else if (dev->type != CMEDIA_CMI8338) { +#endif if (channel & 4) dma[channel].ab = (dma[channel].ab & 0xfffe0000) | ((*addr) << 1); else @@ -196,6 +199,11 @@ cmi8x38_sb_dma_post(cmi8x38_t *dev, uint16_t *addr, uint16_t *count, int channel /* Mask TDMA. */ dev->tdma_mask |= 1 << channel; } + + /* Set the mysterious LHBTOG bit, assuming it corresponds to + the 8237 channel status bit. Nothing appears to read it. */ + dev->io_regs[0x10] |= 0x40; + return DMA_OVER; } return 0; @@ -309,29 +317,34 @@ cmi8x38_dma_write(uint16_t addr, uint8_t val, void *priv) if (!(dev->io_regs[0x27] & 0x01)) return; - /* Write TDMA registers if this is a TDMA channel. */ + /* Stop if this is not a TDMA channel. Also set or + clear the high channel flag while we're here. */ int channel; - if (addr < 0x08) + if (addr < 0x08) { channel = addr >> 1; - else + if (channel != dev->tdma_8) + return; + dev->io_regs[0x10] &= ~0x20; + } else { channel = 4 | ((addr >> 2) & 3); - if ((channel == dev->tdma_8) || (channel == dev->tdma_16)) { - /* Write base address and count. */ - uint16_t *addr = (uint16_t *) &dev->io_regs[0x1c], - *count = (uint16_t *) &dev->io_regs[0x1e]; - *addr = dev->tdma_base_addr = dma[channel].ab >> !!(channel & 4); - *count = dev->tdma_base_count = dma[channel].cb; - cmi8x38_log("CMI8x38: Starting TDMA on DMA %d with addr %08X count %04X\n", - channel, - (channel & 4) ? ((dma[channel].ab & 0xfffe0000) | ((*addr) << 1)) : ((dma[channel].ab & 0xffff0000) | *addr), - *count); - - /* Set high channel flag. */ - if (channel & 4) - dev->io_regs[0x10] |= 0x20; - else - dev->io_regs[0x10] &= ~0x20; + if (channel != dev->tdma_16) + return; + dev->io_regs[0x10] |= 0x20; } + + /* Write base address and count. */ + uint16_t *daddr = (uint16_t *) &dev->io_regs[0x1c], + *count = (uint16_t *) &dev->io_regs[0x1e]; + *daddr = dev->tdma_base_addr = dma[channel].ab >> !!(channel & 4); + *count = dev->tdma_base_count = dma[channel].cb; + cmi8x38_log("CMI8x38: Starting TDMA on DMA %d with addr %08X count %04X\n", + channel, + (channel & 4) ? ((dma[channel].ab & 0xfffe0000) | ((*daddr) << 1)) : ((dma[channel].ab & 0xffff0000) | *daddr), + *count); + + /* Clear the mysterious LHBTOG bit, assuming it corresponds to + the 8237 channel status bit. Nothing appears to read it. */ + dev->io_regs[0x10] &= ~0x40; } static void @@ -655,8 +668,14 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv) case 0x02: /* Reset DMA channels if requested. */ - if (val & 0x04) + if (val & 0x04) { val &= ~0x01; + + if (dev->sb->dsp.sb_8_enable || dev->sb->dsp.sb_16_enable || dev->sb->dsp.sb_irq8 || dev->sb->dsp.sb_irq16) { + dev->sb->dsp.sb_8_enable = dev->sb->dsp.sb_16_enable = dev->sb->dsp.sb_irq8 = dev->sb->dsp.sb_irq16 = 0; + cmi8x38_update_irqs(dev); + } + } if (val & 0x08) val &= ~0x02; From 94be8cdfc6fa7604426eef0ef4537ed87449859d Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Sat, 12 Mar 2022 20:20:25 -0300 Subject: [PATCH 29/62] GDB Stub: Initial commit --- CMakeLists.txt | 1 + src/86box.c | 5 + src/CMakeLists.txt | 5 + src/cpu/386.c | 6 + src/cpu/808x.c | 6 + src/gdbstub.c | 1042 +++++++++++++++++++++++++++++++++++ src/include/86box/gdbstub.h | 38 ++ src/qt/qt_platform.cpp | 3 + src/unix/unix.c | 3 + src/win/win_ui.c | 3 + 10 files changed, 1112 insertions(+) create mode 100644 src/gdbstub.c create mode 100644 src/include/86box/gdbstub.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8d1923a6f..80a77d9f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -121,6 +121,7 @@ option(DINPUT "DirectInput" option(CPPTHREADS "C++11 threads" ON) option(NEW_DYNAREC "Use the PCem v15 (\"new\") dynamic recompiler" OFF) option(MINITRACE "Enable Chrome tracing using the modified minitrace library" OFF) +option(GDBSTUB "Enable GDB stub server for debugging" ON) option(DEV_BRANCH "Development branch" OFF) if(NOT WIN32) option(QT "QT GUI" ON) diff --git a/src/86box.c b/src/86box.c index 17a1cca9c..262dc436d 100644 --- a/src/86box.c +++ b/src/86box.c @@ -93,6 +93,7 @@ #include <86box/ui.h> #include <86box/plat.h> #include <86box/version.h> +#include <86box/gdbstub.h> /* Stuff that used to be globally declared in plat.h but is now extern there @@ -736,6 +737,8 @@ usage: if (lang_init) lang_id = lang_init; + gdbstub_init(); + /* All good! */ return(1); } @@ -1171,6 +1174,8 @@ pc_close(thread_t *ptr) mo_close(); scsi_disk_close(); + + gdbstub_close(); } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8f1465d1e..8be7580e0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -23,6 +23,11 @@ if(CPPTHREADS) target_sources(86Box PRIVATE thread.cpp) endif() +if(GDBSTUB) + add_compile_definitions(USE_GDBSTUB) + target_sources(86Box PRIVATE gdbstub.c) +endif() + if(NEW_DYNAREC) add_compile_definitions(USE_NEW_DYNAREC) endif() diff --git a/src/cpu/386.c b/src/cpu/386.c index 6af8173eb..48fb30798 100644 --- a/src/cpu/386.c +++ b/src/cpu/386.c @@ -22,6 +22,7 @@ #include <86box/fdd.h> #include <86box/fdc.h> #include <86box/machine.h> +#include <86box/gdbstub.h> #include "386_common.h" #ifdef USE_NEW_DYNAREC #include "codegen.h" @@ -256,6 +257,11 @@ exec386(int cycs) if (TIMER_VAL_LESS_THAN_VAL(timer_target, (uint32_t) tsc)) timer_process_inline(); + +#ifdef USE_GDBSTUB + if (gdbstub_singlestep) + return; +#endif } } } diff --git a/src/cpu/808x.c b/src/cpu/808x.c index 7a2695810..a00a4b057 100644 --- a/src/cpu/808x.c +++ b/src/cpu/808x.c @@ -34,6 +34,7 @@ #include <86box/pic.h> #include <86box/ppi.h> #include <86box/timer.h> +#include <86box/gdbstub.h> /* Is the CPU 8088 or 8086. */ int is8086 = 0; @@ -2832,5 +2833,10 @@ execx86(int cycs) cpu_alu_op = 0; } + +#ifdef USE_GDBSTUB + if (gdbstub_singlestep) + return; +#endif } } diff --git a/src/gdbstub.c b/src/gdbstub.c new file mode 100644 index 000000000..9d5c32519 --- /dev/null +++ b/src/gdbstub.c @@ -0,0 +1,1042 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * GDB stub server for remote debugging. + * + * + * + * Authors: RichardG, + * + * Copyright 2022 RichardG. + */ +#include +#include +#include +#include +#include +#include +#include +#ifdef _WIN32 +# include +# include +#else +# include +# include +#endif +#define HAVE_STDARG_H +#include <86box/86box.h> +#include "cpu.h" +#include "x86seg.h" +#include <86box/io.h> +#include <86box/mem.h> +#include <86box/plat.h> + + +#define GDBSTUB_MAX_REG 38 + +enum { + GDB_SIGINT = 2, + GDB_SIGTRAP = 5 +}; + +typedef struct _gdbstub_client_ { + int socket; + struct sockaddr_in addr; + + char packet[16384], response[16384]; + int packet_pos, response_pos; + + event_t *response_event; + + struct _gdbstub_client_ *next; +} gdbstub_client_t; + +#define ENABLE_GDBSTUB_LOG 1 +#ifdef ENABLE_GDBSTUB_LOG +int gdbstub_do_log = ENABLE_GDBSTUB_LOG; + +static void +gdbstub_log(const char *fmt, ...) +{ + va_list ap; + + if (gdbstub_do_log) { + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); + } +} +#else +#define gdbstub_log(fmt, ...) +#endif + +static x86seg *segment_regs[] = {&cpu_state.seg_cs, &cpu_state.seg_ss, &cpu_state.seg_ds, &cpu_state.seg_es, &cpu_state.seg_fs, &cpu_state.seg_gs}; +static uint32_t *cr_regs[] = {&cpu_state.CR0.l, &cr2, &cr3, &cr4}; +static void *fpu_regs[] = {&cpu_state.npxc, &cpu_state.npxs, NULL, x87_pc_seg, x87_pc_off, x87_op_seg, x87_op_off}; +static const char target_xml[] = /* based on qemu's i386-32bit.xml */ + "" + "" + "" + "i8086" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + ""; + +#ifdef _WIN32 +static WSADATA wsa; +#endif +static int gdbstub_socket = -1, gdbstub_paused = 0; +static char stop_reason[2048]; +static gdbstub_client_t *first_client, *last_client; +static mutex_t *client_list_mutex; +static void (*cpu_exec_shadow)(int cycs); + +int gdbstub_singlestep = 0; + + +static void +gdbstub_break() +{ + /* Initiate pause. */ + plat_pause(1); + + /* Force CPU execution to return as soon as possible. */ + gdbstub_singlestep = 1; +} + + +static void +gdbstub_singlestep_exec(int cycs) +{ + /* Call the original cpu_exec function. */ + cpu_exec_shadow(cycs); + + /* Swap the original function back in. */ + cpu_exec = cpu_exec_shadow; + + /* Break immediately. */ + gdbstub_break(); +} + + +static void +gdbstub_jump(uint32_t new_pc) +{ + /* Nasty hack; qemu always uses the full 32-bit EIP internally... */ + if (cpu_state.op32 || ((new_pc >= cs) && (new_pc < (cs + 65536)))) { + cpu_state.pc = new_pc - cs; + } else { + loadseg((new_pc >> 4) & 0xf000, &cpu_state.seg_cs); + cpu_state.pc = new_pc & 0xffff; + } +} + + +static inline int +gdbstub_hex_decode(int c) +{ + if ((c >= '0') && (c <= '9')) + return c - '0'; + else if ((c >= 'A') && (c <= 'F')) + return c - 'A' + 10; + else if ((c >= 'a') && (c <= 'f')) + return c - 'a' + 10; + else + return 0; +} + + +static inline int +gdbstub_hex_encode(int c) +{ + if (c < 10) + return c + '0'; + else + return c - 10 + 'A'; +} + + +static int +gdbstub_client_read_word(gdbstub_client_t *client, int *dest) +{ + char *p = &client->packet[client->packet_pos], *q = p; + while (((*p >= '0') && (*p <= '9')) || ((*p >= 'A') && (*p <= 'F')) || ((*p >= 'a') && (*p <= 'f'))) + *dest = ((*dest) << 4) | gdbstub_hex_decode(*p++); + return p - q; +} + + +static int +gdbstub_client_read_hex(gdbstub_client_t *client, uint8_t *buf, int size) +{ + int pp = client->packet_pos; + while (size-- && (pp < (sizeof(client->packet) - 2))) { + *buf = gdbstub_hex_decode(client->packet[pp++]) << 4; + *buf++ |= gdbstub_hex_decode(client->packet[pp++]); + } + return pp - client->packet_pos; +} + + +static int +gdbstub_client_read_string(gdbstub_client_t *client, char *buf, int size, char terminator) +{ + int pp = client->packet_pos; + char c; + while (size-- && (pp < (sizeof(client->packet) - 1))) { + c = client->packet[pp]; + if ((c == terminator) || (c == '\0')) { + *buf = '\0'; + break; + } + pp++; + *buf++ = c; + } + return pp - client->packet_pos; +} + + +static int +gdbstub_client_write_reg(int index, uint8_t *buf) +{ + int width = 4; + switch (index) { + case 0 ... 7: /* [EAX:EDI] */ + cpu_state.regs[index].l = *((uint32_t *) buf); + break; + + case 8: /* EIP */ + gdbstub_jump(*((uint32_t *) buf)); + break; + + case 9: /* EFLAGS */ + cpu_state.flags = *((uint16_t *) &buf[0]); + cpu_state.eflags = *((uint16_t *) &buf[2]); + break; + + case 10 ... 15: /* [CS:GS] */ + width = 2; + loadseg(*((uint16_t *) buf), segment_regs[index - 10]); + break; + + case 16 ... 17: /* FSbase, GSbase */ + /* Do what qemu does and just load the base. */ + segment_regs[index - 12]->base = *((uint32_t *) buf); + break; + + case 18 ... 21: /* [CR0:CR4] */ + *cr_regs[index - 18] = *((uint32_t *) buf); + break; + + case 22: /* EFER */ + msr.amd_efer = *((uint64_t *) buf); + break; + + case 23 ... 30: /* ST(0:7) */ + width = 10; + break; + + /* FPU CONTROL REGS */ + + default: + width = 0; + } + + flushmmucache(); /* incredibly cursed to be calling that from here */ + + return width; +} + + +static void +gdbstub_client_respond(gdbstub_client_t *client) +{ + /* Calculate checksum. */ + uint8_t checksum = 0; + int i; + for (i = 0; i < client->response_pos; i++) + checksum += client->response[i]; + + /* Send response packet. */ + client->response[client->response_pos] = '\0'; +#ifdef ENABLE_GDBSTUB_LOG + i = client->response[995]; /* pclog_ex buffer too small */ + client->response[995] = '\0'; + gdbstub_log("GDB Stub: Sending response: %s\n", client->response); + client->response[995] = i; +#endif + send(client->socket, "$", 1, 0); + send(client->socket, client->response, client->response_pos, 0); + char response_cksum[3] = {'#', gdbstub_hex_encode(checksum >> 4), gdbstub_hex_encode(checksum & 0x0f)}; + send(client->socket, response_cksum, sizeof(response_cksum), 0); +} + + +static void +gdbstub_client_respond_hex(gdbstub_client_t *client, uint8_t *buf, int size) +{ + while (size-- && (client->response_pos < (sizeof(client->response) - 2))) { + client->response[client->response_pos++] = gdbstub_hex_encode((*buf) >> 4); + client->response[client->response_pos++] = gdbstub_hex_encode((*buf++) & 0x0f); + } +} + + +static int +gdbstub_client_read_reg(int index, uint8_t *buf) +{ + int width = 4; + switch (index) { + case 0 ... 7: /* [EAX:EDI] */ + *((uint32_t *) buf) = cpu_state.regs[index].l; + break; + + case 8: /* EIP */ + *((uint32_t *) buf) = cs + cpu_state.pc; + break; + + case 9: /* EFLAGS */ + *((uint16_t *) &buf[0]) = cpu_state.flags; + *((uint16_t *) &buf[2]) = cpu_state.eflags; + break; + + case 10 ... 15: /* [CS:GS] */ + *((uint16_t *) buf) = segment_regs[index - 10]->seg; + break; + + case 16 ... 17: /* FSbase, GSbase */ + *((uint32_t *) buf) = segment_regs[index - 12]->base; + break; + + case 18 ... 21: /* [CR0:CR4] */ + *((uint32_t *) buf) = *cr_regs[index - 18]; + break; + + case 22: /* EFER */ + *((uint64_t *) buf) = msr.amd_efer; + break; + + case 23 ... 30: /* ST(0:7) */ + width = 10; + *((uint64_t *) &buf[0]) = 0; + *((uint16_t *) &buf[8]) = 0; + break; + + case 31 ... 32: /* [FCTRL:FSTAT] */ + case 34: /* [FISEG] */ + case 36: /* [FOSEG] */ + width = 2; + *((uint16_t *) buf) = *((uint16_t *) fpu_regs[index - 31]); + break; + + case 33: /* FTAG */ + width = 2; + *((uint16_t *) buf) = x87_gettag(); + break; + + case 35: /* [FIOFF] */ + case 37: /* [FOOFF] */ + *((uint32_t *) buf) = *((uint32_t *) fpu_regs[index - 31]); + break; + + case 38: /* [FOP] */ + width = 2; + *((uint16_t *) buf) = 0; /* we don't store the FPU opcode */ + break; + + default: + width = 0; + } + + return width; +} + + +static void +gdbstub_client_packet(gdbstub_client_t *client) +{ + uint8_t rcv_checksum = 0, checksum = 0; + int i, j = 0, k = 0, l; + uint8_t buf[10] = {0}; + char *p; + + /* Validate checksum. */ + client->packet_pos -= 2; + gdbstub_client_read_hex(client, &rcv_checksum, 1); + client->packet[client->packet_pos] = '\0'; + client->packet[client->packet_pos + 1] = '\0'; + client->packet[--client->packet_pos] = '\0'; + for (i = 0; i < client->packet_pos; i++) + checksum += client->packet[i]; + +#if 0 /* msys2 gdb 11.1 transmits qSupported and H with invalid checksum... */ + if (checksum != rcv_checksum) { + /* Send negative acknowledgement. */ +#ifdef ENABLE_GDBSTUB_LOG + i = client->packet[953]; /* pclog_ex buffer too small */ + client->packet[953] = '\0'; + gdbstub_log("GDB Stub: Received packet with invalid checksum (expected %02X got %02X): %s\n", checksum, rcv_checksum, client->packet); + client->packet[953] = i; +#endif + send(client->socket, "-", 1, 0); + return; + } +#endif + + /* Send positive acknowledgement. */ +#ifdef ENABLE_GDBSTUB_LOG + i = client->packet[996]; /* pclog_ex buffer too small */ + client->packet[996] = '\0'; + gdbstub_log("GDB Stub: Received packet: %s\n", client->packet); + client->packet[996] = i; +#endif + send(client->socket, "+", 1, 0); + + /* Block other responses from being written while this one isn't acknowledged. */ + thread_wait_event(client->response_event, 0); + thread_reset_event(client->response_event); + client->response_pos = 0; + client->packet_pos = 1; + + /* Parse command. */ + switch (client->packet[0]) { + case '?': /* stop reason */ + /* Respond with a stop reply packet. */ + strcpy(client->response, stop_reason); + client->response_pos = strlen(client->response); + break; + + case 'c': /* continue */ + case 's': /* step */ + /* No immediate response. */ + thread_set_event(client->response_event); + + /* Jump to address if specified. */ + if (gdbstub_client_read_word(client, &j)) + gdbstub_jump(j); + + /* Resume emulation. */ + if (client->packet[0] == 'c') { + gdbstub_singlestep = 0; + } else { + /* Replace cpu_exec with our own function, which breaks after a single step. */ + gdbstub_singlestep = 1; + if (cpu_exec != gdbstub_singlestep_exec) + cpu_exec_shadow = cpu_exec; + cpu_exec = gdbstub_singlestep_exec; + } + gdbstub_paused = 0; + plat_pause(0); + return; + + case 'D': /* detach */ + /* Resume emulation. */ + gdbstub_paused = 0; + plat_pause(0); + + /* Respond positively. */ + client->response_pos = sprintf(client->response, "OK"); + break; + + case 'g': /* read all registers */ + /* Output the values of all registers. */ + for (i = 0; i <= GDBSTUB_MAX_REG; i++) + gdbstub_client_respond_hex(client, buf, gdbstub_client_read_reg(i, buf)); + break; + + case 'G': /* write all registers */ + /* Write the values of all registers. */ + for (i = 0; i <= GDBSTUB_MAX_REG; i++) { + if (!gdbstub_client_read_hex(client, buf, sizeof(buf))) + break; + client->packet_pos += gdbstub_client_write_reg(i, buf); + } + break; + + case 'H': /* set thread */ + /* Read operation type and thread ID. */ + if ((client->packet[1] == '\0') || (client->packet[2] == '\0')) { +e22: client->response_pos = sprintf(client->response, "E22"); + break; + } + + /* Respond positively only on thread 1. */ + if ((client->packet[2] == '1') && !client->packet[3]) + client->response_pos = sprintf(client->response, "OK"); + else + goto e22; + break; + + case 'm': /* read memory */ + /* Read address and length. */ + if (!(i = gdbstub_client_read_word(client, &j))) + goto e22; + client->packet_pos += i + 1; + gdbstub_client_read_word(client, &k); + if (!k) + goto e22; + + /* Clamp length. */ + if (k >= (sizeof(client->response) >> 1)) + k = (sizeof(client->response) >> 1) - 1; + + /* Read by qwords, then by dwords, then by words, then by bytes. */ + i = 0; + if (is386) { + for (; i < (k & ~7); i += 8) { + *((uint64_t *) buf) = readmemql(j); + j += 8; + gdbstub_client_respond_hex(client, buf, 8); + } + for (; i < (k & ~3); i += 4) { + *((uint32_t *) buf) = readmemll(j); + j += 4; + gdbstub_client_respond_hex(client, buf, 4); + } + } + for (; i < (k & ~1); i += 2) { + *((uint16_t *) buf) = readmemwl(j); + j += 2; + gdbstub_client_respond_hex(client, buf, 2); + } + for (; i < k; i++) { + buf[0] = readmembl(j++); + gdbstub_client_respond_hex(client, buf, 1); + } + break; + + case 'M': /* write memory */ + case 'X': /* write memory binary */ + /* Read address and length. */ + if (!(i = gdbstub_client_read_word(client, &j))) + goto e22; + client->packet_pos += i + 1; + client->packet_pos += gdbstub_client_read_word(client, &k) + 1; + if (!k) + goto e22; + + /* Clamp length. */ + if (k >= ((sizeof(client->response) >> 1) - client->packet_pos)) + k = (sizeof(client->response) >> 1) - client->packet_pos - 1; + + /* Decode the data. */ + if (client->packet[0] == 'M') { /* hex encoded */ + gdbstub_client_read_hex(client, (uint8_t *) client->packet, k); + } else { /* binary encoded */ + i = 0; + while (i < k) { + if (client->packet[client->packet_pos] == '}') { + client->packet_pos++; + client->packet[i++] = client->packet[client->packet_pos++] ^ 0x20; + } else { + client->packet[i++] = client->packet[client->packet_pos++]; + } + } + } + + /* Write by qwords, then by dwords, then by words, then by bytes. */ + p = client->packet; + i = 0; + if (is386) { + for (; i < (k & ~7); i += 8) { + writememql(j, *((uint64_t *) p)); + j += 8; + p += 8; + } + for (; i < (k & ~3); i += 4) { + writememll(j, *((uint32_t *) p)); + j += 4; + p += 4; + } + } + for (; i < (k & ~1); i += 2) { + writememwl(j, *((uint16_t *) p)); + j += 2; + p += 2; + } + for (; i < k; i++) { + writemembl(j++, p[0]); + p++; + } + + /* Respond positively. */ + client->response_pos = sprintf(client->response, "OK"); + break; + + case 'p': /* read register */ + /* Read register index. */ + if (!gdbstub_client_read_word(client, &j)) { +e14: client->response_pos = sprintf(client->response, "E14"); + break; + } + + /* Read the register's value. */ + if (!(i = gdbstub_client_read_reg(j, buf))) + goto e14; + + /* Return value. */ + gdbstub_client_respond_hex(client, buf, i); + break; + + case 'P': /* write register */ + /* Read register index and value. */ + if (!(i = gdbstub_client_read_word(client, &j))) + goto e14; + client->packet_pos += i + 1; + if (!gdbstub_client_read_hex(client, buf, sizeof(buf))) + goto e14; + + /* Write the value to the register. */ + if (!gdbstub_client_write_reg(j, buf)) + goto e14; + + /* Respond positively. */ + client->response_pos = sprintf(client->response, "OK"); + break; + + case 'q': /* query */ + /* Erase response, as we'll use it as a scratch buffer. */ + memset(client->response, 0, sizeof(client->response)); + + /* Read the query type. */ + client->packet_pos += gdbstub_client_read_string(client, client->response, sizeof(client->response) - 1, ':') + 1; + + /* Perform the query. */ + if (!strcmp(client->response, "Supported")) { + /* Go through the feature list and negate ones we don't support. */ + while ((client->response_pos < (sizeof(client->response) - 1)) && + (i = gdbstub_client_read_string(client, &client->response[client->response_pos], sizeof(client->response) - client->response_pos - 1, ';'))) { + client->packet_pos += i + 1; + if (strncmp(&client->response[client->response_pos], "PacketSize", 10) && + strcmp(&client->response[client->response_pos], "swbreak") && + strcmp(&client->response[client->response_pos], "hwbreak") && + strncmp(&client->response[client->response_pos], "xmlRegisters", 12) && + strcmp(&client->response[client->response_pos], "qXfer:features:read")) { + gdbstub_log("GDB Stub: Feature \"%s\" is not supported\n", &client->response[client->response_pos]); + client->response_pos += i; + client->response[client->response_pos++] = '-'; + client->response[client->response_pos++] = ';'; + } else { + gdbstub_log("GDB Stub: Feature \"%s\" is supported\n", &client->response[client->response_pos]); + } + } + + /* Add our supported features to the end. */ + if (client->response_pos < (sizeof(client->response) - 1)) + client->response_pos += snprintf(&client->response[client->response_pos], sizeof(client->response) - client->response_pos, + "PacketSize=%X;swbreak+;hwbreak+;qXfer:features:read+", sizeof(client->packet) - 1); + break; + } else if (!strcmp(client->response, "Xfer")) { + /* Read the transfer object. */ + client->packet_pos += gdbstub_client_read_string(client, client->response, sizeof(client->response) - 1, ':') + 1; + if (!strcmp(client->response, "features")) { + /* Read the transfer operation. */ + client->packet_pos += gdbstub_client_read_string(client, client->response, sizeof(client->response) - 1, ':') + 1; + if (!strcmp(client->response, "read")) { + /* Read the transfer annex. */ + client->packet_pos += gdbstub_client_read_string(client, client->response, sizeof(client->response) - 1, ':') + 1; + if (!strcmp(client->response, "target.xml")) + p = (char *) target_xml; + else + p = NULL; + + /* Stop if the file wasn't found. */ + if (!p) { +e00: client->response_pos = sprintf(client->response, "E00"); + break; + } + + /* Read offset and length. */ + if (!(i = gdbstub_client_read_word(client, &j))) + goto e22; + client->packet_pos += i + 1; + client->packet_pos += gdbstub_client_read_word(client, &k) + 1; + if (!k) + goto e22; + + /* Check if the offset is valid. */ + l = strlen(p); + if (j > l) + goto e00; + p += j; + + /* Return the more/less flag while also clamping the length. */ + if (k >= ((sizeof(client->response) >> 1) - 2)) + k = (sizeof(client->response) >> 1) - 3; + if (k < (l - j)) { + client->response[client->response_pos++] = 'm'; + } else { + client->response[client->response_pos++] = 'l'; + k = l - j; + } + + /* Encode the data. */ + while (k--) { + i = *p++; + if ((i == '#') || (i == '$') || (i == '*') || (i == '}')) { + client->response[client->response_pos++] = '}'; + client->response[client->response_pos++] = i ^ 0x20; + } else { + client->response[client->response_pos++] = i; + } + } + break; + } + } + } + + /* No response by default. */ + client->response_pos = 0; + break; + } + + /* Send response. */ + gdbstub_client_respond(client); +} + + +static void +gdbstub_client_thread(void *priv) +{ + gdbstub_client_t *client = (gdbstub_client_t *) priv, *other_client; + uint8_t buf[256]; + ssize_t bytes_read; + int i; + + gdbstub_log("GDB Stub: New connection from %s:%d\n", inet_ntoa(client->addr.sin_addr), client->addr.sin_port); + + /* Read data from client. */ + while ((bytes_read = recv(client->socket, (char *) buf, sizeof(buf), 0)) > 0) { + for (i = 0; i < bytes_read; i++) { + switch (buf[i]) { + case '$': /* packet start */ + client->packet_pos = 0; + break; + + case '-': /* negative acknowledgement */ + /* Retransmit the current response. */ + gdbstub_client_respond(client); + break; + + case '+': /* positive acknowledgement */ + /* Allow another response to be written. */ + thread_set_event(client->response_event); + break; + + case 0x03: /* break */ + /* Break immediately. */ + gdbstub_log("GDB Stub: Break requested\n"); + gdbstub_paused = 0; + gdbstub_break(); + break; + + default: + if (client->packet_pos < (sizeof(client->packet) - 1)) { + /* Append byte to the packet. */ + client->packet[client->packet_pos++] = buf[i]; + + /* Check if this is the end of a packet. */ + if ((client->packet_pos >= 3) && (client->packet[client->packet_pos - 3] == '#')) { /* packet checksum start */ + gdbstub_client_packet(client); + client->packet_pos = 0; + } + } + break; + } + } + } + + gdbstub_log("GDB Stub: Connection with %s:%d broken\n", inet_ntoa(client->addr.sin_addr), client->addr.sin_port); + + /* Close socket. */ + if (client->socket != -1) { + close(client->socket); + client->socket = -1; + } + + /* Unblock anyone waiting on the response event. */ + thread_set_event(client->response_event); + + /* Remove this client from the list. */ + thread_wait_mutex(client_list_mutex); + if (client == first_client) { + first_client = client->next; + if (first_client == NULL) + last_client = NULL; + gdbstub_paused = 0; /* allow user to unpause when all clients are disconnected */ + } else { + other_client = first_client; + while (other_client) { + if (other_client->next == client) { + if (last_client == client) + last_client = other_client; + other_client->next = client->next; + break; + } + other_client = other_client->next; + } + } + free(client); + thread_release_mutex(client_list_mutex); +} + + +static void +gdbstub_server_thread(void *priv) +{ + /* Listen on GDB socket. */ + listen(gdbstub_socket, 1); + + /* Accept connections. */ + gdbstub_client_t *client; + socklen_t sl = sizeof(struct sockaddr_in); + while (1) { + /* Allocate client structure. */ + client = malloc(sizeof(gdbstub_client_t)); + memset(client, 0, sizeof(gdbstub_client_t)); + client->response_event = thread_create_event(); + + /* Accept connection. */ + client->socket = accept(gdbstub_socket, (struct sockaddr *) &client->addr, &sl); + if (client->socket < 0) + break; + + /* Add to client list. */ + thread_wait_mutex(client_list_mutex); + if (first_client) { + last_client->next = client; + last_client = client; + } else { + first_client = last_client = client; + } + thread_release_mutex(client_list_mutex); + + /* Pause emulation. */ + gdbstub_paused = 1; + gdbstub_break(); + + /* Start client thread. */ + thread_create(gdbstub_client_thread, client); + } + + /* Deallocate the redundant client structure. */ + thread_destroy_event(client->response_event); + free(client); +} + + +void +gdbstub_pause(int *p) +{ + if (!(*p) && gdbstub_paused) { + /* Don't allow the user to unpause if we're pausing. */ + gdbstub_log("GDB Stub: Blocked user unpause\n"); + *p = 1; + } else if (*p) { + sprintf(stop_reason, "S%02X", gdbstub_singlestep ? GDB_SIGTRAP : GDB_SIGINT); + if (!gdbstub_paused) { + /* Send interrupt packet to all clients. */ + gdbstub_log("GDB Stub: Pausing\n"); + gdbstub_paused = 1; + thread_wait_mutex(client_list_mutex); + gdbstub_client_t *client = first_client; + while (client) { + if (!thread_wait_event(client->response_event, -1)) { + /* Block other responses from being written while this one isn't acknowledged. */ + thread_reset_event(client->response_event); + + /* Write stop reply packet. */ + client->response_pos = sprintf(client->response, "%s", stop_reason); + gdbstub_client_respond(client); + } else { + gdbstub_log("GDB Stub: Timed out waiting for client %08X\n", client); + } + client = client->next; + } + thread_release_mutex(client_list_mutex); + } + } +} + + +void +gdbstub_init() +{ +#ifdef _WIN32 + WSAStartup(MAKEWORD(2, 2), &wsa); +#endif + + /* Create GDB server socket. */ + if ((gdbstub_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + pclog("GDB Stub: Failed to create socket\n"); + return; + } + + /* Bind GDB server socket. */ + int port = 12345; + struct sockaddr_in bind_addr = { + .sin_family = AF_INET, + .sin_addr = { .s_addr = INADDR_ANY }, + .sin_port = htons(port) + }; + if (bind(gdbstub_socket, (struct sockaddr *) &bind_addr, sizeof(bind_addr)) == -1) { + pclog("GDB Stub: Failed to bind on port %d (%d)\n", port, WSAGetLastError()); + gdbstub_socket = -1; + return; + } + + /* Create client list mutex. */ + client_list_mutex = thread_create_mutex(); + + /* Start server thread. */ + pclog("GDB Stub: Listening on port %d\n", port); + thread_create(gdbstub_server_thread, NULL); +} + + +void +gdbstub_close() +{ + /* Stop if the GDB server hasn't initialized. */ + if (gdbstub_socket < 0) + return; + + /* Close GDB server socket. */ + close(gdbstub_socket); + + /* Clear client list. */ + thread_wait_mutex(client_list_mutex); + gdbstub_client_t *client = first_client; + int socket; + while (client) { + socket = client->socket; + client->socket = -1; + close(socket); + client = client->next; + } + thread_release_mutex(client_list_mutex); + thread_close_mutex(client_list_mutex); +} diff --git a/src/include/86box/gdbstub.h b/src/include/86box/gdbstub.h new file mode 100644 index 000000000..d46c8fe6d --- /dev/null +++ b/src/include/86box/gdbstub.h @@ -0,0 +1,38 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * Definitions for the GDB stub server. + * + * + * + * Authors: RichardG, + * + * Copyright 2022 RichardG. + */ +#ifndef EMU_GDBSTUB_H +# define EMU_GDBSTUB_H + +#ifdef USE_GDBSTUB + +extern int gdbstub_singlestep; + +extern void gdbstub_pause(int *p); +extern void gdbstub_init(); +extern void gdbstub_close(); + +#else + +#define gdbstub_singlestep 0 + +#define gdbstub_pause(p) +#define gdbstub_init() +#define gdbstub_close() + +#endif + +#endif diff --git a/src/qt/qt_platform.cpp b/src/qt/qt_platform.cpp index 4ec31b842..b4bd38e83 100644 --- a/src/qt/qt_platform.cpp +++ b/src/qt/qt_platform.cpp @@ -92,6 +92,7 @@ extern "C" { #include "../cpu/cpu.h" #include <86box/plat.h> +#include <86box/gdbstub.h> volatile int cpu_thread_run = 1; int mouse_capture = 0; @@ -314,6 +315,8 @@ plat_pause(int p) static wchar_t oldtitle[512]; wchar_t title[512], paused_msg[64]; + gdbstub_pause(&p); + if (p == dopause) { #ifdef Q_OS_WINDOWS if (source_hwnd) diff --git a/src/unix/unix.c b/src/unix/unix.c index 83f6b4a9b..ead580372 100644 --- a/src/unix/unix.c +++ b/src/unix/unix.c @@ -32,6 +32,7 @@ #include <86box/timer.h> #include <86box/nvr.h> #include <86box/ui.h> +#include <86box/gdbstub.h> static int first_use = 1; static uint64_t StartingTime; @@ -719,6 +720,8 @@ plat_pause(int p) static wchar_t oldtitle[512]; wchar_t title[512]; + gdbstub_pause(&p); + if ((p == 0) && (time_sync & TIME_SYNC_ENABLED)) nvr_time_sync(); diff --git a/src/win/win_ui.c b/src/win/win_ui.c index 1a9ad961a..b99ca6414 100644 --- a/src/win/win_ui.c +++ b/src/win/win_ui.c @@ -44,6 +44,7 @@ #include <86box/win.h> #include <86box/version.h> #include <86box/discord.h> +#include <86box/gdbstub.h> #ifdef MTR_ENABLED #include @@ -1496,6 +1497,8 @@ plat_pause(int p) static wchar_t oldtitle[512]; wchar_t title[512]; + gdbstub_pause(&p); + /* If un-pausing, as the renderer if that's OK. */ if (p == 0) p = get_vidpause(); From 25ac5e0edff8a59c0ad08cff22961f007862485b Mon Sep 17 00:00:00 2001 From: ts-korhonen Date: Mon, 14 Mar 2022 19:45:47 +0200 Subject: [PATCH 30/62] qt: Fix joystick dialog size. Set fixed size as a constraint to allow the dialog conform to selections instead of staying the initial minimum size. --- src/qt/qt_joystickconfiguration.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/qt/qt_joystickconfiguration.cpp b/src/qt/qt_joystickconfiguration.cpp index 594a1ef17..e5d70a68e 100644 --- a/src/qt/qt_joystickconfiguration.cpp +++ b/src/qt/qt_joystickconfiguration.cpp @@ -43,7 +43,7 @@ JoystickConfiguration::JoystickConfiguration(int type, int joystick_nr, QWidget } ui->comboBoxDevice->setCurrentIndex(joystick_state[joystick_nr].plat_joystick_nr); - setFixedSize(minimumSizeHint()); + layout()->setSizeConstraint(QLayout::SetFixedSize); } JoystickConfiguration::~JoystickConfiguration() @@ -197,6 +197,4 @@ void JoystickConfiguration::on_comboBoxDevice_currentIndexChanged(int index) { ++row; } - - setFixedSize(minimumSizeHint()); } From d12a92141bf0b927cb6727cfa9fe0282a9577713 Mon Sep 17 00:00:00 2001 From: ts-korhonen Date: Mon, 14 Mar 2022 19:50:20 +0200 Subject: [PATCH 31/62] qt: Fix joystick dialog title --- src/qt/qt_joystickconfiguration.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt/qt_joystickconfiguration.ui b/src/qt/qt_joystickconfiguration.ui index abe17b5cc..7789b48c4 100644 --- a/src/qt/qt_joystickconfiguration.ui +++ b/src/qt/qt_joystickconfiguration.ui @@ -11,7 +11,7 @@ - Dialog + Joystick configuration From 4b8b7c3950ebfb92949684218e38a07befa8f0f6 Mon Sep 17 00:00:00 2001 From: Alexander Babikov Date: Tue, 15 Mar 2022 04:19:10 +0500 Subject: [PATCH 32/62] Add handling for several missing strings to plat_get_string on Qt and legacy Unix UIs --- src/qt/qt_platform.cpp | 3 +++ src/unix/unix.c | 10 +++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/qt/qt_platform.cpp b/src/qt/qt_platform.cpp index 855e8a04c..508103169 100644 --- a/src/qt/qt_platform.cpp +++ b/src/qt/qt_platform.cpp @@ -533,9 +533,11 @@ void ProgSettings::reloadStrings() translatedstrings[IDS_2078] = QCoreApplication::translate("", "Press F8+F12 to release mouse").replace("F8+F12", MOUSE_CAPTURE_KEYSEQ).replace("CTRL-END", QLocale::system().name() == "de_DE" ? "Strg+Ende" : "CTRL-END").toStdWString(); translatedstrings[IDS_2079] = QCoreApplication::translate("", "Press F8+F12 or middle button to release mouse").replace("F8+F12", MOUSE_CAPTURE_KEYSEQ).replace("CTRL-END", QLocale::system().name() == "de_DE" ? "Strg+Ende" : "CTRL-END").toStdWString(); translatedstrings[IDS_2080] = QCoreApplication::translate("", "Failed to initialize FluidSynth").toStdWString(); + translatedstrings[IDS_2130] = QCoreApplication::translate("", "Invalid configuration").toStdWString(); translatedstrings[IDS_4099] = QCoreApplication::translate("", "MFM/RLL or ESDI CD-ROM drives never existed").toStdWString(); translatedstrings[IDS_2093] = QCoreApplication::translate("", "Failed to set up PCap").toStdWString(); translatedstrings[IDS_2094] = QCoreApplication::translate("", "No PCap devices found").toStdWString(); + translatedstrings[IDS_2095] = QCoreApplication::translate("", "Invalid PCap device").toStdWString(); translatedstrings[IDS_2110] = QCoreApplication::translate("", "Unable to initialize FreeType").toStdWString(); translatedstrings[IDS_2111] = QCoreApplication::translate("", "Unable to initialize SDL, libsdl2 is required").toStdWString(); translatedstrings[IDS_2129] = QCoreApplication::translate("", "Make sure libpcap is installed and that you are on a libpcap-compatible network connection.").toStdWString(); @@ -543,6 +545,7 @@ void ProgSettings::reloadStrings() translatedstrings[IDS_2063] = QCoreApplication::translate("", "Machine \"%hs\" is not available due to missing ROMs in the roms/machines directory. Switching to an available machine.").toStdWString(); translatedstrings[IDS_2064] = QCoreApplication::translate("", "Video card \"%hs\" is not available due to missing ROMs in the roms/video directory. Switching to an available video card.").toStdWString(); translatedstrings[IDS_2128] = QCoreApplication::translate("", "Hardware not available").toStdWString(); + translatedstrings[IDS_2142] = QCoreApplication::translate("", "Monitor in sleep mode").toStdWString(); translatedstrings[IDS_2120] = QCoreApplication::translate("", "No ROMs found").toStdWString(); translatedstrings[IDS_2056] = QCoreApplication::translate("", "86Box could not find any usable ROM images.\n\nPlease download a ROM set and extract it into the \"roms\" directory.").replace("roms", ROMDIR).toStdWString(); diff --git a/src/unix/unix.c b/src/unix/unix.c index 4e7e3602d..47931fda6 100644 --- a/src/unix/unix.c +++ b/src/unix/unix.c @@ -229,19 +229,23 @@ wchar_t* plat_get_string(int i) switch (i) { case IDS_2077: - return L"Click to capture mouse."; + return L"Click to capture mouse"; case IDS_2078: return L"Press CTRL-END to release mouse"; case IDS_2079: return L"Press CTRL-END or middle button to release mouse"; case IDS_2080: return L"Failed to initialize FluidSynth"; + case IDS_2130: + return L"Invalid configuration"; case IDS_4099: return L"MFM/RLL or ESDI CD-ROM drives never existed"; case IDS_2093: return L"Failed to set up PCap"; case IDS_2094: return L"No PCap devices found"; + case IDS_2095: + return L"Invalid PCap device"; case IDS_2110: return L"Unable to initialize FreeType"; case IDS_2111: @@ -250,6 +254,8 @@ wchar_t* plat_get_string(int i) return L"libfreetype is required for ESC/P printer emulation."; case IDS_2132: return L"libgs is required for automatic conversion of PostScript files to PDF.\n\nAny documents sent to the generic PostScript printer will be saved as PostScript (.ps) files."; + case IDS_2133: + return L"libfluidsynth is required for FluidSynth MIDI output."; case IDS_2129: return L"Make sure libpcap is installed and that you are on a libpcap-compatible network connection."; case IDS_2114: @@ -260,6 +266,8 @@ wchar_t* plat_get_string(int i) return L"Video card \"%hs\" is not available due to missing ROMs in the roms/video directory. Switching to an available video card."; case IDS_2128: return L"Hardware not available"; + case IDS_2142: + return L"Monitor in sleep mode"; } return L""; } From 3c3e42fb84373faff012aa09ac585edac12376a4 Mon Sep 17 00:00:00 2001 From: ts-korhonen Date: Tue, 15 Mar 2022 18:24:38 +0200 Subject: [PATCH 33/62] qt: Destroy the widgets when changing joystick device. Just removing them from layout will not destroy them. --- src/qt/qt_joystickconfiguration.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/qt/qt_joystickconfiguration.cpp b/src/qt/qt_joystickconfiguration.cpp index e5d70a68e..54aec5c6d 100644 --- a/src/qt/qt_joystickconfiguration.cpp +++ b/src/qt/qt_joystickconfiguration.cpp @@ -82,7 +82,9 @@ int JoystickConfiguration::selectedPov(int pov) { void JoystickConfiguration::on_comboBoxDevice_currentIndexChanged(int index) { for (auto w : widgets) { ui->ct->removeWidget(w); + w->deleteLater(); } + widgets.clear(); if (index == 0) { return; From 88b201b37f710975563ed48be1b702b7839d4304 Mon Sep 17 00:00:00 2001 From: richardg867 Date: Tue, 15 Mar 2022 19:52:17 -0300 Subject: [PATCH 34/62] Jenkins: Update build script with Windows dependency management, FAudio on Linux --- .ci/build.sh | 174 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 147 insertions(+), 27 deletions(-) diff --git a/.ci/build.sh b/.ci/build.sh index 493f42789..e9f9075a4 100644 --- a/.ci/build.sh +++ b/.ci/build.sh @@ -34,7 +34,7 @@ # # Define common functions. -alias is_windows='[ ! -z "$MSYSTEM" ]' +alias is_windows='[ -n "$MSYSTEM" ]' alias is_mac='uname -s | grep -q Darwin' make_tar() { @@ -62,7 +62,7 @@ make_tar() { fi # Make tar verbose if requested. - [ ! -z "$VERBOSE" ] && local compression_flag="$compression_flag -v" + [ -n "$VERBOSE" ] && local compression_flag="$compression_flag -v" # tar is notorious for having many diverging implementations. For instance, # the flags we use to strip UID/GID metadata can be --owner/group (GNU), @@ -136,7 +136,7 @@ done cmake_flags_extra= # Check if mandatory arguments were specified. -if [ -z "$package_name" -a -z "$tarball_name" ] || [ ! -z "$package_name" -a -z "$arch" ] +if [ -z "$package_name" -a -z "$tarball_name" ] || [ -n "$package_name" -a -z "$arch" ] then echo '[!] Usage: build.sh -b {package_name} {architecture} [-t] [cmake_flags...]' echo ' build.sh -s {source_tarball_name}' @@ -147,7 +147,7 @@ fi cd "$(dirname "$0")/.." # Make source tarball if requested. -if [ ! -z "$tarball_name" ] +if [ -n "$tarball_name" ] then echo [-] Making source tarball [$tarball_name] @@ -211,6 +211,106 @@ then fi echo [-] Using MSYSTEM [$MSYSTEM] + # Update keyring, as the package signing keys sometimes change. + echo [-] Updating package databases and keyring + yes | pacman -Sy --needed msys2-keyring + + # Query installed packages. + pacman -Qe > pacman.txt + + # Download the specified versions of architecture-specific dependencies. + echo -n [-] Downloading dependencies: + pkg_dir="/var/cache/pacman/pkg" + repo_base="https://repo.msys2.org/mingw/$(echo $MSYSTEM | tr '[:upper:]' '[:lower:]')" + pkgs="" + while IFS=" " read pkg version + do + prefixed_pkg="$MINGW_PACKAGE_PREFIX-$pkg" + installed_version=$(grep -E "^$prefixed_pkg " pacman.txt | cut -d " " -f 2) + if [ "$installed_version" != "$version" ] # installed_version will be empty if not installed + then + echo -n " [$pkg" + + # Download package if not already present in the local cache. + pkg_tar="$prefixed_pkg-$version-any.pkg.tar" + if [ -s "$pkg_dir/$pkg_tar.xz" ] + then + pkg_fn="$pkg_tar.xz" + pkg_dest="$pkg_dir/$pkg_fn" + else + pkg_fn="$pkg_tar.zst" + pkg_dest="$pkg_dir/$pkg_fn" + if [ ! -s "$pkg_dest" ] + then + if ! wget -qO "$pkg_dest" "$repo_base/$pkg_fn" + then + rm -f "$pkg_dest" + pkg_fn="$pkg_tar.xz" + pkg_dest="$pkg_dir/$pkg_fn" + wget -qO "$pkg_dest" "$repo_base/$pkg_fn" || rm -f "$pkg_dest" + fi + if [ -s "$pkg_dest" ] + then + wget -qO "$pkg_dest.sig" "$repo_base/$pkg_fn.sig" || rm -f "$pkg_dest.sig" + [ ! -s "$pkg_dest.sig" ] && rm -f "$pkg_dest.sig" + fi + fi + fi + + # Check if the cached package is valid. + if [ -s "$pkg_dest" ] + then + # Add cached zst package. + pkgs="$pkgs $pkg_fn" + else + # Not valid, remove if it exists. + rm -f "$pkg_dest" "$pkg_dest.sig" + echo -n " FAIL" + fi + echo -n "]" + fi + done < <(cat .ci/dependencies_msys.txt | tr -d '\r') + [ -z "$pkgs" ] && echo -n none required + echo + + # Install the downloaded architecture-specific dependencies. + echo [-] Installing dependencies through pacman + if [ -n "$pkgs" ] + then + pushd "$pkg_dir" + yes | pacman -U --needed $pkgs + if [ $? -ne 0 ] + then + # Install packages individually if installing them all together failed. + for pkg in pkgs + do + yes | pacman -U --needed "$pkg" + done + fi + popd + + # Query installed packages again. + pacman -Qe > pacman.txt + fi + + # Install the latest versions for any missing packages (if the specified version couldn't be installed). + pkgs="make git" + while IFS=" " read pkg version + do + prefixed_pkg="$MINGW_PACKAGE_PREFIX-$pkg" + grep -qE "^$prefixed_pkg " pacman.txt || pkgs="$pkgs $prefixed_pkg" + done < <(cat .ci/dependencies_msys.txt | tr -d '\r') + rm -f pacman.txt + yes | pacman -S --needed $pkgs + if [ $? -ne 0 ] + then + # Install packages individually if installing them all together failed. + for pkg in pkgs + do + yes | pacman -S --needed "$pkg" + done + fi + # Point CMake to the toolchain file. cmake_flags_extra="$cmake_flags_extra -D \"CMAKE_TOOLCHAIN_FILE=cmake/$toolchain.cmake\"" elif is_mac @@ -227,7 +327,7 @@ else esac # Establish general dependencies. - pkgs="cmake pkg-config git imagemagick wget p7zip-full wayland-protocols tar gzip" + pkgs="cmake pkg-config git imagemagick wget p7zip-full wayland-protocols tar gzip file" if [ "$(dpkg --print-architecture)" = "$arch_deb" ] then pkgs="$pkgs build-essential" @@ -242,7 +342,7 @@ else # ...and the ones we do want listed. Non-dev packages fill missing spots on the list. libpkgs="" longest_libpkg=0 - for pkg in libc6-dev libstdc++6 libopenal-dev libfreetype6-dev libx11-dev libsdl2-dev libpng-dev librtmidi-dev qtdeclarative5-dev libwayland-dev libevdev-dev libglib2.0-dev libslirp-dev + for pkg in libc6-dev libstdc++6 libopenal-dev libfreetype6-dev libx11-dev libsdl2-dev libpng-dev librtmidi-dev qtdeclarative5-dev libwayland-dev libevdev-dev libglib2.0-dev libslirp-dev libfaudio-dev libaudio-dev libjack-jackd2-dev libpipewire-0.3-dev libsamplerate0-dev libsndio-dev do libpkgs="$libpkgs $pkg:$arch_deb" length=$(echo -n $pkg | sed 's/-dev$//' | sed "s/qtdeclarative/qt/" | wc -c) @@ -298,9 +398,6 @@ EOF # Link against the system libslirp instead of compiling ours. cmake_flags_extra="$cmake_flags_extra -D SLIRP_EXTERNAL=ON" - - # Use OpenAL for Linux builds before FAudio builds are set up. - cmake_flags_extra="$cmake_flags_extra -D OPENAL=ON" fi # Clean workspace. @@ -327,16 +424,16 @@ if [ "$CI" = "true" ] then # Backup strategy when running under Jenkins. [ -z "$git_hash" ] && git_hash=$(echo $GIT_COMMIT | cut -c 1-8) -elif [ ! -z "$git_hash" ] +elif [ -n "$git_hash" ] then # Append + to denote a dirty tree. git diff --quiet 2> /dev/null || git_hash="$git_hash+" fi -[ ! -z "$git_hash" ] && cmake_flags_extra="$cmake_flags_extra -D \"EMU_GIT_HASH=$git_hash\"" +[ -n "$git_hash" ] && cmake_flags_extra="$cmake_flags_extra -D \"EMU_GIT_HASH=$git_hash\"" # Add copyright year. year=$(date +%Y) -[ ! -z "$year" ] && cmake_flags_extra="$cmake_flags_extra -D \"EMU_COPYRIGHT_YEAR=$year\"" +[ -n "$year" ] && cmake_flags_extra="$cmake_flags_extra -D \"EMU_COPYRIGHT_YEAR=$year\"" # Run CMake. echo [-] Running CMake with flags [$cmake_flags $cmake_flags_extra] @@ -432,19 +529,42 @@ then else cwd_root=$(pwd) - # Build openal-soft 1.21.1 manually to fix audio issues. This is a temporary - # workaround until a newer version of openal-soft trickles down to Debian repos. - if [ -d "openal-soft-1.21.1" ] + if grep -q "OPENAL:BOOL=ON" build/CMakeCache.txt then - rm -rf openal-soft-1.21.1/build/* + # Build openal-soft 1.21.1 manually to fix audio issues. This is a temporary + # workaround until a newer version of openal-soft trickles down to Debian repos. + if [ -d "openal-soft-1.21.1" ] + then + rm -rf openal-soft-1.21.1/build/* + else + wget -qO - https://github.com/kcat/openal-soft/archive/refs/tags/1.21.1.tar.gz | tar zxf - + fi + cd openal-soft-1.21.1/build + [ -e Makefile ] && make clean + cmake -G "Unix Makefiles" -D "CMAKE_TOOLCHAIN_FILE=$cwd_root/toolchain.cmake" -D "CMAKE_INSTALL_PREFIX=$cwd_root/archive_tmp/usr" .. + make -j$(nproc) install || exit 99 + cd "$cwd_root" + + # Build SDL2 without sound systems. + sdl_ss=OFF else - wget -qO - https://github.com/kcat/openal-soft/archive/refs/tags/1.21.1.tar.gz | tar zxf - + # Build FAudio 22.03 manually to remove the dependency on GStreamer. This is a temporary + # workaround until a newer version of FAudio trickles down to Debian repos. + if [ -d "FAudio-22.03" ] + then + rm -rf FAudio-22.03/build + else + wget -qO - https://github.com/FNA-XNA/FAudio/archive/refs/tags/22.03.tar.gz | tar zxf - + fi + mkdir FAudio-22.03/build + cd FAudio-22.03/build + cmake -G "Unix Makefiles" -D "CMAKE_TOOLCHAIN_FILE=$cwd_root/toolchain.cmake" -D "CMAKE_INSTALL_PREFIX=$cwd_root/archive_tmp/usr" .. + make -j$(nproc) install || exit 99 + cd "$cwd_root" + + # Build SDL2 with sound systems. + sdl_ss=ON fi - cd openal-soft-1.21.1/build - [ -e Makefile ] && make clean - cmake -G "Unix Makefiles" -D "CMAKE_TOOLCHAIN_FILE=$cwd_root/toolchain.cmake" -D "CMAKE_INSTALL_PREFIX=$cwd_root/archive_tmp/usr" .. - make -j$(nproc) install || exit 99 - cd "$cwd_root" # Build rtmidi without JACK support to remove the dependency on libjack. if [ -d "rtmidi-4.0.0" ] @@ -468,10 +588,10 @@ else rm -rf sdlbuild mkdir sdlbuild cd sdlbuild - cmake -G "Unix Makefiles" -D SDL_DISKAUDIO=OFF -D SDL_DIRECTFB_SHARED=OFF -D SDL_OPENGL=OFF -D SDL_OPENGLES=OFF -D SDL_OSS=OFF -D SDL_ALSA=OFF \ - -D SDL_ALSA_SHARED=OFF -D SDL_JACK=OFF -D SDL_JACK_SHARED=OFF -D SDL_ESD=OFF -D SDL_ESD_SHARED=OFF -D SDL_PIPEWIRE=OFF -D SDL_PIPEWIRE_SHARED=OFF \ - -D SDL_PULSEAUDIO=OFF -D SDL_PULSEAUDIO_SHARED=OFF -D SDL_ARTS=OFF -D SDL_ARTS_SHARED=OFF -D SDL_NAS=OFF -D SDL_NAS_SHARED=OFF -D SDL_SNDIO=OFF \ - -D SDL_SNDIO_SHARED=OFF -D SDL_FUSIONSOUND=OFF -D SDL_FUSIONSOUND_SHARED=OFF -D SDL_LIBSAMPLERATE=OFF -D SDL_LIBSAMPLERATE_SHARED=OFF -D SDL_X11=OFF \ + cmake -G "Unix Makefiles" -D SDL_DISKAUDIO=OFF -D SDL_DIRECTFB_SHARED=OFF -D SDL_OPENGL=OFF -D SDL_OPENGLES=OFF -D SDL_OSS=OFF -D SDL_ALSA=$sdl_ss \ + -D SDL_ALSA_SHARED=$sdl_ss -D SDL_JACK=$sdl_ss -D SDL_JACK_SHARED=$sdl_ss -D SDL_ESD=OFF -D SDL_ESD_SHARED=OFF -D SDL_PIPEWIRE=$sdl_ss -D SDL_PIPEWIRE_SHARED=$sdl_ss \ + -D SDL_PULSEAUDIO=$sdl_ss -D SDL_PULSEAUDIO_SHARED=$sdl_ss -D SDL_ARTS=OFF -D SDL_ARTS_SHARED=OFF -D SDL_NAS=$sdl_ss -D SDL_NAS_SHARED=$sdl_ss -D SDL_SNDIO=$sdl_ss \ + -D SDL_SNDIO_SHARED=$sdl_ss -D SDL_FUSIONSOUND=OFF -D SDL_FUSIONSOUND_SHARED=OFF -D SDL_LIBSAMPLERATE=$sdl_ss -D SDL_LIBSAMPLERATE_SHARED=$sdl_ss -D SDL_X11=OFF \ -D SDL_X11_SHARED=OFF -D SDL_WAYLAND=OFF -D SDL_WAYLAND_SHARED=OFF -D SDL_WAYLAND_LIBDECOR=OFF -D SDL_WAYLAND_LIBDECOR_SHARED=OFF \ -D SDL_WAYLAND_QT_TOUCH=OFF -D SDL_RPI=OFF -D SDL_VIVANTE=OFF -D SDL_VULKAN=OFF -D SDL_KMSDRM=OFF -D SDL_KMSDRM_SHARED=OFF -D SDL_OFFSCREEN=OFF \ -D SDL_HIDAPI_JOYSTICK=ON -D SDL_VIRTUAL_JOYSTICK=ON -D SDL_SHARED=ON -D SDL_STATIC=OFF -S "$cwd_root/SDL2-2.0.20" \ @@ -544,7 +664,7 @@ else project_version=$(grep -oP '#define\s+EMU_VERSION\s+"\K([^"]+)' "build/src/include/$project_lower/version.h" 2> /dev/null) [ -z "$project_version" ] && project_version=unknown build_num=$(grep -oP '#define\s+EMU_BUILD_NUM\s+\K([0-9]+)' "build/src/include/$project_lower/version.h" 2> /dev/null) - [ ! -z "$build_num" -a "$build_num" != "0" ] && project_version="$project_version-b$build_num" + [ -n "$build_num" -a "$build_num" != "0" ] && project_version="$project_version-b$build_num" # Download appimage-builder if necessary. [ ! -e "appimage-builder.AppImage" ] && wget -qO appimage-builder.AppImage \ From 95b145d118ecda4e7094c22d73f12cc15d6c9d53 Mon Sep 17 00:00:00 2001 From: richardg867 Date: Tue, 15 Mar 2022 19:55:39 -0300 Subject: [PATCH 35/62] Jenkins: Add MSYS2 dependencies file I forgot --- .ci/dependencies_msys.txt | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .ci/dependencies_msys.txt diff --git a/.ci/dependencies_msys.txt b/.ci/dependencies_msys.txt new file mode 100644 index 000000000..674f1121a --- /dev/null +++ b/.ci/dependencies_msys.txt @@ -0,0 +1,20 @@ +zlib 1.2.11-9 +binutils 2.37-4 +headers-git 9.0.0.6357.eac8c38c1-1 +crt-git 9.0.0.6357.eac8c38c1-2 +libwinpthread-git 9.0.0.6357.eac8c38c1-1 +winpthreads-git 9.0.0.6357.eac8c38c1-1 +winstorecompat-git 9.0.0.6357.eac8c38c1-1 +gcc-libs 11.2.0-4 +gcc 11.2.0-4 +libgccjit 11.2.0-4 +tools-git 9.0.0.6357.eac8c38c1-1 +make 4.3-1 +pkgconf 1.8.0-2 +openal 1.21.1-3 +libpng 1.6.37-6 +freetype 2.11.1-1 +SDL2 2.0.18-2 +rtmidi 4.0.0-1 +cmake 3.22.1-1 +qt5-static 5.15.2-4 From fa795f0f500380fba584b018374e2f5fbc213210 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Tue, 15 Mar 2022 21:23:51 -0300 Subject: [PATCH 36/62] Jenkins: Fix build script sh compatibility --- .ci/build.sh | 7 ++++--- .gitignore | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.ci/build.sh b/.ci/build.sh index e9f9075a4..6e0257876 100644 --- a/.ci/build.sh +++ b/.ci/build.sh @@ -222,6 +222,7 @@ then echo -n [-] Downloading dependencies: pkg_dir="/var/cache/pacman/pkg" repo_base="https://repo.msys2.org/mingw/$(echo $MSYSTEM | tr '[:upper:]' '[:lower:]')" + cat .ci/dependencies_msys.txt | tr -d '\r' > deps.txt pkgs="" while IFS=" " read pkg version do @@ -269,7 +270,7 @@ then fi echo -n "]" fi - done < <(cat .ci/dependencies_msys.txt | tr -d '\r') + done < deps.txt [ -z "$pkgs" ] && echo -n none required echo @@ -299,8 +300,8 @@ then do prefixed_pkg="$MINGW_PACKAGE_PREFIX-$pkg" grep -qE "^$prefixed_pkg " pacman.txt || pkgs="$pkgs $prefixed_pkg" - done < <(cat .ci/dependencies_msys.txt | tr -d '\r') - rm -f pacman.txt + done < deps.txt + rm -f pacman.txt deps.txt yes | pacman -S --needed $pkgs if [ $? -ne 0 ] then diff --git a/.gitignore b/.gitignore index 85bbd51d8..a38da5deb 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ Makefile /archive_tmp /static2dll.* /pacman.txt +/deps.txt /VERSION *.zip *.tar From 5d91eb14862f44e9d1a0f3dd903732e96cc6c980 Mon Sep 17 00:00:00 2001 From: richardg867 Date: Tue, 15 Mar 2022 21:31:40 -0300 Subject: [PATCH 37/62] Jenkins: Fix build script sh compatibility --- .ci/build.sh | 1401 +++++++++++++++++++++++++------------------------- 1 file changed, 701 insertions(+), 700 deletions(-) diff --git a/.ci/build.sh b/.ci/build.sh index e9f9075a4..447cc498d 100644 --- a/.ci/build.sh +++ b/.ci/build.sh @@ -1,700 +1,701 @@ -#!/bin/sh -# -# 86Box A hypervisor and IBM PC system emulator that specializes in -# running old operating systems and software designed for IBM -# PC systems and compatibles from 1981 through fairly recent -# system designs based on the PCI bus. -# -# This file is part of the 86Box distribution. -# -# Jenkins build script. -# -# -# Authors: RichardG, -# -# Copyright 2021-2022 RichardG. -# - -# -# While this script was made for our Jenkins infrastructure, you can run it -# to produce Jenkins-like builds on your local machine by following these notes: -# -# - Run build.sh without parameters to see its usage -# - For Windows (MSYS MinGW) builds: -# - Packaging requires 7-Zip on Program Files -# - Packaging the Ghostscript DLL requires 32-bit and/or 64-bit Ghostscript on Program Files -# - Packaging the FluidSynth DLL requires it to be at /home/86Box/dll32/libfluidsynth.dll -# and/or /home/86Box/dll64/libfluidsynth64.dll (for 32-bit and 64-bit builds respectively) -# - Packaging the Discord DLL requires wget (MSYS should come with it) -# - For Linux builds: -# - Only Debian and derivatives are supported -# - dpkg and apt-get are called through sudo to manage dependencies -# - For macOS builds: -# - TBD -# - -# Define common functions. -alias is_windows='[ -n "$MSYSTEM" ]' -alias is_mac='uname -s | grep -q Darwin' - -make_tar() { - # Install dependencies. - if ! which tar xz > /dev/null 2>&1 - then - which apt-get > /dev/null 2>&1 && DEBIAN_FRONTEND=noninteractive sudo apt-get install -y tar xz-utils - fi - - # Determine the best supported compression type. - local compression_flag= - local compression_ext= - if which xz > /dev/null 2>&1 - then - local compression_flag=-J - local compression_ext=.xz - elif which bzip2 > /dev/null 2>&1 - then - local compression_flag=-j - local compression_ext=.bz2 - elif which gzip > /dev/null 2>&1 - then - local compression_flag=-z - local compression_ext=.gz - fi - - # Make tar verbose if requested. - [ -n "$VERBOSE" ] && local compression_flag="$compression_flag -v" - - # tar is notorious for having many diverging implementations. For instance, - # the flags we use to strip UID/GID metadata can be --owner/group (GNU), - # --uid/gid (bsdtar) or even none at all (MSYS2 bsdtar). Account for such - # flag differences by checking if they're mentioned on the help text. - local ownership_flags= - local tar_help=$(tar --help 2>&1) - if echo $tar_help | grep -q -- --owner - then - local ownership_flags="--owner=0 --group=0" - elif echo $tar_help | grep -q -- --uid - then - local ownership_flags="--uid 0 --gid 0" - fi - - # Run tar. - tar -c $compression_flag -f "$1$compression_ext" $ownership_flags * - return $? -} - -# Set common variables. -project=86Box -project_lower=86box -cwd=$(pwd) - -# Parse arguments. -package_name= -arch= -tarball_name= -strip=0 -cmake_flags= -while [ $# -gt 0 ] -do - case $1 in - -b) - shift - package_name="$1" - shift - arch="$1" - shift - ;; - - -s) - shift - tarball_name="$1" - shift - ;; - - -t) - shift - strip=1 - ;; - - *) - if echo $1 | grep -q " " - then - cmake_flag="\"$1\"" - else - cmake_flag="$1" - fi - if [ -z "$cmake_flags" ] - then - cmake_flags="$cmake_flag" - else - cmake_flags="$cmake_flags $cmake_flag" - fi - shift - ;; - esac -done -cmake_flags_extra= - -# Check if mandatory arguments were specified. -if [ -z "$package_name" -a -z "$tarball_name" ] || [ -n "$package_name" -a -z "$arch" ] -then - echo '[!] Usage: build.sh -b {package_name} {architecture} [-t] [cmake_flags...]' - echo ' build.sh -s {source_tarball_name}' - exit 100 -fi - -# Switch to the repository root directory. -cd "$(dirname "$0")/.." - -# Make source tarball if requested. -if [ -n "$tarball_name" ] -then - echo [-] Making source tarball [$tarball_name] - - # Clean local tree of gitignored files. - git clean -dfX - - # Recreate working directory if it was removed by git clean. - [ ! -d "$cwd" ] && mkdir -p "$cwd" - - # Save current HEAD commit to VERSION. - git log --stat -1 > VERSION || rm -f VERSION - - # Archive source. - make_tar "$cwd/$tarball_name.tar" - status=$? - - # Check if the archival succeeded. - if [ $status -ne 0 ] - then - echo [!] Tarball creation failed with status [$status] - exit 1 - else - echo [-] Source tarball [$tarball_name] created successfully - [ -z "$package_name" ] && exit 0 - fi -fi - -echo [-] Building [$package_name] for [$arch] with flags [$cmake_flags] - -# Determine CMake toolchain file for this architecture. -case $arch in - 32 | x86) toolchain="flags-gcc-i686";; - 64 | x86_64) toolchain="flags-gcc-x86_64";; - ARM32 | arm32) toolchain="flags-gcc-armv7";; - ARM64 | arm64) toolchain="flags-gcc-aarch64";; - *) toolchain="flags-gcc-$arch";; -esac - -# Perform platform-specific setup. -strip_binary=strip -if is_windows -then - # Switch into the correct MSYSTEM if required. - msys=MINGW$arch - [ ! -d "/$msys" ] && msys=CLANG$arch - if [ -d "/$msys" ] - then - if [ "$MSYSTEM" != "$msys" ] - then - # Call build with the correct MSYSTEM. - echo [-] Switching to MSYSTEM [$msys] - cd "$cwd" - strip_arg= - [ $strip -ne 0 ] && strip_arg="-t " - CHERE_INVOKING=yes MSYSTEM="$msys" bash -lc 'exec "'"$0"'" -b "'"$package_name"'" "'"$arch"'" '"$strip_arg""$cmake_flags" - exit $? - fi - else - echo [!] No MSYSTEM for architecture [$arch] - exit 2 - fi - echo [-] Using MSYSTEM [$MSYSTEM] - - # Update keyring, as the package signing keys sometimes change. - echo [-] Updating package databases and keyring - yes | pacman -Sy --needed msys2-keyring - - # Query installed packages. - pacman -Qe > pacman.txt - - # Download the specified versions of architecture-specific dependencies. - echo -n [-] Downloading dependencies: - pkg_dir="/var/cache/pacman/pkg" - repo_base="https://repo.msys2.org/mingw/$(echo $MSYSTEM | tr '[:upper:]' '[:lower:]')" - pkgs="" - while IFS=" " read pkg version - do - prefixed_pkg="$MINGW_PACKAGE_PREFIX-$pkg" - installed_version=$(grep -E "^$prefixed_pkg " pacman.txt | cut -d " " -f 2) - if [ "$installed_version" != "$version" ] # installed_version will be empty if not installed - then - echo -n " [$pkg" - - # Download package if not already present in the local cache. - pkg_tar="$prefixed_pkg-$version-any.pkg.tar" - if [ -s "$pkg_dir/$pkg_tar.xz" ] - then - pkg_fn="$pkg_tar.xz" - pkg_dest="$pkg_dir/$pkg_fn" - else - pkg_fn="$pkg_tar.zst" - pkg_dest="$pkg_dir/$pkg_fn" - if [ ! -s "$pkg_dest" ] - then - if ! wget -qO "$pkg_dest" "$repo_base/$pkg_fn" - then - rm -f "$pkg_dest" - pkg_fn="$pkg_tar.xz" - pkg_dest="$pkg_dir/$pkg_fn" - wget -qO "$pkg_dest" "$repo_base/$pkg_fn" || rm -f "$pkg_dest" - fi - if [ -s "$pkg_dest" ] - then - wget -qO "$pkg_dest.sig" "$repo_base/$pkg_fn.sig" || rm -f "$pkg_dest.sig" - [ ! -s "$pkg_dest.sig" ] && rm -f "$pkg_dest.sig" - fi - fi - fi - - # Check if the cached package is valid. - if [ -s "$pkg_dest" ] - then - # Add cached zst package. - pkgs="$pkgs $pkg_fn" - else - # Not valid, remove if it exists. - rm -f "$pkg_dest" "$pkg_dest.sig" - echo -n " FAIL" - fi - echo -n "]" - fi - done < <(cat .ci/dependencies_msys.txt | tr -d '\r') - [ -z "$pkgs" ] && echo -n none required - echo - - # Install the downloaded architecture-specific dependencies. - echo [-] Installing dependencies through pacman - if [ -n "$pkgs" ] - then - pushd "$pkg_dir" - yes | pacman -U --needed $pkgs - if [ $? -ne 0 ] - then - # Install packages individually if installing them all together failed. - for pkg in pkgs - do - yes | pacman -U --needed "$pkg" - done - fi - popd - - # Query installed packages again. - pacman -Qe > pacman.txt - fi - - # Install the latest versions for any missing packages (if the specified version couldn't be installed). - pkgs="make git" - while IFS=" " read pkg version - do - prefixed_pkg="$MINGW_PACKAGE_PREFIX-$pkg" - grep -qE "^$prefixed_pkg " pacman.txt || pkgs="$pkgs $prefixed_pkg" - done < <(cat .ci/dependencies_msys.txt | tr -d '\r') - rm -f pacman.txt - yes | pacman -S --needed $pkgs - if [ $? -ne 0 ] - then - # Install packages individually if installing them all together failed. - for pkg in pkgs - do - yes | pacman -S --needed "$pkg" - done - fi - - # Point CMake to the toolchain file. - cmake_flags_extra="$cmake_flags_extra -D \"CMAKE_TOOLCHAIN_FILE=cmake/$toolchain.cmake\"" -elif is_mac -then - # macOS lacks nproc, but sysctl can do the same job. - alias nproc='sysctl -n hw.logicalcpu' -else - # Determine Debian architecture. - case $arch in - x86) arch_deb="i386";; - x86_64) arch_deb="amd64";; - arm32) arch_deb="armhf";; - *) arch_deb="$arch";; - esac - - # Establish general dependencies. - pkgs="cmake pkg-config git imagemagick wget p7zip-full wayland-protocols tar gzip file" - if [ "$(dpkg --print-architecture)" = "$arch_deb" ] - then - pkgs="$pkgs build-essential" - else - sudo dpkg --add-architecture "$arch_deb" - pkgs="$pkgs crossbuild-essential-$arch_deb" - fi - - # Establish architecture-specific dependencies we don't want listed on the readme... - pkgs="$pkgs linux-libc-dev:$arch_deb extra-cmake-modules:$arch_deb qttools5-dev:$arch_deb qtbase5-private-dev:$arch_deb" - - # ...and the ones we do want listed. Non-dev packages fill missing spots on the list. - libpkgs="" - longest_libpkg=0 - for pkg in libc6-dev libstdc++6 libopenal-dev libfreetype6-dev libx11-dev libsdl2-dev libpng-dev librtmidi-dev qtdeclarative5-dev libwayland-dev libevdev-dev libglib2.0-dev libslirp-dev libfaudio-dev libaudio-dev libjack-jackd2-dev libpipewire-0.3-dev libsamplerate0-dev libsndio-dev - do - libpkgs="$libpkgs $pkg:$arch_deb" - length=$(echo -n $pkg | sed 's/-dev$//' | sed "s/qtdeclarative/qt/" | wc -c) - [ $length -gt $longest_libpkg ] && longest_libpkg=$length - done - - # Determine GNU toolchain architecture. - case $arch in - x86) arch_gnu="i686-linux-gnu";; - arm32) arch_gnu="arm-linux-gnueabihf";; - arm64) arch_gnu="aarch64-linux-gnu";; - *) arch_gnu="$arch-linux-gnu";; - esac - - # Determine library directory name for this architecture. - case $arch in - x86) libdir="i386-linux-gnu";; - *) libdir="$arch_gnu";; - esac - - # Create CMake toolchain file. - cat << EOF > toolchain.cmake -set(CMAKE_SYSTEM_NAME Linux) -set(CMAKE_SYSTEM_PROCESSOR $arch) - -set(CMAKE_AR $arch_gnu-ar) -set(CMAKE_ASM_COMPILER $arch_gnu-gcc) -set(CMAKE_C_COMPILER $arch_gnu-gcc) -set(CMAKE_CXX_COMPILER $arch_gnu-g++) -set(CMAKE_LINKER $arch_gnu-ld) -set(CMAKE_OBJCOPY $arch_gnu-objcopy) -set(CMAKE_RANLIB $arch_gnu-ranlib) -set(CMAKE_SIZE $arch_gnu-size) -set(CMAKE_STRIP $arch_gnu-strip) - -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) - -set(ENV{PKG_CONFIG_PATH} "") -set(ENV{PKG_CONFIG_LIBDIR} "/usr/lib/$libdir/pkgconfig:/usr/share/$libdir/pkgconfig") - -include("$(pwd)/cmake/$toolchain.cmake") -EOF - cmake_flags_extra="$cmake_flags_extra -D CMAKE_TOOLCHAIN_FILE=toolchain.cmake" - strip_binary="$arch_gnu-strip" - - # Install or update dependencies. - echo [-] Installing dependencies through apt - sudo apt-get update - DEBIAN_FRONTEND=noninteractive sudo apt-get -y install $pkgs $libpkgs - sudo apt-get clean - - # Link against the system libslirp instead of compiling ours. - cmake_flags_extra="$cmake_flags_extra -D SLIRP_EXTERNAL=ON" -fi - -# Clean workspace. -echo [-] Cleaning workspace -if [ -d "build" ] -then - MAKEFLAGS=-j$(nproc) cmake --build build --target clean 2> /dev/null - rm -rf build -fi -find . \( -name Makefile -o -name CMakeCache.txt -o -name CMakeFiles \) -exec rm -rf "{}" \; 2> /dev/null - -# Add ARCH to skip the arch_detect process. -case $arch in - 32 | x86) cmake_flags_extra="$cmake_flags_extra -D ARCH=i386";; - 64 | x86_64) cmake_flags_extra="$cmake_flags_extra -D ARCH=x86_64";; - ARM32 | arm32) cmake_flags_extra="$cmake_flags_extra -D ARCH=arm";; - ARM64 | arm64) cmake_flags_extra="$cmake_flags_extra -D ARCH=arm64";; - *) cmake_flags_extra="$cmake_flags_extra -D \"ARCH=$arch\"";; -esac - -# Add git hash. -git_hash=$(git rev-parse --short HEAD 2> /dev/null) -if [ "$CI" = "true" ] -then - # Backup strategy when running under Jenkins. - [ -z "$git_hash" ] && git_hash=$(echo $GIT_COMMIT | cut -c 1-8) -elif [ -n "$git_hash" ] -then - # Append + to denote a dirty tree. - git diff --quiet 2> /dev/null || git_hash="$git_hash+" -fi -[ -n "$git_hash" ] && cmake_flags_extra="$cmake_flags_extra -D \"EMU_GIT_HASH=$git_hash\"" - -# Add copyright year. -year=$(date +%Y) -[ -n "$year" ] && cmake_flags_extra="$cmake_flags_extra -D \"EMU_COPYRIGHT_YEAR=$year\"" - -# Run CMake. -echo [-] Running CMake with flags [$cmake_flags $cmake_flags_extra] -eval cmake -G \"Unix Makefiles\" -B build $cmake_flags $cmake_flags_extra . -status=$? -if [ $status -ne 0 ] -then - echo [!] CMake failed with status [$status] - exit 3 -fi - -# Run actual build. -make_flags=-j$(nproc) -echo [-] Running build with make flags [$make_flags] -MAKEFLAGS=$make_flags cmake --build build -status=$? -if [ $status -ne 0 ] -then - echo [!] Make failed with status [$status] - exit 4 -fi - -# Download Discord Game SDK from their CDN if necessary. -if [ ! -e "discord_game_sdk.zip" ] -then - echo [-] Downloading Discord Game SDK - wget -qO discord_game_sdk.zip "https://dl-game-sdk.discordapp.net/2.5.6/discord_game_sdk.zip" - status=$? - if [ $status -ne 0 ] - then - echo [!] Discord Game SDK download failed with status [$status] - rm -f discord_game_sdk.zip - fi -fi - -# Determine Discord Game SDK architecture. -case $arch in - 32) arch_discord="x86";; - 64) arch_discord="x86_64";; - *) arch_discord="$arch";; -esac - -# Create temporary directory for archival. -echo [-] Gathering archive files -rm -rf archive_tmp -mkdir archive_tmp -if [ ! -d "archive_tmp" ] -then - echo [!] Archive directory creation failed - exit 5 -fi - -# Archive the executable and its dependencies. -# The executable should always be archived last for the check after this block. -status=0 -if is_windows -then - # Determine Program Files directory for Ghostscript and 7-Zip. - # Manual checks because MSYS is bad at passing the ProgramFiles variables. - pf="/c/Program Files" - sevenzip="$pf/7-Zip/7z.exe" - [ "$arch" = "32" -a -d "/c/Program Files (x86)" ] && pf="/c/Program Files (x86)" - - # Archive freetype from local MSYS installation. - .ci/static2dll.sh -p freetype2 /$MSYSTEM/lib/libfreetype.a archive_tmp/freetype.dll - - # Archive Ghostscript DLL from local official distribution installation. - for gs in "$pf"/gs/gs*.*.* - do - cp -p "$gs"/bin/gsdll*.dll archive_tmp/ - done - - # Archive Discord Game SDK DLL. - "$sevenzip" e -y -o"archive_tmp" discord_game_sdk.zip "lib/$arch_discord/discord_game_sdk.dll" - [ ! -e "archive_tmp/discord_game_sdk.dll" ] && echo [!] No Discord Game SDK for architecture [$arch_discord] - - # Archive other DLLs from local directory. - cp -p "/home/$project/dll$arch/"* archive_tmp/ - - # Archive executable, while also stripping it if requested. - if [ $strip -ne 0 ] - then - "$strip_binary" -o "archive_tmp/$project.exe" "build/src/$project.exe" - status=$? - else - mv "build/src/$project.exe" "archive_tmp/$project.exe" - status=$? - fi -elif is_mac -then - # TBD - : -else - cwd_root=$(pwd) - - if grep -q "OPENAL:BOOL=ON" build/CMakeCache.txt - then - # Build openal-soft 1.21.1 manually to fix audio issues. This is a temporary - # workaround until a newer version of openal-soft trickles down to Debian repos. - if [ -d "openal-soft-1.21.1" ] - then - rm -rf openal-soft-1.21.1/build/* - else - wget -qO - https://github.com/kcat/openal-soft/archive/refs/tags/1.21.1.tar.gz | tar zxf - - fi - cd openal-soft-1.21.1/build - [ -e Makefile ] && make clean - cmake -G "Unix Makefiles" -D "CMAKE_TOOLCHAIN_FILE=$cwd_root/toolchain.cmake" -D "CMAKE_INSTALL_PREFIX=$cwd_root/archive_tmp/usr" .. - make -j$(nproc) install || exit 99 - cd "$cwd_root" - - # Build SDL2 without sound systems. - sdl_ss=OFF - else - # Build FAudio 22.03 manually to remove the dependency on GStreamer. This is a temporary - # workaround until a newer version of FAudio trickles down to Debian repos. - if [ -d "FAudio-22.03" ] - then - rm -rf FAudio-22.03/build - else - wget -qO - https://github.com/FNA-XNA/FAudio/archive/refs/tags/22.03.tar.gz | tar zxf - - fi - mkdir FAudio-22.03/build - cd FAudio-22.03/build - cmake -G "Unix Makefiles" -D "CMAKE_TOOLCHAIN_FILE=$cwd_root/toolchain.cmake" -D "CMAKE_INSTALL_PREFIX=$cwd_root/archive_tmp/usr" .. - make -j$(nproc) install || exit 99 - cd "$cwd_root" - - # Build SDL2 with sound systems. - sdl_ss=ON - fi - - # Build rtmidi without JACK support to remove the dependency on libjack. - if [ -d "rtmidi-4.0.0" ] - then - rm -rf rtmidi-4.0.0/CMakeCache.txt rtmidi-4.0.0/CMakeFiles - else - wget -qO - http://www.music.mcgill.ca/~gary/rtmidi/release/rtmidi-4.0.0.tar.gz | tar zxf - - fi - cwd_root=$(pwd) - cd rtmidi-4.0.0 - [ -e Makefile ] && make clean - cmake -G "Unix Makefiles" -D RTMIDI_API_JACK=OFF -D "CMAKE_TOOLCHAIN_FILE=$cwd_root/toolchain.cmake" -D "CMAKE_INSTALL_PREFIX=$cwd_root/archive_tmp/usr" . - make -j$(nproc) install || exit 99 - cd "$cwd_root" - - # Build SDL2 for joystick support with most components disabled to remove the dependencies on PulseAudio and libdrm. - if [ ! -d "SDL2-2.0.20" ] - then - wget -qO - https://www.libsdl.org/release/SDL2-2.0.20.tar.gz | tar zxf - - fi - rm -rf sdlbuild - mkdir sdlbuild - cd sdlbuild - cmake -G "Unix Makefiles" -D SDL_DISKAUDIO=OFF -D SDL_DIRECTFB_SHARED=OFF -D SDL_OPENGL=OFF -D SDL_OPENGLES=OFF -D SDL_OSS=OFF -D SDL_ALSA=$sdl_ss \ - -D SDL_ALSA_SHARED=$sdl_ss -D SDL_JACK=$sdl_ss -D SDL_JACK_SHARED=$sdl_ss -D SDL_ESD=OFF -D SDL_ESD_SHARED=OFF -D SDL_PIPEWIRE=$sdl_ss -D SDL_PIPEWIRE_SHARED=$sdl_ss \ - -D SDL_PULSEAUDIO=$sdl_ss -D SDL_PULSEAUDIO_SHARED=$sdl_ss -D SDL_ARTS=OFF -D SDL_ARTS_SHARED=OFF -D SDL_NAS=$sdl_ss -D SDL_NAS_SHARED=$sdl_ss -D SDL_SNDIO=$sdl_ss \ - -D SDL_SNDIO_SHARED=$sdl_ss -D SDL_FUSIONSOUND=OFF -D SDL_FUSIONSOUND_SHARED=OFF -D SDL_LIBSAMPLERATE=$sdl_ss -D SDL_LIBSAMPLERATE_SHARED=$sdl_ss -D SDL_X11=OFF \ - -D SDL_X11_SHARED=OFF -D SDL_WAYLAND=OFF -D SDL_WAYLAND_SHARED=OFF -D SDL_WAYLAND_LIBDECOR=OFF -D SDL_WAYLAND_LIBDECOR_SHARED=OFF \ - -D SDL_WAYLAND_QT_TOUCH=OFF -D SDL_RPI=OFF -D SDL_VIVANTE=OFF -D SDL_VULKAN=OFF -D SDL_KMSDRM=OFF -D SDL_KMSDRM_SHARED=OFF -D SDL_OFFSCREEN=OFF \ - -D SDL_HIDAPI_JOYSTICK=ON -D SDL_VIRTUAL_JOYSTICK=ON -D SDL_SHARED=ON -D SDL_STATIC=OFF -S "$cwd_root/SDL2-2.0.20" \ - -D "CMAKE_TOOLCHAIN_FILE=$cwd_root/toolchain.cmake" -D "CMAKE_INSTALL_PREFIX=$cwd_root/archive_tmp/usr" - make -j$(nproc) install || exit 99 - cd "$cwd_root" - - # Archive Discord Game SDK library. - 7z e -y -o"archive_tmp/usr/lib" discord_game_sdk.zip "lib/$arch_discord/discord_game_sdk.so" - [ ! -e "archive_tmp/usr/lib/discord_game_sdk.so" ] && echo [!] No Discord Game SDK for architecture [$arch_discord] - - # Archive readme with library package versions. - echo Libraries used to compile this $arch build of $project: > archive_tmp/README - dpkg-query -f '${Package} ${Version}\n' -W $libpkgs | sed "s/-dev / /" | sed "s/qtdeclarative/qt/" | while IFS=" " read pkg version - do - for i in $(seq $(expr $longest_libpkg - $(echo -n $pkg | wc -c))) - do - echo -n " " >> archive_tmp/README - done - echo $pkg $version >> archive_tmp/README - done - - # Archive icon, while also shrinking it to 512x512 if necessary. - convert src/win/assets/$project_lower.png -resize '512x512>' icon.png - icon_base="$(identify -format 'archive_tmp/usr/share/icons/%wx%h' icon.png)" - mkdir -p "$icon_base" - mv icon.png "$icon_base/$project_lower.png" - - # Archive executable, while also stripping it if requested. - mkdir -p archive_tmp/usr/local/bin - if [ $strip -ne 0 ] - then - "$strip_binary" -o "archive_tmp/usr/local/bin/$project" "build/src/$project" - status=$? - else - mv "build/src/$project" "archive_tmp/usr/local/bin/$project" - status=$? - fi -fi - -# Check if the executable strip/move succeeded. -if [ $status -ne 0 ] -then - echo [!] Executable strip/move failed with status [$status] - exit 6 -fi - -# Produce artifact archive. -echo [-] Creating artifact archive -if is_windows -then - # Create zip. - cd archive_tmp - "$sevenzip" a -y "$(cygpath -w "$cwd")\\$package_name.zip" * - status=$? -elif is_mac -then - # TBD - : -else - # Determine AppImage runtime architecture. - case $arch in - x86) arch_appimage="i686";; - arm32) arch_appimage="armhf";; - arm64) arch_appimage="aarch64";; - *) arch_appimage="$arch";; - esac - - # Get version for AppImage metadata. - project_version=$(grep -oP '#define\s+EMU_VERSION\s+"\K([^"]+)' "build/src/include/$project_lower/version.h" 2> /dev/null) - [ -z "$project_version" ] && project_version=unknown - build_num=$(grep -oP '#define\s+EMU_BUILD_NUM\s+\K([0-9]+)' "build/src/include/$project_lower/version.h" 2> /dev/null) - [ -n "$build_num" -a "$build_num" != "0" ] && project_version="$project_version-b$build_num" - - # Download appimage-builder if necessary. - [ ! -e "appimage-builder.AppImage" ] && wget -qO appimage-builder.AppImage \ - https://github.com/AppImageCrafters/appimage-builder/releases/download/v0.9.2/appimage-builder-0.9.2-35e3eab-x86_64.AppImage - chmod u+x appimage-builder.AppImage - - # Remove any dangling AppImages which may interfere with the renaming process. - rm -rf "$project-"*".AppImage" - - # Run appimage-builder in extract-and-run mode for Docker compatibility. - project="$project" project_lower="$project_lower" project_version="$project_version" arch_deb="$arch_deb" arch_appimage="$arch_appimage" \ - APPIMAGE_EXTRACT_AND_RUN=1 ./appimage-builder.AppImage --recipe .ci/AppImageBuilder.yml - status=$? - - # Rename AppImage to the final name if the build succeeded. - if [ $status -eq 0 ] - then - mv "$project-"*".AppImage" "$cwd/$package_name.AppImage" - status=$? - fi -fi -cd .. - -# Check if the archival succeeded. -if [ $status -ne 0 ] -then - echo [!] Artifact archive creation failed with status [$status] - exit 7 -fi - -# All good. -echo [-] Build of [$package_name] for [$arch] with flags [$cmake_flags] successful -exit 0 +#!/bin/sh +# +# 86Box A hypervisor and IBM PC system emulator that specializes in +# running old operating systems and software designed for IBM +# PC systems and compatibles from 1981 through fairly recent +# system designs based on the PCI bus. +# +# This file is part of the 86Box distribution. +# +# Jenkins build script. +# +# +# Authors: RichardG, +# +# Copyright 2021-2022 RichardG. +# + +# +# While this script was made for our Jenkins infrastructure, you can run it +# to produce Jenkins-like builds on your local machine by following these notes: +# +# - Run build.sh without parameters to see its usage +# - For Windows (MSYS MinGW) builds: +# - Packaging requires 7-Zip on Program Files +# - Packaging the Ghostscript DLL requires 32-bit and/or 64-bit Ghostscript on Program Files +# - Packaging the FluidSynth DLL requires it to be at /home/86Box/dll32/libfluidsynth.dll +# and/or /home/86Box/dll64/libfluidsynth64.dll (for 32-bit and 64-bit builds respectively) +# - Packaging the Discord DLL requires wget (MSYS should come with it) +# - For Linux builds: +# - Only Debian and derivatives are supported +# - dpkg and apt-get are called through sudo to manage dependencies +# - For macOS builds: +# - TBD +# + +# Define common functions. +alias is_windows='[ -n "$MSYSTEM" ]' +alias is_mac='uname -s | grep -q Darwin' + +make_tar() { + # Install dependencies. + if ! which tar xz > /dev/null 2>&1 + then + which apt-get > /dev/null 2>&1 && DEBIAN_FRONTEND=noninteractive sudo apt-get install -y tar xz-utils + fi + + # Determine the best supported compression type. + local compression_flag= + local compression_ext= + if which xz > /dev/null 2>&1 + then + local compression_flag=-J + local compression_ext=.xz + elif which bzip2 > /dev/null 2>&1 + then + local compression_flag=-j + local compression_ext=.bz2 + elif which gzip > /dev/null 2>&1 + then + local compression_flag=-z + local compression_ext=.gz + fi + + # Make tar verbose if requested. + [ -n "$VERBOSE" ] && local compression_flag="$compression_flag -v" + + # tar is notorious for having many diverging implementations. For instance, + # the flags we use to strip UID/GID metadata can be --owner/group (GNU), + # --uid/gid (bsdtar) or even none at all (MSYS2 bsdtar). Account for such + # flag differences by checking if they're mentioned on the help text. + local ownership_flags= + local tar_help=$(tar --help 2>&1) + if echo $tar_help | grep -q -- --owner + then + local ownership_flags="--owner=0 --group=0" + elif echo $tar_help | grep -q -- --uid + then + local ownership_flags="--uid 0 --gid 0" + fi + + # Run tar. + tar -c $compression_flag -f "$1$compression_ext" $ownership_flags * + return $? +} + +# Set common variables. +project=86Box +project_lower=86box +cwd=$(pwd) + +# Parse arguments. +package_name= +arch= +tarball_name= +strip=0 +cmake_flags= +while [ $# -gt 0 ] +do + case $1 in + -b) + shift + package_name="$1" + shift + arch="$1" + shift + ;; + + -s) + shift + tarball_name="$1" + shift + ;; + + -t) + shift + strip=1 + ;; + + *) + if echo $1 | grep -q " " + then + cmake_flag="\"$1\"" + else + cmake_flag="$1" + fi + if [ -z "$cmake_flags" ] + then + cmake_flags="$cmake_flag" + else + cmake_flags="$cmake_flags $cmake_flag" + fi + shift + ;; + esac +done +cmake_flags_extra= + +# Check if mandatory arguments were specified. +if [ -z "$package_name" -a -z "$tarball_name" ] || [ -n "$package_name" -a -z "$arch" ] +then + echo '[!] Usage: build.sh -b {package_name} {architecture} [-t] [cmake_flags...]' + echo ' build.sh -s {source_tarball_name}' + exit 100 +fi + +# Switch to the repository root directory. +cd "$(dirname "$0")/.." + +# Make source tarball if requested. +if [ -n "$tarball_name" ] +then + echo [-] Making source tarball [$tarball_name] + + # Clean local tree of gitignored files. + git clean -dfX + + # Recreate working directory if it was removed by git clean. + [ ! -d "$cwd" ] && mkdir -p "$cwd" + + # Save current HEAD commit to VERSION. + git log --stat -1 > VERSION || rm -f VERSION + + # Archive source. + make_tar "$cwd/$tarball_name.tar" + status=$? + + # Check if the archival succeeded. + if [ $status -ne 0 ] + then + echo [!] Tarball creation failed with status [$status] + exit 1 + else + echo [-] Source tarball [$tarball_name] created successfully + [ -z "$package_name" ] && exit 0 + fi +fi + +echo [-] Building [$package_name] for [$arch] with flags [$cmake_flags] + +# Determine CMake toolchain file for this architecture. +case $arch in + 32 | x86) toolchain="flags-gcc-i686";; + 64 | x86_64) toolchain="flags-gcc-x86_64";; + ARM32 | arm32) toolchain="flags-gcc-armv7";; + ARM64 | arm64) toolchain="flags-gcc-aarch64";; + *) toolchain="flags-gcc-$arch";; +esac + +# Perform platform-specific setup. +strip_binary=strip +if is_windows +then + # Switch into the correct MSYSTEM if required. + msys=MINGW$arch + [ ! -d "/$msys" ] && msys=CLANG$arch + if [ -d "/$msys" ] + then + if [ "$MSYSTEM" != "$msys" ] + then + # Call build with the correct MSYSTEM. + echo [-] Switching to MSYSTEM [$msys] + cd "$cwd" + strip_arg= + [ $strip -ne 0 ] && strip_arg="-t " + CHERE_INVOKING=yes MSYSTEM="$msys" bash -lc 'exec "'"$0"'" -b "'"$package_name"'" "'"$arch"'" '"$strip_arg""$cmake_flags" + exit $? + fi + else + echo [!] No MSYSTEM for architecture [$arch] + exit 2 + fi + echo [-] Using MSYSTEM [$MSYSTEM] + + # Update keyring, as the package signing keys sometimes change. + echo [-] Updating package databases and keyring + yes | pacman -Sy --needed msys2-keyring + + # Query installed packages. + pacman -Qe > pacman.txt + + # Download the specified versions of architecture-specific dependencies. + echo -n [-] Downloading dependencies: + pkg_dir="/var/cache/pacman/pkg" + repo_base="https://repo.msys2.org/mingw/$(echo $MSYSTEM | tr '[:upper:]' '[:lower:]')" + cat .ci/dependencies_msys.txt | tr -d '\r' > deps.txt + pkgs="" + while IFS=" " read pkg version + do + prefixed_pkg="$MINGW_PACKAGE_PREFIX-$pkg" + installed_version=$(grep -E "^$prefixed_pkg " pacman.txt | cut -d " " -f 2) + if [ "$installed_version" != "$version" ] # installed_version will be empty if not installed + then + echo -n " [$pkg" + + # Download package if not already present in the local cache. + pkg_tar="$prefixed_pkg-$version-any.pkg.tar" + if [ -s "$pkg_dir/$pkg_tar.xz" ] + then + pkg_fn="$pkg_tar.xz" + pkg_dest="$pkg_dir/$pkg_fn" + else + pkg_fn="$pkg_tar.zst" + pkg_dest="$pkg_dir/$pkg_fn" + if [ ! -s "$pkg_dest" ] + then + if ! wget -qO "$pkg_dest" "$repo_base/$pkg_fn" + then + rm -f "$pkg_dest" + pkg_fn="$pkg_tar.xz" + pkg_dest="$pkg_dir/$pkg_fn" + wget -qO "$pkg_dest" "$repo_base/$pkg_fn" || rm -f "$pkg_dest" + fi + if [ -s "$pkg_dest" ] + then + wget -qO "$pkg_dest.sig" "$repo_base/$pkg_fn.sig" || rm -f "$pkg_dest.sig" + [ ! -s "$pkg_dest.sig" ] && rm -f "$pkg_dest.sig" + fi + fi + fi + + # Check if the cached package is valid. + if [ -s "$pkg_dest" ] + then + # Add cached zst package. + pkgs="$pkgs $pkg_fn" + else + # Not valid, remove if it exists. + rm -f "$pkg_dest" "$pkg_dest.sig" + echo -n " FAIL" + fi + echo -n "]" + fi + done < deps.txt + [ -z "$pkgs" ] && echo -n none required + echo + + # Install the downloaded architecture-specific dependencies. + echo [-] Installing dependencies through pacman + if [ -n "$pkgs" ] + then + pushd "$pkg_dir" + yes | pacman -U --needed $pkgs + if [ $? -ne 0 ] + then + # Install packages individually if installing them all together failed. + for pkg in pkgs + do + yes | pacman -U --needed "$pkg" + done + fi + popd + + # Query installed packages again. + pacman -Qe > pacman.txt + fi + + # Install the latest versions for any missing packages (if the specified version couldn't be installed). + pkgs="make git" + while IFS=" " read pkg version + do + prefixed_pkg="$MINGW_PACKAGE_PREFIX-$pkg" + grep -qE "^$prefixed_pkg " pacman.txt || pkgs="$pkgs $prefixed_pkg" + done < deps.txt + rm -f pacman.txt deps.txt + yes | pacman -S --needed $pkgs + if [ $? -ne 0 ] + then + # Install packages individually if installing them all together failed. + for pkg in pkgs + do + yes | pacman -S --needed "$pkg" + done + fi + + # Point CMake to the toolchain file. + cmake_flags_extra="$cmake_flags_extra -D \"CMAKE_TOOLCHAIN_FILE=cmake/$toolchain.cmake\"" +elif is_mac +then + # macOS lacks nproc, but sysctl can do the same job. + alias nproc='sysctl -n hw.logicalcpu' +else + # Determine Debian architecture. + case $arch in + x86) arch_deb="i386";; + x86_64) arch_deb="amd64";; + arm32) arch_deb="armhf";; + *) arch_deb="$arch";; + esac + + # Establish general dependencies. + pkgs="cmake pkg-config git imagemagick wget p7zip-full wayland-protocols tar gzip file" + if [ "$(dpkg --print-architecture)" = "$arch_deb" ] + then + pkgs="$pkgs build-essential" + else + sudo dpkg --add-architecture "$arch_deb" + pkgs="$pkgs crossbuild-essential-$arch_deb" + fi + + # Establish architecture-specific dependencies we don't want listed on the readme... + pkgs="$pkgs linux-libc-dev:$arch_deb extra-cmake-modules:$arch_deb qttools5-dev:$arch_deb qtbase5-private-dev:$arch_deb" + + # ...and the ones we do want listed. Non-dev packages fill missing spots on the list. + libpkgs="" + longest_libpkg=0 + for pkg in libc6-dev libstdc++6 libopenal-dev libfreetype6-dev libx11-dev libsdl2-dev libpng-dev librtmidi-dev qtdeclarative5-dev libwayland-dev libevdev-dev libglib2.0-dev libslirp-dev libfaudio-dev libaudio-dev libjack-jackd2-dev libpipewire-0.3-dev libsamplerate0-dev libsndio-dev + do + libpkgs="$libpkgs $pkg:$arch_deb" + length=$(echo -n $pkg | sed 's/-dev$//' | sed "s/qtdeclarative/qt/" | wc -c) + [ $length -gt $longest_libpkg ] && longest_libpkg=$length + done + + # Determine GNU toolchain architecture. + case $arch in + x86) arch_gnu="i686-linux-gnu";; + arm32) arch_gnu="arm-linux-gnueabihf";; + arm64) arch_gnu="aarch64-linux-gnu";; + *) arch_gnu="$arch-linux-gnu";; + esac + + # Determine library directory name for this architecture. + case $arch in + x86) libdir="i386-linux-gnu";; + *) libdir="$arch_gnu";; + esac + + # Create CMake toolchain file. + cat << EOF > toolchain.cmake +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR $arch) + +set(CMAKE_AR $arch_gnu-ar) +set(CMAKE_ASM_COMPILER $arch_gnu-gcc) +set(CMAKE_C_COMPILER $arch_gnu-gcc) +set(CMAKE_CXX_COMPILER $arch_gnu-g++) +set(CMAKE_LINKER $arch_gnu-ld) +set(CMAKE_OBJCOPY $arch_gnu-objcopy) +set(CMAKE_RANLIB $arch_gnu-ranlib) +set(CMAKE_SIZE $arch_gnu-size) +set(CMAKE_STRIP $arch_gnu-strip) + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +set(ENV{PKG_CONFIG_PATH} "") +set(ENV{PKG_CONFIG_LIBDIR} "/usr/lib/$libdir/pkgconfig:/usr/share/$libdir/pkgconfig") + +include("$(pwd)/cmake/$toolchain.cmake") +EOF + cmake_flags_extra="$cmake_flags_extra -D CMAKE_TOOLCHAIN_FILE=toolchain.cmake" + strip_binary="$arch_gnu-strip" + + # Install or update dependencies. + echo [-] Installing dependencies through apt + sudo apt-get update + DEBIAN_FRONTEND=noninteractive sudo apt-get -y install $pkgs $libpkgs + sudo apt-get clean + + # Link against the system libslirp instead of compiling ours. + cmake_flags_extra="$cmake_flags_extra -D SLIRP_EXTERNAL=ON" +fi + +# Clean workspace. +echo [-] Cleaning workspace +if [ -d "build" ] +then + MAKEFLAGS=-j$(nproc) cmake --build build --target clean 2> /dev/null + rm -rf build +fi +find . \( -name Makefile -o -name CMakeCache.txt -o -name CMakeFiles \) -exec rm -rf "{}" \; 2> /dev/null + +# Add ARCH to skip the arch_detect process. +case $arch in + 32 | x86) cmake_flags_extra="$cmake_flags_extra -D ARCH=i386";; + 64 | x86_64) cmake_flags_extra="$cmake_flags_extra -D ARCH=x86_64";; + ARM32 | arm32) cmake_flags_extra="$cmake_flags_extra -D ARCH=arm";; + ARM64 | arm64) cmake_flags_extra="$cmake_flags_extra -D ARCH=arm64";; + *) cmake_flags_extra="$cmake_flags_extra -D \"ARCH=$arch\"";; +esac + +# Add git hash. +git_hash=$(git rev-parse --short HEAD 2> /dev/null) +if [ "$CI" = "true" ] +then + # Backup strategy when running under Jenkins. + [ -z "$git_hash" ] && git_hash=$(echo $GIT_COMMIT | cut -c 1-8) +elif [ -n "$git_hash" ] +then + # Append + to denote a dirty tree. + git diff --quiet 2> /dev/null || git_hash="$git_hash+" +fi +[ -n "$git_hash" ] && cmake_flags_extra="$cmake_flags_extra -D \"EMU_GIT_HASH=$git_hash\"" + +# Add copyright year. +year=$(date +%Y) +[ -n "$year" ] && cmake_flags_extra="$cmake_flags_extra -D \"EMU_COPYRIGHT_YEAR=$year\"" + +# Run CMake. +echo [-] Running CMake with flags [$cmake_flags $cmake_flags_extra] +eval cmake -G \"Unix Makefiles\" -B build $cmake_flags $cmake_flags_extra . +status=$? +if [ $status -ne 0 ] +then + echo [!] CMake failed with status [$status] + exit 3 +fi + +# Run actual build. +make_flags=-j$(nproc) +echo [-] Running build with make flags [$make_flags] +MAKEFLAGS=$make_flags cmake --build build +status=$? +if [ $status -ne 0 ] +then + echo [!] Make failed with status [$status] + exit 4 +fi + +# Download Discord Game SDK from their CDN if necessary. +if [ ! -e "discord_game_sdk.zip" ] +then + echo [-] Downloading Discord Game SDK + wget -qO discord_game_sdk.zip "https://dl-game-sdk.discordapp.net/2.5.6/discord_game_sdk.zip" + status=$? + if [ $status -ne 0 ] + then + echo [!] Discord Game SDK download failed with status [$status] + rm -f discord_game_sdk.zip + fi +fi + +# Determine Discord Game SDK architecture. +case $arch in + 32) arch_discord="x86";; + 64) arch_discord="x86_64";; + *) arch_discord="$arch";; +esac + +# Create temporary directory for archival. +echo [-] Gathering archive files +rm -rf archive_tmp +mkdir archive_tmp +if [ ! -d "archive_tmp" ] +then + echo [!] Archive directory creation failed + exit 5 +fi + +# Archive the executable and its dependencies. +# The executable should always be archived last for the check after this block. +status=0 +if is_windows +then + # Determine Program Files directory for Ghostscript and 7-Zip. + # Manual checks because MSYS is bad at passing the ProgramFiles variables. + pf="/c/Program Files" + sevenzip="$pf/7-Zip/7z.exe" + [ "$arch" = "32" -a -d "/c/Program Files (x86)" ] && pf="/c/Program Files (x86)" + + # Archive freetype from local MSYS installation. + .ci/static2dll.sh -p freetype2 /$MSYSTEM/lib/libfreetype.a archive_tmp/freetype.dll + + # Archive Ghostscript DLL from local official distribution installation. + for gs in "$pf"/gs/gs*.*.* + do + cp -p "$gs"/bin/gsdll*.dll archive_tmp/ + done + + # Archive Discord Game SDK DLL. + "$sevenzip" e -y -o"archive_tmp" discord_game_sdk.zip "lib/$arch_discord/discord_game_sdk.dll" + [ ! -e "archive_tmp/discord_game_sdk.dll" ] && echo [!] No Discord Game SDK for architecture [$arch_discord] + + # Archive other DLLs from local directory. + cp -p "/home/$project/dll$arch/"* archive_tmp/ + + # Archive executable, while also stripping it if requested. + if [ $strip -ne 0 ] + then + "$strip_binary" -o "archive_tmp/$project.exe" "build/src/$project.exe" + status=$? + else + mv "build/src/$project.exe" "archive_tmp/$project.exe" + status=$? + fi +elif is_mac +then + # TBD + : +else + cwd_root=$(pwd) + + if grep -q "OPENAL:BOOL=ON" build/CMakeCache.txt + then + # Build openal-soft 1.21.1 manually to fix audio issues. This is a temporary + # workaround until a newer version of openal-soft trickles down to Debian repos. + if [ -d "openal-soft-1.21.1" ] + then + rm -rf openal-soft-1.21.1/build/* + else + wget -qO - https://github.com/kcat/openal-soft/archive/refs/tags/1.21.1.tar.gz | tar zxf - + fi + cd openal-soft-1.21.1/build + [ -e Makefile ] && make clean + cmake -G "Unix Makefiles" -D "CMAKE_TOOLCHAIN_FILE=$cwd_root/toolchain.cmake" -D "CMAKE_INSTALL_PREFIX=$cwd_root/archive_tmp/usr" .. + make -j$(nproc) install || exit 99 + cd "$cwd_root" + + # Build SDL2 without sound systems. + sdl_ss=OFF + else + # Build FAudio 22.03 manually to remove the dependency on GStreamer. This is a temporary + # workaround until a newer version of FAudio trickles down to Debian repos. + if [ -d "FAudio-22.03" ] + then + rm -rf FAudio-22.03/build + else + wget -qO - https://github.com/FNA-XNA/FAudio/archive/refs/tags/22.03.tar.gz | tar zxf - + fi + mkdir FAudio-22.03/build + cd FAudio-22.03/build + cmake -G "Unix Makefiles" -D "CMAKE_TOOLCHAIN_FILE=$cwd_root/toolchain.cmake" -D "CMAKE_INSTALL_PREFIX=$cwd_root/archive_tmp/usr" .. + make -j$(nproc) install || exit 99 + cd "$cwd_root" + + # Build SDL2 with sound systems. + sdl_ss=ON + fi + + # Build rtmidi without JACK support to remove the dependency on libjack. + if [ -d "rtmidi-4.0.0" ] + then + rm -rf rtmidi-4.0.0/CMakeCache.txt rtmidi-4.0.0/CMakeFiles + else + wget -qO - http://www.music.mcgill.ca/~gary/rtmidi/release/rtmidi-4.0.0.tar.gz | tar zxf - + fi + cwd_root=$(pwd) + cd rtmidi-4.0.0 + [ -e Makefile ] && make clean + cmake -G "Unix Makefiles" -D RTMIDI_API_JACK=OFF -D "CMAKE_TOOLCHAIN_FILE=$cwd_root/toolchain.cmake" -D "CMAKE_INSTALL_PREFIX=$cwd_root/archive_tmp/usr" . + make -j$(nproc) install || exit 99 + cd "$cwd_root" + + # Build SDL2 for joystick support with most components disabled to remove the dependencies on PulseAudio and libdrm. + if [ ! -d "SDL2-2.0.20" ] + then + wget -qO - https://www.libsdl.org/release/SDL2-2.0.20.tar.gz | tar zxf - + fi + rm -rf sdlbuild + mkdir sdlbuild + cd sdlbuild + cmake -G "Unix Makefiles" -D SDL_DISKAUDIO=OFF -D SDL_DIRECTFB_SHARED=OFF -D SDL_OPENGL=OFF -D SDL_OPENGLES=OFF -D SDL_OSS=OFF -D SDL_ALSA=$sdl_ss \ + -D SDL_ALSA_SHARED=$sdl_ss -D SDL_JACK=$sdl_ss -D SDL_JACK_SHARED=$sdl_ss -D SDL_ESD=OFF -D SDL_ESD_SHARED=OFF -D SDL_PIPEWIRE=$sdl_ss -D SDL_PIPEWIRE_SHARED=$sdl_ss \ + -D SDL_PULSEAUDIO=$sdl_ss -D SDL_PULSEAUDIO_SHARED=$sdl_ss -D SDL_ARTS=OFF -D SDL_ARTS_SHARED=OFF -D SDL_NAS=$sdl_ss -D SDL_NAS_SHARED=$sdl_ss -D SDL_SNDIO=$sdl_ss \ + -D SDL_SNDIO_SHARED=$sdl_ss -D SDL_FUSIONSOUND=OFF -D SDL_FUSIONSOUND_SHARED=OFF -D SDL_LIBSAMPLERATE=$sdl_ss -D SDL_LIBSAMPLERATE_SHARED=$sdl_ss -D SDL_X11=OFF \ + -D SDL_X11_SHARED=OFF -D SDL_WAYLAND=OFF -D SDL_WAYLAND_SHARED=OFF -D SDL_WAYLAND_LIBDECOR=OFF -D SDL_WAYLAND_LIBDECOR_SHARED=OFF \ + -D SDL_WAYLAND_QT_TOUCH=OFF -D SDL_RPI=OFF -D SDL_VIVANTE=OFF -D SDL_VULKAN=OFF -D SDL_KMSDRM=OFF -D SDL_KMSDRM_SHARED=OFF -D SDL_OFFSCREEN=OFF \ + -D SDL_HIDAPI_JOYSTICK=ON -D SDL_VIRTUAL_JOYSTICK=ON -D SDL_SHARED=ON -D SDL_STATIC=OFF -S "$cwd_root/SDL2-2.0.20" \ + -D "CMAKE_TOOLCHAIN_FILE=$cwd_root/toolchain.cmake" -D "CMAKE_INSTALL_PREFIX=$cwd_root/archive_tmp/usr" + make -j$(nproc) install || exit 99 + cd "$cwd_root" + + # Archive Discord Game SDK library. + 7z e -y -o"archive_tmp/usr/lib" discord_game_sdk.zip "lib/$arch_discord/discord_game_sdk.so" + [ ! -e "archive_tmp/usr/lib/discord_game_sdk.so" ] && echo [!] No Discord Game SDK for architecture [$arch_discord] + + # Archive readme with library package versions. + echo Libraries used to compile this $arch build of $project: > archive_tmp/README + dpkg-query -f '${Package} ${Version}\n' -W $libpkgs | sed "s/-dev / /" | sed "s/qtdeclarative/qt/" | while IFS=" " read pkg version + do + for i in $(seq $(expr $longest_libpkg - $(echo -n $pkg | wc -c))) + do + echo -n " " >> archive_tmp/README + done + echo $pkg $version >> archive_tmp/README + done + + # Archive icon, while also shrinking it to 512x512 if necessary. + convert src/win/assets/$project_lower.png -resize '512x512>' icon.png + icon_base="$(identify -format 'archive_tmp/usr/share/icons/%wx%h' icon.png)" + mkdir -p "$icon_base" + mv icon.png "$icon_base/$project_lower.png" + + # Archive executable, while also stripping it if requested. + mkdir -p archive_tmp/usr/local/bin + if [ $strip -ne 0 ] + then + "$strip_binary" -o "archive_tmp/usr/local/bin/$project" "build/src/$project" + status=$? + else + mv "build/src/$project" "archive_tmp/usr/local/bin/$project" + status=$? + fi +fi + +# Check if the executable strip/move succeeded. +if [ $status -ne 0 ] +then + echo [!] Executable strip/move failed with status [$status] + exit 6 +fi + +# Produce artifact archive. +echo [-] Creating artifact archive +if is_windows +then + # Create zip. + cd archive_tmp + "$sevenzip" a -y "$(cygpath -w "$cwd")\\$package_name.zip" * + status=$? +elif is_mac +then + # TBD + : +else + # Determine AppImage runtime architecture. + case $arch in + x86) arch_appimage="i686";; + arm32) arch_appimage="armhf";; + arm64) arch_appimage="aarch64";; + *) arch_appimage="$arch";; + esac + + # Get version for AppImage metadata. + project_version=$(grep -oP '#define\s+EMU_VERSION\s+"\K([^"]+)' "build/src/include/$project_lower/version.h" 2> /dev/null) + [ -z "$project_version" ] && project_version=unknown + build_num=$(grep -oP '#define\s+EMU_BUILD_NUM\s+\K([0-9]+)' "build/src/include/$project_lower/version.h" 2> /dev/null) + [ -n "$build_num" -a "$build_num" != "0" ] && project_version="$project_version-b$build_num" + + # Download appimage-builder if necessary. + [ ! -e "appimage-builder.AppImage" ] && wget -qO appimage-builder.AppImage \ + https://github.com/AppImageCrafters/appimage-builder/releases/download/v0.9.2/appimage-builder-0.9.2-35e3eab-x86_64.AppImage + chmod u+x appimage-builder.AppImage + + # Remove any dangling AppImages which may interfere with the renaming process. + rm -rf "$project-"*".AppImage" + + # Run appimage-builder in extract-and-run mode for Docker compatibility. + project="$project" project_lower="$project_lower" project_version="$project_version" arch_deb="$arch_deb" arch_appimage="$arch_appimage" \ + APPIMAGE_EXTRACT_AND_RUN=1 ./appimage-builder.AppImage --recipe .ci/AppImageBuilder.yml + status=$? + + # Rename AppImage to the final name if the build succeeded. + if [ $status -eq 0 ] + then + mv "$project-"*".AppImage" "$cwd/$package_name.AppImage" + status=$? + fi +fi +cd .. + +# Check if the archival succeeded. +if [ $status -ne 0 ] +then + echo [!] Artifact archive creation failed with status [$status] + exit 7 +fi + +# All good. +echo [-] Build of [$package_name] for [$arch] with flags [$cmake_flags] successful +exit 0 From f9e3cb0956c319bdc22967f6866f03a4e8f387f8 Mon Sep 17 00:00:00 2001 From: richardg867 Date: Tue, 15 Mar 2022 21:35:41 -0300 Subject: [PATCH 38/62] Using the upload tool was a mistake... From 7acf41b1e4f660026da50eff44cb2c01d042f563 Mon Sep 17 00:00:00 2001 From: richardg867 Date: Tue, 15 Mar 2022 21:37:12 -0300 Subject: [PATCH 39/62] Using the upload tool was a mistake, part 2 --- .ci/build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/build.sh b/.ci/build.sh index 447cc498d..61d37012a 100644 --- a/.ci/build.sh +++ b/.ci/build.sh @@ -301,7 +301,7 @@ then prefixed_pkg="$MINGW_PACKAGE_PREFIX-$pkg" grep -qE "^$prefixed_pkg " pacman.txt || pkgs="$pkgs $prefixed_pkg" done < deps.txt - rm -f pacman.txt deps.txt + rm -f deps.txt pacman.txt yes | pacman -S --needed $pkgs if [ $? -ne 0 ] then From 78a6a67a5497ff63cd16b374eedbd61d38c15975 Mon Sep 17 00:00:00 2001 From: richardg867 Date: Tue, 15 Mar 2022 21:39:14 -0300 Subject: [PATCH 40/62] Using the upload tool was a mistake, part 3 --- .ci/build.sh | 1402 +++++++++++++++++++++++++------------------------- 1 file changed, 701 insertions(+), 701 deletions(-) diff --git a/.ci/build.sh b/.ci/build.sh index 61d37012a..6e0257876 100644 --- a/.ci/build.sh +++ b/.ci/build.sh @@ -1,701 +1,701 @@ -#!/bin/sh -# -# 86Box A hypervisor and IBM PC system emulator that specializes in -# running old operating systems and software designed for IBM -# PC systems and compatibles from 1981 through fairly recent -# system designs based on the PCI bus. -# -# This file is part of the 86Box distribution. -# -# Jenkins build script. -# -# -# Authors: RichardG, -# -# Copyright 2021-2022 RichardG. -# - -# -# While this script was made for our Jenkins infrastructure, you can run it -# to produce Jenkins-like builds on your local machine by following these notes: -# -# - Run build.sh without parameters to see its usage -# - For Windows (MSYS MinGW) builds: -# - Packaging requires 7-Zip on Program Files -# - Packaging the Ghostscript DLL requires 32-bit and/or 64-bit Ghostscript on Program Files -# - Packaging the FluidSynth DLL requires it to be at /home/86Box/dll32/libfluidsynth.dll -# and/or /home/86Box/dll64/libfluidsynth64.dll (for 32-bit and 64-bit builds respectively) -# - Packaging the Discord DLL requires wget (MSYS should come with it) -# - For Linux builds: -# - Only Debian and derivatives are supported -# - dpkg and apt-get are called through sudo to manage dependencies -# - For macOS builds: -# - TBD -# - -# Define common functions. -alias is_windows='[ -n "$MSYSTEM" ]' -alias is_mac='uname -s | grep -q Darwin' - -make_tar() { - # Install dependencies. - if ! which tar xz > /dev/null 2>&1 - then - which apt-get > /dev/null 2>&1 && DEBIAN_FRONTEND=noninteractive sudo apt-get install -y tar xz-utils - fi - - # Determine the best supported compression type. - local compression_flag= - local compression_ext= - if which xz > /dev/null 2>&1 - then - local compression_flag=-J - local compression_ext=.xz - elif which bzip2 > /dev/null 2>&1 - then - local compression_flag=-j - local compression_ext=.bz2 - elif which gzip > /dev/null 2>&1 - then - local compression_flag=-z - local compression_ext=.gz - fi - - # Make tar verbose if requested. - [ -n "$VERBOSE" ] && local compression_flag="$compression_flag -v" - - # tar is notorious for having many diverging implementations. For instance, - # the flags we use to strip UID/GID metadata can be --owner/group (GNU), - # --uid/gid (bsdtar) or even none at all (MSYS2 bsdtar). Account for such - # flag differences by checking if they're mentioned on the help text. - local ownership_flags= - local tar_help=$(tar --help 2>&1) - if echo $tar_help | grep -q -- --owner - then - local ownership_flags="--owner=0 --group=0" - elif echo $tar_help | grep -q -- --uid - then - local ownership_flags="--uid 0 --gid 0" - fi - - # Run tar. - tar -c $compression_flag -f "$1$compression_ext" $ownership_flags * - return $? -} - -# Set common variables. -project=86Box -project_lower=86box -cwd=$(pwd) - -# Parse arguments. -package_name= -arch= -tarball_name= -strip=0 -cmake_flags= -while [ $# -gt 0 ] -do - case $1 in - -b) - shift - package_name="$1" - shift - arch="$1" - shift - ;; - - -s) - shift - tarball_name="$1" - shift - ;; - - -t) - shift - strip=1 - ;; - - *) - if echo $1 | grep -q " " - then - cmake_flag="\"$1\"" - else - cmake_flag="$1" - fi - if [ -z "$cmake_flags" ] - then - cmake_flags="$cmake_flag" - else - cmake_flags="$cmake_flags $cmake_flag" - fi - shift - ;; - esac -done -cmake_flags_extra= - -# Check if mandatory arguments were specified. -if [ -z "$package_name" -a -z "$tarball_name" ] || [ -n "$package_name" -a -z "$arch" ] -then - echo '[!] Usage: build.sh -b {package_name} {architecture} [-t] [cmake_flags...]' - echo ' build.sh -s {source_tarball_name}' - exit 100 -fi - -# Switch to the repository root directory. -cd "$(dirname "$0")/.." - -# Make source tarball if requested. -if [ -n "$tarball_name" ] -then - echo [-] Making source tarball [$tarball_name] - - # Clean local tree of gitignored files. - git clean -dfX - - # Recreate working directory if it was removed by git clean. - [ ! -d "$cwd" ] && mkdir -p "$cwd" - - # Save current HEAD commit to VERSION. - git log --stat -1 > VERSION || rm -f VERSION - - # Archive source. - make_tar "$cwd/$tarball_name.tar" - status=$? - - # Check if the archival succeeded. - if [ $status -ne 0 ] - then - echo [!] Tarball creation failed with status [$status] - exit 1 - else - echo [-] Source tarball [$tarball_name] created successfully - [ -z "$package_name" ] && exit 0 - fi -fi - -echo [-] Building [$package_name] for [$arch] with flags [$cmake_flags] - -# Determine CMake toolchain file for this architecture. -case $arch in - 32 | x86) toolchain="flags-gcc-i686";; - 64 | x86_64) toolchain="flags-gcc-x86_64";; - ARM32 | arm32) toolchain="flags-gcc-armv7";; - ARM64 | arm64) toolchain="flags-gcc-aarch64";; - *) toolchain="flags-gcc-$arch";; -esac - -# Perform platform-specific setup. -strip_binary=strip -if is_windows -then - # Switch into the correct MSYSTEM if required. - msys=MINGW$arch - [ ! -d "/$msys" ] && msys=CLANG$arch - if [ -d "/$msys" ] - then - if [ "$MSYSTEM" != "$msys" ] - then - # Call build with the correct MSYSTEM. - echo [-] Switching to MSYSTEM [$msys] - cd "$cwd" - strip_arg= - [ $strip -ne 0 ] && strip_arg="-t " - CHERE_INVOKING=yes MSYSTEM="$msys" bash -lc 'exec "'"$0"'" -b "'"$package_name"'" "'"$arch"'" '"$strip_arg""$cmake_flags" - exit $? - fi - else - echo [!] No MSYSTEM for architecture [$arch] - exit 2 - fi - echo [-] Using MSYSTEM [$MSYSTEM] - - # Update keyring, as the package signing keys sometimes change. - echo [-] Updating package databases and keyring - yes | pacman -Sy --needed msys2-keyring - - # Query installed packages. - pacman -Qe > pacman.txt - - # Download the specified versions of architecture-specific dependencies. - echo -n [-] Downloading dependencies: - pkg_dir="/var/cache/pacman/pkg" - repo_base="https://repo.msys2.org/mingw/$(echo $MSYSTEM | tr '[:upper:]' '[:lower:]')" - cat .ci/dependencies_msys.txt | tr -d '\r' > deps.txt - pkgs="" - while IFS=" " read pkg version - do - prefixed_pkg="$MINGW_PACKAGE_PREFIX-$pkg" - installed_version=$(grep -E "^$prefixed_pkg " pacman.txt | cut -d " " -f 2) - if [ "$installed_version" != "$version" ] # installed_version will be empty if not installed - then - echo -n " [$pkg" - - # Download package if not already present in the local cache. - pkg_tar="$prefixed_pkg-$version-any.pkg.tar" - if [ -s "$pkg_dir/$pkg_tar.xz" ] - then - pkg_fn="$pkg_tar.xz" - pkg_dest="$pkg_dir/$pkg_fn" - else - pkg_fn="$pkg_tar.zst" - pkg_dest="$pkg_dir/$pkg_fn" - if [ ! -s "$pkg_dest" ] - then - if ! wget -qO "$pkg_dest" "$repo_base/$pkg_fn" - then - rm -f "$pkg_dest" - pkg_fn="$pkg_tar.xz" - pkg_dest="$pkg_dir/$pkg_fn" - wget -qO "$pkg_dest" "$repo_base/$pkg_fn" || rm -f "$pkg_dest" - fi - if [ -s "$pkg_dest" ] - then - wget -qO "$pkg_dest.sig" "$repo_base/$pkg_fn.sig" || rm -f "$pkg_dest.sig" - [ ! -s "$pkg_dest.sig" ] && rm -f "$pkg_dest.sig" - fi - fi - fi - - # Check if the cached package is valid. - if [ -s "$pkg_dest" ] - then - # Add cached zst package. - pkgs="$pkgs $pkg_fn" - else - # Not valid, remove if it exists. - rm -f "$pkg_dest" "$pkg_dest.sig" - echo -n " FAIL" - fi - echo -n "]" - fi - done < deps.txt - [ -z "$pkgs" ] && echo -n none required - echo - - # Install the downloaded architecture-specific dependencies. - echo [-] Installing dependencies through pacman - if [ -n "$pkgs" ] - then - pushd "$pkg_dir" - yes | pacman -U --needed $pkgs - if [ $? -ne 0 ] - then - # Install packages individually if installing them all together failed. - for pkg in pkgs - do - yes | pacman -U --needed "$pkg" - done - fi - popd - - # Query installed packages again. - pacman -Qe > pacman.txt - fi - - # Install the latest versions for any missing packages (if the specified version couldn't be installed). - pkgs="make git" - while IFS=" " read pkg version - do - prefixed_pkg="$MINGW_PACKAGE_PREFIX-$pkg" - grep -qE "^$prefixed_pkg " pacman.txt || pkgs="$pkgs $prefixed_pkg" - done < deps.txt - rm -f deps.txt pacman.txt - yes | pacman -S --needed $pkgs - if [ $? -ne 0 ] - then - # Install packages individually if installing them all together failed. - for pkg in pkgs - do - yes | pacman -S --needed "$pkg" - done - fi - - # Point CMake to the toolchain file. - cmake_flags_extra="$cmake_flags_extra -D \"CMAKE_TOOLCHAIN_FILE=cmake/$toolchain.cmake\"" -elif is_mac -then - # macOS lacks nproc, but sysctl can do the same job. - alias nproc='sysctl -n hw.logicalcpu' -else - # Determine Debian architecture. - case $arch in - x86) arch_deb="i386";; - x86_64) arch_deb="amd64";; - arm32) arch_deb="armhf";; - *) arch_deb="$arch";; - esac - - # Establish general dependencies. - pkgs="cmake pkg-config git imagemagick wget p7zip-full wayland-protocols tar gzip file" - if [ "$(dpkg --print-architecture)" = "$arch_deb" ] - then - pkgs="$pkgs build-essential" - else - sudo dpkg --add-architecture "$arch_deb" - pkgs="$pkgs crossbuild-essential-$arch_deb" - fi - - # Establish architecture-specific dependencies we don't want listed on the readme... - pkgs="$pkgs linux-libc-dev:$arch_deb extra-cmake-modules:$arch_deb qttools5-dev:$arch_deb qtbase5-private-dev:$arch_deb" - - # ...and the ones we do want listed. Non-dev packages fill missing spots on the list. - libpkgs="" - longest_libpkg=0 - for pkg in libc6-dev libstdc++6 libopenal-dev libfreetype6-dev libx11-dev libsdl2-dev libpng-dev librtmidi-dev qtdeclarative5-dev libwayland-dev libevdev-dev libglib2.0-dev libslirp-dev libfaudio-dev libaudio-dev libjack-jackd2-dev libpipewire-0.3-dev libsamplerate0-dev libsndio-dev - do - libpkgs="$libpkgs $pkg:$arch_deb" - length=$(echo -n $pkg | sed 's/-dev$//' | sed "s/qtdeclarative/qt/" | wc -c) - [ $length -gt $longest_libpkg ] && longest_libpkg=$length - done - - # Determine GNU toolchain architecture. - case $arch in - x86) arch_gnu="i686-linux-gnu";; - arm32) arch_gnu="arm-linux-gnueabihf";; - arm64) arch_gnu="aarch64-linux-gnu";; - *) arch_gnu="$arch-linux-gnu";; - esac - - # Determine library directory name for this architecture. - case $arch in - x86) libdir="i386-linux-gnu";; - *) libdir="$arch_gnu";; - esac - - # Create CMake toolchain file. - cat << EOF > toolchain.cmake -set(CMAKE_SYSTEM_NAME Linux) -set(CMAKE_SYSTEM_PROCESSOR $arch) - -set(CMAKE_AR $arch_gnu-ar) -set(CMAKE_ASM_COMPILER $arch_gnu-gcc) -set(CMAKE_C_COMPILER $arch_gnu-gcc) -set(CMAKE_CXX_COMPILER $arch_gnu-g++) -set(CMAKE_LINKER $arch_gnu-ld) -set(CMAKE_OBJCOPY $arch_gnu-objcopy) -set(CMAKE_RANLIB $arch_gnu-ranlib) -set(CMAKE_SIZE $arch_gnu-size) -set(CMAKE_STRIP $arch_gnu-strip) - -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) - -set(ENV{PKG_CONFIG_PATH} "") -set(ENV{PKG_CONFIG_LIBDIR} "/usr/lib/$libdir/pkgconfig:/usr/share/$libdir/pkgconfig") - -include("$(pwd)/cmake/$toolchain.cmake") -EOF - cmake_flags_extra="$cmake_flags_extra -D CMAKE_TOOLCHAIN_FILE=toolchain.cmake" - strip_binary="$arch_gnu-strip" - - # Install or update dependencies. - echo [-] Installing dependencies through apt - sudo apt-get update - DEBIAN_FRONTEND=noninteractive sudo apt-get -y install $pkgs $libpkgs - sudo apt-get clean - - # Link against the system libslirp instead of compiling ours. - cmake_flags_extra="$cmake_flags_extra -D SLIRP_EXTERNAL=ON" -fi - -# Clean workspace. -echo [-] Cleaning workspace -if [ -d "build" ] -then - MAKEFLAGS=-j$(nproc) cmake --build build --target clean 2> /dev/null - rm -rf build -fi -find . \( -name Makefile -o -name CMakeCache.txt -o -name CMakeFiles \) -exec rm -rf "{}" \; 2> /dev/null - -# Add ARCH to skip the arch_detect process. -case $arch in - 32 | x86) cmake_flags_extra="$cmake_flags_extra -D ARCH=i386";; - 64 | x86_64) cmake_flags_extra="$cmake_flags_extra -D ARCH=x86_64";; - ARM32 | arm32) cmake_flags_extra="$cmake_flags_extra -D ARCH=arm";; - ARM64 | arm64) cmake_flags_extra="$cmake_flags_extra -D ARCH=arm64";; - *) cmake_flags_extra="$cmake_flags_extra -D \"ARCH=$arch\"";; -esac - -# Add git hash. -git_hash=$(git rev-parse --short HEAD 2> /dev/null) -if [ "$CI" = "true" ] -then - # Backup strategy when running under Jenkins. - [ -z "$git_hash" ] && git_hash=$(echo $GIT_COMMIT | cut -c 1-8) -elif [ -n "$git_hash" ] -then - # Append + to denote a dirty tree. - git diff --quiet 2> /dev/null || git_hash="$git_hash+" -fi -[ -n "$git_hash" ] && cmake_flags_extra="$cmake_flags_extra -D \"EMU_GIT_HASH=$git_hash\"" - -# Add copyright year. -year=$(date +%Y) -[ -n "$year" ] && cmake_flags_extra="$cmake_flags_extra -D \"EMU_COPYRIGHT_YEAR=$year\"" - -# Run CMake. -echo [-] Running CMake with flags [$cmake_flags $cmake_flags_extra] -eval cmake -G \"Unix Makefiles\" -B build $cmake_flags $cmake_flags_extra . -status=$? -if [ $status -ne 0 ] -then - echo [!] CMake failed with status [$status] - exit 3 -fi - -# Run actual build. -make_flags=-j$(nproc) -echo [-] Running build with make flags [$make_flags] -MAKEFLAGS=$make_flags cmake --build build -status=$? -if [ $status -ne 0 ] -then - echo [!] Make failed with status [$status] - exit 4 -fi - -# Download Discord Game SDK from their CDN if necessary. -if [ ! -e "discord_game_sdk.zip" ] -then - echo [-] Downloading Discord Game SDK - wget -qO discord_game_sdk.zip "https://dl-game-sdk.discordapp.net/2.5.6/discord_game_sdk.zip" - status=$? - if [ $status -ne 0 ] - then - echo [!] Discord Game SDK download failed with status [$status] - rm -f discord_game_sdk.zip - fi -fi - -# Determine Discord Game SDK architecture. -case $arch in - 32) arch_discord="x86";; - 64) arch_discord="x86_64";; - *) arch_discord="$arch";; -esac - -# Create temporary directory for archival. -echo [-] Gathering archive files -rm -rf archive_tmp -mkdir archive_tmp -if [ ! -d "archive_tmp" ] -then - echo [!] Archive directory creation failed - exit 5 -fi - -# Archive the executable and its dependencies. -# The executable should always be archived last for the check after this block. -status=0 -if is_windows -then - # Determine Program Files directory for Ghostscript and 7-Zip. - # Manual checks because MSYS is bad at passing the ProgramFiles variables. - pf="/c/Program Files" - sevenzip="$pf/7-Zip/7z.exe" - [ "$arch" = "32" -a -d "/c/Program Files (x86)" ] && pf="/c/Program Files (x86)" - - # Archive freetype from local MSYS installation. - .ci/static2dll.sh -p freetype2 /$MSYSTEM/lib/libfreetype.a archive_tmp/freetype.dll - - # Archive Ghostscript DLL from local official distribution installation. - for gs in "$pf"/gs/gs*.*.* - do - cp -p "$gs"/bin/gsdll*.dll archive_tmp/ - done - - # Archive Discord Game SDK DLL. - "$sevenzip" e -y -o"archive_tmp" discord_game_sdk.zip "lib/$arch_discord/discord_game_sdk.dll" - [ ! -e "archive_tmp/discord_game_sdk.dll" ] && echo [!] No Discord Game SDK for architecture [$arch_discord] - - # Archive other DLLs from local directory. - cp -p "/home/$project/dll$arch/"* archive_tmp/ - - # Archive executable, while also stripping it if requested. - if [ $strip -ne 0 ] - then - "$strip_binary" -o "archive_tmp/$project.exe" "build/src/$project.exe" - status=$? - else - mv "build/src/$project.exe" "archive_tmp/$project.exe" - status=$? - fi -elif is_mac -then - # TBD - : -else - cwd_root=$(pwd) - - if grep -q "OPENAL:BOOL=ON" build/CMakeCache.txt - then - # Build openal-soft 1.21.1 manually to fix audio issues. This is a temporary - # workaround until a newer version of openal-soft trickles down to Debian repos. - if [ -d "openal-soft-1.21.1" ] - then - rm -rf openal-soft-1.21.1/build/* - else - wget -qO - https://github.com/kcat/openal-soft/archive/refs/tags/1.21.1.tar.gz | tar zxf - - fi - cd openal-soft-1.21.1/build - [ -e Makefile ] && make clean - cmake -G "Unix Makefiles" -D "CMAKE_TOOLCHAIN_FILE=$cwd_root/toolchain.cmake" -D "CMAKE_INSTALL_PREFIX=$cwd_root/archive_tmp/usr" .. - make -j$(nproc) install || exit 99 - cd "$cwd_root" - - # Build SDL2 without sound systems. - sdl_ss=OFF - else - # Build FAudio 22.03 manually to remove the dependency on GStreamer. This is a temporary - # workaround until a newer version of FAudio trickles down to Debian repos. - if [ -d "FAudio-22.03" ] - then - rm -rf FAudio-22.03/build - else - wget -qO - https://github.com/FNA-XNA/FAudio/archive/refs/tags/22.03.tar.gz | tar zxf - - fi - mkdir FAudio-22.03/build - cd FAudio-22.03/build - cmake -G "Unix Makefiles" -D "CMAKE_TOOLCHAIN_FILE=$cwd_root/toolchain.cmake" -D "CMAKE_INSTALL_PREFIX=$cwd_root/archive_tmp/usr" .. - make -j$(nproc) install || exit 99 - cd "$cwd_root" - - # Build SDL2 with sound systems. - sdl_ss=ON - fi - - # Build rtmidi without JACK support to remove the dependency on libjack. - if [ -d "rtmidi-4.0.0" ] - then - rm -rf rtmidi-4.0.0/CMakeCache.txt rtmidi-4.0.0/CMakeFiles - else - wget -qO - http://www.music.mcgill.ca/~gary/rtmidi/release/rtmidi-4.0.0.tar.gz | tar zxf - - fi - cwd_root=$(pwd) - cd rtmidi-4.0.0 - [ -e Makefile ] && make clean - cmake -G "Unix Makefiles" -D RTMIDI_API_JACK=OFF -D "CMAKE_TOOLCHAIN_FILE=$cwd_root/toolchain.cmake" -D "CMAKE_INSTALL_PREFIX=$cwd_root/archive_tmp/usr" . - make -j$(nproc) install || exit 99 - cd "$cwd_root" - - # Build SDL2 for joystick support with most components disabled to remove the dependencies on PulseAudio and libdrm. - if [ ! -d "SDL2-2.0.20" ] - then - wget -qO - https://www.libsdl.org/release/SDL2-2.0.20.tar.gz | tar zxf - - fi - rm -rf sdlbuild - mkdir sdlbuild - cd sdlbuild - cmake -G "Unix Makefiles" -D SDL_DISKAUDIO=OFF -D SDL_DIRECTFB_SHARED=OFF -D SDL_OPENGL=OFF -D SDL_OPENGLES=OFF -D SDL_OSS=OFF -D SDL_ALSA=$sdl_ss \ - -D SDL_ALSA_SHARED=$sdl_ss -D SDL_JACK=$sdl_ss -D SDL_JACK_SHARED=$sdl_ss -D SDL_ESD=OFF -D SDL_ESD_SHARED=OFF -D SDL_PIPEWIRE=$sdl_ss -D SDL_PIPEWIRE_SHARED=$sdl_ss \ - -D SDL_PULSEAUDIO=$sdl_ss -D SDL_PULSEAUDIO_SHARED=$sdl_ss -D SDL_ARTS=OFF -D SDL_ARTS_SHARED=OFF -D SDL_NAS=$sdl_ss -D SDL_NAS_SHARED=$sdl_ss -D SDL_SNDIO=$sdl_ss \ - -D SDL_SNDIO_SHARED=$sdl_ss -D SDL_FUSIONSOUND=OFF -D SDL_FUSIONSOUND_SHARED=OFF -D SDL_LIBSAMPLERATE=$sdl_ss -D SDL_LIBSAMPLERATE_SHARED=$sdl_ss -D SDL_X11=OFF \ - -D SDL_X11_SHARED=OFF -D SDL_WAYLAND=OFF -D SDL_WAYLAND_SHARED=OFF -D SDL_WAYLAND_LIBDECOR=OFF -D SDL_WAYLAND_LIBDECOR_SHARED=OFF \ - -D SDL_WAYLAND_QT_TOUCH=OFF -D SDL_RPI=OFF -D SDL_VIVANTE=OFF -D SDL_VULKAN=OFF -D SDL_KMSDRM=OFF -D SDL_KMSDRM_SHARED=OFF -D SDL_OFFSCREEN=OFF \ - -D SDL_HIDAPI_JOYSTICK=ON -D SDL_VIRTUAL_JOYSTICK=ON -D SDL_SHARED=ON -D SDL_STATIC=OFF -S "$cwd_root/SDL2-2.0.20" \ - -D "CMAKE_TOOLCHAIN_FILE=$cwd_root/toolchain.cmake" -D "CMAKE_INSTALL_PREFIX=$cwd_root/archive_tmp/usr" - make -j$(nproc) install || exit 99 - cd "$cwd_root" - - # Archive Discord Game SDK library. - 7z e -y -o"archive_tmp/usr/lib" discord_game_sdk.zip "lib/$arch_discord/discord_game_sdk.so" - [ ! -e "archive_tmp/usr/lib/discord_game_sdk.so" ] && echo [!] No Discord Game SDK for architecture [$arch_discord] - - # Archive readme with library package versions. - echo Libraries used to compile this $arch build of $project: > archive_tmp/README - dpkg-query -f '${Package} ${Version}\n' -W $libpkgs | sed "s/-dev / /" | sed "s/qtdeclarative/qt/" | while IFS=" " read pkg version - do - for i in $(seq $(expr $longest_libpkg - $(echo -n $pkg | wc -c))) - do - echo -n " " >> archive_tmp/README - done - echo $pkg $version >> archive_tmp/README - done - - # Archive icon, while also shrinking it to 512x512 if necessary. - convert src/win/assets/$project_lower.png -resize '512x512>' icon.png - icon_base="$(identify -format 'archive_tmp/usr/share/icons/%wx%h' icon.png)" - mkdir -p "$icon_base" - mv icon.png "$icon_base/$project_lower.png" - - # Archive executable, while also stripping it if requested. - mkdir -p archive_tmp/usr/local/bin - if [ $strip -ne 0 ] - then - "$strip_binary" -o "archive_tmp/usr/local/bin/$project" "build/src/$project" - status=$? - else - mv "build/src/$project" "archive_tmp/usr/local/bin/$project" - status=$? - fi -fi - -# Check if the executable strip/move succeeded. -if [ $status -ne 0 ] -then - echo [!] Executable strip/move failed with status [$status] - exit 6 -fi - -# Produce artifact archive. -echo [-] Creating artifact archive -if is_windows -then - # Create zip. - cd archive_tmp - "$sevenzip" a -y "$(cygpath -w "$cwd")\\$package_name.zip" * - status=$? -elif is_mac -then - # TBD - : -else - # Determine AppImage runtime architecture. - case $arch in - x86) arch_appimage="i686";; - arm32) arch_appimage="armhf";; - arm64) arch_appimage="aarch64";; - *) arch_appimage="$arch";; - esac - - # Get version for AppImage metadata. - project_version=$(grep -oP '#define\s+EMU_VERSION\s+"\K([^"]+)' "build/src/include/$project_lower/version.h" 2> /dev/null) - [ -z "$project_version" ] && project_version=unknown - build_num=$(grep -oP '#define\s+EMU_BUILD_NUM\s+\K([0-9]+)' "build/src/include/$project_lower/version.h" 2> /dev/null) - [ -n "$build_num" -a "$build_num" != "0" ] && project_version="$project_version-b$build_num" - - # Download appimage-builder if necessary. - [ ! -e "appimage-builder.AppImage" ] && wget -qO appimage-builder.AppImage \ - https://github.com/AppImageCrafters/appimage-builder/releases/download/v0.9.2/appimage-builder-0.9.2-35e3eab-x86_64.AppImage - chmod u+x appimage-builder.AppImage - - # Remove any dangling AppImages which may interfere with the renaming process. - rm -rf "$project-"*".AppImage" - - # Run appimage-builder in extract-and-run mode for Docker compatibility. - project="$project" project_lower="$project_lower" project_version="$project_version" arch_deb="$arch_deb" arch_appimage="$arch_appimage" \ - APPIMAGE_EXTRACT_AND_RUN=1 ./appimage-builder.AppImage --recipe .ci/AppImageBuilder.yml - status=$? - - # Rename AppImage to the final name if the build succeeded. - if [ $status -eq 0 ] - then - mv "$project-"*".AppImage" "$cwd/$package_name.AppImage" - status=$? - fi -fi -cd .. - -# Check if the archival succeeded. -if [ $status -ne 0 ] -then - echo [!] Artifact archive creation failed with status [$status] - exit 7 -fi - -# All good. -echo [-] Build of [$package_name] for [$arch] with flags [$cmake_flags] successful -exit 0 +#!/bin/sh +# +# 86Box A hypervisor and IBM PC system emulator that specializes in +# running old operating systems and software designed for IBM +# PC systems and compatibles from 1981 through fairly recent +# system designs based on the PCI bus. +# +# This file is part of the 86Box distribution. +# +# Jenkins build script. +# +# +# Authors: RichardG, +# +# Copyright 2021-2022 RichardG. +# + +# +# While this script was made for our Jenkins infrastructure, you can run it +# to produce Jenkins-like builds on your local machine by following these notes: +# +# - Run build.sh without parameters to see its usage +# - For Windows (MSYS MinGW) builds: +# - Packaging requires 7-Zip on Program Files +# - Packaging the Ghostscript DLL requires 32-bit and/or 64-bit Ghostscript on Program Files +# - Packaging the FluidSynth DLL requires it to be at /home/86Box/dll32/libfluidsynth.dll +# and/or /home/86Box/dll64/libfluidsynth64.dll (for 32-bit and 64-bit builds respectively) +# - Packaging the Discord DLL requires wget (MSYS should come with it) +# - For Linux builds: +# - Only Debian and derivatives are supported +# - dpkg and apt-get are called through sudo to manage dependencies +# - For macOS builds: +# - TBD +# + +# Define common functions. +alias is_windows='[ -n "$MSYSTEM" ]' +alias is_mac='uname -s | grep -q Darwin' + +make_tar() { + # Install dependencies. + if ! which tar xz > /dev/null 2>&1 + then + which apt-get > /dev/null 2>&1 && DEBIAN_FRONTEND=noninteractive sudo apt-get install -y tar xz-utils + fi + + # Determine the best supported compression type. + local compression_flag= + local compression_ext= + if which xz > /dev/null 2>&1 + then + local compression_flag=-J + local compression_ext=.xz + elif which bzip2 > /dev/null 2>&1 + then + local compression_flag=-j + local compression_ext=.bz2 + elif which gzip > /dev/null 2>&1 + then + local compression_flag=-z + local compression_ext=.gz + fi + + # Make tar verbose if requested. + [ -n "$VERBOSE" ] && local compression_flag="$compression_flag -v" + + # tar is notorious for having many diverging implementations. For instance, + # the flags we use to strip UID/GID metadata can be --owner/group (GNU), + # --uid/gid (bsdtar) or even none at all (MSYS2 bsdtar). Account for such + # flag differences by checking if they're mentioned on the help text. + local ownership_flags= + local tar_help=$(tar --help 2>&1) + if echo $tar_help | grep -q -- --owner + then + local ownership_flags="--owner=0 --group=0" + elif echo $tar_help | grep -q -- --uid + then + local ownership_flags="--uid 0 --gid 0" + fi + + # Run tar. + tar -c $compression_flag -f "$1$compression_ext" $ownership_flags * + return $? +} + +# Set common variables. +project=86Box +project_lower=86box +cwd=$(pwd) + +# Parse arguments. +package_name= +arch= +tarball_name= +strip=0 +cmake_flags= +while [ $# -gt 0 ] +do + case $1 in + -b) + shift + package_name="$1" + shift + arch="$1" + shift + ;; + + -s) + shift + tarball_name="$1" + shift + ;; + + -t) + shift + strip=1 + ;; + + *) + if echo $1 | grep -q " " + then + cmake_flag="\"$1\"" + else + cmake_flag="$1" + fi + if [ -z "$cmake_flags" ] + then + cmake_flags="$cmake_flag" + else + cmake_flags="$cmake_flags $cmake_flag" + fi + shift + ;; + esac +done +cmake_flags_extra= + +# Check if mandatory arguments were specified. +if [ -z "$package_name" -a -z "$tarball_name" ] || [ -n "$package_name" -a -z "$arch" ] +then + echo '[!] Usage: build.sh -b {package_name} {architecture} [-t] [cmake_flags...]' + echo ' build.sh -s {source_tarball_name}' + exit 100 +fi + +# Switch to the repository root directory. +cd "$(dirname "$0")/.." + +# Make source tarball if requested. +if [ -n "$tarball_name" ] +then + echo [-] Making source tarball [$tarball_name] + + # Clean local tree of gitignored files. + git clean -dfX + + # Recreate working directory if it was removed by git clean. + [ ! -d "$cwd" ] && mkdir -p "$cwd" + + # Save current HEAD commit to VERSION. + git log --stat -1 > VERSION || rm -f VERSION + + # Archive source. + make_tar "$cwd/$tarball_name.tar" + status=$? + + # Check if the archival succeeded. + if [ $status -ne 0 ] + then + echo [!] Tarball creation failed with status [$status] + exit 1 + else + echo [-] Source tarball [$tarball_name] created successfully + [ -z "$package_name" ] && exit 0 + fi +fi + +echo [-] Building [$package_name] for [$arch] with flags [$cmake_flags] + +# Determine CMake toolchain file for this architecture. +case $arch in + 32 | x86) toolchain="flags-gcc-i686";; + 64 | x86_64) toolchain="flags-gcc-x86_64";; + ARM32 | arm32) toolchain="flags-gcc-armv7";; + ARM64 | arm64) toolchain="flags-gcc-aarch64";; + *) toolchain="flags-gcc-$arch";; +esac + +# Perform platform-specific setup. +strip_binary=strip +if is_windows +then + # Switch into the correct MSYSTEM if required. + msys=MINGW$arch + [ ! -d "/$msys" ] && msys=CLANG$arch + if [ -d "/$msys" ] + then + if [ "$MSYSTEM" != "$msys" ] + then + # Call build with the correct MSYSTEM. + echo [-] Switching to MSYSTEM [$msys] + cd "$cwd" + strip_arg= + [ $strip -ne 0 ] && strip_arg="-t " + CHERE_INVOKING=yes MSYSTEM="$msys" bash -lc 'exec "'"$0"'" -b "'"$package_name"'" "'"$arch"'" '"$strip_arg""$cmake_flags" + exit $? + fi + else + echo [!] No MSYSTEM for architecture [$arch] + exit 2 + fi + echo [-] Using MSYSTEM [$MSYSTEM] + + # Update keyring, as the package signing keys sometimes change. + echo [-] Updating package databases and keyring + yes | pacman -Sy --needed msys2-keyring + + # Query installed packages. + pacman -Qe > pacman.txt + + # Download the specified versions of architecture-specific dependencies. + echo -n [-] Downloading dependencies: + pkg_dir="/var/cache/pacman/pkg" + repo_base="https://repo.msys2.org/mingw/$(echo $MSYSTEM | tr '[:upper:]' '[:lower:]')" + cat .ci/dependencies_msys.txt | tr -d '\r' > deps.txt + pkgs="" + while IFS=" " read pkg version + do + prefixed_pkg="$MINGW_PACKAGE_PREFIX-$pkg" + installed_version=$(grep -E "^$prefixed_pkg " pacman.txt | cut -d " " -f 2) + if [ "$installed_version" != "$version" ] # installed_version will be empty if not installed + then + echo -n " [$pkg" + + # Download package if not already present in the local cache. + pkg_tar="$prefixed_pkg-$version-any.pkg.tar" + if [ -s "$pkg_dir/$pkg_tar.xz" ] + then + pkg_fn="$pkg_tar.xz" + pkg_dest="$pkg_dir/$pkg_fn" + else + pkg_fn="$pkg_tar.zst" + pkg_dest="$pkg_dir/$pkg_fn" + if [ ! -s "$pkg_dest" ] + then + if ! wget -qO "$pkg_dest" "$repo_base/$pkg_fn" + then + rm -f "$pkg_dest" + pkg_fn="$pkg_tar.xz" + pkg_dest="$pkg_dir/$pkg_fn" + wget -qO "$pkg_dest" "$repo_base/$pkg_fn" || rm -f "$pkg_dest" + fi + if [ -s "$pkg_dest" ] + then + wget -qO "$pkg_dest.sig" "$repo_base/$pkg_fn.sig" || rm -f "$pkg_dest.sig" + [ ! -s "$pkg_dest.sig" ] && rm -f "$pkg_dest.sig" + fi + fi + fi + + # Check if the cached package is valid. + if [ -s "$pkg_dest" ] + then + # Add cached zst package. + pkgs="$pkgs $pkg_fn" + else + # Not valid, remove if it exists. + rm -f "$pkg_dest" "$pkg_dest.sig" + echo -n " FAIL" + fi + echo -n "]" + fi + done < deps.txt + [ -z "$pkgs" ] && echo -n none required + echo + + # Install the downloaded architecture-specific dependencies. + echo [-] Installing dependencies through pacman + if [ -n "$pkgs" ] + then + pushd "$pkg_dir" + yes | pacman -U --needed $pkgs + if [ $? -ne 0 ] + then + # Install packages individually if installing them all together failed. + for pkg in pkgs + do + yes | pacman -U --needed "$pkg" + done + fi + popd + + # Query installed packages again. + pacman -Qe > pacman.txt + fi + + # Install the latest versions for any missing packages (if the specified version couldn't be installed). + pkgs="make git" + while IFS=" " read pkg version + do + prefixed_pkg="$MINGW_PACKAGE_PREFIX-$pkg" + grep -qE "^$prefixed_pkg " pacman.txt || pkgs="$pkgs $prefixed_pkg" + done < deps.txt + rm -f pacman.txt deps.txt + yes | pacman -S --needed $pkgs + if [ $? -ne 0 ] + then + # Install packages individually if installing them all together failed. + for pkg in pkgs + do + yes | pacman -S --needed "$pkg" + done + fi + + # Point CMake to the toolchain file. + cmake_flags_extra="$cmake_flags_extra -D \"CMAKE_TOOLCHAIN_FILE=cmake/$toolchain.cmake\"" +elif is_mac +then + # macOS lacks nproc, but sysctl can do the same job. + alias nproc='sysctl -n hw.logicalcpu' +else + # Determine Debian architecture. + case $arch in + x86) arch_deb="i386";; + x86_64) arch_deb="amd64";; + arm32) arch_deb="armhf";; + *) arch_deb="$arch";; + esac + + # Establish general dependencies. + pkgs="cmake pkg-config git imagemagick wget p7zip-full wayland-protocols tar gzip file" + if [ "$(dpkg --print-architecture)" = "$arch_deb" ] + then + pkgs="$pkgs build-essential" + else + sudo dpkg --add-architecture "$arch_deb" + pkgs="$pkgs crossbuild-essential-$arch_deb" + fi + + # Establish architecture-specific dependencies we don't want listed on the readme... + pkgs="$pkgs linux-libc-dev:$arch_deb extra-cmake-modules:$arch_deb qttools5-dev:$arch_deb qtbase5-private-dev:$arch_deb" + + # ...and the ones we do want listed. Non-dev packages fill missing spots on the list. + libpkgs="" + longest_libpkg=0 + for pkg in libc6-dev libstdc++6 libopenal-dev libfreetype6-dev libx11-dev libsdl2-dev libpng-dev librtmidi-dev qtdeclarative5-dev libwayland-dev libevdev-dev libglib2.0-dev libslirp-dev libfaudio-dev libaudio-dev libjack-jackd2-dev libpipewire-0.3-dev libsamplerate0-dev libsndio-dev + do + libpkgs="$libpkgs $pkg:$arch_deb" + length=$(echo -n $pkg | sed 's/-dev$//' | sed "s/qtdeclarative/qt/" | wc -c) + [ $length -gt $longest_libpkg ] && longest_libpkg=$length + done + + # Determine GNU toolchain architecture. + case $arch in + x86) arch_gnu="i686-linux-gnu";; + arm32) arch_gnu="arm-linux-gnueabihf";; + arm64) arch_gnu="aarch64-linux-gnu";; + *) arch_gnu="$arch-linux-gnu";; + esac + + # Determine library directory name for this architecture. + case $arch in + x86) libdir="i386-linux-gnu";; + *) libdir="$arch_gnu";; + esac + + # Create CMake toolchain file. + cat << EOF > toolchain.cmake +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR $arch) + +set(CMAKE_AR $arch_gnu-ar) +set(CMAKE_ASM_COMPILER $arch_gnu-gcc) +set(CMAKE_C_COMPILER $arch_gnu-gcc) +set(CMAKE_CXX_COMPILER $arch_gnu-g++) +set(CMAKE_LINKER $arch_gnu-ld) +set(CMAKE_OBJCOPY $arch_gnu-objcopy) +set(CMAKE_RANLIB $arch_gnu-ranlib) +set(CMAKE_SIZE $arch_gnu-size) +set(CMAKE_STRIP $arch_gnu-strip) + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +set(ENV{PKG_CONFIG_PATH} "") +set(ENV{PKG_CONFIG_LIBDIR} "/usr/lib/$libdir/pkgconfig:/usr/share/$libdir/pkgconfig") + +include("$(pwd)/cmake/$toolchain.cmake") +EOF + cmake_flags_extra="$cmake_flags_extra -D CMAKE_TOOLCHAIN_FILE=toolchain.cmake" + strip_binary="$arch_gnu-strip" + + # Install or update dependencies. + echo [-] Installing dependencies through apt + sudo apt-get update + DEBIAN_FRONTEND=noninteractive sudo apt-get -y install $pkgs $libpkgs + sudo apt-get clean + + # Link against the system libslirp instead of compiling ours. + cmake_flags_extra="$cmake_flags_extra -D SLIRP_EXTERNAL=ON" +fi + +# Clean workspace. +echo [-] Cleaning workspace +if [ -d "build" ] +then + MAKEFLAGS=-j$(nproc) cmake --build build --target clean 2> /dev/null + rm -rf build +fi +find . \( -name Makefile -o -name CMakeCache.txt -o -name CMakeFiles \) -exec rm -rf "{}" \; 2> /dev/null + +# Add ARCH to skip the arch_detect process. +case $arch in + 32 | x86) cmake_flags_extra="$cmake_flags_extra -D ARCH=i386";; + 64 | x86_64) cmake_flags_extra="$cmake_flags_extra -D ARCH=x86_64";; + ARM32 | arm32) cmake_flags_extra="$cmake_flags_extra -D ARCH=arm";; + ARM64 | arm64) cmake_flags_extra="$cmake_flags_extra -D ARCH=arm64";; + *) cmake_flags_extra="$cmake_flags_extra -D \"ARCH=$arch\"";; +esac + +# Add git hash. +git_hash=$(git rev-parse --short HEAD 2> /dev/null) +if [ "$CI" = "true" ] +then + # Backup strategy when running under Jenkins. + [ -z "$git_hash" ] && git_hash=$(echo $GIT_COMMIT | cut -c 1-8) +elif [ -n "$git_hash" ] +then + # Append + to denote a dirty tree. + git diff --quiet 2> /dev/null || git_hash="$git_hash+" +fi +[ -n "$git_hash" ] && cmake_flags_extra="$cmake_flags_extra -D \"EMU_GIT_HASH=$git_hash\"" + +# Add copyright year. +year=$(date +%Y) +[ -n "$year" ] && cmake_flags_extra="$cmake_flags_extra -D \"EMU_COPYRIGHT_YEAR=$year\"" + +# Run CMake. +echo [-] Running CMake with flags [$cmake_flags $cmake_flags_extra] +eval cmake -G \"Unix Makefiles\" -B build $cmake_flags $cmake_flags_extra . +status=$? +if [ $status -ne 0 ] +then + echo [!] CMake failed with status [$status] + exit 3 +fi + +# Run actual build. +make_flags=-j$(nproc) +echo [-] Running build with make flags [$make_flags] +MAKEFLAGS=$make_flags cmake --build build +status=$? +if [ $status -ne 0 ] +then + echo [!] Make failed with status [$status] + exit 4 +fi + +# Download Discord Game SDK from their CDN if necessary. +if [ ! -e "discord_game_sdk.zip" ] +then + echo [-] Downloading Discord Game SDK + wget -qO discord_game_sdk.zip "https://dl-game-sdk.discordapp.net/2.5.6/discord_game_sdk.zip" + status=$? + if [ $status -ne 0 ] + then + echo [!] Discord Game SDK download failed with status [$status] + rm -f discord_game_sdk.zip + fi +fi + +# Determine Discord Game SDK architecture. +case $arch in + 32) arch_discord="x86";; + 64) arch_discord="x86_64";; + *) arch_discord="$arch";; +esac + +# Create temporary directory for archival. +echo [-] Gathering archive files +rm -rf archive_tmp +mkdir archive_tmp +if [ ! -d "archive_tmp" ] +then + echo [!] Archive directory creation failed + exit 5 +fi + +# Archive the executable and its dependencies. +# The executable should always be archived last for the check after this block. +status=0 +if is_windows +then + # Determine Program Files directory for Ghostscript and 7-Zip. + # Manual checks because MSYS is bad at passing the ProgramFiles variables. + pf="/c/Program Files" + sevenzip="$pf/7-Zip/7z.exe" + [ "$arch" = "32" -a -d "/c/Program Files (x86)" ] && pf="/c/Program Files (x86)" + + # Archive freetype from local MSYS installation. + .ci/static2dll.sh -p freetype2 /$MSYSTEM/lib/libfreetype.a archive_tmp/freetype.dll + + # Archive Ghostscript DLL from local official distribution installation. + for gs in "$pf"/gs/gs*.*.* + do + cp -p "$gs"/bin/gsdll*.dll archive_tmp/ + done + + # Archive Discord Game SDK DLL. + "$sevenzip" e -y -o"archive_tmp" discord_game_sdk.zip "lib/$arch_discord/discord_game_sdk.dll" + [ ! -e "archive_tmp/discord_game_sdk.dll" ] && echo [!] No Discord Game SDK for architecture [$arch_discord] + + # Archive other DLLs from local directory. + cp -p "/home/$project/dll$arch/"* archive_tmp/ + + # Archive executable, while also stripping it if requested. + if [ $strip -ne 0 ] + then + "$strip_binary" -o "archive_tmp/$project.exe" "build/src/$project.exe" + status=$? + else + mv "build/src/$project.exe" "archive_tmp/$project.exe" + status=$? + fi +elif is_mac +then + # TBD + : +else + cwd_root=$(pwd) + + if grep -q "OPENAL:BOOL=ON" build/CMakeCache.txt + then + # Build openal-soft 1.21.1 manually to fix audio issues. This is a temporary + # workaround until a newer version of openal-soft trickles down to Debian repos. + if [ -d "openal-soft-1.21.1" ] + then + rm -rf openal-soft-1.21.1/build/* + else + wget -qO - https://github.com/kcat/openal-soft/archive/refs/tags/1.21.1.tar.gz | tar zxf - + fi + cd openal-soft-1.21.1/build + [ -e Makefile ] && make clean + cmake -G "Unix Makefiles" -D "CMAKE_TOOLCHAIN_FILE=$cwd_root/toolchain.cmake" -D "CMAKE_INSTALL_PREFIX=$cwd_root/archive_tmp/usr" .. + make -j$(nproc) install || exit 99 + cd "$cwd_root" + + # Build SDL2 without sound systems. + sdl_ss=OFF + else + # Build FAudio 22.03 manually to remove the dependency on GStreamer. This is a temporary + # workaround until a newer version of FAudio trickles down to Debian repos. + if [ -d "FAudio-22.03" ] + then + rm -rf FAudio-22.03/build + else + wget -qO - https://github.com/FNA-XNA/FAudio/archive/refs/tags/22.03.tar.gz | tar zxf - + fi + mkdir FAudio-22.03/build + cd FAudio-22.03/build + cmake -G "Unix Makefiles" -D "CMAKE_TOOLCHAIN_FILE=$cwd_root/toolchain.cmake" -D "CMAKE_INSTALL_PREFIX=$cwd_root/archive_tmp/usr" .. + make -j$(nproc) install || exit 99 + cd "$cwd_root" + + # Build SDL2 with sound systems. + sdl_ss=ON + fi + + # Build rtmidi without JACK support to remove the dependency on libjack. + if [ -d "rtmidi-4.0.0" ] + then + rm -rf rtmidi-4.0.0/CMakeCache.txt rtmidi-4.0.0/CMakeFiles + else + wget -qO - http://www.music.mcgill.ca/~gary/rtmidi/release/rtmidi-4.0.0.tar.gz | tar zxf - + fi + cwd_root=$(pwd) + cd rtmidi-4.0.0 + [ -e Makefile ] && make clean + cmake -G "Unix Makefiles" -D RTMIDI_API_JACK=OFF -D "CMAKE_TOOLCHAIN_FILE=$cwd_root/toolchain.cmake" -D "CMAKE_INSTALL_PREFIX=$cwd_root/archive_tmp/usr" . + make -j$(nproc) install || exit 99 + cd "$cwd_root" + + # Build SDL2 for joystick support with most components disabled to remove the dependencies on PulseAudio and libdrm. + if [ ! -d "SDL2-2.0.20" ] + then + wget -qO - https://www.libsdl.org/release/SDL2-2.0.20.tar.gz | tar zxf - + fi + rm -rf sdlbuild + mkdir sdlbuild + cd sdlbuild + cmake -G "Unix Makefiles" -D SDL_DISKAUDIO=OFF -D SDL_DIRECTFB_SHARED=OFF -D SDL_OPENGL=OFF -D SDL_OPENGLES=OFF -D SDL_OSS=OFF -D SDL_ALSA=$sdl_ss \ + -D SDL_ALSA_SHARED=$sdl_ss -D SDL_JACK=$sdl_ss -D SDL_JACK_SHARED=$sdl_ss -D SDL_ESD=OFF -D SDL_ESD_SHARED=OFF -D SDL_PIPEWIRE=$sdl_ss -D SDL_PIPEWIRE_SHARED=$sdl_ss \ + -D SDL_PULSEAUDIO=$sdl_ss -D SDL_PULSEAUDIO_SHARED=$sdl_ss -D SDL_ARTS=OFF -D SDL_ARTS_SHARED=OFF -D SDL_NAS=$sdl_ss -D SDL_NAS_SHARED=$sdl_ss -D SDL_SNDIO=$sdl_ss \ + -D SDL_SNDIO_SHARED=$sdl_ss -D SDL_FUSIONSOUND=OFF -D SDL_FUSIONSOUND_SHARED=OFF -D SDL_LIBSAMPLERATE=$sdl_ss -D SDL_LIBSAMPLERATE_SHARED=$sdl_ss -D SDL_X11=OFF \ + -D SDL_X11_SHARED=OFF -D SDL_WAYLAND=OFF -D SDL_WAYLAND_SHARED=OFF -D SDL_WAYLAND_LIBDECOR=OFF -D SDL_WAYLAND_LIBDECOR_SHARED=OFF \ + -D SDL_WAYLAND_QT_TOUCH=OFF -D SDL_RPI=OFF -D SDL_VIVANTE=OFF -D SDL_VULKAN=OFF -D SDL_KMSDRM=OFF -D SDL_KMSDRM_SHARED=OFF -D SDL_OFFSCREEN=OFF \ + -D SDL_HIDAPI_JOYSTICK=ON -D SDL_VIRTUAL_JOYSTICK=ON -D SDL_SHARED=ON -D SDL_STATIC=OFF -S "$cwd_root/SDL2-2.0.20" \ + -D "CMAKE_TOOLCHAIN_FILE=$cwd_root/toolchain.cmake" -D "CMAKE_INSTALL_PREFIX=$cwd_root/archive_tmp/usr" + make -j$(nproc) install || exit 99 + cd "$cwd_root" + + # Archive Discord Game SDK library. + 7z e -y -o"archive_tmp/usr/lib" discord_game_sdk.zip "lib/$arch_discord/discord_game_sdk.so" + [ ! -e "archive_tmp/usr/lib/discord_game_sdk.so" ] && echo [!] No Discord Game SDK for architecture [$arch_discord] + + # Archive readme with library package versions. + echo Libraries used to compile this $arch build of $project: > archive_tmp/README + dpkg-query -f '${Package} ${Version}\n' -W $libpkgs | sed "s/-dev / /" | sed "s/qtdeclarative/qt/" | while IFS=" " read pkg version + do + for i in $(seq $(expr $longest_libpkg - $(echo -n $pkg | wc -c))) + do + echo -n " " >> archive_tmp/README + done + echo $pkg $version >> archive_tmp/README + done + + # Archive icon, while also shrinking it to 512x512 if necessary. + convert src/win/assets/$project_lower.png -resize '512x512>' icon.png + icon_base="$(identify -format 'archive_tmp/usr/share/icons/%wx%h' icon.png)" + mkdir -p "$icon_base" + mv icon.png "$icon_base/$project_lower.png" + + # Archive executable, while also stripping it if requested. + mkdir -p archive_tmp/usr/local/bin + if [ $strip -ne 0 ] + then + "$strip_binary" -o "archive_tmp/usr/local/bin/$project" "build/src/$project" + status=$? + else + mv "build/src/$project" "archive_tmp/usr/local/bin/$project" + status=$? + fi +fi + +# Check if the executable strip/move succeeded. +if [ $status -ne 0 ] +then + echo [!] Executable strip/move failed with status [$status] + exit 6 +fi + +# Produce artifact archive. +echo [-] Creating artifact archive +if is_windows +then + # Create zip. + cd archive_tmp + "$sevenzip" a -y "$(cygpath -w "$cwd")\\$package_name.zip" * + status=$? +elif is_mac +then + # TBD + : +else + # Determine AppImage runtime architecture. + case $arch in + x86) arch_appimage="i686";; + arm32) arch_appimage="armhf";; + arm64) arch_appimage="aarch64";; + *) arch_appimage="$arch";; + esac + + # Get version for AppImage metadata. + project_version=$(grep -oP '#define\s+EMU_VERSION\s+"\K([^"]+)' "build/src/include/$project_lower/version.h" 2> /dev/null) + [ -z "$project_version" ] && project_version=unknown + build_num=$(grep -oP '#define\s+EMU_BUILD_NUM\s+\K([0-9]+)' "build/src/include/$project_lower/version.h" 2> /dev/null) + [ -n "$build_num" -a "$build_num" != "0" ] && project_version="$project_version-b$build_num" + + # Download appimage-builder if necessary. + [ ! -e "appimage-builder.AppImage" ] && wget -qO appimage-builder.AppImage \ + https://github.com/AppImageCrafters/appimage-builder/releases/download/v0.9.2/appimage-builder-0.9.2-35e3eab-x86_64.AppImage + chmod u+x appimage-builder.AppImage + + # Remove any dangling AppImages which may interfere with the renaming process. + rm -rf "$project-"*".AppImage" + + # Run appimage-builder in extract-and-run mode for Docker compatibility. + project="$project" project_lower="$project_lower" project_version="$project_version" arch_deb="$arch_deb" arch_appimage="$arch_appimage" \ + APPIMAGE_EXTRACT_AND_RUN=1 ./appimage-builder.AppImage --recipe .ci/AppImageBuilder.yml + status=$? + + # Rename AppImage to the final name if the build succeeded. + if [ $status -eq 0 ] + then + mv "$project-"*".AppImage" "$cwd/$package_name.AppImage" + status=$? + fi +fi +cd .. + +# Check if the archival succeeded. +if [ $status -ne 0 ] +then + echo [!] Artifact archive creation failed with status [$status] + exit 7 +fi + +# All good. +echo [-] Build of [$package_name] for [$arch] with flags [$cmake_flags] successful +exit 0 From 0cdc68d2bd465b39de56fcb4f3ad5f47f92765e1 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Tue, 15 Mar 2022 21:45:08 -0300 Subject: [PATCH 41/62] Jenkins: Make the pipeline more resilient to node dropouts --- .ci/Jenkinsfile | 142 +++++++++++++++++++++++++++--------------------- 1 file changed, 81 insertions(+), 61 deletions(-) diff --git a/.ci/Jenkinsfile b/.ci/Jenkinsfile index 00532dc58..1c70bd0f4 100644 --- a/.ci/Jenkinsfile +++ b/.ci/Jenkinsfile @@ -12,7 +12,7 @@ * * Authors: RichardG, * - * Copyright 2021 RichardG. + * Copyright 2021-2022 RichardG. */ def repository = 'https://github.com/86Box/86Box.git' @@ -105,9 +105,9 @@ def gitClone(repository, branch) { } else if (env.GIT_COMMIT != scmVars.GIT_COMMIT) { /* Checkout the commit read from the polling log. */ if (isUnix()) - sh "git checkout ${env.GIT_COMMIT} || exit 0" + sh returnStatus: true, script: "git checkout ${env.GIT_COMMIT}" else - bat "git checkout ${env.GIT_COMMIT} || exit /b 0" + bat returnStatus: true, script: "git checkout ${env.GIT_COMMIT}" } println "[-] Using git commit [${env.GIT_COMMIT}]" @@ -122,16 +122,26 @@ def gitClone(repository, branch) { def removeDir(dir) { if (isUnix()) - sh "rm -rf \"$dir\" || exit 0" + return sh(returnStatus: true, script: "rm -rf \"$dir\"") else - bat "if exist \"$dir\" rd /s /q \"$dir\" & exit /b 0" + return bat(returnStatus: true, script: "rd /s /q \"$dir\"") } def runBuild(args) { if (isUnix()) - sh "chmod u+x \"$WORKSPACE/.ci/build.sh\" && exec \"$WORKSPACE/.ci/build.sh\" $args" + return sh(returnStatus: true, script: "chmod u+x \"$WORKSPACE/.ci/build.sh\" && exec \"$WORKSPACE/.ci/build.sh\" $args") else - bat "C:\\msys64\\msys2_shell.cmd -msys2 -defterm -here -no-start -c 'exec \"\$(cygpath -u \\'%WORKSPACE%\\')/.ci/build.sh\" $args'" + return bat(returnStatus: true, script: "C:\\msys64\\msys2_shell.cmd -msys2 -defterm -here -no-start -c 'exec \"\$(cygpath -u \\'%WORKSPACE%\\')/.ci/build.sh\" $args'") +} + +def failStage() { + /* Mark that a failure occurred. */ + anyFailure = true + + /* Force this stage to fail. */ + catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') { + def x = 1 / 0 + } } pipeline { @@ -142,7 +152,6 @@ pipeline { } options { - disableConcurrentBuilds() quietPeriod(0) } @@ -155,6 +164,7 @@ pipeline { stages { stage('Source Tarball') { agent none + failFast false steps { script { @@ -178,85 +188,95 @@ pipeline { /* Adding to the above, run a git clone as soon as possible on any node to further avoid race conditions caused by busy node executor delays. */ - node { - /* Run git clone. */ - gitClone(repository, branch) - - /* Clean workspace, in case this is running in a non-build node. */ - cleanWs() - } - - /* Create source tarball. */ - node('Linux') { - try { + retry(10) { + node('citadel') { /* Run git clone. */ gitClone(repository, branch) - /* Switch to temp directory. */ - dir("${env.WORKSPACE_TMP}/output") { - /* Run source tarball creation process. */ - def packageName = "${env.JOB_BASE_NAME}-Source-b${env.BUILD_NUMBER}" - runBuild("-s \"$packageName\"") + /* Clean workspace, in case this is running in a non-build node. */ + cleanWs() + } + } - /* Archive resulting artifacts. */ - archiveArtifacts artifacts: "$packageName*" - } + /* Create source tarball. */ + try { + retry(10) { + node('Linux') { + /* Run git clone. */ + gitClone(repository, branch) - /* Clean up. */ - removeDir("${env.WORKSPACE_TMP}/output") - } catch (e) { - /* Mark that a failure occurred. */ - anyFailure = true + /* Switch to temp directory. */ + dir("${env.WORKSPACE_TMP}/output") { + /* Run source tarball creation process. */ + def packageName = "${env.JOB_BASE_NAME}-Source-b${env.BUILD_NUMBER}" + if (runBuild("-s \"$packageName\"") == 0) { + /* Archive resulting artifacts. */ + archiveArtifacts artifacts: "$packageName*" + } else { + /* Fail this stage. */ + failStage() + } + } - /* Force this stage to fail. */ - catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') { - throw e; + /* Clean up. */ + removeDir("${env.WORKSPACE_TMP}/output") } } + } catch (e) { + /* Fail this stage. */ + failStage() } /* Build here to avoid creating a redundant parent stage on the stage view. */ osArchs.each { os, thisOsArchs -> + def combinations = [:] thisOsArchs.each { arch -> def thisArchDynarecs = dynarecArchs[arch] if (!thisArchDynarecs) thisArchDynarecs = ['NoDR'] thisArchDynarecs.each { dynarec -> presets.each { preset -> - node(os) { - stage("$os $arch $dynarec $preset") { - try { - /* Run git clone. */ - gitClone(repository, branch) + def combination = "$os $arch $dynarec $preset" + combinations[combination] = { + try { + retry(10) { + node(os) { + stage(combination) { + /* Run git clone. */ + gitClone(repository, branch) - /* Switch to output directory. */ - dir("${env.WORKSPACE_TMP}/output") { - /* Run build process. */ - def packageName = "${env.JOB_BASE_NAME}${dynarecSlugs[dynarec]}${presetSlugs[preset]}-$os-$arch-b${env.BUILD_NUMBER}" - dir("${dynarecNames[dynarec]}/$os - ${archNames[arch]}") { - runBuild("-b \"$packageName\" \"$arch\" ${presetFlags[preset]} ${dynarecFlags[dynarec]} ${osFlags[os]} -D \"BUILD_TYPE=$BUILD_TYPE\" -D \"EMU_BUILD=build ${env.BUILD_NUMBER}\" -D \"EMU_BUILD_NUM=${env.BUILD_NUMBER}\"") + /* Switch to output directory. */ + dir("${env.WORKSPACE_TMP}/output") { + /* Run build process. */ + def packageName = "${env.JOB_BASE_NAME}${dynarecSlugs[dynarec]}${presetSlugs[preset]}-$os-$arch-b${env.BUILD_NUMBER}" + def ret = -1 + dir("${dynarecNames[dynarec]}/$os - ${archNames[arch]}") { + ret = runBuild("-b \"$packageName\" \"$arch\" ${presetFlags[preset]} ${dynarecFlags[dynarec]} ${osFlags[os]} -D \"BUILD_TYPE=$BUILD_TYPE\" -D \"EMU_BUILD=build ${env.BUILD_NUMBER}\" -D \"EMU_BUILD_NUM=${env.BUILD_NUMBER}\"") + } + + if (ret == 0) { + /* Archive resulting artifacts. */ + archiveArtifacts artifacts: "**/**/$packageName*" + } else { + /* Fail this stage. */ + failStage() + } + } + + /* Clean up. */ + removeDir("${env.WORKSPACE_TMP}/output") } - - /* Archive resulting artifacts. */ - archiveArtifacts artifacts: "**/**/$packageName*" - } - - /* Clean up. */ - removeDir("${env.WORKSPACE_TMP}/output") - } catch (e) { - /* Mark that a failure occurred. */ - anyFailure = true - - /* Force this stage to fail. */ - catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') { - throw e; } } + } catch (e) { + /* Fail this stage. */ + failStage() } } } } } + parallel combinations } } } @@ -287,7 +307,7 @@ pipeline { scmWebUrl: commitBrowser /* Notify IRC, which needs a node for whatever reason. */ - node { + node('citadel') { ircNotify() } } catch (e) { From 1cc29c0aab3b3fa013c4ec9c10264b6d71518ad5 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Tue, 15 Mar 2022 21:46:10 -0300 Subject: [PATCH 42/62] Jenkins: Small cosmetic change to build script --- .ci/build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/build.sh b/.ci/build.sh index 6e0257876..72c08129c 100644 --- a/.ci/build.sh +++ b/.ci/build.sh @@ -271,7 +271,7 @@ then echo -n "]" fi done < deps.txt - [ -z "$pkgs" ] && echo -n none required + [ -z "$pkgs" ] && echo -n ' none required' echo # Install the downloaded architecture-specific dependencies. From 68ad22bf0969d2b6602475693f2fed36370b2496 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Tue, 15 Mar 2022 23:16:41 -0300 Subject: [PATCH 43/62] Jenkins: Streamline AppImage exclusion list with globs --- .ci/AppImageBuilder.yml | 33 ++++----------------------------- 1 file changed, 4 insertions(+), 29 deletions(-) diff --git a/.ci/AppImageBuilder.yml b/.ci/AppImageBuilder.yml index c4a250563..cadb3862d 100644 --- a/.ci/AppImageBuilder.yml +++ b/.ci/AppImageBuilder.yml @@ -63,39 +63,14 @@ AppDir: - etc - lib/udev - opt/libc/usr/share - - usr/bin - - usr/include + - usr/[a-km-rt-zA-Z]* - usr/lib/*/libasound.so.* - usr/lib/*.a - usr/lib/cmake - usr/lib/pkgconfig - - usr/sbin - - usr/share/aclocal - - usr/share/alsa - - usr/share/apport - - usr/share/bug - - usr/share/color - - usr/share/doc - - usr/share/doc-base - - usr/share/fontconfig - - usr/share/fonts - - usr/share/ghostscript - - usr/share/glib-2.0 - - usr/share/info - - usr/share/libinput - - usr/share/libwacom - - usr/share/lintian - - usr/share/locale - - usr/share/man - - usr/share/metainfo - - usr/share/openal - - usr/share/pkgconfig - - usr/share/poppler - - usr/share/readline - - usr/share/rtmidi - - usr/share/sounds - - usr/share/X11 - - usr/share/xml + - usr/s[a-gi-zA-Z]* + - usr/share/[a-hj-zA-Z]* + - usr/share/i[a-bd-zA-Z]* - var AppImage: arch: !ENV '${arch_appimage}' From cc800ee64dd265949445df55dcee9f586cb44600 Mon Sep 17 00:00:00 2001 From: richardg867 Date: Tue, 15 Mar 2022 23:18:03 -0300 Subject: [PATCH 44/62] Jenkins: Make the pipeline more resilient to node dropouts --- .ci/Jenkinsfile | 142 +++++++++++++++++++++++++++--------------------- 1 file changed, 81 insertions(+), 61 deletions(-) diff --git a/.ci/Jenkinsfile b/.ci/Jenkinsfile index e20b0d363..1c70bd0f4 100644 --- a/.ci/Jenkinsfile +++ b/.ci/Jenkinsfile @@ -12,7 +12,7 @@ * * Authors: RichardG, * - * Copyright 2021 RichardG. + * Copyright 2021-2022 RichardG. */ def repository = 'https://github.com/86Box/86Box.git' @@ -105,9 +105,9 @@ def gitClone(repository, branch) { } else if (env.GIT_COMMIT != scmVars.GIT_COMMIT) { /* Checkout the commit read from the polling log. */ if (isUnix()) - sh "git checkout ${env.GIT_COMMIT} || exit 0" + sh returnStatus: true, script: "git checkout ${env.GIT_COMMIT}" else - bat "git checkout ${env.GIT_COMMIT} || exit /b 0" + bat returnStatus: true, script: "git checkout ${env.GIT_COMMIT}" } println "[-] Using git commit [${env.GIT_COMMIT}]" @@ -122,16 +122,26 @@ def gitClone(repository, branch) { def removeDir(dir) { if (isUnix()) - sh "rm -rf \"$dir\" || exit 0" + return sh(returnStatus: true, script: "rm -rf \"$dir\"") else - bat "if exist \"$dir\" rd /s /q \"$dir\" & exit /b 0" + return bat(returnStatus: true, script: "rd /s /q \"$dir\"") } def runBuild(args) { if (isUnix()) - sh "chmod u+x \"$WORKSPACE/.ci/build.sh\" && exec \"$WORKSPACE/.ci/build.sh\" $args" + return sh(returnStatus: true, script: "chmod u+x \"$WORKSPACE/.ci/build.sh\" && exec \"$WORKSPACE/.ci/build.sh\" $args") else - bat "C:\\msys64\\msys2_shell.cmd -msys2 -defterm -here -no-start -c 'exec \"\$(cygpath -u \\'%WORKSPACE%\\')/.ci/build.sh\" $args'" + return bat(returnStatus: true, script: "C:\\msys64\\msys2_shell.cmd -msys2 -defterm -here -no-start -c 'exec \"\$(cygpath -u \\'%WORKSPACE%\\')/.ci/build.sh\" $args'") +} + +def failStage() { + /* Mark that a failure occurred. */ + anyFailure = true + + /* Force this stage to fail. */ + catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') { + def x = 1 / 0 + } } pipeline { @@ -142,7 +152,6 @@ pipeline { } options { - disableConcurrentBuilds() quietPeriod(0) } @@ -155,6 +164,7 @@ pipeline { stages { stage('Source Tarball') { agent none + failFast false steps { script { @@ -178,85 +188,95 @@ pipeline { /* Adding to the above, run a git clone as soon as possible on any node to further avoid race conditions caused by busy node executor delays. */ - node('!Windows') { - /* Run git clone. */ - gitClone(repository, branch) - - /* Clean workspace, in case this is running in a non-build node. */ - cleanWs() - } - - /* Create source tarball. */ - node('Linux') { - try { + retry(10) { + node('citadel') { /* Run git clone. */ gitClone(repository, branch) - /* Switch to temp directory. */ - dir("${env.WORKSPACE_TMP}/output") { - /* Run source tarball creation process. */ - def packageName = "${env.JOB_BASE_NAME}-Source-b${env.BUILD_NUMBER}" - runBuild("-s \"$packageName\"") + /* Clean workspace, in case this is running in a non-build node. */ + cleanWs() + } + } - /* Archive resulting artifacts. */ - archiveArtifacts artifacts: "$packageName*" - } + /* Create source tarball. */ + try { + retry(10) { + node('Linux') { + /* Run git clone. */ + gitClone(repository, branch) - /* Clean up. */ - removeDir("${env.WORKSPACE_TMP}/output") - } catch (e) { - /* Mark that a failure occurred. */ - anyFailure = true + /* Switch to temp directory. */ + dir("${env.WORKSPACE_TMP}/output") { + /* Run source tarball creation process. */ + def packageName = "${env.JOB_BASE_NAME}-Source-b${env.BUILD_NUMBER}" + if (runBuild("-s \"$packageName\"") == 0) { + /* Archive resulting artifacts. */ + archiveArtifacts artifacts: "$packageName*" + } else { + /* Fail this stage. */ + failStage() + } + } - /* Force this stage to fail. */ - catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') { - throw e; + /* Clean up. */ + removeDir("${env.WORKSPACE_TMP}/output") } } + } catch (e) { + /* Fail this stage. */ + failStage() } /* Build here to avoid creating a redundant parent stage on the stage view. */ osArchs.each { os, thisOsArchs -> + def combinations = [:] thisOsArchs.each { arch -> def thisArchDynarecs = dynarecArchs[arch] if (!thisArchDynarecs) thisArchDynarecs = ['NoDR'] thisArchDynarecs.each { dynarec -> presets.each { preset -> - node(os) { - stage("$os $arch $dynarec $preset") { - try { - /* Run git clone. */ - gitClone(repository, branch) + def combination = "$os $arch $dynarec $preset" + combinations[combination] = { + try { + retry(10) { + node(os) { + stage(combination) { + /* Run git clone. */ + gitClone(repository, branch) - /* Switch to output directory. */ - dir("${env.WORKSPACE_TMP}/output") { - /* Run build process. */ - def packageName = "${env.JOB_BASE_NAME}${dynarecSlugs[dynarec]}${presetSlugs[preset]}-$os-$arch-b${env.BUILD_NUMBER}" - dir("${dynarecNames[dynarec]}/$os - ${archNames[arch]}") { - runBuild("-b \"$packageName\" \"$arch\" ${presetFlags[preset]} ${dynarecFlags[dynarec]} ${osFlags[os]} -D \"BUILD_TYPE=$BUILD_TYPE\" -D \"EMU_BUILD=build ${env.BUILD_NUMBER}\" -D \"EMU_BUILD_NUM=${env.BUILD_NUMBER}\"") + /* Switch to output directory. */ + dir("${env.WORKSPACE_TMP}/output") { + /* Run build process. */ + def packageName = "${env.JOB_BASE_NAME}${dynarecSlugs[dynarec]}${presetSlugs[preset]}-$os-$arch-b${env.BUILD_NUMBER}" + def ret = -1 + dir("${dynarecNames[dynarec]}/$os - ${archNames[arch]}") { + ret = runBuild("-b \"$packageName\" \"$arch\" ${presetFlags[preset]} ${dynarecFlags[dynarec]} ${osFlags[os]} -D \"BUILD_TYPE=$BUILD_TYPE\" -D \"EMU_BUILD=build ${env.BUILD_NUMBER}\" -D \"EMU_BUILD_NUM=${env.BUILD_NUMBER}\"") + } + + if (ret == 0) { + /* Archive resulting artifacts. */ + archiveArtifacts artifacts: "**/**/$packageName*" + } else { + /* Fail this stage. */ + failStage() + } + } + + /* Clean up. */ + removeDir("${env.WORKSPACE_TMP}/output") } - - /* Archive resulting artifacts. */ - archiveArtifacts artifacts: "**/**/$packageName*" - } - - /* Clean up. */ - removeDir("${env.WORKSPACE_TMP}/output") - } catch (e) { - /* Mark that a failure occurred. */ - anyFailure = true - - /* Force this stage to fail. */ - catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') { - throw e; } } + } catch (e) { + /* Fail this stage. */ + failStage() } } } } } + parallel combinations } } } @@ -287,7 +307,7 @@ pipeline { scmWebUrl: commitBrowser /* Notify IRC, which needs a node for whatever reason. */ - node { + node('citadel') { ircNotify() } } catch (e) { From 81cf8b7e8e78d39410c51c033ab08ea27c33e2a1 Mon Sep 17 00:00:00 2001 From: richardg867 Date: Tue, 15 Mar 2022 23:23:35 -0300 Subject: [PATCH 45/62] Jenkins: Don't git clone on Windows nodes due to line ending issues --- .ci/Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/Jenkinsfile b/.ci/Jenkinsfile index 1c70bd0f4..7c5502cec 100644 --- a/.ci/Jenkinsfile +++ b/.ci/Jenkinsfile @@ -189,7 +189,7 @@ pipeline { /* Adding to the above, run a git clone as soon as possible on any node to further avoid race conditions caused by busy node executor delays. */ retry(10) { - node('citadel') { + node('citadel && !Windows') { /* Run git clone. */ gitClone(repository, branch) From 7a3ff82a633b96ca8cd20a71ecef25fee32727cb Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Tue, 15 Mar 2022 23:23:47 -0300 Subject: [PATCH 46/62] Jenkins: Don't git clone on Windows nodes due to line ending issues --- .ci/Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/Jenkinsfile b/.ci/Jenkinsfile index 1c70bd0f4..7c5502cec 100644 --- a/.ci/Jenkinsfile +++ b/.ci/Jenkinsfile @@ -189,7 +189,7 @@ pipeline { /* Adding to the above, run a git clone as soon as possible on any node to further avoid race conditions caused by busy node executor delays. */ retry(10) { - node('citadel') { + node('citadel && !Windows') { /* Run git clone. */ gitClone(repository, branch) From f57cbe36b1cd9a266411970844d4975f142e5124 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Wed, 16 Mar 2022 00:33:01 -0300 Subject: [PATCH 47/62] GDB Stub: More progress --- CMakeLists.txt | 2 +- src/86box.c | 3 + src/cpu/386.c | 2 +- src/cpu/386_dynarec.c | 6 + src/cpu/386_dynarec_ops.c | 1 + src/cpu/808x.c | 4 +- src/cpu/cpu.c | 2 + src/cpu/x86_ops_int.h | 4 + src/cpu/x87_ops.h | 88 +--- src/cpu/x87_ops_conv.h | 68 +++ src/gdbstub.c | 937 ++++++++++++++++++++++++++++++------ src/include/86box/gdbstub.h | 48 +- src/mem/mem.c | 23 + src/qt/qt_main.cpp | 6 + src/qt/qt_platform.cpp | 3 - src/unix/unix.c | 7 +- src/win/win.c | 6 + src/win/win_ui.c | 3 - 18 files changed, 972 insertions(+), 241 deletions(-) create mode 100644 src/cpu/x87_ops_conv.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 80a77d9f7..ac268dea5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -121,7 +121,7 @@ option(DINPUT "DirectInput" option(CPPTHREADS "C++11 threads" ON) option(NEW_DYNAREC "Use the PCem v15 (\"new\") dynamic recompiler" OFF) option(MINITRACE "Enable Chrome tracing using the modified minitrace library" OFF) -option(GDBSTUB "Enable GDB stub server for debugging" ON) +option(GDBSTUB "Enable GDB stub server for debugging" OFF) option(DEV_BRANCH "Development branch" OFF) if(NOT WIN32) option(QT "QT GUI" ON) diff --git a/src/86box.c b/src/86box.c index 262dc436d..2d6095560 100644 --- a/src/86box.c +++ b/src/86box.c @@ -1204,6 +1204,9 @@ pc_run(void) /* Run a block of code. */ startblit(); cpu_exec(cpu_s->rspeed / 100); +#ifdef USE_GDBSTUB /* avoid a KBC FIFO overflow when CPU emulation is stalled */ + if (gdbstub_step == GDBSTUB_EXEC) +#endif mouse_process(); joystick_process(); endblit(); diff --git a/src/cpu/386.c b/src/cpu/386.c index 48fb30798..2484cbb66 100644 --- a/src/cpu/386.c +++ b/src/cpu/386.c @@ -259,7 +259,7 @@ exec386(int cycs) timer_process_inline(); #ifdef USE_GDBSTUB - if (gdbstub_singlestep) + if (gdbstub_instruction()) return; #endif } diff --git a/src/cpu/386_dynarec.c b/src/cpu/386_dynarec.c index 6e70c03ea..e9a187216 100644 --- a/src/cpu/386_dynarec.c +++ b/src/cpu/386_dynarec.c @@ -26,6 +26,7 @@ #include <86box/fdd.h> #include <86box/fdc.h> #include <86box/machine.h> +#include <86box/gdbstub.h> #ifdef USE_DYNAREC #include "codegen.h" #ifdef USE_NEW_DYNAREC @@ -858,6 +859,11 @@ exec386_dynarec(int cycs) if (TIMER_VAL_LESS_THAN_VAL(timer_target, (uint32_t) tsc)) timer_process_inline(); } + +#ifdef USE_GDBSTUB + if (gdbstub_instruction()) + return; +#endif } cycles_main -= (cycles_start - cycles); diff --git a/src/cpu/386_dynarec_ops.c b/src/cpu/386_dynarec_ops.c index 2eb3a5f00..28fd3ec79 100644 --- a/src/cpu/386_dynarec_ops.c +++ b/src/cpu/386_dynarec_ops.c @@ -19,6 +19,7 @@ #include <86box/mem.h> #include <86box/nmi.h> #include <86box/pic.h> +#include <86box/gdbstub.h> #include "codegen.h" #define CPU_BLOCK_END() cpu_block_end = 1 diff --git a/src/cpu/808x.c b/src/cpu/808x.c index a00a4b057..96b184bc4 100644 --- a/src/cpu/808x.c +++ b/src/cpu/808x.c @@ -2835,8 +2835,8 @@ execx86(int cycs) } #ifdef USE_GDBSTUB - if (gdbstub_singlestep) - return; + if (gdbstub_instruction()) + return; #endif } } diff --git a/src/cpu/cpu.c b/src/cpu/cpu.c index 9909e243c..b416206f6 100644 --- a/src/cpu/cpu.c +++ b/src/cpu/cpu.c @@ -36,6 +36,7 @@ #include <86box/nmi.h> #include <86box/pic.h> #include <86box/pci.h> +#include <86box/gdbstub.h> #ifdef USE_DYNAREC # include "codegen.h" #endif @@ -1383,6 +1384,7 @@ cpu_set(void) cpu_exec = exec386; else cpu_exec = execx86; + gdbstub_cpu_init(); } diff --git a/src/cpu/x86_ops_int.h b/src/cpu/x86_ops_int.h index f35e526a6..0074aec29 100644 --- a/src/cpu/x86_ops_int.h +++ b/src/cpu/x86_ops_int.h @@ -1,6 +1,10 @@ static int opINT3(uint32_t fetchdat) { int cycles_old = cycles; UN_USED(cycles_old); +#ifdef USE_GDBSTUB + if (gdbstub_int3()) + return 1; +#endif if ((cr0 & 1) && (cpu_state.eflags & VM_FLAG) && (IOPL != 3)) { x86gpf(NULL,0); diff --git a/src/cpu/x87_ops.h b/src/cpu/x87_ops.h index 2f8927060..c41074a2c 100644 --- a/src/cpu/x87_ops.h +++ b/src/cpu/x87_ops.h @@ -239,89 +239,25 @@ static __inline int64_t x87_fround(double b) return 0LL; } -#define BIAS80 16383 -#define BIAS64 1023 + +#include "x87_ops_conv.h" static __inline double x87_ld80() { - int64_t exp64; - int64_t blah; - int64_t exp64final; - int64_t mant64; - int64_t sign; - struct { - int16_t begin; - union - { - double d; - uint64_t ll; - } eind; - } test; - test.eind.ll = readmeml(easeg,cpu_state.eaaddr); - test.eind.ll |= (uint64_t)readmeml(easeg,cpu_state.eaaddr+4)<<32; - test.begin = readmemw(easeg,cpu_state.eaaddr+8); - - exp64 = (((test.begin&0x7fff) - BIAS80)); - blah = ((exp64 >0)?exp64:-exp64)&0x3ff; - exp64final = ((exp64 >0)?blah:-blah) +BIAS64; - - mant64 = (test.eind.ll >> 11) & (0xfffffffffffffll); - sign = (test.begin&0x8000)?1:0; - - if ((test.begin & 0x7fff) == 0x7fff) - exp64final = 0x7ff; - if ((test.begin & 0x7fff) == 0) - exp64final = 0; - if (test.eind.ll & 0x400) - mant64++; - - test.eind.ll = (sign <<63)|(exp64final << 52)| mant64; - - return test.eind.d; + x87_conv_t test; + test.eind.ll = readmeml(easeg,cpu_state.eaaddr); + test.eind.ll |= (uint64_t)readmeml(easeg,cpu_state.eaaddr+4)<<32; + test.begin = readmemw(easeg,cpu_state.eaaddr+8); + return x87_from80(&test); } static __inline void x87_st80(double d) { - int64_t sign80; - int64_t exp80; - int64_t exp80final; - int64_t mant80; - int64_t mant80final; - - struct { - int16_t begin; - union - { - double d; - uint64_t ll; - } eind; - } test; - - test.eind.d=d; - - sign80 = (test.eind.ll&(0x8000000000000000ll))?1:0; - exp80 = test.eind.ll&(0x7ff0000000000000ll); - exp80final = (exp80>>52); - mant80 = test.eind.ll&(0x000fffffffffffffll); - mant80final = (mant80 << 11); - - if (exp80final == 0x7ff) /*Infinity / Nan*/ - { - exp80final = 0x7fff; - mant80final |= (0x8000000000000000ll); - } - else if (d != 0){ /* Zero is a special case */ - /* Elvira wants the 8 and tcalc doesn't */ - mant80final |= (0x8000000000000000ll); - /* Ca-cyber doesn't like this when result is zero. */ - exp80final += (BIAS80 - BIAS64); - } - test.begin = (((int16_t)sign80)<<15)| (int16_t)exp80final; - test.eind.ll = mant80final; - - writememl(easeg,cpu_state.eaaddr,test.eind.ll & 0xffffffff); - writememl(easeg,cpu_state.eaaddr+4,test.eind.ll>>32); - writememw(easeg,cpu_state.eaaddr+8,test.begin); + x87_conv_t test; + x87_to80(d, &test); + writememl(easeg,cpu_state.eaaddr,test.eind.ll & 0xffffffff); + writememl(easeg,cpu_state.eaaddr+4,test.eind.ll>>32); + writememw(easeg,cpu_state.eaaddr+8,test.begin); } static __inline void x87_st_fsave(int reg) diff --git a/src/cpu/x87_ops_conv.h b/src/cpu/x87_ops_conv.h new file mode 100644 index 000000000..44304c1bc --- /dev/null +++ b/src/cpu/x87_ops_conv.h @@ -0,0 +1,68 @@ +#define BIAS80 16383 +#define BIAS64 1023 + +typedef struct { + int16_t begin; + union { + double d; + uint64_t ll; + } eind; +} x87_conv_t; + +static __inline double x87_from80(x87_conv_t *test) +{ + int64_t exp64; + int64_t blah; + int64_t exp64final; + int64_t mant64; + int64_t sign; + + exp64 = (((test->begin&0x7fff) - BIAS80)); + blah = ((exp64 >0)?exp64:-exp64)&0x3ff; + exp64final = ((exp64 >0)?blah:-blah) +BIAS64; + + mant64 = (test->eind.ll >> 11) & (0xfffffffffffffll); + sign = (test->begin&0x8000)?1:0; + + if ((test->begin & 0x7fff) == 0x7fff) + exp64final = 0x7ff; + if ((test->begin & 0x7fff) == 0) + exp64final = 0; + if (test->eind.ll & 0x400) + mant64++; + + test->eind.ll = (sign <<63)|(exp64final << 52)| mant64; + + return test->eind.d; +} + +static __inline void x87_to80(double d, x87_conv_t *test) +{ + int64_t sign80; + int64_t exp80; + int64_t exp80final; + int64_t mant80; + int64_t mant80final; + + test->eind.d=d; + + sign80 = (test->eind.ll&(0x8000000000000000ll))?1:0; + exp80 = test->eind.ll&(0x7ff0000000000000ll); + exp80final = (exp80>>52); + mant80 = test->eind.ll&(0x000fffffffffffffll); + mant80final = (mant80 << 11); + + if (exp80final == 0x7ff) /*Infinity / Nan*/ + { + exp80final = 0x7fff; + mant80final |= (0x8000000000000000ll); + } + else if (d != 0){ /* Zero is a special case */ + /* Elvira wants the 8 and tcalc doesn't */ + mant80final |= (0x8000000000000000ll); + /* Ca-cyber doesn't like this when result is zero. */ + exp80final += (BIAS80 - BIAS64); + } + test->begin = (((int16_t)sign80)<<15)| (int16_t)exp80final; + test->eind.ll = mant80final; +} diff --git a/src/gdbstub.c b/src/gdbstub.c index 9d5c32519..70c67d662 100644 --- a/src/gdbstub.c +++ b/src/gdbstub.c @@ -32,30 +32,65 @@ #include <86box/86box.h> #include "cpu.h" #include "x86seg.h" +#include "x87.h" +#include "x87_ops_conv.h" #include <86box/io.h> #include <86box/mem.h> #include <86box/plat.h> +#include <86box/gdbstub.h> -#define GDBSTUB_MAX_REG 38 +#define FAST_RESPONSE(s) strcpy(client->response, s); client->response_pos = sizeof(s); +#define FAST_RESPONSE_HEX(s) gdbstub_client_respond_hex(client, (uint8_t *) s, sizeof(s)); + enum { GDB_SIGINT = 2, GDB_SIGTRAP = 5 }; +enum { + GDB_REG_EAX = 0, GDB_REG_ECX, GDB_REG_EDX, GDB_REG_EBX, GDB_REG_ESP, GDB_REG_EBP, GDB_REG_ESI, GDB_REG_EDI, + GDB_REG_EIP, GDB_REG_EFLAGS, + GDB_REG_CS, GDB_REG_SS, GDB_REG_DS, GDB_REG_ES, GDB_REG_FS, GDB_REG_GS, GDB_REG_FS_BASE, GDB_REG_GS_BASE, + GDB_REG_CR0, GDB_REG_CR2, GDB_REG_CR3, GDB_REG_CR4, GDB_REG_EFER, + GDB_REG_ST0, GDB_REG_ST1, GDB_REG_ST2, GDB_REG_ST3, GDB_REG_ST4, GDB_REG_ST5, GDB_REG_ST6, GDB_REG_ST7, + GDB_REG_FCTRL, GDB_REG_FSTAT, GDB_REG_FTAG, GDB_REG_FISEG, GDB_REG_FIOFF, GDB_REG_FOSEG, GDB_REG_FOOFF, GDB_REG_FOP, + GDB_REG_MM0, GDB_REG_MM1, GDB_REG_MM2, GDB_REG_MM3, GDB_REG_MM4, GDB_REG_MM5, GDB_REG_MM6, GDB_REG_MM7, + GDB_REG_MAX +}; + +enum { + GDB_MODE_BASE10 = 0, + GDB_MODE_HEX, + GDB_MODE_OCT, + GDB_MODE_BIN +}; + typedef struct _gdbstub_client_ { int socket; struct sockaddr_in addr; char packet[16384], response[16384]; - int packet_pos, response_pos; + int has_packet, waiting_stop, packet_pos, response_pos; - event_t *response_event; + event_t *processed_event, *response_event; + + uint16_t last_io_base, last_io_len; struct _gdbstub_client_ *next; } gdbstub_client_t; +typedef struct _gdbstub_breakpoint_ { + uint32_t addr; + union { + uint8_t orig_val; + uint32_t end; + }; + + struct _gdbstub_breakpoint_ *next; +} gdbstub_breakpoint_t; + #define ENABLE_GDBSTUB_LOG 1 #ifdef ENABLE_GDBSTUB_LOG int gdbstub_do_log = ENABLE_GDBSTUB_LOG; @@ -77,7 +112,7 @@ gdbstub_log(const char *fmt, ...) static x86seg *segment_regs[] = {&cpu_state.seg_cs, &cpu_state.seg_ss, &cpu_state.seg_ds, &cpu_state.seg_es, &cpu_state.seg_fs, &cpu_state.seg_gs}; static uint32_t *cr_regs[] = {&cpu_state.CR0.l, &cr2, &cr3, &cr4}; -static void *fpu_regs[] = {&cpu_state.npxc, &cpu_state.npxs, NULL, x87_pc_seg, x87_pc_off, x87_op_seg, x87_op_off}; +static void *fpu_regs[] = {&cpu_state.npxc, &cpu_state.npxs, NULL, &x87_pc_seg, &x87_pc_off, &x87_op_seg, &x87_op_off}; static const char target_xml[] = /* based on qemu's i386-32bit.xml */ "" "" @@ -202,43 +237,55 @@ static const char target_xml[] = /* based on qemu's i386-32bit.xml */ "" "" "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" "" ""; #ifdef _WIN32 -static WSADATA wsa; +static WSADATA wsa; #endif -static int gdbstub_socket = -1, gdbstub_paused = 0; -static char stop_reason[2048]; -static gdbstub_client_t *first_client, *last_client; -static mutex_t *client_list_mutex; -static void (*cpu_exec_shadow)(int cycs); +static int gdbstub_socket = -1, stop_reason_len = 0, in_gdbstub = 0; +static uint32_t watch_addr; +static char stop_reason[2048]; -int gdbstub_singlestep = 0; +static gdbstub_client_t *first_client = NULL, *last_client = NULL; +static mutex_t *client_list_mutex; + +static void (*cpu_exec_shadow)(int cycs); +static gdbstub_breakpoint_t *first_swbreak = NULL, *first_hwbreak = NULL, + *first_rwatch = NULL, *first_wwatch = NULL, *first_awatch = NULL; + +int gdbstub_step = 0, gdbstub_next_asap = 0; +uint64_t gdbstub_watch_pages[(((uint32_t) -1) >> (MEM_GRANULARITY_BITS + 6)) + 1]; static void gdbstub_break() { - /* Initiate pause. */ - plat_pause(1); - - /* Force CPU execution to return as soon as possible. */ - gdbstub_singlestep = 1; -} - - -static void -gdbstub_singlestep_exec(int cycs) -{ - /* Call the original cpu_exec function. */ - cpu_exec_shadow(cycs); - - /* Swap the original function back in. */ - cpu_exec = cpu_exec_shadow; - - /* Break immediately. */ - gdbstub_break(); + /* Pause CPU execution as soon as possible. */ + if (gdbstub_step <= GDBSTUB_EXEC) + gdbstub_step = GDBSTUB_BREAK; } @@ -252,6 +299,7 @@ gdbstub_jump(uint32_t new_pc) loadseg((new_pc >> 4) & 0xf000, &cpu_state.seg_cs); cpu_state.pc = new_pc & 0xffff; } + flushmmucache(); } @@ -279,6 +327,95 @@ gdbstub_hex_encode(int c) } +static int +gdbstub_num_decode(char *p, int *dest, int mode) +{ + /* Stop if the pointer is invalid. */ + if (!p) + return 0; + + /* Read sign. */ + int sign = 1; + if ((p[0] == '+') || (p[0] == '-')) { + if (p[0] == '-') + sign = -1; + p++; + } + + /* Read type identifer if present (0x/0o/0b/0n) */ + if (p[0] == '0') { + switch (p[1]) { + case 'x': + mode = GDB_MODE_HEX; + break; + + case '0' ... '7': + p -= 1; + /* fall-through */ + + case 'o': + mode = GDB_MODE_OCT; + break; + + case 'b': + mode = GDB_MODE_BIN; + break; + + case 'n': + mode = GDB_MODE_BASE10; + break; + + default: + p -= 2; + break; + } + p += 2; + } + + /* Parse each character. */ + *dest = 0; + while (*p) { + switch (mode) { + case GDB_MODE_BASE10: + if ((*p >= '0') && (*p <= '9')) + *dest = ((*dest) * 10) + ((*p) - '0'); + else + return 0; + break; + + case GDB_MODE_HEX: + if (((*p >= '0') && (*p <= '9')) || ((*p >= 'A') && (*p <= 'F')) || ((*p >= 'a') && (*p <= 'f'))) + *dest = ((*dest) << 4) | gdbstub_hex_decode(*p); + else + return 0; + break; + + case GDB_MODE_OCT: + if ((*p >= '0') && (*p <= '7')) + *dest = ((*dest) << 3) | ((*p) - '0'); + else + return 0; + break; + + case GDB_MODE_BIN: + if ((*p == '0') || (*p == '1')) + *dest = ((*dest) << 1) | ((*p) - '0'); + else + return 0; + break; + } + p++; + } + + /* Apply sign. */ + if (sign < 0) + *dest = -(*dest); + + /* Return success. */ + return 1; +} + + static int gdbstub_client_read_word(gdbstub_client_t *client, int *dest) { @@ -324,49 +461,81 @@ gdbstub_client_write_reg(int index, uint8_t *buf) { int width = 4; switch (index) { - case 0 ... 7: /* [EAX:EDI] */ - cpu_state.regs[index].l = *((uint32_t *) buf); + case GDB_REG_EAX ... GDB_REG_EDI: + cpu_state.regs[index - GDB_REG_EAX].l = *((uint32_t *) buf); break; - case 8: /* EIP */ + case GDB_REG_EIP: gdbstub_jump(*((uint32_t *) buf)); break; - case 9: /* EFLAGS */ + case GDB_REG_EFLAGS: cpu_state.flags = *((uint16_t *) &buf[0]); cpu_state.eflags = *((uint16_t *) &buf[2]); break; - case 10 ... 15: /* [CS:GS] */ + case GDB_REG_CS ... GDB_REG_GS: width = 2; - loadseg(*((uint16_t *) buf), segment_regs[index - 10]); + loadseg(*((uint16_t *) buf), segment_regs[index - GDB_REG_CS]); + flushmmucache(); break; - case 16 ... 17: /* FSbase, GSbase */ + case GDB_REG_FS_BASE ... GDB_REG_GS_BASE: /* Do what qemu does and just load the base. */ - segment_regs[index - 12]->base = *((uint32_t *) buf); + segment_regs[(index - 16) + (GDB_REG_FS - GDB_REG_CS)]->base = *((uint32_t *) buf); break; - case 18 ... 21: /* [CR0:CR4] */ - *cr_regs[index - 18] = *((uint32_t *) buf); + case GDB_REG_CR0 ... GDB_REG_CR4: + *cr_regs[index - GDB_REG_CR0] = *((uint32_t *) buf); + flushmmucache(); break; - case 22: /* EFER */ + case GDB_REG_EFER: msr.amd_efer = *((uint64_t *) buf); break; - case 23 ... 30: /* ST(0:7) */ + case GDB_REG_ST0 ... GDB_REG_ST7: width = 10; + x87_conv_t conv = { + .eind = { .ll = *((uint64_t *) &buf[0]) }, + .begin = *((uint16_t *) &buf[8]) + }; + cpu_state.ST[(cpu_state.TOP + (index - GDB_REG_ST0)) & 7] = x87_from80(&conv); break; - /* FPU CONTROL REGS */ + case GDB_REG_FCTRL: + case GDB_REG_FISEG: + case GDB_REG_FOSEG: + width = 2; + *((uint16_t *) fpu_regs[index - GDB_REG_FCTRL]) = *((uint16_t *) buf); + if (index >= GDB_REG_FISEG) + flushmmucache(); + break; + + case GDB_REG_FSTAT: + case GDB_REG_FOP: + width = 2; + break; + + case GDB_REG_FTAG: + width = 2; + x87_settag(*((uint16_t *) buf)); + break; + + case GDB_REG_FIOFF: + case GDB_REG_FOOFF: + *((uint32_t *) fpu_regs[index - GDB_REG_FCTRL]) = *((uint32_t *) buf); + break; + + case GDB_REG_MM0 ... GDB_REG_MM7: + width = 8; + cpu_state.MM[index - GDB_REG_MM0].q = *((uint64_t *) &buf); + break; default: width = 0; } - flushmmucache(); /* incredibly cursed to be calling that from here */ - return width; } @@ -375,8 +544,7 @@ static void gdbstub_client_respond(gdbstub_client_t *client) { /* Calculate checksum. */ - uint8_t checksum = 0; - int i; + int checksum = 0, i; for (i = 0; i < client->response_pos; i++) checksum += client->response[i]; @@ -390,11 +558,23 @@ gdbstub_client_respond(gdbstub_client_t *client) #endif send(client->socket, "$", 1, 0); send(client->socket, client->response, client->response_pos, 0); - char response_cksum[3] = {'#', gdbstub_hex_encode(checksum >> 4), gdbstub_hex_encode(checksum & 0x0f)}; + char response_cksum[3] = {'#', gdbstub_hex_encode((checksum >> 4) & 0x0f), gdbstub_hex_encode(checksum & 0x0f)}; send(client->socket, response_cksum, sizeof(response_cksum), 0); } +static void +gdbstub_client_respond_partial(gdbstub_client_t *client) +{ + /* Send response. */ + gdbstub_client_respond(client); + + /* Wait for the response to be acknowledged. */ + thread_wait_event(client->response_event, -1); + thread_reset_event(client->response_event); +} + + static void gdbstub_client_respond_hex(gdbstub_client_t *client, uint8_t *buf, int size) { @@ -410,63 +590,70 @@ gdbstub_client_read_reg(int index, uint8_t *buf) { int width = 4; switch (index) { - case 0 ... 7: /* [EAX:EDI] */ + case GDB_REG_EAX ... GDB_REG_EDI: *((uint32_t *) buf) = cpu_state.regs[index].l; break; - case 8: /* EIP */ + case GDB_REG_EIP: *((uint32_t *) buf) = cs + cpu_state.pc; break; - case 9: /* EFLAGS */ + case GDB_REG_EFLAGS: *((uint16_t *) &buf[0]) = cpu_state.flags; *((uint16_t *) &buf[2]) = cpu_state.eflags; break; - case 10 ... 15: /* [CS:GS] */ - *((uint16_t *) buf) = segment_regs[index - 10]->seg; + case GDB_REG_CS ... GDB_REG_GS: + *((uint16_t *) buf) = segment_regs[index - GDB_REG_CS]->seg; break; - case 16 ... 17: /* FSbase, GSbase */ - *((uint32_t *) buf) = segment_regs[index - 12]->base; + case GDB_REG_FS_BASE ... GDB_REG_GS_BASE: + *((uint32_t *) buf) = segment_regs[(index - 16) + (GDB_REG_FS - GDB_REG_CS)]->base; break; - case 18 ... 21: /* [CR0:CR4] */ - *((uint32_t *) buf) = *cr_regs[index - 18]; + case GDB_REG_CR0 ... GDB_REG_CR4: + *((uint32_t *) buf) = *cr_regs[index - GDB_REG_CR0]; break; - case 22: /* EFER */ + case GDB_REG_EFER: *((uint64_t *) buf) = msr.amd_efer; break; - case 23 ... 30: /* ST(0:7) */ + case GDB_REG_ST0 ... GDB_REG_ST7: width = 10; - *((uint64_t *) &buf[0]) = 0; - *((uint16_t *) &buf[8]) = 0; + x87_conv_t conv; + x87_to80(cpu_state.ST[(cpu_state.TOP + (index - GDB_REG_ST0)) & 7], &conv); + *((uint64_t *) &buf[0]) = conv.eind.ll; + *((uint16_t *) &buf[8]) = conv.begin; break; - case 31 ... 32: /* [FCTRL:FSTAT] */ - case 34: /* [FISEG] */ - case 36: /* [FOSEG] */ + case GDB_REG_FCTRL ... GDB_REG_FSTAT: + case GDB_REG_FISEG: + case GDB_REG_FOSEG: width = 2; - *((uint16_t *) buf) = *((uint16_t *) fpu_regs[index - 31]); + *((uint16_t *) buf) = *((uint16_t *) fpu_regs[index - GDB_REG_FCTRL]); break; - case 33: /* FTAG */ + case GDB_REG_FTAG: width = 2; *((uint16_t *) buf) = x87_gettag(); break; - case 35: /* [FIOFF] */ - case 37: /* [FOOFF] */ - *((uint32_t *) buf) = *((uint32_t *) fpu_regs[index - 31]); + case GDB_REG_FIOFF: + case GDB_REG_FOOFF: + *((uint32_t *) buf) = *((uint32_t *) fpu_regs[index - GDB_REG_FCTRL]); break; - case 38: /* [FOP] */ + case GDB_REG_FOP: width = 2; *((uint16_t *) buf) = 0; /* we don't store the FPU opcode */ break; + case GDB_REG_MM0 ... GDB_REG_MM7: + width = 8; + cpu_state.MM[index - GDB_REG_MM0].q = *((uint64_t *) &buf); + break; + default: width = 0; } @@ -478,21 +665,23 @@ gdbstub_client_read_reg(int index, uint8_t *buf) static void gdbstub_client_packet(gdbstub_client_t *client) { +#ifdef GDBSTUB_CHECK_CHECKSUM /* msys2 gdb 11.1 transmits qSupported and H with invalid checksum... */ uint8_t rcv_checksum = 0, checksum = 0; +#endif int i, j = 0, k = 0, l; uint8_t buf[10] = {0}; char *p; /* Validate checksum. */ client->packet_pos -= 2; +#ifdef GDBSTUB_CHECK_CHECKSUM gdbstub_client_read_hex(client, &rcv_checksum, 1); - client->packet[client->packet_pos] = '\0'; - client->packet[client->packet_pos + 1] = '\0'; - client->packet[--client->packet_pos] = '\0'; +#endif + *((uint16_t *) &client->packet[--client->packet_pos]) = 0; +#ifdef GDBSTUB_CHECK_CHECKSUM for (i = 0; i < client->packet_pos; i++) checksum += client->packet[i]; -#if 0 /* msys2 gdb 11.1 transmits qSupported and H with invalid checksum... */ if (checksum != rcv_checksum) { /* Send negative acknowledgement. */ #ifdef ENABLE_GDBSTUB_LOG @@ -515,61 +704,54 @@ gdbstub_client_packet(gdbstub_client_t *client) #endif send(client->socket, "+", 1, 0); - /* Block other responses from being written while this one isn't acknowledged. */ - thread_wait_event(client->response_event, 0); - thread_reset_event(client->response_event); + /* Block other responses from being written while this one (if any is produced) isn't acknowledged. */ + if ((client->packet[0] != 'c') && (client->packet[0] != 's')) { + thread_wait_event(client->response_event, -1); + thread_reset_event(client->response_event); + } client->response_pos = 0; client->packet_pos = 1; /* Parse command. */ switch (client->packet[0]) { case '?': /* stop reason */ - /* Respond with a stop reply packet. */ - strcpy(client->response, stop_reason); - client->response_pos = strlen(client->response); + /* Respond with a stop reply packet if one is present. */ + if (stop_reason_len) { + strcpy(client->response, stop_reason); + client->response_pos = strlen(client->response); + } break; case 'c': /* continue */ case 's': /* step */ - /* No immediate response. */ - thread_set_event(client->response_event); + /* Flag that the client is waiting for a stop reason. */ + client->waiting_stop = 1; /* Jump to address if specified. */ - if (gdbstub_client_read_word(client, &j)) + if (client->packet[1] && gdbstub_client_read_word(client, &j)) gdbstub_jump(j); - /* Resume emulation. */ - if (client->packet[0] == 'c') { - gdbstub_singlestep = 0; - } else { - /* Replace cpu_exec with our own function, which breaks after a single step. */ - gdbstub_singlestep = 1; - if (cpu_exec != gdbstub_singlestep_exec) - cpu_exec_shadow = cpu_exec; - cpu_exec = gdbstub_singlestep_exec; - } - gdbstub_paused = 0; - plat_pause(0); + /* Resume CPU. */ + gdbstub_step = gdbstub_next_asap = (client->packet[0] == 's') ? GDBSTUB_SSTEP : GDBSTUB_EXEC; return; case 'D': /* detach */ /* Resume emulation. */ - gdbstub_paused = 0; - plat_pause(0); + gdbstub_step = GDBSTUB_EXEC; /* Respond positively. */ - client->response_pos = sprintf(client->response, "OK"); +ok: FAST_RESPONSE("OK"); break; case 'g': /* read all registers */ /* Output the values of all registers. */ - for (i = 0; i <= GDBSTUB_MAX_REG; i++) + for (i = 0; i < GDB_REG_MAX; i++) gdbstub_client_respond_hex(client, buf, gdbstub_client_read_reg(i, buf)); break; case 'G': /* write all registers */ /* Write the values of all registers. */ - for (i = 0; i <= GDBSTUB_MAX_REG; i++) { + for (i = 0; i < GDB_REG_MAX; i++) { if (!gdbstub_client_read_hex(client, buf, sizeof(buf))) break; client->packet_pos += gdbstub_client_write_reg(i, buf); @@ -579,16 +761,15 @@ gdbstub_client_packet(gdbstub_client_t *client) case 'H': /* set thread */ /* Read operation type and thread ID. */ if ((client->packet[1] == '\0') || (client->packet[2] == '\0')) { -e22: client->response_pos = sprintf(client->response, "E22"); +e22: FAST_RESPONSE("E22"); break; } /* Respond positively only on thread 1. */ if ((client->packet[2] == '1') && !client->packet[3]) - client->response_pos = sprintf(client->response, "OK"); + goto ok; else goto e22; - break; case 'm': /* read memory */ /* Read address and length. */ @@ -683,13 +864,12 @@ e22: client->response_pos = sprintf(client->response, "E22"); } /* Respond positively. */ - client->response_pos = sprintf(client->response, "OK"); - break; + goto ok; case 'p': /* read register */ /* Read register index. */ if (!gdbstub_client_read_word(client, &j)) { -e14: client->response_pos = sprintf(client->response, "E14"); +e14: FAST_RESPONSE("E14"); break; } @@ -714,15 +894,15 @@ e14: client->response_pos = sprintf(client->response, "E14"); goto e14; /* Respond positively. */ - client->response_pos = sprintf(client->response, "OK"); - break; + goto ok; case 'q': /* query */ /* Erase response, as we'll use it as a scratch buffer. */ memset(client->response, 0, sizeof(client->response)); /* Read the query type. */ - client->packet_pos += gdbstub_client_read_string(client, client->response, sizeof(client->response) - 1, ':') + 1; + client->packet_pos += gdbstub_client_read_string(client, client->response, sizeof(client->response) - 1, + (client->packet[1] == 'R') ? ',' : ':') + 1; /* Perform the query. */ if (!strcmp(client->response, "Supported")) { @@ -765,7 +945,7 @@ e14: client->response_pos = sprintf(client->response, "E14"); /* Stop if the file wasn't found. */ if (!p) { -e00: client->response_pos = sprintf(client->response, "E00"); +e00: FAST_RESPONSE("E00"); break; } @@ -796,7 +976,7 @@ e00: client->response_pos = sprintf(c /* Encode the data. */ while (k--) { i = *p++; - if ((i == '#') || (i == '$') || (i == '*') || (i == '}')) { + if ((i == '\0') || (i == '#') || (i == '$') || (i == '*') || (i == '}')) { client->response[client->response_pos++] = '}'; client->response[client->response_pos++] = i ^ 0x20; } else { @@ -806,33 +986,371 @@ e00: client->response_pos = sprintf(c break; } } + } else if (!strcmp(client->response, "Rcmd")) { + /* Read and decode command in-place. */ + i = gdbstub_client_read_hex(client, (uint8_t *) client->packet, strlen(client->packet) - client->packet_pos); + client->packet[i] = 0; + gdbstub_log("GDB Stub: Monitor command: %s\n", client->packet); + + /* Parse the command name. */ + char *strtok_save; + p = strtok_r(client->packet, " ", &strtok_save); + if (!p) + goto ok; + + /* Interpret the command. */ + if (p[0] == 'i') { + /* Read I/O operation width. */ + l = (p[1] == 'n') ? p[2] : p[1]; + + /* Read optional I/O port. */ + if (!(p = strtok_r(NULL, " ", &strtok_save)) || + !gdbstub_num_decode(p, &j, GDB_MODE_HEX) || + (j < 0) || (j >= 65536)) + j = client->last_io_base; + + /* Read optional length. */ + if (!(p = strtok_r(NULL, " ", &strtok_save)) || !gdbstub_num_decode(p, &k, GDB_MODE_BASE10)) + k = client->last_io_len; + + /* Clamp length. */ + if (k < 1) + k = 1; + if (k > (65536 - j)) + k = 65536 - j; + + /* Read ports. */ + i = 0; + while (i < k) { + if ((i % 16) == 0) { + if (i) { + client->packet[client->packet_pos++] = '\n'; + + /* Provide partial response with the last line. */ + client->response_pos = 0; + client->response[client->response_pos++] = 'O'; + gdbstub_client_respond_hex(client, (uint8_t *) client->packet, client->packet_pos); + gdbstub_client_respond_partial(client); + } + client->packet_pos = sprintf(client->packet, "%04X:", j + i); + } + /* Act according to I/O operation width. */ + switch (l) { + case 'd': + case 'l': + client->packet_pos += sprintf(&client->packet[client->packet_pos], " %08X", inl(j + i)); + i += 4; + break; + + case 'w': + client->packet_pos += sprintf(&client->packet[client->packet_pos], " %04X", inw(j + i)); + i += 2; + break; + + case 'b': + case '\0': + client->packet_pos += sprintf(&client->packet[client->packet_pos], " %02X", inb(j + i)); + i++; + break; + + default: + goto unknown; + } + } + client->packet[client->packet_pos++] = '\n'; + + /* Respond with the final line. */ + client->response_pos = 0; + gdbstub_client_respond_hex(client, (uint8_t *) &client->packet, client->packet_pos); + break; + } else { +unknown: FAST_RESPONSE_HEX("Unknown command\n"); + break; + } + + goto ok; + } + break; + + case 'z': /* remove break/watchpoint */ + case 'Z': /* insert break/watchpoint */ + gdbstub_breakpoint_t *breakpoint, *prev_breakpoint = NULL, **first_breakpoint; + + /* Parse breakpoint type. */ + switch (client->packet[1]) { + case '0': /* software breakpoint */ + first_breakpoint = &first_swbreak; + break; + + case '1': /* hardware breakpoint */ + first_breakpoint = &first_hwbreak; + break; + + case '2': /* write watchpoint */ + first_breakpoint = &first_wwatch; + break; + + case '3': /* read watchpoint */ + first_breakpoint = &first_rwatch; + break; + + case '4': /* access watchpoint */ + first_breakpoint = &first_awatch; + break; + + default: /* unknown type */ + client->packet[2] = '\0'; /* force address check to fail */ + break; } - /* No response by default. */ - client->response_pos = 0; - break; - } + /* Read address. */ + if (client->packet[2] != ',') + break; + client->packet_pos = 3; + if (!(i = gdbstub_client_read_word(client, &j))) + break; + client->packet_pos += i; + if (client->packet[client->packet_pos++] == ',') + gdbstub_client_read_word(client, &k); + else + k = 1; + /* Test writability of software breakpoint. */ + if (client->packet[1] == '0') { + buf[0] = readmembl(j); + writemembl(j, 0xcc); + buf[1] = readmembl(j); + writemembl(j, buf[0]); + if (buf[1] != 0xcc) + goto end; + } + + /* Find an existing breakpoint with this address. */ + breakpoint = *first_breakpoint; + while (breakpoint) { + if (breakpoint->addr == j) + break; + prev_breakpoint = breakpoint; + breakpoint = breakpoint->next; + } + + /* Check if the breakpoint is already present (when inserting) or not found (when removing). */ + if ((!!breakpoint) ^ (client->packet[0] == 'z')) + goto e22; + + /* Insert or remove the breakpoint. */ + if (client->packet[0] != 'z') { + /* Allocate a new breakpoint. */ + breakpoint = malloc(sizeof(gdbstub_breakpoint_t)); + breakpoint->addr = j; + breakpoint->end = j + k; + breakpoint->next = NULL; + + /* Add the new breakpoint to the list. */ + if (!(*first_breakpoint)) + *first_breakpoint = breakpoint; + else if (prev_breakpoint) + prev_breakpoint->next = breakpoint; + } else { + /* Remove breakpoint from the list. */ + if (breakpoint == *first_breakpoint) + *first_breakpoint = breakpoint->next; + else if (prev_breakpoint) + prev_breakpoint->next = breakpoint->next; + + /* De-allocate breakpoint. */ + free(breakpoint); + } + + /* Update the page watchpoint map if we're dealing with a watchpoint. */ + if (client->packet[1] >= '2') { + /* Clear this watchpoint's corresponding page map groups, + as everything is going to be recomputed soon anyway. */ + memset(&gdbstub_watch_pages[j >> (MEM_GRANULARITY_BITS + 6)], 0, + (((k - 1) >> (MEM_GRANULARITY_BITS + 6)) + 1) * sizeof(gdbstub_watch_pages[0])); + + /* Go through all watchpoint lists. */ + l = 0; + breakpoint = first_rwatch; + while (1) { + if (breakpoint) { + /* Flag this watchpoint's corresponding pages as having a watchpoint. */ + k = (breakpoint->end - 1) >> MEM_GRANULARITY_BITS; + for (i = breakpoint->addr >> MEM_GRANULARITY_BITS; i <= k; i++) + gdbstub_watch_pages[i >> 6] |= (1 << (i & 63)); + + breakpoint = breakpoint->next; + } else { + /* Jump from list to list as a shortcut. */ + if (l == 0) + breakpoint = first_wwatch; + else if (l == 1) + breakpoint = first_awatch; + else + break; + l++; + } + } + } + + /* Respond positively. */ + goto ok; + } +end: /* Send response. */ gdbstub_client_respond(client); } +static void +gdbstub_cpu_exec(int cycs) +{ + /* Flag that we're now in the debugger context to avoid triggering watchpoints. */ + in_gdbstub = 1; + + /* Handle CPU execution if it isn't paused. */ + if (gdbstub_step <= GDBSTUB_SSTEP) { + /* Swap in any software breakpoints. */ + gdbstub_breakpoint_t *swbreak = first_swbreak; + while (swbreak) { + /* Swap the INT 3 opcode into the address. */ + swbreak->orig_val = readmembl(swbreak->addr); + writemembl(swbreak->addr, 0xcc); + swbreak = swbreak->next; + } + + /* Call the original cpu_exec function outside the debugger context. */ + if ((gdbstub_step == GDBSTUB_SSTEP) && ((cycles + cycs) <= 0)) + cycs += -(cycles + cycs) + 1; + in_gdbstub = 0; + cpu_exec_shadow(cycs); + in_gdbstub = 1; + + /* Swap out any software breakpoints. */ + swbreak = first_swbreak; + while (swbreak) { + if (readmembl(swbreak->addr) == 0xcc) + writemembl(swbreak->addr, swbreak->orig_val); + swbreak = swbreak->next; + } + } + + /* Populate stop reason if we have stopped. */ + stop_reason_len = 0; + if (gdbstub_step > GDBSTUB_EXEC) { + /* Assemble stop reason manually (avoiding sprintf and friends) for performance. */ + stop_reason[stop_reason_len++] = 'T'; + stop_reason[stop_reason_len++] = '0'; + stop_reason[stop_reason_len++] = '0' + ((gdbstub_step == GDBSTUB_BREAK) ? GDB_SIGINT : GDB_SIGTRAP); + + /* Add extended break reason. */ + if (gdbstub_step >= GDBSTUB_BREAK_RWATCH) { + if (gdbstub_step != GDBSTUB_BREAK_WWATCH) + stop_reason[stop_reason_len++] = (gdbstub_step == GDBSTUB_BREAK_RWATCH) ? 'r' : 'a'; + stop_reason[stop_reason_len++] = 'w'; + stop_reason[stop_reason_len++] = 'a'; + stop_reason[stop_reason_len++] = 't'; + stop_reason[stop_reason_len++] = 'c'; + stop_reason[stop_reason_len++] = 'h'; + stop_reason[stop_reason_len++] = ':'; + stop_reason_len += sprintf(&stop_reason[stop_reason_len], "%X;", watch_addr); + } else if (gdbstub_step >= GDBSTUB_BREAK_SW) { + stop_reason[stop_reason_len++] = (gdbstub_step == GDBSTUB_BREAK_SW) ? 's' : 'h'; + stop_reason[stop_reason_len++] = 'w'; + stop_reason[stop_reason_len++] = 'b'; + stop_reason[stop_reason_len++] = 'r'; + stop_reason[stop_reason_len++] = 'e'; + stop_reason[stop_reason_len++] = 'a'; + stop_reason[stop_reason_len++] = 'k'; + stop_reason[stop_reason_len++] = ':'; + stop_reason[stop_reason_len++] = ';'; + } + + /* Add register dump. */ + uint8_t buf[10] = {0}; + int i, j, k; + for (i = 0; i < GDB_REG_MAX; i++) { + if (i >= 0x10) + stop_reason[stop_reason_len++] = gdbstub_hex_encode(i >> 4); + stop_reason[stop_reason_len++] = gdbstub_hex_encode(i & 0x0f); + stop_reason[stop_reason_len++] = ':'; + j = gdbstub_client_read_reg(i, buf); + for (k = 0; k < j; k++) { + stop_reason[stop_reason_len++] = gdbstub_hex_encode(buf[k] >> 4); + stop_reason[stop_reason_len++] = gdbstub_hex_encode(buf[k] & 0x0f); + } + stop_reason[stop_reason_len++] = ';'; + } + + /* Don't execute the CPU any further if single-stepping. */ + gdbstub_step = GDBSTUB_BREAK; + } + + /* Return the framerate to normal. */ + gdbstub_next_asap = 0; + + /* Process client packets. */ + thread_wait_mutex(client_list_mutex); + gdbstub_client_t *client = first_client; + while (client) { + /* Report stop reason if the client is waiting for one. */ + if (client->waiting_stop && stop_reason_len) { + client->waiting_stop = 0; + + /* Wait for any pending responses to be acknowledged. */ + if (!thread_wait_event(client->response_event, -1)) { + /* Block other responses from being written while this one isn't acknowledged. */ + thread_reset_event(client->response_event); + + /* Write stop reason response. */ + strcpy(client->response, stop_reason); + client->response_pos = stop_reason_len; + gdbstub_client_respond(client); + } else { + gdbstub_log("GDB Stub: Timed out waiting for client %s:%d\n", inet_ntoa(client->addr.sin_addr), client->addr.sin_port); + } + } + + if (client->has_packet) { + gdbstub_client_packet(client); + client->has_packet = client->packet_pos = 0; + thread_set_event(client->processed_event); + } + +#ifdef GDBSTUB_ALLOW_MULTI_CLIENTS + client = client->next; +#else + break; +#endif + } + thread_release_mutex(client_list_mutex); + + /* Flag that we're now out of the debugger context. */ + in_gdbstub = 0; +} + + static void gdbstub_client_thread(void *priv) { - gdbstub_client_t *client = (gdbstub_client_t *) priv, *other_client; + gdbstub_client_t *client = (gdbstub_client_t *) priv; uint8_t buf[256]; ssize_t bytes_read; int i; gdbstub_log("GDB Stub: New connection from %s:%d\n", inet_ntoa(client->addr.sin_addr), client->addr.sin_port); + /* Allow packets to be processed. */ + thread_set_event(client->processed_event); + /* Read data from client. */ while ((bytes_read = recv(client->socket, (char *) buf, sizeof(buf), 0)) > 0) { for (i = 0; i < bytes_read; i++) { switch (buf[i]) { case '$': /* packet start */ + /* Wait for any existing packets to be processed. */ + thread_wait_event(client->processed_event, -1); + client->packet_pos = 0; break; @@ -847,21 +1365,37 @@ gdbstub_client_thread(void *priv) break; case 0x03: /* break */ + /* Wait for any existing packets to be processed. */ + thread_wait_event(client->processed_event, -1); + /* Break immediately. */ gdbstub_log("GDB Stub: Break requested\n"); - gdbstub_paused = 0; gdbstub_break(); break; default: + /* Wait for any existing packets to be processed, just in case. */ + thread_wait_event(client->processed_event, -1); + if (client->packet_pos < (sizeof(client->packet) - 1)) { /* Append byte to the packet. */ client->packet[client->packet_pos++] = buf[i]; - /* Check if this is the end of a packet. */ + /* Check if we're at the end of a packet. */ if ((client->packet_pos >= 3) && (client->packet[client->packet_pos - 3] == '#')) { /* packet checksum start */ - gdbstub_client_packet(client); - client->packet_pos = 0; + /* Small hack to speed up IDA instruction trace mode. */ + if (*((uint32_t *) client->packet) == ('H' | ('c' << 8) | ('1' << 16) | ('#' << 24))) { + /* Send pre-computed response. */ + send(client->socket, "+$OK#9A", 7, 0); + + /* Skip processing. */ + continue; + } + + /* Flag that a packet should be processed. */ + client->packet[client->packet_pos] = '\0'; + thread_reset_event(client->processed_event); + gdbstub_next_asap = client->has_packet = 1; } } break; @@ -882,11 +1416,15 @@ gdbstub_client_thread(void *priv) /* Remove this client from the list. */ thread_wait_mutex(client_list_mutex); +#ifdef GDBSTUB_ALLOW_MULTI_CLIENTS if (client == first_client) { +#endif first_client = client->next; - if (first_client == NULL) + if (first_client == NULL) { last_client = NULL; - gdbstub_paused = 0; /* allow user to unpause when all clients are disconnected */ + gdbstub_step = GDBSTUB_EXEC; /* unpause CPU when all clients are disconnected */ + } +#ifdef GDBSTUB_ALLOW_MULTI_CLIENTS } else { other_client = first_client; while (other_client) { @@ -899,6 +1437,8 @@ gdbstub_client_thread(void *priv) other_client = other_client->next; } } +#endif + free(client); thread_release_mutex(client_list_mutex); } @@ -917,6 +1457,7 @@ gdbstub_server_thread(void *priv) /* Allocate client structure. */ client = malloc(sizeof(gdbstub_client_t)); memset(client, 0, sizeof(gdbstub_client_t)); + client->processed_event = thread_create_event(); client->response_event = thread_create_event(); /* Accept connection. */ @@ -927,15 +1468,19 @@ gdbstub_server_thread(void *priv) /* Add to client list. */ thread_wait_mutex(client_list_mutex); if (first_client) { +#ifdef GDBSTUB_ALLOW_MULTI_CLIENTS last_client->next = client; last_client = client; +#else + first_client->next = last_client = client; + close(first_client->socket); +#endif } else { first_client = last_client = client; } thread_release_mutex(client_list_mutex); - /* Pause emulation. */ - gdbstub_paused = 1; + /* Pause CPU execution. */ gdbstub_break(); /* Start client thread. */ @@ -943,40 +1488,128 @@ gdbstub_server_thread(void *priv) } /* Deallocate the redundant client structure. */ + thread_destroy_event(client->processed_event); thread_destroy_event(client->response_event); free(client); } void -gdbstub_pause(int *p) +gdbstub_cpu_init() { - if (!(*p) && gdbstub_paused) { - /* Don't allow the user to unpause if we're pausing. */ - gdbstub_log("GDB Stub: Blocked user unpause\n"); - *p = 1; - } else if (*p) { - sprintf(stop_reason, "S%02X", gdbstub_singlestep ? GDB_SIGTRAP : GDB_SIGINT); - if (!gdbstub_paused) { - /* Send interrupt packet to all clients. */ - gdbstub_log("GDB Stub: Pausing\n"); - gdbstub_paused = 1; - thread_wait_mutex(client_list_mutex); - gdbstub_client_t *client = first_client; - while (client) { - if (!thread_wait_event(client->response_event, -1)) { - /* Block other responses from being written while this one isn't acknowledged. */ - thread_reset_event(client->response_event); + /* Replace cpu_exec with our own function if the GDB stub is active. */ + if ((gdbstub_socket != -1) && (cpu_exec != gdbstub_cpu_exec)) { + cpu_exec_shadow = cpu_exec; + cpu_exec = gdbstub_cpu_exec; + } +} - /* Write stop reply packet. */ - client->response_pos = sprintf(client->response, "%s", stop_reason); - gdbstub_client_respond(client); - } else { - gdbstub_log("GDB Stub: Timed out waiting for client %08X\n", client); - } - client = client->next; + +int +gdbstub_instruction() +{ + /* Check hardware breakpoints if any are present. */ + gdbstub_breakpoint_t *breakpoint = first_hwbreak; + if (breakpoint) { + /* Calculate the current instruction's address. */ + uint32_t wanted_addr = cs + cpu_state.pc; + + /* Go through the list of software breakpoints. */ + do { + /* Check if the breakpoint coincides with this address. */ + if (breakpoint->addr == wanted_addr) { + gdbstub_log("GDB Stub: Hardware breakpoint at %08X\n", wanted_addr); + + /* Flag that we're in a hardware breakpoint. */ + gdbstub_step = GDBSTUB_BREAK_HW; + + /* Pause execution. */ + return 1; + } + + breakpoint = breakpoint->next; + } while (breakpoint); + } + + /* No breakpoint found, continue execution or stop if execution is paused. */ + return gdbstub_step - GDBSTUB_EXEC; +} + + +int +gdbstub_int3() +{ + /* Check software breakpoints if any are present. */ + gdbstub_breakpoint_t *breakpoint = first_swbreak; + if (breakpoint) { + /* Calculate the breakpoint instruction's address. */ + uint32_t new_pc = cpu_state.pc - 1; + if (cpu_state.op32) + new_pc &= 0xffff; + uint32_t wanted_addr = cs + new_pc; + + /* Go through the list of software breakpoints. */ + do { + /* Check if the breakpoint coincides with this address. */ + if (breakpoint->addr == wanted_addr) { + gdbstub_log("GDB Stub: Software breakpoint at %08X\n", wanted_addr); + + /* Move EIP back to where the break instruction was. */ + cpu_state.pc = new_pc; + + /* Flag that we're in a software breakpoint. */ + gdbstub_step = GDBSTUB_BREAK_SW; + + /* Abort INT 3 execution. */ + return 1; + } + + breakpoint = breakpoint->next; + } while (breakpoint); + } + + /* No breakpoint found, continue INT 3 execution as normal. */ + return 0; +} + + +void +gdbstub_mem_access(uint32_t *addrs, int access) +{ + /* Stop if we're in the debugger context. */ + if (in_gdbstub) + return; + + int width = access & (GDBSTUB_MEM_WRITE - 1), i; + + /* Go through the lists of watchpoints for this type of access. */ + gdbstub_breakpoint_t *watchpoint = (access & GDBSTUB_MEM_WRITE) ? first_wwatch : first_rwatch; + while (1) { + if (watchpoint) { + /* Check if any component of this address is within the breakpoint's range. */ + for (i = 0; i < width; i++) { + if ((addrs[i] >= watchpoint->addr) && (addrs[i] < watchpoint->end)) + break; + } + if (i < width) { + gdbstub_log("GDB Stub: %s watchpoint at %08X\n", (access & GDBSTUB_MEM_AWATCH) ? "Access" : ((access & GDBSTUB_MEM_WRITE) ? "Write" : "Read"), watch_addr); + + /* Flag that we're in a read/write watchpoint. */ + gdbstub_step = (access & GDBSTUB_MEM_AWATCH) ? GDBSTUB_BREAK_AWATCH : ((access & GDBSTUB_MEM_WRITE) ? GDBSTUB_BREAK_WWATCH : GDBSTUB_BREAK_RWATCH); + + /* Stop looking. */ + return; + } + + watchpoint = watchpoint->next; + } else { + /* Jump from list to list as a shortcut. */ + if (access & GDBSTUB_MEM_AWATCH) { + break; + } else { + watchpoint = first_awatch; + access |= GDBSTUB_MEM_AWATCH; } - thread_release_mutex(client_list_mutex); } } } @@ -1011,9 +1644,15 @@ gdbstub_init() /* Create client list mutex. */ client_list_mutex = thread_create_mutex(); + /* Clear watchpoint page map. */ + memset(gdbstub_watch_pages, 0, sizeof(gdbstub_watch_pages)); + /* Start server thread. */ pclog("GDB Stub: Listening on port %d\n", port); thread_create(gdbstub_server_thread, NULL); + + /* Start the CPU paused. */ + gdbstub_step = GDBSTUB_BREAK; } diff --git a/src/include/86box/gdbstub.h b/src/include/86box/gdbstub.h index d46c8fe6d..d562f6a6e 100644 --- a/src/include/86box/gdbstub.h +++ b/src/include/86box/gdbstub.h @@ -16,20 +16,60 @@ */ #ifndef EMU_GDBSTUB_H # define EMU_GDBSTUB_H +#include <86box/mem.h> + +#define GDBSTUB_MEM_READ 0 +#define GDBSTUB_MEM_WRITE 16 +#define GDBSTUB_MEM_AWATCH 32 + +enum { + GDBSTUB_EXEC = 0, + GDBSTUB_SSTEP, + GDBSTUB_BREAK, + GDBSTUB_BREAK_SW, + GDBSTUB_BREAK_HW, + GDBSTUB_BREAK_RWATCH, + GDBSTUB_BREAK_WWATCH, + GDBSTUB_BREAK_AWATCH +}; #ifdef USE_GDBSTUB -extern int gdbstub_singlestep; +#define GDBSTUB_MEM_ACCESS(addr, access, width) \ + uint32_t gdbstub_page = addr >> MEM_GRANULARITY_BITS; \ + if (gdbstub_watch_pages[gdbstub_page >> 6] & (1 << (gdbstub_page & 63)) || (addr == 0xb8dd4)) { \ + uint32_t gdbstub_addrs[width]; \ + for (int gdbstub_i = 0; gdbstub_i < width; gdbstub_i++) \ + gdbstub_addrs[gdbstub_i] = addr + gdbstub_i; \ + gdbstub_mem_access(gdbstub_addrs, access | width); \ + } -extern void gdbstub_pause(int *p); +#define GDBSTUB_MEM_ACCESS_FAST(addrs, access, width) \ + uint32_t gdbstub_page = addr >> MEM_GRANULARITY_BITS; \ + if (gdbstub_watch_pages[gdbstub_page >> 6] & (1 << (gdbstub_page & 63)) || (addr == 0xb8dd4)) \ + gdbstub_mem_access(addrs, access | width); + +extern int gdbstub_step, gdbstub_next_asap; +extern uint64_t gdbstub_watch_pages[(((uint32_t) -1) >> (MEM_GRANULARITY_BITS + 6)) + 1]; + +extern void gdbstub_cpu_init(); +extern int gdbstub_instruction(); +extern int gdbstub_int3(); +extern void gdbstub_mem_access(uint32_t *addrs, int access); extern void gdbstub_init(); extern void gdbstub_close(); #else -#define gdbstub_singlestep 0 +#define GDBSTUB_MEM_ACCESS(addr, access, width) +#define GDBSTUB_MEM_ACCESS_FAST(addrs, access, width) -#define gdbstub_pause(p) +#define gdbstub_step 0 +#define gdbstub_next_asap 0 + +#define gdbstub_cpu_init() +#define gdbstub_instruction() 0 +#define gdbstub_int3() 0 #define gdbstub_init() #define gdbstub_close() diff --git a/src/mem/mem.c b/src/mem/mem.c index fbc7d8f08..bdb0c4cdb 100644 --- a/src/mem/mem.c +++ b/src/mem/mem.c @@ -36,6 +36,7 @@ #include <86box/mem.h> #include <86box/plat.h> #include <86box/rom.h> +#include <86box/gdbstub.h> #ifdef USE_DYNAREC # include "codegen_public.h" #else @@ -783,6 +784,8 @@ readmembl(uint32_t addr) mem_mapping_t *map; uint64_t a; + GDBSTUB_MEM_ACCESS(addr, GDBSTUB_MEM_READ, 1); + addr64 = (uint64_t) addr; mem_logical_addr = addr; @@ -811,6 +814,8 @@ writemembl(uint32_t addr, uint8_t val) mem_mapping_t *map; uint64_t a; + GDBSTUB_MEM_ACCESS(addr, GDBSTUB_MEM_WRITE, 1); + addr64 = (uint64_t) addr; mem_logical_addr = addr; @@ -842,6 +847,8 @@ readmembl_no_mmut(uint32_t addr, uint32_t a64) { mem_mapping_t *map; + GDBSTUB_MEM_ACCESS(addr, GDBSTUB_MEM_READ, 1); + mem_logical_addr = addr; if (cr0 >> 31) { @@ -866,6 +873,8 @@ writemembl_no_mmut(uint32_t addr, uint32_t a64, uint8_t val) { mem_mapping_t *map; + GDBSTUB_MEM_ACCESS(addr, GDBSTUB_MEM_WRITE, 1); + mem_logical_addr = addr; if (page_lookup[addr >> 12] && page_lookup[addr >> 12]->write_b) { @@ -896,6 +905,7 @@ readmemwl(uint32_t addr) addr64a[0] = addr; addr64a[1] = addr + 1; + GDBSTUB_MEM_ACCESS_FAST(addr64a, GDBSTUB_MEM_READ, 2); mem_logical_addr = addr; @@ -957,6 +967,7 @@ writememwl(uint32_t addr, uint16_t val) addr64a[0] = addr; addr64a[1] = addr + 1; + GDBSTUB_MEM_ACCESS_FAST(addr64a, GDBSTUB_MEM_WRITE, 2); mem_logical_addr = addr; @@ -1029,6 +1040,8 @@ readmemwl_no_mmut(uint32_t addr, uint32_t *a64) { mem_mapping_t *map; + GDBSTUB_MEM_ACCESS(addr, GDBSTUB_MEM_READ, 2); + mem_logical_addr = addr; if (addr & 1) { @@ -1076,6 +1089,8 @@ writememwl_no_mmut(uint32_t addr, uint32_t *a64, uint16_t val) { mem_mapping_t *map; + GDBSTUB_MEM_ACCESS(addr, GDBSTUB_MEM_WRITE, 2); + mem_logical_addr = addr; if (addr & 1) { @@ -1135,6 +1150,7 @@ readmemll(uint32_t addr) for (i = 0; i < 4; i++) addr64a[i] = (uint64_t) (addr + i); + GDBSTUB_MEM_ACCESS_FAST(addr64a, GDBSTUB_MEM_READ, 4); mem_logical_addr = addr; @@ -1214,6 +1230,7 @@ writememll(uint32_t addr, uint32_t val) for (i = 0; i < 4; i++) addr64a[i] = (uint64_t) (addr + i); + GDBSTUB_MEM_ACCESS_FAST(addr64a, GDBSTUB_MEM_WRITE, 4); mem_logical_addr = addr; @@ -1305,6 +1322,8 @@ readmemll_no_mmut(uint32_t addr, uint32_t *a64) #ifndef NO_MMUT mem_mapping_t *map; + GDBSTUB_MEM_ACCESS(addr, GDBSTUB_MEM_READ, 4); + mem_logical_addr = addr; if (addr & 3) { @@ -1361,6 +1380,8 @@ writememll_no_mmut(uint32_t addr, uint32_t *a64, uint32_t val) #ifndef NO_MMUT mem_mapping_t *map; + GDBSTUB_MEM_ACCESS(addr, GDBSTUB_MEM_WRITE, 4); + mem_logical_addr = addr; if (addr & 3) { @@ -1429,6 +1450,7 @@ readmemql(uint32_t addr) for (i = 0; i < 8; i++) addr64a[i] = (uint64_t) (addr + i); + GDBSTUB_MEM_ACCESS_FAST(addr64a, GDBSTUB_MEM_READ, 8); mem_logical_addr = addr; @@ -1496,6 +1518,7 @@ writememql(uint32_t addr, uint64_t val) for (i = 0; i < 8; i++) addr64a[i] = (uint64_t) (addr + i); + GDBSTUB_MEM_ACCESS_FAST(addr64a, GDBSTUB_MEM_WRITE, 8); mem_logical_addr = addr; diff --git a/src/qt/qt_main.cpp b/src/qt/qt_main.cpp index 7ec4930d2..47dc52356 100644 --- a/src/qt/qt_main.cpp +++ b/src/qt/qt_main.cpp @@ -54,6 +54,7 @@ extern "C" #include <86box/ui.h> #include <86box/video.h> #include <86box/discord.h> +#include <86box/gdbstub.h> } #include @@ -95,6 +96,11 @@ main_thread_fn() while (!is_quit && cpu_thread_run) { /* See if it is time to run a frame of code. */ new_time = elapsed_timer.elapsed(); +#ifdef USE_GDBSTUB + if (gdbstub_next_asap && (drawits <= 0)) + drawits = 10; + else +#endif drawits += (new_time - old_time); old_time = new_time; if (drawits > 0 && !dopause) { diff --git a/src/qt/qt_platform.cpp b/src/qt/qt_platform.cpp index b4bd38e83..4ec31b842 100644 --- a/src/qt/qt_platform.cpp +++ b/src/qt/qt_platform.cpp @@ -92,7 +92,6 @@ extern "C" { #include "../cpu/cpu.h" #include <86box/plat.h> -#include <86box/gdbstub.h> volatile int cpu_thread_run = 1; int mouse_capture = 0; @@ -315,8 +314,6 @@ plat_pause(int p) static wchar_t oldtitle[512]; wchar_t title[512], paused_msg[64]; - gdbstub_pause(&p); - if (p == dopause) { #ifdef Q_OS_WINDOWS if (source_hwnd) diff --git a/src/unix/unix.c b/src/unix/unix.c index ead580372..ab3f5b349 100644 --- a/src/unix/unix.c +++ b/src/unix/unix.c @@ -523,6 +523,11 @@ main_thread(void *param) while (!is_quit && cpu_thread_run) { /* See if it is time to run a frame of code. */ new_time = SDL_GetTicks(); +#ifdef USE_GDBSTUB + if (gdbstub_next_asap && (drawits <= 0)) + drawits = 10; + else +#endif drawits += (new_time - old_time); old_time = new_time; if (drawits > 0 && !dopause) { @@ -720,8 +725,6 @@ plat_pause(int p) static wchar_t oldtitle[512]; wchar_t title[512]; - gdbstub_pause(&p); - if ((p == 0) && (time_sync & TIME_SYNC_ENABLED)) nvr_time_sync(); diff --git a/src/win/win.c b/src/win/win.c index 1490a1ce1..f4b906d7d 100644 --- a/src/win/win.c +++ b/src/win/win.c @@ -55,6 +55,7 @@ #include <86box/win_opengl.h> #include <86box/win.h> #include <86box/version.h> +#include <86box/gdbstub.h> #ifdef MTR_ENABLED #include #endif @@ -525,6 +526,11 @@ main_thread(void *param) while (!is_quit && cpu_thread_run) { /* See if it is time to run a frame of code. */ new_time = GetTickCount(); +#ifdef USE_GDBSTUB + if (gdbstub_next_asap && (drawits <= 0)) + drawits = 10; + else +#endif drawits += (new_time - old_time); old_time = new_time; if (drawits > 0 && !dopause) { diff --git a/src/win/win_ui.c b/src/win/win_ui.c index b99ca6414..1a9ad961a 100644 --- a/src/win/win_ui.c +++ b/src/win/win_ui.c @@ -44,7 +44,6 @@ #include <86box/win.h> #include <86box/version.h> #include <86box/discord.h> -#include <86box/gdbstub.h> #ifdef MTR_ENABLED #include @@ -1497,8 +1496,6 @@ plat_pause(int p) static wchar_t oldtitle[512]; wchar_t title[512]; - gdbstub_pause(&p); - /* If un-pausing, as the renderer if that's OK. */ if (p == 0) p = get_vidpause(); From 66e565000f81df09f73e7aea248695346ca6af3f Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Wed, 16 Mar 2022 00:46:25 -0300 Subject: [PATCH 48/62] Change struct initializers on new devices --- src/sound/snd_cmi8x38.c | 100 ++++++++++++++++++++++------------------ src/sound/snd_sb.c | 38 ++++++++------- 2 files changed, 77 insertions(+), 61 deletions(-) diff --git a/src/sound/snd_cmi8x38.c b/src/sound/snd_cmi8x38.c index 3e1bb6997..bcfb7ba21 100644 --- a/src/sound/snd_cmi8x38.c +++ b/src/sound/snd_cmi8x38.c @@ -1431,61 +1431,71 @@ static const device_config_t cmi8738_config[] = { }; const device_t cmi8338_device = { - "C-Media CMI8338", - "cmi8338", - DEVICE_PCI, - CMEDIA_CMI8338, - cmi8x38_init, cmi8x38_close, cmi8x38_reset, - { NULL }, - cmi8x38_speed_changed, - NULL, - cmi8x38_config + .name = "C-Media CMI8338", + .internal_name = "cmi8338", + .flags = DEVICE_PCI, + .local = CMEDIA_CMI8338, + .init = cmi8x38_init, + .close = cmi8x38_close, + .reset = cmi8x38_reset, + { .available = NULL }, + .speed_changed = cmi8x38_speed_changed, + .force_redraw = NULL, + .config = cmi8x38_config }; const device_t cmi8338_onboard_device = { - "C-Media CMI8338 (On-Board)", - "cmi8338_onboard", - DEVICE_PCI, - CMEDIA_CMI8338 | (1 << 13), - cmi8x38_init, cmi8x38_close, cmi8x38_reset, - { NULL }, - cmi8x38_speed_changed, - NULL, - cmi8x38_config + .name = "C-Media CMI8338 (On-Board)", + .internal_name = "cmi8338_onboard", + .flags = DEVICE_PCI, + .local = CMEDIA_CMI8338 | (1 << 13), + .init = cmi8x38_init, + .close = cmi8x38_close, + .reset = cmi8x38_reset, + { .available = NULL }, + .speed_changed = cmi8x38_speed_changed, + .force_redraw = NULL, + .config = cmi8x38_config }; const device_t cmi8738_device = { - "C-Media CMI8738", - "cmi8738", - DEVICE_PCI, - CMEDIA_CMI8738_6CH, - cmi8x38_init, cmi8x38_close, cmi8x38_reset, - { NULL }, - cmi8x38_speed_changed, - NULL, - cmi8738_config + .name = "C-Media CMI8738", + .internal_name = "cmi8738", + .flags = DEVICE_PCI, + .local = CMEDIA_CMI8738_6CH, + .init = cmi8x38_init, + .close = cmi8x38_close, + .reset = cmi8x38_reset, + { .available = NULL }, + .speed_changed = cmi8x38_speed_changed, + .force_redraw = NULL, + .config = cmi8738_config }; const device_t cmi8738_onboard_device = { - "C-Media CMI8738 (On-Board)", - "cmi8738_onboard", - DEVICE_PCI, - CMEDIA_CMI8738_4CH | (1 << 13), - cmi8x38_init, cmi8x38_close, cmi8x38_reset, - { NULL }, - cmi8x38_speed_changed, - NULL, - cmi8x38_config + .name = "C-Media CMI8738 (On-Board)", + .internal_name = "cmi8738_onboard", + .flags = DEVICE_PCI, + .local = CMEDIA_CMI8738_4CH | (1 << 13), + .init = cmi8x38_init, + .close = cmi8x38_close, + .reset = cmi8x38_reset, + { .available = NULL }, + .speed_changed = cmi8x38_speed_changed, + .force_redraw = NULL, + .config = cmi8x38_config }; const device_t cmi8738_6ch_onboard_device = { - "C-Media CMI8738-6CH (On-Board)", - "cmi8738_6ch_onboard", - DEVICE_PCI, - CMEDIA_CMI8738_6CH | (1 << 13), - cmi8x38_init, cmi8x38_close, cmi8x38_reset, - { NULL }, - cmi8x38_speed_changed, - NULL, - cmi8x38_config + .name = "C-Media CMI8738-6CH (On-Board)", + .internal_name = "cmi8738_6ch_onboard", + .flags = DEVICE_PCI, + .local = CMEDIA_CMI8738_6CH | (1 << 13), + .init = cmi8x38_init, + .close = cmi8x38_close, + .reset = cmi8x38_reset, + { .available = NULL }, + .speed_changed = cmi8x38_speed_changed, + .force_redraw = NULL, + .config = cmi8x38_config }; diff --git a/src/sound/snd_sb.c b/src/sound/snd_sb.c index 2029bdd89..5f5aa5899 100644 --- a/src/sound/snd_sb.c +++ b/src/sound/snd_sb.c @@ -2566,26 +2566,32 @@ const device_t sb_16_pnp_device = { const device_t sb_16_compat_device = { - "Sound Blaster 16 (Compatibility)", - "sb16_compat", - DEVICE_ISA | DEVICE_AT, - 1, - sb_16_compat_init, sb_close, NULL, { NULL }, - sb_speed_changed, - NULL, - NULL + .name = "Sound Blaster 16 (Compatibility)", + .internal_name = "sb16_compat", + .flags = DEVICE_ISA | DEVICE_AT, + .local = 1, + .init = sb_16_compat_init, + .close = sb_close, + .reset = NULL, + { .available = NULL }, + .speed_changed = sb_speed_changed, + .force_redraw = NULL, + .config = NULL }; const device_t sb_16_compat_nompu_device = { - "Sound Blaster 16 (Compatibility - MPU-401 Off)", - "sb16_compat", - DEVICE_ISA | DEVICE_AT, - 0, - sb_16_compat_init, sb_close, NULL, { NULL }, - sb_speed_changed, - NULL, - NULL + .name = "Sound Blaster 16 (Compatibility - MPU-401 Off)", + .internal_name = "sb16_compat", + .flags = DEVICE_ISA | DEVICE_AT, + .local = 0, + .init = sb_16_compat_init, + .close = sb_close, + .reset = NULL, + { .available = NULL }, + .speed_changed =sb_speed_changed, + .force_redraw = NULL, + .config = NULL }; const device_t sb_32_pnp_device = { From f2ec3c62012e19bc41339a42ab11cc82bbc369b9 Mon Sep 17 00:00:00 2001 From: ts-korhonen Date: Wed, 16 Mar 2022 18:19:44 +0200 Subject: [PATCH 49/62] qt: uncapture mouse when deactivated or dialog. Uncapture mouse when application is deactivated or window is blocked (dialog shown). Re-capture after full screen dialog. --- src/qt/qt_mainwindow.cpp | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/qt/qt_mainwindow.cpp b/src/qt/qt_mainwindow.cpp index b3d85ada4..1d558c38b 100644 --- a/src/qt/qt_mainwindow.cpp +++ b/src/qt/qt_mainwindow.cpp @@ -214,6 +214,11 @@ MainWindow::MainWindow(QWidget *parent) : } }); + connect(qApp, &QGuiApplication::applicationStateChanged, [this](Qt::ApplicationState state) { + if (mouse_capture && state != Qt::ApplicationState::ApplicationActive) + emit setMouseCapture(false); + }); + connect(this, &MainWindow::resizeContents, this, [this](int w, int h) { if (!QApplication::platformName().contains("eglfs") && vid_resize == 0) { w = qRound(w / (!dpi_scale ? util::screenOfWidget(this)->devicePixelRatio() : 1.)); @@ -1265,6 +1270,8 @@ void MainWindow::on_actionFullscreen_triggered() { } else { if (video_fullscreen_first) { + bool wasCaptured = mouse_capture == 1; + QMessageBox questionbox(QMessageBox::Icon::Information, tr("Entering fullscreen mode"), tr("Press Ctrl+Alt+PgDn to return to windowed mode."), QMessageBox::Ok, this); QCheckBox *chkbox = new QCheckBox(tr("Don't show this message again")); questionbox.setCheckBox(chkbox); @@ -1275,6 +1282,10 @@ void MainWindow::on_actionFullscreen_triggered() { }); questionbox.exec(); config_save(); + + /* (re-capture mouse after dialog. */ + if (wasCaptured) + emit setMouseCapture(true); } video_fullscreen = 1; setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); @@ -1305,22 +1316,27 @@ bool MainWindow::eventFilter(QObject* receiver, QEvent* event) if (this->keyboardGrabber() == this) { if (event->type() == QEvent::KeyPress) { event->accept(); - this->keyPressEvent((QKeyEvent*)event); + this->keyPressEvent((QKeyEvent *) event); return true; } if (event->type() == QEvent::KeyRelease) { event->accept(); - this->keyReleaseEvent((QKeyEvent*)event); + this->keyReleaseEvent((QKeyEvent *) event); return true; } } - if (receiver == this) - { + if (receiver == this) { static auto curdopause = dopause; - if (event->type() == QEvent::WindowBlocked) { curdopause = dopause; plat_pause(1); } - else if (event->type() == QEvent::WindowUnblocked) { plat_pause(curdopause); } + if (event->type() == QEvent::WindowBlocked) { + curdopause = dopause; + plat_pause(1); + emit setMouseCapture(false); + } else if (event->type() == QEvent::WindowUnblocked) { + plat_pause(curdopause); + } } + return QMainWindow::eventFilter(receiver, event); } From fd6646f21b70d0268596dd014963051e61ae28ad Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Wed, 16 Mar 2022 14:12:45 -0300 Subject: [PATCH 50/62] Run clang-format on all my new code --- src/gdbstub.c | 1798 ++++++++++++++++++----------------- src/include/86box/gdbstub.h | 52 +- src/sound/snd_ad1848.c | 18 +- src/sound/snd_cmi8x38.c | 1459 ++++++++++++++-------------- src/sound/snd_cs423x.c | 80 +- src/sound/snd_sb.c | 323 ++++--- src/sound/snd_sb_dsp.c | 4 +- 7 files changed, 1877 insertions(+), 1857 deletions(-) diff --git a/src/gdbstub.c b/src/gdbstub.c index 70c67d662..d6b410a95 100644 --- a/src/gdbstub.c +++ b/src/gdbstub.c @@ -16,17 +16,17 @@ */ #include #include -#include #include -#include +#include #include +#include #include #ifdef _WIN32 -# include -# include +# include +# include #else -# include -# include +# include +# include #endif #define HAVE_STDARG_H #include <86box/86box.h> @@ -39,24 +39,64 @@ #include <86box/plat.h> #include <86box/gdbstub.h> - -#define FAST_RESPONSE(s) strcpy(client->response, s); client->response_pos = sizeof(s); +#define FAST_RESPONSE(s) \ + strcpy(client->response, s); \ + client->response_pos = sizeof(s); #define FAST_RESPONSE_HEX(s) gdbstub_client_respond_hex(client, (uint8_t *) s, sizeof(s)); - enum { - GDB_SIGINT = 2, + GDB_SIGINT = 2, GDB_SIGTRAP = 5 }; enum { - GDB_REG_EAX = 0, GDB_REG_ECX, GDB_REG_EDX, GDB_REG_EBX, GDB_REG_ESP, GDB_REG_EBP, GDB_REG_ESI, GDB_REG_EDI, - GDB_REG_EIP, GDB_REG_EFLAGS, - GDB_REG_CS, GDB_REG_SS, GDB_REG_DS, GDB_REG_ES, GDB_REG_FS, GDB_REG_GS, GDB_REG_FS_BASE, GDB_REG_GS_BASE, - GDB_REG_CR0, GDB_REG_CR2, GDB_REG_CR3, GDB_REG_CR4, GDB_REG_EFER, - GDB_REG_ST0, GDB_REG_ST1, GDB_REG_ST2, GDB_REG_ST3, GDB_REG_ST4, GDB_REG_ST5, GDB_REG_ST6, GDB_REG_ST7, - GDB_REG_FCTRL, GDB_REG_FSTAT, GDB_REG_FTAG, GDB_REG_FISEG, GDB_REG_FIOFF, GDB_REG_FOSEG, GDB_REG_FOOFF, GDB_REG_FOP, - GDB_REG_MM0, GDB_REG_MM1, GDB_REG_MM2, GDB_REG_MM3, GDB_REG_MM4, GDB_REG_MM5, GDB_REG_MM6, GDB_REG_MM7, + GDB_REG_EAX = 0, + GDB_REG_ECX, + GDB_REG_EDX, + GDB_REG_EBX, + GDB_REG_ESP, + GDB_REG_EBP, + GDB_REG_ESI, + GDB_REG_EDI, + GDB_REG_EIP, + GDB_REG_EFLAGS, + GDB_REG_CS, + GDB_REG_SS, + GDB_REG_DS, + GDB_REG_ES, + GDB_REG_FS, + GDB_REG_GS, + GDB_REG_FS_BASE, + GDB_REG_GS_BASE, + GDB_REG_CR0, + GDB_REG_CR2, + GDB_REG_CR3, + GDB_REG_CR4, + GDB_REG_EFER, + GDB_REG_ST0, + GDB_REG_ST1, + GDB_REG_ST2, + GDB_REG_ST3, + GDB_REG_ST4, + GDB_REG_ST5, + GDB_REG_ST6, + GDB_REG_ST7, + GDB_REG_FCTRL, + GDB_REG_FSTAT, + GDB_REG_FTAG, + GDB_REG_FISEG, + GDB_REG_FIOFF, + GDB_REG_FOSEG, + GDB_REG_FOOFF, + GDB_REG_FOP, + GDB_REG_MM0, + GDB_REG_MM1, + GDB_REG_MM2, + GDB_REG_MM3, + GDB_REG_MM4, + GDB_REG_MM5, + GDB_REG_MM6, + GDB_REG_MM7, GDB_REG_MAX }; @@ -107,163 +147,165 @@ gdbstub_log(const char *fmt, ...) } } #else -#define gdbstub_log(fmt, ...) +# define gdbstub_log(fmt, ...) #endif -static x86seg *segment_regs[] = {&cpu_state.seg_cs, &cpu_state.seg_ss, &cpu_state.seg_ds, &cpu_state.seg_es, &cpu_state.seg_fs, &cpu_state.seg_gs}; -static uint32_t *cr_regs[] = {&cpu_state.CR0.l, &cr2, &cr3, &cr4}; -static void *fpu_regs[] = {&cpu_state.npxc, &cpu_state.npxs, NULL, &x87_pc_seg, &x87_pc_off, &x87_op_seg, &x87_op_off}; -static const char target_xml[] = /* based on qemu's i386-32bit.xml */ +static x86seg *segment_regs[] = { &cpu_state.seg_cs, &cpu_state.seg_ss, &cpu_state.seg_ds, &cpu_state.seg_es, &cpu_state.seg_fs, &cpu_state.seg_gs }; +static uint32_t *cr_regs[] = { &cpu_state.CR0.l, &cr2, &cr3, &cr4 }; +static void *fpu_regs[] = { &cpu_state.npxc, &cpu_state.npxs, NULL, &x87_pc_seg, &x87_pc_off, &x87_op_seg, &x87_op_off }; +static const char target_xml[] = /* based on qemu's i386-32bit.xml */ + // clang-format off "" "" "" "i8086" "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" "" ""; +// clang-format on #ifdef _WIN32 -static WSADATA wsa; +static WSADATA wsa; #endif static int gdbstub_socket = -1, stop_reason_len = 0, in_gdbstub = 0; static uint32_t watch_addr; @@ -272,14 +314,13 @@ static char stop_reason[2048]; static gdbstub_client_t *first_client = NULL, *last_client = NULL; static mutex_t *client_list_mutex; -static void (*cpu_exec_shadow)(int cycs); +static void (*cpu_exec_shadow)(int cycs); static gdbstub_breakpoint_t *first_swbreak = NULL, *first_hwbreak = NULL, *first_rwatch = NULL, *first_wwatch = NULL, *first_awatch = NULL; -int gdbstub_step = 0, gdbstub_next_asap = 0; +int gdbstub_step = 0, gdbstub_next_asap = 0; uint64_t gdbstub_watch_pages[(((uint32_t) -1) >> (MEM_GRANULARITY_BITS + 6)) + 1]; - static void gdbstub_break() { @@ -288,7 +329,6 @@ gdbstub_break() gdbstub_step = GDBSTUB_BREAK; } - static void gdbstub_jump(uint32_t new_pc) { @@ -302,7 +342,6 @@ gdbstub_jump(uint32_t new_pc) flushmmucache(); } - static inline int gdbstub_hex_decode(int c) { @@ -316,7 +355,6 @@ gdbstub_hex_decode(int c) return 0; } - static inline int gdbstub_hex_encode(int c) { @@ -326,7 +364,6 @@ gdbstub_hex_encode(int c) return c - 10 + 'A'; } - static int gdbstub_num_decode(char *p, int *dest, int mode) { @@ -338,36 +375,36 @@ gdbstub_num_decode(char *p, int *dest, int mode) int sign = 1; if ((p[0] == '+') || (p[0] == '-')) { if (p[0] == '-') - sign = -1; + sign = -1; p++; } /* Read type identifer if present (0x/0o/0b/0n) */ if (p[0] == '0') { switch (p[1]) { - case 'x': - mode = GDB_MODE_HEX; - break; + case 'x': + mode = GDB_MODE_HEX; + break; - case '0' ... '7': - p -= 1; - /* fall-through */ + case '0' ... '7': + p -= 1; + /* fall-through */ - case 'o': - mode = GDB_MODE_OCT; - break; + case 'o': + mode = GDB_MODE_OCT; + break; - case 'b': - mode = GDB_MODE_BIN; - break; + case 'b': + mode = GDB_MODE_BIN; + break; - case 'n': - mode = GDB_MODE_BASE10; - break; + case 'n': + mode = GDB_MODE_BASE10; + break; - default: - p -= 2; - break; + default: + p -= 2; + break; } p += 2; } @@ -376,33 +413,33 @@ gdbstub_num_decode(char *p, int *dest, int mode) *dest = 0; while (*p) { switch (mode) { - case GDB_MODE_BASE10: - if ((*p >= '0') && (*p <= '9')) - *dest = ((*dest) * 10) + ((*p) - '0'); - else - return 0; - break; + case GDB_MODE_BASE10: + if ((*p >= '0') && (*p <= '9')) + *dest = ((*dest) * 10) + ((*p) - '0'); + else + return 0; + break; - case GDB_MODE_HEX: - if (((*p >= '0') && (*p <= '9')) || ((*p >= 'A') && (*p <= 'F')) || ((*p >= 'a') && (*p <= 'f'))) - *dest = ((*dest) << 4) | gdbstub_hex_decode(*p); - else - return 0; - break; + case GDB_MODE_HEX: + if (((*p >= '0') && (*p <= '9')) || ((*p >= 'A') && (*p <= 'F')) || ((*p >= 'a') && (*p <= 'f'))) + *dest = ((*dest) << 4) | gdbstub_hex_decode(*p); + else + return 0; + break; - case GDB_MODE_OCT: - if ((*p >= '0') && (*p <= '7')) - *dest = ((*dest) << 3) | ((*p) - '0'); - else - return 0; - break; + case GDB_MODE_OCT: + if ((*p >= '0') && (*p <= '7')) + *dest = ((*dest) << 3) | ((*p) - '0'); + else + return 0; + break; - case GDB_MODE_BIN: - if ((*p == '0') || (*p == '1')) - *dest = ((*dest) << 1) | ((*p) - '0'); - else - return 0; - break; + case GDB_MODE_BIN: + if ((*p == '0') || (*p == '1')) + *dest = ((*dest) << 1) | ((*p) - '0'); + else + return 0; + break; } p++; } @@ -415,7 +452,6 @@ gdbstub_num_decode(char *p, int *dest, int mode) return 1; } - static int gdbstub_client_read_word(gdbstub_client_t *client, int *dest) { @@ -425,29 +461,27 @@ gdbstub_client_read_word(gdbstub_client_t *client, int *dest) return p - q; } - static int gdbstub_client_read_hex(gdbstub_client_t *client, uint8_t *buf, int size) { int pp = client->packet_pos; while (size-- && (pp < (sizeof(client->packet) - 2))) { - *buf = gdbstub_hex_decode(client->packet[pp++]) << 4; + *buf = gdbstub_hex_decode(client->packet[pp++]) << 4; *buf++ |= gdbstub_hex_decode(client->packet[pp++]); } return pp - client->packet_pos; } - static int gdbstub_client_read_string(gdbstub_client_t *client, char *buf, int size, char terminator) { - int pp = client->packet_pos; + int pp = client->packet_pos; char c; while (size-- && (pp < (sizeof(client->packet) - 1))) { c = client->packet[pp]; if ((c == terminator) || (c == '\0')) { - *buf = '\0'; - break; + *buf = '\0'; + break; } pp++; *buf++ = c; @@ -455,91 +489,89 @@ gdbstub_client_read_string(gdbstub_client_t *client, char *buf, int size, char t return pp - client->packet_pos; } - static int gdbstub_client_write_reg(int index, uint8_t *buf) { int width = 4; switch (index) { case GDB_REG_EAX ... GDB_REG_EDI: - cpu_state.regs[index - GDB_REG_EAX].l = *((uint32_t *) buf); - break; + cpu_state.regs[index - GDB_REG_EAX].l = *((uint32_t *) buf); + break; case GDB_REG_EIP: - gdbstub_jump(*((uint32_t *) buf)); - break; + gdbstub_jump(*((uint32_t *) buf)); + break; case GDB_REG_EFLAGS: - cpu_state.flags = *((uint16_t *) &buf[0]); - cpu_state.eflags = *((uint16_t *) &buf[2]); - break; + cpu_state.flags = *((uint16_t *) &buf[0]); + cpu_state.eflags = *((uint16_t *) &buf[2]); + break; case GDB_REG_CS ... GDB_REG_GS: - width = 2; - loadseg(*((uint16_t *) buf), segment_regs[index - GDB_REG_CS]); - flushmmucache(); - break; + width = 2; + loadseg(*((uint16_t *) buf), segment_regs[index - GDB_REG_CS]); + flushmmucache(); + break; case GDB_REG_FS_BASE ... GDB_REG_GS_BASE: - /* Do what qemu does and just load the base. */ - segment_regs[(index - 16) + (GDB_REG_FS - GDB_REG_CS)]->base = *((uint32_t *) buf); - break; + /* Do what qemu does and just load the base. */ + segment_regs[(index - 16) + (GDB_REG_FS - GDB_REG_CS)]->base = *((uint32_t *) buf); + break; case GDB_REG_CR0 ... GDB_REG_CR4: - *cr_regs[index - GDB_REG_CR0] = *((uint32_t *) buf); - flushmmucache(); - break; + *cr_regs[index - GDB_REG_CR0] = *((uint32_t *) buf); + flushmmucache(); + break; case GDB_REG_EFER: - msr.amd_efer = *((uint64_t *) buf); - break; + msr.amd_efer = *((uint64_t *) buf); + break; case GDB_REG_ST0 ... GDB_REG_ST7: - width = 10; - x87_conv_t conv = { - .eind = { .ll = *((uint64_t *) &buf[0]) }, - .begin = *((uint16_t *) &buf[8]) - }; - cpu_state.ST[(cpu_state.TOP + (index - GDB_REG_ST0)) & 7] = x87_from80(&conv); - break; + width = 10; + x87_conv_t conv = { + .eind = { .ll = *((uint64_t *) &buf[0]) }, + .begin = *((uint16_t *) &buf[8]) + }; + cpu_state.ST[(cpu_state.TOP + (index - GDB_REG_ST0)) & 7] = x87_from80(&conv); + break; case GDB_REG_FCTRL: case GDB_REG_FISEG: case GDB_REG_FOSEG: - width = 2; - *((uint16_t *) fpu_regs[index - GDB_REG_FCTRL]) = *((uint16_t *) buf); - if (index >= GDB_REG_FISEG) - flushmmucache(); - break; + width = 2; + *((uint16_t *) fpu_regs[index - GDB_REG_FCTRL]) = *((uint16_t *) buf); + if (index >= GDB_REG_FISEG) + flushmmucache(); + break; case GDB_REG_FSTAT: case GDB_REG_FOP: - width = 2; - break; + width = 2; + break; case GDB_REG_FTAG: - width = 2; - x87_settag(*((uint16_t *) buf)); - break; + width = 2; + x87_settag(*((uint16_t *) buf)); + break; case GDB_REG_FIOFF: case GDB_REG_FOOFF: - *((uint32_t *) fpu_regs[index - GDB_REG_FCTRL]) = *((uint32_t *) buf); - break; + *((uint32_t *) fpu_regs[index - GDB_REG_FCTRL]) = *((uint32_t *) buf); + break; case GDB_REG_MM0 ... GDB_REG_MM7: - width = 8; - cpu_state.MM[index - GDB_REG_MM0].q = *((uint64_t *) &buf); - break; + width = 8; + cpu_state.MM[index - GDB_REG_MM0].q = *((uint64_t *) &buf); + break; default: - width = 0; + width = 0; } return width; } - static void gdbstub_client_respond(gdbstub_client_t *client) { @@ -551,18 +583,17 @@ gdbstub_client_respond(gdbstub_client_t *client) /* Send response packet. */ client->response[client->response_pos] = '\0'; #ifdef ENABLE_GDBSTUB_LOG - i = client->response[995]; /* pclog_ex buffer too small */ + i = client->response[995]; /* pclog_ex buffer too small */ client->response[995] = '\0'; gdbstub_log("GDB Stub: Sending response: %s\n", client->response); client->response[995] = i; #endif send(client->socket, "$", 1, 0); send(client->socket, client->response, client->response_pos, 0); - char response_cksum[3] = {'#', gdbstub_hex_encode((checksum >> 4) & 0x0f), gdbstub_hex_encode(checksum & 0x0f)}; + char response_cksum[3] = { '#', gdbstub_hex_encode((checksum >> 4) & 0x0f), gdbstub_hex_encode(checksum & 0x0f) }; send(client->socket, response_cksum, sizeof(response_cksum), 0); } - static void gdbstub_client_respond_partial(gdbstub_client_t *client) { @@ -574,7 +605,6 @@ gdbstub_client_respond_partial(gdbstub_client_t *client) thread_reset_event(client->response_event); } - static void gdbstub_client_respond_hex(gdbstub_client_t *client, uint8_t *buf, int size) { @@ -584,93 +614,91 @@ gdbstub_client_respond_hex(gdbstub_client_t *client, uint8_t *buf, int size) } } - static int gdbstub_client_read_reg(int index, uint8_t *buf) { int width = 4; switch (index) { case GDB_REG_EAX ... GDB_REG_EDI: - *((uint32_t *) buf) = cpu_state.regs[index].l; - break; + *((uint32_t *) buf) = cpu_state.regs[index].l; + break; case GDB_REG_EIP: - *((uint32_t *) buf) = cs + cpu_state.pc; - break; + *((uint32_t *) buf) = cs + cpu_state.pc; + break; case GDB_REG_EFLAGS: - *((uint16_t *) &buf[0]) = cpu_state.flags; - *((uint16_t *) &buf[2]) = cpu_state.eflags; - break; + *((uint16_t *) &buf[0]) = cpu_state.flags; + *((uint16_t *) &buf[2]) = cpu_state.eflags; + break; case GDB_REG_CS ... GDB_REG_GS: - *((uint16_t *) buf) = segment_regs[index - GDB_REG_CS]->seg; - break; + *((uint16_t *) buf) = segment_regs[index - GDB_REG_CS]->seg; + break; case GDB_REG_FS_BASE ... GDB_REG_GS_BASE: - *((uint32_t *) buf) = segment_regs[(index - 16) + (GDB_REG_FS - GDB_REG_CS)]->base; - break; + *((uint32_t *) buf) = segment_regs[(index - 16) + (GDB_REG_FS - GDB_REG_CS)]->base; + break; case GDB_REG_CR0 ... GDB_REG_CR4: - *((uint32_t *) buf) = *cr_regs[index - GDB_REG_CR0]; - break; + *((uint32_t *) buf) = *cr_regs[index - GDB_REG_CR0]; + break; case GDB_REG_EFER: - *((uint64_t *) buf) = msr.amd_efer; - break; + *((uint64_t *) buf) = msr.amd_efer; + break; case GDB_REG_ST0 ... GDB_REG_ST7: - width = 10; - x87_conv_t conv; - x87_to80(cpu_state.ST[(cpu_state.TOP + (index - GDB_REG_ST0)) & 7], &conv); - *((uint64_t *) &buf[0]) = conv.eind.ll; - *((uint16_t *) &buf[8]) = conv.begin; - break; + width = 10; + x87_conv_t conv; + x87_to80(cpu_state.ST[(cpu_state.TOP + (index - GDB_REG_ST0)) & 7], &conv); + *((uint64_t *) &buf[0]) = conv.eind.ll; + *((uint16_t *) &buf[8]) = conv.begin; + break; case GDB_REG_FCTRL ... GDB_REG_FSTAT: case GDB_REG_FISEG: case GDB_REG_FOSEG: - width = 2; - *((uint16_t *) buf) = *((uint16_t *) fpu_regs[index - GDB_REG_FCTRL]); - break; + width = 2; + *((uint16_t *) buf) = *((uint16_t *) fpu_regs[index - GDB_REG_FCTRL]); + break; case GDB_REG_FTAG: - width = 2; - *((uint16_t *) buf) = x87_gettag(); - break; + width = 2; + *((uint16_t *) buf) = x87_gettag(); + break; case GDB_REG_FIOFF: case GDB_REG_FOOFF: - *((uint32_t *) buf) = *((uint32_t *) fpu_regs[index - GDB_REG_FCTRL]); - break; + *((uint32_t *) buf) = *((uint32_t *) fpu_regs[index - GDB_REG_FCTRL]); + break; case GDB_REG_FOP: - width = 2; - *((uint16_t *) buf) = 0; /* we don't store the FPU opcode */ - break; + width = 2; + *((uint16_t *) buf) = 0; /* we don't store the FPU opcode */ + break; case GDB_REG_MM0 ... GDB_REG_MM7: - width = 8; - cpu_state.MM[index - GDB_REG_MM0].q = *((uint64_t *) &buf); - break; + width = 8; + cpu_state.MM[index - GDB_REG_MM0].q = *((uint64_t *) &buf); + break; default: - width = 0; + width = 0; } return width; } - static void gdbstub_client_packet(gdbstub_client_t *client) { #ifdef GDBSTUB_CHECK_CHECKSUM /* msys2 gdb 11.1 transmits qSupported and H with invalid checksum... */ uint8_t rcv_checksum = 0, checksum = 0; #endif - int i, j = 0, k = 0, l; - uint8_t buf[10] = {0}; - char *p; + int i, j = 0, k = 0, l; + uint8_t buf[10] = { 0 }; + char *p; /* Validate checksum. */ client->packet_pos -= 2; @@ -684,12 +712,12 @@ gdbstub_client_packet(gdbstub_client_t *client) if (checksum != rcv_checksum) { /* Send negative acknowledgement. */ -#ifdef ENABLE_GDBSTUB_LOG - i = client->packet[953]; /* pclog_ex buffer too small */ +# ifdef ENABLE_GDBSTUB_LOG + i = client->packet[953]; /* pclog_ex buffer too small */ client->packet[953] = '\0'; gdbstub_log("GDB Stub: Received packet with invalid checksum (expected %02X got %02X): %s\n", checksum, rcv_checksum, client->packet); client->packet[953] = i; -#endif +# endif send(client->socket, "-", 1, 0); return; } @@ -697,7 +725,7 @@ gdbstub_client_packet(gdbstub_client_t *client) /* Send positive acknowledgement. */ #ifdef ENABLE_GDBSTUB_LOG - i = client->packet[996]; /* pclog_ex buffer too small */ + i = client->packet[996]; /* pclog_ex buffer too small */ client->packet[996] = '\0'; gdbstub_log("GDB Stub: Received packet: %s\n", client->packet); client->packet[996] = i; @@ -710,498 +738,496 @@ gdbstub_client_packet(gdbstub_client_t *client) thread_reset_event(client->response_event); } client->response_pos = 0; - client->packet_pos = 1; + client->packet_pos = 1; /* Parse command. */ switch (client->packet[0]) { case '?': /* stop reason */ - /* Respond with a stop reply packet if one is present. */ - if (stop_reason_len) { - strcpy(client->response, stop_reason); - client->response_pos = strlen(client->response); - } - break; + /* Respond with a stop reply packet if one is present. */ + if (stop_reason_len) { + strcpy(client->response, stop_reason); + client->response_pos = strlen(client->response); + } + break; case 'c': /* continue */ case 's': /* step */ - /* Flag that the client is waiting for a stop reason. */ - client->waiting_stop = 1; + /* Flag that the client is waiting for a stop reason. */ + client->waiting_stop = 1; - /* Jump to address if specified. */ - if (client->packet[1] && gdbstub_client_read_word(client, &j)) - gdbstub_jump(j); + /* Jump to address if specified. */ + if (client->packet[1] && gdbstub_client_read_word(client, &j)) + gdbstub_jump(j); - /* Resume CPU. */ - gdbstub_step = gdbstub_next_asap = (client->packet[0] == 's') ? GDBSTUB_SSTEP : GDBSTUB_EXEC; - return; + /* Resume CPU. */ + gdbstub_step = gdbstub_next_asap = (client->packet[0] == 's') ? GDBSTUB_SSTEP : GDBSTUB_EXEC; + return; case 'D': /* detach */ - /* Resume emulation. */ - gdbstub_step = GDBSTUB_EXEC; + /* Resume emulation. */ + gdbstub_step = GDBSTUB_EXEC; - /* Respond positively. */ -ok: FAST_RESPONSE("OK"); - break; + /* Respond positively. */ +ok: + FAST_RESPONSE("OK"); + break; case 'g': /* read all registers */ - /* Output the values of all registers. */ - for (i = 0; i < GDB_REG_MAX; i++) - gdbstub_client_respond_hex(client, buf, gdbstub_client_read_reg(i, buf)); - break; + /* Output the values of all registers. */ + for (i = 0; i < GDB_REG_MAX; i++) + gdbstub_client_respond_hex(client, buf, gdbstub_client_read_reg(i, buf)); + break; case 'G': /* write all registers */ - /* Write the values of all registers. */ - for (i = 0; i < GDB_REG_MAX; i++) { - if (!gdbstub_client_read_hex(client, buf, sizeof(buf))) - break; - client->packet_pos += gdbstub_client_write_reg(i, buf); - } - break; + /* Write the values of all registers. */ + for (i = 0; i < GDB_REG_MAX; i++) { + if (!gdbstub_client_read_hex(client, buf, sizeof(buf))) + break; + client->packet_pos += gdbstub_client_write_reg(i, buf); + } + break; case 'H': /* set thread */ - /* Read operation type and thread ID. */ - if ((client->packet[1] == '\0') || (client->packet[2] == '\0')) { -e22: FAST_RESPONSE("E22"); - break; - } + /* Read operation type and thread ID. */ + if ((client->packet[1] == '\0') || (client->packet[2] == '\0')) { +e22: + FAST_RESPONSE("E22"); + break; + } - /* Respond positively only on thread 1. */ - if ((client->packet[2] == '1') && !client->packet[3]) - goto ok; - else - goto e22; + /* Respond positively only on thread 1. */ + if ((client->packet[2] == '1') && !client->packet[3]) + goto ok; + else + goto e22; case 'm': /* read memory */ - /* Read address and length. */ - if (!(i = gdbstub_client_read_word(client, &j))) - goto e22; - client->packet_pos += i + 1; - gdbstub_client_read_word(client, &k); - if (!k) - goto e22; + /* Read address and length. */ + if (!(i = gdbstub_client_read_word(client, &j))) + goto e22; + client->packet_pos += i + 1; + gdbstub_client_read_word(client, &k); + if (!k) + goto e22; - /* Clamp length. */ - if (k >= (sizeof(client->response) >> 1)) - k = (sizeof(client->response) >> 1) - 1; + /* Clamp length. */ + if (k >= (sizeof(client->response) >> 1)) + k = (sizeof(client->response) >> 1) - 1; - /* Read by qwords, then by dwords, then by words, then by bytes. */ - i = 0; - if (is386) { - for (; i < (k & ~7); i += 8) { - *((uint64_t *) buf) = readmemql(j); - j += 8; - gdbstub_client_respond_hex(client, buf, 8); - } - for (; i < (k & ~3); i += 4) { - *((uint32_t *) buf) = readmemll(j); - j += 4; - gdbstub_client_respond_hex(client, buf, 4); - } + /* Read by qwords, then by dwords, then by words, then by bytes. */ + i = 0; + if (is386) { + for (; i < (k & ~7); i += 8) { + *((uint64_t *) buf) = readmemql(j); + j += 8; + gdbstub_client_respond_hex(client, buf, 8); } - for (; i < (k & ~1); i += 2) { - *((uint16_t *) buf) = readmemwl(j); - j += 2; - gdbstub_client_respond_hex(client, buf, 2); + for (; i < (k & ~3); i += 4) { + *((uint32_t *) buf) = readmemll(j); + j += 4; + gdbstub_client_respond_hex(client, buf, 4); } - for (; i < k; i++) { - buf[0] = readmembl(j++); - gdbstub_client_respond_hex(client, buf, 1); - } - break; + } + for (; i < (k & ~1); i += 2) { + *((uint16_t *) buf) = readmemwl(j); + j += 2; + gdbstub_client_respond_hex(client, buf, 2); + } + for (; i < k; i++) { + buf[0] = readmembl(j++); + gdbstub_client_respond_hex(client, buf, 1); + } + break; case 'M': /* write memory */ case 'X': /* write memory binary */ - /* Read address and length. */ - if (!(i = gdbstub_client_read_word(client, &j))) - goto e22; - client->packet_pos += i + 1; - client->packet_pos += gdbstub_client_read_word(client, &k) + 1; - if (!k) - goto e22; + /* Read address and length. */ + if (!(i = gdbstub_client_read_word(client, &j))) + goto e22; + client->packet_pos += i + 1; + client->packet_pos += gdbstub_client_read_word(client, &k) + 1; + if (!k) + goto e22; - /* Clamp length. */ - if (k >= ((sizeof(client->response) >> 1) - client->packet_pos)) - k = (sizeof(client->response) >> 1) - client->packet_pos - 1; + /* Clamp length. */ + if (k >= ((sizeof(client->response) >> 1) - client->packet_pos)) + k = (sizeof(client->response) >> 1) - client->packet_pos - 1; - /* Decode the data. */ - if (client->packet[0] == 'M') { /* hex encoded */ - gdbstub_client_read_hex(client, (uint8_t *) client->packet, k); - } else { /* binary encoded */ - i = 0; - while (i < k) { - if (client->packet[client->packet_pos] == '}') { - client->packet_pos++; - client->packet[i++] = client->packet[client->packet_pos++] ^ 0x20; - } else { - client->packet[i++] = client->packet[client->packet_pos++]; - } - } - } - - /* Write by qwords, then by dwords, then by words, then by bytes. */ - p = client->packet; + /* Decode the data. */ + if (client->packet[0] == 'M') { /* hex encoded */ + gdbstub_client_read_hex(client, (uint8_t *) client->packet, k); + } else { /* binary encoded */ i = 0; - if (is386) { - for (; i < (k & ~7); i += 8) { - writememql(j, *((uint64_t *) p)); - j += 8; - p += 8; - } - for (; i < (k & ~3); i += 4) { - writememll(j, *((uint32_t *) p)); - j += 4; - p += 4; - } + while (i < k) { + if (client->packet[client->packet_pos] == '}') { + client->packet_pos++; + client->packet[i++] = client->packet[client->packet_pos++] ^ 0x20; + } else { + client->packet[i++] = client->packet[client->packet_pos++]; + } } - for (; i < (k & ~1); i += 2) { - writememwl(j, *((uint16_t *) p)); - j += 2; - p += 2; - } - for (; i < k; i++) { - writemembl(j++, p[0]); - p++; - } + } - /* Respond positively. */ - goto ok; + /* Write by qwords, then by dwords, then by words, then by bytes. */ + p = client->packet; + i = 0; + if (is386) { + for (; i < (k & ~7); i += 8) { + writememql(j, *((uint64_t *) p)); + j += 8; + p += 8; + } + for (; i < (k & ~3); i += 4) { + writememll(j, *((uint32_t *) p)); + j += 4; + p += 4; + } + } + for (; i < (k & ~1); i += 2) { + writememwl(j, *((uint16_t *) p)); + j += 2; + p += 2; + } + for (; i < k; i++) { + writemembl(j++, p[0]); + p++; + } + + /* Respond positively. */ + goto ok; case 'p': /* read register */ - /* Read register index. */ - if (!gdbstub_client_read_word(client, &j)) { -e14: FAST_RESPONSE("E14"); - break; - } - - /* Read the register's value. */ - if (!(i = gdbstub_client_read_reg(j, buf))) - goto e14; - - /* Return value. */ - gdbstub_client_respond_hex(client, buf, i); + /* Read register index. */ + if (!gdbstub_client_read_word(client, &j)) { +e14: + FAST_RESPONSE("E14"); break; + } + + /* Read the register's value. */ + if (!(i = gdbstub_client_read_reg(j, buf))) + goto e14; + + /* Return value. */ + gdbstub_client_respond_hex(client, buf, i); + break; case 'P': /* write register */ - /* Read register index and value. */ - if (!(i = gdbstub_client_read_word(client, &j))) - goto e14; - client->packet_pos += i + 1; - if (!gdbstub_client_read_hex(client, buf, sizeof(buf))) - goto e14; + /* Read register index and value. */ + if (!(i = gdbstub_client_read_word(client, &j))) + goto e14; + client->packet_pos += i + 1; + if (!gdbstub_client_read_hex(client, buf, sizeof(buf))) + goto e14; - /* Write the value to the register. */ - if (!gdbstub_client_write_reg(j, buf)) - goto e14; + /* Write the value to the register. */ + if (!gdbstub_client_write_reg(j, buf)) + goto e14; - /* Respond positively. */ - goto ok; + /* Respond positively. */ + goto ok; case 'q': /* query */ - /* Erase response, as we'll use it as a scratch buffer. */ - memset(client->response, 0, sizeof(client->response)); + /* Erase response, as we'll use it as a scratch buffer. */ + memset(client->response, 0, sizeof(client->response)); - /* Read the query type. */ - client->packet_pos += gdbstub_client_read_string(client, client->response, sizeof(client->response) - 1, - (client->packet[1] == 'R') ? ',' : ':') + 1; + /* Read the query type. */ + client->packet_pos += gdbstub_client_read_string(client, client->response, sizeof(client->response) - 1, + (client->packet[1] == 'R') ? ',' : ':') + + 1; - /* Perform the query. */ - if (!strcmp(client->response, "Supported")) { - /* Go through the feature list and negate ones we don't support. */ - while ((client->response_pos < (sizeof(client->response) - 1)) && - (i = gdbstub_client_read_string(client, &client->response[client->response_pos], sizeof(client->response) - client->response_pos - 1, ';'))) { - client->packet_pos += i + 1; - if (strncmp(&client->response[client->response_pos], "PacketSize", 10) && - strcmp(&client->response[client->response_pos], "swbreak") && - strcmp(&client->response[client->response_pos], "hwbreak") && - strncmp(&client->response[client->response_pos], "xmlRegisters", 12) && - strcmp(&client->response[client->response_pos], "qXfer:features:read")) { - gdbstub_log("GDB Stub: Feature \"%s\" is not supported\n", &client->response[client->response_pos]); - client->response_pos += i; - client->response[client->response_pos++] = '-'; - client->response[client->response_pos++] = ';'; - } else { - gdbstub_log("GDB Stub: Feature \"%s\" is supported\n", &client->response[client->response_pos]); - } - } + /* Perform the query. */ + if (!strcmp(client->response, "Supported")) { + /* Go through the feature list and negate ones we don't support. */ + while ((client->response_pos < (sizeof(client->response) - 1)) && (i = gdbstub_client_read_string(client, &client->response[client->response_pos], sizeof(client->response) - client->response_pos - 1, ';'))) { + client->packet_pos += i + 1; + if (strncmp(&client->response[client->response_pos], "PacketSize", 10) && strcmp(&client->response[client->response_pos], "swbreak") && strcmp(&client->response[client->response_pos], "hwbreak") && strncmp(&client->response[client->response_pos], "xmlRegisters", 12) && strcmp(&client->response[client->response_pos], "qXfer:features:read")) { + gdbstub_log("GDB Stub: Feature \"%s\" is not supported\n", &client->response[client->response_pos]); + client->response_pos += i; + client->response[client->response_pos++] = '-'; + client->response[client->response_pos++] = ';'; + } else { + gdbstub_log("GDB Stub: Feature \"%s\" is supported\n", &client->response[client->response_pos]); + } + } - /* Add our supported features to the end. */ - if (client->response_pos < (sizeof(client->response) - 1)) - client->response_pos += snprintf(&client->response[client->response_pos], sizeof(client->response) - client->response_pos, - "PacketSize=%X;swbreak+;hwbreak+;qXfer:features:read+", sizeof(client->packet) - 1); - break; - } else if (!strcmp(client->response, "Xfer")) { - /* Read the transfer object. */ + /* Add our supported features to the end. */ + if (client->response_pos < (sizeof(client->response) - 1)) + client->response_pos += snprintf(&client->response[client->response_pos], sizeof(client->response) - client->response_pos, + "PacketSize=%X;swbreak+;hwbreak+;qXfer:features:read+", sizeof(client->packet) - 1); + break; + } else if (!strcmp(client->response, "Xfer")) { + /* Read the transfer object. */ + client->packet_pos += gdbstub_client_read_string(client, client->response, sizeof(client->response) - 1, ':') + 1; + if (!strcmp(client->response, "features")) { + /* Read the transfer operation. */ + client->packet_pos += gdbstub_client_read_string(client, client->response, sizeof(client->response) - 1, ':') + 1; + if (!strcmp(client->response, "read")) { + /* Read the transfer annex. */ client->packet_pos += gdbstub_client_read_string(client, client->response, sizeof(client->response) - 1, ':') + 1; - if (!strcmp(client->response, "features")) { - /* Read the transfer operation. */ - client->packet_pos += gdbstub_client_read_string(client, client->response, sizeof(client->response) - 1, ':') + 1; - if (!strcmp(client->response, "read")) { - /* Read the transfer annex. */ - client->packet_pos += gdbstub_client_read_string(client, client->response, sizeof(client->response) - 1, ':') + 1; - if (!strcmp(client->response, "target.xml")) - p = (char *) target_xml; - else - p = NULL; + if (!strcmp(client->response, "target.xml")) + p = (char *) target_xml; + else + p = NULL; - /* Stop if the file wasn't found. */ - if (!p) { -e00: FAST_RESPONSE("E00"); - break; - } - - /* Read offset and length. */ - if (!(i = gdbstub_client_read_word(client, &j))) - goto e22; - client->packet_pos += i + 1; - client->packet_pos += gdbstub_client_read_word(client, &k) + 1; - if (!k) - goto e22; - - /* Check if the offset is valid. */ - l = strlen(p); - if (j > l) - goto e00; - p += j; - - /* Return the more/less flag while also clamping the length. */ - if (k >= ((sizeof(client->response) >> 1) - 2)) - k = (sizeof(client->response) >> 1) - 3; - if (k < (l - j)) { - client->response[client->response_pos++] = 'm'; - } else { - client->response[client->response_pos++] = 'l'; - k = l - j; - } - - /* Encode the data. */ - while (k--) { - i = *p++; - if ((i == '\0') || (i == '#') || (i == '$') || (i == '*') || (i == '}')) { - client->response[client->response_pos++] = '}'; - client->response[client->response_pos++] = i ^ 0x20; - } else { - client->response[client->response_pos++] = i; - } - } - break; - } + /* Stop if the file wasn't found. */ + if (!p) { +e00: + FAST_RESPONSE("E00"); + break; } - } else if (!strcmp(client->response, "Rcmd")) { - /* Read and decode command in-place. */ - i = gdbstub_client_read_hex(client, (uint8_t *) client->packet, strlen(client->packet) - client->packet_pos); - client->packet[i] = 0; - gdbstub_log("GDB Stub: Monitor command: %s\n", client->packet); - /* Parse the command name. */ - char *strtok_save; - p = strtok_r(client->packet, " ", &strtok_save); - if (!p) - goto ok; + /* Read offset and length. */ + if (!(i = gdbstub_client_read_word(client, &j))) + goto e22; + client->packet_pos += i + 1; + client->packet_pos += gdbstub_client_read_word(client, &k) + 1; + if (!k) + goto e22; - /* Interpret the command. */ - if (p[0] == 'i') { - /* Read I/O operation width. */ - l = (p[1] == 'n') ? p[2] : p[1]; + /* Check if the offset is valid. */ + l = strlen(p); + if (j > l) + goto e00; + p += j; - /* Read optional I/O port. */ - if (!(p = strtok_r(NULL, " ", &strtok_save)) || - !gdbstub_num_decode(p, &j, GDB_MODE_HEX) || - (j < 0) || (j >= 65536)) - j = client->last_io_base; + /* Return the more/less flag while also clamping the length. */ + if (k >= ((sizeof(client->response) >> 1) - 2)) + k = (sizeof(client->response) >> 1) - 3; + if (k < (l - j)) { + client->response[client->response_pos++] = 'm'; + } else { + client->response[client->response_pos++] = 'l'; + k = l - j; + } - /* Read optional length. */ - if (!(p = strtok_r(NULL, " ", &strtok_save)) || !gdbstub_num_decode(p, &k, GDB_MODE_BASE10)) - k = client->last_io_len; + /* Encode the data. */ + while (k--) { + i = *p++; + if ((i == '\0') || (i == '#') || (i == '$') || (i == '*') || (i == '}')) { + client->response[client->response_pos++] = '}'; + client->response[client->response_pos++] = i ^ 0x20; + } else { + client->response[client->response_pos++] = i; + } + } + break; + } + } + } else if (!strcmp(client->response, "Rcmd")) { + /* Read and decode command in-place. */ + i = gdbstub_client_read_hex(client, (uint8_t *) client->packet, strlen(client->packet) - client->packet_pos); + client->packet[i] = 0; + gdbstub_log("GDB Stub: Monitor command: %s\n", client->packet); - /* Clamp length. */ - if (k < 1) - k = 1; - if (k > (65536 - j)) - k = 65536 - j; + /* Parse the command name. */ + char *strtok_save; + p = strtok_r(client->packet, " ", &strtok_save); + if (!p) + goto ok; - /* Read ports. */ - i = 0; - while (i < k) { - if ((i % 16) == 0) { - if (i) { - client->packet[client->packet_pos++] = '\n'; + /* Interpret the command. */ + if (p[0] == 'i') { + /* Read I/O operation width. */ + l = (p[1] == 'n') ? p[2] : p[1]; - /* Provide partial response with the last line. */ - client->response_pos = 0; - client->response[client->response_pos++] = 'O'; - gdbstub_client_respond_hex(client, (uint8_t *) client->packet, client->packet_pos); - gdbstub_client_respond_partial(client); - } - client->packet_pos = sprintf(client->packet, "%04X:", j + i); - } - /* Act according to I/O operation width. */ - switch (l) { - case 'd': - case 'l': - client->packet_pos += sprintf(&client->packet[client->packet_pos], " %08X", inl(j + i)); - i += 4; - break; + /* Read optional I/O port. */ + if (!(p = strtok_r(NULL, " ", &strtok_save)) || !gdbstub_num_decode(p, &j, GDB_MODE_HEX) || (j < 0) || (j >= 65536)) + j = client->last_io_base; - case 'w': - client->packet_pos += sprintf(&client->packet[client->packet_pos], " %04X", inw(j + i)); - i += 2; - break; + /* Read optional length. */ + if (!(p = strtok_r(NULL, " ", &strtok_save)) || !gdbstub_num_decode(p, &k, GDB_MODE_BASE10)) + k = client->last_io_len; - case 'b': - case '\0': - client->packet_pos += sprintf(&client->packet[client->packet_pos], " %02X", inb(j + i)); - i++; - break; + /* Clamp length. */ + if (k < 1) + k = 1; + if (k > (65536 - j)) + k = 65536 - j; - default: - goto unknown; - } - } + /* Read ports. */ + i = 0; + while (i < k) { + if ((i % 16) == 0) { + if (i) { client->packet[client->packet_pos++] = '\n'; - /* Respond with the final line. */ - client->response_pos = 0; - gdbstub_client_respond_hex(client, (uint8_t *) &client->packet, client->packet_pos); - break; - } else { -unknown: FAST_RESPONSE_HEX("Unknown command\n"); - break; + /* Provide partial response with the last line. */ + client->response_pos = 0; + client->response[client->response_pos++] = 'O'; + gdbstub_client_respond_hex(client, (uint8_t *) client->packet, client->packet_pos); + gdbstub_client_respond_partial(client); + } + client->packet_pos = sprintf(client->packet, "%04X:", j + i); } + /* Act according to I/O operation width. */ + switch (l) { + case 'd': + case 'l': + client->packet_pos += sprintf(&client->packet[client->packet_pos], " %08X", inl(j + i)); + i += 4; + break; - goto ok; + case 'w': + client->packet_pos += sprintf(&client->packet[client->packet_pos], " %04X", inw(j + i)); + i += 2; + break; + + case 'b': + case '\0': + client->packet_pos += sprintf(&client->packet[client->packet_pos], " %02X", inb(j + i)); + i++; + break; + + default: + goto unknown; + } + } + client->packet[client->packet_pos++] = '\n'; + + /* Respond with the final line. */ + client->response_pos = 0; + gdbstub_client_respond_hex(client, (uint8_t *) &client->packet, client->packet_pos); + break; + } else { +unknown: + FAST_RESPONSE_HEX("Unknown command\n"); + break; } - break; + + goto ok; + } + break; case 'z': /* remove break/watchpoint */ case 'Z': /* insert break/watchpoint */ - gdbstub_breakpoint_t *breakpoint, *prev_breakpoint = NULL, **first_breakpoint; + gdbstub_breakpoint_t *breakpoint, *prev_breakpoint = NULL, **first_breakpoint; - /* Parse breakpoint type. */ - switch (client->packet[1]) { - case '0': /* software breakpoint */ - first_breakpoint = &first_swbreak; - break; + /* Parse breakpoint type. */ + switch (client->packet[1]) { + case '0': /* software breakpoint */ + first_breakpoint = &first_swbreak; + break; - case '1': /* hardware breakpoint */ - first_breakpoint = &first_hwbreak; - break; + case '1': /* hardware breakpoint */ + first_breakpoint = &first_hwbreak; + break; - case '2': /* write watchpoint */ - first_breakpoint = &first_wwatch; - break; + case '2': /* write watchpoint */ + first_breakpoint = &first_wwatch; + break; - case '3': /* read watchpoint */ - first_breakpoint = &first_rwatch; - break; + case '3': /* read watchpoint */ + first_breakpoint = &first_rwatch; + break; - case '4': /* access watchpoint */ - first_breakpoint = &first_awatch; - break; + case '4': /* access watchpoint */ + first_breakpoint = &first_awatch; + break; - default: /* unknown type */ - client->packet[2] = '\0'; /* force address check to fail */ - break; - } + default: /* unknown type */ + client->packet[2] = '\0'; /* force address check to fail */ + break; + } - /* Read address. */ - if (client->packet[2] != ',') - break; - client->packet_pos = 3; - if (!(i = gdbstub_client_read_word(client, &j))) - break; - client->packet_pos += i; - if (client->packet[client->packet_pos++] == ',') - gdbstub_client_read_word(client, &k); - else - k = 1; + /* Read address. */ + if (client->packet[2] != ',') + break; + client->packet_pos = 3; + if (!(i = gdbstub_client_read_word(client, &j))) + break; + client->packet_pos += i; + if (client->packet[client->packet_pos++] == ',') + gdbstub_client_read_word(client, &k); + else + k = 1; - /* Test writability of software breakpoint. */ - if (client->packet[1] == '0') { - buf[0] = readmembl(j); - writemembl(j, 0xcc); - buf[1] = readmembl(j); - writemembl(j, buf[0]); - if (buf[1] != 0xcc) - goto end; - } + /* Test writability of software breakpoint. */ + if (client->packet[1] == '0') { + buf[0] = readmembl(j); + writemembl(j, 0xcc); + buf[1] = readmembl(j); + writemembl(j, buf[0]); + if (buf[1] != 0xcc) + goto end; + } + + /* Find an existing breakpoint with this address. */ + breakpoint = *first_breakpoint; + while (breakpoint) { + if (breakpoint->addr == j) + break; + prev_breakpoint = breakpoint; + breakpoint = breakpoint->next; + } + + /* Check if the breakpoint is already present (when inserting) or not found (when removing). */ + if ((!!breakpoint) ^ (client->packet[0] == 'z')) + goto e22; + + /* Insert or remove the breakpoint. */ + if (client->packet[0] != 'z') { + /* Allocate a new breakpoint. */ + breakpoint = malloc(sizeof(gdbstub_breakpoint_t)); + breakpoint->addr = j; + breakpoint->end = j + k; + breakpoint->next = NULL; + + /* Add the new breakpoint to the list. */ + if (!(*first_breakpoint)) + *first_breakpoint = breakpoint; + else if (prev_breakpoint) + prev_breakpoint->next = breakpoint; + } else { + /* Remove breakpoint from the list. */ + if (breakpoint == *first_breakpoint) + *first_breakpoint = breakpoint->next; + else if (prev_breakpoint) + prev_breakpoint->next = breakpoint->next; + + /* De-allocate breakpoint. */ + free(breakpoint); + } + + /* Update the page watchpoint map if we're dealing with a watchpoint. */ + if (client->packet[1] >= '2') { + /* Clear this watchpoint's corresponding page map groups, + as everything is going to be recomputed soon anyway. */ + memset(&gdbstub_watch_pages[j >> (MEM_GRANULARITY_BITS + 6)], 0, + (((k - 1) >> (MEM_GRANULARITY_BITS + 6)) + 1) * sizeof(gdbstub_watch_pages[0])); + + /* Go through all watchpoint lists. */ + l = 0; + breakpoint = first_rwatch; + while (1) { + if (breakpoint) { + /* Flag this watchpoint's corresponding pages as having a watchpoint. */ + k = (breakpoint->end - 1) >> MEM_GRANULARITY_BITS; + for (i = breakpoint->addr >> MEM_GRANULARITY_BITS; i <= k; i++) + gdbstub_watch_pages[i >> 6] |= (1 << (i & 63)); - /* Find an existing breakpoint with this address. */ - breakpoint = *first_breakpoint; - while (breakpoint) { - if (breakpoint->addr == j) - break; - prev_breakpoint = breakpoint; breakpoint = breakpoint->next; + } else { + /* Jump from list to list as a shortcut. */ + if (l == 0) + breakpoint = first_wwatch; + else if (l == 1) + breakpoint = first_awatch; + else + break; + l++; + } } + } - /* Check if the breakpoint is already present (when inserting) or not found (when removing). */ - if ((!!breakpoint) ^ (client->packet[0] == 'z')) - goto e22; - - /* Insert or remove the breakpoint. */ - if (client->packet[0] != 'z') { - /* Allocate a new breakpoint. */ - breakpoint = malloc(sizeof(gdbstub_breakpoint_t)); - breakpoint->addr = j; - breakpoint->end = j + k; - breakpoint->next = NULL; - - /* Add the new breakpoint to the list. */ - if (!(*first_breakpoint)) - *first_breakpoint = breakpoint; - else if (prev_breakpoint) - prev_breakpoint->next = breakpoint; - } else { - /* Remove breakpoint from the list. */ - if (breakpoint == *first_breakpoint) - *first_breakpoint = breakpoint->next; - else if (prev_breakpoint) - prev_breakpoint->next = breakpoint->next; - - /* De-allocate breakpoint. */ - free(breakpoint); - } - - /* Update the page watchpoint map if we're dealing with a watchpoint. */ - if (client->packet[1] >= '2') { - /* Clear this watchpoint's corresponding page map groups, - as everything is going to be recomputed soon anyway. */ - memset(&gdbstub_watch_pages[j >> (MEM_GRANULARITY_BITS + 6)], 0, - (((k - 1) >> (MEM_GRANULARITY_BITS + 6)) + 1) * sizeof(gdbstub_watch_pages[0])); - - /* Go through all watchpoint lists. */ - l = 0; - breakpoint = first_rwatch; - while (1) { - if (breakpoint) { - /* Flag this watchpoint's corresponding pages as having a watchpoint. */ - k = (breakpoint->end - 1) >> MEM_GRANULARITY_BITS; - for (i = breakpoint->addr >> MEM_GRANULARITY_BITS; i <= k; i++) - gdbstub_watch_pages[i >> 6] |= (1 << (i & 63)); - - breakpoint = breakpoint->next; - } else { - /* Jump from list to list as a shortcut. */ - if (l == 0) - breakpoint = first_wwatch; - else if (l == 1) - breakpoint = first_awatch; - else - break; - l++; - } - } - } - - /* Respond positively. */ - goto ok; + /* Respond positively. */ + goto ok; } end: /* Send response. */ gdbstub_client_respond(client); } - static void gdbstub_cpu_exec(int cycs) { @@ -1213,15 +1239,15 @@ gdbstub_cpu_exec(int cycs) /* Swap in any software breakpoints. */ gdbstub_breakpoint_t *swbreak = first_swbreak; while (swbreak) { - /* Swap the INT 3 opcode into the address. */ - swbreak->orig_val = readmembl(swbreak->addr); - writemembl(swbreak->addr, 0xcc); - swbreak = swbreak->next; + /* Swap the INT 3 opcode into the address. */ + swbreak->orig_val = readmembl(swbreak->addr); + writemembl(swbreak->addr, 0xcc); + swbreak = swbreak->next; } /* Call the original cpu_exec function outside the debugger context. */ if ((gdbstub_step == GDBSTUB_SSTEP) && ((cycles + cycs) <= 0)) - cycs += -(cycles + cycs) + 1; + cycs += -(cycles + cycs) + 1; in_gdbstub = 0; cpu_exec_shadow(cycs); in_gdbstub = 1; @@ -1229,9 +1255,9 @@ gdbstub_cpu_exec(int cycs) /* Swap out any software breakpoints. */ swbreak = first_swbreak; while (swbreak) { - if (readmembl(swbreak->addr) == 0xcc) - writemembl(swbreak->addr, swbreak->orig_val); - swbreak = swbreak->next; + if (readmembl(swbreak->addr) == 0xcc) + writemembl(swbreak->addr, swbreak->orig_val); + swbreak = swbreak->next; } } @@ -1245,41 +1271,41 @@ gdbstub_cpu_exec(int cycs) /* Add extended break reason. */ if (gdbstub_step >= GDBSTUB_BREAK_RWATCH) { - if (gdbstub_step != GDBSTUB_BREAK_WWATCH) - stop_reason[stop_reason_len++] = (gdbstub_step == GDBSTUB_BREAK_RWATCH) ? 'r' : 'a'; - stop_reason[stop_reason_len++] = 'w'; - stop_reason[stop_reason_len++] = 'a'; - stop_reason[stop_reason_len++] = 't'; - stop_reason[stop_reason_len++] = 'c'; - stop_reason[stop_reason_len++] = 'h'; - stop_reason[stop_reason_len++] = ':'; - stop_reason_len += sprintf(&stop_reason[stop_reason_len], "%X;", watch_addr); + if (gdbstub_step != GDBSTUB_BREAK_WWATCH) + stop_reason[stop_reason_len++] = (gdbstub_step == GDBSTUB_BREAK_RWATCH) ? 'r' : 'a'; + stop_reason[stop_reason_len++] = 'w'; + stop_reason[stop_reason_len++] = 'a'; + stop_reason[stop_reason_len++] = 't'; + stop_reason[stop_reason_len++] = 'c'; + stop_reason[stop_reason_len++] = 'h'; + stop_reason[stop_reason_len++] = ':'; + stop_reason_len += sprintf(&stop_reason[stop_reason_len], "%X;", watch_addr); } else if (gdbstub_step >= GDBSTUB_BREAK_SW) { - stop_reason[stop_reason_len++] = (gdbstub_step == GDBSTUB_BREAK_SW) ? 's' : 'h'; - stop_reason[stop_reason_len++] = 'w'; - stop_reason[stop_reason_len++] = 'b'; - stop_reason[stop_reason_len++] = 'r'; - stop_reason[stop_reason_len++] = 'e'; - stop_reason[stop_reason_len++] = 'a'; - stop_reason[stop_reason_len++] = 'k'; - stop_reason[stop_reason_len++] = ':'; - stop_reason[stop_reason_len++] = ';'; + stop_reason[stop_reason_len++] = (gdbstub_step == GDBSTUB_BREAK_SW) ? 's' : 'h'; + stop_reason[stop_reason_len++] = 'w'; + stop_reason[stop_reason_len++] = 'b'; + stop_reason[stop_reason_len++] = 'r'; + stop_reason[stop_reason_len++] = 'e'; + stop_reason[stop_reason_len++] = 'a'; + stop_reason[stop_reason_len++] = 'k'; + stop_reason[stop_reason_len++] = ':'; + stop_reason[stop_reason_len++] = ';'; } /* Add register dump. */ - uint8_t buf[10] = {0}; - int i, j, k; + uint8_t buf[10] = { 0 }; + int i, j, k; for (i = 0; i < GDB_REG_MAX; i++) { - if (i >= 0x10) - stop_reason[stop_reason_len++] = gdbstub_hex_encode(i >> 4); - stop_reason[stop_reason_len++] = gdbstub_hex_encode(i & 0x0f); - stop_reason[stop_reason_len++] = ':'; - j = gdbstub_client_read_reg(i, buf); - for (k = 0; k < j; k++) { - stop_reason[stop_reason_len++] = gdbstub_hex_encode(buf[k] >> 4); - stop_reason[stop_reason_len++] = gdbstub_hex_encode(buf[k] & 0x0f); - } - stop_reason[stop_reason_len++] = ';'; + if (i >= 0x10) + stop_reason[stop_reason_len++] = gdbstub_hex_encode(i >> 4); + stop_reason[stop_reason_len++] = gdbstub_hex_encode(i & 0x0f); + stop_reason[stop_reason_len++] = ':'; + j = gdbstub_client_read_reg(i, buf); + for (k = 0; k < j; k++) { + stop_reason[stop_reason_len++] = gdbstub_hex_encode(buf[k] >> 4); + stop_reason[stop_reason_len++] = gdbstub_hex_encode(buf[k] & 0x0f); + } + stop_reason[stop_reason_len++] = ';'; } /* Don't execute the CPU any further if single-stepping. */ @@ -1295,26 +1321,26 @@ gdbstub_cpu_exec(int cycs) while (client) { /* Report stop reason if the client is waiting for one. */ if (client->waiting_stop && stop_reason_len) { - client->waiting_stop = 0; + client->waiting_stop = 0; - /* Wait for any pending responses to be acknowledged. */ - if (!thread_wait_event(client->response_event, -1)) { - /* Block other responses from being written while this one isn't acknowledged. */ - thread_reset_event(client->response_event); + /* Wait for any pending responses to be acknowledged. */ + if (!thread_wait_event(client->response_event, -1)) { + /* Block other responses from being written while this one isn't acknowledged. */ + thread_reset_event(client->response_event); - /* Write stop reason response. */ - strcpy(client->response, stop_reason); - client->response_pos = stop_reason_len; - gdbstub_client_respond(client); - } else { - gdbstub_log("GDB Stub: Timed out waiting for client %s:%d\n", inet_ntoa(client->addr.sin_addr), client->addr.sin_port); - } + /* Write stop reason response. */ + strcpy(client->response, stop_reason); + client->response_pos = stop_reason_len; + gdbstub_client_respond(client); + } else { + gdbstub_log("GDB Stub: Timed out waiting for client %s:%d\n", inet_ntoa(client->addr.sin_addr), client->addr.sin_port); + } } if (client->has_packet) { - gdbstub_client_packet(client); - client->has_packet = client->packet_pos = 0; - thread_set_event(client->processed_event); + gdbstub_client_packet(client); + client->has_packet = client->packet_pos = 0; + thread_set_event(client->processed_event); } #ifdef GDBSTUB_ALLOW_MULTI_CLIENTS @@ -1329,7 +1355,6 @@ gdbstub_cpu_exec(int cycs) in_gdbstub = 0; } - static void gdbstub_client_thread(void *priv) { @@ -1346,60 +1371,60 @@ gdbstub_client_thread(void *priv) /* Read data from client. */ while ((bytes_read = recv(client->socket, (char *) buf, sizeof(buf), 0)) > 0) { for (i = 0; i < bytes_read; i++) { - switch (buf[i]) { - case '$': /* packet start */ - /* Wait for any existing packets to be processed. */ - thread_wait_event(client->processed_event, -1); + switch (buf[i]) { + case '$': /* packet start */ + /* Wait for any existing packets to be processed. */ + thread_wait_event(client->processed_event, -1); - client->packet_pos = 0; - break; + client->packet_pos = 0; + break; - case '-': /* negative acknowledgement */ - /* Retransmit the current response. */ - gdbstub_client_respond(client); - break; + case '-': /* negative acknowledgement */ + /* Retransmit the current response. */ + gdbstub_client_respond(client); + break; - case '+': /* positive acknowledgement */ - /* Allow another response to be written. */ - thread_set_event(client->response_event); - break; + case '+': /* positive acknowledgement */ + /* Allow another response to be written. */ + thread_set_event(client->response_event); + break; - case 0x03: /* break */ - /* Wait for any existing packets to be processed. */ - thread_wait_event(client->processed_event, -1); + case 0x03: /* break */ + /* Wait for any existing packets to be processed. */ + thread_wait_event(client->processed_event, -1); - /* Break immediately. */ - gdbstub_log("GDB Stub: Break requested\n"); - gdbstub_break(); - break; + /* Break immediately. */ + gdbstub_log("GDB Stub: Break requested\n"); + gdbstub_break(); + break; - default: - /* Wait for any existing packets to be processed, just in case. */ - thread_wait_event(client->processed_event, -1); + default: + /* Wait for any existing packets to be processed, just in case. */ + thread_wait_event(client->processed_event, -1); - if (client->packet_pos < (sizeof(client->packet) - 1)) { - /* Append byte to the packet. */ - client->packet[client->packet_pos++] = buf[i]; + if (client->packet_pos < (sizeof(client->packet) - 1)) { + /* Append byte to the packet. */ + client->packet[client->packet_pos++] = buf[i]; - /* Check if we're at the end of a packet. */ - if ((client->packet_pos >= 3) && (client->packet[client->packet_pos - 3] == '#')) { /* packet checksum start */ - /* Small hack to speed up IDA instruction trace mode. */ - if (*((uint32_t *) client->packet) == ('H' | ('c' << 8) | ('1' << 16) | ('#' << 24))) { - /* Send pre-computed response. */ - send(client->socket, "+$OK#9A", 7, 0); + /* Check if we're at the end of a packet. */ + if ((client->packet_pos >= 3) && (client->packet[client->packet_pos - 3] == '#')) { /* packet checksum start */ + /* Small hack to speed up IDA instruction trace mode. */ + if (*((uint32_t *) client->packet) == ('H' | ('c' << 8) | ('1' << 16) | ('#' << 24))) { + /* Send pre-computed response. */ + send(client->socket, "+$OK#9A", 7, 0); - /* Skip processing. */ - continue; - } + /* Skip processing. */ + continue; + } - /* Flag that a packet should be processed. */ - client->packet[client->packet_pos] = '\0'; - thread_reset_event(client->processed_event); - gdbstub_next_asap = client->has_packet = 1; - } - } - break; - } + /* Flag that a packet should be processed. */ + client->packet[client->packet_pos] = '\0'; + thread_reset_event(client->processed_event); + gdbstub_next_asap = client->has_packet = 1; + } + } + break; + } } } @@ -1421,20 +1446,20 @@ gdbstub_client_thread(void *priv) #endif first_client = client->next; if (first_client == NULL) { - last_client = NULL; - gdbstub_step = GDBSTUB_EXEC; /* unpause CPU when all clients are disconnected */ + last_client = NULL; + gdbstub_step = GDBSTUB_EXEC; /* unpause CPU when all clients are disconnected */ } #ifdef GDBSTUB_ALLOW_MULTI_CLIENTS } else { other_client = first_client; while (other_client) { - if (other_client->next == client) { - if (last_client == client) - last_client = other_client; - other_client->next = client->next; - break; - } - other_client = other_client->next; + if (other_client->next == client) { + if (last_client == client) + last_client = other_client; + other_client->next = client->next; + break; + } + other_client = other_client->next; } } #endif @@ -1443,7 +1468,6 @@ gdbstub_client_thread(void *priv) thread_release_mutex(client_list_mutex); } - static void gdbstub_server_thread(void *priv) { @@ -1452,31 +1476,31 @@ gdbstub_server_thread(void *priv) /* Accept connections. */ gdbstub_client_t *client; - socklen_t sl = sizeof(struct sockaddr_in); + socklen_t sl = sizeof(struct sockaddr_in); while (1) { /* Allocate client structure. */ client = malloc(sizeof(gdbstub_client_t)); memset(client, 0, sizeof(gdbstub_client_t)); client->processed_event = thread_create_event(); - client->response_event = thread_create_event(); + client->response_event = thread_create_event(); /* Accept connection. */ client->socket = accept(gdbstub_socket, (struct sockaddr *) &client->addr, &sl); if (client->socket < 0) - break; + break; /* Add to client list. */ thread_wait_mutex(client_list_mutex); if (first_client) { #ifdef GDBSTUB_ALLOW_MULTI_CLIENTS - last_client->next = client; - last_client = client; + last_client->next = client; + last_client = client; #else - first_client->next = last_client = client; - close(first_client->socket); + first_client->next = last_client = client; + close(first_client->socket); #endif } else { - first_client = last_client = client; + first_client = last_client = client; } thread_release_mutex(client_list_mutex); @@ -1493,18 +1517,16 @@ gdbstub_server_thread(void *priv) free(client); } - void gdbstub_cpu_init() { /* Replace cpu_exec with our own function if the GDB stub is active. */ if ((gdbstub_socket != -1) && (cpu_exec != gdbstub_cpu_exec)) { cpu_exec_shadow = cpu_exec; - cpu_exec = gdbstub_cpu_exec; + cpu_exec = gdbstub_cpu_exec; } } - int gdbstub_instruction() { @@ -1516,18 +1538,18 @@ gdbstub_instruction() /* Go through the list of software breakpoints. */ do { - /* Check if the breakpoint coincides with this address. */ - if (breakpoint->addr == wanted_addr) { - gdbstub_log("GDB Stub: Hardware breakpoint at %08X\n", wanted_addr); + /* Check if the breakpoint coincides with this address. */ + if (breakpoint->addr == wanted_addr) { + gdbstub_log("GDB Stub: Hardware breakpoint at %08X\n", wanted_addr); - /* Flag that we're in a hardware breakpoint. */ - gdbstub_step = GDBSTUB_BREAK_HW; + /* Flag that we're in a hardware breakpoint. */ + gdbstub_step = GDBSTUB_BREAK_HW; - /* Pause execution. */ - return 1; - } + /* Pause execution. */ + return 1; + } - breakpoint = breakpoint->next; + breakpoint = breakpoint->next; } while (breakpoint); } @@ -1535,7 +1557,6 @@ gdbstub_instruction() return gdbstub_step - GDBSTUB_EXEC; } - int gdbstub_int3() { @@ -1545,26 +1566,26 @@ gdbstub_int3() /* Calculate the breakpoint instruction's address. */ uint32_t new_pc = cpu_state.pc - 1; if (cpu_state.op32) - new_pc &= 0xffff; + new_pc &= 0xffff; uint32_t wanted_addr = cs + new_pc; /* Go through the list of software breakpoints. */ do { - /* Check if the breakpoint coincides with this address. */ - if (breakpoint->addr == wanted_addr) { - gdbstub_log("GDB Stub: Software breakpoint at %08X\n", wanted_addr); + /* Check if the breakpoint coincides with this address. */ + if (breakpoint->addr == wanted_addr) { + gdbstub_log("GDB Stub: Software breakpoint at %08X\n", wanted_addr); - /* Move EIP back to where the break instruction was. */ - cpu_state.pc = new_pc; + /* Move EIP back to where the break instruction was. */ + cpu_state.pc = new_pc; - /* Flag that we're in a software breakpoint. */ - gdbstub_step = GDBSTUB_BREAK_SW; + /* Flag that we're in a software breakpoint. */ + gdbstub_step = GDBSTUB_BREAK_SW; - /* Abort INT 3 execution. */ - return 1; - } + /* Abort INT 3 execution. */ + return 1; + } - breakpoint = breakpoint->next; + breakpoint = breakpoint->next; } while (breakpoint); } @@ -1572,7 +1593,6 @@ gdbstub_int3() return 0; } - void gdbstub_mem_access(uint32_t *addrs, int access) { @@ -1586,35 +1606,34 @@ gdbstub_mem_access(uint32_t *addrs, int access) gdbstub_breakpoint_t *watchpoint = (access & GDBSTUB_MEM_WRITE) ? first_wwatch : first_rwatch; while (1) { if (watchpoint) { - /* Check if any component of this address is within the breakpoint's range. */ - for (i = 0; i < width; i++) { - if ((addrs[i] >= watchpoint->addr) && (addrs[i] < watchpoint->end)) - break; - } - if (i < width) { - gdbstub_log("GDB Stub: %s watchpoint at %08X\n", (access & GDBSTUB_MEM_AWATCH) ? "Access" : ((access & GDBSTUB_MEM_WRITE) ? "Write" : "Read"), watch_addr); + /* Check if any component of this address is within the breakpoint's range. */ + for (i = 0; i < width; i++) { + if ((addrs[i] >= watchpoint->addr) && (addrs[i] < watchpoint->end)) + break; + } + if (i < width) { + gdbstub_log("GDB Stub: %s watchpoint at %08X\n", (access & GDBSTUB_MEM_AWATCH) ? "Access" : ((access & GDBSTUB_MEM_WRITE) ? "Write" : "Read"), watch_addr); - /* Flag that we're in a read/write watchpoint. */ - gdbstub_step = (access & GDBSTUB_MEM_AWATCH) ? GDBSTUB_BREAK_AWATCH : ((access & GDBSTUB_MEM_WRITE) ? GDBSTUB_BREAK_WWATCH : GDBSTUB_BREAK_RWATCH); + /* Flag that we're in a read/write watchpoint. */ + gdbstub_step = (access & GDBSTUB_MEM_AWATCH) ? GDBSTUB_BREAK_AWATCH : ((access & GDBSTUB_MEM_WRITE) ? GDBSTUB_BREAK_WWATCH : GDBSTUB_BREAK_RWATCH); - /* Stop looking. */ - return; - } + /* Stop looking. */ + return; + } - watchpoint = watchpoint->next; + watchpoint = watchpoint->next; } else { - /* Jump from list to list as a shortcut. */ - if (access & GDBSTUB_MEM_AWATCH) { - break; - } else { - watchpoint = first_awatch; - access |= GDBSTUB_MEM_AWATCH; - } + /* Jump from list to list as a shortcut. */ + if (access & GDBSTUB_MEM_AWATCH) { + break; + } else { + watchpoint = first_awatch; + access |= GDBSTUB_MEM_AWATCH; + } } } } - void gdbstub_init() { @@ -1629,11 +1648,11 @@ gdbstub_init() } /* Bind GDB server socket. */ - int port = 12345; + int port = 12345; struct sockaddr_in bind_addr = { .sin_family = AF_INET, - .sin_addr = { .s_addr = INADDR_ANY }, - .sin_port = htons(port) + .sin_addr = { .s_addr = INADDR_ANY }, + .sin_port = htons(port) }; if (bind(gdbstub_socket, (struct sockaddr *) &bind_addr, sizeof(bind_addr)) == -1) { pclog("GDB Stub: Failed to bind on port %d (%d)\n", port, WSAGetLastError()); @@ -1655,7 +1674,6 @@ gdbstub_init() gdbstub_step = GDBSTUB_BREAK; } - void gdbstub_close() { @@ -1669,9 +1687,9 @@ gdbstub_close() /* Clear client list. */ thread_wait_mutex(client_list_mutex); gdbstub_client_t *client = first_client; - int socket; + int socket; while (client) { - socket = client->socket; + socket = client->socket; client->socket = -1; close(socket); client = client->next; diff --git a/src/include/86box/gdbstub.h b/src/include/86box/gdbstub.h index d562f6a6e..7ed893392 100644 --- a/src/include/86box/gdbstub.h +++ b/src/include/86box/gdbstub.h @@ -15,12 +15,12 @@ * Copyright 2022 RichardG. */ #ifndef EMU_GDBSTUB_H -# define EMU_GDBSTUB_H +#define EMU_GDBSTUB_H #include <86box/mem.h> -#define GDBSTUB_MEM_READ 0 -#define GDBSTUB_MEM_WRITE 16 -#define GDBSTUB_MEM_AWATCH 32 +#define GDBSTUB_MEM_READ 0 +#define GDBSTUB_MEM_WRITE 16 +#define GDBSTUB_MEM_AWATCH 32 enum { GDBSTUB_EXEC = 0, @@ -35,21 +35,21 @@ enum { #ifdef USE_GDBSTUB -#define GDBSTUB_MEM_ACCESS(addr, access, width) \ - uint32_t gdbstub_page = addr >> MEM_GRANULARITY_BITS; \ - if (gdbstub_watch_pages[gdbstub_page >> 6] & (1 << (gdbstub_page & 63)) || (addr == 0xb8dd4)) { \ - uint32_t gdbstub_addrs[width]; \ - for (int gdbstub_i = 0; gdbstub_i < width; gdbstub_i++) \ - gdbstub_addrs[gdbstub_i] = addr + gdbstub_i; \ - gdbstub_mem_access(gdbstub_addrs, access | width); \ - } +# define GDBSTUB_MEM_ACCESS(addr, access, width) \ + uint32_t gdbstub_page = addr >> MEM_GRANULARITY_BITS; \ + if (gdbstub_watch_pages[gdbstub_page >> 6] & (1 << (gdbstub_page & 63))) { \ + uint32_t gdbstub_addrs[width]; \ + for (int gdbstub_i = 0; gdbstub_i < width; gdbstub_i++) \ + gdbstub_addrs[gdbstub_i] = addr + gdbstub_i; \ + gdbstub_mem_access(gdbstub_addrs, access | width); \ + } -#define GDBSTUB_MEM_ACCESS_FAST(addrs, access, width) \ - uint32_t gdbstub_page = addr >> MEM_GRANULARITY_BITS; \ - if (gdbstub_watch_pages[gdbstub_page >> 6] & (1 << (gdbstub_page & 63)) || (addr == 0xb8dd4)) \ - gdbstub_mem_access(addrs, access | width); +# define GDBSTUB_MEM_ACCESS_FAST(addrs, access, width) \ + uint32_t gdbstub_page = addr >> MEM_GRANULARITY_BITS; \ + if (gdbstub_watch_pages[gdbstub_page >> 6] & (1 << (gdbstub_page & 63))) \ + gdbstub_mem_access(addrs, access | width); -extern int gdbstub_step, gdbstub_next_asap; +extern int gdbstub_step, gdbstub_next_asap; extern uint64_t gdbstub_watch_pages[(((uint32_t) -1) >> (MEM_GRANULARITY_BITS + 6)) + 1]; extern void gdbstub_cpu_init(); @@ -61,17 +61,17 @@ extern void gdbstub_close(); #else -#define GDBSTUB_MEM_ACCESS(addr, access, width) -#define GDBSTUB_MEM_ACCESS_FAST(addrs, access, width) +# define GDBSTUB_MEM_ACCESS(addr, access, width) +# define GDBSTUB_MEM_ACCESS_FAST(addrs, access, width) -#define gdbstub_step 0 -#define gdbstub_next_asap 0 +# define gdbstub_step 0 +# define gdbstub_next_asap 0 -#define gdbstub_cpu_init() -#define gdbstub_instruction() 0 -#define gdbstub_int3() 0 -#define gdbstub_init() -#define gdbstub_close() +# define gdbstub_cpu_init() +# define gdbstub_instruction() 0 +# define gdbstub_int3() 0 +# define gdbstub_init() +# define gdbstub_close() #endif diff --git a/src/sound/snd_ad1848.c b/src/sound/snd_ad1848.c index 87713712a..bf3858aa3 100644 --- a/src/sound/snd_ad1848.c +++ b/src/sound/snd_ad1848.c @@ -423,7 +423,7 @@ ad1848_update(ad1848_t *ad1848) static int16_t ad1848_process_mulaw(uint8_t byte) { - byte = ~byte; + byte = ~byte; int16_t dec = ((byte & 0x0f) << 3) + 0x84; dec <<= (byte & 0x70) >> 4; return (byte & 0x80) ? (0x84 - dec) : (dec - 0x84); @@ -434,18 +434,18 @@ ad1848_process_alaw(uint8_t byte) { byte ^= 0x55; int16_t dec = (byte & 0x0f) << 4; - int seg = (byte & 0x70) >> 4; + int seg = (byte & 0x70) >> 4; switch (seg) { case 0: - dec += 0x8; + dec |= 0x8; break; case 1: - dec += 0x108; + dec |= 0x108; break; default: - dec += 0x108; + dec |= 0x108; dec <<= seg - 1; break; } @@ -460,7 +460,7 @@ ad1848_process_adpcm(ad1848_t *ad1848) temp = (ad1848->adpcm_data & 0x0f) + ad1848->adpcm_step; } else { ad1848->adpcm_data = dma_channel_read(ad1848->dma); - temp = (ad1848->adpcm_data >> 4) + ad1848->adpcm_step; + temp = (ad1848->adpcm_data >> 4) + ad1848->adpcm_step; } if (temp < 0) temp = 0; @@ -533,7 +533,7 @@ ad1848_poll(void *priv) ad1848->out_r = ad1848_process_alaw(dma_channel_read(ad1848->dma)); break; - /* 0x80 and 0x90 reserved */ + /* 0x80 and 0x90 reserved */ case 0xa0: /* Mono, 4-bit ADPCM */ ad1848->out_l = ad1848->out_r = ad1848_process_adpcm(ad1848); @@ -556,7 +556,7 @@ ad1848_poll(void *priv) ad1848->out_r = dma_channel_read(ad1848->dma) | (temp << 8); break; - /* 0xe0 and 0xf0 reserved */ + /* 0xe0 and 0xf0 reserved */ } if (ad1848->regs[6] & 0x80) @@ -581,7 +581,7 @@ ad1848_poll(void *priv) } if (!(ad1848->adpcm_pos & 7)) /* ADPCM counts down every 4 bytes */ - ad1848->count--; + ad1848->count--; } else { ad1848->out_l = ad1848->out_r = 0; ad1848->cd_vol_l = ad1848->cd_vol_r = 0; diff --git a/src/sound/snd_cmi8x38.c b/src/sound/snd_cmi8x38.c index bcfb7ba21..4d02afcaa 100644 --- a/src/sound/snd_cmi8x38.c +++ b/src/sound/snd_cmi8x38.c @@ -35,15 +35,14 @@ #include <86box/nmi.h> #include <86box/ui.h> - enum { /* [23:16] = reg 0F [7:0] (reg 0C [31:24]) [13] = onboard flag [12:8] = reg 0B [4:0] (reg 08 [28:24]) [7:0] = PCI device ID [7:0] */ - CMEDIA_CMI8338 = 0x000000, + CMEDIA_CMI8338 = 0x000000, CMEDIA_CMI8738_4CH = 0x040011, /* chip version 039 with 4-channel output */ - CMEDIA_CMI8738_6CH = 0x080011 /* chip version 055 with 6-channel output */ + CMEDIA_CMI8738_6CH = 0x080011 /* chip version 055 with 6-channel output */ }; enum { @@ -55,39 +54,38 @@ enum { }; typedef struct { - uint8_t id, reg, always_run, playback_enabled, channels; + uint8_t id, reg, always_run, playback_enabled, channels; struct _cmi8x38_ *dev; - uint32_t sample_ptr, fifo_pos, fifo_end; - int32_t frame_count_dma, frame_count_fragment, sample_count_out; - uint8_t fifo[256], restart; + uint32_t sample_ptr, fifo_pos, fifo_end; + int32_t frame_count_dma, frame_count_fragment, sample_count_out; + uint8_t fifo[256], restart; - int16_t out_fl, out_fr, out_rl, out_rr, out_c, out_lfe; - int vol_l, vol_r, pos; - int32_t buffer[SOUNDBUFLEN * 2]; - uint64_t timer_latch; - double dma_latch; + int16_t out_fl, out_fr, out_rl, out_rr, out_c, out_lfe; + int vol_l, vol_r, pos; + int32_t buffer[SOUNDBUFLEN * 2]; + uint64_t timer_latch; + double dma_latch; - pc_timer_t dma_timer, poll_timer; + pc_timer_t dma_timer, poll_timer; } cmi8x38_dma_t; typedef struct _cmi8x38_ { - uint32_t type; - uint16_t io_base, sb_base, opl_base, mpu_base; - uint8_t pci_regs[256], io_regs[256]; - int slot; + uint32_t type; + uint16_t io_base, sb_base, opl_base, mpu_base; + uint8_t pci_regs[256], io_regs[256]; + int slot; - sb_t *sb; - void *gameport, *io_traps[TRAP_MAX]; + sb_t *sb; + void *gameport, *io_traps[TRAP_MAX]; cmi8x38_dma_t dma[2]; - uint16_t tdma_base_addr, tdma_base_count; - int tdma_8, tdma_16, tdma_mask, prev_mask; + uint16_t tdma_base_addr, tdma_base_count; + int tdma_8, tdma_16, tdma_mask, prev_mask; - int master_vol_l, master_vol_r, cd_vol_l, cd_vol_r; + int master_vol_l, master_vol_r, cd_vol_l, cd_vol_r; } cmi8x38_t; - #ifdef ENABLE_CMI8X38_LOG int cmi8x38_do_log = ENABLE_CMI8X38_LOG; @@ -97,50 +95,46 @@ cmi8x38_log(const char *fmt, ...) va_list ap; if (cmi8x38_do_log) { - va_start(ap, fmt); - pclog_ex(fmt, ap); - va_end(ap); + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); } } #else -#define cmi8x38_log(fmt, ...) +# define cmi8x38_log(fmt, ...) #endif -static const double freqs[] = {5512.0, 11025.0, 22050.0, 44100.0, 8000.0, 16000.0, 32000.0, 48000.0}; -static const uint16_t opl_ports_cmi8738[] = {0x388, 0x3c8, 0x3e0, 0x3e8}; - - -static void cmi8x38_dma_process(void *priv); -static void cmi8x38_speed_changed(void *priv); +static const double freqs[] = { 5512.0, 11025.0, 22050.0, 44100.0, 8000.0, 16000.0, 32000.0, 48000.0 }; +static const uint16_t opl_ports_cmi8738[] = { 0x388, 0x3c8, 0x3e0, 0x3e8 }; +static void cmi8x38_dma_process(void *priv); +static void cmi8x38_speed_changed(void *priv); static void cmi8x38_update_irqs(cmi8x38_t *dev) { /* Calculate and use the INTR flag. */ if (*((uint32_t *) &dev->io_regs[0x10]) & 0x0401c003) { - dev->io_regs[0x13] |= 0x80; - pci_set_irq(dev->slot, PCI_INTA); - cmi8x38_log("CMI8x38: Raising IRQ\n"); + dev->io_regs[0x13] |= 0x80; + pci_set_irq(dev->slot, PCI_INTA); + cmi8x38_log("CMI8x38: Raising IRQ\n"); } else { - dev->io_regs[0x13] &= ~0x80; - pci_clear_irq(dev->slot, PCI_INTA); + dev->io_regs[0x13] &= ~0x80; + pci_clear_irq(dev->slot, PCI_INTA); } } - static void cmi8x38_mpu_irq_update(void *priv, int set) { cmi8x38_t *dev = (cmi8x38_t *) priv; if (set) - dev->io_regs[0x12] |= 0x01; + dev->io_regs[0x12] |= 0x01; else - dev->io_regs[0x12] &= ~0x01; + dev->io_regs[0x12] &= ~0x01; cmi8x38_update_irqs(dev); } - static int cmi8x38_mpu_irq_pending(void *priv) { @@ -148,7 +142,6 @@ cmi8x38_mpu_irq_pending(void *priv) return dev->io_regs[0x12] & 0x01; } - static void cmi8x38_sb_irq_update(void *priv, int set) { @@ -156,13 +149,12 @@ cmi8x38_sb_irq_update(void *priv, int set) /* Interrupt flag shared with the first DMA channel. Combining SB and PCI DMA is undefined; the VxD driver disables SB if PCI is in use. */ if (set) - dev->io_regs[0x10] |= 0x01; + dev->io_regs[0x10] |= 0x01; else - dev->io_regs[0x10] &= ~0x01; + dev->io_regs[0x10] &= ~0x01; cmi8x38_update_irqs(dev); } - static int cmi8x38_sb_dma_post(cmi8x38_t *dev, uint16_t *addr, uint16_t *count, int channel) { @@ -173,43 +165,43 @@ cmi8x38_sb_dma_post(cmi8x38_t *dev, uint16_t *addr, uint16_t *count, int channel /* Copy TDMA registers to DMA on CMI8738+. Everything so far suggests that those chips use PCI bus mastering to directly write to the DMA registers. */ #if 0 /* TSRs don't set ENWR8237, except for the patched C3DPCI - does that bit have no effect? */ - if ((dev->type != CMEDIA_CMI8338) && (dev->io_regs[0x17] & 0x10)) { + if ((dev->type != CMEDIA_CMI8338) && (dev->io_regs[0x17] & 0x10)) #else - if (dev->type != CMEDIA_CMI8338) { + if (dev->type != CMEDIA_CMI8338) #endif - if (channel & 4) - dma[channel].ab = (dma[channel].ab & 0xfffe0000) | ((*addr) << 1); - else - dma[channel].ab = (dma[channel].ab & 0xffff0000) | *addr; - dma[channel].ac = dma[channel].ab; - dma[channel].cc = dma[channel].cb = *count; + { + if (channel & 4) + dma[channel].ab = (dma[channel].ab & 0xfffe0000) | ((*addr) << 1); + else + dma[channel].ab = (dma[channel].ab & 0xffff0000) | *addr; + dma[channel].ac = dma[channel].ab; + dma[channel].cc = dma[channel].cb = *count; } /* Handle end of DMA. */ if (*count == 0xffff) { - if (dma[channel].mode & 0x10) { /* auto-init */ - /* Restart TDMA. */ - *addr = dev->tdma_base_addr; - *count = dev->tdma_base_count; - cmi8x38_log("CMI8x38: Restarting TDMA on DMA %d with addr %08X count %04X\n", - channel, - (channel & 4) ? ((dma[channel].ab & 0xfffe0000) | ((*addr) << 1)) : ((dma[channel].ab & 0xffff0000) | *addr), - *count); - } else { - /* Mask TDMA. */ - dev->tdma_mask |= 1 << channel; - } + if (dma[channel].mode & 0x10) { /* auto-init */ + /* Restart TDMA. */ + *addr = dev->tdma_base_addr; + *count = dev->tdma_base_count; + cmi8x38_log("CMI8x38: Restarting TDMA on DMA %d with addr %08X count %04X\n", + channel, + (channel & 4) ? ((dma[channel].ab & 0xfffe0000) | ((*addr) << 1)) : ((dma[channel].ab & 0xffff0000) | *addr), + *count); + } else { + /* Mask TDMA. */ + dev->tdma_mask |= 1 << channel; + } - /* Set the mysterious LHBTOG bit, assuming it corresponds to - the 8237 channel status bit. Nothing appears to read it. */ - dev->io_regs[0x10] |= 0x40; + /* Set the mysterious LHBTOG bit, assuming it corresponds to + the 8237 channel status bit. Nothing appears to read it. */ + dev->io_regs[0x10] |= 0x40; - return DMA_OVER; + return DMA_OVER; } return 0; } - static int cmi8x38_sb_dma_readb(void *priv) { @@ -218,10 +210,10 @@ cmi8x38_sb_dma_readb(void *priv) /* Stop if the DMA channel is invalid or if TDMA is masked. */ int channel = dev->tdma_8; if ((channel < 0) || (dev->tdma_mask & (1 << channel))) - return DMA_NODATA; + return DMA_NODATA; /* Get 16-bit address and count registers. */ - uint16_t *addr = (uint16_t *) &dev->io_regs[0x1c], + uint16_t *addr = (uint16_t *) &dev->io_regs[0x1c], *count = (uint16_t *) &dev->io_regs[0x1e]; /* Read data. */ @@ -233,7 +225,6 @@ cmi8x38_sb_dma_readb(void *priv) return ret; } - static int cmi8x38_sb_dma_readw(void *priv) { @@ -242,10 +233,10 @@ cmi8x38_sb_dma_readw(void *priv) /* Stop if the DMA channel is invalid or if TDMA is masked. */ int channel = dev->tdma_16; if ((channel < 0) || (dev->tdma_mask & (1 << channel))) - return DMA_NODATA; + return DMA_NODATA; /* Get 16-bit address and count registers. */ - uint16_t *addr = (uint16_t *) &dev->io_regs[0x1c], + uint16_t *addr = (uint16_t *) &dev->io_regs[0x1c], *count = (uint16_t *) &dev->io_regs[0x1e]; /* Read data. */ @@ -257,7 +248,6 @@ cmi8x38_sb_dma_readw(void *priv) return ret; } - static int cmi8x38_sb_dma_writeb(void *priv, uint8_t val) { @@ -266,10 +256,10 @@ cmi8x38_sb_dma_writeb(void *priv, uint8_t val) /* Stop if the DMA channel is invalid or if TDMA is masked. */ int channel = dev->tdma_8; if ((channel < 0) || (dev->tdma_mask & (1 << channel))) - return 1; + return 1; /* Get 16-bit address and count registers. */ - uint16_t *addr = (uint16_t *) &dev->io_regs[0x1c], + uint16_t *addr = (uint16_t *) &dev->io_regs[0x1c], *count = (uint16_t *) &dev->io_regs[0x1e]; /* Write data. */ @@ -281,7 +271,6 @@ cmi8x38_sb_dma_writeb(void *priv, uint8_t val) return 0; } - static int cmi8x38_sb_dma_writew(void *priv, uint16_t val) { @@ -290,10 +279,10 @@ cmi8x38_sb_dma_writew(void *priv, uint16_t val) /* Stop if the DMA channel is invalid or if TDMA is masked. */ int channel = dev->tdma_16; if ((channel < 0) || (dev->tdma_mask & (1 << channel))) - return 1; + return 1; /* Get 16-bit address and count registers. */ - uint16_t *addr = (uint16_t *) &dev->io_regs[0x1c], + uint16_t *addr = (uint16_t *) &dev->io_regs[0x1c], *count = (uint16_t *) &dev->io_regs[0x1e]; /* Write data. */ @@ -305,7 +294,6 @@ cmi8x38_sb_dma_writew(void *priv, uint16_t val) return 0; } - static void cmi8x38_dma_write(uint16_t addr, uint8_t val, void *priv) { @@ -315,21 +303,21 @@ cmi8x38_dma_write(uint16_t addr, uint8_t val, void *priv) which disables auto-detection while copying the TDMA address/count to the SB DMA channel, so that those writes don't loop back to the DMA register snoop mechanism implemented here. */ if (!(dev->io_regs[0x27] & 0x01)) - return; + return; /* Stop if this is not a TDMA channel. Also set or clear the high channel flag while we're here. */ int channel; if (addr < 0x08) { - channel = addr >> 1; - if (channel != dev->tdma_8) - return; - dev->io_regs[0x10] &= ~0x20; + channel = addr >> 1; + if (channel != dev->tdma_8) + return; + dev->io_regs[0x10] &= ~0x20; } else { - channel = 4 | ((addr >> 2) & 3); - if (channel != dev->tdma_16) - return; - dev->io_regs[0x10] |= 0x20; + channel = 4 | ((addr >> 2) & 3); + if (channel != dev->tdma_16) + return; + dev->io_regs[0x10] |= 0x20; } /* Write base address and count. */ @@ -354,17 +342,16 @@ cmi8x38_dma_mask_write(uint16_t addr, uint8_t val, void *priv) /* See comment on dma_write above. */ if (!(dev->io_regs[0x27] & 0x01)) - return; + return; /* Unmask TDMA on DMA unmasking edge. */ if ((dev->tdma_8 >= 0) && (dev->prev_mask & (1 << dev->tdma_8)) && !(dma_m & (1 << dev->tdma_8))) - dev->tdma_mask &= ~(1 << dev->tdma_8); + dev->tdma_mask &= ~(1 << dev->tdma_8); else if ((dev->tdma_16 >= 0) && (dev->prev_mask & (1 << dev->tdma_16)) && !(dma_m & (1 << dev->tdma_16))) - dev->tdma_mask &= ~(1 << dev->tdma_16); + dev->tdma_mask &= ~(1 << dev->tdma_16); dev->prev_mask = dma_m; } - static void cmi8338_io_trap(int size, uint16_t addr, uint8_t write, uint8_t val, void *priv) { @@ -372,9 +359,9 @@ cmi8338_io_trap(int size, uint16_t addr, uint8_t write, uint8_t val, void *priv) #ifdef ENABLE_CMI8X38_LOG if (write) - cmi8x38_log("CMI8x38: cmi8338_io_trap(%04X, %02X)\n", addr, val); + cmi8x38_log("CMI8x38: cmi8338_io_trap(%04X, %02X)\n", addr, val); else - cmi8x38_log("CMI8x38: cmi8338_io_trap(%04X)\n", addr); + cmi8x38_log("CMI8x38: cmi8338_io_trap(%04X)\n", addr); #endif /* Weird offsets, it's best to just treat the register as a big dword. */ @@ -382,175 +369,169 @@ cmi8338_io_trap(int size, uint16_t addr, uint8_t write, uint8_t val, void *priv) *lcs &= ~0x0003dff0; *lcs |= (addr & 0x0f) << 14; if (write) - *lcs |= 0x1000 | (val << 4); + *lcs |= 0x1000 | (val << 4); /* Raise NMI. */ nmi = 1; } - static uint8_t cmi8x38_sb_mixer_read(uint16_t addr, void *priv) { - cmi8x38_t *dev = (cmi8x38_t *) priv; + cmi8x38_t *dev = (cmi8x38_t *) priv; sb_ct1745_mixer_t *mixer = &dev->sb->mixer_sb16; - uint8_t ret = sb_ct1745_mixer_read(addr, dev->sb); + uint8_t ret = sb_ct1745_mixer_read(addr, dev->sb); if (addr & 1) { - if ((mixer->index == 0x0e) || (mixer->index >= 0xf0)) - ret = mixer->regs[mixer->index]; - cmi8x38_log("CMI8x38: sb_mixer_read(1, %02X) = %02X\n", mixer->index, ret); + if ((mixer->index == 0x0e) || (mixer->index >= 0xf0)) + ret = mixer->regs[mixer->index]; + cmi8x38_log("CMI8x38: sb_mixer_read(1, %02X) = %02X\n", mixer->index, ret); } else { - cmi8x38_log("CMI8x38: sb_mixer_read(0) = %02X\n", ret); + cmi8x38_log("CMI8x38: sb_mixer_read(0) = %02X\n", ret); } return ret; } - static void cmi8x38_sb_mixer_write(uint16_t addr, uint8_t val, void *priv) { - cmi8x38_t *dev = (cmi8x38_t *) priv; + cmi8x38_t *dev = (cmi8x38_t *) priv; sb_ct1745_mixer_t *mixer = &dev->sb->mixer_sb16; /* Our clone mixer has a few differences. */ if (addr & 1) { - cmi8x38_log("CMI8x38: sb_mixer_write(1, %02X, %02X)\n", mixer->index, val); + cmi8x38_log("CMI8x38: sb_mixer_write(1, %02X, %02X)\n", mixer->index, val); - switch (mixer->index) { - /* Reset interleaved stereo flag for SBPro mode. */ - case 0x00: - mixer->regs[0x0e] = 0x00; - break; + switch (mixer->index) { + /* Reset interleaved stereo flag for SBPro mode. */ + case 0x00: + mixer->regs[0x0e] = 0x00; + break; - /* No dynamic MPU port assignment. */ - case 0x84: - return; + /* No dynamic MPU port assignment. */ + case 0x84: + return; - /* Some extended registers beyond those accepted by the CT1745. */ - case 0xf0: - if (dev->type == CMEDIA_CMI8338) - val &= 0xfe; - mixer->regs[mixer->index] = val; - return; + /* Some extended registers beyond those accepted by the CT1745. */ + case 0xf0: + if (dev->type == CMEDIA_CMI8338) + val &= 0xfe; + mixer->regs[mixer->index] = val; + return; - case 0xf8 ... 0xff: - if (dev->type == CMEDIA_CMI8338) - mixer->regs[mixer->index] = val; - /* fall-through */ + case 0xf8 ... 0xff: + if (dev->type == CMEDIA_CMI8338) + mixer->regs[mixer->index] = val; + /* fall-through */ - case 0xf1 ... 0xf7: - return; - } + case 0xf1 ... 0xf7: + return; + } - sb_ct1745_mixer_write(addr, val, dev->sb); + sb_ct1745_mixer_write(addr, val, dev->sb); - /* No [3F:47] controls. */ - mixer->input_gain_L = 0; - mixer->input_gain_R = 0; - mixer->output_gain_L = (double) 1.0; - mixer->output_gain_R = (double) 1.0; - mixer->bass_l = 8; - mixer->bass_r = 8; - mixer->treble_l = 8; - mixer->treble_r = 8; + /* No [3F:47] controls. */ + mixer->input_gain_L = 0; + mixer->input_gain_R = 0; + mixer->output_gain_L = (double) 1.0; + mixer->output_gain_R = (double) 1.0; + mixer->bass_l = 8; + mixer->bass_r = 8; + mixer->treble_l = 8; + mixer->treble_r = 8; - /* Check interleaved stereo flag for SBPro mode. */ - if ((mixer->index == 0x00) || (mixer->index == 0x0e)) - sb_dsp_set_stereo(&dev->sb->dsp, mixer->regs[0x0e] & 2); + /* Check interleaved stereo flag for SBPro mode. */ + if ((mixer->index == 0x00) || (mixer->index == 0x0e)) + sb_dsp_set_stereo(&dev->sb->dsp, mixer->regs[0x0e] & 2); - /* Set TDMA channels if autodetection is enabled. */ - if ((dev->io_regs[0x27] & 0x01) && (mixer->index == 0x81)) { - dev->tdma_8 = dev->sb->dsp.sb_8_dmanum; - if (dev->sb->dsp.sb_type >= SB16) - dev->tdma_16 = dev->sb->dsp.sb_16_dmanum; - } + /* Set TDMA channels if autodetection is enabled. */ + if ((dev->io_regs[0x27] & 0x01) && (mixer->index == 0x81)) { + dev->tdma_8 = dev->sb->dsp.sb_8_dmanum; + if (dev->sb->dsp.sb_type >= SB16) + dev->tdma_16 = dev->sb->dsp.sb_16_dmanum; + } } else { - cmi8x38_log("CMI8x38: sb_mixer_write(0, %02X)\n", val); - sb_ct1745_mixer_write(addr, val, dev->sb); + cmi8x38_log("CMI8x38: sb_mixer_write(0, %02X)\n", val); + sb_ct1745_mixer_write(addr, val, dev->sb); } } - static void cmi8x38_remap_sb(cmi8x38_t *dev) { if (dev->sb_base) { - io_removehandler(dev->sb_base, 0x0004, opl3_read, NULL, NULL, - opl3_write, NULL, NULL, &dev->sb->opl); - io_removehandler(dev->sb_base + 8, 0x0002, opl3_read, NULL, NULL, - opl3_write, NULL, NULL, &dev->sb->opl); - io_removehandler(dev->sb_base + 4, 0x0002, cmi8x38_sb_mixer_read, NULL, NULL, - cmi8x38_sb_mixer_write, NULL, NULL, dev); + io_removehandler(dev->sb_base, 0x0004, opl3_read, NULL, NULL, + opl3_write, NULL, NULL, &dev->sb->opl); + io_removehandler(dev->sb_base + 8, 0x0002, opl3_read, NULL, NULL, + opl3_write, NULL, NULL, &dev->sb->opl); + io_removehandler(dev->sb_base + 4, 0x0002, cmi8x38_sb_mixer_read, NULL, NULL, + cmi8x38_sb_mixer_write, NULL, NULL, dev); - sb_dsp_setaddr(&dev->sb->dsp, 0); + sb_dsp_setaddr(&dev->sb->dsp, 0); } dev->sb_base = 0x220; if (dev->type == CMEDIA_CMI8338) - dev->sb_base += (dev->io_regs[0x17] & 0x80) >> 2; + dev->sb_base += (dev->io_regs[0x17] & 0x80) >> 2; else - dev->sb_base += (dev->io_regs[0x17] & 0x0c) << 3; + dev->sb_base += (dev->io_regs[0x17] & 0x0c) << 3; if (!(dev->io_regs[0x04] & 0x08)) - dev->sb_base = 0; + dev->sb_base = 0; cmi8x38_log("CMI8x38: remap_sb(%04X)\n", dev->sb_base); if (dev->sb_base) { - io_sethandler(dev->sb_base, 0x0004, opl3_read, NULL, NULL, - opl3_write, NULL, NULL, &dev->sb->opl); - io_sethandler(dev->sb_base + 8, 0x0002, opl3_read, NULL, NULL, - opl3_write, NULL, NULL, &dev->sb->opl); - io_sethandler(dev->sb_base + 4, 0x0002, cmi8x38_sb_mixer_read, NULL, NULL, - cmi8x38_sb_mixer_write, NULL, NULL, dev); + io_sethandler(dev->sb_base, 0x0004, opl3_read, NULL, NULL, + opl3_write, NULL, NULL, &dev->sb->opl); + io_sethandler(dev->sb_base + 8, 0x0002, opl3_read, NULL, NULL, + opl3_write, NULL, NULL, &dev->sb->opl); + io_sethandler(dev->sb_base + 4, 0x0002, cmi8x38_sb_mixer_read, NULL, NULL, + cmi8x38_sb_mixer_write, NULL, NULL, dev); - sb_dsp_setaddr(&dev->sb->dsp, dev->sb_base); + sb_dsp_setaddr(&dev->sb->dsp, dev->sb_base); } } - static void cmi8x38_remap_opl(cmi8x38_t *dev) { if (dev->opl_base) { - io_removehandler(dev->opl_base, 0x0004, opl3_read, NULL, NULL, - opl3_write, NULL, NULL, &dev->sb->opl); + io_removehandler(dev->opl_base, 0x0004, opl3_read, NULL, NULL, + opl3_write, NULL, NULL, &dev->sb->opl); } dev->opl_base = (dev->type == CMEDIA_CMI8338) ? 0x388 : opl_ports_cmi8738[dev->io_regs[0x17] & 0x03]; io_trap_remap(dev->io_traps[TRAP_OPL], (dev->io_regs[0x04] & 0x01) && (dev->io_regs[0x16] & 0x80), dev->opl_base, 4); if (!(dev->io_regs[0x1a] & 0x08)) - dev->opl_base = 0; + dev->opl_base = 0; cmi8x38_log("CMI8x38: remap_opl(%04X)\n", dev->opl_base); if (dev->opl_base) { - io_sethandler(dev->opl_base, 0x0004, opl3_read, NULL, NULL, - opl3_write, NULL, NULL, &dev->sb->opl); + io_sethandler(dev->opl_base, 0x0004, opl3_read, NULL, NULL, + opl3_write, NULL, NULL, &dev->sb->opl); } } - static void cmi8x38_remap_mpu(cmi8x38_t *dev) { if (dev->mpu_base) - mpu401_change_addr(dev->sb->mpu, 0); + mpu401_change_addr(dev->sb->mpu, 0); /* The CMI8338 datasheet's port range of [300:330] is inaccurate. Drivers expect [330:300] like CMI8738. */ dev->mpu_base = 0x330 - ((dev->io_regs[0x17] & 0x60) >> 1); io_trap_remap(dev->io_traps[TRAP_MPU], (dev->io_regs[0x04] & 0x01) && (dev->io_regs[0x16] & 0x20), dev->mpu_base, 2); if (!(dev->io_regs[0x04] & 0x04)) - dev->mpu_base = 0; + dev->mpu_base = 0; cmi8x38_log("CMI8x38: remap_mpu(%04X)\n", dev->mpu_base); if (dev->mpu_base) - mpu401_change_addr(dev->sb->mpu, dev->mpu_base); + mpu401_change_addr(dev->sb->mpu, dev->mpu_base); } - static void cmi8x38_remap_traps(cmi8x38_t *dev) { @@ -560,7 +541,6 @@ cmi8x38_remap_traps(cmi8x38_t *dev) io_trap_remap(dev->io_traps[TRAP_PIC], (dev->io_regs[0x04] & 0x01) && (dev->io_regs[0x17] & 0x01), 0x0020, 2); } - static void cmi8x38_start_playback(cmi8x38_t *dev) { @@ -568,16 +548,15 @@ cmi8x38_start_playback(cmi8x38_t *dev) i = !(val & 0x01); if (!dev->dma[0].playback_enabled && i) - timer_advance_u64(&dev->dma[0].poll_timer, dev->dma[0].timer_latch); + timer_advance_u64(&dev->dma[0].poll_timer, dev->dma[0].timer_latch); dev->dma[0].playback_enabled = i; i = !(val & 0x02); if (!dev->dma[1].playback_enabled && i) - timer_advance_u64(&dev->dma[1].poll_timer, dev->dma[1].timer_latch); + timer_advance_u64(&dev->dma[1].poll_timer, dev->dma[1].timer_latch); dev->dma[1].playback_enabled = i; } - static uint8_t cmi8x38_read(uint16_t addr, void *priv) { @@ -586,66 +565,75 @@ cmi8x38_read(uint16_t addr, void *priv) uint8_t ret; switch (addr) { - case 0x22: case 0x23: - ret = cmi8x38_sb_mixer_read(addr ^ 1, dev); - break; + case 0x22: + case 0x23: + ret = cmi8x38_sb_mixer_read(addr ^ 1, dev); + break; - case 0x40 ... 0x4f: - if (dev->type == CMEDIA_CMI8338) - goto io_reg; - else - ret = mpu401_read(addr, dev->sb->mpu); - break; + case 0x40 ... 0x4f: + if (dev->type == CMEDIA_CMI8338) + goto io_reg; + else + ret = mpu401_read(addr, dev->sb->mpu); + break; - case 0x50 ... 0x5f: - if (dev->type == CMEDIA_CMI8338) - goto io_reg; - else - ret = opl3_read(addr, &dev->sb->opl); - break; + case 0x50 ... 0x5f: + if (dev->type == CMEDIA_CMI8338) + goto io_reg; + else + ret = opl3_read(addr, &dev->sb->opl); + break; - case 0x80: case 0x88: - ret = dev->dma[(addr & 0x78) >> 3].sample_ptr; - break; + case 0x80: + case 0x88: + ret = dev->dma[(addr & 0x78) >> 3].sample_ptr; + break; - case 0x81: case 0x89: - ret = dev->dma[(addr & 0x78) >> 3].sample_ptr >> 8; - break; + case 0x81: + case 0x89: + ret = dev->dma[(addr & 0x78) >> 3].sample_ptr >> 8; + break; - case 0x82: case 0x8a: - ret = dev->dma[(addr & 0x78) >> 3].sample_ptr >> 16; - break; + case 0x82: + case 0x8a: + ret = dev->dma[(addr & 0x78) >> 3].sample_ptr >> 16; + break; - case 0x83: case 0x8b: - ret = dev->dma[(addr & 0x78) >> 3].sample_ptr >> 24; - break; + case 0x83: + case 0x8b: + ret = dev->dma[(addr & 0x78) >> 3].sample_ptr >> 24; + break; - case 0x84: case 0x8c: - ret = dev->dma[(addr & 0x78) >> 3].frame_count_dma; - break; + case 0x84: + case 0x8c: + ret = dev->dma[(addr & 0x78) >> 3].frame_count_dma; + break; - case 0x85: case 0x8d: - ret = dev->dma[(addr & 0x78) >> 3].frame_count_dma >> 8; - break; + case 0x85: + case 0x8d: + ret = dev->dma[(addr & 0x78) >> 3].frame_count_dma >> 8; + break; - case 0x86: case 0x8e: - ret = dev->dma[(addr & 0x78) >> 3].sample_count_out >> 2; - break; + case 0x86: + case 0x8e: + ret = dev->dma[(addr & 0x78) >> 3].sample_count_out >> 2; + break; - case 0x87: case 0x8f: - ret = dev->dma[(addr & 0x78) >> 3].sample_count_out >> 10; - break; + case 0x87: + case 0x8f: + ret = dev->dma[(addr & 0x78) >> 3].sample_count_out >> 10; + break; - default: -io_reg: ret = dev->io_regs[addr]; - break; + default: +io_reg: + ret = dev->io_regs[addr]; + break; } - + cmi8x38_log("CMI8x38: read(%02X) = %02X\n", addr, ret); return ret; } - static void cmi8x38_write(uint16_t addr, uint8_t val, void *priv) { @@ -654,358 +642,361 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv) cmi8x38_log("CMI8x38: write(%02X, %02X)\n", addr, val); switch (addr) { - case 0x00: - val &= 0x0f; + case 0x00: + val &= 0x0f; - /* Don't care about recording DMA. */ - dev->dma[0].always_run = val & 0x01; - dev->dma[1].always_run = val & 0x02; + /* Don't care about recording DMA. */ + dev->dma[0].always_run = val & 0x01; + dev->dma[1].always_run = val & 0x02; - /* Start playback if requested. */ - dev->io_regs[addr] = val; - cmi8x38_start_playback(dev); - break; + /* Start playback if requested. */ + dev->io_regs[addr] = val; + cmi8x38_start_playback(dev); + break; - case 0x02: - /* Reset DMA channels if requested. */ - if (val & 0x04) { - val &= ~0x01; + case 0x02: + /* Reset DMA channels if requested. */ + if (val & 0x04) { + val &= ~0x01; - if (dev->sb->dsp.sb_8_enable || dev->sb->dsp.sb_16_enable || dev->sb->dsp.sb_irq8 || dev->sb->dsp.sb_irq16) { - dev->sb->dsp.sb_8_enable = dev->sb->dsp.sb_16_enable = dev->sb->dsp.sb_irq8 = dev->sb->dsp.sb_irq16 = 0; - cmi8x38_update_irqs(dev); - } - } - if (val & 0x08) - val &= ~0x02; + if (dev->sb->dsp.sb_8_enable || dev->sb->dsp.sb_16_enable || dev->sb->dsp.sb_irq8 || dev->sb->dsp.sb_irq16) { + dev->sb->dsp.sb_8_enable = dev->sb->dsp.sb_16_enable = dev->sb->dsp.sb_irq8 = dev->sb->dsp.sb_irq16 = 0; + cmi8x38_update_irqs(dev); + } + } + if (val & 0x08) + val &= ~0x02; - val &= 0x03; - dev->io_regs[addr] = val; + val &= 0x03; + dev->io_regs[addr] = val; - /* Start DMA channels if requested. */ - if (val & 0x01) { - cmi8x38_log("CMI8x38: DMA 0 trigger\n"); - dev->dma[0].restart = 1; - cmi8x38_dma_process(&dev->dma[0]); - } - if (val & 0x02) { - cmi8x38_log("CMI8x38: DMA 1 trigger\n"); - dev->dma[1].restart = 1; - cmi8x38_dma_process(&dev->dma[1]); - } + /* Start DMA channels if requested. */ + if (val & 0x01) { + cmi8x38_log("CMI8x38: DMA 0 trigger\n"); + dev->dma[0].restart = 1; + cmi8x38_dma_process(&dev->dma[0]); + } + if (val & 0x02) { + cmi8x38_log("CMI8x38: DMA 1 trigger\n"); + dev->dma[1].restart = 1; + cmi8x38_dma_process(&dev->dma[1]); + } - /* Start playback along with DMA channels. */ - if (val & 0x03) - cmi8x38_start_playback(dev); - break; + /* Start playback along with DMA channels. */ + if (val & 0x03) + cmi8x38_start_playback(dev); + break; - case 0x04: - /* Enable or disable the game port. */ - gameport_remap(dev->gameport, (val & 0x02) ? 0x200 : 0); + case 0x04: + /* Enable or disable the game port. */ + gameport_remap(dev->gameport, (val & 0x02) ? 0x200 : 0); - /* Enable or disable the legacy devices. */ - dev->io_regs[addr] = val; - cmi8x38_remap_sb(dev); - /* remap_mpu called by remap_traps */ + /* Enable or disable the legacy devices. */ + dev->io_regs[addr] = val; + cmi8x38_remap_sb(dev); + /* remap_mpu called by remap_traps */ - /* Enable or disable I/O traps. */ - cmi8x38_remap_traps(dev); - break; + /* Enable or disable I/O traps. */ + cmi8x38_remap_traps(dev); + break; - case 0x05: - dev->io_regs[addr] = val; - cmi8x38_speed_changed(dev); - break; + case 0x05: + dev->io_regs[addr] = val; + cmi8x38_speed_changed(dev); + break; - case 0x08: - if (dev->type == CMEDIA_CMI8338) - val &= 0x0f; - break; + case 0x08: + if (dev->type == CMEDIA_CMI8338) + val &= 0x0f; + break; - case 0x09: + case 0x09: #if 0 /* actual CMI8338 behavior unconfirmed; this register is required for the Windows XP driver which outputs 96K */ if (dev->type == CMEDIA_CMI8338) return; #endif - /* Update sample rate. */ - dev->io_regs[addr] = val; - cmi8x38_speed_changed(dev); - break; + /* Update sample rate. */ + dev->io_regs[addr] = val; + cmi8x38_speed_changed(dev); + break; - case 0x0a: case 0x0b: - if (dev->type == CMEDIA_CMI8338) - return; - else - val &= 0xe0; + case 0x0a: + case 0x0b: + if (dev->type == CMEDIA_CMI8338) + return; + else + val &= 0xe0; - if (addr == 0x0a) { - /* Set PCI latency timer if requested. */ - dev->pci_regs[0x0d] = (val & 0x80) ? 0x48 : 0x20; /* clearing SETLAT48 is undefined */ - } else { - /* Update channel count. */ - dev->io_regs[addr] = val; - cmi8x38_speed_changed(dev); - } - break; + if (addr == 0x0a) { + /* Set PCI latency timer if requested. */ + dev->pci_regs[0x0d] = (val & 0x80) ? 0x48 : 0x20; /* clearing SETLAT48 is undefined */ + } else { + /* Update channel count. */ + dev->io_regs[addr] = val; + cmi8x38_speed_changed(dev); + } + break; - case 0x0e: - val &= 0x07; + case 0x0e: + val &= 0x07; - /* Clear interrupts. */ - dev->io_regs[0x10] &= val | 0xfc; - if (!(val & 0x04)) - dev->io_regs[0x11] &= ~0xc0; - cmi8x38_update_irqs(dev); - break; + /* Clear interrupts. */ + dev->io_regs[0x10] &= val | 0xfc; + if (!(val & 0x04)) + dev->io_regs[0x11] &= ~0xc0; + cmi8x38_update_irqs(dev); + break; - case 0x15: - if (dev->type == CMEDIA_CMI8338) - return; - else - val &= 0xf0; + case 0x15: + if (dev->type == CMEDIA_CMI8338) + return; + else + val &= 0xf0; - /* Update channel count. */ - dev->io_regs[addr] = val; - cmi8x38_speed_changed(dev); - break; + /* Update channel count. */ + dev->io_regs[addr] = val; + cmi8x38_speed_changed(dev); + break; - case 0x16: - if (dev->type == CMEDIA_CMI8338) { - val &= 0xa0; + case 0x16: + if (dev->type == CMEDIA_CMI8338) { + val &= 0xa0; - /* Enable or disable I/O traps. */ - dev->io_regs[addr] = val; - cmi8x38_remap_traps(dev); - } - break; + /* Enable or disable I/O traps. */ + dev->io_regs[addr] = val; + cmi8x38_remap_traps(dev); + } + break; - case 0x17: - if (dev->type == CMEDIA_CMI8338) { - val &= 0xf3; + case 0x17: + if (dev->type == CMEDIA_CMI8338) { + val &= 0xf3; - /* Force IRQ if requested. Clearing this bit is undefined. */ - if (val & 0x10) - pci_set_irq(dev->slot, PCI_INTA); - else if ((dev->io_regs[0x17] & 0x10) && !(val & 0x10)) - pci_clear_irq(dev->slot, PCI_INTA); + /* Force IRQ if requested. Clearing this bit is undefined. */ + if (val & 0x10) + pci_set_irq(dev->slot, PCI_INTA); + else if ((dev->io_regs[0x17] & 0x10) && !(val & 0x10)) + pci_clear_irq(dev->slot, PCI_INTA); - /* Enable or disable I/O traps. */ - dev->io_regs[addr] = val; - cmi8x38_remap_traps(dev); - } + /* Enable or disable I/O traps. */ + dev->io_regs[addr] = val; + cmi8x38_remap_traps(dev); + } - /* Remap the legacy devices. */ - dev->io_regs[addr] = val; - cmi8x38_remap_sb(dev); - cmi8x38_remap_mpu(dev); - break; + /* Remap the legacy devices. */ + dev->io_regs[addr] = val; + cmi8x38_remap_sb(dev); + cmi8x38_remap_mpu(dev); + break; - case 0x18: - if (dev->type == CMEDIA_CMI8338) - val &= 0x0f; - else - val &= 0xdf; - break; + case 0x18: + if (dev->type == CMEDIA_CMI8338) + val &= 0x0f; + else + val &= 0xdf; + break; - case 0x19: - if (dev->type == CMEDIA_CMI8338) - return; - else - val &= 0xe0; - break; + case 0x19: + if (dev->type == CMEDIA_CMI8338) + return; + else + val &= 0xe0; + break; - case 0x1a: - val &= 0xfd; + case 0x1a: + val &= 0xfd; - /* Enable or disable the OPL. */ - dev->io_regs[addr] = val; - cmi8x38_remap_opl(dev); - break; + /* Enable or disable the OPL. */ + dev->io_regs[addr] = val; + cmi8x38_remap_opl(dev); + break; - case 0x1b: - if (dev->type == CMEDIA_CMI8338) - val &= 0xf0; - else - val &= 0xd7; - break; + case 0x1b: + if (dev->type == CMEDIA_CMI8338) + val &= 0xf0; + else + val &= 0xd7; + break; - case 0x20: - /* ??? */ - break; + case 0x20: + /* ??? */ + break; - case 0x21: - if (dev->type == CMEDIA_CMI8338) - val &= 0xf7; - else - val &= 0x07; + case 0x21: + if (dev->type == CMEDIA_CMI8338) + val &= 0xf7; + else + val &= 0x07; - /* Enable or disable SBPro channel swapping. */ - dev->sb->dsp.sbleftright_default = !!(val & 0x02); + /* Enable or disable SBPro channel swapping. */ + dev->sb->dsp.sbleftright_default = !!(val & 0x02); - /* Enable or disable SB16 mode. */ - dev->sb->dsp.sb_type = (val & 0x01) ? SBPRO2 : SB16; - break; + /* Enable or disable SB16 mode. */ + dev->sb->dsp.sb_type = (val & 0x01) ? SBPRO2 : SB16; + break; - case 0x22: case 0x23: - cmi8x38_sb_mixer_write(addr ^ 1, val, dev); - return; + case 0x22: + case 0x23: + cmi8x38_sb_mixer_write(addr ^ 1, val, dev); + return; - case 0x24: - if (dev->type == CMEDIA_CMI8338) - val &= 0xcf; - break; + case 0x24: + if (dev->type == CMEDIA_CMI8338) + val &= 0xcf; + break; - case 0x27: - if (dev->type == CMEDIA_CMI8338) - val &= 0x03; - else - val &= 0x27; - break; + case 0x27: + if (dev->type == CMEDIA_CMI8338) + val &= 0x03; + else + val &= 0x27; + break; - case 0x40 ... 0x4f: - if (dev->type != CMEDIA_CMI8338) - mpu401_write(addr, val, dev->sb->mpu); - return; + case 0x40 ... 0x4f: + if (dev->type != CMEDIA_CMI8338) + mpu401_write(addr, val, dev->sb->mpu); + return; - case 0x50 ... 0x5f: - if (dev->type != CMEDIA_CMI8338) - opl3_write(addr, val, &dev->sb->opl); - return; + case 0x50 ... 0x5f: + if (dev->type != CMEDIA_CMI8338) + opl3_write(addr, val, &dev->sb->opl); + return; - case 0x92: - if (dev->type == CMEDIA_CMI8338) - return; - else - val &= 0x1f; - break; + case 0x92: + if (dev->type == CMEDIA_CMI8338) + return; + else + val &= 0x1f; + break; - case 0x93: - if (dev->type == CMEDIA_CMI8338) - return; - else - val &= 0x10; - break; + case 0x93: + if (dev->type == CMEDIA_CMI8338) + return; + else + val &= 0x10; + break; - case 0x25: case 0x26: - case 0x70: case 0x71: - case 0x80 ... 0x8f: - break; + case 0x25: + case 0x26: + case 0x70: + case 0x71: + case 0x80 ... 0x8f: + break; - default: - return; + default: + return; } dev->io_regs[addr] = val; } - static void cmi8x38_remap(cmi8x38_t *dev) { if (dev->io_base) - io_removehandler(dev->io_base, 256, cmi8x38_read, NULL, NULL, cmi8x38_write, NULL, NULL, dev); + io_removehandler(dev->io_base, 256, cmi8x38_read, NULL, NULL, cmi8x38_write, NULL, NULL, dev); dev->io_base = (dev->pci_regs[0x04] & 0x01) ? (dev->pci_regs[0x11] << 8) : 0; cmi8x38_log("CMI8x38: remap(%04X)\n", dev->io_base); if (dev->io_base) - io_sethandler(dev->io_base, 256, cmi8x38_read, NULL, NULL, cmi8x38_write, NULL, NULL, dev); + io_sethandler(dev->io_base, 256, cmi8x38_read, NULL, NULL, cmi8x38_write, NULL, NULL, dev); } - static uint8_t cmi8x38_pci_read(int func, int addr, void *priv) { cmi8x38_t *dev = (cmi8x38_t *) priv; - uint8_t ret = 0xff; + uint8_t ret = 0xff; if (!func) { - ret = dev->pci_regs[addr]; - cmi8x38_log("CMI8x38: pci_read(%02X) = %02X\n", addr, ret); + ret = dev->pci_regs[addr]; + cmi8x38_log("CMI8x38: pci_read(%02X) = %02X\n", addr, ret); } return ret; } - static void cmi8x38_pci_write(int func, int addr, uint8_t val, void *priv) { cmi8x38_t *dev = (cmi8x38_t *) priv; if (func) - return; + return; cmi8x38_log("CMI8x38: pci_write(%02X, %02X)\n", addr, val); switch (addr) { - case 0x04: - val &= 0x05; + case 0x04: + val &= 0x05; - /* Enable or disable the I/O BAR. */ - dev->pci_regs[addr] = val; - cmi8x38_remap(dev); - break; + /* Enable or disable the I/O BAR. */ + dev->pci_regs[addr] = val; + cmi8x38_remap(dev); + break; - case 0x05: - val &= 0x01; - break; + case 0x05: + val &= 0x01; + break; - case 0x11: - /* Remap the I/O BAR. */ - dev->pci_regs[addr] = val; - cmi8x38_remap(dev); - break; + case 0x11: + /* Remap the I/O BAR. */ + dev->pci_regs[addr] = val; + cmi8x38_remap(dev); + break; - case 0x2c: case 0x2d: case 0x2e: case 0x2f: - if (!(dev->io_regs[0x1a] & 0x01)) - return; - break; + case 0x2c: + case 0x2d: + case 0x2e: + case 0x2f: + if (!(dev->io_regs[0x1a] & 0x01)) + return; + break; - case 0x40: - if (dev->type == CMEDIA_CMI8338) - val &= 0x0f; - else - return; - break; + case 0x40: + if (dev->type == CMEDIA_CMI8338) + val &= 0x0f; + else + return; + break; - case 0x0c: case 0x0d: - case 0x3c: - break; + case 0x0c: + case 0x0d: + case 0x3c: + break; - default: - return; + default: + return; } dev->pci_regs[addr] = val; } - static void cmi8x38_update(cmi8x38_t *dev, cmi8x38_dma_t *dma) { sb_ct1745_mixer_t *mixer = &dev->sb->mixer_sb16; - int32_t l = (dma->out_fl * mixer->voice_l) * mixer->master_l, - r = (dma->out_fr * mixer->voice_r) * mixer->master_r; + int32_t l = (dma->out_fl * mixer->voice_l) * mixer->master_l, + r = (dma->out_fr * mixer->voice_r) * mixer->master_r; for (; dma->pos < sound_pos_global; dma->pos++) { - dma->buffer[dma->pos*2] = l; - dma->buffer[dma->pos*2 + 1] = r; + dma->buffer[dma->pos * 2] = l; + dma->buffer[dma->pos * 2 + 1] = r; } } - static void cmi8x38_dma_process(void *priv) { cmi8x38_dma_t *dma = (cmi8x38_dma_t *) priv; - cmi8x38_t *dev = dma->dev; + cmi8x38_t *dev = dma->dev; /* Stop if this DMA channel is not active. */ uint8_t dma_bit = 0x01 << dma->id; if (!(dev->io_regs[0x02] & dma_bit)) { - cmi8x38_log("CMI8x38: Stopping DMA %d due to inactive channel (%02X)\n", dma->id, dev->io_regs[0x02]); - return; + cmi8x38_log("CMI8x38: Stopping DMA %d due to inactive channel (%02X)\n", dma->id, dev->io_regs[0x02]); + return; } /* Schedule next run. */ @@ -1014,185 +1005,184 @@ cmi8x38_dma_process(void *priv) /* Process DMA if it's active, and the FIFO has room or is disabled. */ uint8_t dma_status = dev->io_regs[0x00] >> dma->id; if (!(dma_status & 0x04) && (dma->always_run || ((dma->fifo_end - dma->fifo_pos) <= (sizeof(dma->fifo) - 4)))) { - /* Start DMA if requested. */ - if (dma->restart) { - /* Set up base address and counters. - I have no idea how sample_count_out is supposed to work, - nothing consumes it, so it's implemented as an assumption. */ - dma->restart = 0; - dma->sample_ptr = *((uint32_t *) &dev->io_regs[dma->reg]); - dma->frame_count_dma = dma->sample_count_out = *((uint16_t *) &dev->io_regs[dma->reg | 0x4]) + 1; - dma->frame_count_fragment = *((uint16_t *) &dev->io_regs[dma->reg | 0x6]) + 1; + /* Start DMA if requested. */ + if (dma->restart) { + /* Set up base address and counters. + I have no idea how sample_count_out is supposed to work, + nothing consumes it, so it's implemented as an assumption. */ + dma->restart = 0; + dma->sample_ptr = *((uint32_t *) &dev->io_regs[dma->reg]); + dma->frame_count_dma = dma->sample_count_out = *((uint16_t *) &dev->io_regs[dma->reg | 0x4]) + 1; + dma->frame_count_fragment = *((uint16_t *) &dev->io_regs[dma->reg | 0x6]) + 1; - cmi8x38_log("CMI8x38: Starting DMA %d at %08X (count %04X fragment %04X)\n", dma->id, dma->sample_ptr, dma->frame_count_dma, dma->frame_count_fragment); - } + cmi8x38_log("CMI8x38: Starting DMA %d at %08X (count %04X fragment %04X)\n", dma->id, dma->sample_ptr, dma->frame_count_dma, dma->frame_count_fragment); + } - if (dma_status & 0x01) { - /* Write channel: read data from FIFO. */ - mem_writel_phys(dma->sample_ptr, *((uint32_t *) &dma->fifo[dma->fifo_end & (sizeof(dma->fifo) - 1)])); - } else { - /* Read channel: write data to FIFO. */ - *((uint32_t *) &dma->fifo[dma->fifo_end & (sizeof(dma->fifo) - 1)]) = mem_readl_phys(dma->sample_ptr); - } - dma->fifo_end += 4; - dma->sample_ptr += 4; + if (dma_status & 0x01) { + /* Write channel: read data from FIFO. */ + mem_writel_phys(dma->sample_ptr, *((uint32_t *) &dma->fifo[dma->fifo_end & (sizeof(dma->fifo) - 1)])); + } else { + /* Read channel: write data to FIFO. */ + *((uint32_t *) &dma->fifo[dma->fifo_end & (sizeof(dma->fifo) - 1)]) = mem_readl_phys(dma->sample_ptr); + } + dma->fifo_end += 4; + dma->sample_ptr += 4; - /* Check if the fragment size was reached. */ - if (--dma->frame_count_fragment <= 0) { - cmi8x38_log("CMI8x38: DMA %d fragment size reached at %04X frames left", dma->id, dma->frame_count_dma - 1); + /* Check if the fragment size was reached. */ + if (--dma->frame_count_fragment <= 0) { + cmi8x38_log("CMI8x38: DMA %d fragment size reached at %04X frames left", dma->id, dma->frame_count_dma - 1); - /* Reset fragment counter. */ - dma->frame_count_fragment = *((uint16_t *) &dev->io_regs[dma->reg | 0x6]) + 1; + /* Reset fragment counter. */ + dma->frame_count_fragment = *((uint16_t *) &dev->io_regs[dma->reg | 0x6]) + 1; - /* Fire interrupt if requested. */ - if (dev->io_regs[0x0e] & dma_bit) { - cmi8x38_log(", firing interrupt\n"); + /* Fire interrupt if requested. */ + if (dev->io_regs[0x0e] & dma_bit) { + cmi8x38_log(", firing interrupt\n"); - /* Set channel interrupt flag. */ - dev->io_regs[0x10] |= dma_bit; + /* Set channel interrupt flag. */ + dev->io_regs[0x10] |= dma_bit; - /* Fire interrupt. */ - cmi8x38_update_irqs(dev); - } else { - cmi8x38_log("\n"); - } - } + /* Fire interrupt. */ + cmi8x38_update_irqs(dev); + } else { + cmi8x38_log("\n"); + } + } - /* Check if the buffer's end was reached. */ - if (--dma->frame_count_dma <= 0) { - dma->frame_count_dma = 0; - cmi8x38_log("CMI8x38: DMA %d end reached, restarting\n", dma->id); + /* Check if the buffer's end was reached. */ + if (--dma->frame_count_dma <= 0) { + dma->frame_count_dma = 0; + cmi8x38_log("CMI8x38: DMA %d end reached, restarting\n", dma->id); - /* Restart DMA on the next run. */ - dma->restart = 1; - } + /* Restart DMA on the next run. */ + dma->restart = 1; + } } } - static void cmi8x38_poll(void *priv) { cmi8x38_dma_t *dma = (cmi8x38_dma_t *) priv; - cmi8x38_t *dev = dma->dev; - int16_t *out_l, *out_r, *out_ol, *out_or; /* o = opposite */ + cmi8x38_t *dev = dma->dev; + int16_t *out_l, *out_r, *out_ol, *out_or; /* o = opposite */ /* Schedule next run if playback is enabled. */ if (dev->io_regs[0x00] & (1 << dma->id)) - dma->playback_enabled = 0; + dma->playback_enabled = 0; else - timer_advance_u64(&dma->poll_timer, dma->timer_latch); + timer_advance_u64(&dma->poll_timer, dma->timer_latch); /* Update audio buffer. */ cmi8x38_update(dev, dma); /* Swap stereo pair if this is the rear DMA channel according to ENDBDAC and XCHGDAC. */ if ((dev->io_regs[0x1a] & 0x80) && (!!(dev->io_regs[0x1a] & 0x40) ^ dma->id)) { - out_l = &dma->out_rl; - out_r = &dma->out_rr; - out_ol = &dma->out_fl; - out_or = &dma->out_fr; + out_l = &dma->out_rl; + out_r = &dma->out_rr; + out_ol = &dma->out_fl; + out_or = &dma->out_fr; } else { - out_l = &dma->out_fl; - out_r = &dma->out_fr; - out_ol = &dma->out_rl; - out_or = &dma->out_rr; + out_l = &dma->out_fl; + out_r = &dma->out_fr; + out_ol = &dma->out_rl; + out_or = &dma->out_rr; } *out_ol = *out_or = dma->out_c = dma->out_lfe = 0; /* Feed next sample from the FIFO. */ switch ((dev->io_regs[0x08] >> (dma->id << 1)) & 0x03) { - case 0x00: /* Mono, 8-bit PCM */ - if ((dma->fifo_end - dma->fifo_pos) >= 1) { - *out_l = *out_r = (dma->fifo[dma->fifo_pos++ & (sizeof(dma->fifo) - 1)] ^ 0x80) << 8; - dma->sample_count_out--; - goto n4spk3d; - } - break; + case 0x00: /* Mono, 8-bit PCM */ + if ((dma->fifo_end - dma->fifo_pos) >= 1) { + *out_l = *out_r = (dma->fifo[dma->fifo_pos++ & (sizeof(dma->fifo) - 1)] ^ 0x80) << 8; + dma->sample_count_out--; + goto n4spk3d; + } + break; - case 0x01: /* Stereo, 8-bit PCM */ - if ((dma->fifo_end - dma->fifo_pos) >= 2) { - *out_l = (dma->fifo[dma->fifo_pos++ & (sizeof(dma->fifo) - 1)] ^ 0x80) << 8; - *out_r = (dma->fifo[dma->fifo_pos++ & (sizeof(dma->fifo) - 1)] ^ 0x80) << 8; - dma->sample_count_out -= 2; - goto n4spk3d; - } - break; + case 0x01: /* Stereo, 8-bit PCM */ + if ((dma->fifo_end - dma->fifo_pos) >= 2) { + *out_l = (dma->fifo[dma->fifo_pos++ & (sizeof(dma->fifo) - 1)] ^ 0x80) << 8; + *out_r = (dma->fifo[dma->fifo_pos++ & (sizeof(dma->fifo) - 1)] ^ 0x80) << 8; + dma->sample_count_out -= 2; + goto n4spk3d; + } + break; - case 0x02: /* Mono, 16-bit PCM */ - if ((dma->fifo_end - dma->fifo_pos) >= 2) { - *out_l = *out_r = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); - dma->fifo_pos += 2; - dma->sample_count_out -= 2; - goto n4spk3d; - } - break; + case 0x02: /* Mono, 16-bit PCM */ + if ((dma->fifo_end - dma->fifo_pos) >= 2) { + *out_l = *out_r = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); + dma->fifo_pos += 2; + dma->sample_count_out -= 2; + goto n4spk3d; + } + break; - case 0x03: /* Stereo, 16-bit PCM */ - switch (dma->channels) { - case 2: - if ((dma->fifo_end - dma->fifo_pos) >= 4) { - *out_l = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); - dma->fifo_pos += 2; - *out_r = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); - dma->fifo_pos += 2; - dma->sample_count_out -= 4; - goto n4spk3d; - } - break; + case 0x03: /* Stereo, 16-bit PCM */ + switch (dma->channels) { + case 2: + if ((dma->fifo_end - dma->fifo_pos) >= 4) { + *out_l = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); + dma->fifo_pos += 2; + *out_r = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); + dma->fifo_pos += 2; + dma->sample_count_out -= 4; + goto n4spk3d; + } + break; - case 4: - if ((dma->fifo_end - dma->fifo_pos) >= 8) { - dma->out_fl = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); - dma->fifo_pos += 2; - dma->out_fr = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); - dma->fifo_pos += 2; - dma->out_rl = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); - dma->fifo_pos += 2; - dma->out_rr = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); - dma->fifo_pos += 2; - dma->sample_count_out -= 8; - return; - } - break; + case 4: + if ((dma->fifo_end - dma->fifo_pos) >= 8) { + dma->out_fl = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); + dma->fifo_pos += 2; + dma->out_fr = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); + dma->fifo_pos += 2; + dma->out_rl = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); + dma->fifo_pos += 2; + dma->out_rr = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); + dma->fifo_pos += 2; + dma->sample_count_out -= 8; + return; + } + break; - case 5: /* not supported by WDM and Linux drivers; channel layout assumed */ - if ((dma->fifo_end - dma->fifo_pos) >= 10) { - dma->out_fl = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); - dma->fifo_pos += 2; - dma->out_fr = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); - dma->fifo_pos += 2; - dma->out_rl = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); - dma->fifo_pos += 2; - dma->out_rr = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); - dma->fifo_pos += 2; - dma->out_c = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); - dma->fifo_pos += 2; - dma->sample_count_out -= 10; - return; - } - break; + case 5: /* not supported by WDM and Linux drivers; channel layout assumed */ + if ((dma->fifo_end - dma->fifo_pos) >= 10) { + dma->out_fl = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); + dma->fifo_pos += 2; + dma->out_fr = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); + dma->fifo_pos += 2; + dma->out_rl = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); + dma->fifo_pos += 2; + dma->out_rr = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); + dma->fifo_pos += 2; + dma->out_c = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); + dma->fifo_pos += 2; + dma->sample_count_out -= 10; + return; + } + break; - case 6: - if ((dma->fifo_end - dma->fifo_pos) >= 12) { - dma->out_fl = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); - dma->fifo_pos += 2; - dma->out_fr = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); - dma->fifo_pos += 2; - dma->out_rl = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); - dma->fifo_pos += 2; - dma->out_rr = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); - dma->fifo_pos += 2; - dma->out_c = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); - dma->fifo_pos += 2; - dma->out_lfe= *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); - dma->fifo_pos += 2; - dma->sample_count_out -= 12; - return; - } - break; - } - break; + case 6: + if ((dma->fifo_end - dma->fifo_pos) >= 12) { + dma->out_fl = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); + dma->fifo_pos += 2; + dma->out_fr = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); + dma->fifo_pos += 2; + dma->out_rl = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); + dma->fifo_pos += 2; + dma->out_rr = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); + dma->fifo_pos += 2; + dma->out_c = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); + dma->fifo_pos += 2; + dma->out_lfe = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); + dma->fifo_pos += 2; + dma->sample_count_out -= 12; + return; + } + break; + } + break; } /* Feed silence if the FIFO is empty. */ @@ -1202,12 +1192,11 @@ cmi8x38_poll(void *priv) n4spk3d: /* Mirror front and rear channels if requested. */ if (dev->io_regs[0x1b] & 0x04) { - *out_ol = *out_l; - *out_or = *out_r; + *out_ol = *out_l; + *out_or = *out_r; } } - static void cmi8x38_get_buffer(int32_t *buffer, int len, void *priv) { @@ -1219,24 +1208,23 @@ cmi8x38_get_buffer(int32_t *buffer, int len, void *priv) /* Apply wave mute. */ if (!(dev->io_regs[0x24] & 0x40)) { - /* Fill buffer. */ - for (int c = 0; c < len * 2; c++) { - buffer[c] += dev->dma[0].buffer[c]; - buffer[c] += dev->dma[1].buffer[c]; - } + /* Fill buffer. */ + for (int c = 0; c < len * 2; c++) { + buffer[c] += dev->dma[0].buffer[c]; + buffer[c] += dev->dma[1].buffer[c]; + } } dev->dma[0].pos = dev->dma[1].pos = 0; } - static void cmi8x38_speed_changed(void *priv) { cmi8x38_t *dev = (cmi8x38_t *) priv; - double freq; - uint8_t dsr = dev->io_regs[0x09], freqreg = dev->io_regs[0x05] >> 2, - chfmt45 = dev->io_regs[0x0b], chfmt6 = dev->io_regs[0x15]; + double freq; + uint8_t dsr = dev->io_regs[0x09], freqreg = dev->io_regs[0x05] >> 2, + chfmt45 = dev->io_regs[0x0b], chfmt6 = dev->io_regs[0x15]; #ifdef ENABLE_CMI8X38_LOG char buf[256]; @@ -1247,48 +1235,56 @@ cmi8x38_speed_changed(void *priv) respectively, while CMI8738 claims they're for channel 0 and channel 1. The Linux driver just assumes the latter definition, so that's what we're going to use here. */ for (int i = 0; i < (sizeof(dev->dma) / sizeof(dev->dma[0])); i++) { - /* More confusion. The Linux driver implies the sample rate doubling - bits take precedence over any configured sample rate. 128K with both - doubling bits set is also supported there, but that's for newer chips. */ - switch (dsr & 0x03) { - case 0x01: freq = 88200.0; break; - case 0x02: freq = 96000.0; break; - case 0x03: freq = 128000.0; break; - default: freq = freqs[freqreg & 0x07]; break; - } + /* More confusion. The Linux driver implies the sample rate doubling + bits take precedence over any configured sample rate. 128K with both + doubling bits set is also supported there, but that's for newer chips. */ + switch (dsr & 0x03) { + case 0x01: + freq = 88200.0; + break; + case 0x02: + freq = 96000.0; + break; + case 0x03: + freq = 128000.0; + break; + default: + freq = freqs[freqreg & 0x07]; + break; + } - /* Set polling timer period. */ - freq = 1000000.0 / freq; - dev->dma[i].timer_latch = (uint64_t) ((double) TIMER_USEC * freq); + /* Set polling timer period. */ + freq = 1000000.0 / freq; + dev->dma[i].timer_latch = (uint64_t) ((double) TIMER_USEC * freq); - /* Calculate channel count and set DMA timer period. */ - if ((dev->type == CMEDIA_CMI8338) || (i == 0)) { -stereo: dev->dma[i].channels = 2; - } else { - if (chfmt45 & 0x80) - dev->dma[i].channels = (chfmt6 & 0x80) ? 6 : 5; - else if (chfmt45 & 0x20) - dev->dma[i].channels = 4; - else - goto stereo; - } - dev->dma[i].dma_latch = freq / dev->dma[i].channels; /* frequency / approximately(dwords * 2) */ + /* Calculate channel count and set DMA timer period. */ + if ((dev->type == CMEDIA_CMI8338) || (i == 0)) { +stereo: + dev->dma[i].channels = 2; + } else { + if (chfmt45 & 0x80) + dev->dma[i].channels = (chfmt6 & 0x80) ? 6 : 5; + else if (chfmt45 & 0x20) + dev->dma[i].channels = 4; + else + goto stereo; + } + dev->dma[i].dma_latch = freq / dev->dma[i].channels; /* frequency / approximately(dwords * 2) */ - /* Shift sample rate configuration registers. */ + /* Shift sample rate configuration registers. */ #ifdef ENABLE_CMI8X38_LOG - sprintf(&buf[strlen(buf)], " %d:%X-%X-%.0f-%dC", i, dsr & 0x03, freqreg & 0x07, 1000000.0 / freq, dev->dma[i].channels); + sprintf(&buf[strlen(buf)], " %d:%X-%X-%.0f-%dC", i, dsr & 0x03, freqreg & 0x07, 1000000.0 / freq, dev->dma[i].channels); #endif - dsr >>= 2; - freqreg >>= 3; + dsr >>= 2; + freqreg >>= 3; } #ifdef ENABLE_CMI8X38_LOG if (cmi8x38_do_log) - ui_sb_bugui(buf); + ui_sb_bugui(buf); #endif } - static void cmi8x38_reset(void *priv) { @@ -1296,19 +1292,26 @@ cmi8x38_reset(void *priv) /* Reset PCI configuration registers. */ memset(dev->pci_regs, 0, sizeof(dev->pci_regs)); - dev->pci_regs[0x00] = 0xf6; dev->pci_regs[0x01] = 0x13; - dev->pci_regs[0x02] = dev->type; dev->pci_regs[0x03] = 0x01; - dev->pci_regs[0x06] = (dev->type == CMEDIA_CMI8338) ? 0x80 : 0x10; dev->pci_regs[0x07] = 0x02; + dev->pci_regs[0x00] = 0xf6; + dev->pci_regs[0x01] = 0x13; + dev->pci_regs[0x02] = dev->type; + dev->pci_regs[0x03] = 0x01; + dev->pci_regs[0x06] = (dev->type == CMEDIA_CMI8338) ? 0x80 : 0x10; + dev->pci_regs[0x07] = 0x02; dev->pci_regs[0x08] = 0x10; - dev->pci_regs[0x0a] = 0x01; dev->pci_regs[0x0b] = 0x04; + dev->pci_regs[0x0a] = 0x01; + dev->pci_regs[0x0b] = 0x04; dev->pci_regs[0x0d] = 0x20; dev->pci_regs[0x10] = 0x01; - dev->pci_regs[0x2c] = 0xf6; dev->pci_regs[0x2d] = 0x13; + dev->pci_regs[0x2c] = 0xf6; + dev->pci_regs[0x2d] = 0x13; if (dev->type == CMEDIA_CMI8338) { - dev->pci_regs[0x2e] = 0xff; dev->pci_regs[0x2f] = 0xff; + dev->pci_regs[0x2e] = 0xff; + dev->pci_regs[0x2f] = 0xff; } else { - dev->pci_regs[0x2e] = dev->type; dev->pci_regs[0x2f] = 0x01; - dev->pci_regs[0x34] = 0x40; + dev->pci_regs[0x2e] = dev->type; + dev->pci_regs[0x2f] = 0x01; + dev->pci_regs[0x34] = 0x40; } dev->pci_regs[0x3d] = 0x01; dev->pci_regs[0x3e] = 0x02; @@ -1327,22 +1330,21 @@ cmi8x38_reset(void *priv) /* Reset DMA channels. */ for (int i = 0; i < (sizeof(dev->dma) / sizeof(dev->dma[0])); i++) { - dev->dma[i].playback_enabled = 0; + dev->dma[i].playback_enabled = 0; - dev->dma[i].fifo_pos = dev->dma[i].fifo_end = 0; - memset(dev->dma[i].fifo, 0, sizeof(dev->dma[i].fifo)); + dev->dma[i].fifo_pos = dev->dma[i].fifo_end = 0; + memset(dev->dma[i].fifo, 0, sizeof(dev->dma[i].fifo)); } /* Reset legacy DMA channel. */ - dev->tdma_8 = 1; - dev->tdma_16 = 5; + dev->tdma_8 = 1; + dev->tdma_16 = 5; dev->tdma_mask = 0; /* Reset Sound Blaster 16 mixer. */ sb_ct1745_mixer_reset(dev->sb); } - static void * cmi8x38_init(const device_t *info) { @@ -1351,33 +1353,33 @@ cmi8x38_init(const device_t *info) /* Set the chip type. */ if ((info->local == CMEDIA_CMI8738_6CH) && !device_get_config_int("six_channel")) - dev->type = CMEDIA_CMI8738_4CH; + dev->type = CMEDIA_CMI8738_4CH; else - dev->type = info->local; + dev->type = info->local; cmi8x38_log("CMI8x38: init(%06X)\n", dev->type); /* Initialize Sound Blaster 16. */ - dev->sb = device_add_inst(device_get_config_int("receive_input") ? &sb_16_compat_device : &sb_16_compat_nompu_device, 1); - dev->sb->opl_enabled = 1; /* let snd_sb.c handle the OPL3 */ + dev->sb = device_add_inst(device_get_config_int("receive_input") ? &sb_16_compat_device : &sb_16_compat_nompu_device, 1); + dev->sb->opl_enabled = 1; /* let snd_sb.c handle the OPL3 */ dev->sb->mixer_sb16.output_filter = 0; /* no output filtering */ /* Initialize legacy interrupt and DMA handlers. */ mpu401_irq_attach(dev->sb->mpu, cmi8x38_mpu_irq_update, cmi8x38_mpu_irq_pending, dev); sb_dsp_irq_attach(&dev->sb->dsp, cmi8x38_sb_irq_update, dev); sb_dsp_dma_attach(&dev->sb->dsp, cmi8x38_sb_dma_readb, cmi8x38_sb_dma_readw, cmi8x38_sb_dma_writeb, cmi8x38_sb_dma_writew, dev); - io_sethandler(0x00, 8, NULL, NULL, NULL, cmi8x38_dma_write, NULL, NULL, dev); - io_sethandler(0x08, 8, NULL, NULL, NULL, cmi8x38_dma_mask_write, NULL, NULL, dev); - io_sethandler(0xc0, 16, NULL, NULL, NULL, cmi8x38_dma_write, NULL, NULL, dev); + io_sethandler(0x00, 8, NULL, NULL, NULL, cmi8x38_dma_write, NULL, NULL, dev); + io_sethandler(0x08, 8, NULL, NULL, NULL, cmi8x38_dma_mask_write, NULL, NULL, dev); + io_sethandler(0xc0, 16, NULL, NULL, NULL, cmi8x38_dma_write, NULL, NULL, dev); io_sethandler(0xd0, 16, NULL, NULL, NULL, cmi8x38_dma_mask_write, NULL, NULL, dev); /* Initialize DMA channels. */ for (int i = 0; i < (sizeof(dev->dma) / sizeof(dev->dma[0])); i++) { - dev->dma[i].id = i; - dev->dma[i].reg = 0x80 + (8 * i); - dev->dma[i].dev = dev; + dev->dma[i].id = i; + dev->dma[i].reg = 0x80 + (8 * i); + dev->dma[i].dev = dev; - timer_add(&dev->dma[i].dma_timer, cmi8x38_dma_process, &dev->dma[i], 0); - timer_add(&dev->dma[i].poll_timer, cmi8x38_poll, &dev->dma[i], 0); + timer_add(&dev->dma[i].dma_timer, cmi8x38_dma_process, &dev->dma[i], 0); + timer_add(&dev->dma[i].poll_timer, cmi8x38_poll, &dev->dma[i], 0); } cmi8x38_speed_changed(dev); @@ -1390,10 +1392,10 @@ cmi8x38_init(const device_t *info) /* Initialize I/O traps. */ if (dev->type == CMEDIA_CMI8338) { - dev->io_traps[TRAP_DMA] = io_trap_add(cmi8338_io_trap, dev); - dev->io_traps[TRAP_PIC] = io_trap_add(cmi8338_io_trap, dev); - dev->io_traps[TRAP_OPL] = io_trap_add(cmi8338_io_trap, dev); - dev->io_traps[TRAP_MPU] = io_trap_add(cmi8338_io_trap, dev); + dev->io_traps[TRAP_DMA] = io_trap_add(cmi8338_io_trap, dev); + dev->io_traps[TRAP_PIC] = io_trap_add(cmi8338_io_trap, dev); + dev->io_traps[TRAP_OPL] = io_trap_add(cmi8338_io_trap, dev); + dev->io_traps[TRAP_MPU] = io_trap_add(cmi8338_io_trap, dev); } /* Add PCI card. */ @@ -1405,7 +1407,6 @@ cmi8x38_init(const device_t *info) return dev; } - static void cmi8x38_close(void *priv) { @@ -1414,88 +1415,92 @@ cmi8x38_close(void *priv) cmi8x38_log("CMI8x38: close()\n"); for (int i = 0; i < TRAP_MAX; i++) - io_trap_remove(dev->io_traps[i]); + io_trap_remove(dev->io_traps[i]); free(dev); } static const device_config_t cmi8x38_config[] = { - { "receive_input", "Receive input (MPU-401)", CONFIG_BINARY, "", 1 }, - { "", "", -1 } + // clang-format off + {"receive_input", "Receive input (MPU-401)", CONFIG_BINARY, "", 1 }, + { "", "", -1 } + // clang-format on }; static const device_config_t cmi8738_config[] = { - { "six_channel", "6CH variant (6-channel)", CONFIG_BINARY, "", 1 }, - { "receive_input", "Receive input (MPU-401)", CONFIG_BINARY, "", 1 }, - { "", "", -1 } + // clang-format off + { "six_channel", "6CH variant (6-channel)", CONFIG_BINARY, "", 1 }, + { "receive_input", "Receive input (MPU-401)", CONFIG_BINARY, "", 1 }, + { "", "", -1 } + // clang-format on }; const device_t cmi8338_device = { - .name = "C-Media CMI8338", + .name = "C-Media CMI8338", .internal_name = "cmi8338", - .flags = DEVICE_PCI, - .local = CMEDIA_CMI8338, - .init = cmi8x38_init, - .close = cmi8x38_close, - .reset = cmi8x38_reset, + .flags = DEVICE_PCI, + .local = CMEDIA_CMI8338, + .init = cmi8x38_init, + .close = cmi8x38_close, + .reset = cmi8x38_reset, { .available = NULL }, .speed_changed = cmi8x38_speed_changed, - .force_redraw = NULL, - .config = cmi8x38_config + .force_redraw = NULL, + .config = cmi8x38_config }; const device_t cmi8338_onboard_device = { - .name = "C-Media CMI8338 (On-Board)", + .name = "C-Media CMI8338 (On-Board)", .internal_name = "cmi8338_onboard", - .flags = DEVICE_PCI, - .local = CMEDIA_CMI8338 | (1 << 13), - .init = cmi8x38_init, - .close = cmi8x38_close, - .reset = cmi8x38_reset, + .flags = DEVICE_PCI, + .local = CMEDIA_CMI8338 | (1 << 13), + .init = cmi8x38_init, + .close = cmi8x38_close, + .reset = cmi8x38_reset, { .available = NULL }, .speed_changed = cmi8x38_speed_changed, - .force_redraw = NULL, - .config = cmi8x38_config + .force_redraw = NULL, + .config = cmi8x38_config }; const device_t cmi8738_device = { - .name = "C-Media CMI8738", + .name = "C-Media CMI8738", .internal_name = "cmi8738", - .flags = DEVICE_PCI, - .local = CMEDIA_CMI8738_6CH, - .init = cmi8x38_init, - .close = cmi8x38_close, - .reset = cmi8x38_reset, + .flags = DEVICE_PCI, + .local = CMEDIA_CMI8738_6CH, + .init = cmi8x38_init, + .close = cmi8x38_close, + .reset = cmi8x38_reset, { .available = NULL }, .speed_changed = cmi8x38_speed_changed, - .force_redraw = NULL, - .config = cmi8738_config + .force_redraw = NULL, + .config = cmi8738_config }; const device_t cmi8738_onboard_device = { - .name = "C-Media CMI8738 (On-Board)", + .name = "C-Media CMI8738 (On-Board)", .internal_name = "cmi8738_onboard", - .flags = DEVICE_PCI, - .local = CMEDIA_CMI8738_4CH | (1 << 13), - .init = cmi8x38_init, - .close = cmi8x38_close, - .reset = cmi8x38_reset, + .flags = DEVICE_PCI, + .local = CMEDIA_CMI8738_4CH | (1 << 13), + .init = cmi8x38_init, + .close = cmi8x38_close, + .reset = cmi8x38_reset, { .available = NULL }, .speed_changed = cmi8x38_speed_changed, - .force_redraw = NULL, - .config = cmi8x38_config + .force_redraw = NULL, + .config = cmi8x38_config }; const device_t cmi8738_6ch_onboard_device = { - .name = "C-Media CMI8738-6CH (On-Board)", + .name = "C-Media CMI8738-6CH (On-Board)", .internal_name = "cmi8738_6ch_onboard", - .flags = DEVICE_PCI, - .local = CMEDIA_CMI8738_6CH | (1 << 13), - .init = cmi8x38_init, - .close = cmi8x38_close, - .reset = cmi8x38_reset, + .flags = DEVICE_PCI, + .local = CMEDIA_CMI8738_6CH | (1 << 13), + .init = cmi8x38_init, + .close = cmi8x38_close, + .reset = cmi8x38_reset, { .available = NULL }, .speed_changed = cmi8x38_speed_changed, - .force_redraw = NULL, - .config = cmi8x38_config + .force_redraw = NULL, + .config = cmi8x38_config }; diff --git a/src/sound/snd_cs423x.c b/src/sound/snd_cs423x.c index 5a4a8108e..1f366e373 100644 --- a/src/sound/snd_cs423x.c +++ b/src/sound/snd_cs423x.c @@ -827,71 +827,71 @@ cs423x_speed_changed(void *priv) } const device_t cs4235_device = { - .name = "Crystal CS4235", + .name = "Crystal CS4235", .internal_name = "cs4235", - .flags = DEVICE_ISA | DEVICE_AT, - .local = CRYSTAL_CS4235, - .init = cs423x_init, - .close = cs423x_close, - .reset = cs423x_reset, + .flags = DEVICE_ISA | DEVICE_AT, + .local = CRYSTAL_CS4235, + .init = cs423x_init, + .close = cs423x_close, + .reset = cs423x_reset, { .available = NULL }, .speed_changed = cs423x_speed_changed, - .force_redraw = NULL, - .config = NULL + .force_redraw = NULL, + .config = NULL }; const device_t cs4235_onboard_device = { - .name = "Crystal CS4235 (On-Board)", + .name = "Crystal CS4235 (On-Board)", .internal_name = "cs4235_onboard", - .flags = DEVICE_ISA | DEVICE_AT, - .local = CRYSTAL_CS4235 | CRYSTAL_NOEEPROM, - .init = cs423x_init, - .close = cs423x_close, - .reset = cs423x_reset, + .flags = DEVICE_ISA | DEVICE_AT, + .local = CRYSTAL_CS4235 | CRYSTAL_NOEEPROM, + .init = cs423x_init, + .close = cs423x_close, + .reset = cs423x_reset, { .available = NULL }, .speed_changed = cs423x_speed_changed, - .force_redraw = NULL, - .config = NULL + .force_redraw = NULL, + .config = NULL }; const device_t cs4236b_device = { - .name = "Crystal CS4236B", + .name = "Crystal CS4236B", .internal_name = "cs4236b", - .flags = DEVICE_ISA | DEVICE_AT, - .local = CRYSTAL_CS4236B, - .init = cs423x_init, - .close = cs423x_close, - .reset = cs423x_reset, + .flags = DEVICE_ISA | DEVICE_AT, + .local = CRYSTAL_CS4236B, + .init = cs423x_init, + .close = cs423x_close, + .reset = cs423x_reset, { .available = NULL }, .speed_changed = cs423x_speed_changed, - .force_redraw = NULL, - .config = NULL + .force_redraw = NULL, + .config = NULL }; const device_t cs4237b_device = { - .name = "Crystal CS4237B", + .name = "Crystal CS4237B", .internal_name = "cs4237b", - .flags = DEVICE_ISA | DEVICE_AT, - .local = CRYSTAL_CS4237B, - .init = cs423x_init, - .close = cs423x_close, - .reset = cs423x_reset, + .flags = DEVICE_ISA | DEVICE_AT, + .local = CRYSTAL_CS4237B, + .init = cs423x_init, + .close = cs423x_close, + .reset = cs423x_reset, { .available = NULL }, .speed_changed = cs423x_speed_changed, - .force_redraw = NULL, - .config = NULL + .force_redraw = NULL, + .config = NULL }; const device_t cs4238b_device = { - .name = "Crystal CS4238B", + .name = "Crystal CS4238B", .internal_name = "cs4238b", - .flags = DEVICE_ISA | DEVICE_AT, - .local = CRYSTAL_CS4238B, - .init = cs423x_init, - .close = cs423x_close, - .reset = cs423x_reset, + .flags = DEVICE_ISA | DEVICE_AT, + .local = CRYSTAL_CS4238B, + .init = cs423x_init, + .close = cs423x_close, + .reset = cs423x_reset, { .available = NULL }, .speed_changed = cs423x_speed_changed, - .force_redraw = NULL, - .config = NULL + .force_redraw = NULL, + .config = NULL }; diff --git a/src/sound/snd_sb.c b/src/sound/snd_sb.c index 5f5aa5899..9d80ef65c 100644 --- a/src/sound/snd_sb.c +++ b/src/sound/snd_sb.c @@ -377,12 +377,12 @@ sb_get_buffer_sb16_awe32(int32_t *buffer, int len, void *p) : 0; if (mixer->output_filter) { - /* We divide by 3 to get the volume down to normal. */ - out_l += (low_fir_sb16(0, 0, (double) sb->dsp.buffer[c]) * mixer->voice_l) / 3.0; - out_r += (low_fir_sb16(0, 1, (double) sb->dsp.buffer[c + 1]) * mixer->voice_r) / 3.0; + /* We divide by 3 to get the volume down to normal. */ + out_l += (low_fir_sb16(0, 0, (double) sb->dsp.buffer[c]) * mixer->voice_l) / 3.0; + out_r += (low_fir_sb16(0, 1, (double) sb->dsp.buffer[c + 1]) * mixer->voice_r) / 3.0; } else { - out_l += (((double) sb->dsp.buffer[c]) * mixer->voice_l) / 3.0; - out_r += (((double) sb->dsp.buffer[c + 1]) * mixer->voice_r) / 3.0; + out_l += (((double) sb->dsp.buffer[c]) * mixer->voice_l) / 3.0; + out_r += (((double) sb->dsp.buffer[c + 1]) * mixer->voice_r) / 3.0; } out_l *= mixer->master_l; @@ -1764,10 +1764,10 @@ sb_16_init(const device_t *info) &sb->opl); } - sb->mixer_enabled = 1; + sb->mixer_enabled = 1; sb->mixer_sb16.output_filter = 1; - io_sethandler(addr + 4, 0x0002, sb_ct1745_mixer_read, NULL, NULL, - sb_ct1745_mixer_write, NULL, NULL, sb); + io_sethandler(addr + 4, 0x0002, sb_ct1745_mixer_read, NULL, NULL, + sb_ct1745_mixer_write, NULL, NULL, sb); sound_add_handler(sb_get_buffer_sb16_awe32, sb); sound_set_cd_audio_filter(sb16_awe32_filter_cd_audio, sb); @@ -1797,7 +1797,7 @@ sb_16_pnp_init(const device_t *info) sb_dsp_init(&sb->dsp, SB16, SB_SUBTYPE_DEFAULT, sb); sb_ct1745_mixer_reset(sb); - sb->mixer_enabled = 1; + sb->mixer_enabled = 1; sb->mixer_sb16.output_filter = 1; sound_add_handler(sb_get_buffer_sb16_awe32, sb); sound_set_cd_audio_filter(sb16_awe32_filter_cd_audio, sb); @@ -1841,7 +1841,6 @@ sb_16_compat_init(const device_t *info) return sb; } - static int sb_awe32_available() { @@ -1915,10 +1914,10 @@ sb_awe32_init(const device_t *info) &sb->opl); } - sb->mixer_enabled = 1; + sb->mixer_enabled = 1; sb->mixer_sb16.output_filter = 1; - io_sethandler(addr + 4, 0x0002, sb_ct1745_mixer_read, NULL, NULL, - sb_ct1745_mixer_write, NULL, NULL, sb); + io_sethandler(addr + 4, 0x0002, sb_ct1745_mixer_read, NULL, NULL, + sb_ct1745_mixer_write, NULL, NULL, sb); sound_add_handler(sb_get_buffer_sb16_awe32, sb); sound_set_cd_audio_filter(sb16_awe32_filter_cd_audio, sb); @@ -1952,7 +1951,7 @@ sb_awe32_pnp_init(const device_t *info) sb_dsp_init(&sb->dsp, ((info->local == 2) || (info->local == 3) || (info->local == 4)) ? SBAWE64 : SBAWE32, SB_SUBTYPE_DEFAULT, sb); sb_ct1745_mixer_reset(sb); - sb->mixer_enabled = 1; + sb->mixer_enabled = 1; sb->mixer_sb16.output_filter = 1; sound_add_handler(sb_get_buffer_sb16_awe32, sb); sound_set_cd_audio_filter(sb16_awe32_filter_cd_audio, sb); @@ -2425,255 +2424,253 @@ static const device_config_t sb_awe64_gold_config[] = { // clang-format on const device_t sb_1_device = { - .name = "Sound Blaster v1.0", + .name = "Sound Blaster v1.0", .internal_name = "sb", - .flags = DEVICE_ISA, - .local = 0, - .init = sb_1_init, - .close = sb_close, - .reset = NULL, + .flags = DEVICE_ISA, + .local = 0, + .init = sb_1_init, + .close = sb_close, + .reset = NULL, { .available = NULL }, .speed_changed = sb_speed_changed, - .force_redraw = NULL, - .config = sb_config + .force_redraw = NULL, + .config = sb_config }; const device_t sb_15_device = { - .name = "Sound Blaster v1.5", + .name = "Sound Blaster v1.5", .internal_name = "sb1.5", - .flags = DEVICE_ISA, - .local = 0, - .init = sb_15_init, - .close = sb_close, - .reset = NULL, + .flags = DEVICE_ISA, + .local = 0, + .init = sb_15_init, + .close = sb_close, + .reset = NULL, { .available = NULL }, .speed_changed = sb_speed_changed, - .force_redraw = NULL, - .config = sb15_config + .force_redraw = NULL, + .config = sb15_config }; const device_t sb_mcv_device = { - .name = "Sound Blaster MCV", + .name = "Sound Blaster MCV", .internal_name = "sbmcv", - .flags = DEVICE_MCA, - .local = 0, - .init = sb_mcv_init, - .close = sb_close, - .reset = NULL, + .flags = DEVICE_MCA, + .local = 0, + .init = sb_mcv_init, + .close = sb_close, + .reset = NULL, { .available = NULL }, .speed_changed = sb_speed_changed, - .force_redraw = NULL, - .config = sb_mcv_config + .force_redraw = NULL, + .config = sb_mcv_config }; const device_t sb_2_device = { - .name = "Sound Blaster v2.0", + .name = "Sound Blaster v2.0", .internal_name = "sb2.0", - .flags = DEVICE_ISA, - .local = 0, - .init = sb_2_init, - .close = sb_close, - .reset = NULL, + .flags = DEVICE_ISA, + .local = 0, + .init = sb_2_init, + .close = sb_close, + .reset = NULL, { .available = NULL }, .speed_changed = sb_speed_changed, - .force_redraw = NULL, - .config = sb2_config + .force_redraw = NULL, + .config = sb2_config }; const device_t sb_pro_v1_device = { - .name = "Sound Blaster Pro v1", + .name = "Sound Blaster Pro v1", .internal_name = "sbprov1", - .flags = DEVICE_ISA, - .local = 0, - .init = sb_pro_v1_init, - .close = sb_close, - .reset = NULL, + .flags = DEVICE_ISA, + .local = 0, + .init = sb_pro_v1_init, + .close = sb_close, + .reset = NULL, { .available = NULL }, .speed_changed = sb_speed_changed, - .force_redraw = NULL, - .config = sb_pro_config + .force_redraw = NULL, + .config = sb_pro_config }; const device_t sb_pro_v2_device = { - .name = "Sound Blaster Pro v2", + .name = "Sound Blaster Pro v2", .internal_name = "sbprov2", - .flags = DEVICE_ISA, - .local = 0, - .init = sb_pro_v2_init, - .close = sb_close, - .reset = NULL, + .flags = DEVICE_ISA, + .local = 0, + .init = sb_pro_v2_init, + .close = sb_close, + .reset = NULL, { .available = NULL }, .speed_changed = sb_speed_changed, - .force_redraw = NULL, - .config = sb_pro_config + .force_redraw = NULL, + .config = sb_pro_config }; const device_t sb_pro_mcv_device = { - .name = "Sound Blaster Pro MCV", + .name = "Sound Blaster Pro MCV", .internal_name = "sbpromcv", - .flags = DEVICE_MCA, - .local = 0, - .init = sb_pro_mcv_init, - .close = sb_close, - .reset = NULL, + .flags = DEVICE_MCA, + .local = 0, + .init = sb_pro_mcv_init, + .close = sb_close, + .reset = NULL, { .available = NULL }, .speed_changed = sb_speed_changed, - .force_redraw = NULL, - .config = NULL + .force_redraw = NULL, + .config = NULL }; const device_t sb_pro_compat_device = { - .name = "Sound Blaster Pro (Compatibility)", + .name = "Sound Blaster Pro (Compatibility)", .internal_name = "sbpro_compat", - .flags = DEVICE_ISA | DEVICE_AT, - .local = 0, - .init = sb_pro_compat_init, - .close = sb_close, - .reset = NULL, + .flags = DEVICE_ISA | DEVICE_AT, + .local = 0, + .init = sb_pro_compat_init, + .close = sb_close, + .reset = NULL, { .available = NULL }, .speed_changed = sb_speed_changed, - .force_redraw = NULL, - .config = NULL + .force_redraw = NULL, + .config = NULL }; const device_t sb_16_device = { - .name = "Sound Blaster 16", + .name = "Sound Blaster 16", .internal_name = "sb16", - .flags = DEVICE_ISA | DEVICE_AT, - .local = 0, - .init = sb_16_init, - .close = sb_close, - .reset = NULL, + .flags = DEVICE_ISA | DEVICE_AT, + .local = 0, + .init = sb_16_init, + .close = sb_close, + .reset = NULL, { .available = NULL }, .speed_changed = sb_speed_changed, - .force_redraw = NULL, - .config = sb_16_config + .force_redraw = NULL, + .config = sb_16_config }; const device_t sb_16_pnp_device = { - .name = "Sound Blaster 16 PnP", + .name = "Sound Blaster 16 PnP", .internal_name = "sb16_pnp", - .flags = DEVICE_ISA | DEVICE_AT, - .local = 0, - .init = sb_16_pnp_init, - .close = sb_close, - .reset = NULL, + .flags = DEVICE_ISA | DEVICE_AT, + .local = 0, + .init = sb_16_pnp_init, + .close = sb_close, + .reset = NULL, { .available = NULL }, .speed_changed = sb_speed_changed, - .force_redraw = NULL, - .config = sb_16_pnp_config + .force_redraw = NULL, + .config = sb_16_pnp_config }; -const device_t sb_16_compat_device = -{ - .name = "Sound Blaster 16 (Compatibility)", +const device_t sb_16_compat_device = { + .name = "Sound Blaster 16 (Compatibility)", .internal_name = "sb16_compat", - .flags = DEVICE_ISA | DEVICE_AT, - .local = 1, - .init = sb_16_compat_init, - .close = sb_close, - .reset = NULL, + .flags = DEVICE_ISA | DEVICE_AT, + .local = 1, + .init = sb_16_compat_init, + .close = sb_close, + .reset = NULL, { .available = NULL }, .speed_changed = sb_speed_changed, - .force_redraw = NULL, - .config = NULL + .force_redraw = NULL, + .config = NULL }; -const device_t sb_16_compat_nompu_device = -{ - .name = "Sound Blaster 16 (Compatibility - MPU-401 Off)", +const device_t sb_16_compat_nompu_device = { + .name = "Sound Blaster 16 (Compatibility - MPU-401 Off)", .internal_name = "sb16_compat", - .flags = DEVICE_ISA | DEVICE_AT, - .local = 0, - .init = sb_16_compat_init, - .close = sb_close, - .reset = NULL, + .flags = DEVICE_ISA | DEVICE_AT, + .local = 0, + .init = sb_16_compat_init, + .close = sb_close, + .reset = NULL, { .available = NULL }, - .speed_changed =sb_speed_changed, - .force_redraw = NULL, - .config = NULL + .speed_changed = sb_speed_changed, + .force_redraw = NULL, + .config = NULL }; const device_t sb_32_pnp_device = { - .name = "Sound Blaster 32 PnP", + .name = "Sound Blaster 32 PnP", .internal_name = "sb32_pnp", - .flags = DEVICE_ISA | DEVICE_AT, - .local = 0, - .init = sb_awe32_pnp_init, - .close = sb_awe32_close, - .reset = NULL, + .flags = DEVICE_ISA | DEVICE_AT, + .local = 0, + .init = sb_awe32_pnp_init, + .close = sb_awe32_close, + .reset = NULL, { .available = sb_32_pnp_available }, .speed_changed = sb_speed_changed, - .force_redraw = NULL, - .config = sb_32_pnp_config + .force_redraw = NULL, + .config = sb_32_pnp_config }; const device_t sb_awe32_device = { - .name = "Sound Blaster AWE32", + .name = "Sound Blaster AWE32", .internal_name = "sbawe32", - .flags = DEVICE_ISA | DEVICE_AT, - .local = 0, - .init = sb_awe32_init, - .close = sb_awe32_close, - .reset = NULL, + .flags = DEVICE_ISA | DEVICE_AT, + .local = 0, + .init = sb_awe32_init, + .close = sb_awe32_close, + .reset = NULL, { .available = sb_awe32_available }, .speed_changed = sb_speed_changed, - .force_redraw = NULL, - .config = sb_awe32_config + .force_redraw = NULL, + .config = sb_awe32_config }; const device_t sb_awe32_pnp_device = { - .name = "Sound Blaster AWE32 PnP", + .name = "Sound Blaster AWE32 PnP", .internal_name = "sbawe32_pnp", - .flags = DEVICE_ISA | DEVICE_AT, - .local = 1, - .init = sb_awe32_pnp_init, - .close = sb_awe32_close, - .reset = NULL, + .flags = DEVICE_ISA | DEVICE_AT, + .local = 1, + .init = sb_awe32_pnp_init, + .close = sb_awe32_close, + .reset = NULL, { .available = sb_awe32_pnp_available }, .speed_changed = sb_speed_changed, - .force_redraw = NULL, - .config = sb_awe32_pnp_config + .force_redraw = NULL, + .config = sb_awe32_pnp_config }; const device_t sb_awe64_value_device = { - .name = "Sound Blaster AWE64 Value", + .name = "Sound Blaster AWE64 Value", .internal_name = "sbawe64_value", - .flags = DEVICE_ISA | DEVICE_AT, - .local = 2, - .init = sb_awe32_pnp_init, - .close = sb_awe32_close, - .reset = NULL, + .flags = DEVICE_ISA | DEVICE_AT, + .local = 2, + .init = sb_awe32_pnp_init, + .close = sb_awe32_close, + .reset = NULL, { .available = sb_awe64_value_available }, .speed_changed = sb_speed_changed, - .force_redraw = NULL, - .config = sb_awe64_value_config + .force_redraw = NULL, + .config = sb_awe64_value_config }; const device_t sb_awe64_device = { - .name = "Sound Blaster AWE64", + .name = "Sound Blaster AWE64", .internal_name = "sbawe64", - .flags = DEVICE_ISA | DEVICE_AT, - .local = 3, - .init = sb_awe32_pnp_init, - .close = sb_awe32_close, - .reset = NULL, + .flags = DEVICE_ISA | DEVICE_AT, + .local = 3, + .init = sb_awe32_pnp_init, + .close = sb_awe32_close, + .reset = NULL, { .available = sb_awe64_available }, .speed_changed = sb_speed_changed, - .force_redraw = NULL, - .config = sb_awe64_config + .force_redraw = NULL, + .config = sb_awe64_config }; const device_t sb_awe64_gold_device = { - .name = "Sound Blaster AWE64 Gold", + .name = "Sound Blaster AWE64 Gold", .internal_name = "sbawe64_gold", - .flags = DEVICE_ISA | DEVICE_AT, - .local = 4, - .init = sb_awe32_pnp_init, - .close = sb_awe32_close, - .reset = NULL, + .flags = DEVICE_ISA | DEVICE_AT, + .local = 4, + .init = sb_awe32_pnp_init, + .close = sb_awe32_close, + .reset = NULL, { .available = sb_awe64_gold_available }, .speed_changed = sb_speed_changed, - .force_redraw = NULL, - .config = sb_awe64_gold_config + .force_redraw = NULL, + .config = sb_awe64_gold_config }; diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index 147a1c4e6..194beee41 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -174,9 +174,9 @@ sb_irq_update_pic(void *priv, int set) { sb_dsp_t *dsp = (sb_dsp_t *) priv; if (set) - picint(1 << dsp->sb_irqnum); + picint(1 << dsp->sb_irqnum); else - picintc(1 << dsp->sb_irqnum); + picintc(1 << dsp->sb_irqnum); } void From 3de4fbb75ccbf27ba05cd1a308c5d46149079d19 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Wed, 16 Mar 2022 14:36:57 -0300 Subject: [PATCH 51/62] Jenkins: Minor fixes to Windows build flow --- .ci/build.sh | 4 ++-- .ci/dependencies_msys.txt | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.ci/build.sh b/.ci/build.sh index 72c08129c..e62d037bd 100644 --- a/.ci/build.sh +++ b/.ci/build.sh @@ -283,7 +283,7 @@ then if [ $? -ne 0 ] then # Install packages individually if installing them all together failed. - for pkg in pkgs + for pkg in $pkgs do yes | pacman -U --needed "$pkg" done @@ -306,7 +306,7 @@ then if [ $? -ne 0 ] then # Install packages individually if installing them all together failed. - for pkg in pkgs + for pkg in $pkgs do yes | pacman -S --needed "$pkg" done diff --git a/.ci/dependencies_msys.txt b/.ci/dependencies_msys.txt index 8c85eb90b..99b278fb3 100644 --- a/.ci/dependencies_msys.txt +++ b/.ci/dependencies_msys.txt @@ -6,6 +6,10 @@ libwinpthread-git 9.0.0.6357.eac8c38c1-1 winpthreads-git 9.0.0.6357.eac8c38c1-1 winstorecompat-git 9.0.0.6357.eac8c38c1-1 gcc-libs 11.2.0-4 +gcc-ada 11.2.0-4 +gcc-fortran 11.2.0-4 +gcc-libgfortran 11.2.0-4 +gcc-objc 11.2.0-4 gcc 11.2.0-4 libgccjit 11.2.0-4 tools-git 9.0.0.6357.eac8c38c1-1 From 5e9a47d6d1ffa7ed7b03320a5ca191bb1ad41ee3 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Wed, 16 Mar 2022 15:41:58 -0300 Subject: [PATCH 52/62] Jenkins: Attempt to streamline failure state --- .ci/Jenkinsfile | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/.ci/Jenkinsfile b/.ci/Jenkinsfile index 7c5502cec..a90e1d806 100644 --- a/.ci/Jenkinsfile +++ b/.ci/Jenkinsfile @@ -84,8 +84,6 @@ def presetFlags = [ 'Dev': '--preset=experimental -D CMAKE_BUILD_TYPE=Debug -D VNC=OFF' ] -def anyFailure = false - def gitClone(repository, branch) { /* Clean workspace. */ cleanWs() @@ -135,11 +133,8 @@ def runBuild(args) { } def failStage() { - /* Mark that a failure occurred. */ - anyFailure = true - /* Force this stage to fail. */ - catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') { + catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') { def x = 1 / 0 } } @@ -238,7 +233,7 @@ pipeline { presets.each { preset -> def combination = "$os $arch $dynarec $preset" combinations[combination] = { - try { + catchError(buildResult: 'FAILURE', stageResult: 'SUCCESS') { retry(10) { node(os) { stage(combination) { @@ -268,9 +263,6 @@ pipeline { } } } - } catch (e) { - /* Fail this stage. */ - failStage() } } } @@ -286,12 +278,6 @@ pipeline { post { always { script { - /* Mark build as failed if any step has failed. */ - if (anyFailure) { - println "[!] Failing build because a build stage failed" - currentBuild.result = 'FAILURE' - } - /* Send out build notifications. */ if (!env.JOB_BASE_NAME.contains('TestBuildPleaseIgnore')) { try { From 7571c0b155bdd0fe980182ab99d1df1084040449 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Thu, 17 Mar 2022 15:06:19 -0300 Subject: [PATCH 53/62] CMI8x38: Disable status bar logging due to a Qt incompatibility --- src/sound/snd_cmi8x38.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sound/snd_cmi8x38.c b/src/sound/snd_cmi8x38.c index 4d02afcaa..5020c0b63 100644 --- a/src/sound/snd_cmi8x38.c +++ b/src/sound/snd_cmi8x38.c @@ -1279,7 +1279,7 @@ stereo: freqreg >>= 3; } -#ifdef ENABLE_CMI8X38_LOG +#if 0 if (cmi8x38_do_log) ui_sb_bugui(buf); #endif From 90e9054f2c8360b9318f24933a522e770e5d10f5 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Thu, 17 Mar 2022 15:47:25 -0300 Subject: [PATCH 54/62] CMI8x38: Implement TDMA update registers (nothing uses them) --- src/include/86box/snd_sb_dsp.h | 4 +-- src/sound/snd_cmi8x38.c | 56 ++++++++++++++++++++++------------ src/sound/snd_sb_dsp.c | 16 +++++----- 3 files changed, 46 insertions(+), 30 deletions(-) diff --git a/src/include/86box/snd_sb_dsp.h b/src/include/86box/snd_sb_dsp.h index aeceb62f1..9dd184ac5 100644 --- a/src/include/86box/snd_sb_dsp.h +++ b/src/include/86box/snd_sb_dsp.h @@ -15,9 +15,9 @@ typedef struct sb_dsp_t { int sb_subtype; /* which clone */ void *parent; /* "sb_t *" if default subtype, "azt2316a_t *" if aztech. */ - int sb_8_length, sb_8_format, sb_8_autoinit, sb_8_pause, sb_8_enable, sb_8_autolen, sb_8_output; + int sb_8_length, sb_8_origlength, sb_8_format, sb_8_autoinit, sb_8_pause, sb_8_enable, sb_8_autolen, sb_8_output; int sb_8_dmanum; - int sb_16_length, sb_16_format, sb_16_autoinit, sb_16_pause, sb_16_enable, sb_16_autolen, sb_16_output; + int sb_16_length, sb_16_origlength, sb_16_format, sb_16_autoinit, sb_16_pause, sb_16_enable, sb_16_autolen, sb_16_output; int sb_16_dmanum; int sb_pausetime; int (*dma_readb)(void *priv), diff --git a/src/sound/snd_cmi8x38.c b/src/sound/snd_cmi8x38.c index 5020c0b63..79df3d534 100644 --- a/src/sound/snd_cmi8x38.c +++ b/src/sound/snd_cmi8x38.c @@ -1,18 +1,18 @@ /* - * 86Box A hypervisor and IBM PC system emulator that specializes in - * running old operating systems and software designed for IBM - * PC systems and compatibles from 1981 through fairly recent - * system designs based on the PCI bus. + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. * - * This file is part of the 86Box distribution. + * This file is part of the 86Box distribution. * - * C-Media CMI8x38 PCI audio controller emulation. + * C-Media CMI8x38 PCI audio controller emulation. * * * - * Authors: RichardG, + * Authors: RichardG, * - * Copyright 2022 RichardG. + * Copyright 2022 RichardG. */ #include #include @@ -81,11 +81,12 @@ typedef struct _cmi8x38_ { cmi8x38_dma_t dma[2]; uint16_t tdma_base_addr, tdma_base_count; - int tdma_8, tdma_16, tdma_mask, prev_mask; + int tdma_8, tdma_16, tdma_mask, prev_mask, tdma_irq_mask; int master_vol_l, master_vol_r, cd_vol_l, cd_vol_r; } cmi8x38_t; +#define ENABLE_CMI8X38_LOG 1 #ifdef ENABLE_CMI8X38_LOG int cmi8x38_do_log = ENABLE_CMI8X38_LOG; @@ -178,6 +179,18 @@ cmi8x38_sb_dma_post(cmi8x38_t *dev, uint16_t *addr, uint16_t *count, int channel dma[channel].cc = dma[channel].cb = *count; } + /* Check TDMA position update interrupt if enabled. */ + if (dev->io_regs[0x0e] & 0x04) { + /* Nothing uses this; I assume it goes by the SB DSP sample counter (forwards instead of backwards). */ + int origlength = (channel & 4) ? dev->sb->dsp.sb_16_origlength : dev->sb->dsp.sb_8_origlength, + length = (channel & 4) ? dev->sb->dsp.sb_16_length : dev->sb->dsp.sb_8_length; + if ((origlength != length) && (((origlength - length) & dev->tdma_irq_mask) == 0)) { /* skip initial sample */ + /* Fire the interrupt. */ + dev->io_regs[0x11] |= (channel & 4) ? 0x40 : 0x80; + cmi8x38_update_irqs(dev); + } + } + /* Handle end of DMA. */ if (*count == 0xffff) { if (dma[channel].mode & 0x10) { /* auto-init */ @@ -193,8 +206,8 @@ cmi8x38_sb_dma_post(cmi8x38_t *dev, uint16_t *addr, uint16_t *count, int channel dev->tdma_mask |= 1 << channel; } - /* Set the mysterious LHBTOG bit, assuming it corresponds to - the 8237 channel status bit. Nothing appears to read it. */ + /* Set the mysterious LHBTOG bit, assuming it corresponds + to the 8237 channel status bit. Nothing reads it. */ dev->io_regs[0x10] |= 0x40; return DMA_OVER; @@ -330,8 +343,8 @@ cmi8x38_dma_write(uint16_t addr, uint8_t val, void *priv) (channel & 4) ? ((dma[channel].ab & 0xfffe0000) | ((*daddr) << 1)) : ((dma[channel].ab & 0xffff0000) | *daddr), *count); - /* Clear the mysterious LHBTOG bit, assuming it corresponds to - the 8237 channel status bit. Nothing appears to read it. */ + /* Clear the mysterious LHBTOG bit, assuming it corresponds + to the 8237 channel status bit. Nothing reads it. */ dev->io_regs[0x10] &= ~0x40; } @@ -445,7 +458,7 @@ cmi8x38_sb_mixer_write(uint16_t addr, uint8_t val, void *priv) if ((mixer->index == 0x00) || (mixer->index == 0x0e)) sb_dsp_set_stereo(&dev->sb->dsp, mixer->regs[0x0e] & 2); - /* Set TDMA channels if autodetection is enabled. */ + /* Set TDMA channels if auto-detection is enabled. */ if ((dev->io_regs[0x27] & 0x01) && (mixer->index == 0x81)) { dev->tdma_8 = dev->sb->dsp.sb_8_dmanum; if (dev->sb->dsp.sb_type >= SB16) @@ -712,8 +725,8 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv) case 0x09: #if 0 /* actual CMI8338 behavior unconfirmed; this register is required for the Windows XP driver which outputs 96K */ - if (dev->type == CMEDIA_CMI8338) - return; + if (dev->type == CMEDIA_CMI8338) + return; #endif /* Update sample rate. */ dev->io_regs[addr] = val; @@ -794,6 +807,9 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv) val &= 0x0f; else val &= 0xdf; + + /* Update the TDMA position update interrupt's sample interval. */ + dev->tdma_irq_mask = 2047 >> ((val >> 2) & 3); break; case 0x19: @@ -1008,8 +1024,7 @@ cmi8x38_dma_process(void *priv) /* Start DMA if requested. */ if (dma->restart) { /* Set up base address and counters. - I have no idea how sample_count_out is supposed to work, - nothing consumes it, so it's implemented as an assumption. */ + Nothing reads sample_count_out; it's implemented as an assumption. */ dma->restart = 0; dma->sample_ptr = *((uint32_t *) &dev->io_regs[dma->reg]); dma->frame_count_dma = dma->sample_count_out = *((uint16_t *) &dev->io_regs[dma->reg | 0x4]) + 1; @@ -1321,6 +1336,7 @@ cmi8x38_reset(void *priv) memset(dev->io_regs, 0, sizeof(dev->io_regs)); dev->io_regs[0x0b] = (dev->type >> 8) & 0x1f; dev->io_regs[0x0f] = dev->type >> 16; + dev->tdma_irq_mask = 2047; /* Reset I/O mappings. */ cmi8x38_remap(dev); @@ -1422,8 +1438,8 @@ cmi8x38_close(void *priv) static const device_config_t cmi8x38_config[] = { // clang-format off - {"receive_input", "Receive input (MPU-401)", CONFIG_BINARY, "", 1 }, - { "", "", -1 } + { "receive_input", "Receive input (MPU-401)", CONFIG_BINARY, "", 1 }, + { "", "", -1 } // clang-format on }; diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index 194beee41..c87f45e2f 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -349,7 +349,7 @@ sb_start_dma(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len) dsp->sb_pausetime = -1; if (dma8) { - dsp->sb_8_length = len; + dsp->sb_8_length = dsp->sb_8_origlength = len; dsp->sb_8_format = format; dsp->sb_8_autoinit = autoinit; dsp->sb_8_pause = 0; @@ -363,7 +363,7 @@ sb_start_dma(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len) dsp->sbleftright = dsp->sbleftright_default; dsp->sbdacpos = 0; } else { - dsp->sb_16_length = len; + dsp->sb_16_length = dsp->sb_16_origlength = len; dsp->sb_16_format = format; dsp->sb_16_autoinit = autoinit; dsp->sb_16_pause = 0; @@ -380,7 +380,7 @@ void sb_start_dma_i(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len) { if (dma8) { - dsp->sb_8_length = len; + dsp->sb_8_length = dsp->sb_8_origlength = len; dsp->sb_8_format = format; dsp->sb_8_autoinit = autoinit; dsp->sb_8_pause = 0; @@ -391,7 +391,7 @@ sb_start_dma_i(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len) if (!timer_is_enabled(&dsp->input_timer)) timer_set_delay_u64(&dsp->input_timer, dsp->sblatchi); } else { - dsp->sb_16_length = len; + dsp->sb_16_length = dsp->sb_16_origlength = len; dsp->sb_16_format = format; dsp->sb_16_autoinit = autoinit; dsp->sb_16_pause = 0; @@ -1383,7 +1383,7 @@ pollsb(void *p) if (dsp->sb_8_length < 0) { if (dsp->sb_8_autoinit) - dsp->sb_8_length = dsp->sb_8_autolen; + dsp->sb_8_length = dsp->sb_8_origlength = dsp->sb_8_autolen; else { dsp->sb_8_enable = 0; timer_disable(&dsp->output_timer); @@ -1432,7 +1432,7 @@ pollsb(void *p) if (dsp->sb_16_length < 0) { sb_dsp_log("16DMA over %i\n", dsp->sb_16_autoinit); if (dsp->sb_16_autoinit) - dsp->sb_16_length = dsp->sb_16_autolen; + dsp->sb_16_length = dsp->sb_16_origlength = dsp->sb_16_autolen; else { dsp->sb_16_enable = 0; timer_disable(&dsp->output_timer); @@ -1491,7 +1491,7 @@ sb_poll_i(void *p) if (dsp->sb_8_length < 0) { if (dsp->sb_8_autoinit) - dsp->sb_8_length = dsp->sb_8_autolen; + dsp->sb_8_length = dsp->sb_8_origlength = dsp->sb_8_autolen; else { dsp->sb_8_enable = 0; timer_disable(&dsp->input_timer); @@ -1536,7 +1536,7 @@ sb_poll_i(void *p) if (dsp->sb_16_length < 0) { if (dsp->sb_16_autoinit) - dsp->sb_16_length = dsp->sb_16_autolen; + dsp->sb_16_length = dsp->sb_16_origlength = dsp->sb_16_autolen; else { dsp->sb_16_enable = 0; timer_disable(&dsp->input_timer); From 64c38a2334bc82b69b3bd733f9171bad564d8aa4 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Thu, 17 Mar 2022 15:49:24 -0300 Subject: [PATCH 55/62] GDB Stub: Fix header indentation --- src/gdbstub.c | 16 ++++++++-------- src/include/86box/gdbstub.h | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/gdbstub.c b/src/gdbstub.c index d6b410a95..20103c4cd 100644 --- a/src/gdbstub.c +++ b/src/gdbstub.c @@ -1,18 +1,18 @@ /* - * 86Box A hypervisor and IBM PC system emulator that specializes in - * running old operating systems and software designed for IBM - * PC systems and compatibles from 1981 through fairly recent - * system designs based on the PCI bus. + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. * - * This file is part of the 86Box distribution. + * This file is part of the 86Box distribution. * - * GDB stub server for remote debugging. + * GDB stub server for remote debugging. * * * - * Authors: RichardG, + * Authors: RichardG, * - * Copyright 2022 RichardG. + * Copyright 2022 RichardG. */ #include #include diff --git a/src/include/86box/gdbstub.h b/src/include/86box/gdbstub.h index 7ed893392..49eed2c45 100644 --- a/src/include/86box/gdbstub.h +++ b/src/include/86box/gdbstub.h @@ -1,18 +1,18 @@ /* - * 86Box A hypervisor and IBM PC system emulator that specializes in - * running old operating systems and software designed for IBM - * PC systems and compatibles from 1981 through fairly recent - * system designs based on the PCI bus. + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. * - * This file is part of the 86Box distribution. + * This file is part of the 86Box distribution. * - * Definitions for the GDB stub server. + * Definitions for the GDB stub server. * * * - * Authors: RichardG, + * Authors: RichardG, * - * Copyright 2022 RichardG. + * Copyright 2022 RichardG. */ #ifndef EMU_GDBSTUB_H #define EMU_GDBSTUB_H From 81dfba16f3f16f3b02a1043966db57bfed73c204 Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Fri, 18 Mar 2022 00:56:59 +0600 Subject: [PATCH 56/62] wl_mouse: Don't uncapture mouse if is already uncaptured --- src/qt/wl_mouse.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qt/wl_mouse.cpp b/src/qt/wl_mouse.cpp index 420ddc486..75a03813c 100644 --- a/src/qt/wl_mouse.cpp +++ b/src/qt/wl_mouse.cpp @@ -98,8 +98,8 @@ void wl_mouse_capture(QWindow *window) void wl_mouse_uncapture() { - zwp_locked_pointer_v1_destroy(conf_pointer); - zwp_relative_pointer_v1_destroy(rel_pointer); + if (conf_pointer) zwp_locked_pointer_v1_destroy(conf_pointer); + if (rel_pointer) zwp_relative_pointer_v1_destroy(rel_pointer); rel_pointer = nullptr; conf_pointer = nullptr; } From abdac1d524ed5b2e0ff548a8f7826e5ea959e1ec Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Thu, 17 Mar 2022 16:23:40 -0300 Subject: [PATCH 57/62] CMI8x38: Improve DMA/playback start/stop --- src/sound/snd_cmi8x38.c | 58 +++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/src/sound/snd_cmi8x38.c b/src/sound/snd_cmi8x38.c index 79df3d534..1a7f690dd 100644 --- a/src/sound/snd_cmi8x38.c +++ b/src/sound/snd_cmi8x38.c @@ -668,36 +668,35 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv) break; case 0x02: - /* Reset DMA channels if requested. */ - if (val & 0x04) { - val &= ~0x01; + /* Reset or start DMA channels if requested. */ + dev->io_regs[addr] = val & 0x03; + for (int i = 0; i < (sizeof(dev->dma) / sizeof(dev->dma[0])); i++) { + if (val & (0x04 << i)) { + /* Reset DMA channel. */ + val &= ~(0x01 << i); + dev->io_regs[0x10] &= ~(0x01 << i); /* clear interrupt */ - if (dev->sb->dsp.sb_8_enable || dev->sb->dsp.sb_16_enable || dev->sb->dsp.sb_irq8 || dev->sb->dsp.sb_irq16) { - dev->sb->dsp.sb_8_enable = dev->sb->dsp.sb_16_enable = dev->sb->dsp.sb_irq8 = dev->sb->dsp.sb_irq16 = 0; - cmi8x38_update_irqs(dev); + /* Reset Sound Blaster as well when resetting channel 0. */ + if ((i == 0) && (dev->sb->dsp.sb_8_enable || dev->sb->dsp.sb_16_enable || dev->sb->dsp.sb_irq8 || dev->sb->dsp.sb_irq16)) + dev->sb->dsp.sb_8_enable = dev->sb->dsp.sb_16_enable = dev->sb->dsp.sb_irq8 = dev->sb->dsp.sb_irq16 = 0; + } else if (val & (0x01 << i)) { + /* Start DMA channel. */ + cmi8x38_log("CMI8x38: DMA %d trigger\n", i); + dev->dma[i].restart = 1; + cmi8x38_dma_process(&dev->dma[i]); } } - if (val & 0x08) - val &= ~0x02; + /* Clear reset bits. */ val &= 0x03; - dev->io_regs[addr] = val; - - /* Start DMA channels if requested. */ - if (val & 0x01) { - cmi8x38_log("CMI8x38: DMA 0 trigger\n"); - dev->dma[0].restart = 1; - cmi8x38_dma_process(&dev->dma[0]); - } - if (val & 0x02) { - cmi8x38_log("CMI8x38: DMA 1 trigger\n"); - dev->dma[1].restart = 1; - cmi8x38_dma_process(&dev->dma[1]); - } /* Start playback along with DMA channels. */ if (val & 0x03) cmi8x38_start_playback(dev); + + /* Update interrupts. */ + dev->io_regs[addr] = val; + cmi8x38_update_irqs(dev); break; case 0x04: @@ -1083,9 +1082,7 @@ cmi8x38_poll(void *priv) int16_t *out_l, *out_r, *out_ol, *out_or; /* o = opposite */ /* Schedule next run if playback is enabled. */ - if (dev->io_regs[0x00] & (1 << dma->id)) - dma->playback_enabled = 0; - else + if (dma->playback_enabled) timer_advance_u64(&dma->poll_timer, dma->timer_latch); /* Update audio buffer. */ @@ -1134,7 +1131,7 @@ cmi8x38_poll(void *priv) break; case 0x03: /* Stereo, 16-bit PCM */ - switch (dma->channels) { + switch (dma->channels) { /* multi-channel requires this data format */ case 2: if ((dma->fifo_end - dma->fifo_pos) >= 4) { *out_l = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]); @@ -1202,8 +1199,14 @@ cmi8x38_poll(void *priv) /* Feed silence if the FIFO is empty. */ *out_l = *out_r = 0; - return; + /* Stop playback if DMA is disabled. */ + if ((*((uint32_t *) &dev->io_regs[0x00]) & (0x00010001 << dma->id)) != (0x00010000 << dma->id)) { + cmi8x38_log("CMI8x38: Stopping playback of DMA channel %d\n", dma->id); + dma->playback_enabled = 0; + } + + return; n4spk3d: /* Mirror front and rear channels if requested. */ if (dev->io_regs[0x1b] & 0x04) { @@ -1347,12 +1350,11 @@ cmi8x38_reset(void *priv) /* Reset DMA channels. */ for (int i = 0; i < (sizeof(dev->dma) / sizeof(dev->dma[0])); i++) { dev->dma[i].playback_enabled = 0; - dev->dma[i].fifo_pos = dev->dma[i].fifo_end = 0; memset(dev->dma[i].fifo, 0, sizeof(dev->dma[i].fifo)); } - /* Reset legacy DMA channel. */ + /* Reset TDMA channel. */ dev->tdma_8 = 1; dev->tdma_16 = 5; dev->tdma_mask = 0; From 1650b0692d547517946513648068ccc1a65f6e6e Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Thu, 17 Mar 2022 16:31:14 -0300 Subject: [PATCH 58/62] CMI8x38: Fix for logging status bar segfault --- src/qt/qt_mainwindow.cpp | 2 +- src/qt/qt_progsettings.cpp | 2 +- src/sound/snd_cmi8x38.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/qt/qt_mainwindow.cpp b/src/qt/qt_mainwindow.cpp index 1d558c38b..ecfaef9f9 100644 --- a/src/qt/qt_mainwindow.cpp +++ b/src/qt/qt_mainwindow.cpp @@ -248,7 +248,7 @@ MainWindow::MainWindow(QWidget *parent) : connect(this, &MainWindow::updateStatusBarTip, status.get(), &MachineStatus::updateTip); connect(this, &MainWindow::updateStatusBarActivity, status.get(), &MachineStatus::setActivity); connect(this, &MainWindow::updateStatusBarEmpty, status.get(), &MachineStatus::setEmpty); - connect(this, &MainWindow::statusBarMessage, status.get(), &MachineStatus::message); + connect(this, &MainWindow::statusBarMessage, status.get(), &MachineStatus::message, Qt::QueuedConnection); ui->actionKeyboard_requires_capture->setChecked(kbd_req_capture); ui->actionRight_CTRL_is_left_ALT->setChecked(rctrl_is_lalt); diff --git a/src/qt/qt_progsettings.cpp b/src/qt/qt_progsettings.cpp index 9e39c4844..d5bfdaa2f 100644 --- a/src/qt/qt_progsettings.cpp +++ b/src/qt/qt_progsettings.cpp @@ -131,7 +131,7 @@ void ProgSettings::accept() connect(main_window, &MainWindow::updateStatusBarTip, main_window->status.get(), &MachineStatus::updateTip); connect(main_window, &MainWindow::updateStatusBarActivity, main_window->status.get(), &MachineStatus::setActivity); connect(main_window, &MainWindow::updateStatusBarEmpty, main_window->status.get(), &MachineStatus::setEmpty); - connect(main_window, &MainWindow::statusBarMessage, main_window->status.get(), &MachineStatus::message); + connect(main_window, &MainWindow::statusBarMessage, main_window->status.get(), &MachineStatus::message, Qt::QueuedConnection); QDialog::accept(); } diff --git a/src/sound/snd_cmi8x38.c b/src/sound/snd_cmi8x38.c index 1a7f690dd..2b690c627 100644 --- a/src/sound/snd_cmi8x38.c +++ b/src/sound/snd_cmi8x38.c @@ -1276,7 +1276,7 @@ cmi8x38_speed_changed(void *priv) dev->dma[i].timer_latch = (uint64_t) ((double) TIMER_USEC * freq); /* Calculate channel count and set DMA timer period. */ - if ((dev->type == CMEDIA_CMI8338) || (i == 0)) { + if ((dev->type == CMEDIA_CMI8338) || (i == 0)) { /* multi-channel requires channel 1 */ stereo: dev->dma[i].channels = 2; } else { @@ -1297,7 +1297,7 @@ stereo: freqreg >>= 3; } -#if 0 +#ifdef ENABLE_CMI8X38_LOG if (cmi8x38_do_log) ui_sb_bugui(buf); #endif From 9dbdcb752fd50e27a5757bd10bc8acd73129e302 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Thu, 17 Mar 2022 19:31:45 -0300 Subject: [PATCH 59/62] It's Qt --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 667aac765..1c7d22365 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -133,7 +133,7 @@ option(NEW_DYNAREC "Use the PCem v15 (\"new\") dynamic recompiler" option(MINITRACE "Enable Chrome tracing using the modified minitrace library" OFF) option(GDBSTUB "Enable GDB stub server for debugging" OFF) option(DEV_BRANCH "Development branch" OFF) -option(QT "QT GUI" ON) +option(QT "Qt GUI" ON) # Development branch features # From fd2682e352544ad52b3bc41c57aff2f4952daf94 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Thu, 17 Mar 2022 19:32:49 -0300 Subject: [PATCH 60/62] CMI8x38: Disable 128 KHz mode as that's not for our chips --- src/sound/snd_cmi8x38.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sound/snd_cmi8x38.c b/src/sound/snd_cmi8x38.c index 2b690c627..e24f70970 100644 --- a/src/sound/snd_cmi8x38.c +++ b/src/sound/snd_cmi8x38.c @@ -1263,9 +1263,11 @@ cmi8x38_speed_changed(void *priv) case 0x02: freq = 96000.0; break; +#if 0 case 0x03: freq = 128000.0; break; +#endif default: freq = freqs[freqreg & 0x07]; break; From e6976412aa40691adee98a1f4ec4776b2bce809a Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Fri, 18 Mar 2022 00:21:02 -0300 Subject: [PATCH 61/62] CMI8x38: Disable logging again --- src/sound/snd_cmi8x38.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sound/snd_cmi8x38.c b/src/sound/snd_cmi8x38.c index e24f70970..53ed17804 100644 --- a/src/sound/snd_cmi8x38.c +++ b/src/sound/snd_cmi8x38.c @@ -86,7 +86,6 @@ typedef struct _cmi8x38_ { int master_vol_l, master_vol_r, cd_vol_l, cd_vol_r; } cmi8x38_t; -#define ENABLE_CMI8X38_LOG 1 #ifdef ENABLE_CMI8X38_LOG int cmi8x38_do_log = ENABLE_CMI8X38_LOG; From 1da566e6734117156b462dde694edf01502dbdeb Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Fri, 18 Mar 2022 15:06:43 -0300 Subject: [PATCH 62/62] New Dynarec: Disable recompilation of some MMX opcodes on ARM, pending investigation --- src/codegen_new/codegen_ops.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/codegen_new/codegen_ops.c b/src/codegen_new/codegen_ops.c index a1570fc31..d59e3f1b3 100644 --- a/src/codegen_new/codegen_ops.c +++ b/src/codegen_new/codegen_ops.c @@ -85,8 +85,13 @@ RecompOpFn recomp_opcodes_0f[512] = /*40*/ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /*50*/ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +#if defined __ARM_EABI__ || defined _ARM_ || defined _M_ARM || defined __aarch64__ || defined _M_ARM64 +/*60*/ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +/*70*/ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +#else /*60*/ ropPUNPCKLBW, ropPUNPCKLWD, ropPUNPCKLDQ, ropPACKSSWB, ropPCMPGTB, ropPCMPGTW, ropPCMPGTD, ropPACKUSWB, ropPUNPCKHBW, ropPUNPCKHWD, ropPUNPCKHDQ, ropPACKSSDW, NULL, NULL, ropMOVD_r_d, ropMOVQ_r_q, /*70*/ NULL, ropPSxxW_imm, ropPSxxD_imm, ropPSxxQ_imm, ropPCMPEQB, ropPCMPEQW, ropPCMPEQD, NULL, NULL, NULL, NULL, NULL, NULL, NULL, ropMOVD_d_r, ropMOVQ_q_r, +#endif /*80*/ ropJO_16, ropJNO_16, ropJB_16, ropJNB_16, ropJE_16, ropJNE_16, ropJBE_16, ropJNBE_16, ropJS_16, ropJNS_16, ropJP_16, ropJNP_16, ropJL_16, ropJNL_16, ropJLE_16, ropJNLE_16, /*90*/ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, @@ -107,8 +112,13 @@ RecompOpFn recomp_opcodes_0f[512] = /*40*/ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /*50*/ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +#if defined __ARM_EABI__ || defined _ARM_ || defined _M_ARM || defined __aarch64__ || defined _M_ARM64 +/*60*/ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +/*70*/ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +#else /*60*/ ropPUNPCKLBW, ropPUNPCKLWD, ropPUNPCKLDQ, ropPACKSSWB, ropPCMPGTB, ropPCMPGTW, ropPCMPGTD, ropPACKUSWB, ropPUNPCKHBW, ropPUNPCKHWD, ropPUNPCKHDQ, ropPACKSSDW, NULL, NULL, ropMOVD_r_d, ropMOVQ_r_q, /*70*/ NULL, ropPSxxW_imm, ropPSxxD_imm, ropPSxxQ_imm, ropPCMPEQB, ropPCMPEQW, ropPCMPEQD, NULL, NULL, NULL, NULL, NULL, NULL, NULL, ropMOVD_d_r, ropMOVQ_q_r, +#endif /*80*/ ropJO_32, ropJNO_32, ropJB_32, ropJNB_32, ropJE_32, ropJNE_32, ropJBE_32, ropJNBE_32, ropJS_32, ropJNS_32, ropJP_32, ropJNP_32, ropJL_32, ropJNL_32, ropJLE_32, ropJNLE_32, /*90*/ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,