Skip to content

andrakis/c4

 
 

Repository files navigation

C4KE - C4 Kernel Experiment

c4 was "An exercise in minimalism."

C4KE is "an exploration in over-extending minimalism." It provides a multitasking kernel, shell, compiler, and utilities.

C4KE runs under a modified c4 interpreter called c4m, which itself can be run be in an unmodified c4 interpreter. It does however run much faster under a natively compiled c4m.

Architecture

C4KE is a pre-emptive multitasking operating system. It uses a cycle-based trap to switch between tasks automatically, or custom opcodes that can manually switch tasks (cooperative multitasking.)

c4m can trap on unknown opcodes, and this feature is used to implement C4KE's extended opcodes. All registers, including return addresses, can be modified inside these trap handlers. By swapping register values from one task with those from another, task switching is accomplished transparently to running tasks.

C4KE uses a custom executable format, the C4 Relocatable. This format contains symbols and runtime patch information.

Running

To run under the extended c4m interpreter, use:

make run

To run under the original c4 interpreter, use:

make run-c4

See the Makefile for additional make targets.

Once in C4KE, type help for a command listing, or ls to display runnable programs.

Try bench or innerbench to test out how fast C4KE runs on your system.

C4M?

c4m is the C4 Multiloader, an extended version of c4 which apart from adding a few new opcodes, can also read multiple .c source files sequentially to output a single program.

Changes over C4:

  • Designed to run under unmodified C4: any version of c4 should be able to run c4m without changes.

  • Designed to be enhanced when compiled natively: basic signal handling is supported (SIGINT), terminal control to come in the future.

  • Reads all given source files on the command line, as if they were one big file.

  • Related to the above, allows multiple instances of functions and variables to exist. If there are multiple main's, it is the last one seen that gets used. This functionality was a hack to enable modularization before a preprocessor was implemented, and will be replaced by proper preprocessing soon.

  • Can use the address of functions (&func) to store into int * variables.

  • Can call C4 functions stored in int *: int *func; func = (int *)&test; func(1, 2);.

  • Trap handlers: C code called when an event occurs. c4m additionally supports some POSIX signals, like SIGINT.

  • Traps on unknown opcodes for custom handling or new opcodes implemented in C.

  • Cycle based trap for task switching.

  • Opcodes JSRI, JSRS: JSR variants that use a global variable (I) or a stack variable (S). Generated by c4m.

  • Builtin __c4_opcode: Allows custom opcodes to be executed, used heavily by u0.h.

  • Builtin __c4_jmp: Unconditional jump to target address, used by c4ke.c for custom opcode handling.

  • Builtin __c4_adjust: Adjust the stack manually, allowing stack allocation/deallocation at will.

  • Builtin __opcode, __builtin: Allows querying the interpreter's opcodes and builtin functions.

  • Fixes a potential stack overflow when using printf()

Interesting files

  • c4ke.c - The C4 Kernel Experiment, main source file.

  • load-c4r.c - The "C4 Relocatable" executable loader and runner.

  • c4cc.c - A compiler for C4 - see also asm-c4r.c, for compiling to C4R format, or asm-js.c for compiling to JavaScript.

  • c4le.c - A C4 Line Editor.

  • c4m.c - An updated C4 interpreter with new opcodes and support for traps. Can be run under C4.

  • c4.c - The original C4 interpreter, with only enough modifications to compile portably.

  • u0.h - User runtime for C4KE programs, interfaces with C4KE via extended opcodes.

  • mandel.c - A port of Johnlon's Integer Mandelbrot to run on C4KE

  • tests - Various test programs

Other experiments

  • asm-js.c - A module for C4CC that compiles to files than can run under C4JS.

  • libjs/ - A JavaScript port of C4. Incomplete, slow, and cannot run C4KE.

  • OISC4: One Instruction Set Computer for C4, an attempt to implement C4 using a single instruction. The compiler does most of the heavy lifting. The interpreter is fairly simple.

    • Only partially implemented. Most of the logic is there but subtle bugs prevent working code.

    • The instruction is: add source, incremental, destination.

      • source is a memory address to read from.

      • incremental is a literal value to add to the value read from source.

      • destination is the memory address to write the result to.

    • Additionally, flags are set to indicate whether the final value written to destination was:

      • LT0: less than 0

      • EQ0: equal to 0

      • GT0: greater than 0

      • Flags are either 0 to indicate false, or 3 (the size of an instruction in words) to indicate true

    • Code can arbritrarily jump by storing results into PC, or add the value of one of the above flags followed by two jump instructions. The first would execute if the flag was false, and the second would if the flag was true:

       EQ0, LABEL_1, PC                ; Jump using flag and address of LABEL_1
       LABEL_1: ABS0, NOT_EQ0, PC      ; was false, jump to NOT_EQ0
                ABS0, WAS_EQ0, PC      ; was true, jump to WAS_EQ0
       NOT_EQ0: ; do something
                ABS0, DONE, PC         ; unconditional jump
       WAS_EQ0: ; do something else
       DONE:
      
    • Instructions in memory are able to be altered, and combined with jumping allow for later instructions to be modified based on conditions. This is used heavily by the compiler to implement many C4 instructions.

      • For example, to implement the LOAD INT C4 instruction (LI), the value of a memory location needs to be dereferenced. This is done by altering a future instruction to change the source to the value read from memory:

               ABS0, some_variable, LOC1   ; Read from a variable (a memory address really),
                                           ; which must use ABS0 to get the true memory address,
                                           ; and alter the source in the next instruction.
         LOC1: PLACEHOLDER, 0, R0          ; Read from altered source and store into register R0.
        
        • PLACEHOLDER is just a visual reference to know where something will be updated. It could also be a different part of the instruction.
    • By using such logic, all of C4 is able to be implemented, though cheated a little by the use of DEVICEs.

    • DEVICEs: theoratical hardware devices listening on specific memory addresses, and triggering some sort of behaviour when read or written.

      • Currently all math and IO operations are implemented as DEVICEs.

About

C4KE - The C4 Kernel Experiment

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • C 94.6%
  • JavaScript 4.2%
  • Makefile 1.2%