Merge branch 'master' into RenderingPerformance

This commit is contained in:
Stefan Arentz 2017-01-29 22:10:36 -05:00 committed by GitHub
commit 534b16c4f4
25 changed files with 732 additions and 129 deletions

2
.gitignore vendored
View File

@ -53,6 +53,8 @@ dkms.conf
ewm
scr_test
cpu_test
chr_test
mem_bench
*.bin
*.lst

View File

@ -52,16 +52,3 @@ Here are some of the things I want to accomplish for each emulated machine:
* Audio Support
* Display Emulation - High resolution graphics - Mostly works.
### Apple IIe
*64K / 65C02 / Enhanced ROM*
* Central *MMU* to coordinate/own all memory and IO
* Generic slot/card infrastructure
* Disk II emulation - In progress
* Extended 80-Column Text Card
* Display Emulation - 40 Column mode
* Display Emulation - 80 Column mode
* Display Emulation - Low resolution graphics
* Display Emulation - High resolution graphics
* Display Emulation - Double high resolution graphics

View File

@ -25,7 +25,7 @@ CFLAGS=-std=gnu11 -O3 -g -Wall -Wextra -Werror -Wno-unused-parameter
LDFLAGS=-g -L/usr/local/lib
EWM_EXECUTABLE=ewm
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 utl.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 utl.c boo.c
EWM_OBJECTS=$(EWM_SOURCES:.c=.o)
EWM_LIBS=-lSDL2
@ -44,10 +44,20 @@ CHR_TEST_SOURCES=chr.c chr_test.c
CHR_TEST_OBJECTS=$(CHR_TEST_SOURCES:.c=.o)
CHR_TEST_LIBS=-lSDL2
all: $(EWM_EXECUTABLE) $(CPU_TEST_SOURCES) $(CPU_TEST_EXECUTABLE) $(SCR_TEST_EXECUTABLE) $(CHR_TEST)
CPU_BENCH=cpu_bench
CPU_BENCH_SOURCES=cpu.c ins.c mem.c fmt.c cpu_bench.c
CPU_BENCH_OBJECTS=$(CPU_BENCH_SOURCES:.c=.o)
CPU_BENCH_LIBS=
MEM_BENCH=mem_bench
MEM_BENCH_SOURCES=cpu.c ins.c mem.c fmt.c mem_bench.c
MEM_BENCH_OBJECTS=$(MEM_BENCH_SOURCES:.c=.o)
MEM_BENCH_LIBS=
all: $(EWM_EXECUTABLE) $(CPU_TEST_EXECUTABLE) $(SCR_TEST_EXECUTABLE) $(CHR_TEST) $(CPU_BENCH) $(MEM_BENCH)
clean:
rm -f $(EWM_OBJECTS) $(EWM_EXECUTABLE) $(CPU_TEST_OBJECTS) $(CPU_TEST_EXECUTABLE) $(SCR_TEST_OBJECTS) $(SCR_TEST_EXECUTABLE) $(CHR_TEST) $(CHR_TEST_OBJECTS)
rm -f $(EWM_OBJECTS) $(EWM_EXECUTABLE) $(CPU_TEST_OBJECTS) $(CPU_TEST_EXECUTABLE) $(SCR_TEST_OBJECTS) $(SCR_TEST_EXECUTABLE) $(CHR_TEST) $(CHR_TEST_OBJECTS) $(CPU_BENCH_OBJECTS) $(CPU_BENCH) $(MEM_BENCH_OBJECTS) $(MEM_BENCH)
$(EWM_EXECUTABLE): $(EWM_OBJECTS)
$(CC) $(LDFLAGS) $(EWM_OBJECTS) $(EWM_LIBS) -o $@
@ -61,5 +71,11 @@ $(SCR_TEST_EXECUTABLE): $(SCR_TEST_OBJECTS)
$(CHR_TEST): $(CHR_TEST_OBJECTS)
$(CC) $(LDFLAGS) $(CHR_TEST_OBJECTS) $(CHR_TEST_LIBS) -o $@
$(CPU_BENCH): $(CPU_BENCH_OBJECTS)
$(CC) $(LDFLAGS) $(CPU_BENCH_OBJECTS) $(CPU_BENCH_LIBS) -o $@
$(MEM_BENCH): $(MEM_BENCH_OBJECTS)
$(CC) $(LDFLAGS) $(MEM_BENCH_OBJECTS) $(MEM_BENCH_LIBS) -o $@
.c.o:
$(CC) $(CFLAGS) $< -c -o $@

164
src/boo.c Normal file
View File

@ -0,0 +1,164 @@
// 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 <string.h>
#include <SDL2/SDL.h>
#include "tty.h"
#include "boo.h"
static char *menu[24] = {
"****************************************",
"* *",
"* _______ ________ _______ *",
"* ! ___! ! ! ! ! ! *",
"* ! ___! ! ! ! ! *",
"* !_______!________!__!_!__! *",
"* *",
"* GITHUB.COM/ST3FAN/EWM *",
"* *",
"* *",
"* WHAT WOULD YOU LIKE TO EMULATE? *",
"* *",
"* 1) APPLE 1 *",
"* 6502 / 8KB / WOZ MONITOR *",
"* *",
"* 2) REPLICA 1 *",
"* 65C02 / 48KB / KRUSADER *",
"* *",
"* 3) APPLE ][+ *",
"* 6502 / 64KB (LANGUAGE CARD) *",
"* DISK II / AUTOSTART ROM *",
"* *",
"* *",
"****************************************"
};
int ewm_boo_main(int argc, char **argv) {
// 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);
// We only need a tty to display the menu
struct ewm_tty_t *tty = ewm_tty_create(renderer);
// Main loop
uint32_t ticks = SDL_GetTicks();
uint32_t phase = 1;
int result = -1;
while (result == -1) {
// Handle the next event
SDL_Event event;
while (SDL_PollEvent(&event) != 0) {
switch (event.type) {
case SDL_QUIT:
result = EWM_BOO_QUIT;
break;
case SDL_KEYDOWN:
switch (event.key.keysym.sym) {
case SDLK_1:
result = EWM_BOO_BOOT_APPLE1;
break;
case SDLK_2:
result = EWM_BOO_BOOT_REPLICA1;
break;
case SDLK_3:
result = EWM_BOO_BOOT_APPLE2PLUS;
break;
}
break;
}
}
// If we are done, exit
if (result != -1) {
break;
}
// Update the screen
if ((SDL_GetTicks() - ticks) >= (1000 / EWM_BOO_FPS)) {
if (tty->screen_dirty || (phase == 0) || ((phase % (EWM_BOO_FPS / 4)) == 0)) {
SDL_SetRenderDrawColor(tty->renderer, 0, 0, 0, 255);
SDL_RenderClear(tty->renderer);
for (int i = 0; i < 24; i++) {
char *p = (char*) tty->screen_buffer;
p += (i * 40);
memcpy(p, menu[i], 40);
}
tty->screen_cursor_column = 34;
tty->screen_cursor_row = 10;
//strcpy((char*) tty->screen_buffer, "1) APPLE 1 2) REPLICA 1 3) APPLE ][+");
ewm_tty_refresh(tty, phase, EWM_BOO_FPS);
tty->screen_dirty = false;
SDL_RenderPresent(tty->renderer);
}
ticks = SDL_GetTicks();
phase += 1;
if (phase == EWM_BOO_FPS) {
phase = 0;
}
}
}
// Destroy SDL
SDL_DestroyWindow(window);
SDL_DestroyRenderer(renderer);
SDL_Quit();
return result;
}

