<commit_before>#include <gtest/gtest.h>
#include <errors.h>
#include <basecpu.h>
#include "dummy_instruction.h"
#include "mock_instruction.h"
#include "mock_system.h"

#define BASECPU_TEST_CLASS BaseCPUTest
#define GENERICCPU_TEST_CLASS GenericCPUTest

#define GENERICCPU_BAD_OPERAND_INT 1
#define GENERICCPU_BAD_INSTRUCTION_INT 2

class BaseTestCPU : public BaseCPU {
public:
    BaseTestCPU(int maxTicks, const System &sys) :
        maxTicks(maxTicks), resets(0), startups(0), ticks(0), BaseCPU(sys) { }

    virtual bool startup() {
        startups++;
        return BaseCPU::startup();
    }

    void reset() {
        resets++;
    }

    void tick() {
        assert(isRunning());
        ticks++;
        if(ticks>= maxTicks) {
            stop();
        }
    }

    int doReadMemory(SizeType offset, SizeType len, void *data) const {
        return BaseCPU::readMemory(offset, len, data);
    }

    int doWriteMemory(SizeType offset, SizeType len, const void *data) const {
        return BaseCPU::writeMemory(offset, len, data);
    }

    int doReadPort(PortType port, SizeType len, void *data) const {
        return BaseCPU::readPort(port, len, data);
    }

    int doWritePort(PortType port, SizeType len, const void *data) const {
        return BaseCPU::writePort(port, len, data);
    }

    int maxTicks;
    int resets;
    int startups;
    int ticks;
};

class TestInterruptable : public Interruptable {
public:
    virtual void serviceInterrupt(Interrupt interrupt) {
        servicedInterrupts.push_back(interrupt);
    }

    bool _nextI() { return serviceNextInterrupt(); }

    std::vector<Interrupt> servicedInterrupts;
};

class GenericTestCPU : public GenericCPU, public TestInterruptable {
public:
    GenericTestCPU(const System &sys, const InstructionSet &set) :
        GenericCPU(sys, set, GENERICCPU_BAD_INSTRUCTION_INT, GENERICCPU_BAD_OPERAND_INT),
        ip(nullptr), interruptable(true), resets(0) { }

    void *instructionPointer() { return ip; }
    bool interruptsEnabled() { return interruptable; }

    void reset() {
        resets++;
    }

    SizeType interruptQueueLength() {
        return GenericCPU::interruptQueueLength();
    }

    bool serviceNextInterrupt() {
        return GenericCPU::serviceNextInterrupt();
    }

    void serviceInterrupt(Interrupt interrupt) {
        TestInterruptable::serviceInterrupt(interrupt);
    }

    bool readNextByte(ByteString &buffer, const void * const base) {
        return GenericCPU::readNextByte(buffer, base);
    }

    int readInstruction(const void * const instructionBase, const Instruction **out) {
        return GenericCPU::readInstruction(instructionBase, out);
    }

    void loadNextInstruction(const Instruction **instruction, ByteString &operands) {
        return GenericCPU::loadNextInstruction(instruction, operands);
    }

    void *ip;
    bool interruptable;
    int resets;
};

int buildDummyInstruction(const ByteString &opcode, const Instruction **out) {
    // FIXME: Memory leak
    *out = new DummyInstruction(opcode, 2);
    return ERR_SUCCESS;
}

const int TEST_TICK_COUNT = 3;

class BASECPU_TEST_CLASS : public testing::Test {
protected:
    BASECPU_TEST_CLASS() : cpu(TEST_TICK_COUNT, dummySystem) { }
    BaseTestCPU cpu;
    TestInterruptable server;
    StrictMockSystem dummySystem;
};

TEST_F(BASECPU_TEST_CLASS, TestRunLoop) {
    ASSERT_FALSE(cpu.isRunning()) << "Incorrect initial CPU running state";
    ASSERT_EQ(cpu.ticks, 0) << "Incorrect initial CPU tick count";
    ASSERT_EQ(cpu.resets, 0) << "Incorrect initial CPU reset count";
    bool started = cpu.startup();
    ASSERT_TRUE(started) << "CPU failed to indicate startup";
    ASSERT_FALSE(cpu.isRunning()) << "Incorrect final CPU running state";
    ASSERT_EQ(cpu.ticks, TEST_TICK_COUNT) << "Incorrect final CPU tick count";
    ASSERT_EQ(cpu.resets, 1) << "Incorrect final CPU reset count";
    ASSERT_EQ(cpu.startups, 1) << "Incorrect final CPU startup count";
}

TEST_F(BASECPU_TEST_CLASS, TestStopStart) {
    ASSERT_FALSE(cpu.isRunning()) << "Incorrect initial CPU running state";
    ASSERT_EQ(cpu.ticks, 0) << "Incorrect initial CPU tick count";
    ASSERT_EQ(cpu.resets, 0) << "Incorrect initial CPU reset count";
    bool started = cpu.startup();
    ASSERT_TRUE(started) << "CPU failed to indicate startup";
    ASSERT_FALSE(cpu.isRunning()) << "Incorrect final CPU running state";
    ASSERT_EQ(cpu.ticks, TEST_TICK_COUNT) << "Incorrect final CPU tick count";
    ASSERT_EQ(cpu.resets, 1) << "Incorrect final CPU reset count";
    ASSERT_EQ(cpu.startups, 1) << "Incorrect final CPU startup count";

    started = cpu.startup();
    ASSERT_TRUE(started) << "CPU failed to indicate startup";
    ASSERT_FALSE(cpu.isRunning()) << "Incorrect final CPU running state";
    ASSERT_EQ(cpu.ticks, TEST_TICK_COUNT + 1) << "Incorrect final CPU tick count";
    ASSERT_EQ(cpu.resets, 2) << "Incorrect final CPU reset count";
    ASSERT_EQ(cpu.startups, 2) << "Incorrect final CPU startup count";
}

TEST_F(BASECPU_TEST_CLASS, TestInterruptService) {
    ASSERT_FALSE(server._nextI()) << "Incorrect service of empty queue";
    server.signalInterrupt(3);
    server.signalInterrupt(5);
    ASSERT_TRUE(server._nextI()) << "Failed to service queue";
    ASSERT_EQ(server.servicedInterrupts.size(), 1) << "Incorrect number of interrupts serviced";
    ASSERT_EQ(server.servicedInterrupts.at(0), 3) << "Incorrect interrupt serviced";
    ASSERT_TRUE(server._nextI()) << "Failed to service queue";
    ASSERT_EQ(server.servicedInterrupts.size(), 2) << "Incorrect number of interrupts serviced";
    ASSERT_EQ(server.servicedInterrupts.at(1), 5) << "Incorrect interrupt serviced";
    ASSERT_FALSE(server._nextI()) << "Incorrect service of empty queue";
}

TEST_F(BASECPU_TEST_CLASS, TestSystemPassThru) {
    // These constants aren't important.  I just wanted to make sure calls aren't crossed
    EXPECT_CALL(dummySystem, readMemory(1, 2, 0)).WillOnce(testing::Return(ERR_SUCCESS));
    EXPECT_CALL(dummySystem, writeMemory(7, 8, 0)).WillOnce(testing::Return(ERR_SUCCESS));
    EXPECT_CALL(dummySystem, readPort(5, 6, 0)).WillOnce(testing::Return(ERR_SUCCESS));
    EXPECT_CALL(dummySystem, writePort(3, 4, 0)).WillOnce(testing::Return(ERR_SUCCESS));

    cpu.doReadMemory(1, 2, 0);
    cpu.doWriteMemory(7, 8, 0);
    cpu.doReadPort(5, 6, 0);
    cpu.doWritePort(3, 4, 0);
}

TEST_F(BASECPU_TEST_CLASS, TestStartup) {
    ASSERT_FALSE(cpu.isRunning()) << "Incorrect initial CPU running state";
    ASSERT_EQ(cpu.ticks, 0) << "Incorrect initial CPU tick count";
    ASSERT_EQ(cpu.resets, 0) << "Incorrect initial CPU reset count";
    bool started = cpu.startup();
    ASSERT_TRUE(started) << "CPU failed to indicate startup";
    ASSERT_FALSE(cpu.isRunning()) << "Incorrect final CPU running state";
    ASSERT_EQ(cpu.ticks, TEST_TICK_COUNT) << "Incorrect final CPU tick count";
    ASSERT_EQ(cpu.resets, 1) << "Incorrect final CPU reset count";
    ASSERT_EQ(cpu.startups, 1) << "Incorrect final CPU startup count";
}

class GENERICCPU_TEST_CLASS : public testing::Test {
protected:
    GENERICCPU_TEST_CLASS() : cpu(dummySystem, dummySet) { }
    GenericTestCPU cpu;
    StrictMockSystem dummySystem;
    StrictMockInstructionSet dummySet;
};

TEST_F(GENERICCPU_TEST_CLASS, readNextByteSuccess) {
    ByteString buffer;
    const void * const baseAddr = (void*)0x1234;
    SizeType bytesToRead = 8;

    EXPECT_CALL(dummySystem, readMemory(::testing::_, 1, ::testing::_))
        .Times(bytesToRead).WillRepeatedly(testing::Return(ERR_SUCCESS));

    while(bytesToRead-- && cpu.readNextByte(buffer, baseAddr)) { }
}

TEST_F(GENERICCPU_TEST_CLASS, readNextByteFailure) {
    ByteString buffer;
    const void * const baseAddr = (void*)0x1234;
    SizeType bytesToRead = 8;
    SizeType bytesToSucceed = 5;

    EXPECT_CALL(dummySystem, readMemory(::testing::_, 1, ::testing::_))
        .WillOnce(testing::Return(ERR_BADRANGE))
        .RetiresOnSaturation();
    EXPECT_CALL(dummySystem, readMemory(::testing::_, 1, ::testing::_))
        .Times(bytesToSucceed).WillRepeatedly(testing::Return(ERR_SUCCESS))
        .RetiresOnSaturation();

    while(bytesToRead-- && cpu.readNextByte(buffer, baseAddr)) { }
}

TEST_F(GENERICCPU_TEST_CLASS, readInstructionSuccess) {
    const void * const baseAddr = (void*)0x1234;
    const Instruction *instr;

    EXPECT_CALL(dummySystem, readMemory(::testing::_, 1, ::testing::_))
        .WillOnce(testing::Return(ERR_SUCCESS)).WillOnce(testing::Return(ERR_SUCCESS));

    EXPECT_CALL(dummySet, decode(::testing::_, &instr))
        .WillOnce(testing::Return(ERR_INCOMPLETE)).WillOnce(testing::Return(ERR_SUCCESS));

    int instructionRc = cpu.readInstruction(baseAddr, &instr);
    ASSERT_EQ(instructionRc, ERR_SUCCESS) << "Failed to decode instruction";
}

TEST_F(GENERICCPU_TEST_CLASS, readInstructionMemoryError) {
    const void * const baseAddr = (void*)0x1234;
    const Instruction *instr;

    EXPECT_CALL(dummySystem, readMemory(::testing::_, 1, ::testing::_))
        .WillOnce(testing::Return(ERR_SUCCESS)).WillOnce(testing::Return(ERR_BADRANGE));

    EXPECT_CALL(dummySet, decode(::testing::_, &instr))
        .WillOnce(testing::Return(ERR_INCOMPLETE));

    int instructionRc = cpu.readInstruction(baseAddr, &instr);
    ASSERT_EQ(instructionRc, ERR_CONFLICT) << "Failed to detect memory failure";
}

TEST_F(GENERICCPU_TEST_CLASS, readInstructionInstructionError) {
    const void * const baseAddr = (void*)0x1234;
    const Instruction *instr;

    EXPECT_CALL(dummySystem, readMemory(::testing::_, 1, ::testing::_))
        .Times(2).WillRepeatedly(testing::Return(ERR_SUCCESS));

    EXPECT_CALL(dummySet, decode(::testing::_, &instr))
        .WillOnce(testing::Return(ERR_INCOMPLETE)).WillOnce(testing::Return(ERR_BADRANGE));

    int instructionRc = cpu.readInstruction(baseAddr, &instr);
    ASSERT_EQ(instructionRc, ERR_BADRANGE) << "Failed to detect invalid instruction";
}

