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.
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.
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.
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.
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.
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.
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.
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:
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.
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.
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.
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.
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:
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.
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.
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.
Blink that cursor
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.
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.
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.
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.
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 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:
(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.
Today I found out that ProDOS 2.4.1 crashes in my Apple ][+
emulator. Very early on when booting a disk, even before it displays
the welcome screen. This is odd because ProDOS 2.4.1 is supposed to be
compatible with any Apple II model that has 64KB of RAM.
After some sleuthing I figured out what is happening here. The big hint is what the emulator prints:
CPU: Exited because of unimplemented instruction 0x1a at 0x2095
This is a good hint. The Apple ][+ uses a 6502, and for that CPU there
is no instruction with opcode 0x1a. It does exist on the 65C02,
which later models of the Apple II family use, where it is the INC,
Increment Accumulator, instruction.
ProDOS 2.4.1 is supposed to run on all models, including the 6502, so
why is this happening? Are we trying to execute some random invalid
code in memory or is there something else happening?
Looking at the code around 0x2095 we can see this:
It is now pretty clear what this does. This is code to detect the CPU
type.
On a 6502 the $1A instruction is invalid, but it does does not halt
or interrupt the the CPU. On the 65C02 it increments the
accumulator. The above CPU detection code takes advantage of that: on
a 65C02, the accumulator wil increment to 1 and the branch to $20A9
will happen. On the 6502 the accumulator stays zero and the branch
will not happen.
The problem is that my emulator does not handle illegal instructions
correctly. It incorrectly triggers an interrupt, which puts the
program counter at 0x03FE, which is what we see in the screenshot
too.
(Ending up at 0x3FE is actually not correct since the interrupt
handler is at 0x03FB. So maybe that is another bug.)
I fixed this with a patch that does two things:
I brought back the --strict option for the emulator. If --strict
is enabled, then the emulator will immediate abort if it encounters an
invalid instruction. This is not correct behaviour, because the 6502
does not actually do this in real hardware, but it can be used o
verify that programs are correct.
I aliased all unimplemented 6502 instructions to nop. So they are
simply ignored. This is also not entirely correct because some of
those instructions have undocumented behaviour that some programs may
take advantage of.
For now this is a decent fix. ProDOS 2.4.1 boots again. Maybe some day
I will try to implement the undocumented behaviour of the 6502.
If you are interested in playing around with my emulator, you can find
the project at github.com/st3fan/ewm.