DMA Fill

For anything related to VDP (plane, color, sprite, tiles)

Moderators: BigEvilCorporation, Mask of Destiny

Post Reply
Graz
Very interested
Posts: 81
Joined: Thu Aug 23, 2007 12:36 am
Location: Orlando, FL

DMA Fill

Post by Graz » Fri Jun 19, 2009 11:07 pm

Hi All,

I'm trying to do a DMA fill on a non-contiguous chunk of VRAM by setting auto-increment to > 2. I have a rectangular set of tiles laid out top-to-bottom, left-to-right as a sprite would be. Within the rectangle, every 8 bytes is one row down. What I want to do is draw a vertical line through the rectangle by issuing a DMA fill with auto increment set to 8. However, what appears to happen is that the high byte of the DMA data is written to the current vram address, then the low byte is written to the next address and then every 8 bytes after that. What I end up with is a pattern like this:

Code: Select all

**
 *
 *
 *
 *
 *
 *
 *
 *
I've played around with starting on odd and even addresses, different auto-increment settings, putting zeros in high or low bytes, but nothing works.
It seems that no matter what you do, the VDP will write two consecutive bytes at the start of the DMA fill, and then honor the auto-increment setting from there onwards. It is therefore impossible to fill every N'th byte of VRAM without the two-byte start. Is DMA fill useful for anything but filling contiguous regions of VRAM. Can anyone think of a way to do what I want?

Nemesis
Very interested
Posts: 791
Joined: Wed Nov 07, 2007 1:09 am
Location: Sydney, Australia

Post by Nemesis » Sat Jun 20, 2009 3:06 am

You can't. If you look at how you initiate a DMA fill, you'll note the setup is exactly the same as for a normal VRAM write, except that you set some additional settings for the DMA fill operation, and set an extra flag in the write to trigger the operation. There's a reason for that. Performing a DMA fill does perform a normal VRAM write. After the VRAM write has been processed however, a DMA fill operation is triggered immediately after. Normal VRAM writes are always 16-bit, so the first write that is carried out when you try and start a DMA fill will always be 16-bit. The DMA fill operation that follows will perform 8-bit writes.

TascoDLX
Very interested
Posts: 262
Joined: Tue Feb 06, 2007 8:18 pm

Post by TascoDLX » Sat Jun 20, 2009 4:25 am

Before you set the DMA, you read the byte before your target address from VRAM and use that value as the high byte for your trigger write, so you'll merely be overwriting the extra byte with the same value. You'll just have to start one byte earlier than your target VRAM address.

EDIT: Actually, this could be a problem if you're forced to start at an odd address since writes can't cross a word boundary. In that case, you could read the value, do the fill, then write back the previous value. Same idea.

Eke
Very interested
Posts: 885
Joined: Wed Feb 28, 2007 2:57 pm
Contact:

Post by Eke » Wed Feb 16, 2011 5:49 pm

Normal VRAM writes are always 16-bit, so the first write that is carried out when you try and start a DMA fill will always be 16-bit. The DMA fill operation that follows will perform 8-bit writes.
Just to be sure, does the first write count in DMA length ?

Because if you look at the maximal rate for VRAM fill, it is indeed 15 bytes per line (outside VBLANK) which means there is one additional access (out of the 16 normally available) that is automatically done: this would be the first byte (LSB or MSB) of the 16-bit write that triggers DMA Fill operation, which mean the last byte of the 16-bit write would be included in total count.

I mean, if you set a DMA fill operation with length = 1, address = $1, it will first write VRAM addresses $0 and $1 (16-bit write) then stop right ? Or does it write an additional byte to address $2 instead ?

I' m emulating the latter (this was how Genesis Plus was originally coded) but I was having some doubt recently when trying to connect DMA operation with VDP external access slots.

Nemesis
Very interested
Posts: 791
Joined: Wed Nov 07, 2007 1:09 am
Location: Sydney, Australia

Post by Nemesis » Wed Feb 16, 2011 9:25 pm

I'll check my notes, but I'm pretty sure the length of the write is as you'd expect, IE, if you setup a fill for 8 bytes, 8 bytes will end up filled with the desired fill value. I have a feeling the first fill byte probably gets written twice though, once by the normal word write, then getting overwritten with the same value by the first write in the DMA fill operation. In that sence, it wouldn't really be "15 writes per line", more like "count+1 writes per fill", with the same access slots as any other write operation. Note that this would only be for CRAM and VSRAM though. I suspect when writing the documentation, the person doing it forgot that writes to VRAM are byte-wide, so the first write needs two slots to VRAM, making that "count+2 writes per fill".

This is speculation though. This is on my to-do list of things to test in hardware. In particular, I have no idea whether the VDP actually caches the fill byte for the entire operation, or whether it needs to read it back from VRAM periodically during the fill. It shouldn't need to, but I guess it probably depends on what was easier for the designers than anything else.

Oh, also note that from previous hardware tests, I know that most emulators get DMA fill wrong in subtle ways, the most common being the byte order of the first write. All the official and unofficial documentation I've seen gets the byte order of the first write (IE, the word write that triggers the fill) the wrong way around. Contrary to what you may read and observe in other emulators, the same byteswapping rules apply for writing to odd/even addresses in VRAM with a write to trigger a DMA fill, as with any normal write to VRAM. Even Kega makes this mistake IIRC.

Eke
Very interested
Posts: 885
Joined: Wed Feb 28, 2007 2:57 pm
Contact:

Post by Eke » Thu Feb 17, 2011 8:31 am

Nemesis wrote:I'll check my notes, but I'm pretty sure the length of the write is as you'd expect, IE, if you setup a fill for 8 bytes, 8 bytes will end up filled with the desired fill value.
I agree but the question is: does the DATA port write that triggers DMA Fill counts as one of these bytes (at least the second part of the 16-bit write) ?
You would think it is because otherwise you would end up with N+1 or N+2 modified bytes (depending if the VRAM address is odd or even, as explained below).

I have a feeling the first fill byte probably gets written twice though, once by the normal word write, then getting overwritten with the same value by the first write in the DMA fill operation.
Well, it depends on the VRAM address and increment register value.
With an increment value of 1, writing to an even address would indeed get the odd address written twice. But with an odd address ($0001 for example), the first word would put LSB at $0000, MSB at $0001 then address would be incremented by 1 and fill starts at $0002, so no address got written "twice" in that case.

In that sence, it wouldn't really be "15 writes per line", more like "count+1 writes per fill", with the same access slots as any other write operation.
Yes, that's what I wanted to say.

Note that this would only be for CRAM and VSRAM though. I suspect when writing the documentation, the person doing it forgot that writes to VRAM are byte-wide, so the first write needs two slots to VRAM, making that "count+2 writes per fill".

This is speculation though. This is on my to-do list of things to test in hardware.
If I have time, I will try to test it myself, it's very easy actually, set a fixed length for DMA fill then read back VRAM and see how many bytes were modified.

In particular, I have no idea whether the VDP actually caches the fill byte for the entire operation, or whether it needs to read it back from VRAM periodically during the fill. It shouldn't need to, but I guess it probably depends on what was easier for the designers than anything else.
Easiest would probably to use the FIFO since it stores the 16-bit written value from which fill byte is coming.
Oh, also note that from previous hardware tests, I know that most emulators get DMA fill wrong in subtle ways, the most common being the byte order of the first write. All the official and unofficial documentation I've seen gets the byte order of the first write (IE, the word write that triggers the fill) the wrong way around. Contrary to what you may read and observe in other emulators, the same byteswapping rules apply for writing to odd/even addresses in VRAM with a write to trigger a DMA fill, as with any normal write to VRAM. Even Kega makes this mistake IIRC.
Yes, that's something I noticed too, both official doc and genvdp.txt are wrong or inaccurate.
However, it was properly implemented in genesis plus: the 16-bit write that triggers DMA fill is handled like any other DATA port write (MSB goes to address, LSB goes to address ^1 and address is incremented) then DMA Fill starts immediately. Only thing I need to figure is if MSB is always used as fill byte, or if LSB can be used too, depending on the even/odd address thing... the 1st post of this thread seems to indicate that LSB is used as fill data.

Charles MacDonald
Very interested
Posts: 292
Joined: Sat Apr 21, 2007 1:14 am

Post by Charles MacDonald » Mon Feb 21, 2011 1:31 am

However, it was properly implemented in genesis plus: the 16-bit write that triggers DMA fill is handled like any other DATA port write (MSB goes to address, LSB goes to address ^1 and address is incremented) then DMA Fill starts immediately. Only thing I need to figure is if MSB is always used as fill byte, or if LSB can be used too, depending on the even/odd address thing... the 1st post of this thread seems to indicate that LSB is used as fill data.
IMO it's always the LSB. I could never get it to fill with the MSB regardless of the VRAM address being used.

Doesn't Kega get it right? All the games that have weird fill edge cases seem to work fine on it (Contra Hard Corps, Taiga Drama Taiheki), etc.

Eke
Very interested
Posts: 885
Joined: Wed Feb 28, 2007 2:57 pm
Contact:

Post by Eke » Mon Feb 21, 2011 8:52 am

I don't know, I had to change your implementation when porting to Gamecube (those games were indeed broken), I think it worked in the original PC version because the platform was little-endian: the rendering code read VRAM as words, i.e without swapping bytes (which is more efficient since VRAM is generally written with words as well) so when you write a byte (DMA Fill or DMA Copy) on a little-endian platform, it should be written to adjacent address instead (which is what the WRITE_BYTE macro does).

It now works fine (on both little or bi gendian platforms) but I use MSB (data >> 8 ) to fill VRAM

Post Reply