37
src/boo.h Normal file
View File

@ -0,0 +1,37 @@
// 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_BOO_H
#define EWM_BOO_H
#include <SDL2/SDL.h>
#define EWM_BOO_QUIT (0)
#define EWM_BOO_BOOT_APPLE1 (1)
#define EWM_BOO_BOOT_REPLICA1 (2)
#define EWM_BOO_BOOT_APPLE2PLUS (3)
#define EWM_BOO_FPS (40)
int ewm_boo_main(int argc, char **argv);
#endif // EWM_BOO_H

View File

@ -60,16 +60,6 @@ static int _load_rom_data(char *rom_path, uint8_t rom_data[2048]) {
return 0;
}
struct ewm_chr_t* ewm_chr_create(char *rom_path, int rom_type, SDL_Renderer *renderer, uint32_t color) {
struct ewm_chr_t *chr = (struct ewm_chr_t*) malloc(sizeof(struct ewm_chr_t));
int ret = ewm_chr_init(chr, rom_path, rom_type, renderer, color);
if (ret != 0) {
free(chr);
chr = NULL;
}
return chr;
}
static void _set_pixel(SDL_Surface * surface, int x, int y, Uint32 color) {
uint32_t *pixel = (uint32_t*) ((uint8_t*) surface->pixels + (y * surface->pitch) + (x * sizeof(uint32_t)));
*pixel = color;
@ -124,7 +114,7 @@ static SDL_Texture *_generate_texture(SDL_Renderer *renderer, SDL_Surface *surfa
return texture;
}
int ewm_chr_init(struct ewm_chr_t *chr, char *rom_path, int rom_type, SDL_Renderer *renderer, uint32_t color) {
static int ewm_chr_init(struct ewm_chr_t *chr, char *rom_path, int rom_type, SDL_Renderer *renderer, uint32_t color) {
if (rom_type != EWM_CHR_ROM_TYPE_2716) {
return -1;
}
@ -175,3 +165,12 @@ int ewm_chr_init(struct ewm_chr_t *chr, char *rom_path, int rom_type, SDL_Render
return 0;
}
struct ewm_chr_t* ewm_chr_create(char *rom_path, int rom_type, SDL_Renderer *renderer, uint32_t color) {
struct ewm_chr_t *chr = (struct ewm_chr_t*) malloc(sizeof(struct ewm_chr_t));
int ret = ewm_chr_init(chr, rom_path, rom_type, renderer, color);
if (ret != 0) {
free(chr);
chr = NULL;
}
return chr;
}

View File

@ -33,6 +33,5 @@ struct ewm_chr_t {
};
struct ewm_chr_t* ewm_chr_create(char *rom_path, int rom_type, SDL_Renderer *renderer, uint32_t color);
int ewm_chr_init(struct ewm_chr_t *chr, char *rom_path, int rom_type, SDL_Renderer *renderer, uint32_t color);
#endif

View File

@ -37,31 +37,24 @@
#include "mem.h"
#include "fmt.h"
/* Private API */
typedef void (*cpu_instruction_handler_t)(struct cpu_t *cpu);
typedef void (*cpu_instruction_handler_byte_t)(struct cpu_t *cpu, uint8_t oper);
typedef void (*cpu_instruction_handler_word_t)(struct cpu_t *cpu, uint16_t oper);
// Stack management.
void _cpu_push_byte(struct cpu_t *cpu, uint8_t b) {
mem_set_byte(cpu, 0x0100 + cpu->state.sp, b);
cpu->state.sp -= 1;
cpu->page1[cpu->state.sp--] = b;
}
void _cpu_push_word(struct cpu_t *cpu, uint16_t w) {
_cpu_push_byte(cpu, (uint8_t) (w >> 8));
_cpu_push_byte(cpu, (uint8_t) w);
cpu->page1[cpu->state.sp--] = w >> 8;
cpu->page1[cpu->state.sp--] = w;
}
uint8_t _cpu_pull_byte(struct cpu_t *cpu) {
cpu->state.sp += 1;
return mem_get_byte(cpu, 0x0100 + cpu->state.sp);
return cpu->page1[++cpu->state.sp];
}
uint16_t _cpu_pull_word(struct cpu_t *cpu) {
return (uint16_t) _cpu_pull_byte(cpu) | ((uint16_t) _cpu_pull_byte(cpu) << 8);
uint16_t w = (uint16_t) cpu->page1[++cpu->state.sp];
return w | ((uint16_t) cpu->page1[++cpu->state.sp] << 8);
}
uint8_t _cpu_stack_free(struct cpu_t *cpu) {
@ -189,7 +182,7 @@ static void cpu_initialize() {
}
}
int cpu_init(struct cpu_t *cpu, int model) {
static int cpu_init(struct cpu_t *cpu, int model) {
if (!cpu_initialized) {
cpu_initialize();
cpu_initialized = true;
@ -218,6 +211,17 @@ void cpu_destroy(struct cpu_t *cpu) {
}
}
static struct mem_t *cpu_mem_for_page(struct cpu_t *cpu, uint8_t page) {
struct mem_t *mem = cpu->mem;
while (mem != NULL) {
if (mem->enabled && ((page * 0x100) >= mem->start) && ((page * 0x0100 + 0xff) <= mem->end)) {
return mem;
}
mem = mem->next;
}
return NULL;
}
struct mem_t *cpu_add_mem(struct cpu_t *cpu, struct mem_t *mem) {
if (cpu->mem == NULL) {
cpu->mem = mem;
@ -351,6 +355,27 @@ struct mem_t *cpu_add_iom(struct cpu_t *cpu, uint16_t start, uint16_t end, void
return cpu_add_mem(cpu, mem);
}
// Call this when the memory layout changes for page 0 and 1. For
// example when those pages are bankswitched. I don't think anything
// does that right now. The Apple Language Card works in different
// memory regions.
void cpu_optimize_memory(struct cpu_t *cpu) {
struct mem_t *page0 = cpu_mem_for_page(cpu, 0);
if (page0 == NULL || (page0->flags != (MEM_FLAGS_READ | MEM_FLAGS_WRITE))) {
printf("[CPU] Cannot find rw memory region that handles Page 0\n");
exit(1);
}
cpu->page0 = page0->obj + (0x0000 - page0->start);
struct mem_t *page1 = cpu_mem_for_page(cpu, 1);
if (page1 == NULL || (page1->flags != (MEM_FLAGS_READ | MEM_FLAGS_WRITE))) {
printf("[CPU] Cannot find rw memory region that handles Page 1\n");
exit(1);
}
cpu->page1 = page1->obj + (0x0100 - page1->start);
}
void cpu_strict(struct cpu_t *cpu, bool strict) {
cpu->strict = strict;
}
@ -384,6 +409,8 @@ void cpu_reset(struct cpu_t *cpu) {
cpu->state.z = 0;
cpu->state.c = 0;
cpu->state.sp = 0xff;
cpu_optimize_memory(cpu);
}
int cpu_irq(struct cpu_t *cpu) {

View File

@ -54,8 +54,15 @@ struct cpu_t {
struct mem_t *mem;
struct cpu_instruction_t *instructions;
uint64_t counter;
uint8_t *page0;
uint8_t *page1;
};
typedef void (*cpu_instruction_handler_t)(struct cpu_t *cpu);
typedef void (*cpu_instruction_handler_byte_t)(struct cpu_t *cpu, uint8_t oper);
typedef void (*cpu_instruction_handler_word_t)(struct cpu_t *cpu, uint16_t oper);
#define MEM_FLAGS_READ 0x01
#define MEM_FLAGS_WRITE 0x02
@ -87,7 +94,6 @@ uint8_t _cpu_stack_used(struct cpu_t *cpu);
uint8_t _cpu_get_status(struct cpu_t *cpu);
void _cpu_set_status(struct cpu_t *cpu, uint8_t status);
int cpu_init(struct cpu_t *cpu, int model);
struct cpu_t *cpu_create(int model);
void cpu_destroy(struct cpu_t *cpu);
@ -99,6 +105,8 @@ struct mem_t *cpu_add_rom_data(struct cpu_t *cpu, uint16_t start, uint16_t end,
struct mem_t *cpu_add_rom_file(struct cpu_t *cpu, uint16_t start, char *path);
struct mem_t *cpu_add_iom(struct cpu_t *cpu, uint16_t start, uint16_t end, void *obj, mem_read_handler_t read_handler, mem_write_handler_t write_handler);
void cpu_optimize_memory(struct cpu_t *cpu);
void cpu_strict(struct cpu_t *cpu, bool strict);
int cpu_trace(struct cpu_t *cpu, char *path);

88
src/cpu_bench.c Normal file
View File

@ -0,0 +1,88 @@
// 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 <stdint.h>
#include <time.h>
#include "cpu.h"
#include "ins.h"
#include "mem.h"
#define CPU_BENCH_ITERATIONS (100 * 1000 * 1000)
void test(struct cpu_t *cpu, uint8_t opcode) {
uint64_t runs[3];
struct cpu_instruction_t *ins = &cpu->instructions[opcode];
for (int run = 0; run < 3; run++) {
struct timespec start;
if (clock_gettime(CLOCK_REALTIME, &start) != 0) {
perror("Cannot get time");
exit(1);
}
switch (ins->bytes) {
case 1:
for (uint64_t i = 0; i < CPU_BENCH_ITERATIONS; i++) {
((cpu_instruction_handler_t) ins->handler)(cpu);
}
break;
case 2:
for (uint64_t i = 0; i < CPU_BENCH_ITERATIONS; i++) {
((cpu_instruction_handler_byte_t) ins->handler)(cpu, 0x12);
}
break;
case 3:
for (uint64_t i = 0; i < CPU_BENCH_ITERATIONS; i++) {
((cpu_instruction_handler_word_t) ins->handler)(cpu, 0x1234);
}
break;
}
struct timespec now;
if (clock_gettime(CLOCK_REALTIME, &now) != 0) {
perror("Cannot get time");
exit(1);
}
uint64_t duration_ms = (now.tv_sec * 1000 + (now.tv_nsec / 1000000))
- (start.tv_sec * 1000 + (start.tv_nsec / 1000000));
runs[run] = duration_ms;
}
printf("$%.2X %s %8llu %8llu %8llu -> %8llu\n",
opcode, ins->name, runs[0], runs[1], runs[2],
(runs[0] + runs[1] + runs[2]) / 3);
}
int main(int argc, char **argv) {
struct cpu_t *cpu = cpu_create(EWM_CPU_MODEL_65C02);
cpu_add_ram_data(cpu, 0, 0xffff, malloc(0xffff));
cpu_reset(cpu);
for (int opcode = 0; opcode <= 255; opcode++) {
test(cpu, opcode);
}
}

View File

@ -31,13 +31,12 @@
#include "mem.h"
int test(int model, uint16_t start_addr, uint16_t success_addr, char *rom_path) {
struct cpu_t cpu;
cpu_init(&cpu, model);
cpu_add_ram_file(&cpu, 0x0000, rom_path);
cpu_reset(&cpu);
cpu.state.pc = start_addr;
struct cpu_t *cpu = cpu_create(model);
cpu_add_ram_file(cpu, 0x0000, rom_path);
cpu_reset(cpu);
cpu->state.pc = start_addr;
uint16_t last_pc = cpu.state.pc;
uint16_t last_pc = cpu->state.pc;
struct timespec start;
if (clock_gettime(CLOCK_REALTIME, &start) != 0) {
@ -46,12 +45,12 @@ int test(int model, uint16_t start_addr, uint16_t success_addr, char *rom_path)
}
while (true) {
int ret = cpu_step(&cpu);
int ret = cpu_step(cpu);
if (ret < 0) {
switch (ret) {
case EWM_CPU_ERR_UNIMPLEMENTED_INSTRUCTION:
fprintf(stderr, "TEST Unimplemented instruction 0x%.2x at 0x%.4x\n",
mem_get_byte(&cpu, cpu.state.pc), cpu.state.pc);
mem_get_byte(cpu, cpu->state.pc), cpu->state.pc);
return -1;
default:
fprintf(stderr, "TEST Unexpected error %d\n", ret);
@ -62,7 +61,7 @@ int test(int model, uint16_t start_addr, uint16_t success_addr, char *rom_path)
// End of the tests is at 0x3399. This is hard coded and not
// ideal. Is there a better way to detect this?
if (cpu.state.pc == success_addr) {
if (cpu->state.pc == success_addr) {
struct timespec now;
if (clock_gettime(CLOCK_REALTIME, &now) != 0) {
perror("Cannot get time");
@ -75,10 +74,10 @@ int test(int model, uint16_t start_addr, uint16_t success_addr, char *rom_path)
uint64_t duration_ms = (now.tv_sec * 1000 + (now.tv_nsec / 1000000))
- (start.tv_sec * 1000 + (start.tv_nsec / 1000000));
double duration = (double) duration_ms / 1000.0;
double mhz = (double) cpu.counter * (1.0 / duration) / 1000000.0;
double mhz = (double) cpu->counter * (1.0 / duration) / 1000000.0;
fprintf(stderr, "TEST Success; executed %" PRIu64 " cycles in %.4f at %.4f MHz\n",
cpu.counter, duration, mhz);
cpu->counter, duration, mhz);
return 0;
}
@ -87,17 +86,17 @@ int test(int model, uint16_t start_addr, uint16_t success_addr, char *rom_path)
// which we can easily detect by remembering the previous pc and
// then looking at what we are about to execute.
if (cpu.state.pc == last_pc) {
uint8_t i = mem_get_byte(&cpu, cpu.state.pc);
if (cpu->state.pc == last_pc) {
uint8_t i = mem_get_byte(cpu, cpu->state.pc);
if (i == 0x10 || i == 0x30 || i == 0x50 || i == 0x70 || i == 0x90 || i == 0xb0 || i == 0xd0 || i == 0xf0) {
if (mem_get_byte(&cpu, cpu.state.pc + 1) == 0xfe) {
fprintf(stderr, "TEST Failure at 0x%.4x \n", cpu.state.pc);
if (mem_get_byte(cpu, cpu->state.pc + 1) == 0xfe) {
fprintf(stderr, "TEST Failure at 0x%.4x \n", cpu->state.pc);
return -1;
}
}
}
last_pc = cpu.state.pc;
last_pc = cpu->state.pc;
}
}

View File

@ -413,7 +413,7 @@ static struct ewm_dsk_track_t dsk_convert_track(struct ewm_dsk_t *disk, struct e
// Public
int ewm_dsk_init(struct ewm_dsk_t *dsk, struct cpu_t *cpu) {
static int ewm_dsk_init(struct ewm_dsk_t *dsk, struct cpu_t *cpu) {
memset(dsk, 0x00, sizeof(struct ewm_dsk_t));
dsk->rom = cpu_add_rom_data(cpu, 0xc600, 0xc6ff, dsk_rom);
dsk->rom->description = "rom/dsk/$C600";
@ -428,10 +428,37 @@ struct ewm_dsk_t *ewm_dsk_create(struct cpu_t *cpu) {
return dsk;
}
static uint8_t dsk_defourxfour(uint8_t h, uint8_t l) {
return ((h << 1) | 0x01) & l;
}
static uint8_t dsk_locate_volume_number(struct ewm_dsk_track_t *track) {
for (int i = 0; i < track->length / 2; i++) {
if (track->data[i+0] == 0xd5 && track->data[i+1] == 0xaa && track->data[i+2] == 0x96) {
return dsk_defourxfour(track->data[i+3], track->data[i+4]);
}
}
return 0;
}
int ewm_dsk_set_disk_data(struct ewm_dsk_t *dsk, uint8_t index, bool readonly, void *data, size_t length, int type) {
assert(type == EWM_DSK_TYPE_DO || type == EWM_DSK_TYPE_PO);
assert(index < 2);
assert(length == (EWM_DSK_TRACKS * EWM_DSK_SECTORS * EWM_DSK_SECTOR_SIZE));
if (type == EWM_DSK_TYPE_UNKNOWN) {
return -1;
}
if (index > 1) {
return -1;
}
if (type == EWM_DSK_TYPE_DO || type == EWM_DSK_TYPE_PO) {
if (length != (EWM_DSK_TRACKS * EWM_DSK_SECTORS * 256)) {
return -1;
}
} else if (type == EWM_DSK_TYPE_NIB) {
if (length != (EWM_DSK_TRACKS * EWM_DSK_NIBBLES_PER_TRACK)) {
return -1;
}
}
struct ewm_dsk_drive_t *drive = &dsk->drives[index];
@ -444,15 +471,28 @@ int ewm_dsk_set_disk_data(struct ewm_dsk_t *dsk, uint8_t index, bool readonly, v
}
drive->loaded = true;
drive->volume = 254; // TODO Find Volume from disk image. Or does this not matter? I guess this gets lost in .dsk files.
drive->volume = 254; // Default volume number
drive->track = 0;
drive->head = 0;
drive->phase = 0;
drive->readonly = readonly;
drive->dirty = false;
for (int t = 0; t < EWM_DSK_TRACKS; t++) {
drive->tracks[t] = dsk_convert_track(dsk, drive, data, t, type);
if (type == EWM_DSK_TYPE_DO || type == EWM_DSK_TYPE_PO) {
for (int t = 0; t < EWM_DSK_TRACKS; t++) {
drive->tracks[t] = dsk_convert_track(dsk, drive, data, t, type);
}
} else if (type == EWM_DSK_TYPE_NIB) {
for (int t = 0; t < EWM_DSK_TRACKS; t++) {
drive->tracks[t].length = 6656;
drive->tracks[t].data = malloc(6656);
memcpy(drive->tracks[t].data, data + (t * 6656), 6656);
}
uint8_t volume = dsk_locate_volume_number(&drive->tracks[0]);
if (volume != 0) {
drive->volume = volume;
}
}
return 0;
@ -465,6 +505,9 @@ static int ewm_dsk_type_from_path(char *path) {
if (ewm_utl_endswith(path, ".po")) {
return EWM_DSK_TYPE_PO;
}
if (ewm_utl_endswith(path, ".nib")) {
return EWM_DSK_TYPE_NIB;
}
return EWM_DSK_TYPE_UNKNOWN;
}
@ -485,9 +528,16 @@ int ewm_dsk_set_disk_file(struct ewm_dsk_t *dsk, uint8_t drive, bool readonly, c
return -1;
}
if (file_info.st_size != (EWM_DSK_TRACKS * EWM_DSK_SECTORS * 256)) {
close(fd);
return -1;
if (type == EWM_DSK_TYPE_DO || type == EWM_DSK_TYPE_PO) {
if (file_info.st_size != (EWM_DSK_TRACKS * EWM_DSK_SECTORS * 256)) {
close(fd);
return -1;
}
} else if (type == EWM_DSK_TYPE_NIB) {
if (file_info.st_size != (EWM_DSK_TRACKS * EWM_DSK_NIBBLES_PER_TRACK)) {
close(fd);
return -1;
}
}
char *data = calloc(file_info.st_size, 1);

View File

@ -29,12 +29,13 @@
struct cpu_t;
struct mem_t;
#define EWM_DSK_DRIVE1 0
#define EWM_DSK_DRIVE2 1
#define EWM_DSK_DRIVE1 (0)
#define EWM_DSK_DRIVE2 (1)
#define EWM_DSK_TRACKS 35
#define EWM_DSK_SECTORS 16
#define EWM_DSK_SECTOR_SIZE 256
#define EWM_DSK_TRACKS (35)
#define EWM_DSK_SECTORS (16)
#define EWM_DSK_SECTOR_SIZE (256)
#define EWM_DSK_NIBBLES_PER_TRACK (6656)
struct ewm_dsk_track_t {
int length;
@ -65,8 +66,8 @@ struct ewm_dsk_t {
#define EWM_DSK_TYPE_UNKNOWN (-1)
#define EWM_DSK_TYPE_DO (0)
#define EWM_DSK_TYPE_PO (1)
#define EWM_DSK_TYPE_NIB (2)
int ewm_dsk_init(struct ewm_dsk_t *dsk, struct cpu_t *cpu);
struct ewm_dsk_t *ewm_dsk_create(struct cpu_t *cpu);
int ewm_dsk_set_disk_data(struct ewm_dsk_t *dsk, uint8_t index, bool readonly, void *data, size_t length, int type);
int ewm_dsk_set_disk_file(struct ewm_dsk_t *dsk, uint8_t index, bool readonly, char *path);

View File

@ -20,15 +20,45 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <getopt.h>
#include <SDL2/SDL.h>
#include "one.h"
#include "two.h"
#include "boo.h"
int main(int argc, char **argv) {
if (strcmp(argv[1], "one") == 0) {
return ewm_one_main(argc-1, &argv[1]);
if (argc > 1) {
// Delegate to the Apple 1 / Replica 1 emulation
if (strcmp(argv[1], "one") == 0) {
return ewm_one_main(argc-1, &argv[1]);
}
// Delegate to the Apple ][+ emulation
if (strcmp(argv[1], "two") == 0) {
return ewm_two_main(argc-1, &argv[1]);
}
return 1; // TODO Print usage
}
if (strcmp(argv[1], "two") == 0) {
return ewm_two_main(argc-1, &argv[1]);
// If we were not started with no arguments then we run the bootloader
switch (ewm_boo_main(argc, argv)) {
case EWM_BOO_BOOT_APPLE1: {
char *args[] = { "one", "-model", "apple1", NULL };
return ewm_one_main(3, args);
}
case EWM_BOO_BOOT_REPLICA1: {
char *args[] = { "one", "-model", "replica1", NULL };
return ewm_one_main(3, args);
}
case EWM_BOO_BOOT_APPLE2PLUS: {
char *args[] = { "two", NULL };
return ewm_two_main(1, args);
}
}
return 1;
}

View File

@ -74,54 +74,53 @@ uint8_t mem_get_byte_abs(struct cpu_t *cpu, uint16_t addr) {
}
uint8_t mem_get_byte_absx(struct cpu_t *cpu, uint16_t addr) {
return mem_get_byte(cpu, addr + cpu->state.x); /* TODO: Carry? */
return mem_get_byte(cpu, addr + cpu->state.x);
}
uint8_t mem_get_byte_absy(struct cpu_t *cpu, uint16_t addr) {
return mem_get_byte(cpu, addr + cpu->state.y); /* TODO: Carry? */
return mem_get_byte(cpu, addr + cpu->state.y);
}
uint8_t mem_get_byte_zpg(struct cpu_t *cpu, uint8_t addr) {
return mem_get_byte(cpu, addr);
return cpu->page0[addr];
}
uint8_t mem_get_byte_zpgx(struct cpu_t *cpu, uint8_t addr) {
return mem_get_byte(cpu, ((uint16_t) addr + cpu->state.x) & 0x00ff);
return cpu->page0[((uint16_t) addr + cpu->state.x) & 0x00ff];
}
uint8_t mem_get_byte_zpgy(struct cpu_t *cpu, uint8_t addr) {
return mem_get_byte(cpu, ((uint16_t) addr + cpu->state.y) & 0x00ff);
return cpu->page0[((uint16_t) addr + cpu->state.y) & 0x00ff];
}
uint8_t mem_get_byte_indx(struct cpu_t *cpu, uint8_t addr) {
return mem_get_byte(cpu, mem_get_word(cpu, (uint8_t)(addr + cpu->state.x)));
return mem_get_byte(cpu, (((uint16_t) cpu->page0[((uint16_t)addr+1+cpu->state.x)&0x00ff] << 8) | (uint16_t) cpu->page0[((uint16_t) addr+cpu->state.x) & 0x00ff]));
}
uint8_t mem_get_byte_indy(struct cpu_t *cpu, uint8_t addr) {
return mem_get_byte(cpu, mem_get_word(cpu, addr) + cpu->state.y);
return mem_get_byte(cpu, (((uint16_t) cpu->page0[addr+1] << 8) | (uint16_t) cpu->page0[addr]) + cpu->state.y);
}
uint8_t mem_get_byte_ind(struct cpu_t *cpu, uint8_t addr) {
return mem_get_byte(cpu, mem_get_word(cpu, addr));
return mem_get_byte(cpu, ((uint16_t) cpu->page0[addr+1] << 8) | (uint16_t) cpu->page0[addr]);
}
uint16_t mem_get_word(struct cpu_t *cpu, uint16_t addr) {
// TODO Did I do this right?
return ((uint16_t) mem_get_byte(cpu, addr+1) << 8) | (uint16_t) mem_get_byte(cpu, addr);
}
// Setters
void mem_set_byte_zpg(struct cpu_t *cpu, uint8_t addr, uint8_t v) {
mem_set_byte(cpu, addr, v);
cpu->page0[addr] = v;
}
void mem_set_byte_zpgx(struct cpu_t *cpu, uint8_t addr, uint8_t v) {
mem_set_byte(cpu, ((uint16_t) addr + cpu->state.x) & 0x00ff, v);
cpu->page0[((uint16_t) addr + cpu->state.x) & 0x00ff] = v;
}
void mem_set_byte_zpgy(struct cpu_t *cpu, uint8_t addr, uint8_t v) {
mem_set_byte(cpu, ((uint16_t) addr + cpu->state.y) & 0x00ff, v);
cpu->page0[((uint16_t) addr + cpu->state.y) & 0x00ff] = v;
}
void mem_set_byte_abs(struct cpu_t *cpu, uint16_t addr, uint8_t v) {
@ -137,16 +136,15 @@ void mem_set_byte_absy(struct cpu_t *cpu, uint16_t addr, uint8_t v) {
}
void mem_set_byte_indx(struct cpu_t *cpu, uint8_t addr, uint8_t v) {
//uint8_t a = ;
mem_set_byte(cpu, mem_get_word(cpu, (uint8_t)(addr + cpu->state.x)), v);
mem_set_byte(cpu, (((uint16_t) cpu->page0[((uint16_t)addr+1+cpu->state.x)&0x00ff] << 8) | (uint16_t) cpu->page0[((uint16_t) addr+cpu->state.x) & 0x00ff]), v);
}
void mem_set_byte_indy(struct cpu_t *cpu, uint8_t addr, uint8_t v) {
mem_set_byte(cpu, mem_get_word(cpu, addr)+cpu->state.y, v);
mem_set_byte(cpu, (((uint16_t) cpu->page0[addr+1] << 8) | (uint16_t) cpu->page0[addr]) + cpu->state.y, v);
}
void mem_set_byte_ind(struct cpu_t *cpu, uint8_t addr, uint8_t v) {
mem_set_byte(cpu, mem_get_word(cpu, addr), v);
mem_set_byte(cpu, (((uint16_t) cpu->page0[addr+1] << 8) | (uint16_t) cpu->page0[addr]), v);
}
void mem_set_word(struct cpu_t *cpu, uint16_t addr, uint16_t v) {

187
src/mem_bench.c Normal file
View File

@ -0,0 +1,187 @@
// 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 <stdint.h>
#include <time.h>
#include "cpu.h"
#include "mem.h"
#define MEM_BENCH_ITERATIONS (100 * 1000 * 1000)
#define MEM_GET_TEST(NAME, ADDR) \
void test_ ## NAME ## _ ## ADDR(struct cpu_t *cpu) { \
for (uint64_t i = 0; i < MEM_BENCH_ITERATIONS; i++) { \
(void) NAME(cpu, ADDR); \
} \
}
#define MEM_SET_TEST(NAME, ADDR) \
void test_ ## NAME ## _ ## ADDR(struct cpu_t *cpu) { \
for (uint64_t i = 0; i < MEM_BENCH_ITERATIONS; i++) { \
(void) NAME(cpu, ADDR, 0xaa); \
} \
}
#define RUN_TEST(NAME, ADDR) test(cpu, #NAME, test_ ## NAME ## _ ## ADDR)
typedef void (*test_run_t)(struct cpu_t *cpu);
void test_cpu_push_byte(struct cpu_t *cpu) {
for (uint64_t i = 0; i < MEM_BENCH_ITERATIONS; i++) {
_cpu_push_byte(cpu, 0xaa);
}
}
void test_cpu_pull_byte(struct cpu_t *cpu) {
for (uint64_t i = 0; i < MEM_BENCH_ITERATIONS; i++) {
(void) _cpu_pull_byte(cpu);
}
}
void test_cpu_push_word(struct cpu_t *cpu) {
for (uint64_t i = 0; i < MEM_BENCH_ITERATIONS; i++) {
_cpu_push_word(cpu, 0xaeae);
}
}
void test_cpu_pull_word(struct cpu_t *cpu) {
for (uint64_t i = 0; i < MEM_BENCH_ITERATIONS; i++) {
(void) _cpu_pull_word(cpu);
}
}
void test_mem_get_byte(struct cpu_t *cpu) {
for (uint64_t i = 0; i < MEM_BENCH_ITERATIONS; i++) {
(void) mem_get_byte(cpu, 0x1234);
}
}
void test_mem_set_byte(struct cpu_t *cpu) {
for (uint64_t i = 0; i < MEM_BENCH_ITERATIONS; i++) {
mem_set_byte(cpu, 0x1234, 0xaa);
}
}
void test_mem_get_byte_zp(struct cpu_t *cpu) {
for (uint64_t i = 0; i < MEM_BENCH_ITERATIONS; i++) {
(void) mem_get_byte(cpu, 0x0011);
}
}
void test_mem_set_byte_zp(struct cpu_t *cpu) {
for (uint64_t i = 0; i < MEM_BENCH_ITERATIONS; i++) {
mem_set_byte(cpu, 0x0011, 0xaa);
}
}
void test_mem_get_byte_stack(struct cpu_t *cpu) {
for (uint64_t i = 0; i < MEM_BENCH_ITERATIONS; i++) {
(void) mem_get_byte(cpu, 0x0111);
}
}
void test_mem_set_byte_stack(struct cpu_t *cpu) {
for (uint64_t i = 0; i < MEM_BENCH_ITERATIONS; i++) {
mem_set_byte(cpu, 0x0111, 0xaa);
}
}
MEM_GET_TEST(mem_get_byte, 0x1234)
MEM_GET_TEST(mem_get_byte_abs, 0x1234)
MEM_GET_TEST(mem_get_byte_absx, 0x1234)
MEM_GET_TEST(mem_get_byte_absy, 0x1234)
MEM_GET_TEST(mem_get_byte_zpg, 0x12)
MEM_GET_TEST(mem_get_byte_zpgx, 0x12)
MEM_GET_TEST(mem_get_byte_zpgy, 0x12)
MEM_GET_TEST(mem_get_byte_ind, 0x12)
MEM_GET_TEST(mem_get_byte_indx, 0x12)
MEM_GET_TEST(mem_get_byte_indy, 0x12)
MEM_SET_TEST(mem_set_byte, 0x1234)
MEM_SET_TEST(mem_set_byte_abs, 0x1234)
MEM_SET_TEST(mem_set_byte_absx, 0x1234)
MEM_SET_TEST(mem_set_byte_absy, 0x1234)
MEM_SET_TEST(mem_set_byte_zpg, 0x12)
MEM_SET_TEST(mem_set_byte_zpgx, 0x12)
MEM_SET_TEST(mem_set_byte_zpgy, 0x12)
MEM_SET_TEST(mem_set_byte_ind, 0x12)
MEM_SET_TEST(mem_set_byte_indx, 0x12)
MEM_SET_TEST(mem_set_byte_indy, 0x12)
void test(struct cpu_t *cpu, char *name, test_run_t test_run) {
struct timespec start;
if (clock_gettime(CLOCK_REALTIME, &start) != 0) {
perror("Cannot get time");
exit(1);
}
test_run(cpu);
struct timespec now;
if (clock_gettime(CLOCK_REALTIME, &now) != 0) {
perror("Cannot get time");
exit(1);
}
uint64_t duration_ms = (now.tv_sec * 1000 + (now.tv_nsec / 1000000))
- (start.tv_sec * 1000 + (start.tv_nsec / 1000000));
printf("%-32s %8llu\n", name, duration_ms);
}
int main(int argc, char **argv) {
struct cpu_t *cpu = cpu_create(EWM_CPU_MODEL_6502);
cpu_add_ram_data(cpu, 0, 0xffff, malloc(0xffff));
cpu_reset(cpu);
printf("-------------------------------- --------\n");
test(cpu, "_cpu_push_byte", test_cpu_push_byte);
test(cpu, "_cpu_pull_byte", test_cpu_pull_byte);
test(cpu, "_cpu_push_word", test_cpu_push_word);
test(cpu, "_cpu_pull_word", test_cpu_pull_word);
printf("-------------------------------- --------\n");
RUN_TEST(mem_get_byte, 0x1234);
RUN_TEST(mem_get_byte_abs, 0x1234);
RUN_TEST(mem_get_byte_absx, 0x1234);
RUN_TEST(mem_get_byte_absy, 0x1234);
RUN_TEST(mem_get_byte_zpg, 0x12);
RUN_TEST(mem_get_byte_zpgx, 0x12);
RUN_TEST(mem_get_byte_zpgy, 0x12);
RUN_TEST(mem_get_byte_ind, 0x12);
RUN_TEST(mem_get_byte_indx, 0x12);
RUN_TEST(mem_get_byte_indy, 0x12);
printf("-------------------------------- --------\n");
RUN_TEST(mem_set_byte, 0x1234);
RUN_TEST(mem_set_byte_abs, 0x1234);
RUN_TEST(mem_set_byte_absx, 0x1234);
RUN_TEST(mem_set_byte_absy, 0x1234);
RUN_TEST(mem_set_byte_zpg, 0x12);
RUN_TEST(mem_set_byte_zpgx, 0x12);
RUN_TEST(mem_set_byte_zpgy, 0x12);
RUN_TEST(mem_set_byte_ind, 0x12);
RUN_TEST(mem_set_byte_indx, 0x12);
RUN_TEST(mem_set_byte_indy, 0x12);
}

View File

@ -32,15 +32,6 @@
#include "tty.h"
#include "one.h"
struct ewm_one_t *ewm_one_create(int model, SDL_Window *window, SDL_Renderer *renderer) {
struct ewm_one_t *one = (struct ewm_one_t*) malloc(sizeof(struct ewm_one_t));
if (ewm_one_init(one, model, window, 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;
if (one->model == EWM_ONE_MODEL_APPLE1) {
@ -49,7 +40,7 @@ static void ewm_one_pia_callback(struct ewm_pia_t *pia, void *obj, uint8_t ddr,
ewm_tty_write(one->tty, v);
}
int ewm_one_init(struct ewm_one_t *one, int model, SDL_Window *window, SDL_Renderer *renderer) {
static int ewm_one_init(struct ewm_one_t *one, int model, SDL_Window *window, SDL_Renderer *renderer) {
memset(one, 0, sizeof(struct ewm_one_t));
one->model = model;
switch (model) {
@ -281,7 +272,8 @@ int ewm_one_main(int argc, char **argv) {
SDL_StartTextInput();
Uint32 ticks = SDL_GetTicks();
uint32_t ticks = SDL_GetTicks();
uint32_t phase = 1;
while (true) {
if (!ewm_one_poll_event(one, window)) { // TODO Move window into one
@ -290,20 +282,27 @@ int ewm_one_main(int argc, char **argv) {
// 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)) {
if ((SDL_GetTicks() - ticks) >= (1000 / EWM_ONE_FPS)) {
if (!ewm_one_step_cpu(one, EWM_ONE_CPS / EWM_ONE_FPS)) {
break;
}
if (one->tty->screen_dirty) {
if (one->tty->screen_dirty || (phase == 0) || ((phase % (EWM_ONE_FPS / 4)) == 0)) {
SDL_SetRenderDrawColor(one->tty->renderer, 0, 0, 0, 255);
SDL_RenderClear(one->tty->renderer);
ewm_tty_refresh(one->tty);
ewm_tty_refresh(one->tty, phase, EWM_ONE_FPS);
one->tty->screen_dirty = false;
SDL_RenderPresent(one->tty->renderer);
}
ticks = SDL_GetTicks();
phase += 1;
if (phase == EWM_ONE_FPS) {
phase = 0;
}
}
}
@ -316,3 +315,11 @@ int ewm_one_main(int argc, char **argv) {
return 0;
}
struct ewm_one_t *ewm_one_create(int model, SDL_Window *window, SDL_Renderer *renderer) {
struct ewm_one_t *one = (struct ewm_one_t*) malloc(sizeof(struct ewm_one_t));
if (ewm_one_init(one, model, window, renderer) != 0) {
free(one);
one = NULL;
}
return one;
}

View File

@ -23,11 +23,14 @@
#ifndef EWM_ONE_H
#define EWM_ONE_H
#include <SDL2/SDL.h>
#define EWM_ONE_MODEL_APPLE1 (0)
#define EWM_ONE_MODEL_REPLICA1 (1)
#define EWM_ONE_MODEL_DEFAULT (EWM_ONE_MODEL_REPLICA1)
#include <SDL2/SDL.h>
#define EWM_ONE_FPS (40)
#define EWM_ONE_CPS (1023000)
struct cpu_t;
struct ewm_tty_t;
@ -41,7 +44,6 @@ struct ewm_one_t {
};
struct ewm_one_t *ewm_one_create(int type, SDL_Window *window, SDL_Renderer *renderer);
int ewm_one_init(struct ewm_one_t *one, int type, SDL_Window *window, SDL_Renderer *renderer);
void ewm_one_destroy(struct ewm_one_t *one);
int ewm_one_main(int argc, char **argv);

View File

@ -102,6 +102,12 @@ static void pia_write(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr, uint8
}
}
static 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;
}
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) {
@ -111,12 +117,6 @@ struct ewm_pia_t *ewm_pia_create(struct cpu_t *cpu) {
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);
}

View File

@ -57,7 +57,6 @@ struct ewm_pia_t {
};
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);

View File

@ -380,9 +380,7 @@ inline static void scr_render_hgr_screen(struct scr_t *scr, bool flash) {
}
}
// TODO This is the only actual API exposed
int ewm_scr_init(struct scr_t *scr, struct ewm_two_t *two, SDL_Window *window, SDL_Renderer *renderer) {
static int ewm_scr_init(struct scr_t *scr, struct ewm_two_t *two, SDL_Window *window, SDL_Renderer *renderer) {
memset(scr, 0x00, sizeof(struct scr_t));
scr->two = two;

View File

@ -49,7 +49,6 @@ struct scr_t {
};
struct scr_t *ewm_scr_create(struct ewm_two_t *two, SDL_Window *window, SDL_Renderer *renderer);
int ewm_scr_init(struct scr_t *scr, struct ewm_two_t *two, SDL_Window* window, SDL_Renderer *renderer);
void ewm_scr_destroy(struct scr_t *scr);
void ewm_scr_update(struct scr_t *scr, int phase, int fps);
void ewm_scr_set_color_scheme(struct scr_t *scr, int color_scheme);

View File

@ -97,11 +97,18 @@ void ewm_tty_reset(struct ewm_tty_t *tty) {
tty->screen_dirty = true;
}
void ewm_tty_refresh(struct ewm_tty_t *tty) {
void ewm_tty_refresh(struct ewm_tty_t *tty, uint32_t phase, uint32_t fps) {
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);
if ((phase % (fps / 4)) == 0) {
tty->screen_cursor_blink = !tty->screen_cursor_blink;
}
if (tty->screen_cursor_blink) {
ewm_tty_render_character(tty, tty->screen_cursor_row, tty->screen_cursor_column, EWM_ONE_TTY_CURSOR);
}
}

View File

@ -43,12 +43,13 @@ struct ewm_tty_t {
int screen_cursor_row;
int screen_cursor_column;
uint32_t text_color;
int screen_cursor_blink;
};
struct ewm_tty_t *ewm_tty_create(SDL_Window *window, 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);
void ewm_tty_refresh(struct ewm_tty_t *tty, uint32_t phase, uint32_t fps);
#endif // EWM_TTY_H

View File

@ -390,9 +390,7 @@ static bool ewm_two_poll_event(struct ewm_two_t *two, SDL_Window *window) { // T
case SDL_KEYDOWN:
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
two->key = (event.key.keysym.sym - SDLK_a + 1) | 0x80;
}
} else if (event.key.keysym.mod & KMOD_GUI) {
switch (event.key.keysym.sym) {