re-adding jax stuff

unstable
Penguin 5 years ago
parent 5c3995b053
commit 7f209442ef

@ -0,0 +1,38 @@
@echo off
setlocal
REM Script assumes tools directory is in your path.
REM For now, this is done by running shell.bat inside of tools directory
set CommonCompilerFlags=-I../libcommon/src -I../tests/src -DBUILD_WIN32=1 -D_CRT_SECURE_NO_WARNINGS -fp:fast -fp:except- -nologo -Gm- -GR- -EHsc- -EHa- -Od -Oi -WX -W3 -FC -Zi -GS- -Gs9999999
set CommonLinkerFlags= -nologo -STACK:0x100000,0x100000 -incremental:no -opt:ref -nodefaultlib libcmt.lib libvcruntime.lib libucrt.lib user32.lib gdi32.lib winmm.lib kernel32.lib shlwapi.lib
set BuildDir=%~dp0build
REM set CompileCommand=clang-cl
set CompileCommand=cl
IF NOT EXIST tools\btime.exe (
pushd tools\build-timer
call build.bat
popd
)
IF NOT EXIST %BuildDir% mkdir %BuildDir%
pushd %BuildDir%
del *.pdb > NUL 2> NUL
del *.map > NUL 2> NUL
..\tools\btime.exe --begin ePenguin-Software-Framework.aet
%CompileCommand% %CommonCompilerFlags% -LD ../libcommon/src/libcommon.cpp -Folibcommon.obj -Fmlibcommon.map /link %CommonLinkerFlags% -noentry
lib -nologo libcommon.obj
%CompileCommand% %CommonCompilerFlags% ../tests/src/tests.cpp -Fotests.obj -Fmtests.map /link %CommonLinkerFlags% libcommon.lib
robocopy . ..\ libcommon.lib > NUL 2> NUL
del ..\test > NUL 2> NUL
copy tests.exe ..\test > NUL 2> NUL
..\tools\btime.exe --end ePenguin-Software-Framework.aet
popd

@ -0,0 +1,14 @@
#!/bin/sh
# Script assumes tools directory is in your path.
# For now, this is done by running shell.sh inside of tools directory
if [ ! -f tools/btime ]; then
pushd tools/build-timer
./build.sh
popd
fi
tools/btime --begin ePenguin-Software-Framework.aet
tools/btime --end ePenguin-Software-Framework.aet

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -0,0 +1,3 @@
load
monitor reset
q

@ -0,0 +1,96 @@
LDSCRIPT = ./ld/ex.ld
MCUTYPE=__SAMD21J18A__
print-% : ; @echo $* = $($*)
BUILD_DIR:=build
SRC_DIR:=src
INC_DIR:=inc
BIN_DIR:=bin
VERBOSE:=0
_ATSYM_:=@
ifeq ($(VERBOSE),1)
_ATSYM_=
clean-build:
else
_ATSYM_=@
endif
# Tools
CC=arm-none-eabi-g++
LD=arm-none-eabi-gcc
AR=arm-none-eabi-ar
AS=arm-none-eabi-as
ELF=$(BIN_DIR)/main.elf
INC:=$(sort $(shell find -L . -type f,l \( -name '*.h' -o -name '*.hpp' \) -printf '-I%h ' | sort -u))
vpath %.c $(sort $(shell find -L ./src -type f,l -name "*.c" -printf '%h\n' | sort -u))
vpath %.s $(sort $(shell find -L ./src -type f,l -name "*.s" -printf '%h\n' | sort -u))
vpath %.cpp $(sort $(shell find -L ./src -type f,l -name "*.cpp" -printf '%h\n' | sort -u))
vpath %.o $(BUILD_DIR)
OBJ_C=$(shell find -L ./src -type f,l -name '*.c')
OBJ_CPP=$(shell find -L ./src -type f,l -name '*.cpp')
OBJ_S=$(shell find -L ./src -type f,l -name '*.s')
OBJ:=$(addprefix $(BUILD_DIR)/, $(notdir $(OBJ_C:%.c=%.o)))
OBJ+=$(addprefix $(BUILD_DIR)/, $(notdir $(OBJ_CPP:%.cpp=%.o)))
OBJ+=$(addprefix $(BUILD_DIR)/, $(notdir $(OBJ_S:%.s=%.o)))
DEPS:=$(OBJ:.o=.d)
LDFLAGS+= -T$(LDSCRIPT) -Wall -mthumb -mcpu=cortex-m0 -Wl,--gc-sections
CFLAGS+= $(INC) -mcpu=cortex-m0 -mthumb -ggdb -D$(MCUTYPE)
.PHONY: all clean-build clean
all: $(ELF)
$(ELF): $(OBJ)
@mkdir -p $(dir $@)
@echo "***************"
@echo "Linking Target: " $@
@echo "prereq: $(OBJ)"
$(_ATSYM_)$(LD) $(LDFLAGS) -o $@ $(OBJ) $(LDLIBS)
# compile and generate dependency info
$(BUILD_DIR)/%.o: %.c
@mkdir -p $(dir $@)
@echo "***************"
@echo "Compiling $< ---> $@"
$(_ATSYM_)arm-none-eabi-gcc -c $(CFLAGS) $< -o $@
@echo "Outputting Dependency from $< ---> "$(BUILD_DIR)/$*.d
$(_ATSYM_)arm-none-eabi-gcc -MM $(CFLAGS) $< > $(BUILD_DIR)/$*.d
# compile and generate dependency info
$(BUILD_DIR)/%.o: %.cpp
@mkdir -p $(dir $@)
@echo "***************"
@echo "Compiling $< ---> $@"
$(_ATSYM_)$(CC) -c $(CFLAGS) $< -o $@
@echo "Outputting Dependency from $< ---> "$(BUILD_DIR)/$*.d
$(_ATSYM_)$(CC) -MM $(CFLAGS) $< > $(BUILD_DIR)/$*.d
$(BUILD_DIR)/%.o: %.s
@mkdir -p $(dir $@)
@echo "***************"
@echo "Assembling $< ---> $@"
$(_ATSYM_)$(AS) $< -o $@
clean:
@echo "Cleaning project..."
$(_ATSYM_)rm -f $(OBJ) $(DEPS)
$(_ATSYM_)rm -f $(BUILD_DIR)/*.d $(BUILD_DIR)/*.o
$(_ATSYM_)rm -f $(ELF) .gdb_history
clean-build: clean all
debug: $(ELF)
arm-none-eabi-gdb -iex "target extended-remote localhost:3333" $(ELF)
# pull in dependencies
-include $(DEPS)

Binary file not shown.

@ -0,0 +1,11 @@
hal_sam_d2x.o: src/hal/hal_sam_d2x.cpp inc/hal/hal_sam_d2x.hpp \
inc/common/hal_arm.hpp conf/epenguin_conf.h inc/hdi/hdi_sam_d2x.h \
inc/common/epenguin_dev_arm_util.h inc/common/cmsis/core_cm0plus.h \
inc/common/cmsis/cmsis_version.h inc/common/cmsis/cmsis_compiler.h \
inc/common/cmsis/cmsis_gcc.h inc/hdi/hdi_ac.h inc/hdi/hdi_adc.h \
inc/hdi/hdi_dmac.h inc/hdi/hdi_dsu.h inc/hdi/hdi_eic.h \
inc/hdi/hdi_evsys.h inc/hdi/hdi_gclk.h inc/hdi/hdi_hmatrixb.h \
inc/hdi/hdi_i2s.h inc/hdi/hdi_mtb.h inc/hdi/hdi_nvmctrl.h \
inc/hdi/hdi_pac.h inc/hdi/hdi_pm.h inc/hdi/hdi_port.h inc/hdi/hdi_rtc.h \
inc/hdi/hdi_sercom.h inc/hdi/hdi_sysctrl.h inc/hdi/hdi_tc.h \
inc/hdi/hdi_tcc.h inc/hdi/hdi_usb.h inc/hdi/hdi_wdt.h

@ -0,0 +1,10 @@
hdi_sam_d2x.o: src/hdi/hdi_sam_d2x.c inc/hdi/hdi_sam_d2x.h \
inc/common/epenguin_dev_arm_util.h inc/common/cmsis/core_cm0plus.h \
inc/common/cmsis/cmsis_version.h inc/common/cmsis/cmsis_compiler.h \
inc/common/cmsis/cmsis_gcc.h inc/hdi/hdi_ac.h inc/hdi/hdi_adc.h \
inc/hdi/hdi_dmac.h inc/hdi/hdi_dsu.h inc/hdi/hdi_eic.h \
inc/hdi/hdi_evsys.h inc/hdi/hdi_gclk.h inc/hdi/hdi_hmatrixb.h \
inc/hdi/hdi_i2s.h inc/hdi/hdi_mtb.h inc/hdi/hdi_nvmctrl.h \
inc/hdi/hdi_pac.h inc/hdi/hdi_pm.h inc/hdi/hdi_port.h inc/hdi/hdi_rtc.h \
inc/hdi/hdi_sercom.h inc/hdi/hdi_sysctrl.h inc/hdi/hdi_tc.h \
inc/hdi/hdi_tcc.h inc/hdi/hdi_usb.h inc/hdi/hdi_wdt.h

@ -0,0 +1,11 @@
main.o: src/main.cpp inc/epenguin.hpp conf/epenguin_conf.h \
inc/hal/hal_sam_d2x.hpp inc/common/hal_arm.hpp inc/hdi/hdi_sam_d2x.h \
inc/common/epenguin_dev_arm_util.h inc/common/cmsis/core_cm0plus.h \
inc/common/cmsis/cmsis_version.h inc/common/cmsis/cmsis_compiler.h \
inc/common/cmsis/cmsis_gcc.h inc/hdi/hdi_ac.h inc/hdi/hdi_adc.h \
inc/hdi/hdi_dmac.h inc/hdi/hdi_dsu.h inc/hdi/hdi_eic.h \
inc/hdi/hdi_evsys.h inc/hdi/hdi_gclk.h inc/hdi/hdi_hmatrixb.h \
inc/hdi/hdi_i2s.h inc/hdi/hdi_mtb.h inc/hdi/hdi_nvmctrl.h \
inc/hdi/hdi_pac.h inc/hdi/hdi_pm.h inc/hdi/hdi_port.h inc/hdi/hdi_rtc.h \
inc/hdi/hdi_sercom.h inc/hdi/hdi_sysctrl.h inc/hdi/hdi_tc.h \
inc/hdi/hdi_tcc.h inc/hdi/hdi_usb.h inc/hdi/hdi_wdt.h

Binary file not shown.

@ -0,0 +1 @@
mcu.o: src/common/mcu.cpp

Binary file not shown.

@ -0,0 +1,11 @@
#ifndef _EPENGUIN_CONF_H_
#define _EPENGUIN_CONF_H_
#define EP_UARCH __UARCH_ARM__
#define EP_MCU_FAMILY __SAM_D2X__
#define EP_MCU __ATSAMD21J18A__
#define EP_DRIVERS (0)
#endif

@ -0,0 +1 @@
/storage/Shared/Documents/Projects/epenguin_software_framework/arch/common/inc/cmsis/

@ -0,0 +1 @@
/storage/Shared/Documents/Projects/epenguin_software_framework/arch/arm/common/inc

@ -0,0 +1,89 @@
#ifndef _EPENGUIN_HPP_
#define _EPENGUIN_HPP_
/* -- ePenguin --
This master file dictates all supported hardware. It lists supported
architectures, mcu families, and specific mcus. This software framework
targets hardware at the mcu level--not at the board level. Board layers can
be applied, but this framework targets the mcu and the mcu alone.
*/
#ifndef __UARCHITECTURES__
#define __UARCHITECTURES__
#define __UARCH_ARM__ (0)
#define __UARCH_ARM64__ (1)
#define __UARCH_AVR__ (2)
#define __UARCH_AVR32__ (3)
#define __UARCH_X86__ (4)
#define __UARCH_RISCV__ (5)
#endif
#ifndef __UFAMILIES__
#define __UFAMILIES__
/* Support ARM MCU Families */
/* Microchip */
#define __SAM_D1X__ (0)
#define __SAM_C1X__ (1)
#define __SAM_D2X__ (2)
#define __SAM_E_D5X__ (3)
#define __SAM_C2X__ (4)
#define __SAM_L2X__ (5)
/* STMicroelectronics */
/* NXP */
#endif
#ifndef __UCONTROLLERS__
#define __UCONTROLLERS__
/* ARM */
/* Microchip */
/* SAM_D2X */
/* D2X Series E */
#define __ATSAMD21E15A__ (0)
#define __ATSAMD21E15B__ (1)
#define __ATSAMD21E15BU__ (2)
#define __ATSAMD21E15L__ (3)
#define __ATSAMD21E16A__ (4)
#define __ATSAMD21E16B__ (5)
#define __ATSAMD21E16BU__ (6)
#define __ATSAMD21E16L__ (7)
#define __ATSAMD21E17A__ (8)
#define __ATSAMD21E17D__ (9)
#define __ATSAMD21E17DU__ (10)
#define __ATSAMD21E17L__ (11)
#define __ATSAMD21E18A__ (12)
/* D2X Series G */
#define __ATSAMD21G15A__ (13)
#define __ATSAMD21G15B__ (14)
#define __ATSAMD21G15L__ (15)
#define __ATSAMD21G16A__ (16)
#define __ATSAMD21G16B__ (17)
#define __ATSAMD21G16L__ (18)
#define __ATSAMD21G17A__ (19)
#define __ATSAMD21G17AU__ (20)
#define __ATSAMD21G17D__ (21)
#define __ATSAMD21G17L__ (22)
#define __ATSAMD21G18A__ (23)
#define __ATSAMD21G18AU__ (24)
/* D2X Series J */
#define __ATSAMD21J15A__ (25)
#define __ATSAMD21J15B__ (26)
#define __ATSAMD21J16A__ (27)
#define __ATSAMD21J16B__ (28)
#define __ATSAMD21J17A__ (29)
#define __ATSAMD21J17D__ (30)
#define __ATSAMD21J18A__ (31)
#endif
#include "epenguin_conf.h"
#endif

