Jump to content
IGNORED

Harmony DPC+ programming


Recommended Posts

I wanted a fully programmable playfield and two full color sprites and quickly realized that DPC+ with fastfetch would let me focus on writing a game instead of a bunch of custom kernels.

 

I cut and paste a bunch of the sample code and got my own app working. Nothing special about my code otherwise, but it might be easier to start with.

 

(Note that my code doesn't bother to do the special "priming read" that SpiceWare's code has in it, been meaning to ask him about that)

 

-Dave

 

Note: Example sprite from this thread -- http://www.atariage.com/forums/topic/169238-free-sprites-for-the-taking/ -- courtesy of PAC-MAN-RED.

hack2600.zip

Edited by DavidEth
  • Like 1
Link to comment
Share on other sites

(Note that my code doesn't bother to do the special "priming read" that SpiceWare's code has in it, been meaning to ask him about that)

That's only needed for a special condition when using the Fractional Data Fetcher. It's covered in DPCplus.h

 

;----------------------------------------
; Fractional Data Fetcher
;----------------------------------------
; Another 8 Data Fetchers exist which work differently than the first 8.
; These allow you to fractionally increment the Data Fetcher so a single
; value can be read a set number of times before advancing to the next value.
; This is commonly used to draw asymmetrical playfields without needing to 
; use 1200 bytes of data (200 scanlines * 6 playfield updates).
; Before using, you must point the Fractional Data Fetcher at the data to read
; via DFxFRACLOW and DFxFRACHI.  You must also set the increment value via
; DFxFRACINC.
;
; Set pointer
;	LDA #<PlayfieldPF0l
;	STA DF0FRACLOW
;	... repeat for PF1l, PF2l, PF0r, PF1r and PF2r
;	lda #>PlayFieldPF0l
;	STA DF0FRACHI
;	... repeat for PF1l, PF2l, PF0r, PF1r and PF2r
; Set the increment to repeat the value for x reads
;	LDA #(256/x)
;	STA DF0FRACINC
;       STA DF1FRACINC
;	... repeat for 2-5
;
; Special Condition - IF you want to increment the pointer after every read 
; (just like the normal Data Fetcher), then use the following to set the
; increment AND prime the Fractional Data Fetcher
;	LDA #255
;	STA DF0FRACINC
;       STA DF1FRACINC
;	... repeat for 2-5
;	LDA DF0FRACDATA - priming read (first value will be read twice)
;	LDA DF1FRACDATA - priming read (first value will be read twice)
;	... repeat for 2-5
;
; then in the kernel read the Fractional Data Fetchers and update the playfield
;	LDA DF0FRACDATA
;	STA PF0
;	LDA PF1FRACDATA
;	STA PF1
;	... repeat for Data Fetchers 2-5, putting them in PF2, PF0, PF1 and PF2
;----------------------------------------

Link to comment
Share on other sites

So is the priming read just because you're using 1.8 fixed point with only 8 fractional bits? In other words, without the priming read, you're going to return the first data twice, because if the counter starts at zero and the most you can add is 255, you'd get:

 

0x0000 - return line 0

0x00FF - return line 0

0x01FE - return line 1

0x02FD - return line 2

0x03FC - return line 3

 

It wouldn't repeat a line again for another 255 or so scanlines, typically more than you're ever going to render on a 2600.

 

-Dave

Link to comment
Share on other sites

That's it exactly.

 

In the big scheme of things none of this matters, but since this is all just firmware anyway, there are three possible "fixes" to this nonexistent problem:

 

1. Treat 0 as a special case meaning 256, either implicitly, or by having the "hardware" add 0x0100 - FRAC.

2. Make the fractional value 1.7 or 2.6 instead of 0.8; this would also let you zoom out a playfield vertically, for whatever that is worth.

