OSDN Git Service

[VM][OSD] Add upstream devices/OSD updates.
authorK.Ohta <whatisthis.sowhat@gmail.com>
Wed, 18 Jan 2023 03:12:42 +0000 (12:12 +0900)
committerK.Ohta <whatisthis.sowhat@gmail.com>
Wed, 18 Jan 2023 03:12:42 +0000 (12:12 +0900)
17 files changed:
source/src/vm/midi.cpp [new file with mode: 0644]
source/src/vm/midi.h [new file with mode: 0644]
source/src/vm/speaker.cpp [new file with mode: 0644]
source/src/vm/speaker.h [new file with mode: 0644]
source/src/vm/tmpz84c015.cpp [new file with mode: 0644]
source/src/vm/tmpz84c015.h [new file with mode: 0644]
source/src/vm/trnjr/display.cpp [new file with mode: 0644]
source/src/vm/trnjr/display.h [new file with mode: 0644]
source/src/vm/trnjr/membus.cpp [new file with mode: 0644]
source/src/vm/trnjr/membus.h [new file with mode: 0644]
source/src/vm/trnjr/trnjr.cpp [new file with mode: 0644]
source/src/vm/trnjr/trnjr.h [new file with mode: 0644]
source/src/vm/vm.h
source/src/win32/osd.cpp
source/src/win32/osd.h
source/src/win32/osd_console.cpp
source/src/win32/osd_midi.cpp [new file with mode: 0644]

