Sonic and Sega Retro Message Board: Dual PCM - Sonic and Sega Retro Message Board

Jump to content

Hey there, Guest!  (Log In · Register) Help
  • 2 Pages +
  • 1
  • 2
    Locked
    Locked Forum

Dual PCM

#1 User is offline MarkeyJester 

Posted 27 November 2016 - 05:11 AM

  • The architect of the universe, didn't build a stairway, leading nowhere.
  • Posts: 1827
  • Joined: 22-July 08
  • Gender:Male
  • Location:Japan
  • Wiki edits:16
IF YOU WANT TO GET THE MOST OUT OF THE DRIVER I AM ABOUT TO RELEASE HERE, THEN PLEASE READ THIS ENTIRE POST BEFORE USING/RESPONDING. ANYONE WHO RESPONDS BECAUSE THEY DIDN'T READ, I SHALL NOT RESPOND TO.

Posted Image

What is it?

Dual PCM is a Z80 sound driver for the SEGA Mega Drive, it can play any two PCM samples, of any size, at any single time, through the DAC channel. It comes complete with Jester Stream Technologyâ„¢ allowing it to play the samples at the least damaging quality possible.

Here is a test ROM of Sonic 1, with Dual PCM, Download.

This one has a few edits to the original songs, Green Hill Zone has a set of piano chords during the chorus, as does Labyrinth Zone, Scrap Brain Zone has it's Timpani drums seperated onto the second channel, and Spring Yard Zone and Invincible have a PCM bass guitar now. This is just an example of what you can do with this driver.

Requirements:

Dual PCM simply requires the 68k (Main CPU) to give it the sample information in order for it to play the sample(s), Dual PCM also requires the 68k to tell it when it's about to request VDP DMA transfers, and when it's finished.

If you intend on playing FM/PSG sounds, this MUST be handled by the 68k, so if you have a sound driver that plays back tracker data (for example SMPS), then it must be a 68k variant, Dual PCM operates on its own and requires all of the Z80's resources. It is a puppet, and the 68k is the puppeteer.

Installation

Please use this as a "guide" and nothing more, it doesn't have to be used with 68k SMPS, it can be used with any 68k code that can operate it properly.

This guide below will simply assume you're using Sonic 1 and its 68k SMPS variant, I'll be using the 2005 Hivebrain disassembly (known as "Sonic 1 (Split and Text by Hivebrain) (ASM68K)" on this page), nothing against other disassembly variants, I just know this one best.

You must handle the splitting and normal setup of the disassembly yourself, however, I will place a download link to a disassembly of the changes below made already, for your conveniece.

Step 01 - Replacing the Z80 driver

Sonic 1 has a Z80 DPCM playback driver, we must first rip this out, and then replace it with the new driver.

>> 1. Inside the "sound" folder, you'll have "z80_1.bin" and "z80_2.bin", you won't need these, so delete them.
>> 2. Next, open up "sonic1.asm", and find this:

Spoiler

Replace it with:

Spoiler

>> 3. Find this:

Spoiler


Replace it with:

Spoiler

>> 4. Make a new folder at the root of your disassembly, and name it "Dual PCM":

Posted Image

>> 5. Download this new Z80 sound driver source code, and place it in the "Dual PCM" folder.

Step 02 - Replacing the assembly tools

We need an assembler for the Z80, as well as some tools to help assemble/convert the new driver

>> 1. Delete "build.bat", "ASM68K.EXE", "fixheadr.exe", and "rompad.exe".

>> 2. Download this folder of tools and extract it at the root of your disassembly:

Download (32-Bit)
Download (64-Bit)

Posted Image

>> 3. Download this new batch file and place it at the root of your disassembly:

Posted Image

Step 03 - Editing the 68k Sound Driver

The sound driver is still handling the old Z80 driver, as well as only handling one DAC channel, and a few unoptimal things (this one is quite lengthy...)

>> 1. Find:

Spoiler

And replace with:

Spoiler

>> 2. Find:

Spoiler

And replace with:

Spoiler

>> 3. Goto "loc_71C44:" and replace this:

Spoiler

With this:

Spoiler

As long as you place all includes here, you will be fine.

A thank you to "Ralakimus" for providing 32-bit builds of my programs, and to "Egor The Cat" for his helpful guide, and for reminding me of extra details I forgot to add.

