Restart + PRAM integrated into GUI + misc

Restart/shutdown now work (most of the time)
PRAM is now integrated into the GUI
The real time clock sorta works, but is a bit wonky
Full-screen support
Lots of other little bug fixes
This commit is contained in:
Peter Rutenbar 2014-05-24 13:39:39 -04:00
parent d19c17812c
commit f4f546deb5
35 changed files with 1436 additions and 993 deletions

View File

@ -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))

View File

@ -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 ;

View File

@ -28,15 +28,44 @@
#include <stdint.h>
#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;
}

View File

@ -30,7 +30,6 @@
#include <stdint.h>
#include <assert.h>
#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; i<cf->num_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");

View File

@ -1,85 +0,0 @@
/*
* Copyright (c) 2013, Peter Rutenbar <pruten@gmail.com>
* 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 <stdint.h>
#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

View File

@ -30,19 +30,61 @@
#include <assert.h>
#include <pthread.h>
#include <unistd.h>
#include "shoebill.h"
#include "coff.h"
#include "core_api.h"
#include <signal.h>
#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);
}
}

View File

@ -1,134 +0,0 @@
/*
* Copyright (c) 2013, Peter Rutenbar <pruten@gmail.com>
* 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 <stdint.h>
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

View File

@ -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) {

View File

@ -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

View File

@ -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));

View File

@ -26,7 +26,8 @@
#include <stdio.h>
#include <assert.h>
#include "shoebill.h"
#include <string.h>
#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));
}

View File

@ -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);

View File

@ -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;

View File

@ -27,23 +27,27 @@
#include <stdlib.h>
#include <assert.h>
#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);
}

View File

@ -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);
}

View File

@ -31,12 +31,100 @@
#include <stdint.h>
#include <sys/time.h>
#include <pthread.h>
//#include <histedit.h>
// 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

View File

@ -30,11 +30,12 @@
#include <math.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#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;
}
}
}

View File

@ -27,7 +27,6 @@
#include <stdlib.h>
#include <string.h>
#include "shoebill.h"
#include "core_api.h"
#include "video_rom/rom.c"

Binary file not shown.

View File

@ -32,9 +32,7 @@
#include <unistd.h>
#include <errno.h>
#include <histedit.h>
#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,

View File

@ -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 = "<group>"; };
870A2F9D192D2A6D00ABBC14 /* shoeScreenWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = shoeScreenWindowController.m; sourceTree = "<group>"; };
87495444189980E200E80F5B /* shoeScreenView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = shoeScreenView.xib; sourceTree = "<group>"; };
8749544618999F5300E80F5B /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = /System/Library/Frameworks/OpenGL.framework; sourceTree = "<absolute>"; };
874954481899A22D00E80F5B /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = /System/Library/Frameworks/QuartzCore.framework; sourceTree = "<absolute>"; };
@ -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 = "<group>";
};
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;
};

View File

@ -1,13 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="4514" systemVersion="13B42" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="5056" systemVersion="13C1021" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment defaultVersion="1080" identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="4514"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="5056"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="shoeApplication">
<connections>
<outlet property="delegate" destination="494" id="495"/>
<outlet property="run_stop_menu_item" destination="82" id="kWm-r3-myc"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
@ -185,4 +186,4 @@
<customObject id="420" customClass="NSFontManager"/>
<customObject id="VMD-IW-vcf" userLabel="Preferences" customClass="shoePreferencesWindowController"/>
</objects>
</document>
</document>

View File

@ -2,12 +2,12 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleGetInfoString</key>
<string>Copyright © 2013-2014 Peter Rutenbar</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>org.shoebill.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>

View File

@ -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];
}

View File

@ -24,19 +24,31 @@
*/
#import <Cocoa/Cocoa.h>
#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

View File

@ -25,6 +25,8 @@
#import "shoeApplication.h"
#import "shoeScreenWindow.h"
#import "shoeScreenWindowController.h"
#include <ctype.h>
@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];
}

