muforth/kernel.c

/*
 * This file is part of muFORTH: http://pages.nimblemachines.com/muforth
 *
 * Copyright (c) 2002-2008 David Frech. All rights reserved, and all wrongs
 * reversed. (See the file COPYRIGHT for details.)
 */

/* the very basic words */

#include "muforth.h"

#define MIN(a,b)    (((a) < (b)) ? (a) : (b))

cell pop_dstack()  { cell t = TOP; DROP(1); return t; }

void mu_plus()  { TOP += ST1; NIP(1); }
void mu_and()   { TOP &= ST1; NIP(1); }
void mu_or()    { TOP |= ST1; NIP(1); }
void mu_xor()   { TOP ^= ST1; NIP(1); }

void mu_negate()  { TOP = -TOP; }
void mu_invert()  { TOP = ~TOP; }

void mu_2star()                    { TOP <<= 1; }
void mu_2slash()                   { TOP >>= 1; }
void mu_u2slash()  { TOP = (unsigned)TOP >>  1; }

void mu_shift_left()              { TOP = ST1 << TOP; NIP(1); }
void mu_shift_right()             { TOP = ST1 >> TOP; NIP(1); }
void mu_ushift_right()  { TOP = (unsigned)ST1 >> TOP; NIP(1); }

void mu_fetch()   { TOP =  *(cell *)TOP; }
void mu_cfetch()  { TOP = *(uint8 *)TOP; }

void mu_store()        { *(cell *)TOP = ST1; DROP(2); }
void mu_cstore()      { *(uint8 *)TOP = ST1; DROP(2); }
void mu_plus_store()  { *(cell *)TOP += ST1; DROP(2); }

void mu_dup()    { DUP; }
void mu_nip()    { NIP(1); }
void mu_drop()   { DROP(1); }
void mu_2drop()  { DROP(2); }
void mu_swap()   { cell t = TOP; TOP = ST1; ST1 = t; }
void mu_over()   { DUP; TOP = ST2; }          /* a b -> a b a */

void mu_rot()        { cell t = TOP; TOP = ST2; ST2 = ST1; ST1 = t; }
void mu_minus_rot()  { cell t = TOP; TOP = ST1; ST1 = ST2; ST2 = t; }

void mu_uless()  { TOP = (ST1 < (unsigned)TOP) ? -1 : 0; NIP(1); }
void mu_less()   { TOP = (ST1 < TOP)           ? -1 : 0; NIP(1); }

void mu_zero_less()   { TOP = (TOP <  0) ? -1 : 0; }
void mu_zero_equal()  { TOP = (TOP == 0) ? -1 : 0; }

void mu_depth()     { cell d = S0 - SP; PUSH(d); }
void mu_sp_reset()  { SP = S0; TOP = 0xdecafbad; }
void mu_push_s0()   { PUSH(S0); }   /* address of stack bottom */

/*
 * Single-length math routines.
 *
 * Sometimes I really hate C and gcc. This is one of those times. It is
 * trivially easy to write the basic Forth word fm/mod in assembler. The
 * machine gives you the pieces you need: given two 32bit operands, it
 * multiplies and gives a 64bit result; then you divide that by another
 * 32bit operand. There is no opportunity for over- or underflow, and it's
 * about 6 instructions - including stack moves - in x86 assembler.
 *
 * It's impossible to do this in gcc. Grrr.
 *
 * Also, since integer divide by definition gives you both quotient and
 * remainder, why does C make you calculate them separately? It's stupid.
 *
 * So, I've given up on double-length math for muFORTH. It's a beautiful
 * and elegant part of Forth, but since I intend muFORTH mostly for
 * cross-compiling (to 32bit architectures at the moment, though that could
 * change!), single-length is plenty. So don't try using star-slash with
 * large operands. ;-)
 */

/*
 * We don't need a ustar, since single-length star and ustar yield the same
 * answers! (Prove this!)
 */
void mu_star()    { TOP *= ST1; NIP(1); }

void mu_uslash_mod()  /* u1 u2 -- um uq */
{
    ucell umod;
    ucell uquot;

    uquot = (unsigned)ST1 / TOP;
    umod  = (unsigned)ST1 % TOP;
    ST1 = umod;
    TOP = uquot;
}

