Jump to content
IGNORED

HoleyDMA & Atari 7800


Heaven/TQA

Recommended Posts

finally i have realised how this mode works... unfortunatly MARIA description & programming docs aren't so large as I wish. thanks to Dan's vertical sprite movement example (without any comments... :() i figured out how this works...

 

most of you know that the 7800 architecture is different to 2600,5200, Lynx and 800... and is more like on older arcade games... and works with "zones" & object lists for scanlines...

 

quote from the atari 7800 dev guide:

 

"The group of rasters specified by one DLL entry is called a

"zone." Again, the number of rasters in a zone equals OFFSET+1.

Larger zones mean less RAM is needed for DLLs, Display Lists and

Character Maps (see DMA MODES below). But upon consideration of

how to use zones, you will realize that to achieve smooth

vertical motion each stamp must be padded at top and bottom with

zeros. For example, if the top raster of an object is to appear

on the last line of a 16 high zone, it must have 15 lines of

zeros above it. If that object is 8 pixels (2 bytes) wide, and

its top line of data is located at x'CF04', then you

will need two bytes of zeros at x'D004', x'D104', x'D304',...,

and x'DE04' (remember that OFFSET decrements). As this can add

up to many pages of zeros, you can specify that MARIA should

interpret certain data as zeros, even if it isn't. This is

called "Holey DMA" because DMA will see "holes" in the data that

aren't really there. This can be enabled and disabled on a zone

by zone basis via a DLL entry. Holey DMA has been aimed at 8 or

16 raster zones, but will have the same effect for other zone

sizes. MARIA can be told to interpret odd 4K blocks as zero,

for 16 high zones, or odd 2K blocks as zeros for 8 high zones.

This will only work for addresses above x '8000'. This means

that these blocks can hold meaningful code, or tables, or

graphics data used in a zone where Holey DMA is not on."

 

 

so... my 1st mistake was... i mixed up "odd" with "even"... which wasn't good... ;)

 

then i went through Dan's vertical sprite demo and tried to understand how vertical sprite posititioning works on 7800 in detail. the basics are clear... but the holey DMA issue drove me crazy...

 

so... assuming you have build up the screen with zones covering each 16 scanlines. your sprite is f.e. 16x16 pixel...

 

remember that gfx data has to be layed out in ram with 256 bytes "scanlines"... f.e. the sprite would cover the adress space:

 

$a000-$a003

$a100-$a103

$a200-$a203

$a300-$a303

...

$af00-$af03

 

(16 pixel are in 160x2 mode, similar to antic e, 4 bytes as each byte holds 4 pixels)

 

for vertcial sprite positioning we have to modify the DLs for the zone(s) the sprite would appear... which means the starting adress of the sprite data... as it is written in the dev document...

 

to avoid a lot of wasting ROM space because of the "0" each sprite would need to be positioned smoothly... MARIA has the holeyDMA feature...

 

assuming holeyDMA16 (for 16 scanline zones) is set in the display list list (DLL) the dev doc says that now every odd 4k block above $8000 will be interpreted by MARIA as 0 even if there is no zero data...

 

ok... what does this mean? now my bad logical brain confused me... for me each 4k block is even, not odd... 4096 is even... but then i found this bit in the dev doc:

 

DLI - Display List Interrupt flag.

0 => No DLI.

1 => Interrupt after DMA on last line

of previous zone.

 

H16 - 16 high zone Holey DMA enable.

0 => Not enabled.

1 => Enabled. DMA interprets odd 4K

blocks as zeros. (A12 high => data=0)

 

H8 - 8 high zone Holey DMA enable.

0 => Not enabled.

1 => Enabled. DMA interprets odd 2K

blocks as zeros. (A11 high => data=0)

 

OFFSET - OFFSET starting value.

4 bits only.

 

DL ADDRESS - Address of Display List for this zone.

 

 

the interesting part is "A12 high => data=0" in the H16 section... now we have to think "binary". so let us perform a CLD in our brain... ;)

 

A12 means the Bit12 of the sprite data adress (A0-A15)

 

now... let us check:

 

Bit counting from left to right 15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0

 

$a000 = %10100000 00000000 (high low)

$a100 = %10100001 00000000

$a200 = %10100010 00000000

...

 

etc... Bit 12 is always 0 = even in MARIA definition so gfx data is being visible on screen.

 

