# -*- coding: utf-8 -*- """ Created on Fri Jan 28 22:46:53 2022 @author: Repose A module to analyze arithmetic via logs ref. https://www.siranah.de/html/sail042q.htm """ #imports import sys from math import log2 #functions/classes scale=256/log2(256) #8-bit range lo=lambda x:x&255 #util for creating tables (for future development) hi=lambda x:(x>>8) logadd=lambda x:round(log2(1+1/pow(2,x/scale))*scale) #scaled table to add two logs logsub=lambda x:round(log2(1+pow(2,x/scale))*scale) #scaled table to sub two logs exp=lambda x:round(pow(2,x/scale)) #scaled exponential 2^x table lg=lambda x:round(log2(x)*scale) #scaled log2(x) table def mult(x,y): '''Multiply two integers via their logs Keyword arguments: x,y -- integers 0 to 255 Returns: The approximate product ''' return exp(lg(x)+lg(y)) def add(x,y): '''Add two integers via their logs Keyword arguments: x,y -- integers 0 to 255 Returns: The approximate sum ''' return exp(logadd(lg(y)-lg(x))+lg(y)) def output_table(fmt, fname, name, addr, tab, start, end): '''Output a table as an assembly include file Keyword arguments: fmt -- string, "ACME" fname -- string, filename of output file name -- string, table name addr -- integer start address of table tab -- generator function start -- integer start index value end -- integer end index value + 1 Returns: 0 (success) 5 (failed to open file) ''' try: with open(fname, 'w') as f: print(f'writing {fname}') if fmt.lower()=="acme": fmt_data='!by ' elif fmt.lower()=="dasm": fmt_data='dc.b ' else: print(f'unknown assembler format {fmt}') return 1 #traditional DOS incorrect function #header f.write(f'{name}=${addr:04X}\n') step=16 for i in range(start,end,step): #line start f.write(fmt_data) k=i+step if i+step<end else end first_invalid=True first_overflow=True for j in range(i,k): try: val=tab(j) except ValueError: val=0 if first_invalid: print(f'warning: invalid table value replaced with 0 at index {j}') first_invalid=False if val>0xff: val=val & 0xff if first_overflow: print(f'warning: table value overflowed at index {j}') first_overflow=False f.write(f'${val:02X}') if j!=k-1: f.write(',') else: f.write('\n') except: print(f"Couldn't open {fname} for writing") return 5 #traditional DOS error code #main def main(): '''Analyze arithmetic of two integers via their logs Keyword arguments: none Returns: 0 (success) ''' print('Guassian logs analysis\n') print('accuracy test\n') #should write a helper class for this erm=0 #maximum multiply error era=0 #maximum addition error for x in range(1,256): for y in range(x, 256): er=mult(x,y)-x*y if er>erm: erm=er pxm=x pym=y er=add(x,y)-(x+y) if er>era: era=er pxa=x pya=y #requires python 3.6+ due to f strings print('multiply') print(f'max err was {erm} ({erm/(pxm*pym)*100:.2f}%) at x={pxm} and y={pym}') print(f'{pxm}*{pym}={pxm*pym}, mult({pxm},{pym})={mult(pxm,pym)}\n') print('add') print(f'max err was {era} ({era/(pxa+pya)*100:.2f}%) at x={pxa} and y={pya}') print(f'{pxa}+{pya}={pxa+pya}, add({pxa},{pya})={add(pxa,pya)}\n') #write tables print('writing tables') tables=[{"func":lg,"name":"logtab"}, {"func":exp,"name":"exptab"}, {"func":logadd,"name":"logadd"}] addr=0xc000 assembler='acme' for table in tables: #output_table(fmt, fname, name, addr, tab, start, end+1) output_table(assembler, table["name"]+'.asm', table["name"], addr, table["func"], 0, 256) addr+=0x100 if __name__ == '__main__': sys.exit(main())
;This section performs the 4 sub-multiplies to form ; the partials to be added later in self-mod code. ;Addresses like "x1y0l+1" refer to low(x1*y0) stored ; to the argument of a later "adc #{value}" ;mult8_set_mier_snippet {multiplier} ;mult8_snippet {multiplicand}, {low result}, {high result} +mult8_set_mier_snippet x1 ;17 +mult8_snippet y0, x1y0l+1, x1y0h+1 ;35 +mult8_snippet y1, x1y1l+1, z3 ;32 +mult8_set_mier_snippet x0 ;17 +mult8_snippet "Y", x0y1l+1, "X" ;28 +mult8_snippet y0, z0, "A" ;28 ;results in X=x0y1h and A=x0y0h ;multiply part total: 149-165, average 157 ;z3 z2 z1 clc x0y1l adc #0 ;x0y0h + x0y1l tay ;6 txa x1y0h adc #0 ;x0y1h + x1y0h tax bcc + ;9 inc z3 clc ;(+6) taken 7% of the time + tya x1y0l adc #0 ;+ x1y0l sta z1 ;7 txa x1y1l adc #0 ;+ x1y1l bcc done ;7 inc z3 ;(+4) taken 42% of the time
y1 y0 x x1 x0 -------- x0y0h x0y0l x0y1h x0y1l x1y0h x1y0l x1y1h x1y1l ----------------------- z3 z2 z1 z0
!macro mult8_set_mier_snippet .mier { ;set multiplier as mier ;mier can be m/A ;requires .p_.sqr* and .p_.neg.sqr* pointers, ;(mathlib.p_sqr_lo, mathlib.p_neg_sqr_lo, mathlib.p_sqr_hi, mathlib.p_neg_sqr_hi) ;uses these macros from mathlib_util_macros.asm: ;reset_cycles, add_cycles_const !if .mier = "X" or .mier = "Y" { !error "multlib: mult8_snippet: mier must be m/A, not ", .mier } +reset_cycles !if .mier != "A" { lda .mier !if .mier<$100 { +add_cycles_const 3 } else { +add_cycles_const 4 } } sta mathlib.p_sqr_lo ;3 sta mathlib.p_sqr_hi ;3 eor #$ff ;2 sta mathlib.p_neg_sqr_lo ;3 sta mathlib.p_neg_sqr_hi ;3; 3+3+3+2+3+3 = 17 cycles +add_cycles_const 14 }
mier = x1 (x1) * y0 -> x1y0l, x1y0h (x1) * y1 -> x1y1l, z3 mier = x0 (x0) * (y1) -> x0y1l, X (x0) * y0 -> z0, A