How useful closures are!

drhyde on 2008-02-23T15:16:35

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.