David Cantrell > CPU-Emulator-Z80 > CPU::Emulator::Z80::Manual

Download:
CPU-Emulator-Z80-1.0.tar.gz

Annotate this POD

View/Report Bugs
Source  

NAME ^

CPU::Emulator::Z80::Manual

DESCRIPTION ^

CPU::Emulator::Z80 implements a Z80 emulator. See its manpage for nitty-gritty details about interfacing with it. This manpage, by contrast, serves as a HOWTO.

WHAT ELSE DO I NEED? ^

First of all, you need perl 5.6. You will also need a few extra perl modules. Those will be automatically installed for you if you use the CPAN.pm module to install this.

If you want to write your own software that uses this module, then you will need to know how to program a Z80 processor. The rest of this document assumes that you do.

HOW DO I CREATE A CPU? ^

    my $cpu = CPU::Emulator::Z80->new();

This will initialise it with 64K of banked memory attached, and set all registers to zero. If you want a different memory model, then pass a CPU::Emulator::Memory object to the constructor using the 'memory' parameter:

    my $cpu = CPU::Emulator::Z80->new(
        memory => CPU::Emulator::Memory->new(
            size => 0x4000,
            file => 'memory.ram'
        )
    );

Generally, the default is what you want, but note that the default configuration does *not* backup the CPU's RAM to a file. Note that if you define your own memory, it *must* be little-endian.

HOW DO IT GET A PROGRAM INTO MEMORY ^

The hard way, which is also most suitable for very small hacks, is to peek() and poke() the memory directly. You can get at the memory using the memory() method.

Alternatively, you can initialise RAM from a file by creating your own memory object, or from a string by passing a scalar with the 'memory' parameter:

    my $cpu = CPU::Emulator::Z80->new(
        memory => 'blob of impenetrable binary gibberish'
    );

If you are using banked memory then you can bank() a ROM image file.

HOW DO I RUN A PROGRAM? ^

After loading a program into memory, use the run() method. With no parameters it will run forever, starting wherever the PC points. Pass a number to it and it will run for that many instructions.

Note that the repeating instructions like LDIR count as several instructions. That's because they are really just a non-repeating instruction, plus a conditional decrement of PC, so the same instruction gets executed several times. This is the correct behaviour.

The astute reader will have noticed that you can single-step your Z80 programs with run(1), and consequently stop "half way through" a repeating instruction.

The run() method will also terminate if it executes a STOP instruction.

WHAT THE HELL IS THE 'STOP' INSTRUCTION? ^

It's something I added cos it's useful for emulators. It's a three byte instruction, the first two bytes being any combination of 0xDD and 0xFD, the third being 0x00. Strictly speaking this breaks compatibility with real Z80s, as the two byte prefix is legal but stupid. If this is a problem for you please contact me and we can try to find a work-around.

You can tell the difference between the CPU stopping because it reached a STOP instruction and for any other reason by using the stopped() method.

HOW DO I GET DATA OUT? ^

The hard way (which, again, is also the most suitable for small hacks) is to peek() the memory directly. You can also inspect register contents:

    print "A register is ".$cpu->register('A')->get();

See also "I/O and Interrupts" below.

HOW DO I ALTER A RUNNING PROGRAM? ^

You can poke() values into memory at any time. You can also fiddle with registers' values:

    $cpu->register('BC')->set(0xBEEF);
    $cpu->register('F')->set(0);

The flags register can, as well as being set to a numeric value, have its individual bits twiddled:

    $cpu->register('F')->setZ(0);  # set the Z flag to 0 (false)
    $cpu->register('F')->setZ();   # set the Z flag (set defaults to 1)
    $cpu->register('F')->resetZ(); # reset the Z flag
    $cpu->register('F')->getZ();   # get the Z flag (true or false)

There is a full set (ha ha) of set/get/reset methods for each flag bit, accessed by appending the flag's name to the method name. Flags are:

S - sign
Z - zero
H - half-carry
P - parity or overflow
N - negative
C - carry

and also the undocumented bits 3 and 5 of the register:

3 - bit 3 (with the least-significant bit being bit 0)
5 - bit 5

It's worth noting that bits 3 and 5 are emulated correctly, so don't go storing anything in them that you care about.

I/O and INTERRUPTS ^

INTERRUPTS

For now, only IM 1 and NMIs are supported. You can generate a non-maskable interrupt using the nmi() method, and a maskable interrupt using the interrupt() method.

The nmi() method will execute a CALL 0x0066 immediately after the current instruction has completed. Note that maskable interrupts are disabled until either an EI instruction or you reach RETN, at which point maskable interrupts will be restored to whatever state they were in when nmi() was called.

If maskable interrupts are enabled (they are disabled at power-on) then the interrupt() method will return true, and the next instructions will be DI and RST 0x38. You will need to re-enable interrupts at the earliest opportunity in your interrupt handler.

Because only IM 1 is supported, interrupts can't tell the CPU what to do. This means that you'll have to write your own interrupt handler that polls any I/O devices you have defined.

Given that interrupts need to be generated by calling a method from perl, and that the run() method doesn't normally return, you will need to find some way of generating them asychronously.

INPUT

You can create an input device using the add_input_device() method.

OUTPUT

You can create an output device using the add_output_device() method.

AN EXAMPLE

Here's an example of how to make the keyboard available to the emulator. It generates an interrupt every 0.1 seconds, and also feeds key-presses into a buffer. The input function for port 0xC001 in the emulator will return values from that buffer, and that for port 0xC000 will return how many values are in the buffer.

    use Time::HiRes qw(setitimer ITIMER_REAL);
    use Term::ReadKey;
    
    ...

    my @kb_buffer;
    $cpu->add_input_device(
        address  => 0xC000,
        function => sub { $#kb_buffer + 1 }
    );
    $cpu->add_input_device(
        address  => 0xC001,
        function => sub { shift(@kb_buffer) || 0 }
    );
    setitimer(ITIMER_REAL, 1, 0.1); # after 1 sec, int every 0.1 sec
    $SIG{ALRM} = sub {
        my $key = ReadKey(-1);
        push @kb_buffer, $key if($key);
        $cpu->interrupt();
    };

    ReadMode 'noecho';
    ReadMode 'cbreak';
    $cpu->run();
    $SIG{ALRM} = 'IGNORE';
    ReadMode'normal';

SEE ALSO ^

CPU::Emulator::Z80

CPU::Emulator::Memory

CPU::Z80::Assembler

AUTHOR, LICENCE and COPYRIGHT ^

Copyright 2008 David Cantrell <david@cantrell.org.uk>

This documentation is free-as-in-speech. It may be used, distributed and modified under the terms of the Creative Commons Attribution-Share Alike 2.0 UK: England & Wales License, whose text you may read at http://creativecommons.org/licenses/by-sa/2.0/uk/.

CONSPIRACY ^

This is also free-as-in-mason documentation.

syntax highlighting: