Some initial VIOCALLS support via ncurses, other 16-bit updates.
authorRyan C. Gordon <icculus@icculus.org>
Sat, 29 Oct 2016 02:05:01 -0400
changeset 71 1a6a5ecb59de
parent 70 118c53b69851
child 72 75ded8736bbd
Some initial VIOCALLS support via ncurses, other 16-bit updates.
CMakeLists.txt
lx_loader.c
lx_loader.h
native/doscalls.c
native/doscalls.h
native/os2native16.h
native/os2types.h
native/viocalls.c
native/viocalls.h
--- a/CMakeLists.txt	Fri Oct 28 02:32:05 2016 -0400
+++ b/CMakeLists.txt	Sat Oct 29 02:05:01 2016 -0400
@@ -46,6 +46,24 @@
 add_definitions(-std=c99 -Wall -ggdb3)
 add_definitions(-D_FILE_OFFSET_BITS=64)
 
+set(CURSES_NEED_WIDE TRUE)
+set(CURSES_NEED_NCURSES TRUE)
+find_package(Curses)
+if(NOT CURSES_FOUND)
+    message(FATAL_ERROR "ncurses not found")
+endif()
+
+include_directories(${CURSES_INCLUDE_DIR})
+if(CURSES_HAVE_NCURSESW_NCURSES_H)
+    add_definitions(-DHAVE_NCURSESW_NCURSES_H)
+elseif(CURSES_HAVE_NCURSESW_CURSES_H)
+    add_definitions(-DHAVE_NCURSESW_CURSES_H)
+elseif(CURSES_HAVE_NCURSESW_H)
+    add_definitions(-DHAVE_NCURSESW_H)
+elseif(CURSES_HAVE_CURSES_H)
+    add_definitions(-DHAVE_CURSES_H)
+endif()
+
 foreach(_I doscalls;msg;nls;quecalls;viocalls;kbdcalls;sesmgr)
     add_library(${_I} SHARED "native/${_I}.c")
     set_target_properties(${_I} PROPERTIES COMPILE_FLAGS "-m32")
@@ -53,6 +71,8 @@
 endforeach()
 
 target_link_libraries(doscalls "pthread")
+# FIXME target_link_libraries(viocalls ${CURSES_LIBRARIES})
+target_link_libraries(viocalls ncursesw)
 
 add_executable(lx_dump lx_dump.c)
 
--- a/lx_loader.c	Fri Oct 28 02:32:05 2016 -0400
+++ b/lx_loader.c	Sat Oct 29 02:05:01 2016 -0400
@@ -449,6 +449,9 @@
 
 static void *convert1616to32(const uint32 addr1616)
 {
+    if (addr1616 == 0)
+        return NULL;
+
     const uint16 selector = (uint16) (addr1616 >> 19);  // slide segment down, and shift out control bits.
     const uint16 offset = (uint16) (addr1616 % 0x10000);  // all our LDT segments start at 64k boundaries (at the moment!).
     assert(GLoaderState->ldt[selector] != 0);
@@ -456,6 +459,25 @@
     return (void *) (size_t) (GLoaderState->ldt[selector] + offset);
 } // convert1616to32
 
+static uint32 convert32to1616(void *addr32)
+{
+    if (addr32 == NULL)
+        return 0;
+
+    uint16 selector = 0;
+    uint16 offset = 0;
+    if (!findSelector((uint32) addr32, &selector, &offset)) {
+        fprintf(stderr, "Uhoh, ran out of LDT entries?!\n");
+        return 0;  // oh well, crash, probably.
+    } // if
+
+    //printf("selector: 0x%X\n", (uint) selector);
+    selector = (selector << 3) | 7;
+    //printf("shifted selector: 0x%X\n", (uint) selector);
+    return (((uint32)selector) << 16) | ((uint32) offset);
+} // convert32to1616
+
+
 // EMX (and probably many other things) occasionally has to call a 16-bit
 //  system API, and assumes its stack is tiled in the LDT; it'll just shift
 //  the stack pointer and use it as a stack segment for the 16-bit call
@@ -2066,6 +2088,7 @@
     GLoaderState->findSelector = findSelector;
     GLoaderState->freeSelector = freeSelector;
     GLoaderState->convert1616to32 = convert1616to32;
+    GLoaderState->convert32to1616 = convert32to1616;
     GLoaderState->loadModule = loadLxModuleByPathOrModuleName;
     GLoaderState->locatePathCaseInsensitive = locatePathCaseInsensitive;
     GLoaderState->makeUnixPath = makeUnixPath;
--- a/lx_loader.h	Fri Oct 28 02:32:05 2016 -0400
+++ b/lx_loader.h	Sat Oct 29 02:05:01 2016 -0400
@@ -226,6 +226,7 @@
     int (*findSelector)(const uint32 addr, uint16 *outselector, uint16 *outoffset);
     void (*freeSelector)(const uint16 selector);
     void *(*convert1616to32)(const uint32 addr1616);
+    uint32 (*convert32to1616)(void *addr32);
     LxModule *(*loadModule)(const char *modname);
     int (*locatePathCaseInsensitive)(char *buf);
     char *(*makeUnixPath)(const char *os2path, uint32 *err);
--- a/native/doscalls.c	Fri Oct 28 02:32:05 2016 -0400
+++ b/native/doscalls.c	Sat Oct 29 02:05:01 2016 -0400
@@ -240,6 +240,7 @@
     LX_NATIVE_EXPORT(DosExitMustComplete, 381),
     LX_NATIVE_EXPORT(DosSetRelMaxFH, 382),
     LX_NATIVE_EXPORT(DosFlatToSel, 425),
