From e9a7b9e8d7070a7206d223a6fe1bbea992b3b14d Mon Sep 17 00:00:00 2001 From: Melefo <42809472+Melefo@users.noreply.github.com> Date: Mon, 24 Feb 2020 19:06:35 +0100 Subject: [PATCH] Finishing Program Flow Operations --- CMakeLists.txt | 4 +- sources/APU/APU.cpp | 73 +++++++++++-------- sources/APU/APU.hpp | 32 ++++++--- sources/APU/Instructions/ProgramFlow.cpp | 59 +++++++++++++++- sources/APU/Instructions/Subroutine.cpp | 2 +- sources/APU/Operand.cpp | 59 ++++++++++++++++ tests/APU/testAPUInstructions.cpp | 90 ++++++++++++++++++++++++ 7 files changed, 278 insertions(+), 41 deletions(-) create mode 100644 sources/APU/Operand.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 51c3181..1eb91ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,7 +74,7 @@ add_executable(unit_tests sources/APU/Instructions/Stack.cpp sources/APU/Instructions/Subroutine.cpp sources/APU/Instructions/ProgramFlow.cpp -) + sources/APU/Operand.cpp) # include criterion & coverage target_link_libraries(unit_tests criterion -lgcov) @@ -160,7 +160,7 @@ add_executable(ComSquare sources/APU/Instructions/Stack.cpp sources/APU/Instructions/Subroutine.cpp sources/APU/Instructions/ProgramFlow.cpp -) + sources/APU/Operand.cpp) target_compile_definitions(ComSquare PUBLIC DEBUGGER_ENABLED) diff --git a/sources/APU/APU.cpp b/sources/APU/APU.cpp index 19a96d9..2658bf5 100644 --- a/sources/APU/APU.cpp +++ b/sources/APU/APU.cpp @@ -166,6 +166,8 @@ namespace ComSquare::APU return this->TCALL(0); case 0x02: return this->SET1(this->_getDirectAddr(), 0); + case 0x03: + return this->BBS(this->_getDirectAddr(), 0); case 0x0A: return this->OR1(this->_getAbsoluteBit()); case 0x0D: @@ -180,16 +182,24 @@ namespace ComSquare::APU return this->TCALL(1); case 0x12: return this->CLR1(this->_getDirectAddr(), 0); + case 0x13: + return this->BBC(this->_getDirectAddr(), 0); + case 0x1F: + return this->JMP(this->_getAbsoluteAddrByX(), true); case 0x20: return this->CLRP(); case 0x21: return this->TCALL(2); case 0x22: return this->SET1(this->_getDirectAddr(), 1); + case 0x23: + return this->BBS(this->_getDirectAddr(), 1); case 0x2A: return this->OR1(this->_getAbsoluteBit(), true); case 0x2D: return this->PUSH(this->_internalRegisters.a); + case 0x2E: + return this->CBNE(this->_getDirectValue()); case 0x2F: return this->BRA(); case 0x30: @@ -198,6 +208,8 @@ namespace ComSquare::APU return this->TCALL(3); case 0x32: return this->CLR1(this->_getDirectAddr(), 1); + case 0x33: + return this->BBC(this->_getDirectAddr(), 1); case 0x3F: return this->CALL(this->_getAbsoluteAddr()); case 0x40: @@ -206,6 +218,8 @@ namespace ComSquare::APU return this->TCALL(4); case 0x42: return this->SET1(this->_getDirectAddr(), 2); + case 0x43: + return this->BBS(this->_getDirectAddr(), 2); case 0x4A: return this->AND1(this->_getAbsoluteBit()); case 0x4D: @@ -220,16 +234,24 @@ namespace ComSquare::APU return this->TCALL(5); case 0x52: return this->CLR1(this->_getDirectAddr(), 2); + case 0x53: + return this->BBC(this->_getDirectAddr(), 2); + case 0x5F: + return this->JMP(this->_getAbsoluteAddr()); case 0x60: return this->CLRC(); case 0x61: return this->TCALL(6); case 0x62: return this->SET1(this->_getDirectAddr(), 3); + case 0x63: + return this->BBS(this->_getDirectAddr(), 3); case 0x6A: return this->AND1(this->_getAbsoluteBit(), true); case 0x6D: return this->PUSH(this->_internalRegisters.y); + case 0x6E: + return this->DBNZ(true); case 0x6F: return this->RET(); case 0x70: @@ -238,6 +260,8 @@ namespace ComSquare::APU return this->TCALL(7); case 0x72: return this->CLR1(this->_getDirectAddr(), 3); + case 0x73: + return this->BBC(this->_getDirectAddr(), 3); case 0x7F: return this->RETI(); case 0x80: @@ -246,6 +270,8 @@ namespace ComSquare::APU return this->TCALL(8); case 0x82: return this->SET1(this->_getDirectAddr(), 4); + case 0x83: + return this->BBS(this->_getDirectAddr(), 4); case 0x8A: return this->EOR1(this->_getAbsoluteBit()); case 0x8E: @@ -256,12 +282,16 @@ namespace ComSquare::APU return this->TCALL(9); case 0x92: return this->CLR1(this->_getDirectAddr(), 4); + case 0x93: + return this->BBC(this->_getDirectAddr(), 4); case 0xA0: return this->EI(); case 0xA1: return this->TCALL(10); case 0xA2: return this->SET1(this->_getDirectAddr(), 5); + case 0xA3: + return this->BBS(this->_getDirectAddr(), 5); case 0xAA: return this->MOV1(this->_getAbsoluteBit(), true); case 0xAE: @@ -272,12 +302,16 @@ namespace ComSquare::APU return this->TCALL(11); case 0xB2: return this->CLR1(this->_getDirectAddr(), 5); + case 0xB3: + return this->BBC(this->_getDirectAddr(), 5); case 0xC0: return this->DI(); case 0xC1: return this->TCALL(12); case 0xC2: return this->SET1(this->_getDirectAddr(), 6); + case 0xC3: + return this->BBS(this->_getDirectAddr(), 6); case 0xCA: return this->MOV1(this->_getAbsoluteBit()); case 0xCE: @@ -288,10 +322,16 @@ namespace ComSquare::APU return this->TCALL(13); case 0xD2: return this->CLR1(this->_getDirectAddr(), 6); + case 0xD3: + return this->BBC(this->_getDirectAddr(), 6); + case 0xDE: + return this->CBNE(this->_getDirectAddrByX(), true); case 0xE1: return this->TCALL(14); case 0xE2: return this->SET1(this->_getDirectAddr(), 7); + case 0xE3: + return this->BBS(this->_getDirectAddr(), 7); case 0xEE: return this->POP(this->_internalRegisters.y); case 0xF0: @@ -300,12 +340,16 @@ namespace ComSquare::APU return this->TCALL(15); case 0xF2: return this->CLR1(this->_getDirectAddr(), 7); + case 0xF3: + return this->BBC(this->_getDirectAddr(), 7); case 0xEA: return this->NOT1(this->_getAbsoluteBit()); case 0xED: return this->NOTC(); case 0xEF: return this->SLEEP(); + case 0xFE: + return this->DBNZ(); case 0xFF: return this->STOP(); default: @@ -324,35 +368,6 @@ namespace ComSquare::APU this->_paddingCycles = total - cycles; } - uint24_t APU::_getDirectAddr() - { - uint24_t addr = this->_internalRead(this->_internalRegisters.pc++); - - if (this->_internalRegisters.p) - addr += 0x100; - return addr; - } - - uint24_t APU::_getAbsoluteAddr() - { - uint24_t addr1 = this->_internalRead(this->_internalRegisters.pc++); - uint24_t addr2 = this->_internalRead(this->_internalRegisters.pc++); - - return (addr2 << 8u) | addr1; - } - - std::pair APU::_getAbsoluteBit() - { - uint24_t addr1 = this->_internalRead(this->_internalRegisters.pc++); - uint24_t addr2 = this->_internalRead(this->_internalRegisters.pc++); - - uint24_t operandA = (addr2 << 8u) | addr1; - uint24_t operandB = operandA >> 13u; - - operandA = operandA & 0x1FFFu; - return std::make_pair(operandA, operandB); - } - MemoryMap::MemoryMap() : Page0(0x00F0), Page1(0x0100), diff --git a/sources/APU/APU.hpp b/sources/APU/APU.hpp index 5006316..1088166 100644 --- a/sources/APU/APU.hpp +++ b/sources/APU/APU.hpp @@ -161,10 +161,16 @@ namespace ComSquare::APU //! @brief Keep the number of excess cycles executed to pad the next update unsigned int _paddingCycles = 0; + //! @brief Get value of the Pointer Counter + uint8_t _getDirectValue(); //! @brief Get direct page offset uint24_t _getDirectAddr(); + //! @brief Get direct page offset and add to it the X Index Flag + uint24_t _getDirectAddrByX(); //! @brief Get absolute direct page offset uint24_t _getAbsoluteAddr(); + //! @brief Get absolute direct page offset nd add to it the X Index Flag + uint24_t _getAbsoluteAddrByX(); //! @brief Get absolute offset and separate its bits std::pair _getAbsoluteBit(); @@ -235,22 +241,32 @@ namespace ComSquare::APU //! @brief Branch Always, go to the specified location from the next instruction. int BRA(); - //! @brief Branch if Zero Flag is set + //! @brief Branch if Zero Flag is set. int BEQ(); - //! @brief Branch if Zero Flag is clear + //! @brief Branch if Zero Flag is clear. int BNE(); - //! @brief Branch if Carry Flag is set + //! @brief Branch if Carry Flag is set. int BCS(); - //! @brief Branch if Carry Flag is clear + //! @brief Branch if Carry Flag is clear. int BCC(); - //! @brief Branch if Overflow Flag is set + //! @brief Branch if Overflow Flag is set. int BVS(); - //! @brief Branch if Overflow Flag is set + //! @brief Branch if Overflow Flag is set. int BVC(); - //! @brief Branch if Negative Flag is set + //! @brief Branch if Negative Flag is set. int BMI(); - //! @brief Branch if Negative Flag is clear + //! @brief Branch if Negative Flag is clear. int BPL(); + //! @brief Branch if the specified is set in the address, go to the specified location from the next instruction. + int BBS(uint24_t addr, uint8_t bit); + //! @brief Branch if the specified is clear in the address, go to the specified location from the next instruction. + int BBC(uint24_t addr, uint8_t bit); + //! @brief Branch if the value at the specified address is not equal to the Accumulator Flag. + int CBNE(uint24_t addr, bool by_x = false); + //! @brief Decrement a value then branch to the specified location if the value is not zero. + int DBNZ(bool direct_addr = false); + //! @brief Jump to the specified location. + int JMP(uint24_t addr, bool by_x = false); public: explicit APU(std::shared_ptr &map); APU(const APU &) = default; diff --git a/sources/APU/Instructions/ProgramFlow.cpp b/sources/APU/Instructions/ProgramFlow.cpp index bb7cc65..3098d32 100644 --- a/sources/APU/Instructions/ProgramFlow.cpp +++ b/sources/APU/Instructions/ProgramFlow.cpp @@ -8,7 +8,7 @@ namespace ComSquare::APU { int APU::BRA() { - int8_t offset = this->_internalRead(this->_internalRegisters.pc++); + int8_t offset = this->_getDirectValue(); this->_internalRegisters.pc += offset; return 4; @@ -77,4 +77,61 @@ namespace ComSquare::APU this->BRA(); return 4; } + + int APU::BBS(uint24_t addr, uint8_t bit) + { + uint8_t data = this->_internalRead(addr); + + if (!(data & (1u << bit))) + return 5; + this->BRA(); + return 7; + } + + int APU::BBC(uint24_t addr, uint8_t bit) + { + uint8_t data = this->_internalRead(addr); + + if (data & (1u << bit)) + return 5; + this->BRA(); + return 7; + } + + int APU::CBNE(uint24_t addr, bool by_x) + { + uint8_t data = this->_internalRead(addr); + + if (this->_internalRegisters.a == data) + return 5 + by_x; + this->BRA(); + return 7 + by_x; + } + + int APU::DBNZ(bool direct_addr) + { + uint8_t data; + + if (direct_addr) { + uint24_t addr = this->_getDirectAddr(); + + data = this->_internalRead(addr); + this->_internalWrite(addr, --data); + } + else + data = --this->_internalRegisters.y; + if (!data) + return 4 + direct_addr; + this->BRA(); + return 6 + direct_addr; + } + + int APU::JMP(uint24_t addr, bool by_x) + { + this->_internalRegisters.pc = addr; + if (by_x) + return 6; + else + return 3; + } } \ No newline at end of file diff --git a/sources/APU/Instructions/Subroutine.cpp b/sources/APU/Instructions/Subroutine.cpp index 34eb031..61f84cc 100644 --- a/sources/APU/Instructions/Subroutine.cpp +++ b/sources/APU/Instructions/Subroutine.cpp @@ -17,7 +17,7 @@ namespace ComSquare::APU int APU::PCALL() { - this->CALL(0xFF00u + this->_internalRead(this->_internalRegisters.pc++)); + this->CALL(0xFF00u + this->_getDirectValue()); return 6; } diff --git a/sources/APU/Operand.cpp b/sources/APU/Operand.cpp new file mode 100644 index 0000000..1819a8d --- /dev/null +++ b/sources/APU/Operand.cpp @@ -0,0 +1,59 @@ +// +// Created by Melefo on 24/02/2020. +// + +#include "../Models/Int24.hpp" +#include "APU.hpp" + +namespace ComSquare::APU +{ + uint8_t APU::_getDirectValue() + { + return this->_internalRead(this->_internalRegisters.pc++); + } + + uint24_t APU::_getDirectAddr() + { + uint24_t addr = this->_getDirectValue(); + + if (this->_internalRegisters.p) + addr += 0x100; + return addr; + } + + uint24_t APU::_getDirectAddrByX() + { + uint24_t addr = this->_getDirectAddr(); + + addr += this->_internalRegisters.x; + return addr; + } + + uint24_t APU::_getAbsoluteAddr() + { + uint24_t addr1 = this->_getDirectValue(); + uint24_t addr2 = this->_getDirectValue(); + + return (addr2 << 8u) | addr1; + } + + uint24_t APU::_getAbsoluteAddrByX() + { + uint24_t addr1 = this->_getDirectValue() + this->_internalRegisters.x; + uint24_t addr2 = this->_getDirectValue() + this->_internalRegisters.x++; + + return (addr2 << 8u) | addr1; + } + + std::pair APU::_getAbsoluteBit() + { + uint24_t addr1 = _getDirectValue(); + uint24_t addr2 = this->_getDirectValue(); + + uint24_t operandA = (addr2 << 8u) | addr1; + uint24_t operandB = operandA >> 13u; + + operandA = operandA & 0x1FFFu; + return std::make_pair(operandA, operandB); + } +} \ No newline at end of file diff --git a/tests/APU/testAPUInstructions.cpp b/tests/APU/testAPUInstructions.cpp index cf3b5c4..2eb273a 100644 --- a/tests/APU/testAPUInstructions.cpp +++ b/tests/APU/testAPUInstructions.cpp @@ -606,4 +606,94 @@ Test(ProgramFlow, BPL) result = apu->BPL(); cr_assert_eq(result, 4); cr_assert_eq(apu->_internalRead(apu->_internalRegisters.pc), 101); +} + +Test(ProgramFlow, BBS) +{ + auto apu = Init().second.apu; + int result = 0; + + apu->_internalRegisters.pc = 0; + apu->_internalWrite(apu->_internalRegisters.pc, 23); + apu->_internalWrite(23, 0); + result = apu->BBS(apu->_getDirectAddr(), 2); + cr_assert_eq(result, 5); + apu->_internalRegisters.pc = 0; + apu->_internalWrite(apu->_internalRegisters.pc + 1, 10); + apu->_internalWrite(23, 255); + result = apu->BBS(apu->_getDirectAddr(), 2); + cr_assert_eq(result, 7); + cr_assert_eq(apu->_internalRegisters.pc, 12); +} + +Test(ProgramFlow, BBC) +{ + auto apu = Init().second.apu; + int result = 0; + + apu->_internalRegisters.pc = 0; + apu->_internalWrite(apu->_internalRegisters.pc, 10); + apu->_internalWrite( 10, 255); + apu->_internalWrite(apu->_internalRegisters.pc + 1, 23); + result = apu->BBC(apu->_getDirectAddr(), 2); + cr_assert_eq(result, 5); + apu->_internalRegisters.pc = 0; + apu->_internalWrite( 10, 0); + result = apu->BBC(apu->_getDirectAddr(), 2); + cr_assert_eq(result, 7); + cr_assert_eq(apu->_internalRegisters.pc, 25); +} + +Test(ProgramFlow, CBNE) +{ + auto apu = Init().second.apu; + int result = 0; + + apu->_internalRegisters.pc = 0; + apu->_internalRegisters.a = 4; + apu->_internalWrite(apu->_internalRegisters.pc, 10); + apu->_internalWrite( 10, 4); + apu->_internalWrite(apu->_internalRegisters.pc + 1, 23); + result = apu->CBNE(apu->_getDirectAddr()); + cr_assert_eq(result, 5); + apu->_internalRegisters.pc = 0; + apu->_internalWrite( 10, 0); + result = apu->CBNE(apu->_getDirectAddrByX(), true); + cr_assert_eq(result, 8); + cr_assert_eq(apu->_internalRegisters.pc, 25); +} + +Test(ProgramFlow, DBNZ) +{ + auto apu = Init().second.apu; + int result = 0; + + apu->_internalRegisters.pc = 0; + apu->_internalRegisters.y = 1; + result = apu->DBNZ(); + cr_assert_eq(result, 4); + apu->_internalWrite(apu->_internalRegisters.pc, 10); + apu->_internalWrite(apu->_internalRegisters.pc + 1, 23); + apu->_internalWrite( 10, 0); + result = apu->DBNZ(true); + cr_assert_eq(result, 7); + cr_assert_eq(apu->_internalRegisters.pc, 25); +} + +Test(ProgramFlow, JMP) +{ + auto apu = Init().second.apu; + int result = 0; + + apu->_internalRegisters.pc = 0x32; + apu->_internalWrite(0x32, 0b00001111); + apu->_internalWrite(0x33, 0b11110000); + result = apu->JMP(apu->_getAbsoluteAddr()); + cr_assert_eq(result, 3); + cr_assert_eq(apu->_internalRegisters.pc, 61455); + apu->_internalRegisters.pc = 0x32; + apu->_internalRegisters.x = 0b000000001; + result = apu->JMP(apu->_getAbsoluteAddrByX(), true); + cr_assert_eq(result, 6); + cr_assert_eq(apu->_internalRegisters.pc, 61712); } \ No newline at end of file