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.
-
Druhý: staticky vytvořený program.
-
Třetí: rychlost provádění programu.
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):
-
Nastavení číselné soustavy zobrazení přímého operandu instrukcí:
-
-h (šestnáctková),
-
-o (osmičková),
-
-u (desítková bez znaménka).
Desítková se znaménkem je výchozí hodnotou.
-
Argumenty následující skupiny options jsou znaky/oddělovače použité ve výpisu:
-
-n (znaky pro neexistující navěští),
-
-a (oddělovač navěští od čísla řádku),
-
-p (oddělovač instrukce od adresy),
-
-i (oddělovač parametru od instrukce).
-
Nastavení příznaku trasování pro funkci rpnproc_execute().
Výchozí stav je bez trasování.
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ů:
-
K získání paměti pro vnitřní proměnné vrstvy "RPNPROC" je užito funkce alloca(). (Pro ty, co tuto funkci běžně nepoužívají: paměť není alokována z heapu ale na stacku, jako v případě automatických proměnných).
-
Nejsou žádné požadavky na paměť pro tabulky proměnných a návěští. (Pro inicializaci je použito makro Rpnprocinitpgmonly()).
-
Není specifikována callback funkce pro rpnproc_execute(). V tomto konkrétním případě by stejně nebyla nikdy volána.
-
Na místě očekávané instrukce PSE pro zobrazení mezivýsledku je použita funkce jiná - STOP. Po opětovném spuštění provádění programu funkcí rpnproc_execute() běh programu pokračuje.
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
-
21.35s / 100000000 = 213.5ns
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
#