I will be doing some clean up and some quality improvements as time goes on, but I am hoping that some other Z80 programmers out there will contribute to improve the driver, we need as many improvements as we can get if we want Pitch/Volume support. Adding those may cause a major drop in sample rate, and so, we need to be careful how we go about this.

If any of you have any ideas, or wish to contribute in some way, by all means post away, I'll be happy to at least "attempt" to implement or allow the implementation of ideas into the product as it grows (if it grows).

I need to stop typing more stuff now as the site's editor is lagging from the amount of text and code put in this tiny dialogue box, have fun~
This post has been edited by MarkeyJester: 13 December 2016 - 01:46 PM

#2 User is offline amphobius 

Posted 27 November 2016 - 12:39 PM

  • where are the japanese shaman girls, lintahlo?
  • Posts: 2120
  • Joined: 19-February 08
  • Gender:Male
  • Location:Northern Ireland, Belfast
  • Project:life
  • Wiki edits:165
You're a madman and I love you.

#3 User is offline nineko 

Posted 27 November 2016 - 03:55 PM

  • I am the Holy Cat
  • Posts: 5576
  • Joined: 17-August 06
  • Gender:Male
  • Location:italy
  • Project:I... don't even know anymore :U
  • Wiki edits:5,251
Many of you might think that it's a small detail, but one thing I really like is how the pitches of the timpanis are correct. Many custom drivers do terrible things to the timpanis for some reason, and while it's particularly noticeable in the 1-up jingle and in the boss theme, many people don't even seem to notice that.

Aside from all the obvious things which can be praised about this driver (sound quality, dual channel, ease of use, and so on), having proper timpanis is always good.

#4 User is offline MarkeyJester 

Posted 27 November 2016 - 04:21 PM

  • The architect of the universe, didn't build a stairway, leading nowhere.
  • Posts: 1827
  • Joined: 22-July 08
  • Gender:Male
  • Location:Japan
  • Wiki edits:16
Your reputation proceeds you Nineko!

I am so glad you noticed, I spent hours adjusting the sample rate value in the ".wav" files, until the output pitch matched the originals.

#5 User is offline rata 

Posted 30 November 2016 - 08:33 AM

  • Posts: 412
  • Joined: 11-January 15
  • Gender:Male
  • Location:Argentina
  • Project:Trying to be useful somehow.
I may try to add this to my base rom, wish me luck! (GitHub's modern disassembly user here)

This is so awesome it's like black magic or something, thank you very much for doing this!

Now, correct me if I'm wrong, but as far I can notice, since I have to double the volume of the samples to play properly on the lower bit, theese samples have to be forever bound to the second channel, am I right? Not that it is a problem, not at all, it's just to make sure I'm understanding things right since I'm a bit dumb for theese things.

Also, before starting, one serious doubt: do I have to first insert a 68k sound driver and after that putting this? This raises my faliure possibilities to like, quad as much as before :p.

#6 User is offline MarkeyJester 

Posted 30 November 2016 - 12:07 PM

  • The architect of the universe, didn't build a stairway, leading nowhere.
  • Posts: 1827
  • Joined: 22-July 08
  • Gender:Male
  • Location:Japan
  • Wiki edits:16

View Postrata, on 30 November 2016 - 08:33 AM, said:

Now, correct me if I'm wrong, but as far I can notice, since I have to double the volume of the samples to play properly on the lower bit, theese samples have to be forever bound to the second channel, am I right?

No.

When you use the program of your choice to raise the volume of your samples to twice as loud, that program will most definitely cap it if it overflows. ConvPCM.exe will convert it to 7-bit no matter how loud you have it, allowing the samples to be played on any of the two channels at any time, without overflow, so you do not have to bound your samples to a specific channel. In the test ROM shared at the top of the first post, the Sonic 1 samples have their volumes doubled, as well as the piano/guitar samples, ConvPCM forced them to a capped 7-bit, this is why they don't ever overflow.

You do not have to worry about which channel you use the samples on, when you use them, or anything like that. Use both of them as much to your heart's contempt, abuse them, get the most out of them! Just raise the volume of your samples until it sounds loud enough for your liking.

View Postrata, on 30 November 2016 - 08:33 AM, said:

Also, before starting, one serious doubt: do I have to first insert a 68k sound driver and after that putting this? This raises my faliure possibilities to like, quad as much as before :p.

You will need a 68k driver for FM/PSG/tracker playback, like I said, Dual PCM needs the entire Z80's resources to do its job. If you are hacking a game that does not have a 68k FM/PSG/tracker playback driver, then I'd suggest looking into the Clone Driver (originally by Tweaker, and someone else I currently forget who. And lately being improved by Clownacy with heaps of bug-fixes).

The guide above, is nothing more than a guide.

#7 User is offline MarkeyJester 

Posted 30 November 2016 - 06:31 PM

  • The architect of the universe, didn't build a stairway, leading nowhere.
  • Posts: 1827
  • Joined: 22-July 08
  • Gender:Male
  • Location:Japan
  • Wiki edits:16
Update...

Some changes that will improve the quality slightly...

Open up "sonic1.asm" source, find this:

Spoiler

Change it to this:

Spoiler

Then find this:

Spoiler

Change it to this:

Spoiler

Download this new Z80.asm file, and replace the old one.

And finally, open up "ConvPCM.txt" and change "19500" to "20500", and of course, drag and drop your samples again.

------------------------------------------------------------------------------------

Basically, the sample flushing is staggered, but it wasn't 100% smooth/consistent. I analysed the output waveform and found difference in times between each byte flush, I've changed the order slightly, and now it's considerably smoother, this'll remove the "crinkly" sound you here a little bit.

In addition, some emulators (like KEGA) as well as hardware had samples playing at a slightly higher pitch when one of the channels were resting, I'm not too sure though I suspect the Z80 accessing the window is slightly slower. And so, the null/mute sample that the channels will play when resting, will now be on the 68k side and accessed through the window instead. This will cost you $8000 bytes of ROM space. But it'll mean the pitch will also remain consistent now.

I'll update the first post to include this update in a moment.

#8 User is offline rata 

Posted 01 December 2016 - 04:53 PM

  • Posts: 412
  • Joined: 11-January 15
  • Gender:Male
  • Location:Argentina
  • Project:Trying to be useful somehow.
Thank you very much for your answer, I will follow your advice and try to port Clone Driver then.

#9 User is offline MarkeyJester 

Posted 12 December 2016 - 07:01 PM

  • The architect of the universe, didn't build a stairway, leading nowhere.
  • Posts: 1827
  • Joined: 22-July 08
  • Gender:Male
  • Location:Japan
  • Wiki edits:16
I am a little embarrassed to report that...

...well, I've made a fatal error that caused the sound driver to sound aweful on hardware. I STRONGLY suggest fixing it by opening up "Macro.asm", and in "Z80DMA_ON", changing this:

		move.w	#$0000,d7


To this:

		move.w	#$0180,d7


This didn't allow the Z80 enough time, and well, the results were horrible, this will fix the quality greatly... You won't notice this quality on emulation, but you will definately on hardware!

I will have an update coming later on tonight which will ensure the highest quality possible for 20,500Hz Dual samples, it's gonna be AMAZING guys, because you'll be able to hear the difference on emulation as well as hardware~

Edit; here's a recording of it so far from hardware:

Model 1 Recording
Nomad Recording

I couldn't get a Model 2 recording because it lacks a headphone jack, but it sounds pretty damn fine on there (in fact, Model 2's filter seems to do the samples some extra justice).
This post has been edited by MarkeyJester: 12 December 2016 - 08:27 PM

#10 User is offline MarkeyJester 

Posted 13 December 2016 - 01:44 PM

  • The architect of the universe, didn't build a stairway, leading nowhere.
  • Posts: 1827
  • Joined: 22-July 08
  • Gender:Male
  • Location:Japan
  • Wiki edits:16
UPDATE!!

We now have an update, this driver now avoids Z80 stops caused by FM operator data being written, I shall update the first post with the most recent version, but here is a list of steps you those that already have the driver installed and in use.

>> 1. Download this new "Z80.asm" source, and replace the old one in the "Dual PCM" folder.

>> 2. Download this new "Macros.asm" source, and replace the old one.

>> 3. Open up "sonic1.asm" and find "sub_71B4C:":

sub_71B4C:				; XREF: loc_B10; PalToCRAM
		lea	($FFF000).l,a6


