--- nono/debugger/debugger_m88xx0.cpp 2026/04/29 17:04:30 1.1 +++ nono/debugger/debugger_m88xx0.cpp 2026/04/29 17:04:54 1.1.1.7 @@ -1,13 +1,14 @@ // // nono -// Copyright (C) 2020 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // #include "console.h" #include "debugger_private.h" #include "debugger_m88xx0.h" #include "m88100disasm.h" -#include "mystring.h" +#include "m88100acc.h" // newval が oldval から変化していれば太字属性にするマクロ #define TSBOLD(newval, oldval) (((newval) != (oldval)) ? TA::Em : TA::Off) @@ -18,12 +19,129 @@ // name の mask で示される部分が変化していれば太字属性にする #define TSBOLDM(name, mask) TSBOLD((cpu->name & (mask)), (prev.name & (mask))) +// コンストラクタ +DebuggerMD_m88xx0::DebuggerMD_m88xx0(Debugger *parent_, m88kcpu *cpu_) + : inherited(parent_, Debugger::Arch::M88xx0) +{ + cpu = cpu_; + + // 機種依存変数 + vector_max = 512; + inst_bytes = 4; + inst_bytes_fixed = 4; + + cmp_rd = -1; +} + +// デストラクタ +DebuggerMD_m88xx0::~DebuggerMD_m88xx0() +{ +} + +bool +DebuggerMD_m88xx0::MMUEnabled() const +{ + // XXX 命令/データの区別がまだないので、とりあえず命令のほうだけ見る + uint32 xapr = cpu->cmmu[0].GetAPR(IsSuper() ? 1 : 0); + return (xapr & m88200::APR_TE); +} + +// アドレス変換 +uint64 +DebuggerMD_m88xx0::TranslateAddr(saddr_t laddr, bool lookup) const +{ + m88200 *cmmu; + + // XXX とりあえずね + if (laddr.IsData() == false) { + cmmu = &cpu->cmmu[0]; + } else { + cmmu = &cpu->cmmu[1]; + } + + return cmmu->TranslatePeek((uint32)laddr, laddr.IsSuper(), lookup); +} + +// op が bb0 命令なら true を返す +/*static*/ bool +DebuggerMD_m88xx0::IsBB0(uint32 op) +{ + return ((op & 0xf8000000) == 0xd0000000); // bb0 +} + +// op が tb0 命令なら true を返す +/*static*/ bool +DebuggerMD_m88xx0::IsTB0(uint32 op) +{ + return ((op & 0xfc00fe00) == 0xf000d000); // tb0 +} + +// op が bb0/tb0 命令なら true を返す +/*static*/ bool +DebuggerMD_m88xx0::IsBBTB0(uint32 op) +{ + return IsBB0(op) || IsTB0(op); +} + +// op が bb1 命令なら true を返す +/*static*/ bool +DebuggerMD_m88xx0::IsBB1(uint32 op) +{ + return ((op & 0xf8000000) == 0xd8000000); // bb1 +} + +// op が tb1 命令なら true を返す +/*static*/ bool +DebuggerMD_m88xx0::IsTB1(uint32 op) +{ + return ((op & 0xfc00fe00) == 0xf000d800); // tb1 +} + +// op が bb1/tb1 命令なら true を返す +/*static*/ bool +DebuggerMD_m88xx0::IsBBTB1(uint32 op) +{ + return IsBB1(op) || IsTB1(op); +} + void DebuggerMD_m88xx0::BackupRegs() { prev = (m88100reg)*cpu; } +void +DebuggerMD_m88xx0::SetStepOut() +{ + so_r1 = cpu->r[1]; +} + +bool +DebuggerMD_m88xx0::IsStepOut() const +{ + return (cpu->xip == so_r1); +} + +// この命令がステップイン出来るなら true を返す +bool +DebuggerMD_m88xx0::IsOpStepIn(DebuggerMemoryStream& mem) +{ + uint32 op = mem.FetchInst(); + + if ((op & 0xf800'0000) == 0xc800'0000 // bsr + || IsTB0(op) // tb0 + || (IsTB1(op) && ((op >> 16) & 0x1f) != 0) // tb1 (except r0) + || (op & 0xfc00'fe00) == 0xf000'e800 // tcnd + || (op & 0xfc00'fb00) == 0xf400'c800 // jsr + || (op & 0xfc00'0000) == 0xf800'0000 // tbnd imm + || (op & 0xfc00'ffe0) == 0xf400'f800 // tbnd rs2 + ) { + return true; + } + + return false; +} + // レジスタ名からそのレジスタ値を返す。 // メモリダンプのような用途なのでアドレスとして使えるアドレスのみ。 uint64 @@ -40,9 +158,9 @@ DebuggerMD_m88xx0::GetRegAddr(const char //if (strcmp(name, "psr") == 0) return cpu->psr; //if (strcmp(name, "epsr") == 0) return cpu->epsr; //if (strcmp(name, "ssbr") == 0) return cpu->ssbr; - if (strcmp(name, "sxip") == 0) return cpu->sxip; - if (strcmp(name, "snip") == 0) return cpu->snip; - if (strcmp(name, "sfip") == 0) return cpu->sfip; + if (strcmp(name, "sxip") == 0) return cpu->sxip & m88100reg::SIP_MASK; + if (strcmp(name, "snip") == 0) return cpu->snip & m88100reg::SIP_MASK; + if (strcmp(name, "sfip") == 0) return cpu->sfip & m88100reg::SIP_MASK; if (strcmp(name, "vbr") == 0) return cpu->vbr; //if (strcmp(name, "dmt0") == 0) return cpu->dmt0; if (strcmp(name, "dmd0") == 0) return cpu->dmd0; @@ -79,16 +197,212 @@ DebuggerMD_m88xx0::GetRegAddr(const char return (uint64)-1; } +// レジスタ表示系コマンドのヘルプ +/*static*/ const HelpMessages +DebuggerMD_m88xx0::HelpListReg = { + { "r", "Show general registers" }, + { "rc", "Show (integer) control registers" }, + { "rf", "Show floating point control registers" }, + { "ro", "Show other (shadow) registers" }, + { "ra", "Show ATC on CMMU" }, + { "rd", "Show data cache on CMMU" }, + { "rm", "Show CMMU registers" }, +}; + +/*static*/ const HelpMessages +DebuggerMD_m88xx0::HelpReg = { + //----- + { "r", R"**( + Command: r + + Shows general registers. + )**" }, + + //----- + { "rc", R"**( + Command: rc + + Shows the (integer) control registers. + )**" }, + + //----- + { "rf", R"**( + Command: rf + + Shows the floating point control registers. + )**" }, + + //----- + { "ro", R"**( + Command: ro + + Shows the other registers. + )**" }, + + //----- + { "ra", R"**( + Command: ra := 7, 6 + + Shows BATC/PATC on CMMU. + CMMU7 is instruction CMMU of CPU#0. + CMMU6 is data CMMU of CPU#0. + )**" }, + { "ra6", "=ra" }, + { "ra7", "=ra" }, + + //----- + { "rd", R"**( + Command: rd := 7, 6 + + Shows data cache on CMMU. + CMMU7 is instruction CMMU of CPU#0. + CMMU6 is data CMMU of CPU#0. + )**" }, + { "rd6", "=rd" }, + { "rd7", "=rd" }, + + //----- + { "rm", R"**( + Command: rm := 7, 6 + + Shows CMMU registers. + CMMU7 is instruction CMMU of CPU#0. + CMMU6 is data CMMU of CPU#0. + )**" }, + { "rm6", "=rm" }, + { "rm7", "=rm" }, +}; + +const HelpMessages& +DebuggerMD_m88xx0::GetHelpListReg() const +{ + return HelpListReg; +} + +const HelpMessages& +DebuggerMD_m88xx0::GetHelpReg() const +{ + return HelpReg; +} + +// レジスタ表示系コマンド +bool +DebuggerMD_m88xx0::ShowRegister(Console *cons, + const std::vector& args) +{ + int cmmu_id; + + // 余分な引数は無視する? + + if (args[0] == "r") { + ShowRegMain(cons); + return true; + } + if (args[0] == "rc") { + ShowRegCtrl(cons); + return true; + } + if (args[0] == "rf") { + ShowRegFPU(cons); + return true; + } + if (args[0] == "ro") { + ShowRegOther(cons); + return true; + } + + if (MatchCMMUCmd(args[0], "ra", &cmmu_id)) { + Monitor *mon = gMonitorManager.Find(ID_MONITOR_ATC(cmmu_id)); + if (mon) { + parent->ShowMonitor(*mon); + return true; + } + goto notfound; + } + if (MatchCMMUCmd(args[0], "rd", &cmmu_id)) { + return ShowRegCache(cons, args, cmmu_id); + } + if (MatchCMMUCmd(args[0], "rm", &cmmu_id)) { + Monitor *mon = gMonitorManager.Find(ID_MONITOR_CMMU(cmmu_id)); + if (mon) { + parent->ShowMonitor(*mon); + return true; + } + goto notfound; + } + + // 該当なし + notfound: + return false; +} + +// CMMU ID 入りコマンド名を照合する。 +// cmdname が basename + の時、*idp に id を格納して true を返す。 +// 一致しないか id が(数値でないなど)不正なら false を返す。 +// 実際にその id の CMMU が存在するかはここではチェックしない。 +bool +DebuggerMD_m88xx0::MatchCMMUCmd(const std::string& cmdname, + const char *basename, int *idp) +{ + int baselen = strlen(basename); + + if (strncmp(cmdname.c_str(), basename, baselen) != 0) { + return false; + } + + // 先頭が一致したら id を調べる + int id; + const char *start = cmdname.c_str() + baselen; + char *end; + errno = 0; + id = strtoul(start, &end, 10); + if (*start == '\0' || *end != '\0' || errno == ERANGE) { + return false; + } + + *idp = id; + return true; +} + void DebuggerMD_m88xx0::ShowRegMain(Console *cons) { TextScreen s(80, 8); +/* +r0 :00000000 r8 :00000000 r16:00000000 r24:00000000 b:bs=1 a:lo=1 8:ls=0 +r1 :00700000 r9 :00000000 r17:00000000 r25:00000000 +r2 :0070e1a0 r10:00000000 r18:00000000 r26:00000000 +*/ + for (int i = 0; i < countof(cpu->r); i++) { s.Print((i / 8) * 14, i % 8, TSBOLDC(r[i]), "r%-2d:%08x", i, cpu->r[i]); } - parent->ShowMonitor(s); + // cmp 以降最初の分岐命令で結果レジスタの条件ビットマップを表示する。 + // 分岐命令は bb1 #3 みたいな形式なのでそれだけでは条件演算子が分からない + // のと、bb1 #n 自体は cmp 以外に対しても使えるので、cmp 命令以降最初の + // 分岐命令でのみ表示してみる。 + if (cmp_rd >= 0 && (IsBBTB0(cpu->opX) || IsBBTB1(cpu->opX))) { + static const char * const cmpstr[] = { + NULL, NULL, "eq", "ne", "gt", "le", "lt", "ge", + "hi", "ls", "lo", "hs", + }; + int x = 0; + int y = 0; + uint32 res = cpu->r[cmp_rd]; + for (int b = 11; b >= 2; b--) { + s.Print(57 + x * 7, y, "%x:%s=%d", b, cmpstr[b], + (res & (1 << b)) ? 1 : 0); + x++; + if (x > 2) { + x = 0; + y++; + } + } + } + + parent->ShowTextScreen(s); } void @@ -101,12 +415,12 @@ DebuggerMD_m88xx0::ShowRegCtrl(Console * /* 0 1 2 3 4 5 6 7 0123456789012345678901234567890123456789012345678901234567890123456789012345678 -pid (cr0):12345678(arch=$00 ver=$00 M) ssbr(cr3):00000000 -psr (cr1):12345678(S,LE,SER,Cy SFD1,MXM,IND,SFRZ) vbr (cr7):00000000 -epsr(cr2):12345678(S,LE,SER,Cy SFD1,MXM,IND,SFRZ) sr0(cr17):00000000 -xip:00000000 opX:00000000(--) sxip(cr4):00000000:VE sr1(cr18):00000000 -nip:00000000 opF:00000000(--) snip(cr5):00000000:-- sr2(cr19):00000000 -fip:00000000 sfip(cr6):00000000:VE sr3(cr20):00000000 +pid (cr0):12345678(Arch=$00 Ver=$00 M/C=Checker) sr0(cr17):00000000 +psr (cr1):12345678(S,LE,SER,Cy SFD1,MXM,IND,SFRZ) sr1(cr18):00000000 +epsr(cr2):12345678(S,LE,SER,Cy SFD1,MXM,IND,SFRZ) sr2(cr19):00000000 +xip:00000000 opX:00000000(--) sxip(cr4):00000000:VE sr3(cr20):00000000 +nip:00000000 opF:00000000(--) snip(cr5):00000000:-- ssbr(cr3):00000000 +fip:00000000 sfip(cr6):00000000:VE vbr (cr7):00000000 */ #define PRINT_B(x, y, reg, mask, fmt, arg) do { \ @@ -118,12 +432,13 @@ fip:00000000 sfip(cr6 // pid(cr0) x = 0; y = 0; - s.Print(x, y, TSBOLDC(pid), "pid (cr0):%08x", cpu->pid); - s.Print(x + 18, y, "("); - PRINT_B(x + 19, y, pid, m88100reg::PID_REV_MASK, "arch=$%02x", _new >> 8); - PRINT_B(x + 28, y, pid, m88100reg::PID_VER_MASK, "ver=$%02x", _new); - PRINT_B(x + 36, y, pid, m88100reg::PID_MASTER, "%c", _new ? 'M' : '-'); - s.Print(x + 37, y, ")"); + s.Print(x, y, TSBOLDC(pid), "pid (cr0):%08x(", cpu->pid); + PRINT_B(x + 19, y, pid, m88100reg::PID_REV_MASK, "Arch=$%02x", + (_new >> 8) & 0xff); + PRINT_B(x + 28, y, pid, m88100reg::PID_VER_MASK, "Ver=$%02x", + (_new >> 1) & 0x7f); + PRINT_B(x + 36, y, pid, m88100reg::PID_MASTER, "M/C=%s)", + (_new & m88100reg::PID_MASTER) ? "Master" : "Checker"); // psr(cr1) y++; @@ -140,7 +455,7 @@ fip:00000000 sfip(cr6 // epsr(cr2) y++; - s.Print(x, y, TSBOLDC(psr), "epsr(cr2):%08x", cpu->epsr); + s.Print(x, y, TSBOLDC(epsr), "epsr(cr2):%08x", cpu->epsr); s.Print(x + 18, y, "(s,le,ser,cy sfd1,mxm,ind,sfrz)"); PRINT_B(x + 19, y, epsr, m88100reg::PSR_SUPER, "%c", _new ? 'S' : '-'); PRINT_B(x + 21, y, epsr, m88100reg::PSR_BO_LE, "%s", _new ? "LE" : "BE"); @@ -176,19 +491,78 @@ fip:00000000 sfip(cr6 x = 55; y = 0; + // SR* + for (int i = 0; i < 4; i++) { + int rn = 17 + i; + s.Print(x, y++, TSBOLDC(cr[rn]), "sr%d(cr%d):%08x", + i, rn, cpu->cr[rn]); + } // ssbr(cr3) s.Print(x, y++, TSBOLDC(ssbr), "ssbr(cr3):%08x", cpu->ssbr); // vbr(cr7) s.Print(x, y++, TSBOLDC(vbr), "vbr (cr7):%08x", cpu->vbr); - // SR* - for (int i = 0; i < 4; i++) { - int rn = 17 + i; - s.Print(x, y + i, TSBOLDC(cr[rn]), "sr%d(cr%d):%08x", - i, rn, cpu->cr[rn]); + parent->ShowTextScreen(s); +} + +// CMMU のデータキャッシュを表示 +// rd なら概要表示。 +// rd なら個別セットの詳細表示。 +// id を受け付ければ true を、そうでなければ false を返す。 +bool +DebuggerMD_m88xx0::ShowRegCache(Console *cons, + const std::vector& args, int id) +{ + m88200 *cmmu; + + // XXX id はまだ決め打ち + if (id == 7) { + cmmu = &cpu->cmmu[0]; + } else if (id == 6) { + cmmu = &cpu->cmmu[1]; + } else { + return false; } - parent->ShowMonitor(s); + if (args.size() < 2) { + ShowRegCacheOverview(cons, cmmu); + } else { + int set; + char *end; + errno = 0; + set = strtol(args[1].c_str(), &end, 16); + if (end == &args[1][0] || *end != '\0' || errno == ERANGE) { + cons->Print(" must be a number\n"); + goto done; + } + if (set < 0 || set > 256) { + cons->Print(" must be in 00..ff\n"); + goto done; + } + ShowRegCacheSet(cons, cmmu, set); + } + done: + return true; +} + +// CMMU データキャッシュの概要表示 +void +DebuggerMD_m88xx0::ShowRegCacheOverview(Console *cons, m88200 *cmmu) +{ + TextScreen s(70, 17); + + cmmu->MonitorCacheOverview(s, 0, false); + parent->ShowTextScreen(s); +} + +// CMMU データキャッシュの指定セットの詳細表示 +void +DebuggerMD_m88xx0::ShowRegCacheSet(Console *cons, m88200 *cmmu, int setidx) +{ + TextScreen s(70, 5); + + cmmu->MonitorCacheSet(s, 0, setidx); + parent->ShowTextScreen(s); } void @@ -217,19 +591,19 @@ DebuggerMD_m88xx0::ShowRegFPU(Console *c s.Print(44, 2, TSBOLDC(fpsr), "fpsr(fcr62):%08x", cpu->fpsr); s.Print(44, 3, TSBOLDC(fpcr), "fpcr(fcr63):%08x", cpu->fpcr); - parent->ShowMonitor(s); + parent->ShowTextScreen(s); } void -DebuggerMD_m88xx0::ShowRegMMU(Console *cons) +DebuggerMD_m88xx0::ShowRegOther(Console *cons) { TextScreen s(80, 3); /* 0 1 2 3 4 5 6 7 0123456789012345678901234567890123456789012345678901234567890123456789012345678 -dmt0(cr8) :0000(B,DA,D1,L,Rxx,S,ENn,W,V) dmd0(cr9) :00000000 dma0(cr10):00000000 -dmt0(cr11):0000(B,DA,D1,L,Rxx,S,ENn,W,V) dmd0(cr9) :00000000 dma0(cr10):00000000 -dmt0(cr14):0000(B,DA,D1,L,Rxx,S,ENn,W,V) dmd0(cr9) :00000000 dma0(cr10):00000000 +dmt0(cr8) :0000(B,S,D,L,Rxx,S,---B,W,V) dmd0(cr9) :00000000 dma0(cr10):00000000 +dmt0(cr11):0000(B,U,D,L,Rxx,S,HH--,W,V) dmd0(cr9) :00000000 dma0(cr10):00000000 +dmt0(cr14):0000(B, ,D,L,Rxx,S,LLLL,W,V) dmd0(cr9) :00000000 dma0(cr10):00000000 */ for (int i = 0; i <= 2; i++) { int dt = 8 + i * 3; @@ -238,39 +612,36 @@ dmt0(cr14):0000(B,DA,D1,L,Rxx,S,ENn,W,V) s.Print(0, i, TSBOLDC(cr[dt]), "dmt%d(cr%-2d):%04x", i, dt, (cpu->cr[dt] & 0xffff)); - s.Print(15, i, "(b,da,d1,l,rx, s,ENn,w,v)"); + s.Print(15, i, "(b,s,d,l,rx, s,en ,w,v)"); PRINT_B(16, i, cr[dt], m88100reg::DM_BO, "%c", _new ? 'B' : '-'); - PRINT_B(18, i, cr[dt], m88100reg::DM_DAS, "%s", _new ? "DA" : "--"); - PRINT_B(21, i, cr[dt], m88100reg::DM_DOUB1, "%s", _new ? "D1" : "--"); - PRINT_B(24, i, cr[dt], m88100reg::DM_LOCK, "%c", _new ? 'L' : '-'); - // カンマが自然に見えるようにここだけカンマも含めて前詰めで出力 - PRINT_B(27, i, cr[dt], m88100reg::DM_DREG_MASK, "%d,", _new >> 7); - PRINT_B(30, i, cr[dt], m88100reg::DM_SIGNED, "%c", _new ? 'S' : '-'); - PRINT_B(34, i, cr[dt], m88100reg::DM_EN_MASK,"%d", _new >> 2); - PRINT_B(36, i, cr[dt], m88100reg::DM_WRITE, "%c", _new ? 'W' : '-'); - PRINT_B(38, i, cr[dt], m88100reg::DM_VALID, "%c", _new ? 'V' : '-'); + PRINT_B(18, i, cr[dt], m88100reg::DM_DAS, "%c", _new ? 'S' : 'U'); + PRINT_B(20, i, cr[dt], m88100reg::DM_DOUB1, "%c", _new ? 'D' : '-'); + PRINT_B(22, i, cr[dt], m88100reg::DM_LOCK, "%c", _new ? 'L' : '-'); + // カンマを前詰め。かつ DREG がボールドでもカンマはボールドにしない + PRINT_B(24, i, cr[dt], m88100reg::DM_DREG_MASK, "r%d", _new >> 7); + s.Puts(","); + PRINT_B(28, i, cr[dt], m88100reg::DM_SIGNED, "%c", _new ? 'S' : '-'); + PRINT_B(30, i, cr[dt], m88100reg::DM_EN_MASK,"%s", + m88100reg::dmt_en_str[(_new >> 2) & 0xf]); + PRINT_B(35, i, cr[dt], m88100reg::DM_WRITE, "%c", _new ? 'W' : '-'); + PRINT_B(37, i, cr[dt], m88100reg::DM_VALID, "%c", _new ? 'V' : '-'); - s.Print(41, i, TSBOLDC(cr[dd]), "dmd%d(cr%-2d):%08x", + s.Print(40, i, TSBOLDC(cr[dd]), "dmd%d(cr%-2d):%08x", i, dd, cpu->cr[dd]); - s.Print(61, i, TSBOLDC(cr[dd]), "dma%d(cr%2d):%08x", + s.Print(60, i, TSBOLDC(cr[dd]), "dma%d(cr%2d):%08x", i, da, cpu->cr[da]); } - parent->ShowMonitor(s); -} - -void -DebuggerMD_m88xx0::ShowRegOther(Console *cons) -{ + parent->ShowTextScreen(s); } // 逆アセンブル bool -DebuggerMD_m88xx0::Disassemble(saddr_t laddr, +DebuggerMD_m88xx0::Disassemble(DebuggerMemoryStream& mem, std::string& mnemonic, std::vector& bin) { - m88100disasm dis(this); - if (dis.Exec(laddr) == false) { + m88100disasm dis(mem); + if (dis.Exec() == false) { return false; } mnemonic = dis.text; @@ -302,5 +673,85 @@ DebuggerMD_m88xx0::FormatDisasmLive(cons delay = "(delayed) "; } - return string_format("%08x %s%s", op, delay.c_str(), mnemonic.c_str()); + const char *cond = CondStr(op); + return string_format("%08x %s%s%s", + op, delay.c_str(), mnemonic.c_str(), cond); +} + +// 例外のベクタ番号から名前を取得。 +const char * +DebuggerMD_m88xx0::GetExceptionName(int vector) const +{ + return m88kcpu::GetExceptionName(vector); +} + +#define TAKE_IF(expr) do { \ + if ((expr)) \ + return " (will take)"; \ + else \ + return " (will not take)"; \ +} while (0) + +// 条件命令なら、成立可否などの文字列を返す。 +const char * +DebuggerMD_m88xx0::CondStr(uint32 op) +{ + uint32 s = m88100opf_S1(op); + + if (IsBBTB0(op)) { + // 11010N BBBBB SSSSS dddddd dd dddddddd bb0 + // 111100 BBBBB SSSSS 110100 0V VVVVVVVV tb0 + // rS の bit B が %0 ならブランチ/トラップ + cmp_rd = -1; + uint32 b = m88100opf_B5(op); + TAKE_IF((cpu->r[s] & (1 << b)) == 0); + } + if (IsBB1(op)) { + // 11011N BBBBB SSSSS dddddd dd dddddddd bb1 + // rS の bit B が %1 ならブランチ + cmp_rd = -1; + // 対 r0 なら絶対成立しないので nop + if (s == 0) { + return " (nop)"; + } + uint32 b = m88100opf_B5(op); + TAKE_IF((cpu->r[s] & (1 << b)) != 0); + } + if (IsTB1(op)) { + // 111100 BBBBB SSSSS 110110 0V VVVVVVVV tb1 + // rS の bit B が %1 ならトラップ + cmp_rd = -1; + // 対 r0 なら sync として使っており逆アセンブラ側で表示を加工してある + // のでこちら側では対処不要。 + if (s == 0) { + return ""; + } + uint32 b = m88100opf_B5(op); + TAKE_IF((cpu->r[s] & (1 << b)) != 0); + } + if ((op & 0xf8000000) == 0xe8000000 || // bcnd + (op & 0xfc00fe00) == 0xf000e800) // tcnd + { + // 11101N MMMMM SSSSS dddddd dd dddddddd bcnd + // rS が M5 で示される条件にマッチすればブランチ/トラップ + cmp_rd = -1; + uint32 m5 = m88100opf_M5(op); + TAKE_IF( + (m5 == 0x2 && cpu->r[s] == 0) || // eq0 + (m5 == 0xd && cpu->r[s] != 0) || // ne0 + (m5 == 0x1 && (int32)cpu->r[s] > 0) || // gt0 + (m5 == 0xc && (int32)cpu->r[s] < 0) || // lt0 + (m5 == 0x3 && (int32)cpu->r[s] >= 0) || // ge0 + (m5 == 0xe && (int32)cpu->r[s] <= 0) // le0 + ); + } + + if ((op & 0xfc00fce0) == 0xf4007c00 || // cmp rD,rS1,rS2 + (op & 0xfc000000) == 0x7c000000 ) // cmp rD,rS1,imm16 + { + // 次の条件分岐命令で結果レジスタを表示するため、rD だけ覚えておく + cmp_rd = m88100opf_D(op); + } + + return ""; }