now...for vertical movement we would need data "on top of our sprite at $b000-$bf00 for being possible to set the sprite on scanline basis...

 

so... let us write $b000 etc... in binary mode

 

$b000 = %10110000 00000000

$b100 = %10110001 00000000

$b200 = %10110010 00000000

...

 

and voila... Bit 12 is set for the whole range $b000 - $bfff ergo is this ROM space "odd" for Maria when holeyDMA16 is enabled... so data lying here will not be interpreted as sprite data but instead as "zero" = 0

 

so... now your code has just to calculate on which zone(s) your sprite appears and has to modify the DL entries including the sprite adress pointer... but this is not more complicated then it looks...check out dan's sprite demo...

 

it's still not so comfortable like on "real hardware sprite" systems... where you just need to alter x,y hardware adresses but with this method is very similar as you move the sprites by altering the "high byte" of your sprite pointer... and this is similar to the above system...you have just to add the starting adress of your sprite data to the y-position... and voila...

 

MARIA designes had really fast gfx in mind... as the chip seems designed for speed (and having the 6502 in mind as well...) all counters count downwards, gfx data has to be stored "top-down", gfx data has to be stored with "256 bytes" long scanlines in mind... all neat tricks by hardware which we demo coder would use in our own productions...

 

i hope i could explain a little bit to the newbies...

Link to comment
Share on other sites

On the 7800 the screen is broken up into "zones" of 1-16 scanlines (typically 8 or 16), based on the 3 byte DLL header. The DLL header contains a 16 bit address pointer to the display list for the zone.

 

The display list is made up of a sequence of 4 or 5 byte sprite headers ended by a 2 byte null header. Each direct sprite header contains a 16 bit address pointer to the graphics data drawn on the last scanline of the zone. (Indirect sprites, or tiles, are a little different.)

 

Graphics data is laid out "upside down" with the last scanline on the lowest address and each scanline on a separate page. e.g. for a 2 byte x 16 line sprite

$e000-$e001 bottom line

$e100-$e101 2nd last line

...

$ee00-$ee01 2nd line

$ef00-$ef01 top line

 

Horizontal motion on the 7800 is easy 'cause each sprite header contains a 1 byte horizontal position (though this means only 160 onscreen positions even in 320 modes). Vertical motion requires a little more work. Assume we are using 16 line sprites and zones. If the sprite is "on grid" then we have the following

 

line 0-15 DLL -> sprite header -> $e000

 

Now say that the sprite moves down one scanline, then the sprite is overlaps two zones and we need the following

 

line 0-15 DLL -> sprite header -> $e100

line 16-31 DLL -> sprite header -> $d100

 

Some of this seems counter-intuitive, but remember that the sprite header points to the bottom of the sprite. So the top scanline of the second zone will draw the graphics stored at $d100 + 15*256 = $e000 or the last scanline of the sprite. So the sprite has moved down one scanline.

 

This is also where "Holey DMA" comes into play. In the above example the top scanline of the first zone will draw the graphics stored at $e100 + 15*256 = $f000. In the DLL there are two flags, one which treats odd 4K segment of ROM as full of zeros. So when that flag is set, graphics reads from $dxxx and $fxxx will be treated as zeros. Thus we can store code and non-graphics data in those segments.

 

Now that we know how to handle vertical motion, the next challenge is building that silly display list. (See the mega sprite demo I posted to the Atari 7800 Programming mailing list.)

Link to comment
Share on other sites

it's still not so comfortable like on "real hardware sprite" systems... where you just need to alter x,y hardware adresses but with this method is very similar as you move the sprites by altering the "high byte" of your sprite pointer... and this is similar to the above system...you have just to add the starting adress of your sprite data to the y-position... and voila...

 

The nice thing about the 7800 hardware is that you can simulate a "real hardware sprite system", or any other type of graphics hardware you like. You would create a display engine that reads a table of sprite X,Y positions and generate the DL's from this table. Once this is done, you game logic only has to deal with the sprite table and not directly with the display lists. This also gives you the flexibility to design different display engines for different games. You might have an engine for just doing sprites, an engine for a tiled background with sprites, an engine for a side scrolling background, etc.

 

Dan

Link to comment
Share on other sites

just for comparison attachted the source code of my playstation1 intro... it's pure R3000 assembler... huj... it's from 1999... long time ago...

 

 

 

 

