From: Thomas Walker Lynch Date: Fri, 14 Feb 2025 06:30:20 +0000 (+0000) Subject: N32 tests pass, addition of N16 and N8 X-Git-Url: https://git.reasoningtechnology.com/style/static/%7Bstyle.link%7D?a=commitdiff_plain;h=15a6cfda7e974b669d82782a68ed070d1a8d3270;p=N N32 tests pass, addition of N16 and N8 --- diff --git a/developer/amd64/test_N32 b/developer/amd64/test_N32 new file mode 100755 index 0000000..36150b2 Binary files /dev/null and b/developer/amd64/test_N32 differ diff --git a/developer/amd64/test_setup b/developer/amd64/test_setup new file mode 100755 index 0000000..d5834b8 Binary files /dev/null and b/developer/amd64/test_setup differ diff --git "a/developer/cc\360\237\226\211/N16.lib.c" "b/developer/cc\360\237\226\211/N16.lib.c" new file mode 100644 index 0000000..3b77116 --- /dev/null +++ "b/developer/cc\360\237\226\211/N16.lib.c" @@ -0,0 +1,433 @@ +/* + N16 - 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 N16·DEBUG + +#ifndef FACE +#define N16·IMPLEMENTATION +#define FACE +#endif + +//-------------------------------------------------------------------------------- +// Interface + +#ifndef N16·FACE +#define N16·FACE + + #include + #include + #include + #include + + //---------------------------------------- + // Instance Data (Declaration Only) + + typedef uint16_t Extent; + typedef uint16_t Digit; + + typedef struct N16·T N16·T; + + extern N16·T *N16·zero; + extern N16·T *N16·one; + extern N16·T *N16·all_one_bit; + extern N16·T *N16·lsb; + extern N16·T *N16·msb; + + //---------------------------------------- + // Return/Error Status and handlers + + typedef enum{ + N16·Status·ok = 0 + ,N16·Status·overflow = 1 + ,N16·Status·accumulator1_overflow = 2 + ,N16·Status·carry = 3 + ,N16·Status·borrow = 4 + ,N16·Status·undefined_divide_by_zero = 5 + ,N16·Status·undefined_modulus_zero = 6 + ,N16·Status·gt_max_shift_count = 7 + ,N16·Status·spill_eq_operand = 8 // not currently signaled, result will be spill value + ,N16·Status·one_word_product = 9 + ,N16·Status·two_word_product = 10 + } N16·Status; + + typedef enum{ + N16·Order_lt = -1 + ,N16·Order_eq = 0 + ,N16·Order_gt = 1 + } N16·Order; + + typedef N16·T *( *N16·Allocate_MemoryFault )(Extent); + + //---------------------------------------- + // Interface + + typedef struct{ + + N16·T *(*allocate_array_zero)(Extent, N16·Allocate_MemoryFault); + N16·T *(*allocate_array)(Extent, N16·Allocate_MemoryFault); + void (*deallocate)(N16·T*); + + void (*copy)(N16·T*, N16·T*); + void (*bit_and)(N16·T*, N16·T*, N16·T*); + void (*bit_or)(N16·T*, N16·T*, N16·T*); + void (*bit_complement)(N16·T*, N16·T*); + void (*bit_twos_complement)(N16·T*, N16·T*); + N16·Order (*compare)(N16·T*, N16·T*); + bool (*lt)(N16·T*, N16·T*); + bool (*gt)(N16·T*, N16·T*); + bool (*eq)(N16·T*, N16·T*); + bool (*eq_zero)(N16·T*); + N16·Status (*accumulate)(N16·T *accumulator1 ,N16·T *accumulator0 ,...); + N16·Status (*add)(N16·T*, N16·T*, N16·T*); + bool (*increment)(N16·T *a); + N16·Status (*subtract)(N16·T*, N16·T*, N16·T*); + N16·Status (*multiply)(N16·T*, N16·T*, N16·T*, N16·T*); + N16·Status (*divide)(N16·T*, N16·T*, N16·T*, N16·T*); + N16·Status (*modulus)(N16·T*, N16·T*, N16·T*); + N16·Status (*shift_left)(Extent, N16·T*, N16·T*, N16·T*); + N16·Status (*shift_right)(Extent, N16·T*, N16·T*, N16·T*); + N16·Status (*arithmetic_shift_right)(Extent, N16·T*, N16·T*); + + N16·T* (*access)(N16·T*, Extent); + void (*from_uint32)(N16·T *destination ,uint32_t value); + } N16·Λ; + + Local const N16·Λ N16·λ; // initialized in the LOCAL section + +#endif + +//-------------------------------------------------------------------------------- +// Implementation + +#ifdef N16·IMPLEMENTATION + + // this part goes into the library + #ifndef LOCAL + + #include + #include + + struct N16·T{ + Digit d0; + }; + + N16·T N16·constant[4] = { + {.d0 = 0}, + {.d0 = 1}, + {.d0 = ~(uint16_t)0}, + {.d0 = 1 << 15} + }; + + N16·T *N16·zero = &N16·constant[0]; + N16·T *N16·one = &N16·constant[1]; + N16·T *N16·all_one_bit = &N16·constant[2]; + N16·T *N16·msb = &N16·constant[3]; + N16·T *N16·lsb = &N16·constant[1]; + + // the allocate an array of N16 + N16·T *N16·allocate_array(Extent extent ,N16·Allocate_MemoryFault memory_fault){ + N16·T *instance = malloc((extent + 1) * sizeof(N16·T)); + if(!instance){ + return memory_fault ? memory_fault(extent) : NULL; + } + return instance; + } + + N16·T *N16·allocate_array_zero(Extent extent ,N16·Allocate_MemoryFault memory_fault){ + N16·T *instance = calloc(extent + 1, sizeof(N16·T)); + if(!instance){ + return memory_fault ? memory_fault(extent) : NULL; + } + return instance; + } + + void N16·deallocate(N16·T *unencumbered){ + free(unencumbered); + } + + + #endif + + // This part is included after the library user's code + #ifdef LOCAL + + // instance + + struct N16·T{ + Digit d0; + }; + + // temporary variables + Local N16·T N16·t[4]; + + // allocation + + extern N16·T *N16·allocate_array(Extent, N16·Allocate_MemoryFault); + extern N16·T *N16·allocate_array_zero(Extent, N16·Allocate_MemoryFault); + extern void N16·deallocate(N16·T *); + + // so the user can access numbers in an array allocation + Local N16·T* N16·access(N16·T *array ,Extent index){ + return &array[index]; + } + + Local void N16·from_uint32(N16·T *destination ,uint32_t value){ + if(destination == NULL) return; + destination->d0 = (uint16_t)(value & 0xFFFF); + } + + // copy, convenience copy + + Local void N16·copy(N16·T *destination ,N16·T *source){ + if(source == destination) return; + *destination = *source; + } + + Local void N16·set_to_zero(N16·T *instance){ + instance->d0 = 0; + } + + Local void N16·set_to_one(N16·T *instance){ + instance->d0 = 1; + } + + // bit operations + + Local void N16·bit_and(N16·T *result, N16·T *a, N16·T *b){ + result->d0 = a->d0 & b->d0; + } + + Local void N16·bit_or(N16·T *result, N16·T *a, N16·T *b){ + result->d0 = a->d0 | b->d0; + } + + Local void N16·bit_complement(N16·T *result, N16·T *a){ + result->d0 = ~a->d0; + } + + Local void N16·bit_twos_complement(N16·T *result ,N16·T *a){ + result->d0 = (uint16_t)(~a->d0 + 1); + } + + // test functions + + Local N16·Order N16·compare(N16·T *a, N16·T *b){ + if(a->d0 < b->d0) return N16·Order_lt; + if(a->d0 > b->d0) return N16·Order_gt; + return N16·Order_eq; + } + + Local bool N16·lt(N16·T *a ,N16·T *b){ + return a->d0 < b->d0; + } + + Local bool N16·gt(N16·T *a ,N16·T *b){ + return a->d0 > b->d0; + } + + Local bool N16·eq(N16·T *a ,N16·T *b){ + return a->d0 == b->d0; + } + + Local bool N16·eq_zero(N16·T *a){ + return a->d0 == 0; + } + + // arithmetic operations + + Local N16·Status N16·accumulate(N16·T *accumulator1 ,N16·T *accumulator0 ,...){ + + va_list args; + va_start(args ,accumulator0); + uint32_t sum = accumulator0->d0; + uint32_t carry = 0; + N16·T *current; + + while( (current = va_arg(args ,N16·T*)) ){ + sum += current->d0; + if(sum < current->d0){ + (carry)++; + if(carry == 0){ + va_end(args); + return N16·Status·accumulator1_overflow; + } + } + } + va_end(args); + + accumulator1->d0 = (uint16_t)carry; + return N16·Status·ok; + } + + Local N16·Status N16·add(N16·T *sum ,N16·T *a ,N16·T *b){ + uint32_t result = (uint32_t)a->d0 + (uint32_t)b->d0; + sum->d0 = (uint16_t)(result & 0xFFFF); + return (result >> 16) ? N16·Status·carry : N16·Status·ok; + } + + Local bool N16·increment(N16·T *a){ + a->d0++; + return (a->d0 == 0); + } + + Local N16·Status N16·subtract(N16·T *difference ,N16·T *a ,N16·T *b){ + uint32_t diff = (uint32_t)a->d0 - (uint32_t)b->d0; + difference->d0 = (uint16_t)(diff & 0xFFFF); + return (diff > a->d0) ? N16·Status·borrow : N16·Status·ok; + } + + Local N16·Status N16·multiply(N16·T *product1 ,N16·T *product0 ,N16·T *a ,N16·T *b){ + uint32_t product = (uint32_t)a->d0 * (uint32_t)b->d0; + product0->d0 = (uint16_t)(product & 0xFFFF); + product1->d0 = (uint16_t)((product >> 16) & 0xFFFF); + + if(product1->d0 == 0) return N16·Status·one_word_product; + return N16·Status·two_word_product; + } + + Local N16·Status N16·divide(N16·T *remainder ,N16·T *quotient ,N16·T *a ,N16·T *b){ + if(b->d0 == 0) return N16·Status·undefined_divide_by_zero; + + uint32_t dividend = a->d0; + uint32_t divisor = b->d0; + quotient->d0 = (uint16_t)(dividend / divisor); + remainder->d0 = (uint16_t)(dividend - (quotient->d0 * divisor)); + + return N16·Status·ok; + } + + Local N16·Status N16·modulus(N16·T *remainder ,N16·T *a ,N16·T *b){ + if(b->d0 == 0) return N16·Status·undefined_modulus_zero; + uint32_t dividend = a->d0; + uint32_t divisor = b->d0; + uint32_t q = dividend / divisor; + remainder->d0 = (uint16_t)(dividend - (q * divisor)); + return N16·Status·ok; + } + + // bit motion + + typedef uint16_t (*ShiftOp)(uint16_t, uint16_t); + + Local uint16_t shift_left_op(uint16_t value, uint16_t amount){ + return value << amount; + } + + Local uint16_t shift_right_op(uint16_t value, uint16_t amount){ + return (uint16_t)(value >> amount); + } + + Local N16·Status N16·shift + ( + uint16_t shift_count + ,N16·T *spill + ,N16·T *operand + ,N16·T *fill + ,ShiftOp shift_op + ,ShiftOp complement_shift_op + ){ + + if(operand == NULL && spill == NULL) return N16·Status·ok; + + if(operand == NULL){ + operand = &N16·t[0]; + N16·copy(operand, N16·zero); + } + + if(shift_count > 15) return N16·Status·gt_max_shift_count; + + N16·T *given_operand = &N16·t[1]; + N16·copy(given_operand, operand); + + operand->d0 = shift_op(given_operand->d0, shift_count); + if(fill != NULL){ + fill->d0 = complement_shift_op(fill->d0, (16 - shift_count)); + N16·bit_or(operand, operand, fill); + } + if(spill != NULL){ + spill->d0 = shift_op(spill->d0, shift_count); + spill->d0 += complement_shift_op(given_operand->d0, (16 - shift_count)); + } + + return N16·Status·ok; + } + + Local N16·Status + N16·shift_left(uint16_t shift_count, N16·T *spill, N16·T *operand, N16·T *fill){ + return N16·shift(shift_count, spill, operand, fill, shift_left_op, shift_right_op); + } + + Local N16·Status + N16·shift_right(uint16_t shift_count, N16·T *spill, N16·T *operand, N16·T *fill){ + return N16·shift(shift_count, spill, operand, fill, shift_right_op, shift_left_op); + } + + Local N16·Status + N16·arithmetic_shift_right(uint16_t shift_count, N16·T *operand, N16·T *spill){ + + if(shift_count > 15) return N16·Status·gt_max_shift_count; + + if(operand == NULL){ + operand = &N16·t[0]; + N16·copy(operand, N16·zero); + } + + N16·T *fill = (operand->d0 & 0x8000) ? N16·all_one_bit : N16·zero; + return N16·shift_right(shift_count, spill, operand, fill); + } + + Local const N16·Λ N16·λ = { + + .allocate_array = N16·allocate_array + ,.allocate_array_zero = N16·allocate_array_zero + ,.deallocate = N16·deallocate + + ,.copy = N16·copy + ,.bit_and = N16·bit_and + ,.bit_or = N16·bit_or + ,.bit_complement = N16·bit_complement + ,.bit_twos_complement = N16·bit_twos_complement + ,.compare = N16·compare + ,.lt = N16·lt + ,.gt = N16·gt + ,.eq = N16·eq + ,.eq_zero = N16·eq_zero + ,.accumulate = N16·accumulate + ,.add = N16·add + ,.increment = N16·increment + ,.subtract = N16·subtract + ,.multiply = N16·multiply + ,.divide = N16·divide + ,.modulus = N16·modulus + ,.shift_left = N16·shift_left + ,.shift_right = N16·shift_right + ,.arithmetic_shift_right = N16·arithmetic_shift_right + + ,.access = N16·access + ,.from_uint32 = N16·from_uint32 + }; + + #endif + +#endif diff --git "a/developer/cc\360\237\226\211/N32.lib.c" "b/developer/cc\360\237\226\211/N32.lib.c" index ff69120..fba3c21 100644 --- "a/developer/cc\360\237\226\211/N32.lib.c" +++ "b/developer/cc\360\237\226\211/N32.lib.c" @@ -36,85 +36,85 @@ #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 + #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 @@ -123,336 +123,335 @@ Local const N32·Λ N32·λ; // initialized in the LOCAL section #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); -} + // 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; + } -#endif + 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; + } -// This part is included after the library user's code -#ifdef LOCAL + // bit operations -// instance + 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; + } -struct N32·T{ - Digit d0; -}; + // test functions -// 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·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 N32·T N32·t[4]; + 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; -// allocation + return N32·Status·ok; + } -extern N32·T *N32·allocate_array(Extent, N32·Allocate_MemoryFault); -extern N32·T *N32·alloc_array_zero(Extent, N32·Allocate_MemoryFault); -extern void N32·deallocate(N32·T *); + 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; + } -// so the user can access numbers in an array allocation -Local N32·T* N32·access(N32·T *array ,Extent index){ - return &array[index]; -} + 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 void N32·from_uint32(N32·T *destination ,uint32_t value){ - if(destination == NULL) return; - destination->d0 = value; -} -// copy, convenience copy + 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); -Local void N32·copy(N32·T *destination ,N32·T *source){ - if(source == destination) return; // that was easy! - *destination = *source; -} + if(product1->d0 == 0) return N32·Status·one_word_product; + return N32·Status·two_word_product; + } -Local void N32·set_to_zero(N32·T *instance){ - instance->d0 = 0; -} + 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; -Local void N32·set_to_one(N32·T *instance){ - instance->d0 = 1; -} + quotient->d0 = a->d0 / b->d0; + remainder->d0 = a->d0 - (quotient->d0 * b->d0); -// bit operations + return N32·Status·ok; + } -Local void N32·bit_and(N32·T *result, N32·T *a, N32·T *b){ - result->d0 = a->d0 & b->d0; -} + 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; + } -// 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; -} + // bit motion -// result can the same as the operand -Local void N32·bit_complement(N32·T *result, N32·T *a){ - result->d0 = ~a->d0; -} + typedef uint32_t (*ShiftOp)(uint32_t, uint32_t); -// result can the same as the operand -Local void N32·bit_twos_complement(N32·T *result ,N32·T *a){ - result->d0 = ~a->d0 + 1; -} + Local uint32_t shift_left_op(uint32_t value, uint32_t amount){ + return value << amount; + } -// test functions + Local uint32_t shift_right_op(uint32_t value, uint32_t amount){ + return value >> amount; + } -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; -} + // 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); + } -Local bool N32·lt(N32·T *a ,N32·T *b){ - return a->d0 < b->d0; -} + // Shifting more than one word breaks our fill/spill model. + if(shift_count > 31) return N32·Status·gt_max_shift_count; -Local bool N32·gt(N32·T *a ,N32·T *b){ - return a->d0 > b->d0; -} + // 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); -Local bool N32·eq(N32·T *a ,N32·T *b){ - return a->d0 == b->d0; -} + // 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)); + } -Local bool N32·eq_zero(N32·T *a){ - return a->d0 == 0; -} + 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); + } -// arithmetic operations + 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); + } -// 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 ,...){ + Local N32·Status + N32·arithmetic_shift_right(uint32_t shift_count, N32·T *operand, N32·T *spill){ - va_list args; - va_start(args ,accumulator0); - uint32_t sum = accumulator0->d0; - uint32_t carry = 0; - N32·T *current; + // Guard against excessive shift counts + if(shift_count > 31) return N32·Status·gt_max_shift_count; - 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; + // 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); } - } - 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·alloc_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 + 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/N8.lib.c" "b/developer/cc\360\237\226\211/N8.lib.c" new file mode 100644 index 0000000..521666f --- /dev/null +++ "b/developer/cc\360\237\226\211/N8.lib.c" @@ -0,0 +1,433 @@ +/* + N8 - 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 N8·DEBUG + +#ifndef FACE +#define N8·IMPLEMENTATION +#define FACE +#endif + +//-------------------------------------------------------------------------------- +// Interface + +#ifndef N8·FACE +#define N8·FACE + + #include + #include + #include + #include + + //---------------------------------------- + // Instance Data (Declaration Only) + + typedef uint8_t Extent; + typedef uint8_t Digit; + + typedef struct N8·T N8·T; + + extern N8·T *N8·zero; + extern N8·T *N8·one; + extern N8·T *N8·all_one_bit; + extern N8·T *N8·lsb; + extern N8·T *N8·msb; + + //---------------------------------------- + // Return/Error Status and handlers + + typedef enum{ + N8·Status·ok = 0 + ,N8·Status·overflow = 1 + ,N8·Status·accumulator1_overflow = 2 + ,N8·Status·carry = 3 + ,N8·Status·borrow = 4 + ,N8·Status·undefined_divide_by_zero = 5 + ,N8·Status·undefined_modulus_zero = 6 + ,N8·Status·gt_max_shift_count = 7 + ,N8·Status·spill_eq_operand = 8 + ,N8·Status·one_word_product = 9 + ,N8·Status·two_word_product = 10 + } N8·Status; + + typedef enum{ + N8·Order_lt = -1 + ,N8·Order_eq = 0 + ,N8·Order_gt = 1 + } N8·Order; + + typedef N8·T *( *N8·Allocate_MemoryFault )(Extent); + + //---------------------------------------- + // Interface + + typedef struct{ + + N8·T *(*allocate_array_zero)(Extent, N8·Allocate_MemoryFault); + N8·T *(*allocate_array)(Extent, N8·Allocate_MemoryFault); + void (*deallocate)(N8·T*); + + void (*copy)(N8·T*, N8·T*); + void (*bit_and)(N8·T*, N8·T*, N8·T*); + void (*bit_or)(N8·T*, N8·T*, N8·T*); + void (*bit_complement)(N8·T*, N8·T*); + void (*bit_twos_complement)(N8·T*, N8·T*); + N8·Order (*compare)(N8·T*, N8·T*); + bool (*lt)(N8·T*, N8·T*); + bool (*gt)(N8·T*, N8·T*); + bool (*eq)(N8·T*, N8·T*); + bool (*eq_zero)(N8·T*); + N8·Status (*accumulate)(N8·T *accumulator1 ,N8·T *accumulator0 ,...); + N8·Status (*add)(N8·T*, N8·T*, N8·T*); + bool (*increment)(N8·T *a); + N8·Status (*subtract)(N8·T*, N8·T*, N8·T*); + N8·Status (*multiply)(N8·T*, N8·T*, N8·T*, N8·T*); + N8·Status (*divide)(N8·T*, N8·T*, N8·T*, N8·T*); + N8·Status (*modulus)(N8·T*, N8·T*, N8·T*); + N8·Status (*shift_left)(Extent, N8·T*, N8·T*, N8·T*); + N8·Status (*shift_right)(Extent, N8·T*, N8·T*, N8·T*); + N8·Status (*arithmetic_shift_right)(Extent, N8·T*, N8·T*); + + N8·T* (*access)(N8·T*, Extent); + void (*from_uint32)(N8·T *destination ,uint32_t value); + } N8·Λ; + + Local const N8·Λ N8·λ; // initialized in the LOCAL section + +#endif + +//-------------------------------------------------------------------------------- +// Implementation + +#ifdef N8·IMPLEMENTATION + + // this part goes into the library + #ifndef LOCAL + + #include + #include + + struct N8·T{ + Digit d0; + }; + + N8·T N8·constant[4] = { + {.d0 = 0}, + {.d0 = 1}, + {.d0 = ~(uint8_t)0}, + {.d0 = 1 << 7} + }; + + N8·T *N8·zero = &N8·constant[0]; + N8·T *N8·one = &N8·constant[1]; + N8·T *N8·all_one_bit = &N8·constant[2]; + N8·T *N8·msb = &N8·constant[3]; + N8·T *N8·lsb = &N8·constant[1]; + + // the allocate an array of N8 + N8·T *N8·allocate_array(Extent extent ,N8·Allocate_MemoryFault memory_fault){ + N8·T *instance = malloc((extent + 1) * sizeof(N8·T)); + if(!instance){ + return memory_fault ? memory_fault(extent) : NULL; + } + return instance; + } + + N8·T *N8·allocate_array_zero(Extent extent ,N8·Allocate_MemoryFault memory_fault){ + N8·T *instance = calloc(extent + 1, sizeof(N8·T)); + if(!instance){ + return memory_fault ? memory_fault(extent) : NULL; + } + return instance; + } + + void N8·deallocate(N8·T *unencumbered){ + free(unencumbered); + } + + + #endif + + // This part is included after the library user's code + #ifdef LOCAL + + // instance + + struct N8·T{ + Digit d0; + }; + + // temporary variables + Local N8·T N8·t[4]; + + // allocation + + extern N8·T *N8·allocate_array(Extent, N8·Allocate_MemoryFault); + extern N8·T *N8·allocate_array_zero(Extent, N8·Allocate_MemoryFault); + extern void N8·deallocate(N8·T *); + + // so the user can access numbers in an array allocation + Local N8·T* N8·access(N8·T *array ,Extent index){ + return &array[index]; + } + + Local void N8·from_uint32(N8·T *destination ,uint32_t value){ + if(destination == NULL) return; + destination->d0 = (uint8_t)(value & 0xFF); + } + + // copy, convenience copy + + Local void N8·copy(N8·T *destination ,N8·T *source){ + if(source == destination) return; + *destination = *source; + } + + Local void N8·set_to_zero(N8·T *instance){ + instance->d0 = 0; + } + + Local void N8·set_to_one(N8·T *instance){ + instance->d0 = 1; + } + + // bit operations + + Local void N8·bit_and(N8·T *result, N8·T *a, N8·T *b){ + result->d0 = a->d0 & b->d0; + } + + Local void N8·bit_or(N8·T *result, N8·T *a, N8·T *b){ + result->d0 = a->d0 | b->d0; + } + + Local void N8·bit_complement(N8·T *result, N8·T *a){ + result->d0 = ~a->d0; + } + + Local void N8·bit_twos_complement(N8·T *result ,N8·T *a){ + result->d0 = (uint8_t)(~a->d0 + 1); + } + + // test functions + + Local N8·Order N8·compare(N8·T *a, N8·T *b){ + if(a->d0 < b->d0) return N8·Order_lt; + if(a->d0 > b->d0) return N8·Order_gt; + return N8·Order_eq; + } + + Local bool N8·lt(N8·T *a ,N8·T *b){ + return a->d0 < b->d0; + } + + Local bool N8·gt(N8·T *a ,N8·T *b){ + return a->d0 > b->d0; + } + + Local bool N8·eq(N8·T *a ,N8·T *b){ + return a->d0 == b->d0; + } + + Local bool N8·eq_zero(N8·T *a){ + return a->d0 == 0; + } + + // arithmetic operations + + Local N8·Status N8·accumulate(N8·T *accumulator1 ,N8·T *accumulator0 ,...){ + + va_list args; + va_start(args ,accumulator0); + uint32_t sum = accumulator0->d0; + uint32_t carry = 0; + N8·T *current; + + while( (current = va_arg(args ,N8·T*)) ){ + sum += current->d0; + if(sum < current->d0){ + (carry)++; + if(carry == 0){ + va_end(args); + return N8·Status·accumulator1_overflow; + } + } + } + va_end(args); + + accumulator1->d0 = (uint8_t)carry; + return N8·Status·ok; + } + + Local N8·Status N8·add(N8·T *sum ,N8·T *a ,N8·T *b){ + uint32_t result = (uint32_t)a->d0 + (uint32_t)b->d0; + sum->d0 = (uint8_t)(result & 0xFF); + return (result >> 8) ? N8·Status·carry : N8·Status·ok; + } + + Local bool N8·increment(N8·T *a){ + a->d0++; + return (a->d0 == 0); + } + + Local N8·Status N8·subtract(N8·T *difference ,N8·T *a ,N8·T *b){ + uint32_t diff = (uint32_t)a->d0 - (uint32_t)b->d0; + difference->d0 = (uint8_t)(diff & 0xFF); + return (diff > a->d0) ? N8·Status·borrow : N8·Status·ok; + } + + Local N8·Status N8·multiply(N8·T *product1 ,N8·T *product0 ,N8·T *a ,N8·T *b){ + uint32_t product = (uint32_t)a->d0 * (uint32_t)b->d0; + product0->d0 = (uint8_t)(product & 0xFF); + product1->d0 = (uint8_t)((product >> 8) & 0xFF); + + if(product1->d0 == 0) return N8·Status·one_word_product; + return N8·Status·two_word_product; + } + + Local N8·Status N8·divide(N8·T *remainder ,N8·T *quotient ,N8·T *a ,N8·T *b){ + if(b->d0 == 0) return N8·Status·undefined_divide_by_zero; + + uint32_t dividend = a->d0; + uint32_t divisor = b->d0; + quotient->d0 = (uint8_t)(dividend / divisor); + remainder->d0 = (uint8_t)(dividend - (quotient->d0 * divisor)); + + return N8·Status·ok; + } + + Local N8·Status N8·modulus(N8·T *remainder ,N8·T *a ,N8·T *b){ + if(b->d0 == 0) return N8·Status·undefined_modulus_zero; + uint32_t dividend = a->d0; + uint32_t divisor = b->d0; + uint32_t q = dividend / divisor; + remainder->d0 = (uint8_t)(dividend - (q * divisor)); + return N8·Status·ok; + } + + // bit motion + + typedef uint8_t (*ShiftOp)(uint8_t, uint8_t); + + Local uint8_t shift_left_op(uint8_t value, uint8_t amount){ + return (uint8_t)(value << amount); + } + + Local uint8_t shift_right_op(uint8_t value, uint8_t amount){ + return (uint8_t)(value >> amount); + } + + Local N8·Status N8·shift + ( + uint8_t shift_count + ,N8·T *spill + ,N8·T *operand + ,N8·T *fill + ,ShiftOp shift_op + ,ShiftOp complement_shift_op + ){ + + if(operand == NULL && spill == NULL) return N8·Status·ok; + + if(operand == NULL){ + operand = &N8·t[0]; + N8·copy(operand, N8·zero); + } + + if(shift_count > 7) return N8·Status·gt_max_shift_count; + + N8·T *given_operand = &N8·t[1]; + N8·copy(given_operand, operand); + + operand->d0 = shift_op(given_operand->d0, shift_count); + if(fill != NULL){ + fill->d0 = complement_shift_op(fill->d0, (8 - shift_count)); + N8·bit_or(operand, operand, fill); + } + if(spill != NULL){ + spill->d0 = shift_op(spill->d0, shift_count); + spill->d0 += complement_shift_op(given_operand->d0, (8 - shift_count)); + } + + return N8·Status·ok; + } + + Local N8·Status + N8·shift_left(uint8_t shift_count, N8·T *spill, N8·T *operand, N8·T *fill){ + return N8·shift(shift_count, spill, operand, fill, shift_left_op, shift_right_op); + } + + Local N8·Status + N8·shift_right(uint8_t shift_count, N8·T *spill, N8·T *operand, N8·T *fill){ + return N8·shift(shift_count, spill, operand, fill, shift_right_op, shift_left_op); + } + + Local N8·Status + N8·arithmetic_shift_right(uint8_t shift_count, N8·T *operand, N8·T *spill){ + + if(shift_count > 7) return N8·Status·gt_max_shift_count; + + if(operand == NULL){ + operand = &N8·t[0]; + N8·copy(operand, N8·zero); + } + + N8·T *fill = (operand->d0 & 0x80) ? N8·all_one_bit : N8·zero; + return N8·shift_right(shift_count, spill, operand, fill); + } + + Local const N8·Λ N8·λ = { + + .allocate_array = N8·allocate_array + ,.allocate_array_zero = N8·allocate_array_zero + ,.deallocate = N8·deallocate + + ,.copy = N8·copy + ,.bit_and = N8·bit_and + ,.bit_or = N8·bit_or + ,.bit_complement = N8·bit_complement + ,.bit_twos_complement = N8·bit_twos_complement + ,.compare = N8·compare + ,.lt = N8·lt + ,.gt = N8·gt + ,.eq = N8·eq + ,.eq_zero = N8·eq_zero + ,.accumulate = N8·accumulate + ,.add = N8·add + ,.increment = N8·increment + ,.subtract = N8·subtract + ,.multiply = N8·multiply + ,.divide = N8·divide + ,.modulus = N8·modulus + ,.shift_left = N8·shift_left + ,.shift_right = N8·shift_right + ,.arithmetic_shift_right = N8·arithmetic_shift_right + + ,.access = N8·access + ,.from_uint32 = N8·from_uint32 + }; + + #endif + +#endif diff --git "a/developer/cc\360\237\226\211/test_N32.cli.c" "b/developer/cc\360\237\226\211/test_N32.cli.c" index 6e79571..563711f 100644 --- "a/developer/cc\360\237\226\211/test_N32.cli.c" +++ "b/developer/cc\360\237\226\211/test_N32.cli.c" @@ -119,6 +119,245 @@ bool test_arithmetic(){ return true; } +bool test_bitwise_operations(){ + // Allocate memory + N32·T *array = N32·λ.allocate_array(3, NULL); + if(!array) return false; + + N32·T *a = N32·λ.access(array, 0); + N32·T *b = N32·λ.access(array, 1); + N32·T *result = N32·λ.access(array, 2); + + // a = 0x0F0F0F0F, b = 0xF0F0F0F0 + N32·λ.from_uint32(a, 0x0F0F0F0F); + N32·λ.from_uint32(b, 0xF0F0F0F0); + + // bit_and => expect 0x00000000 + N32·λ.bit_and(result, a, b); + N32·λ.from_uint32(a, 0x00000000); + if(N32·λ.compare(result, a) != N32·Order_eq){ + N32·λ.deallocate(array); + return false; + } + + // Reset a to 0x0F0F0F0F for next tests + N32·λ.from_uint32(a, 0x0F0F0F0F); + + // bit_or => expect 0xFFFFFFFF + N32·λ.bit_or(result, a, b); + N32·λ.from_uint32(b, 0xFFFFFFFF); + if(N32·λ.compare(result, b) != N32·Order_eq){ + N32·λ.deallocate(array); + return false; + } + + // bit_complement(a=0x0F0F0F0F) => expect 0xF0F0F0F0 + N32·λ.from_uint32(a, 0x0F0F0F0F); + N32·λ.bit_complement(result, a); + N32·λ.from_uint32(b, 0xF0F0F0F0); + if(N32·λ.compare(result, b) != N32·Order_eq){ + N32·λ.deallocate(array); + return false; + } + + // bit_twos_complement(a=0x0F0F0F0F) => expect 0xF0F0F0F1 + N32·λ.from_uint32(a, 0x0F0F0F0F); + N32·λ.bit_twos_complement(result, a); + N32·λ.from_uint32(b, 0xF0F0F0F1); + if(N32·λ.compare(result, b) != N32·Order_eq){ + N32·λ.deallocate(array); + return false; + } + + N32·λ.deallocate(array); + return true; +} + +bool test_comparisons(){ + // Allocate memory + N32·T *array = N32·λ.allocate_array(3, NULL); + if(!array) return false; + + N32·T *a = N32·λ.access(array, 0); + N32·T *b = N32·λ.access(array, 1); + N32·T *c = N32·λ.access(array, 2); + + // First set: a=0, b=42, c=42 + N32·λ.from_uint32(a, 0); + N32·λ.from_uint32(b, 42); + N32·λ.from_uint32(c, 42); + + // eq_zero(a) => true + if(!N32·λ.eq_zero(a)){ + N32·λ.deallocate(array); + return false; + } + // eq_zero(b) => false + if(N32·λ.eq_zero(b)){ + N32·λ.deallocate(array); + return false; + } + // eq(b, c) => true + if(!N32·λ.eq(b, c)){ + N32·λ.deallocate(array); + return false; + } + // eq(a, b) => false + if(N32·λ.eq(a, b)){ + N32·λ.deallocate(array); + return false; + } + // compare(a, b) => N32·Order_lt + if(N32·λ.compare(a, b) != N32·Order_lt){ + N32·λ.deallocate(array); + return false; + } + // compare(b, a) => N32·Order_gt + if(N32·λ.compare(b, a) != N32·Order_gt){ + N32·λ.deallocate(array); + return false; + } + // compare(b, c) => N32·Order_eq + if(N32·λ.compare(b, c) != N32·Order_eq){ + N32·λ.deallocate(array); + return false; + } + // lt(a, b) => true, gt(b, a) => true + if(!N32·λ.lt(a, b) || !N32·λ.gt(b, a)){ + N32·λ.deallocate(array); + return false; + } + + // Second set: a=100, b=50 + N32·λ.from_uint32(a, 100); + N32·λ.from_uint32(b, 50); + if(N32·λ.compare(a, b) != N32·Order_gt){ + N32·λ.deallocate(array); + return false; + } + // eq_zero(a) => false + if(N32·λ.eq_zero(a)){ + N32·λ.deallocate(array); + return false; + } + // eq_zero(b) => false + if(N32·λ.eq_zero(b)){ + N32·λ.deallocate(array); + return false; + } + + N32·λ.deallocate(array); + return true; +} + +bool test_shifts(){ + // Allocate memory for operand, fill, spill + N32·T *array = N32·λ.allocate_array(3, NULL); + if(!array) return false; + + N32·T *operand = N32·λ.access(array, 0); + N32·T *fill = N32·λ.access(array, 1); + N32·T *spill = N32·λ.access(array, 2); + + // Subtest A: shift_left(4) with operand=1 => expect operand=16, fill=0, spill=0 + N32·λ.from_uint32(operand, 1); + N32·λ.from_uint32(fill, 0); + N32·λ.from_uint32(spill, 0); + if(N32·λ.shift_left(4, spill, operand, fill) != N32·Status·ok){ + N32·λ.deallocate(array); + return false; + } + N32·T *temp = N32·λ.allocate_array(1, NULL); + if(!temp){ + N32·λ.deallocate(array); + return false; + } + N32·λ.from_uint32(temp, 16); + if(N32·λ.compare(operand, temp) != N32·Order_eq){ + N32·λ.deallocate(temp); + N32·λ.deallocate(array); + return false; + } + if(N32·λ.compare(fill, N32·zero) != N32·Order_eq){ + N32·λ.deallocate(temp); + N32·λ.deallocate(array); + return false; + } + if(N32·λ.compare(spill, N32·zero) != N32·Order_eq){ + N32·λ.deallocate(temp); + N32·λ.deallocate(array); + return false; + } + + // Subtest B: shift_left(1) with operand=0x80000000 => expect operand=0, spill=1 + N32·λ.from_uint32(operand, 0x80000000); + N32·λ.from_uint32(fill, 0); + N32·λ.from_uint32(spill, 0); + if(N32·λ.shift_left(1, spill, operand, fill) != N32·Status·ok){ + N32·λ.deallocate(temp); + N32·λ.deallocate(array); + return false; + } + if(!N32·λ.eq_zero(operand)){ + N32·λ.deallocate(temp); + N32·λ.deallocate(array); + return false; + } + N32·λ.from_uint32(temp, 1); + if(N32·λ.compare(spill, temp) != N32·Order_eq){ + N32·λ.deallocate(temp); + N32·λ.deallocate(array); + return false; + } + + // Subtest C: shift_right(1) with operand=0x80000000 => expect operand=0x40000000, spill=0 + N32·λ.from_uint32(operand, 0x80000000); + N32·λ.from_uint32(fill, 0); + N32·λ.from_uint32(spill, 0); + if(N32·λ.shift_right(1, spill, operand, fill) != N32·Status·ok){ + N32·λ.deallocate(temp); + N32·λ.deallocate(array); + return false; + } + N32·λ.from_uint32(temp, 0x40000000); + if(N32·λ.compare(operand, temp) != N32·Order_eq){ + N32·λ.deallocate(temp); + N32·λ.deallocate(array); + return false; + } + if(!N32·λ.eq_zero(spill)){ + N32·λ.deallocate(temp); + N32·λ.deallocate(array); + return false; + } + + // Subtest D: arithmetic_shift_right(1) with operand=0x80000000 => expect operand=0xC0000000, spill=0 + N32·λ.from_uint32(operand, 0x80000000); + N32·λ.from_uint32(spill, 0); + if(N32·λ.arithmetic_shift_right(1, operand, spill) != N32·Status·ok){ + N32·λ.deallocate(temp); + N32·λ.deallocate(array); + return false; + } + N32·λ.from_uint32(temp, 0xC0000000); + if(N32·λ.compare(operand, temp) != N32·Order_eq){ + N32·λ.deallocate(temp); + N32·λ.deallocate(array); + return false; + } + if(!N32·λ.eq_zero(spill)){ + N32·λ.deallocate(temp); + N32·λ.deallocate(array); + return false; + } + + N32·λ.deallocate(temp); + N32·λ.deallocate(array); + return true; +} + + + // Include the local section of N32.lib.c for testing #define LOCAL #include "N32.lib.c" diff --git "a/developer/document\360\237\226\211/Approach_to_headers.txt" "b/developer/document\360\237\226\211/Approach_to_headers.txt" deleted file mode 100644 index c929c4d..0000000 --- "a/developer/document\360\237\226\211/Approach_to_headers.txt" +++ /dev/null @@ -1,46 +0,0 @@ -✅ 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/SectionedFileFormat.org" "b/developer/document\360\237\226\211/SectionedFileFormat.org" index 46710ee..db89851 100644 --- "a/developer/document\360\237\226\211/SectionedFileFormat.org" +++ "b/developer/document\360\237\226\211/SectionedFileFormat.org" @@ -33,3 +33,53 @@ #+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. 😆