; ======================================================================
; XANDRIAN LABS  /  CS617 CHAPTER 09
; ======================================================================
; Program    : MathROM - Numeric Sweep
; Source     : mathrom_numeric_sweep.s
; Revision   : r028
; Target     : Casio PB-2000C / HD61700
; Role       : Exercise 18 functions on 16 input fixtures each.
; Interface  : Aggregate results, oracle comparisons and VT pages.
; Maintainer : Xandrian Labs
; ----------------------------------------------------------------------
; Owns the DY_ scratch ledger and generated fixture tables.
; The specialist suite uses some of the same scratch storage.
; ======================================================================

; ----------------------------------------------------------------------
; IMPLEMENTATION NOTES
; ----------------------------------------------------------------------
; 38-digit signed decimal accumulators are application-owned.
; Test tables are included as dyadic16_tables.inc.

DY_POS:     EQU &H2B80 ; 38 digits LSD first: nonnegative terms
DY_NEG:     EQU &H2BA8 ; 38 digits LSD first: negative terms
DY_DIFF:    EQU &H2BD0 ; 38 digits difference
DY_MANT:    EQU &H2BF8 ; 13 LSD-first decimal digits
DY_SIGN:    EQU &H2C05
DY_SHIFT:   EQU &H2C06
DY_CARRY:   EQU &H2C07
DY_RAW:     EQU &H2C08
DY_SUMSIGN: EQU &H2C09
DY_TOP:     EQU &H2C0A
DY_OUTB9:   EQU &H2C10 ; 9 bytes final actual aggregate
DY_PTR:     EQU &H2C20 ; two-byte pointer into ROM operands
DY_FUNC:    EQU &H2C22 ; 0..17, canonical V6 function order
DY_CASE:    EQU &H2C23 ; 0..15 completed cases
DY_ANG:     EQU &H2C24 ; saved PB-2000C angle byte
DY_STATUS:  EQU &H2C25 ; 18 bytes: 1 exact,4 near,2 fail,3 check
DY_COUNT:   EQU &H2C40 ; first 16 counts, through 2C4F
DY_COUNT2:  EQU &H2B7A ; last two counts, through 2B7B
DY_RESULTS: EQU &H2C80 ; first 14*9 B9, through 2CFD
DY_RES_OVF: EQU &H2B68 ; function 14-15 B9 records, 2B68..2B79
DY_RES_FIX: EQU &H2C37 ; function 16 B9 (FIX), 2C37..2C3F
DY_RES_INC: EQU &H2C77 ; function 17 B9 (INC), 2C77..2C7F
DY_LAST9:   EQU &H2C50 ; 9 bytes captured from native $10..$18
DY_ERR_RAW: EQU &H2C60 ; 9 bytes first unexpected raw result/aggregate
DY_ERR_FN:  EQU &H2C70 ; 0..17; 255 means none
DY_ERR_K:   EQU &H2C71 ; 1..16 failing term, 0 aggregate mismatch
DY_ERR_CT:  EQU &H2C72 ; number of function rows with CHECK
DY_NTXT:    EQU &H2C73 ; two ASCII decimal count digits
DY_KTXT:    EQU &H2C75 ; two ASCII decimal fail index digits

; ETC cycles from the original four-FK page to this second menu page.
; ETC again cycles back. MENU is an exit/home key, never a page selector.
; All four legacy home softkeys remain unchanged.
; ----------------------------------------------------------------------
; NUMERICAL SWEEP MENU
; ----------------------------------------------------------------------
DY_MENU:
        PST UA,&H14
        CAL LCD_CLEAR
        LD $6,0
        PRE IZ,DY_MENU_TITLE
        CAL LCD_ROM32
        LD $6,&H20
        PRE IZ,DY_MENU_TEXT
        CAL LCD_ROM32
        LD $6,&H40
        PRE IZ,DY_MENU_HINT
        CAL LCD_ROM32
        LD $6,&H60
        PRE IZ,DY_MENU_KEYS
        CAL LCD_ROM32
DY_MENU_WAIT:
        CAL KEY_WAIT
        SBC $17,2
        JP Z,LAB_WARM
        LD $15,$0
        SBC $15,KEYF1
        JP Z,DY_ENTER
        LD $15,$0
        SBC $15,KEYF2
        JP Z,SP_ENTER
        LD $15,$0
        SBC $15,KEYETC
        JP Z,MR_HOME
        LD $15,$0
        SBC $15,KEYMENU
        JP Z,MR_HOME
        JP DY_MENU_WAIT

; [dyadic] is F1 on ETC page 2. Neither MENU nor ETC executes it.
; ----------------------------------------------------------------------
; FIXTURE EVALUATION
; ----------------------------------------------------------------------
DY_ENTER:
        PST UA,&H14
        PRE IX,&H0A03
        LD $0,(IX+$SX)
        PRE IX,DY_ANG
        ST $0,(IX+$SX)
        PRE IX,&H0A03
        LD $0,0                     ; degree mode; restore below
        ST $0,(IX+$SX)
        PRE IX,DY_ERR_FN
        LD $0,255
        ST $0,(IX+$SX)
        PRE IX,DY_ERR_CT
        LD $0,0
        ST $0,(IX+$SX)
        PRE IX,DY_STATUS
        LD $20,18
DY_INIT_ST:
        LD $0,3                     ; CHECK until a full suite completes
        STI $0,(IX+$SX)
        SB $20,$SY
        JP NZ,DY_INIT_ST
        LDW $2,DY_RESULTS
        LD $20,126
        CAL DY_CLEAR
        LDW $2,DY_RES_OVF
        LD $20,20             ; 18 B9 bytes + 2 count bytes 2B7A..7B
        CAL DY_CLEAR
        LDW $2,DY_RES_FIX
        LD $20,9
        CAL DY_CLEAR
        LDW $2,DY_RES_INC
        LD $20,9
        CAL DY_CLEAR
        PRE IX,DY_FUNC
        LD $0,0
        ST $0,(IX+$SX)
DY_FUNC_LOOP:
        CAL DY_RUN_ONE
        PRE IX,DY_FUNC
        LD $0,(IX+$SX)
        AD $0,$SY
        ST $0,(IX+$SX)
        SBC $0,18
        JP C,DY_FUNC_LOOP
        PST UA,&H14
        PRE IX,DY_ANG
        LD $0,(IX+$SX)
        PRE IX,&H0A03
        ST $0,(IX+$SX)
        CAL DY_REPORT
        CAL VT23_VIEW
        SBC $0,2
        JP Z,LAB_WARM
        AN $0,$0
        JP NZ,MR_HOME
        CAL DY_DETAIL
        CAL VT23_VIEW
        SBC $0,2
        JP Z,LAB_WARM
        JP MR_HOME

; $2 bank-one RAM start, $20 byte count. Uses one-byte $31=0 constant.
DY_CLEAR:
        PRE IX,$2
DY_CL_LOOP:
        ST $31,(IX+$SX)
        ADW $2,$SY
        PRE IX,$2
        SB $20,$SY
        JP NZ,DY_CL_LOOP
        RTN

DY_RUN_ONE:
        LDW $2,DY_POS
        LD $20,38
        CAL DY_CLEAR
        LDW $2,DY_NEG
        LD $20,38
        CAL DY_CLEAR
        LDW $2,DY_DIFF
        LD $20,38
        CAL DY_CLEAR
        PRE IX,DY_FUNC
        LD $0,(IX+$SX)
        AN $0,$0
        JP Z,DY_SETUP_SIN
        SBC $0,1
        JP Z,DY_SETUP_SQR
        SBC $0,2
        JP Z,DY_SETUP_ADD
        SBC $0,3
        JP Z,DY_SETUP_COS
        SBC $0,4
        JP Z,DY_SETUP_SUB
        SBC $0,5
        JP Z,DY_SETUP_MUL
        SBC $0,6
        JP Z,DY_SETUP_DIV
        SBC $0,7
        JP Z,DY_SETUP_EXP
        SBC $0,8
        JP Z,DY_SETUP_LOG
        SBC $0,9
        JP Z,DY_SETUP_LGT
        SBC $0,10
        JP Z,DY_SETUP_TAN
        SBC $0,11
        JP Z,DY_SETUP_SQ
        SBC $0,12
        JP Z,DY_SETUP_POWER
        SBC $0,13
        JP Z,DY_SETUP_ATN
        SBC $0,14
        JP Z,DY_SETUP_ACS
        SBC $0,15
        JP Z,DY_SETUP_ASN
        SBC $0,16
        JP Z,DY_SETUP_FIX
        LDW $2,DY_ARG_INC
        JP DY_SETUP_PTR
DY_SETUP_ATN:
        LDW $2,DY_ARG_ATN
        JP DY_SETUP_PTR
DY_SETUP_ASN:
        LDW $2,DY_ARG_ASN
        JP DY_SETUP_PTR
DY_SETUP_FIX:
        LDW $2,DY_ARG_FIX
        JP DY_SETUP_PTR
DY_SETUP_ACS:
        LDW $2,DY_ARG_ACS
        JP DY_SETUP_PTR
DY_SETUP_LGT:
        LDW $2,DY_ARG_LGT
        JP DY_SETUP_PTR
DY_SETUP_TAN:
        LDW $2,DY_ARG_TAN
        JP DY_SETUP_PTR
DY_SETUP_SQ:
        LDW $2,DY_ARG_SQ
        JP DY_SETUP_PTR
DY_SETUP_POWER:
        LDW $2,DY_ARG_POWER
        JP DY_SETUP_PTR
DY_SETUP_DIV:
        LDW $2,DY_ARG_DIV
        JP DY_SETUP_PTR
DY_SETUP_EXP:
        LDW $2,DY_ARG_EXP
        JP DY_SETUP_PTR
DY_SETUP_LOG:
        LDW $2,DY_ARG_LOG
        JP DY_SETUP_PTR
DY_SETUP_MUL:
        LDW $2,DY_ARG_MUL
        JP DY_SETUP_PTR
DY_SETUP_SIN:
        LDW $2,DY_ARG_SIN
        JP DY_SETUP_PTR
DY_SETUP_SQR:
        LDW $2,DY_ARG_SQR
        JP DY_SETUP_PTR
DY_SETUP_ADD:
        LDW $2,DY_ARG_ADD
        JP DY_SETUP_PTR
DY_SETUP_COS:
        LDW $2,DY_ARG_COS
        JP DY_SETUP_PTR
DY_SETUP_SUB:
        LDW $2,DY_ARG_SUB
DY_SETUP_PTR:
        PRE IX,DY_PTR
        STW $2,(IX+$SX)
        PRE IX,DY_CASE
        LD $0,0
        ST $0,(IX+$SX)
DY_TERM_LOOP:
; Dispatch BEFORE loading operands; $0..$8 are the entire binary RHS.
; A selector read after DY_LOAD_B would corrupt its least-significant B9 byte.
        PRE IX,DY_FUNC
        LD $21,(IX+$SX)
        AN $21,$21
        JP Z,DY_DO_SIN
        SBC $21,1
        JP Z,DY_DO_SQR
        SBC $21,2
        JP Z,DY_DO_ADD
        SBC $21,3
        JP Z,DY_DO_COS
        SBC $21,4
        JP Z,DY_DO_SUB
        SBC $21,5
        JP Z,DY_DO_MUL
        SBC $21,6
        JP Z,DY_DO_DIV
        SBC $21,7
        JP Z,DY_DO_EXP
        SBC $21,8
        JP Z,DY_DO_LOG
        SBC $21,9
        JP Z,DY_DO_LGT
        SBC $21,10
        JP Z,DY_DO_TAN
        SBC $21,11
        JP Z,DY_DO_SQ
        SBC $21,12
        JP Z,DY_DO_POWER
        SBC $21,13
        JP Z,DY_DO_ATN
        SBC $21,14
        JP Z,DY_DO_ACS
        SBC $21,15
        JP Z,DY_DO_ASN
        SBC $21,16
        JP Z,DY_DO_FIX
        JP DY_DO_INC
DY_DO_SIN:
        CAL DY_LOAD_A
        CAL MATH_SIN
        JP DY_RETURNED
DY_DO_SQR:
        CAL DY_LOAD_A
        CAL MATH_SQR
        JP DY_RETURNED
DY_DO_ADD:
        CAL DY_LOAD_A
        CAL DY_LOAD_B
        CAL MATH_ADD
        JP DY_RETURNED
DY_DO_COS:
        CAL DY_LOAD_A
        CAL MATH_COS
        JP DY_RETURNED
DY_DO_SUB:
        CAL DY_LOAD_A
        CAL DY_LOAD_B
        CAL MATH_SUB
        JP DY_RETURNED
DY_DO_MUL:
        CAL DY_LOAD_A
        CAL DY_LOAD_B
        CAL MATH_MUL
        JP DY_RETURNED
DY_DO_DIV:
        CAL DY_LOAD_A
        CAL DY_LOAD_B
        CAL MATH_DIV
        JP DY_RETURNED
DY_DO_EXP:
        CAL DY_LOAD_A
        CAL MATH_EXP
        JP DY_RETURNED
DY_DO_LOG:
        CAL DY_LOAD_A
        CAL MATH_LOG
        JP DY_RETURNED
DY_DO_LGT:
        CAL DY_LOAD_A
        CAL MATH_LGT
        JP DY_RETURNED
DY_DO_TAN:
        CAL DY_LOAD_A
        CAL MATH_TAN
        JP DY_RETURNED
DY_DO_SQ:
        CAL DY_LOAD_A
        CAL MATH_SQ
        JP DY_RETURNED
DY_DO_POWER:
        CAL DY_LOAD_A
        CAL DY_LOAD_B
        CAL MATH_POWER
        JP DY_RETURNED
DY_DO_ATN:
        CAL DY_LOAD_A
        CAL MATH_ATN
        JP DY_RETURNED
DY_DO_ACS:
        CAL DY_LOAD_A
        CAL MATH_ACS
        JP DY_RETURNED
DY_DO_ASN:
        CAL DY_LOAD_A
        CAL MATH_ASN
        JP DY_RETURNED
DY_DO_FIX:
        CAL DY_LOAD_A
        CAL MATH_FIX
        JP DY_RETURNED
DY_DO_INC:
        CAL DY_LOAD_A
        CAL MATH_INC
DY_RETURNED:
; First capture all nine returned bytes, before any report/accumulator work.
        LDW $8,DY_LAST9
        CAL MR_SAVE9
        CAL DY_ADV_PTR
        CAL DY_ADD_B9
        AN $0,$0
        JP NZ,DY_BAD_TERM
        PRE IX,DY_CASE
        LD $0,(IX+$SX)
        AD $0,$SY
        ST $0,(IX+$SX)
        SBC $0,16
        JP C,DY_TERM_LOOP
; All sixteen native values have been captured and added exactly in
; a separate 38-digit decimal accumulator. Round the sum only once.
        CAL DY_FINAL
        AN $0,$0
        JP NZ,DY_BAD_SUM
        CAL DY_SAVE_SUM
        CAL DY_COMPARE
        LD $21,$0
        CAL DY_SET_STATUS
        SBC $21,2
        JP Z,DY_RECORD_SUM
        SBC $21,3
        JP Z,DY_BAD_COMPARE
        RTN
DY_BAD_COMPARE:
        CAL DY_RECORD_SUM
        CAL DY_ADD_ERR
        RTN
DY_BAD_TERM:
        LD $21,3
        CAL DY_SET_STATUS
        CAL DY_RECORD_TERM
        CAL DY_ADD_ERR
        RTN
DY_BAD_SUM:
        LD $21,3
        CAL DY_SET_STATUS
        CAL DY_RECORD_SUM
        CAL DY_ADD_ERR
        RTN
DY_RECORD_TERM:
        PRE IX,DY_ERR_FN
        LD $0,(IX+$SX)
        SBC $0,255
        RTN NZ
        PRE IX,DY_FUNC
        LD $0,(IX+$SX)
        PRE IX,DY_ERR_FN
        ST $0,(IX+$SX)
        PRE IX,DY_CASE
        LD $0,(IX+$SX)
        AD $0,$SY
        PRE IX,DY_ERR_K
        ST $0,(IX+$SX)
        LDW $2,DY_LAST9
        JP DY_COPY_ERR
DY_RECORD_SUM:
; Aggregate disagreement cannot identify an individual term: record k=0.
        PRE IX,DY_ERR_FN
        LD $0,(IX+$SX)
        SBC $0,255
        RTN NZ
        PRE IX,DY_FUNC
        LD $0,(IX+$SX)
        PRE IX,DY_ERR_FN
        ST $0,(IX+$SX)
        PRE IX,DY_ERR_K
        ST $31,(IX+$SX)
        LDW $2,DY_OUTB9
DY_COPY_ERR:
        LDW $4,DY_ERR_RAW
        LD $20,9
DY_COPY_ERR_L:
        PRE IX,$2
        LD $16,(IX+$SX)
        PRE IX,$4
        ST $16,(IX+$SX)
        ADW $2,$SY
        ADW $4,$SY
        SB $20,$SY
        JP NZ,DY_COPY_ERR_L
        RTN
DY_ADD_ERR:
        PRE IX,DY_ERR_CT
        LD $0,(IX+$SX)
        AD $0,$SY
        ST $0,(IX+$SX)
        RTN

; Preserve caller's A registers ($10..$18); only use $20..$25.
DY_LOAD_A:
        PRE IX,DY_PTR
        LDW $22,(IX+$SX)
        PRE IZ,$22
        LDM $10,(IZ+$SX),8
        LDW $24,$22
        LDW $20,8
        ADW $24,$20
        PRE IZ,$24
        LD $18,(IZ+$SX)
        RTN
; Binary rhs occupies the next 9 ROM bytes; A is already loaded.
; Compute all pointers before loading $0..$8, avoid clobber of operand B.
DY_LOAD_B:
        PRE IX,DY_PTR
        LDW $22,(IX+$SX)
        LDW $24,9
        ADW $22,$24
        LDW $24,$22
        LDW $20,8
        ADW $24,$20
        PRE IZ,$22
        LDM $0,(IZ+$SX),8
        PRE IZ,$24
        LD $8,(IZ+$SX)
        RTN
DY_ADV_PTR:
        PRE IX,DY_PTR
        LDW $22,(IX+$SX)
        LDW $24,9
        PRE IX,DY_FUNC
        LD $0,(IX+$SX)
        SBC $0,2
        JP Z,DY_ADV_BIN
        SBC $0,4
        JP Z,DY_ADV_BIN
        SBC $0,5
        JP Z,DY_ADV_BIN
        SBC $0,6
        JP Z,DY_ADV_BIN       ; DIV also consumes A+B
        SBC $0,12
        JP NZ,DY_ADV_GO       ; POWER also consumes A+B
DY_ADV_BIN:
        LDW $24,18
DY_ADV_GO:
        ADW $22,$24
        PRE IX,DY_PTR
        STW $22,(IX+$SX)
        RTN

; Save final observed nine B9 bytes in 3 x 9; no reference substitution.
DY_SAVE_SUM:
        CAL DY_OFF_RESULT
        LDW $2,DY_OUTB9
        LD $20,9
DY_SAVE_LOOP:
        PRE IX,$2
        LD $16,(IX+$SX)
        PRE IX,$4
        ST $16,(IX+$SX)
        ADW $2,$SY
        ADW $4,$SY
        SB $20,$SY
        JP NZ,DY_SAVE_LOOP
        RTN
; Store 14 primary, 2 overflow, and 2 new results in separate
; verified private RAM gaps, never writing across 2D00.
DY_OFF_RESULT:
        PRE IX,DY_FUNC
        LD $20,(IX+$SX)
        SBC $20,16
        JP C,DY_RES_LT16
        LDW $4,DY_RES_FIX
        SBC $20,16
        RTN Z
        LDW $4,DY_RES_INC
        RTN
DY_RES_LT16:
        SBC $20,14
        JP C,DY_RES_PRIMARY
        LDW $4,DY_RES_OVF
        SB $20,14
        AN $20,$20
        RTN Z
        LDW $0,9
        ADW $4,$0
        RTN
DY_RES_PRIMARY:
        LDW $4,DY_RESULTS
        JP DY_OFF9
; On entry $4 = base. Offset by current DY_FUNC * 9.
DY_OFF9:
        PRE IX,DY_FUNC
        LD $20,(IX+$SX)
DY_OFF9_L:
        AN $20,$20
        RTN Z
        LDW $0,9
        ADW $4,$0
        SB $20,$SY
        JP DY_OFF9_L
; On entry $4 = base. Offset by current DY_FUNC * 14.
DY_OFF14:
        PRE IX,DY_FUNC
        LD $20,(IX+$SX)
DY_OFF14_L:
        AN $20,$20
        RTN Z
        LDW $0,14
        ADW $4,$0
        SB $20,$SY
        JP DY_OFF14_L
; On entry $4 = base. Offset by current DY_FUNC * 18.
DY_OFF18:
        PRE IX,DY_FUNC
        LD $20,(IX+$SX)
DY_OFF18_L:
        AN $20,$20
        RTN Z
        LDW $0,18
        ADW $4,$0
        SB $20,$SY
        JP DY_OFF18_L

; Result states: exact 1, near 4, outside tolerance 2, CHECK 3.
DY_COMPARE:
        LDW $4,DY_REF_ROM
        CAL DY_OFF9
        PRE IZ,$4
        PRE IX,DY_OUTB9
        LD $20,9
        LD $21,1
DY_EXACT_L:
        LDI $16,(IZ+$SX)
        LDI $17,(IX+$SX)
        SBC $16,$17
        JP Z,DY_EXACT_NEXT
        LD $21,0
DY_EXACT_NEXT:
        SB $20,$SY
        JP NZ,DY_EXACT_L
        AN $21,$21
        JP NZ,DY_EXACT_YES
; Decode actual observed B9 aggregate, not its expected oracle.
        LDW $4,DY_OUTB9
        CAL MR_B9_DEC
        PRE IX,MR_BOK
        LD $0,(IX+$SX)
        AN $0,$0
        JP Z,DY_CHECK
        LDW $4,DY_LO_SIG
        CAL DY_OFF14
        CAL MR_B9_SIGNCMP
        SBC $0,1                    ; actual below lower threshold
        JP Z,DY_FAIL
        LDW $4,DY_HI_SIG
        CAL DY_OFF14
        CAL MR_B9_SIGNCMP
        SBC $0,2                    ; actual above upper threshold
        JP Z,DY_FAIL
        LD $0,4
        RTN
DY_EXACT_YES:
        LD $0,1
        RTN
DY_FAIL:
        LD $0,2
        RTN
DY_CHECK:
        LD $0,3
        RTN
DY_SET_STATUS:
; Input $21=1/4/2/3; preserve it for caller.
        PRE IX,DY_FUNC
        LD $20,(IX+$SX)
        LDW $4,DY_STATUS
        LDW $0,0
        LD $0,$20
        ADW $4,$0
        PRE IX,$4
        ST $21,(IX+$SX)
