From 5be83357c5243c7d242413a63a0a8cb2f32bf8b1 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Sun, 20 Mar 2022 14:31:25 -0300 Subject: [PATCH] Hide API code examples using expand containers by default --- _static/css/86box.css | 25 ++ _static/js/86box.js | 25 ++ conf.py | 4 + dev/api/device.rst | 216 ++++++------ dev/api/io.rst | 32 +- dev/api/pci.rst | 769 ++++++++++++++++++++++++------------------ dev/api/timer.rst | 127 +++---- 7 files changed, 695 insertions(+), 503 deletions(-) create mode 100644 _static/js/86box.js diff --git a/_static/css/86box.css b/_static/css/86box.css index 6d056e3..d7e7e9d 100644 --- a/_static/css/86box.css +++ b/_static/css/86box.css @@ -54,3 +54,28 @@ div.bit-table > div.wy-table-responsive > table > thead > tr > th:not(.stub), di min-width: 26px; padding: 0; } + +/* Toggle containers. */ +.toggle { + background: #f3f6f6; + border: 1px solid #e1e4e5; + padding: 1em; + margin: 0 0 24px; +} +.toggle-closed > .toggle-header, .toggle-open > .toggle-header { + cursor: pointer; +} +.toggle > .toggle-header > p { + font-weight: bold; + margin-bottom: 0; +} +.toggle > div:nth-child(2) { + margin-top: 12px; + margin-bottom: 0; +} +.toggle-closed > .toggle-header > p:before { + content: "\0025B6 "; +} +.toggle-open > .toggle-header > p:before { + content: "\0025BC "; +} diff --git a/_static/js/86box.js b/_static/js/86box.js new file mode 100644 index 0000000..b806184 --- /dev/null +++ b/_static/js/86box.js @@ -0,0 +1,25 @@ +/* Toggle containers, modified from: https://stackoverflow.com/questions/2454577/sphinx-restructuredtext-show-hide-code-snippets */ +$(document).ready(function() { + /* Hide all toggle containers. */ + $('.toggle').children().not('.toggle-header').hide(); + $('.toggle').toggleClass('toggle-closed'); + + /* Add click handlers for the header. */ + $('.toggle-header').click(function() { + /* Toggle the container. */ + $(this).parent().children().not('.toggle-header').toggle(400); + $(this).parent().toggleClass('toggle-open toggle-closed'); + }); + + /* Fix scroll position if a heading is provided in the URL. + Actually hit or miss but I can't think of a better solution. */ + if ($('.toggle').length && document.location.hash) { + $(window).on('load', function() { + setTimeout(function() { + var hash = document.location.hash; + document.location.hash = hash + '_'; + document.location.hash = hash; + }, 0); + }); + } +}); diff --git a/conf.py b/conf.py index 151c435..f65b846 100644 --- a/conf.py +++ b/conf.py @@ -61,4 +61,8 @@ html_css_files = [ 'css/86box.css', ] +html_js_files = [ + 'js/86box.js', +] + highlight_language = 'c' diff --git a/dev/api/device.rst b/dev/api/device.rst index 8ee7d75..3f3ec65 100644 --- a/dev/api/device.rst +++ b/dev/api/device.rst @@ -105,60 +105,66 @@ The **device** is the main unit of emulated components in 86Box. Each device is State structure --------------- -Most devices need a place to store their internal state. We discourage the use of global structures, and instead recommend allocating **state structures** dynamically in the ``init`` callback and freeing them in the ``close`` callback:: +Most devices need a place to store their internal state. We discourage the use of global structures, and instead recommend allocating **state structures** dynamically in the ``init`` callback and freeing them in the ``close`` callback: - #include <86box/device.h> +.. container:: toggle - typedef struct { - uint32_t type; /* example: copied from device_t.local */ - uint8_t regs[256]; /* example: 256*8-bit registers */ - } foo_t; + .. container:: toggle-header - /* ... */ + Code example: allocating and freeing a state structure - static void * - foo_init(const device_t *info) - { - /* Allocate a blank state structure. */ - foo_t *dev = (foo_t *) malloc(sizeof(foo_t)); - memset(dev, 0, sizeof(foo_t)); + .. code-block:: - /* Do whatever you want. */ - dev->type = info->local; /* copy device_t.local value */ + #include <86box/device.h> - /* Return a pointer to the state structure. */ - return dev; - } + typedef struct { + uint32_t type; /* example: copied from device_t.local */ + uint8_t regs[256]; /* example: 256*8-bit registers */ + } foo_t; - static void - foo_close(void *priv) - { - /* Get the state structure. */ - foo_t *dev = (foo_t *) priv; + static void * + foo_init(const device_t *info) + { + /* Allocate the device state structure. */ + foo_t *dev = (foo_t *) malloc(sizeof(foo_t)); + memset(dev, 0, sizeof(foo_t)); /* blank structure */ - /* Do whatever you want, then deallocate the state structure. */ - free(dev); - } + /* Do whatever you want. */ + dev->type = info->local; /* copy device_t.local value */ - const device_t foo1234_device = { - .name = "Foo-1234", - .internal_name = "foo1234", - .flags = DEVICE_AT, /* 16-bit ISA */ - .local = 1234, - .init = foo_init, - .close = foo_close, - /* ... */ - }; + /* Return a pointer to the state structure. */ + return dev; + } - const device_t foo4321_device = { - .name = "Foo-4321", - .internal_name = "foo4321", - .flags = DEVICE_PCI, /* 32-bit PCI */ - .local = 4321, /* different device subtype */ - .init = foo_init, - .close = foo_close, - /* ... */ - }; + static void + foo_close(void *priv) + { + /* Get the state structure. */ + foo_t *dev = (foo_t *) priv; + + /* Do whatever you want, then deallocate the state structure. */ + free(dev); + } + + const device_t foo1234_device = { + .name = "Foo-1234", + .internal_name = "foo1234", + .flags = DEVICE_AT, /* 16-bit ISA */ + .local = 1234, + .init = foo_init, + .close = foo_close, + /* ... */ + }; + + const device_t foo4321_device = { + .name = "Foo-4321", + .internal_name = "foo4321", + .flags = DEVICE_PCI, /* 32-bit PCI */ + .local = 4321, /* different device subtype */ + .init = foo_init, + .close = foo_close, + /* ... */ + }; Registration ------------ @@ -186,26 +192,30 @@ A device will be **available** for selection by the user if these criteria are m 2. The selected machine has any of the expansion buses specified in the device's ``flags``; 3. The device's ``available`` callback returns ``1`` to indicate the device is available (this will always be true if the ``available`` callback function is ``NULL``). -The ``available`` callback can be used to verify the presence of requisite ROMs, for example:: +The ``available`` callback can be used to verify the presence of ROM files if any ROMs are required by the device: - #include <86box/device.h> - #include <86box/rom.h> +.. container:: toggle - /* ... */ + .. container:: toggle-header - static int - foo1234_available() - { - return rom_present("roms/scsi/foo/foo1234.bin"); - } + Code example: ``available`` checking for the presence of a ROM - /* ... */ + .. code-block:: - const device_t foo1234_device = { - /* ... */ - { .available = foo1234_available }, /* must have brackets due to the union */ - /* ... */ - }; + #include <86box/device.h> + #include <86box/rom.h> + + static int + foo1234_available() + { + return rom_present("roms/scsi/foo/foo1234.bin"); + } + + const device_t foo1234_device = { + /* ... */ + { .available = foo1234_available }, /* must have brackets due to the union */ + /* ... */ + }; Configuration ------------- @@ -230,51 +240,57 @@ These options are stored in the emulated machine's configuration file, in a sect midi_in = 0 -Configuration options can be specified in the ``config`` member of ``device_t``, as a pointer to a ``const`` array of ``device_config_t`` objects terminated by an object of ``type`` ``-1``:: +Configuration options can be specified in the ``config`` member of ``device_t``, as a pointer to a ``const`` array of ``device_config_t`` objects terminated by an object of ``type`` ``-1``: - #include <86box/device.h> +.. container:: toggle - /* ... */ + .. container:: toggle-header - static const device_config_t foo_config[] = { - { "selection", "Selection", CONFIG_SELECTION, "", 5, "", { 0 }, - { - { "IRQ 5", 5 }, - { "IRQ 7", 7 }, - { "" } - } - }, - { "hex16", "16-bit hex", CONFIG_HEX16, "", 0x220, "", { 0 }, - { - { "0x220", 0x220 }, - { "0x330", 0x330 }, - { "" } - } - }, - { "hex20", "20-bit hex", CONFIG_HEX20, "", 0xd8000, "", { 0 }, - { - /* While the memory *segment* is displayed to the user, we store the - *linear* (segment << 4) base address in the configuration file. */ - { "D800h", 0xd8000 }, - { "DC00h", 0xdc000 }, - { "" } - } - }, - { "string", "String", CONFIG_STRING, "Default" }, - { "fname", "Filename", CONFIG_FNAME, "", 0, "File type (*.foo)|*.foo|Another file type (*.bar)|*.bar" }, - { "binary", "Binary", CONFIG_BINARY, "", 1 /* checked by default */ }, - { "int", "Integer", CONFIG_INT, "", 1234 }, - { "spinner", "Spinner", CONFIG_SPINNER, "", 1234, "", { 1204, 1294, 10 } }, - { "mac", "MAC address", CONFIG_MAC, "", 0 } - { "midi_out", "MIDI output", CONFIG_MIDI_OUT, "", 0 }, - { "midi_in", "MIDI input", CONFIG_MIDI_IN, "", 0 }, - { "", "", -1 } - }; + Code example: device configuration options - const device_t foo_device = { - /* ... */ - .config = foo_config - }; + .. code-block:: + + #include <86box/device.h> + + static const device_config_t foo_config[] = { + { "selection", "Selection", CONFIG_SELECTION, "", 5, "", { 0 }, + { + { "IRQ 5", 5 }, + { "IRQ 7", 7 }, + { "" } + } + }, + { "hex16", "16-bit hex", CONFIG_HEX16, "", 0x220, "", { 0 }, + { + { "0x220", 0x220 }, + { "0x330", 0x330 }, + { "" } + } + }, + { "hex20", "20-bit hex", CONFIG_HEX20, "", 0xd8000, "", { 0 }, + { + /* While the memory *segment* is displayed to the user, we store the + *linear* (segment << 4) base address in the configuration file. */ + { "D800h", 0xd8000 }, + { "DC00h", 0xdc000 }, + { "" } + } + }, + { "string", "String", CONFIG_STRING, "Default" }, + { "fname", "Filename", CONFIG_FNAME, "", 0, "File type (*.foo)|*.foo|Another file type (*.bar)|*.bar" }, + { "binary", "Binary", CONFIG_BINARY, "", 1 /* checked by default */ }, + { "int", "Integer", CONFIG_INT, "", 1234 }, + { "spinner", "Spinner", CONFIG_SPINNER, "", 1234, "", { 1204, 1294, 10 } }, + { "mac", "MAC address", CONFIG_MAC, "", 0 } + { "midi_out", "MIDI output", CONFIG_MIDI_OUT, "", 0 }, + { "midi_in", "MIDI input", CONFIG_MIDI_IN, "", 0 }, + { "", "", -1 } + }; + + const device_t foo_device = { + /* ... */ + .config = foo_config + }; .. flat-table:: device_config_t :header-rows: 1 diff --git a/dev/api/io.rst b/dev/api/io.rst index bbe7bd3..cfdb182 100644 --- a/dev/api/io.rst +++ b/dev/api/io.rst @@ -104,21 +104,29 @@ The same rule applies to write callbacks: .. note:: Each broken-down operation triggers the I/O handlers for its respective port number, no matter which handlers are responsible for the starting port number. A handler will **never** receive callbacks for ports outside its ``base`` and ``size`` boundaries. -This feature's main use cases are devices which store registers that are 8-bit wide but may be accessed with 16- or 32-bit operations:: +This feature's main use cases are devices which store registers that are 8-bit wide but may be accessed with 16- or 32-bit operations: - typedef struct { - uint8_t regs[256]; - } foo_t; +.. container:: toggle - static uint8_t - foo_io_inb(uint16_t addr, void *priv) - { - foo_t *dev = (foo_t *) priv; - return dev->regs[addr & 0xff]; /* example: register index = I/O port's least significant byte */ - } + .. container:: toggle-header - /* No foo_io_inw, so a 16-bit read will read two 8-bit registers in succession. - No foo_io_inl, so a 32-bit read will read four 8-bit registers in succession. */ + Code example: ``inb`` handler for reading 8-bit registers + + .. code-block:: + + typedef struct { + uint8_t regs[256]; + } foo_t; + + static uint8_t + foo_io_inb(uint16_t addr, void *priv) + { + foo_t *dev = (foo_t *) priv; + return dev->regs[addr & 0xff]; /* register index = I/O port's least significant byte */ + } + + /* No foo_io_inw, so a 16-bit read will read two 8-bit registers in succession. + No foo_io_inl, so a 32-bit read will read four 8-bit registers in succession. */ Multiple I/O handlers --------------------- diff --git a/dev/api/pci.rst b/dev/api/pci.rst index 245382c..cb02a63 100644 --- a/dev/api/pci.rst +++ b/dev/api/pci.rst @@ -6,7 +6,74 @@ PCI Adding a device --------------- -PCI devices can be added with the ``pci_add_card`` function. A slot is automatically selected according to the ``add_type``; if the emulated machine runs out of PCI slots, a **DEC 21150** PCI-PCI bridge is automatically deployed to add 9 more slots. +PCI devices can be added with the ``pci_add_card`` function in the device's ``init`` callback. A slot is :ref:`automatically selected ` according to the ``add_type``; if the emulated machine runs out of PCI slots, a **DEC 21150** PCI-PCI bridge is automatically deployed to add 9 more slots. + +.. container:: toggle + + .. container:: toggle-header + + Code example: adding a PCI device + + .. code-block:: + + #include <86box/device.h> + #include <86box/pci.h> + + #define FOO_ONBOARD 0x80000000 /* most significant bit set = on-board */ + + typedef struct { + uint8_t pci_regs[256]; + int slot; + } foo_t; + + static uint8_t + foo_pci_read(int func, int addr, void *priv) + { + /* Get the device state structure. */ + foo_t *dev = (foo_t *) priv; + + /* Ignore unknown functions. */ + if (func > 0) + return 0xff; + + /* Read configuration space register. */ + return dev->pci_regs[addr]; + } + + static void + foo_pci_write(int func, int addr, uint8_t val, void *priv) + { + /* Get the device state structure. */ + foo_t *dev = (foo_t *) priv; + + /* Ignore unknown functions. */ + if (func > 0) + return; + + /* Write configuration space register. */ + dev->pci_regs[addr] = val; + } + + static void * + foo_init(const device_t *info) + { + /* Allocate the device state structure. */ + foo_t *dev = /* ... */ + + /* Add PCI device. */ + dev->slot = pci_add_card(PCI_ADD_NORMAL, foo_pci_read, foo_pci_write, dev); + + return dev; + } + + const device_t foo4321_device = { + .name = "Foo-4321", + .internal_name = "foo4321", + .flags = DEVICE_PCI, + .local = 4321, + .init = foo_init, + /* ... */ + }; .. flat-table:: pci_add_card :header-rows: 1 @@ -43,7 +110,7 @@ PCI devices can be added with the ``pci_add_card`` function. A slot is automatic Usually a pointer to a device's :ref:`state structure `. * - **Return value** - - ``int`` value (subject to change in the future) which uniquely identifies the newly-added device. + - ``int`` value (subject to change in the future) representing the newly-added device. Slot types ---------- @@ -59,45 +126,56 @@ A machine may declare **special PCI slots** for specific purposes, such as on-bo * ``PCI_ADD_NETWORK``: on-board network controller; * ``PCI_ADD_NORTHBRIDGE``, ``PCI_ADD_AGPBRIDGE``, ``PCI_ADD_SOUTHBRIDGE``: reserved for the chipset. -A device available both as a discrete card and as an on-board device should have different ``device_t`` objects with unique ``local`` values to set both variants apart:: +A device available both as a discrete card and as an on-board device should have different ``device_t`` objects with unique ``local`` values to set both variants apart. - #include <86box/device.h> - #include <86box/pci.h> +.. container:: toggle - #define FOO_ONBOARD 0x80000000 /* example: most significant bit set = on-board */ + .. container:: toggle-header - /* ... */ + Code example: device available as both discrete and on-board - static void * - foo_init(const device_t *info) - { - foo_t *dev = /* ... */ + .. code-block:: - /* Add PCI device. The normal variant goes in any normal slot, - and the on-board variant goes in the on-board SCSI "slot". */ - pci_add_slot((info->local & FOO_ONBOARD) ? PCI_ADD_SCSI : PCI_ADD_NORMAL, - foo_pci_read, foo_pci_write, dev); + #include <86box/device.h> + #include <86box/pci.h> - /* ... */ - } + #define FOO_ONBOARD 0x80000000 /* most significant bit set = on-board */ - const device_t foo1234_device = { - .name = "Foo-1234", - .internal_name = "foo1234", - .flags = DEVICE_PCI, - .local = 1234, /* on-board bit not set */ - .init = foo_init, - /* ... */ - }; + typedef struct { + int slot; + } foo_t; - const device_t foo1234_onboard_device = { - .name = "Foo-1234 (On-Board)", - .internal_name = "foo1234_onboard", - .flags = DEVICE_PCI, - .local = 1234 | FOO_ONBOARD, /* on-board bit set */ - .init = foo_init, - /* ... */ - }; + static void * + foo_init(const device_t *info) + { + /* Allocate the device state structure. */ + foo_t *dev = /* ... */ + + /* Add PCI device. The normal variant goes in any normal slot, + and the on-board variant goes in the on-board SCSI "slot". */ + dev->slot = pci_add_card((info->local & FOO_ONBOARD) ? PCI_ADD_SCSI : PCI_ADD_NORMAL, + foo_pci_read, foo_pci_write, dev); + + return dev; + } + + const device_t foo4321_device = { + .name = "Foo-4321", + .internal_name = "foo4321", + .flags = DEVICE_PCI, + .local = 4321, /* on-board bit not set */ + .init = foo_init, + /* ... */ + }; + + const device_t foo4321_onboard_device = { + .name = "Foo-4321 (On-Board)", + .internal_name = "foo4321_onboard", + .flags = DEVICE_PCI, + .local = 4321 | FOO_ONBOARD, /* on-board bit set */ + .init = foo_init, + /* ... */ + }; Configuration space ------------------- @@ -188,69 +266,80 @@ The ``func`` parameter passed to a device's configuration space read/write callb 1. The first function (function ``0``) must have bit 7 (``0x80``) of the Header Type (``0x0e``) register set; 2. Unused functions must return ``0xff`` on all configuration register reads and should ignore writes. -.. code-block:: - typedef struct { - /* ... */ - uint8_t pci_regs[2][256]; - } foo_t; +.. container:: toggle - /* ... */ + .. container:: toggle-header - static uint8_t - foo_pci_read(int func, int addr, void *priv) - { - /* Get the device state structure. */ - foo_t *dev = (foo_t *) dev; + Code example: device with two functions - /* Read from a register on the given function. */ - switch (func) { - case 0: /* function 0 */ - return dev->pci_regs[0][addr]; + .. code-block:: - case 1: /* function 1 */ - return dev->pci_regs[1][addr]; + typedef struct { + uint8_t pci_regs[2][256]; /* two 256*8-bit register arrays, one for each function */ + } foo_t; - default: /* out of range */ - return 0xff; + static uint8_t + foo_pci_read(int func, int addr, void *priv) + { + /* Get the device state structure. */ + foo_t *dev = (foo_t *) priv; + + /* Read configuration space register on the given function. */ + switch (func) { + case 0: /* function 0 */ + return dev->pci_regs[0][addr]; + + case 1: /* function 1 */ + return dev->pci_regs[1][addr]; + + default: /* out of range */ + return 0xff; + } } - } - static void - foo_pci_write(int func, int addr, uint8_t val, void *priv) - { - /* Get the device state structure. */ - foo_t *dev = (foo_t *) dev; + static void + foo_pci_write(int func, int addr, uint8_t val, void *priv) + { + /* Get the device state structure. */ + foo_t *dev = (foo_t *) priv; - /* Write to a register on the given function. */ - switch (func) { - case 0: /* function 0 */ - dev->pci_regs[0][addr] = val; - break; + /* Write configuration space register on the given function. */ + switch (func) { + case 0: /* function 0 */ + dev->pci_regs[0][addr] = val; + break; - case 1: /* function 1 */ - dev->pci_regs[1][addr] = val; - break; + case 1: /* function 1 */ + dev->pci_regs[1][addr] = val; + break; - default: /* out of range */ - break; + default: /* out of range */ + break; + } } - } - /* ... */ + static void + foo_reset(void *priv) + { + /* Get the device state structure. */ + foo_t *dev = (foo_t *) priv; - static void - foo_reset(void *priv) - { - foo_t *dev = /* ... */ + /* Reset PCI configuration registers. */ + memset(dev->pci_regs[0], 0, sizeof(dev->pci_regs[0])); + memset(dev->pci_regs[0], 0, sizeof(dev->pci_regs[0])); - /* Flag this device as multi-function. */ - dev->pci_regs[0][0x0e] = 0x80; + /* Write default vendor IDs, device IDs, etc. */ - /* ... */ - } + /* Flag this device as multi-function. */ + dev->pci_regs[0][0x0e] = 0x80; + } - /* ... */ + const device_t foo4321_device = { + /* ... */ + .reset = foo_reset, + /* ... */ + }; Base Address Registers ---------------------- @@ -270,15 +359,15 @@ The aforementioned base address alignment allows software (BIOSes and operating .. container:: bit-table - .. flat-table:: Memory BAR (example: 4 KB large) + .. flat-table:: Memory BAR (example: 4 KB large, starting at 0x10) :header-rows: 2 :stub-columns: 1 * - Byte - - :cspan:`7` BAR+3 - - :cspan:`7` BAR+2 - - :cspan:`7` BAR+1 - - :cspan:`7` BAR+0 + - :cspan:`7` 0x13 + - :cspan:`7` 0x12 + - :cspan:`7` 0x11 + - :cspan:`7` 0x10 * - Bit - 31 @@ -317,18 +406,22 @@ The aforementioned base address alignment allows software (BIOSes and operating * - Value - :cspan:`19` Base memory address (4096-byte aligned) - :cspan:`7` Always ``0`` - - :cspan:`2` Flags + - :cspan:`2` + + .. raw:: html + + Flags (RO) - ``0`` - .. flat-table:: I/O BAR (64 ports large) + .. flat-table:: I/O BAR (example: 64 ports large, starting at 0x14) :header-rows: 2 :stub-columns: 1 * - Byte - - :cspan:`7` BAR+3 - - :cspan:`7` BAR+2 - - :cspan:`7` BAR+1 - - :cspan:`7` BAR+0 + - :cspan:`7` 0x17 + - :cspan:`7` 0x16 + - :cspan:`7` 0x15 + - :cspan:`7` 0x14 * - Bit - 31 @@ -370,165 +463,168 @@ The aforementioned base address alignment allows software (BIOSes and operating - :cspan:`3` Always ``0`` - .. raw:: html - R + R - ``1`` -.. code-block:: +.. container:: toggle - #include <86box/io.h> - #include <86box/mem.h> + .. container:: toggle-header - typedef struct { - /* ... */ - uint8_t pci_regs[256]; /* note: 1D array as this example is not multi-function */ - uint16_t io_base; - mem_mapping_t mem_mapping; - } foo_t; + Code example: memory and I/O BARs descibed above - /* ... */ + .. code-block:: - static void - foo_remap_mem(foo_t *dev) - { - if (dev->pci_regs[0x04] & 0x02) { - /* Memory Space bit set, apply the base address. - Least significant bits are masked off to maintain 4096-byte alignment. - We skip reading dev->pci_regs[0x10] as it contains nothing of interest. */ - mem_mapping_set_addr(&dev->mem_mapping, - ((dev->pci_regs[0x11] << 8) | (dev->pci_regs[0x12] << 16) | (dev->pci_regs[0x13] << 24)) & 0xfffff000, - 4096); - } else { - /* Memory Space bit not set, disable the mapping. */ - mem_mapping_set_addr(&dev->mem_mapping, 0, 0); - } - } + #include <86box/io.h> + #include <86box/mem.h> - static void - foo_remap_io(foo_t *dev) - { - /* Remove existing I/O handler if present. */ - if (dev->io_base) - io_removehandler(dev->io_base, 64, - foo_io_inb, foo_io_inw, foo_io_inl, - foo_io_outb, foo_io_outw, foo_io_outl, dev); + typedef struct { + uint8_t pci_regs[256]; + uint16_t io_base; + mem_mapping_t mem_mapping; + } foo_t; - if (dev->pci_regs[0x04] & 0x01) { - /* I/O Space bit set, read the base address. - Least significant bits are masked off to maintain 64-byte alignment. */ - dev->io_base = (dev->pci_regs[0x14] | (dev->pci_regs[0x15] << 8)) & 0xffc0; - } else { - /* I/O Space bit not set, don't do anything. */ - dev->io_base = 0; + static void + foo_remap_mem(foo_t *dev) + { + if (dev->pci_regs[0x04] & 0x02) { + /* Memory Space bit set, apply the base address. + Least significant bits are masked off to maintain 4096-byte alignment. + We skip reading dev->pci_regs[0x10] as it contains nothing of interest. */ + mem_mapping_set_addr(&dev->mem_mapping, + ((dev->pci_regs[0x11] << 8) | (dev->pci_regs[0x12] << 16) | (dev->pci_regs[0x13] << 24)) & 0xfffff000, + 4096); + } else { + /* Memory Space bit not set, disable the mapping. */ + mem_mapping_set_addr(&dev->mem_mapping, 0, 0); + } } - /* Add new I/O handler if required. */ - if (dev->io_base) - io_sethandler(dev->io_base, 64, - foo_io_inb, foo_io_inw, foo_io_inl, - foo_io_outb, foo_io_outw, foo_io_outl, dev); - } + static void + foo_remap_io(foo_t *dev) + { + /* Remove existing I/O handler if present. */ + if (dev->io_base) + io_removehandler(dev->io_base, 64, + foo_io_inb, foo_io_inw, foo_io_inl, + foo_io_outb, foo_io_outw, foo_io_outl, dev); - /* ... */ + if (dev->pci_regs[0x04] & 0x01) { + /* I/O Space bit set, read the base address. + Least significant bits are masked off to maintain 64-byte alignment. */ + dev->io_base = (dev->pci_regs[0x14] | (dev->pci_regs[0x15] << 8)) & 0xffc0; + } else { + /* I/O Space bit not set, don't do anything. */ + dev->io_base = 0; + } - static void - foo_pci_write(int func, int addr, uint8_t val, void *priv) - { - /* Get the device state structure. */ - foo_t *dev = (foo_t *) dev; - - /* Ignore unknown functions. */ - if (func > 0) - return; - - /* Write register. */ - switch (addr) { - case 0x04: - /* Our device only supports the I/O and Memory Space bits of the Command register. */ - dev->pci_regs[addr] = val & 0x03; - - /* Update memory and I/O spaces. */ - foo_remap_mem(dev); - foo_remap_io(dev); - break; - - case 0x10: - /* Least significant byte of the memory BAR is read-only. */ - break; - - case 0x11: - /* 2nd byte of the memory BAR is masked to maintain 4096-byte alignment. */ - dev->pci_regs[addr] = val & 0xf0; - - /* Update memory space. */ - foo_remap_mem(dev); - break; - - case 0x12: case 0x13: - /* 3rd and most significant bytes of the memory BAR are fully writable. */ - dev->pci_regs[addr] = val; - - /* Update memory space. */ - foo_remap_mem(dev); - break; - - case 0x14: - /* Least significant byte of the I/O BAR is masked to maintain 64-byte alignment, and - ORed with the default value's least significant bits so that the flags stay in place. */ - dev->pci_regs[addr] = (val & 0xc0) | (dev->pci_regs[addr] & 0x03); - - /* Update I/O space. */ - foo_remap_io(dev); - break; - - case 0x15: - /* Most significant byte of the I/O BAR is fully writable. */ - dev->pci_regs[addr] = val; - - /* Update I/O space. */ - foo_remap_io(dev); - break; - - case 0x16: case 0x17: - /* I/O BARs are only 2 bytes long, ignore the rest. */ - break; + /* Add new I/O handler if required. */ + if (dev->io_base) + io_sethandler(dev->io_base, 64, + foo_io_inb, foo_io_inw, foo_io_inl, + foo_io_outb, foo_io_outw, foo_io_outl, dev); } - } - /* ... */ + static void + foo_pci_write(int func, int addr, uint8_t val, void *priv) + { + /* Get the device state structure. */ + foo_t *dev = (foo_t *) priv; - static void - foo_reset(void *priv) - { - foo_t *dev = /* ... */ + /* Ignore unknown functions. */ + if (func > 0) + return; - /* Example: the BAR at 0x10-0x13 is a memory BAR. */ - dev->pci_regs[0x10] = 0x00; /* least significant bit not set = memory */ - dev->pci_regs[0x11] = 0x00; - dev->pci_regs[0x12] = 0x00; - dev->pci_regs[0x13] = 0x00; + /* Write configuration space register. */ + switch (addr) { + case 0x04: + /* Our device only supports the I/O and Memory Space bits of the Command register. */ + dev->pci_regs[addr] = val & 0x03; - /* Example: the BAR at 0x14-0x17 is an I/O BAR. */ - dev->pci_regs[0x14] = 0x01; /* least significant bit set = I/O */ - dev->pci_regs[0x15] = 0x00; - dev->pci_regs[0x16] = 0x00; - dev->pci_regs[0x17] = 0x00; + /* Update memory and I/O spaces. */ + foo_remap_mem(dev); + foo_remap_io(dev); + break; - /* Clear all BAR memory mappings and I/O handlers. */ - dev->pci_regs[0x04] = 0x00; - foo_remap_mem(dev); - foo_remap_io(dev); + case 0x10: + /* Least significant byte of the memory BAR is read-only. */ + break; - /* ... */ - } + case 0x11: + /* 2nd byte of the memory BAR is masked to maintain 4096-byte alignment. */ + dev->pci_regs[addr] = val & 0xf0; + + /* Update memory space. */ + foo_remap_mem(dev); + break; + + case 0x12: case 0x13: + /* 3rd and most significant bytes of the memory BAR are fully writable. */ + dev->pci_regs[addr] = val; + + /* Update memory space. */ + foo_remap_mem(dev); + break; + + case 0x14: + /* Least significant byte of the I/O BAR is masked to maintain 64-byte alignment, and + ORed with the default value's least significant bits so that the flags stay in place. */ + dev->pci_regs[addr] = (val & 0xc0) | (dev->pci_regs[addr] & 0x03); + + /* Update I/O space. */ + foo_remap_io(dev); + break; + + case 0x15: + /* Most significant byte of the I/O BAR is fully writable. */ + dev->pci_regs[addr] = val; + + /* Update I/O space. */ + foo_remap_io(dev); + break; + + case 0x16: case 0x17: + /* I/O BARs are only 2 bytes long, ignore the rest. */ + break; + } + } + + static void + foo_reset(void *priv) + { + /* Get the device state structure. */ + foo_t *dev = (foo_t *) dev; + + /* Reset PCI configuration registers. */ + memset(dev->pci_regs, 0, sizeof(dev->pci_regs)); + + /* Write default vendor ID, device ID, etc. */ + + /* The BAR at 0x10-0x13 is a memory BAR. */ + //dev->pci_regs[0x10] = 0x00; /* least significant bit already not set = memory */ + + /* The BAR at 0x14-0x17 is an I/O BAR. */ + dev->pci_regs[0x14] = 0x01; /* least significant bit set = I/O */ + + /* Clear all BAR memory mappings and I/O handlers. */ + //dev->pci_regs[0x04] = 0x00; /* Memory and I/O Space bits already cleared */ + foo_remap_mem(dev); + foo_remap_io(dev); + } + + const device_t foo4321_device = { + /* ... */ + .reset = foo_reset, + /* ... */ + }; Option ROM ---------- -A PCI function can have an **option ROM**, which behaves similarly to a :ref:`memory BAR ` in that the ROM can be mapped to any address in 32-bit memory space. As with BARs, the BIOS and/or operating system takes care of mapping; for example, a BIOS will map the primary PCI video card's ROM to the legacy ``0xc0000`` address. +A PCI function may have an **option ROM**, which behaves similarly to a :ref:`memory BAR ` in that the ROM can be mapped to any address in 32-bit memory space, aligned to its size. As with BARs, the BIOS and/or operating system takes care of mapping; for example, a BIOS will map the primary PCI video card's ROM to the legacy ``0xc0000`` address. -The main difference between this register and BARs is that the ROM can be enabled or disabled through this register's least significant bit. Both that bit and the Command (``0x04``) register's Memory Space bit (bit 1 or ``0x02``) must be set for the ROM to be accessible. +The main difference between this register and BARs is that the ROM can be enabled or disabled through bit 0 (``0x01``) of this register. Both that bit and the Command (``0x04``) register's Memory Space bit (bit 1 or ``0x02``) must be set for the ROM to be accessible. -.. note:: The minimum size for an option ROM is 4 KB (see the note about 86Box memory limitations on the :ref:`BAR ` section), and the maximum size is 16 MB. +.. note:: The minimum size for an option ROM is 4 KB (see the note about 86Box memory limitations in the :ref:`BAR ` section), and the maximum size is 16 MB. .. container:: bit-table @@ -581,121 +677,132 @@ The main difference between this register and BARs is that the ROM can be enable - :cspan:`13` Always ``0`` - .. raw:: html - E + E -.. code-block:: +.. container:: toggle - #include <86box/mem.h> - #include <86box/rom.h> + .. container:: toggle-header - typedef struct { - /* ... */ - uint8_t pci_regs[256]; /* note: 1D array as this example is not multi-function */ - rom_t rom; - } foo_t; + Code example: 32 KB option ROM - /* ... */ + .. code-block:: - static void - foo_remap_rom(foo_t *dev) - { - if ((dev->pci_regs[0x30] & 0x01) && (dev->pci_regs[0x04] & 0x02)) { - /* Expansion ROM Enable and Memory Space bits set, apply the base address. - Least significant bits are masked off to maintain 32768-byte alignment. - We skip reading dev->pci_regs[0x30] as it contains nothing of interest. */ - mem_mapping_set_addr(&dev->rom.mapping, - ((dev->pci_regs[0x31] << 8) | (dev->pci_regs[0x32] << 16) | (dev->pci_regs[0x33] << 24)) & 0xffff8000, - 4096); - } else { - /* Expansion ROM Enable and/or Memory Space bits not set, disable the mapping. */ + #include <86box/mem.h> + #include <86box/rom.h> + + typedef struct { + uint8_t pci_regs[256]; + rom_t rom; + } foo_t; + + static void + foo_remap_rom(foo_t *dev) + { + if ((dev->pci_regs[0x30] & 0x01) && (dev->pci_regs[0x04] & 0x02)) { + /* Expansion ROM Enable and Memory Space bits set, apply the base address. + Least significant bits are masked off to maintain 32768-byte alignment. + We skip reading dev->pci_regs[0x30] as it contains nothing of interest. */ + mem_mapping_set_addr(&dev->rom.mapping, + ((dev->pci_regs[0x31] << 8) | (dev->pci_regs[0x32] << 16) | (dev->pci_regs[0x33] << 24)) & 0xffff8000, + 4096); + } else { + /* Expansion ROM Enable and/or Memory Space bits not set, disable the mapping. */ + mem_mapping_set_addr(&dev->rom.mapping, 0, 0); + } + } + + static void + foo_pci_write(int func, int addr, uint8_t val, void *priv) + { + /* Get the device state structure. */ + foo_t *dev = (foo_t *) priv; + + /* Ignore unknown functions. */ + if (func > 0) + return; + + /* Write configuration space register. */ + switch (addr) { + case 0x04: + /* Our device only supports the Memory Space bit of the Command register. */ + dev->pci_regs[addr] = val & 0x02; + + /* Update ROM space. */ + foo_remap_rom(dev); + break; + + case 0x30: + /* Least significant byte of the ROM address is read-only, except for the enable bit. */ + dev->pci_regs[addr] = val & 0x01; + + /* Update ROM space. */ + foo_remap_rom(dev); + break; + + case 0x31: + /* 2nd byte of the ROM address is masked to maintain 32768-byte alignment. */ + dev->pci_regs[addr] = val & 0x80; + + /* Update ROM space. */ + foo_remap_rom(dev); + break; + + case 0x32: case 0x33: + /* 3rd and most significant bytes of the ROM address are fully writable. */ + dev->pci_regs[addr] = val; + + /* Update ROM space. */ + foo_remap_rom(dev); + break; + } + } + + static void + foo_reset(void *priv) + { + /* Get the device state structure. */ + foo_t *dev = (foo_t *) dev; + + /* Reset PCI configuration registers. */ + memset(dev->pci_regs, 0, sizeof(dev->pci_regs)); + + /* Clear ROM memory mapping. */ + //dev->pci_regs[0x04] = 0x00; /* Memory Space bit already cleared */ + //dev->pci_regs[0x30] = 0x00; /* Expansion ROM Enable bit already cleared */ + foo_remap_rom(dev); + } + + static int + foo_available() + { + /* This device can only be used if its ROM is present. */ + return rom_present("roms/scsi/foo/foo4321.bin"); + } + + static void * + foo_init(const device_t *info) + { + /* Allocate the device state structure. */ + foo_t *dev = /* ... */ + + /* Don't forget to add the PCI device. */ + + /* Load 32 KB ROM... */ + rom_init(&dev->rom, "roms/scsi/foo/foo4321.bin", 0, 0x8000, 0x7fff, 0, MEM_MAPPING_EXTERNAL); + + /* ...but don't map it right now. */ mem_mapping_set_addr(&dev->rom.mapping, 0, 0); + + return dev; } - } - /* ... */ - - static void - foo_pci_write(int func, int addr, uint8_t val, void *priv) - { - /* Get the device state structure. */ - foo_t *dev = (foo_t *) dev; - - /* Ignore unknown functions. */ - if (func > 0) - return; - - /* Write register. */ - switch (addr) { - case 0x04: - /* Our device only supports the Memory Space bit of the Command register. */ - dev->pci_regs[addr] = val & 0x02; - - /* Update ROM space. */ - foo_remap_rom(dev); - break; - - case 0x30: - /* Least significant byte of the ROM address is read-only, except for the enable bit. */ - dev->pci_regs[addr] = val & 0x01; - - /* Update ROM space. */ - foo_remap_rom(dev); - break; - - case 0x31: - /* 2nd byte of the ROM address is masked to maintain 32768-byte alignment. */ - dev->pci_regs[addr] = val & 0x80; - - /* Update ROM space. */ - foo_remap_rom(dev); - break; - - case 0x32: case 0x33: - /* 3rd and most significant bytes of the ROM address are fully writable. */ - dev->pci_regs[addr] = val; - - /* Update ROM space. */ - foo_remap_rom(dev); - break; - } - } - - /* ... */ - - static void - foo_reset(void *priv) - { - foo_t *dev = /* ... */ - - /* Clear ROM memory mapping. */ - dev->pci_regs[0x04] = 0x00; - foo_remap_rom(dev); - - /* ... */ - } - - static int - foo_available() - { - /* This device can only be used if its ROM is present. */ - return rom_present("roms/scsi/foo/foo4321.bin"); - } - - static void * - foo_init(const device_t *info) - { - foo_t *dev = /* ... */ - - /* Example: load 32 KB ROM... */ - rom_init(&dev->rom, "roms/scsi/foo/foo4321.bin", 0, 0x8000, 0x7fff, 0, MEM_MAPPING_EXTERNAL); - - /* ...but don't map it right now. */ - mem_mapping_set_addr(&dev->rom.mapping, 0, 0); - - /* ... */ - } - - /* ... */ + const device_t foo4321_device = { + /* ... */ + .init = foo_init, + .reset = foo_reset, + { .available = foo_available }, + /* ... */ + }; Interrupts ---------- diff --git a/dev/api/timer.rst b/dev/api/timer.rst index 10978c3..932643f 100644 --- a/dev/api/timer.rst +++ b/dev/api/timer.rst @@ -8,39 +8,48 @@ Timers Adding ------ -Timers can be added with the ``timer_add`` function. The best place for adding a timer is in a :doc:`device `'s ``init`` callback, storing the ``pc_timer_t`` object in the :ref:`state structure `:: +Timers can be added with the ``timer_add`` function. The best place for adding a timer is in a :doc:`device `'s ``init`` callback, storing the ``pc_timer_t`` object in the :ref:`state structure `. - #include <86box/device.h> - #include <86box/timer.h> +.. container:: toggle - typedef struct { - /* ... */ - pc_timer_t countdown_timer; - } foo_t; + .. container:: toggle-header - /* ... */ + Code example: adding a timer - /* Called once the timer period is reached. */ - static void - foo_countdown_timer(void *priv) - { - foo_t *dev = (foo_t *) priv; + .. code-block:: - /* Do whatever you want. */ - } + #include <86box/device.h> + #include <86box/timer.h> - /* ... */ + typedef struct { + pc_timer_t countdown_timer; + } foo_t; - static void * - foo_init(const device_t *info) - { - foo_t *dev = /* ... */ + /* Called once the timer period is reached. */ + static void + foo_countdown_timer(void *priv) + { + /* Get the device state structure. */ + foo_t *dev = (foo_t *) priv; - /* Add timer. */ - timer_add(&dev->countdown_timer, foo_countdown_timer, foo, 0); + /* Do whatever you want. */ + } - /* ... */ - } + static void * + foo_init(const device_t *info) + { + /* Allocate the device state structure. */ + foo_t *dev = /* ... */ + + /* Add timer. */ + timer_add(&dev->countdown_timer, foo_countdown_timer, foo, 0); + } + + const foo1234_device = { + /* ... */ + .init = foo_init, + /* ... */ + }; .. flat-table:: timer_add :header-rows: 1 @@ -69,54 +78,52 @@ Timers can be added with the ``timer_add`` function. The best place for adding a Triggering ---------- -The ``timer_on_auto`` function can be used to start (with the provided microsecond period) or stop a timer. It can also be called from a timer callback to restart the timer:: +The ``timer_on_auto`` function can be used to start (with the provided microsecond period) or stop a timer. It can also be called from a timer callback to restart the timer: - #include <86box/timer.h> +.. container:: toggle - typedef struct { - /* ... */ - uint8_t regs[256]; - pc_timer_t countdown_timer; - } foo_t; + .. container:: toggle-header - /* ... */ + Code example: starting, restarting and stopping a timer - static void - foo_countdown_timer(void *priv) - { - /* Get the device state structure. */ - foo_t *dev = (foo_t *) priv; + .. code-block:: - /* ... */ + #include <86box/timer.h> - /* Example: restart timer automatically if bit 1 (0x02) of register 0x80 is set. */ - if (dev->regs[0x80] & 0x02) - timer_on_auto(&dev->countdown_timer, 100.0); - } + typedef struct { + uint8_t regs[256]; + pc_timer_t countdown_timer; /* remember to timer_add on init, per the example above */ + } foo_t; - /* Example: writing to I/O port register 0x__80: - - Bit 0 (0x01) set: start 100-microsecond countdown timer; - - Bit 0 (0x01) clear: stop countdown timer. */ - static void - foo_outb(uint16_t addr, uint8_t val, void *priv) - { - /* Get the device state structure. */ - foo_t *dev = (foo_t *) priv; + static void + foo_countdown_timer(void *priv) + { + /* Get the device state structure. */ + foo_t *dev = (foo_t *) priv; - /* ... */ - - if ((addr & 0xff) == 0x80) { - dev->regs[0x80] = val; - if (val & 0x01) + /* Restart timer automatically if bit 1 (0x02) of register 0x80 is set. */ + if (dev->regs[0x80] & 0x02) timer_on_auto(&dev->countdown_timer, 100.0); - else - timer_on_auto(&dev->countdown_timer, 0.0); } - /* ... */ - } + /* Example: writing to I/O port register 0x__80: + - Bit 0 (0x01) set: start 100-microsecond countdown timer; + - Bit 0 (0x01) clear: stop countdown timer. */ + static void + foo_outb(uint16_t addr, uint8_t val, void *priv) + { + /* Get the device state structure. */ + foo_t *dev = (foo_t *) priv; - /* ... */ + /* Handle writes to register 0x80. */ + if ((addr & 0xff) == 0x80) { + dev->regs[0x80] = val; + if (val & 0x01) + timer_on_auto(&dev->countdown_timer, 100.0); + else + timer_on_auto(&dev->countdown_timer, 0.0); + } + } .. flat-table:: timer_on_auto :header-rows: 1