diff --git a/devices/common/pci/bandit.cpp b/devices/common/pci/bandit.cpp index 6ebd6a1..5b5df92 100644 --- a/devices/common/pci/bandit.cpp +++ b/devices/common/pci/bandit.cpp @@ -81,15 +81,27 @@ uint32_t Bandit::pci_cfg_read(uint32_t reg_offs, uint32_t size) if (reg_offs < 64) { return PCIDevice::pci_cfg_read(reg_offs, size); } - - switch (reg_offs) { - case BANDIT_ADDR_MASK: - return BYTESWAP_32(this->addr_mask); - default: - LOG_F(WARNING, "%s: reading from unimplemented config register at 0x%X", - this->pci_name.c_str(), reg_offs); + + uint32_t offset = reg_offs & 3; + reg_offs &= ~3; + if (~-size & offset) { + LOG_F( + WARNING, "%s: unaligned read @%02x.%c", + this->name.c_str(), reg_offs + offset, + size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size + ); } + if (reg_offs == BANDIT_ADDR_MASK) { + return pci_cfg_rev_read(this->addr_mask, offset, size); + } + + LOG_F( + WARNING, "%s: reading from unimplemented config register @%02x.%c", + this->name.c_str(), reg_offs + offset, + size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size + ); + return 0; } @@ -100,15 +112,27 @@ void Bandit::pci_cfg_write(uint32_t reg_offs, uint32_t value, uint32_t size) return; } - switch (reg_offs) { - case BANDIT_ADDR_MASK: - this->addr_mask = BYTESWAP_32(value); - this->verbose_address_space(); - break; - default: - LOG_F(WARNING, "%s: writing to unimplemented config register at 0x%X", - this->pci_name.c_str(), reg_offs); + uint32_t offset = reg_offs & 3; + reg_offs &= ~3; + if (~-size & offset) { + LOG_F( + WARNING, "%s: unaligned write @%02x.%c = %0*x", + this->name.c_str(), reg_offs + offset, + size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size, size * 2, flip_sized(value, size) + ); } + + if (reg_offs == BANDIT_ADDR_MASK) { + this->addr_mask = pci_cfg_rev_write(this->addr_mask, offset, size, value); + this->verbose_address_space(); + return; + } + + LOG_F( + WARNING, "%s: writing to unimplemented config register @%02x.%c = %0*x", + this->name.c_str(), reg_offs + offset, + size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size, size * 2, flip_sized(value, size) + ); } uint32_t Bandit::read(uint32_t rgn_start, uint32_t offset, int size) @@ -147,10 +171,10 @@ uint32_t Bandit::read(uint32_t rgn_start, uint32_t offset, int size) } if (idsel == BANDIT_ID_SEL) { // access to myself - result = this->pci_cfg_read(reg_offs, size); + result = this->pci_cfg_read(reg_offs + (offset & 3), size); } else { if (this->dev_map.count(idsel)) { - result = this->dev_map[idsel]->pci_cfg_read(reg_offs, size); + result = this->dev_map[idsel]->pci_cfg_read(reg_offs + (offset & 3), size); } else { dev_num = WHAT_BIT_SET(idsel) + 11; LOG_F( @@ -196,7 +220,7 @@ void Bandit::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size LOG_F( WARNING, "%s: write config cycle type 1 not supported yet %02x:%02x.%x @%02x.%c = %0*x", this->name.c_str(), bus_num, dev_num, fun_num, reg_offs + (offset & 3), - size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size, size * 2, value + size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size, size * 2, flip_sized(value, size) ); return; } @@ -208,24 +232,24 @@ void Bandit::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size ERROR, "%s: write invalid IDSEL=0x%X config:0x%X ??:??.%x? @%02x?.%c = %0*x", this->name.c_str(), idsel, this->config_addr, fun_num, reg_offs + (offset & 3), - size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size, size * 2, value + size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size, size * 2, flip_sized(value, size) ); return; } if (idsel == BANDIT_ID_SEL) { // access to myself - this->pci_cfg_write(reg_offs, value, size); + this->pci_cfg_write(reg_offs + (offset & 3), value, size); return; } if (this->dev_map.count(idsel)) { - this->dev_map[idsel]->pci_cfg_write(reg_offs, value, size); + this->dev_map[idsel]->pci_cfg_write(reg_offs + (offset & 3), value, size); } else { dev_num = WHAT_BIT_SET(idsel) + 11; LOG_F( ERROR, "%s err: write attempt to non-existing PCI device ??:%02x.%x @%02x.%c = %0*x", this->name.c_str(), dev_num, fun_num, reg_offs + (offset & 3), - size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size, size * 2, value + size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size, size * 2, flip_sized(value, size) ); } } else { @@ -320,7 +344,7 @@ uint32_t Chaos::read(uint32_t rgn_start, uint32_t offset, int size) } if (this->dev_map.count(idsel)) { - result = this->dev_map[idsel]->pci_cfg_read(reg_offs, size); + result = this->dev_map[idsel]->pci_cfg_read(reg_offs + (offset & 3), size); } else { dev_num = WHAT_BIT_SET(idsel) + 11; LOG_F( @@ -358,7 +382,7 @@ void Chaos::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size) LOG_F( WARNING, "%s: write config cycle type 1 not supported yet %02x:%02x.%x @%02x.%c = %0*x", this->name.c_str(), bus_num, dev_num, fun_num, reg_offs + (offset & 3), - size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size, size * 2, value + size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size, size * 2, flip_sized(value, size) ); return; } @@ -370,19 +394,19 @@ void Chaos::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size) ERROR, "%s: write invalid IDSEL=0x%X config:0x%X ??:??.%x? @%02x?.%c = %0*x", this->name.c_str(), idsel, this->config_addr, fun_num, reg_offs + (offset & 3), - size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size, size * 2, value + size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size, size * 2, flip_sized(value, size) ); return; } if (this->dev_map.count(idsel)) { - this->dev_map[idsel]->pci_cfg_write(reg_offs, value, size); + this->dev_map[idsel]->pci_cfg_write(reg_offs + (offset & 3), value, size); } else { dev_num = WHAT_BIT_SET(idsel) + 11; LOG_F( ERROR, "%s err: write attempt to non-existing VCI device ??:%02x.%x @%02x.%c = %0*x", this->name.c_str(), dev_num, fun_num, reg_offs + (offset & 3), - size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size, size * 2, value + size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size, size * 2, flip_sized(value, size) ); } } else { diff --git a/devices/common/pci/pcidevice.cpp b/devices/common/pci/pcidevice.cpp index 41676b8..e301e48 100644 --- a/devices/common/pci/pcidevice.cpp +++ b/devices/common/pci/pcidevice.cpp @@ -52,6 +52,16 @@ uint32_t PCIDevice::pci_cfg_read(uint32_t reg_offs, uint32_t size) { uint32_t result; + uint32_t offset = reg_offs & 3; + reg_offs &= ~3; + if (~-size & offset) { + LOG_F( + WARNING, "%s: unaligned read @%02x.%c", + this->pci_name.c_str(), reg_offs + offset, + size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size + ); + } + switch (reg_offs) { case PCI_CFG_DEV_ID: result = (this->device_id << 16) | (this->vendor_id); @@ -89,31 +99,32 @@ uint32_t PCIDevice::pci_cfg_read(uint32_t reg_offs, uint32_t size) default: LOG_F( WARNING, "%s: attempt to read from reserved/unimplemented register @%02x.%c", - this->pci_name.c_str(), reg_offs, + this->pci_name.c_str(), reg_offs + offset, size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size ); return 0; } - if (size == 4) { - return BYTESWAP_32(result); - } else { - return read_mem_rev(((uint8_t *)&result) + (reg_offs & 3), size); - } + return pci_cfg_rev_read(result, offset, size); } void PCIDevice::pci_cfg_write(uint32_t reg_offs, uint32_t value, uint32_t size) { uint32_t data; - if (size == 4) { - data = BYTESWAP_32(value); - } else { - // get current register content as DWORD and update it partially - data = BYTESWAP_32(this->pci_cfg_read(reg_offs, 4)); - write_mem_rev(((uint8_t *)&data) + (reg_offs & 3), value, size); + uint32_t offset = reg_offs & 3; + reg_offs &= ~3; + if (~-size & offset) { + LOG_F( + WARNING, "%s: unaligned write @%02x.%c = %0*x", + this->pci_name.c_str(), reg_offs + offset, + size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size, size * 2, flip_sized(value, size) + ); } + // get current register content as DWORD and update it partially + data = pci_cfg_rev_write(size == 4 ? 0 : BYTESWAP_32(this->pci_cfg_read(reg_offs, 4)), offset, size, value); + switch (reg_offs) { case PCI_CFG_STAT_CMD: this->pci_wr_stat(data >> 16); @@ -137,10 +148,10 @@ void PCIDevice::pci_cfg_write(uint32_t reg_offs, uint32_t value, uint32_t size) } break; case PCI_CFG_ROM_BAR: - if (data == 0xFFFFF800UL) { - this->exp_rom_bar = this->exp_bar_cfg; + if ((data & this->exp_bar_cfg) == this->exp_bar_cfg) { + this->exp_rom_bar = (data & (this->exp_bar_cfg | 1)); } else { - this->exp_rom_bar = (data & 0xFFFFF801UL); + this->exp_rom_bar = (data & (this->exp_bar_cfg | 1)); if (this->exp_rom_bar & 1) { this->map_exp_rom_mem(); } else { @@ -156,7 +167,7 @@ void PCIDevice::pci_cfg_write(uint32_t reg_offs, uint32_t value, uint32_t size) LOG_F( WARNING, "%s: attempt to write to reserved/unimplemented register @%02x.%c = %0*x", this->pci_name.c_str(), reg_offs, - size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size, size * 2, value + size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size, size * 2, flip_sized(value, size) ); } } @@ -243,7 +254,7 @@ void PCIDevice::set_bar_value(int bar_num, uint32_t value) { uint32_t bar_cfg = this->bars_cfg[bar_num]; if (bar_cfg & 1) { - this->bars[bar_num] = (value & 0xFFFFFFFCUL) | 1; + this->bars[bar_num] = (value & 0xFFFFFFFCUL) | (bar_cfg & 3); } else { if (bar_cfg & 6) { ABORT_F("Invalid or unsupported PCI space type: %d", (bar_cfg >> 1) & 3); @@ -257,7 +268,7 @@ void PCIDevice::map_exp_rom_mem() { uint32_t rom_addr, rom_size; - rom_addr = this->exp_rom_bar & 0xFFFFF800UL; + rom_addr = this->exp_rom_bar & this->exp_bar_cfg; rom_size = ~this->exp_bar_cfg + 1; if (!this->exp_rom_addr || this->exp_rom_addr != rom_addr) { diff --git a/devices/floppy/floppyimg.cpp b/devices/floppy/floppyimg.cpp index 47e1db3..7298cad 100644 --- a/devices/floppy/floppyimg.cpp +++ b/devices/floppy/floppyimg.cpp @@ -64,7 +64,7 @@ static FlopImgType identify_image(std::ifstream& img_file) } } - return FlopImgType::UNKNOWN; + return FlopImgType::RAW; } static int64_t get_hfs_vol_size(const uint8_t *mdb_data) @@ -133,17 +133,16 @@ int RawFloppyImg::calc_phys_params() } else if (buf[0] == 0xD2 && buf[1] == 0xD7) { // check MFS volume size } else { - LOG_F(ERROR, "RawFloppyImg: unknown volume type!"); - return -1; + LOG_F(WARNING, "RawFloppyImg: unknown volume type!"); } - if (vol_size > this->img_size) { + if (vol_size && (vol_size > this->img_size)) { LOG_F(INFO, "RawFloppyImg: volume size > image size!"); LOG_F(INFO, "Volume size: %llu, Image size: %d", vol_size, this->img_size); return -1; } - // raw images don't include anything than raw disk data + // raw images don't include anything other than raw disk data this->data_size = this->img_size; // guess disk format from image file size diff --git a/devices/memctrl/mpc106.cpp b/devices/memctrl/mpc106.cpp index 7ca2ab4..bfe4d33 100644 --- a/devices/memctrl/mpc106.cpp +++ b/devices/memctrl/mpc106.cpp @@ -89,7 +89,7 @@ uint32_t MPC106::read(uint32_t rgn_start, uint32_t offset, int size) { } else { if (offset >= 0x200000) { if (this->config_addr & 0x80) // process only if bit E (enable) is set - return pci_read(size); + return pci_read(offset & 3, size); } } @@ -113,39 +113,39 @@ void MPC106::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size this->config_addr = value; } else { if (this->config_addr & 0x80) // process only if bit E (enable) is set - return pci_write(value, size); + return pci_write(offset & 3, value, size); } } } -uint32_t MPC106::pci_read(uint32_t size) { +uint32_t MPC106::pci_read(uint32_t offset, uint32_t size) { int bus_num, dev_num, fun_num, reg_offs; - bus_num = (this->config_addr >> 8) & 0xFF; + bus_num = (this->config_addr >> 8) & 0xFF; dev_num = (this->config_addr >> 19) & 0x1F; fun_num = (this->config_addr >> 16) & 0x07; reg_offs = (this->config_addr >> 24) & 0xFC; if (bus_num) { - LOG_F( - ERROR, - "%s err: read attempt from non-local PCI bus, config_addr = %x %02x:%02x.%x @%02x.%c", - this->name.c_str(), this->config_addr, bus_num, dev_num, fun_num, reg_offs, - size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size - ); + LOG_F( + ERROR, + "%s err: read attempt from non-local PCI bus, config_addr = %x, offset = %x %02x:%02x.%x @%02x.%c", + this->name.c_str(), this->config_addr, offset, bus_num, dev_num, fun_num, reg_offs, + size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size + ); return 0xFFFFFFFFUL; // PCI spec §6.1 } if (dev_num == 0 && fun_num == 0) { // dev_num 0 is assigned to myself - return this->pci_cfg_read(reg_offs, size); + return this->pci_cfg_read(reg_offs + offset, size); } else { if (this->dev_map.count(dev_num)) { - return this->dev_map[dev_num]->pci_cfg_read(reg_offs, size); + return this->dev_map[dev_num]->pci_cfg_read(reg_offs + offset, size); } else { LOG_F( ERROR, "%s err: read attempt from non-existing PCI device %02x:%02x.%x @%02x.%c", - this->name.c_str(), bus_num, dev_num, fun_num, reg_offs, + this->name.c_str(), bus_num, dev_num, fun_num, reg_offs + offset, size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size ); return 0xFFFFFFFFUL; // PCI spec §6.1 @@ -155,10 +155,10 @@ uint32_t MPC106::pci_read(uint32_t size) { return 0; } -void MPC106::pci_write(uint32_t value, uint32_t size) { +void MPC106::pci_write(uint32_t offset, uint32_t value, uint32_t size) { int bus_num, dev_num, fun_num, reg_offs; - bus_num = (this->config_addr >> 8) & 0xFF; + bus_num = (this->config_addr >> 8) & 0xFF; dev_num = (this->config_addr >> 19) & 0x1F; fun_num = (this->config_addr >> 16) & 0x07; reg_offs = (this->config_addr >> 24) & 0xFC; @@ -166,26 +166,26 @@ void MPC106::pci_write(uint32_t value, uint32_t size) { if (bus_num) { LOG_F( ERROR, - "%s err: write attempt to non-local PCI bus, config_addr = %x %02x:%02x.%x @%02x.%c = %0*x", - this->name.c_str(), this->config_addr, bus_num, dev_num, fun_num, reg_offs, - size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size, - size * 2, value - ); + "%s err: write attempt to non-local PCI bus, config_addr = %x, offset = %x %02x:%02x.%x @%02x.%c = %0*x", + this->name.c_str(), this->config_addr, offset, bus_num, dev_num, fun_num, reg_offs + offset, + size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size, + size * 2, flip_sized(value, size) + ); return; } if (dev_num == 0 && fun_num == 0) { // dev_num 0 is assigned to myself - this->pci_cfg_write(reg_offs, value, size); + this->pci_cfg_write(reg_offs + offset, value, size); } else { if (this->dev_map.count(dev_num)) { - this->dev_map[dev_num]->pci_cfg_write(reg_offs, value, size); + this->dev_map[dev_num]->pci_cfg_write(reg_offs + offset, value, size); } else { LOG_F( ERROR, "%s err: write attempt to non-existing PCI device %02x:%02x.%x @%02x.%c = %0*x", - this->name.c_str(), bus_num, dev_num, fun_num, reg_offs, + this->name.c_str(), bus_num, dev_num, fun_num, reg_offs + offset, size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size, - size * 2, value + size * 2, flip_sized(value, size) ); } } @@ -200,7 +200,17 @@ uint32_t MPC106::pci_cfg_read(uint32_t reg_offs, uint32_t size) { return PCIDevice::pci_cfg_read(reg_offs, size); } - return read_mem(&this->my_pci_cfg_hdr[reg_offs], size); + uint32_t offset = reg_offs & 3; + reg_offs &= ~3; + if (~-size & offset) { + LOG_F( + WARNING, "%s: unaligned read @%02x.%c", + this->pci_name.c_str(), reg_offs + offset, + size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size + ); + } + + return pci_cfg_rev_read(READ_DWORD_LE_A(&this->my_pci_cfg_hdr[reg_offs]), offset, size); } void MPC106::pci_cfg_write(uint32_t reg_offs, uint32_t value, uint32_t size) { @@ -213,9 +223,20 @@ void MPC106::pci_cfg_write(uint32_t reg_offs, uint32_t value, uint32_t size) { return; } + uint32_t offset = reg_offs & 3; + reg_offs &= ~3; + if (~-size & offset) { + LOG_F( + WARNING, "%s: unaligned write @%02x.%c = %0*x", + this->pci_name.c_str(), reg_offs + offset, + size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size, size * 2, flip_sized(value, size) + ); + } + // FIXME: implement write-protection for read-only registers - write_mem(&this->my_pci_cfg_hdr[reg_offs], value, size); + uint32_t *addr = (uint32_t *)&this->my_pci_cfg_hdr[reg_offs]; + WRITE_DWORD_LE_A(addr, pci_cfg_rev_write(READ_DWORD_LE_A(addr), offset, size, value)); if (this->my_pci_cfg_hdr[0xF2] & 8) { #ifdef MPC106_DEBUG diff --git a/devices/memctrl/mpc106.h b/devices/memctrl/mpc106.h index 5d62df2..99de748 100644 --- a/devices/memctrl/mpc106.h +++ b/devices/memctrl/mpc106.h @@ -59,8 +59,8 @@ public: protected: /* PCI access */ - uint32_t pci_read(uint32_t size); - void pci_write(uint32_t value, uint32_t size); + uint32_t pci_read(uint32_t offset, uint32_t size); + void pci_write(uint32_t offset, uint32_t value, uint32_t size); /* my own PCI configuration registers access */ uint32_t pci_cfg_read(uint32_t reg_offs, uint32_t size); diff --git a/devices/video/atirage.cpp b/devices/video/atirage.cpp index 5efb89b..3b6a556 100644 --- a/devices/video/atirage.cpp +++ b/devices/video/atirage.cpp @@ -167,11 +167,25 @@ uint32_t ATIRage::pci_cfg_read(uint32_t reg_offs, uint32_t size) return PCIDevice::pci_cfg_read(reg_offs, size); } + uint32_t offset = reg_offs & 3; + reg_offs &= ~3; + if (~-size & offset) { + LOG_F( + WARNING, "%s: unaligned read @%02x.%c", + this->pci_name.c_str(), reg_offs + offset, + size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size + ); + } + switch (reg_offs) { case 0x40: - return this->user_cfg; + return pci_cfg_rev_read(this->user_cfg, offset, size); default: - LOG_F(WARNING, "ATIRage: reading from unimplemented config register at 0x%X", reg_offs); + LOG_F( + WARNING, "%s: reading from unimplemented config register @%02x.%c", + this->pci_name.c_str(), reg_offs + offset, + size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size + ); } return 0; @@ -181,14 +195,29 @@ void ATIRage::pci_cfg_write(uint32_t reg_offs, uint32_t value, uint32_t size) { if (reg_offs < 64) { PCIDevice::pci_cfg_write(reg_offs, value, size); - } else { - switch (reg_offs) { - case 0x40: - this->user_cfg = value; - break; - default: - LOG_F(WARNING, "ATIRage: writing to unimplemented config register at 0x%X", reg_offs); - } + return; + } + + uint32_t offset = reg_offs & 3; + reg_offs &= ~3; + if (~-size & offset) { + LOG_F( + WARNING, "%s: unaligned write @%02x.%c = %0*x", + this->pci_name.c_str(), reg_offs + offset, + size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size, size * 2, flip_sized(value, size) + ); + } + + switch (reg_offs) { + case 0x40: + this->user_cfg = pci_cfg_rev_write(this->user_cfg, offset, size, value); + break; + default: + LOG_F( + WARNING, "%s: writing to unimplemented config register @%02x.%c = %0*x", + this->pci_name.c_str(), reg_offs + offset, + size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size, size * 2, flip_sized(value, size) + ); } } diff --git a/devices/video/control.cpp b/devices/video/control.cpp index b15b4c2..cfbcedc 100644 --- a/devices/video/control.cpp +++ b/devices/video/control.cpp @@ -59,6 +59,7 @@ ControlVideo::ControlVideo() this->vendor_id = PCI_VENDOR_APPLE; this->device_id = 3; this->class_rev = 0; + this->bars_cfg[0] = 0xFFFFFFFFUL; // I/O region (4 bytes but it's weird because bit 1 is set) this->bars_cfg[1] = 0xFFFFF000UL; // base address for the HW registers (4KB) this->bars_cfg[2] = 0xFC000000UL; // base address for the VRAM (64MB) @@ -84,6 +85,10 @@ ControlVideo::ControlVideo() void ControlVideo::notify_bar_change(int bar_num) { switch (bar_num) { + case 0: + this->io_base = this->bars[bar_num] & ~3; + LOG_F(INFO, "Control: I/O space address set to 0x%08X", this->io_base); + break; case 1: if (this->regs_base != (this->bars[bar_num] & 0xFFFFFFF0UL)) { this->regs_base = this->bars[bar_num] & 0xFFFFFFF0UL; @@ -116,18 +121,22 @@ uint32_t ControlVideo::read(uint32_t rgn_start, uint32_t offset, int size) } } - switch (offset >> 4) { - case ControlRegs::TEST: - result = this->test; - break; - case ControlRegs::MON_SENSE: - result = this->cur_mon_id << 6; - break; - default: - LOG_F(INFO, "read from 0x%08X:0x%08X", rgn_start, offset); + if (rgn_start == this->regs_base) { + switch (offset >> 4) { + case ControlRegs::TEST: + result = this->test; + break; + case ControlRegs::MON_SENSE: + result = this->cur_mon_id << 6; + break; + default: + LOG_F(INFO, "read from 0x%08X:0x%08X", rgn_start, offset); + } + + return BYTESWAP_32(result); } - return BYTESWAP_32(result); + return 0; } void ControlVideo::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size) @@ -141,75 +150,77 @@ void ControlVideo::write(uint32_t rgn_start, uint32_t offset, uint32_t value, in return; } - value = BYTESWAP_32(value); + if (rgn_start == this->regs_base) { + value = BYTESWAP_32(value); - switch (offset >> 4) { - case ControlRegs::VFPEQ: - case ControlRegs::VFP: - case ControlRegs::VAL: - case ControlRegs::VBP: - case ControlRegs::VBPEQ: - case ControlRegs::VSYNC: - case ControlRegs::VHLINE: - case ControlRegs::PIPED: - case ControlRegs::HPIX: - case ControlRegs::HFP: - case ControlRegs::HAL: - case ControlRegs::HBWAY: - case ControlRegs::HSP: - case ControlRegs::HEQ: - case ControlRegs::HLFLN: - case ControlRegs::HSERR: - this->swatch_params[(offset >> 4) - 1] = value; - break; - case ControlRegs::TEST: - if (this->test != value) { - if ((this->test & ~TEST_STROBE) != (value & ~TEST_STROBE)) { - this->test = value; - this->test_shift = 0; - LOG_F(9, "New TEST value: 0x%08X", this->test); - } else { - LOG_F(9, "TEST strobe bit flipped, new value: 0x%08X", value); - this->test = value; - if (++this->test_shift >= 3) { - LOG_F(9, "Received TEST reg value: 0x%08X", this->test & ~TEST_STROBE); - if ((this->test ^ this->prev_test) & 0x400) { - if (this->test & 0x400) { - this->disable_display(); - } else { - this->enable_display(); + switch (offset >> 4) { + case ControlRegs::VFPEQ: + case ControlRegs::VFP: + case ControlRegs::VAL: + case ControlRegs::VBP: + case ControlRegs::VBPEQ: + case ControlRegs::VSYNC: + case ControlRegs::VHLINE: + case ControlRegs::PIPED: + case ControlRegs::HPIX: + case ControlRegs::HFP: + case ControlRegs::HAL: + case ControlRegs::HBWAY: + case ControlRegs::HSP: + case ControlRegs::HEQ: + case ControlRegs::HLFLN: + case ControlRegs::HSERR: + this->swatch_params[(offset >> 4) - 1] = value; + break; + case ControlRegs::TEST: + if (this->test != value) { + if ((this->test & ~TEST_STROBE) != (value & ~TEST_STROBE)) { + this->test = value; + this->test_shift = 0; + LOG_F(9, "New TEST value: 0x%08X", this->test); + } else { + LOG_F(9, "TEST strobe bit flipped, new value: 0x%08X", value); + this->test = value; + if (++this->test_shift >= 3) { + LOG_F(9, "Received TEST reg value: 0x%08X", this->test & ~TEST_STROBE); + if ((this->test ^ this->prev_test) & 0x400) { + if (this->test & 0x400) { + this->disable_display(); + } else { + this->enable_display(); + } + this->prev_test = this->test; } - this->prev_test = this->test; } } } + break; + case ControlRegs::GBASE: + this->fb_base = value; + break; + case ControlRegs::ROW_WORDS: + this->row_words = value; + break; + case ControlRegs::MON_SENSE: + LOG_F(9, "Control: monitor sense written with 0x%X", value); + value = (value >> 3) & 7; + this->cur_mon_id = this->display_id->read_monitor_sense(value & 7, value ^ 7); + break; + case ControlRegs::ENABLE: + this->flags = value; + break; + case ControlRegs::GSC_DIVIDE: + this->clock_divider = value; + break; + case ControlRegs::REFRESH_COUNT: + LOG_F(INFO, "Control: refresh count set to 0x%08X", value); + break; + case ControlRegs::INT_ENABLE: + this->int_enable = value; + break; + default: + LOG_F(INFO, "write 0x%08X to 0x%08X:0x%08X", value, rgn_start, offset); } - break; - case ControlRegs::GBASE: - this->fb_base = value; - break; - case ControlRegs::ROW_WORDS: - this->row_words = value; - break; - case ControlRegs::MON_SENSE: - LOG_F(9, "Control: monitor sense written with 0x%X", value); - value = (value >> 3) & 7; - this->cur_mon_id = this->display_id->read_monitor_sense(value & 7, value ^ 7); - break; - case ControlRegs::ENABLE: - this->flags = value; - break; - case ControlRegs::GSC_DIVIDE: - this->clock_divider = value; - break; - case ControlRegs::REFRESH_COUNT: - LOG_F(INFO, "Control: refresh count set to 0x%08X", value); - break; - case ControlRegs::INT_ENABLE: - this->int_enable = value; - break; - default: - LOG_F(INFO, "write 0x%08X to 0x%08X:0x%08X", value, rgn_start, offset); } } diff --git a/devices/video/control.h b/devices/video/control.h index 323d695..89aa376 100644 --- a/devices/video/control.h +++ b/devices/video/control.h @@ -117,6 +117,7 @@ private: std::unique_ptr vram_ptr; uint32_t vram_size; + uint32_t io_base = 0; uint32_t vram_base = 0; uint32_t regs_base = 0; uint32_t prev_test = 0x433; diff --git a/memaccess.h b/memaccess.h index 46bc60c..c90c11c 100644 --- a/memaccess.h +++ b/memaccess.h @@ -177,6 +177,60 @@ inline uint32_t read_mem_rev(const uint8_t* buf, uint32_t size) { } } +/* value is dword from PCI config. MSB..LSB of value is stored in PCI config as 0:LSB..3:MSB. + result is part of value at byte offset from LSB and size bytes (with wrap around) and flipped as required for pci_cfg_read result. */ +inline uint32_t pci_cfg_rev_read(uint32_t value, uint32_t offset, uint32_t size) { + switch (size << 2 | offset) { + case 0x04: return value & 0xff; // 0 + case 0x05: return (value >> 8) & 0xff; // 1 + case 0x06: return (value >> 16) & 0xff; // 2 + case 0x07: return (value >> 24) & 0xff; // 3 + + case 0x08: return ((value & 0xff) << 8) | ((value >> 8) & 0xff); // 0 1 + case 0x09: return ( value & 0xff00) | ((value >> 16) & 0xff); // 1 2 + case 0x0a: return ((value >> 8) & 0xff00) | ((value >> 24) & 0xff); // 2 3 + case 0x0b: return ((value >> 16) & 0xff00) | ( value & 0xff); // 3 0 + + case 0x10: return ((value & 0xff) << 24) | ((value & 0xff00) << 8) | ((value >> 8) & 0xff00) | ((value >> 24) & 0xff); // 0 1 2 3 + case 0x11: return ((value & 0xff00) << 16) | ( value & 0xff0000) | ((value >> 16) & 0xff00) | ( value & 0xff); // 1 2 3 0 + case 0x12: return ((value & 0xff0000) << 8) | ((value >> 8) & 0xff0000) | ((value & 0xff) << 8) | ((value >> 8) & 0xff); // 2 3 0 1 + case 0x13: return ( value & 0xff000000) | ((value & 0xff) << 16) | ( value & 0xff00) | ((value >> 16) & 0xff); // 3 0 1 2 + default: LOG_F(ERROR, "pci_cfg_rev: invalid offset %d for size %d!", offset, size); return 0xffffffff; + } +} + +/* value is dword from PCI config. MSB..LSB of value (3.2.1.0) is stored in PCI config as 0:LSB..3:MSB. + data is flipped bytes (d0.d1.d2.d3, as passed to pci_cfg_write) to be merged into value. + result is part of value at byte offset from LSB and size bytes (with wrap around) modified by data. */ +inline uint32_t pci_cfg_rev_write(uint32_t value, uint32_t offset, uint32_t size, uint32_t data) { + switch (size << 2 | offset) { + case 0x04: return (value & 0xffffff00) | (data & 0xff); // 3 2 1 d0 + case 0x05: return (value & 0xffff00ff) | ((data & 0xff) << 8); // 3 2 d0 0 + case 0x06: return (value & 0xff00ffff) | ((data & 0xff) << 16); // 3 d0 1 0 + case 0x07: return (value & 0x00ffffff) | ((data & 0xff) << 24); // d0 2 1 0 + + case 0x08: return (value & 0xffff0000) | ((data >> 8) & 0xff) | ((data & 0xff) << 8); // 3 2 d1 d0 + case 0x09: return (value & 0xff0000ff) | (data & 0xff00) | ((data & 0xff) << 16); // 3 d1 d0 0 + case 0x0a: return (value & 0x0000ffff) | ((data & 0xff00) << 8) | ((data & 0xff) << 24); // d1 d0 1 0 + case 0x0b: return (value & 0x00ffff00) | ((data & 0xff00) << 16) | (data & 0xff); // d0 2 1 d1 + + case 0x10: return ((data & 0xff) << 24) | ((data & 0xff00) << 8) | ((data >> 8) & 0xff00) | ((data >> 24) & 0xff); // d3 d2 d1 d0 + case 0x11: return ((data & 0xff00) << 16) | ( data & 0xff0000) | ((data >> 16) & 0xff00) | ( data & 0xff); // d2 d1 d0 d3 + case 0x12: return ((data & 0xff0000) << 8) | ((data >> 8) & 0xff0000) | ((data & 0xff) << 8) | ((data >> 8) & 0xff); // d1 d0 d3 d2 + case 0x13: return ( data & 0xff000000) | ((data & 0xff) << 16) | ( data & 0xff00) | ((data >> 16) & 0xff); // d0 d3 d2 d1 + default: LOG_F(ERROR, "pci_cfg_rev: invalid offset %d for size %d!", offset, size); return 0xffffffff; + } +} + +inline uint32_t flip_sized(uint32_t value, uint32_t size) { + switch (size) { + case 1: return value; + case 2: return BYTESWAP_16(value); + case 4: return BYTESWAP_32(value); + default: LOG_F(ERROR, "flip_sized: invalid size %d!", size); return 0xffffffff; + } +} + /* write the specified value of the specified size to memory pointed to by addr, perform necessary byte swapping so that the byte order of the destination remains unchanged. */