; Store how many native results were successfully accumulated (0..16).
        PRE IX,DY_FUNC
        LD $20,(IX+$SX)
        CAL DY_COUNT_PTR
        PRE IX,DY_CASE
        LD $0,(IX+$SX)
        PRE IX,$4
        ST $0,(IX+$SX)
        RTN

DY_REPORT:
        CAL VT23_CLEAR
        PRE IX,MR_VROW
        LD $0,1
        ST $0,(IX+$SX)
        LDW $4,DY_TITLE
        LD $9,1
        LD $13,36
        CAL MR_PUT_ROM
        PRE IX,MR_VROW
        LD $0,2
        ST $0,(IX+$SX)
        LDW $4,DY_META
        LD $9,1
        LD $13,60
        CAL MR_PUT_ROM
        PRE IX,MR_VROW
        LD $0,4
        ST $0,(IX+$SX)
        LDW $4,DY_HEADER
        LD $9,1
        LD $13,77
        CAL MR_PUT_ROM
        PRE IX,DY_FUNC
        LD $0,0
        ST $0,(IX+$SX)
DY_RPT_LOOP:
        CAL DY_RPT_ROW
        PRE IX,DY_FUNC
        LD $0,(IX+$SX)
        AD $0,$SY
        ST $0,(IX+$SX)
        SBC $0,18
        JP C,DY_RPT_LOOP
        PRE IX,MR_VROW
        LD $0,23
        ST $0,(IX+$SX)
        LDW $4,DY_FOOT
        LD $9,1
        LD $13,72
        CAL MR_PUT_ROM
        PRE IX,MR_VROW
        LD $0,24
        ST $0,(IX+$SX)
        LDW $4,DY_ERRTXT
        LD $9,1
        LD $13,24
        CAL MR_PUT_ROM
        PRE IX,DY_ERR_CT
        LD $0,(IX+$SX)
        CAL DY_TO_2DIG       ; 00..18 rows, never ASCII ':' for ten errors
        LDW $4,DY_NTXT
        LD $9,25
        LD $13,2
        CAL MR_PUT_RAM
        RTN

DY_RPT_ROW:
        PRE IX,DY_FUNC
        LD $0,(IX+$SX)
        AD $0,5
        PRE IX,MR_VROW
        ST $0,(IX+$SX)
        LDW $4,DY_FN_NAMES
        CAL DY_OFF8
        LD $9,1
        LD $13,8
        CAL MR_PUT_ROM
        CAL DY_STATUS_GET
        SBC $0,3
        JP Z,DY_RPT_CHECK
        CAL DY_OFF_RESULT
        CAL MR_B9_DEC
        PRE IX,MR_BOK
        LD $0,(IX+$SX)
        AN $0,$0
        JP Z,DY_RPT_CHECK
        LDW $4,MR_TEXT
        LD $9,12
        LD $13,18
        CAL MR_PUT_RAM
        JP DY_RPT_OP
DY_RPT_CHECK:
        LDW $4,DY_CHECK_TXT
        LD $9,12
        LD $13,18
        CAL MR_PUT_ROM
DY_RPT_OP:
        CAL DY_STATUS_GET
        SBC $0,1
        JP Z,DY_OP_EQ
        SBC $0,4
        JP Z,DY_OP_NEAR
        SBC $0,2
        JP Z,DY_OP_FAIL
        LDW $4,DY_QTXT
        JP DY_OP_SHOW
DY_OP_EQ:
        LDW $4,DY_EQTXT
        JP DY_OP_SHOW
DY_OP_NEAR:
        LDW $4,DY_NEARTXT
        JP DY_OP_SHOW
DY_OP_FAIL:
        LDW $4,DY_FAILTXT
DY_OP_SHOW:
        LD $9,33
        LD $13,4
        CAL MR_PUT_ROM
        LDW $4,DY_ORACLE_TXT
        CAL DY_OFF18
        LD $9,40
        LD $13,18
        CAL MR_PUT_ROM
; completed successful accumulation count from stored DY_COUNT[function]
        PRE IX,DY_FUNC
        LD $20,(IX+$SX)
        CAL DY_COUNT_PTR
        PRE IX,$4
        LD $0,(IX+$SX)
        CAL DY_TO_2DIG
        LDW $4,DY_NTXT
        LD $9,66
        LD $13,2
        CAL MR_PUT_RAM
        RTN
; $20 index 0..17; returns $4 pointer into split count storage.
DY_COUNT_PTR:
        LDW $4,DY_COUNT
        SBC $20,16
        JP C,DY_COUNT_MAIN
        LDW $4,DY_COUNT2
        SB $20,16
DY_COUNT_MAIN:
        LDW $0,0
        LD $0,$20
        ADW $4,$0
        RTN

DY_STATUS_GET:
        PRE IX,DY_FUNC
        LD $20,(IX+$SX)
        LDW $4,DY_STATUS
        LDW $0,0
        LD $0,$20
        ADW $4,$0
        PRE IX,$4
        LD $0,(IX+$SX)
        RTN
DY_OFF8:
        PRE IX,DY_FUNC
        LD $20,(IX+$SX)
DY_OFF8_L:
        AN $20,$20
        RTN Z
        LDW $0,8
        ADW $4,$0
        SB $20,$SY
        JP DY_OFF8_L
DY_TO_2DIG:
; Input 0..16 in $0. Output two ASCII chars at DY_NTXT.
        LD $1,0
DY_CNT_LOOP:
        LD $2,$0
        SBC $2,10
        JP C,DY_CNT_DONE
        SB $0,10
        AD $1,$SY
        JP DY_CNT_LOOP
DY_CNT_DONE:
        AD $0,&H30
        AD $1,&H30
        PRE IX,DY_NTXT
        STI $1,(IX+$SX)
        ST $0,(IX+$SX)
        RTN

