From f4f546deb5bcbb85b62e4d83c0cf60b3ddcbd852 Mon Sep 17 00:00:00 2001 From: Peter Rutenbar Date: Sat, 24 May 2014 13:39:39 -0400 Subject: [PATCH] Restart + PRAM integrated into GUI + misc Restart/shutdown now work (most of the time) PRAM is now integrated into the GUI The real time clock sorta works, but is a bit wonky Full-screen support Lots of other little bug fixes --- core/Makefile | 6 +- core/adb.c | 30 +- core/alloc_pool.c | 108 ++++- core/coff.c | 37 +- core/coff.h | 85 ---- core/core_api.c | 444 +++++++++++++----- core/core_api.h | 134 ------ core/cpu.c | 7 +- core/exception.c | 12 +- core/filesystem.c | 2 +- core/floppy.c | 25 +- core/macro.pl | 60 --- core/mem.c | 12 +- core/redblack.c | 36 +- core/scsi.c | 383 +++++++-------- core/shoebill.h | 357 +++++++++++++- core/via.c | 123 ++--- core/video.c | 1 - debugger/debugger | Bin 368068 -> 0 bytes debugger/debugger.c | 30 +- gui/Shoebill.xcodeproj/project.pbxproj | 24 +- gui/Shoebill/Base.lproj/MainMenu.xib | 7 +- gui/Shoebill/Shoebill-Info.plist | 4 +- gui/Shoebill/shoeAppDelegate.m | 7 +- gui/Shoebill/shoeApplication.h | 22 +- gui/Shoebill/shoeApplication.m | 152 +++++- .../shoePreferencesWindowController.m | 7 + .../shoePreferencesWindowController.xib | 23 +- gui/Shoebill/shoeScreenView.h | 1 - gui/Shoebill/shoeScreenView.m | 141 ++---- gui/Shoebill/shoeScreenView.xib | 11 +- gui/Shoebill/shoeScreenWindow.h | 2 - gui/Shoebill/shoeScreenWindow.m | 29 +- .../Shoebill/shoeScreenWindowController.h | 31 +- gui/Shoebill/shoeScreenWindowController.m | 76 +++ 35 files changed, 1436 insertions(+), 993 deletions(-) delete mode 100644 core/coff.h delete mode 100644 core/core_api.h delete mode 100755 debugger/debugger rename core/redblack.h => gui/Shoebill/shoeScreenWindowController.h (66%) create mode 100644 gui/Shoebill/shoeScreenWindowController.m diff --git a/core/Makefile b/core/Makefile index c9609d2..ff0b688 100644 --- a/core/Makefile +++ b/core/Makefile @@ -4,10 +4,10 @@ CFLAGS = -O3 -ggdb -flto -Wno-deprecated-declarations # CFLAGS = -O0 -ggdb -Wno-deprecated-declarations -DEPS = core_api.h coff.h mc68851.h redblack.h shoebill.h Makefile macro.pl +DEPS = mc68851.h shoebill.h Makefile macro.pl NEED_DECODER = cpu dis -NEED_PREPROCESSING = adb fpu mc68851 mem via -NEED_NOTHING = atrap_tab coff exception floppy macii_symbols redblack scsi video core_api filesystem alloc_pool +NEED_PREPROCESSING = adb fpu mc68851 mem via floppy core_api +NEED_NOTHING = atrap_tab coff exception macii_symbols redblack scsi video filesystem alloc_pool # Object files that can be compiled directly from the source OBJ_NEED_NOTHING = $(patsubst %,$(TEMP)/%.o,$(NEED_NOTHING)) diff --git a/core/adb.c b/core/adb.c index e725aa5..629c172 100644 --- a/core/adb.c +++ b/core/adb.c @@ -79,6 +79,32 @@ */ +void reset_adb_state() +{ + pthread_mutex_t lock = shoe.adb.lock; + + memset(&shoe.adb, 0, sizeof(adb_state_t)); + memset(&shoe.key, 0, sizeof(keyboard_state_t)); + memset(&shoe.mouse, 0, sizeof(mouse_state_t)); + + // Put the adb chip in state 3 (idle) + shoe.adb.state = 3; + + shoe.adb.lock = lock; +} + +void init_adb_state() +{ + memset(&shoe.adb, 0, sizeof(adb_state_t)); + memset(&shoe.key, 0, sizeof(keyboard_state_t)); + memset(&shoe.mouse, 0, sizeof(mouse_state_t)); + + // Put the adb chip in state 3 (idle) + shoe.adb.state = 3; + + pthread_mutex_init(&shoe.adb.lock, NULL); +} + void adb_start_service_request() { //printf("adb_start_service_request: pending_requests = 0x%02x\n", shoe.adb.pending_service_requests); @@ -122,8 +148,8 @@ static void keyboard_talk(uint8_t reg) case 2: // All the modifier keys are up - shoe.adb.data[0] = 0b01111111; - shoe.adb.data[1] = 0b11100111; + shoe.adb.data[0] = ~b(01111111); + shoe.adb.data[1] = ~b(11100111); shoe.adb.data_len = 2; return ; diff --git a/core/alloc_pool.c b/core/alloc_pool.c index a82d489..95a21bf 100644 --- a/core/alloc_pool.c +++ b/core/alloc_pool.c @@ -28,15 +28,44 @@ #include #include "../core/shoebill.h" -/*typedef struct _alloc_pool_t { + + +/* +#define POOL_ALLOC_TYPE 0 +#define POOL_CHILD_LINK 1 +#define POOL_HEAD 2 +typedef struct _alloc_pool_t { struct _alloc_pool_t *prev, *next; - uint32_t size, magic; -} alloc_pool_t;*/ + uint8_t type; + union { + struct { + uint32_t size; + } alloc; + struct { + struct _alloc_pool_t *child; // pointer to the child's HEAD + } child_link; + struct { + struct _alloc_pool_t *parent_link; // pointer to the parent's CHILD_LINK + } head; + } t; + + uint32_t magic; +} alloc_pool_t; +*/ + +static alloc_pool_t* _ptr_to_header(void *ptr) +{ + alloc_pool_t *apt = (alloc_pool_t*)ptr; + return &apt[-1]; +} void* p_alloc(alloc_pool_t *pool, uint64_t size) { alloc_pool_t *buf = calloc(sizeof(alloc_pool_t) + size, 1); - buf->size = size; + + buf->type = POOL_ALLOC_TYPE; + buf->t.alloc.size = size; + buf->magic = 'moof'; buf->next = pool->next; @@ -51,18 +80,33 @@ void* p_alloc(alloc_pool_t *pool, uint64_t size) void* p_realloc(void *ptr, uint64_t size) { - alloc_pool_t *header = &((alloc_pool_t*)ptr)[-1]; + alloc_pool_t *header = _ptr_to_header(ptr); + + assert(header->magic == 'moof'); + assert(header->type == POOL_ALLOC_TYPE); + alloc_pool_t *new_header = realloc(header, size + sizeof(alloc_pool_t)); - if (new_header) + if (new_header) { + new_header->t.alloc.size = size; + + if (new_header->next) + new_header->next->prev = new_header; + + if (new_header->prev) + new_header->prev->next = new_header; + return &new_header[1]; + } return NULL; } -void p_free(void *ptr) +/* + * Free *any* kind of alloc_pool_t header + */ +static void _p_free_any(alloc_pool_t *header) { - alloc_pool_t *header = &((alloc_pool_t*)ptr)[-1]; assert(header->magic == 'moof'); if (header->next) @@ -74,6 +118,16 @@ void p_free(void *ptr) free(header); } +/* + * Free an alloc_pool allocation (but not HEAD or CHILD_LINK) + */ +void p_free(void *ptr) +{ + alloc_pool_t *header = _ptr_to_header(ptr); + assert(header->type == POOL_ALLOC_TYPE); + _p_free_any(header); +} + void p_free_pool(alloc_pool_t *pool) { while (pool->prev) @@ -83,13 +137,47 @@ void p_free_pool(alloc_pool_t *pool) alloc_pool_t *cur = pool; pool = cur->next; assert(cur->magic == 'moof'); - free(cur); + + switch (cur->type) { + case POOL_ALLOC_TYPE: + _p_free_any(cur); + break; + case POOL_CHILD_LINK: { + // p_free_pool will free and unlink cur + // (its parent's CHILD_LINK) + p_free_pool(cur->t.child_link.child); + break; + } + case POOL_HEAD: { + if (cur->t.head.parent_link) { + assert(cur->t.head.parent_link->type == POOL_CHILD_LINK); + _p_free_any(cur->t.head.parent_link); + } + _p_free_any(cur); + break; + } + default: + assert(!"unknown POOL_ type"); + } } } -alloc_pool_t* p_new_pool(void) +alloc_pool_t* p_new_pool(alloc_pool_t *parent_pool) { alloc_pool_t *pool = calloc(sizeof(alloc_pool_t), 1); + pool->magic = 'moof'; + pool->type = POOL_HEAD; + + if (parent_pool) { + alloc_pool_t *link = _ptr_to_header(p_alloc(parent_pool, 0)); + link->type = POOL_CHILD_LINK; + link->t.child_link.child = pool; // child_link.child points to the child's HEAD + + pool->t.head.parent_link = link; // head.parent_link points to the parent's CHILD_LINK + } + else + pool->t.head.parent_link = NULL; + return pool; } \ No newline at end of file diff --git a/core/coff.c b/core/coff.c index 5a57a09..67c6911 100644 --- a/core/coff.c +++ b/core/coff.c @@ -30,7 +30,6 @@ #include #include #include "shoebill.h" -#include "coff.h" void symb_inorder(rb_node *cur) { const coff_symbol *sym = (coff_symbol*)cur->value; @@ -59,15 +58,19 @@ void symb_inorder(rb_node *cur) { _result; \ }) +void coff_free(coff_file *coff) +{ + p_free_pool(coff->pool); +} // Given a path to a COFF binary, create a coff_file structure and return a pointer. -// God help you if you want to free it. -coff_file* coff_parse(uint8_t *buf, uint32_t buflen) +coff_file* coff_parse(uint8_t *buf, uint32_t buflen, alloc_pool_t *parent_pool) { uint8_t rawhead[20], *ptr; uint32_t i; coff_file *cf = NULL; uint32_t bufptr = 0; + alloc_pool_t *pool = p_new_pool(parent_pool); // Pull out 20 bytes (the file header) if (!_coff_buf_read(rawhead, 20)) { @@ -76,8 +79,10 @@ coff_file* coff_parse(uint8_t *buf, uint32_t buflen) } // Allocate a coff_file and copy in the header - cf = (coff_file*)p_alloc(shoe.pool, sizeof(coff_file)); + cf = (coff_file*)p_alloc(pool, sizeof(coff_file)); + cf->pool = pool; ptr = rawhead; + cf->magic = be2native(&ptr, 2); cf->num_sections = be2native(&ptr, 2); cf->timestamp = be2native(&ptr, 4); @@ -98,7 +103,7 @@ coff_file* coff_parse(uint8_t *buf, uint32_t buflen) // pull out cf->opt_header bytes (a.out-format header, I guess?) if (cf->opt_header_len > 0) { - uint8_t *opt = p_alloc(shoe.pool, cf->opt_header_len); + uint8_t *opt = p_alloc(cf->pool, cf->opt_header_len); if (!_coff_buf_read(opt, cf->opt_header_len)) { printf("coff_parse: I ran out of data pulling the optional header (%u bytes)\n", cf->opt_header_len); p_free(opt); @@ -108,7 +113,7 @@ coff_file* coff_parse(uint8_t *buf, uint32_t buflen) } // start pulling out sections - cf->sections = p_alloc(shoe.pool, cf->num_sections * sizeof(coff_section)); + cf->sections = p_alloc(cf->pool, cf->num_sections * sizeof(coff_section)); for (i=0; inum_sections; i++) { // read the header uint8_t rawsec[40]; @@ -158,7 +163,7 @@ coff_file* coff_parse(uint8_t *buf, uint32_t buflen) } // load the data and attach it to the section struct - data = p_alloc(shoe.pool, cf->sections[i].sz); // FIXME: sz might not be a sane value + data = p_alloc(cf->pool, cf->sections[i].sz); // FIXME: sz might not be a sane value if (!_coff_buf_read(data, cf->sections[i].sz)) { printf("coff_parse: I couldn't fread section %u (%s)'s data (%u bytes)\n", i+1, cf->sections[i].name, cf->sections[i].sz); p_free(data); @@ -172,9 +177,9 @@ coff_file* coff_parse(uint8_t *buf, uint32_t buflen) if (cf->num_symbols == 0) // if num_symbols==0, symtab_offset may be bogus return cf; // just return - cf->func_tree = rb_new(); + cf->func_tree = rb_new(cf->pool); //printf("func_tree = %llx, *func_tree = %llx\n", cf->func_tree, *cf->func_tree); - cf->symbols = (coff_symbol*)p_alloc(shoe.pool, sizeof(coff_symbol) *cf->num_symbols); + cf->symbols = (coff_symbol*)p_alloc(cf->pool, sizeof(coff_symbol) *cf->num_symbols); // Seek to the symbol table if (!_coff_buf_seek(cf->symtab_offset)) { @@ -208,7 +213,7 @@ coff_file* coff_parse(uint8_t *buf, uint32_t buflen) goto fail; } } - cf->symbols[i].name = p_alloc(shoe.pool, j+1); + cf->symbols[i].name = p_alloc(cf->pool, j+1); memcpy(cf->symbols[i].name, tmp_name, j); cf->symbols[i].name[j] = 0; _coff_buf_seek(cf->symtab_offset + (i+1)*18); @@ -267,15 +272,15 @@ fail: return NULL; } -coff_file* coff_parse_from_path(const char *path) +coff_file* coff_parse_from_path(const char *path, alloc_pool_t *parent_pool) { FILE *f = fopen(path, "r"); - uint8_t *buf = p_alloc(shoe.pool, 1); + uint8_t *buf = malloc(1); uint32_t i=0, tmp; coff_file *coff; do { - buf = p_realloc(buf, i + 128*1024); + buf = realloc(buf, i + 128*1024); assert(buf); tmp = fread(buf+i, 1, 128*1024, f); i += tmp; @@ -284,8 +289,8 @@ coff_file* coff_parse_from_path(const char *path) } while ((tmp > 0) && (i < 64*1024*1024)); - coff = coff_parse(buf, i); - p_free(buf); + coff = coff_parse(buf, i, parent_pool); + free(buf); return coff; } @@ -331,7 +336,7 @@ coff_symbol* coff_find_func(coff_file *coff, uint32_t addr) // printf("coff->num_symbols = %u\n", coff->num_symbols); if (coff->num_symbols == 0) return NULL; - cur = *coff->func_tree; + cur = coff->func_tree->root; while (cur) { // printf("... iterating\n"); diff --git a/core/coff.h b/core/coff.h deleted file mode 100644 index 8bace64..0000000 --- a/core/coff.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2013, Peter Rutenbar - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _COFF_H -#define _COFF_H - -#include -#include "redblack.h" - -typedef struct { - char *name; - uint32_t value; - uint16_t scnum, type; - uint8_t sclass, numaux; -} coff_symbol; - -// informed by http://www.delorie.com/djgpp/doc/coff/scnhdr.html -typedef struct { - char name[8]; - uint32_t p_addr; - uint32_t v_addr; - uint32_t sz; - uint32_t data_ptr; - uint32_t reloc_ptr; - uint32_t line_ptr; - uint16_t num_relocs; - uint16_t num_lines; - uint32_t flags; - - uint8_t *data; -} coff_section; - -// data for this segment appears in the file, but shouldn't be copied into memory -#define coff_copy 0x0010 -#define coff_text 0x0020 -#define coff_data 0x0040 -#define coff_bss 0x0080 - -typedef struct { - uint16_t magic; - uint16_t num_sections; - uint32_t timestamp; - uint32_t symtab_offset; - uint32_t num_symbols; - uint16_t opt_header_len; - uint16_t flags; - uint8_t *opt_header; - coff_section *sections; - rb_tree *func_tree; - coff_symbol *symbols; - -} coff_file; - -coff_symbol* coff_find_func(coff_file *coff, uint32_t addr); -coff_symbol* coff_find_symbol(coff_file *coff, const char *name); - -coff_file* coff_parse(uint8_t *buf, uint32_t buflen); -coff_file* coff_parse_from_path(const char *path); -uint32_t be2native (uint8_t **dat, uint32_t bytes); -void print_coff_info(coff_file *coff); - - -#endif // _COFF_H diff --git a/core/core_api.c b/core/core_api.c index b36952c..cf356d0 100644 --- a/core/core_api.c +++ b/core/core_api.c @@ -30,19 +30,61 @@ #include #include #include -#include "shoebill.h" -#include "coff.h" -#include "core_api.h" +#include +#include "../core/shoebill.h" void shoebill_start() { + shoe.running = 1; pthread_mutex_unlock(&shoe.via_clock_thread_lock); pthread_mutex_unlock(&shoe.cpu_thread_lock); } +void shoebill_stop() +{ + uint32_t i; + + // Tear down the CPU / timer threads + shoe.cpu_thread_notifications |= SHOEBILL_STATE_RETURN; + shoe.via_thread_notifications = SHOEBILL_STATE_RETURN; + + pthread_mutex_lock(&shoe.via_clock_thread_lock); + pthread_mutex_unlock(&shoe.via_clock_thread_lock); + pthread_join(shoe.via_thread_pid, NULL); + pthread_mutex_destroy(&shoe.via_clock_thread_lock); + + pthread_kill(shoe.cpu_thread_pid, SIGUSR2); // wake up the CPU thread if it was STOPPED + pthread_mutex_lock(&shoe.cpu_thread_lock); + pthread_mutex_unlock(&shoe.cpu_thread_lock); + pthread_join(shoe.cpu_thread_pid, NULL); + pthread_mutex_destroy(&shoe.cpu_thread_lock); + + shoe.running = 0; + + // Close all the SCSI disk images + for (i=0; i<8; i++) { + if (shoe.scsi_devices[i].f) + fclose(shoe.scsi_devices[i].f); + shoe.scsi_devices[i].f = NULL; + } + + // Free the alloc pool + p_free_pool(shoe.pool); + + // Zero the global context + memset(&shoe, 0, sizeof(shoe)); +} + +void _sigusr2 (int p) +{ + return ; +} + void *_cpu_thread (void *arg) { + signal(SIGUSR2, _sigusr2); + pthread_mutex_lock(&shoe.cpu_thread_lock); while (1) { @@ -54,62 +96,20 @@ void *_cpu_thread (void *arg) process_pending_interrupt(); } + if (shoe.cpu_thread_notifications & SHOEBILL_STATE_RETURN) { + pthread_mutex_unlock(&shoe.cpu_thread_lock); + return NULL; + } + if (shoe.cpu_thread_notifications & SHOEBILL_STATE_STOPPED) { - continue; // FIXME: yield or block on a condition variable here + sleep(1); + continue; } } cpu_step(); } } -static void _cpu_loop_debug() -{ - pthread_mutex_lock(&shoe.cpu_thread_lock); - - while (1) { - if (shoe.cpu_thread_notifications) { - // I think we can safely ignore "stop" instructions for A/UX in debug mode - shoe.cpu_thread_notifications &= ~SHOEBILL_STATE_STOPPED; - - if (shoe.cpu_thread_notifications & 0xff) { - process_pending_interrupt(); - } - - if (shoe.cpu_thread_notifications & SHOEBILL_STATE_STOPPED) { - continue; // FIXME: yield or block on a condition variable here - } - } - - cpu_step(); - } -} - -/*void shoebill_cpu_stepi (void) -{ - if (shoe.cpu_mode != CPU_MODE_FREEZE) - return ; - - shoe.cpu_mode = CPU_MODE_STEPI; - pthread_mutex_unlock(&shoe.cpu_freeze_lock); - - // Just spin until the instruction completes - it should be quick - while (shoe.cpu_mode != CPU_MODE_STEPI_COMPLETE) - pthread_yield_np(); - - pthread_mutex_lock(&shoe.cpu_freeze_lock); - shoe.cpu_mode = CPU_MODE_FREEZE; -} - -void shoebill_cpu_freeze (void) -{ - pthread_mutex_lock(&shoe.cpu_freeze_lock); - shoe.cpu_mode = CPU_MODE_FREEZE; - shoe.cpu_thread_notifications &= SHOEBILL_STATE_SWITCH_MODE; - - while (shoe.cpu_thread_notifications & SHOEBILL_STATE_SWITCH_MODE); - pthread_yield_np(); -}*/ - /* * The A/UX bootloader blasts this structure into memory * somewhere before jumping into the kernel to commmunicate @@ -215,7 +215,7 @@ static void _init_macintosh_lomem_globals (const uint32_t offset) * before booting A/UX. * Side-effects: sets CPU registers d0 and a0 */ -static void _init_kernel_info(shoebill_control_t *control, scsi_device_t *disks, uint32_t offset) +static void _init_kernel_info(shoebill_config_t *config, scsi_device_t *disks, uint32_t offset) { struct kernel_info ki, *p; uint32_t i, p_addr; @@ -242,7 +242,7 @@ static void _init_kernel_info(shoebill_control_t *control, scsi_device_t *disks, // FIXME: I need to stick the auto_id for each nubus card in here // ki.auto_id[0xb] = 0x5; // Macintosh II video card has an auto_id of 5 (I guess?) - ki.auto_command = control->aux_autoconfig; // AUTO_NONE/AUTO_CONFIG + ki.auto_command = config->aux_autoconfig; // AUTO_NONE/AUTO_CONFIG /* * Note: ctrl -> SCSI controller chip @@ -252,16 +252,16 @@ static void _init_kernel_info(shoebill_control_t *control, scsi_device_t *disks, * (Used by escher/eschatology somehow) */ - ki.root_ctrl = control->root_ctrl; - ki.swap_ctrl = control->swap_ctrl; + ki.root_ctrl = config->root_ctrl; + ki.swap_ctrl = config->swap_ctrl; - ki.root_drive = control->root_drive; - ki.swap_drive = control->swap_drive; + ki.root_drive = config->root_drive; + ki.swap_drive = config->swap_drive; - ki.root_partition = control->root_partition; - ki.swap_partition = control->swap_partition; + ki.root_partition = config->root_partition; + ki.swap_partition = config->swap_partition; - ki.root_cluster = control->root_cluster; + ki.root_cluster = config->root_cluster; // Find the text, data, and bss segments in the kernel for (i = 0; i < shoe.coff->num_sections; i++) { @@ -287,7 +287,7 @@ static void _init_kernel_info(shoebill_control_t *control, scsi_device_t *disks, // +4 because the DrvQEl structure has a hidden "flags" field 4 bytes below the pointer ki.drive_queue_offset = sizeof(struct kernel_info) + 4; - ki.ki_flags = control->aux_verbose; + ki.ki_flags = config->aux_verbose; ki.ki_version = 1; /* ----- Copy ki into memory ----- */ @@ -357,14 +357,14 @@ static uint32_t _compute_rom_checksum (const uint8_t *rom, const uint32_t len) return checksum; } -static uint32_t _load_rom (shoebill_control_t *control, uint8_t **_rom_data, uint32_t *_rom_size) +static uint32_t _load_rom (shoebill_config_t *config, uint8_t **_rom_data, uint32_t *_rom_size) { uint32_t i, rom_size; uint8_t *rom_data = (uint8_t*)p_alloc(shoe.pool, 64 * 1024); - FILE *f = fopen(control->rom_path, "r"); + FILE *f = fopen(config->rom_path, "r"); if (f == NULL) { - sprintf(control->error_msg, "Couldn't open rom path [%s]\n", control->rom_path); + sprintf(config->error_msg, "Couldn't open rom path [%s]\n", config->rom_path); goto fail; } @@ -376,7 +376,7 @@ static uint32_t _load_rom (shoebill_control_t *control, uint8_t **_rom_data, uin // Rom_size had better be a power of two if ((rom_size & (rom_size - 1)) != 0) { - sprintf(control->error_msg, + sprintf(config->error_msg, "Rom is probably corrupt (size not a power of two %u)\n", rom_size); goto fail; @@ -386,7 +386,7 @@ static uint32_t _load_rom (shoebill_control_t *control, uint8_t **_rom_data, uin const uint32_t computed_checksum = _compute_rom_checksum(rom_data, rom_size); const uint32_t purported_checksum = ntohl(*(uint32_t*)rom_data); if (computed_checksum != purported_checksum) { - sprintf(control->error_msg, + sprintf(config->error_msg, "Rom checksum doesn't match (computed=0x%08x, expected=0x%08x)\n", computed_checksum, purported_checksum); goto fail; @@ -407,7 +407,7 @@ fail: return 0; } -static uint32_t _open_disk_images (shoebill_control_t *control, scsi_device_t *disks) +static uint32_t _open_disk_images (shoebill_config_t *config, scsi_device_t *disks) { uint32_t i; @@ -421,27 +421,31 @@ static uint32_t _open_disk_images (shoebill_control_t *control, scsi_device_t *d for (i=0; i<7; i++) { struct stat stat_buf; - const char *path = control->scsi_devices[i].path; + const char *path = config->scsi_devices[i].path; + char *tmp; if (!path) continue; FILE *f = fopen(path, "r+"); if (f == NULL) { - sprintf(control->error_msg, "Couldn't open scsi id #%u disk [%s]\n", i, path); + sprintf(config->error_msg, "Couldn't open scsi id #%u disk [%s]\n", i, path); goto fail; } disks[i].scsi_id = i; disks[i].f = f; - disks[i].image_path = path; + tmp = p_alloc(shoe.pool, strlen(path+1)); + strcpy(tmp, path); + disks[i].image_path = tmp; + if (fstat(fileno(f), &stat_buf)) { - sprintf(control->error_msg, "Couldn't fstat() scsi id #%u disk [%s]\n", i, path); + sprintf(config->error_msg, "Couldn't fstat() scsi id #%u disk [%s]\n", i, path); goto fail; } else if (stat_buf.st_size % 512) { - sprintf(control->error_msg, "Not aligned to 512 byte blocks: [%s]\n", path); + sprintf(config->error_msg, "Not aligned to 512 byte blocks: [%s]\n", path); goto fail; } @@ -458,7 +462,7 @@ fail: return 0; } -static uint32_t _load_aux_kernel(shoebill_control_t *control, coff_file *coff, uint32_t *_pc) +static uint32_t _load_aux_kernel(shoebill_config_t *config, coff_file *coff, uint32_t *_pc) { uint32_t j, i, pc = 0xffffffff; for (i = 0; i < coff->num_sections; i++) { @@ -486,7 +490,7 @@ static uint32_t _load_aux_kernel(shoebill_control_t *control, coff_file *coff, u } if (pc == 0xffffffff) { - sprintf(control->error_msg, "This unix kernel doesn't contain a pstart segment\n"); + sprintf(config->error_msg, "This unix kernel doesn't contain a pstart segment\n"); return 0; } @@ -498,17 +502,22 @@ fail: return 0; } -uint32_t shoebill_install_video_card(shoebill_control_t *control, uint8_t slotnum, +uint32_t shoebill_install_video_card(shoebill_config_t *config, uint8_t slotnum, uint16_t width, uint16_t height, double refresh_rate) { - shoebill_card_video_t *ctx = &control->slots[slotnum].card.video; + shoebill_card_video_t *ctx; - if (control->slots[slotnum].card_type != card_none) { - sprintf(control->error_msg, "This slot (%u) already has a card\n", slotnum); + if (shoe.slots[slotnum].card_type != card_none) { + sprintf(config->error_msg, "This slot (%u) already has a card\n", slotnum); return 0; } + ctx = p_alloc(shoe.pool, sizeof(shoebill_card_video_t)); + shoe.slots[slotnum].ctx = ctx; + + shoe.slots[slotnum].card_type = card_shoebill_video; + // Make sure the scanline width is a multiple of 32 pixels, and is at least 32 pixels // beyond the end of the display. If scanline_width==width, A/UX 2.0 will wrap the mouse around // the edge of the screen. @@ -526,13 +535,92 @@ uint32_t shoebill_install_video_card(shoebill_control_t *control, uint8_t slotnu return 1; } +static void _do_clut_translation(shoebill_card_video_t *ctx) +{ + uint32_t i; + + switch (ctx->depth) { + case 1: { + for (i=0; i < ctx->pixels/8; i++) { + const uint8_t byte = ctx->indexed_buf[i]; + ctx->direct_buf[i * 8 + 0] = ctx->clut[(byte >> 7) & 1]; + ctx->direct_buf[i * 8 + 1] = ctx->clut[(byte >> 6) & 1]; + ctx->direct_buf[i * 8 + 2] = ctx->clut[(byte >> 5) & 1]; + ctx->direct_buf[i * 8 + 3] = ctx->clut[(byte >> 4) & 1]; + ctx->direct_buf[i * 8 + 4] = ctx->clut[(byte >> 3) & 1]; + ctx->direct_buf[i * 8 + 5] = ctx->clut[(byte >> 2) & 1]; + ctx->direct_buf[i * 8 + 6] = ctx->clut[(byte >> 1) & 1]; + ctx->direct_buf[i * 8 + 7] = ctx->clut[(byte >> 0) & 1]; + } + break; + } + case 2: { + for (i=0; i < ctx->pixels/4; i++) { + const uint8_t byte = ctx->indexed_buf[i]; + ctx->direct_buf[i * 4 + 0] = ctx->clut[(byte >> 6) & 3]; + ctx->direct_buf[i * 4 + 1] = ctx->clut[(byte >> 4) & 3]; + ctx->direct_buf[i * 4 + 2] = ctx->clut[(byte >> 2) & 3]; + ctx->direct_buf[i * 4 + 3] = ctx->clut[(byte >> 0) & 3]; + } + break; + } + case 4: { + for (i=0; i < ctx->pixels/2; i++) { + const uint8_t byte = ctx->indexed_buf[i]; + ctx->direct_buf[i * 2 + 0] = ctx->clut[(byte >> 4) & 0xf]; + ctx->direct_buf[i * 2 + 1] = ctx->clut[(byte >> 0) & 0xf]; + } + break; + } + case 8: + for (i=0; i < ctx->pixels; i++) + ctx->direct_buf[i] = ctx->clut[ctx->indexed_buf[i]]; + break; + + default: + assert(!"unknown depth"); + } +} + +shoebill_video_frame_info_t shoebill_get_video_frame(uint8_t slotnum, + _Bool just_params) +{ + shoebill_card_video_t *ctx = (shoebill_card_video_t*)shoe.slots[slotnum].ctx; + shoebill_video_frame_info_t result; + + assert(shoe.slots[slotnum].card_type == card_shoebill_video); // TBF not supported yet + + if (!shoe.running) { + memset(&result, 0, sizeof(result)); + return result; + } + + result.width = ctx->width; + result.height = ctx->height; + result.scan_width = ctx->scanline_width; + result.depth = ctx->depth; + + // If caller just wants video parameters... + if (just_params) + return result; + + if (ctx->depth <= 8) { + _do_clut_translation(ctx); + result.buf = (uint8_t*)ctx->direct_buf; + + return result; + } + + assert(!"depth not supported"); +} + /* - * Given a config_control_t structure, configure and initialize + * Given a shoebill_config_t structure, configure and initialize * the emulator. * This is the first function you should call if you're writing an * interface to Shoebill. */ -uint32_t shoebill_initialize(shoebill_control_t *control) +uint32_t shoebill_initialize(shoebill_config_t *config) { uint32_t i, j, pc = 0xffffffff; coff_file *coff = NULL; @@ -544,54 +632,57 @@ uint32_t shoebill_initialize(shoebill_control_t *control) memset(&disks[0], 0, 8 * sizeof(scsi_device_t)); memset(&shoe, 0, sizeof(global_shoebill_context_t)); - shoe.pool = p_new_pool(); + // Keep a copy of *config in shoe.config_copy, so shoebill_reset() can refer to it + memcpy(&shoe.config_copy, config, sizeof(shoebill_config_t)); + + shoe.pool = p_new_pool(NULL); fpu_setup_jump_table(); // Try to load the ROM - if (control->rom_path == NULL) { - sprintf(control->error_msg, "No rom file specified\n"); + if (config->rom_path == NULL) { + sprintf(config->error_msg, "No rom file specified\n"); goto fail; } - else if (!_load_rom(control, &rom_data, &rom_size)) + else if (!_load_rom(config, &rom_data, &rom_size)) goto fail; // Try to load the A/UX kernel - if (control->aux_kernel_path == NULL) { - sprintf(control->error_msg, "No A/UX kernel specified\n"); + if (config->aux_kernel_path == NULL) { + sprintf(config->error_msg, "No A/UX kernel specified\n"); goto fail; } - else if (!control->scsi_devices[0].path || strlen((char*)control->scsi_devices[0].path)==0) { - sprintf(control->error_msg, "The root A/UX disk needs to be at scsi ID 0\n"); + else if (!config->scsi_devices[0].path || strlen((char*)config->scsi_devices[0].path)==0) { + sprintf(config->error_msg, "The root A/UX disk needs to be at scsi ID 0\n"); goto fail; } // Load the kernel from the disk at scsi id #0 - kernel_data = shoebill_extract_kernel((char*)control->scsi_devices[0].path, - control->aux_kernel_path, - control->error_msg, + kernel_data = shoebill_extract_kernel((char*)config->scsi_devices[0].path, + config->aux_kernel_path, + config->error_msg, &kernel_size); if (!kernel_data) goto fail; - coff = coff_parse(kernel_data, kernel_size); + coff = coff_parse(kernel_data, kernel_size, shoe.pool); free(kernel_data); // kernel_data was allocated with malloc() if (coff == NULL) { - sprintf(control->error_msg, "Can't open that A/UX kernel [%s]\n", - control->aux_kernel_path); + sprintf(config->error_msg, "Can't open that A/UX kernel [%s]\n", + config->aux_kernel_path); goto fail; } shoe.coff = coff; // Try to open the disk images - if (!_open_disk_images(control, disks)) + if (!_open_disk_images(config, disks)) goto fail; // Allocate and configure the rom and memory space - if (control->ram_size < (1024*1024)) { - sprintf(control->error_msg, "%u bytes is too little ram\n", control->ram_size); + if (config->ram_size < (1024*1024)) { + sprintf(config->error_msg, "%u bytes is too little ram\n", config->ram_size); goto fail; } @@ -601,8 +692,8 @@ uint32_t shoebill_initialize(shoebill_control_t *control) p_free(rom_data); rom_data = NULL; - shoe.physical_mem_size = control->ram_size; - shoe.physical_mem_base = p_alloc(shoe.pool, control->ram_size+8); // +8 because of physical_get hack + shoe.physical_mem_size = config->ram_size; + shoe.physical_mem_base = p_alloc(shoe.pool, config->ram_size+8); // +8 because of physical_get hack memset(shoe.physical_mem_base, 0, shoe.physical_mem_size); // Initialize Macintosh lomem variables that A/UX actually cares about @@ -612,11 +703,11 @@ uint32_t shoebill_initialize(shoebill_control_t *control) // Initialize A/UX's kernel_info structure - _init_kernel_info(control, disks, AUX_LOMEM_OFFSET); + _init_kernel_info(config, disks, AUX_LOMEM_OFFSET); // Load A/UX kernel COFF segments into memory (returns PC, the entry point into the kernel) - if (!_load_aux_kernel(control, coff, &pc)) + if (!_load_aux_kernel(config, coff, &pc)) goto fail; /* @@ -628,13 +719,10 @@ uint32_t shoebill_initialize(shoebill_control_t *control) * FIXME: to implement clean resetting, everything with a global structure needs * an initialization function. Starting here with via/pram... */ - init_via_state(); - - - // Put the adb chip in state 3 (idle) - // FIXME: put this in a "init_adb_state()"-type function - shoe.adb.state = 3; - pthread_mutex_init(&shoe.adb.lock, NULL); + init_via_state(config->pram, config->pram_callback, config->pram_callback_param); + init_adb_state(); + init_scsi_bus_state(); + init_iwm_state(); set_sr(0x2000); @@ -643,15 +731,15 @@ uint32_t shoebill_initialize(shoebill_control_t *control) pthread_mutex_init(&shoe.via_clock_thread_lock, NULL); pthread_mutex_lock(&shoe.via_clock_thread_lock); - pthread_create(&control->via_thread_pid, NULL, via_clock_thread, NULL); + pthread_create(&shoe.via_thread_pid, NULL, via_clock_thread, NULL); /* - * control->debug_mode is a hack - the debugger implements its own CPU thread + * config->debug_mode is a hack - the debugger implements its own CPU thread */ pthread_mutex_init(&shoe.cpu_thread_lock, NULL); pthread_mutex_lock(&shoe.cpu_thread_lock); - if (!control->debug_mode) - pthread_create(&control->cpu_thread_pid, NULL, _cpu_thread, NULL); + if (!config->debug_mode) + pthread_create(&shoe.cpu_thread_pid, NULL, _cpu_thread, NULL); return 1; @@ -670,6 +758,108 @@ fail: return 0; } +/* + * This should only be called from the context of cpu_thread + * Specifically, inst_reset() + */ +void shoebill_restart (void) +{ + coff_file *coff; + uint32_t pc, kernel_size; + uint8_t *kernel_data; + + // block other threads from twiddling shoe.adb + pthread_mutex_lock(&shoe.adb.lock); + + // FIXME: block via thread from firing timers + + // zero memory + memset(shoe.physical_mem_base, 0, shoe.physical_mem_size); + + // clear the pmmu cache + memset(shoe.pmmu_cache, 0, sizeof(shoe.pmmu_cache)); + + // Reset all CPU registers + memset(shoe.d, 0, sizeof(shoe.d)); + memset(shoe.a, 0, sizeof(shoe.a)); + shoe.vbr = 0; + shoe.sfc = 0; + shoe.dfc = 0; + shoe.cacr = 0; + shoe.usp = 0; + shoe.isp = 0; + shoe.msp = 0; + + // Reset all pmmu registers + shoe.crp = shoe.srp = shoe.drp = 0; + shoe.tc = 0; + shoe.pcsr = 0; + shoe.ac = 0; + memset(shoe.bad, 0, sizeof(shoe.bad)); + memset(shoe.bac, 0, sizeof(shoe.bac)); + shoe.cal = 0; + shoe.val = 0; + shoe.scc = 0; + shoe.psr.word = 0; + + // Reset all FPU registers + shoe.fpiar = 0; + shoe.fpcr.raw = 0; + shoe.fpsr.raw = 0; + memset(shoe.fp, 0, sizeof(shoe.fp)); + + + // Free the old unix coff_file, + coff_free(shoe.coff); + + // Close the disk at scsi id #0 + fclose(shoe.scsi_devices[0].f); + + // Reload the kernel from that disk + kernel_data = shoebill_extract_kernel((char*)shoe.scsi_devices[0].image_path, + shoe.config_copy.aux_kernel_path, + shoe.config_copy.error_msg, + &kernel_size); + + // FIXME: handle this more gracefully + assert(kernel_data && "can't reload the kernel from the root filesystem"); + + // Re-parse the kernel binary + coff = coff_parse(kernel_data, kernel_size, shoe.pool); + free(kernel_data); // kernel_data was allocated with malloc() + + // FIXME: also handle this more gracefully + assert(coff && "can't parse the kernel"); + + // Re-open the root disk image + shoe.scsi_devices[0].f = fopen(shoe.scsi_devices[0].image_path, "r+"); + assert(shoe.scsi_devices[0].f && "couldn't reopen the disk image at scsi id #0"); // FIXME: and this + + shoe.coff = coff; + + // Initialize Macintosh lomem variables that A/UX actually cares about + _init_macintosh_lomem_globals(AUX_LOMEM_OFFSET); + + // Initialize A/UX's kernel_info structure + _init_kernel_info(&shoe.config_copy, shoe.scsi_devices, AUX_LOMEM_OFFSET); + + // Load A/UX kernel COFF segments into memory (returns PC, the entry point into the kernel) + if (!_load_aux_kernel(&shoe.config_copy, shoe.coff, &pc)) + assert(!"_load_aux_kernel failed, and I can't handle that yet..."); + + // reset all devices + reset_adb_state(); + reset_scsi_bus_state(); + reset_iwm_state(); + reset_via_state(); + + set_sr(0x2000); + shoe.pc = pc; + shoe.cpu_thread_notifications = 0; + + pthread_mutex_unlock(&shoe.adb.lock); +} + static void _send_key(uint8_t code) { if ((shoe.key.key_i+1) < KEYBOARD_STATE_MAX_KEYS) { @@ -681,6 +871,9 @@ static void _send_key(uint8_t code) void shoebill_key(uint8_t down, uint8_t key) { + if (!shoe.running) + return ; + const uint8_t down_mask = down ? 0 : 0x80; assert(pthread_mutex_lock(&shoe.adb.lock) == 0); @@ -692,6 +885,9 @@ void shoebill_key(uint8_t down, uint8_t key) void shoebill_key_modifier(uint8_t modifier_mask) { + if (!shoe.running) + return ; + assert(pthread_mutex_lock(&shoe.adb.lock) == 0); const uint8_t changed_mask = shoe.key.last_modifier_mask ^ modifier_mask; @@ -717,6 +913,9 @@ void shoebill_key_modifier(uint8_t modifier_mask) void shoebill_mouse_move(int32_t x, int32_t y) { + if (!shoe.running) + return ; + assert(pthread_mutex_lock(&shoe.adb.lock) == 0); int32_t delta_x = x - shoe.mouse.old_x; @@ -736,6 +935,9 @@ void shoebill_mouse_move(int32_t x, int32_t y) void shoebill_mouse_move_delta (int32_t x, int32_t y) { + if (!shoe.running) + return ; + assert(pthread_mutex_lock(&shoe.adb.lock) == 0); shoe.mouse.delta_x += x; @@ -749,6 +951,9 @@ void shoebill_mouse_move_delta (int32_t x, int32_t y) void shoebill_mouse_click(uint8_t down) { + if (!shoe.running) + return ; + assert(pthread_mutex_lock(&shoe.adb.lock) == 0); shoe.mouse.button_down = (down != 0); @@ -759,3 +964,16 @@ void shoebill_mouse_click(uint8_t down) pthread_mutex_unlock(&shoe.adb.lock); } +void shoebill_send_vbl_interrupt(uint8_t slotnum) +{ + if (!shoe.running) + return ; + + assert((slotnum >= 9) && (slotnum <= 14) && shoe.slots[slotnum].connected); + + if (shoe.slots[slotnum].interrupts_enabled) { + shoe.via[1].rega_input &= ~b(00111111) & ~~(1 << (slotnum - 9)); + via_raise_interrupt(2, IFR_CA1); + } +} + diff --git a/core/core_api.h b/core/core_api.h deleted file mode 100644 index 70afb74..0000000 --- a/core/core_api.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (c) 2013, Peter Rutenbar - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _CORE_API_H -#define _CORE_API_H - -#include - -typedef enum { - card_none = 0, // Empty slot - card_toby_frame_buffer, // Original Macintosh II video card - card_shoebill_video, // Fancy 21st-century Shoebill video card - card_shoebill_ethernet // FIXME: doesn't exist yet -} card_names_t; - -typedef struct { - uint8_t *frame_buffer; -} shoebill_card_tfb_t; - -typedef struct { - uint8_t r, g, b, a; -} video_ctx_color_t; - -typedef struct { - video_ctx_color_t *direct_buf, *clut; - uint8_t *indexed_buf, *rom; - uint8_t *cur_buf; - - uint32_t pixels; - - uint16_t width, height, scanline_width; - - uint16_t depth, clut_idx; - - double refresh_rate; -} shoebill_card_video_t; - -typedef struct { - // Doesn't exist yet -} shoebill_card_ethernet_t; - -typedef struct { -/* - * Fill in this part of the struct - * before you call shoebill_initialize() - */ - uint32_t ram_size; - const char *rom_path; - const char *aux_kernel_path; - - uint8_t aux_verbose : 1; // Whether to boot A/UX in verbose mode - uint8_t aux_autoconfig : 1; // Whether to run A/UX autoconfig - - uint16_t root_ctrl, swap_ctrl; - uint8_t root_drive, swap_drive; - uint8_t root_partition, swap_partition; - uint8_t root_cluster; - - /* Devices at the 7 possible target SCSI ids */ - struct { - const char *path; - uint64_t size; // will be filled in later - } scsi_devices[7]; // scsi device #7 is the initiator (can't be a target) - -/* ^^^ You can stop now ^^^ */ - - /* Cards installed in the 16 (really, just 0x9 to 0xe) nubus slots */ - struct { - card_names_t card_type; - union { - shoebill_card_tfb_t tfb; - shoebill_card_video_t video; - shoebill_card_ethernet_t ethernet; - } card; - } slots[16]; - - pthread_t cpu_thread_pid, via_thread_pid; - - _Bool debug_mode; - - char error_msg[8192]; -} shoebill_control_t; - -uint32_t shoebill_initialize(shoebill_control_t *params); - -uint32_t shoebill_install_video_card(shoebill_control_t *control, uint8_t slotnum, - uint16_t width, uint16_t height, - double refresh_rate); - -/* - * These keyboard modifier constants match the ones used - * in NSEvent shifted right by 16 bits. - */ -enum { - modCapsLock = 1 << 0, - modShift = 1 << 1, - modControl = 1 << 2, - modOption = 1 << 3, - modCommand = 1 << 4 -}; - -void shoebill_key(uint8_t down, uint8_t key); -void shoebill_key_modifier(uint8_t modifier_mask); -void shoebill_mouse_move(int32_t x, int32_t y); -void shoebill_mouse_move_delta (int32_t x, int32_t y); -void shoebill_mouse_click(uint8_t down); - -void shoebill_start(); - -uint8_t* shoebill_extract_kernel(const char *disk_path, const char *kernel_path, char *error_str, uint32_t *len); - -#endif diff --git a/core/cpu.c b/core/cpu.c index 8e90b6a..94a150d 100644 --- a/core/cpu.c +++ b/core/cpu.c @@ -1241,12 +1241,7 @@ static void inst_not (void) { static void inst_reset (void) { verify_supervisor(); - // Reset does a number of things (so... look them up) - // 1: reset the "enabled" bit of the TC register - - printf("Reset! (not implemented)\n"); - assert(!"reset called"); - //dbg_state.running = 0; + shoebill_restart(); } static void inst_movec (void) { diff --git a/core/exception.c b/core/exception.c index c771e93..8578ed9 100644 --- a/core/exception.c +++ b/core/exception.c @@ -30,8 +30,12 @@ #define SSW_IS_READ (1<<6) #define SSW_DF (1<<8) -// throw the long-format (format 0xb) buss error. Mac II ROM expects to see this format for nubus bus errors. -// FIXME: nearly all the fields here are bogus +/* + * Throw the long-format (format 0xb) bus error. Mac II ROM expects to see + * this format for nubus bus errors. + * + * FIXME: nearly all the fields here are bogus + */ void throw_long_bus_error(uint32_t addr, uint8_t is_write) { if (shoe.suppress_exceptions) { @@ -61,7 +65,7 @@ void throw_long_bus_error(uint32_t addr, uint8_t is_write) const uint16_t ssw = SSW_DF | (is_write ? 0 : SSW_IS_READ) | fc; - // Note: We're pushing frame format 0xA + // Note: We're pushing frame format 0xB push_a7(0, 4); // internal registers, 18 words push_a7(0, 4); @@ -93,7 +97,7 @@ void throw_long_bus_error(uint32_t addr, uint8_t is_write) push_a7(0, 4); // instruction pipe stage B and C push_a7(ssw, 2); // special status word push_a7(0, 2); // internal register 1 - push_a7(0xB000 | vector_offset, 2); // format word + push_a7(0xB000 | vector_offset, 2); // format word (frame format B) push_a7(shoe.orig_pc, 4); // PC for the current instruction push_a7(shoe.orig_sr, 2); // original status register diff --git a/core/filesystem.c b/core/filesystem.c index 1d5ccf6..b451780 100644 --- a/core/filesystem.c +++ b/core/filesystem.c @@ -197,7 +197,7 @@ static disk_t* open_disk (const char *disk_path, char *error_str) uint8_t block[512]; apple_partition_map_t apm; uint32_t i; - alloc_pool_t *pool = p_new_pool(); + alloc_pool_t *pool = p_new_pool(NULL); FILE *f; disk = p_alloc(pool, sizeof(disk_t)); diff --git a/core/floppy.c b/core/floppy.c index d79fde0..55a4762 100644 --- a/core/floppy.c +++ b/core/floppy.c @@ -26,7 +26,8 @@ #include #include -#include "shoebill.h" +#include +#include "../core/shoebill.h" const char *latch_names[8] = { "phase0", "phase1", "phase2", "phase3", @@ -47,7 +48,7 @@ uint8_t iwm_dma_read() if (latch_val) shoe.iwm.latch |= (1 << latch_addr); else - shoe.iwm.latch &= (~(1 << latch_addr)); + shoe.iwm.latch &= (~~(1 << latch_addr)); // reg = {q7, q6, motor} const uint8_t reg = ((shoe.iwm.latch >> 5) & 6) | @@ -75,12 +76,12 @@ uint8_t iwm_dma_read() case 2: case 3: // Read status register // High 3 bits are mode register, low 5 are status - result = (shoe.iwm.status & 0b10100000); - result |= (shoe.iwm.mode & 0b11111); + result = (shoe.iwm.status & ~b(10100000)); + result |= (shoe.iwm.mode & ~b(11111)); break; case 4: case 5: // Read "write-handshake" register - result = (shoe.iwm.handshake | 0b00111111); // low 6 bits all 1's + result = (shoe.iwm.handshake | ~b(00111111)); // low 6 bits all 1's break; default: result = 0; @@ -102,7 +103,7 @@ void iwm_dma_write() if (latch_val) shoe.iwm.latch |= (1 << latch_addr); else - shoe.iwm.latch &= (~(1 << latch_addr)); + shoe.iwm.latch &= (~~(1 << latch_addr)); // reg = {q7, q6, motor} const uint8_t reg = ((shoe.iwm.latch >> 5) & 6) | @@ -121,10 +122,20 @@ void iwm_dma_write() switch (reg) { case 6: // Write mode - shoe.iwm.mode = data & 0b01111111; + shoe.iwm.mode = data & ~b(01111111); break; case 7: // Write data shoe.iwm.data = data; break; } +} + +void init_iwm_state () +{ + memset(&shoe.iwm, 0, sizeof(iwm_state_t)); +} + +void reset_iwm_state () +{ + memset(&shoe.iwm, 0, sizeof(iwm_state_t)); } \ No newline at end of file diff --git a/core/macro.pl b/core/macro.pl index 4af7a67..b540d08 100755 --- a/core/macro.pl +++ b/core/macro.pl @@ -28,7 +28,6 @@ use strict; use Carp; use Storable qw(dclone); -my $glob_printLineNumbers = 1; # Whether to prepend generated lines with their corresponding input file's line numbers my $tab = " "; # one tab => four spaces main(); @@ -140,9 +139,6 @@ sub parse { if ($newline) { # a newline just began $newline = 0; $ctx->{indent} = $text->{indents}->[$line_no]; # keep track of how many indents are on this line - if ($glob_printLineNumbers and ($ctx->{depth}==0)) { - # $out .= sprintf('/*%04u*/ ', $line_no+1); - } $out .= spaces($ctx->{indent}); # and begin the line with the appropriate indentation } @@ -155,9 +151,6 @@ sub parse { my $macro = resolve_macro($ctx); $i = $ctx->{cur_pos}; - if ($glob_printLineNumbers and ($ctx->{depth}==0)) { - # $line_str = sprintf('/*%04u*/ ', $line_no+1); - } $out .= join("\n$line_str".spaces($ctx->{indent}), split(/\n/, $macro->{str})); } @@ -328,19 +321,6 @@ sub count_args { # Macros go here: # ------------------------------------------------------------------ -sub macro_repeat { - my $args = shift; - my $num = $args->[0]; - my $text = $args->[1]; - - my $str = ""; - for (my $i=0; $i < $num; $i++) { - $str .= $text; - } - - return "{$str}"; -} - # ~decompose(op, "0101 ab0cd mmmrrr") sub macro_decompose { my ($args, $ctx) = @_; @@ -416,23 +396,6 @@ sub macro_b { return sprintf("0x%x", $val); } -# ~perl({my $str="hello"; print $str; return $str;}) // causes macrophile.pl to print hello and insert "hello" into the generated file -sub macro_perl { - my ($args, $ctx) = @_; - if (scalar(@$args) != 1) { - croak(sprintf("line %u: ~perl: ~perl() expects 1 argument (preferably a block), got %u", $ctx->{current_line}, scalar(@$args))); - } - my $code = $args->[0]; - - my $_perl_return_val; - eval 'sub ___macro_perl_sub {'.$code.'} $_perl_return_val=___macro_perl_sub($args,$ctx);'; - if ($@) { - croak(sprintf("line %u: ~perl: code fragment croaked, err={%s}", $ctx->{current_line}, $@)); - } - - return $_perl_return_val; -} - # if (~bmatch(op, 1000 xxxx 01 xxx xxx)) {...} sub macro_bmatch { my ($args, $ctx) = @_; @@ -497,29 +460,6 @@ sub macro_newmacro { return ""; } -# ~ignore( This is a comment, the only character you can't use is the closing paren. ) -sub macro_ignore { - # ignore arguments, return empty string - return ""; -} - -# qw(word, foobar) -> {"word", "foobar"} -sub macro_qw { - my ($args, $ctx) = @_; - my $str = "{"; - - if (scalar(@$args)==0) { - return "{}"; - } - foreach my $word (@$args) { - $str .= '"'.$word.'", '; - } - - $str = substr($str, 0, -2); - $str .= "}"; - return $str; -} - sub macro_bytes { my ($args, $ctx) = @_; count_args("bytes", $args, $ctx, 1); diff --git a/core/mem.c b/core/mem.c index 7699447..d4c3945 100644 --- a/core/mem.c +++ b/core/mem.c @@ -757,11 +757,11 @@ static void ea_decode_extended() uint32_t outer_disp = 0; // based on the I/IS behavior switch ((i<<3)|I) { - case 0b0010: case 0b0110: case 0b1010: + case ~b(0010): case ~b(0110): case ~b(1010): // sign-extended word-length outer displacement outer_disp = (int16_t)nextword(mypc); break; - case 0b0011: case 0b0111: case 0b1011: { + case ~b(0011): case ~b(0111): case ~b(1011): { // long word outer displacement outer_disp = nextlong(mypc); break ; @@ -775,8 +775,8 @@ static void ea_decode_extended() // Now mash all these numbers together to get an EA switch ((i<<3)|I) { - case 0b0001: case 0b0010: case 0b0011: - case 0b1001: case 0b1010: case 0b1011: { + case ~b(0001): case ~b(0010): case ~b(0011): + case ~b(1001): case ~b(1010): case ~b(1011): { // Indirect preindexed const uint32_t intermediate = lget(base_addr + base_disp + index_val, 4); if (shoe.abort) return ; @@ -786,7 +786,7 @@ static void ea_decode_extended() return ; } - case 0b0101: case 0b0110: case 0b0111: { + case ~b(0101): case ~b(0110): case ~b(0111): { // Indirect postindexed const uint32_t intermediate = lget(base_addr + base_disp, 4); if (shoe.abort) return ; @@ -795,7 +795,7 @@ static void ea_decode_extended() return ; } - case 0b1000: case 0b0000: { + case ~b(1000): case ~b(0000): { // No memory indirect action // EA = base_addr + base_disp + index shoe.extended_addr = base_addr + base_disp + index_val; diff --git a/core/redblack.c b/core/redblack.c index dc8ef32..227ac19 100644 --- a/core/redblack.c +++ b/core/redblack.c @@ -27,23 +27,27 @@ #include #include #include "shoebill.h" -#include "redblack.h" // Create a new red-black tree // (just return an empty black leaf pointer) -rb_tree* rb_new() +rb_tree* rb_new(alloc_pool_t *pool) { - return p_alloc(shoe.pool, sizeof(rb_tree)); + rb_tree *tree = (rb_tree*)p_alloc(pool, sizeof(rb_tree)); + tree->root = NULL; + tree->pool = pool; + return tree; } // Insert a new key/value into the tree // (and return the old value if *old_value is non-null.) // Returns true if the key already existed. -uint8_t rb_insert(rb_tree *root, rb_key_t key, rb_value_t value, rb_value_t *old_value) -{ +uint8_t rb_insert(rb_tree *tree, rb_key_t key, rb_value_t value, rb_value_t *old_value) +{ + rb_node **root = &tree->root; + // Special edge case: insert the root node if tree's empty if (*root == NULL) { - *root = p_alloc(shoe.pool, sizeof(rb_node)); + *root = p_alloc(tree->pool, sizeof(rb_node)); (*root)->key = key; (*root)->value = value; return 0; @@ -66,7 +70,7 @@ uint8_t rb_insert(rb_tree *root, rb_key_t key, rb_value_t value, rb_value_t *old } // insert - *cur = p_alloc(shoe.pool, sizeof(rb_node)); + *cur = p_alloc(tree->pool, sizeof(rb_node)); (*cur)->parent = parent; (*cur)->key = key; (*cur)->value = value; @@ -194,7 +198,7 @@ uint8_t rb_insert(rb_tree *root, rb_key_t key, rb_value_t value, rb_value_t *old // Find a value given a key uint8_t rb_find (rb_tree *tree, rb_key_t key, rb_value_t *value) { - rb_node *cur = *tree; + rb_node *cur = tree->root; while (cur) { if (key < cur->key) @@ -230,7 +234,7 @@ uint8_t _rb_index (rb_node *cur, uint32_t *index, rb_node **result) // Do an in-order traversal, and retrieve the (index)th sorted key/value uint8_t rb_index (rb_tree *tree, uint32_t index, rb_key_t *key, rb_value_t *value) { - rb_node *cur = *tree, *result; + rb_node *cur = tree->root, *result; if (_rb_index(cur, &index, &result)) { if (key) *key = result->key; if (value) *value = result->value; @@ -240,12 +244,16 @@ uint8_t rb_index (rb_tree *tree, uint32_t index, rb_key_t *key, rb_value_t *valu } // Count the number of nodes in the tree -uint32_t rb_count (rb_tree *tree) +static uint32_t _rb_count (rb_node *node) { - rb_node *node = *tree; if (!node) return 0; - return 1 + rb_count(&node->left) + rb_count(&node->right); + return 1 + _rb_count(node->left) + _rb_count(node->right); +} + +uint32_t rb_count (rb_tree *tree) +{ + return _rb_count(tree->root); } void _rb_free (rb_node *node) @@ -260,8 +268,8 @@ void _rb_free (rb_node *node) // Free all the nodes (and the rb_tree ptr itself) void rb_free (rb_tree *tree) { - _rb_free(*tree); - p_free(*tree); + _rb_free(tree->root); + p_free(tree->root); p_free(tree); } diff --git a/core/scsi.c b/core/scsi.c index f1f8a3a..5d18699 100644 --- a/core/scsi.c +++ b/core/scsi.c @@ -102,85 +102,17 @@ const char *scsi_write_reg_str[8] = { "start_dma_initiator_receive" }; -enum scsi_bus_phase { - BUS_FREE = 0, - ARBITRATION, - SELECTION, - RESELECTION, - COMMAND, - DATA_OUT, - DATA_IN, - STATUS, - MESSAGE_IN, - MESSAGE_OUT -}; - -typedef struct { - // Phase - enum scsi_bus_phase phase; - - // Scsi bus signals - - uint8_t init_bsy:1; // BSY, driven by initiator - uint8_t target_bsy:1; // BSY, driven by target - - uint8_t sel:1; // SEL, driven by both target and initiator - - uint8_t rst:1; // RST, driven by both target and initiator - - uint8_t cd:1; // C/D (control or data), driven by target - uint8_t io:1; // I/O, driven by target - uint8_t ack:1; // ACK, driven by initiator - uint8_t msg:1; // MSG, driven by target - uint8_t atn:1; // ATN, driven by initiator - uint8_t req:1; // REQ, driven by target - - uint8_t data; // DB0-7, data lines, driven by both target and initiator - - // NCR 5380 registers - uint8_t initiator_command; - uint8_t mode; - uint8_t target_command; - uint8_t select_enable; // probably not implementing this... - - // Arbitration state - uint8_t init_id; // initiator ID (as a bit mask) (usually 0x80) - - // Selection state - uint8_t target_id; // target ID (as an int [0, 7]) - - // transfer buffers - uint8_t buf[512 * 256]; - uint32_t bufi; - uint32_t in_len, in_i; - uint32_t out_len, out_i; - uint32_t write_offset; - uint8_t status_byte; - uint8_t message_byte; // only one-byte messages supported for now - - // hack - uint8_t dma_send_written; // Gets set whenever register 5 (start_dma_send) is written to, and cleared randomly. - // This is because aux 1.1.1 sends an extra byte after sending the write command, and that's not - // part of the write data. start_dma_send will be written when the data is actually starting. - uint8_t sent_status_byte_via_reg0; // Gets set when the status byte is red via register 0. - // This lets us know it's safe to switch to the MESSAGE_IN phase - -} scsi_bus_state_t; - -scsi_bus_state_t scsi; - static void switch_status_phase (uint8_t status_byte) { - printf("scsi_reg_something: switching to STATUS phase\n"); - scsi.phase = STATUS; - scsi.status_byte = status_byte; - scsi.msg = 0; - scsi.cd = 1; - scsi.io = 1; + shoe.scsi.phase = STATUS; + shoe.scsi.status_byte = status_byte; + shoe.scsi.msg = 0; + shoe.scsi.cd = 1; + shoe.scsi.io = 1; - scsi.bufi = 0; + shoe.scsi.bufi = 0; // Phase mismatch (I think) via_raise_interrupt(2, 0); @@ -189,13 +121,13 @@ static void switch_status_phase (uint8_t status_byte) static void switch_command_phase (void) { printf("scsi_reg_something: switching to COMMAND phase\n"); - scsi.phase = COMMAND; + shoe.scsi.phase = COMMAND; - scsi.msg = 0; - scsi.cd = 1; - scsi.io = 0; + shoe.scsi.msg = 0; + shoe.scsi.cd = 1; + shoe.scsi.io = 0; - scsi.bufi = 0; + shoe.scsi.bufi = 0; // Phase mismatch, probably via_raise_interrupt(2, 0); @@ -205,12 +137,12 @@ static void switch_message_in_phase (uint8_t message_byte) { printf("scsi_reg_something: switching to MESSAGE_IN phase\n"); - scsi.phase = MESSAGE_IN; - scsi.msg = 1; - scsi.cd = 1; - scsi.io = 1; + shoe.scsi.phase = MESSAGE_IN; + shoe.scsi.msg = 1; + shoe.scsi.cd = 1; + shoe.scsi.io = 1; - scsi.message_byte = message_byte; // only one-byte messages supported for now + shoe.scsi.message_byte = message_byte; // only one-byte messages supported for now // Phase mismatch, probably via_raise_interrupt(2, 0); @@ -220,16 +152,16 @@ static void switch_bus_free_phase (void) { printf("scsi_reg_something: switching to BUS_FREE phase\n"); - scsi.phase = BUS_FREE; + shoe.scsi.phase = BUS_FREE; - scsi.msg = 0; - scsi.cd = 0; - scsi.io = 0; + shoe.scsi.msg = 0; + shoe.scsi.cd = 0; + shoe.scsi.io = 0; - scsi.target_bsy = 0; - scsi.req = 0; + shoe.scsi.target_bsy = 0; + shoe.scsi.req = 0; - scsi.bufi = 0; + shoe.scsi.bufi = 0; // Phase mismatch not possible here. } @@ -237,11 +169,11 @@ static void switch_data_in_phase (void) { printf("scsi_reg_something: switching to DATA_IN phase\n"); - scsi.phase = DATA_IN; + shoe.scsi.phase = DATA_IN; - scsi.msg = 0; - scsi.cd = 0; - scsi.io = 1; + shoe.scsi.msg = 0; + shoe.scsi.cd = 0; + shoe.scsi.io = 1; // Phase mismatch, probably via_raise_interrupt(2, 0); @@ -251,11 +183,11 @@ static void switch_data_out_phase (void) { printf("scsi_reg_something: switching to DATA_OUT phase\n"); - scsi.phase = DATA_OUT; + shoe.scsi.phase = DATA_OUT; - scsi.msg = 0; - scsi.cd = 0; - scsi.io = 0; + shoe.scsi.msg = 0; + shoe.scsi.cd = 0; + shoe.scsi.io = 0; via_raise_interrupt(2, 0); } @@ -334,34 +266,34 @@ static void scsi_handle_inquiry_command(const uint8_t alloc_len) // XXX: added this because A/UX 3.0.1 requsts 6 bytes of the the inquiry response (sometimes?) I think it's polling for all attached scsi devices. // Fixme: figure out how to respond "not attached" if (alloc_len > sizeof(resp)) - scsi.in_len = sizeof(resp); + shoe.scsi.in_len = sizeof(resp); else - scsi.in_len = alloc_len; - memcpy(scsi.buf, &resp, scsi.in_len); - scsi.in_i = 0; + shoe.scsi.in_len = alloc_len; + memcpy(shoe.scsi.buf, &resp, shoe.scsi.in_len); + shoe.scsi.in_i = 0; switch_data_in_phase(); } static void scsi_buf_set (uint8_t byte) { - assert(scsi.bufi <= sizeof(scsi.buf)); - scsi.buf[scsi.bufi++] = byte; + assert(shoe.scsi.bufi <= sizeof(shoe.scsi.buf)); + shoe.scsi.buf[shoe.scsi.bufi++] = byte; - if (scsi.phase == COMMAND) { - const uint32_t cmd_len = (scsi.buf[0] >= 0x20) ? 10 : 6; // 10 or 6 byte command? + if (shoe.scsi.phase == COMMAND) { + const uint32_t cmd_len = (shoe.scsi.buf[0] >= 0x20) ? 10 : 6; // 10 or 6 byte command? - assert(scsi.target_id < 8); - scsi_device_t *dev = &shoe.scsi_devices[scsi.target_id]; + assert(shoe.scsi.target_id < 8); + scsi_device_t *dev = &shoe.scsi_devices[shoe.scsi.target_id]; // If we need more data for this command, keep driving REQ - if (scsi.bufi < cmd_len) { - // scsi.req = 1; + if (shoe.scsi.bufi < cmd_len) { + // shoe.scsi.req = 1; // FIXME: keep driving DMA_REQUEST too return ; } - switch (scsi.buf[0]) { + switch (shoe.scsi.buf[0]) { case 0: // test unit ready (6) printf("scsi_buf_set: responding to test-unit-ready\n"); switch_status_phase(0); // switch to the status phase, with a status byte of 0 @@ -375,27 +307,27 @@ static void scsi_buf_set (uint8_t byte) case 0x25: // read capacity (10) printf("scsi_buf_set: responding to read-capacity\n"); // bytes [0,3] -> BE number of blocks - scsi.buf[0] = (dev->num_blocks >> 24) & 0xff; - scsi.buf[1] = (dev->num_blocks >> 16) & 0xff; - scsi.buf[2] = (dev->num_blocks >> 8) & 0xff; - scsi.buf[3] = (dev->num_blocks) & 0xff; + shoe.scsi.buf[0] = (dev->num_blocks >> 24) & 0xff; + shoe.scsi.buf[1] = (dev->num_blocks >> 16) & 0xff; + shoe.scsi.buf[2] = (dev->num_blocks >> 8) & 0xff; + shoe.scsi.buf[3] = (dev->num_blocks) & 0xff; // bytes [4,7] -> BE block size (needs to be 512) - scsi.buf[4] = (dev->block_size >> 24) & 0xff; - scsi.buf[5] = (dev->block_size >> 16) & 0xff; - scsi.buf[6] = (dev->block_size >> 8) & 0xff; - scsi.buf[7] = (dev->block_size) & 0xff; + shoe.scsi.buf[4] = (dev->block_size >> 24) & 0xff; + shoe.scsi.buf[5] = (dev->block_size >> 16) & 0xff; + shoe.scsi.buf[6] = (dev->block_size >> 8) & 0xff; + shoe.scsi.buf[7] = (dev->block_size) & 0xff; - scsi.in_i = 0; - scsi.in_len = 8; + shoe.scsi.in_i = 0; + shoe.scsi.in_len = 8; switch_data_in_phase(); break; case 0x12: { // inquiry command (6) printf("scsi_buf_set: responding to inquiry\n"); - const uint8_t alloc_len = scsi.buf[4]; + const uint8_t alloc_len = shoe.scsi.buf[4]; scsi_handle_inquiry_command(alloc_len); break; @@ -403,10 +335,10 @@ static void scsi_buf_set (uint8_t byte) case 0x8: { // read (6) const uint32_t offset = - (scsi.buf[1] << 16) | - (scsi.buf[2] << 8 ) | - (scsi.buf[3]); - const uint8_t len = scsi.buf[4]; + (shoe.scsi.buf[1] << 16) | + (shoe.scsi.buf[2] << 8 ) | + (shoe.scsi.buf[3]); + const uint8_t len = shoe.scsi.buf[4]; assert(dev->f); @@ -422,11 +354,11 @@ static void scsi_buf_set (uint8_t byte) } assert(0 == fseeko(dev->f, 512 * offset, SEEK_SET)); - assert(fread(scsi.buf, len * 512, 1, dev->f) == 1); + assert(fread(shoe.scsi.buf, len * 512, 1, dev->f) == 1); - scsi.in_len = len * 512; - scsi.in_i = 0; + shoe.scsi.in_len = len * 512; + shoe.scsi.in_i = 0; switch_data_in_phase(); break; @@ -434,20 +366,20 @@ static void scsi_buf_set (uint8_t byte) case 0xa: { // write (6) const uint32_t offset = - (scsi.buf[1] << 16) | - (scsi.buf[2] << 8 ) | - (scsi.buf[3]); - const uint8_t len = scsi.buf[4]; + (shoe.scsi.buf[1] << 16) | + (shoe.scsi.buf[2] << 8 ) | + (shoe.scsi.buf[3]); + const uint8_t len = shoe.scsi.buf[4]; printf("scsi_buf_set: Responding to write at off=%u len=%u\n", offset, len); //assert(len <= 64); - scsi.write_offset = offset; - scsi.out_len = len * 512; - scsi.out_i = 0; + shoe.scsi.write_offset = offset; + shoe.scsi.out_len = len * 512; + shoe.scsi.out_i = 0; - scsi.dma_send_written = 0; // reset here. The real data will come in after start_dma_send is written to. + shoe.scsi.dma_send_written = 0; // reset here. The real data will come in after start_dma_send is written to. switch_data_out_phase(); break; } @@ -457,15 +389,22 @@ static void scsi_buf_set (uint8_t byte) break; } - scsi.bufi = 0; + shoe.scsi.bufi = 0; } } void init_scsi_bus_state () { - memset(&scsi, 0, sizeof(scsi_bus_state_t)); + memset(&shoe.scsi, 0, sizeof(scsi_bus_state_t)); - scsi.phase = BUS_FREE; + shoe.scsi.phase = BUS_FREE; +} + +void reset_scsi_bus_state () +{ + memset(&shoe.scsi, 0, sizeof(scsi_bus_state_t)); + + shoe.scsi.phase = BUS_FREE; } void scsi_reg_read () @@ -476,14 +415,14 @@ void scsi_reg_read () switch (reg) { case 0: // Current scsi data bus register - if (scsi.phase == ARBITRATION) + if (shoe.scsi.phase == ARBITRATION) shoe.physical_dat = 0; // I don't know why A/UX expects 0 here. It should be the initiator's ID, I think - else if (scsi.phase == MESSAGE_IN) { - shoe.physical_dat = scsi.message_byte; // one-byte messages supported for now + else if (shoe.scsi.phase == MESSAGE_IN) { + shoe.physical_dat = shoe.scsi.message_byte; // one-byte messages supported for now } - else if (scsi.phase == STATUS) { - shoe.physical_dat = scsi.status_byte; - scsi.sent_status_byte_via_reg0 = 1; + else if (shoe.scsi.phase == STATUS) { + shoe.physical_dat = shoe.scsi.status_byte; + shoe.scsi.sent_status_byte_via_reg0 = 1; } else assert(!"scsi_reg_read: reading data reg (0) from unknown phase\n"); @@ -492,38 +431,38 @@ void scsi_reg_read () case 1: // Initiator command register - if (scsi.phase == ARBITRATION && - (scsi.initiator_command & INIT_COMM_ARBITRATION_IN_PROGRESS)) { + if (shoe.scsi.phase == ARBITRATION && + (shoe.scsi.initiator_command & INIT_COMM_ARBITRATION_IN_PROGRESS)) { - shoe.physical_dat = scsi.initiator_command; + shoe.physical_dat = shoe.scsi.initiator_command; // the INIT_COMM_ARBITRATION_IN_PROGRESS bit is transient. Just clear // it after the first access (it needs to go hi, then later low) - scsi.initiator_command &= ~INIT_COMM_ARBITRATION_IN_PROGRESS; + shoe.scsi.initiator_command &= ~INIT_COMM_ARBITRATION_IN_PROGRESS; } else - shoe.physical_dat = scsi.initiator_command; + shoe.physical_dat = shoe.scsi.initiator_command; break; case 2: // Mode register - shoe.physical_dat = scsi.mode; + shoe.physical_dat = shoe.scsi.mode; break; case 3: // Target command register - shoe.physical_dat = scsi.target_command & 0xf; // only the low 4 bits are significant + shoe.physical_dat = shoe.scsi.target_command & 0xf; // only the low 4 bits are significant break; case 4: { // Current SCSI control register uint8_t tmp = 0; - tmp |= (scsi.sel * CURR_SCSI_CONTROL_SEL); - tmp |= (scsi.io * CURR_SCSI_CONTROL_IO); - tmp |= (scsi.cd * CURR_SCSI_CONTROL_CD); - tmp |= (scsi.msg * CURR_SCSI_CONTROL_MSG); - tmp |= (scsi.req * CURR_SCSI_CONTROL_REQ); - tmp |= ((scsi.target_bsy || scsi.init_bsy) ? CURR_SCSI_CONTROL_BSY : 0); - tmp |= (scsi.rst * CURR_SCSI_CONTROL_RST); + tmp |= (shoe.scsi.sel * CURR_SCSI_CONTROL_SEL); + tmp |= (shoe.scsi.io * CURR_SCSI_CONTROL_IO); + tmp |= (shoe.scsi.cd * CURR_SCSI_CONTROL_CD); + tmp |= (shoe.scsi.msg * CURR_SCSI_CONTROL_MSG); + tmp |= (shoe.scsi.req * CURR_SCSI_CONTROL_REQ); + tmp |= ((shoe.scsi.target_bsy || shoe.scsi.init_bsy) ? CURR_SCSI_CONTROL_BSY : 0); + tmp |= (shoe.scsi.rst * CURR_SCSI_CONTROL_RST); shoe.physical_dat = tmp; break; } @@ -534,14 +473,14 @@ void scsi_reg_read () // Compute phase match (IO, CD, MSG match the assertions in target_command register) uint8_t phase_tmp = 0; { - phase_tmp = (phase_tmp << 1) | scsi.msg; - phase_tmp = (phase_tmp << 1) | scsi.cd; - phase_tmp = (phase_tmp << 1) | scsi.io; - phase_tmp = (phase_tmp == (scsi.target_command & 7)); + phase_tmp = (phase_tmp << 1) | shoe.scsi.msg; + phase_tmp = (phase_tmp << 1) | shoe.scsi.cd; + phase_tmp = (phase_tmp << 1) | shoe.scsi.io; + phase_tmp = (phase_tmp == (shoe.scsi.target_command & 7)); } - tmp |= (scsi.ack * BUS_STATUS_ACK); - tmp |= (scsi.atn * BUS_STATUS_ATN); + tmp |= (shoe.scsi.ack * BUS_STATUS_ACK); + tmp |= (shoe.scsi.atn * BUS_STATUS_ATN); tmp |= (phase_tmp * BUS_STATUS_PHASE_MATCH); // let's just say BUS_ERROR is always false (fixme: wrong) @@ -571,67 +510,67 @@ void scsi_reg_write () switch (reg) { case 0: // Output data register - scsi.data = dat; + shoe.scsi.data = dat; break; case 1: { // Initiator command register - scsi.initiator_command = dat; + shoe.scsi.initiator_command = dat; - scsi.ack = ((scsi.initiator_command & INIT_COMM_ASSERT_ACK) != 0); - scsi.rst = ((scsi.initiator_command & INIT_COMM_ASSERT_RST) != 0); - scsi.init_bsy = ((scsi.initiator_command & INIT_COMM_ASSERT_BSY) != 0); - scsi.sel = ((scsi.initiator_command & INIT_COMM_ASSERT_SEL) != 0); - scsi.atn = ((scsi.initiator_command & INIT_COMM_ASSERT_ATN) != 0); + shoe.scsi.ack = ((shoe.scsi.initiator_command & INIT_COMM_ASSERT_ACK) != 0); + shoe.scsi.rst = ((shoe.scsi.initiator_command & INIT_COMM_ASSERT_RST) != 0); + shoe.scsi.init_bsy = ((shoe.scsi.initiator_command & INIT_COMM_ASSERT_BSY) != 0); + shoe.scsi.sel = ((shoe.scsi.initiator_command & INIT_COMM_ASSERT_SEL) != 0); + shoe.scsi.atn = ((shoe.scsi.initiator_command & INIT_COMM_ASSERT_ATN) != 0); /* // --- Arbitration --- // Check whether to switch from ARBITRATION to SELECTION - if (scsi.sel && scsi.phase == ARBITRATION) { + if (shoe.scsi.sel && shoe.scsi.phase == ARBITRATION) { // Asserting SEL in arbitration phase means we switch to selection phase :) - scsi.phase = SELECTION; - scsi.target_id = INVALID_ID; // invalid ID + shoe.scsi.phase = SELECTION; + shoe.scsi.target_id = INVALID_ID; // invalid ID printf("scsi_reg_write: selection phase\n"); break; } */ // --- Selection --- - // If we're in SELECTION, receive the target_id from scsi.data - if (scsi.sel && (scsi.initiator_command & INIT_COMM_ASSERT_DATA_BUS) && - ((scsi.phase == ARBITRATION) || (scsi.phase == BUS_FREE))) + // If we're in SELECTION, receive the target_id from shoe.scsi.data + if (shoe.scsi.sel && (shoe.scsi.initiator_command & INIT_COMM_ASSERT_DATA_BUS) && + ((shoe.scsi.phase == ARBITRATION) || (shoe.scsi.phase == BUS_FREE))) { uint8_t id; - for (id=0; (id < 8) && !(scsi.data & (1 << id)); id++) ; + for (id=0; (id < 8) && !(shoe.scsi.data & (1 << id)); id++) ; assert(id != 8); - scsi.target_id = id; + shoe.scsi.target_id = id; printf("scsi_reg_write: selected target id %u\n", id); - scsi.target_bsy = 1; // target asserts BSY to acknowledge being selected - scsi.phase = SELECTION; + shoe.scsi.target_bsy = 1; // target asserts BSY to acknowledge being selected + shoe.scsi.phase = SELECTION; break; } // SELECTION ends when SEL gets unset - if (!scsi.sel && scsi.phase == SELECTION) { + if (!shoe.scsi.sel && shoe.scsi.phase == SELECTION) { printf("scsi_reg_write: switch to COMMAND phase\n"); // what's next? - scsi.req = 1; // target asserts REQ after initiator deasserts SEL + shoe.scsi.req = 1; // target asserts REQ after initiator deasserts SEL // Switch to COMMAND phase - scsi.cd = 1; - scsi.io = 0; - scsi.msg = 0; - scsi.phase = COMMAND; + shoe.scsi.cd = 1; + shoe.scsi.io = 0; + shoe.scsi.msg = 0; + shoe.scsi.phase = COMMAND; break; } // --- Information transfer --- // If initiator asserts ACK, then target needs to deassert REQ // (I think this only makes sense for non-arbitration/selection/busfree situations - if ((scsi.phase != BUS_FREE) && (scsi.phase != ARBITRATION) && (scsi.phase != SELECTION)) { + if ((shoe.scsi.phase != BUS_FREE) && (shoe.scsi.phase != ARBITRATION) && (shoe.scsi.phase != SELECTION)) { // If this is the message_in phase, use the unsetting-ACK portion of the REQ/ACK handshake // to go to BUS_FREE. // Don't bother asserting REQ here. Also, switch_bus_free_phase() will deassert target_BSY. - if (scsi.phase == MESSAGE_IN && !scsi.ack && !scsi.req) { + if (shoe.scsi.phase == MESSAGE_IN && !shoe.scsi.ack && !shoe.scsi.req) { switch_bus_free_phase(); break ; } @@ -640,12 +579,12 @@ void scsi_reg_write () // Do this when the OS deasserts ACK. We know that ACK was previously asserted if !REQ. // (This is kinda hacky - maybe I can detect if ACK is deasserted by looking at the // previous value of reg1) - else if (scsi.phase == STATUS && !scsi.ack && !scsi.req && scsi.sent_status_byte_via_reg0) { - scsi.req = 1; + else if (shoe.scsi.phase == STATUS && !shoe.scsi.ack && !shoe.scsi.req && shoe.scsi.sent_status_byte_via_reg0) { + shoe.scsi.req = 1; switch_message_in_phase(0); } else { - scsi.req = !scsi.ack; + shoe.scsi.req = !shoe.scsi.ack; } } @@ -653,12 +592,12 @@ void scsi_reg_write () } case 2: { // Mode register - scsi.mode = dat; + shoe.scsi.mode = dat; - if (scsi.mode & MODE_ARBITRATE) { + if (shoe.scsi.mode & MODE_ARBITRATE) { printf("scsi_reg_write: arbitration phase\n"); - scsi.phase = ARBITRATION; - scsi.initiator_command |= INIT_COMM_ARBITRATION_IN_PROGRESS; + shoe.scsi.phase = ARBITRATION; + shoe.scsi.initiator_command |= INIT_COMM_ARBITRATION_IN_PROGRESS; } else { @@ -667,15 +606,15 @@ void scsi_reg_write () break; } case 3: // Target command register - scsi.target_command = dat & 0xf; // only the bottom 4 bits are writable + shoe.scsi.target_command = dat & 0xf; // only the bottom 4 bits are writable break; case 4: // ID select register - scsi.select_enable = dat; + shoe.scsi.select_enable = dat; break; case 5: // Start DMA send - scsi.dma_send_written = 1; + shoe.scsi.dma_send_written = 1; via_raise_interrupt(2, 0); break; @@ -700,31 +639,31 @@ void scsi_dma_write_long(const uint32_t dat) void scsi_dma_write (const uint8_t byte) { - if (scsi.phase == COMMAND) { + if (shoe.scsi.phase == COMMAND) { printf("scsi_reg_dma_write: writing COMMAND byte 0x%02x\n", byte); scsi_buf_set(byte); } - else if (scsi.phase == DATA_OUT && scsi.dma_send_written) { - scsi.buf[scsi.out_i++] = byte; + else if (shoe.scsi.phase == DATA_OUT && shoe.scsi.dma_send_written) { + shoe.scsi.buf[shoe.scsi.out_i++] = byte; //printf("scsi_reg_dma_write: writing DATA_OUT byte 0x%02x (%c)\n", byte, isprint(byte)?byte:'.'); - if (scsi.out_i >= scsi.out_len) { - assert(scsi.target_id < 8); - scsi_device_t *dev = &shoe.scsi_devices[scsi.target_id]; + if (shoe.scsi.out_i >= shoe.scsi.out_len) { + assert(shoe.scsi.target_id < 8); + scsi_device_t *dev = &shoe.scsi_devices[shoe.scsi.target_id]; assert(dev->f); - assert(0 == fseeko(dev->f, 512 * scsi.write_offset, SEEK_SET)); - assert(fwrite(scsi.buf, scsi.out_len, 1, dev->f) == 1); + assert(0 == fseeko(dev->f, 512 * shoe.scsi.write_offset, SEEK_SET)); + assert(fwrite(shoe.scsi.buf, shoe.scsi.out_len, 1, dev->f) == 1); fflush(dev->f); - scsi.out_i = 0; - scsi.out_len = 0; + shoe.scsi.out_i = 0; + shoe.scsi.out_len = 0; switch_status_phase(0); } } - else if (scsi.phase == DATA_OUT) { - printf("scsi_reg_dma_write: writing DATA_OUT byte (without scsi.dma_send_written) 0x%02x\n", byte); + else if (shoe.scsi.phase == DATA_OUT) { + printf("scsi_reg_dma_write: writing DATA_OUT byte (without shoe.scsi.dma_send_written) 0x%02x\n", byte); } else { printf("scsi_reg_dma_write: writing 0x%02x in UNKNOWN PHASE!\n", byte); @@ -747,17 +686,17 @@ uint8_t scsi_dma_read () { uint8_t result = 0; - if (scsi.phase == STATUS) { + if (shoe.scsi.phase == STATUS) { // If in the STATUS phase, return the status byte and switch back to COMMAND phase - result = scsi.status_byte; + result = shoe.scsi.status_byte; switch_message_in_phase(0); } - else if (scsi.phase == DATA_IN) { - assert(scsi.in_len > 0); - result = scsi.buf[scsi.in_i++]; - if (scsi.in_i >= scsi.in_len) { - scsi.in_i = 0; - scsi.in_len = 0; + else if (shoe.scsi.phase == DATA_IN) { + assert(shoe.scsi.in_len > 0); + result = shoe.scsi.buf[shoe.scsi.in_i++]; + if (shoe.scsi.in_i >= shoe.scsi.in_len) { + shoe.scsi.in_i = 0; + shoe.scsi.in_len = 0; switch_status_phase(0); } diff --git a/core/shoebill.h b/core/shoebill.h index d6c1990..a256291 100644 --- a/core/shoebill.h +++ b/core/shoebill.h @@ -31,12 +31,100 @@ #include #include #include -//#include -// void ring_print(const char *str); -// extern char *ring_tmp; -#include "coff.h" + +/* + * core_api.c stuff + */ + + + +typedef void (*shoebill_pram_callback_t) (void *param, const uint8_t addr, const uint8_t byte); + +typedef struct { + uint32_t ram_size; + const char *rom_path; + const char *aux_kernel_path; // almost always "/unix" + + _Bool aux_verbose : 1; // Whether to boot A/UX in verbose mode + _Bool aux_autoconfig : 1; // Whether to run A/UX autoconfig + _Bool debug_mode : 1; // Whether to enable hacks that debugger depends on + + uint16_t root_ctrl, swap_ctrl; + uint8_t root_drive, swap_drive; + uint8_t root_partition, swap_partition; + uint8_t root_cluster; + + /* Devices at the 7 possible target SCSI ids */ + struct { + const char *path; + } scsi_devices[7]; // scsi id #7 is the initiator (can't be a target) + + /* Initialize pram[] with initial PRAM data */ + uint8_t pram[256]; + + /* + * This callback is called whenever a PRAM byte is changed. + * It blocks the CPU, so try to return immediately. + */ + shoebill_pram_callback_t pram_callback; + void *pram_callback_param; + + char error_msg[8192]; +} shoebill_config_t; + +typedef struct { + const uint8_t *buf; + uint16_t width, height, scan_width, depth; +} shoebill_video_frame_info_t; + +/* Take a shoebill_config_t structure and configure the global emulator context */ +uint32_t shoebill_initialize(shoebill_config_t *params); + +void shoebill_restart (void); + +/* Call this after shoebill_initialize() to configure a video card */ +uint32_t shoebill_install_video_card(shoebill_config_t *config, uint8_t slotnum, + uint16_t width, uint16_t height, + double refresh_rate); + +/* Get a video frame from a particular video card */ +shoebill_video_frame_info_t shoebill_get_video_frame(uint8_t slotnum, _Bool just_params); + +/* Call this after rendering a video frame to send a VBL interrupt */ +void shoebill_send_vbl_interrupt(uint8_t slotnum); + +/* + * These keyboard modifier constants match the ones used + * in NSEvent shifted right by 16 bits. + */ +enum { + modCapsLock = 1 << 0, + modShift = 1 << 1, + modControl = 1 << 2, + modOption = 1 << 3, + modCommand = 1 << 4 +}; + +void shoebill_key(uint8_t down, uint8_t key); +void shoebill_key_modifier(uint8_t modifier_mask); +void shoebill_mouse_move(int32_t x, int32_t y); +void shoebill_mouse_move_delta (int32_t x, int32_t y); +void shoebill_mouse_click(uint8_t down); + +void shoebill_start(); +void shoebill_stop(); + +uint8_t* shoebill_extract_kernel(const char *disk_path, const char *kernel_path, char *error_str, uint32_t *len); + + + +/* + * Internal shoebill stuff + */ + + // -- Global constants -- @@ -138,18 +226,123 @@ #define ea_n(s) ((shoe.dat>>((s)*8-1))&1) #define ea_z(s) (chop(shoe.dat, (s))==0) -// alloc_pool.c +/* + * alloc_pool.c + */ + +#define POOL_ALLOC_TYPE 0 +#define POOL_CHILD_LINK 1 +#define POOL_HEAD 2 typedef struct _alloc_pool_t { struct _alloc_pool_t *prev, *next; - uint32_t size, magic; + + union { + struct { + uint64_t size; + } alloc; + struct { + struct _alloc_pool_t *child; // pointer to the child's HEAD + } child_link; + struct { + struct _alloc_pool_t *parent_link; // pointer to the parent's CHILD_LINK + } head; + } t; + + uint32_t type; + uint32_t magic; } alloc_pool_t; void* p_alloc(alloc_pool_t *pool, uint64_t size); void* p_realloc(void *ptr, uint64_t size); void p_free(void *ptr); void p_free_pool(alloc_pool_t *pool); -alloc_pool_t* p_new_pool(void); +alloc_pool_t* p_new_pool(alloc_pool_t *parent_pool); +/* + * redblack.c + */ + +typedef uint32_t rb_key_t; +typedef void* rb_value_t; + +typedef struct _rb_node { + struct _rb_node *left, *right, *parent; + rb_key_t key; + rb_value_t value; + uint8_t is_red : 1; +} rb_node; + +typedef struct { + rb_node *root; + alloc_pool_t *pool; +} rb_tree; + + +rb_tree* rb_new(alloc_pool_t *pool); +void rb_free (rb_tree *tree); + +uint8_t rb_insert (rb_tree *root, rb_key_t key, rb_value_t value, rb_value_t *old_value); +uint8_t rb_find (rb_tree *tree, rb_key_t key, rb_value_t *value); +uint8_t rb_index (rb_tree *tree, uint32_t index, rb_key_t *key, rb_value_t *value); +uint32_t rb_count (rb_tree *tree); + + +/* + * coff.c + */ + +typedef struct { + char *name; + uint32_t value; + uint16_t scnum, type; + uint8_t sclass, numaux; +} coff_symbol; + +// informed by http://www.delorie.com/djgpp/doc/coff/scnhdr.html +typedef struct { + char name[8]; + uint32_t p_addr; + uint32_t v_addr; + uint32_t sz; + uint32_t data_ptr; + uint32_t reloc_ptr; + uint32_t line_ptr; + uint16_t num_relocs; + uint16_t num_lines; + uint32_t flags; + + uint8_t *data; +} coff_section; + +// data for this segment appears in the file, but shouldn't be copied into memory +#define coff_copy 0x0010 +#define coff_text 0x0020 +#define coff_data 0x0040 +#define coff_bss 0x0080 + +typedef struct { + uint16_t magic; + uint16_t num_sections; + uint32_t timestamp; + uint32_t symtab_offset; + uint32_t num_symbols; + uint16_t opt_header_len; + uint16_t flags; + uint8_t *opt_header; + coff_section *sections; + rb_tree *func_tree; + coff_symbol *symbols; + alloc_pool_t *pool; +} coff_file; + +coff_symbol* coff_find_func(coff_file *coff, uint32_t addr); +coff_symbol* coff_find_symbol(coff_file *coff, const char *name); + +coff_file* coff_parse(uint8_t *buf, uint32_t buflen, alloc_pool_t *parent_pool); +coff_file* coff_parse_from_path(const char *path, alloc_pool_t *parent_pool); +void coff_free(coff_file *coff); +uint32_t be2native (uint8_t **dat, uint32_t bytes); +void print_coff_info(coff_file *coff); typedef struct dbg_breakpoint_t { @@ -213,9 +406,20 @@ typedef struct { // FSM uint8_t command[8]; uint8_t byte, mode, command_i, bit_i; + + shoebill_pram_callback_t callback; + void *callback_param; } pram_state_t; -void init_via_state(); +void init_via_state (uint8_t pram_data[256], shoebill_pram_callback_t callback, void *callback_param); +void init_adb_state(); +void init_scsi_bus_state(); +void init_iwm_state(); + +void reset_via_state(); +void reset_adb_state(); +void reset_scsi_bus_state(); +void reset_iwm_state(); typedef struct { uint8_t scsi_id; @@ -242,7 +446,7 @@ typedef struct { uint8_t changed; } mouse_state_t; -typedef struct { +/*typedef struct { uint8_t *buf_base; uint32_t buf_size; @@ -256,7 +460,7 @@ typedef struct { uint8_t clut[256 * 3]; uint32_t clut_idx; -} video_state_t; +} video_state_t;*/ typedef struct { // lsb==phase0, msb==L7 @@ -266,13 +470,116 @@ typedef struct { uint8_t data, status, mode, handshake; } iwm_state_t; +enum scsi_bus_phase { + BUS_FREE = 0, + ARBITRATION, + SELECTION, + RESELECTION, + COMMAND, + DATA_OUT, + DATA_IN, + STATUS, + MESSAGE_IN, + MESSAGE_OUT +}; + +typedef struct { + // Phase + enum scsi_bus_phase phase; + + // Scsi bus signals + + uint8_t init_bsy:1; // BSY, driven by initiator + uint8_t target_bsy:1; // BSY, driven by target + + uint8_t sel:1; // SEL, driven by both target and initiator + + uint8_t rst:1; // RST, driven by both target and initiator + + uint8_t cd:1; // C/D (control or data), driven by target + uint8_t io:1; // I/O, driven by target + uint8_t ack:1; // ACK, driven by initiator + uint8_t msg:1; // MSG, driven by target + uint8_t atn:1; // ATN, driven by initiator + uint8_t req:1; // REQ, driven by target + + uint8_t data; // DB0-7, data lines, driven by both target and initiator + + // NCR 5380 registers + uint8_t initiator_command; + uint8_t mode; + uint8_t target_command; + uint8_t select_enable; // probably not implementing this... + + // Arbitration state + uint8_t init_id; // initiator ID (as a bit mask) (usually 0x80) + + // Selection state + uint8_t target_id; // target ID (as an int [0, 7]) + + // transfer buffers + uint8_t buf[512 * 256]; + uint32_t bufi; + uint32_t in_len, in_i; + uint32_t out_len, out_i; + uint32_t write_offset; + uint8_t status_byte; + uint8_t message_byte; // only one-byte messages supported for now + + // hack + uint8_t dma_send_written; // Gets set whenever register 5 (start_dma_send) is written to, and cleared randomly. + // This is because aux 1.1.1 sends an extra byte after sending the write command, and that's not + // part of the write data. start_dma_send will be written when the data is actually starting. + uint8_t sent_status_byte_via_reg0; // Gets set when the status byte is red via register 0. + // This lets us know it's safe to switch to the MESSAGE_IN phase + +} scsi_bus_state_t; + +typedef struct { + uint8_t r, g, b, a; +} video_ctx_color_t; + +typedef struct { + video_ctx_color_t *direct_buf, *clut; + uint8_t *indexed_buf, *rom; + uint8_t *cur_buf; + + uint32_t pixels; + + uint16_t width, height, scanline_width; + + uint16_t depth, clut_idx; + + double refresh_rate; +} shoebill_card_video_t; + +typedef struct { + uint8_t *frame_buffer; +} shoebill_card_tfb_t; + +typedef struct { + // Doesn't exist yet +} shoebill_card_ethernet_t; + +typedef enum { + card_none = 0, // Empty slot + card_toby_frame_buffer, // Original Macintosh II video card + card_shoebill_video, // Fancy 21st-century Shoebill video card + card_shoebill_ethernet // FIXME: doesn't exist yet +} card_names_t; typedef struct { uint32_t (*read_func)(uint32_t, uint32_t, uint8_t); void (*write_func)(uint32_t, uint32_t, uint32_t, uint8_t); + + uint8_t slotnum; + _Bool connected; + _Bool interrupts_enabled; + + long double interrupt_rate, last_fired; // FIXME: probably don't need these? + void *ctx; - uint8_t slotnum, connected, interrupts_enabled; - long double interrupt_rate, last_fired; + card_names_t card_type; } nubus_card_t; typedef struct { @@ -293,11 +600,15 @@ typedef struct { typedef struct { -#define SHOEBILL_STATE_STOPPED (1<<8) + _Bool running; + +#define SHOEBILL_STATE_STOPPED (1 << 8) +#define SHOEBILL_STATE_RETURN (1 << 9) // bits 0-6 are CPU interrupt priorities // bit 8 indicates that STOP was called volatile uint32_t cpu_thread_notifications; + volatile uint32_t via_thread_notifications; pthread_mutex_t cpu_thread_lock; pthread_mutex_t via_clock_thread_lock; @@ -445,26 +756,27 @@ typedef struct { // -- Interrupts/VIA chips -- via_state_t via[2]; + via_clock_t via_clocks; adb_state_t adb; + pram_state_t pram; keyboard_state_t key; mouse_state_t mouse; + iwm_state_t iwm; + scsi_bus_state_t scsi; + scsi_device_t scsi_devices[8]; // SCSI devices + nubus_card_t slots[16]; - via_clock_t via_clocks; - - struct timeval start_time; // when the emulator started (for computing timer interrupts) - uint64_t total_ticks; // how many 60hz ticks have been generated - coff_file *coff; // Data/symbols from the unix kernel - scsi_device_t scsi_devices[8]; // SCSI devices - - pram_state_t pram; + pthread_t cpu_thread_pid, via_thread_pid; debugger_state_t dbg; alloc_pool_t *pool; + + shoebill_config_t config_copy; // copy of the config structure passed to shoebill_initialize() } global_shoebill_context_t; extern global_shoebill_context_t shoe; // declared in cpu.c @@ -666,9 +978,6 @@ uint32_t nubus_video_read_func(const uint32_t rawaddr, const uint32_t size, void nubus_video_write_func(const uint32_t rawaddr, const uint32_t size, const uint32_t data, const uint8_t slotnum); -// debug_server.c -void *debug_cpu_thread (void *arg); - #endif // _SHOEBILL_H diff --git a/core/via.c b/core/via.c index d450eab..0a67f11 100644 --- a/core/via.c +++ b/core/via.c @@ -30,11 +30,12 @@ #include #include #include +#include #include "../core/shoebill.h" char *via_reg_str[16] = { - "orb", - "ora", + "regb", + "rega", "ddrb", "ddra", "t1c-l", @@ -67,8 +68,12 @@ void via_raise_interrupt(uint8_t vianum, uint8_t ifr_bit) // Only if the bit is enabled in IER do we raise a cpu interrupt if (via->ier & (1 << ifr_bit)) set_pending_interrupt(vianum); - //else - // printf("didn't set pending interrupt\n"); + + // if the CPU was stopped, wake it up + if (shoe.cpu_thread_notifications & SHOEBILL_STATE_STOPPED) { + if (!shoe.config_copy.debug_mode) + pthread_kill(shoe.cpu_thread_pid, SIGUSR2); + } } @@ -97,7 +102,6 @@ void process_pending_interrupt () return ; } - // If the CPU was stopped, unstop it shoe.cpu_thread_notifications &= ~~SHOEBILL_STATE_STOPPED; const uint16_t vector_offset = (priority + 24) * 4; @@ -157,34 +161,6 @@ void process_pending_interrupt () shoe.cpu_thread_notifications &= ~~(1 << priority); } -/* - Reset: - Host sends command, switch to state 0 - Device sends byte 1 - Host switches to state 2 - Device sends byte 2 - Host switches to state 3 - - Talk: - Host sends command, switch to state 0 - Device sends byte 0 (even) - Hosts switches to state 1 (even = "just got even byte") - Device sends byte 1 (odd) - Host switches to state 2 (odd = "just got odd byte") - Device sends byte 2 (even) - - - */ - - - -// via1 ORB bits abcd efgh -// cd -> adb FSM state -// e -> adb timeout occurred / service request (?) -// f/g/h nvram stuff - -#define VIA_REGB_DONE 8 - // VIA registers #define VIA_ORB 0 @@ -253,11 +229,8 @@ static void handle_pram_read_byte (void) pram->mode = PRAM_READ; // stay in read-mode pram->data[addr] = pram->command[2]; - FILE *f = fopen("pram.dump", "w"); - if (f) { - fwrite(pram->data, 256, 1, f); - fclose(f); - } + if (pram->callback) + pram->callback(pram->callback_param, addr, pram->command[2]); printf("PRAMPRAM: setting pram addr 0x%02x = 0x%02x\n", addr, pram->command[2]); @@ -435,7 +408,18 @@ done: pram->last_bits = (shoe.via[0].regb_output & shoe.via[0].ddrb & 6); } -void init_via_state (void) +void reset_via_state (void) +{ + uint8_t pram_data[256]; + shoebill_pram_callback_t callback = shoe.pram.callback; + void *callback_param = shoe.pram.callback_param; + + memcpy(pram_data, shoe.pram.data, 256); + + init_via_state(pram_data, callback, callback_param); +} + +void init_via_state (uint8_t pram_data[256], shoebill_pram_callback_t callback, void *callback_param) { /* -- Zero everything -- */ @@ -456,7 +440,7 @@ void init_via_state (void) * Bit 3 - output - vSync * Bit 2-0 unused */ - shoe.via[0].ddra = 0b00111000; + shoe.via[0].ddra = ~b(00111000); /* VIA 1 reg B * Bit 7 - output - vSndEnb @@ -468,7 +452,7 @@ void init_via_state (void) * Bit 1 - output - rtcClk * Bit 0 - in/out - rtcData (initialize to output) */ - shoe.via[0].ddrb = 0b10110111; // A/UX apparently neglects to initialize ddra/b + shoe.via[0].ddrb = ~b(10110111); // A/UX apparently neglects to initialize ddra/b /* -- Initialize VIA2 -- */ @@ -480,7 +464,7 @@ void init_via_state (void) * Bit 0 - Interrupt for slot 9 */ shoe.via[1].ddra = 0x00; // via2/rega consists of input pins for nubus interrupts - shoe.via[1].rega_input = 0b00111111; // no nubus interrupts currently asserted + shoe.via[1].rega_input = ~b(00111111); // no nubus interrupts currently asserted /* VIA 2 reg B * Bit 7 - output - v2VBL @@ -492,9 +476,9 @@ void init_via_state (void) * Bit 1 - output - v2BusLk * Bit 0 - output - v2cdis */ - shoe.via[1].ddrb = 0b10001111; + shoe.via[1].ddrb = ~b(10001111); // FIXME: apparently via2/regb bit 7 is tied to VIA1, and driven by timer T1, to - // generate 60.15hz interrupts on VIA1 + // generate 60.15hz (really 60.0hz) interrupts on VIA1 // emulate this more accurately! /* -- Initialize PRAM -- */ @@ -502,11 +486,9 @@ void init_via_state (void) pram_state_t *pram = &shoe.pram; pram->mode = PRAM_READ; - FILE *f = fopen("pram.dump", "r"); - if (f) { - fread(pram->data, 256, 1, f); - fclose(f); - } + memcpy(pram->data, pram_data, 256); + pram->callback = callback; + pram->callback_param = callback_param; } #define E_CLOCK 783360 @@ -641,15 +623,6 @@ static void via_write_reg(const uint8_t vianum, const uint8_t reg, const uint8_t case VIA_ORB: { - // The OS should only be able to "set" the bits that are marked as "output" in ddra/b - - // FIXME: we need separate ORA/ORB and IRA/IRB registers - - /*const uint8_t ddr_mask = via->ddrb; - const uint8_t data_sans_input = data & ddr_mask; - const uint8_t reg_sans_output = via->regb & (~~ddr_mask); - via->regb = data_sans_input | reg_sans_output; - // via->regb = data;*/ via->regb_output = data; if (vianum == 1) { @@ -669,15 +642,6 @@ static void via_write_reg(const uint8_t vianum, const uint8_t reg, const uint8_t case VIA_ORA_AUX: case VIA_ORA: { - // The OS should only be able to "set" the bits that are marked as "output" in ddra/b - - // FIXME: we need separate ORA/ORB and IRA/IRB registers - - /*const uint8_t ddr_mask = via->ddra; - const uint8_t data_sans_input = data & ddr_mask; - const uint8_t reg_sans_output = via->rega & (~~ddr_mask); - via->rega = data_sans_input | reg_sans_output; - // via->rega = data;*/ via->rega_output = data; @@ -705,6 +669,7 @@ static void via_write_reg(const uint8_t vianum, const uint8_t reg, const uint8_t case VIA_T2C_HI: via->ifr &= ~~VIA_IFR_T2; // Write to T2C_HI clears TIMER 2 interrupt + via->t2_last_set = now; break; case VIA_T1C_LO: @@ -792,11 +757,17 @@ void *via_clock_thread(void *arg) long double earliest_next_timer = 1.0; - // Check whether the 60.15hz timer should fire (via1 CA1) - const uint64_t expected_ca1_ticks = ((now - start_time) * 60.15L); + /* + * Check whether the 60hz timer should fire (via1 CA1) + * + * Note! Inside Macintosh claims this should be 60.15hz, + * but every version of A/UX configures the timer to be + * exactly 60.0hz + */ + const uint64_t expected_ca1_ticks = ((now - start_time) * 60.0L); if (expected_ca1_ticks > ca1_ticks) { // Figure out when the timer should fire next - const long double next_firing = (1.0L/60.15L) - fmodl(now - start_time, 1.0L/60.15L); + const long double next_firing = (1.0L/60.0L) - fmodl(now - start_time, 1.0L/60.0L); fire(next_firing); ca1_ticks = expected_ca1_ticks; @@ -815,12 +786,13 @@ void *via_clock_thread(void *arg) via_raise_interrupt(1, IFR_CA2); - via_raise_interrupt(1, IFR_TIMER1); + /*via_raise_interrupt(1, IFR_TIMER1); via_raise_interrupt(1, IFR_TIMER2); via_raise_interrupt(2, IFR_TIMER1); - via_raise_interrupt(2, IFR_TIMER2); + via_raise_interrupt(2, IFR_TIMER2);*/ } + /* // Check if any nubus cards have interrupt timers shoe.via[1].rega_input = 0b00111111; for (i=9; i<15; i++) { @@ -841,10 +813,15 @@ void *via_clock_thread(void *arg) } } - + */ usleep((useconds_t)(earliest_next_timer * 1000000.0L)); + + if (shoe.via_thread_notifications & SHOEBILL_STATE_RETURN) { + pthread_mutex_unlock(&shoe.via_clock_thread_lock); + return NULL; + } } } diff --git a/core/video.c b/core/video.c index 692a470..8ef7a83 100644 --- a/core/video.c +++ b/core/video.c @@ -27,7 +27,6 @@ #include #include #include "shoebill.h" -#include "core_api.h" #include "video_rom/rom.c" diff --git a/debugger/debugger b/debugger/debugger deleted file mode 100755 index 339b16db06d66b694c6f70a016fc648ab78cbf00..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 368068 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 z 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ð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 #include #include -#include "../core/core_api.h" #include "../core/shoebill.h" -#include "../core/coff.h" rb_tree *keymap; @@ -44,10 +42,12 @@ struct dbg_state_t { uint64_t breakpoint_counter; dbg_breakpoint_t *breakpoints; _Bool trace; + }; struct dbg_state_t dbg_state; + void print_mmu_rp(uint64_t rp) { printf("lu=%u limit=0x%x sg=%u dt=%u addr=0x%08x\n", rp_lu(rp), rp_limit(rp), rp_sg(rp), rp_dt(rp), rp_addr(rp)); @@ -675,6 +675,8 @@ void _display_func (void) _do_clut_translation(video); + shoebill_send_vbl_interrupt(9); + glDrawBuffer(GL_BACK); glClear(GL_COLOR_BUFFER_BIT); @@ -706,7 +708,7 @@ static void _init_keyboard_map (void) #define mapkey(_u, a) mapkeymod(_u, a, 0) - keymap = rb_new(); + keymap = rb_new(p_new_pool(NULL)); // Letters mapkey('a', 0x00); @@ -863,11 +865,11 @@ static void _init_glut_video (void) int main (int argc, char **argv) { - shoebill_control_t control; + shoebill_config_t config; pthread_t pid; - bzero(&control, sizeof(shoebill_control_t)); + bzero(&config, sizeof(shoebill_config_t)); /* * A variety of hacky things happen in debug mode. @@ -878,23 +880,23 @@ int main (int argc, char **argv) * This is not a great example of how to write a GUI * for shoebill... */ - control.debug_mode = 1; + config.debug_mode = 1; - control.aux_verbose = 1; - control.ram_size = 16 * 1024 * 1024; - control.aux_kernel_path = "/unix"; - control.rom_path = "/Users/pruten/checkouts/shoebill/priv/macii.rom"; + config.aux_verbose = 1; + config.ram_size = 16 * 1024 * 1024; + config.aux_kernel_path = "/unix"; + config.rom_path = "../priv/macii.rom"; - control.scsi_devices[0].path = "/Users/pruten/checkouts/shoebill/priv/Apple_UNIX_3.iso"; + config.scsi_devices[0].path = "../priv/Apple_UNIX_3.iso"; - if (!shoebill_initialize(&control)) { - printf("%s\n", control.error_msg); + if (!shoebill_initialize(&config)) { + printf("%s\n", config.error_msg); return 0; } _init_keyboard_map(); - shoebill_install_video_card(&control, + shoebill_install_video_card(&config, 9, // slotnum 1024, 768, diff --git a/gui/Shoebill.xcodeproj/project.pbxproj b/gui/Shoebill.xcodeproj/project.pbxproj index f442d1e..a649098 100644 --- a/gui/Shoebill.xcodeproj/project.pbxproj +++ b/gui/Shoebill.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 870A2F9E192D2A6D00ABBC14 /* shoeScreenWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 870A2F9D192D2A6D00ABBC14 /* shoeScreenWindowController.m */; }; 87495445189980E200E80F5B /* shoeScreenView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 87495444189980E200E80F5B /* shoeScreenView.xib */; }; 8749544718999F5300E80F5B /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8749544618999F5300E80F5B /* OpenGL.framework */; }; 874954491899A22D00E80F5B /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 874954481899A22D00E80F5B /* QuartzCore.framework */; }; @@ -26,6 +27,8 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 870A2F9C192D2A6D00ABBC14 /* shoeScreenWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = shoeScreenWindowController.h; sourceTree = ""; }; + 870A2F9D192D2A6D00ABBC14 /* shoeScreenWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = shoeScreenWindowController.m; sourceTree = ""; }; 87495444189980E200E80F5B /* shoeScreenView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = shoeScreenView.xib; sourceTree = ""; }; 8749544618999F5300E80F5B /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = /System/Library/Frameworks/OpenGL.framework; sourceTree = ""; }; 874954481899A22D00E80F5B /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = /System/Library/Frameworks/QuartzCore.framework; sourceTree = ""; }; @@ -70,6 +73,20 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 870A2F9F192D2ADA00ABBC14 /* Screen */ = { + isa = PBXGroup; + children = ( + 870A2F9C192D2A6D00ABBC14 /* shoeScreenWindowController.h */, + 870A2F9D192D2A6D00ABBC14 /* shoeScreenWindowController.m */, + 87495444189980E200E80F5B /* shoeScreenView.xib */, + AE75B30718A8210D00E66DB6 /* shoeScreenWindow.h */, + AE75B30818A8210D00E66DB6 /* shoeScreenWindow.m */, + 87F9775F18987767000D589E /* shoeScreenView.h */, + 87F9776018987767000D589E /* shoeScreenView.m */, + ); + name = Screen; + sourceTree = ""; + }; 87F9771F18987700000D589E = { isa = PBXGroup; children = ( @@ -117,14 +134,10 @@ 8782FCE3189AFEFB0081E19E /* shoeApplication.m */, 87F9773D18987700000D589E /* shoeAppDelegate.h */, 87F9773E18987700000D589E /* shoeAppDelegate.m */, - 87495444189980E200E80F5B /* shoeScreenView.xib */, - AE75B30718A8210D00E66DB6 /* shoeScreenWindow.h */, - AE75B30818A8210D00E66DB6 /* shoeScreenWindow.m */, - 87F9775F18987767000D589E /* shoeScreenView.h */, - 87F9776018987767000D589E /* shoeScreenView.m */, 8781D24B18A19C340016F604 /* shoePreferencesWindowController.xib */, 8781D24918A19C340016F604 /* shoePreferencesWindowController.h */, 8781D24A18A19C340016F604 /* shoePreferencesWindowController.m */, + 870A2F9F192D2ADA00ABBC14 /* Screen */, 87F9773218987700000D589E /* Supporting Files */, ); path = Shoebill; @@ -219,6 +232,7 @@ 8781D24C18A19C340016F604 /* shoePreferencesWindowController.m in Sources */, AE75B30918A8210D00E66DB6 /* shoeScreenWindow.m in Sources */, 8782FCE4189AFEFB0081E19E /* shoeApplication.m in Sources */, + 870A2F9E192D2A6D00ABBC14 /* shoeScreenWindowController.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/gui/Shoebill/Base.lproj/MainMenu.xib b/gui/Shoebill/Base.lproj/MainMenu.xib index ea724df..be5cf61 100644 --- a/gui/Shoebill/Base.lproj/MainMenu.xib +++ b/gui/Shoebill/Base.lproj/MainMenu.xib @@ -1,13 +1,14 @@ - + - + + @@ -185,4 +186,4 @@ - \ No newline at end of file + diff --git a/gui/Shoebill/Shoebill-Info.plist b/gui/Shoebill/Shoebill-Info.plist index 0698ae1..dedea78 100644 --- a/gui/Shoebill/Shoebill-Info.plist +++ b/gui/Shoebill/Shoebill-Info.plist @@ -2,12 +2,12 @@ + CFBundleGetInfoString + Copyright © 2013-2014 Peter Rutenbar CFBundleDevelopmentRegion en CFBundleExecutable ${EXECUTABLE_NAME} - CFBundleIconFile - CFBundleIdentifier org.shoebill.${PRODUCT_NAME:rfc1034identifier} CFBundleInfoDictionaryVersion diff --git a/gui/Shoebill/shoeAppDelegate.m b/gui/Shoebill/shoeAppDelegate.m index 7b45c79..602a1f2 100644 --- a/gui/Shoebill/shoeAppDelegate.m +++ b/gui/Shoebill/shoeAppDelegate.m @@ -24,7 +24,7 @@ */ #import "shoeAppDelegate.h" - +#import "shoeApplication.h" @implementation shoeAppDelegate @@ -33,6 +33,7 @@ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; uint32_t i; + [((shoeApplication*)NSApp) zapPram:defaults ptr:nil]; [defaults setObject:@"/unix" forKey:@"rootKernelPath"]; [defaults setObject:@"" forKey:@"romPath"]; [defaults setInteger:NSOnState forKey:@"verboseState"]; @@ -59,6 +60,10 @@ // Going from 0.0.1 to 0.0.2 leaves rootKernelPath uninitialized if ([defaults objectForKey:@"rootKernelPath"] == nil) [defaults setObject:@"/unix" forKey:@"rootKernelPath"]; + + // 0.0.1-2 leave pramData uninitialized + if ([defaults objectForKey:@"pramData"] == nil) + [((shoeApplication*)NSApp) zapPram:defaults ptr:nil]; } diff --git a/gui/Shoebill/shoeApplication.h b/gui/Shoebill/shoeApplication.h index 9571196..528f42e 100644 --- a/gui/Shoebill/shoeApplication.h +++ b/gui/Shoebill/shoeApplication.h @@ -24,19 +24,31 @@ */ #import -#include "../../core/core_api.h" -#include "../../core/redblack.h" +#import "shoeScreenWindowController.h" +#include "../../core/shoebill.h" + +struct shoe_app_pram_data_t +{ + uint8_t pram[256]; + volatile _Bool updated; +}; @interface shoeApplication : NSApplication { rb_tree *keymap; - NSWindowController *windowController[16]; + shoeScreenWindowController *windowController[16]; + IBOutlet __weak NSMenuItem *run_stop_menu_item; + + NSTimer *pram_flush_timer; + struct shoe_app_pram_data_t *pram; @public + BOOL doCaptureMouse, doCaptureKeys; BOOL isRunning; - shoebill_control_t control; + shoebill_config_t config; } -- (void) startEmulator; +- (void) startEmulator; +- (void) zapPram:(NSUserDefaults*)defaults ptr:(uint8_t*)ptr; @end diff --git a/gui/Shoebill/shoeApplication.m b/gui/Shoebill/shoeApplication.m index 48df1d4..2787b9a 100644 --- a/gui/Shoebill/shoeApplication.m +++ b/gui/Shoebill/shoeApplication.m @@ -25,6 +25,8 @@ #import "shoeApplication.h" #import "shoeScreenWindow.h" +#import "shoeScreenWindowController.h" +#include @implementation shoeApplication @@ -38,7 +40,7 @@ - (void)initKeyboardMap { - keymap = rb_new(); + keymap = rb_new(p_new_pool(NULL)); // Letters mapkey('a', 0x00); @@ -223,6 +225,7 @@ NSString *romPathStr = [defaults objectForKey:@"romPath"]; NSInteger verboseState = [defaults integerForKey:@"verboseState"]; NSInteger memsize = [defaults integerForKey:@"memorySize"]; + NSData *pramData = [defaults objectForKey:@"pramData"]; if (rootKernelPathStr == Nil || [rootKernelPathStr length]==0) { [self complain:@"Kernel path invalid!"]; @@ -250,22 +253,30 @@ for (i=0; i<7; i++) { NSString *str = [defaults objectForKey:[NSString stringWithFormat:@"scsiPath%u", i]]; if (str == nil || [str length] == 0) - control.scsi_devices[i].path = NULL; + config.scsi_devices[i].path = NULL; else - control.scsi_devices[i].path = strdup([str UTF8String]); + config.scsi_devices[i].path = strdup([str UTF8String]); } char *rootKernelPathCString = strdup([rootKernelPathStr UTF8String]); char *romPathCString = strdup([romPathStr UTF8String]); - // FIXME: I'm leaking these strings. Stop leaking stuff when the UI is more finalized + config.aux_verbose = (verboseState == NSOnState); + config.ram_size = (uint32_t)memsize * 1024 * 1024; + config.aux_kernel_path = rootKernelPathCString; + config.rom_path = romPathCString; + config.debug_mode = 0; - control.aux_verbose = (verboseState == NSOnState); - control.ram_size = (uint32_t)memsize * 1024 * 1024; - control.aux_kernel_path = rootKernelPathCString; - control.rom_path = romPathCString; - control.debug_mode = 0; + [pramData getBytes:config.pram length:256]; + + /* + * If the pram is corrupt, zap it. + * A/UX will apparently never zap corrupted pram, + * probably because it expects the bootloader/MacOS to do it. + */ + if (memcmp(config.pram+0xc, "NuMc", 4) != 0) + [self zapPram:defaults ptr:config.pram]; *width = screenWidthValue; *height = screenHeightValue; @@ -278,15 +289,67 @@ width:(uint16_t)width refresh_freq:(double)refresh_freq { - shoebill_install_video_card(&control, + shoebill_install_video_card(&config, slotnum, width, height, refresh_freq); - windowController[slotnum] = [[NSWindowController alloc] initWithWindowNibName:@"shoeScreenView"]; - shoeScreenWindow *win = (shoeScreenWindow*)windowController[slotnum].window; - [win configure:slotnum]; + windowController[slotnum] = [[shoeScreenWindowController alloc] + initWithWindowNibName:@"shoeScreenView" + slotnum:slotnum]; +} + +- (void) zapPram:(NSUserDefaults*)defaults ptr:(uint8_t*)ptr +{ + uint8_t init[256]; + + memset(init, 0, 256); + + /* Mark PRAM as "valid" */ + memcpy(init+0xc, "NuMc", 4); + /* + * Set text box I-beam blink speed and mouse acceleration + * to something reasonable + */ + init[9] = 0x88; + + if (ptr) + memcpy(ptr, init, 256); + + NSData *data = [NSData dataWithBytes:init length:256]; + [defaults setObject:data forKey:@"pramData"]; + [defaults synchronize]; + + assert("zapPram" && (memcmp(init+0xc, "NuMc", 4) == 0)); +} + +void pram_callback (void *param, const uint8_t addr, const uint8_t byte) +{ + struct shoe_app_pram_data_t *pram = (struct shoe_app_pram_data_t*)param; + pram->pram[addr] = byte; + pram->updated = 1; + //printf("pram_callback: set pram[0x%x] = 0x%02x (%c)\n", addr, byte, isprint(byte)?byte:'.'); +} + +- (void) flushPram +{ + uint8_t copy[256]; + if (pram->updated) { + pram->updated = 0; + memcpy(copy, pram->pram, 256); + + NSData* data = [NSData dataWithBytes:copy length:256]; + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + + [defaults setObject:data forKey:@"pramData"]; + [defaults synchronize]; + } +} + +- (void) pramFlushTimerFired:(NSTimer *)timer +{ + [self flushPram]; } - (void) startEmulator @@ -297,38 +360,81 @@ uint16_t width, height; uint32_t i; - bzero(&control, sizeof(shoebill_control_t)); + bzero(&config, sizeof(shoebill_config_t)); [self fetchUserDefaults:&height width:&width]; - uint32_t result = shoebill_initialize(&control); + self->pram = calloc(1, sizeof(struct shoe_app_pram_data_t)); + memcpy(self->pram, config.pram, 256); + pram_flush_timer = [NSTimer + scheduledTimerWithTimeInterval:1.0 + target:self + selector:@selector(pramFlushTimerFired:) + userInfo:nil + repeats:YES]; + config.pram_callback = pram_callback; + config.pram_callback_param = (void*)self->pram; + + uint32_t result = shoebill_initialize(&config); if (!result) { - [self complain:[NSString stringWithFormat:@"%s", control.error_msg]]; + [self complain:[NSString stringWithFormat:@"%s", config.error_msg]]; return ; } [self createScreenWindow:9 height:height width:width refresh_freq:200.0/3.0]; - /*[self createScreenWindow:10 height:height width:width refresh_freq:200.0/3.0]; - [self createScreenWindow:11 height:height width:width refresh_freq:200.0/3.0]; - [self createScreenWindow:12 height:height width:width refresh_freq:200.0/3.0]; - [self createScreenWindow:13 height:height width:width refresh_freq:200.0/3.0]; - [self createScreenWindow:14 height:height width:width refresh_freq:200.0/3.0];*/ shoebill_start(); + isRunning = true; - for (i=0; i<16; i++) + for (i=0; i<16; i++) { if (windowController[i]) { shoeScreenWindow *win = (shoeScreenWindow*)[windowController[i] window]; [win reevaluateKeyWindowness]; } + } + + [run_stop_menu_item setTitle: @"Stop"]; + [run_stop_menu_item setKeyEquivalent:@""]; +} +- (void) stopEmulator +{ + uint32_t i; + + for (i=0; i<16; i++) { + if (windowController[i]) { + [windowController[i] close]; + windowController[i] = NULL; + } + } + doCaptureKeys = false; + doCaptureMouse = false; + isRunning = false; + + shoebill_stop(); + + [pram_flush_timer invalidate]; + pram_flush_timer = nil; + [self flushPram]; + free(self->pram); + + if (config.aux_kernel_path) + free((void*)config.aux_kernel_path); + if (config.rom_path) + free((void*)config.rom_path); + + [run_stop_menu_item setTitle: @"Run"]; + [run_stop_menu_item setKeyEquivalent:@"r"]; } - (IBAction)runMenuItem:(id)sender { - [self startEmulator]; + if (isRunning) + [self stopEmulator]; + else + [self startEmulator]; } diff --git a/gui/Shoebill/shoePreferencesWindowController.m b/gui/Shoebill/shoePreferencesWindowController.m index 993a789..65c31de 100644 --- a/gui/Shoebill/shoePreferencesWindowController.m +++ b/gui/Shoebill/shoePreferencesWindowController.m @@ -186,4 +186,11 @@ [[self window] close]; } +-(IBAction)zapPramPressed:(id)sender +{ + shoeApplication *shoeApp = (shoeApplication*) NSApp; + [shoeApp zapPram:[NSUserDefaults standardUserDefaults] ptr:nil]; +} + + @end diff --git a/gui/Shoebill/shoePreferencesWindowController.xib b/gui/Shoebill/shoePreferencesWindowController.xib index 97d6b7f..f4fbc7d 100644 --- a/gui/Shoebill/shoePreferencesWindowController.xib +++ b/gui/Shoebill/shoePreferencesWindowController.xib @@ -1,8 +1,8 @@ - + - + @@ -40,7 +40,7 @@ - + @@ -118,7 +118,7 @@ - + @@ -127,7 +127,7 @@ - + @@ -136,6 +136,17 @@ + @@ -351,7 +362,7 @@ - + diff --git a/gui/Shoebill/shoeScreenView.h b/gui/Shoebill/shoeScreenView.h index 469fdd9..20ac28f 100644 --- a/gui/Shoebill/shoeScreenView.h +++ b/gui/Shoebill/shoeScreenView.h @@ -31,7 +31,6 @@ @interface shoeScreenView : NSOpenGLView { CGColorSpaceRef colorspace; - shoebill_control_t *control; NSTimer *timer; NSRecursiveLock *lock; CIContext *ciContext; diff --git a/gui/Shoebill/shoeScreenView.m b/gui/Shoebill/shoeScreenView.m index fcc93b9..782602e 100644 --- a/gui/Shoebill/shoeScreenView.m +++ b/gui/Shoebill/shoeScreenView.m @@ -35,7 +35,6 @@ - (void)initCommon { shoeApp = (shoeApplication*) NSApp; - control = &shoeApp->control; } @@ -73,7 +72,7 @@ colorspace = CGColorSpaceCreateDeviceRGB(); timer = [NSTimer - scheduledTimerWithTimeInterval:0.001 + scheduledTimerWithTimeInterval:(1.0/60.0) target:self selector:@selector(timerFireMethod:) userInfo:nil @@ -83,15 +82,6 @@ [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSEventTrackingRunLoopMode]; - - - shoebill_card_video_t *video = &control->slots[10].card.video; - NSSize size = { - .height=video->height, - .width=video->width - }; - - [[self window] setContentSize:size]; [[self window] setTitle:[NSString stringWithFormat:@"Shoebill - Screen 1"]]; [[self window] makeKeyAndOrderFront:nil]; } @@ -103,83 +93,35 @@ - (void)prepareOpenGL { - NSRect frame = [self frame]; - NSRect bounds = [self bounds]; - GLfloat minX, minY, maxX, maxY; - - minX = NSMinX(bounds); - minY = NSMinY(bounds); - maxX = NSMaxX(bounds); - maxY = NSMaxY(bounds); - GLint swapInt = 1; [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; - - if(NSIsEmptyRect([self visibleRect])) - { - glViewport(0, 0, 1, 1); - } else { - glViewport(0, 0, frame.size.width ,frame.size.height); - } - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(minX, maxX, minY, maxY, -1.0, 1.0); -} - -static void _do_clut_translation(shoebill_card_video_t *ctx) -{ - uint32_t i; - - switch (ctx->depth) { - case 1: { - for (i=0; i < ctx->pixels/8; i++) { - const uint8_t byte = ctx->indexed_buf[i]; - ctx->direct_buf[i * 8 + 0] = ctx->clut[(byte >> 7) & 1]; - ctx->direct_buf[i * 8 + 1] = ctx->clut[(byte >> 6) & 1]; - ctx->direct_buf[i * 8 + 2] = ctx->clut[(byte >> 5) & 1]; - ctx->direct_buf[i * 8 + 3] = ctx->clut[(byte >> 4) & 1]; - ctx->direct_buf[i * 8 + 4] = ctx->clut[(byte >> 3) & 1]; - ctx->direct_buf[i * 8 + 5] = ctx->clut[(byte >> 2) & 1]; - ctx->direct_buf[i * 8 + 6] = ctx->clut[(byte >> 1) & 1]; - ctx->direct_buf[i * 8 + 7] = ctx->clut[(byte >> 0) & 1]; - } - break; - } - case 2: { - for (i=0; i < ctx->pixels/4; i++) { - const uint8_t byte = ctx->indexed_buf[i]; - ctx->direct_buf[i * 4 + 0] = ctx->clut[(byte >> 6) & 3]; - ctx->direct_buf[i * 4 + 1] = ctx->clut[(byte >> 4) & 3]; - ctx->direct_buf[i * 4 + 2] = ctx->clut[(byte >> 2) & 3]; - ctx->direct_buf[i * 4 + 3] = ctx->clut[(byte >> 0) & 3]; - } - break; - } - case 4: { - for (i=0; i < ctx->pixels/2; i++) { - const uint8_t byte = ctx->indexed_buf[i]; - ctx->direct_buf[i * 2 + 0] = ctx->clut[(byte >> 4) & 0xf]; - ctx->direct_buf[i * 2 + 1] = ctx->clut[(byte >> 0) & 0xf]; - } - break; - } - case 8: - for (i=0; i < ctx->pixels; i++) - ctx->direct_buf[i] = ctx->clut[ctx->indexed_buf[i]]; - break; - - default: - assert(!"unknown depth"); - } } - (void)drawRect:(NSRect)rect { + const uint8_t slotnum = ((shoeScreenWindowController*)[[self window] windowController])->slotnum; + [[self openGLContext] makeCurrentContext]; + NSRect frame = [self frame]; + NSRect bounds = [self bounds]; + + GLfloat minX = NSMinX(bounds); + GLfloat minY = NSMinY(bounds); + GLfloat maxX = NSMaxX(bounds); + GLfloat maxY = NSMaxY(bounds); + + if(NSIsEmptyRect([self visibleRect])) + glViewport(0, 0, 1, 1); + else + glViewport(0, 0, frame.size.width ,frame.size.height); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(minX, maxX, minY, maxY, -1.0, 1.0); glDrawBuffer(GL_BACK); glClear(GL_COLOR_BUFFER_BIT); @@ -187,32 +129,29 @@ static void _do_clut_translation(shoebill_card_video_t *ctx) glClearColor(0.0, 0.0, 0.0, 0.0); if (shoeApp->isRunning) { - uint8_t slotnum = ((shoeScreenWindow*)[self window])->slotnum; - shoebill_card_video_t *video = &control->slots[slotnum].card.video; + shoebill_video_frame_info_t frame = shoebill_get_video_frame(slotnum, 0); - /*NSSize size = { - .height=video->height, - .width=video->width - }; - [self setFrameSize:size]; - [[self window] setContentSize:size];*/ - - _do_clut_translation(video); - glViewport(0, 0, video->width, video->height); - glRasterPos2i(0, video->height); + glViewport(0, 0, frame.width, frame.height); + glRasterPos2i(0, frame.height); glPixelStorei(GL_UNPACK_LSB_FIRST, GL_TRUE); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glPixelZoom(1.0, -1.0); - glDrawPixels(video->width, - video->height, + glDrawPixels(frame.width, + frame.height, GL_RGBA, GL_UNSIGNED_BYTE, - video->direct_buf); + frame.buf); + + [[self openGLContext] flushBuffer]; + + shoebill_send_vbl_interrupt(slotnum); + } + else { + [[self openGLContext] flushBuffer]; } - [[self openGLContext] flushBuffer]; } - (void)viewDidMoveToWindow @@ -247,18 +186,6 @@ static void _do_clut_translation(shoebill_card_video_t *ctx) [self mouseMoved:theEvent]; } -/*- (void) say:(NSString*)str -{ - NSAlert *theAlert = [NSAlert - alertWithMessageText:nil - defaultButton:nil - alternateButton:nil - otherButton:nil - informativeTextWithFormat:@"%@", str - ]; - [theAlert runModal]; -}*/ - - (void)mouseDown:(NSEvent *)theEvent { diff --git a/gui/Shoebill/shoeScreenView.xib b/gui/Shoebill/shoeScreenView.xib index 6d86a58..041bca9 100644 --- a/gui/Shoebill/shoeScreenView.xib +++ b/gui/Shoebill/shoeScreenView.xib @@ -1,19 +1,20 @@ - + - + - + - + + @@ -23,4 +24,4 @@ - \ No newline at end of file + diff --git a/gui/Shoebill/shoeScreenWindow.h b/gui/Shoebill/shoeScreenWindow.h index 971f900..6a68d72 100644 --- a/gui/Shoebill/shoeScreenWindow.h +++ b/gui/Shoebill/shoeScreenWindow.h @@ -28,10 +28,8 @@ @interface shoeScreenWindow : NSWindow { @public - uint8_t slotnum; } -- (void) configure:(uint8_t) _slotnum; - (void) reevaluateKeyWindowness; - (void) warpToCenter; - (void) captureMouse; diff --git a/gui/Shoebill/shoeScreenWindow.m b/gui/Shoebill/shoeScreenWindow.m index 2f127ae..df35c78 100644 --- a/gui/Shoebill/shoeScreenWindow.m +++ b/gui/Shoebill/shoeScreenWindow.m @@ -29,20 +29,6 @@ @implementation shoeScreenWindow -- (void)configure:(uint8_t) _slotnum -{ - slotnum = _slotnum; - - shoeApplication *shoeApp = (shoeApplication*) NSApp; - shoebill_control_t *control = &shoeApp->control; - shoebill_card_video_t *video = &control->slots[slotnum].card.video; - NSSize size = { - .height=video->height, - .width=video->width - }; - - [self setContentSize:size]; -} // Called after all the shoeScreenWindows are created and configured, // because one of them was already made key while isRunning==NO, @@ -50,7 +36,7 @@ - (void)reevaluateKeyWindowness { shoeApplication *shoeApp = (shoeApplication*)NSApp; - + assert(shoeApp->isRunning); if ([self isKeyWindow]) { @@ -62,6 +48,19 @@ } } +- (void)toggleFullScreen:(id)sender +{ + [super toggleFullScreen:sender]; + + const uint8_t slotnum = ((shoeScreenWindowController*)[self windowController])->slotnum; + shoebill_video_frame_info_t frame = shoebill_get_video_frame(slotnum, 1); + NSSize size = { + .height=frame.height, + .width=frame.width, + }; + [self setContentSize:size]; +} + - (void)becomeKeyWindow { shoeApplication *shoeApp = (shoeApplication*)NSApp; diff --git a/core/redblack.h b/gui/Shoebill/shoeScreenWindowController.h similarity index 66% rename from core/redblack.h rename to gui/Shoebill/shoeScreenWindowController.h index d91e520..a056c40 100644 --- a/core/redblack.h +++ b/gui/Shoebill/shoeScreenWindowController.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Peter Rutenbar + * Copyright (c) 2014, Peter Rutenbar * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,28 +23,13 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef _REDBLACK_H -#define _REDBLACK_H +#import -typedef uint32_t rb_key_t; -typedef void* rb_value_t; +@interface shoeScreenWindowController : NSWindowController { +@public + uint8_t slotnum; +} -typedef struct _rb_node { - struct _rb_node *left, *right, *parent; - rb_key_t key; - rb_value_t value; - uint8_t is_red : 1; -} rb_node; +- (id)initWithWindowNibName:(NSString *)windowNibName slotnum:(uint8_t)_slotnum; -typedef rb_node* rb_tree; - - -rb_tree* rb_new(); -void rb_free (rb_tree *tree); - -uint8_t rb_insert (rb_tree *root, rb_key_t key, rb_value_t value, rb_value_t *old_value); -uint8_t rb_find (rb_tree *tree, rb_key_t key, rb_value_t *value); -uint8_t rb_index (rb_tree *tree, uint32_t index, rb_key_t *key, rb_value_t *value); -uint32_t rb_count (rb_tree *tree); - -#endif // _REDBLACK_H \ No newline at end of file +@end diff --git a/gui/Shoebill/shoeScreenWindowController.m b/gui/Shoebill/shoeScreenWindowController.m new file mode 100644 index 0000000..97c1aba --- /dev/null +++ b/gui/Shoebill/shoeScreenWindowController.m @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2014, Peter Rutenbar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "shoeScreenWindowController.h" +#import "shoeApplication.h" + +@interface shoeScreenWindowController () + +@end + +@implementation shoeScreenWindowController + +- (id)initWithWindowNibName:(NSString *)windowNibName + slotnum:(uint8_t)_slotnum +{ + shoeScreenWindowController *result = [super initWithWindowNibName:windowNibName]; + result->slotnum = _slotnum; + return result; +} + +- (void)windowDidLoad +{ + [super windowDidLoad]; + + shoebill_video_frame_info_t frame = shoebill_get_video_frame(slotnum, 1); + NSSize size = { + .height=frame.height, + .width=frame.width, + }; + + [[self window] setContentSize:size]; +} + +- (NSApplicationPresentationOptions)window:(NSWindow *)window + willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions +{ + + return (NSApplicationPresentationFullScreen | // support full screen for this window (required) + NSApplicationPresentationHideDock | // completely hide the dock + NSApplicationPresentationAutoHideMenuBar); // yes we want the menu bar to show/hide +} + +- (NSSize)window:(NSWindow *)window willUseFullScreenContentSize:(NSSize)proposedSize +{ + shoebill_video_frame_info_t frame = shoebill_get_video_frame(slotnum, 1); + NSSize size = { + .height=frame.height, + .width=frame.width, + }; + + return size; +} + +@end