diff --git a/Makefile b/Makefile index 35f1e8f..3d26841 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,13 @@ CC = clang -CFLAGS = -O4 -arch x86_64 -Wno-deprecated-declarations +CFLAGS = -O3 -arch i386 -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 +shoebill: make_core +# shoebill: make_core test.c +# $(CC) $(LFLAGS) $(CFLAGS) -L intermediates -l shoebill_core test.c -o shoebill make_core: $(MAKE) -C core -j 4 diff --git a/core/Makefile b/core/Makefile index 83f3596..fec5999 100644 --- a/core/Makefile +++ b/core/Makefile @@ -1,20 +1,34 @@ CC = clang -CFLAGS = -O4 -ggdb -arch x86_64 -Wno-deprecated-declarations +CFLAGS = -O3 -flto -ggdb -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 +NEED_NOTHING = atrap_tab coff exception floppy macii_symbols redblack scsi toby_frame_buffer video core_api filesystem debug_server +# Object files that can be compiled directly from the source OBJ_NEED_NOTHING = $(patsubst %,$(TEMP)/%.o,$(NEED_NOTHING)) + +# Object files than need preprocessing with macro.pl OBJ_NEED_PREPROCESSING = $(patsubst %,$(TEMP)/%.o,$(NEED_PREPROCESSING)) + +# Object files that depend on the instruction decoder 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)) + + +# All the object files compiled for x86_64 +OBJ_x86_64 = $(OBJ_NEED_NOTHING) $(OBJ_NEED_PREPROCESSING) $(OBJ_NEED_DECODER) + +# The object files compiled for i386 (the same as x86_64 files, but with .i386 appended) +OBJ_i386 = $(patsubst %,%.i386,$(OBJ_x86_64)) + + MACRO = perl macro.pl TEMP = ../intermediates @@ -22,18 +36,29 @@ 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) +$(TEMP)/libshoebill_core.a: $(TEMP) $(DEPS) $(OBJ_x86_64) + libtool -static -v -o $(TEMP)/libshoebill_core.a.x86_64 $(OBJ_x86_64) + libtool -static -v -o $(TEMP)/libshoebill_core.a.i386 $(OBJ_i386) + lipo -create -output $(TEMP)/libshoebill_core.a $(TEMP)/libshoebill_core.a.x86_64 $(TEMP)/libshoebill_core.a.i386 + + +# Split object files into i386/x86_64 versions, since it seems that libtool is unable to +# link a static universal library for -O4 object files. +# x86_64 object files have the form "intermediates/.o +# i386 object files have the form "intermediates/.o.i386 # Build object files $(OBJ_NEED_NOTHING): $(TEMP)/%.o: %.c $(DEPS) - $(CC) -c $(CFLAGS) $< -o $@ + $(CC) -c -arch x86_64 $(CFLAGS) $< -o $@ + $(CC) -c -arch i386 $(CFLAGS) $< -o $@.i386 $(OBJ_NEED_PREPROCESSING): $(TEMP)/%.o: $(TEMP)/%.post.c $(DEPS) - $(CC) -c $(CFLAGS) $< -o $@ + $(CC) -c -arch x86_64 $(CFLAGS) $< -o $@ + $(CC) -c -arch i386 $(CFLAGS) $< -o $@.i386 $(OBJ_NEED_DECODER): $(TEMP)/%.o: $(TEMP)/%.post.c $(DEPS) $(TEMP)/dis_decoder_guts.c $(TEMP)/inst_decoder_guts.c - $(CC) -c $(CFLAGS) $< -o $@ + $(CC) -c -arch x86_64 $(CFLAGS) $< -o $@ + $(CC) -c -arch i386 $(CFLAGS) $< -o $@.i386 # Preprocess C files $(POST_PREPROCESSING): $(TEMP)/%.post.c: %.c $(DEPS) diff --git a/core/adb.c b/core/adb.c index 0bfbf1e..deb385a 100644 --- a/core/adb.c +++ b/core/adb.c @@ -148,11 +148,22 @@ static void mouse_talk(uint8_t reg) case 0: if (shoe.mouse.changed) { + const int32_t hi_delta_limit = 32; + const int32_t low_delta_limit = -32; - printf("mouse_talk: x=%d, y=%d button=%u\n", shoe.mouse.delta_x, shoe.mouse.delta_y, shoe.mouse.button_down); + int32_t x = shoe.mouse.delta_x; + int32_t y = shoe.mouse.delta_y; - shoe.adb.data[1] = shoe.mouse.delta_x & 0x7f; - shoe.adb.data[0] = shoe.mouse.delta_y & 0x7f; + //printf("mouse_talk: x=%d, y=%d button=%u\n", shoe.mouse.delta_x, shoe.mouse.delta_y, shoe.mouse.button_down); + + + if (x > hi_delta_limit) x = hi_delta_limit; + if (x < low_delta_limit) x = low_delta_limit; + if (y > hi_delta_limit) y = hi_delta_limit; + if (y < low_delta_limit) y = low_delta_limit; + + shoe.adb.data[1] = x & 0x7f; + shoe.adb.data[0] = y & 0x7f; if (!shoe.mouse.button_down) { //shoe.adb.data[1] |= 0x80; shoe.adb.data[0] |= 0x80; @@ -164,7 +175,7 @@ static void mouse_talk(uint8_t reg) shoe.mouse.delta_x = 0; shoe.mouse.delta_y = 0; - // shoe.mouse.button_down = 0; + shoe.mouse.changed = 0; } else diff --git a/core/core_api.c b/core/core_api.c index 514a527..3bfee7f 100644 --- a/core/core_api.c +++ b/core/core_api.c @@ -144,9 +144,11 @@ void shoebill_start() pthread_mutex_unlock(&shoe.cpu_thread_lock); pthread_mutex_unlock(&shoe.via_clock_thread_lock); } - -static void _cpu_loop_fast() -{ + +void *_cpu_thread (void *arg) { + + pthread_mutex_lock(&shoe.cpu_thread_lock); + while (1) { if (shoe.cpu_thread_notifications) { @@ -159,16 +161,13 @@ static void _cpu_loop_fast() 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() +/*static void _cpu_loop_debug() { while (1) { if (shoe.cpu_thread_notifications) { @@ -181,33 +180,9 @@ static void _cpu_loop_debug() } } -} +}*/ -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) +/*void shoebill_cpu_stepi (void) { if (shoe.cpu_mode != CPU_MODE_FREEZE) return ; @@ -231,7 +206,7 @@ void shoebill_cpu_freeze (void) while (shoe.cpu_thread_notifications & SHOEBILL_STATE_SWITCH_MODE); pthread_yield_np(); -} +}*/ /* * The A/UX bootloader blasts this structure into memory @@ -338,7 +313,7 @@ static void _init_macintosh_lomem_globals (const uint32_t offset) #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 + pset(offset+0xb22, 2, HWCfgFlags); // HWCfgFlags } @@ -587,40 +562,6 @@ fail: return 0; } -/*static uint32_t _setup_nubus_cards(shoebill_control_t *control) -{ - uint32_t i; - - for (i=0; i<16; i++) { - switch (control->slots[i].card_type) { - case card_empty: - continue; - - case card_toby_frame_buffer: - case card_shoebill_ethernet: - assert(!"not implemented"); - - case card_shoebill_video: { - const uint16_t width = control->slots[i].card.video.width; - const uint16_t height = control->slots[i].card.video.height; - const uint16_t scanline_width = control->slots[i].card.video.scanline_width; - const double refresh_rate = control->slots[i].card.video.refresh_rate; - uint8_t *frame_buffer = control->slots[i].card.video.frame_buffer; - assert(scanline_width >= width); - - nubus_video_init(i, width, height, scanline_width, refresh_rate, frame_buffer); - - } - } - } - - return 1; - -fail: - - return 0; -}*/ - static uint32_t _load_aux_kernel(shoebill_control_t *control, coff_file *coff, uint32_t *_pc) { uint32_t j, i, pc = 0xffffffff; @@ -801,7 +742,8 @@ uint32_t shoebill_initialize(shoebill_control_t *control) pthread_mutex_lock(&shoe.via_clock_thread_lock); pthread_create(&control->cpu_thread_pid, NULL, _cpu_thread, NULL); - pthread_create(&control->cpu_thread_pid, NULL, via_clock_thread, NULL); + // pthread_create(&control->cpu_thread_pid, NULL, debug_cpu_thread, NULL); + pthread_create(&control->via_thread_pid, NULL, via_clock_thread, NULL); return 1; @@ -885,6 +827,7 @@ void shoebill_mouse_move(int32_t x, int32_t y) void shoebill_mouse_move_delta (int32_t x, int32_t y) { assert(pthread_mutex_lock(&shoe.adb.lock) == 0); + shoe.mouse.delta_x += x; shoe.mouse.delta_y += y; diff --git a/core/core_api.h b/core/core_api.h index db08eea..ed1e18a 100644 --- a/core/core_api.h +++ b/core/core_api.h @@ -95,7 +95,7 @@ typedef struct { } card; } slots[16]; - pthread_t cpu_thread_pid, clock_pid; + pthread_t cpu_thread_pid, via_thread_pid; char error_msg[8192]; } shoebill_control_t; @@ -126,4 +126,6 @@ void shoebill_mouse_click(uint8_t down); void shoebill_start(); +uint8_t* shoebill_extract_kernel(const char *disk_path, const char *kernel_path, char *error_str, uint32_t *len); + #endif diff --git a/core/cpu.c b/core/cpu.c index 8a75e11..bf68036 100644 --- a/core/cpu.c +++ b/core/cpu.c @@ -31,7 +31,7 @@ #include "../core/mc68851.h" global_shoebill_context_t shoe; -struct dbg_state_t dbg_state; +//struct dbg_state_t dbg_state; #define nextword() ({const uint16_t w=lget(shoe.pc,2); if (shoe.abort) {return;}; shoe.pc+=2; w;}) #define verify_supervisor() {if (!sr_s()) {throw_privilege_violation(); return;}} @@ -867,7 +867,7 @@ struct dbg_state_t dbg_state; const uint8_t Rm = mib(R, 4); const uint8_t Sm = mib(shoe.dat, 4); const uint8_t Dm = mib(shoe.a[r], 4); - set_sr_z(chop(R,sz)==0); + set_sr_z(R == 0); /* <- This is where A/UX 3.0.0 started working */ set_sr_n(Rm); set_sr_v((!Sm && Dm && !Rm) || (Sm && !Dm && Rm)); set_sr_c((Sm && !Dm) || (Rm && !Dm) || (Sm && Rm)); @@ -1162,7 +1162,8 @@ struct dbg_state_t dbg_state; // 1: reset the "enabled" bit of the TC register printf("Reset! (not implemented)\n"); - dbg_state.running = 0; + assert(!"reset called"); + //dbg_state.running = 0; }) ~inst(movec, { @@ -1359,11 +1360,13 @@ struct dbg_state_t dbg_state; shoe.dat = R; call_ea_write(M, sz); - set_sr_z(R == 0); - set_sr_n(Rm); + const uint32_t result = chop(R, sz); + + set_sr_z(result == 0); + set_sr_c(result != 0); + set_sr_x(result != 0); set_sr_v(Dm && Rm); - set_sr_c(Dm || Rm); - set_sr_x(Dm || Rm); + set_sr_n(Rm); }) ~inst(negx, { diff --git a/core/debug_server.c b/core/debug_server.c new file mode 100644 index 0000000..29b4cf0 --- /dev/null +++ b/core/debug_server.c @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2014, Peter Rutenbar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../core/shoebill.h" +#include "../core/coff.h" + +#define DEBUG_MODE_STOPPED 0 +#define DEBUG_MODE_RUNNING 1 +#define DEBUG_MODE_STEP 2 + +#define SHOEBILL_DEBUG_PORT 0xfded +static int _start_debug_server(void) +{ + struct sockaddr_in addr; + int sock = socket(AF_INET, SOCK_STREAM, 0); + + memset(&addr, 0, sizeof(addr)); + addr.sin_len = sizeof(struct sockaddr_in); + addr.sin_family = AF_INET; + addr.sin_port = htons(SHOEBILL_DEBUG_PORT); + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + if (sock == -1) { + assert(!"can't socket"); + } + else if (bind(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr)) != 0) { + assert(!"can't bind"); + // return -1; + } + else if (listen(sock, 1) != 0) { + assert(!"can't listen"); + } + + return sock; +} + +void *debug_server_thread (void *arg) +{ + struct sockaddr_in addr; + socklen_t sin_size = sizeof(struct sockaddr_in); + uint8_t *inbuf = calloc(0x10000, 1); + uint8_t *outbuf = calloc(0x10000, 1); + int sock = _start_debug_server(); + int clientfd = accept(sock, (struct sockaddr*)&addr, &sin_size); + + shoe.dbg.connected = 1; + shoe.dbg.mode = DEBUG_MODE_RUNNING; + + + return NULL; +} + +void *debug_cpu_thread (void *arg) +{ + memset(&shoe.dbg, 0, sizeof(shoe.dbg)); + shoe.dbg.mode = DEBUG_MODE_STOPPED; + + pthread_t server_thread_pid; + pthread_create(&server_thread_pid, + NULL, + debug_server_thread, + NULL); + + /* + * The CPU only runs once the debugger is connected, and the + * emulator has started + */ + pthread_mutex_lock(&shoe.cpu_thread_lock); + while (!shoe.dbg.connected) + usleep(1000); + + while (1) { + if (shoe.dbg.mode == DEBUG_MODE_RUNNING) { + if (!shoe.dbg.ignore_interrupts && + (shoe.cpu_thread_notifications & 0xff)) { + process_pending_interrupt(); + } + + if (shoe.cpu_thread_notifications & SHOEBILL_STATE_STOPPED) { + continue; + } + + cpu_step(); + } + else if (shoe.dbg.mode == DEBUG_MODE_STOPPED) + pthread_yield_np(); + else if (shoe.dbg.mode == DEBUG_MODE_STEP) { + cpu_step(); + shoe.dbg.mode = DEBUG_MODE_STOPPED; + } + } +} + + + diff --git a/core/exception.c b/core/exception.c index b0b4d81..56cc605 100644 --- a/core/exception.c +++ b/core/exception.c @@ -215,9 +215,9 @@ void throw_illegal_instruction() { //printf("throw_illegal_instruction(): I'm throwing an illegal instruction exception! (shoe.pc = 0x%08x, op=0x%04x, a7=0x%08x)\n", shoe.orig_pc, shoe.op, shoe.a[7]); - if ((shoe.op != 0xf010) && ((shoe.op >> 12) != 0xa)) + /*if ((shoe.op != 0xf010) && ((shoe.op >> 12) != 0xa)) //assert(!"illegal"); - dbg_state.running = 0; + dbg_state.running = 0; */ // fetch vector number const uint32_t vector_num = diff --git a/core/filesystem.c b/core/filesystem.c new file mode 100644 index 0000000..74eab03 --- /dev/null +++ b/core/filesystem.c @@ -0,0 +1,1389 @@ +/* + * Copyright (c) 2014, Peter Rutenbar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#pragma mark Alloc pool stuff + +/* --- alloc pool --- */ + +typedef struct _alloc_pool_t{ + struct _alloc_pool_t *prev, *next; + uint32_t size, magic; +} alloc_pool_t; + +#define fix_endian(x) do { \ + if (ntohs(1) == 1) \ + break; \ + switch (sizeof(x)) { \ + case 1: break; \ + case 2: (x) = ntohs(x); break; \ + case 4: (x) = ntohl(x); break; \ + case 8: { \ + const uint64_t n = ntohl((x) & 0xffffffff); \ + (x) = (n<<32) | ntohl((x)>>32); \ + break; \ + } \ + default: assert(!"bogus size"); \ +}} while (0) + +static 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->magic = 'moof'; + + buf->next = pool->next; + buf->prev = pool; + + if (pool->next) + pool->next->prev = buf; + pool->next = buf; + + return &buf[1]; +} + +static void* p_realloc(void *ptr, uint64_t size) +{ + alloc_pool_t *header = &((alloc_pool_t*)ptr)[-1]; + alloc_pool_t *new_header = realloc(header, size + sizeof(alloc_pool_t)); + + if (new_header) + return &new_header[1]; + + return NULL; +} + +static void p_free(void *ptr) +{ + alloc_pool_t *header = &((alloc_pool_t*)ptr)[-1]; + assert(header->magic == 'moof'); + + if (header->next) + header->next->prev = header->prev; + + if (header->prev) + header->prev->next = header->next; + + free(header); +} + +static void p_free_pool(alloc_pool_t *pool) +{ + while (pool->prev) + pool = pool->prev; + + while (pool) { + alloc_pool_t *cur = pool; + pool = cur->next; + assert(cur->magic == 'moof'); + free(cur); + } +} + +static alloc_pool_t* p_new_pool(void) +{ + alloc_pool_t *pool = calloc(sizeof(alloc_pool_t), 1); + pool->magic = 'moof'; + return pool; +} + + +/* --- Disk/partition management stuff --- */ +#pragma mark Disk/partition management stuff + +typedef struct __attribute__ ((__packed__)) { + uint32_t magic; + uint8_t cluster; + uint8_t type; + uint16_t inode; + + // no no no SLICErrr rrrrrCUR (NOPE, sys-V compiler orders these bits backwards) + // yes RUCrrrrr rrrSLICE + + uint8_t dummy:5; + uint8_t crit:1; + uint8_t usr:1; + uint8_t root:1; + + uint8_t slice:5; + uint8_t dummy2:3; + + uint16_t dummy3; + uint32_t tmade; + uint32_t tmount; + uint32_t tunmount; + + /* "Alternate block map" */ + uint32_t abm_size; + uint32_t abm_ents; + uint32_t abm_start; +} block_zero_t; + +typedef struct __attribute__ ((__packed__)) { + uint8_t pmSig[2]; + uint16_t pmSigPad; + uint32_t pmMapBlkCnt; + uint32_t pmPyPartStart; + uint32_t pmPartBlkCnt; + char pmPartName[32]; + char pmPartType[32]; + uint32_t pmLgDataStart; + uint32_t pmDataCnt; + uint32_t pmPartStatus; + uint32_t pmLgBootStart; + uint32_t pmBootSize; + uint32_t pmBootAddr; + uint32_t pmBootAddr2; + uint32_t pmBootEntry; + uint32_t pmBootEntry2; + uint32_t pmBootCksum; + char pmProcessor[16]; + block_zero_t bz; +} apple_partition_map_t; + +typedef struct __attribute__ ((__packed__)) { + uint8_t sbSig[2]; // device signature + uint16_t sbBlkSize; // block size of the device + uint32_t sbBlkCount; // number of blocks on the device + uint16_t sbDevType; // reserved + uint16_t sbDevId; // reserved + uint32_t sbData; // reserved + uint16_t sbDrvrCount; // nnumber of driver descriptor entries + uint32_t ddBlock; // first driver's starting block + uint16_t ddSize; // size of the driver, in 512-byte blocks + uint16_t ddType; // operating system type (MacOS = 1) +} driver_descriptor_record_t; + + +typedef struct { + struct _disk_t *disk; + char *error_str; + alloc_pool_t *pool; + uint32_t start_block, num_blocks; + char name[33], type[33]; +} partition_t; + +typedef struct _disk_t { + // -- fill these out -- + const char *path; + char *error_str; + + // -- "private" -- + alloc_pool_t *pool; + FILE *f; + uint32_t block_size; + + driver_descriptor_record_t ddr; + + uint32_t num_partitions; + apple_partition_map_t *partition_maps; + partition_t *partitions; +} disk_t; + +static void disk_get_block (disk_t *disk, uint8_t buf[512], uint32_t blockno) +{ + const uint32_t block_size = disk->block_size; + assert(0 == fseeko(disk->f, block_size * blockno, SEEK_SET)); + assert(fread(buf, block_size, 1, disk->f) == 1); +} + +static void part_get_block(partition_t *part, uint8_t buf[512], uint32_t blockno) +{ + assert(blockno < part->num_blocks); + disk_get_block(part->disk, buf, part->start_block + blockno); +} + +static uint8_t disk_load_partition_map(disk_t *disk, apple_partition_map_t *apm, uint32_t idx) +{ + uint8_t block[512]; + + disk_get_block(disk, block, 1 + idx); + memcpy(apm, block, sizeof(apple_partition_map_t)); + + fix_endian(apm->pmSigPad); + fix_endian(apm->pmMapBlkCnt); + fix_endian(apm->pmPyPartStart); + fix_endian(apm->pmPartBlkCnt); + fix_endian(apm->pmLgDataStart); + fix_endian(apm->pmDataCnt); + fix_endian(apm->pmPartStatus); + fix_endian(apm->pmLgBootStart); + fix_endian(apm->pmBootSize); + fix_endian(apm->pmBootAddr); + fix_endian(apm->pmBootAddr2); + fix_endian(apm->pmBootEntry); + fix_endian(apm->pmBootEntry2); + fix_endian(apm->pmBootCksum); + + fix_endian(apm->bz.magic); + fix_endian(apm->bz.inode); + fix_endian(apm->bz.tmade); + fix_endian(apm->bz.tmount); + fix_endian(apm->bz.tunmount); + fix_endian(apm->bz.abm_size); + fix_endian(apm->bz.abm_ents); + fix_endian(apm->bz.abm_start); + + if (memcmp(apm->pmSig, "PM", 2) != 0) { + sprintf(disk->error_str, "partition index %u has bad magic %02x%02x", + idx, apm->pmSig[0], apm->pmSig[1]); + return 0; + } + + return 1; +} + +static void close_disk(disk_t *disk) +{ + fclose(disk->f); + p_free_pool(disk->pool); +} + +static disk_t* open_disk (const char *disk_path, char *error_str) +{ + disk_t *disk; + uint8_t block[512]; + apple_partition_map_t apm; + uint32_t i; + alloc_pool_t *pool = p_new_pool(); + FILE *f; + + disk = p_alloc(pool, sizeof(disk_t)); + + disk->pool = pool; + disk->block_size = 512; + disk->error_str = error_str; + disk->path = disk_path; + + f = fopen(disk_path, "r"); + if (f == NULL) { + sprintf(error_str, "Can't open that path"); + goto fail; + } + + disk->f = f; + + // Load the driver descriptor record + + disk_get_block(disk, block, 0); + memcpy(&disk->ddr, block, sizeof(disk->ddr)); + + fix_endian(disk->ddr.sbBlkSize); + fix_endian(disk->ddr.sbBlkCount); + fix_endian(disk->ddr.sbDevType); + fix_endian(disk->ddr.sbDevId); + fix_endian(disk->ddr.sbData); + fix_endian(disk->ddr.sbDrvrCount); + fix_endian(disk->ddr.ddBlock); + fix_endian(disk->ddr.ddSize); + fix_endian(disk->ddr.ddType); + + // If the DDR block exists, (it doesn't have to necessarially) + if (memcmp(disk->ddr.sbSig, "ER", 2) == 0) { + // Can't handle non-512 byte block sizes + if (disk->ddr.sbBlkSize != 512) { + sprintf(error_str, "This disk uses blkSize=%u and I can't handle that", + disk->ddr.sbBlkSize); + goto fail; + } + } + + // printf("sizeof(apple_part_map_t) = %lu\n", sizeof(apple_partition_map_t)); + + // Load the partition maps + + if (!disk_load_partition_map(disk, &apm, 0)) + goto fail; + else if ((apm.pmMapBlkCnt > 256) || (apm.pmMapBlkCnt == 0)) { + sprintf(error_str, "Crazy number of partitions on this disk %u", apm.pmMapBlkCnt); + goto fail; + } + + disk->num_partitions = apm.pmMapBlkCnt; + disk->partition_maps = p_alloc(disk->pool, disk->num_partitions * sizeof(apple_partition_map_t)); + disk->partitions = p_alloc(disk->pool, disk->num_partitions * sizeof(partition_t)); + + for (i=0; inum_partitions; i++) { + if (!disk_load_partition_map(disk, &disk->partition_maps[i], i)) + goto fail; + + memset(&disk->partitions[i], 0, sizeof(partition_t)); + disk->partitions[i].disk = disk; + disk->partitions[i].pool = disk->pool; + disk->partitions[i].error_str = error_str; + disk->partitions[i].start_block = disk->partition_maps[i].pmPyPartStart; + disk->partitions[i].num_blocks = disk->partition_maps[i].pmPartBlkCnt; + + memcpy(disk->partitions[i].name, disk->partition_maps[i].pmPartName, 32); + memcpy(disk->partitions[i].type, disk->partition_maps[i].pmPartType, 32); + + // printf("%u type:%s name:%s\n", i, disk->partitions[i].type, disk->partitions[i].name); + // printf("bz_magic=0x%08x slice=%u\n", disk->partition_maps[i].bz.magic, disk->partition_maps[i].bz.slice); + } + + return disk; + +fail: + if (f) fclose(f); + p_free_pool(pool); + return NULL; +} + +/*static uint8_t translate_aux_to_apm_partition_num(disk_t *disk, + uint32_t aux_idx, + uint32_t *apm_idx) +{ + uint32_t i, aux_i = 0; + + for (i=0; inum_partitions; i++) { + partition_t *part = &disk->partitions[i]; + if (strcmp("Apple_UNIX_SVR2", part->type) != 0) + continue; + else if (strstr(part->name, "Eschatology") != NULL) + continue; + else if (aux_i == aux_idx) { + *apm_idx = i; + return 1; + } + aux_i++; + } + + return 0; +}*/ + +static int32_t find_root_partition_number(disk_t *disk, uint8_t clus_num) +{ + /* + * See the man page for bzb for the full scoop. + * Basically, A/UX partition 0 is the first partition with + * ** type="Apple_UNIX_SVR2", + * ** a matching cluster number, and + * ** the root bit set + * + * A/UX partition 1 is the first partition with + * ** type="Apple_UNIX_SVR2", + * ** a matching cluster number, and + * ** the swap filesystem type + * + * A/UX partition 2 is the next partition with + * ** type="Apple_UNIX_SVR2", + * ** a matching cluster number + * ** and the user bit set + * + * The logic is probably even more arbitrary + * and complicated than that. Why doesn't A/UX + * just use the native APM partition numbers? + */ + + uint32_t i; + for (i=0; inum_partitions; i++) { + partition_t *part = &disk->partitions[i]; + apple_partition_map_t *apm = &disk->partition_maps[i]; + + // printf("%u magic=0x%08x root=%u type=%s\n", i, apm->bz.magic, apm->bz.root, part->type); + + if (apm->bz.magic != 0xabadbabe) + continue; + + if (!apm->bz.root) + continue; + + if (apm->bz.cluster != clus_num) + continue; + + // slice==0 -> This partition doesn't prefer any particular slice number + // slice==N -> This partition prefers slice number (N-1) + // (I think) + /*if (apm->bz.slice != 0 && apm->bz.slice != 1) + continue;*/ + + if (strcmp("Apple_UNIX_SVR2", part->type) != 0) + continue; + + return (int32_t)i; + } + return -1; +} + +#pragma mark SVFS stuff +/* --- SVFS stuff --- */ + +typedef struct __attribute__ ((__packed__)) { + uint16_t isize; + uint32_t fsize; + uint16_t nfree; + uint32_t free[50]; + uint16_t ninode; + uint16_t inode[100]; + uint8_t flock; + uint8_t ilock; + uint8_t fmod; + uint8_t ronly; + uint32_t time; + uint16_t dinfo[4]; + uint32_t tfree; + uint16_t tinode; + uint8_t fname[6]; + uint8_t fpack[6]; + uint32_t _unused[13]; + uint32_t state; + uint16_t lasti; + uint16_t nbehind; + uint32_t magic; + uint32_t type; +} svfs_superblock_t; + +typedef struct __attribute__ ((__packed__)) { + uint16_t mode; + uint16_t nlink; + uint16_t uid; + uint16_t gid; + uint32_t size; + uint8_t __addr[39]; + uint8_t gen; + uint32_t atime; + uint32_t mtime; + uint32_t ctime; + + uint32_t addr[13]; +} svfs_inode_t; + +typedef struct { + char *error_str; + alloc_pool_t *pool; + partition_t *part; + + svfs_superblock_t superblock; + + uint32_t blocksize; // SVFS can use 512, 1024, 2048, 4096-byte block sizes + +} svfs_t; + +static uint8_t svfs_read_block(svfs_t *mount, uint8_t *block, uint32_t blockno) +{ + const uint32_t sectors_per_block = mount->blocksize / 512; + const uint32_t start_sector = blockno * sectors_per_block; + uint32_t i; + + // printf("sectors_per_block = %u, start_sector=%u\n", sectors_per_block, start_sector); + + for (i=0; ipart, &block[i * 512], start_sector+i); + } + + return 1; +} + +static uint8_t svfs_load_inode(svfs_t *mount, svfs_inode_t *inode, uint32_t inum) +{ + uint32_t i; + uint8_t block[4096]; + const uint32_t max_inode_blocks = mount->superblock.isize - 2; + const uint32_t max_inodes = (max_inode_blocks * mount->blocksize) / 64; + if (inum > max_inodes) { + sprintf(mount->error_str, "svfs_load_inode: inode %u too big (max=%u)", inum, max_inodes); + return 0; + } + + const uint32_t inum_byte_idx_in_partition = ((inum-1) * 64) + (2 * mount->blocksize); + const uint32_t inum_block = inum_byte_idx_in_partition / mount->blocksize; + const uint32_t inum_byte_idx_in_block = inum_byte_idx_in_partition % mount->blocksize; + + if (!svfs_read_block(mount, block, inum_block)) + return 0; + + memcpy(inode, &block[inum_byte_idx_in_block], 64); + + fix_endian(inode->mode); + fix_endian(inode->nlink); + fix_endian(inode->uid); + fix_endian(inode->gid); + fix_endian(inode->size); + fix_endian(inode->atime); + fix_endian(inode->mtime); + fix_endian(inode->ctime); + + for (i=0; i<13; i++) { + uint32_t addr = inode->__addr[i*3 + 0]; + addr = (addr << 8) + inode->__addr[i*3 + 1]; + addr = (addr << 8) + inode->__addr[i*3 + 2]; + inode->addr[i] = addr; + } + + return 1; +} + +static uint8_t svfs_read_level(svfs_t *mount, + svfs_inode_t *inode, + uint8_t *buf, + uint32_t *len, + uint32_t *indirects, + uint32_t level) +{ + uint8_t *tmp = p_alloc(mount->pool, mount->blocksize); + const uint32_t num_indirects = mount->blocksize / 4; + uint32_t i; + + for (i=0; (isize); i++) { + uint32_t chunk_size = inode->size - *len; + if (chunk_size > mount->blocksize) + chunk_size = mount->blocksize; + + const uint32_t addr = ntohl(indirects[i]); + + if (!svfs_read_block(mount, tmp, addr)) { + sprintf(mount->error_str, "couldn't read svfs block num %u at L%u", addr, level); + goto fail; + } + + if (level == 1) { + memcpy(buf + *len, tmp, chunk_size); + *len += chunk_size; + } + else { + if (!svfs_read_level(mount, inode, buf, len, (uint32_t*)tmp, level-1)) + goto fail; + } + } + + p_free(tmp); + return 1; + +fail: + p_free(tmp); + return 0; +} + + +static uint8_t* svfs_read_inode_data(svfs_t *mount, svfs_inode_t *inode) +{ + uint8_t *tmp = p_alloc(mount->pool, mount->blocksize); + uint8_t *buf = p_alloc(mount->pool, inode->size); + uint32_t i, len = 0; + + // The first 10 block pointers in the inode point to data + // The addr[10] is a L1 block pointer, [11] is L2, and [12] is L3 + for (i=0; (len < inode->size) && (i < 10); i++) { + uint32_t chunk_size = inode->size - len; + if (chunk_size > mount->blocksize) + chunk_size = mount->blocksize; + + if (!svfs_read_block(mount, tmp, inode->addr[i])) { + sprintf(mount->error_str, "couldn't read svfs block num %u at L0", inode->addr[i]); + goto fail; + } + + memcpy(buf + len, tmp, chunk_size); + len += chunk_size; + } + + + if (!svfs_read_block(mount, tmp, inode->addr[10])) { + sprintf(mount->error_str, "couldn't read svfs L1 block ptr %u", inode->addr[10]); + goto fail; + } + else if (!svfs_read_level(mount, inode, buf, &len, (uint32_t*)tmp, 1)) + goto fail; + + if (!svfs_read_block(mount, tmp, inode->addr[11])) { + sprintf(mount->error_str, "couldn't read svfs L2 block ptr %u", inode->addr[11]); + goto fail; + } + else if (!svfs_read_level(mount, inode, buf, &len, (uint32_t*)tmp, 2)) + goto fail; + + if (!svfs_read_block(mount, tmp, inode->addr[12])) { + sprintf(mount->error_str, "couldn't read svfs L3 block ptr %u", inode->addr[12]); + goto fail; + } + else if (!svfs_read_level(mount, inode, buf, &len, (uint32_t*)tmp, 3)) + goto fail; + + p_free(tmp); + return buf; + +fail: + p_free(tmp); + p_free(buf); + return NULL; +} + +static svfs_t* svfs_mount(partition_t *part) +{ + assert(sizeof(svfs_superblock_t) == 512); + + uint32_t i; + svfs_t *mount = p_alloc(part->pool, sizeof(svfs_t)); + mount->pool = part->pool; + mount->error_str = part->error_str; + mount->part = part; + + part_get_block(part, (uint8_t*)&mount->superblock, 1); + + fix_endian(mount->superblock.isize); + fix_endian(mount->superblock.fsize); + fix_endian(mount->superblock.nfree); + for (i=0; i<50; i++) { + fix_endian(mount->superblock.free[i]); + } + fix_endian(mount->superblock.ninode); + for (i=0; i<100; i++) { + fix_endian(mount->superblock.inode[i]); + } + fix_endian(mount->superblock.time); + for (i=0; i<4; i++) { + fix_endian(mount->superblock.dinfo[i]); + } + fix_endian(mount->superblock.tfree); + fix_endian(mount->superblock.tinode); + fix_endian(mount->superblock.state); + fix_endian(mount->superblock.lasti); + fix_endian(mount->superblock.nbehind); + fix_endian(mount->superblock.magic); + fix_endian(mount->superblock.type); + + if (mount->superblock.magic != 0xfd187e20) { + sprintf(part->error_str, "Magic doesn't match svfs"); + goto fail; + } + + // It is SVFS! + + const uint32_t type = mount->superblock.type; + if ((type != 1) && (type != 2) && (type != 4) && (type != 8)) { + sprintf(part->error_str, "Unknown SVFS type (%u)", type); + goto fail; + } + + mount->blocksize = 512 * type; + + return mount; + +fail: + if (mount) p_free(mount); + + return NULL; +} + +typedef struct __attribute__ ((__packed__)) { + uint16_t inum; + char name[14]; +} svfs_dir_entry_t; + +svfs_inode_t* svfs_traverse_path(svfs_t *mount, const char *_path) +{ + uint32_t i; + uint16_t inum = 2; // 2 == root + svfs_inode_t *inode = p_alloc(mount->pool, sizeof(svfs_inode_t)); + char *path = p_alloc(mount->pool, strlen(_path)+1); + strcpy(path, _path); + + if (!svfs_load_inode(mount, inode, inum)) + goto fail; + + char *last, *elem; + for (elem = strtok_r(path, "/", &last); + elem; + elem = strtok_r(NULL, "/", &last)) { + //printf("elem = [%s]\n", elem); + const uint32_t num_entries = inode->size / 16; + //printf("inode size = %u\n", inode->size); + svfs_dir_entry_t *dir = (svfs_dir_entry_t*)svfs_read_inode_data(mount, inode); + if (!dir) + goto fail; + + for (i=0; ierror_str, "'%s' in '%s' doesn't exist", elem, _path); + goto fail; + } + } + //printf("final inode size = %u\n", inode->size); + p_free(path); + return inode; + +fail: + p_free(inode); + p_free(path); + return NULL; +} + +/* --- UFS stuff --- */ +#pragma mark UFS stuff + +typedef struct __attribute__ ((__packed__)) { + uint32_t link; + uint32_t rlink; + uint32_t sblkno; + uint32_t cblkno; + uint32_t iblkno; + uint32_t dblkno; + uint32_t cgoffset; + uint32_t cgmask; + uint32_t time; + uint32_t size; + uint32_t dsize; + uint32_t ncg; + uint32_t bsize; + uint32_t fsize; + uint32_t frag; + uint32_t minfree; + uint32_t rotdelay; + uint32_t rps; + uint32_t bmask; + uint32_t fmask; + uint32_t bshift; + uint32_t fshift; + uint32_t maxcontig; + uint32_t maxbpg; + uint32_t fragshift; + uint32_t fsbtodb; + uint32_t sbsize; + uint32_t csmask; + uint32_t csshift; + uint32_t nindir; + uint32_t inopb; + uint32_t nspf; + uint32_t optim; + uint32_t dummy[2]; + uint32_t state; + uint32_t id[2]; + uint32_t csaddr; + uint32_t cssize; + uint32_t cgsize; + uint32_t ntrak; + uint32_t nsect; + uint32_t spc; + uint32_t ncyl; + uint32_t cpg; + uint32_t ipg; + uint32_t fpg; + uint32_t csum_ndir; + uint32_t csum_nbfree; + uint32_t csum_nifree; + uint32_t csum_nffree; + uint8_t fmod; + uint8_t clean; + uint8_t ronly; + uint8_t flags; + uint8_t fsmnt[500]; + uint8_t fname[6]; + uint8_t fpack[6]; + uint32_t cgrotor; + uint32_t dummy2[32]; + uint32_t cpc; + uint16_t postbl[32][8]; + uint32_t magic; +} ufs_superblock_t; + +typedef struct __attribute__ ((__packed__)) { + uint32_t link; + uint32_t rlink; + uint32_t time; + uint32_t cgx; + uint16_t ncyl; + uint16_t niblk; + uint32_t ndblk; + uint32_t csum_ndir; + uint32_t csum_nbfree; + uint32_t csum_nifree; + uint32_t csum_nffree; + uint32_t rotor; + uint32_t frotor; + uint32_t irotor; + uint32_t frsum[8]; + uint32_t btot[32]; + uint16_t b[32][8]; + uint8_t iused[256]; + uint32_t magic; +} ufs_cylinder_group_t; + +typedef struct { + char *error_str; + alloc_pool_t *pool; + partition_t *part; + + uint32_t frag_size; + uint32_t block_size; + uint32_t frag_per_block; + + ufs_superblock_t superblock; + + uint32_t num_groups; + ufs_cylinder_group_t *groups; +} ufs_t; + +typedef struct __attribute__ ((__packed__)) { + uint16_t mode; + uint16_t nlink; + uint16_t uid; + uint16_t gid; + uint64_t size; + uint32_t atime; + uint32_t dummy; + uint32_t mtime; + uint32_t dummy2; + uint32_t ctime; + uint32_t dummy3; + uint32_t direct[12]; + uint32_t indirect[3]; + uint32_t flags; + uint32_t blocks; + uint32_t gen; + uint32_t dummy4[4]; + + +} ufs_inode_t; + +/* + * I truly don't understand the concept behind cgoffset/cgmask, + * but this is apparently the algorithm for finding the fragment-offset + * into a cylinder group. + */ +#define ufs_group_base(mount, num) ( \ + ((mount)->superblock.fpg * (num)) + \ + ((mount)->superblock.cgoffset * \ + ((num) & ~(mount)->superblock.cgmask))) + +static uint8_t ufs_read_frag(ufs_t *mount, uint8_t *frag, uint32_t fragno) +{ + const uint32_t sectors_per_frag = mount->frag_size / 512; + const uint32_t start_sector = fragno * sectors_per_frag; + uint32_t i; + + for (i=0; ipart, &frag[i * 512], start_sector+i); + } + + return 1; +} + +static uint8_t ufs_read_block(ufs_t *mount, uint8_t *block, uint32_t blockno) +{ + uint32_t i; + + /* + * block numbers and fragment numbers are identical - they both refer + * to fragment numbers. But if we're reading a "block", then we're reading + * mount->frag_per_block fragments starting at that block address. + */ + + assert((blockno % mount->frag_per_block) == 0); // This had better align to a block boundary + + for (i=0; ifrag_per_block; i++) { + if (!ufs_read_frag(mount, block + i * mount->frag_size, blockno + i)) + return 0; + } + return 1; +} + +static uint8_t ufs_load_cylinder_group(ufs_t *mount, uint32_t frag_offset, ufs_cylinder_group_t *group) +{ + uint32_t numfrags = sizeof(ufs_cylinder_group_t) / mount->frag_size; + numfrags += ((sizeof(ufs_cylinder_group_t) % mount->frag_size) != 0); + + uint8_t *buf = p_alloc(mount->pool, numfrags * mount->frag_size); + uint32_t i; + + for (i=0; i <= numfrags; i++) + ufs_read_frag(mount, &buf[i * mount->frag_size], frag_offset + i); + memcpy(group, buf, sizeof(ufs_cylinder_group_t)); + + fix_endian(group->link); + fix_endian(group->rlink); + fix_endian(group->time); + fix_endian(group->cgx); + fix_endian(group->ncyl); + fix_endian(group->niblk); + fix_endian(group->ndblk); + fix_endian(group->csum_ndir); + fix_endian(group->csum_nbfree); + fix_endian(group->csum_nifree); + fix_endian(group->csum_nffree); + fix_endian(group->rotor); + fix_endian(group->frotor); + fix_endian(group->irotor); + for (i=0; i<8; i++) + fix_endian(group->frsum[i]); + for (i=0; i<(32*8); i++) + fix_endian(group->b[i/8][i%8]); + fix_endian(group->magic); + + p_free(buf); + return 1; +fail: + p_free(buf); + return 0; +} + +static uint8_t ufs_load_inode(ufs_t *mount, ufs_inode_t *inode, uint32_t inum) +{ + assert(sizeof(ufs_inode_t) == 128); + + /* Which cylinder group is this inode in? */ + const uint32_t group_num = inum / mount->superblock.ipg; + + /* Index of this inode in its cylinder group's inode table */ + const uint32_t group_ino_offset = inum % mount->superblock.ipg; + + /* Fragment address that contains inode */ + const uint32_t frag_addr = ufs_group_base(mount, group_num) + + mount->superblock.iblkno + + ((group_ino_offset * 128) / mount->frag_size); + + /* Byte offset into the fragment where the inode begins */ + const uint32_t frag_offset = (group_ino_offset * 128) % mount->frag_size; + + uint32_t i; + uint8_t *buf = p_alloc(mount->pool, mount->frag_size); + + // printf("group_num = %u, ino_offset=%u, addr = 0x%08x, offset = 0x%08x\n", group_num, group_ino_offset, frag_addr, frag_offset); + // printf("mount->superblock.iblkno = 0x%08x\n", mount->superblock.iblkno); + + if (!ufs_read_frag(mount, buf, frag_addr)) + goto fail; + + memcpy(inode, buf + frag_offset, 128); + + fix_endian(inode->mode); + fix_endian(inode->nlink); + fix_endian(inode->uid); + fix_endian(inode->gid); + fix_endian(inode->size); + fix_endian(inode->atime); + fix_endian(inode->mtime); + fix_endian(inode->ctime); + for (i=0; i<12; i++) + fix_endian(inode->direct[i]); + for (i=0; i<3; i++) + fix_endian(inode->indirect[i]); + fix_endian(inode->flags); + fix_endian(inode->blocks); + fix_endian(inode->gen); + + p_free(buf); + return 1; +fail: + if (buf) + p_free(buf); + return 0; +} + +static uint8_t ufs_read_level(ufs_t *mount, + ufs_inode_t *inode, + uint8_t *buf, + uint64_t *len, + uint32_t indirect_blockno, + uint32_t level) +{ + if (inode->size <= *len) + return 1; + + uint32_t *table = p_alloc(mount->pool, mount->block_size); + uint8_t *block = NULL; + + const uint32_t num_pointers = mount->block_size / 4; + uint32_t i; + + if (!ufs_read_block(mount, (uint8_t*)table, indirect_blockno)) + goto fail; + + // for (i=0; ipool, mount->block_size); + + for (i=0; (i < num_pointers) && (inode->size > *len); i++) { + const uint32_t blockno = ntohl(table[i]); + + if (level == 1) { + // direct block + uint64_t chunk_size = inode->size - *len; + if (chunk_size > mount->block_size) chunk_size = mount->block_size; + + // Which block are we reading, and at which byte-offset into it does our data exist + const uint32_t block_addr = (blockno / mount->frag_per_block) * mount->frag_per_block; + const uint32_t block_offset = (blockno - block_addr) * mount->frag_size; + + //printf("L%u: raw_blkno=0x%08x len=0x%08x blockno:0x%08x chunk_size=0x%08x\n", level-1, blockno, (uint32_t)*len, block_addr, (uint32_t)chunk_size); + + // If the chunk_size is a whole block, then we better be reading in a whole block + if (chunk_size == mount->block_size) { + // printf("block_offset = 0x%x\n", block_offset); + assert(block_offset == 0); + } + + if (!ufs_read_block(mount, block, block_addr)) + goto fail; + + memcpy(buf + *len, block + block_offset, chunk_size); + (*len) += chunk_size; + } + else { + // indirect block + if (!ufs_read_level(mount, inode, buf, len, blockno, level-1)) + goto fail; + } + } + + if (block) + p_free(block); + p_free(table); + return 1; +fail: + if (block) + p_free(block); + p_free(table); + return 0; +} + +static uint8_t* ufs_read_inode_data(ufs_t *mount, ufs_inode_t *inode) +{ + uint32_t i, j; + uint8_t *block = p_alloc(mount->pool, mount->block_size); + uint8_t *buf = p_alloc(mount->pool, inode->size); + uint64_t len = 0; + + /* Read in direct blocks */ + for (i=0; (i<12) && (len < inode->size); i++) { + + // How many bytes are we reading from this block? + uint64_t chunk_size = inode->size - len; + if (chunk_size > mount->block_size) chunk_size = mount->block_size; + + // Which block are we reading, and at which byte-offset into it does our data exist + const uint32_t block_addr = (inode->direct[i] / mount->frag_per_block) * mount->frag_per_block; + const uint32_t block_offset = (inode->direct[i] - block_addr) * mount->frag_size; + + // printf("block_addr=0x%08x, block_offset") + + // If the chunk_size is a whole block, then we better be reading in a whole block + if (chunk_size == mount->block_size) + assert(block_offset == 0); + + if (!ufs_read_block(mount, block, block_addr)) + goto fail; + + memcpy(buf + len, block + block_offset, chunk_size); + + len += chunk_size; + // printf("direct block %u = 0x%08x\n", i, inode->direct[i]); + } + + + for (i=0; i<3; i++) { + if (!ufs_read_level(mount, inode, buf, &len, inode->indirect[i], i+1)) + goto fail; + } + + + p_free(block); + return buf; +fail: + p_free(block); + p_free(buf); + return NULL; +} + +typedef struct __attribute__ ((__packed__)) { + uint32_t inum; + uint16_t len; + uint16_t namelen; + char name[1]; +} ufs_dir_t; + +ufs_inode_t* ufs_traverse_path(ufs_t *mount, const char *_path) +{ + uint32_t i; + uint32_t inum = 2; // 2 == root + ufs_inode_t *inode = p_alloc(mount->pool, sizeof(ufs_inode_t)); + char *path = p_alloc(mount->pool, strlen(_path)+1); + strcpy(path, _path); + + if (!ufs_load_inode(mount, inode, inum)) + goto fail; + + char *last, *elem; + for (elem = strtok_r(path, "/", &last); + elem; + elem = strtok_r(NULL, "/", &last)) { + + uint32_t next_inum = 0; + uint8_t *dir = ufs_read_inode_data(mount, inode); + if (!dir) + goto fail; + + for (i=0; inode->size; ) { + ufs_dir_t *entry = (ufs_dir_t*)&dir[i]; + fix_endian(entry->inum); + fix_endian(entry->len); + fix_endian(entry->namelen); + + if (entry->inum == 0) + break; + + if ((entry->namelen == strlen(elem)) && + (strncmp(elem, entry->name, entry->namelen) == 0)) { + next_inum = entry->inum; + break; + } + + i += entry->len; + } + + p_free(dir); + + if (next_inum == 0) { + sprintf(mount->error_str, "'%s' in '%s' doesn't exist", elem, _path); + goto fail; + } + + inum = next_inum; + if (!ufs_load_inode(mount, inode, inum)) + goto fail; + } + + p_free(path); + return inode; + +fail: + p_free(inode); + p_free(path); + return NULL; +} + +static ufs_t* ufs_mount(partition_t *part) +{ + ufs_t *mount = p_alloc(part->pool, sizeof(ufs_t)); + uint8_t *buf = p_alloc(part->pool, 32 * 512); + uint32_t i; + + mount->pool = part->pool; + mount->part = part; + mount->error_str = part->error_str; + + for (i=0; i<4; i++) + part_get_block(part, &buf[i*512], 16 + i); + memcpy(&mount->superblock, buf, sizeof(ufs_superblock_t)); + + fix_endian(mount->superblock.link); + fix_endian(mount->superblock.rlink); + fix_endian(mount->superblock.sblkno); + fix_endian(mount->superblock.cblkno); + fix_endian(mount->superblock.iblkno); + fix_endian(mount->superblock.dblkno); + fix_endian(mount->superblock.cgoffset); + fix_endian(mount->superblock.cgmask); + fix_endian(mount->superblock.time); + fix_endian(mount->superblock.size); + fix_endian(mount->superblock.dsize); + fix_endian(mount->superblock.ncg); + fix_endian(mount->superblock.bsize); + fix_endian(mount->superblock.fsize); + fix_endian(mount->superblock.frag); + fix_endian(mount->superblock.minfree); + fix_endian(mount->superblock.rotdelay); + fix_endian(mount->superblock.rps); + fix_endian(mount->superblock.bmask); + fix_endian(mount->superblock.fmask); + fix_endian(mount->superblock.bshift); + fix_endian(mount->superblock.fshift); + fix_endian(mount->superblock.maxcontig); + fix_endian(mount->superblock.maxbpg); + fix_endian(mount->superblock.fragshift); + fix_endian(mount->superblock.fsbtodb); + fix_endian(mount->superblock.sbsize); + fix_endian(mount->superblock.csmask); + fix_endian(mount->superblock.csshift); + fix_endian(mount->superblock.nindir); + fix_endian(mount->superblock.inopb); + fix_endian(mount->superblock.nspf); + fix_endian(mount->superblock.optim); + fix_endian(mount->superblock.state); + fix_endian(mount->superblock.id[0]); + fix_endian(mount->superblock.id[1]); + fix_endian(mount->superblock.csaddr); + fix_endian(mount->superblock.cssize); + fix_endian(mount->superblock.cgsize); + fix_endian(mount->superblock.ntrak); + fix_endian(mount->superblock.nsect); + fix_endian(mount->superblock.spc); + fix_endian(mount->superblock.ncyl); + fix_endian(mount->superblock.cpg); + fix_endian(mount->superblock.ipg); + fix_endian(mount->superblock.fpg); + fix_endian(mount->superblock.csum_ndir); + fix_endian(mount->superblock.csum_nbfree); + fix_endian(mount->superblock.csum_nifree); + fix_endian(mount->superblock.csum_nffree); + fix_endian(mount->superblock.cgrotor); + fix_endian(mount->superblock.cpc); + for (i=0; i<(32*8); i++) + fix_endian(mount->superblock.postbl[i/8][i%8]); + fix_endian(mount->superblock.magic); + + + if (mount->superblock.magic != 0x00011954) { + sprintf(part->error_str, "Magic doesn't match ufs"); + goto fail; + } + + // It is UFS! + + mount->frag_size = mount->superblock.fsize; + mount->frag_per_block = mount->superblock.frag; + mount->block_size = mount->frag_size * mount->frag_per_block; + assert(mount->block_size == mount->superblock.bsize); + + mount->num_groups = mount->superblock.ncg; + mount->groups = (ufs_cylinder_group_t*)p_alloc(mount->pool, + mount->num_groups * sizeof(ufs_cylinder_group_t)); + + for (i=0; inum_groups; i++) { + uint32_t group_base = ufs_group_base(mount, i) + mount->superblock.cblkno; + ufs_load_cylinder_group(mount, group_base, &mount->groups[i]); + if ((mount->groups[i].cgx != i) || (mount->groups[i].magic != 0x00090255)) { + sprintf(mount->error_str, "bad cylinder group %u frag_offset=0x%x", i, group_base); + goto fail; + } + } + + if (buf) + p_free(buf); + return mount; +fail: + if (mount) { + if (mount->groups) + p_free(mount->groups); + p_free(mount); + } + if (buf) + p_free(buf); + return NULL; +} + + + +/* --- Public interfaces --- */ +#pragma mark Public interfaces + + +uint8_t* shoebill_extract_kernel(const char *disk_path, const char *kernel_path, char *error_str, uint32_t *len) +{ + uint8_t *pool_data, *kernel_data = NULL; + disk_t *disk; + svfs_t *svfs_mount_obj; + ufs_t *ufs_mount_obj; + int32_t apm_part_num; + + strcpy(error_str, ""); + + disk = open_disk(disk_path, error_str); + if (!disk) + goto done; + + apm_part_num = find_root_partition_number(disk, 0); + if (apm_part_num == -1) { + sprintf(error_str, "Couldn't find root partition"); + goto done; + } + + svfs_mount_obj = svfs_mount(&disk->partitions[apm_part_num]); + if (svfs_mount_obj) { + svfs_inode_t *inode = svfs_traverse_path(svfs_mount_obj, kernel_path); + if (!inode) + goto done; + + pool_data = svfs_read_inode_data(svfs_mount_obj, inode); + if (!pool_data) + goto done; + + kernel_data = malloc(inode->size); + memcpy(kernel_data, pool_data, inode->size); + *len = inode->size; + goto done; + } + + ufs_mount_obj = ufs_mount(&disk->partitions[apm_part_num]); + if (ufs_mount_obj) { + ufs_inode_t *inode = ufs_traverse_path(ufs_mount_obj, kernel_path); + if (!inode) + goto done; + + pool_data = ufs_read_inode_data(ufs_mount_obj, inode); + if (!pool_data) + goto done; + + kernel_data = malloc(inode->size); + memcpy(kernel_data, pool_data, inode->size); + *len = inode->size; + goto done; + } + + sprintf(error_str, "I can read the partition map, but the filesystem doesn't seem to be UFS or SVFS"); + +done: + if (strlen(error_str)) + printf("error: [%s]\n", error_str); + if (disk) + close_disk(disk); + return kernel_data; +} + +/*int main (int argc, char **argv) +{ + uint8_t *buf; + uint32_t size; + char error_str[1024]; + + buf = extract_kernel(argv[1], argv[2], error_str, &size); + if (!buf) + return 0; + + FILE *f = fopen("result", "w"); + fwrite(buf, size, 1, f); + fclose(f); + + return 0; +}*/ + + + diff --git a/core/fpu.c b/core/fpu.c index 05cb7fc..91f9d0c 100644 --- a/core/fpu.c +++ b/core/fpu.c @@ -258,7 +258,8 @@ void inst_fpu_decode () } printf("inst_fpu_decode: unhandled instruction: %s op=0x%04x ext = 0x%04x pc=0x%08x\n", fpu_inst_table[name].name, shoe.op, ext, shoe.orig_pc); - dbg_state.running = 0; + assert(!"unknown fpu inst"); + //dbg_state.running = 0; } diff --git a/core/mem.c b/core/mem.c index 616ac7e..3d4b535 100644 --- a/core/mem.c +++ b/core/mem.c @@ -365,6 +365,9 @@ void _logical_get (void) void logical_set (void) { + if ((shoe.logical_addr >= 0xaf2) && (shoe.logical_addr < 0xaf6) && (shoe.logical_size == 1) && ((shoe.logical_dat&0xff) == 0xff)) { + shoe.logical_dat = 0; + } // if ((shoe.logical_addr >= 0x12fffff6) && (shoe.logical_addr <= 0x12ffffff)) // printf("aux3: setting 0x%08x = 0x%x\n", shoe.logical_addr, (uint32_t)shoe.logical_dat); // diff --git a/core/debugger.c b/core/old_debugger.c similarity index 100% rename from core/debugger.c rename to core/old_debugger.c diff --git a/core/shoebill.h b/core/shoebill.h index 9f2a800..6bede22 100644 --- a/core/shoebill.h +++ b/core/shoebill.h @@ -26,10 +26,12 @@ #ifndef _SHOEBILL_H #define _SHOEBILL_H +#include #include #include #include -#include +#include +//#include #include "coff.h" @@ -138,13 +140,14 @@ typedef struct dbg_breakpoint_t { uint64_t num; } dbg_breakpoint_t; -struct dbg_state_t { - EditLine *el; - uint8_t running; +typedef struct { + // EditLine *el; + uint8_t mode; + uint8_t ignore_interrupts; + uint8_t connected; uint64_t breakpoint_counter; dbg_breakpoint_t *breakpoints; -}; -extern struct dbg_state_t dbg_state; +} debugger_state_t; typedef enum { adb_talk, @@ -257,7 +260,6 @@ typedef struct { typedef struct { #define SHOEBILL_STATE_STOPPED (1<<9) -#define SHOEBILL_STATE_SWITCH_MODE (1<<10) // bits 0-6 are CPU interrupt priorities // bit 8 indicates that STOP was called @@ -267,13 +269,6 @@ typedef struct { pthread_mutex_t via_clock_thread_lock; pthread_mutex_t cpu_freeze_lock; -#define CPU_MODE_FAST 0 -#define CPU_MODE_DEBUG 1 -#define CPU_MODE_STEPI 2 -#define CPU_MODE_STEPI_COMPLETE 3 -#define CPU_MODE_FREEZE 4 - uint32_t cpu_mode; - // -- PMMU caching structures --- struct { pmmu_cache_entry_t entry[512]; @@ -427,7 +422,6 @@ typedef struct { via_clock_t via_clocks; - uint32_t dbg; struct timeval start_time; // when the emulator started (for computing timer interrupts) uint64_t total_ticks; // how many 60hz ticks have been generated @@ -437,6 +431,8 @@ typedef struct { coff_file *launch; // FIXME: delete me: coff symbols from aux 1.1.1 launch binary scsi_device_t scsi_devices[8]; // SCSI devices + + debugger_state_t dbg; } global_shoebill_context_t; extern global_shoebill_context_t shoe; // declared in cpu.c @@ -448,7 +444,7 @@ void fpu_setup_jump_table(); // cpu.c fuctions void cpu_step (void); -inline void inst_decode (void); +void inst_decode (void); // exception.c functions @@ -463,10 +459,10 @@ void throw_frame_two (uint16_t sr, uint32_t next_pc, uint32_t vector_num, uint32 // mem.c functions -void inline physical_get (void); +void physical_get (void); #define pget(addr, s) ({shoe.physical_addr=(addr); shoe.physical_size=(s); physical_get(); shoe.physical_dat;}) -void inline logical_get (void); +void logical_get (void); #define lget_fc(addr, s, fc) ({ \ shoe.logical_addr=(addr); \ shoe.logical_size=(s); \ @@ -483,10 +479,10 @@ void inline logical_get (void); shoe.logical_dat; \ }) -void inline physical_set (void); +void physical_set (void); #define pset(addr, s, val) {shoe.physical_addr=(addr); shoe.physical_size=(s); shoe.physical_dat=(val); physical_set();} -void inline logical_set (void); +void logical_set (void); #define lset_fc(addr, s, val, fc) {\ shoe.logical_addr=(addr); \ shoe.logical_size=(s); \ @@ -536,7 +532,7 @@ void ea_addr(); void disassemble_inst(uint8_t binary[24], uint32_t orig_pc, char *str, uint32_t *instlen); char* decode_ea_rw (uint8_t mr, uint8_t sz); char* decode_ea_addr (uint8_t mr); -inline void dis_decode(void); +void dis_decode(void); uint16_t dis_next_word (void); char* decode_ea_addr (uint8_t mr); char* decode_ea_rw (uint8_t mr, uint8_t sz); @@ -630,5 +626,9 @@ uint32_t nubus_video_read_func(const uint32_t rawaddr, const uint32_t size, void nubus_video_write_func(const uint32_t rawaddr, const uint32_t size, const uint32_t data, const uint8_t slotnum); +// debug_server.c +void *debug_cpu_thread (void *arg); + + #endif // _SHOEBILL_H diff --git a/core/via.c b/core/via.c index 582e7b9..44ceed3 100644 --- a/core/via.c +++ b/core/via.c @@ -414,6 +414,7 @@ void *via_clock_thread(void *arg) } // Check if any nubus cards have interrupt timers + shoe.via[1].rega = 0b00111111; for (i=9; i<15; i++) { if (!shoe.slots[i].connected) continue; @@ -424,7 +425,8 @@ void *via_clock_thread(void *arg) fire(1.0L/shoe.slots[i].interrupt_rate); if (shoe.slots[i].interrupts_enabled) { - shoe.via[1].rega = 0b00111111 & ~~(1<<(i-9)); + // shoe.via[1].rega = 0b00111111 & ~~(1<<(i-9)); + shoe.via[1].rega &= 0b00111111 & ~~(1<<(i-9)); via_raise_interrupt(2, IFR_CA1); printf("Fired nubus interrupt %u\n", i); } diff --git a/gui/Shoebill.xcodeproj/project.pbxproj b/gui/Shoebill.xcodeproj/project.pbxproj index 1cd5e59..f442d1e 100644 --- a/gui/Shoebill.xcodeproj/project.pbxproj +++ b/gui/Shoebill.xcodeproj/project.pbxproj @@ -256,9 +256,10 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_ARC = NO; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; @@ -284,8 +285,9 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 10.8; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = macosx; + ONLY_ACTIVE_ARCH = NO; + SDKROOT = macosx10.8; + VALID_ARCHS = "x86_64 i386"; }; name = Debug; }; @@ -293,9 +295,10 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_ARC = NO; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; @@ -316,15 +319,19 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 10.8; - SDKROOT = macosx; + SDKROOT = macosx10.8; + VALID_ARCHS = "x86_64 i386"; }; name = Release; }; 87F9775A18987700000D589E /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_OBJC_ARC = NO; COMBINE_HIDPI_IMAGES = YES; + GCC_FAST_OBJC_DISPATCH = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "Shoebill/Shoebill-Prefix.pch"; INFOPLIST_FILE = "Shoebill/Shoebill-Info.plist"; @@ -332,7 +339,9 @@ "$(inherited)", ../intermediates, ); + ONLY_ACTIVE_ARCH = NO; PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx10.8; WRAPPER_EXTENSION = app; }; name = Debug; @@ -340,8 +349,11 @@ 87F9775B18987700000D589E /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_OBJC_ARC = NO; COMBINE_HIDPI_IMAGES = YES; + GCC_FAST_OBJC_DISPATCH = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "Shoebill/Shoebill-Prefix.pch"; INFOPLIST_FILE = "Shoebill/Shoebill-Info.plist"; @@ -350,6 +362,7 @@ ../intermediates, ); PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx10.8; WRAPPER_EXTENSION = app; }; name = Release; diff --git a/gui/Shoebill/Shoebill-Info.plist b/gui/Shoebill/Shoebill-Info.plist index 620d65b..0698ae1 100644 --- a/gui/Shoebill/Shoebill-Info.plist +++ b/gui/Shoebill/Shoebill-Info.plist @@ -16,10 +16,12 @@ ${PRODUCT_NAME} CFBundlePackageType APPL - CFBundleVersion - 0.0.1 + CFBundleShortVersionString + 0.0.2 CFBundleSignature ???? + CFBundleVersion + 0.0.2 LSMinimumSystemVersion ${MACOSX_DEPLOYMENT_TARGET} NSMainNibFile diff --git a/gui/Shoebill/shoeApplication.m b/gui/Shoebill/shoeApplication.m index 839ea41..51f5a5d 100644 --- a/gui/Shoebill/shoeApplication.m +++ b/gui/Shoebill/shoeApplication.m @@ -307,7 +307,12 @@ return ; } - [self createScreenWindow:10 height:height width:width refresh_freq:200.0/3.0]; + [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; diff --git a/gui/Shoebill/shoePreferencesWindowController.m b/gui/Shoebill/shoePreferencesWindowController.m index e923d7d..14e46f3 100644 --- a/gui/Shoebill/shoePreferencesWindowController.m +++ b/gui/Shoebill/shoePreferencesWindowController.m @@ -105,7 +105,7 @@ if ([urls count] != 1) return ; - NSURL *url = [urls firstObject]; + NSURL *url = [urls objectAtIndex:0]; if (![url isFileURL]) return ; diff --git a/gui/Shoebill/shoeScreenView.m b/gui/Shoebill/shoeScreenView.m index 47c08c1..fcc93b9 100644 --- a/gui/Shoebill/shoeScreenView.m +++ b/gui/Shoebill/shoeScreenView.m @@ -73,11 +73,17 @@ colorspace = CGColorSpaceCreateDeviceRGB(); timer = [NSTimer - scheduledTimerWithTimeInterval:(0.015/2.0) + scheduledTimerWithTimeInterval:0.001 target:self selector:@selector(timerFireMethod:) userInfo:nil repeats:YES]; + [[NSRunLoop currentRunLoop] addTimer:timer + forMode:NSDefaultRunLoopMode]; + [[NSRunLoop currentRunLoop] addTimer:timer + forMode:NSEventTrackingRunLoopMode]; + + shoebill_card_video_t *video = &control->slots[10].card.video; NSSize size = { @@ -106,7 +112,8 @@ maxX = NSMaxX(bounds); maxY = NSMaxY(bounds); - //[self update]; + GLint swapInt = 1; + [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; if(NSIsEmptyRect([self visibleRect])) { @@ -180,7 +187,16 @@ static void _do_clut_translation(shoebill_card_video_t *ctx) glClearColor(0.0, 0.0, 0.0, 0.0); if (shoeApp->isRunning) { - shoebill_card_video_t *video = &control->slots[10].card.video; + uint8_t slotnum = ((shoeScreenWindow*)[self window])->slotnum; + shoebill_card_video_t *video = &control->slots[slotnum].card.video; + + /*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); diff --git a/gui/Shoebill/shoeScreenWindow.m b/gui/Shoebill/shoeScreenWindow.m index 765016d..2f127ae 100644 --- a/gui/Shoebill/shoeScreenWindow.m +++ b/gui/Shoebill/shoeScreenWindow.m @@ -32,6 +32,16 @@ - (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, @@ -107,4 +117,5 @@ [self setTitle:@"Shoebill - Screen 1 (Ctrl-click to escape)"]; } + @end diff --git a/test.c b/test.c deleted file mode 100644 index 0842caa..0000000 --- a/test.c +++ /dev/null @@ -1,407 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include - -#include "core/shoebill.h" -#include "core/core_api.h" - -shoebill_control_t control; -shoebill_card_video_t *video_card = NULL; - -void glut_display_func (void) -{ - uint32_t myw = glutGet(GLUT_WINDOW_WIDTH); - uint32_t myh = glutGet(GLUT_WINDOW_HEIGHT); - uint32_t slotnum, i; - shoebill_card_video_t *ctx = video_card; - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0, myw, 0, myh, 0, 1); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - glClear(GL_COLOR_BUFFER_BIT); - - glColor3f(0.1, 0.1, 0.8); - - uint32_t gli = 0; - - 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"); - } - - glViewport(0, 0, myw, myh); - glRasterPos2i(0, myh); - glPixelStorei(GL_UNPACK_LSB_FIRST, GL_TRUE); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - glPixelZoom(1.0, -1.0); - - glDrawPixels(myw, myh, GL_RGBA, GL_UNSIGNED_BYTE, ctx->direct_buf); - - glFlush(); -} - -/*void vbl_callback (shoebill_control_t *ctrl, uint8_t slotnum) -{ - - -} - -void video_depth_calback (sheobill_control_t *ctrl, uint8_t slotnum) -{ - - -}*/ - -#define KEY_SHIFT 1 - -const struct { - uint8_t code; - char c; - uint8_t modifiers; -} key_codes[] = { - {0x0, 'A', 0}, - {0x1, 'S', 0}, - {2, 'D', 0}, - {3, 'F', 0}, - {4, 'H', 0}, - {5, 'G', 0}, - {6, 'Z', 0}, - {7, 'X', 0}, - {8, 'C', 0}, - {9, 'V', 0}, - // {0xa ?? - {0xb, 'B', 0}, - {0xc, 'Q', 0}, - {0xd, 'W', 0}, - {0xe, 'E', 0}, - {0xf, 'R', 0}, - {0x10, 'Y', 0}, - {0x11, 'T', 0}, - - {0x12, '1', 0}, - {0x12, '!', KEY_SHIFT}, - - - {0x13, '2', 0}, - {0x13, '@', KEY_SHIFT}, - - {0x14, '3', 0}, - {0x14, '#', KEY_SHIFT}, - - {0x15, '4', 0}, - {0x15, '$', KEY_SHIFT}, - - {0x16, '6', 0}, - {0x16, '^', KEY_SHIFT}, - - {0x17, '5', 0}, - {0x17, '%', KEY_SHIFT}, - - {0x18, '=', 0}, - {0x18, '+', KEY_SHIFT}, - - {0x19, '9', 0}, - {0x19, '(', KEY_SHIFT}, - - {0x1a, '7', 0}, - {0x1a, '&', KEY_SHIFT}, - - {0x1b, '-', 0}, - {0x1b, '_', KEY_SHIFT}, - - {0x1c, '8', 0}, - {0x1c, '*', KEY_SHIFT}, - - {0x1d, '0', 0}, - {0x1d, ')', KEY_SHIFT}, - - {0x1e, ']', 0}, - {0x1e, '}', KEY_SHIFT}, - - {0x1f, 'O', 0}, - {0x20, 'U', 0}, - - {0x21, '[', 0}, - {0x21, '{', KEY_SHIFT}, - - {0x22, 'I', 0}, - {0x23, 'P', 0}, - - {0x24, '\n', 0}, - {0x24, '\r', 0}, - - {0x25, 'L', 0}, - {0x26, 'J', 0}, - - {0x27, '"', KEY_SHIFT}, - {0x27, '\'', 0}, - - {0x28, 'K', 0}, - - {0x29, ';', 0}, - {0x29, ':', KEY_SHIFT}, - - {0x2a, '\\', 0}, - {0x2a, '|', KEY_SHIFT}, - - {0x2b, ',', 0}, - {0x2b, '<', KEY_SHIFT}, - - {0x2c, '/', 0}, - {0x2c, '?', 0}, - - {0x2d, 'N', 0}, - {0x2e, 'M', 0}, - - {0x2f, '.', 0}, - {0x2f, '>', KEY_SHIFT}, - - {0x30, '\t', 0}, - {0x31, ' ', 0}, - - {0x32, '`', 0}, - {0x32, '~', KEY_SHIFT}, - - {0x33, '\b', 0}, - {0x33, 0x7f, 0}, - // {0x34, ?? - // {0x35 // escape char - // 0x36 // ctrl - // 0x37 // command - // 0x38 // shift - // 0x39 // caps lock - // 0x3a // option - // 0x3b // left arrow - // 0x3c // right arrow - // 0x3d // down arrow - // 0x3e // up arrow - - {0, 0, 0}, -}; - -static uint8_t lookup_key(char c) -{ - uint32_t i; - uint8_t upper=toupper(c); - - for (i=0; key_codes[i].c; i++) { - if (key_codes[i].c == upper) - return key_codes[i].code; - - } - - return 0xff; -} - -static uint8_t lookup_special(int special) -{ - switch (special) { - case GLUT_KEY_UP: return 0x3e; - case GLUT_KEY_DOWN: return 0x3d; - case GLUT_KEY_LEFT: return 0x3b; - case GLUT_KEY_RIGHT: return 0x3c; - default: return 0xff; - } -} - -static void keyboard_add_entry(uint8_t code, uint8_t up) -{ - uint8_t up_mask = up ? 0x80 : 0; - uint32_t i; - int modifiers = glutGetModifiers(); - - assert(pthread_mutex_lock(&shoe.adb.lock) == 0); - - if ((shoe.key.key_i+1) < KEYBOARD_STATE_MAX_KEYS) { - if (modifiers & GLUT_ACTIVE_SHIFT) { - shoe.key.keys[shoe.key.key_i].code_a = 0x38; - shoe.key.keys[shoe.key.key_i].code_b = 0xff; - shoe.key.key_i++; - } - else if (shoe.key.down_modifiers & GLUT_ACTIVE_SHIFT) { - shoe.key.keys[shoe.key.key_i].code_a = 0x80 | 0x38; - shoe.key.keys[shoe.key.key_i].code_b = 0xff; - shoe.key.key_i++; - } - shoe.key.keys[shoe.key.key_i].code_a = code | up_mask; - shoe.key.keys[shoe.key.key_i].code_b = 0xff; - shoe.key.key_i++; - } - - shoe.key.down_modifiers = modifiers; - - adb_request_service_request(2); - - pthread_mutex_unlock(&shoe.adb.lock); -} - -void global_mouse_func (int button, int state, int x, int y) -{ - //if (button != GLUT_LEFT_BUTTON) - // return ; - - assert(pthread_mutex_lock(&shoe.adb.lock) == 0); - - shoe.mouse.button_down = (state == GLUT_DOWN); - shoe.mouse.changed = 1; - - adb_request_service_request(3); - - pthread_mutex_unlock(&shoe.adb.lock); - - // printf("mouse_func: setting service request\n"); -} - -static void move_mouse (int x, int y, uint8_t button_down) -{ - printf("%s: lock\n", __func__); fflush(stdout); - assert(pthread_mutex_lock(&shoe.adb.lock) == 0); - - int32_t delta_x = x - shoe.mouse.old_x; - int32_t delta_y = y - shoe.mouse.old_y; - - shoe.mouse.old_x = x; - shoe.mouse.old_y = y; - - shoe.mouse.delta_x += delta_x; - shoe.mouse.delta_y += delta_y; - shoe.mouse.button_down = button_down; - shoe.mouse.changed = 1; - - adb_request_service_request(3); - printf("%s: unlock\n", __func__); fflush(stdout); - pthread_mutex_unlock(&shoe.adb.lock); - - // printf("move_mouse: setting service request\n"); -} - -void global_motion_func (int x, int y) -{ - move_mouse(x, y, 1); -} - -void global_passive_motion_func (int x, int y) -{ - move_mouse(x, y, 0); -} - -void global_keyboard_up_func (unsigned char c, int x, int y) -{ - uint8_t code = lookup_key(c); - if (code != 0xff) - keyboard_add_entry(code, 1); -} - -void global_keyboard_down_func (unsigned char c, int x, int y) -{ - uint8_t code = lookup_key(c); - if (code != 0xff) - keyboard_add_entry(code, 0); -} - -void global_special_up_func (int special, int x, int y) -{ - const uint8_t code = lookup_special(special); - if (code != 0xff) - keyboard_add_entry(code, 1); -} - -void global_special_down_func (int special, int x, int y) -{ - const uint8_t code = lookup_special(special); - if (code != 0xff) - keyboard_add_entry(code, 0); -} - -void timer_func (int arg) -{ - glutTimerFunc(15, timer_func, 0); // 66.67hz is the usual refresh interval (right?) - glutPostRedisplay(); -} - -int main (int argc, char **argv) -{ - bzero(&control, sizeof(shoebill_control_t)); - - control.aux_verbose = 1; - control.ram_size = 1024*1024*1024; - control.aux_kernel_path = "priv/unix2"; - control.rom_path = "priv/macii.rom"; - - control.scsi_devices[0].path = "priv/aux2.img"; - - uint32_t result = shoebill_initialize(&control); - if (!result) { - printf("fail: %s\n", control.error_msg); - return 0; - } - else - printf("success!\n"); - - shoebill_install_video_card(&control, 10, 800, 600, - (2.0/3.0) * 100.0); // 66.67hz - video_card = &control.slots[10].card.video; - - - int dummyargc = 1; - glutInit(&dummyargc, argv); - glutInitWindowSize(video_card->scanline_width, video_card->height); - glutCreateWindow(""); - glutDisplayFunc(glut_display_func); - glShadeModel(GL_FLAT); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glClearColor(0.1, 1.0, 0.1, 1.0); - - shoebill_start(); - - glutTimerFunc(15, timer_func, 0); - glutMainLoop(); - - return 0; -} diff --git a/web/2014_04_05_aux3.0.0_sorta_works.png b/web/2014_04_05_aux3.0.0_sorta_works.png new file mode 100644 index 0000000..b15fa2f Binary files /dev/null and b/web/2014_04_05_aux3.0.0_sorta_works.png differ