; basic psx-routines by Silpheed of HITMEN
; Heaven/Taquart 1st intro on psx!!!


;
; start: several weeks ago...
; end  : 05th jun 1999
;
; still playing around with 256 color-images...
;
; greetinx to all at napalm message-board
; and of course to all members of Taquart !!!


; - 06/05 dolphin sprite rendered
;         lets try to include it in demo...
;         yeah... i got it...
; - 06/05 try to fade the logo in...
;         yeah... works...
; - 06/05 now moving dolphin-head works, too
; - 06/05 added some gouraud-lines moving around
; - 06/06 after going out night i have new ideas to improve intro
;         i will add a lensflare moving behind the logo...









           org $80010000

           

           li sp, $801fff00

           li k1, $1f800000           ; set to hardware base



           li a0, $08000009           ; a0 = display-mode

                                      ; 8 = pal

                                       

           jal InitGPU                ; initialise the GPU

           nop



           la a0, image               ; transfer image data to VRAM

           li a1, 320                ; 320 position in vram

           li a2, $8000a0            ; image-size y,x/2 

           li a3, 10240              ; (x/2)*y div 2

           jal MEM2VRAM_IO

           nop



           la a0, clut                ; transfer clut data to VRAM

           li a1, $1000140

           li a2, $10100

           li a3, 128

           jal MEM2VRAM_IO

           nop

           nop



           la a0, dolphin             ; transfer dolphin to vram

           li a1, 512             ; at (320,256)

           li a2, $640028             ; size 80x100

           li a3, 2000

           jal MEM2VRAM_IO

           nop



           la a0, clut2

           li a1, $0000300

           li a2, $10100

           li a3, 128

           jal MEM2VRAM_IO

           nop


;exit        nop
;            j exit
;            nop



           jal InitPads               ; init pads, also the wait vsync routine

           nop



           la a0, module

           jal HM_Init                ; init the mod player

           nop

                                       

part1                                  ; fade logo in...



           li s0,$64000000

           li s1,$00111111

           li s3, 0

part1_loop0

           li s2, 0



part1_loop



           jal HM_Poll                ; call mod player

           nop



           jal WaitIdle               ; wait for GPU to finish processing

           nop

           

           jal WaitVSync              ; wait for vertical retrace period

           nop

           

           la a0, list    

           jal SendList               ; send display list to GPU

           nop



           addiu s2,1

           slt t0, s2, 2           ; wait for 2 frames before continuing

           bnez t0, part1_loop

           nop

           addu s3,1


;crash           j crash
;            nop





           addu s0, s1,s0

           sw s0, fade1

           sw s0, fade2



           slt t0, s3, 15

           bnez t0, part1_loop0

           nop
;crash           j crash
;            nop



part2       li s0,$009cffc0   ; move dolphin

           li s3,0



part2_loop



           jal HM_Poll                ; call mod player

           nop



           jal WaitIdle               ; wait for GPU to finish processing

           nop

           

           jal WaitVSync              ; wait for vertical retrace period

           nop

           

           la a0, list    

           jal SendList               ; send display list to GPU

           nop



           sh s0, dolphin_pos



           addu s0,1

           addu s3, 1

           slt t0, s3, 80            

           bnez t0, part2_loop

           nop

           


; Main parts of the intro:



           li s1, 0

           li s3,0

           li s4,0

part3
; draw the thetraeder...
; verry basic...;)
; there are 4 primitives, 3 sides and one top-one to "close" the thetraeder
; lineposnx means n=primitive nr; x which vertice of the triangle




;set all 4 primitives on the sin-tables to make nice moving...



           la s0, SinTab1

           addu s0,s1

           addu s1,2

           and s1,$000000ff

           lb s2, (s0)

           sb s2, linepos11

           sb s2, linepos41

           sb s2, linepos32

           sb s2, line_color31+2

           sb s2, line_color22+1



           lb s2, (s0)

           lb s2, 64(s0)

           sb s2, linepos12

           sb s2, linepos43

           sb s2, linepos21

           sb s2, line_color21+1

           sb s2, line_color11



           lb s2, 128(s0)

           sb s2, linepos22

           sb s2, linepos42

           sb s2, linepos31

           sb s2, line_color32+2

           sb s2, line_color12


