Compare commits
No commits in common. "ee162a70ed4d7bffaf3d869edfe0aef0ca672872" and "ad5c2b856a5f4f81676f2dd7a4b0ee1cf43d595f" have entirely different histories.
ee162a70ed
...
ad5c2b856a
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@ -7,7 +7,7 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "${workspaceFolder}/bin/tcpu",
|
"program": "${workspaceFolder}/bin/tcpu",
|
||||||
"windows": { "program": "${workspaceFolder}/bin/tcpu.exe" },
|
"windows": { "program": "${workspaceFolder}/bin/tcpu.exe" },
|
||||||
"args": [ "-c", "../examples/conditional_jump.tasm", "o.bin", "--debug", "-i", "o.bin" ],
|
"args": [ "-c", "../examples/s.tasm", "o.bin", "--debug" ],
|
||||||
"cwd": "${workspaceFolder}/bin",
|
"cwd": "${workspaceFolder}/bin",
|
||||||
"preLaunchTask": "build_exec_dbg",
|
"preLaunchTask": "build_exec_dbg",
|
||||||
"stopAtEntry": false,
|
"stopAtEntry": false,
|
||||||
|
|||||||
7
TODO.md
7
TODO.md
@ -1,9 +1,2 @@
|
|||||||
# TODO List
|
# TODO List
|
||||||
- add negative number arguments support
|
|
||||||
- change section binary format:
|
|
||||||
1. code
|
|
||||||
2. exit instruction with code ERR_U_FORGOT_TO_CALL_EXIT
|
|
||||||
3. data
|
|
||||||
- add padding to compilation
|
|
||||||
- arguments validation for each instruction
|
- arguments validation for each instruction
|
||||||
- VM debug mode
|
|
||||||
|
|||||||
@ -1,32 +0,0 @@
|
|||||||
/*
|
|
||||||
Example of behavior change depending on some condition
|
|
||||||
*/
|
|
||||||
|
|
||||||
.main:
|
|
||||||
movc ax 1
|
|
||||||
movc bx 2
|
|
||||||
gt cx ax bx
|
|
||||||
jnz @true cx
|
|
||||||
jz @false cx
|
|
||||||
|
|
||||||
.true:
|
|
||||||
const8 true.msg "true\n"
|
|
||||||
movc cx @true.msg
|
|
||||||
movc dx #true.msg
|
|
||||||
jmp @print
|
|
||||||
|
|
||||||
.false
|
|
||||||
const8 false.msg "false\n"
|
|
||||||
movc cx @false.msg
|
|
||||||
movc dx #false.msg
|
|
||||||
jmp @print
|
|
||||||
|
|
||||||
.print:
|
|
||||||
movc ax 1
|
|
||||||
movc bx 1
|
|
||||||
sys
|
|
||||||
jmp @end
|
|
||||||
|
|
||||||
.end:
|
|
||||||
movc ax 0
|
|
||||||
exit
|
|
||||||
@ -1,7 +1,12 @@
|
|||||||
/*
|
/*
|
||||||
Example of self-repeating code section
|
Example of graphical application
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.main:
|
.main:
|
||||||
//TODO loop example
|
|
||||||
|
le
|
||||||
|
gt
|
||||||
|
eq
|
||||||
|
jif
|
||||||
|
jel
|
||||||
exit
|
exit
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
.data:
|
.data:
|
||||||
// named array of 8-bit values
|
// named array of 8-bit values
|
||||||
const8 msg "Hello, World"
|
const8 msg "Hello, World :3\n\0"
|
||||||
|
|
||||||
.main:
|
.main:
|
||||||
movc ax 1; // sys_write
|
movc ax 1; // sys_write
|
||||||
|
|||||||
@ -27,8 +27,6 @@ void BinaryObject_construct(BinaryObject* ptr){
|
|||||||
ptr->section_list = List_CompiledSection_alloc(64);
|
ptr->section_list = List_CompiledSection_alloc(64);
|
||||||
HashMap_CompiledSectionPtr_alloc(&ptr->section_map);
|
HashMap_CompiledSectionPtr_alloc(&ptr->section_map);
|
||||||
HashMap_ConstDataProps_alloc(&ptr->const_data_map);
|
HashMap_ConstDataProps_alloc(&ptr->const_data_map);
|
||||||
ptr->main_section = NULL;
|
|
||||||
ptr->total_size = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BinaryObject_free(BinaryObject* ptr){
|
void BinaryObject_free(BinaryObject* ptr){
|
||||||
|
|||||||
@ -57,7 +57,6 @@ HashMap_declare(CompiledSectionPtr);
|
|||||||
typedef struct BinaryObject {
|
typedef struct BinaryObject {
|
||||||
List_CompiledSection section_list;
|
List_CompiledSection section_list;
|
||||||
HashMap_CompiledSectionPtr section_map;
|
HashMap_CompiledSectionPtr section_map;
|
||||||
NULLABLE(CompiledSection*) main_section;
|
|
||||||
HashMap_ConstDataProps const_data_map;
|
HashMap_ConstDataProps const_data_map;
|
||||||
u32 total_size;
|
u32 total_size;
|
||||||
} BinaryObject;
|
} BinaryObject;
|
||||||
|
|||||||
@ -124,9 +124,6 @@ static bool compileSection(Compiler* cmp, Section* sec){
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool compileBinary(Compiler* cmp){
|
static bool compileBinary(Compiler* cmp){
|
||||||
returnErrorIf_auto(cmp->state != CompilerState_Parsing);
|
|
||||||
cmp->state = CompilerState_Compiling;
|
|
||||||
|
|
||||||
for(u32 i = 0; i < cmp->ast.sections.len; i++){
|
for(u32 i = 0; i < cmp->ast.sections.len; i++){
|
||||||
SectionPtr sec = &cmp->ast.sections.data[i];
|
SectionPtr sec = &cmp->ast.sections.data[i];
|
||||||
if(!compileSection(cmp, sec)){
|
if(!compileSection(cmp, sec)){
|
||||||
@ -140,7 +137,6 @@ static bool compileBinary(Compiler* cmp){
|
|||||||
if(main_sec_ptrptr == NULL){
|
if(main_sec_ptrptr == NULL){
|
||||||
returnError("no 'main' section was defined");
|
returnError("no 'main' section was defined");
|
||||||
}
|
}
|
||||||
cmp->binary.main_section = *main_sec_ptrptr;
|
|
||||||
|
|
||||||
// create linked list of CompiledSection where main is the first
|
// create linked list of CompiledSection where main is the first
|
||||||
CompiledSection* prev_sec = *main_sec_ptrptr;
|
CompiledSection* prev_sec = *main_sec_ptrptr;
|
||||||
@ -164,8 +160,6 @@ static bool compileBinary(Compiler* cmp){
|
|||||||
returnError("duplicate named data '%s'", str_copy(cd.name).data);
|
returnError("duplicate named data '%s'", str_copy(cd.name).data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
prev_sec = sec;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// insert calculated offsets into sections
|
// insert calculated offsets into sections
|
||||||
@ -200,16 +194,26 @@ static bool compileBinary(Compiler* cmp){
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool writeBinaryFile(Compiler* cmp, FILE* f){
|
static bool writeBinaryFile(Compiler* cmp, FILE* f){
|
||||||
returnErrorIf_auto(cmp->state != CompilerState_Compiling);
|
returnErrorIf_auto(cmp->state != CompilerState_Parsing);
|
||||||
|
cmp->state = CompilerState_Compiling;
|
||||||
|
|
||||||
CompiledSection* sec = cmp->binary.main_section;
|
if(!compileBinary(cmp)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CompiledSection** main_sec_ptrptr = HashMap_CompiledSectionPtr_tryGetPtr(&cmp->binary.section_map, STR("main"));
|
||||||
|
if(main_sec_ptrptr == NULL){
|
||||||
|
returnError("no 'main' section was defined");
|
||||||
|
}
|
||||||
|
CompiledSection* sec = *main_sec_ptrptr;
|
||||||
while(sec){
|
while(sec){
|
||||||
fwrite(sec->bytes.data, 1, sec->bytes.len, f);
|
fwrite(sec->bytes.data, 1, sec->bytes.len, f);
|
||||||
fflush(f);
|
|
||||||
sec = sec->next;
|
sec = sec->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: print warnings for unused sections
|
//TODO: print warnings for unused sections
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,7 +289,6 @@ bool Compiler_compile(Compiler* cmp, cstr source_file_name, cstr out_file_name,
|
|||||||
if(debug_log)
|
if(debug_log)
|
||||||
printf("===================================[parsing]===================================\n");
|
printf("===================================[parsing]===================================\n");
|
||||||
success = Compiler_parse(cmp);
|
success = Compiler_parse(cmp);
|
||||||
|
|
||||||
if (debug_log){
|
if (debug_log){
|
||||||
printf("-------------------------------------[AST]-------------------------------------\n");
|
printf("-------------------------------------[AST]-------------------------------------\n");
|
||||||
for(u32 i = 0; i < cmp->ast.sections.len; i++){
|
for(u32 i = 0; i < cmp->ast.sections.len; i++){
|
||||||
@ -306,7 +309,6 @@ bool Compiler_compile(Compiler* cmp, cstr source_file_name, cstr out_file_name,
|
|||||||
Operation* op = &sec->code.data[j];
|
Operation* op = &sec->code.data[j];
|
||||||
const Instruction* instr = Instruction_getByOpcode(op->opcode);
|
const Instruction* instr = Instruction_getByOpcode(op->opcode);
|
||||||
if(instr == NULL){
|
if(instr == NULL){
|
||||||
fclose(f);
|
|
||||||
returnError("unknown opcode: %i", op->opcode)
|
returnError("unknown opcode: %i", op->opcode)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,7 +352,6 @@ bool Compiler_compile(Compiler* cmp, cstr source_file_name, cstr out_file_name,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!success){
|
if(!success){
|
||||||
fclose(f);
|
fclose(f);
|
||||||
return false;
|
return false;
|
||||||
@ -358,25 +359,6 @@ bool Compiler_compile(Compiler* cmp, cstr source_file_name, cstr out_file_name,
|
|||||||
|
|
||||||
if(debug_log)
|
if(debug_log)
|
||||||
printf("==================================[compiling]==================================\n");
|
printf("==================================[compiling]==================================\n");
|
||||||
success = compileBinary(cmp);
|
|
||||||
|
|
||||||
if(debug_log){
|
|
||||||
for(u32 i = 0; i < cmp->binary.section_list.len; i++){
|
|
||||||
CompiledSection* sec = &cmp->binary.section_list.data[i];
|
|
||||||
str tmpstr = str_copy(sec->name);
|
|
||||||
printf("compiled section '%s' to %u bytes with offset 0x%x\n", tmpstr.data, sec->bytes.len, sec->offset);
|
|
||||||
free(tmpstr.data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!success){
|
|
||||||
fclose(f);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if(debug_log)
|
|
||||||
printf("----------------------------[writing output to file]---------------------------\n");
|
|
||||||
success = writeBinaryFile(cmp, f);
|
success = writeBinaryFile(cmp, f);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
if(success){
|
if(success){
|
||||||
|
|||||||
@ -130,7 +130,7 @@ static void readArguments(Compiler* cmp){
|
|||||||
|
|
||||||
// string argument reading
|
// string argument reading
|
||||||
if(quot != '\0'){
|
if(quot != '\0'){
|
||||||
if(c == quot && (cmp->code.data[cmp->pos - 1] != '\\' || cmp->code.data[cmp->pos - 2] == '\\')){
|
if(c == quot && cmp->code.data[cmp->pos - 1] != '\\'){
|
||||||
quot = '\0';
|
quot = '\0';
|
||||||
}
|
}
|
||||||
else if(c == '\r' || c == '\n'){
|
else if(c == '\r' || c == '\n'){
|
||||||
|
|||||||
@ -44,7 +44,6 @@ static NULLABLE(str) resolveEscapeSequences(Compiler* cmp, str src){
|
|||||||
c = src.data[i];
|
c = src.data[i];
|
||||||
if(c == '\\'){
|
if(c == '\\'){
|
||||||
escaped = !escaped;
|
escaped = !escaped;
|
||||||
if(escaped)
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,17 +69,11 @@ static NULLABLE(str) resolveEscapeSequences(Compiler* cmp, str src){
|
|||||||
case 'e':
|
case 'e':
|
||||||
StringBuilder_append_char(&sb, '\e');
|
StringBuilder_append_char(&sb, '\e');
|
||||||
break;
|
break;
|
||||||
case '"':
|
|
||||||
case '\'':
|
|
||||||
StringBuilder_append_char(&sb, c);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
setError_unexpectedTokenChar(cmp->tokens.data[cmp->tok_i], i);
|
setError_unexpectedTokenChar(cmp->tokens.data[cmp->tok_i], i);
|
||||||
StringBuilder_free(&sb);
|
StringBuilder_free(&sb);
|
||||||
return str_null;
|
return str_null;
|
||||||
}
|
}
|
||||||
|
|
||||||
escaped = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return StringBuilder_getStr(&sb);
|
return StringBuilder_getStr(&sb);
|
||||||
|
|||||||
@ -1,40 +0,0 @@
|
|||||||
#include "impl_macros.h"
|
|
||||||
|
|
||||||
// JUMP [destination address]
|
|
||||||
i32 JMP_impl(VM* vm){
|
|
||||||
u32 dst_addr = 0;
|
|
||||||
readVar(dst_addr);
|
|
||||||
|
|
||||||
vm->current_pos = dst_addr;
|
|
||||||
|
|
||||||
return sizeof(dst_addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// JNZ [destination address] [condition register]
|
|
||||||
i32 JNZ_impl(VM* vm){
|
|
||||||
u32 dst_addr = 0;
|
|
||||||
readVar(dst_addr);
|
|
||||||
u8 cond_register_i = 0;
|
|
||||||
readRegisterVar(cond_register_i);
|
|
||||||
|
|
||||||
if(vm->registers.array[cond_register_i].u32v0 != 0){
|
|
||||||
vm->current_pos = dst_addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sizeof(dst_addr) + sizeof(cond_register_i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// JZ [destination address] [condition register]
|
|
||||||
i32 JZ_impl(VM* vm){
|
|
||||||
u32 dst_addr = 0;
|
|
||||||
readVar(dst_addr);
|
|
||||||
u8 cond_register_i = 0;
|
|
||||||
readRegisterVar(cond_register_i);
|
|
||||||
|
|
||||||
if(vm->registers.array[cond_register_i].u32v0 == 0){
|
|
||||||
vm->current_pos = dst_addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sizeof(dst_addr) + sizeof(cond_register_i);
|
|
||||||
}
|
|
||||||
@ -22,7 +22,7 @@ i32 NAME##_impl (VM* vm) {\
|
|||||||
vm->registers.array[src_register_i].u64v = OPERATOR vm->registers.array[src_register_i].u64v;\
|
vm->registers.array[src_register_i].u64v = OPERATOR vm->registers.array[src_register_i].u64v;\
|
||||||
break;\
|
break;\
|
||||||
}\
|
}\
|
||||||
return sizeof(src_register_i) /*+ sizeof(value_size)*/;\
|
return sizeof(src_register_i) + sizeof(value_size);\
|
||||||
}
|
}
|
||||||
|
|
||||||
#define logicalOperator2Impl(NAME, OPERATOR)\
|
#define logicalOperator2Impl(NAME, OPERATOR)\
|
||||||
@ -48,7 +48,7 @@ i32 NAME##_impl (VM* vm) {\
|
|||||||
vm->registers.array[dst_register_i].u64v OPERATOR##= vm->registers.array[src_register_i].u64v;\
|
vm->registers.array[dst_register_i].u64v OPERATOR##= vm->registers.array[src_register_i].u64v;\
|
||||||
break;\
|
break;\
|
||||||
}\
|
}\
|
||||||
return sizeof(dst_register_i) + sizeof(src_register_i) /*+ sizeof(value_size)*/;\
|
return sizeof(dst_register_i) + sizeof(src_register_i) + sizeof(value_size);\
|
||||||
}
|
}
|
||||||
|
|
||||||
#define logicalOperator3Impl(NAME, OPERATOR)\
|
#define logicalOperator3Impl(NAME, OPERATOR)\
|
||||||
@ -75,7 +75,7 @@ i32 NAME##_impl (VM* vm) {\
|
|||||||
vm->registers.array[dst_register_i].u64v = vm->registers.array[src0_register_i].u64v OPERATOR vm->registers.array[src1_register_i].u64v;\
|
vm->registers.array[dst_register_i].u64v = vm->registers.array[src0_register_i].u64v OPERATOR vm->registers.array[src1_register_i].u64v;\
|
||||||
break;\
|
break;\
|
||||||
}\
|
}\
|
||||||
return sizeof(dst_register_i) + sizeof(src0_register_i) + sizeof(src1_register_i) /*+ sizeof(value_size)*/;\
|
return sizeof(dst_register_i) + sizeof(src0_register_i) + sizeof(src1_register_i) + sizeof(value_size);\
|
||||||
}
|
}
|
||||||
|
|
||||||
logicalOperator3Impl(EQ, ==)
|
logicalOperator3Impl(EQ, ==)
|
||||||
|
|||||||
@ -23,7 +23,7 @@ i32 NAME##_impl (VM* vm) {\
|
|||||||
vm->registers.array[dst_register_i].u64v OPERATOR##= vm->registers.array[src_register_i].u64v;\
|
vm->registers.array[dst_register_i].u64v OPERATOR##= vm->registers.array[src_register_i].u64v;\
|
||||||
break;\
|
break;\
|
||||||
}\
|
}\
|
||||||
return sizeof(dst_register_i) + sizeof(src_register_i) /*+ sizeof(value_size)*/;\
|
return sizeof(dst_register_i) + sizeof(src_register_i) + sizeof(value_size);\
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ADD [dst_register] [src_register]
|
/// ADD [dst_register] [src_register]
|
||||||
|
|||||||
@ -27,8 +27,8 @@ i32 XOR_impl(VM* vm);
|
|||||||
i32 AND_impl(VM* vm);
|
i32 AND_impl(VM* vm);
|
||||||
|
|
||||||
i32 JMP_impl(VM* vm);
|
i32 JMP_impl(VM* vm);
|
||||||
i32 JNZ_impl(VM* vm);
|
i32 JIF_impl(VM* vm);
|
||||||
i32 JZ_impl(VM* vm);
|
i32 JEL_impl(VM* vm);
|
||||||
|
|
||||||
|
|
||||||
Array_declare(Instruction);
|
Array_declare(Instruction);
|
||||||
@ -59,8 +59,8 @@ static const Array_Instruction instructions_array = ARRAY(Instruction, {
|
|||||||
Instruction_construct(AND),
|
Instruction_construct(AND),
|
||||||
|
|
||||||
Instruction_construct(JMP),
|
Instruction_construct(JMP),
|
||||||
Instruction_construct(JNZ),
|
Instruction_construct(JIF),
|
||||||
Instruction_construct(JZ),
|
Instruction_construct(JEL),
|
||||||
});
|
});
|
||||||
|
|
||||||
const Instruction* Instruction_getByOpcode(Opcode opcode){
|
const Instruction* Instruction_getByOpcode(Opcode opcode){
|
||||||
|
|||||||
@ -32,8 +32,8 @@ typedef enum __attribute__((__packed__)) Opcode {
|
|||||||
Opcode_AND,
|
Opcode_AND,
|
||||||
|
|
||||||
Opcode_JMP,
|
Opcode_JMP,
|
||||||
Opcode_JNZ,
|
Opcode_JIF,
|
||||||
Opcode_JZ,
|
Opcode_JEL,
|
||||||
} Opcode;
|
} Opcode;
|
||||||
|
|
||||||
typedef struct Instruction {
|
typedef struct Instruction {
|
||||||
|
|||||||
46
src/main.c
46
src/main.c
@ -10,7 +10,53 @@
|
|||||||
i32 compileSources(cstr source_file, cstr out_file, bool debug_log);
|
i32 compileSources(cstr source_file, cstr out_file, bool debug_log);
|
||||||
i32 bootFromImage(cstr image_file);
|
i32 bootFromImage(cstr image_file);
|
||||||
|
|
||||||
|
#define assert_sdl(EXPR) if(!(EXPR)) { printf("assert failed: %s\nSDL_Error: %s\n", #EXPR, SDL_GetError()); return false; }
|
||||||
|
|
||||||
|
bool test_display(){
|
||||||
|
Display* display = Display_create(STR("le display"), 1600, 900, DisplayFlags_Default);
|
||||||
|
assert_sdl(display != NULL);
|
||||||
|
|
||||||
|
i64 fps = 60;
|
||||||
|
i64 nsec_per_frame = 1e9 / fps;
|
||||||
|
for(u32 i = 0; i < 600; i++){
|
||||||
|
static i64 last_frame_time = 0;
|
||||||
|
i64 current_frame_time = SDL_GetTicksNS();
|
||||||
|
i64 delta_time = current_frame_time - last_frame_time;
|
||||||
|
last_frame_time = current_frame_time;
|
||||||
|
|
||||||
|
SDL_Event event;
|
||||||
|
while(SDL_PollEvent(&event)) {
|
||||||
|
if(event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED){
|
||||||
|
printfe("event WINDOW_CLOSE");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if(event.type == SDL_EVENT_QUIT){
|
||||||
|
printfe("event QUIT");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_sdl(Display_setDrawingColor(display, ColorRGBA_create(200, 200, 200, 255)));
|
||||||
|
assert_sdl(Display_clear(display));
|
||||||
|
assert_sdl(Display_setDrawingColor(display, ColorRGBA_create(240, 40, 40, 255)));
|
||||||
|
assert_sdl(Display_fillRect(display, Rect_create(i, i, 64, 64)));
|
||||||
|
assert_sdl(Display_swapBuffers(display));
|
||||||
|
|
||||||
|
i64 time_ellapsed = SDL_GetTicksNS() - last_frame_time;
|
||||||
|
i64 delay = nsec_per_frame - time_ellapsed;
|
||||||
|
if (delay > 0) {
|
||||||
|
SDL_DelayNS(delay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Display_destroy(display);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
i32 main(const i32 argc, cstr* argv){
|
i32 main(const i32 argc, cstr* argv){
|
||||||
|
test_display();
|
||||||
|
return 0;
|
||||||
|
|
||||||
if(argc < 2){
|
if(argc < 2){
|
||||||
printfe("ERROR: no arguments provided. Use --help to know more.\n");
|
printfe("ERROR: no arguments provided. Use --help to know more.\n");
|
||||||
return 1;
|
return 1;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user