@ -0,0 +1 @@
/storage/Shared/Documents/Projects/epenguin_software_framework/arch/arm/SAM_D2X/hal/inc

@ -0,0 +1 @@
/storage/Shared/Documents/Projects/epenguin_software_framework/arch/arm/SAM_D2X/hdi/inc

@ -0,0 +1 @@
/storage/Shared/Documents/Projects/epenguin_software_framework/arch/arm/SAM_D2X/ld/ex.ld

@ -0,0 +1,20 @@
#!/bin/bash
# links cmsis folder into our inc folder
# ln -s /storage/Shared/Documents/ASF_LIBS/xdk-asf-3.48.0/thirdparty/CMSIS/Include ./inc/cmsis
ln -s ${EPSF_HAL}/arch/common/inc/cmsis/ ./inc/cmsis
# links asf3 samd21 include folder into our inc folder
# ln -s /storage/Shared/Documents/ASF_LIBS/xdk-asf-3.48.0/sam0/utils/cmsis/samd21/include ./inc/asf3-inc
ln -s ${EPSF_HAL}/arch/arm/SAM_D2X/hal/inc ./inc/hal
ln -s ${EPSF_HAL}/arch/arm/SAM_D2X/hdi/inc ./inc/hdi
ln -s ${EPSF_HAL}/arch/arm/SAM_D2X/hal/src ./src/hal
ln -s ${EPSF_HAL}/arch/arm/SAM_D2X/hdi/src ./src/hdi
ln -s ${EPSF_HAL}/arch/arm/common/inc ./inc/common
ln -s ${EPSF_HAL}/arch/arm/common/src ./src/common
# links linker script into our linker folder
# ln -s /storage/Shared/Documents/ASF_LIBS/xdk-asf-3.48.0/sam0/utils/linker_scripts/samd21/gcc/samd21j18a_flash.ld ./linker/.
ln -s ${EPSF_HAL}/arch/arm/SAM_D2X/ld/ex.ld ./ld/ex.ld
# links asf3 startup code for d21 into our src folder
# ln -s /storage/Shared/Documents/ASF_LIBS/xdk-asf-3.48.0/sam0/utils/cmsis/samd21/source/gcc/startup_samd21.c ./src/.
# ln -s /storage/Shared/Documents/ASF_LIBS/xdk-asf-3.48.0/sam0/utils/cmsis/samd21/source/system_samd21.c ./src/.
# ln -s /storage/Shared/Documents/ASF_LIBS/xdk-asf-3.48.0/sam0/utils/cmsis/samd21/source/system_samd21.h ./inc/.

@ -0,0 +1,3 @@
#!/bin/bash
rm -rf ./inc/cmsis ./inc/hal ./inc/hdi ./src/hal ./src/hdi ./ld/ex.ld ./inc/common ./src/common

@ -0,0 +1,8 @@
# Atmel-ICE JTAG/SWD in-circuit debugger.
interface cmsis-dap
cmsis_dap_vid_pid 0x03eb 0x2111
cmsis_dap_serial 3 ATML2748051800004709
# Chip info
set CHIPNAME at91samd21j18
source [find target/at91samdXX.cfg]

@ -0,0 +1 @@
/storage/Shared/Documents/Projects/epenguin_software_framework/arch/arm/common/src

@ -0,0 +1 @@
/storage/Shared/Documents/Projects/epenguin_software_framework/arch/arm/SAM_D2X/hal/src

@ -0,0 +1 @@
/storage/Shared/Documents/Projects/epenguin_software_framework/arch/arm/SAM_D2X/hdi/src

@ -0,0 +1,86 @@
#include "epenguin.hpp"
#include "hal_sam_d2x.hpp"
#include <inttypes.h>
#define PORT_ADDR (0x41004400)
#define PORT_GROUP_SIZE (0x80)
#define PORT_A_OFF (0x00)
#define PORT_B_OFF (0x80)
#define PORT_DIR_OFF (0x00)
#define PORT_OUT_OFF (0x10)
// LED 0: PA09
// LED 1: PB01
#define LED0_PORT (0)
#define LED0_PIN (9)
#define LED1_PORT (1)
#define LED1_PIN (1)
static void init_pin(int port, int pin);
static void set_pin(int port, int pin);
static void clr_pin(int port, int pin);
static void delay(int n)
{
int i;
for(;n>0;n--)
{
for(i=0;i<100;i++)
{
asm volatile("nop");
}
}
}
int main()
{
init_pin(LED0_PORT, LED0_PIN);
init_pin(LED1_PORT, LED1_PIN);
for(;;)
{
set_pin(LED0_PORT, LED0_PIN);
clr_pin(LED1_PORT, LED1_PIN);
delay(500);
clr_pin(LED0_PORT, LED0_PIN);
set_pin(LED1_PORT, LED1_PIN);
delay(500);
}
return 0;
}
// port 0: A
// port 1: B
void init_pin(int port, int pin)
{
uint32_t* dir_reg = (uint32_t*)((PORT_ADDR | (port * PORT_GROUP_SIZE) | PORT_DIR_OFF));
*dir_reg |= (1 << pin);
}
void set_pin(int port, int pin)
{
uint32_t* out_reg = (uint32_t*)((PORT_ADDR | (port * PORT_GROUP_SIZE) | PORT_OUT_OFF));
*out_reg |= (1 << pin);
}
void clr_pin(int port, int pin)
{
uint32_t* out_reg = (uint32_t*)((PORT_ADDR | (port * PORT_GROUP_SIZE) | PORT_OUT_OFF));
*out_reg &= ~(1 << pin);
}

@ -0,0 +1,5 @@
#include "types.h"
#include "shared.h"
#include "shared.cpp"
#include "tokenizer.h"
#include "tokenizer.cpp"

File diff suppressed because it is too large Load Diff

@ -0,0 +1,358 @@
struct format_dest {
umm Size;
char* At;
};
void OutChar(format_dest* Dest, char Value) {
if (Dest->Size){
--Dest->Size;
*Dest->At++ = Value;
}
}
void OutChars(format_dest* Dest, char* Value) {
// note(jax): Not particularily speedy@
while (*Value) {
OutChar(Dest, *Value++);
}
}
#define ReadVarArgUnsignedInteger(Length, ArgList) ((Length) == 8) ? _crt_va_arg(*ArgList, u64) : (u64)_crt_va_arg(*ArgList, u32)
#define ReadVarArgSignedInteger(Length, ArgList) ((Length) == 8) ? _crt_va_arg(*ArgList, s64) : (s64)_crt_va_arg(*ArgList, s32)
#define ReadVarArgFloat(Length, ArgList) _crt_va_arg(*ArgList, f64)
static char DecChars[] = "0123456789";
static char LowerHexChars[] = "0123456789abcdef";
static char UpperHexChars[] = "0123456789ABCDEF";
void U64ToASCII(format_dest* Dest, u64 Value, u32 Base, char* Digits) {
Assert(Base != 0);
char* Start = Dest->At;
do {
u64 DigitIndex = (Value % Base);
char Digit = Digits[DigitIndex];
OutChar(Dest, Digit);
Value /= Base;
} while (Value != 0);
char* End = Dest->At;
while (Start < End) {
--End;
char Temp = *End;
*End = *Start;
*Start = Temp;
++Start;
}
}
void F64ToASCII(format_dest* Dest, f64 Value, u32 Precision) {
if (Value < 0) {
OutChar(Dest, '-');
Value = -Value;
}
u64 IntegerPart = (u64)Value;
Value -= (f64)IntegerPart;
U64ToASCII(Dest, IntegerPart, 10, DecChars);
OutChar(Dest, '.');
// tood(jax): Note that this is NOT an accurate way to do this!
for (u32 PrecisionIndex = 0; PrecisionIndex < Precision; ++PrecisionIndex) {
Value *= 10.0f;
u32 Integer = (u32)Value;
Value -= (f32)Integer;
OutChar(Dest, DecChars[Integer]);
}
}
// note(jax): This function serves as a replacement to `stdio.h` sprintf()
umm FormatArgList(umm DestSize, char* DestInit, char* Format, va_list ArgList) {
format_dest Dest = {DestSize, DestInit};
if (Dest.Size) {
char* At = Format;
while (At[0]) {
if (*At == '%') {
++At;
b32 ForceSign = false;
b32 PadWithZeros = false;
b32 LeftJustify = false;
b32 PositiveSignIsBlank = false;
b32 AnnotateIfNotZero = false;
// note(jax): Handle the flags
b32 Parsing = true;
Parsing = true;
while (Parsing) {
switch (*At) {
case '+': { ForceSign = true; } break;
case '0': { PadWithZeros = true; } break;
case '-': { LeftJustify = true; } break;
case ' ': { PositiveSignIsBlank = true; } break;
case '#': { AnnotateIfNotZero = true; } break;
default: { Parsing = false; } break;
}
if (Parsing) {
++At;
}
}
// note(jax): Handle the width
b32 WidthSpecified = false;
int32 Width = 0;
if (*At == '*') {
Width = _crt_va_arg(ArgList, int);
WidthSpecified = true;
++At;
} else if ((*At >= '0') && (*At <= '9')) {
Width = StringToI32(At);
WidthSpecified = true;
//? ++At;
}
// note(jax): Handle the precision
b32 PrecisionSpecified = false;
int32 Precision = 0;
if (*At == '.') {
++At;
if (*At == '*') {
Precision = _crt_va_arg(ArgList, int);
PrecisionSpecified = true;
++At;
} else if ((*At >= '0') && (*At <= '9')) {
Precision = StringToI32(At);
PrecisionSpecified = true;
//++At;
} else {
Assert(!"Malformed precision specifier!");
}
}
// todo(jax): Right now our routine doesn't allow non-specified
// precisions, so we just set non-specified precisions to a specific value.
if (!PrecisionSpecified) {
Precision = 6;
}
// note(jax): Handle the length
u32 IntegerLength = 4;
u32 FloatLength = 8;
// todo(jax): Actually set different values here.
if ((At[0] == 'h') && (At[1] == 'h')) {
At += 2;
} else if ((At[0] == 'l') && (At[1] == 'l')) {
At += 2;
} else if (*At == 'h') {
++At;
} else if (*At == 'l') {
IntegerLength = 8;
++At;
} else if (*At == 'j') {
++At;
} else if (*At == 'z') {
++At;
} else if (*At == 't') {
++At;
} else if (*At == 'L') {
++At;
}
char TempBuffer[64];
char* Temp = TempBuffer;
format_dest TempDest = {ArrayCount(TempBuffer), Temp};
char* Prefix = "";
b32 IsFloat = false;
switch (*At) {
case 'd':
case 'i': {
s64 Value = ReadVarArgSignedInteger(IntegerLength, &ArgList);
b32 WasNegative = (Value < 0);
if (WasNegative) {
Value = -Value;
}
U64ToASCII(&TempDest, (u64)Value, 10, DecChars);
// todo(jax): Make this a common routine once floating poiunt is available.
if (WasNegative) {
Prefix = "-";
} else if (ForceSign) {
Assert(!PositiveSignIsBlank); // note(jax): Not a problem, but probably shouldn't be specified.
Prefix = "+";
} else if (PositiveSignIsBlank) {
Prefix = " ";
}
} break;
case 'u': {
u64 Value = ReadVarArgUnsignedInteger(IntegerLength, &ArgList);
U64ToASCII(&TempDest, Value, 10, DecChars);
} break;
case 'o': {
u64 Value = ReadVarArgUnsignedInteger(IntegerLength, &ArgList);
U64ToASCII(&TempDest, Value, 8, DecChars);
if (AnnotateIfNotZero && (Value != 0)) {
Prefix = "0";
}
} break;
case 'x': {
u64 Value = ReadVarArgUnsignedInteger(IntegerLength, &ArgList);
U64ToASCII(&TempDest, Value, 16, LowerHexChars);
if (AnnotateIfNotZero && (Value != 0)) {
Prefix = "0x";
}
} break;
case 'X': {
u64 Value = ReadVarArgUnsignedInteger(IntegerLength, &ArgList);
U64ToASCII(&TempDest, Value, 16, UpperHexChars);
if (AnnotateIfNotZero && (Value != 0)) {
Prefix = "0X";
}
} break;
// todo(jax): Support other kinds of floating point prints
// Currently we only do basic decimal output.
case 'f':
case 'F':
case 'e':
case 'E':
case 'g':
case 'G':
case 'a':
case 'A': {
f64 Value = ReadVarArgFloat(FloatLength, &ArgList);
F64ToASCII(&TempDest, Value, Precision);
IsFloat = true;
// @Speed @Cleanup todo(jax): If we still have more floats in temp, increase
// the size again. This is a very, very bad hack and is NOT shippable! (shame!)
if (Temp) {
Dest.Size += FloatLength;
}
} break;
case 'c': {
// todo(jax): How much are we supposed to read here?
int Value = _crt_va_arg(ArgList, int);
OutChar(&TempDest, (char)Value);
} break;
case 's': {
char* String = _crt_va_arg(ArgList, char*);
// todo(jax): Obey precision, width, etc.
Temp = String;
if (PrecisionSpecified) {
TempDest.Size = 0;
for (char* Scan = String; *Scan && (TempDest.Size < Precision); ++Scan) {
++TempDest.Size;
}
}
else {
TempDest.Size = StringLength(String);
}
TempDest.At = String + TempDest.Size;
} break;
case 'p': {
void* Value = _crt_va_arg(ArgList, void*);
U64ToASCII(&TempDest, *(umm*)&Value, 16, LowerHexChars);
} break;
case 'n': {
int* TabDest = _crt_va_arg(ArgList, int*);
*TabDest = (int)(Dest.At - DestInit);
} break;
case '%': {
OutChar(&Dest, '%');
} break;
default: {
Assert(!"Unrecognized format specifier!");
} break;
}
if (TempDest.At - Temp) {
smm UsePrecision = Precision;
if (IsFloat || !PrecisionSpecified) {
UsePrecision = (TempDest.At - Temp);
}
smm PrefixLength = StringLength(Prefix);
smm UseWidth = Width;
smm ComputedWidth = UsePrecision + PrefixLength;
if (UseWidth < ComputedWidth) {
UseWidth = ComputedWidth;
}
if (PadWithZeros) {
Assert(!LeftJustify); // note(jax): Not a problem, but no way to do it?
LeftJustify = false;
}
if (!LeftJustify) {
while (UseWidth > (UsePrecision + PrefixLength)) {
OutChar(&Dest, PadWithZeros ? '0' : ' ');
--UseWidth;
}
}
for (char* Pre = Prefix; *Pre && UseWidth; ++Pre) {
OutChar(&Dest, *Pre);
--UseWidth;
}
if (UsePrecision > UseWidth) {
UsePrecision = UseWidth;
}
while (UsePrecision > (TempDest.At - Temp)) {
OutChar(&Dest, '0');
--UsePrecision;
--UseWidth;
}
while (UsePrecision && (TempDest.At != Temp)) {
OutChar(&Dest, *Temp++);
--UsePrecision;
--UseWidth;
}
if (LeftJustify) {
while (UseWidth) {
OutChar(&Dest, ' ');
--UseWidth;
}
}
}
if (*At) {
++At;
}
} else {
OutChar(&Dest, *At++);
}
}
if (Dest.Size) {
Dest.At[0] = 0;
} else {
Dest.At[-1] = 0;
}
}
umm Result = Dest.At - DestInit;
return Result;
}

@ -0,0 +1,296 @@
#ifndef SHARED_H
#define SHARED_H
#define ZeroStruct(Struct) ZeroSize(&Struct, sizeof(Struct))
inline void ZeroSize(void* Ptr, size_t Size) {
u8* Byte = (u8*)Ptr;
// todo(jax): @Speed: Check performance of the two code blocks
#if 0
for (size_t i = 0; i < Size; ++i) {
*Byte++ = 0;
}
#else
while (Size--) {
*Byte++ = 0;
}
#endif
}
#define CopyArray(Dest, Source, Count) MemoryCopy((Dest), (Source), (Count)*sizeof(*(Source)))
inline void* MemoryCopy(void* _Dest, void* _Source, size_t Size) {
#if 0
return memcpy(_Dest, _Source, Size);
#else //NO CRT VERSION HERE!
if (_Source == 0) {
ZeroSize(_Dest, Size);
return _Dest;
}
u8* Source = (u8*)_Source;
u8* Dest = (u8*)_Dest;
while (Size--) {
*Dest++ = *Source++;
}
return _Dest;
#endif
}
inline u8* Advance(string* String, umm Count) {
u8 *Result = 0;
if(String->Count >= Count) {
Result = String->Data;
String->Data += Count;
String->Count -= Count;
} else {
String->Data += String->Count;
String->Count = 0;
}
return(Result);
}
inline b32 IsSpacing(char C) {
b32 Result = ((C == ' ') || (C == '\t') || (C == '\v') ||(C == '\f'));
return(Result);
}
inline b32 IsEndOfLine(char Value) {
b32 Result = ((Value == '\n') || (Value == '\r'));
return Result;
}
inline b32 IsWhitespace(char Value, bool IncludeEOL = false) {
b32 Result = ((Value == ' ') || (Value == '\t') || (Value == '\v') || (Value == '\f') || ( IncludeEOL ? IsEndOfLine(Value) : 1));
return Result;
}
inline b32 IsAlphabetical(char Value) {
b32 Result = (((Value >= 'a') && (Value <= 'z')) || ((Value >= 'A') && (Value <= 'Z')));
return Result;
}
inline b32 IsNumeric(char Value) {
b32 Result = ((Value >= '0') && (Value <= '9'));
return Result;
}
inline char* GetNextLine(char** Contents) {
char* Text = *Contents;
if (!*Text) {
return nullptr;
}
char* Line = Text;
while (*Text && (*Text != '\n') && (*Text != '\r')) {
++Text;
}
char* End = Text;
++End;
if (*Text == '\r') {
if (*End == '\n') {
++End;
}
*Text = '\0';
}
*Contents = End;
return Line;
}
inline int StringLength(char* String) {
int Count = 0;
while (*String++) {
++Count;
}
return Count;
}
inline string make_string(char* String) {
string Result = {};
Result.Data = (u8*)String;
Result.Count = StringLength(String);
return Result;
}
inline char* Substring(char* Source, char* String) {
while (*Source) {
char *Begin = Source;
char *Pattern = String;
// If first character of sub string match, check for whole string
while (*Source && *Pattern && *Source == *Pattern) {
Source++;
Pattern++;
}
// If complete sub string match, return starting address
if (!*Pattern)
return Begin;
Source = Begin + 1;
}
return NULL;
}
// note: _Char is expected to be an ASCII character
inline char* FindFirstChar(char* String, int _Char) {
char Char = (char)_Char;
while (*String != Char) {
if (!*String++) {
return NULL;
}
}
return (char*)String;
}
// todo(jax): Consider using while loops for Append and StringCopy?? Query performance benefit.
inline char* Append(char* Dest, size_t DestSize, char* Source) {
// todo(jax): Do we need to terminate annd or allocate needed memory?
Dest = (char*)malloc(DestSize + (1 + StringLength(Source) * sizeof(char)));
MemoryCopy(Dest + StringLength(Dest), Source, DestSize + 1);
return Dest;
}
inline char* StringCopy(char* String) {
char* Result = (char*)malloc(sizeof(char) * (StringLength(String) + 1));
MemoryCopy(Result, String, sizeof(char) * (StringLength(String) + 1));
return Result;
}
inline bool StringsMatch(char* A, char* B) {
while (*A && *B) {
if (*A != *B){
return false;
}
++A;
++B;
}
if (*A != *B){
return false;
} else {
return true;
}
}
inline void CatStrings(size_t SourceACount, char* SourceA, size_t SourceBCount, char* SourceB, size_t DestCount, char* Dest) {
// todo(jax): Bounds check the Destination. This code is risky!
for (int Index = 0; Index < SourceACount; ++Index) {
*Dest++ = *SourceA++;
}
for (int Index = 0; Index < SourceBCount; ++Index) {
*Dest++ = *SourceB++;
}
*Dest++ = 0;
}
//
// NOT REALLY SURE IF WE WANT TO USE THE FOLLOWING CODE!
//
#if 1
void __cdecl __va_start(va_list*, ...);
#define _crt_va_start(ap, x) ((void)(__va_start(&ap, x)))
#define _crt_va_arg(ap, t) \
((sizeof(t) > sizeof(__int64) || (sizeof(t) & (sizeof(t) - 1)) != 0) \
? **(t**)((ap += sizeof(__int64)) - sizeof(__int64)) \
: *(t* )((ap += sizeof(__int64)) - sizeof(__int64)))
#define _crt_va_end(ap) ((void)(ap = (va_list)0))
#else
#define _crt_va_start(ap, v) va_start(ap, v)
#define _crt_va_arg(ap, t) va_arg(ap, t)
#define _crt_va_end(ap) va_end(ap)
#endif
inline int32 StringToI32_(char** AtInit) {
int32 Result = {};
char* At = *AtInit;
while ((*At >= '0') && (*At <= '9')) {
Result *= 10;
Result += (*At - '0');
++At;
}
*AtInit = At;
return Result;
}
inline int32 StringToI32(char* At) {
char* Ignored = At;
int32 Result = StringToI32_(&Ignored);
return Result;
}
struct format_dest;
void OutChar(format_dest* Dest, char Value);
void OutChars(format_dest* Dest, char* Value);
void U64ToASCII(format_dest* Dest, u64 Value, u32 Base, char* Digits);
void F64ToASCII(format_dest* Dest, f64 Value, u32 Precision);
// note(jax): This function serves as a replacement to `stdio.h` sprintf()
API_EXPORT umm FormatArgList(umm DestSize, char* DestInit, char* Format, va_list ArgList);
inline umm Format(umm DestSize, char* Dest, char* Format, ...) {
va_list ArgList;
_crt_va_start(ArgList, Format);
umm Result = FormatArgList(DestSize, Dest, Format, ArgList);
_crt_va_end(ArgList);
return Result;
}
inline int _crt_sprintf(char* Buffer, size_t Count, char* Format, ...) {
int Result;
va_list ArgList;
__crt_va_start(ArgList, Format);
Result = _vsprintf_s_l(Buffer, Count, Format, NULL, ArgList);
__crt_va_end(ArgList);
return Result;
}
inline umm ae_sprintf(char* Dest, char* Format, ...) {
va_list ArgList;
_crt_va_start(ArgList, Format);
umm Result = FormatArgList(sizeof(Dest), Dest, Format, ArgList);
_crt_va_end(ArgList);
return Result;
}
inline umm ae_sprintf(char* Dest, char* Format, va_list ArgList) {
umm Result = FormatArgList(sizeof(Dest), Dest, Format, ArgList);
return Result;
}
inline umm ae_printf(char* Format, ...) {
char Buffer[1024];
va_list ArgList;
_crt_va_start(ArgList, Format);
umm Result = FormatArgList(1024, Buffer, Format, ArgList);
_crt_va_end(ArgList);
fwrite(Buffer, sizeof(char), StringLength(Buffer), stdout);
fflush(stdout);
return Result;
}
#endif

