francisrstokes / 16bitjs Goto Github PK
View Code? Open in Web Editor NEW๐ป A 16-bit virtual machine, including assembly language with 37 instructions, binary assembler, and a step through debugger
License: MIT License
๐ป A 16-bit virtual machine, including assembly language with 37 instructions, binary assembler, and a step through debugger
License: MIT License
A SWAP can be done through 3 XORs without allocating a temporary register or using stack:
X := X XOR Y
Y := Y XOR X
X := X XOR Y
Since you word is 16-bit wide, you may split it into 2ร8-bit, and keep two bits of immediate (SS bits) to select a special operation.
In MIPS, you have LUI instruction which sets a register with imm16 left shifted by 16 bits. Then you can use ADDIU or ORI to add/or-ize the register with a signed 16-bit immediate value, allowing in two instructions to set a 32-bit immediate value to a register. ARM Cortex architecture also have a similar way.
So in your case, it could be:
And
|LDV
| D, V
| VVVVVVVVVVDD0001
| Load a value into destination register.
becomes
|LDV
| D, V, O
| VVVVVVVVOODD0001
| Load a value into destination register.
with pseudos
|MVI
| D, V
| VVVVVVVV00DD0001
| Set a zero-extended lower byte to destination register.
|ADI
| D, V
| VVVVVVVV01DD0001
| Set a zero-extended lower byte to destination register.
|MUI
| D, V
| VVVVVVVV10DD0001
| Set a byte left shifted by 8 bits to destination register.
|AUI
| D, V
| VVVVVVVV11DD0001
| Add a byte left shifted by 8 bits to destination register.
Mnemonics stand for:
The rationale is:
โ the 8-bit immediate is zero/signed-extended respectively when bit 0 of SS is 0/1.
โ the effective immediate value is set with the extended 8-bit immediate left shifted by 0/8 bits respectively when bit 1 of SS is 0/1,
โ the effective immediate value is set/added respectively to the destination register when bit 0 of SS is 0/1.
As a result,
MVI Dr, 0xLL; AUI Dr, 0xHH
<=> Dr = 0x00LL + 0xHH00 <=> Dr = 0xHHLL and define a pseudo MVA Dr, 0xHHLL
,AUI Dr, 0xHH;ADI Dr,0xLL
<=> Dr = Dr + 0xHH00 + 0x00LL <=> Dr = Dr + 0xHHLL if 0xLL is inferior or equals to 0x7f,AUI Dr, 0xHH+1; ADI Dr, 0xLL
<=> Dr = Dr + (0xHH00+0x0100) + (0xffLL) <=> Dr = Dr + 0xHHLL if 0xLL is greater than 0x7f.ADA Dr, 0xHHLL
for the previous two points and which issues the right pair according to the sign bit of 0xLL.LoaD into memory through Pointer
becomes SToRe
.
Allow for an 8 bit offset for storing within a data structure.
Create a pseudo instruction LDP
for compatibility.
Not a proposal, not an issue, just a way to show a different ISA which tries to use as much as possible all the field bits of an instruction. This ISA is not complete (no comparison instruction, no jump, missing stack alu, and so one).
Regarding stack operations, some ALU operations is missing (reserving/releasing N word in stack).
Hi, I really enjoyed your article and I am now doing a code-along (I read the specs on your blog post and try to implement them in Python). I just finished coding the LDV16 expander and when I looked at your code, I saw that the (temporary) B register was hardcoded, so setting the B register with LDV16 probably won't work as intended. I solved this like this
MOV is renamed as MVR and MOV, DEC, and INC become pseudos using MVR
|MOV
| D, S
| XXXXXXXXSSDD0000
| Move value at source register to destination register|
|MVR
| D, S, V
| VVVVVVVVSSDD0000
| Add sign-extended immediate value to value at source register and move it to destination register|
|MOV
| D, S
| 00000000SSDD0000
| Move value at source register to destination register|
|INC
| D
| 00000001DDDD0000
| Increment value at destination register|
|DEC
| D
| 11111111DDDD0000
| Decrement value at destination register|
Retro-compatibility is kept for assembly code, not for machine code.
Hello @francisrstokes,
This is a very interesting project, thanks! greatly inspired me to read about some theory this weekend.
I want to contribute as well but the ESLint configuration file is missing in the repository. What is the policy on this? :)
Cheers!
Previously LoaD Memory with register
because STore at Address
.
Create a pseudo instruction LDM
for compatibility.
To make accessing data structures easier, LDR
can take two additional arguments: MODE
and OFFSET_REGISTER
.
If MODE
is 1, then SOURCE
is treated as a base which is offset by the value in OFFSET_REGISTER
.
I think that the way cleanup
trims out comments will make programs with semicolons in the data section fail to assemble, or assemble incorrectly.
Those three instructions have only the 4-bit main opcode considered and no other relevant fields. Instead of having three different main opcode, we can have only one and used some X bits to carry the real opcode for SYS, HLT and RET and be able to add other possible instructions where no SS and DD are needed. As for SYS, I would have considered an immediate (8-bit ?) value to encode the system call code instead of relying on a register value (I believe it is A currently) . Two main opcodes would be freed for other usages that I can have in mind.
There is a different way to use stack (supposedly it resides somewhere in memory).
push a
push b
push c
can be done this way:
sub sp, 3 // allocate 3 slots in word unit
str a,[sp + 0]
str b,[sp + 1]
str c,[sp + 2]
pop c
pop b
pop a
can be done this way:
str a,[sp + 0]
str b,[sp + 1]
str c,[sp + 2]
add sp, 3 // relaese 3 slots in word unit
Benefits are:
One way to call a program by RISC CPUs:
jal a, routine // call a routine by saving return in 'a' and jumping to routine
...
routine:
[sub sp, 1] only if you need to reuse 'a' in the body
[str a,[sp] only if you need to reuse 'a' in the body
... // do something
[ldr d,[sp] only if you needed to reuse 'a'
[add sp, 1] only if you needed to reuse 'a'
jr d
Sure, push
, pop
, call
and ret
allow more compact code but push
and pop
are not great with more complex expressions where you need to dup registers in stack: having a direct access to read/write in a stack slot makes complex expressions cleaner and shorter.
JCP R1, R2, R3, Op
R1 is compared against R2, and the jump address is stored in R3. Operation will be in the list:
-Equal
-Not equal
-Less than
-Greater than
-Less than or equal
-Greater than or equal
-Zero (in which case R2 is ignored)
-Not zero (in which case R2 is ignored)
Create pseudo instructions for all the operations:
Can we put something in memory in a way that when dumped it looks like a giraffe?
p.s.:
There is an eval
in this line. That might result in code execution, so I tried to exploit it and give a proof of concept. This was quite hard to exploit since any ')' and ';' characters will cause the payload to be messed up: ';' will be seen as comments and ')' will cause the regex to stop capturing the group. Here's the proof of concept:
.data
.text
.global main:
main:
ldv A, ( [10, console.log`pwnt`][0] )
hlt
and here's the stdout of node src/assembler -i asm/poc.asm -o test.out
:
[ 'pwnt' ]
Read 6 instructions, including labels.
Expanded to 5, including labels.
Removed labels. Final instruction count: 4/65535
{}
Successfully assembled to binary file test.out
I would normally include a more serious (for example writing a file) proof of concept, but it was extremely hard to code in JS without parentheses.
CMP
should should take 3 arguments: two comparison registers and result register. If the two comparison registers are equal, the result register is set to 1, otherwise 0.
Under Windows 10 64-bit, latest version of Node.js: v8.2.1
.
I'm not familiar with Node.js as it is pretty recent I'm using it at work.
So once Node.js installed, I made npm install
and nothing else.
I tried this: C:\Dev\16bitjs>node src\assembler -i asm\factorial.asm -o bin\factorial.bin
And I got:
C:\Dev\16bitjs\src\assembler\preprocessor\get-data-table.js:70
throw new Error`Unsupported data declaraction:\n${cur}\nExiting...`);
^
SyntaxError: Unexpected token )
at createScript (vm.js:74:10)
at Object.runInThisContext (vm.js:116:10)
at Module._compile (module.js:533:28)
at Object.Module._extensions..js (module.js:580:10)
at Module.load (module.js:503:32)
at tryModuleLoad (module.js:466:12)
at Function.Module._load (module.js:458:3)
at Module.require (module.js:513:17)
at require (internal/module.js:11:18)
at Object.<anonymous> (C:\Dev\16bitjs\src\assembler\preprocessor\index.js:3:22)
at Module._compile (module.js:569:30)
at Object.Module._extensions..js (module.js:580:10)
at Module.load (module.js:503:32)
at tryModuleLoad (module.js:466:12)
at Function.Module._load (module.js:458:3)
at Module.require (module.js:513:17)
Not sure if the issue is due to my way to install Node.js.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.