2017-12-06 20:36:14 +00:00
|
|
|
/*
|
|
|
|
* apple2.c
|
2017-12-09 04:12:31 +00:00
|
|
|
*
|
2018-02-13 03:15:20 +00:00
|
|
|
* This file handles the top-level domain code for the Apple II machine.
|
|
|
|
* It's also a bit of a catch-all for logic which doesn't need its own
|
|
|
|
* file.
|
2017-12-06 20:36:14 +00:00
|
|
|
*/
|
|
|
|
|
2018-02-07 20:44:04 +00:00
|
|
|
#include <unistd.h>
|
|
|
|
|
2017-12-06 20:36:14 +00:00
|
|
|
#include "apple2.h"
|
2017-12-22 21:48:48 +00:00
|
|
|
#include "apple2.draw.h"
|
2018-01-02 22:30:21 +00:00
|
|
|
#include "apple2.mem.h"
|
2018-01-05 20:19:13 +00:00
|
|
|
#include "mos6502.enums.h"
|
2017-12-31 21:50:59 +00:00
|
|
|
#include "mos6502.dis.h"
|
2018-01-04 03:50:01 +00:00
|
|
|
#include "objstore.h"
|
2017-12-16 04:22:40 +00:00
|
|
|
#include "option.h"
|
2018-02-25 20:25:02 +00:00
|
|
|
#include "vm_debug.h"
|
2018-02-07 21:24:02 +00:00
|
|
|
#include "vm_di.h"
|
2018-02-25 20:25:02 +00:00
|
|
|
#include "vm_reflect.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
|
|
|
|
2018-01-04 03:50:01 +00:00
|
|
|
objstore_init();
|
|
|
|
|
2017-12-06 20:36:14 +00:00
|
|
|
mach = malloc(sizeof(apple2));
|
|
|
|
if (mach == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-01-17 21:16:25 +00:00
|
|
|
// By default, we have no strobe set; it should only be set when a
|
|
|
|
// key is pressed
|
|
|
|
mach->strobe = false;
|
|
|
|
|
2018-02-07 20:44:04 +00:00
|
|
|
// Yes, please do execute opcodes to begin with
|
|
|
|
mach->paused = false;
|
|
|
|
|
2018-02-07 21:24:02 +00:00
|
|
|
// Don't print opcodes by default
|
|
|
|
mach->disasm = false;
|
|
|
|
|
2018-01-01 23:11:03 +00:00
|
|
|
// Forward set these to NULL in case we fail to build the machine
|
|
|
|
// properly; that way, we won't try to free garbage data
|
2018-01-02 22:24:51 +00:00
|
|
|
mach->rom = NULL;
|
2018-01-11 19:25:22 +00:00
|
|
|
mach->cpu = NULL;
|
2018-01-11 19:19:17 +00:00
|
|
|
mach->aux = NULL;
|
2018-01-11 03:29:25 +00:00
|
|
|
mach->main = NULL;
|
2018-01-01 23:11:03 +00:00
|
|
|
mach->sysfont = NULL;
|
2018-01-24 20:26:28 +00:00
|
|
|
mach->invfont = NULL;
|
2018-01-01 23:11:03 +00:00
|
|
|
mach->screen = NULL;
|
|
|
|
mach->drive1 = NULL;
|
|
|
|
mach->drive2 = NULL;
|
2018-01-28 03:53:12 +00:00
|
|
|
mach->selected_drive = NULL;
|
2018-01-01 23:11:03 +00:00
|
|
|
|
2018-01-12 01:52:13 +00:00
|
|
|
// This is more-or-less the same setup you do in apple2_reset(). We
|
|
|
|
// need to hard-set these values because apple2_set_bank_switch
|
|
|
|
// assumes that the bank_switch variable has been initialized
|
|
|
|
// before, which to this point, it hasn't!
|
2018-01-12 22:21:49 +00:00
|
|
|
mach->bank_switch = BANK_DEFAULT;
|
2018-01-27 04:01:46 +00:00
|
|
|
mach->memory_mode = MEMORY_DEFAULT | MEMORY_SLOTCXROM;
|
2018-01-12 01:52:13 +00:00
|
|
|
|
2018-01-11 19:19:17 +00:00
|
|
|
mach->main = vm_segment_create(APPLE2_MEMORY_SIZE);
|
2018-01-11 03:28:05 +00:00
|
|
|
if (mach->main == NULL) {
|
|
|
|
log_critical("Could not initialize main RAM!");
|
|
|
|
apple2_free(mach);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
mach->cpu = mos6502_create(mach->main, mach->main);
|
2018-01-01 23:11:03 +00:00
|
|
|
if (mach->cpu == NULL) {
|
|
|
|
log_critical("Could not create CPU!");
|
|
|
|
apple2_free(mach);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-01-02 22:24:51 +00:00
|
|
|
// Initliaze our system ROM and separate bank-switched block of RAM
|
|
|
|
mach->rom = vm_segment_create(APPLE2_ROM_SIZE);
|
2018-01-11 19:19:17 +00:00
|
|
|
mach->aux = vm_segment_create(APPLE2_MEMORY_SIZE);
|
|
|
|
if (mach->rom == NULL || mach->aux == NULL) {
|
|
|
|
log_critical("Could not initialize ROM / AUX!");
|
2018-01-02 22:24:51 +00:00
|
|
|
apple2_free(mach);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-01-12 01:52:13 +00:00
|
|
|
// Set the read/write mappers for everything
|
|
|
|
apple2_mem_map(mach, mach->main);
|
|
|
|
apple2_mem_map(mach, mach->aux);
|
|
|
|
|
2018-01-03 21:20:48 +00:00
|
|
|
if (apple2_mem_init_sys_rom(mach) != OK) {
|
|
|
|
log_critical("Could not initialize apple2 ROM");
|
|
|
|
apple2_free(mach);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-12-21 02:45:26 +00:00
|
|
|
// Our two drives -- we create both of them, even if we intend to
|
|
|
|
// use only one.
|
2018-01-07 20:46:54 +00:00
|
|
|
mach->drive1 = apple2_dd_create();
|
|
|
|
mach->drive2 = apple2_dd_create();
|
2017-12-16 04:22:40 +00:00
|
|
|
|
2018-01-01 23:11:03 +00:00
|
|
|
if (mach->drive1 == NULL || mach->drive2 == NULL) {
|
|
|
|
log_critical("Could not create disk drives!");
|
|
|
|
apple2_free(mach);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-01-28 03:53:12 +00:00
|
|
|
// By default, the selected drive should be drive1
|
|
|
|
mach->selected_drive = mach->drive1;
|
|
|
|
|
2017-12-21 02:45:26 +00:00
|
|
|
// Let's build our screen abstraction!
|
|
|
|
mach->screen = vm_screen_create();
|
|
|
|
if (mach->screen == NULL) {
|
2018-01-01 23:11:03 +00:00
|
|
|
log_critical("Screen creation failed!");
|
|
|
|
apple2_free(mach);
|
2017-12-21 02:45:26 +00:00
|
|
|
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) {
|
2018-01-01 23:11:03 +00:00
|
|
|
log_critical("Window creation failed!");
|
|
|
|
apple2_free(mach);
|
2017-12-21 02:45:26 +00:00
|
|
|
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.
|
2018-01-23 21:51:06 +00:00
|
|
|
apple2_set_display(mach, DISPLAY_TEXT);
|
2017-12-21 03:54:54 +00:00
|
|
|
|
2017-12-22 05:33:04 +00:00
|
|
|
// Let's install our bitmap font.
|
|
|
|
mach->sysfont = vm_bitfont_create(mach->screen,
|
2018-01-04 03:50:01 +00:00
|
|
|
objstore_apple2_sysfont(),
|
|
|
|
APPLE2_SYSFONT_SIZE,
|
2017-12-22 05:33:04 +00:00
|
|
|
7, 8, // 7 pixels wide, 8 pixels tall
|
|
|
|
0x7f); // 7-bit values only
|
2018-01-24 20:26:28 +00:00
|
|
|
mach->invfont = vm_bitfont_create(mach->screen,
|
|
|
|
objstore_apple2_invfont(),
|
|
|
|
APPLE2_SYSFONT_SIZE,
|
|
|
|
7, 8,
|
|
|
|
0x7f);
|
|
|
|
if (mach->sysfont == NULL || mach->invfont == NULL) {
|
2018-01-01 23:11:03 +00:00
|
|
|
apple2_free(mach);
|
2017-12-22 05:33:04 +00:00
|
|
|
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
|
|
|
|
2018-01-07 03:37:15 +00:00
|
|
|
/*
|
2018-01-11 01:59:33 +00:00
|
|
|
* Change the bank switch flags for the apple 2.
|
2018-01-07 03:37:15 +00:00
|
|
|
*/
|
|
|
|
void
|
2018-01-11 01:59:33 +00:00
|
|
|
apple2_set_bank_switch(apple2 *mach, vm_8bit flags)
|
2018-01-07 03:37:15 +00:00
|
|
|
{
|
2018-01-13 03:04:21 +00:00
|
|
|
// If we already have BANK_ALTZP, and the flags we're setting do
|
|
|
|
// _not_ have BANK_ALTZP, then we need to copy aux's zero page and
|
|
|
|
// stack into main. But if we don't have BANK_ALTZP, and flags
|
|
|
|
// _does_, then we have to do the inverse: copy main's zero page and
|
|
|
|
// stack into aux.
|
|
|
|
if (mach->bank_switch & BANK_ALTZP) {
|
|
|
|
if (~flags & BANK_ALTZP) {
|
|
|
|
vm_segment_copy(mach->main, mach->aux, 0, 0, 0x200);
|
|
|
|
}
|
|
|
|
} else if (flags & BANK_ALTZP) {
|
2018-01-12 01:52:13 +00:00
|
|
|
vm_segment_copy(mach->aux, mach->main, 0, 0, 0x200);
|
|
|
|
}
|
|
|
|
|
2018-01-11 01:59:33 +00:00
|
|
|
mach->bank_switch = flags;
|
2018-01-07 03:37:15 +00:00
|
|
|
}
|
|
|
|
|
2018-01-13 03:04:21 +00:00
|
|
|
/*
|
|
|
|
* Set the memory mode of the apple machine. This may cause us to change
|
|
|
|
* some behavior (i.e. start using or stop using auxiliary memory).
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
apple2_set_memory_mode(apple2 *mach, vm_8bit flags)
|
|
|
|
{
|
|
|
|
vm_segment *rmem = NULL,
|
|
|
|
*wmem = NULL;
|
|
|
|
|
|
|
|
mach->memory_mode = flags;
|
|
|
|
|
|
|
|
// We may need to change which segments the CPU can read from or
|
|
|
|
// write to, based upon the below flags.
|
|
|
|
rmem = (flags & MEMORY_READ_AUX) ? mach->aux : mach->main;
|
|
|
|
wmem = (flags & MEMORY_WRITE_AUX) ? mach->aux : mach->main;
|
|
|
|
|
|
|
|
mos6502_set_memory(mach->cpu, rmem, wmem);
|
|
|
|
}
|
|
|
|
|
2017-12-26 23:13:34 +00:00
|
|
|
/*
|
|
|
|
* Return true if we are in a state that the apple2 would consider
|
|
|
|
* double resolution. (In practice, this refers to horizontal screen
|
|
|
|
* density; vertical screen density per-pixel is unchanged.)
|
|
|
|
*/
|
2017-12-26 22:39:23 +00:00
|
|
|
bool
|
|
|
|
apple2_is_double_video(apple2 *mach)
|
2017-12-21 17:52:56 +00:00
|
|
|
{
|
2017-12-26 22:39:23 +00:00
|
|
|
return
|
2018-01-16 22:13:50 +00:00
|
|
|
mach->display_mode & DISPLAY_DHIRES;
|
2017-12-21 17:52:56 +00:00
|
|
|
}
|
|
|
|
|
2017-12-26 23:13:34 +00:00
|
|
|
/*
|
|
|
|
* Try to "boot" the apple2 machine. Look for input sources indicated in
|
|
|
|
* the option system and load those into our disk drives.
|
2018-02-04 00:10:29 +00:00
|
|
|
*
|
|
|
|
* FIXME: we need to get the image type from the file name used
|
2017-12-26 23:13:34 +00:00
|
|
|
*/
|
2017-12-26 22:39:23 +00:00
|
|
|
int
|
|
|
|
apple2_boot(apple2 *mach)
|
2017-12-21 02:45:26 +00:00
|
|
|
{
|
2017-12-26 22:39:23 +00:00
|
|
|
FILE *stream;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
// Do we have any disks?
|
|
|
|
stream = option_get_input(1);
|
|
|
|
if (stream) {
|
2018-02-04 00:10:29 +00:00
|
|
|
err = apple2_dd_insert(mach->drive1, stream, DD_DOS33);
|
2017-12-26 22:39:23 +00:00
|
|
|
if (err != OK) {
|
|
|
|
log_critical("Unable to insert disk1 into drive");
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
stream = option_get_input(2);
|
|
|
|
if (stream) {
|
2018-02-04 00:10:29 +00:00
|
|
|
err = apple2_dd_insert(mach->drive2, stream, DD_DOS33);
|
2017-12-26 22:39:23 +00:00
|
|
|
if (err != OK) {
|
|
|
|
log_critical("Unable to insert disk2 into drive");
|
|
|
|
return err;
|
|
|
|
}
|
2017-12-21 02:45:26 +00:00
|
|
|
}
|
2017-12-26 22:39:23 +00:00
|
|
|
|
2018-01-10 22:47:18 +00:00
|
|
|
// To begin with, we need to set the reset vector to the Applesoft
|
|
|
|
// interpeter.
|
2018-01-11 03:28:05 +00:00
|
|
|
vm_segment_set16(mach->main, APPLE2_RESET_VECTOR,
|
2018-01-10 22:47:18 +00:00
|
|
|
APPLE2_APPLESOFT_MAIN);
|
|
|
|
|
2017-12-31 21:50:59 +00:00
|
|
|
if (option_flag(OPTION_DISASSEMBLE)) {
|
2018-01-11 03:28:05 +00:00
|
|
|
mos6502_dis_scan(mach->cpu, stdout, 0, mach->main->size);
|
2017-12-31 21:50:59 +00:00
|
|
|
}
|
|
|
|
|
2018-01-05 22:04:01 +00:00
|
|
|
// Run the reset routine to get the machine ready to go.
|
|
|
|
apple2_reset(mach);
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This function marks out the procedures that happen when the machine
|
|
|
|
* is reset. A reset can happen at a cold boot, but it can also happen
|
|
|
|
* after the computer is already operational.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
apple2_reset(apple2 *mach)
|
|
|
|
{
|
2018-01-22 03:33:12 +00:00
|
|
|
mach->cpu->P = MOS_STATUS_DEFAULT;
|
2018-01-11 03:28:05 +00:00
|
|
|
mach->cpu->PC = vm_segment_get16(mach->main, 0xFFFC);
|
2018-01-21 07:09:42 +00:00
|
|
|
mach->cpu->S = 0xff;
|
2018-01-10 21:14:07 +00:00
|
|
|
|
2018-01-10 21:16:12 +00:00
|
|
|
// Switch video mode back to 40 column text
|
2018-01-23 21:51:06 +00:00
|
|
|
apple2_set_display(mach, DISPLAY_TEXT);
|
2018-01-10 21:16:12 +00:00
|
|
|
|
2018-01-13 03:04:21 +00:00
|
|
|
// Switch us back to defaults
|
2018-01-12 22:21:49 +00:00
|
|
|
apple2_set_bank_switch(mach, BANK_DEFAULT);
|
2018-01-22 03:33:12 +00:00
|
|
|
apple2_set_memory_mode(mach, MEMORY_DEFAULT | MEMORY_SLOTCXROM);
|
2017-12-26 22:39:23 +00:00
|
|
|
}
|
|
|
|
|
2017-12-06 21:21:39 +00:00
|
|
|
/*
|
|
|
|
* Free the memory reserved for an apple2 struct.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
apple2_free(apple2 *mach)
|
|
|
|
{
|
2018-01-01 23:11:03 +00:00
|
|
|
if (mach->cpu) {
|
|
|
|
mos6502_free(mach->cpu);
|
|
|
|
}
|
|
|
|
|
2018-01-02 22:24:51 +00:00
|
|
|
if (mach->rom) {
|
|
|
|
vm_segment_free(mach->rom);
|
|
|
|
}
|
|
|
|
|
2018-01-11 03:29:25 +00:00
|
|
|
if (mach->main) {
|
|
|
|
vm_segment_free(mach->main);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mach->aux) {
|
|
|
|
vm_segment_free(mach->aux);
|
|
|
|
}
|
|
|
|
|
2018-01-01 23:11:03 +00:00
|
|
|
if (mach->sysfont) {
|
|
|
|
vm_bitfont_free(mach->sysfont);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mach->drive1) {
|
2018-01-07 20:46:54 +00:00
|
|
|
apple2_dd_free(mach->drive1);
|
2018-01-01 23:11:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (mach->drive2) {
|
2018-01-07 20:46:54 +00:00
|
|
|
apple2_dd_free(mach->drive2);
|
2018-01-01 23:11:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (mach->screen) {
|
|
|
|
vm_screen_free(mach->screen);
|
|
|
|
}
|
2017-12-06 21:21:39 +00:00
|
|
|
|
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
|
2017-12-26 23:13:34 +00:00
|
|
|
/*
|
|
|
|
* The run loop is the function that essentially waits for user input
|
|
|
|
* and continues to present the apple2 abstraction for you to use. At
|
|
|
|
* some point the user will indicate they are done, whereby
|
|
|
|
* vm_screen_active() will no longer be true and we exit.
|
|
|
|
*/
|
2017-12-26 22:39:23 +00:00
|
|
|
void
|
|
|
|
apple2_run_loop(apple2 *mach)
|
2017-12-16 04:22:40 +00:00
|
|
|
{
|
2018-02-07 21:24:02 +00:00
|
|
|
FILE *out;
|
|
|
|
|
2017-12-31 21:50:59 +00:00
|
|
|
if (option_flag(OPTION_DISASSEMBLE)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-02-07 21:24:02 +00:00
|
|
|
out = (FILE *)vm_di_get(VM_OUTPUT);
|
2018-02-25 20:25:02 +00:00
|
|
|
vm_reflect_pause(NULL);
|
2018-02-07 21:24:02 +00:00
|
|
|
|
2017-12-26 22:39:23 +00:00
|
|
|
while (vm_screen_active(mach->screen)) {
|
2018-02-25 21:41:37 +00:00
|
|
|
if (vm_debug_broke(mach->cpu->PC)) {
|
|
|
|
mach->paused = true;
|
|
|
|
}
|
|
|
|
|
2018-02-07 20:44:04 +00:00
|
|
|
// If we're paused, then just re-loop until we're not
|
|
|
|
if (mach->paused) {
|
2018-02-25 20:25:02 +00:00
|
|
|
char *input = vm_debug_prompt();
|
|
|
|
|
|
|
|
if (input != NULL) {
|
|
|
|
vm_debug_execute(input);
|
|
|
|
}
|
|
|
|
|
|
|
|
free(input);
|
2018-02-07 20:44:04 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-02-07 21:24:02 +00:00
|
|
|
if (mach->disasm) {
|
|
|
|
if (mach->selected_drive) {
|
|
|
|
mach->selected_drive->locked = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
mos6502_dis_opcode(mach->cpu, out, mach->cpu->PC);
|
|
|
|
|
|
|
|
if (mach->selected_drive) {
|
|
|
|
mach->selected_drive->locked = false;
|
|
|
|
}
|
|
|
|
}
|
2018-01-29 00:06:14 +00:00
|
|
|
|
2018-01-10 02:58:53 +00:00
|
|
|
mos6502_execute(mach->cpu);
|
2018-01-23 20:52:16 +00:00
|
|
|
|
|
|
|
if (vm_screen_dirty(mach->screen)) {
|
|
|
|
vm_screen_refresh(mach->screen);
|
|
|
|
}
|
2017-12-16 04:22:40 +00:00
|
|
|
}
|
2017-12-26 22:39:23 +00:00
|
|
|
}
|
2017-12-16 04:22:40 +00:00
|
|
|
|
2017-12-26 23:13:34 +00:00
|
|
|
/*
|
|
|
|
* Set the color mode of the apple2, which is to say if we are emulating
|
|
|
|
* a monochromatic display, or full color, or just black-and-white.
|
|
|
|
*/
|
2017-12-26 22:39:23 +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-16 04:22:40 +00:00
|
|
|
}
|
2017-12-21 03:52:28 +00:00
|
|
|
|
2017-12-26 23:13:34 +00:00
|
|
|
/*
|
2018-01-16 22:13:50 +00:00
|
|
|
* Set the display mode of the display. This would be the type of
|
2017-12-26 23:13:34 +00:00
|
|
|
* resolution (text by which number of columns, lo-res, hi-res, etc.)
|
|
|
|
*/
|
2017-12-21 03:52:28 +00:00
|
|
|
void
|
2018-01-16 22:13:50 +00:00
|
|
|
apple2_set_display(apple2 *mach, vm_8bit mode)
|
2017-12-21 03:52:28 +00:00
|
|
|
{
|
|
|
|
int width, height;
|
|
|
|
|
2018-01-16 22:13:50 +00:00
|
|
|
mach->display_mode = mode;
|
2017-12-21 03:52:28 +00:00
|
|
|
|
|
|
|
// 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.
|
2018-01-16 22:13:50 +00:00
|
|
|
if (apple2_is_double_video(mach)) {
|
2017-12-21 03:52:28 +00:00
|
|
|
width = 560;
|
|
|
|
}
|
|
|
|
|
|
|
|
vm_screen_set_logical_coords(mach->screen, width, height);
|
|
|
|
}
|