BearSSL/T0/kern.t0

310 lines
7.5 KiB
Plaintext

: \ `\n parse drop ; immediate
\ This file defines the core non-native functions (mainly used for
\ parsing words, i.e. not part of the generated output). The line above
\ defines the syntax for comments.
\ Define parenthesis comments.
\ : ( `) parse drop ; immediate
: else postpone ahead 1 cs-roll postpone then ; immediate
: while postpone if 1 cs-roll ; immediate
: repeat postpone again postpone then ; immediate
: ['] ' ; immediate
: [compile] compile ; immediate
: 2drop drop drop ;
: dup2 over over ;
\ Local variables are defined with the native word '(local)'. We define
\ a helper construction that mimics what is found in Apple's Open Firmware
\ implementation. The syntax is: { a b ... ; c d ... }
\ I.e. there is an opening brace, then some names. Names appearing before
\ the semicolon are locals that are both defined and then filled with the
\ values on stack (in stack order: { a b } fills 'b' with the top-of-stack,
\ and 'a' with the value immediately below). Names appearing after the
\ semicolon are not initialized.
: __deflocal ( from_stack name -- )
dup (local) swap if
compile-local-write
else
drop
then ;
: __deflocals ( from_stack -- )
next-word
dup "}" eqstr if
2drop ret
then
dup ";" eqstr if
2drop 0 __deflocals ret
then
over __deflocals
__deflocal ;
: {
-1 __deflocals ; immediate
\ Data building words.
: data:
new-data-block next-word define-data-word ;
: hexb|
0 0 { acc z }
begin
char
dup `| = if
z if "Truncated hexadecimal byte" puts cr exitvm then
ret
then
dup 0x20 > if
hexval
z if acc 4 << + data-add8 else >acc then
z not >z
then
again ;
\ Convert hexadecimal character to number. Complain loudly if conversion
\ is not possible.
: hexval ( char -- x )
hexval-nf dup 0 < if "Not an hex digit: " puts . cr exitvm then ;
\ Convert hexadecimal character to number. If not an hexadecimal digit,
\ return -1.
: hexval-nf ( char -- x )
dup dup `0 >= swap `9 <= and if `0 - ret then
dup dup `A >= swap `F <= and if `A - 10 + ret then
dup dup `a >= swap `f <= and if `a - 10 + ret then
drop -1 ;
\ Convert decimal character to number. Complain loudly if conversion
\ is not possible.
: decval ( char -- x )
decval-nf dup 0 < if "Not a decimal digit: " puts . cr exitvm then ;
\ Convert decimal character to number. If not a decimal digit,
\ return -1.
: decval-nf ( char -- x )
dup dup `0 >= swap `9 <= and if `0 - ret then
drop -1 ;
\ Commonly used shorthands.
: 1+ 1 + ;
: 2+ 2 + ;
: 1- 1 - ;
: 2- 2 - ;
: 0= 0 = ;
: 0<> 0 <> ;
: 0< 0 < ;
: 0> 0 > ;
\ Get a 16-bit value from the constant data block. This uses big-endian
\ encoding.
: data-get16 ( addr -- x )
dup data-get8 8 << swap 1+ data-get8 + ;
\ The case..endcase construction is the equivalent of 'switch' is C.
\ Usage:
\ case
\ E1 of C1 endof
\ E2 of C2 endof
\ ...
\ CN
\ endcase
\
\ Upon entry, it considers the TOS (let's call it X). It will then evaluate
\ E1, which should yield a single value Y1; at that point, the X value is
\ still on the stack, just below Y1, and must remain untouched. The 'of'
\ word compares X with Y1; if they are equal, C1 is executed, and then
\ control jumps to after the 'endcase'. The X value is popped from the
\ stack immediately before evaluating C1.
\
\ If X and Y1 are not equal, flow proceeds to E2, to obtain a value Y2 to
\ compare with X. And so on.
\
\ If none of the 'of' clauses found a match, then CN is evaluated. When CN
\ is evaluated, the X value is on the TOS, and CN must either leave it on
\ the stack, or replace it with exactly one value; the 'endcase' word
\ expects (and drops) one value.
\
\ Implementation: this is mostly copied from ANS Forth specification,
\ although simplified a bit because we know that our control-flow stack
\ is independent of the data stack. During compilation, the number of
\ clauses is maintained on the stack; each of..endof clause really is
\ an 'if..else' that must be terminated with a matching 'then' in 'endcase'.
: case 0 ; immediate
: of 1+ postpone over postpone = postpone if postpone drop ; immediate
: endof postpone else ; immediate
: endcase
postpone drop
begin dup while 1- postpone then repeat drop ; immediate
\ A simpler and more generic "case": there is no management for a value
\ on the stack, and each test is supposed to come up with its own boolean
\ value.
: choice 0 ; immediate
: uf 1+ postpone if ; immediate
: ufnot 1+ postpone ifnot ; immediate
: enduf postpone else ; immediate
: endchoice begin dup while 1- postpone then repeat drop ; immediate
\ C implementations for native words that can be used in generated code.
add-cc: co { T0_CO(); }
add-cc: execute { T0_ENTER(ip, rp, T0_POP()); }
add-cc: drop { (void)T0_POP(); }
add-cc: dup { T0_PUSH(T0_PEEK(0)); }
add-cc: swap { T0_SWAP(); }
add-cc: over { T0_PUSH(T0_PEEK(1)); }
add-cc: rot { T0_ROT(); }
add-cc: -rot { T0_NROT(); }
add-cc: roll { T0_ROLL(T0_POP()); }
add-cc: pick { T0_PICK(T0_POP()); }
add-cc: + {
uint32_t b = T0_POP();
uint32_t a = T0_POP();
T0_PUSH(a + b);
}
add-cc: - {
uint32_t b = T0_POP();
uint32_t a = T0_POP();
T0_PUSH(a - b);
}
add-cc: neg {
uint32_t a = T0_POP();
T0_PUSH(-a);
}
add-cc: * {
uint32_t b = T0_POP();
uint32_t a = T0_POP();
T0_PUSH(a * b);
}
add-cc: / {
int32_t b = T0_POPi();
int32_t a = T0_POPi();
T0_PUSHi(a / b);
}
add-cc: u/ {
uint32_t b = T0_POP();
uint32_t a = T0_POP();
T0_PUSH(a / b);
}
add-cc: % {
int32_t b = T0_POPi();
int32_t a = T0_POPi();
T0_PUSHi(a % b);
}
add-cc: u% {
uint32_t b = T0_POP();
uint32_t a = T0_POP();
T0_PUSH(a % b);
}
add-cc: < {
int32_t b = T0_POPi();
int32_t a = T0_POPi();
T0_PUSH(-(uint32_t)(a < b));
}
add-cc: <= {
int32_t b = T0_POPi();
int32_t a = T0_POPi();
T0_PUSH(-(uint32_t)(a <= b));
}
add-cc: > {
int32_t b = T0_POPi();
int32_t a = T0_POPi();
T0_PUSH(-(uint32_t)(a > b));
}
add-cc: >= {
int32_t b = T0_POPi();
int32_t a = T0_POPi();
T0_PUSH(-(uint32_t)(a >= b));
}
add-cc: = {
uint32_t b = T0_POP();
uint32_t a = T0_POP();
T0_PUSH(-(uint32_t)(a == b));
}
add-cc: <> {
uint32_t b = T0_POP();
uint32_t a = T0_POP();
T0_PUSH(-(uint32_t)(a != b));
}
add-cc: u< {
uint32_t b = T0_POP();
uint32_t a = T0_POP();
T0_PUSH(-(uint32_t)(a < b));
}
add-cc: u<= {
uint32_t b = T0_POP();
uint32_t a = T0_POP();
T0_PUSH(-(uint32_t)(a <= b));
}
add-cc: u> {
uint32_t b = T0_POP();
uint32_t a = T0_POP();
T0_PUSH(-(uint32_t)(a > b));
}
add-cc: u>= {
uint32_t b = T0_POP();
uint32_t a = T0_POP();
T0_PUSH(-(uint32_t)(a >= b));
}
add-cc: and {
uint32_t b = T0_POP();
uint32_t a = T0_POP();
T0_PUSH(a & b);
}
add-cc: or {
uint32_t b = T0_POP();
uint32_t a = T0_POP();
T0_PUSH(a | b);
}
add-cc: xor {
uint32_t b = T0_POP();
uint32_t a = T0_POP();
T0_PUSH(a ^ b);
}
add-cc: not {
uint32_t a = T0_POP();
T0_PUSH(~a);
}
add-cc: << {
int c = (int)T0_POPi();
uint32_t x = T0_POP();
T0_PUSH(x << c);
}
add-cc: >> {
int c = (int)T0_POPi();
int32_t x = T0_POPi();
T0_PUSHi(x >> c);
}
add-cc: u>> {
int c = (int)T0_POPi();
uint32_t x = T0_POP();
T0_PUSH(x >> c);
}
add-cc: data-get8 {
size_t addr = T0_POP();
T0_PUSH(t0_datablock[addr]);
}
add-cc: . {
extern int printf(const char *fmt, ...);
printf(" %ld", (long)T0_POPi());
}
add-cc: putc {
extern int printf(const char *fmt, ...);
printf("%c", (char)T0_POPi());
}
add-cc: puts {
extern int printf(const char *fmt, ...);
printf("%s", &t0_datablock[T0_POPi()]);
}
add-cc: cr {
extern int printf(const char *fmt, ...);
printf("\n");
}
add-cc: eqstr {
const void *b = &t0_datablock[T0_POPi()];
const void *a = &t0_datablock[T0_POPi()];
T0_PUSH(-(int32_t)(strcmp(a, b) == 0));
}