diff --git a/Makefile b/Makefile index 0c85251..fa6e991 100644 --- a/Makefile +++ b/Makefile @@ -21,13 +21,13 @@ # SOFTWARE. CC=cc -CFLAGS=-std=c11 -O3 -Wpedantic -Wall -Wshadow -Werror -Wshadow -Wno-gnu-binary-literal -g +CFLAGS=-std=c11 -O0 -Wpedantic -Wall -Wshadow -Werror -Wshadow -Wno-gnu-binary-literal -g LDFLAGS=-g -L/usr/local/lib EWM_EXECUTABLE=ewm -EWM_SOURCES=cpu.c ins.c pia.c mem.c ewm.c fmt.c a2p.c scr.c dsk.c chr.c alc.c sdl.c +EWM_SOURCES=cpu.c ins.c pia.c mem.c ewm.c fmt.c two.c scr.c dsk.c chr.c alc.c one.c tty.c EWM_OBJECTS=$(EWM_SOURCES:.c=.o) -EWM_LIBS=-lcurses -lSDL2 +EWM_LIBS=-lSDL2 CPU_TEST_EXECUTABLE=cpu_test CPU_TEST_SOURCES=cpu.c ins.c mem.c fmt.c cpu_test.c @@ -35,7 +35,7 @@ CPU_TEST_OBJECTS=$(CPU_TEST_SOURCES:.c=.o) CPU_TEST_LIBS= SCR_TEST_EXECUTABLE=scr_test -SCR_TEST_SOURCES=cpu.c ins.c mem.c fmt.c a2p.c scr.c dsk.c chr.c alc.c sdl.c scr_test.c +SCR_TEST_SOURCES=cpu.c ins.c mem.c fmt.c two.c scr.c dsk.c chr.c alc.c scr_test.c SCR_TEST_OBJECTS=$(SCR_TEST_SOURCES:.c=.o) SCR_TEST_LIBS=-lSDL2 diff --git a/a2p.c b/a2p.c deleted file mode 100644 index 97f3ec1..0000000 --- a/a2p.c +++ /dev/null @@ -1,290 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2015 Stefan Arentz - http://github.com/st3fan/ewm -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include -#include - -#include "cpu.h" -#include "mem.h" - -#include "dsk.h" -#include "alc.h" -#include "a2p.h" - -#define EWM_A2P_SS_KBD 0xc000 -#define EWM_A2P_SS_KBDSTRB 0xc010 -#define EWM_A2P_SS_SPKR 0xc030 - -#define EWM_A2P_SS_SCREEN_MODE_GRAPHICS 0xc050 -#define EWM_A2P_SS_SCREEN_MODE_TEXT 0xc051 -#define EWM_A2P_SS_GRAPHICS_STYLE_FULL 0xc052 -#define EWM_A2P_SS_GRAPHICS_STYLE_MIXED 0xc053 -#define EWM_A2P_SS_SCREEN_PAGE1 0xc054 -#define EWM_A2P_SS_SCREEN_PAGE2 0xc055 -#define EWM_A2P_SS_GRAPHICS_MODE_LGR 0xc056 -#define EWM_A2P_SS_GRAPHICS_MODE_HGR 0xc057 - -#define EWM_A2P_SS_SETAN0 0xc058 -#define EWM_A2P_SS_CLRAN0 0xc059 -#define EWM_A2P_SS_SETAN1 0xc05a -#define EWM_A2P_SS_CLRAN1 0xc05b -#define EWM_A2P_SS_SETAN2 0xc05c -#define EWM_A2P_SS_CLRAN2 0xc05d -#define EWM_A2P_SS_SETAN3 0xc05e -#define EWM_A2P_SS_CLRAN3 0xc05f - -#define EWM_A2P_SS_PB0 0xC061 -#define EWM_A2P_SS_PB1 0xC062 -#define EWM_A2P_SS_PB2 0xC063 -#define EWM_A2P_SS_PB3 0xC060 // TODO On the gs only? - -uint8_t a2p_iom_read(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr) { - struct a2p_t *a2p = (struct a2p_t*) mem->obj; - switch (addr) { - case EWM_A2P_SS_KBD: - return a2p->key; - case EWM_A2P_SS_KBDSTRB: - a2p->key &= 0x7f; - return 0x00; - - case EWM_A2P_SS_SCREEN_MODE_GRAPHICS: - a2p->screen_mode = EWM_A2P_SCREEN_MODE_GRAPHICS; - a2p->screen_dirty = true; - break; - case EWM_A2P_SS_SCREEN_MODE_TEXT: - a2p->screen_mode = EWM_A2P_SCREEN_MODE_TEXT; - a2p->screen_dirty = true; - break; - - case EWM_A2P_SS_GRAPHICS_MODE_LGR: - a2p->screen_graphics_mode = EWM_A2P_SCREEN_GRAPHICS_MODE_LGR; - a2p->screen_dirty = true; - break; - case EWM_A2P_SS_GRAPHICS_MODE_HGR: - a2p->screen_graphics_mode = EWM_A2P_SCREEN_GRAPHICS_MODE_HGR; - a2p->screen_dirty = true; - break; - - case EWM_A2P_SS_GRAPHICS_STYLE_FULL: - a2p->screen_graphics_style = EWM_A2P_SCREEN_GRAPHICS_STYLE_FULL; - a2p->screen_dirty = true; - break; - case EWM_A2P_SS_GRAPHICS_STYLE_MIXED: - a2p->screen_graphics_style = EWM_A2P_SCREEN_GRAPHICS_STYLE_MIXED; - a2p->screen_dirty = true; - break; - - case EWM_A2P_SS_SCREEN_PAGE1: - a2p->screen_page = EWM_A2P_SCREEN_PAGE1; - a2p->screen_dirty = true; - break; - case EWM_A2P_SS_SCREEN_PAGE2: - a2p->screen_page = EWM_A2P_SCREEN_PAGE2; - a2p->screen_dirty = true; - break; - - case EWM_A2P_SS_SPKR: - // TODO Implement speaker support - break; - - case EWM_A2P_SS_PB0: - return a2p->buttons[0]; - case EWM_A2P_SS_PB1: - return a2p->buttons[1]; - case EWM_A2P_SS_PB2: - return a2p->buttons[2]; - case EWM_A2P_SS_PB3: - return a2p->buttons[3]; - - case EWM_A2P_SS_SETAN0: - break; - case EWM_A2P_SS_SETAN1: - break; - case EWM_A2P_SS_SETAN2: - break; - case EWM_A2P_SS_SETAN3: - break; - - case EWM_A2P_SS_CLRAN0: - break; - case EWM_A2P_SS_CLRAN1: - break; - case EWM_A2P_SS_CLRAN2: - break; - case EWM_A2P_SS_CLRAN3: - break; - - default: - printf("[A2P] Unexpected read at $%.4X\n", addr); - break; - } - return 0; -} - -void a2p_iom_write(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr, uint8_t b) { - struct a2p_t *a2p = (struct a2p_t*) mem->obj; - switch (addr) { - - case EWM_A2P_SS_KBDSTRB: - a2p->key &= 0x7f; - break; - - case EWM_A2P_SS_SPKR: - // TODO Implement speaker support - break; - - case EWM_A2P_SS_SCREEN_MODE_GRAPHICS: - a2p->screen_mode = EWM_A2P_SCREEN_MODE_GRAPHICS; - a2p->screen_dirty = true; - break; - case EWM_A2P_SS_SCREEN_MODE_TEXT: - a2p->screen_mode = EWM_A2P_SCREEN_MODE_TEXT; - a2p->screen_dirty = true; - break; - - case EWM_A2P_SS_GRAPHICS_MODE_LGR: - a2p->screen_graphics_mode = EWM_A2P_SCREEN_GRAPHICS_MODE_LGR; - a2p->screen_dirty = true; - break; - case EWM_A2P_SS_GRAPHICS_MODE_HGR: - a2p->screen_graphics_mode = EWM_A2P_SCREEN_GRAPHICS_MODE_HGR; - a2p->screen_dirty = true; - break; - - case EWM_A2P_SS_GRAPHICS_STYLE_FULL: - a2p->screen_graphics_style = EWM_A2P_SCREEN_GRAPHICS_STYLE_FULL; - a2p->screen_dirty = true; - break; - case EWM_A2P_SS_GRAPHICS_STYLE_MIXED: - a2p->screen_graphics_style = EWM_A2P_SCREEN_GRAPHICS_STYLE_MIXED; - a2p->screen_dirty = true; - break; - - case EWM_A2P_SS_SCREEN_PAGE1: - a2p->screen_page = EWM_A2P_SCREEN_PAGE1; - a2p->screen_dirty = true; - break; - case EWM_A2P_SS_SCREEN_PAGE2: - a2p->screen_page = EWM_A2P_SCREEN_PAGE2; - a2p->screen_dirty = true; - break; - - case EWM_A2P_SS_SETAN0: - break; - case EWM_A2P_SS_SETAN1: - break; - case EWM_A2P_SS_SETAN2: - break; - case EWM_A2P_SS_SETAN3: - break; - - case EWM_A2P_SS_CLRAN0: - break; - case EWM_A2P_SS_CLRAN1: - break; - case EWM_A2P_SS_CLRAN2: - break; - case EWM_A2P_SS_CLRAN3: - break; - - default: - printf("[A2P] Unexpected write at $%.4X\n", addr); - break; - } -} - -uint8_t a2p_screen_txt_read(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr) { - struct a2p_t *a2p = (struct a2p_t*) mem->obj; - return a2p->screen_txt_data[addr - mem->start]; -} - -void a2p_screen_txt_write(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr, uint8_t b) { - struct a2p_t *a2p = (struct a2p_t*) mem->obj; - a2p->screen_txt_data[addr - mem->start] = b; - a2p->screen_dirty = true; -} - -uint8_t a2p_screen_hgr_read(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr) { - struct a2p_t *a2p = (struct a2p_t*) mem->obj; - return a2p->screen_hgr_data[addr - mem->start]; -} - -void a2p_screen_hgr_write(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr, uint8_t b) { - struct a2p_t *a2p = (struct a2p_t*) mem->obj; - a2p->screen_hgr_data[addr - mem->start] = b; - a2p->screen_dirty = true; -} - -int a2p_init(struct a2p_t *a2p, struct cpu_t *cpu) { - memset(a2p, 0x00, sizeof(struct a2p_t)); - - a2p->cpu = cpu; - - a2p->ram = cpu_add_ram(cpu, 0x0000, 48 * 1024); - a2p->ram->description = "ram/a2p/$0000"; - a2p->roms[0] = cpu_add_rom_file(cpu, 0xd000, "roms/341-0011.bin"); // AppleSoft BASIC D000 - a2p->roms[1] = cpu_add_rom_file(cpu, 0xd800, "roms/341-0012.bin"); // AppleSoft BASIC D800 - a2p->roms[2] = cpu_add_rom_file(cpu, 0xe000, "roms/341-0013.bin"); // AppleSoft BASIC E000 - a2p->roms[3] = cpu_add_rom_file(cpu, 0xe800, "roms/341-0014.bin"); // AppleSoft BASIC E800 - a2p->roms[4] = cpu_add_rom_file(cpu, 0xf000, "roms/341-0015.bin"); // AppleSoft BASIC E800 - a2p->roms[5] = cpu_add_rom_file(cpu, 0xf800, "roms/341-0020.bin"); // AppleSoft BASIC Autostart Monitor F8000 - a2p->iom = cpu_add_iom(cpu, 0xc000, 0xc07f, a2p, a2p_iom_read, a2p_iom_write); - a2p->iom->description = "iom/a2p/$C000"; - - a2p->dsk = ewm_dsk_create(cpu); - - // TODO Move into a2p_t - struct ewm_alc_t *alc = ewm_alc_create(cpu); - if (alc == NULL) { - fprintf(stderr, "[A2P] Could not create Apple Language Card\n"); - return -1; - } - - // TODO Introduce ewm_scr_t that captures everything related to the apple 2 screen so that it can be re-used. - - a2p->screen_txt_data = malloc(2 * 1024); - a2p->screen_txt_iom = cpu_add_iom(cpu, 0x0400, 0x0bff, a2p, a2p_screen_txt_read, a2p_screen_txt_write); - a2p->screen_txt_iom->description = "ram/a2p/$0400"; - - a2p->screen_hgr_data = malloc(16 * 1024); - a2p->screen_hgr_iom = cpu_add_iom(cpu, 0x2000, 0x5fff, a2p, a2p_screen_hgr_read, a2p_screen_hgr_write); - a2p->screen_hgr_iom->description = "ram/a2p/$2000"; - - return 0; -} - -struct a2p_t *a2p_create(struct cpu_t *cpu) { - struct a2p_t *a2p = malloc(sizeof(struct a2p_t)); - if (a2p_init(a2p, cpu) != 0) { - free(a2p); - a2p = NULL; - } - return a2p; -} - -void a2p_destroy(struct a2p_t *a2p) { - // TODO -} - -int a2p_load_disk(struct a2p_t *a2p, int drive, char *path) { - return ewm_dsk_set_disk_file(a2p->dsk, drive, false, path); -} diff --git a/chr.c b/chr.c index 868f869..60d886e 100644 --- a/chr.c +++ b/chr.c @@ -96,7 +96,11 @@ static SDL_Texture *_generate_texture(SDL_Renderer *renderer, uint8_t rom_data[2 } } texture = SDL_CreateTextureFromSurface(renderer, surface); - SDL_FreeSurface(surface); + if (texture == NULL) { + fprintf(stderr, "Cannot generate RGBSurface: %s\n", SDL_GetError()); + } + } else { + fprintf(stderr, "Cannot generate Texture: %s\n", SDL_GetError()); } return texture; diff --git a/ewm.c b/ewm.c index e3bf1f0..79b02d7 100644 --- a/ewm.c +++ b/ewm.c @@ -20,283 +20,15 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include -#include -#include -#include -#include -#include - -#include "cpu.h" -#include "mem.h" -#include "pia.h" - -#include "a2p.h" -#include "scr.h" -#include "dsk.h" -#include "scr.h" -#include "sdl.h" - -// TODO Get rid of these, use a struct ewm_t? - -static struct cpu_t *cpu = NULL; -static struct a2p_t *a2p = NULL; -static struct scr_t *scr = NULL; - -// Machine Setup - -#define EWM_DISPLAY_TYPE_NONE 0 -#define EWM_DISPLAY_TYPE_TTY 1 -#define EWM_DISPLAY_TYPE_SDL 2 - -typedef int (*ewm_machine_create_f)(); - -struct ewm_machine_t { - char *name; - char *description; - int display_type; - ewm_machine_create_f create; -}; - -#define EWM_MEMORY_TYPE_RAM 0 -#define EWM_MEMORY_TYPE_ROM 1 - -struct ewm_memory_t { - int type; - char *path; - uint16_t address; - struct ewm_memory_t *next; -}; - -// Apple 1 / 6502 / 8K RAM / WOZ Monitor - -static int setup_apple1() { - cpu = cpu_create(EWM_CPU_MODEL_6502); - struct pia_t *pia = malloc(sizeof(struct pia_t)); - pia_init(pia); - cpu_add_ram(cpu, 0x0000, 8 * 1024 - 1); - cpu_add_rom_file(cpu, 0xff00, "roms/apple1.rom"); - cpu_add_iom(cpu, EWM_A1_PIA6820_ADDR, EWM_A1_PIA6820_ADDR + EWM_A1_PIA6820_LENGTH - 1, pia, pia_read, pia_write); - return 0; -} - -// Replica 1 / 65C02 / 32K RAM / Krusader Assembler & Monitor - -static int setup_replica1() { - cpu = cpu_create(EWM_CPU_MODEL_65C02); - struct pia_t *pia = malloc(sizeof(struct pia_t)); - pia_init(pia); - cpu_add_ram(cpu, 0x0000, 32 * 1024 - 1); - cpu_add_rom_file(cpu, 0xe000, "roms/krusader.rom"); - cpu_add_iom(cpu, EWM_A1_PIA6820_ADDR, EWM_A1_PIA6820_ADDR + EWM_A1_PIA6820_LENGTH - 1, pia, pia_read, pia_write); - return 0; -} - -// Apple ][+ / 6502 / 48K RAM / Original ROMs - -static int setup_apple2plus() { - cpu = cpu_create(EWM_CPU_MODEL_6502); - a2p = a2p_create(cpu); - scr = ewm_scr_create(a2p, renderer); - ewm_scr_color_scheme(scr, EWM_SCR_COLOR_SCHEME_COLOR); - return 0; -} - -struct ewm_memory_t *parse_memory(char *s) { - char *type = strsep(&s, ":"); - if (type == NULL) { // || (strcmp(type, "ram") && strcmp(type, "rom"))) { - printf("type fail\n"); - return NULL; - } - - char *address = strsep(&s, ":"); - if (address == NULL) { - printf("address fail\n"); - return NULL; - } - - char *path = strsep(&s, ":"); - if (path == NULL) { - printf("path fail\n"); - return NULL; - } - - struct ewm_memory_t *m = (struct ewm_memory_t*) malloc(sizeof(struct ewm_memory_t)); - m->type = strcmp(type, "ram") ? EWM_MEMORY_TYPE_RAM : EWM_MEMORY_TYPE_ROM; - m->path = path; - m->address = atoi(address); - m->next = NULL; - - return m; -} - -static struct ewm_machine_t machines[] = { - { "apple1", "Apple 1", EWM_DISPLAY_TYPE_TTY, setup_apple1 }, - { "replica1", "Replica 1", EWM_DISPLAY_TYPE_TTY, setup_replica1 }, - { "apple2plus", "Apple ][+", EWM_DISPLAY_TYPE_SDL, setup_apple2plus }, - { NULL, NULL, EWM_DISPLAY_TYPE_NONE, NULL } -}; - -static struct option options[] = { - { "machine", required_argument, NULL, 'm' }, - { "strict", no_argument, NULL, 's' }, - { "trace", optional_argument, NULL, 't' }, - { "memory", required_argument, NULL, 'x' }, - { "drive1", required_argument, NULL, 'a' }, - { "drive2", required_argument, NULL, 'b' }, - { NULL, 0, NULL, 0 } -}; - -static struct ewm_machine_t *machine_with_name(char *name) { - for (struct ewm_machine_t *m = machines; m->name != NULL; m++) { - if (strcmp(m->name, name) == 0) { - return m; - } - } - return NULL; -} +#include "one.h" +#include "two.h" int main(int argc, char **argv) { - struct ewm_machine_t *machine = &machines[0]; - bool strict = false; - char *trace_path = NULL; - struct ewm_memory_t *memory = NULL; - char *drive1 = NULL; - char *drive2 = NULL; - - char ch; - while ((ch = getopt_long(argc, argv, "m:", options, NULL)) != -1) { - switch (ch) { - case 'm': - machine = machine_with_name(optarg); - break; - case 's': - strict = true; - break; - case 't': - trace_path = optarg ? optarg : "/dev/stderr"; - break; - case 'x': { - struct ewm_memory_t *m = parse_memory(optarg); - if (m == NULL) { - exit(1); - } - if (memory == NULL) { - memory = m; - } else { - memory->next = m; - } - break; - } - case 'a': - drive1 = optarg; - break; - case 'b': - drive2 = optarg; - break; - } + if (strcmp(argv[1], "one") == 0) { + return ewm_one_main(argc-1, &argv[1]); } - - argc -= optind; - argv += optind; - - if (machine == NULL) { - fprintf(stderr, "Usage: ewm --machine apple1|replica1|apple2plus\n"); - exit(1); + if (strcmp(argv[1], "two") == 0) { + return ewm_two_main(argc-1, &argv[1]); } - - // - // Initialize the display - // - - switch (machine->display_type) { - case EWM_DISPLAY_TYPE_NONE: - fprintf(stderr, "[EWM] TODO EWM_DISPLAY_TYPE_NONE\n"); - exit(1); - break; - case EWM_DISPLAY_TYPE_TTY: - fprintf(stderr, "[EWM] TODO EWM_DISPLAY_TYPE_TTY\n"); - break; - case EWM_DISPLAY_TYPE_SDL: - sdl_initialize(); - break; - } - - // Create the machine and initialize it - - fprintf(stderr, "[EWM] Creating %s\n", machine->description); - - machine->create(cpu); - - // TODO This really does not belong here. it is probably better to - // pass arguments to setup_apple2plus so that it can handle its - // specific arguments like --drive1/drive2 and probably some more - // in the future. - - if (a2p != NULL) { - if (drive1 != NULL) { - if (a2p_load_disk(a2p, EWM_DSK_DRIVE1, drive1) != 0) { - fprintf(stderr, "[A2P] Cannot load Drive 1 with %s\n", drive1); - exit(1); - } - } - if (drive2 != NULL) { - if (a2p_load_disk(a2p, EWM_DSK_DRIVE2, drive2) != 0) { - fprintf(stderr, "[A2P] Cannot load Drive 2 with %s\n", drive2); - exit(1); - } - } - } - - struct ewm_memory_t *m = memory; - while (m != NULL) { - fprintf(stderr, "[EWM] Adding %s $%.4X %s\n", m->type == EWM_MEMORY_TYPE_RAM ? "RAM" : "ROM", m->address, m->path); - if (m->type == EWM_MEMORY_TYPE_RAM) { - if (cpu_add_ram_file(cpu, m->address, m->path) == NULL) { - fprintf(stderr, "[EWM] Failed to add RAM from %s\n", m->path); - exit(1); - } - } else { - if (cpu_add_rom_file(cpu, m->address, m->path) == NULL) { - fprintf(stderr, "[EWM] Failed to add ROM from %s\n", m->path); - exit(1); - } - } - m = m->next; - } - - cpu_strict(cpu, strict); - cpu_trace(cpu, trace_path); - - cpu_reset(cpu); - - switch (machine->display_type) { - case EWM_DISPLAY_TYPE_NONE: - fprintf(stderr, "[EWM] TODO EWM_DISPLAY_TYPE_NONE\n"); - exit(1); - break; - case EWM_DISPLAY_TYPE_TTY: - fprintf(stderr, "[EWM] TODO EWM_DISPLAY_TYPE_TTY\n"); - exit(1); - break; - case EWM_DISPLAY_TYPE_SDL: - sdl_main(cpu, a2p, scr); - break; - } - - // Clean up - - if (scr != NULL) { - ewm_scr_destroy(scr); - scr = NULL; - } - - if (a2p != NULL) { - a2p_destroy(a2p); - a2p = NULL; - } - - cpu_destroy(cpu); - - return 0; + return 1; } diff --git a/one.c b/one.c new file mode 100644 index 0000000..aa55a34 --- /dev/null +++ b/one.c @@ -0,0 +1,247 @@ +// The MIT License (MIT) +// +// Copyright (c) 2015 Stefan Arentz - http://github.com/st3fan/ewm +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include + +#include + +#include "cpu.h" +#include "mem.h" +#include "pia.h" +#include "tty.h" +#include "one.h" + +struct ewm_one_t *ewm_one_create(int type, SDL_Renderer *renderer) { + struct ewm_one_t *one = (struct ewm_one_t*) malloc(sizeof(struct ewm_one_t)); + if (ewm_one_init(one, type, renderer) != 0) { + free(one); + one = NULL; + } + return one; +} + +static void ewm_one_pia_callback(struct ewm_pia_t *pia, void *obj, uint8_t ddr, uint8_t v) { + struct ewm_one_t *one = (struct ewm_one_t*) obj; + ewm_tty_write(one->tty, v); +} + +int ewm_one_init(struct ewm_one_t *one, int type, SDL_Renderer *renderer) { + memset(one, 0, sizeof(struct ewm_one_t)); + switch (type) { + case EWM_ONE_TYPE_APPLE1: + one->cpu = cpu_create(EWM_CPU_MODEL_6502); + cpu_add_ram(one->cpu, 0x0000, 8 * 1024 - 1); + cpu_add_rom_file(one->cpu, 0xff00, "roms/apple1.rom"); + one->tty = ewm_tty_create(renderer); + one->pia = ewm_pia_create(one->cpu); + one->pia->callback = ewm_one_pia_callback; + one->pia->callback_obj = one; + break; + case EWM_ONE_TYPE_REPLICA1: + one->cpu = cpu_create(EWM_CPU_MODEL_65C02); + cpu_add_ram(one->cpu, 0x0000, 32 * 1024 - 1); + cpu_add_rom_file(one->cpu, 0xe000, "roms/krusader.rom"); + one->tty = ewm_tty_create(renderer); + one->pia = ewm_pia_create(one->cpu); + one->pia->callback = ewm_one_pia_callback; + one->pia->callback_obj = one; + break; + } + return 0; +} + +void ewm_one_destroy(struct ewm_one_t *one) { + // TODO +} + +static void ewm_one_keydown(struct ewm_one_t *one, uint8_t key) { + ewm_pia_set_ina(one->pia, key | 0x80); + ewm_pia_set_irqa1(one->pia); +} + +static bool ewm_one_poll_event(struct ewm_one_t *one, SDL_Window *window) { + SDL_Event event; + while (SDL_PollEvent(&event) != 0) { + switch (event.type) { + case SDL_QUIT: + return false; + case SDL_KEYDOWN: + if (event.key.keysym.mod & KMOD_CTRL) { + if (event.key.keysym.sym >= SDLK_a && event.key.keysym.sym <= SDLK_z) { + ewm_one_keydown(one, event.key.keysym.sym - SDLK_a); + } else { + // TODO Implement control codes 1b - 1f + } + } else if (event.key.keysym.mod & KMOD_GUI) { + switch (event.key.keysym.sym) { + case SDLK_ESCAPE: + cpu_reset(one->cpu); + break; + case SDLK_RETURN: + if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) { + SDL_SetWindowFullscreen(window, 0); + } else { + SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN); + } + break; + } + } else if (event.key.keysym.mod == KMOD_NONE) { + switch (event.key.keysym.sym) { + case SDLK_RETURN: + ewm_one_keydown(one, 0x0d); // CR + break; + case SDLK_TAB: + ewm_one_keydown(one, 0x09); // HT + case SDLK_DELETE: + ewm_one_keydown(one, 0x7f); // DEL + break; + case SDLK_LEFT: + ewm_one_keydown(one, 0x08); // BS + break; + case SDLK_RIGHT: + ewm_one_keydown(one, 0x15); // NAK + break; + case SDLK_UP: + ewm_one_keydown(one, 0x0b); // VT + break; + case SDLK_DOWN: + ewm_one_keydown(one, 0x0a); // LF + break; + case SDLK_ESCAPE: + ewm_one_keydown(one, 0x1b); // ESC + break; + } + } + break; + case SDL_TEXTINPUT: + if (strlen(event.text.text) == 1) { + ewm_one_keydown(one, toupper(event.text.text[0])); + } + break; + } + } + return true; +} + +static bool ewm_one_step_cpu(struct ewm_one_t *one, int cycles) { + while (true) { + int ret = cpu_step(one->cpu); + if (ret < 0) { + switch (ret) { + case EWM_CPU_ERR_UNIMPLEMENTED_INSTRUCTION: + fprintf(stderr, "CPU: Exited because of unimplemented instructions 0x%.2x at 0x%.4x\n", + mem_get_byte(one->cpu, one->cpu->state.pc), one->cpu->state.pc); + break; + case EWM_CPU_ERR_STACK_OVERFLOW: + fprintf(stderr, "CPU: Exited because of stack overflow at 0x%.4x\n", one->cpu->state.pc); + break; + case EWM_CPU_ERR_STACK_UNDERFLOW: + fprintf(stderr, "CPU: Exited because of stack underflow at 0x%.4x\n", one->cpu->state.pc); + break; + } + cpu_nmi(one->cpu); + } + + cycles -= ret; + if (cycles <= 0) { + break; + } + } + return true; +} + +int ewm_one_main(int argc, char **argv) { + // Parse Apple 1 specific options + + // TODO + + // Setup SDL + + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_EVENTS) < 0) { + fprintf(stderr, "Failed to initialize SDL: %s\n", SDL_GetError()); + return 1; + } + + SDL_Window *window = SDL_CreateWindow("EWM v0.1 / Apple 1", 400, 60, 280*3, 192*3, SDL_WINDOW_SHOWN); + if (window == NULL) { + fprintf(stderr, "Failed create window: %s\n", SDL_GetError()); + return 1; + } + + SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); + if (renderer == NULL) { + fprintf(stderr, "Failed to create renderer: %s\n", SDL_GetError()); + return 1; + } + + SDL_RenderSetLogicalSize(renderer, 280*3, 192*3); + + // Create the machine + + struct ewm_one_t *one = ewm_one_create(EWM_ONE_TYPE_REPLICA1, renderer); + if (one == NULL) { + fprintf(stderr, "Failed to create ewm_one_t\n"); + return 1; + } + + cpu_reset(one->cpu); + + // Main loop + + SDL_StartTextInput(); + + Uint32 ticks = SDL_GetTicks(); + + while (true) { + if (!ewm_one_poll_event(one, window)) { // TODO Move window into one + break; + } + + // This is very basic throttling that does bursts of CPU cycles. + + if ((SDL_GetTicks() - ticks) >= (1000 / 30)) { // TODO EWM_ONE_TTY_FPS ? + if (!ewm_one_step_cpu(one, 1000000 / 30)) { + break; + } + + if (one->tty->screen_dirty) { + SDL_SetRenderDrawColor(one->tty->renderer, 0, 0, 0, 255); + SDL_RenderClear(one->tty->renderer); + ewm_tty_refresh(one->tty); + one->tty->screen_dirty = false; + SDL_RenderPresent(one->tty->renderer); + } + + ticks = SDL_GetTicks(); + } + } + + // Destroy SDL + + SDL_DestroyWindow(window); + SDL_DestroyRenderer(renderer); + SDL_Quit(); + + return 0; +} + diff --git a/sdl.h b/one.h similarity index 71% rename from sdl.h rename to one.h index 13dca08..118780b 100644 --- a/sdl.h +++ b/one.h @@ -20,21 +20,29 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#ifndef EWM_SDL_H -#define EWM_SDL_H +#ifndef EWM_ONE_H +#define EWM_ONE_H + +#define EWM_ONE_TYPE_APPLE1 0 +#define EWM_ONE_TYPE_REPLICA1 1 #include -#define EWM_SDL_FPS (50) - -extern SDL_Window *window; -extern SDL_Renderer *renderer; - struct cpu_t; -struct a2p_t; -struct scr_t; +struct ewm_tty_t; +struct ewm_pia_t; -void sdl_initialize(); -void sdl_main(struct cpu_t *cpu, struct a2p_t *a2p, struct scr_t *scr); +struct ewm_one_t { + int type; + struct cpu_t *cpu; + struct ewm_tty_t *tty; + struct ewm_pia_t *pia; +}; -#endif // EWM_SDL_H +struct ewm_one_t *ewm_one_create(int type, SDL_Renderer *renderer); +int ewm_one_init(struct ewm_one_t *one, int type, SDL_Renderer *renderer); +void ewm_one_destroy(struct ewm_one_t *one); + +int ewm_one_main(int argc, char **argv); + +#endif // EWM_ONE_H diff --git a/pia.c b/pia.c index 5924db1..67e2843 100644 --- a/pia.c +++ b/pia.c @@ -24,109 +24,119 @@ #include #include #include -#include +#include #include "cpu.h" #include "pia.h" // This implements a 6820 Peripheral I/O Adapter. On the Apple I this -// is what connects the keyboard and display logic to the CPU. - -static void pia_dsp_write(uint8_t b) { - b &= 0b01111111; - if (b == '\r') { - b = '\n'; - } - addch(b); - refresh(); -} - -void pia_init(struct pia_t *pia) { - initscr(); - raw(); - noecho(); - pia->a = 0; - pia->cra = 0; - pia->b = 0; - pia->crb = 0; -} - -void pia_trace(struct pia_t *pia, uint8_t enable) { - pia->trace = enable; -} - -uint8_t pia_read(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr) { - struct pia_t *pia = (struct pia_t*) mem->obj; - - uint8_t result = 0; +// is what connects the keyboard and display logic to the CPU. The +// implementation is not complete but does enough to support how the +// keyboard and display are hooked up. +static uint8_t pia_read(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr) { + struct ewm_pia_t *pia = (struct ewm_pia_t*) mem->obj; switch (addr) { - case EWM_A1_PIA6820_KBD: { - result = pia->a; - break; - } - - case EWM_A1_PIA6820_KBDCR: { - int c = getch(); - if (c != ERR) { - /* TODO: Remove this, this is not how we want to stop the emulator */ - if (c == 3) { - exit(1); - } - if (c == '\n') { - c = '\r'; - } - pia->a = c | 0x80; // Set the high bit - WHat is up with the high bits. Document this. - } - result = (c == ERR) ? 0x00 : 0x80; - break; - } - - case EWM_A1_PIA6820_DSP: { - result = 0; - break; - } - - case EWM_A1_PIA6820_DSPCR: { - result = 0; - break; - } - } - - if (pia->trace) { - fprintf(stderr, "PIA: READ BYTE %.2X FROM %.4X\n", result, addr); - } - - return result; -} - -void pia_write(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr, uint8_t b) { - struct pia_t *pia = (struct pia_t*) mem->obj; - - if (pia->trace) { - fprintf(stderr, "PIA: WRITING BYTE %.2X TO %.4X\n", b, addr); - } - - switch (addr) { - case EWM_A1_PIA6820_KBD: { /* KBD */ - break; - } - - case EWM_A1_PIA6820_KBDCR: { /* KBDCR */ - pia->cra = b; - break; - } - - case EWM_A1_PIA6820_DSP: { /* DSP */ - if (pia->crb != 0x00) { /* TODO: Check the actual flag */ - pia_dsp_write(b); + case EWM_A1_PIA6820_KBD_DDR: + if (pia->ctla & 0b00000100) { + pia->ctla &= 0b01111111; // Clear IRQA1 + return (pia->outa & pia->ddra) | (pia->ina & ~pia->ddra); + } else { + return pia->ddra; } break; - } - - case EWM_A1_PIA6820_DSPCR: { /* DSPCR */ - pia->crb = b; + case EWM_A1_PIA6820_KBD_CTL: + return pia->ctla; + break; + case EWM_A1_PIA6820_DSP_DDR: + if (pia->ctlb & 0b00000100) { + return (pia->outb & pia->ddrb) | (pia->inb & ~pia->ddrb); + } else { + return pia->ddrb; + } + break; + case EWM_A1_PIA6820_DSP_CTL: + return pia->ctlb; + break; + } + return 0; +} + +static void pia_write(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr, uint8_t v) { + struct ewm_pia_t *pia = (struct ewm_pia_t*) mem->obj; + switch (addr) { + case EWM_A1_PIA6820_KBD_DDR: + // Check B2 (DDR Access) + if (pia->ctla & 0b00000100) { + // Write output register and run callback + pia->outa = v; + if (pia->callback) { + pia->callback(pia, pia->callback_obj, EWM_PIA6820_DDRA, v); + } + } else { + // Write DDR register + pia->ddra = v; + // TODO Do we need a callback? Not relevant for Apple 1? + } + break; + case EWM_A1_PIA6820_KBD_CTL: + pia->ctla = (v & 0b00111111); + break; + case EWM_A1_PIA6820_DSP_DDR: + // Check B2 (DDR Access) + if (pia->ctlb & 0b00000100) { + // Write output register and run callback + pia->outb = v; + if (pia->callback) { + pia->callback(pia, pia->callback_obj, EWM_PIA6820_DDRB, v); + } + } else { + // Write DDR register + pia->ddrb = v; + // TODO Do we need a callback? Not relevant for Apple 1? + } + break; + case EWM_A1_PIA6820_DSP_CTL: + pia->ctlb = (v & 0b00111111); break; - } } } + +struct ewm_pia_t *ewm_pia_create(struct cpu_t *cpu) { + struct ewm_pia_t *pia = (struct ewm_pia_t*) malloc(sizeof(struct ewm_pia_t)); + if (ewm_pia_init(pia, cpu) != 0) { + free(pia); + pia = NULL; + } + return pia; +} + +int ewm_pia_init(struct ewm_pia_t *pia, struct cpu_t *cpu) { + memset(pia, 0, sizeof(struct ewm_pia_t)); + cpu_add_iom(cpu, EWM_A1_PIA6820_ADDR, EWM_A1_PIA6820_ADDR + EWM_A1_PIA6820_LENGTH - 1, pia, pia_read, pia_write); + return 0; +} + +void ewm_pia_destroy(struct ewm_pia_t *pia) { + free(pia); +} + +void ewm_pia_set_outa(struct ewm_pia_t *pia, uint8_t v) { + pia->outa = v; +} + +void ewm_pia_set_ina(struct ewm_pia_t *pia, uint8_t v) { + pia->ina = v; +} + +void ewm_pia_set_outb(struct ewm_pia_t *pia, uint8_t v) { + pia->outb = v; +} + +void ewm_pia_set_inb(struct ewm_pia_t *pia, uint8_t v) { + pia->inb = v; +} + +void ewm_pia_set_irqa1(struct ewm_pia_t *pia) { + pia->ctla |= 0b10000000; // Set IRQA1 +} diff --git a/pia.h b/pia.h index 1c718be..1e6fca8 100644 --- a/pia.h +++ b/pia.h @@ -34,22 +34,38 @@ #define EWM_A1_PIA6820_ADDR 0xd010 #define EWM_A1_PIA6820_LENGTH 0x0100 -#define EWM_A1_PIA6820_KBD (EWM_A1_PIA6820_ADDR + EWM_PIA6820_DDRA) -#define EWM_A1_PIA6820_KBDCR (EWM_A1_PIA6820_ADDR + EWM_PIA6820_CTLA) -#define EWM_A1_PIA6820_DSP (EWM_A1_PIA6820_ADDR + EWM_PIA6820_DDRB) -#define EWM_A1_PIA6820_DSPCR (EWM_A1_PIA6820_ADDR + EWM_PIA6820_CTLB) +#define EWM_A1_PIA6820_KBD_DDR (EWM_A1_PIA6820_ADDR + EWM_PIA6820_DDRA) +#define EWM_A1_PIA6820_KBD_CTL (EWM_A1_PIA6820_ADDR + EWM_PIA6820_CTLA) +#define EWM_A1_PIA6820_DSP_DDR (EWM_A1_PIA6820_ADDR + EWM_PIA6820_DDRB) +#define EWM_A1_PIA6820_DSP_CTL (EWM_A1_PIA6820_ADDR + EWM_PIA6820_CTLB) -struct pia_t { - uint8_t a; - uint8_t cra; - uint8_t b; - uint8_t crb; - uint8_t trace; +struct ewm_pia_t; + +typedef void (*ewm_pia_callback_t)(struct ewm_pia_t *pia, void *obj, uint8_t ddr, uint8_t v); + +struct ewm_pia_t { + uint8_t ina; + uint8_t outa; + uint8_t ddra; + uint8_t ctla; + uint8_t inb; + uint8_t outb; + uint8_t ddrb; + uint8_t ctlb; + ewm_pia_callback_t callback; + void *callback_obj; }; -void pia_init(struct pia_t *pia); -void pia_trace(struct pia_t *pia, uint8_t enable); -uint8_t pia_read(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr); -void pia_write(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr, uint8_t b); +struct ewm_pia_t *ewm_pia_create(struct cpu_t *cpu); +int ewm_pia_init(struct ewm_pia_t *pia, struct cpu_t *cpu); +void ewm_pia_destroy(struct ewm_pia_t *pia); + +void ewm_pia_set_outa(struct ewm_pia_t *pia, uint8_t v); +void ewm_pia_set_ina(struct ewm_pia_t *pia, uint8_t v); + +void ewm_pia_set_outb(struct ewm_pia_t *pia, uint8_t v); +void ewm_pia_set_inb(struct ewm_pia_t *pia, uint8_t v); + +void ewm_pia_set_irqa1(struct ewm_pia_t *pia); #endif diff --git a/scr.c b/scr.c index 0a58b7b..3ff62cd 100644 --- a/scr.c +++ b/scr.c @@ -27,7 +27,7 @@ #include "mem.h" #include "cpu.h" -#include "a2p.h" +#include "two.h" #include "chr.h" #include "scr.h" @@ -39,8 +39,8 @@ static int txt_line_offsets[24] = { }; static inline void scr_render_character(struct scr_t *scr, int row, int column) { - uint16_t base = (scr->a2p->screen_page == EWM_A2P_SCREEN_PAGE1) ? 0x0400 : 0x0800; - uint8_t c = scr->a2p->screen_txt_data[((txt_line_offsets[row] + base) + column) - 0x0400]; + uint16_t base = (scr->two->screen_page == EWM_A2P_SCREEN_PAGE1) ? 0x0400 : 0x0800; + uint8_t c = scr->two->screen_txt_data[((txt_line_offsets[row] + base) + column) - 0x0400]; if (scr->chr->characters[c] != NULL) { SDL_Rect dst; dst.x = column * 21; @@ -81,8 +81,8 @@ static SDL_Color lores_colors[16] = { }; static inline void scr_render_lores_block(struct scr_t *scr, int row, int column) { - uint16_t base = (scr->a2p->screen_page == EWM_A2P_SCREEN_PAGE1) ? 0x0400 : 0x0800; - uint8_t block = scr->a2p->screen_txt_data[((txt_line_offsets[row] + base) + column) - 0x0400]; + uint16_t base = (scr->two->screen_page == EWM_A2P_SCREEN_PAGE1) ? 0x0400 : 0x0800; + uint8_t block = scr->two->screen_txt_data[((txt_line_offsets[row] + base) + column) - 0x0400]; if (block != 0) { SDL_Rect dst; dst.x = column * 21; @@ -106,7 +106,7 @@ static inline void scr_render_lores_block(struct scr_t *scr, int row, int column } static inline void scr_render_lgr_screen(struct scr_t *scr) { - bool mixed = (scr->a2p->screen_graphics_style == EWM_A2P_SCREEN_GRAPHICS_STYLE_MIXED); + bool mixed = (scr->two->screen_graphics_style == EWM_A2P_SCREEN_GRAPHICS_STYLE_MIXED); // Render graphics int rows = mixed ? 20 : 24; @@ -173,7 +173,7 @@ static SDL_Color hgr_colors[16] = { static void inline scr_render_hgr_line_green(struct scr_t *scr, int line, uint16_t line_base) { int x = 0; for (int i = 0; i < 40; i++) { - uint8_t c = scr->a2p->screen_hgr_data[line_base + i]; + uint8_t c = scr->two->screen_hgr_data[line_base + i]; for (int j = 0; j < 7; j++) { SDL_Rect dst; dst.x = x * 3; @@ -197,7 +197,7 @@ static void inline scr_render_hgr_line_color(struct scr_t *scr, int line, uint16 int pixels[280], x = 0; for (int i = 0; i < 40; i++) { - uint8_t c = scr->a2p->screen_hgr_data[line_base + i]; + uint8_t c = scr->two->screen_hgr_data[line_base + i]; for (int j = 0; j < 7; j++) { if (c & (1 << j)) { if (x % 2 == 0) { @@ -245,8 +245,8 @@ static void inline scr_render_hgr_line_color(struct scr_t *scr, int line, uint16 static void inline scr_render_hgr_screen(struct scr_t *scr) { // Render graphics - int lines = (scr->a2p->screen_graphics_style == EWM_A2P_SCREEN_GRAPHICS_STYLE_MIXED) ? 160 : 192; - uint16_t hgr_base = hgr_page_offsets[scr->a2p->screen_page]; + int lines = (scr->two->screen_graphics_style == EWM_A2P_SCREEN_GRAPHICS_STYLE_MIXED) ? 160 : 192; + uint16_t hgr_base = hgr_page_offsets[scr->two->screen_page]; for (int line = 0; line < lines; line++) { uint16_t line_base = hgr_base + hgr_line_offsets[line]; if (scr->color_scheme == EWM_SCR_COLOR_SCHEME_COLOR) { @@ -257,7 +257,7 @@ static void inline scr_render_hgr_screen(struct scr_t *scr) { } // Render bottom 4 lines of text - if (scr->a2p->screen_graphics_style == EWM_A2P_SCREEN_GRAPHICS_STYLE_MIXED) { + if (scr->two->screen_graphics_style == EWM_A2P_SCREEN_GRAPHICS_STYLE_MIXED) { for (int row = 20; row < 24; row++) { for (int column = 0; column < 40; column++) { scr_render_character(scr, row, column); @@ -268,9 +268,9 @@ static void inline scr_render_hgr_screen(struct scr_t *scr) { // TODO This is the only actual API exposed -int ewm_scr_init(struct scr_t *scr, struct a2p_t *a2p, SDL_Renderer *renderer) { +int ewm_scr_init(struct scr_t *scr, struct ewm_two_t *two, SDL_Renderer *renderer) { memset(scr, 0x00, sizeof(struct scr_t)); - scr->a2p = a2p; + scr->two = two; scr->renderer = renderer; scr->chr = ewm_chr_create("roms/3410036.bin", EWM_CHR_ROM_TYPE_2716, renderer); if (scr->chr == NULL) { @@ -280,9 +280,9 @@ int ewm_scr_init(struct scr_t *scr, struct a2p_t *a2p, SDL_Renderer *renderer) { return 0; } -struct scr_t *ewm_scr_create(struct a2p_t *a2p, SDL_Renderer *renderer) { +struct scr_t *ewm_scr_create(struct ewm_two_t *two, SDL_Renderer *renderer) { struct scr_t *scr = malloc(sizeof(struct scr_t)); - if (ewm_scr_init(scr, a2p, renderer) != 0) { + if (ewm_scr_init(scr, two, renderer) != 0) { free(scr); scr = NULL; } @@ -297,12 +297,12 @@ void ewm_scr_update(struct scr_t *scr) { SDL_SetRenderDrawColor(scr->renderer, 0, 0, 0, 255); SDL_RenderClear(scr->renderer); - switch (scr->a2p->screen_mode) { + switch (scr->two->screen_mode) { case EWM_A2P_SCREEN_MODE_TEXT: scr_render_txt_screen(scr); break; case EWM_A2P_SCREEN_MODE_GRAPHICS: - switch (scr->a2p->screen_graphics_mode) { + switch (scr->two->screen_graphics_mode) { case EWM_A2P_SCREEN_GRAPHICS_MODE_LGR: scr_render_lgr_screen(scr); break; diff --git a/scr.h b/scr.h index edc3f13..feafbca 100644 --- a/scr.h +++ b/scr.h @@ -28,22 +28,21 @@ #define EWM_SCR_COLOR_SCHEME_GREEN 0 #define EWM_SCR_COLOR_SCHEME_COLOR 1 +struct ewm_two_t; struct ewm_chr_t; -struct cpu_t; -struct a2p_t; // The 'scr' object represents the screen. It renders the contents of // the machine. It has pluggable renders. struct scr_t { - struct a2p_t *a2p; + struct ewm_two_t *two; SDL_Renderer *renderer; struct ewm_chr_t *chr; int color_scheme; }; -struct scr_t *ewm_scr_create(struct a2p_t *a2p, SDL_Renderer *renderer); -int ewm_scr_init(struct scr_t *scr, struct a2p_t *a2p, SDL_Renderer *renderer); +struct scr_t *ewm_scr_create(struct ewm_two_t *two, SDL_Renderer *renderer); +int ewm_scr_init(struct scr_t *scr, struct ewm_two_t *two, SDL_Renderer *renderer); void ewm_scr_destroy(struct scr_t *scr); void ewm_scr_update(struct scr_t *scr); void ewm_scr_color_scheme(struct scr_t *scr, int color_scheme); diff --git a/scr_test.c b/scr_test.c index c2c039b..7eaf55f 100644 --- a/scr_test.c +++ b/scr_test.c @@ -25,21 +25,20 @@ #include "cpu.h" #include "mem.h" -#include "a2p.h" +#include "two.h" #include "scr.h" -#include "sdl.h" typedef void (*test_setup_t)(struct scr_t *scr); typedef void (*test_run_t)(struct scr_t *scr); void txt_full_refresh_setup(struct scr_t *scr) { - scr->a2p->screen_mode = EWM_A2P_SCREEN_MODE_TEXT; - scr->a2p->screen_page = EWM_A2P_SCREEN_PAGE1; + scr->two->screen_mode = EWM_A2P_SCREEN_MODE_TEXT; + scr->two->screen_page = EWM_A2P_SCREEN_PAGE1; for (uint16_t a = 0x0400; a <= 0x0bff; a++) { uint8_t v = 0xa0 + (random() % 64); - mem_set_byte(scr->a2p->cpu, a, v); + mem_set_byte(scr->two->cpu, a, v); } } @@ -48,14 +47,14 @@ void txt_full_refresh_test(struct scr_t *scr) { } void lgr_full_refresh_setup(struct scr_t *scr) { - scr->a2p->screen_mode = EWM_A2P_SCREEN_MODE_GRAPHICS; - scr->a2p->screen_page = EWM_A2P_SCREEN_PAGE1; - scr->a2p->screen_graphics_mode = EWM_A2P_SCREEN_GRAPHICS_MODE_LGR; - scr->a2p->screen_graphics_style = EWM_A2P_SCREEN_GRAPHICS_STYLE_FULL; + scr->two->screen_mode = EWM_A2P_SCREEN_MODE_GRAPHICS; + scr->two->screen_page = EWM_A2P_SCREEN_PAGE1; + scr->two->screen_graphics_mode = EWM_A2P_SCREEN_GRAPHICS_MODE_LGR; + scr->two->screen_graphics_style = EWM_A2P_SCREEN_GRAPHICS_STYLE_FULL; for (uint16_t a = 0x0400; a <= 0x0bff; a++) { uint8_t v = ((random() % 16) << 4) | (random() % 16); - mem_set_byte(scr->a2p->cpu, a, v); + mem_set_byte(scr->two->cpu, a, v); } } @@ -64,13 +63,13 @@ void lgr_full_refresh_test(struct scr_t *scr) { } void hgr_full_refresh_setup(struct scr_t *scr) { - scr->a2p->screen_mode = EWM_A2P_SCREEN_MODE_GRAPHICS; - scr->a2p->screen_page = EWM_A2P_SCREEN_PAGE1; - scr->a2p->screen_graphics_mode = EWM_A2P_SCREEN_GRAPHICS_MODE_HGR; - scr->a2p->screen_graphics_style = EWM_A2P_SCREEN_GRAPHICS_STYLE_FULL; + scr->two->screen_mode = EWM_A2P_SCREEN_MODE_GRAPHICS; + scr->two->screen_page = EWM_A2P_SCREEN_PAGE1; + scr->two->screen_graphics_mode = EWM_A2P_SCREEN_GRAPHICS_MODE_HGR; + scr->two->screen_graphics_style = EWM_A2P_SCREEN_GRAPHICS_STYLE_FULL; for (uint16_t a = 0x2000; a <= 0x5fff; a++) { - mem_set_byte(scr->a2p->cpu, a, random()); + mem_set_byte(scr->two->cpu, a, random()); } } @@ -94,22 +93,39 @@ void test(struct scr_t *scr, char *name, test_setup_t test_setup, test_run_t tes } int main() { - sdl_initialize(); + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_EVENTS) < 0) { + fprintf(stderr, "Failed to initialize SDL: %s\n", SDL_GetError()); + return 1; + } + + SDL_Window *window = SDL_CreateWindow("EWM v0.1 / Apple 1", 400, 60, 280*3, 192*3, SDL_WINDOW_SHOWN); + if (window == NULL) { + fprintf(stderr, "Failed create window: %s\n", SDL_GetError()); + return 1; + } + + SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); + if (renderer == NULL) { + fprintf(stderr, "Failed to create renderer: %s\n", SDL_GetError()); + return 1; + } + + SDL_RenderSetLogicalSize(renderer, 280*3, 192*3); + sleep(3); // Is this ok? Seems to be needed to get the window on the screen // Setup the CPU, Apple ][+ and it's screen. - struct cpu_t *cpu = cpu_create(EWM_CPU_MODEL_6502); - struct a2p_t *a2p = a2p_create(cpu); - struct scr_t *scr = ewm_scr_create(a2p, renderer); + struct ewm_two_t *two = ewm_two_create(EWM_TWO_TYPE_APPLE2PLUS, renderer); - test(scr, "txt_full_refresh", txt_full_refresh_setup, txt_full_refresh_test); - test(scr, "lgr_full_refresh", lgr_full_refresh_setup, lgr_full_refresh_test); - test(scr, "hgr_full_refresh", hgr_full_refresh_setup, hgr_full_refresh_test); + test(two->scr, "txt_full_refresh", txt_full_refresh_setup, txt_full_refresh_test); + test(two->scr, "lgr_full_refresh", lgr_full_refresh_setup, lgr_full_refresh_test); + test(two->scr, "hgr_full_refresh", hgr_full_refresh_setup, hgr_full_refresh_test); // Destroy DSL things SDL_DestroyWindow(window); + SDL_DestroyRenderer(renderer); SDL_Quit(); return 0; diff --git a/sdl.c b/sdl.c deleted file mode 100644 index 9676da8..0000000 --- a/sdl.c +++ /dev/null @@ -1,236 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2015 Stefan Arentz - http://github.com/st3fan/ewm -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include -#include -#include - -#include "cpu.h" -#include "a2p.h" -#include "scr.h" -#include "mem.h" - -#include "sdl.h" - -// TODO Remove these globals - Do we need a struct ewm_t? - -SDL_Window *window = NULL; -SDL_Renderer *renderer = NULL; - -void sdl_initialize() { - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_EVENTS) < 0) { - fprintf(stderr, "Failed to initialize SDL: %s\n", SDL_GetError()); - exit(1); - } - - // - - window = SDL_CreateWindow("EWM v0.1", 400, 60, 280*3, 192*3, SDL_WINDOW_SHOWN); - if (window == NULL) { - fprintf(stderr, "Failed create window: %s\n", SDL_GetError()); - exit(1); - } - - renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); - if (renderer == NULL) { - fprintf(stderr, "Failed to create renderer: %s\n", SDL_GetError()); - exit(1); - } - - SDL_RenderSetLogicalSize(renderer, 280*3, 192*3); -} - -void sdl_destroy() { - SDL_DestroyWindow(window); - SDL_Quit(); -} - -static bool sdl_poll_event(struct cpu_t *cpu, struct a2p_t *a2p, struct scr_t *scr) { - SDL_Event event; - while (SDL_PollEvent(&event) != 0) { - switch (event.type) { - case SDL_QUIT: - return false; - - case SDL_JOYBUTTONDOWN: - if (event.jbutton.button < EWM_A2P_BUTTON_COUNT) { - a2p->buttons[event.jbutton.button] = 1; - } - break; - case SDL_JOYBUTTONUP: - if (event.jbutton.button < EWM_A2P_BUTTON_COUNT) { - a2p->buttons[event.jbutton.button] = 0; - } - break; - - case SDL_KEYDOWN: - if (event.key.keysym.mod & KMOD_ALT) { - switch (event.key.keysym.sym) { - case SDLK_1: - a2p->buttons[0] = 1; - break; - case SDLK_2: - a2p->buttons[1] = 1; - break; - case SDLK_3: - a2p->buttons[2] = 1; - break; - case SDLK_4: - a2p->buttons[3] = 1; - break; - } - } else if (event.key.keysym.mod & KMOD_CTRL) { - if (event.key.keysym.sym >= SDLK_a && event.key.keysym.sym <= SDLK_z) { - a2p->key = (event.key.keysym.sym - SDLK_a) | 0x80; - } else { - // TODO Implement control codes 1b - 1f - } - } else if (event.key.keysym.mod & KMOD_GUI) { - switch (event.key.keysym.sym) { - case SDLK_ESCAPE: - fprintf(stderr, "[SDL] Reset\n"); - cpu_reset(cpu); - break; - case SDLK_RETURN: - if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) { - SDL_SetWindowFullscreen(window, 0); - } else { - SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN); - } - break; - } - } else if (event.key.keysym.mod == KMOD_NONE) { - switch (event.key.keysym.sym) { - case SDLK_RETURN: - a2p->key = 0x0d | 0x80; // CR - break; - case SDLK_TAB: - a2p->key = 0x09 | 0x80; // HT - case SDLK_DELETE: - a2p->key = 0x7f | 0x80; // DEL - break; - case SDLK_LEFT: - a2p->key = 0x08 | 0x80; // BS - break; - case SDLK_RIGHT: - a2p->key = 0x15 | 0x80; // NAK - break; - case SDLK_UP: - a2p->key = 0x0b | 0x80; // VT - break; - case SDLK_DOWN: - a2p->key = 0x0a | 0x80; // LF - break; - case SDLK_ESCAPE: - a2p->key = 0x1b | 0x80; // ESC - break; - } - } - break; - - case SDL_KEYUP: - if (event.key.keysym.mod & KMOD_ALT) { - switch (event.key.keysym.sym) { - case SDLK_1: - a2p->buttons[0] = 0; - break; - case SDLK_2: - a2p->buttons[1] = 0; - break; - case SDLK_3: - a2p->buttons[2] = 0; - break; - case SDLK_4: - a2p->buttons[3] = 0; - break; - } - } - break; - - case SDL_TEXTINPUT: - if (strlen(event.text.text) == 1) { - a2p->key = toupper(event.text.text[0]) | 0x80; - } - break; - } - } - - return true; -} - -static bool sdl_step_cpu(struct cpu_t *cpu, struct a2p_t *a2p, struct scr_t *scr, int cycles) { - int i = 0; - while (true) { - int ret = cpu_step(cpu); - if (ret < 0) { - switch (ret) { - case EWM_CPU_ERR_UNIMPLEMENTED_INSTRUCTION: - fprintf(stderr, "CPU: Exited because of unimplemented instructions 0x%.2x at 0x%.4x\n", - mem_get_byte(cpu, cpu->state.pc), cpu->state.pc); - break; - case EWM_CPU_ERR_STACK_OVERFLOW: - fprintf(stderr, "CPU: Exited because of stack overflow at 0x%.4x\n", cpu->state.pc); - break; - case EWM_CPU_ERR_STACK_UNDERFLOW: - fprintf(stderr, "CPU: Exited because of stack underflow at 0x%.4x\n", cpu->state.pc); - break; - } - cpu_nmi(cpu); - } - - i++; - - cycles -= ret; - if (cycles <= 0) { - break; - } - } - return true; -} - -void sdl_main(struct cpu_t *cpu, struct a2p_t *a2p, struct scr_t *scr) { - SDL_StartTextInput(); - - Uint32 ticks = SDL_GetTicks(); - - while (true) { - if (!sdl_poll_event(cpu, a2p, scr)) { - break; - } - - if ((SDL_GetTicks() - ticks) >= (1000 / EWM_SDL_FPS)) { - if (!sdl_step_cpu(cpu, a2p, scr, 1000000 / EWM_SDL_FPS)) { - break; - } - - if (a2p->screen_dirty) { - ewm_scr_update(scr); - a2p->screen_dirty = false; - SDL_RenderPresent(scr->renderer); - } - - ticks = SDL_GetTicks(); - } - } - - sdl_destroy(); -} diff --git a/tty.c b/tty.c new file mode 100644 index 0000000..5b47356 --- /dev/null +++ b/tty.c @@ -0,0 +1,98 @@ +// The MIT License (MIT) +// +// Copyright (c) 2015 Stefan Arentz - http://github.com/st3fan/ewm +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "chr.h" +#include "tty.h" + +struct ewm_tty_t *ewm_tty_create(SDL_Renderer *renderer) { + struct ewm_tty_t *tty = malloc(sizeof(struct ewm_tty_t)); + memset(tty, 0, sizeof(struct ewm_tty_t)); + tty->renderer = renderer; + tty->chr = ewm_chr_create("roms/3410036.bin", EWM_CHR_ROM_TYPE_2716, renderer); + ewm_tty_reset(tty); + return tty; +} + +void ewm_tty_destroy(struct ewm_tty_t *tty) { + // TODO +} + +static inline void ewm_tty_render_character(struct ewm_tty_t *tty, int row, int column, uint8_t c) { + // TODO Should we learn chr.c about the Apple1 character set instead of mapping it to the Apple ][+ one? + c += 0x80; + if (tty->chr->characters[c] != NULL) { + SDL_Rect dst; + dst.x = column * 21; + dst.y = row * 24; + dst.w = 21; + dst.h = 24; + SDL_RenderCopy(tty->renderer, tty->chr->characters[c], NULL, &dst); + } +} + +static void tty_scroll_up(struct ewm_tty_t *tty) { + memmove(tty->screen_buffer, &tty->screen_buffer[EWM_ONE_TTY_COLUMNS], (EWM_ONE_TTY_ROWS-1) * EWM_ONE_TTY_COLUMNS); + memset(&tty->screen_buffer[(EWM_ONE_TTY_ROWS-1) * EWM_ONE_TTY_COLUMNS], 0, EWM_ONE_TTY_COLUMNS); +} + +void ewm_tty_write(struct ewm_tty_t *tty, uint8_t v) { + if (v == '\r') { + tty->screen_cursor_column = 0; + tty->screen_cursor_row++; + if (tty->screen_cursor_row == EWM_ONE_TTY_ROWS) { + tty->screen_cursor_row = EWM_ONE_TTY_ROWS - 1; // TODO Scroll the screen up + tty_scroll_up(tty); + } + } else { + tty->screen_buffer[(tty->screen_cursor_row * EWM_ONE_TTY_COLUMNS) + tty->screen_cursor_column] = v; + tty->screen_cursor_column++; + if (tty->screen_cursor_column == EWM_ONE_TTY_COLUMNS) { + tty->screen_cursor_column = 0; + tty->screen_cursor_row++; + if (tty->screen_cursor_row == EWM_ONE_TTY_ROWS) { + tty->screen_cursor_row = EWM_ONE_TTY_ROWS - 1; // TODO Scroll the screen up + tty_scroll_up(tty); + } + } + } + tty->screen_dirty = 1; +} + +void ewm_tty_reset(struct ewm_tty_t *tty) { + for (int row = 0; row < EWM_ONE_TTY_ROWS; row++) { + for (int column = 0; column < EWM_ONE_TTY_COLUMNS; column++) { + tty->screen_buffer[(row * EWM_ONE_TTY_COLUMNS) + column] = 0x00; + } + } + tty->screen_cursor_row = 0; + tty->screen_cursor_column = 0; + tty->screen_dirty = true; +} + +void ewm_tty_refresh(struct ewm_tty_t *tty) { + for (int row = 0; row < 24; row++) { + for (int column = 0; column < 40; column++) { + ewm_tty_render_character(tty, row, column, tty->screen_buffer[(row * EWM_ONE_TTY_COLUMNS) + column]); + } + } + ewm_tty_render_character(tty, tty->screen_cursor_row, tty->screen_cursor_column, EWM_ONE_TTY_CURSOR); +} diff --git a/tty.h b/tty.h new file mode 100644 index 0000000..37ddc2d --- /dev/null +++ b/tty.h @@ -0,0 +1,52 @@ +// The MIT License (MIT) +// +// Copyright (c) 2015 Stefan Arentz - http://github.com/st3fan/ewm +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef EWM_TTY_H +#define EWM_TTY_H + +#include +#include + +#include + +#define EWM_ONE_TTY_ROWS 24 +#define EWM_ONE_TTY_COLUMNS 40 +#define EWM_ONE_TTY_CURSOR '@' + +struct ewm_chr_t; + +struct ewm_tty_t { + SDL_Renderer *renderer; + struct ewm_chr_t *chr; + bool screen_dirty; + uint8_t screen_buffer[EWM_ONE_TTY_ROWS * EWM_ONE_TTY_COLUMNS]; + int screen_cursor_row; + int screen_cursor_column; +}; + +struct ewm_tty_t *ewm_tty_create(SDL_Renderer *renderer); +void ewm_tty_destroy(struct ewm_tty_t *tty); +void ewm_tty_write(struct ewm_tty_t *tty, uint8_t v); +void ewm_tty_reset(struct ewm_tty_t *tty); +void ewm_tty_refresh(struct ewm_tty_t *tty); + +#endif // EWM_TTY_H diff --git a/two.c b/two.c new file mode 100644 index 0000000..67432f0 --- /dev/null +++ b/two.c @@ -0,0 +1,572 @@ +// The MIT License (MIT) +// +// Copyright (c) 2015 Stefan Arentz - http://github.com/st3fan/ewm +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + + +#include +#include +#include + +#include + +#include "cpu.h" +#include "mem.h" +#include "dsk.h" +#include "alc.h" +#include "scr.h" +#include "two.h" + + +#define EWM_A2P_SS_KBD 0xc000 +#define EWM_A2P_SS_KBDSTRB 0xc010 +#define EWM_A2P_SS_SPKR 0xc030 + +#define EWM_A2P_SS_SCREEN_MODE_GRAPHICS 0xc050 +#define EWM_A2P_SS_SCREEN_MODE_TEXT 0xc051 +#define EWM_A2P_SS_GRAPHICS_STYLE_FULL 0xc052 +#define EWM_A2P_SS_GRAPHICS_STYLE_MIXED 0xc053 +#define EWM_A2P_SS_SCREEN_PAGE1 0xc054 +#define EWM_A2P_SS_SCREEN_PAGE2 0xc055 +#define EWM_A2P_SS_GRAPHICS_MODE_LGR 0xc056 +#define EWM_A2P_SS_GRAPHICS_MODE_HGR 0xc057 + +#define EWM_A2P_SS_SETAN0 0xc058 +#define EWM_A2P_SS_CLRAN0 0xc059 +#define EWM_A2P_SS_SETAN1 0xc05a +#define EWM_A2P_SS_CLRAN1 0xc05b +#define EWM_A2P_SS_SETAN2 0xc05c +#define EWM_A2P_SS_CLRAN2 0xc05d +#define EWM_A2P_SS_SETAN3 0xc05e +#define EWM_A2P_SS_CLRAN3 0xc05f + +#define EWM_A2P_SS_PB0 0xC061 +#define EWM_A2P_SS_PB1 0xC062 +#define EWM_A2P_SS_PB2 0xC063 +#define EWM_A2P_SS_PB3 0xC060 // TODO On the gs only? + +static uint8_t ewm_two_iom_read(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr) { + struct ewm_two_t *two = (struct ewm_two_t*) mem->obj; + switch (addr) { + case EWM_A2P_SS_KBD: + return two->key; + case EWM_A2P_SS_KBDSTRB: + two->key &= 0x7f; + return 0x00; + + case EWM_A2P_SS_SCREEN_MODE_GRAPHICS: + two->screen_mode = EWM_A2P_SCREEN_MODE_GRAPHICS; + two->screen_dirty = true; + break; + case EWM_A2P_SS_SCREEN_MODE_TEXT: + two->screen_mode = EWM_A2P_SCREEN_MODE_TEXT; + two->screen_dirty = true; + break; + + case EWM_A2P_SS_GRAPHICS_MODE_LGR: + two->screen_graphics_mode = EWM_A2P_SCREEN_GRAPHICS_MODE_LGR; + two->screen_dirty = true; + break; + case EWM_A2P_SS_GRAPHICS_MODE_HGR: + two->screen_graphics_mode = EWM_A2P_SCREEN_GRAPHICS_MODE_HGR; + two->screen_dirty = true; + break; + + case EWM_A2P_SS_GRAPHICS_STYLE_FULL: + two->screen_graphics_style = EWM_A2P_SCREEN_GRAPHICS_STYLE_FULL; + two->screen_dirty = true; + break; + case EWM_A2P_SS_GRAPHICS_STYLE_MIXED: + two->screen_graphics_style = EWM_A2P_SCREEN_GRAPHICS_STYLE_MIXED; + two->screen_dirty = true; + break; + + case EWM_A2P_SS_SCREEN_PAGE1: + two->screen_page = EWM_A2P_SCREEN_PAGE1; + two->screen_dirty = true; + break; + case EWM_A2P_SS_SCREEN_PAGE2: + two->screen_page = EWM_A2P_SCREEN_PAGE2; + two->screen_dirty = true; + break; + + case EWM_A2P_SS_SPKR: + // TODO Implement speaker support + break; + + case EWM_A2P_SS_PB0: + return two->buttons[0]; + case EWM_A2P_SS_PB1: + return two->buttons[1]; + case EWM_A2P_SS_PB2: + return two->buttons[2]; + case EWM_A2P_SS_PB3: + return two->buttons[3]; + + case EWM_A2P_SS_SETAN0: + break; + case EWM_A2P_SS_SETAN1: + break; + case EWM_A2P_SS_SETAN2: + break; + case EWM_A2P_SS_SETAN3: + break; + + case EWM_A2P_SS_CLRAN0: + break; + case EWM_A2P_SS_CLRAN1: + break; + case EWM_A2P_SS_CLRAN2: + break; + case EWM_A2P_SS_CLRAN3: + break; + + default: + printf("[A2P] Unexpected read at $%.4X\n", addr); + break; + } + return 0; +} + +static void ewm_two_iom_write(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr, uint8_t b) { + struct ewm_two_t *two = (struct ewm_two_t*) mem->obj; + switch (addr) { + + case EWM_A2P_SS_KBDSTRB: + two->key &= 0x7f; + break; + + case EWM_A2P_SS_SPKR: + // TODO Implement speaker support + break; + + case EWM_A2P_SS_SCREEN_MODE_GRAPHICS: + two->screen_mode = EWM_A2P_SCREEN_MODE_GRAPHICS; + two->screen_dirty = true; + break; + case EWM_A2P_SS_SCREEN_MODE_TEXT: + two->screen_mode = EWM_A2P_SCREEN_MODE_TEXT; + two->screen_dirty = true; + break; + + case EWM_A2P_SS_GRAPHICS_MODE_LGR: + two->screen_graphics_mode = EWM_A2P_SCREEN_GRAPHICS_MODE_LGR; + two->screen_dirty = true; + break; + case EWM_A2P_SS_GRAPHICS_MODE_HGR: + two->screen_graphics_mode = EWM_A2P_SCREEN_GRAPHICS_MODE_HGR; + two->screen_dirty = true; + break; + + case EWM_A2P_SS_GRAPHICS_STYLE_FULL: + two->screen_graphics_style = EWM_A2P_SCREEN_GRAPHICS_STYLE_FULL; + two->screen_dirty = true; + break; + case EWM_A2P_SS_GRAPHICS_STYLE_MIXED: + two->screen_graphics_style = EWM_A2P_SCREEN_GRAPHICS_STYLE_MIXED; + two->screen_dirty = true; + break; + + case EWM_A2P_SS_SCREEN_PAGE1: + two->screen_page = EWM_A2P_SCREEN_PAGE1; + two->screen_dirty = true; + break; + case EWM_A2P_SS_SCREEN_PAGE2: + two->screen_page = EWM_A2P_SCREEN_PAGE2; + two->screen_dirty = true; + break; + + case EWM_A2P_SS_SETAN0: + break; + case EWM_A2P_SS_SETAN1: + break; + case EWM_A2P_SS_SETAN2: + break; + case EWM_A2P_SS_SETAN3: + break; + + case EWM_A2P_SS_CLRAN0: + break; + case EWM_A2P_SS_CLRAN1: + break; + case EWM_A2P_SS_CLRAN2: + break; + case EWM_A2P_SS_CLRAN3: + break; + + default: + printf("[A2P] Unexpected write at $%.4X\n", addr); + break; + } +} + +static uint8_t ewm_two_screen_txt_read(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr) { + struct ewm_two_t *two = (struct ewm_two_t*) mem->obj; + return two->screen_txt_data[addr - mem->start]; +} + +static void ewm_two_screen_txt_write(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr, uint8_t b) { + struct ewm_two_t *two = (struct ewm_two_t*) mem->obj; + two->screen_txt_data[addr - mem->start] = b; + two->screen_dirty = true; +} + +static uint8_t ewm_two_screen_hgr_read(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr) { + struct ewm_two_t *two = (struct ewm_two_t*) mem->obj; + return two->screen_hgr_data[addr - mem->start]; +} + +static void ewm_two_screen_hgr_write(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr, uint8_t b) { + struct ewm_two_t *two = (struct ewm_two_t*) mem->obj; + two->screen_hgr_data[addr - mem->start] = b; + two->screen_dirty = true; +} + +static int ewm_two_init(struct ewm_two_t *two, int type, SDL_Renderer *renderer) { + memset(two, 0, sizeof(struct ewm_two_t)); + + two->type = type; + + switch (type) { + case EWM_TWO_TYPE_APPLE2: { + return -1; // TODO + break; + } + + case EWM_TWO_TYPE_APPLE2PLUS: { + two->cpu = cpu_create(EWM_CPU_MODEL_6502); + + two->ram = cpu_add_ram(two->cpu, 0x0000, 48 * 1024); + two->roms[0] = cpu_add_rom_file(two->cpu, 0xd000, "roms/341-0011.bin"); // AppleSoft BASIC D000 + two->roms[1] = cpu_add_rom_file(two->cpu, 0xd800, "roms/341-0012.bin"); // AppleSoft BASIC D800 + two->roms[2] = cpu_add_rom_file(two->cpu, 0xe000, "roms/341-0013.bin"); // AppleSoft BASIC E000 + two->roms[3] = cpu_add_rom_file(two->cpu, 0xe800, "roms/341-0014.bin"); // AppleSoft BASIC E800 + two->roms[4] = cpu_add_rom_file(two->cpu, 0xf000, "roms/341-0015.bin"); // AppleSoft BASIC E800 + two->roms[5] = cpu_add_rom_file(two->cpu, 0xf800, "roms/341-0020.bin"); // AppleSoft BASIC Autostart Monitor F8000 + two->iom = cpu_add_iom(two->cpu, 0xc000, 0xc07f, two, ewm_two_iom_read, ewm_two_iom_write); + + two->dsk = ewm_dsk_create(two->cpu); + if (two->dsk == NULL) { + fprintf(stderr, "[TWO] Could not create Apple Disk Controller\n"); + return -1; + } + + two->alc = ewm_alc_create(two->cpu); + if (two->alc == NULL) { + fprintf(stderr, "[TWO] Could not create Apple Language Card\n"); + return -1; + } + + two->scr = ewm_scr_create(two, renderer); + if (two->scr == NULL) { + fprintf(stderr, "[TWO] Could not create Screen\n"); + return -1; + } + + break; + } + + case EWM_TWO_TYPE_APPLE2E: { + return -1; // TODO + break; + } + } + + // TODO Introduce ewm_scr_t that captures everything related to the apple 2 screen so that it can be re-used? + + two->screen_txt_data = malloc(2 * 1024); + two->screen_txt_iom = cpu_add_iom(two->cpu, 0x0400, 0x0bff, two, ewm_two_screen_txt_read, ewm_two_screen_txt_write); + + two->screen_hgr_data = malloc(16 * 1024); + two->screen_hgr_iom = cpu_add_iom(two->cpu, 0x2000, 0x5fff, two, ewm_two_screen_hgr_read, ewm_two_screen_hgr_write); + + return 0; +} + +struct ewm_two_t *ewm_two_create(int type, SDL_Renderer *renderer) { + struct ewm_two_t *two = malloc(sizeof(struct ewm_two_t)); + if (ewm_two_init(two, type, renderer) != 0) { + free(two); + two = NULL; + } + return two; +} + +void ewm_two_destroy(struct ewm_two_t *two) { + // TODO +} + +int ewm_two_load_disk(struct ewm_two_t *two, int drive, char *path) { + return ewm_dsk_set_disk_file(two->dsk, drive, false, path); +} + + + +static bool ewm_two_poll_event(struct ewm_two_t *two, SDL_Window *window) { // TODO Should window be part of ewm_two_t? + SDL_Event event; + while (SDL_PollEvent(&event) != 0) { + switch (event.type) { + case SDL_QUIT: + return false; + + case SDL_JOYBUTTONDOWN: + if (event.jbutton.button < EWM_A2P_BUTTON_COUNT) { + two->buttons[event.jbutton.button] = 1; + } + break; + case SDL_JOYBUTTONUP: + if (event.jbutton.button < EWM_A2P_BUTTON_COUNT) { + two->buttons[event.jbutton.button] = 0; + } + break; + + case SDL_KEYDOWN: + if (event.key.keysym.mod & KMOD_ALT) { + switch (event.key.keysym.sym) { + case SDLK_1: + two->buttons[0] = 1; + break; + case SDLK_2: + two->buttons[1] = 1; + break; + case SDLK_3: + two->buttons[2] = 1; + break; + case SDLK_4: + two->buttons[3] = 1; + break; + } + } else if (event.key.keysym.mod & KMOD_CTRL) { + if (event.key.keysym.sym >= SDLK_a && event.key.keysym.sym <= SDLK_z) { + two->key = (event.key.keysym.sym - SDLK_a) | 0x80; + } else { + // TODO Implement control codes 1b - 1f + } + } else if (event.key.keysym.mod & KMOD_GUI) { + switch (event.key.keysym.sym) { + case SDLK_ESCAPE: + fprintf(stderr, "[SDL] Reset\n"); + cpu_reset(two->cpu); + break; + case SDLK_RETURN: + if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) { + SDL_SetWindowFullscreen(window, 0); + } else { + SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN); + } + break; + } + } else if (event.key.keysym.mod == KMOD_NONE) { + switch (event.key.keysym.sym) { + case SDLK_RETURN: + two->key = 0x0d | 0x80; // CR + break; + case SDLK_TAB: + two->key = 0x09 | 0x80; // HT + case SDLK_DELETE: + two->key = 0x7f | 0x80; // DEL + break; + case SDLK_LEFT: + two->key = 0x08 | 0x80; // BS + break; + case SDLK_RIGHT: + two->key = 0x15 | 0x80; // NAK + break; + case SDLK_UP: + two->key = 0x0b | 0x80; // VT + break; + case SDLK_DOWN: + two->key = 0x0a | 0x80; // LF + break; + case SDLK_ESCAPE: + two->key = 0x1b | 0x80; // ESC + break; + } + } + break; + + case SDL_KEYUP: + if (event.key.keysym.mod & KMOD_ALT) { + switch (event.key.keysym.sym) { + case SDLK_1: + two->buttons[0] = 0; + break; + case SDLK_2: + two->buttons[1] = 0; + break; + case SDLK_3: + two->buttons[2] = 0; + break; + case SDLK_4: + two->buttons[3] = 0; + break; + } + } + break; + + case SDL_TEXTINPUT: + if (strlen(event.text.text) == 1) { + two->key = toupper(event.text.text[0]) | 0x80; + } + break; + } + } + + return true; +} + +static bool ewm_two_step_cpu(struct ewm_two_t *two, int cycles) { + while (true) { + int ret = cpu_step(two->cpu); + if (ret < 0) { + switch (ret) { + case EWM_CPU_ERR_UNIMPLEMENTED_INSTRUCTION: + fprintf(stderr, "CPU: Exited because of unimplemented instructions 0x%.2x at 0x%.4x\n", + mem_get_byte(two->cpu, two->cpu->state.pc), two->cpu->state.pc); + break; + case EWM_CPU_ERR_STACK_OVERFLOW: + fprintf(stderr, "CPU: Exited because of stack overflow at 0x%.4x\n", two->cpu->state.pc); + break; + case EWM_CPU_ERR_STACK_UNDERFLOW: + fprintf(stderr, "CPU: Exited because of stack underflow at 0x%.4x\n", two->cpu->state.pc); + break; + } + cpu_nmi(two->cpu); + } + cycles -= ret; + if (cycles <= 0) { + break; + } + } + return true; +} + +#define EWM_TWO_OPT_DRIVE1 0 +#define EWM_TWO_OPT_DRIVE2 1 +#define EWM_TWO_OPT_COLOR 2 + +static struct option one_options[] = { + { "drive1", required_argument, NULL, EWM_TWO_OPT_DRIVE1 }, + { "drive2", required_argument, NULL, EWM_TWO_OPT_DRIVE2 }, + { "color", no_argument, NULL, EWM_TWO_OPT_COLOR }, + { NULL, 0, NULL, 0 } +}; + +int ewm_two_main(int argc, char **argv) { + // Parse options + + char *drive1 = NULL; + char *drive2 = NULL; + bool color = false; + + char ch; + while ((ch = getopt_long_only(argc, argv, "", one_options, NULL)) != -1) { + switch (ch) { + case EWM_TWO_OPT_DRIVE1: + drive1 = optarg; + break; + case EWM_TWO_OPT_DRIVE2: + drive2 = optarg; + break; + case EWM_TWO_OPT_COLOR: + color = true; + break; + } + } + + // Initialize SDL + + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_EVENTS) < 0) { + fprintf(stderr, "Failed to initialize SDL: %s\n", SDL_GetError()); + exit(1); + } + + SDL_Window *window = SDL_CreateWindow("EWM v0.1 / Apple ][+", 400, 60, 280*3, 192*3, SDL_WINDOW_SHOWN); + if (window == NULL) { + fprintf(stderr, "Failed create window: %s\n", SDL_GetError()); + exit(1); + } + + SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); + if (renderer == NULL) { + fprintf(stderr, "Failed to create renderer: %s\n", SDL_GetError()); + exit(1); + } + + SDL_RenderSetLogicalSize(renderer, 280*3, 192*3); + + // Create and configure the Apple II + + struct ewm_two_t *two = ewm_two_create(EWM_TWO_TYPE_APPLE2PLUS, renderer); + + if (color) { + ewm_scr_color_scheme(two->scr, EWM_SCR_COLOR_SCHEME_COLOR); + } + + if (drive1 != NULL) { + if (ewm_two_load_disk(two, EWM_DSK_DRIVE1, drive1) != 0) { + fprintf(stderr, "[A2P] Cannot load Drive 1 with %s\n", drive1); + exit(1); + } + } + + if (drive2 != NULL) { + if (ewm_two_load_disk(two, EWM_DSK_DRIVE2, drive2) != 0) { + fprintf(stderr, "[A2P] Cannot load Drive 2 with %s\n", drive2); + exit(1); + } + } + + cpu_reset(two->cpu); + + // + + SDL_StartTextInput(); + + Uint32 ticks = SDL_GetTicks(); + + while (true) { + if (!ewm_two_poll_event(two, window)) { + break; + } + + if ((SDL_GetTicks() - ticks) >= (1000 / 50)) { + if (!ewm_two_step_cpu(two, 1000000 / 50)) { + break; + } + + if (two->screen_dirty) { + ewm_scr_update(two->scr); + two->screen_dirty = false; + SDL_RenderPresent(two->scr->renderer); + } + + ticks = SDL_GetTicks(); + } + } + + // + + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); + + return 0; +} diff --git a/a2p.h b/two.h similarity index 77% rename from a2p.h rename to two.h index 00a2c30..a07ec5d 100644 --- a/a2p.h +++ b/two.h @@ -20,13 +20,16 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#ifndef EWM_A2P_H -#define EWM_A2P_H +#ifndef EWM_TWO_H +#define EWM_TWO_H #include -struct mem_t; -struct ewm_dsk_t; +#include + +#define EWM_TWO_TYPE_APPLE2 0 +#define EWM_TWO_TYPE_APPLE2PLUS 1 +#define EWM_TWO_TYPE_APPLE2E 2 #define EWM_A2P_SCREEN_MODE_TEXT 0 #define EWM_A2P_SCREEN_MODE_GRAPHICS 1 @@ -43,17 +46,25 @@ struct ewm_dsk_t; #define EWM_A2P_BUTTON1 0 #define EWM_A2P_BUTTON2 1 #define EWM_A2P_BUTTON3 2 -#define EWM_A2P_BUTTON4 3 // Only exists on the gs? +#define EWM_A2P_BUTTON4 3 // Actually ony exists on the gs? #define EWM_A2P_BUTTON_COUNT 4 -struct a2p_t { +struct mem_t; +struct ewm_dsk_t; +struct scr; + +struct ewm_two_t { + int type; struct cpu_t *cpu; + struct scr_t *scr; + struct ewm_dsk_t *dsk; + struct ewm_alc_t *alc; struct mem_t *ram; struct mem_t *roms[6]; struct mem_t *iom; - struct ewm_dsk_t *dsk; + // TODO Should all this move into scr_t uint8_t *screen_txt_data; struct mem_t *screen_txt_iom; @@ -71,10 +82,12 @@ struct a2p_t { uint8_t buttons[EWM_A2P_BUTTON_COUNT]; }; -int a2p_init(struct a2p_t *a2p, struct cpu_t *cpu); -struct a2p_t *a2p_create(struct cpu_t *cpu); -void a2p_destroy(struct a2p_t *a2p); +struct ewm_two_t *ewm_two_create(int type, SDL_Renderer *renderer); +void ewm_two_destroy(struct ewm_two_t *two); -int a2p_load_disk(struct a2p_t *a2p, int drive, char *path); +int ewm_two_load_disk(struct ewm_two_t *two, int drive, char *path); + +int ewm_two_main(int argc, char **argv); + +#endif // EWM_TWO_H -#endif