An Ode to Sad Mac

Here is a more official build of the Sad Mac screen saver that I wrote last year. It is now properly code-signed and should install and run without any permission issues on macOS 10.12 and newer - Download SadMac.saver.zip

Sad Mac Screen Saver

Most of my work is open source. You can see all projects at github.com/st3fan - feel free to leave a bug report or open a feature request.

Screen Savers

I recently wrote three small screen savers for macOS. That was a ton of fun. There is something therapeutic about it. I am making them all available as open source. Let me know what you think.

Sad mac

This one is an ode to the classic Mac. The Sad Mac would show up if it’s internal hardware test failed. Like when you had a bad RAM chip.

Sad Mac Screen Saver

Code-wise it is a very basic screen saver actually. Just drop sprites on the floor and let them pile up. The thing that took some time was to get the physics parameters right to make it feel natural.

Puzzle

The Sliding Puzzle from After Dark was probably my favorite back in the day. It just worked so well and smooth. And I think I specially loved the ones that messed with your actual screen.

Puzzle Screen Saver

My re-implementation simply chops up a screenshot and then turns the pieces into SpriteKit nodes. Those can then easily be moved around. SpriteKit is pretty amazing. I have found it very simple to turn ideas into working prototypes.

Mozilla

This one shows the new Mozilla logo and drops two sizes of Firefox (Focus/Nightly) icons on it. There is lots of bouncing and I spin the logo around if too many pile up.

Mozilla Screen Saver

Since the logo is not a simple rectangular bounding box, I had to create an outline for the collission detection. I used a nice little web based tool that let me draw the path on top of the image and generated Objective-C code that.

Most of my work is open source. You can see all projects at github.com/st3fan - feel free to leave a bug report or open a feature request.

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.

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
      end
   end
end)

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.

EWM Update: Bootloader

I just landed a patch that adds a bootloader to EWM. Now when you start it without telling it what specific machine to emulate, you will see the following:

EWM Bootloader

There is no support for a Character ROM that has lowercase so you have to be a little creative when you use the Github link in it. Or maybe I should make the link clickable?

This is most likely how the Raspberry Pi edition of EWM will start. Which may actually be the first thing I release to the general public.

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

EWM Update: Optimizing Memory Access

I landed a little patch to optimize memory access to the first two pages of memory: the zero page and the stack.

The struct cpu_t now keeps pointers to the memory areas where these pages are kept, so operations on them can access the memory directly instead of going through the memory abstraction.

The speedup is specially noticable on the Raspberry Pi Zero:

Before

pi@raspberrypi:~/Projects/ewm/src $ ./cpu_test
TEST Running 6502 tests
TEST   Success; executed 88957316 cycles in 8.7700 at 10.1434 MHz
TEST Running 65C02 tests
TEST   Success; executed 65094140 cycles in 6.2140 at 10.4754 MHz

After

pi@raspberrypi:~/Projects/ewm/src $ ./cpu_test
TEST Running 6502 tests
TEST   Success; executed 88957316 cycles in 6.0150 at 14.7892 MHz
TEST Running 65C02 tests
TEST   Success; executed 65094140 cycles in 4.2930 at 15.1629 MHz

That is a pretty big jump in unthrottled CPU speed. I think this patch, together with the Software Renderer that I still have in progress will make EWM run pretty well on the Raspberry Pi Zero.

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

EWM Update: Small Things

Here is an overview of some small improvements I made this week. It has been hard to find quality time to work on this project so I am not going as fast as during the Christmas break.

The Apple 1 emulation in EWM now blinks the cursor. I did not know that it blinked until I saw some videos on YouTube.

I probably got the frequency wrong.

Control keys

In the Apple ][+ emulation, I now properly support Control-A to Control-Z. I tested this with the Apple II+ Dealer Diagnostics disk, which has a keyboard hardware test program. There is no date on it, but it is probably 1978 or so.

Disk images in .nib format

EWM can now load disk images in the .nib format. This is a format that does not just contain a dump of the track and sector data. Instead it has the raw encoded disk contents. It turned out that is the exact same internal format that EWM uses for the disk emulation code, so this was a pretty simple thing to do.

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

EWM Update: Raspberry Pi Zero

It has been difficult to find the time to hack on EWM since I went back to work. Busy busy. But I did manage to make some small improvements to better support the Raspberry Pi Zero.

Raspberry Pi Zero on an Apple IIe

The Raspberry Pi Zero is a tiny single board computer with a 1 GHz single-core ARMv6 CPU, 512MB RAM and HDMI and USB connections. They are cheap, around $7 CAD plus shipping, and are capable of running Linux.

Sounds ideal for an emulator. But how fast are they?

To find out, I modified the cpu_test program included with EWM to output the raw CPU speed:

pi@raspberrypi:~/Projects/ewm/src $ ./cpu_test
TEST Running 6502 tests
TEST   Success; executed 88957316 cycles in 8.7700 at 10.1434 MHz
TEST Running 65C02 tests
TEST   Success; executed 65094140 cycles in 6.2140 at 10.4754 MHz

So about 10x the speed of an Apple ][+. Not bad, considering I have spent zero time optimizing the CPU emulation so far. And it is all written in simple portable C without crazy tricks.

Graphics is more interesting. The Pi Zero has no hardware accelerated graphics, at least not supported in Linux, so I have been working on a pure software renderer for text, lores and hires screen. It does not depend on OpenGL and just blits pixels to screen buffers.

This is what the performance looks like when I run the scr_test program:

pi@raspberrypi:~/Projects/ewm/src $ ./scr_test
txt_full_refresh      green      95.103/refresh
txt_full_refresh      color      24.554/refresh
lgr_full_refresh      green      17.619/refresh
lgr_full_refresh      color      17.648/refresh
hgr_full_refresh      green      19.561/refresh
hgr_full_refresh      color      35.250/refresh

(Green is monochrome mode, and it is slower for text because the way I let SDL change the color of the default white text characters. Probably easy to fix.)

The default scheduler in EWM is set to 30 FPS, which means one timeslot in the ‘scheduler’ is 33 milliseconds.

Of those we will need about 5 milliseconds for executing enough CPU instructions and another 5 for random stuff that happens in the event loop.

That leaves just about enough time for a full screen refresh. But it is a bit tight and I’ll be working on getting those numbers down. It will be better if all modes render in at most 20 milliseconds or so.

Work in progress at github.com/st3fan/ewm/issues/120

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

EWM Update: Linux Support

Emulated Woz Machine, my Apple 1 / Apple ][+ emulator now runs succesfully on Linux. Here is a screenshot of it running on Ubuntu 16.04.1.

EWM Running on Linux

This was not very difficult to get going. Using SDL 2.0 to abstract everything screen, keyboard and event loop related was a good call.

I wrote this emulator in portable C, using just SDL and POSIX APIs. There were just a few small compilation issues that I had to fix. Mostly differences between Clang and GCC.

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

EWM Update: Boot Loader

EWM supports multiple machine profiles. Currently the Apple 1, Replica 1 and the Apple ][+. You can start an emulator with a specific machine profile easily from the command line:

ewm one --model=replica1
ewm two --model=apple2plus --drive1=choplifter.dsk

This is useful, but I think there is probably a more common case where people start the emulator by double clicking or tapping an app icon or maybe even auto-boot it on a Raspberry Pi.

So I am thinking about implementing a simple boot loader that runs when you start EWM without any arguments. Here is a first try:

EWM Boot Loader

(Eventually I would like to be able to fully save and restore the state of the emulator and give people the option to start from a saved emulator.)

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