• entries
    222
  • comments
    608
  • views
    588257

About this blog

Z80 and C#-related shenanigans - now with added electronics.

Entries in this blog

benryves

Models

Quote:
Original post by Evil Steve
Do you intend to parse the progs.dat file (I think that's what it was called) - the QuakeC compiled code? I've no idea what's actually involved in that, however...
I'm not sure either (I haven't looked into it). As the source code for Quake and the QuakeC compiler have been released I might be able to do something. I have a hunch that said code is responsible for level events (switches, bridges, lifts, doors and so on) so it would seem pretty important.

Quote:
Original post by Scet
You might want to check the version of your BSP files versus the one used in the Unofficial Quake Specs.

...

Also here's some API independent code for loading Quake MDL models.
So far I've had one minor problem that was resolved with a hex editor (something simple like a short becoming an int or vice-versa). I'll take a look at your MDL code - thank you! [smile]



I've made progress with the Alias model loader and written a ModelRenderer class to go with it.


zombie.mdl before and after texturing support.


Animation isn't handled very well just yet - the frames aren't grouped by action (such as the 'attack' animation, 'walk' animation and so on) and the Draw call just takes a frame number.



It'll do for the moment as a test. [smile]

I'm still having problems with textures not wrapping correctly.



As you can see the texture above the hand is distorted. I was pointed in the direction of a solution via the texture wrapping options, and have added these lines:

graphics.GraphicsDevice.SamplerStates[0].AddressU = TextureAddressMode.Wrap;
graphics.GraphicsDevice.SamplerStates[0].AddressV = TextureAddressMode.Wrap;



This, unfortunately, hasn't helped. Experimentation has shown that this does change something, and by using the TextureAddressMode.Mirror I get the following -


Note the orientation of the red symbols


I still can't get decent looking light maps out of the level - they all have the "diagonal stripes" effect that usually is caused by an erroneous width. I think I'll have to start trawling through Quake's source code.
benryves
Quote:
Original Post by Scet
Do you plan on making anything out of it, or will it just be a bsp viewer?
An XNA Quake implementation the way I intend on working - that is, to start from scratch - is not really worth it (as far as getting the gameplay 100% identical to DOS Quake).

As a learning project, there's a decent quantity of further material (as far as maps, high-resolution textures and so on go), and I intend on trying to learn how to write a 'FPS-friendly' 3D engine based around Quake's data. Whether I shoehorn some sort of game into that or not is yet to be decided. [smile]

Anyhow, I mentioned that I'd be moving on to light maps. Unfortunately the documentation I have is rather vague on this topic (the Unofficial Quake Specs), and I can't get meaningful data out of the BSP files.



I had a slight issue to resolve. Certain textures were appearing as a single colour, like the above. As far as I know this is a symptom of using crazy texture coordinates. I wrote a crude routine that attempts to shunt vertices into the 0..1 range, and the result looks a bit better:



As you can see, the top edge of the large texture is still badly distorted.

I modified the resource loader slightly to handle loading files outside the main pack files.





The result is that I can low load resource files - such as external levels - as you would any other resource, with a call to LoadResource("maps/ikspq5.bsp"). The level loader also parses the entity declaration block inside the .bsp, putting the player in the correct starting place and - more noticably in the screenshots - finding out the title of the level.



I've made a start at loading the Alias .mdl files, but their structure is rather convoluted so the only visible result thus far is a dump of all of the skin pictures.



There doesn't seem to be much information about Quake BSPs (plenty on Quake 3 BSPs, however!) so if anyone has any recommendations I'd be grateful. Looking at flipcode's article on Quake 2 BSPs it would appear that they're not too different, so I'll have a go with that.
benryves

XNA Quake

After pratting around with DOOM with MDX, it's logical to move up to Quake with XNA. After all, the levels are "true" 3D, and there's a healthy amount of new textures (including bump and gloss maps) so that it would make creating an advanced engine easier. My problem is always finding resources.

On the subject of resources, I'm using a system whereby the various classes for game resources (such as the Level or Model class) can implement ResourceLoader.ILoadable, so I can do things like this:

ResourceLoader = new ResourceLoader(@"C:\Program Files\Quake\ID1");

Palette DefaultPalette = ResourceLoader.Load("gfx/palette.lmp");
Level SomeLevel = ResourceLoader.Load("maps/start.bsp");



This way the ResourceLoader can handle dragging data out of multiple pack (.pak) files or loading override files.

First things first - loading levels. This is nice and easy (unlike DOOM) - the .bsp files contain a list of vertices and a list of edges (each edge contains a start and end vertex index). For testing purposes, I used the Graphics class to draw out each level in 2D:



Next up, I create a point list and render those in 3D:



...and now the edges as a line list:



That seems to be on its side. Quake uses (x, y) as a 2D floor plan and z for height, so I need to swap z and y (and negate y whilst we're at it).



Quake uses faces for solid walls, which are simply convex polygons made up from lists of the aforementioned edges (and may have more than three sides). I'm creating a simple triangle list, and applying some fog to make the world look 3D (there's no lighting):



Texturing would make a welcome addition. Quake uses a rather novel texture coordinate system (at least, I've never seen anything quite like it) whereby each face references a TextureInfo structure, which contains the index of the texture itself, a pair of vectors indicating the horizontal and vertical axis (for aligning the texture against the surface of the face) and an offset in texels (so the texture can be aligned to the edges of the face). Fortunately, the BSP documentation I have indicates how to use this information to calculate the texture offset per-vertex, and by dividing by the texture's width and height I can get "conventional" texture coordinates.



The real Quake textures would be nice, I suppose. As I break up the faces into triangle indices, I group them by texture index. The final triangle list is made up of groups of triangles by texture, so that (for example) triangles 0 to 10 will be texture 0, triangles 11 to 14 will be texture 1, triangle 15 will be texture 2 and so on. I also maintain a list of which triangles use which texture, so to render I simply set the texture, draw a range of triangles from the single list, then move on to the next texture.





It's all pretty brute-force stuff at the moment, but it's still very fast on modern hardware. Next up will be lightmaps. [smile]
benryves
Edit: The following code has a bug in it. Please make sure you use the corrected code!


After a conversation with Thevenin regarding the Game class in XNA not letting you use event-driven keyboard input (useful, for example, for entering text) I rolled out this monstrosity.

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* XnaTextInput.TextInputHandler - benryves@benryves.com *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* This is quick and very, VERY dirty. *
* It uses Win32 message hooks to grab messages (as we don't get a nicely wrapped WndProc). *
* I couldn't get WH_KEYBOARD to work (accessing the data via its pointer resulted in access *
* violation exceptions), nor could I get WH_CALLWNDPROC to work. *
* Maybe someone who actually knows what they're doing can work something out that's not so *
* kludgy. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* This quite obviously relies on a Win32 nastiness, so this is for Windows XNA games only! *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */


#region Using Statements
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms; // This class exposes WinForms-style key events.
#endregion

namespace XnaTextInput {

///
/// A class to provide text input capabilities to an XNA application via Win32 hooks.
///

class TextInputHandler : IDisposable {

#region Win32

///
/// Types of hook that can be installed using the SetWindwsHookEx function.
///

public enum HookId {
WH_CALLWNDPROC = 4,
WH_CALLWNDPROCRET = 12,
WH_CBT = 5,
WH_DEBUG = 9,
WH_FOREGROUNDIDLE = 11,
WH_GETMESSAGE = 3,
WH_HARDWARE = 8,
WH_JOURNALPLAYBACK = 1,
WH_JOURNALRECORD = 0,
WH_KEYBOARD = 2,
WH_KEYBOARD_LL = 13,
WH_MAX = 11,
WH_MAXHOOK = WH_MAX,
WH_MIN = -1,
WH_MINHOOK = WH_MIN,
WH_MOUSE_LL = 14,
WH_MSGFILTER = -1,
WH_SHELL = 10,
WH_SYSMSGFILTER = 6,
};

///
/// Window message types.
///

/// Heavily abridged, naturally.
public enum WindowMessage {
WM_KEYDOWN = 0x100,
WM_KEYUP = 0x101,
WM_CHAR = 0x102,
};

///
/// A delegate used to create a hook callback.
///

public delegate int GetMsgProc(int nCode, int wParam, ref Message msg);

///
/// Install an application-defined hook procedure into a hook chain.
///

/// Specifies the type of hook procedure to be installed.
/// Pointer to the hook procedure.
/// Handle to the DLL containing the hook procedure pointed to by the lpfn parameter.
/// Specifies the identifier of the thread with which the hook procedure is to be associated.
/// If the function succeeds, the return value is the handle to the hook procedure. Otherwise returns 0.
[DllImport("user32.dll", EntryPoint = "SetWindowsHookExA")]
public static extern IntPtr SetWindowsHookEx(HookId idHook, GetMsgProc lpfn, IntPtr hmod, int dwThreadId);

///
/// Removes a hook procedure installed in a hook chain by the SetWindowsHookEx function.
///

/// Handle to the hook to be removed. This parameter is a hook handle obtained by a previous call to SetWindowsHookEx.
/// If the function fails, the return value is zero. To get extended error information, call GetLastError.
[DllImport("user32.dll")]
public static extern int UnhookWindowsHookEx(IntPtr hHook);

///
/// Passes the hook information to the next hook procedure in the current hook chain.
///

/// Ignored.
/// Specifies the hook code passed to the current hook procedure.
/// Specifies the wParam value passed to the current hook procedure.
/// Specifies the lParam value passed to the current hook procedure.
/// This value is returned by the next hook procedure in the chain.
[DllImport("user32.dll")]
public static extern int CallNextHookEx(int hHook, int ncode, int wParam, ref Message lParam);

///
/// Translates virtual-key messages into character messages.
///

/// Pointer to an Message structure that contains message information retrieved from the calling thread's message queue.
/// If the message is translated (that is, a character message is posted to the thread's message queue), the return value is true.
[DllImport("user32.dll")]
public static extern bool TranslateMessage(ref Message lpMsg);


///
/// Retrieves the thread identifier of the calling thread.
///

/// The thread identifier of the calling thread.
[DllImport("kernel32.dll")]
public static extern int GetCurrentThreadId();

#endregion

#region Hook management and class construction.

/// Handle for the created hook.
private readonly IntPtr HookHandle;

/// Create an instance of the TextInputHandler.
/// Handle of the window you wish to receive messages (and thus keyboard input) from.
public TextInputHandler(IntPtr whnd) {
// Create the keyboard hook:
this.HookHandle = SetWindowsHookEx(HookId.WH_GETMESSAGE, new GetMsgProc(ProcessMessages), IntPtr.Zero, GetCurrentThreadId());
}

public void Dispose() {
// Remove the hook.
if (this.HookHandle != IntPtr.Zero) UnhookWindowsHookEx(this.HookHandle);
}

#endregion

#region Message processing

private int ProcessMessages(int nCode, int wParam, ref Message msg) {
// Check if we must process this message (and whether it has been retrieved via GetMessage):
if (nCode == 0 && wParam == 1) {

// We need character input, so use TranslateMessage to generate WM_CHAR messages.
TranslateMessage(ref msg);

// If it's one of the keyboard-related messages, raise an event for it:
switch ((WindowMessage)msg.Msg) {
case WindowMessage.WM_CHAR:
this.OnKeyPress(new KeyPressEventArgs((char)msg.WParam));
break;
case WindowMessage.WM_KEYDOWN:
this.OnKeyDown(new KeyEventArgs((Keys)msg.WParam));
break;
case WindowMessage.WM_KEYUP:
this.OnKeyUp(new KeyEventArgs((Keys)msg.WParam));
break;
}

}

// Call next hook in chain:
return CallNextHookEx(0, nCode, wParam, ref msg);
}

#endregion

#region Events

public event KeyEventHandler KeyUp;
protected virtual void OnKeyUp(KeyEventArgs e) {
if (this.KeyUp != null) this.KeyUp(this, e);
}

public event KeyEventHandler KeyDown;
protected virtual void OnKeyDown(KeyEventArgs e) {
if (this.KeyDown != null) this.KeyDown(this, e);
}

public event KeyPressEventHandler KeyPress;
protected virtual void OnKeyPress(KeyPressEventArgs e) {
if (this.KeyPress != null) this.KeyPress(this, e);
}

#endregion
}
}

The demo application will not support drawing characters that aren't provided in the sprite font (so it's a very bad demo), but it's the above class that's the useful bit of the package. It generates KeyDown, KeyUp and KeyPress events like WinForms controls.

