Files
retrobsd/src/cmd/smlrc/lb.c
Alexey Frunze bfc3125556 Floats in Smaller C!
double is an alias for float. IOW, only 32-bit
single precision floats are supported. This isn't
conformant, but OK for some embedded systems (e.g.
RetroBSD) and simple compilers like this.

Also, the following operators are not supported with
floats at the moment: ++, --, +=, -=, *=, /=.
But +, -, *, /, =, ||, &&, ?:, !, comparison, casts,
if/while/for are OK.
2016-01-16 22:45:42 -08:00

1759 lines
36 KiB
C

/*
Copyright (c) 2013-2016, Alexey Frunze
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __APPLE__
/*****************************************************************************/
/* */
/* minimal stdlibc */
/* */
/* Just enough code for Smaller C to compile into a */
/* 16/32-bit DOS/Windows/Linux/RetroBSD "EXE". */
/* */
/*****************************************************************************/
#ifndef __SMALLER_C__
#error must be compiled with Smaller C
#endif
#ifndef __SMALLER_C_SCHAR__
#ifndef __SMALLER_C_UCHAR__
#error __SMALLER_C_SCHAR__ or __SMALLER_C_UCHAR__ must be defined
#endif
#endif
#ifndef __SMALLER_C_16__
#ifndef __SMALLER_C_32__
#error __SMALLER_C_16__ or __SMALLER_C_32__ must be defined
#endif
#endif
#ifndef _RETROBSD
#ifndef _LINUX
#ifndef _WIN32
#ifndef _DOS
#define _DOS
#endif
#endif
#endif
#endif
#define NULL 0
#define EOF (-1)
#define FILE void
extern int main(int argc, char** argv);
void exit(int);
#ifdef __SMALLER_C_32__
extern void __x87init(void);
#endif
#ifdef _RETROBSD
void __start__(int argc, char** argv)
{
exit(main(argc, argv));
}
#else
#ifdef _LINUX
void __start__(int argc, char** argv)
{
__x87init();
exit(main(argc, argv));
}
#else
void __setargs__(int* pargc, char*** pargv);
void __start__(void)
{
int argc;
char** argv;
__setargs__(&argc, &argv);
#ifdef __SMALLER_C_32__
__x87init();
#endif
exit(main(argc, argv));
}
#endif
#endif
unsigned char __chartype__[1 + 256] =
{
0x00, // for EOF=-1
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x03,0x03,0x03,0x03,0x03,0x01,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x02,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x04,0x04,0x04,0x04,0x04,0x04,
0x04,0x50,0x50,0x50,0x50,0x50,0x50,0x10, 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,
0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, 0x10,0x10,0x10,0x04,0x04,0x04,0x04,0x04,
0x04,0x60,0x60,0x60,0x60,0x60,0x60,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x04,0x04,0x04,0x04,0x01,
};
int isspace(int c)
{
return __chartype__[c + 1] & 0x02;
}
int isdigit(int c)
{
return __chartype__[c + 1] & 0x08;
}
int isalpha(int c)
{
return __chartype__[c + 1] & 0x30;
}
int isalnum(int c)
{
return __chartype__[c + 1] & 0x38;
}
int atoi(char* s)
{
// simplified version
int r = 0;
int neg = 0;
if (*s == '-')
{
neg = 1;
++s;
}
else if (*s == '+')
{
++s;
}
while (isdigit(*s & 0xFFu))
r = r * 10u + *s++ - '0';
if (neg)
r = -r;
return r;
}
unsigned strlen(char* str)
{
#ifndef __SMALLER_C_16__
char* s;
if (str == NULL)
return 0;
for (s = str; *s; ++s);
return s - str;
#else
asm("mov di, [bp+4]\n"
"mov cx, -1\n"
"xor al, al\n"
"cld\n"
"repnz scasb\n"
"mov ax, cx\n"
"not ax\n"
"dec ax");
#endif
}
char* strcpy(char* dst, char* src)
{
#ifndef __SMALLER_C_16__
char* p = dst;
while ((*p++ = *src++) != 0);
#else
asm("mov di, [bp+6]\n"
"mov si, di\n"
"mov cx, -1\n"
"xor al, al\n"
"cld\n"
"repnz scasb\n"
"not cx\n"
"mov di, [bp+4]\n"
"rep movsb");
#endif
return dst;
}
char* strchr(char* s, int c)
{
#ifndef __SMALLER_C_16__
char ch = c;
while (*s)
{
if (*s == ch)
return s;
++s;
}
if (!ch)
return s;
return NULL;
#else
asm("mov si, [bp+4]\n"
"mov bl, [bp+6]\n"
"xor ax, ax\n"
"cld\n"
"strchrlp:\n"
"lodsb\n"
"cmp al, bl\n"
"je strchrmch\n"
"or al, al\n"
"jnz strchrlp\n"
"jmp strchrend");
asm("strchrmch:\n"
"lea ax, [si-1]\n"
"strchrend:");
#endif
}
int strcmp(char* s1, char* s2)
{
#ifndef __SMALLER_C_16__
while (*s1 == *s2)
{
if (!*s1)
return 0;
++s1;
++s2;
}
return (*s1 & 0xFF) - (*s2 & 0xFF);
#else
asm("mov di, [bp+6]\n"
"mov si, di\n"
"mov cx, -1\n"
"xor ax, ax\n"
"mov bx, ax\n"
"cld\n"
"repnz scasb\n"
"not cx");
asm("mov di, [bp+4]\n"
"repe cmpsb\n"
"mov al, [di-1]\n"
"mov bl, [si-1]\n"
"sub ax, bx");
#endif
}
int strncmp(char* s1, char* s2, unsigned n)
{
#ifndef __SMALLER_C_16__
if (!n)
return 0;
do
{
if (*s1 != *s2++)
return (*s1 & 0xFF) - (*--s2 & 0xFF);
if (!*s1++)
break;
} while (--n);
return 0;
#else
asm("mov cx, [bp+8]\n"
"xor ax, ax\n"
"jcxz strncmpend\n"
"mov bx, ax\n"
"mov di, [bp+6]\n"
"mov si, di\n"
"cld\n"
"repnz scasb\n"
"sub cx, [bp+8]\n"
"neg cx");
asm("mov di, [bp+4]\n"
"repe cmpsb\n"
"mov al, [di-1]\n"
"mov bl, [si-1]\n"
"sub ax, bx\n"
"strncmpend:");
#endif
}
void* memmove(void* dst, void* src, unsigned n)
{
#ifndef __SMALLER_C_16__
char* d = dst;
char* s = src;
if (s < d)
{
s += n;
d += n;
while (n--)
*--d = *--s;
}
else
{
while (n--)
*d++ = *s++;
}
#else
if (src < dst)
{
asm("mov di, [bp+4]\n"
"mov si, [bp+6]\n"
"mov cx, [bp+8]\n"
"add di, cx\n"
"dec di\n"
"add si, cx\n"
"dec si\n"
"std\n"
"rep movsb");
}
else
{
asm("mov di, [bp+4]\n"
"mov si, [bp+6]\n"
"mov cx, [bp+8]\n"
"cld\n"
"rep movsb");
}
#endif
return dst;
}
void* memcpy(void* dst, void* src, unsigned n)
{
#ifndef __SMALLER_C_16__
char* p1 = dst;
char* p2 = src;
while (n--)
*p1++ = *p2++;
#else
asm("mov di, [bp+4]\n"
"mov si, [bp+6]\n"
"mov cx, [bp+8]\n"
"shr cx, 1\n"
"cld\n"
"rep movsw\n"
"rcl cx, 1\n"
"rep movsb");
#endif
return dst;
}
void* memset(void* s, int c, unsigned n)
{
#ifndef __SMALLER_C_16__
char* p = s;
while (n--)
*p++ = c;
#else
asm("mov di, [bp+4]\n"
"mov al, [bp+6]\n"
"mov ah, al\n"
"mov cx, [bp+8]\n"
"shr cx, 1\n"
"cld\n"
"rep stosw\n"
"rcl cx, 1\n"
"rep stosb");
#endif
return s;
}
#ifdef __SMALLER_C_32__
int memcmp(void* s1, void* s2, unsigned n)
{
if (n)
{
unsigned char *p1 = s1, *p2 = s2;
do
{
if (*p1++ != *p2++)
return (*--p1 - *--p2);
} while (--n);
}
return 0;
}
#endif
int fputc(int c, FILE* stream);
int putchar(int c);
int __putchar__(char** buf, FILE* stream, int c)
{
if (buf)
{
*(*buf)++ = c;
}
else if (stream)
{
fputc(c, stream);
}
else
{
putchar(c);
}
return 1;
}
int __vsprintf__(char** buf, FILE* stream, char* fmt, void* vl)
{
// Simplified version!
// No I/O error checking!
int* pp = vl;
int cnt = 0;
char* p;
char* phex;
char s[1/*sign*/+10/*magnitude*/+1/*\0*/]; // up to 11 octal digits in 32-bit numbers
char* pc;
int n, sign, msign;
int minlen, len;
int leadchar = ' ';
for (p = fmt; *p != '\0'; ++p)
{
if (*p != '%' || p[1] == '%')
{
__putchar__(buf, stream, *p);
p = p + (*p == '%');
++cnt;
continue;
}
++p;
minlen = 0;
msign = 0;
if (*p == '+') { msign = 1; ++p; }
else if (*p == '-') { msign = -1; ++p; }
if (isdigit(*p & 0xFFu))
{
if (*p == '0')
leadchar = '0';
else
leadchar = ' ';
while (isdigit(*p & 0xFFu))
minlen = minlen * 10 + *p++ - '0';
if (msign < 0)
minlen = -minlen;
msign = 0;
}
if (!msign)
{
if (*p == '+') { msign = 1; ++p; }
else if (*p == '-') { msign = -1; ++p; }
}
phex = "0123456789abcdef";
switch (*p)
{
case 'c':
while (minlen > 1) { __putchar__(buf, stream, ' '); ++cnt; --minlen; }
__putchar__(buf, stream, *pp++);
while (-minlen > 1) { __putchar__(buf, stream, ' '); ++cnt; ++minlen; }
++cnt;
break;
case 's':
pc = (char*)*pp++;
len = 0;
if (pc)
len = strlen(pc);
while (minlen > len) { __putchar__(buf, stream, ' '); ++cnt; --minlen; }
if (len)
while (*pc != '\0')
{
__putchar__(buf, stream, *pc++);
++cnt;
}
while (-minlen > len) { __putchar__(buf, stream, ' '); ++cnt; ++minlen; }
break;
case 'i':
case 'd':
pc = &s[sizeof s - 1];
*pc = '\0';
len = 0;
n = *pp++;
sign = 1 - 2 * (n < 0);
do
{
*--pc = '0' + (n - n / 10 * 10) * sign;
n = n / 10;
++len;
} while (n);
if (sign < 0)
{
*--pc = '-';
++len;
}
else if (msign > 0)
{
*--pc = '+';
++len;
msign = 0;
}
while (minlen > len) { __putchar__(buf, stream, leadchar); ++cnt; --minlen; }
while (*pc != '\0')
{
__putchar__(buf, stream, *pc++);
++cnt;
}
while (-minlen > len) { __putchar__(buf, stream, ' '); ++cnt; ++minlen; }
break;
case 'u':
pc = &s[sizeof s - 1];
*pc = '\0';
len = 0;
n = *pp++;
do
{
unsigned nn = n;
*--pc = '0' + nn % 10;
n = nn / 10;
++len;
} while (n);
if (msign > 0)
{
*--pc = '+';
++len;
msign = 0;
}
while (minlen > len) { __putchar__(buf, stream, leadchar); ++cnt; --minlen; }
while (*pc != '\0')
{
__putchar__(buf, stream, *pc++);
++cnt;
}
while (-minlen > len) { __putchar__(buf, stream, ' '); ++cnt; ++minlen; }
break;
case 'X':
phex = "0123456789ABCDEF";
// fallthrough
case 'p':
case 'x':
pc = &s[sizeof s - 1];
*pc = '\0';
len = 0;
n = *pp++;
do
{
*--pc = phex[n & 0xF];
n = (n >> 4) & ((1 << (8 * sizeof n - 4)) - 1); // drop sign-extended bits
++len;
} while (n);
while (minlen > len) { __putchar__(buf, stream, leadchar); ++cnt; --minlen; }
while (*pc != '\0')
{
__putchar__(buf, stream, *pc++);
++cnt;
}
while (-minlen > len) { __putchar__(buf, stream, ' '); ++cnt; ++minlen; }
break;
case 'o':
pc = &s[sizeof s - 1];
*pc = '\0';
len = 0;
n = *pp++;
do
{
*--pc = '0' + (n & 7);
n = (n >> 3) & ((1 << (8 * sizeof n - 3)) - 1); // drop sign-extended bits
++len;
} while (n);
while (minlen > len) { __putchar__(buf, stream, leadchar); ++cnt; --minlen; }
while (*pc != '\0')
{
__putchar__(buf, stream, *pc++);
++cnt;
}
while (-minlen > len) { __putchar__(buf, stream, ' '); ++cnt; ++minlen; }
break;
default:
return -1;
}
}
return cnt;
}
int vprintf(char* fmt, void* vl)
{
return __vsprintf__(NULL, NULL, fmt, vl);
}
int printf(char* fmt, ...)
{
void** pp = (void**)&fmt;
return vprintf(fmt, pp + 1);
}
int vsprintf(char* buf, char* fmt, void* vl)
{
return __vsprintf__(&buf, NULL, fmt, vl);
}
int sprintf(char* buf, char* fmt, ...)
{
void** pp = (void**)&fmt;
return vsprintf(buf, fmt, pp + 1);
}
#ifdef _DOS
void pokeb(unsigned seg, unsigned ofs, unsigned char val)
{
#ifndef __SMALLER_C_16__
*((unsigned char*)(seg * 16 + ofs)) = val;
#else
asm("push ds\n"
"mov ds, [bp + 4]\n"
"mov bx, [bp + 6]\n"
"mov al, [bp + 8]\n"
"mov [bx], al\n"
"pop ds");
#endif
}
unsigned char peekb(unsigned seg, unsigned ofs)
{
#ifndef __SMALLER_C_16__
return *((unsigned char*)(seg * 16 + ofs));
#else
asm("push ds\n"
"mov ds, [bp + 4]\n"
"mov bx, [bp + 6]\n"
"mov al, [bx]\n"
"mov ah, 0\n"
"pop ds");
#endif
}
void poke(unsigned seg, unsigned ofs, unsigned val)
{
#ifndef __SMALLER_C_16__
*((unsigned short*)(seg * 16 + ofs)) = val;
#else
asm("push ds\n"
"mov ds, [bp + 4]\n"
"mov bx, [bp + 6]\n"
"mov ax, [bp + 8]\n"
"mov [bx], ax\n"
"pop ds");
#endif
}
unsigned peek(unsigned seg, unsigned ofs)
{
#ifndef __SMALLER_C_16__
return *((unsigned short*)(seg * 16 + ofs));
#else
asm("push ds\n"
"mov ds, [bp + 4]\n"
"mov bx, [bp + 6]\n"
"mov ax, [bx]\n"
"pop ds");
#endif
}
#endif // _DOS
#ifdef _WIN32
#define INVALID_HANDLE_VALUE (-1)
#define GENERIC_WRITE 0x40000000u
#define GENERIC_READ 0x80000000u
#define FILE_SHARE_READ 1
#define CREATE_ALWAYS 2
#define OPEN_EXISTING 3
#define FILE_ATTRIBUTE_NORMAL 0x80
#define STD_OUTPUT_HANDLE (-11u)
extern void (*_imp__ExitProcess)(unsigned ExitCode);
void ExitProcess(unsigned ExitCode)
{
asm("push dword [ebp+8]\n"
"call [__imp__ExitProcess]");
}
extern char* (*_imp__GetCommandLineA)(void);
char* GetCommandLineA(void)
{
asm("call [__imp__GetCommandLineA]");
}
extern unsigned (*_imp__GetStdHandle)(unsigned nStdHandle);
unsigned GetStdHandle(unsigned nStdHandle)
{
asm("push dword [ebp+8]\n"
"call [__imp__GetStdHandle]");
}
extern unsigned (*_imp__CreateFileA)(char* FileName,
unsigned DesiredAccess,
unsigned ShareMode,
void* SecurityAttributes,
unsigned CreationDisposition,
unsigned FlagsAndAttributes,
unsigned TemplateFile);
unsigned CreateFileA(char* FileName,
unsigned DesiredAccess,
unsigned ShareMode,
void* SecurityAttributes,
unsigned CreationDisposition,
unsigned FlagsAndAttributes,
unsigned TemplateFile)
{
asm("push dword [ebp+32]\n"
"push dword [ebp+28]\n"
"push dword [ebp+24]\n"
"push dword [ebp+20]");
asm("push dword [ebp+16]\n"
"push dword [ebp+12]\n"
"push dword [ebp+8]\n"
"call [__imp__CreateFileA]");
}
extern int (*_imp__CloseHandle)(unsigned Handle);
int CloseHandle(unsigned Handle)
{
asm("push dword [ebp+8]\n"
"call [__imp__CloseHandle]");
}
extern int (*_imp__ReadFile)(unsigned Handle,
void* Buffer,
unsigned NumberOfBytesToRead,
unsigned* NumberOfBytesRead,
void* Overlapped);
int ReadFile(unsigned Handle,
void* Buffer,
unsigned NumberOfBytesToRead,
unsigned* NumberOfBytesRead,
void* Overlapped)
{
asm("push dword [ebp+24]\n"
"push dword [ebp+20]\n"
"push dword [ebp+16]\n"
"push dword [ebp+12]\n"
"push dword [ebp+8]\n"
"call [__imp__ReadFile]");
}
extern int (*_imp__WriteFile)(unsigned Handle,
void* Buffer,
unsigned NumberOfBytesToWrite,
unsigned* NumberOfBytesWritten,
void* Overlapped);
int WriteFile(unsigned Handle,
void* Buffer,
unsigned NumberOfBytesToWrite,
unsigned* NumberOfBytesWritten,
void* Overlapped)
{
asm("push dword [ebp+24]\n"
"push dword [ebp+20]\n"
"push dword [ebp+16]\n"
"push dword [ebp+12]\n"
"push dword [ebp+8]\n"
"call [__imp__WriteFile]");
}
extern unsigned (*_imp__SetFilePointer)(unsigned Handle,
int lDistanceToMove,
int* lpDistanceToMoveHigh,
unsigned dwMoveMethod);
unsigned SetFilePointer(unsigned Handle,
int lDistanceToMove,
int* lpDistanceToMoveHigh,
unsigned dwMoveMethod)
{
asm("push dword [ebp+20]\n"
"push dword [ebp+16]\n"
"push dword [ebp+12]\n"
"push dword [ebp+8]\n"
"call [__imp__SetFilePointer]");
}
#endif // _WIN32
#ifdef _RETROBSD
// flags for RetroBSD's open():
#define O_RDONLY 0x0000
#define O_WRONLY 0x0001
#define O_RDWR 0x0002
#define O_APPEND 0x0008
#define O_CREAT 0x0200
#define O_TRUNC 0x0400
#define O_TEXT 0x0000
#define O_BINARY 0x0000
#endif
int OsCreateOrTruncate(char* name)
{
#ifdef _RETROBSD
int fd;
int oflags = O_TRUNC | O_CREAT | O_WRONLY;
int mode = 0664; // rw-rw-r--
asm volatile ("move $4, %1\n"
"move $5, %2\n"
"move $6, %3\n"
"syscall 5\n" // SYS_open
"nop\n"
"nop\n"
"move %0, $2\n"
: "=r" (fd)
: "r" (name), "r" (oflags), "r" (mode)
: "$2", "$4", "$5", "$6");
if (fd < 0)
fd = -1;
return fd;
#else
#ifdef _LINUX
asm("mov eax, 5\n" // sys_open
"mov ebx, [ebp + 8]\n"
"mov ecx, 0x241\n" // truncate if exists, else create, writing only
"mov edx, 664o\n" // rw-rw-r--
"int 0x80\n"
"mov ebx, eax\n"
"sar ebx, 31\n"
"or eax, ebx");
#else
#ifdef _WIN32
unsigned h = CreateFileA(name,
GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
return h;
#else
#ifndef __SMALLER_C_16__
asm("mov ah, 0x3c\n"
"xor cx, cx\n"
"mov edx, [bp + 8]\n"
"ror edx, 4\n"
"mov ds, dx\n"
"shr edx, 28\n"
"int 0x21\n"
"sbb ebx, ebx\n"
"and eax, 0xffff\n"
"or eax, ebx");
#else
asm("mov ah, 0x3c\n"
"xor cx, cx\n"
"mov dx, [bp + 4]\n"
"int 0x21\n"
"sbb bx, bx\n"
"or ax, bx");
#endif
#endif
#endif
#endif
}
int OsOpen(char* name)
{
#ifdef _RETROBSD
int fd;
int oflags = O_RDONLY;
int mode = 0444; // r--r--r-- (ignored)
asm volatile ("move $4, %1\n"
"move $5, %2\n"
"move $6, %3\n"
"syscall 5\n" // SYS_open
"nop\n"
"nop\n"
"move %0, $2\n"
: "=r" (fd)
: "r" (name), "r" (oflags), "r" (mode)
: "$2", "$4", "$5", "$6");
if (fd < 0)
fd = -1;
return fd;
#else
#ifdef _LINUX
asm("mov eax, 5\n" // sys_open
"mov ebx, [ebp + 8]\n"
"mov ecx, 0\n" // reading only
"mov edx, 444o\n" // r--r--r-- (ignored)
"int 0x80\n"
"mov ebx, eax\n"
"sar ebx, 31\n"
"or eax, ebx");
#else
#ifdef _WIN32
unsigned h = CreateFileA(name,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
return h;
#else
#ifndef __SMALLER_C_16__
asm("mov ax, 0x3d00\n"
"mov edx, [bp + 8]\n"
"ror edx, 4\n"
"mov ds, dx\n"
"shr edx, 28\n"
"int 0x21\n"
"sbb ebx, ebx\n"
"and eax, 0xffff\n"
"or eax, ebx");
#else
asm("mov ax, 0x3d00\n"
"mov dx, [bp + 4]\n"
"int 0x21\n"
"sbb bx, bx\n"
"or ax, bx");
#endif
#endif
#endif
#endif
}
int OsClose(int fd)
{
#ifdef _RETROBSD
int err;
asm volatile ("move $4, %1\n"
"syscall 6\n" // SYS_close
"nop\n"
"nop\n"
"move %0, $2\n"
: "=r" (err)
: "r" (fd)
: "$2", "$4");
return err;
#else
#ifdef _LINUX
asm("mov eax, 6\n" // sys_close
"mov ebx, [ebp + 8]\n"
"int 0x80");
#else
#ifdef _WIN32
return CloseHandle(fd) ? 0 : -1;
#else
#ifndef __SMALLER_C_16__
asm("mov ah, 0x3e\n"
"mov bx, [bp + 8]\n"
"int 0x21\n"
"sbb eax, eax");
#else
asm("mov ah, 0x3e\n"
"mov bx, [bp + 4]\n"
"int 0x21\n"
"sbb ax, ax");
#endif
#endif
#endif
#endif
}
int OsRead(int fd, void* p, unsigned s)
{
#ifdef _RETROBSD
int sz;
asm volatile ("move $4, %1\n"
"move $5, %2\n"
"move $6, %3\n"
"syscall 3\n" // SYS_read
"nop\n"
"nop\n"
"move %0, $2\n"
: "=r" (sz)
: "r" (fd), "r" (p), "r" (s)
: "$2", "$4", "$5", "$6", "memory");
return sz;
#else
#ifdef _LINUX
asm("mov eax, 3\n" // sys_read
"mov ebx, [ebp + 8]\n"
"mov ecx, [ebp + 12]\n"
"mov edx, [ebp + 16]\n"
"int 0x80");
#else
#ifdef _WIN32
unsigned NumberOfBytesRead;
ReadFile(fd,
p,
s,
&NumberOfBytesRead,
NULL);
return NumberOfBytesRead;
#else
#ifndef __SMALLER_C_16__
asm("mov ah, 0x3f\n"
"mov bx, [bp + 8]\n"
"mov edx, [bp + 12]\n"
"ror edx, 4\n"
"mov ds, dx\n"
"shr edx, 28\n"
"mov cx, [bp + 16]\n"
"int 0x21");
asm("sbb ebx, ebx\n"
"and eax, 0xffff\n"
"or eax, ebx");
#else
asm("mov ah, 0x3f\n"
"mov bx, [bp + 4]\n"
"mov dx, [bp + 6]\n"
"mov cx, [bp + 8]\n"
"int 0x21\n"
"sbb bx, bx\n"
"or ax, bx");
#endif
#endif
#endif
#endif
}
int OsWrite(int fd, void* p, unsigned s)
{
#ifdef _RETROBSD
int sz;
asm volatile ("move $4, %1\n"
"move $5, %2\n"
"move $6, %3\n"
"syscall 4\n" // SYS_write
"nop\n"
"nop\n"
"move %0, $2\n"
: "=r" (sz)
: "r" (fd), "r" (p), "r" (s)
: "$2", "$4", "$5", "$6", "memory"); // WTF? I shouldn't need "memory" here !!!
return sz;
#else
#ifdef _LINUX
asm("mov eax, 4\n" // sys_write
"mov ebx, [ebp + 8]\n"
"mov ecx, [ebp + 12]\n"
"mov edx, [ebp + 16]\n"
"int 0x80");
#else
#ifdef _WIN32
unsigned NumberOfBytesWritten;
WriteFile(fd,
p,
s,
&NumberOfBytesWritten,
NULL);
return NumberOfBytesWritten;
#else
#ifndef __SMALLER_C_16__
asm("mov cx, [bp + 16]\n"
"test cx, cx\n"
"jnz DosWriteCont\n"
"xor eax, eax\n"
"jmp DosWriteEnd");
asm("DosWriteCont:\n"
"mov ah, 0x40\n"
"mov bx, [bp + 8]\n"
"mov edx, [bp + 12]\n"
"ror edx, 4\n"
"mov ds, dx\n"
"shr edx, 28\n"
"int 0x21");
asm("sbb ebx, ebx\n"
"and eax, 0xffff\n"
"or eax, ebx\n"
"DosWriteEnd:");
#else
asm("mov cx, [bp + 8]\n"
"test cx, cx\n"
"jnz DosWriteCont\n"
"xor ax, ax\n"
"jmp DosWriteEnd");
asm("DosWriteCont:\n"
"mov ah, 0x40\n"
"mov bx, [bp + 4]\n"
"mov dx, [bp + 6]\n"
"int 0x21\n"
"sbb bx, bx\n"
"or ax, bx\n"
"DosWriteEnd:");
#endif
#endif
#endif
#endif
}
#ifndef __SMALLER_C_16__
long OsLseek(int fd, long ofs, int whence)
{
#ifdef _RETROBSD
long pos;
asm volatile ("move $4, %1\n"
"move $5, %2\n"
"move $6, %3\n"
"syscall 19\n" // SYS_lseek
"nop\n"
"nop\n"
"move %0, $2\n"
: "=r" (pos)
: "r" (fd), "r" (ofs), "r" (whence)
: "$2", "$4", "$5", "$6");
return pos;
#else
#ifdef _LINUX
asm("mov eax, 19\n" // sys_lseek
"mov ebx, [ebp + 8]\n"
"mov ecx, [ebp + 12]\n"
"mov edx, [ebp + 16]\n"
"int 0x80");
#else
#ifdef _WIN32
return SetFilePointer(fd, ofs, 0, whence);
#else
asm("mov ah, 0x42\n"
"mov bx, [bp + 8]\n"
"mov dx, [bp + 12]\n"
"mov cx, [bp + 12 + 2]\n"
"mov al, [bp + 16]\n"
"int 0x21");
asm("sbb ebx, ebx\n"
"and eax, 0xffff\n"
"shl edx, 16\n"
"or eax, edx\n"
"or eax, ebx");
#endif
#endif
#endif
}
#else
int OsLseek16(int fd, unsigned short ofs[2], int whence)
{
asm("mov ah, 0x42\n"
"mov bx, [bp + 4]\n"
"mov si, [bp + 6]");
asm("mov dx, [si]\n"
"mov cx, [si + 2]\n"
"mov al, [bp + 8]\n"
"int 0x21");
asm("sbb bx, bx\n"
"or ax, bx\n"
"or dx, bx\n"
"mov [si], ax\n"
"mov [si + 2], dx\n"
"mov ax, bx");
}
#endif
#ifdef _DOS
unsigned DosGetPspSeg(void)
{
#ifndef __SMALLER_C_16__
asm("mov ah, 0x51\n"
"int 0x21\n"
"movzx eax, bx");
#else
asm("mov ah, 0x51\n"
"int 0x21\n"
"mov ax, bx");
#endif
}
#endif // _DOS
void exit(int e)
{
#ifdef _RETROBSD
asm volatile ("move $4, %0\n"
"syscall 1" // SYS_exit
:
: "r" (e)
: "$2", "$4");
__builtin_unreachable();
#else
#ifdef _LINUX
asm("mov eax, 1\n"
"mov ebx, [ebp + 8]\n"
"int 0x80");
#else
#ifdef _WIN32
ExitProcess(e);
#else
#ifndef __SMALLER_C_16__
asm("mov ah, 0x4c\n"
"mov al, [bp + 8]\n"
"int 0x21");
#else
asm("mov ah, 0x4c\n"
"mov al, [bp + 4]\n"
"int 0x21");
#endif
#endif
#endif
#endif
}
#ifdef _DOS
char __ProgName__[128];
char __ProgParams__[128];
int __argc__ = 1;
char* __argv__[64] = { "" };
#endif
#ifdef _WIN32
char __ProgParams__[1024];
int __argc__ = 1;
char* __argv__[512] = { "" };
#endif
int __StdOutHandle__ = 1; // 1 for DOS/Linux/RetroBSD, GetStdHandle(STD_OUTPUT_HANDLE) for Windows
#ifdef _DOS
void __setargs__(int* pargc, char*** pargv)
{
unsigned psp = DosGetPspSeg();
unsigned env = peek(psp, 0x2c);
unsigned i, j, len;
// First, try to extract the full program name.
// Skip past the environment strings.
i = 0;
for (;;)
{
if ((peekb(env, i) | peekb(env, i + 1)) == 0)
{
i += 2;
break;
}
++i;
}
// Are there any other strings afterwards?
if (peekb(env, i) | peekb(env, i + 1))
{
j = 0;
i += 2;
// The first one is the full program name.
while ((__ProgName__[j++] = peekb(env, i++)) != 0);
__argv__[0] = __ProgName__;
}
// Next, extract program arguments from the PSP.
j = i = 0;
len = peekb(psp, 0x80);
while (i < len)
{
char c;
if ((c = peekb(psp, 0x81 + i)) == ' ')
{
++i;
continue;
}
__argv__[__argc__++] = __ProgParams__ + j;
__ProgParams__[j++] = c;
++i;
while (i < len)
{
if ((c = peekb(psp, 0x81 + i)) == ' ')
break;
__ProgParams__[j++] = c;
++i;
}
__ProgParams__[j++] = 0;
}
*pargc = __argc__;
*pargv = __argv__;
}
#endif
#ifdef _WIN32
void __setargs__(int* pargc, char*** pargv)
{
unsigned i, j, len;
char* p = GetCommandLineA();
j = i = 0;
len = strlen(p);
__argc__ = 0;
while (i < len)
{
char c;
if ((c = p[i]) == ' ')
{
++i;
continue;
}
__argv__[__argc__++] = __ProgParams__ + j;
__ProgParams__[j++] = c;
++i;
while (i < len)
{
if ((c = p[i]) == ' ')
break;
__ProgParams__[j++] = c;
++i;
}
__ProgParams__[j++] = 0;
}
*pargc = __argc__;
*pargv = __argv__;
__StdOutHandle__ = GetStdHandle(STD_OUTPUT_HANDLE);
}
#endif
// TBD??? Not sure if this limited buffering actually improves performance.
#ifndef NO_PREPROCESSOR
#define MAX_FILES 10
#else
#define MAX_FILES 2
#endif
#define FILE_BUF_SIZE (512*1)
unsigned __FileCnt__ = 0;
unsigned __FileHandles__[MAX_FILES];
unsigned char __FileBufs__[MAX_FILES][FILE_BUF_SIZE];
char __FileBufDirty__[MAX_FILES];
unsigned __FileBufSize__[MAX_FILES];
unsigned __FileBufPos__[MAX_FILES];
FILE* fopen(char* name, char* mode)
{
FILE* f = NULL;
unsigned i, fd = -1;
if (strchr(mode, 'a') || strchr(mode, '+') || strchr(mode, 'b'))
return f;
if (__FileCnt__ >= MAX_FILES)
return f;
if (strchr(mode, 'r'))
{
fd = OsOpen(name);
}
else if (strchr(mode, 'w'))
{
fd = OsCreateOrTruncate(name);
}
if (fd != (unsigned)-1)
{
for (i = 0; i < MAX_FILES; ++i)
if (!__FileHandles__[i])
{
__FileHandles__[i] = fd;
__FileBufDirty__[i] = 0;
__FileBufSize__[i] = 0;
__FileBufPos__[i] = 0;
++__FileCnt__;
f = (FILE*)(i + 1);
break;
}
}
return f;
}
int putchar(int c)
{
unsigned char ch = c;
#ifndef _RETROBSD
#ifndef _LINUX
if (c == '\n')
{
if (putchar('\r') == EOF)
return EOF;
}
#endif
#endif
if (OsWrite(__StdOutHandle__, &ch, 1) != 1)
return EOF;
return ch;
}
int puts(char *s)
{
int c;
while ((c = *s++) != '\0')
if (putchar(c) == EOF)
return EOF;
return putchar('\n');
}
int fputs(char* s, FILE* stream)
{
int c;
while ((c = *s++) != '\0')
if (fputc(c, stream) == EOF)
return EOF;
return 0;
}
int vfprintf(FILE* stream, char* fmt, void* vl)
{
return __vsprintf__(NULL, stream, fmt, vl);
}
int fprintf(FILE* stream, char* fmt, ...)
{
void** pp = (void**)&fmt;
return vfprintf(stream, fmt, pp + 1);
}
int fgetc(FILE* stream)
{
unsigned char ch;
unsigned i = (unsigned)stream, fd, pos;
fd = __FileHandles__[--i];
if ((pos = __FileBufPos__[i]) >= __FileBufSize__[i])
{
unsigned sz;
sz = OsRead(fd, __FileBufs__[i], FILE_BUF_SIZE);
if (!sz || sz == (unsigned)-1)
return EOF;
__FileBufSize__[i] = sz;
pos = 0;
}
ch = __FileBufs__[i][pos++];
__FileBufPos__[i] = pos;
return ch;
}
int fputc(int c, FILE* stream)
{
unsigned char ch = c;
unsigned i = (unsigned)stream, fd, pos;
#ifndef _RETROBSD
#ifndef _LINUX
if (c == '\n')
{
if (fputc('\r', stream) == EOF)
return EOF;
}
#endif
#endif
fd = __FileHandles__[--i];
__FileBufDirty__[i] = 1;
pos = __FileBufPos__[i];
__FileBufs__[i][pos++] = ch;
if (pos >= FILE_BUF_SIZE)
{
if (OsWrite(fd, __FileBufs__[i], FILE_BUF_SIZE) != FILE_BUF_SIZE)
return EOF;
__FileBufDirty__[i] = 0;
__FileBufSize__[i] = -1;
__FileBufPos__[i] = -1;
}
++__FileBufSize__[i];
++__FileBufPos__[i];
return ch;
}
int fclose(FILE* stream)
{
unsigned i = (unsigned)stream, fd;
fd = __FileHandles__[--i];
if (__FileBufDirty__[i])
{
unsigned sz = __FileBufSize__[i];
if ((unsigned)OsWrite(fd, __FileBufs__[i], sz) != sz)
{
__FileHandles__[i] = 0;
--__FileCnt__;
OsClose(fd);
return EOF;
}
}
__FileHandles__[i] = 0;
--__FileCnt__;
return OsClose(fd);
}
struct fpos_t_
{
union
{
unsigned short halves[2]; // for 16-bit memory models without 32-bit longs
int align; // for alignment on machine word boundary
} u;
}; // keep in sync with stdio.h !!!
#define fpos_t struct fpos_t_
// Note, these fgetpos() and fsetpos() are implemented for write-only files
int fgetpos(FILE* stream, fpos_t* pos)
{
unsigned i = (unsigned)stream, fd;
fd = __FileHandles__[--i];
#ifndef __SMALLER_C_16__
{
long p;
if ((p = OsLseek(fd, 0, 1/*SEEK_CUR*/)) < 0)
return -1;
p += __FileBufPos__[i];
pos->u.align = p;
}
#else
{
unsigned short p[2];
p[0] = p[1] = 0;
if (OsLseek16(fd, p, 1/*SEEK_CUR*/) < 0)
return -1;
p[1] += __FileBufPos__[i] > 0xFFFF - p[0];
p[0] += __FileBufPos__[i];
pos->u.halves[0] = p[0];
pos->u.halves[1] = p[1];
}
#endif
return 0;
}
int fsetpos(FILE* stream, fpos_t* pos)
{
unsigned i = (unsigned)stream, fd;
unsigned sz;
fd = __FileHandles__[--i];
// flush
sz = __FileBufPos__[i];
if (sz)
{
if ((unsigned)OsWrite(fd, __FileBufs__[i], sz) != sz)
return -1;
}
// seek
#ifndef __SMALLER_C_16__
{
long p = pos->u.align;
if ((p = OsLseek(fd, p, 0/*SEEK_SET*/)) < 0)
return -1;
}
#else
{
if (OsLseek16(fd, pos->u.halves, 0/*SEEK_SET*/) < 0)
return -1;
}
#endif
__FileBufDirty__[i] = 0;
__FileBufSize__[i] = __FileBufPos__[i] = 0;
return 0;
}
#ifdef __SMALLER_C_32__
#ifndef _RETROBSD
#ifdef __HUGE__
#define xbp "bp"
#define xsp "sp"
#else
#define xbp "ebp"
#define xsp "esp"
#endif
void __x87init(void)
{
// Mask all exceptions, set rounding to nearest even and precision to 64 bits.
// TBD??? Actually check for x87???
asm("fninit");
}
float __floatsisf(int i)
{
asm
(
"fild dword ["xbp"+8]\n"
"fstp dword ["xbp"+8]\n"
"mov eax, ["xbp"+8]"
);
}
float __floatunsisf(unsigned i)
{
asm
(
"push dword 0\n"
"push dword ["xbp"+8]\n"
"fild qword ["xbp"-8]\n" // load 32-bit unsigned int as 64-bit signed int
"add "xsp", 8\n"
"fstp dword ["xbp"+8]\n"
"mov eax, ["xbp"+8]"
);
}
int __fixsfsi(float f)
{
asm
(
"sub "xsp", 4\n"
"fnstcw ["xbp"-2]\n" // save rounding
"mov ax, ["xbp"-2]\n"
"mov ah, 0x0c\n" // rounding towards 0 (AKA truncate) since we don't have fisttp
"mov ["xbp"-4], ax\n"
"fld dword ["xbp"+8]\n"
"fldcw ["xbp"-4]\n" // rounding = truncation
"fistp dword ["xbp"+8]\n"
"fldcw ["xbp"-2]\n" // restore rounding
"add "xsp", 4\n"
"mov eax, ["xbp"+8]"
);
}
unsigned __fixunssfsi(float f)
{
asm
(
"sub "xsp", 12\n"
"fnstcw ["xbp"-2]\n" // save rounding
"mov ax, ["xbp"-2]\n"
"mov ah, 0x0c\n" // rounding towards 0 (AKA truncate) since we don't have fisttp
"mov ["xbp"-4], ax\n"
"fld dword ["xbp"+8]\n"
"fldcw ["xbp"-4]\n" // rounding = truncation
"fistp qword ["xbp"-12]\n" // store 64-bit signed int
"fldcw ["xbp"-2]\n" // restore rounding
"mov eax, ["xbp"-12]\n" // take low 32 bits
"add "xsp", 12"
);
}
float __addsf3(float a, float b)
{
asm
(
"fld dword ["xbp"+8]\n"
"fadd dword ["xbp"+12]\n"
"fstp dword ["xbp"+8]\n"
"mov eax, ["xbp"+8]"
);
}
float __subsf3(float a, float b)
{
asm
(
"fld dword ["xbp"+8]\n"
"fsub dword ["xbp"+12]\n"
"fstp dword ["xbp"+8]\n"
"mov eax, ["xbp"+8]"
);
}
float __negsf2(float f)
{
asm
(
"mov eax, ["xbp"+8]\n"
"xor eax, 0x80000000"
);
}
float __mulsf3(float a, float b)
{
asm
(
"fld dword ["xbp"+8]\n"
"fmul dword ["xbp"+12]\n"
"fstp dword ["xbp"+8]\n"
"mov eax, ["xbp"+8]"
);
}
float __divsf3(float a, float b)
{
asm
(
"fld dword ["xbp"+8]\n"
"fdiv dword ["xbp"+12]\n"
"fstp dword ["xbp"+8]\n"
"mov eax, ["xbp"+8]"
);
}
float __lesf2(float a, float b)
{
asm
(
"fld dword ["xbp"+12]\n"
"fld dword ["xbp"+8]\n"
"fucompp\n"
"fstsw ax\n" // must use these moves since we don't have fucomip
"sahf\n"
"jnp .ordered\n"
"mov eax, +1\n" // return +1 if either a or b (or both) is a NaN
"jmp .done\n"
".ordered:\n"
"jnz .unequal\n"
"xor eax, eax\n" // return 0 if a == b
"jmp .done\n"
".unequal:\n"
"sbb eax, eax\n"
"jc .done\n" // return -1 if a < b
"inc eax\n" // return +1 if a > b
".done:"
);
}
float __gesf2(float a, float b)
{
asm
(
"fld dword ["xbp"+12]\n"
"fld dword ["xbp"+8]\n"
"fucompp\n"
"fstsw ax\n" // must use these moves since we don't have fucomip
"sahf\n"
"jnp .ordered\n"
"mov eax, -1\n" // return -1 if either a or b (or both) is a NaN
"jmp .done\n"
".ordered:\n"
"jnz .unequal\n"
"xor eax, eax\n" // return 0 if a == b
"jmp .done\n"
".unequal:\n"
"sbb eax, eax\n"
"jc .done\n" // return -1 if a < b
"inc eax\n" // return +1 if a > b
".done:"
);
}
#endif
#endif
#endif /*__APPLE__*/