mirror of
https://github.com/86Box/docs.git
synced 2026-02-21 09:05:33 -07:00
Hide API code examples using expand containers by default
This commit is contained in:
@@ -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 ";
|
||||
}
|
||||
|
||||
25
_static/js/86box.js
Normal file
25
_static/js/86box.js
Normal file
@@ -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);
|
||||
});
|
||||
}
|
||||
});
|
||||
4
conf.py
4
conf.py
@@ -61,4 +61,8 @@ html_css_files = [
|
||||
'css/86box.css',
|
||||
]
|
||||
|
||||
html_js_files = [
|
||||
'js/86box.js',
|
||||
]
|
||||
|
||||
highlight_language = 'c'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
---------------------
|
||||
|
||||
769
dev/api/pci.rst
769
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 <dev/api/pci:Slot types>` 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 <dev/api/device: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 (<abbr title="Read-only">RO</span>)
|
||||
- ``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
|
||||
|
||||
<abbr title="Reserved">R</abbr>
|
||||
<abbr title="Reserved (read-only)">R</abbr>
|
||||
- ``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 <dev/api/pci:Base Address Registers>` 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 <dev/api/pci:Base Address Registers>` 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 <dev/api/pci:Base Address Registers>` 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 <dev/api/pci:Base Address Registers>` 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
|
||||
|
||||
<abbr title="Enable">E</span>
|
||||
<abbr title="ROM Enable">E</span>
|
||||
|
||||
.. 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
|
||||
----------
|
||||
|
||||
@@ -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 <device>`'s ``init`` callback, storing the ``pc_timer_t`` object in the :ref:`state structure <dev/api/device:State structure>`::
|
||||
Timers can be added with the ``timer_add`` function. The best place for adding a timer is in a :doc:`device <device>`'s ``init`` callback, storing the ``pc_timer_t`` object in the :ref:`state structure <dev/api/device: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
|
||||
|
||||
Reference in New Issue
Block a user