Might be useful if your Windows game needs text input. [smile] Win32 veterans, feel free to lambast my lack of skills.
benryves
The TI-83+ lacks something the 84+ series has - a USB port.


Enter the VMusic2. This low-cost (GBP25) module offers a USB host controller with a simple serial interface that can be used to read/write FAT-formatted USB mass storage devices. It can also play MP3 files straight from the drive!



This is all very well, but the TI doesn't have a standard serial port either. To handle communications between the two, therefore, is a PICAXE-28X1 microcontroller.

The TI can then run a program that communicates using its standard linking protocol.


I've posted a thread on MaxCoderz with more information about the project. For those interested in the VMusic2 device, here's a datasheet and here are the commands.


1.48MB AVI (MPEG-4, MP3)

Yes, I know I should probably get a life. I blame the solder fumes.
benryves

SonyIR

I take a strange enjoyment in reading format and protocol documentation, and I find it to be one of the most rewarding tasks. Hence the Sega emulator project, the DOOM project, the Vgm2Midi project and so on.



This project is more hardware related - it's an infrared remote control library for the TI-83 series calculators. It supports the SIRCS (Sony) protocol, with command words of 12, 15 and 20 bits.

Video of the library in action (960KB WMV).

The hardware is very simple if you just want to transmit, and a single-package infrared receiver/amplifier/demodulator makes the receiving circuit not that much more complex.



