EWM Update: Let's Get Cookin'!

Building on top of the Lua integration work that I landed recently in EWM, my Apple ][+ emulator, here are two small improvements and a fun use case to show those off.

EWM Bootloader

New Lua Callbacks

First, I have added two callbacks that let you intercept key presses. Not emulated presses, but EWM application key downs and ups. This means that you can now trigger scripts by connecting them to a key combination.

For example, say you want to dump the Zero Page when you hit Command-Z. That is now easy with just a few lines of code:

two:onKeyDown(function(two, mod, sym)
   if mod == KMOD_GUI then -- Command on macOS and Meta on Linux
      if sym == string.byte('z') then
         ewm:hexdump(0x0000, 0xff) -- Dump the zero page
         return true
      end
   end
end)

(The KMOD_GUI constant comes from SDL. I hope to have some abstraction for that at some point.)

Second, it is now possible to inspect and modify memory. The cpu global now has a memory property that you can index as an array:

-- Set the memory location at $300
cpu.memory[0x0300] = 1

-- Read the RESET vector
v = bit32.bor(cpu.memory[0xfffc], bit32.lshift(cpu.memory[0xfffd], 8))

(Bitwise operations in Lua are a bit of a pain unfortunately. I will probably introduce a more convenient API to access 16 bit values.)

Let’s build a cheat for Burger Time

Combined, these two new features can be used to build little cheats for games. Let’s try to build one for BurgerTime.

First let’s see if we can find where the game stores the number of chefs and pepper shots and see if we can modify those values.

So with this zero page dumper example in place, we hit Command-Z when the game has started. This is what we see:

0000: 1d 02 ae 1d 0c 00 d0 33 28 77 ae 00 00 b8 00 00
0010: 60 56 d5 00 00 00 00 00 00 00 00 00 00 00 00 00
0020: 00 02 00 b6 01 0a 00 00 01 00 01 01 63 10 0b 57
0030: a2 02 00 00 00 00 00 a3 1b fd 09 09 01 01 01 33
0040: 17 00 00 00 00 0c 07 00 00 09 e6 05 00 00 00 00
0050: 04 00 ea 00 00 00 00 00 00 00 00 00 00 00 00 00
0060: 73 96 00 00 3f 96 05 05 00 00 05 05 00 00 07 00
0070: 00 00 00 00 00 00 83 00 00 00 00 00 00 e2 a1 00
0080: 89 9a ab 9a 26 3d 3f 96 7f 7f 00 00 00 0f 25 1d
0090: 00 00 ff 07 00 1b 0f 03 1b 01 00 1a 0f 00 19 0f
00a0: 00 18 0f 0a 00 c1 00 00 00 00 00 00 00 00 00 00
00b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00d0: 00 0c 00 00 02 00 5c c6 00 00 00 00 00 00 00 00
00e0: c0 92 00 92 04 00 00 2e d0 3d 50 2a 50 26 50 22
00f0: d0 35 d0 39 00 17 00 00 00 00 00 00 00 00 eb 1c

What we are looking for is the value 05 for the number of lives or the number of pepper shots we have left. There are multiple occurences, so what we do is run into an enemy and see if the value changes somewhere:

Easily spotted, the value at 0x66 changed from 0x05 to 0x04.

...
0060: 73 96 00 00 3f 96 04 05 00 00 05 05 00 00 07 00
...                     ^^

And if we get caught once more, by Mr. Pickle, and then use four pepper shots, the values at 0x66 and 0x6a look like this:

...
0060: 73 96 00 00 3f 96 03 05 00 00 01 05 00 00 07 00
...                     ^^          ^^

I think we can safely say that we have found the following memory locations:

CUR_LIVES = 0x66
MAX_LIVES = 0x67
CUR_SHOTS = 0x6a
MAX_SHOTS = 0x6b

How do we change them? Simple, we can define another key combo that simply resets the memory locations to a bigger number:

two:onKeyDown(function(two, mod, sym)
   if mod == KMOD_GUI then
      if sym == string.byte('r') then
         cpu.memory[CUR_LIVES] = 7
         cpu.memory[MAX_LIVES] = 7
         cpu.memory[CUR_SHOTS] = 7
         cpu.memory[MAX_SHOTS] = 7
         return true;
      end
   end
   return false
end)

Now every time you press Command-R, your lives and pepper shots are set to 7.

EWM Bootloader

We can take this one step further and simply make sure that the CUR_LIVES and CUR_SHOTS never changes at all.

There are only a few instructions to store a value in the Zero Page, so with a little bit of trial and error it is pretty easy to find out that the values are set with an STA $66,X and STA $6A,X instructions.

We can intercept those instructons by setting up a callback with onBeforeExecuteInstruction and then in the callback we make sure that the value is always 0x05:

STA_zpg_X = 0x95 -- Opcode for STA $NN,X

cpu:onBeforeExecuteInstruction(STA_zpg_X, function(cpu, op, oper)
   -- If we are modifying the known locations ...
   if oper == CUR_LIVES or oper == CUR_SHOTS then
      cpu.a = 5 -- ... always store 0x05
   end
end)

Now when the chef gets caught, the number of lives just stays the same. Same with the pepper shots.

Note that we have not actually looked at the Burger Time code at all. We have just inspected memory and made a guess about how it changes those values. No disassembly required!

If you are interested in playing around with my emulator, you can find the project at github.com/st3fan/ewm.


This is a post in the Emulated Woz Machine series. Other posts in this series: