// license:BSD-3-Clause // copyright-holders:Aaron Giles /*************************************************************************** uml.c Universal machine language definitions and classes. **************************************************************************** Future improvements/changes: * UML optimizer: - constant folding * Write a back-end validator: - checks all combinations of memory/register/immediate on all params - checks behavior of all opcodes * Extend registers to 16? Depends on if PPC can use them * Support for FPU exceptions * New instructions? - VALID opcode_desc,handle,param checksum/compare code referenced by opcode_desc; if not matching, generate exception with handle,param - RECALL handle change code at caller to call handle in the future ***************************************************************************/ #include "emu.h" #include "drcuml.h" #include "drcumlsh.h" using namespace uml; //************************************************************************** // DEBUGGING //************************************************************************** #define VALIDATE_BACKEND (0) #define LOG_SIMPLIFICATIONS (0) //************************************************************************** // CONSTANTS //************************************************************************** // opcode validation condition/flag valid bitmasks #define OPFLAGS_NONE 0x00 #define OPFLAGS_C FLAG_C #define OPFLAGS_Z FLAG_Z #define OPFLAGS_SZ (FLAG_S | FLAG_Z) #define OPFLAGS_SZC (FLAG_S | FLAG_Z | FLAG_C) #define OPFLAGS_SZV (FLAG_S | FLAG_Z | FLAG_V) #define OPFLAGS_SZVC (FLAG_S | FLAG_Z | FLAG_V | FLAG_C) #define OPFLAGS_UZC (FLAG_U | FLAG_Z | FLAG_C) #define OPFLAGS_ALL 0x1f #define OPFLAGS_P1 0x81 #define OPFLAGS_P2 0x82 #define OPFLAGS_P3 0x83 #define OPFLAGS_P4 0x84 // parameter input/output states #define PIO_IN 0x01 #define PIO_OUT 0x02 #define PIO_INOUT (PIO_IN | PIO_OUT) // parameter sizes #define PSIZE_4 SIZE_DWORD #define PSIZE_8 SIZE_QWORD #define PSIZE_OP 0x80 #define PSIZE_P1 0x81 #define PSIZE_P2 0x82 #define PSIZE_P3 0x83 #define PSIZE_P4 0x84 // basic parameter types #define PTYPES_NONE 0 #define PTYPES_IMM (1 << parameter::PTYPE_IMMEDIATE) #define PTYPES_IREG (1 << parameter::PTYPE_INT_REGISTER) #define PTYPES_FREG (1 << parameter::PTYPE_FLOAT_REGISTER) #define PTYPES_VREG (1 << parameter::PTYPE_VECTOR_REGISTER) #define PTYPES_MVAR (1 << parameter::PTYPE_MAPVAR) #define PTYPES_MEM (1 << parameter::PTYPE_MEMORY) #define PTYPES_SIZE (1 << parameter::PTYPE_SIZE) #define PTYPES_SCSIZE (1 << parameter::PTYPE_SIZE_SCALE) #define PTYPES_SPSIZE (1 << parameter::PTYPE_SIZE_SPACE) #define PTYPES_HANDLE (1 << parameter::PTYPE_CODE_HANDLE) #define PTYPES_LABEL (1 << parameter::PTYPE_CODE_LABEL) #define PTYPES_CFUNC (1 << parameter::PTYPE_C_FUNCTION) #define PTYPES_ROUND (1 << parameter::PTYPE_ROUNDING) #define PTYPES_STR (1 << parameter::PTYPE_STRING) // special parameter types #define PTYPES_PTR (PTYPES_MEM | 0x1000) #define PTYPES_STATE (PTYPES_MEM | 0x2000) // combinations of types #define PTYPES_IRM (PTYPES_IREG | PTYPES_MEM) #define PTYPES_FRM (PTYPES_FREG | PTYPES_MEM) #define PTYPES_IMV (PTYPES_IMM | PTYPES_MVAR) #define PTYPES_IANY (PTYPES_IRM | PTYPES_IMV) #define PTYPES_FANY (PTYPES_FRM) //************************************************************************** // TABLES //************************************************************************** // macro to simplify the table #define PINFO(inout, size, types) { PIO_##inout, PSIZE_##size, PTYPES_##types } #define OPINFO0(op,str,sizes,cond,iflag,oflag,mflag) { OP_##op, str, sizes, cond, OPFLAGS_##iflag, OPFLAGS_##oflag, OPFLAGS_##mflag, { { 0 } } }, #define OPINFO1(op,str,sizes,cond,iflag,oflag,mflag,p0) { OP_##op, str, sizes, cond, OPFLAGS_##iflag, OPFLAGS_##oflag, OPFLAGS_##mflag, { p0 } }, #define OPINFO2(op,str,sizes,cond,iflag,oflag,mflag,p0,p1) { OP_##op, str, sizes, cond, OPFLAGS_##iflag, OPFLAGS_##oflag, OPFLAGS_##mflag, { p0, p1 } }, #define OPINFO3(op,str,sizes,cond,iflag,oflag,mflag,p0,p1,p2) { OP_##op, str, sizes, cond, OPFLAGS_##iflag, OPFLAGS_##oflag, OPFLAGS_##mflag, { p0, p1, p2 } }, #define OPINFO4(op,str,sizes,cond,iflag,oflag,mflag,p0,p1,p2,p3) { OP_##op, str, sizes, cond, OPFLAGS_##iflag, OPFLAGS_##oflag, OPFLAGS_##mflag, { p0, p1, p2, p3 } }, // opcode validation table opcode_info const instruction::s_opcode_info_table[OP_MAX] = { OPINFO0(INVALID, "invalid", 4, false, NONE, NONE, NONE) // Compile-time opcodes OPINFO1(HANDLE, "handle", 4, false, NONE, NONE, NONE, PINFO(IN, OP, HANDLE)) OPINFO2(HASH, "hash", 4, false, NONE, NONE, NONE, PINFO(IN, OP, IMV), PINFO(IN, OP, IMV)) OPINFO1(LABEL, "label", 4, false, NONE, NONE, NONE, PINFO(IN, OP, LABEL)) OPINFO1(COMMENT, "comment", 4, false, NONE, NONE, NONE, PINFO(IN, OP, STR)) OPINFO2(MAPVAR, "mapvar", 4, false, NONE, NONE, NONE, PINFO(OUT, OP, MVAR), PINFO(IN, OP, IMV)) // Control Flow Operations OPINFO0(NOP, "nop", 4, false, NONE, NONE, NONE) OPINFO1(DEBUG, "debug", 4, false, NONE, NONE, ALL, PINFO(IN, OP, IANY)) OPINFO1(EXIT, "exit", 4, true, NONE, NONE, ALL, PINFO(IN, OP, IANY)) OPINFO3(HASHJMP, "hashjmp", 4, false, NONE, NONE, ALL, PINFO(IN, OP, IANY), PINFO(IN, OP, IANY), PINFO(IN, OP, HANDLE)) OPINFO1(JMP, "jmp", 4, true, NONE, NONE, NONE, PINFO(IN, OP, LABEL)) OPINFO2(EXH, "exh", 4, true, NONE, NONE, ALL, PINFO(IN, OP, HANDLE), PINFO(IN, OP, IANY)) OPINFO1(CALLH, "callh", 4, true, NONE, NONE, ALL, PINFO(IN, OP, HANDLE)) OPINFO0(RET, "ret", 4, true, NONE, NONE, ALL) OPINFO2(CALLC, "callc", 4, true, NONE, NONE, ALL, PINFO(IN, OP, CFUNC), PINFO(IN, OP, PTR)) OPINFO2(RECOVER, "recover", 4, false, NONE, NONE, ALL, PINFO(OUT, OP, IRM), PINFO(IN, OP, MVAR)) // Internal Register Operations OPINFO1(SETFMOD, "setfmod", 4, false, NONE, NONE, ALL, PINFO(IN, OP, IANY)) OPINFO1(GETFMOD, "getfmod", 4, false, NONE, NONE, ALL, PINFO(OUT, OP, IRM)) OPINFO1(GETEXP, "getexp", 4, false, NONE, NONE, ALL, PINFO(OUT, OP, IRM)) OPINFO2(GETFLGS, "getflgs", 4, false, P2, NONE, ALL, PINFO(OUT, OP, IRM), PINFO(IN, OP, IMV)) OPINFO1(SAVE, "save", 4, false, ALL, NONE, ALL, PINFO(OUT, OP, STATE)) OPINFO1(RESTORE, "restore", 4, false, NONE, ALL, ALL, PINFO(IN, OP, STATE)) // Integer Operations OPINFO4(LOAD, "!load", 4|8, false, NONE, NONE, ALL, PINFO(OUT, OP, IRM), PINFO(IN, OP, PTR), PINFO(IN, 4, IANY), PINFO(IN, OP, SCSIZE)) OPINFO4(LOADS, "!loads", 4|8, false, NONE, NONE, ALL, PINFO(OUT, OP, IRM), PINFO(IN, OP, PTR), PINFO(IN, 4, IANY), PINFO(IN, OP, SCSIZE)) OPINFO4(STORE, "!store", 4|8, false, NONE, NONE, ALL, PINFO(IN, OP, PTR), PINFO(IN, 4, IANY), PINFO(IN, OP, IANY), PINFO(IN, OP, SCSIZE)) OPINFO3(READ, "!read", 4|8, false, NONE, NONE, ALL, PINFO(OUT, OP, IRM), PINFO(IN, 4, IANY), PINFO(IN, OP, SPSIZE)) OPINFO4(READM, "!readm", 4|8, false, NONE, NONE, ALL, PINFO(OUT, OP, IRM), PINFO(IN, 4, IANY), PINFO(IN, OP, IANY), PINFO(IN, OP, SPSIZE)) OPINFO3(WRITE, "!write", 4|8,
Suites
--------
`SUITE(Name)`: Organizes your tests into suites (groups). Tests can be added to suites across multiple test source files. e.g.:
```cpp
SUITE(MySuite)
{
// tests go here
}
```
Tests
-------
`TEST(Name)`: Creates a single test case. All checks in a test will be run using the standard runners, unless an exception is thrown or an early return is introduced.
```cpp
TEST(MyTest)
{
// checks go here
}
```
`TEST_FIXTURE(FixtureClass, TestName)`: Creates a single test case using a fixture. The FixtureClass is default instantiated before the test is run, then the test runs with access to anything `public` or `protected` in the fixture. Useful for sharing setup / teardown code.
```cpp
class MyFixture
{
public:
MyFixture() { // setup goes here }
~MyFixture() { // teardown goes here }
};
TEST_FIXTURE(MyFixture, MyFixtureTest)
{
// checks go here
}
```
Checks
--------
`CHECK(statement)`: Verifies the statement evaluates to true (not necessary boolean true / false).
```cpp
CHECK(true); // passes
CHECK(1 == 2); // fails
CHECK(0); // fails
```
`CHECK_EQUAL(expected, actual)`: Verifies that the actual value matches the expected. Note that conversions can occur. Requires `operator==` for the types of `expected` and `actual`, and requires the ability for both types to be streamed to `UnitTest::MemoryOutStream` using `operator<<`.
```cpp
CHECK_EQUAL(1, 1); // passes
CHECK_EQUAL("123", std::string("123")); //passes
CHECK_EQUAL((1.0 / 40.0), 0.025000000000000001); // passes... wait what? be careful with floating point types!
```
`CHECK_CLOSE(expected, actual, tolerance)`: Verifies that the actual value is within +/- tolerance of the expected value. This has the same requirements of the types involved as `CHECK_EQUAL`.
```cpp
CHECK_CLOSE(0.025000000000000002, (1.0 / 40.0), 0.000000000000000001); // passes
CHECK_CLOSE(0.025, (1.0 / 40.0), 0.000000000000000001); // also passes
CHECK_CLOSE(0.025000000000000020, (1.0 / 40.0), 0.000000000000000001); // fails
```
`CHECK_THROW(expression, ExpectedExceptionType)`: Verifies that the expression throws an exception that is polymorphically of the ExpectedExceptionType.
`CHECK_ARRAY_EQUAL(expected, actual, count)`: Like `CHECK_EQUAL`, but for arrays and containers that support random access (`operator[]`). `count` is the number of items in the array.
`CHECK_ARRAY_CLOSE(expected, actual, count, tolerance)`: Like `CHECK_CLOSE`, but for arrays and containers that support random access (`operator[]`). `count` is the number of items in the array.
`CHECK_ARRAY2D_CLOSE(expected, actual, rows, columns, tolerance)`: Like `CHECK_ARRAY_CLOSE` but for two-dimensional arrays.