Příklady




Na tomto místě se nabízejí ukázky (programy v jazyku "C"), které demonstrují základní použití nejdůležitějších stavebních prvků vrstvy "RPNPROC":




První: program (job) načtený z disku

Realizace programové smyčky použitím instrukce ISG s odskokem do podprogramu.

V hlavní funkci programu main() nalezneme obligátní rozebrání voleb (options) na příkazové/spouštěcí řádce (command-line):

Je-li (kromě options) k mání ještě nějaký argument, je chápán jako jméno souboru s programem (job). Toto jméno je předáno funkci job_from_file() obsahuje:


Co je vidět na konzoli při spuštění prvního ukázkového programu.

# cat loop.rpnproc
0x3a,0x40,2
sto 0
lbl 1
rcl 0
xeq 2
isg 0
gto 1
rtn
lbl 2
0xFFFF
and
0x30
-
2
*
pse
rtn
# ./rpnproc -h -a. loop.rpnproc

FILE:    loop.rpnproc
* * *  LIST  * * *
###.0000        0x003a,0x40,0x02
###.0001        STO 000
001.0000        LBL 001
001.0001        RCL 000
001.0002        XEQ 002
001.0003        ISG 000
001.0004        GTO 001
001.0005        RTN
002.0000        LBL 002
002.0001        0x0000ffff
002.0002        AND
002.0003        0x00000030
002.0004        -
002.0005        0x00000002
002.0006        *
002.0007        PSE
002.0008        RTN
* * *  EXEC  * * *
PAUSE:   0x00000014,         20
PAUSE:   0x00000018,         24
PAUSE:   0x0000001c,         28
PAUSE:   0x00000020,         32
RESULT:  0x00000020,         32
#


Výpis prvního ukázkového programu.

#include <stdio.h>
#include <string.h>     // strchr
#include <malloc.h>     // malloc
#if !defined(__WATCOMC__) && !defined(__QNX__) && !defined(WIN32)
#include <alloca.h>
#define  Alloc4hndl        alloca
#else
#define  Alloc4hndl        malloc
#endif


#include "rpnproc.h"



// "Options" na "command-line"
//
#define  MULTICHR2(a, b)    ((a)  |  (b) << 8)
#define  OPTI2CHR(c)        MULTICHR2('-', (c))
#define  OPTI2ARG(v)        (*(v) + 2)



// Promenne nastavene z "command-line"
//
static int                  radix = RPNPROC_LIST_RADIX_SGN;
static unsigned long        delim = RPNPROC_LIST_DELIM_DEFLT;
static int                  tracef = 0;


// Format vypisu hodnot
//
#define  FORMAT_VALUES      "0x%08lx,%11ld"



/*
 * CALLBACK A VYKONNA FUNKCE PRO "rpnproc_listjob()" A "rpnproc_listone()"
 */

static int cb_joblist(char linetxt[], void *userdata)
{
    userdata = userdata;

    puts(linetxt);
    return(RPNPROC_CB_CONT);
}

static int job_list(int (*fncp)(void *, rpnproclsjval_t *), void *argp)
{
    rpnproclsjval_t    lsjvalv;

    lsjvalv.radix    = radix;
    lsjvalv.delim    = delim;
    lsjvalv.fncp     = cb_joblist;
    lsjvalv.userdata = NULL;
    return((*fncp)(argp, &lsjvalv));
}

#define  Joblist(f, p)                                                       \
            job_list((int (*)(void *, rpnproclsjval_t *))rpnproc_list##f, (p))



/*
 * CALLBACK PRO FUNKCI "rpnproc_execute()"
 */

static int cb_execute(int execb, rpnprocexeval_t *exevalp, void *userdata)
{
    userdata = userdata;

    switch(execb)
    {
    case RPNPROC_EXECB_TRACE:
        printf("TRACE:   " FORMAT_VALUES ":  ", exevalp->stackx, exevalp->stackx);
        Joblist(one, &exevalp->trcjobv);
        break;

    case RPNPROC_EXECB_PSE:
        printf("PAUSE:   " FORMAT_VALUES "\n", exevalp->stackx, exevalp->stackx);
        break;

    default:
        printf("ERROR:   %d:  label=%d, line=%u\n", execb,
                exevalp->trcjobv.tracev.lblidx, exevalp->trcjobv.tracev.lineno);
    }
    return(RPNPROC_CB_CONT);
}



/*
 * VYKONANI PROGRAMU
 */

static void job_execute(rpnprocjobp_t jobp, unsigned varnum, unsigned lblnum)
{
    rpnprocvalue_t   value;
    rpnprochndp_t    hndp;

    puts("* * *  LIST  * * *");
    Joblist(job, jobp);
    if((hndp = Rpnprocinit(Alloc4hndl, varnum, lblnum)) == NULL)
        return;
    puts("* * *  EXEC  * * *");
    rpnproc_setjob(hndp, jobp, cb_execute, NULL);
    value = rpnproc_execute(hndp, -1, tracef);
    printf("RESULT:  " FORMAT_VALUES "\n", value, value);
}



/*
 * CALLBACK PRO FUNKCI "rpnproc_makejob()"
 */

struct makejobs
{
    FILE            *fp;
    rpnprocjobp_t   jobp;
    unsigned        lineno;
};

static int cb_makejob(int mkjcb, rpnprocmkjval_t *mvp, void *userdata)
{
#define  umjp   ((struct makejobs *)userdata)

    switch(mkjcb)
    {
    case RPNPROC_MKJCB_MEMJOB:
        mvp->memjob.jobp = malloc(mvp->memjob.size = 512);
        if(umjp->jobp == NULL)
            umjp->jobp = mvp->memjob.jobp;
        return(RPNPROC_CB_CONT);

    case RPNPROC_MKJCB_LINE:
        if(fgets(mvp->linet, sizeof(mvp->linet) - 1, umjp->fp) == NULL)
            return(RPNPROC_CB_BREAK);
        ++umjp->lineno;
        return(RPNPROC_CB_CONT);

    default:
        return(RPNPROC_CB_BREAK);
    }

#undef  umjp
}



/*
 * NACTENI A VYKONANI PROGRAMU ZE SOUBORU
 */

static void job_from_file(char *fnamep)
{
    int             test;
    unsigned        varnum,
                    lblnum;
    struct makejobs mjv;

    mjv.jobp = NULL;
    mjv.lineno = 0;
    if((mjv.fp = fopen(fnamep, "r")) != NULL)
    {
        if((test = rpnproc_makejob(&varnum, &lblnum,
                                    cb_makejob, &mjv)) != RPNPROC_MAKEJOB_OK)
            printf("MAKEJOB: %d:  line=%d\n", test, mjv.lineno);
        else
            if(mjv.jobp != NULL)
                job_execute(mjv.jobp, varnum, lblnum);
        fclose(mjv.fp);
    }
}



/*
 * HLAVNI PROGRAM
 */

int main(int argc, char *argv[])
{
#define  _RPNPROC_DELIM_SHIFT_shift      shift

    int          shift;

    while(--argc)
        switch(shift = _RPNPROC_DELIM_SHIFT_NLBL,  *(short *)*++argv)
        {
        case OPTI2CHR('h'):
            radix = RPNPROC_LIST_RADIX_HEX;
            break;

        case OPTI2CHR('o'):
            radix = RPNPROC_LIST_RADIX_OCT;
            break;

        case OPTI2CHR('u'):
            radix = RPNPROC_LIST_RADIX_UNS;
            break;

        case OPTI2CHR('t'):
            tracef = 1;
            break;

        case OPTI2CHR('p'):
            shift += _RPNPROC_DELIM_SHIFT_PARAM - _RPNPROC_DELIM_SHIFT_INSTR;

        case OPTI2CHR('i'):
            shift += _RPNPROC_DELIM_SHIFT_INSTR - _RPNPROC_DELIM_SHIFT_ADDR;

        case OPTI2CHR('a'):
            shift += _RPNPROC_DELIM_SHIFT_ADDR - _RPNPROC_DELIM_SHIFT_NLBL;

        case OPTI2CHR('n'):
            delim  =  delim & ~_RPNPROC_DELIM_MAKE(shift, ~0)  |
                               _RPNPROC_DELIM_MAKE(shift, *OPTI2ARG(argv));
            break;

        default:
            job_from_file(*argv);
        }
    return(0);

#undef  _RPNPROC_DELIM_SHIFT_shift
}




Druhý: staticky vytvořený program.

O tomto způsobu vytváření programů již byla řeč v popisu prostých datových typů. Zde je konkrétní ukázka.
Binární kód programu je uložen přímo ve "statickém poli" (viz typ rpnprocjobt_t diskutovaný zde). Jednotlivé instrukce jsou definovány makry RPNPROC_ST_* obsaženými v headeru rpnproc.h. Zde je tento zápis konfrontován s mnemonickou podobou:

{RPNPROC_ST_UNS, UNS
 RPNPROC_ST_WIDEW, WIDE W
 RPNPROC_ST_STOP, STOP
 RPNPROC_ST_RDN, Rdn
 RPNPROC_ST_STOP, STOP
 RPNPROC_ST_RDN, Rdn
 RPNPROC_ST_STOP, STOP
 RPNPROC_ST_RDN, Rdn
 RPNPROC_ST_IMOP(-2), -2
 RPNPROC_ST_MUL, *
 RPNPROC_ST_END};

Na tomto ukázkovém programu, který ilustruje veskrze minimalistické použití vrstvy "RPNPROC", stojí za zdůraznění několik detailů:

Důležitá poznámka:
Pokud by byla možnost statického vytváření programů použita v případě platformy s byteovou organizací "big-endian", je třeba povinný řádek takového programu doplnit do formy:

#define RPNPROC_ENDIAN_BIG
#include "rpnproc.h"


Sekce Download však nenabízí žádnou verzi knihovny s touto byteovou organizací.


Co je vidět na konzoli při spuštění druhého ukázkového programu.

# ./test 4 5 6 -8
0:  0x0000fff8  65528
1:  0x00000006      6
2:  0x00000005      5
3:  0x0000fff8  65528
#


Výpis druhého ukázkového programu.

#include <stdio.h>
#include <stdlib.h>     // strtol
#ifndef __WATCOMC__
#include <alloca.h>     // alloca
#else
#include <malloc.h>     // alloca
#endif

#include "rpnproc.h"



int main(int argc, char *argv[])
{
    rpnprochndp_t           hndp;
    static rpnprocjobt_t    jobt = {RPNPROC_ST_UNS,
                                    RPNPROC_ST_WIDEW,
                                    RPNPROC_ST_STOP,
                                    RPNPROC_ST_RDN,
                                    RPNPROC_ST_STOP,
                                    RPNPROC_ST_RDN,
                                    RPNPROC_ST_STOP,
                                    RPNPROC_ST_RDN,
                                    RPNPROC_ST_IMOP(-2),
                                    RPNPROC_ST_MUL,
                                    RPNPROC_ST_END};

    if((hndp = Rpnprocinitpgmonly(alloca)) == NULL)
        return(-1);
    while(--argc)
        rpnproc_enter(hndp, strtol(*++argv, NULL, 0));
    rpnproc_setjob(hndp, jobt, NULL, NULL);
    for(argc = 0;  argc < 4;  ++argc)
    {
        rpnprocvalue_t  value;

        value = rpnproc_execute(hndp, -1, 0);
        printf("%d:  0x%08lx %6ld\n", argc, value, value);
    }
    return(0);
}




Třetí: rychlost provádění programu.

Tato ukázka slouží potenciálnímu čtenáři těchto řádků k vytvoření si představy o rychlosti provádění programu. Předmětem zkoumání je klasická programová smyčka odečítání hodnoty do nuly.

Měření, jehož výsledky zveřejňuje následující výpis ("Co je vidět na konzoli..."), bylo provedeno pod OS Linux SuSE 9.1 na obstarožním počítači s taktovací frekvencí 2GHz. Vyplývá z něho, že na jednu iteraci programové smyčky počítač spotřeboval

Nemá smysl zabývat se rozpadem spotřeby času na jednu každou instrukci. Pro účely, na které je RPN procesor "stavěn", to reprezentuje časovou spotřebu pod mezí opravňující k vedení jakékoliv diskuze na téma "...a nebude von z toho ten počiták moc udejchanej?".

V rámci objektivity je však třeba přiznat, že strojový kód generovaný překladačem jazyka "C" s nastavenými optimalizacemi stejný problém "zmákne" rychlostí 1.55ns na jednu iteraci programové smyčky. Je řeč o následujícím kódu:

static int      tracef = 0;

static void calc_decrement(long value)
{
    while(--value != 0)
        if(tracef)
            printf("DECREMENT: %ld\n", value);
}

Co je vidět na konzoli při spuštění třetího ukázkového programu.

# time ./rpnproc rychlost.rpncalc

FILE:    rychlost.rpncalc
* * *  LIST  * * *
###:0000        100000000
000:0000        LBL 000
000:0001        1
000:0002        -
000:0003        x!= 0
000:0004        GTO 000
* * *  EXEC  * * *
RESULT:  0x00000000,          0

real    0m21.350s
user    0m21.290s
sys     0m0.016s
#