Polysix: Add basic initial wave generation, "organ" eg, plus default nvram and floppy support.

This commit is contained in:
Olivier Galibert 2024-11-03 18:47:24 +01:00
parent b6aeaff34f
commit 68c049b67d
2 changed files with 524 additions and 134 deletions

26
hash/polysix.xml Normal file
View File

@ -0,0 +1,26 @@
<?xml version="1.0"?>
<!DOCTYPE softwarelist SYSTEM "softwarelist.dtd">
<!--
license:CC0-1.0
-->
<softwarelist name="polysix" description="Korg Polysix tapes">
<software name="presets" supported="yes">
<description>Factory presets</description>
<year>1981</year>
<publisher>Korg</publisher>
<part name="a" interface="polysix">
<feature name="part_id" value="Bank A"/>
<dataarea name="tape" size="42706">
<rom name="Polysix factory sounds a 8bit 11kHz.wav" size="42706" crc="4ff23b82" sha1="d3b5b17f7aab0fe24ce05fcd4e85dfeb3fadca00"/>
</dataarea>
</part>
<part name="b" interface="polysix">
<feature name="part_id" value="Bank B"/>
<dataarea name="tape" size="42896">
<rom name="Polysix factory sounds b 8bit 11kHz.wav" size="42896" crc="d19be09b" sha1="587816939c558a91cb058a6e54ffb36c82a39f42"/>
</dataarea>
</part>
</software>
</softwarelist>

View File

