Sega Genesis Dev Kit (SGDK)
Moderator: Stef
It is rather easy if you play TFC on M68K side and samples on Z80 side - you don't need a special driver to do this. You only need to modify TFC player a bit, by stopping Z80 before writing to YM2612 registers and selecting DAC register after write and running Z80 again.
By the way, Stef, it seems that waiting for busreq with while(*ptr&0x100) is incorrect, it should be 1 instead of 0x100. Compare compiled C code with code from any old game - games always check bit 0 while compiled C code with 0x100 checks bit 8. In real programs it works on hardware either way, I guess it is because other code create a large enough delay.
By the way, Stef, it seems that waiting for busreq with while(*ptr&0x100) is incorrect, it should be 1 instead of 0x100. Compare compiled C code with code from any old game - games always check bit 0 while compiled C code with 0x100 checks bit 8. In real programs it works on hardware either way, I guess it is because other code create a large enough delay.
-
- Very interested
- Posts: 3131
- Joined: Thu Nov 30, 2006 9:46 pm
- Location: France - Sevres
- Contact:
Err you're right... the correct code should be equivalent to :Shiru wrote:It is rather easy if you play TFC on M68K side and samples on Z80 side - you don't need a special driver to do this. You only need to modify TFC player a bit, by stopping Z80 before writing to YM2612 registers and selecting DAC register after write and running Z80 again.
By the way, Stef, it seems that waiting for busreq with while(*ptr&0x100) is incorrect, it should be 1 instead of 0x100. Compare compiled C code with code from any old game - games always check bit 0 while compiled C code with 0x100 checks bit 8. In real programs it works on hardware either way, I guess it is because other code create a large enough delay.
Code: Select all
while(!Z80_isBusTaken());
Thanks for notifying it
Both Double Dragon 1 and Double Dragon 2 does the same mistake in their code, i remember i was very confused with these games when i developed Gens (i experienced many Z80 issues because of that).
-
- Very interested
- Posts: 3131
- Joined: Thu Nov 30, 2006 9:46 pm
- Location: France - Sevres
- Contact:
Don't expect it *soon*, it's somewhat planned but i cannot give any date...Mixail wrote:How many it will occupy time?Stef wrote:Thats is not possible, no Z80 drivers support that for the moment.
I plan to add better drivers later though
You should follow Shiru advice about how mix TFM play on 68k and sample play on Z80.
Unfortunately SGDK library requires that minimum space for the moment.How to make, that rom (bin file) weighed not so much? At me at empty compilation it turns out 384 kb.
But i think i will re enable optionals tables to minimize that requirement.
The problem is that without these tables some methods (math related stuff mainly) cannot work.
Here is what should be done to make TFC C player work with any Z80 sample player:
Make sure that Z80 code does not attempt to select any register and only writes data.
There are alternatives possible to minimize jitter introduced by this method. For example, you can buffer all the writes from TFC player into an array, then do them all in a single burst, so you only stop and restart Z80 once per frame and minimize the overall time when it is stopped.
Code: Select all
void ym2612wr(u8 reg,u8 val,u8 bank)
{
volatile u16 *pz;
volatile u8 *pw,*pa,*pd;
pz=(u16*)Z80_HALT; //stop Z80 to prevent it from corrupting a register write
*pz=0x100;
while(*pz&1); //wait for busreq
pw=(u8*)YM2612_A0;
if(!bank)
{
pa=(u8*)YM2612_A0;
pd=(u8*)YM2612_D0;
}
else
{
pa=(u8*)YM2612_A1;
pd=(u8*)YM2612_D1;
}
while(*pw&0x80); //select register
*pa=reg;
while(*pw&0x80); //write value
*pd=val;
while(*pw&0x80); //now select DAC register back
*pa=0x2a; //so it is always selected from Z80 point of view
*pz=0; //run Z80 again
}
There are alternatives possible to minimize jitter introduced by this method. For example, you can buffer all the writes from TFC player into an array, then do them all in a single burst, so you only stop and restart Z80 once per frame and minimize the overall time when it is stopped.
What if you add macros that pre-calculate the stuff that normally requires tables but of course this will only work for stuff that has a constant number and will end up with the same result every time any ways and not a variable but for variables if you want you could just do it in software but that may be slightly slower.
-
- Very interested
- Posts: 3131
- Joined: Thu Nov 30, 2006 9:46 pm
- Location: France - Sevres
- Contact:
I realized my last modification broke my code, i mis read your initial post and just inverted my test which was incorrect.Shiru wrote: By the way, Stef, it seems that waiting for busreq with while(*ptr&0x100) is incorrect, it should be 1 instead of 0x100. Compare compiled C code with code from any old game - games always check bit 0 while compiled C code with 0x100 checks bit 8. In real programs it works on hardware either way, I guess it is because other code create a large enough delay.
Then i had a llok back to officials sega genesis tech stuff and it seems you have to test bit 8 and not bit 0 to ensure that BUS has be taken.
I remember that a lot of game were setting BUSREQ with word operation then testing with byte operation, maybe it's why you saw it tested with bit 0.
I have noticed that this function
just called another function so I commented it out and edited the header and ended up with
so that it only calls one function it worked but you have to put
in (if your project has it) global.h or put it in genesis.h either of those will work.
and at first when I was doing the makelib I got a few errors so I included vdp.h and put the extern mentioned above
Code: Select all
void VDP_drawText(const char *str, u16 x, u16 y)
{
// use A plan & high priority by default
VDP_drawTextBG(APLAN, str, textBasetile, x, y);
}
Code: Select all
//void VDP_drawText(const char *str, u16 x, u16 y);
#define VDP_drawText(str, x, y) \
VDP_drawTextBG(APLAN, str, textBasetile, x, y);
Code: Select all
extern u16 textBasetile;
and at first when I was doing the makelib I got a few errors so I included vdp.h and put the extern mentioned above
-
- Very interested
- Posts: 3131
- Joined: Thu Nov 30, 2006 9:46 pm
- Location: France - Sevres
- Contact:
If GCC was smart enough having inner calls this way would not be a problem as inlining would be automatically applied. Unfortunately GCC 3.4.6 is not inlining anything, even if you specify manually the inline keyword...sega16 wrote:I have noticed that this functionjust called another function so I commented it out and edited the header and ended up withCode: Select all
void VDP_drawText(const char *str, u16 x, u16 y) { // use A plan & high priority by default VDP_drawTextBG(APLAN, str, textBasetile, x, y); }
so that it only calls one function it worked but you have to putCode: Select all
//void VDP_drawText(const char *str, u16 x, u16 y); #define VDP_drawText(str, x, y) \ VDP_drawTextBG(APLAN, str, textBasetile, x, y);
in (if your project has it) global.h or put it in genesis.h either of those will work.Code: Select all
extern u16 textBasetile;
and at first when I was doing the makelib I got a few errors so I included vdp.h and put the extern mentioned above
Anyway VDP_drawText is not a critical method, you usually don't call it too much and you can always directly call the other method if speed is a problem.
-
- Interested
- Posts: 24
- Joined: Sun Dec 17, 2006 8:28 pm
- Location: Osaka, Japan
The inline keyword is a hint to the compiler. You should specify the always_inline attribute if you want it to be inlined no matter what.Stef wrote:If GCC was smart enough having inner calls this way would not be a problem as inlining would be automatically applied. Unfortunately GCC 3.4.6 is not inlining anything, even if you specify manually the inline keyword...
Relevant page from the GCC 3.4.6 manual.
As far as I am aware all inlining would do is copy all the code from VDP_drawTextBG and put it into VDP_drawText but what if you want to call VDP_drawTextBG directly you would end up having two copies of the same code. So with my way it just replaces all the references of VDP_drawText with VDP_drawTextBG and uses the constant perimeters that VDP_drawText had.
-
- Very interested
- Posts: 3131
- Joined: Thu Nov 30, 2006 9:46 pm
- Location: France - Sevres
- Contact:
Unfortunately inline is just *not supported* in the m68k-elf GCC 3.4.6 whatever the switch you try to specify, this is really a pity as this is the best GCC version for this target (in term of code generation).fdarkangel wrote:The inline keyword is a hint to the compiler. You should specify the always_inline attribute if you want it to be inlined no matter what.Stef wrote:If GCC was smart enough having inner calls this way would not be a problem as inlining would be automatically applied. Unfortunately GCC 3.4.6 is not inlining anything, even if you specify manually the inline keyword...
Relevant page from the GCC 3.4.6 manual.
To reply to sega16, inlining a function is exactly the same as doing a macro but the function code looks better