FPGA Genesis with Sound

Ask anything your want about Megadrive/Genesis programming.

Moderator: BigEvilCorporation

OzOnE
Interested
Posts: 23
Joined: Wed Dec 04, 2013 3:49 pm

Post by OzOnE » Sun Dec 08, 2013 11:32 pm

Ok, thanks guys.

I've been going through the code today and trying to figure things out.

The tech manual says that Name Table entries (addr part) are normally 10-bits wide, but 11-bits wide in Interlaced mode...

http://emu-docs.org/Genesis/sega2f.htm

I've also put comments in the code for the registers now, which makes things much clearer...

Code: Select all

----------------------------------------------------------------
-- REGISTERS
----------------------------------------------------------------
ADDR_STEP <= REG(15);	-- Address Auto-increment (8 bits. This number gets added to the RAM access addr after each access).

H40 <= REG(12)(0);		-- Horizontal 40-Cell mode (0 = 32-Cell mode, 1 = 40-Cell mode).

								-- V30 bit is set in PAL mode.
V30 <= REG(1)(3);			-- Bug fix! - Changed to match tech docs / Shaho source (OzOnE).
								-- (Was set to Reg 2.)

HSCR <= REG(11)(1 downto 0);	-- H Scroll Mode (0 = Full, 1 = Prohibited, 2 = Each 1 Cell scroll, 3 = Each 1 Line scroll).
VSCR <= REG(11)(2);				-- V Scroll Mode (0 = Full scroll, 1 = 2-Cell scroll).

-- Scroll sizes used for both Scroll A and Scroll B (I think?) OzOnE.
HSIZE <= REG(16)(1 downto 0);	-- H Scroll Size (0 = H 32 Cell, 1 = H 64 Cell, 2 = Prohibited, 3 = H 128 Cell).
VSIZE <= REG(16)(5 downto 4);	-- V Scroll Size (0 = V 32 Cell, 1 = V 64 Cell, 2 = Prohibited, 3 = V 128 Cell).

WVP <= REG(18)(4 downto 0);	-- Window V Position.
WDOWN <= REG(18)(7);				-- Window DOWN / Up (0 = Window is Up from base pos, 1 = Window is DOWN from base pos).

WHP <= REG(17)(4 downto 0);	-- Window H Position.
WRIGT <= REG(17)(7);				-- Window RIGHT / Left (0 = Window is Left from base pos, 1 = Window is RIGHT from base pos).

BGCOL <= REG(7)(5 downto 0);	-- Background Colour.

HIT <= REG(10);	-- Horizontal Interrupt - Sets raster on which to trigger an H Int (if H int bit enabled, below).
IE1 <= REG(0)(4);	-- Enable H Interrupt (68000 Level 4).

IE0 <= REG(1)(5);	-- Enable V Interrupt (68000 Level 6).

DMA <= REG(1)(4); -- Enable DMA ("M1" bit).

IM <= REG(12)(1);	 -- Interlaced mode bits.
IM2 <= REG(12)(2); -- (0 = Non-interlace, 1 = Interlace, 2 = Prohibited, 3 = Interlace-Double-Resolution).

-- Base addresses
HSCB <= REG(13)(5 downto 0);	-- HSCROLL Data Table base address.
NTBB <= REG(4)(2 downto 0);	-- Name Table Base address for Scroll B.
NTWB <= REG(3)(5 downto 1);	-- Name Table Base address for Window.
NTAB <= REG(2)(5 downto 3);	-- Name Table Base address for Scroll A.
SATB <= REG(5)(6 downto 0);	-- Sprite Attrib Table Base address.

-- Read-only registers
ODD <= FIELD when IM = '1' else '0';
IN_DMA <= DMA_FILL or DMA_COPY or DMA_VBUS;

STATUS <= "111111" & FIFO_EMPTY & FIFO_FULL & VINT_TG68_PENDING & SOVR & SCOL & ODD & IN_VBL & IN_HBL & IN_DMA & V30;

HV <= HV_VCNT(8 downto 1) & HV_HCNT(8 downto 1) when IM = '0' else				-- IM bit is 0 in Non-interlace modes.
		HV_VCNT(8 downto 2) & HV_VCNT(9) & HV_HCNT(8 downto 1) when IM = '1';	-- Note: IM bit should be set in both Intelace and Interlace-double-res modes!

@Jorge, does that mean I just halve the name table pointer in Interlace mode (is it only in double-res mode, or whenever Interlace is enabled? ie. IM bit set?).