/*
 * Of course, I'm not giving up floored division. ;-)
 *
 * Most processors do symmetric division. To fix this (to make it _FLOOR_)
 * we have to adjust the quotient and remainder when rem != 0 and the
 * divisor and dividend are different signs. (This is NOT the same as
 * quotient < 0, because the quotient could have been truncated to zero by
 * symmetric division when the actual (floored) quotient is < 0!) The
 * adjustment is:
 *
 *  quot_floored = quot_symm - 1
 *   mod_floored =  rem_symm + divisor
 *
 * This preserves the invariant a / b => (r,q) s.t. (q * b) + r = a.
 *
 *   (q' * b) + r' = (q - 1) * b + (r + b) = (q * b) - b + r + b
 *            = (q * b) + r
 *            = a
 *
 * where q',r' are the _floored_ quotient and remainder (really, modulus),
 * and q,r are the symmetric quotient and remainder.
 *
 */
void mu_slash_mod()  /* n1 n2 -- m q */
{
    cell mod;
    cell quot;

    quot = ST1 / TOP;
    mod  = ST1 % TOP;

#ifndef HOST_DIVIDE_FLOORS
    /*
     * We now have the results of a stupid symmetric division, which we
     * must convert to floored. We only do this if the modulus was non-zero
     * and if the dividend and divisor had opposite signs.
     */
    if (mod != 0 && (ST1 ^ TOP) < 0)
    {
        quot -= 1;
        mod  += TOP;
    }
#endif

    ST1 = mod;
    TOP = quot;
}

/*
 * C, or at least gcc, is sooooo fucking retarded! I cannot define "cell/"
 * the way I want, because gcc (on x86 at least) compiles /= by a power of
 * two of a _signed_ integer as an _un_signed_ shift! What gives!
 *
 * So I have to go to extra effort and circumvent my tool, which, instead of
 * helping me get my job done, is in the way. Sigh.
 *
 * Actually, C is even more retarded than I thought. I was going to check
 * the sizeof(cell) and set SH_CELL accordingly...but I can't do
 * "environmental queries" in the preprocessor! So the user gets to do this
 * by hand! Hooray for automation!
 *
 * (Not really. envtest runs and outputs env.h, which contains useful info
 * about the host machine environment. But, really, it should be possible
 * (and easy!) to do this from the preprocessor.)
*/

/* By defining cell-shift here, I can define cells and cell/ in startup! */
void mu_cell_shift(void)  { PUSH(SH_CELL); }

void mu_string_equal()   /* a1 len1 a2 len2 -- flag */
{
    if (ST2 != TOP)
        TOP = 0;            /* unequal if lengths differ */
    else
        TOP = (memcmp((char *)ST3, (char *)ST1, TOP) == 0) ? -1 : 0;

    NIP(3);
}

void mu_cmove()  /* src dest count */
{
    void *src = (void *) ST2;
    void *dest = (void *) ST1;
    size_t count = TOP;

    bcopy(src, dest, count);  /* allows overlapping strings */
    DROP(3);
}

#ifdef THIS_IS_SILLY
/*
 * I thought I wanted to be able to sort string, but I have more
 * interesting ideas about what muFORTH is good for. ;-)
 *
 * Like C and unlike Forth, mu_string_compare returns an integer representing
 * an ordering (in general the difference between the ASCII codes of the first
 * two non-matching characters):
 *
 *  <0 means the first string is "less";
 *   0 means the two strings are equal;
 *  >0 means the first string is "greater".
 *
 * If two strings are the same length, then:
 *   If every character is equal, 0 is returned;
 *   Else, the ordering (difference) of their first non-equal characters
 *     is returned.
 *
 * If the two strings are of different lengths, then:
 *   If they share the same prefix, the shorter string is "less"; the shorter
 *     string is treated as if it had a last character of 0.
 *   Else, the ordering (difference) of their first non-equal characters
 *     is returned.
 *   Note that in this second case, 0 is never returned.
 */
void mu_string_compare()
{
    TOP = string_compare((char *)ST3, ST2, (char *)ST1, TOP);
    NIP(3);
}

int string_compare(const char *string1, size_t length1,
                   const char *string2, size_t length2)
{
    int ordering;

    /* Careful: if lengths differ the strings can't compare as equal! */
    if (length1 == length2)
        ordering = strncmp(string1, string2, length1);
    else
    {
        int cmp;

        /* Compare as many characters as we can */
        cmp = strncmp(string1, string2, MIN(length1, length2));

        /*
         * If all equal, then their lengths determine the outcome (the
         * shorter string is "less"). Otherwise, use the result of the
         * strncmp (which tells us how the first characters that differed
         * differed).
         */
        if (cmp == 0)
        {
            if (length1 < length2)
                ordering = -string2[length1];
            else
                ordering =  string1[length2];
        }
        else
            ordering = cmp;
    }
    return ordering;
}
#endif