Repairing damaged plastic pegs

Friday, 4th October 2019

It's not uncommon for parts of old electronic devices to have damaged plastic pegs, like the one in this photo:

Button with missing plastic peg marked

If you still have the snapped off plastic peg you may be able to glue it back on, but this can leave a weakened part that doesn't hold up very well. In other cases you might have lost the part entirely. This happened to me recently with the purchase of a six-button Mega Drive control pad. I ordered it from CeX's website and so couldn't see what condition it would be in until it arrived in the post. Unfortunately, it arrived in a filthy condition with a d-pad that only worked if you pressed the buttons very firmly and a non-functioning Mode button. I took it apart and was able to get the d-pad working again by cleaning the contacts. Whilst I left the rest of the plastic parts soaking in the sink to try to remove as much of the encrusted grime as possible I turned my attention to the faulty Mode button.

Broken 'Mode' tactile switch and damaged plastic button

At some point the button must have been pushed in too firmly, damaging the tactile switch on the main board - the metal casing was bent and the plastic switch body had separated from it with the metal diaphragm that closes the contacts falling out. I've seen the same thing happen to some Sega Saturn control pads and fortunately had some spares in my parts bin so was able to swap that out easily. However, the pressure had snapped one of the plastic pegs of the Mode button off and it was nowhere to be found, so I needed to construct a replacement.

Fortunately, I have some scrap plastic parts from cutting out holes in plastic enclosures. In my case I needed to make a new peg that was 1.8mm in diameter, and had some 2mm sheet to use for this purpose. If I didn't have this then I could have gone online to buy a small 2mm thick sheet of ABS, but I prefer to recycle where possible!

The cut plastic stock mounted in a rotary tool chuck

The first thing to do was to get a piece of plastic that was roughly the right size, so I cut a length with a cross-section of 2×2mm. To cut the plastic I scored it with a knife and then snapped it by gripping the short part with a pair of pliers and bending the longer part away from the score line.

As the plastic peg needs to rotate in its slot inside the control pad it needs a round cross-section rather than a square one. To shape the peg into a rough cylinder I mounted it in the chuck of a rotary tool, as pictured above.

A photo of some initial filing of the plastic part

The rotary tool was then switched on and the plastic part held against a file. The photo above shows the start of this process with the peg beginning to take form. You need to work somewhat slowly with plastic as it gets hot when filing, cutting or drilling and if you let the heat build up it can melt and bend or gum up your tools. In my case I only used the file for a short period at a time before giving the piece time to cool back down. If I had been able to use my variable-speed rotary tool I could have set it to a lower speed to reduce the heat however the collet chuck on that tool wouldn't have been able to grip the work piece.

The work piece showing a smooth round profile

It doesn't take very long to get a nice round profile on the peg, though, even when working slowly (it's only a small piece, after all!) I carried on working it until I measured the 1.8mm diameter I was aiming for.

A size comparison between the new peg and the missing peg

The above photo shows a size comparison between the new peg and the intact one on the Mode button. I've left the piece long (with the rough ending as a "handle") to make it easier to work with until it's time to fix it in place rather than cutting it to length straight away.

How to attach the new peg to the damaged button is the next issue to deal with. The rough surface of the snapped area of the button would make gluing difficult, though it could be filed flat to provide an easier surface to work with. My preferred technique, however, is to fix the new plastic peg with a metal pin made from a paper clip. This involves drilling narrow holes in the old button and the new peg to fit the metal pin through.

Starting to drill through the plastic part to make a hole to mount the metal pin

A pin vice is an invaluable tool for fine drilling work like this. I started with a very small drill bit to make the initial hole, being very careful to ensure I was drilling straight and in a well-centred location. This is something that needs to be taken slowly, especially on the original part. Once I had a pilot hole in the right place I switched to a larger drill bit that would drill a hole that the paper clip could fit into.

Starting to drill through the plastic peg to make a hole to mount the metal pin

Starting the hole in the plastic peg might be easier by spinning the piece in your rotary tool and then bringing the stationary drill bit up to the end of it. As before be very careful about melting the plastic as once the hole is drilled the plastic is even thinner and will melt more easily – the peg is much shorter in the above photo than it was in previous photos as I accidentally melted it when I tried to drill all the way through in the rotary tool. I had to cut off the melted and distorted part to start again, only starting the hole in the rotary tool this time and then drilling the rest of the way slowly by hand. It's a good thing there was plenty of excess material!

The broken button, plastic peg, and paper clip used to join them

The above photo shows the short length of paper clip that has been cut to connect the two plastic parts. Using a piece of wire like this should provide a lot of strength to the join – my experiences of gluing plastic to plastic have been very mixed, depending on the glue and plastic involved.

The peg glued to the broken button with the paper clip

The parts in this case are all glued together with superglue and it seems to hold together quite well. The replacement peg was first cut to length before being glued. I coated one end of the paper clip rod in glue before pushing it into the hole on the button, then added glue to the other end and slid the new peg on. The end of the peg had a slightly protruding piece of the paper clip rod, so it was filed flush once the glue had set.

Filling the gaps in the join with two-part epoxy resin

The uneven break still left a small gap in the join between the new peg and the Mode button body, so I filled it with a small amount of two-part epoxy resin. I used a toothpick to transfer a small amount of the mixed epoxy resin into the gap. This is always a bit messy so I protected the main button surface with masking tape. I also tidied up any "blobbiness" or glue that had otherwise run to undesired areas with a sharp knife before the epoxy had fully cured.

Final view of the repair in situ

The button now sits properly in place inside the control pad and swings freely as it should. I could of course have just returned the control pad for a refund considering its condition but in spite of its problems it was still a well-priced item and I'm not sure they would have bothered to repair it. At least I know this way it'll be appreciated in its second life!

Modifying a Master System cartridge for use with flash ROMs

Thursday, 22nd August 2019

I have a ToToTEK GG-PRO flash cartridge to run homebrew software on my Game Gear however I have never been able to get it to work on my current PC and it seems that it's hard to find a Master System equivalent these days. A contemporary alternative is the Master EverDrive and it is by all accounts an excellent piece of equipment however it is a very expensive product.

I had, however, heard that certain Master System cartridges could be modified to accommodate a flash memory chip in place of their stock mask ROM. I do say certain cartridges as it's the ones with separate mapper chips that need to be used. One such cartridge is After Burner, and as I was able to find an inexpensive loose copy on eBay I used that as the basis of my modifications.

Modified After Burner cartridge with flash memory chips

The memory mapper is used to map ROM banks or save RAM on the cartridge into one of three 16KB slots in the Z80's address space. Most Master System cartridges only contain a single chip that combines the ROM data for the game as well as the memory mapper logic. As such, these cartridges can't be modified for use with a generic flash memory chip as you can't access that internal memory mapper. Some cartridges, however, make use of a separate mapper chip and so you can remove the plain mask ROM chip and replace it with a flash memory chip. SMS Power! has this list of mappers and examples of cartridges in which they can be found.

Removing the old masked ROM chip

In the photo above you can see where I removed the mask ROM from the cartridge PCB and have left the mapper chip on the board. I didn't want to damage the old ROM chip (in case I wanted to play After Burner again) so I carefully unsoldered it rather than cutting it off the board. To do so I heated up each solder joint on the back of the board and used my spring-loaded solder sucker to remove the molten solder. After this I checked each pin by gently trying to move it in its hole; if it moved I knew it had been unsoldered and if it was stuck fast I knew I needed to try removing more solder. Once all pins were free the old ROM lifted cleanly out of the PCB.

One of convenient features of these cartridges is that the pinout of Sega's mask ROMs is virtually identical to the pinout of commonly-available flash memories like the 29F010 or 49F040. Only two pins need to be changed, as per the information on Charles MacDonald's website:

Pin Mask ROM Flash memory
1 Not connected A18
31 A18 /WE (Write Enable)

As I wanted to ensure that the cartridge was compatible with both the original mask ROM and the replacement flash memory chip I thought it best to install a switch to let me select the type of memory that is installed. To break the connection between the solder pad on the PCB and the leg on the memory chip I used an IC socket with legs 1 and 31 bent out and not soldered through their corresponding holes. Wires are soldered to the bent out legs and go via the switch to the corresponding solder pads on the bottom of the PCB.

The position of A18 definitely needed to be swapped between pin 31 on the mask ROM and pin 1 on the flash memory if I wanted to be able to address all 512KB of a 4 megabit ROM. I had heard reports that the write enable pin on the flash memory can be left disconnected however the datasheets for the flash memory chips I checked did seem to indicate that it should be held high during read operations so I thought it best to hold it high when in "flash" mode. This means that the function of both pins needed to be changed by the switch, so I used a DPDT to make this happen. The two different states are illustrated below, showing the connections to the six pins on the bottom of the switch:

The two switch positions that let you use the same socket for mask ROMs and flash memory

The heavy black lines show the position of the switch contacts when in the upper and lower positions. When in "MPR" mode you can see that pin 31 of the IC socket (A18) is connected to pin 31 of the PCB and pin 1 (NC) of the IC socket is not connected to anything. When in "FLASH" mode pin 31 of the IC socket (now /WE) is connected to Vcc and pin 1 of the IC socket (now A18) is connected to pin 31 of the PCB.

Close-up detail of the bent pins and soldered wires of the IC socket

The photos above show how the pins of the IC socket were bent outwards with very fine wires soldered to them. These fine wires run through holes on the PCB under the IC socket to the underside. I did stick very small pieces of electrical insulating tape under the points where the solder joints for the wires attached to the bent pins made contact with the PCB for a bit of added security. With those legs bent out and the wires threaded through the PCB the socket could be soldered down.

Photos showing holes being cut in the cartridge enclosure for access to the memory chip and mode switch

Unfortunately, one problem with using an IC socket is that the extra height means that the PCB no longer fits inside the cartridge shell. I needed to cut a rectangular hole in the cartridge enclosure for the chip to protrude through. I started by drilling two large round holes at the far ends of the chip - this allows me to use a chip puller (or small screwdriver) to pull (or lever) the chip out of the socket without needing to dismantle the cartridge each time. Due to the position of an internal support post very near to the memory chip a larger rectangular slot could not be cut – and I think this looks pretty neat anyway! A smaller rectangular hole was cut in the top of the cartridge shell for the switch to protrude through along with mounting holes for its two screws.

The switch and its soldered connections to the main PCB

Here the switch has been mounted inside the case with the wires from the IC socket soldered to the appropriate pins on the switch and other wires connected to the appropriate pads on the back of the back of the PCB.

Inserting the original mask ROM for After Burner and setting the switch to MPR mode lets me play After Burner; writing a ROM image to a flash memory chip and inserting that with the switch set to FLASH mode let me run that program instead. Putting the switch on the wrong mode would take me to the console's built-in game of Hang On as the BIOS is no longer able to read the cartridge as a valid Master System game (at least when using a 512KB memory that requires A18 to be connected to the right pin). All in all I'm now happy that I have a way to run programs on my Master System from flash memory and do some homebrew experiments of my own on real hardware.

Flight of Pigarus running on the Master System

The above photo shows the excellent homebrew Flight of Pigarus by Kagesan running on my Master System courtesy of the modified cartridge. I've tested it so far with an AM29F010B (128KB/1 megabit) and an AT49F040 (512KB/4 megabit) and have been using the Willem programmer (along with Remapped IO.DLL to get it to work with my PC's PCI parallel port) to program the chips.

A temporary solution for 3D games on the Master System without the 3D glasses adaptor

Monday, 29th July 2019

I bought my Sega Master System-compatible 3D glasses almost exactly ten years ago for use in my LCD Shutter Glasses Adaptor project.

More recently I've acquired a CRT television and an actual Sega Master System so I could in theory make use of the glasses as I had originally intended - with Sega Master System games.

Composite video adaptor for the LCD shutter glasses adaptor being tested with Zaxxon 3-D


