initial commit
commit
2cee60a17c
@ -0,0 +1,2 @@
|
||||
venv
|
||||
__pycache__
|
@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.8" project-jdk-type="Python SDK" />
|
||||
</project>
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/riscemu.iml" filepath="$PROJECT_DIR$/.idea/riscemu.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="PyDocumentationSettings">
|
||||
<option name="format" value="PLAIN" />
|
||||
<option name="myDocStringFormat" value="Plain" />
|
||||
</component>
|
||||
</module>
|
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,42 @@
|
||||
# RiscV (userspace) emulator in python
|
||||
|
||||
Implementing a basic RISC-V emulator, aimed at being easily extendable.
|
||||
|
||||
Currently supported (but not implemented) instructions:
|
||||
|
||||
````
|
||||
lb, lh, lw, lbu, lhu, sb, sh, sw, sll, slli, srl, srli, sra,
|
||||
srai, add, addi, sub, lui, auipc, xor, xori, or, ori, and,
|
||||
andi, slt, slti, sltu, sltiu, beq, bne, blt, bge, bltu, bgeu,
|
||||
j, jr, jal, jalr, ret, scall, break, nop
|
||||
````
|
||||
|
||||
Current register implementation (should be all standard userspace registers):
|
||||
```
|
||||
Registers[read,written](
|
||||
zero=0x00000000 ra =0x00000000 sp =0x00000000 gp =0x00000000 tp =0x00000000 fp =0x00000000
|
||||
--------------- --------------- --------------- --------------- --------------- ---------------
|
||||
a0 =0x00000000 s0 =0x00000000 t0 =0x00000000 ft0 =0x00000000 fa0 =0x00000000 fs0 =0x00000000
|
||||
a1 =0x00000000 s1 =0x00000000 t1 =0x00000000 ft1 =0x00000000 fa1 =0x00000000 fs1 =0x00000000
|
||||
a2 =0x00000000 s2 =0x00000000 t2 =0x00000000 ft2 =0x00000000 fa2 =0x00000000 fs2 =0x00000000
|
||||
a3 =0x00000000 s3 =0x00000000 t3 =0x00000000 ft3 =0x00000000 fa3 =0x00000000 fs3 =0x00000000
|
||||
a4 =0x00000000 s4 =0x00000000 t4 =0x00000000 ft4 =0x00000000 fa4 =0x00000000 fs4 =0x00000000
|
||||
a5 =0x00000000 s5 =0x00000000 t5 =0x00000000 ft5 =0x00000000 fa5 =0x00000000 fs5 =0x00000000
|
||||
a6 =0x00000000 s6 =0x00000000 t6 =0x00000000 ft6 =0x00000000 fa6 =0x00000000 fs6 =0x00000000
|
||||
a7 =0x00000000 s7 =0x00000000 ft7 =0x00000000 fa7 =0x00000000 fs7 =0x00000000
|
||||
s8 =0x00000000 fs8 =0x00000000
|
||||
s9 =0x00000000 fs9 =0x00000000
|
||||
s10 =0x00000000 fs10=0x00000000
|
||||
s11 =0x00000000 fs11=0x00000000
|
||||
)
|
||||
```
|
||||
|
||||
Current pseudo ops:
|
||||
```
|
||||
.align, .ascii, .asciiz, .byte, .data, .double, .extern,
|
||||
.float, .globl, .half, .kdata, .ktext, .set, .space, .text, .word
|
||||
```
|
||||
|
||||
## Resources:
|
||||
* Pseudo ops: https://www.codetd.com/article/8981522
|
||||
|
@ -0,0 +1,256 @@
|
||||
from dataclasses import dataclass
|
||||
from collections import defaultdict
|
||||
|
||||
COLOR = True
|
||||
|
||||
FMT_ORANGE = '\033[33m'
|
||||
FMT_GRAY = '\033[37m'
|
||||
FMT_BOLD = '\033[1m'
|
||||
FMT_NONE = '\033[0m'
|
||||
FMT_UNDERLINE = '\033[4m'
|
||||
|
||||
|
||||
class Registers:
|
||||
def __init__(self, cpu: 'CPU'):
|
||||
self.cpu = cpu
|
||||
self.vals = defaultdict(lambda: 0)
|
||||
self.last_mod = 'ft0'
|
||||
self.last_access = 'a3'
|
||||
|
||||
def dump(self, small=False):
|
||||
named_regs = [self.reg_repr(reg) for reg in Registers.named_registers()]
|
||||
|
||||
lines = [[] for i in range(12)]
|
||||
if small:
|
||||
regs = [('a', 8), ('s', 12), ('t', 7)]
|
||||
else:
|
||||
regs = [
|
||||
('a', 8),
|
||||
('s', 12),
|
||||
('t', 7),
|
||||
('ft', 8),
|
||||
('fa', 8),
|
||||
('fs', 12),
|
||||
]
|
||||
for name, s in regs:
|
||||
for i in range(12):
|
||||
if i >= s:
|
||||
lines[i].append(" " * 15)
|
||||
else:
|
||||
reg = '{}{}'.format(name, i)
|
||||
lines[i].append(self.reg_repr(reg))
|
||||
|
||||
print("Registers[{},{}](".format(
|
||||
FMT_ORANGE + FMT_UNDERLINE + 'read' + FMT_NONE,
|
||||
FMT_ORANGE + FMT_BOLD + 'written' + FMT_NONE
|
||||
))
|
||||
if small:
|
||||
print("\t" + " ".join(named_regs[0:3]))
|
||||
print("\t" + " ".join(named_regs[3:]))
|
||||
print("\t" + "--------------- " * 3)
|
||||
else:
|
||||
print("\t" + " ".join(named_regs))
|
||||
print("\t" + "--------------- " * 6)
|
||||
for line in lines:
|
||||
print("\t" + " ".join(line))
|
||||
print(")")
|
||||
|
||||
def reg_repr(self, reg):
|
||||
txt = '{:4}=0x{:08X}'.format(reg, self.get(reg))
|
||||
if reg == 'fp':
|
||||
reg = 's0'
|
||||
if reg == self.last_mod:
|
||||
return FMT_ORANGE + FMT_BOLD + txt + FMT_NONE
|
||||
if reg == self.last_access:
|
||||
return FMT_ORANGE + FMT_UNDERLINE + txt + FMT_NONE
|
||||
if reg == 'zero':
|
||||
return txt
|
||||
if self.get(reg) == 0 and reg not in Registers.named_registers():
|
||||
return FMT_GRAY + txt + FMT_NONE
|
||||
return txt
|
||||
|
||||
def set(self, reg, val):
|
||||
if reg == 'zero':
|
||||
print("[Registers.set] trying to set read-only register: {}".format(reg))
|
||||
return False
|
||||
if reg not in Registers.all_registers():
|
||||
print("[Registers.set] invalid register name: {}".format(reg))
|
||||
return False
|
||||
# replace fp register with s1, as these are the same register
|
||||
if reg == 'fp':
|
||||
reg = 's1'
|
||||
self.last_mod = reg
|
||||
setattr(self, reg, val)
|
||||
|
||||
def get(self, reg):
|
||||
if not reg in Registers.all_registers():
|
||||
print("[Registers.get] invalid register name: {}".format(reg))
|
||||
return 0
|
||||
if reg == 'fp':
|
||||
reg = 's0'
|
||||
return self.vals[reg]
|
||||
|
||||
@staticmethod
|
||||
def all_registers():
|
||||
return ['zero', 'ra', 'sp', 'gp', 'tp', 's0', 'fp',
|
||||
't0', 't1', 't2', 't3', 't4', 't5', 't6',
|
||||
's1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's10', 's11',
|
||||
'a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7',
|
||||
'ft0', 'ft1', 'ft2', 'ft3', 'ft4', 'ft5', 'ft6', 'ft7',
|
||||
'fs0', 'fs1', 'fs2', 'fs3', 'fs4', 'fs5', 'fs6', 'fs7', 'fs8', 'fs9', 'fs10', 'fs11',
|
||||
'fa0', 'fa1', 'fa2', 'fa3', 'fa4', 'fa5', 'fa6', 'fa7']
|
||||
|
||||
@staticmethod
|
||||
def named_registers():
|
||||
return ['zero', 'ra', 'sp', 'gp', 'tp', 'fp']
|
||||
|
||||
class CPU:
|
||||
def instruction_lb(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_lh(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_lw(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_lbu(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_lhu(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_sb(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_sh(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_sw(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_sll(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_slli(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_srl(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_srli(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_sra(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_srai(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_add(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_addi(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_sub(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_lui(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_auipc(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_xor(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_xori(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_or(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_ori(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_and(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_andi(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_slt(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_slti(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_sltu(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_sltiu(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_beq(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_bne(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_blt(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_bge(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_bltu(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_bgeu(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_j(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_jr(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_jal(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_jalr(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_ret(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_scall(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_break(self, instruction):
|
||||
pass
|
||||
|
||||
def instruction_nop(self, instruction):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def all_instructions():
|
||||
for method in vars(CPU):
|
||||
if method.startswith('instruction_'):
|
||||
yield method[12:]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Syscall:
|
||||
id: int
|
||||
registers: Registers
|
||||
|
||||
|
||||
class SyscallInterface:
|
||||
def handle_syscall(self, scall: Syscall):
|
||||
pass
|
||||
|
||||
|
||||
a = Registers(None)
|
||||
a.dump()
|
@ -0,0 +1,2 @@
|
||||
from .CPU import CPU, Registers, Syscall, SyscallInterface
|
||||
from .tokenizer import RiscVToken, RiscVInput, RiscVTokenizer, RiscVInstructionToken, RiscVSymbolToken, RiscVPseudoOpToken
|
@ -0,0 +1,3 @@
|
||||
from .CPU import *
|
||||
from .tokenizer import *
|
||||
|
@ -0,0 +1,299 @@
|
||||
import re
|
||||
from enum import IntEnum
|
||||
from typing import List
|
||||
|
||||
from . import CPU, Registers
|
||||
|
||||
REGISTERS = list(Registers.all_registers())
|
||||
|
||||
INSTRUCTIONS = list(CPU.all_instructions())
|
||||
|
||||
PSEUDO_OPS = [
|
||||
'.asciiz',
|
||||
'.double',
|
||||
'.extern',
|
||||
'.global',
|
||||
'.align',
|
||||
'.float',
|
||||
'.kdata',
|
||||
'.ktext',
|
||||
'.space',
|
||||
'.ascii',
|
||||
'.byte',
|
||||
'.data',
|
||||
'.half',
|
||||
'.text',
|
||||
'.word'
|
||||
'.set',
|
||||
]
|
||||
|
||||
COMMENT_START = ["#", ";"]
|
||||
|
||||
REG_VALID_SYMBOL_LABEL = re.compile(r'^([A-z_.][A-z_0-9.]*[A-z_0-9]|[A-z_]):')
|
||||
|
||||
REG_WHITESPACE_UNTIL_NEWLINE = re.compile(r'^(\s*)\n')
|
||||
|
||||
REG_WHITESPACE = re.compile(r'^\s*')
|
||||
|
||||
REG_NONWHITESPACE = re.compile(r'^[^\s]*')
|
||||
|
||||
REG_UNTIL_NEWLINE = re.compile(r'^[^\n]*')
|
||||
|
||||
REG_WHITESPACE_NO_LINEBREAK = re.compile(r'^[ \t]*')
|
||||
|
||||
REG_VALID_ARGUMENT = re.compile(
|
||||
r'^([+-]?(0x[0-9A-f]+|[0-9]+)|[A-z_.][A-z0-9_.]*[A-z_0-9]|[A-z_])(\(([A-z_.][A-z_0-9.]*[A-z_0-9]|[A-z_])\))?'
|
||||
)
|
||||
|
||||
REG_ARG_SPLIT = re.compile(r'^,[ \t]*')
|
||||
|
||||
|
||||
def split_accepting_quotes(string, at=REG_ARG_SPLIT, quotes=('"', "'")):
|
||||
pos = 0
|
||||
last_piece = 0
|
||||
pieces = []
|
||||
in_quotes = False
|
||||
if string is None:
|
||||
return pieces
|
||||
while pos < len(string):
|
||||
match = at.match(string[pos:])
|
||||
if match is not None:
|
||||
if not in_quotes:
|
||||
pieces.append(string[last_piece:pos])
|
||||
pos += len(match.group(0))
|
||||
last_piece = pos
|
||||
else:
|
||||
pos += len(match.group(0))
|
||||
elif string[pos] in quotes:
|
||||
in_quotes = not in_quotes
|
||||
pos += 1
|
||||
elif string[pos] in COMMENT_START and not in_quotes: # entering comment
|
||||
break
|
||||
else:
|
||||
pos += 1
|
||||
if in_quotes:
|
||||
print("[Tokenizer.split] unbalanced quotes in \"{}\"!".format(string))
|
||||
pieces.append(string[last_piece:pos])
|
||||
return pieces
|
||||
|
||||
|
||||
class RiscVInput:
|
||||
def __init__(self, content: str):
|
||||
self.content = content
|
||||
self.pos = 0
|
||||
self.len = len(content)
|
||||
|
||||
def peek(self, offset: int = 0, size: int = 1, regex: re.Pattern = None, text: str = None, regex_group: int = 0):
|
||||
at = self.pos + offset
|
||||
|
||||
if regex:
|
||||
if not isinstance(regex, re.Pattern):
|
||||
print("uncompiled regex passed to peek!")
|
||||
reges = re.compile(regex)
|
||||
match = regex.match(self.content[at:])
|
||||
if match is None:
|
||||
return None
|
||||
|
||||
if regex_group != 0 and not match.group(0).startswith(match.group(regex_group)):
|
||||
print("Cannot peek regex group that does not start at match start!")
|
||||
return None
|
||||
return match.group(regex_group)
|
||||
if text:
|
||||
if self.content[at:].startswith(text):
|
||||
return self.content[at:at + len(text)]
|
||||
return False
|
||||
return self.content[at:at + size]
|
||||
|
||||
def peek_one_of(self, options: List[str]):
|
||||
for text in options:
|
||||
if self.peek(text=text):
|
||||
return text
|
||||
return False
|
||||
|
||||
def consume(self, size: int = 1, regex: re.Pattern = None, text: str = None, regex_group: int = 0):
|
||||
at = self.pos
|
||||
|
||||
if regex:
|
||||
if not isinstance(regex, re.Pattern):
|
||||
print("uncompiled regex passed to peek!")
|
||||
regex = re.compile(regex)
|
||||
match = regex.match(self.content[at:])
|
||||
if match is None:
|
||||
print("Regex matched none at {}!".format(self.context()))
|
||||
return None
|
||||
|
||||
if regex_group != 0 and not match.group(0).startswith(match.group(regex_group)):
|
||||
print("Cannot consume regex group that does not start at match start!")
|
||||
return None
|
||||
self.pos += len(match.group(regex_group))
|
||||
return match.group(regex_group)
|
||||
|
||||
if text:
|
||||
if self.content[at:].startswith(text):
|
||||
self.pos += len(text)
|
||||
return text
|
||||
return None
|
||||
|
||||
self.pos += size
|
||||
return self.content[at:at + size]
|
||||
|
||||
def consume_one_of(self, options: List[str]):
|
||||
for text in options:
|
||||
if self.peek(text=text):
|
||||
return self.consume(text=text)
|
||||
return False
|
||||
|
||||
def seek_newline(self):
|
||||
return self.consume(regex=REG_WHITESPACE_UNTIL_NEWLINE, regex_group=1)
|
||||
|
||||
def consume_whitespace(self, linebreak=True):
|
||||
if linebreak:
|
||||
return self.consume(regex=REG_WHITESPACE)
|
||||
return self.consume(regex=REG_WHITESPACE_NO_LINEBREAK)
|
||||
|
||||
def has_next(self):
|
||||
return self.pos < self.len
|
||||
|
||||
def context(self, size: int = 5):
|
||||
"""
|
||||
returns a context string:
|
||||
<local input before pos>|<local input after pos>
|
||||
"""
|
||||
start = max(self.pos - size, 0)
|
||||
end = min(self.pos + size, self.len - 1)
|
||||
|
||||
return self.content[start:self.pos] + '|' + self.content[self.pos:end]
|
||||
|
||||
|
||||
class TokenType(IntEnum):
|
||||
SYMBOL = 0
|
||||
INSTRUCTION = 1
|
||||
PSEUDO_OP = 2
|
||||
|
||||
def __repr__(self):
|
||||
return self.name
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class RiscVToken:
|
||||
type: TokenType
|
||||
|
||||
def __init__(self, t_type: TokenType):
|
||||
self.type = t_type
|
||||
|
||||
def __repr__(self):
|
||||
return "{}[{}]({})".format(self.__class__.__name__, self.type, self.text())
|
||||
|
||||
def text(self):
|
||||
"""
|
||||
create text representation of instruction
|
||||
"""
|
||||
return "unknown"
|
||||
|
||||
|
||||
class RiscVInstructionToken(RiscVToken):
|
||||
def __init__(self, name, args):
|
||||
super().__init__(TokenType.INSTRUCTION)
|
||||
self.instruction = name
|
||||
self.args = args
|
||||
|
||||
def text(self):
|
||||
if len(self.args) == 0:
|
||||
return self.instruction
|
||||
if len(self.args) == 1:
|
||||
return "{} {}".format(self.instruction, self.args[0])
|
||||
if len(self.args) == 2:
|
||||
return "{} {}, {}".format(self.instruction, *self.args)
|
||||
return "{} {}, {}, {}".format(self.instruction, *self.args)
|
||||
|
||||
|
||||
class RiscVSymbolToken(RiscVToken):
|
||||
def __init__(self, name):
|
||||
super().__init__(TokenType.SYMBOL)
|
||||
self.name = name
|
||||
|
||||
def text(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class RiscVPseudoOpToken(RiscVToken):
|
||||
def __init__(self, name, args):
|
||||
super().__init__(TokenType.PSEUDO_OP)
|
||||
self.name = name
|
||||
self.args = args
|
||||
|
||||
def text(self):
|
||||
return "{} {}".format(self.name, self.args)
|
||||
|
||||
|
||||
class RiscVTokenizer:
|
||||
def __init__(self, input: RiscVInput):
|
||||
self.input = input
|
||||
self.tokens = []
|
||||
|
||||
def tokenize(self):
|
||||
while self.input.has_next():
|
||||
# remove leading whitespaces, place cursor at text start
|
||||
self.input.consume_whitespace()
|
||||
|
||||
# check if we have a pseudo op
|
||||
if self.input.peek_one_of(PSEUDO_OPS):
|
||||
self.parse_pseudo_op()
|
||||
|
||||
# check if we have a symbol (like main:)
|
||||
elif self.input.peek(regex=REG_VALID_SYMBOL_LABEL):
|
||||
self.parse_symbol()
|
||||
|
||||
# comment
|
||||
elif self.input.peek() in COMMENT_START:
|
||||
self.parse_comment()
|
||||
|
||||
# must be instruction
|
||||
elif self.input.peek_one_of(INSTRUCTIONS):
|
||||
self.parse_instruction()
|
||||
else:
|
||||
token = self.input.peek(size=5)
|
||||
print("Unknown token around {} at: {}".format(repr(token), repr(self.input.context())))
|
||||
self.input.consume_whitespace()
|
||||
print("After whitespace at: {}".format(repr(self.input.context())))
|
||||
self.input.consume_whitespace()
|
||||
|
||||
def parse_pseudo_op(self):
|
||||
name = self.input.consume_one_of(PSEUDO_OPS)
|
||||
self.input.consume_whitespace(linebreak=False)
|
||||
|
||||
arg_str = self.input.consume(regex=REG_UNTIL_NEWLINE)
|
||||
if not arg_str:
|
||||
args = []
|
||||
else:
|
||||
args = split_accepting_quotes(arg_str)
|
||||
|
||||
self.tokens.append(RiscVPseudoOpToken(name[1:], args))
|
||||
|
||||
def parse_symbol(self):
|
||||
name = self.input.consume(regex=REG_VALID_SYMBOL_LABEL)
|
||||
self.tokens.append(RiscVSymbolToken(name[:-1]))
|
||||
if not self.input.consume_whitespace():
|
||||
print("[Tokenizer] symbol declaration should always be followed by whitespace (at {})!".format(
|
||||
self.input.context()))
|
||||
|
||||
def parse_instruction(self):
|
||||
ins = self.input.consume_one_of(INSTRUCTIONS)
|
||||
args = []
|
||||
self.input.consume_whitespace(linebreak=False)
|
||||
while self.input.peek(regex=REG_VALID_ARGUMENT) and len(args) < 3:
|
||||
arg = self.input.consume(regex=REG_VALID_ARGUMENT)
|
||||
args.append(arg)
|
||||
if self.input.peek(text=','):
|
||||
self.input.consume(text=',')
|
||||
self.input.consume_whitespace(linebreak=False)
|
||||
else:
|
||||
break
|
||||
self.tokens.append(RiscVInstructionToken(ins, args))
|
||||
|
||||
def parse_comment(self):
|
||||
# just consume the rest
|
||||
self.input.consume(regex=REG_UNTIL_NEWLINE)
|
||||
|
@ -0,0 +1,30 @@
|
||||
from riscemu import *
|
||||
|
||||
if __name__ == '__main__':
|
||||
example_progr = """
|
||||
.data 0x200
|
||||
fibs: .space 56
|
||||
|
||||
.text
|
||||
main:
|
||||
add s1, zero, 0 # storage index
|
||||
add s2, zero, 56 # last storage index
|
||||
add t0, zero, 1 # t0 = F_{i}
|
||||
add t1, zero, 1 # t1 = F_{i+1}
|
||||
loop:
|
||||
sw t0, fibs(s1) # save
|
||||
add t2, t1, t0 # t2 = F_{i+2}
|
||||
add t0, t1, 0 # t0 = t1
|
||||
add t1, t2, 0 # t1 = t2
|
||||
add s1, s1, 4 # increment storage pointer
|
||||
blt s1, s2, loop # loop as long as we did not reach array length
|
||||
# exit gracefully
|
||||
add a0, zero, 0
|
||||
add a7, zero, 93
|
||||
scall # exit with code 0
|
||||
"""
|
||||
tk = RiscVTokenizer(RiscVInput(example_progr))
|
||||
tk.tokenize()
|
||||
|
||||
for token in tk.tokens:
|
||||
print(token)
|
Loading…
Reference in New Issue