Replace this, with:

YM_Access_WaitRead:
		StartZ80					; EXT: request Z80 stop off (allow it to continue)
		rept	$10					; EXT: delay for a long enough time to allow the...
		nop						; EXT: '' ...68k pointer to be saved correctly.
		endr						; EXT: ''

sub_71B4C:   
		lea	($FFF000).l,a6

		lea	($A00000+YM_Access).l,a0		; EXT: load access address in Z80
		move.l	#$A00000,d0				; EXT: prepare Z80 RAM address in d0 (i.e. start of Cue list address)
		StopZ80						; EXT: request Z80 stop on
		tst.b	(a0)+					; EXT: is the Z80 accessing the 68k pointer?
		bne.s	YM_Access_WaitRead			; EXT: if so, branch and wait for it to finish...
		move.b	(a0)+,d1				; EXT: load lower byte of pointer
		move.b	(a0)+,d0				; EXT: load upper byte of pointer
		StartZ80					; EXT: request Z80 stop off
		lsl.w	#$08,d0					; EXT: shift upper byte up
		move.b	d1,d0					; EXT: put lower byte with it
		move.l	d0,$10(a6)				; EXT: store the cue address


>> 4. Find "loc_71C44:":

loc_71C44:
		rts	


Replace it with this:

loc_71C44:
		bra.s	YM_Access_TestWrite			; EXT: jump into the access loop

YM_Access_WaitWrite:
		StartZ80					; EXT: request Z80 stop off (allow it to continue)
		rept	$10					; EXT: delay for a long enough time to let the Z80...
		nop						; EXT: '' ...finish writing the 68k pointer, so it doesn't...
		endr						; EXT: '' ...clash with 68k's pointer writing.

YM_Access_TestWrite:
		lea	($A00000+YM_Access).l,a0		; EXT: load access address in Z80
		lea	$13(a6),a1				; EXT: load the 68k's pointer finish location
		StopZ80						; EXT: request Z80 stop on
		tst.b	(a0)+					; EXT: is the Z80 accessing the 68k pointer?
		bne.s	YM_Access_WaitWrite			; EXT: if so, branch and wait for it to finish...
		move.b	(a1),(a0)+				; EXT: save lower byte of pointer
		move.b	-(a1),(a0)				; EXT: save upper byte of pointer
		StartZ80					; EXT: request Z80 stop off

		rts	


>> 5. Find routine "loc_72068:", and delete these:

		moveq	#$FFFFFFFF,d2				; MJ: clear PCM finish flag


		addq.b	#$01,d2					; MJ: set PCM finish flag
		beq.s	SD_NoFM					; MJ: if it hasn't finished yet (not for two turns), branch
		moveq	#$02,d5					; MJ: set longer delay for FM/PSG channels (DAC has delay now...)

SD_NoFM:


>> 6. Find routine "loc_72114:", and insert this instruction directly after the label:

		moveq	#$02,d5					; EXT: set PSG to delay for 1 extra frame (This is to match the PSG with the FM/DAC which is delayed a frame by the Z80)



>> 7. Find routine "loc_72228:":

loc_72228:
		moveq	#0,d3
		move.b	1(a1),d3
		move.b	d3,d4
		bmi.s	loc_72244
		subq.w	#2,d3
		lsl.w	#2,d3


And replace it with:

loc_72228:
		moveq	#0,d3
		move.b	1(a1),d3
		moveq	#$02,d2					; EXT: set PSG to delay for 1 extra frame (This is to match the PSG with the FM/DAC which is delayed a frame by the Z80)
		move.b	d3,d4
		bmi.s	loc_72244
		move.b	#$01,d2					; EXT: set DAC/FM to delay for 0 frames like normal (these have an auto delay of 1 frame in the Z80)
		subq.w	#2,d3
		lsl.w	#2,d3


>> 8. Find "loc_72276:", further down you will find this:

		move.b	#1,$E(a5)


Replace it with this:

		move.b	d2,$E(a5)				; EXT: moving d2 contents (1 for FM/4 for PSG)


>> 9. Find "Sound_E4:":

