The CPU Registers
In the previous post we concluded by looking at some values in the CPU Registers. I will first explain shortly what they are and why they exists. We have seen a lot of memory values in the Z80 memory up until now. The processor is limited to what it can actually do with these memory values. It can read and write them and it can execute expressions interpreted by the values they contain. That is about it. The processor can’t do any calculations with these values directly. To do calculations (which it needs to do a lot), the values first need to be loaded into the CPU Registers. Then the processor can add, substract compare and do some other arithmetic operations with them. Most of the time, the result will be put back into the memory, making the register ready for the next arithmetic operation.
Each register has a special use and it is not advisable to use a register for the wrong operation. But we won’t go into much detail on that now. Now let’s see how the registers are used to calculate the new HP for the LYNXMAN when we hit him. The used registers are A, DE, HL, IX and IY. Register A is the Accumulator register, used for calculations. DE and HL are suitable for storing values and IX and IY are suitable for storing memory locations.
If you load the statefile from the previous post again and put a breakpoint at #658E and hit the first LYNXMAN we will see that the mentioned registers are filled with:
Register | Value | Description |
DE | 001F | The damage |
IX | 2300 | The source character (SELIOS) |
IY | 2400 | The destination character (LYNXMAN A) |
The registers A and HL are loaded with non-relevent data at this point.
If we look into the code, we see that first de contents of address iy+#04 and iy+#05 are loaded into the L and H parts of the HL register. Although values in memory are stored lowest significant number first, in the registers, it is the other way around, first the highest significant parts, as expected. So in this case, #2404 is loaded into the L part, and #2405 into the H part. Then the HL register contains the current HP of LYNXMAN A.

I don’t know what the and a expression is for at #67ec, so let’s ignore that for now. It does not seem to alter anything relevant anyway.
Then at #67ed is a substract statement sbc hl, de. This means the value of DE will be substracted from HL. Since HL contains the current HP and DE contains the damage, the result will be the new HP. So now HL contains the new HP.
Then at #67EF is a conditional jump: jr nc,#67f4. This states that if the carrier flag is not set (‘nc’ is short for ‘no carry’), we will jump to #67F4. This only skips the next statement at #67F1, but what is this carrier flag?
The Flag register
One of the CPU Register, the F register, is a special one. It contains information about the last operation that is performed. For example, the ‘Carrier’ part of the flag indicates whether the last operation resulted in a value that went out of range. This can happen if the resulting value is too large to be stored in the register, but can also happen if the resulting value will be below zero. Since the result can not be stored into the register, it will reset (like the mileage counter in a car) and the carrier flag will be set to indicate this.
Now if we imagine the damage being inflicted is bigger than the current HP of the hit character. Then the result of the subtraction in #67ED wont fit into the register and the result will be a very big value. Since this of course is not wanted functionality, the carrier flag is checked, and if it is set, the jump will not be performed and the next statement executed is at #67F1 ld hl,0000. So the HP will be set to 0.
After the optional resetting the HP to #0000, the following statements are to put the values of register HL back into memory again. The rest of the subroutine has a lot of logic for displaying inflicted damage and other things. But we will ignore that for now.
Changing the logic
Now let us change the logic to make every battle a one hit kill. We can achieve this by changing the logic to load a value of #0000 into register HL instead of the real HP stored in memory. So we will change the expressions at addresses #67E6 and #67E9. Now what do we want to put here? Instead of loading the register with the values from memory, we want to load it with the fixed value of #00. Now we need to look online for Z80 instructions set to find what the values are to put in memory. If, for example, you look at http://map.grauw.nl/resources/z80instr.php and search for ld h, you will see all expressions to load values into the h register.
The operation code for loading a fixed value into register H is apparently 26 followed by the amount. Since we want to put 00 in there, we need to put 26 and 00 at #67E6 and #67E7. The following address, #67E8, however, is also part of the old expression. So we need to erase that. We need to put 00 there too to make it a NOP statement.
The same goes for register L, the statement will be 2E followed by the value, so we will put that at #67E9 and #67EA, followed by a NOP again at #67EB.
Let’s enter all the values in the Memory window. This can only be done when the game is in debug mode.

If we continue execution of the program and hit (or be hit) by an enemy, and we step until address #67E6 again, we will see the altered statements.

I swapped the loading of the L and H registers, but that had no impact on program execution. Also, we could have put the second statement at #67E8 and #67E9, and put a nop at #67EA, which would make it a bit more readable.
Now if we play the game again, we see indeed every hit is a one hit kill. The downside, of course, is that also when we are being hit by an enemy, we are swooned in one hit. So this is not really a usable cheat.
An alert reader, ‘Benjamin’, pointed out to me that there is a much easier way to change the logic with the same end result that every hit will kill. Actually, there are many more options to do this. Look at the code again and try to come up with a solution. One answer can be found in the comments. In the next blogpost, we will investigate the rest of the subroutine. Then in the post after that we will re-implement the whole method which will require some more thinking and logic reasoning.
Would you get the same result of structural one-hit kills by changing 67EF to an NOP statement? That way, 67F1 would never be skipped and the outcome would always be as if the carrier flag was set?
Hi Benjamin. Thank you for your response.
Indeed, you are right! If you eliminate the optional jump, the register will always be overwritten with #00, which will result in immediate death. I will add this as a question to the post.