The library also comes with a program that can be used to turn an 83+ into a programmable remote control. It supports multiple device profiles, and editing is easy - press the key (on the calculator) you wish to customise, point your remote at the receiver and press the button you want change to and the details are automatically saved.



SonyIR download page and documentation.
benryves
This post got me wondering about a TI emulator. I'd rather finish the SMS one first, but so as to provide some pictures for this journal I wrote a T6A04 emulator (to you and me, that's the LCD display driver chip in the TI-82/83 series calculators). In all, it's less than a hundred lines of code.

The problem with TI emulation is that one needs to emulate the TIOS to be able to do anything meaningful. Alas, I had zero documentation on the memory layout of the TI calculators, and couldn't really shoe-horn the ROM dump into a 64KB RAM, so left it out entirely. That limits my options as to what I can show, but here's my Microhertz demo -



I've added native support for functions in Brass.

The old Brass could do some function-type things using directives; for example, compare the two source files here:

Quote:
.fopen fhnd, "test.txt"          ; Opens 'test.txt' and stores a handle in fhnd
.fsize fhnd, test_size ; Stores the size of the file in test_size

.for i, 1, test_size
.fread fhnd, chr ; Read a byte and store it as "chr"
.if chr >= 'a' && chr <= 'z' ; Is it a lowercase character?
.db chr + 'A' - 'a'
.else
.db chr
.endif
.loop

.fclose fhnd ; Close our file handle.

I personally find that rather messy. Here's the new version, using a variety of functions from the 'File Operations' plugin I've been writing:

Quote:
fhnd = fopen("test.txt", r)

#while !feof(fhnd)
chr = freadbyte(fhnd)
.if chr >= 'a' && data <= 'z'
.db chr + 'A' - 'a'
.else
.db chr
.endif
#loop

fclose(fhnd)
I find that a lot more readable.

An extreme example is the generation of trig tables. Brass 1 uses a series of directives to try and make this easier.
Quote:
.dbsin angles_in_circle, amplitude_of_wave, start_angle, end_angle, angle_step, DC_offset

Remembering that is not exactly what I'd call easy. If you saw the line of code:
Quote:
.dbsin 256, 127, 0, 63, 1, 32

...what would you think it did? You'd have to consult the manual, something I'm strongly opposed to. However, this code, which compiles under Brass 2, should be much clearer:
Quote:
#for theta = 0, theta < 360, ++theta
.db min(127, round(128 * sin(deg2rad(theta))))
#loop


By registering new plugins at runtime, you can construct an elaborate pair of directives - in this case .function and .endfunction - to allow users to declare their own.
Quote:
_PutS = $450A

.function bcall(label)
rst $28
.dw label
.endfunction

bcall(_PutS)


You can return values the BASIC way;
Quote:
.function slow_mul(op1, op2)
slow_mul = 0
.rept abs(op1)
.if sign(op1) == 1
slow_mul += op2
.else
slow_mul -= op2
.endif
.loop
.endfunction

.echo slow_mul(log(100, 10), slow_mul(5, 4))


I had a thought (as you do) that it would be interesting to see how well a TI game would run on the Sega Master System. After all, they share the CPU, albeit at ~3.5MHz on the SMS.

However, there are some other differences...

  • Completely different video hardware.
  • Completely different input hardware.
  • 8KB RAM rather than 32KB RAM.
  • No TIOS.

The first problem was the easiest to conquer. The SMS has a background layer, broken up into 8x8 tiles. If I wrote a 12x8 pattern of tiles onto the SMS background layer, and modified the tile data in my own implementation of _grBufCpy routine, I could simulate the TI's bitmapped LCD display (programs using direct LCD control would not be possible).

You can only dump so much data to the VRAM during the active display - it is much safer to only write to the VRAM outside of the active display period. I can give myself a lot more of this by switching off the display above and below the small 96x64 window I'll be rendering to; it's enough to perform two blocks, the left half of the display in one frame, the right in the next.

