Compare commits
57
Commits
fbef044905
...
parser
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
307519a2b1 | ||
|
|
d3d2b06dbd | ||
|
|
f38da9cc74 | ||
|
|
33309a2759 | ||
|
|
1f7989533b | ||
|
|
cd1258a724 | ||
|
|
be91e3c112 | ||
|
|
706f480e31 | ||
|
|
a949007c75 | ||
|
|
a0d5d62a34 | ||
|
|
f7cd87f13f | ||
|
|
55f4c32764 | ||
|
|
097eca383b | ||
|
|
984683bfc5 | ||
|
|
0f42ad2997 | ||
|
|
63438b551b | ||
|
|
e94e486e18 | ||
|
|
2b71ac05a0 | ||
|
|
02cf72902e | ||
|
|
afa84076c8 | ||
|
|
925f7703d6 | ||
|
|
a82a4c23ad | ||
|
|
f5c82d1c36 | ||
|
|
0b58e0124f | ||
|
|
2ae60a607c | ||
|
|
6d17dffcdf | ||
|
|
2518214585 | ||
|
|
7004fff659 | ||
|
|
13f8ff194b | ||
|
|
dac01ffd0c | ||
|
|
3c7056c5e6 | ||
|
|
6c7b8d5356 | ||
|
|
5f437b6869 | ||
|
|
e3b4293314 | ||
|
|
ae1e73e00f | ||
|
|
7385a807a5 | ||
|
|
0d3f8a460e | ||
|
|
c2111e2b88 | ||
|
|
8e9d1aefe8 | ||
|
|
0785f21805 | ||
|
|
e4a191a17e | ||
|
|
eb5e2c506e | ||
|
|
0d060248cb | ||
|
|
80bea6f1a8 | ||
|
|
a96f485da6 | ||
|
|
88d550f344 | ||
|
|
0e53f9798f | ||
|
|
03d5660677 | ||
|
|
4f6d5f652e | ||
|
|
64326937b1 | ||
|
|
89ab951c6a | ||
|
|
947de1d14c | ||
|
|
3c4cce0883 | ||
|
|
5265666178 | ||
|
|
2de3538d80 | ||
|
|
72525959f6 | ||
|
|
72abf6c4ab |
@@ -1,3 +1,6 @@
|
||||
assm
|
||||
obj/
|
||||
bin/
|
||||
*.bin
|
||||
docs/*
|
||||
!docs/*.tex
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
\documentclass[a4paper,12pt]{book}
|
||||
\usepackage{tikz}
|
||||
\usepackage{hyperref}
|
||||
\hypersetup{
|
||||
linktoc=all
|
||||
}
|
||||
|
||||
\title{Unnamed Machine}
|
||||
\author{A Very Terrible 16-bit Machine}
|
||||
|
||||
\newcommand{\OpcodeTable}[4] {
|
||||
\begin{tabular}{ c c c c }
|
||||
\hline
|
||||
Opcode & Mnemonic & Operand 1 & Operand 2 \\
|
||||
\hline\hline
|
||||
#1 & #2 & #3 & #4 \\
|
||||
\hline
|
||||
\end{tabular}
|
||||
}
|
||||
|
||||
\begin{document}
|
||||
\maketitle
|
||||
\tableofcontents
|
||||
\chapter{Overview}
|
||||
\section{Introduction}
|
||||
This is a very poorly thought out 16-bit machine, but you've got to start somewhere. Currently debating between a CPU status register, 80x86 style, or just placing arithmetic results into a predetermined register. This is more of a load–store architecture to try and keep the instruction set simple. The machine will be big-endian.
|
||||
\section{Registers}
|
||||
The following are the general purpose registers that can be used.
|
||||
\begin{itemize}
|
||||
\item[] R1
|
||||
\item[] ...
|
||||
\item[] R8
|
||||
\end{itemize}
|
||||
Additionally, there will be a special status register that will be a signed 16 bit register that the compare and jump instructions will read or write to when determining what action, if any, they'll take.
|
||||
\section{Memory Model}
|
||||
Memory will be implicitly mapped I/O. The bottom 3201 bytes of memory will be reserved for the keyboard input and graphics.
|
||||
A single byte is reserved for the keyboard's input. The current key will be stored in byte 0xF37E, with the most significant bit being a flag indicating that the keyboard
|
||||
is ready to be read from. This means that the character encoding is actually 7 bits.
|
||||
Video memory starts at 0xF37F (62335 decimal), and every byte represents an ASCII character in monochrome.
|
||||
\begin{figure}[!htb]
|
||||
\centering
|
||||
\begin{tikzpicture}
|
||||
\fill[gray!5] (0,0)rectangle(5,10);
|
||||
%\draw (0,10) .. controls (-2,6) and (-2,4) .. (0,1);
|
||||
%\draw (0,10) arc (0:180:3cm);
|
||||
\draw (0,10) -- (5,10);
|
||||
\draw (0,1) -- node[above] {Video 0xF37F} (5,1);
|
||||
\draw (0,0) -- (5,0);
|
||||
\node[label=right:Top 0x0000] at (5,10) {};
|
||||
\node[label=right:Bottom 0xFFFF] at (5,0) {};
|
||||
\end{tikzpicture}
|
||||
\caption{Memory Layout}
|
||||
\end{figure}
|
||||
\chapter{Instruction Set Architecture}
|
||||
\section{Instruction Encoding}
|
||||
Instructions are fixed to exactly one byte (8 bits).
|
||||
Instructions that work with two operands the register for operand one will be encoded in the three least significant bits. So an instruction with format XXXX X000 will use Register 1 and so forth all the way to XXXX X111, which will be Register 8.
|
||||
%https://tex.stackexchange.com/questions/32598/force-latex-image-to-appear-in-the-section-in-which-its-declared
|
||||
\begin{figure}[!htb]
|
||||
\centering
|
||||
\begin{tabular}{ c c }
|
||||
\hline
|
||||
Instruction & Register \\
|
||||
\hline\hline
|
||||
0000 0 & 000 \\
|
||||
\hline
|
||||
\end{tabular}
|
||||
\caption{Encoding Layout}
|
||||
\end{figure}
|
||||
\section{Notes}
|
||||
For the opcodes that load or store data at the assembly language level we could have the mnemonics
|
||||
"store" and "load" and have the assembler pick the opcode based on the inclusion of the word "byte"
|
||||
or "word" for two bytes. That would make the assembly easier to read but put a bit more work on the
|
||||
assembler.
|
||||
\section{STOB (Store Byte)}
|
||||
\OpcodeTable{0x20}{stob}{Register}{Address}\\[6pt]
|
||||
Stores a single byte (the lower nibble) from a register to a memory address.
|
||||
\section{STOW (Store Machine Word)}
|
||||
\OpcodeTable{0x20}{stow}{Register}{Address}\\[6pt]
|
||||
Stores a machine word from a register to a memory address.
|
||||
\section{LAA (Load Absolute Address)}
|
||||
\OpcodeTable{0x00}{laa}{Register}{Address}\\[6pt]
|
||||
Loads an address (2 bytes) into the register.
|
||||
\section{LODB (Load Byte)}
|
||||
\OpcodeTable{0x20}{lodb}{Register}{Register}\\[6pt]
|
||||
Loads a single byte into a register from a memory address in the second operand register, zeroing out the high nibble.
|
||||
\section{LODW (Load Machine Word)}
|
||||
\OpcodeTable{0x20}{lodw}{Register}{Register}\\[6pt]
|
||||
Loads a word into a register from a memory address in the second operand register.
|
||||
\section{LODWI (Load Immediate Word)}
|
||||
\OpcodeTable{0x00}{lodwi}{Register}{Constant}\\[6pt]
|
||||
Loads an immediate machine word into the register clearing the high nibble if the value is less then 256.
|
||||
\section{CMP (Compare)}
|
||||
\OpcodeTable{0x20}{cmp}{Register}{Register}\\[6pt]
|
||||
Compares two registers and somewhere sets a result in the status register.
|
||||
\section{CMPI (Compare Immediate)}
|
||||
\OpcodeTable{0x00}{cmpi}{Register}{Constant}\\[6pt]
|
||||
Compares an immediate 2 byte value to the contents of a register setting the status register accordingly.
|
||||
\section{ADD (Add)}
|
||||
\OpcodeTable{0x20}{add}{Register}{Register}\\[6pt]
|
||||
Performs addition on a register with a value from another (or the same) register.
|
||||
\section{SUB (Subtract)}
|
||||
\OpcodeTable{0x20}{sub}{Register}{Register}\\[6pt]
|
||||
Performs subtraction on a register with a value from another (or the same) register.
|
||||
\section{JMP (Jump)}
|
||||
\OpcodeTable{0x20}{jmp}{Address}{None}\\[6pt]
|
||||
Jumps unconditionally to a memory address.
|
||||
\section{JZ (Jump if Zero)}
|
||||
\OpcodeTable{0x20}{jz}{Register}{None}\\[6pt]
|
||||
Jumps to a memory address if the status flag is zero.
|
||||
\section{JG (Jump if Greater Than)}
|
||||
\OpcodeTable{0x70}{jg}{Address}{None}\\[6pt]
|
||||
Jump to the Address if the status flag is greater than zero.
|
||||
\section{JL (Jump if Less Than)}
|
||||
\OpcodeTable{0x00}{jl}{Address}{None}\\[6pt]
|
||||
Jumps to the address if the status flag is less than zero.
|
||||
\section{AND (Logical AND)}
|
||||
\OpcodeTable{0x00}{and}{Register}{Register}\\[6pt]
|
||||
Logical ANDs the two registers together storing the result in operand 1.
|
||||
\section{XOR (Logical Exclusive OR)}
|
||||
\OpcodeTable{0x00}{xor}{Register}{Register}\\[6pt]
|
||||
Logical XORs the two registers together storing the result in operand 1.
|
||||
\section{OR (Logical OR)}
|
||||
\OpcodeTable{0x00}{or}{Register}{Register}\\[6pt]
|
||||
Logical ORs the two registers together storing the result in operand 1.
|
||||
\section{NOT (Logical Negation)}
|
||||
\OpcodeTable{0x00}{not}{Register}{None}\\[6pt]
|
||||
Inverts the bits of the target register.
|
||||
\section{SHR (Shift Right)}
|
||||
\OpcodeTable{0x00}{shr}{Register}{Constant}\\[6pt]
|
||||
Bit-wise shifts the contents of the register right Constant number of times.
|
||||
\section{SHL (Shift Left)}
|
||||
\OpcodeTable{0x00}{shl}{Register}{Constant}\\[6pt]
|
||||
Bit-wise shifts the contents of the register left Constant number of times.
|
||||
\section{INC (Increment)}
|
||||
\OpcodeTable{0x00}{inc}{Register}{None}\\[6pt]
|
||||
Increments the contents of the register by one. Over-flows will not be reported.
|
||||
\section{DEC (Decrement)}
|
||||
\OpcodeTable{0x00}{dec}{Register}{None}\\[6pt]
|
||||
Decrements the contents of the register by one. Under-flows will not be reported.
|
||||
\section{NOP (No Operation)}
|
||||
\OpcodeTable{0x00}{nop}{None}{None}\\[6pt]
|
||||
Skips a clock cycle, incrementing the program counter.
|
||||
\end{document}
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifndef DIASSEMBLER_H
|
||||
#define DIASSEMBLER_H
|
||||
|
||||
#include "parser.h"
|
||||
|
||||
//void Disassemble(unsigned char image[HIGHMEMORY]);
|
||||
|
||||
#endif
|
||||
|
||||
+35
-22
@@ -2,34 +2,47 @@
|
||||
#define OPCODES_H
|
||||
|
||||
#include <string.h>
|
||||
#include "token.h"
|
||||
|
||||
#define OPCODECOUNT 10
|
||||
#define REGISTERCOUNT 8
|
||||
#include "symbols_table.h"
|
||||
|
||||
typedef enum {
|
||||
None = 0,
|
||||
Reg,
|
||||
Imm8,
|
||||
Imm16
|
||||
} ParameterType;
|
||||
R1, R2, R3, R4, R5, R6, R7, R8
|
||||
} Registers;
|
||||
|
||||
typedef enum {
|
||||
ADD, SUB, JZ, INT, YLD, RET,
|
||||
CMP, NOP, JMP, CALL, LOAD, JE, INC, DEC, LOADB
|
||||
} Mnemonic;
|
||||
|
||||
typedef enum {
|
||||
NoParameter,
|
||||
ConstantParameter,
|
||||
AddressParameter,
|
||||
RegisterParameter
|
||||
} OpcodeParameter;
|
||||
|
||||
typedef struct {
|
||||
char* lexeme;
|
||||
TokenType op;
|
||||
ParameterType parameter_one;
|
||||
ParameterType parameter_two;
|
||||
} OpCode;
|
||||
OpcodeParameter ParameterType;
|
||||
OpcodeParameter InterpretedAs;
|
||||
|
||||
union {
|
||||
int Number; //ConstanrParam
|
||||
Registers Register; //Register param
|
||||
Symbol* Symbol; //Address param
|
||||
} Value;
|
||||
} Parameter;
|
||||
|
||||
typedef struct {
|
||||
char* lexeme;
|
||||
TokenType type;
|
||||
} Register;
|
||||
Mnemonic Mnemonic;
|
||||
Parameter* ParameterOne;
|
||||
Parameter* ParameterTwo;
|
||||
} Instruction;
|
||||
|
||||
extern OpCode opcodes[OPCODECOUNT];
|
||||
extern Register registers[REGISTERCOUNT];
|
||||
|
||||
int IsOpcode(const char*, TokenType*);
|
||||
int IsRegister(const char*, TokenType*);
|
||||
Instruction* CreateInstruction(Mnemonic mnemonic);
|
||||
Parameter* CreateParameter(OpcodeParameter parameterType);
|
||||
void FreeInstruction(Instruction* instruction);
|
||||
int IsOpcode(const char*, Mnemonic*);
|
||||
int IsRegister(const char*, Registers*);
|
||||
void GetMnemonicText(Mnemonic mnemonic, char buffer[12]);
|
||||
unsigned char GetInstructionMask(Mnemonic type);
|
||||
|
||||
#endif
|
||||
+13
-1
@@ -1,11 +1,23 @@
|
||||
#ifndef PARSER_H
|
||||
#define PARSER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include "list.h"
|
||||
#include "token.h"
|
||||
#include "opcodes.h"
|
||||
#include "symbols_table.h"
|
||||
|
||||
void ParseTokens(List*);
|
||||
//#define HIGHMEMORY 65535 //64KiB - 1 AKA 0xFFFF
|
||||
|
||||
typedef struct {
|
||||
List* Instructions;
|
||||
SymbolTable* SymbolsTable;
|
||||
} IRState;
|
||||
|
||||
IRState* ParseTokens(List*);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,48 @@
|
||||
#ifndef SYMBOLSTABLE_H
|
||||
#define SYMBOLSTABLE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define SYMBOLSTABLE_DEFAULT_CAPACITY 128
|
||||
|
||||
typedef enum {
|
||||
ValueAt,
|
||||
Address
|
||||
} SymbolType;
|
||||
|
||||
typedef struct {
|
||||
char* String;
|
||||
uint8_t TerminatorByte;
|
||||
int HasTerminatorByte;
|
||||
} SymbolString;
|
||||
|
||||
typedef struct {
|
||||
char* Name;
|
||||
int Length;
|
||||
int Resolved;
|
||||
|
||||
union {
|
||||
SymbolString String;
|
||||
int Number;
|
||||
//Instruction Instruction;
|
||||
} Value;
|
||||
} Symbol;
|
||||
|
||||
typedef struct {
|
||||
Symbol** Symbols;
|
||||
int Size;
|
||||
int Capacity;
|
||||
} SymbolTable;
|
||||
|
||||
SymbolTable* CreateSymbolTable(void);
|
||||
Symbol* TryGetSymbol(char* name, SymbolTable* table);
|
||||
Symbol* AddSymbolToTable(char* name, SymbolTable* table);
|
||||
SymbolString* CreateSymbolString(char* text);
|
||||
void FreeSymbolTable(SymbolTable* table);
|
||||
void FreeSymbol(Symbol* symbol);
|
||||
|
||||
#endif
|
||||
+45
-29
@@ -5,40 +5,56 @@
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include "opcodes.h"
|
||||
|
||||
typedef enum {
|
||||
PLUS,
|
||||
MINUS,
|
||||
STAR,
|
||||
SLASH,
|
||||
POWER,
|
||||
LPARAM,
|
||||
RPARAM,
|
||||
LBRACKET,
|
||||
RBracket,
|
||||
COMMA,
|
||||
STRING,
|
||||
IDENTIFIER,
|
||||
LABEL,
|
||||
NUMBER,
|
||||
HEX,
|
||||
//Keywords
|
||||
DB, ORG,
|
||||
//Opcodes
|
||||
COPY, ADD, SUB, JZ, INT, YLD, RET,
|
||||
CMP, IN, OUT,
|
||||
//Registers
|
||||
R1, R2, R3, R4, R5, R6, R7, R8
|
||||
} TokenType;
|
||||
Plus = '+',
|
||||
Minus = '-',
|
||||
Star = '*',
|
||||
Slash = '/',
|
||||
Power = '^',
|
||||
LParan = '(',
|
||||
RParan = ')',
|
||||
LBracket = '[',
|
||||
RBracket = ']',
|
||||
Comma = ',',
|
||||
NewLine = '\n'
|
||||
} TokenPunctuation;
|
||||
|
||||
typedef enum {
|
||||
DB,
|
||||
Origin,
|
||||
Byte
|
||||
} Directive;
|
||||
|
||||
typedef enum {
|
||||
RegisterClass = 0,
|
||||
NumberClass = 1,
|
||||
CharacterClass = 2,
|
||||
MnemonicClass = 4,
|
||||
DirectiveClass = 8,
|
||||
PunctuationClass = 16,
|
||||
IdentifierClass = 32,
|
||||
LabelClass = 64,
|
||||
AddressClass = LabelClass | IdentifierClass
|
||||
} TokenClass;
|
||||
|
||||
typedef struct {
|
||||
TokenType type;
|
||||
char* lexeme;
|
||||
void* value;
|
||||
int line;
|
||||
char* Lemexe;
|
||||
TokenClass Class;
|
||||
int LineNumber;
|
||||
int EndOfFile;
|
||||
|
||||
union {
|
||||
TokenPunctuation Punctuation;
|
||||
Registers Register;
|
||||
Mnemonic Mnemonic;
|
||||
Directive Directive;
|
||||
int Number;
|
||||
} Value;
|
||||
} Token;
|
||||
|
||||
Token* CreateToken(char*, void*, int, TokenType);
|
||||
void FreeToken(Token*);
|
||||
Token* CreateToken(int lineNumber, TokenClass tokenClass);
|
||||
void FreeToken(Token* token);
|
||||
|
||||
#endif
|
||||
+24
-42
@@ -1,45 +1,27 @@
|
||||
.org 0x100
|
||||
.db video_start 0xF37F
|
||||
.db msg "Hello, world!", 0
|
||||
.db more_stuff "And yet another string!", 0
|
||||
|
||||
copy r1, msg
|
||||
copy r2, 0
|
||||
loop:
|
||||
cmp r1, 0
|
||||
jz loop
|
||||
add r1, 1
|
||||
mov r1, 5 ; move the immediate value 5 into r1
|
||||
add r1,5 ; add 5 into r1
|
||||
int 21 ; maybe that will call some string drawing BIOS-like routine
|
||||
load r1, msg ; Because the lod* instructions can't load an address
|
||||
; from anything but a register, this becomes laa r1, msg
|
||||
load r8, [15]
|
||||
;Routine: string_length
|
||||
;In: String address in r1
|
||||
;Out: Length in R2
|
||||
|
||||
string_length:
|
||||
loadb r3, [r1] ; Load the byte from the address in R1, into R3
|
||||
load r2, 0 ; String length
|
||||
cmp r3, 0 ; Is R3 a null byte?
|
||||
je end
|
||||
inc r2
|
||||
|
||||
start:
|
||||
inc r1 ; next char
|
||||
loadb r3, [r1] ; Load the next character byte into R3
|
||||
cmp r3, 0 ; Null byte?
|
||||
je end
|
||||
inc r2 ; Nope, increment the length counter
|
||||
jmp start
|
||||
|
||||
end:
|
||||
ret
|
||||
; form YYYY YYXX - Y opcode, X modifier
|
||||
; nop: 0000 0000
|
||||
; copy: 0000 01XX
|
||||
; add: 0000 10XX - | Perhaps running these will clear any overflow
|
||||
; addc: 0000 11XX - | or under flow flags if no errors occur.
|
||||
; sub: 0001 00XX - | jz should clear the zero flag.
|
||||
; subb: 0001 01XX - |
|
||||
; call: 0001 1100 - Always call [imm16]
|
||||
; jmp: 0010 0000 - Always jmp [imm16] (No short jumps)
|
||||
; jz: 0010 0100 - Always jz [imm16] (no short jumps)
|
||||
; cmp: 0010 11XX
|
||||
; push: 0011 11XX -| pop / push imm16/imm8 | reg
|
||||
; pop: 0100 00XX -|
|
||||
|
||||
;
|
||||
; imm8 imm16 reg
|
||||
; mov reg, imm16|reg
|
||||
; add reg, imm16|reg
|
||||
; int imm16
|
||||
; db [label] 'String data here' (Null byte is added implicatly by the assemblier)
|
||||
;
|
||||
; enum Type {
|
||||
; Op,
|
||||
; Reg,
|
||||
; Imm16,
|
||||
; Imm8
|
||||
; }
|
||||
; struct Token {
|
||||
; enum Type type;
|
||||
; char *value;
|
||||
; }
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
#include "../includes/disass.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
const unsigned char* Image;
|
||||
const unsigned char COPYMASKREG = 0x20;
|
||||
const unsigned char COPYMASKADD = 0xA0;
|
||||
const unsigned char ADDMASK = 0x40;
|
||||
const unsigned char SUBMASK = 0x60;
|
||||
const unsigned char CMPMASK = 0x80;
|
||||
|
||||
const unsigned char REGMASK = 0x00;//0x18; //0001 1000
|
||||
const unsigned char CONSTMASK = 0x08; //0000 1000
|
||||
const unsigned char ADDRESSMASK = 0x10; //0001 0000
|
||||
|
||||
int IsRegisterPattern(unsigned char pattern, char* lexeme);
|
||||
void GetParameter(unsigned char instruction, char* text);
|
||||
/*
|
||||
void Disassemble(unsigned char image[HIGHMEMORY]) {
|
||||
Image = image;
|
||||
int position = image[0] + image[1];
|
||||
int end = image[2] + image[3];
|
||||
|
||||
char* parameter1 = calloc(32, sizeof(char));
|
||||
char* parameter2 = calloc(32, sizeof(char));
|
||||
|
||||
unsigned char instruction = image[position];
|
||||
|
||||
while(end > position) {
|
||||
unsigned char masked = instruction & 0xE0;
|
||||
|
||||
if (masked) {
|
||||
IsRegisterPattern(instruction & 0x07, parameter1);
|
||||
unsigned char parameterType = instruction & 0x18;
|
||||
|
||||
if (parameterType == REGMASK) {
|
||||
IsRegisterPattern(Image[position + 1], parameter2);
|
||||
position += 2;
|
||||
} else if (parameterType == CONSTMASK) {
|
||||
snprintf(parameter2, sizeof(char) * 31, "%d", Image[position + 1] + Image[position + 2]);
|
||||
position += 3;
|
||||
}
|
||||
else if (parameterType == ADDRESSMASK) {
|
||||
snprintf(parameter2, sizeof(char) * 31, "[%#04X]", Image[position + 1] + Image[position + 2]);
|
||||
position += 3;
|
||||
}
|
||||
|
||||
//If the top bits are set
|
||||
if (masked == COPYMASKREG) {
|
||||
printf("COPY %s, %s\n", parameter1, parameter2);
|
||||
}
|
||||
else if (masked == COPYMASKADD) {
|
||||
printf("COPY %s, %s\n", parameter2, parameter1);
|
||||
}
|
||||
else if (masked == ADDMASK) {
|
||||
printf("ADD %s, %s\n", parameter1, parameter2);
|
||||
}
|
||||
else if (masked == SUBMASK) {
|
||||
printf("SUB %s, %s\n", parameter1, parameter2);
|
||||
}
|
||||
else if (masked == CMPMASK) {
|
||||
printf("CMP %s, %s\n", parameter1, parameter2);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "[Error] Unknown opcode %#02X\n", masked);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
int parameter = Image[position + 1] + Image[position + 2];
|
||||
|
||||
switch(instruction) {
|
||||
case 0:
|
||||
printf("NOP\n");
|
||||
position++;
|
||||
break;
|
||||
case 1:
|
||||
printf("JZ [%#04X]\n", parameter);
|
||||
position += 3;
|
||||
break;
|
||||
case 2:
|
||||
printf("INT %d\n", parameter);
|
||||
position += 3;
|
||||
break;
|
||||
case 3:
|
||||
printf("YLD\n");
|
||||
position++;
|
||||
break;
|
||||
case 4:
|
||||
printf("RET\n");
|
||||
position++;
|
||||
break;
|
||||
case 5:
|
||||
printf("CALL [%#04X]\n", parameter);
|
||||
position += 3;
|
||||
break;
|
||||
case 6:
|
||||
printf("JMP [%#04X]\n", parameter);
|
||||
position += 3;
|
||||
break;
|
||||
case 7:
|
||||
printf("IN %d\n", parameter);
|
||||
position += 3;
|
||||
break;
|
||||
case 8:
|
||||
printf("OUT %d\n", parameter);
|
||||
position += 3;
|
||||
break;
|
||||
default:
|
||||
position++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
instruction = Image[position];
|
||||
|
||||
memset(parameter1, '\0', sizeof(char) * 32);
|
||||
memset(parameter2, '\0', sizeof(char) * 32);
|
||||
}
|
||||
}
|
||||
*/
|
||||
int IsRegisterPattern(unsigned char pattern, char* lexeme) {
|
||||
if (pattern > 8) {
|
||||
lexeme[0] = '\0';
|
||||
return 0;
|
||||
}
|
||||
//The registers' bit patterns are zero indexed, so 'r1' is '000'
|
||||
//but 'r8' is '111' and so forth.
|
||||
pattern++;
|
||||
|
||||
lexeme[0] = 'r';
|
||||
lexeme[1] = pattern | 0x30;
|
||||
lexeme[2] = '\0';
|
||||
|
||||
return 1;
|
||||
}
|
||||
/*
|
||||
//REG XXX0 0XXX
|
||||
//Constant XXX0 1XXX
|
||||
//Address XXX1 0XXX
|
||||
NOP - 0000 0000 NOP
|
||||
JZ - 0000 0001 JZ Address
|
||||
INT - 0000 0010 INT Constant
|
||||
YLD - 0000 0011 YLD
|
||||
RET - 0000 0100 RET
|
||||
CALL - 0000 0101 CALL Address
|
||||
JMP - 0000 0110 JMP Address
|
||||
IN - 0000 0111 IN Constant
|
||||
OUT - 0000 1000 OUT Constant
|
||||
COPY - 0010 0XXX COPY REG, REG
|
||||
- 0010 1XXX COPY REG, Constant
|
||||
- 0011 0XXX COPY REG, Address
|
||||
- 1010 0XXX COPY Address, REG
|
||||
- 1010 1XXX COPY Address, Constant
|
||||
- 1011 0XXX COPY Address, Address
|
||||
ADD - 0100 0XXX ADD REG, REG
|
||||
- 0100 1XXX ADD REG, Constant
|
||||
SUB - 0110 0XXX SUB REG, REG
|
||||
- 0110 1XXX SUB REG, Constant
|
||||
CMP - 1000 0XXX CMP REG, REG
|
||||
- 1000 1XXX CMP REG, Constant
|
||||
- 1001 0XXX CMP REG, Address */
|
||||
+69
-6
@@ -1,3 +1,4 @@
|
||||
#include <bits/types/FILE.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@@ -19,16 +20,78 @@ int main(int argc, char* args[]) {
|
||||
if (!ReadAllString(args[1], &source_code, &bytes_read)) return EX_IOERR;
|
||||
|
||||
List* list = GenerateTokenList(source_code);
|
||||
char mnemonic[12];
|
||||
|
||||
// for(int i = 0; i < list->size; i++) {
|
||||
// Token* t = (Token*) list->content[i];
|
||||
for(int i = 0; i < list->size; i++) {
|
||||
Token* t = (Token*) list->content[i];
|
||||
|
||||
// printf("[%i] '%s'", t->type, t->lexeme);
|
||||
// if (t->type == LABEL) printf("*");
|
||||
// printf("\n");
|
||||
if (t->EndOfFile) {
|
||||
printf("EOF\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (t->Class == PunctuationClass){
|
||||
if(t->Value.Punctuation == NewLine) {
|
||||
printf("\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("<%d>", t->LineNumber);
|
||||
|
||||
printf("%c ", t->Value.Punctuation);
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("<%d>", t->LineNumber);
|
||||
|
||||
if (t->Class == LabelClass) {
|
||||
printf("[L]%s* ", t->Lemexe);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (t->Class == IdentifierClass) {
|
||||
printf("[I]%s ", t->Lemexe);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (t->Class == RegisterClass) {
|
||||
printf("[R]%d", t->Value.Register);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (t->Class == NumberClass) {
|
||||
printf("[N]%d ", t->Value.Number);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (t->Class == MnemonicClass) {
|
||||
GetMnemonicText(t->Value.Mnemonic, mnemonic);
|
||||
printf("[M]%s ", mnemonic);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (t->Class == DirectiveClass) {
|
||||
printf("[D]%d ", t->Value.Directive);
|
||||
|
||||
}
|
||||
|
||||
if (t->Class == CharacterClass) {
|
||||
printf("%s ", t->Lemexe);
|
||||
}
|
||||
}
|
||||
|
||||
IRState* image = ParseTokens(list);
|
||||
//free(image);
|
||||
//Disassemble(image);
|
||||
//for(int i = 0; i < image->Opcodes->size; i++) {
|
||||
// printf("%d\n", ((IROpcode*) image->Opcodes->content[i])->Mnemonic);
|
||||
//}
|
||||
|
||||
ParseTokens(list);
|
||||
//FILE* program = fopen("program.bin", "w+b");
|
||||
|
||||
//fwrite(image, 1, image[2] + image[3], program);
|
||||
|
||||
//fclose(program);
|
||||
|
||||
free(source_code);
|
||||
}
|
||||
+180
-30
@@ -2,36 +2,167 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
OpCode opcodes[OPCODECOUNT] = {
|
||||
{ "copy", COPY, Reg, Reg | Imm8 | Imm16 },
|
||||
{ "add", ADD, Reg, Reg | Imm8 | Imm16 },
|
||||
{ "sub", SUB, Reg, Reg | Imm8 | Imm16 },
|
||||
{ "jz", JZ, Imm8 | Imm16, None },
|
||||
{ "int", INT, Imm8, None },
|
||||
{ "yld", YLD, None, None },
|
||||
{ "ret", RET, None, None },
|
||||
{ "cmp", CMP, Reg | Imm8 | Imm16, Reg | Imm8 | Imm16 },
|
||||
{ "in", IN, None, None},
|
||||
{ "out", OUT, None, None}
|
||||
#define OPCODECOUNT 15
|
||||
|
||||
struct _instruction {
|
||||
char* Name;
|
||||
Mnemonic Mnemonic;
|
||||
};
|
||||
|
||||
Register registers[REGISTERCOUNT] = {
|
||||
{ "r1", R1 },
|
||||
{ "r2", R2 },
|
||||
{ "r3", R3 },
|
||||
{ "r4", R4 },
|
||||
{ "r5", R5 },
|
||||
{ "r6", R6 },
|
||||
{ "r7", R7 },
|
||||
{ "r8", R8 }
|
||||
struct _instruction instructions[OPCODECOUNT] = {
|
||||
{ "add", ADD },//, Reg, Reg | Constant },
|
||||
{ "sub", SUB },//, Reg, Reg | Constant },
|
||||
{ "jz", JZ },//, Address, None },
|
||||
{ "int", INT },//, Constant, None },
|
||||
{ "yld", YLD },//, None, None },
|
||||
{ "ret", RET },//, None, None },
|
||||
{ "cmp", CMP },//, Reg, Reg | Constant },
|
||||
{ "inc", INC },//, Constant | Reg, None},
|
||||
{ "dec", DEC },//, None, None},
|
||||
{ "nop", NOP },//, None, None},
|
||||
{ "jmp", JMP },//, Address, None},
|
||||
{ "call", CALL },//, Address, None}
|
||||
{ "load", LOAD },
|
||||
{ "je", JE },
|
||||
{ "loadb", LOADB}
|
||||
};
|
||||
|
||||
int IsOpcode(const char* text, TokenType* opcode) {
|
||||
Instruction* CreateInstruction(Mnemonic mnemonic) {
|
||||
Instruction* instruction = calloc(1, sizeof(Instruction));
|
||||
|
||||
if (!instruction) {
|
||||
fprintf(stderr, "Failed to calloc room for an Instruction. %s.\n", strerror(errno));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
instruction->Mnemonic = mnemonic;
|
||||
// instruction->ParameterOne = CreateParameter(NoParameter);
|
||||
// instruction->ParameterTwo = CreateParameter(NoParameter);
|
||||
|
||||
return instruction;
|
||||
}
|
||||
|
||||
Parameter* CreateParameter(OpcodeParameter parameterType) {
|
||||
Parameter* param = calloc(1, sizeof(Parameter));
|
||||
|
||||
if (!param) {
|
||||
fprintf(stderr, "Failed to calloc room for a Parameter. %s,\n", strerror(errno));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
param->ParameterType = parameterType;
|
||||
|
||||
return param;
|
||||
}
|
||||
|
||||
void FreeInstruction(Instruction* instruction) {
|
||||
if (!instruction) return;
|
||||
|
||||
free(instruction);
|
||||
}
|
||||
|
||||
unsigned char GetInstructionMask(Mnemonic type) {
|
||||
switch (type) {
|
||||
case LOAD:
|
||||
return 0x20; //0b00100000; ORing 0x80 marks the first parameter as an address
|
||||
case ADD:
|
||||
return 0x40;//0b01000000;
|
||||
case SUB:
|
||||
return 0x60;//0b01100000;
|
||||
case CMP:
|
||||
return 0x80;//0b10000000;
|
||||
case JZ:
|
||||
return 0x01;//0b00000001;
|
||||
case INT:
|
||||
return 0x02; //0b00000010;
|
||||
case YLD:
|
||||
return 0x03;
|
||||
case RET:
|
||||
return 0x04;
|
||||
case CALL:
|
||||
return 0x05;
|
||||
case JMP:
|
||||
return 0x06;//0b00000110;
|
||||
case INC:
|
||||
return 0x07;//0b00000111;
|
||||
case DEC:
|
||||
return 0x08;//0b00001000;
|
||||
default:
|
||||
return 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
void GetMnemonicText(Mnemonic mnemonic, char buffer[12]) {
|
||||
memset(buffer, '\0', 12);
|
||||
|
||||
for(int i = 0; i < OPCODECOUNT; i++) {
|
||||
if (instructions[i].Mnemonic == mnemonic) {
|
||||
strncpy(buffer, instructions[i].Name, 11);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
NOP - 0000 0000 NOP
|
||||
JZ - 0000 0001 JZ Address
|
||||
INT - 0000 0010 INT Constant
|
||||
YLD - 0000 0011 YLD
|
||||
RET - 0000 0100 RET
|
||||
CALL - 0000 0101 CALL Address
|
||||
JMP - 0000 0110 JMP Address
|
||||
IN - 0000 0111 IN Constant
|
||||
OUT - 0000 1000 OUT Constant
|
||||
COPY - 0010 0XXX COPY REG, REG
|
||||
- 0010 1XXX COPY REG, Constant
|
||||
- 0011 0XXX COPY REG, Address
|
||||
- 1010 0XXX COPY Address, REG
|
||||
- 1010 1XXX COPY Address, Constant
|
||||
- 1011 0XXX COPY Address, Address
|
||||
ADD - 0100 0XXX ADD REG, REG
|
||||
- 0100 1XXX ADD REG, Constant
|
||||
SUB - 0110 0XXX SUB REG, REG
|
||||
- 0110 1XXX SUB REG, Constant
|
||||
CMP - 1000 0XXX CMP REG, REG
|
||||
- 1000 1XXX CMP REG, Constant
|
||||
- 1001 0XXX CMP REG, Address
|
||||
*/
|
||||
//COPY X01X XXXX
|
||||
//ADD 010X XXXX
|
||||
//SUB 011X XXXX
|
||||
//CMP 100X XXXX
|
||||
//REG XXX0 0XXX
|
||||
//Constant XXX0 1XXX
|
||||
//Address XXX1 0XXX
|
||||
//R1 XXXX X000 -> XXXX X111 (R1 to R8)
|
||||
|
||||
// Register registers[REGISTERCOUNT] = {
|
||||
// { "r1", R1 },
|
||||
// { "r2", R2 },
|
||||
// { "r3", R3 },
|
||||
// { "r4", R4 },
|
||||
// { "r5", R5 },
|
||||
// { "r6", R6 },
|
||||
// { "r7", R7 },
|
||||
// { "r8", R8 }
|
||||
// };
|
||||
|
||||
// const Instruction* GetOpcodeDetails(Mnemonic type) {
|
||||
// for(int i = 0; i < OPCODECOUNT; i++) {
|
||||
// if (instructions[i].op == type) return &instructions[i];
|
||||
// }
|
||||
|
||||
// return NULL;
|
||||
// }
|
||||
|
||||
int IsOpcode(const char* text, Mnemonic* opcode) {
|
||||
if (!text) return 0;
|
||||
|
||||
for(int i = 0; i < OPCODECOUNT; i++) {
|
||||
if (strcmp(opcodes[i].lexeme, text) == 0) {
|
||||
*opcode = opcodes[i].op;
|
||||
if (strcmp(instructions[i].Name, text) == 0) {
|
||||
if (opcode) *opcode = instructions[i].Mnemonic;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -39,15 +170,34 @@ int IsOpcode(const char* text, TokenType* opcode) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IsRegister(const char* text, TokenType* reg) {
|
||||
int IsRegister(const char* text, Registers* reg) {
|
||||
if (!text) return 0;
|
||||
|
||||
for(int i = 0; i < REGISTERCOUNT; i++) {
|
||||
if (strcmp(registers[i].lexeme, text) == 0) {
|
||||
*reg = registers[i].type;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
int length = strlen(text);
|
||||
Registers r = R8;
|
||||
|
||||
if (length != 2) return 0;
|
||||
if (text[0] != 'r') return 0;
|
||||
|
||||
switch(text[1]) {
|
||||
case '1':
|
||||
r--;
|
||||
case '2':
|
||||
r--;
|
||||
case '3':
|
||||
r--;
|
||||
case '4':
|
||||
r--;
|
||||
case '5':
|
||||
r--;
|
||||
case '6':
|
||||
r--;
|
||||
case '7':
|
||||
r--;
|
||||
case '8':
|
||||
if (reg) *reg = r;
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
+263
-54
@@ -1,89 +1,298 @@
|
||||
#include "../includes/parser.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct {
|
||||
Token* token;
|
||||
int address;
|
||||
} Symbol;
|
||||
const List* TokensList;
|
||||
int CurrentToken = 0;
|
||||
|
||||
Symbol* CreateSymbol(Token*, int);
|
||||
void AddSymbol(Token*);
|
||||
void PrintSymbols(void);
|
||||
Token* GetSymbol(char*);
|
||||
List* SymbolsTable;
|
||||
unsigned int ProgramCounter = 0;
|
||||
SymbolString* ParseIdentifierParameter(void);
|
||||
Instruction* HandleOperation(void);
|
||||
void HandleAssemblerDirective(void);
|
||||
void AdvanceParser(void);
|
||||
void IgnoreParserLine(void);
|
||||
Token* PeekToken(void);
|
||||
int ParserAtEnd(void);
|
||||
IRState MachineState;
|
||||
|
||||
void ParseTokens(List* tokens) {
|
||||
SymbolsTable = CreateList();
|
||||
IRState* ParseTokens(List* tokens) {
|
||||
if (!tokens) return NULL;
|
||||
|
||||
for(int i = 0; i < tokens->size; i++){
|
||||
Token* t = tokens->content[i];
|
||||
TokensList = tokens;
|
||||
|
||||
switch(t->type) {
|
||||
case IDENTIFIER:
|
||||
case LABEL:
|
||||
AddSymbol(t);
|
||||
MachineState.SymbolsTable = CreateSymbolTable();
|
||||
MachineState.Instructions = CreateList();
|
||||
|
||||
while(!ParserAtEnd()) {
|
||||
Token* t = PeekToken();
|
||||
|
||||
switch(t->Class) {
|
||||
case DirectiveClass: //Maybe these should be ignored, let another process handle that.
|
||||
IgnoreParserLine();
|
||||
break;
|
||||
case STRING:
|
||||
ProgramCounter += strlen(t->lexeme);
|
||||
case MnemonicClass:
|
||||
HandleOperation();
|
||||
break;
|
||||
case NUMBER:
|
||||
case HEX:
|
||||
if ((long) t->value < 256) ProgramCounter += 1;
|
||||
else ProgramCounter += 2;
|
||||
case LabelClass:
|
||||
{
|
||||
Symbol* symbol = TryGetSymbol(t->Lemexe, MachineState.SymbolsTable);
|
||||
|
||||
if (symbol && symbol->Resolved) {
|
||||
fprintf(stderr, "[Error] Line %d: Redefinition of symbol '%s'\n", t->LineNumber, t->Lemexe);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
AdvanceParser(); //Consume the label token
|
||||
|
||||
if (!symbol) AddSymbolToTable(t->Lemexe, MachineState.SymbolsTable)->Resolved = 1;
|
||||
else symbol->Resolved = 1;
|
||||
|
||||
AdvanceParser(); //Consume the NewLine
|
||||
|
||||
// Instruction* ins = HandleOperation();
|
||||
|
||||
// if (!ins) continue;
|
||||
|
||||
// symbol->Value.Instruction = ins;
|
||||
// symbol->InstructionPointer = 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (t->type > ORG) ProgramCounter += 1;
|
||||
fprintf(stderr, "[Warning] Line %d: Syntax error, expected start of expression, got '%c' [%d].\n", t->LineNumber, t->Value.Punctuation, t->Class);
|
||||
//exit(1);
|
||||
IgnoreParserLine();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PrintSymbols();
|
||||
DestroyList(SymbolsTable);
|
||||
printf("Program Counter: %d\n", ProgramCounter);
|
||||
if (MachineState.SymbolsTable->Size > 0) PrintSymbols();
|
||||
|
||||
for(int i = 0; i < MachineState.Instructions->size; i++) {
|
||||
char mn[12];
|
||||
|
||||
Instruction* ins = MachineState.Instructions->content[i];
|
||||
|
||||
GetMnemonicText(ins->Mnemonic, mn);
|
||||
|
||||
printf("%s\n", mn);
|
||||
}
|
||||
|
||||
return &MachineState;
|
||||
}
|
||||
|
||||
SymbolString* ParseIdentifierParameter(void) {
|
||||
Token* token = PeekToken();
|
||||
|
||||
if (token->Class != IdentifierClass) {
|
||||
fprintf(stderr, "[Error] Line %d: Expected identifier\n", token->LineNumber);
|
||||
IgnoreParserLine();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SymbolString* string = CreateSymbolString(token->Lemexe);
|
||||
|
||||
AdvanceParser();
|
||||
|
||||
if (token->Class == PunctuationClass && token->Value.Punctuation == NewLine) {
|
||||
return string;
|
||||
}
|
||||
else if (token->Class == PunctuationClass && token->Value.Punctuation == Comma) {
|
||||
AdvanceParser(); // Comsume the comma
|
||||
|
||||
token = PeekToken();
|
||||
|
||||
if (token->Class != NumberClass) {
|
||||
fprintf(stderr, "[Error] Line %d: Expected string termination number.\n", token->LineNumber);
|
||||
IgnoreParserLine();
|
||||
free(string);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
string->HasTerminatorByte = 1;
|
||||
string->TerminatorByte = token->Value.Number;
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Parameter* GetParameterType() {
|
||||
Token* token = PeekToken();
|
||||
Symbol* symbol = NULL;
|
||||
Parameter* param = CreateParameter(NoParameter);
|
||||
|
||||
switch(token->Class) {
|
||||
case RegisterClass:
|
||||
param->ParameterType = RegisterParameter;
|
||||
param->InterpretedAs = RegisterParameter;
|
||||
|
||||
param->Value.Register = token->Value.Register;
|
||||
|
||||
AdvanceParser();
|
||||
|
||||
return param;
|
||||
case IdentifierClass:
|
||||
param->ParameterType = AddressParameter;
|
||||
param->InterpretedAs = AddressParameter;
|
||||
|
||||
symbol = TryGetSymbol(token->Lemexe, MachineState.SymbolsTable);
|
||||
|
||||
if (!symbol) symbol = AddSymbolToTable(token->Lemexe, MachineState.SymbolsTable);
|
||||
|
||||
param->Value.Symbol = symbol;
|
||||
|
||||
AdvanceParser();
|
||||
|
||||
return param;
|
||||
case PunctuationClass:
|
||||
if (token->Value.Punctuation != LBracket) {
|
||||
fprintf(stderr, "[Error] Line %d: Expected opening bracket\n", token->LineNumber);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
AdvanceParser(); // [
|
||||
|
||||
token = PeekToken();
|
||||
|
||||
if (token->Class == NumberClass) {
|
||||
param->ParameterType = ConstantParameter;
|
||||
param->InterpretedAs = AddressParameter;
|
||||
param->Value.Number = token->Value.Number;
|
||||
}
|
||||
else if (token->Class == RegisterClass) {
|
||||
param->ParameterType = RegisterParameter;
|
||||
param->InterpretedAs = AddressParameter;
|
||||
param->Value.Register = token->Value.Register;
|
||||
}
|
||||
else if (token->Class & AddressClass) {
|
||||
param->ParameterType = AddressParameter;
|
||||
param->ParameterType = AddressParameter;
|
||||
|
||||
symbol = TryGetSymbol(token->Lemexe, MachineState.SymbolsTable);
|
||||
|
||||
if (!symbol) symbol = AddSymbolToTable(token->Lemexe, MachineState.SymbolsTable);
|
||||
|
||||
param->Value.Symbol = symbol;
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "[Error] Line %d: Expected identifier, constant number or register.\n", token->LineNumber);
|
||||
IgnoreParserLine();
|
||||
//return NULL;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
AdvanceParser(); //Consume the parameter it self.
|
||||
|
||||
token = PeekToken();
|
||||
|
||||
if (token->Class != PunctuationClass || token->Value.Punctuation != RBracket) {
|
||||
fprintf(stderr, "[Error] Line %d: Expected closing bracket.\n", token->LineNumber);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
AdvanceParser(); // ]
|
||||
|
||||
return param;
|
||||
case NumberClass:
|
||||
param->ParameterType = ConstantParameter;
|
||||
param->InterpretedAs = ConstantParameter;
|
||||
|
||||
param->Value.Number = token->Value.Number;
|
||||
|
||||
AdvanceParser();
|
||||
|
||||
return param;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return param;
|
||||
}
|
||||
|
||||
void HandleOperation() {
|
||||
Token* token = PeekToken();
|
||||
char mn[12];
|
||||
|
||||
Instruction* ins = CreateInstruction(token->Value.Mnemonic);
|
||||
|
||||
GetMnemonicText(ins->Mnemonic, mn);
|
||||
|
||||
AdvanceParser();
|
||||
|
||||
//No parameters here, we have a line break.
|
||||
if (PeekToken()->EndOfFile || (PeekToken()->Class == PunctuationClass && PeekToken()->Value.Punctuation == NewLine)) {
|
||||
goto InsertInstruction;
|
||||
}
|
||||
|
||||
ins->ParameterOne = GetParameterType();
|
||||
|
||||
if (ins->ParameterOne->ParameterType == NoParameter) {
|
||||
//This would be an error if the next token isn't a line break.
|
||||
if (PeekToken()->Class != PunctuationClass || PeekToken()->Value.Punctuation != NewLine) {
|
||||
fprintf(stderr, "[Error] Line %d: Syntax error, expected line break but got %s.\n", token->LineNumber, token->Lemexe);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
goto InsertInstruction;
|
||||
}
|
||||
|
||||
if (PeekToken()->Class == PunctuationClass && PeekToken()->Value.Punctuation == NewLine) {
|
||||
goto InsertInstruction;
|
||||
}
|
||||
|
||||
if (PeekToken()->Class != PunctuationClass || PeekToken()->Value.Punctuation != Comma) {
|
||||
//Syntax error
|
||||
fprintf(stderr, "[Error] Line %d: Expected comma.\n", token->LineNumber);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
AdvanceParser(); //Consume the comma
|
||||
|
||||
ins->ParameterTwo = GetParameterType();
|
||||
|
||||
InsertInstruction:
|
||||
|
||||
AddListItem(ins, sizeof(Instruction), MachineState.Instructions);
|
||||
|
||||
AdvanceParser(); //Consume the line break
|
||||
|
||||
free(ins);
|
||||
}
|
||||
|
||||
void PrintSymbols(void) {
|
||||
printf("-----SYMBOLS-----\n");
|
||||
for(int i = 0; i < SymbolsTable->size; i++) {
|
||||
Symbol* symbol = SymbolsTable->content[i];
|
||||
printf("[%#08X] %s\n", symbol->address, symbol->token->lexeme);
|
||||
for(int i = 0; i < MachineState.SymbolsTable->Size; i++) {
|
||||
Symbol* symbol = MachineState.SymbolsTable->Symbols[i];
|
||||
|
||||
printf("[Resolved? %d] %s\n", symbol->Resolved, symbol->Name);
|
||||
}
|
||||
printf("-----SYMBOLS-----\n");
|
||||
}
|
||||
|
||||
void AddSymbol(Token* token) {
|
||||
if (!token) return;
|
||||
if (token->type != IDENTIFIER && token->type != LABEL) return;
|
||||
void AdvanceParser(void) {
|
||||
if (ParserAtEnd()) return;
|
||||
|
||||
for(int i = 0; i < SymbolsTable->size; i++) {
|
||||
Symbol* s = SymbolsTable->content[i];
|
||||
|
||||
if (strcmp(s->token->lexeme, token->lexeme) == 0) return;
|
||||
CurrentToken++;
|
||||
}
|
||||
|
||||
Symbol* symbol = CreateSymbol(token, ProgramCounter);
|
||||
int ParserAtEnd(void) {
|
||||
if (CurrentToken >= TokensList->size) return 1;
|
||||
|
||||
AddListItem(symbol, sizeof(Symbol), SymbolsTable);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Token* GetSymbol(char* name) {
|
||||
if (!name) return NULL;
|
||||
|
||||
return NULL;
|
||||
Token* PeekToken(void) {
|
||||
return TokensList->content[CurrentToken];
|
||||
}
|
||||
|
||||
Symbol* CreateSymbol(Token* token, int offset) {
|
||||
Symbol* symbol = calloc(1, sizeof(Symbol));
|
||||
|
||||
if (!symbol) {
|
||||
return NULL;
|
||||
void IgnoreParserLine(void) {
|
||||
while(!ParserAtEnd()) {
|
||||
if (PeekToken()->Class == PunctuationClass && PeekToken()->Value.Punctuation == NewLine) {
|
||||
AdvanceParser();
|
||||
break;
|
||||
}
|
||||
|
||||
symbol->token = token;
|
||||
symbol->address = offset;
|
||||
if (PeekToken()->EndOfFile) break;
|
||||
|
||||
return symbol;
|
||||
AdvanceParser();
|
||||
}
|
||||
}
|
||||
+128
-27
@@ -1,13 +1,16 @@
|
||||
#include "../includes/scanner.h"
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
const char* SourceCode;
|
||||
int Line = 0;
|
||||
int Line = 1;
|
||||
int Position = 0;
|
||||
int SourceLength = 0;
|
||||
int ScannerAtEnd(void);
|
||||
int IsPunctuation(char);
|
||||
int IsWhiteSpace(char);
|
||||
char PeekScanner(void);
|
||||
char PeekAheadScanner(void);
|
||||
void AdvanceScanner(void);
|
||||
@@ -34,14 +37,35 @@ List* GenerateTokenList(const char* source) {
|
||||
case ' ':
|
||||
case '\r':
|
||||
case '\t':
|
||||
case '\v':
|
||||
case '\f':
|
||||
AdvanceScanner();
|
||||
break; //Ignore whitespace
|
||||
case '\n':
|
||||
if (tokens->size > 0) {
|
||||
token = tokens->content[tokens->size - 1];
|
||||
|
||||
if (token->Class == PunctuationClass && token->Value.Punctuation == NewLine) {
|
||||
AdvanceScanner();
|
||||
Line++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
token = CreateToken(Line, PunctuationClass);
|
||||
token->Value.Punctuation = NewLine;
|
||||
AddListItem(token, sizeof(Token), tokens);
|
||||
AdvanceScanner();
|
||||
Line++;
|
||||
break;
|
||||
case ';':
|
||||
IgnoreLine();
|
||||
|
||||
if (tokens->size == 0) {
|
||||
//There's nothing here so that means this is some comments block at the start of the file.
|
||||
AdvanceScanner(); //Consume the actual new line char.
|
||||
Line++;
|
||||
}
|
||||
break;
|
||||
case '.': //directive like ".org" or ".db"
|
||||
token = ParseDirective();
|
||||
@@ -74,20 +98,45 @@ List* GenerateTokenList(const char* source) {
|
||||
token = NULL;
|
||||
}
|
||||
|
||||
if (tokens->size > 0) {
|
||||
token = tokens->content[tokens->size - 1];
|
||||
|
||||
if (token->Class == PunctuationClass && token->Value.Punctuation == NewLine) {
|
||||
//If the last token is a line break, remove it as its not too meaningful.
|
||||
tokens->size--;
|
||||
}
|
||||
}
|
||||
|
||||
token = CreateToken(Line, PunctuationClass);
|
||||
|
||||
token->EndOfFile = 1;
|
||||
|
||||
AddListItem(token, sizeof(Token), tokens);
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
Token* ParseNumber(void) {
|
||||
int start = Position;
|
||||
int base = 10;
|
||||
|
||||
while(!ScannerAtEnd() && isdigit(PeekScanner()))
|
||||
AdvanceScanner();
|
||||
|
||||
if (PeekScanner() == 'x' && isdigit(PeekAheadScanner())) {
|
||||
base = 16;
|
||||
if (tolower(PeekScanner()) == 'x') {
|
||||
char ahead = tolower(PeekAheadScanner());
|
||||
|
||||
if ((ahead >= 'a' && ahead <= 'f') || isdigit(ahead)) {
|
||||
AdvanceScanner(); //Consume the 'x'
|
||||
while(!ScannerAtEnd() && isdigit(PeekScanner())) AdvanceScanner();
|
||||
|
||||
while(!ScannerAtEnd() && PeekScanner() != '\n' && !IsWhiteSpace(PeekScanner())) {
|
||||
if (isdigit(PeekScanner())) {
|
||||
AdvanceScanner();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tolower(PeekScanner()) >= 'a' && tolower(PeekScanner()) <= 'f') AdvanceScanner();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int length = Position - start;
|
||||
@@ -95,26 +144,26 @@ Token* ParseNumber(void) {
|
||||
if (length == 0) return NULL;
|
||||
|
||||
char* lexeme = calloc(sizeof(char), length + 1);
|
||||
long* value = calloc(1, sizeof(long));
|
||||
|
||||
if (!lexeme) {
|
||||
fprintf(stderr, "Failed to calloc space for number. %s.\n", strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
free(lexeme);
|
||||
fprintf(stderr, "Failed to calloc space for the raw numeric value of a token. %s.\n", strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(lexeme, &SourceCode[start], length);
|
||||
|
||||
*value = strtol(lexeme, NULL, base);
|
||||
Token* token = CreateToken(Line, NumberClass);
|
||||
|
||||
if (base == 10) return CreateToken(lexeme, value, Line, NUMBER);
|
||||
token->Value.Number = strtol(lexeme, NULL, 0);
|
||||
|
||||
return CreateToken(lexeme, value, Line, HEX);
|
||||
if (errno != 0) {
|
||||
fprintf(stderr, "[Error] Line %d: Invalid number detected. %s.\n", Line, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
token->Lemexe = lexeme;
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
Token* ParseDirective(void) {
|
||||
@@ -135,12 +184,17 @@ Token* ParseDirective(void) {
|
||||
|
||||
memcpy(directive, &SourceCode[start], length);
|
||||
|
||||
Token* token = CreateToken(Line, DirectiveClass);
|
||||
|
||||
if (strcmp(directive, ".db") == 0) {
|
||||
return CreateToken(directive, directive, Line, DB);
|
||||
token->Value.Directive = DB;
|
||||
|
||||
return token;
|
||||
}
|
||||
else if (strcmp(directive, ".org") == 0) {
|
||||
fprintf(stderr, "[Warning] Org is not a supported directive.\n");
|
||||
free(directive);
|
||||
free(token);
|
||||
IgnoreLine();
|
||||
return NULL;
|
||||
}
|
||||
@@ -180,7 +234,9 @@ Token* ParseString(void) {
|
||||
|
||||
AdvanceScanner(); //Consume the trailing double quote.
|
||||
|
||||
Token* token = CreateToken(lexeme, lexeme, Line, STRING);
|
||||
Token* token = CreateToken(Line, CharacterClass);
|
||||
|
||||
token->Lemexe = lexeme;
|
||||
|
||||
return token;
|
||||
}
|
||||
@@ -196,7 +252,8 @@ Token* ParseIdentifier(void) {
|
||||
|
||||
if (length == 0) return NULL;
|
||||
|
||||
TokenType type;
|
||||
Mnemonic mnemonics;
|
||||
Registers reg;
|
||||
char* lexeme = calloc(sizeof(char), length + 1);
|
||||
|
||||
if (!lexeme) {
|
||||
@@ -206,14 +263,33 @@ Token* ParseIdentifier(void) {
|
||||
|
||||
memcpy(lexeme, &SourceCode[start], length);
|
||||
|
||||
if (IsOpcode(lexeme, &type)) return CreateToken(lexeme, lexeme, Line, type);
|
||||
if (IsRegister(lexeme, &type)) return CreateToken(lexeme, lexeme, Line, type);
|
||||
if (IsOpcode(lexeme, &mnemonics)) {
|
||||
Token* token = CreateToken(Line, MnemonicClass);
|
||||
|
||||
token->Value.Mnemonic = mnemonics;
|
||||
|
||||
return token;
|
||||
}
|
||||
if (IsRegister(lexeme, ®)) {
|
||||
Token* token = CreateToken(Line, RegisterClass);
|
||||
token->Value.Register = reg;
|
||||
|
||||
return token;
|
||||
}
|
||||
if (lexeme[length - 1] == ':') {
|
||||
lexeme[length - 1] = '\0'; //Bit hacky, but this makes the parser's job a bit easier.
|
||||
return CreateToken(lexeme, lexeme, Line, LABEL);
|
||||
Token* token = CreateToken(Line, LabelClass);
|
||||
|
||||
token->Lemexe = lexeme;
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
return CreateToken(lexeme, lexeme, Line, IDENTIFIER);
|
||||
Token* token = CreateToken(Line, IdentifierClass);
|
||||
|
||||
token->Lemexe = lexeme;
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
char PeekScanner(void) {
|
||||
@@ -251,19 +327,44 @@ int IsPunctuation(char c) {
|
||||
}
|
||||
}
|
||||
|
||||
int IsWhiteSpace(char c) {
|
||||
switch(c) {
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\v':
|
||||
case '\f':
|
||||
case '\r':
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Token* ParsePunctuation(char c) {
|
||||
TokenPunctuation punctuation;
|
||||
|
||||
switch(c) {
|
||||
case '[':
|
||||
return CreateToken("[", NULL, Line, LBRACKET);
|
||||
punctuation = LBracket;
|
||||
break;
|
||||
case ']':
|
||||
return CreateToken("]", NULL, Line, RBracket);
|
||||
punctuation = RBracket;
|
||||
break;
|
||||
case '(':
|
||||
return CreateToken("(", NULL, Line, LPARAM);
|
||||
punctuation = LParan;
|
||||
break;
|
||||
case ')':
|
||||
return CreateToken(")", NULL, Line, RPARAM);
|
||||
punctuation = RParan;
|
||||
break;
|
||||
case ',':
|
||||
return CreateToken(",", NULL, Line, COMMA);
|
||||
punctuation = Comma;
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Token* token = CreateToken(Line, PunctuationClass);
|
||||
token->Value.Punctuation = punctuation;
|
||||
|
||||
return token;
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
#include "../includes/symbols_table.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
SymbolTable* CreateSymbolTable(void){
|
||||
SymbolTable* table = calloc(1, sizeof(SymbolTable));
|
||||
|
||||
if (!table) {
|
||||
fprintf(stderr, "Failed to calloc memory for a SymbolTable. %s.\n", strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
table->Symbols = calloc(SYMBOLSTABLE_DEFAULT_CAPACITY, sizeof(Symbol*));
|
||||
|
||||
if (!table->Symbols) {
|
||||
fprintf(stderr, "Failed to calloc Symbol list. %s.\n", strerror(errno));
|
||||
free(table);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
table->Capacity = SYMBOLSTABLE_DEFAULT_CAPACITY;
|
||||
table->Size = 0;
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
Symbol* CreateSymbol(char* name) {
|
||||
Symbol* symbol = calloc(1, sizeof(Symbol));
|
||||
|
||||
if (!symbol) {
|
||||
fprintf(stderr, "Failed to create Symbol '%s'. %s.\n", name, strerror(errno));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
symbol->Name = name;
|
||||
|
||||
return symbol;
|
||||
}
|
||||
|
||||
SymbolString* CreateSymbolString(char* text) {
|
||||
SymbolString* string = calloc(1, sizeof(SymbolString));
|
||||
|
||||
if (!string) {
|
||||
fprintf(stderr, "Failed to create string data for symbol. %s.\n", strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
string->String = text;
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
Symbol* TryGetSymbol(char* name, SymbolTable* table) {
|
||||
if (!name || !table) return NULL;
|
||||
|
||||
for(int i = 0; i < table->Size; i++) {
|
||||
if (strcmp(table->Symbols[i]->Name, name) == 0) return table->Symbols[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Symbol* AddSymbolToTable(char* name, SymbolTable* table) {
|
||||
//if (!name || !value || !table || length == 0) return NULL;
|
||||
|
||||
for(int i = 0; i < table->Size; i++) {
|
||||
if (strcmp(table->Symbols[i]->Name, name) == 0) {
|
||||
//TODO: Do we update or throw some kind of an error?
|
||||
|
||||
return table->Symbols[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (table->Capacity < table->Size + 1) {
|
||||
Symbol** newBlock = realloc(table->Symbols, sizeof(Symbol*) * table->Capacity * 2);//calloc(table->Size * 2, sizeof(Symbol*));
|
||||
|
||||
if (!newBlock) {
|
||||
fprintf(stderr, "Failed to realloc space for a new symbol '%s'. %s.\n", name, strerror(errno));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
table->Capacity *= 2;
|
||||
table->Symbols = newBlock;
|
||||
}
|
||||
|
||||
Symbol* symbol = CreateSymbol(name);
|
||||
|
||||
table->Symbols[table->Size] = symbol;
|
||||
table->Size++;
|
||||
|
||||
return symbol;
|
||||
}
|
||||
|
||||
void FreeSymbolTable(SymbolTable* table) {
|
||||
if (!table) return;
|
||||
|
||||
for(int i = 0; i < table->Size; i++) FreeSymbol(table->Symbols[i]);
|
||||
|
||||
free(table);
|
||||
}
|
||||
|
||||
void FreeSymbol(Symbol* symbol) {
|
||||
if (!symbol) return;
|
||||
|
||||
free(symbol);
|
||||
}
|
||||
+3
-7
@@ -1,6 +1,6 @@
|
||||
#include "../includes/token.h"
|
||||
|
||||
Token* CreateToken(char* lexeme, void* value, int lineNumber, TokenType type) {
|
||||
Token* CreateToken(int lineNumber, TokenClass tokenClass) {
|
||||
Token* token = calloc(1, sizeof(Token));
|
||||
|
||||
if (!token) {
|
||||
@@ -8,10 +8,8 @@ Token* CreateToken(char* lexeme, void* value, int lineNumber, TokenType type) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
token->type = type;
|
||||
token->line = lineNumber;
|
||||
token->lexeme = lexeme;
|
||||
token->value = value;
|
||||
token->Class = tokenClass;
|
||||
token->LineNumber = lineNumber;
|
||||
|
||||
return token;
|
||||
}
|
||||
@@ -19,7 +17,5 @@ Token* CreateToken(char* lexeme, void* value, int lineNumber, TokenType type) {
|
||||
void FreeToken(Token* token) {
|
||||
if (!token) return;
|
||||
|
||||
if (token->value && token->type >= STRING) free(token->value);
|
||||
|
||||
free(token);
|
||||
}
|
||||
Reference in New Issue
Block a user