EWM Update: Lua Integration

After an almost eight month interruption by mostly work and life in general, I am finally, and happily, back here writing about something new I added to EWM, my Apple II+ emulator.

I always thought it would be interesting to integrate a scripting language in the emulator, so that you can write little (or big) hacks to modify the behaviour of not just the emulator but also the software it runs.

Today I made a gentle start with that with Introduce Lua Support #148, which lays the foundation for Lua 5.2 support into EWM.

For this release I have only implemented instruction interception; this means you can run a Lua function before or after a specific 6502/65C02 instruction is executed.

That is not super exciting but it does open the door to some cool and simple hacks that were previously only possible by patching software the hard way. Now you can run a script on top of the original software without having to make persistent modifications.

In later releases I am also going to make it possible to intercept reads and writes to memory, interact with EWM, and maybe even more specific things like callbacks for things that happen in the disk controller.

Here is a small example function that uses the onAfterExecuteInstruction callback to change the keybindings for Frogger.

-- Frogger.lua - Change the default Frogger key bindings. It uses A/Z
-- and the arrow keys to move around, but we prefer J/L and I/K
-- instead.
-- A typical loop to wait for a key press looks like this:
--   GETKEY: LDA $C000   ; AD 00 C0
--           CMP #$80    ; C9 80
--           BCC GETKEY  ; 90 F9
--           STA $C010   ; 8D 10 C0
-- Load the value from $C000, and if it is greater than $80, a new
-- key press was recorded. To clear this value, strobe $C010.
-- So to change key input, all we have to do is intercept the LDA
-- $C000 and look at the value in the accumulator and change it to a
-- new value.

LDA_abs = 0xad
KBD = 0xc000

cpu:onAfterExecuteInstruction(LDA_abs, function(cpu, opcode, operand)
   if operand == KBD then
      if cpu.a == 0xc9 then
         cpu.a = 0xc1 -- I -> A
      elseif cpu.a == 0xcb then
         cpu.a = 0xda -- K -> Z
      elseif cpu.a == 0xca then
         cpu.a = 0x88 -- J -> Left
      elseif cpu.a == 0xcc then
         cpu.a = 0x95 -- L -> Right

I have filed some tickets under the lua tag for followup work.

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: