Compare commits
10 Commits
a5416f521a
...
dfd4d82693
Author | SHA1 | Date |
---|---|---|
Maxim Poliakovski | dfd4d82693 | |
Maxim Poliakovski | e5282f9852 | |
Maxim Poliakovski | b2c342e453 | |
Maxim Poliakovski | 3aad040f28 | |
Maxim Poliakovski | e722ef3e8a | |
Maxim Poliakovski | e94be9acee | |
joevt | ae5a1331b6 | |
joevt | a0052b7b9a | |
joevt | 503ee92528 | |
joevt | 6c3715cc32 |
|
@ -33,8 +33,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
using namespace MeshScsi;
|
using namespace MeshScsi;
|
||||||
|
|
||||||
int MeshController::device_postinit()
|
int MeshController::device_postinit() {
|
||||||
{
|
|
||||||
this->bus_obj = dynamic_cast<ScsiBus*>(gMachineObj->get_comp_by_name("ScsiMesh"));
|
this->bus_obj = dynamic_cast<ScsiBus*>(gMachineObj->get_comp_by_name("ScsiMesh"));
|
||||||
|
|
||||||
this->int_ctrl = dynamic_cast<InterruptCtrl*>(
|
this->int_ctrl = dynamic_cast<InterruptCtrl*>(
|
||||||
|
@ -44,11 +43,11 @@ int MeshController::device_postinit()
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeshController::reset(bool is_hard_reset)
|
void MeshController::reset(bool is_hard_reset) {
|
||||||
{
|
|
||||||
this->cur_cmd = SeqCmd::NoOperation;
|
this->cur_cmd = SeqCmd::NoOperation;
|
||||||
this->fifo_cnt = 0;
|
this->fifo_cnt = 0;
|
||||||
this->int_mask = 0;
|
this->int_mask = 0;
|
||||||
|
this->exception = 0;
|
||||||
this->xfer_count = 0;
|
this->xfer_count = 0;
|
||||||
this->src_id = 7;
|
this->src_id = 7;
|
||||||
|
|
||||||
|
@ -58,8 +57,7 @@ void MeshController::reset(bool is_hard_reset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t MeshController::read(uint8_t reg_offset)
|
uint8_t MeshController::read(uint8_t reg_offset) {
|
||||||
{
|
|
||||||
switch(reg_offset) {
|
switch(reg_offset) {
|
||||||
case MeshReg::XferCount0:
|
case MeshReg::XferCount0:
|
||||||
return this->xfer_count & 0xFFU;
|
return this->xfer_count & 0xFFU;
|
||||||
|
@ -74,7 +72,7 @@ uint8_t MeshController::read(uint8_t reg_offset)
|
||||||
case MeshReg::FIFOCount:
|
case MeshReg::FIFOCount:
|
||||||
return this->fifo_cnt;
|
return this->fifo_cnt;
|
||||||
case MeshReg::Exception:
|
case MeshReg::Exception:
|
||||||
return 0;
|
return this->exception;
|
||||||
case MeshReg::Error:
|
case MeshReg::Error:
|
||||||
return 0;
|
return 0;
|
||||||
case MeshReg::IntMask:
|
case MeshReg::IntMask:
|
||||||
|
@ -94,8 +92,7 @@ uint8_t MeshController::read(uint8_t reg_offset)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeshController::write(uint8_t reg_offset, uint8_t value)
|
void MeshController::write(uint8_t reg_offset, uint8_t value) {
|
||||||
{
|
|
||||||
uint16_t new_stat;
|
uint16_t new_stat;
|
||||||
|
|
||||||
switch(reg_offset) {
|
switch(reg_offset) {
|
||||||
|
@ -141,19 +138,20 @@ void MeshController::write(uint8_t reg_offset, uint8_t value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeshController::perform_command(const uint8_t cmd)
|
void MeshController::perform_command(const uint8_t cmd) {
|
||||||
{
|
|
||||||
this->cur_cmd = cmd;
|
this->cur_cmd = cmd;
|
||||||
|
|
||||||
this->int_stat &= ~INT_CMD_DONE;
|
this->int_stat &= ~INT_CMD_DONE;
|
||||||
|
|
||||||
switch (this->cur_cmd & 0xF) {
|
switch (this->cur_cmd & 0xF) {
|
||||||
case SeqCmd::Arbitrate:
|
case SeqCmd::Arbitrate:
|
||||||
|
this->exception &= EXC_ARB_LOST;
|
||||||
this->bus_obj->release_ctrl_lines(this->src_id);
|
this->bus_obj->release_ctrl_lines(this->src_id);
|
||||||
this->cur_state = SeqState::BUS_FREE;
|
this->cur_state = SeqState::BUS_FREE;
|
||||||
this->sequencer();
|
this->sequencer();
|
||||||
break;
|
break;
|
||||||
case SeqCmd::Select:
|
case SeqCmd::Select:
|
||||||
|
this->exception &= EXC_SEL_TIMEOUT;
|
||||||
this->cur_state = SeqState::SEL_BEGIN;
|
this->cur_state = SeqState::SEL_BEGIN;
|
||||||
this->sequencer();
|
this->sequencer();
|
||||||
break;
|
break;
|
||||||
|
@ -162,7 +160,7 @@ void MeshController::perform_command(const uint8_t cmd)
|
||||||
this->int_stat |= INT_CMD_DONE;
|
this->int_stat |= INT_CMD_DONE;
|
||||||
break;
|
break;
|
||||||
case SeqCmd::EnaReselect:
|
case SeqCmd::EnaReselect:
|
||||||
LOG_F(INFO, "MESH: EnaReselect stub invoked");
|
LOG_F(9, "MESH: EnaReselect stub invoked");
|
||||||
this->int_stat |= INT_CMD_DONE;
|
this->int_stat |= INT_CMD_DONE;
|
||||||
break;
|
break;
|
||||||
case SeqCmd::DisReselect:
|
case SeqCmd::DisReselect:
|
||||||
|
@ -182,8 +180,7 @@ void MeshController::perform_command(const uint8_t cmd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeshController::seq_defer_state(uint64_t delay_ns)
|
void MeshController::seq_defer_state(uint64_t delay_ns) {
|
||||||
{
|
|
||||||
seq_timer_id = TimerManager::get_instance()->add_oneshot_timer(
|
seq_timer_id = TimerManager::get_instance()->add_oneshot_timer(
|
||||||
delay_ns,
|
delay_ns,
|
||||||
[this]() {
|
[this]() {
|
||||||
|
@ -254,8 +251,7 @@ void MeshController::sequencer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeshController::update_irq()
|
void MeshController::update_irq() {
|
||||||
{
|
|
||||||
uint8_t new_irq = !!(this->int_stat & this->int_mask);
|
uint8_t new_irq = !!(this->int_stat & this->int_mask);
|
||||||
if (new_irq != this->irq) {
|
if (new_irq != this->irq) {
|
||||||
this->irq = new_irq;
|
this->irq = new_irq;
|
||||||
|
|
|
@ -157,7 +157,7 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint8_t chip_id;
|
uint8_t chip_id;
|
||||||
uint8_t int_mask;
|
uint8_t int_mask = 0;
|
||||||
uint8_t int_stat = 0;
|
uint8_t int_stat = 0;
|
||||||
uint8_t sync_params;
|
uint8_t sync_params;
|
||||||
uint8_t src_id;
|
uint8_t src_id;
|
||||||
|
@ -165,7 +165,7 @@ private:
|
||||||
uint8_t cur_cmd;
|
uint8_t cur_cmd;
|
||||||
uint8_t error;
|
uint8_t error;
|
||||||
uint8_t fifo_cnt;
|
uint8_t fifo_cnt;
|
||||||
uint8_t exception;
|
uint8_t exception = 0;
|
||||||
uint32_t xfer_count;
|
uint32_t xfer_count;
|
||||||
|
|
||||||
ScsiBus* bus_obj;
|
ScsiBus* bus_obj;
|
||||||
|
|
|
@ -73,7 +73,8 @@ void ScsiBus::change_bus_phase(int initiator_id)
|
||||||
|
|
||||||
void ScsiBus::assert_ctrl_line(int initiator_id, uint16_t mask)
|
void ScsiBus::assert_ctrl_line(int initiator_id, uint16_t mask)
|
||||||
{
|
{
|
||||||
DCHECK_F(initiator_id >= 0 && initiator_id < SCSI_MAX_DEVS, "ScsiBus: invalid initiator ID %d", initiator_id);
|
DCHECK_F(initiator_id >= 0 && initiator_id < SCSI_MAX_DEVS,
|
||||||
|
"ScsiBus: invalid initiator ID %d", initiator_id);
|
||||||
|
|
||||||
uint16_t new_state = 0xFFFFU & mask;
|
uint16_t new_state = 0xFFFFU & mask;
|
||||||
|
|
||||||
|
@ -264,7 +265,8 @@ bool ScsiBus::pull_data(const int id, uint8_t* dst_ptr, const int size)
|
||||||
bool ScsiBus::push_data(const int id, const uint8_t* src_ptr, const int size)
|
bool ScsiBus::push_data(const int id, const uint8_t* src_ptr, const int size)
|
||||||
{
|
{
|
||||||
if (!this->devices[id]) {
|
if (!this->devices[id]) {
|
||||||
LOG_F(ERROR, "%s: no device %d for push_data %d bytes", this->get_name().c_str(), id, size);
|
LOG_F(ERROR, "%s: no device %d for push_data %d bytes",
|
||||||
|
this->get_name().c_str(), id, size);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,17 +316,22 @@ void ScsiBus::attach_scsi_devices(const std::string bus_suffix)
|
||||||
std::istringstream image_stream(image_path);
|
std::istringstream image_stream(image_path);
|
||||||
while (std::getline(image_stream, path, ':')) {
|
while (std::getline(image_stream, path, ':')) {
|
||||||
// do two passes because we skip ID 3.
|
// do two passes because we skip ID 3.
|
||||||
for (scsi_id = 0; scsi_id < SCSI_MAX_DEVS * 2 && (scsi_id == 3 || this->devices[scsi_id % SCSI_MAX_DEVS]); scsi_id++) {}
|
for (scsi_id = 0; scsi_id < SCSI_MAX_DEVS * 2 && (scsi_id == 3 ||
|
||||||
|
this->devices[scsi_id % SCSI_MAX_DEVS]); scsi_id++) {}
|
||||||
|
|
||||||
if (scsi_id < SCSI_MAX_DEVS * 2) {
|
if (scsi_id < SCSI_MAX_DEVS * 2) {
|
||||||
scsi_id = scsi_id % SCSI_MAX_DEVS;
|
scsi_id = scsi_id % SCSI_MAX_DEVS;
|
||||||
std::string scsi_device_name = "ScsiHD" + bus_suffix + "," + std::to_string(scsi_id);
|
std::string scsi_device_name = "ScsiHD" + bus_suffix + "," +
|
||||||
|
std::to_string(scsi_id);
|
||||||
ScsiHardDisk *scsi_device = new ScsiHardDisk(scsi_device_name, scsi_id);
|
ScsiHardDisk *scsi_device = new ScsiHardDisk(scsi_device_name, scsi_id);
|
||||||
gMachineObj->add_device(scsi_device_name, std::unique_ptr<ScsiHardDisk>(scsi_device));
|
gMachineObj->add_device(scsi_device_name,
|
||||||
|
std::unique_ptr<ScsiHardDisk>(scsi_device));
|
||||||
this->register_device(scsi_id, scsi_device);
|
this->register_device(scsi_id, scsi_device);
|
||||||
scsi_device->insert_image(path);
|
scsi_device->insert_image(path);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LOG_F(ERROR, "%s: Too many devices. HDD \"%s\" was not added.", this->get_name().c_str(), path.c_str());
|
LOG_F(ERROR, "%s: Too many devices. HDD \"%s\" was not added.",
|
||||||
|
this->get_name().c_str(), path.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -334,17 +341,22 @@ void ScsiBus::attach_scsi_devices(const std::string bus_suffix)
|
||||||
std::istringstream image_stream(image_path);
|
std::istringstream image_stream(image_path);
|
||||||
while (std::getline(image_stream, path, ':')) {
|
while (std::getline(image_stream, path, ':')) {
|
||||||
// do two passes because we start at ID 3.
|
// do two passes because we start at ID 3.
|
||||||
for (scsi_id = 3; scsi_id < SCSI_MAX_DEVS * 2 && this->devices[scsi_id % SCSI_MAX_DEVS]; scsi_id++) {}
|
for (scsi_id = 3; scsi_id < SCSI_MAX_DEVS * 2 &&
|
||||||
|
this->devices[scsi_id % SCSI_MAX_DEVS]; scsi_id++) {}
|
||||||
|
|
||||||
if (scsi_id < SCSI_MAX_DEVS * 2) {
|
if (scsi_id < SCSI_MAX_DEVS * 2) {
|
||||||
scsi_id = scsi_id % SCSI_MAX_DEVS;
|
scsi_id = scsi_id % SCSI_MAX_DEVS;
|
||||||
std::string scsi_device_name = "ScsiCdrom" + bus_suffix + "," + std::to_string(scsi_id);
|
std::string scsi_device_name = "ScsiCdrom" + bus_suffix + "," +
|
||||||
|
std::to_string(scsi_id);
|
||||||
ScsiCdrom *scsi_device = new ScsiCdrom(scsi_device_name, scsi_id);
|
ScsiCdrom *scsi_device = new ScsiCdrom(scsi_device_name, scsi_id);
|
||||||
gMachineObj->add_device(scsi_device_name, std::unique_ptr<ScsiCdrom>(scsi_device));
|
gMachineObj->add_device(scsi_device_name,
|
||||||
|
std::unique_ptr<ScsiCdrom>(scsi_device));
|
||||||
this->register_device(scsi_id, scsi_device);
|
this->register_device(scsi_id, scsi_device);
|
||||||
scsi_device->insert_image(path);
|
scsi_device->insert_image(path);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LOG_F(ERROR, "%s: Too many devices. CD-ROM \"%s\" was not added.", this->get_name().c_str(), path.c_str());
|
LOG_F(ERROR, "%s: Too many devices. CD-ROM \"%s\" was not added.",
|
||||||
|
this->get_name().c_str(), path.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -359,4 +371,4 @@ static const DeviceDescription ScsiMesh_Descriptor = {
|
||||||
};
|
};
|
||||||
|
|
||||||
REGISTER_DEVICE(ScsiCurio, ScsiCurio_Descriptor);
|
REGISTER_DEVICE(ScsiCurio, ScsiCurio_Descriptor);
|
||||||
REGISTER_DEVICE(ScsiMesh, ScsiMesh_Descriptor);
|
REGISTER_DEVICE(ScsiMesh, ScsiMesh_Descriptor);
|
||||||
|
|
|
@ -350,3 +350,14 @@ AddressMapEntry* MemCtrlBase::find_rom_region()
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t *MemCtrlBase::get_region_hostmem_ptr(const uint32_t addr) {
|
||||||
|
AddressMapEntry *reg_desc = this->find_range(addr);
|
||||||
|
if (reg_desc == nullptr || reg_desc->type == RT_MMIO)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (reg_desc->type == RT_MIRROR)
|
||||||
|
return (addr - reg_desc->mirror) + reg_desc->mem_ptr;
|
||||||
|
else
|
||||||
|
return (addr - reg_desc->start) + reg_desc->mem_ptr;
|
||||||
|
}
|
||||||
|
|
|
@ -86,6 +86,8 @@ public:
|
||||||
|
|
||||||
AddressMapEntry* find_rom_region();
|
AddressMapEntry* find_rom_region();
|
||||||
|
|
||||||
|
uint8_t *get_region_hostmem_ptr(const uint32_t addr);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool add_mem_region(
|
bool add_mem_region(
|
||||||
uint32_t start_addr, uint32_t size, uint32_t dest_addr, uint32_t type,
|
uint32_t start_addr, uint32_t size, uint32_t dest_addr, uint32_t type,
|
||||||
|
|
|
@ -87,10 +87,23 @@ void AppleRamdac::iodev_write(uint32_t address, uint16_t value) {
|
||||||
case RamdacRegs::MULTI:
|
case RamdacRegs::MULTI:
|
||||||
switch (this->dac_addr) {
|
switch (this->dac_addr) {
|
||||||
case RamdacRegs::CURSOR_POS_HI:
|
case RamdacRegs::CURSOR_POS_HI:
|
||||||
|
if (this->cursor_timer_id) {
|
||||||
|
TimerManager::get_instance()->cancel_timer(this->cursor_timer_id);
|
||||||
|
cursor_timer_id = 0;
|
||||||
|
}
|
||||||
this->cursor_xpos = (value << 8) | this->cursor_pos_lo;
|
this->cursor_xpos = (value << 8) | this->cursor_pos_lo;
|
||||||
break;
|
break;
|
||||||
case RamdacRegs::CURSOR_POS_LO:
|
case RamdacRegs::CURSOR_POS_LO:
|
||||||
this->cursor_pos_lo = value;
|
if (this->cursor_timer_id) {
|
||||||
|
TimerManager::get_instance()->cancel_timer(this->cursor_timer_id);
|
||||||
|
this->cursor_xpos = (this->cursor_xpos & 0xff00) | (this->cursor_pos_lo & 0x00ff);
|
||||||
|
cursor_timer_id = 0;
|
||||||
|
}
|
||||||
|
this->cursor_pos_lo = value;
|
||||||
|
this->cursor_timer_id = TimerManager::get_instance()->add_oneshot_timer(
|
||||||
|
1000000000 / 60, [this]() {
|
||||||
|
this->cursor_xpos = (this->cursor_xpos & 0xff00) | (this->cursor_pos_lo & 0x00ff);
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
case RamdacRegs::MISC_CTRL:
|
case RamdacRegs::MISC_CTRL:
|
||||||
if (bit_changed(this->dac_cr, value, 1)) {
|
if (bit_changed(this->dac_cr, value, 1)) {
|
||||||
|
@ -221,6 +234,7 @@ void AppleRamdac::draw_hw_cursor(uint8_t *src_buf, uint8_t *dst_buf, int dst_pit
|
||||||
uint8_t *src_row = src_buf + this->fb_pitch * this->cursor_ypos;
|
uint8_t *src_row = src_buf + this->fb_pitch * this->cursor_ypos;
|
||||||
uint8_t *dst_row = dst_buf + this->cursor_ypos * dst_pitch +
|
uint8_t *dst_row = dst_buf + this->cursor_ypos * dst_pitch +
|
||||||
this->cursor_xpos * sizeof(uint32_t);
|
this->cursor_xpos * sizeof(uint32_t);
|
||||||
|
int src_pitch = this->fb_pitch;
|
||||||
|
|
||||||
uint32_t *color = &this->cursor_clut[0];
|
uint32_t *color = &this->cursor_clut[0];
|
||||||
|
|
||||||
|
@ -242,7 +256,7 @@ void AppleRamdac::draw_hw_cursor(uint8_t *src_buf, uint8_t *dst_buf, int dst_pit
|
||||||
}
|
}
|
||||||
dst_16 += 16 * sizeof(uint32_t);
|
dst_16 += 16 * sizeof(uint32_t);
|
||||||
}
|
}
|
||||||
src_row += this->fb_pitch;
|
src_row += src_pitch;
|
||||||
dst_row += dst_pitch;
|
dst_row += dst_pitch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,6 +118,7 @@ protected:
|
||||||
int video_width = 0;
|
int video_width = 0;
|
||||||
int video_height = 0;
|
int video_height = 0;
|
||||||
uint32_t fb_pitch = 0;
|
uint32_t fb_pitch = 0;
|
||||||
|
uint32_t cursor_timer_id = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // APPLE_RAMDAC_H
|
#endif // APPLE_RAMDAC_H
|
||||||
|
|
|
@ -143,8 +143,8 @@ AtiMach64Gx::AtiMach64Gx()
|
||||||
|
|
||||||
// declare expansion ROM containing FCode and Mac OS drivers
|
// declare expansion ROM containing FCode and Mac OS drivers
|
||||||
if (this->attach_exp_rom_image(std::string("113-32900-004_Apple_MACH64.bin"))) {
|
if (this->attach_exp_rom_image(std::string("113-32900-004_Apple_MACH64.bin"))) {
|
||||||
ABORT_F("%s: could not load ROM - this device won't work properly!",
|
LOG_F(WARNING, "%s: could not load ROM - this device may not work properly!",
|
||||||
this->name.c_str());
|
this->name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize display identification
|
// initialize display identification
|
||||||
|
@ -166,7 +166,9 @@ AtiMach64Gx::AtiMach64Gx()
|
||||||
set_bit(regs[ATI_CRTC_GEN_CNTL], ATI_CRTC_DISPLAY_DIS); // because blank_on is true
|
set_bit(regs[ATI_CRTC_GEN_CNTL], ATI_CRTC_DISPLAY_DIS); // because blank_on is true
|
||||||
}
|
}
|
||||||
|
|
||||||
void AtiMach64Gx::change_one_bar(uint32_t &aperture, uint32_t aperture_size, uint32_t aperture_new, int bar_num) {
|
void AtiMach64Gx::change_one_bar(uint32_t &aperture, uint32_t aperture_size,
|
||||||
|
uint32_t aperture_new, int bar_num)
|
||||||
|
{
|
||||||
if (aperture != aperture_new) {
|
if (aperture != aperture_new) {
|
||||||
if (aperture)
|
if (aperture)
|
||||||
this->host_instance->pci_unregister_mmio_region(aperture, aperture_size, this);
|
this->host_instance->pci_unregister_mmio_region(aperture, aperture_size, this);
|
||||||
|
@ -184,11 +186,11 @@ void AtiMach64Gx::notify_bar_change(int bar_num)
|
||||||
if (bar_num) // only BAR0 is supported
|
if (bar_num) // only BAR0 is supported
|
||||||
return;
|
return;
|
||||||
|
|
||||||
{
|
change_one_bar(this->aperture_base[bar_num], this->aperture_size[bar_num],
|
||||||
change_one_bar(this->aperture_base[bar_num], this->aperture_size[bar_num], this->bars[bar_num] & ~15, bar_num);
|
this->bars[bar_num] & ~15, bar_num);
|
||||||
// copy aperture address to CONFIG_CNTL:CFG_MEM_AP_LOC
|
// copy aperture address to CONFIG_CNTL:CFG_MEM_AP_LOC
|
||||||
insert_bits<uint32_t>(this->config_cntl, this->aperture_base[0] >> 22, ATI_CFG_MEM_AP_LOC, ATI_CFG_MEM_AP_LOC_size);
|
insert_bits<uint32_t>(this->config_cntl, this->aperture_base[0] >> 22,
|
||||||
}
|
ATI_CFG_MEM_AP_LOC, ATI_CFG_MEM_AP_LOC_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
|
@ -502,10 +504,13 @@ void AtiMach64Gx::write_reg(uint32_t reg_offset, uint32_t value, uint32_t size)
|
||||||
new_value = value;
|
new_value = value;
|
||||||
// monitor ID is usually accessed using 8bit writes
|
// monitor ID is usually accessed using 8bit writes
|
||||||
if (offset <= 3 && offset + size > 3) {
|
if (offset <= 3 && offset + size > 3) {
|
||||||
uint8_t gpio_dirs = extract_bits<uint32_t>(new_value, ATI_DAC_GIO_DIR, ATI_DAC_GIO_DIR_size);
|
uint8_t gpio_dirs = extract_bits<uint32_t>(new_value, ATI_DAC_GIO_DIR,
|
||||||
uint8_t gpio_levels = extract_bits<uint32_t>(new_value, ATI_DAC_GIO_STATE, ATI_DAC_GIO_STATE_size);
|
ATI_DAC_GIO_DIR_size);
|
||||||
|
uint8_t gpio_levels = extract_bits<uint32_t>(new_value, ATI_DAC_GIO_STATE,
|
||||||
|
ATI_DAC_GIO_STATE_size);
|
||||||
gpio_levels = this->disp_id->read_monitor_sense(gpio_levels, gpio_dirs);
|
gpio_levels = this->disp_id->read_monitor_sense(gpio_levels, gpio_dirs);
|
||||||
insert_bits<uint32_t>(new_value, gpio_levels, ATI_DAC_GIO_STATE, ATI_DAC_GIO_STATE_size);
|
insert_bits<uint32_t>(new_value, gpio_levels, ATI_DAC_GIO_STATE,
|
||||||
|
ATI_DAC_GIO_STATE_size);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ATI_CONFIG_STAT0:
|
case ATI_CONFIG_STAT0:
|
||||||
|
@ -604,8 +609,10 @@ void AtiMach64Gx::crtc_update()
|
||||||
|
|
||||||
bool need_recalc = false;
|
bool need_recalc = false;
|
||||||
|
|
||||||
new_width = (extract_bits<uint32_t>(this->regs[ATI_CRTC_H_TOTAL_DISP], ATI_CRTC_H_DISP, ATI_CRTC_H_DISP_size) + 1) * 8;
|
new_width = (extract_bits<uint32_t>(this->regs[ATI_CRTC_H_TOTAL_DISP],
|
||||||
new_height = extract_bits<uint32_t>(this->regs[ATI_CRTC_V_TOTAL_DISP], ATI_CRTC_V_DISP, ATI_CRTC_V_DISP_size) + 1;
|
ATI_CRTC_H_DISP, ATI_CRTC_H_DISP_size) + 1) * 8;
|
||||||
|
new_height = extract_bits<uint32_t>(this->regs[ATI_CRTC_V_TOTAL_DISP],
|
||||||
|
ATI_CRTC_V_DISP, ATI_CRTC_V_DISP_size) + 1;
|
||||||
|
|
||||||
if (new_width != this->active_width || new_height != this->active_height) {
|
if (new_width != this->active_width || new_height != this->active_height) {
|
||||||
this->create_display_window(new_width, new_height);
|
this->create_display_window(new_width, new_height);
|
||||||
|
@ -613,8 +620,10 @@ void AtiMach64Gx::crtc_update()
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t new_htotal, new_vtotal;
|
uint32_t new_htotal, new_vtotal;
|
||||||
new_htotal = (extract_bits<uint32_t>(this->regs[ATI_CRTC_H_TOTAL_DISP], ATI_CRTC_H_TOTAL, ATI_CRTC_H_TOTAL_size) + 1) * 8;
|
new_htotal = (extract_bits<uint32_t>(this->regs[ATI_CRTC_H_TOTAL_DISP],
|
||||||
new_vtotal = extract_bits<uint32_t>(this->regs[ATI_CRTC_V_TOTAL_DISP], ATI_CRTC_V_TOTAL, ATI_CRTC_V_TOTAL_size) + 1;
|
ATI_CRTC_H_TOTAL, ATI_CRTC_H_TOTAL_size) + 1) * 8;
|
||||||
|
new_vtotal = extract_bits<uint32_t>(this->regs[ATI_CRTC_V_TOTAL_DISP],
|
||||||
|
ATI_CRTC_V_TOTAL, ATI_CRTC_V_TOTAL_size) + 1;
|
||||||
|
|
||||||
if (new_htotal != this->hori_total || new_vtotal != this->vert_total) {
|
if (new_htotal != this->hori_total || new_vtotal != this->vert_total) {
|
||||||
this->hori_total = new_htotal;
|
this->hori_total = new_htotal;
|
||||||
|
@ -636,12 +645,14 @@ void AtiMach64Gx::crtc_update()
|
||||||
|
|
||||||
static uint8_t bits_per_pixel[8] = {0, 0, 4, 8, 16, 24, 32, 0};
|
static uint8_t bits_per_pixel[8] = {0, 0, 4, 8, 16, 24, 32, 0};
|
||||||
|
|
||||||
int new_fb_pitch = extract_bits<uint32_t>(this->regs[ATI_CRTC_OFF_PITCH], ATI_CRTC_PITCH, ATI_CRTC_PITCH_size) * bits_per_pixel[this->pixel_format];
|
int new_fb_pitch = extract_bits<uint32_t>(this->regs[ATI_CRTC_OFF_PITCH],
|
||||||
|
ATI_CRTC_PITCH, ATI_CRTC_PITCH_size) * bits_per_pixel[this->pixel_format];
|
||||||
if (new_fb_pitch != this->fb_pitch) {
|
if (new_fb_pitch != this->fb_pitch) {
|
||||||
this->fb_pitch = new_fb_pitch;
|
this->fb_pitch = new_fb_pitch;
|
||||||
need_recalc = true;
|
need_recalc = true;
|
||||||
}
|
}
|
||||||
uint8_t* new_fb_ptr = &this->vram_ptr[extract_bits<uint32_t>(this->regs[ATI_CRTC_OFF_PITCH], ATI_CRTC_OFFSET, ATI_CRTC_OFFSET_size) * 8];
|
uint8_t* new_fb_ptr = &this->vram_ptr[extract_bits<uint32_t>(this->regs[ATI_CRTC_OFF_PITCH],
|
||||||
|
ATI_CRTC_OFFSET, ATI_CRTC_OFFSET_size) * 8];
|
||||||
if (new_fb_ptr != this->fb_ptr) {
|
if (new_fb_ptr != this->fb_ptr) {
|
||||||
this->fb_ptr = new_fb_ptr;
|
this->fb_ptr = new_fb_ptr;
|
||||||
need_recalc = true;
|
need_recalc = true;
|
||||||
|
@ -707,7 +718,8 @@ void AtiMach64Gx::crtc_update()
|
||||||
}
|
}
|
||||||
|
|
||||||
void AtiMach64Gx::draw_hw_cursor(uint8_t *dst_row, int dst_pitch) {
|
void AtiMach64Gx::draw_hw_cursor(uint8_t *dst_row, int dst_pitch) {
|
||||||
int vert_offset = extract_bits<uint32_t>(this->regs[ATI_CUR_HORZ_VERT_OFF], ATI_CUR_VERT_OFF, ATI_CUR_VERT_OFF_size);
|
int vert_offset = extract_bits<uint32_t>(this->regs[ATI_CUR_HORZ_VERT_OFF],
|
||||||
|
ATI_CUR_VERT_OFF, ATI_CUR_VERT_OFF_size);
|
||||||
int cur_height = 64 - vert_offset;
|
int cur_height = 64 - vert_offset;
|
||||||
|
|
||||||
uint32_t color0 = this->regs[ATI_CUR_CLR0] | 0x000000FFUL;
|
uint32_t color0 = this->regs[ATI_CUR_CLR0] | 0x000000FFUL;
|
||||||
|
@ -768,7 +780,9 @@ int AtiMach64Gx::device_postinit()
|
||||||
#endif
|
#endif
|
||||||
0;
|
0;
|
||||||
|
|
||||||
LOG_F(WARNING, "%s: irq_line_state:%d do_interrupt:%d CRTC_INT_CNTL:%08x", this->name.c_str(), irq_line_state, do_interrupt, this->regs[ATI_CRTC_INT_CNTL]);
|
LOG_F(WARNING, "%s: irq_line_state:%d do_interrupt:%d CRTC_INT_CNTL:%08x",
|
||||||
|
this->name.c_str(), irq_line_state, do_interrupt,
|
||||||
|
this->regs[ATI_CRTC_INT_CNTL]);
|
||||||
|
|
||||||
if (do_interrupt) {
|
if (do_interrupt) {
|
||||||
this->pci_interrupt(irq_line_state);
|
this->pci_interrupt(irq_line_state);
|
||||||
|
|
|
@ -0,0 +1,260 @@
|
||||||
|
/*
|
||||||
|
DingusPPC - The Experimental PowerPC Macintosh emulator
|
||||||
|
Copyright (C) 2018-24 divingkatae and maximum
|
||||||
|
(theweirdo) spatium
|
||||||
|
|
||||||
|
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file Taos video controller emulation. */
|
||||||
|
|
||||||
|
#include <core/bitops.h>
|
||||||
|
#include <devices/common/i2c/athens.h>
|
||||||
|
#include <devices/deviceregistry.h>
|
||||||
|
#include <devices/memctrl/memctrlbase.h>
|
||||||
|
#include <devices/video/taos.h>
|
||||||
|
#include <machines/machinebase.h>
|
||||||
|
#include <machines/machineproperties.h>
|
||||||
|
#include <loguru.hpp>
|
||||||
|
#include <memaccess.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
TaosVideo::TaosVideo() : VideoCtrlBase(640, 480) {
|
||||||
|
set_name("Taos");
|
||||||
|
|
||||||
|
supports_types(HWCompType::MMIO_DEV);
|
||||||
|
|
||||||
|
// initialize the video clock generator
|
||||||
|
this->clk_gen = std::unique_ptr<AthensClocks>(new AthensClocks(0x29, 20000000.0f));
|
||||||
|
|
||||||
|
// register the video clock generator with the I2C host
|
||||||
|
I2CBus* i2c_bus = dynamic_cast<I2CBus*>(gMachineObj->get_comp_by_type(
|
||||||
|
HWCompType::I2C_HOST));
|
||||||
|
i2c_bus->register_device(0x29, this->clk_gen.get());
|
||||||
|
|
||||||
|
// initialize video encoder
|
||||||
|
this->vid_enc = std::unique_ptr<Bt856>(new Bt856(0x44));
|
||||||
|
|
||||||
|
// register the video encoder with the I2C host
|
||||||
|
i2c_bus->register_device(0x44, this->vid_enc.get());
|
||||||
|
|
||||||
|
MemCtrlBase* mem_ctrl = dynamic_cast<MemCtrlBase*>(
|
||||||
|
gMachineObj->get_comp_by_type(HWCompType::MEM_CTRL));
|
||||||
|
|
||||||
|
// add MMIO region for VRAM
|
||||||
|
mem_ctrl->add_ram_region(TAOS_VRAM_REGION_BASE, DRAM_CAP_1MB);
|
||||||
|
this->vram_ptr = mem_ctrl->get_region_hostmem_ptr(TAOS_VRAM_REGION_BASE);
|
||||||
|
|
||||||
|
// add MMIO region for the configuration and status registers
|
||||||
|
mem_ctrl->add_mmio_region(TAOS_IOREG_BASE, 0x800, this);
|
||||||
|
|
||||||
|
// add MMIO region for the CLUT
|
||||||
|
mem_ctrl->add_mmio_region(TAOS_CLUT_BASE, 0x400, this);
|
||||||
|
|
||||||
|
this->vbl_cb = [this](uint8_t irq_line_state) {
|
||||||
|
this->vsync_active = irq_line_state;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string video_out = GET_STR_PROP("video_out");
|
||||||
|
if (video_out == "VGA")
|
||||||
|
this->mon_id = MON_ID_VGA;
|
||||||
|
else if (video_out == "PAL")
|
||||||
|
this->mon_id = MON_ID_PAL;
|
||||||
|
else
|
||||||
|
this->mon_id = MON_ID_NTSC; // NTSC is set by default in the driver
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t TaosVideo::read(uint32_t rgn_start, uint32_t offset, int size) {
|
||||||
|
if (rgn_start == TAOS_CLUT_BASE) {
|
||||||
|
uint8_t r, g, b, a;
|
||||||
|
get_palette_color(offset >> 2, r, g, b, a);
|
||||||
|
return (r << 24) | (g << 16) | (b << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
int reg_num = offset >> 2;
|
||||||
|
|
||||||
|
switch(reg_num) {
|
||||||
|
case CRT_CTRL:
|
||||||
|
return this->crt_ctrl;
|
||||||
|
case GPIO_IN:
|
||||||
|
return ((this->mon_id << 29) | (vsync_active << 25)) & ~this->gpio_cfg;
|
||||||
|
case INT_ENABLES:
|
||||||
|
return this->int_enables;
|
||||||
|
case TAOS_VERSION:
|
||||||
|
return TAOS_CHIP_VERSION;
|
||||||
|
default:
|
||||||
|
LOG_F(WARNING, "%s: reading register at 0x%X", this->name.c_str(), offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaosVideo::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size) {
|
||||||
|
if (rgn_start == TAOS_CLUT_BASE) {
|
||||||
|
set_palette_color(offset >> 2, value >> 24, (value >> 16) & 0xFF,
|
||||||
|
(value >> 8) & 0xFF, 0xFF);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int reg_num = offset >> 2;
|
||||||
|
|
||||||
|
switch(reg_num) {
|
||||||
|
case FB_BASE:
|
||||||
|
this->fb_base = value;
|
||||||
|
break;
|
||||||
|
case ROW_WORDS:
|
||||||
|
this->row_words = value;
|
||||||
|
break;
|
||||||
|
case COLOR_MODE:
|
||||||
|
this->color_mode = value;
|
||||||
|
break;
|
||||||
|
case VIDEO_MODE:
|
||||||
|
this->video_mode = value >> 30;
|
||||||
|
break;
|
||||||
|
case CRT_CTRL:
|
||||||
|
if (bit_changed(this->crt_ctrl, value, ENABLE_VIDEO_OUT)) {
|
||||||
|
if (bit_set(value, ENABLE_VIDEO_OUT))
|
||||||
|
enable_display();
|
||||||
|
}
|
||||||
|
this->crt_ctrl = value;
|
||||||
|
break;
|
||||||
|
case HEQ:
|
||||||
|
case HBWAY:
|
||||||
|
case HAL:
|
||||||
|
case HSERR:
|
||||||
|
case HFP:
|
||||||
|
case HPIX:
|
||||||
|
case HSP:
|
||||||
|
case HLFLN:
|
||||||
|
case VBPEQ:
|
||||||
|
case VBP:
|
||||||
|
case VAL:
|
||||||
|
case VFP:
|
||||||
|
case VFPEQ:
|
||||||
|
case VSYNC:
|
||||||
|
case VHLINE:
|
||||||
|
this->swatch_regs[REG_TO_INDEX(reg_num)] = value;
|
||||||
|
break;
|
||||||
|
case GPIO_CONFIG:
|
||||||
|
this->gpio_cfg = value >> 24;
|
||||||
|
break;
|
||||||
|
case GPIO_OUT:
|
||||||
|
LOG_F(INFO, "%s: value 0x%X written into GPIO pins, GPIO_CONFIG=0x%X",
|
||||||
|
this->name.c_str(), value >> 24, this->gpio_cfg);
|
||||||
|
break;
|
||||||
|
case INT_ENABLES:
|
||||||
|
this->int_enables = value;
|
||||||
|
if (bit_set(value, 29))
|
||||||
|
LOG_F(WARNING, "%s: VBL interrupt enabled", this->name.c_str());
|
||||||
|
for (int gpio_pin = 0; gpio_pin < 8; gpio_pin++) {
|
||||||
|
if (bit_set(value, 27 - gpio_pin))
|
||||||
|
LOG_F(INFO, "%s: GPIO %d interrupt enabled", this->name.c_str(),
|
||||||
|
gpio_pin);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG_F(WARNING, "%s: register at 0x%X set to 0x%X", this->name.c_str(), offset, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaosVideo::enable_display() {
|
||||||
|
int new_width, new_height;
|
||||||
|
|
||||||
|
// get pixel frequency from Athens
|
||||||
|
this->pixel_clock = this->clk_gen->get_dot_freq();
|
||||||
|
|
||||||
|
// calculate active_width and active_height from video timing parameters
|
||||||
|
new_width = (swatch_regs[REG_TO_INDEX(HFP)] >> 20) - (swatch_regs[REG_TO_INDEX(HAL)] >> 20);
|
||||||
|
new_height = (swatch_regs[REG_TO_INDEX(VFP)] >> 20) - (swatch_regs[REG_TO_INDEX(VAL)] >> 20);
|
||||||
|
|
||||||
|
LOG_F(INFO, "%s: width=%d, height=%d", this->name.c_str(), new_width, new_height);
|
||||||
|
|
||||||
|
// calculate display refresh rate
|
||||||
|
this->hori_blank = (swatch_regs[REG_TO_INDEX(HAL)] >> 20) +
|
||||||
|
((swatch_regs[REG_TO_INDEX(HSP)] >> 20) - (swatch_regs[REG_TO_INDEX(HFP)] >> 20));
|
||||||
|
|
||||||
|
this->vert_blank = (swatch_regs[REG_TO_INDEX(VAL)] >> 20) +
|
||||||
|
((swatch_regs[REG_TO_INDEX(VSYNC)] >> 20) - (swatch_regs[REG_TO_INDEX(VFP)] >> 20));
|
||||||
|
|
||||||
|
this->hori_total = this->hori_blank + new_width;
|
||||||
|
this->vert_total = this->vert_blank + new_height;
|
||||||
|
|
||||||
|
this->stop_refresh_task();
|
||||||
|
|
||||||
|
this->refresh_rate = (double)(this->pixel_clock) / (this->hori_total * this->vert_total);
|
||||||
|
|
||||||
|
LOG_F(INFO, "%s: refresh rate set to %f Hz", this->name.c_str(), this->refresh_rate);
|
||||||
|
|
||||||
|
// set framebuffer parameters
|
||||||
|
this->fb_ptr = &this->vram_ptr[this->fb_base >> 20];
|
||||||
|
this->fb_pitch = this->row_words >> 20;
|
||||||
|
|
||||||
|
if (bit_set(this->color_mode, 31)) {
|
||||||
|
this->pixel_depth = 16;
|
||||||
|
this->convert_fb_cb = [this](uint8_t *dst_buf, int dst_pitch) {
|
||||||
|
this->convert_frame_15bpp_indexed(dst_buf, dst_pitch);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
this->pixel_depth = 8;
|
||||||
|
this->convert_fb_cb = [this](uint8_t *dst_buf, int dst_pitch) {
|
||||||
|
this->convert_frame_8bpp_indexed(dst_buf, dst_pitch);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
this->start_refresh_task();
|
||||||
|
|
||||||
|
this->blank_on = false;
|
||||||
|
this->crtc_on = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaosVideo::convert_frame_15bpp_indexed(uint8_t *dst_buf, int dst_pitch) {
|
||||||
|
uint8_t *src_row, *dst_row;
|
||||||
|
uint16_t c;
|
||||||
|
uint32_t pix;
|
||||||
|
int src_pitch;
|
||||||
|
|
||||||
|
src_row = this->fb_ptr;
|
||||||
|
dst_row = dst_buf;
|
||||||
|
|
||||||
|
src_pitch = this->fb_pitch - 2 * this->active_width;
|
||||||
|
dst_pitch = dst_pitch - 4 * this->active_width;
|
||||||
|
|
||||||
|
for (int h = this->active_height; h > 0; h--) {
|
||||||
|
for (int x = this->active_width; x > 0; x--) {
|
||||||
|
c = READ_WORD_BE_A(src_row);
|
||||||
|
pix = (this->palette[(c >> 10) & 0x1F] & 0x00FF0000) |
|
||||||
|
(this->palette[(c >> 5) & 0x1F] & 0x0000FF00) |
|
||||||
|
(this->palette[ c & 0x1F] & 0xFF0000FF);
|
||||||
|
WRITE_DWORD_LE_A(dst_row, pix);
|
||||||
|
src_row += 2;
|
||||||
|
dst_row += 4;
|
||||||
|
}
|
||||||
|
src_row += src_pitch;
|
||||||
|
dst_row += dst_pitch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================== Device registry stuff ==========================
|
||||||
|
static const PropMap Taos_Properties = {
|
||||||
|
{"video_out", new StrProperty("NTSC", {"PAL", "NTSC", "VGA"})},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const DeviceDescription Taos_Descriptor = {
|
||||||
|
TaosVideo::create, {}, Taos_Properties
|
||||||
|
};
|
||||||
|
|
||||||
|
REGISTER_DEVICE(TaosVideo, Taos_Descriptor);
|
|
@ -0,0 +1,195 @@
|
||||||
|
/*
|
||||||
|
DingusPPC - The Experimental PowerPC Macintosh emulator
|
||||||
|
Copyright (C) 2018-24 divingkatae and maximum
|
||||||
|
(theweirdo) spatium
|
||||||
|
|
||||||
|
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file Taos video controller definitions. */
|
||||||
|
|
||||||
|
/**
|
||||||
|
Kudos to Keith Kaisershot @ blitter.net for his precious technical help!
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TAOS_VIDEO_H
|
||||||
|
#define TAOS_VIDEO_H
|
||||||
|
|
||||||
|
#include <devices/common/i2c/i2c.h>
|
||||||
|
#include <devices/common/mmiodevice.h>
|
||||||
|
#include <devices/video/videoctrl.h>
|
||||||
|
|
||||||
|
#include <cinttypes>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#define TAOS_IOREG_BASE 0xF0800000
|
||||||
|
#define TAOS_VRAM_REGION_BASE 0xF0000000
|
||||||
|
#define TAOS_CLUT_BASE 0xF1000000
|
||||||
|
#define TAOS_CHIP_VERSION 0xA5000000
|
||||||
|
|
||||||
|
/** Taos register definitions. */
|
||||||
|
// The implemented bits always start with the most significant bit (bit 31).
|
||||||
|
// Unimplemented bits return zeroes.
|
||||||
|
enum {
|
||||||
|
// framebuffer configuration and control
|
||||||
|
FB_BASE = 0x00, // 0x00, frame buffer base address, 19 bits, 32 byte aligned
|
||||||
|
ROW_WORDS = 0x01, // 0x04, frame buffer pitch 12 bits, 32 byte aligned
|
||||||
|
COLOR_MODE = 0x02, // 0x08, color mode 1 bit (MSB)
|
||||||
|
VIDEO_MODE = 0x03, // 0x0C, current video mode, 2 bits (MSB)
|
||||||
|
VEFS = 0x04, // 0x10, vertical even field start, 12 bits
|
||||||
|
VOFS = 0x05, // 0x14, vertical odd field start, 12 bits
|
||||||
|
HSTART = 0x06, // 0x18, horizontal active start, 12 bits
|
||||||
|
VCONV_END = 0x07, // 0x1C, vertical convolution end, 12 bits
|
||||||
|
CRT_CTRL = 0x08, // 0x20, frame buffer control, 11 bits
|
||||||
|
CRT_TEST = 0x09, // 0x24, enables test features, 18 bits
|
||||||
|
|
||||||
|
// timing generator parameters
|
||||||
|
HEQ = 0x0B, // 0x2C, horizontal equalization, 8 bits
|
||||||
|
HBWAY = 0x0C, // 0x30, horizontal breezeway, 12 bits
|
||||||
|
HAL = 0x0D, // 0x34, horizontal active line, 12 bits
|
||||||
|
HSERR = 0x0E, // 0x38, horizontal serration, 12 bits
|
||||||
|
HFP = 0x0F, // 0x3C, horizontal front porch, 12 bits
|
||||||
|
HPIX = 0x10, // 0x40, horizontal pixel count, 12 bits
|
||||||
|
HSP = 0x11, // 0x44, horizontal sync pulse, 12 bits
|
||||||
|
HLFLN = 0x12, // 0x48, horizontal half line, 12 bits
|
||||||
|
VBPEQ = 0x13, // 0x4C, vertical back porch with EQ, 12 bits
|
||||||
|
VBP = 0x14, // 0x50, vertical back porch, 12 bits
|
||||||
|
VAL = 0x15, // 0x54, vertical active line, 12 bits
|
||||||
|
VFP = 0x16, // 0x58, vertical front porch, 12 bits
|
||||||
|
VFPEQ = 0x17, // 0x5C, vertical front porch with EQ, 12 bits
|
||||||
|
VSYNC = 0x18, // 0x60, vertical sync starting point, 12 bits
|
||||||
|
VHLINE = 0x19, // 0x64, vertical half line, 12 bits
|
||||||
|
|
||||||
|
// GPIO and interrupts
|
||||||
|
GPIO_IN = 0x1A, // 0x68, reads GPIO pins, 8 bits
|
||||||
|
GPIO_CONFIG = 0x1B, // 0x6C, configure GPIO pins, 8 bits
|
||||||
|
GPIO_OUT = 0x1C, // 0x70, outputs to GPIO pins, 8 bits
|
||||||
|
GPIO_INT_CTRL = 0x1D, // 0x74, controls GPIO interrupts, 8 bits
|
||||||
|
INT_LEVELS = 0x1E, // 0x78, interrupt levels, 12 bits (read-only)
|
||||||
|
INT_ENABLES = 0x1F, // 0x7C, enable/disable interrupts, 12 bits
|
||||||
|
INT_CLEAR = 0x20, // 0x80, clear pending interrupts, 12 bits (write-only)
|
||||||
|
TAOS_VERSION = 0x23, // 0x8C, chip version, 8 bits (read-only)
|
||||||
|
INT_SET = 0x24, // 0x90, allow setting interrupts 12 bits (write-only)
|
||||||
|
HEB = 0x25, // 0x94, horizontal early blank, 12 bits
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Taos video modes (see VIDEO_MODE register above). */
|
||||||
|
enum {
|
||||||
|
Progressive_Scan = 0, // Progressive scan
|
||||||
|
Interlace, // Interlaced scan, no convolution
|
||||||
|
Interlace_Conv, // Interlaced scan, convolution, no scaling
|
||||||
|
Interlace_Conv_Scale // Interlaced scan, convolution, and scaling
|
||||||
|
};
|
||||||
|
|
||||||
|
/** CRT_CTRL bit definitions. */
|
||||||
|
enum {
|
||||||
|
ENABLE_VIDEO_OUT = 24,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define REG_TO_INDEX(reg) ((reg) - HEQ)
|
||||||
|
|
||||||
|
/** Monitor ID definitions used internally by the Taos driver. */
|
||||||
|
// Those values are obtained by reading GPIO 0...2 pins that are
|
||||||
|
// directly connected to the three-position mechanical switch
|
||||||
|
// on the backside of the console. All signals are active low.
|
||||||
|
enum {
|
||||||
|
MON_ID_PAL = 3, // GPIO_0 = "0", GPIO_1 = "1", GPIO_2 = "1"
|
||||||
|
MON_ID_NTSC = 5, // GPIO_0 = "1", GPIO_1 = "0", GPIO_2 = "1"
|
||||||
|
MON_ID_VGA = 6 // GPIO_0 = "1", GPIO_1 = "1", GPIO_2 = "0"
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Broktree Bt856 digital video encoder. */
|
||||||
|
class Bt856 : public I2CDevice, public HWComponent {
|
||||||
|
public:
|
||||||
|
Bt856(uint8_t dev_addr) {
|
||||||
|
supports_types(HWCompType::I2C_DEV);
|
||||||
|
|
||||||
|
this->my_addr = dev_addr;
|
||||||
|
};
|
||||||
|
|
||||||
|
~Bt856() = default;
|
||||||
|
|
||||||
|
// I2CDevice methods
|
||||||
|
void start_transaction() {
|
||||||
|
this->pos = 0; // reset read/write position
|
||||||
|
};
|
||||||
|
|
||||||
|
bool send_subaddress(uint8_t sub_addr) {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool send_byte(uint8_t data) {
|
||||||
|
switch (this->pos) {
|
||||||
|
case 0:
|
||||||
|
this->reg_num = data;
|
||||||
|
this->pos++;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
LOG_F(INFO, "Bt856: reg 0x%X set to 0x%X", this->reg_num, data);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG_F(WARNING, "Bt856: too much data received!");
|
||||||
|
return false; // return NACK
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool receive_byte(uint8_t* p_data) {
|
||||||
|
*p_data = 0x60; // return my device ID
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t my_addr = 0;
|
||||||
|
uint8_t reg_num = 0;
|
||||||
|
int pos = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class TaosVideo : public VideoCtrlBase, public MMIODevice {
|
||||||
|
public:
|
||||||
|
TaosVideo();
|
||||||
|
~TaosVideo() = default;
|
||||||
|
|
||||||
|
static std::unique_ptr<HWComponent> create() {
|
||||||
|
return std::unique_ptr<TaosVideo>(new TaosVideo());
|
||||||
|
}
|
||||||
|
|
||||||
|
// MMIODevice methods
|
||||||
|
uint32_t read(uint32_t rgn_start, uint32_t offset, int size) override;
|
||||||
|
void write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void enable_display();
|
||||||
|
void convert_frame_15bpp_indexed(uint8_t *dst_buf, int dst_pitch);
|
||||||
|
|
||||||
|
std::unique_ptr<AthensClocks> clk_gen = nullptr;
|
||||||
|
std::unique_ptr<Bt856> vid_enc = nullptr;
|
||||||
|
|
||||||
|
uint8_t gpio_cfg = 0;
|
||||||
|
uint8_t mon_id = MON_ID_NTSC;
|
||||||
|
uint8_t vsync_active = 0;
|
||||||
|
uint8_t video_mode = 0;
|
||||||
|
uint8_t *vram_ptr = nullptr;
|
||||||
|
uint32_t fb_base = 0;
|
||||||
|
uint32_t row_words = 0;
|
||||||
|
uint32_t color_mode = 0;
|
||||||
|
uint32_t crt_ctrl = 0;
|
||||||
|
uint32_t int_enables = 0;
|
||||||
|
uint32_t swatch_regs[15] = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TAOS_VIDEO_H
|
|
@ -77,7 +77,7 @@ static const PropMap Pippin_Settings = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static vector<string> Pippin_Devices = {
|
static vector<string> Pippin_Devices = {
|
||||||
"Aspen", "AspenPci1", "ScsiMesh", "MeshTnt", "GrandCentral"
|
"Aspen", "AspenPci1", "ScsiMesh", "MeshTnt", "GrandCentral", "TaosVideo"
|
||||||
};
|
};
|
||||||
|
|
||||||
static const MachineDescription Pippin_Descriptor = {
|
static const MachineDescription Pippin_Descriptor = {
|
||||||
|
|
Loading…
Reference in New Issue