// license:BSD-3-Clause // copyright-holders:Olivier Galibert /* * References: * - PCI local bus (rev 2.x) * - https://wiki.osdev.org/PCI */ #include "emu.h" #include "pci.h" #include "bus/pci/pci_slot.h" DEFINE_DEVICE_TYPE(PCI_ROOT, pci_root_device, "pci_root", "PCI virtual root") DEFINE_DEVICE_TYPE(PCI_BRIDGE, pci_bridge_device, "pci_bridge", "PCI-PCI Bridge") void pci_device::config_map(address_map &map) { map(0x00, 0x01).r(FUNC(pci_device::vendor_r)); map(0x02, 0x03).r(FUNC(pci_device::device_r)); map(0x04, 0x05).rw(FUNC(pci_device::command_r), FUNC(pci_device::command_w)); map(0x06, 0x07).r(FUNC(pci_device::status_r)); map(0x08, 0x0b).r(FUNC(pci_device::class_rev_r)); map(0x0c, 0x0c).r(FUNC(pci_device::cache_line_size_r)); map(0x0d, 0x0d).r(FUNC(pci_device::latency_timer_r)); map(0x0e, 0x0e).r(FUNC(pci_device::header_type_r)); map(0x0f, 0x0f).r(FUNC(pci_device::bist_r)); map(0x0c, 0x0f).nopw(); map(0x10, 0x27).rw(FUNC(pci_device::address_base_r), FUNC(pci_device::address_base_w)); // Cardbus CIS pointer at 28 map(0x2c, 0x2d).r(FUNC(pci_device::subvendor_r)); map(0x2e, 0x2f).r(FUNC(pci_device::subsystem_r)); map(0x2c, 0x2f).nopw(); map(0x30, 0x33).rw(FUNC(pci_device::expansion_base_r), FUNC(pci_device::expansion_base_w)); map(0x34, 0x34).r(FUNC(pci_device::capptr_r)); map(0x3c, 0x3c).rw(FUNC(pci_device::interrupt_line_r), FUNC(pci_device::interrupt_line_w)); map(0x3d, 0x3d).rw(FUNC(pci_device::interrupt_pin_r), FUNC(pci_device::interrupt_pin_w)); } void pci_bridge_device::config_map(address_map &map) { map(0x00, 0x01).r(FUNC(pci_bridge_device::vendor_r)); map(0x02, 0x03).r(FUNC(pci_bridge_device::device_r)); map(0x04, 0x05).rw(FUNC(pci_bridge_device::command_r), FUNC(pci_bridge_device::command_w)); map(0x06, 0x07).r(FUNC(pci_bridge_device::status_r)); map(0x08, 0x0b).r(FUNC(pci_bridge_device::class_rev_r)); map(0x0c, 0x0c).r(FUNC(pci_bridge_device::cache_line_size_r)); map(0x0d, 0x0d).r(FUNC(pci_bridge_device::latency_timer_r)); map(0x0e, 0x0e).r(FUNC(pci_bridge_device::header_type_r)); map(0x0f, 0x0f).r(FUNC(pci_bridge_device::bist_r)); map(0x10, 0x17).rw(FUNC(pci_bridge_device::b_address_base_r), FUNC(pci_bridge_device::b_address_base_w)); map(0x18, 0x18).rw(FUNC(pci_bridge_device::primary_bus_r), FUNC(pci_bridge_device::primary_bus_w)); map(0x19, 0x19).rw(FUNC(pci_bridge_device::secondary_bus_r), FUNC(pci_bridge_device::secondary_bus_w)); map(0x1a, 0x1a).rw(FUNC(pci_bridge_device::subordinate_bus_r), FUNC(pci_bridge_device::subordinate_bus_w)); map(0x1b, 0x1b).rw(FUNC(pci_bridge_device::secondary_latency_r), FUNC(pci_bridge_device::secondary_latency_w)); map(0x1c, 0x1c).rw(FUNC(pci_bridge_device::iobase_r), FUNC(pci_bridge_device::iobase_w)); map(0x1d, 0x1d).rw(FUNC(pci_bridge_device::iolimit_r), FUNC(pci_bridge_device::iolimit_w)); map(0x1e, 0x1f).rw(FUNC(pci_bridge_device::secondary_status_r), FUNC(pci_bridge_device::secondary_status_w)); map(0x20, 0x21).rw(FUNC(pci_bridge_device::memory_base_r), FUNC(pci_bridge_device::memory_base_w)); map(0x22, 0x23).rw(FUNC(pci_bridge_device::memory_limit_r), FUNC(pci_bridge_device::memory_limit_w)); map(0x24, 0x25).rw(FUNC(pci_bridge_device::prefetch_base_r), FUNC(pci_bridge_device::prefetch_base_w)); map(0x26, 0x27).rw(FUNC(pci_bridge_device::prefetch_limit_r), FUNC(pci_bridge_device::prefetch_limit_w)); map(0x28, 0x2b).rw(FUNC(pci_bridge_device::prefetch_baseu_r), FUNC(pci_bridge_device::prefetch_baseu_w)); map(0x2c, 0x2f).rw(FUNC(pci_bridge_device::prefetch_limitu_r), FUNC(pci_bridge_device::prefetch_limitu_w)); map(0x30, 0x31).rw(FUNC(pci_bridge_device::iobaseu_r), FUNC(pci_bridge_device::iobaseu_w)); map(0x32, 0x33).rw(FUNC(pci_bridge_device::iolimitu_r), FUNC(pci_bridge_device::iolimitu_w)); map(0x34, 0x34).r(FUNC(pci_bridge_device::capptr_r)); map(0x38, 0x3b).rw(FUNC(pci_bridge_device::expansion_base_r), FUNC(pci_bridge_device::expansion_base_w)); map(0x3c, 0x3c).rw(FUNC(pci_bridge_device::interrupt_line_r), FUNC(pci_bridge_device::interrupt_line_w)); map(0x3d, 0x3d).rw(FUNC(pci_bridge_device::interrupt_pin_r), FUNC(pci_bridge_device::interrupt_pin_w)); map(0x3e, 0x3f).rw(FUNC(pci_bridge_device::bridge_control_r), FUNC(pci_bridge_device::bridge_control_w)); } pci_device::pci_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, type, tag, owner, clock) , m_region(*this, DEVICE_SELF) { main_id = 0xffffffff; revision = 0x00; pclass = 0xffffff; subsystem_id = 0xffffffff; expansion_rom = nullptr; expansion_rom_size = 0; expansion_rom_base = 0; is_multifunction_device = false; intr_pin = 0x0; intr_line = 0xff; for(int i=0; i<6; i++) { bank_infos[i].adr = -1; bank_infos[i].size = 0; bank_infos[i].flags = 0; bank_reg_infos[i].bank = -1; bank_reg_infos[i].hi = 0; } } // main_id << 16 = vendor ID ($00-$01) // main_id & 0xffff = device ID ($02-$03) // revision = board versioning ($08) // pclass = programming interface/sub class code/base class code ($09-$0b) // subsystem_id << 16 = sub vendor ID ($2c-$2d) - NB: not all cards have these // subsystem_id & 0xffff = sub device ID ($2e-$2f) / void pci_device::set_ids(uint32_t _main_id, uint8_t _revision, uint32_t _pclass, uint32_t _subsystem_id) { main_id = _main_id; revision = _revision; pclass = _pclass; subsystem_id = _subsystem_id; } void pci_device::device_start() { command = 0x0000; command_mask = 0x01bf; status = 0x0000; bank_count = 0; bank_reg_count = 0; save_item(STRUCT_MEMBER(bank_infos, adr)); save_item(NAME(command)); save_item(NAME(command_mask)); save_item(NAME(status)); save_item(NAME(intr_line)); save_item(NAME(intr_pin)); device_t *root = owner(); while(root && root->type() != PCI_ROOT) root = root->owner(); if(!root) fatalerror("PCI device %s is without a PCI root\n", tag()); m_pci_root = downcast(root); } void pci_device::device_reset() { } uint32_t pci_device::unmapped_r(offs_t offset, uint32_t mem_mask, int bank) { logerror("%s: unmapped read from %08x & %08x (%s)\n", machine().describe_context(), offset*4, mem_mask, bank_infos[bank].map.name()); return 0; } void pci_device::unmapped_w(offs_t offset, uint32_t data, uint32_t mem_mask, int bank) { logerror("%s: unmapped write to %08x = %08x & %08x (%s)\n", machine().describe_context(), offset*4, data, mem_mask, bank_infos[bank].map.name()); } uint32_t pci_device::unmapped0_r(offs_t offset, uint32_t mem_mask) { return unmapped_r(offset, mem_mask, 0); } void pci_device::unmapped0_w(offs_t offset, uint32_t data, uint32_t mem_mask) { return unmapped_w(offset, data, mem_mask, 0); } uint32_t pci_device::unmapped1_r(offs_t offset, uint32_t mem_mask) { return unmapped_r(offset, mem_mask, 1); } void pci_device::unmapped1_w(offs_t offset, uint32_t data, uint32_t mem_mask) { return unmapped_w(offset, data, mem_mask, 1); } uint32_t pci_device::unmapped2_r(offs_t offset, uint32_t mem_mask) { return unmapped_r(offset, mem_mask, 2); } void pci_device::unmapped2_w(offs_t offset, uint32_t data, uint32_t mem_mask) { return unmapped_w(offset, data, mem_mask, 2); } uint32_t pci_device::unmapped3_r(offs_t offset, uint32_t mem_mask) { return unmapped_r(offset, mem_mask, 3); } void pci_device::unmapped3_
#define SOL_CHECK_ARGUMENTS