3. Whenever the address is rewritten (which presumably zeroes out the fractional LSB's), just set the LSB's to %0000 0001 instead of pure zero.

 

-Dave

Edited by DavidEth
Link to comment
Share on other sites

You can add 256 by always setting the carry. But then you can't add 0 anymore.

 

Yeah, that would work too. I'm not sure how useful adding zero would be anyway, since you can achieve the same end (assuming 0.8 fixed point) by using 1/256th, which will not overflow for another 255 scanlines either. Main problem with always adding 1 is that it throws off all the rest of the fractions (although 0x100 - FRAC is even worse in this regard).

 

-Dave

Link to comment
Share on other sites

SpiceWare,

 

I don't understand what's going on here:

 

SetChopper0:
lda #ChopperGraphicHeight
clc
adc Chopper0Y
cmp #ChopperGraphicHeight
bcs NoYadjustC0
lda #$FF
.byte #$2c
NoYadjustC0
lda #$0
sta NegativeYadjust

sec
lda TempPtr
sbc Chopper0Y
sta DF0LOW
lda TempPtr+1
sbc NegativeYadjust
sta DF0HI

 

Seems like it's comparing Y+8 to 8 and using the result of that test to store 0 or -1 into an adjust value. Seems like it could just be comparing Y to zero directly?

 

-Dave

Link to comment
Share on other sites

It's for when the sprite moves up off the top of the screen. As soon as the Y position hit -1 the sprite would disappear or turn into garbage because the Y is treated as 255 and the Data Fetcher ended up pointing to the wrong page for the graphics. I only check for negative values back to -height as 128 can be considered -127 but for display of a sprite we need to treat it as +128.

 

I'm sure there's a lot of optimizations that could be done in the demos, but my goal was to show how to utilize DPC+ and once I got a routine working I moved onto the next one.

Link to comment
Share on other sites

  • 6 months later...

Does Stella correctly reflect the cycle times when FastFetch mode is enabled? It should read 2 cycles for any LDA instruction from DPC registers such as:

 

lda DF0DATA, however it still shows 4 cycles so I assume it was never updated to reflect this mode? I just want to be sure FastFetch is working properly before I go pushing my kernal to the cycle maximums.

Link to comment
Share on other sites

Does Stella correctly reflect the cycle times when FastFetch mode is enabled? It should read 2 cycles for any LDA instruction from DPC registers such as:

 

lda DF0DATA, however it still shows 4 cycles so I assume it was never updated to reflect this mode? I just want to be sure FastFetch is working properly before I go pushing my kernal to the cycle maximums.

 

I think you must do something like LDA #DF0DATA because the 6507 will have to be executing an immediate load to take 2 cycles - looking at David's code he is using:

 

LDA #<DF0DATAW ; 0,1

STA GRP0 ; 2,3,4

LDA <#DF4DATA ; 5,6

STA COLUP0 ; 7,8,9

LDA <#DF5DATA ; 10,11

STA COLUP1 ; 12,13,14

  • Like 1
Link to comment
Share on other sites

Always appreciate the info.

 

What is the proper method for padding out each bank?

I've tried doing a "ds ($1000 - *)" in each bank after the code segment before the vector table but dasm just sits there churning out a huge file.

 

I've looked at the example project and not sure why this wouldn't work the same. Might be incorrect addressing of *? each bank is RORG to $F000.

 

;**********************************************************
;--------------------
;---[sTART BANK 5]--- 
;--------------------
 ORG  $5000  
 RORG $F000
 JUMP_TABLE
Bank5Code:
;********************
 BASIC_KERNAL  ;MACRO  
 ;Pad remaining bank space
 ds ($0FF6 - *) ;Refuses to work here...
;********************
 ORG  $5FF6
 RORG $FFF6
 BANKS_AND_VECTORS 
;---------------------
;---[END OF BANK 5]--- 
;---------------------

;********************************
;   [*GRAPHICS ONLY* BANK 6]
;********************************
 ORG  $6000
 RORG $0000
 
,[...] Graphic data ect

 ds ($1000 - *) ;Works fine here

 

I use INCBIN at the head of the file to attach the DPCplus.arm code stub, so the rest "should" pad to the correct 32k size.

Link to comment
Share on other sites

I don't see any need for the DS ($0FF6 - *) as the next ORG will automatically fill in the space.

IIRC you need as least one byte at the beginning of each bank before ORG will work.

True, but I was assuming the JUMP_TABLE macro had something in it. For that matter, it needs at least 128 bytes of padding so code doesn't overlap DPC+ addresses.

Link to comment
Share on other sites

Can I get "lDA #<DF0DATA" to take 3 cycles to execute instead of 2?

 

I've got some cycle exact display timing going on and being 1 cycle off makes things look messy. I don't have the luxury of adjusting the timing elsewhere. The loads have to be 3 cycles :( and I really want to use the DPC+ as a display output buffer. It's so much nicer this way :D

 

If not, then much sadness on my end.

Link to comment
Share on other sites

Can I get "lDA #<DF0DATA" to take 3 cycles to execute instead of 2?

 

I've got some cycle exact display timing going on and being 1 cycle off makes things look messy. I don't have the luxury of adjusting the timing elsewhere. The loads have to be 3 cycles :( and I really want to use the DPC+ as a display output buffer. It's so much nicer this way :D

 

If not, then much sadness on my end.

 

LDA.w #<DF0DATA

Link to comment
Share on other sites

Nevermind I fixed it, thank you.

;*************
;* [FRAME 1/2]
;*************
FRAME1:     sta WSYNC               ;[]+3   ;-PixelPos [-68], Color clock [0]
           dey                     ;[0]+2    ;Decrement scanline and compare to #0
           bne .doDraw1            ;[2]+2/3  ;If more scanlines left, branch
           lda #$FF                ;[]+2     ;Disable FastFetch mode        
           sta FASTFETCH           ;[]+4     ;-
           jmp OVERSCAN            ;[]+3     ;EXIT PLAYFIELD KERNAL
           ;***CYCLE EXACT DRAW KERNAL*** 
.doDraw1    sleep 14                ;[5]+14   ;Cycle padding         
           ;***Preload P0(0) and P1(0)
           lda #<DF0DATA           ;[19]+2 Load  P0(0) ;[18] <-Cycles we need to be at
           sta.w GRP0              ;[21]+4 Store P0(0) ;[21]
           lda #<DF0DATA           ;[25]+2 Load  P1(0) ;[24]
           sta.w GRP1              ;[27]+4 Store P1(0) ;[27]      
           ;***TIME TO DRAW!         
           ;***Waiting for P0(0) to draw
           ;***Update P0 2 More times
           lda #<DF0DATA           ;[31]+2 Load P0(1)  ;[30]
           sta.w GRP0              ;[33]+4 Draw P0(1)  ;[33]            
           lda #<DF0DATA           ;[37]+2 Load P0(2)  ;[36]
           sta.w GRP0              ;[39]+4 Draw P0(2)  ;[39]
           sleep 4                 ;[43]+4 Padding     ;[42]
           ;***Waiting for P1(0) to draw
           ;***Update P1 2 More times
           lda #<DF0DATA           ;[47]+2 Load P1(1)  ;[46]      
           sta.w GRP1              ;[49]+4 Draw P1(1)  ;[49]
           lda #<DF0DATA           ;[53]+2 Load P1(2)  ;[52]
           sta.w GRP1              ;[55]+4 Draw P1(2)  ;[55]    
           ;***END DRAW***            
           jmp FRAME1              ;[]+3 ;Will WSYNC after jump

[edit] Corrected the timing. "Slaps forehead"

Why didnt I just adjust my initial sleep delay 1 cycle to begin with.

Edited by ScumSoft
Link to comment
Share on other sites

New problem, most likely an oversight as I've been coding all day.

FillScreenRAM:
           ;***Set Data Fetcher 0 to TitleScreen***
           lda #<TitleScreen       ;Bank5
           sta DF0LOW
           lda #>TitleScreen
           sta DF0HI
           ;***Set Data Fetchers 1,2 to ScreenRAM***
           lda #<ScreenRAM1        ;Bank6
           sta DF1LOW
           lda #>ScreenRAM1
           sta DF1HI
           lda #<ScreenRAM2        ;Bank6
           sta DF2LOW
           lda #>ScreenRAM2
           sta DF2HI
           lda #$0                 ;Enable FastFetch
           sta FASTFETCH           ;-
           ;***Start ScreenRAM fill***
           ldx #11                 ;Load 2304 bytes total
.fillRAM    ldy #$E8                ;-230 bytes * 10 blocks of data
.fillRAMa   lda DF0DATA             ;Load byte from DF0
           sta DF2WRITE            ;Store in DF2
           dey                     ;Next byte counter
           lda DF0DATA             ;Load next byte from DF0
           sta DF1WRITE            ;Store in DF1
           dey                     ;Next byte counter
           bne .fillRAMa           ;repeat
           dex                     ;Next data block
           bne .fillRAM            ;repeat
           ;***Done***
           lda #$FF                ;Disable FastFetch mode
           sta FASTFETCH           ;-
           rts                     ;Return

I am tying to transfer in a 2304-byte block of data from bank 5 into the ScreenRAM buffers:

;--------------------------------------------------------------------------------------------------
;*********************
; [sTART OF BANK 5]
;*********************
 ORG  $5000                        ;Relocatable ORG address
 RORG $1000                        ;-
 JUMP_TABLE                        ;Required Jump table for bank switching
;********************
Bank5Code:

TitleScreen: ;Placeholder for code testing right now
 ;***[2304 bytes]***
 REPEAT 2304
 .byte #$FF
 REPEND
;********************
 ORG  $5FF6                        ;Relocatable ORG address
 RORG $1FF6                        ;-
 BANKS_AND_VECTORS                 ;Required Jump table for bank switching
;/////////////////////
;---[END OF BANK 5]---
;/////////////////////

;********************************
;   [*GRAPHICS ONLY* BANK 6]
;********************************
 ORG $6000
 RORG $0000
 
ScreenRAM1 ds 1152
ScreenRAM2 ds 1152

 ds ($1400 - *)

 

Yet it never fetches the data and returns all #$00's instead of #$FF

 

What did I miss this time?

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...