Got a basic WebAssembly module compiling with support for calling jsStdPrint through printf implementation (provided by stb_sprintf.h). We have a single function "HelloFromWasm" exported for testing purposes. Pulled in a bunch of standard library headers from PigCore's wasm layer.

This commit is contained in:
Taylor Robbins (Piggybank Studios)
2025-08-28 09:44:32 -07:00
parent aa2faca658
commit deae3ccd12
20 changed files with 2882 additions and 85 deletions

85
.gitignore vendored
View File

@@ -1,87 +1,4 @@
# ---> C
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
# ---> SublimeText
# Cache files for Sublime Text
*.tmlanguage.cache
*.tmPreferences.cache
*.stTheme.cache
build/
# Workspace files are user-specific
*.sublime-workspace
# Project files should be checked into the repository, unless a significant
# proportion of contributors will probably not be using Sublime Text
# *.sublime-project
# SFTP configuration file
sftp-config.json
sftp-config-alt*.json
# Package control specific files
Package Control.last-run
Package Control.ca-list
Package Control.ca-bundle
Package Control.system-ca-bundle
Package Control.cache/
Package Control.ca-certs/
Package Control.merged-ca-bundle
Package Control.user-ca-bundle
oscrypto-ca-bundle.crt
bh_unicode_properties.cache
# Sublime-github package stores a github token in this file
# https://packagecontrol.io/packages/sublime-github
GitHub.sublime-settings

14
CWasm.sublime-project Normal file
View File

@@ -0,0 +1,14 @@
{
"folders":
[
{
"path": ".",
"file_exclude_patterns": [
"*.o",
],
"folder_exclude_patterns": [
"build",
],
}
]
}

View File

