Memory added plus basic memory operations

This commit is contained in:
2019-11-23 14:53:56 +01:00
parent 99f616e1e4
commit 22bb974a05
17 changed files with 232 additions and 68 deletions

View File

@@ -53,6 +53,10 @@ All symbols are reserved keywords and can therefore NOT be used as labels.
## Symbols
## Directives
- `DECLARE` declares the first label argument to equal the second, immediate value, argument and is used to declare a constant for the virtual machine.
### Operands
- `addi` add the first to the second argument and store the result in the third argument
@@ -71,6 +75,11 @@ All symbols are reserved keywords and can therefore NOT be used as labels.
- `gti` execute next statement if argument 1 is greater than argument 2 else skip the next statement
- `eqi` execute the next statement if argument 1 is equal to argument 2 else skip the next statement
## Memory
- `popi` pops the first value on the stack into the register specified as the first argument
- `pushi` pushes the value on the stack from the register or immediate value as the first argument
## Interupts
- 0..9 range:

View File

@@ -1,3 +1,5 @@
DECLARE MEMORY_SIZE $4096;
addi $10 $-5 %A;
subi %A $2 %B;
muli $2 %B %C;
@@ -7,7 +9,7 @@ divi $2 %C %D;
seti %A $0;
# Loop from 0 to 10
loop:
count_loop:
addi $1 %A %A;
# Print the current value
@@ -18,7 +20,7 @@ int $0;
seti %A %B;
lti %A $10;
jmp loop;
jmp count_loop;
# Hello world
seti %A $72; # H
@@ -31,5 +33,24 @@ int $0;
seti %A $111; # o
int $0;
seti %A $32; # space
int $0;
pushi $68; # D
pushi $76; # L
pushi $82; # R
pushi $79; # O
pushi $87; # W
seti %B $5;
world_loop:
popi %A;
int $0;
subi %B $1 %B;
gti %B $0;
jmp world_loop;
seti %A $10; # newline
int $0;

19
include/execute/error.hpp Normal file
View File

@@ -0,0 +1,19 @@
#pragma once
namespace Execute
{
struct RuntimeError
{
};
struct StackUnderflow : RuntimeError
{
};
struct StackOverflow : RuntimeError
{
};
}

View File

@@ -11,7 +11,9 @@ namespace Execute
unsigned nextStatement;
std::unordered_map<std::string, unsigned> const & labelStatementIndice;
std::vector<InterruptFn> const interrupts;
std::vector<std::uint8_t> memory;
unsigned stackPointer;
State(std::unordered_map<std::string, unsigned> const & labelStatementIndice);
State(std::unordered_map<std::string, unsigned> const & labelStatementIndice, unsigned const memorySize);
};
}

View File

@@ -17,6 +17,8 @@ namespace Execute
void Step();
VirtualMachine(Interpret::Code const & code, unsigned const memorySize);
public:
void Run();
void SingleStep();
@@ -28,6 +30,6 @@ namespace Execute
bool IsTerminated() const;
VirtualMachine(Interpret::Code const & code);
static VirtualMachine CreateFromCode(Interpret::Code const & code);
};
}

View File

@@ -11,5 +11,6 @@ namespace Interpret
{
std::vector<std::unique_ptr<Statement>> statements;
std::unordered_map<std::string, unsigned> labelStatementIndice;
std::unordered_map<std::string, int> declarations;
};
}

View File

@@ -7,5 +7,7 @@ namespace Interpret
{
std::unique_ptr<Statement> ExtractStatement(unsigned const operatorIndex, std::vector<Token::Token> const & tokens);
std::tuple<std::string, int> ExtractDeclaration(unsigned const operatorIndex, std::vector<Token::Token> const & tokens);
int GetRequiredNumberOfArguments(Token::OperandType const type);
}

View File

@@ -69,4 +69,18 @@ namespace Interpret
void Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) override;
};
struct PushStatement : Statement
{
Value firstArgument;
void Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) override;
};
struct PopStatement : Statement
{
Value firstArgument;
void Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) override;
};
}

View File

@@ -17,7 +17,10 @@ namespace Token
GreaterThanInteger,
EqualInteger,
SetInteger,
Interrupt
Interrupt,
Declaration,
PushInteger,
PopInteger
};
OperandType GetOperandType(std::string const & op);

6
src/execute/error.cpp Normal file
View File

@@ -0,0 +1,6 @@
#include <execute/error.hpp>
namespace Execute
{
}

View File

