|
|
nono 1.3.0
//
// nono
// Copyright (C) 2022 nono project
// Licensed under nono-license.txt
//
//
// HD64180 演算部分
//
#include "hd64180acc.h"
/*static*/ inline uint32
hd64180acc::vflag_add(uint32 dst, uint32 src, uint32 res)
{
return (src ^ res) & (dst ^ res);
}
/*static*/ inline uint32
hd64180acc::vflag_sub(uint32 dst, uint32 src, uint32 res)
{
return (src ^ dst) & (dst ^ res);
}
/*static*/ inline uint32
hd64180acc::hflag_add(uint32 dst, uint32 src)
{
return (dst & 0x0f) + (src & 0x0f);
}
/*static*/ inline uint32
hd64180acc::hflag_sub(uint32 dst, uint32 src)
{
return (dst & 0x0f) - (src & 0x0f);
}
// LD A,I、LD A,R 命令のフラグ変化。
// S:* Z:* H:0 P/V:IEF2 N:0 C:-
uint32
hd64180acc::ld_ir8(uint32 src)
{
// P/V (IEF2) は呼び出し側で行う。
S = Z = src;
SetH(false);
SetN(false);
return Z;
}
// save_c が true なら C を変更しない。
// S:* Z:* H:* V:* N:0 C:*/-
uint32
hd64180acc::add_8(uint32 dst, uint32 src, bool save_c)
{
if (save_c) {
Z = dst + src;
} else {
CZ = dst + src;
}
S = Z;
H = hflag_add(dst, src);
LetV(vflag_add(dst, src, Z));
SetN(false);
return (uint8)Z;
}
// save_c が true なら C を変更しない。
// S:* Z:* H:* V:* N:0 C:*/-
uint32
hd64180acc::sub_8(uint32 dst, uint32 src, bool save_c)
{
if (save_c) {
Z = dst - src;
} else {
CZ = dst - src;
}
S = Z;
H = hflag_sub(dst, src);
LetV(vflag_sub(dst, src, Z));
SetN(true);
return (uint8)Z;
}
// S:* Z:* H:* V:* N:0 C:*
uint32
hd64180acc::adc_8(uint32 dst, uint32 src)
{
uint32 cin = IsC() ? 1 : 0;
uint32 res = dst + src + cin;
CZ = res;
S = res;
LetV(vflag_add(dst, src, res));
H = hflag_add(dst, src);
H |= hflag_add(H, cin);
SetN(false);
return Z;
}
// S:* Z:* H:* V:* N:0 C:*
uint32
hd64180acc::sbc_8(uint32 dst, uint32 src)
{
uint32 cin = IsC() ? 1 : 0;
uint32 res = dst - src - cin;
CZ = res;
S = res;
LetV(vflag_sub(dst, src, res));
H = hflag_sub(dst, src);
H |= hflag_sub(H, cin);
SetN(true);
return Z;
}
// S:- Z:- H:X PV:- N:0 C:*
uint32
hd64180acc::add_16(uint32 dst, uint32 src)
{
uint32 res = dst + src;
C = res >> 16;
SetN(false);
// XXX H は不定
return res;
}
// S:* Z:* H:X V:* N:0 C:*
uint32
hd64180acc::adc_16(uint32 dst, uint32 src)
{
// V のために2回に分けて計算しないといけない?
uint32 res = dst + src;
uint32 v = vflag_add(dst, src, res);
dst = res;
src = IsC() ? 1 : 0;
res = dst + src;
v |= vflag_add(dst, src, res);
LetV((uint32)(v >> 8));
// C, S, Z は 16bit が対象でいい?
C = res >> 16;
S = res >> 8;
Z = res | S;
SetN(false);
// XXX H は本当は不定
return res;
}
// S:* Z:* H:X V:* N:1 C:*
uint32
hd64180acc::sbc_16(uint32 dst, uint32 src)
{
// V のために2回に分けて計算しないといけない?
uint32 res = dst - src;
uint32 v = vflag_sub(dst, src, res);
dst = res;
src = IsC() ? 1 : 0;
res = dst - src;
v |= vflag_sub(dst, src, res);
LetV((uint32)(v >> 8));
// C, S, Z は 16bit が対象でいい?
C = res >> 16;
S = res >> 8;
Z = res | S;
SetN(true);
// XXX H は本当は不定
return res;
}
// BIT 命令
// S:X Z:* H:1 PV:X N:0 C:-
void
hd64180acc::bit_8(uint b, uint32 src)
{
Z = src & (1U << b);
SetH(true);
SetN(false);
// XXX S, P(V) は本当は不定
}
// IN A,(n) 命令のフラグ変化。他からも色々呼ばれる。
// S:* Z:* H:0 P:* N:0 C:-
uint32
hd64180acc::in_8(uint32 src)
{
S = Z = src;
SetN(false);
SetH(false);
LetP(Z);
return Z;
}
// OTIM/OTDM 命令のフラグ変化。
// 戻り値は演算後の B レジスタの値。
// HD64180.pdf p.110
// S:* Z:* H:* P:* N:* C:*
uint32
hd64180acc::outm_8(uint32 src, uint32 breg)
{
CZ = breg - 1;
S = Z;
H = hflag_sub(breg, 1);
LetP(Z); // 減算後の B のパリティ
N = (src & 0x80); // (HL) の MSB
return Z;
}
// S:* Z:* H:1 P:* N:0 C:0
uint32
hd64180acc::and_8(uint32 dst, uint32 src)
{
uint32 res;
SetC(false);
res = in_8(dst & src);
SetH(true); // これだけ H はセット
return res;
}
// S:* Z:* H:0 P:* N:0 C:0
uint32
hd64180acc::or_8(uint32 dst, uint32 src)
{
SetC(false);
return in_8(dst | src);
}
// S:* Z:* H:0 P:* N:0 C:0
uint32
hd64180acc::xor_8(uint32 dst, uint32 src)
{
SetC(false);
return in_8(dst ^ src);
}
// S:* Z:* H:1 P:* N:0 C:0
void
hd64180acc::tst_8(uint32 dst, uint32 src)
{
in_8(dst & src);
SetH(true);
SetC(false);
}
// S:* Z:* H:0 P:* N:0 C:*
uint32
hd64180acc::sla_8(uint32 src)
{
CZ = src << 1;
return in_8(Z);
}
// S:* Z:* H:0 P:* N:0 C:*
uint32
hd64180acc::sra_8(uint32 src)
{
C = src;
Z = (src >> 1);
Z |= src & 0x80;
return in_8(Z);
}
// S:* Z:* H:0 P:* N:0 C:*
uint32
hd64180acc::sll_8(uint32 src)
{
// bit0 に %1 が入るようだ
// http://www.z80.info/z80sflag.htm
CZ = (src << 1) | 1;
return in_8(Z);
}
// S:* Z:* H:0 P:* N:0 C:*
uint32
hd64180acc::srl_8(uint32 src)
{
C = src;
Z = src >> 1;
return in_8(Z);
}
// S Z H P N C
// RLA - - 0 - 0 * : rl_8()
// RL r * * 0 P 0 * : in_8(rl_8())
// RLD * * 0 P 0 - : in_8()
uint32
hd64180acc::rl_8(uint32 src)
{
src <<= 1;
if (IsC()) {
src |= 0x01;
}
C = src >> 8;
SetN(false);
SetH(false);
return (uint8)src;
}
uint32
hd64180acc::rlc_8(uint32 src)
{
src <<= 1;
if ((src & 0x100)) {
src |= 0x01;
}
C = src >> 8;
SetN(false);
SetH(false);
return (uint8)src;
}
uint32
hd64180acc::rr_8(uint32 src)
{
src = (uint8)src;
if (IsC()) {
src |= 0x100;
}
C = src;
src >>= 1;
SetN(false);
SetH(false);
return (uint8)src;
}
uint32
hd64180acc::rrc_8(uint32 src)
{
C = src;
src = (uint8)src;
src |= (C << 8);
src >>= 1;
SetN(false);
SetH(false);
return (uint8)src;
}
uint32
hd64180acc::daa_8(uint32 src)
{
uint32 b;
uint32 res;
if (IsN() == false) {
bool tmpC = IsC(); // 入力時の状態
bool tmpH = IsH();
bool resC = IsC(); // 出力用。C は入力と内部演算の和
bool resH; // 出力用。H は内部演算のみの和
// 下位桁
uint32 al = src & 0xf;
if (al >= 0x0a || IsH()) {
b = 0x06;
} else {
b = 0;
}
src = add_8(src, b);
resC |= IsC();
resH = IsH();
// 上位桁
uint32 ah = src >> 4;
if (ah >= 0x0a || tmpC) {
b = 0x60;
// XXX 入力が 9A-9F で H=1 C=0 の時、
// 下位桁を補正して(6を足して) 上位桁が A になるのに、
// HD64180 ではこれを補正してないようだ。
if (0xa0 <= src && src < 0xa6 && tmpH && tmpC == false) {
b = 0;
}
} else {
b = 0;
// XXX 入力が FA-FF で C=0 の時、
// 下位桁を補正して(6 を足して) C=1 00-05 に繰り上がるだけの
// はずが、HD64180 では 60 も足してしまうようだ。
if (IsC() && ah == 0x00 && tmpC == false) {
b = 0x60;
}
}
res = add_8(src, b);
resC |= IsC();
resH |= IsH();
SetH(resH);
SetC(resC);
} else {
// N==1 (減算) 側は跡形なく動作が違う。
// 意味はさっぱり分からんけどとりあえず実機と同じ動作。
bool resC = IsC();
b = 0;
if (IsH()) {
b += 0x06;
if (src < 0x06) {
resC = true;
}
}
if (IsC()) {
b += 0x60;
}
res = sub_8(src, b);
SetC(resC);
}
// P/V フラグは P
LetP(res);
return res;
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.