DY_DETAIL:
        CAL VT23_CLEAR
        PRE IX,MR_VROW
        LD $0,1
        ST $0,(IX+$SX)
        LDW $4,DY_DET_TITLE
        LD $9,1
        LD $13,42
        CAL MR_PUT_ROM
        LD $8,5
        LDW $10,DY_RESULTS
        LDW $14,DY_RAW_SIN
        CAL MR_RAW_ROW
        LD $8,6
        LDW $10,DY_RESULTS+9
        LDW $14,DY_RAW_SQR
        CAL MR_RAW_ROW
        LD $8,7
        LDW $10,DY_RESULTS+18
        LDW $14,DY_RAW_ADD
        CAL MR_RAW_ROW
        LD $8,8
        LDW $10,DY_RESULTS+27
        LDW $14,DY_RAW_COS
        CAL MR_RAW_ROW
        LD $8,9
        LDW $10,DY_RESULTS+36
        LDW $14,DY_RAW_SUB
        CAL MR_RAW_ROW
        LD $8,10
        LDW $10,DY_RESULTS+45
        LDW $14,DY_RAW_MUL
        CAL MR_RAW_ROW
        LD $8,11
        LDW $10,DY_RESULTS+54
        LDW $14,DY_RAW_DIV
        CAL MR_RAW_ROW
        LD $8,12
        LDW $10,DY_RESULTS+63
        LDW $14,DY_RAW_EXP
        CAL MR_RAW_ROW
        LD $8,13
        LDW $10,DY_RESULTS+72
        LDW $14,DY_RAW_LOG
        CAL MR_RAW_ROW
        LD $8,14
        LDW $10,DY_RESULTS+81
        LDW $14,DY_RAW_LGT
        CAL MR_RAW_ROW
        LD $8,15
        LDW $10,DY_RESULTS+90
        LDW $14,DY_RAW_TAN
        CAL MR_RAW_ROW
        LD $8,16
        LDW $10,DY_RESULTS+99
        LDW $14,DY_RAW_SQ
        CAL MR_RAW_ROW
        LD $8,17
        LDW $10,DY_RESULTS+108
        LDW $14,DY_RAW_POWER
        CAL MR_RAW_ROW
        LD $8,18
        LDW $10,DY_RESULTS+117
        LDW $14,DY_RAW_ATN
        CAL MR_RAW_ROW
        LD $8,19
        LDW $10,DY_RES_OVF
        LDW $14,DY_RAW_ACS
        CAL MR_RAW_ROW
        LD $8,20
        LDW $10,DY_RES_OVF+9
        LDW $14,DY_RAW_ASN
        CAL MR_RAW_ROW
        LD $8,21
        LDW $10,DY_RES_FIX
        LDW $14,DY_RAW_FIX
        CAL MR_RAW_ROW
        LD $8,22
        LDW $10,DY_RES_INC
        LDW $14,DY_RAW_INC
        CAL MR_RAW_ROW
        PRE IX,DY_ERR_FN
        LD $0,(IX+$SX)
        SBC $0,255
        JP Z,DY_DET_OK
        PRE IX,MR_VROW
        LD $0,23
        ST $0,(IX+$SX)
        LDW $4,DY_FIRST_TXT
        LD $9,1
        LD $13,44
        CAL MR_PUT_ROM
        PRE IX,DY_ERR_FN
        LD $20,(IX+$SX)
        LDW $4,DY_FN_NAMES
DY_NAME_SKIP:
        AN $20,$20
        JP Z,DY_NAME_READY
        LDW $0,8
        ADW $4,$0
        SB $20,$SY
        JP DY_NAME_SKIP
DY_NAME_READY:
        LD $9,50
        LD $13,3
        CAL MR_PUT_ROM
        PRE IX,DY_ERR_K
        LD $0,(IX+$SX)
        CAL DY_TO_2DIG
        LDW $4,DY_NTXT
        LD $9,55
        LD $13,2
        CAL MR_PUT_RAM
        LD $8,24
        LDW $10,DY_ERR_RAW
        LDW $14,DY_RAW_BAD
        CAL MR_RAW_ROW
        RTN
DY_DET_OK:
        PRE IX,MR_VROW
        LD $0,23
        ST $0,(IX+$SX)
        LDW $4,DY_ALL_OK
        LD $9,1
        LD $13,37
        CAL MR_PUT_ROM
        RTN

DY_HOME_HINT: DB "                                "
DY_MENU_TITLE: DB "MathROM - Test options          "
DY_MENU_TEXT:  DB "                                "
DY_MENU_HINT:  DB "                                "
DY_MENU_KEYS:  DB "[dyadic][5test ][      ][      ]"
DY_FN_NAMES:
        DB "SIN     "
        DB "SQR     "
        DB "ADD     "
        DB "COS     "
        DB "SUB     "
        DB "MUL     "
        DB "DIV     "
        DB "EXP     "
        DB "LOG     "
        DB "LGT     "
        DB "TAN     "
        DB "SQ      "
        DB "POWER   "
        DB "ATN     "
        DB "ACS     "
        DB "ASN     "
        DB "FIX     "
        DB "INC     "
