Fixes #13 Implement a terminal for the Apple/Replica 1

This commit is contained in:
Stefan Arentz 2016-12-17 17:22:40 -05:00
parent 095ca9d191
commit ab882161fa
16 changed files with 1223 additions and 982 deletions

View File

@ -21,13 +21,13 @@
# SOFTWARE. # SOFTWARE.
CC=cc 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 LDFLAGS=-g -L/usr/local/lib
EWM_EXECUTABLE=ewm 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_OBJECTS=$(EWM_SOURCES:.c=.o)
EWM_LIBS=-lcurses -lSDL2 EWM_LIBS=-lSDL2
CPU_TEST_EXECUTABLE=cpu_test CPU_TEST_EXECUTABLE=cpu_test
CPU_TEST_SOURCES=cpu.c ins.c mem.c fmt.c cpu_test.c 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= CPU_TEST_LIBS=
SCR_TEST_EXECUTABLE=scr_test 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_OBJECTS=$(SCR_TEST_SOURCES:.c=.o)
SCR_TEST_LIBS=-lSDL2 SCR_TEST_LIBS=-lSDL2

290
a2p.c
View File

@ -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 <stdlib.h>
#include <string.h>
#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);
}

6
chr.c
View File

@ -96,7 +96,11 @@ static SDL_Texture *_generate_texture(SDL_Renderer *renderer, uint8_t rom_data[2
} }
} }
texture = SDL_CreateTextureFromSurface(renderer, surface); 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; return texture;

282
ewm.c
View File

@ -20,283 +20,15 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
#include <stdio.h> #include "one.h"
#include <stdlib.h> #include "two.h"
#include <stdint.h>
#include <stdbool.h>
#include <getopt.h>
#include <string.h>
#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;
}
int main(int argc, char **argv) { int main(int argc, char **argv) {
struct ewm_machine_t *machine = &machines[0]; if (strcmp(argv[1], "one") == 0) {
bool strict = false; return ewm_one_main(argc-1, &argv[1]);
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) { if (strcmp(argv[1], "two") == 0) {
memory = m; return ewm_two_main(argc-1, &argv[1]);
} else {
memory->next = m;
} }
break; return 1;
}
case 'a':
drive1 = optarg;
break;
case 'b':
drive2 = optarg;
break;
}
}
argc -= optind;
argv += optind;
if (machine == NULL) {
fprintf(stderr, "Usage: ewm --machine apple1|replica1|apple2plus\n");
exit(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;
} }

247
one.c Normal file
View File

@ -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 <ctype.h>
#include <stdlib.h>
#include <SDL2/SDL.h>
#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;
}

View File

