Memory added plus basic memory operations
This commit is contained in:
@@ -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:
|
||||
@@ -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
19
include/execute/error.hpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
namespace Execute
|
||||
{
|
||||
struct RuntimeError
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
struct StackUnderflow : RuntimeError
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
struct StackOverflow : RuntimeError
|
||||
{
|
||||
|
||||
};
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
@@ -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
6
src/execute/error.cpp
Normal file
@@ -0,0 +1,6 @@
|
||||
#include <execute/error.hpp>
|
||||
|
||||
namespace Execute
|
||||
{
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
100
src/main.cpp
100
src/main.cpp
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user