I've been keeping an eye on eBay for the 3D Glasses Adaptor for the Master System. This is a device that plugs into the console's card slot and allows it to drive the glasses with the software controlling which LCD shutter is open and which is closed by writing to the card. Unfortunately, these cards are not too easy to find in the UK and when they do appear they usually came bundled with a broken pair of glasses (it seems very unusual for both arms to still be attached to the glasses, and I've seen a fair few pairs that are cracked down the middle too). I already own some compatible 3D glasses so didn't want to waste money on buying a broken set of original Master System ones!

I did eventually find someone selling a loose adaptor for a reasonable price so bought it and a few games. Zaxxon 3-D was the first to arrive and I was eager to test it out. Without the card adaptor I needed to find an alternative solution, so my thoughts turned to the LCD Shutter Glasses Adaptor I'd built a few years ago.

This is designed to sit between a PC and a VGA CRT monitor and drives the shutter glasses, alternating which LCD shutter is open and which is closed every vertical sync. It can also blank out alternate scanlines, simulating an interlaced signal from a progressive one by blanking odd scanlines on one frame and even scanlines on the next, but this is not useful in our case. The Master System is already alternating complete left and right eye views on its own, so we just need to catch its equivalent of a vsync pulse and feed that into the VGA port on the back of the shutter glasses adaptor.

Composite video adaptor for the LCD shutter glasses adaptor


Above is the device I built, attached to the existing 3D glasses adaptor. It has a single composite input which should be connected to the composite output from the console, either via a splitter (if the console is connected to the TV using composite video) or via some sort of SCART breakout box (I'm using the composite video output from my SCART switch box). It also has a power socket which is used to power the circuit inside which is also passed through to the LCD shutter glasses adaptor. The box has a DE-15 connector and 5.5x2.1mm barrel plug on the other side for connections:

The barrel plug and DE-15 VGA connectors on the output side of the CVBS adaptor


I'd cut the barrel plug off a faulty power supply years ago, I'm glad I kept it as it made the project a much neater solution than it might have other been!

The circuit inside is very simple indeed. As the original adaptor has its own 5V regulator inside and is designed to be powered from a 9V power supply I had to maintain the same convention for this device, so I use a 7805 regulator to convert the incoming voltage to 5V. This powers a textbook example of an LM1881 sync separator circuit - I'm using the reference circuit from the chip's datasheet, connecting the composite video input to the chip's composite video input via a 0.1µF capacitor (without termination as it's assumed the signal is being terminated by the TV) and I use the LM1881's composite sync output and vertical sync output for the VGA connector's horizontal and vertical sync connections respectively.

The insides of the CVBS adaptor


The circuit can be seen above, stuffed in the bottom of the box - along with copious amounts of hot glue to keep the barrel plug in place! How well does it work? Well, the below animation shows two views of the circuit in action, viewed through first one shutter and then the other of the 3D glasses:

Animated demonstration of the shutter glasses in action, showing alternate views through the shutters in turn


This isn't a perfect solution - the Master System expects to be able to explicitly specify which shutter is open whereas in our case we're simply be alternating every frame. This could mean that the eyes are swapped, however there is an eye swap switch on the shutter glasses adaptor to compensate. If the view looks wrong then the switch can be used to correct it, and once that's done as long as the software alternates the views every frame then it should be fine.

I'm certainly happy for now, as it lets me play my 3D games whilst waiting for the console's intended 3D glasses adaptor to arrive in the post.

Fixing the Dreamcast Race Controller's dead zone with a simple microcontroller circuit

Tuesday, 21st May 2019

I recently bought myself a Race Controller wheel for the Sega Dreamcast and was a little disappointed with the way that it performed. I had read reviews online before buying it and some did mention that it didn't handle particularly well but others did mention that it was about the best controller available for the system so I didn't feel it was too risky a purchase.

The issues I have with the wheel stem from its excessively large dead zone – you need to turn the wheel quite far before your car starts to turn, making it feel sluggish and unresponsive.

Fortunately, the wheel hardware is very simple internally – a 100KΩ potentiometer is used to detect the wheel's position and it outputs an analogue voltage to the controller PCB. We can take advantage of that to insert our own circuit between the potentiometer and controller PCB to sample the wheel position, add an large offset to it to push it outside the dead zone and then output that corrected voltage to the stock PCB. This will then cancel out the offset as part of the large dead zone before sending the position to the console.


The video above goes into more detail about how this circuit works as well as illustrating the problem with the stock dead zone. For more information and to download the code and circuit diagram please see the De-Dead Zone product page.

Cheats for the PAL version of Quake on the Sega Saturn

Friday, 25th November 2016

I recently picked up a Sega Saturn and a copy of the technical marvel that is Quake for it.

Sega Saturn console, controller and copy of the game 'Quake'


The Saturn is not renowned for being a particularly capable 3D machine and so the fact that Quake runs at all is quite remarkable, let alone as well as it does in Lobotomy Software's version. Rather than port the Quake engine to the Saturn the game uses the SlaveDriver engine, and includes conversions of 28 of the original 32 levels with some minor tweaks to improve performance. It certainly captures the atmosphere of Quake far more faithfully than most console ports of DOOM did to that game, leaving the sound and music intact and retaining the gritty aesthetic of Quake's software renderer.

Screenshot of Quake's first level on the Sega Saturn


Unfortunately, I'm not very good at it. Even though I could probably complete the PC version's first level in my sleep these days it took me three shameful attempts on the easiest difficulty level to get through it on the Saturn. The controls are somewhat awkward (for example, to aim up you need to hold X and press down on the d-pad) and so I thought that a cheat code or two might help me along until I'd got to grips with the game's controls.

I found a list of cheats on a newsgroup from the game's developer but most of them did not work with my copy of the game, and the few codes that did do something ended up performing the function of a different cheat. For example, invicibility ("Paul Mode") is toggled by highlighting "Customize Controls" then entering RLXYZRLXYZ, but on my copy of the game that toggled "Jevons-Control Mode" instead. These codes matched the ones on various cheat database sites across the Internet, so I was a bit puzzled until I found a forum post with a couple of codes that did work. This is an incomplete set, and it's clear that the PAL version of the game has different cheats to the NTSC-U version. Other sites either mentioned that the PAL version doesn't have cheats at all, or is missing most of them due to being an older version of the engine.

One thing stuck out to me, though - all NTSC-U cheat codes follow the same basic formula of highlighting a particular menu item under "Options", entering a ten button sequence using only the X, Y, Z, R and L buttons, and then seeing a confirmation message on the screen. I assumed that the PAL version would do something similar, and that some table of cheat codes and messages could be found in the executable. I popped the game CD into my PC CD drive and copied the executable file to the hard disk so I could examine it in a hex editor.

Screenshot of hex editor showing cheat message strings


The first thing I did was search for "Jevons-Control" which identified it at the top of a list of other cheat-related messages such as "All weapons added" or "Nail Tracers On" which made me hopeful that the other cheat codes were present in the PAL version of the game. Some cheats in the NTSC-U version (such as those relating to rain or cluster bombs) didn't seem to have an equivalent message in the list of strings here so these were presumably missing in the PAL version, but at least I knew I was on the right track.

At this point, I had two button sequences that I knew worked - RLXYZRLXYZ and LRLLZYXZYX. I hoped that these codes might be found in the game's executable, but of course didn't know how they'd be represented. At first I assumed each code would be a ten byte sequence, with one value for 'R', one value for 'L', another for 'X' and so on and so forth. As the two known cheats repeated buttons (for example, LRLLZYXZYX has L three times) it would be possible to see if a particular sequence of bytes followed the same pattern as the cheat code (for example, with LRLLZYXZYX the first, third and fourth bytes would need to all have the same value to represent 'L', and that value could not appear anywhere else in the ten byte sequence). With that in mind I wrote a program that scanned through the entire binary from start to finish, checking to see if either of the two codes could be found. Neither could, so I changed the program to instead assume that each button's value would be stored as a sixteen-bit word. Still no luck, but as the Saturn is a 32-bit system I again increased the size of each button in the sequence to try 32-bit integers and found two matches for the two codes.

Console program that matched button sequences


Not only were both sequences found, but both used the same values for the buttons (e.g. L is 0x00000008 in both sequences) and both were near each other in the binary. This seemed like the place to look, so I put the lower address value into the hex editor to see if there were other sequences nearby.

Screenshot of hex editor showing cheat button sequences


Now that I knew where the cheat codes were and how to map each value to a button name I could work through the binary, check to see if each sequence of ten 32-bit integers all matched known button values and if so output the sequence. This gave me three additional sequences for a total of five.

Console program that matched button sequences



Now I could take those five sequences, try them in the game and match them to the known NTSC-U sequences. Most of the cheat codes sequences are used more than once, changing behaviour depending on which menu item was highlighted when they were entered. Comparing the effect of certain codes in the PAL version against what the NTSC-U version was known to do produces the following table:

NTSC-U PAL
RRLRXYZXYZ RLXYZRLXYZ
RLXYZRLXYZ LRLLZYXZYX
RXLZLRYLRY XYZLRXYZRL
RYLYXYZXYZ YYZYYZLRLR
RZLXYLRYLR ZZLZZRXYXY

I'm not sure why the PAL version uses different cheat code sequences, but it is an earlier version of the game and they are also quite a bit easier to enter on the console so maybe it was decided that players needed to work harder to take advantage of their cheat codes. There are a few other NTSC-U cheat code sequences that don't match up with the PAL version, but these are for cheats that seem to be missing equivalent strings (such as the previously mentioned rain or cluster bomb cheats) so I reckon they were not yet added to the PAL version.

Screenshot of Quake Wrestling


For the sake of completeness, here is a list of cheats that work in the PAL version of Quake. All need to be entered by pausing the game, highlighting a particular item in the Options menu and then entering the cheat code as quickly as you can. Some cheats also require you to stand in a particular place in a map or to have collected certain items first; these are noted where appropriate.

Name Menu Cheat
Paul Mode (Invincibility) Customize Controls LRLLZYXZYX
All Weapons Customize Controls XYZLRXYZRL
Full Health Customize Controls YYZYYZLRLR
All Runes
Stand in the area where the first rune can be picked up in "The House of Cthon".
Customize Controls ZZLZZRXYXY
Jevons-Control Mode (3D Control Pad) Customize Controls RLXYZRLXYZ
Level Select
You must either have all four runes or be standing on the right hand side of the flat part of the bridge over the lava in the "Entrance" level.
Reset to Defaults RLXYZRLXYZ
Restart Level Reset to Defaults LRLLZYXZYX
Normal Difficulty Music Volume RLXYZRLXYZ
Easy Difficulty Music Volume LRLLZYXZYX
Hard Difficulty Music Volume XYZLRXYZRL
Show Credits
Stand on the right hand side of the bridge under the round stained glass window in "Castle of the Damned".
Stereo RLXYZRLXYZ
Show Special Credits
Stand in the secret underwater cave containing the Megahealth and Nails in "Gloom Keep".
Stereo LRLLZYXZYX
Quake Wrestling
Stand either at the Quad Damage in the secret area opened by jumping into the overhead light in "The Sewage System" or in the suspended cage half way through "The Tower of Despair".
Stereo XYZLRXYZRL
Zero-G Mode Lookspring RLXYZRLXYZ
Monster Attack Auto Targeting RLXYZRLXYZ
Nail Tracers Auto Targeting LRLLZYXZYX

If there are still people out there struggling through the PAL version of Quake on the Sega Saturn, maybe these cheat codes will come in handy!

Cogwheel 1.0.3.0 beta 3

Monday, 24th August 2009

I managed to break save states in the last build of Cogwheel (attempting to load a save state would fail, not being able to set a property). I've marked the offending read-only property with [StateNotSaved] and made the loader slightly more robust in Cogwheel 1.0.3.0 beta 3. It's beta 3, not 2, because I uploaded 2 and then noticed another issue - you couldn't change the controller mappings! This is something that must have been broken for ages, but either nobody noticed or they just didn't care to report it. Oh well, that's been fixed now. For some reason Google don't let you re-upload files, so beta 3 it has to be.

Phantasy Star save screen

Another addition is this build is preliminary support for persistent cartridge RAM. Some games, such as Phantasy Star (pictured above) let you save your progress in the game onto battery-backed RAM built into the cartridge. If you come back to the game later you should now be able to continue your progress without needing to manually save the entire emulator state.

I've had reports of rather bizarre crashes bringing one poor user's machine to its knees. I'm at a loss to establish why; I've tried the emulator on four machines (two Vista, two XP) and although one of the machines displays a white screen instead of the emulator output (no pixel shader 2.0 support on its Radeon 9000) the software trundles along just fine otherwise (I can at least hear the game music!) The one notable difference between my machines and his machine is that he's using a 64-bit version of Windows, and all of the ones I have access to run 32-bit Windows. To see if this is the issue, I've changed the configuration to x86 (I've encountered strange bugs with .NET code using unmanaged 32-bit code on 64-bit Windows) to see if this will remedy issues, but if anyone has any bright ideas I'd be interested to hear them.

Cogwheel 1.0.3.0 beta 1

Thursday, 20th August 2009

I've released a beta version of Cogwheel 1.0.3.0 in the hope of getting some feedback. My main concern is with the new 3D glasses code, so I'd be very grateful if you could install the emulator and run this ROM in it. The ROM simply alternates between showing a red screen for the left eye and a cyan one for the right eye (the emulator defaults to only showing the left eye, so you'll just see red for the moment). If you select a different 3D glasses mode (Options, 3D glasses, Row interleaved) you should end up with something like this:

3D glasses test - row interleaved

If you drag the around the desktop the lines should appear fixed on the spot (as long as you drag it slowly enough to allow it to repaint), and if you resize it the entire form should always be covered in lines one pixel apart. The same should apply to the column interleaved and chequerboard interleaved modes.

I've also added VGM recording and VGM playback. VGM playback is handled by bundling Maxim's SMS VGM player.

VGM player

The console's region (Japanese or Export) and video standard (NTSC or PAL) are now user-configurable via the Emulation menu. The YM2413 (FM sound) emulation has been converted to straight C# (it used to be a P/Invoked native DLL). Drag-and-drop support has been added to aid in loading ROMs, save-states and VGMs.

There have been a number of internal optimisations, fixes and tweaks (such as per-scanline backdrop colours), but nothing too major (compatibility is roughly the same as it was). If you do find any bugs, please report them!

New 3D renderer in Cogwheel

Monday, 10th August 2009

I have written a new 3D-compatible renderer for Cogwheel. It holds two textures, one for each eye, and uses one of a number of different effect file techniques to mix the two views.

Row-interleaved 3D

Based on the interlacing work from the previous entry, the first technique is one that uses interleaved rows. I'm not really sure if there's a good way to convert texture coordinates into device coordinates, so am passing in the viewport height as a parameter and hoping that floating point errors don't trip me up (they haven't, yet).

float4 RowInterleavedPixelShader(VertexPositionTexture input) : COLOR0 {
	float row = input.Texture.y * ViewportHeight * 0.5f;
	if (abs(round(row) - row) < 0.1f) {
		return tex2D(LeftEyeSampler, input.Texture);
	} else {
		return tex2D(RightEyeSampler, input.Texture);
	}
}

Alternate pixel centres may also pose a problem in the future. If anyone had any recommendations, suggestions or warnings on the way I'm detecting the evenness or oddness of a particular "scanline" then I'd appreciate hearing them!

Colunn-interleaved 3D Chequerboard-interleaved 3D

I have also added two other interleaving modes; one in columns and another in a chequerboard pattern. I included these two as I've seen that some 3D LCD panels use a column interleaving pattern (I suppose that with a lenticular lens in front of such a panel you may not even need 3D glasses) and apparently Sharp have displays that use the chequerboard pattern.

I have also taken advantage of pixel shaders to create colour and monochrome anaglyphs (previously calculated in software), though neither look as good as the above full-colour modes for shutter glasses or similar hardware.

There are a few issues I need to sort out first before I can release this; for example, there's no way to set whether the first row/column/pixel is for the left or right eye. More problematic is the removal of support for non power-of-two textures; the Master System's 256×192 display is fine, but the Game Gear's 160×144 display gets rounded up to 192 pixels wide (and yes, I know that's not a power of two) on my video card. I also mean to give Promit's SlimTune profiler a look to see if I can optimise some of the less efficient pieces of my code. The C# version of emu2413 is probably a good candidate, being a "dumb" translation from the original macro-heavy C.

C# emu2413

Thursday, 8th January 2009

This is fairly embarrassing; somebody sent me an email that was flagged as spam which I accidentally deleted. So if you sent me an email and I haven't replied, I'm not deliberately being rude; could you send it again? embarrass.gif



After encountering strange crashes (not .NET exceptions, full out crashes) with emu2413 I decided to port it to straight C# instead from its existing C incarnation (emu2413.h.cs and emu2413.c.cs). Even though the original was macro-heavy it was relatively simple to port, and so there's no dependency on an unmanaged DLL to generate FM sound any more. However, the C# version is significantly slower (Cogwheel now takes about 50% extra CPU time when FM sound is enabled), possibly due to many extraneous method calls that were macros in the original.

However, the emulator still crashes when FM sound is enabled. And I have no idea why, as it only happens in Release mode and outside the IDE. The Debug build works fine inside and outside the IDE, and Release mode works fine when run within the IDE. sad.gif

Controller input updates to Cogwheel

Monday, 5th January 2009

I hope you all had a good Christmas and New Year period!

I received an Xbox 360 controller for Christmas, so have done a bit of work on Cogwheel to add support for it. (You can download a copy of the latest version 1.0.2.0 with SlimDX here).

The first issue to deal with was the D-pad on the Xbox 360 controller. When treated as a conventional joystick or DirectInput device the D-pad state is returned via the point-of-view (POV) hat. The joystick input source class couldn't raise events generated by the POV hat so support for that had to be added. This now allows other controllers that used the POV hat for slightly bizarre reasons (eg the faceplate buttons on the PlayStation controller when using PPJoy) to work too.

The second issue was the slightly odd way that the Xbox 360's DirectInput driver returns the state of the triggers - as a single axis, with one trigger moving the axis in one direction, the other trigger moving it in the other. You cannot differentiate between both triggers being held and both being released, as both states return 0. To get around this, I've added support for XInput devices, where all buttons and triggers operate independently.

The Xbox 360 controller now shows up twice in the UI - once as an XInput device and again as a conventional joystick. Fortunately, you can check if a device is an XInput device by the presence of IG_ in its device ID. Here's some C# code that can be used to check with a joystick is an XInput device or not.

using System.Globalization;
using System.Management;
using System.Text.RegularExpressions;

namespace CogwheelSlimDX.JoystickInput {
    
    /// <summary>
    /// Provides methods for retrieving the state from a joystick.
    /// </summary>
    public class Joystick {

        /* ... */

        /// <summary>
        /// Gets the vendor identifier of the <see cref="Joystick"/>.
        /// </summary>
        public ushort VendorId { get; private set; }

        /// <summary>
        /// Gets the product identifier of the <see cref="Joystick"/>.
        /// </summary>
        public ushort ProductId { get; private set; }

        /* ... */

        /// <summary>
        /// Determines whether the device is an XInput device or not. Returns true if it is, false if it isn't.
        /// </summary>
        public bool IsXInputDevice {
            get {
                var ParseIds = new Regex(@"([VP])ID_([\da-fA-F]{4})"); // Used to grab the VID/PID components from the device ID string.

                // Iterate over all PNP devices.
                using (var QueryPnp = new ManagementObjectSearcher(@"\\.\root\cimv2", string.Format("Select * FROM Win32_PNPEntity"), new EnumerationOptions() { BlockSize = 20 })) {
                    foreach (var PnpDevice in QueryPnp.Get()) {

                        // Check if the DeviceId contains the tell-tale "IG_".
                        var DeviceId = (string)PnpDevice.Properties["DeviceID"].Value;
                        if (DeviceId.Contains("IG_")) {

                            // Check the VID/PID components against the joystick's.
                            var Ids = ParseIds.Matches(DeviceId);
                            if (Ids.Count == 2) {
                                ushort? VId = null, PId = null;
                                foreach (Match M in Ids) {
                                    ushort Value = ushort.Parse(M.Groups[2].Value, NumberStyles.HexNumber);
                                    switch (M.Groups[1].Value) {
                                        case "V": VId = Value; break;
                                        case "P": PId = Value; break;
                                    }
                                }
                                if (VId.HasValue && this.VendorId == VId && PId.HasValue && this.ProductId == PId) return true;
                            }
                        }
                    }
                }
                return false;
            }
        }

        /* ... */
    }
}

When the joysticks are enumerated they are only added to the input manager if they are not XInput devices.



To round up the entry, here's a screenshot of a minesweeper clone I've been working on in BBC BASIC.

2009.01.03.01.gif

You can view/download the code here and it will run in the shareware version of BBC BASIC for Windows. The code has been deliberately uglified (cramming multiple statements onto a single line, few comments, trimmed whitespace) to try and keep it within the shareware version's 8KB limit as this is a good limit to keep in mind for the TI-83+ version too.

Sega Master System emulation in Silverlight

Monday, 15th December 2008

I've had to quickly learn Silverlight for work recently, which has been an interesting experience. I've had to write new code, which is fine but doesn't really excite me as far as Silverlight is concerned - it doesn't really matter which language new code is developed in, as long as it gets the job done.

What does interest me more is that Silverlight is ".NET in your browser", and I'm a big fan of .NET technology with a handful of .NET-based projects under my belt. Silverlight therefore gives me the opportunity to run these projects within the browser, which is a fun idea. smile.gif

To this end, I've turned Cogwheel, a Sega 8-bit system emulator, into a Silverlight application. It took about an hour and a half, which was not as bad as I'd expected! (Skip to the bottom for instructions for the demo).

Raster graphics

Silverlight's raster graphics support is somewhat lacking. You can display raster graphics in Image elements, but - as far as I can see - that's about it. If you wish to generate and display images dynamically via primitive pixel-pushing, you're out of luck as far as Silverlight's class library is concerned.

Thankfully, Ian Griffiths has developed a class named PngGenerator that can speedily encode a PNG from an array of Colors that can then be displayed in an Image. Cogwheel's rasteriser returns pixel data as an array of integers so there's a small amount of overhead to convert these but other than that it's easy to push pixels, albeit in a fairly roundabout manner.

Render loop

The render loop is based around an empty Storyboard that invokes an Action every time it completes then restarts itself.

using System;
using System.Windows;
using System.Windows.Media.Animation;

namespace Cogwheel.Silverlight {

    public static class RenderLoop {

        public static void AttachRenderLoop(this FrameworkElement c, Action update) {
            var Board = new Storyboard();
            c.Resources.Add("RenderLoop", Board);
            Board.Completed += (sender, e) => {
                if (update != null) update();
                Board.Begin();
            };
            Board.Begin();
        }

        public static void DetachRenderLoop(this FrameworkElement c) {
            var Board = (Storyboard)c.Resources["RenderLoop"];
            Board.Stop();
            c.Resources.Remove("RenderLoop");
        }
    
    }
}

I'm not sure if this is the best way to do it, but it works well enough and is easy to use - just grab any FrameworkElement (in my case the Page UserControl) and call AttachRenderLoop:

private void UserControl_Loaded(object sender, RoutedEventArgs e) {
    this.UserControlRoot.AttachRenderLoop(() => {
        /* Update/render loop in here. */
    });
}

Missing .NET framework class library features

This is the big one; Silverlight does not cover the entire .NET framework class library, and so bits of it are missing. Fortunately this can be resolved, the difficulty depending on how you want the functionality of the original app to be affected.

Missing types you're not interested in.

These are the easiest to deal with, and this includes attributes and interfaces that the existing code uses that you're not especially interested in. For example, Cogwheel uses some of .NET's serialisation features for save states - a feature I wasn't intending on implementing in the Silverlight version. The [Serializable] and [NonSerialized] attributes are not available in Silverlight, nor is the IDeserializationCallback interface. To get the project to compile some dummy types were created.

namespace System {

    class SerializableAttribute : Attribute { }
    class NonSerializedAttribute : Attribute { }
    
    interface IDeserializationCallback {
        void OnDeserialization(object sender);
    }

}

Missing types or methods that you don't mind partially losing.

Cogwheel features some zip file handling code that uses System.IO.Compression.DeflateStream, a class not available in Silverlight. Rather than remove the zip classes entirely (which would require modifications to other files that relied on them) it was easier to use conditional compilation to skip over the DeflateStream where required.

switch (this.Method) {
    case CompressionMethod.Store:
        CompressingStream = CompressedStream;
        break;
    
    #if !SILVERLIGHT
    case CompressionMethod.Deflate:
        CompressingStream = new DeflateStream(CompressedStream, CompressionMode.Compress, true);
        break;
    #endif
    
    default:
        throw new NotSupportedException();
}

Missing instance methods.

C# 3.0 adds support for extension methods - user-defined methods that can be used to extend the functionality of existing classes that you cannot modify directly. Silverlight is missing a number of instance methods on certain classes, such as string.ToLowerInvariant();. By using extension methods the missing methods can be restored.

namespace System {

    public static class Extensions {
        public static string ToLowerInvariant(this string s) { return s.ToLower(CultureInfo.InvariantCulture); }
        public static string ToUpperInvariant(this string s) { return s.ToUpper(CultureInfo.InvariantCulture); }
    }

}

Missing static methods.

These are the most work to fix as extension methods only work on instance methods, not static methods. This requires a change at the place the method is called as well as the code for the method itself.

I've got around this by creating new static classes with Ex appended to the name then using using to alias the types. For example, Silverlight lacks the Array.ConvertAll method.

namespace System {

    static class ArrayEx {
        public static TOut[] ConvertAll<TIn, TOut>(TIn[] input, Func<TIn, TOut> fn) {
            TOut[] result = new TOut[input.Length];
            for (int i = 0; i < input.Length; i++) {
                result[i] = fn(input[i]);
            }
            return result;
        }
    }

}

First, a replacement method is written with Ex appended to the class name. Secondly, any file that contains a reference to the method has this added to the top:

#if SILVERLIGHT
using ArrayEx = System.ArrayEx;
#else
using System.IO.Compression;
using ArrayEx = System.Array;
#endif

Finally, anywhere in the code that calls Array.ConvertAll is modified to call ArrayEx.ConvertAll instead. When compiling for Silverlight it calls the new routine, otherwise it calls the regular Array.Convert.

Demo

The links below launch the emulator with the selected ROM image.

To run your own ROM image, click on the folder image in the bottom-right corner of the browser window to bring up a standard open file dialog.

Zip files are not handled correctly, but if you type *.* into the filename box, right-click a zip file, pick Open, then select the ROM from inside that it should work (it does on Vista at any rate).

The cursor keys act as you'd expect; Ctrl or Z is button 1/Start; Alt, Shift or X is 2 (Alt brings up the menu in IE). Space is Pause if you use an SMS ROM and Start if you use a Game Gear ROM. Keys don't work at all in Opera for some reason, but they should work fine in IE 8 and Firefox 3. You may need to click on the application first!

Issues

There are a number of issues I have yet to address. Performance is an obvious one; it's a little choppy even with 100% usage of one the cores on a Core 2 Duo. Sound is missing, and I'm not sure what Opera's doing with keys. Other than that, I thought it was a fun experiment. smile.gif

Once I've tidied it up a bit I'll merge the source code with the existing source repository.

SC-3000 keyboard and a final release

Sunday, 20th April 2008

The latest addition to Cogwheel is SC-3000 keyboard emulation.

SC3000Keyboard.png

The SC-3000 was a home computer with similar hardware to the SG-1000 console, with the main addition of a keyboard. Software cartridges could add, for example, BASIC programming capabilities.

Due to lack of time and motivation, and the fact that the emulator is pretty much as good as I'm going to get it at this moment in time, I've removed the beta label and uploaded the latest version to its website.

3D glasses and CPU cycle counting

Monday, 7th April 2008

I reintroduced joystick support to the emulator front-end over the weekend, using a much more sensible input manager. The control editor form uses the same interface design for keyboard and joysticks - you click the button you wish to edit, it appears pressed, you press the key (or joystick button) you wish to bind to it and it pops back out again. It'll also check to see if you've moved the joystick in a particular direction on any of its reported axes.

Another feature I added was better support for games that used the 3D glasses, using a simple red-cyan anaglyph output blender.

MazeHunter3D.png    PoseidonWars3D.png

BladeEagle3D.png    SpaceHarrier3D.png

If the memory address range that the glasses respond to has been written to within the last three frames, it switches to the blending mode.

Irritatingly, with one glaring bug fix I managed to lower overall compatibility. Some instructions weren't being timed correctly (or at all) meaning that the emulator was executing too many instructions for the number of clock cycles it was asked to run. During the time each video scanline is run, 228 CPU cycles are executed. Upping this to 248 cycles (just for experimentation) fixes all known bugs - including the long-standing flickering pixels at the top of the screen in Cosmic Spacehead and display corruption in GP Rider. (However, digitised speech plays at a noticably higher pitch).

I'm not entirely sure why this is - it could be that some instructions are claiming to take too long, it could be yet another interrupt problem. To call this mildly frustrating is a bit of an understatement!

ColecoVision and TMS9918 Multicolor

Thursday, 3rd April 2008

One notable gap in my TMS9918 (video) emulation was its "Multicolor" mode. This mode broke the screen down into 4×4 pixel squares, resulting in a 64×48 grid. Each cell could be assigned a unique colour, giving you a crude bitmapped video mode.

No Master System or SG-1000 software used this mode to my knowledge, which reduced the likelihood of it being supported at all (if I could't test it, how could I emulate it?) I was tipped off that some ColecoVision software made use of it, so set about emulating the ColecoVision.

ColecoVision.png

The ColecoVision hardware is very similar to the SG-1000 in terms of what is inside the case - a Z80 CPU, TMS9918 video and SN76489 sound. The memory map (as in, which address ranges map to which memory devices) is different, as is the I/O map (as in, the I/O ports that the various hardware components are connected to). To handle this case, the emulator now has a Family field, which can be set to Sega or ColecoVision. This controls which of the different mappings it uses for memory and hardware I/O.

Another difference is the presence of a BIOS ROM. The Sega Master System and Sega Game Gear consoles had the option of BIOS ROMs, but all these did was very basic initialisation and header/checksum checking. The ColecoVision, however, has an 8KB BIOS ROM that offers a lot of functionality to the programmer, so this must be present to run most ColecoVision games.

ColecoVisionControllers.png

The controllers are also quite different. As well as the typical eight-direction joystick and two fire buttons, it added a 12-key keypad (0-9, * and #). This many keys is making my InputManager class look thoroughly idiotic, so that will certainly need a rewrite.

Apart from that, it's pretty simple. RAM is 1KB instead of 8KB; the sound generator uses the standard 15-bit wide shift register (instead of the 16-bit wide one used in the SMS); the video display processor interrupt output is connected to NMI rather than INT.

With those differences applied, it's easy to add the few lines of code to emulate the Multicolor video mode.

SmurfPaintNPlayWorkShop.png
Smurf Paint 'n' Play Workshop

ColecoVision emulation has not been tested at all thoroughly, so chances are it doesn't work very well; Multicolor emulation has been tested with precisely one game!

You can download the latest build of Cogwheel from its website, featuring the new ColecoVision emulation.

SaveStates and Key Names

Monday, 31st March 2008

It's a good feeling when issue 1 is finally marked as Fixed - in this case it was another interrupt-related bug. The IFF1 flag was being used to mask non-maskable interrupts; I don't think it should and it hasn't seem to have broken anything just yet by making non-maskable interrupts truly non-maskable.

I have also changed the savestate format to something that will be a little more backwards compatible rather than a plain BinaryFormatter dump of the entire emulator state. The data is now saved in what is basically an INI file with C#-style attributes and CSS-style url() syntax for binary data.

[Type(BeeDevelopment.Sega8Bit.Hardware.VideoDisplayProcessor)]
VideoRam=Dump(Video\VideoRam.bin)
Address=49165
WaitingForSecond=False
AccessMode=ColourRamWrite
ReadBuffer=32
Registers=Dump(Video\Registers.bin)
System=Ntsc
SupportsMode4=True
; ... snip ...

Other changes include a faked fullscreen mode (ie, a maximised borderless window) and an option to retain the aspect ratio of the game, so games no longer appear stretched on widescreen monitors. The sound emulation is a bit better, but still a little noisy in certain games with spurious beeps or buzzes.

One minor problem was on the key configuration control panel. Converting members of the Keys enumeration into displayable strings can be done via .ToString(), but this results in names that do not match the user's keyboard layout (such as OemQuestion for /, OemTilde for # and even Oemcomma for , - note the lowercase c, all on a UK keyboard).

With a prod in the right direction from jpetrie, here's a snippet that can be used to convert Keys into friendly key name strings:

#region Converting Keys into human-readable strings.

/// <summary>
/// Converts a <see cref="Keys"/> value into a human-readable string describing the key.
/// </summary>
/// <param name="key">The <see cref="Keys"/> to convert.</param>
/// <returns>A human-readable string describing the key.</returns>
public static string GetKeyName(Keys key) {

	// Convert the virtual key code into a scancode (as required by GetKeyNameText).
	int Scancode = MapVirtualKey((int)key, MapVirtualKeyMode.MAPVK_VK_TO_VSC);

	// If that returned 0 (failure) just use the value returned by Keys.ToString().
	if (Scancode == 0) return key.ToString();

	// Certain keys end up being mapped to the number pad by the above function,
	// as their virtual key can be generated by the number pad too.
	// If it's one of the known number-pad duplicates, set the extended bit:
	switch (key) {
		case Keys.Insert:
		case Keys.Delete:
		case Keys.Home:
		case Keys.End:
		case Keys.PageUp:
		case Keys.PageDown:
		case Keys.Left:
		case Keys.Right:
		case Keys.Up:
		case Keys.Down:
		case Keys.NumLock:
			Scancode |= 0x100;
			break;
	}

	// Perform the conversion:
	StringBuilder KeyName = new StringBuilder("".PadRight(32));
	if (GetKeyNameText((Scancode << 16), KeyName, KeyName.Length) != 0) {
		return KeyName.ToString();
	} else {
		return key.ToString();
	}
}

/// <summary>
/// Retrieves a string that represents the name of a key.
/// </summary>
/// <param name="lParam">Specifies the second parameter of the keyboard message (such as <c>WM_KEYDOWN</c>) to be processed.</param>
/// <param name="lpString">Pointer to a buffer that will receive the key name.</param>
/// <param name="size">Specifies the maximum length, in TCHAR, of the key name, including the terminating null character. (This parameter should be equal to the size of the buffer pointed to by the lpString parameter).</param>
/// <returns>The length of the returned string.</returns>
[DllImport("user32.dll")]
static extern int GetKeyNameText(int lParam, StringBuilder lpString, int size);

/// <summary>
/// Translates (maps) a virtual-key code into a scan code or character value, or translates a scan code into a virtual-key code.
/// </summary>
/// <param name="uCode">Specifies the virtual-key code or scan code for a key. How this value is interpreted depends on the value of the <paramref name="uMapType"/> parameter.</param>
/// <param name="uMapType">Specifies the translation to perform. The value of this parameter depends on the value of the <paramref name="uCode"/> parameter.</param>
/// <returns>Either a scan code, a virtual-key code, or a character value, depending on the value of <paramref="uCode"/> and <paramref="uMapType"/>. If there is no translation, the return value is zero.</returns>
[DllImport("user32.dll")]
static extern int MapVirtualKey(int uCode, MapVirtualKeyMode uMapType);

enum MapVirtualKeyMode {
	/// <summary>uCode is a virtual-key code and is translated into a scan code. If it is a virtual-key code that does not distinguish between left- and right-hand keys, the left-hand scan code is returned. If there is no translation, the function returns 0.</summary>
	MAPVK_VK_TO_VSC = 0,
	/// <summary>uCode is a scan code and is translated into a virtual-key code that does not distinguish between left- and right-hand keys. If there is no translation, the function returns 0.</summary>
	MAPVK_VSC_TO_VK = 1,
	/// <summary>uCode is a virtual-key code and is translated into an unshifted character value in the low-order word of the return value. Dead keys (diacritics) are indicated by setting the top bit of the return value. If there is no translation, the function returns 0.</summary>
	MAPVK_VK_TO_CHAR = 2,
	/// <summary>uCode is a scan code and is translated into a virtual-key code that distinguishes between left- and right-hand keys. If there is no translation, the function returns 0.</summary>
	MAPVK_VSC_TO_VK_EX = 3,
	MAPVK_VK_TO_VSC_EX = 4,
}

#endregion

It uses P/Invoke and the Win32 API so is only suitable for use on Windows.

Fun with IThumbnailProvider

Friday, 28th March 2008

Note: I have been informed that the code below no longer works in Windows 7 due to changes in the way IThumbnailProvider operates. It is recommended that you use unmanaged code instead of the managed solution presented below.



I have started releasing Cogwheel binaries on its project page, so if you'd like a look at the project but can't be bothered to check out and build the source yourself you can now give it a whirl.

One of the newer additions is a savestate mechanism; this is a very lazy bit of code on my behalf as all it does currently is serialise the entire emulator to a file using the BinaryFormatter. This resulted in savestates weighing in at about 6MB; by marking certain private fields (such as look-up tables in the Z80 emulator) as [NonSerialized] it was down to 2MB. To squash it down to the current ~250KB size the savestate is compressed using the zip file classes I've written to handle loading ROMs from zips.

Whilst this is going to change soon (I'm currently working this on an simple INI file serialiser, so the savestate files will be compatible with later releases of the software) I decided to experiment with the idea of dumping extra data into the savestate - namely, a screenshot.

SavedStateThumbnails.jpg

The screenshot is simply saved as Screenshot.png in the root of the savestate's zip archive. Creating a thumbnailer is extremely easy under Vista, and as the thumbnailer runs out-of-process you can use .NET code! Here's a quick and dirty run-down of how to make them if you decide to write one yourself.



Setting up the project

Create a new class library project in Visual Studio, then go switch to its project properties editor. On the Application tab, set Target Framework to something sensible (I currently try and keep everything at .NET 2.0 level), then click on the Assembly Information button and tick the Make assembly COM-Visible box.

Finally, move to the Signing tab, and tick the box marked Sign the assembly. From the drop-down box, pick New, which will create a new key file and add it to the project (this is required later for COM registration).

Add the COM interface wrappers

This is a simple copy and paste job! Just bung this in a source file somewhere:

using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;

namespace Thumbnailer {

	/// <summary>
	/// Defines the format of a bitmap returned by an <see cref="IThumbnailProvider"/>.
	/// </summary>
	public enum WTS_ALPHATYPE {
		/// <summary>
		/// The bitmap is an unknown format. The Shell tries nonetheless to detect whether the image has an alpha channel.
		/// </summary>
		WTSAT_UNKNOWN = 0,
		/// <summary>
		/// The bitmap is an RGB image without alpha. The alpha channel is invalid and the Shell ignores it.
		/// </summary>
		WTSAT_RGB = 1,
		/// <summary>
		/// The bitmap is an ARGB image with a valid alpha channel.
		/// </summary>
		WTSAT_ARGB = 2,
	}

	/// <summary>
	/// Exposes a method for getting a thumbnail image.
	/// </summary>
	[ComVisible(true), Guid("e357fccd-a995-4576-b01f-234630154e96"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
	public interface IThumbnailProvider {
		/// <summary>
		/// Retrieves a thumbnail image and alpha type. 
		/// </summary>
		/// <param name="cx">The maximum thumbnail size, in pixels. The Shell draws the returned bitmap at this size or smaller. The returned bitmap should fit into a square of width and height <paramref name="cx"/>, though it does not need to be a square image. The Shell scales the bitmap to render at lower sizes. For example, if the image has a 6:4 aspect ratio, then the returned bitmap should also have a 6:4 aspect ratio.</param>
		/// <param name="hBitmap">When this method returns, contains a pointer to the thumbnail image handle. The image must be a device-independent bitmap (DIB) section and 32 bits per pixel. The Shell scales down the bitmap if its width or height is larger than the size specified by cx. The Shell always respects the aspect ratio and never scales a bitmap larger than its original size.</param>
		/// <param name="bitmapType">Specifies the format of the output bitmap.</param>
		void GetThumbnail(int cx, out IntPtr hBitmap, out WTS_ALPHATYPE bitmapType);
	}

	/// <summary>
	/// Provides a method used to initialize a handler, such as a property handler, thumbnail provider, or preview handler, with a file stream.
	/// </summary>
	[ComVisible(true), Guid("b824b49d-22ac-4161-ac8a-9916e8fa3f7f"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
	public interface IInitializeWithStream {
		/// <summary>
		/// Initializes a handler with a file stream.
		/// </summary>
		/// <param name="stream">Pointer to an <see cref="IStream"/> interface that represents the file stream source.</param>
		/// <param name="grfMode">Indicates the access mode for <paramref name="stream"/>.</param>
		void Initialize(IStream stream, int grfMode);
	}

}

(You may wish to set the namespace to something more appropriate). As you can see, most of that source file is documentation.

Create your thumbnailer class

First thing you'll need to do here is to generate a GUID for your thumbnailer; this is so that when you register your thumbnailer Windows will know which COM object to create an instance of which it can then call to generate a thumbnail (the GUID of your thumbnailer is attached to the extension of the file via standard file associations - more on that later).

Your thumbnailer class should implement two interfaces; IThumbnailProvider (obviously!) and IInitializeWithStream. Here's a skeleton class for the thumbnailer:

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;

namespace Thumbnailer {
	
	[ComVisible(true), ClassInterface(ClassInterfaceType.None)]
	[ProgId("YourApp.ThumbnailProvider"), Guid("YOUR-GUID-IN-HERE")]
	public class ThumbnailProvider : IThumbnailProvider, IInitializeWithStream {

		#region IInitializeWithStream

		private IStream BaseStream { get; set; }

		public void Initialize(IStream stream, int grfMode) {
			this.BaseStream = stream;
		}

		#endregion
		
		#region IThumbnailProvider

		public void GetThumbnail(int cx, out IntPtr hBitmap, out WTS_ALPHATYPE bitmapType) {

			hBitmap = IntPtr.Zero;
			bitmapType = WTS_ALPHATYPE.WTSAT_UNKNOWN;
			
			try {
			
				// Thumbnailer code in here...
			
			} catch { } // A dirty cop-out.

		}
		
		#endregion
	}
}

You will probably want to set the ProgId to something meaningful, and make sure you set the GUID to the one you just generated.

What will happen is that Windows will first initialise your object by calling IInitializeWithStream.Initialize(), passing in an IStream. The above implementation stores the IStream in a member property for future reference.

Windows will then call IThumbnailProvider.GetThumbnail(). cx is the maximum size of the thumbnail (width and height) you should return; Windows will scale your thumbnail down if you return one that is too large. Do not scale your thumbnail up to match this value; it is perfectly valid to return one that is smaller than the requested value. Also; do not scale your thumbnail up to a square: you should return it at the same aspect ratio of the source image.

For the moment, and for the sake of testing, here's a snippet that will create a bright red thumbnail using GDI+:

using (var Thumbnail = new Bitmap(cx, cx)) {
	using (var G = Graphics.FromImage(Thumbnail)) {
		G.Clear(Color.Red);
	}
	hBitmap = Thumbnail.GetHbitmap();
}

Registration

If you compile your class library at this point you should end up with a single DLL. You need to register this DLL using the command-line tool RegAsm.exe that comes with the .NET framework.

Open an elevated command prompt (you need admin rights for this bit) and set the working directory to the output directory of your DLL. Now, invoke the following command:

%windir%\Microsoft.NET\Framework\v2.0.50727\RegAsm /codebase YourThumbnailer.dll
That's half of the battle; the last bit boils down to conventional file associations.

Run the registry editor, and open the HKEY_CLASSES_ROOT key. You will see a list of keys representing file extensions; find one (or create a new one) to match the extension that you wish to attach your thumbnailer to. Under that create a new key named shellex, and under that create another key named {e357fccd-a995-4576-b01f-234630154e96}. Set its (Default) value to {YOUR-GUID-IN-HERE} - yes, the GUID you created earlier. That should look something like this:

  • HKEY_CLASSES_ROOT
    • .yourextension
      • shellex
        • {e357fccd-a995-4576-b01f-234630154e96} = {YOUR-GUID-IN-HERE}

That's it! smile.gif You may need to log out then in again (and/or reboot and/or just kill all Explorer instances and restart them) for Explorer to catch on if nothing seems to be working.

A final note: IStream to Stream

The only final hitch is that IStream is not the same as our beloved .NET Stream. I use the following snippet to dump all of the contents of an IStream into an array of bytes (which can then be converted to a stream using new MemoryStream(byte[]) if need be).

private byte[] GetStreamContents() {

	if (this.BaseStream == null) return null;

	System.Runtime.InteropServices.ComTypes.STATSTG statData;
	this.BaseStream.Stat(out statData, 1);

	byte[] Result = new byte[statData.cbSize];

	IntPtr P = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(UInt64)));
	try {
		this.BaseStream.Read(Result, Result.Length, P);
	} finally {
		Marshal.FreeCoTaskMem(P);
	}
	return Result;
}

This, naturally, is not a good idea if you're thumbnailing very large files, as it dumps the entire thing into memory!

For more information, take a look at MSDN: Thumbnail Providers, which includes useful information (including how to change the overlay icon in the bottom-right of your thumbnails or the adornments).

The COM wrappers and GetStreamContents() snippet are based on this uberdemo article.


PhantasyStarTranslation.png

Finally, another screenshot; you can now load IPS patch files directly into Cogwheel using the advanced ROM load dialog - which can be useful for translations.

Sound, at long last.

Friday, 7th March 2008

I have finally got around to adding sound to Cogwheel using Ianier Munoz's waveOut API wrapper.

RistarSoundTest.png

The technique used is fairly simple. I start with a sound buffer that is a multiple a number of video frames in length (1/60th of a second is one frame) - four seems a good number. This buffer needs to be periodically topped up with sound samples (every four frames in the above example).

I run the emulator for one frame, then generate a frame's worth of audio. I add these samples to a queue. The sound callback then periodically dequeues these samples and appends them to its buffer.

// This is called once every video frame.
// 735 samples at 44100Hz = 1/60th second.
// (Multiplied by two for stereo).
this.Emulator.RunFrame();
short[] Buffer = new short[735 * 2];
this.Emulator.Sound.CreateSamples(Buffer);
this.GeneratedSoundSamples.Enqueue(Buffer);

The important thing is that the sound is always generated after the video frame (and thus after any hardware writes). I log writes to the sound hardware over the period of a frame (along with the number of CPU cycles that have elapsed), then space them out when generating the sound samples so that they play in synch. My previous problems were caused by the sound emulation trying to "look ahead" past what had already been generated.

However, there is a potential problem with this - as the video and sound emulation are not locked in synch with eachother, there are two cases that could crop up:

  1. The emulator runs faster than 60Hz, generating too many sound samples.
  2. The emulator runs slower than 60Hz, not generating enough sound samples.

The first is the easiest to deal with. In most instances you'd want a couple of extra frames of sound data left in the queue after topping up the sound buffer, in case in the next period not enough are generated. However, if I notice that the queue is longer than entire sound buffer after topping it up, I clear it completely. This would make the sound a little choppy, but so far this hasn't happened in my tests.

The latter is a little more complex. If I just left it the sound buffer would have gaps in it, causing noticable pops (this I have noticed in some of the more processor-intensive games). To cover up the gaps, I generate enough extra frames of sound data to fill the gap. As no sound hardware writes are made, this has the effect of extending any tones that were currently playing, so the sound will play back slightly out of time. However, slightly out of time by a few 60ths of a second is a better solution than a pop.

// This is called when the sound buffer needs topping up.
// That's about once every four frames.
private void SoundBufferFiller(IntPtr data, int size) {

	// Temporary buffer to store the generated samples.
	short[] Generated = new short[size / 2];

	for (int i = 0; i < Generated.Length; i += 735 * 2) {
		if (this.GeneratedSoundSamples.Count > 0) {
			// We've already queued up some sound samples.
			Array.Copy(this.GeneratedSoundSamples.Dequeue(), 0, Generated, i, 735 * 2);
		} else {
			// Erk, we're out of samples... force generate some more and use those instead.
			// (This avoids popping).
			short[] Temp = new short[735 * 2];
			this.Emulator.Sound.CreateSamples(Temp);
			Array.Copy(Temp, 0, Generated, i, 735 * 2);
		}
	}

	// Copy to the sound buffer.
	Marshal.Copy(Generated, 0, data, size / 2);
	
	// If too many samples are being generated (FPS > 60Hz) then make sure it doesn't go out of control.
	while (this.GeneratedSoundSamples.Count > this.SoundBufferSizeInFrames) this.GeneratedSoundSamples.Dequeue();

}

Joysticks and Game Genie codes

Monday, 3rd March 2008

The biggest update to the emulator relates to input.

ConfigureControls.png

I've added a new control panel that lets you customise key bindings. For the keyboard, you simply click on the button you wish to customise, then press the key on your keyboard you wish to bind. As you can probably tell from the screenshot, I've also added joystick support (via the Multimedia Joystick Functions with a bit of P/Invoke for ease) which means that with a simple adapter and a free driver you can use original SMS pads with the emulator.

I haven't added support for the POV hat, which needs doing as the PPJoy PlayStation controller driver exposes the d-pad as a POV hat.

GameGenieEditor.png

I've also added an interface for adding Game Genie codes. The Game Genie was a cheating device that could be used to patch memory.

I have also started reintroducing (albeit in a slightly buggy fashion) multiple memory devices. Previously I've just been emulating a cartridge ROM and work RAM; the real consoles had multiple memory devices including a card slot, ROM BIOS and expansion slots. By emulating this system one can boot with the original BIOS, and (for example) not insert a cartridge to watch the BIOS animation or play its integrated game.

For ease, there's a quick-load ROM menu option that will load anything into the cartridge slot and disable all others for quick game playing. For those who wish to play with the hardware more, there's an "advanced load" dialog that will let you pick what goes in which slot, and also force various options (such as locale, hardware model, video system and so on) instead of the automatically guessed options. I'd also like it to be able to pick patch files (such as translations) so you don't need to use an external tool. Currently this dialog only supports cartridge ROM and BIOS ROM loading, though.

Cogwheel on Google Code

Sunday, 24th February 2008

On bakery2k1's suggestion, I took a look at my sprite collision flag code. It only checked for collisions between sprites appearing in the foreground - sprites that were hidden behind a background tile weren't checked, which caused the Fantastic Dizzy bug.

FantasticDizzyFixedSpriteCollision.png

I have decided to give Google's free project hosting a whirl. As usual, they can't get it working in Opera flaming.gif (no big surprises there) and it's horrifically slow, but I can't really complain given that it's free, and the issue tracking is pretty handy.

Gauntlet.png

Another game that has been fixed is the conversion of that arcade classic, Gauntlet. This was partially due to buggy controller port emulation, but a long-standing bug has been the lack of the y-scrolling inhibition flag. Games could set a flag on the VDP that would prevent the rightmost 64 pixels from scrolling vertically - this is useful to create a locked status bar on the right for a vertical scrolling game, for example.

SmsVdpTest1.png SmsVdpTest2.png

I'm slowly getting there with FluBBa's VDP tester...

/INTerminable Interrupt Problems

Friday, 22nd February 2008

WCLBGolf1.png WCLBGolf2.png
World Class Leaderboard Golf

Well, I've just about got it the way it was before with the new interrupt code; most games work again, but the few that have always given problems (such as Desert Speedtrap) still don't work properly. sad.gif I think this is the stage where I have to start trawling through disassemblies to try and work out why they're not working.

CosmicSpacehead.png
Cosmic Spacehead - one of the few games to use the 256×224 mode.

One problem I still haven't got to the bottom of is with the Dizzy games. They either reset when starting a new game or lock up. I'm hoping this is a problem with my implementation of the Codemasters mapper. I guess that The Excellent Dizzy Collection has a simple front-end game selection screen that switches to the requisite ROM page for the selected game, then jumps to the start of that page - in my case this jumps back to the initial Codemasters screen.

I'm not sure where Fantastic Dizzy's problems originate. It looks like an interrupt problem (maybe the palette should be switched when the VDP has finished with the status bar at the top?), but could also be related to the other Codemasters problems.

Interestingly, Micro Machines (1 and 2) and Cosmic Spacehead - both using the Codemasters ROM mapper - seem to work fine.

Interrupts: A Fresh Start

Thursday, 21st February 2008

I gave in and rewrote all of the Z80's interrupt emulation from scratch, finding some rather horrible bugs in the existing implementation as I went.

Some of the highlights included non-maskable interrupts ignoring the state of the IFF1 flag (this flag is automatically cleared when an interrupt is serviced, and is used to prevent the interrupt handler from being called again before it has finished) and the RETN instruction not copying the state of the IFF2 flag back to IFF1. When non-maskable interrupts are serviced, the state of IFF1 is copied to IFF2 before it gets cleared, the idea being that if you use RETN interrupts are automatically re-enabled on exit of the NMI ISR. (Contrast this with maskable interrupts, where both flags are cleared, and you need an explicit EI to re-enable them).

The HALT instruction (executes NOPs until an interrupt is requested or the CPU is reset) was also completely incorrectly (and bizarrely) implemented. The rewrite just sets a Halted property, which prevents the CPU from fetching or executing any instructions. The interrupt-triggering code simply resets this property.

This has fixed numerous bugs (I'm not sure when they were introduced, as it was all working a while back). It's gone from "not working at all" to "just about working", but some games or demos that rely on precise interrupt timing don't work properly.

HicolorDemoCorruption.png DesertSpeedtrapMisaligned.png
Game Gear Hicolor Demo and Desert Speedtrap

Both problems in the above screenshots relate to line-based interrupts from the VDP (Video Display Processor). Some other games simply hang at startup. sad.gif

Loading ROMs

Wednesday, 20th February 2008

OutRunEuropa1.png OutRunEuropa3.png
OutRun Europa

I've been adding a series of new features to Cogwheel. I wrote a basic zip file class a while back (only supporting store and deflate compression - the majority of zip files use deflate), and have added a Utility namespace with methods for help with loading ROMs.

For example, there is a method that is passed a filename by reference, and it'll return an array of bytes of the loaded file. If the passed filename was a zip file, by any chance, it'll search for ROMs inside the zip, and modify the filename (so it might end up as C:\Path\SomeFile.zip\Game.sms).

Once this is done, the CRC-32 checksum of the file is calculated then looked up against a database of known ROM dumps. I'm currently using the SMS Checker .romdata files as the database source. These contain information about known bad dumps, and can be used to strip headers and footers, patch bytes and remove redundant overdump data to end up with a valid ROM image.

Finally, the mapper is detected. Currently, I have three mappers - RAM (which is just 64KB of RAM), Standard (the standard Sega Master System ROM mapper) and Codemasters. I use the RAM mapper for all SG-1000 and SC-3000 games (I'm not sure what they're meant to use, here, but some games - such as The Castle - don't work with the standard SMS mapper).

The Codemasters mapper uses a different method to swap pages, which means that the conventional SMS BIOS checksumming code fails to swap in pages correctly and thus doesn't check the entire cartridge. The games therefore have their own checksumming routines and store their own checksum in a Codemasters-specific header; this makes detecting Codemasters games fairly easy, as all you need to do is check for the extra Codemasters checksum.

As the BIOS doesn't check the checksum, the cartridge provides code to do so itself. You can run it by holding 1 and 2 down as the game boots.

ExcellentDizzyCollectionChecksum.png
The Excellent Dizzy Collection
MicroMachinesChecksum.png
Micro Machines

I seem to have broken interrupts somewhere along the line; most noticable are non-maskable interrupts (as generated by the Pause button) which crash the emulator (it appears that once they fire, interrupts are never re-enabled). I'm not sure what's causing this, but hopefully it won't be too unpleasant to fix!

Game Gear LCD Scaling

Thursday, 14th February 2008

The Game Gear's hardware is very similar indeed to the Master System's - so similar that you can play Master System games on a Game Gear via a special adapter. Some Game Gear games were just the Master System ROMs in a Game Gear cartridge. smile.gif

That said, the Game Gear's LCD is only 160×144, and the Master System has a resolution of 256×192 (or 256×224 or 256×240, but those modes were very rarely used). In Game Gear mode, this resolution is simply cropped. In Master System mode something more interesting has to be done to scale this down to fit the LCD.

I won't bore you with the details here, but will refer you to a post here on the subject. Using the research, I added a mode to the VDP emulator that would mimic this scaling.

pop_comparison.jpg

not_only_words_comparison.jpg

This is old news, of course. I have nothing especially new to report, but I have pretty much entirely rewritten the emulator now (apart from the Z80 emulator, which was fairly well designed). The code was absolutely horrible, and so I've redesigned it to be a lot more flexible and intuitive to use as a library. I've rewritten the standard memory mapper, I/O mapping, and VDP (though I did copy the rasterisation and timing stuff from the old VDP code and cleaned it a little); there's a lot more to do (so far I haven't even reintroduced joypad input) but at least it'll be nicer for me to work with. smile.gif

In the meantime, here are some ugly screenshots of what Master System games look like on the Game Gear LCD.

RoadRashMasterGear.png

BlackBelt.png

PrinceOfPersia.png

RoboCop.png

Necromancy

Friday, 8th February 2008

After seeing Scet and Drilian's work on their respective emulator projects I decided I needed to do something with the stagnating Cogwheel source on my hard disk drive.

The only ROM I have tested where I can't find an explanation for a bug is the Game Gear Garfield: Caught in the Act game. Like many games, when left at the title screen it'll run a demo loop of the game in action. At one point Garfield would walk to the left of the screen, jump over a totem pole, shunt it to the right and use it as a way to jump out of a pit. However, in Cogwheel he would not jump far enough to the left, and not clearing the totem pole he'd just walk back to the right and not have anything to jump out of the pit on.

I remembered a post on MaxCoderz discussing a long-standing tradition of thinking that when a JP <condition>, <address> failed the instruction took a single clock cycle. You can see this misreported here, for example. This document, on the other hand, claims it always takes 10 clock cycles - and most importantly of all, the official user manual backs this up.

Garfield.png

So, Garfield can now get out of his pit. The user interface has changed (again) - I'm now using SlimDX to dump pixels to a Panel, which seems to be the least hassle distribution-wise and doesn't throw LoaderLockExceptions.

Sega Tween

Thursday, 1st February 2007

No updates for a while, I'm afraid - things have been pretty hectic.

sega_tween_3d_stereo_pair.png

I packaged up and released the Sega Tween demo I'd been working on. As you can see, I added an SMS and a 3D mode - this works with the SMS 3D glasses. The extra 3D is quite cheap to calculate - shift the rotated X coordinates one way for one eye, then the other way for the other eye. After projection to the screen they need to be shifted back a little way to re-centre, but it works quite well.

sega_tween_3d_anaglyph.png

However, I had neglected the fact that the SMS1 (which has the card slot, and hence the model that supports the 3D glasses) had a bug in the VDP and as such only supports four zoomed sprites per scanline. I added this glitch to the emulator;


In other news, I've done a small amount of work on Brass. It's quite embarrassing, really, how slow the old version is. Assembling this file:

.rept 9000
	ld a,1
.unsquish
	ld a,2
.squish
	ret
.loop

...produces this in old Brass:

Brass Z80 Assembler 1.0.4.9 - Ben Ryves 2005-2006
-------------------------------------------------
Assembling...
Pass 1 complete. (2093ms).
Pass 2 complete. (22062ms).
Writing output file...
Errors: 0, Warnings: 0.
Done!

Nearly half a minute! New Brass does a much better job of syntax parsing and caching...

Brass Assembler - Copyright © Bee Development 2005-2007
-------------------------------------------------------
ZiLOG Z80 - Copyright © Bee Development 2005-2006
TI Program Files - Copyright © Bee Development 2005-2006
Core Plugins - Copyright © Bee Development 2005-2006

Parsing source...
Building...
Writing output...
Time taken: 484.38ms.
Done!

Down to just under half a second. That's almost a 50× speed increase!

Parallel-Port SMS Control Pad

Monday, 15th January 2007

I've been wanting to attach an SMS control pad to my PC (and be able to use it to play games with) for a while, so put in an order from those excellent chaps at Rapid for the parts needed.

The joypad (as I've now learned from disassembly) is very primitive - 6 normally-open switches, each connected between a pin on the DE-9 connector and ground. The accepted layout adapter uses the 25-pin parallel port, connecting ground to pin 18, power to pin 1 (not that the control pad uses this pin) and 7 further connections from D0 to D6 for the buttons.

sms_pad.jpg
Master System Control Pad and a poorly-soldered DB-25 to DE-9 adapter.

I had been assured that the data lines on parallel ports (D0..D7) were pulled up, and so the layout seemed easy enough - D0..D6 will return highs normally, and when a button is pressed it is connected to ground.

Unfortunately, for whatever reason the data lines on the parallel port on my PC are not pulled up, at least not in any way that I can find to control. However, if you set the lines to be outputs (using bit 5 of the control register), set them all high, then flip them to inputs, they'll read as highs for a while until they float (slowly) back low again. I've used this to my advantage, and so have this:

/// <summary>Flags corresponding to which buttons are pressed.</summary>
[Flags]
public enum Buttons {
    None = 0x00,
    Up = 0x01,
    Down = 0x02,
    Left = 0x04,
    Right = 0x08,
    Button1 = 0x10,
    Button2 = 0x20,
    All = 0x3F,
}   

// Retrieve the status of the port.
private Buttons GetRawStatus() {
    // Set D0..D7 as outputs.
    Output(this.BaseAddress + 2, 0x00);
    // Set them high:
    Output(this.BaseAddress + 0, 0xFF);
    // Set D0..D7 as inputs.
    Output(this.BaseAddress + 2, 0x20);
    // Retrieve, invert and mask the data lines.
    return (Buttons)(~(Input(this.BaseAddress + 0)) & (int)Buttons.All);
}

This works very well, with one small problem: nothing is debounced, so pressing any button causes 10 or so press/release actions to be detected until the contacts settle. Therefore, the exposed method for retrieving the status is this:

/// <summary>Gets the status of the buttons from the connected SMS joypad.</summary>
/// <returns>The status of the buttons.</returns>
public Buttons GetStatus() {
    if (!this.Debounced) {
        return GetRawStatus();
    } else {
        Buttons Last = GetRawStatus();
        Buttons Current;
        int MaximumIterations = 100;
        while (((Current = GetRawStatus()) != Last) && (MaximumIterations-- > 0)) {
            Last = Current;
            Thread.Sleep(0);
        }
        return Current;
    }
}

For some strange reason, this doesn't quite work; after a while (or rebooting, or reading/writing the EPP registers) the port starts reading nothing but zeroes again. Running another piece of software that uses the parallel port fixes it.

One missing feature of the emulator was support for the SMS pause button. This button is attached to the Z80's non-maskable interrupt line, so pressing it results in the CPU pushing the program counter to the stack then jumping to $0066.

For most games the pause button just pauses the game, but for some others it will display a menu - such as in Psycho Fox, which lets you use the items you have collected to change animal or use a power-up.

psycho_fox_menu.png
Psycho Fox's in-game menu

One major long-standing bug in the emulator has been interrupt handling by the CPU. I think I've (finally!) got it, though it's still not entirely perfect. How I've set it up now is that a flag is set - IntPending or NmiPending, depending on whether the maskable or non-maskable interrupt pin has been modified - when the interrupt is requested, and cleared when it's been handled.

japanese_bios_1.gif
japanese_bios_2.gif
Japanese Master System BIOS

I have updated the memory emulation to better support BIOS ROMs. Initially, the "Majesco" Game Gear BIOS and some of the "Snail Maze" SMS BIOS worked (though the SMS BIOS would display "Software Error" on several games). I've tested a few of them and they seem to work pretty well.

hang_on_safari_hunt.png
Hang On and Safari Hunt

Whilst the Japanese BIOS has (in my opinion) the best final effect, it's the M404 prototype BIOS that has the best effect overall:

prototype_bios.gif

Sega BASIC

Monday, 8th January 2007

mdi.png

I have returned to the MDI view for this project which makes life easier when it comes to putting in debugging tools. For the moment all there is is a palette and tile viewer, but I hope to add some helpful utilities - and work out a way to tie the new Brass and an emulator together for debugging assembly programs.

I've extended the SG-1000 emulation some way towards the SC-3000 (Sega Game 1000 vs. Sega Computer 3000) which has mainly involved adding keyboard emulation. The result is the ability to run Sega BASIC, and so you get the obligatory program that everyone writes sooner or later...

obligatory.png

There is no tape drive emulation, and I'd like to ideally emulate the SF-7000's floppy drive and disk BASIC, but don't quite follow the datasheets I've read thus far (or rather, how everything is tied together).

In the first screenshot you might have noticed that the video output window has "Altered Beast (h) [A]" in the caption. I've added ROM library support, and am currently using the data from the .romdata files arranged by Maxim for his SMS Checker utility. As well as identify a name, there is a more important reason to use the database - it can identify dodgy dumps and be used to correct them (in this case, Altered Beast has an additional header - hence the (h) - that needs to be removed).

SG-1000

Wednesday, 3rd January 2007

I've added some support for the SG-1000, Sega's first? home video game console.

sg_1000_sega.gif

The Master System's VDP is a modified TMS9918, and so most Master System games run in its extended 'Mode 4' setting. That was the only video mode, therefore, that I'd emulated in any form.

For some reason, the older computer games are, the more charm they seem to have to me (maybe because the first games I played would have been on a BBC Micro, which certainly looked a lot more primitive than the Master System games I've been attempting to emulate thus far). I dug out TI's TMS9918 documentation - the differences are quite significant! Tiles are monochrome (though you can pick which foreground and background colour is in use - to some extent), the palette is fixed to 16 pre-defined colours (one of which being 'transparent') and sprite sizes and collisions are handled differently. Various features found in the Master System's VDP (scrolling, line-based interrupts) also appear to be missing from the 'vanilla' TMS9918, but I'm not sure whether or not they make an appearance in the SMS variation of the VDP or not, along with the original TMS9918 limitations.

flipper.png
Flipper

At any rate, the emulator now has a 'SG-1000' mode. The only differences at the moment are that the TMS9918 palette is used and line interrupts are disabled, so you can still (for example) use mode 4 on it.

drol.png choplifter.png hustle_chummy.png championship_loderunner.png space_invaders.png hero.png elevator_action.png zaxxon.png

From first to last: Drol, Choplifter, Hustle Chummy, Championship LodeRunner, Space Invaders, H.E.R.O., Elevator Action, and Zaxxon.

All but one of the SG-1000 games I had ran - and that was The Castle. According to meka.nam, this has an extra 8KB of onboard RAM. Whilst doing some research into the SG-1000 and the TMS9918, I found a forum post by Maxim stating "The Castle runs nicely on my RAM cart :)". Enter the new ROM mapper option to complement Standard, Codemasters and Korean - it's the RAM mapper, which simply represents the entire Z80 address range with a 64KB RAM.

the_castle_1.png the_castle_2.png

That seems to have done the trick!

I mentioned that the palette was different in the SMS VDP and the original TMS9918 - here's an example (SMS on the left, SG-1000 on the right):

music_station_sms.png music_station_sg_1000.png

I'm assuming this is the result of truncating the TMS9918 palette to the SMS 6-bit palette without needing to implement two palette modes on the new VDP. Another comparison is between two versions of Wonder Boy - SMS on the left again, SG-1000 on the right:

wonder_boy_sms.png wonder_boy_sg_1000.png

VDP Interrupts

Wednesday, 20th December 2006

road_rash_2.png

The VDP can generate two different types of CPU interrupt.

The first, and easiest, is the frame interrupt, which is requested when an entire frame has been generated. This is requested, therefore, at a regular 60Hz in NTSC regions and 50Hz in PAL regions - it's a useful timer to synchronise your game to.

The second, and more complex, is the line interrupt. This interrupt is requested when a user-definable number of scanlines have been displayed. An internal counter is decremented each active line (and one more just after), and when it overflows it resets to the value held in a VDP register and requests the interrupt (so 0 would request an interrupt every line, 1 every other line and so on). For every other line outside the active display area, the counter is reset to the contents of the VDP register.

Both interrupt types can be enabled or disabled by defined bits held in the VDP registers.

(The above should be loosely correct, the below is a little more uncertain).

Once an interrupt is requested, a flag for said interrupt is set. The flag is not reset until the VDP control port is read, so you must read the VDP control port if you expect any further interrupts.

To differentiate between line and frame interrupts you can check the value read from the control port. If the most significant bit is set, a frame interrupt (at least) was requested. Reading the vertical counter port (which returns the current scanline's vertical position) will also let you know where you are.

Something is a little wonky with my vertical counting code, as all lines end up being one too large. For the moment I'm subtracting one before returning the value (and waiting one extra scanline before triggering the frame interrupt) which is a horrible solution, but for the moment it has fixed a number of games that weren't working at all before.

earthworm_1.gif earthworm_2.gif

Earthworm Jim, which relies on line interrupts to switch on zoomed sprites to dipslay the status bar at the bottom, now plays. It's missing some graphics on the title screens, though.

For some reason, rebuilding my Z80 emulator (which is in a different project) fixed some other interrupt-related glitches, so I have a sneaking suspicion most of my earlier problems are related to using an out-of-date DLL.

road_rash_1.gif road_rash_3.png road_rash_4.png

Road Rash highlighted another bug. The VDP can draw doubled sprites - that is, when a particular bit is set it will draw sprites as 8x16 pixels, stacking two consecutive sprite tiles on top of each other. Road Rash uses this mode, but also uses odd sprite indices (odd as opposed to even, not strange). The VDP will only take even indices, so a line of code to clear the least significant bit if using doubled sprites fixed that.

Still no sound, though.

Real 3D on classic 2D hardware

Monday, 18th December 2006

My existing implementation of the standard mapper used the values $1FFC..$1FFF in RAM as the paging registers. This is incorrect; from what I can now tell the paging registers are only updated if you write to the $FFFC..$FFFF range, and isn't anything to do with the RAM anyway (the fact that they end up in RAM is a side-effect, not the main way of doing things).

Updating my code to handle this, rather than using the values in RAM, fixed some games; notably Phantasy Star and Space Harrier.

ps_1.gif ps_2.gif ps_3.gif

Thanks to Maxim's post, I fixed the Codemasters mapping for the Excellent Dizzy Collection:

dizzy.gif

A fun bit of hardware is the 3D glasses. These LCD shutter glasses could be used with certain games, and are controlled by writing to the memory range $FFF8..$FFFB. The least-significant bit controls which shutter is open and which is shut, and by alternating frames rapidly in time with the shutters you can display two views; one for the left eye, one for the right.

I've added four different 3D glasses mode. No effect doesn't do anything special, so you just get an unsightly flickery view. Frozen displays frames for one eye only, giving you a 2D view of the 3D game.

To recreate the 3D view on your PC, though, there are two methods. The first method is the standard red-green anaglyph view, for use with those red-green glasses:

anaglyph_1.gif anaglyph_2.gif

Unfortunately, on top of the usual shimmery view you get from anaglyphs, this is made even worse by the fact that I don't remove the existing colour information from the frames. This looks nicer without glasses, but looks really quite horrible when viewed (as intended) through the glasses. For example, the red text is invisible through the green filter, but strongly visible through the red filter, making it shimmer.

anaglyph_grey_1.gif anaglyph_grey_2.gif

By sacrificing the colour and only taking the luminance of the orignal frames, you get an anaglyph that remains stable when viewed.

Fortunately, there is an easy way to retain full colour information in a 3D image, which is to use a stereo pair.

poseiden.gif

The image on the left is to be viewed by your right eye, and the one on the right by your left eye. If you cross your eyes until the two images overlap, and concentrate on the middle one, you should be able to focus on a 3D image.

space_harrier.gif

maze_1.gif

maze_2.gif

I tried adding sound emulation back in, but the timing has confused me once more. If you can help, I've posted the relevant thread here.

Turning Japanese

Friday, 15th December 2006

dizzy.gif

The Z80 CPU uses 16-bit addressing, which limits it to a 64KB address space. As well as fitting the program ROM into this space, we need to fit in the machine's 8KB RAM, limiting us even further.

To get around this limitation, the memory is broken down into a series of windows ("frames"), and you can change what is visible in some of these windows.

The memory range $C000..$DFFF can be used to address the 8KB RAM. This is mirrored from $E000..$FFFF; that is, reads and writes to $E000 work as if you were reading and writing to $C000.

There are three other windows; from $0000..$3FFF, $4000..$7FFF and $8000..$BFFF. By changing the contents of RAM (addresses $FFFC..$FFFF) you can adjust what is accessible from these memory ranges.

The lower 1KB cannot be paged out - this is because when the device boots the contents of RAM (and thus paging registers) are undefined, and you need something fixed in place to set up the machine correctly.

I had already implemented most of the above, with two omissions. The first was that certain cartridges contained their own additional RAM chips (accessible by setting flag bits in paging register $FFFC) that could be used as saved game area. Ys appears to use this area, and so didn't work without it. That was the garbled screenshot I posted earlier:

ys_1.gif ys_2.gif

The Flash now works too.

flash_1.gif flash_2.gif

The second omission was that Codemasters games use a different mapping system to the standard one. Fortunately it is significantly simpler; the 32KB from $0000..$7FFF is locked, and the 16KB from $8000..$BFFF is offset based on a value previously written to address $8000. ($C000..$FFFF is 8KB work RAM as usual).

codemasters.gif

The Excellent Dizzy Collection as seen at the top of this post doesn't entirely work; when you pick a game it resets. Some other emulators do this too. The Game Gear games Cosmic Spacehead and Micro Machines (1 and 2) work well, though.

cosmic.gif mm1.gif mm2.gif

After some fiddling around with the VDP emulation and interrupts, some more games work (but I'm not sure why they now work):

desert_speedtrap.gif asterix.gif indy.gif

Other parts of the emulator have been improved. It can now be set to either domestic (Japanese) or export (everyone else) modes, which affects some games in a variety of ways - from translated text, to removing the Mark III bumper screen, to changing the title of the game.

psychic_jp.gif psychic_en.gif

For Game Gear games, detecting whether it's a Japanese machine or not is simple - test the 6th bit returned when you read port $00. If it's set, you've got an export machine.

However, port $00 is Game Gear-specific - the Master System hardware has a peculiarity that is used to detect region. There are two ports for controllers, and each controller has up, down, left, right, TL, TR and TH lines. You can set the TR and TH lines to be inputs or outputs (and the output level if configured as output) by writing to the I/O control port. If you set them to outputs and try and read from them, on export hardware you get the values you are telling them to output. On Japanese machines, however, they return 0s regardless of the chosen output level.

zillion_jp.gif zillion_en.gif

A more fun addition is Game Genie support. A simple call to Emulator.AddGameGenieCode("3A0-21C-2A2"); and Sonic can stand here all day:

game_genie.gif

Of course, the technical information above is my interpretation of what I've gathered so far from various documents from far brainier chaps than I, and the fact that all I have is a broken emulator indicates that it is probably complete rubbish. smile.gif

Kids, just say No to bilinear filtering

Monday, 11th December 2006

ristar_1.gif

The old layout of the emulator was a bit of a mess.

The Z80 emulator needed to sit inside a class of your design that inherited from IHardwareController (and so exposed methods to handle hardware devices) and another that inherited from IMemoryDevice that exposed methods that could be used to read and write to memory.

As you might imagine, this got a little messy what with the various nested classes needing to pass references to eachother around. The new design is much more straightforwards - you build a class that inherits from Z80A (which is the basic Z80 emulator) and override the four functions for reading from/writing to memory addresses/hardware ports.

ristar_2.gif

Further to that, the Master System emulator itself has been shunted out to its own class library, meaning that the code isn't mixed up with interface code. This way I can easily have two different front-ends - an MDI Windows GUI for example, like the one I've posted screenshots of before, with debugging tools and so on, and an XNA interface that runs full-screen, taking input from a connected joypad.

alex_kidd.gif

For the moment I have the blurry and slow solution which is just to dump the output Bitmap onto a form. You can probably guess as much from the pathetic framerates reported on the above screenshots.

alien_syndrome.gif

Overall compatibility is still increasing slowly. Some programs that used to work now don't, of course. It goes both ways.

The VDP emulation has been significantly rewritten. A lot of the main scanline rasteriser is still the same as it was, though, but slight fixes have been made there (including picking the correct backdrop colour - an x & 0xF + y versus (x & 0xF) + y bug - and sorting out the second column) but I still have a lot of interrupt-related bugs to iron out.

tails_adventure.gif garfield.gif superman.gif greendog.gif dragon_crystal.gif

Just as a test, I cobbled together an 'alternative' interface that uses that console code I posted here a while back.

pinball.gif vfa.gif faceball.gif

It's a sad thing that the above runs at a decent framerate, whereas the graphical versions run at half the rate of they should. At least, that is running in Debug mode within the IDE...

sonic.gif james_pond.gif

Checking the Release build would always be a better plan. That's still lousy performance in my book, though.


Having written the above, I decided to look at the official Game Gear documents once again, and reread the section on ROM banking. I had not realised that for 1MBit cartridges, area 0 and area 1 ($0000..$3FFF and $4000..$7FFF respectively) were fixed, and only area 2 ($8000..$BFFF) could be changed.

Fixing areas 0 and 1 fixed a number of the smaller games:

zillion.gif fantasy_zone_3.gif ghost_house.gif alex_kidd_mir_1.gif alex_kidd_mir_2.gif fantasy_zone_1_1.gif fantasy_zone_1_2.gif wonder_boy.gif
monaco_gp.gif aerial_assault.gif pengo.gif woody_pop.gif

It turns out that if I resize the window so the display is at 1:1 pixel scaling, I get ~73FPS on my machine and - most importantly - only 3% CPU usage. If I make the window even twice as large as it was, it drops to about 30FPS and 100% CPU usage. DirectX beckons...

New Z80 emulator

Wednesday, 6th December 2006

sonic_gg_1.png

One chap I cannot thank enough is CoBB for all his hard work in the Z80 field.

I've rewritten the Z80 emulation from scratch; this time it uses an expanded switch block (the 'manual' way) to decode instructions. Rather than write every combination of instructions out by hand, the code making up the switch blocks up is generated by another program, reading instruction information from a table copied from an Excel spreadsheet.

At the cost of a significantly larger assembly (from 40KB to 140KB) I now get a 100% speed increase (from ~50MHz to 100MHz).

I still can't pass the port of ZEXALL I'm testing with (and the same instructions too - not bad for a 100% rewrite to end up with exactly the same bugs), but after comparing some of my offending tables against CoBB's ones I've isolated some of the hiccoughs. The only instruction group test I fail is, naturally, the one that takes the longest to execute - getting a hardware, or indeed emulator, comparison takes well over an hour.

Anyhow, rewriting the Z80 emulation seems to have been the right thing to do. As you might have guessed from the picture at the top of this entry, Sonic now runs.

sonic_gg_2.png sonic_gg_3.png

My VDP (Video Display Processor) emulation is still rather rough-and-ready (I've really been concentrating on the Z80 bit) and the second column of background tiles is not updated correctly, so I apologise in advance for the distortion! It only appears in SMS mode (the display is cropped in Game Gear mode).

sonic_sms_1.png sonic_sms_2.png

The fill colour (in the left column here) is incorrect in most games too.

sonic_2_1.png sonic_2_2.png sonic_2_3.png sonic_2_4.png sonic_2_5.png

I rather preferred Sonic 2, but maybe that's because you can pick up dropped rings and I'm rather lousy at it otherwise.

wb_1.png wb_2.png wb_3.png

I accused the previous Wonder Boy III shot of not reading the start button. Somehow I also failed to notice the missing sprites (clouds and main part of the castle), which was part of the main problem. It now appears to play well.

vfa_1.png vfa_2.png vfa_3.png vfa_4.png

Support for zoomed sprites seems to be missing in some emulators (at least, the versions of Dega and Pastorama I have to hand), but I use them so implemented them to let my programs work when testing - it's nice to see a commerical game use them too!

gunstar_1.png gunstar_2.png

Gunstar Heroes runs, but flickers and jumps (not the visible sprite 'flicker' in those screenshots) - some bug in my CPU interrupt handling or VDP interrupt generation.

psycho_fox.png the_flash.png

Psycho Fox runs insanely fast - even faster than The Flash as seen to the right - making an already difficult game to control virtually impossible. I'm really not sure what could be causing that, but the enemy sprites sometimes flicker up and back down quickly, so it could be one of the remaining CPU bugs.

fantasy_zone_2.png maze_walker.png

Fantasy Zone demonstrates both the blanking colour bug (that left column should be green) and the distorted second column bug, but plays well. I tried to get a better screenshot of Maze Walker, but not handling 3D glasses makes looking at the screen a rather unpleasant experience (it flickers the left and right eye views in quick succession; the 3D glasses had two LCD shutters that opened and closed in sequence with the images on-screen).

I might add a Dega-esque red/green anaglyph filter, but I find those rather unpleasant to look at so might provide a stereo pair view.

In any case, the most important SMS game now runs -

bios_1.png bios_2.png bios_3.png

I shall refrain from using the Terry Pratchett quote (just this once).

ys_junk.png

Some games look like the above, which makes me happy - it's the ones that do nothing at all that worry me. What with the CPU bugs, dodgy emulation of the mapper, missing (important!) hardware ports and hackish VDP, it's surprising anything runs. It's getting better.

EDIT: How are there so many spelling errors? Fantasy Start as opposed to Fantasy Zone, Video Display Hardware as the expansion of the VDP acronym... I should not write these so late at night.

Compatibility increases further...

Thursday, 23rd November 2006

wonder_boy_1.png

I've added better memory emulation (that is, handling ROM paging, RAM mirroring and enabling a BIOS or not). I wouldn't dare say "more accurate", as that might indicate that something about it is partially accurate. smile.gif

I've isolated one of the biggest problems - and that's programs getting caught in a loop waiting for an interrupt that is never triggered.

The source of these interrupts is the VDP. It can generate two kinds of interrupt - on a line basis (where you can configure it to fire an interrupt every X scanlines) or on a frame basis (where it fires an interrupt at the end of the active display).

Two bits amongst the VDP's own registers control whether the interrupt fires or not. On top of that, there are two internal flags that are set when either of the interrupts fire. They are reset when the VDP's control port is read.

Charles MacDonald's VDP documentation
Bit 5 of register $01 acts like a on/off switch for the VDP's IRQ line. As long as bit 7 of the status flags [this is the frame interrupt pending flag] is set, the VDP will assert the IRQ line if bit 5 of register $01 is set, and it will de-assert the IRQ line if the same bit is cleared.

Bit 4 of register $00 acts like a on/off switch for the VDP's IRQ line. As long as the line interrupt pending flag is set, the VDP will assert the IRQ line if bit 4 of register $00 is set, and it will de-assert the IRQ line if the same bit is cleared.

The way I've chosen to emulate this is as a boolean storing the IRQ status, and to call a function, UpdateIRQ(), every time something that could potentially change the status of it happens.

private bool IRQ = false;
private void UpdateIRQ() {
    bool newIRQ = (LineInterruptEnabled && LineInterruptPending) || (FrameInterruptEnabled && FrameInterruptPending);
    if (newIRQ && !IRQ) this.CPU.Interrupt(true, 0x00);
    IRQ = newIRQ;
}

This detects a rising edge. Falling edge hasn't worked so well. Well, truth be told, neither work very well. So, hitting the I key in the emulator performs a dummy read of the control port which usually 'unblocks' the program.

I'm hoping that my problem is related to this similar one, as the fix seems pretty straightforwards.

The SEGA logo at the top of this entry is not the only evidence of working commerical software...

wonder_boy_2.png wonder_boy_3.png

Not reading the Start button is a common affliction.

faceball_2000.png

Here's the Game Gear DOOM for Scet. smile.gif

marble_madness_1.png marble_madness_2.png marble_madness_3.png marble_madness_4.png marble_madness_5.png

Marble Madness appears to be fully playable, though needs prompting from my I key every so often.

columns_1.png columns_2.png

Columns can't find the start button either, but the demo mode works.

pinball_dreams.png

ys.png desert_strike.png

Getting as far as a title screen is still an achievement in my books.

My luck can't hold out forever, but the homebrew scene is still providing plenty of screenshots...

bock_2002_1.png bock_2002_2.png bock_2002_3.png

Maxim's Bock's Birthday 2002 has been immensely useful for sorting out ROM paging issues.

bock_2003_1.png bock_2003_2.png

Bock's Birthday 2003 demonstrates the interrupt bug, but appears to run healthily otherwise.

hicolor_1.png hicolor_2.png hicolor_3.png

Chris Covell's Hicolor Demo also demonstrates this bug, needing a prod before each new image is displayed.

digger_chan_1.png digger_chan_2.png

Aypok's Digger Chan appears to play fine.

zoop_1.png zoop_2.png nibbles.png

Martin Konrad's Zoop 'em Up and GG Nibbles games run, Zoop 'em Up seems to have collision detection issues (aluop-related bug).

The bug in the Z80 core still eludes me - how aluop with an immediate value passes and aluop with a register fails is rather worrying. At least part of the flags problem has been resolved - the cannon in Bock's KunKun & KokoKun still don't fire, but at the switches now work so you can complete levels.

Screenshots galore

Tuesday, 21st November 2006

I downloaded a collection of homebrew releases from SMS Power! for testing. Here are the obligatory screenshots.

Sega Master System

dc_evolution.png good_advice.png maxim_chip_8_1.png maxim_chip_8_2.png maxim_chip_8_3.png maxim_chip_8_4.png maxim_chip_8_5.png maxim_fantasy_zoop.png sms_power_7th.png tetris.png vik_snd.png violation_1.png violation_2.png violation_3.png violation_4.png

Copyright Violation is displayed incorrectly - the text should be on top of the stars, but that VDP feature is as yet unsupported.

Sega Game Gear

win_gg.png win_gg_paint.png win_gg_text.png win_gg_sea.png win_gg_pic.png sms_power_gg.png

Yes, Windows and DOOM were ported to the Game Gear. That second screenshot is DOOM's automap. wink.gif

There are still lots of ROMs that won't go - that could be due to poor VDP emulation, or it could be due to ROM paging errors (the emulated memory is just straight 64KB RAM), or could be down to the remaining bugs in the Z80 emulation.

Sega: Enter the Pies

Monday, 20th November 2006

The Z80 core is now a bit more accurate - ZEXALL still reports a lot of glitches, and this is even a specially modified version of ZEXALL that masks out the two undocumented flags.

The VDP (Video Display Processor - the graphics hardware) has been given an overhaul and is now slightly more accurate. A lot more software runs now. I have also hacked in my PSG emulator (that's the sound chip) from my VGM player. It's not timed correctly (as nothing is!) but it's good enough for testing.

picohertz_tween.png

picohertz_wormhole.png

Picohertz, the demo I have been working on (on and off) now runs correctly. The hole in the Y in the second screenshot is caused by the 8 sprite per scanline limit. The first screenshot shows off sprite zooming (whereby each sprite is zoomed to 200% the original size). The background plasma is implemented as a palette shifting trick.

fire_track.png

fire_track_paused.png

Fire Track runs and is fully playable. The second shot shows a raster effect (changing the horizontal scroll offset on each scanline.

Seeing as I understand the instructions that my programs use (and the results of them), and have my own understanding of parts of the hardware, it's not really surprising that the programs I've written work perfectly, but ones written by others don't, as they might (and often do) rely on tricks and results that I'm not aware of, or on hardware that I haven't implemented accurately enough. At least I do not need to emulate any sort of OS to run these programs!

SMS Power! has been an amazing resource in terms of hardware documentation and homebrew ROMs. I've been using the entries to the 2006 coding competition to test the emulator.

kunkun_kokokun_title.png kunkun_kokokun_game.png

Bock's game, KunKun & KokoKun, nearly works. The cannon don't fire, which would make the game rather easy if it wasn't for the fact that the switch to open the door doesn't work either. I suspect that a CPU flag isn't being set correctly as the result to an operation somewhere.

pong.png

Haroldoop's PongMaster is especially interesting as it was not written in Z80 assembly, but C. It's also one of the silkiest-smooth pong games I've come across.

an!mal_paws_text.png an!mal_paws.png

An!mal/furrtek's Paws runs, but something means that the effect doesn't work correctly (the 'wavy' bit should only have one full wave in it, not two - it appears my implementation of something doubles the frequency of the wave). The music sounds pretty good, though.

columns.png

Sega's Columns gets the furthest of any official software - the Sega logo fades in then out.

sega_enterpies.png

I do like the idea that Sega is an ENTERPIεS. (From the Game Gear BIOS). (I believe this is a CPU bug).

frogs.png

Charles Doty's Frogs is a bit of a conundrum. The right half of the second frog is missing due to the 8 sprites per scanline limitation of the VDP. However, Meka, Emukon, Dega and now my emulator draw the rightmost frog's tongue (and amount if it showing) differently, as well as whether the frog is sitting or leaping. There's a lot of source for such a static program (it doesn't do anything in any emulator I've tried it on, nor on hardware). Dega is by far the strangest, as the tongue moves in and out rapidly. I'm really not sure what's meant to be happening here.

Here are the results of ZEXALL so far.

Z80 instruction exerciser

ld hl,(nnnn).................OK
ld sp,(nnnn).................OK
ld (nnnn),hl.................OK
ld (nnnn),sp.................OK
ld <bc,de>,(nnnn)............OK
ld <ix,iy>,(nnnn)............OK
ld <ix,iy>,nnnn..............OK
ld (<ix,iy>+1),nn............OK
ld <ixh,ixl,iyh,iyl>,nn......OK
ld a,(nnnn) / ld (nnnn),a....OK
ldd<r> (1)...................OK
ldd<r> (2)...................OK
ldi<r> (1)...................OK
ldi<r> (2)...................OK
ld a,<(bc),(de)>.............OK
ld (nnnn),<ix,iy>............OK
ld <bc,de,hl,sp>,nnnn........OK
ld <b,c,d,e,h,l,(hl),a>,nn...OK
ld (nnnn),<bc,de>............OK
ld (<bc,de>),a...............OK
ld (<ix,iy>+1),a.............OK
ld a,(<ix,iy>+1).............OK
shf/rot (<ix,iy>+1)..........OK
ld <h,l>,(<ix,iy>+1).........OK
ld (<ix,iy>+1),<h,l>.........OK
ld <b,c,d,e>,(<ix,iy>+1).....OK
ld (<ix,iy>+1),<b,c,d,e>.....OK
<inc,dec> c..................OK
<inc,dec> de.................OK
<inc,dec> hl.................OK
<inc,dec> ix.................OK
<inc,dec> iy.................OK
<inc,dec> sp.................OK
<set,res> n,(<ix,iy>+1)......OK
bit n,(<ix,iy>+1)............OK
<inc,dec> a..................OK
<inc,dec> b..................OK
<inc,dec> bc.................OK
<inc,dec> d..................OK
<inc,dec> e..................OK
<inc,dec> h..................OK
<inc,dec> l..................OK
<inc,dec> (hl)...............OK
<inc,dec> ixh................OK
<inc,dec> ixl................OK
<inc,dec> iyh................OK
<inc,dec> iyl................OK
ld <bcdehla>,<bcdehla>.......OK
cpd<r>.......................OK
cpi<r>.......................OK
<inc,dec> (<ix,iy>+1)........OK
<rlca,rrca,rla,rra>..........OK
shf/rot <b,c,d,e,h,l,(hl),a>.OK
ld <bcdexya>,<bcdexya>.......OK
<rrd,rld>....................OK
<set,res> n,<bcdehl(hl)a>....OK
neg..........................OK
add hl,<bc,de,hl,sp>.........OK
add ix,<bc,de,ix,sp>.........OK
add iy,<bc,de,iy,sp>.........OK
aluop a,nn...................   CRC:04d9a31f expected:48799360
<adc,sbc> hl,<bc,de,hl,sp>...   CRC:2eaa987f expected:f39089a0
bit n,<b,c,d,e,h,l,(hl),a>...OK
<daa,cpl,scf,ccf>............   CRC:43c2ed53 expected:9b4ba675
aluop a,(<ix,iy>+1)..........   CRC:a7921163 expected:2bc2d52d
aluop a,<ixh,ixl,iyh,iyl>....   CRC:c803aff7 expected:a4026d5a
aluop a,<b,c,d,e,h,l,(hl),a>.   CRC:60323322 expected:5ddf949b
Tests complete



The aluop (add/adc/sub/sbc/and/xor/or/cp) bug seems to be related to the parity/overflow flag (all other documented flags seem to be generating the correct CRC). daa hasn't even been written yet, so that would be the start of the problems with the daa,cpl,scf,ccf group. adc and sbc bugs are probably related to similar bugs as the aluop instructions.

The biggest risk is that my implementation is so broken it can't detect the CRCs correctly. I'd hope not.

In terms of performance; when running ZEXALL, a flags-happy program, I get about ~60MHz speed in Release mode on a 2.4GHz Pentium 4. When ZEXALL is finished, and it's just looping around on itself, I get ~115MHz.

The emulator has not been programmed in an efficient manner, rather a simple and clear manner. All memory access is done by something that implements the IMemoryDevice controller (with two methods - byte ReadByte(ushort address) and void WriteByte(ushort address, byte data)) and all hardware access is done by something that implements the IHardwareController interface (also exposing two methods - byte ReadDevice(byte port) and void WriteDevice(byte port, byte data)).

Most of the Z80's registers can be accessed via an index which makes up part of an opcode. You'd have thought that the easiest way to represent this would be, of course, an array. However, it's not so simple - one of the registers, index 6, is (HL) - which means "whatever HL is pointing to". I've therefore implemented this with two methods - byte GetRegister(int index) and void SetRegister(int index, byte value).

Life isn't even that simple, though, as by inserting a prefix in front of the opcode you can change the behaviour of the CPU - instead of using HL, it'll use either IX or IY, two other registers. In the case of (HL) it becomes even hairier - it'll not simply substitute in (IX) or (IY), it'll substitute in (IX+d), where d is a signed displacement byte that is inserted after the original opcode.

To sort this out, I have three RegisterCollections - one that controls the "normal" registers (with HL), one for IX and one for IY. After each opcode and prefix is decoded, a variable is set to make sure that the ensuing code to handle each instruction works on the correct RegisterCollection.

The whole emulator is implemented in this simplified and abstracted manner - so I'm not too upset with such lousy performance.

I'm really not sure how to implement timing in the emulator. There's the easy timing, and the not-so-easy timing.

The easy timing relates the VDP speed. On an NTSC machine that generates 262 scanlines (60Hz), on a PAL machine that generates 313 scanlines (50Hz). That's 15720 or 15650 scanlines per second respectively.

According to the official Game Gear manual, the CPU clock runs at 3.579545MHz. I don't know if this differs with the SMS, or whether it's different on NTSC or PAL devices (the Game Gear is fixed to NTSC, as it never needs to output to a TV, having an internal LCD).

I interpret this as meaning that the CPU needs to be run for 227.7 or 228.7 cycles per scanline. That way, my main loop looks a bit like this:

if (Hardware.VDP.VideoStandard == VideoStandardType.NTSC) {
    for (int i = 0; i < 262; ++i) {
        CPU.FetchExecute(228); 
        Hardware.VDP.RasteriseLine();
    }
} else {
    for (int i = 0; i < 313; ++i) {
        CPU.FetchExecute(229);
        Hardware.VDP.RasteriseLine();
    }
}

The VDP raises an event when it enters the vertical blank area, so the interface can capture this and so present an updated frame.

The timing is therefore tied to the refresh rate of the display.

super_gg.png

Here's the fictional Super Game Gear, breezing along at 51MHz. The game runs just as smoothly as it would at 3MHz, though - as the game's timing is tied to waiting for the vertical blank.

Actually, I tell a lie - as Fire Track polls the vertical counter, rather than waiting for an interrupt, it is possible for it to poll this counter so fast (at an increased clock rate) that it hasn't changed between checks. That way "simple" effects run extra fast, but the game (that has a lot of logic code) runs at the same rate.

This works. The problem is caused by sound.

With the video output, I have total control of the rasterisation. However, with sound, I have to contend with the PC's real hardware too! I'm using the most excellent FMOD Ex library, and a simple callback arrangement, whereby when it needs more data to output it requests some in a largish chunk.
If I emulate the sound hardware "normally", that is updating registers when the CPU asks them to be updated, by the time the callback is called they'll have changed a number of times and the granularity of sound updates will be abysmal.

A solution might be to have a render loop like this:

for (int i = 0; i < 313; ++i) {
    CPU.FetchExecute(229);
    Hardware.VDP.RasteriseLine();
    Hardware.PSG.RenderSomeSamples(1000);
}

However, this causes its own problems. I'd have to ensure that I was generating exactly the correct number of samples - if I generated too few I'd end up with crackles and pops in the audio as I ran out of data when the callback requested some, or I'd end up truncating data (which would also crackle) if I generated too much.

My solution thus far has been a half-way-house - I buffer all PSG register updates to a Queue, logging the data written and how many CPU cycles had been executed overall when the write was attempted. This way, when the callback is run, I can run through the queued data, using the delay between writes to ensure I get a clean output.

As before, this has a problem if the timing isn't correct - rather than generate pops or crackles, it means that the music would play at an inconsistent rate.

Of course, the "best" solution would be to use some sort of latency-free audio solution - MIDI, for example, or ASIO. If I timed it, as with everything else, to scanlines I'd end up with a 64µs granularity - which is larger than a conventional 44.1kHz sample (23µs), so PWM sound might not work very well.

chip_8.png

Incidentally, this is not the first emulator I have written - I have written the obligatory Chip-8 emulator, for TI-83 calculator and PC. Being into hardware, but not having the facilities to hand to dabble in hardware as much as I'd like to, an emulator provides a fun middle-ground between hardware and software.

wormhole.gif

And now for something completely different

Friday, 17th November 2006

A long time ago I thought I'd try my hand at this emulation malarkey.

cogwheel.gif

The hardware is the Z80-based Sega Master System and Game Gear.

Due to the design, it was a huge, messy, poorly written series of hacks that could just about produce the above result if you didn't breathe too hard.

After finding this document, I had a bit of a blast at rearranging the Z80 core and timing the video hardware a bit more correctly. Here's that old Fire Track project:

title.png
All this does is run the emulation for 100 scanlines on a Timer's tick, hence the low reported clock speed.

opening_effect.png
The Game Gear's LCD cropped the output screen, so you wouldn't see the junk to the sides of the display here.

level_screen_without_sprite.png
My implementation of the VDP doesn't support sprites yet.

Of course, these are the most exciting screenshots:

cogwheel_sdsc.gif

zexall.png

Two versions of ZEXALL, one that displays the results on the SMS screen, the other in an SDSC debug console.

One day I shall purge all of these CRC errors. One day! But not now, as I don't have the time to put any work on this (I stole a couple hours for the above), and in spare time (hah!) I should really focus on the Latenite software. Which, whilst it doesn't provide such pretty screenshots, is a great deal more useful.

A productive weekend

Wednesday, 3rd May 2006

What with the weekend having an extra Monday tacked on for good measure (Labour Day), I felt the need to be productive.

I also felt the need to listen to VGM files converted to MIDI, so rustled up a VGM to MIDI converter. There already is one (available on the SMS Power! site), but I could never get it to work.
Having never really puzzled out the YM2413 ('OPLL', FM chip) I limited it to the square-wave generating PSG.

First of all, you need to be able to convert a tone register value (from 0 to 1023), the period of the output square wave, to a MIDI key value (0 to 127, where every 12 keys represent an octave).
This is easiest if you have a real frequency (in Hertz) to work with, so I have the formulae:

Frequency = ClockSpeed ÷ (32 × ToneReg)
Key = 12 × Log2|Frequency × Constant|
ClockSpeed is the clock speed of the PSG in Hertz. Constant is a precalculated constant used to scale the frequency to a range so that 440Hz ends up being played as key A, octave 5.

As it is unlikely you'll get a round number with this, I rely on adjusting the MIDI pitch wheel. Now it's a case of detecting attacks (when the volume of a channel increases) and releases (when the volume of a channel is set to 0) to create MIDI key press and release messages.
Percussion (white noise from the PSG) is handled the same way, except that instead of using mapping frequencies to keys and pitch wheels it plays one of 3 different drums corresponding to the 3 different pitches of noise.

It works fairly well, and you can download the software and source from the site here.
You can also listen to some samples:

The problem with sound-related apps is that they don't provide very interesting screenshots, so I took on a little side project that I hoped would. (After all, journals are a bit dull if they're plain text).

tween1.png tween2.png

tween3.png tween4.png

Yep, it's that 3MHz, 32-colour, 8KB powerhouse the Sega Game Gear again.
Looking on pouet.net, there is only one Game Gear demo on there. If I haven't got it in me to complete a full-blown game, I can at least try and contribute something. rolleyes.gif After all, it's a relatively simple Z80 system to write programs for.

Have the typically poorly-shot video to see it in action:

(The odd flickering horizontal band that sometimes appears is a case of me using up the 8 sprite per scanline ration).

YM2413 (OPLL) Emulation

Monday, 30th January 2006

As (yet another) side project to all this Z80 work, I've also decided to have a stab at the Japanese Master System's FM chip. It's a YM2413, and the documentation on it is fairly tricky to get to grips with - not only thanks to it being in typical Japanese-manual English.

So far, I have this - the VGM player is a bit buggy and extremely primitive (unfinished), but should be enough to demonstrate the current state of the OPLL. You'll need an FM VGM to try it with - the Space Harrier demo from the BIOS sounds pretty good.

If anybody has had any experience with the YM2413, I'd be interested to hear if you had any helpful tips...

In other news, I've been adding multipage program support to Brass, and Latenite has had a lot of debugger integration work done on it. It's still a long slog before it's in a presentable state, sadly.

Subscribe to an RSS feed that only contains items with the Sega tag.

FirstLast RSSSearchBrowse by dateIndexTags