TEST_F(GENERICCPU_TEST_CLASS, loadNextInstructionSuccess) {
    const Instruction *instr;
    ByteString operands;

    EXPECT_CALL(dummySystem, readMemory(::testing::_, 1, ::testing::_))
        .WillOnce(testing::Return(ERR_SUCCESS)).WillOnce(testing::Return(ERR_SUCCESS));

    EXPECT_CALL(dummySet, decode(::testing::_, &instr))
        .WillOnce(testing::Return(ERR_INCOMPLETE)).WillOnce((testing::Invoke(buildDummyInstruction)));

    EXPECT_CALL(dummySystem, readMemory(::testing::_, 2, ::testing::_))
        .WillOnce(testing::Return(ERR_SUCCESS));

    bool loadRc = cpu.loadNextInstruction(&instr, operands);

    ASSERT_EQ(cpu.interruptQueueLength(), 0) << "CPU fault loading instruction";
    ASSERT_EQ(loadRc, true) << "Instruction load failure indicated";
}

TEST_F(GENERICCPU_TEST_CLASS, loadNextInstructionMemoryError) {
    const Instruction *instr;
    ByteString operands;

    EXPECT_CALL(dummySystem, readMemory(::testing::_, 1, ::testing::_))
        .WillOnce(testing::Return(ERR_SUCCESS)).WillOnce(testing::Return(ERR_BADRANGE));

    EXPECT_CALL(dummySet, decode(::testing::_, &instr))
        .WillOnce(testing::Return(ERR_INCOMPLETE));

    bool loadRc = cpu.loadNextInstruction(&instr, operands);

    ASSERT_EQ(cpu.interruptQueueLength(), 1) << "Memory error did not trigger CPU fault";
    cpu.serviceNextInterrupt();
    ASSERT_EQ(*cpu.servicedInterrupts.begin(), GENERICCPU_BAD_OPERAND_INT)
        << "Incorrect interrupt trigger by memory fault";

    ASSERT_EQ(loadRc, false) << "Instruction load failure not indicated";
}

TEST_F(GENERICCPU_TEST_CLASS, loadNextInstructionInstructionError) {
    const Instruction *instr;
    ByteString operands;

    EXPECT_CALL(dummySystem, readMemory(::testing::_, 1, ::testing::_))
        .Times(2).WillRepeatedly(testing::Return(ERR_SUCCESS));

    EXPECT_CALL(dummySet, decode(::testing::_, &instr))
        .WillOnce(testing::Return(ERR_INCOMPLETE)).WillOnce(testing::Return(ERR_BADRANGE));

    bool loadRc = cpu.loadNextInstruction(&instr, operands);

    ASSERT_EQ(cpu.interruptQueueLength(), 1) << "Invalid instruction did not trigger CPU fault";
    cpu.serviceNextInterrupt();
    ASSERT_EQ(*cpu.servicedInterrupts.begin(), GENERICCPU_BAD_INSTRUCTION_INT)
        << "Incorrect interrupt trigger by invalid instruction";

    ASSERT_EQ(loadRc, false) << "Instruction load failure not indicated";
}
<commit_msg>Fix return type of wrapper function<commit_after>#include <gtest/gtest.h>
#include <errors.h>
#include <basecpu.h>
#include "dummy_instruction.h"
#include "mock_instruction.h"
#include "mock_system.h"

#define BASECPU_TEST_CLASS BaseCPUTest
#define GENERICCPU_TEST_CLASS GenericCPUTest

#define GENERICCPU_BAD_OPERAND_INT 1
#define GENERICCPU_BAD_INSTRUCTION_INT 2

class BaseTestCPU : public BaseCPU {
public:
    BaseTestCPU(int maxTicks, const System &sys) :
        maxTicks(maxTicks), resets(0), startups(0), ticks(0), BaseCPU(sys) { }

    virtual bool startup() {
        startups++;
        return BaseCPU::startup();
    }

    void reset() {
        resets++;
    }

    void tick() {
        assert(isRunning());
        ticks++;
        if(ticks>= maxTicks) {
            stop();
        }
    }

    int doReadMemory(SizeType offset, SizeType len, void *data) const {
        return BaseCPU::readMemory(offset, len, data);
    }

    int doWriteMemory(SizeType offset, SizeType len, const void *data) const {
        return BaseCPU::writeMemory(offset, len, data);
    }

    int doReadPort(PortType port, SizeType len, void *data) const {
        return BaseCPU::readPort(port, len, data);
    }

    int doWritePort(PortType port, SizeType len, const void *data) const {
        return BaseCPU::writePort(port, len, data);
    }

    int maxTicks;
    int resets;
    int startups;
    int ticks;
};

class TestInterruptable : public Interruptable {
public:
    virtual void serviceInterrupt(Interrupt interrupt) {
        servicedInterrupts.push_back(interrupt);
    }

    bool _nextI() { return serviceNextInterrupt(); }

    std::vector<Interrupt> servicedInterrupts;
};

class GenericTestCPU : public GenericCPU, public TestInterruptable {
public:
    GenericTestCPU(const System &sys, const InstructionSet &set) :
        GenericCPU(sys, set, GENERICCPU_BAD_INSTRUCTION_INT, GENERICCPU_BAD_OPERAND_INT),
        ip(nullptr), interruptable(true), resets(0) { }

    void *instructionPointer() { return ip; }
    bool interruptsEnabled() { return interruptable; }

    void reset() {
        resets++;
    }

    SizeType interruptQueueLength() {
        return GenericCPU::interruptQueueLength();
    }

    bool serviceNextInterrupt() {
        return GenericCPU::serviceNextInterrupt();
    }

    void serviceInterrupt(Interrupt interrupt) {
        TestInterruptable::serviceInterrupt(interrupt);
    }

    bool readNextByte(ByteString &buffer, const void * const base) {
        return GenericCPU::readNextByte(buffer, base);
    }

    int readInstruction(const void * const instructionBase, const Instruction **out) {
        return GenericCPU::readInstruction(instructionBase, out);
    }

    bool loadNextInstruction(const Instruction **instruction, ByteString &operands) {
        return GenericCPU::loadNextInstruction(instruction, operands);
    }

    void *ip;
    bool interruptable;
    int resets;
};

int buildDummyInstruction(const ByteString &opcode, const Instruction **out) {
    // FIXME: Memory leak
    *out = new DummyInstruction(opcode, 2);
    return ERR_SUCCESS;
}

const int TEST_TICK_COUNT = 3;

class BASECPU_TEST_CLASS : public testing::Test {
protected:
    BASECPU_TEST_CLASS() : cpu(TEST_TICK_COUNT, dummySystem) { }
    BaseTestCPU cpu;
    TestInterruptable server;
    StrictMockSystem dummySystem;
};

TEST_F(BASECPU_TEST_CLASS, TestRunLoop) {
    ASSERT_FALSE(cpu.isRunning()) << "Incorrect initial CPU running state";
    ASSERT_EQ(cpu.ticks, 0) << "Incorrect initial CPU tick count";
    ASSERT_EQ(cpu.resets, 0) << "Incorrect initial CPU reset count";
    bool started = cpu.startup();
    ASSERT_TRUE(started) << "CPU failed to indicate startup";
    ASSERT_FALSE(cpu.isRunning()) << "Incorrect final CPU running state";
    ASSERT_EQ(cpu.ticks, TEST_TICK_COUNT) << "Incorrect final CPU tick count";
    ASSERT_EQ(cpu.resets, 1) << "Incorrect final CPU reset count";
    ASSERT_EQ(cpu.startups, 1) << "Incorrect final CPU startup count";
}

TEST_F(BASECPU_TEST_CLASS, TestStopStart) {
    ASSERT_FALSE(cpu.isRunning()) << "Incorrect initial CPU running state";
    ASSERT_EQ(cpu.ticks, 0) << "Incorrect initial CPU tick count";
    ASSERT_EQ(cpu.resets, 0) << "Incorrect initial CPU reset count";
    bool started = cpu.startup();
    ASSERT_TRUE(started) << "CPU failed to indicate startup";
    ASSERT_FALSE(cpu.isRunning()) << "Incorrect final CPU running state";
    ASSERT_EQ(cpu.ticks, TEST_TICK_COUNT) << "Incorrect final CPU tick count";
    ASSERT_EQ(cpu.resets, 1) << "Incorrect final CPU reset count";
    ASSERT_EQ(cpu.startups, 1) << "Incorrect final CPU startup count";

    started = cpu.startup();
    ASSERT_TRUE(started) << "CPU failed to indicate startup";
    ASSERT_FALSE(cpu.isRunning()) << "Incorrect final CPU running state";
    ASSERT_EQ(cpu.ticks, TEST_TICK_COUNT + 1) << "Incorrect final CPU tick count";
    ASSERT_EQ(cpu.resets, 2) << "Incorrect final CPU reset count";
    ASSERT_EQ(cpu.startups, 2) << "Incorrect final CPU startup count";
}

TEST_F(BASECPU_TEST_CLASS, TestInterruptService) {
    ASSERT_FALSE(server._nextI()) << "Incorrect service of empty queue";
    server.signalInterrupt(3);
    server.signalInterrupt(5);
    ASSERT_TRUE(server._nextI()) << "Failed to service queue";
    ASSERT_EQ(server.servicedInterrupts.size(), 1) << "Incorrect number of interrupts serviced";
    ASSERT_EQ(server.servicedInterrupts.at(0), 3) << "Incorrect interrupt serviced";
    ASSERT_TRUE(server._nextI()) << "Failed to service queue";
    ASSERT_EQ(server.servicedInterrupts.size(), 2) << "Incorrect number of interrupts serviced";
    ASSERT_EQ(server.servicedInterrupts.at(1), 5) << "Incorrect interrupt serviced";
    ASSERT_FALSE(server._nextI()) << "Incorrect service of empty queue";
}

TEST_F(BASECPU_TEST_CLASS, TestSystemPassThru) {
    // These constants aren't important.  I just wanted to make sure calls aren't crossed
    EXPECT_CALL(dummySystem, readMemory(1, 2, 0)).WillOnce(testing::Return(ERR_SUCCESS));
    EXPECT_CALL(dummySystem, writeMemory(7, 8, 0)).WillOnce(testing::Return(ERR_SUCCESS));
    EXPECT_CALL(dummySystem, readPort(5, 6, 0)).WillOnce(testing::Return(ERR_SUCCESS));
    EXPECT_CALL(dummySystem, writePort(3, 4, 0)).WillOnce(testing::Return(ERR_SUCCESS));

    cpu.doReadMemory(1, 2, 0);
    cpu.doWriteMemory(7, 8, 0);
    cpu.doReadPort(5, 6, 0);
    cpu.doWritePort(3, 4, 0);
}

TEST_F(BASECPU_TEST_CLASS, TestStartup) {
    ASSERT_FALSE(cpu.isRunning()) << "Incorrect initial CPU running state";
    ASSERT_EQ(cpu.ticks, 0) << "Incorrect initial CPU tick count";
    ASSERT_EQ(cpu.resets, 0) << "Incorrect initial CPU reset count";
    bool started = cpu.startup();
    ASSERT_TRUE(started) << "CPU failed to indicate startup";
    ASSERT_FALSE(cpu.isRunning()) << "Incorrect final CPU running state";
    ASSERT_EQ(cpu.ticks, TEST_TICK_COUNT) << "Incorrect final CPU tick count";
    ASSERT_EQ(cpu.resets, 1) << "Incorrect final CPU reset count";
    ASSERT_EQ(cpu.startups, 1) << "Incorrect final CPU startup count";
}

class GENERICCPU_TEST_CLASS : public testing::Test {
protected:
    GENERICCPU_TEST_CLASS() : cpu(dummySystem, dummySet) { }
    GenericTestCPU cpu;
    StrictMockSystem dummySystem;
    StrictMockInstructionSet dummySet;
};

TEST_F(GENERICCPU_TEST_CLASS, readNextByteSuccess) {
    ByteString buffer;
    const void * const baseAddr = (void*)0x1234;
    SizeType bytesToRead = 8;

    EXPECT_CALL(dummySystem, readMemory(::testing::_, 1, ::testing::_))
        .Times(bytesToRead).WillRepeatedly(testing::Return(ERR_SUCCESS));

    while(bytesToRead-- && cpu.readNextByte(buffer, baseAddr)) { }
}

TEST_F(GENERICCPU_TEST_CLASS, readNextByteFailure) {
    ByteString buffer;
    const void * const baseAddr = (void*)0x1234;
    SizeType bytesToRead = 8;
    SizeType bytesToSucceed = 5;

    EXPECT_CALL(dummySystem, readMemory(::testing::_, 1, ::testing::_))
        .WillOnce(testing::Return(ERR_BADRANGE))
        .RetiresOnSaturation();
    EXPECT_CALL(dummySystem, readMemory(::testing::_, 1, ::testing::_))
        .Times(bytesToSucceed).WillRepeatedly(testing::Return(ERR_SUCCESS))
        .RetiresOnSaturation();

    while(bytesToRead-- && cpu.readNextByte(buffer, baseAddr)) { }
}