+    LX_NATIVE_EXPORT(DosSelToFlat, 426),
     LX_NATIVE_EXPORT(DosAllocThreadLocalMemory, 454),
     LX_NATIVE_EXPORT(DosFreeThreadLocalMemory, 455),
     LX_NATIVE_EXPORT(DosR3ExitAddr, 553),
@@ -619,16 +620,7 @@
 ULONG _DosFlatToSel(PVOID ptr)
 {
     TRACE_NATIVE("DosFlatToSel(%p)", ptr);
-
-    uint16 selector = 0;
-    uint16 offset = 0;
-    if (!GLoaderState->findSelector((uint32) ptr, &selector, &offset)) {
-        fprintf(stderr, "Uhoh, ran out of LDT entries?!\n");
-        return 0;  // oh well, crash.
-    } // if
-
-    selector = (selector << 3) | 7;
-    return (((uint32)selector) << 16) | ((uint32) offset);
+    return GLoaderState->convert32to1616(ptr);
 } // _DosFlatToSel
 
 // DosFlatToSel() passes its argument in %eax, so a little asm to bridge that...
@@ -2851,5 +2843,23 @@
     return ERROR_INVALID_PARAMETER;
 } // DosQueryThreadContext
 
+ULONG _DosSelToFlat(void *ptr)
+{
+    TRACE_NATIVE("DosSelToFlat(%p)", ptr);
+    return (ULONG) GLoaderState->convert1616to32((uint32) ptr);
+} // _DosSelToFlat
+
+// DosSelToFlat() passes its argument in %eax, so a little asm to bridge that...
+__asm__ (
+    ".globl DosSelToFlat  \n\t"
+    ".type	DosSelToFlat, @function \n\t"
+    "DosSelToFlat:  \n\t"
+    "    pushl %eax  \n\t"
+    "    call _DosSelToFlat  \n\t"
+    "    addl $4, %esp  \n\t"
+    "    ret  \n\t"
+	".size	_DosSelToFlat, .-_DosSelToFlat  \n\t"
+);
+
 // end of doscalls.c ...
 
--- a/native/doscalls.h	Fri Oct 28 02:32:05 2016 -0400
+++ b/native/doscalls.h	Sat Oct 29 02:05:01 2016 -0400
@@ -445,6 +445,7 @@
 APIRET OS2API DosQueryExtLIBPATH(PSZ pszExtLIBPATH, ULONG flags);
 APIRET OS2API DosSetMaxFH(ULONG cFH);
 APIRET OS2API DosQueryThreadContext(TID tid, ULONG level, PCONTEXTRECORD pcxt);
+ULONG OS2API DosSelToFlat(VOID);
 
 #ifdef __cplusplus
 }
--- a/native/os2native16.h	Fri Oct 28 02:32:05 2016 -0400
+++ b/native/os2native16.h	Sat Oct 29 02:05:01 2016 -0400
@@ -201,6 +201,12 @@
     } \
 }
 
+#define LX_NATIVE_MODULE_16BIT_BRIDGE_ARG(typ, var) \
+    const typ var = *((typ *) args); args += sizeof (typ)
+
+#define LX_NATIVE_MODULE_16BIT_BRIDGE_PTRARG(typ, var) \
+    typ var = (typ) GLoaderState->convert1616to32(*((uint32 *) args)); args += sizeof (uint32)
+
 #define LX_NATIVE_EXPORT16(fn, ord) { ord, #fn, &fn##16, &obj16 }
 
 #endif
--- a/native/os2types.h	Fri Oct 28 02:32:05 2016 -0400
+++ b/native/os2types.h	Sat Oct 29 02:05:01 2016 -0400
@@ -68,6 +68,7 @@
 typedef SHANDLE HVIO, *PHVIO;
 typedef SHANDLE HKBD, *PHKBD;
 typedef PCHAR PSZ;
+typedef PCHAR PCH;
 
 typedef int (APIENTRY *PFN)(void);
 
--- a/native/viocalls.c	Fri Oct 28 02:32:05 2016 -0400
+++ b/native/viocalls.c	Sat Oct 29 02:05:01 2016 -0400
@@ -1,6 +1,190 @@
 #include "os2native16.h"
 #include "viocalls.h"
 
