diff --git a/CMakeLists.txt b/CMakeLists.txt index a0934b5..092df24 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,7 +77,7 @@ add_executable(unit_tests sources/APU/Instructions/ProgramFlow.cpp sources/APU/Operand.cpp sources/APU/Instructions/DecimalCompensation.cpp; - sources/APU/Instructions/MultiplicationDivision.cpp) + sources/APU/Instructions/MultiplicationDivision.cpp sources/APU/Instructions/16bitArithmetic.cpp) # include criterion & coverage target_link_libraries(unit_tests criterion -lgcov) @@ -165,7 +165,7 @@ add_executable(ComSquare sources/APU/Instructions/ProgramFlow.cpp sources/APU/Operand.cpp sources/APU/Instructions/DecimalCompensation.cpp - sources/APU/Instructions/MultiplicationDivision.cpp) + sources/APU/Instructions/MultiplicationDivision.cpp sources/APU/Instructions/16bitArithmetic.cpp) target_compile_definitions(ComSquare PUBLIC DEBUGGER_ENABLED) diff --git a/sources/APU/APU.cpp b/sources/APU/APU.cpp index caa1165..32f22ec 100644 --- a/sources/APU/APU.cpp +++ b/sources/APU/APU.cpp @@ -184,6 +184,8 @@ namespace ComSquare::APU return this->CLR1(this->_getDirectAddr(), 0); case 0x13: return this->BBC(this->_getDirectAddr(), 0); + case 0x1A: + return this->DECW(this->_getDirectAddr()); case 0x1F: return this->JMP(this->_getAbsoluteAddrByX(), true); case 0x20: @@ -210,6 +212,8 @@ namespace ComSquare::APU return this->CLR1(this->_getDirectAddr(), 1); case 0x33: return this->BBC(this->_getDirectAddr(), 1); + case 0x3A: + return this->INCW(this->_getDirectAddr()); case 0x3F: return this->CALL(this->_getAbsoluteAddr()); case 0x40: @@ -236,6 +240,8 @@ namespace ComSquare::APU return this->CLR1(this->_getDirectAddr(), 2); case 0x53: return this->BBC(this->_getDirectAddr(), 2); + case 0x5A: + return this->CMPW(this->_getDirectAddr()); case 0x5F: return this->JMP(this->_getAbsoluteAddr()); case 0x60: @@ -262,6 +268,8 @@ namespace ComSquare::APU return this->CLR1(this->_getDirectAddr(), 3); case 0x73: return this->BBC(this->_getDirectAddr(), 3); + case 0x7A: + return this->ADDW(this->_getDirectAddr()); case 0x7F: return this->RETI(); case 0x80: @@ -284,6 +292,8 @@ namespace ComSquare::APU return this->CLR1(this->_getDirectAddr(), 4); case 0x93: return this->BBC(this->_getDirectAddr(), 4); + case 0x9A: + return this->SUBW(this->_getDirectAddr()); case 0x9E: return this->DIV(); case 0xA0: diff --git a/sources/APU/APU.hpp b/sources/APU/APU.hpp index f9f883d..3e6b143 100644 --- a/sources/APU/APU.hpp +++ b/sources/APU/APU.hpp @@ -277,6 +277,17 @@ namespace ComSquare::APU int MUL(); //! @brief Divide the 16-bit value YA by X, storing the quotient in A and the remainder in Y. int DIV(); + + //! @brief Increment a word value. + int INCW(uint24_t addr); + //! @brief Decrement a word value. + int DECW(uint24_t addr); + //! @brief Add YA with a word value. + int ADDW(uint24_t addr); + //! @brief Subtract YA with a word value. + int SUBW(uint24_t addr); + //! @brief Compare YA with a word value. + int CMPW(uint24_t addr); public: explicit APU(std::shared_ptr &map); APU(const APU &) = default; diff --git a/sources/APU/Instructions/16bitArithmetic.cpp b/sources/APU/Instructions/16bitArithmetic.cpp new file mode 100644 index 0000000..9ca5c61 --- /dev/null +++ b/sources/APU/Instructions/16bitArithmetic.cpp @@ -0,0 +1,77 @@ +// +// Created by Melefo on 25/02/2020. +// + +#include "../APU.hpp" + +namespace ComSquare::APU +{ + int APU::INCW(uint24_t addr) + { + uint24_t addr2 = addr + 1 + (this->_internalRegisters.p ? 0x0100 : 0); + uint16_t value = ((this->_internalRead(addr2) << 8u) | this->_internalRead(addr)) + 1u; + + this->_internalWrite(addr, value); + this->_internalWrite(addr2, value >> 8u); + this->_internalRegisters.n = value & 0x8000u; + this->_internalRegisters.z = !value; + return 6; + } + + int APU::DECW(uint24_t addr) + { + uint24_t addr2 = addr + 1 + (this->_internalRegisters.p ? 0x0100 : 0); + uint16_t value = ((this->_internalRead(addr2) << 8u) | this->_internalRead(addr)) - 1u; + + this->_internalWrite(addr, value); + this->_internalWrite(addr2, value >> 8u); + this->_internalRegisters.n = value & 0x8000u; + this->_internalRegisters.z = !value; + return 6; + } + + int APU::ADDW(uint24_t addr) + { + uint24_t addr2 = addr + 1 + (this->_internalRegisters.p ? 0x0100 : 0); + uint16_t value = ((this->_internalRead(addr2) << 8u) | this->_internalRead(addr)); + uint24_t result = this->_internalRegisters.ya + value; + + this->_internalRegisters.v = (~(this->_internalRegisters.ya ^ value) & (this->_internalRegisters.ya ^ result)) & 0x8000u; + this->_internalRegisters.c = result > 0xFFFFu; + this->_internalRegisters.h = ((this->_internalRegisters.ya & 0x0FFFu) + (value & 0x0FFFu)) > 0x0FFFu; + this->_internalRegisters.z = !result; + this->_internalRegisters.n = result & 0x8000u; + this->_internalRegisters.ya = result; + return 5; + } + + int APU::SUBW(uint24_t addr) + { + uint24_t addr2 = addr + 1 + (this->_internalRegisters.p ? 0x0100 : 0); + uint16_t value = ((this->_internalRead(addr2) << 8u) | this->_internalRead(addr)); + uint24_t result = this->_internalRegisters.ya - value; + uint16_t tmp = ((this->_internalRegisters.ya & 0x0F00u) - (value & 0x0F00u)) >> 8u; + + if ((this->_internalRegisters.ya & 0x00FFu) < (value & 0x00FFu)) + tmp--; + this->_internalRegisters.v = ((this->_internalRegisters.ya ^ value) & (this->_internalRegisters.ya ^ result)) & 0x8000u; + this->_internalRegisters.c = result <= 0xFFFFu; + this->_internalRegisters.h = tmp <= 0x000F; + this->_internalRegisters.z = !result; + this->_internalRegisters.n = result & 0x8000u; + this->_internalRegisters.ya = result; + return 5; + } + + int APU::CMPW(uint24_t addr) + { + uint24_t addr2 = addr + 1 + (this->_internalRegisters.p ? 0x0100 : 0); + uint16_t value = ((this->_internalRead(addr2) << 8u) | this->_internalRead(addr)); + uint24_t result = this->_internalRegisters.ya - value; + + this->_internalRegisters.z = !result; + this->_internalRegisters.n = result & 0x8000u; + this->_internalRegisters.c = this->_internalRegisters.ya >= value; + return 4; + } +} \ No newline at end of file diff --git a/tests/APU/testAPUInstructions.cpp b/tests/APU/testAPUInstructions.cpp index bdf0c8a..23dd1a8 100644 --- a/tests/APU/testAPUInstructions.cpp +++ b/tests/APU/testAPUInstructions.cpp @@ -768,4 +768,92 @@ Test(MultiplicationDivision, DIV) result = apu->DIV(); cr_assert_eq(apu->_internalRegisters.y, 147); cr_assert_eq(apu->_internalRegisters.a, 211); +} + +////////////////////////////////// +// // +// (XVI)16-bit Arithmetic tests // +// // +////////////////////////////////// + +Test(XVIbitArithmetic, INCW) +{ + auto apu = Init().second.apu; + int result = 0; + + apu->_internalWrite(apu->_internalRegisters.pc, 0x55); + apu->_internalWrite(0x55, 0xFF); + apu->_internalWrite(0x55 + 1, 0x22); + result = apu->INCW(apu->_getDirectAddr()); + cr_assert_eq(result, 6); + cr_assert_eq(apu->_internalRead(0x55), 0x00); + cr_assert_eq(apu->_internalRead(0x55 + 1), 0x23); +} + +Test(XVIbitArithmetic, DECW) +{ + auto apu = Init().second.apu; + int result = 0; + + apu->_internalWrite(apu->_internalRegisters.pc, 0x55); + apu->_internalWrite(0x55, 0x00); + apu->_internalWrite(0x55 + 1, 0x23); + result = apu->DECW(apu->_getDirectAddr()); + cr_assert_eq(result, 6); + cr_assert_eq(apu->_internalRead(0x55), 0xFF); + cr_assert_eq(apu->_internalRead(0x55 + 1), 0x22); +} + +Test(XVIbitArithmetic, ADDW) +{ + auto apu = Init().second.apu; + int result = 0; + + apu->_internalRegisters.ya = 0x4321; + apu->_internalWrite(apu->_internalRegisters.pc, 0x55); + apu->_internalWrite(0x55, 0x11); + apu->_internalWrite(0x55 + 1, 0x22); + result = apu->ADDW(apu->_getDirectAddr()); + cr_assert_eq(result, 5); + cr_assert_eq(apu->_internalRegisters.ya, 0x6532); + cr_assert_eq(apu->_internalRegisters.n, false); + cr_assert_eq(apu->_internalRegisters.v, false); + cr_assert_eq(apu->_internalRegisters.h, false); + cr_assert_eq(apu->_internalRegisters.z, false); + cr_assert_eq(apu->_internalRegisters.c, false); +} + +Test(XVIbitArithmetic, SUBW) +{ + auto apu = Init().second.apu; + int result = 0; + + apu->_internalRegisters.ya = 0x4321; + apu->_internalWrite(apu->_internalRegisters.pc, 0x55); + apu->_internalWrite(0x55, 0x11); + apu->_internalWrite(0x55 + 1, 0x22); + result = apu->SUBW(apu->_getDirectAddr()); + cr_assert_eq(result, 5); + cr_assert_eq(apu->_internalRegisters.ya, 0x2110); + cr_assert_eq(apu->_internalRegisters.n, false); + cr_assert_eq(apu->_internalRegisters.v, false); + cr_assert_eq(apu->_internalRegisters.h, true); + cr_assert_eq(apu->_internalRegisters.z, false); + cr_assert_eq(apu->_internalRegisters.c, true); +} + +Test(XVIbitArithmetic, CMPW) +{ + auto apu = Init().second.apu; + int result = 0; + + apu->_internalRegisters.ya = 0x2211; + apu->_internalWrite(apu->_internalRegisters.pc, 0x55); + apu->_internalWrite(0x55, 0x11); + apu->_internalWrite(0x55 + 1, 0x22); + result = apu->CMPW(apu->_getDirectAddr()); + cr_assert_eq(result, 4); + cr_assert_eq(apu->_internalRegisters.z, true); + cr_assert_eq(apu->_internalRegisters.c, true); + cr_assert_eq(apu->_internalRegisters.n, false); } \ No newline at end of file