DY_TITLE: DB "MathROM r028 DYADIC-16 (18 FUNCS)   "
DY_META: DB "SEED=61700  N=16 PER FUNCTION  MODE=DEG  SKIPS=0  Page 1    "
DY_HEADER: DB "FUNC      COMPUTED            OP    ORACLE                  VALID/16         "
DY_FOOT: DB "= BYTE EXACT   =~ WITHIN ENVELOPE   != OUTSIDE   ? CHECK                "
DY_ERRTXT: DB "UNEXPECTED ERROR ROWS:  "
DY_EQTXT: DB " =  "
DY_NEARTXT: DB " =~ "
DY_FAILTXT: DB " != "
DY_QTXT: DB " ?  "
DY_CHECK_TXT: DB "CHECK / INCOMPLETE"
DY_DET_TITLE: DB "DYADIC-16 RAW AGGREGATES / Page 2         "
DY_RAW_SIN: DB "SIN B9    "
DY_RAW_SQR: DB "SQR B9    "
DY_RAW_ADD: DB "ADD B9    "
DY_RAW_COS: DB "COS B9    "
DY_RAW_SUB: DB "SUB B9    "
DY_RAW_MUL: DB "MUL B9    "
DY_RAW_DIV: DB "DIV B9    "
DY_RAW_EXP: DB "EXP B9    "
DY_RAW_LOG: DB "LOG B9    "
DY_RAW_LGT: DB "LGT B9    "
DY_RAW_TAN: DB "TAN B9    "
DY_RAW_SQ: DB "SQ B9     "
DY_RAW_POWER: DB "POWER B9  "
DY_RAW_ATN: DB "ATN B9    "
DY_RAW_ACS: DB "ACS B9    "
DY_RAW_ASN: DB "ASN B9    "
DY_RAW_FIX: DB "FIX B9    "
DY_RAW_INC: DB "INC B9    "
DY_RAW_BAD: DB "BAD B9    "
DY_DET_NOTE: DB "FIRST K=00: AGGREGATE ONLY; K>00: NATIVE TERM. Q/MENU TO EXIT.      "
DY_FIRST_TXT: DB "FIRST FAIL/ERROR: REPLAY SEED=61700  CASE K="
DY_ALL_OK: DB "NO OUT-OF-ENVELOPE OR INCOMPLETE ROWS"

; Host-only table generator owns this source; exact oracle bytes are
; never copied into the computed accumulator or the result records.
#INCLUDE (apps/dyadic16_tables.inc)

; Adapted extended-decimal accumulator from Chapter09 Math10
; with independent DY_ labels and MathROM-only memory ownership.
DY_ADD_B9:
; Exactly zero is a valid term: LN(1)=LGT(1)=0. Do not decode
; its cleared exponent field as a nonzero out-of-range exponent.
        PRE IX,DY_LAST9
        LD $20,9
        LD $0,0
DY_ZERO_SCN:
        LDI $16,(IX+$SX)
        OR $0,$16
        SB $20,$SY
        JP NZ,DY_ZERO_SCN
        AN $0,$0
        JP Z,DY_ADD_OK
        PRE IX,DY_LAST9+8
        LD $16,(IX+$SX)
        AN $16,&H0F
        SBC $16,5
        JP C,DY_SIGN_POS
        SB $16,5
        LD $17,5
        JP DY_SIGN_STO
DY_SIGN_POS:
        LD $17,0
DY_SIGN_STO:
        PRE IX,DY_SIGN
        ST $17,(IX+$SX)
        SBC $16,2
        JP NC,DY_ADD_BAD
        PRE IX,DY_LAST9+7
        LD $17,(IX+$SX)
        LD $18,$17
        AN $18,&H0F
        SBC $18,10
        JP NC,DY_ADD_BAD
        LD $19,$17
        AN $19,&HF0
DY_ADD_TENS:
        AN $19,$19
        JP Z,DY_ADD_BIAS
        SB $19,&H10
        AD $18,10
        JP DY_ADD_TENS
DY_ADD_BIAS:
        AN $16,$16
        JP NZ,DY_ADD_HUND
        SBC $18,88
        JP C,DY_ADD_BAD
        SB $18,88
        JP DY_ADD_SHIFT
DY_ADD_HUND:
        AD $18,12
DY_ADD_SHIFT:
        SBC $18,25
        JP NC,DY_ADD_BAD
        PRE IX,DY_SHIFT
        ST $18,(IX+$SX)
        LDW $4,DY_POS
        PRE IX,DY_SIGN
        LD $0,(IX+$SX)
        AN $0,$0
        JP Z,DY_ADD_PTR
        LDW $4,DY_NEG
DY_ADD_PTR:
        LDW $2,0
        LD $2,$18
        ADW $4,$2
        PRE IX,DY_CARRY
        ST $31,(IX+$SX)
        LDW $2,DY_LAST9
        LD $20,6
DY_ADD_BYTE:
        PRE IX,$2
        LD $16,(IX+$SX)
        PRE IX,DY_RAW
        ST $16,(IX+$SX)
        ADW $2,$SY
        LD $17,$16
        AN $17,&H0F
        SBC $17,10
        JP NC,DY_ADD_BAD
        CAL DY_ADD_DIGIT
        PRE IX,DY_RAW
        LD $16,(IX+$SX)
        AN $16,&HF0
        LD $17,0
DY_ADD_HINIB:
        AN $16,$16
        JP Z,DY_ADD_HIDONE
        SB $16,&H10
        AD $17,$SY
        JP DY_ADD_HINIB
DY_ADD_HIDONE:
        SBC $17,10
        JP NC,DY_ADD_BAD
        CAL DY_ADD_DIGIT
        SB $20,$SY
        JP NZ,DY_ADD_BYTE
        PRE IX,DY_LAST9+6
        LD $17,(IX+$SX)
        SBC $17,10
        JP NC,DY_ADD_BAD
        CAL DY_ADD_DIGIT
; Finish decimal carry propagation across reserved accumulator lanes.
DY_ADD_CARRY:
        PRE IX,DY_CARRY
        LD $17,(IX+$SX)
        AN $17,$17
        JP Z,DY_ADD_OK
; Move the pending carry into $17, and clear its old slot.
; DY_ADD_DIGIT separately adds DY_CARRY, so retaining it doubles carry.
        ST $31,(IX+$SX)
        CAL DY_ADD_DIGIT
        JP DY_ADD_CARRY
DY_ADD_OK:
        LD $0,0
        RTN
DY_ADD_BAD:
        LD $0,1
        RTN

