From 9f0e6ed1154ddf2dc90d4c3838aefc1869c17039 Mon Sep 17 00:00:00 2001 From: Peter Rutenbar Date: Sat, 26 Apr 2014 13:35:56 -0400 Subject: [PATCH] Speed improvements + little fixes + 0.0.2 release - Converted condition-code + physical_get/set switch statements to jump tables - Moved the pmmu_cache lookup code out of translate_logical_addr - Broke MOVE into move-to-datareg, -from-datareg, and datareg-to-datareg - Fixed UFS code to ignore high 32 bits of inode size (it sometimes contains garbage) - Support for zero-length reads in scsi.c + 256kb read/write buffer - pushing interrupt stack frames now actually changes the priority mask - Updated the READMEs, screencaps for 0.0.2 --- Makefile | 7 +- README.md | 10 +- README.txt | 31 +- core/Makefile | 3 +- core/alloc_pool.c | 95 ++++ core/core_api.c | 113 +++-- core/cpu.c | 460 +++++++------------ core/decoder_gen.c | 59 +++ core/dis.c | 12 + core/filesystem.c | 88 +--- core/mc68851.c | 8 +- core/mem.c | 891 ++++++++++++++++++------------------ core/scsi.c | 16 +- core/shoebill.h | 52 ++- core/via.c | 15 +- web/0.0.2_prefs_general.png | Bin 0 -> 38499 bytes 16 files changed, 933 insertions(+), 927 deletions(-) create mode 100644 core/alloc_pool.c create mode 100644 web/0.0.2_prefs_general.png diff --git a/Makefile b/Makefile index 0eb6dd8..2154a21 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,13 @@ all: shoebill -shoebill: make_core +shoebill: make_gui + +make_gui: make_core + xcodebuild -project gui/Shoebill.xcodeproj SYMROOT=build make_core: $(MAKE) -C core -j 4 clean: - rm -rf intermediates + rm -rf intermediates gui/build diff --git a/README.md b/README.md index de370e2..07eff57 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,17 @@ A Macintosh II emulator that runs A/UX (and A/UX only). Shoebill is an all-new, BSD-licensed Macintosh II emulator designed from the ground up with the singular goal of running A/UX. -A/UX 1.x.x through 2.0.0 are supported currently, and 3.x.x support is in progress. - -Shoebill requires a OS X, a Macintosh II or IIx ROM, a disk image with A/UX installed, and an A/UX kernel. +Shoebill requires a OS X, a Macintosh II, IIx or IIcx ROM, and a disk image with A/UX installed. [Download the latest release], and then see the [getting started] wiki. Also check out [screenshots]. +__Update (April 26, 2014): Shoebill 0.0.2 is available, and it supports A/UX 3.0.0! And you no longer need to supply your own kernel.__ + + +####Supports +* A/UX 1.1.1 through 3.0.0 (but not 3.0.1 or higher, yet) + ####Currently Implements * 68020 CPU (mostly) * 68881 FPU (a little) diff --git a/README.txt b/README.txt index 8d14f39..0fd377f 100644 --- a/README.txt +++ b/README.txt @@ -1,17 +1,13 @@ Shoebill - a Macintosh II emulator that runs A/UX - (except A/UX 3.x.x currently) - See the wiki on https://github.com/pruten/shoebill for better documentation on building and running Shoebill. *** KEEP IN MIND *** -* Shoebill v.first-terrible-code-drop (a.k.a. version 0.0.1) +* Shoebill v.0.0.2 * ONLY RUNS A/UX - * BUT NOT 3.x.x (I’m working on it) - * Only 1.x.x and 2.x.x * Shoebill has broken, ultra-minimalist support for * 68020 (CPU) + 68851 (MMU) + 68881 (FPU) * Some instructions for ‘020 and most for the MMU and FPU are unimplemented @@ -26,14 +22,11 @@ documentation on building and running Shoebill. *** RUNNING *** You will need -* OS X and a 64-bit Intel Macintosh - (32-bit builds are possible by twiddling the makefiles) -* A Macintosh II or IIx ROM -* A disk image with A/UX 1.x.x or 2.x.x installed +* OS X 10.8 or 10.9 +* A Macintosh II, IIx, or IIcx ROM +* A disk image with A/UX 1.x.x or 2.x.x, or 3.0.0 installed + * Note: 3.0.1 and 3.1.x do not work! * If you happen to have an installation CD image for A/UX, that will work -* 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 @@ -42,15 +35,21 @@ To boot A/UX 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). + * Set the paths for your ROM and disk image(s). * Do use SCSI ID #0 for your A/UX boot image. - * Press “Apply and Run” + * Press “Apply and Run” +* Note: As of 0.0.2, you no longer need to provide your own kernel file *** BUILDING *** 1) cd to shoebill/ -2) make # to build shoebill_core -3) xcodebuild -project gui/Shoebill.xcodeproj # to build the Cocoa GUI +2) make +3) The resulting app will be in gui/build + + + +*** ETC. *** +Props to Jared Falter for technical and emotional support! diff --git a/core/Makefile b/core/Makefile index fec5999..45c21d1 100644 --- a/core/Makefile +++ b/core/Makefile @@ -1,12 +1,13 @@ CC = clang CFLAGS = -O3 -flto -ggdb -Wno-deprecated-declarations +# CFLAGS = -O0 -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 filesystem debug_server +NEED_NOTHING = atrap_tab coff exception floppy macii_symbols redblack scsi toby_frame_buffer video core_api filesystem debug_server alloc_pool # Object files that can be compiled directly from the source OBJ_NEED_NOTHING = $(patsubst %,$(TEMP)/%.o,$(NEED_NOTHING)) diff --git a/core/alloc_pool.c b/core/alloc_pool.c new file mode 100644 index 0000000..a82d489 --- /dev/null +++ b/core/alloc_pool.c @@ -0,0 +1,95 @@ +/* + * 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 "../core/shoebill.h" + +/*typedef struct _alloc_pool_t { + struct _alloc_pool_t *prev, *next; + uint32_t size, magic; +} alloc_pool_t;*/ + +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]; +} + +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; +} + +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); +} + +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); + } +} + +alloc_pool_t* p_new_pool(void) +{ + alloc_pool_t *pool = calloc(sizeof(alloc_pool_t), 1); + pool->magic = 'moof'; + return pool; +} \ No newline at end of file diff --git a/core/core_api.c b/core/core_api.c index 8ea2886..64734af 100644 --- a/core/core_api.c +++ b/core/core_api.c @@ -34,12 +34,10 @@ #include "coff.h" #include "core_api.h" -static uint64_t sub_tv (const struct timeval a, const struct timeval b) -{ - - return 0; -} - +/* +char *ring, *ring_tmp; +const uint32_t ring_len = 64 * 1024 * 1024; +uint32_t ring_i = 0; void print_mmu_rp(uint64_t rp) { @@ -48,11 +46,11 @@ void print_mmu_rp(uint64_t 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, + sprintf(ring_tmp+strlen(ring_tmp), "[d0]%08x [d1]%08x [d2]%08x [d3]%08x\n", shoe.d[0], shoe.d[1], shoe.d[2], shoe.d[3]); + sprintf(ring_tmp+strlen(ring_tmp), "[d4]%08x [d5]%08x [d6]%08x [d7]%08x\n", shoe.d[4], shoe.d[5], shoe.d[6], shoe.d[7]); + sprintf(ring_tmp+strlen(ring_tmp), "[a0]%08x [a1]%08x [a2]%08x [a3]%08x\n", shoe.a[0], shoe.a[1], shoe.a[2], shoe.a[3]); + sprintf(ring_tmp+strlen(ring_tmp), "[a4]%08x [a5]%08x [a6]%08x [a7]%08x\n", shoe.a[4], shoe.a[5], shoe.a[6], shoe.a[7]); + sprintf(ring_tmp+strlen(ring_tmp), "[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', @@ -63,24 +61,42 @@ void printregs() shoe.tc ); - printf("[vbr]%08x\n", shoe.vbr); - - printf("srp: "); - print_mmu_rp(shoe.srp); + sprintf(ring_tmp+strlen(ring_tmp), "[vbr]%08x\n", shoe.vbr); - printf("crp: "); - print_mmu_rp(shoe.crp); + //printf("srp: "); + //print_mmu_rp(shoe.srp); - printf("tc: e=%u sre=%u fcl=%u ps=%u is=%u (tia=%u tib=%u tic=%u tid=%u)\n", + //printf("crp: "); + //print_mmu_rp(shoe.crp); + + sprintf(ring_tmp+strlen(ring_tmp), "tc: e=%u sre=%u fcl=%u ps=%u is=%u (tia=%u tib=%u tic=%u tid=%u)\n\n", tc_enable(), tc_sre(), tc_fcl(), tc_ps(), tc_is(), tc_tia(), tc_tib(), tc_tic(), tc_tid()); - printf("\n"); +} + +void dump_ring() +{ + uint32_t i = ring_i+1; + + while (i != ring_i) { + fwrite(&ring[i], 1, 1, stdout); + i = (i+1) % ring_len; + } +} + +void ring_print(const char *str) +{ + uint32_t i; + for (i=0; str[i]; i++) { + ring[ring_i] = str[i]; + ring_i = (ring_i+1) % ring_len; + } } void print_pc() { char str[1024]; - uint8_t binary[32]; + uint8_t binary[64]; uint32_t i; uint32_t len; const char *name = NULL; @@ -99,22 +115,13 @@ void print_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; - }*/ - } + else + name = ""; + + if ((name == NULL) || (name[0] == 0)) + return; + if (strncmp("scsi", name, 4) != 0) + return ; const uint16_t old_abort = shoe.abort; shoe.suppress_exceptions = 1; @@ -125,30 +132,38 @@ void print_pc() disassemble_inst(binary, shoe.pc, str, &len); - printf("*0x%08x %s [ ", shoe.pc, name ? name : ""); + sprintf(ring_tmp, "*0x%08x %s [ ", shoe.pc, name ? name : ""); for (i=0; iram_size; - shoe.physical_mem_base = valloc(control->ram_size); + shoe.physical_mem_base = valloc(control->ram_size+8); // +8 because of physical_get hack memset(shoe.physical_mem_base, 0, shoe.physical_mem_size); // Initialize Macintosh lomem variables that A/UX actually cares about diff --git a/core/cpu.c b/core/cpu.c index 3854557..24a30be 100644 --- a/core/cpu.c +++ b/core/cpu.c @@ -31,9 +31,32 @@ #include "../core/mc68851.h" global_shoebill_context_t shoe; -//struct dbg_state_t dbg_state; + +static _Bool _cc_t() {return 1;} +static _Bool _cc_f() {return 0;} +static _Bool _cc_hi() {return !sr_c() && !sr_z();} +static _Bool _cc_ls() {return sr_c() || sr_z();} +static _Bool _cc_cc() {return !sr_c();} +static _Bool _cc_cs() {return sr_c();} +static _Bool _cc_ne() {return !sr_z();} +static _Bool _cc_eq() {return sr_z();} +static _Bool _cc_vc() {return !sr_v();} +static _Bool _cc_vs() {return sr_v();} +static _Bool _cc_pl() {return !sr_n();} +static _Bool _cc_mi() {return sr_n();} +static _Bool _cc_ge() {return (sr_n() && sr_v()) || (!sr_n() && !sr_v());} +static _Bool _cc_lt() {return (sr_n() && !sr_v()) || (!sr_n() && sr_v());} +static _Bool _cc_gt() {return (sr_n() && sr_v() && !sr_z()) || (!sr_n() && !sr_v() && !sr_z());} +static _Bool _cc_le() {return sr_z() || (sr_n() && !sr_v()) || (!sr_n() && sr_v());} +typedef _Bool (*_cc_func)(); +static const _cc_func evaluate_cc[16] = { + _cc_t, _cc_f, _cc_hi, _cc_ls, _cc_cc, _cc_cs, _cc_ne, _cc_eq, + _cc_vc, _cc_vs, _cc_pl, _cc_mi, _cc_ge, _cc_lt, _cc_gt, _cc_le +}; + #define nextword() ({const uint16_t w=lget(shoe.pc,2); if (shoe.abort) {return;}; shoe.pc+=2; w;}) +#define nextlong() ({const uint32_t L=lget(shoe.pc,4); if (shoe.abort) {return;}; shoe.pc+=4; L;}) #define verify_supervisor() {if (!sr_s()) {throw_privilege_violation(); return;}} ~newmacro(inst, 2, { @@ -75,67 +98,9 @@ global_shoebill_context_t shoe; // (xyz) == (010) -> sz=2 // (xyz) == (011) -> sz=4 const uint32_t sz = y << (z+1); // too clever - const uint32_t next_pc = shoe.orig_pc + 2 + sz; - - const uint8_t C = sr_c(); - const uint8_t Z = sr_z(); - const uint8_t V = sr_v(); - const uint8_t N = sr_n(); - - uint8_t set = 0; - - switch (c) { - case 0: // trapt - set = 1; // FIXME: do-trap unconditionally? - break; - case 1: // trapf - set = 0; // FIXME: do-not-trap unconditionally? - break; - case 2: - if (!C && !Z) set = 1; // traphi - break; - case 3: - if (C || Z) set = 1; // trapls - break; - case 4: - if (!C) set = 1; // trapcc - break; - case 5: - if (C) set = 1; // trapcs - break; - case 6: - if (!Z) set = 1; // trapne - break; - case 7: - if (Z) set = 1; // trapeq - break; - case 8: - if (!V) set = 1; // trapvc - break; - case 9: - if (V) set = 1; // trapvs - break; - case 10: - if (!N) set = 1; // trappl - break; - case 11: - if (N) set = 1; // trapmi - break; - case 12: - if ( (N && V) || (!N && !V) ) set = 1; // trapge - break; - case 13: - if ( (N && !V) || (!N && V) ) set = 1; // traplt - break; - case 14: - if ( (N && V && !Z) || (!N && !V && !Z) ) set = 1; // trapgt - break; - case 15: - if ( (Z || (N && !V) || (!N && V) ) ) set = 1; // traple - break; - } + const uint32_t next_pc = shoe.pc + sz; - if (set) + if (evaluate_cc[c]()) throw_frame_two(shoe.sr, next_pc, 7, shoe.orig_pc); else shoe.pc = next_pc; @@ -638,7 +603,7 @@ global_shoebill_context_t shoe; const uint16_t format_word = lget(shoe.a[7]+6, 2); if (shoe.abort) return ; - printf("rte: sr=0x%04x pc=0x%08x format=0x%04x, post-pop a7=0x%08x\n", sr, pc, format_word, shoe.a[7]+8); + // printf("rte: sr=0x%04x pc=0x%08x format=0x%04x, post-pop a7=0x%08x\n", sr, pc, format_word, shoe.a[7]+8); switch (format_word >> 12) { case 0: @@ -900,8 +865,7 @@ global_shoebill_context_t shoe; if (s < 2) { immed = chop(nextword(), sz); } else { - immed = nextword(); - immed = (immed << 16) | nextword(); + immed = nextlong(); } call_ea_read(M, sz); @@ -1155,25 +1119,64 @@ global_shoebill_context_t shoe; ~inst(movea, { ~decompose(shoe.op, 00 ab rrr 001 MMMMMM); - if (a == 0) { // only word and long sizes are supported for movea - throw_illegal_instruction(); - return ; - } - const uint8_t sz = b ? 2 : 4; // 1<<(1+(!b)); // (3=word, 2=long) + + const uint8_t sz = b ? 2 : 4; call_ea_read(M, sz); call_ea_read_commit(M, sz); if (b) { // word-size, sign extend shoe.dat - int16_t dat = shoe.dat; + const int16_t dat = shoe.dat; shoe.a[r] = (int32_t)dat; } else { - // printf("r = %u, dat=0x%llx\n", r, shoe.dat); shoe.a[r] = shoe.dat; } }) +~inst(move_d_to_d, { + ~decompose(shoe.op, 00 ab RRR 000 000 rrr); // r=source, R=dest + + const uint8_t sz = 1<<(a+(!b)); // (1=byte, 3=word, 2=long) + const uint32_t val = chop(shoe.d[r], sz); + + set_d(R, val, sz); + + set_sr_v(0); + set_sr_c(0); + set_sr_n(mib(val, sz)); + set_sr_z(val == 0); +}) + +~inst(move_from_d, { + ~decompose(shoe.op, 00 ab RRR MMM 000 rrr); // r=source, MR=dest + + const uint8_t sz = 1<<(a+(!b)); // (1=byte, 3=word, 2=long) + const uint32_t val = chop(shoe.d[r], sz); + + set_sr_v(0); + set_sr_c(0); + set_sr_n(mib(val, sz)); + set_sr_z(val == 0); + + shoe.dat = val; + call_ea_write((M << 3) | R, sz); +}) + +~inst(move_to_d, { + ~decompose(shoe.op, 00 ab rrr 000 mmmmmm); // m=source, r=dest + const uint8_t sz = 1<<(a+(!b)); // (1=byte, 3=word, 2=long) + + call_ea_read(m, sz); + call_ea_read_commit(m, sz); + + set_sr_v(0); + set_sr_c(0); + set_sr_n(ea_n(sz)); + set_sr_z(ea_z(sz)); + set_d(r, shoe.dat, sz); +}) + ~inst(move, { - ~decompose(shoe.op, 00 ab RRR MMM mmm rrr); // o=source, MR=dest + ~decompose(shoe.op, 00 ab RRR MMM mmm rrr); // mr=source, MR=dest const uint8_t sz = 1<<(a+(!b)); // (1=byte, 3=word, 2=long) call_ea_read((m<<3) | r, sz); @@ -1567,12 +1570,12 @@ global_shoebill_context_t shoe; } else if (s==1) { immed = (int16_t)nextword(); } else { - immed = nextword(); - immed = (immed << 16) | nextword(); + immed = nextlong(); } // fetch the destination operand call_ea_read(M, sz); + // do the subtraction const uint32_t result = shoe.dat - immed; // find the MIBs for source, dest, and result @@ -1602,8 +1605,7 @@ global_shoebill_context_t shoe; } else if (s==1) { immed = (int16_t)nextword(); } else { - immed = nextword(); - immed = (immed << 16) | nextword(); + immed = nextlong(); } call_ea_read(M, sz); @@ -1633,8 +1635,7 @@ global_shoebill_context_t shoe; } else if (s==1) { immed = (int16_t)nextword(); } else { - immed = nextword(); - immed = (immed << 16) | nextword(); + immed = nextlong(); } call_ea_read(M, sz); @@ -1660,8 +1661,7 @@ global_shoebill_context_t shoe; } else if (s==1) { immed = (int16_t)nextword(); } else { - immed = nextword(); - immed = (immed << 16) | nextword(); + immed = nextlong(); } // fetch the destination operand @@ -1689,8 +1689,7 @@ global_shoebill_context_t shoe; } else if (s==1) { immed = (int16_t)nextword(); } else { - immed = nextword(); - immed = (immed << 16) | nextword(); + immed = nextlong(); } // fetch the destination operand @@ -1813,33 +1812,15 @@ global_shoebill_context_t shoe; ~inst(dbcc, { ~decompose(shoe.op, 0101 cccc 11001 rrr); - const uint32_t orig_pc = shoe.pc; - const int16_t disp = nextword(); - const uint8_t C = sr_c(); - const uint8_t Z = sr_z(); - const uint8_t V = sr_v(); - const uint8_t N = sr_n(); - switch (c) { - case 0: return ; // dbt (what a useless instruction: c==0 => condition=true => return without doing anything) - case 1: break ; // dbf (or dbra) - case 2: if (!C && !Z) return; break; // dbhi - case 3: if (C || Z) return; break; // dbls - case 4: if (!C) return; break; // dbcc - case 5: if (C) return; break; // dbcs - case 6: if (!Z) return; break; // dbne - case 7: if (Z) return; break; // dbeq - case 8: if (!V) return; break; // dbvc - case 9: if (V) return; break; // dbvs - case 10: if (!N) return; break; // dbpl - case 11: if (N) return; break; // dbmi - case 12: if ( (N && V) || (!N && !V) ) return; break; // dbge - case 13: if ( (N && !V) || (!N && V) ) return; break; // dblt - case 14: if ( (N && V && !Z) || (!N && !V && !Z) ) return; break; // dbgt - case 15: if ( (Z || (N && !V) || (!N && V) ) ) return; break; // dble - } - set_d(r, get_d(r, 2)-1, 2); - if (get_d(r, 2) != 0xffff) { - shoe.pc = orig_pc + disp; + if (evaluate_cc[c]()) { + shoe.pc += 2; + } + else { + const int16_t disp = nextword(); + const uint16_t newd = get_d(r, 2) - 1; + set_d(r, newd, 2); + if (newd != 0xffff) + shoe.pc = shoe.pc + disp - 2; } }) @@ -1849,14 +1830,11 @@ global_shoebill_context_t shoe; // find the new PC if ((d==0) || (d==0xff)) { - const uint16_t ext = nextword(); if (d==0xff) { - uint32_t mylong = ((uint32_t)ext)<<16; - mylong |= nextword(); - new_pc += mylong; + new_pc += nextlong(); } else - new_pc += ((int16_t)ext); + new_pc += ((int16_t)nextword()); } else { uint8_t tmp = d; @@ -1870,137 +1848,43 @@ global_shoebill_context_t shoe; }) ~inst(bcc, { - uint32_t new_pc = shoe.pc; + const uint32_t orig_pc = shoe.pc; ~decompose(shoe.op, 0110 cccc dddddddd); - // find the new PC - if ((d==0) || (d==0xff)) { - const uint16_t ext = nextword(); - if (d==0xff) { - uint32_t mylong = ((uint32_t)ext)<<16; - mylong |= nextword(); - new_pc += mylong; - } - else - new_pc += ((int16_t)ext); - } - else { - uint8_t tmp = d; - new_pc += ((int8_t)d); - } - // we'll use these control codes - const uint8_t C = sr_c(); - const uint8_t Z = sr_z(); - const uint8_t V = sr_v(); - const uint8_t N = sr_n(); - - // branch conditionally - switch (c) { - case 0: { // BRA - shoe.pc = new_pc; return; // branch unconditionally + if (evaluate_cc[c]()) { + if (d == 0) { + const int16_t ext = (int16_t)nextword(); + shoe.pc = orig_pc + ext; } - case 1: { - assert(!"How did we get here?"); // decoder should have called inst_bsr() + else if (d == 0xff) { + shoe.pc = orig_pc + nextlong(); + } + else { + const int8_t tmp = (int8_t)d; + shoe.pc = orig_pc + tmp; } - case 2: if (!C && !Z) shoe.pc = new_pc; return; // bhi - case 3: if (C || Z) shoe.pc = new_pc; return; // bls - case 4: if (!C) shoe.pc = new_pc; return; // bcc - case 5: if (C) shoe.pc = new_pc; return; // bcs - case 6: if (!Z) shoe.pc = new_pc; return; // bne - case 7: if (Z) shoe.pc = new_pc; return; // beq - case 8: if (!V) shoe.pc = new_pc; return; // bvc - case 9: if (V) shoe.pc = new_pc ; return; // bvs - case 10: if (!N) shoe.pc = new_pc; return; // bpl - case 11: if (N) shoe.pc = new_pc; return; // bmi - case 12: if ( (N && V) || (!N && !V) ) shoe.pc = new_pc; return; // bge - case 13: if ( (N && !V) || (!N && V) ) shoe.pc = new_pc; return; // blt - case 14: if ( (N && V && !Z) || (!N && !V && !Z) ) shoe.pc = new_pc; return; // bgt - case 15: if ( (Z || (N && !V) || (!N && V) ) ) shoe.pc = new_pc; return; // ble + } + else { + if (d == 0) shoe.pc += 2; + else if (d == 0xff) shoe.pc += 4; } }) ~inst(scc, { ~decompose(shoe.op, 0101 cccc 11 MMMMMM); - const uint8_t C = sr_c(); - const uint8_t Z = sr_z(); - const uint8_t V = sr_v(); - const uint8_t N = sr_n(); - uint8_t byte = 0; - switch (c) { - case 0: // st (set unconditionally? FIXME: TEST ME!) - byte = 0xff; - break; - case 1: // sf - byte = 0x0; // (FIXME: do-not-set unconditionally? This can possibly be right) - break; - case 2: - if (!C && !Z) byte = 0xff; // shi - break; - case 3: - if (C || Z) byte = 0xff; // sls - break; - case 4: - if (!C) byte = 0xff; // scc - break; - case 5: - if (C) byte = 0xff; // scs - break; - case 6: - if (!Z) byte = 0xff; // sne - break; - case 7: - if (Z) byte = 0xff; // seq - break; - case 8: - if (!V) byte = 0xff; // svc - break; - case 9: - if (V) byte = 0xff; // svs - break; - case 10: - if (!N) byte = 0xff; // spl - break; - case 11: - if (N) byte = 0xff; // smi - break; - case 12: - if ( (N && V) || (!N && !V) ) byte = 0xff; // sge - break; - case 13: - if ( (N && !V) || (!N && V) ) byte = 0xff; // slt - break; - case 14: - if ( (N && V && !Z) || (!N && !V && !Z) ) byte = 0xff; // sgt - break; - case 15: - if ( (Z || (N && !V) || (!N && V) ) ) byte = 0xff; // sle - break; - } - shoe.dat = byte; + shoe.dat = evaluate_cc[c]() ? 0xff : 0; call_ea_write(M, 1); }) ~inst(nop, {}) -/* - // Illegal on 68040 -~inst(cinv, { - // Caches aren't implemented, so do nothing -}) - -~inst(cpush, { - // Caches aren't implemented, so do nothing -}) -*/ - ~inst(chk, { ~decompose(shoe.op, 0100 rrr 1s 0 MMMMMM); const uint8_t sz = s ? 2 : 4; call_ea_read(M, sz); - call_ea_read_commit(M, sz); int32_t reg, ea; @@ -2019,6 +1903,8 @@ global_shoebill_context_t shoe; set_sr_n((reg < 0)); throw_frame_two(shoe.sr, shoe.pc, 6, shoe.orig_pc); } + + call_ea_read_commit(M, sz); return ; }) @@ -2036,39 +1922,22 @@ global_shoebill_context_t shoe; shoe.a[7] -= 4; shoe.pc = shoe.dat; - if (sr_s()&&0) { + /* + if (sr_s()) { //return ; coff_symbol *symb = coff_find_func(shoe.coff, shoe.pc); + if (symb) { - uint32_t i; - - printf("CALL %s+%u 0x%08x\n", symb->name, shoe.pc-symb->value, shoe.pc); - if (strcmp(symb->name, "tracescsi") == 0) { - uint32_t addr = lget(shoe.a[7]+4, 4); - - printf("tracescsi: ["); - char c; - do { - c = lget(addr++, 1); - printf("%c", c); - } while ((c != 0) && (!shoe.abort)); - printf("]\n"); - } + sprintf(ring_tmp, "CALL (s) %s+%u 0x%08x\n", symb->name, shoe.pc-symb->value, shoe.pc); + ring_print(ring_tmp); } - - if (shoe.pc == 0x1002ba94) { - uint32_t addr = lget(shoe.a[7]+4, 4); - printf("panic: ["); - char c; - do { - c = lget(addr++, 1); - printf("%c", c); - } while ((c != 0) && (!shoe.abort)); - printf("]\n"); + else { + sprintf(ring_tmp, "CALL (s) unknown+ 0x%08x\n", shoe.pc); + ring_print(ring_tmp); } } - else if (0) { + else { char *name = (char*)"unknown"; uint32_t value = 0; if ((shoe.pc >= 0x40000000) && (shoe.pc < 0x50000000)) { @@ -2081,16 +1950,12 @@ global_shoebill_context_t shoe; value = macii_rom_symbols[i].addr; } } - /*else { - coff_symbol *symb = coff_find_func(shoe.launch, shoe.pc); - if (symb) { - value = symb->value; - name = symb->name; - } - }*/ - printf("CALL %s+%u 0x%08x\n", name, shoe.pc-value, shoe.pc); + + sprintf(ring_tmp, "CALL (u) %s+%u 0x%08x\n", name, shoe.pc-value, shoe.pc); + ring_print(ring_tmp); } + */ }) @@ -2136,6 +2001,7 @@ global_shoebill_context_t shoe; shoe.a[7] += 4; shoe.pc = pop; + /* if (sr_s() && 0) { // return ; coff_symbol *symb = coff_find_func(shoe.coff, shoe.pc); @@ -2155,23 +2021,15 @@ global_shoebill_context_t shoe; value = macii_rom_symbols[i].addr; } } - /*else { - coff_symbol *symb = coff_find_func(shoe.launch, shoe.pc); - if (symb) { - value = symb->value; - name = symb->name; - } - }*/ printf("RETURN TO %s+%u 0x%08x\n", name, shoe.pc-value, shoe.pc); - } + }*/ }) ~inst(link_long, { ~decompose(shoe.op, 0100 1000 0000 1 rrr); - const uint16_t ext_hi = nextword(); - const uint32_t disp = (ext_hi<<16) | nextword(); + const uint32_t disp = nextlong(); // push the contents of the address register onto the stack lset(shoe.a[7]-4, 4, shoe.a[r]); @@ -2926,14 +2784,19 @@ uint32_t extract_bitfield(const uint32_t width, const uint32_t offset, const uin assert(!"never get here"); }) +void dump_ring(); ~inst(unknown, { printf("Unknown instruction (0x%04x)!\n", shoe.op); + /*if (shoe.op == 0x33fe) { + dump_ring(); + assert(!"dumped"); + }*/ throw_illegal_instruction(); }) ~inst(a_line, { - if (shoe.op == 0xA9EB || shoe.op == 0xA9EC) { + /*if (shoe.op == 0xA9EB || shoe.op == 0xA9EC) { shoe.suppress_exceptions = 1; uint32_t fp_op = lget(shoe.a[7]+0, 2); @@ -2961,7 +2824,7 @@ uint32_t extract_bitfield(const uint32_t width, const uint32_t offset, const uin shoe.abort = 0; shoe.suppress_exceptions = 0; - } + }*/ throw_illegal_instruction(); }) @@ -3058,36 +2921,35 @@ void trap_debug() void cpu_step() { - while (1) { - // remember the PC and SR (so we can throw exceptions later) - shoe.orig_pc = shoe.pc; - shoe.orig_sr = shoe.sr; - - // Is this an odd address? Throw an address exception! - if (shoe.pc & 1) { - // throw_address_error(shoe.pc, 0); - assert(!"What do I do here?"); - continue; - } - - // Fetch the next instruction word - shoe.op = lget(shoe.pc, 2); - - // If there was an exception, then the pc changed. Restart execution from the beginning. - if (shoe.abort) { - shoe.abort = 0; - continue; - } - shoe.pc+=2; - - inst_instruction_to_pointer[inst_opcode_map[shoe.op]](); - - /* The abort flag indicates that a routine should stop trying to execute the - instruction and return immediately to cpu_step(), usually to begin - exception processing */ - - shoe.abort = 0; // clear the abort flag + // remember the PC and SR (so we can throw exceptions later) + shoe.orig_pc = shoe.pc; + shoe.orig_sr = shoe.sr; + + // Is this an odd address? Throw an address exception! + if (shoe.pc & 1) { + // throw_address_error(shoe.pc, 0); + // I'm leaving this assert in here for now because it almost always indicates a bug in the emulator when it fires + assert(!"What do I do here?"); + return ; + } + + // Fetch the next instruction word + shoe.op = lget(shoe.pc, 2); + + // If there was an exception, then the pc changed. Restart execution from the beginning. + if (shoe.abort) { + shoe.abort = 0; return ; } + shoe.pc+=2; + + inst_instruction_to_pointer[inst_opcode_map[shoe.op]](); + + /* The abort flag indicates that a routine should stop trying to execute the + instruction and return immediately to cpu_step(), usually to begin + exception processing */ + + shoe.abort = 0; // clear the abort flag + return ; } diff --git a/core/decoder_gen.c b/core/decoder_gen.c index fba40d6..71a1e0b 100644 --- a/core/decoder_gen.c +++ b/core/decoder_gen.c @@ -929,6 +929,65 @@ void begin_definitions() // subtract illegal size (00) sub_range(inst, "00 00 xxxxxx xxxxxx"); + + // subtract move_from_d + sub_range(inst, "00 xx xxxxxx 000xxx"); + + // subtract move_to_d + sub_range(inst, "00 xx xxx000 xxxxxx"); + } + + { // move_d_to_d + inst_t *inst = new_inst("move_d_to_d", "all", 1); + no_ea(inst); + + add_range(inst, "00 xx xxx 000 000 xxx"); + sub_range(inst, "00 00 xxx 000 000 xxx"); + } + + { // move_to_d + inst_t *inst = new_inst("move_to_d", "all", 2); + { // EA mode == addr register (byte-size not allowed) + set_range_group(inst, 0); + add_range(inst, "00 11 xxx000 MMMMMM"); + add_range(inst, "00 10 xxx000 MMMMMM"); + ea_add_mode(inst, EA_001); + } + { // all other EA modes + set_range_group(inst, 1); + add_range(inst, "00 xx xxx000 MMMMMM"); + sub_range(inst, "00 00 xxx000 MMMMMM"); + ea_all(inst); + ea_sub_mode(inst, EA_001); + ea_sub_mode(inst, EA_000); // EA_000 is handled by move_d_to_d + } + } + + { // move_from_d + inst_t *inst = new_inst("move_from_d", "all", 1); + + // I'm manually specifying this entire thing, since the EA description is too complicated + no_ea(inst); + + // Add in all modes, all sizes + add_range(inst, "00 xx xxxxxx 000xxx"); + + // subtract the invalid destination modes (001, 111_010, 111_011, 111_100) + sub_range(inst, "00 xx xxx 001 xxxxxx"); + sub_range(inst, "00 xx 010 111 xxxxxx"); + sub_range(inst, "00 xx 011 111 xxxxxx"); + sub_range(inst, "00 xx 100 111 xxxxxx"); + + // subtract the illegal destination modes (111_101, 111_110, 111_111) + sub_range(inst, "00 xx 101 111 xxxxxx"); + sub_range(inst, "00 xx 110 111 xxxxxx"); + sub_range(inst, "00 xx 111 111 xxxxxx"); + + // subtract illegal size (00) + sub_range(inst, "00 00 xxxxxx xxxxxx"); + + // subtract move_d_to_d + sub_range(inst, "00 xx xxx 000 000xxx"); } { // movea diff --git a/core/dis.c b/core/dis.c index 616d80d..8a50570 100644 --- a/core/dis.c +++ b/core/dis.c @@ -802,6 +802,18 @@ void dis_move () { sprintf(dis.str, "move.%c %s,%s", "blw"[s-1], sourceStr, destStr); } +void dis_move_d_to_d () { + sprintf(dis.str, "move_d_to_d ???"); +} + +void dis_move_to_d () { + sprintf(dis.str, "move_to_d ???"); +} + +void dis_move_from_d () { + sprintf(dis.str, "move_from_d ???"); +} + void dis_scc () { const char *condition_names[16] = { "t", "ra", "hi", "ls", "cc", "cs", "ne", "eq", diff --git a/core/filesystem.c b/core/filesystem.c index 220bfb4..1d5ccf6 100644 --- a/core/filesystem.c +++ b/core/filesystem.c @@ -28,15 +28,7 @@ #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; +#include "../core/shoebill.h" #define fix_endian(x) do { \ if (ntohs(1) == 1) \ @@ -45,76 +37,9 @@ typedef struct _alloc_pool_t{ 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 @@ -344,8 +269,8 @@ static disk_t* open_disk (const char *disk_path, char *error_str) 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); + 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; @@ -855,7 +780,8 @@ typedef struct __attribute__ ((__packed__)) { uint16_t nlink; uint16_t uid; uint16_t gid; - uint64_t size; + uint32_t size_hi; // UFS stores size as a uint64_t, but A/UX apparently only reads/writes + uint32_t size; // to the low bits, so sometimes the hi bits contain garbage uint32_t atime; uint32_t dummy; uint32_t mtime; @@ -986,6 +912,7 @@ static uint8_t ufs_load_inode(ufs_t *mount, ufs_inode_t *inode, uint32_t inum) fix_endian(inode->nlink); fix_endian(inode->uid); fix_endian(inode->gid); + fix_endian(inode->size_hi); fix_endian(inode->size); fix_endian(inode->atime); fix_endian(inode->mtime); @@ -1043,7 +970,7 @@ static uint8_t ufs_read_level(ufs_t *mount, 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); + 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) { @@ -1325,6 +1252,7 @@ uint8_t* shoebill_extract_kernel(const char *disk_path, const char *kernel_path, sprintf(error_str, "Couldn't find root partition"); goto done; } + printf("apm_part_num = %u\n", apm_part_num); svfs_mount_obj = svfs_mount(&disk->partitions[apm_part_num]); if (svfs_mount_obj) { diff --git a/core/mc68851.c b/core/mc68851.c index 338e8d2..937dc2d 100644 --- a/core/mc68851.c +++ b/core/mc68851.c @@ -81,15 +81,15 @@ void inst_mc68851_pflushr(uint16_t ext){ verify_supervisor(); printf("pflushr!"); // Just nuke the entire cache - bzero(shoe.pmmu_cache[0].valid_map, 512/8); - bzero(shoe.pmmu_cache[1].valid_map, 512/8); + bzero(shoe.pmmu_cache[0].valid_map, PMMU_CACHE_SIZE/8); + bzero(shoe.pmmu_cache[1].valid_map, PMMU_CACHE_SIZE/8); } void inst_mc68851_pflush(uint16_t ext){ verify_supervisor(); printf("pflush!"); - bzero(shoe.pmmu_cache[0].valid_map, 512/8); - bzero(shoe.pmmu_cache[1].valid_map, 512/8); + bzero(shoe.pmmu_cache[0].valid_map, PMMU_CACHE_SIZE/8); + bzero(shoe.pmmu_cache[1].valid_map, PMMU_CACHE_SIZE/8); // printf("%s: Error, not implemented!\n", __func__); } diff --git a/core/mem.c b/core/mem.c index 6c69d81..df2a870 100644 --- a/core/mem.c +++ b/core/mem.c @@ -30,410 +30,250 @@ #include #include "../core/shoebill.h" -static void translate_logical_addr(); +#ifdef __APPLE__ +#include +#define ntohll(x) OSSwapBigToHostInt64(x) +#endif -void physical_get (void) { +/* --- Physical_get jump table --- */ +#pragma mark Physical_get jump table + +void _physical_get_ram (void) +{ + uint64_t *addr; + if (shoe.physical_addr < shoe.physical_mem_size) + addr = (uint64_t*)&shoe.physical_mem_base[shoe.physical_addr]; + else + addr = (uint64_t*)&shoe.physical_mem_base[shoe.physical_addr % shoe.physical_mem_size]; - switch (shoe.physical_addr >> 28) { - case 0: - case 1: - case 2: - case 3: { // RAM - void *addr; - if (shoe.physical_addr >= shoe.physical_mem_size) - addr = &shoe.physical_mem_base[shoe.physical_addr % shoe.physical_mem_size]; - else - addr = &shoe.physical_mem_base[shoe.physical_addr]; - - switch (shoe.physical_size) { - case 4: - shoe.physical_dat = ntohl(*(uint32_t*)addr); - return ; - case 2: - shoe.physical_dat = ntohs(*(uint16_t*)addr); - return ; - case 1: - shoe.physical_dat = *(uint8_t*)addr; - return ; - default: { - const uint32_t s = shoe.physical_size; - uint64_t q = 0; - uint32_t i; - for (i=0; i> 24) & 0xf; - if (shoe.slots[slot].connected) - shoe.physical_dat = shoe.slots[slot].read_func(shoe.physical_addr, - shoe.physical_size, - slot); - else - shoe.abort = 1; // throw a bus error for reads to disconnected slots + const uint8_t bits = (8 - shoe.physical_size) * 8; + shoe.physical_dat = ntohll(*addr) >> bits; +} + +void _physical_get_rom (void) +{ + uint64_t *addr = (uint64_t*)&shoe.physical_rom_base[shoe.physical_addr & (shoe.physical_rom_size-1)]; + + const uint8_t bits = (8 - shoe.physical_size) * 8; + shoe.physical_dat = ntohll(*addr) >> bits; +} + +void _physical_get_io (void) +{ + switch (shoe.physical_addr & 0x5003ffff) { + case 0x50000000 ... 0x50001fff: // VIA1 + via_reg_read(); + // printf("physical_get: got read to VIA1 (%x & 0x5003ffff = %x)\n", shoe.physical_addr, shoe.physical_addr & 0x5003ffff); return ; - } - default: { // Nubus super slot space - const uint32_t slot = shoe.physical_addr >> 28; - if (shoe.slots[slot].connected) - shoe.physical_dat = shoe.slots[slot].read_func(shoe.physical_addr, - shoe.physical_size, - slot); - else - shoe.abort = 1; // throw a bus error for reads to disconnected slots - // XXX: Do super slot accesses raise bus errors? + case 0x50002000 ... 0x50003fff: // VIA2 + via_reg_read(); + // printf("physical_get: got read to VIA2\n"); + return ; + case 0x50004000 ... 0x50005fff: {// SCC + //printf("physical_get: got read to SCC\n"); + const uint32_t a = shoe.physical_addr & 0x1fff; + if (a == 2 && shoe.physical_size==1) + shoe.physical_dat = 0x4; // R0TXRDY return ; } + case 0x50006000 ... 0x50007fff: // SCSI (pseudo-DMA with DRQ?) + //printf("physical_get: got read to SCSI low\n"); + //assert(!"physical_get: got read to SCSI low"); + assert(shoe.logical_size == 4); + shoe.physical_dat = scsi_dma_read_long(); + return ; + case 0x50010000 ... 0x50011fff: // SCSI (normal mode?) + scsi_reg_read(); + return ; + case 0x50012000 ... 0x50013fff: // SCSI (pseudo-DMA with no DRQ?) + assert(shoe.logical_size == 1); + shoe.physical_dat = scsi_dma_read(); + // printf("physical_get: got read to SCSI hi\n"); + // assert(!"physical_get: got read to SCSI hi\n"); + return ; + case 0x50014000 ... 0x50015fff: // Sound + //printf("physical_get: got read to sound\n"); + return ; + case 0x50016000 ... 0x50017fff: // SWIM (IWM?) + // printf("physical_get: got read to IWM\n"); + shoe.physical_dat = iwm_dma_read(); + return ; + default: + //printf("physical_get: got read to UNKNOWN IO ADDR %x\n", shoe.physical_addr); + return ; } } -void physical_set (void) +void _physical_get_super_slot (void) { - switch (shoe.physical_addr >> 28) { - case 0: + const uint32_t slot = shoe.physical_addr >> 28; + if (shoe.slots[slot].connected) + shoe.physical_dat = shoe.slots[slot].read_func(shoe.physical_addr, + shoe.physical_size, + slot); + else + shoe.abort = 1; // throw a bus error for reads to disconnected slots + // XXX: Do super slot accesses raise bus errors? +} + +void _physical_get_standard_slot (void) +{ + const uint32_t slot = (shoe.physical_addr >> 24) & 0xf; + if (shoe.slots[slot].connected) + shoe.physical_dat = shoe.slots[slot].read_func(shoe.physical_addr, + shoe.physical_size, + slot); + else + shoe.abort = 1; // throw a bus error for reads to disconnected slots +} + +const physical_get_ptr physical_get_jump_table[16] = { + _physical_get_ram, // 0x0 + _physical_get_ram, // 0x1 + _physical_get_ram, // 0x2 + _physical_get_ram, // 0x3 + _physical_get_rom, // 0x4 + _physical_get_io, // 0x5 + _physical_get_super_slot, // 0x6 + _physical_get_super_slot, // 0x7 + _physical_get_super_slot, // 0x8 + _physical_get_super_slot, // 0x9 + _physical_get_super_slot, // 0xa + _physical_get_super_slot, // 0xb + _physical_get_super_slot, // 0xc + _physical_get_super_slot, // 0xd + _physical_get_super_slot, // 0xe + _physical_get_standard_slot // 0xf +}; + +/* --- Physical_set jump table --- */ +#pragma mark Physical_set jump table + +void _physical_set_ram (void) +{ + uint8_t *addr; + if (shoe.physical_addr >= shoe.physical_mem_size) + addr = &shoe.physical_mem_base[shoe.physical_addr % shoe.physical_mem_size]; + else + addr = &shoe.physical_mem_base[shoe.physical_addr]; + + const uint32_t sz = shoe.physical_size; + switch (sz) { case 1: + *addr = (uint8_t)shoe.physical_dat; + return ; + case 2: - case 3: { // RAM - const uint32_t dat = htonl(shoe.physical_dat) >> ((4-shoe.physical_size)*8); - void *addr; - if (shoe.physical_addr >= shoe.physical_mem_size) - addr = &shoe.physical_mem_base[shoe.physical_addr % shoe.physical_mem_size]; - else - addr = &shoe.physical_mem_base[shoe.physical_addr]; - switch (shoe.physical_size) { - case 4: - *((uint32_t*)addr) = dat; - return ; - case 2: - *((uint16_t*)addr) = (uint16_t)dat; - return ; - case 1: - *((uint8_t*)addr) = (uint8_t)dat; - return ; - // case 8: { } // I should do something fast for case 8 here - default: { - const uint32_t s = shoe.physical_size; - uint64_t q = shoe.physical_dat; - uint32_t i; - for (i=0; i>= 8; - } - return ; - } - } - assert(!"never get here"); - } - case 4: { // ROM - // Nothing happens when you try to modify ROM + *((uint16_t*)addr) = htons((uint16_t)shoe.physical_dat); return ; - } - case 5: { // IO - switch (shoe.physical_addr & 0x5003ffff) { - case 0x50000000 ... 0x50001fff: // VIA1 - via_reg_write(); - // printf("physical_set: got write to VIA1\n"); - return ; - case 0x50002000 ... 0x50003fff: // VIA2 - via_reg_write(); - // printf("physical_set: got write to VIA2\n"); - return ; - case 0x50004000 ... 0x50005fff: // SCC - //printf("physical_set: got write to SCC\n"); - return ; - case 0x50006000 ... 0x50007fff: // SCSI (pseudo-DMA with DRQ?) - assert(shoe.physical_size == 4); - scsi_dma_write_long(shoe.physical_dat); - return ; - case 0x50010000 ... 0x50011fff: // SCSI (normal mode?) - scsi_reg_write(); - return ; - case 0x50012000 ... 0x50013fff: // SCSI (pseudo-DMA with no DRQ?) - assert(shoe.physical_size == 1); - scsi_dma_write(shoe.physical_dat); - //printf("physical_set: got write to SCSI hi\n"); - //assert(!"physical_set: got write to SCSI hi\n"); - return ; - case 0x50014000 ... 0x50015fff: // Sound - printf("physical_set: got write to sound\n"); - return ; - case 0x50016000 ... 0x50017fff: // SWIM (IWM?) - //printf("physical_set: got write to IWM\n"); - iwm_dma_write(); - return ; - default: - //printf("physical_set: got write to UNKNOWN IO ADDR %x\n", shoe.physical_addr); - return ; - } - } - case 0xf: { // Nubus standard slot space - const uint32_t slot = (shoe.physical_addr >> 24) & 0xf; - if (shoe.slots[slot].connected) - shoe.slots[slot].write_func(shoe.physical_addr, - shoe.physical_size, - shoe.physical_dat, - slot); + + case 4: + *((uint32_t*)addr) = htonl((uint32_t)shoe.physical_dat); return ; - // Writing to a disconnected slot won't cause a bus error on macii - } - default: { // Nubus super slot space - const uint32_t slot = shoe.physical_addr >> 28; - if (shoe.slots[slot].connected) - shoe.slots[slot].write_func(shoe.physical_addr, - shoe.physical_size, - shoe.physical_dat, - slot); + + case 8: + *(uint64_t*)addr = ntohll(shoe.physical_dat); + return ; + + default: { + uint64_t q = shoe.physical_dat; + uint8_t i; + for (i=1; i<=sz; i++) { + addr[sz-i] = (uint8_t)q; + q >>= 8; + } return ; } } } -void logical_get (void) +void _physical_set_rom (void) { - - // If address translation isn't enabled, this is a physical address - if (!tc_enable()) { - shoe.physical_addr = shoe.logical_addr; - shoe.physical_size = shoe.logical_size; - physical_get(); - if (shoe.abort) { - shoe.abort = 0; - throw_long_bus_error(shoe.logical_addr, 0); + // nothing happens when you try to modify ROM +} + +void _physical_set_io (void) +{ + switch (shoe.physical_addr & 0x5003ffff) { + case 0x50000000 ... 0x50001fff: // VIA1 + via_reg_write(); + // printf("physical_set: got write to VIA1\n"); return ; - } - shoe.logical_dat = shoe.physical_dat; - return ; - } - - const uint32_t logical_size = shoe.logical_size; - const uint32_t logical_addr = shoe.logical_addr; - - const uint16_t ps = tc_ps(); // log2 of the page size - const uint32_t pagesize = 1 << ps; // the page size - const uint32_t pagemask = pagesize-1; // a mask of the page bits - const uint32_t pageoffset = logical_addr & pagemask; - - shoe.logical_is_write = 0; - - // This read crosses a page boundary - if ((pageoffset + logical_size - 1) >> ps) { - const uint32_t addr_a = shoe.logical_addr; - const uint32_t size_b = (pageoffset + logical_size) & pagemask; - const uint32_t size_a = shoe.logical_size - size_b; - const uint32_t addr_b = addr_a + size_a; - - // printf("ps = %u, pagesize=%u, pagemask=%x, pageoffset=%x\n", ps, pagesize, pagemask, pageoffset); - // printf("multiple page get: addr_a=0x%08x size_a=%u addr_b=0x%08x size_b=%u\n", addr_a, size_a, addr_b, size_b); - shoe.logical_addr = addr_a; - shoe.logical_size = size_a; - translate_logical_addr(); - if (shoe.abort) + case 0x50002000 ... 0x50003fff: // VIA2 + via_reg_write(); + // printf("physical_set: got write to VIA2\n"); return ; - - const uint32_t p_addr_a = shoe.physical_addr; - shoe.physical_size = size_a; - physical_get(); - if (shoe.abort) { - shoe.abort = 0; - throw_long_bus_error(shoe.logical_addr, 0); + case 0x50004000 ... 0x50005fff: // SCC + //printf("physical_set: got write to SCC\n"); return ; - } - const uint64_t fetch_a = shoe.physical_dat; - - shoe.logical_addr = addr_b; - shoe.logical_size = size_b; - translate_logical_addr(); - if (shoe.abort) + case 0x50006000 ... 0x50007fff: // SCSI (pseudo-DMA with DRQ?) + assert(shoe.physical_size == 4); + scsi_dma_write_long(shoe.physical_dat); return ; - - const uint32_t p_addr_b = shoe.physical_addr; - shoe.physical_size = size_b; - physical_get(); - if (shoe.abort) { - shoe.abort = 0; - throw_long_bus_error(shoe.logical_addr, 0); + case 0x50010000 ... 0x50011fff: // SCSI (normal mode?) + scsi_reg_write(); + return ; + case 0x50012000 ... 0x50013fff: // SCSI (pseudo-DMA with no DRQ?) + assert(shoe.physical_size == 1); + scsi_dma_write(shoe.physical_dat); + //printf("physical_set: got write to SCSI hi\n"); + //assert(!"physical_set: got write to SCSI hi\n"); + return ; + case 0x50014000 ... 0x50015fff: // Sound + printf("physical_set: got write to sound\n"); + return ; + case 0x50016000 ... 0x50017fff: // SWIM (IWM?) + //printf("physical_set: got write to IWM\n"); + iwm_dma_write(); + return ; + default: + //printf("physical_set: got write to UNKNOWN IO ADDR %x\n", shoe.physical_addr); return ; - } - - // printf("multiple_page_get: p_addr_a = 0x%08x, p_addr_b = 0x%08x\n", p_addr_a, p_addr_b); - - shoe.logical_dat = (fetch_a << (size_b*8)) | shoe.physical_dat; - - // printf("multiple_page_get: logical_dat = (%llx | %llx) = %llx\n", fetch_a, shoe.physical_dat, shoe.logical_dat); - - return ; - } - - // Common case: the read is contained entirely within a page - translate_logical_addr(); - if (shoe.abort) - return ; - - shoe.physical_size = shoe.logical_size; - physical_get(); - if (shoe.abort) { - shoe.abort = 0; - throw_long_bus_error(shoe.logical_addr, 0); - return ; } - shoe.logical_dat = shoe.physical_dat; - } -void logical_set (void) +void _physical_set_super_slot (void) { - // If address translation isn't enabled, this is a physical address - if (!tc_enable()) { - shoe.physical_addr = shoe.logical_addr; - shoe.physical_size = shoe.logical_size; - shoe.physical_dat = shoe.logical_dat; - physical_set(); - return ; - } - - const uint32_t logical_size = shoe.logical_size; - const uint32_t logical_addr = shoe.logical_addr; - - const uint16_t ps = tc_ps(); // log2 of the page size - const uint32_t pagesize = 1 << ps; // the page size - const uint32_t pagemask = pagesize-1; // a mask of the page bits - const uint32_t pageoffset = logical_addr & pagemask; - - // Make the translate function fail if the page is write-protected - shoe.logical_is_write = 1; - - // This write crosses a page boundary - if ((pageoffset + logical_size - 1) >> ps) { - const uint32_t addr_a = shoe.logical_addr; - const uint32_t size_b = (pageoffset + logical_size) & pagemask; - const uint32_t size_a = shoe.logical_size - size_b; - const uint32_t addr_b = addr_a + size_a; - const uint64_t data_a = shoe.logical_dat >> (size_b*8); - const uint64_t data_b = bitchop_64(shoe.logical_dat, size_b*8); - - // printf("ps = %u, pagesize=%u, pagemask=%x, pageoffset=%x\n", ps, pagesize, pagemask, pageoffset); - // printf("multiple page set: addr_a=0x%08x size_a=%u addr_b=0x%08x size_b=%u\n", addr_a, size_a, addr_b, size_b); - - shoe.logical_addr = addr_a; - shoe.logical_size = size_a; - translate_logical_addr(); - if (shoe.abort) - return ; - const uint32_t p_addr_a = shoe.physical_addr; - - shoe.logical_addr = addr_b; - shoe.logical_size = size_b; - translate_logical_addr(); - if (shoe.abort) - return ; - const uint32_t p_addr_b = shoe.physical_addr; - - // printf("multiple page set: paddr_a=0x%08x (data_a=0x%llx) paddr_b=0x%08x (data_b=0x%llx) (orig_dat=0x%llx)\n", p_addr_a, data_a, p_addr_b, data_b, shoe.logical_dat); - - shoe.physical_addr = p_addr_a; - shoe.physical_size = size_a; - shoe.physical_dat = data_a; - physical_set(); - - shoe.physical_addr = p_addr_b; - shoe.physical_size = size_b; - shoe.physical_dat = data_b; - physical_set(); - - return ; - } - - // Common case: the write is contained entirely within a page - translate_logical_addr(); - if (shoe.abort) - return ; - - // printf("L(0x%08x) = P(0x%08x)\n", shoe.logical_addr, shoe.physical_addr); - - shoe.physical_size = shoe.logical_size; - shoe.physical_dat = shoe.logical_dat; - physical_set(); - /*if (!((tc_sre() && sr_s()))) - printf("set *0x%08x (phys 0x%08x) = %llx\n", shoe.logical_addr, shoe.physical_addr, shoe.physical_dat); - - if ((shoe.logical_addr >> 24) == 0x3f) - printf("set *0x%08x (phys 0x%08x) = %llx\n", shoe.logical_addr, shoe.physical_addr, shoe.physical_dat); - */ + const uint32_t slot = shoe.physical_addr >> 28; + if (shoe.slots[slot].connected) + shoe.slots[slot].write_func(shoe.physical_addr, + shoe.physical_size, + shoe.physical_dat, + slot); +} + +void _physical_set_standard_slot (void) +{ + const uint32_t slot = (shoe.physical_addr >> 24) & 0xf; + if (shoe.slots[slot].connected) + shoe.slots[slot].write_func(shoe.physical_addr, + shoe.physical_size, + shoe.physical_dat, + slot); } +const physical_set_ptr physical_set_jump_table[16] = { + _physical_set_ram, // 0x0 + _physical_set_ram, // 0x1 + _physical_set_ram, // 0x2 + _physical_set_ram, // 0x3 + _physical_set_rom, // 0x4 + _physical_set_io, // 0x5 + _physical_set_super_slot, // 0x6 + _physical_set_super_slot, // 0x7 + _physical_set_super_slot, // 0x8 + _physical_set_super_slot, // 0x9 + _physical_set_super_slot, // 0xa + _physical_set_super_slot, // 0xb + _physical_set_super_slot, // 0xc + _physical_set_super_slot, // 0xd + _physical_set_super_slot, // 0xe + _physical_set_standard_slot // 0xf +}; + +/* --- PMMU logical address translation --- */ +#pragma mark PMMU logical address translation + #define PMMU_CACHE_CRP 0 #define PMMU_CACHE_SRP 1 @@ -445,12 +285,12 @@ void logical_set (void) uint32_t unused1 : 1; uint32_t unused2 : 8; uint32_t physical_addr : 24; -} pmmu_cache_entry; - -struct { + } pmmu_cache_entry; + + struct { pmmu_cache_entry entry[512]; uint8_t valid_map[512 / 8]; -} pmmu_cache[2];*/ + } pmmu_cache[2];*/ #define write_back_desc() { \ if (desc_addr < 0) { \ @@ -460,43 +300,34 @@ struct { } \ } -static void translate_logical_addr() -{ - const uint8_t use_srp = (tc_sre() && (shoe.logical_fc >= 4)); -/* --- First check for an entry in pmmu_cache --- */ +static _Bool check_pmmu_cache(void) +{ + const _Bool use_srp = (tc_sre() && (shoe.logical_fc >= 4)); // logical addr [is]xxxxxxxxxxxx[ps] -> value xxxxxxxxxxxx const uint32_t value = (shoe.logical_addr << tc_is()) >> (tc_is() + tc_ps()); // value xxx[xxxxxxxxx] -> key xxxxxxxxx - const uint32_t key = value & 511; // low 9 bits + const uint32_t key = value & (PMMU_CACHE_SIZE-1); // low PMMU_CACHE_KEY_BITS bits - // Has this cache entry been set yet? - if ((shoe.pmmu_cache[use_srp].valid_map[key/8] >> (key & 7)) & 1) { - const pmmu_cache_entry_t entry = shoe.pmmu_cache[use_srp].entry[key]; - - // Do the values match? - if (entry.logical_value == value) { - - // Is this a write, but the page isn't marked "modified"? - if (!(shoe.logical_is_write && !entry.modified)) { - - // Is this a write, and the page is write-protected? - if (!(shoe.logical_is_write && entry.wp)) { - const uint32_t ps_mask = 0xffffffff >> entry.used_bits; - const uint32_t v_mask = ~~ps_mask; - shoe.physical_addr = ((entry.physical_addr<<8) & v_mask) + (shoe.logical_addr & ps_mask); - return ; - } - // This should be rare, just let the lookup handle it - } - // The page descriptor needs to be marked "modified" - } - // Values don't match, this is a different address - } + const pmmu_cache_entry_t entry = shoe.pmmu_cache[use_srp].entry[key]; + + const _Bool is_set = (shoe.pmmu_cache[use_srp].valid_map[key/8] >> (key & 7)) & 1; + const _Bool values_match = (entry.logical_value == value); + const _Bool first_modify = !(shoe.logical_is_write && !entry.modified); + const _Bool not_write_protected = !(shoe.logical_is_write && entry.wp); + + const uint32_t ps_mask = 0xffffffff >> entry.used_bits; + const uint32_t v_mask = ~~ps_mask; + + shoe.physical_addr = ((entry.physical_addr<<8) & v_mask) | (shoe.logical_addr & ps_mask); + return is_set && values_match && first_modify && not_write_protected; +} -/* --- pmmu_cache miss, manually traverse the page tables to do the translation */ - + +static void translate_logical_addr() +{ + const uint8_t use_srp = (tc_sre() && (shoe.logical_fc >= 4)); uint64_t *rootp_ptr = (use_srp ? (&shoe.srp) : (&shoe.crp)); const uint64_t rootp = *rootp_ptr; uint8_t desc_did_change = 0; @@ -507,9 +338,9 @@ static void translate_logical_addr() uint64_t desc = rootp; // Initial descriptor is the root pointer descriptor uint8_t desc_size = 1; // And the root pointer descriptor is always 8 bytes (1==8 bytes, 0==4 bytes) uint8_t used_bits = tc_is(); // Keep track of how many bits will be the effective "page size" - // (If the table search terminates early (before used_bits == ts_ps()), - // then this will be the effective page size. That is, the number of bits - // we or into the physical addr from the virtual addr) + // (If the table search terminates early (before used_bits == ts_ps()), + // then this will be the effective page size. That is, the number of bits + // we or into the physical addr from the virtual addr) desc_addr = -1; // address of the descriptor (-1 -> register) @@ -617,16 +448,16 @@ static void translate_logical_addr() const uint32_t ps_mask = 0xffffffff >> used_bits; const uint32_t v_mask = ~~ps_mask; - const uint32_t paddr = (desc_page_addr(desc) & v_mask) + (shoe.logical_addr & ps_mask); + const uint32_t paddr = (desc_page_addr(desc) & v_mask) | (shoe.logical_addr & ps_mask); shoe.physical_addr = paddr; - /*if (shoe.logical_addr == 0) { - printf("Success! desc = 0x%llx sz=%u bytes\n", desc, 4< value xxxxxxxxxxxx + const uint32_t value = (shoe.logical_addr << tc_is()) >> (tc_is() + tc_ps()); + // value xxx[xxxxxxxxx] -> key xxxxxxxxx + const uint32_t key = value & (PMMU_CACHE_SIZE-1); // low PMMU_CACHE_KEY_BITS bits pmmu_cache_entry_t entry; shoe.pmmu_cache[use_srp].valid_map[key/8] |= (1 << (key & 7)); @@ -637,17 +468,187 @@ static void translate_logical_addr() entry.used_bits = used_bits; shoe.pmmu_cache[use_srp].entry[key] = entry; - // if (debug_predicted) - // assert(debug_predicted == shoe.physical_addr); } +void logical_get (void) +{ + + // If address translation isn't enabled, this is a physical address + if (!tc_enable()) { + shoe.physical_addr = shoe.logical_addr; + shoe.physical_size = shoe.logical_size; + physical_get(); + if (shoe.abort) { + shoe.abort = 0; + throw_long_bus_error(shoe.logical_addr, 0); + return ; + } + shoe.logical_dat = shoe.physical_dat; + return ; + } + + const uint32_t logical_size = shoe.logical_size; + const uint32_t logical_addr = shoe.logical_addr; + + const uint16_t ps = tc_ps(); // log2 of the page size + const uint32_t pagesize = 1 << ps; // the page size + const uint32_t pagemask = pagesize-1; // a mask of the page bits + const uint32_t pageoffset = logical_addr & pagemask; + + shoe.logical_is_write = 0; + + // Common case: the read is contained entirely within a page + if (!((pageoffset + logical_size - 1) >> ps)) { + if (!check_pmmu_cache()) { + translate_logical_addr(); + if (shoe.abort) + return ; + } + + if (shoe.physical_addr < shoe.physical_mem_size) { + // Fast path + shoe.logical_dat = ntohll(*(uint64_t*)&shoe.physical_mem_base[shoe.physical_addr]) >> ((8-logical_size)*8); + } + else { + shoe.physical_size = logical_size; + physical_get(); + if (shoe.abort) { + shoe.abort = 0; + throw_long_bus_error(logical_addr, 0); + return ; + } + shoe.logical_dat = shoe.physical_dat; + } + } + else { // This read crosses a page boundary + const uint32_t addr_a = logical_addr; + const uint32_t size_b = (pageoffset + logical_size) & pagemask; + const uint32_t size_a = logical_size - size_b; + const uint32_t addr_b = addr_a + size_a; + + shoe.logical_addr = addr_a; + shoe.logical_size = size_a; + if (!check_pmmu_cache()) { + translate_logical_addr(); + if (shoe.abort) + return ; + } + + const uint32_t p_addr_a = shoe.physical_addr; + + shoe.logical_addr = addr_b; + shoe.logical_size = size_b; + if (!check_pmmu_cache()) { + translate_logical_addr(); + if (shoe.abort) + return ; + } + + const uint32_t p_addr_b = shoe.physical_addr; + shoe.physical_size = size_b; + physical_get(); + if (shoe.abort) { + shoe.abort = 0; + throw_long_bus_error(shoe.logical_addr, 0); + return ; + } + const uint64_t fetch_b = shoe.physical_dat; + + shoe.physical_addr = p_addr_a; + shoe.physical_size = size_a; + physical_get(); + if (shoe.abort) { + shoe.abort = 0; + throw_long_bus_error(shoe.logical_addr, 0); + return ; + } + + shoe.logical_dat = (shoe.physical_dat << (size_b*8)) | fetch_b; + } +} + +void logical_set (void) +{ + // If address translation isn't enabled, this is a physical address + if (!tc_enable()) { + shoe.physical_addr = shoe.logical_addr; + shoe.physical_size = shoe.logical_size; + shoe.physical_dat = shoe.logical_dat; + physical_set(); + return ; + } + + const uint32_t logical_size = shoe.logical_size; + const uint32_t logical_addr = shoe.logical_addr; + + const uint16_t ps = tc_ps(); // log2 of the page size + const uint32_t pagesize = 1 << ps; // the page size + const uint32_t pagemask = pagesize-1; // a mask of the page bits + const uint32_t pageoffset = logical_addr & pagemask; + + // Make the translate function fail if the page is write-protected + shoe.logical_is_write = 1; + + // Common case: this write is contained entirely in one page + if (!((pageoffset + logical_size - 1) >> ps)) { + // Common case: the write is contained entirely within a page + if (!check_pmmu_cache()) { + translate_logical_addr(); + if (shoe.abort) + return ; + } + + shoe.physical_size = shoe.logical_size; + shoe.physical_dat = shoe.logical_dat; + physical_set(); + } + else { // This write crosses a page boundary + const uint32_t addr_a = shoe.logical_addr; + const uint32_t size_b = (pageoffset + logical_size) & pagemask; + const uint32_t size_a = shoe.logical_size - size_b; + const uint32_t addr_b = addr_a + size_a; + const uint64_t data_a = shoe.logical_dat >> (size_b*8); + const uint64_t data_b = bitchop_64(shoe.logical_dat, size_b*8); + + shoe.logical_addr = addr_a; + shoe.logical_size = size_a; + if (!check_pmmu_cache()) { + translate_logical_addr(); + if (shoe.abort) + return ; + } + const uint32_t p_addr_a = shoe.physical_addr; + + shoe.logical_addr = addr_b; + shoe.logical_size = size_b; + if (!check_pmmu_cache()) { + translate_logical_addr(); + if (shoe.abort) + return ; + } + const uint32_t p_addr_b = shoe.physical_addr; + + shoe.physical_addr = p_addr_a; + shoe.physical_size = size_a; + shoe.physical_dat = data_a; + physical_set(); + + shoe.physical_addr = p_addr_b; + shoe.physical_size = size_b; + shoe.physical_dat = data_b; + physical_set(); + + return ; + } +} + -// -// EA routines begin here: -// +/* --- EA routines --- */ +#pragma mark EA routines #define nextword(pc) ({const uint16_t w=lget((pc),2);if (shoe.abort){return;}(pc)+=2; w;}) +#define nextlong(pc) ({const uint32_t L=lget((pc),4);if (shoe.abort){return;}(pc)+=4; L;}) // ea_decode_extended() - find the EA for those hiddeous 68020 addr modes static void ea_decode_extended() @@ -737,8 +738,7 @@ static void ea_decode_extended() if (z == 2) { // if word-length, fetch nextword() and sign-extend base_disp = (int16_t)(nextword(mypc)); } else { // otherwise, it's a longword - base_disp = nextword(mypc); - base_disp = (base_disp<<16) | nextword(mypc); + base_disp = nextlong(mypc); } } @@ -752,8 +752,7 @@ static void ea_decode_extended() break; case 0b0011: case 0b0111: case 0b1011: { // long word outer displacement - outer_disp = nextword(mypc); - outer_disp = (outer_disp<<16) | nextword(mypc); + outer_disp = nextlong(mypc); break ; } } @@ -830,17 +829,14 @@ void ea_read() return ; } case 5: { // address register indirect with displacement mode - const uint16_t u_disp = nextword(shoe.uncommitted_ea_read_pc); - const int16_t disp = (int16_t)u_disp; // sign-extend word to long + const int16_t disp = nextword(shoe.uncommitted_ea_read_pc); shoe.dat = lget(shoe.a[reg]+disp, shoe.sz); return ; } case 6: { - // printf("ea_read(): %u %u not implemented\n", mode, reg); ea_decode_extended(); if (shoe.abort) return ; shoe.dat = lget(shoe.extended_addr, shoe.sz); - //eaprintf("read(0x%x) = 0x%x\n", shoe.extended_addr, shoe.dat); return ; } case 7: { @@ -851,12 +847,8 @@ void ea_read() return ; } case 1: { // absolute long addressing mode - //printf("ea_read(): %u %u not implemented\n", mode, reg); - uint32_t addr = nextword(shoe.uncommitted_ea_read_pc); - addr = (addr << 16) | nextword(shoe.uncommitted_ea_read_pc); - // printf("addr = 0x%x\n", addr); + const uint32_t addr = nextlong(shoe.uncommitted_ea_read_pc); shoe.dat = lget(addr, shoe.sz); - // printf("ea_write(): %u %u not implemented\n", mode, reg); return ; } case 2: { // program counter indirect with displacement mode @@ -865,7 +857,6 @@ void ea_read() const int16_t disp = (int16_t)u_disp; shoe.dat = lget(base_pc + disp, shoe.sz); - return ; } case 3: { // (program counter ...) @@ -875,21 +866,15 @@ void ea_read() return ; } case 4: { // immediate data - const uint16_t ext = nextword(shoe.uncommitted_ea_read_pc); if (shoe.sz==1) { - shoe.dat = ext & 0xff; + shoe.dat = nextword(shoe.uncommitted_ea_read_pc) & 0xff; } else if (shoe.sz == 2) { - shoe.dat = ext; + shoe.dat = nextword(shoe.uncommitted_ea_read_pc); } else if (shoe.sz == 4) { - const uint16_t ext2 = nextword(shoe.uncommitted_ea_read_pc); - shoe.dat = ((uint32_t)ext)<<16; - shoe.dat |= ext2; + shoe.dat = nextlong(shoe.uncommitted_ea_read_pc); } else if (shoe.sz == 8) { - uint64_t q; - q = (ext << 16) | nextword(shoe.uncommitted_ea_read_pc); - q = (ext << 16) | nextword(shoe.uncommitted_ea_read_pc); - q = (ext << 16) | nextword(shoe.uncommitted_ea_read_pc); - shoe.dat = q; + const uint64_t q = nextlong(shoe.uncommitted_ea_read_pc); + shoe.dat = (q<<32) | nextlong(shoe.uncommitted_ea_read_pc); } return ; } @@ -913,7 +898,6 @@ void ea_read_commit() case 3: { // address register indirect with postincrement mode const uint8_t delta = ((reg==7) && (shoe.sz==1))?2:shoe.sz; shoe.a[reg] += delta; - //printf("ea_read_commit(): %u %u not implemented\n", mode, reg); return ; } case 4: { // address register indirect with predecrement mode @@ -945,8 +929,7 @@ void ea_read_commit() return ; } case 3: { // (program counter ...) - ea_decode_extended(); - if (shoe.abort) return ; + // shoe.extended_len was set in the previous ea_read() call. shoe.pc += shoe.extended_len; return ; } @@ -1019,8 +1002,7 @@ void ea_write() return ; } case 1: { // absolute long addressing mode - uint32_t addr = nextword(shoe.pc); - addr = (addr << 16) | nextword(shoe.pc); + const uint32_t addr = nextlong(shoe.pc); lset(addr, shoe.sz, shoe.dat); // printf("ea_write(): %u %u not implemented\n", mode, reg); return ; @@ -1062,8 +1044,7 @@ void ea_addr() return ; } case 1: { // absolute long addressing mode - uint32_t addr = (nextword(shoe.pc)) << 16; - addr |= (nextword(shoe.pc)); + const uint32_t addr = nextlong(shoe.pc); shoe.dat = addr; return ; } diff --git a/core/scsi.c b/core/scsi.c index 1b40b03..f1f8a3a 100644 --- a/core/scsi.c +++ b/core/scsi.c @@ -150,7 +150,7 @@ typedef struct { uint8_t target_id; // target ID (as an int [0, 7]) // transfer buffers - uint8_t buf[512 * 64]; + uint8_t buf[512 * 256]; uint32_t bufi; uint32_t in_len, in_i; uint32_t out_len, out_i; @@ -412,13 +412,19 @@ static void scsi_buf_set (uint8_t byte) printf("scsi_buf_set: Responding to read at off=%u len=%u\n", offset, len); - assert(len <= 64); + //assert(len <= 64); assert(dev->num_blocks > offset); + if (len == 0) { + switch_status_phase(0); + break; + } + assert(0 == fseeko(dev->f, 512 * offset, SEEK_SET)); assert(fread(scsi.buf, len * 512, 1, dev->f) == 1); + scsi.in_len = len * 512; scsi.in_i = 0; @@ -435,7 +441,7 @@ static void scsi_buf_set (uint8_t byte) printf("scsi_buf_set: Responding to write at off=%u len=%u\n", offset, len); - assert(len <= 64); + //assert(len <= 64); scsi.write_offset = offset; scsi.out_len = len * 512; @@ -466,7 +472,7 @@ void scsi_reg_read () { const uint32_t reg = ((shoe.physical_addr & 0xffff) >> 4) & 0xf; - printf("\nscsi_reg_read: reading from register %s(%u) ", scsi_read_reg_str[reg], reg); + //printf("\nscsi_reg_read: reading from register %s(%u) ", scsi_read_reg_str[reg], reg); switch (reg) { case 0: // Current scsi data bus register @@ -555,7 +561,7 @@ void scsi_reg_read () break; } - printf("(set to 0x%02x)\n\n", (uint32_t)shoe.physical_dat); + //printf("(set to 0x%02x)\n\n", (uint32_t)shoe.physical_dat); } void scsi_reg_write () diff --git a/core/shoebill.h b/core/shoebill.h index 6bede22..9243c49 100644 --- a/core/shoebill.h +++ b/core/shoebill.h @@ -33,6 +33,9 @@ #include //#include +// void ring_print(const char *str); +// extern char *ring_tmp; + #include "coff.h" // -- Global constants -- @@ -59,7 +62,7 @@ #define get_d(n,s) get_reg__(shoe.d[n], s) #define get_a(n,s) get_reg__(shoe.a[n], s) #define set_d(n,val,s) set_reg__(shoe.d[n], val, s) - // #define set_a(n,val,s) set_reg__(shoe.a[n], val, s) // address registers should always be set with sz==4 + // sr masks #define sr_c() (shoe.sr&1) @@ -100,12 +103,13 @@ shoe.sr = newsr & 0xf71f; \ load_stack_pointer(); \ } - + #define set_sr_c(b) {shoe.sr &= (~(1<<0)); shoe.sr |= (((b)!=0)<<0);} #define set_sr_v(b) {shoe.sr &= (~(1<<1)); shoe.sr |= (((b)!=0)<<1);} #define set_sr_z(b) {shoe.sr &= (~(1<<2)); shoe.sr |= (((b)!=0)<<2);} #define set_sr_n(b) {shoe.sr &= (~(1<<3)); shoe.sr |= (((b)!=0)<<3);} #define set_sr_x(b) {shoe.sr &= (~(1<<4)); shoe.sr |= (((b)!=0)<<4);} + #define set_sr_mask(m) {shoe.sr &= (~(7<<8)); shoe.sr |= ((((uint16_t)(m))&7) << 8);} // Be careful when setting these bits #define set_sr_m(b) {make_stack_pointers_valid(); shoe.sr &= (~(1<<12)); shoe.sr |= (((b)!=0)<<12); load_stack_pointer();} #define set_sr_s(b) {make_stack_pointers_valid(); shoe.sr &= (~(1<<13)); shoe.sr |= (((b)!=0)<<13); load_stack_pointer();} @@ -132,7 +136,21 @@ // misc #define ea_n(s) ((shoe.dat>>((s)*8-1))&1) - #define ea_z(s) ((shoe.dat&((0xffffffff)>>(8*(4-(s)))))==0) + #define ea_z(s) (chop(shoe.dat, (s))==0) + +// alloc_pool.c +typedef struct _alloc_pool_t { + struct _alloc_pool_t *prev, *next; + uint32_t size, magic; +} alloc_pool_t; + +void* p_alloc(alloc_pool_t *pool, uint64_t size); +void* p_realloc(void *ptr, uint64_t size); +void p_free(void *ptr); +void p_free_pool(alloc_pool_t *pool); +alloc_pool_t* p_new_pool(void); + + typedef struct dbg_breakpoint_t { struct dbg_breakpoint_t *next; @@ -231,7 +249,6 @@ typedef struct { } iwm_state_t; - typedef struct { uint32_t (*read_func)(uint32_t, uint32_t, uint8_t); void (*write_func)(uint32_t, uint32_t, uint32_t, uint8_t); @@ -270,9 +287,11 @@ typedef struct { pthread_mutex_t cpu_freeze_lock; // -- PMMU caching structures --- +#define PMMU_CACHE_KEY_BITS 10 +#define PMMU_CACHE_SIZE (1<> 28]() +#define pset(addr, s, val) {shoe.physical_addr=(addr); shoe.physical_size=(s); shoe.physical_dat=(val); physical_set();} + +#define physical_get() physical_get_jump_table[shoe.physical_addr >> 28]() #define pget(addr, s) ({shoe.physical_addr=(addr); shoe.physical_size=(s); physical_get(); shoe.physical_dat;}) void logical_get (void); @@ -479,9 +508,6 @@ void logical_get (void); shoe.logical_dat; \ }) -void physical_set (void); -#define pset(addr, s, val) {shoe.physical_addr=(addr); shoe.physical_size=(s); shoe.physical_dat=(val); physical_set();} - void logical_set (void); #define lset_fc(addr, s, val, fc) {\ shoe.logical_addr=(addr); \ diff --git a/core/via.c b/core/via.c index 44ceed3..e8cd979 100644 --- a/core/via.c +++ b/core/via.c @@ -99,10 +99,10 @@ void process_pending_interrupt () // If the CPU was stopped, unstop it shoe.cpu_thread_notifications &= ~~SHOEBILL_STATE_STOPPED; - printf("Interrupt pri %u! mask=%u\n", priority, sr_mask()); - const uint16_t vector_offset = (priority + 24) * 4; + printf("Interrupt pri %u! mask=%u vector_offset=0x%08x\n", priority, sr_mask(), vector_offset); + // Save the old SR, and switch to supervisor mode const uint16_t old_sr = shoe.sr; set_sr_s(1); @@ -119,7 +119,6 @@ void process_pending_interrupt () push_a7(old_sr, 2); printf("interrupt: pushed sr 0x%04x to 0x%08x\n", old_sr, shoe.a[7]); assert(!shoe.abort); - if (sr_m()) { // clear sr_m, and write a format 1 exception to the ISP @@ -137,8 +136,18 @@ void process_pending_interrupt () assert(!shoe.abort); } + /* + * "When processing an interrupt exception, the MC68020/EC020 first makes an internal copy of the SR, + * sets the privilege level to supervisor, suppresses tracing, and sets the processor interrupt mask + * level to the level of the interrupt being serviced." + */ + set_sr_mask(priority); + set_sr_t0(0); + set_sr_t1(0); + // Fetch the autovector handler address const uint32_t newpc = lget(shoe.vbr + vector_offset, 4); + printf("autovector handler = *0x%08x = 0x%08x\n", shoe.vbr + vector_offset, newpc); assert(!shoe.abort); shoe.pc = newpc; diff --git a/web/0.0.2_prefs_general.png b/web/0.0.2_prefs_general.png new file mode 100644 index 0000000000000000000000000000000000000000..461867874a33888c9aa977e9400ea41155c4c8b4 GIT binary patch literal 38499 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