やっとこマークIIIの起動ロゴが出た。I/O周りを実装したのでPSGは鳴っています。引き続きVDPを実装して行きます。
eSC-3000がベースになっているので、sc3000フォルダをgamegearフォルダにコピーすることから始めます。アイコンは以下のものを使わせて頂きました。
http://www.ix.sakura.ne.jp/~yoshi/icon/game/
gamegear.cpp
/* SEGA GAME GEAR Emulator 'yaGAME GEAR' Author : tanam Date : 2013.08.24- [ virtual machine ] */ #include "gamegear.h" #include "../../emu.h" #include "../device.h" #include "../event.h" #include "../datarec.h" #include "../disk.h" #include "../i8251.h" #include "../i8255.h" #include "../io.h" #include "../sn76489an.h" #include "../315-5124.h" #include "../upd765a.h" #include "../z80.h" #include "keyboard.h" #include "memory.h" #include "system.h" // ---------------------------------------------------------------------------- // initialize // ---------------------------------------------------------------------------- VM::VM(EMU* parent_emu) : emu(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 drec = new DATAREC(this, emu); sio = new I8251(this, emu); pio_k = new I8255(this, emu); pio_f = new I8255(this, emu); io = new IO(this, emu); psg = new SN76489AN(this, emu); vdp = new _315_5124(this, emu); fdc = new UPD765A(this, emu); cpu = new Z80(this, emu); key = new KEYBOARD(this, emu); memory = new MEMORY(this, emu); system = new SYSTEM(this, emu); // set contexts event->set_context_cpu(cpu); event->set_context_sound(psg); drec->set_context_out(pio_k, SIG_I8255_PORT_B, 0x80); pio_k->set_context_port_c(key, SIG_KEYBOARD_COLUMN, 0x07, 0); pio_k->set_context_port_c(drec, SIG_DATAREC_REMOTE, 0x08, 0); pio_k->set_context_port_c(drec, SIG_DATAREC_OUT, 0x10, 0); pio_f->set_context_port_c(fdc, SIG_UPD765A_MOTOR_NEG, 2, 0); pio_f->set_context_port_c(fdc, SIG_UPD765A_TC, 4, 0); pio_f->set_context_port_c(fdc, SIG_UPD765A_RESET, 8, 0); pio_f->set_context_port_c(memory, SIG_MEMORY_SEL, 0x40, 0); vdp->set_context_irq(cpu, SIG_CPU_IRQ, 1); fdc->set_context_irq(pio_f, SIG_I8255_PORT_A, 1); fdc->set_context_index(pio_f, SIG_I8255_PORT_A, 4); #ifdef _FDC_DEBUG_LOG fdc->set_context_cpu(cpu); #endif psg->set_context_vdp(vdp); key->set_context_cpu(cpu); key->set_context_pio(pio_k); system->set_context_key(key); vdp->set_context_key(key); // cpu bus cpu->set_context_mem(memory); cpu->set_context_io(io); cpu->set_context_intr(dummy); // i/o bus io->set_iomap_range_rw(0x00, 0x00, system); io->set_iomap_range_rw(0x7e, 0x7f, psg); io->set_iomap_range_rw(0xbe, 0xbf, vdp); io->set_iomap_range_rw(0xdc, 0xdf, pio_k); io->set_iomap_range_rw(0xe0, 0xe3, fdc); io->set_iomap_range_rw(0xe4, 0xe7, pio_f); io->set_iomap_range_rw(0xe8, 0xe9, sio); // initialize all devices for(DEVICE* device = first_device; device; device = device->next_device) { device->initialize(); } for(int i = 0; i < 4; i++) { fdc->set_drive_type(i, DRIVE_TYPE_2D); } } 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(); } // ---------------------------------------------------------------------------- // draw screen // ---------------------------------------------------------------------------- void VM::draw_screen() { vdp->draw_screen(); } int VM::access_lamp() { uint32 status = fdc->read_signal(0); return (status & (1 | 4)) ? 1 : (status & (2 | 8)) ? 2 : 0; } // ---------------------------------------------------------------------------- // soud manager // ---------------------------------------------------------------------------- void VM::initialize_sound(int rate, int samples) { // init sound manager event->initialize_sound(rate, samples); // init sound gen psg->init(rate, 3579545, 8000); } uint16* VM::create_sound(int* extra_frames) { return event->create_sound(extra_frames); } int VM::sound_buffer_ptr() { return event->sound_buffer_ptr(); } // ---------------------------------------------------------------------------- // user interface // ---------------------------------------------------------------------------- void VM::open_cart(int drv, _TCHAR* file_path) { if(drv == 0) { memory->open_cart(file_path); if (strstr(file_path, ".gg") || strstr(file_path, ".GG")) vdp->set_console(true); else vdp->set_console(false); reset(); } } void VM::close_cart(int drv) { if(drv == 0) { memory->close_cart(); reset(); } } bool VM::cart_inserted(int drv) { if(drv == 0) { return memory->cart_inserted(); } else { return false; } } void VM::open_disk(int drv, _TCHAR* file_path, int offset) { fdc->open_disk(drv, file_path, offset); } void VM::close_disk(int drv) { fdc->close_disk(drv); } bool VM::disk_inserted(int drv) { return fdc->disk_inserted(drv); } void VM::play_tape(_TCHAR* file_path) { drec->play_tape(file_path); } void VM::rec_tape(_TCHAR* file_path) { drec->rec_tape(file_path); } void VM::close_tape() { drec->close_tape(); } bool VM::tape_inserted() { return drec->tape_inserted(); } bool VM::now_skip() { return event->now_skip(); } void VM::update_config() { for(DEVICE* device = first_device; device; device = device->next_device) { device->update_config(); } }
gamegear.h
/* SEGA GAME GEAR Emulator 'yaGAME GEAR' Author : tanam Date : 2013.08.24- [ virtual machine ] */ #ifndef _GAMEGEAR_H_ #define _GAMEGEAR_H_ #define DEVICE_NAME "SEGA GAME GEAR" #define CONFIG_NAME "gamegear" // device informations for virtual machine #define FRAMES_PER_SEC 60 #define LINES_PER_FRAME 262 #define CPU_CLOCKS 3579545 #define SCREEN_WIDTH 256 #define SCREEN_HEIGHT 192 #define TMS9918A_VRAM_SIZE 0x4000 #define TMS9918A_LIMIT_SPRITES #define MAX_DRIVE 4 // device informations for win32 #define MIN_WINDOW_WIDTH 320 #define USE_CART1 #define USE_FD1 #define USE_TAPE #define USE_ALT_F10_KEY #define USE_AUTO_KEY 5 #define USE_AUTO_KEY_RELEASE 8 #define USE_AUTO_KEY_CAPS #define USE_ACCESS_LAMP #include "../../common.h" class EMU; class DEVICE; class EVENT; class DATAREC; class I8251; class I8255; class IO; class SN76489AN; class _315_5124; class UPD765A; class Z80; class KEYBOARD; class MEMORY; class SYSTEM; class VM { protected: EMU* emu; // devices EVENT* event; DATAREC* drec; I8251* sio; I8255* pio_k; I8255* pio_f; IO* io; SN76489AN* psg; _315_5124* vdp; UPD765A* fdc; Z80* cpu; KEYBOARD* key; MEMORY* memory; SYSTEM* system; public: // ---------------------------------------- // initialize // ---------------------------------------- VM(EMU* parent_emu); ~VM(); // ---------------------------------------- // for emulation class // ---------------------------------------- // drive virtual machine void reset(); void run(); // draw screen void draw_screen(); int access_lamp(); // sound generation void initialize_sound(int rate, int samples); uint16* create_sound(int* extra_frames); int sound_buffer_ptr(); // user interface void open_cart(int drv, _TCHAR* file_path); void close_cart(int drv); bool cart_inserted(int drv); void open_disk(int drv, _TCHAR* file_path, int offset); void close_disk(int drv); bool disk_inserted(int drv); void play_tape(_TCHAR* file_path); void rec_tape(_TCHAR* file_path); void close_tape(); bool tape_inserted(); bool now_skip(); void update_config(); // ---------------------------------------- // for each device // ---------------------------------------- // devices DEVICE* get_device(int id); DEVICE* dummy; DEVICE* first_device; DEVICE* last_device; }; #endif
sn76489an.cpp
/* Skelton for retropc emulator Author : Takeda.Toshiya Date : 2006.08.18 - [ SN76489AN ] */ #include "sn76489an.h" #ifdef _GAMEGEAR #include "315-5124.h" #endif #ifdef HAS_SN76489 // SN76489 #define NOISE_FB 0x4000 #define NOISE_DST_TAP 1 #define NOISE_SRC_TAP 2 #else // SN76489A, SN76496 #define NOISE_FB 0x10000 #define NOISE_DST_TAP 4 #define NOISE_SRC_TAP 8 #endif #define NOISE_MODE ((regs[6] & 4) ? 1 : 0) void SN76489AN::initialize() { mute = false; cs = we = true; } void SN76489AN::reset() { for(int i = 0; i < 4; i++) { ch[i].count = 0; ch[i].period = 1; ch[i].volume = 0; ch[i].signal = false; } for(int i = 0; i < 8; i += 2) { regs[i + 0] = 0; regs[i + 1] = 0x0f; // volume = 0 } noise_gen = NOISE_FB; ch[3].signal = false; } void SN76489AN::write_io8(uint32 addr, uint32 data) { if(data & 0x80) { index = (data >> 4) & 7; int c = index >> 1; switch(index & 7) { case 0: case 2: case 4: // tone : frequency regs[index] = (regs[index] & 0x3f0) | (data & 0x0f); ch[c].period = regs[index] ? regs[index] : 0x400; // ch[c].count = 0; break; case 1: case 3: case 5: case 7: // tone / noise : volume regs[index] = data & 0x0f; ch[c].volume = volume_table[data & 0x0f]; break; case 6: // noise : frequency, mode regs[6] = data; data &= 3; ch[3].period = (data == 3) ? (ch[2].period << 1) : (1 << (data + 5)); // ch[3].count = 0; noise_gen = NOISE_FB; ch[3].signal = false; break; } } else { int c = index >> 1; switch(index & 0x07) { case 0: case 2: case 4: // tone : frequency regs[index] = (regs[index] & 0x0f) | (((uint16)data << 4) & 0x3f0); ch[c].period = regs[index] ? regs[index] : 0x400; // ch[c].count = 0; // update noise shift frequency if(index == 4 && (regs[6] & 3) == 3) { ch[3].period = ch[2].period << 1; } break; } } } void SN76489AN::write_signal(int id, uint32 data, uint32 mask) { if(id == SIG_SN76489AN_MUTE) { mute = ((data & mask) != 0); } else if(id == SIG_SN76489AN_DATA) { val = data & mask; } else if(id == SIG_SN76489AN_CS) { bool next = ((data & mask) != 0); if(cs != next) { if(!(cs = next) && !we) { write_io8(0, val); } } } else if(id == SIG_SN76489AN_CS) { bool next = ((data & mask) != 0); if(cs != next) { cs = next; if(!cs && !we) { write_io8(0, val); } } } else if(id == SIG_SN76489AN_WE) { bool next = ((data & mask) != 0); if(we != next) { we = next; if(!cs && !we) { write_io8(0, val); } } } } void SN76489AN::mix(int32* buffer, int cnt) { if(mute) { return; } for(int i = 0; i < cnt; i++) { int32 vol = 0; for(int j = 0; j < 4; j++) { if(!ch[j].volume) { continue; } ch[j].count -= diff; if(ch[j].count < 0) { ch[j].count += ch[j].period << 8; if(j == 3) { if(((noise_gen & NOISE_DST_TAP) ? 1 : 0) ^ (((noise_gen & NOISE_SRC_TAP) ? 1 : 0) * NOISE_MODE)) { noise_gen >>= 1; noise_gen |= NOISE_FB; } else { noise_gen >>= 1; } ch[3].signal = ((noise_gen & 1) != 0); } else { ch[j].signal = !ch[j].signal; } } vol += ch[j].signal ? ch[j].volume : -ch[j].volume; } *buffer++ += vol; // L *buffer++ += vol; // R } } void SN76489AN::init(int rate, int clock, int volume) { // create gain double vol = volume; for(int i = 0; i < 15; i++) { volume_table[i] = (int)vol; vol /= 1.258925412; } volume_table[15] = 0; diff = 16 * clock / rate; } #ifdef _GAMEGEAR uint32 SN76489AN::read_io8(uint32 addr) { if (addr & 1) { return ((_315_5124 *)d_vdp)->hpos(); } else { return ((_315_5124 *)d_vdp)->vpos(); } } #endif
sn76489an.h
/* Skelton for retropc emulator Author : Takeda.Toshiya Date : 2006.08.18 - [ SN76489AN ] */ #ifndef _SN76489AN_H_ #define _SN76489AN_H_ #include "vm.h" #include "../emu.h" #include "device.h" #define SIG_SN76489AN_MUTE 0 #define SIG_SN76489AN_DATA 1 #define SIG_SN76489AN_CS 2 #define SIG_SN76489AN_WE 3 class SN76489AN : public DEVICE { private: // register uint16 regs[8]; int index; // sound info typedef struct { int count; int period; int volume; bool signal; } channel_t; channel_t ch[4]; uint32 noise_gen; int volume_table[16]; int diff; bool mute, cs, we; uint8 val; #ifdef _GAMEGEAR DEVICE *d_vdp; #endif public: SN76489AN(VM* parent_vm, EMU* parent_emu) : DEVICE(parent_vm, parent_emu) {} ~SN76489AN() {} // common functions void initialize(); void reset(); void write_io8(uint32 addr, uint32 data); void write_signal(int id, uint32 data, uint32 mask); void mix(int32* buffer, int cnt); // unique function void init(int rate, int clock, int volume); #ifdef _GAMEGEAR void set_context_vdp(DEVICE* device) { d_vdp = device; } uint32 read_io8(uint32 addr); #endif }; #endif