I don’t really have any experience with electronics so I’m starting the project by developing the emulator.
Since I’ve already gathered together the main chips I think I’ll be needing, I’ll be starting off by writing software equivalents of these chips in a similar fashion as the z80ex library works.
I’ve already written a little test-program to get a hang around how the library and the processor works.
#include <z80ex/z80ex.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <stdlib.h>
#include <time.h>
using namespace std;
Z80EX_CONTEXT *processor;
Z80EX_BYTE mainMemory[0x10000];
void loadMemory()
{
ifstream is("test.bin", ios::in | ios::binary);
is.read((char*) mainMemory, 0x10000);
}
Z80EX_BYTE memory_read(Z80EX_CONTEXT *cpu, Z80EX_WORD addr, int m1_state, void *user_data)
{
return mainMemory[addr];
}
void memory_write(Z80EX_CONTEXT *cpu, Z80EX_WORD addr, Z80EX_BYTE value, void *user_data)
{
mainMemory[addr]=value;
}
Z80EX_BYTE port_read(Z80EX_CONTEXT *cpu, Z80EX_WORD port, void *user_data)
{
if(int(port & 0xFF) == 23) /* Random Number Generator*/
return rand()%256;
return 0;
}
void port_write(Z80EX_CONTEXT *cpu, Z80EX_WORD port, Z80EX_BYTE value, void *user_data)
{
if(int(port & 0xFF) == 42)
cout<<int(value)<<endl;
}
Z80EX_BYTE interrupt_vector(Z80EX_CONTEXT *cpu, void *userdata)
{
cout<<"Interrupt!\n";
return 0;
}
int main()
{
loadMemory();
srand(time(0));
processor = z80ex_create(
memory_read, 0,
memory_write, 0,
port_read, 0,
port_write, 0,
interrupt_vector, 0
);
while(!z80ex_doing_halt(processor))
{
z80ex_step(processor);
}
z80ex_destroy(processor);
return 0;
}
Setting up the library was surprisingly easy as it only required a couple of callback functions and a way to read and write to the emulated memory which in this case is an array of bytes.
When I first started out with the emulator I was using a compiler that produced binary files in the intel hex-format. This a little bit tricker to load into the emulator, but I managed to write a simple enough parser to work my way through it, skipping parts like CRC.
I later decided to just go with plain binary as it allowed me to copy the binary data as one big chunk directly into memory with the stream.read() function. I’ll probably use the equivalent write() function later on when doing memory dumps.
Anyway, in this particular test I’ve written two hardware devices that can be used by the processor. One lies on port 23 and is an output device, it allows you to write numbers to std::out by sending them as bytes. This version only allows you to write 8-bit unsigned numbers and ends them with a newline.
The other is a random number generator on port 42. Reading from this port produces a random 8-bit number.
Using these two virtual devices I’ve written a little test program in assembler.
ld HL, 0x20
ld B, 0x10
ld C, 23
inir
ld HL, 0x20
ld B, 0x10
ld C, 42
otir
halt
The basic function of it is to read 16 random numbers from device 42, store them in memory from address 0×20 to 0×30, and then write these to port 23. This done using an index variable and the inir/otir operators which allow me to read/write blocks of memory with the aid of a counter.
When the program is done it halts, which also closes the emulator.
The first step in writing a proper emulator will probably be to create the video-output device, the TMS9928AN.
I’ll be recreating the behaviour and not the internal structure as it is unknown to me. Since I’ll be unable to test my implementation against the real deal until I’ve sourced all of the needed parts, I’ll be relying entirely on the documentation for this chip.
So far I’ve been thinking about doing the video output with SDL. The problem is going to come from how I load sprite-data from the processor into the library. If I’m allowed to write pixels myself, I could probably cheat by writing the draw routines myself, but this is going to be a lot slower so I’m going to have a look to see if there’s any way to directly create spritesheets at runtime.
If you want to take a look at my prototype intel-hex parser you can find it here. And if you want to follow the development of the source code you can find the github project here.
This is is something I’ve been thinking about for a while now. It’s definitely not an easy task, even more so because I don’t have very much training or knowledge in electronics.
I have however taken a course in Operating Systems, giving a basic knowledge of what components are needed and how they should interconnect, to form a functioning system.
I decided early that the z80 would be a good processor for building a computer from scratch with. The reasons for this is partly because it’s easy to acquire and solder and partly because there already exists a lot of projects around this chip, so there’s a lot of circuit diagrams and source files available on the net.
I’ve only written in two assembly languages previously, one was for the MIPS architecture, the other was the Hitachi H8.
For the MIPS processor, I worked entirely with an emulator, which made it easy to test the code, but didn’t give me any other hardware to work with. We did have to enable two functions in the processor, delayed loads and delayed branches. This was to emulate the pipeline employed in the processor and the quirks this generated.
With the Hitachi H8, we worked with the Lego MindStorm, where we had an assignment to rewrite the LCD driver of BrickOS in assembly. This was a little more practical as we had to write a driver for the i2c bus. This was a lot harder to debug because we did not have access to an emulator or debugger to make sure that it was executing correctly.
Getting the project started
For my project I’ve decided to work partly with an emulator and partly with hardware.
Since the emulator is going to emulate the computer I’ll end up building, I’m going to be making it myself. Not completely from scratch though, because that would take too much time and I don’t understand enough about the insides of the z80 in order to emulate it and it’s undocumented features completely.
I’ll be using the z80ex library to do the hard work of emulating the processor, while I write the software to emulate all the other hardware, like the screen, keyboard, memory etcetera.
For doing the screen I’ll probably end up using nCurses, because it seems to do what I need it to, like capturing single keystrokes, output to a terminal as well as allowing me to have multiple windows which would be useful for having hardware information on the side for step-by-step execution.
Writing the software
As part of the project I’ve decided to write a simple operating system around a micro kernel for doing basic tasks like process scheduling and driver management.
Before I start on this part I’ll probably want to take a good look at CP/M and similar OS’s, to get a feeling about how complex it needs to be and what features I might need to cover. So I should probably try to see if I can get CP/M to run in my emulator before I start on this part.
Building the hardware
I’ve ordered some chips on the web, mostly the Z80 and some of the helper chips. Like one serial and one parallel I/O, two timer chips and DMA.
On another store I also found a couple of interesting chips, like a AY-3-8910 audio processor and a TMS9928NL video processor.
The video chip is interesting because it seems to have been designed for what I’m trying to build. It operates on an 8-bit interface and has 16 colours (one of them being transparency), a text-mode and capabilities to control 32 sprites on the screen. So there’s definitely capabilities to make games with this, as well as be used as a terminal.
The audio-chip also has an 8-bit interface, should work nicely with the processor. What I’m wondering at the moment is whether I’ll need to have these chips running on the same clock or not. The z80 seems to be able to cope with any frequency as long as it’s below 2.5mhz.
One of the problems with the video processor though is that it does not output composite video as I originally planned. It does however output component video, which with an added chip will be able to output composite video. I would have bought the NTSC-version if it was available, since it outputs composite by default. They only had the PAL-version though and an extra chip is not really that big of a deal, but I’ll have to order it from somewhere else. Would also have been awesome if my TV would have accepted component-video directly, but alas…