From 748622d4f23f2587f5d658ec8741ce8379da131d Mon Sep 17 00:00:00 2001 From: angelosa Date: Wed, 1 Jan 2025 13:30:36 +0100 Subject: amiga: refactor device names so that they references chipset names instead --- src/mame/amiga/8364_paula.cpp | 385 ---------------------- src/mame/amiga/8364_paula.h | 125 ------- src/mame/amiga/agnus_copper.cpp | 394 ++++++++++++++++++++++ src/mame/amiga/agnus_copper.h | 101 ++++++ src/mame/amiga/alg.cpp | 4 +- src/mame/amiga/amiga.cpp | 14 +- src/mame/amiga/amiga.h | 15 +- src/mame/amiga/amiga_copper.cpp | 395 ---------------------- src/mame/amiga/amiga_copper.h | 101 ------ src/mame/amiga/amiga_m.cpp | 38 +-- src/mame/amiga/amigafdc.cpp | 703 --------------------------------------- src/mame/amiga/amigafdc.h | 130 -------- src/mame/amiga/arsystems.cpp | 4 +- src/mame/amiga/cubo.cpp | 4 +- src/mame/amiga/mquake.cpp | 4 +- src/mame/amiga/paula.cpp | 390 ++++++++++++++++++++++ src/mame/amiga/paula.h | 120 +++++++ src/mame/amiga/paulafdc.cpp | 704 ++++++++++++++++++++++++++++++++++++++++ src/mame/amiga/paulafdc.h | 130 ++++++++ src/mame/amiga/upscope.cpp | 4 +- 20 files changed, 1881 insertions(+), 1884 deletions(-) delete mode 100644 src/mame/amiga/8364_paula.cpp delete mode 100644 src/mame/amiga/8364_paula.h create mode 100644 src/mame/amiga/agnus_copper.cpp create mode 100644 src/mame/amiga/agnus_copper.h delete mode 100644 src/mame/amiga/amiga_copper.cpp delete mode 100644 src/mame/amiga/amiga_copper.h delete mode 100644 src/mame/amiga/amigafdc.cpp delete mode 100644 src/mame/amiga/amigafdc.h create mode 100644 src/mame/amiga/paula.cpp create mode 100644 src/mame/amiga/paula.h create mode 100644 src/mame/amiga/paulafdc.cpp create mode 100644 src/mame/amiga/paulafdc.h diff --git a/src/mame/amiga/8364_paula.cpp b/src/mame/amiga/8364_paula.cpp deleted file mode 100644 index a06eed90ca4..00000000000 --- a/src/mame/amiga/8364_paula.cpp +++ /dev/null @@ -1,385 +0,0 @@ -// license: BSD-3-Clause -// copyright-holders: Aaron Giles, Dirk Best -/****************************************************************************** - - MOS Technology 8364 "Paula" - - TODO: - - Inherit FDC, serial and irq controller to here; - - Move Agnus "location" logic from here; - - low-pass filter; - - convert volume values to non-linear dB scale (cfr. ) - - Verify ADKCON modulation; - - Verify manual mode: - \- AGA roadkill during gameplay, which also has very long period setups, - extremely aliased; - - When a DMA stop occurs, is the correlated channel playback stopped - at the end of the current cycle or as soon as possible like current - implementation? - -******************************************************************************/ - -#include "emu.h" -#include "8364_paula.h" - -#define LIVE_AUDIO_VIEW 0 - -//#define VERBOSE 1 -#include "logmacro.h" -#include - - -//************************************************************************** -// DEVICE DEFINITIONS -//************************************************************************** - -DEFINE_DEVICE_TYPE(PAULA_8364, paula_8364_device, "paula_8364", "MOS 8364 \"Paula\"") - - -//************************************************************************* -// LIVE DEVICE -//************************************************************************** - -//------------------------------------------------- -// paula_8364_device - constructor -//------------------------------------------------- - -paula_8364_device::paula_8364_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, PAULA_8364, tag, owner, clock) - , device_sound_interface(mconfig, *this) - , m_chipmem_r(*this, 0) - , m_int_w(*this) - , m_stream(nullptr) -{ -} - -//------------------------------------------------- -// device_start - device-specific startup -//------------------------------------------------- - -void paula_8364_device::device_start() -{ - // initialize channels - for (int i = 0; i < 4; i++) - { - m_channel[i].index = i; - m_channel[i].irq_timer = timer_alloc(FUNC(paula_8364_device::signal_irq), this); - } - - // create the stream - m_stream = stream_alloc(0, 4, clock() / CLOCK_DIVIDER); -} - -void paula_8364_device::device_reset() -{ - m_dma_master_enable = false; - for (auto &chan : m_channel) - { - chan.loc = 0; - chan.len = 0; - chan.per = 0; - chan.vol = 0; - chan.curticks = 0; - chan.manualmode = false; - chan.curlocation = 0; - chan.curlength = 0; - chan.dma_enabled = false; - chan.atper = false; - chan.atvol = false; - } -} - -//------------------------------------------------- -// update - stream updater -//------------------------------------------------- - -void paula_8364_device::update() -{ - m_stream->update(); -} - - -//************************************************************************* -// IMPLEMENTATION -//************************************************************************** - -template void paula_8364_device::audio_channel_map(address_map &map) -{ - // TODO: location addresses belongs to Agnus - map(0x00, 0x01).w(FUNC(paula_8364_device::audxlch_w)); - map(0x02, 0x03).w(FUNC(paula_8364_device::audxlcl_w)); - map(0x04, 0x05).w(FUNC(paula_8364_device::audxlen_w)); - map(0x06, 0x07).w(FUNC(paula_8364_device::audxper_w)); - map(0x08, 0x09).w(FUNC(paula_8364_device::audxvol_w)); - map(0x0a, 0x0b).w(FUNC(paula_8364_device::audxdat_w)); -} - -// Instantiate channel maps -template void paula_8364_device::audio_channel_map<0>(address_map &map); -template void paula_8364_device::audio_channel_map<1>(address_map &map); -template void paula_8364_device::audio_channel_map<2>(address_map &map); -template void paula_8364_device::audio_channel_map<3>(address_map &map); - -template void paula_8364_device::audxlch_w(u16 data) -{ - m_stream->update(); - // TODO: chipmem mask - m_channel[ch].loc = (m_channel[ch].loc & 0x0000ffff) | ((data & 0x001f) << 16); -} - -template void paula_8364_device::audxlcl_w(u16 data) -{ - m_stream->update(); - m_channel[ch].loc = (m_channel[ch].loc & 0xffff0000) | ((data & 0xfffe) << 0); -} - -template void paula_8364_device::audxlen_w(u16 data) -{ - m_stream->update(); - m_channel[ch].len = data; -} - -template void paula_8364_device::audxper_w(u16 data) -{ - m_stream->update(); - m_channel[ch].per = data; -} - -template void paula_8364_device::audxvol_w(u16 data) -{ - m_stream->update(); - m_channel[ch].vol = data & 0x7f; -} - -template void paula_8364_device::audxdat_w(u16 data) -{ - m_stream->update(); - m_channel[ch].dat = data; - m_channel[ch].manualmode = true; -} - -void paula_8364_device::dmacon_set(u16 data) -{ - m_stream->update(); - - m_dma_master_enable = bool(BIT(data, 9)); - - // update the DMA latches on each channel and reload if fresh - // This holds true particularly for Ocean games (bchvolly, lostpatr, pang) and waylildr: - // they sets a DMA length for a channel then enable DMA finally resets that length to 1 - // after a short delay loop. - for (int channum = 0; channum < 4; channum++) - { - audio_channel *chan = &m_channel[channum]; - if (!chan->dma_enabled && ((data >> channum) & 1)) - dma_reload(chan, true); - - chan->dma_enabled = bool(BIT(data, channum)); - } -} - -void paula_8364_device::adkcon_set(u16 data) -{ - m_stream->update(); - - for (int channum = 0; channum < 4; channum++) - { - audio_channel *chan = &m_channel[channum]; - - chan->atper = bool(BIT(data, channum + 4)); - chan->atvol = bool(BIT(data, channum)); - } -} - -//------------------------------------------------- -// signal_irq - irq signaling -//------------------------------------------------- - -TIMER_CALLBACK_MEMBER( paula_8364_device::signal_irq ) -{ - m_int_w(param, 1); -} - -//------------------------------------------------- -// dma_reload -//------------------------------------------------- - -void paula_8364_device::dma_reload(audio_channel *chan, bool startup) -{ - chan->curlocation = chan->loc; - // TODO: Unconfirmed, assume max size if length is 0. - // cfr. wrestlmn and videokid, where they pratically never get even close to buffer exhaustion. - chan->curlength = chan->len == 0 ? 0x10000 : chan->len; - // TODO: on startup=false irq should be delayed two cycles - if (startup) - chan->irq_timer->adjust(attotime::from_hz(15750), chan->index); // clock() / 227 - else - signal_irq(chan->index); - - LOG("dma_reload(%d): offs=%06X len=%04X\n", chan->index, chan->curlocation, chan->curlength); -} - -std::string paula_8364_device::print_audio_state() -{ - std::ostringstream outbuffer; - - util::stream_format(outbuffer, "DMA master %d\n", m_dma_master_enable); - for (auto &chan : m_channel) - { - util::stream_format(outbuffer, "%d DMA (%d) ADK (%d%d) REGS: %06x %04x %03x %02x %d LIVE: %06x %04x %d\n" - , chan.index - , chan.dma_enabled - , chan.atper - , chan.atvol - , chan.loc - , chan.len - , chan.per - , chan.vol - , chan.manualmode - , chan.curlocation - , chan.curlength - , chan.dma_enabled - ); - } - - return outbuffer.str(); -} - -//------------------------------------------------- -// sound_stream_update - handle a stream update -//------------------------------------------------- - -void paula_8364_device::sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) -{ - int channum, sampoffs = 0; - - // if all DMA off, disable all channels - if (m_dma_master_enable == false) - { - m_channel[0].dma_enabled = - m_channel[1].dma_enabled = - m_channel[2].dma_enabled = - m_channel[3].dma_enabled = false; - - // clear the sample data to 0 - for (channum = 0; channum < 4; channum++) - outputs[channum].fill(0); - return; - } - - int samples = outputs[0].samples() * CLOCK_DIVIDER; - - if (LIVE_AUDIO_VIEW) - popmessage(print_audio_state()); - - // loop until done - while (samples > 0) - { - int nextper, nextvol; - int ticks = samples; - - // determine the number of ticks we can do in this chunk - if (ticks > m_channel[0].curticks) - ticks = m_channel[0].curticks; - if (ticks > m_channel[1].curticks) - ticks = m_channel[1].curticks; - if (ticks > m_channel[2].curticks) - ticks = m_channel[2].curticks; - if (ticks > m_channel[3].curticks) - ticks = m_channel[3].curticks; - - // loop over channels - nextper = nextvol = -1; - for (channum = 0; channum < 4; channum++) - { - audio_channel *chan = &m_channel[channum]; - int volume = (nextvol == -1) ? chan->vol : nextvol; - int period = (nextper == -1) ? chan->per : nextper; - s32 sample; - int i; - - // normalize the volume value - // FIXME: definitely not linear - volume = (volume & 0x40) ? 64 : (volume & 0x3f); - volume *= 4; - - // are we modulating the period of the next channel? - if (chan->atper) - { - nextper = chan->dat; - nextvol = -1; - sample = 0; - } - - // are we modulating the volume of the next channel? - else if (chan->atvol) - { - nextper = -1; - nextvol = chan->dat; - sample = 0; - } - - // otherwise, we are generating data - else - { - nextper = nextvol = -1; - sample = chan->latched * volume; - } - - // fill the buffer with the sample - for (i = 0; i < ticks; i += CLOCK_DIVIDER) - outputs[channum].put_int((sampoffs + i) / CLOCK_DIVIDER, sample, 32768); - - // account for the ticks; if we hit 0, advance - chan->curticks -= ticks; - if (chan->curticks == 0) - { - // reset the clock and ensure we're above the minimum ticks - chan->curticks = period; - if (chan->curticks < 124) - chan->curticks = 124; - - // move forward one byte; if we move to an even byte, fetch new - if (chan->dma_enabled || chan->manualmode) - chan->curlocation++; - if (chan->dma_enabled && !(chan->curlocation & 1)) - { - chan->dat = m_chipmem_r(chan->curlocation); - - if (chan->curlength != 0) - chan->curlength--; - - // if we run out of data, reload the dma and signal an IRQ, - // gpmaster/asparmgp definitely expects this - // (uses channel 3 as a sequencer, changing the start address on the fly) - if (chan->curlength == 0) - { - dma_reload(chan, false); - // reload the data pointer, otherwise aliasing / buzzing outside the given buffer will be heard - // For example: Xenon 2 sets up location=0x63298 length=0x20 - // for silencing channels on-the-fly without relying on irqs. - // Without this the location will read at 0x632d8 (data=0x7a7d), causing annoying buzzing. - chan->dat = m_chipmem_r(chan->curlocation); - } - } - - // latch the next byte of the sample - if (!(chan->curlocation & 1)) - chan->latched = chan->dat >> 8; - else - chan->latched = chan->dat >> 0; - - // if we're in manual mode, signal an interrupt once we latch the low byte - if (!chan->dma_enabled && chan->manualmode && (chan->curlocation & 1)) - { - signal_irq(channum); - chan->manualmode = false; - } - } - } - - // bump ourselves forward by the number of ticks - sampoffs += ticks; - samples -= ticks; - } -} diff --git a/src/mame/amiga/8364_paula.h b/src/mame/amiga/8364_paula.h deleted file mode 100644 index ffbfaaffa79..00000000000 --- a/src/mame/amiga/8364_paula.h +++ /dev/null @@ -1,125 +0,0 @@ -// license: BSD-3-Clause -// copyright-holders: Aaron Giles, Dirk Best -/*************************************************************************** - - Commodore 8364 "Paula" - - Multi-purpose chip that is part of the Amiga chipset. The name "Paula" - is derived from "Ports, Audio, UART and Logic". It features 4-channel - DMA driven audio, the floppy controller, a serial receiver/transmitter, - analog inputs and contains the interrupt controller. - - ____ ____ - D8 1 |* u | 48 D9 - D7 2 | | 47 D10 - D6 3 | | 46 D11 - D5 4 | | 45 D12 - D4 5 | | 44 D13 - D3 6 | | 43 D14 - D2 7 | | 42 D15 - GND 8 | | 41 RXD - D1 9 | | 40 TXD - D0 10 | | 39 DKWE - /RES 11 | | 38 /DKWD - DMAL 12 | | 37 /DKRD - /IPL0 13 | | 36 POT1Y - /IPL1 14 | | 35 POT1X - /IPL2 15 | | 34 ANAGND - /INT2 16 | | 33 POT0Y - /INT3 17 | | 32 POT0X - /INT6 18 | | 31 AUDL - RGA8 19 | | 30 AUDR - RGA7 20 | | 29 CCKQ - RGA6 21 | | 28 CCK - RGA5 22 | | 27 VCC - RGA4 23 | | 26 RGA1 - RGA3 24 |_________| 25 RGA2 - -***************************************************************************/ - -#ifndef MAME_AMIGA_8364_PAULA_H -#define MAME_AMIGA_8364_PAULA_H - -#pragma once - - -//************************************************************************** -// TYPE DEFINITIONS -//************************************************************************** - -// ======================> paula_8364_device - -class paula_8364_device : public device_t, public device_sound_interface -{ -public: - paula_8364_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - - // configuration - auto mem_read_cb() { return m_chipmem_r.bind(); } - auto int_cb() { return m_int_w.bind(); } - - void update(); - - template void audio_channel_map(address_map &map) ATTR_COLD; - void dmacon_set(u16 data); - void adkcon_set(u16 data); - -protected: - // device-level overrides - virtual void device_start() override ATTR_COLD; - virtual void device_reset() override ATTR_COLD; - - // sound stream update overrides - virtual void sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) override; - -private: - template void audxlch_w(u16 data); - template void audxlcl_w(u16 data); - template void audxlen_w(u16 data); - template void audxper_w(u16 data); - template void audxvol_w(u16 data); - template void audxdat_w(u16 data); - - static constexpr int CLOCK_DIVIDER = 16; - - struct audio_channel - { - emu_timer *irq_timer; - uint32_t curlocation; - uint32_t curlength; - uint16_t curticks; - uint8_t index; - bool dma_enabled; - bool manualmode; - int8_t latched; - - // custom chip registers - uint32_t loc; // to be moved, not part of paula - uint16_t len; - uint16_t per; - uint16_t vol; - uint16_t dat; - bool atper; - bool atvol; - }; - - bool m_dma_master_enable; - - void dma_reload(audio_channel *chan, bool startup); - - // callbacks - devcb_read16 m_chipmem_r; - devcb_write8 m_int_w; - - audio_channel m_channel[4]; - sound_stream *m_stream; - - TIMER_CALLBACK_MEMBER( signal_irq ); - - std::string print_audio_state(); -}; - -// device type definition -DECLARE_DEVICE_TYPE(PAULA_8364, paula_8364_device) - -#endif // MAME_DEVICES_MACHINE_8364_PAULA_H diff --git a/src/mame/amiga/agnus_copper.cpp b/src/mame/amiga/agnus_copper.cpp new file mode 100644 index 00000000000..d321ae70cf7 --- /dev/null +++ b/src/mame/amiga/agnus_copper.cpp @@ -0,0 +1,394 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles, Angelo Salese +/************************************************************************************************** + + Amiga Copper + + "Co-processor" contained inside Agnus, + it's a finite-state machine that either wait to a h/v video beam position or direct writes to + the Amiga chipset registers via program lists, ranging from "simple" video mode/color changes + to override sprite structures and beyond ... + + TODO: + - Current handling is horizontally offset by several pixels, also cfr. amiga video notes + (screen geometry slightly incorrect?); + - Fix Bitplane offset corruption in some games (e.g. exile, zoola, AGA games). + Same as above? + - Fix missing/corrupt sprites in known nasty examples + (e.g. zoola status bar, parasol score layer on top, riskyw backgrounds); + - Find & verify cdang examples (especially for ECS/AGA); + - Find & verify examples that uses this non-canonically, + i.e. anything that may use this for controlling Paula, FDC or Blitter; + - Add debugger command for printing the current disassembler structure + (current live logging is painfully slow in places, cfr. lweapon); + +**************************************************************************************************/ + +#include "emu.h" +#include "agnus_copper.h" + +#define LOG_WARN (1U << 1) // Show warnings +#define LOG_COPINS (1U << 2) // Show instruction fetches thru COPINS +#define LOG_INST (1U << 3) // Show live instruction fetches +#define LOG_PC (1U << 4) // Show PC fetches +#define LOG_CHIPSET (1U << 5) // Show custom chipset writes + +#define VERBOSE (LOG_WARN) +//#define VERBOSE (LOG_WARN | LOG_CHIPSET | LOG_PC | LOG_INST) +//#define LOG_OUTPUT_FUNC osd_printf_info + +#include "logmacro.h" + +#define LOGWARN(...) LOGMASKED(LOG_WARN, __VA_ARGS__) +#define LOGCOPINS(...) LOGMASKED(LOG_COPINS, __VA_ARGS__) +#define LOGINST(...) LOGMASKED(LOG_INST, __VA_ARGS__) +#define LOGPC(...) LOGMASKED(LOG_PC, __VA_ARGS__) +#define LOGCHIPSET(...) LOGMASKED(LOG_CHIPSET, __VA_ARGS__) + +// TODO: legacy inheritance, to be verified +#define COPPER_CYCLES_TO_PIXELS(x) (4 * (x)) + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + + +// device type definition +DEFINE_DEVICE_TYPE(AGNUS_COPPER, agnus_copper_device, "agnus_copper", "Amiga Agnus Copper") + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + + +//------------------------------------------------- +// agnus_copper_device - constructor +//------------------------------------------------- + + +agnus_copper_device::agnus_copper_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, AGNUS_COPPER, tag, owner, clock) + , m_host_cpu(*this, finder_base::DUMMY_TAG) + , m_chipmem_r(*this, 0) +{ +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + + +void agnus_copper_device::device_start() +{ + m_host_space = &m_host_cpu->space(AS_PROGRAM); + + save_item(NAME(m_cdang_setting)); + save_item(NAME(m_cdang_min_reg)); + save_item(NAME(m_dma_master_enable)); + save_item(NAME(m_dma_copen)); + save_pointer(NAME(m_lc), 2); + save_item(NAME(m_pc)); + save_item(NAME(m_state_waiting)); + save_item(NAME(m_state_waitblit)); + save_item(NAME(m_waitval)); + save_item(NAME(m_waitmask)); + save_item(NAME(m_pending_data)); + save_item(NAME(m_pending_offset)); +} + + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + + +void agnus_copper_device::device_reset() +{ + m_cdang_setting = 0x40; + m_dma_master_enable = false; + m_dma_copen = false; + // TODO: latches states on soft reset +} + + +//************************************************************************** +// I/Os +//************************************************************************** + +// $dff080-8d memory map +void agnus_copper_device::regs_map(address_map &map) +{ + // TODO: location addresses belongs to Agnus + map(0x00, 0x01).w(FUNC(agnus_copper_device::copxlch_w<0>)); + map(0x02, 0x03).w(FUNC(agnus_copper_device::copxlcl_w<0>)); + map(0x04, 0x05).w(FUNC(agnus_copper_device::copxlch_w<1>)); + map(0x06, 0x07).w(FUNC(agnus_copper_device::copxlcl_w<1>)); + map(0x08, 0x09).rw(FUNC(agnus_copper_device::copjmpx_r<0>), FUNC(agnus_copper_device::copjmpx_w<0>)); + map(0x0a, 0x0b).rw(FUNC(agnus_copper_device::copjmpx_r<1>), FUNC(agnus_copper_device::copjmpx_w<1>)); +// map(0x0c, 0x0d).w(FUNC(agnus_copper_device::copins_w)); +} + +void agnus_copper_device::dmacon_set(u16 data) +{ + m_dma_master_enable = bool(BIT(data, 9)); + m_dma_copen = bool(BIT(data, 7)); +} + +/* + * COPCON 02E W A Copper Control Register + * + * ---- ---- ---- --x- CDANG (Copper Danger Mode) setting + * + * Documentation is quite contradictory here. + * This bit is supposed to allow Copper access of the + * lowest registers. OriginaL HRM states that it gives + * access to the blitter HW therefore $dff080 is the + * minimum for non-cdang and $dff040 for cdang mode. + * + * In ECS and AGA the latter limitation is lifted so Copper + * can access $dff000-$dff03f too, which basically means the + * possibility of accessing disk block regs. + * (i.e. the other regs are either r/o or wouldn't have much + * sense to write via Copper). + * + */ +void agnus_copper_device::copcon_w(u16 data) +{ + bool cdang = bool(BIT(data, 1)); + + // TODO: is min_reg working even with BPLCON0 bit 0 unset? + m_cdang_setting = cdang ? m_cdang_min_reg : 0x40; + if (cdang) + LOGWARN("%s: cdang enabled\n", machine().describe_context()); + if (data & 0xfffd) + LOGWARN("%s: COPCON undocumented setting write %04x\n", machine().describe_context(), data); +} + +template void agnus_copper_device::copxlch_w(u16 data) +{ + // TODO: chipmem mask + m_lc[ch] = (m_lc[ch] & 0x0000ffff) | ((data & 0x001f) << 16); +} + +template void agnus_copper_device::copxlcl_w(u16 data) +{ + m_lc[ch] = (m_lc[ch] & 0xffff0000) | ((data & 0xfffe) << 0); +} + +/* + * COPJMP1 088 S A Copper restart at first location + * COPJMP2 08a S A Copper restart at second location + * + * Strobe register that loads the new PC into Copper. + * NB: Copper can use this to control its program flow and even + * do conditional branching by clever use of the skip opcode. + * + */ +template void agnus_copper_device::copjmpx_w(u16 data) +{ + set_pc(ch, false); +} + +template u16 agnus_copper_device::copjmpx_r() +{ + if (!machine().side_effects_disabled()) + set_pc(ch, false); + return m_host_space->unmap(); +} + +inline void agnus_copper_device::set_pc(u8 ch, bool is_sync) +{ + m_pc = m_lc[ch]; + m_state_waiting = false; + LOGPC("%s: COPJMP%d new PC = %08x%s\n" + , machine().describe_context() + , ch + 1 + , m_pc + , is_sync ? " (resync)" : "" + ); +} + +/* + * COPINS 08c W A Copper instruction fetch identify + * + * Apparently this register is pinged every time that + * the Copper internally decodes an instruction value. + * Shouldn't have any implementation detail connected, + * de-facto it's a mailbox that can be listened by external HW + * (such as our debugger ;=). + * + */ +void agnus_copper_device::copins_w(u16 data) +{ + LOGCOPINS("%s: COPINS %04x\n", machine().describe_context(), data); +} + +//************************************************************************** +// Implementation getters/setters +//************************************************************************** + +// executed on scanline == 0 +void agnus_copper_device::vblank_sync() +{ + set_pc(0, true); +} + +// TODO: h/vblank checks against xpos/vpos +int agnus_copper_device::execute_next(int xpos, int ypos, bool is_blitter_busy, int num_planes) +{ + int word0, word1; + + /* bail if not enabled */ + if (!m_dma_master_enable || !m_dma_copen) + return 511; + + /* flush any pending writes */ + if (m_pending_offset) + { + //LOGCHIPSET("%02X.%02X: Write to %s = %04x\n", ypos, xpos / 2, s_custom_reg_names[m_copper_pending_offset & 0xff], m_copper_pending_data); + LOGCHIPSET("%02X.%02X: MOVE $dff%03x = %04x\n", + ypos, + xpos / 2, + (m_pending_offset << 1), + m_pending_data + ); + m_host_space->write_word(0xdff000 | (m_pending_offset << 1), m_pending_data); + m_pending_offset = 0; + } + + /* if we're waiting, check for a breakthrough */ + if (m_state_waiting) + { + int curpos = (ypos << 8) | (xpos >> 1); + + /* if we're past the wait time, stop it and hold up 2 cycles */ + if ((curpos & m_waitmask) >= (m_waitval & m_waitmask) && + (!m_state_waitblit || !(is_blitter_busy))) + { + m_state_waiting = false; + return xpos + COPPER_CYCLES_TO_PIXELS(1); + } + + /* otherwise, see if this line is even a possibility; if not, punt */ + if (((curpos | 0xff) & m_waitmask) < (m_waitval & m_waitmask)) + return 511; + + /* else just advance another pixel */ + xpos += COPPER_CYCLES_TO_PIXELS(1); + return xpos; + } + + /* fetch the first data word */ + // TODO: swap between ir0 and ir1 is controlled thru a selins latch + // (which can't be this instant too) + word0 = m_chipmem_r(m_pc); + m_host_space->write_word(0xdff08c, word0); + m_pc += 2; + xpos += COPPER_CYCLES_TO_PIXELS(1); + + /* fetch the second data word */ + word1 = m_chipmem_r(m_pc); + m_host_space->write_word(0xdff08c, word1); + m_pc += 2; + xpos += COPPER_CYCLES_TO_PIXELS(1); + + LOGINST("%02X.%02X: Copper inst @ %06x = %04x %04x\n", + ypos, + xpos / 2, + m_pc - 4, + word0, + word1 + ); + + /* handle a move */ + if ((word0 & 1) == 0) + { + /* do the write if we're allowed */ + word0 = (word0 >> 1) & 0xff; + if (word0 >= m_cdang_setting) + { + if (delay[word0] == 0) + { + //LOGCHIPSET("%02X.%02X: Write to %s = %04x\n", ypos, xpos / 2, s_custom_reg_names[word0 & 0xff], word1); + LOGCHIPSET("%02X.%02X: MOVE $dff%03x = %04x\n", + ypos, + xpos / 2, + word0 << 1, + word1 + ); + m_host_space->write_word(0xdff000 | (word0 << 1), word1); + } + else // additional 2 cycles needed for non-Agnus registers + { + m_pending_offset = word0; + m_pending_data = word1; + } + } + + /* illegal writes suspend until next frame */ + else + { + LOGWARN("%02X.%02X: Aborting copper on illegal write\n", ypos, xpos / 2); + + m_waitval = 0xffff; + m_waitmask = 0xffff; + m_state_waitblit = false; + m_state_waiting = true; + + return 511; + } + } + else + { + /* extract common wait/skip values */ + m_waitval = word0 & 0xfffe; + + m_waitmask = word1 | 0x8001; + m_state_waitblit = (~word1 >> 15) & 1; + + /* handle a wait */ + if ((word1 & 1) == 0) + { + const int wait_offset = std::max(num_planes - 4, 0) + 1; + + LOGINST(" WAIT %04x & %04x (currently %04x, num planes %d +%d)\n", + m_waitval, + m_waitmask, + (ypos << 8) | (xpos >> 1), + num_planes, + wait_offset + ); + + m_state_waiting = true; + xpos += COPPER_CYCLES_TO_PIXELS(wait_offset); + } + + /* handle a skip */ + else + { + int curpos = (ypos << 8) | (xpos >> 1); + + LOGINST(" SKIP %04x & %04x (currently %04x)\n", + m_waitval, + m_waitmask, + (ypos << 8) | (xpos >> 1) + ); + + /* if we're past the wait time, stop it and hold up 2 cycles */ + if ((curpos & m_waitmask) >= (m_waitval & m_waitmask) && + (!m_state_waitblit || !(is_blitter_busy))) + { + LOGINST(" Skipped\n"); + + /* count the cycles it out have taken to fetch the next instruction */ + m_pc += 4; + xpos += COPPER_CYCLES_TO_PIXELS(2); + } + } + } + + /* advance and consume 8 cycles */ + return xpos; +} diff --git a/src/mame/amiga/agnus_copper.h b/src/mame/amiga/agnus_copper.h new file mode 100644 index 00000000000..c60ab18c980 --- /dev/null +++ b/src/mame/amiga/agnus_copper.h @@ -0,0 +1,101 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles, Angelo Salese +/*************************************************************************** + + Amiga Copper + +***************************************************************************/ + +#ifndef MAME_AMIGA_AGNUS_COPPER_H +#define MAME_AMIGA_AGNUS_COPPER_H + +#pragma once + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +class agnus_copper_device : public device_t +{ +public: + // construction/destruction + agnus_copper_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + // config + template void set_host_cpu_tag(T &&tag) { m_host_cpu.set_tag(std::forward(tag)); } + auto mem_read_cb() { return m_chipmem_r.bind(); } + void set_ecs_mode(bool ecs_mode) { m_cdang_min_reg = ecs_mode ? 0x00 : 0x20; } + + // I/O operations + void regs_map(address_map &map) ATTR_COLD; + void dmacon_set(u16 data); + void copcon_w(u16 data); + + // getters/setters + void vblank_sync(); + int execute_next(int xpos, int ypos, bool is_blitter_busy, int num_planes); + +protected: + // device-level overrides + //virtual void device_validity_check(validity_checker &valid) const override; + //virtual void device_add_mconfig(machine_config &config) override ATTR_COLD; + virtual void device_start() override ATTR_COLD; + virtual void device_reset() override ATTR_COLD; + +private: + required_device m_host_cpu; + address_space *m_host_space = nullptr; + // callbacks + devcb_read16 m_chipmem_r; + + bool m_dma_master_enable, m_dma_copen; + u16 m_cdang_setting, m_cdang_min_reg; + u32 m_lc[2], m_pc; + + template void copxlch_w(u16 data); + template void copxlcl_w(u16 data); + template void copjmpx_w(u16 data); + template u16 copjmpx_r(); +public: + void copins_w(u16 data); +private: + void set_pc(u8 ch, bool is_sync); + + // internal state + bool m_state_waiting; + bool m_state_waitblit; + u16 m_waitval; + u16 m_waitmask; + u16 m_pending_offset; + u16 m_pending_data; + + // waitstate delays for copper + // basically anything that doesn't belong to Angus has a penalty for Copper + static constexpr u16 delay[256] = + { + 1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0, /* 0x000 - 0x03e */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x040 - 0x05e */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x060 - 0x07e */ + 0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0, /* 0x080 - 0x09e */ + 1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0, /* 0x0a0 - 0x0de */ + /* BPLxPTH/BPLxPTL */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x0e0 - 0x0fe */ + /* BPLCON0-3,BPLMOD1-2 */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x100 - 0x11e */ + /* SPRxPTH/SPRxPTL */ + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 0x120 - 0x13e */ + /* SPRxPOS/SPRxCTL/SPRxDATA/SPRxDATB */ + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 0x140 - 0x17e */ + /* COLORxx */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x180 - 0x1be */ + /* RESERVED */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* 0x1c0 - 0x1fe */ + }; + +}; + + +// device type definition +DECLARE_DEVICE_TYPE(AGNUS_COPPER, agnus_copper_device) + +#endif // MAME_AMIGA_AGNUS_COPPER_H diff --git a/src/mame/amiga/alg.cpp b/src/mame/amiga/alg.cpp index 3fedac044f2..cc9cfb843ea 100644 --- a/src/mame/amiga/alg.cpp +++ b/src/mame/amiga/alg.cpp @@ -340,7 +340,7 @@ void alg_state::alg_r1(machine_config &config) // Video hardware ntsc_video(config); - AMIGA_COPPER(config, m_copper, amiga_state::CLK_7M_NTSC); + AGNUS_COPPER(config, m_copper, amiga_state::CLK_7M_NTSC); m_copper->set_host_cpu_tag(m_maincpu); m_copper->mem_read_cb().set(FUNC(amiga_state::chip_ram_r)); m_copper->set_ecs_mode(false); @@ -379,7 +379,7 @@ void alg_state::alg_r1(machine_config &config) MOS8520(config, m_cia_1, amiga_state::CLK_E_NTSC); m_cia_1->irq_wr_callback().set(FUNC(amiga_state::cia_1_irq)); - AMIGA_FDC(config, m_fdc, amiga_state::CLK_7M_NTSC); + PAULA_FDC(config, m_fdc, amiga_state::CLK_7M_NTSC); m_fdc->index_callback().set("cia_1", FUNC(mos8520_device::flag_w)); m_fdc->read_dma_callback().set(FUNC(amiga_state::chip_ram_r)); m_fdc->write_dma_callback().set(FUNC(amiga_state::chip_ram_w)); diff --git a/src/mame/amiga/amiga.cpp b/src/mame/amiga/amiga.cpp index f4f6c8a4eb6..39829d971c0 100644 --- a/src/mame/amiga/amiga.cpp +++ b/src/mame/amiga/amiga.cpp @@ -1646,7 +1646,7 @@ void amiga_state::amiga_base(machine_config &config) m_cia_1->irq_wr_callback().set(FUNC(amiga_state::cia_1_irq)); m_cia_1->pa_rd_callback().set(FUNC(amiga_state::cia_1_port_a_read)); m_cia_1->pa_wr_callback().set(FUNC(amiga_state::cia_1_port_a_write)); - m_cia_1->pb_wr_callback().set(m_fdc, FUNC(amiga_fdc_device::ciaaprb_w)); + m_cia_1->pb_wr_callback().set(m_fdc, FUNC(paula_fdc_device::ciaaprb_w)); // audio SPEAKER(config, "lspeaker").front_left(); @@ -1660,20 +1660,20 @@ void amiga_state::amiga_base(machine_config &config) m_paula->int_cb().set(FUNC(amiga_state::paula_int_w)); // floppy drives - AMIGA_FDC(config, m_fdc, amiga_state::CLK_7M_PAL); + PAULA_FDC(config, m_fdc, amiga_state::CLK_7M_PAL); m_fdc->index_callback().set(m_cia_1, FUNC(mos8520_device::flag_w)); m_fdc->read_dma_callback().set(FUNC(amiga_state::chip_ram_r)); m_fdc->write_dma_callback().set(FUNC(amiga_state::chip_ram_w)); m_fdc->dskblk_callback().set(FUNC(amiga_state::fdc_dskblk_w)); m_fdc->dsksyn_callback().set(FUNC(amiga_state::fdc_dsksyn_w)); - FLOPPY_CONNECTOR(config, "fdc:0", amiga_floppies, "35dd", amiga_fdc_device::floppy_formats).enable_sound(true); - FLOPPY_CONNECTOR(config, "fdc:1", amiga_floppies, nullptr, amiga_fdc_device::floppy_formats).enable_sound(true); - FLOPPY_CONNECTOR(config, "fdc:2", amiga_floppies, nullptr, amiga_fdc_device::floppy_formats).enable_sound(true); - FLOPPY_CONNECTOR(config, "fdc:3", amiga_floppies, nullptr, amiga_fdc_device::floppy_formats).enable_sound(true); + FLOPPY_CONNECTOR(config, "fdc:0", amiga_floppies, "35dd", paula_fdc_device::floppy_formats).enable_sound(true); + FLOPPY_CONNECTOR(config, "fdc:1", amiga_floppies, nullptr, paula_fdc_device::floppy_formats).enable_sound(true); + FLOPPY_CONNECTOR(config, "fdc:2", amiga_floppies, nullptr, paula_fdc_device::floppy_formats).enable_sound(true); + FLOPPY_CONNECTOR(config, "fdc:3", amiga_floppies, nullptr, paula_fdc_device::floppy_formats).enable_sound(true); // TODO: shouldn't have a clock // (finite state machine, controlled by Agnus beams) - AMIGA_COPPER(config, m_copper, amiga_state::CLK_7M_PAL); + AGNUS_COPPER(config, m_copper, amiga_state::CLK_7M_PAL); m_copper->set_host_cpu_tag(m_maincpu); m_copper->mem_read_cb().set(FUNC(amiga_state::chip_ram_r)); m_copper->set_ecs_mode(false); diff --git a/src/mame/amiga/amiga.h b/src/mame/amiga/amiga.h index a8d02c131dc..0f6c6d1e307 100644 --- a/src/mame/amiga/amiga.h +++ b/src/mame/amiga/amiga.h @@ -25,11 +25,11 @@ Ernesto Corvi & Mariusz Wojcieszek #include "machine/msm6242.h" #include "machine/i2cmem.h" -#include "8364_paula.h" +#include "agnus_copper.h" #include "akiko.h" #include "amigaaga.h" -#include "amigafdc.h" -#include "amiga_copper.h" +#include "paula.h" +#include "paulafdc.h" #include "emupal.h" #include "screen.h" @@ -307,8 +307,6 @@ Ernesto Corvi & Mariusz Wojcieszek #define INTENA_INTEN 0x4000 #define INTENA_SETCLR 0x8000 -#define MAX_PLANES 6 /* 0 to 6, inclusive ( but we count from 0 to 5 ) */ - class amiga_state : public driver_device { @@ -409,7 +407,6 @@ public: uint16_t m_copper_waitmask = 0; uint16_t m_copper_pending_offset = 0; uint16_t m_copper_pending_data = 0; - int m_wait_offset = 0; /* playfield states */ int m_last_scanline = 0; @@ -588,9 +585,9 @@ protected: required_device m_cia_1; optional_device m_rs232; optional_device m_centronics; - required_device m_copper; - required_device m_paula; - optional_device m_fdc; + required_device m_copper; + required_device m_paula; + optional_device m_fdc; required_device m_screen; optional_device m_palette; required_device m_overlay; diff --git a/src/mame/amiga/amiga_copper.cpp b/src/mame/amiga/amiga_copper.cpp deleted file mode 100644 index f110edb3632..00000000000 --- a/src/mame/amiga/amiga_copper.cpp +++ /dev/null @@ -1,395 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Aaron Giles, Angelo Salese -/************************************************************************************************** - - Amiga Copper - - "Co-processor" contained inside Agnus, - it's a finite-state machine that either wait to a h/v video beam position or direct writes to - the Amiga chipset registers via program lists, ranging from "simple" video mode/color changes - to override sprite structures and beyond ... - - TODO: - - Current handling is horizontally offset by several pixels, also cfr. amiga video notes - (screen geometry slightly incorrect?); - - Fix Bitplane offset corruption in some games (e.g. exile, zoola, AGA games). - Same as above? - - Fix missing/corrupt sprites in known nasty examples - (e.g. zoola status bar, parasol score layer on top, riskyw backgrounds); - - Find & verify cdang examples (especially for ECS/AGA); - - Find & verify examples that uses this non-canonically, - i.e. anything that may use this for controlling Paula, FDC or Blitter; - - Add debugger command for printing the current disassembler structure - (current live logging is painfully slow in places, cfr. lweapon); - -**************************************************************************************************/ - -#include "emu.h" -#include "amiga_copper.h" - -#define LOG_WARN (1U << 1) // Show warnings -#define LOG_COPINS (1U << 2) // Show instruction fetches thru COPINS -#define LOG_INST (1U << 3) // Show live instruction fetches -#define LOG_PC (1U << 4) // Show PC fetches -#define LOG_CHIPSET (1U << 5) // Show custom chipset writes - -#define VERBOSE (LOG_WARN) -//#define VERBOSE (LOG_WARN | LOG_CHIPSET | LOG_PC | LOG_INST) -//#define LOG_OUTPUT_FUNC osd_printf_info - -#include "logmacro.h" - -#define LOGWARN(...) LOGMASKED(LOG_WARN, __VA_ARGS__) -#define LOGCOPINS(...) LOGMASKED(LOG_COPINS, __VA_ARGS__) -#define LOGINST(...) LOGMASKED(LOG_INST, __VA_ARGS__) -#define LOGPC(...) LOGMASKED(LOG_PC, __VA_ARGS__) -#define LOGCHIPSET(...) LOGMASKED(LOG_CHIPSET, __VA_ARGS__) - -// TODO: legacy inheritance, to be verified -#define COPPER_CYCLES_TO_PIXELS(x) (4 * (x)) - -//************************************************************************** -// GLOBAL VARIABLES -//************************************************************************** - - -// device type definition -DEFINE_DEVICE_TYPE(AMIGA_COPPER, amiga_copper_device, "amiga_copper", "Amiga Copper") - - -//************************************************************************** -// LIVE DEVICE -//************************************************************************** - - -//------------------------------------------------- -// amiga_copper_device - constructor -//------------------------------------------------- - - -amiga_copper_device::amiga_copper_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, AMIGA_COPPER, tag, owner, clock) - , m_host_cpu(*this, finder_base::DUMMY_TAG) - , m_chipmem_r(*this, 0) -{ -} - - -//------------------------------------------------- -// device_start - device-specific startup -//------------------------------------------------- - - -void amiga_copper_device::device_start() -{ - m_host_space = &m_host_cpu->space(AS_PROGRAM); - - save_item(NAME(m_cdang_setting)); - save_item(NAME(m_cdang_min_reg)); - save_item(NAME(m_dma_master_enable)); - save_item(NAME(m_dma_copen)); - save_pointer(NAME(m_lc), 2); - save_item(NAME(m_pc)); - save_item(NAME(m_state_waiting)); - save_item(NAME(m_state_waitblit)); - save_item(NAME(m_waitval)); - save_item(NAME(m_waitmask)); -// save_item(NAME(m_wait_offset)); - save_item(NAME(m_pending_data)); - save_item(NAME(m_pending_offset)); -} - - -//------------------------------------------------- -// device_reset - device-specific reset -//------------------------------------------------- - - -void amiga_copper_device::device_reset() -{ - m_cdang_setting = 0x40; - m_dma_master_enable = false; - m_dma_copen = false; - // TODO: latches states on soft reset -} - - -//************************************************************************** -// I/Os -//************************************************************************** - -// $dff080-8d memory map -void amiga_copper_device::regs_map(address_map &map) -{ - // TODO: location addresses belongs to Agnus - map(0x00, 0x01).w(FUNC(amiga_copper_device::copxlch_w<0>)); - map(0x02, 0x03).w(FUNC(amiga_copper_device::copxlcl_w<0>)); - map(0x04, 0x05).w(FUNC(amiga_copper_device::copxlch_w<1>)); - map(0x06, 0x07).w(FUNC(amiga_copper_device::copxlcl_w<1>)); - map(0x08, 0x09).rw(FUNC(amiga_copper_device::copjmpx_r<0>), FUNC(amiga_copper_device::copjmpx_w<0>)); - map(0x0a, 0x0b).rw(FUNC(amiga_copper_device::copjmpx_r<1>), FUNC(amiga_copper_device::copjmpx_w<1>)); -// map(0x0c, 0x0d).w(FUNC(amiga_copper_device::copins_w)); -} - -void amiga_copper_device::dmacon_set(u16 data) -{ - m_dma_master_enable = bool(BIT(data, 9)); - m_dma_copen = bool(BIT(data, 7)); -} - -/* - * COPCON 02E W A Copper Control Register - * - * ---- ---- ---- --x- CDANG (Copper Danger Mode) setting - * - * Documentation is quite contradictory here. - * This bit is supposed to allow Copper access of the - * lowest registers. OriginaL HRM states that it gives - * access to the blitter HW therefore $dff080 is the - * minimum for non-cdang and $dff040 for cdang mode. - * - * In ECS and AGA the latter limitation is lifted so Copper - * can access $dff000-$dff03f too, which basically means the - * possibility of accessing disk block regs. - * (i.e. the other regs are either r/o or wouldn't have much - * sense to write via Copper). - * - */ -void amiga_copper_device::copcon_w(u16 data) -{ - bool cdang = bool(BIT(data, 1)); - - // TODO: is min_reg working even with BPLCON0 bit 0 unset? - m_cdang_setting = cdang ? m_cdang_min_reg : 0x40; - if (cdang) - LOGWARN("%s: cdang enabled\n", machine().describe_context()); - if (data & 0xfffd) - LOGWARN("%s: COPCON undocumented setting write %04x\n", machine().describe_context(), data); -} - -template void amiga_copper_device::copxlch_w(u16 data) -{ - // TODO: chipmem mask - m_lc[ch] = (m_lc[ch] & 0x0000ffff) | ((data & 0x001f) << 16); -} - -template void amiga_copper_device::copxlcl_w(u16 data) -{ - m_lc[ch] = (m_lc[ch] & 0xffff0000) | ((data & 0xfffe) << 0); -} - -/* - * COPJMP1 088 S A Copper restart at first location - * COPJMP2 08a S A Copper restart at second location - * - * Strobe register that loads the new PC into Copper. - * NB: Copper can use this to control its program flow and even - * do conditional branching by clever use of the skip opcode. - * - */ -template void amiga_copper_device::copjmpx_w(u16 data) -{ - set_pc(ch, false); -} - -template u16 amiga_copper_device::copjmpx_r() -{ - if (!machine().side_effects_disabled()) - set_pc(ch, false); - return m_host_space->unmap(); -} - -inline void amiga_copper_device::set_pc(u8 ch, bool is_sync) -{ - m_pc = m_lc[ch]; - m_state_waiting = false; - LOGPC("%s: COPJMP%d new PC = %08x%s\n" - , machine().describe_context() - , ch + 1 - , m_pc - , is_sync ? " (resync)" : "" - ); -} - -/* - * COPINS 08c W A Copper instruction fetch identify - * - * Apparently this register is pinged every time that - * the Copper internally decodes an instruction value. - * Shouldn't have any implementation detail connected, - * de-facto it's a mailbox that can be listened by external HW - * (such as our debugger ;=). - * - */ -void amiga_copper_device::copins_w(u16 data) -{ - LOGCOPINS("%s: COPINS %04x\n", machine().describe_context(), data); -} - -//************************************************************************** -// Implementation getters/setters -//************************************************************************** - -// executed on scanline == 0 -void amiga_copper_device::vblank_sync() -{ - set_pc(0, true); -} - -// TODO: h/vblank checks against xpos/vpos -int amiga_copper_device::execute_next(int xpos, int ypos, bool is_blitter_busy, int num_planes) -{ - int word0, word1; - - /* bail if not enabled */ - if (!m_dma_master_enable || !m_dma_copen) - return 511; - - /* flush any pending writes */ - if (m_pending_offset) - { - //LOGCHIPSET("%02X.%02X: Write to %s = %04x\n", ypos, xpos / 2, s_custom_reg_names[m_copper_pending_offset & 0xff], m_copper_pending_data); - LOGCHIPSET("%02X.%02X: MOVE $dff%03x = %04x\n", - ypos, - xpos / 2, - (m_pending_offset << 1), - m_pending_data - ); - m_host_space->write_word(0xdff000 | (m_pending_offset << 1), m_pending_data); - m_pending_offset = 0; - } - - /* if we're waiting, check for a breakthrough */ - if (m_state_waiting) - { - int curpos = (ypos << 8) | (xpos >> 1); - - /* if we're past the wait time, stop it and hold up 2 cycles */ - if ((curpos & m_waitmask) >= (m_waitval & m_waitmask) && - (!m_state_waitblit || !(is_blitter_busy))) - { - m_state_waiting = false; - return xpos + COPPER_CYCLES_TO_PIXELS(1); - } - - /* otherwise, see if this line is even a possibility; if not, punt */ - if (((curpos | 0xff) & m_waitmask) < (m_waitval & m_waitmask)) - return 511; - - /* else just advance another pixel */ - xpos += COPPER_CYCLES_TO_PIXELS(1); - return xpos; - } - - /* fetch the first data word */ - // TODO: swap between ir0 and ir1 is controlled thru a selins latch - // (which can't be this instant too) - word0 = m_chipmem_r(m_pc); - m_host_space->write_word(0xdff08c, word0); - m_pc += 2; - xpos += COPPER_CYCLES_TO_PIXELS(1); - - /* fetch the second data word */ - word1 = m_chipmem_r(m_pc); - m_host_space->write_word(0xdff08c, word1); - m_pc += 2; - xpos += COPPER_CYCLES_TO_PIXELS(1); - - LOGINST("%02X.%02X: Copper inst @ %06x = %04x %04x\n", - ypos, - xpos / 2, - m_pc - 4, - word0, - word1 - ); - - /* handle a move */ - if ((word0 & 1) == 0) - { - /* do the write if we're allowed */ - word0 = (word0 >> 1) & 0xff; - if (word0 >= m_cdang_setting) - { - if (delay[word0] == 0) - { - //LOGCHIPSET("%02X.%02X: Write to %s = %04x\n", ypos, xpos / 2, s_custom_reg_names[word0 & 0xff], word1); - LOGCHIPSET("%02X.%02X: MOVE $dff%03x = %04x\n", - ypos, - xpos / 2, - word0 << 1, - word1 - ); - m_host_space->write_word(0xdff000 | (word0 << 1), word1); - } - else // additional 2 cycles needed for non-Agnus registers - { - m_pending_offset = word0; - m_pending_data = word1; - } - } - - /* illegal writes suspend until next frame */ - else - { - LOGWARN("%02X.%02X: Aborting copper on illegal write\n", ypos, xpos / 2); - - m_waitval = 0xffff; - m_waitmask = 0xffff; - m_state_waitblit = false; - m_state_waiting = true; - - return 511; - } - } - else - { - /* extract common wait/skip values */ - m_waitval = word0 & 0xfffe; - - m_waitmask = word1 | 0x8001; - m_state_waitblit = (~word1 >> 15) & 1; - - /* handle a wait */ - if ((word1 & 1) == 0) - { - const int wait_offset = std::max(num_planes - 4, 0) + 1; - - LOGINST(" WAIT %04x & %04x (currently %04x, num planes %d +%d)\n", - m_waitval, - m_waitmask, - (ypos << 8) | (xpos >> 1), - num_planes, - wait_offset - ); - - m_state_waiting = true; - xpos += COPPER_CYCLES_TO_PIXELS(wait_offset); - } - - /* handle a skip */ - else - { - int curpos = (ypos << 8) | (xpos >> 1); - - LOGINST(" SKIP %04x & %04x (currently %04x)\n", - m_waitval, - m_waitmask, - (ypos << 8) | (xpos >> 1) - ); - - /* if we're past the wait time, stop it and hold up 2 cycles */ - if ((curpos & m_waitmask) >= (m_waitval & m_waitmask) && - (!m_state_waitblit || !(is_blitter_busy))) - { - LOGINST(" Skipped\n"); - - /* count the cycles it out have taken to fetch the next instruction */ - m_pc += 4; - xpos += COPPER_CYCLES_TO_PIXELS(2); - } - } - } - - /* advance and consume 8 cycles */ - return xpos; -} diff --git a/src/mame/amiga/amiga_copper.h b/src/mame/amiga/amiga_copper.h deleted file mode 100644 index ec92e2d29b8..00000000000 --- a/src/mame/amiga/amiga_copper.h +++ /dev/null @@ -1,101 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Aaron Giles, Angelo Salese -/*************************************************************************** - - Amiga Copper - -***************************************************************************/ - -#ifndef MAME_AMIGA_AMIGA_COPPER_H -#define MAME_AMIGA_AMIGA_COPPER_H - -#pragma once - -//************************************************************************** -// TYPE DEFINITIONS -//************************************************************************** - -class amiga_copper_device : public device_t -{ -public: - // construction/destruction - amiga_copper_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - - // config - template void set_host_cpu_tag(T &&tag) { m_host_cpu.set_tag(std::forward(tag)); } - auto mem_read_cb() { return m_chipmem_r.bind(); } - void set_ecs_mode(bool ecs_mode) { m_cdang_min_reg = ecs_mode ? 0x00 : 0x20; } - - // I/O operations - void regs_map(address_map &map) ATTR_COLD; - void dmacon_set(u16 data); - void copcon_w(u16 data); - - // getters/setters - void vblank_sync(); - int execute_next(int xpos, int ypos, bool is_blitter_busy, int num_planes); - -protected: - // device-level overrides - //virtual void device_validity_check(validity_checker &valid) const override; - //virtual void device_add_mconfig(machine_config &config) override ATTR_COLD; - virtual void device_start() override ATTR_COLD; - virtual void device_reset() override ATTR_COLD; - -private: - required_device m_host_cpu; - address_space *m_host_space = nullptr; - // callbacks - devcb_read16 m_chipmem_r; - - bool m_dma_master_enable, m_dma_copen; - u16 m_cdang_setting, m_cdang_min_reg; - u32 m_lc[2], m_pc; - - template void copxlch_w(u16 data); - template void copxlcl_w(u16 data); - template void copjmpx_w(u16 data); - template u16 copjmpx_r(); -public: - void copins_w(u16 data); -private: - void set_pc(u8 ch, bool is_sync); - - // internal state - bool m_state_waiting; - bool m_state_waitblit; - u16 m_waitval; - u16 m_waitmask; - u16 m_pending_offset; - u16 m_pending_data; - - // waitstate delays for copper - // basically anything that doesn't belong to Angus has a penalty for Copper - static constexpr u16 delay[256] = - { - 1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0, /* 0x000 - 0x03e */ - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x040 - 0x05e */ - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x060 - 0x07e */ - 0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0, /* 0x080 - 0x09e */ - 1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0, /* 0x0a0 - 0x0de */ - /* BPLxPTH/BPLxPTL */ - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x0e0 - 0x0fe */ - /* BPLCON0-3,BPLMOD1-2 */ - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x100 - 0x11e */ - /* SPRxPTH/SPRxPTL */ - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 0x120 - 0x13e */ - /* SPRxPOS/SPRxCTL/SPRxDATA/SPRxDATB */ - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 0x140 - 0x17e */ - /* COLORxx */ - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x180 - 0x1be */ - /* RESERVED */ - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* 0x1c0 - 0x1fe */ - }; - -}; - - -// device type definition -DECLARE_DEVICE_TYPE(AMIGA_COPPER, amiga_copper_device) - -#endif // MAME_AMIGA_AMIGA_COPPER_H diff --git a/src/mame/amiga/amiga_m.cpp b/src/mame/amiga/amiga_m.cpp index 68a6336e659..dadb0ad6077 100644 --- a/src/mame/amiga/amiga_m.cpp +++ b/src/mame/amiga/amiga_m.cpp @@ -1126,26 +1126,26 @@ void amiga_state::ocs_map(address_map &map) // map(0x00a, 0x00b).r(FUNC(amiga_state::joydat_r<0>)); // map(0x00c, 0x00d).r(FUNC(amiga_state::joydat_r<1>)); // map(0x00e, 0x00f).r(FUNC(amiga_state::clxdat_r)); - map(0x010, 0x011).r(m_fdc, FUNC(amiga_fdc_device::adkcon_r)); + map(0x010, 0x011).r(m_fdc, FUNC(paula_fdc_device::adkcon_r)); // map(0x012, 0x013).r(FUNC(amiga_state::potdat_r<0>)); // POT0DAT // map(0x014, 0x015).r(FUNC(amiga_state::potdat_r<1>)); // map(0x016, 0x017).r(FUNC(amiga_state::potgor_r)); // a.k.a. POTINP // map(0x018, 0x019).r(FUNC(amiga_state::serdat_r)); - map(0x01a, 0x01b).r(m_fdc, FUNC(amiga_fdc_device::dskbytr_r)); -// map(0x01c, 0x01d).r(m_paula, FUNC(paula_8364_device::intenar_r)); -// map(0x01e, 0x01f).r(m_paula, FUNC(paula_8364_device::intreqr_r)); + map(0x01a, 0x01b).r(m_fdc, FUNC(paula_fdc_device::dskbytr_r)); +// map(0x01c, 0x01d).r(m_paula, FUNC(paula_device::intenar_r)); +// map(0x01e, 0x01f).r(m_paula, FUNC(paula_device::intreqr_r)); // FDC writes // FIXME: these two belongs to Agnus, also shouldn't be readable - map(0x020, 0x021).rw(m_fdc, FUNC(amiga_fdc_device::dskpth_r), FUNC(amiga_fdc_device::dskpth_w)); - map(0x022, 0x023).rw(m_fdc, FUNC(amiga_fdc_device::dskptl_r), FUNC(amiga_fdc_device::dskptl_w)); - map(0x024, 0x025).w(m_fdc, FUNC(amiga_fdc_device::dsklen_w)); -// map(0x026, 0x027).w(m_fdc, FUNC(amiga_fdc_device::dskdat_w)); + map(0x020, 0x021).rw(m_fdc, FUNC(paula_fdc_device::dskpth_r), FUNC(paula_fdc_device::dskpth_w)); + map(0x022, 0x023).rw(m_fdc, FUNC(paula_fdc_device::dskptl_r), FUNC(paula_fdc_device::dskptl_w)); + map(0x024, 0x025).w(m_fdc, FUNC(paula_fdc_device::dsklen_w)); +// map(0x026, 0x027).w(m_fdc, FUNC(paula_fdc_device::dskdat_w)); // map(0x028, 0x029).w(FUNC(amiga_state::refptr_w)); map(0x02a, 0x02b).w(FUNC(amiga_state::vposw_w)); // map(0x02c, 0x02d).w(FUNC(amiga_state::vhposw_w)); - map(0x02e, 0x02f).w(m_copper, FUNC(amiga_copper_device::copcon_w)); + map(0x02e, 0x02f).w(m_copper, FUNC(agnus_copper_device::copcon_w)); // input strobes // map(0x030, 0x031).w(FUNC(amiga_state::serdat_w)); @@ -1160,11 +1160,11 @@ void amiga_state::ocs_map(address_map &map) // map(0x040, 0x075).m(m_blitter, FUNC(amiga_blitter_device::regs_map)); // map(0x07c, 0x07d).r - map(0x07e, 0x07f).w(m_fdc, FUNC(amiga_fdc_device::dsksync_w)); + map(0x07e, 0x07f).w(m_fdc, FUNC(paula_fdc_device::dsksync_w)); // Copper - map(0x080, 0x08b).m(m_copper, FUNC(amiga_copper_device::regs_map)); - map(0x08c, 0x08d).w(m_copper, FUNC(amiga_copper_device::copins_w)); + map(0x080, 0x08b).m(m_copper, FUNC(agnus_copper_device::regs_map)); + map(0x08c, 0x08d).w(m_copper, FUNC(agnus_copper_device::copins_w)); // Display window // map(0x08e, 0x08f).w(FUNC(amiga_state::diwstrt_w)); // map(0x090, 0x091).w(FUNC(amiga_state::diwstop_w)); @@ -1174,14 +1174,14 @@ void amiga_state::ocs_map(address_map &map) // map(0x096, 0x097).w(FUNC(amiga_state::dmacon_w)); // map(0x098, 0x099).w(FUNC(amiga_state::clxcon_w)); -// map(0x09a, 0x09b).w(m_paula, FUNC(paula_8364_device::intena_w)); -// map(0x09c, 0x09d).w(m_paula, FUNC(paula_8364_device::intreq_w)); -// map(0x09e, 0x09f).w(m_paula, FUNC(paula_8364_device::adkcon_w)); +// map(0x09a, 0x09b).w(m_paula, FUNC(paula_device::intena_w)); +// map(0x09c, 0x09d).w(m_paula, FUNC(paula_device::intreq_w)); +// map(0x09e, 0x09f).w(m_paula, FUNC(paula_device::adkcon_w)); // Audio section - map(0x0a0, 0x0ab).m(m_paula, FUNC(paula_8364_device::audio_channel_map<0>)); - map(0x0b0, 0x0bb).m(m_paula, FUNC(paula_8364_device::audio_channel_map<1>)); - map(0x0c0, 0x0cb).m(m_paula, FUNC(paula_8364_device::audio_channel_map<2>)); - map(0x0d0, 0x0db).m(m_paula, FUNC(paula_8364_device::audio_channel_map<3>)); + map(0x0a0, 0x0ab).m(m_paula, FUNC(paula_device::audio_channel_map<0>)); + map(0x0b0, 0x0bb).m(m_paula, FUNC(paula_device::audio_channel_map<1>)); + map(0x0c0, 0x0cb).m(m_paula, FUNC(paula_device::audio_channel_map<2>)); + map(0x0d0, 0x0db).m(m_paula, FUNC(paula_device::audio_channel_map<3>)); // Bitplane pointer section // map(0x0e0, 0x0ff).m(amiga_state::bplxptr_map)); diff --git a/src/mame/amiga/amigafdc.cpp b/src/mame/amiga/amigafdc.cpp deleted file mode 100644 index 5b0fb798b5a..00000000000 --- a/src/mame/amiga/amigafdc.cpp +++ /dev/null @@ -1,703 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Olivier Galibert -/************************************************************************************************** - - Amiga floppy disk controller emulation "Trackdisk" - - Contained inside MOS 8364 Paula device - - TODO: - - Some games currently writes 2+ dsksync to the buffer (marked as "[FDC] dsksync" in SW list): - Current workaround: - 1. comment out dma_write in DMA_WAIT_START handling and change the dma_state *only*; - 2. remove all of the non-DMA_WAIT_START phase inside the dsksync sub-section; - NB: according to documentation syncing doesn't really write anything on the bus, - so technically this "workaround" is more correct. - However it unfortunately causes other SW regressions, most notably in Workbench. - - Other games trashes memory or refuses to boot, in a few instances randomly - (marked as "[FDC] with adkcon=1100", implies dsksync disabled): - they often uses the AmigaDOS trackdisk BIOS functions, which may be expecting a - different timing. May be worth testing this out with the SDK; - - "[FDC] format" or in general writing to disks doesn't work properly. - i.e. formatting a disk in any Workbench version will cause a system crash once it completes. - - Fix ready line read handling; - - FDC LED output callback; - -**************************************************************************************************/ - - -#include "emu.h" -#include "formats/ami_dsk.h" -#include "formats/ipf_dsk.h" -#include "amigafdc.h" - -#define LOG_WARN (1U << 1) // Show warnings -#define LOG_DMA (1U << 2) // Show DMA setups -#define LOG_SYNC (1U << 3) // Show sync block setups - -#define VERBOSE (LOG_WARN | LOG_DMA | LOG_SYNC) - -#include "logmacro.h" - -#define LOGWARN(...) LOGMASKED(LOG_WARN, __VA_ARGS__) -#define LOGDMA(...) LOGMASKED(LOG_DMA, __VA_ARGS__) -#define LOGSYNC(...) LOGMASKED(LOG_SYNC, __VA_ARGS__) - -DEFINE_DEVICE_TYPE(AMIGA_FDC, amiga_fdc_device, "amiga_fdc", "Amiga \"Trackdisk\" FDC") - -void amiga_fdc_device::floppy_formats(format_registration &fr) -{ - fr.add_mfm_containers(); - fr.add(FLOPPY_ADF_FORMAT); - fr.add(FLOPPY_IPF_FORMAT); -} - -amiga_fdc_device::amiga_fdc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, AMIGA_FDC, tag, owner, clock) - , m_write_index(*this) - , m_read_dma(*this, 0) - , m_write_dma(*this) - , m_write_dskblk(*this) - , m_write_dsksyn(*this) - , m_leds(*this, "led%u", 1U) - , m_fdc_led(*this, "fdc_led") - , floppy(nullptr), t_gen(nullptr), dsklen(0), pre_dsklen(0), dsksync(0), dskbyt(0), adkcon(0), dmacon(0), dskpt(0), dma_value(0), dma_state(0) -{ -} - -void amiga_fdc_device::device_start() -{ - m_leds.resolve(); - m_fdc_led.resolve(); - - static char const *const names[] = { "0", "1", "2", "3" }; - for(int i=0; i != 4; i++) { - floppy_connector *con = subdevice(names[i]); - if(con) - floppy_devices[i] = con->get_device(); - else - floppy_devices[i] = nullptr; - } - - floppy = nullptr; - - t_gen = timer_alloc(FUNC(amiga_fdc_device::update_tick), this); -} - -void amiga_fdc_device::device_reset() -{ - floppy = nullptr; - dsklen = 0x4000; - dsksync = 0x4489; - adkcon = 0; - dmacon = 0; - dskpt = 0; - dskbyt = 0; - pre_dsklen = 0x4000; - dma_value = 0; - dma_state = DMA_IDLE; - - live_abort(); -} - -void amiga_fdc_device::dma_done() -{ - if(dskbyt & 0x2000) { - dskbyt &= ~0x2000; - cur_live.pll.stop_writing(floppy, cur_live.tm); - } - - dma_state = DMA_IDLE; - m_write_dskblk(1); -} - -void amiga_fdc_device::dma_write(uint16_t value) -{ - m_write_dma(dskpt, value, 0xffff); - - dskpt += 2; - dsklen--; - - if(dsklen & 0x3fff) - dma_state = DMA_RUNNING_BYTE_0; - else - dma_done(); -} - -uint16_t amiga_fdc_device::dma_read() -{ - uint16_t res = m_read_dma(dskpt, 0xffff); - - dskpt += 2; - dsklen--; - - // This loses the last word. So does the real hardware. - if(dsklen & 0x3fff) - dma_state = DMA_RUNNING_BYTE_0; - else - dma_done(); - - return res; -} - -void amiga_fdc_device::live_start() -{ - cur_live.tm = machine().time(); - cur_live.state = RUNNING; - cur_live.next_state = -1; - cur_live.shift_reg = 0; - cur_live.bit_counter = 0; - cur_live.pll.reset(cur_live.tm); - cur_live.pll.set_clock(clocks_to_attotime(1)); - checkpoint_live = cur_live; - - live_run(); -} - -void amiga_fdc_device::checkpoint() -{ - cur_live.pll.commit(floppy, cur_live.tm); - checkpoint_live = cur_live; -} - -void amiga_fdc_device::rollback() -{ - cur_live = checkpoint_live; -} - -void amiga_fdc_device::live_delay(int state) -{ - cur_live.next_state = state; - if(cur_live.tm != machine().time()) - t_gen->adjust(cur_live.tm - machine().time()); -} - -void amiga_fdc_device::live_sync() -{ - if(!cur_live.tm.is_never()) { - if(cur_live.tm > machine().time()) { - rollback(); - live_run(machine().time()); - cur_live.pll.commit(floppy, cur_live.tm); - - } else { - cur_live.pll.commit(floppy, cur_live.tm); - - if(cur_live.next_state != -1) { - cur_live.state = cur_live.next_state; - cur_live.next_state = -1; - } - if(cur_live.state == IDLE) { - cur_live.pll.stop_writing(floppy, cur_live.tm); - cur_live.tm = attotime::never; - } - } - cur_live.next_state = -1; - checkpoint(); - } -} - -void amiga_fdc_device::live_abort() -{ - if(!cur_live.tm.is_never() && cur_live.tm > machine().time()) { - rollback(); - live_run(machine().time()); - } - - cur_live.pll.stop_writing(floppy, cur_live.tm); - cur_live.tm = attotime::never; - cur_live.state = IDLE; - cur_live.next_state = -1; -} - -void amiga_fdc_device::live_run(const attotime &limit) -{ - if(cur_live.state == IDLE || cur_live.next_state != -1) - return; - - for(;;) { - switch(cur_live.state) { - case RUNNING: { - if(!(dskbyt & 0x2000)) { - int bit = cur_live.pll.get_next_bit(cur_live.tm, floppy, limit); - if(bit < 0) - return; - - cur_live.shift_reg = (cur_live.shift_reg << 1) | bit; - cur_live.bit_counter++; - - if((adkcon & 0x0200) && !(cur_live.shift_reg & 0x80)) { - cur_live.bit_counter--; - - // Avoid any risk of livelock - live_delay(RUNNING_SYNCPOINT); - return; - } - - if(cur_live.bit_counter > 8) - { - // CHECKME: abreed, ghoulsvf Ghouls'n Goblins and lastnin2 at very least throws this - // is it a side effect of something else not happening at the right time or the assumption is right? - cur_live.bit_counter = 0; - LOGWARN("%s: live_run - cur_live.bit_counter > 8\n", machine().describe_context()); - } - - if(cur_live.bit_counter == 8) { - live_delay(RUNNING_SYNCPOINT); - return; - } - if(dskbyt & 0x1000) { - if(cur_live.shift_reg != dsksync) { - live_delay(RUNNING_SYNCPOINT); - return; - } - } else { - if(cur_live.shift_reg == dsksync) { - live_delay(RUNNING_SYNCPOINT); - return; - } - } - } else { - int bit = (dma_state == DMA_RUNNING_BYTE_0 ? 15 : 7) - cur_live.bit_counter; - if(cur_live.pll.write_next_bit((dma_value >> bit) & 1, cur_live.tm, floppy, limit)) - return; - cur_live.bit_counter++; - if(cur_live.bit_counter > 8) - { - cur_live.bit_counter = 0; - LOGWARN("%s: live_run - cur_live.bit_counter > 8\n", machine().describe_context()); - } - - if(cur_live.bit_counter == 8) { - live_delay(RUNNING_SYNCPOINT); - return; - } - } - break; - } - - case RUNNING_SYNCPOINT: { - if(!(dskbyt & 0x2000)) { - if(cur_live.shift_reg == dsksync) { - if(adkcon & 0x0400) { - // FIXME: exact dsksync behaviour, cfr. note at top - if(dma_state == DMA_WAIT_START) { - cur_live.bit_counter = 0; - - if(dsklen & 0x3fff) - dma_state = DMA_RUNNING_BYTE_0; - else - dma_done(); - } - } - - dskbyt |= 0x1000; - m_write_dsksyn(1); - } else - dskbyt &= ~0x1000; - - if(cur_live.bit_counter == 8) { - dskbyt = (dskbyt & 0xff00) | 0x8000 | (cur_live.shift_reg & 0xff); - cur_live.bit_counter = 0; - - switch(dma_state) { - case DMA_IDLE: - case DMA_WAIT_START: - break; - - case DMA_RUNNING_BYTE_0: - dma_value = (cur_live.shift_reg & 0xff) << 8; - dma_state = DMA_RUNNING_BYTE_1; - break; - - case DMA_RUNNING_BYTE_1: - dma_value |= cur_live.shift_reg & 0xff; - dma_write(dma_value); - break; - } - } - } else { - if(cur_live.bit_counter != 8) - fatalerror("amiga_fdc_device::live_run - cur_live.bit_counter != 8\n"); - cur_live.bit_counter = 0; - - switch(dma_state) { - case DMA_IDLE: - case DMA_WAIT_START: - break; - - case DMA_RUNNING_BYTE_0: - dma_state = DMA_RUNNING_BYTE_1; - break; - - case DMA_RUNNING_BYTE_1: { - dma_value = dma_read(); - break; - } - } - } - - cur_live.state = RUNNING; - checkpoint(); - break; - } - } - } -} - -bool amiga_fdc_device::dma_enabled() -{ - return (dsklen & 0x8000) && ((dmacon & 0x0210) == 0x0210); -} - -void amiga_fdc_device::dma_check() -{ - bool was_writing = dskbyt & 0x2000; - dskbyt &= 0x9fff; - if(dma_enabled()) { - LOGDMA("%s: DMA start dskpt=%08x dsklen=%04x dir=%s adkcon=%04x dsksync=%04x state=%d\n", - machine().describe_context(), - dskpt, dsklen & 0x3fff, BIT(dsklen, 14) ? "RAM->disk" : "disk->RAM", adkcon, dsksync, dma_state - ); - - if(dma_state == IDLE) { - dma_state = adkcon & 0x0400 ? DMA_WAIT_START : DMA_RUNNING_BYTE_0; - if(dma_state == DMA_RUNNING_BYTE_0) { - if(!(dsklen & 0x3fff)) - dma_done(); - else if(dsklen & 0x4000) { - dskbyt |= 0x2000; - cur_live.bit_counter = 0; - dma_value = dma_read(); - } - } - } else { - if(dsklen & 0x4000) - dskbyt |= 0x2000; - } - } else - dma_state = IDLE; - - if(was_writing && !(dskbyt & 0x2000)) - cur_live.pll.stop_writing(floppy, cur_live.tm); - if(!was_writing && (dskbyt & 0x2000)) - cur_live.pll.start_writing(cur_live.tm); -} - -void amiga_fdc_device::adkcon_set(uint16_t data) -{ - live_sync(); - adkcon = data; - live_run(); -} - -uint16_t amiga_fdc_device::adkcon_r(void) -{ - return adkcon; -} - -void amiga_fdc_device::dsklen_w(uint16_t data) -{ - live_sync(); - if(!(data & 0x8000) || (data == pre_dsklen)) { - dsklen = pre_dsklen = data; - dma_check(); - - } else - pre_dsklen = data; - live_run(); -} - -void amiga_fdc_device::dskpth_w(uint16_t data) -{ - live_sync(); - dskpt = (dskpt & 0xffff) | (data << 16); - live_run(); -} - -void amiga_fdc_device::dskptl_w(uint16_t data) -{ - live_sync(); - dskpt = (dskpt & 0xffff0000) | data; - live_run(); -} - -uint16_t amiga_fdc_device::dskpth_r() -{ - return dskpt >> 16; -} - -uint16_t amiga_fdc_device::dskptl_r() -{ - return dskpt; -} - -void amiga_fdc_device::dsksync_w(uint16_t data) -{ - live_sync(); - LOGSYNC("%s: DSKSYNC %04x\n", machine().describe_context(), data); - dsksync = data; - live_run(); -} - -void amiga_fdc_device::dmacon_set(uint16_t data) -{ - live_sync(); - // log changes only - // FIXME: needs better boilerplate code on top level - if ((data & 0x210) != (dmacon & 0x210)) - LOGDMA("%s: DMACON set DSKEN %d DMAEN %d (%04x)\n", machine().describe_context(), BIT(data, 4), BIT(data, 9), data); - dmacon = data; - dma_check(); - live_run(); -} - -uint16_t amiga_fdc_device::dskbytr_r() -{ - uint16_t res = (dskbyt & ~0x4000); - // check if DMA is on - // logica2 diagnostic BIOS floppy test requires this - bool dmaon = (dma_state != DMA_IDLE) && ((dmacon & 0x0210) == 0x0210); - res |= dmaon << 14; - - // reset DSKBYT ready on read - if (!machine().side_effects_disabled()) - dskbyt &= 0x7fff; - return res; -} - -TIMER_CALLBACK_MEMBER(amiga_fdc_device::update_tick) -{ - live_sync(); - live_run(); -} - -void amiga_fdc_device::setup_leds() -{ - if(floppy) { - int drive = - floppy == floppy_devices[0] ? 0 : - floppy == floppy_devices[1] ? 1 : - floppy == floppy_devices[2] ? 2 : - 3; - - m_leds[0] = drive == 0 ? 1 : 0; // update internal drive led - m_leds[1] = drive == 1 ? 1 : 0; // update external drive led - } -} - -void amiga_fdc_device::ciaaprb_w(uint8_t data) -{ - floppy_image_device *old_floppy = floppy; - - live_sync(); - - // FIXME: several sources claims that multiple drive selects is really possible - if(!(data & 0x08)) - floppy = floppy_devices[0]; - else if(!(data & 0x10)) - floppy = floppy_devices[1]; - else if(!(data & 0x20)) - floppy = floppy_devices[2]; - else if(!(data & 0x40)) - floppy = floppy_devices[3]; - else - floppy = nullptr; - - if(old_floppy != floppy) { - if(old_floppy) - old_floppy->setup_index_pulse_cb(floppy_image_device::index_pulse_cb()); - if(floppy) - floppy->setup_index_pulse_cb(floppy_image_device::index_pulse_cb(&amiga_fdc_device::index_callback, this)); - } - - // btanb: trackdisk has no HW interlock for motor running with disk not inserted. - if(floppy) { - floppy->ss_w(!(BIT(data, 2))); - floppy->dir_w(BIT(data, 1)); - floppy->stp_w(BIT(data, 0)); - floppy->mon_w(BIT(data, 7)); - m_fdc_led = BIT(data, 7); // LED directly connected to FDC motor - } - - if(floppy) { - if(cur_live.state == IDLE) - live_start(); - } else - live_abort(); - - setup_leds(); - live_run(); -} - -uint8_t amiga_fdc_device::ciaapra_r() -{ - uint8_t ret = 0x3c; - if(floppy) { - // FIXME: seems to not work well with multiple disk drives - //if(!floppy->ready_r()) - ret &= ~0x20; - if(!floppy->trk00_r()) - ret &= ~0x10; - if(floppy->wpt_r()) - ret &= ~0x08; - if(!floppy->dskchg_r()) - ret &= ~0x04; - } - - return ret; -} - -void amiga_fdc_device::index_callback(floppy_image_device *floppy, int state) -{ - /* Issue a index pulse when a disk revolution completes */ - m_write_index(!state); -} - -void amiga_fdc_device::pll_t::set_clock(const attotime &period) -{ - for(int i=0; i<38; i++) - delays[i] = period*(i+1); -} - -void amiga_fdc_device::pll_t::reset(const attotime &when) -{ - counter = 0; - increment = 146; - transition_time = 0xffff; - history = 0x80; - slot = 0; - ctime = when; - phase_add = 0x00; - phase_sub = 0x00; - freq_add = 0x00; - freq_sub = 0x00; -} - -int amiga_fdc_device::pll_t::get_next_bit(attotime &tm, floppy_image_device *floppy, const attotime &limit) -{ - attotime when = floppy ? floppy->get_next_transition(ctime) : attotime::never; - - for(;;) { - attotime etime = ctime+delays[slot]; - if(etime > limit) - return -1; - if(transition_time == 0xffff && !when.is_never() && etime >= when) - transition_time = counter; - if(slot < 8) { - uint8_t mask = 1 << slot; - if(phase_add & mask) - counter += 258; - else if(phase_sub & mask) - counter += 34; - else - counter += increment; - - if((freq_add & mask) && increment < 159) - increment++; - else if((freq_sub & mask) && increment > 134) - increment--; - } else - counter += increment; - - slot++; - tm = etime; - if(counter & 0x800) - break; - } - - int bit = transition_time != 0xffff; - - if(transition_time != 0xffff) { - static uint8_t const pha[8] = { 0xf, 0x7, 0x3, 0x1, 0, 0, 0, 0 }; - static uint8_t const phs[8] = { 0, 0, 0, 0, 0x1, 0x3, 0x7, 0xf }; - static uint8_t const freqa[4][8] = { - { 0xf, 0x7, 0x3, 0x1, 0, 0, 0, 0 }, - { 0x7, 0x3, 0x1, 0, 0, 0, 0, 0 }, - { 0x7, 0x3, 0x1, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0 } - }; - static uint8_t const freqs[4][8] = { - { 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0x1, 0x3, 0x7 }, - { 0, 0, 0, 0, 0, 0x1, 0x3, 0x7 }, - { 0, 0, 0, 0, 0x1, 0x3, 0x7, 0xf }, - }; - - int cslot = transition_time >> 8; - phase_add = pha[cslot]; - phase_sub = phs[cslot]; - int way = transition_time & 0x400 ? 1 : 0; - if(history & 0x80) - history = way ? 0x80 : 0x83; - else if(history & 0x40) - history = way ? history & 2 : (history & 2) | 1; - freq_add = freqa[history & 3][cslot]; - freq_sub = freqs[history & 3][cslot]; - history = way ? (history >> 1) | 2 : history >> 1; - - } else - phase_add = phase_sub = freq_add = freq_sub = 0; - - counter &= 0x7ff; - - ctime = tm; - transition_time = 0xffff; - slot = 0; - - return bit; -} - -void amiga_fdc_device::pll_t::start_writing(const attotime & tm) -{ - write_start_time = tm; - write_position = 0; -} - -void amiga_fdc_device::pll_t::stop_writing(floppy_image_device *floppy, const attotime &tm) -{ - commit(floppy, tm); - write_start_time = attotime::never; -} - -bool amiga_fdc_device::pll_t::write_next_bit(bool bit, attotime &tm, floppy_image_device *floppy, const attotime &limit) -{ - if(write_start_time.is_never()) { - write_start_time = ctime; - write_position = 0; - } - - for(;;) { - attotime etime = ctime+delays[slot]; - if(etime > limit) - return true; - uint16_t pre_counter = counter; - counter += increment; - if(bit && !(pre_counter & 0x400) && (counter & 0x400)) - if(write_position < std::size(write_buffer)) - write_buffer[write_position++] = etime; - slot++; - tm = etime; - if(counter & 0x800) - break; - } - - counter &= 0x7ff; - - ctime = tm; - slot = 0; - - return false; -} - - -void amiga_fdc_device::pll_t::commit(floppy_image_device *floppy, const attotime &tm) -{ - if(write_start_time.is_never() || tm == write_start_time) - return; - - if(floppy) - floppy->write_flux(write_start_time, tm, write_position, write_buffer); - write_start_time = tm; - write_position = 0; -} diff --git a/src/mame/amiga/amigafdc.h b/src/mame/amiga/amigafdc.h deleted file mode 100644 index 9732414e74c..00000000000 --- a/src/mame/amiga/amigafdc.h +++ /dev/null @@ -1,130 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Olivier Galibert -#ifndef MAME_AMIGA_AMIGAFDC_H -#define MAME_AMIGA_AMIGAFDC_H - -#pragma once - -#include "imagedev/floppy.h" - -class amiga_fdc_device : public device_t { -public: - amiga_fdc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - - auto index_callback() { return m_write_index.bind(); } - auto read_dma_callback() { return m_read_dma.bind(); } - auto write_dma_callback() { return m_write_dma.bind(); } - auto dskblk_callback() { return m_write_dskblk.bind(); } - auto dsksyn_callback() { return m_write_dsksyn.bind(); } - - void ciaaprb_w(uint8_t data); - - uint8_t ciaapra_r(); - uint16_t dskbytr_r(); - uint16_t dskpth_r(); - uint16_t dskptl_r(); - - void dsksync_w(uint16_t data); - void dskpth_w(uint16_t data); - void dskptl_w(uint16_t data); - void dsklen_w(uint16_t data); - void adkcon_set(uint16_t data); - void dmacon_set(uint16_t data); - uint16_t adkcon_r(void); - - static void floppy_formats(format_registration &fr); - -protected: - virtual void device_start() override ATTR_COLD; - virtual void device_reset() override ATTR_COLD; - - TIMER_CALLBACK_MEMBER(update_tick); - -private: - // Running states - enum { - IDLE, - RUNNING, - RUNNING_SYNCPOINT - }; - - // DMA states - enum { - DMA_IDLE, - DMA_WAIT_START, - DMA_RUNNING_BYTE_0, - DMA_RUNNING_BYTE_1 - }; - - struct pll_t { - uint16_t counter; - uint16_t increment; - uint16_t transition_time; - uint8_t history; - uint8_t slot; - uint8_t phase_add, phase_sub, freq_add, freq_sub; - attotime ctime; - - attotime delays[38]; - - attotime write_start_time; - attotime write_buffer[32]; - int write_position; - - void set_clock(const attotime &period); - void reset(const attotime &when); - int get_next_bit(attotime &tm, floppy_image_device *floppy, const attotime &limit); - bool write_next_bit(bool bit, attotime &tm, floppy_image_device *floppy, const attotime &limit); - void start_writing(const attotime &tm); - void commit(floppy_image_device *floppy, const attotime &tm); - void stop_writing(floppy_image_device *floppy, const attotime &tm); - }; - - struct live_info { - attotime tm; - int state, next_state; - uint16_t shift_reg; - int bit_counter; - pll_t pll; - }; - - devcb_write_line m_write_index; - devcb_read16 m_read_dma; - devcb_write16 m_write_dma; - devcb_write_line m_write_dskblk; - devcb_write_line m_write_dsksyn; - output_finder<2> m_leds; - output_finder<> m_fdc_led; - - floppy_image_device *floppy; - floppy_image_device *floppy_devices[4]; - - live_info cur_live, checkpoint_live; - - emu_timer *t_gen; - uint16_t dsklen, pre_dsklen, dsksync, dskbyt, adkcon, dmacon; - uint32_t dskpt; - uint16_t dma_value; - - int dma_state; - - void setup_leds(); - void index_callback(floppy_image_device *floppy, int state); - bool dma_enabled(); - void dma_check(); - void dma_done(); - void dma_write(uint16_t value); - uint16_t dma_read(); - - void live_start(); - void checkpoint(); - void rollback(); - void live_delay(int state); - void live_sync(); - void live_abort(); - void live_run(const attotime &limit = attotime::never); -}; - -DECLARE_DEVICE_TYPE(AMIGA_FDC, amiga_fdc_device) - -#endif // MAME_AMIGA_AMIGAFDC_H diff --git a/src/mame/amiga/arsystems.cpp b/src/mame/amiga/arsystems.cpp index bc53085267a..d717618fcfa 100644 --- a/src/mame/amiga/arsystems.cpp +++ b/src/mame/amiga/arsystems.cpp @@ -327,7 +327,7 @@ void arcadia_amiga_state::arcadia(machine_config &config) ADDRESS_MAP_BANK(config, m_overlay).set_map(&arcadia_amiga_state::overlay_512kb_map).set_options(ENDIANNESS_BIG, 16, 22, 0x200000); ADDRESS_MAP_BANK(config, m_chipset).set_map(&arcadia_amiga_state::ocs_map).set_options(ENDIANNESS_BIG, 16, 9, 0x200); - AMIGA_COPPER(config, m_copper, amiga_state::CLK_7M_NTSC); + AGNUS_COPPER(config, m_copper, amiga_state::CLK_7M_NTSC); m_copper->set_host_cpu_tag(m_maincpu); m_copper->mem_read_cb().set(FUNC(amiga_state::chip_ram_r)); m_copper->set_ecs_mode(false); @@ -365,7 +365,7 @@ void arcadia_amiga_state::arcadia(machine_config &config) m_cia_1->irq_wr_callback().set(FUNC(amiga_state::cia_1_irq)); /* fdc */ - AMIGA_FDC(config, m_fdc, amiga_state::CLK_7M_NTSC); + PAULA_FDC(config, m_fdc, amiga_state::CLK_7M_NTSC); m_fdc->index_callback().set("cia_1", FUNC(mos8520_device::flag_w)); m_fdc->read_dma_callback().set(FUNC(amiga_state::chip_ram_r)); m_fdc->write_dma_callback().set(FUNC(amiga_state::chip_ram_w)); diff --git a/src/mame/amiga/cubo.cpp b/src/mame/amiga/cubo.cpp index afa32f41145..ffbb72bdf09 100644 --- a/src/mame/amiga/cubo.cpp +++ b/src/mame/amiga/cubo.cpp @@ -1075,7 +1075,7 @@ void cubo_state::cubo(machine_config &config) ADDRESS_MAP_BANK(config, m_overlay).set_map(&cubo_state::overlay_2mb_map32).set_options(ENDIANNESS_BIG, 32, 22, 0x200000); ADDRESS_MAP_BANK(config, m_chipset).set_map(&cubo_state::aga_map).set_options(ENDIANNESS_BIG, 32, 9, 0x200); - AMIGA_COPPER(config, m_copper, amiga_state::CLK_28M_PAL / 2); + AGNUS_COPPER(config, m_copper, amiga_state::CLK_28M_PAL / 2); m_copper->set_host_cpu_tag(m_maincpu); m_copper->mem_read_cb().set(FUNC(amiga_state::chip_ram_r)); m_copper->set_ecs_mode(true); @@ -1122,7 +1122,7 @@ void cubo_state::cubo(machine_config &config) MICROTOUCH(config, m_microtouch, 9600).stx().set(FUNC(cubo_state::rs232_rx_w)); /* fdc */ - AMIGA_FDC(config, m_fdc, amiga_state::CLK_7M_PAL); + PAULA_FDC(config, m_fdc, amiga_state::CLK_7M_PAL); m_fdc->index_callback().set("cia_1", FUNC(mos8520_device::flag_w)); m_fdc->read_dma_callback().set(FUNC(amiga_state::chip_ram_r)); m_fdc->write_dma_callback().set(FUNC(amiga_state::chip_ram_w)); diff --git a/src/mame/amiga/mquake.cpp b/src/mame/amiga/mquake.cpp index 4c2ab3bd727..6b1f5815a25 100644 --- a/src/mame/amiga/mquake.cpp +++ b/src/mame/amiga/mquake.cpp @@ -324,7 +324,7 @@ void mquake_state::mquake(machine_config &config) ADDRESS_MAP_BANK(config, m_overlay).set_map(&mquake_state::overlay_512kb_map).set_options(ENDIANNESS_BIG, 16, 22, 0x200000); ADDRESS_MAP_BANK(config, m_chipset).set_map(&mquake_state::ocs_map).set_options(ENDIANNESS_BIG, 16, 9, 0x200); - AMIGA_COPPER(config, m_copper, amiga_state::CLK_7M_NTSC); + AGNUS_COPPER(config, m_copper, amiga_state::CLK_7M_NTSC); m_copper->set_host_cpu_tag(m_maincpu); m_copper->mem_read_cb().set(FUNC(amiga_state::chip_ram_r)); m_copper->set_ecs_mode(false); @@ -365,7 +365,7 @@ void mquake_state::mquake(machine_config &config) m_cia_1->irq_wr_callback().set(FUNC(amiga_state::cia_1_irq)); /* fdc */ - AMIGA_FDC(config, m_fdc, amiga_state::CLK_7M_NTSC); + PAULA_FDC(config, m_fdc, amiga_state::CLK_7M_NTSC); m_fdc->index_callback().set("cia_1", FUNC(mos8520_device::flag_w)); m_fdc->read_dma_callback().set(FUNC(amiga_state::chip_ram_r)); m_fdc->write_dma_callback().set(FUNC(amiga_state::chip_ram_w)); diff --git a/src/mame/amiga/paula.cpp b/src/mame/amiga/paula.cpp new file mode 100644 index 00000000000..6b8a38ca0ed --- /dev/null +++ b/src/mame/amiga/paula.cpp @@ -0,0 +1,390 @@ +// license: BSD-3-Clause +// copyright-holders: Aaron Giles, Dirk Best +/****************************************************************************** + + MOS Technology/Commodore 8364 "Paula" + + Multi-purpose chip that is part of the Amiga chipset. The name "Paula" + is derived from "Ports, Audio, UART and Logic". It features 4-channel + DMA driven audio, the floppy controller, a serial receiver/transmitter, + analog inputs and contains the interrupt controller. + + TODO: + - Inherit FDC, serial and irq controller to here; + - Move Agnus "location" logic out of here; + - low-pass filter; + - convert volume values to non-linear dB scale (cfr. ) + - Verify ADKCON modulation; + - Verify manual mode: + \- AGA roadkill during gameplay, which also has very long period setups, + extremely aliased; + - When a DMA stop occurs, is the correlated channel playback stopped + at the end of the current cycle or as soon as possible like current + implementation? + +******************************************************************************/ + +#include "emu.h" +#include "paula.h" + +#define LIVE_AUDIO_VIEW 0 + +//#define VERBOSE 1 +#include "logmacro.h" +#include + + +//************************************************************************** +// DEVICE DEFINITIONS +//************************************************************************** + +DEFINE_DEVICE_TYPE(PAULA_8364, paula_device, "paula_8364", "MOS 8364 \"Paula\"") + + +//************************************************************************* +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// paula_device - constructor +//------------------------------------------------- + +paula_device::paula_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, PAULA_8364, tag, owner, clock) + , device_sound_interface(mconfig, *this) + , m_chipmem_r(*this, 0) + , m_int_w(*this) + , m_stream(nullptr) +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void paula_device::device_start() +{ + // initialize channels + for (int i = 0; i < 4; i++) + { + m_channel[i].index = i; + m_channel[i].irq_timer = timer_alloc(FUNC(paula_device::signal_irq), this); + } + + // create the stream + m_stream = stream_alloc(0, 4, clock() / CLOCK_DIVIDER); +} + +void paula_device::device_reset() +{ + m_dma_master_enable = false; + for (auto &chan : m_channel) + { + chan.loc = 0; + chan.len = 0; + chan.per = 0; + chan.vol = 0; + chan.curticks = 0; + chan.manualmode = false; + chan.curlocation = 0; + chan.curlength = 0; + chan.dma_enabled = false; + chan.atper = false; + chan.atvol = false; + } +} + +//------------------------------------------------- +// update - stream updater +//------------------------------------------------- + +void paula_device::update() +{ + m_stream->update(); +} + + +//************************************************************************* +// IMPLEMENTATION +//************************************************************************** + +template void paula_device::audio_channel_map(address_map &map) +{ + // TODO: location addresses belongs to Agnus + map(0x00, 0x01).w(FUNC(paula_device::audxlch_w)); + map(0x02, 0x03).w(FUNC(paula_device::audxlcl_w)); + map(0x04, 0x05).w(FUNC(paula_device::audxlen_w)); + map(0x06, 0x07).w(FUNC(paula_device::audxper_w)); + map(0x08, 0x09).w(FUNC(paula_device::audxvol_w)); + map(0x0a, 0x0b).w(FUNC(paula_device::audxdat_w)); +} + +// Instantiate channel maps +template void paula_device::audio_channel_map<0>(address_map &map); +template void paula_device::audio_channel_map<1>(address_map &map); +template void paula_device::audio_channel_map<2>(address_map &map); +template void paula_device::audio_channel_map<3>(address_map &map); + +template void paula_device::audxlch_w(u16 data) +{ + m_stream->update(); + // TODO: chipmem mask + m_channel[ch].loc = (m_channel[ch].loc & 0x0000ffff) | ((data & 0x001f) << 16); +} + +template void paula_device::audxlcl_w(u16 data) +{ + m_stream->update(); + m_channel[ch].loc = (m_channel[ch].loc & 0xffff0000) | ((data & 0xfffe) << 0); +} + +template void paula_device::audxlen_w(u16 data) +{ + m_stream->update(); + m_channel[ch].len = data; +} + +template void paula_device::audxper_w(u16 data) +{ + m_stream->update(); + m_channel[ch].per = data; +} + +template void paula_device::audxvol_w(u16 data) +{ + m_stream->update(); + m_channel[ch].vol = data & 0x7f; +} + +template void paula_device::audxdat_w(u16 data) +{ + m_stream->update(); + m_channel[ch].dat = data; + m_channel[ch].manualmode = true; +} + +void paula_device::dmacon_set(u16 data) +{ + m_stream->update(); + + m_dma_master_enable = bool(BIT(data, 9)); + + // update the DMA latches on each channel and reload if fresh + // This holds true particularly for Ocean games (bchvolly, lostpatr, pang) and waylildr: + // they sets a DMA length for a channel then enable DMA finally resets that length to 1 + // after a short delay loop. + for (int channum = 0; channum < 4; channum++) + { + audio_channel *chan = &m_channel[channum]; + if (!chan->dma_enabled && ((data >> channum) & 1)) + dma_reload(chan, true); + + chan->dma_enabled = bool(BIT(data, channum)); + } +} + +void paula_device::adkcon_set(u16 data) +{ + m_stream->update(); + + for (int channum = 0; channum < 4; channum++) + { + audio_channel *chan = &m_channel[channum]; + + chan->atper = bool(BIT(data, channum + 4)); + chan->atvol = bool(BIT(data, channum)); + } +} + +//------------------------------------------------- +// signal_irq - irq signaling +//------------------------------------------------- + +TIMER_CALLBACK_MEMBER( paula_device::signal_irq ) +{ + m_int_w(param, 1); +} + +//------------------------------------------------- +// dma_reload +//------------------------------------------------- + +void paula_device::dma_reload(audio_channel *chan, bool startup) +{ + chan->curlocation = chan->loc; + // TODO: Unconfirmed, assume max size if length is 0. + // cfr. wrestlmn and videokid, where they pratically never get even close to buffer exhaustion. + chan->curlength = chan->len == 0 ? 0x10000 : chan->len; + // TODO: on startup=false irq should be delayed two cycles + if (startup) + chan->irq_timer->adjust(attotime::from_hz(15750), chan->index); // clock() / 227 + else + signal_irq(chan->index); + + LOG("dma_reload(%d): offs=%06X len=%04X\n", chan->index, chan->curlocation, chan->curlength); +} + +std::string paula_device::print_audio_state() +{ + std::ostringstream outbuffer; + + util::stream_format(outbuffer, "DMA master %d\n", m_dma_master_enable); + for (auto &chan : m_channel) + { + util::stream_format(outbuffer, "%d DMA (%d) ADK (%d%d) REGS: %06x %04x %03x %02x %d LIVE: %06x %04x %d\n" + , chan.index + , chan.dma_enabled + , chan.atper + , chan.atvol + , chan.loc + , chan.len + , chan.per + , chan.vol + , chan.manualmode + , chan.curlocation + , chan.curlength + , chan.dma_enabled + ); + } + + return outbuffer.str(); +} + +//------------------------------------------------- +// sound_stream_update - handle a stream update +//------------------------------------------------- + +void paula_device::sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) +{ + int channum, sampoffs = 0; + + // if all DMA off, disable all channels + if (m_dma_master_enable == false) + { + m_channel[0].dma_enabled = + m_channel[1].dma_enabled = + m_channel[2].dma_enabled = + m_channel[3].dma_enabled = false; + + // clear the sample data to 0 + for (channum = 0; channum < 4; channum++) + outputs[channum].fill(0); + return; + } + + int samples = outputs[0].samples() * CLOCK_DIVIDER; + + if (LIVE_AUDIO_VIEW) + popmessage(print_audio_state()); + + // loop until done + while (samples > 0) + { + int nextper, nextvol; + int ticks = samples; + + // determine the number of ticks we can do in this chunk + if (ticks > m_channel[0].curticks) + ticks = m_channel[0].curticks; + if (ticks > m_channel[1].curticks) + ticks = m_channel[1].curticks; + if (ticks > m_channel[2].curticks) + ticks = m_channel[2].curticks; + if (ticks > m_channel[3].curticks) + ticks = m_channel[3].curticks; + + // loop over channels + nextper = nextvol = -1; + for (channum = 0; channum < 4; channum++) + { + audio_channel *chan = &m_channel[channum]; + int volume = (nextvol == -1) ? chan->vol : nextvol; + int period = (nextper == -1) ? chan->per : nextper; + s32 sample; + int i; + + // normalize the volume value + // FIXME: definitely not linear + volume = (volume & 0x40) ? 64 : (volume & 0x3f); + volume *= 4; + + // are we modulating the period of the next channel? + if (chan->atper) + { + nextper = chan->dat; + nextvol = -1; + sample = 0; + } + + // are we modulating the volume of the next channel? + else if (chan->atvol) + { + nextper = -1; + nextvol = chan->dat; + sample = 0; + } + + // otherwise, we are generating data + else + { + nextper = nextvol = -1; + sample = chan->latched * volume; + } + + // fill the buffer with the sample + for (i = 0; i < ticks; i += CLOCK_DIVIDER) + outputs[channum].put_int((sampoffs + i) / CLOCK_DIVIDER, sample, 32768); + + // account for the ticks; if we hit 0, advance + chan->curticks -= ticks; + if (chan->curticks == 0) + { + // reset the clock and ensure we're above the minimum ticks + chan->curticks = period; + if (chan->curticks < 124) + chan->curticks = 124; + + // move forward one byte; if we move to an even byte, fetch new + if (chan->dma_enabled || chan->manualmode) + chan->curlocation++; + if (chan->dma_enabled && !(chan->curlocation & 1)) + { + chan->dat = m_chipmem_r(chan->curlocation); + + if (chan->curlength != 0) + chan->curlength--; + + // if we run out of data, reload the dma and signal an IRQ, + // gpmaster/asparmgp definitely expects this + // (uses channel 3 as a sequencer, changing the start address on the fly) + if (chan->curlength == 0) + { + dma_reload(chan, false); + // reload the data pointer, otherwise aliasing / buzzing outside the given buffer will be heard + // For example: Xenon 2 sets up location=0x63298 length=0x20 + // for silencing channels on-the-fly without relying on irqs. + // Without this the location will read at 0x632d8 (data=0x7a7d), causing annoying buzzing. + chan->dat = m_chipmem_r(chan->curlocation); + } + } + + // latch the next byte of the sample + if (!(chan->curlocation & 1)) + chan->latched = chan->dat >> 8; + else + chan->latched = chan->dat >> 0; + + // if we're in manual mode, signal an interrupt once we latch the low byte + if (!chan->dma_enabled && chan->manualmode && (chan->curlocation & 1)) + { + signal_irq(channum); + chan->manualmode = false; + } + } + } + + // bump ourselves forward by the number of ticks + sampoffs += ticks; + samples -= ticks; + } +} diff --git a/src/mame/amiga/paula.h b/src/mame/amiga/paula.h new file mode 100644 index 00000000000..a1db6ae9800 --- /dev/null +++ b/src/mame/amiga/paula.h @@ -0,0 +1,120 @@ +// license: BSD-3-Clause +// copyright-holders: Aaron Giles, Dirk Best +/*************************************************************************** + + MOS Technology/Commodore 8364 "Paula" + + ____ ____ + D8 1 |* u | 48 D9 + D7 2 | | 47 D10 + D6 3 | | 46 D11 + D5 4 | | 45 D12 + D4 5 | | 44 D13 + D3 6 | | 43 D14 + D2 7 | | 42 D15 + GND 8 | | 41 RXD + D1 9 | | 40 TXD + D0 10 | | 39 DKWE + /RES 11 | | 38 /DKWD + DMAL 12 | | 37 /DKRD + /IPL0 13 | | 36 POT1Y + /IPL1 14 | | 35 POT1X + /IPL2 15 | | 34 ANAGND + /INT2 16 | | 33 POT0Y + /INT3 17 | | 32 POT0X + /INT6 18 | | 31 AUDL + RGA8 19 | | 30 AUDR + RGA7 20 | | 29 CCKQ + RGA6 21 | | 28 CCK + RGA5 22 | | 27 VCC + RGA4 23 | | 26 RGA1 + RGA3 24 |_________| 25 RGA2 + +***************************************************************************/ + +#ifndef MAME_AMIGA_PAULA_H +#define MAME_AMIGA_PAULA_H + +#pragma once + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> paula_device + +class paula_device : public device_t, public device_sound_interface +{ +public: + paula_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + // configuration + auto mem_read_cb() { return m_chipmem_r.bind(); } + auto int_cb() { return m_int_w.bind(); } + + void update(); + + template void audio_channel_map(address_map &map) ATTR_COLD; + void dmacon_set(u16 data); + void adkcon_set(u16 data); + +protected: + // device-level overrides + virtual void device_start() override ATTR_COLD; + virtual void device_reset() override ATTR_COLD; + + // sound stream update overrides + virtual void sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) override; + +private: + template void audxlch_w(u16 data); + template void audxlcl_w(u16 data); + template void audxlen_w(u16 data); + template void audxper_w(u16 data); + template void audxvol_w(u16 data); + template void audxdat_w(u16 data); + + static constexpr int CLOCK_DIVIDER = 16; + + struct audio_channel + { + emu_timer *irq_timer; + uint32_t curlocation; + uint32_t curlength; + uint16_t curticks; + uint8_t index; + bool dma_enabled; + bool manualmode; + int8_t latched; + + // custom chip registers + uint32_t loc; // to be moved, not part of paula + uint16_t len; + uint16_t per; + uint16_t vol; + uint16_t dat; + bool atper; + bool atvol; + }; + + bool m_dma_master_enable; + + void dma_reload(audio_channel *chan, bool startup); + + // callbacks + devcb_read16 m_chipmem_r; + devcb_write8 m_int_w; + + audio_channel m_channel[4]; + sound_stream *m_stream; + + TIMER_CALLBACK_MEMBER( signal_irq ); + + std::string print_audio_state(); +}; + +// device type definition +DECLARE_DEVICE_TYPE(PAULA_8364, paula_device) + +#endif // MAME_AMIGA_PAULA_H diff --git a/src/mame/amiga/paulafdc.cpp b/src/mame/amiga/paulafdc.cpp new file mode 100644 index 00000000000..33df573e72e --- /dev/null +++ b/src/mame/amiga/paulafdc.cpp @@ -0,0 +1,704 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert +/************************************************************************************************** + + Amiga floppy disk controller emulation "Trackdisk" + + Contained inside MOS 8364 Paula device + + TODO: + - Some games currently writes 2+ dsksync to the buffer (marked as "[FDC] dsksync" in SW list): + Current workaround: + 1. comment out dma_write in DMA_WAIT_START handling and change the dma_state *only*; + 2. remove all of the non-DMA_WAIT_START phase inside the dsksync sub-section; + NB: according to documentation syncing doesn't really write anything on the bus, + so technically this "workaround" is more correct. + However it unfortunately causes other SW regressions, most notably in Workbench. + - Other games trashes memory or refuses to boot, in a few instances randomly + (marked as "[FDC] with adkcon=1100", implies dsksync disabled): + they often uses the AmigaDOS trackdisk BIOS functions, which may be expecting a + different timing. May be worth testing this out with the SDK; + - "[FDC] format" or in general writing to disks doesn't work properly. + i.e. formatting a disk in any Workbench version will cause a system crash once it completes. + - Fix ready line read handling; + - FDC LED output callback; + +**************************************************************************************************/ + + +#include "emu.h" +#include "paulafdc.h" + +#include "formats/ami_dsk.h" +#include "formats/ipf_dsk.h" + +#define LOG_WARN (1U << 1) // Show warnings +#define LOG_DMA (1U << 2) // Show DMA setups +#define LOG_SYNC (1U << 3) // Show sync block setups + +#define VERBOSE (LOG_WARN | LOG_DMA | LOG_SYNC) + +#include "logmacro.h" + +#define LOGWARN(...) LOGMASKED(LOG_WARN, __VA_ARGS__) +#define LOGDMA(...) LOGMASKED(LOG_DMA, __VA_ARGS__) +#define LOGSYNC(...) LOGMASKED(LOG_SYNC, __VA_ARGS__) + +DEFINE_DEVICE_TYPE(PAULA_FDC, paula_fdc_device, "paula_fdc", "Amiga Paula \"Trackdisk\" FDC") + +void paula_fdc_device::floppy_formats(format_registration &fr) +{ + fr.add_mfm_containers(); + fr.add(FLOPPY_ADF_FORMAT); + fr.add(FLOPPY_IPF_FORMAT); +} + +paula_fdc_device::paula_fdc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, PAULA_FDC, tag, owner, clock) + , m_write_index(*this) + , m_read_dma(*this, 0) + , m_write_dma(*this) + , m_write_dskblk(*this) + , m_write_dsksyn(*this) + , m_leds(*this, "led%u", 1U) + , m_fdc_led(*this, "fdc_led") + , floppy(nullptr), t_gen(nullptr), dsklen(0), pre_dsklen(0), dsksync(0), dskbyt(0), adkcon(0), dmacon(0), dskpt(0), dma_value(0), dma_state(0) +{ +} + +void paula_fdc_device::device_start() +{ + m_leds.resolve(); + m_fdc_led.resolve(); + + static char const *const names[] = { "0", "1", "2", "3" }; + for(int i=0; i != 4; i++) { + floppy_connector *con = subdevice(names[i]); + if(con) + floppy_devices[i] = con->get_device(); + else + floppy_devices[i] = nullptr; + } + + floppy = nullptr; + + t_gen = timer_alloc(FUNC(paula_fdc_device::update_tick), this); +} + +void paula_fdc_device::device_reset() +{ + floppy = nullptr; + dsklen = 0x4000; + dsksync = 0x4489; + adkcon = 0; + dmacon = 0; + dskpt = 0; + dskbyt = 0; + pre_dsklen = 0x4000; + dma_value = 0; + dma_state = DMA_IDLE; + + live_abort(); +} + +void paula_fdc_device::dma_done() +{ + if(dskbyt & 0x2000) { + dskbyt &= ~0x2000; + cur_live.pll.stop_writing(floppy, cur_live.tm); + } + + dma_state = DMA_IDLE; + m_write_dskblk(1); +} + +void paula_fdc_device::dma_write(uint16_t value) +{ + m_write_dma(dskpt, value, 0xffff); + + dskpt += 2; + dsklen--; + + if(dsklen & 0x3fff) + dma_state = DMA_RUNNING_BYTE_0; + else + dma_done(); +} + +uint16_t paula_fdc_device::dma_read() +{ + uint16_t res = m_read_dma(dskpt, 0xffff); + + dskpt += 2; + dsklen--; + + // This loses the last word. So does the real hardware. + if(dsklen & 0x3fff) + dma_state = DMA_RUNNING_BYTE_0; + else + dma_done(); + + return res; +} + +void paula_fdc_device::live_start() +{ + cur_live.tm = machine().time(); + cur_live.state = RUNNING; + cur_live.next_state = -1; + cur_live.shift_reg = 0; + cur_live.bit_counter = 0; + cur_live.pll.reset(cur_live.tm); + cur_live.pll.set_clock(clocks_to_attotime(1)); + checkpoint_live = cur_live; + + live_run(); +} + +void paula_fdc_device::checkpoint() +{ + cur_live.pll.commit(floppy, cur_live.tm); + checkpoint_live = cur_live; +} + +void paula_fdc_device::rollback() +{ + cur_live = checkpoint_live; +} + +void paula_fdc_device::live_delay(int state) +{ + cur_live.next_state = state; + if(cur_live.tm != machine().time()) + t_gen->adjust(cur_live.tm - machine().time()); +} + +void paula_fdc_device::live_sync() +{ + if(!cur_live.tm.is_never()) { + if(cur_live.tm > machine().time()) { + rollback(); + live_run(machine().time()); + cur_live.pll.commit(floppy, cur_live.tm); + + } else { + cur_live.pll.commit(floppy, cur_live.tm); + + if(cur_live.next_state != -1) { + cur_live.state = cur_live.next_state; + cur_live.next_state = -1; + } + if(cur_live.state == IDLE) { + cur_live.pll.stop_writing(floppy, cur_live.tm); + cur_live.tm = attotime::never; + } + } + cur_live.next_state = -1; + checkpoint(); + } +} + +void paula_fdc_device::live_abort() +{ + if(!cur_live.tm.is_never() && cur_live.tm > machine().time()) { + rollback(); + live_run(machine().time()); + } + + cur_live.pll.stop_writing(floppy, cur_live.tm); + cur_live.tm = attotime::never; + cur_live.state = IDLE; + cur_live.next_state = -1; +} + +void paula_fdc_device::live_run(const attotime &limit) +{ + if(cur_live.state == IDLE || cur_live.next_state != -1) + return; + + for(;;) { + switch(cur_live.state) { + case RUNNING: { + if(!(dskbyt & 0x2000)) { + int bit = cur_live.pll.get_next_bit(cur_live.tm, floppy, limit); + if(bit < 0) + return; + + cur_live.shift_reg = (cur_live.shift_reg << 1) | bit; + cur_live.bit_counter++; + + if((adkcon & 0x0200) && !(cur_live.shift_reg & 0x80)) { + cur_live.bit_counter--; + + // Avoid any risk of livelock + live_delay(RUNNING_SYNCPOINT); + return; + } + + if(cur_live.bit_counter > 8) + { + // CHECKME: abreed, ghoulsvf Ghouls'n Goblins and lastnin2 at very least throws this + // is it a side effect of something else not happening at the right time or the assumption is right? + cur_live.bit_counter = 0; + LOGWARN("%s: live_run - cur_live.bit_counter > 8\n", machine().describe_context()); + } + + if(cur_live.bit_counter == 8) { + live_delay(RUNNING_SYNCPOINT); + return; + } + if(dskbyt & 0x1000) { + if(cur_live.shift_reg != dsksync) { + live_delay(RUNNING_SYNCPOINT); + return; + } + } else { + if(cur_live.shift_reg == dsksync) { + live_delay(RUNNING_SYNCPOINT); + return; + } + } + } else { + int bit = (dma_state == DMA_RUNNING_BYTE_0 ? 15 : 7) - cur_live.bit_counter; + if(cur_live.pll.write_next_bit((dma_value >> bit) & 1, cur_live.tm, floppy, limit)) + return; + cur_live.bit_counter++; + if(cur_live.bit_counter > 8) + { + cur_live.bit_counter = 0; + LOGWARN("%s: live_run - cur_live.bit_counter > 8\n", machine().describe_context()); + } + + if(cur_live.bit_counter == 8) { + live_delay(RUNNING_SYNCPOINT); + return; + } + } + break; + } + + case RUNNING_SYNCPOINT: { + if(!(dskbyt & 0x2000)) { + if(cur_live.shift_reg == dsksync) { + if(adkcon & 0x0400) { + // FIXME: exact dsksync behaviour, cfr. note at top + if(dma_state == DMA_WAIT_START) { + cur_live.bit_counter = 0; + + if(dsklen & 0x3fff) + dma_state = DMA_RUNNING_BYTE_0; + else + dma_done(); + } + } + + dskbyt |= 0x1000; + m_write_dsksyn(1); + } else + dskbyt &= ~0x1000; + + if(cur_live.bit_counter == 8) { + dskbyt = (dskbyt & 0xff00) | 0x8000 | (cur_live.shift_reg & 0xff); + cur_live.bit_counter = 0; + + switch(dma_state) { + case DMA_IDLE: + case DMA_WAIT_START: + break; + + case DMA_RUNNING_BYTE_0: + dma_value = (cur_live.shift_reg & 0xff) << 8; + dma_state = DMA_RUNNING_BYTE_1; + break; + + case DMA_RUNNING_BYTE_1: + dma_value |= cur_live.shift_reg & 0xff; + dma_write(dma_value); + break; + } + } + } else { + if(cur_live.bit_counter != 8) + fatalerror("paula_fdc_device::live_run - cur_live.bit_counter != 8\n"); + cur_live.bit_counter = 0; + + switch(dma_state) { + case DMA_IDLE: + case DMA_WAIT_START: + break; + + case DMA_RUNNING_BYTE_0: + dma_state = DMA_RUNNING_BYTE_1; + break; + + case DMA_RUNNING_BYTE_1: { + dma_value = dma_read(); + break; + } + } + } + + cur_live.state = RUNNING; + checkpoint(); + break; + } + } + } +} + +bool paula_fdc_device::dma_enabled() +{ + return (dsklen & 0x8000) && ((dmacon & 0x0210) == 0x0210); +} + +void paula_fdc_device::dma_check() +{ + bool was_writing = dskbyt & 0x2000; + dskbyt &= 0x9fff; + if(dma_enabled()) { + LOGDMA("%s: DMA start dskpt=%08x dsklen=%04x dir=%s adkcon=%04x dsksync=%04x state=%d\n", + machine().describe_context(), + dskpt, dsklen & 0x3fff, BIT(dsklen, 14) ? "RAM->disk" : "disk->RAM", adkcon, dsksync, dma_state + ); + + if(dma_state == IDLE) { + dma_state = adkcon & 0x0400 ? DMA_WAIT_START : DMA_RUNNING_BYTE_0; + if(dma_state == DMA_RUNNING_BYTE_0) { + if(!(dsklen & 0x3fff)) + dma_done(); + else if(dsklen & 0x4000) { + dskbyt |= 0x2000; + cur_live.bit_counter = 0; + dma_value = dma_read(); + } + } + } else { + if(dsklen & 0x4000) + dskbyt |= 0x2000; + } + } else + dma_state = IDLE; + + if(was_writing && !(dskbyt & 0x2000)) + cur_live.pll.stop_writing(floppy, cur_live.tm); + if(!was_writing && (dskbyt & 0x2000)) + cur_live.pll.start_writing(cur_live.tm); +} + +void paula_fdc_device::adkcon_set(uint16_t data) +{ + live_sync(); + adkcon = data; + live_run(); +} + +uint16_t paula_fdc_device::adkcon_r(void) +{ + return adkcon; +} + +void paula_fdc_device::dsklen_w(uint16_t data) +{ + live_sync(); + if(!(data & 0x8000) || (data == pre_dsklen)) { + dsklen = pre_dsklen = data; + dma_check(); + + } else + pre_dsklen = data; + live_run(); +} + +void paula_fdc_device::dskpth_w(uint16_t data) +{ + live_sync(); + dskpt = (dskpt & 0xffff) | (data << 16); + live_run(); +} + +void paula_fdc_device::dskptl_w(uint16_t data) +{ + live_sync(); + dskpt = (dskpt & 0xffff0000) | data; + live_run(); +} + +uint16_t paula_fdc_device::dskpth_r() +{ + return dskpt >> 16; +} + +uint16_t paula_fdc_device::dskptl_r() +{ + return dskpt; +} + +void paula_fdc_device::dsksync_w(uint16_t data) +{ + live_sync(); + LOGSYNC("%s: DSKSYNC %04x\n", machine().describe_context(), data); + dsksync = data; + live_run(); +} + +void paula_fdc_device::dmacon_set(uint16_t data) +{ + live_sync(); + // log changes only + // FIXME: needs better boilerplate code on top level + if ((data & 0x210) != (dmacon & 0x210)) + LOGDMA("%s: DMACON set DSKEN %d DMAEN %d (%04x)\n", machine().describe_context(), BIT(data, 4), BIT(data, 9), data); + dmacon = data; + dma_check(); + live_run(); +} + +uint16_t paula_fdc_device::dskbytr_r() +{ + uint16_t res = (dskbyt & ~0x4000); + // check if DMA is on + // logica2 diagnostic BIOS floppy test requires this + bool dmaon = (dma_state != DMA_IDLE) && ((dmacon & 0x0210) == 0x0210); + res |= dmaon << 14; + + // reset DSKBYT ready on read + if (!machine().side_effects_disabled()) + dskbyt &= 0x7fff; + return res; +} + +TIMER_CALLBACK_MEMBER(paula_fdc_device::update_tick) +{ + live_sync(); + live_run(); +} + +void paula_fdc_device::setup_leds() +{ + if(floppy) { + int drive = + floppy == floppy_devices[0] ? 0 : + floppy == floppy_devices[1] ? 1 : + floppy == floppy_devices[2] ? 2 : + 3; + + m_leds[0] = drive == 0 ? 1 : 0; // update internal drive led + m_leds[1] = drive == 1 ? 1 : 0; // update external drive led + } +} + +void paula_fdc_device::ciaaprb_w(uint8_t data) +{ + floppy_image_device *old_floppy = floppy; + + live_sync(); + + // FIXME: several sources claims that multiple drive selects is really possible + if(!(data & 0x08)) + floppy = floppy_devices[0]; + else if(!(data & 0x10)) + floppy = floppy_devices[1]; + else if(!(data & 0x20)) + floppy = floppy_devices[2]; + else if(!(data & 0x40)) + floppy = floppy_devices[3]; + else + floppy = nullptr; + + if(old_floppy != floppy) { + if(old_floppy) + old_floppy->setup_index_pulse_cb(floppy_image_device::index_pulse_cb()); + if(floppy) + floppy->setup_index_pulse_cb(floppy_image_device::index_pulse_cb(&paula_fdc_device::index_callback, this)); + } + + // btanb: trackdisk has no HW interlock for motor running with disk not inserted. + if(floppy) { + floppy->ss_w(!(BIT(data, 2))); + floppy->dir_w(BIT(data, 1)); + floppy->stp_w(BIT(data, 0)); + floppy->mon_w(BIT(data, 7)); + m_fdc_led = BIT(data, 7); // LED directly connected to FDC motor + } + + if(floppy) { + if(cur_live.state == IDLE) + live_start(); + } else + live_abort(); + + setup_leds(); + live_run(); +} + +uint8_t paula_fdc_device::ciaapra_r() +{ + uint8_t ret = 0x3c; + if(floppy) { + // FIXME: seems to not work well with multiple disk drives + //if(!floppy->ready_r()) + ret &= ~0x20; + if(!floppy->trk00_r()) + ret &= ~0x10; + if(floppy->wpt_r()) + ret &= ~0x08; + if(!floppy->dskchg_r()) + ret &= ~0x04; + } + + return ret; +} + +void paula_fdc_device::index_callback(floppy_image_device *floppy, int state) +{ + /* Issue a index pulse when a disk revolution completes */ + m_write_index(!state); +} + +void paula_fdc_device::pll_t::set_clock(const attotime &period) +{ + for(int i=0; i<38; i++) + delays[i] = period*(i+1); +} + +void paula_fdc_device::pll_t::reset(const attotime &when) +{ + counter = 0; + increment = 146; + transition_time = 0xffff; + history = 0x80; + slot = 0; + ctime = when; + phase_add = 0x00; + phase_sub = 0x00; + freq_add = 0x00; + freq_sub = 0x00; +} + +int paula_fdc_device::pll_t::get_next_bit(attotime &tm, floppy_image_device *floppy, const attotime &limit) +{ + attotime when = floppy ? floppy->get_next_transition(ctime) : attotime::never; + + for(;;) { + attotime etime = ctime+delays[slot]; + if(etime > limit) + return -1; + if(transition_time == 0xffff && !when.is_never() && etime >= when) + transition_time = counter; + if(slot < 8) { + uint8_t mask = 1 << slot; + if(phase_add & mask) + counter += 258; + else if(phase_sub & mask) + counter += 34; + else + counter += increment; + + if((freq_add & mask) && increment < 159) + increment++; + else if((freq_sub & mask) && increment > 134) + increment--; + } else + counter += increment; + + slot++; + tm = etime; + if(counter & 0x800) + break; + } + + int bit = transition_time != 0xffff; + + if(transition_time != 0xffff) { + static uint8_t const pha[8] = { 0xf, 0x7, 0x3, 0x1, 0, 0, 0, 0 }; + static uint8_t const phs[8] = { 0, 0, 0, 0, 0x1, 0x3, 0x7, 0xf }; + static uint8_t const freqa[4][8] = { + { 0xf, 0x7, 0x3, 0x1, 0, 0, 0, 0 }, + { 0x7, 0x3, 0x1, 0, 0, 0, 0, 0 }, + { 0x7, 0x3, 0x1, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 } + }; + static uint8_t const freqs[4][8] = { + { 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0x1, 0x3, 0x7 }, + { 0, 0, 0, 0, 0, 0x1, 0x3, 0x7 }, + { 0, 0, 0, 0, 0x1, 0x3, 0x7, 0xf }, + }; + + int cslot = transition_time >> 8; + phase_add = pha[cslot]; + phase_sub = phs[cslot]; + int way = transition_time & 0x400 ? 1 : 0; + if(history & 0x80) + history = way ? 0x80 : 0x83; + else if(history & 0x40) + history = way ? history & 2 : (history & 2) | 1; + freq_add = freqa[history & 3][cslot]; + freq_sub = freqs[history & 3][cslot]; + history = way ? (history >> 1) | 2 : history >> 1; + + } else + phase_add = phase_sub = freq_add = freq_sub = 0; + + counter &= 0x7ff; + + ctime = tm; + transition_time = 0xffff; + slot = 0; + + return bit; +} + +void paula_fdc_device::pll_t::start_writing(const attotime & tm) +{ + write_start_time = tm; + write_position = 0; +} + +void paula_fdc_device::pll_t::stop_writing(floppy_image_device *floppy, const attotime &tm) +{ + commit(floppy, tm); + write_start_time = attotime::never; +} + +bool paula_fdc_device::pll_t::write_next_bit(bool bit, attotime &tm, floppy_image_device *floppy, const attotime &limit) +{ + if(write_start_time.is_never()) { + write_start_time = ctime; + write_position = 0; + } + + for(;;) { + attotime etime = ctime+delays[slot]; + if(etime > limit) + return true; + uint16_t pre_counter = counter; + counter += increment; + if(bit && !(pre_counter & 0x400) && (counter & 0x400)) + if(write_position < std::size(write_buffer)) + write_buffer[write_position++] = etime; + slot++; + tm = etime; + if(counter & 0x800) + break; + } + + counter &= 0x7ff; + + ctime = tm; + slot = 0; + + return false; +} + + +void paula_fdc_device::pll_t::commit(floppy_image_device *floppy, const attotime &tm) +{ + if(write_start_time.is_never() || tm == write_start_time) + return; + + if(floppy) + floppy->write_flux(write_start_time, tm, write_position, write_buffer); + write_start_time = tm; + write_position = 0; +} diff --git a/src/mame/amiga/paulafdc.h b/src/mame/amiga/paulafdc.h new file mode 100644 index 00000000000..c5268702aa5 --- /dev/null +++ b/src/mame/amiga/paulafdc.h @@ -0,0 +1,130 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert +#ifndef MAME_AMIGA_PAULAFDC_H +#define MAME_AMIGA_PAULAFDC_H + +#pragma once + +#include "imagedev/floppy.h" + +class paula_fdc_device : public device_t { +public: + paula_fdc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + auto index_callback() { return m_write_index.bind(); } + auto read_dma_callback() { return m_read_dma.bind(); } + auto write_dma_callback() { return m_write_dma.bind(); } + auto dskblk_callback() { return m_write_dskblk.bind(); } + auto dsksyn_callback() { return m_write_dsksyn.bind(); } + + void ciaaprb_w(uint8_t data); + + uint8_t ciaapra_r(); + uint16_t dskbytr_r(); + uint16_t dskpth_r(); + uint16_t dskptl_r(); + + void dsksync_w(uint16_t data); + void dskpth_w(uint16_t data); + void dskptl_w(uint16_t data); + void dsklen_w(uint16_t data); + void adkcon_set(uint16_t data); + void dmacon_set(uint16_t data); + uint16_t adkcon_r(void); + + static void floppy_formats(format_registration &fr); + +protected: + virtual void device_start() override ATTR_COLD; + virtual void device_reset() override ATTR_COLD; + + TIMER_CALLBACK_MEMBER(update_tick); + +private: + // Running states + enum { + IDLE, + RUNNING, + RUNNING_SYNCPOINT + }; + + // DMA states + enum { + DMA_IDLE, + DMA_WAIT_START, + DMA_RUNNING_BYTE_0, + DMA_RUNNING_BYTE_1 + }; + + struct pll_t { + uint16_t counter; + uint16_t increment; + uint16_t transition_time; + uint8_t history; + uint8_t slot; + uint8_t phase_add, phase_sub, freq_add, freq_sub; + attotime ctime; + + attotime delays[38]; + + attotime write_start_time; + attotime write_buffer[32]; + int write_position; + + void set_clock(const attotime &period); + void reset(const attotime &when); + int get_next_bit(attotime &tm, floppy_image_device *floppy, const attotime &limit); + bool write_next_bit(bool bit, attotime &tm, floppy_image_device *floppy, const attotime &limit); + void start_writing(const attotime &tm); + void commit(floppy_image_device *floppy, const attotime &tm); + void stop_writing(floppy_image_device *floppy, const attotime &tm); + }; + + struct live_info { + attotime tm; + int state, next_state; + uint16_t shift_reg; + int bit_counter; + pll_t pll; + }; + + devcb_write_line m_write_index; + devcb_read16 m_read_dma; + devcb_write16 m_write_dma; + devcb_write_line m_write_dskblk; + devcb_write_line m_write_dsksyn; + output_finder<2> m_leds; + output_finder<> m_fdc_led; + + floppy_image_device *floppy; + floppy_image_device *floppy_devices[4]; + + live_info cur_live, checkpoint_live; + + emu_timer *t_gen; + uint16_t dsklen, pre_dsklen, dsksync, dskbyt, adkcon, dmacon; + uint32_t dskpt; + uint16_t dma_value; + + int dma_state; + + void setup_leds(); + void index_callback(floppy_image_device *floppy, int state); + bool dma_enabled(); + void dma_check(); + void dma_done(); + void dma_write(uint16_t value); + uint16_t dma_read(); + + void live_start(); + void checkpoint(); + void rollback(); + void live_delay(int state); + void live_sync(); + void live_abort(); + void live_run(const attotime &limit = attotime::never); +}; + +DECLARE_DEVICE_TYPE(PAULA_FDC, paula_fdc_device) + +#endif // MAME_AMIGA_PAULAFDC_H diff --git a/src/mame/amiga/upscope.cpp b/src/mame/amiga/upscope.cpp index 8f79987d055..4caa4f0bc4e 100644 --- a/src/mame/amiga/upscope.cpp +++ b/src/mame/amiga/upscope.cpp @@ -273,7 +273,7 @@ void upscope_state::upscope(machine_config &config) ADDRESS_MAP_BANK(config, m_overlay).set_map(&upscope_state::overlay_512kb_map).set_options(ENDIANNESS_BIG, 16, 22, 0x200000); ADDRESS_MAP_BANK(config, m_chipset).set_map(&upscope_state::ocs_map).set_options(ENDIANNESS_BIG, 16, 9, 0x200); - AMIGA_COPPER(config, m_copper, amiga_state::CLK_7M_NTSC); + AGNUS_COPPER(config, m_copper, amiga_state::CLK_7M_NTSC); m_copper->set_host_cpu_tag(m_maincpu); m_copper->mem_read_cb().set(FUNC(amiga_state::chip_ram_r)); m_copper->set_ecs_mode(false); @@ -312,7 +312,7 @@ void upscope_state::upscope(machine_config &config) m_cia_1->pa_wr_callback().set(FUNC(upscope_state::upscope_cia_1_porta_w)); /* fdc */ - AMIGA_FDC(config, m_fdc, amiga_state::CLK_7M_NTSC); + PAULA_FDC(config, m_fdc, amiga_state::CLK_7M_NTSC); m_fdc->index_callback().set("cia_1", FUNC(mos8520_device::flag_w)); m_fdc->read_dma_callback().set(FUNC(amiga_state::chip_ram_r)); m_fdc->write_dma_callback().set(FUNC(amiga_state::chip_ram_w)); -- cgit v1.2.3