2017-12-06 20:36:14 +00:00
|
|
|
/*
|
|
|
|
* apple2.c
|
2017-12-09 04:12:31 +00:00
|
|
|
*
|
|
|
|
* Here we have support for the apple2 machine. I suspect that we will
|
|
|
|
* need to break this file up into components in the future...
|
2017-12-06 20:36:14 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "apple2.h"
|
2017-12-16 04:22:40 +00:00
|
|
|
#include "option.h"
|
2017-12-06 21:21:39 +00:00
|
|
|
#include "vm_segment.h"
|
2017-12-06 20:36:14 +00:00
|
|
|
|
2017-12-06 21:21:39 +00:00
|
|
|
/*
|
|
|
|
* This is the memory address where an apple program can find the value
|
|
|
|
* of the key that was last pressed.
|
|
|
|
*/
|
|
|
|
#define LAST_KEY 0xC000
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is the address in memory where you can find whether a key is
|
|
|
|
* currently pressed or not.
|
|
|
|
*/
|
|
|
|
#define ANY_KEY_DOWN 0xC010
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create the basic apple2 structure.
|
|
|
|
*/
|
2017-12-06 20:36:14 +00:00
|
|
|
apple2 *
|
2017-12-21 02:45:26 +00:00
|
|
|
apple2_create(int width, int height)
|
2017-12-06 20:36:14 +00:00
|
|
|
{
|
|
|
|
apple2 *mach;
|
2017-12-21 02:45:26 +00:00
|
|
|
int err;
|
2017-12-06 20:36:14 +00:00
|
|
|
|
|
|
|
mach = malloc(sizeof(apple2));
|
|
|
|
if (mach == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
mach->cpu = mos6502_create();
|
2017-12-06 21:21:39 +00:00
|
|
|
mach->memory = mach->cpu->memory;
|
2017-12-16 04:22:40 +00:00
|
|
|
|
2017-12-21 02:45:26 +00:00
|
|
|
// Our two drives -- we create both of them, even if we intend to
|
|
|
|
// use only one.
|
2017-12-16 04:22:40 +00:00
|
|
|
mach->drive1 = apple2dd_create();
|
|
|
|
mach->drive2 = apple2dd_create();
|
|
|
|
|
2017-12-21 02:45:26 +00:00
|
|
|
// Let's build our screen abstraction!
|
|
|
|
mach->screen = vm_screen_create();
|
|
|
|
if (mach->screen == NULL) {
|
|
|
|
log_critical("Screen creation failed!\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We still need to add a window, since we want to render some
|
|
|
|
// graphics.
|
|
|
|
err = vm_screen_add_window(mach->screen, width, height);
|
|
|
|
if (err != OK) {
|
|
|
|
log_critical("Window creation failed!\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-12-21 17:52:56 +00:00
|
|
|
// Default to full color
|
|
|
|
apple2_set_color(mach, COLOR_FULL);
|
|
|
|
|
2017-12-21 03:54:54 +00:00
|
|
|
// We default to lo-res mode.
|
|
|
|
apple2_set_video(mach, VIDEO_LORES);
|
|
|
|
|
2017-12-22 05:33:04 +00:00
|
|
|
// Let's install our bitmap font.
|
|
|
|
mach->sysfont = vm_bitfont_create(mach->screen,
|
|
|
|
"apple2-system",
|
|
|
|
7, 8, // 7 pixels wide, 8 pixels tall
|
|
|
|
0x7f); // 7-bit values only
|
|
|
|
if (mach->sysfont == NULL) {
|
|
|
|
log_critical("Could not initialize apple2: bad font");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-12-06 20:36:14 +00:00
|
|
|
return mach;
|
|
|
|
}
|
2017-12-06 21:21:39 +00:00
|
|
|
|
2017-12-21 17:52:56 +00:00
|
|
|
void
|
|
|
|
apple2_set_color(apple2 *mach, int mode)
|
|
|
|
{
|
|
|
|
mach->color_mode = mode;
|
|
|
|
|
|
|
|
// FIXME: doing this should force us to redraw everything in the
|
|
|
|
// correct color interpretation
|
|
|
|
}
|
|
|
|
|
2017-12-22 18:56:22 +00:00
|
|
|
#define SHOW(ch) \
|
|
|
|
vm_bitfont_render(mach->sysfont, mach->screen, &area, ch); \
|
|
|
|
area.x += 7
|
|
|
|
|
2017-12-21 02:45:26 +00:00
|
|
|
void
|
|
|
|
apple2_run_loop(apple2 *mach)
|
|
|
|
{
|
2017-12-22 18:56:22 +00:00
|
|
|
SDL_Rect area;
|
|
|
|
|
|
|
|
|
2017-12-21 02:45:26 +00:00
|
|
|
while (vm_screen_active(mach->screen)) {
|
2017-12-22 18:56:22 +00:00
|
|
|
area.x = 50;
|
|
|
|
area.y = 50;
|
|
|
|
SHOW('H'); SHOW('e'); SHOW('l'); SHOW('l'); SHOW('o'); SHOW(' ');
|
|
|
|
SHOW('w'); SHOW('o'); SHOW('r'); SHOW('l'); SHOW('d'); SHOW('!');
|
2017-12-21 02:45:26 +00:00
|
|
|
vm_screen_refresh(mach->screen);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-06 21:21:39 +00:00
|
|
|
/*
|
|
|
|
* Free the memory reserved for an apple2 struct.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
apple2_free(apple2 *mach)
|
|
|
|
{
|
|
|
|
mos6502_free(mach->cpu);
|
|
|
|
|
|
|
|
// NOTE: we do _NOT_ want to clear the memory field of mach, as it's
|
|
|
|
// co-owned with the cpu struct that we just freed above.
|
|
|
|
|
|
|
|
free(mach);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
apple2_press_key(apple2 *mach, vm_8bit ch)
|
|
|
|
{
|
|
|
|
// The apple2 can only handle ASCII values of 0 through 127.
|
|
|
|
// However, the eigth bit is called the "strobe" bit, and is treated
|
|
|
|
// specially. In particular, the strobe bit is 1 if a key was
|
|
|
|
// pressed down, and remains 1 until you reset it by reading from
|
|
|
|
// the clear-strobe location.
|
|
|
|
ch = ch | 0x80;
|
|
|
|
|
|
|
|
// This is the location in memory where a program will expect to
|
|
|
|
// find the value of the last key that was pressed.
|
|
|
|
vm_segment_set(mach->memory, LAST_KEY, ch);
|
|
|
|
|
|
|
|
// This area is a combination of flags; the eighth bit here is the
|
|
|
|
// "any-key-down" flag, which is a bit of a mouthful. It's 1 if a
|
|
|
|
// key is pressed, and 0 if not. The effect of reading this bit will
|
|
|
|
// also _clear_ the strobe bit in the $C000 address (above).
|
|
|
|
vm_segment_set(mach->memory, ANY_KEY_DOWN, 0x80);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This function will clear the 8th bit, which is the "strobe" bit, from
|
|
|
|
* the position in memory where the value of the last key that was
|
|
|
|
* pressed is held.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
apple2_clear_strobe(apple2 *mach)
|
|
|
|
{
|
|
|
|
vm_8bit ch;
|
|
|
|
|
|
|
|
ch = vm_segment_get(mach->memory, LAST_KEY);
|
|
|
|
vm_segment_set(mach->memory, LAST_KEY, ch & 0x7F);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This function will clear the value of the any-key-down switch/flag.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
apple2_release_key(apple2 *mach)
|
|
|
|
{
|
|
|
|
vm_segment_set(mach->memory, ANY_KEY_DOWN, 0);
|
|
|
|
}
|
2017-12-16 04:22:40 +00:00
|
|
|
|
|
|
|
int
|
|
|
|
apple2_boot(apple2 *mach)
|
|
|
|
{
|
|
|
|
FILE *stream;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
// Do we have any disks?
|
|
|
|
stream = option_get_input(1);
|
|
|
|
if (stream) {
|
|
|
|
err = apple2dd_insert(mach->drive1, stream);
|
|
|
|
if (err != OK) {
|
|
|
|
log_critical("Unable to insert disk1 into drive");
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
stream = option_get_input(2);
|
|
|
|
if (stream) {
|
|
|
|
err = apple2dd_insert(mach->drive2, stream);
|
|
|
|
if (err != OK) {
|
|
|
|
log_critical("Unable to insert disk2 into drive");
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|
2017-12-21 03:52:28 +00:00
|
|
|
|
|
|
|
void
|
|
|
|
apple2_set_video(apple2 *mach, int mode)
|
|
|
|
{
|
|
|
|
int width, height;
|
|
|
|
|
|
|
|
mach->video_mode = mode;
|
|
|
|
|
|
|
|
// In the traditional video modes that Apple II first came in, you
|
|
|
|
// would have a maximum width of 280 pixels. (In lo-res, you have
|
|
|
|
// fewer pixels, but that is something we have to handle in our
|
|
|
|
// drawing functions rather than by changing the logical size.)
|
|
|
|
width = 280;
|
|
|
|
height = 192;
|
|
|
|
|
|
|
|
// In double video modes, the width is effectively doubled, but the
|
|
|
|
// height is untouched.
|
|
|
|
if (mach->video_mode == VIDEO_DOUBLE_LORES ||
|
|
|
|
mach->video_mode == VIDEO_DOUBLE_HIRES
|
|
|
|
) {
|
|
|
|
width = 560;
|
|
|
|
}
|
|
|
|
|
|
|
|
vm_screen_set_logical_coords(mach->screen, width, height);
|
|
|
|
}
|