@@ -2,11 +2,13 @@
namespace Execute
{
State::State(std::unordered_map<std::string, unsigned> const & _labelStatementIndice)
State::State(std::unordered_map<std::string, unsigned> const & _labelStatementIndice, unsigned const memorySize)
: currentStatement(0u),
nextStatement(1u),
labelStatementIndice(_labelStatementIndice),
interrupts(GetInterrupts())
interrupts(GetInterrupts()),
memory(memorySize),
stackPointer(0u)
{
}
}

View File

@@ -15,6 +15,15 @@ namespace Execute
}
}
VirtualMachine::VirtualMachine(Interpret::Code const & _code, unsigned const memorySize)
: flags(),
registers(),
state(_code.labelStatementIndice, memorySize),
terminated(false),
code(_code)
{
}
void VirtualMachine::Run()
{
while(!IsTerminated())
@@ -41,12 +50,16 @@ namespace Execute
bool VirtualMachine::IsTerminated() const { return terminated; }
VirtualMachine::VirtualMachine(Interpret::Code const & _code)
: flags(),
registers(),
state(_code.labelStatementIndice),
terminated(false),
code(_code)
VirtualMachine VirtualMachine::CreateFromCode(Interpret::Code const & code)
{
unsigned memorySize = 1024;
auto const & userMemorySizeDeclared = code.declarations.find("MEMORY_SIZE");
if (userMemorySizeDeclared != code.declarations.end() && userMemorySizeDeclared->second >= 0)
{
memorySize = userMemorySizeDeclared->second;
}
std::printf("Creating Wassembly virtual machine with %u bytes of memory.\n", memorySize);
return VirtualMachine(code, memorySize);
}
}

View File

@@ -67,7 +67,19 @@ namespace Interpret
}
else
{
code.statements.emplace_back(ExtractStatement(operatorTokenIndex, tokens));
switch(std::get<Token::OperandType>(tokens[operatorTokenIndex].data))
{
case Token::OperandType::Declaration:
{
auto const tuple = ExtractDeclaration(operatorTokenIndex, tokens);
code.declarations[std::get<0>(tuple)] = std::get<1>(tuple);
}
break;
default:
code.statements.emplace_back(ExtractStatement(operatorTokenIndex, tokens));
break;
}
state = InterpreterState::FindOperand;
}
break;

View File

@@ -210,6 +210,22 @@ namespace Interpret
return statement;
}
case Token::OperandType::PopInteger:
{
auto statement = std::make_unique<PopStatement>();
statement->firstArgument = GetRegisterArgument(operandIndex + 1u, tokens);
return statement;
}
case Token::OperandType::PushInteger:
{
auto statement = std::make_unique<PushStatement>();
statement->firstArgument = GetImmediateOrRegisterArgument(operandIndex + 1u, tokens);
return statement;
}
default:
{
auto statement = std::make_unique<NoArgumentStatement>();
@@ -221,6 +237,22 @@ namespace Interpret
}
}
std::tuple<std::string, int> ExtractDeclaration(unsigned const operatorIndex, std::vector<Token::Token> const & tokens)
{
if (tokens[operatorIndex + 1u].type != Token::TokenType::Label)
{
throw ExpectedLabel(tokens[operatorIndex + 1u]);
}
if (tokens[operatorIndex + 2u].type != Token::TokenType::ImmediateInteger)
{
throw ExpectedImmediate(tokens[operatorIndex + 2u]);
}
auto const label = std::get<std::string>(tokens[operatorIndex + 1u].data);
auto const value = std::get<int>(tokens[operatorIndex + 2u].data);
return std::make_tuple(label, value);
}
int GetRequiredNumberOfArguments(Token::OperandType const type)
{
switch (type)
@@ -237,10 +269,13 @@ namespace Interpret
case Token::OperandType::GreaterThanInteger:
case Token::OperandType::EqualInteger:
case Token::OperandType::SetInteger:
case Token::OperandType::Declaration:
return 2;
case Token::OperandType::Jump:
case Token::OperandType::Interrupt:
case Token::OperandType::PushInteger:
case Token::OperandType::PopInteger:
return 1;
default:

View File

@@ -1,3 +1,4 @@
#include <execute/error.hpp>
#include <interpret/statement.hpp>
namespace Interpret
@@ -49,4 +50,29 @@ namespace Interpret
{
state.interrupts[firstArgument.GetValue(registers)](registers);
}
void PopStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers)
{
if (state.stackPointer < sizeof(int))
{
throw Execute::StackUnderflow();
}
auto const value = *(reinterpret_cast<int const *>(state.memory.data() + (state.stackPointer - 1u)));
firstArgument.GetValue(registers) = value;
state.stackPointer -= sizeof(int);
}
void PushStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers)
{
if ((state.memory.size() - state.stackPointer) < sizeof(int))
{
throw Execute::StackOverflow();
}
*(reinterpret_cast<int *>(state.memory.data() + state.stackPointer + (sizeof(int) - 1))) = firstArgument.GetValue(registers);
state.stackPointer += sizeof(int);
}
}