@ -20,21 +20,29 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
#ifndef EWM_SDL_H #ifndef EWM_ONE_H
#define EWM_SDL_H #define EWM_ONE_H
#define EWM_ONE_TYPE_APPLE1 0
#define EWM_ONE_TYPE_REPLICA1 1
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#define EWM_SDL_FPS (50)
extern SDL_Window *window;
extern SDL_Renderer *renderer;
struct cpu_t; struct cpu_t;
struct a2p_t; struct ewm_tty_t;
struct scr_t; struct ewm_pia_t;
void sdl_initialize(); struct ewm_one_t {
void sdl_main(struct cpu_t *cpu, struct a2p_t *a2p, struct scr_t *scr); 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

178
pia.c
View File

@ -24,109 +24,119 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <curses.h> #include <string.h>
#include "cpu.h" #include "cpu.h"
#include "pia.h" #include "pia.h"
// This implements a 6820 Peripheral I/O Adapter. On the Apple I this // This implements a 6820 Peripheral I/O Adapter. On the Apple I this
// is what connects the keyboard and display logic to the CPU. // is what connects the keyboard and display logic to the CPU. The
// implementation is not complete but does enough to support how the
static void pia_dsp_write(uint8_t b) { // keyboard and display are hooked up.
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;
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) { switch (addr) {
case EWM_A1_PIA6820_KBD: { case EWM_A1_PIA6820_KBD_DDR:
result = pia->a; 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_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; break;
} }
return 0;
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);
} }
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) { switch (addr) {
case EWM_A1_PIA6820_KBD: { /* KBD */ 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; 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;
}
} }
case EWM_A1_PIA6820_KBDCR: { /* KBDCR */ struct ewm_pia_t *ewm_pia_create(struct cpu_t *cpu) {
pia->cra = b; struct ewm_pia_t *pia = (struct ewm_pia_t*) malloc(sizeof(struct ewm_pia_t));
break; if (ewm_pia_init(pia, cpu) != 0) {
free(pia);
pia = NULL;
}
return pia;
} }
case EWM_A1_PIA6820_DSP: { /* DSP */ int ewm_pia_init(struct ewm_pia_t *pia, struct cpu_t *cpu) {
if (pia->crb != 0x00) { /* TODO: Check the actual flag */ memset(pia, 0, sizeof(struct ewm_pia_t));
pia_dsp_write(b); cpu_add_iom(cpu, EWM_A1_PIA6820_ADDR, EWM_A1_PIA6820_ADDR + EWM_A1_PIA6820_LENGTH - 1, pia, pia_read, pia_write);
} return 0;
break;
} }
case EWM_A1_PIA6820_DSPCR: { /* DSPCR */ void ewm_pia_destroy(struct ewm_pia_t *pia) {
pia->crb = b; free(pia);
break;
} }
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
} }

44
pia.h
View File