;now move them a little bit vertical
;and set the vertice colors...



           la s0, SinTab2

           addu s0,s1

           

           lb s2, (s0)

           sb s2, linepos11+2

           sb s2, linepos22+2

           sb s2, linepos32+2

           sb s2, linepos41+2





           lb s2, 64(s0)

           sb s2, linepos12+2

           sb s2, linepos43+2

           sb s2, linepos21+2

           lb s2, 128(s0)

           addu s2, 48

           sb s2, linepos31+2

           sb s2, linepos42+2





           

           jal HM_Poll                ; call mod player

           nop

skipupdate            

           jal WaitIdle

           nop



           jal WaitVSync

           nop

   

           la a0, list

           jal SendList

           nop

                                           

           j part3                    ; loop forever

           nop

           

           



include silph.inc                      ; some useful routines            



align 4

image incbin logo31.raw                   ; the raw image data for the logo and chars

clut incbin logo31.clt                    ; clut info for above



align 4

dolphin incbin dolphin.raw             ; 80x100x256 head of dolphin

clut2 incbin dolphin.clt               ; clutinfo for dolphin



align 4

list include hvelist3.inc                  ; the display list



align 4

module incbin thesong.hit



align 4

include hitmod.inc



align 4

include sintab.inc

 

and here is the interesting part... the "Object list" for the grafix processor of the PSone...

 


prim291   ; SPRT

   db prim292, prim292>>8, prim292>>16, $4



fade1 dw $64000000

lpos1

   dw $00000004

   dw $40140000

   dw $800100



prim292   ; DR_TPAGE

   db prim293, prim293>>8, prim293>>16, $1

   dw $E1000086



prim293   ; SPRT

   db prim294, prim294>>8, prim294>>16, $4



fade2    dw $64000000

lpos2

   dw $00000104

   dw $40140080

   dw $800040





prim294        ; dr_tpage

   db prim295, prim295>>8, prim295>>16, $1

   dw $e1000088



prim295        ; dolphin
;     db $ff,$ff,$ff, $4



   db prim100, prim100>>8, prim100>>16, $4

   dw $64aaaaaa

dolphin_pos

   dw $009cffb0

   dw $00300000       ; clut at $00003000

   dw $640050



prim100        ; gouraud triangle

   db prim101, prim101>>8, prim101>>16, $6

line_color11

   dw $30000080

linepos11

   dw $00800080

   dw $00ffffff

   dw $00f000c0

line_color12

   dw $00000080

linepos12

   dw $00800080



prim101        ; gouraud triangle

   db prim102, prim102>>8, prim102>>16, $6

line_color21

   dw $30000000

linepos21

   dw $00800080

   dw $00ffffff

   dw $00f000c0

line_color22

   dw $00000000

linepos22

   dw $00800080



prim102        ; gouraud triangle

   db prim103, prim103>>8, prim103>>16, $6

line_color31

   dw $30800000

linepos31

   dw $00800080

   dw $00ffffff

   dw $00f000c0

line_color32

   dw $00800000

linepos32

   dw $00800080



prim103        ; gouraud triangle

   db $ff, $ff, $ff, $6

line_color41

   dw $30115011

linepos41

   dw $00800080

   dw $00ffffff

linepos43

   dw $00f000c0

line_color42

   dw $00ffffff

linepos42

   dw $00800080



   



prim999   ; BLOCK FILL

   db $ff, $ff, $ff, $3

   dw $02634522

   dw $0

   dw $F0140


; End of display list



 

and you see it's the main concept as MARIA chip has... and it's even called display list... ok...you have more possibilities but basicly it's the same... but each object in the list can have every postion on the screen...

 

sorry...don't know why i have posted that...

Link to comment
Share on other sites

  • 1 year later...
This is also where "Holey DMA" comes into play.  In the above example the top scanline of the first zone will draw the graphics stored at $e100 + 15*256 = $f000.  In the DLL there are two flags, one which treats odd 4K segment of ROM as full of zeros.  So when that flag is set, graphics reads from $dxxx and $fxxx will be treated as zeros.  Thus we can store code and non-graphics data in those segments.

 

Wow, I've been trying to understand Holey DMA and almost gave up. This post cleared it right up. Thanks for this. Damn, it's really simple.

Link to comment
Share on other sites

I dunno, I understood it right away, and got it right the first time when I actually wrote some code that used it.

 