View File

@ -186,4 +186,11 @@
[[self window] close];
}
-(IBAction)zapPramPressed:(id)sender
{
shoeApplication *shoeApp = (shoeApplication*) NSApp;
[shoeApp zapPram:[NSUserDefaults standardUserDefaults] ptr:nil];
}
@end

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="5053" systemVersion="13C64" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="5056" systemVersion="13D65" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment defaultVersion="1080" identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="5053"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="5056"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="shoePreferencesWindowController">
@ -40,7 +40,7 @@
<font key="font" metaFont="system"/>
<tabViewItems>
<tabViewItem label="General" identifier="1" id="Ffn-1Z-2rp">
<view key="view" id="BF8-Dm-rF5">
<view key="view" ambiguous="YES" id="BF8-Dm-rF5">
<rect key="frame" x="10" y="33" width="452" height="348"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
@ -118,7 +118,7 @@
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</textFieldCell>
</textField>
<textField identifier="memorySize" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="EMf-gC-m9T">
<textField identifier="widthBox" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="EMf-gC-m9T">
<rect key="frame" x="98" y="220" width="43" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" title="width" placeholderString="width" drawsBackground="YES" id="sbq-WW-C05">
@ -127,7 +127,7 @@
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField identifier="memorySize" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Wyt-jg-xmk">
<textField identifier="heightBox" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Wyt-jg-xmk">
<rect key="frame" x="168" y="220" width="49" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" title="height" placeholderString="height" drawsBackground="YES" id="qvE-Bk-NDl">
@ -136,6 +136,17 @@
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fNZ-zo-4uj">
<rect key="frame" x="7" y="10" width="103" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Zap PRAM" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="EGV-fk-y4d">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="zapPramPressed:" target="-2" id="9N7-dY-Fl6"/>
</connections>
</button>
</subviews>
</view>
</tabViewItem>
@ -351,7 +362,7 @@
</view>
</tabViewItem>
<tabViewItem label="Booting" identifier="" id="3o8-Ma-LAk">
<view key="view" ambiguous="YES" id="Zdy-ik-svz">
<view key="view" id="Zdy-ik-svz">
<rect key="frame" x="10" y="33" width="452" height="348"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>

View File

@ -31,7 +31,6 @@
@interface shoeScreenView : NSOpenGLView {
CGColorSpaceRef colorspace;
shoebill_control_t *control;
NSTimer *timer;
NSRecursiveLock *lock;
CIContext *ciContext;

View File

@ -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
{

View File

@ -1,19 +1,20 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="4514" systemVersion="13B42" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="5056" systemVersion="13D65" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment defaultVersion="1080" identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="4514"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="5056"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSWindowController">
<customObject id="-2" userLabel="File's Owner" customClass="shoeScreenWindowController">
<connections>
<outlet property="window" destination="1" id="aqm-Nb-PaF"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="shoeApplication"/>
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" wantsToBeColor="NO" visibleAtLaunch="NO" animationBehavior="default" id="1" customClass="shoeScreenWindow">
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="1" customClass="shoeScreenWindow">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/>
<windowCollectionBehavior key="collectionBehavior" fullScreenPrimary="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="196" y="240" width="480" height="270"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="878"/>
@ -23,4 +24,4 @@
</view>
</window>
</objects>
</document>
</document>

View File

@ -28,10 +28,8 @@
@interface shoeScreenWindow : NSWindow {
@public
uint8_t slotnum;
}
- (void) configure:(uint8_t) _slotnum;
- (void) reevaluateKeyWindowness;
- (void) warpToCenter;
- (void) captureMouse;

View File

@ -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;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Rutenbar <pruten@gmail.com>
* Copyright (c) 2014, Peter Rutenbar <pruten@gmail.com>
* 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 <Cocoa/Cocoa.h>
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
@end

View File

@ -0,0 +1,76 @@
/*
* Copyright (c) 2014, Peter Rutenbar <pruten@gmail.com>
* 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