shoebill/core/mc68851.c
Peter Rutenbar 476a8bb570 Major speed improvements (25-50%)
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
2015-01-29 00:19:57 -05:00

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");
}