mirror of
https://github.com/st3fan/ewm.git
synced 2024-12-28 00:29:49 +00:00
Fixes #13 Implement a terminal for the Apple/Replica 1
This commit is contained in:
parent
095ca9d191
commit
ab882161fa
8
Makefile
8
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
|
||||
|
||||
|
290
a2p.c
290
a2p.c
@ -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
6
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;
|
||||
|
282
ewm.c
282
ewm.c
@ -20,283 +20,15 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.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;
|
||||
}
|
||||
#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;
|
||||
}
|
||||
|
247
one.c
Normal file
247
one.c
Normal 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;
|
||||
}
|
||||
|
@ -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 <SDL2/SDL.h>
|
||||
|
||||
#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
|
198
pia.c
198
pia.c
@ -24,109 +24,119 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <curses.h>
|
||||
#include <string.h>
|
||||
|
||||
#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
|
||||
}
|
||||
|
44
pia.h
44
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
|
||||
|
34
scr.c
34
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;
|
||||
|
9
scr.h
9
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);
|
||||
|
60
scr_test.c
60
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;
|
||||
|
236
sdl.c
236
sdl.c
@ -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
98
tty.c
Normal 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
52
tty.h
Normal 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
572
two.c
Normal 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;
|
||||
}
|
@ -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 <stdint.h>
|
||||
|
||||
struct mem_t;
|
||||
struct ewm_dsk_t;
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#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
|
Loading…
Reference in New Issue
Block a user