Jump to content
IGNORED

Harmony DPC+ programming


Recommended Posts

With the release of BIOS 1.05, the Harmony cartridge now supports programs with custom bank switch routines. DPC+ is the first to be released.

 

DPC+ expands upon the capabilities of the DPC chip found in Pitfall II. The original DPC supported two 4K banks and 2K of Display Data (holds graphics and other data). DPC+ expands that to six 4K banks and 4K of Display Data and adds some new features such as "Fast Fetcher" mode. Some DPC features are not supported by the DPC+ (mostly those not used by Pitfall II).

 

 

This demo shows off all the features of DPC+, with the exception of custom ARM code. I plan to release that demo next weekend. For in depth information on the features, read the comments at the start of DPCplus.asm as well as the comments in DPCplus.h.

 

Your code should result in a 29K BIN file. It should then be merged with DPC+.arm, which contains the custom bank switching code, to result in a 32K BIN file. When merging, the DPC+.arm file must come first.

 

With the exception of custom ARM code, Stella also supports DPC+ so you can test and debug your programs via Stella. Use version 3.1.2 or higher.

 

This code includes 6 demos, one for each 4K bank. Use GAME SELECT to cycle between the demos.

 

Demo 0 - Data Fetcher Demo

  • Shows how to use the Data Fetchers in both "Windowed" and "non-windowed" modes.
  • Joystick moves sprite, fire button changes image (unless qualified, "joystick" refers to the left joystick)
  • post-3056-127463698799_thumb.png

Demo 1 - Music Demo

  • Joystick disables voices via fire, right and/or down
  • Right Joystick selects waveforms. Fire, up, down, left, right
  • sprites are used to display the amplitude in real time
  • post-3056-12746376061_thumb.png

Demo 2 - Fractional Data Fetcher Demo

  • Draws an asymmetrical playfield where the playfield data is repeated over multiple lines
  • Joystick up/down controls the fractional increment amount
  • post-3056-127463700166_thumb.png

Demo 3 - Random Number Demo

  • Shows off the 32 bit random number generator
  • Joystick left to select prior set of random numbers
  • Joystick right to select next set of random numbers
  • Fire to reset the generator to the initial random seed
  • post-3056-1274637008_thumb.png

Demo 4 - Data Writer Demo

  • Unlike the DPC cartridge, the DPC+ Display Data ends up in RAM and can be modified at run time.
  • Joystick to move snowman - notice when he moves on/off screen he is masked to smoothly enter/exit the display. (compare with Demo 0)
  • Fire to flip snowman - this shows the difference between DFxWRITE and DFxPUSH.
  • post-3056-127463701261_thumb.png

Demo 5 - Fast Fetch Demo

  • Shows how to enable "Fast Fetch" mode, which overloads LDA # allowing you to read DPC+ registers in 2 cycles instead of 4
  • 29 TIA updates every 2 scanlines.
  • use both joysticks to move helicopters
  • post-3056-127463701796_thumb.png

 

DPC+ Demo source code

DPCplus Demo.zip

 

DPC+ Demo, ready for Harmony and Stella

DPC+ demo.bin

 

 

Addendum Oct 16, 2022, the above ROM no longer downloads so I've reattached it:

DPC+ demo.bin

  • Like 6
Link to comment
Share on other sites

And I'm looking forward to seeing what y'all come up with :)

 

I may not finish what I planned for the ARM code demo, but I will release it this weekend anyway as it does show what you need to know.

 

Also, don't ignore this demo if you're waiting for the forthcoming ARM Code Demo as the ARM Code Demo will assume you're already familiar with this demo. Oh, and "ARM Code" really means C Code.

Link to comment
Share on other sites

  • 3 months later...

I ran into an issue using DPC+ and Fast Fetch mode and wanted to give y'all a heads up:

 

My original code was this:

 

 	dey		; 2 24
cpy #10		; 2 26
bcc ExitLoop	; 2 28
lda #<AMPLITUDE	; 2 30 load music data for next line

 

The code worked fine under Stella, but crashed on a real system. Turns out that when a branch command (BCC, BCS, BEQ, etc) is used, the command immediately after is always fetched even if it ends up not being used (most likely why taking a branch is 3 cycles while not taking the branch is 2 cycles). Normally it's not a problem, but when Fast Fetch mode is turned on the ARM routines see the LDA # and ends up return a Fast Fetch value even though you didn't get to the command. This leads to unexpected results, such as the crashing.

 

A quick fix was this:

 	dey		; 2 24
cpy #10		; 2 26
bcc ExitLoop	; 2 28
nop		; 2 30 - CANNOT have an LDA #< immediately after a
		;        branch instruction when using Fast Fetch mode
		;        as the 6507 always fetches instruction
		;        following the branch command.
lda #<AMPLITUDE	; 2 32 load music data for next line

Edited by SpiceWare
Link to comment
Share on other sites