View File

@@ -24,35 +24,8 @@ void PrintTokenError(Interpret::TokenError const & err, std::vector<std::string>
PrintBadToken(err.errorToken, lines);
}
int main(int argc, char ** argv)
void PrintTokens(std::vector<Token::Token> const & tokens)
{
if (argc != 2)
{
std::puts("Usage: wassembly [filename.wasm]");
return 1;
}
std::ifstream input(argv[1]);
if (!input.is_open())
{
std::printf("Error: Cannot open file %s for reading", argv[1]);
return 1;
}
std::vector<Token::Token> tokens;
Token::Tokenizer tokenizer;
std::vector<std::string> lines; // DEBUG
std::string line;
unsigned lineNumber = 0;
while(std::getline(input, line))
{
tokenizer.Tokenize(line, lineNumber, tokens);
++lineNumber;
lines.push_back(line); // DEBUG
}
input.close();
// DEBUG
std::puts("*** Tokenization result ***");
unsigned statementNumber = 0u;
std::printf("%02u - ", statementNumber);
@@ -73,7 +46,38 @@ int main(int argc, char ** argv)
}
}
}
// END DEBUG
}
void PrintLabels(Interpret::Code const & code)
{
std::puts("\n*** Labels ***");
for(auto const & labelIndice : code.labelStatementIndice)
{
std::printf("Label %s points to statement %u\n", labelIndice.first.c_str(), labelIndice.second);
}
}
Interpret::Code GetCodeFromFile(std::string const & filePath)
{
std::ifstream input(filePath);
if (!input.is_open())
{
std::printf("Error: Cannot open file %s for reading", filePath.c_str());
exit(1);
}
std::vector<Token::Token> tokens;
Token::Tokenizer tokenizer;
std::vector<std::string> lines;
std::string line;
unsigned lineNumber = 0;
while(std::getline(input, line))
{
tokenizer.Tokenize(line, lineNumber, tokens);
++lineNumber;
lines.push_back(line);
}
input.close();
// Validate the syntax
bool syntaxOk = true;
@@ -90,7 +94,7 @@ int main(int argc, char ** argv)
if (!syntaxOk)
{
std::puts("Aborting due to syntax error(s)");
return 1;
exit(1);
}
Interpret::Interpreter interpreter;
@@ -102,33 +106,23 @@ int main(int argc, char ** argv)
catch(Interpret::TokenError & e)
{
PrintTokenError(e, lines);
exit(1);
}
return code;
}
int main(int argc, char ** argv)
{
if (argc != 2)
{
std::puts("Usage: wassembly [filename.wasm]");
return 1;
}
// DEBUG
std::puts("\n*** Labels ***");
for(auto const & labelIndice : code.labelStatementIndice)
{
std::printf("Label %s points to statement %u\n", labelIndice.first.c_str(), labelIndice.second);
}
std::puts("\n*** Execution ***");
Execute::VirtualMachine vm(code);
auto const code = GetCodeFromFile(argv[1]);
auto vm = Execute::VirtualMachine::CreateFromCode(code);
vm.Run();
/*
while(!vm.IsTerminated())
{
vm.SingleStep();
auto const & registers = vm.GetRegisters();
std::printf("A=%i B=%i C=%i D=%i\n", registers.A, registers.B, registers.C, registers.D);
auto const & state = vm.GetState();
std::printf("current_statement=%i\n", state.currentStatement);
std::puts("Press any key to step...");
std::getchar();
}
*/
// END DEBUG
return 0;
}

View File

@@ -18,7 +18,10 @@ namespace Token
{ "gti", OperandType::GreaterThanInteger },
{ "eqi", OperandType::EqualInteger },
{ "seti", OperandType::SetInteger },
{ "int", OperandType::Interrupt }
{ "int", OperandType::Interrupt },
{ "DECLARE", OperandType::Declaration },
{ "pushi", OperandType::PushInteger},
{ "popi", OperandType::PopInteger},
};
auto const & result = operations.find(op);