Skip to content

A simple interpreter for emulating 8085 microprocessor instructions with CLI , REPL and Emacs org-mode interface.

Notifications You must be signed in to change notification settings

hemanta212/8085-simulator

Repository files navigation

8085-Interpreter

A simple exploratory interpreter with REPL experience. (Beginning learning purpose only)

  • For windows users, you can download and run the .exe file from releases page.
  • Clone and run this repository,
git clone https://github.com/hemanta212/8085-simulator
python -m pip install loguru rich pyreadline
cd 8085-simulator
python main.py

Type help and run the commands listed.

For detailed examples on available commands, see file usage_examples.org.

Table of contents

Features

  • [x] - Implement Registers and Memory
  • [x] - Implement Basic commands (ADD, ADI, SUB, SUI, MOV, MVI, STA, LDA)
  • [x] - Implement Xtended Register Pairs (LXI, M, LDAX)
  • [x] - Write a ob-8085 for emacs org-mode (babel)
  • [x] - Implement Flags (CY, Z, S)
  • [x] - Implement labels and jumps (Loops) [Commands: JC, JNC, JZ, JNZ)
  • [x] - Implement increment/decrements [INR, DCR]
  • [x] - Implement Xtended increment/decrements [INX, DCX]
  • [x] - Implement basic commands (CMP/CPI, OUT)
  • [x] - Implement comments, empty lines, HLT(no-effect)
  • [x] - Release a pyinstaller executable
  • [ ] - Implement commands (ANI, ORI, RRC, STAX)
  • [ ] - … ( a lot of commands)
  • [ ] - Implement proper 8-bit 16-bit type system and validation
  • [ ] - Implement a stacktrace in case of errors
  • [x] - Implement integration test and actions CI
  • [ ] - A pypi package
  • [-] - Write a major mode for emacs.

REPL Mode

python main.py
Welcome to the 8085 emulator.
Type 'help' for a list of commands.
>>>
>>> help
MOV - Move data from one register to another
MVI - Move to immediate
INR - Increment Register
DCR - Decrement Register
LXI - Load register pair immediate
LDA - Load accumulator
STA - Store accumulator
HLT - Halt
ADD - Add
SUB - Subtract
ADI - Add Immediate
SUI - Subtract Immediate
CMP - Compare
CPI - Compare Immediate
ANI - And Immediate with Accumulator
ORI - OR Immediate with Accumulator
RRC - Rotate Right Accumulator
LDAX - Load accumulator from register pair
STAX - Store accumulator to register pair
INX - Incremented xtended register pairs
DCX - Decrement xtended register pairs
JZ - Jump If Zero
JNZ - Jump If Not Zero
JC - Jump If Carry
JNC - Jump If Not Carry
OUT - Out

Command line arguments

Beside repl, the interpreter can be entirely run from command line.

python main.py --help
:8085 Interpreter:

help | --help | -h: Display this message
-i                : Run in indirect mode, dont display welcome msg and >>>
prompt
-v <d/i/w/e>      : Verbosity option use (d,i,w,e) for (DEBUG, INFO, WARNING,
ERROR) resp.
-db <FILENAME>    : Run in file db mode save and restore after each cmd from
file
-f <FILENAME>     : Read command/commands from file
-c "cmd1;cmd2"    : Run cmd directly, separate with ";" for more than one
commands

NOTE: In case of using multiple options, they need to be specified in order
listed above.

The interpreter provides:

  • An option to run in raw plain mode without any welcome message or >>> prompt through -i option.
  • An option to pass a file (containing commands line by line) to the interpreter to evaluate through -f option.
  • An option to save state after each command to a json file through -db option.
  • An option to run commands in place through -c option.
  • An option to customize the verbosity of logging messages through -v option.

NOTE: In case of using multiple options, they need to be specified in order,

  • -i , -v, -db, -f, -c

Providing options otherwise will result in an error.

Example Repl Workflow

NOTE Fore more extensive examples for each commands, see file usage_examples.org.

>>> inspect
Registers:
	A: 0x00
	B: 0x00
	C: 0x00
	D: 0x00
	E: 0x00
	H: 0x00
	L: 0x00
	M: 0x00

Memory:
	0x1000: 0x2b
	0x1001: 0x34
	0x0000: 0x00

Flags:
	carry: 0
	auxillary_carry: 0
	zero: 0
	sign: 0
>>> MVI B 05H
B -> 05H
>>> MVI C 05H
C -> 05H
>>> ADD B
A -> 00H + 05H -> 05H
>>> ADD C
A -> 05H + 05H -> 0AH
>>> STA 3322H
3322H -> 0AH
>>> inspect
Registers:
	A: 0x0a
	B: 0x05
	C: 0x05
	D: 0x00
	E: 0x00
	H: 0x00
	L: 0x00
	M: 0x00

Memory:
	0x1000: 0x2b
	0x1001: 0x34
	0x0000: 0x00
	0x3322: 0x0a

Flags:
	carry: 0
	auxillary_carry: 0
	zero: 0
	sign: 0

Example Command line Workflow

The file option (-f)

echo "MVI B 05H" > test.txt
echo "MVI A 00H" >> test.txt
python main.py -f test.txt
B -> 05H
A -> 00H

