Update after a long time!
This commit is contained in:
parent
2bc7ee5f42
commit
d6f7a51e11
32 changed files with 3005 additions and 117 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
||||||
/target
|
/target
|
||||||
|
examples/*.bin
|
||||||
|
|
43
Cargo.lock
generated
43
Cargo.lock
generated
|
@ -231,10 +231,22 @@ dependencies = [
|
||||||
"rand",
|
"rand",
|
||||||
"rhexdump",
|
"rhexdump",
|
||||||
"rust-embed",
|
"rust-embed",
|
||||||
"sdl2",
|
|
||||||
"unescape",
|
"unescape",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "henceforth"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"clap",
|
||||||
|
"hence",
|
||||||
|
"indexmap",
|
||||||
|
"itertools",
|
||||||
|
"lazy_static",
|
||||||
|
"parse_int",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.1.19"
|
version = "0.1.19"
|
||||||
|
@ -468,29 +480,6 @@ dependencies = [
|
||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sdl2"
|
|
||||||
version = "0.35.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f7959277b623f1fb9e04aea73686c3ca52f01b2145f8ea16f4ff30d8b7623b1a"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags",
|
|
||||||
"lazy_static",
|
|
||||||
"libc",
|
|
||||||
"sdl2-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sdl2-sys"
|
|
||||||
version = "0.35.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e3586be2cf6c0a8099a79a12b4084357aa9b3e0b0d7980e3b67aaf7a9d55f9f0"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"libc",
|
|
||||||
"version-compare",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha2"
|
name = "sha2"
|
||||||
version = "0.10.6"
|
version = "0.10.6"
|
||||||
|
@ -568,12 +557,6 @@ version = "0.1.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "version-compare"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.4"
|
version = "0.9.4"
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["hence"]
|
members = ["hence", "henceforth"]
|
||||||
|
|
56
examples/henceforth.asm
Normal file
56
examples/henceforth.asm
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
.include "$lib/core.asm"
|
||||||
|
.include "$lib/std.asm"
|
||||||
|
.include "$lib/main.asm"
|
||||||
|
|
||||||
|
.define HENCEFORTH_SOURCE_MAX_LEN, 128
|
||||||
|
.define HENCEFORTH_SOURCE_START, CORE_MEM_MEM
|
||||||
|
.define HENCEFORTH_SOURCE_END, (HENCEFORTH_SOURCE_START + HENCEFORTH_SOURCE_MAX_LEN)
|
||||||
|
.define HENCEFORTH_SOURCE_LEN, (HENCEFORTH_SOURCE_END + 1)
|
||||||
|
|
||||||
|
.jump_main
|
||||||
|
|
||||||
|
.def_main
|
||||||
|
ts HENCEFORTH_SOURCE_START
|
||||||
|
tlr CORE_REG_B
|
||||||
|
|
||||||
|
loop:
|
||||||
|
; get key press
|
||||||
|
ts CORE_MEM_KEY
|
||||||
|
get
|
||||||
|
tlr CORE_REG_A
|
||||||
|
|
||||||
|
; echo
|
||||||
|
ts CORE_MEM_CHR
|
||||||
|
set
|
||||||
|
|
||||||
|
; store in source
|
||||||
|
tsr CORE_REG_B
|
||||||
|
set
|
||||||
|
|
||||||
|
; increment pointer
|
||||||
|
ts 1
|
||||||
|
tlr CORE_REG_A
|
||||||
|
ts CORE_ALU_ADD
|
||||||
|
alu
|
||||||
|
tlr CORE_REG_B
|
||||||
|
|
||||||
|
; jump to loop start or not
|
||||||
|
; backup
|
||||||
|
tsr CORE_REG_B
|
||||||
|
tlr CORE_REG_C
|
||||||
|
|
||||||
|
; check
|
||||||
|
ts HENCEFORTH_SOURCE_END
|
||||||
|
tlr CORE_REG_A
|
||||||
|
ts CORE_ALU_GT
|
||||||
|
alu
|
||||||
|
tlr CORE_REG_A
|
||||||
|
|
||||||
|
; restore
|
||||||
|
tsr CORE_REG_C
|
||||||
|
tlr CORE_REG_B
|
||||||
|
|
||||||
|
; jump
|
||||||
|
ts loop
|
||||||
|
tlrc CORE_REG_PC
|
||||||
|
.std_stop
|
|
@ -5,5 +5,4 @@
|
||||||
.jump_main
|
.jump_main
|
||||||
|
|
||||||
main: .main main
|
main: .main main
|
||||||
|
|
||||||
.std_stop
|
.std_stop
|
||||||
|
|
Binary file not shown.
68
examples/testforth.asm
Normal file
68
examples/testforth.asm
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
.include "$lib/core.asm"
|
||||||
|
.include "$lib/std.asm"
|
||||||
|
.include "$lib/main.asm"
|
||||||
|
.define MEM_LOOP_I, CORE_MEM_MEM
|
||||||
|
.define MEM_LOOP_J, (MEM_LOOP_I + 1)
|
||||||
|
.define MEM_CALL_STACK_LEN, 16
|
||||||
|
.define MEM_CALL_STACK_PTR, (MEM_LOOP_J + 1)
|
||||||
|
.define MEM_CALL_STACK_END, (MEM_CALL_STACK_PTR + MEM_CALL_STACK_LEN)
|
||||||
|
.define MEM_ALLOC_PTR, MEM_CALL_STACK_END
|
||||||
|
.macro stack_transfer_alu
|
||||||
|
.std_ld
|
||||||
|
tlr CORE_REG_B
|
||||||
|
.std_ld
|
||||||
|
tlr CORE_REG_A
|
||||||
|
.endmacro
|
||||||
|
.macro call_stack_jump, call_stack_jump_arg_0_label, call_stack_jump_arg_1_offset
|
||||||
|
.std_rset CORE_REG_C, call_stack_jump_arg_0_label
|
||||||
|
.std_rset CORE_REG_D, (call_stack_jump_arg_1_offset + 7)
|
||||||
|
ts call_stack_jump
|
||||||
|
tlr CORE_REG_PC
|
||||||
|
.endmacro
|
||||||
|
.macro return_call_stack_jump
|
||||||
|
.std_jump return_call_stack_jump
|
||||||
|
.endmacro
|
||||||
|
.std_rset CORE_REG_A, MEM_CALL_STACK_PTR
|
||||||
|
.std_set MEM_CALL_STACK_PTR
|
||||||
|
.std_rset CORE_REG_A, (MEM_ALLOC_PTR + 1)
|
||||||
|
.std_set MEM_ALLOC_PTR
|
||||||
|
.jump_main
|
||||||
|
call_stack_jump:
|
||||||
|
.std_get MEM_CALL_STACK_PTR
|
||||||
|
tlr CORE_REG_A
|
||||||
|
.std_rset CORE_REG_B, 1
|
||||||
|
.std_alu CORE_ALU_ADD
|
||||||
|
tlr CORE_REG_A
|
||||||
|
tlr CORE_REG_B
|
||||||
|
.std_set MEM_CALL_STACK_PTR
|
||||||
|
tsr CORE_REG_D
|
||||||
|
tlr CORE_REG_A
|
||||||
|
tsr CORE_REG_B
|
||||||
|
set
|
||||||
|
tsr CORE_REG_C
|
||||||
|
tlr CORE_REG_PC
|
||||||
|
return_call_stack_jump:
|
||||||
|
.std_get MEM_CALL_STACK_PTR
|
||||||
|
tlr CORE_REG_A
|
||||||
|
tlr CORE_REG_C
|
||||||
|
.std_rset CORE_REG_B, 1
|
||||||
|
.std_alu CORE_ALU_SUB
|
||||||
|
tlr CORE_REG_A
|
||||||
|
.std_set MEM_CALL_STACK_PTR
|
||||||
|
tsr CORE_REG_C
|
||||||
|
get
|
||||||
|
tlr CORE_REG_PC
|
||||||
|
words_0:
|
||||||
|
; word: "test"
|
||||||
|
push 40
|
||||||
|
push 2
|
||||||
|
.stack_transfer_alu
|
||||||
|
.std_alu CORE_ALU_ADD
|
||||||
|
tls
|
||||||
|
.return_call_stack_jump
|
||||||
|
main:
|
||||||
|
.main main
|
||||||
|
.call_stack_jump words_0, OFFSET
|
||||||
|
dbg
|
||||||
|
.call_stack_jump words_0, OFFSET
|
||||||
|
.std_stop
|
3
examples/testforth.fth
Normal file
3
examples/testforth.fth
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
: test 40 2 + ;
|
||||||
|
|
||||||
|
test debug test
|
|
@ -18,4 +18,3 @@ anyhow = { version = "1.0.62", features = ["backtrace"] }
|
||||||
rust-embed = "6.4.0"
|
rust-embed = "6.4.0"
|
||||||
unescape = "0.1.0"
|
unescape = "0.1.0"
|
||||||
parse_int = "0.6.0"
|
parse_int = "0.6.0"
|
||||||
sdl2 = "0.35"
|
|
||||||
|
|
|
@ -1,52 +1,52 @@
|
||||||
; hence core lib
|
; hence core lib
|
||||||
|
|
||||||
.define NULL, 0x0000
|
.define NULL, 0
|
||||||
.define VOID, NULL
|
.define VOID, NULL
|
||||||
|
|
||||||
.define FALSE, NULL
|
.define FALSE, NULL
|
||||||
.define TRUE, 0x0001
|
.define TRUE, 1
|
||||||
|
|
||||||
.define CORE_U8_MAX, 0x00ff
|
.define CORE_U8_MAX, 0x00ff
|
||||||
.define CORE_U16_MAX, 0xffff
|
.define CORE_U16_MAX, 0xffff
|
||||||
|
|
||||||
.define CORE_KB, 1024
|
.define CORE_KB, 1024
|
||||||
|
|
||||||
.define CORE_MEM_PRG, (0 * CORE_KB)
|
.define CORE_MEM_PRG, (0 * CORE_KB)
|
||||||
.define CORE_MEM_PRG_END, (32 * CORE_KB)
|
.define CORE_MEM_PRG_END, (32 * CORE_KB)
|
||||||
.define CORE_MEM_ST, CORE_MEM_PRG_END
|
.define CORE_MEM_ST, CORE_MEM_PRG_END
|
||||||
.define CORE_MEM_MEM, (40 * CORE_KB)
|
.define CORE_MEM_MEM, (40 * CORE_KB)
|
||||||
.define CORE_MEM_MEM_END, (48 * CORE_KB)
|
.define CORE_MEM_MEM_END, (48 * CORE_KB)
|
||||||
.define CORE_MEM_OUT, CORE_MEM_MEM_END
|
.define CORE_MEM_OUT, CORE_MEM_MEM_END
|
||||||
.define CORE_MEM_CHR, (CORE_MEM_MEM_END + 1)
|
.define CORE_MEM_CHR, (CORE_MEM_MEM_END + 1)
|
||||||
.define CORE_MEM_KEY, (CORE_MEM_MEM_END + 2)
|
.define CORE_MEM_KEY, (CORE_MEM_MEM_END + 2)
|
||||||
|
|
||||||
.define CORE_REG_PC, 0x0
|
.define CORE_REG_PC, 0x0
|
||||||
.define CORE_REG_OPC, 0x1
|
.define CORE_REG_OPC, 0x1
|
||||||
.define CORE_REG_ARG, 0x2
|
.define CORE_REG_ARG, 0x2
|
||||||
.define CORE_REG_S, 0x3
|
.define CORE_REG_S, 0x3
|
||||||
.define CORE_REG_SP, 0x4
|
.define CORE_REG_SP, 0x4
|
||||||
.define CORE_REG_A, 0x5
|
.define CORE_REG_A, 0x5
|
||||||
.define CORE_REG_B, 0x6
|
.define CORE_REG_B, 0x6
|
||||||
.define CORE_REG_C, 0x7
|
.define CORE_REG_C, 0x7
|
||||||
.define CORE_REG_D, 0x8
|
.define CORE_REG_D, 0x8
|
||||||
|
|
||||||
.define CORE_ALU_NOT, 0x00
|
.define CORE_ALU_NOT, 0x00
|
||||||
.define CORE_ALU_AND, 0x01
|
.define CORE_ALU_AND, 0x01
|
||||||
.define CORE_ALU_OR, 0x02
|
.define CORE_ALU_OR, 0x02
|
||||||
.define CORE_ALU_XOR, 0x03
|
.define CORE_ALU_XOR, 0x03
|
||||||
.define CORE_ALU_LSH, 0x04
|
.define CORE_ALU_LSH, 0x04
|
||||||
.define CORE_ALU_RSH, 0x05
|
.define CORE_ALU_RSH, 0x05
|
||||||
.define CORE_ALU_ADD, 0x06
|
.define CORE_ALU_ADD, 0x06
|
||||||
.define CORE_ALU_SUB, 0x07
|
.define CORE_ALU_SUB, 0x07
|
||||||
.define CORE_ALU_MUL, 0x08
|
.define CORE_ALU_MUL, 0x08
|
||||||
.define CORE_ALU_DIV, 0x09
|
.define CORE_ALU_DIV, 0x09
|
||||||
.define CORE_ALU_CMP, 0x0a
|
.define CORE_ALU_CMP, 0x0a
|
||||||
.define CORE_ALU_EQ, 0x0b
|
.define CORE_ALU_EQ, 0x0b
|
||||||
.define CORE_ALU_LT, 0x0c
|
.define CORE_ALU_LT, 0x0c
|
||||||
.define CORE_ALU_GT, 0x0d
|
.define CORE_ALU_GT, 0x0d
|
||||||
.define CORE_ALU_LEQ, 0x0e
|
.define CORE_ALU_LEQ, 0x0e
|
||||||
.define CORE_ALU_GEQ, 0x0f
|
.define CORE_ALU_GEQ, 0x0f
|
||||||
.define CORE_ALU_BOL, 0x10
|
.define CORE_ALU_BOL, 0x10
|
||||||
.define CORE_ALU_INV, 0x11
|
.define CORE_ALU_INV, 0x11
|
||||||
.define CORE_ALU_RND, 0x12
|
.define CORE_ALU_RND, 0x12
|
||||||
.define CORE_ALU_TME, 0x13
|
.define CORE_ALU_TME, 0x13
|
||||||
|
|
|
@ -15,3 +15,7 @@
|
||||||
tlr CORE_REG_PC
|
tlr CORE_REG_PC
|
||||||
.org main
|
.org main
|
||||||
.endmacro
|
.endmacro
|
||||||
|
|
||||||
|
.macro def_main
|
||||||
|
main: .main main
|
||||||
|
.endmacro
|
||||||
|
|
|
@ -23,7 +23,9 @@ pub trait ByteResolvable<T> {
|
||||||
fn resolve_string(&self, data: &mut T) -> Result<String>;
|
fn resolve_string(&self, data: &mut T) -> Result<String>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const PROGRAM_SIZE: u16 = 32 * 1024;
|
pub const KB: u16 = 1024;
|
||||||
|
|
||||||
|
pub const PROGRAM_SIZE: u16 = 32 * KB;
|
||||||
pub type Program = [u8; PROGRAM_SIZE as usize];
|
pub type Program = [u8; PROGRAM_SIZE as usize];
|
||||||
|
|
||||||
#[derive(RustEmbed)]
|
#[derive(RustEmbed)]
|
||||||
|
|
|
@ -6,7 +6,7 @@ use std::io::{self, Write};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Data {
|
pub struct Emulator {
|
||||||
program: [u8; 32 * 1024],
|
program: [u8; 32 * 1024],
|
||||||
pub tmp: u16,
|
pub tmp: u16,
|
||||||
pub reg_pc: u16,
|
pub reg_pc: u16,
|
||||||
|
@ -24,7 +24,7 @@ pub struct Data {
|
||||||
term: console::Term,
|
term: console::Term,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Data {
|
impl Emulator {
|
||||||
pub fn new(program: [u8; 32 * 1024]) -> Self {
|
pub fn new(program: [u8; 32 * 1024]) -> Self {
|
||||||
Self {
|
Self {
|
||||||
program,
|
program,
|
||||||
|
@ -135,7 +135,7 @@ impl Data {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alu(&mut self, operation: u8) -> Result<()> {
|
pub fn alu(&mut self, operation: u8) -> Result<()> {
|
||||||
match operation {
|
match operation {
|
||||||
0x00 => {
|
0x00 => {
|
||||||
self.tmp = !self.reg_a;
|
self.tmp = !self.reg_a;
|
||||||
|
@ -224,76 +224,86 @@ impl Data {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn emulate(data: &mut Data) -> Result<()> {
|
pub fn cycle(&mut self) -> Result<()> {
|
||||||
while data.reg_pc != 0xffff {
|
self.reg_opc = self.get_memory(self.reg_pc)? as u8;
|
||||||
data.reg_opc = data.get_memory(data.reg_pc)? as u8;
|
self.reg_pc = self.reg_pc.wrapping_add(1);
|
||||||
data.reg_pc = data.reg_pc.wrapping_add(1);
|
|
||||||
|
|
||||||
if data.reg_opc >> 7 == 1 {
|
if self.reg_opc >> 7 == 1 {
|
||||||
data.reg_opc &= 0b0111111;
|
self.reg_opc &= 0b0111111;
|
||||||
data.reg_arg = 0;
|
self.reg_arg = 0;
|
||||||
} else {
|
} else {
|
||||||
data.reg_arg = data.get_memory(data.reg_pc)? << 8;
|
self.reg_arg = self.get_memory(self.reg_pc)? << 8;
|
||||||
data.reg_pc = data.reg_pc.wrapping_add(1);
|
self.reg_pc = self.reg_pc.wrapping_add(1);
|
||||||
|
|
||||||
data.reg_arg |= data.get_memory(data.reg_pc)?;
|
self.reg_arg |= self.get_memory(self.reg_pc)?;
|
||||||
data.reg_pc = data.reg_pc.wrapping_add(1);
|
self.reg_pc = self.reg_pc.wrapping_add(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
match data.reg_opc {
|
match self.reg_opc {
|
||||||
0x00 => {}
|
0x00 => {}
|
||||||
0x01 => {
|
0x01 => {
|
||||||
data.tmp = data.reg_arg;
|
self.tmp = self.reg_arg;
|
||||||
data.memory[data.reg_sp as usize] = data.reg_arg;
|
self.memory[self.reg_sp as usize] = self.reg_arg;
|
||||||
data.reg_sp = data.reg_sp.wrapping_add(1);
|
self.reg_sp = self.reg_sp.wrapping_add(1);
|
||||||
}
|
}
|
||||||
0x02 => {
|
0x02 => {
|
||||||
data.reg_sp = data.reg_sp.wrapping_sub(1);
|
self.reg_sp = self.reg_sp.wrapping_sub(1);
|
||||||
}
|
}
|
||||||
0x03 => {
|
0x03 => {
|
||||||
data.tmp = data.reg_arg;
|
self.tmp = self.reg_arg;
|
||||||
}
|
}
|
||||||
0x04 => {
|
0x04 => {
|
||||||
data.tmp = data.get_register(data.reg_arg as u8);
|
self.tmp = self.get_register(self.reg_arg as u8);
|
||||||
}
|
}
|
||||||
0x05 => {
|
0x05 => {
|
||||||
data.tmp = data.memory[data.reg_sp.wrapping_sub(1) as usize];
|
self.tmp = self.memory[self.reg_sp.wrapping_sub(1) as usize];
|
||||||
}
|
}
|
||||||
0x06 => {
|
0x06 => {
|
||||||
data.set_register(data.reg_arg as u8, data.tmp);
|
self.set_register(self.reg_arg as u8, self.tmp);
|
||||||
}
|
}
|
||||||
0x07 => {
|
0x07 => {
|
||||||
if data.reg_a & 1 == 1 {
|
if self.reg_a & 1 == 1 {
|
||||||
data.set_register(data.reg_arg as u8, data.tmp);
|
self.set_register(self.reg_arg as u8, self.tmp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0x08 => {
|
0x08 => {
|
||||||
data.memory[data.reg_sp as usize] = data.tmp;
|
self.memory[self.reg_sp as usize] = self.tmp;
|
||||||
data.reg_sp = data.reg_sp.wrapping_add(1);
|
self.reg_sp = self.reg_sp.wrapping_add(1);
|
||||||
}
|
}
|
||||||
0x09 => {
|
0x09 => {
|
||||||
println!(
|
println!(
|
||||||
"[DEBUG]: [{}]",
|
"[DEBUG]: [{}]",
|
||||||
data.memory.iter().take(data.reg_sp as usize).join(", ")
|
self.memory.iter().take(self.reg_sp as usize).join(", ")
|
||||||
);
|
);
|
||||||
print!("Press enter to continue execution...",);
|
print!("Press enter to continue execution...",);
|
||||||
io::stdout().flush()?;
|
io::stdout().flush()?;
|
||||||
data.term.read_line()?;
|
self.term.read_line()?;
|
||||||
}
|
}
|
||||||
0x0a => {
|
0x0a => {
|
||||||
data.alu(data.tmp as u8)?;
|
self.alu(self.tmp as u8)?;
|
||||||
}
|
}
|
||||||
0x0b => {
|
0x0b => {
|
||||||
data.tmp = data.get_memory(data.tmp)?;
|
self.tmp = self.get_memory(self.tmp)?;
|
||||||
}
|
}
|
||||||
0x0c => {
|
0x0c => {
|
||||||
data.set_memory(data.tmp, data.reg_a)?;
|
self.set_memory(self.tmp, self.reg_a)?;
|
||||||
}
|
|
||||||
_ => bail!("Invalid opcode: 0x{}", radix(data.reg_opc, 16)),
|
|
||||||
}
|
}
|
||||||
|
_ => bail!("Invalid opcode: 0x{}", radix(self.reg_opc, 16)),
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_stopped(&self) -> bool {
|
||||||
|
self.reg_pc == 0xffff
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(&mut self) -> Result<()> {
|
||||||
|
while !self.is_stopped() {
|
||||||
|
self.cycle()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,9 +95,7 @@ fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let program: [u8; 32 * 1024] = (&program_buf[0..(32 * 1024)]).try_into()?;
|
let program: [u8; 32 * 1024] = (&program_buf[0..(32 * 1024)]).try_into()?;
|
||||||
let mut data = emulator::Data::new(program);
|
emulator::Emulator::new(program).run()?;
|
||||||
|
|
||||||
emulator::emulate(&mut data)?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
2
henceforth/.gitignore
vendored
Normal file
2
henceforth/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
examples/*.asm
|
||||||
|
examples/*.bin
|
17
henceforth/Cargo.toml
Normal file
17
henceforth/Cargo.toml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
[package]
|
||||||
|
name = "henceforth"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
authors = ["Dominic Grimm <dominic@dergrimm.net>"]
|
||||||
|
repository = "https://git.dergrimm.net/dergrimm/hence.git"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
hence = { path = "../hence" }
|
||||||
|
clap = { version = "3.2.16", features = ["derive"] }
|
||||||
|
anyhow = { version = "1.0.62", features = ["backtrace"] }
|
||||||
|
itertools = "0.10.2"
|
||||||
|
parse_int = "0.6.0"
|
||||||
|
indexmap = "1.9.1"
|
||||||
|
lazy_static = "1.4.0"
|
259
henceforth/src/compiler.rs
Normal file
259
henceforth/src/compiler.rs
Normal file
|
@ -0,0 +1,259 @@
|
||||||
|
use anyhow::{bail, Context, Result};
|
||||||
|
use indexmap::IndexSet;
|
||||||
|
use itertools::Itertools;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::parser;
|
||||||
|
|
||||||
|
mod instruction;
|
||||||
|
|
||||||
|
pub use instruction::Instruction;
|
||||||
|
|
||||||
|
pub const TEMPLATE_ASM: &str = include_str!("compiler/templates/default.asm");
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub static ref TEMPLATE: hence::parser::ast::Body = hence::parser::parse(
|
||||||
|
hence::lexer::lex(TEMPLATE_ASM).unwrap()
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
.body;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Compilable<T, U> {
|
||||||
|
fn compile(&self, data: &T) -> Result<U>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Word {
|
||||||
|
pub id: usize,
|
||||||
|
pub instructions: Vec<Instruction>,
|
||||||
|
pub times_used: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Condition {
|
||||||
|
pub if_instructions: Vec<Instruction>,
|
||||||
|
pub else_instructions: Vec<Instruction>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum CallableId {
|
||||||
|
Word(String),
|
||||||
|
Condition(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Compiler {
|
||||||
|
pub strings: IndexSet<String>,
|
||||||
|
pub words: HashMap<String, Word>,
|
||||||
|
pub conditions: Vec<Condition>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Compiler {
|
||||||
|
pub fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
// words: HashMap::new(),
|
||||||
|
// conditions: IndexSet::new(),
|
||||||
|
strings: IndexSet::new(),
|
||||||
|
words: HashMap::new(),
|
||||||
|
conditions: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_instructions(
|
||||||
|
&mut self,
|
||||||
|
body: parser::ast::Body,
|
||||||
|
optimize: bool,
|
||||||
|
) -> Result<Vec<Instruction>> {
|
||||||
|
let mut instructions: Vec<Instruction> = vec![];
|
||||||
|
let mut iter = body.into_iter().peekable();
|
||||||
|
while let Some(node) = iter.next() {
|
||||||
|
match node {
|
||||||
|
_ if optimize && iter.next_if_eq(&node).is_some() => {
|
||||||
|
let count = iter.by_ref().peeking_take_while(|n| *n == node).count() + 2;
|
||||||
|
instructions.push(Instruction::Multiple {
|
||||||
|
instruction: Box::new(
|
||||||
|
self.generate_instructions(vec![node], optimize)?
|
||||||
|
.into_iter()
|
||||||
|
.next()
|
||||||
|
.unwrap(),
|
||||||
|
),
|
||||||
|
count,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
parser::ast::Node::Comment(_) => {}
|
||||||
|
parser::ast::Node::String { mode, string } => {
|
||||||
|
instructions.push(match mode.as_str() {
|
||||||
|
"." => {
|
||||||
|
let id = self.strings.insert_full(string).0;
|
||||||
|
Instruction::StringPrint(id)
|
||||||
|
}
|
||||||
|
"r" => {
|
||||||
|
let id = self.strings.insert_full(string).0;
|
||||||
|
Instruction::StringReference(id)
|
||||||
|
}
|
||||||
|
"asm" => Instruction::AsmQuote(string),
|
||||||
|
_ => bail!("Unknown string mode: {}", mode),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
parser::ast::Node::Number(x) => {
|
||||||
|
instructions.push(instruction::Instruction::Push(x));
|
||||||
|
}
|
||||||
|
parser::ast::Node::WordDefinition {
|
||||||
|
name,
|
||||||
|
stack: _,
|
||||||
|
body,
|
||||||
|
} => {
|
||||||
|
if Instruction::from_word(&name).is_some() {
|
||||||
|
bail!("Word already exists as compiler instruction: {}", name);
|
||||||
|
} else if self.words.contains_key(&name) {
|
||||||
|
bail!("Word already exists as user word definition: {}", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
let instructions = self.generate_instructions(body, optimize)?;
|
||||||
|
self.words.insert(
|
||||||
|
name.to_string(),
|
||||||
|
Word {
|
||||||
|
id: self.words.len(),
|
||||||
|
instructions,
|
||||||
|
times_used: 0,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
parser::ast::Node::Condition { if_body, else_body } => {
|
||||||
|
// let if_instructions = self.generate_instructions(if_body, optimize)?;
|
||||||
|
// let else_instructions = self.generate_instructions(else_body, optimize)?;
|
||||||
|
// let id = self.conditions.len();
|
||||||
|
// let origin = self.callable_graph.add_node(CallableId::Condition(id));
|
||||||
|
// self.conditions.push(Condition {
|
||||||
|
// if_instructions: if_instructions.clone(),
|
||||||
|
// else_instructions: else_instructions.clone(),
|
||||||
|
// callable_graph_node: origin,
|
||||||
|
// });
|
||||||
|
// instructions.push(Instruction::Condition(id));
|
||||||
|
// self.add_graph_edges(origin, if_instructions)?;
|
||||||
|
// self.add_graph_edges(origin, else_instructions)?;
|
||||||
|
// dbg!(&self);
|
||||||
|
}
|
||||||
|
parser::ast::Node::Word(x) => {
|
||||||
|
dbg!(&self.words, &x);
|
||||||
|
if let Some(ins) = Instruction::from_word(&x) {
|
||||||
|
instructions.push(ins);
|
||||||
|
} else if let Some(w) = self.words.get_mut(&x) {
|
||||||
|
w.times_used += 1;
|
||||||
|
instructions.push(Instruction::Call(x));
|
||||||
|
} else {
|
||||||
|
bail!("Word does not exist: {}", x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(instructions)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn embed(&self, body: hence::parser::ast::Body) -> Result<hence::parser::ast::Body> {
|
||||||
|
let mut x = TEMPLATE.to_vec();
|
||||||
|
|
||||||
|
// strings
|
||||||
|
for (id, s) in self.strings.iter().enumerate() {
|
||||||
|
x.extend([
|
||||||
|
hence::parser::ast::Node::Label(format!("data_strings_{}", id)),
|
||||||
|
hence::parser::ast::Node::MacroCall {
|
||||||
|
name: "bytes".to_string(),
|
||||||
|
args: vec![hence::arg::Arg::String(s.to_string())],
|
||||||
|
},
|
||||||
|
hence::parser::ast::Node::Label(format!("data_strings_end_{}", id)),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// conditions
|
||||||
|
for (id, c) in self.conditions.iter().enumerate() {
|
||||||
|
x.push(hence::parser::ast::Node::Label(format!(
|
||||||
|
"conditions_if_{}",
|
||||||
|
id
|
||||||
|
)));
|
||||||
|
x.extend(
|
||||||
|
c.if_instructions
|
||||||
|
.iter()
|
||||||
|
.map(|ins| ins.compile(self))
|
||||||
|
.collect::<Result<Vec<_>>>()?
|
||||||
|
.into_iter()
|
||||||
|
.flatten(),
|
||||||
|
);
|
||||||
|
x.push(hence::parser::ast::Node::Label(format!(
|
||||||
|
"conditions_else_{}",
|
||||||
|
id
|
||||||
|
)));
|
||||||
|
x.extend(
|
||||||
|
c.else_instructions
|
||||||
|
.iter()
|
||||||
|
.map(|ins| ins.compile(self))
|
||||||
|
.collect::<Result<Vec<_>>>()?
|
||||||
|
.into_iter()
|
||||||
|
.flatten(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// words
|
||||||
|
for (name, word) in &self
|
||||||
|
.words
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, w)| w.times_used > 1)
|
||||||
|
.sorted_by(|a, b| Ord::cmp(&a.1.id, &b.1.id))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
{
|
||||||
|
x.extend(vec![
|
||||||
|
hence::parser::ast::Node::Label(format!("words_{}", word.id)),
|
||||||
|
hence::parser::ast::Node::Comment(format!("word: \"{}\"", name)),
|
||||||
|
]);
|
||||||
|
x.extend(
|
||||||
|
word.instructions
|
||||||
|
.iter()
|
||||||
|
.map(|ins| ins.compile(self))
|
||||||
|
.collect::<Result<Vec<hence::parser::ast::Body>>>()
|
||||||
|
.unwrap()
|
||||||
|
.into_iter()
|
||||||
|
.flatten(),
|
||||||
|
);
|
||||||
|
x.push(hence::parser::ast::Node::MacroCall {
|
||||||
|
name: "return_call_stack_jump".to_string(),
|
||||||
|
args: vec![],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
x.extend([
|
||||||
|
hence::parser::ast::Node::Label("main".to_string()),
|
||||||
|
hence::parser::ast::Node::MacroCall {
|
||||||
|
name: "main".to_string(),
|
||||||
|
args: vec![hence::arg::Arg::Variable("main".to_string())],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
x.extend(body);
|
||||||
|
x.push(hence::parser::ast::Node::MacroCall {
|
||||||
|
name: "std_stop".to_string(),
|
||||||
|
args: vec![],
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compile(ast: parser::ast::AST, optimize: bool) -> Result<hence::parser::ast::AST> {
|
||||||
|
let mut data = Compiler::default();
|
||||||
|
let instructions = data.generate_instructions(ast.body, optimize)?;
|
||||||
|
|
||||||
|
Ok(hence::parser::ast::AST {
|
||||||
|
body: data.embed(
|
||||||
|
instructions
|
||||||
|
.iter()
|
||||||
|
.map(|ins| ins.compile(&data))
|
||||||
|
.collect::<Result<Vec<hence::parser::ast::Body>>>()?
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.collect(),
|
||||||
|
)?,
|
||||||
|
})
|
||||||
|
}
|
1533
henceforth/src/compiler/instruction.rs
Normal file
1533
henceforth/src/compiler/instruction.rs
Normal file
File diff suppressed because it is too large
Load diff
67
henceforth/src/compiler/templates/default.asm
Normal file
67
henceforth/src/compiler/templates/default.asm
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
.include "$lib/core.asm"
|
||||||
|
.include "$lib/std.asm"
|
||||||
|
.include "$lib/main.asm"
|
||||||
|
|
||||||
|
.define MEM_LOOP_I, CORE_MEM_MEM
|
||||||
|
.define MEM_LOOP_J, (MEM_LOOP_I + 1)
|
||||||
|
|
||||||
|
.define MEM_CALL_STACK_LEN, 16
|
||||||
|
.define MEM_CALL_STACK_PTR, (MEM_LOOP_J + 1)
|
||||||
|
.define MEM_CALL_STACK_END, (MEM_CALL_STACK_PTR + MEM_CALL_STACK_LEN)
|
||||||
|
|
||||||
|
.define MEM_ALLOC_PTR, MEM_CALL_STACK_END
|
||||||
|
|
||||||
|
.macro stack_transfer_alu
|
||||||
|
.std_ld
|
||||||
|
tlr CORE_REG_B
|
||||||
|
.std_ld
|
||||||
|
tlr CORE_REG_A
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
.macro call_stack_jump, call_stack_jump_arg_0_label, call_stack_jump_arg_1_offset
|
||||||
|
.std_rset CORE_REG_C, call_stack_jump_arg_0_label
|
||||||
|
.std_rset CORE_REG_D, (call_stack_jump_arg_1_offset + 7)
|
||||||
|
ts call_stack_jump
|
||||||
|
tlr CORE_REG_PC
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
.macro return_call_stack_jump
|
||||||
|
.std_jump return_call_stack_jump
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
.std_rset CORE_REG_A, MEM_CALL_STACK_PTR
|
||||||
|
.std_set MEM_CALL_STACK_PTR
|
||||||
|
|
||||||
|
.std_rset CORE_REG_A, (MEM_ALLOC_PTR + 1)
|
||||||
|
.std_set MEM_ALLOC_PTR
|
||||||
|
|
||||||
|
.jump_main
|
||||||
|
|
||||||
|
call_stack_jump:
|
||||||
|
.std_get MEM_CALL_STACK_PTR
|
||||||
|
tlr CORE_REG_A
|
||||||
|
.std_rset CORE_REG_B, 1
|
||||||
|
.std_alu CORE_ALU_ADD
|
||||||
|
tlr CORE_REG_A
|
||||||
|
tlr CORE_REG_B
|
||||||
|
.std_set MEM_CALL_STACK_PTR
|
||||||
|
|
||||||
|
tsr CORE_REG_D
|
||||||
|
tlr CORE_REG_A
|
||||||
|
tsr CORE_REG_B
|
||||||
|
set
|
||||||
|
tsr CORE_REG_C
|
||||||
|
tlr CORE_REG_PC
|
||||||
|
|
||||||
|
return_call_stack_jump:
|
||||||
|
.std_get MEM_CALL_STACK_PTR
|
||||||
|
tlr CORE_REG_A
|
||||||
|
tlr CORE_REG_C
|
||||||
|
.std_rset CORE_REG_B, 1
|
||||||
|
.std_alu CORE_ALU_SUB
|
||||||
|
tlr CORE_REG_A
|
||||||
|
.std_set MEM_CALL_STACK_PTR
|
||||||
|
|
||||||
|
tsr CORE_REG_C
|
||||||
|
get
|
||||||
|
tlr CORE_REG_PC
|
84
henceforth/src/lexer.rs
Normal file
84
henceforth/src/lexer.rs
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use hence::assembler::ToCode;
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Token {
|
||||||
|
Newline(usize),
|
||||||
|
Whitespace(usize),
|
||||||
|
|
||||||
|
ParenComment(String),
|
||||||
|
BackslashComment(String),
|
||||||
|
DoubleDashComment(String),
|
||||||
|
|
||||||
|
StringLiteral { mode: String, string: String },
|
||||||
|
Number(String),
|
||||||
|
Word(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCode for Token {
|
||||||
|
fn to_code(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Token::Newline(x) => ["\n"].into_iter().cycle().take(*x).join(""),
|
||||||
|
Token::Whitespace(x) => [" "].into_iter().cycle().take(*x).join(""),
|
||||||
|
Token::ParenComment(x) => format!("( {})", x),
|
||||||
|
Token::BackslashComment(x) => format!("\\{}", x),
|
||||||
|
Token::DoubleDashComment(x) => format!("-- {}", x),
|
||||||
|
Token::StringLiteral { mode, string } => format!("{}\" {}\"", mode, string),
|
||||||
|
Token::Number(x) | Token::Word(x) => x.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_space(c: char) -> bool {
|
||||||
|
c.is_whitespace() || c == '\n'
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lex(source: &str) -> Result<Vec<Token>> {
|
||||||
|
let mut chars = source.chars().peekable();
|
||||||
|
let mut tokens: Vec<Token> = vec![];
|
||||||
|
|
||||||
|
while let Some(c) = chars.peek() {
|
||||||
|
tokens.push(match c {
|
||||||
|
'\n' => Token::Newline(chars.peeking_take_while(|&c| c == '\n').count()),
|
||||||
|
_ if c.is_whitespace() => {
|
||||||
|
Token::Whitespace(chars.peeking_take_while(|&c| c.is_whitespace()).count())
|
||||||
|
}
|
||||||
|
'\\' => Token::BackslashComment(chars.peeking_take_while(|&c| c != '\n').collect()),
|
||||||
|
_ if c.is_numeric() => {
|
||||||
|
Token::Number(chars.peeking_take_while(|&c| !is_space(c)).collect())
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let x: String = chars.peeking_take_while(|&c| !is_space(c)).collect();
|
||||||
|
let mut iter = x.chars();
|
||||||
|
|
||||||
|
match x.as_str() {
|
||||||
|
"(" => Token::ParenComment(
|
||||||
|
chars.by_ref().skip(1).take_while(|&c| c != ')').collect(),
|
||||||
|
),
|
||||||
|
"--" => Token::DoubleDashComment(
|
||||||
|
chars.by_ref().take_while(|&c| c != '\n').collect(),
|
||||||
|
),
|
||||||
|
_ if x.ends_with('"') => Token::StringLiteral {
|
||||||
|
mode: x.chars().take(x.len() - 1).collect(),
|
||||||
|
string: chars.by_ref().skip(1).take_while(|&c| c != '"').collect(),
|
||||||
|
},
|
||||||
|
_ if iter.next() == Some('-') => {
|
||||||
|
if let Some(c) = iter.next() {
|
||||||
|
if c.is_numeric() {
|
||||||
|
Token::Number(x)
|
||||||
|
} else {
|
||||||
|
Token::Word(x)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Token::Word(x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Token::Word(x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(tokens)
|
||||||
|
}
|
3
henceforth/src/lib.rs
Normal file
3
henceforth/src/lib.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
pub mod compiler;
|
||||||
|
pub mod lexer;
|
||||||
|
pub mod parser;
|
80
henceforth/src/main.rs
Normal file
80
henceforth/src/main.rs
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use clap::{Parser, Subcommand};
|
||||||
|
use hence::assembler::ToCode;
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
|
use henceforth::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
#[clap(author, version, about, long_about = None)]
|
||||||
|
struct Cli {
|
||||||
|
#[clap(subcommand)]
|
||||||
|
commands: Commands,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Subcommand)]
|
||||||
|
enum Commands {
|
||||||
|
#[clap(about = "Lexes source code and outputs tokens")]
|
||||||
|
Lex {
|
||||||
|
#[clap(value_parser)]
|
||||||
|
src: String,
|
||||||
|
},
|
||||||
|
#[clap(about = "Parses source code and outputs AST")]
|
||||||
|
Parse {
|
||||||
|
#[clap(value_parser)]
|
||||||
|
src: String,
|
||||||
|
},
|
||||||
|
#[clap(about = "Compiles assembly from source code")]
|
||||||
|
Compile {
|
||||||
|
#[clap(value_parser)]
|
||||||
|
src: String,
|
||||||
|
#[clap(value_parser)]
|
||||||
|
out: Option<String>,
|
||||||
|
#[clap(short, long, action)]
|
||||||
|
optimize: Option<bool>,
|
||||||
|
#[clap(long, action)]
|
||||||
|
dump: bool,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
let args = Cli::parse();
|
||||||
|
match args.commands {
|
||||||
|
Commands::Lex { src } => {
|
||||||
|
let source = fs::read_to_string(src)?;
|
||||||
|
let tokens = lexer::lex(&source)?;
|
||||||
|
println!("{:#?}", tokens);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Commands::Parse { src } => {
|
||||||
|
let source = fs::read_to_string(src)?;
|
||||||
|
let tokens = lexer::lex(&source)?;
|
||||||
|
let body = parser::parse(tokens)?;
|
||||||
|
println!("{:#?}", body);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Commands::Compile {
|
||||||
|
src,
|
||||||
|
out,
|
||||||
|
optimize,
|
||||||
|
dump,
|
||||||
|
} => {
|
||||||
|
let source = fs::read_to_string(&src)?;
|
||||||
|
let tokens = lexer::lex(&source)?;
|
||||||
|
let ast = parser::parse(tokens)?;
|
||||||
|
let ast = compiler::compile(ast, optimize.unwrap_or(true))?;
|
||||||
|
let assembly = format!("{}\n", ast.to_code());
|
||||||
|
|
||||||
|
if dump {
|
||||||
|
print!("{}", assembly);
|
||||||
|
}
|
||||||
|
if let Some(x) = out {
|
||||||
|
fs::write(x, &assembly)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
168
henceforth/src/parser.rs
Normal file
168
henceforth/src/parser.rs
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
use anyhow::{bail, Result};
|
||||||
|
use parse_int;
|
||||||
|
|
||||||
|
use crate::lexer;
|
||||||
|
|
||||||
|
pub mod ast;
|
||||||
|
|
||||||
|
pub fn parse_stack_state(s: Option<&str>) -> Vec<String> {
|
||||||
|
match s {
|
||||||
|
Some(x) if !x.trim().is_empty() => {
|
||||||
|
x.split_whitespace().map(|x| x.trim().to_string()).collect()
|
||||||
|
}
|
||||||
|
_ => vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_stack_result(s: &str) -> ast::StackResult {
|
||||||
|
let mut splitter = s.splitn(2, "--");
|
||||||
|
|
||||||
|
ast::StackResult {
|
||||||
|
before: parse_stack_state(splitter.next()),
|
||||||
|
after: parse_stack_state(splitter.next()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(tokens: Vec<lexer::Token>) -> Result<ast::AST> {
|
||||||
|
let mut iter = tokens.into_iter().peekable();
|
||||||
|
let mut body: ast::Body = vec![];
|
||||||
|
|
||||||
|
while let Some(token) = iter.next() {
|
||||||
|
match token {
|
||||||
|
lexer::Token::Newline(_) | lexer::Token::Whitespace(_) => {}
|
||||||
|
lexer::Token::ParenComment(x)
|
||||||
|
| lexer::Token::BackslashComment(x)
|
||||||
|
| lexer::Token::DoubleDashComment(x) => {
|
||||||
|
body.push(ast::Node::Comment(x.trim().to_string()));
|
||||||
|
}
|
||||||
|
lexer::Token::StringLiteral { mode, string } => {
|
||||||
|
body.push(ast::Node::String { mode, string });
|
||||||
|
}
|
||||||
|
lexer::Token::Number(x) => body.push(ast::Node::Number(parse_int::parse(&x)?)),
|
||||||
|
lexer::Token::Word(x) => match x.as_str() {
|
||||||
|
":" => {
|
||||||
|
let mut depth: usize = 1;
|
||||||
|
let mut content = iter
|
||||||
|
.by_ref()
|
||||||
|
.take_while(|t| match t {
|
||||||
|
lexer::Token::Word(x) => match x.as_str() {
|
||||||
|
":" => {
|
||||||
|
depth += 1;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
";" => {
|
||||||
|
depth -= 1;
|
||||||
|
depth != 0
|
||||||
|
}
|
||||||
|
_ => true,
|
||||||
|
},
|
||||||
|
_ => true,
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.into_iter()
|
||||||
|
.peekable();
|
||||||
|
if depth != 0 {
|
||||||
|
bail!("Unbalanced word definitions");
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = match content.find(|t| {
|
||||||
|
!matches!(t, lexer::Token::Newline(_) | lexer::Token::Whitespace(_))
|
||||||
|
}) {
|
||||||
|
Some(t) => match t {
|
||||||
|
lexer::Token::Word(x) => x.clone(),
|
||||||
|
_ => bail!("Word definition name must be a word itself: {:?}", t),
|
||||||
|
},
|
||||||
|
None => bail!("Word definition can not be empty"),
|
||||||
|
};
|
||||||
|
let stack = loop {
|
||||||
|
if let Some(t) = content.peek() {
|
||||||
|
match t {
|
||||||
|
lexer::Token::Newline(_) | lexer::Token::Whitespace(_) => {
|
||||||
|
content.next();
|
||||||
|
}
|
||||||
|
lexer::Token::ParenComment(x)
|
||||||
|
| lexer::Token::BackslashComment(x)
|
||||||
|
| lexer::Token::DoubleDashComment(x) => {
|
||||||
|
let y = &x.to_string();
|
||||||
|
content.next();
|
||||||
|
break Some(parse_stack_result(y));
|
||||||
|
}
|
||||||
|
_ => break None,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
body.push(ast::Node::WordDefinition {
|
||||||
|
name,
|
||||||
|
stack,
|
||||||
|
body: parse(content.collect())?.body,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
"if" => {
|
||||||
|
let mut depth: usize = 1;
|
||||||
|
let mut else_used = false;
|
||||||
|
let if_toks: Vec<_> = iter
|
||||||
|
.by_ref()
|
||||||
|
.take_while(|t| match t {
|
||||||
|
lexer::Token::Word(x) => match x.as_str() {
|
||||||
|
"if" => {
|
||||||
|
depth += 1;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
"else" => {
|
||||||
|
if depth == 1 {
|
||||||
|
else_used = true;
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"then" => {
|
||||||
|
depth -= 1;
|
||||||
|
depth != 0
|
||||||
|
}
|
||||||
|
_ => true,
|
||||||
|
},
|
||||||
|
_ => true,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let else_toks: Vec<_> = if else_used {
|
||||||
|
iter.by_ref()
|
||||||
|
.take_while(|t| match t {
|
||||||
|
lexer::Token::Word(x) => match x.as_str() {
|
||||||
|
"if" => {
|
||||||
|
depth += 1;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
"then" => {
|
||||||
|
depth -= 1;
|
||||||
|
depth != 0
|
||||||
|
}
|
||||||
|
_ => true,
|
||||||
|
},
|
||||||
|
_ => true,
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
};
|
||||||
|
if depth != 0 {
|
||||||
|
bail!("Unbalanced conditions");
|
||||||
|
}
|
||||||
|
|
||||||
|
body.push(ast::Node::Condition {
|
||||||
|
if_body: parse(if_toks)?.body,
|
||||||
|
else_body: parse(else_toks)?.body,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
body.push(ast::Node::Word(x));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ast::AST { body })
|
||||||
|
}
|
90
henceforth/src/parser/ast.rs
Normal file
90
henceforth/src/parser/ast.rs
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
use hence::assembler::ToCode;
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
|
pub struct StackResult {
|
||||||
|
pub before: Vec<String>,
|
||||||
|
pub after: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCode for StackResult {
|
||||||
|
fn to_code(&self) -> String {
|
||||||
|
format!(
|
||||||
|
"{}--{}",
|
||||||
|
if self.before.is_empty() {
|
||||||
|
"".to_string()
|
||||||
|
} else {
|
||||||
|
format!("{} ", self.before.join(" "))
|
||||||
|
},
|
||||||
|
if self.after.is_empty() {
|
||||||
|
"".to_string()
|
||||||
|
} else {
|
||||||
|
format!("{} ", self.after.join(" "))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum Node {
|
||||||
|
Comment(String),
|
||||||
|
String {
|
||||||
|
mode: String,
|
||||||
|
string: String,
|
||||||
|
},
|
||||||
|
Number(i32),
|
||||||
|
WordDefinition {
|
||||||
|
name: String,
|
||||||
|
stack: Option<StackResult>,
|
||||||
|
body: Body,
|
||||||
|
},
|
||||||
|
Condition {
|
||||||
|
if_body: Body,
|
||||||
|
else_body: Body,
|
||||||
|
},
|
||||||
|
Word(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCode for Node {
|
||||||
|
fn to_code(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Node::Comment(x) => format!("\\ {}", x),
|
||||||
|
Node::String { mode, string } => format!("{}\" {}\"", mode, string),
|
||||||
|
Node::Number(x) => x.to_string(),
|
||||||
|
Node::WordDefinition { name, stack, body } => format!(
|
||||||
|
": {}{} {} ;",
|
||||||
|
name,
|
||||||
|
match stack {
|
||||||
|
Some(x) => format!(" {}", x.to_code()),
|
||||||
|
None => "".to_string(),
|
||||||
|
},
|
||||||
|
body.iter().map(|x| x.to_code()).join(" ")
|
||||||
|
),
|
||||||
|
Node::Condition { if_body, else_body } => {
|
||||||
|
if else_body.is_empty() {
|
||||||
|
format!("if {} then", if_body.iter().map(|x| x.to_code()).join(" "))
|
||||||
|
} else {
|
||||||
|
format!(
|
||||||
|
"if {} else {} then",
|
||||||
|
if_body.iter().map(|x| x.to_code()).join(" "),
|
||||||
|
else_body.iter().map(|x| x.to_code()).join(" ")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Node::Word(x) => x.to_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Body = Vec<Node>;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct AST {
|
||||||
|
pub body: Body,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCode for AST {
|
||||||
|
fn to_code(&self) -> String {
|
||||||
|
self.body.iter().map(|x| x.to_code()).join(" ")
|
||||||
|
}
|
||||||
|
}
|
1
presentation/.gitignore
vendored
Normal file
1
presentation/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
dist/
|
30
presentation/Makefile
Normal file
30
presentation/Makefile
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# Author: Daniel Nicolas Gisolfi
|
||||||
|
# Date: 2020-8-11
|
||||||
|
|
||||||
|
source=$(shell find . -type f -name "*.md")
|
||||||
|
title=$(shell grep -m 1 title $(source) | cut -d ':' -f2 | xargs)
|
||||||
|
version=$(shell grep -m 1 version $(source) | cut -d ':' -f2 | xargs)
|
||||||
|
theme=$(shell find . -type f -name "*.css")
|
||||||
|
flags=--allow-local-files --theme $(theme)
|
||||||
|
devflags=--server --watch --allow-local-files
|
||||||
|
dist="./dist"
|
||||||
|
|
||||||
|
.PHONY: pptx
|
||||||
|
pptx:
|
||||||
|
marp $(flags) "$(source)" -o "$(dist)/$(title)-$(version).pptx"
|
||||||
|
|
||||||
|
.PHONY: pdf
|
||||||
|
pdf:
|
||||||
|
marp $(flags) "$(source)" -o "$(dist)/$(title)-$(version).pdf"
|
||||||
|
|
||||||
|
.PHONY: html
|
||||||
|
html:
|
||||||
|
marp $(flags) "$(source)" -o "$(dist)/$(title)-$(version).html"
|
||||||
|
|
||||||
|
.PHONY: png
|
||||||
|
png:
|
||||||
|
marp $(flags) --images png "$(source)"
|
||||||
|
|
||||||
|
.PHONY: dev
|
||||||
|
dev:
|
||||||
|
marp $(devflags) .
|
BIN
presentation/assets/assembler.png
Normal file
BIN
presentation/assets/assembler.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 65 KiB |
BIN
presentation/assets/brian-kostiuk-S4jSvcHYcOs-unsplash.jpg
Normal file
BIN
presentation/assets/brian-kostiuk-S4jSvcHYcOs-unsplash.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.3 MiB |
BIN
presentation/assets/harrison-broadbent-VOz0gV9HC0I-unsplash.jpg
Normal file
BIN
presentation/assets/harrison-broadbent-VOz0gV9HC0I-unsplash.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 996 KiB |
BIN
presentation/assets/was-ist-cpu-darstellung.png
Normal file
BIN
presentation/assets/was-ist-cpu-darstellung.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.4 KiB |
330
presentation/dracula.css
Normal file
330
presentation/dracula.css
Normal file
|
@ -0,0 +1,330 @@
|
||||||
|
@charset "UTF-8";
|
||||||
|
/*!
|
||||||
|
* Marp Dracula theme.
|
||||||
|
* @theme dracula
|
||||||
|
* @author Daniel Nicolas Gisolfi
|
||||||
|
*
|
||||||
|
* @auto-scaling true
|
||||||
|
* @size 4:3 960px 720px
|
||||||
|
* @size 16:9 1280px 720px
|
||||||
|
*/
|
||||||
|
|
||||||
|
@import url("https://fonts.googleapis.com/css?family=Lato:400,900|IBM+Plex+Sans:400,700");
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--dracula-background: #282a36;
|
||||||
|
--dracula-current-line: #44475a;
|
||||||
|
--dracula-foreground: #f8f8f2;
|
||||||
|
--dracula-comment: #6272a4;
|
||||||
|
--dracula-cyan: #8be9fd;
|
||||||
|
--dracula-green: #50fa7b;
|
||||||
|
--dracula-orange: #ffb86c;
|
||||||
|
--dracula-pink: #ff79c6;
|
||||||
|
--dracula-purple: #bd93f9;
|
||||||
|
--dracula-red: #ff5555;
|
||||||
|
--dracula-yellow: #f1fa8c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs {
|
||||||
|
display: block;
|
||||||
|
overflow-x: auto;
|
||||||
|
padding: 0.5em;
|
||||||
|
background: var(--dracula-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dracula Foreground */
|
||||||
|
.hljs,
|
||||||
|
.hljs-subst,
|
||||||
|
.hljs-typing,
|
||||||
|
.hljs-variable,
|
||||||
|
.hljs-template-variable {
|
||||||
|
color: var(--dracula-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dracula Comment */
|
||||||
|
.hljs-comment,
|
||||||
|
.hljs-quote,
|
||||||
|
.hljs-deletion {
|
||||||
|
color: var(--dracula-comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dracula Cyan */
|
||||||
|
.hljs-meta .hljs-doctag,
|
||||||
|
.hljs-built_in,
|
||||||
|
.hljs-selector-tag,
|
||||||
|
.hljs-section,
|
||||||
|
.hljs-link,
|
||||||
|
.hljs-class {
|
||||||
|
color: var(--dracula-cyan);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dracula Green */
|
||||||
|
.hljs-title {
|
||||||
|
color: var(--dracula-green);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dracula Orange */
|
||||||
|
.hljs-params {
|
||||||
|
color: var(--dracula-orange);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dracula Pink */
|
||||||
|
.hljs-keyword {
|
||||||
|
color: var(--dracula-pink);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dracula Purple */
|
||||||
|
.hljs-literal,
|
||||||
|
.hljs-number {
|
||||||
|
color: var(--dracula-purple);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dracula Red */
|
||||||
|
.hljs-regexp {
|
||||||
|
color: var(--dracula-red);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dracula Yellow */
|
||||||
|
.hljs-string,
|
||||||
|
.hljs-name,
|
||||||
|
.hljs-type,
|
||||||
|
.hljs-attr,
|
||||||
|
.hljs-symbol,
|
||||||
|
.hljs-bullet,
|
||||||
|
.hljs-addition,
|
||||||
|
.hljs-template-tag {
|
||||||
|
color: var(--dracula-yellow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-keyword,
|
||||||
|
.hljs-selector-tag,
|
||||||
|
.hljs-literal,
|
||||||
|
.hljs-title,
|
||||||
|
.hljs-section,
|
||||||
|
.hljs-doctag,
|
||||||
|
.hljs-type,
|
||||||
|
.hljs-name,
|
||||||
|
.hljs-strong {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-params,
|
||||||
|
.hljs-emphasis {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg[data-marp-fitting="svg"] {
|
||||||
|
max-height: 580px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
margin: 0.5em 0 0 0;
|
||||||
|
color: var(--dracula-pink);
|
||||||
|
}
|
||||||
|
h1 strong,
|
||||||
|
h2 strong,
|
||||||
|
h3 strong,
|
||||||
|
h4 strong,
|
||||||
|
h5 strong,
|
||||||
|
h6 strong {
|
||||||
|
font-weight: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 1.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h6 {
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
p,
|
||||||
|
blockquote {
|
||||||
|
margin: 1em 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul > li,
|
||||||
|
ol > li {
|
||||||
|
margin: 0.3em 0 0 0;
|
||||||
|
color: var(--dracula-cyan);
|
||||||
|
}
|
||||||
|
ul > li > p,
|
||||||
|
ol > li > p {
|
||||||
|
margin: 0.6em 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
display: inline-block;
|
||||||
|
font-family: "IBM Plex Mono", monospace;
|
||||||
|
font-size: 0.8em;
|
||||||
|
letter-spacing: 0;
|
||||||
|
margin: -0.1em 0.15em;
|
||||||
|
padding: 0.1em 0.2em;
|
||||||
|
vertical-align: baseline;
|
||||||
|
color: var(--dracula-green);
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
display: block;
|
||||||
|
margin: 1em 0 0 0;
|
||||||
|
min-height: 1em;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
pre code {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
min-width: 100%;
|
||||||
|
padding: 0.5em;
|
||||||
|
font-size: 0.7em;
|
||||||
|
}
|
||||||
|
pre code svg[data-marp-fitting="svg"] {
|
||||||
|
max-height: calc(580px - 1em);
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
margin: 1em 0 0 0;
|
||||||
|
padding: 0 1em;
|
||||||
|
position: relative;
|
||||||
|
color: var(--dracula-orange);
|
||||||
|
}
|
||||||
|
blockquote::after,
|
||||||
|
blockquote::before {
|
||||||
|
content: "“";
|
||||||
|
display: block;
|
||||||
|
font-family: "Times New Roman", serif;
|
||||||
|
font-weight: bold;
|
||||||
|
position: absolute;
|
||||||
|
color: var(--dracula-green);
|
||||||
|
}
|
||||||
|
blockquote::before {
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
blockquote::after {
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
blockquote > *:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
mark {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-spacing: 0;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 1em 0 0 0;
|
||||||
|
}
|
||||||
|
table th,
|
||||||
|
table td {
|
||||||
|
padding: 0.2em 0.4em;
|
||||||
|
border-width: 1px;
|
||||||
|
border-style: solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
section {
|
||||||
|
font-size: 35px;
|
||||||
|
font-family: "IBM Plex Sans";
|
||||||
|
line-height: 1.35;
|
||||||
|
letter-spacing: 1.25px;
|
||||||
|
padding: 70px;
|
||||||
|
color: var(--dracula-foreground);
|
||||||
|
background-color: var(--dracula-background);
|
||||||
|
}
|
||||||
|
section > *:first-child,
|
||||||
|
section > header:first-child + * {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
section a,
|
||||||
|
section mark {
|
||||||
|
color: var(--dracula-red);
|
||||||
|
}
|
||||||
|
section code {
|
||||||
|
background: var(--dracula-current-line);
|
||||||
|
color: var(--dracula-current-green);
|
||||||
|
}
|
||||||
|
section h1 strong,
|
||||||
|
section h2 strong,
|
||||||
|
section h3 strong,
|
||||||
|
section h4 strong,
|
||||||
|
section h5 strong,
|
||||||
|
section h6 strong {
|
||||||
|
color: var(--dracula-current-line);
|
||||||
|
}
|
||||||
|
section pre > code {
|
||||||
|
background: var(--dracula-current-line);
|
||||||
|
}
|
||||||
|
section header,
|
||||||
|
section footer,
|
||||||
|
section section::after,
|
||||||
|
section blockquote::before,
|
||||||
|
section blockquote::after {
|
||||||
|
color: var(--dracula-comment);
|
||||||
|
}
|
||||||
|
section table th,
|
||||||
|
section table td {
|
||||||
|
border-color: var(--dracula-current-line);
|
||||||
|
}
|
||||||
|
section table thead th {
|
||||||
|
background: var(--dracula-current-line);
|
||||||
|
color: var(--dracula-yellow);
|
||||||
|
}
|
||||||
|
section table tbody > tr:nth-child(even) td,
|
||||||
|
section table tbody > tr:nth-child(even) th {
|
||||||
|
background: var(--dracula-current-line);
|
||||||
|
}
|
||||||
|
|
||||||
|
header,
|
||||||
|
footer,
|
||||||
|
section::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 66%;
|
||||||
|
height: 70px;
|
||||||
|
line-height: 50px;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 10px 25px;
|
||||||
|
position: absolute;
|
||||||
|
color: var(--dracula-comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
section::after {
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
101
presentation/presentation.md
Normal file
101
presentation/presentation.md
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
---
|
||||||
|
title: Hence
|
||||||
|
version: 1.0.0
|
||||||
|
theme: dracula
|
||||||
|
footer: Grimm
|
||||||
|
header: Hence
|
||||||
|
marp: true
|
||||||
|
size: 4K
|
||||||
|
---
|
||||||
|
|
||||||
|
# Hence
|
||||||
|
|
||||||
|
Ein Ausflug in die Welt einer virtuellen CPU
|
||||||
|
|
||||||
|
_Dominic Grimm_
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
h1 {
|
||||||
|
padding-top: 1.5em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
![bg left](./assets/brian-kostiuk-S4jSvcHYcOs-unsplash.jpg)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Inhalt
|
||||||
|
|
||||||
|
- Was ist eine CPU?
|
||||||
|
- Überblick Assembler
|
||||||
|
- Hence
|
||||||
|
- (Hence-)Forth
|
||||||
|
|
||||||
|
=> _Workshop_
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Was ist eine CPU?
|
||||||
|
|
||||||
|
- Herzstück eines Computers
|
||||||
|
- Kleiner Chip auf Hauptplatine
|
||||||
|
- Verarbeitung Arithmetik und Logik
|
||||||
|
- Zugriff auf Speicher und Peripherie
|
||||||
|
|
||||||
|
![bg right](./assets/harrison-broadbent-VOz0gV9HC0I-unsplash.jpg)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Aufbau
|
||||||
|
|
||||||
|
![bg right:35% fit](./assets/was-ist-cpu-darstellung.png)
|
||||||
|
|
||||||
|
- _Steuerwerk, Rechenwerk, Cache, Register_
|
||||||
|
- Cache: schneller Speicher für oft benötigte Daten
|
||||||
|
- CPU kommuniziert über Bus-Systeme
|
||||||
|
- Leistung abhängig von Kernzahl, Taktfrequenz, Cache und Architektur
|
||||||
|
- Neueste Entwicklungen: Multi-Core-Prozessoren und KI
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Machine code
|
||||||
|
|
||||||
|
- Niedrigste Ebene der Programmierung.
|
||||||
|
- Binäre Instruktionen, direkt von der CPU ausgeführt
|
||||||
|
- Jede CPU-Architektur hat ihre eigene Maschinensprache, die spezifisch für diese Architektur ist
|
||||||
|
|
||||||
|
- Sehr effizient, da direkt von CPU ausgeführt
|
||||||
|
- Meist in höherer Programmiersprache geschrieben
|
||||||
|
|
||||||
|
=> *Assembler*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Beispiel
|
||||||
|
|
||||||
|
*hexadezimal kodiert (`hexdump`)*
|
||||||
|
|
||||||
|
```
|
||||||
|
00000000 03 7f fc 86 03 a0 00 06 00 06 03 c0 02 8b 06 00 |................|
|
||||||
|
00000010 05 03 c0 01 8c 04 00 06 8c 03 00 01 06 00 05 03 |................|
|
||||||
|
*
|
||||||
|
```
|
||||||
|
|
||||||
|
| Code | Opcode | Argument |
|
||||||
|
| ---------- | -------------- | ---------------- |
|
||||||
|
| `03 7f fc` | `0x03` (`ts`) | `0x7ffc` (32764) |
|
||||||
|
| `86` | `0x06` (`tlr`) | |
|
||||||
|
| `03 a0 00` | `0x03` (`ts`) | `0xa000` (40960) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Überblick Assembler
|
||||||
|
|
||||||
|
![width:30cm](./assets/assembler.png)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Hence
|
||||||
|
|
||||||
|
- Virtuelle "CPU" (eher SBC)
|
||||||
|
-
|
Loading…
Reference in a new issue