@ -8,36 +8,58 @@
#include "emu.h"
#include "cpu/mcs48/mcs48.h"
#include "imagedev/cassette.h"
#include "machine/nvram.h"
#include "softlist_dev.h"
#include "speaker.h"
namespace {
class polysix_state : public driver_device
class polysix_sound_block : public device_t, public device_sound_interface
{
public:
polysix_state(const machine_config &mconfig, device_type type, const char *tag)
: driver_device(mconfig, type, tag)
, m_progmcu(*this, "progmcu")
, m_keymcu(*this, "keymcu")
, m_keyboard(*this, "K%u", 0U)
, m_progboard(*this, "P%u", 0U)
, m_knobs(*this, "A%x", 0U)
{
}
polysix_sound_block(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
virtual void machine_start() override ATTR_COLD;
void polysix(machine_config &config);
void set_pitch(int channel, u8 pitch);
void set_gates(u8 gates);
void set_effect_speed(u8 value);
void set_cutoff(u8 value);
void set_eg_intensity(u8 value);
void set_resonance(u8 value);
void set_attack(u8 value);
void set_decay(u8 value);
void set_suspend(u8 value);
void set_release(u8 value);
void set_keyboard_tracking(u8 value);
void set_pw_pwm(u8 value);
void set_pwm_speed(u8 value);
void set_mg_speed(u8 value);
void set_mg_delay(u8 value);
void set_mg_level(u8 value);
void set_control_low(u8 value);
void set_control_high(u8 value);
u8 get_control_low();
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
virtual void device_start() override;
virtual void device_reset() override;
private:
required_device<mcs48_cpu_device> m_progmcu;
required_device<mcs48_cpu_device> m_keymcu;
std::unique_ptr<u8[]> m_nvram_ptr;
required_ioport_array<10> m_keyboard;
required_ioport_array<8> m_progboard;
required_ioport_array<16> m_knobs;
static const std::array<float, 0x100> phase_step;
static const std::array<float, 0x100> pwm_phase_step;
static const std::array<float, 0x100> pw_threshold;
std::array<u8, 8> m_pitch;
sound_stream *m_stream;
std::array<float, 6> m_phase;
std::array<float, 6> m_organ_eg;
float m_pwm_phase;
std::array<u8, 6> m_pitch;
u8 m_gates;
u8 m_current_gates;
u8 m_effect_speed;
u8 m_cutoff;
@ -54,8 +76,386 @@ private:
u8 m_mg_delay;
u8 m_mg_level;
u8 m_control_low;
u8 m_control_high;
};
DEFINE_DEVICE_TYPE(POLYSIX_SOUND_BLOCK, polysix_sound_block, "polysix_sound_block", "Korg Polysix sound block")
// Phase step at 48KHz for a given pitch for the main oscillator.
// Real range is 0 (C0) to 84 (C7), since it's a 61-keys, 5 octaves
// keyboard with +/- 1 octave transpose capability.
const std::array<float, 0x100> polysix_sound_block::phase_step = []() {
std::array<float, 0x100> steps;
// Tune A4 = 440Hz
for(int i=0; i != 0x100; i++)
steps[i] = 440.0 * pow(2, (i - 12*4 - 9)/12.0) / 48000;
return steps;
}();
// Phase step for the pwm. Actual curve is unclear but looks linear.
const std::array<float, 0x100> polysix_sound_block::pwm_phase_step = []() {
std::array<float, 0x100> steps;
// 0 = 0Hz, max = 20Hz, possibly linear?
for(int i=0; i != 0x100; i++)
steps[i] = 20 * (i/255.0) / 48000;
return steps;
}();
// Threshold for the pulse width, linear between 0.5 and 1.1, where 1
// is the actual max value.
const std::array<float, 0x100> polysix_sound_block::pw_threshold = []() {
std::array<float, 0x100> thr;
for(int i=0; i != 0x100; i++)
thr[i] = 0.5 + 0.6 * (i/255.0);
return thr;
}();
polysix_sound_block::polysix_sound_block(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, POLYSIX_SOUND_BLOCK, tag, owner, clock)
, device_sound_interface(mconfig, *this)
{
}
void polysix_sound_block::device_start()
{
m_stream = stream_alloc(0, 1, 48000);
save_item(NAME(m_phase));
save_item(NAME(m_organ_eg));
save_item(NAME(m_pwm_phase));
save_item(NAME(m_pitch));
save_item(NAME(m_gates));
save_item(NAME(m_current_gates));
save_item(NAME(m_effect_speed));
save_item(NAME(m_cutoff));
save_item(NAME(m_eg_intensity));
save_item(NAME(m_resonance));
save_item(NAME(m_attack));
save_item(NAME(m_decay));
save_item(NAME(m_suspend));
save_item(NAME(m_release));
save_item(NAME(m_keyboard_tracking));
save_item(NAME(m_pw_pwm));
save_item(NAME(m_pwm_speed));
save_item(NAME(m_mg_speed));
save_item(NAME(m_mg_delay));
save_item(NAME(m_mg_level));
save_item(NAME(m_control_low));
save_item(NAME(m_control_high));
std::fill(m_phase.begin(), m_phase.end(), 0);
std::fill(m_organ_eg.begin(), m_organ_eg.end(), 0);
m_pwm_phase = 0;
std::fill(m_pitch.begin(), m_pitch.end(), 0);
m_effect_speed = 0;
m_cutoff = 0;
m_eg_intensity = 0;
m_resonance = 0;
m_attack = 0;
m_decay = 0;
m_suspend = 0;
m_release = 0;
m_keyboard_tracking = 0;
m_pw_pwm = 0;
m_pwm_speed = 0;
m_mg_speed = 0;
m_mg_delay = 0;
m_mg_level = 0;
m_control_low = 0;
m_control_high = 0;
m_gates = 0;
m_current_gates = 0;
}
void polysix_sound_block::device_reset()
{
}
void polysix_sound_block::set_pitch(int channel, u8 pitch)
{
if(m_pitch[channel] != pitch) {
m_stream->update();
m_pitch[channel] = pitch;
static const char *const notes[12] = { "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" };
logerror("channel %d pitch %02x %s%d\n", channel, pitch, notes[pitch % 12], (pitch/12));
}
}
void polysix_sound_block::set_gates(u8 gates)
{
if(gates != m_gates) {
m_stream->update();
m_gates = gates;
}
}
void polysix_sound_block::set_effect_speed(u8 value)
{
if(value != m_effect_speed) {
m_stream->update();
m_effect_speed = value;
logerror("effect_speed = %02x\n", value);
}
}
void polysix_sound_block::set_cutoff(u8 value)
{
if(value != m_cutoff) {
m_stream->update();
m_cutoff = value;
logerror("cutoff = %02x\n", value);
}
}
void polysix_sound_block::set_eg_intensity(u8 value)
{
if(value != m_eg_intensity) {
m_stream->update();
m_eg_intensity = value;
logerror("eg_intensity = %02x\n", value);
}
}
void polysix_sound_block::set_resonance(u8 value)
{
if(value != m_resonance) {
m_stream->update();
m_resonance = value;
logerror("resonance = %02x\n", value);
}
}
void polysix_sound_block::set_attack(u8 value)
{
if(value != m_attack) {
m_stream->update();
m_attack = value;
logerror("attack = %02x\n", value);
}
}
void polysix_sound_block::set_decay(u8 value)
{
if(value != m_decay) {
m_stream->update();
m_decay = value;
logerror("decay = %02x\n", value);
}
}
void polysix_sound_block::set_suspend(u8 value)
{
if(value != m_suspend) {
m_stream->update();
m_suspend = value;
logerror("suspend = %02x\n", value);
}
}
void polysix_sound_block::set_release(u8 value)
{
if(value != m_release) {
m_stream->update();
m_release = value;
logerror("release = %02x\n", value);
}
}
void polysix_sound_block::set_keyboard_tracking(u8 value)
{
if(value != m_keyboard_tracking) {
m_stream->update();
m_keyboard_tracking = value;
logerror("keyboard_tracking = %02x\n", value);
}
}
void polysix_sound_block::set_pw_pwm(u8 value)
{
if(value != m_pw_pwm) {
m_stream->update();
m_pw_pwm = value;
logerror("pw_pwm = %02x\n", value);
}
}
void polysix_sound_block::set_pwm_speed(u8 value)
{
if(value != m_pwm_speed) {
m_stream->update();
m_pwm_speed = value;
logerror("pwm_speed = %02x\n", value);
}
}
void polysix_sound_block::set_mg_speed(u8 value)
{
if(value != m_mg_speed) {
m_stream->update();
m_mg_speed = value;
logerror("mg_speed = %02x\n", value);
}
}
void polysix_sound_block::set_mg_delay(u8 value)
{
if(value != m_mg_delay) {
m_stream->update();
m_mg_delay = value;
logerror("mg_delay = %02x\n", value);
}
}
void polysix_sound_block::set_mg_level(u8 value)
{
if(value != m_mg_level) {
m_stream->update();
m_mg_level = value;
logerror("mg_level = %02x\n", value);
}
}
void polysix_sound_block::set_control_low(u8 value)
{
if(value != m_control_low) {
m_stream->update();
m_control_low = value;
logerror("control low vco=%s wave=%s sub=%s mod=%s\n",
BIT(value, 0, 2) == 0 ? "16'" : BIT(value, 0, 2) == 1 ? "8'" : BIT(value, 0, 2) == 2 ? "4'" : "?",
BIT(value, 2, 2) == 1 ? "tri" : BIT(value, 2, 2) == 0 ? "pw" : BIT(value, 2, 2) == 2 ? "pwm" : "?",
BIT(value, 4, 2) == 0 ? "off" : BIT(value, 4, 2) == 1 ? "1oct" : BIT(value, 4, 2) == 2 ? "2oct" : "?",
BIT(value, 6, 2) == 0 ? "vca" : BIT(value, 6, 2) == 1 ? "vcf" : BIT(value, 6, 2) == 2 ? "vco" : "?"
);
}
}
void polysix_sound_block::set_control_high(u8 value)
{
if(value != m_control_high) {
m_stream->update();
m_control_high = value;
logerror("control high vca-mode=%s chorus=%s phase=%s ens=%s p.vol=%x\n",
BIT(value, 0) ? "eg" : "square",
BIT(value, 1) ? "on" : "off",
BIT(value, 2) ? "on" : "off",
BIT(value, 3) ? "on" : "off",
BIT(value, 4, 4)
);
}
}
u8 polysix_sound_block::get_control_low()
{
return m_control_low;
}
// #*#*#*#*#*#*#*#
void polysix_sound_block::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
{
for(int sample=0; sample != outputs[0].samples(); sample++) {
// u8 trigger = m_gates & ~m_current_gates;
float out = 0;
// Step the pwm phase (common to all channels)
m_pwm_phase += pwm_phase_step[m_pwm_speed];
while(m_pwm_phase >= 2)
m_pwm_phase -= 2;
// Wrap into a triangle
float pwm_phase = m_pwm_phase >= 1 ? 2 - m_pwm_phase : m_pwm_phase;
// Compute the threshold
float pw_thr = pw_threshold[m_pw_pwm];
if(BIT(m_control_low, 3) || 1)
// PWM mode, the modulation multiplies the threshold part over 0.5 with the phase wrapped between 0.2 and 1
pw_thr = 0.5 + (pw_thr - 0.5) * (0.2 + pwm_phase * 0.8);
for(int channel = 0; channel != 6; channel ++) {
if((m_gates ^ m_current_gates) & (1 << channel))
logerror("channel %d %s\n", channel, (m_gates & (1<<channel)) ? "keyon" : "keyoff");
// Step the phase
m_phase[channel] += phase_step[m_pitch[channel]];
while(m_phase[channel] >= 4)
m_phase[channel] -= 4;
int subosc_step = int(m_phase[channel]);
float phase = m_phase[channel] - subosc_step;
// Generate the initial wave in the [-1, 1] range
float wave;
if(BIT(m_control_low, 2))
// triangle
wave = 2*(phase - 0.5);
else
// PW(M)
wave = phase >= pw_thr ? 1 : -1;
// Add the sub-oscillator, if active
if(BIT(m_control_low, 4, 2))
wave += (subosc_step & (BIT(m_control_low, 5, 1) ? 2 : 1)) ? 1 : 0;
// Step the organ EG
if(BIT(m_gates, channel))
// When gate is on, charge a 0.047uF cap through a 10K resistor
m_organ_eg[channel] += (1-m_organ_eg[channel])*0.0433581893516088; // 1-exp(-1/(10e3 * 0.047e-6 * 48000))
else
// When gate is off, discharge a 0.047uF cap through a 230K resistor
m_organ_eg[channel] -= m_organ_eg[channel]*0.00192537196422815; // 1-exp(-1/(230e3 * 0.047e-6 * 48000))
out += wave * m_organ_eg[channel];
}
m_current_gates = m_gates;
outputs[0].put_clamp(sample, out/2);
}
}
class polysix_state : public driver_device
{
public:
polysix_state(const machine_config &mconfig, device_type type, const char *tag)
: driver_device(mconfig, type, tag)
, m_progmcu(*this, "progmcu")
, m_keymcu(*this, "keymcu")
, m_sound(*this, "soundblock")
, m_keyboard(*this, "K%u", 0U)
, m_progboard(*this, "P%u", 0U)
, m_knobs(*this, "A%x", 0U)
, m_tape_enable(*this, "TAPE")
, m_tape(*this, "tape")
{
}
virtual void machine_start() override ATTR_COLD;
void polysix(machine_config &config);
INPUT_CHANGED_MEMBER(tape_enable_w);
private:
required_device<mcs48_cpu_device> m_progmcu;
required_device<mcs48_cpu_device> m_keymcu;
required_device<polysix_sound_block> m_sound;
std::unique_ptr<u8[]> m_nvram_ptr;
required_ioport_array<10> m_keyboard;
required_ioport_array<8> m_progboard;
required_ioport_array<16> m_knobs;
required_ioport m_tape_enable;
required_device<cassette_image_device> m_tape;
u16 m_pleds;
u16 m_pctrl;
u8 m_kleds;
u8 m_prog_bus;
@ -63,10 +463,11 @@ private:
u8 m_prog_p2;
u8 m_key_bus;
u8 m_keyon;
u8 m_key_ic2;
u8 m_key_p2;
u8 m_trigger;
bool m_tape_enabled;
void key_bus_w(u8 data);
u8 key_p1_r();
void key_p2_w(u8 data);
@ -96,67 +497,34 @@ void polysix_state::machine_start()
subdevice<nvram_device>("nvram")->set_base(&m_nvram_ptr[0], 0x400);
save_pointer(NAME(m_nvram_ptr), 0x400);
save_item(NAME(m_pitch));
save_item(NAME(m_pleds));
save_item(NAME(m_pctrl));
save_item(NAME(m_kleds));
save_item(NAME(m_key_bus));
save_item(NAME(m_keyon));
save_item(NAME(m_key_ic2));
save_item(NAME(m_trigger));
save_item(NAME(m_prog_bus));
save_item(NAME(m_prog_p1));
save_item(NAME(m_prog_p2));
save_item(NAME(m_effect_speed));
save_item(NAME(m_cutoff));
save_item(NAME(m_eg_intensity));
save_item(NAME(m_resonance));
save_item(NAME(m_attack));
save_item(NAME(m_decay));
save_item(NAME(m_suspend));
save_item(NAME(m_release));
save_item(NAME(m_keyboard_tracking));
save_item(NAME(m_pw_pwm));
save_item(NAME(m_pwm_speed));
save_item(NAME(m_mg_speed));
save_item(NAME(m_mg_delay));
save_item(NAME(m_mg_level));
std::fill(m_pitch.begin(), m_pitch.end(), 0);
m_effect_speed = 0;
m_cutoff = 0;
m_eg_intensity = 0;
m_resonance = 0;
m_attack = 0;
m_decay = 0;
m_suspend = 0;
m_release = 0;
m_keyboard_tracking = 0;
m_pw_pwm = 0;
m_pwm_speed = 0;
m_mg_speed = 0;
m_mg_delay = 0;
m_mg_level = 0;
save_item(NAME(m_tape_enabled));
m_pleds = 0;
m_key_bus = 0;
m_keyon = 0;
m_key_ic2 = 0;
m_key_p2 = 0;
m_trigger = 0;
m_prog_bus = 0;
m_prog_p1 = 0;
m_prog_p2 = 0;
m_tape_enabled = false;
}
u8 polysix_state::prog_r(offs_t offset)
{
if(BIT(m_prog_p1, 7)) {
u16 adr = BIT(m_prog_p2, 0) | (offset << 1) | ( BIT(m_prog_p2, 1) << 9);
logerror("nvram_r %03x = %x\n", adr, m_nvram_ptr[adr]);
return m_nvram_ptr[adr] | 0xf0;
}
@ -167,7 +535,6 @@ void polysix_state::prog_w(offs_t offset, u8 data)
{
if(BIT(m_prog_p1, 7)) {
u16 adr = BIT(m_prog_p2, 0) | (offset << 1) | ( BIT(m_prog_p2, 1) << 9);
logerror("nvram_w %03x = %x\n", adr, data & 0x0f);
m_nvram_ptr[adr] = data & 0x0f;
}
}
@ -191,37 +558,20 @@ void polysix_state::prog_p1_w(u8 data)
if(BIT(chg, 6) && BIT(m_prog_p1, 6)) {
switch(m_prog_p2 & 0xf) {
case 0x0: if(m_prog_bus != m_effect_speed) logerror("effect_speed = %02x\n", m_prog_bus); break;
case 0x1: if(m_prog_bus != m_cutoff) logerror("cutoff = %02x\n", m_prog_bus); break;
case 0x2: if(m_prog_bus != m_eg_intensity) logerror("eg_intensity = %02x\n", m_prog_bus); break;
case 0x3: if(m_prog_bus != m_resonance) logerror("resonance = %02x\n", m_prog_bus); break;
case 0x4: if(m_prog_bus != m_attack) logerror("attack = %02x\n", m_prog_bus); break;
case 0x5: if(m_prog_bus != m_decay) logerror("decay = %02x\n", m_prog_bus); break;
case 0x6: if(m_prog_bus != m_suspend) logerror("suspend = %02x\n", m_prog_bus); break;
case 0x7: if(m_prog_bus != m_release) logerror("release = %02x\n", m_prog_bus); break;
case 0x8: if(m_prog_bus != m_keyboard_tracking) logerror("keyboard_tracking = %02x\n", m_prog_bus); break;
case 0x9: if(m_prog_bus != m_pw_pwm) logerror("pw_pwm = %02x\n", m_prog_bus); break;
case 0xa: if(m_prog_bus != m_pwm_speed) logerror("pwm_speed = %02x\n", m_prog_bus); break;
case 0xb: if(m_prog_bus != m_mg_speed) logerror("mg_speed = %02x\n", m_prog_bus); break;
case 0xc: if(m_prog_bus != m_mg_delay) logerror("mg_delay = %02x\n", m_prog_bus); break;
case 0xd: if(m_prog_bus != m_mg_level) logerror("mg_level = %02x\n", m_prog_bus); break;
}
switch(m_prog_p2 & 0xf) {
case 0x0: m_effect_speed = m_prog_bus; break;
case 0x1: m_cutoff = m_prog_bus; break;
case 0x2: m_eg_intensity = m_prog_bus; break;
case 0x3: m_resonance = m_prog_bus; break;
case 0x4: m_attack = m_prog_bus; break;
case 0x5: m_decay = m_prog_bus; break;
case 0x6: m_suspend = m_prog_bus; break;
case 0x7: m_release = m_prog_bus; break;
case 0x8: m_keyboard_tracking = m_prog_bus; break;
case 0x9: m_pw_pwm = m_prog_bus; break;
case 0xa: m_pwm_speed = m_prog_bus; break;
case 0xb: m_mg_speed = m_prog_bus; break;
case 0xc: m_mg_delay = m_prog_bus; break;
case 0xd: m_mg_level = m_prog_bus; break;
case 0x0: m_sound->set_effect_speed(m_prog_bus); break;
case 0x1: m_sound->set_cutoff(m_prog_bus); break;
case 0x2: m_sound->set_eg_intensity(m_prog_bus); break;
case 0x3: m_sound->set_resonance(m_prog_bus); break;
case 0x4: m_sound->set_attack(m_prog_bus); break;
case 0x5: m_sound->set_decay(m_prog_bus); break;
case 0x6: m_sound->set_suspend(m_prog_bus); break;
case 0x7: m_sound->set_release(m_prog_bus); break;
case 0x8: m_sound->set_keyboard_tracking(m_prog_bus); break;
case 0x9: m_sound->set_pw_pwm(m_prog_bus); break;
case 0xa: m_sound->set_pwm_speed(m_prog_bus); break;
case 0xb: m_sound->set_mg_speed(m_prog_bus); break;
case 0xc: m_sound->set_mg_delay(m_prog_bus); break;
case 0xd: m_sound->set_mg_level(m_prog_bus); break;
}
}
}
@ -232,10 +582,10 @@ void polysix_state::prog_p2_w(u8 data)
m_prog_p2 = data;
if(BIT(chg, 4) && !BIT(m_prog_p2, 4))
m_pctrl = (m_pctrl & 0xff00) | m_prog_bus;
m_sound->set_control_low(m_prog_bus);
if(BIT(chg, 5) && !BIT(m_prog_p2, 5))
m_pctrl = (m_pctrl & 0x00ff) | (m_prog_bus << 8);
m_sound->set_control_high(m_prog_bus);
u16 old_leds = m_pleds;
@ -259,8 +609,8 @@ void polysix_state::prog_p2_w(u8 data)
BIT(m_pleds, 9) ? 'B' : '.',
BIT(m_pleds, 10) ? 'C' : '.',
BIT(m_pleds, 11) ? 'D' : '.',
BIT(m_pleds, 12) ? " manual" : "",
BIT(m_pleds, 13) ? " write" : "");
BIT(m_pleds, 12) ? " manual/found" : "",
BIT(m_pleds, 13) ? " write/loading" : "");
}
void polysix_state::prog_bus_w(u8 data)
@ -275,8 +625,8 @@ int polysix_state::prog_t0_r()
int polysix_state::prog_t1_r()
{
logerror("prog t1\n");
return 1;
// logerror("prog t1 %f\n", m_tape->input());
return m_tape->input() >= 0;
}
u8 polysix_state::key_p1_r()
@ -298,7 +648,7 @@ void polysix_state::key_bus_w(u8 data)
}
// cn12 at klm-371
// cn12-17 -> int, aoki, arpeggiator
// cn12-17 -> int, acki, arpeggiator
// cn12-16 = p27, gnd?
// klm-367
// cn05-1 = reset
@ -312,20 +662,8 @@ void polysix_state::key_p2_w(u8 data)
m_key_p2 = data;
if(BIT(chg, 3) && !BIT(m_key_p2, 3)) {
u8 chgk = m_keyon ^ m_key_bus;
m_keyon = m_key_bus;
m_trigger |= m_keyon & chgk & 0x3f;
if(m_trigger)
logerror("keyon %c%c%c%c%c%c\n",
BIT(m_trigger, 0) ? '0' : '.',
BIT(m_trigger, 1) ? '1' : '.',
BIT(m_trigger, 2) ? '2' : '.',
BIT(m_trigger, 3) ? '3' : '.',
BIT(m_trigger, 4) ? '4' : '.',
BIT(m_trigger, 5) ? '5' : '.');
m_trigger = 0;
if(BIT(chgk, 7))
logerror("ic45 = %02x %s\n", m_keyon, machine().describe_context());
m_sound->set_gates(m_tape_enabled ? 0 : m_key_bus & 0x3f);
// TODO logerror("ic45 = %02x %s\n", m_key_bus, machine().describe_context());
}
if(BIT(chg, 4) && !BIT(m_key_p2, 4)) {
@ -342,13 +680,10 @@ void polysix_state::key_p2_w(u8 data)
if(!BIT(m_key_p2, 5)) {
int index = BIT(data, 0, 3);
if(m_key_bus != m_pitch[index]) {
m_pitch[index] = m_key_bus;
if(index < 6)
logerror("voice %d pitch %3d\n", index, m_key_bus);
else
logerror("key analog #%d = %3d\n", index, m_key_bus);
}
if(index < 6)
m_sound->set_pitch(index, m_key_bus);
else
; // TODO
}
if(BIT(chg, 7))
@ -359,12 +694,21 @@ void polysix_state::key_p2_w(u8 data)
int polysix_state::key_t0_r()
{
return BIT(m_pctrl, 0);
return !BIT(m_sound->get_control_low(), 0);
}
int polysix_state::key_t1_r()
{
return BIT(m_pctrl, 01);
return !BIT(m_sound->get_control_low(), 01);
}
INPUT_CHANGED_MEMBER(polysix_state::tape_enable_w)
{
logerror("leds tape enable %s\n", newval ? "on" : "off");
m_tape_enabled = newval;
m_progmcu->set_input_line(0, newval);
if(newval)
m_keyon = 0;
}
static INPUT_PORTS_START(polysix)
@ -469,21 +813,21 @@ static INPUT_PORTS_START(polysix)
PORT_CONFSETTING( 0xc0, "1 octave")
PORT_START("P0")
PORT_CONFNAME(0x03, 0x03, "VCO octave")
PORT_CONFSETTING( 0x01, "16'")
PORT_CONFNAME(0x03, 0x02, "VCO octave")
PORT_CONFSETTING( 0x03, "16'")
PORT_CONFSETTING( 0x02, "8'")
PORT_CONFSETTING( 0x03, "4'")
PORT_CONFNAME(0x0c, 0x0c, "Waveform")
PORT_CONFSETTING( 0x04, "Triangle")
PORT_CONFSETTING( 0x08, "PW")
PORT_CONFSETTING( 0x0c, "PWM")
PORT_CONFSETTING( 0x01, "4'")
PORT_CONFNAME(0x0c, 0x08, "Waveform")
PORT_CONFSETTING( 0x08, "Triangle")
PORT_CONFSETTING( 0x0c, "PW")
PORT_CONFSETTING( 0x04, "PWM")
PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("P1")
PORT_CONFNAME(0x03, 0x0c, "Sub oscillator")
PORT_CONFSETTING( 0x01, "off")
PORT_CONFNAME(0x03, 0x01, "Sub oscillator")
PORT_CONFSETTING( 0x03, "off")
PORT_CONFSETTING( 0x02, "1 oct")
PORT_CONFSETTING( 0x03, "2 oct")
PORT_CONFSETTING( 0x01, "2 oct")
PORT_CONFNAME(0x0c, 0x0c, "MG modulation")
PORT_CONFSETTING( 0x04, "VCO")
PORT_CONFSETTING( 0x08, "VCF")
@ -540,7 +884,9 @@ static INPUT_PORTS_START(polysix)
PORT_START("P7")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("found / manual")
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("write / loading")
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("write enable")
PORT_CONFNAME(0x04, 0x04, "write enable")
PORT_CONFSETTING( 0x00, "enabled")
PORT_CONFSETTING( 0x04, "disabled")
PORT_BIT(0xf8, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("A0")
@ -590,6 +936,12 @@ static INPUT_PORTS_START(polysix)
PORT_START("Af")
PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
PORT_START("TAPE")
PORT_CONFNAME(0x01, 0x00, "Tape access") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(polysix_state::tape_enable_w), 0)
PORT_CONFSETTING( 0x01, "enabled")
PORT_CONFSETTING( 0x00, "disabled")
INPUT_PORTS_END
void polysix_state::polysix(machine_config &config)
@ -611,6 +963,17 @@ void polysix_state::polysix(machine_config &config)
m_keymcu->t1_in_cb().set(FUNC(polysix_state::key_t1_r));
NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // TC5514APL-3 + battery
CASSETTE(config, m_tape);
m_tape->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_MUTED);
m_tape->set_interface("polysix");
SOFTWARE_LIST(config, "polysix").set_original("polysix");
POLYSIX_SOUND_BLOCK(config, m_sound);
m_sound->add_route(0, "speaker", 1.0);
SPEAKER(config, "speaker").front_center();
}
ROM_START(polysix)
@ -619,9 +982,10 @@ ROM_START(polysix)
ROM_REGION(0x800, "keymcu", 0)
ROM_LOAD("d8049c-217.bin", 0x000, 0x800, CRC(246d7767) SHA1(5b608c750e7fe7832070a53a74df416fd132ecb7))
ROM_END
} // anonymous namespace
ROM_REGION(0x400, "nvram", 0)
ROM_LOAD("nvram.bin", 0x000, 0x400, CRC(1a913972) SHA1(891c7c2aa2f3cd54e3dd6847fad58ab831838c3e))
ROM_END
SYST(1980, polysix, 0, 0, polysix, polysix, polysix_state, empty_init, "Korg", "Polysix Programmable Polyphonic Synthesizer", MACHINE_IS_SKELETON)