447 lines
14 KiB
C
447 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() {
|
|
printf("%s: Error, not implemented!\n", __func__);
|
|
assert(!"blowup");
|
|
}
|
|
|
|
void inst_mc68851_psave(){
|
|
printf("%s: Error, not implemented!\n", __func__);
|
|
assert(!"blowup");
|
|
}
|
|
|
|
void inst_mc68851_pbcc(){
|
|
printf("%s: Error, not implemented!\n", __func__);
|
|
assert(!"blowup");
|
|
}
|
|
|
|
|
|
void inst_mc68851_pdbcc(uint16_t cond){
|
|
printf("%s: Error, not implemented!\n", __func__);
|
|
assert(!"blowup");
|
|
}
|
|
|
|
void inst_mc68851_ptrapcc(uint16_t cond){
|
|
printf("%s: Error, not implemented!\n", __func__);
|
|
assert(!"blowup");
|
|
}
|
|
|
|
void inst_mc68851_pscc(uint16_t cond){
|
|
printf("%s: Error, not implemented!\n", __func__);
|
|
assert(!"blowup");
|
|
}
|
|
|
|
|
|
void inst_mc68851_pload(uint16_t ext){
|
|
printf("%s: Error, not implemented!\n", __func__);
|
|
assert(!"blowup");
|
|
}
|
|
|
|
void inst_mc68851_pvalid(uint16_t ext){
|
|
printf("%s: Error, not implemented!\n", __func__);
|
|
assert(!"blowup");
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
// printf("%s: Error, not implemented!\n", __func__);
|
|
}
|
|
|
|
void inst_mc68851_pmove(uint16_t ext){
|
|
|
|
verify_supervisor();
|
|
|
|
~decompose(shoe.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};
|
|
|
|
// 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;
|
|
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 = 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)
|
|
printf("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)
|
|
printf("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(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 {
|
|
printf("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;
|
|
}
|
|
|
|
|
|
|
|
// printf("%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");
|
|
}
|
|
|
|
|
|
|
|
|