TEST_F(GENERICCPU_TEST_CLASS, readInstructionSuccess) {
    const void * const baseAddr = (void*)0x1234;
    const Instruction *instr;

    EXPECT_CALL(dummySystem, readMemory(::testing::_, 1, ::testing::_))
        .WillOnce(testing::Return(ERR_SUCCESS)).WillOnce(testing::Return(ERR_SUCCESS));

    EXPECT_CALL(dummySet, decode(::testing::_, &instr))
        .WillOnce(testing::Return(ERR_INCOMPLETE)).WillOnce(testing::Return(ERR_SUCCESS));

    int instructionRc = cpu.readInstruction(baseAddr, &instr);
    ASSERT_EQ(instructionRc, ERR_SUCCESS) << "Failed to decode instruction";
}

TEST_F(GENERICCPU_TEST_CLASS, readInstructionMemoryError) {
    const void * const baseAddr = (void*)0x1234;
    const Instruction *instr;

    EXPECT_CALL(dummySystem, readMemory(::testing::_, 1, ::testing::_))
        .WillOnce(testing::Return(ERR_SUCCESS)).WillOnce(testing::Return(ERR_BADRANGE));

    EXPECT_CALL(dummySet, decode(::testing::_, &instr))
        .WillOnce(testing::Return(ERR_INCOMPLETE));

    int instructionRc = cpu.readInstruction(baseAddr, &instr);
    ASSERT_EQ(instructionRc, ERR_CONFLICT) << "Failed to detect memory failure";
}

TEST_F(GENERICCPU_TEST_CLASS, readInstructionInstructionError) {
    const void * const baseAddr = (void*)0x1234;
    const Instruction *instr;

    EXPECT_CALL(dummySystem, readMemory(::testing::_, 1, ::testing::_))
        .Times(2).WillRepeatedly(testing::Return(ERR_SUCCESS));

    EXPECT_CALL(dummySet, decode(::testing::_, &instr))
        .WillOnce(testing::Return(ERR_INCOMPLETE)).WillOnce(testing::Return(ERR_BADRANGE));

    int instructionRc = cpu.readInstruction(baseAddr, &instr);
    ASSERT_EQ(instructionRc, ERR_BADRANGE) << "Failed to detect invalid instruction";
}

TEST_F(GENERICCPU_TEST_CLASS, loadNextInstructionSuccess) {
    const Instruction *instr;
    ByteString operands;

    EXPECT_CALL(dummySystem, readMemory(::testing::_, 1, ::testing::_))
        .WillOnce(testing::Return(ERR_SUCCESS)).WillOnce(testing::Return(ERR_SUCCESS));

    EXPECT_CALL(dummySet, decode(::testing::_, &instr))
        .WillOnce(testing::Return(ERR_INCOMPLETE)).WillOnce((testing::Invoke(buildDummyInstruction)));

    EXPECT_CALL(dummySystem, readMemory(::testing::_, 2, ::testing::_))
        .WillOnce(testing::Return(ERR_SUCCESS));

    bool loadRc = cpu.loadNextInstruction(&instr, operands);

    ASSERT_EQ(cpu.interruptQueueLength(), 0) << "CPU fault loading instruction";
    ASSERT_EQ(loadRc, true) << "Instruction load failure indicated";
}

TEST_F(GENERICCPU_TEST_CLASS, loadNextInstructionMemoryError) {
    const Instruction *instr;
    ByteString operands;

    EXPECT_CALL(dummySystem, readMemory(::testing::_, 1, ::testing::_))
        .WillOnce(testing::Return(ERR_SUCCESS)).WillOnce(testing::Return(ERR_BADRANGE));

    EXPECT_CALL(dummySet, decode(::testing::_, &instr))
        .WillOnce(testing::Return(ERR_INCOMPLETE));

    bool loadRc = cpu.loadNextInstruction(&instr, operands);

    ASSERT_EQ(cpu.interruptQueueLength(), 1) << "Memory error did not trigger CPU fault";
    cpu.serviceNextInterrupt();
    ASSERT_EQ(*cpu.servicedInterrupts.begin(), GENERICCPU_BAD_OPERAND_INT)
        << "Incorrect interrupt trigger by memory fault";

    ASSERT_EQ(loadRc, false) << "Instruction load failure not indicated";
}

TEST_F(GENERICCPU_TEST_CLASS, loadNextInstructionInstructionError) {
    const Instruction *instr;
    ByteString operands;

    EXPECT_CALL(dummySystem, readMemory(::testing::_, 1, ::testing::_))
        .Times(2).WillRepeatedly(testing::Return(ERR_SUCCESS));

    EXPECT_CALL(dummySet, decode(::testing::_, &instr))
        .WillOnce(testing::Return(ERR_INCOMPLETE)).WillOnce(testing::Return(ERR_BADRANGE));

    bool loadRc = cpu.loadNextInstruction(&instr, operands);

    ASSERT_EQ(cpu.interruptQueueLength(), 1) << "Invalid instruction did not trigger CPU fault";
    cpu.serviceNextInterrupt();
    ASSERT_EQ(*cpu.servicedInterrupts.begin(), GENERICCPU_BAD_INSTRUCTION_INT)
        << "Incorrect interrupt trigger by invalid instruction";

    ASSERT_EQ(loadRc, false) << "Instruction load failure not indicated";
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: object.hxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: kz $ $Date: 2004-07-30 15:29:34 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): Matthias Huetsch <matthias.huetsch@sun.com>
 *
 *
 ************************************************************************/

#ifndef _STORE_OBJECT_HXX_
#define _STORE_OBJECT_HXX_ "$Revision: 1.3 $"

#ifndef _SAL_TYPES_H_
#include <sal/types.h>
#endif

#ifndef _RTL_REF_HXX_
#include <rtl/ref.hxx>
#endif

#ifndef _OSL_INTERLCK_H_
#include <osl/interlck.h>
#endif

namespace store
{

/*========================================================================
 *
 * IStoreHandle interface.
 *
 *======================================================================*/
class IStoreHandle : public rtl::IReference
{
public:
    /** Replaces dynamic_cast type checking.
     */
    virtual sal_Bool SAL_CALL isKindOf (sal_uInt32 nTypeId) = 0;
};

/** Template helper function as dynamic_cast replacement.
 */
template<class store_handle_type>
store_handle_type * SAL_CALL query (
    IStoreHandle * pHandle, store_handle_type *);

/*========================================================================
 *
 * OStoreObject interface.
 *
 *======================================================================*/
class OStoreObject : public store::IStoreHandle
{
    /** Template function specialization as dynamic_cast replacement.
     */
    friend inline OStoreObject*
    SAL_CALL query (IStoreHandle *pHandle, OStoreObject*);

public:
    /** Construction.
     */
    OStoreObject (void);

    /** Allocation.
     */
    static void* operator new (size_t n);
    static void  operator delete (void *p);

    /** IStoreHandle.
     */
    virtual sal_Bool SAL_CALL isKindOf (sal_uInt32 nTypeId);

    /** IReference.
     */
    virtual oslInterlockedCount SAL_CALL acquire (void);
    virtual oslInterlockedCount SAL_CALL release (void);

protected:
    /** Destruction.
     */
    virtual ~OStoreObject (void);

private:
    /** The IStoreHandle TypeId.
     */
    static const sal_uInt32 m_nTypeId;

    /** Representation.
     */
    oslInterlockedCount m_nRefCount;

    /** Not implemented.
     */
    OStoreObject (const OStoreObject&);
    OStoreObject& operator= (const OStoreObject&);
};

/** Template function specialization as dynamic_cast replacement.
 */
inline OStoreObject*
SAL_CALL query (IStoreHandle *pHandle, OStoreObject*)
{
    if (pHandle && pHandle->isKindOf (OStoreObject::m_nTypeId))
    {
        // Handle is kind of OStoreObject.
        return static_cast<OStoreObject*>(pHandle);
    }
    return 0;
}

/*========================================================================
 *
 * The End.
 *
 *======================================================================*/

} // namespace store

#endif /* !_STORE_OBJECT_HXX_ */

<commit_msg>INTEGRATION: CWS ooo19126 (1.3.20); FILE MERGED 2005/09/05 17:18:56 rt 1.3.20.1: #i54170# Change license header: remove SISSL<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: object.hxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-08 08:39:02 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef _STORE_OBJECT_HXX_
#define _STORE_OBJECT_HXX_ "$Revision: 1.4 $"

#ifndef _SAL_TYPES_H_
#include <sal/types.h>
#endif

#ifndef _RTL_REF_HXX_
#include <rtl/ref.hxx>
#endif

#ifndef _OSL_INTERLCK_H_
#include <osl/interlck.h>
#endif

namespace store
{

/*========================================================================
 *
 * IStoreHandle interface.
 *
 *======================================================================*/
class IStoreHandle : public rtl::IReference
{
public:
    /** Replaces dynamic_cast type checking.
     */
    virtual sal_Bool SAL_CALL isKindOf (sal_uInt32 nTypeId) = 0;
};

/** Template helper function as dynamic_cast replacement.
 */
template<class store_handle_type>
store_handle_type * SAL_CALL query (
    IStoreHandle * pHandle, store_handle_type *);

/*========================================================================
 *
 * OStoreObject interface.
 *
 *======================================================================*/
class OStoreObject : public store::IStoreHandle
{
    /** Template function specialization as dynamic_cast replacement.
     */
    friend inline OStoreObject*
    SAL_CALL query (IStoreHandle *pHandle, OStoreObject*);

public:
    /** Construction.
     */
    OStoreObject (void);

    /** Allocation.
     */
    static void* operator new (size_t n);
    static void  operator delete (void *p);

    /** IStoreHandle.
     */
    virtual sal_Bool SAL_CALL isKindOf (sal_uInt32 nTypeId);

    /** IReference.
     */
    virtual oslInterlockedCount SAL_CALL acquire (void);
    virtual oslInterlockedCount SAL_CALL release (void);

protected:
    /** Destruction.
     */
    virtual ~OStoreObject (void);

private:
    /** The IStoreHandle TypeId.
     */
    static const sal_uInt32 m_nTypeId;

    /** Representation.
     */
    oslInterlockedCount m_nRefCount;

    /** Not implemented.
     */
    OStoreObject (const OStoreObject&);
    OStoreObject& operator= (const OStoreObject&);
};

/** Template function specialization as dynamic_cast replacement.
 */
inline OStoreObject*
SAL_CALL query (IStoreHandle *pHandle, OStoreObject*)
{
    if (pHandle && pHandle->isKindOf (OStoreObject::m_nTypeId))
    {
        // Handle is kind of OStoreObject.
        return static_cast<OStoreObject*>(pHandle);
    }
    return 0;
}

/*========================================================================
 *
 * The End.
 *
 *======================================================================*/

} // namespace store

#endif /* !_STORE_OBJECT_HXX_ */

<|endoftext|>


<commit_before>#include <iostream>
#include "Cluster.h"
#include "utils.h"
#include "numerics.h"
#include "RandomNumberGenerator.h"

#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/io.hpp>

using namespace std;

int main(int argc, char** argv) {
  cout << "Begin:: test_cluster" << endl;
  RandomNumberGenerator rng;

  // set some test sizing parameters
  int max_value = 20;
  int num_rows = 3;
  int num_cols = 3;

  // create the objects
  Cluster cd(num_cols);
  vector<ContinuousComponentModel> ccm_v;
  for(int row_idx=0; row_idx<num_rows; row_idx++) {
    ContinuousComponentModel ccm;
    ccm_v.push_back(ccm);
  }

  // print the empty cluster
  cout << "empty cluster print" << endl;
  cout << cd << endl;

  // generate random data;
  vector<vector<double> > rows;
  for(int row_idx=0; row_idx<num_rows; row_idx++) {
    vector<double> row_data;
    for(int col_idx=0; col_idx<num_cols; col_idx++) {
      double random_value = (rng.nexti(max_value) + 1) * rng.next();
      row_data.push_back(random_value);
    }
    rows.push_back(row_data);
  }

  // poplute the objects
  cout << "Populating objects" << endl;
  for(int row_idx=0; row_idx<num_rows; row_idx++) {
    vector<double> row_data = rows[row_idx];
    for(int col_idx=0; col_idx<num_cols; col_idx++) {
      double random_value = rows[row_idx][col_idx];
      ccm_v[col_idx].insert_element(random_value);
    }
    cd.insert_row(row_data, row_idx);
  }

  // test score equivalence
  vector<double> score_v;
  double sum_scores = 0;
  for(int col_idx=0; col_idx<num_cols; col_idx++) {
    double suff_score = ccm_v[col_idx].calc_marginal_logp();
    score_v.push_back(suff_score);
    sum_scores += suff_score;
  }
  cout << "vector of separate suffstats scores after population: ";
  cout << score_v << endl;
  cout << "sum separate scores: " << sum_scores << endl;
  cout << "Cluster score with same data: " << cd.calc_sum_marginal_logps() << endl;
  cout << endl;
  //
  assert(is_almost(sum_scores, cd.calc_sum_marginal_logps(), 1E-10));

  // test hypers
  for(int which_col=0; which_col<num_cols; which_col++) {
    int N_grid = 11;
    double test_scale = 10;
    ContinuousComponentModel ccm_i = cd.get_model(which_col);
    double r, nu, s, mu;
    double precision = 1E-10;
    ccm_i.get_hyper_doubles(r, nu, s, mu);
    double score_0 = ccm_i.calc_marginal_logp();
    vector<double> hyper_grid;
    vector<double> hyper_conditionals;
    double curr_hyper_conditional_in_grid;
    //
    //    test 'r' hyper
    cout << "testing r conditionals" << endl;
    hyper_grid = log_linspace(r / test_scale, r * test_scale, N_grid);
    hyper_conditionals = cd.calc_hyper_conditionals(which_col, "r", hyper_grid);
    cout << "r_grid from function: " << hyper_grid << endl;
    cout << "r_conditioanls from function: " << hyper_conditionals << endl;
    curr_hyper_conditional_in_grid = hyper_conditionals[(int)(N_grid-1)/2];
    cout << "curr r conditional in grid: " << curr_hyper_conditional_in_grid << endl;
    assert(is_almost(score_0, curr_hyper_conditional_in_grid, precision));
    //
    //    test 'nu' hyper
    cout << "testing nu conditionals" << endl;
    hyper_grid = log_linspace(nu / test_scale, nu * test_scale, N_grid);
    hyper_conditionals = cd.calc_hyper_conditionals(which_col, "nu", hyper_grid);
    cout << "nu_grid: " << hyper_grid << endl;
    cout << "nu_conditionals: " << hyper_conditionals << endl;
    curr_hyper_conditional_in_grid = hyper_conditionals[(int)(N_grid-1)/2];
    cout << "curr nu conditional in grid: " << curr_hyper_conditional_in_grid << endl;
    assert(is_almost(score_0, curr_hyper_conditional_in_grid, precision));
    //
    //    test 's' hyper
    cout << "testing s conditionals" << endl;
    hyper_grid = log_linspace(s / test_scale, s * test_scale, N_grid);
    hyper_conditionals = cd.calc_hyper_conditionals(which_col, "s", hyper_grid);
    cout << "s_grid: " << hyper_grid << endl;
    cout << "s_conditionals: " << hyper_conditionals << endl;
    curr_hyper_conditional_in_grid = hyper_conditionals[(int)(N_grid-1)/2];
    cout << "curr s conditional in grid: " << curr_hyper_conditional_in_grid << endl;
    assert(is_almost(score_0, curr_hyper_conditional_in_grid, precision));
    //
    //    test 'mu' hyper
    cout << "testing mu conditionals" << endl;
    hyper_grid = log_linspace(mu / test_scale, mu * test_scale, N_grid);
    hyper_conditionals = cd.calc_hyper_conditionals(which_col, "mu", hyper_grid);
    cout << "mu_grid: " << hyper_grid << endl;
    cout << "mu_conditionals: " << hyper_conditionals << endl;
    curr_hyper_conditional_in_grid = hyper_conditionals[(int)(N_grid-1)/2];
    cout << "curr  mu conditional in grid: " << curr_hyper_conditional_in_grid << endl;
    assert(is_almost(score_0, curr_hyper_conditional_in_grid, precision));
  }

  // depopulate the objects
  cout << "De-populating objects" << endl;
  for(int row_idx=0; row_idx<num_rows; row_idx++) {
    vector<double> row_data = rows[row_idx];
    for(int col_idx=0; col_idx<num_cols; col_idx++) {
      double random_value = rows[row_idx][col_idx];
      ccm_v[col_idx].remove_element(random_value);
    }
    cd.remove_row(row_data, row_idx);
  }

  // test score equivalence
  score_v.clear();
  sum_scores = 0;
  for(int col_idx=0; col_idx<num_cols; col_idx++) {
    double suff_score = ccm_v[col_idx].calc_marginal_logp();
    score_v.push_back(suff_score);
    sum_scores += suff_score;
  }
  cout << "vector of separate suffstats scores after depopulation: ";
  cout << score_v << endl;
  cout << "sum separate scores: " << sum_scores << endl;
  cout << "Cluster score with same data: " << cd.calc_sum_marginal_logps() << endl;
  cout << endl;
  //
  assert(is_almost(sum_scores, cd.calc_sum_marginal_logps(), 1E-10));

  // test ability to remove columns
  //
  // poplute the cluster object
  cout << "Populating objects" << endl;
  for(int row_idx=0; row_idx<num_rows; row_idx++) {
    vector<double> row_data = rows[row_idx];
    cd.insert_row(row_data, row_idx);
  }
  cout << "cluster after population" << endl;
  cout << cd << endl;
  //
  // depopulate columns one by one
  while(cd.get_num_cols()>0) {
    int col_idx = cd.get_num_cols() - 1;
    cout << "removing column: " << col_idx << endl;
    cd.remove_col(col_idx);
    cout << "removed column: " << col_idx << endl;
    cout << "cluster now looks like: " << endl;
    cout << cd << endl;
  }

  cout << "Stop:: test_cluster" << endl;
}
<commit_msg>test incorporate_hyper_update<commit_after>#include <iostream>
#include "Cluster.h"
#include "utils.h"
#include "numerics.h"
#include "RandomNumberGenerator.h"

#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/io.hpp>

using namespace std;

const static double r0_0 = 1.0;
const static double nu0_0 = 2.0;
const static double s0_0 = 2.0;
const static double mu0_0 = 0.0;

map<string, double> create_default_hypers() {
  map<string, double> hypers;
  hypers["r"] = r0_0;
  hypers["nu"] = nu0_0;
  hypers["s"] = s0_0;
  hypers["mu"] = mu0_0;
  return hypers;
}

int main(int argc, char** argv) {
  cout << "Begin:: test_cluster" << endl;
  RandomNumberGenerator rng;

  // set some test sizing parameters
  int max_value = 20;
  int num_rows = 3;
  int num_cols = 3;

  // create the objects
  map<int, map<string, double> > hypers_m;
  for(int i=0; i<num_cols; i++) {
    hypers_m[i] = create_default_hypers();
  }
  vector<map<string, double>*> hypers_v;
  map<int, map<string, double> >::iterator hm_it;
  for(hm_it=hypers_m.begin(); hm_it!=hypers_m.end(); hm_it++) {
    int key = hm_it->first;
    map<string, double> &hypers = hm_it->second;
    hypers_v.push_back(&hypers);
    cout << "hypers_" << key << ": " << hypers << endl;
  }
  cout << "hypers_v: " << hypers_v << endl;

  Cluster cd(hypers_v);
  vector<ContinuousComponentModel> ccm_v;
  for(int col_idx=0; col_idx<num_cols; col_idx++) {
    ContinuousComponentModel ccm(*hypers_v[col_idx]);
    ccm_v.push_back(ccm);
  }

  // print the empty cluster
  cout << endl << endl <<"begin empty cluster print" << endl;
  cout << cd << endl;
  cout << "end empty cluster print" << endl << endl << endl;

  // generate random data;
  vector<vector<double> > rows;
  for(int row_idx=0; row_idx<num_rows; row_idx++) {
    vector<double> row_data;
    for(int col_idx=0; col_idx<num_cols; col_idx++) {
      double random_value = (rng.nexti(max_value) + 1) * rng.next();
      row_data.push_back(random_value);
    }
    rows.push_back(row_data);
  }

  // poplute the objects
  cout << "Populating objects" << endl;
  for(int row_idx=0; row_idx<num_rows; row_idx++) {
    vector<double> row_data = rows[row_idx];
    for(int col_idx=0; col_idx<num_cols; col_idx++) {
      double random_value = rows[row_idx][col_idx];
      ccm_v[col_idx].insert_element(random_value);
    }
    cd.insert_row(row_data, row_idx);
  }

  // test score equivalence
  vector<double> score_v;
  double sum_scores = 0;
  for(int col_idx=0; col_idx<num_cols; col_idx++) {
    double suff_score = ccm_v[col_idx].calc_marginal_logp();
    score_v.push_back(suff_score);
    sum_scores += suff_score;
  }
  cout << "vector of separate suffstats scores after population: ";
  cout << score_v << endl;
  cout << "sum separate scores: " << sum_scores << endl;
  cout << "Cluster score with same data: " << cd.calc_sum_marginal_logps() << endl;
  cout << endl;
  //
  assert(is_almost(sum_scores, cd.calc_sum_marginal_logps(), 1E-10));

  // test hypers
  for(int which_col=0; which_col<num_cols; which_col++) {
    int N_grid = 11;
    double test_scale = 10;
    ContinuousComponentModel ccm_i = cd.get_model(which_col);
    double r, nu, s, mu;
    double precision = 1E-10;
    ccm_i.get_hyper_doubles(r, nu, s, mu);
    double score_0 = ccm_i.calc_marginal_logp();
    vector<double> hyper_grid;
    vector<double> hyper_conditionals;
    double curr_hyper_conditional_in_grid;
    //
    //    test 'r' hyper
    cout << "testing r conditionals" << endl;
    hyper_grid = log_linspace(r / test_scale, r * test_scale, N_grid);
    hyper_conditionals = cd.calc_hyper_conditionals(which_col, "r", hyper_grid);
    cout << "r_grid from function: " << hyper_grid << endl;
    cout << "r_conditioanls from function: " << hyper_conditionals << endl;
    curr_hyper_conditional_in_grid = hyper_conditionals[(int)(N_grid-1)/2];
    cout << "curr r conditional in grid: " << curr_hyper_conditional_in_grid << endl;
    assert(is_almost(score_0, curr_hyper_conditional_in_grid, precision));

    map<string, double> &hypers = *(cd.model_v[which_col].p_hypers);
    double prior_r = hypers["r"];
    double new_r = hyper_grid[0];
    //
    cout << endl << "testing incorporate hyper update" << endl;
    cout << "new r: " << new_r << endl;
    hypers["r"] = new_r;
    cd.incorporate_hyper_update(which_col);
    cout << "marginal logp with new r: " << cd.get_model(which_col).calc_marginal_logp() << endl;
    //
    cout << "changing r back to: " << prior_r << endl;
    hypers["r"] = prior_r;
    cd.incorporate_hyper_update(which_col);
    cout << "marginal logp with prior r: " << cd.get_model(which_col).calc_marginal_logp() << endl;
    cout << "done testing incorporate hyper update on col" << endl << endl;

    //
    //    test 'nu' hyper
    cout << "testing nu conditionals" << endl;
    hyper_grid = log_linspace(nu / test_scale, nu * test_scale, N_grid);
    hyper_conditionals = cd.calc_hyper_conditionals(which_col, "nu", hyper_grid);
    cout << "nu_grid: " << hyper_grid << endl;
    cout << "nu_conditionals: " << hyper_conditionals << endl;
    curr_hyper_conditional_in_grid = hyper_conditionals[(int)(N_grid-1)/2];
    cout << "curr nu conditional in grid: " << curr_hyper_conditional_in_grid << endl;
    assert(is_almost(score_0, curr_hyper_conditional_in_grid, precision));
    //
    //    test 's' hyper
    cout << "testing s conditionals" << endl;
    hyper_grid = log_linspace(s / test_scale, s * test_scale, N_grid);
    hyper_conditionals = cd.calc_hyper_conditionals(which_col, "s", hyper_grid);
    cout << "s_grid: " << hyper_grid << endl;
    cout << "s_conditionals: " << hyper_conditionals << endl;
    curr_hyper_conditional_in_grid = hyper_conditionals[(int)(N_grid-1)/2];
    cout << "curr s conditional in grid: " << curr_hyper_conditional_in_grid << endl;
    assert(is_almost(score_0, curr_hyper_conditional_in_grid, precision));
    //
    //    test 'mu' hyper
    cout << "testing mu conditionals" << endl;
    hyper_grid = log_linspace(mu / test_scale, mu * test_scale, N_grid);
    hyper_conditionals = cd.calc_hyper_conditionals(which_col, "mu", hyper_grid);
    cout << "mu_grid: " << hyper_grid << endl;
    cout << "mu_conditionals: " << hyper_conditionals << endl;
    curr_hyper_conditional_in_grid = hyper_conditionals[(int)(N_grid-1)/2];
    cout << "curr  mu conditional in grid: " << curr_hyper_conditional_in_grid << endl;
    assert(is_almost(score_0, curr_hyper_conditional_in_grid, precision));
  }

  // depopulate the objects
  cout << "De-populating objects" << endl;
  for(int row_idx=0; row_idx<num_rows; row_idx++) {
    vector<double> row_data = rows[row_idx];
    for(int col_idx=0; col_idx<num_cols; col_idx++) {
      double random_value = rows[row_idx][col_idx];
      ccm_v[col_idx].remove_element(random_value);
    }
    cd.remove_row(row_data, row_idx);
  }

  // test score equivalence
  score_v.clear();
  sum_scores = 0;
  for(int col_idx=0; col_idx<num_cols; col_idx++) {
    double suff_score = ccm_v[col_idx].calc_marginal_logp();
    score_v.push_back(suff_score);
    sum_scores += suff_score;
  }
  cout << "vector of separate suffstats scores after depopulation: ";
  cout << score_v << endl;
  cout << "sum separate scores: " << sum_scores << endl;
  cout << "Cluster score with same data: " << cd.calc_sum_marginal_logps() << endl;
  cout << endl;
  //
  assert(is_almost(sum_scores, cd.calc_sum_marginal_logps(), 1E-10));

  // test ability to remove columns
  //
  // poplute the cluster object
  cout << "Populating objects" << endl;
  for(int row_idx=0; row_idx<num_rows; row_idx++) {
    vector<double> row_data = rows[row_idx];
    cd.insert_row(row_data, row_idx);
  }
  cout << "cluster after population" << endl;
  cout << cd << endl;
  //
  // depopulate columns one by one
  while(cd.get_num_cols()>0) {
    int col_idx = cd.get_num_cols() - 1;
    cout << "removing column: " << col_idx << endl;
    cd.remove_col(col_idx);
    cout << "removed column: " << col_idx << endl;
    cout << "cluster now looks like: " << endl;
    cout << cd << endl;
  }

  cout << "Stop:: test_cluster" << endl;
}
<|endoftext|>


<commit_before>/* ---------------------------------------------------------------------
 * Numenta Platform for Intelligent Computing (NuPIC)
 * Copyright (C) 2013, Numenta, Inc.  Unless you have an agreement
 * with Numenta, Inc., for a separate license for this software code, the
 * following terms and conditions apply:
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see http://www.gnu.org/licenses.
 *
 * http://numenta.org/licenses/
 * ---------------------------------------------------------------------
 */

#ifndef NTA_PY_REGION_HPP
#define NTA_PY_REGION_HPP

#include <py_support/PyArray.hpp>

#include <string>
#include <vector>
#include <set>

#include <capnp/any.h>

#include <nupic/engine/RegionImpl.hpp>
#include <nupic/engine/Spec.hpp>
#include <nupic/ntypes/Value.hpp>

namespace nupic
{
  struct Spec;

  class PyRegion : public RegionImpl
  {
    typedef std::map<std::string, Spec> SpecMap;
  public:
    // Used by RegionImplFactory to create and cache a nodespec
    static Spec * createSpec(const char * nodeType, const char* className="");

    // Used by RegionImplFactory to destroy a node spec when clearing its cache
    static void destroySpec(const char * nodeType, const char* className="");

    PyRegion(const char * module, const ValueMap & nodeParams, Region * region, const char* className="");
    PyRegion(const char * module, BundleIO& bundle, Region * region, const char* className="");
    virtual ~PyRegion();

    // Manual serialization methods
    void serialize(BundleIO& bundle);
    void deserialize(BundleIO& bundle);

    // Capnp serialization methods - not yet implemented for PyRegions
    virtual void write(capnp::AnyPointer::Builder& proto) const override;
    virtual void read(capnp::AnyPointer::Reader& proto) override;

    const Spec & getSpec();

    static void createSpec(const char * nodeType, Spec & ns, const char* className="");

    // RegionImpl interface

    size_t getNodeOutputElementCount(const std::string& outputName);
    void getParameterFromBuffer(const std::string& name, Int64 index, IWriteBuffer& value);
    void setParameterFromBuffer(const std::string& name, Int64 index, IReadBuffer& value);

    void initialize();
    void compute();
    std::string executeCommand(const std::vector<std::string>& args, Int64 index);

    size_t getParameterArrayCount(const std::string& name, Int64 index);

    virtual Byte getParameterByte(const std::string& name, Int64 index);
    virtual Int32 getParameterInt32(const std::string& name, Int64 index);
    virtual UInt32 getParameterUInt32(const std::string& name, Int64 index);
    virtual Int64 getParameterInt64(const std::string& name, Int64 index);
    virtual UInt64 getParameterUInt64(const std::string& name, Int64 index);
    virtual Real32 getParameterReal32(const std::string& name, Int64 index);
    virtual Real64 getParameterReal64(const std::string& name, Int64 index);
    virtual Handle getParameterHandle(const std::string& name, Int64 index);
    virtual std::string getParameterString(const std::string& name, Int64 index);

    virtual void setParameterByte(const std::string& name, Int64 index, Byte value);
    virtual void setParameterInt32(const std::string& name, Int64 index, Int32 value);
    virtual void setParameterUInt32(const std::string& name, Int64 index, UInt32 value);
    virtual void setParameterInt64(const std::string& name, Int64 index, Int64 value);
    virtual void setParameterUInt64(const std::string& name, Int64 index, UInt64 value);
    virtual void setParameterReal32(const std::string& name, Int64 index, Real32 value);
    virtual void setParameterReal64(const std::string& name, Int64 index, Real64 value);
    virtual void setParameterHandle(const std::string& name, Int64 index, Handle value);
    virtual void setParameterString(const std::string& name, Int64 index, const std::string& value);

    virtual void getParameterArray(const std::string& name, Int64 index, Array & array);
    virtual void setParameterArray(const std::string& name, Int64 index, const Array & array);

    // Helper methods
    template <typename T, typename PyT>
    T getParameterT(const std::string & name, Int64 index);

    template <typename T, typename PyT>
    void setParameterT(const std::string & name, Int64 index, T value);

  private:
    PyRegion();
    PyRegion(const Region &);

  private:
    static SpecMap specs_;
    std::string module_;
    std::string className_;
    py::Instance node_;
    std::set<boost::shared_ptr<PyArray<UInt64> > > splitterMaps_;
    // pointers rather than objects because Array doesnt
    // have a default constructor
    std::map<std::string, Array*> inputArrays_;
  };
}

#endif // NTA_PY_REGION_HPP
<commit_msg>Removes redundant virtual keyword for overridden functions.<commit_after>/* ---------------------------------------------------------------------
 * Numenta Platform for Intelligent Computing (NuPIC)
 * Copyright (C) 2013, Numenta, Inc.  Unless you have an agreement
 * with Numenta, Inc., for a separate license for this software code, the
 * following terms and conditions apply:
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see http://www.gnu.org/licenses.
 *
 * http://numenta.org/licenses/
 * ---------------------------------------------------------------------
 */

#ifndef NTA_PY_REGION_HPP
#define NTA_PY_REGION_HPP

#include <py_support/PyArray.hpp>

#include <string>
#include <vector>
#include <set>

#include <capnp/any.h>

#include <nupic/engine/RegionImpl.hpp>
#include <nupic/engine/Spec.hpp>
#include <nupic/ntypes/Value.hpp>

namespace nupic
{
  struct Spec;

  class PyRegion : public RegionImpl
  {
    typedef std::map<std::string, Spec> SpecMap;
  public:
    // Used by RegionImplFactory to create and cache a nodespec
    static Spec * createSpec(const char * nodeType, const char* className="");

    // Used by RegionImplFactory to destroy a node spec when clearing its cache
    static void destroySpec(const char * nodeType, const char* className="");

    PyRegion(const char * module, const ValueMap & nodeParams, Region * region, const char* className="");
    PyRegion(const char * module, BundleIO& bundle, Region * region, const char* className="");
    virtual ~PyRegion();

    // Manual serialization methods
    void serialize(BundleIO& bundle);
    void deserialize(BundleIO& bundle);

    // Capnp serialization methods - not yet implemented for PyRegions
    void write(capnp::AnyPointer::Builder& proto) const override;
    void read(capnp::AnyPointer::Reader& proto) override;

    const Spec & getSpec();

    static void createSpec(const char * nodeType, Spec & ns, const char* className="");

    // RegionImpl interface

    size_t getNodeOutputElementCount(const std::string& outputName);
    void getParameterFromBuffer(const std::string& name, Int64 index, IWriteBuffer& value);
    void setParameterFromBuffer(const std::string& name, Int64 index, IReadBuffer& value);

    void initialize();
    void compute();
    std::string executeCommand(const std::vector<std::string>& args, Int64 index);

    size_t getParameterArrayCount(const std::string& name, Int64 index);

    virtual Byte getParameterByte(const std::string& name, Int64 index);
    virtual Int32 getParameterInt32(const std::string& name, Int64 index);
    virtual UInt32 getParameterUInt32(const std::string& name, Int64 index);
    virtual Int64 getParameterInt64(const std::string& name, Int64 index);
    virtual UInt64 getParameterUInt64(const std::string& name, Int64 index);
    virtual Real32 getParameterReal32(const std::string& name, Int64 index);
    virtual Real64 getParameterReal64(const std::string& name, Int64 index);
    virtual Handle getParameterHandle(const std::string& name, Int64 index);
    virtual std::string getParameterString(const std::string& name, Int64 index);

    virtual void setParameterByte(const std::string& name, Int64 index, Byte value);
    virtual void setParameterInt32(const std::string& name, Int64 index, Int32 value);
    virtual void setParameterUInt32(const std::string& name, Int64 index, UInt32 value);
    virtual void setParameterInt64(const std::string& name, Int64 index, Int64 value);
    virtual void setParameterUInt64(const std::string& name, Int64 index, UInt64 value);
    virtual void setParameterReal32(const std::string& name, Int64 index, Real32 value);
    virtual void setParameterReal64(const std::string& name, Int64 index, Real64 value);
    virtual void setParameterHandle(const std::string& name, Int64 index, Handle value);
    virtual void setParameterString(const std::string& name, Int64 index, const std::string& value);

    virtual void getParameterArray(const std::string& name, Int64 index, Array & array);
    virtual void setParameterArray(const std::string& name, Int64 index, const Array & array);

    // Helper methods
    template <typename T, typename PyT>
    T getParameterT(const std::string & name, Int64 index);

    template <typename T, typename PyT>
    void setParameterT(const std::string & name, Int64 index, T value);

  private:
    PyRegion();
    PyRegion(const Region &);

  private:
    static SpecMap specs_;
    std::string module_;
    std::string className_;
    py::Instance node_;
    std::set<boost::shared_ptr<PyArray<UInt64> > > splitterMaps_;
    // pointers rather than objects because Array doesnt
    // have a default constructor
    std::map<std::string, Array*> inputArrays_;
  };
}

#endif // NTA_PY_REGION_HPP
<|endoftext|>


<commit_before><commit_msg>Fix for Issue 61605 Browser crash @ AutoFillProfile::operator=+0x49 BUG=61605 TEST=In the bug Review URL: http://codereview.chromium.org/4693004<commit_after><|endoftext|>


<commit_before>// Filename: collisionPlane.cxx
// Created by:  drose (25Apr00)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license.  You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////