+#include <unistd.h>
+#include <ctype.h>
+
+// CMake searches for a whole bunch of different possible curses includes
+#if defined(HAVE_NCURSESW_NCURSES_H)
+#include <ncursesw/ncurses.h>
+#elif defined(HAVE_NCURSESW_CURSES_H)
+#include <ncursesw/curses.h>
+#elif defined(HAVE_NCURSESW_H)
+#include <ncursesw.h>
+#elif defined(HAVE_CURSES_H)
+#include <curses.h>
+#else
+#error ncurses gui enabled, but no known header file found
+#endif
+
+#include <locale.h>
+
+enum
+{
+    VIOATTR_BACK_BLACK = 0x00,
+    VIOATTR_BACK_BLUE = 0x10,
+    VIOATTR_BACK_GREEN = 0x20,
+    VIOATTR_BACK_CYAN = 0x30,
+    VIOATTR_BACK_RED = 0x40,
+    VIOATTR_BACK_MAGENTA = 0x50,
+    VIOATTR_BACK_YELLOW = 0x60,
+    VIOATTR_BACK_BROWN = 0x60,
+    VIOATTR_BACK_WHITE = 0x70,
+
+    VIOATTR_FORE_BLACK = 0x00,
+    VIOATTR_FORE_BLUE = 0x01,
+    VIOATTR_FORE_GREEN = 0x02,
+    VIOATTR_FORE_CYAN = 0x03,
+    VIOATTR_FORE_RED = 0x04,
+    VIOATTR_FORE_MAGENTA = 0x05,
+    VIOATTR_FORE_YELLOW = 0x06,
+    VIOATTR_FORE_BROWN = 0x06,
+    VIOATTR_FORE_WHITE = 0x07,
+
+    VIOATTR_INTENSITY = 0x08,
+    VIOATTR_BLINK = 0x80
+};
+
+
+static uint16 *vio_buffer = NULL;
+static uint16 vio_scrw, vio_scrh;
+static uint16 vio_curx, vio_cury;
+static VIOCURSORINFO vio_cursorinfo;
+
+static int initNcurses(void)
+{
+    if (vio_buffer != NULL)
+        return 1;
+
+    setlocale(LC_CTYPE, ""); // !!! FIXME: we assume you have a UTF-8 terminal.
+    if (initscr() == NULL) {
+        fprintf(stderr, "ncurses: initscr() failed\n");
+        return 0;
+    } // if
+
+	cbreak();
+	keypad(stdscr, TRUE);
+	noecho();
+    start_color();
+    use_default_colors();
+
+    // map to VIO attributes...
+    static const short curses_colormap[] = {
+        COLOR_BLACK, COLOR_BLUE, COLOR_GREEN, COLOR_CYAN,
+        COLOR_RED, COLOR_MAGENTA, COLOR_YELLOW, COLOR_WHITE
+    };
+
+    if (COLORS >= 64) {   // foreground and background
+        for (uint32 i = 0; i < 8; i++) {
+            for (uint32 j = 0; j < 8; j++) {
+                const int pair = (int) (i | (j << 3));
+                init_pair(pair, curses_colormap[i], (j == 0) ? -1 : curses_colormap[j]);
+            } // for
+        } // for
+    } else if (COLORS >= 8) {  // just foregrounds
+        for (int i = 0; i < 8; i++)
+            init_pair(i, curses_colormap[i], COLOR_BLACK);
+    } // else if
+
+    // (otherwise, we won't set colors at all.)
+
+    int scrh, scrw;
+    getmaxyx(stdscr, scrh, scrw);
+
+    const size_t buflen = scrw * scrh * sizeof (uint16);
+    vio_buffer = malloc(buflen);
+    if (!vio_buffer) {
+        endwin();
+        delwin(stdscr);  // not sure if this is safe, but valgrind said it leaks.
+        stdscr = NULL;
+        return 0;
+    } // if
+
+    memset(vio_buffer, '\0', buflen);
+    vio_scrw = (uint16) scrw;
+    vio_scrh = (uint16) scrh;
+    vio_curx = vio_cury = 0;
+
+    FIXME("these are just the default values OS/2 4.52 returns");
+    vio_cursorinfo.yStart = 15;
+    vio_cursorinfo.cEnd = 15;
+    vio_cursorinfo.cx = 1;
+    vio_cursorinfo.attr = 0;
+
+    return 1;
+} // initNcurses
+
+static void deinitNcurses(void)
+{
+    if (!vio_buffer)
+        return;
+
+    // !!! FIXME: this is wrong
+    //endwin();
+    reset_shell_mode();
+
+    printf("\n"); fflush(stdout);
+
+    delwin(stdscr);  // not sure if this is safe, but valgrind said it leaks.
+    stdscr = NULL;
+    free(vio_buffer);
+    vio_buffer = NULL;
+    vio_curx = vio_cury = vio_scrw = vio_scrh = 0;
+} // deinitNcurses
+
+static inline void commitToNcurses(void)
+{
+    refresh();
+} // commitToNcurses
+
+static void pushToNcurses(const int y, const int x, int numcells, const int commit)
+{
+    const uint8 *src = (const uint8 *) (vio_buffer + ((y * vio_scrw) + x));
+    const uint32 avail = (((vio_scrh - y) * vio_scrw) - x);
+    if (numcells > avail)
+        numcells = avail;
+
+    move(y, x);
+
+    for (int i = 0; i < numcells; i++) {
+        const uint8 viochar = *(src++);
+        const uint8 vioattr = *(src++);
+
+        chtype ch;
+        switch (viochar) {
+            case 0xC4: ch = ACS_HLINE; break;
+            case 0xB3: ch = ACS_VLINE; break;
+            case 0xDA: ch = ACS_ULCORNER; break;
+            case 0xBF: ch = ACS_URCORNER; break;
+            case 0xC0: ch = ACS_LLCORNER; break;
+            case 0xD9: ch = ACS_LRCORNER; break;
+            default: ch = (chtype) viochar; break;
+        } // switch
+
+        chtype attr = 0;
+
+        if (COLORS >= 64) {   // foreground and background
+            const int color = ((vioattr & 0x70) >> 1) | (vioattr & 0x7);
+            attr |= COLOR_PAIR(color);
+        } else if (COLORS >= 8) {  // just foregrounds
+            const int color = (vioattr & 0x7);
+            attr |= COLOR_PAIR(color);
+        } // else if
+
+        if (vioattr & VIOATTR_INTENSITY)
+            attr |= A_BOLD;
+        if (vioattr & VIOATTR_BLINK)
+            attr |= A_BLINK;
+
+        addch(ch | attr);
+    } // for
+
+    move(vio_cury, vio_curx);
+
+    if (commit)
+        commitToNcurses();
+} // pushToNcurses
+
 APIRET16 VioGetMode(PVIOMODEINFO pvioModeInfo, HVIO hvio)
 {
     TRACE_NATIVE("VioGetMode(%p, %u)", pvioModeInfo, (uint) hvio);
@@ -11,18 +195,20 @@
         return ERROR_VIO_INVALID_PARMS;
     else if (pvioModeInfo->cb != sizeof (*pvioModeInfo))
         return ERROR_VIO_INVALID_LENGTH;
+    else if (!initNcurses())
+        return ERROR_VIO_INVALID_HANDLE;
 
     memset(pvioModeInfo, '\0', sizeof (*pvioModeInfo));
     pvioModeInfo->cb = sizeof (*pvioModeInfo);
     pvioModeInfo->fbType = VGMT_OTHER;
-    pvioModeInfo->color = 8;  // bits?
-    pvioModeInfo->col = 80;
-    pvioModeInfo->row = 25;
-    FIXME("I don't know what most of these fields do");
-    //pvioModeInfo->hres = 640;
-    //pvioModeInfo->vres = 480;
-    //UCHAR fmt_ID;
-    //UCHAR attrib;
+    pvioModeInfo->color = 4;
+    pvioModeInfo->col = vio_scrw;
+    pvioModeInfo->row = vio_scrh;
+    pvioModeInfo->hres = 640;
+    pvioModeInfo->vres = 400;
+    pvioModeInfo->fmt_ID = 0;
+    pvioModeInfo->attrib = 1;
+    FIXME("fill in the rest of these");
     //ULONG buf_addr;
     //ULONG buf_length;
     //ULONG full_length;
@@ -34,8 +220,8 @@
 
 static APIRET16 bridge16to32_VioGetMode(uint8 *args)
 {
-    const HVIO hvio = *((HVIO *) args); args += 2;
-    PVIOMODEINFO pvmi = (PVIOMODEINFO) GLoaderState->convert1616to32(*((uint32*) args)); //args += 4;
+    LX_NATIVE_MODULE_16BIT_BRIDGE_ARG(HVIO, hvio);
+    LX_NATIVE_MODULE_16BIT_BRIDGE_PTRARG(PVIOMODEINFO, pvmi);
     return VioGetMode(pvmi, hvio);
 } // bridge16to32_VioGetMode
 
@@ -46,48 +232,393 @@
 
     if (hvio != 0)
         return ERROR_VIO_INVALID_HANDLE;  // !!! FIXME: can be non-zero when VioCreatePS() is implemented.
+    else if (!initNcurses())
+        return ERROR_VIO_INVALID_HANDLE;
 
-    FIXME("write me");
     if (pusRow)
-        *pusRow = 0;
+        *pusRow = vio_cury;
+
     if (pusColumn)
-        *pusColumn = 0;
+        *pusColumn = vio_curx;
 
     return NO_ERROR;
 } // VioGetCurPos
 
 static APIRET16 bridge16to32_VioGetCurPos(uint8 *args)
 {
-    const HVIO hvio = *((HVIO *) args); args += 2;
-    PUSHORT pusRow = GLoaderState->convert1616to32(*((uint32*) args)); args += 4;
-    PUSHORT pusColumn = GLoaderState->convert1616to32(*((uint32*) args)); args += 4;
+    LX_NATIVE_MODULE_16BIT_BRIDGE_ARG(HVIO, hvio);
+    LX_NATIVE_MODULE_16BIT_BRIDGE_PTRARG(PUSHORT, pusRow);
+    LX_NATIVE_MODULE_16BIT_BRIDGE_PTRARG(PUSHORT, pusColumn);
     return VioGetCurPos(pusRow, pusColumn, hvio);
 } // bridge16to32_VioGetCurPos
 
+APIRET16 VioGetBuf(PULONG pLVB, PUSHORT pcbLVB, HVIO hvio)
+{
+    TRACE_NATIVE("VioGetBuf(%p, %p, %u)", pLVB, pcbLVB, (uint) hvio);
+
+    if (hvio != 0)
+        return ERROR_VIO_INVALID_HANDLE;  // !!! FIXME: can be non-zero when VioCreatePS() is implemented.
+    else if (!initNcurses())
+        return ERROR_VIO_INVALID_HANDLE;
+    if (pLVB)
+        *pLVB = (ULONG) vio_buffer;
+    if (pcbLVB)
+        *pcbLVB = vio_scrw * vio_scrh * sizeof (ULONG);
+    return NO_ERROR;
+} // VioGetBuf
+
+static APIRET16 bridge16to32_VioGetBuf(uint8 *args)
+{
+    LX_NATIVE_MODULE_16BIT_BRIDGE_ARG(HVIO, hvio);
+    LX_NATIVE_MODULE_16BIT_BRIDGE_PTRARG(PUSHORT, pcbLVB);
+    LX_NATIVE_MODULE_16BIT_BRIDGE_PTRARG(PULONG, pLVB);
+    const APIRET16 retval = VioGetBuf(pLVB, pcbLVB, hvio);
+    *pLVB = GLoaderState->convert32to1616((void *) *pLVB);
+    return retval;
+} // bridge16to32_VioGetBuf
+
+APIRET16 VioGetCurType(PVIOCURSORINFO pvioCursorInfo, HVIO hvio)
+{
+    TRACE_NATIVE("VioGetCurType(%p, %u)", pvioCursorInfo, (uint) hvio);
+
+    if (hvio != 0)
+        return ERROR_VIO_INVALID_HANDLE;  // !!! FIXME: can be non-zero when VioCreatePS() is implemented.
+    else if (pvioCursorInfo == NULL)
+        return ERROR_VIO_INVALID_PARMS;
+    else if (!initNcurses())
+        return ERROR_VIO_INVALID_HANDLE;
+
+    memcpy(pvioCursorInfo, &vio_cursorinfo, sizeof (*pvioCursorInfo));
+    return NO_ERROR;
+} // VioGetCurType
+
+static APIRET16 bridge16to32_VioGetCurType(uint8 *args)
+{
+    LX_NATIVE_MODULE_16BIT_BRIDGE_ARG(HVIO, hvio);
+    LX_NATIVE_MODULE_16BIT_BRIDGE_PTRARG(PVIOCURSORINFO, pvioCursorInfo);
+    return VioGetCurType(pvioCursorInfo, hvio);
+} // bridge16to32_VioGetCurType
+
+APIRET16 VioScrollUp(USHORT usTopRow, USHORT usLeftCol, USHORT usBotRow, USHORT usRightCol, USHORT cbLines, PBYTE pCell, HVIO hvio)
+{
+    TRACE_NATIVE("VioScrollUp(%u, %u, %u, %u, %u, %p, %u)", (uint) usTopRow, (uint) usLeftCol, (uint) usBotRow, (uint) usRightCol, (uint) cbLines, pCell, (uint) hvio);
+FIXME("buggy");
+return NO_ERROR;  // !!! FIXME: buggy
+    if (hvio != 0)
+        return ERROR_VIO_INVALID_HANDLE;  // !!! FIXME: can be non-zero when VioCreatePS() is implemented.
+    else if (pCell == NULL)
+        return ERROR_VIO_INVALID_PARMS;
+    else if (!initNcurses())
+        return ERROR_VIO_INVALID_HANDLE;
+
+    if (usLeftCol >= vio_scrw)
+        usLeftCol = vio_scrw - 1;
+    if (usRightCol >= vio_scrw)
+        usRightCol = vio_scrw - 1;
+    if (usTopRow >= vio_scrh)
+        usTopRow = vio_scrh - 1;
+    if (usBotRow >= vio_scrh)
+        usBotRow = vio_scrh - 1;
+
+    if (usLeftCol >= usRightCol)
+        return NO_ERROR;  // already done.
+    else if (usTopRow >= usBotRow)
+        return NO_ERROR;  // already done.
+    else if (cbLines == 0)
+        return NO_ERROR;  // already done.
+
+    const uint32 rowlen = (usRightCol - usLeftCol) + 1;
+    const uint32 overlines = (cbLines > usTopRow) ? (cbLines - usTopRow) : 0;
+    const uint32 collen = ((usBotRow - usTopRow) + 1) - overlines;
+    const uint32 adjust = overlines * vio_scrw;
+
+    uint16 *src = vio_buffer + ((usTopRow * vio_scrw) + usLeftCol) + adjust;
+    uint16 *dst = (src - (cbLines * vio_scrw)) + adjust;
+    const size_t rowcpylen = rowlen * sizeof (uint16);
+    for (uint32 i = 0; i < collen; i++) {
+        memcpy(dst, src, rowcpylen);
+        src += vio_scrw;
+        dst += vio_scrw;
+    } // for
+
+    const uint16 clear_cell = *((uint16 *) pCell);
+    for (uint32 i = 0; i < cbLines; i++) {
+        if (dst >= (vio_buffer + (vio_scrw * vio_scrh)))
+            break;  // !!! FIXME: just calculate this outside the loop and adjust cbLines.
+        uint16 *origdst = dst;
+        for (uint32 j = 0; j < rowlen; j++) {
+            *(dst++) = clear_cell;
+        }
+        dst = origdst + vio_scrw;
+    } // for
+
+    int starty = usTopRow - cbLines;
+    if (starty < 0)
+        starty = 0;
+    int endy = starty + collen + cbLines;
+    if (endy >= vio_scrh)
+        endy = vio_scrh;
+
+    for (int y = starty; y < endy; y++)
+        pushToNcurses(y, usLeftCol, rowlen, 0);
+    commitToNcurses();
+
+    return NO_ERROR;
+} // VioScrollUp
+
+static APIRET16 bridge16to32_VioScrollUp(uint8 *args)
+{
+    LX_NATIVE_MODULE_16BIT_BRIDGE_ARG(HVIO, hvio);
+    LX_NATIVE_MODULE_16BIT_BRIDGE_PTRARG(PBYTE, pCell);
+    LX_NATIVE_MODULE_16BIT_BRIDGE_ARG(USHORT, cbLines);
+    LX_NATIVE_MODULE_16BIT_BRIDGE_ARG(USHORT, usRightCol);
+    LX_NATIVE_MODULE_16BIT_BRIDGE_ARG(USHORT, usBotRow);
+    LX_NATIVE_MODULE_16BIT_BRIDGE_ARG(USHORT, usLeftCol);
+    LX_NATIVE_MODULE_16BIT_BRIDGE_ARG(USHORT, usTopRow);
+    return VioScrollUp(usTopRow, usLeftCol, usBotRow, usRightCol, cbLines, pCell, hvio);
+} // bridge16to32_VioGetCurType
+
+APIRET16 VioSetCurPos(USHORT usRow, USHORT usColumn, HVIO hvio)
+{
+    TRACE_NATIVE("VioSetCurPos(%u, %u, %u)", (uint) usRow, (uint) usColumn, (uint) hvio);
+    if (hvio != 0)
+        return ERROR_VIO_INVALID_HANDLE;  // !!! FIXME: can be non-zero when VioCreatePS() is implemented.
+    else if (!initNcurses())
+        return ERROR_VIO_INVALID_HANDLE;
+    else if (usRow >= vio_scrh)
+        return ERROR_VIO_ROW;
+    else if (usColumn >= vio_scrw)
+        return ERROR_VIO_COL;
+
+    vio_cury = usRow;
+    vio_curx = usColumn;
+
+    move(vio_cury, vio_curx);
+    refresh();
+
+    return NO_ERROR;
+} // VioSetCurPos
+
+static APIRET16 bridge16to32_VioSetCurPos(uint8 *args)
+{
+    LX_NATIVE_MODULE_16BIT_BRIDGE_ARG(HVIO, hvio);
+    LX_NATIVE_MODULE_16BIT_BRIDGE_ARG(USHORT, usColumn);
+    LX_NATIVE_MODULE_16BIT_BRIDGE_ARG(USHORT, usRow);
+    return VioSetCurPos(usRow, usColumn, hvio);
+} // bridge16to32_VioSetCurPos
+
+APIRET16 OS2API16 VioSetCurType(PVIOCURSORINFO pvioCursorInfo, HVIO hvio)
+{
+    TRACE_NATIVE("VioSetCurType(%p, %u)", pvioCursorInfo, (uint) hvio);
+    FIXME("write me");
+    return NO_ERROR;
+} // VioSetCurType
+
+static APIRET16 bridge16to32_VioSetCurType(uint8 *args)
+{
+    LX_NATIVE_MODULE_16BIT_BRIDGE_ARG(HVIO, hvio);
+    LX_NATIVE_MODULE_16BIT_BRIDGE_PTRARG(PVIOCURSORINFO, pvioCursorInfo);
+    return VioSetCurType(pvioCursorInfo, hvio);
+} // bridge16to32_VioSetCurType
+
+APIRET16 VioReadCellStr(PCH pchCellStr, PUSHORT pcb, USHORT usRow, USHORT usColumn, HVIO hvio)
+{
+    TRACE_NATIVE("VioReadCellStr(%p, %p, %u, %u, %u)", pchCellStr, pcb, (uint) usRow, (uint) usColumn, (uint) hvio);
+
+    if (hvio != 0)
+        return ERROR_VIO_INVALID_HANDLE;  // !!! FIXME: can be non-zero when VioCreatePS() is implemented.
+    else if (!initNcurses())
+        return ERROR_VIO_INVALID_HANDLE;
+    else if (usRow >= vio_scrh)
+        return ERROR_VIO_ROW;
+    else if (usColumn >= vio_scrw)
+        return ERROR_VIO_COL;
+
+    const uint32 maxidx = ((uint32)vio_scrh) * ((uint32)vio_scrw);
+    const uint32 idx = (((uint32)usRow) * ((uint32)vio_scrw)) + ((uint32)usColumn);
+    const uint32 avail = (maxidx - idx) * sizeof (uint16);
+    if (((uint32) *pcb) > avail)
+        *pcb = (USHORT) avail;
+    memcpy(pchCellStr, vio_buffer + idx, (size_t) *pcb);
+    return NO_ERROR;
+} // VioReadCellStr
+
+static APIRET16 bridge16to32_VioReadCellStr(uint8 *args)
+{
+    LX_NATIVE_MODULE_16BIT_BRIDGE_ARG(HVIO, hvio);
+    LX_NATIVE_MODULE_16BIT_BRIDGE_ARG(USHORT, usColumn);
+    LX_NATIVE_MODULE_16BIT_BRIDGE_ARG(USHORT, usRow);
+    LX_NATIVE_MODULE_16BIT_BRIDGE_PTRARG(PUSHORT, pcb);
+    LX_NATIVE_MODULE_16BIT_BRIDGE_PTRARG(PCH, pchCellStr);
+    return VioReadCellStr(pchCellStr, pcb, usRow, usColumn, hvio);
+} // bridge16to32_VioReadCellStr
+
+APIRET16 VioWrtCellStr(PCH pchCellStr, USHORT cb, USHORT usRow, USHORT usColumn, HVIO hvio)
+{
+    TRACE_NATIVE("VioWrtCellStr(%p, %u, %u, %u, %u)", pchCellStr, (uint) cb, (uint) usRow, (uint) usColumn, (uint) hvio);
+
+    if (hvio != 0)
+        return ERROR_VIO_INVALID_HANDLE;  // !!! FIXME: can be non-zero when VioCreatePS() is implemented.
+    else if (!initNcurses())
+        return ERROR_VIO_INVALID_HANDLE;
+    else if (usRow >= vio_scrh)
+        return ERROR_VIO_ROW;
+    else if (usColumn >= vio_scrw)
+        return ERROR_VIO_COL;
+
+    const uint16 *src = (uint16 *) pchCellStr;
+    uint16 *dst = vio_buffer + ((usRow * vio_scrw) + usColumn);
+    const uint32 avail = (((vio_scrh - usRow) * vio_scrw) - usColumn) * sizeof (uint16);
+    if (((uint32) cb) > avail)
+        cb = (USHORT) avail;
+
+    memcpy(dst, src, cb);  // !!! FIXME: what happens if cb extends into half a cell?
+
+    pushToNcurses(usRow, usColumn, cb / sizeof (uint16), 1);
+
+    return NO_ERROR;
+} // VioWrtCellStr
+
+static APIRET16 bridge16to32_VioWrtCellStr(uint8 *args)
+{
+    LX_NATIVE_MODULE_16BIT_BRIDGE_ARG(HVIO, hvio);
+    LX_NATIVE_MODULE_16BIT_BRIDGE_ARG(USHORT, usColumn);
+    LX_NATIVE_MODULE_16BIT_BRIDGE_ARG(USHORT, usRow);
+    LX_NATIVE_MODULE_16BIT_BRIDGE_ARG(USHORT, cb);
+    LX_NATIVE_MODULE_16BIT_BRIDGE_PTRARG(PCH, pchCellStr);
+    return VioWrtCellStr(pchCellStr, cb, usRow, usColumn, hvio);
+} // bridge16to32_VioWrtCellStr
+
+APIRET16 VioWrtCharStrAtt(PCH pch, USHORT cb, USHORT usRow, USHORT usColumn, PBYTE pAttr, HVIO hvio)
+{
+    TRACE_NATIVE("VioWrtCharStrAtt(%p, %u, %u, %u, %p, %u)", pch, (uint) cb, (uint) usRow, (uint) usColumn, pAttr, (uint) hvio);
+
+    if (hvio != 0)
+        return ERROR_VIO_INVALID_HANDLE;  // !!! FIXME: can be non-zero when VioCreatePS() is implemented.
+    else if (!initNcurses())
+        return ERROR_VIO_INVALID_HANDLE;
+    else if (usRow >= vio_scrh)
+        return ERROR_VIO_ROW;
+    else if (usColumn >= vio_scrw)
+        return ERROR_VIO_COL;
+
+    const uint8 attr = *pAttr;
+    const uint8 *src = (uint8 *) pch;
+    uint8 *dst = (uint8 *) (vio_buffer + ((usRow * vio_scrw) + usColumn));
+    const uint32 avail = (((vio_scrh - usRow) * vio_scrw) - usColumn);
+    if (((uint32) cb) > avail)
+        cb = (USHORT) avail;
+
+    for (uint32 i = 0; i < cb; i++, src++) {
+        *(dst++) = *src;
+        *(dst++) = attr;
+    } // for
+
+    pushToNcurses(usRow, usColumn, cb, 1);
+
+    return NO_ERROR;
+} // VioWrtCharStrAtt
+
+static APIRET16 bridge16to32_VioWrtCharStrAtt(uint8 *args)
+{
+    LX_NATIVE_MODULE_16BIT_BRIDGE_ARG(HVIO, hvio);
+    LX_NATIVE_MODULE_16BIT_BRIDGE_PTRARG(PBYTE, pAttr);
+    LX_NATIVE_MODULE_16BIT_BRIDGE_ARG(USHORT, usColumn);
+    LX_NATIVE_MODULE_16BIT_BRIDGE_ARG(USHORT, usRow);
+    LX_NATIVE_MODULE_16BIT_BRIDGE_ARG(USHORT, cb);
+    LX_NATIVE_MODULE_16BIT_BRIDGE_PTRARG(PCH, pch);
+    return VioWrtCharStrAtt(pch, cb, usRow, usColumn, pAttr, hvio);
+} // bridge16to32_VioWrtCharStrAtt
+
+
+APIRET16 VioWrtNCell(PBYTE pCell, USHORT cb, USHORT usRow, USHORT usColumn, HVIO hvio)
+{
+    TRACE_NATIVE("VioWrtNCell(%p, %u, %u, %u, %u)", pCell, (uint) cb, (uint) usRow, (uint) usColumn, (uint) hvio);
+
+    if (hvio != 0)
+        return ERROR_VIO_INVALID_HANDLE;  // !!! FIXME: can be non-zero when VioCreatePS() is implemented.
+    else if (!initNcurses())
+        return ERROR_VIO_INVALID_HANDLE;
+    else if (usRow >= vio_scrh)
+        return ERROR_VIO_ROW;
+    else if (usColumn >= vio_scrw)
+        return ERROR_VIO_COL;
+
+    const uint16 cell = *((uint16 *) pCell);
+    uint16 *dst = vio_buffer + ((usRow * vio_scrw) + usColumn);
+    const uint32 avail = (((vio_scrh - usRow) * vio_scrw) - usColumn);
+    if (((uint32) cb) > avail)
+        cb = (USHORT) avail;
+
+    for (uint32 i = 0; i < cb; i++)
+        *(dst++) = cell;
+
+    pushToNcurses(usRow, usColumn, cb, 1);
+
+    return NO_ERROR;
+} // VioWrtNCell
+
+static APIRET16 bridge16to32_VioWrtNCell(uint8 *args)
+{
+    LX_NATIVE_MODULE_16BIT_BRIDGE_ARG(HVIO, hvio);
+    LX_NATIVE_MODULE_16BIT_BRIDGE_ARG(USHORT, usColumn);
+    LX_NATIVE_MODULE_16BIT_BRIDGE_ARG(USHORT, usRow);
+    LX_NATIVE_MODULE_16BIT_BRIDGE_ARG(USHORT, cb);
+    LX_NATIVE_MODULE_16BIT_BRIDGE_PTRARG(PBYTE, pCell);
+    return VioWrtNCell(pCell, cb, usRow, usColumn, hvio);
+} // bridge16to32_VioWrtNCell
 
 
 LX_NATIVE_MODULE_16BIT_SUPPORT()
