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 339b16d..0000000 Binary files a/debugger/debugger and /dev/null differ diff --git a/debugger/debugger.c b/debugger/debugger.c index bbd58f9..20ad1eb 100644 --- a/debugger/debugger.c +++ b/debugger/debugger.c @@ -32,9 +32,7 @@ #include #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