From: Thomas Walker Lynch Date: Mon, 17 Feb 2025 14:26:06 +0000 (+0000) Subject: full flow multiple parameter selections though unlikely test results X-Git-Url: https://git.reasoningtechnology.com/style/static/git-logo.png?a=commitdiff_plain;h=d4427fbfcfaee40118675886f05246b3ba31ee37;p=N full flow multiple parameter selections though unlikely test results --- 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/N16PN.lib.c" "b/developer/cc\360\237\226\211/N16PN.lib.c" deleted file mode 100644 index d7a2e05..0000000 --- "a/developer/cc\360\237\226\211/N16PN.lib.c" +++ /dev/null @@ -1,433 +0,0 @@ -/* - 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 N16PN·DEBUG - -#ifndef FACE -#define N16PN·IMPLEMENTATION -#define FACE -#endif - -//-------------------------------------------------------------------------------- -// Interface - -#ifndef N16PN·FACE -#define N16PN·FACE - - #include - #include - #include - #include - - //---------------------------------------- - // Instance Data (Declaration Only) - - typedef uint16_t Extent; - typedef uint16_t Digit; - - typedef struct N16PN·T N16PN·T; - - extern N16PN·T *N16PN·zero; - extern N16PN·T *N16PN·one; - extern N16PN·T *N16PN·all_one_bit; - extern N16PN·T *N16PN·lsb; - extern N16PN·T *N16PN·msb; - - //---------------------------------------- - // Return/Error Status and handlers - - typedef enum{ - N16PN·Status·ok = 0 - ,N16PN·Status·overflow = 1 - ,N16PN·Status·accumulator1_overflow = 2 - ,N16PN·Status·carry = 3 - ,N16PN·Status·borrow = 4 - ,N16PN·Status·undefined_divide_by_zero = 5 - ,N16PN·Status·undefined_modulus_zero = 6 - ,N16PN·Status·gt_max_shift_count = 7 - ,N16PN·Status·spill_eq_operand = 8 // not currently signaled, result will be spill value - ,N16PN·Status·one_word_product = 9 - ,N16PN·Status·two_word_product = 10 - } N16PN·Status; - - typedef enum{ - N16PN·Order_lt = -1 - ,N16PN·Order_eq = 0 - ,N16PN·Order_gt = 1 - } N16PN·Order; - - typedef N16PN·T *( *N16PN·Allocate_MemoryFault )(Extent); - - //---------------------------------------- - // Interface - - typedef struct{ - - N16PN·T *(*allocate_array_zero)(Extent, N16PN·Allocate_MemoryFault); - N16PN·T *(*allocate_array)(Extent, N16PN·Allocate_MemoryFault); - void (*deallocate)(N16PN·T*); - - void (*copy)(N16PN·T*, N16PN·T*); - void (*bit_and)(N16PN·T*, N16PN·T*, N16PN·T*); - void (*bit_or)(N16PN·T*, N16PN·T*, N16PN·T*); - void (*bit_complement)(N16PN·T*, N16PN·T*); - void (*bit_twos_complement)(N16PN·T*, N16PN·T*); - N16PN·Order (*compare)(N16PN·T*, N16PN·T*); - bool (*lt)(N16PN·T*, N16PN·T*); - bool (*gt)(N16PN·T*, N16PN·T*); - bool (*eq)(N16PN·T*, N16PN·T*); - bool (*eq_zero)(N16PN·T*); - N16PN·Status (*accumulate)(N16PN·T *accumulator1 ,N16PN·T *accumulator0 ,...); - N16PN·Status (*add)(N16PN·T*, N16PN·T*, N16PN·T*); - bool (*increment)(N16PN·T *a); - N16PN·Status (*subtract)(N16PN·T*, N16PN·T*, N16PN·T*); - N16PN·Status (*multiply)(N16PN·T*, N16PN·T*, N16PN·T*, N16PN·T*); - N16PN·Status (*divide)(N16PN·T*, N16PN·T*, N16PN·T*, N16PN·T*); - N16PN·Status (*modulus)(N16PN·T*, N16PN·T*, N16PN·T*); - N16PN·Status (*shift_left)(Extent, N16PN·T*, N16PN·T*, N16PN·T*); - N16PN·Status (*shift_right)(Extent, N16PN·T*, N16PN·T*, N16PN·T*); - N16PN·Status (*arithmetic_shift_right)(Extent, N16PN·T*, N16PN·T*); - - N16PN·T* (*access)(N16PN·T*, Extent); - void (*from_uint32)(N16PN·T *destination ,uint32_t value); - } N16PN·Λ; - - Local const N16PN·Λ N16PN·λ; // initialized in the LOCAL section - -#endif - -//-------------------------------------------------------------------------------- -// Implementation - -#ifdef N16PN·IMPLEMENTATION - - // this part goes into the library - #ifndef LOCAL - - #include - #include - - struct N16PN·T{ - Digit d0; - }; - - N16PN·T N16PN·constant[4] = { - {.d0 = 0}, - {.d0 = 1}, - {.d0 = ~(uint16_t)0}, - {.d0 = 1 << 15} - }; - - N16PN·T *N16PN·zero = &N16PN·constant[0]; - N16PN·T *N16PN·one = &N16PN·constant[1]; - N16PN·T *N16PN·all_one_bit = &N16PN·constant[2]; - N16PN·T *N16PN·msb = &N16PN·constant[3]; - N16PN·T *N16PN·lsb = &N16PN·constant[1]; - - // the allocate an array of N16 - N16PN·T *N16PN·allocate_array(Extent extent ,N16PN·Allocate_MemoryFault memory_fault){ - N16PN·T *instance = malloc((extent + 1) * sizeof(N16PN·T)); - if(!instance){ - return memory_fault ? memory_fault(extent) : NULL; - } - return instance; - } - - N16PN·T *N16PN·allocate_array_zero(Extent extent ,N16PN·Allocate_MemoryFault memory_fault){ - N16PN·T *instance = calloc(extent + 1, sizeof(N16PN·T)); - if(!instance){ - return memory_fault ? memory_fault(extent) : NULL; - } - return instance; - } - - void N16PN·deallocate(N16PN·T *unencumbered){ - free(unencumbered); - } - - - #endif - - // This part is included after the library user's code - #ifdef LOCAL - - // instance - - struct N16PN·T{ - Digit d0; - }; - - // temporary variables - Local N16PN·T N16PN·t[4]; - - // allocation - - extern N16PN·T *N16PN·allocate_array(Extent, N16PN·Allocate_MemoryFault); - extern N16PN·T *N16PN·allocate_array_zero(Extent, N16PN·Allocate_MemoryFault); - extern void N16PN·deallocate(N16PN·T *); - - // so the user can access numbers in an array allocation - Local N16PN·T* N16PN·access(N16PN·T *array ,Extent index){ - return &array[index]; - } - - Local void N16PN·from_uint32(N16PN·T *destination ,uint32_t value){ - if(destination == NULL) return; - destination->d0 = (uint16_t)(value & 0xFFFF); - } - - // copy, convenience copy - - Local void N16PN·copy(N16PN·T *destination ,N16PN·T *source){ - if(source == destination) return; - *destination = *source; - } - - Local void N16PN·set_to_zero(N16PN·T *instance){ - instance->d0 = 0; - } - - Local void N16PN·set_to_one(N16PN·T *instance){ - instance->d0 = 1; - } - - // bit operations - - Local void N16PN·bit_and(N16PN·T *result, N16PN·T *a, N16PN·T *b){ - result->d0 = a->d0 & b->d0; - } - - Local void N16PN·bit_or(N16PN·T *result, N16PN·T *a, N16PN·T *b){ - result->d0 = a->d0 | b->d0; - } - - Local void N16PN·bit_complement(N16PN·T *result, N16PN·T *a){ - result->d0 = ~a->d0; - } - - Local void N16PN·bit_twos_complement(N16PN·T *result ,N16PN·T *a){ - result->d0 = (uint16_t)(~a->d0 + 1); - } - - // test functions - - Local N16PN·Order N16PN·compare(N16PN·T *a, N16PN·T *b){ - if(a->d0 < b->d0) return N16PN·Order_lt; - if(a->d0 > b->d0) return N16PN·Order_gt; - return N16PN·Order_eq; - } - - Local bool N16PN·lt(N16PN·T *a ,N16PN·T *b){ - return a->d0 < b->d0; - } - - Local bool N16PN·gt(N16PN·T *a ,N16PN·T *b){ - return a->d0 > b->d0; - } - - Local bool N16PN·eq(N16PN·T *a ,N16PN·T *b){ - return a->d0 == b->d0; - } - - Local bool N16PN·eq_zero(N16PN·T *a){ - return a->d0 == 0; - } - - // arithmetic operations - - Local N16PN·Status N16PN·accumulate(N16PN·T *accumulator1 ,N16PN·T *accumulator0 ,...){ - - va_list args; - va_start(args ,accumulator0); - uint32_t sum = accumulator0->d0; - uint32_t carry = 0; - N16PN·T *current; - - while( (current = va_arg(args ,N16PN·T*)) ){ - sum += current->d0; - if(sum < current->d0){ - (carry)++; - if(carry == 0){ - va_end(args); - return N16PN·Status·accumulator1_overflow; - } - } - } - va_end(args); - - accumulator1->d0 = (uint16_t)carry; - return N16PN·Status·ok; - } - - Local N16PN·Status N16PN·add(N16PN·T *sum ,N16PN·T *a ,N16PN·T *b){ - uint32_t result = (uint32_t)a->d0 + (uint32_t)b->d0; - sum->d0 = (uint16_t)(result & 0xFFFF); - return (result >> 16) ? N16PN·Status·carry : N16PN·Status·ok; - } - - Local bool N16PN·increment(N16PN·T *a){ - a->d0++; - return (a->d0 == 0); - } - - Local N16PN·Status N16PN·subtract(N16PN·T *difference ,N16PN·T *a ,N16PN·T *b){ - uint32_t diff = (uint32_t)a->d0 - (uint32_t)b->d0; - difference->d0 = (uint16_t)(diff & 0xFFFF); - return (diff > a->d0) ? N16PN·Status·borrow : N16PN·Status·ok; - } - - Local N16PN·Status N16PN·multiply(N16PN·T *product1 ,N16PN·T *product0 ,N16PN·T *a ,N16PN·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 N16PN·Status·one_word_product; - return N16PN·Status·two_word_product; - } - - Local N16PN·Status N16PN·divide(N16PN·T *remainder ,N16PN·T *quotient ,N16PN·T *a ,N16PN·T *b){ - if(b->d0 == 0) return N16PN·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 N16PN·Status·ok; - } - - Local N16PN·Status N16PN·modulus(N16PN·T *remainder ,N16PN·T *a ,N16PN·T *b){ - if(b->d0 == 0) return N16PN·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 N16PN·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 N16PN·Status N16PN·shift - ( - uint16_t shift_count - ,N16PN·T *spill - ,N16PN·T *operand - ,N16PN·T *fill - ,ShiftOp shift_op - ,ShiftOp complement_shift_op - ){ - - if(operand == NULL && spill == NULL) return N16PN·Status·ok; - - if(operand == NULL){ - operand = &N16PN·t[0]; - N16PN·copy(operand, N16PN·zero); - } - - if(shift_count > 15) return N16PN·Status·gt_max_shift_count; - - N16PN·T *given_operand = &N16PN·t[1]; - N16PN·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)); - N16PN·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 N16PN·Status·ok; - } - - Local N16PN·Status - N16PN·shift_left(uint16_t shift_count, N16PN·T *spill, N16PN·T *operand, N16PN·T *fill){ - return N16PN·shift(shift_count, spill, operand, fill, shift_left_op, shift_right_op); - } - - Local N16PN·Status - N16PN·shift_right(uint16_t shift_count, N16PN·T *spill, N16PN·T *operand, N16PN·T *fill){ - return N16PN·shift(shift_count, spill, operand, fill, shift_right_op, shift_left_op); - } - - Local N16PN·Status - N16PN·arithmetic_shift_right(uint16_t shift_count, N16PN·T *operand, N16PN·T *spill){ - - if(shift_count > 15) return N16PN·Status·gt_max_shift_count; - - if(operand == NULL){ - operand = &N16PN·t[0]; - N16PN·copy(operand, N16PN·zero); - } - - N16PN·T *fill = (operand->d0 & 0x8000) ? N16PN·all_one_bit : N16PN·zero; - return N16PN·shift_right(shift_count, spill, operand, fill); - } - - Local const N16PN·Λ N16PN·λ = { - - .allocate_array = N16PN·allocate_array - ,.allocate_array_zero = N16PN·allocate_array_zero - ,.deallocate = N16PN·deallocate - - ,.copy = N16PN·copy - ,.bit_and = N16PN·bit_and - ,.bit_or = N16PN·bit_or - ,.bit_complement = N16PN·bit_complement - ,.bit_twos_complement = N16PN·bit_twos_complement - ,.compare = N16PN·compare - ,.lt = N16PN·lt - ,.gt = N16PN·gt - ,.eq = N16PN·eq - ,.eq_zero = N16PN·eq_zero - ,.accumulate = N16PN·accumulate - ,.add = N16PN·add - ,.increment = N16PN·increment - ,.subtract = N16PN·subtract - ,.multiply = N16PN·multiply - ,.divide = N16PN·divide - ,.modulus = N16PN·modulus - ,.shift_left = N16PN·shift_left - ,.shift_right = N16PN·shift_right - ,.arithmetic_shift_right = N16PN·arithmetic_shift_right - - ,.access = N16PN·access - ,.from_uint32 = N16PN·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" new file mode 100644 index 0000000..fba3c21 --- /dev/null +++ "b/developer/cc\360\237\226\211/N32.lib.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/N32PN.lib.c" "b/developer/cc\360\237\226\211/N32PN.lib.c" deleted file mode 100644 index 952f570..0000000 --- "a/developer/cc\360\237\226\211/N32PN.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 N32PN·DEBUG - -#ifndef FACE -#define N32PN·IMPLEMENTATION -#define FACE -#endif - -//-------------------------------------------------------------------------------- -// Interface - -#ifndef N32PN·FACE -#define N32PN·FACE - - #include - #include - #include - #include - - //---------------------------------------- - // Instance Data (Declaration Only) - - typedef uint32_t Extent; - typedef uint32_t Digit; - - typedef struct N32PN·T N32PN·T; - - extern N32PN·T *N32PN·zero; - extern N32PN·T *N32PN·one; - extern N32PN·T *N32PN·all_one_bit; - extern N32PN·T *N32PN·lsb; - extern N32PN·T *N32PN·msb; - - //---------------------------------------- - // Return/Error Status and handlers - - typedef enum{ - N32PN·Status·ok = 0 - ,N32PN·Status·overflow = 1 - ,N32PN·Status·accumulator1_overflow = 2 - ,N32PN·Status·carry = 3 - ,N32PN·Status·borrow = 4 - ,N32PN·Status·undefined_divide_by_zero = 5 - ,N32PN·Status·undefined_modulus_zero = 6 - ,N32PN·Status·gt_max_shift_count = 7 - ,N32PN·Status·spill_eq_operand = 8 // not currently signaled, result will be spill value - ,N32PN·Status·one_word_product = 9 - ,N32PN·Status·two_word_product = 10 - } N32PN·Status; - - typedef enum{ - N32PN·Order_lt = -1 - ,N32PN·Order_eq = 0 - ,N32PN·Order_gt = 1 - } N32PN·Order; - - typedef N32PN·T *( *N32PN·Allocate_MemoryFault )(Extent); - - //---------------------------------------- - // Interface - - typedef struct{ - - N32PN·T *(*allocate_array_zero)(Extent, N32PN·Allocate_MemoryFault); - N32PN·T *(*allocate_array)(Extent, N32PN·Allocate_MemoryFault); - void (*deallocate)(N32PN·T*); - - void (*copy)(N32PN·T*, N32PN·T*); - void (*bit_and)(N32PN·T*, N32PN·T*, N32PN·T*); - void (*bit_or)(N32PN·T*, N32PN·T*, N32PN·T*); - void (*bit_complement)(N32PN·T*, N32PN·T*); - void (*bit_twos_complement)(N32PN·T*, N32PN·T*); - N32PN·Order (*compare)(N32PN·T*, N32PN·T*); - bool (*lt)(N32PN·T*, N32PN·T*); - bool (*gt)(N32PN·T*, N32PN·T*); - bool (*eq)(N32PN·T*, N32PN·T*); - bool (*eq_zero)(N32PN·T*); - N32PN·Status (*accumulate)(N32PN·T *accumulator1 ,N32PN·T *accumulator0 ,...); - N32PN·Status (*add)(N32PN·T*, N32PN·T*, N32PN·T*); - bool (*increment)(N32PN·T *a); - N32PN·Status (*subtract)(N32PN·T*, N32PN·T*, N32PN·T*); - N32PN·Status (*multiply)(N32PN·T*, N32PN·T*, N32PN·T*, N32PN·T*); - N32PN·Status (*divide)(N32PN·T*, N32PN·T*, N32PN·T*, N32PN·T*); - N32PN·Status (*modulus)(N32PN·T*, N32PN·T*, N32PN·T*); - N32PN·Status (*shift_left)(Extent, N32PN·T*, N32PN·T*, N32PN·T*); - N32PN·Status (*shift_right)(Extent, N32PN·T*, N32PN·T*, N32PN·T*); - N32PN·Status (*arithmetic_shift_right)(Extent, N32PN·T*, N32PN·T*); - - N32PN·T* (*access)(N32PN·T*, Extent); - void (*from_uint32)(N32PN·T *destination ,uint32_t value); - } N32PN·Λ; - - Local const N32PN·Λ N32PN·λ; // initialized in the LOCAL section - -#endif - -//-------------------------------------------------------------------------------- -// Implementation - -#ifdef N32PN·IMPLEMENTATION - - // this part goes into the library - #ifndef LOCAL - - #include - #include - - struct N32PN·T{ - Digit d0; - }; - - N32PN·T N32PN·constant[4] = { - {.d0 = 0}, - {.d0 = 1}, - {.d0 = ~(uint32_t)0}, - {.d0 = 1 << 31} - }; - - N32PN·T *N32PN·zero = &N32PN·constant[0]; - N32PN·T *N32PN·one = &N32PN·constant[1]; - N32PN·T *N32PN·all_one_bit = &N32PN·constant[2]; - N32PN·T *N32PN·msb = &N32PN·constant[3]; - N32PN·T *N32PN·lsb = &N32PN·constant[1]; - - // the allocate an array of N32 - N32PN·T *N32PN·allocate_array(Extent extent ,N32PN·Allocate_MemoryFault memory_fault){ - N32PN·T *instance = malloc((extent + 1) * sizeof(N32PN·T) ); - if(!instance){ - return memory_fault ? memory_fault(extent) : NULL; - } - return instance; - } - - N32PN·T *N32PN·allocate_array_zero(Extent extent ,N32PN·Allocate_MemoryFault memory_fault){ - N32PN·T *instance = calloc( extent + 1 ,sizeof(N32PN·T) ); - if(!instance){ - return memory_fault ? memory_fault(extent) : NULL; - } - return instance; - } - - void N32PN·deallocate(N32PN·T *unencumbered){ - free(unencumbered); - } - - #endif - - // This part is included after the library user's code - #ifdef LOCAL - - // instance - - struct N32PN·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 N32PN·T N32PN·t[4]; - - - // allocation - - extern N32PN·T *N32PN·allocate_array(Extent, N32PN·Allocate_MemoryFault); - extern N32PN·T *N32PN·allocate_array_zero(Extent, N32PN·Allocate_MemoryFault); - extern void N32PN·deallocate(N32PN·T *); - - // so the user can access numbers in an array allocation - Local N32PN·T* N32PN·access(N32PN·T *array ,Extent index){ - return &array[index]; - } - - Local void N32PN·from_uint32(N32PN·T *destination ,uint32_t value){ - if(destination == NULL) return; - destination->d0 = value; - } - - // copy, convenience copy - - Local void N32PN·copy(N32PN·T *destination ,N32PN·T *source){ - if(source == destination) return; // that was easy! - *destination = *source; - } - - Local void N32PN·set_to_zero(N32PN·T *instance){ - instance->d0 = 0; - } - - Local void N32PN·set_to_one(N32PN·T *instance){ - instance->d0 = 1; - } - - // bit operations - - Local void N32PN·bit_and(N32PN·T *result, N32PN·T *a, N32PN·T *b){ - result->d0 = a->d0 & b->d0; - } - - // result can be one of the operands - Local void N32PN·bit_or(N32PN·T *result, N32PN·T *a, N32PN·T *b){ - result->d0 = a->d0 | b->d0; - } - - // result can the same as the operand - Local void N32PN·bit_complement(N32PN·T *result, N32PN·T *a){ - result->d0 = ~a->d0; - } - - // result can the same as the operand - Local void N32PN·bit_twos_complement(N32PN·T *result ,N32PN·T *a){ - result->d0 = ~a->d0 + 1; - } - - // test functions - - Local N32PN·Order N32PN·compare(N32PN·T *a, N32PN·T *b){ - if(a->d0 < b->d0) return N32PN·Order_lt; - if(a->d0 > b->d0) return N32PN·Order_gt; - return N32PN·Order_eq; - } - - Local bool N32PN·lt(N32PN·T *a ,N32PN·T *b){ - return a->d0 < b->d0; - } - - Local bool N32PN·gt(N32PN·T *a ,N32PN·T *b){ - return a->d0 > b->d0; - } - - Local bool N32PN·eq(N32PN·T *a ,N32PN·T *b){ - return a->d0 == b->d0; - } - - Local bool N32PN·eq_zero(N32PN·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 N32PN·Status·accumulator1_overflow - // - // When accumulator1 and accumulator0 point to the same location, the result is the accumulator1 value. - Local N32PN·Status N32PN·accumulate(N32PN·T *accumulator1 ,N32PN·T *accumulator0 ,...){ - - va_list args; - va_start(args ,accumulator0); - uint32_t sum = accumulator0->d0; - uint32_t carry = 0; - N32PN·T *current; - - while( (current = va_arg(args ,N32PN·T *)) ){ - sum += current->d0; - if(sum < current->d0){ // Accumulator1 into carry - (carry)++; - if(carry == 0){ - va_end(args); - return N32PN·Status·accumulator1_overflow; - } - } - } - va_end(args); - - // wipes out prior value of accumulator1 - accumulator1->d0 = carry; - - return N32PN·Status·ok; - } - - Local N32PN·Status N32PN·add(N32PN·T *sum ,N32PN·T *a ,N32PN·T *b){ - uint64_t result = (uint64_t)a->d0 + (uint64_t)b->d0; - sum->d0 = (uint32_t)result; - return (result >> 32) ? N32PN·Status·carry : N32PN·Status·ok; - } - - Local bool N32PN·increment(N32PN·T *a){ - a->d0++; - return a->d0 == 0; - } - - Local N32PN·Status N32PN·subtract(N32PN·T *difference ,N32PN·T *a ,N32PN·T *b){ - uint64_t diff = (uint64_t) a->d0 - (uint64_t) b->d0; - difference->d0 = (uint32_t)diff; - return (diff > a->d0) ? N32PN·Status·borrow : N32PN·Status·ok; - } - - - Local N32PN·Status N32PN·multiply(N32PN·T *product1 ,N32PN·T *product0 ,N32PN·T *a ,N32PN·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 N32PN·Status·one_word_product; - return N32PN·Status·two_word_product; - } - - Local N32PN·Status N32PN·divide(N32PN·T *remainder ,N32PN·T *quotient ,N32PN·T *a ,N32PN·T *b){ - if(b->d0 == 0) return N32PN·Status·undefined_divide_by_zero; - - quotient->d0 = a->d0 / b->d0; - remainder->d0 = a->d0 - (quotient->d0 * b->d0); - - return N32PN·Status·ok; - } - - Local N32PN·Status N32PN·modulus(N32PN·T *remainder ,N32PN·T *a ,N32PN·T *b){ - if(b->d0 == 0) return N32PN·Status·undefined_modulus_zero; - uint32_t quotient = a->d0 / b->d0; - remainder->d0 = a->d0 - (quotient * b->d0); - return N32PN·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 N32PN·Status N32PN·shift - ( - uint32_t shift_count - ,N32PN·T *spill - ,N32PN·T *operand - ,N32PN·T *fill - ,ShiftOp shift_op - ,ShiftOp complement_shift_op - ){ - - // If no result is needed, return immediately. - if(operand == NULL && spill == NULL) return N32PN·Status·ok; - - // Treat NULL operand as zero. - if(operand == NULL){ - operand = &N32PN·t[0]; - N32PN·copy(operand, N32PN·zero); - } - - // Shifting more than one word breaks our fill/spill model. - if(shift_count > 31) return N32PN·Status·gt_max_shift_count; - - // The given operand is still required after it is modified, so we copy it. - N32PN·T *given_operand = &N32PN·t[1]; - N32PN·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)); - N32PN·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 N32PN·Status·ok; - } - - // Define concrete shift functions using valid C function pointers - Local N32PN·Status - N32PN·shift_left(uint32_t shift_count, N32PN·T *spill, N32PN·T *operand, N32PN·T *fill){ - return N32PN·shift(shift_count, spill, operand, fill, shift_left_op, shift_right_op); - } - - Local N32PN·Status - N32PN·shift_right(uint32_t shift_count, N32PN·T *spill, N32PN·T *operand, N32PN·T *fill){ - return N32PN·shift(shift_count, spill, operand, fill, shift_right_op, shift_left_op); - } - - Local N32PN·Status - N32PN·arithmetic_shift_right(uint32_t shift_count, N32PN·T *operand, N32PN·T *spill){ - - // Guard against excessive shift counts - if(shift_count > 31) return N32PN·Status·gt_max_shift_count; - - // A NULL operand is treated as zero - if(operand == NULL){ - operand = &N32PN·t[0]; - N32PN·copy(operand, N32PN·zero); - } - - // Pick the fill value based on the sign bit - N32PN·T *fill = (operand->d0 & 0x80000000) ? N32PN·all_one_bit : N32PN·zero; - - // Call shift_right with the appropriate fill - return N32PN·shift_right(shift_count, spill, operand, fill); - } - - Local const N32PN·Λ N32PN·λ = { - - .allocate_array = N32PN·allocate_array - ,.allocate_array_zero = N32PN·allocate_array_zero - ,.deallocate = N32PN·deallocate - - ,.copy = N32PN·copy - ,.bit_and = N32PN·bit_and - ,.bit_or = N32PN·bit_or - ,.bit_complement = N32PN·bit_complement - ,.bit_twos_complement = N32PN·bit_twos_complement - ,.compare = N32PN·compare - ,.lt = N32PN·lt - ,.gt = N32PN·gt - ,.eq = N32PN·eq - ,.eq_zero = N32PN·eq_zero - ,.accumulate = N32PN·accumulate - ,.add = N32PN·add - ,.increment = N32PN·increment - ,.subtract = N32PN·subtract - ,.multiply = N32PN·multiply - ,.divide = N32PN·divide - ,.modulus = N32PN·modulus - ,.shift_left = N32PN·shift_left - ,.shift_right = N32PN·shift_right - ,.arithmetic_shift_right = N32PN·arithmetic_shift_right - - ,.access = N32PN·access - ,.from_uint32 = N32PN·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/N64PN.lib.c" "b/developer/cc\360\237\226\211/N64PN.lib.c" deleted file mode 100644 index 583ad08..0000000 --- "a/developer/cc\360\237\226\211/N64PN.lib.c" +++ /dev/null @@ -1,477 +0,0 @@ -/* - 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 N64PN·DEBUG - -#ifndef FACE -#define N64PN·IMPLEMENTATION -#define FACE -#endif - -//-------------------------------------------------------------------------------- -// Interface - -#ifndef N64PN·FACE -#define N64PN·FACE - - #include - #include - #include - #include - - //---------------------------------------- - // Instance Data (Declaration Only) - - typedef uint64_t Extent; - typedef uint64_t Digit; - - typedef struct N64PN·T N64PN·T; - - extern N64PN·T *N64PN·zero; - extern N64PN·T *N64PN·one; - extern N64PN·T *N64PN·all_one_bit; - extern N64PN·T *N64PN·lsb; - extern N64PN·T *N64PN·msb; - - //---------------------------------------- - // Return/Error Status and handlers - - typedef enum { - N64PN·Status·ok = 0 - ,N64PN·Status·overflow = 1 - ,N64PN·Status·accumulator1_overflow = 2 - ,N64PN·Status·carry = 3 - ,N64PN·Status·borrow = 4 - ,N64PN·Status·undefined_divide_by_zero = 5 - ,N64PN·Status·undefined_modulus_zero = 6 - ,N64PN·Status·gt_max_shift_count = 7 - ,N64PN·Status·spill_eq_operand = 8 // not currently signaled, result will be spill value - ,N64PN·Status·one_word_product = 9 - ,N64PN·Status·two_word_product = 10 - } N64PN·Status; - - typedef enum { - N64PN·Order_lt = -1 - ,N64PN·Order_eq = 0 - ,N64PN·Order_gt = 1 - } N64PN·Order; - - typedef N64PN·T *(*N64PN·Allocate_MemoryFault)(Extent); - - //---------------------------------------- - // Interface - - typedef struct { - - N64PN·T *(*allocate_array_zero)(Extent, N64PN·Allocate_MemoryFault); - N64PN·T *(*allocate_array)(Extent, N64PN·Allocate_MemoryFault); - void (*deallocate)(N64PN·T*); - - void (*copy)(N64PN·T*, N64PN·T*); - void (*bit_and)(N64PN·T*, N64PN·T*, N64PN·T*); - void (*bit_or)(N64PN·T*, N64PN·T*, N64PN·T*); - void (*bit_complement)(N64PN·T*, N64PN·T*); - void (*bit_twos_complement)(N64PN·T*, N64PN·T*); - N64PN·Order (*compare)(N64PN·T*, N64PN·T*); - bool (*lt)(N64PN·T*, N64PN·T*); - bool (*gt)(N64PN·T*, N64PN·T*); - bool (*eq)(N64PN·T*, N64PN·T*); - bool (*eq_zero)(N64PN·T*); - N64PN·Status (*accumulate)(N64PN·T *accumulator1, N64PN·T *accumulator0, ...); - N64PN·Status (*add)(N64PN·T*, N64PN·T*, N64PN·T*); - bool (*increment)(N64PN·T *a); - N64PN·Status (*subtract)(N64PN·T*, N64PN·T*, N64PN·T*); - N64PN·Status (*multiply)(N64PN·T*, N64PN·T*, N64PN·T*, N64PN·T*); - N64PN·Status (*divide)(N64PN·T*, N64PN·T*, N64PN·T*, N64PN·T*); - N64PN·Status (*modulus)(N64PN·T*, N64PN·T*, N64PN·T*); - N64PN·Status (*shift_left)(Extent, N64PN·T*, N64PN·T*, N64PN·T*); - N64PN·Status (*shift_right)(Extent, N64PN·T*, N64PN·T*, N64PN·T*); - N64PN·Status (*arithmetic_shift_right)(Extent, N64PN·T*, N64PN·T*); - - N64PN·T* (*access)(N64PN·T*, Extent); - void (*from_uint64)(N64PN·T *destination, uint64_t value); - - } N64PN·Λ; - - Local const N64PN·Λ N64PN·λ; // initialized in the LOCAL section - -#endif - -//-------------------------------------------------------------------------------- -// Implementation - -#ifdef N64PN·IMPLEMENTATION - - // this part goes into the library - #ifndef LOCAL - - #include - #include - - struct N64PN·T { - Digit d0; - }; - - // For constants, we store them in an array for convenience - // 0, 1, all bits set (~0ULL), and MSB set (1ULL<<63) - N64PN·T N64PN·constant[4] = { - {.d0 = 0ULL}, - {.d0 = 1ULL}, - {.d0 = ~(uint64_t)0ULL}, - {.d0 = 1ULL << 63} - }; - - N64PN·T *N64PN·zero = &N64PN·constant[0]; - N64PN·T *N64PN·one = &N64PN·constant[1]; - N64PN·T *N64PN·all_one_bit = &N64PN·constant[2]; - N64PN·T *N64PN·msb = &N64PN·constant[3]; - N64PN·T *N64PN·lsb = &N64PN·constant[1]; - - // allocate an array of N64 - N64PN·T *N64PN·allocate_array(Extent extent, N64PN·Allocate_MemoryFault memory_fault){ - N64PN·T *instance = malloc( (extent + 1) * sizeof(N64PN·T) ); - if(!instance){ - return memory_fault ? memory_fault(extent) : NULL; - } - return instance; - } - - N64PN·T *N64PN·allocate_array_zero(Extent extent, N64PN·Allocate_MemoryFault memory_fault){ - N64PN·T *instance = calloc(extent + 1, sizeof(N64PN·T)); - if(!instance){ - return memory_fault ? memory_fault(extent) : NULL; - } - return instance; - } - - void N64PN·deallocate(N64PN·T *unencumbered){ - free(unencumbered); - } - - #endif - - // This part is included after the library user's code - #ifdef LOCAL - - // instance - - struct N64PN·T { - Digit d0; - }; - - // local temporary variables - Local N64PN·T N64PN·t[4]; - - // allocation references - extern N64PN·T *N64PN·allocate_array(Extent, N64PN·Allocate_MemoryFault); - extern N64PN·T *N64PN·allocate_array_zero(Extent, N64PN·Allocate_MemoryFault); - extern void N64PN·deallocate(N64PN·T *); - - // Access array - Local N64PN·T* N64PN·access(N64PN·T *array, Extent index){ - return &array[index]; - } - - Local void N64PN·from_uint64(N64PN·T *destination, uint64_t value){ - if(destination == NULL) return; - destination->d0 = value; - } - - // copy - Local void N64PN·copy(N64PN·T *destination, N64PN·T *source){ - if(source == destination) return; - *destination = *source; - } - - // bit operations - - Local void N64PN·bit_and(N64PN·T *result, N64PN·T *a, N64PN·T *b){ - result->d0 = a->d0 & b->d0; - } - - Local void N64PN·bit_or(N64PN·T *result, N64PN·T *a, N64PN·T *b){ - result->d0 = a->d0 | b->d0; - } - - Local void N64PN·bit_complement(N64PN·T *result, N64PN·T *a){ - result->d0 = ~a->d0; - } - - Local void N64PN·bit_twos_complement(N64PN·T *result, N64PN·T *a){ - result->d0 = ~a->d0 + 1ULL; - } - - // compare & test functions - - Local N64PN·Order N64PN·compare(N64PN·T *a, N64PN·T *b){ - if(a->d0 < b->d0) return N64PN·Order_lt; - if(a->d0 > b->d0) return N64PN·Order_gt; - return N64PN·Order_eq; - } - - Local bool N64PN·lt(N64PN·T *a, N64PN·T *b){ - return (a->d0 < b->d0); - } - - Local bool N64PN·gt(N64PN·T *a, N64PN·T *b){ - return (a->d0 > b->d0); - } - - Local bool N64PN·eq(N64PN·T *a, N64PN·T *b){ - return (a->d0 == b->d0); - } - - Local bool N64PN·eq_zero(N64PN·T *a){ - return (a->d0 == 0ULL); - } - - // arithmetic operations - - // accumulate - Local N64PN·Status N64PN·accumulate(N64PN·T *accumulator1, N64PN·T *accumulator0, ...){ - va_list args; - va_start(args, accumulator0); - - uint64_t sum = accumulator0->d0; - uint64_t carry = 0; - N64PN·T *current; - - while( (current = va_arg(args, N64PN·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 N64PN·Status·accumulator1_overflow; - } - } - } - va_end(args); - - accumulator1->d0 = carry; - return N64PN·Status·ok; - } - - // add - Local N64PN·Status N64PN·add(N64PN·T *sum, N64PN·T *a, N64PN·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 N64PN·Status·carry; // means we overflowed - return N64PN·Status·ok; - } - - Local bool N64PN·increment(N64PN·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 N64PN·Status N64PN·subtract(N64PN·T *difference, N64PN·T *a, N64PN·T *b){ - uint64_t tmpA = a->d0; - uint64_t tmpB = b->d0; - uint64_t diff = tmpA - tmpB; - difference->d0 = diff; - if(diff > tmpA) return N64PN·Status·borrow; // indicates we borrowed - return N64PN·Status·ok; - } - - // multiply - // We'll do a 64x64->128 using two 64-bit accumulators - Local N64PN·Status N64PN·multiply(N64PN·T *product1, N64PN·T *product0, N64PN·T *a, N64PN·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 N64PN·Status·one_word_product; - return N64PN·Status·two_word_product; - } - - // divide - Local N64PN·Status N64PN·divide(N64PN·T *remainder, N64PN·T *quotient, N64PN·T *a, N64PN·T *b){ - // we do not handle a > 64-bit, just the single 64-bit - if(b->d0 == 0ULL) return N64PN·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 N64PN·Status·ok; - } - - // modulus - Local N64PN·Status N64PN·modulus(N64PN·T *remainder, N64PN·T *a, N64PN·T *b){ - if(b->d0 == 0ULL) return N64PN·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 N64PN·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 N64PN·Status N64PN·shift - ( - uint64_t shift_count, - N64PN·T *spill, - N64PN·T *operand, - N64PN·T *fill, - ShiftOp shift_op, - ShiftOp complement_shift_op - ){ - if(operand == NULL && spill == NULL) return N64PN·Status·ok; - - // Treat NULL operand as zero - if(operand == NULL){ - operand = &N64PN·t[0]; - N64PN·copy(operand, N64PN·zero); - } - - // Shifting more than 63 bits breaks fill/spill logic - if(shift_count > 63ULL) return N64PN·Status·gt_max_shift_count; - - N64PN·T *given_operand = &N64PN·t[1]; - N64PN·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)); - N64PN·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 N64PN·Status·ok; - } - - Local N64PN·Status N64PN·shift_left(uint64_t shift_count, N64PN·T *spill, N64PN·T *operand, N64PN·T *fill){ - return N64PN·shift(shift_count, spill, operand, fill, shift_left_op, shift_right_op); - } - - Local N64PN·Status N64PN·shift_right(uint64_t shift_count, N64PN·T *spill, N64PN·T *operand, N64PN·T *fill){ - return N64PN·shift(shift_count, spill, operand, fill, shift_right_op, shift_left_op); - } - - Local N64PN·Status N64PN·arithmetic_shift_right(uint64_t shift_count, N64PN·T *operand, N64PN·T *spill){ - if(shift_count > 63ULL) return N64PN·Status·gt_max_shift_count; - - // A NULL operand is treated as zero - if(operand == NULL){ - operand = &N64PN·t[0]; - N64PN·copy(operand, N64PN·zero); - } - - // sign bit check - N64PN·T *fill = (operand->d0 & (1ULL << 63)) ? N64PN·all_one_bit : N64PN·zero; - return N64PN·shift_right(shift_count, spill, operand, fill); - } - - Local const N64PN·Λ N64PN·λ = { - .allocate_array = N64PN·allocate_array - ,.allocate_array_zero = N64PN·allocate_array_zero - ,.deallocate = N64PN·deallocate - - ,.copy = N64PN·copy - ,.bit_and = N64PN·bit_and - ,.bit_or = N64PN·bit_or - ,.bit_complement = N64PN·bit_complement - ,.bit_twos_complement = N64PN·bit_twos_complement - ,.compare = N64PN·compare - ,.lt = N64PN·lt - ,.gt = N64PN·gt - ,.eq = N64PN·eq - ,.eq_zero = N64PN·eq_zero - ,.accumulate = N64PN·accumulate - ,.add = N64PN·add - ,.increment = N64PN·increment - ,.subtract = N64PN·subtract - ,.multiply = N64PN·multiply - ,.divide = N64PN·divide - ,.modulus = N64PN·modulus - ,.shift_left = N64PN·shift_left - ,.shift_right = N64PN·shift_right - ,.arithmetic_shift_right = N64PN·arithmetic_shift_right - - ,.access = N64PN·access - ,.from_uint64 = N64PN·from_uint64 - }; - - #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..ad52309 --- /dev/null +++ "b/developer/cc\360\237\226\211/N8.lib.c" @@ -0,0 +1,433 @@ +/* + N8 - PN = refers to the use of processor native accumulator + + 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/N8PN.lib.c" "b/developer/cc\360\237\226\211/N8PN.lib.c" deleted file mode 100644 index 9cfd66d..0000000 --- "a/developer/cc\360\237\226\211/N8PN.lib.c" +++ /dev/null @@ -1,433 +0,0 @@ -/* - N8PN - PN = refers to the use of processor native accumulator - - 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 N8PN·DEBUG - -#ifndef FACE -#define N8PN·IMPLEMENTATION -#define FACE -#endif - -//-------------------------------------------------------------------------------- -// Interface - -#ifndef N8PN·FACE -#define N8PN·FACE - - #include - #include - #include - #include - - //---------------------------------------- - // Instance Data (Declaration Only) - - typedef uint8_t Extent; - typedef uint8_t Digit; - - typedef struct N8PN·T N8PN·T; - - extern N8PN·T *N8PN·zero; - extern N8PN·T *N8PN·one; - extern N8PN·T *N8PN·all_one_bit; - extern N8PN·T *N8PN·lsb; - extern N8PN·T *N8PN·msb; - - //---------------------------------------- - // Return/Error Status and handlers - - typedef enum{ - N8PN·Status·ok = 0 - ,N8PN·Status·overflow = 1 - ,N8PN·Status·accumulator1_overflow = 2 - ,N8PN·Status·carry = 3 - ,N8PN·Status·borrow = 4 - ,N8PN·Status·undefined_divide_by_zero = 5 - ,N8PN·Status·undefined_modulus_zero = 6 - ,N8PN·Status·gt_max_shift_count = 7 - ,N8PN·Status·spill_eq_operand = 8 - ,N8PN·Status·one_word_product = 9 - ,N8PN·Status·two_word_product = 10 - } N8PN·Status; - - typedef enum{ - N8PN·Order_lt = -1 - ,N8PN·Order_eq = 0 - ,N8PN·Order_gt = 1 - } N8PN·Order; - - typedef N8PN·T *( *N8PN·Allocate_MemoryFault )(Extent); - - //---------------------------------------- - // Interface - - typedef struct{ - - N8PN·T *(*allocate_array_zero)(Extent, N8PN·Allocate_MemoryFault); - N8PN·T *(*allocate_array)(Extent, N8PN·Allocate_MemoryFault); - void (*deallocate)(N8PN·T*); - - void (*copy)(N8PN·T*, N8PN·T*); - void (*bit_and)(N8PN·T*, N8PN·T*, N8PN·T*); - void (*bit_or)(N8PN·T*, N8PN·T*, N8PN·T*); - void (*bit_complement)(N8PN·T*, N8PN·T*); - void (*bit_twos_complement)(N8PN·T*, N8PN·T*); - N8PN·Order (*compare)(N8PN·T*, N8PN·T*); - bool (*lt)(N8PN·T*, N8PN·T*); - bool (*gt)(N8PN·T*, N8PN·T*); - bool (*eq)(N8PN·T*, N8PN·T*); - bool (*eq_zero)(N8PN·T*); - N8PN·Status (*accumulate)(N8PN·T *accumulator1 ,N8PN·T *accumulator0 ,...); - N8PN·Status (*add)(N8PN·T*, N8PN·T*, N8PN·T*); - bool (*increment)(N8PN·T *a); - N8PN·Status (*subtract)(N8PN·T*, N8PN·T*, N8PN·T*); - N8PN·Status (*multiply)(N8PN·T*, N8PN·T*, N8PN·T*, N8PN·T*); - N8PN·Status (*divide)(N8PN·T*, N8PN·T*, N8PN·T*, N8PN·T*); - N8PN·Status (*modulus)(N8PN·T*, N8PN·T*, N8PN·T*); - N8PN·Status (*shift_left)(Extent, N8PN·T*, N8PN·T*, N8PN·T*); - N8PN·Status (*shift_right)(Extent, N8PN·T*, N8PN·T*, N8PN·T*); - N8PN·Status (*arithmetic_shift_right)(Extent, N8PN·T*, N8PN·T*); - - N8PN·T* (*access)(N8PN·T*, Extent); - void (*from_uint32)(N8PN·T *destination ,uint32_t value); - } N8PN·Λ; - - Local const N8PN·Λ N8PN·λ; // initialized in the LOCAL section - -#endif - -//-------------------------------------------------------------------------------- -// Implementation - -#ifdef N8PN·IMPLEMENTATION - - // this part goes into the library - #ifndef LOCAL - - #include - #include - - struct N8PN·T{ - Digit d0; - }; - - N8PN·T N8PN·constant[4] = { - {.d0 = 0}, - {.d0 = 1}, - {.d0 = ~(uint8_t)0}, - {.d0 = 1 << 7} - }; - - N8PN·T *N8PN·zero = &N8PN·constant[0]; - N8PN·T *N8PN·one = &N8PN·constant[1]; - N8PN·T *N8PN·all_one_bit = &N8PN·constant[2]; - N8PN·T *N8PN·msb = &N8PN·constant[3]; - N8PN·T *N8PN·lsb = &N8PN·constant[1]; - - // the allocate an array of N8 - N8PN·T *N8PN·allocate_array(Extent extent ,N8PN·Allocate_MemoryFault memory_fault){ - N8PN·T *instance = malloc((extent + 1) * sizeof(N8PN·T)); - if(!instance){ - return memory_fault ? memory_fault(extent) : NULL; - } - return instance; - } - - N8PN·T *N8PN·allocate_array_zero(Extent extent ,N8PN·Allocate_MemoryFault memory_fault){ - N8PN·T *instance = calloc(extent + 1, sizeof(N8PN·T)); - if(!instance){ - return memory_fault ? memory_fault(extent) : NULL; - } - return instance; - } - - void N8PN·deallocate(N8PN·T *unencumbered){ - free(unencumbered); - } - - - #endif - - // This part is included after the library user's code - #ifdef LOCAL - - // instance - - struct N8PN·T{ - Digit d0; - }; - - // temporary variables - Local N8PN·T N8PN·t[4]; - - // allocation - - extern N8PN·T *N8PN·allocate_array(Extent, N8PN·Allocate_MemoryFault); - extern N8PN·T *N8PN·allocate_array_zero(Extent, N8PN·Allocate_MemoryFault); - extern void N8PN·deallocate(N8PN·T *); - - // so the user can access numbers in an array allocation - Local N8PN·T* N8PN·access(N8PN·T *array ,Extent index){ - return &array[index]; - } - - Local void N8PN·from_uint32(N8PN·T *destination ,uint32_t value){ - if(destination == NULL) return; - destination->d0 = (uint8_t)(value & 0xFF); - } - - // copy, convenience copy - - Local void N8PN·copy(N8PN·T *destination ,N8PN·T *source){ - if(source == destination) return; - *destination = *source; - } - - Local void N8PN·set_to_zero(N8PN·T *instance){ - instance->d0 = 0; - } - - Local void N8PN·set_to_one(N8PN·T *instance){ - instance->d0 = 1; - } - - // bit operations - - Local void N8PN·bit_and(N8PN·T *result, N8PN·T *a, N8PN·T *b){ - result->d0 = a->d0 & b->d0; - } - - Local void N8PN·bit_or(N8PN·T *result, N8PN·T *a, N8PN·T *b){ - result->d0 = a->d0 | b->d0; - } - - Local void N8PN·bit_complement(N8PN·T *result, N8PN·T *a){ - result->d0 = ~a->d0; - } - - Local void N8PN·bit_twos_complement(N8PN·T *result ,N8PN·T *a){ - result->d0 = (uint8_t)(~a->d0 + 1); - } - - // test functions - - Local N8PN·Order N8PN·compare(N8PN·T *a, N8PN·T *b){ - if(a->d0 < b->d0) return N8PN·Order_lt; - if(a->d0 > b->d0) return N8PN·Order_gt; - return N8PN·Order_eq; - } - - Local bool N8PN·lt(N8PN·T *a ,N8PN·T *b){ - return a->d0 < b->d0; - } - - Local bool N8PN·gt(N8PN·T *a ,N8PN·T *b){ - return a->d0 > b->d0; - } - - Local bool N8PN·eq(N8PN·T *a ,N8PN·T *b){ - return a->d0 == b->d0; - } - - Local bool N8PN·eq_zero(N8PN·T *a){ - return a->d0 == 0; - } - - // arithmetic operations - - Local N8PN·Status N8PN·accumulate(N8PN·T *accumulator1 ,N8PN·T *accumulator0 ,...){ - - va_list args; - va_start(args ,accumulator0); - uint32_t sum = accumulator0->d0; - uint32_t carry = 0; - N8PN·T *current; - - while( (current = va_arg(args ,N8PN·T*)) ){ - sum += current->d0; - if(sum < current->d0){ - (carry)++; - if(carry == 0){ - va_end(args); - return N8PN·Status·accumulator1_overflow; - } - } - } - va_end(args); - - accumulator1->d0 = (uint8_t)carry; - return N8PN·Status·ok; - } - - Local N8PN·Status N8PN·add(N8PN·T *sum ,N8PN·T *a ,N8PN·T *b){ - uint32_t result = (uint32_t)a->d0 + (uint32_t)b->d0; - sum->d0 = (uint8_t)(result & 0xFF); - return (result >> 8) ? N8PN·Status·carry : N8PN·Status·ok; - } - - Local bool N8PN·increment(N8PN·T *a){ - a->d0++; - return (a->d0 == 0); - } - - Local N8PN·Status N8PN·subtract(N8PN·T *difference ,N8PN·T *a ,N8PN·T *b){ - uint32_t diff = (uint32_t)a->d0 - (uint32_t)b->d0; - difference->d0 = (uint8_t)(diff & 0xFF); - return (diff > a->d0) ? N8PN·Status·borrow : N8PN·Status·ok; - } - - Local N8PN·Status N8PN·multiply(N8PN·T *product1 ,N8PN·T *product0 ,N8PN·T *a ,N8PN·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 N8PN·Status·one_word_product; - return N8PN·Status·two_word_product; - } - - Local N8PN·Status N8PN·divide(N8PN·T *remainder ,N8PN·T *quotient ,N8PN·T *a ,N8PN·T *b){ - if(b->d0 == 0) return N8PN·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 N8PN·Status·ok; - } - - Local N8PN·Status N8PN·modulus(N8PN·T *remainder ,N8PN·T *a ,N8PN·T *b){ - if(b->d0 == 0) return N8PN·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 N8PN·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 N8PN·Status N8PN·shift - ( - uint8_t shift_count - ,N8PN·T *spill - ,N8PN·T *operand - ,N8PN·T *fill - ,ShiftOp shift_op - ,ShiftOp complement_shift_op - ){ - - if(operand == NULL && spill == NULL) return N8PN·Status·ok; - - if(operand == NULL){ - operand = &N8PN·t[0]; - N8PN·copy(operand, N8PN·zero); - } - - if(shift_count > 7) return N8PN·Status·gt_max_shift_count; - - N8PN·T *given_operand = &N8PN·t[1]; - N8PN·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)); - N8PN·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 N8PN·Status·ok; - } - - Local N8PN·Status - N8PN·shift_left(uint8_t shift_count, N8PN·T *spill, N8PN·T *operand, N8PN·T *fill){ - return N8PN·shift(shift_count, spill, operand, fill, shift_left_op, shift_right_op); - } - - Local N8PN·Status - N8PN·shift_right(uint8_t shift_count, N8PN·T *spill, N8PN·T *operand, N8PN·T *fill){ - return N8PN·shift(shift_count, spill, operand, fill, shift_right_op, shift_left_op); - } - - Local N8PN·Status - N8PN·arithmetic_shift_right(uint8_t shift_count, N8PN·T *operand, N8PN·T *spill){ - - if(shift_count > 7) return N8PN·Status·gt_max_shift_count; - - if(operand == NULL){ - operand = &N8PN·t[0]; - N8PN·copy(operand, N8PN·zero); - } - - N8PN·T *fill = (operand->d0 & 0x80) ? N8PN·all_one_bit : N8PN·zero; - return N8PN·shift_right(shift_count, spill, operand, fill); - } - - Local const N8PN·Λ N8PN·λ = { - - .allocate_array = N8PN·allocate_array - ,.allocate_array_zero = N8PN·allocate_array_zero - ,.deallocate = N8PN·deallocate - - ,.copy = N8PN·copy - ,.bit_and = N8PN·bit_and - ,.bit_or = N8PN·bit_or - ,.bit_complement = N8PN·bit_complement - ,.bit_twos_complement = N8PN·bit_twos_complement - ,.compare = N8PN·compare - ,.lt = N8PN·lt - ,.gt = N8PN·gt - ,.eq = N8PN·eq - ,.eq_zero = N8PN·eq_zero - ,.accumulate = N8PN·accumulate - ,.add = N8PN·add - ,.increment = N8PN·increment - ,.subtract = N8PN·subtract - ,.multiply = N8PN·multiply - ,.divide = N8PN·divide - ,.modulus = N8PN·modulus - ,.shift_left = N8PN·shift_left - ,.shift_right = N8PN·shift_right - ,.arithmetic_shift_right = N8PN·arithmetic_shift_right - - ,.access = N8PN·access - ,.from_uint32 = N8PN·from_uint32 - }; - - #endif - -#endif diff --git a/developer/python/fill_template b/developer/python/fill_template index f22e5cc..4569e33 100755 --- a/developer/python/fill_template +++ b/developer/python/fill_template @@ -19,7 +19,7 @@ def main(): #---------------------------------------- # N32 made from 1 digit, of 32 bits. - type_name = "N32" + type_name = "N32_1x32" digit_type = "uint32_t" digit_extent = 0 diff --git a/release/x86_64/fedora41/glibc_2.40/N16.lib.c b/release/x86_64/fedora41/glibc_2.40/N16.lib.c new file mode 100644 index 0000000..3b77116 --- /dev/null +++ b/release/x86_64/fedora41/glibc_2.40/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/release/x86_64/fedora41/glibc_2.40/N32_1x32.lib.c b/release/x86_64/fedora41/glibc_2.40/N32_1x32.lib.c new file mode 100644 index 0000000..c43bc8b --- /dev/null +++ b/release/x86_64/fedora41/glibc_2.40/N32_1x32.lib.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_1x32·DEBUG + +#ifndef FACE +#define N32_1x32·IMPLEMENTATION +#define FACE +#endif + +//-------------------------------------------------------------------------------- +// Interface + +#ifndef N32_1x32·FACE +#define N32_1x32·FACE + + #include + #include + #include + #include + + //---------------------------------------- + // Instance Data (Declaration Only) + + typedef uint32_t Extent; + typedef uint32_t Digit; + + typedef struct N32_1x32·T N32_1x32·T; + + extern N32_1x32·T *N32_1x32·zero; + extern N32_1x32·T *N32_1x32·one; + extern N32_1x32·T *N32_1x32·all_one_bit; + extern N32_1x32·T *N32_1x32·lsb; + extern N32_1x32·T *N32_1x32·msb; + + //---------------------------------------- + // Return/Error Status and handlers + + typedef enum{ + N32_1x32·Status·ok = 0 + ,N32_1x32·Status·overflow = 1 + ,N32_1x32·Status·accumulator1_overflow = 2 + ,N32_1x32·Status·carry = 3 + ,N32_1x32·Status·borrow = 4 + ,N32_1x32·Status·undefined_divide_by_zero = 5 + ,N32_1x32·Status·undefined_modulus_zero = 6 + ,N32_1x32·Status·gt_max_shift_count = 7 + ,N32_1x32·Status·spill_eq_operand = 8 // not currently signaled, result will be spill value + ,N32_1x32·Status·one_word_product = 9 + ,N32_1x32·Status·two_word_product = 10 + } N32_1x32·Status; + + typedef enum{ + N32_1x32·Order_lt = -1 + ,N32_1x32·Order_eq = 0 + ,N32_1x32·Order_gt = 1 + } N32_1x32·Order; + + typedef N32_1x32·T *( *N32_1x32·Allocate_MemoryFault )(Extent); + + //---------------------------------------- + // Interface + + typedef struct{ + + N32_1x32·T *(*allocate_array_zero)(Extent, N32_1x32·Allocate_MemoryFault); + N32_1x32·T *(*allocate_array)(Extent, N32_1x32·Allocate_MemoryFault); + void (*deallocate)(N32_1x32·T*); + + void (*copy)(N32_1x32·T*, N32_1x32·T*); + void (*bit_and)(N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); + void (*bit_or)(N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); + void (*bit_complement)(N32_1x32·T*, N32_1x32·T*); + void (*bit_twos_complement)(N32_1x32·T*, N32_1x32·T*); + N32_1x32·Order (*compare)(N32_1x32·T*, N32_1x32·T*); + bool (*lt)(N32_1x32·T*, N32_1x32·T*); + bool (*gt)(N32_1x32·T*, N32_1x32·T*); + bool (*eq)(N32_1x32·T*, N32_1x32·T*); + bool (*eq_zero)(N32_1x32·T*); + N32_1x32·Status (*accumulate)(N32_1x32·T *accumulator1 ,N32_1x32·T *accumulator0 ,...); + N32_1x32·Status (*add)(N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); + bool (*increment)(N32_1x32·T *a); + N32_1x32·Status (*subtract)(N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); + N32_1x32·Status (*multiply)(N32_1x32·T*, N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); + N32_1x32·Status (*divide)(N32_1x32·T*, N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); + N32_1x32·Status (*modulus)(N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); + N32_1x32·Status (*shift_left)(Extent, N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); + N32_1x32·Status (*shift_right)(Extent, N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); + N32_1x32·Status (*arithmetic_shift_right)(Extent, N32_1x32·T*, N32_1x32·T*); + + N32_1x32·T* (*access)(N32_1x32·T*, Extent); + void (*from_uint32)(N32_1x32·T *destination ,uint32_t value); + } N32_1x32·Λ; + + Local const N32_1x32·Λ N32_1x32·λ; // initialized in the LOCAL section + +#endif + +//-------------------------------------------------------------------------------- +// Implementation + +#ifdef N32_1x32·IMPLEMENTATION + + // this part goes into the library + #ifndef LOCAL + + #include + #include + + struct N32_1x32·T{ + Digit d0; + }; + + N32_1x32·T N32_1x32·constant[4] = { + {.d0 = 0}, + {.d0 = 1}, + {.d0 = ~(uint32_t)0}, + {.d0 = 1 << 31} + }; + + N32_1x32·T *N32_1x32·zero = &N32_1x32·constant[0]; + N32_1x32·T *N32_1x32·one = &N32_1x32·constant[1]; + N32_1x32·T *N32_1x32·all_one_bit = &N32_1x32·constant[2]; + N32_1x32·T *N32_1x32·msb = &N32_1x32·constant[3]; + N32_1x32·T *N32_1x32·lsb = &N32_1x32·constant[1]; + + // the allocate an array of N32 + N32_1x32·T *N32_1x32·allocate_array(Extent extent ,N32_1x32·Allocate_MemoryFault memory_fault){ + N32_1x32·T *instance = malloc((extent + 1) * sizeof(N32_1x32·T) ); + if(!instance){ + return memory_fault ? memory_fault(extent) : NULL; + } + return instance; + } + + N32_1x32·T *N32_1x32·allocate_array_zero(Extent extent ,N32_1x32·Allocate_MemoryFault memory_fault){ + N32_1x32·T *instance = calloc( extent + 1 ,sizeof(N32_1x32·T) ); + if(!instance){ + return memory_fault ? memory_fault(extent) : NULL; + } + return instance; + } + + void N32_1x32·deallocate(N32_1x32·T *unencumbered){ + free(unencumbered); + } + + #endif + + // This part is included after the library user's code + #ifdef LOCAL + + // instance + + struct N32_1x32·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_1x32·T N32_1x32·t[4]; + + + // allocation + + extern N32_1x32·T *N32_1x32·allocate_array(Extent, N32_1x32·Allocate_MemoryFault); + extern N32_1x32·T *N32_1x32·allocate_array_zero(Extent, N32_1x32·Allocate_MemoryFault); + extern void N32_1x32·deallocate(N32_1x32·T *); + + // so the user can access numbers in an array allocation + Local N32_1x32·T* N32_1x32·access(N32_1x32·T *array ,Extent index){ + return &array[index]; + } + + Local void N32_1x32·from_uint32(N32_1x32·T *destination ,uint32_t value){ + if(destination == NULL) return; + destination->d0 = value; + } + + // copy, convenience copy + + Local void N32_1x32·copy(N32_1x32·T *destination ,N32_1x32·T *source){ + if(source == destination) return; // that was easy! + *destination = *source; + } + + Local void N32_1x32·set_to_zero(N32_1x32·T *instance){ + instance->d0 = 0; + } + + Local void N32_1x32·set_to_one(N32_1x32·T *instance){ + instance->d0 = 1; + } + + // bit operations + + Local void N32_1x32·bit_and(N32_1x32·T *result, N32_1x32·T *a, N32_1x32·T *b){ + result->d0 = a->d0 & b->d0; + } + + // result can be one of the operands + Local void N32_1x32·bit_or(N32_1x32·T *result, N32_1x32·T *a, N32_1x32·T *b){ + result->d0 = a->d0 | b->d0; + } + + // result can the same as the operand + Local void N32_1x32·bit_complement(N32_1x32·T *result, N32_1x32·T *a){ + result->d0 = ~a->d0; + } + + // result can the same as the operand + Local void N32_1x32·bit_twos_complement(N32_1x32·T *result ,N32_1x32·T *a){ + result->d0 = ~a->d0 + 1; + } + + // test functions + + Local N32_1x32·Order N32_1x32·compare(N32_1x32·T *a, N32_1x32·T *b){ + if(a->d0 < b->d0) return N32_1x32·Order_lt; + if(a->d0 > b->d0) return N32_1x32·Order_gt; + return N32_1x32·Order_eq; + } + + Local bool N32_1x32·lt(N32_1x32·T *a ,N32_1x32·T *b){ + return a->d0 < b->d0; + } + + Local bool N32_1x32·gt(N32_1x32·T *a ,N32_1x32·T *b){ + return a->d0 > b->d0; + } + + Local bool N32_1x32·eq(N32_1x32·T *a ,N32_1x32·T *b){ + return a->d0 == b->d0; + } + + Local bool N32_1x32·eq_zero(N32_1x32·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_1x32·Status·accumulator1_overflow + // + // When accumulator1 and accumulator0 point to the same location, the result is the accumulator1 value. + Local N32_1x32·Status N32_1x32·accumulate(N32_1x32·T *accumulator1 ,N32_1x32·T *accumulator0 ,...){ + + va_list args; + va_start(args ,accumulator0); + uint32_t sum = accumulator0->d0; + uint32_t carry = 0; + N32_1x32·T *current; + + while( (current = va_arg(args ,N32_1x32·T *)) ){ + sum += current->d0; + if(sum < current->d0){ // Accumulator1 into carry + (carry)++; + if(carry == 0){ + va_end(args); + return N32_1x32·Status·accumulator1_overflow; + } + } + } + va_end(args); + + // wipes out prior value of accumulator1 + accumulator1->d0 = carry; + + return N32_1x32·Status·ok; + } + + Local N32_1x32·Status N32_1x32·add(N32_1x32·T *sum ,N32_1x32·T *a ,N32_1x32·T *b){ + uint64_t result = (uint64_t)a->d0 + (uint64_t)b->d0; + sum->d0 = (uint32_t)result; + return (result >> 32) ? N32_1x32·Status·carry : N32_1x32·Status·ok; + } + + Local bool N32_1x32·increment(N32_1x32·T *a){ + a->d0++; + return a->d0 == 0; + } + + Local N32_1x32·Status N32_1x32·subtract(N32_1x32·T *difference ,N32_1x32·T *a ,N32_1x32·T *b){ + uint64_t diff = (uint64_t) a->d0 - (uint64_t) b->d0; + difference->d0 = (uint32_t)diff; + return (diff > a->d0) ? N32_1x32·Status·borrow : N32_1x32·Status·ok; + } + + + Local N32_1x32·Status N32_1x32·multiply(N32_1x32·T *product1 ,N32_1x32·T *product0 ,N32_1x32·T *a ,N32_1x32·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_1x32·Status·one_word_product; + return N32_1x32·Status·two_word_product; + } + + Local N32_1x32·Status N32_1x32·divide(N32_1x32·T *remainder ,N32_1x32·T *quotient ,N32_1x32·T *a ,N32_1x32·T *b){ + if(b->d0 == 0) return N32_1x32·Status·undefined_divide_by_zero; + + quotient->d0 = a->d0 / b->d0; + remainder->d0 = a->d0 - (quotient->d0 * b->d0); + + return N32_1x32·Status·ok; + } + + Local N32_1x32·Status N32_1x32·modulus(N32_1x32·T *remainder ,N32_1x32·T *a ,N32_1x32·T *b){ + if(b->d0 == 0) return N32_1x32·Status·undefined_modulus_zero; + uint32_t quotient = a->d0 / b->d0; + remainder->d0 = a->d0 - (quotient * b->d0); + return N32_1x32·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_1x32·Status N32_1x32·shift + ( + uint32_t shift_count + ,N32_1x32·T *spill + ,N32_1x32·T *operand + ,N32_1x32·T *fill + ,ShiftOp shift_op + ,ShiftOp complement_shift_op + ){ + + // If no result is needed, return immediately. + if(operand == NULL && spill == NULL) return N32_1x32·Status·ok; + + // Treat NULL operand as zero. + if(operand == NULL){ + operand = &N32_1x32·t[0]; + N32_1x32·copy(operand, N32_1x32·zero); + } + + // Shifting more than one word breaks our fill/spill model. + if(shift_count > 31) return N32_1x32·Status·gt_max_shift_count; + + // The given operand is still required after it is modified, so we copy it. + N32_1x32·T *given_operand = &N32_1x32·t[1]; + N32_1x32·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_1x32·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_1x32·Status·ok; + } + + // Define concrete shift functions using valid C function pointers + Local N32_1x32·Status + N32_1x32·shift_left(uint32_t shift_count, N32_1x32·T *spill, N32_1x32·T *operand, N32_1x32·T *fill){ + return N32_1x32·shift(shift_count, spill, operand, fill, shift_left_op, shift_right_op); + } + + Local N32_1x32·Status + N32_1x32·shift_right(uint32_t shift_count, N32_1x32·T *spill, N32_1x32·T *operand, N32_1x32·T *fill){ + return N32_1x32·shift(shift_count, spill, operand, fill, shift_right_op, shift_left_op); + } + + Local N32_1x32·Status + N32_1x32·arithmetic_shift_right(uint32_t shift_count, N32_1x32·T *operand, N32_1x32·T *spill){ + + // Guard against excessive shift counts + if(shift_count > 31) return N32_1x32·Status·gt_max_shift_count; + + // A NULL operand is treated as zero + if(operand == NULL){ + operand = &N32_1x32·t[0]; + N32_1x32·copy(operand, N32_1x32·zero); + } + + // Pick the fill value based on the sign bit + N32_1x32·T *fill = (operand->d0 & 0x80000000) ? N32_1x32·all_one_bit : N32_1x32·zero; + + // Call shift_right with the appropriate fill + return N32_1x32·shift_right(shift_count, spill, operand, fill); + } + + Local const N32_1x32·Λ N32_1x32·λ = { + + .allocate_array = N32_1x32·allocate_array + ,.allocate_array_zero = N32_1x32·allocate_array_zero + ,.deallocate = N32_1x32·deallocate + + ,.copy = N32_1x32·copy + ,.bit_and = N32_1x32·bit_and + ,.bit_or = N32_1x32·bit_or + ,.bit_complement = N32_1x32·bit_complement + ,.bit_twos_complement = N32_1x32·bit_twos_complement + ,.compare = N32_1x32·compare + ,.lt = N32_1x32·lt + ,.gt = N32_1x32·gt + ,.eq = N32_1x32·eq + ,.eq_zero = N32_1x32·eq_zero + ,.accumulate = N32_1x32·accumulate + ,.add = N32_1x32·add + ,.increment = N32_1x32·increment + ,.subtract = N32_1x32·subtract + ,.multiply = N32_1x32·multiply + ,.divide = N32_1x32·divide + ,.modulus = N32_1x32·modulus + ,.shift_left = N32_1x32·shift_left + ,.shift_right = N32_1x32·shift_right + ,.arithmetic_shift_right = N32_1x32·arithmetic_shift_right + + ,.access = N32_1x32·access + ,.from_uint32 = N32_1x32·from_uint32 + }; + + #endif + +#endif diff --git a/release/x86_64/fedora41/glibc_2.40/N64.lib.c b/release/x86_64/fedora41/glibc_2.40/N64.lib.c new file mode 100644 index 0000000..9d98d58 --- /dev/null +++ b/release/x86_64/fedora41/glibc_2.40/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/release/x86_64/fedora41/glibc_2.40/N8.lib.c b/release/x86_64/fedora41/glibc_2.40/N8.lib.c new file mode 100644 index 0000000..ad52309 --- /dev/null +++ b/release/x86_64/fedora41/glibc_2.40/N8.lib.c @@ -0,0 +1,433 @@ +/* + N8 - PN = refers to the use of processor native accumulator + + 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/release/x86_64/fedora41/glibc_2.40/libN.a b/release/x86_64/fedora41/glibc_2.40/libN.a index 82a1f5c..f81996f 100644 Binary files a/release/x86_64/fedora41/glibc_2.40/libN.a and b/release/x86_64/fedora41/glibc_2.40/libN.a differ diff --git a/tester/cc/N16.lib.c b/tester/cc/N16.lib.c new file mode 100644 index 0000000..3b77116 --- /dev/null +++ b/tester/cc/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/tester/cc/N32_1x32.lib.c b/tester/cc/N32_1x32.lib.c new file mode 100644 index 0000000..c43bc8b --- /dev/null +++ b/tester/cc/N32_1x32.lib.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_1x32·DEBUG + +#ifndef FACE +#define N32_1x32·IMPLEMENTATION +#define FACE +#endif + +//-------------------------------------------------------------------------------- +// Interface + +#ifndef N32_1x32·FACE +#define N32_1x32·FACE + + #include + #include + #include + #include + + //---------------------------------------- + // Instance Data (Declaration Only) + + typedef uint32_t Extent; + typedef uint32_t Digit; + + typedef struct N32_1x32·T N32_1x32·T; + + extern N32_1x32·T *N32_1x32·zero; + extern N32_1x32·T *N32_1x32·one; + extern N32_1x32·T *N32_1x32·all_one_bit; + extern N32_1x32·T *N32_1x32·lsb; + extern N32_1x32·T *N32_1x32·msb; + + //---------------------------------------- + // Return/Error Status and handlers + + typedef enum{ + N32_1x32·Status·ok = 0 + ,N32_1x32·Status·overflow = 1 + ,N32_1x32·Status·accumulator1_overflow = 2 + ,N32_1x32·Status·carry = 3 + ,N32_1x32·Status·borrow = 4 + ,N32_1x32·Status·undefined_divide_by_zero = 5 + ,N32_1x32·Status·undefined_modulus_zero = 6 + ,N32_1x32·Status·gt_max_shift_count = 7 + ,N32_1x32·Status·spill_eq_operand = 8 // not currently signaled, result will be spill value + ,N32_1x32·Status·one_word_product = 9 + ,N32_1x32·Status·two_word_product = 10 + } N32_1x32·Status; + + typedef enum{ + N32_1x32·Order_lt = -1 + ,N32_1x32·Order_eq = 0 + ,N32_1x32·Order_gt = 1 + } N32_1x32·Order; + + typedef N32_1x32·T *( *N32_1x32·Allocate_MemoryFault )(Extent); + + //---------------------------------------- + // Interface + + typedef struct{ + + N32_1x32·T *(*allocate_array_zero)(Extent, N32_1x32·Allocate_MemoryFault); + N32_1x32·T *(*allocate_array)(Extent, N32_1x32·Allocate_MemoryFault); + void (*deallocate)(N32_1x32·T*); + + void (*copy)(N32_1x32·T*, N32_1x32·T*); + void (*bit_and)(N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); + void (*bit_or)(N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); + void (*bit_complement)(N32_1x32·T*, N32_1x32·T*); + void (*bit_twos_complement)(N32_1x32·T*, N32_1x32·T*); + N32_1x32·Order (*compare)(N32_1x32·T*, N32_1x32·T*); + bool (*lt)(N32_1x32·T*, N32_1x32·T*); + bool (*gt)(N32_1x32·T*, N32_1x32·T*); + bool (*eq)(N32_1x32·T*, N32_1x32·T*); + bool (*eq_zero)(N32_1x32·T*); + N32_1x32·Status (*accumulate)(N32_1x32·T *accumulator1 ,N32_1x32·T *accumulator0 ,...); + N32_1x32·Status (*add)(N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); + bool (*increment)(N32_1x32·T *a); + N32_1x32·Status (*subtract)(N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); + N32_1x32·Status (*multiply)(N32_1x32·T*, N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); + N32_1x32·Status (*divide)(N32_1x32·T*, N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); + N32_1x32·Status (*modulus)(N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); + N32_1x32·Status (*shift_left)(Extent, N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); + N32_1x32·Status (*shift_right)(Extent, N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); + N32_1x32·Status (*arithmetic_shift_right)(Extent, N32_1x32·T*, N32_1x32·T*); + + N32_1x32·T* (*access)(N32_1x32·T*, Extent); + void (*from_uint32)(N32_1x32·T *destination ,uint32_t value); + } N32_1x32·Λ; + + Local const N32_1x32·Λ N32_1x32·λ; // initialized in the LOCAL section + +#endif + +//-------------------------------------------------------------------------------- +// Implementation + +#ifdef N32_1x32·IMPLEMENTATION + + // this part goes into the library + #ifndef LOCAL + + #include + #include + + struct N32_1x32·T{ + Digit d0; + }; + + N32_1x32·T N32_1x32·constant[4] = { + {.d0 = 0}, + {.d0 = 1}, + {.d0 = ~(uint32_t)0}, + {.d0 = 1 << 31} + }; + + N32_1x32·T *N32_1x32·zero = &N32_1x32·constant[0]; + N32_1x32·T *N32_1x32·one = &N32_1x32·constant[1]; + N32_1x32·T *N32_1x32·all_one_bit = &N32_1x32·constant[2]; + N32_1x32·T *N32_1x32·msb = &N32_1x32·constant[3]; + N32_1x32·T *N32_1x32·lsb = &N32_1x32·constant[1]; + + // the allocate an array of N32 + N32_1x32·T *N32_1x32·allocate_array(Extent extent ,N32_1x32·Allocate_MemoryFault memory_fault){ + N32_1x32·T *instance = malloc((extent + 1) * sizeof(N32_1x32·T) ); + if(!instance){ + return memory_fault ? memory_fault(extent) : NULL; + } + return instance; + } + + N32_1x32·T *N32_1x32·allocate_array_zero(Extent extent ,N32_1x32·Allocate_MemoryFault memory_fault){ + N32_1x32·T *instance = calloc( extent + 1 ,sizeof(N32_1x32·T) ); + if(!instance){ + return memory_fault ? memory_fault(extent) : NULL; + } + return instance; + } + + void N32_1x32·deallocate(N32_1x32·T *unencumbered){ + free(unencumbered); + } + + #endif + + // This part is included after the library user's code + #ifdef LOCAL + + // instance + + struct N32_1x32·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_1x32·T N32_1x32·t[4]; + + + // allocation + + extern N32_1x32·T *N32_1x32·allocate_array(Extent, N32_1x32·Allocate_MemoryFault); + extern N32_1x32·T *N32_1x32·allocate_array_zero(Extent, N32_1x32·Allocate_MemoryFault); + extern void N32_1x32·deallocate(N32_1x32·T *); + + // so the user can access numbers in an array allocation + Local N32_1x32·T* N32_1x32·access(N32_1x32·T *array ,Extent index){ + return &array[index]; + } + + Local void N32_1x32·from_uint32(N32_1x32·T *destination ,uint32_t value){ + if(destination == NULL) return; + destination->d0 = value; + } + + // copy, convenience copy + + Local void N32_1x32·copy(N32_1x32·T *destination ,N32_1x32·T *source){ + if(source == destination) return; // that was easy! + *destination = *source; + } + + Local void N32_1x32·set_to_zero(N32_1x32·T *instance){ + instance->d0 = 0; + } + + Local void N32_1x32·set_to_one(N32_1x32·T *instance){ + instance->d0 = 1; + } + + // bit operations + + Local void N32_1x32·bit_and(N32_1x32·T *result, N32_1x32·T *a, N32_1x32·T *b){ + result->d0 = a->d0 & b->d0; + } + + // result can be one of the operands + Local void N32_1x32·bit_or(N32_1x32·T *result, N32_1x32·T *a, N32_1x32·T *b){ + result->d0 = a->d0 | b->d0; + } + + // result can the same as the operand + Local void N32_1x32·bit_complement(N32_1x32·T *result, N32_1x32·T *a){ + result->d0 = ~a->d0; + } + + // result can the same as the operand + Local void N32_1x32·bit_twos_complement(N32_1x32·T *result ,N32_1x32·T *a){ + result->d0 = ~a->d0 + 1; + } + + // test functions + + Local N32_1x32·Order N32_1x32·compare(N32_1x32·T *a, N32_1x32·T *b){ + if(a->d0 < b->d0) return N32_1x32·Order_lt; + if(a->d0 > b->d0) return N32_1x32·Order_gt; + return N32_1x32·Order_eq; + } + + Local bool N32_1x32·lt(N32_1x32·T *a ,N32_1x32·T *b){ + return a->d0 < b->d0; + } + + Local bool N32_1x32·gt(N32_1x32·T *a ,N32_1x32·T *b){ + return a->d0 > b->d0; + } + + Local bool N32_1x32·eq(N32_1x32·T *a ,N32_1x32·T *b){ + return a->d0 == b->d0; + } + + Local bool N32_1x32·eq_zero(N32_1x32·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_1x32·Status·accumulator1_overflow + // + // When accumulator1 and accumulator0 point to the same location, the result is the accumulator1 value. + Local N32_1x32·Status N32_1x32·accumulate(N32_1x32·T *accumulator1 ,N32_1x32·T *accumulator0 ,...){ + + va_list args; + va_start(args ,accumulator0); + uint32_t sum = accumulator0->d0; + uint32_t carry = 0; + N32_1x32·T *current; + + while( (current = va_arg(args ,N32_1x32·T *)) ){ + sum += current->d0; + if(sum < current->d0){ // Accumulator1 into carry + (carry)++; + if(carry == 0){ + va_end(args); + return N32_1x32·Status·accumulator1_overflow; + } + } + } + va_end(args); + + // wipes out prior value of accumulator1 + accumulator1->d0 = carry; + + return N32_1x32·Status·ok; + } + + Local N32_1x32·Status N32_1x32·add(N32_1x32·T *sum ,N32_1x32·T *a ,N32_1x32·T *b){ + uint64_t result = (uint64_t)a->d0 + (uint64_t)b->d0; + sum->d0 = (uint32_t)result; + return (result >> 32) ? N32_1x32·Status·carry : N32_1x32·Status·ok; + } + + Local bool N32_1x32·increment(N32_1x32·T *a){ + a->d0++; + return a->d0 == 0; + } + + Local N32_1x32·Status N32_1x32·subtract(N32_1x32·T *difference ,N32_1x32·T *a ,N32_1x32·T *b){ + uint64_t diff = (uint64_t) a->d0 - (uint64_t) b->d0; + difference->d0 = (uint32_t)diff; + return (diff > a->d0) ? N32_1x32·Status·borrow : N32_1x32·Status·ok; + } + + + Local N32_1x32·Status N32_1x32·multiply(N32_1x32·T *product1 ,N32_1x32·T *product0 ,N32_1x32·T *a ,N32_1x32·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_1x32·Status·one_word_product; + return N32_1x32·Status·two_word_product; + } + + Local N32_1x32·Status N32_1x32·divide(N32_1x32·T *remainder ,N32_1x32·T *quotient ,N32_1x32·T *a ,N32_1x32·T *b){ + if(b->d0 == 0) return N32_1x32·Status·undefined_divide_by_zero; + + quotient->d0 = a->d0 / b->d0; + remainder->d0 = a->d0 - (quotient->d0 * b->d0); + + return N32_1x32·Status·ok; + } + + Local N32_1x32·Status N32_1x32·modulus(N32_1x32·T *remainder ,N32_1x32·T *a ,N32_1x32·T *b){ + if(b->d0 == 0) return N32_1x32·Status·undefined_modulus_zero; + uint32_t quotient = a->d0 / b->d0; + remainder->d0 = a->d0 - (quotient * b->d0); + return N32_1x32·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_1x32·Status N32_1x32·shift + ( + uint32_t shift_count + ,N32_1x32·T *spill + ,N32_1x32·T *operand + ,N32_1x32·T *fill + ,ShiftOp shift_op + ,ShiftOp complement_shift_op + ){ + + // If no result is needed, return immediately. + if(operand == NULL && spill == NULL) return N32_1x32·Status·ok; + + // Treat NULL operand as zero. + if(operand == NULL){ + operand = &N32_1x32·t[0]; + N32_1x32·copy(operand, N32_1x32·zero); + } + + // Shifting more than one word breaks our fill/spill model. + if(shift_count > 31) return N32_1x32·Status·gt_max_shift_count; + + // The given operand is still required after it is modified, so we copy it. + N32_1x32·T *given_operand = &N32_1x32·t[1]; + N32_1x32·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_1x32·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_1x32·Status·ok; + } + + // Define concrete shift functions using valid C function pointers + Local N32_1x32·Status + N32_1x32·shift_left(uint32_t shift_count, N32_1x32·T *spill, N32_1x32·T *operand, N32_1x32·T *fill){ + return N32_1x32·shift(shift_count, spill, operand, fill, shift_left_op, shift_right_op); + } + + Local N32_1x32·Status + N32_1x32·shift_right(uint32_t shift_count, N32_1x32·T *spill, N32_1x32·T *operand, N32_1x32·T *fill){ + return N32_1x32·shift(shift_count, spill, operand, fill, shift_right_op, shift_left_op); + } + + Local N32_1x32·Status + N32_1x32·arithmetic_shift_right(uint32_t shift_count, N32_1x32·T *operand, N32_1x32·T *spill){ + + // Guard against excessive shift counts + if(shift_count > 31) return N32_1x32·Status·gt_max_shift_count; + + // A NULL operand is treated as zero + if(operand == NULL){ + operand = &N32_1x32·t[0]; + N32_1x32·copy(operand, N32_1x32·zero); + } + + // Pick the fill value based on the sign bit + N32_1x32·T *fill = (operand->d0 & 0x80000000) ? N32_1x32·all_one_bit : N32_1x32·zero; + + // Call shift_right with the appropriate fill + return N32_1x32·shift_right(shift_count, spill, operand, fill); + } + + Local const N32_1x32·Λ N32_1x32·λ = { + + .allocate_array = N32_1x32·allocate_array + ,.allocate_array_zero = N32_1x32·allocate_array_zero + ,.deallocate = N32_1x32·deallocate + + ,.copy = N32_1x32·copy + ,.bit_and = N32_1x32·bit_and + ,.bit_or = N32_1x32·bit_or + ,.bit_complement = N32_1x32·bit_complement + ,.bit_twos_complement = N32_1x32·bit_twos_complement + ,.compare = N32_1x32·compare + ,.lt = N32_1x32·lt + ,.gt = N32_1x32·gt + ,.eq = N32_1x32·eq + ,.eq_zero = N32_1x32·eq_zero + ,.accumulate = N32_1x32·accumulate + ,.add = N32_1x32·add + ,.increment = N32_1x32·increment + ,.subtract = N32_1x32·subtract + ,.multiply = N32_1x32·multiply + ,.divide = N32_1x32·divide + ,.modulus = N32_1x32·modulus + ,.shift_left = N32_1x32·shift_left + ,.shift_right = N32_1x32·shift_right + ,.arithmetic_shift_right = N32_1x32·arithmetic_shift_right + + ,.access = N32_1x32·access + ,.from_uint32 = N32_1x32·from_uint32 + }; + + #endif + +#endif diff --git a/tester/cc/N64.lib.c b/tester/cc/N64.lib.c new file mode 100644 index 0000000..9d98d58 --- /dev/null +++ b/tester/cc/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/tester/cc/N8.lib.c b/tester/cc/N8.lib.c new file mode 100644 index 0000000..ad52309 --- /dev/null +++ b/tester/cc/N8.lib.c @@ -0,0 +1,433 @@ +/* + N8 - PN = refers to the use of processor native accumulator + + 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/tester/cc/test_N32.cli.c b/tester/cc/test_N32.cli.c new file mode 100644 index 0000000..563711f --- /dev/null +++ b/tester/cc/test_N32.cli.c @@ -0,0 +1,364 @@ +#include +#include +#include +#include + +// Enable interface section +#define FACE +#include "N32.lib.c" +#undef FACE + +// Jump buffer for signal handling +static sigjmp_buf jump_buffer; + +// Signal handler for catching fatal errors +void signal_handler(int signal){ + siglongjmp(jump_buffer ,1); // Jump back to test_head on error +} + +// Test function prototypes +bool test_copy(); +bool test_bitwise_operations(); +bool test_comparisons(); +bool test_arithmetic(); +bool test_shifts(); + +// Test array (null-terminated) +typedef bool (*TestFunction)(); +typedef struct{ + TestFunction function; + const char *name; +}TestEntry; + +TestEntry test_list[] = { + {test_copy ,"test_copy"} + ,{test_bitwise_operations ,"test_bitwise_operations"} + ,{test_comparisons ,"test_comparisons"} + ,{test_arithmetic ,"test_arithmetic"} + ,{test_shifts ,"test_shifts"} + ,{NULL ,NULL} // Null termination +}; + +// The test runner +int test_head(){ + int pass_count = 0; + int fail_count = 0; + + // Set up signal handlers + signal(SIGSEGV ,signal_handler); // Catch segmentation faults + signal(SIGFPE ,signal_handler); // Catch floating point errors + signal(SIGABRT ,signal_handler); // Catch abort() calls + + for(TestEntry *entry = test_list; entry->function != NULL; entry++){ + if( sigsetjmp(jump_buffer ,1) == 0 ){ + // Run the test normally + if( !entry->function() ){ + printf("Failed: %s\n" ,entry->name); + fail_count++; + }else{ + pass_count++; + } + }else{ + // If a signal was caught + printf("Failed due to signaling: %s\n" ,entry->name); + fail_count++; + } + } + + printf("Tests passed: %d\n" ,pass_count); + printf("Tests failed: %d\n" ,fail_count); + return (fail_count == 0) ? 0 : 1; +} + +// Main function +int main(int argc ,char **argv){ + return test_head(); +} + +//------------------------------------------------------------------------------ +// Test Implementations +//------------------------------------------------------------------------------ + +bool test_copy(){ + // Allocate memory + N32·T *array = N32·λ.allocate_array(2 ,NULL); + if( !array ) return false; + + // Access elements via access function + N32·T *a = N32·λ.access(array ,0); + N32·T *b = N32·λ.access(array ,1); + + // Assign value and copy + N32·λ.from_uint32(a ,42); + N32·λ.copy(b ,a); + + bool success = ( N32·λ.compare(b ,a) == N32·Order_eq ); + N32·λ.deallocate(array); + return success; +} + +bool test_arithmetic(){ + // 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); + + N32·λ.from_uint32(a ,20); + N32·λ.from_uint32(b ,22); + + if( N32·λ.add(result ,a ,b) != N32·Status·ok ) return false; + if( N32·λ.compare(result ,N32·λ.access(array ,0)) != N32·Order_gt ) return false; + + if( N32·λ.subtract(result ,b ,a) != N32·Status·ok ) return false; + if( N32·λ.compare(result ,N32·λ.access(array ,0)) != N32·Order_lt ) return false; + + N32·λ.deallocate(array); + 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" +#undef LOCAL diff --git a/tester/cc/test_N32_1x32.cli.c b/tester/cc/test_N32_1x32.cli.c new file mode 100644 index 0000000..2960d43 --- /dev/null +++ b/tester/cc/test_N32_1x32.cli.c @@ -0,0 +1,364 @@ +#include +#include +#include +#include + +// Enable interface section +#define FACE +#include "N32_1x32.lib.c" +#undef FACE + +// Jump buffer for signal handling +static sigjmp_buf jump_buffer; + +// Signal handler for catching fatal errors +void signal_handler(int signal){ + siglongjmp(jump_buffer ,1); // Jump back to test_head on error +} + +// Test function prototypes +bool test_copy(); +bool test_bitwise_operations(); +bool test_comparisons(); +bool test_arithmetic(); +bool test_shifts(); + +// Test array (null-terminated) +typedef bool (*TestFunction)(); +typedef struct{ + TestFunction function; + const char *name; +}TestEntry; + +TestEntry test_list[] = { + {test_copy ,"test_copy"} + ,{test_bitwise_operations ,"test_bitwise_operations"} + ,{test_comparisons ,"test_comparisons"} + ,{test_arithmetic ,"test_arithmetic"} + ,{test_shifts ,"test_shifts"} + ,{NULL ,NULL} // Null termination +}; + +// The test runner +int test_head(){ + int pass_count = 0; + int fail_count = 0; + + // Set up signal handlers + signal(SIGSEGV ,signal_handler); // Catch segmentation faults + signal(SIGFPE ,signal_handler); // Catch floating point errors + signal(SIGABRT ,signal_handler); // Catch abort() calls + + for(TestEntry *entry = test_list; entry->function != NULL; entry++){ + if( sigsetjmp(jump_buffer ,1) == 0 ){ + // Run the test normally + if( !entry->function() ){ + printf("Failed: %s\n" ,entry->name); + fail_count++; + }else{ + pass_count++; + } + }else{ + // If a signal was caught + printf("Failed due to signaling: %s\n" ,entry->name); + fail_count++; + } + } + + printf("Tests passed: %d\n" ,pass_count); + printf("Tests failed: %d\n" ,fail_count); + return (fail_count == 0) ? 0 : 1; +} + +// Main function +int main(int argc ,char **argv){ + return test_head(); +} + +//------------------------------------------------------------------------------ +// Test Implementations +//------------------------------------------------------------------------------ + +bool test_copy(){ + // Allocate memory + N32_1x32·T *array = N32_1x32·λ.allocate_array(2 ,NULL); + if( !array ) return false; + + // Access elements via access function + N32_1x32·T *a = N32_1x32·λ.access(array ,0); + N32_1x32·T *b = N32_1x32·λ.access(array ,1); + + // Assign value and copy + N32_1x32·λ.from_uint32(a ,42); + N32_1x32·λ.copy(b ,a); + + bool success = ( N32_1x32·λ.compare(b ,a) == N32_1x32·Order_eq ); + N32_1x32·λ.deallocate(array); + return success; +} + +bool test_arithmetic(){ + // Allocate memory + N32_1x32·T *array = N32_1x32·λ.allocate_array(3 ,NULL); + if( !array ) return false; + + N32_1x32·T *a = N32_1x32·λ.access(array ,0); + N32_1x32·T *b = N32_1x32·λ.access(array ,1); + N32_1x32·T *result = N32_1x32·λ.access(array ,2); + + N32_1x32·λ.from_uint32(a ,20); + N32_1x32·λ.from_uint32(b ,22); + + if( N32_1x32·λ.add(result ,a ,b) != N32_1x32·Status·ok ) return false; + if( N32_1x32·λ.compare(result ,N32_1x32·λ.access(array ,0)) != N32_1x32·Order_gt ) return false; + + if( N32_1x32·λ.subtract(result ,b ,a) != N32_1x32·Status·ok ) return false; + if( N32_1x32·λ.compare(result ,N32_1x32·λ.access(array ,0)) != N32_1x32·Order_lt ) return false; + + N32_1x32·λ.deallocate(array); + return true; +} + +bool test_bitwise_operations(){ + // Allocate memory + N32_1x32·T *array = N32_1x32·λ.allocate_array(3, NULL); + if(!array) return false; + + N32_1x32·T *a = N32_1x32·λ.access(array, 0); + N32_1x32·T *b = N32_1x32·λ.access(array, 1); + N32_1x32·T *result = N32_1x32·λ.access(array, 2); + + // a = 0x0F0F0F0F, b = 0xF0F0F0F0 + N32_1x32·λ.from_uint32(a, 0x0F0F0F0F); + N32_1x32·λ.from_uint32(b, 0xF0F0F0F0); + + // bit_and => expect 0x00000000 + N32_1x32·λ.bit_and(result, a, b); + N32_1x32·λ.from_uint32(a, 0x00000000); + if(N32_1x32·λ.compare(result, a) != N32_1x32·Order_eq){ + N32_1x32·λ.deallocate(array); + return false; + } + + // Reset a to 0x0F0F0F0F for next tests + N32_1x32·λ.from_uint32(a, 0x0F0F0F0F); + + // bit_or => expect 0xFFFFFFFF + N32_1x32·λ.bit_or(result, a, b); + N32_1x32·λ.from_uint32(b, 0xFFFFFFFF); + if(N32_1x32·λ.compare(result, b) != N32_1x32·Order_eq){ + N32_1x32·λ.deallocate(array); + return false; + } + + // bit_complement(a=0x0F0F0F0F) => expect 0xF0F0F0F0 + N32_1x32·λ.from_uint32(a, 0x0F0F0F0F); + N32_1x32·λ.bit_complement(result, a); + N32_1x32·λ.from_uint32(b, 0xF0F0F0F0); + if(N32_1x32·λ.compare(result, b) != N32_1x32·Order_eq){ + N32_1x32·λ.deallocate(array); + return false; + } + + // bit_twos_complement(a=0x0F0F0F0F) => expect 0xF0F0F0F1 + N32_1x32·λ.from_uint32(a, 0x0F0F0F0F); + N32_1x32·λ.bit_twos_complement(result, a); + N32_1x32·λ.from_uint32(b, 0xF0F0F0F1); + if(N32_1x32·λ.compare(result, b) != N32_1x32·Order_eq){ + N32_1x32·λ.deallocate(array); + return false; + } + + N32_1x32·λ.deallocate(array); + return true; +} + +bool test_comparisons(){ + // Allocate memory + N32_1x32·T *array = N32_1x32·λ.allocate_array(3, NULL); + if(!array) return false; + + N32_1x32·T *a = N32_1x32·λ.access(array, 0); + N32_1x32·T *b = N32_1x32·λ.access(array, 1); + N32_1x32·T *c = N32_1x32·λ.access(array, 2); + + // First set: a=0, b=42, c=42 + N32_1x32·λ.from_uint32(a, 0); + N32_1x32·λ.from_uint32(b, 42); + N32_1x32·λ.from_uint32(c, 42); + + // eq_zero(a) => true + if(!N32_1x32·λ.eq_zero(a)){ + N32_1x32·λ.deallocate(array); + return false; + } + // eq_zero(b) => false + if(N32_1x32·λ.eq_zero(b)){ + N32_1x32·λ.deallocate(array); + return false; + } + // eq(b, c) => true + if(!N32_1x32·λ.eq(b, c)){ + N32_1x32·λ.deallocate(array); + return false; + } + // eq(a, b) => false + if(N32_1x32·λ.eq(a, b)){ + N32_1x32·λ.deallocate(array); + return false; + } + // compare(a, b) => N32_1x32·Order_lt + if(N32_1x32·λ.compare(a, b) != N32_1x32·Order_lt){ + N32_1x32·λ.deallocate(array); + return false; + } + // compare(b, a) => N32_1x32·Order_gt + if(N32_1x32·λ.compare(b, a) != N32_1x32·Order_gt){ + N32_1x32·λ.deallocate(array); + return false; + } + // compare(b, c) => N32_1x32·Order_eq + if(N32_1x32·λ.compare(b, c) != N32_1x32·Order_eq){ + N32_1x32·λ.deallocate(array); + return false; + } + // lt(a, b) => true, gt(b, a) => true + if(!N32_1x32·λ.lt(a, b) || !N32_1x32·λ.gt(b, a)){ + N32_1x32·λ.deallocate(array); + return false; + } + + // Second set: a=100, b=50 + N32_1x32·λ.from_uint32(a, 100); + N32_1x32·λ.from_uint32(b, 50); + if(N32_1x32·λ.compare(a, b) != N32_1x32·Order_gt){ + N32_1x32·λ.deallocate(array); + return false; + } + // eq_zero(a) => false + if(N32_1x32·λ.eq_zero(a)){ + N32_1x32·λ.deallocate(array); + return false; + } + // eq_zero(b) => false + if(N32_1x32·λ.eq_zero(b)){ + N32_1x32·λ.deallocate(array); + return false; + } + + N32_1x32·λ.deallocate(array); + return true; +} + +bool test_shifts(){ + // Allocate memory for operand, fill, spill + N32_1x32·T *array = N32_1x32·λ.allocate_array(3, NULL); + if(!array) return false; + + N32_1x32·T *operand = N32_1x32·λ.access(array, 0); + N32_1x32·T *fill = N32_1x32·λ.access(array, 1); + N32_1x32·T *spill = N32_1x32·λ.access(array, 2); + + // Subtest A: shift_left(4) with operand=1 => expect operand=16, fill=0, spill=0 + N32_1x32·λ.from_uint32(operand, 1); + N32_1x32·λ.from_uint32(fill, 0); + N32_1x32·λ.from_uint32(spill, 0); + if(N32_1x32·λ.shift_left(4, spill, operand, fill) != N32_1x32·Status·ok){ + N32_1x32·λ.deallocate(array); + return false; + } + N32_1x32·T *temp = N32_1x32·λ.allocate_array(1, NULL); + if(!temp){ + N32_1x32·λ.deallocate(array); + return false; + } + N32_1x32·λ.from_uint32(temp, 16); + if(N32_1x32·λ.compare(operand, temp) != N32_1x32·Order_eq){ + N32_1x32·λ.deallocate(temp); + N32_1x32·λ.deallocate(array); + return false; + } + if(N32_1x32·λ.compare(fill, N32_1x32·zero) != N32_1x32·Order_eq){ + N32_1x32·λ.deallocate(temp); + N32_1x32·λ.deallocate(array); + return false; + } + if(N32_1x32·λ.compare(spill, N32_1x32·zero) != N32_1x32·Order_eq){ + N32_1x32·λ.deallocate(temp); + N32_1x32·λ.deallocate(array); + return false; + } + + // Subtest B: shift_left(1) with operand=0x80000000 => expect operand=0, spill=1 + N32_1x32·λ.from_uint32(operand, 0x80000000); + N32_1x32·λ.from_uint32(fill, 0); + N32_1x32·λ.from_uint32(spill, 0); + if(N32_1x32·λ.shift_left(1, spill, operand, fill) != N32_1x32·Status·ok){ + N32_1x32·λ.deallocate(temp); + N32_1x32·λ.deallocate(array); + return false; + } + if(!N32_1x32·λ.eq_zero(operand)){ + N32_1x32·λ.deallocate(temp); + N32_1x32·λ.deallocate(array); + return false; + } + N32_1x32·λ.from_uint32(temp, 1); + if(N32_1x32·λ.compare(spill, temp) != N32_1x32·Order_eq){ + N32_1x32·λ.deallocate(temp); + N32_1x32·λ.deallocate(array); + return false; + } + + // Subtest C: shift_right(1) with operand=0x80000000 => expect operand=0x40000000, spill=0 + N32_1x32·λ.from_uint32(operand, 0x80000000); + N32_1x32·λ.from_uint32(fill, 0); + N32_1x32·λ.from_uint32(spill, 0); + if(N32_1x32·λ.shift_right(1, spill, operand, fill) != N32_1x32·Status·ok){ + N32_1x32·λ.deallocate(temp); + N32_1x32·λ.deallocate(array); + return false; + } + N32_1x32·λ.from_uint32(temp, 0x40000000); + if(N32_1x32·λ.compare(operand, temp) != N32_1x32·Order_eq){ + N32_1x32·λ.deallocate(temp); + N32_1x32·λ.deallocate(array); + return false; + } + if(!N32_1x32·λ.eq_zero(spill)){ + N32_1x32·λ.deallocate(temp); + N32_1x32·λ.deallocate(array); + return false; + } + + // Subtest D: arithmetic_shift_right(1) with operand=0x80000000 => expect operand=0xC0000000, spill=0 + N32_1x32·λ.from_uint32(operand, 0x80000000); + N32_1x32·λ.from_uint32(spill, 0); + if(N32_1x32·λ.arithmetic_shift_right(1, operand, spill) != N32_1x32·Status·ok){ + N32_1x32·λ.deallocate(temp); + N32_1x32·λ.deallocate(array); + return false; + } + N32_1x32·λ.from_uint32(temp, 0xC0000000); + if(N32_1x32·λ.compare(operand, temp) != N32_1x32·Order_eq){ + N32_1x32·λ.deallocate(temp); + N32_1x32·λ.deallocate(array); + return false; + } + if(!N32_1x32·λ.eq_zero(spill)){ + N32_1x32·λ.deallocate(temp); + N32_1x32·λ.deallocate(array); + return false; + } + + N32_1x32·λ.deallocate(temp); + N32_1x32·λ.deallocate(array); + return true; +} + + + +// Include the local section of N32_1x32.lib.c for testing +#define LOCAL +#include "N32_1x32.lib.c" +#undef LOCAL diff --git a/tester/cc/test_N32_4x8.cli.c b/tester/cc/test_N32_4x8.cli.c new file mode 100644 index 0000000..c540478 --- /dev/null +++ b/tester/cc/test_N32_4x8.cli.c @@ -0,0 +1,364 @@ +#include +#include +#include +#include + +// Enable interface section +#define FACE +#include "N32_4x8.lib.c" +#undef FACE + +// Jump buffer for signal handling +static sigjmp_buf jump_buffer; + +// Signal handler for catching fatal errors +void signal_handler(int signal){ + siglongjmp(jump_buffer ,1); // Jump back to test_head on error +} + +// Test function prototypes +bool test_copy(); +bool test_bitwise_operations(); +bool test_comparisons(); +bool test_arithmetic(); +bool test_shifts(); + +// Test array (null-terminated) +typedef bool (*TestFunction)(); +typedef struct{ + TestFunction function; + const char *name; +}TestEntry; + +TestEntry test_list[] = { + {test_copy ,"test_copy"} + ,{test_bitwise_operations ,"test_bitwise_operations"} + ,{test_comparisons ,"test_comparisons"} + ,{test_arithmetic ,"test_arithmetic"} + ,{test_shifts ,"test_shifts"} + ,{NULL ,NULL} // Null termination +}; + +// The test runner +int test_head(){ + int pass_count = 0; + int fail_count = 0; + + // Set up signal handlers + signal(SIGSEGV ,signal_handler); // Catch segmentation faults + signal(SIGFPE ,signal_handler); // Catch floating point errors + signal(SIGABRT ,signal_handler); // Catch abort() calls + + for(TestEntry *entry = test_list; entry->function != NULL; entry++){ + if( sigsetjmp(jump_buffer ,1) == 0 ){ + // Run the test normally + if( !entry->function() ){ + printf("Failed: %s\n" ,entry->name); + fail_count++; + }else{ + pass_count++; + } + }else{ + // If a signal was caught + printf("Failed due to signaling: %s\n" ,entry->name); + fail_count++; + } + } + + printf("Tests passed: %d\n" ,pass_count); + printf("Tests failed: %d\n" ,fail_count); + return (fail_count == 0) ? 0 : 1; +} + +// Main function +int main(int argc ,char **argv){ + return test_head(); +} + +//------------------------------------------------------------------------------ +// Test Implementations +//------------------------------------------------------------------------------ + +bool test_copy(){ + // Allocate memory + N32_4x8·T *array = N32_4x8·λ.allocate_array(2 ,NULL); + if( !array ) return false; + + // Access elements via access function + N32_4x8·T *a = N32_4x8·λ.access(array ,0); + N32_4x8·T *b = N32_4x8·λ.access(array ,1); + + // Assign value and copy + N32_4x8·λ.from_uint32(a ,42); + N32_4x8·λ.copy(b ,a); + + bool success = ( N32_4x8·λ.compare(b ,a) == N32_4x8·Order_eq ); + N32_4x8·λ.deallocate(array); + return success; +} + +bool test_arithmetic(){ + // Allocate memory + N32_4x8·T *array = N32_4x8·λ.allocate_array(3 ,NULL); + if( !array ) return false; + + N32_4x8·T *a = N32_4x8·λ.access(array ,0); + N32_4x8·T *b = N32_4x8·λ.access(array ,1); + N32_4x8·T *result = N32_4x8·λ.access(array ,2); + + N32_4x8·λ.from_uint32(a ,20); + N32_4x8·λ.from_uint32(b ,22); + + if( N32_4x8·λ.add(result ,a ,b) != N32_4x8·Status·ok ) return false; + if( N32_4x8·λ.compare(result ,N32_4x8·λ.access(array ,0)) != N32_4x8·Order_gt ) return false; + + if( N32_4x8·λ.subtract(result ,b ,a) != N32_4x8·Status·ok ) return false; + if( N32_4x8·λ.compare(result ,N32_4x8·λ.access(array ,0)) != N32_4x8·Order_lt ) return false; + + N32_4x8·λ.deallocate(array); + return true; +} + +bool test_bitwise_operations(){ + // Allocate memory + N32_4x8·T *array = N32_4x8·λ.allocate_array(3, NULL); + if(!array) return false; + + N32_4x8·T *a = N32_4x8·λ.access(array, 0); + N32_4x8·T *b = N32_4x8·λ.access(array, 1); + N32_4x8·T *result = N32_4x8·λ.access(array, 2); + + // a = 0x0F0F0F0F, b = 0xF0F0F0F0 + N32_4x8·λ.from_uint32(a, 0x0F0F0F0F); + N32_4x8·λ.from_uint32(b, 0xF0F0F0F0); + + // bit_and => expect 0x00000000 + N32_4x8·λ.bit_and(result, a, b); + N32_4x8·λ.from_uint32(a, 0x00000000); + if(N32_4x8·λ.compare(result, a) != N32_4x8·Order_eq){ + N32_4x8·λ.deallocate(array); + return false; + } + + // Reset a to 0x0F0F0F0F for next tests + N32_4x8·λ.from_uint32(a, 0x0F0F0F0F); + + // bit_or => expect 0xFFFFFFFF + N32_4x8·λ.bit_or(result, a, b); + N32_4x8·λ.from_uint32(b, 0xFFFFFFFF); + if(N32_4x8·λ.compare(result, b) != N32_4x8·Order_eq){ + N32_4x8·λ.deallocate(array); + return false; + } + + // bit_complement(a=0x0F0F0F0F) => expect 0xF0F0F0F0 + N32_4x8·λ.from_uint32(a, 0x0F0F0F0F); + N32_4x8·λ.bit_complement(result, a); + N32_4x8·λ.from_uint32(b, 0xF0F0F0F0); + if(N32_4x8·λ.compare(result, b) != N32_4x8·Order_eq){ + N32_4x8·λ.deallocate(array); + return false; + } + + // bit_twos_complement(a=0x0F0F0F0F) => expect 0xF0F0F0F1 + N32_4x8·λ.from_uint32(a, 0x0F0F0F0F); + N32_4x8·λ.bit_twos_complement(result, a); + N32_4x8·λ.from_uint32(b, 0xF0F0F0F1); + if(N32_4x8·λ.compare(result, b) != N32_4x8·Order_eq){ + N32_4x8·λ.deallocate(array); + return false; + } + + N32_4x8·λ.deallocate(array); + return true; +} + +bool test_comparisons(){ + // Allocate memory + N32_4x8·T *array = N32_4x8·λ.allocate_array(3, NULL); + if(!array) return false; + + N32_4x8·T *a = N32_4x8·λ.access(array, 0); + N32_4x8·T *b = N32_4x8·λ.access(array, 1); + N32_4x8·T *c = N32_4x8·λ.access(array, 2); + + // First set: a=0, b=42, c=42 + N32_4x8·λ.from_uint32(a, 0); + N32_4x8·λ.from_uint32(b, 42); + N32_4x8·λ.from_uint32(c, 42); + + // eq_zero(a) => true + if(!N32_4x8·λ.eq_zero(a)){ + N32_4x8·λ.deallocate(array); + return false; + } + // eq_zero(b) => false + if(N32_4x8·λ.eq_zero(b)){ + N32_4x8·λ.deallocate(array); + return false; + } + // eq(b, c) => true + if(!N32_4x8·λ.eq(b, c)){ + N32_4x8·λ.deallocate(array); + return false; + } + // eq(a, b) => false + if(N32_4x8·λ.eq(a, b)){ + N32_4x8·λ.deallocate(array); + return false; + } + // compare(a, b) => N32_4x8·Order_lt + if(N32_4x8·λ.compare(a, b) != N32_4x8·Order_lt){ + N32_4x8·λ.deallocate(array); + return false; + } + // compare(b, a) => N32_4x8·Order_gt + if(N32_4x8·λ.compare(b, a) != N32_4x8·Order_gt){ + N32_4x8·λ.deallocate(array); + return false; + } + // compare(b, c) => N32_4x8·Order_eq + if(N32_4x8·λ.compare(b, c) != N32_4x8·Order_eq){ + N32_4x8·λ.deallocate(array); + return false; + } + // lt(a, b) => true, gt(b, a) => true + if(!N32_4x8·λ.lt(a, b) || !N32_4x8·λ.gt(b, a)){ + N32_4x8·λ.deallocate(array); + return false; + } + + // Second set: a=100, b=50 + N32_4x8·λ.from_uint32(a, 100); + N32_4x8·λ.from_uint32(b, 50); + if(N32_4x8·λ.compare(a, b) != N32_4x8·Order_gt){ + N32_4x8·λ.deallocate(array); + return false; + } + // eq_zero(a) => false + if(N32_4x8·λ.eq_zero(a)){ + N32_4x8·λ.deallocate(array); + return false; + } + // eq_zero(b) => false + if(N32_4x8·λ.eq_zero(b)){ + N32_4x8·λ.deallocate(array); + return false; + } + + N32_4x8·λ.deallocate(array); + return true; +} + +bool test_shifts(){ + // Allocate memory for operand, fill, spill + N32_4x8·T *array = N32_4x8·λ.allocate_array(3, NULL); + if(!array) return false; + + N32_4x8·T *operand = N32_4x8·λ.access(array, 0); + N32_4x8·T *fill = N32_4x8·λ.access(array, 1); + N32_4x8·T *spill = N32_4x8·λ.access(array, 2); + + // Subtest A: shift_left(4) with operand=1 => expect operand=16, fill=0, spill=0 + N32_4x8·λ.from_uint32(operand, 1); + N32_4x8·λ.from_uint32(fill, 0); + N32_4x8·λ.from_uint32(spill, 0); + if(N32_4x8·λ.shift_left(4, spill, operand, fill) != N32_4x8·Status·ok){ + N32_4x8·λ.deallocate(array); + return false; + } + N32_4x8·T *temp = N32_4x8·λ.allocate_array(1, NULL); + if(!temp){ + N32_4x8·λ.deallocate(array); + return false; + } + N32_4x8·λ.from_uint32(temp, 16); + if(N32_4x8·λ.compare(operand, temp) != N32_4x8·Order_eq){ + N32_4x8·λ.deallocate(temp); + N32_4x8·λ.deallocate(array); + return false; + } + if(N32_4x8·λ.compare(fill, N32_4x8·zero) != N32_4x8·Order_eq){ + N32_4x8·λ.deallocate(temp); + N32_4x8·λ.deallocate(array); + return false; + } + if(N32_4x8·λ.compare(spill, N32_4x8·zero) != N32_4x8·Order_eq){ + N32_4x8·λ.deallocate(temp); + N32_4x8·λ.deallocate(array); + return false; + } + + // Subtest B: shift_left(1) with operand=0x80000000 => expect operand=0, spill=1 + N32_4x8·λ.from_uint32(operand, 0x80000000); + N32_4x8·λ.from_uint32(fill, 0); + N32_4x8·λ.from_uint32(spill, 0); + if(N32_4x8·λ.shift_left(1, spill, operand, fill) != N32_4x8·Status·ok){ + N32_4x8·λ.deallocate(temp); + N32_4x8·λ.deallocate(array); + return false; + } + if(!N32_4x8·λ.eq_zero(operand)){ + N32_4x8·λ.deallocate(temp); + N32_4x8·λ.deallocate(array); + return false; + } + N32_4x8·λ.from_uint32(temp, 1); + if(N32_4x8·λ.compare(spill, temp) != N32_4x8·Order_eq){ + N32_4x8·λ.deallocate(temp); + N32_4x8·λ.deallocate(array); + return false; + } + + // Subtest C: shift_right(1) with operand=0x80000000 => expect operand=0x40000000, spill=0 + N32_4x8·λ.from_uint32(operand, 0x80000000); + N32_4x8·λ.from_uint32(fill, 0); + N32_4x8·λ.from_uint32(spill, 0); + if(N32_4x8·λ.shift_right(1, spill, operand, fill) != N32_4x8·Status·ok){ + N32_4x8·λ.deallocate(temp); + N32_4x8·λ.deallocate(array); + return false; + } + N32_4x8·λ.from_uint32(temp, 0x40000000); + if(N32_4x8·λ.compare(operand, temp) != N32_4x8·Order_eq){ + N32_4x8·λ.deallocate(temp); + N32_4x8·λ.deallocate(array); + return false; + } + if(!N32_4x8·λ.eq_zero(spill)){ + N32_4x8·λ.deallocate(temp); + N32_4x8·λ.deallocate(array); + return false; + } + + // Subtest D: arithmetic_shift_right(1) with operand=0x80000000 => expect operand=0xC0000000, spill=0 + N32_4x8·λ.from_uint32(operand, 0x80000000); + N32_4x8·λ.from_uint32(spill, 0); + if(N32_4x8·λ.arithmetic_shift_right(1, operand, spill) != N32_4x8·Status·ok){ + N32_4x8·λ.deallocate(temp); + N32_4x8·λ.deallocate(array); + return false; + } + N32_4x8·λ.from_uint32(temp, 0xC0000000); + if(N32_4x8·λ.compare(operand, temp) != N32_4x8·Order_eq){ + N32_4x8·λ.deallocate(temp); + N32_4x8·λ.deallocate(array); + return false; + } + if(!N32_4x8·λ.eq_zero(spill)){ + N32_4x8·λ.deallocate(temp); + N32_4x8·λ.deallocate(array); + return false; + } + + N32_4x8·λ.deallocate(temp); + N32_4x8·λ.deallocate(array); + return true; +} + + + +// Include the local section of N32_4x8.lib.c for testing +#define LOCAL +#include "N32_4x8.lib.c" +#undef LOCAL diff --git a/tester/python/fill_template b/tester/python/fill_template index 573f570..f94a8f2 100755 --- a/tester/python/fill_template +++ b/tester/python/fill_template @@ -15,16 +15,25 @@ def main(): generates `.c` source code from templates """ - # base line test N32 test - type_name = "N32PN" + # N32 processor native + type_name = "N32" code = test_N32(namespace = type_name) write(code ,type_name); - # a single digit N32 - type_name = "N32" + # N32 1 digit of 32 bits + type_name = "N32_1x32" code = test_N32(namespace = type_name) write(code ,type_name); + # N32 2 digits of 16 bits + # type_name = "N32_4x8" + # code = test_N32(namespace = type_name) + # write(code ,type_name); + + # N32 4 digits of 8 bits + type_name = "N32_4x8" + code = test_N32(namespace = type_name) + write(code ,type_name); if __name__ == "__main__": diff --git "a/tester/tool\360\237\226\211/make" "b/tester/tool\360\237\226\211/make" index ed0e7c5..f0949f7 100755 --- "a/tester/tool\360\237\226\211/make" +++ "b/tester/tool\360\237\226\211/make" @@ -14,6 +14,10 @@ set -x cd "$REPO_HOME"/tester || exit 1 + pushd python + ./fill_template + popd + /bin/make -f tool🖉/makefile $@ set +x