The command option (-c)

python main.py -c "MVI B 05H"
B -> 05H
python main.py -c "MVI A 00H; MVI B 05H; ADD B"
A -> 00H
B -> 05H
A -> 00H + 05H -> 05H

The json file db option (-db)

Specifying the file db option saves the state of interpreter to a json file and restores from it every time a 8085 command is executed.

This is useful when trying to run multiple -c commans as a session.

python main.py -db /tmp/pyassm-readme-02 -c "MVI B 05H"
B -> 05H
python main.py -db /tmp/pyassm-readme-02 -c "MVI A 00H; ADD B"
A -> 00H
A -> 00H + 05H -> 05H
python main.py -db /tmp/pyassm-readme-02 -c "STA 5555H; inspect"
5555H -> 05H
Registers:
	A: 0x05
	B: 0x05
	C: 0x00
	D: 0x00
	E: 0x00
	H: 0x00
	L: 0x00
	M: 0x00

Memory:
	0x1000: 0x2b
	0x1001: 0x34
	0x0000: 0x00
	0x5555: 0x05

Flags:
	carry: 0
	auxillary_carry: 0
	zero: 0
	sign: 0

The plain/indirect mode option (-i)

This is very useful for piping interactions to and from other applications. It is also recommended to run in -db file mode for continuous session-like interaction.

echo "MVI B 05H" | python main.py -i
B -> 05H
echo "MVI B 05H\nADD B" | python main.py -i
B -> 05H
A -> 00H + 05H -> 05H

The verbosity logging option (-v)

You can customize the verbosity of logging messages by providing,

  • d : For DEBUG level
  • e : For ERROR level
  • w : For WARNING level
  • i : For INFO level
echo "MVI B 05H" | python main.py -i -v d

Using From Terminal, Vim and Emacs

The command line options provided by interpreter allows it to be used through editors like Vim and Emacs. Either you can:

  • Use the -f option and write and execute using a temp buffer/file.
  • Use combination of -c and -db option to emulate a repl session.
  • Use combnation of -i and -db option to emulate a repl session.

Example Emacs Org babel config

With some configuration, the interpreter can be made to work with Emacs’ Org Mode using the org-babel-eval function. This uses -i command option to write to the interpreter.

Put this in your init.el file,

(defcustom path-to-8085 "~/dev/8085-interpreter/"
  "Path to folder where 8085-interpreter was cloned")

(defcustom org-babel-8085-command
  (concat
   "python"
   (concat path-to-8085 "/main.py"))
  "Name of the command for executing 8085 interpreter.")

(defun org-babel-execute:8085 (body params)
  (let ((args (cdr (assoc :args params))))
    (org-babel-eval
     (concat
      org-babel-8085-command
      (if args  (concat " -i " args) " -i " ))
     body)))

;; Placeholder major mode, look below for more featured major mode
(define-derived-mode 8085-mode prog-mode "8085"
   "Major mode for 8085."
   (setq-local comment-start ";")
   (setq-local comment-start-skip ";+[\t ]*"))
  • The path-to-8085 should be folder where you cloned this project.
  • The org-babel-8085-command should be the command to run the interpreter (eg python main.py),
    • You could use (concat path-to-8085 "/.venv/bin/python") in place of ”python” if you use in-project virtual environments.

Emacs 8085 major mode

  (require 'rx)
  (defvar 8085-mode-map
    (let ((map (make-sparse-keymap)))
      map))

(defconst 8085--font-lock-defaults
  (let (
        (instructions '("MVI" "MOV" "ADD" "SUB" "ADI"
                        "SUI" "JNZ" "JNC" "JZ" "JC" "LXI"
                        "LXAD" "INR" "DCR" "INX" "DCX" "OUT"
                        "HLT" "CPI" "CMP" "STA" "LDA")))
    `(((,(rx-to-string `(: (or ,@instructions))) 0 font-lock-keyword-face)
    ("\\([[:word:]]+\\):" 1 font-lock-function-name-face)))))

  (defvar 8085-mode-syntax-table
  (let ((st (make-syntax-table)))
    ;; - and _ are word constituents
    (modify-syntax-entry ?_ "w" st)
    (modify-syntax-entry ?- "w" st)

    ;; add comments. lua-mode does something similar, so it shouldn't
    ;; bee *too* wrong.
    (modify-syntax-entry ?\; "<" st)
    (modify-syntax-entry ?\n ">" st)
    st))

  (define-derived-mode 8085-mode prog-mode "8085"
    "Major mode for 8085."
    (setq font-lock-defaults 8085--font-lock-defaults)
    (setq-local comment-start ";")
    (setq-local comment-start-skip ";+[\t ]*")
    (setq-local case-fold-search nil))

Save and restart your emacs (or execute each block with C-x C-e). Then you can use org mode to write block like:

  • Use C-c C-c to execute a given block.
#+begin_src 8085 :args -v d -db /tmp/8085-session1
MVI B 80H
#+end_src
  • For session-like use,
#+begin_src 8085 :args -v d -db /tmp/8085-session1
MVI B 80H
#+end_src
  • For verbose logging,
#+begin_src 8085 :args -v d -db /tmp/8085-session1
MVI B 80H
#+end_src