summaryrefslogtreecommitdiff
path: root/src/rblib.c
diff options
context:
space:
mode:
authorJeffrey Armstrong <jeff@approximatrix.com>2020-09-28 20:25:16 -0400
committerJeffrey Armstrong <jeff@approximatrix.com>2020-09-28 20:25:16 -0400
commit334ee3f66a7aad17c9865bd9952b0c501b97721a (patch)
tree0ee71269e8c482e6a2df9cbb37dbe15302136799 /src/rblib.c
downloadrainbow-lua-334ee3f66a7aad17c9865bd9952b0c501b97721a.zip
rainbow-lua-334ee3f66a7aad17c9865bd9952b0c501b97721a.tar.gz
Initial import based on Lua 5.3.5
Diffstat (limited to 'src/rblib.c')
-rw-r--r--src/rblib.c479
1 files changed, 479 insertions, 0 deletions
diff --git a/src/rblib.c b/src/rblib.c
new file mode 100644
index 0000000..ec4d8a5
--- /dev/null
+++ b/src/rblib.c
@@ -0,0 +1,479 @@
+#include <stdio.h>
+
+#include <dos.h>
+#include <i86.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include "lua.h"
+#include "lauxlib.h"
+
+#define RAINBOW_CONSOLE_INTR 0x18
+#define RAINBOW_TIMER_INTR 0x64
+
+#define RAINBOW_KEYS_L2FULL ((unsigned char)1)
+#define RAINBOW_KEYS_NONE ((unsigned char)0)
+#define RAINBOW_KEYS_AVAIL ((unsigned char)0xFF)
+
+#ifdef __WATCOMC__
+#define FAR_MEMCPY(x,y,n) _fmemcpy(x,y,n)
+#define FAR_MEMSET(x,c,n) _fmemset(x,c,n)
+#else
+#define FAR_MEMCPY(x,y,n) memcpy(x,y,n)
+#define FAR_MEMSET(x,c,n) memset(x,c,n)
+#endif
+
+static long _rb_ticker;
+
+static void (__interrupt __far *_original_rb_tick)();
+
+static void __interrupt __far _rb_tick()
+{
+ _rb_ticker++;
+ _chain_intr(_original_rb_tick);
+}
+
+static void unload_timer()
+{
+ _dos_setvect(RAINBOW_TIMER_INTR, _original_rb_tick);
+}
+
+static int rb_ticks(lua_State *L) {
+ lua_pushinteger(L, _rb_ticker);
+ return 1;
+}
+
+/* 0 for 60Hz, 1 for 50Hz */
+static int _clock_rate() {
+ union REGS registers;
+
+ registers.w.di = 0xE;
+ int86(RAINBOW_CONSOLE_INTR, &registers, &registers);
+
+ return registers.h.al;
+}
+
+static int rb_ticks_per_second(lua_State *L) {
+
+ if(_clock_rate() == 1)
+ lua_pushinteger(L, 50);
+ else
+ lua_pushinteger(L, 60);
+
+ return 1;
+}
+
+static int rb_sleep(lua_State *L) {
+ long divisor;
+ long mult;
+ long start;
+ long desired_ms;
+ long diff;
+
+ start = _rb_ticker;
+
+ if(_clock_rate() == 1) {
+ mult = 1L;
+ divisor = 20L;
+ } else {
+ mult = 100L;
+ divisor = 6L;
+ }
+
+ desired_ms = (long)luaL_checkinteger(L, 1);
+ desired_ms = desired_ms * divisor / mult;
+
+ diff = (_rb_ticker - start);
+ while(diff < desired_ms) {
+ diff = (_rb_ticker - start);
+ }
+
+ return 0;
+}
+
+char far *_get_screen_address(int r, int c)
+{
+int i;
+unsigned int offset;
+char far *ch;
+unsigned int segment;
+
+ segment = 0xEE00;
+ offset = 0;
+
+ for(i = -1; i < r; i++) {
+ ch = MK_FP(segment, offset);
+ while((*ch) != (char)0xFF) {
+ ch++;
+ }
+ offset = *(unsigned int far *)(ch+1);
+ }
+ return (char far *)MK_FP(segment, offset+c-1);
+}
+
+static void _escseq_f(const char *fmt, ...)
+{
+char *fullfmt;
+va_list args;
+
+ if(fmt == NULL) return;
+
+ fullfmt = (char *)malloc((strlen(fmt)+3)*sizeof(char));
+ if(fullfmt == NULL) return;
+
+ fullfmt[0] = (char)27;
+ strcpy(&fullfmt[1], fmt);
+
+ va_start(args, fmt);
+ vprintf(fullfmt, args);
+ va_end(args);
+
+ free(fullfmt);
+}
+
+static int rb_escape(lua_State *L) {
+const char *s;
+
+ s = luaL_checkstring(L, 1);
+ _escseq_f(s);
+
+ return 0;
+}
+
+static int _get_column_count() {
+unsigned char far *vram;
+
+ /* Reset the video ram (shouldn't erase anything...) */
+ /* _escseq_f("[?3l"); */
+ /* Check if the line ends after 80 characters */
+ vram = MK_FP(0xEE00,0x0062);
+ if(*vram ==(unsigned char)0xFF)
+ return 80;
+ else
+ return 132;
+}
+
+static int rb_columns (lua_State *L) {
+int colrequest;
+
+ if(lua_gettop(L) > 0)
+ colrequest = luaL_checkinteger(L, 1);
+ else
+ colrequest = 0;
+
+ if(colrequest > 0) {
+
+ if(colrequest == 80)
+ _escseq_f("[?3l");
+ else if(colrequest == 132)
+ _escseq_f("[?3h");
+ else
+ luaL_error(L, "hardware only support 80 or 132 columns");
+
+ lua_pushinteger(L, colrequest);
+
+ } else {
+
+ lua_pushinteger(L, _get_column_count());
+
+ }
+ return 1;
+}
+
+static int _checkinteger_bounds(lua_State *L, int index, int lower, int upper)
+{
+int value;
+
+ value = (int)luaL_checkinteger(L, index);
+ if(value < lower || value > upper)
+ luaL_error(L, "argument %d not within allowable bounds [%d, %d]", index, lower, upper);
+
+ return value;
+}
+
+static int rb_locate (lua_State *L) {
+ int r = (int)_checkinteger_bounds(L, 1, 1, 24);
+ int c = (int)_checkinteger_bounds(L, 2, 1, _get_column_count());
+
+ _escseq_f("[%c;%cH", (char)r, (char)c);
+ return 0;
+}
+
+static int rb_bell (lua_State *L) {
+ union REGS registers;
+
+ registers.w.di = 0x1e;
+ int86(RAINBOW_CONSOLE_INTR, &registers, &registers);
+
+ return 0;
+}
+
+static void _outtextat(int r, int c, const char far *txt, char attr)
+{
+char far *base_screen;
+char far *screen;
+char far *attributes;
+int i, n;
+ if(txt == NULL) return;
+
+ base_screen = _get_screen_address(r, c);
+ screen = base_screen;
+ while(*txt != '\0' && *screen != (char)0xFF) {
+ *screen = *txt;
+ screen++;
+ txt++;
+ }
+
+ if(attr != (char)0xFF) {
+ attributes = MK_FP(0xEF00, FP_OFF(base_screen));
+ FAR_MEMSET(attributes, (int)attr,
+ FP_OFF(screen) - FP_OFF(base_screen));
+ }
+}
+
+static int rb_outtext (lua_State *L) {
+ const char far *text = luaL_checkstring(L, 3);
+ int r = (int)_checkinteger_bounds(L, 1, 1, 24);
+ int c = (int)_checkinteger_bounds(L, 2, 1, _get_column_count() - strlen(text) + 1);
+ unsigned char attribute_flags = 0;
+ int i;
+ int textlength;
+ char attr;
+
+ if(text != NULL)
+ textlength = strlen(text);
+ if(textlength == 0)
+ return 0;
+
+ attr = (char)0xFF;
+
+ if(lua_gettop(L) > 3) {
+ if(lua_istable(L, 4)) {
+ i = 1;
+ lua_rawgeti(L, 4, i);
+ while(!lua_isnil(L, 1)) {
+ attribute_flags += (unsigned char)luaL_checkinteger(L,1);
+ i++;
+ lua_rawgeti(L, 4, i);
+ }
+ } else {
+ i = 4;
+ while(i <= lua_gettop(L)) {
+ attribute_flags += (unsigned char)luaL_checkinteger(L,i);
+ i++;
+ }
+ }
+
+ if(attribute_flags != 0) {
+ /* We have to XOR bits 1, 2, and 3... */
+ attr = attribute_flags ^ (unsigned char)14;
+ }
+ }
+
+ _outtextat(r, c, text, attr);
+
+ return 0;
+}
+
+static int rb_clearscreen (lua_State *L) {
+ char far *line;
+ unsigned int offset;
+ int i;
+
+ line = (char *)malloc(133*sizeof(char));
+ if(line == NULL) return 0;
+
+ memset(line, (unsigned char)' ', 132);
+ line[132] = '\0';
+
+ for(i=1;i<=24;i++)
+ _outtextat(i,1,line,(char)14);
+
+ free(line);
+
+ /* Put the cursor at the top of the screen */
+ _escseq_f("[0;0H");
+
+ return 0;
+}
+
+static int rb_getscreen (lua_State *L) {
+ int r = (int)_checkinteger_bounds(L, 1, 1, 24);
+ int c = (int)_checkinteger_bounds(L, 2, 1, _get_column_count());
+ char far *screen;
+ char local_char;
+
+ screen = _get_screen_address(r, c);
+ if(screen == NULL)
+ lua_pushnil(L);
+ else {
+ local_char = *screen;
+ lua_pushlstring(L, &local_char, 1);
+ }
+
+ return 1;
+}
+
+static int rb_peek(lua_State *L) {
+ uint8_t far *adr;
+ uint16_t segment = luaL_checkinteger(L, 1);
+ uint16_t offset = luaL_checkinteger(L, 2);
+
+ adr = MK_FP(segment, offset);
+ lua_pushinteger(L, *adr);
+
+ return 1;
+}
+
+static unsigned char _level2_key(int *key) {
+ union REGS registers;
+
+ registers.w.di = 0x2;
+ int86(RAINBOW_CONSOLE_INTR, &registers, &registers);
+
+ *key = registers.w.ax;
+
+ return registers.h.cl;
+}
+
+static unsigned char _level1_key(int *key) {
+ union REGS registers;
+
+ registers.w.di = 0x6;
+ int86(RAINBOW_CONSOLE_INTR, &registers, &registers);
+
+ *key = registers.w.ax;
+
+ return registers.h.cl;
+}
+
+static int _level1_getkey_generic(lua_State *L, int blocking) {
+unsigned char status;
+int key;
+
+ status = _level1_key(&key);
+ while(status == RAINBOW_KEYS_L2FULL || (blocking && status == RAINBOW_KEYS_NONE)) {
+ if(status == RAINBOW_KEYS_L2FULL)
+ _level2_key(&key);
+ status = _level1_key(&key);
+ }
+
+ if(status == RAINBOW_KEYS_NONE)
+ lua_pushnil(L);
+ else
+ lua_pushinteger(L, (int)key);
+
+ return 1;
+}
+
+static int rb_getkey(lua_State *L) {
+ return _level1_getkey_generic(L, 0);
+}
+
+static int rb_inkey(lua_State *L) {
+ return _level1_getkey_generic(L, 1);
+}
+
+static int rb_linemode(lua_State *L) {
+int mode;
+int line;
+char far *screen;
+char far *line_attr;
+
+ line = luaL_checkinteger(L, 1);
+
+ if(lua_gettop(L) > 1)
+ mode = luaL_checkinteger(L, 2);
+ else
+ mode = 0;
+
+ screen = _get_screen_address(line, 1);
+ while(*screen != (char)0xFF) screen++;
+ line_attr = MK_FP(0xEF00, FP_OFF(screen)+1);
+
+ switch(mode) {
+ case 3:
+ case 4:
+ *line_attr = (char)2;
+ break;
+ case 6:
+ *line_attr = (char)4;
+ break;
+ case 5:
+ default:
+ *line_attr = (char)0;
+ break;
+ }
+
+ return 0;
+}
+
+static const luaL_Reg rblib[] = {
+ {"bell", rb_bell},
+ {"cls", rb_clearscreen},
+ {"locate", rb_locate},
+ {"outtext", rb_outtext},
+ {"getscreen", rb_getscreen},
+ {"columns", rb_columns},
+ {"inkey", rb_inkey},
+ {"getkey", rb_getkey},
+ {"ticks", rb_ticks},
+ {"tick_rate", rb_ticks_per_second},
+ {"sleep", rb_sleep},
+ {"peek", rb_peek},
+ {"escape", rb_escape},
+ {"linemode", rb_linemode},
+
+ {"attribute", NULL},
+ {"linemodes", NULL},
+ {NULL, NULL}
+};
+
+LUAMOD_API int luaopen_rb (lua_State *L) {
+ luaL_newlib(L, rblib);
+
+ /* Attribute constants */
+ lua_createtable(L, 0, 5);
+
+ lua_pushinteger(L, 0);
+ lua_setfield(L, -2, "normal");
+
+ lua_pushinteger(L, 1);
+ lua_setfield(L, -2, "reverse");
+
+ lua_pushinteger(L, 2);
+ lua_setfield(L, -2, "bold");
+
+ lua_pushinteger(L, 4);
+ lua_setfield(L, -2, "blink");
+
+ lua_pushinteger(L, 8);
+ lua_setfield(L, -2, "underline");
+
+ lua_setfield(L, -2, "attribute");
+
+ /* Line mode constants */
+ lua_createtable(L, 0, 4);
+
+ lua_pushinteger(L, 5);
+ lua_setfield(L, -2, "normal");
+
+ lua_pushinteger(L, 6);
+ lua_setfield(L, -2, "wide");
+
+ lua_pushinteger(L, 3);
+ lua_setfield(L, -2, "wide_tall_top");
+
+ lua_pushinteger(L, 4);
+ lua_setfield(L, -2, "wide_tall_bottom");
+
+ lua_setfield(L, -2, "linemodes");
+
+ /* Need to set up timers */
+ _rb_ticker = 0;
+ _original_rb_tick = _dos_getvect(RAINBOW_TIMER_INTR);
+ _dos_setvect(RAINBOW_TIMER_INTR, _rb_tick);
+ atexit(unload_timer);
+
+ return 1;
+}