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:
parent
d19c17812c
commit
f4f546deb5
|
@ -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))
|
||||
|
|
30
core/adb.c
30
core/adb.c
|
@ -79,6 +79,32 @@
|
|||
|
||||
*/
|
||||
|
||||
void reset_adb_state()
|
||||
{
|
||||
pthread_mutex_t lock = shoe.adb.lock;
|
||||
|
||||
memset(&shoe.adb, 0, sizeof(adb_state_t));
|
||||
memset(&shoe.key, 0, sizeof(keyboard_state_t));
|
||||
memset(&shoe.mouse, 0, sizeof(mouse_state_t));
|
||||
|
||||
// Put the adb chip in state 3 (idle)
|
||||
shoe.adb.state = 3;
|
||||
|
||||
shoe.adb.lock = lock;
|
||||
}
|
||||
|
||||
void init_adb_state()
|
||||
{
|
||||
memset(&shoe.adb, 0, sizeof(adb_state_t));
|
||||
memset(&shoe.key, 0, sizeof(keyboard_state_t));
|
||||
memset(&shoe.mouse, 0, sizeof(mouse_state_t));
|
||||
|
||||
// Put the adb chip in state 3 (idle)
|
||||
shoe.adb.state = 3;
|
||||
|
||||
pthread_mutex_init(&shoe.adb.lock, NULL);
|
||||
}
|
||||
|
||||
void adb_start_service_request()
|
||||
{
|
||||
//printf("adb_start_service_request: pending_requests = 0x%02x\n", shoe.adb.pending_service_requests);
|
||||
|
@ -122,8 +148,8 @@ static void keyboard_talk(uint8_t reg)
|
|||
|
||||
case 2:
|
||||
// All the modifier keys are up
|
||||
shoe.adb.data[0] = 0b01111111;
|
||||
shoe.adb.data[1] = 0b11100111;
|
||||
shoe.adb.data[0] = ~b(01111111);
|
||||
shoe.adb.data[1] = ~b(11100111);
|
||||
shoe.adb.data_len = 2;
|
||||
return ;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
37
core/coff.c
37
core/coff.c
|
@ -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");
|
||||
|
|
85
core/coff.h
85
core/coff.h
|
@ -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
|
444
core/core_api.c
444
core/core_api.c
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
134
core/core_api.h
134
core/core_api.h
|
@ -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
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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));
|
||||
}
|
|
@ -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);
|
||||
|
|
12
core/mem.c
12
core/mem.c
|
@ -757,11 +757,11 @@ static void ea_decode_extended()
|
|||
uint32_t outer_disp = 0;
|
||||
// based on the I/IS behavior
|
||||
switch ((i<<3)|I) {
|
||||
case 0b0010: case 0b0110: case 0b1010:
|
||||
case ~b(0010): case ~b(0110): case ~b(1010):
|
||||
// sign-extended word-length outer displacement
|
||||
outer_disp = (int16_t)nextword(mypc);
|
||||
break;
|
||||
case 0b0011: case 0b0111: case 0b1011: {
|
||||
case ~b(0011): case ~b(0111): case ~b(1011): {
|
||||
// long word outer displacement
|
||||
outer_disp = nextlong(mypc);
|
||||
break ;
|
||||
|
@ -775,8 +775,8 @@ static void ea_decode_extended()
|
|||
|
||||
// Now mash all these numbers together to get an EA
|
||||
switch ((i<<3)|I) {
|
||||
case 0b0001: case 0b0010: case 0b0011:
|
||||
case 0b1001: case 0b1010: case 0b1011: {
|
||||
case ~b(0001): case ~b(0010): case ~b(0011):
|
||||
case ~b(1001): case ~b(1010): case ~b(1011): {
|
||||
// Indirect preindexed
|
||||
const uint32_t intermediate = lget(base_addr + base_disp + index_val, 4);
|
||||
if (shoe.abort) return ;
|
||||
|
@ -786,7 +786,7 @@ static void ea_decode_extended()
|
|||
return ;
|
||||
}
|
||||
|
||||
case 0b0101: case 0b0110: case 0b0111: {
|
||||
case ~b(0101): case ~b(0110): case ~b(0111): {
|
||||
// Indirect postindexed
|
||||
const uint32_t intermediate = lget(base_addr + base_disp, 4);
|
||||
if (shoe.abort) return ;
|
||||
|
@ -795,7 +795,7 @@ static void ea_decode_extended()
|
|||
return ;
|
||||
}
|
||||
|
||||
case 0b1000: case 0b0000: {
|
||||
case ~b(1000): case ~b(0000): {
|
||||
// No memory indirect action
|
||||
// EA = base_addr + base_disp + index
|
||||
shoe.extended_addr = base_addr + base_disp + index_val;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
383
core/scsi.c
383
core/scsi.c
|
@ -102,85 +102,17 @@ const char *scsi_write_reg_str[8] = {
|
|||
"start_dma_initiator_receive"
|
||||
};
|
||||
|
||||
enum scsi_bus_phase {
|
||||
BUS_FREE = 0,
|
||||
ARBITRATION,
|
||||
SELECTION,
|
||||
RESELECTION,
|
||||
COMMAND,
|
||||
DATA_OUT,
|
||||
DATA_IN,
|
||||
STATUS,
|
||||
MESSAGE_IN,
|
||||
MESSAGE_OUT
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
// Phase
|
||||
enum scsi_bus_phase phase;
|
||||
|
||||
// Scsi bus signals
|
||||
|
||||
uint8_t init_bsy:1; // BSY, driven by initiator
|
||||
uint8_t target_bsy:1; // BSY, driven by target
|
||||
|
||||
uint8_t sel:1; // SEL, driven by both target and initiator
|
||||
|
||||
uint8_t rst:1; // RST, driven by both target and initiator
|
||||
|
||||
uint8_t cd:1; // C/D (control or data), driven by target
|
||||
uint8_t io:1; // I/O, driven by target
|
||||
uint8_t ack:1; // ACK, driven by initiator
|
||||
uint8_t msg:1; // MSG, driven by target
|
||||
uint8_t atn:1; // ATN, driven by initiator
|
||||
uint8_t req:1; // REQ, driven by target
|
||||
|
||||
uint8_t data; // DB0-7, data lines, driven by both target and initiator
|
||||
|
||||
// NCR 5380 registers
|
||||
uint8_t initiator_command;
|
||||
uint8_t mode;
|
||||
uint8_t target_command;
|
||||
uint8_t select_enable; // probably not implementing this...
|
||||
|
||||
// Arbitration state
|
||||
uint8_t init_id; // initiator ID (as a bit mask) (usually 0x80)
|
||||
|
||||
// Selection state
|
||||
uint8_t target_id; // target ID (as an int [0, 7])
|
||||
|
||||
// transfer buffers
|
||||
uint8_t buf[512 * 256];
|
||||
uint32_t bufi;
|
||||
uint32_t in_len, in_i;
|
||||
uint32_t out_len, out_i;
|
||||
uint32_t write_offset;
|
||||
uint8_t status_byte;
|
||||
uint8_t message_byte; // only one-byte messages supported for now
|
||||
|
||||
// hack
|
||||
uint8_t dma_send_written; // Gets set whenever register 5 (start_dma_send) is written to, and cleared randomly.
|
||||
// This is because aux 1.1.1 sends an extra byte after sending the write command, and that's not
|
||||
// part of the write data. start_dma_send will be written when the data is actually starting.
|
||||
uint8_t sent_status_byte_via_reg0; // Gets set when the status byte is red via register 0.
|
||||
// This lets us know it's safe to switch to the MESSAGE_IN phase
|
||||
|
||||
} scsi_bus_state_t;
|
||||
|
||||
scsi_bus_state_t scsi;
|
||||
|
||||
static void switch_status_phase (uint8_t status_byte)
|
||||
{
|
||||
|
||||
printf("scsi_reg_something: switching to STATUS phase\n");
|
||||
|
||||
scsi.phase = STATUS;
|
||||
scsi.status_byte = status_byte;
|
||||
scsi.msg = 0;
|
||||
scsi.cd = 1;
|
||||
scsi.io = 1;
|
||||
shoe.scsi.phase = STATUS;
|
||||
shoe.scsi.status_byte = status_byte;
|
||||
shoe.scsi.msg = 0;
|
||||
shoe.scsi.cd = 1;
|
||||
shoe.scsi.io = 1;
|
||||
|
||||
scsi.bufi = 0;
|
||||
shoe.scsi.bufi = 0;
|
||||
|
||||
// Phase mismatch (I think)
|
||||
via_raise_interrupt(2, 0);
|
||||
|
@ -189,13 +121,13 @@ static void switch_status_phase (uint8_t status_byte)
|
|||
static void switch_command_phase (void)
|
||||
{
|
||||
printf("scsi_reg_something: switching to COMMAND phase\n");
|
||||
scsi.phase = COMMAND;
|
||||
shoe.scsi.phase = COMMAND;
|
||||
|
||||
scsi.msg = 0;
|
||||
scsi.cd = 1;
|
||||
scsi.io = 0;
|
||||
shoe.scsi.msg = 0;
|
||||
shoe.scsi.cd = 1;
|
||||
shoe.scsi.io = 0;
|
||||
|
||||
scsi.bufi = 0;
|
||||
shoe.scsi.bufi = 0;
|
||||
|
||||
// Phase mismatch, probably
|
||||
via_raise_interrupt(2, 0);
|
||||
|
@ -205,12 +137,12 @@ static void switch_message_in_phase (uint8_t message_byte)
|
|||
{
|
||||
printf("scsi_reg_something: switching to MESSAGE_IN phase\n");
|
||||
|
||||
scsi.phase = MESSAGE_IN;
|
||||
scsi.msg = 1;
|
||||
scsi.cd = 1;
|
||||
scsi.io = 1;
|
||||
shoe.scsi.phase = MESSAGE_IN;
|
||||
shoe.scsi.msg = 1;
|
||||
shoe.scsi.cd = 1;
|
||||
shoe.scsi.io = 1;
|
||||
|
||||
scsi.message_byte = message_byte; // only one-byte messages supported for now
|
||||
shoe.scsi.message_byte = message_byte; // only one-byte messages supported for now
|
||||
|
||||
// Phase mismatch, probably
|
||||
via_raise_interrupt(2, 0);
|
||||
|
@ -220,16 +152,16 @@ static void switch_bus_free_phase (void)
|
|||
{
|
||||
printf("scsi_reg_something: switching to BUS_FREE phase\n");
|
||||
|
||||
scsi.phase = BUS_FREE;
|
||||
shoe.scsi.phase = BUS_FREE;
|
||||
|
||||
scsi.msg = 0;
|
||||
scsi.cd = 0;
|
||||
scsi.io = 0;
|
||||
shoe.scsi.msg = 0;
|
||||
shoe.scsi.cd = 0;
|
||||
shoe.scsi.io = 0;
|
||||
|
||||
scsi.target_bsy = 0;
|
||||
scsi.req = 0;
|
||||
shoe.scsi.target_bsy = 0;
|
||||
shoe.scsi.req = 0;
|
||||
|
||||
scsi.bufi = 0;
|
||||
shoe.scsi.bufi = 0;
|
||||
// Phase mismatch not possible here.
|
||||
}
|
||||
|
||||
|
@ -237,11 +169,11 @@ static void switch_data_in_phase (void)
|
|||
{
|
||||
printf("scsi_reg_something: switching to DATA_IN phase\n");
|
||||
|
||||
scsi.phase = DATA_IN;
|
||||
shoe.scsi.phase = DATA_IN;
|
||||
|
||||
scsi.msg = 0;
|
||||
scsi.cd = 0;
|
||||
scsi.io = 1;
|
||||
shoe.scsi.msg = 0;
|
||||
shoe.scsi.cd = 0;
|
||||
shoe.scsi.io = 1;
|
||||
|
||||
// Phase mismatch, probably
|
||||
via_raise_interrupt(2, 0);
|
||||
|
@ -251,11 +183,11 @@ static void switch_data_out_phase (void)
|
|||
{
|
||||
printf("scsi_reg_something: switching to DATA_OUT phase\n");
|
||||
|
||||
scsi.phase = DATA_OUT;
|
||||
shoe.scsi.phase = DATA_OUT;
|
||||
|
||||
scsi.msg = 0;
|
||||
scsi.cd = 0;
|
||||
scsi.io = 0;
|
||||
shoe.scsi.msg = 0;
|
||||
shoe.scsi.cd = 0;
|
||||
shoe.scsi.io = 0;
|
||||
|
||||
via_raise_interrupt(2, 0);
|
||||
}
|
||||
|
@ -334,34 +266,34 @@ static void scsi_handle_inquiry_command(const uint8_t alloc_len)
|
|||
// XXX: added this because A/UX 3.0.1 requsts 6 bytes of the the inquiry response (sometimes?) I think it's polling for all attached scsi devices.
|
||||
// Fixme: figure out how to respond "not attached"
|
||||
if (alloc_len > sizeof(resp))
|
||||
scsi.in_len = sizeof(resp);
|
||||
shoe.scsi.in_len = sizeof(resp);
|
||||
else
|
||||
scsi.in_len = alloc_len;
|
||||
memcpy(scsi.buf, &resp, scsi.in_len);
|
||||
scsi.in_i = 0;
|
||||
shoe.scsi.in_len = alloc_len;
|
||||
memcpy(shoe.scsi.buf, &resp, shoe.scsi.in_len);
|
||||
shoe.scsi.in_i = 0;
|
||||
|
||||
switch_data_in_phase();
|
||||
}
|
||||
|
||||
static void scsi_buf_set (uint8_t byte)
|
||||
{
|
||||
assert(scsi.bufi <= sizeof(scsi.buf));
|
||||
scsi.buf[scsi.bufi++] = byte;
|
||||
assert(shoe.scsi.bufi <= sizeof(shoe.scsi.buf));
|
||||
shoe.scsi.buf[shoe.scsi.bufi++] = byte;
|
||||
|
||||
if (scsi.phase == COMMAND) {
|
||||
const uint32_t cmd_len = (scsi.buf[0] >= 0x20) ? 10 : 6; // 10 or 6 byte command?
|
||||
if (shoe.scsi.phase == COMMAND) {
|
||||
const uint32_t cmd_len = (shoe.scsi.buf[0] >= 0x20) ? 10 : 6; // 10 or 6 byte command?
|
||||
|
||||
assert(scsi.target_id < 8);
|
||||
scsi_device_t *dev = &shoe.scsi_devices[scsi.target_id];
|
||||
assert(shoe.scsi.target_id < 8);
|
||||
scsi_device_t *dev = &shoe.scsi_devices[shoe.scsi.target_id];
|
||||
|
||||
// If we need more data for this command, keep driving REQ
|
||||
if (scsi.bufi < cmd_len) {
|
||||
// scsi.req = 1;
|
||||
if (shoe.scsi.bufi < cmd_len) {
|
||||
// shoe.scsi.req = 1;
|
||||
// FIXME: keep driving DMA_REQUEST too
|
||||
return ;
|
||||
}
|
||||
|
||||
switch (scsi.buf[0]) {
|
||||
switch (shoe.scsi.buf[0]) {
|
||||
case 0: // test unit ready (6)
|
||||
printf("scsi_buf_set: responding to test-unit-ready\n");
|
||||
switch_status_phase(0); // switch to the status phase, with a status byte of 0
|
||||
|
@ -375,27 +307,27 @@ static void scsi_buf_set (uint8_t byte)
|
|||
case 0x25: // read capacity (10)
|
||||
printf("scsi_buf_set: responding to read-capacity\n");
|
||||
// bytes [0,3] -> BE number of blocks
|
||||
scsi.buf[0] = (dev->num_blocks >> 24) & 0xff;
|
||||
scsi.buf[1] = (dev->num_blocks >> 16) & 0xff;
|
||||
scsi.buf[2] = (dev->num_blocks >> 8) & 0xff;
|
||||
scsi.buf[3] = (dev->num_blocks) & 0xff;
|
||||
shoe.scsi.buf[0] = (dev->num_blocks >> 24) & 0xff;
|
||||
shoe.scsi.buf[1] = (dev->num_blocks >> 16) & 0xff;
|
||||
shoe.scsi.buf[2] = (dev->num_blocks >> 8) & 0xff;
|
||||
shoe.scsi.buf[3] = (dev->num_blocks) & 0xff;
|
||||
|
||||
// bytes [4,7] -> BE block size (needs to be 512)
|
||||
|
||||
scsi.buf[4] = (dev->block_size >> 24) & 0xff;
|
||||
scsi.buf[5] = (dev->block_size >> 16) & 0xff;
|
||||
scsi.buf[6] = (dev->block_size >> 8) & 0xff;
|
||||
scsi.buf[7] = (dev->block_size) & 0xff;
|
||||
shoe.scsi.buf[4] = (dev->block_size >> 24) & 0xff;
|
||||
shoe.scsi.buf[5] = (dev->block_size >> 16) & 0xff;
|
||||
shoe.scsi.buf[6] = (dev->block_size >> 8) & 0xff;
|
||||
shoe.scsi.buf[7] = (dev->block_size) & 0xff;
|
||||
|
||||
scsi.in_i = 0;
|
||||
scsi.in_len = 8;
|
||||
shoe.scsi.in_i = 0;
|
||||
shoe.scsi.in_len = 8;
|
||||
|
||||
switch_data_in_phase();
|
||||
break;
|
||||
|
||||
case 0x12: { // inquiry command (6)
|
||||
printf("scsi_buf_set: responding to inquiry\n");
|
||||
const uint8_t alloc_len = scsi.buf[4];
|
||||
const uint8_t alloc_len = shoe.scsi.buf[4];
|
||||
|
||||
scsi_handle_inquiry_command(alloc_len);
|
||||
break;
|
||||
|
@ -403,10 +335,10 @@ static void scsi_buf_set (uint8_t byte)
|
|||
|
||||
case 0x8: { // read (6)
|
||||
const uint32_t offset =
|
||||
(scsi.buf[1] << 16) |
|
||||
(scsi.buf[2] << 8 ) |
|
||||
(scsi.buf[3]);
|
||||
const uint8_t len = scsi.buf[4];
|
||||
(shoe.scsi.buf[1] << 16) |
|
||||
(shoe.scsi.buf[2] << 8 ) |
|
||||
(shoe.scsi.buf[3]);
|
||||
const uint8_t len = shoe.scsi.buf[4];
|
||||
|
||||
assert(dev->f);
|
||||
|
||||
|
@ -422,11 +354,11 @@ static void scsi_buf_set (uint8_t byte)
|
|||
}
|
||||
|
||||
assert(0 == fseeko(dev->f, 512 * offset, SEEK_SET));
|
||||
assert(fread(scsi.buf, len * 512, 1, dev->f) == 1);
|
||||
assert(fread(shoe.scsi.buf, len * 512, 1, dev->f) == 1);
|
||||
|
||||
|
||||
scsi.in_len = len * 512;
|
||||
scsi.in_i = 0;
|
||||
shoe.scsi.in_len = len * 512;
|
||||
shoe.scsi.in_i = 0;
|
||||
|
||||
switch_data_in_phase();
|
||||
break;
|
||||
|
@ -434,20 +366,20 @@ static void scsi_buf_set (uint8_t byte)
|
|||
|
||||
case 0xa: { // write (6)
|
||||
const uint32_t offset =
|
||||
(scsi.buf[1] << 16) |
|
||||
(scsi.buf[2] << 8 ) |
|
||||
(scsi.buf[3]);
|
||||
const uint8_t len = scsi.buf[4];
|
||||
(shoe.scsi.buf[1] << 16) |
|
||||
(shoe.scsi.buf[2] << 8 ) |
|
||||
(shoe.scsi.buf[3]);
|
||||
const uint8_t len = shoe.scsi.buf[4];
|
||||
|
||||
printf("scsi_buf_set: Responding to write at off=%u len=%u\n", offset, len);
|
||||
|
||||
//assert(len <= 64);
|
||||
|
||||
scsi.write_offset = offset;
|
||||
scsi.out_len = len * 512;
|
||||
scsi.out_i = 0;
|
||||
shoe.scsi.write_offset = offset;
|
||||
shoe.scsi.out_len = len * 512;
|
||||
shoe.scsi.out_i = 0;
|
||||
|
||||
scsi.dma_send_written = 0; // reset here. The real data will come in after start_dma_send is written to.
|
||||
shoe.scsi.dma_send_written = 0; // reset here. The real data will come in after start_dma_send is written to.
|
||||
switch_data_out_phase();
|
||||
break;
|
||||
}
|
||||
|
@ -457,15 +389,22 @@ static void scsi_buf_set (uint8_t byte)
|
|||
break;
|
||||
}
|
||||
|
||||
scsi.bufi = 0;
|
||||
shoe.scsi.bufi = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void init_scsi_bus_state ()
|
||||
{
|
||||
memset(&scsi, 0, sizeof(scsi_bus_state_t));
|
||||
memset(&shoe.scsi, 0, sizeof(scsi_bus_state_t));
|
||||
|
||||
scsi.phase = BUS_FREE;
|
||||
shoe.scsi.phase = BUS_FREE;
|
||||
}
|
||||
|
||||
void reset_scsi_bus_state ()
|
||||
{
|
||||
memset(&shoe.scsi, 0, sizeof(scsi_bus_state_t));
|
||||
|
||||
shoe.scsi.phase = BUS_FREE;
|
||||
}
|
||||
|
||||
void scsi_reg_read ()
|
||||
|
@ -476,14 +415,14 @@ void scsi_reg_read ()
|
|||
|
||||
switch (reg) {
|
||||
case 0: // Current scsi data bus register
|
||||
if (scsi.phase == ARBITRATION)
|
||||
if (shoe.scsi.phase == ARBITRATION)
|
||||
shoe.physical_dat = 0; // I don't know why A/UX expects 0 here. It should be the initiator's ID, I think
|
||||
else if (scsi.phase == MESSAGE_IN) {
|
||||
shoe.physical_dat = scsi.message_byte; // one-byte messages supported for now
|
||||
else if (shoe.scsi.phase == MESSAGE_IN) {
|
||||
shoe.physical_dat = shoe.scsi.message_byte; // one-byte messages supported for now
|
||||
}
|
||||
else if (scsi.phase == STATUS) {
|
||||
shoe.physical_dat = scsi.status_byte;
|
||||
scsi.sent_status_byte_via_reg0 = 1;
|
||||
else if (shoe.scsi.phase == STATUS) {
|
||||
shoe.physical_dat = shoe.scsi.status_byte;
|
||||
shoe.scsi.sent_status_byte_via_reg0 = 1;
|
||||
}
|
||||
else
|
||||
assert(!"scsi_reg_read: reading data reg (0) from unknown phase\n");
|
||||
|
@ -492,38 +431,38 @@ void scsi_reg_read ()
|
|||
|
||||
case 1: // Initiator command register
|
||||
|
||||
if (scsi.phase == ARBITRATION &&
|
||||
(scsi.initiator_command & INIT_COMM_ARBITRATION_IN_PROGRESS)) {
|
||||
if (shoe.scsi.phase == ARBITRATION &&
|
||||
(shoe.scsi.initiator_command & INIT_COMM_ARBITRATION_IN_PROGRESS)) {
|
||||
|
||||
shoe.physical_dat = scsi.initiator_command;
|
||||
shoe.physical_dat = shoe.scsi.initiator_command;
|
||||
|
||||
// the INIT_COMM_ARBITRATION_IN_PROGRESS bit is transient. Just clear
|
||||
// it after the first access (it needs to go hi, then later low)
|
||||
scsi.initiator_command &= ~INIT_COMM_ARBITRATION_IN_PROGRESS;
|
||||
shoe.scsi.initiator_command &= ~INIT_COMM_ARBITRATION_IN_PROGRESS;
|
||||
|
||||
}
|
||||
else
|
||||
shoe.physical_dat = scsi.initiator_command;
|
||||
shoe.physical_dat = shoe.scsi.initiator_command;
|
||||
|
||||
break;
|
||||
|
||||
case 2: // Mode register
|
||||
shoe.physical_dat = scsi.mode;
|
||||
shoe.physical_dat = shoe.scsi.mode;
|
||||
break;
|
||||
|
||||
case 3: // Target command register
|
||||
shoe.physical_dat = scsi.target_command & 0xf; // only the low 4 bits are significant
|
||||
shoe.physical_dat = shoe.scsi.target_command & 0xf; // only the low 4 bits are significant
|
||||
break;
|
||||
|
||||
case 4: { // Current SCSI control register
|
||||
uint8_t tmp = 0;
|
||||
tmp |= (scsi.sel * CURR_SCSI_CONTROL_SEL);
|
||||
tmp |= (scsi.io * CURR_SCSI_CONTROL_IO);
|
||||
tmp |= (scsi.cd * CURR_SCSI_CONTROL_CD);
|
||||
tmp |= (scsi.msg * CURR_SCSI_CONTROL_MSG);
|
||||
tmp |= (scsi.req * CURR_SCSI_CONTROL_REQ);
|
||||
tmp |= ((scsi.target_bsy || scsi.init_bsy) ? CURR_SCSI_CONTROL_BSY : 0);
|
||||
tmp |= (scsi.rst * CURR_SCSI_CONTROL_RST);
|
||||
tmp |= (shoe.scsi.sel * CURR_SCSI_CONTROL_SEL);
|
||||
tmp |= (shoe.scsi.io * CURR_SCSI_CONTROL_IO);
|
||||
tmp |= (shoe.scsi.cd * CURR_SCSI_CONTROL_CD);
|
||||
tmp |= (shoe.scsi.msg * CURR_SCSI_CONTROL_MSG);
|
||||
tmp |= (shoe.scsi.req * CURR_SCSI_CONTROL_REQ);
|
||||
tmp |= ((shoe.scsi.target_bsy || shoe.scsi.init_bsy) ? CURR_SCSI_CONTROL_BSY : 0);
|
||||
tmp |= (shoe.scsi.rst * CURR_SCSI_CONTROL_RST);
|
||||
shoe.physical_dat = tmp;
|
||||
break;
|
||||
}
|
||||
|
@ -534,14 +473,14 @@ void scsi_reg_read ()
|
|||
// Compute phase match (IO, CD, MSG match the assertions in target_command register)
|
||||
uint8_t phase_tmp = 0;
|
||||
{
|
||||
phase_tmp = (phase_tmp << 1) | scsi.msg;
|
||||
phase_tmp = (phase_tmp << 1) | scsi.cd;
|
||||
phase_tmp = (phase_tmp << 1) | scsi.io;
|
||||
phase_tmp = (phase_tmp == (scsi.target_command & 7));
|
||||
phase_tmp = (phase_tmp << 1) | shoe.scsi.msg;
|
||||
phase_tmp = (phase_tmp << 1) | shoe.scsi.cd;
|
||||
phase_tmp = (phase_tmp << 1) | shoe.scsi.io;
|
||||
phase_tmp = (phase_tmp == (shoe.scsi.target_command & 7));
|
||||
}
|
||||
|
||||
tmp |= (scsi.ack * BUS_STATUS_ACK);
|
||||
tmp |= (scsi.atn * BUS_STATUS_ATN);
|
||||
tmp |= (shoe.scsi.ack * BUS_STATUS_ACK);
|
||||
tmp |= (shoe.scsi.atn * BUS_STATUS_ATN);
|
||||
tmp |= (phase_tmp * BUS_STATUS_PHASE_MATCH);
|
||||
|
||||
// let's just say BUS_ERROR is always false (fixme: wrong)
|
||||
|
@ -571,67 +510,67 @@ void scsi_reg_write ()
|
|||
|
||||
switch (reg) {
|
||||
case 0: // Output data register
|
||||
scsi.data = dat;
|
||||
shoe.scsi.data = dat;
|
||||
break;
|
||||
|
||||
case 1: { // Initiator command register
|
||||
scsi.initiator_command = dat;
|
||||
shoe.scsi.initiator_command = dat;
|
||||
|
||||
scsi.ack = ((scsi.initiator_command & INIT_COMM_ASSERT_ACK) != 0);
|
||||
scsi.rst = ((scsi.initiator_command & INIT_COMM_ASSERT_RST) != 0);
|
||||
scsi.init_bsy = ((scsi.initiator_command & INIT_COMM_ASSERT_BSY) != 0);
|
||||
scsi.sel = ((scsi.initiator_command & INIT_COMM_ASSERT_SEL) != 0);
|
||||
scsi.atn = ((scsi.initiator_command & INIT_COMM_ASSERT_ATN) != 0);
|
||||
shoe.scsi.ack = ((shoe.scsi.initiator_command & INIT_COMM_ASSERT_ACK) != 0);
|
||||
shoe.scsi.rst = ((shoe.scsi.initiator_command & INIT_COMM_ASSERT_RST) != 0);
|
||||
shoe.scsi.init_bsy = ((shoe.scsi.initiator_command & INIT_COMM_ASSERT_BSY) != 0);
|
||||
shoe.scsi.sel = ((shoe.scsi.initiator_command & INIT_COMM_ASSERT_SEL) != 0);
|
||||
shoe.scsi.atn = ((shoe.scsi.initiator_command & INIT_COMM_ASSERT_ATN) != 0);
|
||||
|
||||
/*
|
||||
// --- Arbitration ---
|
||||
// Check whether to switch from ARBITRATION to SELECTION
|
||||
if (scsi.sel && scsi.phase == ARBITRATION) {
|
||||
if (shoe.scsi.sel && shoe.scsi.phase == ARBITRATION) {
|
||||
// Asserting SEL in arbitration phase means we switch to selection phase :)
|
||||
scsi.phase = SELECTION;
|
||||
scsi.target_id = INVALID_ID; // invalid ID
|
||||
shoe.scsi.phase = SELECTION;
|
||||
shoe.scsi.target_id = INVALID_ID; // invalid ID
|
||||
printf("scsi_reg_write: selection phase\n");
|
||||
break;
|
||||
}
|
||||
*/
|
||||
// --- Selection ---
|
||||
// If we're in SELECTION, receive the target_id from scsi.data
|
||||
if (scsi.sel && (scsi.initiator_command & INIT_COMM_ASSERT_DATA_BUS) &&
|
||||
((scsi.phase == ARBITRATION) || (scsi.phase == BUS_FREE)))
|
||||
// If we're in SELECTION, receive the target_id from shoe.scsi.data
|
||||
if (shoe.scsi.sel && (shoe.scsi.initiator_command & INIT_COMM_ASSERT_DATA_BUS) &&
|
||||
((shoe.scsi.phase == ARBITRATION) || (shoe.scsi.phase == BUS_FREE)))
|
||||
{
|
||||
uint8_t id;
|
||||
for (id=0; (id < 8) && !(scsi.data & (1 << id)); id++) ;
|
||||
for (id=0; (id < 8) && !(shoe.scsi.data & (1 << id)); id++) ;
|
||||
assert(id != 8);
|
||||
scsi.target_id = id;
|
||||
shoe.scsi.target_id = id;
|
||||
printf("scsi_reg_write: selected target id %u\n", id);
|
||||
scsi.target_bsy = 1; // target asserts BSY to acknowledge being selected
|
||||
scsi.phase = SELECTION;
|
||||
shoe.scsi.target_bsy = 1; // target asserts BSY to acknowledge being selected
|
||||
shoe.scsi.phase = SELECTION;
|
||||
break;
|
||||
}
|
||||
|
||||
// SELECTION ends when SEL gets unset
|
||||
if (!scsi.sel && scsi.phase == SELECTION) {
|
||||
if (!shoe.scsi.sel && shoe.scsi.phase == SELECTION) {
|
||||
printf("scsi_reg_write: switch to COMMAND phase\n"); // what's next?
|
||||
|
||||
scsi.req = 1; // target asserts REQ after initiator deasserts SEL
|
||||
shoe.scsi.req = 1; // target asserts REQ after initiator deasserts SEL
|
||||
|
||||
// Switch to COMMAND phase
|
||||
scsi.cd = 1;
|
||||
scsi.io = 0;
|
||||
scsi.msg = 0;
|
||||
scsi.phase = COMMAND;
|
||||
shoe.scsi.cd = 1;
|
||||
shoe.scsi.io = 0;
|
||||
shoe.scsi.msg = 0;
|
||||
shoe.scsi.phase = COMMAND;
|
||||
break;
|
||||
}
|
||||
|
||||
// --- Information transfer ---
|
||||
// If initiator asserts ACK, then target needs to deassert REQ
|
||||
// (I think this only makes sense for non-arbitration/selection/busfree situations
|
||||
if ((scsi.phase != BUS_FREE) && (scsi.phase != ARBITRATION) && (scsi.phase != SELECTION)) {
|
||||
if ((shoe.scsi.phase != BUS_FREE) && (shoe.scsi.phase != ARBITRATION) && (shoe.scsi.phase != SELECTION)) {
|
||||
|
||||
// If this is the message_in phase, use the unsetting-ACK portion of the REQ/ACK handshake
|
||||
// to go to BUS_FREE.
|
||||
// Don't bother asserting REQ here. Also, switch_bus_free_phase() will deassert target_BSY.
|
||||
if (scsi.phase == MESSAGE_IN && !scsi.ack && !scsi.req) {
|
||||
if (shoe.scsi.phase == MESSAGE_IN && !shoe.scsi.ack && !shoe.scsi.req) {
|
||||
switch_bus_free_phase();
|
||||
break ;
|
||||
}
|
||||
|
@ -640,12 +579,12 @@ void scsi_reg_write ()
|
|||
// Do this when the OS deasserts ACK. We know that ACK was previously asserted if !REQ.
|
||||
// (This is kinda hacky - maybe I can detect if ACK is deasserted by looking at the
|
||||
// previous value of reg1)
|
||||
else if (scsi.phase == STATUS && !scsi.ack && !scsi.req && scsi.sent_status_byte_via_reg0) {
|
||||
scsi.req = 1;
|
||||
else if (shoe.scsi.phase == STATUS && !shoe.scsi.ack && !shoe.scsi.req && shoe.scsi.sent_status_byte_via_reg0) {
|
||||
shoe.scsi.req = 1;
|
||||
switch_message_in_phase(0);
|
||||
}
|
||||
else {
|
||||
scsi.req = !scsi.ack;
|
||||
shoe.scsi.req = !shoe.scsi.ack;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -653,12 +592,12 @@ void scsi_reg_write ()
|
|||
}
|
||||
|
||||
case 2: { // Mode register
|
||||
scsi.mode = dat;
|
||||
shoe.scsi.mode = dat;
|
||||
|
||||
if (scsi.mode & MODE_ARBITRATE) {
|
||||
if (shoe.scsi.mode & MODE_ARBITRATE) {
|
||||
printf("scsi_reg_write: arbitration phase\n");
|
||||
scsi.phase = ARBITRATION;
|
||||
scsi.initiator_command |= INIT_COMM_ARBITRATION_IN_PROGRESS;
|
||||
shoe.scsi.phase = ARBITRATION;
|
||||
shoe.scsi.initiator_command |= INIT_COMM_ARBITRATION_IN_PROGRESS;
|
||||
}
|
||||
else {
|
||||
|
||||
|
@ -667,15 +606,15 @@ void scsi_reg_write ()
|
|||
break;
|
||||
}
|
||||
case 3: // Target command register
|
||||
scsi.target_command = dat & 0xf; // only the bottom 4 bits are writable
|
||||
shoe.scsi.target_command = dat & 0xf; // only the bottom 4 bits are writable
|
||||
break;
|
||||
|
||||
case 4: // ID select register
|
||||
scsi.select_enable = dat;
|
||||
shoe.scsi.select_enable = dat;
|
||||
break;
|
||||
|
||||
case 5: // Start DMA send
|
||||
scsi.dma_send_written = 1;
|
||||
shoe.scsi.dma_send_written = 1;
|
||||
via_raise_interrupt(2, 0);
|
||||
break;
|
||||
|
||||
|
@ -700,31 +639,31 @@ void scsi_dma_write_long(const uint32_t dat)
|
|||
|
||||
void scsi_dma_write (const uint8_t byte)
|
||||
{
|
||||
if (scsi.phase == COMMAND) {
|
||||
if (shoe.scsi.phase == COMMAND) {
|
||||
printf("scsi_reg_dma_write: writing COMMAND byte 0x%02x\n", byte);
|
||||
scsi_buf_set(byte);
|
||||
}
|
||||
else if (scsi.phase == DATA_OUT && scsi.dma_send_written) {
|
||||
scsi.buf[scsi.out_i++] = byte;
|
||||
else if (shoe.scsi.phase == DATA_OUT && shoe.scsi.dma_send_written) {
|
||||
shoe.scsi.buf[shoe.scsi.out_i++] = byte;
|
||||
|
||||
//printf("scsi_reg_dma_write: writing DATA_OUT byte 0x%02x (%c)\n", byte, isprint(byte)?byte:'.');
|
||||
|
||||
if (scsi.out_i >= scsi.out_len) {
|
||||
assert(scsi.target_id < 8);
|
||||
scsi_device_t *dev = &shoe.scsi_devices[scsi.target_id];
|
||||
if (shoe.scsi.out_i >= shoe.scsi.out_len) {
|
||||
assert(shoe.scsi.target_id < 8);
|
||||
scsi_device_t *dev = &shoe.scsi_devices[shoe.scsi.target_id];
|
||||
assert(dev->f);
|
||||
|
||||
assert(0 == fseeko(dev->f, 512 * scsi.write_offset, SEEK_SET));
|
||||
assert(fwrite(scsi.buf, scsi.out_len, 1, dev->f) == 1);
|
||||
assert(0 == fseeko(dev->f, 512 * shoe.scsi.write_offset, SEEK_SET));
|
||||
assert(fwrite(shoe.scsi.buf, shoe.scsi.out_len, 1, dev->f) == 1);
|
||||
fflush(dev->f);
|
||||
|
||||
scsi.out_i = 0;
|
||||
scsi.out_len = 0;
|
||||
shoe.scsi.out_i = 0;
|
||||
shoe.scsi.out_len = 0;
|
||||
switch_status_phase(0);
|
||||
}
|
||||
}
|
||||
else if (scsi.phase == DATA_OUT) {
|
||||
printf("scsi_reg_dma_write: writing DATA_OUT byte (without scsi.dma_send_written) 0x%02x\n", byte);
|
||||
else if (shoe.scsi.phase == DATA_OUT) {
|
||||
printf("scsi_reg_dma_write: writing DATA_OUT byte (without shoe.scsi.dma_send_written) 0x%02x\n", byte);
|
||||
}
|
||||
else {
|
||||
printf("scsi_reg_dma_write: writing 0x%02x in UNKNOWN PHASE!\n", byte);
|
||||
|
@ -747,17 +686,17 @@ uint8_t scsi_dma_read ()
|
|||
{
|
||||
uint8_t result = 0;
|
||||
|
||||
if (scsi.phase == STATUS) {
|
||||
if (shoe.scsi.phase == STATUS) {
|
||||
// If in the STATUS phase, return the status byte and switch back to COMMAND phase
|
||||
result = scsi.status_byte;
|
||||
result = shoe.scsi.status_byte;
|
||||
switch_message_in_phase(0);
|
||||
}
|
||||
else if (scsi.phase == DATA_IN) {
|
||||
assert(scsi.in_len > 0);
|
||||
result = scsi.buf[scsi.in_i++];
|
||||
if (scsi.in_i >= scsi.in_len) {
|
||||
scsi.in_i = 0;
|
||||
scsi.in_len = 0;
|
||||
else if (shoe.scsi.phase == DATA_IN) {
|
||||
assert(shoe.scsi.in_len > 0);
|
||||
result = shoe.scsi.buf[shoe.scsi.in_i++];
|
||||
if (shoe.scsi.in_i >= shoe.scsi.in_len) {
|
||||
shoe.scsi.in_i = 0;
|
||||
shoe.scsi.in_len = 0;
|
||||
|
||||
switch_status_phase(0);
|
||||
}
|
||||
|
|
357
core/shoebill.h
357
core/shoebill.h
|
@ -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
|
||||
|
|
123
core/via.c
123
core/via.c
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -186,4 +186,11 @@
|
|||
[[self window] close];
|
||||
}
|
||||
|
||||
-(IBAction)zapPramPressed:(id)sender
|
||||
{
|
||||
shoeApplication *shoeApp = (shoeApplication*) NSApp;
|
||||
[shoeApp zapPram:[NSUserDefaults standardUserDefaults] ptr:nil];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
|
||||
@interface shoeScreenView : NSOpenGLView {
|
||||
CGColorSpaceRef colorspace;
|
||||
shoebill_control_t *control;
|
||||
NSTimer *timer;
|
||||
NSRecursiveLock *lock;
|
||||
CIContext *ciContext;
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -28,10 +28,8 @@
|
|||
|
||||
@interface shoeScreenWindow : NSWindow {
|
||||
@public
|
||||
uint8_t slotnum;
|
||||
}
|
||||
|
||||
- (void) configure:(uint8_t) _slotnum;
|
||||
- (void) reevaluateKeyWindowness;
|
||||
- (void) warpToCenter;
|
||||
- (void) captureMouse;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue