mirror of
https://github.com/pruten/shoebill.git
synced 2025-02-19 01:30:52 +00:00
First big barely-functional code drop
This commit is contained in:
parent
6b3a5839d2
commit
93cefd9451
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
/intermediates
|
||||
/shoebill
|
||||
.DS_Store
|
||||
*.xcworkspace
|
||||
xcuserdata
|
26
LICENSE
26
LICENSE
@ -1,23 +1,23 @@
|
||||
Copyright (c) 2014, pruten
|
||||
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:
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* 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.
|
||||
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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
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
|
||||
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.
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
16
Makefile
Normal file
16
Makefile
Normal file
@ -0,0 +1,16 @@
|
||||
|
||||
CC = clang
|
||||
CFLAGS = -O4 -arch x86_64 -Wno-deprecated-declarations
|
||||
LFLAGS = -framework OpenGL -framework GLUT
|
||||
|
||||
all: shoebill
|
||||
|
||||
shoebill: make_core test.c
|
||||
$(CC) $(LFLAGS) $(CFLAGS) -L intermediates -l shoebill_core test.c -o shoebill
|
||||
|
||||
make_core:
|
||||
$(MAKE) -C core -j 4
|
||||
|
||||
clean:
|
||||
rm -rf intermediates
|
||||
rm -f shoebill
|
42
README
Normal file
42
README
Normal file
@ -0,0 +1,42 @@
|
||||
|
||||
Shoebill - a Macintosh II emulator that can only run A/UX
|
||||
|
||||
|
||||
See the wiki on https://code.google.com/p/shoebill for better
|
||||
documentation on building and running Shoebill.
|
||||
|
||||
*** RUNNING ***
|
||||
|
||||
You will need
|
||||
* OS X and a 64-bit Intel Macintosh
|
||||
(32-bit builds may be possible in the future)
|
||||
* A Macintosh II or IIx ROM
|
||||
* A disk image with A/UX 1.x.x or 2.x.x installed
|
||||
* The kernel on that image (/unix). Shoebill can’t read
|
||||
SVFS or UFS file sytems yet to load the kernel directly
|
||||
from the disk image.
|
||||
|
||||
|
||||
To boot A/UX
|
||||
* Backup your disk images!!
|
||||
When Shoebill inevitably crashes, your A/UX boot image
|
||||
will very likely be corrupted - sometimes so severely
|
||||
that A/UX can’t even boot enough to run fsck.
|
||||
* Open Shoebill.app and select Preferences menu item
|
||||
* Set the paths for your ROM, kernel, and disk image(s).
|
||||
* Do use SCSI ID #0 for your A/UX boot image.
|
||||
* Give A/UX a reasonable amount of RAM - say, 32MB
|
||||
* Press “Apply and Run”
|
||||
|
||||
|
||||
*** BUILDING ***
|
||||
|
||||
1) cd to shoebill/
|
||||
2) make # to build shoebill_core
|
||||
3) xcodebuild -project gui/Shoebill.xcodeproj # to build the Cocoa GUI
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
58
core/Makefile
Normal file
58
core/Makefile
Normal file
@ -0,0 +1,58 @@
|
||||
|
||||
CC = clang
|
||||
CFLAGS = -O4 -ggdb -arch x86_64 -Wno-deprecated-declarations
|
||||
|
||||
|
||||
DEPS = core_api.h coff.h mc68851.h redblack.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 toby_frame_buffer video core_api
|
||||
|
||||
OBJ_NEED_NOTHING = $(patsubst %,$(TEMP)/%.o,$(NEED_NOTHING))
|
||||
OBJ_NEED_PREPROCESSING = $(patsubst %,$(TEMP)/%.o,$(NEED_PREPROCESSING))
|
||||
OBJ_NEED_DECODER = $(patsubst %,$(TEMP)/%.o,$(NEED_DECODER))
|
||||
|
||||
# Files that NEED_DECODER also NEED_PREPROCESSING
|
||||
POST_PREPROCESSING = $(patsubst %,$(TEMP)/%.post.c,$(NEED_PREPROCESSING)) $(patsubst %,$(TEMP)/%.post.c,$(NEED_DECODER))
|
||||
|
||||
MACRO = perl macro.pl
|
||||
|
||||
TEMP = ../intermediates
|
||||
|
||||
|
||||
all: $(TEMP)/libshoebill_core.a
|
||||
|
||||
$(TEMP)/libshoebill_core.a: $(TEMP) $(DEPS) $(OBJ_NEED_NOTHING) $(OBJ_NEED_PREPROCESSING) $(OBJ_NEED_DECODER)
|
||||
libtool -static -o $(TEMP)/libshoebill_core.a $(OBJ_NEED_NOTHING) $(OBJ_NEED_PREPROCESSING) $(OBJ_NEED_DECODER)
|
||||
|
||||
# Build object files
|
||||
$(OBJ_NEED_NOTHING): $(TEMP)/%.o: %.c $(DEPS)
|
||||
$(CC) -c $(CFLAGS) $< -o $@
|
||||
|
||||
$(OBJ_NEED_PREPROCESSING): $(TEMP)/%.o: $(TEMP)/%.post.c $(DEPS)
|
||||
$(CC) -c $(CFLAGS) $< -o $@
|
||||
|
||||
$(OBJ_NEED_DECODER): $(TEMP)/%.o: $(TEMP)/%.post.c $(DEPS) $(TEMP)/dis_decoder_guts.c $(TEMP)/inst_decoder_guts.c
|
||||
$(CC) -c $(CFLAGS) $< -o $@
|
||||
|
||||
# Preprocess C files
|
||||
$(POST_PREPROCESSING): $(TEMP)/%.post.c: %.c $(DEPS)
|
||||
$(MACRO) $< $@
|
||||
|
||||
# Generate instruction decoders
|
||||
$(TEMP)/inst_decoder_guts.c: $(TEMP)/decoder_gen $(DEPS)
|
||||
$(TEMP)/decoder_gen inst $(TEMP)/
|
||||
$(TEMP)/dis_decoder_guts.c: $(TEMP)/decoder_gen $(DEPS)
|
||||
$(TEMP)/decoder_gen dis $(TEMP)/
|
||||
|
||||
# Compile the decoder generator
|
||||
$(TEMP)/decoder_gen: decoder_gen.c $(DEPS)
|
||||
$(CC) $(CFLAGS) decoder_gen.c -o $(TEMP)/decoder_gen
|
||||
|
||||
|
||||
$(TEMP):
|
||||
mkdir -p $(TEMP)
|
||||
|
||||
clean:
|
||||
rm -rf $(TEMP)
|
||||
|
393
core/adb.c
Normal file
393
core/adb.c
Normal file
@ -0,0 +1,393 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include "../core/shoebill.h"
|
||||
#include <assert.h>
|
||||
|
||||
// via1 ORB bits abcd efgh
|
||||
// cd -> adb FSM state
|
||||
// e -> adb timeout occurred / service request (?)
|
||||
// f/g/h nvram stuff
|
||||
|
||||
#define VIA_REGB_ADB_STATUS 8
|
||||
|
||||
/*
|
||||
Reset:
|
||||
|
||||
-> OS writes reset command to sr, sets state 0
|
||||
<- chip sets ADB status line, raises interrupt
|
||||
-> OS dummy-reads sr, sets state 2
|
||||
<- chip sets ADB status line, raises interrupt
|
||||
-> OS dummy-reads sr, sets state 3
|
||||
|
||||
|
||||
Talk:
|
||||
-> OS writes talk command to sr, sets state 0
|
||||
<- chip responds with an interrupt. Status=0 -> poll(unsolicited?), status=1 -> response?
|
||||
-> OS sets state=1
|
||||
<- chip responds with an interrupt. Status=0 -> timeout, status=1 -> not-timedout?
|
||||
-> OS reads first byte from sr, sets state=2
|
||||
<- chip responds with interrupt. Status=0 -> service request, service=1 no-service-reques
|
||||
-> OS reads second byte from sr, sets state=3
|
||||
|
||||
|
||||
Listen:
|
||||
-> OS writes listen command to sr, sets state 0
|
||||
<- chip
|
||||
???
|
||||
|
||||
Keyboard:
|
||||
-> OS sends EXISTS (TALK) for id 2, reg 3
|
||||
<- (if no keyboard: timeout. If keyboard: respond with some data)
|
||||
|
||||
-> OS sends FLUSH
|
||||
<- Keyboard responds
|
||||
|
||||
-> OS sends LISTEN for id 2, reg 3 (data: 0x2201 - sets the service request enable bit)
|
||||
<- Keyboard respnods
|
||||
|
||||
-> OS sends TALK for id 2 reg 2
|
||||
<- Keyboard responds with some keyboard setup data
|
||||
|
||||
-> OS sends TALK for id 2 reg 0
|
||||
<- if there's a key, keyboard returns key data. Otherwise, response times out.
|
||||
|
||||
*/
|
||||
|
||||
void adb_start_service_request()
|
||||
{
|
||||
printf("adb_start_service_request: pending_requests = 0x%02x\n", shoe.adb.pending_service_requests);
|
||||
if (shoe.adb.pending_service_requests) {
|
||||
shoe.adb.service_request = 1;
|
||||
|
||||
shoe.adb.poll = shoe.adb.pending_poll;
|
||||
shoe.adb.pending_poll = 0;
|
||||
|
||||
via_raise_interrupt(1, IFR_SHIFT_REG);
|
||||
}
|
||||
}
|
||||
|
||||
void adb_request_service_request(uint8_t id)
|
||||
{
|
||||
shoe.adb.pending_service_requests |= (1 << id);
|
||||
shoe.adb.pending_poll = 1;
|
||||
|
||||
if (shoe.adb.state == 3) {
|
||||
adb_start_service_request();
|
||||
}
|
||||
}
|
||||
|
||||
static void keyboard_talk(uint8_t reg)
|
||||
{
|
||||
shoe.adb.timeout = 0;
|
||||
|
||||
switch (reg) {
|
||||
case 0:
|
||||
if (shoe.key.key_i > 0) {
|
||||
shoe.adb.data[0] = shoe.key.keys[0].code_b;
|
||||
shoe.adb.data[1] = shoe.key.keys[0].code_a;
|
||||
shoe.adb.data_len = 2;
|
||||
shoe.key.key_i--;
|
||||
memmove(&shoe.key.keys[0], &shoe.key.keys[1], shoe.key.key_i * sizeof(shoe.key.keys[0]));
|
||||
}
|
||||
else
|
||||
shoe.adb.timeout = 1;
|
||||
|
||||
return ;
|
||||
|
||||
case 2:
|
||||
// All the modifier keys are up
|
||||
shoe.adb.data[0] = 0b01111111;
|
||||
shoe.adb.data[1] = 0b11100111;
|
||||
shoe.adb.data_len = 2;
|
||||
return ;
|
||||
|
||||
case 1:
|
||||
shoe.adb.timeout = 1;
|
||||
return ;
|
||||
|
||||
case 3:
|
||||
shoe.adb.data[0] = 0;
|
||||
shoe.adb.data[1] = 0;
|
||||
shoe.adb.data_len = 2;
|
||||
return ;
|
||||
}
|
||||
}
|
||||
|
||||
static void mouse_talk(uint8_t reg)
|
||||
{
|
||||
shoe.adb.timeout = 0;
|
||||
|
||||
printf("mouse_talk: reg=%u\n", reg);
|
||||
switch (reg) {
|
||||
|
||||
case 0:
|
||||
if (shoe.mouse.changed) {
|
||||
|
||||
printf("mouse_talk: x=%d, y=%d button=%u\n", shoe.mouse.delta_x, shoe.mouse.delta_y, shoe.mouse.button_down);
|
||||
|
||||
shoe.adb.data[1] = shoe.mouse.delta_x & 0x7f;
|
||||
shoe.adb.data[0] = shoe.mouse.delta_y & 0x7f;
|
||||
if (!shoe.mouse.button_down) {
|
||||
//shoe.adb.data[1] |= 0x80;
|
||||
shoe.adb.data[0] |= 0x80;
|
||||
}
|
||||
// printf("mouse_talk: ")
|
||||
|
||||
|
||||
shoe.adb.data_len = 2;
|
||||
|
||||
shoe.mouse.delta_x = 0;
|
||||
shoe.mouse.delta_y = 0;
|
||||
// shoe.mouse.button_down = 0;
|
||||
shoe.mouse.changed = 0;
|
||||
}
|
||||
else
|
||||
shoe.adb.timeout = 1;
|
||||
|
||||
return ;
|
||||
|
||||
case 1:
|
||||
assert(!"Can't handle reg 1");
|
||||
|
||||
case 2:
|
||||
assert(!"Can't handle reg 2");
|
||||
|
||||
case 3:
|
||||
shoe.adb.data[0] = 3; // device address: 3
|
||||
shoe.adb.data[1] = 1; // handler ID: 1
|
||||
shoe.adb.data_len = 2;
|
||||
return ;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void adb_handle_state_zero(uint8_t command_byte, uint8_t is_poll) // "Command" state
|
||||
{
|
||||
via_state_t *via = &shoe.via[0];
|
||||
const uint8_t id = command_byte >> 4; // the target device ID
|
||||
const uint8_t reg = command_byte & 3;
|
||||
|
||||
// Figure out the command type (reset/flush/talk/listen)
|
||||
|
||||
if ((command_byte & 0xf) == 0) // reset
|
||||
shoe.adb.command_type = adb_reset;
|
||||
else if ((command_byte & 0xf) == 1) // flush
|
||||
shoe.adb.command_type = adb_flush;
|
||||
else if (~bmatch(command_byte, xxxx 11 xx)) // talk
|
||||
shoe.adb.command_type = adb_talk;
|
||||
else if (~bmatch(command_byte, xxxx 10 xx)) // listen
|
||||
shoe.adb.command_type = adb_listen;
|
||||
else
|
||||
assert(!"What is this adb state-0 command? xxxx 01xx");
|
||||
|
||||
printf("adb_handle_state_zero: command_byte=0x%02x, id=%u, reg=%u\n", command_byte, id, reg);
|
||||
|
||||
shoe.adb.command_device_id = id;
|
||||
shoe.adb.command_reg = reg;
|
||||
|
||||
// Flush/reset/listen and talk-with-timeout need data_i initialized to 0
|
||||
shoe.adb.data_i = 0;
|
||||
shoe.adb.data_len = 0;
|
||||
|
||||
// If talk, go ask they keyboard/mouse if they have anything to say
|
||||
if (shoe.adb.command_type == adb_talk) {
|
||||
|
||||
if (id == 2) {
|
||||
keyboard_talk(reg);
|
||||
}
|
||||
else if (id == 3) {
|
||||
mouse_talk(reg);
|
||||
}
|
||||
else { // timeout
|
||||
shoe.adb.timeout = 1;
|
||||
}
|
||||
|
||||
// If there was a service request pending for this device, it is now handled.
|
||||
shoe.adb.pending_service_requests &= ~~(1 << id);
|
||||
}
|
||||
|
||||
shoe.adb.poll = 0;
|
||||
|
||||
via->regb |= VIA_REGB_ADB_STATUS;
|
||||
via_raise_interrupt(1, IFR_SHIFT_REG);
|
||||
}
|
||||
|
||||
static void adb_handle_state_one (void) // "Even" state
|
||||
{
|
||||
via_state_t *via = &shoe.via[0];
|
||||
|
||||
printf("adb_handle_state_one: ");
|
||||
if (shoe.adb.poll) {
|
||||
// Upon receiving a service request, the adb controller sends a TALK/reg=0 to the last accessed device
|
||||
adb_handle_state_zero((shoe.adb.command_device_id << 4) | 0x0c, 1);
|
||||
}
|
||||
|
||||
switch (shoe.adb.command_type) {
|
||||
case adb_flush:
|
||||
case adb_reset:
|
||||
assert(!"adb_handle_state_one: unexpected command type");
|
||||
break;
|
||||
|
||||
case adb_talk:
|
||||
printf("adb_talk: ");
|
||||
if (shoe.adb.timeout) {
|
||||
shoe.adb.timeout = 0;
|
||||
via->regb &= ~~VIA_REGB_ADB_STATUS; // adb_status_line cleared == timeout
|
||||
via_raise_interrupt(1, IFR_SHIFT_REG);
|
||||
printf("timeout\n");
|
||||
return ;
|
||||
}
|
||||
|
||||
if (shoe.adb.data_i < shoe.adb.data_len)
|
||||
via->sr = shoe.adb.data[shoe.adb.data_i++];
|
||||
else
|
||||
via->sr = 0;
|
||||
|
||||
printf("set sr = 0x%02x\n", via->sr);
|
||||
|
||||
break;
|
||||
|
||||
case adb_listen:
|
||||
printf("adb_listen: ");
|
||||
if (shoe.adb.timeout) {
|
||||
shoe.adb.timeout = 0;
|
||||
via->regb &= ~~VIA_REGB_ADB_STATUS; // adb_status_line cleared == timeout
|
||||
via_raise_interrupt(1, IFR_SHIFT_REG);
|
||||
printf("timeout\n");
|
||||
return ;
|
||||
}
|
||||
|
||||
if (shoe.adb.data_i < 8)
|
||||
shoe.adb.data[shoe.adb.data_i++] = via->sr;
|
||||
else
|
||||
assert(!"OS made us listen to > 8 bytes");
|
||||
|
||||
printf("loaded sr = 0x%02x\n", via->sr);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
via->regb |= VIA_REGB_ADB_STATUS; // adb_status_line set == didn't-timeout
|
||||
via_raise_interrupt(1, IFR_SHIFT_REG);
|
||||
}
|
||||
|
||||
static void adb_handle_state_two (void) // "Odd" state
|
||||
{
|
||||
via_state_t *via = &shoe.via[0];
|
||||
|
||||
printf("adb_handle_state_two: ");
|
||||
|
||||
// If this transaction was part of a service request, clear the service_request flag now
|
||||
if (shoe.adb.service_request) {
|
||||
shoe.adb.service_request = 0;
|
||||
via->regb &= ~~VIA_REGB_ADB_STATUS; // adb_status_line cleared == service request
|
||||
printf("(service request) ");
|
||||
}
|
||||
else
|
||||
via->regb |= VIA_REGB_ADB_STATUS; // adb_status_line set == no-service request
|
||||
|
||||
switch (shoe.adb.command_type) {
|
||||
case adb_flush:
|
||||
case adb_reset:
|
||||
printf("adb_flush/reset\n");
|
||||
break;
|
||||
|
||||
case adb_talk:
|
||||
printf("adb_talk: ");
|
||||
if (shoe.adb.data_i < shoe.adb.data_len)
|
||||
via->sr = shoe.adb.data[shoe.adb.data_i++];
|
||||
else
|
||||
via->sr = 0;
|
||||
printf("set sr = 0x%02x\n", via->sr);
|
||||
break;
|
||||
|
||||
case adb_listen:
|
||||
printf("adb_listen: ");
|
||||
if (shoe.adb.data_i < 8)
|
||||
shoe.adb.data[shoe.adb.data_i++] = via->sr;
|
||||
else
|
||||
assert(!"OS made us listen to > 8 bytes");
|
||||
printf("read sr = 0x%02x\n", via->sr);
|
||||
break;
|
||||
}
|
||||
|
||||
via_raise_interrupt(1, IFR_SHIFT_REG);
|
||||
}
|
||||
|
||||
static void adb_handle_state_three (void) // "idle" state
|
||||
{
|
||||
printf("adb_handle_state_three: completed for id %u\n", shoe.adb.command_device_id);
|
||||
|
||||
switch (shoe.adb.command_type) {
|
||||
case adb_reset:
|
||||
case adb_flush:
|
||||
case adb_talk:
|
||||
break;
|
||||
|
||||
case adb_listen:
|
||||
printf("adb_handle_state_three: listen completed for id %u, reg %u, data_len = %u {%02x %02x}\n",
|
||||
shoe.adb.command_device_id, shoe.adb.command_reg, shoe.adb.data_i, shoe.adb.data[0], shoe.adb.data[1]);
|
||||
break;
|
||||
}
|
||||
|
||||
adb_start_service_request();
|
||||
}
|
||||
|
||||
void adb_handle_state_change(uint8_t old_state, uint8_t new_state)
|
||||
{
|
||||
via_state_t *via = &shoe.via[0];
|
||||
|
||||
printf("%s: lock\n", __func__); fflush(stdout);
|
||||
assert(pthread_mutex_lock(&shoe.adb.lock) == 0);
|
||||
|
||||
shoe.adb.state = new_state;
|
||||
|
||||
switch (new_state) {
|
||||
case 0:
|
||||
shoe.adb.command_byte = via->sr;
|
||||
adb_handle_state_zero(shoe.adb.command_byte, 0);
|
||||
break ;
|
||||
|
||||
case 1:
|
||||
adb_handle_state_one();
|
||||
break ;
|
||||
|
||||
case 2:
|
||||
adb_handle_state_two();
|
||||
break ;
|
||||
|
||||
case 3:
|
||||
adb_handle_state_three();
|
||||
break ;
|
||||
}
|
||||
|
||||
printf("%s: unlock\n", __func__); fflush(stdout);
|
||||
pthread_mutex_unlock(&shoe.adb.lock);
|
||||
}
|
||||
|
4102
core/atrap_tab.c
Normal file
4102
core/atrap_tab.c
Normal file
File diff suppressed because it is too large
Load Diff
329
core/coff.c
Normal file
329
core/coff.c
Normal file
@ -0,0 +1,329 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <stdint.h>
|
||||
#include "shoebill.h"
|
||||
#include "coff.h"
|
||||
|
||||
void symb_inorder(rb_node *cur) {
|
||||
const coff_symbol *sym = (coff_symbol*)cur->value;
|
||||
if (!sym)
|
||||
return ;
|
||||
symb_inorder(cur->left);
|
||||
printf("0x%x %s\n", cur->key, sym->name);
|
||||
symb_inorder(cur->right);
|
||||
}
|
||||
|
||||
// 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_parser(const char *path)
|
||||
{
|
||||
FILE *f;
|
||||
uint8_t rawhead[20], *ptr;
|
||||
uint32_t i;
|
||||
coff_file *cf = NULL;
|
||||
|
||||
// Open the file
|
||||
f = fopen(path, "r");
|
||||
if (!f) {
|
||||
printf("coff_parser: I couldn't open that binary for reading (%s)\n", path);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Pull out 20 bytes (the file header)
|
||||
if (fread(rawhead, 20, 1, f) != 1) {
|
||||
printf("coff_parser: error: this binary is missing its file header\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Allocate a coff_file and copy in the header
|
||||
cf = (coff_file*)calloc(1, sizeof(coff_file));
|
||||
ptr = rawhead;
|
||||
cf->magic = be2native(&ptr, 2);
|
||||
cf->num_sections = be2native(&ptr, 2);
|
||||
cf->timestamp = be2native(&ptr, 4);
|
||||
cf->symtab_offset = be2native(&ptr, 4);
|
||||
cf->num_symbols = be2native(&ptr, 4);
|
||||
cf->opt_header_len = be2native(&ptr, 2);
|
||||
cf->flags = be2native(&ptr, 2);
|
||||
|
||||
// A little sanity checking...
|
||||
if (cf->magic != 0x150) {
|
||||
printf("coff_parse: I don't recognize this magic number: 0x%04x\n", cf->magic);
|
||||
goto fail;
|
||||
}
|
||||
else if (cf->num_sections != 3) {
|
||||
//printf("coff_parse: warning: there are %u sections in this file (not 3, like I expect)\n", cf->num_sections);
|
||||
// FIXME: investigate all the other possible section types
|
||||
}
|
||||
|
||||
// pull out cf->opt_header bytes (a.out-format header, I guess?)
|
||||
if (cf->opt_header_len > 0) {
|
||||
uint8_t *opt = malloc(cf->opt_header_len);
|
||||
if (fread(opt, cf->opt_header_len, 1, f) != 1) {
|
||||
printf("coff_parse: I ran out of data pulling the optional header (%u bytes)\n", cf->opt_header_len);
|
||||
free(opt);
|
||||
goto fail;
|
||||
}
|
||||
cf->opt_header = opt;
|
||||
}
|
||||
|
||||
// start pulling out sections
|
||||
cf->sections = calloc(cf->num_sections, sizeof(coff_section));
|
||||
for (i=0; i<cf->num_sections; i++) {
|
||||
// read the header
|
||||
uint8_t rawsec[40];
|
||||
if (fread(rawsec, 40, 1, f) != 1) {
|
||||
printf("coff_parse: I ran out of data pulling section #%u\n", i+1);
|
||||
goto fail;
|
||||
}
|
||||
// and copy it into cf->sections[i]
|
||||
memcpy(cf->sections[i].name, rawsec, 8);
|
||||
ptr = &rawsec[8];
|
||||
cf->sections[i].p_addr = be2native(&ptr, 4);
|
||||
cf->sections[i].v_addr = be2native(&ptr, 4);
|
||||
cf->sections[i].sz = be2native(&ptr, 4);
|
||||
cf->sections[i].data_ptr = be2native(&ptr, 4);
|
||||
cf->sections[i].reloc_ptr = be2native(&ptr, 4);
|
||||
cf->sections[i].line_ptr = be2native(&ptr, 4);
|
||||
cf->sections[i].num_relocs = be2native(&ptr, 2);
|
||||
cf->sections[i].num_lines = be2native(&ptr, 2);
|
||||
cf->sections[i].flags = be2native(&ptr, 4);
|
||||
|
||||
// a little bit of sanity checking:
|
||||
if (cf->sections[i].v_addr != cf->sections[i].p_addr) {
|
||||
//printf("coff_parse: warning: section %u's virtual_addr and physical_addr don't match: p=%x v=%x\n",
|
||||
// i+1, cf->sections[i].p_addr, cf->sections[i].v_addr);
|
||||
// This is okay for the unix kernel
|
||||
}
|
||||
}
|
||||
|
||||
// pull out the corresponding raw data for each section
|
||||
for (i=0; i<cf->num_sections; i++) {
|
||||
uint8_t *data;
|
||||
|
||||
// don't bother if there's no data
|
||||
if (cf->sections[i].sz == 0) {
|
||||
continue ;
|
||||
}
|
||||
|
||||
// don't bother if it's .bss
|
||||
if (memcmp(cf->sections[i].name, ".bss\0\0\0\0", 8)==0) {
|
||||
continue ;
|
||||
}
|
||||
|
||||
// seek to the position in the binary that holds this section's raw data
|
||||
if (fseek(f, cf->sections[i].data_ptr, SEEK_SET) != 0) {
|
||||
printf("coff_parse: I couldn't seek to 0x%x in section %u\n", cf->sections[i].data_ptr, i+1);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// load the data and attach it to the section struct
|
||||
data = malloc(cf->sections[i].sz); // FIXME: sz might not be a sane value
|
||||
if (fread(data, cf->sections[i].sz, 1, f) != 1) {
|
||||
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);
|
||||
free(data);
|
||||
goto fail;
|
||||
}
|
||||
cf->sections[i].data = data;
|
||||
}
|
||||
|
||||
// pull out the symbol table
|
||||
|
||||
if (cf->num_symbols == 0) // if num_symbols==0, symtab_offset may be bogus
|
||||
return cf; // just return
|
||||
|
||||
cf->func_tree = rb_new();
|
||||
//printf("func_tree = %llx, *func_tree = %llx\n", cf->func_tree, *cf->func_tree);
|
||||
cf->symbols = (coff_symbol*)calloc(sizeof(coff_symbol), cf->num_symbols);
|
||||
|
||||
// Seek to the symbol table
|
||||
if (fseek(f, cf->symtab_offset, SEEK_SET) != 0) {
|
||||
printf("coff_parse: I couldn't seek to symtab_offset, 0x%x\n", cf->symtab_offset);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (i=0; i < cf->num_symbols; i++) {
|
||||
uint8_t raw_symb[18];
|
||||
if (fread(raw_symb, 18, 1, f) != 1) {
|
||||
printf("coff_parse: I ran out of data pulling symbol #%u\n", i+1);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// load the name
|
||||
if (*((uint32_t*)raw_symb) == 0) {
|
||||
uint8_t tmp_name[256];
|
||||
uint32_t j, offset, idx = cf->symtab_offset + 18*cf->num_symbols;
|
||||
for (j=4, offset=0; j<8; j++) offset = (offset<<8) | raw_symb[j];
|
||||
idx += offset;
|
||||
|
||||
// printf("Loading from external: base idx=0x%x, offset=%u, addr=0x%x\n", idx-offset, offset, idx);
|
||||
|
||||
if (fseek(f, idx, SEEK_SET) != 0) {
|
||||
printf("coff_parse: I ran out of data pulling symbol %u's name (idx=0x%x)\n", i+1, idx);
|
||||
goto fail;
|
||||
}
|
||||
for (j=0; (fread(&tmp_name[j], 1, 1, f)==1) && tmp_name[j]; j++) {
|
||||
if (j >= 255) {
|
||||
// printf("coff_parse: this symbol's name is too long: %u\n", i+1);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
cf->symbols[i].name = malloc(j+1);
|
||||
memcpy(cf->symbols[i].name, tmp_name, j);
|
||||
cf->symbols[i].name[j] = 0;
|
||||
fseek(f, cf->symtab_offset + (i+1)*18, SEEK_SET);
|
||||
//printf("cf->symtab_offset = 0x%x, i=%u, (i+1)*18 = %u\n",
|
||||
//cf->symtab_offset, i, (i+1)*18);
|
||||
//printf("seeking back to 0x%x\n", cf->symtab_offset + (i+1)*18);
|
||||
}
|
||||
else {
|
||||
uint8_t tmp_name[9];
|
||||
memcpy(tmp_name, raw_symb, 8);
|
||||
tmp_name[8] = 0;
|
||||
cf->symbols[i].name = strdup((char*)tmp_name);
|
||||
}
|
||||
|
||||
ptr = &raw_symb[8];
|
||||
cf->symbols[i].value = be2native(&ptr, 4);
|
||||
cf->symbols[i].scnum = be2native(&ptr, 2);
|
||||
cf->symbols[i].type = be2native(&ptr, 2);
|
||||
cf->symbols[i].sclass = raw_symb[16];
|
||||
cf->symbols[i].numaux = raw_symb[17];
|
||||
|
||||
// FIXME: I need to handle numaux > 0.
|
||||
|
||||
//if (cf->symbols[i].numaux > 0) {
|
||||
printf("%s\n", cf->symbols[i].name);
|
||||
printf("value=0x%08x scnum=0x%04x type=0x%04x sclass=0x%02x numaux=%u\n\n",
|
||||
cf->symbols[i].value, cf->symbols[i].scnum, cf->symbols[i].type, cf->symbols[i].sclass, cf->symbols[i].numaux);
|
||||
//}
|
||||
|
||||
|
||||
if (cf->symbols[i].sclass == 2 || cf->symbols[i].sclass == 3) {
|
||||
rb_insert(cf->func_tree, cf->symbols[i].value, &cf->symbols[i], NULL);
|
||||
//printf("%s addr=0x%x\n", cf->symbols[i].name, cf->symbols[i].value);
|
||||
}
|
||||
// printf("%u: %s (class=%u)\n", i+1, cf->symbols[i].name, cf->symbols[i].sclass);
|
||||
|
||||
}
|
||||
|
||||
// symb_inorder(*cf->func_tree);
|
||||
|
||||
// and we're done
|
||||
return cf;
|
||||
|
||||
fail:
|
||||
if (cf) {
|
||||
if (cf->opt_header) {
|
||||
free(cf->opt_header);
|
||||
}
|
||||
if (cf->sections) {
|
||||
for (i=0; i<cf->num_sections; i++) {
|
||||
if (cf->sections[i].data) {
|
||||
free(cf->sections[i].data);
|
||||
}
|
||||
}
|
||||
free(cf->sections);
|
||||
}
|
||||
free(cf);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// dump some data about a coff_file structure
|
||||
void print_coff_info(coff_file *coff)
|
||||
{
|
||||
char timebuf[32];
|
||||
time_t timestamp = coff->timestamp;
|
||||
uint32_t i;
|
||||
|
||||
printf("Magic = 0x%04x\n", coff->magic);
|
||||
printf("Linked on %s", ctime_r(×tamp, timebuf));
|
||||
printf("Num sections = %u\n", coff->num_sections);
|
||||
|
||||
printf("debug: num_symbols=%u, symtab_offset=0x%x\n", coff->num_symbols, coff->symtab_offset);
|
||||
|
||||
for (i=0; i<coff->num_sections; i++) {
|
||||
coff_section *s = &coff->sections[i];
|
||||
char name[9];
|
||||
memcpy(name, s->name, 8);
|
||||
name[8] = 0;
|
||||
printf("Section #%u: %s\n", i+1, name);
|
||||
printf("\taddr=0x%08x, len=0x%x, (debug: paddr=0x%08x, flags=0x%08x)\n", s->v_addr, s->sz, s->p_addr, s->flags);
|
||||
}
|
||||
}
|
||||
|
||||
coff_symbol* coff_find_symbol(coff_file *coff, const char *name)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i=0; i < coff->num_symbols; i++) {
|
||||
if (strcmp(coff->symbols[i].name, name) == 0)
|
||||
return &coff->symbols[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
coff_symbol* coff_find_func(coff_file *coff, uint32_t addr)
|
||||
{
|
||||
rb_node *cur;
|
||||
coff_symbol *last = NULL;
|
||||
|
||||
// printf("coff->num_symbols = %u\n", coff->num_symbols);
|
||||
if (coff->num_symbols == 0)
|
||||
return NULL;
|
||||
cur = *coff->func_tree;
|
||||
|
||||
while (cur) {
|
||||
// printf("... iterating\n");
|
||||
if (addr < cur->key)
|
||||
cur = cur->left;
|
||||
else if (addr > cur->key) {
|
||||
last = cur->value;
|
||||
cur = cur->right;
|
||||
}
|
||||
else
|
||||
return cur->value;
|
||||
}
|
||||
|
||||
return last;
|
||||
}
|
||||
|
||||
|
||||
// big endian -> native int
|
||||
uint32_t be2native (uint8_t **dat, uint32_t bytes)
|
||||
{
|
||||
uint32_t i, v = 0;
|
||||
for (i=0; i < bytes; i++)
|
||||
v = (v<<8) | (*dat)[i];
|
||||
(*dat) += bytes;
|
||||
return v;
|
||||
}
|
||||
|
84
core/coff.h
Normal file
84
core/coff.h
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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_parser(const char *path);
|
||||
uint32_t be2native (uint8_t **dat, uint32_t bytes);
|
||||
void print_coff_info(coff_file *coff);
|
||||
|
||||
|
||||
#endif // _COFF_H
|
908
core/core_api.c
Normal file
908
core/core_api.c
Normal file
@ -0,0 +1,908 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include "shoebill.h"
|
||||
#include "coff.h"
|
||||
#include "core_api.h"
|
||||
|
||||
static uint64_t sub_tv (const struct timeval a, const struct timeval b)
|
||||
{
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
void printregs()
|
||||
{
|
||||
printf("[d0]%08x [d1]%08x [d2]%08x [d3]%08x\n", shoe.d[0], shoe.d[1], shoe.d[2], shoe.d[3]);
|
||||
printf("[d4]%08x [d5]%08x [d6]%08x [d7]%08x\n", shoe.d[4], shoe.d[5], shoe.d[6], shoe.d[7]);
|
||||
printf("[a0]%08x [a1]%08x [a2]%08x [a3]%08x\n", shoe.a[0], shoe.a[1], shoe.a[2], shoe.a[3]);
|
||||
printf("[a4]%08x [a5]%08x [a6]%08x [a7]%08x\n", shoe.a[4], shoe.a[5], shoe.a[6], shoe.a[7]);
|
||||
printf("[pc]%08x [sr]%c%c%c%c%c%c%c [tc]%08x\n", shoe.pc,
|
||||
sr_s()?'S':'s',
|
||||
sr_m()?'M':'m',
|
||||
sr_x()?'X':'x',
|
||||
sr_n()?'N':'n',
|
||||
sr_z()?'Z':'z',
|
||||
sr_v()?'V':'v',
|
||||
sr_c()?'C':'c',
|
||||
shoe.tc
|
||||
);
|
||||
|
||||
printf("[vbr]%08x\n", shoe.vbr);
|
||||
|
||||
printf("srp: ");
|
||||
print_mmu_rp(shoe.srp);
|
||||
|
||||
printf("crp: ");
|
||||
print_mmu_rp(shoe.crp);
|
||||
|
||||
printf("tc: e=%u sre=%u fcl=%u ps=%u is=%u (tia=%u tib=%u tic=%u tid=%u)\n",
|
||||
tc_enable(), tc_sre(), tc_fcl(), tc_ps(), tc_is(), tc_tia(), tc_tib(), tc_tic(), tc_tid());
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void print_pc()
|
||||
{
|
||||
char str[1024];
|
||||
uint8_t binary[32];
|
||||
uint32_t i;
|
||||
uint32_t len;
|
||||
const char *name = NULL;
|
||||
|
||||
if ((shoe.pc >= 0x40000000) && (shoe.pc < 0x50000000)) {
|
||||
uint32_t i, addr = shoe.pc % (shoe.physical_rom_size);
|
||||
for (i=0; macii_rom_symbols[i].name; i++) {
|
||||
if (macii_rom_symbols[i].addr > addr) {
|
||||
break;
|
||||
}
|
||||
name = macii_rom_symbols[i].name;
|
||||
}
|
||||
}
|
||||
else if (sr_s()) { // these symbols are only meaningful in supervisor mode
|
||||
coff_symbol *symb = coff_find_func(shoe.coff, shoe.pc);
|
||||
if (symb && strlen(symb->name))
|
||||
name = symb->name;
|
||||
}
|
||||
else {
|
||||
if ((shoe.pc >= 0x10000000) && (shoe.pc < 0x20000000)) {
|
||||
uint32_t i, addr = shoe.pc % (shoe.physical_rom_size);
|
||||
for (i=0; macii_rom_symbols[i].name; i++) {
|
||||
if (macii_rom_symbols[i].addr > addr) {
|
||||
break;
|
||||
}
|
||||
name = macii_rom_symbols[i].name;
|
||||
}
|
||||
}
|
||||
/*else {
|
||||
coff_symbol *symb = coff_find_func(shoe.launch, shoe.pc);
|
||||
if (symb)
|
||||
name = symb->name;
|
||||
}*/
|
||||
}
|
||||
|
||||
const uint16_t old_abort = shoe.abort;
|
||||
shoe.suppress_exceptions = 1;
|
||||
|
||||
for (i=0; i<32; i++) {
|
||||
binary[i] = (uint8_t) lget(shoe.pc+i, 1);
|
||||
}
|
||||
|
||||
disassemble_inst(binary, shoe.pc, str, &len);
|
||||
|
||||
printf("*0x%08x %s [ ", shoe.pc, name ? name : "");
|
||||
for (i=0; i<len; i+=2) {
|
||||
printf("%02x%02x ", binary[i], binary[i+1]);
|
||||
}
|
||||
printf("] %s\n", str);
|
||||
|
||||
shoe.abort = old_abort;
|
||||
shoe.suppress_exceptions = 0;
|
||||
|
||||
if (name && strcmp(name, "ui_ioctl")==0)
|
||||
printregs();
|
||||
|
||||
}
|
||||
|
||||
void shoebill_start()
|
||||
{
|
||||
pthread_mutex_unlock(&shoe.cpu_thread_lock);
|
||||
pthread_mutex_unlock(&shoe.via_clock_thread_lock);
|
||||
}
|
||||
|
||||
static void _cpu_loop_fast()
|
||||
{
|
||||
while (1) {
|
||||
if (shoe.cpu_thread_notifications) {
|
||||
|
||||
// If there's an interrupt pending
|
||||
if (shoe.cpu_thread_notifications & 0xff) {
|
||||
// process_pending_interrupt() may clear SHOEBILL_STATE_STOPPED
|
||||
process_pending_interrupt();
|
||||
}
|
||||
|
||||
if (shoe.cpu_thread_notifications & SHOEBILL_STATE_STOPPED) {
|
||||
continue; // FIXME: yield or block on a condition variable here
|
||||
}
|
||||
|
||||
if (shoe.cpu_thread_notifications & SHOEBILL_STATE_SWITCH_MODE)
|
||||
return ;
|
||||
}
|
||||
|
||||
cpu_step();
|
||||
}
|
||||
}
|
||||
|
||||
static void _cpu_loop_debug()
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void *_cpu_thread (void *arg) {
|
||||
|
||||
pthread_mutex_lock(&shoe.cpu_thread_lock);
|
||||
|
||||
while (1) {
|
||||
shoe.cpu_thread_notifications &= ~SHOEBILL_STATE_SWITCH_MODE;
|
||||
|
||||
if (shoe.cpu_mode == CPU_MODE_FAST)
|
||||
_cpu_loop_fast();
|
||||
else if (shoe.cpu_mode == CPU_MODE_DEBUG)
|
||||
_cpu_loop_debug();
|
||||
else if (shoe.cpu_mode == CPU_MODE_STEPI) {
|
||||
cpu_step();
|
||||
shoe.cpu_mode = CPU_MODE_STEPI_COMPLETE;
|
||||
while (shoe.cpu_mode == CPU_MODE_STEPI_COMPLETE)
|
||||
pthread_yield_np();
|
||||
}
|
||||
else if (shoe.cpu_mode == CPU_MODE_FREEZE) {
|
||||
pthread_mutex_lock(&shoe.cpu_freeze_lock);
|
||||
pthread_mutex_unlock(&shoe.cpu_freeze_lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
* stuff like: which scsi device/partition is root?
|
||||
*/
|
||||
struct __attribute__ ((__packed__)) kernel_info {
|
||||
// Auto data
|
||||
uint32_t auto_magic;
|
||||
uint32_t auto_id[16];
|
||||
uint32_t auto_version[16];
|
||||
uint32_t auto_command;
|
||||
|
||||
uint16_t root_ctrl;
|
||||
uint8_t root_drive;
|
||||
uint8_t root_cluster;
|
||||
|
||||
struct __attribute__ ((__packed__)) sect_info {
|
||||
uint32_t vstart;
|
||||
uint32_t pstart;
|
||||
uint32_t size;
|
||||
} si[3];
|
||||
|
||||
uint16_t machine_type; // Not the same as gestalt IDs!
|
||||
uint32_t drive_queue_offset;
|
||||
uint16_t ki_flags;
|
||||
uint8_t ki_version; // always 1
|
||||
uint8_t root_partition;
|
||||
uint16_t swap_ctrl;
|
||||
uint8_t swap_drive;
|
||||
uint8_t swap_partition;
|
||||
|
||||
// A series of DrvQEl (drive queue elements) follow this structure
|
||||
};
|
||||
|
||||
/* Inside Macintosh: Files 2-85 throughtfully provides this information
|
||||
* on the secret internal flags:
|
||||
*
|
||||
* The File Manager also maintains four flag bytes preceding each drive queue element.
|
||||
* These bytes contain the following information:
|
||||
* Byte (from low address to high address)
|
||||
* 0 Bit 7=1 if the volume on the drive is locked
|
||||
* 1 0 if no disk in drive; 1 or 2 if disk in drive; 8 if nonejectable disk in drive; $FC–$FF if
|
||||
* disk was ejected within last 1.5 seconds; $48 if disk in drive is nonejectable but driver
|
||||
* wants a call
|
||||
* 2 Used internally during system startup
|
||||
* 3 Bit 7=0 if disk is single-sided
|
||||
* You can read these flags by subtracting 4 bytes from the beginning of a drive queue element,
|
||||
* as illustrated in Listing 2-11.
|
||||
*/
|
||||
|
||||
struct __attribute__ ((__packed__)) drive_queue_element {
|
||||
/* Secret internal flags */
|
||||
uint8_t unknown_1 : 7;
|
||||
uint8_t is_locked : 1; // 1 if locked, 0 -> unlocked
|
||||
uint8_t is_in_drive;
|
||||
uint8_t unknown_2;
|
||||
uint8_t unknown_3 : 7;
|
||||
uint8_t double_sided : 1; // 0 if single-sided, presumably 1 if HDD or double-sided
|
||||
/* --- */
|
||||
|
||||
uint32_t next_offset;
|
||||
int16_t qType; // 1 -> both dQDrvSz and dQDrvSz2 are used
|
||||
int16_t dQDrive; // drive number (scsi target ID)
|
||||
int16_t dQRefNum; // Driver reference number (I'm thinking -33 is the right value here)
|
||||
int16_t dQFSID; // Filesystem identifier (0 is best, I think)
|
||||
uint16_t dQDrvSz; // low 16 bits of numblocks
|
||||
uint16_t dQDrvSz2; // high 16 bits of numblocks
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialize the Macintosh lomem variables, or some of them anyways.
|
||||
* Debugging weird errors revealed that A/UX actually cares about
|
||||
* some of them.
|
||||
*/
|
||||
static void _init_macintosh_lomem_globals (const uint32_t offset)
|
||||
{
|
||||
/*
|
||||
* FIXME: explain what I'm doing here
|
||||
*/
|
||||
|
||||
uint32_t i;
|
||||
|
||||
// Fill the entire lomem space with 0xBB to make debugging easier
|
||||
// if somebody tries to read an uninitialized lomem value
|
||||
// for (i=0; i<0x4000; i++)
|
||||
// pset(offset + i, 1, 0xBB);
|
||||
|
||||
// There are the lomem globals that matter, apparently
|
||||
|
||||
pset(offset+0x12f, 1, 0x02); // CPUFlag = 0x02 (MC68020)
|
||||
pset(offset+0x31a, 4, 0x00ffffff); // Lo3Bytes (always 0x00ffffff)
|
||||
pset(offset+0x28e, 2, 0x3fff); // ROM85 (always 0x3fff, I think?)
|
||||
// universal info ptr. is allowed to be null on Mac II, (I THINK)
|
||||
pset(offset+0xdd8, 4, 0);
|
||||
pset(offset+0x1d4, 4, 0x50000000); // VIA (via1 ptr)
|
||||
pset(offset+0x1d8, 4, 0x50004000); // SCC
|
||||
|
||||
#define hwCbSCSI (1<<15)
|
||||
#define hwCbClock (1<<14)
|
||||
#define hwCbExPRAM (1<<13)
|
||||
#define hwCbFPU (1<<12)
|
||||
#define hwCbMMU (1<<11)
|
||||
#define hwCbADB (1<<10)
|
||||
#define hwCbAUX (1<<9) /* not sure if I need to set this one */
|
||||
const uint16_t HWCfgFlags = hwCbSCSI | hwCbClock | hwCbFPU | hwCbMMU | hwCbADB;
|
||||
pset(0xb22, 2, HWCfgFlags); // HWCfgFlags
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Setup and blast the kernel_info structure into memory
|
||||
* 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)
|
||||
{
|
||||
struct kernel_info ki, *p;
|
||||
uint32_t i, p_addr;
|
||||
|
||||
p_addr = offset + 0x3c00;
|
||||
p = (struct kernel_info*) (uint64_t)p_addr;
|
||||
|
||||
/*
|
||||
* On boot, the kernel looks for this magic constant in d0 to see whether
|
||||
* the bootloader setup a kernel_info structure.
|
||||
*/
|
||||
shoe.d[0] = 0x536d7201; // "Smr\1"? Something version 1?
|
||||
shoe.a[0] = (uint32_t)p; // Address of the kernel_info structure
|
||||
|
||||
/* ----- Setup kernel info structure ------ */
|
||||
|
||||
ki.auto_magic = 0x50696773; // 'Pigs' (Pigs in space?)
|
||||
|
||||
for (i=0; i<16; i++) {
|
||||
ki.auto_id[i] = 0x0000ffff;
|
||||
ki.auto_version[i] = 0;
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
/*
|
||||
* Note: ctrl -> SCSI controller chip
|
||||
* drive -> SCSI Target ID
|
||||
* partition -> partition
|
||||
* cluster -> A set of partitions on a given drive
|
||||
* (Used by escher/eschatology somehow)
|
||||
*/
|
||||
|
||||
ki.root_ctrl = control->root_ctrl;
|
||||
ki.swap_ctrl = control->swap_ctrl;
|
||||
|
||||
ki.root_drive = control->root_drive;
|
||||
ki.swap_drive = control->swap_drive;
|
||||
|
||||
ki.root_partition = control->root_partition;
|
||||
ki.swap_partition = control->swap_partition;
|
||||
|
||||
ki.root_cluster = control->root_cluster;
|
||||
|
||||
// Find the text, data, and bss segments in the kernel
|
||||
for (i = 0; i < shoe.coff->num_sections; i++) {
|
||||
coff_section *s = &shoe.coff->sections[i];
|
||||
uint8_t sect;
|
||||
|
||||
if (strcmp(s->name, ".text") == 0)
|
||||
sect = 0;
|
||||
else if (strcmp(s->name, ".data") == 0)
|
||||
sect = 1;
|
||||
else if (strcmp(s->name, ".bss") == 0)
|
||||
sect = 2;
|
||||
else
|
||||
continue;
|
||||
|
||||
ki.si[sect].vstart = s->v_addr;
|
||||
ki.si[sect].pstart = s->p_addr;
|
||||
ki.si[sect].size = s->sz;
|
||||
}
|
||||
|
||||
ki.machine_type = 4; // Macintosh II?
|
||||
|
||||
// +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_version = 1;
|
||||
|
||||
/* ----- Copy ki into memory ----- */
|
||||
#define ki_pset(_f) pset((uint32_t)&p->_f, sizeof(p->_f), ki._f)
|
||||
ki_pset(auto_magic);
|
||||
for (i=0; i<16; i++) {
|
||||
ki_pset(auto_id[i]);
|
||||
ki_pset(auto_version[i]);
|
||||
}
|
||||
ki_pset(auto_command);
|
||||
|
||||
ki_pset(root_ctrl);
|
||||
ki_pset(root_drive);
|
||||
ki_pset(root_cluster);
|
||||
|
||||
for (i=0; i<3; i++) {
|
||||
ki_pset(si[i].vstart);
|
||||
ki_pset(si[i].pstart);
|
||||
ki_pset(si[i].size);
|
||||
}
|
||||
|
||||
ki_pset(machine_type);
|
||||
ki_pset(drive_queue_offset);
|
||||
ki_pset(ki_flags);
|
||||
ki_pset(ki_version);
|
||||
ki_pset(root_partition);
|
||||
ki_pset(swap_ctrl);
|
||||
ki_pset(swap_drive);
|
||||
ki_pset(swap_partition);
|
||||
|
||||
/* ---- Copy DrvQEl elements into memory ---- */
|
||||
// FIXME: btw, this is probably wrong. DrvQEl elements are supposed to be partitions, I think
|
||||
|
||||
uint32_t addr = p_addr + sizeof(struct kernel_info);
|
||||
uint32_t num_disks = 0;
|
||||
for (i=0; i<7; i++)
|
||||
if (disks[i].f) num_disks++;
|
||||
|
||||
for (i=0; i<7; i++) {
|
||||
if (disks[i].f == NULL)
|
||||
continue;
|
||||
|
||||
num_disks--;
|
||||
|
||||
pset(addr, 4, 0x00080000); addr += 4; // magic internal flags (non-ejectable)
|
||||
pset(addr, 4, num_disks ? 0x14 : 0); addr += 4; // offset to next drvqel
|
||||
pset(addr, 2, 1); addr += 2; // Use both dQDrvSzs
|
||||
pset(addr, 2, i | 8); addr += 2; // SCSI ID (with bit 3 always set?)
|
||||
// pset(addr, 2, 0xffdf); addr += 2; // dQRefNum: not sure if this is right
|
||||
pset(addr, 2, (i | 0x20) ^ 0xffff); addr += 2; // dQRefNum: not sure if this is right
|
||||
pset(addr, 2, 0); addr += 2; // FSID
|
||||
pset(addr, 2, disks[i].num_blocks & 0xffff); addr += 2; // low 16 bits of num_blocks
|
||||
pset(addr, 2, disks[i].num_blocks >> 16); addr += 2; // high bits |