Sound_E4:				; XREF: Sound_ChkValue; Sound_ExIndex; sub_72504
		moveq	#$2B,d0
		move.b	#$80,d1
		jsr	sub_7272E(pc)
		moveq	#$27,d0
		moveq	#0,d1
		jsr	sub_7272E(pc)
		movea.l	a6,a0
		move.w	#$EF,d0				; MJ: new size of data to clear

loc_725B6:
		clr.l	(a0)+
		dbf	d0,loc_725B6

		move.b	#$80,9(a6)	; set music to $80 (silence)
		jsr	sub_7256A(pc)
		bra.w	sub_729B6

; ||||||||||||||| S U B	R O U T	I N E |||||||||||||||||||||||||||||||||||||||


sub_725CA:				; XREF: Sound_ChkValue
		movea.l	a6,a0
		move.b	0(a6),d1
		move.b	$27(a6),d2
		move.b	$2A(a6),d3
		move.b	$26(a6),d4
		move.w	$A(a6),d5
		move.w	#$93,d0				; MJ: new size

loc_725E4:
		clr.l	(a0)+
		dbf	d0,loc_725E4

		move.b	d1,0(a6)
		move.b	d2,$27(a6)
		move.b	d3,$2A(a6)
		move.b	d4,$26(a6)
		move.w	d5,$A(a6)
		move.b	#$80,9(a6)
		jsr	sub_7256A(pc)
		bra.w	sub_729B6
; End of function sub_725CA


Replace it with this:

Sound_E4:
		StopZ80
		lea	(StopSample).l,a0				; EXT: load stop sample address
		lea	($A00000+PCM1_Sample).l,a1			; EXT: load PCM 1 slot address
		move.b	(a0)+,(a1)+					; EXT: set address of sample
		move.b	(a0)+,(a1)+					; EXT: ''
		move.b	(a0)+,(a1)+					; EXT: ''
		move.b	#(CUPCM1_NewSample&$FF),($A00000+CU_Stack).l	; EXT: set routine to run
		move.b	#(CUPCM1_NewSample>>$08),($A00000+CU_Stack+1).l	; EXT: ''
		move.b	#%11001001,($A00000+CUPCM1_RET).l		; EXT: change "NOP" to "RET"
		lea	(StopSample).l,a0				; EXT: load stop sample address
		lea	($A00000+PCM2_Sample).l,a1			; EXT: load PCM 2 slot address
		move.b	(a0)+,(a1)+					; EXT: ''
		move.b	(a0)+,(a1)+					; EXT: ''
		move.b	(a0)+,(a1)+					; EXT: ''
		move.b	#%00101000,($A00000+CUPCM2_RET).l		; EXT: change "JR NZ" to "JR Z"
		StartZ80

		moveq	#$2B,d0
		move.b	#$80,d1
		jsr	sub_7272E(pc)
		moveq	#$27,d0
		moveq	#0,d1
		jsr	sub_7272E(pc)
		movea.l	a6,a0
		move.l	$10(a6),d6					; EXT: store YM Cue list pointer
		move.w	#$EF,d0						; MJ: new size of data to clear

loc_725B6:
		clr.l	(a0)+
		dbf	d0,loc_725B6

		move.l	d6,$10(a6)					; EXT: restore YM Cue list pointer
		move.b	#$80,9(a6)	; set music to $80 (silence)
		jsr	sub_7256A(pc)
		bra.w	sub_729B6

; ||||||||||||||| S U B	R O U T	I N E |||||||||||||||||||||||||||||||||||||||


sub_725CA:				; XREF: Sound_ChkValue
		movea.l	a6,a0
		move.b	0(a6),d1
		move.b	$27(a6),d2
		move.b	$2A(a6),d3
		move.b	$26(a6),d4
		move.w	$A(a6),d5
		move.l	$10(a6),d6					; EXT: store YM Cue list pointer
		move.w	#$93,d0						; MJ: new size

loc_725E4:
		clr.l	(a0)+
		dbf	d0,loc_725E4

		move.b	d1,0(a6)
		move.b	d2,$27(a6)
		move.b	d3,$2A(a6)
		move.b	d4,$26(a6)
		move.w	d5,$A(a6)
		move.l	d6,$10(a6)					; EXT: restore YM Cue list pointer
		move.b	#$80,9(a6)
		jsr	sub_7256A(pc)
		bra.w	sub_729B6
; End of function sub_725CA


>> 10. Find this:

