Category : C Source Code
Archive   : RXGRAPH.ZIP
Filename : RXGRAPH.C

Output of file : RXGRAPH.C contained in archive : RXGRAPH.ZIP
#if 0

Written by: Bill Mayne
9707 Lawndale Dr.
Silver Spring, MD 20901
Date written: Sept. 16, 1987


Install REXX function package, then terminate and stay resident.
RC=0 if successfully loaded
1 if error loading function package;
2 if REXX interrupt manager not resident.
(No error messages generated to save space.)

Note: RXGRAPH should not be loaded from a REXX program, since you want
it to be the first thing loaded in memory after any other TSR
routines. Load it manually or from a .BAT file. LOADGRAF.BAT shows
how to check the return code and generate an appropriate message.

Include Files:

REXXCALL.H - maps REXX control block
BITMAP.H - defines bit mapped graphics functions
RXDRAW.H - defines some front end routines to graphics
(basically to add error checking)
SELECT.H - defines SELECT control structure

This program was tested with:
*Personal REXX 1.5 and 1.60 (beta test copy),
DOS 2.11, 3.1, and 3.2,
MSC 4.0.

* Include (.H and .OBJ) files from Personal REXX 1.60 are required to
recreate the executable (.EXE) file, but once created it will work
with version 1.5.

/* Standard headers from Microsoft */

/* Define structure for REXX interface */
#include "rexxcall.h"

/* include function definitions for graphics calls */
#include "bitmap.h"
#include "rxdraw.h"

/* control structure for SELECT...WHEN... */
#include "select.h"

/* Macros for handling segment and offset components of pointers */
/* Note HIGH_WORD could be replaced by Microsoft's FP_SEG and */
/* LOW_WORD by FP_OFF. The versions here are used because they */
/* were in Mansfield's FUNCPACK.C, on which this is based. */
#define MAKE_LONG(hi, low) \
((long) ((((unsigned long) (unsigned) (hi)) << 16) + \
(unsigned long) (unsigned) (low)))
#define HIGH_WORD(l) ((unsigned) (((unsigned long) (l)) >> 16))
#define LOW_WORD(l) ((unsigned) (((unsigned long) (l)) & 0xffff))

/* Define macros to check and apply for functions of various types */
#define STR_MATCH(str1,str2) (0==strcmpi(str1,str2))
#define WHEN_FUNC(func) WHEN STR_MATCH(fcn,"func") THEN

/* Small model should be used if possible, since this is TSR. */
#ifdef M_I86SM /* small model */
#define FINDINT findints
#ifdef M_I86MM /* medium model */
#define FINDINT findintm
#ifdef M_I86LM /* large model */
#define FINDINT findintl

extern int hndutil(), rexxfcn();
unsigned showsp();

static int result;
static char return_buffer[21];
static int nargs;
static unsigned data_segment;
static char **arg;
static int *length;

union REGS inregs, outregs;
struct SREGS segregs;
setargv() { } /* Dummy function to save space */
setenvp() { } /* Dummy function to save space */

struct env_block eb, *ebp = &eb;

int rexx_interrupt,interrupt_status;

rexx_interrupt = FINDINT(&interrupt_status);
if (interrupt_status != 0) exit(2);

/* setup environment block telling rexx who and where we are */
memset(&eb, 0, sizeof(struct env_block));
memcpy(eb.env_name, "RXGRAPH ", 8);
eb.env_entry_addr = (FARP) rexxfcn; /* assembler gate for calls from rexx */
#ifdef M_I86SM /* predefined by microsoft c if small model */
/* 0 for segment address of hndutil will indicate to rexxfcn */
/* to use near call to hndutil */
eb.env_exec_addr = (FARP) MAKE_LONG(0, hndutil); /* function handler */
#else /* medium or large model */
eb.env_exec_addr = (FARP) hndutil; /* function handler */
/* setup segment register values to be put into effect by rexxfcn when */
/* our package is called */
eb.env_dseg = data_segment = segregs.ds; /* our data segment */
eb.env_sseg =; /* our stack segment */
eb.env_sp = showsp();

/* announce ourselves to rexx */
inregs.h.ah = REXX_FCN; = FCN_INSTALL;
#ifdef M_I86LM /* predefined by microsoft c if large model */
inregs.x.bx = FP_SEG(ebp);
inregs.x.dx = FP_OFF(ebp);
#else /* medium or small model */
inregs.x.bx = data_segment;
inregs.x.dx = (unsigned) &eb;
int86x(rexx_interrupt, &inregs, &outregs, &segregs);

if ( != 0) exit(1);

/* terminate and stay resident, awaiting calls from rexx to hndutil */

/* handle function requests */
hndutil (cb)
struct call_block far *cb;
char *fcn;
unsigned int n, dataseg;
struct arg_block far *argp;
int i, l;

/* note the number of arguments passed */
nargs = cb->arg_count;

/* create a string containing the name of the function involved */
fcn = malloc(cb->name_length + 1);
#ifdef M_I86LM /* large model */
strncpy(fcn, cb->name_addr, cb->name_length);
#else /* small and medium models */
movedata(HIGH_WORD(cb->name_addr), LOW_WORD(cb->name_addr),
data_segment, fcn, cb->name_length);
fcn[cb->name_length] = '\0';

/* for easier access to arguments, copy them from rexx's memory */
/* into our data segment. */
/* null ('\0') character is appended to arguments so they can */
/* be accessed with c string handling functions when appropriate */
/* it is not always appropriate, since rexx strings, unlike c */
/* strings, can contain embedded null characters. */
/* functions has_null and args_have_null are provided to test */
/* whether any of the arguments contain null characters */
arg = (char **) malloc(nargs*sizeof(char *));
length = (int *) malloc(nargs*sizeof(int));
argp = (struct arg_block far *) cb->arg_list_addr;
for (i=0; i {
if (argp->arg_addr != (FARP) NULL)
arg[i] = malloc((length[i] = argp->arg_length) + 1);
#ifdef M_I86LM /* large model */
strncpy(arg[i], argp->arg_addr, length[i]);
#else /* small and medium models */
movedata(HIGH_WORD(argp->arg_addr), LOW_WORD(argp->arg_addr),
data_segment, arg[i], length[i]);
l = length[i];
arg[i][l] = '\0';
/* allow for omitted arguments */
arg[i] = NULL;
length[i] = 0;

/* Call our routine to handle the function */
This series of macro calls generates a large nested if
which is one way of implementing a select or switch with
noninteger conditions. Add any functions you like here,
written in any language callable from MSC. You could also
save some space by eliminating functions you never use.
Because of the overhead necessary to support floating point
operations and data conversion, it is a good idea to combine
all MSC or MSC callable functions in one function package.
This source program supplies the necessary REXX-C interface.
result=0; /* Be optimistic and assume we can handle it. */
return_buffer[0]=0; /* null string as default return value */
WHEN_FUNC(get_mode) itoa(get_mode(),return_buffer,10);
WHEN_FUNC(set_mode) set_mode(atoi(arg[0]));
WHEN_FUNC(set_background) set_background(atoi(arg[0]));
WHEN_FUNC(set_pallette) set_pallette(atoi(arg[0]));
WHEN_FUNC(put_dot) rx_put_dot(atoi(arg[0]),atoi(arg[1]),atoi(arg[2]));
#ifndef BIOS
WHEN_FUNC(scr_fill) scr_fill(atoi(arg[0]));
result=1; /* function not implemented */

/* free storage allocated using malloc() */
for (i=0; i free(fcn);
free (length);
free (arg);

/* tell rexx address and length of the returned value */
cb->ret_addr = (FARP) MAKE_LONG(data_segment, (unsigned) &return_buffer[0]);
cb->ret_count = strlen(return_buffer);


#ifdef M_I86LM
/* returns the current stack pointer */
unsigned showsp()
int dummy, *dummyp = &dummy;

return (FP_OFF(dummyp));
/* returns the current stack pointer */
unsigned showsp ()
int dummy; /* dummy variable allocated on stack so we can get its address */

return ((unsigned) &dummy);

/* returns 1 if a 'string' of length l contains any null characters */
int has_null (s, l)
return (memchr(s, '\0', l) != NULL);

/* returns 1 if any of the arguments contains a null character */
int args_have_null ()
int i;

for (i = 0; i < nargs; i++)
if (length[i] > 0 && has_null(arg[i], length[i]))
return (1);

return (0);

/* terminate and stay resident */
exit_tsr ()
unsigned far *memp;
extern unsigned _psp;

/* point at second word of our psp, which contains our ending paragraph */
memp = (unsigned far *) MAKE_LONG(_psp, 2); = 0x3100; /* dos terminate and stay resident function */
inregs.x.dx = *memp - _psp; /* paragraphs of memory allocated to us */

int86x(0x21, &inregs, &outregs);

/* we never return here */

/* fixup memory allocation block */
/* microsoft c's memory allocation routines occasionally call dos to */
/* allocate additional memory. in our case (a terminate-stay-resident pgm) */
/* this memory allocation will fail. unfortunately, the dos code to */
/* handle this call marks our memory as owned by the currently-executing */
/* program (usually rx), and when the rexx program finishes, this causes */
/* our memory to be freed */
/* so before we return from our function handler, we always re-mark our */
/* memory as owned by us, to prevent its being freed by dos */
/* we do this by putting our psp address into the dos memory header block */
/* located one paragraph before our psp */
unsigned far *memp;
extern unsigned _psp;

memp = (unsigned far *) MAKE_LONG(_psp - 1, 1);
*memp = _psp;

  3 Responses to “Category : C Source Code
Archive   : RXGRAPH.ZIP
Filename : RXGRAPH.C

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: