As some of you may be aware, I'm working on a pure-perl Z80 emulator. Why? <shrug> Why not! And we can't go letting the 6502 boys have all the fun!
It also makes a very good example of how useful closures can be. Here's just one example.
The Z80 is an 8 bit processor, with several 8 bit registers. But it also has some 16 bit "reigster pairs", which are made up of two 8 bit registers that can be treated as a single larger register. For example, the 8 bit register B and C can be combined to make a 16 bit register BC. Any operation that changes B or C also changes BC, and vice versa. Now, to implement them, I could have lots of hairy logic so that any instruction that operates on BC actually operates on both B and C. But that would lead to lots of duplication of code, for all the register pairs, and for all the instructions that operate on 'em.
I already have a ::Register8 class for an 8 bit register, and a Register16 class for real 16 bit registers such as the Program Counter (a register that points at the next instruction to execute) which store a value and have get() and set() methods. So, I thought, instead of putting lots of hairy logic in the instructions themselves, I could move it into the registers so I'd only have to write it once. The Register* classes already have a 'value' field for storing the register's current value. So I changed Register16 to have either a value field or a pair of 'get' and 'set' fields, depending on how the register was initialised in perl. If it has a value field, then get() and set() simply operate on that. But if instead it has get/set fields, then get() and set() call those code-refs instead.
So now, I can create a 16 bit register pair like this ...
my $BC = CPU::Emulator::Z80::Register16->new(
get => sub {
return 256 * $self->register('B')->get() +
$self->register('C')->get()
},
set => sub {
my $value = shift;
$self->register('B')->set($value >>8);
$self->register('C')->set($value & 0xFF);
}
);
and then as far as implementing the actual Z80 instructions goes, I can just get() and set() sixteen bit registers with a single method call, instead of doing all the multiplication, shifting, bitmasking and so on every time. Note that I use a variable $self
inside the two anonymous subroutines without declaring it with my
. This isn't a bug. That is the $self
in the code that creates the anonysubs. It has nothing to do with whatever
$self
might be inside a Register16 class's get() or set() method.
But there are actually several register-pairs, so I went one step further. Instead of repeating that chunk of code several times, once for each pair, I did this:
$AF = _derive_register16($self, qw(A F));
$BC = _derive_register16($self, qw(B C));
$DE = _derive_register16($self, qw(D E));
...
sub _derive_register16 {
my($self, $high, $low) = @_;
return CPU::Emulator::Z80::Register16->new(
get => sub {
return 256 * $self->register($high)->get() +
$self->register($low)->get()
},
set => sub {
my $value = shift;
$self->register($high)->set($value >>8);
$self->register($low)->set($value & 0xFF);
},
);
}
In this case, I pass $self
and the names of two 8 bit registers to
the _derive_register16
function. That function then creates and returns a Register16 now "closing over" 3 variables.
Closures rock. By combining objects and closures I have ended up writing a lot less code. And by reducing the amount of duplication in my code, when I inevitably find a bug, I'll have just one place to fix it, instead of having to remember all the hundred places where I twiddle register pairs. If you ever find yourself writing the same code over and over again just with minimally different data, this is a useful technique.
You can see what I'm up to on this project by looking at the CVS repository.