sub_7272E:
		pea	(a0)					; MJ: store register
		lea	($A04000).l,a0				; MJ: load YM2612 address port
		StopZ80						; MJ: request Z80 stop "ON"
		move.b	d0,(a0)					; MJ: write address
	rept	$0F
		nop						; MJ: delay (Tested on MD1 and 2. F nops are required)
	endr
		move.b	d1,$01(a0)				; MJ: write data
	rept	$0F
		nop						; MJ: delay (Tested on MD1 and 2. F nops are required)
	endr
		move.b	#$2A,(a0)				; MJ: write address (set it back to DAC port for the Z80)
		StartZ80					; MJ: request Z80 stop "OFF"
		move.l	(sp)+,a0				; MJ: restore register
		rts						; MJ: return

; End of function sub_7272E

; ===========================================================================

loc_7275A:				; XREF: sub_72722
		move.b	1(a5),d2
		bclr	#2,d2
		add.b	d2,d0

; ||||||||||||||| S U B	R O U T	I N E |||||||||||||||||||||||||||||||||||||||


sub_72764:
		pea	(a0)					; MJ: store register
		lea	($A04000).l,a0				; MJ: load YM2612 address port
		StopZ80						; MJ: request Z80 stop "ON"
		move.b	d0,$02(a0)				; MJ: write address
	rept	$0F
		nop						; MJ: delay (Tested on MD1 and 2. F nops are required)
	endr
		move.b	d1,$03(a0)				; MJ: write data
	rept	$0F
		nop						; MJ: delay (Tested on MD1 and 2. F nops are required)
	endr
		move.b	#$2A,(a0)				; MJ: write address (set it back to DAC port for the Z80)
		StartZ80					; MJ: request Z80 stop "OFF"
		move.l	(sp)+,a0				; MJ: restore register
		rts						; MJ: return


Replace it with:

sub_7272E:
		movem.l	d2/a0,-(sp)				; EXT: store register data
		movea.l	$10(a6),a0				; EXT: load Cue pointer
		addq.w	#$01,a0					; EXT: skip $40
		move.b	#$00,d2					; EXT: prepare d2 for YM2612 port address ($4000 - $4001)
		StopZ80						; EXT: request Z80 stop "ON"
		move.b	d2,(a0)+				; EXT: write YM2612 port address
		move.b	d1,(a0)+				; EXT: write YM2612 data
		move.b	d0,(a0)+				; EXT: write YM2612 address
		StartZ80					; EXT: request Z80 stop "OFF"
		move.w	a0,d2					; EXT: load Cue pointer
		andi.w	#$0FFF,d2				; EXT: wrap it
		ori.w	#$1000,d2				; EXT: ''
		move.w	d2,$12(a6)				; EXT: update it
		movem.l	(sp)+,d2/a0				; EXT: restore register data
		rts						; EXT: return

; ===========================================================================

loc_7275A:				; XREF: sub_72722
		move.b	1(a5),d2
		bclr	#2,d2
		add.b	d2,d0

; ||||||||||||||| S U B	R O U T	I N E |||||||||||||||||||||||||||||||||||||||


sub_72764:
		movem.l	d2/a0,-(sp)				; EXT: store register data
		movea.l	$10(a6),a0				; EXT: load Cue pointer
		addq.w	#$01,a0					; EXT: skip $40
		move.b	#$02,d2					; EXT: prepare d2 for YM2612 port address ($4002 - $4003)
		StopZ80						; EXT: request Z80 stop "ON"
		move.b	d2,(a0)+				; EXT: write YM2612 port address
		move.b	d1,(a0)+				; EXT: write YM2612 data
		move.b	d0,(a0)+				; EXT: write YM2612 address
		StartZ80					; EXT: request Z80 stop "OFF"
		move.w	a0,d2					; EXT: load Cue pointer
		andi.w	#$0FFF,d2				; EXT: wrap it
		ori.w	#$1000,d2				; EXT: ''
		move.w	d2,$12(a6)				; EXT: update it
		movem.l	(sp)+,d2/a0				; EXT: restore register data
		rts						; EXT: return


>> 11. Find this:

SoundDriverLoad:
		lea	(Z80ROM).l,a0				; load Z80 ROM data
		lea	($A00000).l,a1				; load Z80 RAM space address
		move.w	#(Z80ROM_End-Z80ROM)-$01,d1		; set repeat times
		move.w	#$2000,d2				; prepare total Z80 size
		sub.w	d1,d2					; subtract repeat times from total size
		subq.w	#$02,d2					; decrease by 2 (for dbf)
		move.w	#$0100,($A11100).l			; request Z80 stop (ON)
		move.w	#$0100,($A11200).l			; request Z80 reset (OFF)
		btst.b	#$00,($A11100).l			; has the Z80 stopped yet?
		bne.s	*-$08					; if not, branch

SM_LoadZ80:
		move.b	(a0)+,(a1)+				; dump Z80 data to Z80 space
		dbf	d1,SM_LoadZ80				; repeat til done

SM_ClearZ80:
		move.b	d0,(a1)+				; clear remaining Z80 space
		dbf	d2,SM_ClearZ80				; repeat til done
		move.w	#$0000,($A11200).l			; request Z80 reset (ON)
		moveq	#$7F,d1					; set repeat times
		dbf	d1,*					; there's no way of checking for reset, so a manual delay is necessary
		move.w	#$0000,($A11100).l			; request Z80 stop (OFF)
		move.w	#$0100,($A11200).l			; request Z80 reset (OFF)
		rts						; return


Replace it with:

SoundDriverLoad:
		lea	(Z80ROM).l,a0				; load Z80 ROM data
		lea	($A00000).l,a1				; load Z80 RAM space address
		move.w	#(Z80ROM_End-Z80ROM)-$01,d1		; set repeat times
		move.w	#$0100,($A11100).l			; request Z80 stop (ON)
		move.w	#$0100,($A11200).l			; request Z80 reset (OFF)
		btst.b	#$00,($A11100).l			; has the Z80 stopped yet?
		bne.s	*-$08					; if not, branch

SM_LoadZ80:
		move.b	(a0)+,(a1)+				; dump Z80 data to Z80 space
		dbf	d1,SM_LoadZ80				; repeat til done
		lea	(StopSample).l,a0			; load stop/mute sample address
		lea	($A00000+MuteSample).l,a1		; load Z80 RAM space where the pointer is to be stored
		move.b	(a0)+,(a1)+				; copy pointer over into Z80
		move.b	(a0)+,(a1)+				; ''
		move.b	(a0)+,(a1)+				; ''
		move.w	#$0000,($A11200).l			; request Z80 reset (ON)
		moveq	#$7F,d1					; set repeat times
		dbf	d1,*					; there's no way of checking for reset, so a manual delay is necessary
		move.w	#$0000,($A11100).l			; request Z80 stop (OFF)
		move.w	#$0100,($A11200).l			; request Z80 reset (OFF)
		rts						; return


>> 12. Find this:

Z80ROM:		incbin	"Dual PCM\Z80.bin"
		dcz80	SWF_StopSample
Z80ROM_End:	even


Replace it with this:

Z80ROM:		incbin	"Dual PCM\Z80.bin"
Z80ROM_End:	even


Finished... I'll be updating the first post now.
This post has been edited by MarkeyJester: 13 December 2016 - 03:03 PM

#11 User is offline Caverns 4 

Posted 13 December 2016 - 02:06 PM

  • Posts: 324
  • Joined: 07-December 12
  • Gender:Male
  • Project:Sanik Quest: Journey To The Right
I really, honestly doubted something like would be possible just because of how limited the Z80's power is, but I'm happy to eat my words.
This is outstanding, MarkeyJester!

#12 User is offline rata 

Posted 13 December 2016 - 04:10 PM

  • Posts: 412
  • Joined: 11-January 15
  • Gender:Male
  • Location:Argentina
  • Project:Trying to be useful somehow.
Thank god I didn't start with that yet :p.

Also, I thought that Model 2 was worse in terms of sound? What did I miss now?

#13 User is offline MarkeyJester 

Posted 06 March 2017 - 07:21 AM

  • The architect of the universe, didn't build a stairway, leading nowhere.
  • Posts: 1827
  • Joined: 22-July 08
  • Gender:Male
  • Location:Japan
  • Wiki edits:16
Alright ladies and gentlemen, I have some good news for you all!

I rewrote the Z80 side of Dual PCM in order to support pitch control, this form of pitch control isn't done by delay however since a delay would cause both samples to pitch at the same time. Instead I'm using a mathematical fixed point 16 bit quotient 8 bit dividend to control the pitch of each PCM channel individually, this also means that a specific pitch can be played, any specific pitch that PCM playback can muster. This brought the sample playback down to about 13.000Hz unfortunately...

However, after rearranging the time in which the samples are fused (doing on playback rather than collection), I was able to increase that sample rate to 14.400Hz. And with suggestions, support and encouragement from Natsumi, as well as a lot of shared ideas, we've managed to get a stable enough build to play back any two samples, any size, at any pitch, including looping sample support, as well as reversal playback. All at a sample rate of 14.400Hz, but with little to no chopping on hardware for that clean and crisp quality.



(I posted this in the music thread.)

However, I went a step further and wondered how well it'd perform by changing the number of bytes loaded into the buffer vs playback. Dual PCM has always been; "Load 20, Play 10". So after ripping the driver apart, and applying some careful buffer wrapping methods for non-POT (not power of two) sizes, I tried with; "Load 18, Play 10", and this raised the sample rate to 16.500Hz. I then went a step further and tried "Load 14, Play 10", and the sample rate is a nice healthy 18.000Hz playback!

I've muted FM and PSG playback so you can hear just Dual PCM running (FM and PSG are still running in the background, they in quite mode):



I haven't tested this on hardware yet, and I suspect there may be a considerable delay, there's also a lot of testing and some clean-up to do, so stay tuned~

This is so exciting! =D

#14 User is offline MarkeyJester 

Posted 28 March 2017 - 11:16 PM

  • The architect of the universe, didn't build a stairway, leading nowhere.
  • Posts: 1827
  • Joined: 22-July 08
  • Gender:Male
  • Location:Japan
  • Wiki edits:16
A small bump (nothing major yet).

After some heavy drumbling, a lot of nudging and fudging, and some very precise instruction usage, and abusing undocumented quirks, I have managed to do the following so far:
  • The sample rate playback is currently 20,500Hz and counting (might get it a little higher if you're lucky).
  • The samples are now stored on the ROM in their native unsigned 8-bit format. The output is still the same, as in, you'll still have a 7-bit sample being played, but 8-bit unsigned is stored on the ROM. So you could in reality just include an unsigned 8-bit sample, without the use of the tool I've written. You just have to make sure that the byte "00" isn't used anywhere in the sample (I'm keeping the tool in of course, just for ease of use for noobs, but still...)
  • I "might" include some volume control at the expense of some of that sample rate (let me know your opinion on that).
  • The SMPS structure to DAC channels has changed significantly. Now you select a sample using the F5 flag (F5 XX, where XX is the instrument/sample ID), and you play the note you want from 81 to DF (for pitch/notes).
  • The FA flag will turn on/off the "reverse playback" mode, use once to turn on, use again to turn off.
  • The FB flag will turn on/off the "looping" mode, when the sample reaches the end it'll loop back and play again (will be useful for instruments/sounds that play a sound/note forever with no decay).
  • The E1 and F0 (detune and modulation) flags affect the frequency/pitch of the samples, so you can make it vibrato, or bend or whatever... as well as the E9 flag for pitch changing.
  • Reduced the wait time on the 68k side by a MASSIVE amount of time.

All of the above is working with the (still) ability to play any sample, anywhere, any size, anytime, and at near perfect quality on hardware (due to Jester Stream Technology, and the YMCUE Listing System).

I have a lot of clean up to do, but so long as you're all patient the coming weeks, you'll be thankful of the features and control available to you on release~ Also, I'm open to feature ideas related to the driver itself, if I can support those ideas, I will. But you have to speak up...

#15 User is offline redquebec 

Posted 29 March 2017 - 08:52 AM

  • Posts: 38
  • Joined: 29-April 14
Thanks a lot for the hard-work you provide to us all. Some features? Well, I don't know how much it is possible, but the smooth PCM panning? LINK: http://www.sega-16.c...oth-PCM-panning!
This post has been edited by redquebec: 29 March 2017 - 08:58 AM

  • 2 Pages +
  • 1
  • 2
    Locked
    Locked Forum

1 User(s) are reading this topic
0 members, 1 guests, 0 anonymous users