; $4 points to current decimal digit; $17=0..9; preserve $2,$20.
DY_ADD_DIGIT:
        PRE IX,$4
        LD $16,(IX+$SX)
        AD $16,$17
        PRE IX,DY_CARRY
        LD $21,(IX+$SX)
        AD $16,$21
        LD $21,0
        SBC $16,10
        JP C,DY_ADD_STORE
        SB $16,10
        LD $21,1
DY_ADD_STORE:
        PRE IX,$4
        ST $16,(IX+$SX)
        ADW $4,$SY
        PRE IX,DY_CARRY
        ST $21,(IX+$SX)
        RTN

DY_FINAL:
; Compare unsigned positive and negative 38-digit accumulators MSD down.
        LDW $2,DY_POS+37
        LDW $4,DY_NEG+37
        LD $20,38
        PRE IX,DY_SUMSIGN
        ST $31,(IX+$SX)
DY_CMP_MAG:
        PRE IX,$2
        LD $16,(IX+$SX)
        PRE IX,$4
        LD $17,(IX+$SX)
        SBC $16,$17
        JP C,DY_NEG_WINS
        JP NZ,DY_POS_WINS
        SBW $2,$SY
        SBW $4,$SY
        SB $20,$SY
        JP NZ,DY_CMP_MAG
DY_POS_WINS:
        LDW $2,DY_POS
        LDW $4,DY_NEG
        JP DY_SUB_INIT
DY_NEG_WINS:
        PRE IX,DY_SUMSIGN
        LD $0,5
        ST $0,(IX+$SX)
        LDW $2,DY_NEG
        LDW $4,DY_POS
DY_SUB_INIT:
        LDW $6,DY_DIFF
        LD $20,38
        LD $21,0
DY_SUB_LOOP:
        PRE IX,$2
        LD $16,(IX+$SX)
        PRE IX,$4
        LD $17,(IX+$SX)
        AD $17,$21
        LD $21,0
        SBC $16,$17
        JP NC,DY_SUB_NOBORROW
        AD $16,10
        LD $21,1
DY_SUB_NOBORROW:
        SB $16,$17
        PRE IX,$6
        ST $16,(IX+$SX)
        ADW $2,$SY
        ADW $4,$SY
        ADW $6,$SY
        SB $20,$SY
        JP NZ,DY_SUB_LOOP
        AN $21,$21
        JP NZ,DY_ADD_BAD
; Find most significant nonzero decimal digit of exact signed sum.
        LDW $2,DY_DIFF+37
        LD $18,37
DY_FIND_TOP:
        PRE IX,$2
        LD $16,(IX+$SX)
        AN $16,$16
        JP NZ,DY_GOT_TOP
        AN $18,$18
        JP Z,DY_ADD_BAD
        SBW $2,$SY
        SB $18,$SY
        JP DY_FIND_TOP
DY_GOT_TOP:
; All six admitted aggregate sums are nonzero with >=13 digits.
        SBC $18,13
        JP C,DY_ADD_BAD
        PRE IX,DY_TOP
        ST $18,(IX+$SX)
        LDW $4,DY_MANT+12
        LD $20,13
DY_GET_MANT:
        PRE IX,$2
        LD $16,(IX+$SX)
        PRE IX,$4
        ST $16,(IX+$SX)
        SBW $2,$SY
        SBW $4,$SY
        SB $20,$SY
        JP NZ,DY_GET_MANT
; $2 now points to fourteenth digit (rounding digit).
        PRE IX,$2
        LD $0,(IX+$SX)
        SBC $0,5
        JP C,DY_PACK_MANT
        LDW $2,DY_MANT
        LD $20,13
DY_ROUND_UP:
        PRE IX,$2
        LD $16,(IX+$SX)
        AD $16,$SY
        SBC $16,10
        JP C,DY_ROUND_STORE
        LD $16,0
        ST $16,(IX+$SX)
        ADW $2,$SY
        SB $20,$SY
        JP NZ,DY_ROUND_UP
; Exact carry 9999999999999 -> 10000000000000.
        PRE IX,DY_TOP
        LD $0,(IX+$SX)
        AD $0,$SY
        ST $0,(IX+$SX)
        PRE IX,DY_MANT+12
        LD $16,1
        ST $16,(IX+$SX)
        JP DY_PACK_MANT
DY_ROUND_STORE:
        PRE IX,$2
        ST $16,(IX+$SX)
DY_PACK_MANT:
        LDW $2,DY_MANT
        LDW $4,DY_OUTB9
        LD $20,6
DY_PACK_PAIR:
        PRE IX,$2
        LD $16,(IX+$SX)
        ADW $2,$SY
        PRE IX,$2
        LD $17,(IX+$SX)
        ADW $2,$SY
        AD $17,$17
        AD $17,$17
        AD $17,$17
        AD $17,$17
        OR $16,$17
        PRE IX,$4
        ST $16,(IX+$SX)
        ADW $4,$SY
        SB $20,$SY
        JP NZ,DY_PACK_PAIR
        PRE IX,DY_MANT+12
        LD $16,(IX+$SX)
        PRE IX,DY_OUTB9+6
        ST $16,(IX+$SX)
; Biased exponent 100+(TOP-24) = TOP+76.
        PRE IX,DY_TOP
        LD $18,(IX+$SX)
        AD $18,76
        LD $19,0
        SBC $18,100
        JP C,DY_PACK_TENS
        SB $18,100
        LD $19,1
DY_PACK_TENS:
        LD $17,0
DY_TENS_LOOP:
        SBC $18,10
        JP C,DY_PACK_EXP
        SB $18,10
        AD $17,&H10
        JP DY_TENS_LOOP
DY_PACK_EXP:
        OR $17,$18
        PRE IX,DY_OUTB9+7
        ST $17,(IX+$SX)
        PRE IX,DY_SUMSIGN
        LD $0,(IX+$SX)
        AD $0,$19
        PRE IX,DY_OUTB9+8
        ST $0,(IX+$SX)
        LD $0,0
        RTN

