File:  [Isaki's NoNo m68k/m88k emulator] / nono / hd64180 / hd64180acc.cpp
Revision 1.1.1.4 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:05:43 2026 UTC (3 months ago) by root
Branches: MAIN, Isaki
CVS tags: v027, v026, v025, v024, v023, HEAD
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;
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.