From: Thomas Walker Lynch Date: Mon, 17 Feb 2025 14:02:33 +0000 (+0000) Subject: test flow working X-Git-Url: https://git.reasoningtechnology.com/style/static/git-favicon.png?a=commitdiff_plain;h=768ebca806ef17d506c969a56ed9db8ca98e970a;p=N test flow working --- diff --git a/developer/python/N64.lib.c b/developer/python/N64.lib.c deleted file mode 100644 index 8827b35..0000000 --- a/developer/python/N64.lib.c +++ /dev/null @@ -1,74 +0,0 @@ -#define N64·DEBUG - -#ifndef FACE - #define N64·IMPLEMENTATION - #define FACE -#endif - -#ifndef N64·FACE -#define N64·FACE - - #include - #include - #include - #include - - 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 - #include - - // 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 diff --git a/developer/python/fill_template b/developer/python/fill_template index bf73da9..f22e5cc 100755 --- a/developer/python/fill_template +++ b/developer/python/fill_template @@ -1,27 +1,43 @@ -#+BEGIN_SRC python #!/usr/bin/env python3 -from get_template import get_template +import sys +from template_N import N 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 +def write(code ,basename): + filepath = "../cc/" + basename + ".lib.c" + with open(filepath, "w") as f: + f.write(code) + print("Generated " + filepath) + + +def main(): + """ + generates `.c` source code from templates + """ + + #---------------------------------------- + # N32 made from 1 digit, of 32 bits. + + type_name = "N32" + digit_type = "uint32_t" + digit_extent = 0 + + constants_block = make_constants_block(type_name, digit_type, digit_extent) + code = N(type_name ,digit_type ,digit_extent ,constants_block) + write(code ,type_name); + + #---------------------------------------- + # N32 made from 4 digits, each 8 bits. + + type_name = "N32_4x8" + digit_type = "uint8_t" + digit_extent = 3 + + constants_block = make_constants_block(type_name, digit_type, digit_extent) + code = N(type_name ,digit_type ,digit_extent ,constants_block) + write(code ,type_name); + +if __name__ == "__main__": + main() + diff --git a/developer/python/gen_N32_8_by_4.py b/developer/python/gen_N32_8_by_4.py deleted file mode 100755 index 22ef30e..0000000 --- a/developer/python/gen_N32_8_by_4.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python3 - -import sys -from fill_template import fill_template - -def main(): - """ - Example: generate an N.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() diff --git a/developer/python/gen_n64_lib.py b/developer/python/gen_n64_lib.py deleted file mode 100755 index 6288b1d..0000000 --- a/developer/python/gen_n64_lib.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/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() diff --git a/developer/python/get_template.py b/developer/python/get_template.py deleted file mode 100755 index a25336b..0000000 --- a/developer/python/get_template.py +++ /dev/null @@ -1,65 +0,0 @@ -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 - #include - #include - #include - - 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 - #include - - // 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 -''' diff --git a/developer/python/template_N.py b/developer/python/template_N.py new file mode 100644 index 0000000..45a4b9e --- /dev/null +++ b/developer/python/template_N.py @@ -0,0 +1,472 @@ +def N(namespace: str ,digit_type: str ,digit_extent: int ,constants_block: str) -> str: + """ + Returns a source code file for cc to munch on + """ + template = template_N() + code = template.format( + NAMESPACE = namespace + ,DIGIT_TYPE = digit_type + ,DIGIT_EXTENT = digit_extent + ,CONSTANTS_BLOCK = constants_block + ) + return code + +def template_N(): + return r'''/* + 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 {NAMESPACE}·DEBUG + +#ifndef FACE +#define {NAMESPACE}·IMPLEMENTATION +#define FACE +#endif + +//-------------------------------------------------------------------------------- +// Interface + +#ifndef {NAMESPACE}·FACE +#define {NAMESPACE}·FACE + + #include + #include + #include + #include + + //---------------------------------------- + // Instance Data (Declaration Only) + + typedef uint32_t Extent; + typedef uint32_t Digit; + + typedef struct {NAMESPACE}·T {NAMESPACE}·T; + + extern {NAMESPACE}·T *{NAMESPACE}·zero; + extern {NAMESPACE}·T *{NAMESPACE}·one; + extern {NAMESPACE}·T *{NAMESPACE}·all_one_bit; + extern {NAMESPACE}·T *{NAMESPACE}·lsb; + extern {NAMESPACE}·T *{NAMESPACE}·msb; + + //---------------------------------------- + // Return/Error Status and handlers + + typedef enum{{ + {NAMESPACE}·Status·ok = 0 + ,{NAMESPACE}·Status·overflow = 1 + ,{NAMESPACE}·Status·accumulator1_overflow = 2 + ,{NAMESPACE}·Status·carry = 3 + ,{NAMESPACE}·Status·borrow = 4 + ,{NAMESPACE}·Status·undefined_divide_by_zero = 5 + ,{NAMESPACE}·Status·undefined_modulus_zero = 6 + ,{NAMESPACE}·Status·gt_max_shift_count = 7 + ,{NAMESPACE}·Status·spill_eq_operand = 8 // not currently signaled, result will be spill value + ,{NAMESPACE}·Status·one_word_product = 9 + ,{NAMESPACE}·Status·two_word_product = 10 + }} {NAMESPACE}·Status; + + typedef enum{{ + {NAMESPACE}·Order_lt = -1 + ,{NAMESPACE}·Order_eq = 0 + ,{NAMESPACE}·Order_gt = 1 + }} {NAMESPACE}·Order; + + typedef {NAMESPACE}·T *( *{NAMESPACE}·Allocate_MemoryFault )(Extent); + + //---------------------------------------- + // Interface + + typedef struct{{ + + {NAMESPACE}·T *(*allocate_array_zero)(Extent, {NAMESPACE}·Allocate_MemoryFault); + {NAMESPACE}·T *(*allocate_array)(Extent, {NAMESPACE}·Allocate_MemoryFault); + void (*deallocate)({NAMESPACE}·T*); + + void (*copy)({NAMESPACE}·T*, {NAMESPACE}·T*); + void (*bit_and)({NAMESPACE}·T*, {NAMESPACE}·T*, {NAMESPACE}·T*); + void (*bit_or)({NAMESPACE}·T*, {NAMESPACE}·T*, {NAMESPACE}·T*); + void (*bit_complement)({NAMESPACE}·T*, {NAMESPACE}·T*); + void (*bit_twos_complement)({NAMESPACE}·T*, {NAMESPACE}·T*); + {NAMESPACE}·Order (*compare)({NAMESPACE}·T*, {NAMESPACE}·T*); + bool (*lt)({NAMESPACE}·T*, {NAMESPACE}·T*); + bool (*gt)({NAMESPACE}·T*, {NAMESPACE}·T*); + bool (*eq)({NAMESPACE}·T*, {NAMESPACE}·T*); + bool (*eq_zero)({NAMESPACE}·T*); + {NAMESPACE}·Status (*accumulate)({NAMESPACE}·T *accumulator1 ,{NAMESPACE}·T *accumulator0 ,...); + {NAMESPACE}·Status (*add)({NAMESPACE}·T*, {NAMESPACE}·T*, {NAMESPACE}·T*); + bool (*increment)({NAMESPACE}·T *a); + {NAMESPACE}·Status (*subtract)({NAMESPACE}·T*, {NAMESPACE}·T*, {NAMESPACE}·T*); + {NAMESPACE}·Status (*multiply)({NAMESPACE}·T*, {NAMESPACE}·T*, {NAMESPACE}·T*, {NAMESPACE}·T*); + {NAMESPACE}·Status (*divide)({NAMESPACE}·T*, {NAMESPACE}·T*, {NAMESPACE}·T*, {NAMESPACE}·T*); + {NAMESPACE}·Status (*modulus)({NAMESPACE}·T*, {NAMESPACE}·T*, {NAMESPACE}·T*); + {NAMESPACE}·Status (*shift_left)(Extent, {NAMESPACE}·T*, {NAMESPACE}·T*, {NAMESPACE}·T*); + {NAMESPACE}·Status (*shift_right)(Extent, {NAMESPACE}·T*, {NAMESPACE}·T*, {NAMESPACE}·T*); + {NAMESPACE}·Status (*arithmetic_shift_right)(Extent, {NAMESPACE}·T*, {NAMESPACE}·T*); + + {NAMESPACE}·T* (*access)({NAMESPACE}·T*, Extent); + void (*from_uint32)({NAMESPACE}·T *destination ,uint32_t value); + }} {NAMESPACE}·Λ; + + Local const {NAMESPACE}·Λ {NAMESPACE}·λ; // initialized in the LOCAL section + +#endif + +//-------------------------------------------------------------------------------- +// Implementation + +#ifdef {NAMESPACE}·IMPLEMENTATION + + // this part goes into the library + #ifndef LOCAL + + #include + #include + + struct {NAMESPACE}·T{{ + Digit d0; + }}; + + {NAMESPACE}·T {NAMESPACE}·constant[4] = {{ + {{.d0 = 0}}, + {{.d0 = 1}}, + {{.d0 = ~(uint32_t)0}}, + {{.d0 = 1 << 31}} + }}; + + {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]; + {NAMESPACE}·T *{NAMESPACE}·lsb = &{NAMESPACE}·constant[1]; + + // the allocate an array of N32 + {NAMESPACE}·T *{NAMESPACE}·allocate_array(Extent extent ,{NAMESPACE}·Allocate_MemoryFault memory_fault){{ + {NAMESPACE}·T *instance = malloc((extent + 1) * sizeof({NAMESPACE}·T) ); + if(!instance){{ + return memory_fault ? memory_fault(extent) : NULL; + }} + return instance; + }} + + {NAMESPACE}·T *{NAMESPACE}·allocate_array_zero(Extent extent ,{NAMESPACE}·Allocate_MemoryFault memory_fault){{ + {NAMESPACE}·T *instance = calloc( extent + 1 ,sizeof({NAMESPACE}·T) ); + if(!instance){{ + return memory_fault ? memory_fault(extent) : NULL; + }} + return instance; + }} + + void {NAMESPACE}·deallocate({NAMESPACE}·T *unencumbered){{ + free(unencumbered); + }} + + #endif + + // This part is included after the library user's code + #ifdef LOCAL + + // instance + + struct {NAMESPACE}·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 {NAMESPACE}·T {NAMESPACE}·t[4]; + + + // allocation + + extern {NAMESPACE}·T *{NAMESPACE}·allocate_array(Extent, {NAMESPACE}·Allocate_MemoryFault); + extern {NAMESPACE}·T *{NAMESPACE}·allocate_array_zero(Extent, {NAMESPACE}·Allocate_MemoryFault); + extern void {NAMESPACE}·deallocate({NAMESPACE}·T *); + + // so the user can access numbers in an array allocation + Local {NAMESPACE}·T* {NAMESPACE}·access({NAMESPACE}·T *array ,Extent index){{ + return &array[index]; + }} + + Local void {NAMESPACE}·from_uint32({NAMESPACE}·T *destination ,uint32_t value){{ + if(destination == NULL) return; + destination->d0 = value; + }} + + // copy, convenience copy + + Local void {NAMESPACE}·copy({NAMESPACE}·T *destination ,{NAMESPACE}·T *source){{ + if(source == destination) return; // that was easy! + *destination = *source; + }} + + Local void {NAMESPACE}·set_to_zero({NAMESPACE}·T *instance){{ + instance->d0 = 0; + }} + + Local void {NAMESPACE}·set_to_one({NAMESPACE}·T *instance){{ + instance->d0 = 1; + }} + + // bit operations + + Local void {NAMESPACE}·bit_and({NAMESPACE}·T *result, {NAMESPACE}·T *a, {NAMESPACE}·T *b){{ + result->d0 = a->d0 & b->d0; + }} + + // result can be one of the operands + Local void {NAMESPACE}·bit_or({NAMESPACE}·T *result, {NAMESPACE}·T *a, {NAMESPACE}·T *b){{ + result->d0 = a->d0 | b->d0; + }} + + // result can the same as the operand + Local void {NAMESPACE}·bit_complement({NAMESPACE}·T *result, {NAMESPACE}·T *a){{ + result->d0 = ~a->d0; + }} + + // result can the same as the operand + Local void {NAMESPACE}·bit_twos_complement({NAMESPACE}·T *result ,{NAMESPACE}·T *a){{ + result->d0 = ~a->d0 + 1; + }} + + // test functions + + Local {NAMESPACE}·Order {NAMESPACE}·compare({NAMESPACE}·T *a, {NAMESPACE}·T *b){{ + if(a->d0 < b->d0) return {NAMESPACE}·Order_lt; + if(a->d0 > b->d0) return {NAMESPACE}·Order_gt; + return {NAMESPACE}·Order_eq; + }} + + Local bool {NAMESPACE}·lt({NAMESPACE}·T *a ,{NAMESPACE}·T *b){{ + return a->d0 < b->d0; + }} + + Local bool {NAMESPACE}·gt({NAMESPACE}·T *a ,{NAMESPACE}·T *b){{ + return a->d0 > b->d0; + }} + + Local bool {NAMESPACE}·eq({NAMESPACE}·T *a ,{NAMESPACE}·T *b){{ + return a->d0 == b->d0; + }} + + Local bool {NAMESPACE}·eq_zero({NAMESPACE}·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 {NAMESPACE}·Status·accumulator1_overflow + // + // When accumulator1 and accumulator0 point to the same location, the result is the accumulator1 value. + Local {NAMESPACE}·Status {NAMESPACE}·accumulate({NAMESPACE}·T *accumulator1 ,{NAMESPACE}·T *accumulator0 ,...){{ + + va_list args; + va_start(args ,accumulator0); + uint32_t sum = accumulator0->d0; + uint32_t carry = 0; + {NAMESPACE}·T *current; + + while( (current = va_arg(args ,{NAMESPACE}·T *)) ){{ + sum += current->d0; + if(sum < current->d0){{ // Accumulator1 into carry + (carry)++; + if(carry == 0){{ + va_end(args); + return {NAMESPACE}·Status·accumulator1_overflow; + }} + }} + }} + va_end(args); + + // wipes out prior value of accumulator1 + accumulator1->d0 = carry; + + return {NAMESPACE}·Status·ok; + }} + + Local {NAMESPACE}·Status {NAMESPACE}·add({NAMESPACE}·T *sum ,{NAMESPACE}·T *a ,{NAMESPACE}·T *b){{ + uint64_t result = (uint64_t)a->d0 + (uint64_t)b->d0; + sum->d0 = (uint32_t)result; + return (result >> 32) ? {NAMESPACE}·Status·carry : {NAMESPACE}·Status·ok; + }} + + Local bool {NAMESPACE}·increment({NAMESPACE}·T *a){{ + a->d0++; + return a->d0 == 0; + }} + + Local {NAMESPACE}·Status {NAMESPACE}·subtract({NAMESPACE}·T *difference ,{NAMESPACE}·T *a ,{NAMESPACE}·T *b){{ + uint64_t diff = (uint64_t) a->d0 - (uint64_t) b->d0; + difference->d0 = (uint32_t)diff; + return (diff > a->d0) ? {NAMESPACE}·Status·borrow : {NAMESPACE}·Status·ok; + }} + + + Local {NAMESPACE}·Status {NAMESPACE}·multiply({NAMESPACE}·T *product1 ,{NAMESPACE}·T *product0 ,{NAMESPACE}·T *a ,{NAMESPACE}·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 {NAMESPACE}·Status·one_word_product; + return {NAMESPACE}·Status·two_word_product; + }} + + Local {NAMESPACE}·Status {NAMESPACE}·divide({NAMESPACE}·T *remainder ,{NAMESPACE}·T *quotient ,{NAMESPACE}·T *a ,{NAMESPACE}·T *b){{ + if(b->d0 == 0) return {NAMESPACE}·Status·undefined_divide_by_zero; + + quotient->d0 = a->d0 / b->d0; + remainder->d0 = a->d0 - (quotient->d0 * b->d0); + + return {NAMESPACE}·Status·ok; + }} + + Local {NAMESPACE}·Status {NAMESPACE}·modulus({NAMESPACE}·T *remainder ,{NAMESPACE}·T *a ,{NAMESPACE}·T *b){{ + if(b->d0 == 0) return {NAMESPACE}·Status·undefined_modulus_zero; + uint32_t quotient = a->d0 / b->d0; + remainder->d0 = a->d0 - (quotient * b->d0); + return {NAMESPACE}·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 {NAMESPACE}·Status {NAMESPACE}·shift + ( + uint32_t shift_count + ,{NAMESPACE}·T *spill + ,{NAMESPACE}·T *operand + ,{NAMESPACE}·T *fill + ,ShiftOp shift_op + ,ShiftOp complement_shift_op + ){{ + + // If no result is needed, return immediately. + if(operand == NULL && spill == NULL) return {NAMESPACE}·Status·ok; + + // Treat NULL operand as zero. + if(operand == NULL){{ + operand = &{NAMESPACE}·t[0]; + {NAMESPACE}·copy(operand, {NAMESPACE}·zero); + }} + + // Shifting more than one word breaks our fill/spill model. + if(shift_count > 31) return {NAMESPACE}·Status·gt_max_shift_count; + + // The given operand is still required after it is modified, so we copy it. + {NAMESPACE}·T *given_operand = &{NAMESPACE}·t[1]; + {NAMESPACE}·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)); + {NAMESPACE}·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 {NAMESPACE}·Status·ok; + }} + + // Define concrete shift functions using valid C function pointers + Local {NAMESPACE}·Status + {NAMESPACE}·shift_left(uint32_t shift_count, {NAMESPACE}·T *spill, {NAMESPACE}·T *operand, {NAMESPACE}·T *fill){{ + return {NAMESPACE}·shift(shift_count, spill, operand, fill, shift_left_op, shift_right_op); + }} + + Local {NAMESPACE}·Status + {NAMESPACE}·shift_right(uint32_t shift_count, {NAMESPACE}·T *spill, {NAMESPACE}·T *operand, {NAMESPACE}·T *fill){{ + return {NAMESPACE}·shift(shift_count, spill, operand, fill, shift_right_op, shift_left_op); + }} + + Local {NAMESPACE}·Status + {NAMESPACE}·arithmetic_shift_right(uint32_t shift_count, {NAMESPACE}·T *operand, {NAMESPACE}·T *spill){{ + + // Guard against excessive shift counts + if(shift_count > 31) return {NAMESPACE}·Status·gt_max_shift_count; + + // A NULL operand is treated as zero + if(operand == NULL){{ + operand = &{NAMESPACE}·t[0]; + {NAMESPACE}·copy(operand, {NAMESPACE}·zero); + }} + + // Pick the fill value based on the sign bit + {NAMESPACE}·T *fill = (operand->d0 & 0x80000000) ? {NAMESPACE}·all_one_bit : {NAMESPACE}·zero; + + // Call shift_right with the appropriate fill + return {NAMESPACE}·shift_right(shift_count, spill, operand, fill); + }} + + Local const {NAMESPACE}·Λ {NAMESPACE}·λ = {{ + + .allocate_array = {NAMESPACE}·allocate_array + ,.allocate_array_zero = {NAMESPACE}·allocate_array_zero + ,.deallocate = {NAMESPACE}·deallocate + + ,.copy = {NAMESPACE}·copy + ,.bit_and = {NAMESPACE}·bit_and + ,.bit_or = {NAMESPACE}·bit_or + ,.bit_complement = {NAMESPACE}·bit_complement + ,.bit_twos_complement = {NAMESPACE}·bit_twos_complement + ,.compare = {NAMESPACE}·compare + ,.lt = {NAMESPACE}·lt + ,.gt = {NAMESPACE}·gt + ,.eq = {NAMESPACE}·eq + ,.eq_zero = {NAMESPACE}·eq_zero + ,.accumulate = {NAMESPACE}·accumulate + ,.add = {NAMESPACE}·add + ,.increment = {NAMESPACE}·increment + ,.subtract = {NAMESPACE}·subtract + ,.multiply = {NAMESPACE}·multiply + ,.divide = {NAMESPACE}·divide + ,.modulus = {NAMESPACE}·modulus + ,.shift_left = {NAMESPACE}·shift_left + ,.shift_right = {NAMESPACE}·shift_right + ,.arithmetic_shift_right = {NAMESPACE}·arithmetic_shift_right + + ,.access = {NAMESPACE}·access + ,.from_uint32 = {NAMESPACE}·from_uint32 + }}; + + #endif + +#endif +''' diff --git "a/developer/tool\360\237\226\211/clean" "b/developer/tool\360\237\226\211/clean" index cbb86b8..48d4f35 100755 --- "a/developer/tool\360\237\226\211/clean" +++ "b/developer/tool\360\237\226\211/clean" @@ -15,8 +15,11 @@ set -x cd "$REPO_HOME"/developer || exit 1 -# remove library pulled from release and other scratchpad files - rm_na scratchpad/{makefile-cc.deps,*.o} || true +# remove synthesized .c files + rm_na cc/* + +# remove object files, deps file, library, and whatever else is on the scratchpad if anything + rm_na scratchpad/* || true # remove built executables rm_na -f machine/* || true diff --git "a/developer/tool\360\237\226\211/make" "b/developer/tool\360\237\226\211/make" index 0df0712..cfc2138 100755 --- "a/developer/tool\360\237\226\211/make" +++ "b/developer/tool\360\237\226\211/make" @@ -14,6 +14,9 @@ set -x cd "$REPO_HOME"/developer || exit 1 + pushd python + ./fill_template + popd /bin/make -f tool🖉/makefile $@ set +x diff --git a/release/x86_64/fedora41/glibc_2.40/N32.lib.c b/release/x86_64/fedora41/glibc_2.40/N32.lib.c new file mode 100644 index 0000000..fba3c21 --- /dev/null +++ b/release/x86_64/fedora41/glibc_2.40/N32.lib.c @@ -0,0 +1,457 @@ +/* + N32 - a processor native type + + For binary operations: a op b -> c + + See the document on the proper use of the Natural types. + + On the subject of multiple pointers indicating the same location in memory: + + When a routine has multiple results, and one or more of the result location + pointers point to the same storage, the routine will either return an error + status, or have defined behavior. + + When a routine has multiple operands, in any combination, those + pointers can point to the same location, and the routine will + function as advertised. + + When an operand functions as both an input and a result, perhaps due + to a result pointer pointing to the same place as an operand + pointer, the routine will function as advertised. (Internally the + routine might make a temporary copy of the operand to accomplish + this.) + +*/ + +#define N32·DEBUG + +#ifndef FACE +#define N32·IMPLEMENTATION +#define FACE +#endif + +//-------------------------------------------------------------------------------- +// Interface + +#ifndef N32·FACE +#define N32·FACE + + #include + #include + #include + #include + + //---------------------------------------- + // Instance Data (Declaration Only) + + typedef uint32_t Extent; + typedef uint32_t Digit; + + typedef struct N32·T N32·T; + + extern N32·T *N32·zero; + extern N32·T *N32·one; + extern N32·T *N32·all_one_bit; + extern N32·T *N32·lsb; + extern N32·T *N32·msb; + + //---------------------------------------- + // Return/Error Status and handlers + + typedef enum{ + N32·Status·ok = 0 + ,N32·Status·overflow = 1 + ,N32·Status·accumulator1_overflow = 2 + ,N32·Status·carry = 3 + ,N32·Status·borrow = 4 + ,N32·Status·undefined_divide_by_zero = 5 + ,N32·Status·undefined_modulus_zero = 6 + ,N32·Status·gt_max_shift_count = 7 + ,N32·Status·spill_eq_operand = 8 // not currently signaled, result will be spill value + ,N32·Status·one_word_product = 9 + ,N32·Status·two_word_product = 10 + } N32·Status; + + typedef enum{ + N32·Order_lt = -1 + ,N32·Order_eq = 0 + ,N32·Order_gt = 1 + } N32·Order; + + typedef N32·T *( *N32·Allocate_MemoryFault )(Extent); + + //---------------------------------------- + // Interface + + typedef struct{ + + N32·T *(*allocate_array_zero)(Extent, N32·Allocate_MemoryFault); + N32·T *(*allocate_array)(Extent, N32·Allocate_MemoryFault); + void (*deallocate)(N32·T*); + + void (*copy)(N32·T*, N32·T*); + void (*bit_and)(N32·T*, N32·T*, N32·T*); + void (*bit_or)(N32·T*, N32·T*, N32·T*); + void (*bit_complement)(N32·T*, N32·T*); + void (*bit_twos_complement)(N32·T*, N32·T*); + N32·Order (*compare)(N32·T*, N32·T*); + bool (*lt)(N32·T*, N32·T*); + bool (*gt)(N32·T*, N32·T*); + bool (*eq)(N32·T*, N32·T*); + bool (*eq_zero)(N32·T*); + N32·Status (*accumulate)(N32·T *accumulator1 ,N32·T *accumulator0 ,...); + N32·Status (*add)(N32·T*, N32·T*, N32·T*); + bool (*increment)(N32·T *a); + N32·Status (*subtract)(N32·T*, N32·T*, N32·T*); + N32·Status (*multiply)(N32·T*, N32·T*, N32·T*, N32·T*); + N32·Status (*divide)(N32·T*, N32·T*, N32·T*, N32·T*); + N32·Status (*modulus)(N32·T*, N32·T*, N32·T*); + N32·Status (*shift_left)(Extent, N32·T*, N32·T*, N32·T*); + N32·Status (*shift_right)(Extent, N32·T*, N32·T*, N32·T*); + N32·Status (*arithmetic_shift_right)(Extent, N32·T*, N32·T*); + + N32·T* (*access)(N32·T*, Extent); + void (*from_uint32)(N32·T *destination ,uint32_t value); + } N32·Λ; + + Local const N32·Λ N32·λ; // initialized in the LOCAL section + +#endif + +//-------------------------------------------------------------------------------- +// Implementation + +#ifdef N32·IMPLEMENTATION + + // this part goes into the library + #ifndef LOCAL + + #include + #include + + struct N32·T{ + Digit d0; + }; + + N32·T N32·constant[4] = { + {.d0 = 0}, + {.d0 = 1}, + {.d0 = ~(uint32_t)0}, + {.d0 = 1 << 31} + }; + + N32·T *N32·zero = &N32·constant[0]; + N32·T *N32·one = &N32·constant[1]; + N32·T *N32·all_one_bit = &N32·constant[2]; + N32·T *N32·msb = &N32·constant[3]; + N32·T *N32·lsb = &N32·constant[1]; + + // the allocate an array of N32 + N32·T *N32·allocate_array(Extent extent ,N32·Allocate_MemoryFault memory_fault){ + N32·T *instance = malloc((extent + 1) * sizeof(N32·T) ); + if(!instance){ + return memory_fault ? memory_fault(extent) : NULL; + } + return instance; + } + + N32·T *N32·allocate_array_zero(Extent extent ,N32·Allocate_MemoryFault memory_fault){ + N32·T *instance = calloc( extent + 1 ,sizeof(N32·T) ); + if(!instance){ + return memory_fault ? memory_fault(extent) : NULL; + } + return instance; + } + + void N32·deallocate(N32·T *unencumbered){ + free(unencumbered); + } + + #endif + + // This part is included after the library user's code + #ifdef LOCAL + + // instance + + struct N32·T{ + Digit d0; + }; + + // temporary variables + // making these LOCAL rather than reserving one block in the library is thread safe + // allocating a block once is more efficient + // library code writes these, they are not on the interface + + Local N32·T N32·t[4]; + + + // allocation + + extern N32·T *N32·allocate_array(Extent, N32·Allocate_MemoryFault); + extern N32·T *N32·allocate_array_zero(Extent, N32·Allocate_MemoryFault); + extern void N32·deallocate(N32·T *); + + // so the user can access numbers in an array allocation + Local N32·T* N32·access(N32·T *array ,Extent index){ + return &array[index]; + } + + Local void N32·from_uint32(N32·T *destination ,uint32_t value){ + if(destination == NULL) return; + destination->d0 = value; + } + + // copy, convenience copy + + Local void N32·copy(N32·T *destination ,N32·T *source){ + if(source == destination) return; // that was easy! + *destination = *source; + } + + Local void N32·set_to_zero(N32·T *instance){ + instance->d0 = 0; + } + + Local void N32·set_to_one(N32·T *instance){ + instance->d0 = 1; + } + + // bit operations + + Local void N32·bit_and(N32·T *result, N32·T *a, N32·T *b){ + result->d0 = a->d0 & b->d0; + } + + // result can be one of the operands + Local void N32·bit_or(N32·T *result, N32·T *a, N32·T *b){ + result->d0 = a->d0 | b->d0; + } + + // result can the same as the operand + Local void N32·bit_complement(N32·T *result, N32·T *a){ + result->d0 = ~a->d0; + } + + // result can the same as the operand + Local void N32·bit_twos_complement(N32·T *result ,N32·T *a){ + result->d0 = ~a->d0 + 1; + } + + // test functions + + Local N32·Order N32·compare(N32·T *a, N32·T *b){ + if(a->d0 < b->d0) return N32·Order_lt; + if(a->d0 > b->d0) return N32·Order_gt; + return N32·Order_eq; + } + + Local bool N32·lt(N32·T *a ,N32·T *b){ + return a->d0 < b->d0; + } + + Local bool N32·gt(N32·T *a ,N32·T *b){ + return a->d0 > b->d0; + } + + Local bool N32·eq(N32·T *a ,N32·T *b){ + return a->d0 == b->d0; + } + + Local bool N32·eq_zero(N32·T *a){ + return a->d0 == 0; + } + + + // arithmetic operations + + // For a large number of summands for the lower precision Natural implementations, for accumulate/add/sub, the 'overflow' operand could overflow and thus this routine will halt and return N32·Status·accumulator1_overflow + // + // When accumulator1 and accumulator0 point to the same location, the result is the accumulator1 value. + Local N32·Status N32·accumulate(N32·T *accumulator1 ,N32·T *accumulator0 ,...){ + + va_list args; + va_start(args ,accumulator0); + uint32_t sum = accumulator0->d0; + uint32_t carry = 0; + N32·T *current; + + while( (current = va_arg(args ,N32·T *)) ){ + sum += current->d0; + if(sum < current->d0){ // Accumulator1 into carry + (carry)++; + if(carry == 0){ + va_end(args); + return N32·Status·accumulator1_overflow; + } + } + } + va_end(args); + + // wipes out prior value of accumulator1 + accumulator1->d0 = carry; + + return N32·Status·ok; + } + + Local N32·Status N32·add(N32·T *sum ,N32·T *a ,N32·T *b){ + uint64_t result = (uint64_t)a->d0 + (uint64_t)b->d0; + sum->d0 = (uint32_t)result; + return (result >> 32) ? N32·Status·carry : N32·Status·ok; + } + + Local bool N32·increment(N32·T *a){ + a->d0++; + return a->d0 == 0; + } + + Local N32·Status N32·subtract(N32·T *difference ,N32·T *a ,N32·T *b){ + uint64_t diff = (uint64_t) a->d0 - (uint64_t) b->d0; + difference->d0 = (uint32_t)diff; + return (diff > a->d0) ? N32·Status·borrow : N32·Status·ok; + } + + + Local N32·Status N32·multiply(N32·T *product1 ,N32·T *product0 ,N32·T *a ,N32·T *b){ + uint64_t product = (uint64_t)a->d0 * (uint64_t)b->d0; + product0->d0 = (uint32_t)product; + product1->d0 = (uint32_t)(product >> 32); + + if(product1->d0 == 0) return N32·Status·one_word_product; + return N32·Status·two_word_product; + } + + Local N32·Status N32·divide(N32·T *remainder ,N32·T *quotient ,N32·T *a ,N32·T *b){ + if(b->d0 == 0) return N32·Status·undefined_divide_by_zero; + + quotient->d0 = a->d0 / b->d0; + remainder->d0 = a->d0 - (quotient->d0 * b->d0); + + return N32·Status·ok; + } + + Local N32·Status N32·modulus(N32·T *remainder ,N32·T *a ,N32·T *b){ + if(b->d0 == 0) return N32·Status·undefined_modulus_zero; + uint32_t quotient = a->d0 / b->d0; + remainder->d0 = a->d0 - (quotient * b->d0); + return N32·Status·ok; + } + + // bit motion + + typedef uint32_t (*ShiftOp)(uint32_t, uint32_t); + + Local uint32_t shift_left_op(uint32_t value, uint32_t amount){ + return value << amount; + } + + Local uint32_t shift_right_op(uint32_t value, uint32_t amount){ + return value >> amount; + } + + // modifies all three of its operands + // in the case of duplicate operands this is the order: first modifies operand, then fill, then spill, + Local N32·Status N32·shift + ( + uint32_t shift_count + ,N32·T *spill + ,N32·T *operand + ,N32·T *fill + ,ShiftOp shift_op + ,ShiftOp complement_shift_op + ){ + + // If no result is needed, return immediately. + if(operand == NULL && spill == NULL) return N32·Status·ok; + + // Treat NULL operand as zero. + if(operand == NULL){ + operand = &N32·t[0]; + N32·copy(operand, N32·zero); + } + + // Shifting more than one word breaks our fill/spill model. + if(shift_count > 31) return N32·Status·gt_max_shift_count; + + // The given operand is still required after it is modified, so we copy it. + N32·T *given_operand = &N32·t[1]; + N32·copy(given_operand, operand); + + // Perform the shift + operand->d0 = shift_op(given_operand->d0, shift_count); + if(fill != NULL){ + fill->d0 = complement_shift_op(fill->d0, (32 - shift_count)); + N32·bit_or(operand, operand, fill); + } + if(spill != NULL){ + spill->d0 = shift_op(spill->d0, shift_count); + spill->d0 += complement_shift_op(given_operand->d0, (32 - shift_count)); + } + + return N32·Status·ok; + } + + // Define concrete shift functions using valid C function pointers + Local N32·Status + N32·shift_left(uint32_t shift_count, N32·T *spill, N32·T *operand, N32·T *fill){ + return N32·shift(shift_count, spill, operand, fill, shift_left_op, shift_right_op); + } + + Local N32·Status + N32·shift_right(uint32_t shift_count, N32·T *spill, N32·T *operand, N32·T *fill){ + return N32·shift(shift_count, spill, operand, fill, shift_right_op, shift_left_op); + } + + Local N32·Status + N32·arithmetic_shift_right(uint32_t shift_count, N32·T *operand, N32·T *spill){ + + // Guard against excessive shift counts + if(shift_count > 31) return N32·Status·gt_max_shift_count; + + // A NULL operand is treated as zero + if(operand == NULL){ + operand = &N32·t[0]; + N32·copy(operand, N32·zero); + } + + // Pick the fill value based on the sign bit + N32·T *fill = (operand->d0 & 0x80000000) ? N32·all_one_bit : N32·zero; + + // Call shift_right with the appropriate fill + return N32·shift_right(shift_count, spill, operand, fill); + } + + Local const N32·Λ N32·λ = { + + .allocate_array = N32·allocate_array + ,.allocate_array_zero = N32·allocate_array_zero + ,.deallocate = N32·deallocate + + ,.copy = N32·copy + ,.bit_and = N32·bit_and + ,.bit_or = N32·bit_or + ,.bit_complement = N32·bit_complement + ,.bit_twos_complement = N32·bit_twos_complement + ,.compare = N32·compare + ,.lt = N32·lt + ,.gt = N32·gt + ,.eq = N32·eq + ,.eq_zero = N32·eq_zero + ,.accumulate = N32·accumulate + ,.add = N32·add + ,.increment = N32·increment + ,.subtract = N32·subtract + ,.multiply = N32·multiply + ,.divide = N32·divide + ,.modulus = N32·modulus + ,.shift_left = N32·shift_left + ,.shift_right = N32·shift_right + ,.arithmetic_shift_right = N32·arithmetic_shift_right + + ,.access = N32·access + ,.from_uint32 = N32·from_uint32 + }; + + #endif + +#endif diff --git a/release/x86_64/fedora41/glibc_2.40/N32_4x8.lib.c b/release/x86_64/fedora41/glibc_2.40/N32_4x8.lib.c new file mode 100644 index 0000000..22cced5 --- /dev/null +++ b/release/x86_64/fedora41/glibc_2.40/N32_4x8.lib.c @@ -0,0 +1,457 @@ +/* + N32 - a processor native type + + For binary operations: a op b -> c + + See the document on the proper use of the Natural types. + + On the subject of multiple pointers indicating the same location in memory: + + When a routine has multiple results, and one or more of the result location + pointers point to the same storage, the routine will either return an error + status, or have defined behavior. + + When a routine has multiple operands, in any combination, those + pointers can point to the same location, and the routine will + function as advertised. + + When an operand functions as both an input and a result, perhaps due + to a result pointer pointing to the same place as an operand + pointer, the routine will function as advertised. (Internally the + routine might make a temporary copy of the operand to accomplish + this.) + +*/ + +#define N32_4x8·DEBUG + +#ifndef FACE +#define N32_4x8·IMPLEMENTATION +#define FACE +#endif + +//-------------------------------------------------------------------------------- +// Interface + +#ifndef N32_4x8·FACE +#define N32_4x8·FACE + + #include + #include + #include + #include + + //---------------------------------------- + // Instance Data (Declaration Only) + + typedef uint32_t Extent; + typedef uint32_t Digit; + + typedef struct N32_4x8·T N32_4x8·T; + + extern N32_4x8·T *N32_4x8·zero; + extern N32_4x8·T *N32_4x8·one; + extern N32_4x8·T *N32_4x8·all_one_bit; + extern N32_4x8·T *N32_4x8·lsb; + extern N32_4x8·T *N32_4x8·msb; + + //---------------------------------------- + // Return/Error Status and handlers + + typedef enum{ + N32_4x8·Status·ok = 0 + ,N32_4x8·Status·overflow = 1 + ,N32_4x8·Status·accumulator1_overflow = 2 + ,N32_4x8·Status·carry = 3 + ,N32_4x8·Status·borrow = 4 + ,N32_4x8·Status·undefined_divide_by_zero = 5 + ,N32_4x8·Status·undefined_modulus_zero = 6 + ,N32_4x8·Status·gt_max_shift_count = 7 + ,N32_4x8·Status·spill_eq_operand = 8 // not currently signaled, result will be spill value + ,N32_4x8·Status·one_word_product = 9 + ,N32_4x8·Status·two_word_product = 10 + } N32_4x8·Status; + + typedef enum{ + N32_4x8·Order_lt = -1 + ,N32_4x8·Order_eq = 0 + ,N32_4x8·Order_gt = 1 + } N32_4x8·Order; + + typedef N32_4x8·T *( *N32_4x8·Allocate_MemoryFault )(Extent); + + //---------------------------------------- + // Interface + + typedef struct{ + + N32_4x8·T *(*allocate_array_zero)(Extent, N32_4x8·Allocate_MemoryFault); + N32_4x8·T *(*allocate_array)(Extent, N32_4x8·Allocate_MemoryFault); + void (*deallocate)(N32_4x8·T*); + + void (*copy)(N32_4x8·T*, N32_4x8·T*); + void (*bit_and)(N32_4x8·T*, N32_4x8·T*, N32_4x8·T*); + void (*bit_or)(N32_4x8·T*, N32_4x8·T*, N32_4x8·T*); + void (*bit_complement)(N32_4x8·T*, N32_4x8·T*); + void (*bit_twos_complement)(N32_4x8·T*, N32_4x8·T*); + N32_4x8·Order (*compare)(N32_4x8·T*, N32_4x8·T*); + bool (*lt)(N32_4x8·T*, N32_4x8·T*); + bool (*gt)(N32_4x8·T*, N32_4x8·T*); + bool (*eq)(N32_4x8·T*, N32_4x8·T*); + bool (*eq_zero)(N32_4x8·T*); + N32_4x8·Status (*accumulate)(N32_4x8·T *accumulator1 ,N32_4x8·T *accumulator0 ,...); + N32_4x8·Status (*add)(N32_4x8·T*, N32_4x8·T*, N32_4x8·T*); + bool (*increment)(N32_4x8·T *a); + N32_4x8·Status (*subtract)(N32_4x8·T*, N32_4x8·T*, N32_4x8·T*); + N32_4x8·Status (*multiply)(N32_4x8·T*, N32_4x8·T*, N32_4x8·T*, N32_4x8·T*); + N32_4x8·Status (*divide)(N32_4x8·T*, N32_4x8·T*, N32_4x8·T*, N32_4x8·T*); + N32_4x8·Status (*modulus)(N32_4x8·T*, N32_4x8·T*, N32_4x8·T*); + N32_4x8·Status (*shift_left)(Extent, N32_4x8·T*, N32_4x8·T*, N32_4x8·T*); + N32_4x8·Status (*shift_right)(Extent, N32_4x8·T*, N32_4x8·T*, N32_4x8·T*); + N32_4x8·Status (*arithmetic_shift_right)(Extent, N32_4x8·T*, N32_4x8·T*); + + N32_4x8·T* (*access)(N32_4x8·T*, Extent); + void (*from_uint32)(N32_4x8·T *destination ,uint32_t value); + } N32_4x8·Λ; + + Local const N32_4x8·Λ N32_4x8·λ; // initialized in the LOCAL section + +#endif + +//-------------------------------------------------------------------------------- +// Implementation + +#ifdef N32_4x8·IMPLEMENTATION + + // this part goes into the library + #ifndef LOCAL + + #include + #include + + struct N32_4x8·T{ + Digit d0; + }; + + N32_4x8·T N32_4x8·constant[4] = { + {.d0 = 0}, + {.d0 = 1}, + {.d0 = ~(uint32_t)0}, + {.d0 = 1 << 31} + }; + + N32_4x8·T *N32_4x8·zero = &N32_4x8·constant[0]; + N32_4x8·T *N32_4x8·one = &N32_4x8·constant[1]; + N32_4x8·T *N32_4x8·all_one_bit = &N32_4x8·constant[2]; + N32_4x8·T *N32_4x8·msb = &N32_4x8·constant[3]; + N32_4x8·T *N32_4x8·lsb = &N32_4x8·constant[1]; + + // the allocate an array of N32 + N32_4x8·T *N32_4x8·allocate_array(Extent extent ,N32_4x8·Allocate_MemoryFault memory_fault){ + N32_4x8·T *instance = malloc((extent + 1) * sizeof(N32_4x8·T) ); + if(!instance){ + return memory_fault ? memory_fault(extent) : NULL; + } + return instance; + } + + N32_4x8·T *N32_4x8·allocate_array_zero(Extent extent ,N32_4x8·Allocate_MemoryFault memory_fault){ + N32_4x8·T *instance = calloc( extent + 1 ,sizeof(N32_4x8·T) ); + if(!instance){ + return memory_fault ? memory_fault(extent) : NULL; + } + return instance; + } + + void N32_4x8·deallocate(N32_4x8·T *unencumbered){ + free(unencumbered); + } + + #endif + + // This part is included after the library user's code + #ifdef LOCAL + + // instance + + struct N32_4x8·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_4x8·T N32_4x8·t[4]; + + + // allocation + + extern N32_4x8·T *N32_4x8·allocate_array(Extent, N32_4x8·Allocate_MemoryFault); + extern N32_4x8·T *N32_4x8·allocate_array_zero(Extent, N32_4x8·Allocate_MemoryFault); + extern void N32_4x8·deallocate(N32_4x8·T *); + + // so the user can access numbers in an array allocation + Local N32_4x8·T* N32_4x8·access(N32_4x8·T *array ,Extent index){ + return &array[index]; + } + + Local void N32_4x8·from_uint32(N32_4x8·T *destination ,uint32_t value){ + if(destination == NULL) return; + destination->d0 = value; + } + + // copy, convenience copy + + Local void N32_4x8·copy(N32_4x8·T *destination ,N32_4x8·T *source){ + if(source == destination) return; // that was easy! + *destination = *source; + } + + Local void N32_4x8·set_to_zero(N32_4x8·T *instance){ + instance->d0 = 0; + } + + Local void N32_4x8·set_to_one(N32_4x8·T *instance){ + instance->d0 = 1; + } + + // bit operations + + Local void N32_4x8·bit_and(N32_4x8·T *result, N32_4x8·T *a, N32_4x8·T *b){ + result->d0 = a->d0 & b->d0; + } + + // result can be one of the operands + Local void N32_4x8·bit_or(N32_4x8·T *result, N32_4x8·T *a, N32_4x8·T *b){ + result->d0 = a->d0 | b->d0; + } + + // result can the same as the operand + Local void N32_4x8·bit_complement(N32_4x8·T *result, N32_4x8·T *a){ + result->d0 = ~a->d0; + } + + // result can the same as the operand + Local void N32_4x8·bit_twos_complement(N32_4x8·T *result ,N32_4x8·T *a){ + result->d0 = ~a->d0 + 1; + } + + // test functions + + Local N32_4x8·Order N32_4x8·compare(N32_4x8·T *a, N32_4x8·T *b){ + if(a->d0 < b->d0) return N32_4x8·Order_lt; + if(a->d0 > b->d0) return N32_4x8·Order_gt; + return N32_4x8·Order_eq; + } + + Local bool N32_4x8·lt(N32_4x8·T *a ,N32_4x8·T *b){ + return a->d0 < b->d0; + } + + Local bool N32_4x8·gt(N32_4x8·T *a ,N32_4x8·T *b){ + return a->d0 > b->d0; + } + + Local bool N32_4x8·eq(N32_4x8·T *a ,N32_4x8·T *b){ + return a->d0 == b->d0; + } + + Local bool N32_4x8·eq_zero(N32_4x8·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_4x8·Status·accumulator1_overflow + // + // When accumulator1 and accumulator0 point to the same location, the result is the accumulator1 value. + Local N32_4x8·Status N32_4x8·accumulate(N32_4x8·T *accumulator1 ,N32_4x8·T *accumulator0 ,...){ + + va_list args; + va_start(args ,accumulator0); + uint32_t sum = accumulator0->d0; + uint32_t carry = 0; + N32_4x8·T *current; + + while( (current = va_arg(args ,N32_4x8·T *)) ){ + sum += current->d0; + if(sum < current->d0){ // Accumulator1 into carry + (carry)++; + if(carry == 0){ + va_end(args); + return N32_4x8·Status·accumulator1_overflow; + } + } + } + va_end(args); + + // wipes out prior value of accumulator1 + accumulator1->d0 = carry; + + return N32_4x8·Status·ok; + } + + Local N32_4x8·Status N32_4x8·add(N32_4x8·T *sum ,N32_4x8·T *a ,N32_4x8·T *b){ + uint64_t result = (uint64_t)a->d0 + (uint64_t)b->d0; + sum->d0 = (uint32_t)result; + return (result >> 32) ? N32_4x8·Status·carry : N32_4x8·Status·ok; + } + + Local bool N32_4x8·increment(N32_4x8·T *a){ + a->d0++; + return a->d0 == 0; + } + + Local N32_4x8·Status N32_4x8·subtract(N32_4x8·T *difference ,N32_4x8·T *a ,N32_4x8·T *b){ + uint64_t diff = (uint64_t) a->d0 - (uint64_t) b->d0; + difference->d0 = (uint32_t)diff; + return (diff > a->d0) ? N32_4x8·Status·borrow : N32_4x8·Status·ok; + } + + + Local N32_4x8·Status N32_4x8·multiply(N32_4x8·T *product1 ,N32_4x8·T *product0 ,N32_4x8·T *a ,N32_4x8·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_4x8·Status·one_word_product; + return N32_4x8·Status·two_word_product; + } + + Local N32_4x8·Status N32_4x8·divide(N32_4x8·T *remainder ,N32_4x8·T *quotient ,N32_4x8·T *a ,N32_4x8·T *b){ + if(b->d0 == 0) return N32_4x8·Status·undefined_divide_by_zero; + + quotient->d0 = a->d0 / b->d0; + remainder->d0 = a->d0 - (quotient->d0 * b->d0); + + return N32_4x8·Status·ok; + } + + Local N32_4x8·Status N32_4x8·modulus(N32_4x8·T *remainder ,N32_4x8·T *a ,N32_4x8·T *b){ + if(b->d0 == 0) return N32_4x8·Status·undefined_modulus_zero; + uint32_t quotient = a->d0 / b->d0; + remainder->d0 = a->d0 - (quotient * b->d0); + return N32_4x8·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_4x8·Status N32_4x8·shift + ( + uint32_t shift_count + ,N32_4x8·T *spill + ,N32_4x8·T *operand + ,N32_4x8·T *fill + ,ShiftOp shift_op + ,ShiftOp complement_shift_op + ){ + + // If no result is needed, return immediately. + if(operand == NULL && spill == NULL) return N32_4x8·Status·ok; + + // Treat NULL operand as zero. + if(operand == NULL){ + operand = &N32_4x8·t[0]; + N32_4x8·copy(operand, N32_4x8·zero); + } + + // Shifting more than one word breaks our fill/spill model. + if(shift_count > 31) return N32_4x8·Status·gt_max_shift_count; + + // The given operand is still required after it is modified, so we copy it. + N32_4x8·T *given_operand = &N32_4x8·t[1]; + N32_4x8·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_4x8·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_4x8·Status·ok; + } + + // Define concrete shift functions using valid C function pointers + Local N32_4x8·Status + N32_4x8·shift_left(uint32_t shift_count, N32_4x8·T *spill, N32_4x8·T *operand, N32_4x8·T *fill){ + return N32_4x8·shift(shift_count, spill, operand, fill, shift_left_op, shift_right_op); + } + + Local N32_4x8·Status + N32_4x8·shift_right(uint32_t shift_count, N32_4x8·T *spill, N32_4x8·T *operand, N32_4x8·T *fill){ + return N32_4x8·shift(shift_count, spill, operand, fill, shift_right_op, shift_left_op); + } + + Local N32_4x8·Status + N32_4x8·arithmetic_shift_right(uint32_t shift_count, N32_4x8·T *operand, N32_4x8·T *spill){ + + // Guard against excessive shift counts + if(shift_count > 31) return N32_4x8·Status·gt_max_shift_count; + + // A NULL operand is treated as zero + if(operand == NULL){ + operand = &N32_4x8·t[0]; + N32_4x8·copy(operand, N32_4x8·zero); + } + + // Pick the fill value based on the sign bit + N32_4x8·T *fill = (operand->d0 & 0x80000000) ? N32_4x8·all_one_bit : N32_4x8·zero; + + // Call shift_right with the appropriate fill + return N32_4x8·shift_right(shift_count, spill, operand, fill); + } + + Local const N32_4x8·Λ N32_4x8·λ = { + + .allocate_array = N32_4x8·allocate_array + ,.allocate_array_zero = N32_4x8·allocate_array_zero + ,.deallocate = N32_4x8·deallocate + + ,.copy = N32_4x8·copy + ,.bit_and = N32_4x8·bit_and + ,.bit_or = N32_4x8·bit_or + ,.bit_complement = N32_4x8·bit_complement + ,.bit_twos_complement = N32_4x8·bit_twos_complement + ,.compare = N32_4x8·compare + ,.lt = N32_4x8·lt + ,.gt = N32_4x8·gt + ,.eq = N32_4x8·eq + ,.eq_zero = N32_4x8·eq_zero + ,.accumulate = N32_4x8·accumulate + ,.add = N32_4x8·add + ,.increment = N32_4x8·increment + ,.subtract = N32_4x8·subtract + ,.multiply = N32_4x8·multiply + ,.divide = N32_4x8·divide + ,.modulus = N32_4x8·modulus + ,.shift_left = N32_4x8·shift_left + ,.shift_right = N32_4x8·shift_right + ,.arithmetic_shift_right = N32_4x8·arithmetic_shift_right + + ,.access = N32_4x8·access + ,.from_uint32 = N32_4x8·from_uint32 + }; + + #endif + +#endif diff --git a/release/x86_64/fedora41/glibc_2.40/libN.a b/release/x86_64/fedora41/glibc_2.40/libN.a index 15d0154..82a1f5c 100644 Binary files a/release/x86_64/fedora41/glibc_2.40/libN.a and b/release/x86_64/fedora41/glibc_2.40/libN.a differ diff --git a/tester/cc/N32.lib.c b/tester/cc/N32.lib.c new file mode 100644 index 0000000..fba3c21 --- /dev/null +++ b/tester/cc/N32.lib.c @@ -0,0 +1,457 @@ +/* + N32 - a processor native type + + For binary operations: a op b -> c + + See the document on the proper use of the Natural types. + + On the subject of multiple pointers indicating the same location in memory: + + When a routine has multiple results, and one or more of the result location + pointers point to the same storage, the routine will either return an error + status, or have defined behavior. + + When a routine has multiple operands, in any combination, those + pointers can point to the same location, and the routine will + function as advertised. + + When an operand functions as both an input and a result, perhaps due + to a result pointer pointing to the same place as an operand + pointer, the routine will function as advertised. (Internally the + routine might make a temporary copy of the operand to accomplish + this.) + +*/ + +#define N32·DEBUG + +#ifndef FACE +#define N32·IMPLEMENTATION +#define FACE +#endif + +//-------------------------------------------------------------------------------- +// Interface + +#ifndef N32·FACE +#define N32·FACE + + #include + #include + #include + #include + + //---------------------------------------- + // Instance Data (Declaration Only) + + typedef uint32_t Extent; + typedef uint32_t Digit; + + typedef struct N32·T N32·T; + + extern N32·T *N32·zero; + extern N32·T *N32·one; + extern N32·T *N32·all_one_bit; + extern N32·T *N32·lsb; + extern N32·T *N32·msb; + + //---------------------------------------- + // Return/Error Status and handlers + + typedef enum{ + N32·Status·ok = 0 + ,N32·Status·overflow = 1 + ,N32·Status·accumulator1_overflow = 2 + ,N32·Status·carry = 3 + ,N32·Status·borrow = 4 + ,N32·Status·undefined_divide_by_zero = 5 + ,N32·Status·undefined_modulus_zero = 6 + ,N32·Status·gt_max_shift_count = 7 + ,N32·Status·spill_eq_operand = 8 // not currently signaled, result will be spill value + ,N32·Status·one_word_product = 9 + ,N32·Status·two_word_product = 10 + } N32·Status; + + typedef enum{ + N32·Order_lt = -1 + ,N32·Order_eq = 0 + ,N32·Order_gt = 1 + } N32·Order; + + typedef N32·T *( *N32·Allocate_MemoryFault )(Extent); + + //---------------------------------------- + // Interface + + typedef struct{ + + N32·T *(*allocate_array_zero)(Extent, N32·Allocate_MemoryFault); + N32·T *(*allocate_array)(Extent, N32·Allocate_MemoryFault); + void (*deallocate)(N32·T*); + + void (*copy)(N32·T*, N32·T*); + void (*bit_and)(N32·T*, N32·T*, N32·T*); + void (*bit_or)(N32·T*, N32·T*, N32·T*); + void (*bit_complement)(N32·T*, N32·T*); + void (*bit_twos_complement)(N32·T*, N32·T*); + N32·Order (*compare)(N32·T*, N32·T*); + bool (*lt)(N32·T*, N32·T*); + bool (*gt)(N32·T*, N32·T*); + bool (*eq)(N32·T*, N32·T*); + bool (*eq_zero)(N32·T*); + N32·Status (*accumulate)(N32·T *accumulator1 ,N32·T *accumulator0 ,...); + N32·Status (*add)(N32·T*, N32·T*, N32·T*); + bool (*increment)(N32·T *a); + N32·Status (*subtract)(N32·T*, N32·T*, N32·T*); + N32·Status (*multiply)(N32·T*, N32·T*, N32·T*, N32·T*); + N32·Status (*divide)(N32·T*, N32·T*, N32·T*, N32·T*); + N32·Status (*modulus)(N32·T*, N32·T*, N32·T*); + N32·Status (*shift_left)(Extent, N32·T*, N32·T*, N32·T*); + N32·Status (*shift_right)(Extent, N32·T*, N32·T*, N32·T*); + N32·Status (*arithmetic_shift_right)(Extent, N32·T*, N32·T*); + + N32·T* (*access)(N32·T*, Extent); + void (*from_uint32)(N32·T *destination ,uint32_t value); + } N32·Λ; + + Local const N32·Λ N32·λ; // initialized in the LOCAL section + +#endif + +//-------------------------------------------------------------------------------- +// Implementation + +#ifdef N32·IMPLEMENTATION + + // this part goes into the library + #ifndef LOCAL + + #include + #include + + struct N32·T{ + Digit d0; + }; + + N32·T N32·constant[4] = { + {.d0 = 0}, + {.d0 = 1}, + {.d0 = ~(uint32_t)0}, + {.d0 = 1 << 31} + }; + + N32·T *N32·zero = &N32·constant[0]; + N32·T *N32·one = &N32·constant[1]; + N32·T *N32·all_one_bit = &N32·constant[2]; + N32·T *N32·msb = &N32·constant[3]; + N32·T *N32·lsb = &N32·constant[1]; + + // the allocate an array of N32 + N32·T *N32·allocate_array(Extent extent ,N32·Allocate_MemoryFault memory_fault){ + N32·T *instance = malloc((extent + 1) * sizeof(N32·T) ); + if(!instance){ + return memory_fault ? memory_fault(extent) : NULL; + } + return instance; + } + + N32·T *N32·allocate_array_zero(Extent extent ,N32·Allocate_MemoryFault memory_fault){ + N32·T *instance = calloc( extent + 1 ,sizeof(N32·T) ); + if(!instance){ + return memory_fault ? memory_fault(extent) : NULL; + } + return instance; + } + + void N32·deallocate(N32·T *unencumbered){ + free(unencumbered); + } + + #endif + + // This part is included after the library user's code + #ifdef LOCAL + + // instance + + struct N32·T{ + Digit d0; + }; + + // temporary variables + // making these LOCAL rather than reserving one block in the library is thread safe + // allocating a block once is more efficient + // library code writes these, they are not on the interface + + Local N32·T N32·t[4]; + + + // allocation + + extern N32·T *N32·allocate_array(Extent, N32·Allocate_MemoryFault); + extern N32·T *N32·allocate_array_zero(Extent, N32·Allocate_MemoryFault); + extern void N32·deallocate(N32·T *); + + // so the user can access numbers in an array allocation + Local N32·T* N32·access(N32·T *array ,Extent index){ + return &array[index]; + } + + Local void N32·from_uint32(N32·T *destination ,uint32_t value){ + if(destination == NULL) return; + destination->d0 = value; + } + + // copy, convenience copy + + Local void N32·copy(N32·T *destination ,N32·T *source){ + if(source == destination) return; // that was easy! + *destination = *source; + } + + Local void N32·set_to_zero(N32·T *instance){ + instance->d0 = 0; + } + + Local void N32·set_to_one(N32·T *instance){ + instance->d0 = 1; + } + + // bit operations + + Local void N32·bit_and(N32·T *result, N32·T *a, N32·T *b){ + result->d0 = a->d0 & b->d0; + } + + // result can be one of the operands + Local void N32·bit_or(N32·T *result, N32·T *a, N32·T *b){ + result->d0 = a->d0 | b->d0; + } + + // result can the same as the operand + Local void N32·bit_complement(N32·T *result, N32·T *a){ + result->d0 = ~a->d0; + } + + // result can the same as the operand + Local void N32·bit_twos_complement(N32·T *result ,N32·T *a){ + result->d0 = ~a->d0 + 1; + } + + // test functions + + Local N32·Order N32·compare(N32·T *a, N32·T *b){ + if(a->d0 < b->d0) return N32·Order_lt; + if(a->d0 > b->d0) return N32·Order_gt; + return N32·Order_eq; + } + + Local bool N32·lt(N32·T *a ,N32·T *b){ + return a->d0 < b->d0; + } + + Local bool N32·gt(N32·T *a ,N32·T *b){ + return a->d0 > b->d0; + } + + Local bool N32·eq(N32·T *a ,N32·T *b){ + return a->d0 == b->d0; + } + + Local bool N32·eq_zero(N32·T *a){ + return a->d0 == 0; + } + + + // arithmetic operations + + // For a large number of summands for the lower precision Natural implementations, for accumulate/add/sub, the 'overflow' operand could overflow and thus this routine will halt and return N32·Status·accumulator1_overflow + // + // When accumulator1 and accumulator0 point to the same location, the result is the accumulator1 value. + Local N32·Status N32·accumulate(N32·T *accumulator1 ,N32·T *accumulator0 ,...){ + + va_list args; + va_start(args ,accumulator0); + uint32_t sum = accumulator0->d0; + uint32_t carry = 0; + N32·T *current; + + while( (current = va_arg(args ,N32·T *)) ){ + sum += current->d0; + if(sum < current->d0){ // Accumulator1 into carry + (carry)++; + if(carry == 0){ + va_end(args); + return N32·Status·accumulator1_overflow; + } + } + } + va_end(args); + + // wipes out prior value of accumulator1 + accumulator1->d0 = carry; + + return N32·Status·ok; + } + + Local N32·Status N32·add(N32·T *sum ,N32·T *a ,N32·T *b){ + uint64_t result = (uint64_t)a->d0 + (uint64_t)b->d0; + sum->d0 = (uint32_t)result; + return (result >> 32) ? N32·Status·carry : N32·Status·ok; + } + + Local bool N32·increment(N32·T *a){ + a->d0++; + return a->d0 == 0; + } + + Local N32·Status N32·subtract(N32·T *difference ,N32·T *a ,N32·T *b){ + uint64_t diff = (uint64_t) a->d0 - (uint64_t) b->d0; + difference->d0 = (uint32_t)diff; + return (diff > a->d0) ? N32·Status·borrow : N32·Status·ok; + } + + + Local N32·Status N32·multiply(N32·T *product1 ,N32·T *product0 ,N32·T *a ,N32·T *b){ + uint64_t product = (uint64_t)a->d0 * (uint64_t)b->d0; + product0->d0 = (uint32_t)product; + product1->d0 = (uint32_t)(product >> 32); + + if(product1->d0 == 0) return N32·Status·one_word_product; + return N32·Status·two_word_product; + } + + Local N32·Status N32·divide(N32·T *remainder ,N32·T *quotient ,N32·T *a ,N32·T *b){ + if(b->d0 == 0) return N32·Status·undefined_divide_by_zero; + + quotient->d0 = a->d0 / b->d0; + remainder->d0 = a->d0 - (quotient->d0 * b->d0); + + return N32·Status·ok; + } + + Local N32·Status N32·modulus(N32·T *remainder ,N32·T *a ,N32·T *b){ + if(b->d0 == 0) return N32·Status·undefined_modulus_zero; + uint32_t quotient = a->d0 / b->d0; + remainder->d0 = a->d0 - (quotient * b->d0); + return N32·Status·ok; + } + + // bit motion + + typedef uint32_t (*ShiftOp)(uint32_t, uint32_t); + + Local uint32_t shift_left_op(uint32_t value, uint32_t amount){ + return value << amount; + } + + Local uint32_t shift_right_op(uint32_t value, uint32_t amount){ + return value >> amount; + } + + // modifies all three of its operands + // in the case of duplicate operands this is the order: first modifies operand, then fill, then spill, + Local N32·Status N32·shift + ( + uint32_t shift_count + ,N32·T *spill + ,N32·T *operand + ,N32·T *fill + ,ShiftOp shift_op + ,ShiftOp complement_shift_op + ){ + + // If no result is needed, return immediately. + if(operand == NULL && spill == NULL) return N32·Status·ok; + + // Treat NULL operand as zero. + if(operand == NULL){ + operand = &N32·t[0]; + N32·copy(operand, N32·zero); + } + + // Shifting more than one word breaks our fill/spill model. + if(shift_count > 31) return N32·Status·gt_max_shift_count; + + // The given operand is still required after it is modified, so we copy it. + N32·T *given_operand = &N32·t[1]; + N32·copy(given_operand, operand); + + // Perform the shift + operand->d0 = shift_op(given_operand->d0, shift_count); + if(fill != NULL){ + fill->d0 = complement_shift_op(fill->d0, (32 - shift_count)); + N32·bit_or(operand, operand, fill); + } + if(spill != NULL){ + spill->d0 = shift_op(spill->d0, shift_count); + spill->d0 += complement_shift_op(given_operand->d0, (32 - shift_count)); + } + + return N32·Status·ok; + } + + // Define concrete shift functions using valid C function pointers + Local N32·Status + N32·shift_left(uint32_t shift_count, N32·T *spill, N32·T *operand, N32·T *fill){ + return N32·shift(shift_count, spill, operand, fill, shift_left_op, shift_right_op); + } + + Local N32·Status + N32·shift_right(uint32_t shift_count, N32·T *spill, N32·T *operand, N32·T *fill){ + return N32·shift(shift_count, spill, operand, fill, shift_right_op, shift_left_op); + } + + Local N32·Status + N32·arithmetic_shift_right(uint32_t shift_count, N32·T *operand, N32·T *spill){ + + // Guard against excessive shift counts + if(shift_count > 31) return N32·Status·gt_max_shift_count; + + // A NULL operand is treated as zero + if(operand == NULL){ + operand = &N32·t[0]; + N32·copy(operand, N32·zero); + } + + // Pick the fill value based on the sign bit + N32·T *fill = (operand->d0 & 0x80000000) ? N32·all_one_bit : N32·zero; + + // Call shift_right with the appropriate fill + return N32·shift_right(shift_count, spill, operand, fill); + } + + Local const N32·Λ N32·λ = { + + .allocate_array = N32·allocate_array + ,.allocate_array_zero = N32·allocate_array_zero + ,.deallocate = N32·deallocate + + ,.copy = N32·copy + ,.bit_and = N32·bit_and + ,.bit_or = N32·bit_or + ,.bit_complement = N32·bit_complement + ,.bit_twos_complement = N32·bit_twos_complement + ,.compare = N32·compare + ,.lt = N32·lt + ,.gt = N32·gt + ,.eq = N32·eq + ,.eq_zero = N32·eq_zero + ,.accumulate = N32·accumulate + ,.add = N32·add + ,.increment = N32·increment + ,.subtract = N32·subtract + ,.multiply = N32·multiply + ,.divide = N32·divide + ,.modulus = N32·modulus + ,.shift_left = N32·shift_left + ,.shift_right = N32·shift_right + ,.arithmetic_shift_right = N32·arithmetic_shift_right + + ,.access = N32·access + ,.from_uint32 = N32·from_uint32 + }; + + #endif + +#endif diff --git a/tester/cc/N32_4x8.lib.c b/tester/cc/N32_4x8.lib.c new file mode 100644 index 0000000..22cced5 --- /dev/null +++ b/tester/cc/N32_4x8.lib.c @@ -0,0 +1,457 @@ +/* + N32 - a processor native type + + For binary operations: a op b -> c + + See the document on the proper use of the Natural types. + + On the subject of multiple pointers indicating the same location in memory: + + When a routine has multiple results, and one or more of the result location + pointers point to the same storage, the routine will either return an error + status, or have defined behavior. + + When a routine has multiple operands, in any combination, those + pointers can point to the same location, and the routine will + function as advertised. + + When an operand functions as both an input and a result, perhaps due + to a result pointer pointing to the same place as an operand + pointer, the routine will function as advertised. (Internally the + routine might make a temporary copy of the operand to accomplish + this.) + +*/ + +#define N32_4x8·DEBUG + +#ifndef FACE +#define N32_4x8·IMPLEMENTATION +#define FACE +#endif + +//-------------------------------------------------------------------------------- +// Interface + +#ifndef N32_4x8·FACE +#define N32_4x8·FACE + + #include + #include + #include + #include + + //---------------------------------------- + // Instance Data (Declaration Only) + + typedef uint32_t Extent; + typedef uint32_t Digit; + + typedef struct N32_4x8·T N32_4x8·T; + + extern N32_4x8·T *N32_4x8·zero; + extern N32_4x8·T *N32_4x8·one; + extern N32_4x8·T *N32_4x8·all_one_bit; + extern N32_4x8·T *N32_4x8·lsb; + extern N32_4x8·T *N32_4x8·msb; + + //---------------------------------------- + // Return/Error Status and handlers + + typedef enum{ + N32_4x8·Status·ok = 0 + ,N32_4x8·Status·overflow = 1 + ,N32_4x8·Status·accumulator1_overflow = 2 + ,N32_4x8·Status·carry = 3 + ,N32_4x8·Status·borrow = 4 + ,N32_4x8·Status·undefined_divide_by_zero = 5 + ,N32_4x8·Status·undefined_modulus_zero = 6 + ,N32_4x8·Status·gt_max_shift_count = 7 + ,N32_4x8·Status·spill_eq_operand = 8 // not currently signaled, result will be spill value + ,N32_4x8·Status·one_word_product = 9 + ,N32_4x8·Status·two_word_product = 10 + } N32_4x8·Status; + + typedef enum{ + N32_4x8·Order_lt = -1 + ,N32_4x8·Order_eq = 0 + ,N32_4x8·Order_gt = 1 + } N32_4x8·Order; + + typedef N32_4x8·T *( *N32_4x8·Allocate_MemoryFault )(Extent); + + //---------------------------------------- + // Interface + + typedef struct{ + + N32_4x8·T *(*allocate_array_zero)(Extent, N32_4x8·Allocate_MemoryFault); + N32_4x8·T *(*allocate_array)(Extent, N32_4x8·Allocate_MemoryFault); + void (*deallocate)(N32_4x8·T*); + + void (*copy)(N32_4x8·T*, N32_4x8·T*); + void (*bit_and)(N32_4x8·T*, N32_4x8·T*, N32_4x8·T*); + void (*bit_or)(N32_4x8·T*, N32_4x8·T*, N32_4x8·T*); + void (*bit_complement)(N32_4x8·T*, N32_4x8·T*); + void (*bit_twos_complement)(N32_4x8·T*, N32_4x8·T*); + N32_4x8·Order (*compare)(N32_4x8·T*, N32_4x8·T*); + bool (*lt)(N32_4x8·T*, N32_4x8·T*); + bool (*gt)(N32_4x8·T*, N32_4x8·T*); + bool (*eq)(N32_4x8·T*, N32_4x8·T*); + bool (*eq_zero)(N32_4x8·T*); + N32_4x8·Status (*accumulate)(N32_4x8·T *accumulator1 ,N32_4x8·T *accumulator0 ,...); + N32_4x8·Status (*add)(N32_4x8·T*, N32_4x8·T*, N32_4x8·T*); + bool (*increment)(N32_4x8·T *a); + N32_4x8·Status (*subtract)(N32_4x8·T*, N32_4x8·T*, N32_4x8·T*); + N32_4x8·Status (*multiply)(N32_4x8·T*, N32_4x8·T*, N32_4x8·T*, N32_4x8·T*); + N32_4x8·Status (*divide)(N32_4x8·T*, N32_4x8·T*, N32_4x8·T*, N32_4x8·T*); + N32_4x8·Status (*modulus)(N32_4x8·T*, N32_4x8·T*, N32_4x8·T*); + N32_4x8·Status (*shift_left)(Extent, N32_4x8·T*, N32_4x8·T*, N32_4x8·T*); + N32_4x8·Status (*shift_right)(Extent, N32_4x8·T*, N32_4x8·T*, N32_4x8·T*); + N32_4x8·Status (*arithmetic_shift_right)(Extent, N32_4x8·T*, N32_4x8·T*); + + N32_4x8·T* (*access)(N32_4x8·T*, Extent); + void (*from_uint32)(N32_4x8·T *destination ,uint32_t value); + } N32_4x8·Λ; + + Local const N32_4x8·Λ N32_4x8·λ; // initialized in the LOCAL section + +#endif + +//-------------------------------------------------------------------------------- +// Implementation + +#ifdef N32_4x8·IMPLEMENTATION + + // this part goes into the library + #ifndef LOCAL + + #include + #include + + struct N32_4x8·T{ + Digit d0; + }; + + N32_4x8·T N32_4x8·constant[4] = { + {.d0 = 0}, + {.d0 = 1}, + {.d0 = ~(uint32_t)0}, + {.d0 = 1 << 31} + }; + + N32_4x8·T *N32_4x8·zero = &N32_4x8·constant[0]; + N32_4x8·T *N32_4x8·one = &N32_4x8·constant[1]; + N32_4x8·T *N32_4x8·all_one_bit = &N32_4x8·constant[2]; + N32_4x8·T *N32_4x8·msb = &N32_4x8·constant[3]; + N32_4x8·T *N32_4x8·lsb = &N32_4x8·constant[1]; + + // the allocate an array of N32 + N32_4x8·T *N32_4x8·allocate_array(Extent extent ,N32_4x8·Allocate_MemoryFault memory_fault){ + N32_4x8·T *instance = malloc((extent + 1) * sizeof(N32_4x8·T) ); + if(!instance){ + return memory_fault ? memory_fault(extent) : NULL; + } + return instance; + } + + N32_4x8·T *N32_4x8·allocate_array_zero(Extent extent ,N32_4x8·Allocate_MemoryFault memory_fault){ + N32_4x8·T *instance = calloc( extent + 1 ,sizeof(N32_4x8·T) ); + if(!instance){ + return memory_fault ? memory_fault(extent) : NULL; + } + return instance; + } + + void N32_4x8·deallocate(N32_4x8·T *unencumbered){ + free(unencumbered); + } + + #endif + + // This part is included after the library user's code + #ifdef LOCAL + + // instance + + struct N32_4x8·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_4x8·T N32_4x8·t[4]; + + + // allocation + + extern N32_4x8·T *N32_4x8·allocate_array(Extent, N32_4x8·Allocate_MemoryFault); + extern N32_4x8·T *N32_4x8·allocate_array_zero(Extent, N32_4x8·Allocate_MemoryFault); + extern void N32_4x8·deallocate(N32_4x8·T *); + + // so the user can access numbers in an array allocation + Local N32_4x8·T* N32_4x8·access(N32_4x8·T *array ,Extent index){ + return &array[index]; + } + + Local void N32_4x8·from_uint32(N32_4x8·T *destination ,uint32_t value){ + if(destination == NULL) return; + destination->d0 = value; + } + + // copy, convenience copy + + Local void N32_4x8·copy(N32_4x8·T *destination ,N32_4x8·T *source){ + if(source == destination) return; // that was easy! + *destination = *source; + } + + Local void N32_4x8·set_to_zero(N32_4x8·T *instance){ + instance->d0 = 0; + } + + Local void N32_4x8·set_to_one(N32_4x8·T *instance){ + instance->d0 = 1; + } + + // bit operations + + Local void N32_4x8·bit_and(N32_4x8·T *result, N32_4x8·T *a, N32_4x8·T *b){ + result->d0 = a->d0 & b->d0; + } + + // result can be one of the operands + Local void N32_4x8·bit_or(N32_4x8·T *result, N32_4x8·T *a, N32_4x8·T *b){ + result->d0 = a->d0 | b->d0; + } + + // result can the same as the operand + Local void N32_4x8·bit_complement(N32_4x8·T *result, N32_4x8·T *a){ + result->d0 = ~a->d0; + } + + // result can the same as the operand + Local void N32_4x8·bit_twos_complement(N32_4x8·T *result ,N32_4x8·T *a){ + result->d0 = ~a->d0 + 1; + } + + // test functions + + Local N32_4x8·Order N32_4x8·compare(N32_4x8·T *a, N32_4x8·T *b){ + if(a->d0 < b->d0) return N32_4x8·Order_lt; + if(a->d0 > b->d0) return N32_4x8·Order_gt; + return N32_4x8·Order_eq; + } + + Local bool N32_4x8·lt(N32_4x8·T *a ,N32_4x8·T *b){ + return a->d0 < b->d0; + } + + Local bool N32_4x8·gt(N32_4x8·T *a ,N32_4x8·T *b){ + return a->d0 > b->d0; + } + + Local bool N32_4x8·eq(N32_4x8·T *a ,N32_4x8·T *b){ + return a->d0 == b->d0; + } + + Local bool N32_4x8·eq_zero(N32_4x8·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_4x8·Status·accumulator1_overflow + // + // When accumulator1 and accumulator0 point to the same location, the result is the accumulator1 value. + Local N32_4x8·Status N32_4x8·accumulate(N32_4x8·T *accumulator1 ,N32_4x8·T *accumulator0 ,...){ + + va_list args; + va_start(args ,accumulator0); + uint32_t sum = accumulator0->d0; + uint32_t carry = 0; + N32_4x8·T *current; + + while( (current = va_arg(args ,N32_4x8·T *)) ){ + sum += current->d0; + if(sum < current->d0){ // Accumulator1 into carry + (carry)++; + if(carry == 0){ + va_end(args); + return N32_4x8·Status·accumulator1_overflow; + } + } + } + va_end(args); + + // wipes out prior value of accumulator1 + accumulator1->d0 = carry; + + return N32_4x8·Status·ok; + } + + Local N32_4x8·Status N32_4x8·add(N32_4x8·T *sum ,N32_4x8·T *a ,N32_4x8·T *b){ + uint64_t result = (uint64_t)a->d0 + (uint64_t)b->d0; + sum->d0 = (uint32_t)result; + return (result >> 32) ? N32_4x8·Status·carry : N32_4x8·Status·ok; + } + + Local bool N32_4x8·increment(N32_4x8·T *a){ + a->d0++; + return a->d0 == 0; + } + + Local N32_4x8·Status N32_4x8·subtract(N32_4x8·T *difference ,N32_4x8·T *a ,N32_4x8·T *b){ + uint64_t diff = (uint64_t) a->d0 - (uint64_t) b->d0; + difference->d0 = (uint32_t)diff; + return (diff > a->d0) ? N32_4x8·Status·borrow : N32_4x8·Status·ok; + } + + + Local N32_4x8·Status N32_4x8·multiply(N32_4x8·T *product1 ,N32_4x8·T *product0 ,N32_4x8·T *a ,N32_4x8·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_4x8·Status·one_word_product; + return N32_4x8·Status·two_word_product; + } + + Local N32_4x8·Status N32_4x8·divide(N32_4x8·T *remainder ,N32_4x8·T *quotient ,N32_4x8·T *a ,N32_4x8·T *b){ + if(b->d0 == 0) return N32_4x8·Status·undefined_divide_by_zero; + + quotient->d0 = a->d0 / b->d0; + remainder->d0 = a->d0 - (quotient->d0 * b->d0); + + return N32_4x8·Status·ok; + } + + Local N32_4x8·Status N32_4x8·modulus(N32_4x8·T *remainder ,N32_4x8·T *a ,N32_4x8·T *b){ + if(b->d0 == 0) return N32_4x8·Status·undefined_modulus_zero; + uint32_t quotient = a->d0 / b->d0; + remainder->d0 = a->d0 - (quotient * b->d0); + return N32_4x8·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_4x8·Status N32_4x8·shift + ( + uint32_t shift_count + ,N32_4x8·T *spill + ,N32_4x8·T *operand + ,N32_4x8·T *fill + ,ShiftOp shift_op + ,ShiftOp complement_shift_op + ){ + + // If no result is needed, return immediately. + if(operand == NULL && spill == NULL) return N32_4x8·Status·ok; + + // Treat NULL operand as zero. + if(operand == NULL){ + operand = &N32_4x8·t[0]; + N32_4x8·copy(operand, N32_4x8·zero); + } + + // Shifting more than one word breaks our fill/spill model. + if(shift_count > 31) return N32_4x8·Status·gt_max_shift_count; + + // The given operand is still required after it is modified, so we copy it. + N32_4x8·T *given_operand = &N32_4x8·t[1]; + N32_4x8·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_4x8·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_4x8·Status·ok; + } + + // Define concrete shift functions using valid C function pointers + Local N32_4x8·Status + N32_4x8·shift_left(uint32_t shift_count, N32_4x8·T *spill, N32_4x8·T *operand, N32_4x8·T *fill){ + return N32_4x8·shift(shift_count, spill, operand, fill, shift_left_op, shift_right_op); + } + + Local N32_4x8·Status + N32_4x8·shift_right(uint32_t shift_count, N32_4x8·T *spill, N32_4x8·T *operand, N32_4x8·T *fill){ + return N32_4x8·shift(shift_count, spill, operand, fill, shift_right_op, shift_left_op); + } + + Local N32_4x8·Status + N32_4x8·arithmetic_shift_right(uint32_t shift_count, N32_4x8·T *operand, N32_4x8·T *spill){ + + // Guard against excessive shift counts + if(shift_count > 31) return N32_4x8·Status·gt_max_shift_count; + + // A NULL operand is treated as zero + if(operand == NULL){ + operand = &N32_4x8·t[0]; + N32_4x8·copy(operand, N32_4x8·zero); + } + + // Pick the fill value based on the sign bit + N32_4x8·T *fill = (operand->d0 & 0x80000000) ? N32_4x8·all_one_bit : N32_4x8·zero; + + // Call shift_right with the appropriate fill + return N32_4x8·shift_right(shift_count, spill, operand, fill); + } + + Local const N32_4x8·Λ N32_4x8·λ = { + + .allocate_array = N32_4x8·allocate_array + ,.allocate_array_zero = N32_4x8·allocate_array_zero + ,.deallocate = N32_4x8·deallocate + + ,.copy = N32_4x8·copy + ,.bit_and = N32_4x8·bit_and + ,.bit_or = N32_4x8·bit_or + ,.bit_complement = N32_4x8·bit_complement + ,.bit_twos_complement = N32_4x8·bit_twos_complement + ,.compare = N32_4x8·compare + ,.lt = N32_4x8·lt + ,.gt = N32_4x8·gt + ,.eq = N32_4x8·eq + ,.eq_zero = N32_4x8·eq_zero + ,.accumulate = N32_4x8·accumulate + ,.add = N32_4x8·add + ,.increment = N32_4x8·increment + ,.subtract = N32_4x8·subtract + ,.multiply = N32_4x8·multiply + ,.divide = N32_4x8·divide + ,.modulus = N32_4x8·modulus + ,.shift_left = N32_4x8·shift_left + ,.shift_right = N32_4x8·shift_right + ,.arithmetic_shift_right = N32_4x8·arithmetic_shift_right + + ,.access = N32_4x8·access + ,.from_uint32 = N32_4x8·from_uint32 + }; + + #endif + +#endif diff --git a/tester/cc/test_N32PN.cli.c b/tester/cc/test_N32PN.cli.c deleted file mode 100644 index e17581a..0000000 --- a/tester/cc/test_N32PN.cli.c +++ /dev/null @@ -1,364 +0,0 @@ -#include -#include -#include -#include - -// Enable interface section -#define FACE -#include "N32PN.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 - N32PN·T *array = N32PN·λ.allocate_array(2 ,NULL); - if( !array ) return false; - - // Access elements via access function - N32PN·T *a = N32PN·λ.access(array ,0); - N32PN·T *b = N32PN·λ.access(array ,1); - - // Assign value and copy - N32PN·λ.from_uint32(a ,42); - N32PN·λ.copy(b ,a); - - bool success = ( N32PN·λ.compare(b ,a) == N32PN·Order_eq ); - N32PN·λ.deallocate(array); - return success; -} - -bool test_arithmetic(){ - // Allocate memory - N32PN·T *array = N32PN·λ.allocate_array(3 ,NULL); - if( !array ) return false; - - N32PN·T *a = N32PN·λ.access(array ,0); - N32PN·T *b = N32PN·λ.access(array ,1); - N32PN·T *result = N32PN·λ.access(array ,2); - - N32PN·λ.from_uint32(a ,20); - N32PN·λ.from_uint32(b ,22); - - if( N32PN·λ.add(result ,a ,b) != N32PN·Status·ok ) return false; - if( N32PN·λ.compare(result ,N32PN·λ.access(array ,0)) != N32PN·Order_gt ) return false; - - if( N32PN·λ.subtract(result ,b ,a) != N32PN·Status·ok ) return false; - if( N32PN·λ.compare(result ,N32PN·λ.access(array ,0)) != N32PN·Order_lt ) return false; - - N32PN·λ.deallocate(array); - return true; -} - -bool test_bitwise_operations(){ - // Allocate memory - N32PN·T *array = N32PN·λ.allocate_array(3, NULL); - if(!array) return false; - - N32PN·T *a = N32PN·λ.access(array, 0); - N32PN·T *b = N32PN·λ.access(array, 1); - N32PN·T *result = N32PN·λ.access(array, 2); - - // a = 0x0F0F0F0F, b = 0xF0F0F0F0 - N32PN·λ.from_uint32(a, 0x0F0F0F0F); - N32PN·λ.from_uint32(b, 0xF0F0F0F0); - - // bit_and => expect 0x00000000 - N32PN·λ.bit_and(result, a, b); - N32PN·λ.from_uint32(a, 0x00000000); - if(N32PN·λ.compare(result, a) != N32PN·Order_eq){ - N32PN·λ.deallocate(array); - return false; - } - - // Reset a to 0x0F0F0F0F for next tests - N32PN·λ.from_uint32(a, 0x0F0F0F0F); - - // bit_or => expect 0xFFFFFFFF - N32PN·λ.bit_or(result, a, b); - N32PN·λ.from_uint32(b, 0xFFFFFFFF); - if(N32PN·λ.compare(result, b) != N32PN·Order_eq){ - N32PN·λ.deallocate(array); - return false; - } - - // bit_complement(a=0x0F0F0F0F) => expect 0xF0F0F0F0 - N32PN·λ.from_uint32(a, 0x0F0F0F0F); - N32PN·λ.bit_complement(result, a); - N32PN·λ.from_uint32(b, 0xF0F0F0F0); - if(N32PN·λ.compare(result, b) != N32PN·Order_eq){ - N32PN·λ.deallocate(array); - return false; - } - - // bit_twos_complement(a=0x0F0F0F0F) => expect 0xF0F0F0F1 - N32PN·λ.from_uint32(a, 0x0F0F0F0F); - N32PN·λ.bit_twos_complement(result, a); - N32PN·λ.from_uint32(b, 0xF0F0F0F1); - if(N32PN·λ.compare(result, b) != N32PN·Order_eq){ - N32PN·λ.deallocate(array); - return false; - } - - N32PN·λ.deallocate(array); - return true; -} - -bool test_comparisons(){ - // Allocate memory - N32PN·T *array = N32PN·λ.allocate_array(3, NULL); - if(!array) return false; - - N32PN·T *a = N32PN·λ.access(array, 0); - N32PN·T *b = N32PN·λ.access(array, 1); - N32PN·T *c = N32PN·λ.access(array, 2); - - // First set: a=0, b=42, c=42 - N32PN·λ.from_uint32(a, 0); - N32PN·λ.from_uint32(b, 42); - N32PN·λ.from_uint32(c, 42); - - // eq_zero(a) => true - if(!N32PN·λ.eq_zero(a)){ - N32PN·λ.deallocate(array); - return false; - } - // eq_zero(b) => false - if(N32PN·λ.eq_zero(b)){ - N32PN·λ.deallocate(array); - return false; - } - // eq(b, c) => true - if(!N32PN·λ.eq(b, c)){ - N32PN·λ.deallocate(array); - return false; - } - // eq(a, b) => false - if(N32PN·λ.eq(a, b)){ - N32PN·λ.deallocate(array); - return false; - } - // compare(a, b) => N32PN·Order_lt - if(N32PN·λ.compare(a, b) != N32PN·Order_lt){ - N32PN·λ.deallocate(array); - return false; - } - // compare(b, a) => N32PN·Order_gt - if(N32PN·λ.compare(b, a) != N32PN·Order_gt){ - N32PN·λ.deallocate(array); - return false; - } - // compare(b, c) => N32PN·Order_eq - if(N32PN·λ.compare(b, c) != N32PN·Order_eq){ - N32PN·λ.deallocate(array); - return false; - } - // lt(a, b) => true, gt(b, a) => true - if(!N32PN·λ.lt(a, b) || !N32PN·λ.gt(b, a)){ - N32PN·λ.deallocate(array); - return false; - } - - // Second set: a=100, b=50 - N32PN·λ.from_uint32(a, 100); - N32PN·λ.from_uint32(b, 50); - if(N32PN·λ.compare(a, b) != N32PN·Order_gt){ - N32PN·λ.deallocate(array); - return false; - } - // eq_zero(a) => false - if(N32PN·λ.eq_zero(a)){ - N32PN·λ.deallocate(array); - return false; - } - // eq_zero(b) => false - if(N32PN·λ.eq_zero(b)){ - N32PN·λ.deallocate(array); - return false; - } - - N32PN·λ.deallocate(array); - return true; -} - -bool test_shifts(){ - // Allocate memory for operand, fill, spill - N32PN·T *array = N32PN·λ.allocate_array(3, NULL); - if(!array) return false; - - N32PN·T *operand = N32PN·λ.access(array, 0); - N32PN·T *fill = N32PN·λ.access(array, 1); - N32PN·T *spill = N32PN·λ.access(array, 2); - - // Subtest A: shift_left(4) with operand=1 => expect operand=16, fill=0, spill=0 - N32PN·λ.from_uint32(operand, 1); - N32PN·λ.from_uint32(fill, 0); - N32PN·λ.from_uint32(spill, 0); - if(N32PN·λ.shift_left(4, spill, operand, fill) != N32PN·Status·ok){ - N32PN·λ.deallocate(array); - return false; - } - N32PN·T *temp = N32PN·λ.allocate_array(1, NULL); - if(!temp){ - N32PN·λ.deallocate(array); - return false; - } - N32PN·λ.from_uint32(temp, 16); - if(N32PN·λ.compare(operand, temp) != N32PN·Order_eq){ - N32PN·λ.deallocate(temp); - N32PN·λ.deallocate(array); - return false; - } - if(N32PN·λ.compare(fill, N32PN·zero) != N32PN·Order_eq){ - N32PN·λ.deallocate(temp); - N32PN·λ.deallocate(array); - return false; - } - if(N32PN·λ.compare(spill, N32PN·zero) != N32PN·Order_eq){ - N32PN·λ.deallocate(temp); - N32PN·λ.deallocate(array); - return false; - } - - // Subtest B: shift_left(1) with operand=0x80000000 => expect operand=0, spill=1 - N32PN·λ.from_uint32(operand, 0x80000000); - N32PN·λ.from_uint32(fill, 0); - N32PN·λ.from_uint32(spill, 0); - if(N32PN·λ.shift_left(1, spill, operand, fill) != N32PN·Status·ok){ - N32PN·λ.deallocate(temp); - N32PN·λ.deallocate(array); - return false; - } - if(!N32PN·λ.eq_zero(operand)){ - N32PN·λ.deallocate(temp); - N32PN·λ.deallocate(array); - return false; - } - N32PN·λ.from_uint32(temp, 1); - if(N32PN·λ.compare(spill, temp) != N32PN·Order_eq){ - N32PN·λ.deallocate(temp); - N32PN·λ.deallocate(array); - return false; - } - - // Subtest C: shift_right(1) with operand=0x80000000 => expect operand=0x40000000, spill=0 - N32PN·λ.from_uint32(operand, 0x80000000); - N32PN·λ.from_uint32(fill, 0); - N32PN·λ.from_uint32(spill, 0); - if(N32PN·λ.shift_right(1, spill, operand, fill) != N32PN·Status·ok){ - N32PN·λ.deallocate(temp); - N32PN·λ.deallocate(array); - return false; - } - N32PN·λ.from_uint32(temp, 0x40000000); - if(N32PN·λ.compare(operand, temp) != N32PN·Order_eq){ - N32PN·λ.deallocate(temp); - N32PN·λ.deallocate(array); - return false; - } - if(!N32PN·λ.eq_zero(spill)){ - N32PN·λ.deallocate(temp); - N32PN·λ.deallocate(array); - return false; - } - - // Subtest D: arithmetic_shift_right(1) with operand=0x80000000 => expect operand=0xC0000000, spill=0 - N32PN·λ.from_uint32(operand, 0x80000000); - N32PN·λ.from_uint32(spill, 0); - if(N32PN·λ.arithmetic_shift_right(1, operand, spill) != N32PN·Status·ok){ - N32PN·λ.deallocate(temp); - N32PN·λ.deallocate(array); - return false; - } - N32PN·λ.from_uint32(temp, 0xC0000000); - if(N32PN·λ.compare(operand, temp) != N32PN·Order_eq){ - N32PN·λ.deallocate(temp); - N32PN·λ.deallocate(array); - return false; - } - if(!N32PN·λ.eq_zero(spill)){ - N32PN·λ.deallocate(temp); - N32PN·λ.deallocate(array); - return false; - } - - N32PN·λ.deallocate(temp); - N32PN·λ.deallocate(array); - return true; -} - - - -// Include the local section of N32PN.lib.c for testing -#define LOCAL -#include "N32PN.lib.c" -#undef LOCAL diff --git a/tester/python/#fill_templates.py# b/tester/python/#fill_templates.py# deleted file mode 100644 index 95519a3..0000000 --- a/tester/python/#fill_templates.py# +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python3 - -import sys -from template_test_N32 import test_N32 - -def write(code ,basename): - filepath = "../cc/" + "test_" + basename + ".cli.c" - with open(filepath, "w") as f: - f.write(code) - print("Generated " + filepath) - - -def main(): - """ - generates `.c` source code from templates - """ - - # base line test N32 test - type_name = "N32PN" - code = test_N32(namespace = type_name) - write(code ,type_name); - -if __name__ == "__main__": - main() diff --git a/tester/python/fill_template b/tester/python/fill_template index 95519a3..573f570 100755 --- a/tester/python/fill_template +++ b/tester/python/fill_template @@ -20,5 +20,12 @@ def main(): code = test_N32(namespace = type_name) write(code ,type_name); + # a single digit N32 + type_name = "N32" + code = test_N32(namespace = type_name) + write(code ,type_name); + + + if __name__ == "__main__": main() diff --git "a/tester/tool\360\237\226\211/release_clean" "b/tester/tool\360\237\226\211/release_clean" deleted file mode 100755 index 2e0da45..0000000 --- "a/tester/tool\360\237\226\211/release_clean" +++ /dev/null @@ -1,28 +0,0 @@ -#!/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 - -# remove library pulled from release and other scratchpad files - rm_na -rf scratchpad/* || true - -# remove cc files pulled from release - rm_na -f cc/* || true - -# remove built executables - rm_na -f machine/* || true - -set +x -echo "$(script_fn) done." diff --git "a/tester/tool\360\237\226\211/release_remove" "b/tester/tool\360\237\226\211/release_remove" new file mode 100755 index 0000000..2e0da45 --- /dev/null +++ "b/tester/tool\360\237\226\211/release_remove" @@ -0,0 +1,28 @@ +#!/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 + +# remove library pulled from release and other scratchpad files + rm_na -rf scratchpad/* || true + +# remove cc files pulled from release + rm_na -f cc/* || true + +# remove built executables + rm_na -f machine/* || true + +set +x +echo "$(script_fn) done." diff --git "a/tool_shared/bespoke\360\237\226\211/version" "b/tool_shared/bespoke\360\237\226\211/version" index 51abf9f..5934da2 100755 --- "a/tool_shared/bespoke\360\237\226\211/version" +++ "b/tool_shared/bespoke\360\237\226\211/version" @@ -1,9 +1,5 @@ #!/bin/env bash script_afp=$(realpath "${BASH_SOURCE[0]}") -# 0.1 pulled groovy files from GQL_to_Cypher build.gradle -# 0.2 conversion to Java -# 0.3 refactored, split into smaller pieces, made into package - -echo v0.3 +echo "N v0.1 2025-02-17"