@ -34,22 +34,38 @@
#define EWM_A1_PIA6820_ADDR 0xd010 #define EWM_A1_PIA6820_ADDR 0xd010
#define EWM_A1_PIA6820_LENGTH 0x0100 #define EWM_A1_PIA6820_LENGTH 0x0100
#define EWM_A1_PIA6820_KBD (EWM_A1_PIA6820_ADDR + EWM_PIA6820_DDRA) #define EWM_A1_PIA6820_KBD_DDR (EWM_A1_PIA6820_ADDR + EWM_PIA6820_DDRA)
#define EWM_A1_PIA6820_KBDCR (EWM_A1_PIA6820_ADDR + EWM_PIA6820_CTLA) #define EWM_A1_PIA6820_KBD_CTL (EWM_A1_PIA6820_ADDR + EWM_PIA6820_CTLA)
#define EWM_A1_PIA6820_DSP (EWM_A1_PIA6820_ADDR + EWM_PIA6820_DDRB) #define EWM_A1_PIA6820_DSP_DDR (EWM_A1_PIA6820_ADDR + EWM_PIA6820_DDRB)
#define EWM_A1_PIA6820_DSPCR (EWM_A1_PIA6820_ADDR + EWM_PIA6820_CTLB) #define EWM_A1_PIA6820_DSP_CTL (EWM_A1_PIA6820_ADDR + EWM_PIA6820_CTLB)
struct pia_t { struct ewm_pia_t;
uint8_t a;
uint8_t cra; typedef void (*ewm_pia_callback_t)(struct ewm_pia_t *pia, void *obj, uint8_t ddr, uint8_t v);
uint8_t b;
uint8_t crb; struct ewm_pia_t {
uint8_t trace; 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); struct ewm_pia_t *ewm_pia_create(struct cpu_t *cpu);
void pia_trace(struct pia_t *pia, uint8_t enable); int ewm_pia_init(struct ewm_pia_t *pia, struct cpu_t *cpu);
uint8_t pia_read(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr); void ewm_pia_destroy(struct ewm_pia_t *pia);
void pia_write(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr, uint8_t b);
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 #endif

34
scr.c
View File

@ -27,7 +27,7 @@
#include "mem.h" #include "mem.h"
#include "cpu.h" #include "cpu.h"
#include "a2p.h" #include "two.h"
#include "chr.h" #include "chr.h"
#include "scr.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) { 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; uint16_t base = (scr->two->screen_page == EWM_A2P_SCREEN_PAGE1) ? 0x0400 : 0x0800;
uint8_t c = scr->a2p->screen_txt_data[((txt_line_offsets[row] + base) + column) - 0x0400]; uint8_t c = scr->two->screen_txt_data[((txt_line_offsets[row] + base) + column) - 0x0400];
if (scr->chr->characters[c] != NULL) { if (scr->chr->characters[c] != NULL) {
SDL_Rect dst; SDL_Rect dst;
dst.x = column * 21; 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) { 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; uint16_t base = (scr->two->screen_page == EWM_A2P_SCREEN_PAGE1) ? 0x0400 : 0x0800;
uint8_t block = scr->a2p->screen_txt_data[((txt_line_offsets[row] + base) + column) - 0x0400]; uint8_t block = scr->two->screen_txt_data[((txt_line_offsets[row] + base) + column) - 0x0400];
if (block != 0) { if (block != 0) {
SDL_Rect dst; SDL_Rect dst;
dst.x = column * 21; 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) { 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 // Render graphics
int rows = mixed ? 20 : 24; 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) { static void inline scr_render_hgr_line_green(struct scr_t *scr, int line, uint16_t line_base) {
int x = 0; int x = 0;
for (int i = 0; i < 40; i++) { 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++) { for (int j = 0; j < 7; j++) {
SDL_Rect dst; SDL_Rect dst;
dst.x = x * 3; 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; int pixels[280], x = 0;
for (int i = 0; i < 40; i++) { 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++) { for (int j = 0; j < 7; j++) {
if (c & (1 << j)) { if (c & (1 << j)) {
if (x % 2 == 0) { 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) { static void inline scr_render_hgr_screen(struct scr_t *scr) {
// Render graphics // Render graphics
int lines = (scr->a2p->screen_graphics_style == EWM_A2P_SCREEN_GRAPHICS_STYLE_MIXED) ? 160 : 192; int lines = (scr->two->screen_graphics_style == EWM_A2P_SCREEN_GRAPHICS_STYLE_MIXED) ? 160 : 192;
uint16_t hgr_base = hgr_page_offsets[scr->a2p->screen_page]; uint16_t hgr_base = hgr_page_offsets[scr->two->screen_page];
for (int line = 0; line < lines; line++) { for (int line = 0; line < lines; line++) {
uint16_t line_base = hgr_base + hgr_line_offsets[line]; uint16_t line_base = hgr_base + hgr_line_offsets[line];
if (scr->color_scheme == EWM_SCR_COLOR_SCHEME_COLOR) { 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 // 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 row = 20; row < 24; row++) {
for (int column = 0; column < 40; column++) { for (int column = 0; column < 40; column++) {
scr_render_character(scr, row, 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 // 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)); memset(scr, 0x00, sizeof(struct scr_t));
scr->a2p = a2p; scr->two = two;
scr->renderer = renderer; scr->renderer = renderer;
scr->chr = ewm_chr_create("roms/3410036.bin", EWM_CHR_ROM_TYPE_2716, renderer); scr->chr = ewm_chr_create("roms/3410036.bin", EWM_CHR_ROM_TYPE_2716, renderer);
if (scr->chr == NULL) { 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; 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)); 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); free(scr);
scr = NULL; scr = NULL;
} }
@ -297,12 +297,12 @@ void ewm_scr_update(struct scr_t *scr) {
SDL_SetRenderDrawColor(scr->renderer, 0, 0, 0, 255); SDL_SetRenderDrawColor(scr->renderer, 0, 0, 0, 255);
SDL_RenderClear(scr->renderer); SDL_RenderClear(scr->renderer);
switch (scr->a2p->screen_mode) { switch (scr->two->screen_mode) {
case EWM_A2P_SCREEN_MODE_TEXT: case EWM_A2P_SCREEN_MODE_TEXT:
scr_render_txt_screen(scr); scr_render_txt_screen(scr);
break; break;
case EWM_A2P_SCREEN_MODE_GRAPHICS: 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: case EWM_A2P_SCREEN_GRAPHICS_MODE_LGR:
scr_render_lgr_screen(scr); scr_render_lgr_screen(scr);
break; break;

9
scr.h
View File

@ -28,22 +28,21 @@
#define EWM_SCR_COLOR_SCHEME_GREEN 0 #define EWM_SCR_COLOR_SCHEME_GREEN 0
#define EWM_SCR_COLOR_SCHEME_COLOR 1 #define EWM_SCR_COLOR_SCHEME_COLOR 1
struct ewm_two_t;
struct ewm_chr_t; struct ewm_chr_t;
struct cpu_t;
struct a2p_t;
// The 'scr' object represents the screen. It renders the contents of // The 'scr' object represents the screen. It renders the contents of
// the machine. It has pluggable renders. // the machine. It has pluggable renders.
struct scr_t { struct scr_t {
struct a2p_t *a2p; struct ewm_two_t *two;
SDL_Renderer *renderer; SDL_Renderer *renderer;
struct ewm_chr_t *chr; struct ewm_chr_t *chr;
int color_scheme; int color_scheme;
}; };
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);
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);
void ewm_scr_destroy(struct scr_t *scr); void ewm_scr_destroy(struct scr_t *scr);
void ewm_scr_update(struct scr_t *scr); void ewm_scr_update(struct scr_t *scr);
void ewm_scr_color_scheme(struct scr_t *scr, int color_scheme); void ewm_scr_color_scheme(struct scr_t *scr, int color_scheme);

View File

@ -25,21 +25,20 @@
#include "cpu.h" #include "cpu.h"
#include "mem.h" #include "mem.h"
#include "a2p.h" #include "two.h"
#include "scr.h" #include "scr.h"
#include "sdl.h"
typedef void (*test_setup_t)(struct scr_t *scr); typedef void (*test_setup_t)(struct scr_t *scr);
typedef void (*test_run_t)(struct scr_t *scr); typedef void (*test_run_t)(struct scr_t *scr);
void txt_full_refresh_setup(struct scr_t *scr) { void txt_full_refresh_setup(struct scr_t *scr) {
scr->a2p->screen_mode = EWM_A2P_SCREEN_MODE_TEXT; scr->two->screen_mode = EWM_A2P_SCREEN_MODE_TEXT;
scr->a2p->screen_page = EWM_A2P_SCREEN_PAGE1; scr->two->screen_page = EWM_A2P_SCREEN_PAGE1;
for (uint16_t a = 0x0400; a <= 0x0bff; a++) { for (uint16_t a = 0x0400; a <= 0x0bff; a++) {
uint8_t v = 0xa0 + (random() % 64); 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) { void lgr_full_refresh_setup(struct scr_t *scr) {
scr->a2p->screen_mode = EWM_A2P_SCREEN_MODE_GRAPHICS; scr->two->screen_mode = EWM_A2P_SCREEN_MODE_GRAPHICS;
scr->a2p->screen_page = EWM_A2P_SCREEN_PAGE1; scr->two->screen_page = EWM_A2P_SCREEN_PAGE1;
scr->a2p->screen_graphics_mode = EWM_A2P_SCREEN_GRAPHICS_MODE_LGR; scr->two->screen_graphics_mode = EWM_A2P_SCREEN_GRAPHICS_MODE_LGR;
scr->a2p->screen_graphics_style = EWM_A2P_SCREEN_GRAPHICS_STYLE_FULL; scr->two->screen_graphics_style = EWM_A2P_SCREEN_GRAPHICS_STYLE_FULL;
for (uint16_t a = 0x0400; a <= 0x0bff; a++) { for (uint16_t a = 0x0400; a <= 0x0bff; a++) {
uint8_t v = ((random() % 16) << 4) | (random() % 16); 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) { void hgr_full_refresh_setup(struct scr_t *scr) {
scr->a2p->screen_mode = EWM_A2P_SCREEN_MODE_GRAPHICS; scr->two->screen_mode = EWM_A2P_SCREEN_MODE_GRAPHICS;
scr->a2p->screen_page = EWM_A2P_SCREEN_PAGE1; scr->two->screen_page = EWM_A2P_SCREEN_PAGE1;
scr->a2p->screen_graphics_mode = EWM_A2P_SCREEN_GRAPHICS_MODE_HGR; scr->two->screen_graphics_mode = EWM_A2P_SCREEN_GRAPHICS_MODE_HGR;
scr->a2p->screen_graphics_style = EWM_A2P_SCREEN_GRAPHICS_STYLE_FULL; scr->two->screen_graphics_style = EWM_A2P_SCREEN_GRAPHICS_STYLE_FULL;
for (uint16_t a = 0x2000; a <= 0x5fff; a++) { 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() { 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 sleep(3); // Is this ok? Seems to be needed to get the window on the screen
// Setup the CPU, Apple ][+ and it's screen. // Setup the CPU, Apple ][+ and it's screen.
struct cpu_t *cpu = cpu_create(EWM_CPU_MODEL_6502); struct ewm_two_t *two = ewm_two_create(EWM_TWO_TYPE_APPLE2PLUS, renderer);
struct a2p_t *a2p = a2p_create(cpu);
struct scr_t *scr = ewm_scr_create(a2p, renderer);
test(scr, "txt_full_refresh", txt_full_refresh_setup, txt_full_refresh_test); test(two->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(two->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, "hgr_full_refresh", hgr_full_refresh_setup, hgr_full_refresh_test);
// Destroy DSL things // Destroy DSL things
SDL_DestroyWindow(window); SDL_DestroyWindow(window);
SDL_DestroyRenderer(renderer);
SDL_Quit(); SDL_Quit();
return 0; return 0;

236
sdl.c
View File

@ -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 <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#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();
}

98
tty.c Normal file
View File

@ -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);
}

52
tty.h Normal file
View File

@ -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 <stdbool.h>
#include <stdint.h>
#include <SDL2/SDL.h>
#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

572
two.c Normal file
View File

@ -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 <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <SDL2/SDL.h>
#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;
}

View File

@ -20,13 +20,16 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
#ifndef EWM_A2P_H #ifndef EWM_TWO_H
#define EWM_A2P_H #define EWM_TWO_H
#include <stdint.h> #include <stdint.h>
struct mem_t; #include <SDL2/SDL.h>
struct ewm_dsk_t;
#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_TEXT 0
#define EWM_A2P_SCREEN_MODE_GRAPHICS 1 #define EWM_A2P_SCREEN_MODE_GRAPHICS 1
@ -43,17 +46,25 @@ struct ewm_dsk_t;
#define EWM_A2P_BUTTON1 0 #define EWM_A2P_BUTTON1 0
#define EWM_A2P_BUTTON2 1 #define EWM_A2P_BUTTON2 1
#define EWM_A2P_BUTTON3 2 #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 #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 cpu_t *cpu;
struct scr_t *scr;
struct ewm_dsk_t *dsk;
struct ewm_alc_t *alc;
struct mem_t *ram; struct mem_t *ram;
struct mem_t *roms[6]; struct mem_t *roms[6];
struct mem_t *iom; struct mem_t *iom;
struct ewm_dsk_t *dsk;
// TODO Should all this move into scr_t
uint8_t *screen_txt_data; uint8_t *screen_txt_data;
struct mem_t *screen_txt_iom; struct mem_t *screen_txt_iom;
@ -71,10 +82,12 @@ struct a2p_t {
uint8_t buttons[EWM_A2P_BUTTON_COUNT]; uint8_t buttons[EWM_A2P_BUTTON_COUNT];
}; };
int a2p_init(struct a2p_t *a2p, struct cpu_t *cpu); struct ewm_two_t *ewm_two_create(int type, SDL_Renderer *renderer);
struct a2p_t *a2p_create(struct cpu_t *cpu); void ewm_two_destroy(struct ewm_two_t *two);
void a2p_destroy(struct a2p_t *a2p);
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