Interesting -- that must be how FE-style bankswitching works on Robot Tank then? Hardware on the cart itself sees if an opcode being fetched is a JSR or RTS and behaves accordingly. How do you tell fetching a JSR opcode (or in your case, LDA #imm) apart from just a normal data read?

 

(Is the source code to the Harmony BIOS available? The 1.05 download I tried only had a bunch of .arm binary files)

 

-Dave

Link to comment
Share on other sites

Interesting -- that must be how FE-style bankswitching works on Robot Tank then? Hardware on the cart itself sees if an opcode being fetched is a JSR or RTS and behaves accordingly. How do you tell fetching a JSR opcode (or in your case, LDA #imm) apart from just a normal data read?

 

(Is the source code to the Harmony BIOS available? The 1.05 download I tried only had a bunch of .arm binary files)

 

-Dave

 

I think FE bankswitching just works by switching on any access to $FE

 

The main program is stored in one bank and all subroutines in the other. The stack pointer is set to $FF at startup and any jsr will push values to $FF and $FE

the rts will then pull values from $FE and $FF.

Edited by eshu
Link to comment
Share on other sites

 

I think FE bankswitching just works by switching on any access to $FE

 

The main program is stored in one bank and all subroutines in the other. The stack pointer is set to $FF at startup and any jsr will push values to $FF and $FE

the rts will then pull values from $FE and $FF.

 

But I thought that the cartridge is never going to see any access to $FE. In other words, bit 12 of the address line of the 6507 is wired to the "cartridge select" line?

 

-Dave

Link to comment
Share on other sites

I think FE bankswitching just works by switching on any access to $FE

 

The main program is stored in one bank and all subroutines in the other. The stack pointer is set to $FF at startup and any jsr will push values to $FF and $FE

the rts will then pull values from $FE and $FF.

 

But I thought that the cartridge is never going to see any access to $FE. In other words, bit 12 of the address line of the 6507 is wired to the "cartridge select" line?

 

-Dave

 

I'm a lot better at software than hardware, so take this with a pinch of salt - but I think that is just how most carts are wired to access the (EP)ROM chips in the cart, I think the whole address bus is exposed on the cart port...

Link to comment
Share on other sites

(Is the source code to the Harmony BIOS available? The 1.05 download I tried only had a bunch of .arm binary files)

I've not seen the source for the Harmony BIOS, I don't think batari has released it.

 

With stephena's and batari's help, I did the implementation of DPC+ in Stella. It supports all but the ability to run custom ARM code. You can get that from here, look for the files CartDPCPlus.hxx and CartDPCPlus.cpp in src/emucore/.

Link to comment
Share on other sites

I think FE bankswitching just works by switching on any access to $FE

 

I've read varied descriptions of how it works. To really find out, one would have to do something like design a board which would feed address/data values to a cart with particular timings and see the result. From some descriptions, it sounded as though the FE cart might be sampling one of the data bits some time after an 01Fx access. Doing that with the proper timing would allow one to JSR/RTS between banks arbitrarily (the cycle following the second stack access will be the MSB of the next code address).

Link to comment
Share on other sites

I ran into an issue using DPC+ and Fast Fetch mode and wanted to give y'all a heads up:

 

My original code was this:

 

 	dey		; 2 24
cpy #10		; 2 26
bcc ExitLoop	; 2 28
lda #<AMPLITUDE	; 2 30 load music data for next line

 

The code worked fine under Stella, but crashed on a real system. Turns out that when a branch command (BCC, BCS, BEQ, etc) is used, the command immediately after is always fetched even if it ends up not being used (most likely why taking a branch is 3 cycles while not taking the branch is 2 cycles). Normally it's not a problem, but when Fast Fetch mode is turned on the ARM routines see the LDA # and ends up return a Fast Fetch value even though you didn't get to the command. This leads to unexpected results, such as the crashing.

 

A quick fix was this:

 	dey		; 2 24
cpy #10		; 2 26
bcc ExitLoop	; 2 28
nop		; 2 30 - CANNOT have an LDA #< immediately after a
		;        branch instruction when using Fast Fetch mode
		;        as the 6507 always fetches instruction
		;        following the branch command.
lda #<AMPLITUDE	; 2 32 load music data for next line

The above was due to a bug in DPC+ ARM code, and it has been fixed. I have some other improvements I intend to add to the ARM code, and I'll post a new version once those are in place.

Interesting -- that must be how FE-style bankswitching works on Robot Tank then? Hardware on the cart itself sees if an opcode being fetched is a JSR or RTS and behaves accordingly. How do you tell fetching a JSR opcode (or in your case, LDA #imm) apart from just a normal data read?

 

(Is the source code to the Harmony BIOS available? The 1.05 download I tried only had a bunch of .arm binary files)

 

-Dave

I'd expect that the FE hardware is as simple as possible, and looking for opcodes is not simple for an 80's cart. Essentially, it grabs bit 5 of the PCH from the stack, which actually occurs at $01FF, or possibly just even-numbered addresses in the range $0101-$01FF (and $01FE is not an odd-numbered address, so perhaps the scheme has a misleading name.)

 

To work with the FE binaries, you need to invert D5 to select the bank (though the actual cart probably had the banks swapped.)

 

I have seen modern implementations of FE that will work with any arbitrary JSR/RTS regardless of the stack pointer, but to support the three or four existing FE games, I doubt that is necessary.

Edited by batari
Link to comment
Share on other sites

I'd expect that the FE hardware is as simple as possible, and looking for opcodes is not simple for an 80's cart. Essentially, it grabs bit 5 of the PCH from the stack, which actually occurs at $01FF, or possibly just even-numbered addresses in the range $0101-$01FF (and $01FE is not an odd-numbered address, so perhaps the scheme has a misleading name.)

 

It would be bit 5 of the cycle following the 1FE access (could be $01FF for an RTS, or it could be the last byte of a JSR instruction). The actual bank switch needs to be delayed long enough to ensure that the operand fetch for the JSR isn't corrupted. It would seem the timing would be a little fussy. I can imagine that it might be handy, for some games, to simply switch one way on every JSR, and the other way on every RTS. That would be easy enough to do, though there are a number of ways one could do it, with differing effects for accesses other than JSR or RTS.

Link to comment
Share on other sites

The above was due to a bug in DPC+ ARM code, and it has been fixed. I have some other improvements I intend to add to the ARM code, and I'll post a new version once those are in place.

Is the ability to set DPC+ registers from custom ARM code one of those improvements? That could be very handy.

Link to comment
Share on other sites

  • 2 weeks later...

What are you looking for?

I want to play around a bit with DPC. It looks very interesting and powerful. I understand that the DPC does too much to include in a hello world.

 

My main questions would be:

1. How do I change my source code so Stella recognises it as a DPC program? Maybe it's easier to start from a template, which includes all the banks etc.?

2. How can I use the datafetcher to display just one sprite.

 

 

Edit: maybe I should just start examining your DPCplus.asm My head was spinning the first time I read it, but maybe I can figure out what I need from it. It's just that it's a lot of code (6000+ lines) to scroll through.

Link to comment
Share on other sites

1 - the ROM coming out of DASM needs to be 29K, and it'll needs to be appended to the 3K DPC+.arm file (it contains the ARM code that makes it work). That's done via the CAT command in readme.txt

 

2 - focus on just the code in bank 0 - specifically the section that starts with the label SetFrostyShape.

 

SetFrostyShape:	
;----------------------------------------
; this shows how to set the Data Fetchers to draw a Sprite using a Window
; and to change the sprite colors w/out using a window
;----------------------------------------
; 	point Data Fetcher 0 to the sprite data
;	lda #<(SpriteDataPosition - HowFarDownScreen)
;	sta DF0LOW
;	lda #>(SpriteDataPosition - HowFarDownScreen)
;	sta DF0HI

;	set the window for Data Fetcher 0
;	lda #<(SpriteDataPosition - 1)
;	sta DF0TOP
;	lda #<(SpriteDataPosition + ImageHeight)
;	sta DF0BOT

; 	point Data Fetcher 1 to the color data
;	lda #<(ColorDataPosition - HowFarDownScreen)
;	sta DF1LOW
;	lda #>(ColorDataPosition - HowFarDownScreen)
;	sta DF1HI 

; if Y is negative then we need to compensate
; else the data fetcher will point to the wrong page
; and the graphics and/or color data will be incorrect
ldx FrostyImage
lda FrostyHeights,x
clc
adc FrostyY
cmp FrostyHeights,x
bcs NoYadjust
lda #$FF
.byte $2c
NoYadjust	
lda #$0
sta NegativeYadjust

; Prep Y for the shape table
lda FrostyImage
asl
tay

; set Data Fetcher 0 based on Frosty's shape and Y position
sec
lda FrostyShapes,y
sbc FrostyY
sta DF0LOW
lda FrostyShapes+1,y
sbc NegativeYadjust
sta DF0HI	

; set the "window" the sprite is visible thru.  Use DFxDATAW to read
; the "windowed" data.
ldx FrostyImage		; use X to lookup height of current image
lda FrostyShapes,y
tay
dey			; adjust TOP value by 1
sty DF0TOP
clc
adc FrostyHeights,x
sta DF0BOT

; for colors we don't need to set a "window" since the sprite data
; will be 0 and thus not visible.  Use DFxDATA to read non-windowed data
sec
lda #<(FrostyColors)
sbc FrostyY
sta DF1LOW
lda #>(FrostyColors)
sbc NegativeYadjust
sta DF1HI

 

If you're not able to figure out by next weekend, give me a heads up and I'll see about making a templet that only shows a single sprite without any other logic to selected the image or reposition it with the joystick.

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