+    LX_NATIVE_MODULE_16BIT_API(VioScrollUp)
     LX_NATIVE_MODULE_16BIT_API(VioGetCurPos)
+    LX_NATIVE_MODULE_16BIT_API(VioWrtCellStr)
+    LX_NATIVE_MODULE_16BIT_API(VioSetCurPos)
     LX_NATIVE_MODULE_16BIT_API(VioGetMode)
+    LX_NATIVE_MODULE_16BIT_API(VioReadCellStr)
+    LX_NATIVE_MODULE_16BIT_API(VioGetCurType)
+    LX_NATIVE_MODULE_16BIT_API(VioGetBuf)
+    LX_NATIVE_MODULE_16BIT_API(VioSetCurType)
+    LX_NATIVE_MODULE_16BIT_API(VioWrtCharStrAtt)
+    LX_NATIVE_MODULE_16BIT_API(VioWrtNCell)
 LX_NATIVE_MODULE_16BIT_SUPPORT_END()
 
-LX_NATIVE_MODULE_DEINIT({
-    LX_NATIVE_MODULE_DEINIT_16BIT_SUPPORT();
-})
-
 static int initViocalls(void)
 {
     LX_NATIVE_MODULE_INIT_16BIT_SUPPORT()
+        LX_NATIVE_INIT_16BIT_BRIDGE(VioScrollUp, 26)
         LX_NATIVE_INIT_16BIT_BRIDGE(VioGetCurPos, 6)
+        LX_NATIVE_INIT_16BIT_BRIDGE(VioWrtCellStr, 12)
+        LX_NATIVE_INIT_16BIT_BRIDGE(VioSetCurPos, 6)
         LX_NATIVE_INIT_16BIT_BRIDGE(VioGetMode, 6)
+        LX_NATIVE_INIT_16BIT_BRIDGE(VioReadCellStr, 12)
+        LX_NATIVE_INIT_16BIT_BRIDGE(VioGetCurType, 6)
+        LX_NATIVE_INIT_16BIT_BRIDGE(VioGetBuf, 10)
+        LX_NATIVE_INIT_16BIT_BRIDGE(VioSetCurType, 6)
+        LX_NATIVE_INIT_16BIT_BRIDGE(VioWrtCharStrAtt, 16)
+        LX_NATIVE_INIT_16BIT_BRIDGE(VioWrtNCell, 12)
     LX_NATIVE_MODULE_INIT_16BIT_SUPPORT_END()
     return 1;
 } // initViocalls
 
 LX_NATIVE_MODULE_INIT({ if (!initViocalls()) return NULL; })
+    LX_NATIVE_EXPORT16(VioScrollUp, 7),
     LX_NATIVE_EXPORT16(VioGetCurPos, 9),
-    LX_NATIVE_EXPORT16(VioGetMode, 21)
+    LX_NATIVE_EXPORT16(VioWrtCellStr, 10),
+    LX_NATIVE_EXPORT16(VioSetCurPos, 15),
+    LX_NATIVE_EXPORT16(VioGetMode, 21),
+    LX_NATIVE_EXPORT16(VioReadCellStr, 24),
+    LX_NATIVE_EXPORT16(VioGetCurType, 27),
+    LX_NATIVE_EXPORT16(VioGetBuf, 31),
+    LX_NATIVE_EXPORT16(VioSetCurType, 32),
+    LX_NATIVE_EXPORT16(VioWrtCharStrAtt, 48),
+    LX_NATIVE_EXPORT16(VioWrtNCell, 52)
 LX_NATIVE_MODULE_INIT_END()
 
+LX_NATIVE_MODULE_DEINIT({
+    deinitNcurses();
+    LX_NATIVE_MODULE_DEINIT_16BIT_SUPPORT();
+})
+
 // end of viocalls.c ...
 
--- a/native/viocalls.h	Fri Oct 28 02:32:05 2016 -0400
+++ b/native/viocalls.h	Sat Oct 29 02:05:01 2016 -0400
@@ -34,8 +34,25 @@
     VGMT_DISABLEBURST = 0x04
 };
 
+typedef struct
+{
+    USHORT yStart;
+    USHORT cEnd;
+    USHORT cx;
+    USHORT attr;
+} VIOCURSORINFO, *PVIOCURSORINFO;
+
 APIRET16 OS2API16 VioGetMode(PVIOMODEINFO pvioModeInfo, HVIO hvio);
 APIRET16 OS2API16 VioGetCurPos(PUSHORT pusRow, PUSHORT pusColumn, HVIO hvio);
+APIRET16 OS2API16 VioGetBuf(PULONG pLVB, PUSHORT pcbLVB, HVIO hvio);
+APIRET16 OS2API16 VioGetCurType(PVIOCURSORINFO pvioCursorInfo, HVIO hvio);
+APIRET16 OS2API16 VioScrollUp(USHORT usTopRow, USHORT usLeftCol, USHORT usBotRow, USHORT usRightCol, USHORT cbLines, PBYTE pCell, HVIO hvio);
+APIRET16 OS2API16 VioSetCurPos(USHORT usRow, USHORT usColumn, HVIO hvio);
+APIRET16 OS2API16 VioSetCurType(PVIOCURSORINFO pvioCursorInfo, HVIO hvio);
+APIRET16 OS2API16 VioReadCellStr(PCH pchCellStr, PUSHORT pcb, USHORT usRow, USHORT usColumn, HVIO hvio);
+APIRET16 OS2API16 VioWrtCellStr(PCH pchCellStr, USHORT cb, USHORT usRow, USHORT usColumn, HVIO hvio);
+APIRET16 OS2API16 VioWrtCharStrAtt(PCH pch, USHORT cb, USHORT usRow, USHORT usColumn, PBYTE pAttr, HVIO hvio);
+APIRET16 OS2API16 VioWrtNCell(PBYTE pCell, USHORT cb, USHORT usRow, USHORT usColumn, HVIO hvio);
 
 #ifdef __cplusplus
 }