Beispiel #1
0
uint Glulxe::perform_glk(uint funcnum, uint numargs, uint *arglist) {
	uint retval = 0;

	switch (funcnum) {
	/* To speed life up, we implement commonly-used Glk functions
	   directly -- instead of bothering with the whole prototype
	   mess. */

	case 0x0047: /* stream_set_current */
		if (numargs != 1)
			goto WrongArgNum;
		glk_stream_set_current(find_stream_by_id(arglist[0]));
		break;
	case 0x0048: /* stream_get_current */
		if (numargs != 0)
			goto WrongArgNum;
		retval = find_id_for_stream(glk_stream_get_current());
		break;
	case 0x0080: /* put_char */
		if (numargs != 1)
			goto WrongArgNum;
		glk_put_char(arglist[0] & 0xFF);
		break;
	case 0x0081: /* put_char_stream */
		if (numargs != 2)
			goto WrongArgNum;
		glk_put_char_stream(find_stream_by_id(arglist[0]), arglist[1] & 0xFF);
		break;
	case 0x00C0: /* select */
		/* call a library hook on every glk_select() */
		if (library_select_hook)
			library_select_hook(arglist[0]);
		/* but then fall through to full dispatcher, because there's no real
		   need for speed here */
		goto FullDispatcher;
	case 0x00A0: /* char_to_lower */
		if (numargs != 1)
			goto WrongArgNum;
		retval = glk_char_to_lower(arglist[0] & 0xFF);
		break;
	case 0x00A1: /* char_to_upper */
		if (numargs != 1)
			goto WrongArgNum;
		retval = glk_char_to_upper(arglist[0] & 0xFF);
		break;
	case 0x0128: /* put_char_uni */
		if (numargs != 1)
			goto WrongArgNum;
		glk_put_char_uni(arglist[0]);
		break;
	case 0x012B: /* put_char_stream_uni */
		if (numargs != 2)
			goto WrongArgNum;
		glk_put_char_stream_uni(find_stream_by_id(arglist[0]), arglist[1]);
		break;

WrongArgNum:
		error("Wrong number of arguments to Glk function.");
		break;

FullDispatcher:
	default: {
		/* Go through the full dispatcher prototype foo. */
		const char *proto, *cx;
		dispatch_splot_t splot;
		int argnum, argnum2;

		/* Grab the string. */
		proto = gidispatch_prototype(funcnum);
		if (!proto)
			error("Unknown Glk function.");

		splot.varglist = arglist;
		splot.numvargs = numargs;
		splot.retval = &retval;

		/* The work goes in four phases. First, we figure out how many
		   arguments we want, and allocate space for the Glk argument
		   list. Then we go through the Glulxe arguments and load them
		   into the Glk list. Then we call. Then we go through the
		   arguments again, unloading the data back into Glulx memory. */

		/* Phase 0. */
		prepare_glk_args(proto, &splot);

		/* Phase 1. */
		argnum = 0;
		cx = proto;
		parse_glk_args(&splot, &cx, 0, &argnum, 0, 0);

		/* Phase 2. */
		gidispatch_call(funcnum, argnum, splot.garglist);

		/* Phase 3. */
		argnum2 = 0;
		cx = proto;
		unparse_glk_args(&splot, &cx, 0, &argnum2, 0, 0);
		if (argnum != argnum2)
			error("Argument counts did not match.");

		break;
	}
	}

	return retval;
}
Beispiel #2
0
void Glulxe::parse_glk_args(dispatch_splot_t *splot, const char **proto, int depth, int *argnumptr,
                            uint subaddress, int subpassin) {
	const char *cx;
	int ix, argx;
	int gargnum, numwanted;
	void *opref;
	gluniversal_t *garglist;
	uint *varglist;

	garglist = splot->garglist;
	varglist = splot->varglist;
	gargnum = *argnumptr;
	cx = *proto;

	numwanted = 0;
	while (*cx >= '0' && *cx <= '9') {
		numwanted = 10 * numwanted + (*cx - '0');
		cx++;
	}

	for (argx = 0, ix = 0; argx < numwanted; argx++, ix++) {
		char typeclass;
		int skipval;
		int isref, passin, passout, nullok, isarray, isretained, isreturn;
		cx = read_prefix(cx, &isref, &isarray, &passin, &passout, &nullok,
		                 &isretained, &isreturn);

		typeclass = *cx;
		cx++;

		skipval = false;
		if (isref) {
			if (!isreturn && varglist[ix] == 0) {
				if (!nullok)
					error("Zero passed invalidly to Glk function.");
				garglist[gargnum]._ptrflag = false;
				gargnum++;
				skipval = true;
			} else {
				garglist[gargnum]._ptrflag = true;
				gargnum++;
			}
		}
		if (!skipval) {
			uint thisval;

			if (typeclass == '[') {

				parse_glk_args(splot, &cx, depth + 1, &gargnum, varglist[ix], passin);

			} else if (isarray) {
				/* definitely isref */

				switch (typeclass) {
				case 'C':
					/* This test checks for a giant array length, which is
					   deprecated. It displays a warning and cuts it down to
					   something reasonable. Future releases of this interpreter
					   may remove this test and go on to verify_array_addresses(),
					   which treats this case as a fatal error. */
					if (varglist[ix + 1] > endmem
					        || varglist[ix] + varglist[ix + 1] > endmem) {
						nonfatal_warning_i("Memory access was much too long -- perhaps a print_to_array call with only one argument", varglist[ix + 1]);
						varglist[ix + 1] = endmem - varglist[ix];
					}
					verify_array_addresses(varglist[ix], varglist[ix + 1], 1);
					garglist[gargnum]._array = CaptureCArray(varglist[ix], varglist[ix + 1], passin);
					gargnum++;
					ix++;
					garglist[gargnum]._uint = varglist[ix];
					gargnum++;
					cx++;
					break;
				case 'I':
					/* See comment above. */
					if (varglist[ix + 1] > endmem / 4
					        || varglist[ix + 1] > (endmem - varglist[ix]) / 4) {
						nonfatal_warning_i("Memory access was much too long -- perhaps a print_to_array call with only one argument", varglist[ix + 1]);
						varglist[ix + 1] = (endmem - varglist[ix]) / 4;
					}
					verify_array_addresses(varglist[ix], varglist[ix + 1], 4);
					garglist[gargnum]._array = CaptureIArray(varglist[ix], varglist[ix + 1], passin);
					gargnum++;
					ix++;
					garglist[gargnum]._uint = varglist[ix];
					gargnum++;
					cx++;
					break;
				case 'Q':
					/* This case was added after the giant arrays were deprecated,
					   so we don't bother to allow for that case. We just verify
					   the length. */
					verify_array_addresses(varglist[ix], varglist[ix + 1], 4);
					garglist[gargnum]._array = CapturePtrArray(varglist[ix], varglist[ix + 1], (*cx - 'a'), passin);
					gargnum++;
					ix++;
					garglist[gargnum]._uint = varglist[ix];
					gargnum++;
					cx++;
					break;
				default:
					error("Illegal format string.");
					break;
				}
			} else {
				/* a plain value or a reference to one. */

				if (isreturn) {
					thisval = 0;
				} else if (depth > 0) {
					/* Definitely not isref or isarray. */
					if (subpassin)
						thisval = ReadStructField(subaddress, ix);
					else
						thisval = 0;
				} else if (isref) {
					if (passin)
						thisval = ReadMemory(varglist[ix]);
					else
						thisval = 0;
				} else {
					thisval = varglist[ix];
				}

				switch (typeclass) {
				case 'I':
					if (*cx == 'u')
						garglist[gargnum]._uint = (uint)(thisval);
					else if (*cx == 's')
						garglist[gargnum]._sint = (int)(thisval);
					else
						error("Illegal format string.");
					gargnum++;
					cx++;
					break;
				case 'Q':
					if (thisval) {
						opref = classes_get(*cx - 'a', thisval);
						if (!opref) {
							error("Reference to nonexistent Glk object.");
						}
					} else {
						opref = nullptr;
					}
					garglist[gargnum]._opaqueref = opref;
					gargnum++;
					cx++;
					break;
				case 'C':
					if (*cx == 'u')
						garglist[gargnum]._uch = (unsigned char)(thisval);
					else if (*cx == 's')
						garglist[gargnum]._sch = (signed char)(thisval);
					else if (*cx == 'n')
						garglist[gargnum]._ch = (char)(thisval);
					else
						error("Illegal format string.");
					gargnum++;
					cx++;
					break;
				case 'S':
					garglist[gargnum]._charstr = DecodeVMString(thisval);
					gargnum++;
					break;
#ifdef GLK_MODULE_UNICODE
				case 'U':
					garglist[gargnum]._unicharstr = DecodeVMUstring(thisval);
					gargnum++;
					break;
#endif /* GLK_MODULE_UNICODE */
				default:
					error("Illegal format string.");
					break;
				}
			}
		} else {
			/* We got a null reference, so we have to skip the format element. */
			if (typeclass == '[') {
				int numsubwanted, refdepth;
				numsubwanted = 0;
				while (*cx >= '0' && *cx <= '9') {
					numsubwanted = 10 * numsubwanted + (*cx - '0');
					cx++;
				}
				refdepth = 1;
				while (refdepth > 0) {
					if (*cx == '[')
						refdepth++;
					else if (*cx == ']')
						refdepth--;
					cx++;
				}
			} else if (typeclass == 'S' || typeclass == 'U') {
				/* leave it */
			} else {
				cx++;
				if (isarray)
					ix++;
			}
		}
	}

	if (depth > 0) {
		if (*cx != ']')
			error("Illegal format string.");
		cx++;
	} else {
		if (*cx != ':' && *cx != '\0')
			error("Illegal format string.");
	}

	*proto = cx;
	*argnumptr = gargnum;
}
Beispiel #3
0
/* parse_glk_args():
   This long and unpleasant function translates a set of Floo objects into
   a gluniversal_t array. It's recursive, too, to deal with structures.
*/
static void parse_glk_args(dispatch_splot_t *splot, char **proto, int depth,
  int *argnumptr, glui32 subaddress, int subpassin)
{
  char *cx;
  int ix, argx;
  int gargnum, numwanted;
  void *opref;
  gluniversal_t *garglist;
  glui32 *varglist;
  
  garglist = splot->garglist;
  varglist = splot->varglist;
  gargnum = *argnumptr;
  cx = *proto;

  numwanted = 0;
  while (*cx >= '0' && *cx <= '9') {
    numwanted = 10 * numwanted + (*cx - '0');
    cx++;
  }

  for (argx = 0, ix = 0; argx < numwanted; argx++, ix++) {
    char typeclass;
    int skipval;
    int isref, passin, passout, nullok, isarray, isretained, isreturn;
    cx = read_prefix(cx, &isref, &isarray, &passin, &passout, &nullok,
      &isretained, &isreturn);
    
    typeclass = *cx;
    cx++;

    skipval = FALSE;
    if (isref) {
      if (!isreturn && varglist[ix] == 0) {
        if (!nullok)
          fatalError("Zero passed invalidly to Glk function.");
        garglist[gargnum].ptrflag = FALSE;
        gargnum++;
        skipval = TRUE;
      }
      else {
        garglist[gargnum].ptrflag = TRUE;
        gargnum++;
      }
    }
    if (!skipval) {
      glui32 thisval;

      if (typeclass == '[') {

        parse_glk_args(splot, &cx, depth+1, &gargnum, varglist[ix], passin);

      }
      else if (isarray) {
        /* definitely isref */

        switch (typeclass) {
        case 'C':
          /* This test checks for a giant array length, and cuts it down to
             something reasonable. Future releases of this interpreter may
             treat this case as a fatal error. */
          if (varglist[ix+1] > gEndMem || varglist[ix]+varglist[ix+1] > gEndMem)
            varglist[ix+1] = gEndMem - varglist[ix];

          garglist[gargnum].array = (void*) CaptureCArray(varglist[ix], varglist[ix+1], passin);
          gargnum++;
          ix++;
          garglist[gargnum].uint = varglist[ix];
          gargnum++;
          cx++;
          break;
        case 'I':
          /* See comment above. */
          if (varglist[ix+1] > gEndMem/4 || varglist[ix+1] > (gEndMem-varglist[ix])/4)
            varglist[ix+1] = (gEndMem - varglist[ix]) / 4;

          garglist[gargnum].array = CaptureIArray(varglist[ix], varglist[ix+1], passin);
          gargnum++;
          ix++;
          garglist[gargnum].uint = varglist[ix];
          gargnum++;
          cx++;
          break;
        case 'Q':
          garglist[gargnum].array = CapturePtrArray(varglist[ix], varglist[ix+1], (*cx-'a'), passin);
          gargnum++;
          ix++;
          garglist[gargnum].uint = varglist[ix];
          gargnum++;
          cx++;
          break;
        default:
          fatalError("Illegal format string.");
          break;
        }
      }
      else {
        /* a plain value or a reference to one. */

        if (isreturn) {
          thisval = 0;
        }
        else if (depth > 0) {
          /* Definitely not isref or isarray. */
          if (subpassin)
            thisval = ReadStructField(subaddress, ix);
          else
            thisval = 0;
        }
        else if (isref) {
          if (passin)
            thisval = ReadMemory(varglist[ix]);
          else
            thisval = 0;
        }
        else {
          thisval = varglist[ix];
        }

        switch (typeclass) {
        case 'I':
          if (*cx == 'u')
            garglist[gargnum].uint = (glui32)(thisval);
          else if (*cx == 's')
            garglist[gargnum].sint = (glsi32)(thisval);
          else
            fatalError("Illegal format string.");
          gargnum++;
          cx++;
          break;
        case 'Q':
          if (thisval) {
            opref = classes_get(*cx-'a', thisval);
            if (!opref) {
              fatalError("Reference to nonexistent Glk object.");
            }
          }
          else {
            opref = NULL;
          }
          garglist[gargnum].opaqueref = opref;
          gargnum++;
          cx++;
          break;
        case 'C':
          if (*cx == 'u') 
            garglist[gargnum].uch = (unsigned char)(thisval);
          else if (*cx == 's')
            garglist[gargnum].sch = (signed char)(thisval);
          else if (*cx == 'n')
            garglist[gargnum].ch = (char)(thisval);
          else
            fatalError("Illegal format string.");
          gargnum++;
          cx++;
          break;
        case 'S':
          garglist[gargnum].charstr = DecodeVMString(thisval);
          gargnum++;
          break;
#ifdef GLK_MODULE_UNICODE
        case 'U':
          garglist[gargnum].unicharstr = DecodeVMUstring(thisval);
          gargnum++;
          break;
#endif /* GLK_MODULE_UNICODE */
        default:
          fatalError("Illegal format string.");
          break;
        }
      }
    }
    else {
      /* We got a null reference, so we have to skip the format element. */
      if (typeclass == '[') {
        int numsubwanted, refdepth;
        numsubwanted = 0;
        while (*cx >= '0' && *cx <= '9') {
          numsubwanted = 10 * numsubwanted + (*cx - '0');
          cx++;
        }
        refdepth = 1;
        while (refdepth > 0) {
          if (*cx == '[')
            refdepth++;
          else if (*cx == ']')
            refdepth--;
          cx++;
        }
      }
      else if (typeclass == 'S' || typeclass == 'U') {
        /* leave it */
      }
      else {
        cx++;
        if (isarray)
          ix++;
      }
    }    
  }

  if (depth > 0) {
    if (*cx != ']')
      fatalError("Illegal format string.");
    cx++;
  }
  else {
    if (*cx != ':' && *cx != '\0')
      fatalError("Illegal format string.");
  }
  
  *proto = cx;
  *argnumptr = gargnum;
}