mirror of
https://github.com/86Box/86Box.git
synced 2025-01-22 09:11:50 -05:00
unittester: Add WIP specification document
This commit is contained in:
parent
5279cd5d8d
commit
04eb9ffc3e
3 changed files with 242 additions and 0 deletions
240
doc/specifications/86box-unit-tester.md
Normal file
240
doc/specifications/86box-unit-tester.md
Normal file
|
@ -0,0 +1,240 @@
|
|||
# 86Box Unit Tester device specification
|
||||
|
||||
**TODO: DESCRIBE ME!**
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
## Conventions
|
||||
|
||||
### Integer types
|
||||
|
||||
- `i8` denotes a signed 8-bit value.
|
||||
- `u8` denotes an unsigned 8-bit value.
|
||||
- `w8` denotes an 8-bit value which wraps around.
|
||||
- `x8` denotes an 8-bit value where the signedness is irrelevant.
|
||||
- `e8` ("either") denotes an 8-bit value where the most significant bit is clear - in effect, this is a 7-bit unsigned value.
|
||||
- `u16L` denotes a little-endian unsigned 16-bit value.
|
||||
- `u16B` would denote a big-endian unsigned 16-bit value if we had any big-endian values.
|
||||
- `[N]T` denotes an array of `N` values of type `T`, whatever `N` and `T` are.
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
## Usage
|
||||
|
||||
### Accessing the device and configuring the I/O base address
|
||||
|
||||
Find an area in I/O space where 2 addresses are confirmed (or assumed) to be unused.
|
||||
There is no need for the 2 addresses to be 2-byte-aligned.
|
||||
|
||||
Send the following sequence of bytes to port 0x80 with INTERRUPTS DISABLED:
|
||||
|
||||
'8', '6', 'B', 'o', 'x', (IOBASE & 0xFF), (IOBASE >> 8)
|
||||
|
||||
Alternatively denoted in hex:
|
||||
|
||||
38 36 42 6F 78 yy xx
|
||||
|
||||
There are no timing constraints. This is an emulator, after all.
|
||||
|
||||
To confirm that this has happened, read the status port at IOBASE+0x00.
|
||||
If it's 0xFF, then the device is most likely not present.
|
||||
Otherwise, one can potentially assume that it exists and has been configured successfully.
|
||||
(You *did* make sure that the space was unused *before* doing this, right?)
|
||||
|
||||
IOBASE is allowed to overlap the trigger port, but please don't do this!
|
||||
|
||||
### Hiding the device
|
||||
|
||||
Set the I/O base address to 0xFFFF using the above method.
|
||||
|
||||
### Executing commands
|
||||
|
||||
**TODO: IMPLEMENT ME!**
|
||||
|
||||
The ports at IOBASE+0x00 and IOBASE+0x01 are all 8 bits wide.
|
||||
|
||||
Writing to IOBASE+0x00 cancels any in-flight commands and sends a new command.
|
||||
|
||||
Reading from IOBASE+0x00 reads the status:
|
||||
|
||||
- bit 0: There is data to be read from this device
|
||||
- If one reads with this bit clear, the returned data will be 0xFF.
|
||||
- bit 1: The device is expecting data to be sent to it
|
||||
- If one writes with this bit clear, the data will be ignored.
|
||||
- bit 2: There is no command in flight
|
||||
- If this is set, then bits 0 and 1 will be clear.
|
||||
- bit 3: The previously-sent command does not exist.
|
||||
- bits 4 .. 7: Reserved, should be 0.
|
||||
|
||||
Writing to IOBASE+0x01 provides data to the device if said data is needed.
|
||||
|
||||
Reading from IOBASE+0x01 fetches the next byte data to the device if said data is needed.
|
||||
|
||||
### General flow of executing a command:
|
||||
|
||||
This is how most commands will work.
|
||||
|
||||
- Write the command to IOBASE+0x00.
|
||||
- If data needs to be written or read:
|
||||
- Read the status from IOBASE+0x00 and confirm that bit 2 is clear.
|
||||
If it is set, then the command may not exist.
|
||||
Check bit 3 if that's the case.
|
||||
- If data needs to be written:
|
||||
- Write all the data one needs to write.
|
||||
- If data needs to be read:
|
||||
- Read the status from IOBASE+0x00 and wait until bit 0 is set.
|
||||
If it is set, then the command may not exist.
|
||||
Check bit 3 if that's the case.
|
||||
- Keep reading bytes until one is satisfied.
|
||||
- Otherwise:
|
||||
- Read the status from IOBASE+0x00 and wait until any of the bottom 3 bits are set.
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
## Command reference
|
||||
|
||||
### 0x00: No-op
|
||||
|
||||
**TODO: IMPLEMENT ME!**
|
||||
|
||||
This does nothing, takes no input, and gives no output.
|
||||
|
||||
This is an easy way to reset the status to 0x04 (no command in flight, not waiting for reads or writes, and no errors).
|
||||
|
||||
### 0x01: Capture Screen Snapshot
|
||||
|
||||
**TODO: IMPLEMENT ME!**
|
||||
|
||||
Captures a snapshot of the current screen state and stores it in the current snapshot buffer.
|
||||
|
||||
The initial state of the screen snapshot buffer has an image area of 0x0, an overscanned area of 0x0, and an image start offset of (0,0).
|
||||
|
||||
Input:
|
||||
|
||||
* u8 monitor
|
||||
- 0x00 = no monitor - clear the screen snapshot
|
||||
- 0x01 = primary monitor
|
||||
- 0x02 = secondary monitor
|
||||
- Any monitor which is not available is treated as 0x00, and clears the screen snapshot.
|
||||
|
||||
Output:
|
||||
|
||||
* `e16L` image width in pixels
|
||||
* `e16L` image height in pixels
|
||||
* `e16L` overscanned width in pixels
|
||||
* `e16L` overscanned height in pixels
|
||||
* `e16L` X offset of image start
|
||||
* `e16L` Y offset of image start
|
||||
|
||||
If there is no screen snapshot, then all values will be 0 as per the initial screen snapshot buffer state.
|
||||
|
||||
### 0x02: Read Screen Snapshot Rectangle
|
||||
|
||||
**TODO: IMPLEMENT ME!**
|
||||
|
||||
Returns a rectangular snapshot of the screen snapshot buffer.
|
||||
|
||||
Input:
|
||||
|
||||
* `e16L` w: rectangle width in pixels
|
||||
* `e16L` h: rectangle height in pixels
|
||||
* `i16L` x: X offset relative to image start
|
||||
* `i16L` y: Y offset relative to image start
|
||||
|
||||
Output:
|
||||
|
||||
* `[h][w][4]u8`: image data
|
||||
- `[y][x][0]` is the blue component, or 0x00 if the pixel is outside the snapshot area.
|
||||
- `[y][x][1]` is the green component, or 0x00 if the pixel is outside the snapshot area.
|
||||
- `[y][x][2]` is the red component, or 0x00 if the pixel is outside the snapshot area.
|
||||
- `[y][x][3]` is 0x00, or 0xFF if the pixel is outside the snapshot area.
|
||||
|
||||
### 0x03: Verify Screen Snapshot Rectangle
|
||||
|
||||
**TODO: IMPLEMENT ME!**
|
||||
|
||||
As per 0x02 "Read Screen Snapshot Rectangle", except instead of returning the pixel data, it returns a CRC-32 of the data.
|
||||
|
||||
The CRC is as per zlib's `crc32()` function. Specifically, one uses a right-shifting Galois LFSR with a polynomial of 0xEDB88320, bytes XORed against the least significant byte, the initial seed is 0xFFFFFFFF, and all bits of the output are inverted.
|
||||
|
||||
(Rationale: There are better CRCs, but this one is ubiquitous and still really good... and we don't need to protect against deliberate tampering.)
|
||||
|
||||
Input:
|
||||
|
||||
* `e16L` w: rectangle width in pixels
|
||||
* `e16L` h: rectangle height in pixels
|
||||
* `i16L` x: X offset relative to image start
|
||||
* `i16L` y: Y offset relative to image start
|
||||
|
||||
Output:
|
||||
|
||||
* `u32L` crc: CRC-32 of rectangle data
|
||||
|
||||
### 0x04: Exit 86Box
|
||||
|
||||
**TODO: IMPLEMENT ME!**
|
||||
|
||||
Exits 86Box, unless this command is disabled.
|
||||
|
||||
- If the command is enabled, then program execution terminates immediately.
|
||||
- If the command is disabled, it still counts as having executed correctly, but program execution continues. This makes it useful to show a "results" screen for a unit test.
|
||||
|
||||
Input:
|
||||
|
||||
* u8 exit code:
|
||||
- The actual exit code is clamped to no greater than the maximum valid exit code.
|
||||
- In practice, this is probably going to be 0x7F.
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
## Implementation notes
|
||||
|
||||
### Port 0x80 sequence detection
|
||||
|
||||
In order to ensure that one can always trigger the activation sequence, there are effectively two finite state machines in action.
|
||||
|
||||
FSM1:
|
||||
- Wait for 8.
|
||||
- Wait for 6.
|
||||
- Wait for B.
|
||||
- Wait for o.
|
||||
- Wait for x.
|
||||
Once received, set FSM2 to "Wait for low byte",
|
||||
then go back to "Wait for 8".
|
||||
|
||||
If at any point an 8 arrives, jump to the "Wait for 6" step.
|
||||
|
||||
Otherwise, if any other unexpected byte arrives, jump to the "Wait for 8" step.
|
||||
|
||||
FSM2:
|
||||
- Idle.
|
||||
- Wait for low byte. Once received, store this in a temporary location.
|
||||
- Wait for high byte.
|
||||
Once received, replace IOBASE with this byte in the high byte and the temporary value in the low byte,
|
||||
then go back to "Idle".
|
||||
|
||||
### Command processing state machine
|
||||
|
||||
**TODO: IMPLEMENT ME!**
|
||||
|
||||
**TODO: DOCUMENT ME!**
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
## Extending the protocol
|
||||
|
||||
### Adding new commands
|
||||
|
||||
Commands 0x01 through 0x7F accept a single command byte.
|
||||
|
||||
Command bytes 0x80 through 0xFB are reserved for 16-bit command IDs, to be written in a similar way to this:
|
||||
|
||||
- Write the first command byte (0x80 through 0xFF) to the command register.
|
||||
- If this block of commands does not exist, then the command is cancelled and the status is set to 0x0C.
|
||||
- Otherwise, the status is set to 0x0
|
||||
- Write the next command byte (0x00 through 0xFF) to the data register.
|
||||
- If this block of commands does not exist, then the command is cancelled and the status is set to 0x0C.
|
||||
- Otherwise, the command exists and the status is set according to the command.
|
||||
|
||||
Command bytes 0xFC through 0xFF are reserved for if we somehow need more than 16 bits worth of command ID.
|
||||
|
|
@ -7,6 +7,7 @@
|
|||
* This file is part of the 86Box distribution.
|
||||
*
|
||||
* Debug device for assisting in unit testing.
|
||||
* See doc/specifications/86box-unit-tester.md for more info.
|
||||
*
|
||||
*
|
||||
*
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
* This file is part of the 86Box distribution.
|
||||
*
|
||||
* Debug device for assisting in unit testing.
|
||||
* See doc/specifications/86box-unit-tester.md for more info.
|
||||
*
|
||||
*
|
||||
*
|
||||
|
|
Loading…
Reference in a new issue