This is the open source Pawn interpreter ported to mbed. See here: http://www.compuphase.com/pawn/pawn.htm and here: http://code.google.com/p/pawnscript/

Dependents:   Pawn4Test

Some instructions:

  • Put the attached include folder next to your source, so when you compile you get all the proper definitions
  • Use the attached main.p as a starting point if you wish
  • Compile your main.p into main.amx - Put your main.amx on the mbed 'drive'
  • Reset and be amazed.

Important Compile Notes:

  • You should use the -S# option to define a smaller default stack size. Start with -S64 and go up from there if needed.
  • To use on the Cortex-M0 version of the mbed (LPC11U24), you MUST include the TARGET=3 command-line option as well, so the pin names are properly defined. In the future this may be handled on the native code side.

Known Issues:

  • At the moment it appears the kbhit() function is not working right - at least on my mac. Will continue testing on Windows. Working fine.

Todo:

  • Add more wrappers for the mbed peripherals
  • Add Pawn overlay support, to allow much larger scripts to run (even on the LPC11U24)
Revision:
0:3ab1d2d14eb3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/amxcons.c	Thu Nov 15 17:41:21 2012 +0000
@@ -0,0 +1,1412 @@
+/* Console output module (terminal I/O) for the Pawn AMX
+ *
+ *  Since some of these routines go further than those of standard C, they
+ *  cannot always be implemented with portable C functions. In other words,
+ *  these routines must be ported to other environments.
+ *
+ *  Copyright (c) ITB CompuPhase, 1997-2011
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ *  use this file except in compliance with the License. You may obtain a copy
+ *  of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ *  License for the specific language governing permissions and limitations
+ *  under the License.
+ *
+ *  Version: $Id: amxcons.c 4523 2011-06-21 15:03:47Z thiadmer $
+ */
+
+#if defined _UNICODE || defined __UNICODE__ || defined UNICODE
+# if !defined UNICODE   /* for Windows */
+#   define UNICODE
+# endif
+# if !defined _UNICODE  /* for C library */
+#   define _UNICODE
+# endif
+#endif
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined __MSDOS__
+  #define HAVE_CONIO
+  #include <conio.h>
+  #include <malloc.h>
+#endif
+#if defined USE_CURSES || defined HAVE_CURSES_H
+  #include <curses.h>
+  #if !defined CURSES
+    #define CURSES  1
+  #endif
+#endif
+#include "osdefs.h"
+#if defined __ECOS__
+  /* eCos puts include files in cyg/package_name */
+  #include <cyg/hal/hal_if.h>
+  #include <cyg/infra/diag.h>
+  #include <cyg/hal/hal_diag.h>
+  #include <cyg/pawn/amx.h>
+#else
+  #include "amx.h"
+#endif
+#if defined __WIN32__ || defined _WIN32 || defined WIN32
+  #include <windows.h>
+#endif
+
+#if defined _UNICODE
+# include <tchar.h>
+#elif !defined __T
+  typedef char          TCHAR;
+# define __T(string)    string
+# define _fgetts        fgets
+# define _puttchar      putchar
+# define _stprintf      sprintf
+# define _tcschr        strchr
+# define _tcscpy        strcpy
+# define _tcsdup        strdup
+# define _tcslen        strlen
+# define _tprintf       printf
+#endif
+#include "amxcons.h"
+
+#if defined AMX_TERMINAL
+  #define EOL_CHAR       '\r'
+#endif
+#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined __MSDOS__
+  #define EOL_CHAR       '\r'
+#endif
+#if !defined EOL_CHAR
+  /* if not a "known" operating system, assume Linux/Unix */
+  #define EOL_CHAR     '\n'
+#endif
+
+#if !defined AMX_STRING_LIB
+
+#if defined AMX_TERMINAL
+  /* required functions are implemented elsewhere */
+  int amx_putstr(const TCHAR *);
+  int amx_putchar(int);
+  int amx_fflush(void);
+  int amx_getch(void);
+  TCHAR *amx_gets(TCHAR *,int);
+  int amx_termctl(int,int);
+  void amx_clrscr(void);
+  void amx_clreol(void);
+  int amx_gotoxy(int x,int y);
+  void amx_wherexy(int *x,int *y);
+  unsigned int amx_setattr(int foregr,int backgr,int highlight);
+  void amx_console(int columns, int lines, int flags);
+  void amx_viewsize(int *width,int *height);
+  int amx_kbhit(void);
+#elif defined CURSES && CURSES != 0
+  /* Use the "curses" library to implement the console */
+  static WINDOW *curseswin;
+  #define amx_putstr(s)       printw("%s",(s))
+  #define amx_putchar(c)      addch(c)
+  #define amx_fflush()        refresh()
+  #define amx_getch()         getch()
+  #define amx_gets(s,n)       getnstr((s),(n))
+  #define amx_clrscr()        clear()
+  #define amx_clreol()        clrtoeol()
+  #define amx_gotoxy(x,y)     move((y)-1,(x)-1)
+  #define amx_console(c,l,f)  ((void)(c),(void)(l),(void)(f))
+  unsigned int amx_setattr(int foregr,int backgr,int highlight)
+  {
+    int attribs=A_NORMAL;
+    if (highlight>0)
+      attribs=(attribs & ~A_NORMAL) | A_STANDOUT;
+    attrset(attribs);
+    //??? in future, also handle colours
+  }
+  void CreateConsole(void);
+  int amx_kbhit(void)
+  {
+    int result;
+    CreateConsole();
+    nodelay(curseswin,TRUE);    /* enter non-blocking state */
+    result=getch();             /* read key (if any) */
+    nodelay(curseswin,FALSE);   /* leave non-blocking state */
+    if (result!=ERR)
+      ungetch(result);          /* a key is waiting, push it back */
+    return (result==ERR) ? 0 : 1;
+  }
+  int amx_termctl(int code,int value)
+  {
+    switch (code) {
+    case 0:           /* query terminal support */
+      return 1;
+    /* case 1: */     /* switch auto-wrap on/off (not supported in curses!) */
+    /* case 2: */     /* create/switch to another console */
+    case 3:           /* set emphasized font */
+      if (value)
+        attron(A_BOLD);
+      else
+        attroff(A_BOLD);
+      return 1;
+    /* case 4: */     /* query whether a terminal is "open" */
+    default:
+      return 0;
+    } /* switch */
+  }
+  void amx_wherexy(int *x,int *y)
+  {
+    int row,col;
+    getyx(curseswin,row,col);
+    if (x!=NULL)
+      *x=col+1;
+    if (y!=NULL)
+      *y=row+1;
+  }
+  void amx_viewsize(int *width,int *height)
+  {
+    int row,col;
+    getmaxyx(curseswin,row,col);
+    if (width!=NULL)
+      *width=col;
+    if (height!=NULL)
+      *height=row;
+  }
+#elif defined VT100 || defined __LINUX__ || defined ANSITERM || defined __ECOS__
+  /* ANSI/VT100 terminal, or shell emulating "xterm" */
+  #if defined __ECOS__
+    #define AMXCONSOLE_NOIDLE
+  #endif
+
+  #if CYGPKG_PAWN_AMXCONSOLE_DIAG==1
+    /* eCos has basically two ways to make simple exchanges with a terminal:
+     * - with the diag_*() functions (no input provided!)
+     * - with f*() functions (fprintf(),fputs(), etc).
+     */
+    #define amx_fflush()
+
+    static int amx_putstr(TCHAR *s)
+    {
+      diag_write_string(s);
+      return 1;
+    }
+    static int amx_putchar(TCHAR c)
+    {
+      diag_write_char(c);
+      return c;
+    }
+    static char amx_getch(void)
+    {
+      char c=-1;
+      HAL_DIAG_READ_CHAR(c);
+      return c;
+    }
+  #else
+
+    #define amx_putstr(s)     fputs((s),stdout)
+    #define amx_putchar(c)    putchar(c)
+    #define amx_fflush()      fflush(stdout)
+    #define amx_getch()       getch()
+    #define amx_gets(s,n)     fgets(s,n,stdin)
+    #define amx_kbhit()       kbhit()
+  #endif
+
+  int amx_termctl(int code,int value)
+  {
+    switch (code) {
+    case 0:             /* query terminal support */
+      return 1;
+
+    case 1:             /* switch "auto-wrap" on or off */
+      if (value)
+        amx_putstr("\033[?7h"); /* enable "auto-wrap" */
+      else
+        amx_putstr("\033[?7l"); /* disable "auto-wrap" */
+      return 1;
+
+    #if 0
+      /* next to swapping buffers, more information should be saved and swapped,
+       * such as the cursor position and the current terminal attributes
+       */
+    case 2:             /* swap console buffers */
+      amx_fflush();
+      if (value==1) {
+        amx_putstr("\033[?47h");
+      } else {
+        amx_putstr("\033[?47l");
+      } /* if */
+      amx_fflush();
+      return 1;
+    #endif
+
+    case 3:             /* set bold/highlighted font */
+      return 0;
+
+    default:
+      return 0;
+    } /* switch */
+  }
+  void amx_clrscr(void)
+  {
+    amx_putstr("\033[2J");
+    amx_fflush();        /* pump through the terminal codes */
+  }
+  void amx_clreol(void)
+  {
+    amx_putstr("\033[K");
+    amx_fflush();        /* pump through the terminal codes */
+  }
+  int amx_gotoxy(int x,int y)
+  {
+    char str[30];
+    _stprintf(str,"\033[%d;%dH",y,x);
+    amx_putstr(str);
+    amx_fflush();        /* pump through the terminal codes */
+    return 1;
+  }
+  void amx_wherexy(int *x,int *y)
+  {
+    int val,i;
+    char str[10];
+
+    assert(x!=NULL && y!=NULL);
+    amx_putstr("\033[6n");
+    amx_fflush();
+    while (amx_getch()!='\033')
+      /* nothing */;
+    val=amx_getch();
+    assert(val=='[');
+    for (i=0; i<8 && (val=amx_getch())!=';'; i++)
+      str[i]=(char)val;
+    str[i]='\0';
+    if (y!=NULL)
+      *y=atoi(str);
+    for (i=0; i<8 && (val=amx_getch())!='R'; i++)
+      str[i]=(char)val;
+    str[i]='\0';
+    if (x!=NULL)
+      *x=atoi(str);
+    #if defined ANSITERM
+      val=amx_getch();
+      assert(val=='\r');    /* ANSI driver adds CR to the end of the command */
+    #endif
+  }
+  unsigned int amx_setattr(int foregr,int backgr,int highlight)
+  {
+    static short current=(0 << 8) | 7;
+    short prev = current;
+    char str[30];
+
+    if (foregr>=0) {
+      _stprintf(str,"\x1b[%dm",foregr+30);
+      amx_putstr(str);
+      current=(current & 0xff00) | (foregr & 0x0f);
+    } /* if */
+    if (backgr>=0) {
+      _stprintf(str,"\x1b[%dm",backgr+40);
+      amx_putstr(str);
+      current=(current & 0x00ff) | ((backgr & 0x0f) << 8);
+    } /* if */
+    if (highlight>=0) {
+      _stprintf(str,"\x1b[%dm",highlight);
+      amx_putstr(str);
+      current=(current & 0x7fff) | ((highlight & 0x01) << 15);
+    } /* if */
+    return prev;
+  }
+  void amx_console(int columns, int lines, int flags)
+  {
+    char str[30];
+
+    (void)flags;
+    /* There is no ANSI code (or VT100/VT220) to set the size of the console
+     * (indeed, the terminal was that of the alphanumeric display). In xterm (a
+     * terminal emulator) we can set the terminal size though, and most
+     * terminals that in use today are in fact emulators.
+     * Putty understands this code too, by many others do not.
+     */
+    sprintf(str,"\033[8;%d;%dt",lines,columns);
+    amx_putstr(str);
+    amx_fflush();
+  }
+  void amx_viewsize(int *width,int *height)
+  {
+    /* a trick to get the size of the terminal is to position the cursor far
+     * away and then read it back
+     */
+    amx_gotoxy(999,999);
+    amx_wherexy(width,height);
+  }
+#elif defined __WIN32__ || defined _WIN32 || defined WIN32
+  /* Win32 console */
+  #define amx_putstr(s)       _tprintf("%s",(s))
+  #define amx_putchar(c)      _puttchar(c)
+  #define amx_fflush()        fflush(stdout)
+  #define amx_getch()         getch()
+  #define amx_gets(s,n)       _fgetts(s,n,stdin)
+  #define amx_kbhit()         kbhit()
+
+  int amx_termctl(int code,int value)
+  {
+    switch (code) {
+    case 0:             /* query terminal support */
+      return 1;
+
+    case 1: {           /* switch auto-wrap on/off */
+      /* only works in Windows 2000/XP */
+      HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE);
+      DWORD Flags=ENABLE_PROCESSED_OUTPUT;
+      if (value)
+        Flags |= ENABLE_WRAP_AT_EOL_OUTPUT;
+      SetConsoleMode(hConsole,Flags);
+      return 1;
+    } /* case */
+
+    /* case 2: */     /* create/switch to another console */
+    /* case 3: */     /* set emphasized font */
+    /* case 4: */     /* query whether a terminal is "open" */
+    default:
+      return 0;
+    } /* switch */
+  }
+  void amx_clrscr(void)
+  {
+    COORD coordScreen={0,0};
+    DWORD cCharsWritten;
+    CONSOLE_SCREEN_BUFFER_INFO csbi;
+    DWORD dwConSize;
+    HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE);
+
+    amx_fflush();       /* make sure the I/O buffer is empty */
+    GetConsoleScreenBufferInfo(hConsole,&csbi);
+    dwConSize=csbi.dwSize.X*csbi.dwSize.Y;
+    FillConsoleOutputCharacter(hConsole,' ',dwConSize,coordScreen,&cCharsWritten);
+    FillConsoleOutputAttribute(hConsole,csbi.wAttributes,dwConSize,coordScreen, &cCharsWritten);
+    SetConsoleCursorPosition(hConsole,coordScreen);
+  }
+  void amx_clreol(void)
+  {
+    DWORD cCharsWritten;
+    CONSOLE_SCREEN_BUFFER_INFO csbi;
+    DWORD dwConSize;
+    HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE);
+
+    amx_fflush();       /* make sure all output is written */
+    GetConsoleScreenBufferInfo(hConsole,&csbi);
+    dwConSize=csbi.dwSize.X - csbi.dwCursorPosition.X;
+    FillConsoleOutputCharacter(hConsole,' ',dwConSize,csbi.dwCursorPosition,&cCharsWritten);
+    FillConsoleOutputAttribute(hConsole,csbi.wAttributes,dwConSize,csbi.dwCursorPosition,&cCharsWritten);
+  }
+  int amx_gotoxy(int x,int y)
+  {
+    COORD point;
+    CONSOLE_SCREEN_BUFFER_INFO csbi;
+    HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE);
+
+    GetConsoleScreenBufferInfo(hConsole, &csbi);
+    if (x<=0 || x>csbi.dwSize.X || y<=0 || y>csbi.dwSize.Y)
+      return 0;
+    amx_fflush();       /* make sure all output is written */
+    point.X=(short)(x-1);
+    point.Y=(short)(y-1);
+    SetConsoleCursorPosition(hConsole,point);
+    return 1;
+  }
+  void amx_wherexy(int *x,int *y)
+  {
+    CONSOLE_SCREEN_BUFFER_INFO csbi;
+    amx_fflush();       /* make sure all output is written */
+    GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
+    if (x!=NULL)
+      *x=csbi.dwCursorPosition.X+1;
+    if (y!=NULL)
+      *y=csbi.dwCursorPosition.Y+1;
+  }
+  unsigned int amx_setattr(int foregr,int backgr,int highlight)
+  {
+    static int ansi_colours[] = { 0, 4, 2, 6, 1, 5, 3, 7 };
+    CONSOLE_SCREEN_BUFFER_INFO csbi;
+    int f,b,h,prev;
+    HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE);
+
+    amx_fflush();       /* make sure all output is written */
+    GetConsoleScreenBufferInfo(hConsole,&csbi);
+    f=csbi.wAttributes & 0x07;
+    b=(csbi.wAttributes >> 4) & 0x0f;
+    h=(csbi.wAttributes & 0x08) ? 1 : 0;
+    prev=(b << 8) | f | (h << 15);
+    if (foregr>=0 && foregr<8)
+      f=ansi_colours[foregr];
+    if (backgr>=0 && backgr<8)
+      b=ansi_colours[backgr];
+    if (highlight>=0)
+      h=highlight!=0;
+    SetConsoleTextAttribute(hConsole, (WORD)((b << 4) | f | (h << 3)));
+    return prev;
+  }
+  void amx_console(int columns, int lines, int flags)
+  {
+    SMALL_RECT rect;
+    COORD dwSize;
+    (void)flags;
+    dwSize.X=(short)columns;
+    dwSize.Y=(short)lines;
+    SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE),dwSize);
+    rect.Left=0;
+    rect.Top=0;
+    rect.Right=(short)(columns-1);
+    rect.Bottom=(short)(lines-1);
+    SetConsoleWindowInfo(GetStdHandle(STD_OUTPUT_HANDLE),TRUE,&rect);
+  }
+  void amx_viewsize(int *width,int *height)
+  {
+    CONSOLE_SCREEN_BUFFER_INFO csbi;
+    GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE),&csbi);
+    if (width!=NULL)
+      *width=(int)csbi.dwSize.X;
+    if (height!=NULL)
+      *height=(int)(csbi.srWindow.Bottom-csbi.srWindow.Top+1);
+  }
+#else
+  /* assume a streaming terminal; limited features (no colour, no cursor
+   * control)
+   */
+  #define amx_putstr(s)       printf("%s",(s))
+  #define amx_putchar(c)      putchar(c)
+  #define amx_fflush()        fflush(stdout)
+  #define amx_gets(s,n)       fgets(s,n,stdin)
+  #define amx_clrscr()        (void)(0)
+  #define amx_clreol()        (void)(0)
+  #define amx_gotoxy(x,y)     ((void)(x),(void)(y),(0))
+  #define amx_wherexy(x,y)    (*(x)=*(y)=0)
+  #define amx_setattr(c,b,h)  ((void)(c),(void)(b),(void)(h),(0))
+  #define amx_termctl(c,v)    ((void)(c),(void)(v),(0))
+  #define amx_console(c,l,f)  ((void)(c),(void)(l),(void)(f))
+  #define amx_viewsize        (*(x)=80,*(y)=25)
+  #if defined HAVE_CONIO
+    #define amx_getch()       getch()
+    #define amx_kbhit()       kbhit()
+  #else
+    #define amx_getch()       getchar()
+    #define amx_kbhit()       (0)
+  #endif
+#endif
+
+#if !defined AMX_TERMINAL && (defined __WIN32__ || defined _WIN32 || defined WIN32)
+  void CreateConsole(void)
+  { static int createdconsole=0;
+    if (!createdconsole) {
+  	  AllocConsole();
+  	  createdconsole=1;
+  	} /* if */
+  }
+#elif defined CURSES && CURSES != 0
+  // The Mac OS X build variant uses curses.
+  void CreateConsole(void)
+  { static int createdconsole=0;
+    if (!createdconsole) {
+      curseswin=initscr();
+      cbreak();
+      noecho();
+      nonl();
+      scrollok(curseswin,TRUE);
+      intrflush(curseswin,FALSE);
+      keypad(curseswin,TRUE);
+      createdconsole=1;
+    } /* if */
+  }
+#else
+  #define CreateConsole()
+#endif
+
+static int cons_putstr(void *dest,const TCHAR *str)
+{
+  (void)dest;
+  return amx_putstr(str);
+}
+
+static int cons_putchar(void *dest,TCHAR ch)
+{
+  (void)dest;
+  return amx_putchar(ch);
+}
+
+#endif /* AMX_STRING_LIB */
+
+enum {
+  SV_DECIMAL,
+  SV_HEX
+};
+
+static TCHAR *reverse(TCHAR *string,int stop)
+{
+	int start=0;
+	TCHAR temp;
+
+	/* swap the string */
+	stop--;				/* avoid swapping the '\0' byte to the first position */
+	while (stop - start > 0) {
+		temp = string[start];
+		string[start] = string[stop];
+		string[stop] = temp;
+		start++;
+		stop--;
+	} /* while */
+	return string;
+}
+
+/* Converts an integral value to a string, with optional padding with spaces or
+ * zeros.
+ * The "format" must be decimal or hexadecimal
+ * The number is right-aligned in the field with the size of the absolute value
+ * of the "width" parameter.
+ * If the width value is positive, the string is padded with spaces; if it is
+ * negative, it is padded with zeros.
+ */
+static TCHAR *amx_strval(TCHAR buffer[], long value, int format, int width)
+{
+	int start, stop;
+	TCHAR temp;
+
+	start = stop = 0;
+	if (format == SV_DECIMAL) {
+		if (value < 0) {
+			buffer[0] = __T('-');
+			start = stop = 1;
+			value = -value;
+		} /* if */
+		do {
+			buffer[stop++] = (TCHAR)((value % 10) + __T('0'));
+			value /= 10;
+		} while (value > 0);
+	} else {
+		/* hexadecimal */
+		unsigned long v = (unsigned long)value;	/* copy to unsigned value for shifting */
+		do {
+			buffer[stop] = (TCHAR)((v & 0x0f) + __T('0'));
+			if (buffer[stop] > __T('9'))
+				buffer[stop] += (TCHAR)(__T('A') - __T('0') - 10);
+			v >>= 4;
+			stop++;
+		} while (v != 0);
+	} /* if */
+
+	/* pad to given width */
+	if (width < 0) {
+		temp = __T('0');
+		width = -width;
+	} else {
+		temp = __T(' ');
+	} /* if */
+	while (stop < width)
+		buffer[stop++] = temp;
+
+	buffer[stop] = __T('\0');
+
+	/* swap the string, and we are done */
+	reverse(buffer+start,stop-start);
+	return buffer;
+}
+
+#if defined FIXEDPOINT
+  #define FIXEDMULT     1000
+  #define FIXEDDIGITS   3
+
+static TCHAR *formatfixed(TCHAR *string,cell value,TCHAR align,int width,TCHAR decpoint,int digits,TCHAR filler)
+{
+  int i, len;
+  cell ipart,v;
+  TCHAR vsign=__T('\0');
+
+  /* make the value positive (but keep the sign) */
+  if (value<0) {
+    value=-value;
+    vsign=__T('-');
+  } /* if */
+
+  /* "prepare" the value so that when it is truncated to the requested
+   * number of digits, the result is rounded towards the dropped digits
+   */
+  assert(digits<INT_MAX);
+  v=FIXEDMULT/2;
+  for (i=0; i<digits; i++)
+    v/=10;
+  value+=v;
+
+  /* get the integer part and remove it from the value */
+  ipart=value/FIXEDMULT;
+  value-=FIXEDMULT*ipart;
+  assert(ipart>=0);
+  assert(value>=0);
+
+  /* truncate the fractional part to the requested number of digits */
+  for (i=FIXEDDIGITS; i>digits; i--)
+    value/=10;
+
+  string[0]=__T('\0');
+
+  /* add sign */
+  i=_tcslen(string);
+  string[i]=vsign;
+  string[i+1]=__T('\0');
+
+  /* add integer part */
+  amx_strval(string+_tcslen(string),(long)ipart,SV_DECIMAL,0);
+
+  /* add fractional part */
+  if (digits>0) {
+    i=_tcslen(string);
+    string[i]=decpoint;
+    amx_strval(string+i+1,(long)value,SV_DECIMAL,-digits);
+  } /* if */
+
+  len=_tcslen(string);
+  if (len<width) {
+    /* pad to the requested width */
+    for (i=len; i<width; i++)
+      string[i]=filler;
+    string[i]=__T('\0');
+    /* optionally move the padding to the beginning of the string, using the handwaving algorithm */
+    if (align!=__T('-')) {
+      assert(i==(int)_tcslen(string));
+      assert(i>=len);
+      reverse(string,len);
+      reverse(string+len,i-len);
+      reverse(string,i);
+    } /* if */
+  } /* if */
+
+  return string;
+}
+#endif
+
+
+static int dochar(AMX *amx,TCHAR ch,cell param,TCHAR sign,TCHAR decpoint,int width,int digits,TCHAR filler,
+                  int (*f_putstr)(void*,const TCHAR *),int (*f_putchar)(void*,TCHAR),void *user)
+{
+  cell *cptr;
+  TCHAR buffer[40];
+  #if defined FLOATPOINT
+    TCHAR formatstring[40];
+  #endif
+
+  #if !defined FIXEDPOINT && !defined FLOATPOINT
+    (void)decpoint;
+  #endif
+  assert(f_putstr!=NULL);
+  assert(f_putchar!=NULL);
+
+  switch (ch) {
+  case __T('c'):
+    cptr=amx_Address(amx,param);
+    width--;            /* single character itself has a with of 1 */
+    if (sign!=__T('-'))
+      while (width-->0)
+        f_putchar(user,filler);
+    f_putchar(user,(TCHAR)*cptr);
+    while (width-->0)
+      f_putchar(user,filler);
+    return 1;
+
+  case __T('d'): {
+    cell value;
+    int length=1;
+    cptr=amx_Address(amx,param);
+    value=*cptr;
+    if (value<0 || sign==__T('+'))
+      length++;
+    if (value<0)
+      value=-value;
+    while (value>=10) {
+      length++;
+      value/=10;
+    } /* while */
+    width-=length;
+    if (sign!=__T('-'))
+      while (width-->0)
+        f_putchar(user,filler);
+    amx_strval(buffer,*cptr,SV_DECIMAL,0);
+    if (sign==__T('+') && *cptr>=0)
+      f_putchar(user,sign);
+    f_putstr(user,buffer);
+    while (width-->0)
+      f_putchar(user,filler);
+    return 1;
+  } /* case */
+
+#if defined FLOATPOINT
+  case __T('f'): /* 32-bit floating point number */
+  case __T('r'): /* if floating point is enabled, %r == %f */
+    /* build a format string */
+    if (digits==INT_MAX)
+      digits=5;
+    else if (digits>25)
+      digits=25;
+    _tcscpy(formatstring,__T("%"));
+    if (sign!=__T('\0'))
+      _stprintf(formatstring+_tcslen(formatstring),__T("%c"),sign);
+    if (width>0)
+      _stprintf(formatstring+_tcslen(formatstring),__T("%d"),width);
+    _stprintf(formatstring+_tcslen(formatstring),__T(".%df"),digits);
+    cptr=amx_Address(amx,param);
+    #if PAWN_CELL_SIZE == 64
+      _stprintf(buffer,formatstring,*(double*)cptr);
+    #else
+      _stprintf(buffer,formatstring,*(float*)cptr);
+    #endif
+    if (decpoint==__T(',')) {
+      TCHAR *ptr=_tcschr(buffer,__T('.'));
+      if (ptr!=NULL)
+        *ptr=__T(',');
+    } /* if */
+    f_putstr(user,buffer);
+    return 1;
+#endif
+
+#if defined FIXEDPOINT
+  #define FIXEDMULT 1000
+  case __T('q'): /* 32-bit fixed point number */
+#if !defined FLOATPOINT
+  case __T('r'): /* if fixed point is enabled, and floating point is not, %r == %q */
+#endif
+    cptr=amx_Address(amx,param);
+    /* format the number */
+    if (digits==INT_MAX)
+      digits=3;
+    else if (digits>25)
+      digits=25;
+    formatfixed(buffer,*cptr,sign,width,decpoint,digits,filler);
+    assert(_tcslen(buffer)<sizeof buffer);
+    f_putstr(user,buffer);
+    return 1;
+#endif
+
+#if !defined FLOATPOINT && !defined FIXEDPOINT
+  case __T('f'):
+  case __T('q'):
+  case __T('r'):
+    f_putstr(user,__T("(no rational number support)"));
+    return 0; /* flag this as an error */
+#endif
+
+  case __T('s'): {
+    AMX_FMTINFO info;
+    memset(&info,0,sizeof info);
+    info.length=digits;
+    info.f_putstr=f_putstr;
+    info.f_putchar=f_putchar;
+    info.user=user;
+    cptr=amx_Address(amx,param);
+    amx_printstring(amx,cptr,&info);
+    return 1;
+  } /* case */
+
+  case __T('x'): {
+    ucell value;
+    int length=1;
+    cptr=amx_Address(amx,param);
+    value=*(ucell*)cptr;
+    while (value>=0x10) {
+      length++;
+      value>>=4;
+    } /* while */
+    width-=length;
+    if (sign!=__T('-'))
+      while (width-->0)
+        f_putchar(user,filler);
+    amx_strval(buffer,(long)*cptr,SV_HEX,0);
+    f_putstr(user,buffer);
+    while (width-->0)
+      f_putchar(user,filler);
+    return 1;
+  } /* case */
+
+  } /* switch */
+  /* error in the string format, try to repair */
+  f_putchar(user,ch);
+  return 0;
+}
+
+enum {
+  FMT_NONE,   /* not in format state; accept '%' */
+  FMT_START,  /* found '%', accept '+', '-' (START), '0' (filler; START), digit (WIDTH), '.' (DECIM), or '%' or format letter (done) */
+  FMT_WIDTH,  /* found digit after '%' or sign, accept digit (WIDTH), '.' (DECIM) or format letter (done) */
+  FMT_DECIM,  /* found digit after '.', accept accept digit (DECIM) or format letter (done) */
+};
+
+static int formatstate(TCHAR c,int *state,TCHAR *sign,TCHAR *decpoint,int *width,int *digits,TCHAR *filler)
+{
+  assert(state!=NULL && sign!=NULL && decpoint!=NULL && width!=NULL && digits!=NULL && filler!=NULL);
+  switch (*state) {
+  case FMT_NONE:
+    if (c==__T('%')) {
+      *state=FMT_START;
+      *sign=__T('\0');
+      *decpoint=__T('.');
+      *width=0;
+      *digits=INT_MAX;
+      *filler=__T(' ');
+    } else {
+      return -1;  /* print a single character */
+    } /* if */
+    break;
+  case FMT_START:
+    if (c==__T('+') || c==__T('-')) {
+      *sign=c;
+    } else if (c==__T('0')) {
+      *filler=c;
+    } else if (c>=__T('1') && c<=__T('9')) {
+      *width=(int)(c-__T('0'));
+      *state=FMT_WIDTH;
+    } else if (c==__T('.') || c==__T(',')) {
+      *decpoint=c;
+      *digits=0;
+      *state=FMT_DECIM;
+    } else if (c==__T('%')) {
+      *state=FMT_NONE;
+      return -1;  /* print literal '%' */
+    } else {
+      return 1;   /* print formatted character */
+    } /* if */
+    break;
+  case FMT_WIDTH:
+    if (c>=__T('0') && c<=__T('9')) {
+      *width=*width*10+(int)(c-__T('0'));
+    } else if (c==__T('.') || c==__T(',')) {
+      *decpoint=c;
+      *digits=0;
+      *state=FMT_DECIM;
+    } else {
+      return 1;   /* print formatted character */
+    } /* if */
+    break;
+  case FMT_DECIM:
+    if (c>=__T('0') && c<=__T('9')) {
+      *digits=*digits*10+(int)(c-__T('0'));
+    } else {
+      return 1;   /* print formatted character */
+    } /* if */
+    break;
+  } /* switch */
+
+  return 0;
+}
+
+int amx_printstring(AMX *amx,cell *cstr,AMX_FMTINFO *info)
+{
+  int i,paramidx=0;
+  int fmtstate=FMT_NONE,width,digits;
+  TCHAR sign,decpoint,filler;
+  int (*f_putstr)(void*,const TCHAR *);
+  int (*f_putchar)(void*,TCHAR);
+  void *user;
+  int skip,length;
+
+  if (info!=NULL) {
+    f_putstr=info->f_putstr;
+    f_putchar=info->f_putchar;
+    user=info->user;
+    skip=info->skip;
+    length=info->length;
+  } else {
+    f_putstr=NULL;
+    f_putchar=NULL;
+    user=NULL;
+    skip=0;
+    length=INT_MAX;
+  } /* if */
+  #if !defined AMX_STRING_LIB
+    if (f_putstr==NULL)
+      f_putstr=cons_putstr;
+    if (f_putchar==NULL)
+      f_putchar=cons_putchar;
+  #else
+    assert(f_putstr!=NULL && f_putchar!=NULL);
+  #endif
+
+  /* if no placeholders appear, we can use a quicker routine */
+  if (info==NULL || info->params==NULL) {
+
+    TCHAR cache[100];
+    int idx=0;
+
+    if ((ucell)*cstr>UNPACKEDMAX) {
+      int j=sizeof(cell)-sizeof(char);
+      char c;
+      /* the string is packed */
+      i=0;
+      for ( ; ; ) {
+        c=(char)((ucell)cstr[i] >> 8*j);
+        if (c==0)
+          break;
+        if (skip>0) {
+          skip--;               /* skip a number of characters */
+        } else {
+          if (length--<=0)
+            break;              /* print up to a certain length */
+          assert(idx<sizeof cache);
+          cache[idx++]=c;
+          if (idx==sizeof cache - 1) {
+            cache[idx]=__T('\0');
+            f_putstr(user,cache);
+            idx=0;
+          } /* if */
+        } /* if */
+        if (j==0)
+          i++;
+        j=(j+sizeof(cell)-sizeof(char)) % sizeof(cell);
+      } /* for */
+    } else {
+      /* unpacked string */
+      for (i=0; cstr[i]!=0; i++) {
+      	if (skip-->0)
+      	  continue;
+        assert(idx<sizeof cache);
+        cache[idx++]=(TCHAR)cstr[i];
+        if (idx==sizeof cache - 1) {
+          cache[idx]=__T('\0');
+          f_putstr(user,cache);
+          idx=0;
+        } /* if */
+      } /* for */
+    } /* if */
+    if (idx>0) {
+      cache[idx]=__T('\0');
+      f_putstr(user,cache);
+    } /* if */
+
+  } else {
+
+    /* check whether this is a packed string */
+    if ((ucell)*cstr>UNPACKEDMAX) {
+      int j=sizeof(cell)-sizeof(char);
+      char c;
+      /* the string is packed */
+      i=0;
+      for ( ; ; ) {
+        c=(char)((ucell)cstr[i] >> 8*j);
+        if (c==0)
+          break;
+        switch (formatstate(c,&fmtstate,&sign,&decpoint,&width,&digits,&filler)) {
+        case -1:
+          f_putchar(user,c);
+          break;
+        case 0:
+          break;
+        case 1:
+          assert(info!=NULL && info->params!=NULL);
+          if (paramidx>=info->numparams)  /* insufficient parameters passed */
+            amx_RaiseError(amx, AMX_ERR_NATIVE);
+          else
+            paramidx+=dochar(amx,c,info->params[paramidx],sign,decpoint,width,digits,filler,
+                             f_putstr,f_putchar,user);
+          fmtstate=FMT_NONE;
+          break;
+        default:
+          assert(0);
+        } /* switch */
+        if (j==0)
+          i++;
+        j=(j+sizeof(cell)-sizeof(char)) % sizeof(cell);
+      } /* for */
+    } else {
+      /* the string is unpacked */
+      for (i=0; cstr[i]!=0; i++) {
+        switch (formatstate((TCHAR)cstr[i],&fmtstate,&sign,&decpoint,&width,&digits,&filler)) {
+        case -1:
+          f_putchar(user,(TCHAR)cstr[i]);
+          break;
+        case 0:
+          break;
+        case 1:
+          assert(info!=NULL && info->params!=NULL);
+          if (paramidx>=info->numparams)  /* insufficient parameters passed */
+            amx_RaiseError(amx, AMX_ERR_NATIVE);
+          else
+            paramidx+=dochar(amx,(TCHAR)cstr[i],info->params[paramidx],sign,decpoint,width,digits,filler,
+                             f_putstr,f_putchar,user);
+          fmtstate=FMT_NONE;
+          break;
+        default:
+          assert(0);
+        } /* switch */
+      } /* for */
+    } /* if */
+
+  } /* if (info==NULL || info->params==NULL) */
+
+  return paramidx;
+}
+
+#if !defined AMX_STRING_LIB
+
+#if defined AMX_ALTPRINT
+/* print(const string[], start=0, end=cellmax) */
+static cell AMX_NATIVE_CALL n_print(AMX *amx,const cell *params)
+{
+  cell *cstr;
+  AMX_FMTINFO info;
+
+  memset(&info,0,sizeof info);
+  info.skip= ((size_t)params[0]>=2*sizeof(cell)) ? (int)params[2] : 0;
+  info.length= ((size_t)params[0]>=3*sizeof(cell)) ? (int)(params[3]-info.skip) : INT_MAX;
+
+  CreateConsole();
+  cstr=amx_Address(amx,params[1]);
+  amx_printstring(amx,cstr,&info);
+  amx_fflush();
+  return 0;
+}
+#else
+/* print(const string[], foreground=-1, background=-1, highlight=-1) */
+static cell AMX_NATIVE_CALL n_print(AMX *amx,const cell *params)
+{
+  cell *cstr;
+  int oldcolours;
+
+  CreateConsole();
+
+  /* set the new colours */
+  oldcolours=amx_setattr((int)params[2],(int)params[3],(int)params[4]);
+
+  cstr=amx_Address(amx,params[1]);
+  amx_printstring(amx,cstr,NULL);
+
+  /* reset the colours */
+  (void)amx_setattr(oldcolours & 0xff,(oldcolours >> 8) & 0x7f,(oldcolours >> 15) & 0x01);
+  amx_fflush();
+  return 0;
+}
+#endif
+
+static cell AMX_NATIVE_CALL n_printf(AMX *amx,const cell *params)
+{
+  cell *cstr;
+  AMX_FMTINFO info;
+
+  memset(&info,0,sizeof info);
+  info.params=params+2;
+  info.numparams=(int)(params[0]/sizeof(cell))-1;
+  info.skip=0;
+  info.length=INT_MAX;
+
+  CreateConsole();
+  cstr=amx_Address(amx,params[1]);
+  amx_printstring(amx,cstr,&info);
+  amx_fflush();
+  return 0;
+}
+
+/* getchar(bool:echo=true) */
+static cell AMX_NATIVE_CALL n_getchar(AMX *amx,const cell *params)
+{
+  int c;
+
+  (void)amx;
+  CreateConsole();
+  c=amx_getch();
+  if (params[1]) {
+    #if defined(SUPPRESS_ECHO)
+ 	  /* For Mac OS X, non-Curses, don't echo the character */
+    #else
+      amx_putchar((TCHAR)c);
+      amx_fflush();
+    #endif
+  } /* if */
+  return c;
+}
+
+/* getstring(string[], size=sizeof string, bool:pack=false) */
+static cell AMX_NATIVE_CALL n_getstring(AMX *amx,const cell *params)
+{
+  int c,chars,max;
+  cell *cptr;
+
+  (void)amx;
+  CreateConsole();
+  chars=0;
+  max=(int)params[2];
+  if (max>0) {
+    #if __STDC_VERSION__ >= 199901L
+      TCHAR str[max];   /* use C99 feature if available */
+    #else
+      TCHAR *str=(TCHAR *)alloca(max*sizeof(TCHAR));
+      if (str==NULL)
+        return chars;
+    #endif
+
+    c=amx_getch();
+    while (c!=EOF && c!=EOL_CHAR && chars<max-1) {
+      str[chars++]=(TCHAR)c;
+      #if defined(SUPPRESS_ECHO)
+ 	    /* For Mac OS X, non-Curses, don't echo the character */
+      #else
+        amx_putchar((TCHAR)c);
+        amx_fflush();
+      #endif
+      if (chars<max-1)
+        c=amx_getch();
+    } /* while */
+
+    if (c==EOL_CHAR)
+      amx_putchar('\n');
+    assert(chars<max);
+    str[chars]='\0';
+
+    cptr=amx_Address(amx,params[1]);
+    amx_SetString(cptr,(char*)str,(int)params[3],sizeof(TCHAR)>1,max);
+
+  } /* if */
+  return chars;
+}
+
+static void acceptchar(int c,int *num)
+{
+  switch (c) {
+  case '\b':
+    amx_putchar('\b');
+    *num-=1;
+    #if defined amx_putchar && (defined __BORLANDC__ || defined __WATCOMC__)
+      /* the backspace key does not erase the
+       * character, so do this explicitly */
+      amx_putchar(' ');     /* erase */
+      amx_putchar('\b');    /* go back */
+    #endif
+    break;
+  case EOL_CHAR:
+    amx_putchar('\n');
+    *num+=1;
+    break;
+  default:
+    #if defined(SUPPRESS_ECHO)
+ 	  /* For Mac OS X, non-Curses, don't echo the character */
+    #else
+      amx_putchar((TCHAR)c);
+    #endif
+    *num+=1;
+  } /* switch */
+  amx_fflush();
+}
+
+static int inlist(AMX *amx,int c,const cell *params,int num)
+{
+  int i, key;
+
+  (void)amx;
+  for (i=0; i<num; i++) {
+    if (i==0) {
+      /* first key is passed by value, others are passed by reference */
+      key = (int)params[i];
+    } else {
+      cell *cptr;
+      cptr=amx_Address(amx,params[i]);
+      key=(int)*cptr;
+    } /* if */
+    if (c==key || c==-key)
+      return key;
+  } /* for */
+  return 0;
+}
+
+static cell AMX_NATIVE_CALL n_getvalue(AMX *amx,const cell *params)
+{
+  cell value;
+  int base,sign,c,d;
+  int chars,n;
+
+  CreateConsole();
+  base=(int)params[1];
+  if (base<2 || base>36)
+    return 0;
+
+  chars=0;
+  value=0;
+  sign=1;       /* to avoid a compiler warning (Microsoft Visual C/C++ 6.0) */
+
+  c=amx_getch();
+  while (c!=EOF) {
+    /* check for sign (if any) */
+    if (chars==0) {
+      if (c=='-') {
+        sign=-1;
+        acceptchar(c,&chars);
+        c=amx_getch();
+      } else {
+        sign=1;
+      } /* if */
+    } /* if */
+
+    /* check end of input */
+    #if EOL_CHAR!='\r'
+      if (c==EOL_CHAR && inlist(amx,'\r',params+2,(int)params[0]/sizeof(cell)-1)!=0)
+        c='\r';
+    #endif
+    if ((chars>1 || chars>0 && sign>0)
+        && (n=inlist(amx,c,params+2,(int)params[0]/sizeof(cell)-1))!=0)
+    {
+      if (n>0)
+        acceptchar(c,&chars);
+      break;
+    } /* if */
+    #if EOL_CHAR!='\r'
+      if (c=='\r')
+        c=EOL_CHAR;
+    #endif
+
+    /* get value */
+    d=base;     /* by default, do not accept the character */
+    if (c>='0' && c<='9') {
+      d=c-'0';
+    } else if (c>='a' && c<='z') {
+      d=c-'a'+10;
+    } else if (c>='A' && c<='Z') {
+      d=c-'A'+10;
+    } else if (c=='\b') {
+      if (chars>0) {
+        value/=base;
+        acceptchar(c,&chars);
+      } /* if */
+    } /* if */
+    if (d<base) {
+      acceptchar(c,&chars);
+      value=value*base + d;
+    } /* if */
+    c=amx_getch();
+  } /* while */
+  return sign*value;
+}
+
+static cell AMX_NATIVE_CALL n_clrscr(AMX *amx,const cell *params)
+{
+  (void)amx;
+  (void)params;
+  CreateConsole();
+  amx_clrscr();
+  return 0;
+}
+
+static cell AMX_NATIVE_CALL n_clreol(AMX *amx,const cell *params)
+{
+  (void)amx;
+  (void)params;
+  CreateConsole();
+  amx_clreol();
+  return 0;
+}
+
+static cell AMX_NATIVE_CALL n_gotoxy(AMX *amx,const cell *params)
+{
+  (void)amx;
+  CreateConsole();
+  return amx_gotoxy((int)params[1],(int)params[2]);
+}
+
+static cell AMX_NATIVE_CALL n_wherexy(AMX *amx,const cell *params)
+{
+  cell *px,*py;
+  int x,y;
+
+  (void)amx;
+  CreateConsole();
+  amx_wherexy(&x,&y);
+  px=amx_Address(amx,params[1]);
+  py=amx_Address(amx,params[2]);
+  *px=x;
+  *py=y;
+  return 0;
+}
+
+static cell AMX_NATIVE_CALL n_setattr(AMX *amx,const cell *params)
+{
+  (void)amx;
+  CreateConsole();
+  (void)amx_setattr((int)params[1],(int)params[2],(int)params[3]);
+  return 0;
+}
+
+static cell AMX_NATIVE_CALL n_consctrl(AMX *amx,const cell *params)
+{
+  (void)amx;
+  CreateConsole();
+  (void)amx_termctl((int)params[1],(int)params[2]);
+  return 0;
+}
+
+static cell AMX_NATIVE_CALL n_console(AMX *amx,const cell *params)
+{
+  (void)amx;
+  CreateConsole();
+  amx_console((int)params[1],(int)params[2],(int)params[3]);
+  return 0;
+}
+
+
+#if !defined AMXCONSOLE_NOIDLE
+static AMX_IDLE PrevIdle = NULL;
+static int idxKeyPressed = -1;
+
+static int AMXAPI amx_ConsoleIdle(AMX *amx, int AMXAPI Exec(AMX *, cell *, int))
+{
+  int err=0, key;
+
+  assert(idxKeyPressed >= 0);
+
+  if (PrevIdle != NULL)
+    PrevIdle(amx, Exec);
+
+  if (amx_kbhit()) {
+    key = amx_getch();
+    amx_Push(amx, key);
+    err = Exec(amx, NULL, idxKeyPressed);
+    while (err == AMX_ERR_SLEEP)
+      err = Exec(amx, NULL, AMX_EXEC_CONT);
+  } /* if */
+
+  return err;
+}
+#endif
+
+#if defined __cplusplus
+  extern "C"
+#endif
+const AMX_NATIVE_INFO console_Natives[] = {
+//  { "getchar",   n_getchar },
+//  { "getstring", n_getstring },
+//  { "getvalue",  n_getvalue },
+  { "print",     n_print },
+  { "printf",    n_printf },
+//  { "clrscr",    n_clrscr },
+//  { "clreol",    n_clreol },
+//  { "gotoxy",    n_gotoxy },
+//  { "wherexy",   n_wherexy },
+//  { "setattr",   n_setattr },
+//  { "console",   n_console },
+//  { "consctrl",  n_consctrl },
+  { NULL, NULL }        /* terminator */
+};
+
+int AMXEXPORT AMXAPI amx_ConsoleInit(AMX *amx)
+{
+  #if !defined AMXCONSOLE_NOIDLE
+    /* see whether there is an @keypressed() function */
+    if (amx_FindPublic(amx, "@keypressed", &idxKeyPressed) == AMX_ERR_NONE) {
+      if (amx_GetUserData(amx, AMX_USERTAG('I','d','l','e'), (void**)&PrevIdle) != AMX_ERR_NONE)
+        PrevIdle = NULL;
+      amx_SetUserData(amx, AMX_USERTAG('I','d','l','e'), (void*)amx_ConsoleIdle);
+    } /* if */
+  #endif
+
+  return amx_Register(amx, console_Natives, -1);
+}
+
+int AMXEXPORT AMXAPI amx_ConsoleCleanup(AMX *amx)
+{
+  (void)amx;
+  #if !defined AMXCONSOLE_NOIDLE
+    PrevIdle = NULL;
+  #endif
+  return AMX_ERR_NONE;
+}
+
+#endif /* AMX_STRING_LIB */