/// <summary> /// Z80 port write routine /// </summary> /// <param name="in_address"></param> /// <param name="in_byte"></param> public void PortWrite(ushort in_address, byte in_byte) { switch (in_address & 0x0f) { case SOUND_REGISTER: TVCManagers.Default.AudioManager.AdvanceChannel(m_audio_channel_index, m_tvcomputer.GetCPUTicks()); m_sound_chip.WriteRegister(in_byte); break; case PAGE_REGISTER: m_current_page_address = (UInt16)((in_byte & PAGE_ADDRESS_MASK) << 13); m_sound_chip_enable = (in_byte & SOUND_ENABLE_MASK) != 0; break; } }
private void AdvanceAudio() { TVCManagers.Default.AudioManager.AdvanceChannel(m_audio_channel_index, m_tvcomputer.GetCPUTicks()); }
/** Write1793() **********************************************/ /** Write value V into WD1793 register A. Returns DRQ/IRQ **/ /** values. **/ /*************************************************************/ public void PortWrite(ushort in_address, byte in_value) { if ((in_address & 0x0f) != 8) { if ((in_address & 0x0f) == 3) { //Debug.Write(in_value.ToString("X2") + " "); } else { Debug.Write((in_address & 0x0f).ToString("X2") + ":" + in_value.ToString("X2") + " = "); } } switch (in_address & 0x0f) { // command address case PORT_COMMAND: m_fdc_command = in_value; // Reset interrupt request m_reg_hw_status &= ~(HardwareFlags.INT); // If it is FORCE-IRQ command... if ((in_value & 0xF0) == 0xD0) { m_force_interrupt = (ForceInterruptFlags)(in_value & 0x0f); Debug.WriteLine("Force interrupt: {0}", m_force_interrupt); // Reset any executing command m_data_length = 0; m_data_count = 0; m_operation_state = OperationState.None; // Either reset BUSY flag or reset all flags if BUSY=0 if ((m_fdc_status & StatusFlags.BUSY) != 0) { m_fdc_status &= ~StatusFlags.BUSY; } else { m_fdc_status = m_disk_drives[m_current_drive_index].Track == 0 ? StatusFlags.TRACK0 : 0; m_fdc_status_mode = 1; } // Cause immediate interrupt if requested if ((in_value & (byte)CommandFlags.IRQ) != 0) { m_reg_hw_status = HardwareFlags.INT; } // Done return; } // If busy, drop out if ((m_fdc_status & StatusFlags.BUSY) != 0) { break; } // Reset status register m_fdc_status = 0x00; m_reg_hw_status = 0x00; // Depending on the command... switch (in_value & 0xF0) { // RESTORE (seek track 0) case 0x00: Debug.WriteLine("Restore"); // command group I m_fdc_status_mode = 1; // set head load m_head_loaded = ((in_value & (byte)CommandFlags.LOADHEAD) != 0); m_fdc_last_step_direction = 0; // if already at track zero if (m_disk_drives[m_current_drive_index].Track == 0) { m_fdc_status |= StatusFlags.TRACK0; m_reg_hw_status |= HardwareFlags.INT; m_fdc_track = 0; m_operation_state = OperationState.None; } else { // not on the first track -> start operation StartOperation((m_disk_drives[m_current_drive_index].Geometry.NumberOfTracks / 2) * SteppingDelays[in_value & (byte)CommandFlags.STEPRATE], StatusFlags.TRACK0 | (StatusFlags)(((in_value & (byte)CommandFlags.LOADHEAD) != 0) ? StatusFlags.HEADLOAD : 0), HardwareFlags.INT, 0); } break; // SEEK command case 0x10: Debug.WriteLine("Seek: {0:x2}", m_fdc_data); // command group I m_fdc_status_mode = 1; // set head load m_head_loaded = ((in_value & (byte)CommandFlags.LOADHEAD) != 0); if (m_fdc_data > m_fdc_track) { m_fdc_last_step_direction = 0; } else { m_fdc_last_step_direction = 0x20; } // Reset any executing command m_data_count = 0; m_data_length = 0; StartOperation(Math.Abs((int)m_fdc_data - (int)m_fdc_track) * SteppingDelays[in_value & (byte)CommandFlags.STEPRATE], (StatusFlags)(((in_value & (byte)CommandFlags.LOADHEAD) != 0) ? StatusFlags.HEADLOAD : 0), HardwareFlags.INT, m_fdc_data); break; case 0x20: // STEP case 0x30: // STEP-AND-UPDATE case 0x40: // STEP-IN case 0x50: // STEP-IN-AND-UPDATE case 0x60: // STEP-OUT case 0x70: // STEP-OUT-AND-UPDATE { Debug.Write(string.Format("Step: {0:x2}", (in_value & 0xF0))); // command group I m_fdc_status_mode = 1; // set head load m_head_loaded = ((in_value & (byte)CommandFlags.LOADHEAD) != 0); // Either store or fetch step direction if ((in_value & 0x40) != 0) { m_fdc_last_step_direction = (byte)(in_value & 0x20); } else { in_value = (byte)((in_value & ~0x20) | m_fdc_last_step_direction); } // Step the head, update track register if requested byte target_track = m_disk_drives[m_current_drive_index].Track; if ((in_value & 0x20) != 0) { if (m_disk_drives[m_current_drive_index].Track > 0) { target_track--; } } else { if (target_track < m_disk_drives[m_current_drive_index].Geometry.NumberOfTracks - 1) { target_track++; } } Debug.WriteLine(" Track: {0}", target_track); //m_disk_drives[m_current_drive_index].Track = target_track; // Update track register if requested StatusFlags new_status = 0; if ((in_value & (byte)CommandFlags.SETTRACK) != 0) { if (target_track >= m_disk_drives[m_current_drive_index].Geometry.NumberOfTracks) { new_status = StatusFlags.SEEKERR; } m_fdc_track = m_disk_drives[m_current_drive_index].Track; } StartOperation(SteppingDelays[in_value & (byte)CommandFlags.STEPRATE], new_status, HardwareFlags.INT, target_track); } break; // Track write case 0xF0: Debug.Write("TrackWrite "); // command group III m_fdc_status_mode = 3; m_operation_start_tick = m_tvcomputer.GetCPUTicks(); m_operation_state = OperationState.TrackWriteWaitForIndex; m_prev_index_pulse_state = IsIndexPulse(); m_fdc_status = StatusFlags.BUSY; m_reg_hw_status = HardwareFlags.DRQ; break; // Sector read case 0x80: // single sector read case 0x90: // multiple sector read Debug.WriteLine("Sector read, T:{0:d}, S:{1:d}, H:{2:d}", m_fdc_track, m_fdc_sector, GetCurrentDriveSide()); // check drive if (m_current_drive_index == InvalidDriveIndex || !m_disk_drives[m_current_drive_index].IsDiskPresent()) { m_fdc_status = StatusFlags.NOTREADY; } else { // check sector and track address if (m_fdc_track != m_disk_drives[m_current_drive_index].Track || m_fdc_sector <1 || m_fdc_sector> m_disk_drives[m_current_drive_index].Geometry.SectorPerTrack) { m_fdc_status = StatusFlags.NOTFOUND; m_reg_hw_status = HardwareFlags.INT; m_operation_state = OperationState.None; m_fdc_status_mode = 2; m_data_length = 0; } else { m_disk_drives[m_current_drive_index].Track = m_fdc_track; m_data_count = 0; m_data_length = m_disk_drives[m_current_drive_index].Geometry.SectorLength * (((in_value & 0x10) != 0) ? m_disk_drives[m_current_drive_index].Geometry.SectorPerTrack - m_fdc_sector + 1 : 1); m_operation_start_tick = m_tvcomputer.GetCPUTicks(); m_operation_state = OperationState.SectorRead; m_fdc_status = StatusFlags.BUSY; m_fdc_status_mode = 2; m_head_loaded = true; m_disk_drives[m_current_drive_index].SeekSector(m_fdc_sector, GetCurrentDriveSide()); } } break; #if false case 0xA0: case 0xB0: /* WRITE-SECTORS */ if (D->Verbose) { printf("WD1793: WRITE-SECTOR%s %c:%d:%d:%d (%02Xh)\n", V & 0x10 ? "S" : "", 'A' + D->Drive, D->Side, D->R[1], D->R[2], V); } /* Seek to the requested sector */ D->Ptr = SeekFDI( D->Disk[D->Drive], D->Side, D->Track[D->Drive], V & C_SIDECOMP ? !!(V & C_SIDE) : D->Side, D->R[1], D->R[2] ); /* If seek successful, set up writing operation */ if (!D->Ptr) { if (D->Verbose) { printf("WD1793: WRITE ERROR\n"); } D->R[0] = (D->R[0] & ~F_ERRCODE) | F_NOTFOUND; m_irq_pending = true;; } else { m_wr_length = D->Disk[D->Drive]->SecSize * (V & 0x10 ? (D->Disk[D->Drive]->Sectors - D->R[2] + 1) : 1); D->R[0] |= F_BUSY | F_DRQ; D->IRQ = WD1793_DRQ; D->Wait = 255; } break; #endif // Read address case 0xC0: Debug.WriteLine("Read address", in_value); m_crc_generator.Reset(); m_address_buffer[0] = GetCurrentDriveTrack(); m_address_buffer[1] = GetCurrentDriveSide(); m_address_buffer[2] = 1; m_address_buffer[3] = GetCurrentSectorLength(); m_crc_generator.Add(m_address_buffer, 4); m_address_buffer[4] = m_crc_generator.CRCLow; m_address_buffer[5] = m_crc_generator.CRCHigh; m_data_count = 0; m_data_length = 6; m_fdc_sector = GetCurrentDriveTrack(); m_prev_index_pulse_state = IsIndexPulse(); m_operation_state = OperationState.IDReadWaitForIndex; m_fdc_status_mode = 3; m_head_loaded = true; break; #if false /* Read first sector address from the track */ if (!D->Disk[D->Drive]) { D->Ptr = 0; } else { for (J = 0; J < 256; ++J) { D->Ptr = SeekFDI( D->Disk[D->Drive], D->Side, D->Track[D->Drive], D->Side, D->Track[D->Drive], J ); if (D->Ptr) { break; } } } /* If address found, initiate data transfer */ if (!D->Ptr) { if (D->Verbose) { printf("WD1793: READ-ADDRESS ERROR\n"); } D->R[0] |= F_NOTFOUND; m_irq_pending = true;; } else { D->Ptr = D->Disk[D->Drive]->Header; m_rd_length = 6; D->R[0] |= F_BUSY | F_DRQ; D->IRQ = WD1793_DRQ; D->Wait = 255; } break; #endif #if false case 0xE0: /* READ-TRACK */ if (D->Verbose) { printf("WD1793: READ-TRACK %d (%02Xh) UNSUPPORTED!\n", D->R[1], V); } break; case 0xF0: /* WRITE-TRACK */ if (D->Verbose) { printf("WD1793: WRITE-TRACK %d (%02Xh) UNSUPPORTED!\n", D->R[1], V); } break; default: /* UNKNOWN */ if (D->Verbose) { printf("WD1793: UNSUPPORTED OPERATION %02Xh!\n", V); } break; #endif } break; // track register case PORT_TRACK: Debug.WriteLine("Track register set: {0:x2}", in_value); if ((m_fdc_status & StatusFlags.BUSY) == 0 && !m_fdc_reset_state) { m_fdc_track = in_value; } break; // sector register case PORT_SECTOR: Debug.WriteLine("Sector register set: {0:x2}", in_value); if ((m_fdc_status & StatusFlags.BUSY) == 0 && !m_fdc_reset_state) { m_fdc_sector = in_value; } break; case PORT_DATA: m_fdc_data = in_value; switch (m_operation_state) { case OperationState.TrackWriteGap: m_track_write_id_buffer = (m_track_write_id_buffer << 8) | in_value; break; } m_reg_hw_status &= ~HardwareFlags.DRQ; /* * if ((m_reg_hw_status & HardwareFlags.DRQ) == 0) * { * m_track_write_id_buffer = (m_track_write_id_buffer << 8) | in_value; * } * else * { * // data overrun occured * m_reg_hw_status = HardwareFlags.INT; * m_fdc_status |= StatusFlags.LOSTDATA; * m_operation_state = OperationState.None; * } */ break; #if false // check track write mode if (m_read_write_mode != ReadWriteMode.TrackWrite) { TrackWriteData(in_value); } else { /* When writing data, store value to disk */ if (m_wr_length > 0) { Debug.WriteLine(string.Format("WD1793: EXTRA DATA WRITE (%02Xh)\n", in_value)); } else { /* Write data */ *D->Ptr++ = V; /* Decrement length */ if (--m_wr_length > 0) { /* Reset timeout watchdog */ D->Wait = 255; /* Advance to the next sector as needed */ if (!(m_wr_length & (D->Disk[D->Drive]->SecSize - 1))) { ++D->R[2]; } } else { /* Write completed */ if (D->Verbose) { printf("WD1793: WRITE COMPLETED\n"); } D->R[0] &= ~(F_DRQ | F_BUSY); m_irq_pending = true;; } } } // Save last written value m_fdc_data = in_value; #endif // parameter register case PORT_PARAM: Debug.WriteLine("Param register set: {0}", (ParametersFlags)in_value); m_reg_param = (ParametersFlags)in_value; switch (m_reg_param & ParametersFlags.DriveSelectMask) { case ParametersFlags.DriveSelect0: m_current_drive_index = 0; break; case ParametersFlags.DriveSelect1: m_current_drive_index = 1; break; case ParametersFlags.DriveSelect2: m_current_drive_index = 2; break; case ParametersFlags.DriveSelect3: m_current_drive_index = 3; break; default: m_current_drive_index = InvalidDriveIndex; break; } break; // page register case PORT_PAGE: m_reg_page = in_value; m_fdc_reset_state = false; break; } }
public void PortWrite(ushort in_address, byte in_byte) { switch (in_address & 0x0f) { case 0: case 1: TVCManagers.Default.AudioManager.AdvanceChannel(m_SN76489_audio_channel_index, m_tvcomputer.GetCPUTicks()); m_SN76489.WriteRegister(in_byte); break; case 2: TVCManagers.Default.AudioManager.AdvanceChannel(m_SAA1099_audio_channel_index, m_tvcomputer.GetCPUTicks()); m_SAA1099.WriteControlRegister(in_byte); break; case 3: TVCManagers.Default.AudioManager.AdvanceChannel(m_SAA1099_audio_channel_index, m_tvcomputer.GetCPUTicks()); m_SAA1099.WriteAddressRegister(in_byte); break; case 4: case 5: break; case 6: case 7: m_page_register = in_byte; break; } }
private void PortWriteFEH(ushort in_port_address, byte in_data) { TVCManagers.Default.AudioManager.AdvanceChannel(m_audio_channel_index, m_tvcomputer.GetCPUTicks()); m_sound_chip.WriteControlRegister(in_data); }