#include <catch.hpp>
#include <sol.hpp>

TEST_CASE("threading/coroutines", "ensure calling a coroutine works") {
	const auto& script = R"(counter = 20
 
function loop()
    while counter ~= 30
    do
        coroutine.yield(counter);
        counter = counter + 1;
    end
    return counter
end
)";

	sol::state lua;
	lua.open_libraries(sol::lib::base, sol::lib::coroutine);
	lua.script(script);
	sol::coroutine cr = lua["loop"];

	int counter;
	for (counter = 20; counter < 31 && cr; ++counter) {
		int value = cr();
		if (counter != value) {
			throw std::logic_error("fuck");
		}
	}
	counter -= 1;
	REQUIRE(counter == 30);
}

TEST_CASE("threading/new-thread-coroutines", "ensure calling a coroutine works when the work is put on a different thread") {
	const auto& script = R"(counter = 20
 
function loop()
    while counter ~= 30
    do
        coroutine.yield(counter);
        counter = counter + 1;
    end
    return counter
end
)";

	sol::state lua;
	lua.open_libraries(sol::lib::base, sol::lib::coroutine);
	lua.script(script);
	sol::thread runner = sol::thread::create(lua.lua_state());
	sol::state_view runnerstate = runner.state();
	sol::coroutine cr = runnerstate["loop"];

	int counter;
	for (counter = 20; counter < 31 && cr; ++counter) {
		int value = cr();
		if (counter != value) {
			throw std::logic_error("fuck");
		}
	}
	counter -= 1;
	REQUIRE(counter == 30);
}
_bus; } void pci_bridge_device::primary_bus_w(uint8_t data) { primary_bus = data; logerror("primary_bus_w %02x\n", data); } uint8_t pci_bridge_device::secondary_bus_r() { logerror("secondary_bus_r\n"); return secondary_bus; } void pci_bridge_device::secondary_bus_w(uint8_t data) { secondary_bus = data; logerror("secondary_bus_w %02x\n", data); } uint8_t pci_bridge_device::subordinate_bus_r() { logerror("subordinate_bus_r\n"); return subordinate_bus; } void pci_bridge_device::subordinate_bus_w(uint8_t data) { subordinate_bus = data; logerror("subordinate_bus_w %02x\n", data); } uint8_t pci_bridge_device::secondary_latency_r() { logerror("secondary_latency_r\n"); return 0xff; } void pci_bridge_device::secondary_latency_w(uint8_t data) { logerror("secondary_latency_w %02x\n", data); } uint8_t pci_bridge_device::iobase_r() { return iobase; } void pci_bridge_device::iobase_w(uint8_t data) { iobase = data; logerror("iobase_w %02x\n", data); } uint8_t pci_bridge_device::iolimit_r() { return iolimit; } void pci_bridge_device::iolimit_w(uint8_t data) { iolimit = data; logerror("iolimit_w %02x\n", data); } uint16_t pci_bridge_device::secondary_status_r() { logerror("secondary_status_r\n"); return 0xffff; } void pci_bridge_device::secondary_status_w(uint16_t data) { logerror("secondary_status_w %04x\n", data); } uint16_t pci_bridge_device::memory_base_r() { return memory_base; } void pci_bridge_device::memory_base_w(offs_t offset, uint16_t data, uint16_t mem_mask) { COMBINE_DATA(&memory_base); logerror("memory_base_w %04x\n", memory_base); } uint16_t pci_bridge_device::memory_limit_r() { return memory_limit; } void pci_bridge_device::memory_limit_w(offs_t offset, uint16_t data, uint16_t mem_mask) { COMBINE_DATA(&memory_limit); logerror("memory_limit_w %04x\n", memory_limit); } uint16_t pci_bridge_device::prefetch_base_r() { return prefetch_base; } void pci_bridge_device::prefetch_base_w(offs_t offset, uint16_t data, uint16_t mem_mask) { COMBINE_DATA(&prefetch_base); logerror("prefetch_base_w %04x\n", prefetch_base); } uint16_t pci_bridge_device::prefetch_limit_r() { return prefetch_limit; } void pci_bridge_device::prefetch_limit_w(offs_t offset, uint16_t data, uint16_t mem_mask) { COMBINE_DATA(&prefetch_limit); logerror("prefetch_limit_w %04x\n", prefetch_limit); } uint32_t pci_bridge_device::prefetch_baseu_r() { return prefetch_baseu; } void pci_bridge_device::prefetch_baseu_w(offs_t offset, uint32_t data, uint32_t mem_mask) { COMBINE_DATA(&prefetch_baseu); logerror("prefetch_baseu_w %08x\n", prefetch_baseu); } uint32_t pci_bridge_device::prefetch_limitu_r() { return prefetch_limitu; } void pci_bridge_device::prefetch_limitu_w(offs_t offset, uint32_t data, uint32_t mem_mask) { COMBINE_DATA(&prefetch_limitu); logerror("prefetch_limitu_w %08x\n", prefetch_limitu); } uint16_t pci_bridge_device::iobaseu_r() { return iobaseu; } void pci_bridge_device::iobaseu_w(offs_t offset, uint16_t data, uint16_t mem_mask) { COMBINE_DATA(&iobaseu); logerror("iobaseu_w %04x\n", iobaseu); } uint16_t pci_bridge_device::iolimitu_r() { return iolimitu; } void pci_bridge_device::iolimitu_w(offs_t offset, uint16_t data, uint16_t mem_mask) { COMBINE_DATA(&iolimitu); logerror("iolimitu_w %04x\n", iolimitu); } uint16_t pci_bridge_device::bridge_control_r() { return bridge_control; } void pci_bridge_device::bridge_control_w(offs_t offset, uint16_t data, uint16_t mem_mask) { COMBINE_DATA(&bridge_control); logerror("bridge_control_w %04x\n", bridge_control); } agp_bridge_device::agp_bridge_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) : pci_bridge_device(mconfig, type, tag, owner, clock) { } void agp_bridge_device::device_start() { pci_bridge_device::device_start(); } void agp_bridge_device::device_reset() { pci_bridge_device::device_reset(); } void pci_host_device::io_configuration_access_map(address_map &map) { map(0xcf8, 0xcfb).rw(FUNC(pci_host_device::config_address_r), FUNC(pci_host_device::config_address_w)); map(0xcfc, 0xcff).rw(FUNC(pci_host_device::config_data_r), FUNC(pci_host_device::config_data_w)); } void pci_host_device::set_spaces(address_space *memory, address_space *io, address_space *busmaster) { memory_space = memory; io_space = io ? io : memory; m_pci_root->set_pci_busmaster_space(busmaster ? busmaster : memory); } pci_host_device::pci_host_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) : pci_bridge_device(mconfig, type, tag, owner, clock) { } device_t *pci_host_device::bus_root() { return owner(); } void pci_host_device::device_start() { remap_cb = mapper_cb(&pci_host_device::regenerate_mapping, this); pci_bridge_device::device_start(); memory_window_start = memory_window_end = memory_offset = 0; io_window_start = io_window_end = io_offset = 0; reset_all_mappings(); save_item(NAME(config_address)); } void pci_host_device::device_reset() { pci_bridge_device::device_reset(); reset_all_mappings(); config_address = 0; } void pci_host_device::interface_post_reset() { pci_bridge_device::interface_post_reset(); regenerate_mapping(); } void pci_host_device::regenerate_mapping() { logerror("Regenerating mapping\n"); memory_space->unmap_readwrite(memory_window_start, memory_window_end); io_space->unmap_readwrite(io_window_start, io_window_end); map_device(memory_window_start, memory_window_end, memory_offset, memory_space, io_window_start, io_window_end, io_offset, io_space); } uint32_t pci_host_device::config_address_r() { return config_address; } void pci_host_device::config_address_w(offs_t offset, uint32_t data, uint32_t mem_mask) { COMBINE_DATA(&config_address); } uint32_t pci_host_device::config_data_r(offs_t offset, uint32_t mem_mask) { return config_address & 0x80000000 ? root_config_read((config_address >> 16) & 0xff, (config_address >> 8) & 0xff, config_address & 0xfc, mem_mask) : 0xffffffff; } void pci_host_device::config_data_w(offs_t offset, uint32_t data, uint32_t mem_mask) { if(config_address & 0x80000000) root_config_write((config_address >> 16) & 0xff, (config_address >> 8) & 0xff, config_address & 0xfc, data, mem_mask); } uint32_t pci_host_device::config_data_ex_r(offs_t offset, uint32_t mem_mask) { // is this a Type 0 or Type 1 configuration address? (page 31, PCI 2.2 Specification) if ((config_address & 3) == 0) { const int devnum = 31 - count_leading_zeros_32(config_address & 0xfffff800); return root_config_read(0, devnum << 3, config_address & 0xfc, mem_mask); } else if ((config_address & 3) == 1) return config_address & 0x80000000 ? root_config_read((config_address >> 16) & 0xff, (config_address >> 8) & 0xff, config_address & 0xfc, mem_mask) : 0xffffffff; logerror("pci: configuration address format %d unsupported", config_address & 3); return 0xffffffff; } void pci_host_device::config_data_ex_w(offs_t offset, uint32_t data, uint32_t mem_mask) { // is this a Type 0 or Type 1 configuration address? (page 31, PCI 2.2 Specification) if ((config_address & 3) == 0) { const int devnum = 31 - count_leading_zeros_32(config_address & 0xfffff800); root_config_write(0, devnum << 3, config_address & 0xfc, data, mem_mask); } else if ((config_address & 3) == 1) { if (config_address & 0x80000000) root_config_write((config_address >> 16) & 0xff, (config_address >> 8) & 0xff, config_address & 0xfc, data, mem_mask); } else logerror("pci: configuration address format %d unsupported", config_address & 3); } uint32_t pci_host_device::root_config_read(uint8_t bus, uint8_t device, uint16_t reg, uint32_t mem_mask) { if(bus == 0x00) return do_config_read(bus, device, reg, mem_mask); return propagate_config_read(bus, device, reg, mem_mask); } void pci_host_device::root_config_write(uint8_t bus, uint8_t device, uint16_t reg, uint32_t data, uint32_t mem_mask) { if(bus == 0x00) do_config_write(bus, device, reg, data, mem_mask); else propagate_config_write(bus, device, reg, data, mem_mask); } pci_root_device::pci_root_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, PCI_ROOT, tag, owner, clock), m_pin_mapper(*this), m_irq_handler(*this), m_pci_busmaster_space(nullptr) { } void pci_root_device::device_start() { } void pci_root_device::device_reset() { } void pci_root_device::irq_pin_w(int pin, int state) { m_irq_handler(m_pin_mapper(pin), state); } void pci_root_device::irq_w(int line, int state) { m_irq_handler(line, state); }