add "swap" instr, document buffer ops
This commit is contained in:
@@ -76,8 +76,8 @@ These keywords (among others) are used in conditional branches to specify flag t
|
|||||||
- `nneg` … NonNegative,
|
- `nneg` … NonNegative,
|
||||||
- `c` … Carry,
|
- `c` … Carry,
|
||||||
- `nc` … NotCarry,
|
- `nc` … NotCarry,
|
||||||
- `valid` … Valid,
|
- `val`, `valid`, `ok` … Valid,
|
||||||
- `inval` … Invalid,
|
- `inval`, `nok` … Invalid,
|
||||||
- `ov` … Overflow,
|
- `ov` … Overflow,
|
||||||
- `nov` … NotOverflow,
|
- `nov` … NotOverflow,
|
||||||
|
|
||||||
@@ -127,19 +127,50 @@ Instructions are written like this:
|
|||||||
|
|
||||||
### Conditional instructions
|
### Conditional instructions
|
||||||
|
|
||||||
All instructions can be made conditional by appending `.<cond>` to the keyword, i.e. `j.ne` means "jump if not equal".
|
All instructions can be made conditional by appending `.<cond>` to the keyword, i.e. `(j.ne :LABEL)` means "jump if not equal".
|
||||||
This is used internally by the assembler when translating conditional branches to executable code.
|
These modifiers are mainly used by the assembler when translating conditional branches to executable code.
|
||||||
|
|
||||||
|
Note that the flags can only be tested immediately after the instruction that produced them, or after instructions that do not
|
||||||
|
affect flags (pseudo-instructions like `def` and `sym`, `nop`, `j`, `fj`, `s`, `call` etc). Instructions that can set flags first
|
||||||
|
clear all flags to make the result predictable.
|
||||||
|
|
||||||
|
Status flags can be saved to and restored from a register using the `stf` and `ldf` instructions. This can also be used to set
|
||||||
|
or test flags manually, but the binary format may change
|
||||||
|
|
||||||
### Instruction arguments
|
### Instruction arguments
|
||||||
|
|
||||||
Args are either:
|
Arguments are always ordered writes-first, reads-last.
|
||||||
- One of the registers (`reg0`, `arg3` etc)
|
|
||||||
- Names of constants defined earlier in the program (e.g. `SCREEN_WIDTH`)
|
This document uses the following notation for arguments:
|
||||||
- Symbols defined as register aliases (e.g. `x`)
|
- `REG` - one of the registers (`regX`, `argX`, `resX`)
|
||||||
- The "discard register" `_` to discard an output value. That is used when you only care about side effects or status flags.
|
- `SYM` - a symbol defined as a register alias (e.g. `(sym x r0)`)
|
||||||
- Literal values (decimal, hex or binary)
|
- `@REG` / `@SYM` - access an object referenced by a handle. Handle is simply a numeric value stored in a register of some kind.
|
||||||
- Label or routine name (e.g. `factorial`, `:again`)
|
- `_` - a special "register" that discards anything written to it.
|
||||||
- ...or anything else an installed crsn extension supports
|
The "discard register" is used when you do not need the value and only care about side effects or status flags.
|
||||||
|
- `CONST` - name of a constant defined earlier in the program (e.g. `(def SCREEN_WIDTH 640)`)
|
||||||
|
- `NUM` - literal values
|
||||||
|
- unsigned `123`
|
||||||
|
- signed `-123`
|
||||||
|
- float `-45.6789`
|
||||||
|
- hex `0xabcd`, `#abcd`
|
||||||
|
- binary `0b0101`
|
||||||
|
- character `'a'`, `'🐁'`. Supports unicode and C-style escapes. Use `\\` for a literal backslash.
|
||||||
|
- `"str"` - a double-quoted string (`"ahoj\n"`). Supports unicode and C-style escapes. Use `\\` for a literal backslash.
|
||||||
|
- `:LABEL` - label name
|
||||||
|
- `PROC` - routine name
|
||||||
|
- `PROC/A` - routine name with arity (number of arguments)
|
||||||
|
|
||||||
|
The different ways to specify a value can be grouped as "reads" and "writes":
|
||||||
|
|
||||||
|
- `Rd` - read: `REG`, `SYM`, `@REG`, `@SYM`, `VALUE`, `CONST`
|
||||||
|
- `Wr` - writes: `REG`, `SYM`, `@REG`, `@SYM`, `_`
|
||||||
|
- `RW` - intersection of the two sets, capable of reading and writing: `REG`, `SYM`, `@REG`, `@SYM`
|
||||||
|
|
||||||
|
Objects (`@reg`, `@sym`) can be read or written as if they were a register, but only if the referenced object supports it.
|
||||||
|
Other objects may produce a runtime fault or set the INVALID flag.
|
||||||
|
|
||||||
|
In the instruction lists below, I will use the symbols `Rd` for reads, `Wr` for writes, `RW` for read-writes, and `@Obj` for object handles,
|
||||||
|
with optional description after a colon, such as: `(add Wr:dst Rd:a Rd:b)`.
|
||||||
|
|
||||||
### Conditional branches
|
### Conditional branches
|
||||||
|
|
||||||
@@ -236,7 +267,7 @@ Jumping to a label is always safer than a manual skip.
|
|||||||
; Mark a jump target.
|
; Mark a jump target.
|
||||||
(:LABEL)
|
(:LABEL)
|
||||||
; Numbered labels
|
; Numbered labels
|
||||||
(:#NUMBER)
|
(:#NUM)
|
||||||
|
|
||||||
; Mark a far jump target (can be jumped to from another routine).
|
; Mark a far jump target (can be jumped to from another routine).
|
||||||
; This label is preserved in optimized code.
|
; This label is preserved in optimized code.
|
||||||
@@ -249,46 +280,50 @@ Jumping to a label is always safer than a manual skip.
|
|||||||
(fj :LABEL)
|
(fj :LABEL)
|
||||||
|
|
||||||
; Skip backward or forward
|
; Skip backward or forward
|
||||||
(s COUNT)
|
(s Rd)
|
||||||
|
|
||||||
; Mark a routine entry point (call target).
|
; Mark a routine entry point (call target).
|
||||||
(routine NAME)
|
(routine PROC)
|
||||||
(routine NAME/ARITY)
|
(routine PROC/A)
|
||||||
|
|
||||||
; Call a routine with arguments.
|
; Call a routine with arguments.
|
||||||
; The arguments are passed as argX. Return values are stored in resX registers.
|
; The arguments are passed as argX. Return values are stored in resX registers.
|
||||||
(call ROUTINE ARGUMENTS...)
|
(call PROC Rd...)
|
||||||
|
|
||||||
; Exit the current routine with return values
|
; Exit the current routine with return values
|
||||||
(ret VALUES...)
|
(ret Rd...)
|
||||||
|
|
||||||
; Deny jumps, skips and run across this address, producing a run-time fault with a message.
|
; Deny jumps, skips and run across this address, producing a run-time fault with a message.
|
||||||
(barrier)
|
(barrier)
|
||||||
|
(barrier message)
|
||||||
(barrier "message text")
|
(barrier "message text")
|
||||||
|
|
||||||
; Block barriers are used for routines. They are automatically skipped in execution
|
; Block barriers are used for routines. They are automatically skipped in execution
|
||||||
; and the whole pair can be jumped *across*
|
; and the whole pair can be jumped *across*.
|
||||||
|
; The label can be a numeric or string label, its sole purpose is tying the two together. They must be unique in the program.
|
||||||
(barrier-open LABEL)
|
(barrier-open LABEL)
|
||||||
(barrier-close LABEL)
|
(barrier-close LABEL)
|
||||||
|
|
||||||
; Generate a run-time fault with a debugger message
|
; Generate a run-time fault with a debugger message
|
||||||
(fault)
|
(fault)
|
||||||
|
(fault message)
|
||||||
(fault "message text")
|
(fault "message text")
|
||||||
|
|
||||||
; Copy value
|
; Copy value
|
||||||
(ld DST SRC)
|
(ld Wr Rd)
|
||||||
|
|
||||||
; Store status flags to a register
|
; Store status flags to a register
|
||||||
(sst DST)
|
(stf Wr)
|
||||||
|
|
||||||
; Load status flags from a register
|
; Load status flags from a register
|
||||||
(sld SRC)
|
(ldf Rd)
|
||||||
|
|
||||||
; Define a register alias. The alias is only valid in the current routine or in the root of the program.
|
; Define a register alias. The alias is only valid in the current routine or in the root of the program.
|
||||||
(sym ALIAS REGISTER)
|
(sym SYM REG)
|
||||||
|
|
||||||
; Define a constant. These are valid in the whole program.
|
; Define a constant. These are valid in the whole program.
|
||||||
(def NAME VALUE)
|
; Value must be known at compile time.
|
||||||
|
(def CONST VALUE)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Arithmetic Module
|
## Arithmetic Module
|
||||||
@@ -304,101 +339,152 @@ Many instructions have two forms:
|
|||||||
(tst SRC)
|
(tst SRC)
|
||||||
|
|
||||||
; Compare two values
|
; Compare two values
|
||||||
(cmp A B)
|
(cmp Rd Rd)
|
||||||
|
|
||||||
; Add A+B
|
; Add A+B
|
||||||
(add DST A B)
|
(add Wr Rd Rd)
|
||||||
(add DST B)
|
(add RW Rd)
|
||||||
|
|
||||||
; Subtract A-B
|
; Subtract A-B
|
||||||
(sub DST A B)
|
(sub Wr Rd Rd)
|
||||||
(sub DST B)
|
(sub RW Rd)
|
||||||
|
|
||||||
; Multiply A*B
|
; Multiply A*B
|
||||||
(mul DST A B)
|
(mul Wr Rd Rd)
|
||||||
(mul DST B)
|
(mul RW Rd)
|
||||||
|
|
||||||
; Divide A/B
|
; Divide A/B
|
||||||
(div DST A B)
|
(div Wr Rd Rd:divider)
|
||||||
(div DST B)
|
(div RW Rd:divider)
|
||||||
|
|
||||||
; Divide and get remainder
|
; Divide and get remainder
|
||||||
; Both DST and REM are output registers
|
; Both DST and REM are output registers
|
||||||
(divr DST REM A B)
|
(divr Wr:result Wr:remainder Rd Rd:divider)
|
||||||
(divr DST REM B)
|
(divr RW Wr:remainder Rd:divider)
|
||||||
|
|
||||||
; Get remainder A%B
|
; Get remainder A%B
|
||||||
; This is equivalent to (divr _ REM A B),
|
; This is equivalent to (divr _ REM A B),
|
||||||
; except status flags are updated by the remainder value
|
; except status flags are updated by the remainder value
|
||||||
(mod DST A B)
|
(mod Wr Rd Rd-divider)
|
||||||
(mod DST B)
|
(mod RW Rd-divider)
|
||||||
|
|
||||||
; AND A&B
|
; AND A&B
|
||||||
(and DST A B)
|
(and Wr Rd Rd)
|
||||||
(and DST B)
|
(and RW Rd)
|
||||||
|
|
||||||
; OR A|B
|
; OR A|B
|
||||||
(or DST A B)
|
(or Wr Rd Rd)
|
||||||
(or DST B)
|
(or RW Rd)
|
||||||
|
|
||||||
; XOR A&B
|
; XOR A&B
|
||||||
(xor DST A B)
|
(xor Wr Rd Rd)
|
||||||
(xor DST B)
|
(xor RW Rd)
|
||||||
|
|
||||||
; CPL ~A (negate all bits)
|
; CPL ~A (negate all bits)
|
||||||
(cpl DST A)
|
(cpl DST A)
|
||||||
(cpl DST)
|
(cpl DST)
|
||||||
|
|
||||||
; Rotate right (wrap around)
|
; Rotate right (wrap around)
|
||||||
(ror DST A B)
|
(ror Wr Rd Rd)
|
||||||
(ror DST B)
|
(ror RW Rd)
|
||||||
|
|
||||||
; Rotate left (wrap around)
|
; Rotate left (wrap around)
|
||||||
(rol DST A B)
|
(rol Wr Rd:value Rd:count)
|
||||||
(rol DST B)
|
(rol RW Rd:count)
|
||||||
|
|
||||||
; Logical shift right (fill with zeros)
|
; Logical shift right (fill with zeros)
|
||||||
(lsr DST A B)
|
(lsr Wr Rd Rd:count)
|
||||||
(lsr DST B)
|
(lsr RW Rd:count)
|
||||||
|
|
||||||
; Logical shift left (fill with zeros)
|
; Logical shift left (fill with zeros)
|
||||||
(lsl DST A B)
|
(lsl Wr Rd Rd:count)
|
||||||
(lsl DST B)
|
(lsl RW Rd:count)
|
||||||
|
|
||||||
; Arithmetic shift right (copy sign bit)
|
; Arithmetic shift right (copy sign bit)
|
||||||
(asr DST A B)
|
(asr Wr Rd Rd:count)
|
||||||
(asr DST B)
|
(asr RW Rd:count)
|
||||||
|
|
||||||
; Arithmetic shift left (this is identical to `lsl`, added for completeness)
|
; Arithmetic shift left (this is identical to `lsl`, added for completeness)
|
||||||
(asl DST A B)
|
(asl Wr Rd Rd:count)
|
||||||
(asl DST B)
|
(asl RW Rd:count)
|
||||||
|
|
||||||
; Delete an object by its handle. Objects are used by some extensions.
|
; Delete an object by its handle. Objects are used by some extensions.
|
||||||
(del @REG)
|
(del @Rd)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Stacks Module
|
## Buffers Module
|
||||||
|
|
||||||
This module defines data stacks. Stacks can be shared by routines by passing a handle.
|
This module defines dynamic size integer buffers.
|
||||||
|
|
||||||
|
A buffer needs to be created using one of the init instructions:
|
||||||
|
|
||||||
```
|
```
|
||||||
; Create a stack. The register then contains the stack handle.
|
; Create an empty buffer and store its handle into a register
|
||||||
(stack REG)
|
(mkbf Wr)
|
||||||
|
|
||||||
; Push to a stack (insert to the end)
|
; Create a buffer of a certain size, filled with zeros.
|
||||||
(push @REG VALUE)
|
; COUNT may be a register or an immediate value
|
||||||
|
(mkbf Wr Rd:count)
|
||||||
|
|
||||||
; Pop from a stack (remove from the end)
|
; Create a buffer and fill it with characters from a string (unicode code points)
|
||||||
(pop DST @REG)
|
(mkbf Wr "string")
|
||||||
|
|
||||||
; Reverse push to a stack (insert to the beginning)
|
; Create a buffer and fill it with values.
|
||||||
(rpush @REG VALUE)
|
(mkbf Wr (Rd...))
|
||||||
|
|
||||||
; Reverse pop from a stack (remove from the beginning)
|
|
||||||
(rpop DST @REG)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
To delete a stack, use the `del` instruction - `(del @REG)`
|
Primitive buffer ops (position is always 0-based)
|
||||||
|
|
||||||
|
```
|
||||||
|
; Get buffer size
|
||||||
|
(bfsz Wr @Obj)
|
||||||
|
|
||||||
|
; Read from a position
|
||||||
|
(bfrd Wr @Obj Rd:index)
|
||||||
|
|
||||||
|
; Write to a position
|
||||||
|
(bfwr @Obj Rd:index Rd)
|
||||||
|
|
||||||
|
; Insert at a position, shifting the rest to the right
|
||||||
|
(bfins @Obj Rd:index Rd)
|
||||||
|
|
||||||
|
; Remove item at a position, shifting the rest to the left to fill the empty space
|
||||||
|
(bfrm Wr @Obj Rd:index)
|
||||||
|
```
|
||||||
|
|
||||||
|
Whole buffer manipulation:
|
||||||
|
|
||||||
|
```
|
||||||
|
; Resize the buffer. Removes trailing elements or inserts zero to match the new size.
|
||||||
|
(bfrsz @Obj Rd:len)
|
||||||
|
|
||||||
|
; Reverse a buffer
|
||||||
|
(bfrev @Obj)
|
||||||
|
|
||||||
|
; Append a buffer
|
||||||
|
(bfapp @Obj @Obj:other)
|
||||||
|
|
||||||
|
; Prepend a buffer
|
||||||
|
(bfprep @Obj @Obj:other)
|
||||||
|
```
|
||||||
|
|
||||||
|
Stack-style buffer ops:
|
||||||
|
|
||||||
|
```
|
||||||
|
; Push (insert at the end)
|
||||||
|
(bfpush @Obj Rd)
|
||||||
|
|
||||||
|
; Pop (remove from the end)
|
||||||
|
(bfpop Wr @Obj)
|
||||||
|
|
||||||
|
; Reverse push (insert to the beginning)
|
||||||
|
(bfrpush @Obj Rd)
|
||||||
|
|
||||||
|
; Reverse pop (remove from the beginning)
|
||||||
|
(bfrpop Wr @Obj)
|
||||||
|
```
|
||||||
|
|
||||||
|
To delete a buffer, use the `del` instruction - `(del @Obj)`
|
||||||
|
|
||||||
## Screen module
|
## Screen module
|
||||||
|
|
||||||
@@ -456,7 +542,7 @@ such as animations.
|
|||||||
|
|
||||||
## Stdio module
|
## Stdio module
|
||||||
|
|
||||||
- This module currently defines two global handles: `@stdin` and `@stdout`.
|
- This module currently defines two global handles (resp. constants): `@stdin` and `@stdout`.
|
||||||
- You can think of these handles as streams or SFRs (special function registers).
|
- You can think of these handles as streams or SFRs (special function registers).
|
||||||
To use them, simply load data to or from the handles (e.g. `(ld r0 @stdin)`).
|
To use them, simply load data to or from the handles (e.g. `(ld r0 @stdin)`).
|
||||||
- They operate over unicode code points, which are a superset of ASCII.
|
- They operate over unicode code points, which are a superset of ASCII.
|
||||||
|
|||||||
@@ -71,12 +71,12 @@ pub fn parse_cond(text: &str, pos: &SourcePosition) -> Result<Cond, CrsnError> {
|
|||||||
"nneg" | "0+" | ">=0" | "≤0" => Cond::NonNegative,
|
"nneg" | "0+" | ">=0" | "≤0" => Cond::NonNegative,
|
||||||
"c" => Cond::Carry,
|
"c" => Cond::Carry,
|
||||||
"nc" => Cond::NotCarry,
|
"nc" => Cond::NotCarry,
|
||||||
"em" => Cond::Empty,
|
"em" | "empty" => Cond::Empty,
|
||||||
"nem" => Cond::NotEmpty,
|
"nem" | "nempty" => Cond::NotEmpty,
|
||||||
"f" => Cond::Full,
|
"f" | "full" => Cond::Full,
|
||||||
"nf" => Cond::NotFull,
|
"nf" | "nfull" => Cond::NotFull,
|
||||||
"valid" => Cond::Valid,
|
"ok" | "val" | "valid" => Cond::Valid,
|
||||||
"inval" => Cond::Invalid,
|
"inval" | "invalid" | "nval" | "nok" => Cond::Invalid,
|
||||||
"ov" => Cond::Overflow,
|
"ov" => Cond::Overflow,
|
||||||
"nov" => Cond::NotOverflow,
|
"nov" => Cond::NotOverflow,
|
||||||
_ => {
|
_ => {
|
||||||
|
|||||||
@@ -71,7 +71,9 @@ pub enum BuiltinOp {
|
|||||||
/// The object is released and the handle becomes invalid.
|
/// The object is released and the handle becomes invalid.
|
||||||
Delete(RdObj),
|
Delete(RdObj),
|
||||||
/// Copy value
|
/// Copy value
|
||||||
Move { dst: Wr, src: Rd },
|
MoveValue { dst: Wr, src: Rd },
|
||||||
|
/// Swap two registers
|
||||||
|
SwapValues { a: Wr, b: Wr },
|
||||||
/// Store runtime status to a register
|
/// Store runtime status to a register
|
||||||
StoreFlags { dst: Wr },
|
StoreFlags { dst: Wr },
|
||||||
/// Load runtime status from a register
|
/// Load runtime status from a register
|
||||||
|
|||||||
@@ -103,12 +103,18 @@ impl OpTrait for BuiltinOp {
|
|||||||
let pc = state.get_pc();
|
let pc = state.get_pc();
|
||||||
program.validate_jump(pc, Addr((pc.0 as i64 + res.advance) as u64))?;
|
program.validate_jump(pc, Addr((pc.0 as i64 + res.advance) as u64))?;
|
||||||
}
|
}
|
||||||
BuiltinOp::Move { dst, src } => {
|
BuiltinOp::MoveValue { dst, src } => {
|
||||||
state.clear_status();
|
state.clear_status();
|
||||||
let val = state.read(*src)?;
|
let val = state.read(*src)?;
|
||||||
state.update_status(val);
|
state.update_status(val);
|
||||||
state.write(*dst, val)?;
|
state.write(*dst, val)?;
|
||||||
}
|
}
|
||||||
|
BuiltinOp::SwapValues { a, b } => {
|
||||||
|
let aa = state.read(a.as_rd())?;
|
||||||
|
let bb = state.read(b.as_rd())?;
|
||||||
|
state.write(*a, bb)?;
|
||||||
|
state.write(*b, aa)?;
|
||||||
|
}
|
||||||
BuiltinOp::StoreFlags { dst } => {
|
BuiltinOp::StoreFlags { dst } => {
|
||||||
let packed = state.frame.status.store();
|
let packed = state.frame.status.store();
|
||||||
state.write(*dst, packed)?;
|
state.write(*dst, packed)?;
|
||||||
|
|||||||
@@ -177,12 +177,19 @@ pub(crate) fn parse_op<'a>(op_pos: &SourcePosition, keyword: &str, mut args: Tok
|
|||||||
}
|
}
|
||||||
|
|
||||||
"ld" => {
|
"ld" => {
|
||||||
BuiltinOp::Move {
|
BuiltinOp::MoveValue {
|
||||||
dst: args.next_wr()?,
|
dst: args.next_wr()?,
|
||||||
src: args.next_rd()?,
|
src: args.next_rd()?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"swap" => {
|
||||||
|
BuiltinOp::SwapValues {
|
||||||
|
a: args.next_wr()?,
|
||||||
|
b: args.next_wr()?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
"stf" => {
|
"stf" => {
|
||||||
BuiltinOp::StoreFlags {
|
BuiltinOp::StoreFlags {
|
||||||
dst: args.next_wr()?,
|
dst: args.next_wr()?,
|
||||||
@@ -295,7 +302,8 @@ pub(crate) fn to_sexp(op: &BuiltinOp) -> Sexp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
BuiltinOp::Delete(obj) => sexp::list(&[A("del"), A(obj)]),
|
BuiltinOp::Delete(obj) => sexp::list(&[A("del"), A(obj)]),
|
||||||
BuiltinOp::Move { dst, src } => sexp::list(&[A("ld"), A(dst), A(src)]),
|
BuiltinOp::MoveValue { dst, src } => sexp::list(&[A("ld"), A(dst), A(src)]),
|
||||||
|
BuiltinOp::SwapValues { a, b } => sexp::list(&[A("swp"), A(a), A(b)]),
|
||||||
BuiltinOp::StoreFlags { dst } => sexp::list(&[A("stf"), A(dst)]),
|
BuiltinOp::StoreFlags { dst } => sexp::list(&[A("stf"), A(dst)]),
|
||||||
BuiltinOp::LoadFlags { src } => sexp::list(&[A("ldf"), A(src)])
|
BuiltinOp::LoadFlags { src } => sexp::list(&[A("ldf"), A(src)])
|
||||||
}
|
}
|
||||||
@@ -364,6 +372,7 @@ mod test {
|
|||||||
("(fault)", "(fault)"),
|
("(fault)", "(fault)"),
|
||||||
("(fault kur*a)", "(fault kur*a)"),
|
("(fault kur*a)", "(fault kur*a)"),
|
||||||
("(fault \"do pr*ele\")", "(fault \"do pr*ele\")"),
|
("(fault \"do pr*ele\")", "(fault \"do pr*ele\")"),
|
||||||
|
("(swap r0 r1)", "(swap r0 r1)"),
|
||||||
("(ld r0 r0)", "(ld r0 r0)"),
|
("(ld r0 r0)", "(ld r0 r0)"),
|
||||||
("(ld r0 156)", "(ld r0 156)"),
|
("(ld r0 156)", "(ld r0 156)"),
|
||||||
("(ld _ -32767)", "(ld _ -32767)"),
|
("(ld _ -32767)", "(ld _ -32767)"),
|
||||||
|
|||||||
+17
-25
@@ -12,40 +12,32 @@ pub(crate) fn parse<'a>(_pos: &SourcePosition, keyword: &str, mut args: TokenPar
|
|||||||
Ok(ParseRes::ext(match keyword {
|
Ok(ParseRes::ext(match keyword {
|
||||||
"mkbf" => {
|
"mkbf" => {
|
||||||
let dst = args.next_wr()?;
|
let dst = args.next_wr()?;
|
||||||
let len = if args.have_more() {
|
|
||||||
args.next_rd()?
|
|
||||||
} else {
|
|
||||||
Rd::immediate(0)
|
|
||||||
};
|
|
||||||
BufOps::New { dst, value: BufValue::Zeros(len) }
|
|
||||||
}
|
|
||||||
|
|
||||||
"mkbfv" => {
|
let value = match args.next() {
|
||||||
let dst = args.next_wr()?;
|
None => {
|
||||||
|
BufValue::Zeros(Rd::immediate(0))
|
||||||
let next = args.next_or_err()?;
|
|
||||||
|
|
||||||
match next {
|
|
||||||
Sexp::Atom(Atom::QS(s), _) => {
|
|
||||||
BufOps::New {
|
|
||||||
dst,
|
|
||||||
value: BufValue::Chars(s),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Sexp::List(list, _) => {
|
Some(tok @ Sexp::Atom(Atom::S(_), _)) |
|
||||||
|
Some(tok @ Sexp::Atom(Atom::I(_), _)) |
|
||||||
|
Some(tok @ Sexp::Atom(Atom::U(_), _)) => {
|
||||||
|
BufValue::Zeros(parse_rd(tok, args.pcx)?)
|
||||||
|
}
|
||||||
|
Some(Sexp::Atom(Atom::QS(s), _)) => {
|
||||||
|
BufValue::Chars(s)
|
||||||
|
}
|
||||||
|
Some(Sexp::List(list, _)) => {
|
||||||
let mut vals = vec![];
|
let mut vals = vec![];
|
||||||
for v in list {
|
for v in list {
|
||||||
vals.push(parse_rd(v, args.pcx)?);
|
vals.push(parse_rd(v, args.pcx)?);
|
||||||
}
|
}
|
||||||
BufOps::New {
|
BufValue::Values(vals)
|
||||||
dst,
|
|
||||||
value: BufValue::Values(vals),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
other => {
|
Some(other) => {
|
||||||
return Err(CrsnError::Parse("Expected quoted string or a tuple of values".into(), other.pos().clone()));
|
return Err(CrsnError::Parse("Expected quoted string or a tuple of values".into(), other.pos().clone()));
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
BufOps::New { dst, value }
|
||||||
}
|
}
|
||||||
|
|
||||||
"bfrd" => {
|
"bfrd" => {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
(
|
(
|
||||||
(ld r0 65)
|
(ld r0 65)
|
||||||
(sym buf r7)
|
(sym buf r7)
|
||||||
(mkbfv buf (r0 66 67 68 '\n'))
|
(mkbf buf (r0 66 67 68 '\n'))
|
||||||
|
|
||||||
(bfrpop @stdout @buf)
|
(bfrpop @stdout @buf)
|
||||||
(s.nem -1)
|
(s.nem -1)
|
||||||
|
|||||||
+2
-2
@@ -4,9 +4,9 @@
|
|||||||
(def T_HELLO 0)
|
(def T_HELLO 0)
|
||||||
(def T_UNK 1)
|
(def T_UNK 1)
|
||||||
(mkbf TXT)
|
(mkbf TXT)
|
||||||
(mkbfv r0 "*** Type ascii to uppercase. Press q to quit. ***\n")
|
(mkbf r0 "*** Type ascii to uppercase. Press q to quit. ***\n")
|
||||||
(bfins @TXT T_HELLO r0)
|
(bfins @TXT T_HELLO r0)
|
||||||
(mkbfv r0 "🐈")
|
(mkbf r0 "🐈")
|
||||||
(bfins @TXT T_UNK r0)
|
(bfins @TXT T_UNK r0)
|
||||||
|
|
||||||
; Print string from the table
|
; Print string from the table
|
||||||
|
|||||||
Reference in New Issue
Block a user