mirror of
https://github.com/holub/mame
synced 2025-04-09 18:17:44 +03:00
Replace uPD1771c high level emulation with a cpu core. (#13106)
* cpu/upd177x/upd177x.cpp: Add NEC uPD177x cpu core. * scv.xml: Promoted 1 item. Software list items promoted to working ------------------------------------------ Star Speeder * cpu/upd177x/upd177x.cpp: Remove a comment. * cpu/upd177x/upd177x.cpp: Remove callback on PA. * cpu/upd177x/upd177x.cpp: Update comment about setting m_pc to 1 during reset. * Add a different workaround for the sync issue between the 2 cpus in scv. * cpu/upd1771x/updf177x.cpp: Use little endian. * cpu/upd177x/upd177x.cpp: Include device tag in fatalerror messages. * epoch/scv.cpp: Update upd1771c rom region sizes. * nec/apc.cpp: Switch to upd177x_cpu_device. * epoch/scv.cpp: Add location to upd1771c rom name. * cpu/upd177x/upd177x.h: Remove 'overrides' comments.
This commit is contained in:
parent
07cc2cf76a
commit
926b6182b1
@ -363,11 +363,10 @@ Information found at http://www.rhod.fr/yeno_epoch.html
|
||||
</part>
|
||||
</software>
|
||||
|
||||
<software name="starspdr" supported="partial">
|
||||
<software name="starspdr">
|
||||
<description>Star Speeder</description>
|
||||
<year>1985</year>
|
||||
<publisher>Epoch</publisher>
|
||||
<notes>uPD1771 adpcm playback is not supported.</notes>
|
||||
<part name="cart" interface="scv_cart">
|
||||
<feature name="slot" value="rom64k"/>
|
||||
<dataarea name="rom" size="0x10000">
|
||||
|
@ -3930,6 +3930,13 @@ end
|
||||
--@src/devices/cpu/upd177x/upd177x.h,CPUS["UPD177X"] = true
|
||||
--------------------------------------------------
|
||||
|
||||
if CPUS["UPD177X"] then
|
||||
files {
|
||||
MAME_DIR .. "src/devices/cpu/upd177x/upd177x.cpp",
|
||||
MAME_DIR .. "src/devices/cpu/upd177x/upd177x.h",
|
||||
}
|
||||
end
|
||||
|
||||
if opt_tool(CPUS, "UPD177X") then
|
||||
table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/upd177x/upd177xd.cpp")
|
||||
table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/upd177x/upd177xd.h")
|
||||
|
@ -1424,18 +1424,6 @@ if (SOUNDS["AD1848"]~=null) then
|
||||
}
|
||||
end
|
||||
|
||||
---------------------------------------------------
|
||||
-- UPD1771
|
||||
--@src/devices/sound/upd1771.h,SOUNDS["UPD1771"] = true
|
||||
---------------------------------------------------
|
||||
|
||||
if (SOUNDS["UPD1771"]~=null) then
|
||||
files {
|
||||
MAME_DIR .. "src/devices/sound/upd1771.cpp",
|
||||
MAME_DIR .. "src/devices/sound/upd1771.h",
|
||||
}
|
||||
end
|
||||
|
||||
---------------------------------------------------
|
||||
-- GB_SOUND
|
||||
--@src/devices/sound/gb.h,SOUNDS["GB_SOUND"] = true
|
||||
|
794
src/devices/cpu/upd177x/upd177x.cpp
Normal file
794
src/devices/cpu/upd177x/upd177x.cpp
Normal file
@ -0,0 +1,794 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Wilbert Pol
|
||||
/*
|
||||
|
||||
TODO:
|
||||
- Only upd1771c supported
|
||||
- Noise, time, and external interrupts not implemented
|
||||
- CH1/CH2 not implemented
|
||||
- Not all instructions implemented
|
||||
|
||||
*/
|
||||
|
||||
#include "emu.h"
|
||||
#include "upd177x.h"
|
||||
#include "upd177xd.h"
|
||||
|
||||
#define VERBOSE 0
|
||||
#include "logmacro.h"
|
||||
|
||||
|
||||
DEFINE_DEVICE_TYPE(UPD1771C, upd1771c_cpu_device, "upd1771c", "NEC uPD1771C")
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
UPD177X_PC, UPD177X_SP,
|
||||
UPD177X_A, UPD177X_A_SAVE, UPD177X_H, UPD177X_N, UPD177X_X, UPD177X_Y,
|
||||
UPD177X_TIMER, UPD177X_PNC1, UPD177X_PNC2,
|
||||
UPD177X_PA, UPD177X_PB, UPD177X_MD0, UPD177X_MD1, UPD177X_SKIP, UPD177X_SKIP_SAVE
|
||||
};
|
||||
|
||||
|
||||
static constexpr u8 INT_TONE1 = 1 << 0;
|
||||
static constexpr u8 INT_TONE2 = 1 << 1;
|
||||
static constexpr u8 INT_TONE3 = 1 << 2;
|
||||
static constexpr u8 INT_TONE4 = 1 << 3;
|
||||
static constexpr u8 INT_NOISE = 1 << 4;
|
||||
static constexpr u8 INT_EXT = 1 << 5;
|
||||
static constexpr u8 INT_TIME = 1 << 6;
|
||||
|
||||
static constexpr u8 MD0_3264_BIT = 0;
|
||||
static constexpr u8 MD0_TN_BIT = 1;
|
||||
static constexpr u8 MD0_NS_BIT = 2;
|
||||
static constexpr u8 MD0_NSF1_BIT = 3;
|
||||
static constexpr u8 MD0_NSF2_BIT = 4;
|
||||
static constexpr u8 MD0_OUT_BIT = 5;
|
||||
static constexpr u8 MD0_IF_BIT = 6;
|
||||
static constexpr u8 MD1_NSF3_BIT = 0;
|
||||
static constexpr u8 MD1_TIME_BIT = 1;
|
||||
static constexpr u8 MD1_EXT_BIT = 2;
|
||||
|
||||
static constexpr u16 VECTOR_TONE1 = 0x20;
|
||||
static constexpr u16 VECTOR_TONE2 = 0x24;
|
||||
static constexpr u16 VECTOR_TONE3 = 0x28;
|
||||
static constexpr u16 VECTOR_TONE4 = 0x2c;
|
||||
static constexpr u16 VECTOR_NOISE = 0x48;
|
||||
static constexpr u16 VECTOR_EXT = 0x60;
|
||||
static constexpr u16 VECTOR_TIME = 0x80;
|
||||
|
||||
static constexpr u8 STACK_START = 0x20;
|
||||
|
||||
|
||||
upd177x_cpu_device::upd177x_cpu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock)
|
||||
: cpu_device(mconfig, type, tag, owner, clock)
|
||||
, device_sound_interface(mconfig, *this)
|
||||
, m_program_config("program", ENDIANNESS_LITTLE, 16, 16, -1, address_map_constructor(FUNC(upd177x_cpu_device::program_map), this))
|
||||
, m_pb_out_cb(*this)
|
||||
{ }
|
||||
|
||||
|
||||
upd1771c_cpu_device::upd1771c_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
|
||||
: upd177x_cpu_device(mconfig, UPD1771C, tag, owner, clock)
|
||||
{ }
|
||||
|
||||
|
||||
device_memory_interface::space_config_vector upd177x_cpu_device::memory_space_config() const
|
||||
{
|
||||
return space_config_vector
|
||||
{
|
||||
std::make_pair(AS_PROGRAM, &m_program_config)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
void upd177x_cpu_device::program_map(address_map &map)
|
||||
{
|
||||
map(0, 0x1ff).rom();
|
||||
}
|
||||
|
||||
|
||||
void upd177x_cpu_device::device_start()
|
||||
{
|
||||
space(AS_PROGRAM).specific(m_program);
|
||||
|
||||
state_add(UPD177X_PC, "PC", m_pc);
|
||||
state_add(STATE_GENPC, "GENPC", m_pc).noshow();
|
||||
state_add(STATE_GENPCBASE, "CURPC", m_pc).noshow();
|
||||
state_add(UPD177X_SP, "SP", m_sp);
|
||||
state_add(UPD177X_A, "A", m_a);
|
||||
state_add(UPD177X_A_SAVE, "A'", m_a_save);
|
||||
state_add(UPD177X_H, "H", m_h);
|
||||
state_add(UPD177X_N, "N", m_n);
|
||||
state_add(UPD177X_TIMER, "Timer", m_timer);
|
||||
state_add(UPD177X_X, "X", m_x);
|
||||
state_add(UPD177X_Y, "Y", m_y);
|
||||
state_add(UPD177X_PA, "PA", m_pa);
|
||||
state_add(UPD177X_PB, "PB", m_pb);
|
||||
state_add(UPD177X_MD0, "MD0", m_md0);
|
||||
state_add(UPD177X_MD1, "MD1", m_md1);
|
||||
state_add(UPD177X_PNC1, "PNC1", m_pnc1);
|
||||
state_add(UPD177X_PNC2, "PNC2", m_pnc2);
|
||||
state_add(UPD177X_SKIP, "SKIP", m_skip);
|
||||
state_add(UPD177X_SKIP_SAVE, "SKIP'", m_skip_save);
|
||||
|
||||
save_item(NAME(m_pc));
|
||||
save_item(NAME(m_sp));
|
||||
save_item(NAME(m_a));
|
||||
save_item(NAME(m_a_save));
|
||||
save_item(NAME(m_h));
|
||||
save_item(NAME(m_n));
|
||||
save_item(NAME(m_timer));
|
||||
save_item(NAME(m_x));
|
||||
save_item(NAME(m_y));
|
||||
save_item(NAME(m_pa));
|
||||
save_item(NAME(m_pb));
|
||||
save_item(NAME(m_ram));
|
||||
save_item(NAME(m_md0));
|
||||
save_item(NAME(m_md1));
|
||||
save_item(NAME(m_pnc1));
|
||||
save_item(NAME(m_pnc2));
|
||||
save_item(NAME(m_skip));
|
||||
save_item(NAME(m_skip_save));
|
||||
save_item(NAME(m_ts));
|
||||
save_item(NAME(m_ns));
|
||||
save_item(NAME(m_ss));
|
||||
save_item(NAME(m_dac));
|
||||
save_item(NAME(m_dac_sign));
|
||||
save_item(NAME(m_tn_int));
|
||||
save_item(NAME(m_counter));
|
||||
|
||||
set_icountptr(m_icount);
|
||||
|
||||
m_channel = stream_alloc(0, 1, clock() / 4);
|
||||
}
|
||||
|
||||
|
||||
void upd177x_cpu_device::device_reset()
|
||||
{
|
||||
m_pc = 0;
|
||||
m_sp = 0;
|
||||
m_a = 0;
|
||||
m_a_save = 0;
|
||||
m_h = 0;
|
||||
m_n = 0;
|
||||
m_x = 0;
|
||||
m_y = 0;
|
||||
m_pa = 0;
|
||||
m_pb = 0;
|
||||
m_md0 = 0;
|
||||
m_md1 = 0;
|
||||
m_skip = false;
|
||||
m_skip_save = false;
|
||||
m_ts = false;
|
||||
m_ns = false;
|
||||
m_ss = false;
|
||||
m_dac = 0;
|
||||
m_dac_sign = true;
|
||||
m_tn_int = false;
|
||||
m_counter = 0;
|
||||
m_timer = 0;
|
||||
set_noise_counter_bit();
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<util::disasm_interface> upd177x_cpu_device::create_disassembler()
|
||||
{
|
||||
return std::make_unique<upd177x_disassembler>();
|
||||
}
|
||||
|
||||
|
||||
void upd177x_cpu_device::op0xxx(u16 opcode)
|
||||
{
|
||||
switch (opcode)
|
||||
{
|
||||
case 0x0000: // NOP
|
||||
break;
|
||||
case 0x0002: // OUT PA
|
||||
m_pa = m_a;
|
||||
break;
|
||||
case 0x0004: // OUT PB
|
||||
m_pb = m_a;
|
||||
m_pb_out_cb(m_pb);
|
||||
break;
|
||||
case 0x0008: // MOV X,RG
|
||||
m_x = m_pnc1 & 0x7f;
|
||||
break;
|
||||
case 0x0201: // MOV N,A
|
||||
m_n = m_a;
|
||||
break;
|
||||
case 0x0208: // MOV X,A
|
||||
m_x = m_a & 0x7f;
|
||||
break;
|
||||
case 0x0401: // IN PA
|
||||
m_a = m_pa;
|
||||
break;
|
||||
case 0x0402: // IN PB
|
||||
m_a = m_pb;
|
||||
break;
|
||||
case 0x0404: // RAR
|
||||
m_a = ((m_a & 0x01) << 7) | (m_a >> 1);
|
||||
break;
|
||||
case 0x0408: // RAL
|
||||
m_a = ((m_a & 0x80) >> 7) | (m_a << 1);
|
||||
break;
|
||||
case 0x0502: // OUT DA
|
||||
m_channel->update();
|
||||
m_dac = (m_ss ^ m_ts) ? ~m_a : m_a;
|
||||
m_dac_sign = m_ss;
|
||||
break;
|
||||
case 0x0504: // MUL1
|
||||
if (!BIT(m_y, 0))
|
||||
{
|
||||
m_a = m_a >> 1;
|
||||
}
|
||||
m_y = m_y >> 1;
|
||||
break;
|
||||
case 0x050c: // MUL2, usually gets executed 5 times to calculate X * (Y / 32)
|
||||
if (BIT(m_y, 0))
|
||||
{
|
||||
m_a = (m_a + m_x) >> 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_a = m_a >> 1;
|
||||
}
|
||||
m_y = m_y >> 1;
|
||||
break;
|
||||
case 0x0800: // RET
|
||||
m_sp = (m_sp - 1) & 0x07;
|
||||
m_pc = (m_ram[STACK_START + (m_sp * 2) + 1] << 8) | m_ram[STACK_START + (m_sp * 2)];
|
||||
break;
|
||||
case 0x090f: // RETI
|
||||
m_sp = (m_sp - 1) & 0x07;
|
||||
m_pc = (m_ram[STACK_START + (m_sp * 2) + 1] << 8) | m_ram[STACK_START + (m_sp * 2)];
|
||||
m_a = m_a_save;
|
||||
m_skip = m_skip_save;
|
||||
m_tn_int = false;
|
||||
break;
|
||||
|
||||
case 0x0005: // STF
|
||||
case 0x0101: // MON
|
||||
case 0x0501: // JMPA
|
||||
case 0x0602: // OFF
|
||||
case 0x0801: // RETS
|
||||
default:
|
||||
fatalerror("%s, %04x: Unsupported instruction %04x\n", tag(), m_pc - 1, opcode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void upd177x_cpu_device::op1xxx(u16 opcode)
|
||||
{
|
||||
switch (opcode & 0x0e0f)
|
||||
{
|
||||
case 0x0000: // MOV Y,Rr
|
||||
m_y = m_ram[(opcode >> 4) & 0x1f] & 0x1f;
|
||||
break;
|
||||
case 0x0005: // MOV A,Rr
|
||||
m_a = m_ram[(opcode >> 4) & 0x1f];
|
||||
break;
|
||||
case 0x0201: // MOV Rr,A
|
||||
m_ram[(opcode >> 4) & 0x1f] = m_a;
|
||||
break;
|
||||
case 0x0409: // MIX Rr
|
||||
case 0x0509: // MIX Rr
|
||||
m_ns = BIT(m_md0, MD0_NSF1_BIT) ? BIT(m_pnc2 , 2) : false;
|
||||
if (m_ts ^ m_ns)
|
||||
{
|
||||
m_ss = m_ts ^ ((m_ram[(opcode >> 4) & 0x1f] < m_a) ? true : false);
|
||||
m_a = m_ram[(opcode >> 4) & 0x1f] - m_a;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ss = m_ts ^ ((m_ram[(opcode >> 4) & 0x1f] + m_a > 255) ? true : false);
|
||||
m_a = m_ram[(opcode >> 4) & 0x1f] + m_a;
|
||||
}
|
||||
break;
|
||||
case 0x0601: // MOV (H),A
|
||||
m_ram[m_h] = m_a;
|
||||
break;
|
||||
case 0x0801: // TBL0 A,(Rr)
|
||||
case 0x0901: // TBL0 A,(Rr)
|
||||
{
|
||||
const u8 reg = (opcode >> 4) & 0x1e;
|
||||
const u16 addr = (m_ram[reg + 1] << 8) | m_ram[reg];
|
||||
const u16 data = m_program.read_word(addr >> 1);
|
||||
m_a = BIT(addr, 0) ? data >> 8 : data & 0xff;
|
||||
}
|
||||
m_cycle();
|
||||
break;
|
||||
case 0x0802: // TBL0 X,(Rr)
|
||||
case 0x0902: // TBL0 X,(Rr)
|
||||
{
|
||||
const u8 reg = (opcode >> 4) & 0x1e;
|
||||
const u16 addr = (m_ram[reg + 1] << 8) | m_ram[reg];
|
||||
const u16 data = m_program.read_word(addr >> 1);
|
||||
m_x = BIT(addr, 0) ? (data >> 8) : data;
|
||||
m_ts = BIT(m_x, 7);
|
||||
m_x &= 0x7f;
|
||||
}
|
||||
m_cycle();
|
||||
break;
|
||||
|
||||
case 0x000a: // MOV H,Rr
|
||||
case 0x0202: // MOV Rr,H
|
||||
case 0x0205: // XCHG Rr,A
|
||||
case 0x020a: // XCHG Rr,H
|
||||
case 0x0405: // MOV A,(H)
|
||||
case 0x0605: // XCHG (H),A
|
||||
case 0x0804: // TBL0 Y,(Rr)
|
||||
case 0x0904: // TBL0 Y,(Rr)
|
||||
case 0x0808: // CALL0 (Rr)
|
||||
case 0x0908: // CALL0 (Rr)
|
||||
case 0x0a01: // TBL1 A,(Rr)
|
||||
case 0x0b01: // TBL1 A,(Rr)
|
||||
case 0x0a02: // TBL1 X,(Rr)
|
||||
case 0x0b02: // TBL1 X,(Rr)
|
||||
case 0x0a04: // TBL1 Y,(Rr)
|
||||
case 0x0b04: // TBL1 Y,(Rr)
|
||||
case 0x0a08: // CALL1 (Rr)
|
||||
case 0x0b08: // CALL1 (Rr)
|
||||
default:
|
||||
fatalerror("%s, %04x: Unsupported instruction %04x\n", tag(), m_pc - 1, opcode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void upd177x_cpu_device::op2xxx(u16 opcode)
|
||||
{
|
||||
switch (opcode & 0x0f00)
|
||||
{
|
||||
case 0x0100: // MVI MD0,A
|
||||
m_md0 = opcode & 0x7f;
|
||||
set_noise_counter_bit();
|
||||
break;
|
||||
case 0x0000: // JPP n
|
||||
case 0x8000: // JMPFZ n
|
||||
default:
|
||||
fatalerror("%s, %04x: Unsupported instruction %04x\n", tag(), m_pc - 1, opcode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void upd177x_cpu_device::op3xxx(u16 opcode)
|
||||
{
|
||||
switch (opcode & 0x0f00)
|
||||
{
|
||||
case 0x0400: // MVI A,n
|
||||
m_a = opcode & 0xff;
|
||||
break;
|
||||
case 0x0800:
|
||||
if (!(opcode & 0x00c0))
|
||||
{
|
||||
m_h = opcode & 0x3f;
|
||||
}
|
||||
break;
|
||||
case 0x0100: // MVI MD1,n
|
||||
case 0x0200: // MVI (H),n
|
||||
default:
|
||||
fatalerror("%s, %04x: Unsupported instruction %04x\n", tag(), m_pc - 1, opcode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void upd177x_cpu_device::op7xxx(u16 opcode)
|
||||
{
|
||||
// CALL
|
||||
m_ram[STACK_START + (m_sp * 2) + 1] = m_pc >> 8;
|
||||
m_ram[STACK_START + (m_sp * 2)] = m_pc & 0xff;
|
||||
m_sp = (m_sp + 1) & 0x07;
|
||||
m_pc = (m_pc & 0xf000) | (opcode & 0x0fff);
|
||||
}
|
||||
|
||||
|
||||
void upd177x_cpu_device::op89xxx(u16 opcode)
|
||||
{
|
||||
switch (opcode & 0x1f00)
|
||||
{
|
||||
case 0x0000: // ADI A,n
|
||||
m_a = m_a + (opcode & 0xff);
|
||||
break;
|
||||
case 0x0200: // ANDI A,n
|
||||
m_a = m_a & (opcode & 0xff);
|
||||
break;
|
||||
case 0x0600: // ORI A,n
|
||||
m_a = m_a | (opcode & 0xff);
|
||||
break;
|
||||
case 0x0800: // ADIS A,n
|
||||
m_a = m_a + (opcode & 0xff);
|
||||
m_skip = (m_a < (opcode & 0xff));
|
||||
break;
|
||||
case 0x0c00: // SBIS A,n
|
||||
m_skip = m_a < (opcode & 0xff);
|
||||
m_a = m_a - (opcode & 0xff);
|
||||
break;
|
||||
case 0x0e00: // XORI A,n
|
||||
m_a = m_a ^ (opcode & 0xff);
|
||||
break;
|
||||
case 0x1000: // TADI NC A,n
|
||||
m_skip = (m_a + (opcode & 0xff) < 0x100);
|
||||
break;
|
||||
case 0x1400: // TSBI NC A,n
|
||||
m_skip = (m_a >= (opcode & 0xff));
|
||||
break;
|
||||
case 0x1600: // TSBI NZ A,n
|
||||
m_skip = (m_a != (opcode & 0xff));
|
||||
break;
|
||||
case 0x1a00: // TANDI Z A,n
|
||||
m_skip = (m_a & (opcode & 0xff)) == 0;
|
||||
break;
|
||||
case 0x1c00: // TSBI C A,n
|
||||
m_skip = (m_a < (opcode & 0xff));
|
||||
break;
|
||||
|
||||
case 0x0100: // ADI MD1,n
|
||||
case 0x1100: // ADI MD0,n
|
||||
case 0x0300: // ANDI MD1,n
|
||||
case 0x1300: // ANDI MD0,n
|
||||
case 0x0400: // SBI A,n
|
||||
case 0x0500: // SBI MD1,n
|
||||
case 0x1500: // SBI MD0,n
|
||||
case 0x0700: // ORI MD1,n
|
||||
case 0x1700: // ORI MD0,n
|
||||
case 0x0900: // ADIS MD1,n
|
||||
case 0x1900: // ADIS MD0,n
|
||||
case 0x0a00: // ANDIS A,n
|
||||
case 0x0b00: // ANDIS MD1,n
|
||||
case 0x1b00: // ANDIS MD0,n
|
||||
case 0x0d00: // SBIS MD1,n
|
||||
case 0x1d00: // SBIS MD0,n
|
||||
case 0x0f00: // XORI MD1,n
|
||||
case 0x1f00: // XORI MD0,n
|
||||
case 0x1200: // TANDI NZ A,n
|
||||
case 0x1800: // TADI C A,n
|
||||
case 0x1e00: // TSBI Z A,n
|
||||
default:
|
||||
fatalerror("%s, %04x: Unsupported instruction %04x\n", tag(), m_pc - 1, opcode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void upd177x_cpu_device::opabxxx(u16 opcode)
|
||||
{
|
||||
switch (opcode & 0x1f00)
|
||||
{
|
||||
case 0x0d00: // SBS H,n
|
||||
{
|
||||
u8 old_h = m_h;
|
||||
m_h = (m_h - (opcode & 0x3f)) & 0x3f;
|
||||
if (old_h < m_h)
|
||||
{
|
||||
m_skip = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x1200: // TANDI NZ (H),n
|
||||
m_skip = (m_ram[m_h] & (opcode & 0xff));
|
||||
break;
|
||||
case 0x1a00: // TANDI Z (H),n
|
||||
m_skip = (m_ram[m_h] & (opcode & 0xff)) == 0;
|
||||
break;
|
||||
case 0x1f00: // TSB Z H,n
|
||||
m_skip = (m_h == (opcode & 0xff));
|
||||
break;
|
||||
|
||||
case 0x0000: // AD (H),n
|
||||
case 0x0100: // AD H,n
|
||||
case 0x0200: // AND (H),n
|
||||
case 0x0300: // AND H,n
|
||||
case 0x0400: // SB (H),n
|
||||
case 0x0500: // SB H,n
|
||||
case 0x0600: // OR (H),n
|
||||
case 0x0700: // OR H,n
|
||||
case 0x0800: // ADS (H),n
|
||||
case 0x0900: // ADS H,n
|
||||
case 0x0a00: // ANDS (H),n
|
||||
case 0x0b00: // ANDS H,n
|
||||
case 0x0c00: // SBS (H),n
|
||||
case 0x0e00: // XOR (H),n
|
||||
case 0x0f00: // XOR H,n
|
||||
case 0x1000: // TAD NC (H),n
|
||||
case 0x1100: // TAD NC H,n
|
||||
case 0x1300: // TAND NZ H,n
|
||||
case 0x1400: // TSB NC (H),n
|
||||
case 0x1500: // TSB NC H,n
|
||||
case 0x1600: // TSB NZ (H),n
|
||||
case 0x1700: // TSB NZ H,n
|
||||
case 0x1800: // TAD C (H),n
|
||||
case 0x1900: // TAD C H,n
|
||||
case 0x1b00: // TAND Z H,n
|
||||
case 0x1c00: // TSB C (H),n
|
||||
case 0x1d00: // TSB C H,n
|
||||
case 0x1e00: // TSB Z (H),n
|
||||
default:
|
||||
fatalerror("%s, %04x: Unsupported instruction %04x\n", tag(), m_pc - 1, opcode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void upd177x_cpu_device::opcdxxx(u16 opcode)
|
||||
{
|
||||
switch (opcode & 0x1e0f)
|
||||
{
|
||||
case 0x0008: // AD Rr,A
|
||||
m_ram[(opcode >> 4) & 0x1f] += m_a;
|
||||
break;
|
||||
case 0x1e00: // TSB Z A,Rr
|
||||
m_skip = (m_a == m_ram[(opcode >> 4) & 0x1f]);
|
||||
break;
|
||||
case 0x0000: // AD A,Rr
|
||||
m_a += m_ram[(opcode >> 4) & 0x1f];
|
||||
break;
|
||||
|
||||
case 0x0001: // AD A,(H)
|
||||
case 0x0009: // AD (H),A
|
||||
case 0x0200: // AND A,Rr
|
||||
case 0x0201: // AND A,(H)
|
||||
case 0x0208: // AND Rr,A
|
||||
case 0x0209: // AND (H),A
|
||||
case 0x0400: // SB A,Rr
|
||||
case 0x0401: // SB A,(H)
|
||||
case 0x0408: // SB Rr,A
|
||||
case 0x0409: // SB (H),A
|
||||
case 0x0600: // OR A,Rr
|
||||
case 0x0601: // OR A,(H)
|
||||
case 0x0608: // OR Rr,A
|
||||
case 0x0609: // OR (H),A
|
||||
case 0x0800: // ADS A,Rr
|
||||
case 0x0801: // ADS A,(H)
|
||||
case 0x0808: // ADS Rr,A
|
||||
case 0x0809: // ADS (H),A
|
||||
case 0x0a00: // ANDS A,Rr
|
||||
case 0x0a01: // ANDS A,(H)
|
||||
case 0x0a08: // ANDS Rr,A
|
||||
case 0x0a09: // ANDS (H),A
|
||||
case 0x0c00: // SBS A,Rr
|
||||
case 0x0c01: // SBS A,(H)
|
||||
case 0x0c08: // SBS Rr,A
|
||||
case 0x0c09: // SBS (H),A
|
||||
case 0x0e00: // XOR A,Rr
|
||||
case 0x0e01: // XOR A,(H)
|
||||
case 0x0e08: // XOR Rr,A
|
||||
case 0x0e09: // XOR (H),A
|
||||
case 0x1000: // TAD NC A,Rr
|
||||
case 0x1001: // TAD NC A,(H)
|
||||
case 0x1008: // TAD NC Rr,A
|
||||
case 0x1009: // TAD NC (H),A
|
||||
case 0x1200: // TAND NZ A,Rr
|
||||
case 0x1201: // TAND NZ A,(H)
|
||||
case 0x1208: // TAND NZ Rr,A
|
||||
case 0x1209: // TAND NZ (H),A
|
||||
case 0x1400: // TSB NC A,Rr
|
||||
case 0x1401: // TSB NC A,(H)
|
||||
case 0x1408: // TSB NC Rr,A
|
||||
case 0x1409: // TSB NC (H),A
|
||||
case 0x1600: // TSB NZ A,Rr
|
||||
case 0x1601: // TSB NZ A,(H)
|
||||
case 0x1608: // TSB NZ Rr,A
|
||||
case 0x1609: // TSB NZ (H),A
|
||||
case 0x1800: // TAD C A,Rr
|
||||
case 0x1801: // TAD C A,(H)
|
||||
case 0x1808: // TAD C Rr,A
|
||||
case 0x1809: // TAD C (H),A
|
||||
case 0x1a00: // TAND Z A,Rr
|
||||
case 0x1a01: // TAND Z A,(H)
|
||||
case 0x1a08: // TAND Z Rr,A
|
||||
case 0x1a09: // TAND Z (H),A
|
||||
case 0x1c00: // TSB C A,Rr
|
||||
case 0x1c01: // TSB C A,(H)
|
||||
case 0x1c08: // TSB C Rr,A
|
||||
case 0x1c09: // TSB C (H),A
|
||||
case 0x1e01: // TSB Z A,(H)
|
||||
case 0x1e08: // TSB Z Rr,A
|
||||
case 0x1e09: // TSB Z (H),A
|
||||
default:
|
||||
fatalerror("%s, %04x: Unsupported instruction %04x\n", tag(), m_pc - 1, opcode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void upd177x_cpu_device::opefxxx(u16 opcode)
|
||||
{
|
||||
switch (opcode & 0x1e00)
|
||||
{
|
||||
case 0x0000: // ADI Rr,n
|
||||
m_ram[(opcode >> 4) & 0x1f] += (opcode & 0x0f);
|
||||
break;
|
||||
case 0x0200: // ADIS Rr,n
|
||||
m_ram[(opcode >> 4) & 0x1f] += (opcode & 0x0f);
|
||||
m_skip = (m_ram[(opcode >> 4) & 0x1f] < (opcode & 0x0f));
|
||||
break;
|
||||
case 0x0400: // SBI Rr,n
|
||||
m_ram[(opcode >> 4) & 0x1f] -= (opcode & 0x0f);
|
||||
break;
|
||||
case 0x0600: // SBIS Rr,n
|
||||
m_skip = (m_ram[(opcode >> 4) & 0x1f] < (opcode & 0x0f));
|
||||
m_ram[(opcode >> 4) & 0x1f] -= (opcode & 0x0f);
|
||||
break;
|
||||
case 0x0800: // TADI NC Rr,n
|
||||
m_skip = (m_ram[(opcode >> 4) & 0x1f] + (opcode & 0x0f) < 0x100);
|
||||
break;
|
||||
case 0x0c00: // TSBI NC Rr,n
|
||||
m_skip = (m_ram[(opcode >> 4) & 0x1f] >= (opcode & 0x0f));
|
||||
break;
|
||||
case 0x0e00: // TSBI C Rr,n
|
||||
m_skip = (m_ram[(opcode >> 4) & 0x1f] < (opcode & 0x0f));
|
||||
break;
|
||||
case 0x1200: // ADIMS Rr,n
|
||||
{
|
||||
const u8 reg = (opcode >> 4) & 0x1f;
|
||||
if (BIT(m_md0, 0))
|
||||
{
|
||||
m_skip = ((m_ram[reg] & 0x3f) + (opcode & 0x0f)) > 0x3f;
|
||||
m_ram[reg] = (m_ram[reg] & 0xc0) | ((m_ram[reg] + (opcode & 0x0f)) & 0x3f);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_skip = ((m_ram[reg] & 0x1f) + (opcode & 0x0f)) > 0x1f;
|
||||
m_ram[reg] = (m_ram[reg] & 0xe0) | ((m_ram[reg] + (opcode & 0x0f)) & 0x1f);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x0a00: // TADI C Rr,n
|
||||
case 0x1000: // ADI5 Rr,n
|
||||
case 0x1800: // TADI5 Rr,n
|
||||
default:
|
||||
if (opcode != 0xffff)
|
||||
{
|
||||
fatalerror("%s, %04x: Unsupported instruction %04x\n", tag(), m_pc - 1, opcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void upd177x_cpu_device::handle_timer()
|
||||
{
|
||||
if (m_n >= 8)
|
||||
{
|
||||
if (m_timer == 0 || !--m_timer)
|
||||
{
|
||||
if (BIT(m_md0, MD0_TN_BIT) && !m_tn_int)
|
||||
{
|
||||
// Trigger TN IRQ
|
||||
m_ram[STACK_START + (m_sp * 2) + 1] = m_pc >> 8;
|
||||
m_ram[STACK_START + (m_sp * 2)] = m_pc & 0xff;
|
||||
m_sp = (m_sp + 1) & 0x07;
|
||||
m_skip_save = m_skip;
|
||||
m_skip = false;
|
||||
m_a_save = m_a;
|
||||
if (m_n >= 0x40)
|
||||
{
|
||||
m_pc = VECTOR_TONE4;
|
||||
}
|
||||
else if (m_n >= 0x20)
|
||||
{
|
||||
m_pc = VECTOR_TONE3;
|
||||
}
|
||||
else if (m_n >= 0x10)
|
||||
{
|
||||
m_pc = VECTOR_TONE2;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pc = VECTOR_TONE1;
|
||||
}
|
||||
m_tn_int = true;
|
||||
}
|
||||
if (m_n >= 0x40)
|
||||
{
|
||||
m_timer = m_n;
|
||||
}
|
||||
else if (m_n >= 0x20)
|
||||
{
|
||||
m_timer = m_n * 2;
|
||||
}
|
||||
else if (m_n >= 0x10)
|
||||
{
|
||||
m_timer = m_n * 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_timer = m_n * 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void upd177x_cpu_device::set_noise_counter_bit()
|
||||
{
|
||||
if (BIT(m_md0, MD0_NSF2_BIT))
|
||||
{
|
||||
m_noise_counter_bit = (BIT(m_md1, MD1_NSF3_BIT)) ? 5 : 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_noise_counter_bit = (BIT(m_md1, MD1_NSF3_BIT)) ? 6 : 9;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void upd177x_cpu_device::m_cycle()
|
||||
{
|
||||
m_icount -= 8;
|
||||
const u16 old_counter = m_counter;
|
||||
m_counter++;
|
||||
const u16 counter_diff = old_counter ^ m_counter;
|
||||
if (BIT(counter_diff, 9))
|
||||
{
|
||||
if (BIT(m_md1, MD1_TIME_BIT))
|
||||
{
|
||||
fatalerror("%s: TM int not supported\n", tag());
|
||||
}
|
||||
}
|
||||
if (BIT(counter_diff, m_noise_counter_bit))
|
||||
{
|
||||
m_pnc1 = ((m_pnc1 << 1) | ((BIT(m_pnc1, 6) ^ BIT(m_pnc1, 5)) ? 1 : 0)) & 0x7f;
|
||||
if (BIT(m_md0, MD0_NSF1_BIT))
|
||||
{
|
||||
m_pnc2 = (m_pnc2 << 1) | (BIT(m_pnc2, 0) ? 0 : 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pnc2 = (m_pnc2 << 1) | ((BIT(m_pnc2, 2) ^ BIT(m_pnc2, 1)) ? 0 : 1);
|
||||
}
|
||||
|
||||
if (BIT(m_md0, MD0_NS_BIT))
|
||||
{
|
||||
fatalerror("%s: NS int not supported\n", tag());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void upd177x_cpu_device::execute_run()
|
||||
{
|
||||
do
|
||||
{
|
||||
handle_timer();
|
||||
debugger_instruction_hook(m_pc);
|
||||
if (!m_skip)
|
||||
{
|
||||
u16 opcode = m_program.read_word(m_pc++);
|
||||
switch (opcode & 0xf000)
|
||||
{
|
||||
case 0x0000: op0xxx(opcode); break;
|
||||
case 0x1000: op1xxx(opcode); break;
|
||||
case 0x2000: op2xxx(opcode); break;
|
||||
case 0x3000: op3xxx(opcode); break;
|
||||
case 0x4000: m_ram[(opcode >> 8) & 0x1f] = opcode & 0xff; break; // MVI Rr,n
|
||||
case 0x5000: m_ram[(opcode >> 8) & 0x1f] = opcode & 0xff; break; // MVI Rr,n
|
||||
case 0x6000: m_pc = (m_pc & 0xf000) | (opcode & 0x0fff); break; // JMP n
|
||||
case 0x7000: op7xxx(opcode); break;
|
||||
case 0x8000: op89xxx(opcode); break;
|
||||
case 0x9000: op89xxx(opcode); break;
|
||||
case 0xa000: opabxxx(opcode); break;
|
||||
case 0xb000: opabxxx(opcode); break;
|
||||
case 0xc000: opcdxxx(opcode); break;
|
||||
case 0xd000: opcdxxx(opcode); break;
|
||||
case 0xe000: opefxxx(opcode); break;
|
||||
case 0xf000: opefxxx(opcode); break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pc++;
|
||||
m_skip = false;
|
||||
}
|
||||
m_cycle();
|
||||
} while (m_icount > 0);
|
||||
}
|
||||
|
||||
|
||||
void upd177x_cpu_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
|
||||
{
|
||||
auto &buffer = outputs[0];
|
||||
|
||||
const int smpl = m_dac_sign ? -m_dac : m_dac;
|
||||
for (int sampindex = 0; sampindex < buffer.samples(); sampindex++)
|
||||
{
|
||||
buffer.put_int(sampindex, smpl, 256);
|
||||
}
|
||||
}
|
124
src/devices/cpu/upd177x/upd177x.h
Normal file
124
src/devices/cpu/upd177x/upd177x.h
Normal file
@ -0,0 +1,124 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Wilbert Pol
|
||||
/*****************************************************************************
|
||||
|
||||
uPD177x
|
||||
|
||||
******************************************************************************
|
||||
|
||||
|
||||
___ ___
|
||||
PB4 1 |* u | 28 PB3
|
||||
PB5 2 | | 27 PB2
|
||||
PB6 3 | | 26 PB1
|
||||
PB7 4 | | 25 PA8
|
||||
PB8 5 | u | 24 PA7
|
||||
/AC 6 | P | 23 PA6
|
||||
/PW ON 7 | D | 22 PA5
|
||||
Vdd 8 | 1 | 21 PA4
|
||||
X1 9 | 7 | 20 PA3
|
||||
X0 10 | 7 | 19 PA2
|
||||
SOUND2 11 | 1 | 18 PA1
|
||||
Vda 12 | | 17 CH2
|
||||
SOUND1 13 | | 16 /EXINT
|
||||
GND 14 |_______| 15 CH1
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#ifndef MAME_CPU_UPD177X_UPD177X_H
|
||||
#define MAME_CPU_UPD177X_UPD177X_H
|
||||
|
||||
class upd177x_cpu_device : public cpu_device
|
||||
, public device_sound_interface
|
||||
{
|
||||
public:
|
||||
upd177x_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
|
||||
|
||||
// configuration helpers
|
||||
auto pb_out_cb() { return m_pb_out_cb.bind(); }
|
||||
|
||||
u8 pa_r() { return m_pa; }
|
||||
void pa_w(u8 data) { m_pa = data; }
|
||||
void pb_w(u8 data) { m_pb = data; }
|
||||
|
||||
protected:
|
||||
upd177x_cpu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock);
|
||||
|
||||
virtual void device_start() override ATTR_COLD;
|
||||
virtual void device_reset() override ATTR_COLD;
|
||||
|
||||
virtual u32 execute_min_cycles() const noexcept override { return 1; }
|
||||
virtual u32 execute_max_cycles() const noexcept override { return 2; }
|
||||
virtual uint32_t execute_default_irq_vector(int inputnum) const noexcept override { return 0xff; }
|
||||
virtual void execute_run() override;
|
||||
|
||||
virtual space_config_vector memory_space_config() const override;
|
||||
|
||||
virtual std::unique_ptr<util::disasm_interface> create_disassembler() override;
|
||||
|
||||
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
|
||||
|
||||
void program_map(address_map &map) ATTR_COLD;
|
||||
|
||||
private:
|
||||
void op0xxx(u16 opcode);
|
||||
void op1xxx(u16 opcode);
|
||||
void op2xxx(u16 opcode);
|
||||
void op3xxx(u16 opcode);
|
||||
void op7xxx(u16 opcode);
|
||||
void op89xxx(u16 opcode);
|
||||
void opabxxx(u16 opcode);
|
||||
void opcdxxx(u16 opcode);
|
||||
void opefxxx(u16 opcode);
|
||||
void handle_timer();
|
||||
void set_noise_counter_bit();
|
||||
void m_cycle();
|
||||
|
||||
address_space_config m_program_config;
|
||||
|
||||
memory_access<16, 1, -1, ENDIANNESS_LITTLE>::specific m_program;
|
||||
devcb_write8 m_pb_out_cb;
|
||||
sound_stream *m_channel;
|
||||
|
||||
int m_icount;
|
||||
|
||||
u16 m_pc; // 16 bits
|
||||
u8 m_sp; // 3 bits
|
||||
u8 m_a; // 8 bits
|
||||
u8 m_a_save; // 8 bits
|
||||
u8 m_h; // 6 bits
|
||||
u8 m_n; // 8 bits
|
||||
u8 m_timer;
|
||||
u16 m_counter;
|
||||
u8 m_x; // 7 bits
|
||||
u8 m_y; // 5 bits
|
||||
u8 m_pa;
|
||||
u8 m_pb;
|
||||
u8 m_ram[0x40];
|
||||
u8 m_md0;
|
||||
u8 m_md1;
|
||||
u8 m_pnc1; // 7 bits
|
||||
u8 m_pnc2; // 3 bits
|
||||
bool m_skip;
|
||||
bool m_skip_save;
|
||||
bool m_ts;
|
||||
bool m_ns;
|
||||
bool m_ss;
|
||||
u8 m_dac;
|
||||
bool m_dac_sign;
|
||||
bool m_tn_int;
|
||||
u8 m_noise_counter_bit;
|
||||
};
|
||||
|
||||
|
||||
class upd1771c_cpu_device : public upd177x_cpu_device
|
||||
{
|
||||
public:
|
||||
upd1771c_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
|
||||
};
|
||||
|
||||
|
||||
DECLARE_DEVICE_TYPE(UPD1771C, upd1771c_cpu_device)
|
||||
|
||||
#endif // MAME_CPU_UPD177X_UPD177X_H
|
@ -1,563 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:David Viens
|
||||
/**********************************************************************
|
||||
|
||||
NEC uPD1771-017 as used in the Epoch Super Cassette Vision (SCV)
|
||||
|
||||
Made using recording/analysis on a Yeno (PAL Super Cassette Vision)
|
||||
by plgDavid
|
||||
|
||||
Full markings on my 2 specimens are
|
||||
"NEC JAPAN 8431K9 D1771C 017" (31st week of 1984, mask rom #017)
|
||||
|
||||
I've since (October 2012) got a Grandstand Firefox F-7 Handheld game
|
||||
(AKA Epoch GalagaX6/Epoch Astro Thunder 7/Tandy Astro Thunder),
|
||||
(http://www.handheldmuseum.com/Grandstand/Firefox.htm)
|
||||
which includes a
|
||||
"NEC JAPAN 8319K9 D1771C 011" (19th week of 1983, mask rom #011)
|
||||
Thanks to user 'Blanka' from Dragonslairfans for the nice catch!
|
||||
(http://www.dragonslairfans.com/smfor/index.php?topic=3061.0)
|
||||
|
||||
Since the chip generates tones using ROM wavetables,
|
||||
it is perfectly possible to generate other sounds with different rom code and data.
|
||||
|
||||
Most upd17XXX devices are typically 4bit NEC MCUs, however based on information
|
||||
in "Electronic Speech Synthesis" by Geoff Bristow (ISBN 0-07-007912-9, pages 148-152)
|
||||
the upd1770/1771 is not one of these 4-bit ones.
|
||||
|
||||
The uPD1770/uPD1771 SSM is a 16-bit-wide rom/ram mcu with 8kb (4kw) of rom code,
|
||||
64 bytes of ram (16x16bit words addressable as 16 or 2x8 bits each, the
|
||||
remaining 32 bytes acting as an 8-level stack), 182 instructions, a complex
|
||||
noise and tone internal interrupt system, external interrupts,
|
||||
and two 8-bit ports with multiple modes allowing for chips to operate as master
|
||||
or slave devices.
|
||||
SSM stands for "Sound Synthesis Microcomputer".
|
||||
|
||||
People who I *THINK* worked on the uPD1771 and what part I think they worked on:
|
||||
Toshio Oura - Project Lead(?), VSRSSS/TSRSSS speech synthesis engine (on upd1776C), master/slave i/o controls, author of bristow article and primary author of the IEEE article
|
||||
Hatsuhide Igarashi - Clock oscillator and pad layout, coauthor on the IEEE article, other IEEE stuff
|
||||
Tomoaki Isozaki - ? (senior NEC engineer?), coauthor on the IEEE article
|
||||
Sachiyuki Toufuku - ?, coauthor on the IEEE article
|
||||
Tojiro Mukawa - IGFETs and the DAC
|
||||
M. Sakai ? - digital filtering for VSRSSS? (IEEE 4131979, 1169295)
|
||||
M. Endo ? - digital design system or speech synthesis? (IEEE 4069656, another? person: IEEE 150330, 225838)
|
||||
H. Aoyama ? - logic design system used to assemble/lay out the chip? (IEEE 1585393)
|
||||
I. Fujitaka ? (no IEEE)
|
||||
Eiji Sugimoto - cpu design? 1156033 1155824
|
||||
F. Tsukuda ? (no IEEE)
|
||||
N. Miyake ? switched capacitor stuff? (IEEE nnnnnn)
|
||||
|
||||
|
||||
The uPD1771 internal workings are described to some extent by the Bristow book
|
||||
and the IEEE article "A Single-Chip Sound Synthesis Microcomputer" which complements the book
|
||||
and are covered by at least four US patents:
|
||||
4184152 - on IGFET-based DAC stuff
|
||||
4488061 - on the IGFET-based drive circuit part of the DAC.
|
||||
4408094 - covers the 3 pin DAC with the volume control/vref pin. Not all that interesting,
|
||||
except it might describe to some extent how the (9->5bit?) PWM works in the text.
|
||||
4470113 - covers the multiplexed PB0/1/2/3 pins and their use as /CS /WR /RD and ALE
|
||||
note as I have marked the pins below I assume the final pins connected
|
||||
to /CS /WR /RD and /ALE are PB7,6,5,4 but this is just a guess of mine:
|
||||
The actual order may well match the patent.
|
||||
4577343 - covers the VSRSSS implementation as discussed in the Bristow book.
|
||||
This patent has an internal diagram of the workings of the chips and
|
||||
a limited description of how many registers etc it has.
|
||||
4805508 - on the operation of the tone divider register and correction for accurate period when
|
||||
the tone interrupt frequency is not perfectly divisible from the clock.
|
||||
These next two may not be specific to the 1771 or even related at all!
|
||||
4321562 - on a self-adjusting circuit for internal coupling to the clock crystal inputs.
|
||||
This may be a generic NEC invention and probably isn't limited to the upd1771.
|
||||
4656491 - on a new method of distributing resistors and transistors on anti-ESD pin buffers
|
||||
This may be a generic NEC invention and probably isn't limited to the upd1771.
|
||||
|
||||
|
||||
Based on the 4577343 patent mostly, plus the bristow and IEEE article:
|
||||
* these are the registers:
|
||||
8bits:
|
||||
AH, AL (forming the 16-bit A' accumulator),
|
||||
B, C (a pair of general purpose registers),
|
||||
4bits (may be technically part of ALU):
|
||||
H -> points to one of the 16 words of ram
|
||||
1bit:
|
||||
L -> selector of left or right half of the ram word
|
||||
?bits:
|
||||
D (having to do with the DAC)
|
||||
N (3 bits? having to do with the pseudorandom noise interrupt, namely setting the clock divider ratio for the PRNG clock vs cpu clock)
|
||||
MODE (5 or more bits? enabling/disabling/acking the noise interrupt, and the tone interrupts (there are four!))
|
||||
SP (the stack pointer, probably 5 bits, points to the stack ram; may encompass H and L as above!)
|
||||
FLO: unsure. quite possibly 'flag overflow' used for branching. there likely exists other flags as well...
|
||||
ODF: 'output data flag?', selects which half of a selected ram word is output to the dac not really sure of this?
|
||||
|
||||
|
||||
Mask roms known:
|
||||
uPD1776C: mentioned in the bristow book, implements VSRSSS speech concatenation
|
||||
(see US Patent 4577343 which is a patent on this VSRSSS implementation)
|
||||
uPD1771C-006: used in NEC APC for sound as the "MPU"
|
||||
-011: used on Firefox F-4/Astro Thunder handheld
|
||||
-015: unknown, known to exist from part scalper sites only.
|
||||
-017: used on Epoch Super Cassette Vision for sound; This audio driver HLEs that part only.
|
||||
|
||||
Used pinout in the SCV:
|
||||
|
||||
NC 1 28 NC
|
||||
NC 2 27 NC
|
||||
NC 3 26 ACK
|
||||
!WR 4 25 D7
|
||||
!CS 5 24 D6
|
||||
RESET 6 23 D5
|
||||
NC 7 22 D4
|
||||
VCC 8 21 D3
|
||||
6Mhz XIN 9 20 D2
|
||||
6Mhz XOUT 10 19 D1
|
||||
AUDOUT 11 18 D0
|
||||
NC(recheck!)12 17 GND
|
||||
AUDOUT(inv) 13 16 VCC
|
||||
GND 14 15 ? tied to pin 16 (VCC) through a resistor (pullup?)
|
||||
|
||||
Pinout based on guesses and information in "Electronic Speech Synthesis" by Geoff Bristow
|
||||
(ISBN 0-07-007912-9, pages 148-152), and the data on page 233 of the Nec APC technical manual at
|
||||
http://bitsavers.trailing-edge.com/pdf/nec/APC/819-000100-1003_APC_System_Reference_Guide_Apr83.pdf
|
||||
I/O pin purpose based on testing by kevtris.
|
||||
[x] is unsure:
|
||||
PB3 <> 1 28 <> PB2
|
||||
PB4(/ALE) <> 2 27 <> PB1
|
||||
PB5(/RD) <> 3 26 <> PB0
|
||||
PB6(/WR) <> 4 25 <> D7(PA7)
|
||||
PB7(/CS) <> 5 24 <> D6(PA6)
|
||||
/RESET -> 6 23 <> D5(PA5)
|
||||
[/TSTOUT?] <- 7 22 <> D4(PA4)
|
||||
VCC -- 8 21 <> D3(PA3)
|
||||
XI(CLK) -> 9 20 <> D2(PA2)
|
||||
XO <- 10 19 <> D1(PA1)
|
||||
D/A OUT + <- 11 18 <> D0(PA0)
|
||||
D/A POWER -- 12 17 <- CH2
|
||||
D/A OUT - <- 13 16 <> /EXTINT and [/TSTOUT2?] (test out is related to pin 15 state)
|
||||
GND -- 14 15 <- CH1 tied to pin 16 (VCC) through a resistor, on APC to VCC thru a 12k resistor and thru a 10uf cap to gnd
|
||||
|
||||
CH1 and CH2 are mode selects, purpose based on testing by kevtris:
|
||||
CH1 CH2
|
||||
H L - 'master' mode, pb4-pb7 are i/o? /EXTINT is an input
|
||||
L L - 'slave' mode where pb4-pb7 are /ale /rd /wr /cs? /EXTINT may be an output in this mode?
|
||||
X H - test mode: the 512 byte 16-bit wide ROM is output sequentially on pins pa7-pa0 and pb7-pb0 for the high and low bytes of each word respectively
|
||||
D/A POWER is the FET source for the D/A OUT+ and D/A OUT- push-pull dac drains; it should be tied to VCC or thereabouts
|
||||
|
||||
In the SCV (info from plgDavid):
|
||||
pin 5 is tied to the !SCPU pin on the Epoch TV chip pin 29 (0x3600 writes)
|
||||
pin 6 is tied to the PC3 pin of the upD7801 CPU
|
||||
pin 26 is tied to the INT1 pin of the upD7801 (CPU pin 12),
|
||||
|
||||
1,2,3,28,27 don't generate any digital signals
|
||||
6 seems to be lowered 2.5 ms before an audio write
|
||||
7 is always low.
|
||||
12 is always high
|
||||
|
||||
(NOTE: the photomicrograph in the bristow book makes it fairly clear due to
|
||||
pad thicknessess that the real VCC is pin 8 and the real GND is pin 14.
|
||||
The function of pin 7 is unknown.
|
||||
|
||||
Pins 11 and 13 go to a special circuit, which according to kevtris's analysis
|
||||
of my schematics, consist of a balanced output (not unlike XLR cables),
|
||||
which are then combined together then sent to the RF box.
|
||||
(The bristow book explains that there are two DAC pins and one DAC
|
||||
VREF/volume pin. The dac+ and dac- are pins 11 and 13, and based on the
|
||||
photomicrograph it looks like dac vref is probably pin 12)
|
||||
|
||||
HLE:
|
||||
All writes are made through address 0x3600 on the upD7801
|
||||
Instead of using register=value, this chip require sending multiple
|
||||
bytes for each command, one after the other.
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#include "emu.h"
|
||||
#include "upd1771.h"
|
||||
|
||||
//#define VERBOSE 1
|
||||
#include "logmacro.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
/*
|
||||
Each of the 8 waveforms have been extracted from the uPD1771c-017 internal
|
||||
ROM, from offset 0x1fd (start of first waveform) to offset 0x2fc (end of
|
||||
last waveform).
|
||||
(note: given test mode dumping offset non-clarity it may be 0x200-0x2ff)
|
||||
The waveforms are stored in an 8-bit sign-magnitude format, so if in ROM the
|
||||
upper bit is 0x80, invert the lower 7 bits to get the 2's complement result
|
||||
seen here.
|
||||
Note that only the last 4 waveforms appear to have been intended for use as
|
||||
waveforms; the first four look as if they're playing back a piece of code as
|
||||
wave data.
|
||||
*/
|
||||
const signed char WAVEFORMS[8][32]={
|
||||
{ 0, 0,-123,-123, -61, -23, 125, 107, 94, 83,-128,-128,-128, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0,-128,-128,-128, 0, 0, 0, 0, 0, 0},
|
||||
{ 37, 16, 32, -21, 32, 52, 4, 4, 33, 18, 60, 56, 0, 8, 5, 16, 65, 19, 69, 16, -2, 19, 37, 16, 97, 19, 0, 87, 127, -3, 1, 2},
|
||||
{ 0, 8, 1, 52, 4, 0, 0, 77, 81,-109, 47, 97, -83,-109, 38, 97, 0, 52, 4, 0, 1, 4, 1, 22, 2, -46, 33, 97, 0, 8, -85, -99},
|
||||
{ 47, 97, 40, 97, -3, 25, 64, 17, 0, 52, 12, 5, 12, 5, 12, 5, 12, 5, 12, 5, 8, 4,-114, 19, 0, 52,-122, 21, 2, 5, 0, 8},
|
||||
{ -52, -96,-118,-128,-111, -74, -37, -5, 31, 62, 89, 112, 127, 125, 115, 93, 57, 23, 0, -16, -8, 15, 37, 54, 65, 70, 62, 54, 43, 31, 19, 0},
|
||||
{ -81,-128, -61, 13, 65, 93, 127, 47, 41, 44, 52, 55, 56, 58, 58, 34, 0, 68, 76, 72, 61, 108, 55, 29, 32, 39, 43, 49, 50, 51, 51, 0},
|
||||
{ -21, -45, -67, -88,-105,-114,-122,-128,-123,-116,-103, -87, -70, -53, -28, -9, 22, 46, 67, 86, 102, 114, 123, 125, 127, 117, 104, 91, 72, 51, 28, 0},
|
||||
{ -78,-118,-128,-102, -54, -3, 40, 65, 84, 88, 84, 80, 82, 88, 94, 103, 110, 119, 122, 125, 122, 122, 121, 123, 125, 126, 127, 127, 125, 118, 82, 0}
|
||||
};
|
||||
|
||||
|
||||
#define NOISE_SIZE 255
|
||||
|
||||
|
||||
const unsigned char noise_tbl[]=
|
||||
{
|
||||
0x1c,0x86,0x8a,0x8f,0x98,0xa1,0xad,0xbe,0xd9,0x8a,0x66,0x4d,0x40,0x33,0x2b,0x23,
|
||||
0x1e,0x8a,0x90,0x97,0xa4,0xae,0xb8,0xd6,0xec,0xe9,0x69,0x4a,0x3e,0x34,0x2d,0x27,
|
||||
0x24,0x24,0x89,0x8e,0x93,0x9c,0xa5,0xb0,0xc1,0xdd,0x40,0x36,0x30,0x29,0x27,0x24,
|
||||
0x8b,0x90,0x96,0x9e,0xa7,0xb3,0xc4,0xe1,0x25,0x21,0x8a,0x8f,0x93,0x9d,0xa5,0xb2,
|
||||
0xc2,0xdd,0xdd,0x98,0xa2,0xaf,0xbf,0xd8,0xfd,0x65,0x4a,0x3c,0x31,0x2b,0x24,0x22,
|
||||
0x1e,0x87,0x8c,0x91,0x9a,0xa3,0xaf,0xc0,0xdb,0xbe,0xd9,0x8c,0x66,0x4d,0x40,0x34,
|
||||
0x2c,0x24,0x1f,0x88,0x90,0x9a,0xa4,0xb2,0xc2,0xda,0xff,0x67,0x4d,0x3d,0x34,0x2d,
|
||||
0x26,0x24,0x20,0x89,0x8e,0x93,0x9c,0xa5,0xb1,0xc2,0xde,0xc1,0xda,0xff,0x67,0x4d,
|
||||
0x3d,0x33,0x2d,0x26,0x24,0x20,0x89,0x8e,0x93,0x9c,0xa5,0xb1,0xc2,0xdd,0xa3,0xb0,
|
||||
0xc0,0xd9,0xfe,0x66,0x4b,0x3c,0x32,0x2b,0x24,0x23,0x1e,0x88,0x8d,0x92,0x9b,0xa4,
|
||||
0xb0,0xc1,0xdc,0xad,0xbe,0xda,0x22,0x20,0x1c,0x85,0x8a,0x8f,0x98,0xa1,0xad,0xbe,
|
||||
0xda,0x20,0x1b,0x85,0x8d,0x97,0xa1,0xaf,0xbf,0xd8,0xfd,0x64,0x49,0x3a,0x30,0x2a,
|
||||
0x23,0x21,0x1d,0x86,0x8b,0x91,0x9a,0xa2,0xae,0xc0,0xdb,0x33,0x2b,0x24,0x1f,0x88,
|
||||
0x90,0x9a,0xa4,0xb2,0xc2,0xda,0xff,0x67,0x4c,0x3e,0x33,0x2d,0x25,0x24,0x1f,0x89,
|
||||
0x8e,0x93,0x9c,0xa5,0xb1,0xc2,0xde,0x85,0x8e,0x98,0xa2,0xb0,0xc0,0xd9,0xfe,0x64,
|
||||
0x4b,0x3b,0x31,0x2a,0x23,0x22,0x1e,0x88,0x8c,0x91,0x9b,0xa3,0xaf,0xc1,0xdc,0xdc
|
||||
};
|
||||
|
||||
|
||||
#define STATE_SILENCE 0
|
||||
#define STATE_NOISE 1
|
||||
#define STATE_TONE 2
|
||||
#define STATE_ADPCM 3
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
DEFINE_DEVICE_TYPE(UPD1771C, upd1771c_device, "upd1771c", "NEC uPD1771C 017")
|
||||
|
||||
|
||||
upd1771c_device::upd1771c_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: device_t(mconfig, UPD1771C, tag, owner, clock)
|
||||
, device_sound_interface(mconfig, *this)
|
||||
, m_ack_handler(*this)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// device_start - device-specific startup
|
||||
//-------------------------------------------------
|
||||
|
||||
void upd1771c_device::device_start()
|
||||
{
|
||||
m_timer = timer_alloc(FUNC(upd1771c_device::ack_callback), this);
|
||||
|
||||
m_channel = stream_alloc(0, 1, clock() / 4);
|
||||
|
||||
save_item(NAME(m_packet));
|
||||
save_item(NAME(m_index));
|
||||
save_item(NAME(m_expected_bytes));
|
||||
save_item(NAME(m_state));
|
||||
save_item(NAME(m_pc3));
|
||||
save_item(NAME(m_t_timbre));
|
||||
save_item(NAME(m_t_offset));
|
||||
save_item(NAME(m_t_period));
|
||||
save_item(NAME(m_t_volume));
|
||||
save_item(NAME(m_t_tpos));
|
||||
save_item(NAME(m_t_ppos));
|
||||
save_item(NAME(m_nw_timbre));
|
||||
save_item(NAME(m_nw_volume));
|
||||
save_item(NAME(m_nw_period));
|
||||
save_item(NAME(m_nw_tpos));
|
||||
save_item(NAME(m_nw_ppos));
|
||||
save_item(NAME(m_n_value));
|
||||
save_item(NAME(m_n_volume));
|
||||
save_item(NAME(m_n_period));
|
||||
save_item(NAME(m_n_ppos));
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// device_reset - device-specific reset
|
||||
//-------------------------------------------------
|
||||
|
||||
void upd1771c_device::device_reset()
|
||||
{
|
||||
m_index = 0;
|
||||
m_expected_bytes = 0;
|
||||
m_pc3 = 0;
|
||||
m_t_tpos = 0;
|
||||
m_t_ppos = 0;
|
||||
m_state = STATE_SILENCE;
|
||||
m_nw_tpos = 0;
|
||||
memset(m_n_value, 0x00, sizeof(m_n_value));
|
||||
memset(m_n_ppos, 0x00, sizeof(m_n_ppos));
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint8_t upd1771c_device::read()
|
||||
{
|
||||
return 0x80; // TODO
|
||||
}
|
||||
|
||||
/*
|
||||
*************TONE*****************
|
||||
Tone consists of a wavetable playback mechanism.
|
||||
Each wavetable is a looping period of 32 samples but can be played with an offset from any point in the table
|
||||
effectively shrinking the sample loop, thus allowing different pitch "macros ranges" to be played.
|
||||
This method is rather crude because the spectrum of the sound get heavily altered...
|
||||
unless that was the intent.
|
||||
|
||||
Tone Write (4 bytes):
|
||||
|
||||
Byte0: 0x02
|
||||
|
||||
Byte1: 0bTTTOOOOO
|
||||
MSB 3 bits of Timbre (8 wavetables)
|
||||
LSB 5 bits offset in the table.
|
||||
|
||||
Byte2: 0bPPPPPPPP
|
||||
8bits of clock divider/period
|
||||
Anything under <= 0x20 give the same value
|
||||
|
||||
Byte3: 0b???VVVVV
|
||||
MSB 3 bits unknown
|
||||
LSB 5 bits of "Volume"
|
||||
|
||||
Note: volume is not a volume in a normal sense but some kind
|
||||
of bit cropping/rounding.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
*************NOISE*****************
|
||||
Noise consists on 4 different components
|
||||
A weird Wavetable LFSR (for lack of a better term),
|
||||
and three independent (but equal) low frequency
|
||||
50/50 pulse wavs.
|
||||
|
||||
The 4 components are mixed in a mysterious way,
|
||||
a weird ORing with volume having a huge effect.
|
||||
|
||||
Byte0: 0x01
|
||||
|
||||
Byte1: 0bTTTOOOOO
|
||||
MSB 3 bits of LFSR Timbre (8 wavetables)
|
||||
LSB 5 bits ?????????
|
||||
|
||||
Byte2: 0bPPPPPPPP
|
||||
8bits of clock divider/period
|
||||
|
||||
Byte3: 0b???VVVVV
|
||||
MSB 3 bits unknown
|
||||
LSB 5 bits of "Volume"
|
||||
|
||||
|
||||
Byte4: 0bPPPPPPPP Low Freq0 period(if not 0 this periodically resets the Wavetable LFSR)
|
||||
Byte5: 0bPPPPPPPP Low Freq1 period(if not 0 this periodically resets the Wavetable LFSR)
|
||||
Byte6: 0bPPPPPPPP Low Freq2 period(if not 0 this periodically resets the Wavetable LFSR)
|
||||
|
||||
Byte7: 0b???VVVVV Low Freq0 volume
|
||||
Byte8: 0b???VVVVV Low Freq1 volume
|
||||
Byte9: 0b???VVVVV Low Freq2 volume
|
||||
*/
|
||||
|
||||
void upd1771c_device::write(uint8_t data)
|
||||
{
|
||||
LOG("upd1771_w: received byte 0x%02x\n", data);
|
||||
|
||||
m_ack_handler(0);
|
||||
|
||||
if (m_index < MAX_PACKET_SIZE)
|
||||
m_packet[m_index++] = data;
|
||||
else
|
||||
{
|
||||
logerror("upd1771_w: received byte 0x%02x overload!\n", data);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (m_packet[0])
|
||||
{
|
||||
case 0:
|
||||
m_state = STATE_SILENCE;
|
||||
m_index = 0;
|
||||
//logerror( "upd1771_w: ----------------silence state reset\n");
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if (m_index == 10)
|
||||
{
|
||||
m_state = STATE_NOISE;
|
||||
m_index = 0;
|
||||
|
||||
m_nw_timbre = (m_packet[1] & 0xe0) >> 5;
|
||||
m_nw_period = ((uint32_t)m_packet[2] + 1) << 7;
|
||||
m_nw_volume = m_packet[3] & 0x1f;
|
||||
|
||||
//very long clocked periods.. used for engine drones
|
||||
m_n_period[0] = (((uint32_t)m_packet[4]) + 1) << 7;
|
||||
m_n_period[1] = (((uint32_t)m_packet[5]) + 1) << 7;
|
||||
m_n_period[2] = (((uint32_t)m_packet[6]) + 1) << 7;
|
||||
|
||||
m_n_volume[0] = m_packet[7] & 0x1f;
|
||||
m_n_volume[1] = m_packet[8] & 0x1f;
|
||||
m_n_volume[2] = m_packet[9] & 0x1f;
|
||||
|
||||
//logerror( "upd1771_w: ----------------noise state reset\n");
|
||||
}
|
||||
else
|
||||
m_timer->adjust(attotime::from_ticks(512, clock()));
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if (m_index == 4)
|
||||
{
|
||||
//logerror( "upd1771_w: ----------------tone state reset\n");
|
||||
m_t_timbre = (m_packet[1] & 0xe0) >> 5;
|
||||
m_t_offset = (m_packet[1] & 0x1f);
|
||||
m_t_period = m_packet[2];
|
||||
//smaller periods don't all equal to 0x20
|
||||
if (m_t_period < 0x20)
|
||||
m_t_period = 0x20;
|
||||
|
||||
m_t_volume = m_packet[3] & 0x1f;
|
||||
m_state = STATE_TONE;
|
||||
m_index = 0;
|
||||
}
|
||||
else
|
||||
m_timer->adjust(attotime::from_ticks(512, clock()));
|
||||
break;
|
||||
|
||||
case 0x1f:
|
||||
//6Khz(ish) DIGI playback
|
||||
|
||||
//end capture
|
||||
{
|
||||
bool have_all_data = false;
|
||||
if (m_index >= 2 && m_packet[m_index - 2] == 0xfe && m_packet[m_index - 1] == 0x00)
|
||||
{
|
||||
//TODO play capture!
|
||||
if (m_index >= 6)
|
||||
{
|
||||
// offsets 2 and 3 in the transferred pcm data seem to contain the number of samples
|
||||
uint16_t count = (m_packet[4] << 8) | m_packet[3];
|
||||
count--;
|
||||
m_packet[3] = count & 0xff;
|
||||
m_packet[4] = (count >> 8);
|
||||
if (count == 0)
|
||||
{
|
||||
m_index = 0;
|
||||
m_packet[0] = 0;
|
||||
m_state = STATE_ADPCM;
|
||||
have_all_data = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!have_all_data)
|
||||
m_timer->adjust(attotime::from_ticks(512, clock()));
|
||||
}
|
||||
break;
|
||||
|
||||
//garbage: wipe stack
|
||||
default:
|
||||
m_state = STATE_SILENCE;
|
||||
m_index = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void upd1771c_device::pcm_write(int state)
|
||||
{
|
||||
//RESET upon HIGH
|
||||
if (state != m_pc3)
|
||||
{
|
||||
logerror("upd1771_pc3 change!: state = %d\n", state);
|
||||
m_index = 0;
|
||||
m_packet[0] = 0;
|
||||
m_state = STATE_SILENCE;
|
||||
}
|
||||
|
||||
m_pc3 = state;
|
||||
}
|
||||
|
||||
|
||||
TIMER_CALLBACK_MEMBER( upd1771c_device::ack_callback )
|
||||
{
|
||||
m_ack_handler(1);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// sound_stream_update - handle a stream update
|
||||
//-------------------------------------------------
|
||||
|
||||
void upd1771c_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
|
||||
{
|
||||
auto &buffer = outputs[0];
|
||||
|
||||
switch (m_state)
|
||||
{
|
||||
case STATE_TONE:
|
||||
//logerror( "upd1771_STATE_TONE samps:%d %d %d %d %d %d\n",(int)samples,
|
||||
// (int)m_t_timbre,(int)m_t_offset,(int)m_t_volume,(int)m_t_period,(int)m_t_tpos);
|
||||
|
||||
for (int sampindex = 0; sampindex < buffer.samples(); sampindex++)
|
||||
{
|
||||
buffer.put_int(sampindex, (WAVEFORMS[m_t_timbre][m_t_tpos]) * m_t_volume, 32768 / 2);
|
||||
|
||||
m_t_ppos++;
|
||||
if (m_t_ppos >= m_t_period)
|
||||
{
|
||||
m_t_tpos++;
|
||||
if (m_t_tpos == 32)
|
||||
m_t_tpos = m_t_offset;
|
||||
|
||||
m_t_ppos = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_NOISE:
|
||||
for (int sampindex = 0; sampindex < buffer.samples(); sampindex++)
|
||||
{
|
||||
//"wavetable-LFSR" component
|
||||
int wlfsr_val = ((int)noise_tbl[m_nw_tpos]) - 127;//data too wide
|
||||
|
||||
m_nw_ppos++;
|
||||
if (m_nw_ppos >= m_nw_period)
|
||||
{
|
||||
m_nw_tpos++;
|
||||
if (m_nw_tpos == NOISE_SIZE)
|
||||
m_nw_tpos = 0;
|
||||
m_nw_ppos = 0;
|
||||
}
|
||||
|
||||
//mix in each of the noise's 3 pulse components
|
||||
char res[3];
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
res[i] = m_n_value[i] * 127;
|
||||
m_n_ppos[i]++;
|
||||
if (m_n_ppos[i] >= m_n_period[i])
|
||||
{
|
||||
m_n_ppos[i] = 0;
|
||||
m_n_value[i] = !m_n_value[i];
|
||||
}
|
||||
}
|
||||
//not quite, but close.
|
||||
buffer.put_int(sampindex,
|
||||
(wlfsr_val * m_nw_volume) |
|
||||
(res[0] * m_n_volume[0]) |
|
||||
(res[1] * m_n_volume[1]) |
|
||||
(res[2] * m_n_volume[2]),
|
||||
32768) ;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
//fill buffer with silence
|
||||
buffer.fill(0);
|
||||
break;
|
||||
}
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Wilbert Pol
|
||||
/**********************************************************************
|
||||
|
||||
NEC uPD1771
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef MAME_SOUND_UPD1771_H
|
||||
#define MAME_SOUND_UPD1771_H
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
MACROS / CONSTANTS
|
||||
***************************************************************************/
|
||||
|
||||
class upd1771c_device : public device_t,
|
||||
public device_sound_interface
|
||||
{
|
||||
public:
|
||||
upd1771c_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
|
||||
|
||||
auto ack_handler() { return m_ack_handler.bind(); }
|
||||
|
||||
uint8_t read();
|
||||
void write(uint8_t data);
|
||||
void pcm_write(int state);
|
||||
|
||||
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<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
|
||||
|
||||
private:
|
||||
static constexpr unsigned MAX_PACKET_SIZE = 0x8000;
|
||||
|
||||
TIMER_CALLBACK_MEMBER(ack_callback);
|
||||
|
||||
// internal state
|
||||
sound_stream *m_channel;
|
||||
devcb_write_line m_ack_handler;
|
||||
emu_timer *m_timer;
|
||||
|
||||
uint8_t m_packet[MAX_PACKET_SIZE];
|
||||
uint32_t m_index;
|
||||
uint8_t m_expected_bytes;
|
||||
|
||||
uint8_t m_state;//0:silence, 1 noise, 2 tone
|
||||
uint8_t m_pc3;
|
||||
|
||||
//tone
|
||||
uint8_t m_t_timbre; //[0; 7]
|
||||
uint8_t m_t_offset; //[0; 32]
|
||||
uint16_t m_t_period; //[0;255]
|
||||
uint8_t m_t_volume; //[0; 31]
|
||||
uint8_t m_t_tpos;//timbre pos
|
||||
uint16_t m_t_ppos;//period pos
|
||||
|
||||
//noise wavetable LFSR
|
||||
uint8_t m_nw_timbre; //[0; 7]
|
||||
uint8_t m_nw_volume; //[0; 31]
|
||||
uint32_t m_nw_period;
|
||||
uint32_t m_nw_tpos; //timbre pos
|
||||
uint32_t m_nw_ppos; //period pos
|
||||
|
||||
//noise pulse components
|
||||
uint8_t m_n_value[3]; //[0;1]
|
||||
uint16_t m_n_volume[3]; //[0; 31]
|
||||
uint32_t m_n_period[3];
|
||||
uint32_t m_n_ppos[3]; //period pos
|
||||
};
|
||||
|
||||
DECLARE_DEVICE_TYPE(UPD1771C, upd1771c_device)
|
||||
|
||||
#endif // MAME_SOUND_UPD1771_H
|
@ -5,15 +5,14 @@
|
||||
Driver for Epoch Super Cassette Vision
|
||||
|
||||
TODO:
|
||||
- adpcm playback by upd1771, needed for star speeder.
|
||||
- verify video clock in European units.
|
||||
- verify raw video parameters.
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#include "emu.h"
|
||||
#include "cpu/upd177x/upd177x.h"
|
||||
#include "cpu/upd7810/upd7810.h"
|
||||
#include "sound/upd1771.h"
|
||||
#include "bus/scv/slot.h"
|
||||
#include "bus/scv/rom.h"
|
||||
#include "emupal.h"
|
||||
@ -47,6 +46,7 @@ protected:
|
||||
virtual void machine_reset() override ATTR_COLD;
|
||||
|
||||
TIMER_CALLBACK_MEMBER(vblank_update);
|
||||
TIMER_CALLBACK_MEMBER(pa_w_update);
|
||||
|
||||
private:
|
||||
void scv_palette(palette_device &palette) const;
|
||||
@ -71,11 +71,12 @@ private:
|
||||
required_shared_ptr<u8> m_videoram;
|
||||
required_device<upd7801_device> m_maincpu;
|
||||
required_device<screen_device> m_screen;
|
||||
required_device<upd1771c_device> m_upd1771c;
|
||||
required_device<upd1771c_cpu_device> m_upd1771c;
|
||||
required_device<scv_cart_slot_device> m_cart;
|
||||
required_ioport_array<8> m_pa;
|
||||
required_ioport m_pc0;
|
||||
required_memory_region m_charrom;
|
||||
emu_timer *m_pa_w_timer;
|
||||
};
|
||||
|
||||
|
||||
@ -88,7 +89,12 @@ void scv_state::scv_mem(address_map &map)
|
||||
map(0x0000, 0x0fff).rom(); // BIOS
|
||||
|
||||
map(0x2000, 0x3403).ram().share(m_videoram); // VRAM + 4 registers
|
||||
map(0x3600, 0x3600).w(m_upd1771c, FUNC(upd1771c_device::write));
|
||||
map(0x3600, 0x3600).lw8(NAME([this] (u8 data) {
|
||||
// With the current cores there are sync issues between writing and clearing PA in scv kungfurd when playing back adpcm.
|
||||
// During a reset an OUT PA gets executed just around the same time when the main cpu is writing to PA.
|
||||
// Since the OUT PA completes while the external write to PA is still in progress the external write to PA wins.
|
||||
m_pa_w_timer->adjust(m_upd1771c->cycles_to_attotime(8), data);
|
||||
}));
|
||||
|
||||
// 8000 - ff7f - Cartridge
|
||||
// ff80 - ffff - CPU internal RAM
|
||||
@ -480,9 +486,16 @@ u32 scv_state::screen_update_scv(screen_device &screen, bitmap_ind16 &bitmap, co
|
||||
}
|
||||
|
||||
|
||||
TIMER_CALLBACK_MEMBER(scv_state::pa_w_update)
|
||||
{
|
||||
m_upd1771c->pa_w(param);
|
||||
}
|
||||
|
||||
|
||||
void scv_state::machine_start()
|
||||
{
|
||||
m_vb_timer = timer_alloc(FUNC(scv_state::vblank_update), this);
|
||||
m_pa_w_timer = timer_alloc(FUNC(scv_state::pa_w_update), this);
|
||||
|
||||
save_item(NAME(m_porta));
|
||||
save_item(NAME(m_portc));
|
||||
@ -540,13 +553,14 @@ void scv_state::scv(machine_config &config)
|
||||
return data;
|
||||
});
|
||||
m_maincpu->pc_in_cb().set([this] () {
|
||||
return (m_pc0->read() & 0x01);
|
||||
return BIT(m_pc0->read(), 0);
|
||||
});
|
||||
m_maincpu->pc_out_cb().set([this] (u8 data) {
|
||||
m_portc = data;
|
||||
m_cart->write_bank(m_portc); // Only bits 5 & 6 are exposed to the cartridge?
|
||||
m_upd1771c->pcm_write(BIT(m_portc, 3));
|
||||
m_upd1771c->set_input_line(INPUT_LINE_RESET, BIT(m_portc, 3) ? CLEAR_LINE : ASSERT_LINE);
|
||||
});
|
||||
config.set_perfect_quantum(m_maincpu);
|
||||
|
||||
// Video chip is EPOCH TV-1
|
||||
SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
|
||||
@ -560,8 +574,8 @@ void scv_state::scv(machine_config &config)
|
||||
// Sound is generated by UPD1771C clocked at XTAL(6'000'000)
|
||||
SPEAKER(config, "mono").front_center();
|
||||
UPD1771C(config, m_upd1771c, 6_MHz_XTAL);
|
||||
m_upd1771c->ack_handler().set([this] (int state) { m_maincpu->set_input_line(UPD7810_INTF1, (state) ? ASSERT_LINE : CLEAR_LINE); });
|
||||
m_upd1771c->add_route(ALL_OUTPUTS, "mono", 1.00);
|
||||
m_upd1771c->pb_out_cb().set([this] (u8 data) { m_maincpu->set_input_line(UPD7810_INTF1, BIT(data, 0) ? ASSERT_LINE : CLEAR_LINE); });
|
||||
m_upd1771c->add_route(ALL_OUTPUTS, "mono", 0.70);
|
||||
|
||||
SCV_CART_SLOT(config, m_cart, scv_cart, nullptr);
|
||||
m_cart->set_address_space(m_maincpu, AS_PROGRAM);
|
||||
@ -587,6 +601,9 @@ ROM_START(scv)
|
||||
|
||||
ROM_REGION(0x400, "charrom", 0)
|
||||
ROM_LOAD("epochtv.chr.s02", 0, 0x400, BAD_DUMP CRC(db521533) SHA1(40b4e44838c35191f115437a14f200f052e71509))
|
||||
|
||||
ROM_REGION(0x400, "upd1771c", 0)
|
||||
ROM_LOAD16_WORD("upd1771c-017.s03", 0, 0x400, CRC(975bd68d) SHA1(e777217c622331677ac1d6520a741d48ad9133c0))
|
||||
ROM_END
|
||||
|
||||
|
||||
@ -596,6 +613,9 @@ ROM_START(scv_pal)
|
||||
|
||||
ROM_REGION(0x400, "charrom", 0)
|
||||
ROM_LOAD("epochtv.chr.s02", 0, 0x400, BAD_DUMP CRC(db521533) SHA1(40b4e44838c35191f115437a14f200f052e71509))
|
||||
|
||||
ROM_REGION(0x400, "upd1771c", 0)
|
||||
ROM_LOAD16_WORD("upd1771c-017.s03", 0, 0x400, CRC(975bd68d) SHA1(e777217c622331677ac1d6520a741d48ad9133c0))
|
||||
ROM_END
|
||||
|
||||
} // anonymous namespace
|
||||
|
@ -54,6 +54,7 @@
|
||||
|
||||
#include "emu.h"
|
||||
#include "cpu/i86/i86.h"
|
||||
#include "cpu/upd177x/upd177x.h"
|
||||
#include "imagedev/floppy.h"
|
||||
#include "machine/am9517a.h"
|
||||
#include "machine/nvram.h"
|
||||
@ -61,7 +62,6 @@
|
||||
#include "machine/pit8253.h"
|
||||
#include "machine/upd1990a.h"
|
||||
#include "machine/upd765.h"
|
||||
#include "sound/upd1771.h"
|
||||
#include "video/upd7220.h"
|
||||
#include "emupal.h"
|
||||
#include "screen.h"
|
||||
@ -123,7 +123,7 @@ private:
|
||||
uint8_t *m_char_rom = nullptr;
|
||||
|
||||
required_device<speaker_device> m_speaker;
|
||||
required_device<upd1771c_device> m_sound;
|
||||
required_device<upd1771c_cpu_device> m_sound;
|
||||
|
||||
required_shared_ptr<uint16_t> m_video_ram_1;
|
||||
required_shared_ptr<uint16_t> m_video_ram_2;
|
||||
@ -500,7 +500,7 @@ void apc_state::apc_io(address_map &map)
|
||||
// 0x5a APU data (Arithmetic Processing Unit!)
|
||||
// 0x5b, Power Off
|
||||
// 0x5e APU status/command
|
||||
map(0x60, 0x60).rw(m_sound, FUNC(upd1771c_device::read), FUNC(upd1771c_device::write));
|
||||
map(0x60, 0x60).rw(m_sound, FUNC(upd1771c_cpu_device::pa_r), FUNC(upd1771c_cpu_device::pa_w));
|
||||
// map(0x68, 0x6f) i8255 , ODA printer port (A: status (R) B: data (W) C: command (W))
|
||||
// map(0x70, 0x76).rw("upd7220_btm", FUNC(upd7220_device::read), FUNC(upd7220_device::write)).umask16(0x00ff);
|
||||
// 0x71, 0x77 IDA Controller
|
||||
@ -992,6 +992,9 @@ ROM_START( apc )
|
||||
|
||||
ROM_REGION( 0x2000, "gfx", ROMREGION_ERASE00 )
|
||||
ROM_LOAD("pfcu1r.bin", 0x000000, 0x002000, CRC(683efa94) SHA1(43157984a1746b2e448f3236f571011af9a3aa73) )
|
||||
|
||||
ROM_REGION( 0x400, "upd1771c", ROMREGION_ERASE00 )
|
||||
ROM_LOAD( "upd1771c_006", 0, 0x400, NO_DUMP )
|
||||
ROM_END
|
||||
|
||||
void apc_state::init_apc()
|
||||
|
Loading…
Reference in New Issue
Block a user