As for the input, that's not so bad. Writing my own _getK which returned TI-like codes for the 6 SMS buttons (Up, Down, Left, Right, 1 and 2) was fine, but games that used direct input were a bit stuck. I resolved this by writing an Out1 and In1 function that has to be called and simulates the TI keypad hardware, mapping Up/Down/Left/Right/2nd/Alpha to Up/Down/Left/Right/1/2.

The RAM issue can't be resolved easily. Copying some chunks of code to RAM (for self-modifying reasons) was necessary in some cases. As for the lack of the TIOS, there's no option but to write my own implementation of missing functions or dummy functions that don't do anything.

Even with the above, it's still not perfect. If I leave the object code in Gemini, the graphics are corrupted after a couple of seconds of play. I think the stack is overwriting some of the code I've copied to RAM.


Gemini on the Game Gear


No enemies make it a pretty bad 'game', but I thought it was an entertaining experiment.
benryves

Sega Tween

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



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.



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;


Click for animated GIF





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:

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


...produces this in old Brass:

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

Quote:
Brass Assembler - Copyright (C) Bee Development 2005-2007
-------------------------------------------------------
ZiLOG Z80 - Copyright (C) Bee Development 2005-2006
TI Program Files - Copyright (C) Bee Development 2005-2006
Core Plugins - Copyright (C) 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 50x speed increase!
benryves
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.


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:

/// Flags corresponding to which buttons are pressed.
[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:

/// Gets the status of the buttons from the connected SMS joypad.
/// The status of the buttons.
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'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 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 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:

benryves

Sega BASIC



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



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

SG-1000

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



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


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.



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.



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):



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:

benryves

VDP Interrupts



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



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



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:



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.



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.



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.







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

Turning Japanese



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:



The Flash now works too.



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



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.



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



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.



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.



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



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]
benryves


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.



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.



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.



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.



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



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



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:





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

New Z80 emulator



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.


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


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


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


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.


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


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


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

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]

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.

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


Not reading the Start button is a common affliction.


Here's the Game Gear DOOM for Scet. [smile]


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


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




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


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


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


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


Aypok's Digger Chan appears to play fine.


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

Screenshots galore

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

Sega Master System



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



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

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

Sega: Enter the Pies

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



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.



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



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



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



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 ,(nnnn)............OK
ld ,(nnnn)............OK
ld ,nnnn..............OK
ld (+1),nn............OK
ld ,nn......OK
ld a,(nnnn) / ld (nnnn),a....OK
ldd (1)...................OK
ldd (2)...................OK
ldi (1)...................OK
ldi (2)...................OK
ld a,<(bc),(de)>.............OK
ld (nnnn),............OK
ld ,nnnn........OK
ld ,nn...OK
ld (nnnn),............OK
ld (),a...............OK
ld (+1),a.............OK
ld a,(+1).............OK
shf/rot (+1)..........OK
ld ,(+1).........OK
ld (+1),.........OK
ld ,(+1).....OK
ld (+1),.....OK
c..................OK
de.................OK
hl.................OK
ix.................OK
iy.................OK
sp.................OK
n,(+1)......OK
bit n,(+1)............OK
a..................OK
b..................OK
bc.................OK
d..................OK
e..................OK
h..................OK
l..................OK
(hl)...............OK
ixh................OK
ixl................OK
iyh................OK
iyl................OK
ld ,.......OK
cpd.......................OK
cpi.......................OK
(+1)........OK
..........OK
shf/rot .OK
ld ,.......OK
....................OK
n,....OK
neg..........................OK
add hl,.........OK
add ix,.........OK
add iy,.........OK
aluop a,nn................... CRC:04d9a31f expected:48799360
hl,... CRC:2eaa987f expected:f39089a0
bit n,...OK
............ CRC:43c2ed53 expected:9b4ba675
aluop a,(+1).......... CRC:a7921163 expected:2bc2d52d
aluop a,.... CRC:c803aff7 expected:a4026d5a
aluop 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.



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 64us granularity - which is larger than a conventional 44.1kHz sample (23us), so PWM sound might not work very well.



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.

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



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:


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


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


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


Of course, these are the most exciting screenshots:





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.
benryves
In light of the TMDC, I thought I'd package up the .NET class for easy text-mode graphics I'd been throwing together.


Click for big


Once initialised, all you need are two things; the Graphics object it provides to handle your drawing, and the Refresh() method to update the console with whatever it is you've been composing.