Basically it just handles going over the top or bottom edge of your sprites for you. Every other zone-sized memory block is a "no-mans land" which the Maria pretends always contains nothing. So you either start or end outside your graphics memory block far enough to show just the scan lines you need.

 

The Maria is a really nice design, if it weren't for stuff like the wierd pixel mappings, the horribly low-res 160-pixel mode, and the cycle-stealing DMA. (And the pathetic 2K SRAM when the 5200 and ColecoVision already had 16K DRAM.) If the 7800 had been released in 1984 and if it had become a success, Atari would probably have had to release an upgraded Maria+ system of some sort just to stay competitive.

 

Heaven/TQA: could you please edit your post to not have all those extra blank lines? There's something wierd about this BB software that it throws a bunch extra blank lines into CODE tags.

 

Horizontal motion on the 7800 is easy 'cause each sprite header contains a 1 byte horizontal position (though this means only 160 onscreen positions even in 320 modes). Vertical motion requires a little more work.

A little more work? Not only do you have to decide how many zones your sprite fits into, you have to figure out which zones and then insert into variable-length display lists.

Link to comment
Share on other sites

Maybe you can explain this to me regarding video resolution.

 

Why does the NES have a better resolution than the Atari's? From the Atari docs I've read, they seem to imply that you cannot exceed the 160x192 resolutions. But the NES has a 256x224 resolution--much better.

 

After reading this, I changed my DLL to use 224 lines (14 zones, 16 high), and it worked in the emulator, but I don't have an MMC card for my CC2 yet, so I could not test it on real hardware.

 

How does the NES pull this off where Atari cannot?

Link to comment
Share on other sites

Maybe you can explain this to me regarding video resolution.

 

Why does the NES have a better resolution than the Atari's?  From the Atari docs I've read, they seem to imply that you cannot exceed the 160x192 resolutions.  But the NES has a 256x224 resolution--much better.

 

After reading this, I changed my DLL to use 224 lines (14 zones, 16 high), and it worked in the emulator, but I don't have an MMC card for my CC2 yet, so I could not test it on real hardware.

 

How does the NES pull this off where Atari cannot?

840559[/snapback]

Atari recommends using 192 scan lines because that would fit with every NTSC TV. IIRC you can fit 200 scan lines inside a modern TV frame without issues. Outside of that the full screen may not show.

 

Also, remember MARIA handles VSYNC for you (unlike STELLA). So the 7800 truly has a vertical resolution of 243 scan lines.

Edited by DEBRO
Link to comment
Share on other sites

Atari recommends using 192 scan lines because that would fit with every NTSC TV. IIRC you can fit 200 scan lines inside a modern TV frame without issues. Outside of that the full screen may not show.

840605[/snapback]

Also, the more lines you show, the less CPU time you get to do computations, because of the DMA cycle stealing. Emulators will generally not show the effect of this.

 

And don't forget the 320 pixel modes. Not a lot of colors (320B gives you a total of seven, and another 320 mode gives you a total of nine, but you can only use three+background at most in any object), and lots more of a DMA hit, but it's got nice square pixels.

 

I wish they had made a 240 pixel mode for Maria. 160 is low enough to be ugly, even with 4 bit pixels (which is what, 13 colors due to the palette mapping?), and 320 is just too hungry.

Link to comment
Share on other sites

And don't forget the 320 pixel modes.  Not a lot of colors (320B gives you a total of seven, and another 320 mode gives you a total of nine, but you can only use three+background at most in any object), and lots more of a DMA hit, but it's got nice square pixels.

 

In essance you are saying the 7800 should be able to run in a 320x200 mode with a palette selection of seven or nine colors?

Link to comment
Share on other sites

Maybe you can explain this to me regarding video resolution.

 

Why does the NES have a better resolution than the Atari's?  From the Atari docs I've read, they seem to imply that you cannot exceed the 160x192 resolutions.  But the NES has a 256x224 resolution--much better.

A standard NTSC field has 262 lines (312 for PAL), however 9 of those lines are part of the vertical sync and some of the remaining lines are not visible onscreen. There's nothing preventing a 7800 game from outputting 224 lines (or even 240) of graphics data.

 

The horizontal resolution is more of a design decision. The 7800 video signal is driven by a clock which is a even multiple of the colorburst frequency (x1 for 160, x2 for 320), while the NES uses a 1.5 multiplier. More pixels also increase the on-chip line RAM requirements.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...