Posted: Wed Sep 11, 2013 10:47 pm
There are a LOT of edge cases in the VDP, and they tend to betray internal information too, so you can kind of chain them together to probe internal operation, eg, building off an edge case that exposes internal information, you can probe into the internal state of another operation, or force unexpected conditions in it and observe the results, and so on. Data port reads are by far the buggiest part of the VDP, and a lot of the tests rely on them to dig information out. I'll try and answer your questions as best as I can, but there's more going on here than I can document in one post.
Note also that the current data in the FIFO for previously processed data port writes can affect read operations. If you're reading from a target like VSRAM or CRAM, which has some bits in the 16-bit wide result that are undefined in the target memory, those undefined bits are actually initialized to the content on the next available FIFO entry (the one containing the data written to control port four writes ago). This is very useful behaviour, as it allows the previous contents of the FIFO to be snooped on, which is how you can determine how and when the VDP uses the FIFO. A lot of the tests in this ROM rely on this being done correctly, as a lot of the tests verify the resulting FIFO state.
Mostly correct, except that read operations aren't added to any kind of "list", a read is simply flagged as pending, and that state is ignored until the FIFO is empty. Each of the four FIFO entries effectively store their own copy of the command code and command address registers, while there's a fifth "live" command code and command address, which is what you write into when you perform control port writes. There's also some interesting behaviour to do with the auto-incremented addresses when performing reads and writes. I'm pretty sure, from memory, that there's a sixth "active" command address register too, which is what gets auto-incremented when performing reads or writes, and in the case of the FIFO, gets written into each FIFO entry when a data port write is made. This cached address register gets reloaded from the live address register even if you only perform a partial control port write, which may cause some surprising results. There's a test which covers this behaviour.1) FIFO state has no impact on CTRL port writes: you can actually setup a READ operation while the FIFO is full and it still returns without delay => this means the VDP command list stores the source/destination as well as the type of access (presumably, setting a READ operation adds a read command at the end of the list)
Correct. The VDP doesn't use the FIFO for performing read operations, there's a separate read buffer. Most importantly, there's a read pre-cache process, where as soon as you setup a valid read target, the VDP will attempt to fetch the first value for the read target once the write FIFO is empty, and after each read operation, it will attempt to cache the next value. Note there are a lot of quirks and potential hardware lockups surrounding this read pre-cache process when you start messing with VDP state while a pre-cache operation is in progress. There's even problems that can occur intermittently if you attempt a data port read immediately after setting up a read target. With all the tests I've done, I still don't have the VDP behaviour fully mapped out surrounding control port access while read caching is in progress, but most of the bugs seem to be related to control port writes when only half of the data has been cached, such as when reading from VRAM where reads are byte-wide, but I saw unusual behaviour with VSRAM as well I think, in that case related to collisions with the render process reading from the VSRAM buffer I speculated. I think there are some disabled tests around this caching behaviour, and I have attempted support for the assumed causes of these problems within Exodus, but I currently don't actually deadlock the VDP core when errors like this occur, I just ignore them. After some more testing, I'm planning to activate this behaviour, when I'm confident I'm emulating it correctly.2) FIFO state is only affected by WRITE operations: setting a READ operation does not clear the FIFO EMPTY flag and this flag isn't cleared either when the read data is available.
Note also that the current data in the FIFO for previously processed data port writes can affect read operations. If you're reading from a target like VSRAM or CRAM, which has some bits in the 16-bit wide result that are undefined in the target memory, those undefined bits are actually initialized to the content on the next available FIFO entry (the one containing the data written to control port four writes ago). This is very useful behaviour, as it allows the previous contents of the FIFO to be snooped on, which is how you can determine how and when the VDP uses the FIFO. A lot of the tests in this ROM rely on this being done correctly, as a lot of the tests verify the resulting FIFO state.
Correct, although be careful attempting data port reads from odd VRAM addresses until the read cache operation is complete! In the case of the FIFO being full, this means you should wait for it to drain first. There's a bug in the VDP core surrounding reads from odd VRAM addresses, where the VDP thinks the read is complete after reading the first byte (upper byte of result), and the second byte (lower byte of result) retains the previous data which was held in the read cache. Check the "TestVRAMByteswapping.asm" test, there's some interesting test cases in there. Also be sure to check out tests surrounding VDP fill, there's some crazy things going on there that'll show a lot about how DMA operations work. Ever thought about what happens when you try and setup a DMA operation while the FIFO contains pending writes? Or setup a DMA fill, then disable DMA operations while the fill is waiting for the data port write, then perform the write anyway? How about repeating these questions when trying a DMA fill to VSRAM? The tests for that kind of crazy stuff is all in there.3) reading from DATA port hangs until read command has been processed (and data is available): if the FIFO is full when the read is being setup, the DATA port read will takes more time than if the FIFO was empty => this indicates the read command is processed after all writes have been processed and FIFO is empty.