JISA: An easy to use custom instruction set assembler
*UPDATED*
Repo link
*UPDATED*
Prepare for incoming update on the tutorial.
Disclaimer: This post is subject to changes as is the script itself. I will try my best to keep the documentation as up to date as possible but it may not always be right on time.
Overview
JISA is a python script that takes a .yaml file (default name IS.yaml) that describes your instruction set and will then be able to assemble any code written in your assembly language into either a hex or a binary format. Both the source and the output file must be in a plain text format.
Requirements:
Constructing your .yaml file
The first thing you must do is have an instruction set. I'll be creating various instructions to demonstrate the way you lay it out.
Within the .yaml file it is important to label everything with bit widths so that the assembler knows how much to fill. It is important to start the file and separate sections with '---' so the script knows where to start.
The .yaml file is constructed of three sections:
In this section you define the bit layout of various different types of instruction.
For example: Say I wanted to layout the format for a simple two operand logic type instruction. I am going to have a 4 bit op code for this IS so I must state that the op code will take up 4 bits explicitly:
As you can see, I have also defined the line length in the declaration. RR(Register, Register) will be the format for any instruction that takes two operands and has an op code. However we haven't finished defining the entire format. We have to ensure that the sum of the bit widths of the constituent parts is equal to the total line length.
We have now completed our first basic format. As you can see I have used 3 different types of predefined argument types (op, r, 0) there are 6 total predefined argument types:
Now the var argument probably needs a bit of explaining. It's probably easier to explain with an example. So let's change how the RR instruction works. Say I wanted to select between using either a second register or an immediate. I would do something like this:
I've used the final bit in the line as a signifier bit that decides whether or not our ISA uses an immediate or a register argument.
When you use the var argument type, you have to define the different types of arguments in the final element of the list. It must always be at the end of the format so that the assembler knows where to look for the forks. There are two different types of argument that have valid forks: r and i.
If one of these types of argument is found, the line format follows the corresponding fork.
Custom Arguments
'But Jallen!' I hear you cry 'What if I want to use an argument other than the ones you've already said?!' Well we can do that!
You need to use a custom argument. There are two stages to making a custom argument:
In this I've both defined the custom argument and declared it. To declare it you must use the '~' symbol in the format followed by the tag and the bit width. In the definition you define the values the argument can take, you then define the binary value each one represents. It is important to use quotation marks so that the assembler treats it as a string rather than a number. It also allows for special character usage in the values your custom can take.
What this .yaml will allow for is a conditional jump instruction to an address specified by the 8-bit immediate and the condition can be any from the list.
Multi-line:
If you want to have a multi-line instruction, it's pretty simple to add it that in:
All you have to do is separate the line lengths for your two or more lines with a comma.
Defining Instructions
It's very simple to define an instruction. All you have to do is declare a keyword with a list of it's format tag and it's opcode. For instance:
Each instruction takes 2 values, the format and the opcode. Again it is important to use quotes for the opcode so the object is kept as a string.
This .yaml file will fully allow us to write a line like this:
N.B. you must write the arguments in the order they are delcared in the format. I may add the ability to mix them up in the future but for now, that's how it is.
To use the script enter:This will show you what arguments you need to use and some other options too
If you have any suggestions or find a bug please leave a comment or find me ingame.
*UPDATED*
Repo link
*UPDATED*
Prepare for incoming update on the tutorial.
Disclaimer: This post is subject to changes as is the script itself. I will try my best to keep the documentation as up to date as possible but it may not always be right on time.
Overview
JISA is a python script that takes a .yaml file (default name IS.yaml) that describes your instruction set and will then be able to assemble any code written in your assembly language into either a hex or a binary format. Both the source and the output file must be in a plain text format.
Requirements:
- Python 3.* Installed
- PyYaml package (see requirements.txt in the repo for link)
Constructing your .yaml file
The first thing you must do is have an instruction set. I'll be creating various instructions to demonstrate the way you lay it out.
Within the .yaml file it is important to label everything with bit widths so that the assembler knows how much to fill. It is important to start the file and separate sections with '---' so the script knows where to start.
The .yaml file is constructed of three sections:
- Formats
- Instructions
- Custom arguments
In this section you define the bit layout of various different types of instruction.
For example: Say I wanted to layout the format for a simple two operand logic type instruction. I am going to have a 4 bit op code for this IS so I must state that the op code will take up 4 bits explicitly:
Code:
---
RR;16:
- op;4
As you can see, I have also defined the line length in the declaration. RR(Register, Register) will be the format for any instruction that takes two operands and has an op code. However we haven't finished defining the entire format. We have to ensure that the sum of the bit widths of the constituent parts is equal to the total line length.
Code:
---
RR;16:
- op;4
- r;3
- r;3
- 0;6
We have now completed our first basic format. As you can see I have used 3 different types of predefined argument types (op, r, 0) there are 6 total predefined argument types:
- op: op code
- r: register argument
- i: immediate (can be given in binary or hex(prefix with '0x' for hex))
- var: forks the format based on what type of argument is provided
- 0: ...it's a 0
- 1: you guessed it!
Now the var argument probably needs a bit of explaining. It's probably easier to explain with an example. So let's change how the RR instruction works. Say I wanted to select between using either a second register or an immediate. I would do something like this:
Code:
---
RRI;16:
- op;4
- r;3
- var;9
- var:
r:
- r;3
- 0;6
i:
- i;8
- 1;1
I've used the final bit in the line as a signifier bit that decides whether or not our ISA uses an immediate or a register argument.
When you use the var argument type, you have to define the different types of arguments in the final element of the list. It must always be at the end of the format so that the assembler knows where to look for the forks. There are two different types of argument that have valid forks: r and i.
If one of these types of argument is found, the line format follows the corresponding fork.
Custom Arguments
'But Jallen!' I hear you cry 'What if I want to use an argument other than the ones you've already said?!' Well we can do that!
You need to use a custom argument. There are two stages to making a custom argument:
- Define
- Declare
Code:
---
JMP;16:
- op;4
- ~cond;4
- i;8
---
(see later for defining the instruction)
---
cond:
'if0': '0001'
'!if0': '0010'
'Cout': '0011'
'!Cout': '0100'
'gt': '0101'
'lt': '0110'
'eq': '0111'
'!eq': '1000'
...
In this I've both defined the custom argument and declared it. To declare it you must use the '~' symbol in the format followed by the tag and the bit width. In the definition you define the values the argument can take, you then define the binary value each one represents. It is important to use quotation marks so that the assembler treats it as a string rather than a number. It also allows for special character usage in the values your custom can take.
What this .yaml will allow for is a conditional jump instruction to an address specified by the 8-bit immediate and the condition can be any from the list.
Multi-line:
If you want to have a multi-line instruction, it's pretty simple to add it that in:
Code:
---
JMP;16,16:
- op;4
- ~cond;4
- 0;8
- i;16
---
===
---
cond:
'if0': '0001'
'!if0': '0010'
'Cout': '0011'
'!Cout': '0100'
'gt': '0101'
'lt': '0110'
'eq': '0111'
'!eq': '1000'
Defining Instructions
It's very simple to define an instruction. All you have to do is declare a keyword with a list of it's format tag and it's opcode. For instance:
Code:
[code]---
JMP;16,16:
- op;4
- ~cond;4
- 0;8
- i;16
---
jump:
- JMP
- '1000'
---
cond:
'if0': '0001'
'!if0': '0010'
'Cout': '0011'
'!Cout': '0100'
'gt': '0101'
'lt': '0110'
'eq': '0111'
'!eq': '1000'
This .yaml file will fully allow us to write a line like this:
Code:
jump !Cout 0xabc1
N.B. you must write the arguments in the order they are delcared in the format. I may add the ability to mix them up in the future but for now, that's how it is.
To use the script enter:
Code:
python <script_name>.py -h
If you have any suggestions or find a bug please leave a comment or find me ingame.