Compare commits
68
Commits
5f437b6869
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4bb90da6df | ||
|
|
4af2527806 | ||
|
|
65383cded2 | ||
|
|
9c8fe17c8f | ||
|
|
3031fa8b9b | ||
|
|
2918f2964d | ||
|
|
980b0449fc | ||
|
|
6741dde7ce | ||
|
|
c6e6e2c5dc | ||
|
|
0eaa5527ea | ||
|
|
403439def4 | ||
|
|
0fd6413d81 | ||
|
|
a9e57132be | ||
|
|
aa3888b578 | ||
|
|
c3d12da54a | ||
|
|
288ba9f9cb | ||
|
|
a9a50055a4 | ||
|
|
2e888913be | ||
|
|
ee60d14570 | ||
|
|
fa12a04bb3 | ||
|
|
2d7aad1617 | ||
|
|
ae03991fa5 | ||
|
|
95ed2143ea | ||
|
|
f96e1c6380 | ||
|
|
4f9f4202b2 | ||
|
|
4230708f15 | ||
|
|
5e9e1bdec4 | ||
|
|
1b246e90bc | ||
|
|
9a602824b6 | ||
|
|
6d6bf1cbaf | ||
|
|
bca2ad67f1 | ||
|
|
fd44e03b1b | ||
|
|
48a77950be | ||
|
|
8b6edc39be | ||
|
|
c142d2c6d0 | ||
|
|
4b08707e67 | ||
|
|
bdb4d2b241 | ||
|
|
d53833c25f | ||
|
|
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 |
+4
-1
@@ -1,3 +1,6 @@
|
||||
assm
|
||||
obj/
|
||||
bin/
|
||||
bin/
|
||||
*.bin
|
||||
docs/*
|
||||
!docs/*.tex
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
CC = gcc
|
||||
CFLAGS=-g -Wall -DDEBUG -Wpedantic
|
||||
CFLAGS=-g -Wall -DDEBUG -Wpedantic -Wextra -Wunused-result -std=c99 -pedantic-errors
|
||||
SRCDIR=src
|
||||
OBJDIR=obj
|
||||
SRCS=$(wildcard $(SRCDIR)/*.c)
|
||||
@@ -11,7 +11,7 @@ BIN=$(BINDIR)/assm
|
||||
|
||||
all: $(BIN)
|
||||
|
||||
release: CFLAGS=-Wall -Wpedantic -O2
|
||||
release: CFLAGS=-Wall -Wpedantic -std=c99 -pedantic-errors -O2
|
||||
release: clean
|
||||
release: $(BIN)
|
||||
|
||||
@@ -35,7 +35,7 @@ clean:
|
||||
rm -rf $(BINDIR)/* $(OBJDIR)/*
|
||||
|
||||
test:
|
||||
$(BIN) misc/test.asm
|
||||
$(BIN) misc/another_test.asm
|
||||
|
||||
disass:
|
||||
objdump -S --disassemble $(OBJDIR)/$(FILE).o > $(OBJDIR)/$(FILE).s
|
||||
@@ -0,0 +1,187 @@
|
||||
\documentclass[a4paper,12pt]{book}
|
||||
\usepackage{tikz}
|
||||
\usepackage{hyperref}
|
||||
\hypersetup{
|
||||
linktoc=all
|
||||
}
|
||||
|
||||
\title{Unnamed Machine}
|
||||
\author{A Very Terrible 16-bit Machine}
|
||||
|
||||
\newcommand{\OpcodeTable}[5] {
|
||||
\begin{tabular}{ c c c c c }
|
||||
\hline
|
||||
Opcode & Bit Pattern & Mnemonic & Operand 1 & Operand 2 \\
|
||||
\hline\hline
|
||||
#1 & #2 & #3 & #4 & #5 \\
|
||||
\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}
|
||||
The instruction encoding is fixed width to exactly 8 bites wide.
|
||||
The encoding for the instructions will differ depending on if the opcode requires a register as its first operand.
|
||||
For instructions that require, as their first argument, a register the encoding format will be as shown in Figure \ref{fig:WithRegister}.
|
||||
The lower 3 bits may be any bit pattern as long as at least one bit is set in the upper 5 bits.
|
||||
%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 }
|
||||
Instruction & Register \\
|
||||
\hline
|
||||
XXXX X & 000 \\
|
||||
\hline
|
||||
\end{tabular}
|
||||
\caption{Encoding Layout, Register Required}
|
||||
\label{fig:WithRegister}
|
||||
\end{figure}
|
||||
|
||||
Figure \ref{fig:NoRegisterEncoding} shows how encoding will look for instructions that lack arguments or do not require a register.
|
||||
In contrast with the previous encoding scheme the upper 5 bits MUST be zero, allowing 7 possible instructions to use this format.
|
||||
All zeroes is not considered a legal instruction.
|
||||
|
||||
\begin{figure}[!htb]
|
||||
\centering
|
||||
\begin{tabular}{ c c }
|
||||
Must Be Zero & Instruction \\
|
||||
\hline
|
||||
0000 0 & XXX \\
|
||||
\hline
|
||||
\end{tabular}
|
||||
\caption{Encoding Layout, No Register Required}
|
||||
\label{fig:NoRegisterEncoding}
|
||||
\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. The Stack Pointer will start 2 bytes above the video memory start.
|
||||
\section{COPY (Copy Word from Address)}
|
||||
\OpcodeTable{0x08}{0000 1000}{copya}{Register}{Address}\\[6pt]
|
||||
Copy a machine word from Operand 2 into Operand 1.
|
||||
\section{COPY BYTE (Copy Byte from Address)}
|
||||
\OpcodeTable{0x10}{0001 0000}{copyab}{Register}{Address}\\[6pt]
|
||||
Copy a byte (8 bits) from Operand 2 into Operand 1, clearing the setting the most significant bits to zero.
|
||||
\section{COPY (Copy Word Indirect Address)}
|
||||
\OpcodeTable{0x18}{0001 1000}{copyra}{Register}{[Register]}\\[6pt]
|
||||
Copy a machine word from the address stored in Operand 2 into Operand 1.
|
||||
\section{COPY BYTE (Copy Byte Indirect Address)}
|
||||
\OpcodeTable{0x20}{0010 0000}{copyrab}{Register}{[Register]}\\[6pt]
|
||||
Copy a byte (8 bits) from the address stored in Operand 2 into Operand 1.
|
||||
\section{COPY (Copy)}
|
||||
\OpcodeTable{0x28}{0010 1000}{copyrara}{[Register]}{[Register]}\\[6pt]
|
||||
Copy a machine word from the address stored in Operand 2 into the address stored in Operand 1.
|
||||
\section{COPY BYTE (Copy Byte)}
|
||||
\OpcodeTable{0x30}{0011 0000}{copyrarab}{[Register]}{[Register]}\\[6pt]
|
||||
Copy a byte (8 bits) from the address stored in Operand 2 into the address stored in Operand 1.
|
||||
\section{COPY (Copy)}
|
||||
\OpcodeTable{0x38}{0011 1000}{copy}{Register}{Register}\\[6pt]
|
||||
Copy a machine word from Operand 2 into Operand 1.
|
||||
\section{COPY (Copy Immediate)}
|
||||
\OpcodeTable{0xC0}{1100 0000}{copyi}{Register}{Constant}\\[6pt]
|
||||
Copy a machine word from Operand 2 into Operand 1.
|
||||
\section{COPY BYTE (Copy Byte)}
|
||||
\OpcodeTable{0x40}{0100 0000}{copyb}{Register}{Constant}\\[6pt]
|
||||
Copy a byte (8 bits) from Operand 2 into Operand 1.
|
||||
\section{CMP (Compare)}
|
||||
\OpcodeTable{0x48}{0100 1000}{cmp}{Register}{Register}\\[6pt]
|
||||
Compares two registers and somewhere sets a result in the status register.
|
||||
\section{CMPI (Compare Immediate)}
|
||||
\OpcodeTable{0x50}{0101 0000}{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{0x58}{0101 1000}{add}{Register}{Register}\\[6pt]
|
||||
Performs addition on a register with a value from another (or the same) register.
|
||||
\section{SUB (Subtract)}
|
||||
\OpcodeTable{0x60}{0110 0000}{sub}{Register}{Register}\\[6pt]
|
||||
Performs subtraction on a register with a value from another (or the same) register.
|
||||
\section{AND (Logical AND)}
|
||||
\OpcodeTable{0x68}{0110 1000}{and}{Register}{Register}\\[6pt]
|
||||
Logical ANDs the two registers together storing the result in operand 1.
|
||||
\section{XOR (Logical Exclusive OR)}
|
||||
\OpcodeTable{0x70}{0111 0000}{xor}{Register}{Register}\\[6pt]
|
||||
Logical XORs the two registers together storing the result in operand 1.
|
||||
\section{OR (Logical OR)}
|
||||
\OpcodeTable{0x78}{0111 1000}{or}{Register}{Register}\\[6pt]
|
||||
Logical ORs the two registers together storing the result in operand 1.
|
||||
\section{NOT (Logical Negation)}
|
||||
\OpcodeTable{0x80}{1000 0000}{not}{Register}{None}\\[6pt]
|
||||
Inverts the bits of the target register.
|
||||
\section{SHR (Shift Right)}
|
||||
\OpcodeTable{0x88}{1000 1000}{shr}{Register}{Constant}\\[6pt]
|
||||
Bit-wise shifts the contents of the register right Constant number of times.
|
||||
\section{SHL (Shift Left)}
|
||||
\OpcodeTable{0x90}{1001 0000}{shl}{Register}{Constant}\\[6pt]
|
||||
Bit-wise shifts the contents of the register left Constant number of times.
|
||||
\section{INC (Increment)}
|
||||
\OpcodeTable{0x98}{1001 1000}{inc}{Register}{None}\\[6pt]
|
||||
Increments the contents of the register by one. Over-flows will not be reported.
|
||||
\section{DEC (Decrement)}
|
||||
\OpcodeTable{0xA0}{1010 0000}{dec}{Register}{None}\\[6pt]
|
||||
Decrements the contents of the register by one. Under-flows will not be reported.
|
||||
\section{Push}
|
||||
\OpcodeTable{0xA8}{1010 1000}{push}{Register}{None}
|
||||
Pushes the value of Register onto the stack, decrementing the Stack Pointer by 2.
|
||||
\section{Pop}
|
||||
\OpcodeTable{0xB0}{1011 0000}{pop}{Register}{None}
|
||||
Pops the top of the stack into Register, incrementing the Stack Pointer by 2.
|
||||
\section{JMPI (Jump Indirect)}
|
||||
\OpcodeTable{0xB8}{1011 1000}{jmpi}{Register}{None}\\[6pt]
|
||||
Jumps unconditionally to a memory address stored in Operand 1. Sets the Program Counter to Operand 1.
|
||||
\section{JMP (Jump)}
|
||||
\OpcodeTable{0x02}{0000 0010}{jmp}{Address}{None}\\[6pt]
|
||||
Jumps unconditionally to a memory address. Sets the Program Counter to Address.
|
||||
\section{JZ (Jump if Zero)}
|
||||
\OpcodeTable{0x03}{0000 0011}{jz}{Address}{None}\\[6pt]
|
||||
Jumps to a memory address if the status flag is zero. Sets the Program Counter to Address.
|
||||
\section{JG (Jump if Greater Than)}
|
||||
\OpcodeTable{0x04}{0000 0100}{jg}{Address}{None}\\[6pt]
|
||||
Jump to the Address if the status flag is greater than zero. Sets the Program Counter to Address.
|
||||
\section{JL (Jump if Less Than)}
|
||||
\OpcodeTable{0x05}{0000 0101}{jl}{Address}{None}\\[6pt]
|
||||
Jumps to the address if the status flag is less than zero. Sets the Program Counter to Address.
|
||||
\section{NOP (No Operation)}
|
||||
\OpcodeTable{0x01}{0000 0001}{nop}{None}{None}\\[6pt]
|
||||
Skips a clock cycle, incrementing the program counter.
|
||||
\section{CALL (Call Subroutine)}
|
||||
\OpcodeTable{0x06}{0000 0110}{call}{Address}{None}\\[6pt]
|
||||
Pushes the base address to the stack and sets the Program Counter to Address.
|
||||
\section{RET (Return from Subroutine)}
|
||||
\OpcodeTable{0x07}{0000 0111}{ret}{None}{None}\\[6pt]
|
||||
Pops the stack and sets the Program Counter to that value.
|
||||
\end{document}
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
|
||||
#include "parser.h"
|
||||
|
||||
void Disassemble(unsigned char image[HIGHMEMORY]);
|
||||
//void Disassemble(unsigned char image[HIGHMEMORY]);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
#ifndef LIST_H
|
||||
#define LIST_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define LISTDEFAULTSIZE 4
|
||||
|
||||
typedef struct {
|
||||
void** content;
|
||||
int size;
|
||||
int capacity;
|
||||
} List;
|
||||
|
||||
List* CreateList(void);
|
||||
int AddListItem(const void *, size_t, List *);
|
||||
void DestroyList(List*);
|
||||
|
||||
#endif
|
||||
+15
-21
@@ -2,29 +2,23 @@
|
||||
#define OPCODES_H
|
||||
|
||||
#include <string.h>
|
||||
#include "token.h"
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define OPCODECOUNT 13
|
||||
#define REGISTERCOUNT 8
|
||||
typedef enum {
|
||||
R1 = 0, R2, R3, R4, R5, R6, R7, R8 = 7
|
||||
} Registers;
|
||||
|
||||
typedef struct {
|
||||
char* lexeme;
|
||||
TokenType op;
|
||||
TokenClass parameter_one;
|
||||
TokenClass parameter_two;
|
||||
} Instruction;
|
||||
typedef enum {
|
||||
COPYA = 0x08, COPYAB = 0x10, COPYRA = 0x18, COPYRAB = 0x20, COPYRARA = 0x28, COPYRARAB = 0x30, COPY = 0x38, COPYB = 0x40, COPYI = 0xC0,
|
||||
CMP = 0x48, CMPI = 0x50, ADD = 0x58, SUB = 0x60, AND = 0x68, XOR = 0x70, OR = 0x78, NOT = 0x80, SHR = 0x88, SHL = 0x90,
|
||||
INC = 0x98, DEC = 0xA0, PUSH = 0xA8, POP = 0xB0,
|
||||
JMPI = 0xB8, JMP = 0x02, JZ = 0x03, JG = 0x04, JL = 0x05, NOP = 0x01, CALL = 0x06, RET = 0x07
|
||||
} Mnemonic;
|
||||
|
||||
typedef struct {
|
||||
char* lexeme;
|
||||
TokenType type;
|
||||
} Register;
|
||||
|
||||
extern Instruction instructions[OPCODECOUNT];
|
||||
extern Register registers[REGISTERCOUNT];
|
||||
|
||||
int IsOpcode(const char*, TokenType*);
|
||||
int IsRegister(const char*, TokenType*);
|
||||
const Instruction* GetOpcodeDetails(TokenType);
|
||||
unsigned char GetInstructionMask(TokenType type, TokenClass parameterOneType);
|
||||
int IsOpcode(const char*, Mnemonic*);
|
||||
int IsRegister(const char*, Registers*);
|
||||
void GetMnemonicText(Mnemonic mnemonic, char buffer[12]);
|
||||
void GetRegisterText(Registers reg, char buffer[3]);
|
||||
|
||||
#endif
|
||||
+5
-4
@@ -1,14 +1,15 @@
|
||||
#ifndef PARSER_H
|
||||
#define PARSER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "list.h"
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include "token.h"
|
||||
#include "opcodes.h"
|
||||
#include "symbols_table.h"
|
||||
|
||||
#define HIGHMEMORY 65535 //64KiB - 1 AKA 0xFFFF
|
||||
|
||||
unsigned char* ParseTokens(List*);
|
||||
void ParseTokens(TokenList* tokens, SymbolTable** symbolsTable);
|
||||
|
||||
#endif
|
||||
+1
-2
@@ -7,9 +7,8 @@
|
||||
#include <errno.h>
|
||||
#include "stdlib.h"
|
||||
#include "token.h"
|
||||
#include "list.h"
|
||||
#include "opcodes.h"
|
||||
|
||||
List* GenerateTokenList(const char*);
|
||||
TokenList* GenerateTokenList(const char*);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef SYMBOLSTABLE_H
|
||||
#define SYMBOLSTABLE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include "opcodes.h"
|
||||
#include "token.h"
|
||||
|
||||
#define SYMBOLSTABLE_DEFAULT_CAPACITY 128
|
||||
|
||||
typedef struct _symbol {
|
||||
char* Name;
|
||||
int Address;
|
||||
int Length;
|
||||
Token* Token;
|
||||
} Symbol;
|
||||
|
||||
typedef struct {
|
||||
Symbol** Symbols;
|
||||
int Size;
|
||||
int Capacity;
|
||||
} SymbolTable;
|
||||
|
||||
SymbolTable* CreateSymbolTable(void);
|
||||
int TryGetSymbol(char* name, SymbolTable* table, Symbol** outSymbol);
|
||||
Symbol* AddSymbolToTable(char* name, int address, SymbolTable* table);
|
||||
void FreeSymbolTable(SymbolTable* table);
|
||||
void FreeSymbol(Symbol* symbol);
|
||||
int SymbolResolved(Symbol* symbol);
|
||||
|
||||
#endif
|
||||
+55
-39
@@ -5,52 +5,68 @@
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define REGISTEROFFSET 29
|
||||
#include "opcodes.h"
|
||||
|
||||
typedef enum {
|
||||
PLUS,
|
||||
MINUS,
|
||||
STAR,
|
||||
SLASH,
|
||||
POWER,
|
||||
LPARAM,
|
||||
RPARAM,
|
||||
LBRACKET,
|
||||
RBracket,
|
||||
COMMA,
|
||||
STRING,
|
||||
IDENTIFIER,
|
||||
LABEL,
|
||||
NUMBER,
|
||||
LineEnd,
|
||||
//Keywords
|
||||
DB, ORG,
|
||||
//Opcodes
|
||||
COPY, ADD, SUB, JZ, INT, YLD, RET,
|
||||
CMP, NOP, JMP, CALL, 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 {
|
||||
None = 0,
|
||||
Reg = 1,
|
||||
Constant = 2,
|
||||
Address = 4,
|
||||
Opcode = 8,
|
||||
Directive = 16
|
||||
DB,
|
||||
Include,
|
||||
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;
|
||||
TokenClass token_class;
|
||||
typedef struct __token {
|
||||
char* Lemexe;
|
||||
TokenClass Class;
|
||||
int LineNumber;
|
||||
int EndOfFile;
|
||||
|
||||
union {
|
||||
TokenPunctuation Punctuation;
|
||||
Registers Register;
|
||||
Mnemonic Mnemonic;
|
||||
Directive Directive;
|
||||
int Number;
|
||||
} Value;
|
||||
|
||||
struct __token* Prev;
|
||||
struct __token* Next;
|
||||
} Token;
|
||||
|
||||
Token* CreateToken(char*, void*, int, TokenType);
|
||||
void FreeToken(Token*);
|
||||
typedef struct {
|
||||
Token** content;
|
||||
int size;
|
||||
int capacity;
|
||||
} TokenList;
|
||||
|
||||
Token* CreateToken(int lineNumber, TokenClass tokenClass);
|
||||
TokenList* CreateTokenList(void);
|
||||
int AddToken(Token* token, TokenList* list);
|
||||
void RemoveToken(int index, TokenList* list);
|
||||
void FreeToken(Token* token);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,44 @@
|
||||
.db MAX_MEM 0xFFFF
|
||||
.db VIDEO_MEM 0xF37F
|
||||
.db MAX_LENGTH MAX_MEM; - MAX_LENGTH
|
||||
.db MSG "Hello, World!", 0
|
||||
|
||||
__start:
|
||||
copy r1, MSG ; Pointer into r1
|
||||
copy r8, VIDEO_MEM
|
||||
call strlen
|
||||
copy r1, MSG
|
||||
cmp r2, 0
|
||||
jz _end
|
||||
cmp r2, MAX_LENGTH
|
||||
jg _end
|
||||
draw_loop:
|
||||
copy byte [r8], [r1]
|
||||
inc r1
|
||||
inc r8
|
||||
dec r2
|
||||
cmp r2, 0
|
||||
jz _end
|
||||
jmp draw_loop
|
||||
_end:
|
||||
jmp _end
|
||||
|
||||
.db NewMsg "My message", 0
|
||||
|
||||
; Returns the length of a NULL terminated string
|
||||
; Arguments: R1 - Pointer to the string
|
||||
; Returns: R2 - Contains the length of the string
|
||||
strlen:
|
||||
copy r2, 0 ; length
|
||||
copy byte r3, [r1]
|
||||
cmp r3, 0
|
||||
jz end ;The string is zero length
|
||||
loop:
|
||||
inc r1
|
||||
copy byte r3, [r1]
|
||||
cmp r3, 0
|
||||
jz end
|
||||
inc r2
|
||||
jmp loop
|
||||
end:
|
||||
ret
|
||||
+26
-26
@@ -1,27 +1,27 @@
|
||||
;.org 0x100
|
||||
;.include "./another_test.asm"
|
||||
.db labelsz reference
|
||||
.db video_start 0xF37F
|
||||
.db msg "Hello, world!", 0
|
||||
.db more_stuff "AA", 0
|
||||
copy r7, msg ; //
|
||||
cmp r8, 45 ;1000 1111
|
||||
add r4, 30
|
||||
sub r1, 69
|
||||
int 20
|
||||
jz 45
|
||||
jmp msg
|
||||
in 10
|
||||
call more_stuff
|
||||
ret
|
||||
yld
|
||||
|
||||
;copy r1, 45 ;//B0 = 10110000
|
||||
;copy 2, 45
|
||||
;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
|
||||
;ret
|
||||
; 4 byte header
|
||||
; | Address of First Opcode (2 bytes) | End of Binary (2 bytes) |
|
||||
.db NOTERM "No terminating byte here"
|
||||
.db null_byte 0
|
||||
jmp [r4]
|
||||
;load r2, label
|
||||
something_insance:
|
||||
load r1, unknown_symbol
|
||||
load r1, 5
|
||||
.db fun_alright 0x70;does this break?
|
||||
load r3, r4
|
||||
store byte r3, r5
|
||||
store r5, r7
|
||||
unknown_symbol:;Does this work?
|
||||
cmp r1, 5;or one after the register?
|
||||
cmp r1, r2;This might work
|
||||
and r1, r2
|
||||
add r4, r7
|
||||
inc r1 ;increment r1
|
||||
xor r1, r1 ;clear self
|
||||
pop r3
|
||||
jmp unknown_symbol
|
||||
jmp [r4]
|
||||
jz [r6]
|
||||
;And a comment at the end
|
||||
+5
-4
@@ -14,7 +14,7 @@ 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];
|
||||
@@ -32,8 +32,9 @@ void Disassemble(unsigned char image[HIGHMEMORY]) {
|
||||
IsRegisterPattern(instruction & 0x07, parameter1);
|
||||
unsigned char parameterType = instruction & 0x18;
|
||||
|
||||
if (!parameterType) {
|
||||
IsRegisterPattern(Image[position], parameter2);
|
||||
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;
|
||||
@@ -116,7 +117,7 @@ void Disassemble(unsigned char image[HIGHMEMORY]) {
|
||||
memset(parameter2, '\0', sizeof(char) * 32);
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
int IsRegisterPattern(unsigned char pattern, char* lexeme) {
|
||||
if (pattern > 8) {
|
||||
lexeme[0] = '\0';
|
||||
|
||||
-66
@@ -1,66 +0,0 @@
|
||||
#include "../includes/list.h"
|
||||
|
||||
List* CreateList() {
|
||||
List *new = malloc(sizeof(List));
|
||||
|
||||
if (!new) {
|
||||
fprintf(stderr, "Failed to malloc() for new new List.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
new->content = malloc(sizeof(void*) * LISTDEFAULTSIZE);
|
||||
|
||||
if (!new->content) {
|
||||
fprintf(stderr, "Failed to malloc() memory for List contents.\n");
|
||||
free(new);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
new->size = 0;
|
||||
new->capacity = LISTDEFAULTSIZE;
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
int AddListItem(const void *value, size_t size, List* list) {
|
||||
if (!list) return -1;
|
||||
if (!value) return -1;
|
||||
if (size == 0) return -1;
|
||||
|
||||
if (list->capacity < list->size + 1) {
|
||||
void* ptr = realloc(list->content, sizeof(void*) * list->capacity * 2);
|
||||
//Note: realloc will free list->root if it succeeds.
|
||||
if (!ptr) {
|
||||
fprintf(stderr, "Failed to resize array with realloc() (%d bytes).\n", list->capacity * 2);
|
||||
return -1;
|
||||
}
|
||||
|
||||
list->content = ptr;
|
||||
list->capacity = list->capacity * 2;
|
||||
}
|
||||
|
||||
void* item = calloc(1, size);
|
||||
|
||||
if (!item) {
|
||||
fprintf(stderr, "Failed to calloc() new memory (%lu bytes).\n", size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(item, value, size);
|
||||
|
||||
list->content[list->size] = item;
|
||||
list->size++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DestroyList(List* list) {
|
||||
if (!list) return;
|
||||
|
||||
for(int i = 0; i < list->size; i++) {
|
||||
free(list->content[i]);
|
||||
}
|
||||
|
||||
free(list->content);
|
||||
free(list);
|
||||
}
|
||||
+228
-22
@@ -1,12 +1,20 @@
|
||||
#include <bits/types/FILE.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sysexits.h>
|
||||
#include "../includes/list.h"
|
||||
#include "../includes/parser.h"
|
||||
#include "../includes/token.h"
|
||||
#include "../includes/futil.h"
|
||||
#include "../includes/scanner.h"
|
||||
#include "../includes/disass.h"
|
||||
#include "../includes/parser.h"
|
||||
|
||||
const char* MagicStartName = "__start";
|
||||
TokenList* LIST;
|
||||
SymbolTable* Symbols = NULL;
|
||||
|
||||
unsigned char mem[128] = {0};
|
||||
void print(void);
|
||||
void assemble(void);
|
||||
|
||||
int main(int argc, char* args[]) {
|
||||
if (argc == 1) {
|
||||
@@ -14,29 +22,227 @@ int main(int argc, char* args[]) {
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
atexit(print);
|
||||
|
||||
char* source_code;
|
||||
size_t bytes_read;
|
||||
|
||||
if (!ReadAllString(args[1], &source_code, &bytes_read)) return EX_IOERR;
|
||||
|
||||
List* list = GenerateTokenList(source_code);
|
||||
|
||||
// for(int i = 0; i < list->size; i++) {
|
||||
// Token* t = (Token*) list->content[i];
|
||||
|
||||
// if (t->type == LineEnd) {
|
||||
// if (i - 1 >= 0 && ((Token*) list->content[i - 1])->type != LineEnd)
|
||||
// printf("\n");
|
||||
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// printf("[%i] '%s' [%i] ", t->type, t->lexeme, t->token_class);
|
||||
// if (t->type == LABEL) printf("* ");
|
||||
// }
|
||||
|
||||
unsigned char* image = ParseTokens(list);
|
||||
Disassemble(image);
|
||||
LIST = GenerateTokenList(source_code);
|
||||
|
||||
ParseTokens(LIST, &Symbols);
|
||||
|
||||
free(source_code);
|
||||
}
|
||||
|
||||
assemble();
|
||||
|
||||
printf("\nBytes:\n");
|
||||
for(unsigned long i = 0; i < sizeof(mem); i++) {
|
||||
if (i != 0 && i % 8 == 0) printf("\n");
|
||||
printf("%02X ", mem[i] & 0xFF);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void print(void) {
|
||||
char mnemonic[12];
|
||||
printf("printing tokens...\n");
|
||||
|
||||
|
||||
for(int i = 0; i < LIST->size; i++) {
|
||||
Token* t = (Token*) LIST->content[i];
|
||||
|
||||
if (t->EndOfFile) {
|
||||
printf("EOF\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (t->Class == PunctuationClass){
|
||||
if(t->Value.Punctuation == NewLine) {
|
||||
printf("<%d>\n", t->LineNumber);
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("[P]%c", t->Value.Punctuation);
|
||||
continue;
|
||||
}
|
||||
|
||||
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("[C]'%s'", t->Lemexe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int CurrentIndex = 0;
|
||||
int PC2 = 0;
|
||||
|
||||
void WriteByte(unsigned char byte) {
|
||||
mem[PC2] = byte;
|
||||
PC2++;
|
||||
}
|
||||
|
||||
void WriteWord(unsigned short word) {
|
||||
mem[PC2] = (word >> 8) & 0xFF;
|
||||
mem[PC2 + 1] = word & 0xFF;
|
||||
PC2 += 2;
|
||||
}
|
||||
|
||||
unsigned short GetValueFromToken(Token* token) {
|
||||
if (token->Class & IdentifierClass) {
|
||||
Symbol* symbol;
|
||||
|
||||
if (TryGetSymbol(token->Lemexe, Symbols, &symbol)) {
|
||||
return symbol->Address;
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Failed to find symbol %s while assembling.\n", token->Lemexe);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
else if (token->Class == NumberClass) return token->Value.Number;
|
||||
|
||||
fprintf(stderr, "Failed to find token '%d' while assmbling.\n", token->Class);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int IsAtEnd(void){
|
||||
return CurrentIndex >= LIST->size;
|
||||
}
|
||||
|
||||
void assemble() {
|
||||
while(!IsAtEnd()) {
|
||||
Token* current = LIST->content[CurrentIndex];
|
||||
switch(current->Value.Mnemonic) {
|
||||
case COPYA:
|
||||
case COPYAB:
|
||||
CurrentIndex++;
|
||||
|
||||
WriteByte(current->Value.Mnemonic | ((Token*) LIST->content[CurrentIndex])->Value.Register);
|
||||
CurrentIndex++;
|
||||
WriteWord(GetValueFromToken(LIST->content[CurrentIndex]));
|
||||
break;
|
||||
case COPYRA:
|
||||
case COPYRAB:
|
||||
case COPYRARA:
|
||||
case COPYRARAB:
|
||||
case COPY:
|
||||
CurrentIndex++;
|
||||
|
||||
WriteByte(current->Value.Mnemonic | ((Token*) LIST->content[CurrentIndex])->Value.Register);
|
||||
CurrentIndex++;
|
||||
WriteByte(((Token*) LIST->content[CurrentIndex])->Value.Register);
|
||||
break;
|
||||
case COPYI: //TODO: should explicitly get number from value I feel
|
||||
CurrentIndex++;
|
||||
|
||||
WriteByte(current->Value.Mnemonic | ((Token*) LIST->content[CurrentIndex])->Value.Register);
|
||||
CurrentIndex++;
|
||||
WriteWord(GetValueFromToken(LIST->content[CurrentIndex]));
|
||||
break;
|
||||
case COPYB:
|
||||
CurrentIndex++;
|
||||
|
||||
WriteByte(current->Value.Mnemonic | ((Token*) LIST->content[CurrentIndex])->Value.Register);
|
||||
CurrentIndex++;
|
||||
WriteByte((unsigned char)GetValueFromToken(LIST->content[CurrentIndex]));
|
||||
break;
|
||||
case CMP:
|
||||
CurrentIndex++;
|
||||
|
||||
WriteByte(current->Value.Mnemonic | ((Token*) LIST->content[CurrentIndex])->Value.Register);
|
||||
CurrentIndex++;
|
||||
WriteByte(((Token*) LIST->content[CurrentIndex])->Value.Register);
|
||||
break;
|
||||
case CMPI:
|
||||
CurrentIndex++;
|
||||
|
||||
WriteByte(current->Value.Mnemonic | ((Token*) LIST->content[CurrentIndex])->Value.Register);
|
||||
CurrentIndex++;
|
||||
WriteWord(GetValueFromToken(LIST->content[CurrentIndex]));
|
||||
break;
|
||||
case ADD:
|
||||
case SUB:
|
||||
case AND:
|
||||
case OR:
|
||||
case XOR:
|
||||
CurrentIndex++;
|
||||
|
||||
WriteByte(current->Value.Mnemonic | ((Token*) LIST->content[CurrentIndex])->Value.Register);
|
||||
CurrentIndex++;
|
||||
WriteByte(((Token*) LIST->content[CurrentIndex])->Value.Register);
|
||||
break;
|
||||
case SHL:
|
||||
case SHR:
|
||||
CurrentIndex++;
|
||||
|
||||
WriteByte(current->Value.Mnemonic | ((Token*) LIST->content[CurrentIndex])->Value.Register);
|
||||
CurrentIndex++;
|
||||
WriteWord(GetValueFromToken(LIST->content[CurrentIndex]));
|
||||
break;
|
||||
case INC:
|
||||
case DEC:
|
||||
case PUSH:
|
||||
case POP:
|
||||
case JMPI:
|
||||
CurrentIndex++;
|
||||
|
||||
WriteByte(current->Value.Mnemonic | ((Token*) LIST->content[CurrentIndex])->Value.Register);
|
||||
break;
|
||||
case JMP:
|
||||
case JZ:
|
||||
case JG:
|
||||
case JL:
|
||||
WriteByte(current->Value.Mnemonic);
|
||||
CurrentIndex++;
|
||||
WriteWord(GetValueFromToken(LIST->content[CurrentIndex]));
|
||||
break;
|
||||
case NOP:
|
||||
case RET:
|
||||
WriteByte(current->Value.Mnemonic);
|
||||
break;
|
||||
case CALL:
|
||||
WriteByte(current->Value.Mnemonic);
|
||||
CurrentIndex++;
|
||||
WriteWord(GetValueFromToken(LIST->content[CurrentIndex]));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
CurrentIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+72
-105
@@ -1,114 +1,76 @@
|
||||
#include "../includes/opcodes.h"
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
Instruction instructions[OPCODECOUNT] = {
|
||||
{ "copy", COPY, Reg | Address, Reg | Constant | Address },
|
||||
{ "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 },
|
||||
{ "in", IN, Constant | Reg, None},
|
||||
{ "out", OUT, None, None},
|
||||
{ "nop", NOP, None, None},
|
||||
{ "jmp", JMP, Address, None},
|
||||
{ "call", CALL, Address, None}
|
||||
#define OPCODECOUNT 34
|
||||
|
||||
struct _instruction {
|
||||
char* Name;
|
||||
Mnemonic Mnemonic;
|
||||
};
|
||||
|
||||
unsigned char GetInstructionMask(TokenType type, TokenClass parameterOneType) {
|
||||
switch (type) {
|
||||
case COPY:
|
||||
if (parameterOneType & Reg) return 0x20; //0b00100000;
|
||||
|
||||
return 0xA0;//0b10100000;
|
||||
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 IN:
|
||||
return 0x07;//0b00000111;
|
||||
case OUT:
|
||||
return 0x08;//0b00001000;
|
||||
default:
|
||||
return 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
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 }
|
||||
struct _instruction instructions[31] = {
|
||||
{ "copya", COPYA },
|
||||
{ "copyab", COPYAB },
|
||||
{ "copyra", COPYRA },
|
||||
{ "copyrab", COPYRAB },
|
||||
{ "copyrara", COPYRARA },
|
||||
{ "copyrarab", COPYRARAB },
|
||||
{ "copy", COPY },
|
||||
{ "copyi", COPYI },
|
||||
{ "copyb", COPYB },
|
||||
{ "cmp", CMP },
|
||||
{ "cmpi", CMPI },
|
||||
{ "add", ADD },
|
||||
{ "sub", SUB },
|
||||
{ "and", AND },
|
||||
{ "xor", XOR },
|
||||
{ "or", OR },
|
||||
{ "not", NOT },
|
||||
{ "shr", SHR },
|
||||
{ "shl", SHL },
|
||||
{ "inc", INC },
|
||||
{ "dec", DEC },
|
||||
{ "push", PUSH },
|
||||
{ "pop", POP },
|
||||
{ "jmpi", JMPI },
|
||||
{ "jmp", JMP },
|
||||
{ "jz", JZ },
|
||||
{ "jg", JG },
|
||||
{ "jl", JL },
|
||||
{ "nop", NOP },
|
||||
{ "call", CALL },
|
||||
{ "ret", RET }
|
||||
//yld
|
||||
};
|
||||
|
||||
const Instruction* GetOpcodeDetails(TokenType type) {
|
||||
void GetMnemonicText(Mnemonic mnemonic, char buffer[12]) {
|
||||
memset(buffer, '\0', 12);
|
||||
|
||||
for(int i = 0; i < OPCODECOUNT; i++) {
|
||||
if (instructions[i].op == type) return &instructions[i];
|
||||
if (instructions[i].Mnemonic == mnemonic) {
|
||||
strncpy(buffer, instructions[i].Name, 11);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int IsOpcode(const char* text, TokenType* opcode) {
|
||||
void GetRegisterText(Registers reg, char buffer[3]) {
|
||||
memset(buffer, '\0', 3);
|
||||
|
||||
if (reg < R1 || reg > R8) return;
|
||||
|
||||
buffer[0] = 'r';
|
||||
buffer[1] = reg + 49;
|
||||
}
|
||||
|
||||
int IsOpcode(const char* text, Mnemonic* opcode) {
|
||||
if (!text) return 0;
|
||||
|
||||
for(int i = 0; i < OPCODECOUNT; i++) {
|
||||
if (strcmp(instructions[i].lexeme, text) == 0) {
|
||||
if (opcode) *opcode = instructions[i].op;
|
||||
for(unsigned long i = 0; i < sizeof(instructions) / sizeof(struct _instruction); i++) {
|
||||
if (strcmp(instructions[i].Name, text) == 0) {
|
||||
if (opcode) *opcode = instructions[i].Mnemonic;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -116,15 +78,20 @@ 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;
|
||||
|
||||
return 0;
|
||||
if (length != 2) return 0;
|
||||
if (text[0] != 'r') return 0;
|
||||
|
||||
if (!isdigit(text[1])) return 0;
|
||||
|
||||
r = text[1] - 0x31;
|
||||
|
||||
if (reg) *reg = r;
|
||||
|
||||
return 1;
|
||||
}
|
||||
+464
-399
@@ -1,463 +1,529 @@
|
||||
#include "../includes/parser.h"
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct {
|
||||
Token* token;
|
||||
int address;
|
||||
} Symbol;
|
||||
typedef enum {
|
||||
NoOptions = 0, ForwardParser = 1, RemoveExpected = 2
|
||||
} ExpectOptions;
|
||||
|
||||
struct intr {
|
||||
TokenType struction;
|
||||
unsigned int parameter1;
|
||||
unsigned int parameter2;
|
||||
};
|
||||
|
||||
Symbol* CreateSymbol(Token* token, int address);
|
||||
const List* TokensList;
|
||||
TokenList* TokensList;
|
||||
int CurrentToken = 0;
|
||||
//int HeapStart = 4; // zero indexed, used for declared variables.
|
||||
//int BinaryEnd = 4; // Zero indexed (binary starts with a 4 byte header)
|
||||
|
||||
void AddSymbol(Token*, int);
|
||||
void PrintSymbols(void);
|
||||
void HandleOperation(void);
|
||||
void RemoveCurrentToken(void);
|
||||
void HandleAssemblerDirective(void);
|
||||
Token* ExpectMnemonic(void);
|
||||
void AdvanceParser(void);
|
||||
void IgnoreParserLine(void);
|
||||
Token* PeekToken(void);
|
||||
int ParserAtEnd(void);
|
||||
int Expect(int, ...);
|
||||
int ExpectTokenClass(int, ...);
|
||||
const Symbol* GetSymbol(char*);
|
||||
List* SymbolsTable;
|
||||
void WriteMemory(unsigned char value, int location);
|
||||
void WriteMemoryORMask(unsigned char mask, int location);
|
||||
unsigned int HeapTop = 4;
|
||||
unsigned int ProgramCounter = 0;
|
||||
unsigned char Heap[HIGHMEMORY];
|
||||
unsigned char Memory[HIGHMEMORY];
|
||||
|
||||
unsigned char* ParseTokens(List* tokens) {
|
||||
if (!tokens) return NULL;
|
||||
void ExpectPuncuation(TokenPunctuation punctuation, ExpectOptions options);
|
||||
void ExpectRegister(void);
|
||||
Symbol* ExpectIdentifier(ExpectOptions options, int expectUnresolved);
|
||||
void ExpectCharacterClass(Symbol* symbol);
|
||||
void ExpectLineEndOrFileEnd(ExpectOptions options);
|
||||
|
||||
memset(Memory, 0, sizeof(Memory));
|
||||
memset(Heap, 0, sizeof(Heap));
|
||||
SymbolTable* SymbolsTable;
|
||||
|
||||
//int HeapSize = 0;
|
||||
int PC = 0;
|
||||
|
||||
void ParseTokens(TokenList* tokens, SymbolTable** symbols) {
|
||||
if (!tokens) return;
|
||||
if (!symbols) return;
|
||||
|
||||
TokensList = tokens;
|
||||
|
||||
SymbolsTable = CreateList();
|
||||
if (!*symbols) *symbols = CreateSymbolTable();
|
||||
|
||||
SymbolsTable = *symbols;
|
||||
|
||||
while(!ParserAtEnd()) {
|
||||
Token* t = PeekToken();
|
||||
if (!t || t->EndOfFile) break;
|
||||
|
||||
switch(t->token_class) {
|
||||
case Directive:
|
||||
switch(t->Class) {
|
||||
case DirectiveClass:
|
||||
HandleAssemblerDirective();
|
||||
break;
|
||||
case Opcode:
|
||||
HandleOperation();
|
||||
case MnemonicClass:
|
||||
ExpectMnemonic();
|
||||
break;
|
||||
case Address:
|
||||
if (t->type == LABEL) {
|
||||
printf("%s: \n", t->lexeme);
|
||||
AddSymbol(t, ProgramCounter);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
case LabelClass:
|
||||
{
|
||||
Symbol* symbol;
|
||||
int found = TryGetSymbol(t->Lemexe, SymbolsTable, &symbol);
|
||||
|
||||
AdvanceParser();
|
||||
}
|
||||
|
||||
if (SymbolsTable->size > 0) PrintSymbols();
|
||||
|
||||
DestroyList(SymbolsTable);
|
||||
|
||||
memcpy(&Heap[HeapTop], Memory, ProgramCounter);
|
||||
int totalSize = HeapTop + ProgramCounter;
|
||||
Heap[0] = 2 >> HeapTop & 0xFF;
|
||||
Heap[1] = HeapTop & 0xFF;
|
||||
Heap[2] = 2 >> totalSize & 0xFF;
|
||||
Heap[3] = totalSize & 0xFF;
|
||||
|
||||
// for(int i = 0; i < 33; i++) {
|
||||
// if (i != 0 && i % 3 == 0) printf("\n");
|
||||
// printf("%02X ", Heap[i] & 0xFF);
|
||||
// }
|
||||
// printf("\n");
|
||||
// printf("Program Counter: %d\n", ProgramCounter);
|
||||
// printf("Heap Top: %#06X\n", HeapTop);
|
||||
|
||||
return Heap;
|
||||
}
|
||||
|
||||
void HandleAssemblerDirective(void) {
|
||||
|
||||
if (PeekToken()->type == DB) {
|
||||
AdvanceParser();
|
||||
|
||||
Token* identifier = PeekToken();
|
||||
|
||||
if (!Expect(1, IDENTIFIER)) {
|
||||
fprintf(stderr, "Expected identifier on line %d\n", identifier->line);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
AddSymbol(identifier, HeapTop);
|
||||
|
||||
identifier = PeekToken();
|
||||
|
||||
switch(identifier->type) {
|
||||
case STRING:
|
||||
//printf("[INFO] %s = '%s' (%#04X)\n", identifier->lexeme, identifier->lexeme, HeapTop);
|
||||
|
||||
for(int i = 0; i < strlen(identifier->lexeme); i++) {
|
||||
//Memory[ProgramCounter + i] = PeekToken()->lexeme[i];
|
||||
Heap[HeapTop + i] = identifier->lexeme[i];
|
||||
}
|
||||
|
||||
//ProgramCounter += strlen(PeekToken()->lexeme);
|
||||
HeapTop += strlen(identifier->lexeme);
|
||||
|
||||
AdvanceParser();
|
||||
|
||||
if (PeekToken()->type == COMMA) {
|
||||
AdvanceParser();
|
||||
|
||||
if (PeekToken()-> type != NUMBER) {
|
||||
fprintf(stderr, "Expected string termination byte on line %d\n", PeekToken()->line);
|
||||
if (found && SymbolResolved(symbol)) {
|
||||
fprintf(stderr, "[Error] Line %d: Redefinition of symbol '%s'\n", t->LineNumber, t->Lemexe);
|
||||
exit(1);
|
||||
}
|
||||
else {
|
||||
//Add the NULL byte.
|
||||
//Memory[ProgramCounter] = '\0';
|
||||
Heap[HeapTop] = '\0';
|
||||
//ProgramCounter++;
|
||||
HeapTop++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!found) symbol = AddSymbolToTable(t->Lemexe, PC, SymbolsTable);
|
||||
// else
|
||||
// {
|
||||
symbol->Address = PC;
|
||||
//symbol->Type = RefPointer;
|
||||
//symbol->Token = t;
|
||||
//}
|
||||
|
||||
RemoveCurrentToken(); //label
|
||||
|
||||
ExpectLineEndOrFileEnd(RemoveExpected);
|
||||
|
||||
//symbol->Value.Mnemonic = ExpectMnemonic();
|
||||
symbol->Token = ExpectMnemonic();
|
||||
}
|
||||
case NUMBER:
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "[Line: %d] Expected either a number or string after identifier '%s'.\n", PeekToken()->line, identifier->lexeme);
|
||||
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();
|
||||
}
|
||||
|
||||
void HandleRegisterBasedOpcode(unsigned char instruction, unsigned char mask) {
|
||||
Memory[ProgramCounter] = mask;
|
||||
void HandleAssemblerDirective() {
|
||||
Token* directive = PeekToken();
|
||||
|
||||
ProgramCounter++;
|
||||
switch(directive->Value.Directive){
|
||||
case DB:
|
||||
{
|
||||
RemoveCurrentToken();
|
||||
|
||||
const Token* token = PeekToken();
|
||||
Symbol* symbol = ExpectIdentifier(RemoveExpected, 1);
|
||||
|
||||
if (token->token_class == Reg) {
|
||||
Memory[ProgramCounter - 1] |= (PeekToken()->type - R1) & 0x07;
|
||||
if (PeekToken()->Class == CharacterClass) {
|
||||
ExpectCharacterClass(symbol);
|
||||
|
||||
AdvanceParser();
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "[Error] Line %d: Expected register operand.\n", token->line);
|
||||
exit(1);
|
||||
}
|
||||
//symbol->Type = RefLiteral;
|
||||
}
|
||||
else if (PeekToken()->Class == NumberClass) {
|
||||
symbol->Length = 2;
|
||||
|
||||
token = PeekToken();
|
||||
RemoveCurrentToken(); //Clear the number token.
|
||||
|
||||
ExpectLineEndOrFileEnd(RemoveExpected);
|
||||
|
||||
if (!Expect(1, COMMA)) {
|
||||
fprintf(stderr, "[Error] Expected command one line %d.\n", token->line);
|
||||
exit(1);
|
||||
}
|
||||
//symbol->Type = RefLiteral;
|
||||
}
|
||||
else if (PeekToken()->Class == IdentifierClass) {
|
||||
//symbol->Type = RefExpression;
|
||||
|
||||
token = PeekToken();
|
||||
symbol = ExpectIdentifier(RemoveExpected, 0);
|
||||
symbol->Length = 2;
|
||||
|
||||
if (token->type == IDENTIFIER || token->type == LABEL) {
|
||||
const Symbol* symbol = GetSymbol(token->lexeme);
|
||||
//TODO: This needs to be expanded to handle expressions, a symbol to symbol reg is not allowed.
|
||||
|
||||
if (!symbol) {
|
||||
fprintf(stderr, "[Error] Line %d: %s is undefined.\n", token->line, token->lexeme);
|
||||
ExpectLineEndOrFileEnd(RemoveExpected);
|
||||
}
|
||||
|
||||
//HeapSize += symbol->Length;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Synatx error on line %d, %s.\n", directive->LineNumber, directive->Lemexe);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
Memory[ProgramCounter - 1] |= 0x10;//0b00010000;
|
||||
|
||||
Memory[ProgramCounter] = 2 >> symbol->address & 0xFF;
|
||||
Memory[ProgramCounter + 1] = symbol->address & 0xFF;
|
||||
|
||||
AdvanceParser();
|
||||
}
|
||||
else if (token->type == NUMBER) {
|
||||
Memory[ProgramCounter - 1] |= 0x08; //0b00001000;
|
||||
|
||||
int* value = PeekToken()->value;
|
||||
|
||||
Memory[ProgramCounter] = 2 >> *value & 0xFF;
|
||||
Memory[ProgramCounter + 1] = *value & 0xFF;
|
||||
|
||||
AdvanceParser();
|
||||
} else {
|
||||
fprintf(stderr, "[Error] Expected operand, got %s\n", token->lexeme);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ProgramCounter += 2;
|
||||
}
|
||||
|
||||
void HandleOperation(void) {
|
||||
const Instruction* inst = GetOpcodeDetails(PeekToken()->type);
|
||||
const unsigned char registerBasedOpcodeMask = 0xE0;
|
||||
Token* ExpectMnemonic(void) {
|
||||
Token* opcode = PeekToken();
|
||||
|
||||
if (!inst) return;
|
||||
if (opcode->Class != MnemonicClass) {
|
||||
fprintf(stderr, "Syntax error on line %d: expected mnemonic.\n", opcode->LineNumber);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
AdvanceParser();
|
||||
|
||||
unsigned char mask = GetInstructionMask(inst->op, inst->parameter_one);
|
||||
switch(opcode->Value.Mnemonic) {
|
||||
case COPY:
|
||||
{
|
||||
Token* arg = PeekToken();
|
||||
int isByte = 0;
|
||||
|
||||
if (mask & registerBasedOpcodeMask) {
|
||||
HandleRegisterBasedOpcode(Memory[ProgramCounter], mask);
|
||||
if (arg->Class == DirectiveClass) {
|
||||
if (arg->Value.Directive != Byte) {
|
||||
fprintf(stderr, "Syntax error on line %d: expected keyword 'byte'.\n", opcode->LineNumber);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
RemoveCurrentToken(); //byte
|
||||
|
||||
isByte = 1;
|
||||
|
||||
arg = PeekToken();
|
||||
}
|
||||
|
||||
PC++;
|
||||
|
||||
if (arg->Class == RegisterClass) {
|
||||
ExpectRegister();
|
||||
|
||||
ExpectPuncuation(Comma, RemoveExpected);
|
||||
|
||||
arg = PeekToken();
|
||||
|
||||
if (arg->Class == NumberClass) {
|
||||
opcode->Value.Mnemonic = isByte ? COPYB : COPYI;
|
||||
opcode->Lemexe = isByte ? "copyb" : "copyi";
|
||||
|
||||
isByte ? PC++ : (PC += 2);
|
||||
|
||||
AdvanceParser();
|
||||
}
|
||||
else if (arg->Class == RegisterClass) {
|
||||
if (isByte) {
|
||||
fprintf(stderr, "Syntax error on line %d: unexpected modifier 'Byte' for Register to Register copy.\n", opcode->LineNumber);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ExpectRegister();
|
||||
|
||||
opcode->Value.Mnemonic = COPY;
|
||||
opcode->Lemexe = "copy";
|
||||
|
||||
PC++;
|
||||
}
|
||||
else if (arg->Class & IdentifierClass) {
|
||||
opcode->Value.Mnemonic = isByte ? COPYAB : COPYA;
|
||||
opcode->Lemexe = isByte ? "copyab" : "copya";
|
||||
PC += 2;
|
||||
|
||||
ExpectIdentifier(ForwardParser, 0);
|
||||
}
|
||||
else {
|
||||
ExpectPuncuation(LBracket, RemoveExpected);
|
||||
|
||||
ExpectRegister();
|
||||
|
||||
ExpectPuncuation(RBracket, RemoveExpected);
|
||||
|
||||
opcode->Value.Mnemonic = isByte ? COPYRAB : COPYRA;
|
||||
opcode->Lemexe = isByte ? "copyrab" : "copyra";
|
||||
PC++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ExpectPuncuation(LBracket, RemoveExpected);
|
||||
|
||||
ExpectRegister();
|
||||
|
||||
ExpectPuncuation(RBracket, RemoveExpected);
|
||||
|
||||
ExpectPuncuation(Comma, RemoveExpected);
|
||||
|
||||
ExpectPuncuation(LBracket, RemoveExpected);
|
||||
|
||||
ExpectRegister();
|
||||
|
||||
ExpectPuncuation(RBracket, RemoveExpected);
|
||||
|
||||
opcode->Value.Mnemonic = isByte ? COPYRARAB : COPYRARA;
|
||||
opcode->Lemexe = isByte ? "copyrarab" : "copyrara";
|
||||
|
||||
PC++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CMP:
|
||||
{
|
||||
ExpectRegister();
|
||||
|
||||
ExpectPuncuation(Comma, RemoveExpected);
|
||||
|
||||
TokenClass class = PeekToken()->Class;
|
||||
|
||||
PC++;
|
||||
|
||||
if (class == NumberClass) {
|
||||
opcode->Value.Mnemonic = CMPI;
|
||||
opcode->Lemexe = "CMPI";
|
||||
|
||||
AdvanceParser();
|
||||
|
||||
PC += 2;
|
||||
}
|
||||
else if (class & IdentifierClass) {
|
||||
opcode->Value.Mnemonic = CMPI;
|
||||
opcode->Lemexe = "CMPI";
|
||||
|
||||
ExpectIdentifier(ForwardParser, 0);
|
||||
|
||||
PC += 2;
|
||||
}
|
||||
else {
|
||||
ExpectRegister();
|
||||
|
||||
PC++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ADD:
|
||||
case SUB:
|
||||
case AND:
|
||||
case OR:
|
||||
case XOR:
|
||||
PC++;
|
||||
|
||||
ExpectRegister();
|
||||
|
||||
ExpectPuncuation(Comma, RemoveExpected);
|
||||
|
||||
ExpectRegister();
|
||||
|
||||
PC++;
|
||||
break;
|
||||
case SHL:
|
||||
case SHR:
|
||||
PC++;
|
||||
ExpectRegister();
|
||||
|
||||
ExpectPuncuation(Comma, RemoveExpected);
|
||||
|
||||
if (PeekToken()->Class != NumberClass) {
|
||||
fprintf(stderr, "[Line %d] Syntax error, expected numeric literal.\n", PeekToken()->LineNumber);
|
||||
|
||||
exit(12);
|
||||
}
|
||||
|
||||
AdvanceParser();
|
||||
|
||||
PC += 2;
|
||||
break;
|
||||
case INC:
|
||||
case DEC:
|
||||
case PUSH:
|
||||
case POP:
|
||||
PC++;
|
||||
ExpectRegister();
|
||||
break;
|
||||
case JMP:
|
||||
{
|
||||
PC++;
|
||||
|
||||
Token* arg = PeekToken();
|
||||
|
||||
if (arg->Class & IdentifierClass) {
|
||||
ExpectIdentifier(ForwardParser, 0);
|
||||
|
||||
opcode->Value.Mnemonic = JMP;
|
||||
opcode->Lemexe = "jmp";
|
||||
|
||||
PC += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
ExpectPuncuation(LBracket, RemoveExpected);
|
||||
|
||||
ExpectRegister();
|
||||
|
||||
ExpectPuncuation(RBracket, RemoveExpected);
|
||||
|
||||
opcode->Value.Mnemonic = JMPI;
|
||||
opcode->Lemexe = "jmpi";
|
||||
}
|
||||
}
|
||||
break;
|
||||
case JZ:
|
||||
{
|
||||
PC++;
|
||||
|
||||
ExpectIdentifier(ForwardParser, 0);
|
||||
|
||||
PC += 2;
|
||||
|
||||
opcode->Value.Mnemonic = JZ;
|
||||
opcode->Lemexe = "jz";
|
||||
}
|
||||
break;
|
||||
case JG:
|
||||
{
|
||||
PC++;
|
||||
|
||||
ExpectIdentifier(ForwardParser, 0);
|
||||
|
||||
PC += 2;
|
||||
|
||||
opcode->Value.Mnemonic = JG;
|
||||
opcode->Lemexe = "jg";
|
||||
}
|
||||
break;
|
||||
case JL:
|
||||
{
|
||||
PC++;
|
||||
|
||||
ExpectIdentifier(ForwardParser, 0);
|
||||
|
||||
PC += 2;
|
||||
|
||||
opcode->Value.Mnemonic = JL;
|
||||
opcode->Lemexe = "jl";
|
||||
}
|
||||
break;
|
||||
case CALL:
|
||||
{
|
||||
PC++;
|
||||
|
||||
Token* arg = PeekToken();
|
||||
|
||||
if ((arg->Class & IdentifierClass) == 0) {
|
||||
fprintf(stderr, "Syntax error on line %d: expected subroutine call target.\n", opcode->LineNumber);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ExpectIdentifier(ForwardParser, 0);
|
||||
|
||||
PC += 2;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ExpectLineEndOrFileEnd(ForwardParser);
|
||||
|
||||
return opcode;
|
||||
}
|
||||
|
||||
void ExpectPuncuation(TokenPunctuation punctuation, ExpectOptions options) {
|
||||
Token* token = PeekToken();
|
||||
|
||||
if (token->Class != PunctuationClass || token->Value.Punctuation != punctuation) {
|
||||
fprintf(stderr, "[Line %d] Syntac error, expected %c.\n", token->LineNumber, punctuation);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (options & RemoveExpected) RemoveCurrentToken();
|
||||
if (options & ForwardParser) AdvanceParser();
|
||||
}
|
||||
|
||||
void ExpectRegister() {
|
||||
Token* token = PeekToken();
|
||||
|
||||
if (token->Class != RegisterClass) {
|
||||
fprintf(stderr, "[Line %d] Syntax error, expected Register.\n", token->LineNumber);
|
||||
exit(2);
|
||||
}
|
||||
|
||||
AdvanceParser();
|
||||
}
|
||||
|
||||
Symbol* ExpectIdentifier(ExpectOptions options, int expectUnresolved) {
|
||||
Token* token = PeekToken();
|
||||
|
||||
if (token->Class != IdentifierClass) {
|
||||
fprintf(stderr, "[Line %d] Expected Identifier.\n", token->LineNumber);
|
||||
exit(3);
|
||||
}
|
||||
|
||||
Symbol* symbol;
|
||||
|
||||
int found = TryGetSymbol(token->Lemexe, SymbolsTable, &symbol);
|
||||
|
||||
if (found && SymbolResolved(symbol) && expectUnresolved) {
|
||||
fprintf(stderr, "[Line %d] Redefinition of symbol '%s'.\n", token->LineNumber, token->Lemexe);
|
||||
exit(4);
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
//symbol->Resolved = resolved || symbol->Resolved(symbol);
|
||||
symbol = AddSymbolToTable(token->Lemexe, PC, SymbolsTable);
|
||||
}
|
||||
|
||||
if (expectUnresolved) symbol->Token = token;
|
||||
|
||||
// else {
|
||||
// symbol = AddSymbolToTable(token->Lemexe, PC, SymbolsTable);
|
||||
// }
|
||||
|
||||
if (options & RemoveExpected) RemoveCurrentToken();
|
||||
if (options & ForwardParser) AdvanceParser();
|
||||
|
||||
return symbol;
|
||||
}
|
||||
|
||||
void ExpectCharacterClass(Symbol* symbol) {
|
||||
Token* token = PeekToken();
|
||||
|
||||
if (token->Class != CharacterClass) {
|
||||
fprintf(stderr, "[Line %d] Syntax error, expected character string.\n", token->LineNumber);
|
||||
exit(5);
|
||||
}
|
||||
|
||||
symbol->Length = strlen(token->Lemexe);
|
||||
|
||||
RemoveCurrentToken(); //Remove the string declared by this DB command.
|
||||
|
||||
token = PeekToken();
|
||||
|
||||
if (token->EndOfFile) return;
|
||||
|
||||
if (token->Class == PunctuationClass && token->Value.Punctuation == NewLine) {
|
||||
RemoveCurrentToken();
|
||||
return;
|
||||
}
|
||||
|
||||
ExpectPuncuation(Comma, RemoveExpected);
|
||||
|
||||
switch (mask) {
|
||||
case 0: //NOP
|
||||
Memory[ProgramCounter] = 0x00;
|
||||
ProgramCounter++;
|
||||
return;
|
||||
case 1: //JZ
|
||||
Memory[ProgramCounter] = 0x01;
|
||||
token = PeekToken();
|
||||
|
||||
ProgramCounter++;
|
||||
|
||||
if (PeekToken()->type == IDENTIFIER || PeekToken()->type == LABEL) {
|
||||
const Symbol* symbol = GetSymbol(PeekToken()->lexeme);
|
||||
|
||||
if (!symbol) {
|
||||
fprintf(stderr, "[Error] %s is undefined.\n", PeekToken()->lexeme);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
Memory[ProgramCounter] = 2 >> symbol->address & 0xFF;
|
||||
Memory[ProgramCounter + 1] = symbol->address & 0xFF;
|
||||
}
|
||||
else if (PeekToken()->type == NUMBER) {
|
||||
int* value = PeekToken()->value;
|
||||
|
||||
Memory[ProgramCounter] = 2 >> *value & 0xFF;
|
||||
Memory[ProgramCounter + 1] = *value & 0xFF;
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "[Error] Line: %d: Expected address after jump if zero (JZ) instruction.\n", ((Token*) TokensList->content[CurrentToken - 1])->line);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ProgramCounter += 2;
|
||||
|
||||
return;
|
||||
case 2: //INT
|
||||
Memory[ProgramCounter] = 0x02;
|
||||
|
||||
ProgramCounter++;
|
||||
|
||||
if (PeekToken()->type == NUMBER) {
|
||||
int* value = PeekToken()->value;
|
||||
|
||||
Memory[ProgramCounter] = 2 >> *value & 0xFF;
|
||||
Memory[ProgramCounter + 1] = *value & 0xFF;
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "[Error] Line %d: Expected Interrupt vector.\n", PeekToken()->line);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ProgramCounter += 2;
|
||||
|
||||
return;
|
||||
case 3: //YLD
|
||||
Memory[ProgramCounter] = 0x03;
|
||||
|
||||
ProgramCounter++;
|
||||
break;
|
||||
case 4: //RET
|
||||
Memory[ProgramCounter] = 0x04;
|
||||
|
||||
ProgramCounter++;
|
||||
break;
|
||||
case 5: //CALL
|
||||
Memory[ProgramCounter] = 0x05;
|
||||
|
||||
ProgramCounter++;
|
||||
|
||||
if (PeekToken()->type == IDENTIFIER || PeekToken()->type == LABEL) {
|
||||
const Symbol* symbol = GetSymbol(PeekToken()->lexeme);
|
||||
|
||||
if (!symbol) {
|
||||
fprintf(stderr, "[Error] Line %d: %s is undefined.\n", PeekToken()->line, PeekToken()->lexeme);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
Memory[ProgramCounter] = 2 >> symbol->address & 0xFF;
|
||||
Memory[ProgramCounter + 1] = symbol->address & 0xFF;
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "[Error] Line %d: Expected address or label.\n", PeekToken()->line);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ProgramCounter += 2;
|
||||
break;
|
||||
case 6: //JMP
|
||||
Memory[ProgramCounter] = 0x06;
|
||||
|
||||
ProgramCounter++;
|
||||
|
||||
if (PeekToken()->type == IDENTIFIER || PeekToken()->type == LABEL) {
|
||||
const Symbol* symbol = GetSymbol(PeekToken()->lexeme);
|
||||
|
||||
if (!symbol) {
|
||||
fprintf(stderr, "[Error] Line %d: %s is undefined.\n", PeekToken()->line, PeekToken()->lexeme);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
Memory[ProgramCounter] = 2 >> symbol->address & 0xFF;
|
||||
Memory[ProgramCounter + 1] = symbol->address & 0xFF;
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "[Error] Line %d: Expected address or label.\n", PeekToken()->line);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ProgramCounter += 2;
|
||||
break;
|
||||
case 7: //IN
|
||||
Memory[ProgramCounter] = 0x07;
|
||||
|
||||
ProgramCounter++;
|
||||
|
||||
if (PeekToken()->type == NUMBER) {
|
||||
int* value = PeekToken()->value;
|
||||
|
||||
Memory[ProgramCounter] = 2 >> *value & 0xFF;
|
||||
Memory[ProgramCounter + 1] = *value & 0xFF;
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "[Error] Line %d: Expected port number.\n", PeekToken()->line);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ProgramCounter += 2;
|
||||
break;
|
||||
case 8: //OUT
|
||||
Memory[ProgramCounter] = 0x08;
|
||||
|
||||
ProgramCounter++;
|
||||
|
||||
if (PeekToken()->type == NUMBER) {
|
||||
int* value = PeekToken()->value;
|
||||
|
||||
Memory[ProgramCounter] = 2 >> *value & 0xFF;
|
||||
Memory[ProgramCounter + 1] = *value & 0xFF;
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "[Error] Line %d: Expected port number.\n", PeekToken()->line);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ProgramCounter += 2;
|
||||
break;
|
||||
if (token->Class != NumberClass) {
|
||||
fprintf(stderr, "[Line %d] Syntax error, expected terminating byte.\n", token->LineNumber);
|
||||
exit(7);
|
||||
}
|
||||
|
||||
return;
|
||||
RemoveCurrentToken(); //Remove terminating byte.
|
||||
//TODO: add the raw value of the byte to the end of the string, but for now just pretend all numbers are zero.
|
||||
symbol->Length++;
|
||||
|
||||
ExpectLineEndOrFileEnd(RemoveExpected);
|
||||
}
|
||||
|
||||
int ExpectTokenClass(int count, ...) {
|
||||
va_list list;
|
||||
void ExpectLineEndOrFileEnd(ExpectOptions options) {
|
||||
Token* token = PeekToken();
|
||||
|
||||
va_start(list, count);
|
||||
if (token->EndOfFile) return;
|
||||
|
||||
for(int i = 0; i < count; i++) {
|
||||
if (va_arg(list, TokenClass) == token->token_class) {
|
||||
va_end(list);
|
||||
AdvanceParser();
|
||||
return 1;
|
||||
}
|
||||
if (token->Class != PunctuationClass || token->Value.Punctuation != NewLine) {
|
||||
fprintf(stderr, "[Line %d] Syntax error, expected line break.\n", token->LineNumber);
|
||||
exit(9);
|
||||
}
|
||||
|
||||
va_end(list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Expect(int count, ...) {
|
||||
va_list list;
|
||||
Token* token = PeekToken();
|
||||
|
||||
va_start(list, count);
|
||||
|
||||
for(int i = 0; i < count; i++) {
|
||||
if (va_arg(list, TokenType) == token->type) {
|
||||
va_end(list);
|
||||
AdvanceParser();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
va_end(list);
|
||||
|
||||
return 0;
|
||||
if (options & RemoveExpected) RemoveCurrentToken();
|
||||
if (options & ForwardParser) AdvanceParser();
|
||||
}
|
||||
|
||||
void PrintSymbols(void) {
|
||||
char mn[12];
|
||||
printf("-----SYMBOLS-----\n");
|
||||
for(int i = 0; i < SymbolsTable->size; i++) {
|
||||
Symbol* symbol = SymbolsTable->content[i];
|
||||
printf("[%#06X] %s\n", symbol->address, symbol->token->lexeme);
|
||||
for(int i = 0; i < SymbolsTable->Size; i++) {
|
||||
Symbol* symbol = SymbolsTable->Symbols[i];
|
||||
|
||||
printf("[%s] %s [Width: %d]", SymbolResolved(symbol) == 0 ? "Unresolved" : "Resolved", symbol->Name, symbol->Length);
|
||||
|
||||
if (symbol->Token && symbol->Token->Class == MnemonicClass)
|
||||
{
|
||||
GetMnemonicText(symbol->Token->Value.Mnemonic, mn);
|
||||
printf(" -> [%s]", mn);
|
||||
}
|
||||
|
||||
if (symbol->Token) printf(" Line %d", symbol->Token->LineNumber);
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
printf("-----SYMBOLS-----\n");
|
||||
}
|
||||
|
||||
void AddSymbol(Token* token, int address) {
|
||||
if (!token) return;
|
||||
if (token->type != IDENTIFIER && token->type != LABEL) return;
|
||||
|
||||
for(int i = 0; i < SymbolsTable->size; i++) {
|
||||
Symbol* s = SymbolsTable->content[i];
|
||||
|
||||
if (strcmp(s->token->lexeme, token->lexeme) == 0) return;
|
||||
}
|
||||
|
||||
Symbol* symbol = CreateSymbol(token, address);
|
||||
|
||||
AddListItem(symbol, sizeof(Symbol), SymbolsTable);
|
||||
}
|
||||
|
||||
const Symbol* GetSymbol(char* name) {
|
||||
if (!name) return NULL;
|
||||
|
||||
for(int i = 0; i < SymbolsTable->size; i++) {
|
||||
const Symbol* s = SymbolsTable->content[i];
|
||||
|
||||
if (strcmp(s->token->lexeme, name) == 0) return s;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Symbol* CreateSymbol(Token* token, int address) {
|
||||
Symbol* symbol = calloc(1, sizeof(Symbol));
|
||||
|
||||
if (!symbol) return NULL;
|
||||
|
||||
symbol->token = token;
|
||||
symbol->address = address;
|
||||
|
||||
return symbol;
|
||||
}
|
||||
|
||||
void AdvanceParser(void) {
|
||||
if (ParserAtEnd()) return;
|
||||
|
||||
@@ -474,20 +540,19 @@ Token* PeekToken(void) {
|
||||
return TokensList->content[CurrentToken];
|
||||
}
|
||||
|
||||
void WriteMemory(unsigned char value, int location) {
|
||||
if (location > HIGHMEMORY) {
|
||||
fprintf(stderr, "[Error] Exceeded memmory size\n");
|
||||
exit(1);
|
||||
}
|
||||
void IgnoreParserLine(void) {
|
||||
while(!ParserAtEnd()) {
|
||||
if (PeekToken()->Class == PunctuationClass && PeekToken()->Value.Punctuation == NewLine) {
|
||||
AdvanceParser();
|
||||
break;
|
||||
}
|
||||
|
||||
Memory[location] = value;
|
||||
if (PeekToken()->EndOfFile) break;
|
||||
|
||||
AdvanceParser();
|
||||
}
|
||||
}
|
||||
|
||||
void WriteMemoryORMask(unsigned char mask, int location) {
|
||||
if (location > HIGHMEMORY) {
|
||||
fprintf(stderr, "[Error] Exceeded memmory size\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
Memory[location] |= mask;
|
||||
void RemoveCurrentToken(void) {
|
||||
RemoveToken(CurrentToken, TokensList);
|
||||
}
|
||||
+151
-41
@@ -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);
|
||||
@@ -18,8 +21,8 @@ Token* ParseNumber(void);
|
||||
Token* ParsePunctuation(char);
|
||||
void IgnoreLine(void);
|
||||
|
||||
List* GenerateTokenList(const char* source) {
|
||||
List* tokens = CreateList();
|
||||
TokenList* GenerateTokenList(const char* source) {
|
||||
TokenList* tokens = CreateTokenList();
|
||||
Token* token = NULL;
|
||||
|
||||
if (!tokens) return NULL;
|
||||
@@ -34,41 +37,60 @@ List* GenerateTokenList(const char* source) {
|
||||
case ' ':
|
||||
case '\r':
|
||||
case '\t':
|
||||
case '\v':
|
||||
case '\f':
|
||||
AdvanceScanner();
|
||||
break; //Ignore whitespace
|
||||
case '\n':
|
||||
token = CreateToken("^", NULL, Line, LineEnd);
|
||||
AddListItem(token, sizeof(Token), tokens);
|
||||
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;
|
||||
AddToken(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"
|
||||
case '.': //directive like ".include" or ".db"
|
||||
token = ParseDirective();
|
||||
if (token) AddListItem(token, sizeof(Token), tokens);
|
||||
if (token) AddToken(token, tokens);
|
||||
break;
|
||||
case '"':
|
||||
token = ParseString();
|
||||
if (token) AddListItem(token, sizeof(Token), tokens);
|
||||
if (token) AddToken(token, tokens);
|
||||
break;
|
||||
default:
|
||||
if (isdigit(c)) {
|
||||
AddListItem(ParseNumber(), sizeof(Token), tokens);
|
||||
AddToken(ParseNumber(), tokens);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (IsPunctuation(c)) {
|
||||
AdvanceScanner();
|
||||
AddListItem(ParsePunctuation(c), sizeof(Token), tokens);
|
||||
AddToken(ParsePunctuation(c), tokens);
|
||||
break;
|
||||
}
|
||||
|
||||
token = ParseIdentifier();
|
||||
|
||||
if (token) AddListItem(token, sizeof(Token), tokens);
|
||||
if (token) AddToken(token, tokens);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -76,6 +98,21 @@ 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;
|
||||
|
||||
AddToken(token, tokens);
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
@@ -85,9 +122,21 @@ Token* ParseNumber(void) {
|
||||
while(!ScannerAtEnd() && isdigit(PeekScanner()))
|
||||
AdvanceScanner();
|
||||
|
||||
if (PeekScanner() == 'x' && isdigit(PeekAheadScanner())) {
|
||||
AdvanceScanner(); //Consume the 'x'
|
||||
while(!ScannerAtEnd() && isdigit(PeekScanner())) AdvanceScanner();
|
||||
if (tolower(PeekScanner()) == 'x') {
|
||||
char ahead = tolower(PeekAheadScanner());
|
||||
|
||||
if ((ahead >= 'a' && ahead <= 'f') || isdigit(ahead)) {
|
||||
AdvanceScanner(); //Consume the 'x'
|
||||
|
||||
while(!ScannerAtEnd() && PeekScanner() != '\n' && !IsWhiteSpace(PeekScanner()) && PeekScanner() != ';') {
|
||||
if (isdigit(PeekScanner())) {
|
||||
AdvanceScanner();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tolower(PeekScanner()) >= 'a' && tolower(PeekScanner()) <= 'f') AdvanceScanner();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int length = Position - start;
|
||||
@@ -95,24 +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);
|
||||
|
||||
Token* token = CreateToken(Line, NumberClass);
|
||||
|
||||
token->Value.Number = strtol(lexeme, NULL, 0);
|
||||
|
||||
if (errno != 0) {
|
||||
fprintf(stderr, "[Error] Line %d: Invalid number detected. %s.\n", Line, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
memcpy(lexeme, &SourceCode[start], length);
|
||||
// Setting the base to zero means the function will pick the base.
|
||||
*value = strtol(lexeme, NULL, 0);
|
||||
token->Lemexe = lexeme;
|
||||
|
||||
return CreateToken(lexeme, value, Line, NUMBER);
|
||||
return token;
|
||||
}
|
||||
|
||||
Token* ParseDirective(void) {
|
||||
@@ -133,19 +184,23 @@ 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);
|
||||
IgnoreLine();
|
||||
return NULL;
|
||||
else if (strcmp(directive, ".include") == 0) {
|
||||
token->Value.Directive = Include;
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
fprintf(stderr, "[Error] Unknown assembler directive '%s'\n", directive);
|
||||
|
||||
free(directive);
|
||||
free(token);
|
||||
|
||||
IgnoreLine();
|
||||
|
||||
@@ -178,7 +233,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;
|
||||
}
|
||||
@@ -186,7 +243,7 @@ Token* ParseString(void) {
|
||||
Token* ParseIdentifier(void) {
|
||||
int start = Position;
|
||||
|
||||
while(!ScannerAtEnd() && !IsPunctuation(PeekScanner()) && PeekScanner() != ' ' && PeekScanner() != '\n') {
|
||||
while(!ScannerAtEnd() && !IsPunctuation(PeekScanner()) && PeekScanner() != ' ' && PeekScanner() != '\n' && PeekScanner() != ';') {
|
||||
AdvanceScanner();
|
||||
}
|
||||
|
||||
@@ -194,7 +251,8 @@ Token* ParseIdentifier(void) {
|
||||
|
||||
if (length == 0) return NULL;
|
||||
|
||||
TokenType type;
|
||||
Mnemonic mnemonics;
|
||||
Registers reg;
|
||||
char* lexeme = calloc(sizeof(char), length + 1);
|
||||
|
||||
if (!lexeme) {
|
||||
@@ -204,14 +262,41 @@ 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;
|
||||
}
|
||||
if (strcmp(lexeme, "byte") == 0) {
|
||||
Token* token = CreateToken(Line, DirectiveClass);
|
||||
|
||||
token->Lemexe = lexeme;
|
||||
token->Value.Directive = Byte;
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
return CreateToken(lexeme, lexeme, Line, IDENTIFIER);
|
||||
Token* token = CreateToken(Line, IdentifierClass);
|
||||
|
||||
token->Lemexe = lexeme;
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
char PeekScanner(void) {
|
||||
@@ -249,19 +334,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,120 @@
|
||||
#include "../includes/symbols_table.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int SymbolResolved(Symbol* symbol) {
|
||||
if (!symbol) return 0;
|
||||
|
||||
return symbol->Token != NULL;
|
||||
|
||||
// switch (symbol->Type) {
|
||||
// case RefLiteral:
|
||||
// return 1;
|
||||
// case RefExpression:
|
||||
// return 0;
|
||||
// case RefPointer:
|
||||
// return symbol->Value.Mnemonic != 0;
|
||||
// default:
|
||||
// return 0;
|
||||
// }
|
||||
}
|
||||
|
||||
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, int address) {
|
||||
Symbol* symbol = calloc(1, sizeof(Symbol));
|
||||
|
||||
if (!symbol) {
|
||||
fprintf(stderr, "Failed to create Symbol '%s'. %s.\n", name, strerror(errno));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
symbol->Address = address;
|
||||
symbol->Name = name;
|
||||
//symbol->Type = RefUnknown;
|
||||
|
||||
return symbol;
|
||||
}
|
||||
|
||||
int TryGetSymbol(char* name, SymbolTable* table, Symbol** outSymbol) {
|
||||
*outSymbol = NULL;
|
||||
|
||||
if (!name || !table) return 0;
|
||||
|
||||
for(int i = 0; i < table->Size; i++) {
|
||||
if (strcmp(table->Symbols[i]->Name, name) == 0) {
|
||||
*outSymbol = table->Symbols[i];
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Symbol* AddSymbolToTable(char* name, int address, 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);
|
||||
|
||||
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, address);
|
||||
|
||||
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);
|
||||
}
|
||||
+69
-26
@@ -1,8 +1,8 @@
|
||||
#include "../includes/token.h"
|
||||
|
||||
TokenClass GetTokenClass(TokenType);
|
||||
#define LISTDEFAULTSIZE 32
|
||||
|
||||
Token* CreateToken(char* lexeme, void* value, int lineNumber, TokenType type) {
|
||||
Token* CreateToken(int lineNumber, TokenClass tokenClass) {
|
||||
Token* token = calloc(1, sizeof(Token));
|
||||
|
||||
if (!token) {
|
||||
@@ -10,36 +10,79 @@ 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->token_class = GetTokenClass(type);
|
||||
token->Class = tokenClass;
|
||||
token->LineNumber = lineNumber;
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
TokenClass GetTokenClass(TokenType type) {
|
||||
if (type >= R1 && type <= R8) return Reg;
|
||||
if (type >= COPY && type <= OUT) return Opcode;
|
||||
if (type >= DB && type <= ORG) return Directive;
|
||||
|
||||
switch(type) {
|
||||
case STRING:
|
||||
case IDENTIFIER:
|
||||
case LABEL:
|
||||
return Address;
|
||||
case NUMBER:
|
||||
return Constant;
|
||||
default:
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
void FreeToken(Token* token) {
|
||||
if (!token) return;
|
||||
|
||||
if (token->value && (token->type >= STRING || token->type == NUMBER)) free(token->value);
|
||||
|
||||
free(token);
|
||||
}
|
||||
|
||||
TokenList* CreateTokenList(void) {
|
||||
TokenList *new = malloc(sizeof(TokenList));
|
||||
|
||||
if (!new) {
|
||||
fprintf(stderr, "Failed to malloc() for new new List.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
new->content = calloc(LISTDEFAULTSIZE, sizeof(void*));
|
||||
|
||||
if (!new->content) {
|
||||
fprintf(stderr, "Failed to malloc() memory for List contents.\n");
|
||||
free(new);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
new->size = 0;
|
||||
new->capacity = LISTDEFAULTSIZE;
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
int AddToken(Token* token, TokenList* list) {
|
||||
if (!list || !token) return 0;
|
||||
|
||||
if (list->capacity < list->size + 1) {
|
||||
void* ptr = realloc(list->content, sizeof(void*) * list->capacity * 2);
|
||||
//Note: realloc will free list->root if it succeeds.
|
||||
if (!ptr) {
|
||||
fprintf(stderr, "Failed to resize array with realloc() (%d bytes).\n", list->capacity * 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
list->content = ptr;
|
||||
list->capacity *= 2;
|
||||
}
|
||||
|
||||
if (list->size > 0)
|
||||
{
|
||||
Token* prev = list->content[list->size - 1];
|
||||
|
||||
token->Prev = prev;
|
||||
prev->Next = token;
|
||||
}
|
||||
|
||||
list->content[list->size] = token;
|
||||
list->size++;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void RemoveToken(int index, TokenList* list) {
|
||||
Token* token = list->content[index];
|
||||
|
||||
if (token->Prev) token->Prev->Next = token->Next;
|
||||
|
||||
memmove(&list->content[index], &list->content[index + 1], (list->size - index) * sizeof(Token*));
|
||||
|
||||
list->size--;
|
||||
|
||||
list->content[list->size] = NULL;
|
||||
|
||||
FreeToken(token);
|
||||
}
|
||||
Reference in New Issue
Block a user