15

I'm currently developing an emulator for the good old GameBoy and I'm facing some problems understandig how some basic operation codes have to be implemented.

Right now I'm implementing the AND operations; the first few (0xA0 -> 0xA3; 0xA6 & 0xA7) are pretty straight forward but the AND operations for the registers H, L are a bit different.

You can download the documention of the z80 under this link: um0080.pdf (page 172)

Here are some examples to show what I mean (with pseudo-code) and basically what I do:

AND A,H (note the bit-shifting)

(read HL register; >> 8) save in cache C
R->C = R->HL >> 8;

perform AND operation with cache
AND_H(R->C);
R->A &= R->C;

AND A,L (note the bit-masking)

(read HL register; &0xFF) save in cache C
R->C = R->HL &0xFF;

I know all the bit-operations and I know what they do, but it seems that I can't figure out why its need to be done like that. I have some theories (correct me if I'm wrong :-)):

What I already understood is, that the registers H and L are basically the register HL, which is an 16-Bit register. Since the CPU/Bus can only handle 8-Bit operations, it needs to be splitted up; or the more logic suggestion: since its only one register, the values of H and L are masked in the register and they simply need to be separated from each other (higher/lower nibble?).

I would be deeply grateful if someone can make this more clear to me because I just want to have some more background knowledge (how all this stuff works internally) so its very important to me that I know what I'm doing.

hippietrail
  • 15,848
  • 18
  • 99
  • 158
Sepultura
  • 997
  • 1
  • 9
  • 28
  • 1
    It doesn't need to be done like that. Clearly if you're keeping HL together, you have to "split" it up when you use either half. An other options is keeping H and L separate and joining them together when HL is needed. – harold Mar 12 '12 at 19:33
  • It seems to me that you're understanding things pretty well. – Michael Burr Mar 12 '12 at 19:36
  • Have you examined existing Z80 emulator source code? – David Pointer Mar 12 '12 at 19:55
  • @harold: yeah, I already thought that it only depends on how you implement it; since its all virtually, you can do whatever you want, just the output (blackbox like) has to be legit. – Sepultura Mar 12 '12 at 20:31
  • @michael: hehe, thanks; that gives some hope - still learning hard – Sepultura Mar 12 '12 at 20:34
  • 1
    By the way, tat Zilog Z80 manual is a nice document, and I used it last summer as a reference when debugging a hardware problem occurring in the ADA MP-1 guitar preamp, 1987 vintage. :) – Kaz Mar 12 '12 at 22:39

1 Answers1

11

As comments have pointed out, the fact that the implementation you have found stores the H and L registers together as the 16-bit entity HL and then decomposes that into H by shifting right and into L by masking off is purely an implementation specific.

The original z80 has a 4-bit ALU (see Shima's comments starting at the bottom of Page 9 in this transcript of a Computer History Museum panel) so it would actually (i) AND the low four bits of L and the accumulator; then (ii) AND the high four bits of L and the accumulator. However it exposes its registers as discrete 8-bit entities so the internal implementation is entirely hidden.

HL is called a register pair because it's two registers taken together to make a 16-bit quantity. Ignoring the shadow and index registers, the original z80 actually has three of those — HL, BC and DE. BC and DE survive in the Gameboy's CPU as alternative pairs for indirect loading (such as opcode 0x1a — LD A, (BC)) and for 16-bit arithmetic (eg, 0x09 ADD HL, BC) and have a few other uses on a z80.

SP and PC are generally regarded as indivisible 16-bit registers (though, of course, you can divide them by storing them to memory and reading the bytes back individually) and AF exists for pushing and popping but F is such a special case that AF isn't usually particularly useful as a 16-bit integer.

Short summary then: you're not having problems with understanding how opcodes have to be implemented, merely how they have been implemented by a particular author.

Tommy
  • 99,986
  • 12
  • 185
  • 204
  • 2
    As it turned out, the IX and IY registers were/are _also_ individually byte-addressable, using undocumented opcodes. (The opcode prefixes for turning HL into IX/IY are 0xBD and 0xFD.) In essence, prefixing any other register-based operation with 0xBD turned a reference to (HL) into one of the form (IX+n) (n a single byte integer), but it also turned a reference to H into the high-half of IX, and a reference to L into the low half of IX. Likewise for IY and opcode-prefix FD. – torek Mar 13 '12 at 06:52
  • Thanks for adding reference to 4-bit ALU! +1 – guga Mar 13 '12 at 17:30
  • wow, thanks alot tommy! Now I know even more then I would have expected :-); and thanks for the link, very interesting document - totally worth reading it. – Sepultura Mar 13 '12 at 17:39