mirror of
https://github.com/pruten/shoebill.git
synced 2024-06-09 12:29:31 +00:00
476a8bb570
There may be bugs lurking in it. On my Core i7 macbook pro, shoebill now runs so fast that SetUpTimeK() on A/UX 3 hangs. (SetUpTimeK tries to time a dbra loop, and refuses to accept any speed faster than a certain threshold, which shoebill is now surpassing. If you see A/UX hanging early in boot, it's probably that.) - Added a new specialized cache for instruction stream reads -- This also lets us distinguish between data and instruction reads, which the 68020 does. Instruction reads are now done with the correct function code (2 or 6), although that doesn't currently fix or improve anything currently - Added an obvious condition code optimization, dunno how I missed it earlier - Other little changes
468 lines
14 KiB
C
468 lines
14 KiB
C
/*
|
|
* Copyright (c) 2013, Peter Rutenbar <pruten@gmail.com>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
* list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include "../core/shoebill.h"
|
|
#include "../core/mc68851.h"
|
|
|
|
#define verify_supervisor() {if (!sr_s()) {throw_privilege_violation(); return;}}
|
|
|
|
extern struct dis_t dis;
|
|
extern uint16_t dis_op;
|
|
|
|
void inst_mc68851_prestore() {
|
|
slog("%s: Error, not implemented!\n", __func__);
|
|
assert(!"blowup");
|
|
}
|
|
|
|
void inst_mc68851_psave(){
|
|
slog("%s: Error, not implemented!\n", __func__);
|
|
assert(!"blowup");
|
|
}
|
|
|
|
void inst_mc68851_pbcc(){
|
|
slog("%s: Error, not implemented!\n", __func__);
|
|
assert(!"blowup");
|
|
}
|
|
|
|
|
|
void inst_mc68851_pdbcc(uint16_t cond){
|
|
slog("%s: Error, not implemented!\n", __func__);
|
|
assert(!"blowup");
|
|
}
|
|
|
|
void inst_mc68851_ptrapcc(uint16_t cond){
|
|
slog("%s: Error, not implemented!\n", __func__);
|
|
assert(!"blowup");
|
|
}
|
|
|
|
void inst_mc68851_pscc(uint16_t cond){
|
|
slog("%s: Error, not implemented!\n", __func__);
|
|
assert(!"blowup");
|
|
}
|
|
|
|
|
|
void inst_mc68851_pload(uint16_t ext){
|
|
slog("%s: Error, not implemented!\n", __func__);
|
|
assert(!"blowup");
|
|
}
|
|
|
|
void inst_mc68851_pvalid(uint16_t ext){
|
|
slog("%s: Error, not implemented!\n", __func__);
|
|
assert(!"blowup");
|
|
}
|
|
|
|
void inst_mc68851_pflushr(uint16_t ext){
|
|
verify_supervisor();
|
|
slog("pflushr!");
|
|
// Just nuke the entire cache
|
|
memset(shoe.pmmu_cache[0].valid_map, 0, PMMU_CACHE_SIZE/8);
|
|
memset(shoe.pmmu_cache[1].valid_map, 0, PMMU_CACHE_SIZE/8);
|
|
|
|
/* Invalidate the pc cache */
|
|
invalidate_pccache();
|
|
}
|
|
|
|
void inst_mc68851_pflush(uint16_t ext){
|
|
verify_supervisor();
|
|
slog("pflush!");
|
|
memset(shoe.pmmu_cache[0].valid_map, 0, PMMU_CACHE_SIZE/8);
|
|
memset(shoe.pmmu_cache[1].valid_map, 0, PMMU_CACHE_SIZE/8);
|
|
// slog("%s: Error, not implemented!\n", __func__);
|
|
|
|
/* Invalidate the pc cache */
|
|
invalidate_pccache();
|
|
}
|
|
|
|
void inst_mc68851_pmove(uint16_t ext){
|
|
|
|
verify_supervisor();
|
|
|
|
~decompose(shoe.op, 1111 000 000 MMMMMM);
|
|
~decompose(ext, fff ppp w 0000 nnn 00);
|
|
|
|
/*
|
|
* For simplicity, just blow away pccache whenever
|
|
* the PMMU state changes at all
|
|
*/
|
|
if (!w)
|
|
invalidate_pccache();
|
|
|
|
|
|
// instruction format #1
|
|
if (f == ~b(010)) {
|
|
const uint8_t sizes[8] = {4, 8, 8, 8, 1, 1, 1, 2};
|
|
|
|
// if accessing d or a reg, and sz==8 bytes, that's bogus
|
|
if (((M>>3) < 2) && (sizes[p]==8)) {
|
|
throw_illegal_instruction();
|
|
return ;
|
|
}
|
|
|
|
if (!w) { // if we're reading from EA
|
|
call_ea_read(M, sizes[p]);
|
|
call_ea_read_commit(M, sizes[p]);
|
|
}
|
|
|
|
switch (p) {
|
|
case 0: // tc
|
|
if (!w) {
|
|
shoe.tc = shoe.dat & 0x83FFFFFF;
|
|
shoe.tc_is = (shoe.tc >> 16) & 0xf;
|
|
shoe.tc_ps = (shoe.tc >> 20) & 0xf;
|
|
shoe.tc_pagesize = 1 << shoe.tc_ps;
|
|
shoe.tc_pagemask = shoe.tc_pagesize - 1;
|
|
shoe.tc_is_plus_ps = shoe.tc_is + shoe.tc_ps;
|
|
shoe.tc_enable = (shoe.tc >> 31) & 1;
|
|
shoe.tc_sre = (shoe.tc >> 25) & 1;
|
|
}
|
|
else {
|
|
shoe.dat = shoe.tc;
|
|
//if (!tc_fcl()) assert(!"pmove->tc: function codes not supported\n");
|
|
|
|
}
|
|
break;
|
|
case 1: // drp
|
|
if (!w) shoe.drp = shoe.dat & 0xffff0203ffffffff;
|
|
else shoe.dat = shoe.drp;
|
|
break;
|
|
case 2: // srp
|
|
if (!w) shoe.srp = shoe.dat & 0xffff0203ffffffff;
|
|
else shoe.dat = shoe.srp;
|
|
break;
|
|
case 3: // crp
|
|
if (!w) shoe.crp = shoe.dat & 0xffff0203ffffffff;
|
|
else shoe.dat = shoe.crp;
|
|
break;
|
|
case 4: // cal
|
|
if (!w) shoe.cal = shoe.dat & ~b(11100000);
|
|
else shoe.dat = shoe.cal;
|
|
break;
|
|
case 5: // val
|
|
if (!w) shoe.val = shoe.dat & ~b(11100000);
|
|
else shoe.dat = shoe.val;
|
|
break;
|
|
case 6: // scc
|
|
if (!w) shoe.scc = shoe.dat;
|
|
else shoe.dat = shoe.scc;
|
|
break;
|
|
case 7: // ac
|
|
if (!w) shoe.ac = shoe.dat & ~b(0000000010110011);
|
|
else shoe.dat = shoe.ac;
|
|
break;
|
|
}
|
|
|
|
if (w)
|
|
call_ea_write(M, sizes[p]);
|
|
return ;
|
|
}
|
|
else if (f == ~b(011)) {
|
|
if (p == 0) { // psr
|
|
if (!w) shoe.psr.word = shoe.dat & ~b(11111111 10000111);
|
|
else shoe.dat = shoe.psr.word;
|
|
}
|
|
else if (p==1) { // pcsr
|
|
if (!w) shoe.pcsr = shoe.dat;
|
|
else shoe.dat = shoe.pcsr;
|
|
}
|
|
else {
|
|
assert(!"Unknown register number for pmove format 3!\n");
|
|
throw_illegal_instruction();
|
|
}
|
|
|
|
if (w)
|
|
call_ea_write(M, 2);
|
|
return ;
|
|
}
|
|
|
|
// TODO: implement instruction formats 2 and 3 (for BAD/BAC, etc.)
|
|
assert(!"Unsupported pmove instruction format");
|
|
throw_illegal_instruction();
|
|
}
|
|
|
|
static int64_t ptest_search(const uint32_t _logical_addr, const uint64_t rootp)
|
|
{
|
|
// const uint64_t rootp = (tc_sre() && sr_s()) ? shoe.srp : shoe.crp;
|
|
uint8_t desc_level = 0;
|
|
int64_t desc_addr = -1; // address of the descriptor (-1 -> register)
|
|
uint8_t wp = 0; // Whether any descriptor in the search has wp (write protected) set
|
|
uint8_t i;
|
|
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 = shoe.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)
|
|
|
|
shoe.psr.word = 0;
|
|
|
|
desc_addr = -1; // address of the descriptor (-1 -> register)
|
|
|
|
/* We'll keep shifting logical_addr such that its most significant bits are the next ti-index */
|
|
uint32_t logical_addr = _logical_addr << used_bits;
|
|
|
|
// TODO: Check limit here
|
|
|
|
// If root descriptor is invalid, throw a bus error
|
|
if (rp_dt(rootp) == 0) {
|
|
shoe.psr.bits.b = 1; // bus error
|
|
shoe.psr.bits.n = 0;
|
|
return desc_addr;
|
|
}
|
|
|
|
// desc is a page descriptor, skip right ahead to search_done:
|
|
if (rp_dt(rootp) == 1)
|
|
goto search_done;
|
|
|
|
for (i=0; i < 4; i++) { // (the condition is unnecessary - just leaving it in for clarity)
|
|
// desc must be a table descriptor here
|
|
|
|
const uint8_t ti = tc_ti(i);
|
|
used_bits += ti;
|
|
|
|
// TODO: do the limit check here
|
|
|
|
// Find the index into our current i-level table
|
|
const uint32_t index = logical_addr >> (32-ti);
|
|
logical_addr <<= ti;
|
|
|
|
// load the child descriptor
|
|
if (_logical_addr == 0)
|
|
slog("Loading descriptor s=%llu from addr=0x%08x\n", desc_dt(desc, desc_size) & 1, (uint32_t)desc_table_addr(desc));
|
|
const uint32_t table_base_addr = desc_table_addr(desc);
|
|
const uint8_t s = desc_dt(desc, desc_size) & 1;
|
|
|
|
// desc = pget(table_base_addr + (4 << s)*index, (4 << s));
|
|
get_desc(table_base_addr + (4 << s)*index, (4 << s));
|
|
desc_size = s;
|
|
|
|
// Desc may be a table descriptor, page descriptor, or indirect descriptor
|
|
|
|
const uint8_t dt = desc_dt(desc, desc_size);
|
|
if (_logical_addr == 0)
|
|
slog("i=%u desc = 0x%llx dt=%u\n", i, desc, dt);
|
|
|
|
// If this descriptor is invalid, throw a bus error
|
|
if (dt == 0) {
|
|
shoe.psr.bits.i = 1; // invalid
|
|
assert(desc_level <= 7);
|
|
shoe.psr.bits.n = desc_level;
|
|
return desc_addr;
|
|
}
|
|
|
|
if (dt == 1) {
|
|
// TODO: do a limit check here
|
|
goto search_done; // it's a page descriptor
|
|
}
|
|
else if ((i==3) || (tc_ti(i+1)==0)) {
|
|
desc_size = desc_dt(desc, s) & 1;
|
|
|
|
/* Otherwise, if this is a leaf in the tree, it's an indirect descriptor.
|
|
The size of the page descriptor is indicated by DT in the indirect descriptor. (or is it???) */
|
|
//desc = pget(desc & 0xfffffff0, (4 << desc_size));
|
|
get_desc(desc & 0xfffffff0, (4 << desc_size));
|
|
|
|
// I think it's possible for an indirect descriptor to point to an invalid descriptor...
|
|
if (desc_dt(desc, desc_size) == 0) {
|
|
shoe.psr.bits.i = 1; // invalid
|
|
assert(desc_level <= 7);
|
|
shoe.psr.bits.n = desc_level;
|
|
return desc_addr;
|
|
}
|
|
|
|
goto search_done;
|
|
}
|
|
|
|
// Now it must be a table descriptor
|
|
|
|
// TODO: set the U (used) bit in this table descriptor
|
|
|
|
wp |= desc_wp(desc, desc_size); // or in the wp flag for this table descriptor
|
|
|
|
}
|
|
|
|
// never get here
|
|
assert(!"translate_logical_addr: never get here");
|
|
|
|
|
|
search_done:
|
|
// Desc must be a page descriptor
|
|
|
|
// NOTE: The limit checks have been done already
|
|
|
|
// TODO: check U (used) bit
|
|
|
|
wp |= desc_wp(desc, desc_size); // or in the wp flag for this page descriptor
|
|
|
|
shoe.psr.bits.w = wp;
|
|
|
|
if (desc & (desc_size ? desc_m_long : desc_m_short))
|
|
shoe.psr.bits.m = 1;
|
|
|
|
assert(desc_level <= 7);
|
|
shoe.psr.bits.n = desc_level;
|
|
|
|
return desc_addr;
|
|
}
|
|
|
|
void inst_mc68851_ptest(uint16_t ext){
|
|
verify_supervisor();
|
|
|
|
~decompose(shoe.op, 1111 0000 00 MMMMMM);
|
|
~decompose(ext, 100 LLL R AAAA FFFFF); // Erata in 68kPRM - F is 6 bits, and A is 3
|
|
|
|
assert(shoe.tc_enable); // XXX: Throws some exception if tc_enable isn't set
|
|
assert(tc_fcl() == 0); // XXX: I can't handle function code lookups, and I don't want to
|
|
assert(L == 7); // XXX: Not currently handling searching to a particular level
|
|
|
|
// Find the function code
|
|
uint8_t fc;
|
|
if (F>>4) // Function code is the low 4 bits of F
|
|
fc = F & 0xf;
|
|
else if ((F >> 3) == 1) // Function code is in shoe.d[F & 7]
|
|
fc = shoe.d[F & 7] & 0xf;
|
|
else if (F == 1) // Function code is SFC
|
|
fc = shoe.sfc;
|
|
else if (F == 0) // Function code is DFC
|
|
fc = shoe.dfc;
|
|
else
|
|
assert(!"ptest: unknown FC bits in instruction");
|
|
|
|
// Pick a root pointer based on the function code
|
|
uint64_t rootp;
|
|
if (fc == 1)
|
|
rootp = shoe.crp;
|
|
else if (fc == 5)
|
|
rootp = shoe.srp;
|
|
else {
|
|
slog("ptest: I can't handle this FC: %u pc=0x%08x\n", fc, shoe.orig_pc);
|
|
assert(!"ptest: I can't handle this FC");
|
|
}
|
|
|
|
call_ea_addr(M);
|
|
|
|
const int64_t desc_addr = ptest_search(shoe.dat, rootp);
|
|
|
|
if ((desc_addr >= 0) && (A >> 3)) {
|
|
shoe.a[A & 7] = (uint32_t)desc_addr;
|
|
}
|
|
|
|
|
|
|
|
// slog("%s: Error, not implemented!\n", __func__);
|
|
}
|
|
|
|
void dis_mc68851_prestore() {
|
|
sprintf(dis.str, "prestore");
|
|
}
|
|
|
|
void dis_mc68851_psave() {
|
|
sprintf(dis.str, "psave");
|
|
}
|
|
|
|
void dis_mc68851_pbcc() {
|
|
sprintf(dis.str, "pbcc");
|
|
}
|
|
|
|
|
|
void dis_mc68851_pdbcc(uint16_t cond) {
|
|
sprintf(dis.str, "pdbcc");
|
|
}
|
|
|
|
void dis_mc68851_ptrapcc(uint16_t cond) {
|
|
sprintf(dis.str, "ptrapcc");
|
|
}
|
|
|
|
void dis_mc68851_pscc(uint16_t cond) {
|
|
sprintf(dis.str, "pscc");
|
|
}
|
|
|
|
|
|
void dis_mc68851_pload(uint16_t ext) {
|
|
sprintf(dis.str, "pload");
|
|
}
|
|
|
|
void dis_mc68851_pvalid(uint16_t ext) {
|
|
sprintf(dis.str, "pvalid");
|
|
}
|
|
|
|
void dis_mc68851_pflush(uint16_t ext) {
|
|
sprintf(dis.str, "pflush");
|
|
}
|
|
|
|
void dis_mc68851_pmove(uint16_t ext) {
|
|
~decompose(dis_op, 1111 000 000 MMMMMM);
|
|
~decompose(ext, fff ppp w 0000 nnn 00);
|
|
|
|
// instruction format #1
|
|
if (f == ~b(010)) {
|
|
const uint8_t sizes[8] = {4, 8, 8, 8, 1, 1, 1, 2};
|
|
char *names[8] = {
|
|
"tc", "drp", "srp", "crp", "cal", "val", "scc", "ac"
|
|
};
|
|
|
|
if (w)
|
|
sprintf(dis.str, "pmove %s,%s", names[p], decode_ea_rw(M, sizes[p]));
|
|
else
|
|
sprintf(dis.str, "pmove %s,%s", decode_ea_rw(M, sizes[p]), names[p]);
|
|
|
|
return ;
|
|
}
|
|
else if (f == ~b(011)) {
|
|
char *names[8] = {"psr" , "pcsr", "?", "?", "?", "?", "?", "?"};
|
|
if (w)
|
|
sprintf(dis.str, "pmove %s,%s", names[p], decode_ea_rw(M, 2));
|
|
else
|
|
sprintf(dis.str, "pmove %s,%s", decode_ea_rw(M, 2), names[p]);
|
|
|
|
return ;
|
|
}
|
|
|
|
// TODO: implement instruction formats 2 and 3 (for BAD/BAC, etc.)
|
|
sprintf(dis.str, "pmove ???");
|
|
|
|
}
|
|
|
|
void dis_mc68851_ptest(uint16_t ext) {
|
|
~decompose(dis_op, 1111 0000 00 MMMMMM);
|
|
~decompose(ext, 100 LLL R AAAA FFFFF); // Erata in 68kPRM - F is 6 bits, and A is 3
|
|
|
|
if (A >> 3)
|
|
sprintf(dis.str, "ptest%c 0x%x,%s,%u,a%u", "wr"[R], F, decode_ea_addr(M), L, A & 7);
|
|
else
|
|
sprintf(dis.str, "ptest%c 0x%x,%s,%u", "wr"[R], F, decode_ea_addr(M), L);
|
|
}
|
|
|
|
void dis_mc68851_pflushr(uint16_t ext) {
|
|
sprintf(dis.str, "pflushr");
|
|
}
|
|
|
|
|
|
|
|
|