Skip to content

Commit

Permalink
chore: Add CI to Dashmips (#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
nbbeeken authored Jul 12, 2020
1 parent 9fc564d commit 0257ad4
Show file tree
Hide file tree
Showing 27 changed files with 387 additions and 300 deletions.
33 changes: 33 additions & 0 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Dashmips

on:
push:
branches:
- master
- "feature/**"
pull_request:
branches:
- master

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Set up Python 3.8
uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Install Poetry
run: |
curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python
source $HOME/.poetry/env
- name: Install Dependencies
run: |
source $HOME/.poetry/env
poetry install
- name: Test with pytest
run: |
source $HOME/.poetry/env
poetry run pytest
62 changes: 62 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
on:
push:
# Sequence of patterns matched against refs/tags
tags:
- "*" # Push events to matching v*, i.e. v1.0, v20.15.10

name: Upload Release Asset

jobs:
build:
name: Upload Release Asset
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.8
uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Install Poetry
run: |
curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python
source $HOME/.poetry/env
# makes ./dist/dashmips-{version}-py3-none-any.whl
# file name is: {distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}.whl
- name: Build project
run: |
source $HOME/.poetry/env
poetry build -f wheel
- name: Get the version
id: get_version
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}
shell: bash

- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
draft: false
prerelease: false

- name: Upload Release Asset
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
asset_path: ./dist/dashmips-${{ steps.get_version.outputs.VERSION }}-py3-none-any.whl
asset_name: dashmips-${{ steps.get_version.outputs.VERSION }}-py3-none-any.whl
asset_content_type: application/x-wheel+zip

- name: Publish a Python distribution to PyPI
uses: pypa/[email protected]
with:
user: __token__
password: ${{ secrets.pypi_password }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ dist/
dashmips_env/
testout/
.pytest_cache/
.venv
9 changes: 3 additions & 6 deletions dashmips/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
_DASH_HOME = _os.path.dirname(_os.path.abspath(__file__))

# Import all instructions
_instr_filter = (lambda fn: fn.endswith("_instructions.py"))
_instr_filter = lambda fn: fn.endswith("_instructions.py")
_instr_files = _os.listdir(_os.path.join(_DASH_HOME, "instructions"))
_instr_files = filter(_instr_filter, _instr_files) # type: ignore
_instr_modules = [f"dashmips.instructions.{mn[:-3]}" for mn in _instr_files]
Expand All @@ -15,15 +15,12 @@
_import_module(_im)

# Import all syscalls
_syscall_filter = (lambda fn: fn.endswith("_syscalls.py"))
_syscall_filter = lambda fn: fn.endswith("_syscalls.py")
_syscall_files = _os.listdir(_os.path.join(_DASH_HOME, "syscalls"))
_syscall_files = filter(_syscall_filter, _syscall_files) # type: ignore
_syscall_modules = [f"dashmips.syscalls.{mn[:-3]}" for mn in _syscall_files]

for _sm in _syscall_modules:
_import_module(_sm)

__all__ = [
"syscalls", "instructions", "plugins", "directives", "hardware", "mips",
"models", "preprocessor", "run", "debugger", "debuggerserver"
]
__all__ = ["syscalls", "instructions", "plugins", "directives", "hardware", "mips", "models", "preprocessor", "run", "debugger", "debuggerserver"]
7 changes: 4 additions & 3 deletions dashmips/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
from threading import Thread
from typing import Any, List, NoReturn

from .utils import MipsException
from .extension import generate_snippets, instruction_name_regex
from .plugins.vt100 import VT100
from .preprocessor import preprocess
from .utils import MipsException


def main_compile(args: argparse.Namespace) -> int:
Expand All @@ -28,7 +28,7 @@ def main_run(args: argparse.Namespace) -> int:
program = preprocess(args.FILE, args=args.mips_args)
plugins: List[Any] = []
if args.vt100:
vt = VT100() # type: ignore
vt = VT100()
# program.memory.on_change(vt.push)
t = Thread(target=run, args=(program,))
t.start()
Expand All @@ -42,10 +42,11 @@ def main_run(args: argparse.Namespace) -> int:
def main_debug(args: argparse.Namespace) -> int:
"""Start debug server for mips."""
from .debuggerserver import debug_mips, connectPreprocessFailure

try:
program = preprocess(args.FILE, args=args.mips_args)
except MipsException as err:
connectPreprocessFailure(host = args.host, port = args.port)
connectPreprocessFailure(host=args.host, port=args.port)
raise MipsException(err.message)
debug_mips(program, args.host, args.port, should_log=args.log)
return 0
Expand Down
1 change: 1 addition & 0 deletions dashmips/debugger.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ def debug_verify_breakpoints(program: MipsProgram, params) -> Tuple[List[int], L
local_breakpoints = []
remote_breakpoints = []
for breakpoint in breakpoints:

def checkfile(f: str) -> bool:
return os.path.samefile(f, breakpoint["path"])

Expand Down
9 changes: 4 additions & 5 deletions dashmips/debuggerserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ def debug_mips(program: MipsProgram, host="localhost", port=2390, should_log=Fal
:param should_log: (Default value = False)
"""
log.basicConfig(
format="%(asctime)-15s %(levelname)-7s %(message)s",
level=log.INFO if should_log else log.CRITICAL,
format="%(asctime)-15s %(levelname)-7s %(message)s", level=log.INFO if should_log else log.CRITICAL,
)
logger = log.getLogger("sockets.server")
logger.addHandler(log.StreamHandler())
Expand All @@ -71,7 +70,7 @@ def handle(self):
header = b""
while True:
header += self.request.recv(1)
if header and chr(header[-1]) == '}':
if header and chr(header[-1]) == "}":
break
if len(header) >= 1000:
log.error("Communication error between client and server")
Expand All @@ -88,7 +87,7 @@ def handle(self):
log.info("Program exited normally")
break

self.request.sendall(bytes(json.dumps({"size": len(response)}), 'ascii') + bytes(response, 'ascii'))
self.request.sendall(bytes(json.dumps({"size": len(response)}), "ascii") + bytes(response, "ascii"))

# Allows server to reuse address to prevent crash
socketserver.TCPServer.allow_reuse_address = True
Expand All @@ -101,8 +100,8 @@ def handle(self):

def connectPreprocessFailure(host: str = "localhost", port: int = 2390):
"""Connect to extension and fail due to preprocessing failure."""
class TCPHandler(socketserver.BaseRequestHandler):

class TCPHandler(socketserver.BaseRequestHandler):
def handle(self):
sleep(0.1)
return
Expand Down
41 changes: 16 additions & 25 deletions dashmips/hardware.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
"""Mips Hardware."""
from typing import Dict, Union, Tuple, Any, NoReturn, TypedDict, Literal, cast

import sys
from .utils import as_twos_comp, intify, MipsException
from typing import Any, Dict, NoReturn, Tuple, Union, cast

from typing_extensions import Literal, TypedDict

from .utils import MipsException, as_twos_comp, intify

register_names = (
# fmt: off
Expand Down Expand Up @@ -47,9 +49,9 @@ def __setitem__(self, key: str, value: int):
if key in Registers.SPECIAL:
return super().__setitem__(key, value)
if key not in Registers.resolve:
raise MipsException(f'Unknown register Reg[{key}]={hex(value)}')
raise MipsException(f"Unknown register Reg[{key}]={hex(value)}")
if value > 0xFFFF_FFFF:
print(f'Warning: Overflowed 32-bit Reg[{key}]={hex(value)}', file=sys.stderr)
print(f"Warning: Overflowed 32-bit Reg[{key}]={hex(value)}", file=sys.stderr)
super().__setitem__(Registers.resolve[key], value & 0xFFFF_FFFF)

def __getitem__(self, key: str) -> int:
Expand All @@ -59,7 +61,7 @@ def __getitem__(self, key: str) -> int:
return as_twos_comp(super().__getitem__(Registers.resolve[key]))


SectionNames = Union[Literal['stack'], Literal['heap'], Literal['data']]
SectionNames = Union[Literal["stack"], Literal["heap"], Literal["data"]]


class RAMPART(TypedDict):
Expand All @@ -81,34 +83,23 @@ class RAM(TypedDict):
class Memory:
"""Memory simulated."""

PAGE_SIZE = 2**12
TASK_LIMIT = 0xc0000000
PAGE_SIZE = 2 ** 12
TASK_LIMIT = 0xC0000000
START_DATA = 0x00804900
STACK_STOP = 0x05F5E100
HEAP_START = 0x00600000

def __init__(self):
"""Create Mips Memory."""
self.ram: RAM = {
"stack": {
"m": bytearray(),
"start": Memory.STACK_STOP,
"stops": Memory.STACK_STOP
},
"heap": {
"m": bytearray(),
"start": Memory.HEAP_START,
"stops": Memory.HEAP_START
},
"data": {
"m": bytearray(),
"start": Memory.START_DATA,
"stops": Memory.START_DATA
},
"stack": {"m": bytearray(), "start": Memory.STACK_STOP, "stops": Memory.STACK_STOP},
"heap": {"m": bytearray(), "start": Memory.HEAP_START, "stops": Memory.HEAP_START},
"data": {"m": bytearray(), "start": Memory.START_DATA, "stops": Memory.START_DATA},
}

def _tlb(self, virtual_address: int, sizeof=1) -> Tuple[SectionNames, slice]:
def v2p(pa: int): return slice(pa, pa + sizeof, 1)
def v2p(pa: int):
return slice(pa, pa + sizeof, 1)

for section_name in self.ram:
section_name = cast(SectionNames, section_name)
Expand Down Expand Up @@ -249,5 +240,5 @@ def write_str(self, virtual_address: int, data: bytes):

def alignment_zeros(data_len) -> bytearray:
"""Return array of 0s to align to 4."""
alignment = ((4 - data_len % 4) % 4)
alignment = (4 - data_len % 4) % 4
return bytearray(alignment)
2 changes: 1 addition & 1 deletion dashmips/instructions/Instruction.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def __call__(self, program: MipsProgram, args: Iterable[Any] = tuple()):
try:
self.function(program, *args)
except KeyError as err:
raise MipsException(f'Symbol {err} not found in symbol table')
raise MipsException(f"Symbol {err} not found in symbol table")
program.registers["pc"] += 1

def __repr__(self) -> str:
Expand Down
1 change: 1 addition & 0 deletions dashmips/instructions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

def mips_instruction(pattern: str, parser, label: bool = False):
"""Make an Instruction object from decorated function."""

def decorator(function) -> Instruction:
"""Instruction Decorator wrapper."""
instr = Instruction(function, pattern, parser, label=label)
Expand Down
14 changes: 4 additions & 10 deletions dashmips/instructions/pseudo_instructions.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@ def b(program: MipsProgram, label: str):


@mips_instruction(
r"{instr_gap}({register}){args_gap}({register}|{number}){args_gap}({label})",
lambda args: (args[2], args[3], args[4]),
r"{instr_gap}({register}){args_gap}({register}|{number}){args_gap}({label})", lambda args: (args[2], args[3], args[4]),
)
def bgt(program: MipsProgram, rd: str, rs: str, label: str):
"""Branch to label if Reg[rd]>Reg[rs]."""
Expand All @@ -57,10 +56,7 @@ def bgt(program: MipsProgram, rd: str, rs: str, label: str):
program.registers["pc"] = program.labels[label].value - 1


@mips_instruction(
r"{instr_gap}({register}){args_gap}({register}|{number}){args_gap}({label})",
lambda args: (args[2], args[3], args[4])
)
@mips_instruction(r"{instr_gap}({register}){args_gap}({register}|{number}){args_gap}({label})", lambda args: (args[2], args[3], args[4]))
def blt(program: MipsProgram, rd: str, rs: str, label: str):
"""Branch to label if Reg[rd]<Reg[rs]."""
if rs not in program.registers:
Expand All @@ -79,8 +75,7 @@ def neg(program: MipsProgram, rd: str, rs: str):


@mips_instruction(
r"{instr_gap}({register}){args_gap}({register}){args_gap}({label})",
lambda args: (args[2], args[3], args[4]),
r"{instr_gap}({register}){args_gap}({register}){args_gap}({label})", lambda args: (args[2], args[3], args[4]),
)
def bge(program: MipsProgram, rd: str, rs: str, label: str):
"""Branch to label if Reg[rd]>Reg[rs]."""
Expand All @@ -89,8 +84,7 @@ def bge(program: MipsProgram, rd: str, rs: str, label: str):


@mips_instruction(
r"{instr_gap}({register}){args_gap}({register}){args_gap}({label})",
lambda args: (args[2], args[3], args[4]),
r"{instr_gap}({register}){args_gap}({register}){args_gap}({label})", lambda args: (args[2], args[3], args[4]),
)
def ble(program: MipsProgram, rd: str, rs: str, label: str):
"""Branch to label if Reg[rd]<Reg[rs]."""
Expand Down
3 changes: 1 addition & 2 deletions dashmips/instructions/rs_rt_imm_instructions.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ def addi(program: MipsProgram, rs: str, rt: str, num: int):
@mips_instruction(PATTERN, parse)
def addiu(program: MipsProgram, rs: str, rt: str, num: int):
"""Add immediate unsigned Reg[rs] = Reg[rt] + immediate."""
program.registers[rs] = (
abs(program.registers[rt]) + abs(num)) & 0xFFFF_FFFF
program.registers[rs] = (abs(program.registers[rt]) + abs(num)) & 0xFFFF_FFFF


@mips_instruction(PATTERN, parse)
Expand Down
6 changes: 2 additions & 4 deletions dashmips/mips.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@

Directives: Dict[str, Callable[[str, Memory], int]] = {
name.replace("directive_", ""): fn
for name, fn in inspect.getmembers(
import_module(".directives", "dashmips"),
inspect.isfunction
) if name.startswith("directive_")
for name, fn in inspect.getmembers(import_module(".directives", "dashmips"), inspect.isfunction)
if name.startswith("directive_")
}


Expand Down
Loading

0 comments on commit 0257ad4

Please sign in to comment.