@ -0,0 +1,206 @@
#include <math.h>
b32 IsTokenValid(token Token) {
b32 Result = (Token.Type != Token_Unknown);
return Result;
}
b32 TokenEquals(token Token, char* Match) {
b32 Result = StringsMatch(Token.String, Match);
return Result;
}
void Refill(tokenizer *Tokenizer){
if(Tokenizer->Input.Count == 0) {
Tokenizer->At[0] = 0;
Tokenizer->At[1] = 0;
} else if(Tokenizer->Input.Count == 1) {
Tokenizer->At[0] = Tokenizer->Input.Data[0];
Tokenizer->At[1] = 0;
} else {
char C0 = Tokenizer->Input.Data[0];
char C1 = Tokenizer->Input.Data[1];
Tokenizer->At[0] = C0;
Tokenizer->At[1] = C1;
}
}
void AdvanceInput(tokenizer* Tokenizer, u32 Count) {
Tokenizer->TotalCount += Count;
Advance(&Tokenizer->Input, Count);
Refill(Tokenizer);
}
tokenizer Tokenize(string Data, char* Filename) {
tokenizer Result = {};
Result.Filename = Filename;
Result.LinesCount = 1;
Result.TokensCount = 1;
Result.TotalCount = 1;
Result.Input = Data;
Result.At = (char*)malloc(2);
Refill(&Result);
return(Result);
}
tokenizer Tokenize(char* Data);
tokenizer Tokenize(char* Filename);
token GetToken(tokenizer* Tokenizer) {
token Token = {};
Token.Filename = Tokenizer->Filename;
Token.TextLength = 1;
Token.Text = Tokenizer->Input;
char C = Tokenizer->At[0];
AdvanceInput(Tokenizer, 1);
switch (C) {
case '\0': { Token.Type = Token_EndOfStream; } break;
case '(': { Token.Type = Token_OpenParen; } break;
case ')': { Token.Type = Token_CloseParen; } break;
case ':': { Token.Type = Token_Colon; } break;
case ';': { Token.Type = Token_Semicolen; } break;
case '*': { Token.Type = Token_Asterik; } break;
case '[': { Token.Type = Token_OpenBracket; } break;
case ']': { Token.Type = Token_CloseBracket; } break;
case '{': { Token.Type = Token_OpenBrace; } break;
case '}': { Token.Type = Token_CloseBrace; } break;
case '=': { Token.Type = Token_Equals; } break;
case ',': { Token.Type = Token_Comma; } break;
case '|': { Token.Type = Token_Or; } break;
case '#': { Token.Type = Token_Pound; } break;
case '"': { // note(jax): We've got a string
Token.Type = Token_String;
while(Tokenizer->At[0] && Tokenizer->At[0] != '"') {
if ((Tokenizer->At[0] == '\\') && (Tokenizer->At[1])) {
AdvanceInput(Tokenizer, 1);
}
AdvanceInput(Tokenizer, 1);;
}
if (Tokenizer->At[0] == '"') {
AdvanceInput(Tokenizer, 1);
}
// Speed: This block of code is pretty hacky...
if (Token.Text.Count &&
(Token.Text.Data[0] == '"')) {
++Token.Text.Data;
--Token.Text.Count;
int TextLength = 0;
char* Temp = (char*)malloc(Token.Text.Count);
sprintf(Temp, "%s", Token.Text.Data);
while (Temp) {
if (*Temp == '\"') {
break;
}
++TextLength;
++Temp;
}
Token.String = (char*)malloc(TextLength);
sprintf(Token.String, "%.*s", (int)TextLength, Token.Text.Data);
}
} break;
default: {
if (IsSpacing(C)) {
Token.Type = Token_Space;
while (IsSpacing(Tokenizer->At[0])) {
AdvanceInput(Tokenizer, 1);
}
} else if (IsEndOfLine(C)) {
Token.Type = Token_EndOfLine;
if(((C == '\r') &&
(Tokenizer->At[0] == '\n')) ||
((C == '\n') &&
(Tokenizer->At[0] == '\r'))) {
AdvanceInput(Tokenizer, 1);
}
++Tokenizer->LinesCount;
} else if((C == '/') && (Tokenizer->At[0] == '/')) {
Token.Type = Token_Comment;
AdvanceInput(Tokenizer, 2);
while(Tokenizer->At[0] && !IsEndOfLine(Tokenizer->At[0])) {
AdvanceInput(Tokenizer, 1);
}
} else if((C == '/') &&
(Tokenizer->At[0] == '*')) {
Token.Type = Token_Comment;
AdvanceInput(Tokenizer, 2);
while(Tokenizer->At[0] && !((Tokenizer->At[0] == '*')
&& (Tokenizer->At[1] == '/'))) {
if(((Tokenizer->At[0] == '\r') && (Tokenizer->At[1] == '\n'))
|| ((Tokenizer->At[0] == '\n') && (Tokenizer->At[1] == '\r'))) {
AdvanceInput(Tokenizer, 1);
}
if (IsEndOfLine(Tokenizer->At[0])) {
++Tokenizer->LinesCount;
}
AdvanceInput(Tokenizer, 1);
}
if(Tokenizer->At[0] == '*') {
AdvanceInput(Tokenizer, 2);
}
} else if (IsAlphabetical(C)) {
Token.Type = Token_Identifier;
while (IsAlphabetical(Tokenizer->At[0]) || IsNumeric(Tokenizer->At[0]) || (Tokenizer->At[0] == '_')) {
AdvanceInput(Tokenizer, 1);
}
__int64 IndentLength = (Tokenizer->Input.Data - Token.Text.Data);
Token.String = (char*)malloc(IndentLength);
sprintf(Token.String, "%.*s", (int)IndentLength, Token.Text.Data);
} else if (IsNumeric(C)) {
f32 Number = (f32)(C - '0');
while (IsNumeric(Tokenizer->At[0])) {
f32 Digit = (f32)(Tokenizer->At[0] - '0');
Number = 10.0f*Number + Digit;
AdvanceInput(Tokenizer, 1);
}
// This float detection code is copy pasted, I have no idea how it works!
if (Tokenizer->At[0] == '.') {
AdvanceInput(Tokenizer, 1);
f32 Coefficient = 0.1f;
while (IsNumeric(Tokenizer->At[0])) {
f32 Digit = (f32)(Tokenizer->At[0] - '0');
Number += Coefficient * Digit;
Coefficient *= 0.1f;
AdvanceInput(Tokenizer, 1);
}
}
Token.Type = Token_Literal;
Token.Float = Number;
Token.Integer = (s32)Number;
} else {
Token.Type = Token_Unknown;
}
} break;
}
++Tokenizer->TokensCount;
Token.Text.Count = (Tokenizer->Input.Data - Token.Text.Data);
return Token;
}
token PeekToken(tokenizer* Tokenizer) {
tokenizer Temp = *Tokenizer;
token Result = GetToken(Tokenizer);
*Tokenizer = Temp;
return Result;
}