@@ -1,3 +1,11 @@
# CWasm
A minimal layer for compiling C applications that render graphics through WebGL from a WebAssembly module using Clang (not Emscripten)
# Compilation
CWasm is compiled with the applicaiton as a [Unity Build](https://en.wikipedia.org/wiki/Unity_build). The application's main source file must #include cwasm.c before anything else. The code in this repository is only tested with Clang as the compiler and `wasm32-unknown-unknown` as the target architecture. You must have a version of Clang that supports compiling to WebAssembly.
1. Install Clang (LLVM's Compiler) from [LLVM's Download Page](https://releases.llvm.org/download.html)
2. Make sure Clang is available from the command-line by doing `clang -v`. If it's not, make sure you update your `PATH` environment variable to include the `bin/` folder of the LLVM install directory
3. Ensure you have **Clang 11.0** or greater for proper WebAssembly support (we recommend using the latest, that's **v18.1.8** as this is written)
4. On Windows run the `build.bat [path/to/app.c]`. On Linux or OSX run the `build.sh [path/to/app.c]`. If compiling CWasm without a real application the app.c argument may be ommitted and test_app.c will be used instead.

41
build.bat Normal file
View File

@@ -0,0 +1,41 @@
@echo off
setlocal enabledelayedexpansion
set app_source_path=..\test_app.c
echo %1 | findstr /r "^[A-Za-z]:" >nul
if not "%1"=="" (
if %errorlevel% equ 0 (
set app_source_path=%1
) else (
set app_source_path=..\%1
)
)
set app_module_name=app.wasm
if not "%2"=="" (
set app_module_name=%2
)
if not exist build mkdir build
pushd build
rem TODO: Take the app.c file path as an argument?
echo [Compiling %app_source_path% to %app_module_name%...]
clang %app_source_path% -o "%app_module_name%" -I ".." -I "..\std" -std=gnu2x --target=wasm32-unknown-unknown -mbulk-memory -Wl,--no-entry,--export-dynamic,--allow-undefined,--export=__heap_base --no-standard-libraries --no-standard-includes
if %ERRORLEVEL% NEQ 0 (
echo [FAILED to build %app_module_name%!]
goto :build_end
) else (
echo [Done!]
)
rem Convert .wasm to .wat (the text format for WebAssembly) for debug inspection
set "app_module_wat_name=%app_module_name:.wasm=.wat%"
if "%app_module_wat_name%" == "%app_module_name%" (
set app_module_wat_name=%app_module_name%.wat
)
echo [Converting %app_module_name% to %app_module_wat_name%...]
wasm2wat "%app_module_name%" > "%app_module_wat_name%"
:build_end
popd

17
cwasm.c Normal file
View File

@@ -0,0 +1,17 @@
/*
File: main.c
Author: Taylor Robbins
Date: 08\28\2025
Description:
** This file serves as the compilable file for all of CWasm. It may be #included by the app.c file
*/
#include "cwasm.h"
#include "std/src/std_main.c"
WASM_EXPORT(HelloFromWasm) int HelloFromWasm(int value)
{
printf("Called HelloFromWasm(%d)!\n", value);
return value*(value+1)*2;
}

91
cwasm.h Normal file
View File

@@ -0,0 +1,91 @@
/*
File: cwasm.h
Author: Taylor Robbins
Date: 08\28\2025
Description:
** This header should be included before any other header and at the top of every compilation unit.
** It in turn includes any files that are needed for a CWasm-based application to function
*/
#ifndef _CWASM_H
#define _CWASM_H
// +--------------------------------------------------------------+
// | Check Compiler |
// +--------------------------------------------------------------+
#if !defined(__clang__)
#error CWasm is only tested to function with Clang as the compiler!
#endif
#if !defined(__clang_major__) || !defined(__clang_minor__) || !defined(__clang_patchlevel__)
#error Missing defines for __clang_major__, __clang_minor__, and __clang_patchlevel__!
#endif
#if !defined(__wasm32__)
#error CWasm only works when compiling for 32-bit WebAssembly!
#endif
#if defined(__EMSCRIPTEN__)
#error CWasm is not meant to be compiled with Emscripten. It functions as a standalone platform for C applications that only depend on a small subset of the C standard library
#endif
#ifndef __BYTE_ORDER__
#error Missing __BYTE_ORDER__ define!
#elif __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
#error This standard library implementation assumes little-endian byte order
#endif
#ifdef __cplusplus
#define LANGUAGE_IS_C 0
#define LANGUAGE_IS_CPP 1
#else
#define LANGUAGE_IS_C 1
#define LANGUAGE_IS_CPP 0
#endif
#if LANGUAGE_IS_C
#define nullptr ((void*)0)
#endif
#define START_EXTERN_C extern "C" {
#define END_EXTERN_C }
#if LANGUAGE_IS_CPP
#define MAYBE_EXTERN_C EXTERN_C
#define MAYBE_START_EXTERN_C START_EXTERN_C
#define MAYBE_END_EXTERN_C END_EXTERN_C
#else
#define MAYBE_EXTERN_C //nothing
#define MAYBE_START_EXTERN_C //nothing
#define MAYBE_END_EXTERN_C //nothing
#endif
#define WASM_MEMORY_PAGE_SIZE (64*1024ULL) //64kB or 65,536b
#define WASM_MEMORY_MAX_NUM_PAGES (64*1024ULL) //65,536 pages * 64 kB/page = 4GB
#define WASM_MEMORY_MAX_SIZE ((u64)WASM_MEMORY_MAX_NUM_PAGES * (u64)WASM_MEMORY_PAGE_SIZE)
#define WASM_PROTECTED_SIZE 1024 //1kB at start of wasm memory should be unused and should never be written to
#define WASM_EXPORT(functionName) MAYBE_EXTERN_C __attribute__((export_name(#functionName)))
// +--------------------------------------------------------------+
// | Standard Includes |
// +--------------------------------------------------------------+
// NOTE: These headers are all contained in CWasm's "std" folder, they are not "real" C standard library headers
#include <stdint.h>
#include <string.h>
// +--------------------------------------------------------------+
// | Basic Type Names |
// +--------------------------------------------------------------+
// We use an "i" to indicate it can hold integer numbers
typedef int8_t i8;
typedef int16_t i16;
typedef int32_t i32;
typedef long long i64;
// We use a "u" to distinguish these as only holding unsigned numbers (they are still integers, but "i" is already taken)
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef unsigned long long u64;
// We use an "r" to indicate it can hold real numbers
typedef float r32;
typedef double r64;
#endif // _CWASM_H

14
std/assert.h Normal file
View File

@@ -0,0 +1,14 @@
/*
File: assert.h
Author: Taylor Robbins
Date: 08\28\2025
*/
#ifndef _ASSERT_H
#define _ASSERT_H
#define assert(condition) do { if (!(condition)) { jsStdAssertFailure(__FILE__, __LINE__, __func__, #condition, nullptr); } } while(0)
//NOTE: assert_msg is not a standard function but we want to be able to pass a message to jsStdAssertFailure so we added this variant
#define assert_msg(condition, message) do { if (!(condition)) { jsStdAssertFailure(__FILE__, __LINE__, __func__, #condition, (message)); } } while(0)
#endif // _ASSERT_H

32
std/limits.h Normal file
View File

@@ -0,0 +1,32 @@
/*
File: limits.h
Author: Taylor Robbins
Date: 08\28\2025
*/
#ifndef _LIMITS_H
#define _LIMITS_H
#define CHAR_MIN (-128)
#define CHAR_MAX 127
#define CHAR_BIT 8
#define SCHAR_MIN (-128)
#define SCHAR_MAX 127
#define UCHAR_MAX 255
#define SHRT_MIN (-1-0x7fff)
#define SHRT_MAX 0x7fff
#define USHRT_MAX 0xffff
#define INT_MIN (-1-0x7fffffff)
#define INT_MAX 0x7fffffff
#define UINT_MAX 0xffffffffU
#define LONG_MIN (-LONG_MAX-1)
#define LONG_MAX __LONG_MAX
#define ULONG_MAX (2UL*LONG_MAX+1)
#define LLONG_MIN (-LLONG_MAX-1)
#define LLONG_MAX 0x7fffffffffffffffLL
#define ULLONG_MAX (2ULL*LLONG_MAX+1)
//TODO: Define PAGESIZE?
#endif // _LIMITS_H

1906
std/src/stb_sprintf.h Normal file

File diff suppressed because it is too large Load Diff

18
std/src/std_js_imports.h Normal file
View File

@@ -0,0 +1,18 @@
/*
File: std_js_imports.h
Author: Taylor Robbins
Date: 08\28\2025
Description:
** Holds the declarations for functions that we expect to be imported by the Javascript glue code and imported to the module when it's loaded
*/
#ifndef _STD_JS_IMPORTS_H
#define _STD_JS_IMPORTS_H
MAYBE_EXTERN_C void jsStdPrint(const char* messageStrPntr, int messageLength);
MAYBE_EXTERN_C _Noreturn void jsStdAbort(const char* messageStrPntr, int exitCode);
MAYBE_EXTERN_C _Noreturn void jsStdAssertFailure(const char* filePathPntr, int fileLineNum, const char* funcNamePntr, const char* conditionStrPntr, const char* messageStrPntr);
MAYBE_EXTERN_C void jsStdDebugBreak();
MAYBE_EXTERN_C void jsStdGrowMemory(unsigned int numPages);
#endif // _STD_JS_IMPORTS_H

21
std/src/std_main.c Normal file
View File

@@ -0,0 +1,21 @@
/*
File: std_main.c
Author: Taylor Robbins
Date: 08\28\2025
Description:
** Contains the implementations for all standard library functions that are declared in the headers in the parent folder
*/
#include <limits.h>
#include <stdint.h>
#include <uchar.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <stddef.h>
#include <assert.h>
#include "std_js_imports.h"
#include "std_memset.c"
#include "std_printf.c"

370
std/src/std_memset.c Normal file
View File

@@ -0,0 +1,370 @@
/*
File: std_memset.c
Author: Taylor Robbins
Date: 08\28\2025
Description:
** Holds implementations of functions like memset(), memcpy(), memmove(), etc.
*/
#define WORD_CONTAINS_ZERO(word) ((word)-((size_t)-1/UCHAR_MAX) & ~(word) & (((size_t)-1/UCHAR_MAX) * (UCHAR_MAX/2+1)))
#define MAX(left, right) ((left) > (right) ? (left) : (right))
#define MIN(left, right) ((left) < (right) ? (left) : (right))
typedef size_t __attribute__((__may_alias__)) a_size_t;
#define SIZEOF_A_SIZE_T sizeof(a_size_t)
typedef uint32_t __attribute__((__may_alias__)) a_u32;
typedef uint64_t __attribute__((__may_alias__)) a_u64;
#if !WASM_STD_USE_BUILTIN_MEMSET
void* _memset(void* pntr, int value, size_t numBytes)
{
unsigned char* charPntr = pntr;
size_t alignment;
// Fill head and tail with minimal branching. Each
// conditional ensures that all the subsequently used
// offsets are well-defined and in the pntr region.
if (!numBytes) { return pntr; }
charPntr[0] = value;
charPntr[numBytes-1] = value;
if (numBytes <= 2) { return pntr; }
charPntr[1] = value;
charPntr[2] = value;
charPntr[numBytes-2] = value;
charPntr[numBytes-3] = value;
if (numBytes <= 6) { return pntr; }
charPntr[3] = value;
charPntr[numBytes-4] = value;
if (numBytes <= 8) { return pntr; }
// Advance pointer to align it at a 4-byte boundary,
// and truncate numBytes to a multiple of 4. The previous code
// already took care of any head/tail that get cut off
// by the alignment.
alignment = (-(uintptr_t)charPntr & 3);
charPntr += alignment;
numBytes -= alignment;
numBytes &= -4;
a_u32 value32 = ((a_u32) - 1) / 255 * (unsigned char)value;
// In preparation to copy 32 bytes at a time, aligned on
// an 8-byte bounary, fill head/tail up to 28 bytes each.
// As in the initial byte-based head/tail fill, each
// conditional below ensures that the subsequent offsets
// are valid (e.g. !(numBytes<=24) implies numBytes>=28).
*(a_u32*)(charPntr + 0) = value32;
*(a_u32*)(charPntr + numBytes - 4) = value32;
if (numBytes <= 8) { return pntr; }
*(a_u32*)(charPntr + 4) = value32;
*(a_u32*)(charPntr + 8) = value32;
*(a_u32*)(charPntr + numBytes - 12) = value32;
*(a_u32*)(charPntr + numBytes - 8) = value32;
if (numBytes <= 24) { return pntr; }
*(a_u32*)(charPntr + 12) = value32;
*(a_u32*)(charPntr + 16) = value32;
*(a_u32*)(charPntr + 20) = value32;
*(a_u32*)(charPntr + 24) = value32;
*(a_u32*)(charPntr + numBytes - 28) = value32;
*(a_u32*)(charPntr + numBytes - 24) = value32;
*(a_u32*)(charPntr + numBytes - 20) = value32;
*(a_u32*)(charPntr + numBytes - 16) = value32;
// Align to a multiple of 8 so we can fill 64 bits at a time,
// and avoid writing the same bytes twice as much as is
// practical without introducing additional branching.
alignment = 24 + ((uintptr_t)charPntr & 4);
charPntr += alignment;
numBytes -= alignment;
// If this loop is reached, 28 tail bytes have already been
// filled, so any remainder when numBytes drops below 32 can be
// safely ignored.
a_u64 value64 = (value32 | ((a_u64)value32 << 32));
for (; numBytes >= 32; numBytes -= 32, charPntr += 32)
{
*(a_u64*)(charPntr + 0) = value64;
*(a_u64*)(charPntr + 8) = value64;
*(a_u64*)(charPntr + 16) = value64;
*(a_u64*)(charPntr + 24) = value64;
}
return pntr;
}
#endif //!WASM_STD_USE_BUILTIN_MEMSET
#if !WASM_STD_USE_BUILTIN_MEMCPY
void* _memcpy(void* dest, const void* source, size_t numBytes)
{
unsigned char* destCharPntr = dest;
const unsigned char* sourceCharPntr = source;
uint32_t wVarU32, xVarU32;
for (; (uintptr_t)sourceCharPntr % 4 && numBytes; numBytes--)
{
*destCharPntr++ = *sourceCharPntr++;
}
if ((uintptr_t)destCharPntr % 4 == 0)
{
for (; numBytes >= 16; sourceCharPntr += 16, destCharPntr += 16, numBytes -= 16)
{
*(a_u32*)(destCharPntr + 0) = *(a_u32*)(sourceCharPntr + 0);
*(a_u32*)(destCharPntr + 4) = *(a_u32*)(sourceCharPntr + 4);
*(a_u32*)(destCharPntr + 8) = *(a_u32*)(sourceCharPntr + 8);
*(a_u32*)(destCharPntr + 12) = *(a_u32*)(sourceCharPntr + 12);
}
if (numBytes & 8)
{
*(a_u32*)(destCharPntr + 0) = *(a_u32*)(sourceCharPntr + 0);
*(a_u32*)(destCharPntr + 4) = *(a_u32*)(sourceCharPntr + 4);
destCharPntr += 8;
sourceCharPntr += 8;
}
if (numBytes & 4)
{
*(a_u32*)(destCharPntr+0) = *(a_u32*)(sourceCharPntr + 0);
destCharPntr += 4;
sourceCharPntr += 4;
}
if (numBytes & 2)
{
*destCharPntr++ = *sourceCharPntr++;
*destCharPntr++ = *sourceCharPntr++;
}
if (numBytes & 1)
{
*destCharPntr = *sourceCharPntr;
}
return dest;
}
if (numBytes >= 32)
{
switch ((uintptr_t)destCharPntr % 4)
{
case 1:
{
wVarU32 = *(a_u32*)sourceCharPntr;
*destCharPntr++ = *sourceCharPntr++;
*destCharPntr++ = *sourceCharPntr++;
*destCharPntr++ = *sourceCharPntr++;
numBytes -= 3;
for (; numBytes >= 17; sourceCharPntr += 16, destCharPntr += 16, numBytes -= 16)
{
xVarU32 = *(a_u32*)(sourceCharPntr + 1);
*(a_u32*)(destCharPntr + 0) = ((wVarU32 >> 24) | (xVarU32 << 8));
wVarU32 = *(a_u32*)(sourceCharPntr + 5);
*(a_u32*)(destCharPntr + 4) = ((xVarU32 >> 24) | (wVarU32 << 8));
xVarU32 = *(a_u32*)(sourceCharPntr + 9);
*(a_u32*)(destCharPntr + 8) = ((wVarU32 >> 24) | (xVarU32 << 8));
wVarU32 = *(a_u32*)(sourceCharPntr + 13);
*(a_u32*)(destCharPntr + 12) = ((xVarU32 >> 24) | (wVarU32 << 8));
}
} break;
case 2:
{
wVarU32 = *(a_u32*)sourceCharPntr;
*destCharPntr++ = *sourceCharPntr++;
*destCharPntr++ = *sourceCharPntr++;
numBytes -= 2;
for (; numBytes >= 18; sourceCharPntr += 16, destCharPntr += 16, numBytes -= 16)
{
xVarU32 = *(a_u32*)(sourceCharPntr + 2);
*(a_u32*)(destCharPntr + 0) = ((wVarU32 >> 16) | (xVarU32 << 16));
wVarU32 = *(a_u32*)(sourceCharPntr + 6);
*(a_u32*)(destCharPntr + 4) = ((xVarU32 >> 16) | (wVarU32 << 16));
xVarU32 = *(a_u32*)(sourceCharPntr + 10);
*(a_u32*)(destCharPntr + 8) = ((wVarU32 >> 16) | (xVarU32 << 16));
wVarU32 = *(a_u32*)(sourceCharPntr + 14);
*(a_u32*)(destCharPntr + 12) = ((xVarU32 >> 16) | (wVarU32 << 16));
}
} break;
case 3:
{
wVarU32 = *(a_u32*)sourceCharPntr;
*destCharPntr++ = *sourceCharPntr++;
numBytes -= 1;
for (; numBytes >= 19; sourceCharPntr += 16, destCharPntr += 16, numBytes -= 16)
{
xVarU32 = *(a_u32*)(sourceCharPntr + 3);
*(a_u32*)(destCharPntr + 0) = ((wVarU32 >> 8) | (xVarU32 << 24));
wVarU32 = *(a_u32*)(sourceCharPntr + 7);
*(a_u32*)(destCharPntr + 4) = ((xVarU32 >> 8) | (wVarU32 << 24));
xVarU32 = *(a_u32*)(sourceCharPntr + 11);
*(a_u32*)(destCharPntr + 8) = ((wVarU32 >> 8) | (xVarU32 << 24));
wVarU32 = *(a_u32*)(sourceCharPntr + 15);
*(a_u32*)(destCharPntr + 12) = ((xVarU32 >> 8) | (wVarU32 << 24));
}
} break;
}
}
if (numBytes & 16)
{
*destCharPntr++ = *sourceCharPntr++;
*destCharPntr++ = *sourceCharPntr++;
*destCharPntr++ = *sourceCharPntr++;
*destCharPntr++ = *sourceCharPntr++;
*destCharPntr++ = *sourceCharPntr++;
*destCharPntr++ = *sourceCharPntr++;
*destCharPntr++ = *sourceCharPntr++;
*destCharPntr++ = *sourceCharPntr++;
*destCharPntr++ = *sourceCharPntr++;
*destCharPntr++ = *sourceCharPntr++;
*destCharPntr++ = *sourceCharPntr++;
*destCharPntr++ = *sourceCharPntr++;
*destCharPntr++ = *sourceCharPntr++;
*destCharPntr++ = *sourceCharPntr++;
*destCharPntr++ = *sourceCharPntr++;
*destCharPntr++ = *sourceCharPntr++;
}
if (numBytes & 8)
{
*destCharPntr++ = *sourceCharPntr++;
*destCharPntr++ = *sourceCharPntr++;
*destCharPntr++ = *sourceCharPntr++;
*destCharPntr++ = *sourceCharPntr++;
*destCharPntr++ = *sourceCharPntr++;
*destCharPntr++ = *sourceCharPntr++;
*destCharPntr++ = *sourceCharPntr++;
*destCharPntr++ = *sourceCharPntr++;
}
if (numBytes & 4)
{
*destCharPntr++ = *sourceCharPntr++;
*destCharPntr++ = *sourceCharPntr++;
*destCharPntr++ = *sourceCharPntr++;
*destCharPntr++ = *sourceCharPntr++;
}
if (numBytes & 2)
{
*destCharPntr++ = *sourceCharPntr++;
*destCharPntr++ = *sourceCharPntr++;
}
if (numBytes & 1)
{
*destCharPntr = *sourceCharPntr;
}
return dest;
}
#endif //!WASM_STD_USE_BUILTIN_MEMSET
#if !WASM_STD_USE_BUILTIN_MEMMOVE
void* _memmove(void* dest, const void* source, size_t numBytes)
{
char* destCharPntr = dest;
const char* sourceCharPntr = source;
if (destCharPntr == sourceCharPntr) { return destCharPntr; }
if ((uintptr_t)sourceCharPntr - (uintptr_t)destCharPntr - numBytes <= -2 * numBytes)
{
return memcpy(destCharPntr, sourceCharPntr, numBytes);
}
if (destCharPntr < sourceCharPntr)
{
if ((uintptr_t)sourceCharPntr % SIZEOF_A_SIZE_T == (uintptr_t)destCharPntr % SIZEOF_A_SIZE_T)
{
while ((uintptr_t)destCharPntr % SIZEOF_A_SIZE_T)
{
if (!numBytes--) { return dest; }
*destCharPntr++ = *sourceCharPntr++;
}
for (; numBytes >= SIZEOF_A_SIZE_T; numBytes -= SIZEOF_A_SIZE_T, destCharPntr += SIZEOF_A_SIZE_T, sourceCharPntr += SIZEOF_A_SIZE_T)
{
*(a_size_t*)destCharPntr = *(a_size_t*)sourceCharPntr;
}
}
for (; numBytes; numBytes--) { *destCharPntr++ = *sourceCharPntr++; }
}
else
{
if (((uintptr_t)sourceCharPntr % SIZEOF_A_SIZE_T) == ((uintptr_t)destCharPntr % SIZEOF_A_SIZE_T))
{
while ((uintptr_t)(destCharPntr + numBytes) % SIZEOF_A_SIZE_T)
{
if (!numBytes--) { return dest; }
destCharPntr[numBytes] = sourceCharPntr[numBytes];
}
while (numBytes >= SIZEOF_A_SIZE_T)
{
numBytes -= SIZEOF_A_SIZE_T;
*(a_size_t*)(destCharPntr+numBytes) = *(a_size_t*)(sourceCharPntr + numBytes);
}
}
while (numBytes)
{
numBytes--;
destCharPntr[numBytes] = sourceCharPntr[numBytes];
}
}
return dest;
}
#endif //!WASM_STD_USE_BUILTIN_MEMMOVE
int memcmp(const void* left, const void* right, size_t numBytes)
{
const unsigned char *leftByte = left, *rightByte = right;
for (; (numBytes && *leftByte == *rightByte); numBytes--, leftByte++, rightByte++);
return (numBytes ? *leftByte - *rightByte : 0);
}
int strcmp(const char* left, const char* right)
{
for (; *left == *right && *left; left++, right++);
return (*(unsigned char*)left - *(unsigned char*)right);
}
char* strcpy(char* dest, const char* source)
{
a_size_t* destWord;
const a_size_t* sourceWord;
if ((uintptr_t)source % sizeof(size_t) == (uintptr_t)dest % sizeof(size_t))
{
for (; (uintptr_t)source % sizeof(size_t); source++, dest++)
{
if (!(*dest=*source)) { return dest; }
}
destWord = (void*)dest;
sourceWord = (const void*)source;
for (; !WORD_CONTAINS_ZERO(*sourceWord); *destWord++ = *sourceWord++) { }
dest=(void*)destWord;
source=(const void*)sourceWord;
}
for (; (*dest=*source); source++, dest++) { }
return dest;
}
//TODO: Add strstr implementation!
int strncmp(const char* left, const char* right, size_t numBytes)
{
const unsigned char* leftPntr = (void*)left;
const unsigned char* rightPntr = (void*)right;
if (!numBytes--) { return 0; }
for (; (*leftPntr && *rightPntr && numBytes && *leftPntr == *rightPntr); leftPntr++, rightPntr++, numBytes--);
return *leftPntr - *rightPntr;
}
size_t strlen(const char* str)
{
const char* startPntr = str;
const a_size_t* wordPntr;
//Walk 1 character at a time until we are size_t aligned
for (; ((uintptr_t)str % sizeof(size_t)) != 0; str++)
{
if (!*str) { return str-startPntr; }
}
//Check size_t bytes at once with WORD_CONTAINS_ZERO
for (wordPntr = (const void *)str; !WORD_CONTAINS_ZERO(*wordPntr); wordPntr++) { }
str = (const void *)wordPntr;
//Walk 0-3 bytes until we find the 0 in the word
for (; *str; str++) { }
return (str - startPntr);
}

65
std/src/std_printf.c Normal file
View File

@@ -0,0 +1,65 @@
/*
File: std_printf.c
Author: Taylor Robbins
Date: 08\28\2025
Description:
** None
*/
#define STB_SPRINTF_IMPLEMENTATION
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
#include "stb_sprintf.h"
#pragma clang diagnostic pop
//TODO: Should we increase this buffer size? Or make it an option for the user to change? Or dynamically allocate from the stack? Or something else?
#define STD_PRINTF_BUFFER_SIZE 1024 //chars
char stdGlobalPrintBuffer[STD_PRINTF_BUFFER_SIZE];
int vsnprintf(char* bufferPntr, size_t bufferSize, const char* formatStr, va_list args)
{
assert(bufferSize <= INT_MAX);
return stbsp_vsnprintf(bufferPntr, bufferSize, formatStr, args);
}
int vsprintf(char* restrict bufferPntr, const char* restrict formatStr, va_list args)
{
return vsnprintf(bufferPntr, INT_MAX, formatStr, args);
}
int vprintf(const char* restrict formatStr, va_list args)
{
int result = stbsp_vsnprintf(stdGlobalPrintBuffer, STD_PRINTF_BUFFER_SIZE, formatStr, args);
if (result > 0 && result < STD_PRINTF_BUFFER_SIZE)
{
stdGlobalPrintBuffer[result] = '\0'; //ensure null-termination
jsStdPrint(&stdGlobalPrintBuffer[0], result);
}
return result;
}
int snprintf(char* restrict bufferPntr, size_t bufferSize, const char* restrict formatStr, ...)
{
int result;
va_list args;
va_start(args, formatStr);
result = vsnprintf(bufferPntr, bufferSize, formatStr, args);
va_end(args);
return result;
}
int sprintf(char* restrict bufferPntr, const char* restrict formatStr, ...)
{
int result;
va_list args;
va_start(args, formatStr);
result = vsprintf(bufferPntr, formatStr, args);
va_end(args);
return result;
}
int printf(const char* restrict formatStr, ...)
{
int result;
va_list args;
va_start(args, formatStr);
result = vprintf(formatStr, args);
va_end(args);
return result;
}

18
std/stdarg.h Normal file
View File

@@ -0,0 +1,18 @@
/*
File: stdarg.h
Author: Taylor Robbins
Date: 08\28\2025
*/
#ifndef _STDARG_H
#define _STDARG_H
typedef __builtin_va_list va_list;
typedef __builtin_va_list __isoc_va_list;
#define va_start(v,l) __builtin_va_start(v,l)
#define va_end(v) __builtin_va_end(v)
#define va_arg(v,l) __builtin_va_arg(v,l)
#define va_copy(d,s) __builtin_va_copy(d,s)
#endif // _STDARG_H

20
std/stddef.h Normal file
View File

@@ -0,0 +1,20 @@
/*
File: stddef.h
Author: Taylor Robbins
Date: 08\28\2025
*/
#ifndef _STDDEF_H
#define _STDDEF_H
#include <stdint.h>
#if LANGUAGE_IS_CPP
#define NULL nullptr
#else
#define NULL ((void*)0)
#endif
#define offsetof(type, member) __builtin_offsetof(type, member)
#endif // _STDDEF_H

132
std/stdint.h Normal file
View File

@@ -0,0 +1,132 @@
/*
File: stdint.h
Author: Taylor Robbins
Date: 08\28\2025
*/
#ifndef _STDINT_H
#define _STDINT_H
// +--------------------------------------------------------------+
// | Typedefs |
// +--------------------------------------------------------------+
#define _Addr long
#define _Int64 long long
#define _Reg long
#define __BYTE_ORDER __LITTLE_ENDIAN
#define __LONG_MAX 0x7FFFFFFFFFFFFFFFL
#if LANGUAGE_IS_C
#ifdef __WCHAR_TYPE__
typedef __WCHAR_TYPE__ wchar_t;
#else
typedef long wchar_t;
#endif
#endif
typedef float float_t;
typedef double double_t;
typedef unsigned _Addr size_t;
typedef unsigned _Addr uintptr_t;
typedef _Addr ptrdiff_t;
typedef _Addr ssize_t;
typedef _Addr intptr_t;
// typedef _Addr regoff_t;
// typedef _Reg register_t;
// typedef _Int64 time_t;
// typedef _Int64 suseconds_t;
typedef signed char int8_t;
typedef signed short int16_t;
typedef signed int int32_t;
typedef signed _Int64 int64_t;
typedef signed _Int64 intmax_t;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned _Int64 uint64_t;
typedef unsigned _Int64 u_int64_t;
typedef unsigned _Int64 uintmax_t;
// typedef unsigned mode_t;
// typedef unsigned _Reg nlink_t;
// typedef _Int64 off_t;
// typedef unsigned _Int64 ino_t;
// typedef unsigned _Int64 dev_t;
// typedef long blksize_t;
// typedef _Int64 blkcnt_t;
// typedef unsigned _Int64 fsblkcnt_t;
// typedef unsigned _Int64 fsfilcnt_t;
// typedef unsigned wint_t;
// typedef unsigned long wctype_t;
// typedef void * timer_t;
// typedef int clockid_t;
// typedef long clock_t;
// struct timeval { time_t tv_sec; suseconds_t tv_usec; };
// struct timespec { time_t tv_sec; int :8*(sizeof(time_t)-sizeof(long))*(__BYTE_ORDER==__BIG_ENDIAN); long tv_nsec; int :8*(sizeof(time_t)-sizeof(long))*(__BYTE_ORDER!=__BIG_ENDIAN); };
// typedef int pid_t;
// typedef unsigned id_t;
// typedef unsigned uid_t;
// typedef unsigned gid_t;
// typedef int key_t;
// typedef unsigned useconds_t;
// +--------------------------------------------------------------+
// | MAX and MIN #defines |
// +--------------------------------------------------------------+
#define INT8_MIN (-1-0x7f)
#define INT16_MIN (-1-0x7fff)
#define INT32_MIN (-1-0x7fffffff)
#define INT64_MIN (-1-0x7fffffffffffffff)
#define INT8_MAX (0x7f)
#define INT16_MAX (0x7fff)
#define INT32_MAX (0x7fffffff)
#define INT64_MAX (0x7fffffffffffffff)
#define UINT8_MAX (0xff)
#define UINT16_MAX (0xffff)
#define UINT32_MAX (0xffffffffu)
#define UINT64_MAX (0xffffffffffffffffu)
// #define INT_FAST8_MIN INT8_MIN
// #define INT_FAST64_MIN INT64_MIN
// #define INT_LEAST8_MIN INT8_MIN
// #define INT_LEAST16_MIN INT16_MIN
// #define INT_LEAST32_MIN INT32_MIN
// #define INT_LEAST64_MIN INT64_MIN
// #define INT_FAST8_MAX INT8_MAX
// #define INT_FAST64_MAX INT64_MAX
// #define INT_LEAST8_MAX INT8_MAX
// #define INT_LEAST16_MAX INT16_MAX
// #define INT_LEAST32_MAX INT32_MAX
// #define INT_LEAST64_MAX INT64_MAX
// #define UINT_FAST8_MAX UINT8_MAX
// #define UINT_FAST64_MAX UINT64_MAX
// #define UINT_LEAST8_MAX UINT8_MAX
// #define UINT_LEAST16_MAX UINT16_MAX
// #define UINT_LEAST32_MAX UINT32_MAX
// #define UINT_LEAST64_MAX UINT64_MAX
#define INTMAX_MIN INT64_MIN
#define INTMAX_MAX INT64_MAX
#define UINTMAX_MAX UINT64_MAX
// #define WINT_MIN 0U
// #define WINT_MAX UINT32_MAX
#define WCHAR_MAX (0x7FFFFFFF+L'\0')
#define WCHAR_MIN (-1-0x7FFFFFFF+L'\0')
#define SIG_ATOMIC_MIN INT32_MIN
#define SIG_ATOMIC_MAX INT32_MAX
#endif // _STDINT_H

27
std/stdio.h Normal file
View File

@@ -0,0 +1,27 @@
/*
File: stdio.h
Author: Taylor Robbins
Date: 08\28\2025
*/
#ifndef _STDIO_H
#define _STDIO_H
#include <stdint.h>
#include <stdarg.h>
MAYBE_START_EXTERN_C
int vprintf(const char* formatStr, va_list args);
// TODO: int vfprintf(FILE*, const char*, va_list args);
int vsprintf(char* bufferPntr, const char* formatStr, va_list args);
int vsnprintf(char* bufferPntr, size_t bufferSize, const char* formatStr, va_list args);
int printf(const char* formatStr, ...);
// TODO: int fprintf(FILE*, const char*, ...);
int sprintf(char* bufferPntr, const char* formatStr, ...);
int snprintf(char* bufferPntr, size_t bufferSize, const char* formatStr, ...);
MAYBE_END_EXTERN_C
#endif // _STDIO_H

56
std/string.h Normal file
View File

@@ -0,0 +1,56 @@
/*
File: string.h
Author: Taylor Robbins
Date: 08\28\2025
*/
#ifndef _STRING_H
#define _STRING_H
#include <stdint.h>
#define WASM_STD_USE_BUILTIN_MEMSET 1
#define WASM_STD_USE_BUILTIN_MEMCPY 1
#define WASM_STD_USE_BUILTIN_MEMMOVE 1
MAYBE_START_EXTERN_C
#if WASM_STD_USE_BUILTIN_MEMSET
// Basically converts to memory.fill instruction
#define memset(pntr, value, numBytes) __builtin_memset((pntr), (value), (numBytes));
#else
void* _memset(void* pntr, int value, size_t numBytes);
#define memset(pntr, value, numBytes) _memset((pntr), (value), (numBytes))
#endif
#if WASM_STD_USE_BUILTIN_MEMCPY
// Basically converts to memory.copy instruction
#define memcpy(dest, source, numBytes) __builtin_memcpy((dest), (source), (numBytes));
#else
void* _memcpy(void* dest, const void* source, size_t numBytes);
#define memcpy(dest, source, numBytes) _memcpy((dest), (source), (numBytes))
#endif
#if WASM_STD_USE_BUILTIN_MEMMOVE
// Basically converts to memory.copy instruction
#define memmove(dest, source, numBytes) __builtin_memmove((dest), (source), (numBytes));
#else
void* _memmove(void* dest, const void* source, size_t numBytes);
#define memmove(dest, source, numBytes) _memmove((dest), (source), (numBytes))
#endif
int memcmp(const void* left, const void* right, size_t numBytes);
char* strcpy(char* dest, const char* source);
// TODO: char* strstr(const char* haystack, const char* needle);
int strcmp(const char* left, const char* right);
int strncmp(const char* left, const char* right, size_t numBytes);
size_t strlen(const char* str);
// TODO: size_t wcslen(const wchar_t* str);
//TODO: char *strchr (const char *, int); //NEEDED for slre.c
//TODO: char *strrchr (const char *, int); //NEEDED for slre.c
MAYBE_END_EXTERN_C
#endif // _STRING_H

19
std/uchar.h Normal file
View File

@@ -0,0 +1,19 @@
/*
File: uchar.h
Author: Taylor Robbins
Date: 08\28\2025
*/
#ifndef _UCHAR_H
#define _UCHAR_H
typedef unsigned short char16_t;
typedef unsigned char32_t;
// TODO: size_t c16rtomb(char *__restrict, char16_t, mbstate_t *__restrict);
// TODO: size_t mbrtoc16(char16_t *__restrict, const char *__restrict, size_t, mbstate_t *__restrict);
// TODO: size_t c32rtomb(char *__restrict, char32_t, mbstate_t *__restrict);
// TODO: size_t mbrtoc32(char32_t *__restrict, const char *__restrict, size_t, mbstate_t *__restrict);
#endif // _UCHAR_H

11
test_app.c Normal file
View File

@@ -0,0 +1,11 @@
/*
File: test_app.c
Author: Taylor Robbins
Date: 08\28\2025
Description:
** When no other application is present, this serves as a simple test case for the CWasm layer
*/
#include "cwasm.c"