mirror of
https://github.com/libretro/RetroArch.git
synced 2024-12-02 13:28:35 +00:00
310 lines
7.5 KiB
Plaintext
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));
|
|
}
|