ibniz-1.18/0000755000175000017500000000000011701150471012233 5ustar viznutviznutibniz-1.18/clipboard.c0000644000175000017500000001016311701150253014335 0ustar viznutviznut#include "ibniz.h" #if defined(WIN32) #include void clipboard_load() { HGLOBAL data; if (!IsClipboardFormatAvailable(CF_TEXT)) return; if (!OpenClipboard(NULL)) return; data = GetClipboardData(CF_TEXT); if(data) { char*t=(char*)GlobalLock(data); int lgt=strlen(t); if(clipboard) free(clipboard); clipboard=malloc(strlen(t)+1); strcpy(clipboard,t); GlobalUnlock(data); } CloseClipboard(); } void clipboard_store() { HGLOBAL buffer; if (!OpenClipboard(NULL)) return; EmptyClipboard(); buffer = GlobalAlloc(GMEM_DDESHARE,strlen(clipboard)+1); if(!buffer) { CloseClipboard(); return; } buffer = (char*)GlobalLock(buffer); strcpy(buffer,clipboard); GlobalUnlock(buffer); SetClipboardData(CF_TEXT,buffer); CloseClipboard(); } #elif defined(X11) #include #include #include #include struct { SDL_SysWMinfo swi; Atom cbatom; } clipbrd; void clipboard_init() { if(!clipbrd.cbatom) { SDL_GetWMInfo(&clipbrd.swi); if(!clipbrd.swi.info.x11.display) return; clipbrd.swi.info.x11.lock_func(); clipbrd.cbatom = XInternAtom(clipbrd.swi.info.x11.display, "IBNIZ_CB",False); clipbrd.swi.info.x11.unlock_func(); } } char* getcbtext(XSelectionEvent *e) { Atom type; int format,lgt; unsigned long nitems; unsigned long remaining; unsigned char *xdata; if(e->property==None) return NULL; xdata = NULL; XGetWindowProperty(e->display, e->requestor, clipbrd.cbatom, 0, 0, False, AnyPropertyType, &type, &format, &nitems, &remaining, &xdata); if(type == None) return NULL; if(xdata) XFree(xdata); xdata = NULL; XGetWindowProperty(e->display, e->requestor, clipbrd.cbatom, 0, (remaining+3)/4, False, AnyPropertyType, &type, &format, &nitems, &remaining, &xdata); if(type==None || !xdata) return NULL; lgt=(nitems*format)/8; if(lgt) XStoreBytes(clipbrd.swi.info.x11.display,xdata,lgt); XDeleteProperty(e->display,e->requestor,clipbrd.cbatom); return xdata; } char* readX11clipboard() { Window owner; owner = XGetSelectionOwner(clipbrd.swi.info.x11.display,XA_PRIMARY); if(owner==clipbrd.swi.info.x11.window) return NULL; if(owner!=None) { int i=64; XConvertSelection(clipbrd.swi.info.x11.display,XA_PRIMARY,XA_STRING, clipbrd.cbatom,clipbrd.swi.info.x11.window,CurrentTime); XFlush(clipbrd.swi.info.x11.display); for(;i;i--) { XEvent e; XNextEvent(clipbrd.swi.info.x11.display, &e); if(e.type==SelectionNotify) { if(e.xselection.property==None) return NULL; return getcbtext(&e.xselection); } } } else return NULL; } void clipboard_load() { char*cbt; clipboard_init(); if(!clipbrd.cbatom) return; clipbrd.swi.info.x11.lock_func(); cbt=readX11clipboard(); clipbrd.swi.info.x11.unlock_func(); if(cbt) { if(clipboard) free(clipboard); clipboard=cbt; } } void clipboard_store() { clipboard_init(); if(!clipbrd.cbatom) return; clipbrd.swi.info.x11.lock_func(); XSetSelectionOwner(clipbrd.swi.info.x11.display,XA_PRIMARY, clipbrd.swi.info.x11.window,CurrentTime); XFlush(clipbrd.swi.info.x11.display); clipbrd.swi.info.x11.unlock_func(); } void clipboard_handlesysreq(SDL_Event*e) { XEvent res; if(e->syswm.msg->event.xevent.type==SelectionRequest) { XSelectionRequestEvent*req= &(e->syswm.msg->event.xevent.xselectionrequest); if(req->target==XA_STRING) { XChangeProperty(clipbrd.swi.info.x11.display,req->requestor, req->property,XA_STRING,8,PropModeReplace,clipboard,strlen(clipboard)); res.xselection.property=req->property; } else res.xselection.property=None; res.xselection.type=SelectionNotify; res.xselection.display=clipbrd.swi.info.x11.display; res.xselection.requestor=req->requestor; res.xselection.target=req->target; res.xselection.time=req->time; XSendEvent(clipbrd.swi.info.x11.display,req->requestor,0,0,&res); XFlush(clipbrd.swi.info.x11.display); } } #else void clipboard_load() { } void clipboard_store() { } #endif ibniz-1.18/ibniz.txt0000644000175000017500000004753011701150434014117 0ustar viznutviznut Compilation in Unix-like systems that have GCC and SDL installed: make This documentation represents IBNIZ version 1.18 released on 2012-01-04. The distribution licence is the "zlib/libpng licence" (see licence.txt). === OVERVIEW === IBNIZ is a virtual machine designed for extremely compact low-level audiovisual programs. The leading design goal is usefulness as a platform for demoscene productions, glitch art and similar projects. Mainsteam software engineering aspects are considered totally irrelevant. IBNIZ stands for Ideally Bare Numeric Impression giZmo. The name also refers to Gottfried Leibniz, the 17th-century polymath who, among all, invented binary arithmetic, built the first four-operation calculating machine, and believed that the world was designed with the principle that a minimal set of rules should yield a maximal diversity. The IBNIZ virtual machine is basically a two-stack machine somewhat similar to Forth implementations but with the major difference that the stack is cyclical and also used as output buffer. The machine runs in an endless loop by default, with the loop counter variable(s) pushed on top of the stack on every loop cycle. Each instruction is one character long, with the exception of 'loadimm' which consists of a string of hexadecimal digits. This also gives IBNIZ some flavor of an esoteric programming language. NOTE: IBNIZ has not been fully defined or implemented yet! Anything mentioned in this document may change (although major changes are unlikely). === QUICK TUTORIAL === The primary implementation of IBNIZ is interactive. You can edit the code like in a normal text editor, start/pause it with f1 and restart it with f2. The simplest example program is the empty program; it uses the loop variables directly as pixel values and audio data. A slightly longer example program would be: ^xp Which consists of three operations: ^ (xor), x (exchange) and p (pop). In the default video context mode ("TYX-video"), the machine pushes the variables T, Y and X on top of the main stack on every loop cycle. The first opcode (xor) replaces the two topmost values on the stack (Y and X) with their exclusive OR (Y XOR X). The next opcode is (exchange) corresponds to Forth's SWAP. It swaps the topmost values on the stack. So, after this operation, T is on top of the stack and Y XOR X is under it. The last opcode, 'pop' ('p') corresponds to Forth's DROP and moves the stack pointer so that the value on top of the stack gets 'popped off'. So, after the execution of the three instructions '^xp', the values T Y X have been transformed into Y XOR X. Whatever data remains in the stack is interpreted as pixel colors in the YUV colorspace (bit format VVUU.YYYY; thus, the integer part roughly corresponds to hue and the fraction part to intensity). As the range of X and Y is between -1.0 and +1.0 (FFFF.0000 .. 0000.FFFF), the picture resulting from X XOR Y will have a full intensity range but the only hues are 0000 (pure gray) and FFFF (nearly pure gray). The unit for T, by the way, is 1/60 seconds. The video stack is two video pages long. The visible page is flipped every time the stack pointer passes a page boundary. An audio example: d3r15&* In the audio context, only one value (T) is pushed on top of stack on each loop cycle. The first opcode 'd' duplicates it, 3r rotates the duplicate right by three bits, 15& ands it with hex number 15 (decimal 21) and * multiplies the result with the original T. In the audio context, T has the same rate as in video mode; the integer part increments 60 times per seconds. However, the fraction part is also used (resulting in a theoretical maximum sample rate of nearly 4 MHz). Of the values left on stack, only the fraction part is used. It is interpreted as a 16-bit unsigned linear PCM value. Regardless of the actual sampling rate of the implementation, the audio stack is one second long. IBNIZ always tries to execute programs simultaneously in video and audio contexts. There are two different modes for the video context: the previously-mentioned TYX-video (which pushes T Y X on every loop as three separate numbers) and T-video (which combines these variables in a single value). IBNIZ automatically detects the correct mode by stack usage. It is possible to separate video and audio calculation using the 'mediaswitch' opcode ('M'). The execution of these separate program portions is scheduled by VM-level logic: in normal cases, the video context loop is run 64 times per audio context loop cycle. *x~FF&* M d3r15&* IBNIZ is a universal programming language, not just an expression evaluator. The secondary stack (return stack or "rstack") makes it possible to implement advanced program control features such as loops, subroutines and recursion. It is also possible to ignore the exterior loop altogether and write to the buffers like to any random access memory as well as to read user input and to have a separate data segment for any arbitrary data. === TECHNICAL NUMBERS** === Technical specs of the default configuration: Word width: 32 bits (arithmetic in 16.16 fixed-point) Address space: 2^20 words (4 megabytes, ~3 of which free user RAM) Video output: 256x256 pixels at 60 Hz, 32 bits per pixel (VVUU.YYYY) Audio output: 61440 Hz mono (30720 Hz stereo), 16 bits per sample Computation speed: not defined yet (fully depends on underlying hardware) === FULL INSTRUCTION SET === Everything is case-sensitive here! NUMBERS symbol name stack ------ ---- ----- 0-F. loadimm (-- val) The basic numeric type is the 32-bit fixed-point number, divided into 16 bits of integer and 16 bits of fraction. The number format in the source code is upper-case hexadecimal using the digits 0-9 and A-F. The separator '.' can be used to separate the fraction part from the integer part. Several immediate numbers can be separated with a blank or comma (','). ARITHMETIC symbol name stack ------ ---- ----- + add (a b -- a+b) - sub (a b -- a-b) * mul (a b -- a*b) / div (a b -- a/b, 0 if b==0) % mod (a b -- a MOD b, 0 if b==0) q sqrt (a -- square root of a; 0 if a<0) & and (a b -- a AND b) | or (a b -- a OR b) ^ xor (a b -- a XOR b) r right (a b -- a ROR b) l left (a b -- a << b) ~ neg (a -- NOT a) s sin (a -- sin(a*2PI)) a atan (a b -- atan2(a,b)/2PI) < isneg (a -- a if a<0, else 0) > ispos (a -- a if a>0, else 0) = iszero (a -- 1 if a==0, else 0) All numbers used in arithmetic are interpreted as signed 16+16-bit fixed-point values (negative numbers in two's complement). The modulus (%) uses fractions. STACK MANIPULATION symbol name stack description ------ ---- ----- ---------- d dup (a -- a a) p pop (a --) same as Forth's DROP x exchange (a b -- b a) same as Forth's SWAP v trirot (a b c -- b c a) same as Forth's ROT ) pick (i -- val) load value from STACK[top-1-i] ( bury (val i --) store value to STACK[top-2-i] The operations 'pick' and 'bury' and 'movesp' are always wrapped within the stack range. The symbol 'v' was chosen because it resembles a triangle. EXTERIOR LOOP symbol name description ------ ---- ----------- M mediaswitch switches between audio and video context w whereami pushes exterior loop variable(s) on stack T terminate stops program execution The execution starts in the video context. When the execution wraps from the end of the program to the beginning, the VM implicitly executes 'mediaswitch' and 'whereami'. The loop variables pushed by 'whereami' depend on the stack pointer and internal video/audio frame counters. The exact operation, depending on context and mode, is as follows: context mode pushes on stack ------- ---- --------------- video TYX TTTT.0000, YYYY.YYYY, XXXX.XXXX where - YYYY.YYYY and XXXX.XXXX are between -1 and +1 (FFFF.0000 and 0000.FFFF) - TTTT is the frame counter (time in 60ths of second) video T TTTT.YYXX where - TTTT is the frame counter - YY and XX range from 00 to FF (directly from SP) audio T TTTT.TTTT where - the integer is the frame counter (same as in video) - the fraction is, well, the 65536th part thereof The current implementation changes the video context mode automatically based on stack balance and how many times 'whereami' is called. MEMORY MANIPULATION symbol name stack ------ ---- ----- @ load (addr -- val) ! store (val addr --) All the memory is addressed in 32-bit-wide chunks. There is no byte-level operation. The fractional part of the memory address is interpreted as the high part of the logical address. (e.g. 1234.FFFF refers to the address FFFF1234). In the default configuration, the top 12 bits of the address are ignored (thus, the actual address in the previous example is F1234). The total address space is therefore 1 megaword == 4 megabytes. It is divided as follows: 00000 - BFFFF free for user data C0000 - C7FFF reserved for internal registers, code, etc. C8000 - CBFFF return stack for audio context CC000 - CFFFF return stack for video context D0000 - DFFFF audio stack E0000 - EFFFF video stack page 0 F0000 - FFFFF video stack page 1 PROGRAM CONTROL Conditional execution symbol name description ------ ---- ----------- ? if (cond --) ; if cond==0, skip until 'else' or 'endif' : else skip until after next 'endif' ; endif nop; marks end of conditional block when skipping End of code is also regarded as a skip terminator in all cases. Loops symbol name description ------ ---- ----------- X times (i0 --) loop i0 times (push i0 and insptr on rstack) L loop decrement RSTACK[top-1], jump back if non-0 i index (-- i) load value from RSTACK[top-1] j outdex (-- j) load value from RSTACK[top-3] [ do begin loop (push insptr on rstack) ] while (cond --) jump back if cond!=0 J jump (v --) set instruction pointer to value v Examples of loop constructs: 100X 3i@L stores the number '3' to addresses 1..100 [1r dA0-<] shifts number right until it is below A0 The jump instruction (like all ops that manipulate instruction pointer directly) wraps around the code length (it is not possible to jump outside the program space). As the internal encoding of programs has not been defined yet, the exact addresses of the instructions are implementation-dependent. The times-loop counters (i and j) are regarded as 32-bit unsigned integers in the same way as memory addresses (.0001 = 10000). Thus, times-loops with more than 65535 steps are possible. Subroutines symbol name stack description ------ ---- ----- ----------- { defsub (i --) define subroutine (store pointer to MEM[i]) } return end of subroutine; pop insptr from rstack V visit (i --) visit subroutine pointed to by MEM[i] The return stack is used for storing the return addresses when visiting subroutines. Defsub ('{') stores the address of the next instruction to the memory address given by the value on top of stack and then skips instructions until '}' or end-of-code is reached. Return stack manipulation symbol name stack rstack description ------ ---- ----- ------ ----------- R retaddr (-- val) (val --) moves from rstack to stack P pushtors (val --) (-- val) moves from stack to rstack The return stack is cyclical just like the main stack. INPUT symbol name stack description ------ ---- ----- ------------ U userin (-- inword) get data from input device The 'userin' instruction polls data from the input device. It returns a word in the format MMKK.YYXX where: - YYXX indicates the last known position, in unsigned coordinates, of the pointing device (mouse, touch, lightpen, etc.) - KK indicates the unicode number of the last character entered on keyboard, or 0 if no character is entered. If the unicode number is above FF, it is wrapped to between 00 and FF. The value is cleared to zero (or the next character in the buffer) whenever 'U' is used. - MM is a bit structure indicating the state of click/state and a couple of keyboard keys. Bits from top to bottom: 80: click state (1 when a screen position is being clicked/touched) 40: ctrl key (1 = down) 20: alt/meta key 10: shift key 08: cursor up key 04: cursor down key 02: cursor left key 01: cursor right key DATA SEGMENT symbol name description ------ ---- ----------- G getdata (numbits -- data) $ startdata end code segment, start data segment A "data segment" containing arbitrary binary data can be defined after the program code. Startdata ($) ends the code segment and starts the data segment. When a program is started, the memory is filled with the contents of the data segment without any alignment. Getdata ('G') can be used for reading the data segment sequentially. It fetches the given number of next bits from the data segment. When it runs out of data, it wraps back to the beginning. In the source code, the data is encoded as digits that represent 1-4 bits in the memory. The following symbols are available: symbol name description ------ ---- ----------- 0-F data encodes a digitful (1-4 bits) of data. b binary sets digit length to 1 bit q quarternary sets digit length to 2 bits o octal sets digit length to 3 bits h hexadecimal sets digit length to 4 bits (default) META symbol name desc ------ ---- ---- \ comment ignore characters in source code until newline , blank nop; also whitespaces and newlines count as blank === PRIMARY IMPLEMENTATION === EDITOR COMMANDS Tab toggles the editor display on/off. When the editor is hidden, keyboard commands don't affect the editor state. Cursor keys etc. work as expected. Shift+cursor selects an area. Ctrl+up/down increments/decrements the number under cursor, with carry. Ctrl+left/right jumps to the final character of the previous or next "word" (i.e. blank-separated section). f1 runs and pauses the code. f2 resets the VM state (including timer and memory). Changes to the source code automatically recompile it but do not restart it. This makes it convenient to do runtime changes to numeric parameters etc. This functionality may change in the future. ESC exits the program. Ctrl+C/X/V/A work as copy/cut/paste/selectall. Ctrl+S saves the program to the file indicated by a line beginning with '\#file' (or if there's no such line, inserts the line '\#file untitled.ib' and uses untitled.ib as the filename. The '\#file' lines are automatically skipped when saving. COMMAND LINE OPTIONS -h Dump help on command line usage -v Dump version info -c CODE Execute code -n No autorun of loaded code The following extra options were added for creating the YouTube video: -e Dump user keystrokes to stdout -p Playback dumped user keystrokes from stdin -M Dump raw video to stdout and raw audio to stderr. 30 fps, non-realtime, yuv4mpeg2 and pcm_s16. Some commands used in this process, for reference: ./ibniz -e > events ./ibniz -M -p < events 2>vid.pcm | ffmpeg -y -i - -r 30 vid.avi ffmpeg -i vid.avi -f s16le -ar 44100 -ac 1 \ -i vid.pcm -vcodec copy vidav.avi === EXAMPLES === \ 2-character programs: *d \ TV noise (without sound) ** \ Mul-texture zoomer 9/ \ Flasher +/ \ "Jupiter storm" +% \ "Jupiter storm" in B&W /% \ Perspective mapper &* \ Sierpinski epilepsy qs \ Polyrhythmic flasher slowing down )~ \ Sliding-down squarewave \ "42 melody" d3r15&* \ Plasma sv5rvs-- \ Munching squares with a Sierpinski harmony ^x7r+Md8r& \ Xor texture zoomer v8rsdv*vv*^ \ Music from the video d6r|5*wdAr&+ \ "Opening gate" (from FreeFull) 8rw10r%w18r% \ "Spinny" (from FreeFull) sxsaxAr+waxBr+^ \ Munching squares zoomer v8rsdv*vv*^wpp8r- \ Texture tunnel ax8r+3lwd*xd*+q1x/x5r+^ \ Rotozoomer v8rds4X3)Lx~2Xv*vv*+i!L1@2@& \ Mandelbrot zoomer (76 chars) vArs1ldv*vv*0!1-1!0dFX4X1)Lv*vv*-vv2**0@+x1@+4X1)Lv*vv*+4x->?Lpp0:ppRpRE.5*; \ Julia morpher (from real_het) (97 chars) 2*2!2*3!10rdF2*s0!F9*s1!10,6! [2@d3@*4!d*2!3@d*3!3@2@+2@3@-0@+2!4@d+1@+3!4-<6@1-d6!*]6@4r.FF^1977+ \ The 122-char demo from the video 6{^^ddd***1%} 5{v8rsdv*vv*^wpp8r-} 4{v8rdsx.6+s4X3)Lx~2Xv*vv*+i!L1@2@^} 3{ax8r+3lwd*xd*+q1x/x6r+^} 2)6r3&3+V55A9^Md6r|5*wdAr&+ \ Bitmap zoomer from the video v7rs6ldv*vv*7&@xr.8&$b 00000000000000000000000000000000 00000000011110111010010011101110 00000000010000010010110100100100 00000000001000010011010011100100 00000000000100010010010100100100 00000000000010010010010100100100 00000000011110111010010011101110 00000000000000000000000000000000 === CHANGES === 1.1000 - Cut/copy/paste implemented, with system clipboard support on X11 and W32. - VM no longer eats up all CPU time if less is enough for 60 fps. - Possibility to hide on-screen display (with autohide on autorun) - Scrolling and buffer size limit check in the editor - More examples included in the distribution package - Help screen implemented 1.1800 - Clipboard bugs fixed, window icon added - Machine status panel implemented === FUTURE === Tasks in an approximate order of priority: - Fix problems that prevent IBNIZ from working in some systems - Fix other known bugs - On-screen machine status info - Improve execution speed with static code analysis and native compilation - Support resolution reduction etc for slow code/machines - Remove MSVC library dependency from Win32 build - Make it possible to limit execution speed - Make internal registers user-accessible - Implement IBNIZ as a website - Native Win32 version (without MSVC library or the statically linked SDL) - Define and implement a compact bitwise machine code - Allow self-modifying code - Support threading, shaders etc. - Native version for MS-DOS, ibniz-to-c64 compiler etc. Once we have all of these, we may call the version number 2.0. ibniz-1.18/Makefile0000644000175000017500000000251311701150253013672 0ustar viznutviznut# For normal builds; remove -DX11 -lX11 from flags if you don't have X11 CC=gcc EXE=ibniz FLAGS=`sdl-config --libs --cflags` -DX11 -lX11 all: ibniz # For win32 builds using mingw32 (you'll probably need to modify these) #CC=i586-mingw32msvc-gcc #EXE=ibniz.exe #FLAGS=-L./SDL-1.2.14/lib -I./SDL-1.2.14/include -static -lmingw32 SDL-1.2.14/lib/libSDL.a SDL-1.2.14/lib/libSDLmain.a -mwindows -lwinmm #all: ibniz.exe clean: rm -f *.o *~ ibniz vmtest ibniz.exe whole.c package: clean cd .. && cp -R src ibniz-1.18 && tar czf ibniz-1.18.tar.gz ibniz-1.18 winexe: clean mkdir ../winbuild && cp * ../winbuild && cd ../winbuild && make -f Makefile.win #$(EXE): whole.c # $(CC) -s -O3 -ffast-math -fwhole-program whole.c -o $(EXE) $(FLAGS) -lm #whole.c: vm_slow.c ui_sdl.c clipboard.c texts.i font.i vm.h ibniz.h # cat ui_sdl.c vm_slow.c clipboard.c > whole.c $(EXE): ui_sdl.o vm_slow.o clipboard.o $(CC) -Os -s ui_sdl.o vm_slow.o clipboard.o -o $(EXE) $(FLAGS) -lm ui_sdl.o: ui_sdl.c ibniz.h font.i vm.h texts.i vm.h $(CC) -c -Os ui_sdl.c -o ui_sdl.o $(FLAGS) clipboard.o: clipboard.c ibniz.h $(CC) -c -Os clipboard.c -o clipboard.o $(FLAGS) vm_slow.o: vm_slow.c ibniz.h vm.h $(CC) -c -O3 vm_slow.c -o vm_slow.o font.i: font.pl perl font.pl > font.i runtest: vmtest ./vmtest vmtest: vm_test.c vm_slow.c gcc vm_test.c vm_slow.c -o vmtest -lm ibniz-1.18/Makefile.osx0000644000175000017500000000105211701150253014477 0ustar viznutviznutC = gcc EXE = ibniz SRC = ui_sdl.c vm_slow.c clipboard.c OBJ = $(SRC:.c=.o) CFLAGS = -O2 -Wall `sdl-config --cflags` LDFLAGS = -lm `sdl-config --libs` -framework Cocoa all: $(EXE) $(EXE): $(OBJ) $(CC) $(OBJ) $(LDFLAGS) -o $@ vmtest: $(OBJ) vm_test.o $(CC) $(OBJ) vm_test.o $(LDFLAGS) -o $@ .c.o: $(CC) -c $(CFLAGS) $< -o $@ clean: rm -f *.o *~ ibniz vmtest ui_sdl.o: ui_sdl.c ibniz.h font.i vm.h texts.i clipboard.o: clipboard.c ibniz.h vm_slow.o: vm_slow.c ibniz.h vm.h font.i: font.pl perl font.pl > font.i runtest: vmtest ./vmtest ibniz-1.18/src/0000755000175000017500000000000011701150471013022 5ustar viznutviznutibniz-1.18/src/clipboard.c0000644000175000017500000001016311701151132015121 0ustar viznutviznut#include "ibniz.h" #if defined(WIN32) #include void clipboard_load() { HGLOBAL data; if (!IsClipboardFormatAvailable(CF_TEXT)) return; if (!OpenClipboard(NULL)) return; data = GetClipboardData(CF_TEXT); if(data) { char*t=(char*)GlobalLock(data); int lgt=strlen(t); if(clipboard) free(clipboard); clipboard=malloc(strlen(t)+1); strcpy(clipboard,t); GlobalUnlock(data); } CloseClipboard(); } void clipboard_store() { HGLOBAL buffer; if (!OpenClipboard(NULL)) return; EmptyClipboard(); buffer = GlobalAlloc(GMEM_DDESHARE,strlen(clipboard)+1); if(!buffer) { CloseClipboard(); return; } buffer = (char*)GlobalLock(buffer); strcpy(buffer,clipboard); GlobalUnlock(buffer); SetClipboardData(CF_TEXT,buffer); CloseClipboard(); } #elif defined(X11) #include #include #include #include struct { SDL_SysWMinfo swi; Atom cbatom; } clipbrd; void clipboard_init() { if(!clipbrd.cbatom) { SDL_GetWMInfo(&clipbrd.swi); if(!clipbrd.swi.info.x11.display) return; clipbrd.swi.info.x11.lock_func(); clipbrd.cbatom = XInternAtom(clipbrd.swi.info.x11.display, "IBNIZ_CB",False); clipbrd.swi.info.x11.unlock_func(); } } char* getcbtext(XSelectionEvent *e) { Atom type; int format,lgt; unsigned long nitems; unsigned long remaining; unsigned char *xdata; if(e->property==None) return NULL; xdata = NULL; XGetWindowProperty(e->display, e->requestor, clipbrd.cbatom, 0, 0, False, AnyPropertyType, &type, &format, &nitems, &remaining, &xdata); if(type == None) return NULL; if(xdata) XFree(xdata); xdata = NULL; XGetWindowProperty(e->display, e->requestor, clipbrd.cbatom, 0, (remaining+3)/4, False, AnyPropertyType, &type, &format, &nitems, &remaining, &xdata); if(type==None || !xdata) return NULL; lgt=(nitems*format)/8; if(lgt) XStoreBytes(clipbrd.swi.info.x11.display,xdata,lgt); XDeleteProperty(e->display,e->requestor,clipbrd.cbatom); return xdata; } char* readX11clipboard() { Window owner; owner = XGetSelectionOwner(clipbrd.swi.info.x11.display,XA_PRIMARY); if(owner==clipbrd.swi.info.x11.window) return NULL; if(owner!=None) { int i=64; XConvertSelection(clipbrd.swi.info.x11.display,XA_PRIMARY,XA_STRING, clipbrd.cbatom,clipbrd.swi.info.x11.window,CurrentTime); XFlush(clipbrd.swi.info.x11.display); for(;i;i--) { XEvent e; XNextEvent(clipbrd.swi.info.x11.display, &e); if(e.type==SelectionNotify) { if(e.xselection.property==None) return NULL; return getcbtext(&e.xselection); } } } else return NULL; } void clipboard_load() { char*cbt; clipboard_init(); if(!clipbrd.cbatom) return; clipbrd.swi.info.x11.lock_func(); cbt=readX11clipboard(); clipbrd.swi.info.x11.unlock_func(); if(cbt) { if(clipboard) free(clipboard); clipboard=cbt; } } void clipboard_store() { clipboard_init(); if(!clipbrd.cbatom) return; clipbrd.swi.info.x11.lock_func(); XSetSelectionOwner(clipbrd.swi.info.x11.display,XA_PRIMARY, clipbrd.swi.info.x11.window,CurrentTime); XFlush(clipbrd.swi.info.x11.display); clipbrd.swi.info.x11.unlock_func(); } void clipboard_handlesysreq(SDL_Event*e) { XEvent res; if(e->syswm.msg->event.xevent.type==SelectionRequest) { XSelectionRequestEvent*req= &(e->syswm.msg->event.xevent.xselectionrequest); if(req->target==XA_STRING) { XChangeProperty(clipbrd.swi.info.x11.display,req->requestor, req->property,XA_STRING,8,PropModeReplace,clipboard,strlen(clipboard)); res.xselection.property=req->property; } else res.xselection.property=None; res.xselection.type=SelectionNotify; res.xselection.display=clipbrd.swi.info.x11.display; res.xselection.requestor=req->requestor; res.xselection.target=req->target; res.xselection.time=req->time; XSendEvent(clipbrd.swi.info.x11.display,req->requestor,0,0,&res); XFlush(clipbrd.swi.info.x11.display); } } #else void clipboard_load() { } void clipboard_store() { } #endif ibniz-1.18/src/ibniz.txt0000644000175000017500000004753011701151132014702 0ustar viznutviznut Compilation in Unix-like systems that have GCC and SDL installed: make This documentation represents IBNIZ version 1.18 released on 2012-01-04. The distribution licence is the "zlib/libpng licence" (see licence.txt). === OVERVIEW === IBNIZ is a virtual machine designed for extremely compact low-level audiovisual programs. The leading design goal is usefulness as a platform for demoscene productions, glitch art and similar projects. Mainsteam software engineering aspects are considered totally irrelevant. IBNIZ stands for Ideally Bare Numeric Impression giZmo. The name also refers to Gottfried Leibniz, the 17th-century polymath who, among all, invented binary arithmetic, built the first four-operation calculating machine, and believed that the world was designed with the principle that a minimal set of rules should yield a maximal diversity. The IBNIZ virtual machine is basically a two-stack machine somewhat similar to Forth implementations but with the major difference that the stack is cyclical and also used as output buffer. The machine runs in an endless loop by default, with the loop counter variable(s) pushed on top of the stack on every loop cycle. Each instruction is one character long, with the exception of 'loadimm' which consists of a string of hexadecimal digits. This also gives IBNIZ some flavor of an esoteric programming language. NOTE: IBNIZ has not been fully defined or implemented yet! Anything mentioned in this document may change (although major changes are unlikely). === QUICK TUTORIAL === The primary implementation of IBNIZ is interactive. You can edit the code like in a normal text editor, start/pause it with f1 and restart it with f2. The simplest example program is the empty program; it uses the loop variables directly as pixel values and audio data. A slightly longer example program would be: ^xp Which consists of three operations: ^ (xor), x (exchange) and p (pop). In the default video context mode ("TYX-video"), the machine pushes the variables T, Y and X on top of the main stack on every loop cycle. The first opcode (xor) replaces the two topmost values on the stack (Y and X) with their exclusive OR (Y XOR X). The next opcode is (exchange) corresponds to Forth's SWAP. It swaps the topmost values on the stack. So, after this operation, T is on top of the stack and Y XOR X is under it. The last opcode, 'pop' ('p') corresponds to Forth's DROP and moves the stack pointer so that the value on top of the stack gets 'popped off'. So, after the execution of the three instructions '^xp', the values T Y X have been transformed into Y XOR X. Whatever data remains in the stack is interpreted as pixel colors in the YUV colorspace (bit format VVUU.YYYY; thus, the integer part roughly corresponds to hue and the fraction part to intensity). As the range of X and Y is between -1.0 and +1.0 (FFFF.0000 .. 0000.FFFF), the picture resulting from X XOR Y will have a full intensity range but the only hues are 0000 (pure gray) and FFFF (nearly pure gray). The unit for T, by the way, is 1/60 seconds. The video stack is two video pages long. The visible page is flipped every time the stack pointer passes a page boundary. An audio example: d3r15&* In the audio context, only one value (T) is pushed on top of stack on each loop cycle. The first opcode 'd' duplicates it, 3r rotates the duplicate right by three bits, 15& ands it with hex number 15 (decimal 21) and * multiplies the result with the original T. In the audio context, T has the same rate as in video mode; the integer part increments 60 times per seconds. However, the fraction part is also used (resulting in a theoretical maximum sample rate of nearly 4 MHz). Of the values left on stack, only the fraction part is used. It is interpreted as a 16-bit unsigned linear PCM value. Regardless of the actual sampling rate of the implementation, the audio stack is one second long. IBNIZ always tries to execute programs simultaneously in video and audio contexts. There are two different modes for the video context: the previously-mentioned TYX-video (which pushes T Y X on every loop as three separate numbers) and T-video (which combines these variables in a single value). IBNIZ automatically detects the correct mode by stack usage. It is possible to separate video and audio calculation using the 'mediaswitch' opcode ('M'). The execution of these separate program portions is scheduled by VM-level logic: in normal cases, the video context loop is run 64 times per audio context loop cycle. *x~FF&* M d3r15&* IBNIZ is a universal programming language, not just an expression evaluator. The secondary stack (return stack or "rstack") makes it possible to implement advanced program control features such as loops, subroutines and recursion. It is also possible to ignore the exterior loop altogether and write to the buffers like to any random access memory as well as to read user input and to have a separate data segment for any arbitrary data. === TECHNICAL NUMBERS** === Technical specs of the default configuration: Word width: 32 bits (arithmetic in 16.16 fixed-point) Address space: 2^20 words (4 megabytes, ~3 of which free user RAM) Video output: 256x256 pixels at 60 Hz, 32 bits per pixel (VVUU.YYYY) Audio output: 61440 Hz mono (30720 Hz stereo), 16 bits per sample Computation speed: not defined yet (fully depends on underlying hardware) === FULL INSTRUCTION SET === Everything is case-sensitive here! NUMBERS symbol name stack ------ ---- ----- 0-F. loadimm (-- val) The basic numeric type is the 32-bit fixed-point number, divided into 16 bits of integer and 16 bits of fraction. The number format in the source code is upper-case hexadecimal using the digits 0-9 and A-F. The separator '.' can be used to separate the fraction part from the integer part. Several immediate numbers can be separated with a blank or comma (','). ARITHMETIC symbol name stack ------ ---- ----- + add (a b -- a+b) - sub (a b -- a-b) * mul (a b -- a*b) / div (a b -- a/b, 0 if b==0) % mod (a b -- a MOD b, 0 if b==0) q sqrt (a -- square root of a; 0 if a<0) & and (a b -- a AND b) | or (a b -- a OR b) ^ xor (a b -- a XOR b) r right (a b -- a ROR b) l left (a b -- a << b) ~ neg (a -- NOT a) s sin (a -- sin(a*2PI)) a atan (a b -- atan2(a,b)/2PI) < isneg (a -- a if a<0, else 0) > ispos (a -- a if a>0, else 0) = iszero (a -- 1 if a==0, else 0) All numbers used in arithmetic are interpreted as signed 16+16-bit fixed-point values (negative numbers in two's complement). The modulus (%) uses fractions. STACK MANIPULATION symbol name stack description ------ ---- ----- ---------- d dup (a -- a a) p pop (a --) same as Forth's DROP x exchange (a b -- b a) same as Forth's SWAP v trirot (a b c -- b c a) same as Forth's ROT ) pick (i -- val) load value from STACK[top-1-i] ( bury (val i --) store value to STACK[top-2-i] The operations 'pick' and 'bury' and 'movesp' are always wrapped within the stack range. The symbol 'v' was chosen because it resembles a triangle. EXTERIOR LOOP symbol name description ------ ---- ----------- M mediaswitch switches between audio and video context w whereami pushes exterior loop variable(s) on stack T terminate stops program execution The execution starts in the video context. When the execution wraps from the end of the program to the beginning, the VM implicitly executes 'mediaswitch' and 'whereami'. The loop variables pushed by 'whereami' depend on the stack pointer and internal video/audio frame counters. The exact operation, depending on context and mode, is as follows: context mode pushes on stack ------- ---- --------------- video TYX TTTT.0000, YYYY.YYYY, XXXX.XXXX where - YYYY.YYYY and XXXX.XXXX are between -1 and +1 (FFFF.0000 and 0000.FFFF) - TTTT is the frame counter (time in 60ths of second) video T TTTT.YYXX where - TTTT is the frame counter - YY and XX range from 00 to FF (directly from SP) audio T TTTT.TTTT where - the integer is the frame counter (same as in video) - the fraction is, well, the 65536th part thereof The current implementation changes the video context mode automatically based on stack balance and how many times 'whereami' is called. MEMORY MANIPULATION symbol name stack ------ ---- ----- @ load (addr -- val) ! store (val addr --) All the memory is addressed in 32-bit-wide chunks. There is no byte-level operation. The fractional part of the memory address is interpreted as the high part of the logical address. (e.g. 1234.FFFF refers to the address FFFF1234). In the default configuration, the top 12 bits of the address are ignored (thus, the actual address in the previous example is F1234). The total address space is therefore 1 megaword == 4 megabytes. It is divided as follows: 00000 - BFFFF free for user data C0000 - C7FFF reserved for internal registers, code, etc. C8000 - CBFFF return stack for audio context CC000 - CFFFF return stack for video context D0000 - DFFFF audio stack E0000 - EFFFF video stack page 0 F0000 - FFFFF video stack page 1 PROGRAM CONTROL Conditional execution symbol name description ------ ---- ----------- ? if (cond --) ; if cond==0, skip until 'else' or 'endif' : else skip until after next 'endif' ; endif nop; marks end of conditional block when skipping End of code is also regarded as a skip terminator in all cases. Loops symbol name description ------ ---- ----------- X times (i0 --) loop i0 times (push i0 and insptr on rstack) L loop decrement RSTACK[top-1], jump back if non-0 i index (-- i) load value from RSTACK[top-1] j outdex (-- j) load value from RSTACK[top-3] [ do begin loop (push insptr on rstack) ] while (cond --) jump back if cond!=0 J jump (v --) set instruction pointer to value v Examples of loop constructs: 100X 3i@L stores the number '3' to addresses 1..100 [1r dA0-<] shifts number right until it is below A0 The jump instruction (like all ops that manipulate instruction pointer directly) wraps around the code length (it is not possible to jump outside the program space). As the internal encoding of programs has not been defined yet, the exact addresses of the instructions are implementation-dependent. The times-loop counters (i and j) are regarded as 32-bit unsigned integers in the same way as memory addresses (.0001 = 10000). Thus, times-loops with more than 65535 steps are possible. Subroutines symbol name stack description ------ ---- ----- ----------- { defsub (i --) define subroutine (store pointer to MEM[i]) } return end of subroutine; pop insptr from rstack V visit (i --) visit subroutine pointed to by MEM[i] The return stack is used for storing the return addresses when visiting subroutines. Defsub ('{') stores the address of the next instruction to the memory address given by the value on top of stack and then skips instructions until '}' or end-of-code is reached. Return stack manipulation symbol name stack rstack description ------ ---- ----- ------ ----------- R retaddr (-- val) (val --) moves from rstack to stack P pushtors (val --) (-- val) moves from stack to rstack The return stack is cyclical just like the main stack. INPUT symbol name stack description ------ ---- ----- ------------ U userin (-- inword) get data from input device The 'userin' instruction polls data from the input device. It returns a word in the format MMKK.YYXX where: - YYXX indicates the last known position, in unsigned coordinates, of the pointing device (mouse, touch, lightpen, etc.) - KK indicates the unicode number of the last character entered on keyboard, or 0 if no character is entered. If the unicode number is above FF, it is wrapped to between 00 and FF. The value is cleared to zero (or the next character in the buffer) whenever 'U' is used. - MM is a bit structure indicating the state of click/state and a couple of keyboard keys. Bits from top to bottom: 80: click state (1 when a screen position is being clicked/touched) 40: ctrl key (1 = down) 20: alt/meta key 10: shift key 08: cursor up key 04: cursor down key 02: cursor left key 01: cursor right key DATA SEGMENT symbol name description ------ ---- ----------- G getdata (numbits -- data) $ startdata end code segment, start data segment A "data segment" containing arbitrary binary data can be defined after the program code. Startdata ($) ends the code segment and starts the data segment. When a program is started, the memory is filled with the contents of the data segment without any alignment. Getdata ('G') can be used for reading the data segment sequentially. It fetches the given number of next bits from the data segment. When it runs out of data, it wraps back to the beginning. In the source code, the data is encoded as digits that represent 1-4 bits in the memory. The following symbols are available: symbol name description ------ ---- ----------- 0-F data encodes a digitful (1-4 bits) of data. b binary sets digit length to 1 bit q quarternary sets digit length to 2 bits o octal sets digit length to 3 bits h hexadecimal sets digit length to 4 bits (default) META symbol name desc ------ ---- ---- \ comment ignore characters in source code until newline , blank nop; also whitespaces and newlines count as blank === PRIMARY IMPLEMENTATION === EDITOR COMMANDS Tab toggles the editor display on/off. When the editor is hidden, keyboard commands don't affect the editor state. Cursor keys etc. work as expected. Shift+cursor selects an area. Ctrl+up/down increments/decrements the number under cursor, with carry. Ctrl+left/right jumps to the final character of the previous or next "word" (i.e. blank-separated section). f1 runs and pauses the code. f2 resets the VM state (including timer and memory). Changes to the source code automatically recompile it but do not restart it. This makes it convenient to do runtime changes to numeric parameters etc. This functionality may change in the future. ESC exits the program. Ctrl+C/X/V/A work as copy/cut/paste/selectall. Ctrl+S saves the program to the file indicated by a line beginning with '\#file' (or if there's no such line, inserts the line '\#file untitled.ib' and uses untitled.ib as the filename. The '\#file' lines are automatically skipped when saving. COMMAND LINE OPTIONS -h Dump help on command line usage -v Dump version info -c CODE Execute code -n No autorun of loaded code The following extra options were added for creating the YouTube video: -e Dump user keystrokes to stdout -p Playback dumped user keystrokes from stdin -M Dump raw video to stdout and raw audio to stderr. 30 fps, non-realtime, yuv4mpeg2 and pcm_s16. Some commands used in this process, for reference: ./ibniz -e > events ./ibniz -M -p < events 2>vid.pcm | ffmpeg -y -i - -r 30 vid.avi ffmpeg -i vid.avi -f s16le -ar 44100 -ac 1 \ -i vid.pcm -vcodec copy vidav.avi === EXAMPLES === \ 2-character programs: *d \ TV noise (without sound) ** \ Mul-texture zoomer 9/ \ Flasher +/ \ "Jupiter storm" +% \ "Jupiter storm" in B&W /% \ Perspective mapper &* \ Sierpinski epilepsy qs \ Polyrhythmic flasher slowing down )~ \ Sliding-down squarewave \ "42 melody" d3r15&* \ Plasma sv5rvs-- \ Munching squares with a Sierpinski harmony ^x7r+Md8r& \ Xor texture zoomer v8rsdv*vv*^ \ Music from the video d6r|5*wdAr&+ \ "Opening gate" (from FreeFull) 8rw10r%w18r% \ "Spinny" (from FreeFull) sxsaxAr+waxBr+^ \ Munching squares zoomer v8rsdv*vv*^wpp8r- \ Texture tunnel ax8r+3lwd*xd*+q1x/x5r+^ \ Rotozoomer v8rds4X3)Lx~2Xv*vv*+i!L1@2@& \ Mandelbrot zoomer (76 chars) vArs1ldv*vv*0!1-1!0dFX4X1)Lv*vv*-vv2**0@+x1@+4X1)Lv*vv*+4x->?Lpp0:ppRpRE.5*; \ Julia morpher (from real_het) (97 chars) 2*2!2*3!10rdF2*s0!F9*s1!10,6! [2@d3@*4!d*2!3@d*3!3@2@+2@3@-0@+2!4@d+1@+3!4-<6@1-d6!*]6@4r.FF^1977+ \ The 122-char demo from the video 6{^^ddd***1%} 5{v8rsdv*vv*^wpp8r-} 4{v8rdsx.6+s4X3)Lx~2Xv*vv*+i!L1@2@^} 3{ax8r+3lwd*xd*+q1x/x6r+^} 2)6r3&3+V55A9^Md6r|5*wdAr&+ \ Bitmap zoomer from the video v7rs6ldv*vv*7&@xr.8&$b 00000000000000000000000000000000 00000000011110111010010011101110 00000000010000010010110100100100 00000000001000010011010011100100 00000000000100010010010100100100 00000000000010010010010100100100 00000000011110111010010011101110 00000000000000000000000000000000 === CHANGES === 1.1000 - Cut/copy/paste implemented, with system clipboard support on X11 and W32. - VM no longer eats up all CPU time if less is enough for 60 fps. - Possibility to hide on-screen display (with autohide on autorun) - Scrolling and buffer size limit check in the editor - More examples included in the distribution package - Help screen implemented 1.1800 - Clipboard bugs fixed, window icon added - Machine status panel implemented === FUTURE === Tasks in an approximate order of priority: - Fix problems that prevent IBNIZ from working in some systems - Fix other known bugs - On-screen machine status info - Improve execution speed with static code analysis and native compilation - Support resolution reduction etc for slow code/machines - Remove MSVC library dependency from Win32 build - Make it possible to limit execution speed - Make internal registers user-accessible - Implement IBNIZ as a website - Native Win32 version (without MSVC library or the statically linked SDL) - Define and implement a compact bitwise machine code - Allow self-modifying code - Support threading, shaders etc. - Native version for MS-DOS, ibniz-to-c64 compiler etc. Once we have all of these, we may call the version number 2.0. ibniz-1.18/src/Makefile0000644000175000017500000000246611701151132014465 0ustar viznutviznut# For normal builds; remove -DX11 -lX11 from flags if you don't have X11 CC=gcc EXE=ibniz FLAGS=`sdl-config --libs --cflags` -DX11 -lX11 all: ibniz # For win32 builds using mingw32 (you'll probably need to modify these) #CC=i586-mingw32msvc-gcc #EXE=ibniz.exe #FLAGS=-L./SDL-1.2.14/lib -I./SDL-1.2.14/include -static -lmingw32 SDL-1.2.14/lib/libSDL.a SDL-1.2.14/lib/libSDLmain.a -mwindows -lwinmm #all: ibniz.exe clean: rm -f *.o *~ ibniz vmtest ibniz.exe whole.c package: clean cd .. && cp -R src ibniz-1.18 && tar czf ibniz-1.18.tar.gz ibniz-1.18 winexe: clean cp * ../winbuild && cd ../winbuild && make -f Makefile.win #$(EXE): whole.c # $(CC) -s -O3 -ffast-math -fwhole-program whole.c -o $(EXE) $(FLAGS) -lm #whole.c: vm_slow.c ui_sdl.c clipboard.c texts.i font.i vm.h ibniz.h # cat ui_sdl.c vm_slow.c clipboard.c > whole.c $(EXE): ui_sdl.o vm_slow.o clipboard.o $(CC) -Os -s ui_sdl.o vm_slow.o clipboard.o -o $(EXE) $(FLAGS) -lm ui_sdl.o: ui_sdl.c ibniz.h font.i vm.h texts.i vm.h $(CC) -c -Os ui_sdl.c -o ui_sdl.o $(FLAGS) clipboard.o: clipboard.c ibniz.h $(CC) -c -Os clipboard.c -o clipboard.o $(FLAGS) vm_slow.o: vm_slow.c ibniz.h vm.h $(CC) -c -O3 vm_slow.c -o vm_slow.o font.i: font.pl perl font.pl > font.i runtest: vmtest ./vmtest vmtest: vm_test.c vm_slow.c gcc vm_test.c vm_slow.c -o vmtest -lm ibniz-1.18/src/Makefile.osx0000644000175000017500000000105211701151132015263 0ustar viznutviznutC = gcc EXE = ibniz SRC = ui_sdl.c vm_slow.c clipboard.c OBJ = $(SRC:.c=.o) CFLAGS = -O2 -Wall `sdl-config --cflags` LDFLAGS = -lm `sdl-config --libs` -framework Cocoa all: $(EXE) $(EXE): $(OBJ) $(CC) $(OBJ) $(LDFLAGS) -o $@ vmtest: $(OBJ) vm_test.o $(CC) $(OBJ) vm_test.o $(LDFLAGS) -o $@ .c.o: $(CC) -c $(CFLAGS) $< -o $@ clean: rm -f *.o *~ ibniz vmtest ui_sdl.o: ui_sdl.c ibniz.h font.i vm.h texts.i clipboard.o: clipboard.c ibniz.h vm_slow.o: vm_slow.c ibniz.h vm.h font.i: font.pl perl font.pl > font.i runtest: vmtest ./vmtest ibniz-1.18/src/licence.txt0000644000175000017500000000176711701151132015173 0ustar viznutviznutIBNIZ ("Ideally Bare Numeric Impression giZmo") Copyright (C) 2011 Ville-Matias Heikkila This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Ville-Matias Heikkila viznut@low.fi (For more information about this licence, look for "zlib license") ibniz-1.18/src/texts.i0000644000175000017500000000606511701151132014345 0ustar viznutviznutchar*usagetext= "usage: %s [file_to_load] [options]\n" "where options include:\n" " -h(elp) Dump this text\n" " -v(ersion) Dump version info\n" " -c(ode) CODE Execute code\n" " -n(oautorun) Do not autorun loaded code or autohide OSD\n"; // " -e(ventlog) Dump user events to stdout\n" // " -p(layback) Play events back from stdin\n" // " -M(edia) Dump media data (YUV video to stdout)\n" char*versiontext= "IBNIZ version 1.1800"; char*welcometext= "\n" "\n" "\n" "\n" "\\ IBNIZ 1.1800\n" "\\\n" "\\ Press f12 for help.\n"; char*helpscreen= //////////////////////////////// "IBNIZ quick reference (F12:quit)" "Use cursor keys to move around\n" "\n" "======= Keyboard commands ======" "\n" "f1 start/pause f12 help" "f2 reset/restart esc quit" "tab hide/show editor ctrl+s save" "\n" "shift+cursor .... select area\n" "ctrl+left/right . jump by words " "ctrl+up/down .... inc/dec number" "ctrl+x/c/v ...... cut/copy/paste" "\n" "======== IBNIZ language ========" "\n" "One character per operation.\n" "Number format 16.16 fixedpoint.\n" "Immediates in uppercase hex.\n" "Implicit whole-program loop\n" " with 'Mw' on each cycle.\n" "\n" "Arithmetic:\n" "\n" "+ - * / % & | ^ ~ are as in C\n" "\n" "q:sqrt s:sin a:atan2 r:ror l:shl" "\n" "< : zero if <0 else keep\n" "> : zero if >0 else keep\n" "= : 1 if zero else 0\n" "\n" "Stack manipulation:\n" "\n" " 1 d = 1 1 2 1 x = 1 2\n" "1 1 p = 1 3 2 1 v = 2 1 3\n" "\n" "N) = copy from N places down\n" "N( = store to N places down\n" "\n" "Memory manipulation:\n" "\n" "N@ = load value from MEM[N]\n" "MN! = store value M to MEM[N]\n" "\n" "Conditionals:\n" "\n" "N?M; = if N!=0 then M\n" "N?M:O; = if N!=0 then M else O\n" "\n" "Loops:\n" "\n" "NX...L execute '...' N times\n" "i = index of inner X loop\n" "j = index of outer X loop\n" "[...N] repeat '...' until N==0\n" "\n" "Subroutines:\n" "\n" "N{...} define subroutine\n" " (store pointer to MEM[N]) " "NV run subroutine N\n" "\n" "Return stack:\n" "\n" "R pop from rstack to stack\n" "P pop from stack to rstack\n" "\n" "User input:\n" "\n" "U return SSKK.YYXX where\n" " SS = click & crskey state " " KK = last key (~ascii)\n" " YYXX = mouse position\n" "\n" "Data segment:\n" "\n" "NG fetch N bits of data\n" "$ start data segment where\n" "0-F data digits\n" "bqoh digit size 1-4 bits\n" "\n" "Data segment is stored in\n" "MEM[0...] on each VM reset\n" "\n" "Specials:\n" "\n" "w : push loop vars (t or t y x) " "M : separate audio/vidoe code\n" "T : terminate program\n" "\\ : comment line\n" ", : blank, separate numbers\n" "\n" "======= Examples =======\n" "\n" "Copy to editor and run:\n\n" "^x6r-\n\n" "ddd***\n\n" "d3r15&*\n\n" "dd6r*3r&\n\n" "^x7r+Md8r&\n\n" "v8rsdv*vv*^\n\n" "d6r|5*wdAr&+\n\n" "v8rsdv*vv*^wpp8r-\n\n" "ax8r+3lwd*xd*+q1x/x5r+^\n\n" "v8rds4X3)Lx~2Xv*vv*+i!L1@2@&\n\n" "6{^^ddd***1%}5{v8rsdv*vv*^wpp8r-}4{v8rdsx.6+s4X3)Lx~2Xv*vv*+i!L1@2@^}" "3{ax8r+3lwd*xd*+q1x/x6r+^}2)6r3&3+V55A9^Md6r|5*wdAr&+\n\n" "Full docs & latest IBNIZ:\n" "http://pelulamu.net/ibniz/\n"; ibniz-1.18/src/Makefile.win0000644000175000017500000000212211701151132015246 0ustar viznutviznut# For normal builds; remove -DX11 -lX11 from flags if you don't have X11 CC=i586-mingw32msvc-gcc EXE=ibniz.exe FLAGS=-L./SDL-1.2.14/lib -I./SDL-1.2.14/include -static -lmingw32 SDL-1.2.14/lib/libSDL.a SDL-1.2.14/lib/libSDLmain.a -mwindows -lwinmm all: ibniz.exe clean: rm -f *.o *~ ibniz vmtest ibniz.exe whole.c package: clean cd .. && cp -R IBNIZ ibniz-1.2 && tar czf ibniz-1.2.tar.gz ibniz-1.2 $(EXE): ui_sdl.o vm_slow.o clipboard.o $(CC) -Os -s ui_sdl.o vm_slow.o clipboard.o -o $(EXE) $(FLAGS) -lm #$(EXE): whole.c # $(CC) -s -O3 -ffast-math -fwhole-program whole.c -o $(EXE) $(FLAGS) -lm #whole.c: vm_slow.c ui_sdl.c clipboard.c texts.i font.i vm.h ibniz.h # cat ui_sdl.c vm_slow.c clipboard.c > whole.c ui_sdl.o: ui_sdl.c ibniz.h font.i vm.h texts.i vm.h $(CC) -c -Os ui_sdl.c -o ui_sdl.o $(FLAGS) clipboard.o: clipboard.c ibniz.h $(CC) -c -Os clipboard.c -o clipboard.o $(FLAGS) vm_slow.o: vm_slow.c ibniz.h vm.h $(CC) -c -O3 vm_slow.c -o vm_slow.o font.i: font.pl perl font.pl > font.i runtest: vmtest ./vmtest vmtest: vm_test.c vm_slow.c gcc vm_test.c vm_slow.c -o vmtest -lm ibniz-1.18/src/vm_slow.c0000644000175000017500000003454611701151132014663 0ustar viznutviznut#include #include #include #include #include "ibniz.h" #define MAXCODESIZE 4096 #define MAXDATASIZE 4096 #define OP_LOADIMM '0' #define ROL(a,s) ((((uint32_t)(a))<<(s))|(((uint32_t)(a))>>(32-(s)))) #define ROR(a,s) ((((uint32_t)(a))>>(s))|(((uint32_t)(a))<<(32-(s)))) #define MOVESP(steps) vm.sp=(vm.sp+(steps))&vm.stackmask #define MOVERSP(steps) vm.rsp=(vm.rsp+(steps))&vm.rstackmask char compiled_code[MAXCODESIZE]; uint32_t compiled_data[MAXDATASIZE]; uint32_t compiled_hints[MAXCODESIZE]; void pushmediavariables(); uint32_t getdatabits(int n) { int s=(32-n-(vm.dataptr&31)); uint32_t mask; uint32_t a; if(n<=0 || vm.datalgt<=0) return 0; mask=(1<=0) a=(compiled_data[vm.dataptr>>5]>>s)&mask; else a=((compiled_data[vm.dataptr>>5]<<(0-s))| (compiled_data[(vm.dataptr>>5)+1]>>(32+s)))&mask; vm.dataptr=(vm.dataptr+n)%vm.datalgt; return a; } void vm_compile(char*src) { char*d=compiled_code; uint32_t*hd=compiled_hints; uint32_t num; char*s,nummode=0,shift=0; int i,j; s=src; /* parse immediates, skip comments & whitespaces */ for(;;) { char a=*s++; if((!a) || (a>='!' && a<='~')) { if(a=='.' || (a>='0' && a<='9') || (a>='A' && a<='F')) { if(nummode==0) { num=0; shift=16; nummode=1; } if(a=='.') { if(nummode==2) { *d++=OP_LOADIMM; *hd++=num; num=0; } nummode=2; shift=12; } else { char digit=(a>='A'?a-'A'+10:a-'0'); if(nummode==1) num=ROL(num,4); num|=digit<=0) { compiled_data[vm.datalgt>>5]|=a<>5)+1]=0; } else { compiled_data[vm.datalgt>>5]|=a>>(0-s); compiled_data[(vm.datalgt>>5)+1]=a<<(32+s); } vm.datalgt+=digitsz; } break; } } /* fill last 2 words to ease fetch */ {int pad=vm.datalgt&31; if(pad) { int i=pad; while(i<32) { compiled_data[vm.datalgt>>5]|=compiled_data[0]>>i; i*=2; } } if(!pad) compiled_data[(vm.datalgt>>5)+1]=compiled_data[0]; else { compiled_data[(vm.datalgt>>5)+1]= (compiled_data[0]<<(32-pad)) | (compiled_data[1]>>pad); } } } /* precalculate skip points */ vm.codelgt=d-compiled_code; for(i=0;;i++) { int j=i+1,seek0=0,seek1=0,seek2=0; char a=compiled_code[i]; if(a=='\0') { seek0='M'; j=0; } if(a=='M') seek0='M'; else if(a=='?') { seek0=';'; seek1=':'; } else if(a==':') seek0=';'; else if(a=='{') { seek0='}'; } if(seek0) { for(;;j++) { int a=compiled_code[j]; if(a=='\0' || a==seek0 || a==seek1) { if(i==j || a==0) compiled_hints[i]=0; else compiled_hints[i]=j+1; break; } } } if(a=='\0') break; } /* DEBUG: dump code */ /* for(i=0;i>5]); } */ } void vm_init() { /* video context */ vm.stack=vm.mem+0xE0000; vm.stackmask=0x1ffff; vm.sp=0; vm.rstack=vm.mem+0xCC000; vm.rstackmask=0x3FFF; vm.rsp=0; /* audio context */ vm.costack=vm.mem+0xD0000; vm.costackmask=0xffff; vm.cosp=1; // to avoid audio skipping bug at start vm.corstack=vm.mem+0xC8000; vm.corstackmask=0x3FFF; vm.corsp=0; /* state */ vm.ip=compiled_code; vm.mediacontext=0; vm.videomode=0; vm.audiomode=0; vm.visiblepage=1; vm.dataptr=0; vm.userinput=0; vm.stopped=0; vm.audiotime=vm.videotime=gettimevalue(); vm.spchange[0]=vm.spchange[1]=0; vm.wcount[0]=vm.wcount[1]=0; vm.currentwcount[0]=vm.currentwcount[1]=0; vm.prevsp[0]=vm.prevsp[1]=0; /* zero out memory */ if(!vm.datalgt) memset(vm.mem,0,MEMSIZE*sizeof(uint32_t)); else { int i; vm.dataptr=0; for(i=0;i>16)&1)^1; //vm.visiblepage^=1; for(;;) { uint32_t newt=gettimevalue(); if(newt!=vm.videotime) break; waitfortimechange(); } vm.videotime=gettimevalue(); } void pushmediavariables() { vm.currentwcount[vm.mediacontext]++; if(vm.mediacontext==0) { int p=vm.sp&65535; if(vm.videomode==0) { if(vm.visiblepage==(vm.sp>>16)) { flipvideopage(); } MOVESP(1); vm.stack[vm.sp]=vm.videotime<<16; MOVESP(1); vm.stack[vm.sp]=(p<<1)-0x10000; MOVESP(1); vm.stack[vm.sp]=((p&255)<<9)-0x10000; } else { if(!p) { flipvideopage(); } MOVESP(1); vm.stack[vm.sp]=(vm.videotime<<16)|p; } } else { if(!vm.sp) // todo we need something better { vm.audiotime+=64; } MOVESP(1); vm.stack[vm.sp]=vm.audiotime*65536+vm.sp*64; // fprintf(stderr,"%x\n",vm.stack[vm.sp]); } } #define CYCLESPERRUN 10223 int vm_run() { int cycles; if(vm.stopped) return 0; for(cycles=CYCLESPERRUN;cycles;cycles--) { char op=*vm.ip++; int32_t*a=&vm.stack[vm.sp],*b; switch(op) { /*** NUMBERS ***/ case(OP_LOADIMM): MOVESP(1); vm.stack[vm.sp]=compiled_hints[vm.ip-1-compiled_code]; break; /*** ARITHMETIC ***/ case('+'): // (b a -- a+b) MOVESP(-1); vm.stack[vm.sp]+=*a; break; case('-'): // (b a -- a-b) MOVESP(-1); vm.stack[vm.sp]-=*a; break; case('*'): // (b a -- a*b) MOVESP(-1); b=&vm.stack[vm.sp]; {int64_t m=*a; m*=((int32_t)*b); *b=m>>16; } break; case('/'): // (b a -- a/b) MOVESP(-1); b=&vm.stack[vm.sp]; if(!*a)*b=0; else {int64_t m=*b; m<<=16; m/=((int32_t)*a); *b=m;} break; case('%'): // (b a -- a%b) MOVESP(-1); b=&vm.stack[vm.sp]; if(!*a)*b=0; else *b=(*b%*a); break; case('q'): // (a -- sqrt(a), 0 if a<0) if(*a<0) *a=0; else *a=sqrt(*a/65536.0)*65536.0; break; case('&'): // (b a -- a&b) MOVESP(-1); vm.stack[vm.sp]&=*a; break; case('|'): // (b a -- a|b) MOVESP(-1); vm.stack[vm.sp]|=*a; break; case('^'): // (b a -- a^b) MOVESP(-1); vm.stack[vm.sp]^=*a; break; case('r'): // (b a -- b ror a) MOVESP(-1); b=&vm.stack[vm.sp]; {int steps=(*a>>16)&31; *b=ROR(*b,steps); } break; case('l'): // (b a -- b >> a) MOVESP(-1); b=&vm.stack[vm.sp]; {int steps=(*a>>16)&63; uint32_t w=*b; if(steps<32) *b=(w<>(steps-32)); } break; case('~'): // (a -- NOT a) *a=~*a; break; case('s'): // (a -- sin(a)) *a=sin(*a*(2*M_PI/65536.0))*65536.0; break; case('a'): // (b a -- atan2(a,b)) MOVESP(-1); b=&vm.stack[vm.sp]; *b=atan2(*a,*b)*(65536.0/(2*M_PI)); break; case('<'): // (a -- a<0?a:0) if(*a>=0)*a=0; break; case('>'): // (a -- a>0?a:0) if(*a&0x80000000)*a=0; break; case('='): // (a -- a==0?1:0) if(*a)*a=0x10000;else *a=0; break; /*** STACK MANIPULATION ***/ case('d'): // (a -- a a) MOVESP(1); vm.stack[vm.sp]=*a; break; case('p'): // (a --) MOVESP(-1); break; case('x'): // (b a -- a b) // forth: SWAP {int32_t tmp=*a; b=&vm.stack[(vm.sp-1)&vm.stackmask]; *a=*b; *b=tmp;} break; case('v'): // (c b a -- b a c) // forth: ROT {int32_t a_v=*a,*c; b=&vm.stack[(vm.sp-1)&vm.stackmask]; c=&vm.stack[(vm.sp-2)&vm.stackmask]; *a=*c; *c=*b; *b=a_v;} break; case(')'): // pick from STACK[top-1-i] *a=vm.stack[(vm.sp-1-ROL(*a,16))&vm.stackmask]; break; case('('): // store to STACK[top-2-i] MOVESP(-1); b=&vm.stack[vm.sp]; MOVESP(-1); vm.stack[(vm.sp-ROL(*a,16))&vm.stackmask]=*b; break; case('z'): MOVESP(1); vm.stack[vm.sp]=ROL(((vm.stack+vm.sp)-vm.mem),16); break; /*** EXTERIOR LOOP ***/ case('M'): // media switch stepmediacontext(compiled_hints[vm.ip-compiled_code-1],0); pushmediavariables(); break; case('\0'): // end of code //vm.ip=compiled_code; // or top of rstack (don't pop it) stepmediacontext(compiled_hints[vm.ip-compiled_code-1],1); pushmediavariables(); break; case('w'): // whereami pushmediavariables(); break; case('T'): // terminate program vm.ip--; vm.stopped=1; return CYCLESPERRUN-cycles; /*** MEMORY MANIPULATION ***/ case('@'): // (addr -- val) *a=vm.mem[ROL(*a,16)&(MEMSIZE-1)]; break; case('!'): // (val addr --) MOVESP(-1); b=&vm.stack[vm.sp]; MOVESP(-1); vm.mem[ROL(*a,16)&(MEMSIZE-1)]=*b; break; /*** PROGRAM CONTROL: Conditional execution ***/ case('?'): // if MOVESP(-1); if(*a!=0) break; case(':'): // then vm.ip=compiled_code+compiled_hints[vm.ip-compiled_code-1]; case(';'): // endif/nop break; /*** PROGRAM CONTROL: Loops ***/ case('i'): // i counter MOVESP(1); vm.stack[vm.sp]=ROL(vm.rstack[(vm.rsp-1)&vm.rstackmask],16); break; case('j'): // j counter MOVESP(1); vm.stack[vm.sp]=ROL(vm.rstack[(vm.rsp-3)&vm.rstackmask],16); break; case('X'): // times MOVERSP(1); MOVESP(-1); vm.rstack[vm.rsp]=ROL(*a,16); case('['): // do MOVERSP(1); vm.rstack[vm.rsp]=vm.ip-compiled_code; break; case('L'): // loop {uint32_t*i=&vm.rstack[(vm.rsp-1)&vm.rstackmask]; (*i)--; if(*i==0) MOVERSP(-2); else vm.ip=(vm.rstack[vm.rsp]%vm.codelgt)+compiled_code; } break; case(']'): // while MOVESP(-1); if(*a) vm.ip=(vm.rstack[vm.rsp]%vm.codelgt)+compiled_code; else MOVERSP(-1); break; case('J'): // jump {int point=*a%vm.codelgt; // !!! addressing will change MOVESP(-1); vm.ip=compiled_code+point;} break; /*** PROGRAM CONTROL: Subroutines ***/ case('{'): // defsub MOVESP(-1); vm.mem[ROL(*a,16)&(MEMSIZE-1)]=vm.ip-compiled_code; vm.ip=compiled_code+compiled_hints[vm.ip-1-compiled_code]; break; case('}'): // ret vm.ip=compiled_code+(vm.rstack[vm.rsp]%vm.codelgt); MOVERSP(-1); break; case('V'): // visit MOVESP(-1); MOVERSP(1); vm.rstack[vm.rsp]=vm.ip-compiled_code; vm.ip=((vm.mem[ROL(*a,16)&(MEMSIZE-1)])%vm.codelgt)+compiled_code; break; /*** PROGRAM CONTROL: Rstack manipulation ***/ case('R'): // pull from rstack to mainstack MOVESP(1); vm.stack[vm.sp]=ROL(vm.rstack[vm.rsp],16); MOVERSP(-1); break; case('P'): // push from stack to rstack MOVERSP(1); vm.rstack[vm.rsp]=ROL(*a,16); MOVESP(-1); break; /*** INPUT ***/ case('U'): // userinput MOVESP(1); vm.stack[vm.sp]=vm.userinput; vm.userinput&=0xff00ffff; break; /*** DATA SEGMENT ***/ case('G'): // getbits *a=ROL(getdatabits((*a>>16)&31),16); break; } } return CYCLESPERRUN; } ibniz-1.18/src/examples/0000755000175000017500000000000011701150471014640 5ustar viznutviznutibniz-1.18/src/examples/ibnizdemo.ib0000644000175000017500000000017211701151132017127 0ustar viznutviznut6{^^ddd***1%}5{v8rsdv*vv*^wpp8r-}4{v8rdsx.6+s4X3)Lx~2Xv*vv*+i!L1@2@^}3{ax8r+3lwd*xd*+q1x/x6r+^}2)6r3&3+V55A9^Md6r|5*wdAr&+ibniz-1.18/src/examples/bitmapzoomer.ib0000644000175000017500000000043111701151132017655 0ustar viznutviznutv7rs6ldv*vv*7&@xr.8& $b 0000000000000000000000000000000000000000011110111010010011101110000000000100000100101101001001000000000000100001001101001110010000000000000100010010010100100100000000000000100100100101001001000000000001111011101001001110111000000000000000000000000000000000 ibniz-1.18/src/examples/oneliners.ib0000644000175000017500000000030411701151132017142 0ustar viznutviznut\ Some examples \ (uncomment and run) \ ^x6r- \ ddd*** \ d3r15&* \ dd6r*3r& \ ^x7r+Md8r& \ v8rsdv*vv*^ \ d6r|5*wdAr&+ \ v8rsdv*vv*^wpp8r- \ ax8r+3lwd*xd*+q1x/x5r+^ \ v8rds4X3)Lx~2Xv*vv*+i!L1@2@& ibniz-1.18/src/vm.h0000644000175000017500000000150411701151132013610 0ustar viznutviznut#ifndef VM_H #define MEMSIZE 0x100000 GLOBAL struct { char *ip; int32_t*stack; uint32_t sp; uint32_t stackmask; uint32_t*rstack; uint32_t rsp; uint32_t rstackmask; int32_t*costack; uint32_t cosp; uint32_t costackmask; uint32_t*corstack; uint32_t corsp; uint32_t corstackmask; char mediacontext; /* 0=video, 1=audio, 2=stdio */ char videomode; /* 0=txy, 1=t */ char audiomode; /* 0=mono, 1=stereo */ char preferredmediacontext; char visiblepage; char stopped; uint32_t videotime; uint32_t audiotime; uint32_t prevsp[2]; uint32_t prevstackval[2]; int currentwcount[2]; int16_t spchange[2]; int wcount[2]; int codelgt; int datalgt; int dataptr; uint32_t userinput; int32_t mem[MEMSIZE]; } vm; #endif ibniz-1.18/src/font.pl0000755000175000017500000001576611701151132014342 0ustar viznutviznut#!/usr/bin/env perl $a=< #else #include #endif #define IBNIZ_MAIN #include "ibniz.h" #include "texts.i" struct { SDL_Surface*s; SDL_Overlay*o; SDL_AudioSpec as; int winsz,xmargin,ymargin; } sdl; struct { char runstat; uint32_t timercorr; uint32_t paused_since; uint32_t auplayptr; uint32_t auplaytime; uint32_t bmtime; int64_t cyclecounter; int framecounter; char audio_off; char osd_visible; char benchmark_mode; float mops; float fps; char opt_dumpkeys; char opt_nonrealtime; char opt_playback; char opt_dumpmedia; } ui; struct { //int width; //int height; int framecount; int subframe; char audiopaused; } dumper; #define WIDTH 256 #define EDITBUFSZ (65536*2) struct { char*cursor; char*selectstart; char*selectend; char*selectbase; char readonly; int firsty; char*textbuffer; } ed,ed_parallel; uint8_t font[]= { #include "font.i" }; #ifndef WIN32 # define PLAYBACKGAP 4 #else # define PLAYBACKGAP 16 #endif #define DEBUG /*** rendering of videostack and osd ***/ void drawChar8x8(uint8_t*d,uint8_t*s,uint32_t fg,uint32_t bg) { int y,x; for(y=0;y<8;y++) { int bitmap=s[y]; uint8_t*dd=d+WIDTH*y*2; for(x=0;x<16;x+=2) { dd[x]=(bitmap&128)?fg:bg; bitmap<<=1; } } } void drawTextBuffer() { int x=0,y=ed.firsty; int scroll=0; char*b=ed.textbuffer; char*lightstart=ed.cursor,*lightend=ed.cursor; if(ed.selectbase) { lightstart=ed.selectstart; lightend=ed.selectend; } for(;;) { int a=*b&127; int fg=0xffffff; int bg=0x000000; if(b>=lightstart && b<=lightend) { fg=0x000000; bg=0xffffff; if(y<0) scroll=-y; else if(y>=28) scroll=27-y; } if(y>=0 && y<28) { drawChar8x8( ((uint8_t*)(sdl.o->pixels[0]))+x*16+y*WIDTH*16, font+(a>=32?a-32:0)*8,fg,bg); } x++; if(x>=32) { y++; x=0; } if(a=='\n') { y++; x=0; } if(!a) break; b++; } ed.firsty+=scroll; } void drawString(char*s,int x,int y) { int fg=0xffffff; int bg=0x000000; while(*s) { int a=*s; drawChar8x8( ((uint8_t*)(sdl.o->pixels[0]))+x*16+y*WIDTH*16, font+(a>=32?a-32:0)*8,fg,bg); s++; x++; } } void drawStatusPanel() { char buf[24]; int sgn,spc; uint32_t a; sprintf(buf,"T=%04X",gettimevalue()&0xFFFF); drawString(buf,0,28); if(ui.runstat) { if(ui.mops>0) { sprintf(buf,"%3.3f Mops%c",ui.mops, ui.benchmark_mode?'!':' '); drawString(buf,0,30); } if(ui.fps>0) { sprintf(buf,"%2.4f fps",ui.fps); drawString(buf,0,31); } } spc=vm.spchange[0]; sgn='+'; if(spc<0) { sgn='-'; spc=0-spc; } if(spc>15) spc=15; sprintf(buf,"VIDEO S=%05X (%c%X)",vm.prevsp[0]&0x1FFFF,sgn,spc); drawString(buf,13,28); drawString(vm.videomode?"t":"tyx",13,29); a=vm.prevstackval[0]; sprintf(buf,"%04X.%04X",(a>>16)&0xFFFF,a&0xFFFF); drawString(buf,21,29); spc=vm.spchange[1]; sgn='+'; if(spc<0) { sgn='-'; spc=0-spc; } if(spc>15) spc=15; sprintf(buf,"AUDIO S=%05X (%c%X)",vm.prevsp[1]&0x1FFFF,sgn,spc); drawString(buf,13,30); drawString(ui.audio_off?"off": (vm.audiomode?"ster":"mono"),13,31); a=vm.prevstackval[1]; sprintf(buf,"%04X.%04X",(a>>16)&0xFFFF,a&0xFFFF); drawString(buf,21,31); } void showyuv() { SDL_Rect area={sdl.xmargin,sdl.ymargin,sdl.winsz,sdl.winsz}; SDL_DisplayYUVOverlay(sdl.o,&area); } void updatescreen() { int x,y; uint32_t*s=vm.mem+0xE0000+(vm.visiblepage<<16); for(y=0;y<256;y++) for(x=0;x<128;x++) { uint32_t b=s[0],a=s[1]; // little_endian, TODO: support big-endian a=(a&0xff000000)| ((a<<8)&0x00ff0000)| ((b>>8)&0x0000ffff); a^=0x80008000; #if SDL_BYTEORDER == SDL_BIG_ENDIAN // big-endian not tested! a=(((a>>24)&0xff)) | (((a>>16)&0xff)<<8) | (((a>>8)&0xff)<<16) | (((a)&0xff)<<24); #endif ((uint32_t*)(sdl.o->pixels[0]))[(WIDTH/2)*y+x]=a; s+=2; } if(ui.osd_visible) { drawTextBuffer(); drawStatusPanel(); } showyuv(); } /*** timer-related ***/ int getticks() { if(!ui.opt_nonrealtime) #ifdef __APPLE__ /* please find a proper fix */ return SDL_GetTicks()/3; #else return SDL_GetTicks(); #endif else { return dumper.framecount*50/3; } } uint32_t getcorrectedticks() { uint32_t t; if(ui.runstat==1) t=getticks()-ui.timercorr; else t=ui.paused_since-ui.timercorr; return t; } uint32_t gettimevalue() { uint32_t t=getcorrectedticks(); return (t*3)/50; // milliseconds to 60Hz-frames } void waitfortimechange() { int wait=200; if(ui.benchmark_mode) return; if(ui.runstat==1) { int f0=gettimevalue(); int nexttickval=((f0+1)*50)/3+ui.timercorr; wait=nexttickval-getcorrectedticks()+1; if(wait<1) wait=1; else if(wait>17) wait=17; } SDL_Delay(wait); } /*** input-related ***/ void getkeystates() { int m=SDL_GetModState(); uint8_t*k=SDL_GetKeyState(NULL); m=((m&KMOD_CTRL)?64:0)|((m&(KMOD_ALT|KMOD_META))?32:0)|((m&KMOD_SHIFT)?16:0) |(k[SDLK_UP]?8:0)|(k[SDLK_DOWN]?4:0)|(k[SDLK_LEFT]?2:0)|(k[SDLK_RIGHT]?1:0); vm.userinput=(vm.userinput&0x80FFFFFF)|(m<<24); } /*** audio-related ***/ void pauseaudio(s) { if(!ui.opt_nonrealtime) SDL_PauseAudio(s); else { dumper.audiopaused=s; } } void updateaudio(void*dum, uint8_t*d0, int lgt) { int16_t*d=(int16_t*)d0; uint32_t aupp0=ui.auplayptr; for(lgt>>=1;lgt;lgt--) { *d++ = vm.mem[0xd0000+((ui.auplayptr>>16)&0xffff)]+0x8000; ui.auplayptr+=0x164A9; /* (61440<<16)/44100 */ // todo later: some interpolation/filtering } if(aupp0>ui.auplayptr) { ui.auplaytime+=64*65536; } } /*** scheduling logic (not really that ui_sdl-specific) ***/ void scheduler_check() { /* audiotime incs by 1 per frametick auplaytime incs by 1<<16 per frametick auplayptr incs by 1<<32 per 1<<22-inc of auplaytime */ uint32_t playback_at = ui.auplaytime+(ui.auplayptr>>10); uint32_t auwriter_at = vm.audiotime*65536+vm.prevsp[1]*64; if((vm.prevsp[1]>0) && playback_at>auwriter_at) { DEBUG(stderr,"%x > %x! (sp %x & %x) jumping forward\n",playback_at,auwriter_at, vm.sp,vm.cosp); vm.audiotime=((ui.auplaytime>>16)&~63)+64; vm.preferredmediacontext=1; } else if(playback_at+PLAYBACKGAP*0x10000>auwriter_at) vm.preferredmediacontext=1; else vm.preferredmediacontext=0; } void checkmediaformats() { if(vm.wcount[1]!=0 && vm.spchange[1]<=0) { DEBUG(stderr,"audio stack underrun; shut it off!\n"); ui.audio_off=1; vm.spchange[1]=vm.wcount[1]=0; pauseaudio(1); } if(vm.wcount[0]==0) return; // t-video in tyx-video mode produces 2 words extra per wcount if((vm.videomode==0) && (vm.spchange[0]-vm.wcount[0]*2==1)) { vm.videomode=1; DEBUG(stderr,"switched to t-video (sp changed by %d with %d w)\n", vm.spchange[0],vm.wcount); } else if((vm.videomode==1) && (vm.spchange[0]+vm.wcount[0]*2==1)) { vm.videomode=0; DEBUG(stderr,"switched to tyx-video"); } if((vm.videomode==1) && (vm.spchange[1]+vm.wcount[1]*2==1)) { DEBUG(stderr,"A<=>V detected!\n"); switchmediacontext(); vm.videomode=0; /* prevent loop */ vm.spchange[0]=0; vm.wcount[0]=0; vm.spchange[1]=0; vm.wcount[1]=0; } } /*** dumper (event recording/playback & video/audio file dumping) ***/ void pollplaybackevent(SDL_Event*e) { static int next=0,nextkey=0,nextasc=0,nextmod=0; int now=getticks(); e->type=SDL_NOEVENT; if(nowtype=SDL_KEYDOWN; e->key.keysym.sym=nextkey; e->key.keysym.mod=nextmod; e->key.keysym.unicode=nextasc; } if(!feof(stdin)) { int base=next; scanf("%d %d %d %d",&next,&nextkey,&nextasc,&nextmod); next+=base; } else next=nextkey=nextmod=0; } void dumpmediaframe() { static char isfirst=1; int x,y; int16_t ab[735]; if(isfirst) { printf("YUV4MPEG2 W%d H%d F%d:%d Ip A0:0 C420mpeg2 XYSCSS=420MPEG2\n", 640,480,60,1); isfirst=0; } printf("FRAME\n"); updatescreen(); for(y=8*2;y<248*2;y++) { for(x=0;x<32*2;x++) putchar(0); for(x=0;x<256;x++) { char*oo=(char*)(sdl.o->pixels[0])+(y>>1)*256*2+x*2; putchar(oo[0]); putchar(oo[0]); } for(x=0;x<32*2;x++) putchar(0); } for(y=8;y<248;y++) { for(x=0;x<32;x++) putchar(0x80); for(x=0;x<256;x++) { char*oo=(char*)(sdl.o->pixels[0])+y*256*2+(x>>1)*4; putchar(oo[1]); } for(x=0;x<32;x++) putchar(0x80); } for(y=8;y<248;y++) { for(x=0;x<32;x++) putchar(0x80); for(x=0;x<256;x++) { char*oo=(char*)(sdl.o->pixels[0])+y*256*2+(x>>1)*4; putchar(oo[3]); } for(x=0;x<32;x++) putchar(0x80); } if(!dumper.audiopaused) updateaudio(NULL,(uint8_t*)ab,735*2); else memset(ab,0,735*2); fwrite(ab,735*2,1,stderr); } void nrtframestep() { dumper.subframe=0; dumper.framecount++; if(ui.opt_dumpmedia) { dumpmediaframe(); } } /*** editor functions ***/ char*getlinestart(char*b) { while(b>ed.textbuffer && b[-1]!='\n') b--; return b; } char*getnextlinestart(char*b) { while(*b && b[-1]!='\n') b++; return b; } char*getsrcvar(char*vn) { char*s=ed.textbuffer; int vnlen=strlen(vn); for(;;) { char*s1=s; while(*s1!='\n' && *s1!='\0') s1++; if(!strncmp(s,vn,vnlen)) { int len=(s1-s)-vnlen+1; char*d=malloc(len); memcpy(d,s+vnlen,len); d[len-1]='\0'; return d; } s=s1; if(!*s) return NULL; s++; } } void inserttosrc(char*line) { int linelgt=strlen(line); int i=strlen(ed.textbuffer); for(;i>=0;i--) ed.textbuffer[i+linelgt]=ed.textbuffer[i]; memcpy(ed.textbuffer,line,linelgt); ed.cursor=ed.textbuffer; } int ishex(char c) { if((c>='0' && c<='9') || (c>='A' && c<='F')) return 1; else return 0; } int isibnizspace(char c) { if(c==' ' || c=='\n') return 1; else return 0; } void ed_increment(char*p) { if(p=ed.selectbase) ed.selectend=target; } ed.cursor=target; } void ed_prev() { ed_unselect(); while(ed.cursor>ed.textbuffer && !isibnizspace(*ed.cursor)) ed.cursor--; while(ed.cursor>ed.textbuffer && isibnizspace(*ed.cursor)) ed.cursor--; } void ed_next() { ed_unselect(); while(*ed.cursor && !isibnizspace(*ed.cursor)) ed.cursor++; while(*ed.cursor && isibnizspace(*ed.cursor)) ed.cursor++; while(*ed.cursor && !isibnizspace(*ed.cursor)) ed.cursor++; if(ed.cursor>ed.textbuffer) ed.cursor--; } void ed_left(char with_select) { if(ed.cursor!=ed.textbuffer) { ed_movecursor(ed.cursor-1,with_select); } } void ed_right(char with_select) { if(*ed.cursor) ed_movecursor(ed.cursor+1,with_select); } void ed_up(char with_select) { char*p=getlinestart(ed.cursor); int x=ed.cursor-p; if(x>=32) ed_movecursor(ed.cursor-32,with_select); else if(p>ed.textbuffer) { char*pp=getlinestart(p-1); if(p-pp-1ed.selectend) ed.cursor-=gap; else if(ed.cursor>ed.selectstart) ed.cursor=ed.selectstart; s=ed.selectstart; for(;;) { char a=s[gap]; *s++=a; if(!a)break; } ed_unselect(); } void ed_backspace(int offset) { if(ed.selectend>ed.selectstart) ed_deleteselection(); else { if(ed.cursor!=ed.textbuffer) { char*s=(ed.cursor+=offset); for(;*s;s++)*s=s[1]; } } } void ed_save() { FILE*f; char*fn=getsrcvar("\\#file "); if(!fn) { inserttosrc("\\#file untitled.ib\n"); fn=strdup("untitled.ib"); } f=fopen(fn,"w"); DEBUG(stderr,"filename: %s\n",fn); if(!f) inserttosrc("\\ ERROR: couldn't save file!\n"); else { char*s=ed.textbuffer; while(*s) { char*s1=s; while(*s1 && *s1!='\n') s1++; if(*s1=='\n') s1++; if(memcmp(s,"\\#file ",6)) fwrite(s,s1-s,1,f); s=s1; } fclose(f); free(fn); } } void ed_char(int ascii) { if(ed.readonly) return; if(ascii==13) ascii=10; if(ascii==10 || (ascii>=32 && ascii<=126)) { if(ed.selectbase) { ed_deleteselection(); } // if in insertmode... { char*s; for(s=ed.cursor;*s;s++); if(s>=ed.textbuffer+EDITBUFSZ) return; for(;s>=ed.cursor;s--)s[1]=*s; } *ed.cursor++=ascii; } } void ed_copy() { int lgt=ed.selectend-ed.selectstart+1; if(lgt<0 || !ed.selectbase) lgt=0; free(clipboard); clipboard=malloc(lgt+1); memcpy(clipboard,ed.selectstart,lgt); clipboard[lgt]='\0'; clipboard_store(); } void ed_paste() { char*s; clipboard_load(); s=clipboard; if(!s) return; while(*s) { ed_char(*s); s++; } } void ed_cut() { ed_copy(); ed_deleteselection(); } void ed_switchbuffers() { char tmp[sizeof(ed)]; memcpy((void*)&tmp,(void*)&ed,sizeof(ed)); memcpy((void*)&ed,(void*)&ed_parallel,sizeof(ed)); memcpy((void*)&ed_parallel,(void*)&tmp,sizeof(ed)); } char*ed_getprogbuf() { if(!ed.readonly) return ed.textbuffer; else return ed_parallel.textbuffer; } /*** main loop etc ***/ void interactivemode(char*codetoload) { int codechanged=0; uint32_t prevtimevalue=gettimevalue(); SDL_Event e; SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,10); SDL_EnableUNICODE(1); ed.textbuffer=malloc(EDITBUFSZ*sizeof(char)); strncpy(ed.textbuffer,codetoload,EDITBUFSZ-1); ed_unselect(); ed.firsty=0; ed.cursor=ed.textbuffer; ed.readonly=0; ed_parallel.cursor= ed_parallel.selectstart= ed_parallel.selectend= ed_parallel.textbuffer=helpscreen; ed_parallel.readonly=1; #ifdef X11 SDL_EventState(SDL_SYSWMEVENT,SDL_ENABLE); #endif for(;;) { uint32_t t = gettimevalue(); if(prevtimevalue!=t || e.type!=SDL_NOEVENT) { updatescreen(); prevtimevalue=t; DEBUG(stderr,"t:%x audio:%x playback:%x video:%x\n", t,(vm.audiotime)+(((vm.mediacontext==1)?vm.sp:vm.cosp)>>10) ,(ui.auplaytime>>16)+(ui.auplayptr>>26),vm.videotime); } { static int lastpage=0; if(lastpage!=vm.visiblepage) { lastpage=vm.visiblepage; ui.framecounter++; if(ui.opt_nonrealtime) nrtframestep(); } } if(t>=120+ui.bmtime) { float secs=(t-ui.bmtime)/60.0; ui.mops=ui.cyclecounter/(secs*1000000); ui.fps=ui.framecounter/secs; ui.cyclecounter=ui.framecounter=0; ui.bmtime=t; } if(ui.runstat==0) { if(!ui.opt_playback) SDL_WaitEvent(&e); else { e.type=SDL_NOEVENT; SDL_PollEvent(&e); if(e.type==SDL_NOEVENT) pollplaybackevent(&e); if(e.type==SDL_NOEVENT && ui.opt_nonrealtime) nrtframestep(); } } else { e.type=SDL_NOEVENT; SDL_PollEvent(&e); if(ui.opt_playback && e.type==SDL_NOEVENT) pollplaybackevent(&e); if(e.type==SDL_NOEVENT) { if(codechanged) { vm_compile(ed_getprogbuf()); if(ui.audio_off) { ui.audio_off=0; pauseaudio(0); } codechanged=0; } { int c = vm_run(); ui.cyclecounter+=c; } if(ui.opt_nonrealtime) { dumper.subframe++; if(!(dumper.subframe&4095)) nrtframestep(); } checkmediaformats(); scheduler_check(); continue; } } if(e.type==SDL_QUIT) break; if(e.type==SDL_KEYDOWN) { int sym=e.key.keysym.sym; int mod=e.key.keysym.mod; if(ui.opt_dumpkeys) { static int last=0; int now=getticks(); if(!sym && e.key.keysym.unicode) sym=e.key.keysym.unicode; printf("%d %d %d %d\n",now-last,sym, e.key.keysym.unicode,mod); last=now; } getkeystates(); if(sym==SDLK_ESCAPE) break; else if(sym==SDLK_TAB) { ui.osd_visible^=1; } else if(sym==SDLK_F1) { pauseaudio(ui.runstat); ui.runstat^=1; if(ui.runstat==0) { ui.paused_since=getticks(); } else { ui.timercorr+=getticks()-ui.paused_since; ui.mops=ui.fps=ui.bmtime=0; } } else if(sym==SDLK_F2) { ui.timercorr=ui.paused_since=getticks(); if(codechanged) { vm_compile(ed_getprogbuf()); codechanged=0; } vm_init(); ui.auplayptr=ui.auplaytime=0; pauseaudio(ui.runstat^1); } else if(ui.osd_visible) { /* editor keys */ if(sym==SDLK_UP && (mod&KMOD_CTRL)) { ed_increment(ed.cursor); codechanged=1; } else if(sym==SDLK_DOWN && (mod&KMOD_CTRL)) { ed_decrement(ed.cursor); codechanged=1; } else if(sym==SDLK_LEFT && (mod&KMOD_CTRL)) { ed_prev(); } else if(sym==SDLK_RIGHT && (mod&KMOD_CTRL)) { ed_next(); } else if(sym==SDLK_LEFT) { ed_left(mod&KMOD_SHIFT); } else if(sym==SDLK_RIGHT) { ed_right(mod&KMOD_SHIFT); } else if(sym==SDLK_UP) { ed_up(mod&KMOD_SHIFT); } else if(sym==SDLK_DOWN) { ed_down(mod&KMOD_SHIFT); } else if(sym==SDLK_BACKSPACE) { ed_backspace(-1); codechanged=1; } else if(sym==SDLK_DELETE) { ed_backspace(0); codechanged=1; } else if(sym==SDLK_F12) { ed_switchbuffers(); } else if(sym=='s' && (mod&KMOD_CTRL)) { ed_save(); } else if(sym=='c' && (mod&KMOD_CTRL)) { ed_copy(); } else if(sym=='k' && (mod&KMOD_CTRL)) { ed_copy(); } else if(sym=='v' && (mod&KMOD_CTRL)) { ed_paste(); } else if(sym=='x' && (mod&KMOD_CTRL)) { ed_cut(); } else if(sym=='a' && (mod&KMOD_CTRL)) { if(ed.selectbase) ed_unselect(); else { ed.selectstart=ed.textbuffer; ed.selectend=ed.textbuffer+strlen(ed.textbuffer); ed.selectbase=ed.cursor; } } /* else if(sym=='b' && (mod&KMOD_CTRL)) { ui.benchmark_mode^=1; } */ else { if(e.key.keysym.unicode) { ed_char(e.key.keysym.unicode); codechanged=1; } } } } else if(e.type==SDL_KEYUP) { getkeystates(); } else if(e.type==SDL_MOUSEMOTION) { int y=(e.motion.y*256)/sdl.winsz; int x=(e.motion.x*256)/sdl.winsz; if(y>=0 && x>=0 && y<=255 && x<=255) vm.userinput=(vm.userinput&0xFFFF0000)|(y<<8)|x; } else if(e.type==SDL_MOUSEBUTTONDOWN) { vm.userinput|=0x80000000; } else if(e.type==SDL_MOUSEBUTTONUP) { vm.userinput&=0x7FFFFFFF; } else if(e.type==SDL_VIDEORESIZE) { sdl.winsz=e.resize.w #ifndef IBNIZ_H #define IBNIZ_H #ifdef IBNIZ_MAIN # define GLOBAL #else # define GLOBAL extern #endif #include "vm.h" /* i/o stuff used by vm */ uint32_t gettimevalue(); int getuserchar(); void waitfortimechange(); /* vm-specific */ void vm_compile(char*src); void vm_init(); int vm_run(); void switchmediacontext(); GLOBAL char*clipboard; void clipboard_load(); void clipboard_store(); #if defined(WIN32) #define CLIPBOARD_WIN32 #elif defined(X11) #define CLIPBOARD_X11 #include void clipboard_handlesysreq(SDL_Event*e); #else #define CLIPBOARD_NONE #endif #endif ibniz-1.18/src/vm_test.c0000644000175000017500000000170111701151132014641 0ustar viznutviznut#define IBNIZ_MAIN #include #include #include "ibniz.h" void waitfortimechange() { } uint32_t gettimevalue() { return 0; } int runtest(char*code,uint32_t correct_stacktop) { vm_init(); vm_compile(code); while(!vm.stopped) vm_run(); if(vm.stack[vm.sp]==correct_stacktop) return 0; fprintf(stderr,"unit test failed with code \"%s\"\n",code); fprintf(stderr,"stacktop=%x (should have been %x)\n", vm.stack[vm.sp],correct_stacktop); exit(1); } void test_numbers() { runtest("12345T",0x23450001); runtest("F.1234T",0xF1234); runtest("123456789ABCDT",0xABCD6789); runtest("1234.56789AT",0x9A345678); runtest("1.15.25+T",0x13A00); } int main() { /* TODO: i guess we need a little bit more coverage here */ test_numbers(); runtest("1,1+T",2<<16); runtest("6,6*T",36<<16); runtest("1,4X3*LT",81<<16); runtest("1,2,3,2)T",1<<16); runtest("3?5:1;T",5<<16); runtest("0?5:1;T",1<<16); return 0; } ibniz-1.18/licence.txt0000644000175000017500000000176711701150253014407 0ustar viznutviznutIBNIZ ("Ideally Bare Numeric Impression giZmo") Copyright (C) 2011 Ville-Matias Heikkila This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Ville-Matias Heikkila viznut@low.fi (For more information about this licence, look for "zlib license") ibniz-1.18/texts.i0000644000175000017500000000606511701150253013561 0ustar viznutviznutchar*usagetext= "usage: %s [file_to_load] [options]\n" "where options include:\n" " -h(elp) Dump this text\n" " -v(ersion) Dump version info\n" " -c(ode) CODE Execute code\n" " -n(oautorun) Do not autorun loaded code or autohide OSD\n"; // " -e(ventlog) Dump user events to stdout\n" // " -p(layback) Play events back from stdin\n" // " -M(edia) Dump media data (YUV video to stdout)\n" char*versiontext= "IBNIZ version 1.1180"; char*welcometext= "\n" "\n" "\n" "\n" "\\ IBNIZ 1.1180\n" "\\\n" "\\ Press f12 for help.\n"; char*helpscreen= //////////////////////////////// "IBNIZ quick reference (F12:quit)" "Use cursor keys to move around\n" "\n" "======= Keyboard commands ======" "\n" "f1 start/pause f12 help" "f2 reset/restart esc quit" "tab hide/show editor ctrl+s save" "\n" "shift+cursor .... select area\n" "ctrl+left/right . jump by words " "ctrl+up/down .... inc/dec number" "ctrl+x/c/v ...... cut/copy/paste" "\n" "======== IBNIZ language ========" "\n" "One character per operation.\n" "Number format 16.16 fixedpoint.\n" "Immediates in uppercase hex.\n" "Implicit whole-program loop\n" " with 'Mw' on each cycle.\n" "\n" "Arithmetic:\n" "\n" "+ - * / % & | ^ ~ are as in C\n" "\n" "q:sqrt s:sin a:atan2 r:ror l:shl" "\n" "< : zero if <0 else keep\n" "> : zero if >0 else keep\n" "= : 1 if zero else 0\n" "\n" "Stack manipulation:\n" "\n" " 1 d = 1 1 2 1 x = 1 2\n" "1 1 p = 1 3 2 1 v = 2 1 3\n" "\n" "N) = copy from N places down\n" "N( = store to N places down\n" "\n" "Memory manipulation:\n" "\n" "N@ = load value from MEM[N]\n" "MN! = store value M to MEM[N]\n" "\n" "Conditionals:\n" "\n" "N?M; = if N!=0 then M\n" "N?M:O; = if N!=0 then M else O\n" "\n" "Loops:\n" "\n" "NX...L execute '...' N times\n" "i = index of inner X loop\n" "j = index of outer X loop\n" "[...N] repeat '...' until N==0\n" "\n" "Subroutines:\n" "\n" "N{...} define subroutine\n" " (store pointer to MEM[N]) " "NV run subroutine N\n" "\n" "Return stack:\n" "\n" "R pop from rstack to stack\n" "P pop from stack to rstack\n" "\n" "User input:\n" "\n" "U return SSKK.YYXX where\n" " SS = click & crskey state " " KK = last key (~ascii)\n" " YYXX = mouse position\n" "\n" "Data segment:\n" "\n" "NG fetch N bits of data\n" "$ start data segment where\n" "0-F data digits\n" "bqoh digit size 1-4 bits\n" "\n" "Data segment is stored in\n" "MEM[0...] on each VM reset\n" "\n" "Specials:\n" "\n" "w : push loop vars (t or t y x) " "M : separate audio/vidoe code\n" "T : terminate program\n" "\\ : comment line\n" ", : blank, separate numbers\n" "\n" "======= Examples =======\n" "\n" "Copy to editor and run:\n\n" "^x6r-\n\n" "ddd***\n\n" "d3r15&*\n\n" "dd6r*3r&\n\n" "^x7r+Md8r&\n\n" "v8rsdv*vv*^\n\n" "d6r|5*wdAr&+\n\n" "v8rsdv*vv*^wpp8r-\n\n" "ax8r+3lwd*xd*+q1x/x5r+^\n\n" "v8rds4X3)Lx~2Xv*vv*+i!L1@2@&\n\n" "6{^^ddd***1%}5{v8rsdv*vv*^wpp8r-}4{v8rdsx.6+s4X3)Lx~2Xv*vv*+i!L1@2@^}" "3{ax8r+3lwd*xd*+q1x/x6r+^}2)6r3&3+V55A9^Md6r|5*wdAr&+\n\n" "Full docs & latest IBNIZ:\n" "http://pelulamu.net/ibniz/\n"; ibniz-1.18/Makefile.win0000644000175000017500000000174311701150253014472 0ustar viznutviznut# For normal builds; remove -DX11 -lX11 from flags if you don't have X11 CC=i586-mingw32msvc-gcc EXE=ibniz.exe FLAGS=-L./SDL-1.2.14/lib -I./SDL-1.2.14/include -static -lmingw32 SDL-1.2.14/lib/libSDL.a SDL-1.2.14/lib/libSDLmain.a -mwindows -lwinmm all: ibniz.exe clean: rm -f *.o *~ ibniz vmtest ibniz.exe whole.c package: clean cd .. && cp -R IBNIZ ibniz-1.2 && tar czf ibniz-1.2.tar.gz ibniz-1.2 $(EXE): whole.c $(CC) -s -O3 -ffast-math -fwhole-program whole.c -o $(EXE) $(FLAGS) -lm whole.c: vm_slow.c ui_sdl.c clipboard.c texts.i font.i vm.h ibniz.h cat ui_sdl.c vm_slow.c clipboard.c > whole.c ui_sdl.o: ui_sdl.c ibniz.h font.i vm.h texts.i vm.h $(CC) -c -Os ui_sdl.c -o ui_sdl.o $(FLAGS) clipboard.o: clipboard.c ibniz.h $(CC) -c -Os clipboard.c -o clipboard.o $(FLAGS) vm_slow.o: vm_slow.c ibniz.h vm.h $(CC) -c -O3 vm_slow.c -o vm_slow.o font.i: font.pl perl font.pl > font.i runtest: vmtest ./vmtest vmtest: vm_test.c vm_slow.c gcc vm_test.c vm_slow.c -o vmtest -lm ibniz-1.18/vm_slow.c0000644000175000017500000003454611701150253014077 0ustar viznutviznut#include #include #include #include #include "ibniz.h" #define MAXCODESIZE 4096 #define MAXDATASIZE 4096 #define OP_LOADIMM '0' #define ROL(a,s) ((((uint32_t)(a))<<(s))|(((uint32_t)(a))>>(32-(s)))) #define ROR(a,s) ((((uint32_t)(a))>>(s))|(((uint32_t)(a))<<(32-(s)))) #define MOVESP(steps) vm.sp=(vm.sp+(steps))&vm.stackmask #define MOVERSP(steps) vm.rsp=(vm.rsp+(steps))&vm.rstackmask char compiled_code[MAXCODESIZE]; uint32_t compiled_data[MAXDATASIZE]; uint32_t compiled_hints[MAXCODESIZE]; void pushmediavariables(); uint32_t getdatabits(int n) { int s=(32-n-(vm.dataptr&31)); uint32_t mask; uint32_t a; if(n<=0 || vm.datalgt<=0) return 0; mask=(1<=0) a=(compiled_data[vm.dataptr>>5]>>s)&mask; else a=((compiled_data[vm.dataptr>>5]<<(0-s))| (compiled_data[(vm.dataptr>>5)+1]>>(32+s)))&mask; vm.dataptr=(vm.dataptr+n)%vm.datalgt; return a; } void vm_compile(char*src) { char*d=compiled_code; uint32_t*hd=compiled_hints; uint32_t num; char*s,nummode=0,shift=0; int i,j; s=src; /* parse immediates, skip comments & whitespaces */ for(;;) { char a=*s++; if((!a) || (a>='!' && a<='~')) { if(a=='.' || (a>='0' && a<='9') || (a>='A' && a<='F')) { if(nummode==0) { num=0; shift=16; nummode=1; } if(a=='.') { if(nummode==2) { *d++=OP_LOADIMM; *hd++=num; num=0; } nummode=2; shift=12; } else { char digit=(a>='A'?a-'A'+10:a-'0'); if(nummode==1) num=ROL(num,4); num|=digit<=0) { compiled_data[vm.datalgt>>5]|=a<>5)+1]=0; } else { compiled_data[vm.datalgt>>5]|=a>>(0-s); compiled_data[(vm.datalgt>>5)+1]=a<<(32+s); } vm.datalgt+=digitsz; } break; } } /* fill last 2 words to ease fetch */ {int pad=vm.datalgt&31; if(pad) { int i=pad; while(i<32) { compiled_data[vm.datalgt>>5]|=compiled_data[0]>>i; i*=2; } } if(!pad) compiled_data[(vm.datalgt>>5)+1]=compiled_data[0]; else { compiled_data[(vm.datalgt>>5)+1]= (compiled_data[0]<<(32-pad)) | (compiled_data[1]>>pad); } } } /* precalculate skip points */ vm.codelgt=d-compiled_code; for(i=0;;i++) { int j=i+1,seek0=0,seek1=0,seek2=0; char a=compiled_code[i]; if(a=='\0') { seek0='M'; j=0; } if(a=='M') seek0='M'; else if(a=='?') { seek0=';'; seek1=':'; } else if(a==':') seek0=';'; else if(a=='{') { seek0='}'; } if(seek0) { for(;;j++) { int a=compiled_code[j]; if(a=='\0' || a==seek0 || a==seek1) { if(i==j || a==0) compiled_hints[i]=0; else compiled_hints[i]=j+1; break; } } } if(a=='\0') break; } /* DEBUG: dump code */ /* for(i=0;i>5]); } */ } void vm_init() { /* video context */ vm.stack=vm.mem+0xE0000; vm.stackmask=0x1ffff; vm.sp=0; vm.rstack=vm.mem+0xCC000; vm.rstackmask=0x3FFF; vm.rsp=0; /* audio context */ vm.costack=vm.mem+0xD0000; vm.costackmask=0xffff; vm.cosp=1; // to avoid audio skipping bug at start vm.corstack=vm.mem+0xC8000; vm.corstackmask=0x3FFF; vm.corsp=0; /* state */ vm.ip=compiled_code; vm.mediacontext=0; vm.videomode=0; vm.audiomode=0; vm.visiblepage=1; vm.dataptr=0; vm.userinput=0; vm.stopped=0; vm.audiotime=vm.videotime=gettimevalue(); vm.spchange[0]=vm.spchange[1]=0; vm.wcount[0]=vm.wcount[1]=0; vm.currentwcount[0]=vm.currentwcount[1]=0; vm.prevsp[0]=vm.prevsp[1]=0; /* zero out memory */ if(!vm.datalgt) memset(vm.mem,0,MEMSIZE*sizeof(uint32_t)); else { int i; vm.dataptr=0; for(i=0;i>16)&1)^1; //vm.visiblepage^=1; for(;;) { uint32_t newt=gettimevalue(); if(newt!=vm.videotime) break; waitfortimechange(); } vm.videotime=gettimevalue(); } void pushmediavariables() { vm.currentwcount[vm.mediacontext]++; if(vm.mediacontext==0) { int p=vm.sp&65535; if(vm.videomode==0) { if(vm.visiblepage==(vm.sp>>16)) { flipvideopage(); } MOVESP(1); vm.stack[vm.sp]=vm.videotime<<16; MOVESP(1); vm.stack[vm.sp]=(p<<1)-0x10000; MOVESP(1); vm.stack[vm.sp]=((p&255)<<9)-0x10000; } else { if(!p) { flipvideopage(); } MOVESP(1); vm.stack[vm.sp]=(vm.videotime<<16)|p; } } else { if(!vm.sp) // todo we need something better { vm.audiotime+=64; } MOVESP(1); vm.stack[vm.sp]=vm.audiotime*65536+vm.sp*64; // fprintf(stderr,"%x\n",vm.stack[vm.sp]); } } #define CYCLESPERRUN 10223 int vm_run() { int cycles; if(vm.stopped) return 0; for(cycles=CYCLESPERRUN;cycles;cycles--) { char op=*vm.ip++; int32_t*a=&vm.stack[vm.sp],*b; switch(op) { /*** NUMBERS ***/ case(OP_LOADIMM): MOVESP(1); vm.stack[vm.sp]=compiled_hints[vm.ip-1-compiled_code]; break; /*** ARITHMETIC ***/ case('+'): // (b a -- a+b) MOVESP(-1); vm.stack[vm.sp]+=*a; break; case('-'): // (b a -- a-b) MOVESP(-1); vm.stack[vm.sp]-=*a; break; case('*'): // (b a -- a*b) MOVESP(-1); b=&vm.stack[vm.sp]; {int64_t m=*a; m*=((int32_t)*b); *b=m>>16; } break; case('/'): // (b a -- a/b) MOVESP(-1); b=&vm.stack[vm.sp]; if(!*a)*b=0; else {int64_t m=*b; m<<=16; m/=((int32_t)*a); *b=m;} break; case('%'): // (b a -- a%b) MOVESP(-1); b=&vm.stack[vm.sp]; if(!*a)*b=0; else *b=(*b%*a); break; case('q'): // (a -- sqrt(a), 0 if a<0) if(*a<0) *a=0; else *a=sqrt(*a/65536.0)*65536.0; break; case('&'): // (b a -- a&b) MOVESP(-1); vm.stack[vm.sp]&=*a; break; case('|'): // (b a -- a|b) MOVESP(-1); vm.stack[vm.sp]|=*a; break; case('^'): // (b a -- a^b) MOVESP(-1); vm.stack[vm.sp]^=*a; break; case('r'): // (b a -- b ror a) MOVESP(-1); b=&vm.stack[vm.sp]; {int steps=(*a>>16)&31; *b=ROR(*b,steps); } break; case('l'): // (b a -- b >> a) MOVESP(-1); b=&vm.stack[vm.sp]; {int steps=(*a>>16)&63; uint32_t w=*b; if(steps<32) *b=(w<>(steps-32)); } break; case('~'): // (a -- NOT a) *a=~*a; break; case('s'): // (a -- sin(a)) *a=sin(*a*(2*M_PI/65536.0))*65536.0; break; case('a'): // (b a -- atan2(a,b)) MOVESP(-1); b=&vm.stack[vm.sp]; *b=atan2(*a,*b)*(65536.0/(2*M_PI)); break; case('<'): // (a -- a<0?a:0) if(*a>=0)*a=0; break; case('>'): // (a -- a>0?a:0) if(*a&0x80000000)*a=0; break; case('='): // (a -- a==0?1:0) if(*a)*a=0x10000;else *a=0; break; /*** STACK MANIPULATION ***/ case('d'): // (a -- a a) MOVESP(1); vm.stack[vm.sp]=*a; break; case('p'): // (a --) MOVESP(-1); break; case('x'): // (b a -- a b) // forth: SWAP {int32_t tmp=*a; b=&vm.stack[(vm.sp-1)&vm.stackmask]; *a=*b; *b=tmp;} break; case('v'): // (c b a -- b a c) // forth: ROT {int32_t a_v=*a,*c; b=&vm.stack[(vm.sp-1)&vm.stackmask]; c=&vm.stack[(vm.sp-2)&vm.stackmask]; *a=*c; *c=*b; *b=a_v;} break; case(')'): // pick from STACK[top-1-i] *a=vm.stack[(vm.sp-1-ROL(*a,16))&vm.stackmask]; break; case('('): // store to STACK[top-2-i] MOVESP(-1); b=&vm.stack[vm.sp]; MOVESP(-1); vm.stack[(vm.sp-ROL(*a,16))&vm.stackmask]=*b; break; case('z'): MOVESP(1); vm.stack[vm.sp]=ROL(((vm.stack+vm.sp)-vm.mem),16); break; /*** EXTERIOR LOOP ***/ case('M'): // media switch stepmediacontext(compiled_hints[vm.ip-compiled_code-1],0); pushmediavariables(); break; case('\0'): // end of code //vm.ip=compiled_code; // or top of rstack (don't pop it) stepmediacontext(compiled_hints[vm.ip-compiled_code-1],1); pushmediavariables(); break; case('w'): // whereami pushmediavariables(); break; case('T'): // terminate program vm.ip--; vm.stopped=1; return CYCLESPERRUN-cycles; /*** MEMORY MANIPULATION ***/ case('@'): // (addr -- val) *a=vm.mem[ROL(*a,16)&(MEMSIZE-1)]; break; case('!'): // (val addr --) MOVESP(-1); b=&vm.stack[vm.sp]; MOVESP(-1); vm.mem[ROL(*a,16)&(MEMSIZE-1)]=*b; break; /*** PROGRAM CONTROL: Conditional execution ***/ case('?'): // if MOVESP(-1); if(*a!=0) break; case(':'): // then vm.ip=compiled_code+compiled_hints[vm.ip-compiled_code-1]; case(';'): // endif/nop break; /*** PROGRAM CONTROL: Loops ***/ case('i'): // i counter MOVESP(1); vm.stack[vm.sp]=ROL(vm.rstack[(vm.rsp-1)&vm.rstackmask],16); break; case('j'): // j counter MOVESP(1); vm.stack[vm.sp]=ROL(vm.rstack[(vm.rsp-3)&vm.rstackmask],16); break; case('X'): // times MOVERSP(1); MOVESP(-1); vm.rstack[vm.rsp]=ROL(*a,16); case('['): // do MOVERSP(1); vm.rstack[vm.rsp]=vm.ip-compiled_code; break; case('L'): // loop {uint32_t*i=&vm.rstack[(vm.rsp-1)&vm.rstackmask]; (*i)--; if(*i==0) MOVERSP(-2); else vm.ip=(vm.rstack[vm.rsp]%vm.codelgt)+compiled_code; } break; case(']'): // while MOVESP(-1); if(*a) vm.ip=(vm.rstack[vm.rsp]%vm.codelgt)+compiled_code; else MOVERSP(-1); break; case('J'): // jump {int point=*a%vm.codelgt; // !!! addressing will change MOVESP(-1); vm.ip=compiled_code+point;} break; /*** PROGRAM CONTROL: Subroutines ***/ case('{'): // defsub MOVESP(-1); vm.mem[ROL(*a,16)&(MEMSIZE-1)]=vm.ip-compiled_code; vm.ip=compiled_code+compiled_hints[vm.ip-1-compiled_code]; break; case('}'): // ret vm.ip=compiled_code+(vm.rstack[vm.rsp]%vm.codelgt); MOVERSP(-1); break; case('V'): // visit MOVESP(-1); MOVERSP(1); vm.rstack[vm.rsp]=vm.ip-compiled_code; vm.ip=((vm.mem[ROL(*a,16)&(MEMSIZE-1)])%vm.codelgt)+compiled_code; break; /*** PROGRAM CONTROL: Rstack manipulation ***/ case('R'): // pull from rstack to mainstack MOVESP(1); vm.stack[vm.sp]=ROL(vm.rstack[vm.rsp],16); MOVERSP(-1); break; case('P'): // push from stack to rstack MOVERSP(1); vm.rstack[vm.rsp]=ROL(*a,16); MOVESP(-1); break; /*** INPUT ***/ case('U'): // userinput MOVESP(1); vm.stack[vm.sp]=vm.userinput; vm.userinput&=0xff00ffff; break; /*** DATA SEGMENT ***/ case('G'): // getbits *a=ROL(getdatabits((*a>>16)&31),16); break; } } return CYCLESPERRUN; } ibniz-1.18/examples/0000755000175000017500000000000011701150253014047 5ustar viznutviznutibniz-1.18/examples/ibnizdemo.ib0000644000175000017500000000017211701150253016343 0ustar viznutviznut6{^^ddd***1%}5{v8rsdv*vv*^wpp8r-}4{v8rdsx.6+s4X3)Lx~2Xv*vv*+i!L1@2@^}3{ax8r+3lwd*xd*+q1x/x6r+^}2)6r3&3+V55A9^Md6r|5*wdAr&+ibniz-1.18/examples/bitmapzoomer.ib0000644000175000017500000000043111701150253017071 0ustar viznutviznutv7rs6ldv*vv*7&@xr.8& $b 0000000000000000000000000000000000000000011110111010010011101110000000000100000100101101001001000000000000100001001101001110010000000000000100010010010100100100000000000000100100100101001001000000000001111011101001001110111000000000000000000000000000000000 ibniz-1.18/examples/oneliners.ib0000644000175000017500000000030411701150253016356 0ustar viznutviznut\ Some examples \ (uncomment and run) \ ^x6r- \ ddd*** \ d3r15&* \ dd6r*3r& \ ^x7r+Md8r& \ v8rsdv*vv*^ \ d6r|5*wdAr&+ \ v8rsdv*vv*^wpp8r- \ ax8r+3lwd*xd*+q1x/x5r+^ \ v8rds4X3)Lx~2Xv*vv*+i!L1@2@& ibniz-1.18/vm.h0000644000175000017500000000150411701150253013024 0ustar viznutviznut#ifndef VM_H #define MEMSIZE 0x100000 GLOBAL struct { char *ip; int32_t*stack; uint32_t sp; uint32_t stackmask; uint32_t*rstack; uint32_t rsp; uint32_t rstackmask; int32_t*costack; uint32_t cosp; uint32_t costackmask; uint32_t*corstack; uint32_t corsp; uint32_t corstackmask; char mediacontext; /* 0=video, 1=audio, 2=stdio */ char videomode; /* 0=txy, 1=t */ char audiomode; /* 0=mono, 1=stereo */ char preferredmediacontext; char visiblepage; char stopped; uint32_t videotime; uint32_t audiotime; uint32_t prevsp[2]; uint32_t prevstackval[2]; int currentwcount[2]; int16_t spchange[2]; int wcount[2]; int codelgt; int datalgt; int dataptr; uint32_t userinput; int32_t mem[MEMSIZE]; } vm; #endif ibniz-1.18/font.pl0000755000175000017500000001576611701150253013556 0ustar viznutviznut#!/usr/bin/env perl $a=< #else #include #endif #define IBNIZ_MAIN #include "ibniz.h" #include "texts.i" struct { SDL_Surface*s; SDL_Overlay*o; SDL_AudioSpec as; int winsz,xmargin,ymargin; } sdl; struct { char runstat; uint32_t timercorr; uint32_t paused_since; uint32_t auplayptr; uint32_t auplaytime; uint32_t bmtime; int64_t cyclecounter; int framecounter; char audio_off; char osd_visible; char benchmark_mode; float mops; float fps; char opt_dumpkeys; char opt_nonrealtime; char opt_playback; char opt_dumpmedia; } ui; struct { //int width; //int height; int framecount; int subframe; char audiopaused; } dumper; #define WIDTH 256 #define EDITBUFSZ (65536*2) struct { char*cursor; char*selectstart; char*selectend; char*selectbase; char readonly; int firsty; char*textbuffer; } ed,ed_parallel; uint8_t font[]= { #include "font.i" }; #ifndef WIN32 # define PLAYBACKGAP 4 #else # define PLAYBACKGAP 16 #endif #define DEBUG /*** rendering of videostack and osd ***/ void drawChar8x8(uint8_t*d,uint8_t*s,uint32_t fg,uint32_t bg) { int y,x; for(y=0;y<8;y++) { int bitmap=s[y]; uint8_t*dd=d+WIDTH*y*2; for(x=0;x<16;x+=2) { dd[x]=(bitmap&128)?fg:bg; bitmap<<=1; } } } void drawTextBuffer() { int x=0,y=ed.firsty; int scroll=0; char*b=ed.textbuffer; char*lightstart=ed.cursor,*lightend=ed.cursor; if(ed.selectbase) { lightstart=ed.selectstart; lightend=ed.selectend; } for(;;) { int a=*b&127; int fg=0xffffff; int bg=0x000000; if(b>=lightstart && b<=lightend) { fg=0x000000; bg=0xffffff; if(y<0) scroll=-y; else if(y>=28) scroll=27-y; } if(y>=0 && y<28) { drawChar8x8( ((uint8_t*)(sdl.o->pixels[0]))+x*16+y*WIDTH*16, font+(a>=32?a-32:0)*8,fg,bg); } x++; if(x>=32) { y++; x=0; } if(a=='\n') { y++; x=0; } if(!a) break; b++; } ed.firsty+=scroll; } void drawString(char*s,int x,int y) { int fg=0xffffff; int bg=0x000000; while(*s) { int a=*s; drawChar8x8( ((uint8_t*)(sdl.o->pixels[0]))+x*16+y*WIDTH*16, font+(a>=32?a-32:0)*8,fg,bg); s++; x++; } } void drawStatusPanel() { char buf[24]; int sgn,spc; uint32_t a; sprintf(buf,"T=%04X",gettimevalue()&0xFFFF); drawString(buf,0,28); if(ui.runstat) { if(ui.mops>0) { sprintf(buf,"%3.3f Mops%c",ui.mops, ui.benchmark_mode?'!':' '); drawString(buf,0,30); } if(ui.fps>0) { sprintf(buf,"%2.4f fps",ui.fps); drawString(buf,0,31); } } spc=vm.spchange[0]; sgn='+'; if(spc<0) { sgn='-'; spc=0-spc; } if(spc>15) spc=15; sprintf(buf,"VIDEO S=%05X (%c%X)",vm.prevsp[0]&0x1FFFF,sgn,spc); drawString(buf,13,28); drawString(vm.videomode?"t":"tyx",13,29); a=vm.prevstackval[0]; sprintf(buf,"%04X.%04X",(a>>16)&0xFFFF,a&0xFFFF); drawString(buf,21,29); spc=vm.spchange[1]; sgn='+'; if(spc<0) { sgn='-'; spc=0-spc; } if(spc>15) spc=15; sprintf(buf,"AUDIO S=%05X (%c%X)",vm.prevsp[1]&0x1FFFF,sgn,spc); drawString(buf,13,30); drawString(ui.audio_off?"off": (vm.audiomode?"ster":"mono"),13,31); a=vm.prevstackval[1]; sprintf(buf,"%04X.%04X",(a>>16)&0xFFFF,a&0xFFFF); drawString(buf,21,31); } void showyuv() { SDL_Rect area={sdl.xmargin,sdl.ymargin,sdl.winsz,sdl.winsz}; SDL_DisplayYUVOverlay(sdl.o,&area); } void updatescreen() { int x,y; uint32_t*s=vm.mem+0xE0000+(vm.visiblepage<<16); for(y=0;y<256;y++) for(x=0;x<128;x++) { uint32_t b=s[0],a=s[1]; // little_endian, TODO: support big-endian a=(a&0xff000000)| ((a<<8)&0x00ff0000)| ((b>>8)&0x0000ffff); a^=0x80008000; #if SDL_BYTEORDER == SDL_BIG_ENDIAN // big-endian not tested! a=(((a>>24)&0xff)) | (((a>>16)&0xff)<<8) | (((a>>8)&0xff)<<16) | (((a)&0xff)<<24); #endif ((uint32_t*)(sdl.o->pixels[0]))[(WIDTH/2)*y+x]=a; s+=2; } if(ui.osd_visible) { drawTextBuffer(); drawStatusPanel(); } showyuv(); } /*** timer-related ***/ int getticks() { if(!ui.opt_nonrealtime) #ifdef __APPLE__ /* please find a proper fix */ return SDL_GetTicks()/3; #else return SDL_GetTicks(); #endif else { return dumper.framecount*50/3; } } uint32_t getcorrectedticks() { uint32_t t; if(ui.runstat==1) t=getticks()-ui.timercorr; else t=ui.paused_since-ui.timercorr; return t; } uint32_t gettimevalue() { uint32_t t=getcorrectedticks(); return (t*3)/50; // milliseconds to 60Hz-frames } void waitfortimechange() { int wait=200; if(ui.benchmark_mode) return; if(ui.runstat==1) { int f0=gettimevalue(); int nexttickval=((f0+1)*50)/3+ui.timercorr; wait=nexttickval-getcorrectedticks()+1; if(wait<1) wait=1; else if(wait>17) wait=17; } SDL_Delay(wait); } /*** input-related ***/ void getkeystates() { int m=SDL_GetModState(); uint8_t*k=SDL_GetKeyState(NULL); m=((m&KMOD_CTRL)?64:0)|((m&(KMOD_ALT|KMOD_META))?32:0)|((m&KMOD_SHIFT)?16:0) |(k[SDLK_UP]?8:0)|(k[SDLK_DOWN]?4:0)|(k[SDLK_LEFT]?2:0)|(k[SDLK_RIGHT]?1:0); vm.userinput=(vm.userinput&0x80FFFFFF)|(m<<24); } /*** audio-related ***/ void pauseaudio(s) { if(!ui.opt_nonrealtime) SDL_PauseAudio(s); else { dumper.audiopaused=s; } } void updateaudio(void*dum, uint8_t*d0, int lgt) { int16_t*d=(int16_t*)d0; uint32_t aupp0=ui.auplayptr; for(lgt>>=1;lgt;lgt--) { *d++ = vm.mem[0xd0000+((ui.auplayptr>>16)&0xffff)]+0x8000; ui.auplayptr+=0x164A9; /* (61440<<16)/44100 */ // todo later: some interpolation/filtering } if(aupp0>ui.auplayptr) { ui.auplaytime+=64*65536; } } /*** scheduling logic (not really that ui_sdl-specific) ***/ void scheduler_check() { /* audiotime incs by 1 per frametick auplaytime incs by 1<<16 per frametick auplayptr incs by 1<<32 per 1<<22-inc of auplaytime */ uint32_t playback_at = ui.auplaytime+(ui.auplayptr>>10); uint32_t auwriter_at = vm.audiotime*65536+vm.prevsp[1]*64; if((vm.prevsp[1]>0) && playback_at>auwriter_at) { DEBUG(stderr,"%x > %x! (sp %x & %x) jumping forward\n",playback_at,auwriter_at, vm.sp,vm.cosp); vm.audiotime=((ui.auplaytime>>16)&~63)+64; vm.preferredmediacontext=1; } else if(playback_at+PLAYBACKGAP*0x10000>auwriter_at) vm.preferredmediacontext=1; else vm.preferredmediacontext=0; } void checkmediaformats() { if(vm.wcount[1]!=0 && vm.spchange[1]<=0) { DEBUG(stderr,"audio stack underrun; shut it off!\n"); ui.audio_off=1; vm.spchange[1]=vm.wcount[1]=0; pauseaudio(1); } if(vm.wcount[0]==0) return; // t-video in tyx-video mode produces 2 words extra per wcount if((vm.videomode==0) && (vm.spchange[0]-vm.wcount[0]*2==1)) { vm.videomode=1; DEBUG(stderr,"switched to t-video (sp changed by %d with %d w)\n", vm.spchange[0],vm.wcount); } else if((vm.videomode==1) && (vm.spchange[0]+vm.wcount[0]*2==1)) { vm.videomode=0; DEBUG(stderr,"switched to tyx-video"); } if((vm.videomode==1) && (vm.spchange[1]+vm.wcount[1]*2==1)) { DEBUG(stderr,"A<=>V detected!\n"); switchmediacontext(); vm.videomode=0; /* prevent loop */ vm.spchange[0]=0; vm.wcount[0]=0; vm.spchange[1]=0; vm.wcount[1]=0; } } /*** dumper (event recording/playback & video/audio file dumping) ***/ void pollplaybackevent(SDL_Event*e) { static int next=0,nextkey=0,nextasc=0,nextmod=0; int now=getticks(); e->type=SDL_NOEVENT; if(nowtype=SDL_KEYDOWN; e->key.keysym.sym=nextkey; e->key.keysym.mod=nextmod; e->key.keysym.unicode=nextasc; } if(!feof(stdin)) { int base=next; scanf("%d %d %d %d",&next,&nextkey,&nextasc,&nextmod); next+=base; } else next=nextkey=nextmod=0; } void dumpmediaframe() { static char isfirst=1; int x,y; int16_t ab[735]; if(isfirst) { printf("YUV4MPEG2 W%d H%d F%d:%d Ip A0:0 C420mpeg2 XYSCSS=420MPEG2\n", 640,480,60,1); isfirst=0; } printf("FRAME\n"); updatescreen(); for(y=8*2;y<248*2;y++) { for(x=0;x<32*2;x++) putchar(0); for(x=0;x<256;x++) { char*oo=(char*)(sdl.o->pixels[0])+(y>>1)*256*2+x*2; putchar(oo[0]); putchar(oo[0]); } for(x=0;x<32*2;x++) putchar(0); } for(y=8;y<248;y++) { for(x=0;x<32;x++) putchar(0x80); for(x=0;x<256;x++) { char*oo=(char*)(sdl.o->pixels[0])+y*256*2+(x>>1)*4; putchar(oo[1]); } for(x=0;x<32;x++) putchar(0x80); } for(y=8;y<248;y++) { for(x=0;x<32;x++) putchar(0x80); for(x=0;x<256;x++) { char*oo=(char*)(sdl.o->pixels[0])+y*256*2+(x>>1)*4; putchar(oo[3]); } for(x=0;x<32;x++) putchar(0x80); } if(!dumper.audiopaused) updateaudio(NULL,(uint8_t*)ab,735*2); else memset(ab,0,735*2); fwrite(ab,735*2,1,stderr); } void nrtframestep() { dumper.subframe=0; dumper.framecount++; if(ui.opt_dumpmedia) { dumpmediaframe(); } } /*** editor functions ***/ char*getlinestart(char*b) { while(b>ed.textbuffer && b[-1]!='\n') b--; return b; } char*getnextlinestart(char*b) { while(*b && b[-1]!='\n') b++; return b; } char*getsrcvar(char*vn) { char*s=ed.textbuffer; int vnlen=strlen(vn); for(;;) { char*s1=s; while(*s1!='\n' && *s1!='\0') s1++; if(!strncmp(s,vn,vnlen)) { int len=(s1-s)-vnlen+1; char*d=malloc(len); memcpy(d,s+vnlen,len); d[len-1]='\0'; return d; } s=s1; if(!*s) return NULL; s++; } } void inserttosrc(char*line) { int linelgt=strlen(line); int i=strlen(ed.textbuffer); for(;i>=0;i--) ed.textbuffer[i+linelgt]=ed.textbuffer[i]; memcpy(ed.textbuffer,line,linelgt); ed.cursor=ed.textbuffer; } int ishex(char c) { if((c>='0' && c<='9') || (c>='A' && c<='F')) return 1; else return 0; } int isibnizspace(char c) { if(c==' ' || c=='\n') return 1; else return 0; } void ed_increment(char*p) { if(p=ed.selectbase) ed.selectend=target; } ed.cursor=target; } void ed_prev() { ed_unselect(); while(ed.cursor>ed.textbuffer && !isibnizspace(*ed.cursor)) ed.cursor--; while(ed.cursor>ed.textbuffer && isibnizspace(*ed.cursor)) ed.cursor--; } void ed_next() { ed_unselect(); while(*ed.cursor && !isibnizspace(*ed.cursor)) ed.cursor++; while(*ed.cursor && isibnizspace(*ed.cursor)) ed.cursor++; while(*ed.cursor && !isibnizspace(*ed.cursor)) ed.cursor++; if(ed.cursor>ed.textbuffer) ed.cursor--; } void ed_left(char with_select) { if(ed.cursor!=ed.textbuffer) { ed_movecursor(ed.cursor-1,with_select); } } void ed_right(char with_select) { if(*ed.cursor) ed_movecursor(ed.cursor+1,with_select); } void ed_up(char with_select) { char*p=getlinestart(ed.cursor); int x=ed.cursor-p; if(x>=32) ed_movecursor(ed.cursor-32,with_select); else if(p>ed.textbuffer) { char*pp=getlinestart(p-1); if(p-pp-1ed.selectend) ed.cursor-=gap; else if(ed.cursor>ed.selectstart) ed.cursor=ed.selectstart; s=ed.selectstart; for(;;) { char a=s[gap]; *s++=a; if(!a)break; } ed_unselect(); } void ed_backspace(int offset) { if(ed.selectend>ed.selectstart) ed_deleteselection(); else { if(ed.cursor!=ed.textbuffer) { char*s=(ed.cursor+=offset); for(;*s;s++)*s=s[1]; } } } void ed_save() { FILE*f; char*fn=getsrcvar("\\#file "); if(!fn) { inserttosrc("\\#file untitled.ib\n"); fn=strdup("untitled.ib"); } f=fopen(fn,"w"); DEBUG(stderr,"filename: %s\n",fn); if(!f) inserttosrc("\\ ERROR: couldn't save file!\n"); else { char*s=ed.textbuffer; while(*s) { char*s1=s; while(*s1 && *s1!='\n') s1++; if(*s1=='\n') s1++; if(memcmp(s,"\\#file ",6)) fwrite(s,s1-s,1,f); s=s1; } fclose(f); free(fn); } } void ed_char(int ascii) { if(ed.readonly) return; if(ascii==13) ascii=10; if(ascii==10 || (ascii>=32 && ascii<=126)) { if(ed.selectbase) { ed_deleteselection(); } // if in insertmode... { char*s; for(s=ed.cursor;*s;s++); if(s>=ed.textbuffer+EDITBUFSZ) return; for(;s>=ed.cursor;s--)s[1]=*s; } *ed.cursor++=ascii; } } void ed_copy() { int lgt=ed.selectend-ed.selectstart+1; if(lgt<0 || !ed.selectbase) lgt=0; free(clipboard); clipboard=malloc(lgt+1); memcpy(clipboard,ed.selectstart,lgt); clipboard[lgt]='\0'; clipboard_store(); } void ed_paste() { char*s; clipboard_load(); s=clipboard; if(!s) return; while(*s) { ed_char(*s); s++; } } void ed_cut() { ed_copy(); ed_deleteselection(); } void ed_switchbuffers() { char tmp[sizeof(ed)]; memcpy((void*)&tmp,(void*)&ed,sizeof(ed)); memcpy((void*)&ed,(void*)&ed_parallel,sizeof(ed)); memcpy((void*)&ed_parallel,(void*)&tmp,sizeof(ed)); } char*ed_getprogbuf() { if(!ed.readonly) return ed.textbuffer; else return ed_parallel.textbuffer; } /*** main loop etc ***/ void interactivemode(char*codetoload) { int codechanged=0; uint32_t prevtimevalue=gettimevalue(); SDL_Event e; SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,10); SDL_EnableUNICODE(1); ed.textbuffer=malloc(EDITBUFSZ*sizeof(char)); strncpy(ed.textbuffer,codetoload,EDITBUFSZ-1); ed_unselect(); ed.firsty=0; ed.cursor=ed.textbuffer; ed.readonly=0; ed_parallel.cursor= ed_parallel.selectstart= ed_parallel.selectend= ed_parallel.textbuffer=helpscreen; ed_parallel.readonly=1; #ifdef X11 SDL_EventState(SDL_SYSWMEVENT,SDL_ENABLE); #endif for(;;) { uint32_t t = gettimevalue(); if(prevtimevalue!=t || e.type!=SDL_NOEVENT) { updatescreen(); prevtimevalue=t; DEBUG(stderr,"t:%x audio:%x playback:%x video:%x\n", t,(vm.audiotime)+(((vm.mediacontext==1)?vm.sp:vm.cosp)>>10) ,(ui.auplaytime>>16)+(ui.auplayptr>>26),vm.videotime); } { static int lastpage=0; if(lastpage!=vm.visiblepage) { lastpage=vm.visiblepage; ui.framecounter++; if(ui.opt_nonrealtime) nrtframestep(); } } if(t>=120+ui.bmtime) { float secs=(t-ui.bmtime)/60.0; ui.mops=ui.cyclecounter/(secs*1000000); ui.fps=ui.framecounter/secs; ui.cyclecounter=ui.framecounter=0; ui.bmtime=t; } if(ui.runstat==0) { if(!ui.opt_playback) SDL_WaitEvent(&e); else { e.type=SDL_NOEVENT; SDL_PollEvent(&e); if(e.type==SDL_NOEVENT) pollplaybackevent(&e); if(e.type==SDL_NOEVENT && ui.opt_nonrealtime) nrtframestep(); } } else { e.type=SDL_NOEVENT; SDL_PollEvent(&e); if(ui.opt_playback && e.type==SDL_NOEVENT) pollplaybackevent(&e); if(e.type==SDL_NOEVENT) { if(codechanged) { vm_compile(ed_getprogbuf()); if(ui.audio_off) { ui.audio_off=0; pauseaudio(0); } codechanged=0; } { int c = vm_run(); ui.cyclecounter+=c; } if(ui.opt_nonrealtime) { dumper.subframe++; if(!(dumper.subframe&4095)) nrtframestep(); } checkmediaformats(); scheduler_check(); continue; } } if(e.type==SDL_QUIT) break; if(e.type==SDL_KEYDOWN) { int sym=e.key.keysym.sym; int mod=e.key.keysym.mod; if(ui.opt_dumpkeys) { static int last=0; int now=getticks(); if(!sym && e.key.keysym.unicode) sym=e.key.keysym.unicode; printf("%d %d %d %d\n",now-last,sym, e.key.keysym.unicode,mod); last=now; } getkeystates(); if(sym==SDLK_ESCAPE) break; else if(sym==SDLK_TAB) { ui.osd_visible^=1; } else if(sym==SDLK_F1) { pauseaudio(ui.runstat); ui.runstat^=1; if(ui.runstat==0) { ui.paused_since=getticks(); } else { ui.timercorr+=getticks()-ui.paused_since; ui.mops=ui.fps=ui.bmtime=0; } } else if(sym==SDLK_F2) { ui.timercorr=ui.paused_since=getticks(); if(codechanged) { vm_compile(ed_getprogbuf()); codechanged=0; } vm_init(); ui.auplayptr=ui.auplaytime=0; pauseaudio(ui.runstat^1); } else if(ui.osd_visible) { /* editor keys */ if(sym==SDLK_UP && (mod&KMOD_CTRL)) { ed_increment(ed.cursor); codechanged=1; } else if(sym==SDLK_DOWN && (mod&KMOD_CTRL)) { ed_decrement(ed.cursor); codechanged=1; } else if(sym==SDLK_LEFT && (mod&KMOD_CTRL)) { ed_prev(); } else if(sym==SDLK_RIGHT && (mod&KMOD_CTRL)) { ed_next(); } else if(sym==SDLK_LEFT) { ed_left(mod&KMOD_SHIFT); } else if(sym==SDLK_RIGHT) { ed_right(mod&KMOD_SHIFT); } else if(sym==SDLK_UP) { ed_up(mod&KMOD_SHIFT); } else if(sym==SDLK_DOWN) { ed_down(mod&KMOD_SHIFT); } else if(sym==SDLK_BACKSPACE) { ed_backspace(-1); codechanged=1; } else if(sym==SDLK_DELETE) { ed_backspace(0); codechanged=1; } else if(sym==SDLK_F12) { ed_switchbuffers(); } else if(sym=='s' && (mod&KMOD_CTRL)) { ed_save(); } else if(sym=='c' && (mod&KMOD_CTRL)) { ed_copy(); } else if(sym=='k' && (mod&KMOD_CTRL)) { ed_copy(); } else if(sym=='v' && (mod&KMOD_CTRL)) { ed_paste(); } else if(sym=='x' && (mod&KMOD_CTRL)) { ed_cut(); } else if(sym=='a' && (mod&KMOD_CTRL)) { if(ed.selectbase) ed_unselect(); else { ed.selectstart=ed.textbuffer; ed.selectend=ed.textbuffer+strlen(ed.textbuffer); ed.selectbase=ed.cursor; } } /* else if(sym=='b' && (mod&KMOD_CTRL)) { ui.benchmark_mode^=1; } */ else { if(e.key.keysym.unicode) { ed_char(e.key.keysym.unicode); codechanged=1; } } } } else if(e.type==SDL_KEYUP) { getkeystates(); } else if(e.type==SDL_MOUSEMOTION) { int y=(e.motion.y*256)/sdl.winsz; int x=(e.motion.x*256)/sdl.winsz; if(y>=0 && x>=0 && y<=255 && x<=255) vm.userinput=(vm.userinput&0xFFFF0000)|(y<<8)|x; } else if(e.type==SDL_MOUSEBUTTONDOWN) { vm.userinput|=0x80000000; } else if(e.type==SDL_MOUSEBUTTONUP) { vm.userinput&=0x7FFFFFFF; } else if(e.type==SDL_VIDEORESIZE) { sdl.winsz=e.resize.w #ifndef IBNIZ_H #define IBNIZ_H #ifdef IBNIZ_MAIN # define GLOBAL #else # define GLOBAL extern #endif #include "vm.h" /* i/o stuff used by vm */ uint32_t gettimevalue(); int getuserchar(); void waitfortimechange(); /* vm-specific */ void vm_compile(char*src); void vm_init(); int vm_run(); void switchmediacontext(); GLOBAL char*clipboard; void clipboard_load(); void clipboard_store(); #if defined(WIN32) #define CLIPBOARD_WIN32 #elif defined(X11) #define CLIPBOARD_X11 #include void clipboard_handlesysreq(SDL_Event*e); #else #define CLIPBOARD_NONE #endif #endif ibniz-1.18/vm_test.c0000644000175000017500000000170111701150253014055 0ustar viznutviznut#define IBNIZ_MAIN #include #include #include "ibniz.h" void waitfortimechange() { } uint32_t gettimevalue() { return 0; } int runtest(char*code,uint32_t correct_stacktop) { vm_init(); vm_compile(code); while(!vm.stopped) vm_run(); if(vm.stack[vm.sp]==correct_stacktop) return 0; fprintf(stderr,"unit test failed with code \"%s\"\n",code); fprintf(stderr,"stacktop=%x (should have been %x)\n", vm.stack[vm.sp],correct_stacktop); exit(1); } void test_numbers() { runtest("12345T",0x23450001); runtest("F.1234T",0xF1234); runtest("123456789ABCDT",0xABCD6789); runtest("1234.56789AT",0x9A345678); runtest("1.15.25+T",0x13A00); } int main() { /* TODO: i guess we need a little bit more coverage here */ test_numbers(); runtest("1,1+T",2<<16); runtest("6,6*T",36<<16); runtest("1,4X3*LT",81<<16); runtest("1,2,3,2)T",1<<16); runtest("3?5:1;T",5<<16); runtest("0?5:1;T",1<<16); return 0; }