diff --git a/source/src/vm/midi.cpp b/source/src/vm/midi.cpp
new file mode 100644 (file)
index 0000000..e940907
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+       Skelton for retropc emulator
+
+       Author : Takeda.Toshiya
+       Date   : 2022.07.03-
+
+       [ midi port ]
+*/
+
+#include "midi.h"
+
+void MIDI::initialize()
+{
+       // midi-in is not implemenetd in win32-osd :-(
+//     register_vline_event(this);
+}
+
+void MIDI::write_signal(int id, uint32_t data, uint32_t mask)
+{
+       if(id == SIG_MIDI_OUT) {
+               emu->send_to_midi(data & mask);
+       }
+}
+
+void MIDI::event_vline(int v, int clock)
+{
+       uint8_t data;
+       
+       while(emu->recv_from_midi(&data)) {
+               write_signals(&outputs, data);
+       }
+}
diff --git a/source/src/vm/midi.h b/source/src/vm/midi.h
new file mode 100644 (file)
index 0000000..7d6f88e
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+       Skelton for retropc emulator
+
+       Author : Takeda.Toshiya
+       Date   : 2022.07.03-
+
+       [ midi port ]
+*/
+
+#ifndef _MIDI_H_
+#define _MIDI_H_
+
+#include "vm.h"
+#include "../emu.h"
+#include "device.h"
+
+#define SIG_MIDI_OUT   0
+
+class MIDI : public DEVICE
+{
+private:
+       outputs_t outputs;
+       
+public:
+       MIDI(VM_TEMPLATE* parent_vm, EMU* parent_emu) : DEVICE(parent_vm, parent_emu)
+       {
+               initialize_output_signals(&outputs);
+               set_device_name(_T("MIDI port"));
+       }
+       ~MIDI() {}
+       
+       // common functions
+       void initialize();
+       void write_signal(int id, uint32_t data, uint32_t mask);
+       void event_vline(int v, int clock);
+       
+       // unique function
+       void set_context_in(DEVICE* device, int id, uint32_t mask)
+       {
+               register_output_signal(&outputs, device, id, mask);
+       }
+};
+
+#endif
+
diff --git a/source/src/vm/speaker.cpp b/source/src/vm/speaker.cpp
new file mode 100644 (file)
index 0000000..9b083fc
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+       Skelton for retropc emulator
+
+       Author : Takeda.Toshiya
+       Date   : 2022.07.03-
+
+       [ Speaker ]
+*/
+
+#include "speaker.h"
+
+void SPEAKER::initialize()
+{
+       realtime = false;
+       last_vol_l = last_vol_r = 0;
+       
+       register_frame_event(this);
+}
+
+void SPEAKER::reset()
+{
+       changed = 0;
+       sample = prev_sample = 0;
+       prev_clock = change_clock = get_current_clock();
+}
+
+void SPEAKER::write_signal(int id, uint32_t data, uint32_t mask)
+{
+       if(id == SIG_SPEAKER_SAMPLE) {
+               if(sample != (data & mask)) {
+                       // mute if signal is not changed in 2 frames
+                       changed = 2;
+                       update_realtime_render();
+                       
+                       sample = data & mask;
+                       change_clock = get_current_clock();
+               }
+       }
+}
+
+void SPEAKER::event_frame()
+{
+       if(changed > 0 && --changed == 0) {
+               update_realtime_render();
+       }
+}
+
+void SPEAKER::update_realtime_render()
+{
+       bool value = (changed != 0);
+       
+       if(realtime != value) {
+               set_realtime_render(this, value);
+               realtime = value;
+       }
+}
+
+void SPEAKER::mix(int32_t* buffer, int cnt)
+{
+       if(changed) {
+               uint32_t cur_clock = get_current_clock();
+               int cur_sample;
+               
+               if(change_clock > prev_clock) {
+                       cur_sample  = prev_sample * (change_clock - prev_clock) + sample * (cur_clock - change_clock);
+                       cur_sample /= cur_clock - prev_clock;
+               } else {
+                       cur_sample = sample;
+               }
+               prev_sample = sample;
+               prev_clock = cur_clock;
+               
+               int volume = max_vol * cur_sample / 256;
+               
+               last_vol_l = apply_volume(volume, volume_l);
+               last_vol_r = apply_volume(volume, volume_r);
+               
+               for(int i = 0; i < cnt; i++) {
+                       *buffer++ += last_vol_l; // L
+                       *buffer++ += last_vol_r; // R
+               }
+       } else {
+               // suppress petite noise when go to mute
+               for(int i = 0; i < cnt; i++) {
+                       *buffer++ += last_vol_l; // L
+                       *buffer++ += last_vol_r; // R
+                       
+                       if(last_vol_l > 0) {
+                               last_vol_l--;
+                       } else if(last_vol_l < 0) {
+                               last_vol_l++;
+                       }
+                       if(last_vol_r > 0) {
+                               last_vol_r--;
+                       } else if(last_vol_r < 0) {
+                               last_vol_r++;
+                       }
+               }
+       }
+}
+
+void SPEAKER::set_volume(int ch, int decibel_l, int decibel_r)
+{
+       volume_l = decibel_to_volume(decibel_l);
+       volume_r = decibel_to_volume(decibel_r);
+}
+
+void SPEAKER::initialize_sound(int rate, int volume)
+{
+       max_vol = volume;
+}
+
+#define STATE_VERSION  1
+
+bool SPEAKER::process_state(FILEIO* state_fio, bool loading)
+{
+       if(!state_fio->StateCheckUint32(STATE_VERSION)) {
+               return false;
+       }
+       if(!state_fio->StateCheckInt32(this_device_id)) {
+               return false;
+       }
+       state_fio->StateValue(realtime);
+       state_fio->StateValue(changed);
+       state_fio->StateValue(sample);
+       state_fio->StateValue(prev_sample);
+       state_fio->StateValue(prev_clock);
+       state_fio->StateValue(change_clock);
+       
+       // post process
+       if(loading) {
+               last_vol_l = last_vol_r = 0;
+       }
+       return true;
+}
+
diff --git a/source/src/vm/speaker.h b/source/src/vm/speaker.h
new file mode 100644 (file)
index 0000000..2f5261b
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+       Skelton for retropc emulator
+
+       Author : Takeda.Toshiya
+       Date   : 2022.07.03-
+
+       [ Speaker ]
+*/
+
+#ifndef _SPEAKER_H_
+#define _SPEAKER_H_
+
+#include "vm.h"
+#include "../emu.h"
+#include "device.h"
+
+#define SIG_SPEAKER_SAMPLE     0
+
+class SPEAKER : public DEVICE
+{
+private:
+       bool realtime;
+       int changed;
+       int sample, prev_sample;
+       uint32_t prev_clock, change_clock;
+       int max_vol, last_vol_l, last_vol_r;
+       int volume_l, volume_r;
+       
+       void update_realtime_render();
+       
+public:
+       SPEAKER(VM_TEMPLATE* parent_vm, EMU* parent_emu) : DEVICE(parent_vm, parent_emu)
+       {
+               volume_l = volume_r = 1024;
+               set_device_name(_T("Speaker"));
+       }
+       ~SPEAKER() {}
+       
+       // common functions
+       void initialize();
+       void reset();
+       void write_signal(int id, uint32_t data, uint32_t mask);
+       void event_frame();
+       void mix(int32_t* buffer, int cnt);
+       void set_volume(int ch, int decibel_l, int decibel_r);
+       bool process_state(FILEIO* state_fio, bool loading);
+       
+       // unique function
+       void initialize_sound(int rate, int volume);
+};
+
+#endif
+
diff --git a/source/src/vm/tmpz84c015.cpp b/source/src/vm/tmpz84c015.cpp
new file mode 100644 (file)
index 0000000..1f228fd
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+       Skelton for retropc emulator
+
+       Author : Takeda.Toshiya
+       Date   : 2022.07.02-
+
+       [ TMPZ84C015/TMP84C013 ]
+*/
+
+#include "tmpz84c015.h"
+
+void TMPZ84C015::reset()
+{
+       iei = true;
+       update_priority(0);
+}
+
+void TMPZ84C015::write_io8(uint32_t addr, uint32_t data)
+{
+       switch(addr) {
+       case 0xf0:
+       case 0xf1:
+               // watch dog timer
+               break;
+       case 0xf4:
+               update_priority(data);
+               d_1st->set_intr_iei(iei);
+               break;
+       }
+}
+
+uint32_t TMPZ84C015::read_io8(uint32_t addr)
+{
+       switch(addr) {
+       case 0xf4:
+               return priority;
+       }
+       return 0xff;
+}
+
+void TMPZ84C015::write_signal(int id, uint32_t data, uint32_t mask)
+{
+       switch(id) {
+       case SIG_TMPZ84C015_CTC_TRIG_0:
+               d_ctc->write_signal(SIG_Z80CTC_TRIG_0, data, mask);
+               break;
+       case SIG_TMPZ84C015_CTC_TRIG_1:
+               d_ctc->write_signal(SIG_Z80CTC_TRIG_1, data, mask);
+               break;
+       case SIG_TMPZ84C015_CTC_TRIG_2:
+               d_ctc->write_signal(SIG_Z80CTC_TRIG_2, data, mask);
+               break;
+       case SIG_TMPZ84C015_CTC_TRIG_3:
+               d_ctc->write_signal(SIG_Z80CTC_TRIG_3, data, mask);
+               break;
+       case SIG_TMPZ84C015_SIO_RECV_CH0:
+               d_sio->write_signal(SIG_Z80SIO_RECV_CH0, data, mask);
+               break;
+       case SIG_TMPZ84C015_SIO_RECV_CH1:
+               d_sio->write_signal(SIG_Z80SIO_RECV_CH1, data, mask);
+               break;
+       case SIG_TMPZ84C015_SIO_BREAK_CH0:
+               d_sio->write_signal(SIG_Z80SIO_BREAK_CH0, data, mask);
+               break;
+       case SIG_TMPZ84C015_SIO_BREAK_CH1:
+               d_sio->write_signal(SIG_Z80SIO_BREAK_CH1, data, mask);
+               break;
+       case SIG_TMPZ84C015_SIO_DCD_CH0:
+               d_sio->write_signal(SIG_Z80SIO_DCD_CH0, data, mask);
+               break;
+       case SIG_TMPZ84C015_SIO_DCD_CH1:
+               d_sio->write_signal(SIG_Z80SIO_DCD_CH1, data, mask);
+               break;
+       case SIG_TMPZ84C015_SIO_CTS_CH0:
+               d_sio->write_signal(SIG_Z80SIO_CTS_CH0, data, mask);
+               break;
+       case SIG_TMPZ84C015_SIO_CTS_CH1:
+               d_sio->write_signal(SIG_Z80SIO_CTS_CH1, data, mask);
+               break;
+       case SIG_TMPZ84C015_SIO_SYNC_CH0:
+               d_sio->write_signal(SIG_Z80SIO_SYNC_CH0, data, mask);
+               break;
+       case SIG_TMPZ84C015_SIO_SYNC_CH1:
+               d_sio->write_signal(SIG_Z80SIO_SYNC_CH1, data, mask);
+               break;
+       case SIG_TMPZ84C015_SIO_TX_CLK_CH0:
+               d_sio->write_signal(SIG_Z80SIO_TX_CLK_CH0, data, mask);
+               break;
+       case SIG_TMPZ84C015_SIO_TX_CLK_CH1:
+               d_sio->write_signal(SIG_Z80SIO_TX_CLK_CH1, data, mask);
+               break;
+       case SIG_TMPZ84C015_SIO_RX_CLK_CH0:
+               d_sio->write_signal(SIG_Z80SIO_RX_CLK_CH0, data, mask);
+               break;
+       case SIG_TMPZ84C015_SIO_RX_CLK_CH1:
+               d_sio->write_signal(SIG_Z80SIO_RX_CLK_CH1, data, mask);
+               break;
+       case SIG_TMPZ84C015_SIO_CLEAR_CH0:
+               d_sio->write_signal(SIG_Z80SIO_CLEAR_CH0, data, mask);
+               break;
+       case SIG_TMPZ84C015_SIO_CLEAR_CH1:
+               d_sio->write_signal(SIG_Z80SIO_CLEAR_CH1, data, mask);
+               break;
+#ifndef HAS_TMPZ84C013
+       case SIG_TMPZ84C015_PIO_PORT_A:
+               d_pio->write_signal(SIG_Z80PIO_PORT_A, data, mask);
+               break;
+       case SIG_TMPZ84C015_PIO_PORT_B:
+               d_pio->write_signal(SIG_Z80PIO_PORT_B, data, mask);
+               break;
+       case SIG_TMPZ84C015_PIO_STROBE_A:
+               d_pio->write_signal(SIG_Z80PIO_STROBE_A, data, mask);
+               break;
+       case SIG_TMPZ84C015_PIO_STROBE_B:
+               d_pio->write_signal(SIG_Z80PIO_STROBE_B, data, mask);
+               break;
+#endif
+       }
+}
+
+void TMPZ84C015::update_priority(uint8_t val)
+{
+       DEVICE *d_2nd;
+       
+#ifdef HAS_TMPZ84C013
+       switch(val & 1) {
+       case 0: d_1st = d_ctc; d_2nd = d_sio; break; // CTC -> SIO
+       case 1: d_1st = d_sio; d_2nd = d_ctc; break; // SIO -> CTC
+       }
+       d_1st->set_context_child(d_2nd);
+       d_2nd->set_context_child(d_child);
+#else
+       DEVICE *d_3rd;
+       
+       switch(val & 7) {
+       case 0: d_1st = d_ctc; d_2nd = d_sio; d_3rd = d_pio; break; // CTC -> SIO -> PIO
+       case 1: d_1st = d_sio; d_2nd = d_ctc; d_3rd = d_pio; break; // SIO -> CTC -> PIO
+       case 2: d_1st = d_ctc; d_2nd = d_pio; d_3rd = d_sio; break; // CTC -> PIO -> SIO
+       case 3: d_1st = d_pio; d_2nd = d_sio; d_3rd = d_ctc; break; // PIO -> SIO -> CTC
+       case 4: d_1st = d_pio; d_2nd = d_ctc; d_3rd = d_sio; break; // PIO -> CTC -> SIO
+       case 5: d_1st = d_sio; d_2nd = d_pio; d_3rd = d_ctc; break; // SIO -> PIO -> CTC
+       default:
+               // XXX: don't update the priority register
+               priority = (val & ~7) & (priority & 7);
+               return;
+       }
+       d_1st->set_context_child(d_2nd);
+       d_2nd->set_context_child(d_3rd);
+       d_3rd->set_context_child(d_child);
+#endif
+       priority = val;
+}
+
+#define STATE_VERSION  1
+
+bool TMPZ84C015::process_state(FILEIO* state_fio, bool loading)
+{
+       if(!state_fio->StateCheckUint32(STATE_VERSION)) {
+               return false;
+       }
+       if(!state_fio->StateCheckInt32(this_device_id)) {
+               return false;
+       }
+       state_fio->StateValue(iei);
+       state_fio->StateValue(priority);
+       
+       if(loading) {
+               update_priority(priority);
+       }
+       return true;
+}
+
diff --git a/source/src/vm/tmpz84c015.h b/source/src/vm/tmpz84c015.h
new file mode 100644 (file)
index 0000000..b1732d6
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+       Skelton for retropc emulator
+
+       Author : Takeda.Toshiya
+       Date   : 2022.07.02-
+
+       [ TMPZ84C015/TMP84C013 ]
+*/
+
+#ifndef _TMPZ84C015_H_
+#define _TMPZ84C015_H_
+
+#include "vm.h"
+#include "../emu.h"
+#include "device.h"
+
+#include "io.h"
+#include "z80ctc.h"
+#include "z80sio.h"
+#ifndef HAS_TMPZ84C013
+#include "z80pio.h"
+#endif
+
+#define SIG_TMPZ84C015_CTC_TRIG_0      0
+#define SIG_TMPZ84C015_CTC_TRIG_1      1
+#define SIG_TMPZ84C015_CTC_TRIG_2      2
+#define SIG_TMPZ84C015_CTC_TRIG_3      3
+
+#define SIG_TMPZ84C015_SIO_RECV_CH0    10
+#define SIG_TMPZ84C015_SIO_RECV_CH1    11
+#define SIG_TMPZ84C015_SIO_BREAK_CH0   12
+#define SIG_TMPZ84C015_SIO_BREAK_CH1   13
+#define SIG_TMPZ84C015_SIO_DCD_CH0     14
+#define SIG_TMPZ84C015_SIO_DCD_CH1     15
+#define SIG_TMPZ84C015_SIO_CTS_CH0     16
+#define SIG_TMPZ84C015_SIO_CTS_CH1     17
+#define SIG_TMPZ84C015_SIO_SYNC_CH0    18
+#define SIG_TMPZ84C015_SIO_SYNC_CH1    19
+#define SIG_TMPZ84C015_SIO_TX_CLK_CH0  20
+#define SIG_TMPZ84C015_SIO_TX_CLK_CH1  21
+#define SIG_TMPZ84C015_SIO_RX_CLK_CH0  22
+#define SIG_TMPZ84C015_SIO_RX_CLK_CH1  23
+// hack: clear recv buffer
+#define SIG_TMPZ84C015_SIO_CLEAR_CH0   24
+#define SIG_TMPZ84C015_SIO_CLEAR_CH1   25
+
+#ifndef HAS_TMPZ84C013
+#define SIG_TMPZ84C015_PIO_PORT_A      30
+#define SIG_TMPZ84C015_PIO_PORT_B      31
+#define SIG_TMPZ84C015_PIO_STROBE_A    32
+#define SIG_TMPZ84C015_PIO_STROBE_B    33
+#endif
+
+class TMPZ84C015 : public DEVICE
+{
+private:
+       Z80CTC *d_ctc;
+       Z80SIO *d_sio;
+#ifndef HAS_TMPZ84C013
+       Z80PIO *d_pio;
+#endif
+       
+       // daisy chain
+       DEVICE *d_child;
+       DEVICE *d_1st;
+       bool iei;
+       uint8_t priority;
+       
+       void update_priority(uint8_t val);
+       
+public:
+       TMPZ84C015(VM_TEMPLATE* parent_vm, EMU* parent_emu) : DEVICE(parent_vm, parent_emu)
+       {
+               d_child = d_1st = NULL;
+#ifdef HAS_TMPZ84C013
+               set_device_name(_T("TMPZ84C013"));
+#else
+               set_device_name(_T("TMPZ84C015"));
+#endif
+       }
+       ~TMPZ84C015() {}
+       
+       // common functions
+       void reset();
+       void write_io8(uint32_t addr, uint32_t data);
+       uint32_t read_io8(uint32_t addr);
+       void write_signal(int id, uint32_t data, uint32_t mask);
+       bool process_state(FILEIO* state_fio, bool loading);
+       
+       // interrupt common functions
+       void set_context_intr(DEVICE* device, uint32_t bit)
+       {
+               d_ctc->set_context_intr(device, bit + 0);
+               d_sio->set_context_intr(device, bit + 1);
+#ifndef HAS_TMPZ84C013
+               d_pio->set_context_intr(device, bit + 2);
+#endif
+       }
+       void set_context_child(DEVICE* device)
+       {
+               d_child = device;
+       }
+       DEVICE *get_context_child()
+       {
+               return d_child;
+       }
+       void set_intr_iei(bool val)
+       {
+               if(d_1st) {
+                       d_1st->set_intr_iei(val);
+               }
+               iei = val;
+       }
+       uint32_t get_intr_ack()
+       {
+               if(d_1st) {
+                       return d_1st->get_intr_ack();
+               }
+               return 0xff;
+       }
+       void notify_intr_reti()
+       {
+               if(d_1st) {
+                       d_1st->notify_intr_reti();
+               }
+       }
+       
+       // unique functions
+       void set_context_ctc(Z80CTC* device)
+       {
+               d_ctc = device;
+               d_ctc->set_device_name(_T("TMPZ84C015 CTC"));
+       }
+       void set_context_sio(Z80SIO* device)
+       {
+               d_sio = device;
+               d_sio->set_device_name(_T("TMPZ84C015 SIO"));
+       }
+#ifndef HAS_TMPZ84C013
+       void set_context_pio(Z80PIO* device)
+       {
+               d_pio = device;
+               d_pio->set_device_name(_T("TMPZ84C015 PIO"));
+       }
+#endif
+       void set_iomap(IO* io)
+       {
+               io->set_iomap_range_rw(0x10, 0x13, d_ctc);
+               io->set_iomap_range_rw(0x18, 0x1b, d_sio);
+#ifndef HAS_TMPZ84C013
+               io->set_iomap_range_rw(0x1c, 0x1f, d_pio);
+#endif
+               io->set_iomap_single_w (0xf0, this);
+               io->set_iomap_single_w (0xf1, this);
+               io->set_iomap_single_rw(0xf4, this);
+       }
+       
+       // unique functions (from Z80CTC)
+       void set_context_ctc_zc0(DEVICE* device, int id, uint32_t mask)
+       {
+               d_ctc->set_context_zc0(device, id, mask);
+       }
+       void set_context_ctc_zc1(DEVICE* device, int id, uint32_t mask)
+       {
+               d_ctc->set_context_zc1(device, id, mask);
+       }
+       void set_context_ctc_zc2(DEVICE* device, int id, uint32_t mask)
+       {
+               d_ctc->set_context_zc2(device, id, mask);
+       }
+       void set_context_ctc_zc3(DEVICE* device, int id, uint32_t mask)
+       {
+               d_ctc->set_context_zc3(device, id, mask);
+       }
+       void set_ctc_constant_clock(int ch, uint32_t hz)
+       {
+               d_ctc->set_constant_clock(ch, hz);
+       }
+       
+       // unique functions (from Z80SIO)
+       void set_context_sio_rts(int ch, DEVICE* device, int id, uint32_t mask)
+       {
+               d_sio->set_context_rts(ch, device, id, mask);
+       }
+       void set_context_sio_dtr(int ch, DEVICE* device, int id, uint32_t mask)
+       {
+               d_sio->set_context_dtr(ch, device, id, mask);
+       }
+       void set_context_sio_send(int ch, DEVICE* device, int id)
+       {
+               d_sio->set_context_send(ch, device, id);
+       }
+       void set_context_sio_sync(int ch, DEVICE* device, int id, uint32_t mask)
+       {
+               d_sio->set_context_sync(ch, device, id, mask);
+       }
+       void set_context_sio_break(int ch, DEVICE* device, int id, uint32_t mask)
+       {
+               d_sio->set_context_break(ch, device, id, mask);
+       }
+       void set_context_sio_rxdone(int ch, DEVICE* device, int id, uint32_t mask)
+       {
+               d_sio->set_context_rxdone(ch, device, id, mask);
+       }
+       void set_context_sio_txdone(int ch, DEVICE* device, int id, uint32_t mask)
+       {
+               d_sio->set_context_txdone(ch, device, id, mask);
+       }
+       void set_sio_tx_clock(int ch, double clock)
+       {
+               d_sio->set_tx_clock(ch, clock);
+       }
+       void set_sio_rx_clock(int ch, double clock)
+       {
+               d_sio->set_rx_clock(ch, clock);
+       }
+       
+#ifndef HAS_TMPZ84C013
+       // unique functions (from Z80PIO)
+       void set_context_pio_port_a(DEVICE* device, int id, uint32_t mask, int shift)
+       {
+               d_pio->set_context_port_a(device, id, mask, shift);
+       }
+       void set_context_pio_port_b(DEVICE* device, int id, uint32_t mask, int shift)
+       {
+               d_pio->set_context_port_b(device, id, mask, shift);
+       }
+       void set_context_pio_ready_a(DEVICE* device, int id, uint32_t mask)
+       {
+               d_pio->set_context_ready_a(device, id, mask);
+       }
+       void set_context_pio_ready_b(DEVICE* device, int id, uint32_t mask)
+       {
+               d_pio->set_context_ready_b(device, id, mask);
+       }
+       void set_pio_hand_shake(int ch, bool value)
+       {
+               d_pio->set_hand_shake(ch, value);
+       }
+#endif
+};
+
+#endif
+
diff --git a/source/src/vm/trnjr/display.cpp b/source/src/vm/trnjr/display.cpp
new file mode 100644 (file)
index 0000000..0a719c7
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+       EPS TRN Junior Emulator 'eTRNJunior'
+
+       Author : Takeda.Toshiya
+       Date   : 2022.07.02-
+
+       [ display / keyboard ]
+*/
+
+#include "display.h"
+#include "../i8255.h"
+
+static const int led_pattern[LED_SIZE_Y][LED_SIZE_X] = {
+       {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+       {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+       {0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0},
+       {0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,2,2,2,0,0,0,0},
+       {0,0,0,0,0,0,6,6,6,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,2,2,2,2,0,0,0,0},
+       {0,0,0,0,0,0,6,6,6,6,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,2,2,2,2,2,0,0,0,0},
+       {0,0,0,0,0,0,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,0,0,0,0},
+       {0,0,0,0,0,0,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,0,0,0,0},
+       {0,0,0,0,0,0,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,0,0,0,0},
+       {0,0,0,0,0,0,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,0,0,0,0},
+       {0,0,0,0,0,0,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,0,0,0,0},
+       {0,0,0,0,0,0,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,0,0,0,0},
+       {0,0,0,0,0,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,0,0,0,0,0},
+       {0,0,0,0,0,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,0,0,0,0,0},
+       {0,0,0,0,0,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,0,0,0,0,0},
+       {0,0,0,0,0,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,0,0,0,0,0},
+       {0,0,0,0,0,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,0,0,0,0,0},
+       {0,0,0,0,0,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,0,0,0,0,0},
+       {0,0,0,0,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,0,0,0,0,0,0},
+       {0,0,0,0,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,0,0,0,0,0,0},
+       {0,0,0,0,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,0,0,0,0,0,0},
+       {0,0,0,0,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,0,0,0,0,0,0},
+       {0,0,0,0,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,0,0,0,0,0,0},
+       {0,0,0,0,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,0,0,0,0,0,0},
+       {0,0,0,0,0,6,6,6,0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,0,2,2,2,0,0,0,0,0,0,0},
+       {0,0,0,0,0,0,0,0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0,0,0,0,0},
+       {0,0,0,0,0,0,0,0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0,0,0,0,0},
+       {0,0,0,0,0,5,5,5,0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,0,3,3,3,0,0,0,0,0,0,0},
+       {0,0,0,0,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,0,0,0,0,0,0},
+       {0,0,0,0,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,0,0,0,0,0,0},
+       {0,0,0,0,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,0,0,0,0,0,0},
+       {0,0,0,0,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,0,0,0,0,0,0},
+       {0,0,0,0,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,0,0,0,0,0,0},
+       {0,0,0,0,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,0,0,0,0,0,0},
+       {0,0,0,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,0,0,0,0,0,0,0},
+       {0,0,0,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,0,0,0,0,0,0,0},
+       {0,0,0,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,0,0,0,0,0,0,0},
+       {0,0,0,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,0,0,0,0,0,0,0},
+       {0,0,0,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,0,0,0,0,0,0,0},
+       {0,0,0,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,0,0,0,0,0,0,0},
+       {0,0,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,0,0,0,0,0,0,0,0},
+       {0,0,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,0,0,0,0,0,0,0,0},
+       {0,0,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,0,0,0,0,0,0,0,0},
+       {0,0,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,0,0,0,0,0,0,0,0},
+       {0,0,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,0,0,0,0,0,0,0,0},
+       {0,0,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,0,0,0,0,0,0,0,0},
+       {0,0,5,5,5,5,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,3,3,3,3,3,0,0,8,8,8,8,0,0},
+       {0,0,5,5,5,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,3,3,3,3,0,0,8,8,8,8,0,0},
+       {0,0,0,0,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,3,3,3,0,0,8,8,8,8,0,0},
+       {0,0,0,0,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,0,0,0,0,0,8,8,8,8,0,0},
+       {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+       {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+};
+
+void DISPLAY::initialize()
+{
+       memset(seg, 0, sizeof(seg));
+       pa = pc = 0;
+       
+       // register event
+       register_vline_event(this);
+}
+
+void DISPLAY::event_vline(int v, int clock)
+{
+       if(!v) {
+               memset(seg, 0, sizeof(seg));
+               update_keyboard();
+       }
+       if(!(pc & 0x80)) {
+               for(int i = 0; i < 8; i++) {
+                       if(pa & (1 << i)) {
+                               seg[(pc >> 4) & 7][i]++;
+                       }
+               }
+       }
+}
+
+void DISPLAY::write_signal(int id, uint32_t data, uint32_t mask)
+{
+       switch(id) {
+       case SIG_DISPLAY_PORT_A:
+               pa = (pa & ~mask) | (data & mask);
+               break;
+       case SIG_DISPLAY_PORT_C:
+               pc = (pc & ~mask) | (data & mask);
+               update_keyboard();
+               break;
+       }
+}
+
+void DISPLAY::draw_screen()
+{
+       // draw 7-seg LEDs
+       scrntype_t col_h, col_l;
+       scrntype_t col[9];
+       
+       col_h = RGB_COLOR(0, 255, 0);
+       col_l = RGB_COLOR(100, 100, 80);
+       col[0] = RGB_COLOR(57, 45, 37);
+       
+       for(int i = 0; i < 8; i++) {
+               for(int j = 0; j < 8; j++) {
+                       col[j + 1] = (seg[7 - i][j] > 8) ? col_h : col_l;
+               }
+               for(int y = 0; y < LED_SIZE_Y; y++) {
+                       scrntype_t* dest = emu->get_screen_buffer(vm_ranges[i].y + y) + vm_ranges[i].x;
+                       for(int x = 0; x < LED_SIZE_X; x++) {
+                               dest[x] = col[led_pattern[y][x]];
+                       }
+               }
+       }
+}
+
+void DISPLAY::update_keyboard()
+{
+       const uint8_t *key = emu->get_key_buffer();
+       uint8_t val = 0xff;
+       
+       switch(pc & 0xf0) {
+       case 0x00:
+               if(key[0x30]) val &= ~0x01;     // 0
+               if(key[0x31]) val &= ~0x02;     // 1
+               if(key[0x32]) val &= ~0x04;     // 2
+               if(key[0x33]) val &= ~0x08;     // 3
+               break;
+       case 0x10:
+               if(key[0x34]) val &= ~0x01;     // 4
+               if(key[0x35]) val &= ~0x02;     // 5
+               if(key[0x36]) val &= ~0x04;     // 6
+               if(key[0x37]) val &= ~0x08;     // 7
+               break;
+       case 0x20:
+               if(key[0x38]) val &= ~0x01;     // 8
+               if(key[0x39]) val &= ~0x02;     // 9
+               if(key[0x41]) val &= ~0x04;     // A
+               if(key[0x42]) val &= ~0x08;     // B
+               break;
+       case 0x30:
+               if(key[0x43]) val &= ~0x01;     // C
+               if(key[0x44]) val &= ~0x02;     // D
+               if(key[0x45]) val &= ~0x04;     // E
+               if(key[0x46]) val &= ~0x08;     // F
+               break;
+       case 0x40:
+               if(key[0x70]) val &= ~0x01;     // REG
+               if(key[0x71]) val &= ~0x02;     // INC
+               if(key[0x72]) val &= ~0x04;     // DEC
+               if(key[0x73]) val &= ~0x08;     // RUN
+               break;
+       case 0x50:
+               if(key[0x74]) val &= ~0x01;     // STEP
+               if(key[0x75]) val &= ~0x02;     // WRT
+               if(key[0x76]) val &= ~0x04;     // ADRS
+               if(key[0x77]) val &= ~0x08;     // SHFT
+               break;
+       }
+       d_pio->write_signal(SIG_I8255_PORT_C, val, 0x0f);
+}
+
+#define STATE_VERSION  1
+
+bool DISPLAY::process_state(FILEIO* state_fio, bool loading)
+{
+       if(!state_fio->StateCheckUint32(STATE_VERSION)) {
+               return false;
+       }
+       if(!state_fio->StateCheckInt32(this_device_id)) {
+               return false;
+       }
+       state_fio->StateArray(&seg[0][0], sizeof(seg), 1);
+       state_fio->StateValue(pa);
+       state_fio->StateValue(pc);
+       return true;
+}
+
diff --git a/source/src/vm/trnjr/display.h b/source/src/vm/trnjr/display.h
new file mode 100644 (file)
index 0000000..2e47984
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+       EPS TRN Junior Emulator 'eTRNJunior'
+
+       Author : Takeda.Toshiya
+       Date   : 2022.07.02-
+
+       [ display / keyboard ]
+*/
+
+#ifndef _DISPLAY_H_
+#define _DISPLAY_H_
+
+#include "../vm.h"
+#include "../../emu.h"
+#include "../device.h"
+
+#define SIG_DISPLAY_PORT_A     0
+#define SIG_DISPLAY_PORT_C     1
+
+class DISPLAY : public DEVICE
+{
+private:
+       DEVICE *d_pio;
+       
+       int seg[8][8];
+       uint8_t pa, pc;
+       
+       void update_keyboard();
+       
+public:
+       DISPLAY(VM_TEMPLATE* parent_vm, EMU* parent_emu) : DEVICE(parent_vm, parent_emu)
+       {
+               set_device_name(_T("Display/Keyboard"));
+       }
+       ~DISPLAY() {}
+       
+       // common functions
+       void initialize();
+       void write_signal(int id, uint32_t data, uint32_t mask);
+       void event_vline(int v, int clock);
+       bool process_state(FILEIO* state_fio, bool loading);
+       
+       // unique functions
+       void set_context_pio(DEVICE* device)
+       {
+               d_pio = device;
+       }
+       void draw_screen();
+};
+
+#endif
+
diff --git a/source/src/vm/trnjr/membus.cpp b/source/src/vm/trnjr/membus.cpp
new file mode 100644 (file)
index 0000000..3fe483f
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+       EPS TRN Junior Emulator 'eTRNJunior'
+
+       Author : Takeda.Toshiya
+       Date   : 2022.07.02-
+
+       [ memory bus ]
+*/
+
+#include "membus.h"
+#include "../tmpz84c015.h"
+
+uint32_t MEMBUS::fetch_op(uint32_t addr, int *wait)
+{
+       d_cpudev->write_signal(SIG_TMPZ84C015_CTC_TRIG_3, 1, 1);
+       d_cpudev->write_signal(SIG_TMPZ84C015_CTC_TRIG_3, 0, 1);
+       *wait = 0;
+       return MEMORY::read_data8(addr);
+}
diff --git a/source/src/vm/trnjr/membus.h b/source/src/vm/trnjr/membus.h
new file mode 100644 (file)
index 0000000..42fa159
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+       EPS TRN Junior Emulator 'eTRNJunior'
+
+       Author : Takeda.Toshiya
+       Date   : 2022.07.02-
+
+       [ memory bus ]
+*/
+
+#ifndef _MEMBUS_H_
+#define _MEMBUS_H_
+
+#include "../memory.h"
+
+class MEMBUS : public MEMORY
+{
+private:
+       DEVICE *d_cpudev;
+       
+public:
+       MEMBUS(VM_TEMPLATE* parent_vm, EMU* parent_emu) : MEMORY(parent_vm, parent_emu)
+       {
+               set_device_name(_T("Memory Bus"));
+       }
+       ~MEMBUS() {}
+       
+       // common function
+       uint32_t fetch_op(uint32_t addr, int *wait);
+       
+       // unique function
+       void set_context_cpudev(DEVICE* device)
+       {
+               d_cpudev = device;
+       }
+};
+
+#endif
diff --git a/source/src/vm/trnjr/trnjr.cpp b/source/src/vm/trnjr/trnjr.cpp
new file mode 100644 (file)
index 0000000..3fa81b4
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+       EPS TRN Junior Emulator 'eTRNJunior'
+
+       Author : Takeda.Toshiya
+       Date   : 2022.07.02-
+
+       [ virtual machine ]
+*/
+
+#include "trnjr.h"
+#include "../../emu.h"
+#include "../device.h"
+#include "../event.h"
+
+#include "../i8255.h"
+#include "../io.h"
+#include "../midi.h"
+#include "../speaker.h"
+#include "../tmpz84c015.h"
+#include "../z80.h"
+#include "../z80ctc.h"
+#include "../z80sio.h"
+
+#ifdef USE_DEBUGGER
+#include "../debugger.h"
+#endif
+
+#include "display.h"
+#include "membus.h"
+
+// ----------------------------------------------------------------------------
+// initialize
+// ----------------------------------------------------------------------------
+
+VM::VM(EMU* parent_emu) : VM_TEMPLATE(parent_emu)
+{
+       // create devices
+       first_device = last_device = NULL;
+       dummy = new DEVICE(this, emu);  // must be 1st device
+       event = new EVENT(this, emu);   // must be 2nd device
+       
+       pio1 = new I8255(this, emu);
+       pio1->set_device_name(_T("8255 PIO (7-Seg/Keyboard)"));
+       pio2 = new I8255(this, emu);
+       pio2->set_device_name(_T("8255 PIO (ROM Writer)"));
+       io = new IO(this, emu);
+       midi = new MIDI(this, emu);
+       speaker = new SPEAKER(this, emu);
+       // TMPZ84C013
+       cpudev = new TMPZ84C015(this, emu);
+       cpudev->set_context_ctc(new Z80CTC(this, emu));
+       cpudev->set_context_sio(new Z80SIO(this, emu));
+       cpu = new Z80(this, emu);
+       
+       display = new DISPLAY(this, emu);
+       memory = new MEMBUS(this, emu);
+       
+       // set contexts
+       event->set_context_cpu(cpu);
+       event->set_context_sound(speaker);
+       
+       cpudev->set_context_ctc_zc1(cpudev, SIG_TMPZ84C015_SIO_TX_CLK_CH0, 1);
+       cpudev->set_context_ctc_zc1(cpudev, SIG_TMPZ84C015_SIO_RX_CLK_CH0, 1);
+       cpudev->set_context_ctc_zc2(cpudev, SIG_TMPZ84C015_SIO_TX_CLK_CH1, 1);
+       cpudev->set_context_ctc_zc2(cpudev, SIG_TMPZ84C015_SIO_RX_CLK_CH1, 1);
+       cpudev->set_context_ctc_zc3(cpu, SIG_CPU_NMI, 1);
+       cpudev->set_context_sio_send(1, midi, SIG_MIDI_OUT);
+       pio1->set_context_port_a(display, SIG_DISPLAY_PORT_A, 0xff, 0);
+       pio1->set_context_port_b(speaker, SIG_SPEAKER_SAMPLE, 0xff, 0);
+       pio1->set_context_port_c(display, SIG_DISPLAY_PORT_C, 0xf0, 0);
+       midi->set_context_in(cpudev, SIG_TMPZ84C015_SIO_RECV_CH1, 0xff);
+       
+       display->set_context_pio(pio1);
+       memory->set_context_cpudev(cpudev);
+       
+       // cpu bus
+       cpu->set_context_mem(memory);
+       cpu->set_context_io(io);
+       cpu->set_context_intr(cpudev);
+#ifdef USE_DEBUGGER
+       cpu->set_context_debugger(new DEBUGGER(this, emu));
+#endif
+       
+       // z80 family daisy chain
+       cpudev->set_context_intr(cpu, 0);
+       
+       // memory bus
+       memset(ram, 0x00, sizeof(ram));
+       memset(rom, 0xff, sizeof(rom));
+       
+       memory->read_bios(_T("MON.ROM"), rom, sizeof(rom));
+       
+       memory->set_memory_r (0x0000, 0x7fff, rom);
+       memory->set_memory_rw(0x8000, 0xffff, ram);
+       
+       // i/o bus
+       cpudev->set_iomap(io);
+       io->set_iomap_range_rw(0x60, 0x63, pio1);
+       io->set_iomap_range_rw(0x64, 0x67, pio2);
+//     io->set_iomap_range_rw(0x6c, 0x6f, fdc ); // uPD72605
+       
+       // initialize all devices
+       for(DEVICE* device = first_device; device; device = device->next_device) {
+               device->initialize();
+       }
+}
+
+VM::~VM()
+{
+       // delete all devices
+       for(DEVICE* device = first_device; device;) {
+               DEVICE *next_device = device->next_device;
+               device->release();
+               delete device;
+               device = next_device;
+       }
+}
+
+DEVICE* VM::get_device(int id)
+{
+       for(DEVICE* device = first_device; device; device = device->next_device) {
+               if(device->this_device_id == id) {
+                       return device;
+               }
+       }
+       return NULL;
+}
+
+// ----------------------------------------------------------------------------
+// drive virtual machine
+// ----------------------------------------------------------------------------
+
+void VM::reset()
+{
+       // reset all devices
+       for(DEVICE* device = first_device; device; device = device->next_device) {
+               device->reset();
+       }
+}
+
+void VM::run()
+{
+       event->drive();
+}
+
+// ----------------------------------------------------------------------------
+// debugger
+// ----------------------------------------------------------------------------
+
+#ifdef USE_DEBUGGER
+DEVICE *VM::get_cpu(int index)
+{
+       if(index == 0) {
+               return cpu;
+       }
+       return NULL;
+}
+#endif
+
+// ----------------------------------------------------------------------------
+// draw screen
+// ----------------------------------------------------------------------------
+
+void VM::draw_screen()
+{
+       display->draw_screen();
+}
+
+// ----------------------------------------------------------------------------
+// soud manager
+// ----------------------------------------------------------------------------
+
+void VM::initialize_sound(int rate, int samples)
+{
+       // init sound manager
+       event->initialize_sound(rate, samples);
+       
+       // init sound gen
+       speaker->initialize_sound(rate, 8000);
+}
+
+uint16_t* VM::create_sound(int* extra_frames)
+{
+       return event->create_sound(extra_frames);
+}
+
+int VM::get_sound_buffer_ptr()
+{
+       return event->get_sound_buffer_ptr();
+}
+
+#ifdef USE_SOUND_VOLUME
+void VM::set_sound_device_volume(int ch, int decibel_l, int decibel_r)
+{
+       if(ch == 0) {
+               speaker->set_volume(0, decibel_l, decibel_r);
+       }
+}
+#endif
+
+// ----------------------------------------------------------------------------
+// notify key
+// ----------------------------------------------------------------------------
+
+void VM::key_down(int code, bool repeat)
+{
+}
+
+void VM::key_up(int code)
+{
+}
+
+// ----------------------------------------------------------------------------
+// user interface
+// ----------------------------------------------------------------------------
+
+void VM::load_binary(int drv, const _TCHAR* file_path)
+{
+       if(drv == 0) {
+               memory->read_image(file_path, ram, sizeof(ram));
+       }
+}
+
+void VM::save_binary(int drv, const _TCHAR* file_path)
+{
+       if(drv == 0) {
+               memory->write_image(file_path, ram, sizeof(ram));
+       }
+}
+
+bool VM::is_frame_skippable()
+{
+       return event->is_frame_skippable();
+}
+
+void VM::update_config()
+{
+       for(DEVICE* device = first_device; device; device = device->next_device) {
+               device->update_config();
+       }
+}
+
+#define STATE_VERSION  1
+
+bool VM::process_state(FILEIO* state_fio, bool loading)
+{
+       if(!state_fio->StateCheckUint32(STATE_VERSION)) {
+               return false;
+       }
+       for(DEVICE* device = first_device; device; device = device->next_device) {
+               const _TCHAR *name = char_to_tchar(typeid(*device).name() + 6); // skip "class "
+               int len = (int)_tcslen(name);
+               
+               if(!state_fio->StateCheckInt32(len)) {
+                       return false;
+               }
+               if(!state_fio->StateCheckBuffer(name, len, 1)) {
+                       return false;
+               }
+               if(!device->process_state(state_fio, loading)) {
+                       return false;
+               }
+       }
+       state_fio->StateArray(ram, sizeof(ram), 1);
+       return true;
+}
+
diff --git a/source/src/vm/trnjr/trnjr.h b/source/src/vm/trnjr/trnjr.h
new file mode 100644 (file)
index 0000000..c39d43d
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+       EPS TRN Junior Emulator 'eTRNJunior'
+
+       Author : Takeda.Toshiya
+       Date   : 2022.07.02-
+
+       [ virtual machine ]
+*/
+
+#ifndef _TRNJR_H_
+#define _TRNJR_H_
+
+#define DEVICE_NAME            "ESP TRN Junior"
+#define CONFIG_NAME            "trnjr"
+
+// device informations for virtual machine
+#define FRAMES_PER_SEC         60
+#define LINES_PER_FRAME        256
+#define CPU_CLOCKS             4000000
+#define SCREEN_WIDTH           768
+#define SCREEN_HEIGHT          512
+#define MEMORY_ADDR_MAX                0x10000
+#define MEMORY_BANK_SIZE       0x1000
+#define IO_ADDR_MAX            0x100
+#define HAS_TMPZ84C013
+
+// device informations for win32
+#define ONE_BOARD_MICRO_COMPUTER
+#define MAX_BUTTONS            25
+#define MAX_DRAW_RANGES                8
+#define USE_BINARY_FILE                1
+#define USE_MIDI
+#define USE_SOUND_VOLUME       1
+#define USE_DEBUGGER
+#define USE_STATE
+
+#include "../../common.h"
+#include "../../fileio.h"
+#include "../vm_template.h"
+
+#ifdef USE_SOUND_VOLUME
+static const _TCHAR *sound_device_caption[] = {
+       _T("Speaker"),
+};
+#endif
+
+#define BUTTON_SPACE_X 50
+#define BUTTON_SPACE_Y 52
+#define BUTTON_SIZE_X  37
+#define BUTTON_SIZE_Y  38
+#define BUTTON_POS_X   378
+#define BUTTON_POS_Y   165
+
+#define LED_SPACE_X    42
+#define LED_SIZE_X     38
+#define LED_SIZE_Y     52
+#define LED_POS_X1     269
+#define LED_POS_X2     449
+#define LED_POS_Y      81
+
+const struct {
+       int x, y;
+       int width, height;
+       int code;
+} vm_buttons[] = {
+       // virtual key codes 0x80-0x8f and 0x98-0x9f are not used in pc keyboard
+       {BUTTON_POS_X + BUTTON_SPACE_X * 0, BUTTON_POS_Y + BUTTON_SPACE_Y * 4, BUTTON_SIZE_X, BUTTON_SIZE_Y, 0x30},     // 0
+       {BUTTON_POS_X + BUTTON_SPACE_X * 1, BUTTON_POS_Y + BUTTON_SPACE_Y * 4, BUTTON_SIZE_X, BUTTON_SIZE_Y, 0x31},     // 1
+       {BUTTON_POS_X + BUTTON_SPACE_X * 2, BUTTON_POS_Y + BUTTON_SPACE_Y * 4, BUTTON_SIZE_X, BUTTON_SIZE_Y, 0x32},     // 2
+       {BUTTON_POS_X + BUTTON_SPACE_X * 3, BUTTON_POS_Y + BUTTON_SPACE_Y * 4, BUTTON_SIZE_X, BUTTON_SIZE_Y, 0x33},     // 3
+       {BUTTON_POS_X + BUTTON_SPACE_X * 0, BUTTON_POS_Y + BUTTON_SPACE_Y * 3, BUTTON_SIZE_X, BUTTON_SIZE_Y, 0x34},     // 4
+       {BUTTON_POS_X + BUTTON_SPACE_X * 1, BUTTON_POS_Y + BUTTON_SPACE_Y * 3, BUTTON_SIZE_X, BUTTON_SIZE_Y, 0x35},     // 5
+       {BUTTON_POS_X + BUTTON_SPACE_X * 2, BUTTON_POS_Y + BUTTON_SPACE_Y * 3, BUTTON_SIZE_X, BUTTON_SIZE_Y, 0x36},     // 6
+       {BUTTON_POS_X + BUTTON_SPACE_X * 3, BUTTON_POS_Y + BUTTON_SPACE_Y * 3, BUTTON_SIZE_X, BUTTON_SIZE_Y, 0x37},     // 7
+       {BUTTON_POS_X + BUTTON_SPACE_X * 0, BUTTON_POS_Y + BUTTON_SPACE_Y * 2, BUTTON_SIZE_X, BUTTON_SIZE_Y, 0x38},     // 8
+       {BUTTON_POS_X + BUTTON_SPACE_X * 1, BUTTON_POS_Y + BUTTON_SPACE_Y * 2, BUTTON_SIZE_X, BUTTON_SIZE_Y, 0x39},     // 9
+       {BUTTON_POS_X + BUTTON_SPACE_X * 2, BUTTON_POS_Y + BUTTON_SPACE_Y * 2, BUTTON_SIZE_X, BUTTON_SIZE_Y, 0x41},     // A
+       {BUTTON_POS_X + BUTTON_SPACE_X * 3, BUTTON_POS_Y + BUTTON_SPACE_Y * 2, BUTTON_SIZE_X, BUTTON_SIZE_Y, 0x42},     // B
+       {BUTTON_POS_X + BUTTON_SPACE_X * 0, BUTTON_POS_Y + BUTTON_SPACE_Y * 1, BUTTON_SIZE_X, BUTTON_SIZE_Y, 0x43},     // C
+       {BUTTON_POS_X + BUTTON_SPACE_X * 1, BUTTON_POS_Y + BUTTON_SPACE_Y * 1, BUTTON_SIZE_X, BUTTON_SIZE_Y, 0x44},     // D
+       {BUTTON_POS_X + BUTTON_SPACE_X * 2, BUTTON_POS_Y + BUTTON_SPACE_Y * 1, BUTTON_SIZE_X, BUTTON_SIZE_Y, 0x45},     // E
+       {BUTTON_POS_X + BUTTON_SPACE_X * 3, BUTTON_POS_Y + BUTTON_SPACE_Y * 1, BUTTON_SIZE_X, BUTTON_SIZE_Y, 0x46},     // F
+       {BUTTON_POS_X + BUTTON_SPACE_X * 0, BUTTON_POS_Y + BUTTON_SPACE_Y * 0, BUTTON_SIZE_X, BUTTON_SIZE_Y, 0x70},     // REG
+       {BUTTON_POS_X + BUTTON_SPACE_X * 1, BUTTON_POS_Y + BUTTON_SPACE_Y * 0, BUTTON_SIZE_X, BUTTON_SIZE_Y, 0x71},     // INC
+       {BUTTON_POS_X + BUTTON_SPACE_X * 2, BUTTON_POS_Y + BUTTON_SPACE_Y * 0, BUTTON_SIZE_X, BUTTON_SIZE_Y, 0x72},     // DEC
+       {BUTTON_POS_X + BUTTON_SPACE_X * 3, BUTTON_POS_Y + BUTTON_SPACE_Y * 0, BUTTON_SIZE_X, BUTTON_SIZE_Y, 0x73},     // RUN
+       {BUTTON_POS_X + BUTTON_SPACE_X * 4, BUTTON_POS_Y + BUTTON_SPACE_Y * 4, BUTTON_SIZE_X, BUTTON_SIZE_Y, 0x74},     // STEP
+       {BUTTON_POS_X + BUTTON_SPACE_X * 4, BUTTON_POS_Y + BUTTON_SPACE_Y * 3, BUTTON_SIZE_X, BUTTON_SIZE_Y, 0x75},     // WRT
+       {BUTTON_POS_X + BUTTON_SPACE_X * 4, BUTTON_POS_Y + BUTTON_SPACE_Y * 2, BUTTON_SIZE_X, BUTTON_SIZE_Y, 0x76},     // ADRS
+       {BUTTON_POS_X + BUTTON_SPACE_X * 4, BUTTON_POS_Y + BUTTON_SPACE_Y * 1, BUTTON_SIZE_X, BUTTON_SIZE_Y, 0x77},     // SHFT
+       {BUTTON_POS_X + BUTTON_SPACE_X * 4, BUTTON_POS_Y + BUTTON_SPACE_Y * 0, BUTTON_SIZE_X, BUTTON_SIZE_Y, 0x00},     // RES
+};
+const struct {
+       int x, y;
+       int width, height;
+} vm_ranges[] = {
+       {LED_POS_X1 + LED_SPACE_X * 0, LED_POS_Y, LED_SIZE_X, LED_SIZE_Y}, // 7-seg LEDs
+       {LED_POS_X1 + LED_SPACE_X * 1, LED_POS_Y, LED_SIZE_X, LED_SIZE_Y},
+       {LED_POS_X1 + LED_SPACE_X * 2, LED_POS_Y, LED_SIZE_X, LED_SIZE_Y},
+       {LED_POS_X1 + LED_SPACE_X * 3, LED_POS_Y, LED_SIZE_X, LED_SIZE_Y},
+       {LED_POS_X2 + LED_SPACE_X * 0, LED_POS_Y, LED_SIZE_X, LED_SIZE_Y},
+       {LED_POS_X2 + LED_SPACE_X * 1, LED_POS_Y, LED_SIZE_X, LED_SIZE_Y},
+       {LED_POS_X2 + LED_SPACE_X * 2, LED_POS_Y, LED_SIZE_X, LED_SIZE_Y},
+       {LED_POS_X2 + LED_SPACE_X * 3, LED_POS_Y, LED_SIZE_X, LED_SIZE_Y},
+};
+
+class EMU;
+class DEVICE;
+class EVENT;
+
+class I8255;
+class IO;
+class MIDI;
+class SPEAKER;
+class TMPZ84C015;
+class Z80;
+
+class DISPLAY;
+class MEMBUS;
+
+class VM : public VM_TEMPLATE
+{
+protected:
+//     EMU* emu;
+       
+       // devices
+       EVENT* event;
+       
+       I8255* pio1;
+       I8255* pio2;
+       IO* io;
+       MIDI* midi;
+       SPEAKER* speaker;
+       TMPZ84C015* cpudev;
+       Z80* cpu;
+       
+       DISPLAY* display;
+       MEMBUS* memory;
+       
+       // memory
+       uint8_t rom[0x8000];
+       uint8_t ram[0x8000];
+       
+public:
+       // ----------------------------------------
+       // initialize
+       // ----------------------------------------
+       
+       VM(EMU* parent_emu);
+       ~VM();
+       
+       // ----------------------------------------
+       // for emulation class
+       // ----------------------------------------
+       
+       // drive virtual machine
+       void reset();
+       void run();
+       double get_frame_rate()
+       {
+               return FRAMES_PER_SEC;
+       }
+       
+#ifdef USE_DEBUGGER
+       // debugger
+       DEVICE *get_cpu(int index);
+#endif
+       
+       // draw screen
+       void draw_screen();
+       
+       // sound generation
+       void initialize_sound(int rate, int samples);
+       uint16_t* create_sound(int* extra_frames);
+       int get_sound_buffer_ptr();
+#ifdef USE_SOUND_VOLUME
+       void set_sound_device_volume(int ch, int decibel_l, int decibel_r);
+#endif
+       
+       // notify key
+       void key_down(int code, bool repeat);
+       void key_up(int code);
+       
+       // user interface
+       void load_binary(int drv, const _TCHAR* file_path);
+       void save_binary(int drv, const _TCHAR* file_path);
+       bool is_frame_skippable();
+       
+       void update_config();
+       bool process_state(FILEIO* state_fio, bool loading);
+       
+       // ----------------------------------------
+       // for each device
+       // ----------------------------------------
+       
+       // devices
+       DEVICE* get_device(int id);
+//     DEVICE* dummy;
+//     DEVICE* first_device;
+//     DEVICE* last_device;
+};
+
+#endif
index 7da84b2..89ed968 100644 (file)
 #if defined(_FMTOWNS2_HR20) || defined(_FMTOWNS2_HR100) || defined(_FMTOWNS2_HR200)
 #include "fmtowns/fmtowns.h"
 #endif
+
 // CASIO FP-200
 #ifdef _FP200
 #include "fp200/fp200.h"
 #include "m5/m5.h"
 #endif
 
+// SORD M68
+#ifdef _M68
+#include "m23/m23.h"
+#endif
+
 // SEIKO MAP-1010
 #ifdef _MAP1010
 #include "phc25/phc25.h"
 #include "tk80bs/tk80bs.h"
 #endif
 
+// ESP TRN Junior
+#ifdef _TRNJR
+#include "trnjr/trnjr.h"
+#endif
+
 // GAKKEN TV BOY
 #ifdef _TVBOY
 #include "tvboy/tvboy.h"
 #if defined(USE_FLOPPY_DISK) && !defined(BASE_FLOPPY_DISK_NUM)
        #define BASE_FLOPPY_DISK_NUM    1
 #endif
-
 #if defined(USE_QUICK_DISK) && !defined(BASE_QUICK_DISK_NUM)
        #define BASE_QUICK_DISK_NUM     1
 #endif
 #if defined(USE_HARD_DISK) && !defined(BASE_HARD_DISK_NUM)
        #define BASE_HARD_DISK_NUM      1
 #endif
+#if defined(USE_TAPE) && !defined(BASE_TAPE_NUM)
+       #define BASE_TAPE_NUM           1
+#endif
 #if defined(USE_COMPACT_DISC) && !defined(BASE_COMPACT_DISC_NUM)
        #define BASE_COMPACT_DISC_NUM   1
 #endif
 #if defined(USE_BUBBLE) && !defined(BASE_BUBBLE_NUM)
        #define BASE_BUBBLE_NUM         1
 #endif
-#if defined(USE_TAPE) && !defined(BASE_TAPE_NUM)
-        #define BASE_TAPE_NUM           1
-#endif
 
 #ifndef KEY_KEEP_FRAMES
        #define KEY_KEEP_FRAMES 3
index 915e5e3..7f4f7e3 100644 (file)
@@ -29,6 +29,9 @@ void OSD::initialize(int rate, int samples)
 #ifdef USE_SOCKET
        initialize_socket();
 #endif
+#ifdef USE_MIDI
+       initialize_midi();
+#endif
 }
 
 void OSD::release()
@@ -44,6 +47,9 @@ void OSD::release()
 #ifdef USE_SOCKET
        release_socket();
 #endif
+#ifdef USE_MIDI
+       release_midi();
+#endif
        GdiplusShutdown(gdiToken);
 }
 
index fb174dc..6b01a61 100644 (file)
@@ -196,6 +196,9 @@ public:
 
 // osd common
 
+class FIFO;
+class FILEIO;
+
 #define OSD_CONSOLE_BLUE       1 // text color contains blue
 #define OSD_CONSOLE_GREEN      2 // text color contains green
 #define OSD_CONSOLE_RED                4 // text color contains red
@@ -255,8 +258,13 @@ typedef struct {
        int result;
 } rec_video_thread_param_t;
 
-class FIFO;
-class FILEIO;
+#ifdef USE_MIDI
+typedef struct midi_thread_params_s {
+       FIFO *send_buffer;
+       FIFO *recv_buffer;
+       bool terminate;
+} midi_thread_params_t;
+#endif
 
 class OSD
 {
@@ -490,6 +498,15 @@ private:
        int recv_r_ptr[SOCKET_MAX], recv_w_ptr[SOCKET_MAX];
 #endif
        
+       //midi
+#ifdef USE_MIDI
+       void initialize_midi();
+       void release_midi();
+       
+       HANDLE hMidiThread;
+       midi_thread_params_t midi_thread_params;
+#endif
+       
 public:
        OSD()
        {
@@ -558,28 +575,14 @@ public:
 #ifdef USE_JOYSTICK
        uint32_t* get_joy_buffer()
        {
-               // ToDo: Lock
                return joy_status;
        }
-       void release_joy_buffer(uint32_t* ptr)
-       {
-               // ToDo: Unlock
-       }
 #endif
 #ifdef USE_MOUSE
        int32_t* get_mouse_buffer()
        {
-               // ToDo: Lock
                return mouse_status;
        }
-       void release_mouse_buffer(int32_t* ptr)
-       {
-               // ToDo: Unlock
-       }
-       int32_t get_mouse_button()
-       {
-               return mouse_status[2];
-       }
 #endif
 #ifdef USE_AUTO_KEY
        bool now_auto_key;
@@ -720,6 +723,12 @@ public:
        void recv_socket_data(int ch);
 #endif
        
+       // common midi
+#ifdef USE_MIDI
+       void send_to_midi(uint8_t data);
+       bool recv_from_midi(uint8_t *data);
+#endif
+       
        // win32 dependent
        void invalidate_screen();
        void update_screen(HDC hdc);
index c540571..a49020f 100644 (file)
@@ -365,7 +365,7 @@ void OSD::open_telnet(const _TCHAR* title)
 void OSD::close_telnet()
 {
        if(svr_socket != INVALID_SOCKET) {
-               shutdown(svr_socket , /*SD_BOTH*/2);
+               shutdown(svr_socket, /*SD_BOTH*/2);
                closesocket(svr_socket);
                svr_socket = cli_socket = INVALID_SOCKET;
        }
diff --git a/source/src/win32/osd_midi.cpp b/source/src/win32/osd_midi.cpp
new file mode 100644 (file)
index 0000000..1d09f5c
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+       Skelton for retropc emulator
+
+       Author : Takeda.Toshiya
+       Date   : 2022.07.03-
+
+       [ win32 midi ]
+*/
+
+#include "osd.h"
+#include "../fifo.h"
+
+CRITICAL_SECTION send_cs;
+CRITICAL_SECTION recv_cs;
+
+unsigned __stdcall midi_thread(void *lpx)
+{
+       volatile midi_thread_params_t *p = (midi_thread_params_t *)lpx;
+       HMIDIOUT hMidi = NULL;
+       MMRESULT result = midiOutOpen(&hMidi, MIDI_MAPPER, NULL, NULL, CALLBACK_NULL);
+       
+       while(!p->terminate) {
+               while(true) {
+                       uint8_t buffer[128];
+                       int length = 0;
+                       
+                       EnterCriticalSection(&send_cs);
+                       if(!p->send_buffer->empty()) {
+                               uint8_t msg = p->send_buffer->read_not_remove(0);
+                               
+                               switch(msg & 0xf0) {
+                               case 0x80: case 0x90: case 0xa0: case 0xb0: case 0xe0:
+                                       length = 3;
+                                       break;
+                               case 0xc0:
+                               case 0xd0:
+                                       length = 2;
+                                       break;
+                               case 0xf0:
+                                       switch(msg) {
+                                       case 0xf0:
+                                               // system exclusive
+                                               for(int i = 1; i < p->send_buffer->count(); i++) {
+                                                       if(p->send_buffer->read_not_remove(i) == 0xf7) {
+                                                               length = i + 1;
+                                                               break;
+                                                       }
+                                               }
+                                               break;
+                                       case 0xf1: case 0xf3:
+                                               length = 2;
+                                               break;
+                                       case 0xf2:
+                                               length = 3;
+                                               break;
+                                       case 0xf6: case 0xf8: case 0xfa: case 0xfb: case 0xfc: case 0xfe: case 0xff:
+                                               length = 1;
+                                               break;
+                                       default:
+                                               // undefined msg
+                                               p->send_buffer->read();
+                                               break;
+                                       }
+                                       break;
+                               default:
+                                       // invalid msg
+                                       p->send_buffer->read();
+                                       break;
+                               }
+                               if(p->send_buffer->count() >= length) {
+                                       for(int i = 0; i < length; i++) {
+                                               buffer[i] = p->send_buffer->read();
+                                       }
+                               } else {
+                                       length = 0;
+                               }
+                       }
+                       LeaveCriticalSection(&send_cs);
+                       
+                       if(length > 0) {
+                               if(result == MMSYSERR_NOERROR) {
+                                       if(buffer[0] == 0xf0) {
+                                               // system exclusive
+                                               MIDIHDR mhMidi;
+                                               
+                                               ZeroMemory(&mhMidi, sizeof(mhMidi));
+                                               mhMidi.lpData = (LPSTR)buffer;
+                                               mhMidi.dwBufferLength = length;
+                                               mhMidi.dwBytesRecorded = length;
+                                               midiOutPrepareHeader(hMidi, &mhMidi, sizeof(mhMidi));
+                                               midiOutLongMsg(hMidi, &mhMidi, sizeof(mhMidi));
+                                               while(!(mhMidi.dwFlags & MHDR_DONE)) {
+                                                       Sleep(10);
+                                               }
+                                               midiOutUnprepareHeader(hMidi, &mhMidi, sizeof(mhMidi));
+                                       } else {
+                                               union UNION_MIDI_DATA {
+                                                       DWORD msg;
+                                                       BYTE data[4];
+                                               };
+                                               UNION_MIDI_DATA out;
+                                               
+                                               for(int i = 0; i < 4; i++) {
+                                                       out.data[i] = (i < length) ? buffer[i] : 0;
+                                               }
+                                               midiOutShortMsg(hMidi,out.msg);
+                                       }
+                               }
+                       } else {
+                               break;
+                       }
+               }
+               Sleep(10);
+       }
+       if(result == MMSYSERR_NOERROR) {
+               midiOutClose(hMidi);
+       }
+       _endthreadex(0);
+       return 0;
+}
+
+void OSD::initialize_midi()
+{
+       midi_thread_params.send_buffer = new FIFO(1024);
+       midi_thread_params.recv_buffer = new FIFO(1024);
+       midi_thread_params.terminate = false;
+       
+       InitializeCriticalSection(&send_cs);
+       InitializeCriticalSection(&recv_cs);
+       hMidiThread = (HANDLE)_beginthreadex(NULL, 0, midi_thread, &midi_thread_params, 0, NULL);
+}
+
+void OSD::release_midi()
+{
+       if(hMidiThread) {
+               midi_thread_params.terminate = true;
+               WaitForSingleObject(hMidiThread, INFINITE);
+               hMidiThread = NULL;
+       }
+       DeleteCriticalSection(&send_cs);
+       DeleteCriticalSection(&recv_cs);
+       
+       midi_thread_params.send_buffer->release();
+       delete midi_thread_params.send_buffer;
+       midi_thread_params.send_buffer = NULL;
+       midi_thread_params.recv_buffer->release();
+       delete midi_thread_params.recv_buffer;
+       midi_thread_params.recv_buffer = NULL;
+}
+
+void OSD::send_to_midi(uint8_t data)
+{
+       EnterCriticalSection(&send_cs);
+       midi_thread_params.send_buffer->write(data);
+       LeaveCriticalSection(&send_cs);
+}
+
+bool OSD::recv_from_midi(uint8_t *data)
+{
+       bool result = false;
+       EnterCriticalSection(&recv_cs);
+       if(!midi_thread_params.recv_buffer->empty()) {
+               *data = (uint8_t)midi_thread_params.recv_buffer->read();
+               result = true;
+       }
+       LeaveCriticalSection(&recv_cs);
+       return result;
+}