diff --git a/.gitignore b/.gitignore index e70d67f..de8628f 100644 --- a/.gitignore +++ b/.gitignore @@ -53,6 +53,8 @@ dkms.conf ewm scr_test cpu_test +chr_test +mem_bench *.bin *.lst diff --git a/README.md b/README.md index cf877a9..afe8879 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/src/Makefile b/src/Makefile index 06bc729..cd2890c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -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 $@ diff --git a/src/boo.c b/src/boo.c new file mode 100644 index 0000000..48f5970 --- /dev/null +++ b/src/boo.c @@ -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 + +#include + +#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; +} + diff --git a/src/boo.h b/src/boo.h new file mode 100644 index 0000000..d135226 --- /dev/null +++ b/src/boo.h @@ -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 + +#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 diff --git a/src/chr.c b/src/chr.c index 9a13e15..1bf344d 100644 --- a/src/chr.c +++ b/src/chr.c @@ -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; +} diff --git a/src/chr.h b/src/chr.h index fa1c739..44abdbe 100644 --- a/src/chr.h +++ b/src/chr.h @@ -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 diff --git a/src/cpu.c b/src/cpu.c index aeef933..0ff4d95 100644 --- a/src/cpu.c +++ b/src/cpu.c @@ -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) { diff --git a/src/cpu.h b/src/cpu.h index 6b93dc5..0cb230e 100644 --- a/src/cpu.h +++ b/src/cpu.h @@ -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); diff --git a/src/cpu_bench.c b/src/cpu_bench.c new file mode 100644 index 0000000..81c5813 --- /dev/null +++ b/src/cpu_bench.c @@ -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 +#include +#include + +#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); + } +} diff --git a/src/cpu_test.c b/src/cpu_test.c index 416863a..1b012b1 100644 --- a/src/cpu_test.c +++ b/src/cpu_test.c @@ -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; } } diff --git a/src/dsk.c b/src/dsk.c index 9a99aa9..5684630 100644 --- a/src/dsk.c +++ b/src/dsk.c @@ -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); diff --git a/src/dsk.h b/src/dsk.h index e0ae176..d63fa70 100644 --- a/src/dsk.h +++ b/src/dsk.h @@ -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); diff --git a/src/ewm.c b/src/ewm.c index 79b02d7..c6c2f05 100644 --- a/src/ewm.c +++ b/src/ewm.c @@ -20,15 +20,45 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +#include + +#include + #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; } diff --git a/src/mem.c b/src/mem.c index d4d05cd..15b9be0 100644 --- a/src/mem.c +++ b/src/mem.c @@ -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) { diff --git a/src/mem_bench.c b/src/mem_bench.c new file mode 100644 index 0000000..b4f0740 --- /dev/null +++ b/src/mem_bench.c @@ -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 +#include +#include + +#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); +} diff --git a/src/one.c b/src/one.c index af5b19c..a80647f 100644 --- a/src/one.c +++ b/src/one.c @@ -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; +} diff --git a/src/one.h b/src/one.h index 280f7da..07f0e8b 100644 --- a/src/one.h +++ b/src/one.h @@ -23,11 +23,14 @@ #ifndef EWM_ONE_H #define EWM_ONE_H +#include + #define EWM_ONE_MODEL_APPLE1 (0) #define EWM_ONE_MODEL_REPLICA1 (1) #define EWM_ONE_MODEL_DEFAULT (EWM_ONE_MODEL_REPLICA1) -#include +#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); diff --git a/src/pia.c b/src/pia.c index 67e2843..bc732ea 100644 --- a/src/pia.c +++ b/src/pia.c @@ -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); } diff --git a/src/pia.h b/src/pia.h index 1e6fca8..9df36d2 100644 --- a/src/pia.h +++ b/src/pia.h @@ -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); diff --git a/src/scr.c b/src/scr.c index c4520fa..f38b359 100644 --- a/src/scr.c +++ b/src/scr.c @@ -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; diff --git a/src/scr.h b/src/scr.h index 288e1d8..adbe2a6 100644 --- a/src/scr.h +++ b/src/scr.h @@ -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); diff --git a/src/tty.c b/src/tty.c index a150f7e..8691aee 100644 --- a/src/tty.c +++ b/src/tty.c @@ -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); + } } diff --git a/src/tty.h b/src/tty.h index 9fd6059..d005b65 100644 --- a/src/tty.h +++ b/src/tty.h @@ -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 diff --git a/src/two.c b/src/two.c index fb65a91..476fea2 100644 --- a/src/two.c +++ b/src/two.c @@ -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) {