|
|
nono 1.7.0
//
// nono
// Copyright (C) 2022 nono project
// Licensed under nono-license.txt
//
//
// HD647180 コア
//
#include "mpu64180.h"
#include "debugger.h"
#include "event.h"
#include "hd64180.h"
#include "hd647180.h"
#include "scheduler.h"
#define OP_DEF(name) void __CONCAT(MPU64180Device::op_,name)()
#define OP_FUNC(name) __CONCAT(op_,name)()
#include "hd64180ops.cpp"
void
MPU64180Device::ExecNormal(Event *ev)
{
uint64 cycle_start = used_cycle;
ppc = reg.pc;
ixiy = USE_HL;
ixd = -1;
ops.clear();
intcheck = true;
// R レジスタは命令ごとにカウント。7bit マスクは読み出し時に行う。
// 命令の前か後かは分からないが。
reg_r++;
// 1バイト目処理
op = Fetch1();
if (__predict_false(opmode == OpMode::Halt)) {
// ホールトモードなら PC を戻し続けるだけ
reg.pc--;
CYCLE(3);
} else {
switch (op) {
#include "hd64180switch_00.inc"
default:
__unreachable();
}
}
// 割り込みチェック。
if (__predict_true(intcheck)) {
// intmap が非ゼロなら割り込み発生中。
if (GetIEF1() == true && __predict_false(intmap != 0)) {
DoInterrupt();
}
} else {
// この命令境界での割り込みチェックを飛ばす。次からは行う。
intcheck = true;
}
int cycle = used_cycle - cycle_start;
ev->time = cycle * clock_tsec;
scheduler->StartEvent(ev);
}
// 不当命令。
// pos は不当命令になったバイト位置。2バイト目なら2。
void
MPU64180Device::op_illegal(int pos)
{
// ログ表示
std::string str;
for (auto o : ops) {
str += string_format(" %02x", o);
}
putlog(1, "Undefined opcode%s", str.c_str());
itc_trap = true;
// 3バイト目なら UFO ビットをセット。
itc_ufo = (pos == 3);
// XXX CYCLE
// TRAP 割り込みはマスク不可能。
// IEF[12] not affected
// (1) ITC の TRAP ビットを 1 にする
// (2) 未定義 opcode 位置の PC をスタックに保存する
// (3) ベクタを 0000H にする
// PC は未定義命令の先頭番地ではなく、未定義 opcode だった位置?
// nono の実装ではフェッチと同時にインクリメントしているので、
// 1つ前でいい? HD64180.pdf p38-39
if (pos == 2) {
Push2(reg.pc - 1);
} else {
// DD CB d OP xx
// ^ PC
// d の位置をスタックに積む
Push2(reg.pc - 2);
}
ExceptionDirect(HD647180::IntmapTRAP, 0x0000);
}
// 分岐
void
MPU64180Device::Jump(uint16 addr)
{
reg.pc = addr;
// 命令本体(オペランドを含まない)を16ビット左詰めにする。
// 分岐命令は1バイト命令か ED XX か DD/FD XX の2バイト命令しかないはず。
// 上位ワードは $0000。
uint32 info;
if (ops[0] == 0xed || ops[0] == 0xdd || ops[0] == 0xfd) {
info = (ops[0] << 8) | ops[1];
} else {
info = (ops[0] << 8);
}
brhist.AddEntry(GetPPC(), addr, info);
}
//
// 割り込み。
//
// TRAP 0000H
// NMI 0066H
// INT0 mode0 命令をデータバスからフェッチ
// INT0 mode1 0038H
// INT0 mode2 Vectored (上位8bit が I レジスタ、下位8bit はデータバス)
// INT1 Vectored (I + IL + 固定コード)
// INT2 Vectored (〃)
// 内蔵割り込み Vectored (〃)
// 内部の割り込みマップ (Intmap) をアサートする。
void
MPU64180Device::AssertIntmap(int source)
{
uint32 oldmap;
uint32 srcmap;
oldmap = intmap;
srcmap = 1U << (31 - source);
intmap |= srcmap;
if ((oldmap ^ intmap) != 0) {
// 立ち上がりエッジでカウント
int_counter[source]++;
// ホールトモード、スリープモードなら解除
if (__predict_false((int)opmode >= (int)OpMode::Halt)) {
if (opmode == OpMode::Halt) {
// ホールトモードなら解除するだけ。
// XXX 割り込みは一つ次の命令境界で処理されるけど、いいか?
opmode = OpMode::Normal;
} else {
// スリープモードなら通常モードに戻す。
EnterWakeup();
}
}
}
}
// 内部の割り込みマップ (Intmap) をネゲートする。
void
MPU64180Device::NegateIntmap(int source)
{
uint32 oldmap;
uint32 srcmap;
oldmap = intmap;
srcmap = 1U << (31 - source);
intmap &= ~srcmap;
if ((oldmap ^ intmap) != 0) {
// 今は立ち下がりで何もすることはない。
}
}
// INT0 信号線をアサートする。(PIO1 から呼ばれる)
void
MPU64180Device::AssertINT0()
{
AssertIntmap(HD647180::IntmapINT0);
}
// INT0 信号線をネゲートする。
void
MPU64180Device::NegateINT0()
{
NegateIntmap(HD647180::IntmapINT0);
}
// ASCIn 割り込みをアサート/ネゲートする。
// IEF1 はこちらで加味する。
void
MPU64180Device::ChangeASCIInt(uint n, bool asci_req)
{
uint32 map = HD647180::IntmapASCI(n);
if ((asci_req && GetIEF1())) {
AssertIntmap(map);
} else {
NegateIntmap(map);
}
}
// マスカブル割り込みを処理する。(命令境界で呼ばれる)
void
MPU64180Device::DoInterrupt()
{
// まず割り込み禁止。
ief1 = false;
ief2 = false;
// 優先順位の最も高い割り込みを一つ選択。
assert(intmap != 0);
uint source = __builtin_clz(intmap);
// TRAP はここに来ない。NMI は LUNA では未接続のため未実装。
assert(source != HD647180::IntmapTRAP);
assert(source != HD647180::IntmapNMI);
if (source == HD647180::IntmapINT0) {
// INT0 はモードによって分岐方法が異なる。
// ただし mode0 と mode2 は使われてないので未対応。
if (int0mode != 1) {
PANIC("INT0 on mode%u (NOT IMPLEMENTED)", int0mode);
}
// mode1 は固定番地へ
Push2(reg.pc);
ExceptionDirect(source, 0x0038);
} else {
// 残りはすべてベクタ方式。
Push2(reg.pc);
ExceptionVector(source);
}
// XXX あとで見る
// HD64180.pdf, p45. Figure 2.7.7
CYCLE(11);
}
// 例外によって new_addr に直接ジャンプする。
// (HD64180 系の用語では例外ではなくて割り込みだが)
// source は Intmap*。
void
MPU64180Device::ExceptionDirect(int source, uint32 new_addr)
{
// 例外発生とジャンプを記録。
last_vector = source;
brhist.AddEntry(GetPPC(), new_addr, 0x80000000 | source);
exhist.AddEntry(GetPPC(), new_addr, 0x80000000 | source);
reg.pc = new_addr;
}
// 例外ごとに固有のベクタにジャンプする。
// (HD64180 系の用語では例外ではなくて割り込みだが)
// source は Intmap*。
void
MPU64180Device::ExceptionVector(int source)
{
// 固定コード
static uint16 vector_offset[] = {
0, // 0:TRAP
0, // 1:NMI
0, // 2:INT0
0x00, // 3:INT1
0x02, // 4:INT2
0x12, // 5:Input Capture
0x14, // 6:Output Compare
0x16, // 7:Timer Overflow
0x04, // 8:Timer0 (PRT0)
0x06, // 9:Timer1 (PRT1)
0x08, // 10:DMA0
0x0a, // 11:DMA1
0x0c, // 12:CSIO
0x0e, // 13:ASCI0
0x10, // 14:ASCI1
};
// 例外からベクタアドレスを求める。ベクタアドレスは以下の構成。
//
// F E D C B A 9 8 7 6 5 4 3 2 1 0
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | I レジスタ | IL | 固定コード |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
assert(source < countof(vector_offset));
uint32 vec_addr = (reg_i << 8) | intvec_low | vector_offset[source];
// 例外発生を記録。
last_vector = source;
brhist.AddEntry(GetPPC(), -1, 0x80000000 | source);
exhist.AddEntry(GetPPC(), -1, 0x80000000 | source);
// ジャンプ先を読み込む
uint32 new_addr = Read2(vec_addr);
// ベクタジャンプを記録。
brhist.AddEntry(vec_addr, new_addr, -1);
reg.pc = new_addr;
}
//
// hd64180flag
//
// F レジスタの値を取得する
uint8
hd64180flag::Get() const
{
uint8 f = 0;
if (IsS()) f |= HD64180::FlagS;
if (IsZ()) f |= HD64180::FlagZ;
if (b5) f |= 0x20;
if (IsH()) f |= HD64180::FlagH;
if (b3) f |= 0x08;
if (IsPV()) f |= HD64180::FlagPV;
if (IsN()) f |= HD64180::FlagN;
if (IsC()) f |= HD64180::FlagC;
return f;
}
// F レジスタに代入する
void
hd64180flag::Set(uint8 val)
{
SetS(val & HD64180::FlagS);
SetZ(val & HD64180::FlagZ);
b5 = (val & 0x20);
SetH(val & HD64180::FlagH);
b3 = (val & 0x08);
SetPV(val & HD64180::FlagPV);
SetN(val & HD64180::FlagN);
SetC(val & HD64180::FlagC);
}
// fff (0..7) の条件が成立すれば true を返す。
bool
hd64180flag::IsCond(uint fff) const
{
switch (fff) {
case 0: return !IsZ(); // NZ
case 1: return IsZ(); // Z
case 2: return !IsC(); // NC
case 3: return IsC(); // C
case 4: return !IsPV(); // PO
case 5: return IsPV(); // PE
case 6: return !IsS(); // P
case 7: return IsS(); // M
default:
PANIC("invalid cond = %u", fff);
}
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.