It relies on a fairly large palette (128KB) which is hugely wasted in the current version - it maps every 16-bit colour value (R5G6B5) to an attribute/character pair. For the purpose of not looking extremely ugly, the palette provided only uses a few basic characters, not the full range, hence that 128KB could be reduced somewhat.

If you want to provide your own palette, it's just a sequence of two byte pairs (character then attribute) for each of the 16-bit colour values.

You'll need to compile with the unsafe flag set.

Download VC? project (with a quick-and-dirty sample program - an oldschool flame that spins around, some curves and some alpha-blended text). ClearType does make the text look a bit odd.

// Set window to 80x50
TextSharp TS = new TextSharp(80, 50);

// Set window title text
TS.Title = "TextSharp Demo";

// Set up some pleasant antialiasing/filtering
TS.Graphics.SmoothingMode = SmoothingMode.HighQuality;
TS.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;

// Now use the Graphics object like any other
TS.Graphics.Clear(Color.White);
TS.Graphics.DrawLine(Pens.Black, 0, 0, 80, 50);

// Update console window
TS.Refresh();


The document browser progresses (slowly) - it now exports and imports to/from a single file (the .docpack) and has a nifty search-as-you-type index.



Why is it that all you need to do to solve all your .NET WinForms woes is to make the control invisible then visible again? When clearing/adding large numbers of nodes to a TreeView control, you get a dancing scrollbar that pauses the app for a second or so - make the control invisible then visible again and it's instant. (This is using Clear and AddRange - it's not as if I'm adding them one-by-one).
benryves

Documentation

This is a standalone application I've been sporadically working on which will be bundled with Latenite, but is perfectly usable away from it.







It's a plugin-extendable document browser. You can use it to manage a library of documents and display them in the tabbed browser on the right. It comes with plugins supporting text files, Rich Text documents, and anything IE supports (so in this case it's HTML and PDF).

I've also wrapped the XP Picture and Fax viewer as another plugin, but am having a few problems with that so I'm not sure if I'll end up writing my own image viewer. Multipage TIFFs would be nice, though.

Plugins can expose their own toolbars - hence the text file has a dropdown button with a list of encodings, which has been very handy for those hundreds of DOS text files I have knocking around.

The library is stored in an XML document, and I'm intending on devising a format for easy distribution of documentation - probably something as simple as a Zip archive containing the relevant files and a small XML document that can be merged with the main library one.

Two nifty links, if you didn't know about them already - famfamfam's glorious "Silk Icons" set and the oh-so-useful SkyBound Visual styles (notice how my tabs on the left of the screenshots render correctly, even though they're upside down).
benryves

It lives..!

Thanks for the support, MrEvil, and thanks for noticing my rather dead tutorials section, linkofazeroth. [smile]

Sadly, I've been horribly busy recently - and so very little progress on any project. However, Brass 2 is starting to come together...


Can you guess which device the code is for?


Well, it'll never be an IOTD, but it's something. With the syntax fixed, it does actually work (and outputs a valid binary). I've been trying to work out the syntax used (with valuable input from CoBB over at MaxCoderz). Currently it operates by loading the entire document, trying to work out what each "command" is, before running it and executing the various commands. This has one big problem I can see - macros won't work, as they need to operate on the tokens before they're executed. This is fine, but to declare a macro you'd need to use, for example, a .define directive - which doesn't get executed until long after the source has been loaded and broken into tokens, expressions and commands.

Lack of macros and a horribly incomplete Z80 assembler plugin mean that so far I've been unable to test alongside old calculator Z80 source.
benryves

Latenite and Brass 2



Both Latenite and Brass are getting a significant upgrade - and both are being written from scratch.

Both will sport a plugin-based architecture. This is most obvious with Brass - where pretty much everything - be it an assembler plugin or output plugin - can be extended by writing your own custom plugins. All Brass does is parse the basic syntax and pass it to the various plugins to work out what to do with it!

Latenite will load Brass and use it to provide feedback - such as error reporting and syntax highlighting - directly to the user.

General discussion is handled here; for what I mean with regards to Brass plugins, there's this post.

The basic idea is that you can plug in your own assembler and use it alongside Brass and Latenite.
benryves

Internal PS/2 Port

What's that in the bottom left hand corner?



Kerm Martian has added a PS/2 port to his calculator (click the picture for better pictures and the original thread). He has been developing a shell, entitled DoorsCS (click for website) which sports an impressive set of GUI controls - hence the mouse!