Matt "Gasman" Westcott (@westdotcodottt / firstname.lastname@example.org)
DivIDEo is a video converter and player system for the ZX Spectrum 128K with DivIDE interface, allowing video to be streamed directly from an IDE device.
To watch the demonstration video (first presented at the Outline 2010 demo party) on an emulator:
On the real hardware:
This is a command-line app - after unpacking it somewhere suitable, start converting videos with:
divideo myvideo.avi myvideo.dvo
See divideo --help for more options to tweak the output.
To build from source, you'll need a recent Subversion snapshot of FFMPEG (I'm using the 2010-04-11 one), Imagemagick, and Argtable 2.x. You'll probably also need to hack the Makefile to specify the paths where you've installed things (sorry...)
zxbruno, nuggetreggae, natxcross, brownb2, Yerzmyey, Zilog, Factor6, LaesQ, Phoenix, Baze, Garry Lancaster, LCD, Velesoft, Hood and Speccy IDE / video enthusiasts everywhere...
Player source code - as ZIP file / as text
The player, and the .dvo file format, are designed around minimising the work that needs to be done on the Spectrum side. There is no buffering going on (other than what you get for free from the 128K's second screen) - all data is arranged so that the Spectrum will receive it at exactly the required time, and at exactly the required rate. As a result, the player and file format are best understood as a single entity; plenty of odd design decisions have been made in the interest of optimising away a few Z80 cycles. (Rest assured that it was even more confusing for me to design in the first place :-) )
let L = number of sectors in first frame NEXT_FRAME: if L = 0, exit Issue an IDE request for the next L sectors Wait for retrace (i.e. a HALT instruction) Flip screens so that old screen is displayed and new screen is paged in at 0xc000 Go to MAINLOOP TABLE: 0) Jump to NEXT_FRAME 2) Execute INI (i.e. read byte from disk to address HL, advance HL) 4) Execute INI 6) Execute INI 8) Execute INI ... 252) Execute INI MAINLOOP (and table offset 254): Wait for IDE 'ready' state Read bytes L, H, A from disk Read byte from disk and output to AY register 8 (channel A volume) Jump to offset A in TABLE
The .dvo file begins with a 512-byte (one sector) header:
|0x00||0x08||Magic number - the ASCII string "DivIDEog"|
|0x08||0x01||Version number - must be 0x01. (These first two fields together are used to identify the start of a .dvo file when scanning a disk sector-by-sector)|
|0x09||0x01||Size of first frame, in sectors|
|0x0a||0x01||Border colour - 0x00-0x07|
|0x0b||0x15||Empty (set to 0x00)|
|0x20||0x20||Video name - padded with 0x00 if less than 0x20 bytes|
|0x40||0x1c0||Empty (set to 0x00)|
The remainder of the .dvo file is a sequence of frames, each one consisting of an arbitrary number of video packets followed by one end marker packet. A video packet contains a sequence of zero or more bytes to update screen memory with at a specified address, plus a single audio sample.
Note that as double buffering is in use, the video data in each frame needs to encompass all the changes to screen memory since two frames ago. (For the purposes of the first two frames - the initial screen state is entirely 0x00 bytes.)
Format of a video packet:
|0x00||0x02||Screen address (little-endian) to copy video bytes to, with 0xc000 being the start of screen memory|
|0x02||0x01||The value 254-(len * 2), where len is the length of the video data (which can be 0). (This is used as an offset into the table of INI instructions, so that the required number of them are executed)|
|0x03||0x01||Sample level (0x00-0x0f) to output to the AY, plus 0x80. (Adding 0x80 allows us to output to the AY with an OUT (0xfd),A instruction, freeing up the C register for DivIDE data reading; incomplete port decoding means that this will still be seen as a write to the AY's data port, and the AY will ignore the high bit. It also ensures that when we come to the next IN instruction, A is in the range 0x80-0xbf which avoids a contended port read. Phew!)|
|0x04||...||Video bytes to be written to screen|
Format of an end marker packet:
|0x00||0x01||Size of next frame, in sectors, or 0x00 to indicate end of video|
|0x01||0x01||Unused (set to 0x00)|
|0x02||0x01||0x00 (used as an offset into the INI table, which handily begins with a jump to the 'next frame' routine)|
|0x03||0x01||Sample level (0x00-0x0f) to output to the AY, plus 0x80|
- gasman 2010-05-01