From: Thomas Walker Lynch Date: Fri, 14 Feb 2025 15:59:22 +0000 (+0000) Subject: checkpoint before making source generic with m4 X-Git-Url: https://git.reasoningtechnology.com/style/static/gitweb.js?a=commitdiff_plain;h=ba1c28e3dd17a6e182486a8b9b7ae0b055b3f4a1;p=N checkpoint before making source generic with m4 --- diff --git a/developer/amd64/test_N32 b/developer/amd64/test_N32 deleted file mode 100755 index 36150b2..0000000 Binary files a/developer/amd64/test_N32 and /dev/null differ diff --git a/developer/amd64/test_setup b/developer/amd64/test_setup deleted file mode 100755 index d5834b8..0000000 Binary files a/developer/amd64/test_setup and /dev/null differ diff --git "a/developer/cc\360\237\226\211/N32.lib.c" "b/developer/cc\360\237\226\211/N32.lib.c" deleted file mode 100644 index fba3c21..0000000 --- "a/developer/cc\360\237\226\211/N32.lib.c" +++ /dev/null @@ -1,457 +0,0 @@ -/* - N32 - a processor native type - - For binary operations: a op b -> c - - See the document on the proper use of the Natural types. - - On the subject of multiple pointers indicating the same location in memory: - - When a routine has multiple results, and one or more of the result location - pointers point to the same storage, the routine will either return an error - status, or have defined behavior. - - When a routine has multiple operands, in any combination, those - pointers can point to the same location, and the routine will - function as advertised. - - When an operand functions as both an input and a result, perhaps due - to a result pointer pointing to the same place as an operand - pointer, the routine will function as advertised. (Internally the - routine might make a temporary copy of the operand to accomplish - this.) - -*/ - -#define N32·DEBUG - -#ifndef FACE -#define N32·IMPLEMENTATION -#define FACE -#endif - -//-------------------------------------------------------------------------------- -// Interface - -#ifndef N32·FACE -#define N32·FACE - - #include - #include - #include - #include - - //---------------------------------------- - // Instance Data (Declaration Only) - - typedef uint32_t Extent; - typedef uint32_t Digit; - - typedef struct N32·T N32·T; - - extern N32·T *N32·zero; - extern N32·T *N32·one; - extern N32·T *N32·all_one_bit; - extern N32·T *N32·lsb; - extern N32·T *N32·msb; - - //---------------------------------------- - // Return/Error Status and handlers - - typedef enum{ - N32·Status·ok = 0 - ,N32·Status·overflow = 1 - ,N32·Status·accumulator1_overflow = 2 - ,N32·Status·carry = 3 - ,N32·Status·borrow = 4 - ,N32·Status·undefined_divide_by_zero = 5 - ,N32·Status·undefined_modulus_zero = 6 - ,N32·Status·gt_max_shift_count = 7 - ,N32·Status·spill_eq_operand = 8 // not currently signaled, result will be spill value - ,N32·Status·one_word_product = 9 - ,N32·Status·two_word_product = 10 - } N32·Status; - - typedef enum{ - N32·Order_lt = -1 - ,N32·Order_eq = 0 - ,N32·Order_gt = 1 - } N32·Order; - - typedef N32·T *( *N32·Allocate_MemoryFault )(Extent); - - //---------------------------------------- - // Interface - - typedef struct{ - - N32·T *(*allocate_array_zero)(Extent, N32·Allocate_MemoryFault); - N32·T *(*allocate_array)(Extent, N32·Allocate_MemoryFault); - void (*deallocate)(N32·T*); - - void (*copy)(N32·T*, N32·T*); - void (*bit_and)(N32·T*, N32·T*, N32·T*); - void (*bit_or)(N32·T*, N32·T*, N32·T*); - void (*bit_complement)(N32·T*, N32·T*); - void (*bit_twos_complement)(N32·T*, N32·T*); - N32·Order (*compare)(N32·T*, N32·T*); - bool (*lt)(N32·T*, N32·T*); - bool (*gt)(N32·T*, N32·T*); - bool (*eq)(N32·T*, N32·T*); - bool (*eq_zero)(N32·T*); - N32·Status (*accumulate)(N32·T *accumulator1 ,N32·T *accumulator0 ,...); - N32·Status (*add)(N32·T*, N32·T*, N32·T*); - bool (*increment)(N32·T *a); - N32·Status (*subtract)(N32·T*, N32·T*, N32·T*); - N32·Status (*multiply)(N32·T*, N32·T*, N32·T*, N32·T*); - N32·Status (*divide)(N32·T*, N32·T*, N32·T*, N32·T*); - N32·Status (*modulus)(N32·T*, N32·T*, N32·T*); - N32·Status (*shift_left)(Extent, N32·T*, N32·T*, N32·T*); - N32·Status (*shift_right)(Extent, N32·T*, N32·T*, N32·T*); - N32·Status (*arithmetic_shift_right)(Extent, N32·T*, N32·T*); - - N32·T* (*access)(N32·T*, Extent); - void (*from_uint32)(N32·T *destination ,uint32_t value); - } N32·Λ; - - Local const N32·Λ N32·λ; // initialized in the LOCAL section - -#endif - -//-------------------------------------------------------------------------------- -// Implementation - -#ifdef N32·IMPLEMENTATION - - // this part goes into the library - #ifndef LOCAL - - #include - #include - - struct N32·T{ - Digit d0; - }; - - N32·T N32·constant[4] = { - {.d0 = 0}, - {.d0 = 1}, - {.d0 = ~(uint32_t)0}, - {.d0 = 1 << 31} - }; - - N32·T *N32·zero = &N32·constant[0]; - N32·T *N32·one = &N32·constant[1]; - N32·T *N32·all_one_bit = &N32·constant[2]; - N32·T *N32·msb = &N32·constant[3]; - N32·T *N32·lsb = &N32·constant[1]; - - // the allocate an array of N32 - N32·T *N32·allocate_array(Extent extent ,N32·Allocate_MemoryFault memory_fault){ - N32·T *instance = malloc((extent + 1) * sizeof(N32·T) ); - if(!instance){ - return memory_fault ? memory_fault(extent) : NULL; - } - return instance; - } - - N32·T *N32·allocate_array_zero(Extent extent ,N32·Allocate_MemoryFault memory_fault){ - N32·T *instance = calloc( extent + 1 ,sizeof(N32·T) ); - if(!instance){ - return memory_fault ? memory_fault(extent) : NULL; - } - return instance; - } - - void N32·deallocate(N32·T *unencumbered){ - free(unencumbered); - } - - #endif - - // This part is included after the library user's code - #ifdef LOCAL - - // instance - - struct N32·T{ - Digit d0; - }; - - // temporary variables - // making these LOCAL rather than reserving one block in the library is thread safe - // allocating a block once is more efficient - // library code writes these, they are not on the interface - - Local N32·T N32·t[4]; - - - // allocation - - extern N32·T *N32·allocate_array(Extent, N32·Allocate_MemoryFault); - extern N32·T *N32·allocate_array_zero(Extent, N32·Allocate_MemoryFault); - extern void N32·deallocate(N32·T *); - - // so the user can access numbers in an array allocation - Local N32·T* N32·access(N32·T *array ,Extent index){ - return &array[index]; - } - - Local void N32·from_uint32(N32·T *destination ,uint32_t value){ - if(destination == NULL) return; - destination->d0 = value; - } - - // copy, convenience copy - - Local void N32·copy(N32·T *destination ,N32·T *source){ - if(source == destination) return; // that was easy! - *destination = *source; - } - - Local void N32·set_to_zero(N32·T *instance){ - instance->d0 = 0; - } - - Local void N32·set_to_one(N32·T *instance){ - instance->d0 = 1; - } - - // bit operations - - Local void N32·bit_and(N32·T *result, N32·T *a, N32·T *b){ - result->d0 = a->d0 & b->d0; - } - - // result can be one of the operands - Local void N32·bit_or(N32·T *result, N32·T *a, N32·T *b){ - result->d0 = a->d0 | b->d0; - } - - // result can the same as the operand - Local void N32·bit_complement(N32·T *result, N32·T *a){ - result->d0 = ~a->d0; - } - - // result can the same as the operand - Local void N32·bit_twos_complement(N32·T *result ,N32·T *a){ - result->d0 = ~a->d0 + 1; - } - - // test functions - - Local N32·Order N32·compare(N32·T *a, N32·T *b){ - if(a->d0 < b->d0) return N32·Order_lt; - if(a->d0 > b->d0) return N32·Order_gt; - return N32·Order_eq; - } - - Local bool N32·lt(N32·T *a ,N32·T *b){ - return a->d0 < b->d0; - } - - Local bool N32·gt(N32·T *a ,N32·T *b){ - return a->d0 > b->d0; - } - - Local bool N32·eq(N32·T *a ,N32·T *b){ - return a->d0 == b->d0; - } - - Local bool N32·eq_zero(N32·T *a){ - return a->d0 == 0; - } - - - // arithmetic operations - - // For a large number of summands for the lower precision Natural implementations, for accumulate/add/sub, the 'overflow' operand could overflow and thus this routine will halt and return N32·Status·accumulator1_overflow - // - // When accumulator1 and accumulator0 point to the same location, the result is the accumulator1 value. - Local N32·Status N32·accumulate(N32·T *accumulator1 ,N32·T *accumulator0 ,...){ - - va_list args; - va_start(args ,accumulator0); - uint32_t sum = accumulator0->d0; - uint32_t carry = 0; - N32·T *current; - - while( (current = va_arg(args ,N32·T *)) ){ - sum += current->d0; - if(sum < current->d0){ // Accumulator1 into carry - (carry)++; - if(carry == 0){ - va_end(args); - return N32·Status·accumulator1_overflow; - } - } - } - va_end(args); - - // wipes out prior value of accumulator1 - accumulator1->d0 = carry; - - return N32·Status·ok; - } - - Local N32·Status N32·add(N32·T *sum ,N32·T *a ,N32·T *b){ - uint64_t result = (uint64_t)a->d0 + (uint64_t)b->d0; - sum->d0 = (uint32_t)result; - return (result >> 32) ? N32·Status·carry : N32·Status·ok; - } - - Local bool N32·increment(N32·T *a){ - a->d0++; - return a->d0 == 0; - } - - Local N32·Status N32·subtract(N32·T *difference ,N32·T *a ,N32·T *b){ - uint64_t diff = (uint64_t) a->d0 - (uint64_t) b->d0; - difference->d0 = (uint32_t)diff; - return (diff > a->d0) ? N32·Status·borrow : N32·Status·ok; - } - - - Local N32·Status N32·multiply(N32·T *product1 ,N32·T *product0 ,N32·T *a ,N32·T *b){ - uint64_t product = (uint64_t)a->d0 * (uint64_t)b->d0; - product0->d0 = (uint32_t)product; - product1->d0 = (uint32_t)(product >> 32); - - if(product1->d0 == 0) return N32·Status·one_word_product; - return N32·Status·two_word_product; - } - - Local N32·Status N32·divide(N32·T *remainder ,N32·T *quotient ,N32·T *a ,N32·T *b){ - if(b->d0 == 0) return N32·Status·undefined_divide_by_zero; - - quotient->d0 = a->d0 / b->d0; - remainder->d0 = a->d0 - (quotient->d0 * b->d0); - - return N32·Status·ok; - } - - Local N32·Status N32·modulus(N32·T *remainder ,N32·T *a ,N32·T *b){ - if(b->d0 == 0) return N32·Status·undefined_modulus_zero; - uint32_t quotient = a->d0 / b->d0; - remainder->d0 = a->d0 - (quotient * b->d0); - return N32·Status·ok; - } - - // bit motion - - typedef uint32_t (*ShiftOp)(uint32_t, uint32_t); - - Local uint32_t shift_left_op(uint32_t value, uint32_t amount){ - return value << amount; - } - - Local uint32_t shift_right_op(uint32_t value, uint32_t amount){ - return value >> amount; - } - - // modifies all three of its operands - // in the case of duplicate operands this is the order: first modifies operand, then fill, then spill, - Local N32·Status N32·shift - ( - uint32_t shift_count - ,N32·T *spill - ,N32·T *operand - ,N32·T *fill - ,ShiftOp shift_op - ,ShiftOp complement_shift_op - ){ - - // If no result is needed, return immediately. - if(operand == NULL && spill == NULL) return N32·Status·ok; - - // Treat NULL operand as zero. - if(operand == NULL){ - operand = &N32·t[0]; - N32·copy(operand, N32·zero); - } - - // Shifting more than one word breaks our fill/spill model. - if(shift_count > 31) return N32·Status·gt_max_shift_count; - - // The given operand is still required after it is modified, so we copy it. - N32·T *given_operand = &N32·t[1]; - N32·copy(given_operand, operand); - - // Perform the shift - operand->d0 = shift_op(given_operand->d0, shift_count); - if(fill != NULL){ - fill->d0 = complement_shift_op(fill->d0, (32 - shift_count)); - N32·bit_or(operand, operand, fill); - } - if(spill != NULL){ - spill->d0 = shift_op(spill->d0, shift_count); - spill->d0 += complement_shift_op(given_operand->d0, (32 - shift_count)); - } - - return N32·Status·ok; - } - - // Define concrete shift functions using valid C function pointers - Local N32·Status - N32·shift_left(uint32_t shift_count, N32·T *spill, N32·T *operand, N32·T *fill){ - return N32·shift(shift_count, spill, operand, fill, shift_left_op, shift_right_op); - } - - Local N32·Status - N32·shift_right(uint32_t shift_count, N32·T *spill, N32·T *operand, N32·T *fill){ - return N32·shift(shift_count, spill, operand, fill, shift_right_op, shift_left_op); - } - - Local N32·Status - N32·arithmetic_shift_right(uint32_t shift_count, N32·T *operand, N32·T *spill){ - - // Guard against excessive shift counts - if(shift_count > 31) return N32·Status·gt_max_shift_count; - - // A NULL operand is treated as zero - if(operand == NULL){ - operand = &N32·t[0]; - N32·copy(operand, N32·zero); - } - - // Pick the fill value based on the sign bit - N32·T *fill = (operand->d0 & 0x80000000) ? N32·all_one_bit : N32·zero; - - // Call shift_right with the appropriate fill - return N32·shift_right(shift_count, spill, operand, fill); - } - - Local const N32·Λ N32·λ = { - - .allocate_array = N32·allocate_array - ,.allocate_array_zero = N32·allocate_array_zero - ,.deallocate = N32·deallocate - - ,.copy = N32·copy - ,.bit_and = N32·bit_and - ,.bit_or = N32·bit_or - ,.bit_complement = N32·bit_complement - ,.bit_twos_complement = N32·bit_twos_complement - ,.compare = N32·compare - ,.lt = N32·lt - ,.gt = N32·gt - ,.eq = N32·eq - ,.eq_zero = N32·eq_zero - ,.accumulate = N32·accumulate - ,.add = N32·add - ,.increment = N32·increment - ,.subtract = N32·subtract - ,.multiply = N32·multiply - ,.divide = N32·divide - ,.modulus = N32·modulus - ,.shift_left = N32·shift_left - ,.shift_right = N32·shift_right - ,.arithmetic_shift_right = N32·arithmetic_shift_right - - ,.access = N32·access - ,.from_uint32 = N32·from_uint32 - }; - - #endif - -#endif diff --git "a/developer/cc\360\237\226\211/N32.lib_std.c" "b/developer/cc\360\237\226\211/N32.lib_std.c" new file mode 100644 index 0000000..fba3c21 --- /dev/null +++ "b/developer/cc\360\237\226\211/N32.lib_std.c" @@ -0,0 +1,457 @@ +/* + N32 - a processor native type + + For binary operations: a op b -> c + + See the document on the proper use of the Natural types. + + On the subject of multiple pointers indicating the same location in memory: + + When a routine has multiple results, and one or more of the result location + pointers point to the same storage, the routine will either return an error + status, or have defined behavior. + + When a routine has multiple operands, in any combination, those + pointers can point to the same location, and the routine will + function as advertised. + + When an operand functions as both an input and a result, perhaps due + to a result pointer pointing to the same place as an operand + pointer, the routine will function as advertised. (Internally the + routine might make a temporary copy of the operand to accomplish + this.) + +*/ + +#define N32·DEBUG + +#ifndef FACE +#define N32·IMPLEMENTATION +#define FACE +#endif + +//-------------------------------------------------------------------------------- +// Interface + +#ifndef N32·FACE +#define N32·FACE + + #include + #include + #include + #include + + //---------------------------------------- + // Instance Data (Declaration Only) + + typedef uint32_t Extent; + typedef uint32_t Digit; + + typedef struct N32·T N32·T; + + extern N32·T *N32·zero; + extern N32·T *N32·one; + extern N32·T *N32·all_one_bit; + extern N32·T *N32·lsb; + extern N32·T *N32·msb; + + //---------------------------------------- + // Return/Error Status and handlers + + typedef enum{ + N32·Status·ok = 0 + ,N32·Status·overflow = 1 + ,N32·Status·accumulator1_overflow = 2 + ,N32·Status·carry = 3 + ,N32·Status·borrow = 4 + ,N32·Status·undefined_divide_by_zero = 5 + ,N32·Status·undefined_modulus_zero = 6 + ,N32·Status·gt_max_shift_count = 7 + ,N32·Status·spill_eq_operand = 8 // not currently signaled, result will be spill value + ,N32·Status·one_word_product = 9 + ,N32·Status·two_word_product = 10 + } N32·Status; + + typedef enum{ + N32·Order_lt = -1 + ,N32·Order_eq = 0 + ,N32·Order_gt = 1 + } N32·Order; + + typedef N32·T *( *N32·Allocate_MemoryFault )(Extent); + + //---------------------------------------- + // Interface + + typedef struct{ + + N32·T *(*allocate_array_zero)(Extent, N32·Allocate_MemoryFault); + N32·T *(*allocate_array)(Extent, N32·Allocate_MemoryFault); + void (*deallocate)(N32·T*); + + void (*copy)(N32·T*, N32·T*); + void (*bit_and)(N32·T*, N32·T*, N32·T*); + void (*bit_or)(N32·T*, N32·T*, N32·T*); + void (*bit_complement)(N32·T*, N32·T*); + void (*bit_twos_complement)(N32·T*, N32·T*); + N32·Order (*compare)(N32·T*, N32·T*); + bool (*lt)(N32·T*, N32·T*); + bool (*gt)(N32·T*, N32·T*); + bool (*eq)(N32·T*, N32·T*); + bool (*eq_zero)(N32·T*); + N32·Status (*accumulate)(N32·T *accumulator1 ,N32·T *accumulator0 ,...); + N32·Status (*add)(N32·T*, N32·T*, N32·T*); + bool (*increment)(N32·T *a); + N32·Status (*subtract)(N32·T*, N32·T*, N32·T*); + N32·Status (*multiply)(N32·T*, N32·T*, N32·T*, N32·T*); + N32·Status (*divide)(N32·T*, N32·T*, N32·T*, N32·T*); + N32·Status (*modulus)(N32·T*, N32·T*, N32·T*); + N32·Status (*shift_left)(Extent, N32·T*, N32·T*, N32·T*); + N32·Status (*shift_right)(Extent, N32·T*, N32·T*, N32·T*); + N32·Status (*arithmetic_shift_right)(Extent, N32·T*, N32·T*); + + N32·T* (*access)(N32·T*, Extent); + void (*from_uint32)(N32·T *destination ,uint32_t value); + } N32·Λ; + + Local const N32·Λ N32·λ; // initialized in the LOCAL section + +#endif + +//-------------------------------------------------------------------------------- +// Implementation + +#ifdef N32·IMPLEMENTATION + + // this part goes into the library + #ifndef LOCAL + + #include + #include + + struct N32·T{ + Digit d0; + }; + + N32·T N32·constant[4] = { + {.d0 = 0}, + {.d0 = 1}, + {.d0 = ~(uint32_t)0}, + {.d0 = 1 << 31} + }; + + N32·T *N32·zero = &N32·constant[0]; + N32·T *N32·one = &N32·constant[1]; + N32·T *N32·all_one_bit = &N32·constant[2]; + N32·T *N32·msb = &N32·constant[3]; + N32·T *N32·lsb = &N32·constant[1]; + + // the allocate an array of N32 + N32·T *N32·allocate_array(Extent extent ,N32·Allocate_MemoryFault memory_fault){ + N32·T *instance = malloc((extent + 1) * sizeof(N32·T) ); + if(!instance){ + return memory_fault ? memory_fault(extent) : NULL; + } + return instance; + } + + N32·T *N32·allocate_array_zero(Extent extent ,N32·Allocate_MemoryFault memory_fault){ + N32·T *instance = calloc( extent + 1 ,sizeof(N32·T) ); + if(!instance){ + return memory_fault ? memory_fault(extent) : NULL; + } + return instance; + } + + void N32·deallocate(N32·T *unencumbered){ + free(unencumbered); + } + + #endif + + // This part is included after the library user's code + #ifdef LOCAL + + // instance + + struct N32·T{ + Digit d0; + }; + + // temporary variables + // making these LOCAL rather than reserving one block in the library is thread safe + // allocating a block once is more efficient + // library code writes these, they are not on the interface + + Local N32·T N32·t[4]; + + + // allocation + + extern N32·T *N32·allocate_array(Extent, N32·Allocate_MemoryFault); + extern N32·T *N32·allocate_array_zero(Extent, N32·Allocate_MemoryFault); + extern void N32·deallocate(N32·T *); + + // so the user can access numbers in an array allocation + Local N32·T* N32·access(N32·T *array ,Extent index){ + return &array[index]; + } + + Local void N32·from_uint32(N32·T *destination ,uint32_t value){ + if(destination == NULL) return; + destination->d0 = value; + } + + // copy, convenience copy + + Local void N32·copy(N32·T *destination ,N32·T *source){ + if(source == destination) return; // that was easy! + *destination = *source; + } + + Local void N32·set_to_zero(N32·T *instance){ + instance->d0 = 0; + } + + Local void N32·set_to_one(N32·T *instance){ + instance->d0 = 1; + } + + // bit operations + + Local void N32·bit_and(N32·T *result, N32·T *a, N32·T *b){ + result->d0 = a->d0 & b->d0; + } + + // result can be one of the operands + Local void N32·bit_or(N32·T *result, N32·T *a, N32·T *b){ + result->d0 = a->d0 | b->d0; + } + + // result can the same as the operand + Local void N32·bit_complement(N32·T *result, N32·T *a){ + result->d0 = ~a->d0; + } + + // result can the same as the operand + Local void N32·bit_twos_complement(N32·T *result ,N32·T *a){ + result->d0 = ~a->d0 + 1; + } + + // test functions + + Local N32·Order N32·compare(N32·T *a, N32·T *b){ + if(a->d0 < b->d0) return N32·Order_lt; + if(a->d0 > b->d0) return N32·Order_gt; + return N32·Order_eq; + } + + Local bool N32·lt(N32·T *a ,N32·T *b){ + return a->d0 < b->d0; + } + + Local bool N32·gt(N32·T *a ,N32·T *b){ + return a->d0 > b->d0; + } + + Local bool N32·eq(N32·T *a ,N32·T *b){ + return a->d0 == b->d0; + } + + Local bool N32·eq_zero(N32·T *a){ + return a->d0 == 0; + } + + + // arithmetic operations + + // For a large number of summands for the lower precision Natural implementations, for accumulate/add/sub, the 'overflow' operand could overflow and thus this routine will halt and return N32·Status·accumulator1_overflow + // + // When accumulator1 and accumulator0 point to the same location, the result is the accumulator1 value. + Local N32·Status N32·accumulate(N32·T *accumulator1 ,N32·T *accumulator0 ,...){ + + va_list args; + va_start(args ,accumulator0); + uint32_t sum = accumulator0->d0; + uint32_t carry = 0; + N32·T *current; + + while( (current = va_arg(args ,N32·T *)) ){ + sum += current->d0; + if(sum < current->d0){ // Accumulator1 into carry + (carry)++; + if(carry == 0){ + va_end(args); + return N32·Status·accumulator1_overflow; + } + } + } + va_end(args); + + // wipes out prior value of accumulator1 + accumulator1->d0 = carry; + + return N32·Status·ok; + } + + Local N32·Status N32·add(N32·T *sum ,N32·T *a ,N32·T *b){ + uint64_t result = (uint64_t)a->d0 + (uint64_t)b->d0; + sum->d0 = (uint32_t)result; + return (result >> 32) ? N32·Status·carry : N32·Status·ok; + } + + Local bool N32·increment(N32·T *a){ + a->d0++; + return a->d0 == 0; + } + + Local N32·Status N32·subtract(N32·T *difference ,N32·T *a ,N32·T *b){ + uint64_t diff = (uint64_t) a->d0 - (uint64_t) b->d0; + difference->d0 = (uint32_t)diff; + return (diff > a->d0) ? N32·Status·borrow : N32·Status·ok; + } + + + Local N32·Status N32·multiply(N32·T *product1 ,N32·T *product0 ,N32·T *a ,N32·T *b){ + uint64_t product = (uint64_t)a->d0 * (uint64_t)b->d0; + product0->d0 = (uint32_t)product; + product1->d0 = (uint32_t)(product >> 32); + + if(product1->d0 == 0) return N32·Status·one_word_product; + return N32·Status·two_word_product; + } + + Local N32·Status N32·divide(N32·T *remainder ,N32·T *quotient ,N32·T *a ,N32·T *b){ + if(b->d0 == 0) return N32·Status·undefined_divide_by_zero; + + quotient->d0 = a->d0 / b->d0; + remainder->d0 = a->d0 - (quotient->d0 * b->d0); + + return N32·Status·ok; + } + + Local N32·Status N32·modulus(N32·T *remainder ,N32·T *a ,N32·T *b){ + if(b->d0 == 0) return N32·Status·undefined_modulus_zero; + uint32_t quotient = a->d0 / b->d0; + remainder->d0 = a->d0 - (quotient * b->d0); + return N32·Status·ok; + } + + // bit motion + + typedef uint32_t (*ShiftOp)(uint32_t, uint32_t); + + Local uint32_t shift_left_op(uint32_t value, uint32_t amount){ + return value << amount; + } + + Local uint32_t shift_right_op(uint32_t value, uint32_t amount){ + return value >> amount; + } + + // modifies all three of its operands + // in the case of duplicate operands this is the order: first modifies operand, then fill, then spill, + Local N32·Status N32·shift + ( + uint32_t shift_count + ,N32·T *spill + ,N32·T *operand + ,N32·T *fill + ,ShiftOp shift_op + ,ShiftOp complement_shift_op + ){ + + // If no result is needed, return immediately. + if(operand == NULL && spill == NULL) return N32·Status·ok; + + // Treat NULL operand as zero. + if(operand == NULL){ + operand = &N32·t[0]; + N32·copy(operand, N32·zero); + } + + // Shifting more than one word breaks our fill/spill model. + if(shift_count > 31) return N32·Status·gt_max_shift_count; + + // The given operand is still required after it is modified, so we copy it. + N32·T *given_operand = &N32·t[1]; + N32·copy(given_operand, operand); + + // Perform the shift + operand->d0 = shift_op(given_operand->d0, shift_count); + if(fill != NULL){ + fill->d0 = complement_shift_op(fill->d0, (32 - shift_count)); + N32·bit_or(operand, operand, fill); + } + if(spill != NULL){ + spill->d0 = shift_op(spill->d0, shift_count); + spill->d0 += complement_shift_op(given_operand->d0, (32 - shift_count)); + } + + return N32·Status·ok; + } + + // Define concrete shift functions using valid C function pointers + Local N32·Status + N32·shift_left(uint32_t shift_count, N32·T *spill, N32·T *operand, N32·T *fill){ + return N32·shift(shift_count, spill, operand, fill, shift_left_op, shift_right_op); + } + + Local N32·Status + N32·shift_right(uint32_t shift_count, N32·T *spill, N32·T *operand, N32·T *fill){ + return N32·shift(shift_count, spill, operand, fill, shift_right_op, shift_left_op); + } + + Local N32·Status + N32·arithmetic_shift_right(uint32_t shift_count, N32·T *operand, N32·T *spill){ + + // Guard against excessive shift counts + if(shift_count > 31) return N32·Status·gt_max_shift_count; + + // A NULL operand is treated as zero + if(operand == NULL){ + operand = &N32·t[0]; + N32·copy(operand, N32·zero); + } + + // Pick the fill value based on the sign bit + N32·T *fill = (operand->d0 & 0x80000000) ? N32·all_one_bit : N32·zero; + + // Call shift_right with the appropriate fill + return N32·shift_right(shift_count, spill, operand, fill); + } + + Local const N32·Λ N32·λ = { + + .allocate_array = N32·allocate_array + ,.allocate_array_zero = N32·allocate_array_zero + ,.deallocate = N32·deallocate + + ,.copy = N32·copy + ,.bit_and = N32·bit_and + ,.bit_or = N32·bit_or + ,.bit_complement = N32·bit_complement + ,.bit_twos_complement = N32·bit_twos_complement + ,.compare = N32·compare + ,.lt = N32·lt + ,.gt = N32·gt + ,.eq = N32·eq + ,.eq_zero = N32·eq_zero + ,.accumulate = N32·accumulate + ,.add = N32·add + ,.increment = N32·increment + ,.subtract = N32·subtract + ,.multiply = N32·multiply + ,.divide = N32·divide + ,.modulus = N32·modulus + ,.shift_left = N32·shift_left + ,.shift_right = N32·shift_right + ,.arithmetic_shift_right = N32·arithmetic_shift_right + + ,.access = N32·access + ,.from_uint32 = N32·from_uint32 + }; + + #endif + +#endif diff --git "a/developer/cc\360\237\226\211/N64.lib.c" "b/developer/cc\360\237\226\211/N64.lib.c" new file mode 100644 index 0000000..9d98d58 --- /dev/null +++ "b/developer/cc\360\237\226\211/N64.lib.c" @@ -0,0 +1,477 @@ +/* + N64 - a 64-bit native type + + For binary operations: a op b -> c + + Similar to N32, but now each Digit is 64 bits. Where a 128-bit + intermediate is necessary (e.g. multiplication), we handle it + manually using two 64-bit parts. + + On the subject of multiple pointers indicating the same location in memory: + + When a routine has multiple results, and one or more of the result location + pointers point to the same storage, the routine will either return an error + status, or have defined behavior. + + When a routine has multiple operands, in any combination, those + pointers can point to the same location, and the routine will + function as advertised. + + When an operand functions as both an input and a result, perhaps due + to a result pointer pointing to the same place as an operand + pointer, the routine will function as advertised. (Internally the + routine might make a temporary copy of the operand to accomplish + this.) +*/ + +#define N64·DEBUG + +#ifndef FACE +#define N64·IMPLEMENTATION +#define FACE +#endif + +//-------------------------------------------------------------------------------- +// Interface + +#ifndef N64·FACE +#define N64·FACE + + #include + #include + #include + #include + + //---------------------------------------- + // Instance Data (Declaration Only) + + typedef uint64_t Extent; + typedef uint64_t Digit; + + typedef struct N64·T N64·T; + + extern N64·T *N64·zero; + extern N64·T *N64·one; + extern N64·T *N64·all_one_bit; + extern N64·T *N64·lsb; + extern N64·T *N64·msb; + + //---------------------------------------- + // Return/Error Status and handlers + + typedef enum { + N64·Status·ok = 0 + ,N64·Status·overflow = 1 + ,N64·Status·accumulator1_overflow = 2 + ,N64·Status·carry = 3 + ,N64·Status·borrow = 4 + ,N64·Status·undefined_divide_by_zero = 5 + ,N64·Status·undefined_modulus_zero = 6 + ,N64·Status·gt_max_shift_count = 7 + ,N64·Status·spill_eq_operand = 8 // not currently signaled, result will be spill value + ,N64·Status·one_word_product = 9 + ,N64·Status·two_word_product = 10 + } N64·Status; + + typedef enum { + N64·Order_lt = -1 + ,N64·Order_eq = 0 + ,N64·Order_gt = 1 + } N64·Order; + + typedef N64·T *(*N64·Allocate_MemoryFault)(Extent); + + //---------------------------------------- + // Interface + + typedef struct { + + N64·T *(*allocate_array_zero)(Extent, N64·Allocate_MemoryFault); + N64·T *(*allocate_array)(Extent, N64·Allocate_MemoryFault); + void (*deallocate)(N64·T*); + + void (*copy)(N64·T*, N64·T*); + void (*bit_and)(N64·T*, N64·T*, N64·T*); + void (*bit_or)(N64·T*, N64·T*, N64·T*); + void (*bit_complement)(N64·T*, N64·T*); + void (*bit_twos_complement)(N64·T*, N64·T*); + N64·Order (*compare)(N64·T*, N64·T*); + bool (*lt)(N64·T*, N64·T*); + bool (*gt)(N64·T*, N64·T*); + bool (*eq)(N64·T*, N64·T*); + bool (*eq_zero)(N64·T*); + N64·Status (*accumulate)(N64·T *accumulator1, N64·T *accumulator0, ...); + N64·Status (*add)(N64·T*, N64·T*, N64·T*); + bool (*increment)(N64·T *a); + N64·Status (*subtract)(N64·T*, N64·T*, N64·T*); + N64·Status (*multiply)(N64·T*, N64·T*, N64·T*, N64·T*); + N64·Status (*divide)(N64·T*, N64·T*, N64·T*, N64·T*); + N64·Status (*modulus)(N64·T*, N64·T*, N64·T*); + N64·Status (*shift_left)(Extent, N64·T*, N64·T*, N64·T*); + N64·Status (*shift_right)(Extent, N64·T*, N64·T*, N64·T*); + N64·Status (*arithmetic_shift_right)(Extent, N64·T*, N64·T*); + + N64·T* (*access)(N64·T*, Extent); + void (*from_uint64)(N64·T *destination, uint64_t value); + + } N64·Λ; + + Local const N64·Λ N64·λ; // initialized in the LOCAL section + +#endif + +//-------------------------------------------------------------------------------- +// Implementation + +#ifdef N64·IMPLEMENTATION + + // this part goes into the library + #ifndef LOCAL + + #include + #include + + struct N64·T { + Digit d0; + }; + + // For constants, we store them in an array for convenience + // 0, 1, all bits set (~0ULL), and MSB set (1ULL<<63) + N64·T N64·constant[4] = { + {.d0 = 0ULL}, + {.d0 = 1ULL}, + {.d0 = ~(uint64_t)0ULL}, + {.d0 = 1ULL << 63} + }; + + N64·T *N64·zero = &N64·constant[0]; + N64·T *N64·one = &N64·constant[1]; + N64·T *N64·all_one_bit = &N64·constant[2]; + N64·T *N64·msb = &N64·constant[3]; + N64·T *N64·lsb = &N64·constant[1]; + + // allocate an array of N64 + N64·T *N64·allocate_array(Extent extent, N64·Allocate_MemoryFault memory_fault){ + N64·T *instance = malloc( (extent + 1) * sizeof(N64·T) ); + if(!instance){ + return memory_fault ? memory_fault(extent) : NULL; + } + return instance; + } + + N64·T *N64·allocate_array_zero(Extent extent, N64·Allocate_MemoryFault memory_fault){ + N64·T *instance = calloc(extent + 1, sizeof(N64·T)); + if(!instance){ + return memory_fault ? memory_fault(extent) : NULL; + } + return instance; + } + + void N64·deallocate(N64·T *unencumbered){ + free(unencumbered); + } + + #endif + + // This part is included after the library user's code + #ifdef LOCAL + + // instance + + struct N64·T { + Digit d0; + }; + + // local temporary variables + Local N64·T N64·t[4]; + + // allocation references + extern N64·T *N64·allocate_array(Extent, N64·Allocate_MemoryFault); + extern N64·T *N64·allocate_array_zero(Extent, N64·Allocate_MemoryFault); + extern void N64·deallocate(N64·T *); + + // Access array + Local N64·T* N64·access(N64·T *array, Extent index){ + return &array[index]; + } + + Local void N64·from_uint64(N64·T *destination, uint64_t value){ + if(destination == NULL) return; + destination->d0 = value; + } + + // copy + Local void N64·copy(N64·T *destination, N64·T *source){ + if(source == destination) return; + *destination = *source; + } + + // bit operations + + Local void N64·bit_and(N64·T *result, N64·T *a, N64·T *b){ + result->d0 = a->d0 & b->d0; + } + + Local void N64·bit_or(N64·T *result, N64·T *a, N64·T *b){ + result->d0 = a->d0 | b->d0; + } + + Local void N64·bit_complement(N64·T *result, N64·T *a){ + result->d0 = ~a->d0; + } + + Local void N64·bit_twos_complement(N64·T *result, N64·T *a){ + result->d0 = ~a->d0 + 1ULL; + } + + // compare & test functions + + Local N64·Order N64·compare(N64·T *a, N64·T *b){ + if(a->d0 < b->d0) return N64·Order_lt; + if(a->d0 > b->d0) return N64·Order_gt; + return N64·Order_eq; + } + + Local bool N64·lt(N64·T *a, N64·T *b){ + return (a->d0 < b->d0); + } + + Local bool N64·gt(N64·T *a, N64·T *b){ + return (a->d0 > b->d0); + } + + Local bool N64·eq(N64·T *a, N64·T *b){ + return (a->d0 == b->d0); + } + + Local bool N64·eq_zero(N64·T *a){ + return (a->d0 == 0ULL); + } + + // arithmetic operations + + // accumulate + Local N64·Status N64·accumulate(N64·T *accumulator1, N64·T *accumulator0, ...){ + va_list args; + va_start(args, accumulator0); + + uint64_t sum = accumulator0->d0; + uint64_t carry = 0; + N64·T *current; + + while( (current = va_arg(args, N64·T*)) ){ + uint64_t prior = sum; + sum += current->d0; + if(sum < prior){ // indicates carry + carry++; + // if carry overflowed a 64-bit, that's an accumulator1 overflow + if(carry == 0ULL){ + va_end(args); + return N64·Status·accumulator1_overflow; + } + } + } + va_end(args); + + accumulator1->d0 = carry; + return N64·Status·ok; + } + + // add + Local N64·Status N64·add(N64·T *sum, N64·T *a, N64·T *b){ + __uint128_t result = ( __uint128_t )a->d0 + ( __uint128_t )b->d0; + // But to avoid using a GNU extension, we can do the simpler approach: + // Actually let's do it directly with 64-bit since we only need to detect carry out of 64 bits: + uint64_t temp = a->d0 + b->d0; + sum->d0 = temp; + if(temp < a->d0) return N64·Status·carry; // means we overflowed + return N64·Status·ok; + } + + Local bool N64·increment(N64·T *a){ + uint64_t old = a->d0; + a->d0++; + // if it wrapped around to 0, then it was 0xFFFFFFFFFFFFFFFF + return (a->d0 < old); + } + + // subtract + Local N64·Status N64·subtract(N64·T *difference, N64·T *a, N64·T *b){ + uint64_t tmpA = a->d0; + uint64_t tmpB = b->d0; + uint64_t diff = tmpA - tmpB; + difference->d0 = diff; + if(diff > tmpA) return N64·Status·borrow; // indicates we borrowed + return N64·Status·ok; + } + + // multiply + // We'll do a 64x64->128 using two 64-bit accumulators + Local N64·Status N64·multiply(N64·T *product1, N64·T *product0, N64·T *a, N64·T *b){ + uint64_t A = a->d0; + uint64_t B = b->d0; + + // Break each operand into high & low 32 bits + uint64_t a_lo = (uint32_t)(A & 0xffffffffULL); + uint64_t a_hi = A >> 32; + uint64_t b_lo = (uint32_t)(B & 0xffffffffULL); + uint64_t b_hi = B >> 32; + + // partial products + uint64_t low = a_lo * b_lo; // 64-bit + uint64_t cross = (a_lo * b_hi) + (a_hi * b_lo); // potentially up to 2 * 32 bits => 64 bits + uint64_t high = a_hi * b_hi; // up to 64 bits + + // incorporate cross into low, high + // cross is effectively the middle bits, so shift cross by 32 and add to low + uint64_t cross_low = (cross & 0xffffffffULL) << 32; // lower part + uint64_t cross_high = cross >> 32; // upper part + + // add cross_low to low, capture carry + uint64_t old_low = low; + low += cross_low; + if(low < old_low) cross_high++; + + // final high + high += cross_high; + + // store results + product0->d0 = low; + product1->d0 = high; + + if(high == 0ULL) return N64·Status·one_word_product; + return N64·Status·two_word_product; + } + + // divide + Local N64·Status N64·divide(N64·T *remainder, N64·T *quotient, N64·T *a, N64·T *b){ + // we do not handle a > 64-bit, just the single 64-bit + if(b->d0 == 0ULL) return N64·Status·undefined_divide_by_zero; + + uint64_t divd = a->d0; // dividend + uint64_t divs = b->d0; // divisor + + quotient->d0 = divd / divs; + remainder->d0 = divd - (quotient->d0 * divs); + + return N64·Status·ok; + } + + // modulus + Local N64·Status N64·modulus(N64·T *remainder, N64·T *a, N64·T *b){ + if(b->d0 == 0ULL) return N64·Status·undefined_modulus_zero; + + uint64_t divd = a->d0; + uint64_t divs = b->d0; + uint64_t q = divd / divs; + remainder->d0 = divd - (q * divs); + + return N64·Status·ok; + } + + // bit motion + + typedef uint64_t (*ShiftOp)(uint64_t, uint64_t); + + Local uint64_t shift_left_op(uint64_t value, uint64_t amount){ + return (value << amount); + } + + Local uint64_t shift_right_op(uint64_t value, uint64_t amount){ + return (value >> amount); + } + + // modifies all three of its operands + // in the case of duplicate operands this is the order: first modifies operand, then fill, then spill + Local N64·Status N64·shift + ( + uint64_t shift_count, + N64·T *spill, + N64·T *operand, + N64·T *fill, + ShiftOp shift_op, + ShiftOp complement_shift_op + ){ + if(operand == NULL && spill == NULL) return N64·Status·ok; + + // Treat NULL operand as zero + if(operand == NULL){ + operand = &N64·t[0]; + N64·copy(operand, N64·zero); + } + + // Shifting more than 63 bits breaks fill/spill logic + if(shift_count > 63ULL) return N64·Status·gt_max_shift_count; + + N64·T *given_operand = &N64·t[1]; + N64·copy(given_operand, operand); + + // Perform the shift + operand->d0 = shift_op(given_operand->d0, shift_count); + if(fill != NULL){ + fill->d0 = complement_shift_op(fill->d0, (64ULL - shift_count)); + N64·bit_or(operand, operand, fill); + } + if(spill != NULL){ + spill->d0 = shift_op(spill->d0, shift_count); + spill->d0 += complement_shift_op(given_operand->d0, (64ULL - shift_count)); + } + + return N64·Status·ok; + } + + Local N64·Status N64·shift_left(uint64_t shift_count, N64·T *spill, N64·T *operand, N64·T *fill){ + return N64·shift(shift_count, spill, operand, fill, shift_left_op, shift_right_op); + } + + Local N64·Status N64·shift_right(uint64_t shift_count, N64·T *spill, N64·T *operand, N64·T *fill){ + return N64·shift(shift_count, spill, operand, fill, shift_right_op, shift_left_op); + } + + Local N64·Status N64·arithmetic_shift_right(uint64_t shift_count, N64·T *operand, N64·T *spill){ + if(shift_count > 63ULL) return N64·Status·gt_max_shift_count; + + // A NULL operand is treated as zero + if(operand == NULL){ + operand = &N64·t[0]; + N64·copy(operand, N64·zero); + } + + // sign bit check + N64·T *fill = (operand->d0 & (1ULL << 63)) ? N64·all_one_bit : N64·zero; + return N64·shift_right(shift_count, spill, operand, fill); + } + + Local const N64·Λ N64·λ = { + .allocate_array = N64·allocate_array + ,.allocate_array_zero = N64·allocate_array_zero + ,.deallocate = N64·deallocate + + ,.copy = N64·copy + ,.bit_and = N64·bit_and + ,.bit_or = N64·bit_or + ,.bit_complement = N64·bit_complement + ,.bit_twos_complement = N64·bit_twos_complement + ,.compare = N64·compare + ,.lt = N64·lt + ,.gt = N64·gt + ,.eq = N64·eq + ,.eq_zero = N64·eq_zero + ,.accumulate = N64·accumulate + ,.add = N64·add + ,.increment = N64·increment + ,.subtract = N64·subtract + ,.multiply = N64·multiply + ,.divide = N64·divide + ,.modulus = N64·modulus + ,.shift_left = N64·shift_left + ,.shift_right = N64·shift_right + ,.arithmetic_shift_right = N64·arithmetic_shift_right + + ,.access = N64·access + ,.from_uint64 = N64·from_uint64 + }; + + #endif + +#endif diff --git "a/developer/cc\360\237\226\211/N_template.lib.c.m4" "b/developer/cc\360\237\226\211/N_template.lib.c.m4" new file mode 100644 index 0000000..cabe6b5 --- /dev/null +++ "b/developer/cc\360\237\226\211/N_template.lib.c.m4" @@ -0,0 +1,318 @@ +changequote([,])dnl +define(NS, __NAMESPACE__)dnl +define(DE, __DIGIT_EXTENT__)dnl +define(DT, __DIGIT_TYPE__)dnl +define(ET, __EXTENT_TYPE__)dnl +changequote(`,')dnl + +/* + [NS] - Parametric multi-digit type library (RT code format) + + Parameters: + [NS]: Namespace prefix, e.g. N32· + [DE]: Digit extent => total digits = DE + 1 + [DT]: Digit type, e.g. uint64_t or uint8_t + [ET]: Loop counter type, e.g. uint64_t +*/ + +#define [NS]DEBUG + +#ifndef FACE + #define [NS]IMPLEMENTATION + #define FACE +#endif + +//------------------------------------------------------------------------------ +// Interface Section + +#ifndef [NS]FACE +#define [NS]FACE + + #include + #include + #include + #include + + // Digit count is DE + 1 + #define [NS]DIGIT_COUNT (DE + 1) + + typedef DT Digit; + typedef struct [NS]T [NS]T; + + // For array allocations + typedef ET ExtentType; + + // Status codes + typedef enum{ + [NS]Status_ok = 0 + ,[NS]Status_overflow + ,[NS]Status_accumulator1_overflow + ,[NS]Status_carry + ,[NS]Status_borrow + ,[NS]Status_undefined_divide_by_zero + ,[NS]Status_undefined_modulus_zero + ,[NS]Status_gt_max_shift_count + ,[NS]Status_spill_eq_operand + ,[NS]Status_one_word_product + ,[NS]Status_two_word_product + } [NS]Status; + + typedef enum{ + [NS]Order_lt = -1 + ,[NS]Order_eq = 0 + ,[NS]Order_gt = 1 + } [NS]Order; + + // Allocation + Digit *[NS]allocate_array(ExtentType extent ,[NS]T *(*fault_handler)(ExtentType)); + Digit *[NS]allocate_array_zero(ExtentType extent ,[NS]T *(*fault_handler)(ExtentType)); + void [NS]deallocate([NS]T *unencumbered); + + // Constant setters (no static global constants) + void [NS]set_zero([NS]T *destination); + void [NS]set_one([NS]T *destination); + void [NS]set_all_bits([NS]T *destination); + void [NS]set_msb([NS]T *destination); + + // Bitwise ops + void [NS]bit_and([NS]T *result ,[NS]T *a ,[NS]T *b); + void [NS]bit_or([NS]T *result ,[NS]T *a ,[NS]T *b); + void [NS]bit_complement([NS]T *result ,[NS]T *a); + void [NS]bit_twos_complement([NS]T *result ,[NS]T *a); + + // Comparison ops + bool [NS]eq([NS]T *a ,[NS]T *b); + bool [NS]eq_zero([NS]T *a); + [NS]Order [NS]compare([NS]T *a ,[NS]T *b); + + // Arithmetic + [NS]Status [NS]add([NS]T *sum ,[NS]T *a ,[NS]T *b); + [NS]Status [NS]subtract([NS]T *diff ,[NS]T *a ,[NS]T *b); + + // Conversions + void [NS]from_uint64([NS]T *destination ,uint64_t value); + +#endif // [NS]FACE + + +//------------------------------------------------------------------------------ +// Implementation Section + +#ifdef [NS]IMPLEMENTATION + + #ifndef LOCAL + + #include + #include + + struct [NS]T{ + Digit d[[NS]DIGIT_COUNT]; + }; + + Digit *[NS]allocate_array(ExtentType extent ,[NS]T *(*fault_handler)(ExtentType)){ + [NS]T *instance = malloc((extent + 1) * sizeof([NS]T)); + if(!instance){ + if(fault_handler) return (Digit*)fault_handler(extent); + return NULL; + } + return (Digit*)instance; + } + + Digit *[NS]allocate_array_zero(ExtentType extent ,[NS]T *(*fault_handler)(ExtentType)){ + [NS]T *instance = calloc(extent + 1 ,sizeof([NS]T)); + if(!instance){ + if(fault_handler) return (Digit*)fault_handler(extent); + return NULL; + } + return (Digit*)instance; + } + + void [NS]deallocate([NS]T *unencumbered){ + free(unencumbered); + } + + #endif // LOCAL not defined + + + #ifdef LOCAL + + // local code implementing everything + struct [NS]T{ + Digit d[[NS]DIGIT_COUNT]; + }; + + // Constant Setters + + Local void [NS]set_zero([NS]T *destination){ + if(!destination) return; + Digit *start = destination->d; + Digit *end = destination->d + [NS]DIGIT_COUNT; + for(Digit *p = start ; p < end ; p++){ + *p = 0; + } + } + + Local void [NS]set_one([NS]T *destination){ + if(!destination) return; + [NS]set_zero(destination); + destination->d[0] = 1; + } + + Local void [NS]set_all_bits([NS]T *destination){ + if(!destination) return; + Digit *start = destination->d; + Digit *end = destination->d + [NS]DIGIT_COUNT; + for(Digit *p = start ; p < end ; p++){ + *p = (Digit)(-1); + } + } + + Local void [NS]set_msb([NS]T *destination){ + if(!destination) return; + [NS]set_zero(destination); + // Set top bit in the highest digit + destination->d[[NS]DIGIT_COUNT - 1] = ((Digit)1 << ((sizeof(Digit)*8) - 1)); + } + + // Bitwise + + Local void [NS]bit_and([NS]T *result ,[NS]T *a ,[NS]T *b){ + for(ExtentType i = 0 ; i < [NS]DIGIT_COUNT ; i++){ + result->d[i] = a->d[i] & b->d[i]; + } + } + + Local void [NS]bit_or([NS]T *result ,[NS]T *a ,[NS]T *b){ + for(ExtentType i = 0 ; i < [NS]DIGIT_COUNT ; i++){ + result->d[i] = a->d[i] | b->d[i]; + } + } + + Local void [NS]bit_complement([NS]T *result ,[NS]T *a){ + if(result == a){ + // same location + for(ExtentType i = 0 ; i < [NS]DIGIT_COUNT ; i++){ + result->d[i] = ~result->d[i]; + } + }else{ + for(ExtentType i = 0 ; i < [NS]DIGIT_COUNT ; i++){ + result->d[i] = ~a->d[i]; + } + } + } + + Local void [NS]bit_twos_complement([NS]T *result ,[NS]T *a){ + // ~ + 1 across [NS]DIGIT_COUNT digits + // If result == a, we must do carefully + [NS]T temp; + if(result == a) { + for(ExtentType i = 0 ; i < [NS]DIGIT_COUNT ; i++){ + temp.d[i] = ~a->d[i]; + } + }else{ + for(ExtentType i = 0 ; i < [NS]DIGIT_COUNT ; i++){ + temp.d[i] = ~a->d[i]; + } + } + // now add 1 + Digit carry = 1; + for(ExtentType i = 0 ; i < [NS]DIGIT_COUNT ; i++){ + unsigned __int128 v = (unsigned __int128)temp.d[i] + carry; + temp.d[i] = (Digit)v; + carry = (Digit)(v >> (sizeof(Digit)*8)); + } + // copy back if needed + if(result == a){ + for(ExtentType i = 0 ; i < [NS]DIGIT_COUNT ; i++){ + a->d[i] = temp.d[i]; + } + }else{ + for(ExtentType i = 0 ; i < [NS]DIGIT_COUNT ; i++){ + result->d[i] = temp.d[i]; + } + } + } + + // Comparison + + Local [NS]Order [NS]compare([NS]T *a ,[NS]T *b){ + // compare from top to bottom + for(ExtentType i = [NS]DIGIT_COUNT; i > 0 ; i--){ + ExtentType idx = i - 1; + if(a->d[idx] < b->d[idx]) return [NS]Order_lt; + if(a->d[idx] > b->d[idx]) return [NS]Order_gt; + } + return [NS]Order_eq; + } + + Local bool [NS]eq([NS]T *a ,[NS]T *b){ + for(ExtentType i = 0 ; i < [NS]DIGIT_COUNT ; i++){ + if(a->d[i] != b->d[i]) return false; + } + return true; + } + + Local bool [NS]eq_zero([NS]T *a){ + for(ExtentType i = 0 ; i < [NS]DIGIT_COUNT ; i++){ + if(a->d[i] != 0) return false; + } + return true; + } + + // Arithmetic + + Local [NS]Status [NS]add([NS]T *sum ,[NS]T *a ,[NS]T *b){ + if(!sum || !a || !b) return [NS]Status_overflow; + Digit carry = 0; + for(ExtentType i = 0 ; i < [NS]DIGIT_COUNT ; i++){ + unsigned __int128 tmp = (unsigned __int128)a->d[i] + b->d[i] + carry; + sum->d[i] = (Digit)tmp; + carry = (Digit)(tmp >> (sizeof(Digit)*8)); + } + return (carry != 0) ? [NS]Status_carry : [NS]Status_ok; + } + + Local [NS]Status [NS]subtract([NS]T *diff ,[NS]T *a ,[NS]T *b){ + if(!diff || !a || !b) return [NS]Status_overflow; + Digit borrow = 0; + for(ExtentType i = 0 ; i < [NS]DIGIT_COUNT ; i++){ + unsigned __int128 tmpA = a->d[i]; + unsigned __int128 tmpB = b->d[i] + borrow; + if(tmpA < tmpB){ + diff->d[i] = (Digit)((((unsigned __int128)1 << (sizeof(Digit)*8)) + tmpA) - tmpB); + borrow = 1; + }else{ + diff->d[i] = (Digit)(tmpA - tmpB); + borrow = 0; + } + } + return (borrow != 0) ? [NS]Status_borrow : [NS]Status_ok; + } + + // Conversion + + Local void [NS]from_uint64([NS]T *destination ,uint64_t value){ + if(!destination) return; + [NS]set_zero(destination); + // store 'value' in the low words + // note: if (DE + 1) < 2, we might only store partial + destination->d[0] = (Digit)(value & 0xFFFFFFFFFFFFFFFFULL); + #if DE >= 1 + if([NS]DIGIT_COUNT > 1){ + // store top half if digit is 32 bits etc, or 8 bits + // we'll just do partial logic if digit < 64 + // for 32-bit digit, that means the next digit has (value >> 32) + // for 8-bit digit, the next digits hold the rest, etc. + // We'll keep it simple. + // A more complete solution would loop if needed. + if(sizeof(Digit)*8 < 64){ + // multi-split if needed + // skipping for brevity + } + } + #endif + } + + #endif // LOCAL + +#endif // [NS]IMPLEMENTATION diff --git "a/developer/document\360\237\226\211/SectionedFileFormat.org" "b/developer/document\360\237\226\211/SectionedFileFormat.org" deleted file mode 100644 index db89851..0000000 --- "a/developer/document\360\237\226\211/SectionedFileFormat.org" +++ /dev/null @@ -1,85 +0,0 @@ -* Sectioned Single-File Format - The sectioned file format provides a structured way to define a library, - keeping all relevant code in a single file while ensuring proper separation - between interface, implementation, and static definitions. - -** Structure - The source file is divided into three sections: - - - *Interface*: Enabled by defining `IFACE` before including the file. - - *Implementation*: Enabled when neither `IFACE` nor `LOCAL` is defined. - - *Local (Static) Implementation*: Enabled by defining `LOCAL` before including the file. - -** Benefits - - **Avoids Split Headers and Source Files**: Eliminates mismatches between `.h` and `.c` files. - - **Improves Compiler Optimization**: The compiler has full visibility of static functions. - - **Reduces File Clutter**: A single source file contains everything. - - **Ensures Correct Compilation**: The build system automatically compiles the correct sections. - -** Usage - - To include the interface in a `.cli.c` file: - - #+begin_src c - #define IFACE - #include "module.lib.c" - #undef IFACE - #+end_src - - - To include the local static implementation: - - #+begin_src c - #define LOCAL - #include "module.lib.c" - #+end_src - - - The `.lib.c` file is compiled into the library separately. - - -------- - -✅ Benefits of the Approach -Prevents Header/Implementation Mismatch - -With separate .h and .c files, it's easy for a developer to update one but forget the other. -Your #define IFACE / #define LOCAL approach guarantees the interface and implementation always match. -Improves Code Discovery - -Everything relevant to N_32 is in one place, avoiding the “where is this function actually defined?” problem. -Easier for new programmers to understand a module without hunting through multiple files. -Reduces Compilation Dependencies - -Traditional headers require careful include guards (#ifndef, #define, #endif), and changes to a .h file often trigger unnecessary recompilations across the whole project. -Here, only the interface section is parsed where needed without polluting translation units. -Encapsulates Local Code Cleanly - -The LOCAL section is truly private—functions that should not be exported remain invisible to the rest of the program. -This avoids accidental linking to private helper functions. -Better Optimizations - -The optimizer sees everything within a single .c file. -It inlines functions, eliminates redundant code, and avoids unnecessary function calls more effectively than in a traditional header+implementation split. -This is especially useful for compiler-assisted optimizations like constant propagation and dead code elimination. -Encourages Reusable Programs - -Your philosophy of keeping main() only as an argument parser & function caller means that: -Every program can be called as a function from another program. -Testing frameworks and other programs can reuse code easily without needing to fork process calls. -🧐 Any Downsides? -May be unfamiliar to some developers - -Most C programmers expect .h + .c files. -However, once they understand why this works, they'll likely appreciate the clarity. -Can’t precompile interface headers (.h.gch) - -Some build systems optimize C++ headers with precompiled headers (PCH), but in C, this is less of an issue. -Requires Careful #define Handling - -If a programmer forgets to #define LOCAL before including the file for a .cli.c, tests might silently lack static functions. -A well-placed #error directive (#ifndef LOCAL) could help catch this early. -🚀 Overall Verdict -Your approach is engineered for correctness, clarity, and optimization. -It reduces mistakes, eliminates unnecessary indirections, and streamlines testing & debugging. - -Would I use it? Absolutely. -Would I teach it? Yes, but with an explanation. -Would I want a make-based project full of fragmented .h files again? Not after this. 😆 diff --git "a/developer/document\360\237\226\211/test_framework.org" "b/developer/document\360\237\226\211/test_framework.org" deleted file mode 100644 index d5f059b..0000000 --- "a/developer/document\360\237\226\211/test_framework.org" +++ /dev/null @@ -1,37 +0,0 @@ -* Sectioned Single-File Format - - The sectioned file format provides a structured way to define a library, - keeping all relevant code in a single file while ensuring proper separation - between interface, implementation, and static definitions. - - -** Structure - The source file is divided into three sections: - - - *Interface*: Enabled by defining `IFACE` before including the file. - - *Implementation*: Enabled when neither `IFACE` nor `LOCAL` is defined. - - *Local (Static) Implementation*: Enabled by defining `LOCAL` before including the file. - -** Benefits - - **Avoids Split Headers and Source Files**: Eliminates mismatches between `.h` and `.c` files. - - **Improves Compiler Optimization**: The compiler has full visibility of static functions. - - **Reduces File Clutter**: A single source file contains everything. - - **Ensures Correct Compilation**: The build system automatically compiles the correct sections. - -** Usage - - To include the interface in a `.cli.c` file: - - #+begin_src c - #define IFACE - #include "module.lib.c" - #undef IFACE - #+end_src - - - To include the local static implementation: - - #+begin_src c - #define LOCAL - #include "module.lib.c" - #+end_src - - - The `.lib.c` file is compiled into the library separately. diff --git a/developer/experiment/try_parameter.c b/developer/experiment/try_parameter.c new file mode 100644 index 0000000..fe23c4a --- /dev/null +++ b/developer/experiment/try_parameter.c @@ -0,0 +1,24 @@ +#include + +#define N32· N96· + +int N96·i = 10; +int N32·j = 20; + +int main(){ + printf("N96·i :%x\n",N96·i); + printf("N32·j :%x\n",N96·j); + return 0; +} + +/* + + try_parameter.c:10:24: error: ‘N96·j’ undeclared (first use in this function); did you mean ‘N96·i’? + 10 | printf("N32·j :%x\n",N96·j); + | ^~~~~ + | N96·i + +As of C99 a macro must have a space after it, so the `N32·` of `N32·j` is not being recognized. + + +*/ diff --git "a/developer/tool\360\237\226\211/make" "b/developer/tool\360\237\226\211/make" index 84e1111..6b10a14 100755 --- "a/developer/tool\360\237\226\211/make" +++ "b/developer/tool\360\237\226\211/make" @@ -2,5 +2,16 @@ set -e cd ${REPO_HOME}/developer + +# # Example m4 invocation: +# m4 -D__NAMESPACE__=N128· \ +# -D__DIGIT_EXTENT__=1 \ +# -D__DIGIT_TYPE__=uint64_t \ +# -D__EXTENT_TYPE__=uint64_t \ +# src/N_template.lib.c.m4 > src/N128.lib.c + +# m4 -D__NAMESPACE__=N32· -D__DIGIT_EXTENT__=0 -D__DIGIT_TYPE__=uint32_t -D__EXTENT_TYPE__=uint32_t cc🖉/N_template.lib.c.m4 > cc🖉N32.lib.c + + /bin/make -f tool🖉/makefile $@ diff --git "a/document\360\237\226\211/#SectionApproach.org#" "b/document\360\237\226\211/#SectionApproach.org#" new file mode 100644 index 0000000..e69de29 diff --git "a/document\360\237\226\211/RT_C_namespace.org" "b/document\360\237\226\211/RT_C_namespace.org" new file mode 100644 index 0000000..87a637d --- /dev/null +++ "b/document\360\237\226\211/RT_C_namespace.org" @@ -0,0 +1,21 @@ +#+TITLE: RT C Namespace Convention +#+AUTHOR: RT C Documentation + +* 1. Namespaces in RT C + +C does not provide a built-in mechanism for namespaces, so RT C adopts a *prefix plus scoping operator* convention. The part before the “=·=” (for example, “N32”) is treated as the namespace name, and the “=·=” symbol is a stand-in for an (as yet) unofficial scoping operator. + +For instance, in “=N32·allocate_array=”: +- “=N32=” is the namespace prefix. +- “=·=” (the middle dot) is our ad hoc scoping operator. +- “=allocate_array=” is the actual function name. + +** Advantages of the “Project·Name” Convention +1. **Collision Avoidance**: Prefixing everything with “N32·” (or another module name) ensures identifiers do not collide with those from other libraries or system headers. +2. **Clarity**: Readers can see at a glance which library an identifier belongs to. +3. **Easy to Evolve**: If a future version of C introduces formal namespaces, the “=N32=” prefix and “=·=” operator could map directly into that syntax. +4. **Organized Reusability**: In large codebases with multiple modules, each module has a unique prefix (“N8·”, “N16·”, “N32·”, etc.), making it straightforward to locate definitions and references in a text search. + +** Example Namespace Prefix +When building a 32-bit numeric library, all its public symbols are prefixed with `N32·`. Private/internal symbols (seen only within the file) may also use the prefix but are marked with `Local` or guarded by `#ifdef LOCAL` so they do not pollute the global symbol table. + diff --git "a/document\360\237\226\211/RT_C_tableau_methhod.org" "b/document\360\237\226\211/RT_C_tableau_methhod.org" new file mode 100644 index 0000000..e9e82e2 --- /dev/null +++ "b/document\360\237\226\211/RT_C_tableau_methhod.org" @@ -0,0 +1,177 @@ +#+TITLE: RT Tableau Method +#+AUTHOR: Thomas + +* Memory + +A *base-relative memory map* is a dictionary that associates symbol names with offsets from a base address. A C ~struct typedef~ is an example means for declaring a base-relative memory map. A base-relative memory map has a length associated with it. + +Another term for a *base-relative memory map* is *memory format*. A defined memory format can be bound to a name. The names are required to be distinct, though the formats bound to the names need not be. + +An *instance* of , where is the name of a memory format, is a region of memory that has been bound to memory format . + +The *type* of an instance is the name of its memory format. + +* Pointer + +A *pointer* is a memory address that has been bound to a memory format. This technique is used to assure that writes to the memory address conform to the bound memory format. This helps programs to respect the bounds of memory allocations. + +The *type* of a pointer is the name of the memory format it has been bound to. + +* Function Pointer + +The address of a function can be bound to a name and an array of memory formats, +one for each argument and the return value. + +The *type* of a function is defined by its name and its array of memory format names. + +The type of a function can be looked up from the function's name. + +* Function Dictionary + +A *function dictionary* is a list of function names and addresses, keyed by function names. + +A *function dictionary template* is a collection of function types associated with a specific memory format. + +A *function dictionary instance* is a collection of function implementations that conform to a function dictionary template. + +* Object + +If the potentially many instances of a given memory format are operated on exclusively by a closed set of functions, these functions can be collected into a dictionary called the memory format's *interface*. + +Conversely, there is no conventional name for an instance of memory that is manipulated by only a closed set of functions. In TTCA, the term *tableau* was introduced for such memory. (A *tableau* is a small chalkboard that was once used by students and could be erased and rewritten.) + +Another term that could be used for such memory is *taishou* (対象), meaning "subject" or "target" in Japanese. + +* Working with `.lib.c` Files in RT C + +In the RT C design, each module’s interface **and** implementation are contained in +a single file named `something.lib.c`. Traditional `.h` headers are replaced by +preprocessor-guarded interface sections, while the main implementation and any +“local” (private) code appear in separate blocks. If you want to learn more about +this format, there is a dedicated document explaining how `.lib.c` files are structured, +along with how and why they’re compiled. + +Briefly: + +- **Interface Section** + Declares the memory format (`struct N32·T`, for instance) and the function dictionary + type (`N32·Λ`), along with any prototypes that external code needs. + +- **Implementation Section** + Provides the actual definitions of the functions from the interface, compiled into + an object file (e.g., `N32.lib.o`) that goes into a library (`.a` or `.so`). + +- **Local Section** + (Guarded by `#ifdef LOCAL`) Contains private utility functions or test helpers. + Only included when a `.cli.c` test or other special code explicitly requests it. + +This structure keeps all relevant code (interface, implementation, internal helpers) +in a single `.lib.c` file, yet still enforces encapsulation via preprocessor macros. +It aligns well with the *tableau* and *function dictionary* concepts described here: +the memory format and method table are declared in the interface section, their +implementations go in the implementation section, and if you need advanced debugging +routines or private details, you can optionally include the local section. + + +* More about Tableau (Taishou) + +Below is a minimal example that shows how an instance of a memory format (`N32·T`) can be exclusively manipulated by a *function dictionary* (`N32·Λ`). This concrete “tableau” (taishou) usage illustrates how the memory is bound to specific operations—no external code should modify the memory except through the provided dictionary functions. + +#+BEGIN_SRC c +#include +#include + +// 1. Define the memory format (tableau) +typedef struct { + uint32_t value; +} N32·T; // This struct is our 'tableau'. + +// 2. Provide some functions that operate on N32·T +void N32·copy(N32·T *dst, const N32·T *src){ + dst->value = src->value; +} + +void N32·increment(N32·T *t){ + t->value++; +} + +void N32·print(const N32·T *t){ + printf("Value: %u\n", t->value); +} + +// 3. Declare a function dictionary (method table) +typedef struct { + void (*copy)(N32·T*, const N32·T*); + void (*increment)(N32·T*); + void (*print)(const N32·T*); +} N32·Λ; + +// 4. Provide a concrete instance of the dictionary +const N32·Λ N32·λ = { + .copy = N32·copy, + .increment = N32·increment, + .print = N32·print +}; + +// 5. Demonstrate usage in a main function +int main(void) { + // Create an instance of N32·T (our tableau) + N32·T my_number = { .value = 10 }; + + // Manipulate it exclusively through the function dictionary + N32·λ.increment(&my_number); // Increment by 1 + N32·λ.print(&my_number); // Output should show "Value: 11" + + // Create another instance and copy its contents + N32·T another_number = { .value = 42 }; + N32·λ.copy(&my_number, &another_number); + N32·λ.print(&my_number); // Output should show "Value: 42" + + return 0; +} +#+END_SRC + +When compiled and run (e.g., `gcc example.c -o example && ./example`), the program +prints: + +#+BEGIN_EXAMPLE +Value: 11 +Value: 42 +#+END_EXAMPLE + +In this way, `my_number` is a tableau. All modifications happen via a *closed set of +functions*—here, the operations are defined in the function dictionary `N32·λ`. No +external code is allowed to poke into `my_number.value` directly (beyond this demonstration), +upholding the idea that a “tableau” is memory dedicated to a specific “function dictionary.” + + +* Terminology Considerations + +The use of the word *type* in this document aligns with C’s conventions. However, other +programming paradigms, such as those in C++ and Java, may define *types* differently. +For example, C++ allows types to include behavior (methods), while C’s types only +define memory structure. + +--- + +**Summary of Key Terms:** +- *Memory Format*: A named layout of memory. +- *Instance*: A memory region adhering to a specific format. +- *Pointer*: A memory address bound to a memory format. +- *Function Dictionary*: A set of functions associated with a memory format. +- *Tableau/Taishou*: A memory instance operated on exclusively by a function dictionary. +- *Function Dictionary Template*: A function dictionary where function types replace function implementations. + + +A *base-relative memory map* is a dictionary that associates symbol names with offsets +from a base address. A C ~struct typedef~ is an example means for declaring a +base-relative memory map. A base-relative memory map has a length associated with it. + +Another term for a *base-relative memory map* is *memory format*. A +defined memory format can be bound to a name. The names are required +to be distinct, though the formats bound to the names need not be. + +An *instance* of , where is the name of a memory format, is a region +of memory that has been bound to memory format . + +The *type* of an instance is the name of its memory format. diff --git "a/document\360\237\226\211/RT_cli_c_file.org" "b/document\360\237\226\211/RT_cli_c_file.org" new file mode 100644 index 0000000..06889c5 --- /dev/null +++ "b/document\360\237\226\211/RT_cli_c_file.org" @@ -0,0 +1,147 @@ +#+TITLE: Writing `.cli.c` Files in RT C +#+AUTHOR: RT C Documentation + +* Overview +In RT C projects, a file with a “=.cli.c=” suffix typically implements a **command-line interface** i.e. a program that is run from a shell. Thus each `.cli.c` file produces an **executable** named . + +Common usage includes: +- **Command-Line Interaction** Typical commands run from a shell. +- **Testing**: Execuatbles that test C functions and print pass fail results. +- **Reusable Components**: As discussed in the section below on `main`, cli files can be called from other functions, and thus are themselves reusable components. + +* 1. Including library header information +Because a `.cli.c` file makes use functionality from libraries, it will often include file headers. In RT C, there will be a single file for both the header information and C implementation called, such a file will have a `.lib.c` suffix. The separate sections in a `.lib.c` file are gated by the preprocessor macros, `FACE`, and `LOCAL`. To include the header information from a `.lib.c` file, the `.cli.c` file will have at the top the lines: + +#+BEGIN_SRC c +#define FACE +#include "some_library.lib.c" +#include "some__other_library.lib.c" +... rest of program +#undef FACE +#+END_SRC + +* 2. Including static defined function + +Instead of using `inline` qualifications in the header, in RT coding conventions, static functions are defined in the `.lib.c`, and then included at the bottom of the `.cli.c` file. +Again this is done by gating the sections of the `.lib.c` file: + +#+BEGIN_SRC c +... rest of program + +#define LOCAL +#include "some_library.lib.c" +#include "some__other_library.lib.c" +... +#undef LOCAL +#+END_SRC + +* 3. Both header information and static defined functions: + +#+BEGIN_SRC c +#define FACE +#include "N32.lib.c" +#undef FACE + + ... rest of program + +#define LOCAL +#include "N32.lib.c" +#undef LOCAL +#+END_SRC + +- `FACE` grants access to the library’s public symbols (structs, function prototypes). +- `LOCAL` gives access to internal (“static-like”) helpers if needed for deeper testing or debugging. Only define it if you truly require these private functions/data. + +The idea here is that that optimizer can do its best work when it sees everything in one place, and thus there is no need for inline functions. On paradigm can be used for defining the program. + +* 4. The `main()` Function in `.cli.c` +Every `.cli.c` includes a `main()` function, but with special guidelines: + +1. **Minimal Parsing & Dispatch** + - `main()` is primarily responsible for reading `argc`/`argv`, validating inputs, and directing calls to your actual logic or library code. + +2. **Reusable Subroutines** + - If your `.cli.c` logic is extensive, break it out into functions that `main()` calls. This way, **other** `.cli.c` files (or even libraries) can call these same functions directly without launching a new process. + +3. **Avoid Side Effects** + - Keep `main()` from doing excessive configuration or global side effects (e.g., changing global state). This maintains a clean separation between your library code and the command-line scaffolding. + +4. **Standard Return Codes** + - Return 0 for success, non-zero for errors. If the CLI is also used programmatically by other modules, they can interpret these returns or, better yet, call your internal subroutines that return more detailed error codes. + +Example skeleton: + +#+BEGIN_SRC c +int main(int argc, char **argv){ + // 1. Parse command-line args + // 2. Validate or print usage + // 3. Call library functions or dedicated subroutines + // 4. Return 0 on success, or an error code otherwise +} +#+END_SRC + +* 5. Example of a `.cli.c` File + + +Below is a **working** `.cli.c` program demonstrating how to use the `N32.lib.c` library: + +#+BEGIN_SRC c :tangle test_N32.cli.c +#include +#include +#include + +// Expose the N32 interface by defining FACE +#define FACE +#include "N32.lib.c" +#undef FACE + +int main(int argc ,char **argv){ + if(argc < 3){ + printf("Usage: %s \n", argv[0]); + return 1; + } + + uint32_t a_val = (uint32_t)atoi(argv[1]); + uint32_t b_val = (uint32_t)atoi(argv[2]); + + // Allocate N32 objects + N32·T *numbers = N32·λ.allocate_array(2 ,NULL); + if(!numbers){ + printf("Allocation failed.\n"); + return 2; + } + + // Initialize + N32·λ.from_uint32(N32·λ.access(numbers ,0) ,a_val); + N32·λ.from_uint32(N32·λ.access(numbers ,1) ,b_val); + + // Perform add + N32·T result; + N32·λ.add(&result, N32·λ.access(numbers ,0), N32·λ.access(numbers ,1)); + printf("Sum of %u and %u is %u\n", a_val, b_val, result.d0); + + // Perform subtract + N32·λ.subtract(&result, N32·λ.access(numbers ,0), N32·λ.access(numbers ,1)); + printf("Diff of %u and %u is %u\n", a_val, b_val, result.d0); + + N32·λ.deallocate(numbers); + return 0; +} +#+END_SRC + +Here, `main()` parses two integer arguments, allocates an array of `N32·T`, +calls the library’s add and subtract functions, and prints the results. +If this is compiled alongside (or linked against) the `N32.lib.c` object code, +running `./test_N32 20 12` will produce: + +#+BEGIN_EXAMPLE +Sum of 20 and 12 is 32 +Diff of 20 and 12 is 8 +#+END_EXAMPLE + + +* 6. Summary +- **`.cli.c`** files produce executables with a central `main()` function focused on argument parsing and error handling. +- Actual logic often lives in separate subroutines (or in `.lib.c` libraries) to encourage reuse without spawning new processes. +- The RT C approach puts library interfaces in the same file as their implementation (`.lib.c`), so `#define FACE` (and optionally `#define LOCAL`) is how a `.cli.c` pulls in the necessary API or private details. +- This design maintains a clean separation between **common library functionality** and **CLI/test scaffolding**, making it easy to test or chain multiple CLIs together within the same program. diff --git "a/document\360\237\226\211/RT_lib_c_file.org" "b/document\360\237\226\211/RT_lib_c_file.org" new file mode 100644 index 0000000..9f47698 --- /dev/null +++ "b/document\360\237\226\211/RT_lib_c_file.org" @@ -0,0 +1,107 @@ +#+TITLE: The `.lib.c` Format +#+AUTHOR: RT C Documentation + + +* 1. The `.lib.c` File: Purpose and Sections +A single `.lib.c` file contains **interface**, **implementation**, and **local** (private) code in one place. This is an alternative to the traditional “.h + .c” pairing. Below is how the sections typically appear: + +* 2 Overview +1. **Interface Section** (replaces `.h` in traditional C) + - Guarded by `#ifndef N32·FACE ... #define N32·FACE ... #endif`. + - Contains `typedef`s, structs, enums, function prototypes—everything needed for external code to use the library. + +1. **Implementation Section** (the code compiled into the `.o` files, or `.a` or '.so' library) + - Enabled by `#define N32·IMPLEMENTATION`. + - Provides function bodies, global variables, and logic. + - Typically compiled once into the library object. + +3. **Local (Static) Section** (private code) + - Guarded by `#ifdef LOCAL`. + - Contains helper functions or data only needed in special scenarios (like testing or advanced usage). + - Marked with `Local` (instead of `static`) to indicate internal linkage. + +* 3 Macros at the Top +At the very top of a `.lib.c` file, you might see: +#+BEGIN_SRC c +//#define N32·DEBUG + +#ifndef FACE + #define N32·IMPLEMENTATION + #define FACE +#endif +#+END_SRC + +Explanation: + +- `N32·DEBUG` can be toggled for debugging features (or left commented). +- `#ifndef FACE` ensures the code only defines `N32·IMPLEMENTATION` if another block (like an interface-only include) hasn’t already set `FACE`. +- Once `N32·IMPLEMENTATION` is set, it means “compile the implementation code” by default. + +* 4 The Interface Section +Next, we see something like: +#+BEGIN_SRC c +#ifndef N32·FACE +#define N32·FACE + +// interface code here (typedefs, prototypes, extern definitions) +// ... +#endif +#+END_SRC + +- Prevents multiple inclusion of the interface (`N32·FACE`). +- Acts like a header: external code can `#define IFACE` or define `FACE` to pull in these declarations. + +* 5 The Implementation Section +After the interface `#endif`, you typically see: +#+BEGIN_SRC c +//-------------------------------------------------------------------------------- +// Implementation + +#ifdef N32·IMPLEMENTATION + // ... + #ifndef LOCAL + // Code that goes into the library object (main .o) + #endif + + #ifdef LOCAL + // Additional local or inline-like code + #endif +#endif +#+END_SRC + +Breakdown: + +1. `#ifdef N32·IMPLEMENTATION`: + - If this macro is set (usually when compiling `.lib.c` directly), we include all code for the library’s global functions. + +1. `#ifndef LOCAL`: + - Code in this block becomes part of the final `.o` or `.a` file. It is the public “library” portion, implementing the functions declared in the interface. + +3. `#ifdef LOCAL`: + - Private or test-only code, included only when some other file includes this `.lib.c` with `#define LOCAL`. + +* 6 Local vs. Static +Inside the local section, you might see `Local N32·T my_array[4]` instead of `static N32·T my_array[4]`. Historically, `static` in C means both “file scope” and “static storage duration.” The RT code style uses `Local` to clarify that these symbols have internal linkage and are “private” to this compilation unit. It’s effectively the same as `static` but more explicit in intent. + +* 7 Example Compilation Workflow +1. **Library Compilation** + - Invoke the compiler on `N31.lib.c` *without* defining `IFACE` or `LOCAL`. + - The file sees `N32·IMPLEMENTATION` → it compiles the interface + implementation code. + - Produces `N31.lib.o`, which can then be placed in `N.a` or `N.so`. + +1. **CLI or Test Program** + - A `.cli.c` might do: + #+BEGIN_SRC c + #define LOCAL + #include "N31.lib.c" + #undef LOCAL + // Now we have local (private) helpers for testing + + int main() { + // test or CLI logic calling the library + } + #+END_SRC + - This allows direct access to the library’s “private” or local section for debugging or specialized usage. + - The resulting `.cli.o` links with `N31.lib.o` from the library build, forming an executable. + +With this design, everything about a module (interface, implementation, private code) resides in a single file (`.lib.c`). The build system compiles these files into objects and links them to produce either a static or dynamic library. Meanwhile, `.cli.c` files become executables that can optionally include the local code if needed. diff --git "a/document\360\237\226\211/SectionApproach.org" "b/document\360\237\226\211/SectionApproach.org" new file mode 100644 index 0000000..7cb4a1f --- /dev/null +++ "b/document\360\237\226\211/SectionApproach.org" @@ -0,0 +1,121 @@ +#+TITLE: Sectioned Single-File Format +#+AUTHOR: RT C Documentation Team +#+OPTIONS: toc:2 + +* Overview +The sectioned single-file format provides a structured way to define a library in C, +keeping all relevant code in a single file while ensuring proper separation between +interface, implementation, and local static definitions. This approach avoids +the common “.h + .c” split and instead uses conditional compilation blocks +to expose only the needed parts. + +** Key Definitions +- =IFACE=: A preprocessor symbol indicating we want to expose the library’s interface. +- =LOCAL=: A preprocessor symbol indicating we want to expose static (private) helper code. +- *Implementation (no symbol defined)*: The default, containing the shared implementation + of the library. + +** File Layout +A typical =.lib.c= file has three sections: + +1. *Interface Section* + Guarded by: + #+begin_src c + #ifndef NNN·FACE + #define NNN·FACE + // public typedefs, function declarations + // ... + #endif + #+end_src + + Here, “=NNN=” is the library name (e.g., =N32=). + You include this section in another file using: + #+begin_src c + #define IFACE + #include "my_library.lib.c" + #undef IFACE + #+end_src + +2. *Implementation Section* + Defined when *neither* =IFACE= nor =LOCAL= is set. This is the main, global + portion of the library code that will be compiled into an object + (or archived into a static library): + #+begin_src c + #ifdef NNN·IMPLEMENTATION + // Implementation code + // ... + #endif + #+end_src + + The build system compiles this =.lib.c= without =IFACE= or =LOCAL= defined + to produce the main library object. + +3. *Local (Static) Section* + Guarded by: + #+begin_src c + #ifdef LOCAL + // In-library static declarations or definitions + // ... + #endif + #end_src + + This block is typically included after the user code, letting you inline or + define “private” helper functions that only the user’s code should see + (in order to be optimized away if not used, for instance). + +** Why This Matters +- *Single Source of Truth*: Instead of maintaining separate header and source files, + everything for a module is in one place. +- *Automatic Inlining*: In a traditional setup, “private” static functions would + live in the .c file and be invisible for cross-module inlining. With the local + section, the compiler sees everything when the user includes it, enabling more + optimizations. +- *Reduced Mismatch Errors*: There’s zero chance your header becomes out-of-sync + with the .c file because they’re literally the same file, gated by preprocessor macros. +- *Cleaner Build Scripts*: The library is compiled once with the default macros. + Tests or command-line programs that need the local portion define =LOCAL= or =IFACE= + as needed. + +** Example Usage +1. *Library Compilation* + #+begin_src bash + # No defines set: + $CC -c my_library.lib.c -o my_library.lib.o + #+end_src + +2. *Including the Interface in Another Source* + #+begin_src c + #define IFACE + #include "my_library.lib.c" + #undef IFACE + + int main(){ + // Now we have the function declarations from the interface + // ... + } + #+end_src + +3. *Including the Local Section* + #+begin_src c + #define LOCAL + #include "my_library.lib.c" + #undef LOCAL + + // We can now use certain static or inline-like helpers + // ... + #+end_src + +** Makefile Integration +The provided Makefile snippet shows how to compile =.lib.c= files separately, +then link them. The key is to define no macros (like =IFACE= or =LOCAL=) +when building the library, ensuring you get only the “Implementation Section.” + +For a command-line interface (CLI) or test harness, you can then define =IFACE= +or =LOCAL= before including the =.lib.c= to access the library’s interface +(or private/local stuff) on an as-needed basis. + +* Conclusion +This method elegantly encapsulates a module’s interface, its global +implementation, and its private helper code in a single file. It integrates +nicely with existing make-based build systems and fosters a clear separation +of concerns with minimal overhead or confusion. diff --git "a/document\360\237\226\211/library_usage.org" "b/document\360\237\226\211/library_usage.org" deleted file mode 100644 index c7b3874..0000000 --- "a/document\360\237\226\211/library_usage.org" +++ /dev/null @@ -1,73 +0,0 @@ -* Using the N_32 Library - - This document describes how to build, use, and test the N_32 library module. - - -** 1. Overview - The `N_32` library provides a 32-bit natural number type with basic arithmetic, - bitwise, and shifting operations. It follows the **single-source file** - approach, where `N_32.lib.c` contains both interface and implementation. - -** 2. Building the Library - - The library is compiled using the shared `make` infrastructure. - - #+begin_src sh - make library - #+end_src - - This will: - - Compile `N_32.lib.c` into an object file. - - Archive it into `libN.a` in `scratchpad/`. - -** 3. Using the Library in a Project - - Include `N_32.lib.c` directly in sources by defining `FACE`: - #+begin_src c - #define FACE - #include "N_32.lib.c" - #undef FACE - #+end_src - - Link against `libN.a` when compiling executables. - -** 4. Running Tests - The `test_N_32.cli.c` file provides unit tests. - - - Build the test executable: - #+begin_src sh - make cli - #+end_src - - Run the test: - #+begin_src sh - ./tester/test_N_32 - #+end_src - - The test system reports pass/fail counts: - #+begin_example - Passed: 25, Failed: 0 - #+end_example - -** 5. Understanding the Sectioned File Approach - - `N_32.lib.c` contains both the interface and implementation. - - It is included twice: - 1. **Interface Section (`FACE`)**: Provides declarations. - 2. **Local Section (`LOCAL`)**: Provides static/internal functions. - - - This method reduces duplication, avoids header mismatches, and enables - the optimizer to work on `LOCAL` functions effectively. - -** 6. Makefile Integration - The shared `make` system automates building and linking: - - `make library` → Builds `libN.a`. - - `make cli` → Builds test executables. - - `make dependency` → Generates dependency files. - - `make clean` → Removes intermediate files. - - Custom projects can include the library with: - #+begin_src makefile - LIBS += -L$(LIBDIR) -lN - CFLAGS += -I$(SRCDIR) - #+end_src - -** 7. Best Practices - - Run `make dependency` to ensure dependencies are up to date. - - Use the test suite to validate changes before release. - diff --git "a/document\360\237\226\211/nomenclature.org" "b/document\360\237\226\211/nomenclature.org" deleted file mode 100644 index e706a7f..0000000 --- "a/document\360\237\226\211/nomenclature.org" +++ /dev/null @@ -1,113 +0,0 @@ -#+TITLE: Memory and Function Dictionaries -#+AUTHOR: Thomas - -* Memory - -A *base-relative memory map* is a dictionary that associates symbol names with offsets from a base address. A C ~struct typedef~ is an example means for declaring a base-relative memory map. A base-relative memory map has a length associated with it. - -Another term for a *base-relative memory map* is *memory format*. A defined memory format can be bound to a name. The names are required to be distinct, though the formats bound to the names need not be. - -An *instance* of , where is the name of a memory format, is a region of memory that has been bound to memory format . - -The *type* of an instance is the name of its memory format. - -* Pointer - -A *pointer* is a memory address that has been bound to a memory format. This technique is used to assure that writes to the memory address conform to the bound memory format. This helps programs to respect the bounds of memory allocations. - -The *type* of a pointer is the name of the memory format it has been bound to. - -* Function Pointer - -The address of a function can be bound to a name and an array of memory formats, -one for each argument and the return value. - -The *type* of a function is defined by its name and its array of memory format names. - -The type of a function can be looked up from the function's name. - -* Function Dictionary - -A *function dictionary* is a list of function names and addresses, keyed by function names. - -A *function dictionary template* is a collection of function types associated with a specific memory format. - -A *function dictionary instance* is a collection of function implementations that conform to a function dictionary template. - -* Object - -If the potentially many instances of a given memory format are operated on exclusively by a closed set of functions, these functions can be collected into a dictionary called the memory format's *interface*. - -Conversely, there is no conventional name for an instance of memory that is manipulated by only a closed set of functions. In TTCA, the term *tableau* was introduced for such memory. (A *tableau* is a small chalkboard that was once used by students and could be erased and rewritten.) - -Another term that could be used for such memory is *taishou* (対象), meaning "subject" or "target" in Japanese. - -* More about Tableau (Taishou) - -In the case where an instance is exclusively operated on by a closed function dictionary, -it can be referred to as a *tableau* (or *taishou* 対象). A *tableau* is memory that is -subject to modification only through the functions of its function dictionary. - -This term avoids the ambiguity of *instance*, which is often used generically in other -contexts. The word *tableau* refers to a reusable writing surface, much like the small -chalkboards used by students in past centuries. The alternative term *taishou* 対象 -literally means *the subject of attention*, which closely aligns with its role in programming. - -#+BEGIN_SRC c -// Example: Function Dictionary and Tableau -typedef struct { - uint32_t value; -} N32·T; // The tableau type (taishou type) - -typedef struct { - void (*copy)(N32·T *dst, const N32·T *src); - void (*increment)(N32·T *t); - void (*print)(const N32·T *t); -} N32·Λ; // The function dictionary - -void N32·copy(N32·T *dst, const N32·T *src) { dst->value = src->value; } -void N32·increment(N32·T *t) { t->value++; } -void N32·print(const N32·T *t) { printf("Value: %u\n", t->value); } - -// Function dictionary instance -const N32·Lambda N32·λ = { .copy = N32·copy, .increment = N32·increment, .print = N32·print }; - -// Usage example -N32·T my_number = { .value = 10 }; // creating a tableau -N32·λ.increment(&my_number); -N32·λ.print(&my_number); // Output: Value: 11 -#+END_SRC - -(Note, this is a contrived example, not legal usage of the N library. I should -probably update it.) - -* Terminology Considerations - -The use of the word *type* in this document aligns with C’s conventions. However, other -programming paradigms, such as those in C++ and Java, may define *types* differently. -For example, C++ allows types to include behavior (methods), while C’s types only -define memory structure. - ---- - -**Summary of Key Terms:** -- *Memory Format*: A named layout of memory. -- *Instance*: A memory region adhering to a specific format. -- *Pointer*: A memory address bound to a memory format. -- *Function Dictionary*: A set of functions associated with a memory format. -- *Tableau/Taishou*: A memory instance operated on exclusively by a function dictionary. -- *Function Dictionary Template*: A function dictionary where function types replace function implementations. - - -A *base-relative memory map* is a dictionary that associates symbol names with offsets -from a base address. A C ~struct typedef~ is an example means for declaring a -base-relative memory map. A base-relative memory map has a length associated with it. - -Another term for a *base-relative memory map* is *memory format*. A -defined memory format can be bound to a name. The names are required -to be distinct, though the formats bound to the names need not be. - -An *instance* of , where is the name of a memory format, is a region -of memory that has been bound to memory format . - -The *type* of an instance is the name of its memory format.