@ -0,0 +1,63 @@
#ifndef TOKENIZER_H
#define TOKENIZER_H
enum token_type {
Token_Unknown,
Token_OpenParen, // (
Token_CloseParen, // )
Token_Colon, // :
Token_Semicolen, // ;
Token_Asterik, // *
Token_OpenBracket, // [
Token_CloseBracket, // ]
Token_OpenBrace, // {
Token_CloseBrace, // }
Token_Equals,
Token_Comma,
Token_Or,
Token_Pound,
Token_String,
Token_Identifier,
Token_Literal, // Number
Token_Space,
Token_EndOfLine,
Token_Comment,
Token_EndOfStream,
};
struct token {
char* Filename;
token_type Type;
size_t TextLength;
string Text;
union {
char* String;
f32 Float;
s32 Integer;
};
};
struct tokenizer {
char* Filename;
u32 LinesCount;
u32 TokensCount;
u32 TotalCount;
string Input;
char* At;
};
b32 IsTokenValid(token Token);
b32 TokenEquals(token Token, char* Match);
API_EXPORT token GetToken(tokenizer* Tokenizer);
API_EXPORT token PeekToken(tokenizer* Tokenizer);
API_EXPORT tokenizer Tokenize(char* Data);
API_EXPORT tokenizer Tokenize(char* Filename);
API_EXPORT tokenizer Tokenize(string Data, char* Filename);
#endif

@ -0,0 +1,218 @@
#ifndef TYPES_H
#define TYPES_H
#include <direct.h>
#include <float.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef BUILD_WIN32
#define API_EXPORT __declspec(dllexport)
#else
#define API_EXPORT
#endif
typedef unsigned int uint;
typedef int8_t int8;
typedef int16_t int16;
typedef int32_t int32;
typedef int64_t int64;
typedef int32 bool32;
typedef uint8_t uint8;
typedef uint16_t uint16;
typedef uint32_t uint32;
typedef uint64_t uint64;
typedef float real32;
typedef double real64;
typedef int8 s8;
typedef int16 s16;
typedef int32 s32;
typedef int64 s64;
typedef bool32 b32;
typedef uint8 u8;
typedef uint16 u16;
typedef uint32 u32;
typedef uint64 u64;
typedef real32 f32;
typedef real64 f64;
typedef uintptr_t umm;
typedef intptr_t smm;
#define S32Min ((s32)0x80000000)
#define S32Max ((s32)0x7fffffff)
#define U16Max 65535
#define U32Max ((u32)-1)
#define U64Max ((u64)-1)
#define F32Max FLT_MAX
#define F32Min -FLT_MAX
#define Minimum(A, B) ((A < B) ? (A) : (B))
#define Maximum(A, B) ((A > B) ? (A) : (B))
#define For(Value) For_e((Value), ArrayCount(Value))
#define For_e(Value, End) For_se((Value), 0, (End))
#define For_se(Value, Start, End) for (int (Value) = (Start); (Value) < (End); ++(Value))
// todo(jax): Should these always be 64-bit?
#define Kilobytes(Value) (((uint64)Value) * 1024LL)
#define Megabytes(Value) (Kilobytes((uint64)Value) * 1024LL)
#define Gigabytes(Value) (Megabytes((uint64)Value) * 1024LL)
#define Terabytes(Value) (Gigabytes((uint64)Value) * 1024LL)
#define ArrayCount(Array) (sizeof(Array) / sizeof((Array)[0]))
// todo(jax): swap, min, max ... macros???
#define AlignPow2(Value, Alignment) ((Value + ((Alignment) - 1)) & ~((Alignment) - 1))
#define Align4(Value) ((Value + 3) & ~3)
#define Align8(Value) ((Value + 7) & ~7)
#define Align16(Value) ((Value + 15) & ~15)
#define Stringize(x) PrimitiveStringize(x)
#define PrimitiveStringize(x) #x
inline b32 IsPow2(u32 Value) {
return ((Value & ~(Value - 1)) == Value);
}
//Regular text
#define BLACK "\33[0;30m"
#define RED "\33[0;31m"
#define GREEN "\33[0;32m"
#define YELLOW "\33[0;33m"
#define BLUE "\33[0;34m"
#define MAGENTA "\33[0;35m"
#define CYAN "\33[0;36m"
#define WHITE "\33[0;37m"
#define LIGHT_GRAY "\33[0;37m"
#define DARK_GRAY "\33[1;30m"
//Regular bold text
#define BOLD_BLACK "\33[1;30m"
#define BOLD_RED "\33[1;31m"
#define BOLD_GREEN "\33[1;32m"
#define BOLD_YELLOW "\33[1;33m"
#define BOLD_BLUE "\33[1;34m"
#define BOLD_MAGENTA "\33[1;35m"
#define BOLD_CYAN "\33[1;36m"
#define BOLD_WHITE "\33[1;37m"
//Reset
#define RESET "\33[0m"
// note(jax): Platform-independent way to perform an assertion.
// Flat out writes to zero memory to crash the program.
// todo(jax): Create some sort of assert function that creates a message box
// like in previous engines I've worked on!
#if ENGINE_SLOW
#define Assert(Expression) if (!(Expression)) { *(int*)0=0; }
#else
#define Assert(Expression)
#endif
// A structure that encapsulates a non-terminated buffer
struct string {
u8* Data;
umm Count;
};
#include "shared.h"
//#define printf ae_printf
//#define sprintf ae_sprintf
inline u32 SafeTruncateU32(u64 Value) {
// todo(jax): Defines for min/max values
Assert(Value <= 0xFFFFFFFF);
u32 Result = (u32)Value;
return Result;
}
inline u16 SafeTruncateU16(u32 Value) {
// todo(jax): Defines for min/max values
Assert(Value <= 0xFFFF);
u16 Result = (u16)Value;
return Result;
}
inline u8 SafeTruncateU8(u64 Value) {
Assert(Value <= 0xFF);
u8 Result = (u8)Value;
return Result;
}
inline s32 SafeTruncateS32(s64 Value) {
if (Value >> 63) {
b32 IsSafeOperation = !(!(Value >> 32) && 0xffffffff);
if (!IsSafeOperation) {
printf("SafeTruncateS32: Performing unsafe truncation on '%lld'\n", Value);
}
return (s32)Value;
} else {
b32 IsSafeOperation = !((Value >> 32) && 0xffffffff);
if (!IsSafeOperation) {
printf("SafeTruncateS32: Performing unsafe truncation on '%lld'\n", Value);
}
return (s32)Value;
}
}
inline s16 SafeTruncateS16(s32 Value) {
if (Value >> 31) {
b32 IsSafeOperation = !(!(Value >> 16) && 0xffff);
if (!IsSafeOperation) {
printf("SafeTruncateS16: Performing unsafe truncation on '%d'\n", Value);
}
return (s16)Value;
} else {
b32 IsSafeOperation = !((Value >> 16) && 0xffff);
if (!IsSafeOperation) {
printf("SafeTruncateS16: Performing unsafe truncation on '%d'\n", Value);
}
return (s16)Value;
}
}
inline s8 SafeTruncateS8(s16 Value) {
if (Value >> 15) {
b32 IsSafeOperation = !(!(Value >> 8) && 0xff);
if (!IsSafeOperation) {
printf("SafeTruncateS8: Performing unsafe truncation on '%d'\n", Value);
}
return (s8)Value;
} else {
b32 IsSafeOperation = !((Value >> 8) && 0xff);
if (!IsSafeOperation) {
printf("SafeTruncateS8: Performing unsafe truncation on '%d'\n", Value);
}
return (s8)Value;
}
}
//
// note: Scalar operations
//
// todo(jax): These will eventually go into mathlib
inline real32 Square(real32 A) {
real32 Result = A*A;
return Result;
}
inline real32 Lerp(real32 A, real32 t, real32 B){
real32 Result = (1.0f - t)*A + t*B;
return Result;
}
#endif

