# CROISSANT VIRTUAL MACHINE Croissant (or Crsn for short) is an extensible runtime emulating a weird microcomputer. ## FAQ ### What is this for? F U N #### What if I don't enjoy writing assembly that looks like Lisp? maybe go play fortnite instead # Architecture ## Registers - 8 general purpose registers `reg0`-`reg7` - 8 argument registers `arg0`-`arg7` - 8 result registers `res0`-`res7` All registers are 64-bit unsigned integers that can be treated as signed, if you want to. Overflow is allowed and reported by status flags. 8-, 16-, and 32-bit arithmetic is not currently implemented (only 64-bit), but will be added later. Probably. Maybe. ## Status Flags Arithmetic and other operations set status flags that can be used for conditional jumps. - Equal … Values are equal - Lower … A < B - Greater … A > B - Zero … Value is zero, buffer is empty, etc. - Positive … Value is positive - Negative … Value is negative - Overflow … Arithmetic overflow or underflow, buffer underflow, etc. - Invalid … Invalid arguments for an instruction - Carry … Arithmetic carry *this is not currently used for anything* ### Status Tests These keywords (among others) are used in conditional branches to specify flag tests: - `eq` … Equal, - `ne` … NotEqual, - `z` … Zero, - `nz` … NotZero, - `lt` … Lower, - `le` … LowerOrEqual, - `gt` … Greater, - `ge` … GreaterOrEqual, - `pos` … Positive, - `neg` … Negative, - `npos` … NonPositive, - `nneg` … NonNegative, - `c` … Carry, - `nc` … NotCarry, - `valid` … Valid, - `inval` … Invalid, - `ov` … Overflow, - `nov` … NotOverflow, # Syntax *The syntax is very much subject to change at the moment. The format described here is valid at the time this file is added to version control.* Instructions are written using S-expressions, because they are easy to parse and everyone loves Lisp. A program has this format: ``` ( (RoutineName Instructions…) … ) ``` Instructions are written like this: ``` (Keyword Args… ConditionalBranches…) ``` Args are either: - one of the registers (`reg0`, `arg3` etc) - `_` to discard an output value - a literal value (decimal, hex or binary) - label or routine name - condition flag keyword - anything else an extension supports... Conditonal branches are written like this: ``` (Condition? Instructions…) ``` If there is more than one conditional branch chained to an instruction, then only one branch is taken - there is no fall-through. The definition order is preserved, i.e. if the `inval` flag is to be checked, it should be done before checking e.g. `nz`, which is, incidentally, true by default, because all flags start cleared. Example routine to calculate the factorial of `arg0`: ``` (fac (cmp arg0 2 (eq? (ret 2))) (sub r0 arg0 1) (call fac r0) (mul r0 arg0 res0) (ret r0) ) ``` # Instruction Set Crsn instruction set is composed of extensions. Extensions can also define special syntax for their instructions, so long as it's valid S-expressions. ## Labels, jumps and barriers These are defined as part of the built-in instruction set (see below). - Barrier - marks the boundary between routines to prevent overrun. Cannot be jumped across. - Local labels - can be jumped to within the same routine, both forward and backward. - Far labels - can be jumped to from any place in the code using a far jump (disregarding barriers). This is a very cursed functionality that may or may not have some valid use case. - Skips - cannot cross a barrier, similar to a local label but without explicitly defining a label. Skipping across conditional branches may have *surprising results* - conditional branches are expanded to series of simple and conditional skips by the assembler. Only use skips if you really know what you're doing. Jumping to a label is always a safer choice. ## Built-in Instructions ``` ; Do nothing (nop) ; Stop execution (halt) ; Mark a jump target. (:LABEL) ; Mark a far jump target (can be jumped to from another routine). ; This label is preserved in optimized code. (far :LABEL) ; Jump to a label (j :LABEL) ; Jump to a label if a flag is set (jif COND :LABEL) ; Jump to a label that can be in another function (fj :LABEL) ; Far jump to a label if a flag is set (fjif COND :LABEL) ; Skip backward or forward (s COUNT) ; Skip if a flag is set (sif COND COUNT) ; Mark a routine entry point (call target). (routine NAME) ; Call a routine with arguments. ; The arguments are passed as argX. Return values are stored in resX registers. (call ROUTINE ARGS…) ; Exit the current routine with return values (ret VALS…) ; Deny jumps, skips and run across this address, producing a run-time fault with a message. (barrier) (barrier "message text") ; Generate a run-time fault with a debugger message (fault) (fault "message text") ; Copy value (ld DST SRC) ; Store status flags to a register (sst DST) ; Load status flags from a register (sld SRC) ``` ## Arithmetic Module This module makes heavy use of status flags. Many instructions have two forms: - 3 args ... explicit source and destination - 2 args ... destination is also used as the first argument ``` ; Test properties of a value - zero, positive, negative (tst SRC) ; Compare two values (cmp A B) ; Add A+B (add DST A B) (add DST B) ; Subtract A-B (sub DST A B) (sub DST B) ; Multiply A*B (mul DST A B) (mul DST B) ; Divide A/B (div DST A B) (div DST B) ; Divide and get remainder ; Both DST and REM are output registers (divr DST REM A B) (divr DST REM B) ; Get remainder A%B ; This is equivalent to (divr _ REM A B), ; except status flags are updated by the remainder value (mod DST A B) (mod DST B) ; AND A&B (and DST A B) (and DST B) ; OR A|B (or DST A B) (or DST B) ; XOR A&B (xor DST A B) (xor DST B) ; CPL ~A (negate all bits) (cpl DST A) (cpl DST) ; Rotate right (wrap around) (ror DST A B) (ror DST B) ; Rotate left (wrap around) (rol DST A B) (rol DST B) ; Logical shift right (fill with zeros) (lsr DST A B) (lsr DST B) ; Logical shift left (fill with zeros) (lsl DST A B) (lsl DST B) ; Arithmetic shift right (copy sign bit) (asr DST A B) (asr DST B) ; Arithmetic shift left (this is identical to `lsl`, added for completeness) (asl DST A B) (asl DST B) ```