#*#
*~
+__pycache__
a.out
.gradle/
--- /dev/null
+/*
+ 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 <stdint.h>
+ #include <stdbool.h>
+ #include <stdarg.h>
+ #include <stdlib.h>
+
+ //----------------------------------------
+ // 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 <stdarg.h>
+ #include <stdlib.h>
+
+ 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
+++ /dev/null
-/*
- 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 N16n·DEBUG
-
-#ifndef FACE
-#define N16n·IMPLEMENTATION
-#define FACE
-#endif
-
-//--------------------------------------------------------------------------------
-// Interface
-
-#ifndef N16n·FACE
-#define N16n·FACE
-
- #include <stdint.h>
- #include <stdbool.h>
- #include <stdarg.h>
- #include <stdlib.h>
-
- //----------------------------------------
- // Instance Data (Declaration Only)
-
- typedef uint16_t Extent;
- typedef uint16_t Digit;
-
- typedef struct N16n·T N16n·T;
-
- extern N16n·T *N16n·zero;
- extern N16n·T *N16n·one;
- extern N16n·T *N16n·all_one_bit;
- extern N16n·T *N16n·lsb;
- extern N16n·T *N16n·msb;
-
- //----------------------------------------
- // Return/Error Status and handlers
-
- typedef enum{
- N16n·Status·ok = 0
- ,N16n·Status·overflow = 1
- ,N16n·Status·accumulator1_overflow = 2
- ,N16n·Status·carry = 3
- ,N16n·Status·borrow = 4
- ,N16n·Status·undefined_divide_by_zero = 5
- ,N16n·Status·undefined_modulus_zero = 6
- ,N16n·Status·gt_max_shift_count = 7
- ,N16n·Status·spill_eq_operand = 8 // not currently signaled, result will be spill value
- ,N16n·Status·one_word_product = 9
- ,N16n·Status·two_word_product = 10
- } N16n·Status;
-
- typedef enum{
- N16n·Order_lt = -1
- ,N16n·Order_eq = 0
- ,N16n·Order_gt = 1
- } N16n·Order;
-
- typedef N16n·T *( *N16n·Allocate_MemoryFault )(Extent);
-
- //----------------------------------------
- // Interface
-
- typedef struct{
-
- N16n·T *(*allocate_array_zero)(Extent, N16n·Allocate_MemoryFault);
- N16n·T *(*allocate_array)(Extent, N16n·Allocate_MemoryFault);
- void (*deallocate)(N16n·T*);
-
- void (*copy)(N16n·T*, N16n·T*);
- void (*bit_and)(N16n·T*, N16n·T*, N16n·T*);
- void (*bit_or)(N16n·T*, N16n·T*, N16n·T*);
- void (*bit_complement)(N16n·T*, N16n·T*);
- void (*bit_twos_complement)(N16n·T*, N16n·T*);
- N16n·Order (*compare)(N16n·T*, N16n·T*);
- bool (*lt)(N16n·T*, N16n·T*);
- bool (*gt)(N16n·T*, N16n·T*);
- bool (*eq)(N16n·T*, N16n·T*);
- bool (*eq_zero)(N16n·T*);
- N16n·Status (*accumulate)(N16n·T *accumulator1 ,N16n·T *accumulator0 ,...);
- N16n·Status (*add)(N16n·T*, N16n·T*, N16n·T*);
- bool (*increment)(N16n·T *a);
- N16n·Status (*subtract)(N16n·T*, N16n·T*, N16n·T*);
- N16n·Status (*multiply)(N16n·T*, N16n·T*, N16n·T*, N16n·T*);
- N16n·Status (*divide)(N16n·T*, N16n·T*, N16n·T*, N16n·T*);
- N16n·Status (*modulus)(N16n·T*, N16n·T*, N16n·T*);
- N16n·Status (*shift_left)(Extent, N16n·T*, N16n·T*, N16n·T*);
- N16n·Status (*shift_right)(Extent, N16n·T*, N16n·T*, N16n·T*);
- N16n·Status (*arithmetic_shift_right)(Extent, N16n·T*, N16n·T*);
-
- N16n·T* (*access)(N16n·T*, Extent);
- void (*from_uint32)(N16n·T *destination ,uint32_t value);
- } N16n·Λ;
-
- Local const N16n·Λ N16n·λ; // initialized in the LOCAL section
-
-#endif
-
-//--------------------------------------------------------------------------------
-// Implementation
-
-#ifdef N16n·IMPLEMENTATION
-
- // this part goes into the library
- #ifndef LOCAL
-
- #include <stdarg.h>
- #include <stdlib.h>
-
- struct N16n·T{
- Digit d0;
- };
-
- N16n·T N16n·constant[4] = {
- {.d0 = 0},
- {.d0 = 1},
- {.d0 = ~(uint16_t)0},
- {.d0 = 1 << 15}
- };
-
- N16n·T *N16n·zero = &N16n·constant[0];
- N16n·T *N16n·one = &N16n·constant[1];
- N16n·T *N16n·all_one_bit = &N16n·constant[2];
- N16n·T *N16n·msb = &N16n·constant[3];
- N16n·T *N16n·lsb = &N16n·constant[1];
-
- // the allocate an array of N16
- N16n·T *N16n·allocate_array(Extent extent ,N16n·Allocate_MemoryFault memory_fault){
- N16n·T *instance = malloc((extent + 1) * sizeof(N16n·T));
- if(!instance){
- return memory_fault ? memory_fault(extent) : NULL;
- }
- return instance;
- }
-
- N16n·T *N16n·allocate_array_zero(Extent extent ,N16n·Allocate_MemoryFault memory_fault){
- N16n·T *instance = calloc(extent + 1, sizeof(N16n·T));
- if(!instance){
- return memory_fault ? memory_fault(extent) : NULL;
- }
- return instance;
- }
-
- void N16n·deallocate(N16n·T *unencumbered){
- free(unencumbered);
- }
-
-
- #endif
-
- // This part is included after the library user's code
- #ifdef LOCAL
-
- // instance
-
- struct N16n·T{
- Digit d0;
- };
-
- // temporary variables
- Local N16n·T N16n·t[4];
-
- // allocation
-
- extern N16n·T *N16n·allocate_array(Extent, N16n·Allocate_MemoryFault);
- extern N16n·T *N16n·allocate_array_zero(Extent, N16n·Allocate_MemoryFault);
- extern void N16n·deallocate(N16n·T *);
-
- // so the user can access numbers in an array allocation
- Local N16n·T* N16n·access(N16n·T *array ,Extent index){
- return &array[index];
- }
-
- Local void N16n·from_uint32(N16n·T *destination ,uint32_t value){
- if(destination == NULL) return;
- destination->d0 = (uint16_t)(value & 0xFFFF);
- }
-
- // copy, convenience copy
-
- Local void N16n·copy(N16n·T *destination ,N16n·T *source){
- if(source == destination) return;
- *destination = *source;
- }
-
- Local void N16n·set_to_zero(N16n·T *instance){
- instance->d0 = 0;
- }
-
- Local void N16n·set_to_one(N16n·T *instance){
- instance->d0 = 1;
- }
-
- // bit operations
-
- Local void N16n·bit_and(N16n·T *result, N16n·T *a, N16n·T *b){
- result->d0 = a->d0 & b->d0;
- }
-
- Local void N16n·bit_or(N16n·T *result, N16n·T *a, N16n·T *b){
- result->d0 = a->d0 | b->d0;
- }
-
- Local void N16n·bit_complement(N16n·T *result, N16n·T *a){
- result->d0 = ~a->d0;
- }
-
- Local void N16n·bit_twos_complement(N16n·T *result ,N16n·T *a){
- result->d0 = (uint16_t)(~a->d0 + 1);
- }
-
- // test functions
-
- Local N16n·Order N16n·compare(N16n·T *a, N16n·T *b){
- if(a->d0 < b->d0) return N16n·Order_lt;
- if(a->d0 > b->d0) return N16n·Order_gt;
- return N16n·Order_eq;
- }
-
- Local bool N16n·lt(N16n·T *a ,N16n·T *b){
- return a->d0 < b->d0;
- }
-
- Local bool N16n·gt(N16n·T *a ,N16n·T *b){
- return a->d0 > b->d0;
- }
-
- Local bool N16n·eq(N16n·T *a ,N16n·T *b){
- return a->d0 == b->d0;
- }
-
- Local bool N16n·eq_zero(N16n·T *a){
- return a->d0 == 0;
- }
-
- // arithmetic operations
-
- Local N16n·Status N16n·accumulate(N16n·T *accumulator1 ,N16n·T *accumulator0 ,...){
-
- va_list args;
- va_start(args ,accumulator0);
- uint32_t sum = accumulator0->d0;
- uint32_t carry = 0;
- N16n·T *current;
-
- while( (current = va_arg(args ,N16n·T*)) ){
- sum += current->d0;
- if(sum < current->d0){
- (carry)++;
- if(carry == 0){
- va_end(args);
- return N16n·Status·accumulator1_overflow;
- }
- }
- }
- va_end(args);
-
- accumulator1->d0 = (uint16_t)carry;
- return N16n·Status·ok;
- }
-
- Local N16n·Status N16n·add(N16n·T *sum ,N16n·T *a ,N16n·T *b){
- uint32_t result = (uint32_t)a->d0 + (uint32_t)b->d0;
- sum->d0 = (uint16_t)(result & 0xFFFF);
- return (result >> 16) ? N16n·Status·carry : N16n·Status·ok;
- }
-
- Local bool N16n·increment(N16n·T *a){
- a->d0++;
- return (a->d0 == 0);
- }
-
- Local N16n·Status N16n·subtract(N16n·T *difference ,N16n·T *a ,N16n·T *b){
- uint32_t diff = (uint32_t)a->d0 - (uint32_t)b->d0;
- difference->d0 = (uint16_t)(diff & 0xFFFF);
- return (diff > a->d0) ? N16n·Status·borrow : N16n·Status·ok;
- }
-
- Local N16n·Status N16n·multiply(N16n·T *product1 ,N16n·T *product0 ,N16n·T *a ,N16n·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 N16n·Status·one_word_product;
- return N16n·Status·two_word_product;
- }
-
- Local N16n·Status N16n·divide(N16n·T *remainder ,N16n·T *quotient ,N16n·T *a ,N16n·T *b){
- if(b->d0 == 0) return N16n·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 N16n·Status·ok;
- }
-
- Local N16n·Status N16n·modulus(N16n·T *remainder ,N16n·T *a ,N16n·T *b){
- if(b->d0 == 0) return N16n·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 N16n·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 N16n·Status N16n·shift
- (
- uint16_t shift_count
- ,N16n·T *spill
- ,N16n·T *operand
- ,N16n·T *fill
- ,ShiftOp shift_op
- ,ShiftOp complement_shift_op
- ){
-
- if(operand == NULL && spill == NULL) return N16n·Status·ok;
-
- if(operand == NULL){
- operand = &N16n·t[0];
- N16n·copy(operand, N16n·zero);
- }
-
- if(shift_count > 15) return N16n·Status·gt_max_shift_count;
-
- N16n·T *given_operand = &N16n·t[1];
- N16n·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));
- N16n·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 N16n·Status·ok;
- }
-
- Local N16n·Status
- N16n·shift_left(uint16_t shift_count, N16n·T *spill, N16n·T *operand, N16n·T *fill){
- return N16n·shift(shift_count, spill, operand, fill, shift_left_op, shift_right_op);
- }
-
- Local N16n·Status
- N16n·shift_right(uint16_t shift_count, N16n·T *spill, N16n·T *operand, N16n·T *fill){
- return N16n·shift(shift_count, spill, operand, fill, shift_right_op, shift_left_op);
- }
-
- Local N16n·Status
- N16n·arithmetic_shift_right(uint16_t shift_count, N16n·T *operand, N16n·T *spill){
-
- if(shift_count > 15) return N16n·Status·gt_max_shift_count;
-
- if(operand == NULL){
- operand = &N16n·t[0];
- N16n·copy(operand, N16n·zero);
- }
-
- N16n·T *fill = (operand->d0 & 0x8000) ? N16n·all_one_bit : N16n·zero;
- return N16n·shift_right(shift_count, spill, operand, fill);
- }
-
- Local const N16n·Λ N16n·λ = {
-
- .allocate_array = N16n·allocate_array
- ,.allocate_array_zero = N16n·allocate_array_zero
- ,.deallocate = N16n·deallocate
-
- ,.copy = N16n·copy
- ,.bit_and = N16n·bit_and
- ,.bit_or = N16n·bit_or
- ,.bit_complement = N16n·bit_complement
- ,.bit_twos_complement = N16n·bit_twos_complement
- ,.compare = N16n·compare
- ,.lt = N16n·lt
- ,.gt = N16n·gt
- ,.eq = N16n·eq
- ,.eq_zero = N16n·eq_zero
- ,.accumulate = N16n·accumulate
- ,.add = N16n·add
- ,.increment = N16n·increment
- ,.subtract = N16n·subtract
- ,.multiply = N16n·multiply
- ,.divide = N16n·divide
- ,.modulus = N16n·modulus
- ,.shift_left = N16n·shift_left
- ,.shift_right = N16n·shift_right
- ,.arithmetic_shift_right = N16n·arithmetic_shift_right
-
- ,.access = N16n·access
- ,.from_uint32 = N16n·from_uint32
- };
-
- #endif
-
-#endif
--- /dev/null
+/*
+ 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 <stdint.h>
+ #include <stdbool.h>
+ #include <stdarg.h>
+ #include <stdlib.h>
+
+ //----------------------------------------
+ // 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 <stdarg.h>
+ #include <stdlib.h>
+
+ 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
+++ /dev/null
-/*
- 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 N32n·DEBUG
-
-#ifndef FACE
-#define N32n·IMPLEMENTATION
-#define FACE
-#endif
-
-//--------------------------------------------------------------------------------
-// Interface
-
-#ifndef N32n·FACE
-#define N32n·FACE
-
- #include <stdint.h>
- #include <stdbool.h>
- #include <stdarg.h>
- #include <stdlib.h>
-
- //----------------------------------------
- // Instance Data (Declaration Only)
-
- typedef uint32_t Extent;
- typedef uint32_t Digit;
-
- typedef struct N32n·T N32n·T;
-
- extern N32n·T *N32n·zero;
- extern N32n·T *N32n·one;
- extern N32n·T *N32n·all_one_bit;
- extern N32n·T *N32n·lsb;
- extern N32n·T *N32n·msb;
-
- //----------------------------------------
- // Return/Error Status and handlers
-
- typedef enum{
- N32n·Status·ok = 0
- ,N32n·Status·overflow = 1
- ,N32n·Status·accumulator1_overflow = 2
- ,N32n·Status·carry = 3
- ,N32n·Status·borrow = 4
- ,N32n·Status·undefined_divide_by_zero = 5
- ,N32n·Status·undefined_modulus_zero = 6
- ,N32n·Status·gt_max_shift_count = 7
- ,N32n·Status·spill_eq_operand = 8 // not currently signaled, result will be spill value
- ,N32n·Status·one_word_product = 9
- ,N32n·Status·two_word_product = 10
- } N32n·Status;
-
- typedef enum{
- N32n·Order_lt = -1
- ,N32n·Order_eq = 0
- ,N32n·Order_gt = 1
- } N32n·Order;
-
- typedef N32n·T *( *N32n·Allocate_MemoryFault )(Extent);
-
- //----------------------------------------
- // Interface
-
- typedef struct{
-
- N32n·T *(*allocate_array_zero)(Extent, N32n·Allocate_MemoryFault);
- N32n·T *(*allocate_array)(Extent, N32n·Allocate_MemoryFault);
- void (*deallocate)(N32n·T*);
-
- void (*copy)(N32n·T*, N32n·T*);
- void (*bit_and)(N32n·T*, N32n·T*, N32n·T*);
- void (*bit_or)(N32n·T*, N32n·T*, N32n·T*);
- void (*bit_complement)(N32n·T*, N32n·T*);
- void (*bit_twos_complement)(N32n·T*, N32n·T*);
- N32n·Order (*compare)(N32n·T*, N32n·T*);
- bool (*lt)(N32n·T*, N32n·T*);
- bool (*gt)(N32n·T*, N32n·T*);
- bool (*eq)(N32n·T*, N32n·T*);
- bool (*eq_zero)(N32n·T*);
- N32n·Status (*accumulate)(N32n·T *accumulator1 ,N32n·T *accumulator0 ,...);
- N32n·Status (*add)(N32n·T*, N32n·T*, N32n·T*);
- bool (*increment)(N32n·T *a);
- N32n·Status (*subtract)(N32n·T*, N32n·T*, N32n·T*);
- N32n·Status (*multiply)(N32n·T*, N32n·T*, N32n·T*, N32n·T*);
- N32n·Status (*divide)(N32n·T*, N32n·T*, N32n·T*, N32n·T*);
- N32n·Status (*modulus)(N32n·T*, N32n·T*, N32n·T*);
- N32n·Status (*shift_left)(Extent, N32n·T*, N32n·T*, N32n·T*);
- N32n·Status (*shift_right)(Extent, N32n·T*, N32n·T*, N32n·T*);
- N32n·Status (*arithmetic_shift_right)(Extent, N32n·T*, N32n·T*);
-
- N32n·T* (*access)(N32n·T*, Extent);
- void (*from_uint32)(N32n·T *destination ,uint32_t value);
- } N32n·Λ;
-
- Local const N32n·Λ N32n·λ; // initialized in the LOCAL section
-
-#endif
-
-//--------------------------------------------------------------------------------
-// Implementation
-
-#ifdef N32n·IMPLEMENTATION
-
- // this part goes into the library
- #ifndef LOCAL
-
- #include <stdarg.h>
- #include <stdlib.h>
-
- struct N32n·T{
- Digit d0;
- };
-
- N32n·T N32n·constant[4] = {
- {.d0 = 0},
- {.d0 = 1},
- {.d0 = ~(uint32_t)0},
- {.d0 = 1 << 31}
- };
-
- N32n·T *N32n·zero = &N32n·constant[0];
- N32n·T *N32n·one = &N32n·constant[1];
- N32n·T *N32n·all_one_bit = &N32n·constant[2];
- N32n·T *N32n·msb = &N32n·constant[3];
- N32n·T *N32n·lsb = &N32n·constant[1];
-
- // the allocate an array of N32
- N32n·T *N32n·allocate_array(Extent extent ,N32n·Allocate_MemoryFault memory_fault){
- N32n·T *instance = malloc((extent + 1) * sizeof(N32n·T) );
- if(!instance){
- return memory_fault ? memory_fault(extent) : NULL;
- }
- return instance;
- }
-
- N32n·T *N32n·allocate_array_zero(Extent extent ,N32n·Allocate_MemoryFault memory_fault){
- N32n·T *instance = calloc( extent + 1 ,sizeof(N32n·T) );
- if(!instance){
- return memory_fault ? memory_fault(extent) : NULL;
- }
- return instance;
- }
-
- void N32n·deallocate(N32n·T *unencumbered){
- free(unencumbered);
- }
-
- #endif
-
- // This part is included after the library user's code
- #ifdef LOCAL
-
- // instance
-
- struct N32n·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 N32n·T N32n·t[4];
-
-
- // allocation
-
- extern N32n·T *N32n·allocate_array(Extent, N32n·Allocate_MemoryFault);
- extern N32n·T *N32n·allocate_array_zero(Extent, N32n·Allocate_MemoryFault);
- extern void N32n·deallocate(N32n·T *);
-
- // so the user can access numbers in an array allocation
- Local N32n·T* N32n·access(N32n·T *array ,Extent index){
- return &array[index];
- }
-
- Local void N32n·from_uint32(N32n·T *destination ,uint32_t value){
- if(destination == NULL) return;
- destination->d0 = value;
- }
-
- // copy, convenience copy
-
- Local void N32n·copy(N32n·T *destination ,N32n·T *source){
- if(source == destination) return; // that was easy!
- *destination = *source;
- }
-
- Local void N32n·set_to_zero(N32n·T *instance){
- instance->d0 = 0;
- }
-
- Local void N32n·set_to_one(N32n·T *instance){
- instance->d0 = 1;
- }
-
- // bit operations
-
- Local void N32n·bit_and(N32n·T *result, N32n·T *a, N32n·T *b){
- result->d0 = a->d0 & b->d0;
- }
-
- // result can be one of the operands
- Local void N32n·bit_or(N32n·T *result, N32n·T *a, N32n·T *b){
- result->d0 = a->d0 | b->d0;
- }
-
- // result can the same as the operand
- Local void N32n·bit_complement(N32n·T *result, N32n·T *a){
- result->d0 = ~a->d0;
- }
-
- // result can the same as the operand
- Local void N32n·bit_twos_complement(N32n·T *result ,N32n·T *a){
- result->d0 = ~a->d0 + 1;
- }
-
- // test functions
-
- Local N32n·Order N32n·compare(N32n·T *a, N32n·T *b){
- if(a->d0 < b->d0) return N32n·Order_lt;
- if(a->d0 > b->d0) return N32n·Order_gt;
- return N32n·Order_eq;
- }
-
- Local bool N32n·lt(N32n·T *a ,N32n·T *b){
- return a->d0 < b->d0;
- }
-
- Local bool N32n·gt(N32n·T *a ,N32n·T *b){
- return a->d0 > b->d0;
- }
-
- Local bool N32n·eq(N32n·T *a ,N32n·T *b){
- return a->d0 == b->d0;
- }
-
- Local bool N32n·eq_zero(N32n·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 N32n·Status·accumulator1_overflow
- //
- // When accumulator1 and accumulator0 point to the same location, the result is the accumulator1 value.
- Local N32n·Status N32n·accumulate(N32n·T *accumulator1 ,N32n·T *accumulator0 ,...){
-
- va_list args;
- va_start(args ,accumulator0);
- uint32_t sum = accumulator0->d0;
- uint32_t carry = 0;
- N32n·T *current;
-
- while( (current = va_arg(args ,N32n·T *)) ){
- sum += current->d0;
- if(sum < current->d0){ // Accumulator1 into carry
- (carry)++;
- if(carry == 0){
- va_end(args);
- return N32n·Status·accumulator1_overflow;
- }
- }
- }
- va_end(args);
-
- // wipes out prior value of accumulator1
- accumulator1->d0 = carry;
-
- return N32n·Status·ok;
- }
-
- Local N32n·Status N32n·add(N32n·T *sum ,N32n·T *a ,N32n·T *b){
- uint64_t result = (uint64_t)a->d0 + (uint64_t)b->d0;
- sum->d0 = (uint32_t)result;
- return (result >> 32) ? N32n·Status·carry : N32n·Status·ok;
- }
-
- Local bool N32n·increment(N32n·T *a){
- a->d0++;
- return a->d0 == 0;
- }
-
- Local N32n·Status N32n·subtract(N32n·T *difference ,N32n·T *a ,N32n·T *b){
- uint64_t diff = (uint64_t) a->d0 - (uint64_t) b->d0;
- difference->d0 = (uint32_t)diff;
- return (diff > a->d0) ? N32n·Status·borrow : N32n·Status·ok;
- }
-
-
- Local N32n·Status N32n·multiply(N32n·T *product1 ,N32n·T *product0 ,N32n·T *a ,N32n·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 N32n·Status·one_word_product;
- return N32n·Status·two_word_product;
- }
-
- Local N32n·Status N32n·divide(N32n·T *remainder ,N32n·T *quotient ,N32n·T *a ,N32n·T *b){
- if(b->d0 == 0) return N32n·Status·undefined_divide_by_zero;
-
- quotient->d0 = a->d0 / b->d0;
- remainder->d0 = a->d0 - (quotient->d0 * b->d0);
-
- return N32n·Status·ok;
- }
-
- Local N32n·Status N32n·modulus(N32n·T *remainder ,N32n·T *a ,N32n·T *b){
- if(b->d0 == 0) return N32n·Status·undefined_modulus_zero;
- uint32_t quotient = a->d0 / b->d0;
- remainder->d0 = a->d0 - (quotient * b->d0);
- return N32n·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 N32n·Status N32n·shift
- (
- uint32_t shift_count
- ,N32n·T *spill
- ,N32n·T *operand
- ,N32n·T *fill
- ,ShiftOp shift_op
- ,ShiftOp complement_shift_op
- ){
-
- // If no result is needed, return immediately.
- if(operand == NULL && spill == NULL) return N32n·Status·ok;
-
- // Treat NULL operand as zero.
- if(operand == NULL){
- operand = &N32n·t[0];
- N32n·copy(operand, N32n·zero);
- }
-
- // Shifting more than one word breaks our fill/spill model.
- if(shift_count > 31) return N32n·Status·gt_max_shift_count;
-
- // The given operand is still required after it is modified, so we copy it.
- N32n·T *given_operand = &N32n·t[1];
- N32n·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));
- N32n·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 N32n·Status·ok;
- }
-
- // Define concrete shift functions using valid C function pointers
- Local N32n·Status
- N32n·shift_left(uint32_t shift_count, N32n·T *spill, N32n·T *operand, N32n·T *fill){
- return N32n·shift(shift_count, spill, operand, fill, shift_left_op, shift_right_op);
- }
-
- Local N32n·Status
- N32n·shift_right(uint32_t shift_count, N32n·T *spill, N32n·T *operand, N32n·T *fill){
- return N32n·shift(shift_count, spill, operand, fill, shift_right_op, shift_left_op);
- }
-
- Local N32n·Status
- N32n·arithmetic_shift_right(uint32_t shift_count, N32n·T *operand, N32n·T *spill){
-
- // Guard against excessive shift counts
- if(shift_count > 31) return N32n·Status·gt_max_shift_count;
-
- // A NULL operand is treated as zero
- if(operand == NULL){
- operand = &N32n·t[0];
- N32n·copy(operand, N32n·zero);
- }
-
- // Pick the fill value based on the sign bit
- N32n·T *fill = (operand->d0 & 0x80000000) ? N32n·all_one_bit : N32n·zero;
-
- // Call shift_right with the appropriate fill
- return N32n·shift_right(shift_count, spill, operand, fill);
- }
-
- Local const N32n·Λ N32n·λ = {
-
- .allocate_array = N32n·allocate_array
- ,.allocate_array_zero = N32n·allocate_array_zero
- ,.deallocate = N32n·deallocate
-
- ,.copy = N32n·copy
- ,.bit_and = N32n·bit_and
- ,.bit_or = N32n·bit_or
- ,.bit_complement = N32n·bit_complement
- ,.bit_twos_complement = N32n·bit_twos_complement
- ,.compare = N32n·compare
- ,.lt = N32n·lt
- ,.gt = N32n·gt
- ,.eq = N32n·eq
- ,.eq_zero = N32n·eq_zero
- ,.accumulate = N32n·accumulate
- ,.add = N32n·add
- ,.increment = N32n·increment
- ,.subtract = N32n·subtract
- ,.multiply = N32n·multiply
- ,.divide = N32n·divide
- ,.modulus = N32n·modulus
- ,.shift_left = N32n·shift_left
- ,.shift_right = N32n·shift_right
- ,.arithmetic_shift_right = N32n·arithmetic_shift_right
-
- ,.access = N32n·access
- ,.from_uint32 = N32n·from_uint32
- };
-
- #endif
-
-#endif
--- /dev/null
+/*
+ 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 <stdint.h>
+ #include <stdbool.h>
+ #include <stdarg.h>
+ #include <stdlib.h>
+
+ //----------------------------------------
+ // 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 <stdarg.h>
+ #include <stdlib.h>
+
+ 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
+++ /dev/null
-/*
- 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 N64n·DEBUG
-
-#ifndef FACE
-#define N64n·IMPLEMENTATION
-#define FACE
-#endif
-
-//--------------------------------------------------------------------------------
-// Interface
-
-#ifndef N64n·FACE
-#define N64n·FACE
-
- #include <stdint.h>
- #include <stdbool.h>
- #include <stdarg.h>
- #include <stdlib.h>
-
- //----------------------------------------
- // Instance Data (Declaration Only)
-
- typedef uint64_t Extent;
- typedef uint64_t Digit;
-
- typedef struct N64n·T N64n·T;
-
- extern N64n·T *N64n·zero;
- extern N64n·T *N64n·one;
- extern N64n·T *N64n·all_one_bit;
- extern N64n·T *N64n·lsb;
- extern N64n·T *N64n·msb;
-
- //----------------------------------------
- // Return/Error Status and handlers
-
- typedef enum {
- N64n·Status·ok = 0
- ,N64n·Status·overflow = 1
- ,N64n·Status·accumulator1_overflow = 2
- ,N64n·Status·carry = 3
- ,N64n·Status·borrow = 4
- ,N64n·Status·undefined_divide_by_zero = 5
- ,N64n·Status·undefined_modulus_zero = 6
- ,N64n·Status·gt_max_shift_count = 7
- ,N64n·Status·spill_eq_operand = 8 // not currently signaled, result will be spill value
- ,N64n·Status·one_word_product = 9
- ,N64n·Status·two_word_product = 10
- } N64n·Status;
-
- typedef enum {
- N64n·Order_lt = -1
- ,N64n·Order_eq = 0
- ,N64n·Order_gt = 1
- } N64n·Order;
-
- typedef N64n·T *(*N64n·Allocate_MemoryFault)(Extent);
-
- //----------------------------------------
- // Interface
-
- typedef struct {
-
- N64n·T *(*allocate_array_zero)(Extent, N64n·Allocate_MemoryFault);
- N64n·T *(*allocate_array)(Extent, N64n·Allocate_MemoryFault);
- void (*deallocate)(N64n·T*);
-
- void (*copy)(N64n·T*, N64n·T*);
- void (*bit_and)(N64n·T*, N64n·T*, N64n·T*);
- void (*bit_or)(N64n·T*, N64n·T*, N64n·T*);
- void (*bit_complement)(N64n·T*, N64n·T*);
- void (*bit_twos_complement)(N64n·T*, N64n·T*);
- N64n·Order (*compare)(N64n·T*, N64n·T*);
- bool (*lt)(N64n·T*, N64n·T*);
- bool (*gt)(N64n·T*, N64n·T*);
- bool (*eq)(N64n·T*, N64n·T*);
- bool (*eq_zero)(N64n·T*);
- N64n·Status (*accumulate)(N64n·T *accumulator1, N64n·T *accumulator0, ...);
- N64n·Status (*add)(N64n·T*, N64n·T*, N64n·T*);
- bool (*increment)(N64n·T *a);
- N64n·Status (*subtract)(N64n·T*, N64n·T*, N64n·T*);
- N64n·Status (*multiply)(N64n·T*, N64n·T*, N64n·T*, N64n·T*);
- N64n·Status (*divide)(N64n·T*, N64n·T*, N64n·T*, N64n·T*);
- N64n·Status (*modulus)(N64n·T*, N64n·T*, N64n·T*);
- N64n·Status (*shift_left)(Extent, N64n·T*, N64n·T*, N64n·T*);
- N64n·Status (*shift_right)(Extent, N64n·T*, N64n·T*, N64n·T*);
- N64n·Status (*arithmetic_shift_right)(Extent, N64n·T*, N64n·T*);
-
- N64n·T* (*access)(N64n·T*, Extent);
- void (*from_uint64)(N64n·T *destination, uint64_t value);
-
- } N64n·Λ;
-
- Local const N64n·Λ N64n·λ; // initialized in the LOCAL section
-
-#endif
-
-//--------------------------------------------------------------------------------
-// Implementation
-
-#ifdef N64n·IMPLEMENTATION
-
- // this part goes into the library
- #ifndef LOCAL
-
- #include <stdarg.h>
- #include <stdlib.h>
-
- struct N64n·T {
- Digit d0;
- };
-
- // For constants, we store them in an array for convenience
- // 0, 1, all bits set (~0ULL), and MSB set (1ULL<<63)
- N64n·T N64n·constant[4] = {
- {.d0 = 0ULL},
- {.d0 = 1ULL},
- {.d0 = ~(uint64_t)0ULL},
- {.d0 = 1ULL << 63}
- };
-
- N64n·T *N64n·zero = &N64n·constant[0];
- N64n·T *N64n·one = &N64n·constant[1];
- N64n·T *N64n·all_one_bit = &N64n·constant[2];
- N64n·T *N64n·msb = &N64n·constant[3];
- N64n·T *N64n·lsb = &N64n·constant[1];
-
- // allocate an array of N64
- N64n·T *N64n·allocate_array(Extent extent, N64n·Allocate_MemoryFault memory_fault){
- N64n·T *instance = malloc( (extent + 1) * sizeof(N64n·T) );
- if(!instance){
- return memory_fault ? memory_fault(extent) : NULL;
- }
- return instance;
- }
-
- N64n·T *N64n·allocate_array_zero(Extent extent, N64n·Allocate_MemoryFault memory_fault){
- N64n·T *instance = calloc(extent + 1, sizeof(N64n·T));
- if(!instance){
- return memory_fault ? memory_fault(extent) : NULL;
- }
- return instance;
- }
-
- void N64n·deallocate(N64n·T *unencumbered){
- free(unencumbered);
- }
-
- #endif
-
- // This part is included after the library user's code
- #ifdef LOCAL
-
- // instance
-
- struct N64n·T {
- Digit d0;
- };
-
- // local temporary variables
- Local N64n·T N64n·t[4];
-
- // allocation references
- extern N64n·T *N64n·allocate_array(Extent, N64n·Allocate_MemoryFault);
- extern N64n·T *N64n·allocate_array_zero(Extent, N64n·Allocate_MemoryFault);
- extern void N64n·deallocate(N64n·T *);
-
- // Access array
- Local N64n·T* N64n·access(N64n·T *array, Extent index){
- return &array[index];
- }
-
- Local void N64n·from_uint64(N64n·T *destination, uint64_t value){
- if(destination == NULL) return;
- destination->d0 = value;
- }
-
- // copy
- Local void N64n·copy(N64n·T *destination, N64n·T *source){
- if(source == destination) return;
- *destination = *source;
- }
-
- // bit operations
-
- Local void N64n·bit_and(N64n·T *result, N64n·T *a, N64n·T *b){
- result->d0 = a->d0 & b->d0;
- }
-
- Local void N64n·bit_or(N64n·T *result, N64n·T *a, N64n·T *b){
- result->d0 = a->d0 | b->d0;
- }
-
- Local void N64n·bit_complement(N64n·T *result, N64n·T *a){
- result->d0 = ~a->d0;
- }
-
- Local void N64n·bit_twos_complement(N64n·T *result, N64n·T *a){
- result->d0 = ~a->d0 + 1ULL;
- }
-
- // compare & test functions
-
- Local N64n·Order N64n·compare(N64n·T *a, N64n·T *b){
- if(a->d0 < b->d0) return N64n·Order_lt;
- if(a->d0 > b->d0) return N64n·Order_gt;
- return N64n·Order_eq;
- }
-
- Local bool N64n·lt(N64n·T *a, N64n·T *b){
- return (a->d0 < b->d0);
- }
-
- Local bool N64n·gt(N64n·T *a, N64n·T *b){
- return (a->d0 > b->d0);
- }
-
- Local bool N64n·eq(N64n·T *a, N64n·T *b){
- return (a->d0 == b->d0);
- }
-
- Local bool N64n·eq_zero(N64n·T *a){
- return (a->d0 == 0ULL);
- }
-
- // arithmetic operations
-
- // accumulate
- Local N64n·Status N64n·accumulate(N64n·T *accumulator1, N64n·T *accumulator0, ...){
- va_list args;
- va_start(args, accumulator0);
-
- uint64_t sum = accumulator0->d0;
- uint64_t carry = 0;
- N64n·T *current;
-
- while( (current = va_arg(args, N64n·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 N64n·Status·accumulator1_overflow;
- }
- }
- }
- va_end(args);
-
- accumulator1->d0 = carry;
- return N64n·Status·ok;
- }
-
- // add
- Local N64n·Status N64n·add(N64n·T *sum, N64n·T *a, N64n·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 N64n·Status·carry; // means we overflowed
- return N64n·Status·ok;
- }
-
- Local bool N64n·increment(N64n·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 N64n·Status N64n·subtract(N64n·T *difference, N64n·T *a, N64n·T *b){
- uint64_t tmpA = a->d0;
- uint64_t tmpB = b->d0;
- uint64_t diff = tmpA - tmpB;
- difference->d0 = diff;
- if(diff > tmpA) return N64n·Status·borrow; // indicates we borrowed
- return N64n·Status·ok;
- }
-
- // multiply
- // We'll do a 64x64->128 using two 64-bit accumulators
- Local N64n·Status N64n·multiply(N64n·T *product1, N64n·T *product0, N64n·T *a, N64n·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 N64n·Status·one_word_product;
- return N64n·Status·two_word_product;
- }
-
- // divide
- Local N64n·Status N64n·divide(N64n·T *remainder, N64n·T *quotient, N64n·T *a, N64n·T *b){
- // we do not handle a > 64-bit, just the single 64-bit
- if(b->d0 == 0ULL) return N64n·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 N64n·Status·ok;
- }
-
- // modulus
- Local N64n·Status N64n·modulus(N64n·T *remainder, N64n·T *a, N64n·T *b){
- if(b->d0 == 0ULL) return N64n·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 N64n·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 N64n·Status N64n·shift
- (
- uint64_t shift_count,
- N64n·T *spill,
- N64n·T *operand,
- N64n·T *fill,
- ShiftOp shift_op,
- ShiftOp complement_shift_op
- ){
- if(operand == NULL && spill == NULL) return N64n·Status·ok;
-
- // Treat NULL operand as zero
- if(operand == NULL){
- operand = &N64n·t[0];
- N64n·copy(operand, N64n·zero);
- }
-
- // Shifting more than 63 bits breaks fill/spill logic
- if(shift_count > 63ULL) return N64n·Status·gt_max_shift_count;
-
- N64n·T *given_operand = &N64n·t[1];
- N64n·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));
- N64n·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 N64n·Status·ok;
- }
-
- Local N64n·Status N64n·shift_left(uint64_t shift_count, N64n·T *spill, N64n·T *operand, N64n·T *fill){
- return N64n·shift(shift_count, spill, operand, fill, shift_left_op, shift_right_op);
- }
-
- Local N64n·Status N64n·shift_right(uint64_t shift_count, N64n·T *spill, N64n·T *operand, N64n·T *fill){
- return N64n·shift(shift_count, spill, operand, fill, shift_right_op, shift_left_op);
- }
-
- Local N64n·Status N64n·arithmetic_shift_right(uint64_t shift_count, N64n·T *operand, N64n·T *spill){
- if(shift_count > 63ULL) return N64n·Status·gt_max_shift_count;
-
- // A NULL operand is treated as zero
- if(operand == NULL){
- operand = &N64n·t[0];
- N64n·copy(operand, N64n·zero);
- }
-
- // sign bit check
- N64n·T *fill = (operand->d0 & (1ULL << 63)) ? N64n·all_one_bit : N64n·zero;
- return N64n·shift_right(shift_count, spill, operand, fill);
- }
-
- Local const N64n·Λ N64n·λ = {
- .allocate_array = N64n·allocate_array
- ,.allocate_array_zero = N64n·allocate_array_zero
- ,.deallocate = N64n·deallocate
-
- ,.copy = N64n·copy
- ,.bit_and = N64n·bit_and
- ,.bit_or = N64n·bit_or
- ,.bit_complement = N64n·bit_complement
- ,.bit_twos_complement = N64n·bit_twos_complement
- ,.compare = N64n·compare
- ,.lt = N64n·lt
- ,.gt = N64n·gt
- ,.eq = N64n·eq
- ,.eq_zero = N64n·eq_zero
- ,.accumulate = N64n·accumulate
- ,.add = N64n·add
- ,.increment = N64n·increment
- ,.subtract = N64n·subtract
- ,.multiply = N64n·multiply
- ,.divide = N64n·divide
- ,.modulus = N64n·modulus
- ,.shift_left = N64n·shift_left
- ,.shift_right = N64n·shift_right
- ,.arithmetic_shift_right = N64n·arithmetic_shift_right
-
- ,.access = N64n·access
- ,.from_uint64 = N64n·from_uint64
- };
-
- #endif
-
-#endif
--- /dev/null
+/*
+ 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 <stdint.h>
+ #include <stdbool.h>
+ #include <stdarg.h>
+ #include <stdlib.h>
+
+ //----------------------------------------
+ // 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 <stdarg.h>
+ #include <stdlib.h>
+
+ 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
+++ /dev/null
-/*
- N8n - 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 N8n·DEBUG
-
-#ifndef FACE
-#define N8n·IMPLEMENTATION
-#define FACE
-#endif
-
-//--------------------------------------------------------------------------------
-// Interface
-
-#ifndef N8n·FACE
-#define N8n·FACE
-
- #include <stdint.h>
- #include <stdbool.h>
- #include <stdarg.h>
- #include <stdlib.h>
-
- //----------------------------------------
- // Instance Data (Declaration Only)
-
- typedef uint8_t Extent;
- typedef uint8_t Digit;
-
- typedef struct N8n·T N8n·T;
-
- extern N8n·T *N8n·zero;
- extern N8n·T *N8n·one;
- extern N8n·T *N8n·all_one_bit;
- extern N8n·T *N8n·lsb;
- extern N8n·T *N8n·msb;
-
- //----------------------------------------
- // Return/Error Status and handlers
-
- typedef enum{
- N8n·Status·ok = 0
- ,N8n·Status·overflow = 1
- ,N8n·Status·accumulator1_overflow = 2
- ,N8n·Status·carry = 3
- ,N8n·Status·borrow = 4
- ,N8n·Status·undefined_divide_by_zero = 5
- ,N8n·Status·undefined_modulus_zero = 6
- ,N8n·Status·gt_max_shift_count = 7
- ,N8n·Status·spill_eq_operand = 8
- ,N8n·Status·one_word_product = 9
- ,N8n·Status·two_word_product = 10
- } N8n·Status;
-
- typedef enum{
- N8n·Order_lt = -1
- ,N8n·Order_eq = 0
- ,N8n·Order_gt = 1
- } N8n·Order;
-
- typedef N8n·T *( *N8n·Allocate_MemoryFault )(Extent);
-
- //----------------------------------------
- // Interface
-
- typedef struct{
-
- N8n·T *(*allocate_array_zero)(Extent, N8n·Allocate_MemoryFault);
- N8n·T *(*allocate_array)(Extent, N8n·Allocate_MemoryFault);
- void (*deallocate)(N8n·T*);
-
- void (*copy)(N8n·T*, N8n·T*);
- void (*bit_and)(N8n·T*, N8n·T*, N8n·T*);
- void (*bit_or)(N8n·T*, N8n·T*, N8n·T*);
- void (*bit_complement)(N8n·T*, N8n·T*);
- void (*bit_twos_complement)(N8n·T*, N8n·T*);
- N8n·Order (*compare)(N8n·T*, N8n·T*);
- bool (*lt)(N8n·T*, N8n·T*);
- bool (*gt)(N8n·T*, N8n·T*);
- bool (*eq)(N8n·T*, N8n·T*);
- bool (*eq_zero)(N8n·T*);
- N8n·Status (*accumulate)(N8n·T *accumulator1 ,N8n·T *accumulator0 ,...);
- N8n·Status (*add)(N8n·T*, N8n·T*, N8n·T*);
- bool (*increment)(N8n·T *a);
- N8n·Status (*subtract)(N8n·T*, N8n·T*, N8n·T*);
- N8n·Status (*multiply)(N8n·T*, N8n·T*, N8n·T*, N8n·T*);
- N8n·Status (*divide)(N8n·T*, N8n·T*, N8n·T*, N8n·T*);
- N8n·Status (*modulus)(N8n·T*, N8n·T*, N8n·T*);
- N8n·Status (*shift_left)(Extent, N8n·T*, N8n·T*, N8n·T*);
- N8n·Status (*shift_right)(Extent, N8n·T*, N8n·T*, N8n·T*);
- N8n·Status (*arithmetic_shift_right)(Extent, N8n·T*, N8n·T*);
-
- N8n·T* (*access)(N8n·T*, Extent);
- void (*from_uint32)(N8n·T *destination ,uint32_t value);
- } N8n·Λ;
-
- Local const N8n·Λ N8n·λ; // initialized in the LOCAL section
-
-#endif
-
-//--------------------------------------------------------------------------------
-// Implementation
-
-#ifdef N8n·IMPLEMENTATION
-
- // this part goes into the library
- #ifndef LOCAL
-
- #include <stdarg.h>
- #include <stdlib.h>
-
- struct N8n·T{
- Digit d0;
- };
-
- N8n·T N8n·constant[4] = {
- {.d0 = 0},
- {.d0 = 1},
- {.d0 = ~(uint8_t)0},
- {.d0 = 1 << 7}
- };
-
- N8n·T *N8n·zero = &N8n·constant[0];
- N8n·T *N8n·one = &N8n·constant[1];
- N8n·T *N8n·all_one_bit = &N8n·constant[2];
- N8n·T *N8n·msb = &N8n·constant[3];
- N8n·T *N8n·lsb = &N8n·constant[1];
-
- // the allocate an array of N8
- N8n·T *N8n·allocate_array(Extent extent ,N8n·Allocate_MemoryFault memory_fault){
- N8n·T *instance = malloc((extent + 1) * sizeof(N8n·T));
- if(!instance){
- return memory_fault ? memory_fault(extent) : NULL;
- }
- return instance;
- }
-
- N8n·T *N8n·allocate_array_zero(Extent extent ,N8n·Allocate_MemoryFault memory_fault){
- N8n·T *instance = calloc(extent + 1, sizeof(N8n·T));
- if(!instance){
- return memory_fault ? memory_fault(extent) : NULL;
- }
- return instance;
- }
-
- void N8n·deallocate(N8n·T *unencumbered){
- free(unencumbered);
- }
-
-
- #endif
-
- // This part is included after the library user's code
- #ifdef LOCAL
-
- // instance
-
- struct N8n·T{
- Digit d0;
- };
-
- // temporary variables
- Local N8n·T N8n·t[4];
-
- // allocation
-
- extern N8n·T *N8n·allocate_array(Extent, N8n·Allocate_MemoryFault);
- extern N8n·T *N8n·allocate_array_zero(Extent, N8n·Allocate_MemoryFault);
- extern void N8n·deallocate(N8n·T *);
-
- // so the user can access numbers in an array allocation
- Local N8n·T* N8n·access(N8n·T *array ,Extent index){
- return &array[index];
- }
-
- Local void N8n·from_uint32(N8n·T *destination ,uint32_t value){
- if(destination == NULL) return;
- destination->d0 = (uint8_t)(value & 0xFF);
- }
-
- // copy, convenience copy
-
- Local void N8n·copy(N8n·T *destination ,N8n·T *source){
- if(source == destination) return;
- *destination = *source;
- }
-
- Local void N8n·set_to_zero(N8n·T *instance){
- instance->d0 = 0;
- }
-
- Local void N8n·set_to_one(N8n·T *instance){
- instance->d0 = 1;
- }
-
- // bit operations
-
- Local void N8n·bit_and(N8n·T *result, N8n·T *a, N8n·T *b){
- result->d0 = a->d0 & b->d0;
- }
-
- Local void N8n·bit_or(N8n·T *result, N8n·T *a, N8n·T *b){
- result->d0 = a->d0 | b->d0;
- }
-
- Local void N8n·bit_complement(N8n·T *result, N8n·T *a){
- result->d0 = ~a->d0;
- }
-
- Local void N8n·bit_twos_complement(N8n·T *result ,N8n·T *a){
- result->d0 = (uint8_t)(~a->d0 + 1);
- }
-
- // test functions
-
- Local N8n·Order N8n·compare(N8n·T *a, N8n·T *b){
- if(a->d0 < b->d0) return N8n·Order_lt;
- if(a->d0 > b->d0) return N8n·Order_gt;
- return N8n·Order_eq;
- }
-
- Local bool N8n·lt(N8n·T *a ,N8n·T *b){
- return a->d0 < b->d0;
- }
-
- Local bool N8n·gt(N8n·T *a ,N8n·T *b){
- return a->d0 > b->d0;
- }
-
- Local bool N8n·eq(N8n·T *a ,N8n·T *b){
- return a->d0 == b->d0;
- }
-
- Local bool N8n·eq_zero(N8n·T *a){
- return a->d0 == 0;
- }
-
- // arithmetic operations
-
- Local N8n·Status N8n·accumulate(N8n·T *accumulator1 ,N8n·T *accumulator0 ,...){
-
- va_list args;
- va_start(args ,accumulator0);
- uint32_t sum = accumulator0->d0;
- uint32_t carry = 0;
- N8n·T *current;
-
- while( (current = va_arg(args ,N8n·T*)) ){
- sum += current->d0;
- if(sum < current->d0){
- (carry)++;
- if(carry == 0){
- va_end(args);
- return N8n·Status·accumulator1_overflow;
- }
- }
- }
- va_end(args);
-
- accumulator1->d0 = (uint8_t)carry;
- return N8n·Status·ok;
- }
-
- Local N8n·Status N8n·add(N8n·T *sum ,N8n·T *a ,N8n·T *b){
- uint32_t result = (uint32_t)a->d0 + (uint32_t)b->d0;
- sum->d0 = (uint8_t)(result & 0xFF);
- return (result >> 8) ? N8n·Status·carry : N8n·Status·ok;
- }
-
- Local bool N8n·increment(N8n·T *a){
- a->d0++;
- return (a->d0 == 0);
- }
-
- Local N8n·Status N8n·subtract(N8n·T *difference ,N8n·T *a ,N8n·T *b){
- uint32_t diff = (uint32_t)a->d0 - (uint32_t)b->d0;
- difference->d0 = (uint8_t)(diff & 0xFF);
- return (diff > a->d0) ? N8n·Status·borrow : N8n·Status·ok;
- }
-
- Local N8n·Status N8n·multiply(N8n·T *product1 ,N8n·T *product0 ,N8n·T *a ,N8n·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 N8n·Status·one_word_product;
- return N8n·Status·two_word_product;
- }
-
- Local N8n·Status N8n·divide(N8n·T *remainder ,N8n·T *quotient ,N8n·T *a ,N8n·T *b){
- if(b->d0 == 0) return N8n·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 N8n·Status·ok;
- }
-
- Local N8n·Status N8n·modulus(N8n·T *remainder ,N8n·T *a ,N8n·T *b){
- if(b->d0 == 0) return N8n·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 N8n·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 N8n·Status N8n·shift
- (
- uint8_t shift_count
- ,N8n·T *spill
- ,N8n·T *operand
- ,N8n·T *fill
- ,ShiftOp shift_op
- ,ShiftOp complement_shift_op
- ){
-
- if(operand == NULL && spill == NULL) return N8n·Status·ok;
-
- if(operand == NULL){
- operand = &N8n·t[0];
- N8n·copy(operand, N8n·zero);
- }
-
- if(shift_count > 7) return N8n·Status·gt_max_shift_count;
-
- N8n·T *given_operand = &N8n·t[1];
- N8n·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));
- N8n·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 N8n·Status·ok;
- }
-
- Local N8n·Status
- N8n·shift_left(uint8_t shift_count, N8n·T *spill, N8n·T *operand, N8n·T *fill){
- return N8n·shift(shift_count, spill, operand, fill, shift_left_op, shift_right_op);
- }
-
- Local N8n·Status
- N8n·shift_right(uint8_t shift_count, N8n·T *spill, N8n·T *operand, N8n·T *fill){
- return N8n·shift(shift_count, spill, operand, fill, shift_right_op, shift_left_op);
- }
-
- Local N8n·Status
- N8n·arithmetic_shift_right(uint8_t shift_count, N8n·T *operand, N8n·T *spill){
-
- if(shift_count > 7) return N8n·Status·gt_max_shift_count;
-
- if(operand == NULL){
- operand = &N8n·t[0];
- N8n·copy(operand, N8n·zero);
- }
-
- N8n·T *fill = (operand->d0 & 0x80) ? N8n·all_one_bit : N8n·zero;
- return N8n·shift_right(shift_count, spill, operand, fill);
- }
-
- Local const N8n·Λ N8n·λ = {
-
- .allocate_array = N8n·allocate_array
- ,.allocate_array_zero = N8n·allocate_array_zero
- ,.deallocate = N8n·deallocate
-
- ,.copy = N8n·copy
- ,.bit_and = N8n·bit_and
- ,.bit_or = N8n·bit_or
- ,.bit_complement = N8n·bit_complement
- ,.bit_twos_complement = N8n·bit_twos_complement
- ,.compare = N8n·compare
- ,.lt = N8n·lt
- ,.gt = N8n·gt
- ,.eq = N8n·eq
- ,.eq_zero = N8n·eq_zero
- ,.accumulate = N8n·accumulate
- ,.add = N8n·add
- ,.increment = N8n·increment
- ,.subtract = N8n·subtract
- ,.multiply = N8n·multiply
- ,.divide = N8n·divide
- ,.modulus = N8n·modulus
- ,.shift_left = N8n·shift_left
- ,.shift_right = N8n·shift_right
- ,.arithmetic_shift_right = N8n·arithmetic_shift_right
-
- ,.access = N8n·access
- ,.from_uint32 = N8n·from_uint32
- };
-
- #endif
-
-#endif
+++ /dev/null
-#include <stdio.h>
-#include <stdbool.h>
-#include <signal.h>
-#include <setjmp.h>
-
-// 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
--- /dev/null
+#+TITLE: Python-Based Template Generation (Multi-Module Approach)
+#+AUTHOR: Example
+
+Below is a single Org-mode document containing **four** separate Python modules
+(or scripts). Each code block is top-level (not nested), ensuring they render
+correctly. Together, they demonstrate how to generate a parametric `.lib.c` file
+for an RT code format library.
+
+1. **get_template.py**: Returns a base template with `{named_blank}` placeholders.
+2. **make_constants.py**: Creates the compile-time constants block for zero, one, etc.
+3. **fill_template.py**: Combines the template and constants block, substituting parameters.
+4. **A "Main" generator script** (for instance, `gen_n64_lib.py`) that writes out a final file (like `N64.lib.c`).
+
+You can create additional “main” scripts to produce different `.lib.c` files (e.g., `N128.lib.c`).
+
+---
+
+*1. `get_template.py`*
+
+#+BEGIN_SRC python
+#!/usr/bin/env python3
+
+def get_template():
+ """
+ Returns the base RT C code template, with placeholders like:
+ {NAMESPACE}, {DIGIT_EXTENT}, {DIGIT_TYPE}, {EXTENT_TYPE}, {CONSTANTS_BLOCK}.
+
+ The final generated .lib.c will replace these with user-specified values.
+ """
+ return r'''#define {NAMESPACE}DEBUG
+
+#ifndef FACE
+ #define {NAMESPACE}IMPLEMENTATION
+ #define FACE
+#endif
+
+#ifndef {NAMESPACE}FACE
+#define {NAMESPACE}FACE
+
+ #include <stdint.h>
+ #include <stdbool.h>
+ #include <stdarg.h>
+ #include <stdlib.h>
+
+ typedef {EXTENT_TYPE} ExtentType;
+ typedef {DIGIT_TYPE} Digit;
+
+ // Digit count is (DIGIT_EXTENT + 1)
+ #define {NAMESPACE}DIGIT_COUNT ( {DIGIT_EXTENT} + 1 )
+
+ typedef struct {NAMESPACE}T{{
+ Digit d[{NAMESPACE}DIGIT_COUNT];
+ }} {NAMESPACE}T;
+
+ // forward declarations for constants
+ extern {NAMESPACE}T *{NAMESPACE}zero;
+ extern {NAMESPACE}T *{NAMESPACE}one;
+ extern {NAMESPACE}T *{NAMESPACE}all_one_bit;
+ extern {NAMESPACE}T *{NAMESPACE}msb;
+
+ // forward declarations for allocations, etc.
+
+#endif // {NAMESPACE}FACE
+
+#ifdef {NAMESPACE}IMPLEMENTATION
+
+#ifndef LOCAL
+ #include <stdarg.h>
+ #include <stdlib.h>
+
+ // compile-time constants
+ {CONSTANTS_BLOCK}
+
+ {NAMESPACE}T *{NAMESPACE}zero = &{NAMESPACE}constant[0];
+ {NAMESPACE}T *{NAMESPACE}one = &{NAMESPACE}constant[1];
+ {NAMESPACE}T *{NAMESPACE}all_one_bit = &{NAMESPACE}constant[2];
+ {NAMESPACE}T *{NAMESPACE}msb = &{NAMESPACE}constant[3];
+
+ // memory allocation prototypes, etc.
+
+#endif // not LOCAL
+
+#ifdef LOCAL
+ // local code: actual function bodies, add, subtract, etc.
+#endif // LOCAL
+
+#endif // {NAMESPACE}IMPLEMENTATION
+'''
+#+END_SRC
+
+---
+
+*2. `make_constants.py`*
+
+#+BEGIN_SRC python
+#!/usr/bin/env python3
+
+def make_constants_block(namespace: str,
+ digit_type: str,
+ digit_extent: int) -> str:
+ """
+ Returns a block of code defining static compile-time constants:
+ static {namespace}T {namespace}constant[4] = {
+ { { ...zero... } },
+ { { ...one... } },
+ { { ...allbits... } },
+ { { ...msb... } }
+ };
+
+ The total digit count is digit_extent + 1.
+ """
+ digit_count = digit_extent + 1
+
+ def digits_zero():
+ return ", ".join("0" for _ in range(digit_count))
+
+ def digits_one():
+ # index 0 => 1, rest => 0
+ return ", ".join("1" if i == 0 else "0" for i in range(digit_count))
+
+ def digits_allbits():
+ # each digit => (digit_type)(-1)
+ return ", ".join(f"( {digit_type} )( -1 )" for _ in range(digit_count))
+
+ def digits_msb():
+ # top index => (digit_type)1 << ((sizeof(digit_type)*8)-1)
+ items = []
+ for i in range(digit_count):
+ if i == digit_count - 1:
+ items.append(f"( {digit_type} )1 << ((sizeof({digit_type})*8) - 1)")
+ else:
+ items.append("0")
+ return ", ".join(items)
+
+ return f'''\
+static {namespace}T {namespace}constant[4] = {{
+ {{
+ // zero
+ {{ {digits_zero()} }}
+ }},
+ {{
+ // one
+ {{ {digits_one()} }}
+ }},
+ {{
+ // all bits
+ {{ {digits_allbits()} }}
+ }},
+ {{
+ // msb
+ {{ {digits_msb()} }}
+ }}
+}};'''
+
+#+END_SRC
+
+---
+
+*3. `fill_template.py`*
+
+#+BEGIN_SRC python
+#!/usr/bin/env python3
+
+from get_template import get_template
+from make_constants import make_constants_block
+
+def fill_template(namespace: str,
+ digit_extent: int,
+ digit_type: str,
+ extent_type: str) -> str:
+ """
+ Renders the final .lib.c code by merging:
+ - the base template from get_template()
+ - the compile-time constants block from make_constants_block()
+ - placeholders for namespace, digit_extent, digit_type, extent_type
+ """
+ template = get_template()
+ constants_block = make_constants_block(namespace, digit_type, digit_extent)
+
+ # Substitute placeholders
+ code = template.format(
+ NAMESPACE = namespace,
+ DIGIT_EXTENT = digit_extent,
+ DIGIT_TYPE = digit_type,
+ EXTENT_TYPE = extent_type,
+ CONSTANTS_BLOCK = constants_block
+ )
+ return code
+#+END_SRC
+
+---
+
+*4. An Example “Main” Script, `gen_n64_lib.py`*
+
+#+BEGIN_SRC python
+#!/usr/bin/env python3
+
+import sys
+from fill_template import fill_template
+
+def main():
+ """
+ Example: generate a .lib.c with an 'N64·' namespace,
+ digit_extent=0 => 1 digit total,
+ digit_type='uint32_t',
+ extent_type='uint64_t'.
+ """
+ code = fill_template(
+ namespace = "N64·",
+ digit_extent = 0, # => digit_count = 1 => 32-bit
+ digit_type = "uint32_t",
+ extent_type = "uint64_t"
+ )
+
+ with open("N64.lib.c", "w") as f:
+ f.write(code)
+ print("Generated N64.lib.c")
+
+if __name__ == "__main__":
+ main()
+#+END_SRC
+
+---
+
+### Usage
+
+1. **Place** these four files side-by-side in your project (e.g., a `scripts/` directory).
+2. **Mark** them as executable (`chmod +x`).
+3. **Run** `./gen_n64_lib.py` to produce `N64.lib.c`. This file will appear in the same directory (or wherever you prefer).
+4. **Compile** `N64.lib.c` using your RT C build system (`make -f tool🖉/makefile`, etc.).
+
+You can then create similar scripts like `gen_n128_lib.py` or unify them into a single driver that takes parameters for namespace, digit_extent, etc. The main advantage is that **Python code** for generating constants is much more readable than M4 macros, and you keep the RT code format in your template exactly as it should appear.
+++ /dev/null
-changequote([,])dnl
-define(NS, __NAMESPACE__)dnl
-define(DE, __DIGIT_EXTENT__)dnl
-define(DT, __DIGIT_TYPE__)dnl
-define(ET, __EXTENT_TYPE__)dnl
-changequote(`,')dnl
-
-/*
- [NS] - Parametric multi-digit type library (RT code format)
-
- Parameters:
- [NS]: Namespace prefix, e.g. N32·
- [DE]: Digit extent => total digits = DE + 1
- [DT]: Digit type, e.g. uint64_t or uint8_t
- [ET]: Loop counter type, e.g. uint64_t
-*/
-
-#define [NS]DEBUG
-
-#ifndef FACE
- #define [NS]IMPLEMENTATION
- #define FACE
-#endif
-
-//------------------------------------------------------------------------------
-// Interface Section
-
-#ifndef [NS]FACE
-#define [NS]FACE
-
- #include <stdint.h>
- #include <stdbool.h>
- #include <stdarg.h>
- #include <stdlib.h>
-
- // Digit count is DE + 1
- #define [NS]DIGIT_COUNT (DE + 1)
-
- typedef DT Digit;
- typedef struct [NS]T [NS]T;
-
- // For array allocations
- typedef ET ExtentType;
-
- // Status codes
- typedef enum{
- [NS]Status_ok = 0
- ,[NS]Status_overflow
- ,[NS]Status_accumulator1_overflow
- ,[NS]Status_carry
- ,[NS]Status_borrow
- ,[NS]Status_undefined_divide_by_zero
- ,[NS]Status_undefined_modulus_zero
- ,[NS]Status_gt_max_shift_count
- ,[NS]Status_spill_eq_operand
- ,[NS]Status_one_word_product
- ,[NS]Status_two_word_product
- } [NS]Status;
-
- typedef enum{
- [NS]Order_lt = -1
- ,[NS]Order_eq = 0
- ,[NS]Order_gt = 1
- } [NS]Order;
-
- // Allocation
- Digit *[NS]allocate_array(ExtentType extent ,[NS]T *(*fault_handler)(ExtentType));
- Digit *[NS]allocate_array_zero(ExtentType extent ,[NS]T *(*fault_handler)(ExtentType));
- void [NS]deallocate([NS]T *unencumbered);
-
- // Constant setters (no static global constants)
- void [NS]set_zero([NS]T *destination);
- void [NS]set_one([NS]T *destination);
- void [NS]set_all_bits([NS]T *destination);
- void [NS]set_msb([NS]T *destination);
-
- // Bitwise ops
- void [NS]bit_and([NS]T *result ,[NS]T *a ,[NS]T *b);
- void [NS]bit_or([NS]T *result ,[NS]T *a ,[NS]T *b);
- void [NS]bit_complement([NS]T *result ,[NS]T *a);
- void [NS]bit_twos_complement([NS]T *result ,[NS]T *a);
-
- // Comparison ops
- bool [NS]eq([NS]T *a ,[NS]T *b);
- bool [NS]eq_zero([NS]T *a);
- [NS]Order [NS]compare([NS]T *a ,[NS]T *b);
-
- // Arithmetic
- [NS]Status [NS]add([NS]T *sum ,[NS]T *a ,[NS]T *b);
- [NS]Status [NS]subtract([NS]T *diff ,[NS]T *a ,[NS]T *b);
-
- // Conversions
- void [NS]from_uint64([NS]T *destination ,uint64_t value);
-
-#endif // [NS]FACE
-
-
-//------------------------------------------------------------------------------
-// Implementation Section
-
-#ifdef [NS]IMPLEMENTATION
-
- #ifndef LOCAL
-
- #include <stdarg.h>
- #include <stdlib.h>
-
- struct [NS]T{
- Digit d[[NS]DIGIT_COUNT];
- };
-
- Digit *[NS]allocate_array(ExtentType extent ,[NS]T *(*fault_handler)(ExtentType)){
- [NS]T *instance = malloc((extent + 1) * sizeof([NS]T));
- if(!instance){
- if(fault_handler) return (Digit*)fault_handler(extent);
- return NULL;
- }
- return (Digit*)instance;
- }
-
- Digit *[NS]allocate_array_zero(ExtentType extent ,[NS]T *(*fault_handler)(ExtentType)){
- [NS]T *instance = calloc(extent + 1 ,sizeof([NS]T));
- if(!instance){
- if(fault_handler) return (Digit*)fault_handler(extent);
- return NULL;
- }
- return (Digit*)instance;
- }
-
- void [NS]deallocate([NS]T *unencumbered){
- free(unencumbered);
- }
-
- #endif // LOCAL not defined
-
-
- #ifdef LOCAL
-
- // local code implementing everything
- struct [NS]T{
- Digit d[[NS]DIGIT_COUNT];
- };
-
- // Constant Setters
-
- Local void [NS]set_zero([NS]T *destination){
- if(!destination) return;
- Digit *start = destination->d;
- Digit *end = destination->d + [NS]DIGIT_COUNT;
- for(Digit *p = start ; p < end ; p++){
- *p = 0;
- }
- }
-
- Local void [NS]set_one([NS]T *destination){
- if(!destination) return;
- [NS]set_zero(destination);
- destination->d[0] = 1;
- }
-
- Local void [NS]set_all_bits([NS]T *destination){
- if(!destination) return;
- Digit *start = destination->d;
- Digit *end = destination->d + [NS]DIGIT_COUNT;
- for(Digit *p = start ; p < end ; p++){
- *p = (Digit)(-1);
- }
- }
-
- Local void [NS]set_msb([NS]T *destination){
- if(!destination) return;
- [NS]set_zero(destination);
- // Set top bit in the highest digit
- destination->d[[NS]DIGIT_COUNT - 1] = ((Digit)1 << ((sizeof(Digit)*8) - 1));
- }
-
- // Bitwise
-
- Local void [NS]bit_and([NS]T *result ,[NS]T *a ,[NS]T *b){
- for(ExtentType i = 0 ; i < [NS]DIGIT_COUNT ; i++){
- result->d[i] = a->d[i] & b->d[i];
- }
- }
-
- Local void [NS]bit_or([NS]T *result ,[NS]T *a ,[NS]T *b){
- for(ExtentType i = 0 ; i < [NS]DIGIT_COUNT ; i++){
- result->d[i] = a->d[i] | b->d[i];
- }
- }
-
- Local void [NS]bit_complement([NS]T *result ,[NS]T *a){
- if(result == a){
- // same location
- for(ExtentType i = 0 ; i < [NS]DIGIT_COUNT ; i++){
- result->d[i] = ~result->d[i];
- }
- }else{
- for(ExtentType i = 0 ; i < [NS]DIGIT_COUNT ; i++){
- result->d[i] = ~a->d[i];
- }
- }
- }
-
- Local void [NS]bit_twos_complement([NS]T *result ,[NS]T *a){
- // ~ + 1 across [NS]DIGIT_COUNT digits
- // If result == a, we must do carefully
- [NS]T temp;
- if(result == a) {
- for(ExtentType i = 0 ; i < [NS]DIGIT_COUNT ; i++){
- temp.d[i] = ~a->d[i];
- }
- }else{
- for(ExtentType i = 0 ; i < [NS]DIGIT_COUNT ; i++){
- temp.d[i] = ~a->d[i];
- }
- }
- // now add 1
- Digit carry = 1;
- for(ExtentType i = 0 ; i < [NS]DIGIT_COUNT ; i++){
- unsigned __int128 v = (unsigned __int128)temp.d[i] + carry;
- temp.d[i] = (Digit)v;
- carry = (Digit)(v >> (sizeof(Digit)*8));
- }
- // copy back if needed
- if(result == a){
- for(ExtentType i = 0 ; i < [NS]DIGIT_COUNT ; i++){
- a->d[i] = temp.d[i];
- }
- }else{
- for(ExtentType i = 0 ; i < [NS]DIGIT_COUNT ; i++){
- result->d[i] = temp.d[i];
- }
- }
- }
-
- // Comparison
-
- Local [NS]Order [NS]compare([NS]T *a ,[NS]T *b){
- // compare from top to bottom
- for(ExtentType i = [NS]DIGIT_COUNT; i > 0 ; i--){
- ExtentType idx = i - 1;
- if(a->d[idx] < b->d[idx]) return [NS]Order_lt;
- if(a->d[idx] > b->d[idx]) return [NS]Order_gt;
- }
- return [NS]Order_eq;
- }
-
- Local bool [NS]eq([NS]T *a ,[NS]T *b){
- for(ExtentType i = 0 ; i < [NS]DIGIT_COUNT ; i++){
- if(a->d[i] != b->d[i]) return false;
- }
- return true;
- }
-
- Local bool [NS]eq_zero([NS]T *a){
- for(ExtentType i = 0 ; i < [NS]DIGIT_COUNT ; i++){
- if(a->d[i] != 0) return false;
- }
- return true;
- }
-
- // Arithmetic
-
- Local [NS]Status [NS]add([NS]T *sum ,[NS]T *a ,[NS]T *b){
- if(!sum || !a || !b) return [NS]Status_overflow;
- Digit carry = 0;
- for(ExtentType i = 0 ; i < [NS]DIGIT_COUNT ; i++){
- unsigned __int128 tmp = (unsigned __int128)a->d[i] + b->d[i] + carry;
- sum->d[i] = (Digit)tmp;
- carry = (Digit)(tmp >> (sizeof(Digit)*8));
- }
- return (carry != 0) ? [NS]Status_carry : [NS]Status_ok;
- }
-
- Local [NS]Status [NS]subtract([NS]T *diff ,[NS]T *a ,[NS]T *b){
- if(!diff || !a || !b) return [NS]Status_overflow;
- Digit borrow = 0;
- for(ExtentType i = 0 ; i < [NS]DIGIT_COUNT ; i++){
- unsigned __int128 tmpA = a->d[i];
- unsigned __int128 tmpB = b->d[i] + borrow;
- if(tmpA < tmpB){
- diff->d[i] = (Digit)((((unsigned __int128)1 << (sizeof(Digit)*8)) + tmpA) - tmpB);
- borrow = 1;
- }else{
- diff->d[i] = (Digit)(tmpA - tmpB);
- borrow = 0;
- }
- }
- return (borrow != 0) ? [NS]Status_borrow : [NS]Status_ok;
- }
-
- // Conversion
-
- Local void [NS]from_uint64([NS]T *destination ,uint64_t value){
- if(!destination) return;
- [NS]set_zero(destination);
- // store 'value' in the low words
- // note: if (DE + 1) < 2, we might only store partial
- destination->d[0] = (Digit)(value & 0xFFFFFFFFFFFFFFFFULL);
- #if DE >= 1
- if([NS]DIGIT_COUNT > 1){
- // store top half if digit is 32 bits etc, or 8 bits
- // we'll just do partial logic if digit < 64
- // for 32-bit digit, that means the next digit has (value >> 32)
- // for 8-bit digit, the next digits hold the rest, etc.
- // We'll keep it simple.
- // A more complete solution would loop if needed.
- if(sizeof(Digit)*8 < 64){
- // multi-split if needed
- // skipping for brevity
- }
- }
- #endif
- }
-
- #endif // LOCAL
-
-#endif // [NS]IMPLEMENTATION
--- /dev/null
+*
+!/.gitignore
--- /dev/null
+#define N64·DEBUG
+
+#ifndef FACE
+ #define N64·IMPLEMENTATION
+ #define FACE
+#endif
+
+#ifndef N64·FACE
+#define N64·FACE
+
+ #include <stdint.h>
+ #include <stdbool.h>
+ #include <stdarg.h>
+ #include <stdlib.h>
+
+ typedef uint8_t Digit;
+
+ // Digit count is (DIGIT_EXTENT + 1)
+ #define N64·DIGIT_COUNT ( 8 + 1 )
+
+ typedef struct N64·T{
+ Digit d[N64·DIGIT_COUNT];
+ } N64·T;
+
+ // forward declarations for constants
+ extern N64·T *N64·zero;
+ extern N64·T *N64·one;
+ extern N64·T *N64·all_one_bit;
+ extern N64·T *N64·msb;
+
+ // forward declarations for allocations, etc.
+
+#endif // N64·FACE
+
+#ifdef N64·IMPLEMENTATION
+
+#ifndef LOCAL
+ #include <stdarg.h>
+ #include <stdlib.h>
+
+ // compile-time constants
+ static N64·T N64·constant[4] = {
+ {
+ // zero
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+ },
+ {
+ // one
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0 }
+ },
+ {
+ // all one bits
+ { ( uint8_t )( -1 ), ( uint8_t )( -1 ), ( uint8_t )( -1 ), ( uint8_t )( -1 ), ( uint8_t )( -1 ), ( uint8_t )( -1 ), ( uint8_t )( -1 ), ( uint8_t )( -1 ), ( uint8_t )( -1 ) }
+ },
+ {
+ // msb
+ { 0, 0, 0, 0, 0, 0, 0, 0, ( uint8_t )1 << ((sizeof(uint8_t)*8) - 1) }
+ }
+};
+
+ 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];
+
+ // memory allocation prototypes, etc.
+
+#endif // not LOCAL
+
+#ifdef LOCAL
+ // local code: actual function bodies, add, subtract, etc.
+#endif // LOCAL
+
+#endif // N64·IMPLEMENTATION
--- /dev/null
+#+BEGIN_SRC python
+#!/usr/bin/env python3
+
+from get_template import get_template
+from make_constants import make_constants_block
+
+def fill_template(namespace: str,
+ digit_extent: int,
+ digit_type: str) -> str:
+ """
+ Renders the final .lib.c code by merging:
+ - the base template from get_template()
+ - the compile-time constants block from make_constants_block()
+ - placeholders for namespace, digit_extent, digit_type, extent_type
+ """
+ template = get_template()
+ constants_block = make_constants_block(namespace, digit_type, digit_extent)
+
+ # Substitute placeholders
+ code = template.format(
+ NAMESPACE = namespace,
+ DIGIT_EXTENT = digit_extent,
+ DIGIT_TYPE = digit_type,
+ CONSTANTS_BLOCK = constants_block
+ )
+ return code
+#+END_SRC
--- /dev/null
+#!/usr/bin/env python3
+
+import sys
+from fill_template import fill_template
+
+def main():
+ """
+ Example: generate an N<type_name>.lib.c
+ """
+
+ type_name = "N32";
+
+ # 4 x 8 bit
+ code = fill_template(
+ namespace = type_name + "·",
+ digit_extent = 3,
+ digit_type = "uint8_t",
+ )
+
+
+ filename = "../cc/" + type_name + ".lib.c"
+ with open(filename, "w") as f:
+ f.write(code)
+ print("Generated " + filename)
+
+if __name__ == "__main__":
+ main()
--- /dev/null
+#!/usr/bin/env python3
+
+import sys
+from fill_template import fill_template
+
+def main():
+ """
+ Example: generate a .lib.c with an 'N64·' namespace,
+ """
+ code = fill_template(
+ namespace = "N64·",
+ digit_extent = 8, # => digit_count = 1 => 32-bit
+ digit_type = "uint8_t",
+ )
+
+ with open("N64.lib.c", "w") as f:
+ f.write(code)
+ print("Generated N64.lib.c")
+
+if __name__ == "__main__":
+ main()
--- /dev/null
+def get_template():
+ """
+ Returns the base RT C code template, with placeholders like:
+ {NAMESPACE}, {DIGIT_EXTENT}, {DIGIT_TYPE}, {EXTENT_TYPE}, {CONSTANTS_BLOCK}.
+
+ The final generated .lib.c will replace these with user-specified values.
+ """
+ return r'''#define {NAMESPACE}DEBUG
+
+#ifndef FACE
+ #define {NAMESPACE}IMPLEMENTATION
+ #define FACE
+#endif
+
+#ifndef {NAMESPACE}FACE
+#define {NAMESPACE}FACE
+
+ #include <stdint.h>
+ #include <stdbool.h>
+ #include <stdarg.h>
+ #include <stdlib.h>
+
+ typedef {DIGIT_TYPE} Digit;
+
+ // Digit count is (DIGIT_EXTENT + 1)
+ #define {NAMESPACE}DIGIT_COUNT ( {DIGIT_EXTENT} + 1 )
+
+ typedef struct {NAMESPACE}T{{
+ Digit d[{NAMESPACE}DIGIT_COUNT];
+ }} {NAMESPACE}T;
+
+ // forward declarations for constants
+ extern {NAMESPACE}T *{NAMESPACE}zero;
+ extern {NAMESPACE}T *{NAMESPACE}one;
+ extern {NAMESPACE}T *{NAMESPACE}all_one_bit;
+ extern {NAMESPACE}T *{NAMESPACE}msb;
+
+ // forward declarations for allocations, etc.
+
+#endif // {NAMESPACE}FACE
+
+#ifdef {NAMESPACE}IMPLEMENTATION
+
+#ifndef LOCAL
+ #include <stdarg.h>
+ #include <stdlib.h>
+
+ // compile-time constants
+ {CONSTANTS_BLOCK}
+
+ {NAMESPACE}T *{NAMESPACE}zero = &{NAMESPACE}constant[0];
+ {NAMESPACE}T *{NAMESPACE}one = &{NAMESPACE}constant[1];
+ {NAMESPACE}T *{NAMESPACE}all_one_bit = &{NAMESPACE}constant[2];
+ {NAMESPACE}T *{NAMESPACE}msb = &{NAMESPACE}constant[3];
+
+ // memory allocation prototypes, etc.
+
+#endif // not LOCAL
+
+#ifdef LOCAL
+ // local code: actual function bodies, add, subtract, etc.
+#endif // LOCAL
+
+#endif // {NAMESPACE}IMPLEMENTATION
+'''
--- /dev/null
+def make_constants_block(namespace: str,
+ digit_type: str,
+ digit_extent: int) -> str:
+ """
+ Returns a block of code defining static compile-time constants:
+ static {namespace}T {namespace}constant[4] = {
+ { { ...zero... } },
+ { { ...one... } },
+ { { ...allbits... } },
+ { { ...msb... } }
+ };
+
+ The total digit count is digit_extent + 1.
+ """
+ digit_count = digit_extent + 1
+
+ def digits_zero():
+ return ", ".join("0" for _ in range(digit_count))
+
+ def digits_one():
+ # index 0 => 1, rest => 0
+ return ", ".join("1" if i == 0 else "0" for i in range(digit_count))
+
+ def digits_allbits():
+ # each digit => (digit_type)(-1)
+ return ", ".join(f"( {digit_type} )( -1 )" for _ in range(digit_count))
+
+ def digits_msb():
+ # top index => (digit_type)1 << ((sizeof(digit_type)*8)-1)
+ items = []
+ for i in range(digit_count):
+ if i == digit_count - 1:
+ items.append(f"( {digit_type} )1 << ((sizeof({digit_type})*8) - 1)")
+ else:
+ items.append("0")
+ return ", ".join(items)
+
+ return f'''\
+static {namespace}T {namespace}constant[4] = {{
+ {{
+ // zero
+ {{ {digits_zero()} }}
+ }},
+ {{
+ // one
+ {{ {digits_one()} }}
+ }},
+ {{
+ // all one bits
+ {{ {digits_allbits()} }}
+ }},
+ {{
+ // msb
+ {{ {digits_msb()} }}
+ }}
+}};'''
if $error_not_sourced; then exit 1; fi
if $error_bad_env; then return 1; fi
+
+export ROLE=developer
+
# so we can do the build
export PATH=\
-"$REPO_HOME"/developer/tool🖉/\
+"$REPO_HOME"/${ROLE}/tool🖉/\
:"$PATH"
# misc
# some feedback to show all went well
- export PROMPT_DECOR="$PROJECT"_developer
+ export PROMPT_DECOR="$PROJECT_$ROLE"
export ENV=$(script_fp)
echo ENV "$ENV"
- cd "$REPO_HOME"/developer/
-
-
+ cd "$REPO_HOME/$ROLE"
-#!/bin/env /bin/bash
+#!/usr/bin/env bash
+script_afp=$(realpath "${BASH_SOURCE[0]}")
-set -e
-cd ${REPO_HOME}/developer
+# input guards
-# # Example m4 invocation:
-# m4 -D__NAMESPACE__=N128· \
-# -D__DIGIT_EXTENT__=1 \
-# -D__DIGIT_TYPE__=uint64_t \
-# -D__EXTENT_TYPE__=uint64_t \
-# src/N_template.lib.c.m4 > src/N128.lib.c
+ env_must_be="developer/tool🖉/env"
+ if [ "$ENV" != "$env_must_be" ]; then
+ echo "$(script_fp):: error: must be run in the $env_must_be environment"
+ exit 1
+ fi
-# m4 -D__NAMESPACE__=N32· -D__DIGIT_EXTENT__=0 -D__DIGIT_TYPE__=uint32_t -D__EXTENT_TYPE__=uint32_t m4🖉/N_template.lib.c.m4 > cc/N32.lib.c
+set -e
+set -x
+ cd "$REPO_HOME"/developer || exit 1
-/bin/make -f tool🖉/makefile $@
+ /bin/make -f tool🖉/makefile $@
+set +x
+echo "$(script_fn) done."
-
RT-INCOMMON:=$(REPO_HOME)/tool_shared/third_party/RT-project-share/release
include $(RT-INCOMMON)/make/environment_RT_0
--- /dev/null
+#!/usr/bin/env bash
+script_afp=$(realpath "${BASH_SOURCE[0]}")
+
+# before running this, make library is built and is in the scratchpad directory
+
+# input guards
+
+ env_must_be="developer/tool🖉/env"
+ if [ "$ENV" != "$env_must_be" ]; then
+ echo "$(script_fp):: error: must be run in the $env_must_be environment"
+ exit 1
+ fi
+
+set -e
+set -x
+
+ cd "$REPO_HOME"/developer || exit 1
+
+ if [ ! -d scratchpad ]; then
+ echo "$(script_fp):: no scratchpad directory"
+ exit 1
+ fi
+
+ release_dir=$(release_dir)
+ mkdir -p ${release_dir}
+
+ install_file scratchpad/libN.a ${release_dir} "ug+r"
+
+ # header files
+ install_file {cc,cc🖉}/*.lib.c ${release_dir} "ug+r"
+
+
+set +x
+echo "$(script_fn) done."
+
-#+TITLE: Sectioned Single-File Format
-#+AUTHOR: RT C Documentation Team
-#+OPTIONS: toc:2
-
-* Overview
-The sectioned single-file format provides a structured way to define a library in C,
-keeping all relevant code in a single file while ensuring proper separation between
-interface, implementation, and local static definitions. This approach avoids
-the common “.h + .c” split and instead uses conditional compilation blocks
-to expose only the needed parts.
-
-** Key Definitions
-- =IFACE=: A preprocessor symbol indicating we want to expose the library’s interface.
-- =LOCAL=: A preprocessor symbol indicating we want to expose static (private) helper code.
-- *Implementation (no symbol defined)*: The default, containing the shared implementation
- of the library.
-
-** File Layout
-A typical =.lib.c= file has three sections:
-
-1. *Interface Section*
- Guarded by:
- #+begin_src c
- #ifndef NNN·FACE
- #define NNN·FACE
- // public typedefs, function declarations
- // ...
- #endif
- #+end_src
-
- Here, “=NNN=” is the library name (e.g., =N32=).
- You include this section in another file using:
- #+begin_src c
- #define IFACE
- #include "my_library.lib.c"
- #undef IFACE
- #+end_src
-
-2. *Implementation Section*
- Defined when *neither* =IFACE= nor =LOCAL= is set. This is the main, global
- portion of the library code that will be compiled into an object
- (or archived into a static library):
- #+begin_src c
- #ifdef NNN·IMPLEMENTATION
- // Implementation code
- // ...
- #endif
- #+end_src
-
- The build system compiles this =.lib.c= without =IFACE= or =LOCAL= defined
- to produce the main library object.
-
-3. *Local (Static) Section*
- Guarded by:
- #+begin_src c
- #ifdef LOCAL
- // In-library static declarations or definitions
- // ...
- #endif
- #end_src
-
- This block is typically included after the user code, letting you inline or
- define “private” helper functions that only the user’s code should see
- (in order to be optimized away if not used, for instance).
-
-** Why This Matters
-- *Single Source of Truth*: Instead of maintaining separate header and source files,
- everything for a module is in one place.
-- *Automatic Inlining*: In a traditional setup, “private” static functions would
- live in the .c file and be invisible for cross-module inlining. With the local
- section, the compiler sees everything when the user includes it, enabling more
- optimizations.
-- *Reduced Mismatch Errors*: There’s zero chance your header becomes out-of-sync
- with the .c file because they’re literally the same file, gated by preprocessor macros.
-- *Cleaner Build Scripts*: The library is compiled once with the default macros.
- Tests or command-line programs that need the local portion define =LOCAL= or =IFACE=
- as needed.
-
-** Example Usage
-1. *Library Compilation*
- #+begin_src bash
- # No defines set:
- $CC -c my_library.lib.c -o my_library.lib.o
- #+end_src
-
-2. *Including the Interface in Another Source*
- #+begin_src c
- #define IFACE
- #include "my_library.lib.c"
- #undef IFACE
-
- int main(){
- // Now we have the function declarations from the interface
- // ...
- }
- #+end_src
-
-3. *Including the Local Section*
- #+begin_src c
- #define LOCAL
- #include "my_library.lib.c"
- #undef LOCAL
-
- // We can now use certain static or inline-like helpers
- // ...
- #+end_src
-
-** Makefile Integration
-The provided Makefile snippet shows how to compile =.lib.c= files separately,
-then link them. The key is to define no macros (like =IFACE= or =LOCAL=)
-when building the library, ensuring you get only the “Implementation Section.”
-
-For a command-line interface (CLI) or test harness, you can then define =IFACE=
-or =LOCAL= before including the =.lib.c= to access the library’s interface
-(or private/local stuff) on an as-needed basis.
-
-* Conclusion
-This method elegantly encapsulates a module’s interface, its global
-implementation, and its private helper code in a single file. It integrates
-nicely with existing make-based build systems and fosters a clear separation
-of concerns with minimal overhead or confusion.
--- /dev/null
+/*
+ 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 <stdint.h>
+ #include <stdbool.h>
+ #include <stdarg.h>
+ #include <stdlib.h>
+
+ //----------------------------------------
+ // 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 <stdarg.h>
+ #include <stdlib.h>
+
+ 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
--- /dev/null
+/*
+ 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 <stdint.h>
+ #include <stdbool.h>
+ #include <stdarg.h>
+ #include <stdlib.h>
+
+ //----------------------------------------
+ // 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 <stdarg.h>
+ #include <stdlib.h>
+
+ 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
--- /dev/null
+/*
+ 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 <stdint.h>
+ #include <stdbool.h>
+ #include <stdarg.h>
+ #include <stdlib.h>
+
+ //----------------------------------------
+ // 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 <stdarg.h>
+ #include <stdlib.h>
+
+ 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
--- /dev/null
+/*
+ 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 <stdint.h>
+ #include <stdbool.h>
+ #include <stdarg.h>
+ #include <stdlib.h>
+
+ //----------------------------------------
+ // 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 <stdarg.h>
+ #include <stdlib.h>
+
+ 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
--- /dev/null
+/*
+ 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 <stdint.h>
+ #include <stdbool.h>
+ #include <stdarg.h>
+ #include <stdlib.h>
+
+ //----------------------------------------
+ // 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 <stdarg.h>
+ #include <stdlib.h>
+
+ 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
--- /dev/null
+/*
+ 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 <stdint.h>
+ #include <stdbool.h>
+ #include <stdarg.h>
+ #include <stdlib.h>
+
+ //----------------------------------------
+ // 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 <stdarg.h>
+ #include <stdlib.h>
+
+ 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
--- /dev/null
+/*
+ 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 <stdint.h>
+ #include <stdbool.h>
+ #include <stdarg.h>
+ #include <stdlib.h>
+
+ //----------------------------------------
+ // 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 <stdarg.h>
+ #include <stdlib.h>
+
+ 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
--- /dev/null
+/*
+ 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 <stdint.h>
+ #include <stdbool.h>
+ #include <stdarg.h>
+ #include <stdlib.h>
+
+ //----------------------------------------
+ // 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 <stdarg.h>
+ #include <stdlib.h>
+
+ 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
--- /dev/null
+/*
+ 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 <stdint.h>
+ #include <stdbool.h>
+ #include <stdarg.h>
+ #include <stdlib.h>
+
+ //----------------------------------------
+ // 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 <stdarg.h>
+ #include <stdlib.h>
+
+ 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
--- /dev/null
+#ifndef Mpblock·ENVIRONMENT_H
+#define Mpblock·ENVIRONMENT_H
+
+
+#endif
--- /dev/null
+#include <stdio.h>
+#include <stdbool.h>
+#include <signal.h>
+#include <setjmp.h>
+
+// 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
--- /dev/null
+/*
+
+ A placeholder to see if make etc. is working.
+
+*/
+
+#define IFACE
+#include <stdio.h>
+#include <stdlib.h>
+
+// No need to define IMPLEMENTATION as `main` is one and done.
+
+int main(int argc ,char *argv[] ,char *envp[]){
+ if(argc != 1){
+ fprintf(stderr, "Usage: %s\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ fprintf(stderr, "%s done\n", argv[0]);
+
+ return 0;
+}
--- /dev/null
+*
+!/.gitignore
--- /dev/null
+*
+!/.gitignore
--- /dev/null
+#!/usr/bin/env bash
+script_afp=$(realpath "${BASH_SOURCE[0]}")
+
+# input guards
+
+ env_must_be="tool_shared/bespoke🖉/env"
+ error_bad_env=false
+ error_not_sourced=false
+ if [ "$ENV" != "$env_must_be" ]; then
+ echo "$(script_fp):: error: must be run in the $env_must_be environment"
+ error_bad_env=true
+ fi
+ if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
+ echo "$script_afp:: This script must be sourced, not executed."
+ error_not_sourced=true
+ fi
+ if $error_not_sourced; then exit 1; fi
+ if $error_bad_env; then return 1; fi
+
+
+export ROLE=tester
+
+# so we can do the build
+
+export PATH=\
+"$REPO_HOME"/${ROLE}/tool🖉/\
+:"$PATH"
+
+# misc
+
+ # make .githolder and .gitignore visible
+ alias ls="ls -a"
+
+# some feedback to show all went well
+
+ export PROMPT_DECOR="$PROJECT_$ROLE"
+ export ENV=$(script_fp)
+ echo ENV "$ENV"
+ cd "$REPO_HOME/$ROLE"
+
+
+
--- /dev/null
+#!/usr/bin/env bash
+script_afp=$(realpath "${BASH_SOURCE[0]}")
+
+# input guards
+
+ env_must_be="tester/tool🖉/env"
+ if [ "$ENV" != "$env_must_be" ]; then
+ echo "$(script_fp):: error: must be run in the $env_must_be environment"
+ exit 1
+ fi
+
+set -e
+set -x
+
+cd "$REPO_HOME"/tester || exit 1
+
+/bin/make -f tool🖉/makefile $@
+
+set +x
+echo "$(script_fn) done."
--- /dev/null
+
+
+RT-INCOMMON:=$(REPO_HOME)/tool_shared/third_party/RT-project-share/release
+
+include $(RT-INCOMMON)/make/environment_RT_0
+
+CFLAGS+=-Werror -include "$(RT-INCOMMON)/make/RT_0.h"
+LINKFLAGS+= -l$(PROJECT)
+LIBFILE=$(LIBDIR)/lib$(PROJECT).a
+
+include $(RT-INCOMMON)/make/targets
+-include $(DEPFILE)
+
+
+
+
+
+
--- /dev/null
+#!/usr/bin/env bash
+script_afp=$(realpath "${BASH_SOURCE[0]}")
+
+# before running this script, gather everything needed for the release on the scratchpad
+
+# input guards
+
+ env_must_be="tester/tool🖉/env"
+ if [ "$ENV" != "$env_must_be" ]; then
+ echo "$(script_fp):: error: must be run in the $env_must_be environment"
+ exit 1
+ fi
+
+set -e
+set -x
+
+ cd "$REPO_HOME"/tester || exit 1
+
+ if [ ! -d scratchpad ]; then
+ echo "$(script_fp):: no scratchpad directory"
+ exit 1
+ fi
+
+ release_dir=$(release_dir)
+
+ if [ ! -d ${release_dir} ]; then
+ echo "$(script_fp):: no release directory to pull install files from"
+ exit 1
+ fi
+
+
+ cp ${release_dir}/libN.a scratchpad || true
+ cp ${release_dir}/*.lib.c cc || true
+
+set +x
+echo "$(script_fn) done."
+
--- /dev/null
+#!/usr/bin/env bash
+script_afp=$(realpath "${BASH_SOURCE[0]}")
+
+# release test machine code
+
+# input guards
+
+ env_must_be="tester/tool🖉/env"
+ if [ "$ENV" != "$env_must_be" ]; then
+ echo "$(script_fp):: error: must be run in the $env_must_be environment"
+ exit 1
+ fi
+
+set -e
+set -x
+
+ cd "$REPO_HOME"/tester || exit 1
+
+ if [ ! -d scratchpad ]; then
+ echo "$(script_fp):: no scratchpad directory"
+ exit 1
+ fi
+
+ release_dir=$(release_dir)
+
+ if [ ! -d ${release_dir} ]; then
+ echo "$(script_fp):: no release directory to pull install files from"
+ exit 1
+ fi
+
+ install_file machine/* ${release_dir} "ug+rx"
+
+
+
+
+set +x
+echo "$(script_fn) done."
+
export -f script_adp script_fn script_dp script_fp
+#--------------------------------------------------------------------------------
+# used by release scripts
+#
+
+ install_file() {
+ if [ "$#" -lt 3 ]; then
+ echo "Usage: install_file <source1> <source2> ... <target_dir> <permissions>"
+ return 1
+ fi
+
+ perms="${@: -1}" # Last argument is permissions
+ target_dp="${@: -2:1}" # Second-to-last argument is the target directory
+ sources=("${@:1:$#-2}") # All other arguments are source files
+
+ if [ ! -d "$target_dp" ]; then
+ echo "Error: Target directory '$target_dp' does not exist."
+ return 1
+ fi
+
+ for source_fp in "${sources[@]}"; do
+ if [ ! -f "$source_fp" ]; then
+ echo "Error: Source file '$source_fp' does not exist."
+ return 1
+ fi
+
+ target_file="$target_dp/$(basename "$source_fp")"
+
+ if ! install -m "$perms" "$source_fp" "$target_file"; then
+ echo "Error: Failed to install $(basename "$source_fp") to $target_dp"
+ return 1
+ else
+ echo "Installed $(basename "$source_fp") to $target_dp with permissions $perms"
+ fi
+ done
+ }
+
+ export -f install_file
+
# --------------------------------------------------------------------------------
# closing
#