Binary file not shown.

@ -0,0 +1,136 @@
#ifdef TEST_PASS
test "string_test" {
}
#endif
// note: Add a .test file that can be optionally included in the directory which allows a shell command to be executed prior to the test?
// todo: Finalization pass over tokenizer
// Tokenizer will go into a parse layer which swaps test "foo" { } to an actual function call
// int test_foo(test_ctx* Ctx) {}
// these function calls are put into <orig_name>_generated.cpp
// after tests are compiled and executed the generated files are deleted
// maybe have the shell command stuff inside of ctx?
// todo: Write and research testing framework!
#include <direct.h>
#include <stdio.h>
#include <stdlib.h>
#include <types.h>
#include <shared.h>
#include <tokenizer.h>
static string msg_prefix = make_string("[Test Suite] ");
string ReadEntireFileIntoMemory(char* FileName) {
string Result = { };
FILE* File = fopen(FileName, "r");
if (File) {
fseek(File, 0, SEEK_END);
Result.Count = ftell(File);
fseek(File, 0, SEEK_SET);
Result.Data = (u8*)malloc(Result.Count);
fread(Result.Data, Result.Count, 1, File);
fclose(File);
}
return Result;
}
#include <string.h>
#include <minwindows.h>
struct lexer_state {
char** FileNames;
u32 FileNameCount;
};
static lexer_state State;
void ListContents(const char* Cwd) {
char Path[MAX_PATH];
_WIN32_FIND_DATAA FindData;
HANDLE FindHandle = INVALID_HANDLE_VALUE;
//Specify a file mask. *.* = We want everything!
sprintf(Path, "%s\\*.*", Cwd);
FindHandle = FindFirstFileA(Path, &FindData);
do {
if (strcmp(FindData.cFileName, ".") != 0 && strcmp(FindData.cFileName, "..") != 0) {
sprintf(Path, "%s\\%s", Cwd, FindData.cFileName);
if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
ListContents(Path);
}
else if (Substring(Path, "src") != NULL) {
State.FileNames[State.FileNameCount] = (char*)malloc(sizeof(char) * MAX_PATH);
strcpy(State.FileNames[State.FileNameCount], Path);
++State.FileNameCount;
}
}
} while (FindNextFileA(FindHandle, &FindData));
}
int main(char** Args, int ArgCount) {
TCHAR** lppPart = {NULL}; // For GetFullPath :/
char* cwd;
cwd = _getcwd(NULL, 256);
printf("%sTest Suite v0.1\n", msg_prefix.Data);
printf("%sOperating in directory: %s\n\n", msg_prefix.Data, cwd);
// Get the names of the files we will operate on
State.FileNames = (char**)malloc(2048 * sizeof(char*));
if (Substring(cwd, "build")) {
ListContents("..\\tests");
} else {
ListContents("tests");
}
for (u32 FileIndex = 0; FileIndex < State.FileNameCount; ++FileIndex) {
char* Filename = State.FileNames[FileIndex];
char* FullFilename = (char*)malloc(MAX_PATH * sizeof(char));
GetFullPathNameA(Filename, MAX_PATH, FullFilename, lppPart);
printf("%s: \n", FullFilename);
string Contents = ReadEntireFileIntoMemory(Filename);
tokenizer Tokenizer = Tokenize(Contents, Filename);
b32 Parsing = true;
while (Parsing) {
token Token = GetToken(&Tokenizer);
switch (Token.Type) {
case Token_EndOfStream: {
Parsing = false;
} break;
case Token_Identifier: {
if (StringsMatch(Token.String, "test")) {
GetToken(&Tokenizer);
token Next = GetToken(&Tokenizer);
char* TestName = Next.String;
printf(" test '%s'\n", TestName);
}
} break;
case Token_Unknown:
default: {
} break;
}
}
}
return 1;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -0,0 +1,553 @@
/* Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright (c) 2020 by Asteria Development
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.
*/
/* ===================================================================
$
aetime works under the same premise as Casey Muratori's ctime under
the public domain. It's a small utility that allows you to time how
long your machine spends building your project. It works in the same
manner as a begin/end block in your typical profiler.
$ WARNING
---------
This software is provided UNDER AN "AS IS" BASIS. The author makes NO
WARRENTY to the RELIABILITY of this software. Take this message as your
only warning, USE AT YOUR OWN RISK. This shit could even format your
entire disk! Not my fault though.
INSTRUCTIONS
------------
On the first line of your build script, write
aetime --begin <project-name>.aet
then on the last line, write
aetime --end <project-name>.aet
and the program will output how many seconds it took to build your project!
Also if you must debug the project, you can append a '-v' flag to the end
of the argument list for additional debug output into the console.
======================================================================== */
#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
#define global static
#define u8 uint8_t
#define u32 uint32_t
#define f32 float
#define uint u32
#define GenerateMagic(a, b, c, d) (((u32)(a) << 0) | ((u32)(b) << 8) | ((u32)(c) << 16) | ((u32)(d) << 24))
#pragma pack(push, 1)
#define AET_MAGIC_VALUE GenerateMagic('a', 'e', 't', 'c')
typedef struct timer_file_header {
u32 Magic;
} timer_file_header;
typedef struct timer_file_entry {
f32 Elapsed;
} timer_file_entry;
typedef struct timer_entry_array {
u32 EntryCount;
timer_file_entry* Entries;
} timer_entry_array;
#pragma pack(pop)
int StringLength(char* String) {
int Count = 0;
while (*String++) {
++Count;
}
return Count;
}
bool StringsMatch(char* A, char* B) {
if (!A || !B) {
return false;
}
while (*A && *B) {
if (*A != *B){
return false;
}
++A;
++B;
}
if (*A != *B){
return false;
} else {
return true;
}
}
global f32 GlobalFrequency;
#define COUNTERTOMS 1.f / (GlobalFrequency / 1000.f)
#define COUNTERTOUS COUNTERTOMS * 1000.f
#define COUNTERTONS COUNTERTOUS * 1000.f
#define COUNTERTOS COUNTERTOMS / 1000.f
//
// PLATFORM SPECIFIC CODE
//
// note(jax): Eventually support Mac...
#ifdef BUILD_LINUX
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <linux/limits.h>
#include <sys/time.h>
global void Usage(char** Args) {
fprintf(stdout, "Usage: %s --begin <file>.aet [-v|--v|--verbose|-verbose]\n", Args[0]);
fprintf(stdout, " %s --end <file>.aet [-v|--v|--verbose|-verbose]\n", Args[0]);
}
// This function assumes FileName is a full path with an extension
char* GetBaseName(char* FileName) {
int BaseNameSize = 0;
char* BaseName = (char*)malloc(sizeof(char)*PATH_MAX);
char* BaseNameBegin = FileName;
char* BaseNameEnd = FileName + StringLength(FileName);
bool RightOfPeriod = false;
for (char* Scan = BaseNameBegin; *Scan; ++Scan) {
if ((Scan[0] == '\\') || (Scan[0] == '/')) {
BaseNameBegin = Scan + 1;
}
if (Scan[0] == '.') {
BaseNameEnd = Scan;
RightOfPeriod = true;
}
else if (!RightOfPeriod) {
++BaseNameSize;
}
}
memcpy(BaseName, BaseNameBegin, BaseNameSize);
BaseName[BaseNameSize] = 0;
return BaseName;
}
uint64_t rdtsc(){
unsigned int lo,hi;
__asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
return ((uint64_t)hi << 32) | lo;
}
int main(int ArgCount, char** Args) {
bool IsVerbose = false;
if (StringsMatch(Args[3], "--verbose") ||
StringsMatch(Args[3], "--v") ||
StringsMatch(Args[3], "-verbose") ||
StringsMatch(Args[3], "-v")) {
IsVerbose = true;
}
char Path[PATH_MAX];
sprintf(Path, "/tmp/");
GlobalFrequency = 1000000;
if (ArgCount >= 3) {
if (StringsMatch(Args[1], "--begin")) {
char* FileName = Args[2];
sprintf(Path + strlen(Path), "%s", FileName);
FILE* Dest = fopen(Path, "wb");
if (Dest) {
if (IsVerbose) {
printf("Writing to '%s'\n", Path);
}
timer_file_header Header = {0};
Header.Magic = AET_MAGIC_VALUE;
fwrite(&Header, sizeof(Header), 1, Dest);
timer_file_entry Entry = {0};
struct timespec Timer;
clock_gettime(CLOCK_MONOTONIC_RAW, &Timer);
Entry.Elapsed = (Timer.tv_nsec) + (Timer.tv_sec) * 1E9;
printf("Compilation started for %s\n", GetBaseName(FileName));
if (fwrite(&Entry, sizeof(Entry), 1, Dest) != 1) {
fprintf(stdout, "ERROR: Failed to append new start entry to file '%s'.\n", FileName);
} else {
fclose(Dest);
}
} else {
fprintf(stdout, "ERROR: Failed to open file '%s'.\n", FileName);
}
} else if (StringsMatch(Args[1], "--end")) {
char* FileName = Args[2];
sprintf(Path + strlen(Path), "%s", FileName);
FILE* Dest = fopen(Path, "rb");
if (Dest) {
if (IsVerbose) {
printf("Reading from '%s'\n", Path);
}
timer_file_header Header = {0};
fread(&Header, sizeof(Header), 1, Dest);
if (IsVerbose) {
printf("struct timer_file_header {\n Magic: %u\n};\n", Header.Magic);
}
timer_file_entry Entry = {0};
if(fread(&Entry, sizeof(Entry), 1, Dest) == 1) {
struct timespec Timer;
clock_gettime(CLOCK_MONOTONIC_RAW, &Timer);
f32 Elapsed = ((Timer.tv_nsec) + (Timer.tv_sec) * 1E9) - Entry.Elapsed;
f32 Milliseconds = Elapsed / 1000.f / 1000.f;
f32 Seconds = Milliseconds / 1000.f;
printf("Compilation ended: %f seconds\n", Seconds);
} else {
fprintf(stdout, "ERROR: Failed to read start entry from file '%s'.\n", FileName);
}
fclose(Dest);
if (remove(Path) != 0) {
fprintf(stdout, "ERROR: Failed to remove file '%s'.\n", FileName);
}
} else {
fprintf(stdout, "ERROR: Failed to open file '%s'.\n", FileName);
}
} else {
Usage(Args);
}
} else {
Usage(Args);
}
return 1;
}
#elif BUILD_WIN32
#include <Windows.h>
global void Usage(char** Args) {
fprintf(stderr, "Usage: %s --begin <file>.aet [-v|--v|--verbose|-verbose]\n", Args[0]);
fprintf(stderr, " %s --end <file>.aet [-v|--v|--verbose|-verbose]\n", Args[0]);
}
// This function assumes FileName is a full path with an extension
char* GetBaseName(char* FileName) {
int BaseNameSize = 0;
char* BaseName = malloc(sizeof(char)*MAX_PATH);
char* BaseNameBegin = FileName;
char* BaseNameEnd = FileName + StringLength(FileName);
bool RightOfPeriod = false;
for (char* Scan = BaseNameBegin; *Scan; ++Scan) {
if ((Scan[0] == '\\') || (Scan[0] == '/')) {
BaseNameBegin = Scan + 1;
}
if (Scan[0] == '.') {
BaseNameEnd = Scan;
RightOfPeriod = true;
}
else if (!RightOfPeriod) {
++BaseNameSize;
}
}
memcpy(BaseName, BaseNameBegin, BaseNameSize);
BaseName[BaseNameSize] = 0;
return BaseName;
}
int main(int ArgCount, char** Args) {
bool IsVerbose = false;
if (StringsMatch(Args[3], "--verbose") ||
StringsMatch(Args[3], "--v") ||
StringsMatch(Args[3], "-verbose") ||
StringsMatch(Args[3], "-v")) {
IsVerbose = true;
}
LARGE_INTEGER _F;
QueryPerformanceFrequency(&_F);
GlobalFrequency = (f32)_F.QuadPart;
char Path[MAX_PATH];
GetTempPathA(MAX_PATH, Path);
if (ArgCount >= 3) {
if (StringsMatch(Args[1], "--begin")) {
char* FileName = Args[2];
sprintf(Path, "%s\\%s", Path, FileName);
FILE* Dest = fopen(Path, "wb");
if (Dest) {
if (IsVerbose) {
printf("Writing to '%s'\n", Path);
}
timer_file_header Header = {0};
Header.Magic = AET_MAGIC_VALUE;
fwrite(&Header, sizeof(Header), 1, Dest);
LARGE_INTEGER Start;
timer_file_entry Entry = {0};
QueryPerformanceCounter(&Start);
Entry.Elapsed = (f32)Start.QuadPart;
printf("Compilation started for %s\n", GetBaseName(FileName));
if (fwrite(&Entry, sizeof(Entry), 1, Dest) != 1) {
fprintf(stderr, "ERROR: Failed to append new start entry to file '%s'.\n", FileName);
} else {
fclose(Dest);
}
} else {
fprintf(stderr, "ERROR: Failed to open file '%s'.\n", FileName);
}
} else if (StringsMatch(Args[1], "--end")) {
char* FileName = Args[2];
sprintf(Path, "%s\\%s", Path, FileName);
FILE* Dest = fopen(Path, "rb");
if (Dest) {
if (IsVerbose) {
printf("Reading from '%s'\n", Path);
}
timer_file_header Header = {0};
fread(&Header, sizeof(Header), 1, Dest);
if (IsVerbose) {
printf("struct timer_file_header {\n Magic: %u\n};\n", Header.Magic);
}
timer_file_entry Entry = {0};
if(fread(&Entry, sizeof(Entry), 1, Dest) == 1) {
LARGE_INTEGER End;
QueryPerformanceCounter(&End);
f32 Elapsed = ((f32)End.QuadPart) - Entry.Elapsed;
f32 Seconds = Elapsed * COUNTERTOS;
f32 Milliseconds = Elapsed * COUNTERTOMS;
printf("Compilation ended: %f seconds\n", Seconds);
} else {
fprintf(stderr, "ERROR: Failed to read start entry from file '%s'.\n", FileName);
}
fclose(Dest);
if (remove(Path) != 0) {
fprintf(stderr, "ERROR: Failed to remove file '%s'.\n", FileName);
}
} else {
fprintf(stderr, "ERROR: Failed to open file '%s'.\n", FileName);
}
} else {
Usage(Args);
}
} else {
Usage(Args);
}
return 1;
}
#endif

@ -0,0 +1,5 @@
@echo off
REM -Zi for debugging
cl -nologo -DBUILD_WIN32=1 -FC -O2 -TC -EHsc btime.c /link
@if ERRORLEVEL 1 exit /B 1
robocopy . ../ btime.exe > nul

@ -0,0 +1,6 @@
#!/bin/sh
set -eu
CXX=${CXX:-clang++}
${CXX} $* -Wwritable-strings -DBUILD_LINUX=1 -I. -O3 -o btime btime.c
cp btime ../btime

@ -0,0 +1,3 @@
#!/bin/sh
export PATH=$PWD/tools:$PATH
Loading…
Cancel
Save