So, could I just shift the NT pointer bits right by one bit?

I can't quite visualize how the interlaced lines get rendered, or how the NT pointer change addresses the 8x16 cells instead of 8x8?

It's slowly starting to make sense now that I've found the hardware manual though.

@Eke - many thanks for that. I will try to track down the problem now.

I will try getting Reads to work from the real YM chip, but would be nice to fix the timers on the FPGA as well.

It's probably easier if I post the code and you can have a quick look.
It's not too bad now with the registers commented.

Grégory said it's fine for me to post the updated code, but please be aware that this is NOT and official release in any way.

I'm still having weird compilation issues as well, but the code should be OK...

https://mega.co.nz/#!f4w1kToC!XGxcIiWbR ... rOH4c70RcI

This is the version with Shaho's PSG code, but with one YM channel enabled (chan 2). The other channels have been commented out in the gen_fm.vhd file, otherwise the design wouldn't fit on the DE1.

For some strange reason, even if I assign the PSG / FM busses to the Hex displays on the DE1, all music / effects are silent?

It's like Quartus is routing something wrong, and doesn't want to assign to two different things at once? Crazy!

If anyone has had similar issues with VHDL or Quartus before, please let me know.

I mean, I literally just added this...

HEXVALUE(15 downto 8) <= FM_DI(7 downto 0);
HEXVALUE(7 downto 0) <= PSG_DI(7 downto 0);

And it stopped outputting data to the PSG and FM block / YM chip!?

Anyway, it's mostly working atm.

I'm still using the real YM chip, but would be great to get interlace mode working, and fix the Timer issue.

I've added a proper joypad port too, but haven't wired it up yet.

Oh btw, I did add Shaho's gen_io.vhd file to that source as well, so it now routes the inputs for the second joypad port from the top level file.

OzOnE.

OzOnE
Interested
Posts: 23
Joined: Wed Dec 04, 2013 3:49 pm

Post by OzOnE » Sun Dec 08, 2013 11:36 pm

btw, the part at the bottom of that code was my attempt at starting to add the interlaced stuff....

Code: Select all

HV <= HV_VCNT(8 downto 1) & HV_HCNT(8 downto 1) when IM = '0' else            -- IM bit is 0 in Non-interlace modes. 
      HV_VCNT(8 downto 2) & HV_VCNT(9) & HV_HCNT(8 downto 1) when IM = '1';   -- Note: IM bit should be set in both Intelace and Interlace-double-res modes!
I have no idea if it is correct or not to do the stuff after the "else" statement?

It looks like Grégory was using bits 8:1 instead of 7:0 btw?
Might be that his counter is actually running twice as fast as normal, so he can do the scandoubler stuff??

He said via Facebook earlier today that he is way too busy to help with the code, but he is happen for us to modify it and hunt for bugs etc. ;)

OzOnE.

Jorge Nuno
Very interested
Posts: 374
Joined: Mon Jun 11, 2007 3:09 am
Location: Azeitão, PT

Post by Jorge Nuno » Mon Dec 09, 2013 12:08 pm

I could be that you're assigning multiple values to the same bus even though there's no bit collision.. You can try this

HEXVALUE <= FM_DI & PSG_DI;

The & is a concatenation operator, it will put the FM byte in the upper half because your bus is 15 downto 0


You take the nametable index and process it like so (Simplistic code, without scrolling or anything):

if Interlace = '0' then
VRAMTileAddress <= Nametableword(10 downto 0) & "00000"; -- (Nametableword and 0x7FF) * 32;
else
VRAMTileAddress <= Nametableword(10 downto 1) & "000000"; -- (Nametableword and 0x7FF) >> 1 * 64;
end if;

OzOnE
Interested
Posts: 23
Joined: Wed Dec 04, 2013 3:49 pm

Post by OzOnE » Mon Dec 09, 2013 9:07 pm

Thanks, Jorge,

I'm not sure why something like a direct assignment would normally work fine in Verilog?

Maybe it's something specific to VHDL?
I was looking all over the Web last night to see if I was doing the assignment right, and it seemed to be fine?

In verilog, the same sort of assignment might be...

assign HEXVALUE[15:8] = FM_DI[7:0];
assign HEXVALUE[7:0] = PSG_DI[7:0];

Or, concat version...

assign HEXVALUE = {FM_DI[7:0], PSG_DI[7:0]};

The FM_DI and PSG_DI busses are actually 16-bits wide, and it looks like that's just to allow the 68K to access the chips directly as well.

That could be one explanation as to why the timers aren't being set correctly for Mickey Mouse? Maybe the 68K is initializing them at startup, but I'm not accounting for it?

Although, the timers are actually being Written / Read from the internal YM block, and I think that takes the 68K accesses into account?

Anyway, I'm convinced now that either the FPGA on my board is faulty in some way, or something seriously screwy is going on with the newer versions of Quartus on Win 7?

Last night, I did a compare of the resulting SOF files from when I compiled with and without the HEXVALUE assignment, and the files were completely different, as if Quartus was doing a major remapping of everything?

Normally this wouldn't be an issue, but maybe part of my FPGA got zapped somehow, and it's doing strange things?

If I recompile without the HEXVALUE stuff, it generates the exact file as before (as expected), and runs fine again? (plays the music / effects etc.)

So frustrating. I have no idea how I can test my board, so if anyone has a DE1 board they can compile / test the code on, PLEASE let me know. 8)

The version I posted before uses the internal YM channel only though, not the real external YM chip.

I'm not sure what else I can do to test this board, and I can't progress further until I can confirm that it's a hardware / software issue or not.

It's putting me off the idea of even buying a new board, it's really annoying. :evil:

I even tried compiling under Quartus 12 on my new PC, which had a fresh install of Win 7 x64 on it (Core i5, 120GB SSD).

The PC I use most of the time is running Win 7 x86.
This is running Quartus Web Edition 13 (not cracked or anything)

Maybe it's a Win 7 specific issue?
The issues appear the same on the new PC, even on a different version of Quartus (12).


Thanks for the Nametable code, Jorge, I'll give that a try now. :D
If I can just see an improvement in the tile mapping, then I know I'm making headway.

(if my FPGA doesn't explode. lol)

OzOnE.

OzOnE
Interested
Posts: 23
Joined: Wed Dec 04, 2013 3:49 pm

Post by OzOnE » Mon Dec 09, 2013 9:30 pm

Here's an example of where the VDP code grabs the tile base address (not including the testbench / debug stuff)...

Code: Select all

	when BGAC_BASE_RD =>
		if BGA_DTACK_N = '0' then
			BGA_SEL <= '0';
			T_BGA_PRI <= BGA_VRAM_DO(15);
			T_BGA_PAL <= BGA_VRAM_DO(14 downto 13);
			BGA_HF <= BGA_VRAM_DO(11);
			if BGA_VRAM_DO(12) = '1' then	-- VF
				BGA_TILEBASE <= BGA_VRAM_DO(10 downto 0) & not(BGA_Y(2 downto 0)) & "00";
			else
				BGA_TILEBASE <= BGA_VRAM_DO(10 downto 0) & BGA_Y(2 downto 0) & "00";
			end if;
			BGAC <= BGAC_LOOP;
		end if;

I don't think I can make the assignment for interlace mode global (ie. modify the BGA_VRAM_DO bits with a new assignment), as it would affect things like the colour bits.

What would be an appropriate way to modify the above code for interlaced? I'm guessing it only matters for the tilebase address when in the double-res interlace mode?

It doesn't look like it does an address multiplication at this stage, so I'll just try this...

Code: Select all

	when BGAC_BASE_RD =>
		if BGA_DTACK_N = '0' then
			BGA_SEL <= '0';
			T_BGA_PRI <= BGA_VRAM_DO(15);
			T_BGA_PAL <= BGA_VRAM_DO(14 downto 13);
			BGA_HF <= BGA_VRAM_DO(11);
			if BGA_VRAM_DO(12) = '1' then	-- VF
				if IM2 = '0' then BGA_TILEBASE <= BGA_VRAM_DO(10 downto 0) & not(BGA_Y(2 downto 0)) & "00";
				else BGA_TILEBASE <= BGA_VRAM_DO(10 downto 1) & '0' & not(BGA_Y(2 downto 0)) & "00";
				end if;
			else
				if IM2 = '0' then BGA_TILEBASE <= BGA_VRAM_DO(10 downto 0) & BGA_Y(2 downto 0) & "00";
				else BGA_TILEBASE <= BGA_VRAM_DO(10 downto 1) & '0' & BGA_Y(2 downto 0) & "00";
				end if;
			end if;
			BGAC <= BGAC_LOOP;
		end if;
I'll need to do the same for the other layers / sprites of course.
Let's see what the above change does though....

OzOnE.

Jorge Nuno
Very interested
Posts: 374
Joined: Mon Jun 11, 2007 3:09 am
Location: Azeitão, PT

Post by Jorge Nuno » Mon Dec 09, 2013 10:49 pm

Oh, it uses the vertical flip flag to modify the address generator.
Inside the if VF= '1' then.. you have to add another if statement for the interlace flag

The "multiplication stage" is nothing more than the concatenation of 0s to the right side :P


I saw your last code snippet... try it, I guess.. [What could possibly happen? =P]
Still, there's other stuff to account for: Tile drawing and scrolling

OzOnE
Interested
Posts: 23
Joined: Wed Dec 04, 2013 3:49 pm

Post by OzOnE » Mon Dec 09, 2013 11:56 pm

I tried those code changes on the background A and background B stages, but it didn't appear to make any difference?

But, the software is still acting up, because with those relatively minor changes, it had some glitches on the normal graphics (nothing major), but also the audio wasn't working again?

I'm just trying to squeeze in SignalTap into the design to see what is going on.

A friend on Facebook has just bought a DE1 as well, and he's just trying the software for the first time (he had a Xilinx GO-DIL module before now).

I'll see if he can test some of my .sof files on his board to rule out the hardware. :)

Yep, I see the multiplication stuff, Jorge, but does my code look OK?
ie. I'm just forcing the NameTable addr LSB to 0 when IM2 = 1, is that correct?

Is the process like this?...

non-interlaced...
VRAMTileAddress <= NametableAddr(10 downto 0);

interlaced...
VRAMTileAddress <= NametableAddr(10 downto 1) & '0';

Or, am I missing an extra zero for doing the multiplication in interlace mode?

ie....
VRAMTileAddress <= NametableAddr(10 downto 1) & "00";

I'll try that now.

OzOnE.

Jorge Nuno
Very interested
Posts: 374
Joined: Mon Jun 11, 2007 3:09 am
Location: Azeitão, PT

Post by Jorge Nuno » Tue Dec 10, 2013 12:20 am

That looks correct

I have a XC3S1200E, not sure if this project fits inside... probably not.
How many LEs?
Last edited by Jorge Nuno on Tue Dec 10, 2013 12:21 am, edited 1 time in total.

OzOnE
Interested
Posts: 23
Joined: Wed Dec 04, 2013 3:49 pm

Post by OzOnE » Tue Dec 10, 2013 12:21 am

OK, just to get this clear in my limited brain. LOL

When using the double-res interlaced mode, the tiles are accessed as 8x16 instead of 8x8, correct?

Does that mean that there is the same amount of tile data, it's just the way it "pairs" the tiles together that makes the difference?

Ohhhh, hang on a sec....

I think I see now. To use the example image from the Genesis Tech Manual...

Image

In non-interlace mode, a tile would only be the top "half" of the pattern shown above for an 8x8 tile.

But in interlace mode, an extra MSB bit is needed to access the second half for the 8x16.

So, if you look at the non-interlace code...

Code: Select all

BGA_TILEBASE <= BGA_VRAM_DO(10 downto 0) & not(BGA_Y(2 downto 0)) & "00";
...the BGA_TILEBASE register is only 16-bits wide, but would it need to be increased to 17-bits wide to accommodate the extra MSB, or am I looking at everything backwards?

In Sonic 2, the normal bit 10 from BGA_VRAM_DO would then be considered bit 11?

My head hurts. :(

I'll get it in the end, just need to visualize everything first.

Has anyone made any nice diagrams to show the relationship between the different parts of the VDP before?

The diagram could show the VRAM / VSRAM / CRAM etc., and how their outputs related to other stuff?

OzOnE.

Jorge Nuno
Very interested
Posts: 374
Joined: Mon Jun 11, 2007 3:09 am
Location: Azeitão, PT

Post by Jorge Nuno » Tue Dec 10, 2013 12:23 am

Problem is that the CRAM and VSRM are internal.. There's a bunch of work from Nemesis on interpreting VDP inner behaviours, but it's doing indirectly. We have no direct access to internal stuff, not untill a chip is decapped and analysed


Tile pointers always go from 0 upto 7FF. On interlace it's the same except it's 0 -- 7FE (meaning you can't "map" odd tiles, because they are accessed in pairs now).

How many LEs does the project use? I think you didn't saw my edit :V

OzOnE
Interested
Posts: 23
Joined: Wed Dec 04, 2013 3:49 pm

Post by OzOnE » Tue Dec 10, 2013 12:47 am

Interesting.

I may have to make up some diagrams then, just to try to visualize some stuff better.

Has the VDP been decapped yet?

I was looking at the huge photos of the YM2612 last night. It's fantastic stuff, but the VDP would be even better.

Just seen your edit, Jorge....

The fpgagen core takes around 64% of the LE's on the DE1, so around 12,000 out of 18,752.

That's with the PSG and still with one internal YM channel running though, so I think it could be made a tad smaller if you go back to Grégory's original code with no audio?

I think it might fit on your chip? It's hard to compare them directly, but the XC3S1200E is also around 20,000 LEs isn't it?

Is that the same chip as on the original Minimig?

Anyway, I'm slowly getting my head around the VDP stuff...

I can see now that the tile base addr can access the full 64KB of VRAM (16-bits), and BGB_VRAM_DO in the project is set up to access each TILE...

The lower two bits of the address are set to 00, so the BGB_Y part selects each "row" of the chosen tile (each row of four bytes).

BGB_VRAM_DO selects each 32-Byte block of VRAM, so it chooses the actual Tile. ;)

So, forcing the LSB of BGB_VRAM_DO is probably correct, but I need to handle the SCROLL pointers as well?

OzOnE.

Jorge Nuno
Very interested
Posts: 374
Joined: Mon Jun 11, 2007 3:09 am
Location: Azeitão, PT

Post by Jorge Nuno » Tue Dec 10, 2013 1:08 am

No the VDP is still a goddamn virgin, noone poked at it yet. It's loooong overdue :P

That Spartan 3E has 8672 "slices" [Xilinx terminology]
Each S3E slice has 2 LUT-4s and 2 FFs, so I guess it would be equivalent to an altera chip with 17344 LEs... maybe I dunno. The chips may have a different internal logic stuffs making the number incomparable but ehh. Easiest way to figure out would be to build the project on ISE and checking :P

Still, Id need to load the bin file into a spearate memory, but I can already do that

HScrolling affects pairs of lines, and vertical scrolling goes in steps of 2.

OzOnE
Interested
Posts: 23
Joined: Wed Dec 04, 2013 3:49 pm

Post by OzOnE » Tue Dec 10, 2013 1:51 am

LOL

Surprised that people wanted to poke the YM chip first?
Maybe it was due to it's sexy tones?

I only just found this thread - Nemesis to the rescue again!...

viewtopic.php?p=15708

Having a read through now.

OzOnE.

OzOnE
Interested
Posts: 23
Joined: Wed Dec 04, 2013 3:49 pm

Post by OzOnE » Tue Dec 10, 2013 2:14 am

Ahaaaa!!

Got it now. :D

Tiles in background mode are properly displayed now....

The scroll values and sprites still need to be fixed, but it's a lot better.

Just needed that one sentence from Nemesis...

"When you specify a block number in your mapping data for each layer, you're specifying the number of an 8x16 pixel block, which is 0x40 bytes in size instead of 0x20 bytes (so in 8x16 mode, block 0 is at 0x00, block 1 is at 0x40, etc, while in 8x8 mode, block 1 would be at 0x20)."

So, we were shifting the bits in the wrong direction. It looks like it actually just dumps the MSB of the Tile index, then adds an extra zero to shift the bits left...

Code: Select all

	if BGA_VRAM_DO(12) = '1' then	-- VF
		if IM = '0' then BGA_TILEBASE <= BGA_VRAM_DO(10 downto 0) & not(BGA_Y(2 downto 0)) & "00";
		else BGA_TILEBASE <= BGA_VRAM_DO(9 downto 0) & "0" & not(BGA_Y(2 downto 0)) & "00";
		end if;
Just fixing the sprites now and see what happens.
I think even the scroll values are working OK? Just some weird tiles here and there due to the Sprites not being fixed yet (on waterfalls etc).

I will of course post the new source code if I manage to get it all working well.

Audio is working again on this compile - makes a damn change.


OzOnE. 8)

Jorge Nuno
Very interested
Posts: 374
Joined: Mon Jun 11, 2007 3:09 am
Location: Azeitão, PT

Post by Jorge Nuno » Tue Dec 10, 2013 2:24 am

Aha, so it was the MSB instead of the LSB as I thought initially.. damn

Post Reply