RetroBASIC
Retrogamecoding(.org) => Announcements => Topic started by: Cybermonkey on June 10, 2019, 10:45:11 AM
-
So it's a while since I started combining the Pulsar2D framework with the My_Basic interpreter. Until today I had problems with DATA & READ but the issue could be solved today. There is no download, yet. It's a rather complicated compilation on Windows because I did not port the Library to C. Yeah, I am not so fluent in C, so I felt this wouldn't work. Anyway, I figured out that it is possible to compile a library with FreeBASIC so it can be used from C. The "only" thing is providing a *h.-file. And still the pulsarbasic.c file has about 2700 lines.
Anyway, this might be interesting for drfloyd since I am providing a tiny editor. So one can start the editor, load a file, press run and that's it. (The editor is based on my old project TinyIDE from 2010 and compiled with Lazarus.)
So the funny thing for me as a developer is that I am actually using 3 programming languages and compilers: ANSI C (gcc), BASIC (FreeBASIC) and Object Pascal (FreePascal). Can it be more versatile?
I hope I can provide a download soon.
-
It looks interesting even i am not very much in game programming. ;)
..for drFloyd ...hmm he just jumping from place to place ,
looking for "perfect" replacement for Blitz Basic.
-
Yes, that's right. Anyway, I think this is a project I will push further. There are a few things I want to implement, most important of all the creation of executables.
Oh, btw, I am using now the old editor of EGSL which is now the PulsarBASIC-Editor as seen in the screenshot.
-
There are a few things I want to implement, most important of all the creation of executables.
That should be no problem on Windows.
Using BIND topology : runtime.exe + sourcefile(in binary form- not ascii file).
That technic i was used in Aurel Basic long time ago.
-
Yes, that's the technique I am using with PulsarLua. But it's a bit harder to implement it in C ...
-
well yes
but must be there some C example about that ?
-
I didn't find one, yet. So if anyone has an example, feel free to post it here. ;)
-
I found it long time ago somewhere but i don't know now ...
-
I didn't find one, yet. So if anyone has an example, feel free to post it here. ;)
I didn't find one, yet. So if anyone has an example, feel free to post it here. ;)
How about this?
First, the binder - to compile: gcc -s -Os -o binder binder.c
/*
--binder=> bind runtime script output-filename -----------
To build: gcc -s -Os -o binder.exe binder.c
ed_davis2@yahoo.com
arg[1] = runtime
arg[2] = script
arg[3] = output-filename
copy_file(arg[1], arg[3]) -- copy runtime to output
copy_file(arg[2], arg[3], APPEND) -- append script to output
--append runtime length
write(arg[3], format(filesize(arg[1]):8:'0'), APPEND)
-- append script length
write(arg[3], format(filesize(arg[2]):8:'0'), APPEND)
-- append a pity eyecatcher
write(arg[3], format("Southern":8:'0'), APPEND)
halt
--interpreter---------------------------------------------------
-- find the size of the runtime
f = open(arg[0], "r")
seek(f, -24, SEEK_END)
new string buf(8)
read(f, buf, buf.size)
runtime_size = val(buf)
read(f, buf, buf.size)
script_size = val(buf)
read(f, buf, buf.size)
if buf <> "Southern" error("Eyecatcher not found!!!")
-- read in the script
seek(f, runtime_size, SEEK_SET)
new string buf(script_size)
read(f, buf, script_size)
close(f)
run(buf)
halt
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#if defined(__linux__)
#include <unistd.h>
#include <sys/stat.h>
#define stricmp strcasecmp
#endif
enum {n_interp = 1, n_script = 2, n_final = 3, REPLY_SIZE = 4};
int main(int argc, char *argv[]) {
FILE *interp, *script, *final;
int c, rt_size, script_size;
if (argc < 4) {
printf("format: binder interp script output-file\n");
return 2;
}
interp = fopen(argv[n_interp], "rb");
if (!interp) {
perror(argv[n_interp]);
return 2;
}
script = fopen(argv[n_script], "rb");
if (!script) {
perror(argv[n_script]);
return 2;
}
/* don't overwrite output without confirmation */
final = fopen(argv[n_final], "rb");
if (final) {
char buf[REPLY_SIZE];
fclose(final);
printf("File %s exists - delete it? (y/n)\n", argv[n_final]);
if (!fgets(buf, REPLY_SIZE, stdin)) {
printf("Nothing ventured, nothing gained\n");
return 2;
}
if (buf[0] != 'y') {
printf("Please try again later\n");
return 2;
}
}
final = fopen(argv[n_final], "wb");
if (!final) {
perror(argv[n_final]);
return 2;
}
rt_size = 0;
while ((c = fgetc(interp)) != EOF) {
fputc(c, final);
++rt_size;
}
fclose(interp);
printf("Interp size is %d\n", rt_size);
script_size = 0;
while ((c = fgetc(script)) != EOF) {
fputc(c, final);
++script_size;
}
fclose(script);
printf("Script size is %d\n", script_size);
fprintf(final, "%08d", rt_size);
fprintf(final, "%08d", script_size);
fprintf(final, "31415926");
fclose(final);
printf("%s size is %d + %d + %d = %d\n", argv[n_final],
rt_size, script_size, 24, rt_size+script_size+24);
#if defined(__linux__)
chmod(argv[n_final], 0755);
#endif
printf("%s bound to %s in %s\n", argv[n_script], argv[n_interp], argv[n_final]);
return 0;
}
Then the interpreter - to compile: gcc -s -Os -o pl0 pl0.c
Just showing the "load the code routine here". Complete interpreter is attached as pl0.c.
/*------------------------------------------------------------------------
-- find the size of the runtime
f = open(arg[0], "r")
seek(f, -24, SEEK_END)
new string buf(8)
read(f, buf, buf.size)
runtime_size = val(buf)
read(f, buf, buf.size)
script_size = val(buf)
read(f, buf, buf.size)
if buf <> "Southern" error("Eyecatcher not found!!!")
-- read in the script
seek(f, runtime_size, SEEK_SET)
new string buf(script_size)
read(f, buf, script_size)
close(f)
data is stored in 3 8 char fields:
0..7: size of the exe
8..15: size of the appended script
16..23: eyecatcher
------------------------------------------------------------------------*/
int load_script(void) {
FILE *fp;
char buf[24], buf2[9];
int size_read, rt_size;
char exe_name[PATH_MAX];
/* get the exec name */
#if defined(__WIN32)
GetModuleFileName(NULL, exe_name, MAX_PATH);
#elif defined(__linux__)
char linkname[PATH_MAX];
struct stat finfo;
int len;
sprintf(linkname, "/proc/%d/exe", getpid());
if ((len = readlink(linkname, exe_name, PATH_MAX - 1)) > 0) {
exe_name[len] = '\0';
} else {
perror("Error: ");
return false;
}
#endif
fp = fopen(exe_name, "rb");
if (!fp) {
perror(exe_name);
return false;
}
/* 24 bytes from end, to read 3 fields noted above */
if (fseek(fp, -24, SEEK_END) != 0) {
perror("fseek: ");
return false;
}
/* read all 3 fields */
size_read = fread(buf, 1, sizeof(buf), fp);
/* eyecather matches? (3rd field) */
if (memcmp(&buf[16], "31415926", 8) != 0) {
printf("Eyecatcher not found\n");
return false;
}
/* first field, the exe length */
memcpy(buf2, buf, 8);
buf2[8] = '\0';
rt_size = strtol(buf2, NULL, 10);
/* second field, the script length */
memcpy(buf2, &buf[8], 8);
buf2[8] = '\0';
script_size = strtol(buf2, NULL, 10);
script = malloc(script_size);
/* seek to the start of the script */
if (fseek(fp, rt_size, SEEK_SET) != 0) {
perror("fseek: ");
return false;
}
size_read = fread(script, 1, script_size, fp);
fclose(fp);
if (size_read != script_size) {
printf("Could not read entire script: asked %d got %d\n", script_size, size_read);
return false;
}
return true;
}
And a simple script to be bound:
const left = 0, middle = 1, right = 2;
procedure move(n, source, aux, dest);
procedure printmove();
procedure printpole(p);
begin
if p = 0 then
write('left ');
elseif p = 1 then
write('middle');
elseif p = 2 then
write('right ');
end;
end;
begin {printmove}
write('Move a disk from '); printpole(source);
write(' to '); printpole(dest);
writeln();
end;
begin {move}
if n = 1 then
printmove();
else
move(n-1, source, dest, aux);
printmove();
move(n-1, aux, source, dest);
end;
end;
var disks;
begin
repeat
writeln();
write('Number of disks (2-10, 1 to stop)? ');
read(disks);
if (disks > 1) and (disks <= 10) then
writeln();
writeln('For ', disks, ' disks, the required moves are:');
writeln();
move(disks, left, middle, right);
end;
until disks = 1;
end.
To bind:
binder pl0 hanoi.pl0 hhh
(binder pl0.exe hanoi.pl0 hhh.exe on Windows)
Tested under Windows and Linux.
Let me know if you have questions/problems.
-
Ok, thanks that's what I was looking for. So far as I can see, one can't start a script simply by writing
pl0 hanoi.pl0
Error is
Eyecatcher not found
Would of course be nice that one could use the interpreter in both ways... well, I will have a closer look on the weekend.
-
Ok, thanks that's what I was looking for. So far as I can see, one can't start a script simply by writing
pl0 hanoi.pl0
Error is
Eyecatcher not found
Would of course be nice that one could use the interpreter in both ways... well, I will have a closer look on the weekend.
Whoops! In my original version, you could do that, but I remove the code :-)
Also, I was brain-dead when I wrote this. Really only need the script size, not the runtime size.
If you are interested, I can post a new version with both features mentioned above.
-
Whoops! In my original version, you could do that, but I remove the code :-)
Also, I was brain-dead when I wrote this. Really only need the script size, not the runtime size.
If you are interested, I can post a new version with both features mentioned above.
That would be highly appreciated. :D
I think I will this first implement in AllegroBASIC since it is easier to compile (C only).
-
Yes Ed post it..
My own version is written long time ago in Emergence basic
runtime.exe put it into memory - size let say 100
source.bas - put in another memory block in binary form - size let say 50
concat two memory blocks into one
my_runtime = mem1 + mem2
save runtime on disk as exe file
when you run your program runtime jump to location of 101 to read source
and start processing it until the end of file is reached.
Is that ok?
-
If you are interested, I can post a new version with both features mentioned above.
That would be highly appreciated. :D
New version is attached.
-
My own version is written long time ago in Emergence basic
runtime.exe put it into memory - size let say 100
source.bas - put in another memory block in binary form - size let say 50
concat two memory blocks into one
my_runtime = mem1 + mem2
save runtime on disk as exe file
when you run your program runtime jump to location of 101 to read source
and start processing it until the end of file is reached.
Is that ok?
If it works, then it is ok!
:-)
-
If you are interested, I can post a new version with both features mentioned above.
That would be highly appreciated. :D
New version is attached.
Thanks a lot. That looks quite handy. I will try to implemented it in AllegroBASIC on Saturday.
-
Yes Cyb
it works of course
but only on 32bit
-
Yes Cyb
it works of course
but only on 32bit
Why so? I compiled it on Linux 64bit and it works fine. On Windows I usually compile my programs for 32bit destination.
-
No i was talking about my old binder written in Emergence basic 32bit.
I don't tried this one ..because i have in plan to use Oxygen basic for that job.
But of course i will try Ed binder too.
-
No i was talking about my old binder written in Emergence basic 32bit.
I don't tried this one ..because i have in plan to use Oxygen basic for that job.
But of course i will try Ed binder too.
Ok, now I understand. :)
-
Well
Ed Binder is not just binder than
PLO interpreter + binder
what is PLO i am not sure - looks like
some sort of pascal language ..right Ed?
Is this bytecode compiler?
-
https://en.wikipedia.org/wiki/PL/0
-
Ed Binder is not just binder than
There are two C programs in the zip file:
binder.c - which is just a binder.
pl0.c - pl/0 interpreter. Once compiled, this one handles either bound programs or programs passed on the command line.
PLO interpreter + binder
what is PLO i am not sure - looks like
some sort of pascal language ..right Ed?
Is this bytecode compiler?
PL/0 was a simple language Wirth (Algol-W/Pascal/Modula/Oberon creator) created for teaching students how to write compilers, in the compiler courses he taught. It is not a strict subset of Pascal - a few minor differences. First book about it was Algorithms + Data Structures = Programs, circa 1976. An execellent book! You can get it for a song on Amazon - highly recommended, even today. He also has an excellent compiler book, in PDF form, on his website. Again, this one is also highly recommended. If you have any interest in compilers and interpreters :)
Anyway, I flunked my compilers class, but about 30 years ago (has it been that long?), I took up the subject again, and translated Wirth's Pascal version of PL/0 to C. Recently I updated it for Linux.
And yes, it is a byte-code interpreter (Wirth called them p-codes).
do {
int f_ret; /* instruction register */
const Code ir = code[pc];
pc++;
switch (ir.opcode) {
case o_lit: top++; stack[top] = ir.adr; break;
case o_lod: top++; stack[top] = stack[display[ir.level] + ir.adr]; break;
case o_sto: stack[display[ir.level] + ir.adr] = stack[top]; top--; break;
case o_cal: /* generate new block mark */
stack[top + 1] = display[ir.level + 1];
stack[top + 2] = pc;
display[ir.level + 1] = top + 1;
pc = ir.adr;
break;
case o_rtf:
f_ret = stack[top];
// fall-thru
case o_rtp:
top = display[ir.level];
display[ir.level] = stack[top];
pc = stack[top + 1];
top -= (ir.adr + 1);
if (ir.opcode == o_rtf) {
top++;
stack[top] = f_ret;
}
break;
case o_int: top += ir.adr; break;
case o_jmp: pc = ir.adr; break;
case o_jpc:
if (stack[top] == 0)
pc = ir.adr;
top--;
break;
case o_pop: top--; break;
case o_neg: stack[top] = -stack[top]; break;
case o_add: top--; stack[top] += stack[top+1]; break;
case o_sub: top--; stack[top] -= stack[top+1]; break;
case o_mul: top--; stack[top] *= stack[top+1]; break;
case o_div: top--; stack[top] /= stack[top+1]; break;
case o_mod: top--; stack[top] %= stack[top+1]; break;
case o_equ: top--; stack[top] = stack[top] == stack[top+1]; break;
case o_neq: top--; stack[top] = stack[top] != stack[top+1]; break;
case o_lss: top--; stack[top] = stack[top] < stack[top+1]; break;
case o_geq: top--; stack[top] = stack[top] >= stack[top+1]; break;
case o_gtr: top--; stack[top] = stack[top] > stack[top+1]; break;
case o_leq: top--; stack[top] = stack[top] <= stack[top+1]; break;
case o_and: top--; stack[top] = stack[top] && stack[top+1]; break;
case o_or: top--; stack[top] = stack[top] || stack[top+1]; break;
case o_rdi: {
int temp;
fflush(stdout);
if (scanf("%d", &temp) < 0)
perror("scanf: ");
top++;
stack[top] = temp;
break;
}
case o_wrl: printf("\n"); break;
case o_wrt: printf("%d ", stack[top]); top--; break;
case o_wrs: {
int len = lit_data[ir.adr];
char *cp = &lit_data[ir.adr + 1];
printf("%*.*s", len, len, cp);
break;
}
default:
printf("Invalid opcode %d at position %d\n", ir.opcode, pc - 1);
// fall-thru
case o_hlt: pc = 0; break;
}
if (top > last_stack) {
last_stack = top;
if (verbose)
printf("stack increased to %d at %d\n", top, pc);
}
} while (pc > 0);
Only data type is integer. But it does have nested procedures, which can be interesting; no parameters though.
It is definitely worth the time to study Wirth's compilers - after PL/0, take a look at Pascal-S.
-
There are two C programs in the zip file:
binder.c - which is just a binder.
pl0.c - pl/0 interpreter. Once compiled, this one handles either bound programs or programs passed on the command line.
...
no parameters though.
I forgot! I updated this one to include parameters. Plus, blocks are more Modula2/Oberon like than Pascal. So not strictly PL/0, more PL/0+ :-)
-
Ed
sorry Cyb if i babeling on your topic...
I see on many your and other authors
stack-this ,..stack--that
How should look stack-less interpreter?
like mine..even i use some sort of stack for FOR/GOSUB blocks
-
Ok, back to topic, although this thread is already far away from it ...
I've got a question to the load_script function: is the complete listing saved in the "static char *script"?
I actually don't get where's the decision if the script is bound and executed or loaded from a file.
-
I've got a question to the load_script function: is the complete listing saved in the "static char *script"?
Yes, in load_script(), starting at line 1036:
script = malloc(script_size);
/* seek to the start of the script */
if (fseek(fp, -(script_size + extra_size), SEEK_END) != 0) {
perror("load_script fseek end 2");
return false;
}
size_read = fread(script, 1, script_size, fp);
I actually don't get where's the decision if the script is bound and executed or loaded from a file.
In the main() routine.
It loops around the args, and if one does not start with "-", then it assumes it is a filename.
Then at line 1093:
if (!fn && !load_script()) {
show_help();
return 2;
}
So, if the fn is NULL (e.g., !fn) && !load_script() -- load_script() is a boolean -- we show the help and exit.
I probably need more comments - sorry about that!
If you have other questions, feel free to ask.
-
Ok, thanks, this I understood. Now I still don't get the decision whether the script or the file is executed. There's that "init_scan" function; does it only execute if a file is loaded otherwise the "load_script" is used? (Sorry for my dumb questions but as I said C is rather confusing for me.)
In AllegroBASIC it's that simple (for now):
if (argc>1) {
if(mb_load_file(bas, argv[1]) == MB_FUNC_OK)
_alloc_data_seq();
mb_run(bas,true);
_free_data_seq();
}
else {
tinyfd_messageBox("No Script", "AllegroBASIC 0.6.5\nUsage: allegrobasic[.exe] script.bas", "ok", "error", 1);
}
And in PulsarLua the decision whether to use the script or the file, looks like this (Pascal is so much easier to read...):
if isbound = true then
begin
if luaL_dostring (L,PChar(prog))<>0 then
begin
//show if an error occured
Messagebox(1,'Error',lua_tostring (L,-1));
closeapplication;
halt;
end;
end
else
if (luaL_dofile(L, script))<>0 then
begin
//show if an error occured
Messagebox(1,'Error',lua_tostring (L,-1));
closeapplication;
halt;
end;
lua_close(L);
end.
("isbound" is a boolean which is filled at runtime - if I found out that the script is bound then it's true otherwise false.)
-
Ok, forget my last question. It works!! :D
No I will try the compilation of AllegroBASIC on Windows. Keep your fingers crossed ...
Edit: added a screenshot from the new AllegroBASIC editor including the output of the binder program.
-
Phew, it took me over an hour to figure out how to compile it on Windows. The problem was that the "load_script" function needs <windows.h> which interferes with <allegro.h>.
So if any of you will ever use (the old version of) Allegr: use <winalleg.h> instead of <windows.h>.
But anyway, this is about PulsarBASIC, not AllegroBASIC, so the latter one will get its' own thread.
-
Ok, PulsarBASIC works now on Linux. The editor is (almost) the same as for AllegroBASIC. See the attached screenshot for a custom colour configuration. (It's called "himbeere", it's also available for Geany and my favourite colour scheme.)
-
So this Screen is from the AllegroBASIC editor, but the colour config files are compatible of course. The following three are shipped with AllergroBASIC (soon!)