#include "collisionFloorMesh.h"
#include "collisionHandler.h"
#include "collisionEntry.h"
#include "collisionSphere.h"
#include "collisionLine.h"
#include "collisionRay.h"
#include "collisionSegment.h"
#include "config_collide.h"
#include "pointerToArray.h"
#include "geomNode.h"
#include "geom.h"
#include "datagram.h"
#include "datagramIterator.h"
#include "bamReader.h"
#include "bamWriter.h"
#include "boundingPlane.h"
#include "geom.h"
#include "geomTrifans.h"
#include "geomLinestrips.h"
#include "geomVertexWriter.h"
#include <algorithm>
PStatCollector CollisionFloorMesh::_volume_pcollector("Collision Volumes:CollisionFloorMesh");
PStatCollector CollisionFloorMesh::_test_pcollector("Collision Tests:CollisionFloorMesh");
TypeHandle CollisionFloorMesh::_type_handle;

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::make_copy
//       Access: Public, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
CollisionSolid *CollisionFloorMesh::
make_copy() {
  return new CollisionFloorMesh(*this);
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::xform
//       Access: Public, Virtual
//  Description: Transforms the solid by the indicated matrix.
////////////////////////////////////////////////////////////////////
void CollisionFloorMesh::
xform(const LMatrix4f &mat) {
  CollisionSolid::xform(mat);
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::get_collision_origin
//       Access: Public, Virtual
//  Description: Returns the point in space deemed to be the "origin"
//               of the solid for collision purposes.  The closest
//               intersection point to this origin point is considered
//               to be the most significant.
////////////////////////////////////////////////////////////////////
LPoint3f CollisionFloorMesh::
get_collision_origin() const {
  // No real sensible origin exists for a plane.  We return 0, 0, 0,
  // without even bothering to ensure that that point exists on the
  // plane.
  return LPoint3f::origin();
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::output
//       Access: Public, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
void CollisionFloorMesh::
output(ostream &out) const {
  out << "cfloor";
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionFloorMesh::compute_internal_bounds
//       Access: Protected, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
PT(BoundingVolume) CollisionFloorMesh::
compute_internal_bounds() const {
  return new BoundingBox(LPoint3f(0,0,0), LPoint3f(1,1,1));
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionFloorMesh::test_intersection_from_ray
//       Access: Public, Virtual
//  Description: must be a vertical Ray!!!
////////////////////////////////////////////////////////////////////
PT(CollisionEntry) CollisionFloorMesh::
test_intersection_from_ray(const CollisionEntry &entry) const {
  const CollisionRay *ray;
  DCAST_INTO_R(ray, entry.get_from(), 0);
  const LMatrix4f &wrt_mat = entry.get_wrt_mat();
  LPoint3f from_origin = ray->get_origin() * wrt_mat;
  LVector3f from_direction = ray->get_direction() * wrt_mat;

  double fx=from_origin[0];
  double fy=from_origin[1];

  CollisionFloorMesh::Triangles::const_iterator ti;
  bool collided = false;
  for (ti=_triangles.begin();ti< _triangles.end();++ti) {
    TriangleIndices tri = *ti;
    LPoint3d p0=_vertices[tri.p1];
    LPoint3d p1=_vertices[tri.p2];
    LPoint3d p2=_vertices[tri.p3];
    double p0x = p0[0];
    double p1x = p1[0];
    double p2x = p2[0];

    double p0y = p0[1];
    double p1y = p1[1];
    double p2y = p2[1];

    double e0x,e0y,e1x,e1y,e2x,e2y;
    double u,v;

    e0x = fx - p0x; e0y = fy - p0y;
    e1x = p1x - p0x; e1y = p1y - p0y;
    e2x = p2x - p0x; e2y = p2y - p0y;
    if (e1x==0) {
      if (e2x == 0) continue;
      u = e0x/e2x;
      if (u<0 || u>1) continue;
      if (e1y == 0) continue;
      v = ( e0y - (e2y*u))/e1y;
      if (v<0) continue;
    } else {
      double d = (e2y * e1x)-(e2x * e1y);
      if (d==0) continue;
      u = ((e0y * e1x) - (e0x * e1y))/d;
      if (u<0 || u>1) continue;
      v = (e0x - (e2x * u)) / e1x;
      if (v<0) continue;
      if (u + v > 1) continue;
    }
    //we collided!!
    double mag = u + v;
    double p0z = p0[2];
    double p1z = p1[2];
    double p2z = p2[2];

    double uz = (p2z - p0z) *  mag;
    double vz = (p1z - p0z) *  mag;
    double ratio = u/(u+v);
    double finalz = vz+((uz - vz) * ratio);
    PT(CollisionEntry) new_entry = new CollisionEntry(entry);

    new_entry->set_surface_normal(LPoint3f(0,0,1));
    new_entry->set_surface_point(LPoint3f(fx,fy,finalz));
    return new_entry;
  }
  return NULL;
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionFloorMesh::fill_viz_geom
//       Access: Protected, Virtual
//  Description: Fills the _viz_geom GeomNode up with Geoms suitable
//               for rendering this solid.
////////////////////////////////////////////////////////////////////
void CollisionFloorMesh::
fill_viz_geom() {

}

////////////////////////////////////////////////////////////////////
//     Function: CollisionFloorMesh::get_volume_pcollector
//       Access: Public, Virtual
//  Description: Returns a PStatCollector that is used to count the
//               number of bounding volume tests made against a solid
//               of this type in a given frame.
////////////////////////////////////////////////////////////////////
PStatCollector &CollisionFloorMesh::
get_volume_pcollector() {
  return _volume_pcollector;
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionFloorMesh::get_test_pcollector
//       Access: Public, Virtual
//  Description: Returns a PStatCollector that is used to count the
//               number of intersection tests made against a solid
//               of this type in a given frame.
////////////////////////////////////////////////////////////////////
PStatCollector &CollisionFloorMesh::
get_test_pcollector() {
  return _test_pcollector;
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionFloorMesh::write_datagram
//       Access: Public
//  Description: Function to write the important information in
//               the particular object to a Datagram
////////////////////////////////////////////////////////////////////
void CollisionFloorMesh::
write_datagram(BamWriter *manager, Datagram &me)
{
  CollisionSolid::write_datagram(manager, me);
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionFloorMesh::fillin
//       Access: Protected
//  Description: Function that reads out of the datagram (or asks
//               manager to read) all of the data that is needed to
//               re-create this object and stores it in the appropiate
//               place
////////////////////////////////////////////////////////////////////
void CollisionFloorMesh::
fillin(DatagramIterator& scan, BamReader* manager)
{
  CollisionSolid::fillin(scan, manager);
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPolygon::make_CollisionPolygon
//       Access: Protected
//  Description: Factory method to generate a CollisionPolygon object
////////////////////////////////////////////////////////////////////
TypedWritable* CollisionFloorMesh::
make_CollisionFloorMesh(const FactoryParams &params) {
  CollisionFloorMesh *me = new CollisionFloorMesh;
  DatagramIterator scan;
  BamReader *manager;

  parse_params(params, scan, manager);
  me->fillin(scan, manager);
  return me;
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPolygon::register_with_factory
//       Access: Public, Static
//  Description: Factory method to generate a CollisionPolygon object
////////////////////////////////////////////////////////////////////
void CollisionFloorMesh::
register_with_read_factory() {
  BamReader::get_factory()->register_factory(get_class_type(), make_CollisionFloorMesh);
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionFloorMesh::write
//       Access: Public, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
void CollisionFloorMesh::
write(ostream &out, int indent_level) const {
  indent(out, indent_level) << (*this) << "\n";
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionFloorMesh::add_triangle
//       Access: Published
//  Description: store a triangle for processing
////////////////////////////////////////////////////////////////////
void CollisionFloorMesh::
add_triangle(unsigned int pointA, unsigned int pointB, unsigned int pointC) {
  CollisionFloorMesh::TriangleIndices  tri;
  tri.p1 = pointA;
  tri.p2 = pointB;
  tri.p3 = pointC;
  _triangles.push_back(tri);
}
<commit_msg>bam data transfer phase_1. No objects in anybodys tree that use this, so its going into the wild!<commit_after>// Filename: collisionPlane.cxx
// Created by:  drose (25Apr00)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license.  You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////

#include "collisionFloorMesh.h"
#include "collisionHandler.h"
#include "collisionEntry.h"
#include "collisionSphere.h"
#include "collisionLine.h"
#include "collisionRay.h"
#include "collisionSegment.h"
#include "config_collide.h"
#include "pointerToArray.h"
#include "geomNode.h"
#include "geom.h"
#include "datagram.h"
#include "datagramIterator.h"
#include "bamReader.h"
#include "bamWriter.h"
#include "boundingPlane.h"
#include "geom.h"
#include "geomTrifans.h"
#include "geomLinestrips.h"
#include "geomVertexWriter.h"
#include <algorithm>
PStatCollector CollisionFloorMesh::_volume_pcollector("Collision Volumes:CollisionFloorMesh");
PStatCollector CollisionFloorMesh::_test_pcollector("Collision Tests:CollisionFloorMesh");
TypeHandle CollisionFloorMesh::_type_handle;

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::make_copy
//       Access: Public, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
CollisionSolid *CollisionFloorMesh::
make_copy() {
  return new CollisionFloorMesh(*this);
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::xform
//       Access: Public, Virtual
//  Description: Transforms the solid by the indicated matrix.
////////////////////////////////////////////////////////////////////
void CollisionFloorMesh::
xform(const LMatrix4f &mat) {
  CollisionSolid::xform(mat);
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::get_collision_origin
//       Access: Public, Virtual
//  Description: Returns the point in space deemed to be the "origin"
//               of the solid for collision purposes.  The closest
//               intersection point to this origin point is considered
//               to be the most significant.
////////////////////////////////////////////////////////////////////
LPoint3f CollisionFloorMesh::
get_collision_origin() const {
  // No real sensible origin exists for a plane.  We return 0, 0, 0,
  // without even bothering to ensure that that point exists on the
  // plane.
  return LPoint3f::origin();
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::output
//       Access: Public, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
void CollisionFloorMesh::
output(ostream &out) const {
  out << "cfloor";
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionFloorMesh::compute_internal_bounds
//       Access: Protected, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
PT(BoundingVolume) CollisionFloorMesh::
compute_internal_bounds() const {
  return new BoundingBox(LPoint3f(0,0,0), LPoint3f(1,1,1));
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionFloorMesh::test_intersection_from_ray
//       Access: Public, Virtual
//  Description: must be a vertical Ray!!!
////////////////////////////////////////////////////////////////////
PT(CollisionEntry) CollisionFloorMesh::
test_intersection_from_ray(const CollisionEntry &entry) const {
  const CollisionRay *ray;
  DCAST_INTO_R(ray, entry.get_from(), 0);
  const LMatrix4f &wrt_mat = entry.get_wrt_mat();
  LPoint3f from_origin = ray->get_origin() * wrt_mat;
  LVector3f from_direction = ray->get_direction() * wrt_mat;

  double fx=from_origin[0];
  double fy=from_origin[1];

  CollisionFloorMesh::Triangles::const_iterator ti;
  bool collided = false;
  for (ti=_triangles.begin();ti< _triangles.end();++ti) {
    TriangleIndices tri = *ti;
    LPoint3d p0=_vertices[tri.p1];
    LPoint3d p1=_vertices[tri.p2];
    LPoint3d p2=_vertices[tri.p3];
    double p0x = p0[0];
    double p1x = p1[0];
    double p2x = p2[0];

    double p0y = p0[1];
    double p1y = p1[1];
    double p2y = p2[1];

    double e0x,e0y,e1x,e1y,e2x,e2y;
    double u,v;

    e0x = fx - p0x; e0y = fy - p0y;
    e1x = p1x - p0x; e1y = p1y - p0y;
    e2x = p2x - p0x; e2y = p2y - p0y;
    if (e1x==0) {
      if (e2x == 0) continue;
      u = e0x/e2x;
      if (u<0 || u>1) continue;
      if (e1y == 0) continue;
      v = ( e0y - (e2y*u))/e1y;
      if (v<0) continue;
    } else {
      double d = (e2y * e1x)-(e2x * e1y);
      if (d==0) continue;
      u = ((e0y * e1x) - (e0x * e1y))/d;
      if (u<0 || u>1) continue;
      v = (e0x - (e2x * u)) / e1x;
      if (v<0) continue;
      if (u + v > 1) continue;
    }
    //we collided!!
    double mag = u + v;
    double p0z = p0[2];
    double p1z = p1[2];
    double p2z = p2[2];

    double uz = (p2z - p0z) *  mag;
    double vz = (p1z - p0z) *  mag;
    double ratio = u/(u+v);
    double finalz = vz+((uz - vz) * ratio);
    PT(CollisionEntry) new_entry = new CollisionEntry(entry);

    new_entry->set_surface_normal(LPoint3f(0,0,1));
    new_entry->set_surface_point(LPoint3f(fx,fy,finalz));
    return new_entry;
  }
  return NULL;
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionFloorMesh::fill_viz_geom
//       Access: Protected, Virtual
//  Description: Fills the _viz_geom GeomNode up with Geoms suitable
//               for rendering this solid.
////////////////////////////////////////////////////////////////////
void CollisionFloorMesh::
fill_viz_geom() {

}

////////////////////////////////////////////////////////////////////
//     Function: CollisionFloorMesh::get_volume_pcollector
//       Access: Public, Virtual
//  Description: Returns a PStatCollector that is used to count the
//               number of bounding volume tests made against a solid
//               of this type in a given frame.
////////////////////////////////////////////////////////////////////
PStatCollector &CollisionFloorMesh::
get_volume_pcollector() {
  return _volume_pcollector;
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionFloorMesh::get_test_pcollector
//       Access: Public, Virtual
//  Description: Returns a PStatCollector that is used to count the
//               number of intersection tests made against a solid
//               of this type in a given frame.
////////////////////////////////////////////////////////////////////
PStatCollector &CollisionFloorMesh::
get_test_pcollector() {
  return _test_pcollector;
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionFloorMesh::write_datagram
//       Access: Public
//  Description: Function to write the important information in
//               the particular object to a Datagram
////////////////////////////////////////////////////////////////////
void CollisionFloorMesh::
write_datagram(BamWriter *manager, Datagram &me)
{
  CollisionSolid::write_datagram(manager, me);
  me.add_uint16(_vertices.size());
  for (size_t i = 0; i < _vertices.size(); i++) {
    _vertices[i].write_datagram(me);
  }
  me.add_uint16(_triangles.size());
  for (size_t i = 0; i < _triangles.size(); i++) {
    me.add_uint16(_triangles[i].p1);
    me.add_uint16(_triangles[i].p2);
    me.add_uint16(_triangles[i].p3);
  }
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionFloorMesh::fillin
//       Access: Protected
//  Description: Function that reads out of the datagram (or asks
//               manager to read) all of the data that is needed to
//               re-create this object and stores it in the appropiate
//               place
////////////////////////////////////////////////////////////////////
void CollisionFloorMesh::
fillin(DatagramIterator& scan, BamReader* manager)
{
  CollisionSolid::fillin(scan, manager);

  unsigned int num_verts = scan.get_uint16();
  for (size_t i = 0; i < num_verts; i++) {
    LPoint3d vert;
    vert.read_datagram(scan);

    _vertices.push_back(vert);
  }
  unsigned int num_tris = scan.get_uint16();
  for (size_t i = 0; i < num_tris; i++) {
    CollisionFloorMesh::TriangleIndices tri;

    tri.p1 = scan.get_uint32();
    tri.p2 = scan.get_uint32();
    tri.p3 = scan.get_uint32();

    _triangles.push_back(tri);
  }
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPolygon::make_CollisionPolygon
//       Access: Protected
//  Description: Factory method to generate a CollisionPolygon object
////////////////////////////////////////////////////////////////////
TypedWritable* CollisionFloorMesh::
make_CollisionFloorMesh(const FactoryParams &params) {
  CollisionFloorMesh *me = new CollisionFloorMesh;
  DatagramIterator scan;
  BamReader *manager;

  parse_params(params, scan, manager);
  me->fillin(scan, manager);
  return me;
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPolygon::register_with_factory
//       Access: Public, Static
//  Description: Factory method to generate a CollisionPolygon object
////////////////////////////////////////////////////////////////////
void CollisionFloorMesh::
register_with_read_factory() {
  BamReader::get_factory()->register_factory(get_class_type(), make_CollisionFloorMesh);
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionFloorMesh::write
//       Access: Public, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
void CollisionFloorMesh::
write(ostream &out, int indent_level) const {
  indent(out, indent_level) << (*this) << "\n";
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionFloorMesh::add_triangle
//       Access: Published
//  Description: store a triangle for processing
////////////////////////////////////////////////////////////////////
void CollisionFloorMesh::
add_triangle(unsigned int pointA, unsigned int pointB, unsigned int pointC) {
  CollisionFloorMesh::TriangleIndices  tri;
  tri.p1 = pointA;
  tri.p2 = pointB;
  tri.p3 = pointC;
  _triangles.push_back(tri);
}
<|endoftext|>


<commit_before>#include "cartridge.h"

#include <map>
#include <string>

#include "common/logging.h"

namespace dgb {

namespace {

const uint16_t kCartridgeTypeAddress = 0x147;

const std::map<int, std::string> kCartridgeTypes = {
  {0x00, "ROM ONLY"},
  {0x01, "MBC1"},
  {0x02, "MBC1+RAM"},
  {0x03, "MBC1+RAM+BATTERY"},
  {0x05, "MBC2"},
  {0x06, "MBC2+BATTERY"},
  {0x08, "ROM+RAM"},
  {0x09, "ROM+RAM+BATTERY"},
  {0x0B, "MMM01"},
  {0x0C, "MMM01+RAM"},
  {0x0D, "MMM01+RAM+BATTERY"},
  {0x0F, "MBC3+TIMER+BATTERY"},
  {0x10, "MBC3+TIMER+RAM+BATTERY"},
  {0x11, "MBC3"},
  {0x12, "MBC3+RAM"},
  {0x13, "MBC3+RAM+BATTERY"},
  {0x15, "MBC4"},
  {0x16, "MBC4+RAM"},
  {0x17, "MBC4+RAM+BATTERY"},
  {0x19, "MBC5"},
  {0x1A, "MBC5+RAM"},
  {0x1B, "MBC5+RAM+BATTERY"},
  {0x1C, "MBC5+RUMBLE"},
  {0x1D, "MBC5+RUMBLE+RAM"},
  {0x1E, "MBC5+RUMBLE+RAM+BATTERY"}
  // Unsupported types:
  // 0xFC  POCKET CAMERA
  // 0xFD  BANDAI TAMA5
  // 0xFE  HuC3
  // 0xFF  HuC1+RAM+BATTERY
};

const std::map<uint8_t, std::string> kRomSizes = {
  {0x00, "32KByte (no ROM banking)"},
  {0x01, "64KByte (4 banks)"},
  {0x02, "128KByte (8 banks)"},
  {0x03, "256KByte (16 banks)"},
  {0x04, "512KByte (32 banks)"},
  {0x05, "1MByte (64 banks)  - only 63 banks used by MBC1"},
  {0x06, "2MByte (128 banks) - only 125 banks used by MBC1"},
  {0x07, "4MByte (256 banks)"},
  {0x52, "1.1MByte (72 banks)"},
  {0x53, "1.2MByte (80 banks)"},
  {0x54, "1.5MByte (96 banks)"}
};

const std::map<uint8_t, std::string> kRamSizes = {
  {0x00, "None"},
  {0x01, "2 KBytes"},
  {0x02, "8 Kbytes"},
  {0x03, "32 KBytes (4 banks of 8KBytes each)"}
};

class MBCNone : public MemoryBankController {
 public:
  MBCNone(std::shared_ptr<MMapFile> rom) : MemoryBankController(rom) {}

  uint8_t Read8(uint16_t offset) override {
    if (offset > 0x7FFF) {
      FATALF("MBCNone: unsupported read offset: 0x%04x", offset);
    }
    return *(reinterpret_cast<uint8_t*>(rom_->memory()) + offset);
  }

  void Write8(uint16_t offset, uint8_t value) override {
    if (0x2000 <= offset && offset <= 0x3FFF) {
      if (value > 1) {
        FATALF("MBCNone does not support ROM bank 0x%02x", value & 0xff);
      }
      return;
    }
    FATALF("MBCNone: Unimplemented Write to Cartridge: (0x%04x) <- 0x%02x",
        offset & 0xffff, value & 0xff);
  }
};

class MBC1 : public MemoryBankController {
 public:
  MBC1(std::shared_ptr<MMapFile> rom) : MemoryBankController(rom) {}

  uint8_t Read8(uint16_t offset) override {
    if (offset > 0x7FFF) {
      FATALF("TODO: MBC1 unsupported read offset: 0x%04x", offset);
    }
    if (0x4000 <= offset && offset < 0x8000) {
      return *(reinterpret_cast<uint8_t*>(rom_->memory())
          + (0x4000 * rom_bank_) + (offset - 0x4000));
    }
    return *(reinterpret_cast<uint8_t*>(rom_->memory()) + offset);
  }

  void Write8(uint16_t offset, uint8_t value) override {
    if (0x2000 <= offset && offset < 0x4000) {
      // Lower 5 bits of ROM bank.
      rom_bank_ = (rom_bank_ & 0xe0) | (value & 0x1f);
      if (rom_bank_ == 0 || rom_bank_ == 20 ||
          rom_bank_ == 40 || rom_bank_ == 60) {
        rom_bank_ += 1;
      }
      return;
    }
    if (0x4000 <= offset && offset < 0x6000) {
      // RAM bank, or upper 2 bits of ROM bank.
      rom_bank_ = (rom_bank_ & 0x1f) | (value & 0xe0);
      if (rom_bank_ == 0 || rom_bank_ == 20 ||
          rom_bank_ == 40 || rom_bank_ == 60) {
        rom_bank_ += 1;
      }
      return;
    }
    if (0xA000 <= offset && offset < 0xC000) {
      // TODO: RAM
      ERRORF("TODO: MBC1: RAM Write to Cartridge: (0x%04x) <- 0x%02x",
          offset & 0xffff, value & 0xff);
      return;
    }
    FATALF("MBC1: Unimplemented Write to Cartridge: (0x%04x) <- 0x%02x",
        offset & 0xffff, value & 0xff);
  }

 private:
  int rom_bank_ = 0;
};

class MBC3 : public MemoryBankController {
 public:
  MBC3(std::shared_ptr<MMapFile> rom) : MemoryBankController(rom) {}

  uint8_t Read8(uint16_t offset) override {
    if (offset < 0x4000) {
      return *(reinterpret_cast<uint8_t*>(rom_->memory()) + offset);
    }
    if (0x4000 <= offset && offset < 0x8000) {
      uint32_t bank_offset = (0x4000 * rom_bank_) + (offset - 0x4000);
      if (bank_offset >= rom_->size()) {
        FATALF("Attempted to read past the end of the ROM file");
      }
      return *(reinterpret_cast<uint8_t*>(rom_->memory()) + bank_offset);
    }

    FATALF("TODO: MBC3 unimplemented read offset: 0x%04x", offset);
    return 0;
  }

  void Write8(uint16_t offset, uint8_t value) override {
    if (offset < 0x2000) {
      // TODO: support enable/disable of RAM & RTC so we know when to save to
      // disk
      return;
    }

    if (0x2000 <= offset && offset < 0x4000) {
      // All 7 bits of rom bank.
      rom_bank_ = value & 0x7f;
      if (rom_bank_ == 0) rom_bank_ = 1;
      return;
    }
    //if (0x4000 <= offset && offset < 0x6000) {
    //  // RAM bank, or upper 2 bits of ROM bank.
    //  rom_bank_ = (rom_bank_ & 0x1f) | (value & 0xe0);
    //  if (rom_bank_ == 0 || rom_bank_ == 20 ||
    //      rom_bank_ == 40 || rom_bank_ == 60) {
    //    rom_bank_ += 1;
    //  }
    //  return;
    //}
    //if (0xA000 <= offset && offset < 0xC000) {
    //  // TODO: RAM
    //  ERRORF("TODO: MBC1: RAM Write to Cartridge: (0x%04x) <- 0x%02x",
    //      offset & 0xffff, value & 0xff);
    //  return;
    //}
    FATALF("MBC3: Unimplemented Write to Cartridge: (0x%04x) <- 0x%02x",
        offset & 0xffff, value & 0xff);
  }

 private:
  int rom_bank_ = 0;
};
}  // namespace

Cartridge::Cartridge(const std::string &filename)
    : rom_(new MMapFile(filename)) {
  uint8_t type_byte = rom_->Read8(kCartridgeTypeAddress);
  switch (type_byte) {
    case 0x0:
      mbc_.reset(new MBCNone(rom_));
      break;
    case 0x1:
      mbc_.reset(new MBC1(rom_));
      break;
    case 0x13:
      mbc_.reset(new MBC3(rom_));
      break;
    default:
      FATALF("Unsupported cartridge type: 0x%02x", type_byte & 0xff);
  }
}

uint8_t Cartridge::Read8(uint16_t offset) {
  return mbc_->Read8(offset);

  if (0x3FFF < offset && offset < 0x8000 && rom_bank_ > 1) {
    FATALF("NOT IMPLEMENTED: rom banking");
  }
  return *(reinterpret_cast<uint8_t*>(rom_->memory()) + offset);
}

void Cartridge::Write8(uint16_t offset, uint8_t value) {
  //if (offset < 0x8000) {
  //  ERRORF("Unimplemented Write to Cartridge RAM: (0x%04x) <- 0x%02x",
  //      offset & 0xffff, value & 0xff);
  //  return;
  //}
  //if (0xA000 <= offset && offset <= 0xBFFF) {
  //  ERRORF("Unimplemented Write to Cartridge RAM: (0x%04x) <- 0x%02x",
  //      offset & 0xffff, value & 0xff);
  //  return;
  //}
  mbc_->Write8(offset, value);
  return;
}

std::string Cartridge::Title() {
  char *title = reinterpret_cast<char*>(rom_->memory()) + 0x134;
  char buf[17];
  std::strncpy(buf, title, 16);
  return std::string(buf);
}

void Cartridge::PrintCartridgeDebug() {
  std::string title = Title();
  INFOF("Cartridge: %s", title.c_str());

  uint8_t type_byte = Read8(kCartridgeTypeAddress);
  auto type = kCartridgeTypes.find(type_byte);
  if (type == kCartridgeTypes.end()) {
    INFOF("Unknown Cartridge Type: 0x%01x", type_byte & 0xff);
  } else {
    INFOF("Cartridge Type (0x%01x): %s",
        type_byte & 0xff, type->second.c_str());
  }

  uint8_t rom_size_byte = Read8(0x148);
  auto rom_size = kRomSizes.find(rom_size_byte);
  if (rom_size == kRomSizes.end()) {
    INFOF("Unrecognized ROM Size: 0x%01x", rom_size_byte & 0xff);
  } else {
    INFOF("ROM Size (0x%01x): %s",
        rom_size_byte & 0xff, rom_size->second.c_str());
  }

  uint8_t ram_size_byte = Read8(0x149);
  auto ram_size = kRamSizes.find(ram_size_byte);
  if (ram_size == kRamSizes.end()) {
    INFOF("Unrecognized RAM Size: 0x%01x", ram_size_byte & 0xff);
  } else {
    INFOF("RAM Size (0x%01x): %s",
        ram_size_byte & 0xff, ram_size->second.c_str());
  }
}

}  // namespace dgb
<commit_msg>Added RAM banking for MBC3<commit_after>#include "cartridge.h"

#include <map>
#include <string>

#include "common/logging.h"

namespace dgb {

namespace {

const uint16_t kCartridgeTypeAddress = 0x147;

const std::map<int, std::string> kCartridgeTypes = {
  {0x00, "ROM ONLY"},
  {0x01, "MBC1"},
  {0x02, "MBC1+RAM"},
  {0x03, "MBC1+RAM+BATTERY"},
  {0x05, "MBC2"},
  {0x06, "MBC2+BATTERY"},
  {0x08, "ROM+RAM"},
  {0x09, "ROM+RAM+BATTERY"},
  {0x0B, "MMM01"},
  {0x0C, "MMM01+RAM"},
  {0x0D, "MMM01+RAM+BATTERY"},
  {0x0F, "MBC3+TIMER+BATTERY"},
  {0x10, "MBC3+TIMER+RAM+BATTERY"},
  {0x11, "MBC3"},
  {0x12, "MBC3+RAM"},
  {0x13, "MBC3+RAM+BATTERY"},
  {0x15, "MBC4"},
  {0x16, "MBC4+RAM"},
  {0x17, "MBC4+RAM+BATTERY"},
  {0x19, "MBC5"},
  {0x1A, "MBC5+RAM"},
  {0x1B, "MBC5+RAM+BATTERY"},
  {0x1C, "MBC5+RUMBLE"},
  {0x1D, "MBC5+RUMBLE+RAM"},
  {0x1E, "MBC5+RUMBLE+RAM+BATTERY"}
  // Unsupported types:
  // 0xFC  POCKET CAMERA
  // 0xFD  BANDAI TAMA5
  // 0xFE  HuC3
  // 0xFF  HuC1+RAM+BATTERY
};

const std::map<uint8_t, std::string> kRomSizes = {
  {0x00, "32KByte (no ROM banking)"},
  {0x01, "64KByte (4 banks)"},
  {0x02, "128KByte (8 banks)"},
  {0x03, "256KByte (16 banks)"},
  {0x04, "512KByte (32 banks)"},
  {0x05, "1MByte (64 banks)  - only 63 banks used by MBC1"},
  {0x06, "2MByte (128 banks) - only 125 banks used by MBC1"},
  {0x07, "4MByte (256 banks)"},
  {0x52, "1.1MByte (72 banks)"},
  {0x53, "1.2MByte (80 banks)"},
  {0x54, "1.5MByte (96 banks)"}
};

const std::map<uint8_t, std::string> kRamSizes = {
  {0x00, "None"},
  {0x01, "2 KBytes"},
  {0x02, "8 Kbytes"},
  {0x03, "32 KBytes (4 banks of 8KBytes each)"}
};

class MBCNone : public MemoryBankController {
 public:
  MBCNone(std::shared_ptr<MMapFile> rom) : MemoryBankController(rom) {}

  uint8_t Read8(uint16_t offset) override {
    if (offset > 0x7FFF) {
      FATALF("MBCNone: unsupported read offset: 0x%04x", offset);
    }
    return *(reinterpret_cast<uint8_t*>(rom_->memory()) + offset);
  }

  void Write8(uint16_t offset, uint8_t value) override {
    if (0x2000 <= offset && offset <= 0x3FFF) {
      if (value > 1) {
        FATALF("MBCNone does not support ROM bank 0x%02x", value & 0xff);
      }
      return;
    }
    FATALF("MBCNone: Unimplemented Write to Cartridge: (0x%04x) <- 0x%02x",
        offset & 0xffff, value & 0xff);
  }
};

class MBC1 : public MemoryBankController {
 public:
  MBC1(std::shared_ptr<MMapFile> rom) : MemoryBankController(rom) {}

  uint8_t Read8(uint16_t offset) override {
    if (offset > 0x7FFF) {
      FATALF("TODO: MBC1 unsupported read offset: 0x%04x", offset);
    }
    if (0x4000 <= offset && offset < 0x8000) {
      return *(reinterpret_cast<uint8_t*>(rom_->memory())
          + (0x4000 * rom_bank_) + (offset - 0x4000));
    }
    return *(reinterpret_cast<uint8_t*>(rom_->memory()) + offset);
  }

  void Write8(uint16_t offset, uint8_t value) override {
    if (0x2000 <= offset && offset < 0x4000) {
      // Lower 5 bits of ROM bank.
      rom_bank_ = (rom_bank_ & 0xe0) | (value & 0x1f);
      if (rom_bank_ == 0 || rom_bank_ == 20 ||
          rom_bank_ == 40 || rom_bank_ == 60) {
        rom_bank_ += 1;
      }
      return;
    }
    if (0x4000 <= offset && offset < 0x6000) {
      // RAM bank, or upper 2 bits of ROM bank.
      rom_bank_ = (rom_bank_ & 0x1f) | (value & 0xe0);
      if (rom_bank_ == 0 || rom_bank_ == 20 ||
          rom_bank_ == 40 || rom_bank_ == 60) {
        rom_bank_ += 1;
      }
      return;
    }
    if (0xA000 <= offset && offset < 0xC000) {
      // TODO: RAM
      ERRORF("TODO: MBC1: RAM Write to Cartridge: (0x%04x) <- 0x%02x",
          offset & 0xffff, value & 0xff);
      return;
    }
    FATALF("MBC1: Unimplemented Write to Cartridge: (0x%04x) <- 0x%02x",
        offset & 0xffff, value & 0xff);
  }

 private:
  int rom_bank_ = 0;
};

class MBC3 : public MemoryBankController {
 public:
  MBC3(std::shared_ptr<MMapFile> rom) : MemoryBankController(rom) {}

  uint8_t Read8(uint16_t offset) override {
    if (offset < 0x4000) {
      return *(reinterpret_cast<uint8_t*>(rom_->memory()) + offset);
    }
    if (0x4000 <= offset && offset < 0x8000) {
      uint32_t bank_offset = (0x4000 * rom_bank_) + (offset - 0x4000);
      if (bank_offset >= rom_->size()) {
        FATALF("Attempted to read past the end of the ROM file");
      }
      return *(reinterpret_cast<uint8_t*>(rom_->memory()) + bank_offset);
    }
    if (0xA000 <= offset && offset < 0xC000) {
      uint32_t bank_offset = (0x2000 * ram_bank_) + (offset - 0xA000);
      return ram_[bank_offset];
    }

    FATALF("TODO: MBC3 unimplemented read offset: 0x%04x", offset);
    return 0;
  }

  void Write8(uint16_t offset, uint8_t value) override {
    if (offset < 0x2000) {
      // TODO: support enable/disable of RAM & RTC so we know when to save to
      // disk
      return;
    }

    if (0x2000 <= offset && offset < 0x4000) {
      // All 7 bits of rom bank.
      rom_bank_ = value & 0x7f;
      if (rom_bank_ == 0) rom_bank_ = 1;
      return;
    }
    if (0x4000 <= offset && offset < 0x6000) {
      if (value > 0x3) { FATALF("TODO: implement MBC3 RTC registers"); }

      // RAM bank number 0x00-0x03.
      ram_bank_ = (value & 0x3);
      return;
    }
    if (0xA000 <= offset && offset < 0xC000) {
      uint32_t bank_offset = (0x2000 * ram_bank_) + (offset - 0xA000);
      ram_[bank_offset] = value;
      return;
    }
    FATALF("MBC3: Unimplemented Write to Cartridge: (0x%04x) <- 0x%02x",
        offset & 0xffff, value & 0xff);
  }

 private:
  int rom_bank_ = 0;
  int ram_bank_ = 0;

  // 32kb of RAM.
  uint8_t ram_[0x8000];
};
}  // namespace

Cartridge::Cartridge(const std::string &filename)
    : rom_(new MMapFile(filename)) {
  uint8_t type_byte = rom_->Read8(kCartridgeTypeAddress);
  switch (type_byte) {
    case 0x0:
      mbc_.reset(new MBCNone(rom_));
      break;
    case 0x1:
      mbc_.reset(new MBC1(rom_));
      break;
    case 0x13:
      mbc_.reset(new MBC3(rom_));
      break;
    default:
      FATALF("Unsupported cartridge type: 0x%02x", type_byte & 0xff);
  }
}

uint8_t Cartridge::Read8(uint16_t offset) {
  return mbc_->Read8(offset);

  if (0x3FFF < offset && offset < 0x8000 && rom_bank_ > 1) {
    FATALF("NOT IMPLEMENTED: rom banking");
  }
  return *(reinterpret_cast<uint8_t*>(rom_->memory()) + offset);
}

void Cartridge::Write8(uint16_t offset, uint8_t value) {
  //if (offset < 0x8000) {
  //  ERRORF("Unimplemented Write to Cartridge RAM: (0x%04x) <- 0x%02x",
  //      offset & 0xffff, value & 0xff);
  //  return;
  //}
  //if (0xA000 <= offset && offset <= 0xBFFF) {
  //  ERRORF("Unimplemented Write to Cartridge RAM: (0x%04x) <- 0x%02x",
  //      offset & 0xffff, value & 0xff);
  //  return;
  //}
  mbc_->Write8(offset, value);
  return;
}

std::string Cartridge::Title() {
  char *title = reinterpret_cast<char*>(rom_->memory()) + 0x134;
  char buf[17];
  std::strncpy(buf, title, 16);
  return std::string(buf);
}

void Cartridge::PrintCartridgeDebug() {
  std::string title = Title();
  INFOF("Cartridge: %s", title.c_str());

  uint8_t type_byte = Read8(kCartridgeTypeAddress);
  auto type = kCartridgeTypes.find(type_byte);
  if (type == kCartridgeTypes.end()) {
    INFOF("Unknown Cartridge Type: 0x%01x", type_byte & 0xff);
  } else {
    INFOF("Cartridge Type (0x%01x): %s",
        type_byte & 0xff, type->second.c_str());
  }

  uint8_t rom_size_byte = Read8(0x148);
  auto rom_size = kRomSizes.find(rom_size_byte);
  if (rom_size == kRomSizes.end()) {
    INFOF("Unrecognized ROM Size: 0x%01x", rom_size_byte & 0xff);
  } else {
    INFOF("ROM Size (0x%01x): %s",
        rom_size_byte & 0xff, rom_size->second.c_str());
  }

  uint8_t ram_size_byte = Read8(0x149);
  auto ram_size = kRamSizes.find(ram_size_byte);
  if (ram_size == kRamSizes.end()) {
    INFOF("Unrecognized RAM Size: 0x%01x", ram_size_byte & 0xff);
  } else {
    INFOF("RAM Size (0x%01x): %s",
        ram_size_byte & 0xff, ram_size->second.c_str());
  }
}

}  // namespace dgb
<|endoftext|>