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:
wilbertpol 2024-12-28 02:58:48 +00:00 committed by GitHub
parent 07cc2cf76a
commit 926b6182b1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 960 additions and 666 deletions

View File

@ -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">

View File

@ -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")

View File

@ -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

View 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);
}
}

View 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

View File

@ -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;
}
}

View File

@ -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

View File

@ -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

View File

@ -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()