/********************************************************************** Commodore 64H156 Gate Array emulation Copyright MESS Team. Visit http://mamedev.org for licensing and usage restrictions. **********************************************************************/ /* TODO: http://staff.washington.edu/rrcc/uwweb/1541early/1540-2.GIF - model disk rotation for proper track alignment - write circuitry - cycle exact M6502 - cycle exact VIA - refactor to use modern floppy system - get these running and we're golden - Quiwi (speed change within track) - Defender of the Crown (V-MAX! v2, density checks) - Test Drive / Cabal (HLS, sub-cycle jitter) - Galaxian (?, needs 100% accurate VIA) */ #include "emu.h" #include "64h156.h" #include "formats/g64_dsk.h" //************************************************************************** // MACROS / CONSTANTS //************************************************************************** #define LOG 0 #define ATN (m_atni ^ m_atna) #define CYCLES_UNTIL_ANALOG_DESYNC 288 // 18 us //************************************************************************** // DEVICE DEFINITIONS //************************************************************************** const device_type C64H156 = &device_creator; //------------------------------------------------- // device_config_complete - perform any // operations now that the configuration is // complete //------------------------------------------------- void c64h156_device::device_config_complete() { // inherit a copy of the static data const c64h156_interface *intf = reinterpret_cast(static_config()); if (intf != NULL) *static_cast(this) = *intf; // or initialize to defaults if none provided else { memset(&m_out_atn_cb, 0, sizeof(m_out_atn_cb)); memset(&m_out_sync_cb, 0, sizeof(m_out_sync_cb)); memset(&m_out_byte_cb, 0, sizeof(m_out_byte_cb)); } } //************************************************************************** // INLINE HELPERS //************************************************************************** //------------------------------------------------- // set_atn_line - //------------------------------------------------- inline void c64h156_device::set_atn_line() { m_out_atn_func(ATN); } //------------------------------------------------- // read_current_track - //------------------------------------------------- inline void c64h156_device::read_current_track() { int track_length = G64_BUFFER_SIZE; // read track data floppy_drive_read_track_data_info_buffer(m_floppy, m_side, m_track_buffer, &track_length); // extract track length m_track_len = floppy_drive_get_current_track_size(m_floppy, m_side); // set bit pointer to track start m_buffer_pos = 0; m_bit_pos = 7; m_bit_count = 0; m_zero_count = 0; if (m_track_len) { memcpy(m_speed_buffer, m_track_buffer + m_track_len, G64_SPEED_BLOCK_SIZE); update_cycles_until_next_bit(); } } //------------------------------------------------- // update_cycles_until_next_bit - //------------------------------------------------- inline void c64h156_device::update_cycles_until_next_bit() { int speed_offset = m_buffer_pos / 4; int speed_shift = (3 - (m_buffer_pos % 4)) * 2; UINT8 speed = m_speed_buffer[speed_offset]; int ds = (speed >> speed_shift) & 0x03; m_cycles_until_next_bit = (16 - ds) * 4; if (LOG) logerror("buff %04x:%u speed %04x shift %u ds %u cycles %u\n", m_buffer_pos, m_bit_pos, speed_offset, speed_shift, ds, m_cycles_until_next_bit); } //------------------------------------------------- // receive_bit - receive bit //------------------------------------------------- inline void c64h156_device::receive_bit() { if (!m_track_len) { m_bit_sync = 0; } else { if (m_cycles_until_next_bit == 0) { m_bit_sync = BIT(m_track_buffer[m_buffer_pos], m_bit_pos); if (m_bit_sync) { m_zero_count = 0; m_cycles_until_random_flux = (rand() % 31) + 289; } else { m_zero_count++; } if (m_zero_count >= m_cycles_until_random_flux) { // receive phantom bit if (LOG) logerror("PHANTOM BIT SYNC 1 after %u cycles\n", m_zero_count); m_bit_sync = 1; m_zero_count = 0; m_cycles_until_random_flux = (rand() % 367) + 33; } else { if (LOG) logerror("BIT SYNC %u\n", m_bit_sync); } m_shift <<= 1; m_shift |= m_bit_sync; m_bit_pos--; m_bit_count++; if (m_bit_pos < 0) { m_bit_pos = 7; m_buffer_pos++; if (m_buffer_pos >= m_track_len) { // loop to the start of the track m_buffer_pos = 0; } } update_cycles_until_next_bit(); } m_cycles_until_next_bit--; } } //------------------------------------------------- // decode_bit - //------------------------------------------------- inline void c64h156_device::decode_bit() { // UE7 bool ue7_tc = false; if (!m_last_bit_sync && m_bit_sync) { m_ue7 = m_ds; } else { m_ue7++; if (m_ue7 == 16) { m_ue7 = m_ds; ue7_tc = true; } } if (LOG) logerror("UE7 CTR %01x TC %u, ", m_ue7, ue7_tc); // UF4 if (!m_last_bit_sync && m_bit_sync) { m_uf4 = 0; if (LOG) logerror("--"); } else { if (!m_ue7_tc && ue7_tc) { m_uf4++; m_uf4 &= 0x0f; if (LOG) logerror("++"); } else { if (LOG) logerror(" "); } } m_last_bit_sync = m_bit_sync; m_ue7_tc = ue7_tc; int uf4_qa = BIT(m_uf4, 0); int uf4_qb = BIT(m_uf4, 1); int uf4_qc = BIT(m_uf4, 2); int uf4_qd = BIT(m_uf4, 3); int ue5a = !(uf4_qc || uf4_qd); if (LOG) logerror("UF4 CTR %01x %u%u%u%u UE5A %u, ", m_uf4, uf4_qd, uf4_qc, uf4_qb, uf4_qa, ue5a); if (!m_uf4_qb && uf4_qb) { // shift bits thru flip-flops m_u4b = m_u4a; m_u4a = BIT(m_ud2, 7); // shift in data bit m_ud2 <<= 1; m_ud2 |= ue5a; if (LOG) logerror("<<"); } else { if (LOG) logerror(" "); } if (LOG) logerror("UD2 %u%u%u%u%u%u%u%u%u%u : %02x (%02x), ", m_u4b, m_u4a, BIT(m_ud2, 7), BIT(m_ud2, 6), BIT(m_ud2, 5), BIT(m_ud2, 4), BIT(m_ud2, 3), BIT(m_ud2, 2), BIT(m_ud2, 1), BIT(m_ud2, 0), m_ud2, m_shift & 0xff); // UE3 int block_sync = !(m_oe && (m_ud2 == 0xff) && m_u4a && m_u4b); if (LOG) logerror("SYNC %u, ", block_sync); if (!block_sync) { // load UE3 m_ue3 = 8; // pin D is floating and TTL inputs float high if (LOG) logerror("--"); } else { if (m_block_sync && !m_uf4_qb && uf4_qb) { // clock UE3 m_ue3++; if (m_ue3 == 16) { m_ue3 = 0; } if (LOG) logerror("++"); } else { if (LOG) logerror(" "); } } int ue3_qa = BIT(m_ue3, 0); int ue3_qb = BIT(m_ue3, 1); int ue3_qc = BIT(m_ue3, 2); int uf3a = !(ue3_qa && ue3_qb && ue3_qc); int uc1b = !uf3a; // schmitt trigger if (LOG) logerror("UE3 CTR %01x UF3A %u UC1B %u, ", m_ue3, uf3a, uc1b); int byte_sync = !(uc1b && m_soe && !uf4_qb); if (LOG) logerror("BYTE %u SOE %u\n", byte_sync, m_soe); // UD3 int uf3b = !(uc1b && uf4_qa && uf4_qb); if (!uf3b) { m_ud3 = m_via_pa; } else if (!m_uf4_qb && uf4_qb) { // shift out bits int ud3_qh = BIT(m_ud3, 0); m_ud3 >>= 1; int uf5b = !(!uf4_qb && ud3_qh); if (!m_oe && m_wp) { // TODO write bit to disk if (LOG) logerror("WRITE BIT %u\n", uf5b); } } // prepare for next cycle if (m_block_sync != block_sync) { m_block_sync = block_sync; m_out_sync_func(m_block_sync); } if (m_byte_sync != byte_sync) { m_byte_sync = byte_sync; if (m_accl) { if (!byte_sync) { m_accl_yb = m_ud2; m_accl_byte_sync = byte_sync; m_out_byte_func(m_accl_byte_sync); } } else { m_out_byte_func(m_byte_sync); } } m_uf4_qb = uf4_qb; m_bit_sync = 0; } //************************************************************************** // LIVE DEVICE //************************************************************************** //------------------------------------------------- // c64h156_device - constructor //------------------------------------------------- c64h156_device::c64h156_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : device_t(mconfig, C64H156, "64H156", tag, owner, clock), device_execute_interface(mconfig, *this), m_icount(0), m_floppy(NULL), m_track_buffer(*this, "track_buffer"), m_speed_buffer(*this, "speed_buffer"), m_side(0), m_track_len(0), m_buffer_pos(0), m_bit_pos(0), m_bit_count(0), m_mtr(0), m_accl(0), m_ds(0), m_soe(0), m_oe(0), m_atni(1), m_atna(1), m_last_bit_sync(0), m_bit_sync(0), m_byte_sync(1), m_accl_byte_sync(1), m_block_sync(1), m_ue7(0), m_ue7_tc(0), m_uf4(0), m_uf4_qb(0), m_ud2(0), m_accl_yb(0), m_u4a(0), m_u4b(0), m_ue3(0), m_uc1b(0), m_wp(0) { } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void c64h156_device::device_start() { // set our instruction counter m_icountptr = &m_icount; // allocate track buffer m_track_buffer.allocate(G64_BUFFER_SIZE); m_speed_buffer.allocate(G64_SPEED_BLOCK_SIZE); // resolve callbacks m_out_atn_func.resolve(m_out_atn_cb, *this); m_out_sync_func.resolve(m_out_sync_cb, *this); m_out_byte_func.resolve(m_out_byte_cb, *this); // register for state saving save_item(NAME(m_shift)); save_item(NAME(m_side)); save_item(NAME(m_track_len)); save_item(NAME(m_buffer_pos)); save_item(NAME(m_bit_pos)); save_item(NAME(m_bit_count)); save_item(NAME(m_cycles_until_next_bit)); save_item(NAME(m_zero_count)); save_item(NAME(m_cycles_until_random_flux)); save_item(NAME(m_mtr)); save_item(NAME(m_accl)); save_item(NAME(m_ds)); save_item(NAME(m_soe)); save_item(NAME(m_oe)); save_item(NAME(m_atni)); save_item(NAME(m_atna)); save_item(NAME(m_last_bit_sync)); save_item(NAME(m_bit_sync)); save_item(NAME(m_byte_sync)); save_item(NAME(m_accl_byte_sync)); save_item(NAME(m_block_sync)); save_item(NAME(m_ue7)); save_item(NAME(m_ue7_tc)); save_item(NAME(m_uf4)); save_item(NAME(m_uf4_qb)); save_item(NAME(m_ud2)); save_item(NAME(m_accl_yb)); save_item(NAME(m_u4a)); save_item(NAME(m_u4b)); save_item(NAME(m_ue3)); save_item(NAME(m_uc1b)); save_item(NAME(m_via_pa)); save_item(NAME(m_ud3)); save_item(NAME(m_wp)); } //------------------------------------------------- // execute_run - //------------------------------------------------- void c64h156_device::execute_run() { do { if (m_mtr) { receive_bit(); } decode_bit(); m_icount--; } while (m_icount > 0); } //------------------------------------------------- // yb_r - //------------------------------------------------- READ8_MEMBER( c64h156_device::yb_r ) { UINT8 data = 0; if (m_soe) { if (m_accl) { data = m_accl_yb; } else { data = m_ud2; } } if (LOG) logerror("%s YB read %02x:%02x\n", machine().describe_context(), m_ud2, data); return data; } //------------------------------------------------- // yb_w - //------------------------------------------------- WRITE8_MEMBER( c64h156_device::yb_w ) { m_via_pa = data; } //------------------------------------------------- // test_w - test write //------------------------------------------------- WRITE_LINE_MEMBER( c64h156_device::test_w ) { } //------------------------------------------------- // accl_w - //------------------------------------------------- WRITE_LINE_MEMBER( c64h156_device::accl_w ) { m_accl = state; } //------------------------------------------------- // sync_r - sync read //------------------------------------------------- READ_LINE_MEMBER( c64h156_device::sync_r ) { return m_block_sync; } //------------------------------------------------- // byte_r - byte ready read //------------------------------------------------- READ_LINE_MEMBER( c64h156_device::byte_r ) { int state = 1; if (m_accl) { state = m_accl_byte_sync; } else { state = m_byte_sync; } return state; } //------------------------------------------------- // ted_w - //------------------------------------------------- WRITE_LINE_MEMBER( c64h156_device::ted_w ) { if (m_accl && !m_accl_byte_sync && !state) { m_accl_byte_sync = 1; m_out_byte_func(m_accl_byte_sync); } } //------------------------------------------------- // mtr_w - motor write //------------------------------------------------- WRITE_LINE_MEMBER( c64h156_device::mtr_w ) { if (m_mtr != state) { if (LOG) logerror("MTR %u\n", state); if (state) { // read track data read_current_track(); } floppy_mon_w(m_floppy, !state); m_mtr = state; } } //------------------------------------------------- // oe_w - output enable write //------------------------------------------------- WRITE_LINE_MEMBER( c64h156_device::oe_w ) { if (LOG) logerror("OE %u\n", state); m_oe = state; } //------------------------------------------------- // soe_w - SO enable write //------------------------------------------------- WRITE_LINE_MEMBER( c64h156_device::soe_w ) { if (LOG) logerror("SOE %u\n", state); m_soe = state; } //------------------------------------------------- // atn_r - serial attention read //------------------------------------------------- READ_LINE_MEMBER( c64h156_device::atn_r ) { return ATN; } //------------------------------------------------- // atni_w - serial attention input write //------------------------------------------------- WRITE_LINE_MEMBER( c64h156_device::atni_w ) { if (LOG) logerror("ATNI %u\n", state); m_atni = state; set_atn_line(); } //------------------------------------------------- // atna_w - serial attention acknowledge write //------------------------------------------------- WRITE_LINE_MEMBER( c64h156_device::atna_w ) { if (LOG) logerror("ATNA %u\n", state); m_atna = state; set_atn_line(); } //------------------------------------------------- // set_floppy - //------------------------------------------------- void c64h156_device::set_floppy(legacy_floppy_image_device *floppy) { m_floppy = floppy; // install image callbacks floppy_install_unload_proc(m_floppy, c64h156_device::on_disk_change); floppy_install_load_proc(m_floppy, c64h156_device::on_disk_change); } //------------------------------------------------- // stp_w - //------------------------------------------------- void c64h156_device::stp_w(int data) { if (m_mtr) { int track = floppy_drive_get_current_track(m_floppy); int tracks = (data - track) & 0x03; if (tracks == 3) { tracks = -1; } if (tracks == -1 || tracks == 1) { // step read/write head floppy_drive_seek(m_floppy, tracks); // read new track data read_current_track(); } } } //------------------------------------------------- // ds_w - density select //------------------------------------------------- void c64h156_device::ds_w(int data) { m_ds = data; } //------------------------------------------------- // set_side - //------------------------------------------------- void c64h156_device::set_side(int side) { m_side = side; } //------------------------------------------------- // on_disk_change - //------------------------------------------------- void c64h156_device::on_disk_change(device_image_interface &image) { //m_wp = !floppy_wpt_r(image); //read_current_track(); }