2017-02-19 18:46:13 +00:00
|
|
|
Aiie!
|
|
|
|
=====
|
|
|
|
|
|
|
|
Aiie! is an Apple //e emulator, written ground-up for the Teensy
|
|
|
|
3.6.
|
|
|
|
|
|
|
|
The name comes from a game I used to play on the Apple //e back
|
|
|
|
around 1986 - Ali Baba and the Forty Thieves, published by Quality
|
|
|
|
Software in 1981.
|
|
|
|
|
|
|
|
http://crpgaddict.blogspot.com/2013/07/game-103-ali-baba-and-forty-thieves-1981.html
|
|
|
|
|
|
|
|
When characters in the game did damage to each other, they exclaimed
|
|
|
|
something like "HAH! JUST A SCRATCH!" or "AAARGH!" or "OH MA, I THINK
|
|
|
|
ITS MY TIME" [sic]. One of these exclamations was "AIIYEEEEE!!"
|
|
|
|
|
|
|
|
Build log:
|
|
|
|
----------
|
|
|
|
|
|
|
|
https://hackaday.io/project/19925-aiie-an-embedded-apple-e-emulator
|
|
|
|
|
2017-02-20 00:08:29 +00:00
|
|
|
Getting the ROMs
|
|
|
|
================
|
|
|
|
|
|
|
|
As with many emulators, you have to go get the ROMs yourself. I've got
|
|
|
|
the ROMs that I dumped out of my Apple //e. You can probably get yours
|
|
|
|
a lot easier.
|
|
|
|
|
2017-02-20 23:41:46 +00:00
|
|
|
There are three files that you'll need:
|
2017-02-20 00:08:29 +00:00
|
|
|
|
2017-02-20 00:15:40 +00:00
|
|
|
* apple2e.rom -- a 32k dump of the entire Apple //e ROM
|
|
|
|
* disk.rom -- a 256 byte dump of the DiskII controller ROM (16-sector P5)
|
2017-02-21 01:37:11 +00:00
|
|
|
* parallel.rom -- a 256 byte dump of the Apple Parallel Card
|
2017-02-20 00:08:29 +00:00
|
|
|
|
2017-02-20 23:41:46 +00:00
|
|
|
The MD5 sums of those files are:
|
2017-02-20 00:08:29 +00:00
|
|
|
|
2017-02-20 00:15:40 +00:00
|
|
|
* 003a780b461c96ae3e72861ed0f4d3d9 apple2e.rom
|
|
|
|
* 2020aa1413ff77fe29353f3ee72dc295 disk.rom
|
2017-02-20 23:41:46 +00:00
|
|
|
* 5902996f16dc78fc013f6e1db14805b3 parallel.rom
|
|
|
|
|
2017-02-20 00:08:29 +00:00
|
|
|
From those, the appropriate headers will be automatically generated by
|
|
|
|
"make roms" (or any other target that relies on the ROMs).
|
|
|
|
|
2017-02-20 18:55:16 +00:00
|
|
|
Building (for the Teensy)
|
|
|
|
=========================
|
|
|
|
|
|
|
|
The directory 'teensy' contains 'teensy.ino' - the Arduino development
|
|
|
|
environment project file. You'll need to open that up and compile from
|
|
|
|
within.
|
|
|
|
|
|
|
|
However.
|
|
|
|
|
|
|
|
I built this on a Mac, and I used a lot of symlinks because of
|
|
|
|
limitations in the Arduino IDE. There's no reason that shouldn't work
|
|
|
|
under Linux, but I have absolutely no idea what Windows will make of
|
|
|
|
it. I would expect trouble. No, I won't accept pull requests that
|
|
|
|
remove the symlinks and replace them with the bare files. Sorry.
|
|
|
|
|
|
|
|
Also, you'll have to build the ROM headers (above) with 'make roms'
|
|
|
|
before you can build the Teensy .ino file.
|
|
|
|
|
|
|
|
If anyone knows how to make the Arduino development environment do any
|
|
|
|
form of scripting that could be used to generate those headers, I'd
|
|
|
|
gladly adopt that instead of forcing folks to run the Perl script via
|
|
|
|
Makefile. And if you have a better way of dealing with subfolders of
|
|
|
|
code, with the Teensy-specific code segregated as it is, I'm all ears...
|
|
|
|
|
|
|
|
I compile this with optimization set to "Faster" for the Teensy 3.6 at
|
|
|
|
180MHz. There's no need to overclock the CPU -- but it does give
|
|
|
|
better video performance, all the way up to 240MHz. Do as you see fit
|
|
|
|
:)
|
|
|
|
|
|
|
|
Environment and Libraries
|
|
|
|
-------------------------
|
|
|
|
|
|
|
|
I built this with arduino 1.8.1 and TeensyDuino 1.35.
|
|
|
|
|
|
|
|
https://www.pjrc.com/teensy/td_download.html
|
|
|
|
|
|
|
|
These libraries I'm using right from Teensy's environment: TimerOne;
|
|
|
|
SPI; EEPROM; Time; Keypad.
|
|
|
|
|
|
|
|
There's an error in the Time library. On Macs, where the filesystem is
|
|
|
|
case-insensitive, you'll have to do something like this:
|
|
|
|
|
|
|
|
<pre>
|
|
|
|
$ mv /Applications/Arduino.app/Contents/Java//hardware/teensy/avr/libraries/Time/Time.h /Applications/Arduino.app/Contents/Java//hardware/teensy/avr/libraries/Time/_Time.h
|
|
|
|
</pre>
|
|
|
|
|
|
|
|
I'm also using these libraries that don't come with TeensyDuino:
|
|
|
|
|
|
|
|
### uSDFS ###
|
|
|
|
|
|
|
|
This has long filename support, where the Teensy SD library
|
|
|
|
doesn't. It's pretty messy code, though, and I hope to abandon it
|
|
|
|
eventually.
|
|
|
|
|
|
|
|
https://github.com/WMXZ-EU/uSDFS
|
|
|
|
|
|
|
|
### RingBuffer ###
|
|
|
|
|
|
|
|
My library for dealing with ring and circular buffers. Simplistic.
|
|
|
|
You'll need v1.2.0 or later.
|
|
|
|
|
|
|
|
https://github.com/JorjBauer/RingBuffer
|
|
|
|
|
|
|
|
### RadioHead ###
|
|
|
|
|
|
|
|
This library comes with TeensyDuino, but I found the version that's
|
|
|
|
with 1.35 hangs. Using the stock v1.70 seems to be fine.
|
|
|
|
|
|
|
|
http://www.airspayce.com/mikem/arduino/RadioHead
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Running (on the Teensy)
|
|
|
|
=======================
|
|
|
|
|
|
|
|
The reset/menu button brings up a BIOS menu:
|
|
|
|
|
|
|
|
Resume
|
|
|
|
Reset
|
|
|
|
Cold Reboot
|
|
|
|
Drop to Monitor
|
|
|
|
Display: RGB
|
|
|
|
Debug: off
|
|
|
|
Insert/Eject Disk 1
|
|
|
|
Insert/Eject Disk 2
|
|
|
|
Volume +
|
|
|
|
Volume -
|
|
|
|
|
|
|
|
Reset
|
|
|
|
-----
|
|
|
|
|
|
|
|
This is the same as control-reset on the actual hardware. If you
|
|
|
|
want to execute the Apple //e self-test, then hold down the two
|
|
|
|
joystick buttons; hit the reset/menu key; and select "Reset".
|
|
|
|
|
|
|
|
Cold Reboot
|
|
|
|
-----------
|
|
|
|
|
|
|
|
This resets much of the hardware to a default state and forces a
|
|
|
|
reboot. (You can get the self-test using this, too.)
|
|
|
|
|
|
|
|
Drop to Monitor
|
|
|
|
---------------
|
|
|
|
|
|
|
|
"Drop to Monitor" tries fairly hard to get you back to a monitor
|
|
|
|
prompt. Useful for debugging, probably not for much else.
|
|
|
|
|
|
|
|
Display
|
|
|
|
-------
|
|
|
|
|
|
|
|
"Display" has four values, and they're only really implemented for
|
|
|
|
text and hi-res modes (not for lo-res modes). To describe them, I have
|
|
|
|
to talk about the details of the Apple II display system.
|
|
|
|
|
|
|
|
In hires modes, the Apple II can only display certain colors in
|
|
|
|
certain horizontal pixel columns. Because of how the composite video
|
|
|
|
out works, the color "carries over" from one pixel to its neighbor;
|
|
|
|
multiple pixels turned on in a row makes them all white. Which means
|
|
|
|
that, if you're trying to display a picture in hires mode, you get
|
|
|
|
color artifacts on the edges of white areas.
|
|
|
|
|
|
|
|
The Apple Color Composite Monitor had a button on it that turned on
|
|
|
|
"Monochrome" mode, with the finer resolution necessary to display the
|
|
|
|
pixels without the color cast. So its two display modes would be the
|
|
|
|
ones I call "NTSC-like" and "Black and White."
|
|
|
|
|
|
|
|
There are two other video modes. The "RGB" mode (the default, because
|
|
|
|
it's my preference) shows the color pixels as they're actually drawn
|
|
|
|
in to memory. That means there can't be a solid field of, for example,
|
|
|
|
orange; there can only be vertical stripes of orange with black
|
|
|
|
between them.
|
|
|
|
|
|
|
|
The last mode is "Monochrome" which looks like the original "Monitor
|
|
|
|
II", a black-and-green display.
|
|
|
|
|
|
|
|
Debug
|
|
|
|
-----
|
|
|
|
|
|
|
|
This has several settings:
|
|
|
|
|
|
|
|
off
|
|
|
|
Show FPS
|
|
|
|
Show mem free
|
|
|
|
Show paddles
|
|
|
|
Show PC
|
|
|
|
Show cycles
|
|
|
|
Show battery
|
|
|
|
Show time
|
|
|
|
|
|
|
|
... these are all fairly self-explanatory.
|
|
|
|
|
|
|
|
|
2017-02-19 23:55:54 +00:00
|
|
|
Building (on a Mac)
|
|
|
|
===================
|
|
|
|
|
|
|
|
While this isn't the purpose of the emulator, it is functional, and is
|
|
|
|
my first test target for most of the work. With MacOS 10.11.6 and
|
|
|
|
Homebrew, you can build and run it like this:
|
|
|
|
|
2017-02-20 00:18:42 +00:00
|
|
|
<pre>
|
|
|
|
$ make opencv
|
|
|
|
$ ./aiie-opencv /path/to/disk.dsk
|
|
|
|
</pre>
|
2017-02-19 23:55:54 +00:00
|
|
|
|
|
|
|
As the name implies, this requires that OpenCV is installed and in
|
|
|
|
/usr/local/lib. I've done that with Homebrew like this
|
|
|
|
|
2017-02-20 00:18:42 +00:00
|
|
|
<pre>
|
|
|
|
$ brew install opencv
|
|
|
|
</pre>
|
2017-02-19 23:55:54 +00:00
|
|
|
|
|
|
|
"Why OpenCV?" you might ask. Well, it's just because I had code from
|
|
|
|
another project lying around that directly manipulated OpenCV bitmap
|
|
|
|
data. It's functional, and the Mac build is only about functional
|
|
|
|
testing (for me).
|
|
|
|
|
|
|
|
VM
|
|
|
|
==
|
|
|
|
|
|
|
|
The virtual machine architecture is broken in half - the virtual and
|
|
|
|
physical pieces. There's the root VM object (vm.h), which ties
|
|
|
|
together the MMU, virtual keyboard, and virtual display.
|
|
|
|
|
|
|
|
Then there are the physical interfaces, which aren't as well
|
|
|
|
organized. They exist as globals in globals.cpp:
|
|
|
|
|
2017-02-20 00:18:42 +00:00
|
|
|
<pre>
|
|
|
|
FileManager *g_filemanager = NULL;
|
2017-02-19 23:55:54 +00:00
|
|
|
PhysicalDisplay *g_display = NULL;
|
|
|
|
PhysicalKeyboard *g_keyboard = NULL;
|
|
|
|
PhysicalSpeaker *g_speaker = NULL;
|
2017-02-20 00:18:42 +00:00
|
|
|
PhysicalPaddles *g_paddles = NULL;
|
|
|
|
</pre>
|
2017-02-19 23:55:54 +00:00
|
|
|
|
|
|
|
There are the two globals that point to the VM and the virtual CPU:
|
|
|
|
|
2017-02-20 00:18:42 +00:00
|
|
|
<pre>
|
|
|
|
Cpu *g_cpu = NULL;
|
|
|
|
VM *g_vm = NULL;
|
|
|
|
</pre>
|
2017-02-19 23:55:54 +00:00
|
|
|
|
|
|
|
And there are two global configuration values that probably belong in
|
|
|
|
some sort of Prefs class:
|
|
|
|
|
2017-02-20 00:18:42 +00:00
|
|
|
<pre>
|
|
|
|
int16_t g_volume;
|
|
|
|
uint8_t g_displayType;
|
|
|
|
</pre>
|
2017-02-19 23:55:54 +00:00
|
|
|
|
|
|
|
|
2017-02-19 18:46:13 +00:00
|
|
|
CPU
|
|
|
|
===
|
|
|
|
|
|
|
|
The CPU is a 65C02, not quite complete; it supports all of the 65C02
|
|
|
|
documented opcodes but not the undocumented ones here:
|
|
|
|
|
|
|
|
http://www.oxyron.de/html/opcodes02.html
|
|
|
|
|
|
|
|
The timing of the CPU is also not quite correct. It's close, but
|
|
|
|
doesn't count cycles due to page boundary crossings during branch
|
|
|
|
instructions. (See the "cycle count footnotes" in cpu.cpp.)
|
|
|
|
|
|
|
|
The CPU passes the 6502 functional test from here:
|
|
|
|
|
|
|
|
https://github.com/Klaus2m5/6502_65C02_functional_tests
|
|
|
|
|
|
|
|
... which is included in binary form in the test harness (see the .h
|
|
|
|
files in util/ for notes).
|
|
|
|
|
|
|
|
testharness.basic should reach "test number 240", hang for a while,
|
|
|
|
and then exit.
|
|
|
|
|
|
|
|
testharness.verbose should show that it gets through 43 tests, test
|
|
|
|
240, and then loops repeatedly for a while (exiting at a somewhat
|
|
|
|
arbitrary point).
|
|
|
|
|
|
|
|
testharness.extended currently fails (hanging at 0x733) because I
|
|
|
|
haven't implemented the undocumented opcodes. It should get to address
|
|
|
|
0x24a8 and hang. Some day I'll finish implementing all of the
|
|
|
|
undocumented opcodes :)
|
|
|
|
|