From c150c4d6ba4c1a85d0058923797e93f4df9fc80d Mon Sep 17 00:00:00 2001 From: jsa2340 <36245808+jsa2340@users.noreply.github.com> Date: Tue, 12 Apr 2022 21:09:17 +1000 Subject: [PATCH 1/8] Word table special functions added --- Development/SADV4_07_16b.exe | Bin 0 -> 131584 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Development/SADV4_07_16b.exe diff --git a/Development/SADV4_07_16b.exe b/Development/SADV4_07_16b.exe new file mode 100644 index 0000000000000000000000000000000000000000..62bfb10f125c27694d1f7cfd98c52239c338b4ba GIT binary patch literal 131584 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 J^ZyP9{wJCA&y)ZF literal 0 HcmV?d00001 From 4692ab0737d805ffeb0a27b8b035e2311bdac2da Mon Sep 17 00:00:00 2001 From: jsa2340 <36245808+jsa2340@users.noreply.github.com> Date: Tue, 12 Apr 2022 21:12:52 +1000 Subject: [PATCH 2/8] Update Dev_Versions.txt --- Development/Dev_Versions.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Development/Dev_Versions.txt b/Development/Dev_Versions.txt index 5e42e0c..e1ccc4f 100644 --- a/Development/Dev_Versions.txt +++ b/Development/Dev_Versions.txt @@ -83,4 +83,6 @@ Fix print bug which shows wrong value for STW (probably only happens in test bin 10 March - minor fix, so not upped version number. Fix incorrect read of symbols at 0x3ff returning "invalid bank" - +4.07.16 (11 April 2022) +Changed - Add special function command "uwtlu" for unsigned word tables +Changed - Add special function command "swtlu" for signed word tables From 74e22d911c379aeb326f498d8ed10e53ee745328 Mon Sep 17 00:00:00 2001 From: jsa2340 <36245808+jsa2340@users.noreply.github.com> Date: Tue, 12 Apr 2022 21:14:58 +1000 Subject: [PATCH 3/8] test --- Development/4.07.16b/test.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 Development/4.07.16b/test.txt diff --git a/Development/4.07.16b/test.txt b/Development/4.07.16b/test.txt new file mode 100644 index 0000000..190a180 --- /dev/null +++ b/Development/4.07.16b/test.txt @@ -0,0 +1 @@ +123 From dfb8aca35bd457afb541a76224c3596365d55681 Mon Sep 17 00:00:00 2001 From: jsa2340 <36245808+jsa2340@users.noreply.github.com> Date: Tue, 12 Apr 2022 21:15:31 +1000 Subject: [PATCH 4/8] Delete Development/4.07.16b directory --- Development/4.07.16b/test.txt | 1 - 1 file changed, 1 deletion(-) delete mode 100644 Development/4.07.16b/test.txt diff --git a/Development/4.07.16b/test.txt b/Development/4.07.16b/test.txt deleted file mode 100644 index 190a180..0000000 --- a/Development/4.07.16b/test.txt +++ /dev/null @@ -1 +0,0 @@ -123 From f43455fb2ec10b2fc612bea0804a097325b773a1 Mon Sep 17 00:00:00 2001 From: jsa2340 <36245808+jsa2340@users.noreply.github.com> Date: Tue, 12 Apr 2022 21:16:50 +1000 Subject: [PATCH 5/8] Add Word Table special functions --- Development/sources/4.07.16b/text | 1 + 1 file changed, 1 insertion(+) create mode 100644 Development/sources/4.07.16b/text diff --git a/Development/sources/4.07.16b/text b/Development/sources/4.07.16b/text new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/Development/sources/4.07.16b/text @@ -0,0 +1 @@ + From 77914f728a353ba68389e5e7c67b8cac789a01d4 Mon Sep 17 00:00:00 2001 From: jsa2340 <36245808+jsa2340@users.noreply.github.com> Date: Tue, 12 Apr 2022 21:18:18 +1000 Subject: [PATCH 6/8] Added Word Table special functions --- Development/sources/4.07.16b/Core.cpp | 14379 +++++++++++++++++++++++ Development/sources/4.07.16b/chain.cpp | 5707 +++++++++ Development/sources/4.07.16b/core.h | 878 ++ Development/sources/4.07.16b/debug.cpp | 570 + Development/sources/4.07.16b/main.cpp | 209 + Development/sources/4.07.16b/shared.h | 102 + Development/sources/4.07.16b/sign.cpp | 1085 ++ Development/sources/4.07.16b/sign.h | 123 + 8 files changed, 23053 insertions(+) create mode 100644 Development/sources/4.07.16b/Core.cpp create mode 100644 Development/sources/4.07.16b/chain.cpp create mode 100644 Development/sources/4.07.16b/core.h create mode 100644 Development/sources/4.07.16b/debug.cpp create mode 100644 Development/sources/4.07.16b/main.cpp create mode 100644 Development/sources/4.07.16b/shared.h create mode 100644 Development/sources/4.07.16b/sign.cpp create mode 100644 Development/sources/4.07.16b/sign.h diff --git a/Development/sources/4.07.16b/Core.cpp b/Development/sources/4.07.16b/Core.cpp new file mode 100644 index 0000000..c6b00d4 --- /dev/null +++ b/Development/sources/4.07.16b/Core.cpp @@ -0,0 +1,14379 @@ + +/****************************************************** + * 8065 advanced disassembler for EEC-IV + * + * This program is for private use only. + * No liability of any kind accepted or offered. + * + * Version 4.0.7.16b + ****************************************************** + * BUILD NOTES + * This code assumes INT and UNSIGNED INT are at least 32 bits (4 bytes). + * and WILL NOT WORK with 16 bit compilers. + * sizes here (in bits) for information for different platforms + * + * OS compiler int float long void* double + * Win32 mingw 32 32 32 32 64 + * Linux amd64-gcc 32 32 64 64 64 + * + * This code built with CodeBlocks and/or CodeLite IDE environments + * on both Win and Linux + * + ****************************************************** + * Declarations and includes + *******************************************************/ + +// #include // if printing date and time + +#include "core.h" // this calls shared.h +#include "sign.h" + + +/********************************************************************************** +external subroutines, variables +***********************************************************************************/ + +extern PAT intign; // sig 'ignore interrupt' (for naming) +extern PAT hdr; // sig 'start jump' (and vects); +extern PAT fnlp; +extern PAT tblp; +extern PAT inthd; + +void show_prog (int); + +SIG* scan_sigs (int); +SIG* scan_asigs (int); +void prescan_sigs (void); +int do_jumpsig (int, uint, uint); +void do_jumps (void); + +void *mem (void*,size_t, size_t); +void mfree (void *, size_t); + +void free_structs (void); +void set_rgsize (RST *,uint); + +int adtchnsize (CHAIN *, uint, uint, uint); + +ADT *add_adt (CHAIN *, uint, uint); +LBK *add_cmd (uint, uint, uint, uint); +LBK *add_aux_cmd (uint, uint, uint, uint); +RBT *add_rbase (uint, uint, uint, uint); +SYM *add_sym (const char *, uint, uint, uint, uint, uint, uint); +SUB *add_subr (uint); +SBK *add_scan (uint, int, SBK *); +SPF *add_spf (uint); +SBK *add_escan (uint, SBK *); +JMP *add_jump (SBK *, int, int); +RST *add_rgstat (uint, uint, uint, uint); +PSW *add_psw (uint, uint); +TRK* add_dtk (SBK *,INST *); + +SBK *get_scan (uint, uint); +RST *get_rgarg (uint, uint); +LBK *get_cmd (uint, uint); +LBK *get_aux_cmd (uint, uint); +SBK *get_scan (uint); + +LBK *get_prt_cmd (LBK *dflt, BANK *b, uint ofst); +RST* get_rgstat (uint); +SIG* get_sig (uint); +uint get_tjmp_ix (uint); +JMP *get_fjump (uint); +uint get_jump (CHAIN *,uint); +SUB *get_subr (uint); +RBT* get_rbt (uint, uint); +SYM* get_sym (uint , uint , uint , uint); +ADT *get_adnl (CHAIN *, uint, uint ); +ADT *get_next_adnl (CHAIN *, ADT *); +SPF *get_spf (uint) ; +PSW* get_psw (uint); +TRK* get_dtk (uint); +DTD* get_dtkd (CHAIN *, uint, uint ); + + +int find_tjump (uint); +JMP * find_fjump (uint , int*); + +SIG* match_sig (PAT *, uint,uint); + +ADT *start_adnl_loop(CHAIN *, uint); + +SIG* inssig(SIG *blk); + +void fixbitfield(uint *, uint *, uint *); + +//void scan_dc_olaps(void); +//void scan_code_gaps(void); +void scan_gaps(void); +void check_dtk_links(void); + +//void scan_lpdata(void); + +void free_adnl(CHAIN *,void *); +ADT *set_adnl(ADT* a); +uint set_adt_fkey(uint,uint); + +void chdelete(CHAIN *, uint, int); +void clearchain(CHAIN *); + + + +extern CHAIN chjpt; +extern CHAIN chjpf; +extern CHAIN chcmd; +extern CHAIN chaux; +extern CHAIN chadnl; +extern CHAIN chadcm; +extern CHAIN chans; +extern CHAIN chbase; +extern CHAIN chscan; +extern CHAIN chsubr; +extern CHAIN chsig; +extern CHAIN chsym; +extern CHAIN chrgst; +extern CHAIN chemul; +extern CHAIN chsbcn; +extern CHAIN chpsw; +extern CHAIN chdtko; +extern CHAIN chdtk; + + +#ifdef XDBGX + +extern const char *chntxt[]; +extern const char *jtxt[]; + +uint DBGPRT (uint, const char *, ...); +void DBG_stack (FSTK *); +void DBG_rgchain(void); +void DBG_rgstat(RST *); +void DBG_sbk(const char *t, SBK *s); +void DBGPRTFGS(int ,LBK *,LBK *); + +void DBG_banks(void); + +void DBG_sigs(CHAIN*); +void DBG_data(void); + +extern int DBGmcalls; // malloc calls +extern int DBGmrels; // malloc releases +extern int DBGmaxalloc; // TEMP for malloc tracing +extern int DBGcuralloc; + + +#endif + + + +/********************************************************************************** +local global declarations +***********************************************************************************/ + +void scan_blk(SBK *, INST *); // used in several places +void do_code (SBK *, INST *); + +const char *empty = "\0\0\0"; // safety and allows increments, fixes compiler warnings too + +//for chain.lasterr errors +const char *etxts[] = {empty, "\002Duplicate Command", "\001Invalid Address","\001Banks Not Match", "\001Odd address\003", + "\001Commands\006", "\001Ranges Overlap","\002XCODE bans Command", "\002Symname replaces previous \"%s\"" + + }; + +//for compressing common strings +const char *cmptxts[16] = + {empty, " Error - "," Warning - ", " Invalid "," Required"," Not Allowed"," Overlap"," Missing", " Ignored" , // use \001 upwards ?? + // 0 1 2 3 4 5 6 7 8 \10 (OCTAL) + " Parameters", "Data Item(s)", empty,empty,empty,empty, "FATAL - " }; +// 9 \11, 10 \12 11-15 \13 -17 + +/***************************************************************************** +* declarations for subroutines in command structure declaration +***************************************************************************/ + +/********** COMMAND Option letters - +* +* Global opts + A Args mode print (one per line) default for ARGS and subrs + C Compact mode print default for all data structs + F special Function (subr) + Q Quit, terminator byte(s) 1-3 + +* Standard, data items + + = Answer definition ? + A + B Bit symbol (in sym) + C Cell for subword fields [start,width] + D Displacement - Data offset + E Encoded address + F Flags byte/word (symbol) + G Gen Type ?? [ text, mS, Volts, A/d Time/mS/scale etc ?] or Hybrid ?? + H + I imd name OK for syms + J + K banK replace bank where applicable + L Long + M [ mask ? ](byte or word pair) not done yet + N Name lookup, find symbol + O Count (cols etc) + P Minimum Print fieldwidth + Q + R Reference pointer (vector) in struct + S Signed + T Triple biT symbol (deprecated) + U Unsigned + V diVisor - Float value + W Word, Write (symbol) + X Print RadiX + Y bYte + Z + ***************************************************************/ + +// print processors + +uint pp_dflt (uint, LBK *); uint pp_wdbl (uint, LBK *); uint pp_text (uint, LBK *); +uint pp_vect (uint, LBK *); uint pp_code (uint, LBK *); uint pp_stct (uint, LBK *); +uint pp_timer (uint, LBK *); uint pp_dmy (uint, LBK *); + +// command processors + +uint set_vect (CPS*); uint set_code (CPS*); uint set_scan (CPS*); uint set_cdih (CPS*); +uint set_args (CPS*); uint set_data (CPS*); uint set_rbas (CPS*); uint set_sym (CPS*); +uint set_subr (CPS*); uint set_bnk (CPS*); uint set_time (CPS*); uint set_tab (CPS*); +uint set_func (CPS*); uint set_stct (CPS*); uint set_opts (CPS*); uint clr_opts (CPS*); +uint set_psw (CPS*); uint set_lay (CPS*); + +// string options for SETOPTS and CLROPTS + +const char *optstrs[15] = + +{ "default" ,"sceprt", "tabnames" , "funcnames", "ssubnames" , + "labelnames" ,"manual", "intrnames" , "subnames" , "signatures", "acomments", + "sympresets" ,"8065" , "cptargs" , "dataxtnd" }; + +// add param options ?? + +int mopts [15] = { + PDEFLT , // default + 1 , // sceprt print source code + 2 , // auto table names + 0x200 , // auto func names + 4 , // auto special func subnames + 0x10 , // auto labelnames + 0x20 , // full manual operation + 0x40 , // auto interrupt handler names + 0x80 , // auto subroutine names + 0x100 , // signatures/fingerprint + 0x400 , // do autocomments + 0x800 , // preset symbols + 0x8 , // set 8065 (not opt D) + 0x2000 , // set arg compact layout + 0x4000 // set data extend layout +}; + + +// what about decimal versus hex prints for each data type ??? (too much ?) +// other default layouts and types DONE A & C +// symbol types and print options ? + +//special function strings for subroutines + +const char *spfstrs[13] = + +{ empty ,"uuyflu", "usyflu" , "suyflu", "ssyflu" , + "uuwflu", "uswflu" , "suwflu", "sswflu" , + "uytlu" , "sytlu" , "uwtlu" , "swtlu" +}; + + +const char *cmds[23] = + +{"fill", "byte", "word", "triple", "long", "text", "vect", "table","func","struct", + "timer", "code", "args", "xcode", "subr", "scan", "rbase","symbol", "bank", "setopt", "clropt", "pswset", "layout"}; + +// Params - +// command processor subr, command printer subr, +// max pars expected, single start address posn, pair start addr posn (7 = none), name allowed, +// max addnl levels, min addnl levels, default size, +// merge allowed, string opts, +// global options (scanf), data options string (scanf) + + +DIRS dirs[23] = { + +// these go in cmd chain +{ set_data, pp_dflt, 2, 7, 0, 0, 0, 0, 1, 1, 0, 0, 0 }, // fill (default) MUST be entry zero +{ set_data, pp_wdbl, 2, 7, 0, 1, 1, 0, 1, 1, 0, 0, "PSUXV" }, // byte +{ set_data, pp_wdbl, 2, 7, 0, 1, 1, 0, 2, 1, 0, 0, "PSUXV" }, // word +{ set_data, pp_wdbl, 2, 7, 0, 1, 1, 0, 2, 0, 0, 0, "PSUXV" }, // triple +{ set_data, pp_wdbl, 2, 7, 0, 1, 1, 0, 3, 0, 0, 0, "PSUXV" }, // long +{ set_data, pp_text, 2, 7, 0, 0, 0, 0, 1, 1, 0, 0, 0 } , // text +{ set_vect, pp_vect, 2, 7, 0, 1, 1, 0, 2, 0, 0, 0, "DKQ"}, // vect +{ set_tab, pp_stct, 2, 7, 0, 1, 1, 1, 0, 0, 0, 0, "OPSUVWXY" }, // table +{ set_func, pp_stct, 2, 7, 0, 1, 2, 2, 0, 0, 0, 0, "LPSUVWXY" } , // func +{ set_stct, pp_stct, 2, 7, 0, 1, 15, 1, 0, 0, 0, "QAC", "DELNOPRSUVWXY|" }, // struct +{ set_time, pp_timer, 2, 7, 0, 1, 2, 0, 0, 0, 0, 0, "NTUWY" }, // timer +{ set_code, pp_code, 2, 7, 0, 0, 0, 0, 0, 1, 0, 0, 0 } , // code + +//these go in aux chain +{ set_args, pp_dflt, 2, 7, 0, 0, 15, 1, 0, 0, 0, 0, "DELNOPSUVWXY|" }, // args +{ set_cdih, pp_dmy, 2, 7, 0, 0, 0, 0, 0, 1, 0, 0, 0 } , // xcode + + +{ set_subr, pp_dmy, 1, 0, 7, 1, 15, 0, 0, 0, 0, "ACF", "DELNOPSUVWXY|=" }, // subr +{ set_scan, pp_dmy , 1, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0 }, // scan +{ set_rbas, pp_dmy, 4, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0 }, // rbase + +{ set_sym, pp_dmy, 3, 0, 1, 1, 1, 0, 0, 0, 0, 0, "BFITWN" }, // sym +{ set_bnk, pp_dmy, 4, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0 }, // bank + +{ set_opts, pp_dmy, 0, 7, 7, 0, 1, 0, 0, 0, 1, 0, 0 }, // set options (external strings array) +{ clr_opts, pp_dmy, 0, 7, 7, 0, 1, 0, 0, 0, 1, 0, 0 }, // clear options +{ set_psw, pp_dmy, 2, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0 }, // psw setter for '0=0' jumps +{ set_lay, pp_dmy, 3, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0 } // layout for prints +}; + + +/********************** + * opcode index to opcode definition table + * index for opcodes first - points into main opcode table - 0 is invalid + * main table ordered to put main opcode types together. + * **********************/ + + +uchar opcind[256] = +{ +// 0 1 2 3 4 5 6 7 8 9 a b c d e f + 80, 89, 96, 91, 0, 98, 100, 93, 4, 1, 5, 0, 6, 3, 7, 94, // 00 - 0f +// skip,clrw,cplw,negw, decw,sexw,incw,shrw,shlw,asrw, shrd,shld,asrd,norm + 112, 88, 95, 90, 0, 97, 99, 92, 8, 2, 9, 0, 0, 0, 0, 0, // 10 - 1f +// RBNK,CLRB,CPLB,NEGB,----,DECB,SEXB,INCB,SHRB,SHLB,ASRB + 81, 81, 81, 81, 81, 81, 81, 81, 82, 82, 82, 82, 82, 82, 82, 82, // 20 - 2f +// sjmp scall + 70, 70, 70, 70, 70, 70, 70, 70, 71, 71, 71, 71, 71, 71, 71, 71, // 30 - 3f +// jnb jb + 13, 13, 13, 13, 25, 25, 25, 25, 29, 29, 29, 29, 20, 20, 20, 20, // 40 - 4f +// AN3W AD3W SB3W ML3W + 12, 12, 12, 12, 24, 24, 24, 24, 28, 28, 28, 28, 18, 18, 18, 18, // 50 - 5f +// AN3B AD3B SB3B ML3B + 11, 11, 11, 11, 23, 23, 23, 23, 27, 27, 27, 27, 16, 16, 16, 16, // 60 - 6f +// AN2W AD2W SB2W ML2W + 10, 10, 10, 10, 22, 22, 22, 22, 26, 26, 26, 26, 14, 14, 14, 14, // 70 - 7f +// AN2B AD2B SB2B ML2B + 31, 31, 31, 31, 33, 33, 33, 33, 35, 35, 35, 35, 38, 38, 38, 38, // 80 - 8f +// ORW XRW CMPW DIVW + 30, 30, 30, 30, 32, 32, 32, 32, 34, 34, 34, 34, 36, 36, 36, 36, // 90 - 9f +// ORB XORB CMPB DIVB + 41, 41, 41, 41, 43, 43, 43, 43, 45, 45, 45, 45, 46, 46, 46, 46, // a0 - af +// LDW ADCW SBBW LDZBW + 40, 40, 40, 40, 42, 42, 42, 42, 44, 44, 44, 44, 47, 47, 47, 47, // b0 - bf +// LDB ADCB SBBB LDSBW + 49, 0, 49, 49, 48, 0, 48, 48, 50, 50, 50, 50, 52, 0, 52, 52, // c0 - cf +// STW (not IM) STB (not IM) PUSHW POPW (not IM) + 54, 63, 64, 56, 60, 58, 66, 69, 55, 62, 65, 57, 61, 59, 67, 68, // d0 - df +// JNST,JLEU,JGT, JNC, NVT, JNV, JGE, JNE, JST, JGTU,JLE, JC, JVT, JV, JLT, JE + 72, 0, 0, 0, 0, 0, 0, 75, 0, 0, 0, 0, 0, 0, 0, 73, // e0 - ef +// DJNZ JUMP CALL + 76, 78, 84, 86, 107, 108, 109, 0, 102, 101, 103, 104, 105, 110, 111, 106 // f0 - ff +// RET, RETI,PSHP,POPP,Bnk0,Bnk1,Bnk2, CLC, STC, DI, EI, CLVT,Bnk3,SIGN,NOP +}; + + +/********************************************************************** + + Signature index value for like instructions - MAX 63 + 0 special 1 clr 2 neg/cpl 3 shift l 4 shift R 5 and + 6 add 7 subt 8 mult 9 or 10 cmp 11 divide + 12 ldx 13 stx 14 push 15 pop 16 sjmp 17 subr + 18 djnz 19 cjmp 20 ret 21 carry 22 other + 24 dec 25 inc + + (sjmp is static jump , cjmp is conditional jump) + + * more complicated - + 23 jnb/jb - can cross match to 19 with extra par + 12 and 13 (ldx, stx) can be regarded as equal with params swopped + + 40+ are optional and skipped by default, but detectable with - + + 40 sig 41 pushp 42 popp 43 di 44 ei 45 (RAM)bnk + 46 (ROM)bnk 47 nop 48 others +*/ + +//***************************************************************************** +// code emulation procs for opcode struct + +void clr (SBK *, INST *); void ldx (SBK *, INST *); void stx (SBK *, INST *); void orx (SBK *, INST *); +void add (SBK *, INST *); void andx (SBK *, INST *); void neg (SBK *, INST *); void cpl (SBK *, INST *); +void sub (SBK *, INST *); void cmp (SBK *, INST *); void mlx (SBK *, INST *); void dvx (SBK *, INST *); +void shl (SBK *, INST *); void shr (SBK *, INST *); void popw (SBK *, INST *); void pshw (SBK *, INST *); +void inc (SBK *, INST *); void dec (SBK *, INST *); void bka (SBK *, INST *); void cjm (SBK *, INST *); +void bjm (SBK *, INST *); void djm (SBK *, INST *); void ljm (SBK *, INST *); void cll (SBK *, INST *); +void ret (SBK *, INST *); void scl (SBK *, INST *); void skj (SBK *, INST *); void sjm (SBK *, INST *); +void php (SBK *, INST *); void ppp (SBK *, INST *); void nrm (SBK *, INST *); void sex (SBK *, INST *); +void clc (SBK *, INST *); void stc (SBK *, INST *); void die (SBK *, INST *); void nop (SBK *, INST *); +void clv (SBK *, INST *); + + + +// NB. in pseudo source - Octal escape sequences (\nnn) are limited to three digits, +// whereas hex (\xnn..) format is not limited (ansi C rules). +// Octal used in strings below so that letters not interpreted as part of hex number (e.g. 'C' of "CY ="). +// '\2x' in the sring means add size and sign flags to the operand 'x' (1-3). + +// bit 7 is most significant, 0 is least, so order is 7,6,5,4,3,2,1,0 as read. + +// OPC table items across - signature index, 'fe' prefix allowed (= use next entry), +// 8065 opcode only, opcode changes PSW, number of ops, write op index, write size, +// read sizes (operand 1,2,3), opcode name, pseudo source + +// op sizes are by 'field end' with an implied field start of 0 +// 7,15,23,31 = unsigned byte,word,triple,long +// 39,47,55,63 = signed byte,word,triple,long (+32 signed) + + +OPC opctbl[] = { // 113 entries +{ 0 , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ,"!INV!" , empty }, // zero for invalid opcodes + +{ 3 , 0, 0, 1, 2, 2, 15, 7, 15, 0, shl ,"shlw" , "\2 <<= \1;" }, // 1 +{ 3 , 0, 0, 1, 2, 2, 7, 7, 7, 0, shl ,"shlb" , "\2 <<= \1;" }, +{ 3 , 0, 0, 1, 2, 2, 31, 7, 31, 0, shl ,"shldw" , "\22 <<= \1;" }, + +{ 4 , 0, 0, 1, 2, 2, 15, 7, 15, 0, shr ,"shrw" , "\2 >>= \1;" }, // 4 +{ 4 , 0, 0, 1, 2, 2, 47, 7, 47, 0, shr ,"asrw" , "\22 >>= \1;" }, +{ 4 , 0, 0, 1, 2, 2, 31, 7, 31, 0, shr ,"shrdw" , "\22 >>= \1;" }, // 6 +{ 4 , 0, 0, 1, 2, 2, 63, 7, 63, 0, shr ,"asrdw" , "\22 >>= \21;" }, +{ 4 , 0, 0, 1, 2, 2, 7, 7, 7, 0, shr ,"shrb" , "\2 >>= \1;" }, // 8 +{ 4 , 0, 0, 1, 2, 2, 39, 7, 39, 0, shr ,"asrb" , "\22 >>= \1;" }, + +{ 5 , 0, 0, 1, 2, 2, 7, 7, 7, 0, andx ,"an2b" , "\4\2 &= \1;" }, // 10 +{ 5 , 0, 0, 1, 2, 2, 15, 15, 15, 0, andx ,"an2w" , "\4\2 &= \1;" }, +{ 5 , 0, 0, 1, 3, 3, 7, 7, 7, 7, andx ,"an3b" , "\4\3 = \2 & \1;" }, +{ 5 , 0, 0, 1, 3, 3, 15, 15, 15, 15, andx ,"an3w" , "\4\3 = \2 & \1;" }, + +{ 8 , 1, 0, 1, 2, 2, 15, 7, 7, 0, mlx ,"ml2b" , "\22 *= \1;" }, // 14 +{ 8 , 0, 0, 1, 2, 2, 47, 39, 39, 0, mlx ,"sml2b" , "\22 *= \1;"}, +{ 8 , 1, 0, 1, 2, 2, 31, 15, 15, 0, mlx ,"ml2w" , "\22 *= \1;" }, +{ 8 , 0, 0, 1, 2, 2, 63, 47, 47, 0, mlx ,"sml2w" , "\22 *= \1;"}, + +{ 8 , 1, 0, 0, 3, 3, 15, 7, 7, 7, mlx ,"ml3b" , "\23 = \22 * \1;" }, // 18 +{ 8 , 0, 0, 0, 3, 3, 47, 39, 39, 15, mlx ,"sml3b" , "\23 = \22 * \1;" }, +{ 8 , 1, 0, 0, 3, 3, 31, 15, 15, 15, mlx ,"ml3w" , "\23 = \22 * \1;" }, +{ 8 , 0, 0, 0, 3, 3, 63, 47, 47, 15, mlx ,"sml3w" , "\23 = \22 * \1;" }, + +{ 6 , 0, 0, 1, 2, 2, 7, 7, 7, 0, add ,"ad2b" , "\2 += \1;" }, // 22 +{ 6 , 0, 0, 1, 2, 2, 15, 15, 15, 0, add ,"ad2w" , "\2 += \1;" }, +{ 6 , 0, 0, 1, 3, 3, 7, 7, 7, 7, add ,"ad3b" , "\3 = \2 + \1;" }, +{ 6 , 0, 0, 1, 3, 3, 15, 15, 15, 15, add ,"ad3w" , "\3 = \2 + \1;" }, + +{ 7 , 0, 0, 1, 2, 2, 7, 7, 7, 0, sub ,"sb2b" , "\2 -= \1;" }, // 26 +{ 7 , 0, 0, 1, 2, 2, 15, 15, 15, 0, sub ,"sb2w" , "\2 -= \1;" }, +{ 7 , 0, 0, 1, 3, 3, 7, 7, 7, 7, sub ,"sb3b" , "\3 = \2 - \1;" }, +{ 7 , 0, 0, 1, 3, 3, 15, 15, 15, 15, sub ,"sb3w" , "\3 = \2 - \1;" }, + +{ 9 , 0, 0, 1, 2, 2, 7, 7, 7, 0, orx ,"orb" , "\4\2 |= \1;" }, // 30 +{ 9 , 0, 0, 1, 2, 2, 15, 15, 15, 0, orx ,"orw" , "\4\2 |= \1;" }, +{ 9 , 0, 0, 1, 2, 2, 7, 7, 7, 0, orx ,"xorb" , "\4\2 ^= \1;" }, +{ 9 , 0, 0, 1, 2, 2, 15, 15, 15, 0, orx ,"xrw" , "\4\2 ^= \1;" }, + +{ 10, 0, 0, 1, 2, 0, 0, 7, 7, 0, cmp ,"cmpb" , empty }, // 34 +{ 10, 0, 0, 1, 2, 0, 0, 15, 15, 0, cmp ,"cmpw" , empty }, + +{ 11, 1, 0, 1, 2, 2, 7, 7, 15, 0, dvx ,"divb" , "\22 /= \1;" }, // 36 +{ 11, 0, 0, 1, 2, 2, 39, 39, 47, 0, dvx ,"sdivb" , "\22 /= \1;"}, +{ 11, 1, 0, 1, 2, 2, 15, 15, 31, 0, dvx ,"divw" , "\22 /= \1;" }, +{ 11, 0, 0, 1, 2, 2, 47, 47, 63, 0, dvx ,"sdivw" , "\22 /= \1;"}, + +{ 12, 0, 0, 0, 2, 2, 7, 7, 7, 0, ldx ,"ldb" , "\4\2 = \1;" }, // 40 +{ 12, 0, 0, 0, 2, 2, 15, 15, 15, 0, ldx ,"ldw" , "\4\2 = \1;" }, + +{ 6 , 0, 0, 1, 2, 2, 7, 7, 7, 0, add ,"adcb" , "\2 += \1 + CY;" }, // 42 +{ 6 , 0, 0, 1, 2, 2, 15, 15, 15, 0, add ,"adcw" , "\2 += \1 + CY;" }, +{ 7 , 0, 0, 1, 2, 2, 7, 7, 7, 0, sub ,"sbbb" , "\2 -= \1 - CY;" }, +{ 7 , 0, 0, 1, 2, 2, 15, 15, 15, 0, sub ,"sbbw" , "\2 -= \1 - CY;" }, + +{ 12, 0, 0, 0, 2, 2, 15, 7, 7, 0, ldx ,"ldzbw" , "\22 = \21;" }, // 46 +{ 12, 0, 0, 0, 2, 2, 47, 7, 7, 0, ldx ,"ldsbw" , "\22 = \21;" }, + +{ 13, 0, 0, 0, 2, 1, 7, 7, 7, 0, stx ,"stb" , "\1 = \2;" }, // 48 +{ 13, 0, 0, 0, 2, 1, 15, 15, 15, 0, stx ,"stw" , "\1 = \2;" }, + +{ 14, 1, 0, 0, 1, 0, 0, 15, 0, 0, pshw ,"push" , "push(\1);" }, // 50 +{ 14, 0, 1, 0, 1, 0, 0, 15, 0, 0, pshw ,"pusha" , "pusha(\1);" }, +{ 15, 1, 0, 0, 1, 1, 15, 15, 0, 0, popw ,"pop" , "\1 = pop();" }, // 52 +{ 15, 0, 1, 0, 1, 1, 15, 15, 0, 0, popw ,"popa" , "\1 = popa();" }, + +{ 19, 0, 0, 0, 1, 0, 0, 0, 0, 0, cjm ,"jnst" , "if (STK = 0) \x9" }, // 54 to 71 cond jumps +{ 19, 0, 0, 0, 1, 0, 0, 0, 0, 0, cjm ,"jst" , "if (STK = 1) \x9" }, // 55 + +{ 19, 0, 0, 0, 1, 0, 0, 0, 0, 0, cjm ,"jnc" , "\10CY = 0) \x9" }, // 56 +{ 19, 0, 0, 0, 1, 0, 0, 0, 0, 0, cjm ,"jc" , "\10CY = 1) \x9" }, // 57 + +{ 19, 0, 0, 0, 1, 0, 0, 0, 0, 0, cjm ,"jnv" , "\10OVF = 0) \x9" }, // 58 +{ 19, 0, 0, 0, 1, 0, 0, 0, 0, 0, cjm ,"jv" , "\10OVF = 1) \x9" }, // 59 + +{ 19, 0, 0, 0, 1, 0, 0, 0, 0, 0, cjm ,"jnvt" , "if (OVT = 0) \x9" }, // 60 +{ 19, 0, 0, 0, 1, 0, 0, 0, 0, 0, cjm ,"jvt" , "if (OVT = 1) \x9" }, // 61 + +{ 19, 0, 0, 0, 1, 0, 0, 0, 0, 0, cjm ,"jgtu" , "\7>" }, // 62 +{ 19, 0, 0, 0, 1, 0, 0, 0, 0, 0, cjm ,"jleu" , "\7<=" }, // 63 + +{ 19, 0, 0, 0, 1, 0, 0, 0, 0, 0, cjm ,"jgt" , "\7>" }, // 64 +{ 19, 0, 0, 0, 1, 0, 0, 0, 0, 0, cjm ,"jle" , "\7<=" }, // 65 + +{ 19, 0, 0, 0, 1, 0, 0, 0, 0, 0, cjm ,"jge" , "\7>=" }, // 66 +{ 19, 0, 0, 0, 1, 0, 0, 0, 0, 0, cjm ,"jlt" , "\7<" }, // 67 + +{ 19, 0, 0, 0, 1, 0, 0, 0, 0, 0, cjm ,"je" , "\7=" }, // 68 +{ 19, 0, 0, 0, 1, 0, 0, 0, 0, 0, cjm ,"jne" , "\7!=" }, // 69 + +{ 23, 0, 0, 0, 2, 0, 0, 15, 7, 7, bjm ,"jnb" , "if (\3\2 = 0) \x9" }, // 70 +{ 23, 0, 0, 0, 2, 0, 0, 15, 7, 7, bjm ,"jb" , "if (\3\2 = 1) \x9" }, // 71 + +{ 18, 0, 0, 0, 2, 2, 7, 7, 7, 0, djm ,"djnz" , "\2--;\xbif (\2 != 0) \x9" }, // 72 + +{ 17, 1, 0, 0, 1, 0, 0, 0, 0, 0, cll ,"call" , "\5" }, // 73 Long jumps +{ 17, 0, 1, 0, 2, 0, 0, 0, 0, 0, cll ,"calla" , "\5" }, // 74 +{ 16, 0, 0, 0, 1, 0, 0, 0, 0, 0, ljm ,"jump" , "\x9" }, // 75 + +{ 20, 1, 0, 0, 0, 0, 0, 0, 0, 0, ret ,"ret" , "return;" }, // 76 returns +{ 20, 0, 1, 0, 0, 0, 0, 0, 0, 0, ret ,"reta" , "return;" }, +{ 20, 1, 0, 0, 0, 0, 0, 0, 0, 0, ret ,"reti" , "return;" }, +{ 20, 0, 1, 0, 0, 0, 0, 0, 0, 0, ret ,"retia" , "return;" }, // 79 + +{ 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, skj ,"skip" , "\x9" }, // 80 skip (= sjmp [pc+2]) +{ 16, 0, 0, 0, 1, 0, 0, 0, 0, 0, sjm ,"sjmp" , "\x9" }, // 81 short jumps +{ 17, 1, 0, 0, 1, 0, 0, 0, 0, 0, scl ,"scall" , "\5" }, // 82 +{ 17, 0, 0, 0, 1, 0, 0, 0, 0, 0, scl ,"sclla" , "\5" }, // 83 + +{ 41, 1, 0, 0, 0, 0, 0, 0, 0, 0, php ,"pushp" , "push(PSW);" }, // 84 push psw +{ 41, 0, 1, 0, 0, 0, 0, 0, 0, 0, php ,"pshpa" , "pusha(PSW);" }, +{ 42, 1, 0, 1, 0, 0, 0, 0, 0, 0, ppp ,"popp" , "PSW = pop();" }, // 86 +{ 42, 0, 1, 1, 0, 0, 0, 0, 0, 0, ppp ,"poppa" , "PSW = popa();" }, // 87 + + +{ 1 , 0, 0, 0, 1, 1, 7, 7, 0, 0, clr ,"clrb" , "\4\1 = 0;" }, // 88 +{ 1 , 0, 0, 0, 1, 1, 15, 15, 0, 0, clr ,"clrw" , "\4\1 = 0;" }, + +{ 2 , 0, 0, 1, 1, 1, 7, 7, 0, 0, neg ,"negb" , "\1 = -\1;" }, // 90 +{ 2 , 0, 0, 1, 1, 1, 15, 15, 0, 0, neg ,"negw" , "\1 = -\1;" }, + +{ 25, 0, 0, 1, 1, 1, 7, 7, 0, 0, inc ,"incb" , "\1++;" }, // 92 +{ 25, 0, 0, 1, 1, 1, 15, 15, 0, 0, inc ,"incw" , "\1++;" }, // 93 +{ 22, 0, 0, 1, 2, 1, 7, 7, 31, 0, nrm ,"norm" , "\1 = normalize(\22);" }, +{ 2 , 0, 0, 1, 1, 1, 7, 7, 0, 0, cpl ,"cplb" , "\1 = ~\1;" }, // 95 +{ 2 , 0, 0, 1, 1, 1, 15, 15, 0, 0, cpl ,"cplw" , "\1 = ~\1;" }, + +{ 24, 0, 0, 1, 1, 1, 7, 7, 0, 0, dec ,"decb" , "\1--;" }, // 97 +{ 24, 0, 0, 1, 1, 1, 15, 15, 0, 0, dec ,"decw" , "\1--;" }, +{ 22, 0, 0, 1, 1, 1, 47, 7, 0, 0, sex ,"sexb" , "\21 = \1;" }, +{ 22, 0, 0, 0, 1, 1, 63, 15, 0, 0, sex ,"sexw" , "\21 = \1;" }, // 100 + + +{ 21, 0, 0, 1, 0, 0, 0, 0, 0, 0, stc ,"stc" , "CY = 1;" }, // 101 +{ 21, 0, 0, 1, 0, 0, 0, 0, 0, 0, clc ,"clc" , "CY = 0;" }, // 102 +{ 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, die ,"di" , "interrupts OFF;" }, +{ 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, die ,"ei" , "interrupts ON;" }, +{ 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, clv ,"clrvt" , "OVT = 0;" }, +{ 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, nop ,"nop" , empty }, + +{ 45, 0, 1, 0, 0, 0, 0, 0, 0, 0, bka ,"regbk" , empty }, // 107 +{ 45, 0, 1, 0, 0, 0, 0, 0, 0, 0, bka ,"regbk" , empty }, +{ 45, 0, 1, 0, 0, 0, 0, 0, 0, 0, bka ,"regbk" , empty }, // 109 +{ 45, 0, 1, 0, 0, 0, 0, 0, 0, 0, bka ,"regbk" , empty }, // 110 + +{ 46, 0, 1, 0, 1, 0, 0, 7, 0, 0, 0 ,"rombk" , empty }, // 111 bank prefix (8065) +{ 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ,"!PRE!" , empty } // 112 signed prefix + +}; + + +// extra opcode pseudo sources for replace of R0 opcodes, +// carry, ldx, goto + +const char *scespec[] = { + "\x2 += CY;", // R0 + carry replace + "\x2 -= CY;", + "\2 = \1;", // LDX for reduced 3 op, XOR + "\x9" // 'bare' goto + }; + +// for swopping over cmp (R0, x) operands + +const char *swopcmpop[] = { + + "<" , // jgtu '>' (62) to 'jltu' and JC std + ">=", // jleu '<= (63) to 'jgeu' and JNC std + "<" , // jgt '>' (64) to 'jlt' + ">=", // jle '<=' (65) to 'jge' + "<=", // jge '>=' (66) to 'jle' + ">" , // jlt '<' (67) to 'jgt' + "=" , // je remains same + "!=", // jne remains same + ">", // JNC + "<=" // JC + +}; + +// for the dummy cmp R0,0 or cmp Rx Rx etc + +const char *zerocmpop[] = { + + "false" , // jgtu + "true" , // jleu + "false" , // jgt + "true" , // jle + "true" , // jge + "false" , // jlt + "true" , // je + "false" // jne +}; + + + + /* interrupt subroutine names array, for auto naming + * "HSI_" and default name is output as prefix in code + * switched on/off with Opt N + * params - + * start, end address(es) of interrupt (+ 0x2010) + * I8065 1 = 8065 interrupt, 0 is both 8065 and 8061 + * num 1 = name has number appended (8065) + * name + */ + + +NAMES inames[] = { +{ 0, 0, 0, 0, "HSO_2" }, // 8061 ints - actual at 2010 +{ 1, 1, 0, 0, "Timer_OVF" }, +{ 2, 2, 0, 0, "AD_Rdy" }, +{ 3, 3, 0, 0, "HSI_Data" }, +{ 4, 4, 0, 0, "External" }, +{ 5, 5, 0, 0, "HSO_1" }, +{ 6, 6, 0, 0, "HSI_1" }, +{ 7, 7, 0, 0, "HSI_0" }, // 201f - end 8061 list + +{ 0, 15, 1, 1, "HSO_" }, // 8065 ints - 2010-201f (plus int num) +{ 16, 16, 1, 0, "HSI_FIFO" }, +{ 17, 17, 1, 0, "External" }, +{ 18, 18, 1, 0, "HSI_0" }, +{ 19, 19, 1, 0, "HSI_Data" }, +{ 20, 20, 1, 0, "HSI_1" }, +{ 21, 21, 1, 0, "AD_Imm_Rdy" }, +{ 22, 22, 1, 0, "AD_Timed_Rdy" }, +{ 23, 23, 1, 0, "ATimer_OVF" }, +{ 24, 24, 1, 0, "AD_Timed_Start" }, +{ 25, 25, 1, 0, "ATimer_reset" }, +{ 26, 29, 1, 1, "Counter_" }, // add num +{ 30, 39, 1, 1, "Software_" } // add num 2040-205f end 8065 +}; + + +// Auto names - appended with address. +// option cmd mask, size in, size out, name +// sizes are for function and table subroutine names as opcode table + +AUTON anames [] = { + { 0, 0, 0, empty }, // 0 null entry + { 0x80 , 0, 0, "Sub" }, // 1 PPROC auto proc names (func) + { 0x10 , 0, 0, "Lab" }, // 2 PLABEL label names (lab) + { 0x200, 0, 0, "Func" }, // 3 PFUNC function data names + { 0x2 , 0, 0, "Table" }, // 4 PTABL table data names + + { 0x4 , 7, 7, "UUYFuncLU" }, // 5 PSTRS auto subroutine names for function and table lookup procs + { 0x4 , 7, 39, "USYFuncLU" }, + { 0x4 , 39, 7, "SUYFuncLU" }, + { 0x4 , 39, 39, "SSYFuncLU" }, + + { 0x4 , 15, 15, "UUWFuncLU" }, // 9 + { 0x4 , 15, 47, "USWFuncLU" }, + { 0x4 , 47, 15, "SUWFuncLU" }, + { 0x4 , 47, 47, "SSWFuncLU" }, + + { 0x4 , 7, 7, "UYTabLU" }, // 13 + { 0x4 , 39, 39, "SYTabLU" }, + { 0x4 , 15, 15, "UWTabLU" }, + { 0x4 , 47, 47, "SWTabLU" }, + + { 0 , 7, 7, "Timer" } // 17 used as a SPECIAL for timers. + }; + + + +/***** Auto default symbols ******* + + * added to analysis if PRSYM set + * fixed names for special regs as Ford Handbook + * Params in order - + * for 8061 + * for 8065 (both can be set) + * read sym 0, write 1 + * whole 0, bit 1 + * bitnumber (0-7) + * address + * name +*/ + + +DFSYM defsyms [] = { + + {1, 0, 0, 6, 6, 0x2, "CPU_OK" }, // 8061 only and not sure if this should be kept. + {1, 1, 0, 32, 32, 0x2, "LSO_Port" }, + {1, 1, 0, 32, 32, 0x3, "LIO_Port" }, + {1, 1, 0, 32, 32, 0x4, "AD_Low" }, + {1, 1, 0, 32, 32, 0x5, "AD_High" }, + {1, 1, 0, 32, 32, 0x6, "IO_Timer" }, + {1, 0, 0, 32, 32, 0x7, "IO_Timer_Hi" }, // 8061 only + {0, 1, 0, 32, 32, 0x7, "AD_Timed_Lo" }, // 8065 only + {1, 1, 0, 32, 32, 0x8, "INT_Mask" }, + {1, 1, 0, 32, 32, 0x9, "INT_Pend" }, + + {1, 1, 0, 0, 0, 0xa, "HSO_OVF" }, // both, bit 0 + {1, 1, 0, 1, 1, 0xa, "HSI_OVF" }, + {1, 1, 0, 2, 2, 0xa, "HSI_Ready" }, + {1, 1, 0, 3, 3, 0xa, "AD_Ready" }, // both, bit 3 + {1, 0, 0, 4, 4, 0xa, "INT_Handling" } , // 8061 only + {0, 1, 0, 4, 4, 0xa, "MEM_Expand" }, // 8065 only + {1, 0, 0, 5, 5, 0xa, "INT_Priority" }, + {0, 1, 0, 5, 6, 0xa, "HSO_Pcnt_OVF" }, + + {0, 1, 0, 6, 6, 0xa, "Timed_AD_Ready"}, // bits 6,7 not used in 8061 + {0 ,1, 0, 7, 7, 0xa, "HSO_Port_OVF" }, + + + {1, 1, 0, 32, 32, 0xa, "IO_Status" }, + {1, 1, 0, 32, 32, 0xb, "HSI_Sample" }, + {1, 1, 0, 32, 32, 0xc, "HSI_Mask" }, + {1, 1, 0, 32, 32, 0xd, "HSI_Data" }, + {1, 1, 0, 32, 32, 0xe, "HSI_Time" }, + {1, 0, 0, 32, 32, 0x10, "StackPtr" }, // 8061 stack + + {0, 1, 0, 32, 32, 0xf, "AD_Timed_HI" }, // with 007 above + + + {0, 1, 0, 32, 32, 0x10, "HSO_IntPend1" }, // 8065 only + {0, 1, 0, 32, 32, 0x11, "BANK_Select" }, + {0, 1, 0, 32, 32, 0x12, "HSO_IntMask1" }, + {0, 1, 0, 32, 32, 0x13, "IO_Timer_Hi" }, + {0, 1, 0, 32, 32, 0x14, "HSO_IntPend2" }, + {0, 1, 0, 32, 32, 0x15, "LSSI_A" }, + {0, 1, 0, 32, 32, 0x16, "HSO_IntMask2" }, + {0, 1, 0, 32, 32, 0x17, "LSSI_B" }, + {0, 1, 0, 32, 32, 0x18, "HSO_Status" }, + {0, 1, 0, 32, 32, 0x19, "LSSI_C" }, + {0, 1, 0, 32, 32, 0x1a, "HSI_Mode" }, + {0, 1, 0, 32, 32, 0x1b, "HSO_UsedCnt" }, + {0, 1, 0, 32, 32, 0x1c, "HSO_TimeCnt" }, + {0, 1, 0, 32, 32, 0x1d, "LSSI_D" }, + {0, 1, 0, 32, 32, 0x1e, "HSO_LastUsed" }, + {0, 1, 0, 32, 32, 0x1f, "HSO_SlotSel" }, + {0, 1, 0, 32, 32, 0x20, "StackPtr" }, // 8065 stacks + {0, 1, 0, 32, 32, 0x22, "StackPtr2" }, + + {1, 1, 1, 32, 32, 0x4, "AD_Cmd" }, // Write symbols from here + {1, 1, 1, 32, 32, 0x5, "WDG_Timer" }, + {0, 1, 1, 32, 32, 0x7, "AD_Timed_Cmd" }, + {0, 1, 1, 32, 32, 0xb, "IDDQ_Test" }, + {1, 1, 1, 32, 32, 0xd, "HSO_Cmd" }, + {0, 1, 1, 32, 32, 0xe, "HSO_Time" }, + {0, 1, 1, 32, 32, 0x15, "LSSO_A" }, + {0, 1, 1, 32, 32, 0x17, "LSSO_B" }, + {0, 1, 1, 32, 32, 0x19, "LSSO_C" }, + {0, 1, 1, 32, 32, 0x1d, "LSSO_D" }, + +}; + + + /************************************************** + * main global variables + **************************************************/ + +//time_t timenow; + +HDATA fldata; // files data holder (declared in shared.h) + +/* bank layout. +* Full bank malloced to each relevant bkmap[bankno].fbuf, as 0-0xffff, for max of 16 banks. +* Regs (< 0x400) are mapped to Bank 8. Single banks are mapped completely in bank 8. +*/ + +BANK bkmap[BMAX]; // 16 banks max. + +// opcode and data bit arrays - each 0x8000 bytes malloced + +uint *opcbit; // opcode start +//uint *opdbit; // opcode data + +// bit array for RBINV. Allows for 0-0x400 only +// speeds up rbase searches, as most are invalid + +uint rbinv[0x20]; + +/* positions of print columns in printout +* +* 26 opcode mnemonic starts (after header) +* 32 operands start (+6) +* 49 pseudo source code starts (+17) +* 86 comment starts (+37) +* 180 max width of page - wrap here +*/ + +#define MNEMPOSN cposn[1] +#define OPNDPOSN cposn[2] +#define PSCEPOSN cposn[3] +#define CMNTPOSN cposn[4] +#define WRAPPOSN cposn[5] + +// [0] is for 'modified' marker for print + +const uint stdpos[6] = {0, 26, 32, 49, 83, 180}; // standard posns + +uint cposn [6] = {0, 26, 32, 49, 83, 180}; // modifiable by cmd + +#ifdef XDBGX + // debug counters various + int DBGrecurse = 0; + int DBGnumops = 0; // num of opcodes +#endif + +// number files, does not include sad.ini (= entry [6]), but does include debug file. + +#ifdef XDBGX + int numfiles = 6; +#else + int numfiles = 5; +#endif + + +uint tscans = 0; // total scan count, for safety +int xscans = 0; // main loop (for investigation) +uint lowscan = 100000; // rescan from here in scan_all +int opcnt = 0; // opcode count for emulate +int cmdopts = 0; // command options +int anlpass = 0; // number of passes done so far + +int numbanks; // number of banks-1 (so 8061 is always 0) + +//int indent; // TEST for code print layout +uint gcol; // where main print is at (no of chars = colno) +uint lastpad; // last padded to (column) +uint lastcmt; // last cmt state for end +uint wcol; // where warning output is at + +BASP basepars; + +CMNT fcmnt; // comments holder from cmnts file +//CMNT acmnt; // auto comments holder + +// instance holders (each hold one analysed opcode and all its operand data) + +INST cinst; // current (default) instance for std scans +INST sinst; // search instance (for conditionals, params, etc ) +INST einst; // current emulate instance + +// special scan blocks, outside standard scan chain +SBK tmpscan; // scan block for searches +SBK prtscan; // scan block for printing +SBK *emuscan; // scan block for imd pushes in emulate + +LBK prtblk; // dummy LBK block for printing adjustments +LBK *emuargs[EMUGSZ]; // tracking argument LBKs, created in emulate, for size fixup + +char flbuf [256]; // for directives and comment file reads +char nm [128]; // for temp name construction, string hacking, etc. +uint plist [32]; // generic param list and column positions for structs +SYM *bitnames[16]; // sym names for bitwise ops + +FSTK scanstack[STKSZ]; // fake stack holder for previous subr calls (for arg tracking) +FSTK emulstack[STKSZ]; // fake stack for emulation - emulate all parameter/argument getters +CPS cmnd; // command structure + + +int recurse = 0; // recurse count check + + + +//********** file extensions and flags for linux and Win32 ******************** + +#ifdef __linux__ + +// linux (gcc) + + HOP fd[] ={{".bin","r"}, {"_lst.txt","w"}, {"_msg.txt","w"}, + {"_dir.txt","r"},{"_cmt.txt","r"}, {"_dbg.txt","w"}, {"sad.ini","r"}}; + + #define PATHCHAR '/' + +#endif + +#if defined __MINGW32__ || defined __MINGW64__ + +// windows (CodeBlocks, mingw) + + HOP fd[] ={{".bin","rb"}, {"_lst.txt","wt"}, {"_msg.txt","wt"}, + {"_dir.txt","rt"},{"_cmt.txt","rt"}, {"_dbg.txt","wt"}, {"sad.ini","rt"} }; + + #define PATHCHAR '\\' + +#endif + + +/************************ + * print routines + **************************/ + +uint wnprt (uint nl, const char *fmt, ...) +{ // warning file prt + + uint chars; + chars = 0; + + if (fmt) + { + va_list args; + va_start (args, fmt); + chars = vfprintf (fldata.fl[2], fmt, args); + va_end (args); + } + wcol += chars; + if (nl) wcol = 0; // newline + while (nl--) fprintf(fldata.fl[2], "\n"); + return chars; // useful for if statements +} + +float rnd(float fv) +{ + +// round a float in 3 decimal places +// need varable places............ +// for digits d=1; (none), d *10; etc. + + int c, r, m; + + m = fv * 1000; // round down to integer (= NO OF DIGITS) + c = m%10; // lowest digit + r = m/10; // div by ten for round up + if(c >= 5) // round up + r++; + fv = (float) r/100; //restore float + return fv; +} + +int sprtfl(float fv, int pfw) +{ + // print float to nm holder array + char *tx, *s, p; + int cs; + + if (pfw) p = ' '; else p = '\0'; // zero or space + s = nm+64; //move after other strings + cs = sprintf(s,"%*.3f", pfw, rnd(fv)); + tx = s + cs-1; + + // trim/replace trailing zeroes and dot if no digits. + + while (*tx == '0') *tx-- = p; + if (*tx == '.') *tx-- = p; + if (pfw) return cs; + return tx-s; // chars 'printed' +} + +uint flprt(const char *fmt, float fv, uint (*prt) (uint,const char *, ...)) + { + // separate float print, to control width + // 'prt' argument allows print to debug or message files + + uint cs; + prt(0,fmt); + cs = sprtfl(fv,0); + prt(0,"%s ",nm+64); + return cs; +} + +void p_pad(uint c) +{ +// List file. Pad out to required column number if not already there +// and not already padded to. Add spaces if pstr (gcol) > lastpad +uint i; + + if (c <= gcol && gcol > lastpad) c = gcol+2; // add 2 spaces... + for (i = gcol; i < c; i++) fputc(' ',fldata.fl[1]); // fprintf(fldata.fl[1], " "); + gcol = c; + lastpad = c; // last padded column +} + +void wrap() + { + // newline and wrap back to comments column if already greater, + // or lastpad column if not + fprintf(fldata.fl[1], "\n"); + gcol = 0; + if (lastpad > CMNTPOSN) lastpad = CMNTPOSN; + p_pad(lastpad); + } + +void p_run(char c, uint num) +{ + uint i; + for (i = 0; i < num; i++) fprintf(fldata.fl[1], "%c",c); + gcol +=num; + if (gcol >= WRAPPOSN) wrap(); +} + + + + + + +void pchar(char z) +{ + fputc(z,fldata.fl[1]); + gcol++; + if (gcol >= WRAPPOSN) wrap(); +} + +uint pstr (uint nl,const char *fmt, ...) +{ // List file print + va_list argptr; + uint chars; + chars = 0; + + if (fmt) + { + va_start (argptr, fmt); + chars = vfprintf (fldata.fl[1], fmt, argptr); + va_end (argptr); + if (gcol >= WRAPPOSN) wrap(); + } + gcol += chars; + + if (nl) gcol = 0; // newline + while (nl--) fprintf(fldata.fl[1], "\n"); + return chars; +} + +uint pbin(uint val, uint fend) +{ // print val in binary + // not sure about pfw yet ... + uint i, tval, pfw; //, places; + +/* tval = val; + i = 1; + places = 0; + +// where is + while (tval) + { // find ms '1' bit + if (tval & 1) places = i; + tval >>= 1; + i++; + } + + if (pfw < cnv[size].bytes*8) pfw = cnv[size].bytes*8; //min size + if (pfw < places) pfw = places; */ + +//simpler + +if (val & 0xf000) pfw = 16; +else if (val & 0x0f00) pfw = 12; +else if (val & 0x00f0) pfw = 8; +//else if (val & 0x000f) pfw = 4; +else pfw = 4; // temp + + tval = val; + for (i = 16; i > 0; i--) + { // find ms '1' bit + if (i <= pfw) + { + if ((i/4*4) == i) pchar(' '); //space every 4 + if (tval & 0x8000) pchar('1'); else pchar('0'); + } + tval <<= 1; + } + +return pfw; + +} + +/* +uint pdec(int val, uint size) +{ + // print val in decimal can be -ve + // not sure about pfw yet ... + uint i, pfw, base; //, places; + int tval; + const char digits[] = "01234567890ABCDEF"; + char *p; + +* tval = val; + i = 1; + places = 0; + +// where is + while (tval) + { // find ms '1' bit + if (tval & 1) places = i; + tval >>= 1; + i++; + } + + if (pfw < cnv[size].bytes*8) pfw = cnv[size].bytes*8; //min size + if (pfw < places) pfw = places; / + +//simpler + + + base = 10; + tval = val; + p = nm+127; + *p-- = 0; + + do + { // have to build digits backwards !! + i = tval % base; + tval /= base; + *p-- = digits[i]; + } while (tval); + pstr (0,"%s",p+1); + + +* +if (i <= pfw) + { + if ((i/4*4) == i) pchar(' '); //space every 4 + if (tval & 0x8000) pchar('1'); else pchar('0'); + } + tval <<= 1; + } + +return pfw; / + +} +*/ + + + +void paddr(uint addr,uint sp, uint (*prt) (uint,const char *, ...) ) +{ + // printer for addresses with or without bank. + // if single bank or no bank drop bank + // For listing or debug output + + if (!numbanks || g_bank(addr) == 0) + prt(0,"%x", nobank(addr)); // no leading zero + else + { // bank+1 used internally, leading zeroes + prt(0,"%0x", (addr >> 16) -1); + prt(0,"%04x", nobank(addr)); + } + while (sp--) prt(0," "); +} + + + + // master exit and cleanup routines + +void closefiles(void) +{ + // flush and close all files + int i; + + for (i=0; i < numfiles; i++) // include sad.ini + { + if (fldata.fl[i]) // close file + { + fflush (fldata.fl[i]); + fclose (fldata.fl[i]); + fldata.fl[i] = NULL; + } + } + fldata.fillen = 0; //flag marker. + +} + +// 32 bit flag setters (int array) + +void set_flag(uint addr, uint *x) + { + uint bit; + + addr = nobank(addr); + bit = addr & 0x1F; // 32 bits + addr = addr / 32; + x[addr] |= (1 << bit); + } + +uint get_flag (uint addr, uint *x) +{ + uint bit; + + addr = nobank(addr); + bit = addr & 0x1F; + addr = addr / 32; + return x[addr] & (1 << bit); +} + + + +uint val_bank(uint bk) +{ + //for bank number + BANK *b; + + if (bk > 16) return 0; + + b = bkmap + bk; + if (b->bok) return 1; + return 0; +} + + +BANK *mapbank(uint addr) +{ + // if register, force to bank 8 + + if (addr < 0x400) return bkmap + 9; + return bkmap + ((addr >> 16) & 0xf); +} + + +uint bytes(uint fend) +{ //size in bytes. From field end (assumes start at 0) + fend &= 31; //drop sign + if (!fend) return 0; + + return (fend / 8) + 1; +} + + +void set_opstart (uint ofst) +{ + // 1 for code start + BANK *b; + b = mapbank(ofst); + if (!b->bok) return; // invalid + set_flag(ofst,b->opcbt); +} + +uint get_opstart (uint ofst) +{ + // for code start + BANK *b; + + b = mapbank(ofst); + if (!b->bok) return 0; // invalid + return get_flag(ofst, b->opcbt); +} + + +/* +void set_opdata (uint ofst, uint fend) +{ + // for opcode data + BANK *b; + uint i; + + b = mapbank(ofst); + if (!b->bok) return; // invalid + for (i = 0; i < bytes(fend); i++) + set_flag(ofst+i,b->opdbt); +} + + +void set_opdatar (uint start, uint end) +{ + // for opcode data + BANK *b; + uint i; + + b = mapbank(start); + if (!b->bok) return; // invalid + for (i = start; i <= end; i++) + set_flag(i,b->opdbt); +} + + +uint get_opdatar (uint start, uint end) +{ + // for all data and opcodes + BANK *b; + uint i; + + b = mapbank(start); + if (!b->bok) return 0; // invalid + for (i = start; i <= end; i++) + { + if (get_flag(i,b->opdbt)) break; + } + if (i <= end) return i; + return 0; +} + +uint get_opdata (uint ofst) +{ + // for opcode data + BANK *b; + + b = mapbank(ofst); + if (!b->bok) return 0; // invalid + return get_flag(ofst, b->opdbt); +} +*/ + + +// address handling subrs 0xfffff (4 bits bank, 16 bits addr in bank) + + +uint minadd(uint addr) +{ // min address for this bank + BANK *b; + b = mapbank(addr); + if (!b->bok) return 0xffff; // invalid + return b->minpc; +} + +uint maxpcadd (uint addr) +{ // max program address for this bank + BANK *b; + b = mapbank(addr); + if (!b->bok) return 0; // invalid + return b->maxpc; +} + +uint maxbkadd (uint addr) +{ // max bank address for this bank + BANK *b; + b = mapbank(addr); + if (!b->bok) return 0; // invalid + + return b->maxbk; +} + + +uint val_rom_addr(uint addr) + { + // return 1 if valid 0 if invalid. + // allows ROM addresses only. + // check Code, jumps, immds + + BANK *b; + + b = mapbank(addr); + + if (!b->bok) return 0; // invalid + + if (addr < b->minpc) return 0; // minpc = PCORG + if (addr > b->maxpc) return 0; // where fill starts + + return 1; // within valid range, OK + } + +uint max_reg(void) +{ + if (P8065) return 0x3ff; + else return 0xff; +} + +uint zero_reg(OPS *o) +{ + // answers 1 if zero register + // 8061 is R0 and R1 (max reg 0xff) + // 8065 is 0/1, 100/1,200/1,300/1 + +if (!o->rgf) return 0; + +if (nobank(o->addr) > max_reg()) return 0; // 8061 and safety check + +if ((o->addr & 0xfe) == 0) return 1; // allow 0 or 1 options +return 0; + +} + +uint val_stack_reg(uint addr) +{ + addr = nobank(addr); + if (P8065) + { + if (addr == 0x20 || addr == 0x22) return 1; + } + else + { + if (addr == 0x10) return 1; + } + +return 0; +} + +uint is_special_reg(uint reg) +{ + reg = nobank(reg); + if (P8065 && reg <= 0x22) return 1; + if (reg <= 0x10) return 1; + return 0; +} + +uint val_general_reg(uint reg) + { + // can't have special regs or stack + // and must be valid register + reg = nobank(reg); + if (P8065) + { + if (reg > 0x3ff) return 0; + if (reg <= 0x22) return 0; + } + else + { + if (reg > 0xff) return 0; + if (reg <= 0x10) return 0; + } + return 1; + } + +uint fix_addr_bank(uint addr) // INST *c ?? +{ + // sort out bank number and default + // remember bank+1 throughout + + uint x; + x = nobank(addr); + + // < 0x400 is a register, no bank + if (x <= max_reg()) return x; + + // single banks always 9 (databank) + if (!numbanks) return x | basepars.datbnk; + + // no bank and multibank, default to databank + if (!g_bank(addr)) return x | basepars.datbnk; + + return addr; +} + + +uint fix_input_addr(uint addr) +{ + // force single bank addrs to bank 9, + //otherwise add one to bank + // almost same as fix_addr_bank + + uint x; + x = nobank(addr); + + // < 0x400 is a register, no bank + if (x <= max_reg()) return x; + + // single banks always 9 (databank) + if (!numbanks) return x | basepars.datbnk; + + // no bank and multibank, default to databank - wait, is this right ?? + // leading zero marked as bank 0x20 so 05486 is 205486 + // can't use g_bank + + if ((addr & 0xff0000) == 0x200000) + { // bank 0 marker + return x | 0x10000; // bank 0 + } + + if (!g_bank(addr)) return x | basepars.datbnk; + + x = g_bank(addr); + x = x + 0x10000; // add one to bank + return nobank(addr) | x; + +} + + +uint val_input_bank(uint addr) + { + uint bk; + // registers always OK. + // uses internal bank, already +1 + if (nobank(addr) <= max_reg()) return 1; + + bk = addr >> 16; + + if (bkmap[bk].bok) return 1; + return 0; + } + +// ---------------------------- fieldwidth and masks etc + + +uint get_pfwdef(ADT *a) +{ // default print field width + uint ans, sz, sign; + + sign = (a->fend & 32); + sz = (a->fend & 31) - a->fstart; + +// need more here for variable fields. Probably lookup array ? + + ans = 0; // min default size + + if (sz == 7) ans = 3; + if (sz == 15) ans = 5; + if (sz == 23) ans = 6; + if (sz == 31) ans = 8; + + if (sign) ans += 1; //for -ve sign + // if (a->flt) val+= 4; // floating point + + return ans; +} + +uint get_signmask(uint fend) +{ // calc mask for sign bit + + if (!(fend & 32)) return 0; // not signed + + return (1 << fend); + +} + + +uint get_sizemask(uint fend) +{ // calc size mask + uint ans; + + // or is this (1 << (fend+1)) - 1; ?? + + fend &= 31; + ans = 1; + + while (fend--) + { + ans <<= 1; + ans |= 1; + } + + return ans; +} + + +uint get_startval(uint fend) +{ // calc startval for functions + uint ans; + + ans = get_sizemask(fend); + + if (fend < 32) return ans; + + ans >>= 1; // one bit less for sign bit + +return ans; + +} + + + +uchar* map_addr(uint *addr) + { + // only used by g_word, g_byte, g_val. Allow maxbk as limit (so fill can be read) + // registers are always mapped to bank 8 + + uint x; + BANK *b; + + x = *addr; + *addr = nobank(*addr); // return addr without bank + + if (*addr < 0x400) return bkmap[9].fbuf; + + b = mapbank(x); + + if (!b->bok || x > b->maxbk) return NULL; + + return b->fbuf; // valid address, return pointer + + } + + +//---------------------------------------------------------- +// probably need more smarts in here need to know if register address ?? + + +uint addbank(uint addr, INST *c, uint bkset) + { + addr = nobank(addr); + if (addr <= max_reg()) return addr; + +// redundant ??? + if (!numbanks) return addr |= basepars.datbnk; //0x90000; // force bank 8 //basepars.datbank ?? + + if (c && c->bank) return addr | (c->bank << 16); + return addr | bkset; + } + + +uint codebank(uint addr, INST *c) + { + // get BANK opcode to override default + return addbank(addr,c,basepars.codbnk); + } + +uint databank(uint addr, INST *c) + { + // get BANK opcode to override default + return addbank(addr,c,basepars.datbnk); + } + + +int g_byte (uint addr) +{ // addr is encoded bank | addr + uchar *b; + + b = map_addr(&addr); + if (!b) return 0; // safety + return (int) b[addr]; +} + + +uint g_word (uint addr) +{ // addr is encoded bank | addr + uint val; + uchar *b; + + b = map_addr(&addr); // map to required bank + if (!b) return 0; + + val = b[addr+1]; + val = val << 8; + val |= b[addr]; + return val; + + } + + +int sjmp_ofst (uint addr) +{ // negate value according to sign bit position + // used ONLY for jumps + // special 11 bit signed field..... + + int ofs; + + ofs = g_byte(addr) & 7; // bottom 3 bits + ofs <<= 8; + ofs |= g_byte(addr+1); // and next byte + if (ofs & 0x400) ofs |= 0xfffffc00; // negate if sign bit set + + return ofs; +} + + +int upd_ram(uint add, int val, uint fend) + { + int ans; + uint i, sz; + uchar *b; + + // not right for later bins ?? + + if (add > minadd(add)) return 0; // No writes to ROM + if ((add & 0xfe) == 0) return 0; // R0/1, 100/1, 200, 300 all fixed at zero + + b = map_addr(&add); + if (!b) return 0; // safety + + add = nobank(add); + + ans = 0; + sz = bytes(fend); // size in bytes + + for (i = add; i < add + sz; i++) + { + uchar x; + x = b[i]; // old value + b[i] = val & 0xff; + if (x != b[i]) ans = 1; // value has changed + val >>= 8; + } + + return ans; + } + + +int check_argdiff(int nv, int ov) +{ + // value difference for arguments + // nv is new value, ov is original + + int cnt; + + cnt = nobank(nv) - nobank(ov); //is this wrong for multibanks ? + if (cnt > 0 && cnt < 32) return cnt; + + #ifdef XDBGX + if (nv != ov) DBGPRT(1,"Argdiff reject %x %x", nv,ov); + #endif + return 0; +} + +uint get_pn_opstart (uint ofst, int d) +{ + // get previous or next opcode start + // d = 0 for previous, 1 for next + + int cnt; + uint end; + BANK *b; + + b = mapbank(ofst); + end = maxpcadd(ofst); + cnt = 0; + +// CAN scan THIS opcode if forwards, but not if backwards +// max possible code length is 8 (bank+fe+long index) + + while (ofst > 0 && ofst <= end) + { + if (d) ofst++; else ofst--; + cnt++; + if (get_flag(ofst,b->opcbt)) break; + if (cnt > 8) break; // no opcodes here + } + + if (ofst > end) return 0; + if (cnt > 8) return 0; + + return ofst; + +} + +uint find_opcode(int d, uint xofst, OPC **opl) + { + // would be simple lookup if it weren't for the prefixes. + // d = 0 for previous, 1 for next + int x, indx; + + *opl = 0; + xofst = get_pn_opstart (xofst, d); // previous or next + + if (!xofst) return 0; + if (!val_rom_addr(xofst)) return 0; + + x = g_byte(xofst); + indx = opcind[x]; // opcode table index + *opl = opctbl + indx; + + // Ford book states opcodes can have rombank(bank) AND fe as prefix (= 3 bytes) + + if (indx == 112) // bank swop - do as prefix + { + if (!P8065) indx = 0; // invalid for 8061 + else + { // skip op & bank, 2 bytes + x = g_byte(xofst+2); // get true opcode + indx = opcind[x]; + } + } + + if (indx == 111) // SIGN prefix + { // skip prefix, 1 byte + x = g_byte(xofst + 1); + indx = opcind[x]; // get base opcode + *opl = opctbl + indx; + if ((*opl)->sign) indx += 1; // get prefix opcode + else indx = 0; // prefix not valid + } + + if ((*opl)->p65 && !P8065) return 0; // 8065 check only + if (!indx) return 0; + *opl = opctbl + indx; // set opl for return + return xofst; + } + +void clr_inst(INST *x) +{ + memset(x,0,sizeof(INST)); // set all zero + x->opr[1].rgf = 1; + x->opr[2].rgf = 1; + x->opr[3].rgf = 1; +} + + +void do_one_opcode(uint xofst) + { + tmpscan.curaddr = xofst; + tmpscan.start = xofst; + tmpscan.stop = 0; + do_code(&tmpscan,&sinst); + } + + + +int find_pn_opcodes(int d, uint addr, int num, uint n, ...) // number back from start + { + // finds previous or next opcodes in list (sigix) max num is NUMPARS (16) + // use get_pn_opcode above, may be merged in here later + + // add a check for block terminators - jumps, rets etc. + // d = 0 for previous, 1 for next + // num = num of opcodes to scan (n and list is the opcodes) + // results in sinst instance + + int xofst, ans, cnt; + uint i; + OPC *opl; + va_list ix; + + // assemble opcode sigixs into array + va_start(ix,n); + + memset(plist,0,sizeof(plist)); + + if (n > NC(plist)) n = NC(plist); + + i = n; + while (i--) + { + plist[i] = va_arg(ix, int); + } + + va_end(ix); + + // OK -now see if we can find a match + + xofst = addr; + if (d) xofst--; // allow use of supplied xofst if going forwards, not backwards + ans = 0; + cnt = 0; + // x->ofst must fall within specifed range (backwards from current). Allow for curinst + // being fed in so <= + + while (!ans) + { + xofst = find_opcode(d, xofst, &opl); + if (!xofst) break; + + cnt ++; + if (cnt > num) {break;} + + // 16 jump, 20 ret. + if (d && opl->sigix == 20) break; // not through a RET (forwards) + + if (d && opl->sigix == 16) + { //allow jumps forwards + do_one_opcode(xofst); + xofst = sinst.opr[1].addr; + xofst--; + } + + for (i = 0; i < n; i++) + { + if (opl->sigix == plist[i]) ans = xofst; + } + } + + if (ans) + { + do_one_opcode(xofst); + } + else + { + clr_inst(&sinst); + +// memset(&sinst,0,sizeof(INST)); +// sinst.opr[1].rgf = 1; +// sinst.opr[2].rgf = 1; +// sinst.opr[3].rgf = 1; + } + return ans; + } + + +int mark_emu(SBK *s, uint ofst) + { + // get the calling proc from opcode before any new args + // and flag it as emulate reqd + + OPC *opl; + SBK *x; + SUB * sub; + uint ans; + + #ifdef XDBGX + DBGPRT(1,"In Mark_emu (for ARGS) for %x", s->start); + #endif + + ans = 0; + + if (!ofst) return ans; + + ofst = find_opcode(0, ofst, &opl); // opcode before args + + if (!ofst || opl->sigix != 17) + { + #ifdef XDBGX + DBGPRT(1,"Not found CALL !!"); + #endif + + return ans; +} + // CALL, + do_one_opcode(ofst); + x = get_scan(sinst.opr[1].addr, sinst.opr[1].addr); + + sub = get_subr(sinst.opr[1].addr); + + if (sub && sub->size) + { +#ifdef XDBGX + DBGPRT(1,"args ignored - preset size"); //preset size, no emul +#endif + return x->start; + } + + if (x && !x->emulreqd) + { + #ifdef XDBGX + DBGPRT(1,"SET EMUREQD for sub %x", x->start); + #endif + x->emulreqd = 1; // this is where args attached + ans = x->start; // address of sub + s->args = 1; // and mark caller + } + + return ans; + } + + + + + + + + +void check_args(SBK *s, INST *c) + { + // check if register qualifies subroutine for EMULATE + // i.e. was popped and modified.... + // only used in scanning mode. Called from upd_watch. + + int i, args; + FSTK *t; + RST *r; + OPS *o; + +// for autoinc and indirect only at present + + + + o = c->opr+1; // target for increments + if (zero_reg(o)) return; // >addr)) return; // not for R0 + + r = get_rgstat(o->addr); + + if (!r) return; // not found + +#ifdef XDBGX + DBGPRT(1,"R%x found pop=%d", o->addr, r->popped); +#endif + + r->ofst = o->val; // update to inc'ed address + r->inc = 1; // set inc + set_rgsize(r,15); // incs are ALWAYS word + + if (!s->scanning) return; + +#ifdef XDBGX + DBGPRT(1,"R%x check args", o->addr); +#endif + + // subroutine immediately before the ARGS is the one which needs to be + // flagged as EMUREQD and THIS subr flagged as an ARGS getter + + if (!r->popped) return; // not popped + + + + args = 0; + + for (i=STKSZ-1; i >= 0; i--) + { + t = scanstack+i; // this is UP the scan mode call chain + if (t->type == 1 && t->reg == o->addr && t->popped) + { + args = check_argdiff(o->val, t->origadd); + if (args) + { + #ifdef XDBGX + DBGPRT(1,"found FSTK for %x at %d", o->addr, i); + #endif + break; + } + } + } + + // don't actually have to find a stack entry + // check against zero address + + if (!args) args = check_argdiff(o->val, 0); + + if (args) + { + // flag this subr doing the arg get so that + // calling it will trigger emu of the caller. + #ifdef XDBGX + if (i < 0) DBGPRT(1,"no FSTK %x (but %d args)", s->curaddr, args); + #endif + mark_emu(s, t->origadd); // seems to be OK even if not found (by i) + } + } + + +void log_emuargs(LBK *l) +{ +int i; + +if (!l) return; + +for (i = 0; i < EMUGSZ; i++) + { + if (emuargs[i] == l) break; // already logged + if (!emuargs[i]) + { + emuargs[i] = l; + #ifdef XDBGX + DBGPRT(1,"log_emu %x = %d",l,i); + #endif + break; + } + } + #ifdef XDBGX +if (i >= EMUGSZ) DBGPRT(1,"NO SLOTS FOR LOG EMU"); + #endif + +} + + +int cellsize(ADT *a) +{ + // byte size of single ADT entry + if (!a) return 0; + return a->cnt * (bytes(a->fend)); +} + + +int listsize(CHAIN *x, uint start, int seq) +{ + // list size to next newline (last par). + // may continue (via seq) from last newline + // use chain.lastix in case it hits end + + return adtchnsize(x, start, seq, 1); + +} + +int totsize(CHAIN *x, uint start) +{ + // total size of aditional data chain + // ignore newlines + return adtchnsize(x, start, 0, 0); +} + + + + + +int add_args(LBK *l, int dreg, int args) +{ + // add args in pairs (= words) to make easier sizing later + // assume l is set up as C_ARGS already + + RST *r; + int size, reg; + uint addr; + + if (!l) return -1; + + #ifdef XDBGX + DBGPRT(1,"in add_args"); + #endif + + size = args - totsize(&chadnl,l->start); // new args required + reg = dreg; + addr = l->start; + + if (size <= 0) return 0; // bigger or match + + while (size > 0) + { + ADT *a; + a = add_adt(&chadnl, l->start,256); //new one + if (a) + { +#ifdef XDBGX + DBGPRT(1,"add_adt size %d", size); +#endif + r = get_rgarg(dreg, addr); + if (r) reg = r->reg; + if ((r && (r->reg & 1)) || size < 2) a->cnt = 1; + else a->cnt = 2; // i.e. for each byte so far + + a->dreg = reg; + a->data |= basepars.datbnk; // g_bank(l->start); + dreg += a->cnt; // next lookup + addr += a->cnt; + reg += a->cnt; // next reg if list + size -= a->cnt; + } + else break; // not found + } + + l->end = l->start+args-1; // set size correctly + l->size = totsize(&chadnl,l->start); // safety check + return 1; +} + + +void do_args(SBK *s, FSTK *t) + { + // called from pushw or stx in emulate. + + // but not if subr has args attached ?? how to get this ??? + + LBK *l; + + int args, addr; + + args = check_argdiff(t->newadd, t->origadd); // safety check..... + if (!args) return; + + addr = mark_emu(s,t->origadd); // mark calling subr for args + + if (addr) + { // check if subr is manually defined + SUB *sub; + sub = get_subr(addr); + if (sub && sub->size) + { + #ifdef XDBGX + DBGPRT(1,"ARGS IGNORED, SUB %x has cmd args (%d)", addr, sub->size); + #endif + return; + } + } + + // probably, data entries will have been created for reqd args. + // Delete them first......... + + for (addr = t->origadd; addr < t->origadd+args; addr++) + { + l = get_cmd(addr, 0); + if (l) + { + addr = l->end; // skip to end of this block + if (l->fcom <= C_LONG) chdelete(&chcmd,chcmd.lastix,1); + } + } + + l = get_aux_cmd(t->origadd,0); // any command here ? + + if (l && l->cmd) + { + #ifdef XDBGX + DBGPRT(0,"ARGS CMD SET for %x (%x-%x)", t->origadd, l->start, l->end); + #endif + return; + } + + if (l) + { + if (l->fcom == C_ARGS) + { // a further set, probably call same getter again + // THIS DOESN'T CHECK FOR OVERLAPS !!! + int tsz; + #ifdef XDBGX + DBGPRT(1,"Found ARGS %x-%x (%d)", l->start, l->end, l->size); + #endif + tsz = totsize(&chadnl,l->start); //debug test + if (tsz != l->size) + { +#ifdef XDBGX + DBGPRT(1,"PANIC - sz no match, %d %d", l->size, tsz); +#endif + } + + + if (add_args(l, t->reg, args) > 0) + { + #ifdef XDBGX + DBGPRT(1,"ARGS-extend to %d, %x-%x", args, l->start, l->end); + #endif + } + + } + } + else + { // if cmd not found, add a new args command + #ifdef XDBGX + DBGPRT(1,"ARGS-create %d, %x-%x", args, t->origadd, t->newadd-1); + #endif + l = add_aux_cmd(t->origadd, t->newadd-1, C_ARGS,s->curaddr); + add_args(l, t->reg, args); + } + log_emuargs(l); + } + + + +void do_rbcheck(SBK *s, INST *c) +{ + OPS *o; + RBT *b; + uint val; + + if (!s->scanning) return; // only in scan mode + if (!c->wop) return; // no writes + + o = c->opr + c->wop; // destination operand (wop) + +// BUT with STW, c->wop could be indirect or indexed and be in [4] +// but the 'find' will always be full default range........... + + if (c->wop == 1) + { + if (c->opcsub == 2 && c->opr[1].inc) o = c->opr + 4; // c->inc move to true register + if (c->opcsub == 3) return; // ignore indexed stx + } + + if (!o->rgf) return; + if (!val_general_reg(o->addr)) return; + if (get_flag(o->addr, rbinv)) return; // already marked as invalid + + b = get_rbt(o->addr,0); // is it an rbase (default)? + + if (b && b->cmd) + { + #ifdef XDBGX + DBGPRT(1,"cmd bans rbase update %x", o->addr); + #endif + return; + } + + val = databank(o->val,c); // if val is an address, add bank + + // only add an RBASE if ldw or stw, immediate,WORD, not zero and not invalid + // AD3w allowed for NEW rbase if other op is an rbase already (A9L 7096 ad3w) + + if (!b) + { + if (c->opcsub == 1 && o->val > max_reg() && bytes(o->wfend) == 2) //o->wsize == 2) + { + // ldw, stw or ad3w and immediate - new rbase candidate + if (c->opcix == 41 || c->opcix == 49 || (c->opcix == 25 && c->opr[2].rbs)) + b = add_rbase(o->addr, val,0,0xfffff); + } + } + else + { // rbase already exists, invalidate if not same value or not immediate + + if (b->val != val || c->opcsub != 1) + { + b->inv = 1; // not same value + set_flag(o->addr, rbinv); + #ifdef XDBGX + DBGPRT(1,"Inv rbase R%x (%x)", o->addr, c->ofst); + #endif + } + } // end check rbase + } + + +void upd_watch(SBK *s, INST *c) + { + OPS *o; + + if (anlpass >= ANLPRT) return; // ignore if in print phase + + o = c->opr + c->wop; // destination entry (wop) + + do_rbcheck(s,c); + + if (s->scanning || s->emulating) + { + if (c->wop) + { + upd_ram(o->addr,o->val,o->wfend); + + #ifdef XDBGX + + if (anlpass < ANLPRT) + { + DBGPRT(0,"%05x ",s->curaddr); + if (s->emulating) DBGPRT(0, "E"); + DBGPRT(0, "RAM %x = %x (%d)", o->addr, o->val, o->wfend); + if (c->opcsub == 2) DBGPRT(0, "[%x]", c->opr[4].addr); // for indirect + + //only place wop = 1 is for stx.... + + if (c->opcsub == 3 && c->wop != 1) DBGPRT(0, "[%x]", c->opr[1].addr); // for indexed + if (nobank(o->addr) >= PCORG) DBGPRT(0," WROM !!"); + + DBGPRT(1,0); + } + #endif + } // end if wop (write op) + + // NOW check for autoinc ....always opr[1] + + o = c->opr+1; // this operand gets incremented + + if (c->opcsub == 2) // indirect + { // addr & val replaced with indirect ones. Put originals vals back for increment, but keep indirect address in [0] + o->val = o->addr; // value = address accessed + o->addr = c->opr[4].addr; // restore orig register address + c->opr[4].val = o->val; // save this value + } + + if (o->inc) // c->inc can only be set within opcsub == 2 + { // auto increment set - update value + + o->val += bytes(o->rfend); // autoinc is by op size + + // o->val &= getsizemask...camk[o->ssize]; // allow for overflow ? + upd_ram(o->addr,o->val,15); //word update + check_args(s,c); // check if qualifies as subr argument + + #ifdef XDBGX + if (anlpass < ANLPRT) + { + DBGPRT(0, "%05x RAM++ %x = %x (%d)",s->curaddr, o->addr, o->val, 15); + if (nobank(o->addr) >= PCORG) DBGPRT(0," ROM !!"); + DBGPRT(1,0); + } + #endif + } + } +} + + +//------------------------------------------------------------------------ + +void prt_banks(uint (*prt) (uint,const char *, ...)) +{ + int i; + BANK *x; + + // bank no , file offset, (opt) pcstart, (opt) pcend (opt) end file offset, (opt) fillstart + + if (prt == wnprt) + { + prt(2,"# Banks Found. For information, can uncomment to manually override"); + // prt(1,"# bank_num, file_offset, start_addr, end addr, [filler_start]"); + + for (i = 0; i < BMAX; i++) + { + x = bkmap+i; + if (x->bprt) + { + prt(0,"# bank %d %5x ", x->dbank-1, x->filstrt); + paddr(x->minpc,1,prt); + paddr(x->maxbk,1,prt); + if (x->cmd) prt (0," # by user command"); + + if (!x->bok) prt (0,"** ignored **"); + prt(1,0); + } + } + prt(1,0); + } + +#ifdef XDBGX +else + { + // debug printout + + DBGPRT(1,0); + DBGPRT(1,"-------DEBUG BANKS---------"); + DBGPRT(1,"bank filstart, minpc, maxbk, maxpc, filend. bok,cmd,bkmask, cbnk, P65 dbank" ); + + for (i = 0; i < BMAX; i++) + { + x = bkmap + i; + if (x->bok) + { + DBGPRT(0,"%2d %5x %5x %5x %5x (%5x)", i, x->filstrt, x->minpc, x->maxbk,x->maxpc, x->filend); + DBGPRT(1," %x %x %x %x %x %x", x->bok, x->cmd,x->bkmask, x->cbnk, x->P65, x->dbank); + } + } + DBGPRT(1,0); + DBGPRT(1," Non bok ---"); + + for (i = 0; i < BMAX; i++) + { + x = bkmap + i; + if (!x->bok && x->minpc) + { + DBGPRT(0,"%2d %5x %5x %5x %5x (%5x)", i, x->filstrt, x->minpc, x->maxbk,x->maxpc, x->filend); + DBGPRT(1," %x %x %x %x %x %x", x->bok, x->cmd,x->bkmask, x->cbnk, x->P65, x->dbank); + } + } + DBGPRT(1,0); +} + +#endif + +} + + + +void prt_cellsize(ADT *a, uint (*prt) (uint,const char *, ...)) +{ +char c; +uint sign, fend; +if (a->fstart) +{ +} +else +{ + c = 'Y'; +sign = (a->fend & 32); +fend = a->fend & 31; + prt (0,"%c", sign ? 'S' : 'U'); + + if (fend == 15) c = 'W'; + if (fend == 23) c = 'T'; + if (fend == 31) c = 'L'; + prt(0,"%c ", c); +} + +} + +void prt_pfw(ADT *a, uint (*prt) (uint,const char *, ...)) +{ +// will have subfield sizes in here + if (a->pfw && a->pfw > get_pfwdef(a)) prt(0,"P %x ",a->pfw); +} + +void prt_radix(ADT *a, int drdx, uint (*prt) (uint,const char *, ...)) +{ + if ((a->prdx != drdx) || prt != wnprt) + { // print radix + if (a->prdx == 1) prt(0,"X 16 "); + if (a->prdx == 2) prt(0,"X 10 "); + if (a->prdx == 3) prt(0,"X 2 "); + } +} + + +void prt_adt(CHAIN *x, uint start, int drdx, uint (*prt) (uint,const char *, ...)) +{ + // print additional params for any attached ADT chain, LBK mainly + // the 'prt' argument allows print to debug or message files + // dcm is 1 bit + +ADT *a; +int nl; + + a = start_adnl_loop(x,start); //(ADT*) chmem(x,0); + + +//if (start == 0x9279c) +//{ +//DBGPRT(1,0); +//} + + + nl = 0; + +while ((a = get_next_adnl(x,a))) + { + if (a->cnt || prt != wnprt) + { // ignore if zero size, and not debug + if (nl) prt(0,"| "); else prt(0,": "); + if (a->newl) nl = 1; else nl = 0; // newline is for END of block + + if (a->ans) + { //subroutine answer definition + prt(0," = %x ", a->data); + } + + if (a->cnt > 1) prt(0,"O %d ", a->cnt); + if (a->foff) prt(0,"D %x ", a->data); + + if (a->enc) prt(0,"E %d %x ", a->enc, a->data & 0xff); + else + if (a->vaddr) + { + prt(0,"R "); + } + else + if (a->bank) prt(0,"K %x ", a->bank-1); + else prt_cellsize(a,prt); + + prt_pfw(a,prt); // print fieldwidth if not default + prt_radix(a,drdx,prt); // print radix if not default + + if (a->flt) flprt("V ", a->fldat, prt); + if (a->fnam) prt(0,"N "); + + #ifdef XDBGX + if (prt == DBGPRT) prt (0,"[R%x, csz %d] ", a->dreg, cellsize(a)); + #endif + } + + } +} + + +void prtflgs(int flgs, uint (*prt) (uint,const char *, ...)) +{ + // stop dupls by removing matched flags + uint i; + + for (i = 0; i < NC(mopts); i++) + { + if ((flgs & mopts[i]) == mopts[i]) + { + prt(0, ": %s ", optstrs[i]); + flgs &= ~mopts[i]; + } + } +} + + +void prt_opts(void) +{ + // print opt flags to msg file + wnprt(0,"setopts " ); + prtflgs(cmdopts, wnprt); + wnprt(2,0); +} + +void prt_layout(void) +{ + // print layout options + int i; + if (!cposn[0]) return; //no changes + + wnprt(0,"layout " ); + for (i = 3; i < 6; i++) + { + wnprt(0," %d",cposn[i]); + } + wnprt(2,0); +} + +void prt_glo(SUB *sub, uint (*prt) (uint,const char *, ...)) +{ + SPF * s; + int x; + + // print special func in style of adt + x = 0; + + if (!PARGC && sub->cptl) + { + prt (0," $ C"); + x = 1; + } + + s = get_spf(sub->start); + + if (!s) return; // no special funcs + + if (!x) prt(0," $"); + + prt(0," F %s %x", spfstrs[s->spf-4],s->addrreg); + + // tab subroutines need extra par + if (s->spf > 12) prt (0," %x", s->sizereg); + +} + + +void prt_rbt ( uint (*prt) (uint,const char *, ...)) +{ + RBT *r ; + uint ix, v; + + for (ix = 0; ix < chbase.num; ix++) + { + r = (RBT*) chbase.ptrs[ix]; + + v = get_flag(r->reg,rbinv); +// if (get_flag(o->addr, rbinv)) return; // already marked as invalid + + if (prt == wnprt) + { + if (!r->inv && !v) + { //not invalid or flagged invalid + prt(0,"rbase %x " , r->reg); + paddr(r->val,1,prt); + if (r->rstart) + { + prt(0," "); + paddr(r->rstart,1,prt); + paddr(r->rend,0,prt); + } + if (r->cmd) prt(0," # cmd"); + prt(1,0); + } + } + else + { + prt(0,"rbase %x %x" , r->reg , r->val); + prt(0," %x %x", r->rstart, r->rend); + if (r->inv) prt (0, " INV"); + if (v) prt(0," INVFLAG"); + if (r->cmd) prt(0," # cmd"); + prt(1,0); + } + } + prt(1,0); +} + +void prt_scans(void) +{ + // not merged with DBG version, as it's very different. + uint ix; + SBK *s; + + for (ix = 0; ix < chscan.num; ix++) + { + s = (SBK*) chscan.ptrs[ix]; + if (s->cmd) {wnprt(0,"scan "); + paddr(s->start,0,wnprt); + wnprt(1,0);} + } + wnprt(1,0); +} + +void prt_psw (void) +{ + PSW *r ; + uint ix; + + for (ix = 0; ix < chpsw.num; ix++) + { + r = (PSW*) chpsw.ptrs[ix]; + + wnprt(0,"pswset "); + paddr(r->jstart,1,wnprt); + paddr(r->pswop ,0,wnprt); + wnprt(1,0); + } + wnprt(1,0); +} + + +/************************************************ + * Symbols + ************************************************/ + +int new_symname (SYM *xsym, const char *fnam) + { + // replace symbol name + // symname cannot be NULL + + int oldsize; + + if (xsym->cmd) return 0; // can't rename if set by CMD + if (!fnam || !*fnam) return 0; + + oldsize = xsym->tsize; // old size + xsym->tsize = strlen (fnam) + 1; // include null, strlen does not... + + if (xsym->tsize > SYMSZE) xsym->tsize = SYMSZE; // max storage size is 64 + + xsym->name = (char *) mem (xsym->name,oldsize,xsym->tsize); // get new sym size + + if (xsym->name) strncpy (xsym->name, fnam, xsym->tsize); // and replace it + xsym->name[xsym->tsize-1] = '\0'; // safety + + return xsym->tsize; + } + + + +void prt_cmd(LBK *c, uint (*prt) (uint,const char *, ...)) +{ + uint f; + // print single LBK command + + if (prt == wnprt) + { // real printout + if (nobank(c->start) < PCORG) return; // safety + if (c->fcom == C_TIMR) prt (0,"# "); + prt(0,"%-7s ", cmds[c->fcom]); + paddr(c->start,1,prt); + paddr(c->end ,0,prt); + } + else + { // debug printout + prt(0,"%-7s %x %x", cmds[c->fcom], c->start,c->end); + } + +/* if (t->namex) // name expected + { + x = get_sym(0, c->start,-1,0); + if (x) + { + prt(0," %c%s%c ", '\"', x->name, '\"'); // TEMP + x->noprt = 1; // printed this symbol + } + } */ + + // global options here + + f = 0; + if (c->term) + { + prt (0," $"); + prt (0," Q"); + if (c->term > 1) prt(0," %d", c->term); + f = 1; + } + + if (c->argl) + { + if (!f) prt (0," $"); + prt (0," A"); + } + + if (c->fcom == C_TABLE || c->fcom == C_FUNC) prt_adt(&chadnl, c->start,1, prt); + else prt_adt(&chadnl, c->start,0, prt); + + if (c->sys) { prt(0," # auto");} + prt(1,0); + } + + + +void prt_cmds(uint (*prt) (uint,const char *, ...)) +{ + // command printer for both cmd and auxcmd, debug or real + // do both chains in one pass in start order + + uint ixc, ixa; + LBK *cmd, *aux; + + ixa = 0; + + aux = (LBK*) chaux.ptrs[ixa]; // first aux + + for (ixc = 0; ixc < chcmd.num; ixc++) + { + cmd = (LBK*) chcmd.ptrs[ixc]; + + while (aux && aux->start <= cmd->start) + { + prt_cmd(aux,prt); + ixa++; + if (ixa < chaux.num) aux = (LBK*) chaux.ptrs[ixa]; + else aux = 0; + } + + prt_cmd(cmd,prt); + + } + prt(2,0); +} + + +SYM *new_autosym (uint ix, int addr) +{ + +// add auto sym with address +// all of these are whole read symbols + + SYM *xsym; + int c; + + if (anlpass >= ANLPRT) return 0; + + if (ix >= NC(anames)) return NULL; // safety check + + if (!(cmdopts & anames[ix].mask)) return NULL; // autoname option not set + + // make name first + + c = sprintf(nm, "%s", anames[ix].pname); + // add address + c += sprintf(nm+c, "_"); + if (numbanks) c += sprintf(nm+c, "%x", (addr >> 16) -1); + c += sprintf(nm+c, "%04x", nobank(addr)); + //else c += sprintf(nm+c, "_%05x", addr); // include bank + + xsym = add_sym(nm, C_SYS, addr,32, 32, 0,0xfffff); // add new (read) symbol + + if (xsym->cmd) return xsym; // can't change a cmd sym + + if (ix > xsym->xnum) + { //only if higher level autonumber + new_symname (xsym, nm); + xsym->xnum = ix; + } + + return xsym; +} + + +int do_table_sizes(uint start, int scols, uint *apnd, uint maxend) +{ + // assume a table for now............... +// so a rectangle of numbers... +// last byte may be a filler. +// last byte/word by command may or may not be correct end.... +// start should be OK though. + +// how to 'trim' the sizes ??? + + +// to decide on table structure more closely, need an array of rows-1 x cols-1 +// and keep ALL the interdigit differences to get best fit. this should also +// allow splitting up unknown blocks into probable tables............. +// so algorthm must be size independent ?? + + +//or find a central value ? (mean/median) or modal value (most common) + + + + +// func finder should be child's play compared to this.......... + + +//looks like we need a function finder too........................ +// and then a struct finder.............. + + int i,j; + uint apend; + int size, addr; + int rmax; + int cmax; + int rscore, cscore; + int row, col, lval, val, dif; + int cols, rsize; // bit array ?? + + int ansrow, anscol; + + int cdif, rdif, tot, ansscore; //test + + ansscore = 20000; + anscol = 0; + ansrow = 0; + + apend = *apnd; + cols = 0; + scols = nobank(scols); + + +#ifdef XDBGX + DBGPRT(2,0); + DBGPRT(1,"do data table sizes for %x-%x cols %d, max %x", start, apend, scols, maxend); + #endif + + for (j = 0; j < 2; j++) + { // for apparent and maxend + + if (!j) size = apend-start; + else + { + if (maxend == apend) break; else size = maxend-start; + } + + +// first find candidate sizes + + + for (i = 3; i < 32; i++) + { + row = size/i; + if (row*i == size && row < 32) cols |= (1<>= 1; + if (cols & 1) + { + rmax = 0; + rscore = 0; + cmax = 0; + cscore = 0; + cdif = 0; + rdif = 0; + rsize = size/i; //possible row size + +//but this may be too many rows.......... + #ifdef XDBGX + DBGPRT(1, "- - - - %d x %d (%d)", i, rsize, size ); + #endif + + // do rows first (cols within each row) + + for (row = 0; row < i; row++) // for each row + { + addr = start+(row*rsize); // start of this row (col zero) + lval = g_byte(addr); // first col of row + + for(col = 1; col < rsize; col++) // col in row + { + val = g_byte(addr+col); + dif = abs(val-lval); + if (dif > rmax) rmax = dif; // max dif for this row + rscore += dif; // all rows; + lval = val; // update last value + } + + } + + + // do cols (rows in each col) + + for (col = 0; col < rsize; col++) // col in row + { + addr = start+col; // start of this row + lval = g_byte(addr); // first row of col + + for (row = 1; row < i; row++) // for each row + { + val = g_byte(addr+(row*rsize)); + dif = abs(val-lval); + if (dif > cmax) cmax = dif; + cscore += dif; + lval = val; + } + } + +// max diff appears to be the real deal. + + dif = (rsize-1)*(i-1); // no of elements + if (dif) { + rdif = (rscore*10) / dif; + cdif = (cscore*10) / dif; + } + + tot = rdif +cdif; + + + #ifdef XDBGX + DBGPRT(1,"%dx%d rdf,cdf %d+%d/%d = %d", i,rsize,rdif,cdif,dif, tot); + #endif + + if (tot >= 0 && tot < ansscore) + { + ansscore = tot; + ansrow = rsize; + anscol = i; + } + + if (!tot && anscol < 4 && i > 4 and i < 8) + { // choose a middle value for small cols and all zeroes + ansscore = -1; + ansrow = rsize; + anscol = i; + } + } // end possible colcnt + + } // end col count loop +} +#ifdef XDBGX + + DBGPRT(1,"LOWEST %dx%d TOT %d ",anscol, ansrow, ansscore ); +#endif + +// if score = 0 then default to common sizes. + +if (ansrow > 16 || ansscore == 0) ansrow = 0; + + *apnd = (ansrow*anscol); + return ansrow; +} + +LOOP l; + +void turn_dtk_into_data(void) +{ + + uint ix, cix, dix, dcmd, start,lstart; //, end; + uint push; + TRK *d, *n; + DTD *a; + LBK *k, *p; + JMP* j, *m; + +#ifdef XDBGX + DBGPRT(2,0); + DBGPRT(1,"dtk to data"); +#endif + +//start with all loops must be spotted via autoinc + +/* for (ix = 0; ix < chdtk.num; ix++) + { + d = (DTK*) chdtk.ptrs[ix]; + if (d->opcsub == 1 ) + { + lstart = 1; + while ((a = get_dtkd(&chdtka, d, lstart))) + { + lstart = a->stdat+1; //next start + start = a->stdat; + if (val_rom_addr(start)) + { + p = get_cmd(start,0); + DBGPRT(0,"%x IMD = %x ", d->ofst,a->stdat); + if (p) DBGPRT(1," IGNORED %x-%x %s",p->start, p->end,cmds[p->fcom]); else DBGPRT(1,0); +//get cmd first................. + + // DBGPRT(1,"%x-%x %s",p->start, p->end,cmds[p->fcom]); + // else DBGPRT(1, "***"); + } + } + // DBGPRT(1,0); + } + } + +/pushes first ?? +// these are ALL INCORRECT except for 79f9 on A9L + + for (ix = 0; ix < chdtk.num; ix++) + { + d = (DTK*) chdtk.ptrs[ix]; + // dcmd = bytes(d->fend); //may add an fcom instead... + + if (d->psh) // && d->opcsub == 1) + { //imd push + // DBGPRT(0, "%x is push ", d->ofst); + lstart = 1; + + while ((a = get_dtkd(&chdtka, d, lstart))) + { + lstart = a->stdat+1; //next start + start = a->stdat; + if (val_rom_addr(start)) + { + + get_cmd(start,0); + cix = chcmd.lastix; + while (cix < chcmd.num && start) + { + p = (LBK*) chcmd.ptrs[cix--]; + if (p->end < start) break; //no overlap + if (p->start <= start && p->end >= start) start = 0; + } + if (start) DBGPRT(1,"push %x from %x",start, d->ofst); + } +//get cmd to make sure it's not used. + } + // DBGPRT(1,0); + } + } + // add_cmd(start, start+bytes(d->fend)-1,bytes(d->fend), d->ofst); + //scan dtk chain and add data entries + DBGPRT(2,0); +*/ + + + for (ix = 0; ix < chdtk.num; ix++) + { + d = (TRK*) chdtk.ptrs[ix]; + dcmd = bytes(opctbl[d->opcix].rfend1); //may add an fcom instead... + if (opctbl[d->opcix].sigix == 14) push = 1; else push = 0; + + // find attached data start(s) + + lstart = 1; + +#ifdef XDBGX + if (d->ainc) DBGPRT(1, "%x is INC++ sz %d", d->ofst, d->ainc); + #endif + + while ((a = get_dtkd(&chdtko, d->ofst, lstart))) + { + lstart = a->stdat+1; //next start + start = a->stdat; + + + // add_cmd(start, start+bytes(d->fend)-1,bytes(d->fend), d->ofst); // add data entry TEMP + +#ifdef XDBGX + DBGPRT(1,"%x dat=%x opc %d",d->ofst, a->stdat, d->opcsub); +#endif + // get nearest command, for overlap checks with data + + // end = start + bytes(d->fend)-1; //end of this data item + p = get_cmd(start,0); + + if (p && p->start <= start && p->end >= start) + { + //overlap - kill if matches criteria + + if (p->fcom == dcmd) + { +#ifdef XDBGX + DBGPRT(1,"duplicate command %x %s", start, cmds[dcmd]); +#endif + start = 0; // kill insert + break; + } + // words/bytes/longs within func/tab (+ struct/timer for now). args ?? - different chain) + if ( dcmd >= C_BYTE && dcmd <= C_LONG && p->fcom >= C_TABLE && p->fcom <= C_TIMR) + { +#ifdef XDBGX + DBGPRT(1,"overlap struct %x %s", start, cmds[dcmd]); +#endif + start = 0; + // a->olp = 1; + break; + } + else + { +#ifdef XDBGX + DBGPRT(1,"overlap %x %s from %x with %x-%x %s", a->stdat, cmds[dcmd], d->ofst, p->start, p->end,cmds[p->fcom]); +#endif + if (p->fcom == C_CODE && !push) //push + { +#ifdef XDBGX + DBGPRT(1, " **** CODE ***"); +#endif +//a->inv = 1; // temp.......... +if (p->end-start < 16 ) //&& start > 0x9201f) + { +#ifdef XDBGX + DBGPRT(1,"inx?"); +#endif + } + + } + //and extra work to go here.............. + break; + } + } + // } // end overlap check + + + +// if imd, then look for a To at d->ofst+d->ocnt, for a loop with start at... +// AA has start and END, to be careful of...check by using inc.... + + + + if (start && d->ainc) + { + // inc is set, look for a loop. may be better after an imd ??? will be before the inc ?? + j = 0; + cix = get_jump(&chjpf,d->ofst); // from jump (after inc opcode) + while (cix < chjpf.num) + { + m = (JMP*) chjpf.ptrs[cix]; + if (m->toaddr > d->ofst) break; // must span d->ofst + if (m->toaddr <= d->ofst && m->toaddr != m->fromaddr &&m->fromaddr >= d->ofst && m->back) j = m; + cix++; + + + + // next jump ? prev jump? + // if (dix < chjpt.num) j = (JMP*) chjpt.ptrs[dix]; else break; + + } + + // j = (JMP*) chjpt.ptrs[cix]; // candidate jump + + + if (j && j->back) // && j->fromaddr > d->ofst) + { //loop spans this increment. + //new subr ?? + //need to check loop size too, A9L JUMPS to SUBRS.... +#ifdef XDBGX + DBGPRT(1,"Loop? %x (%x-%x)", d->ofst, j->toaddr,j->fromaddr); +#endif + // set up loop holder + memset(&l,0,sizeof(LOOP)); //clear loop struct + l.increg = d->rgvl[1]; + l.incr = d->ainc; + l.start = j->toaddr; // where loop starts + l.end = j->fromaddr; + + dix = ix; + +while (dix && dix < chdtk.num) + { + DTD *b; + n = (TRK*) chdtk.ptrs[dix--]; + + if (n->ofst < l.start-32) break; + if (n->opcsub == 1 && n->rgvl[2] == l.increg) + { + b = get_dtkd(&chdtko, n->ofst, 1); + if (b) { l.datastart = b->stdat; +#ifdef XDBGX + DBGPRT(1,"loop imd %x = %x", n->ofst, b->stdat); +#endif + break;} + } + } + + + +} + +} +/*go back to find start address of designated register.............. +// so this must be FROM ordered.............. + + + +// can use ix here for further loop + // n = (DTK*) chdtk.ptrs[ix]; + + + + + + + while (d->ofst < l.end && ix < chdtk.num) + { //inside loop + ix++; + n = d; + d = (DTK*) chdtk.ptrs[ix]; + if (d->ainc && d->ofst != n->ofst && d->rreg == n->rreg) l.incr += d->ainc; + else + { + if (!l.incr2) l.incr2 = d->start[0]-n->start[0]; + // else if (l.incr2 != d->start[0]-n->start) DBGPRT(1,"inc mismatch"); + } + // DBGPRT(1,"L %x", d->from); + } + + DBGPRT(1,"loop analyse %x-%x for R%x inc %x %x start %x",l.start,l.end, l.increg, l.incr, l.incr2, l.datastart); + // ix--; // to stay at end of loop + +// and HERE is where to find start, end etc conditions ?? +// inc should be FIRST in loop............. +// if jump is STAT then need to find exit condition + } + } + + +*/ +// else DBGPRT(1,"no p"); + + + + if (val_rom_addr(start) && !push && d->opcsub > 1) + { +//ignore reg to reg and immds + //split out add_cmd for ix value fo range checks ? or just do bfindix ?? + + k = add_cmd(start, start+dcmd-1,dcmd, d->ofst); // add data entry + + if (!k) + { +#ifdef XDBGX + DBGPRT(1,"no cmd [%d] (%x %s)", ix, start, cmds[dcmd]); +#endif + } + } + + } // end while 'a' + + +} //master 'for' + + + +} // end of subr + + + + + + + + + +/* +void add_opr_data(INST *c) +{ + OPS *o; + LBK *k; + +// THIS ONE to change to TRACKING data ?? + + + // LBK *k; + int addr, sig; //x + + if (anlpass >= ANLFFS) return; + // if (s->nodata) return; + + + addr = 0; + sig = c->sigix; + + if (sig == 14) return; // not push + + + + --- opcsub == 1 for immediates with valid ROM / RAM address ?? + // how to tell what's real though ? Assume it always calls a indirect ?? + + + if (c->opcsub == 1) + { //ldx or add with immediate only. ALSO check it's not a cmp limit check ? + if (sig == 12 || sig == 6) + { + o = c->opr+1; + + // if (val_rom_addr(o->addr)) addr = o->addr ; + + // if (c->wop == 1) addr = o->addr; + + if (nobank(addr) > PCORG) + { + x = find_last_psw(c->ofst,0); + if (x && sinst.opcix > 33 && sinst.opcix < 36) + { // found a cmp + if (sinst.opr[2].addr == c->opr[2].addr + && sinst.opr[2].val == c->opr[2].val) + { // cmp matches the ldx, i.e. a LIMIT CHECK + addr = 0; + } + } + } + } + } / + + +// indirect but NOT for writes (STX) or zero wops + + if (c->opcsub == 2) + { + o = c->opr+1; + if (c->wop > 1) addr = o->addr; + + // if (c->inc) + // { //struct ? + // DBGPRT(1,"INC++ STRUCT?"); + // } + } + + // indexed + + if (c->opcsub == 3) + { + o = c->opr+4; // = saved opr[1] + + // if (!o->wsize) + // { + // if [0] is a valid ROM address, then use that. + // if R[1] is a valid ROM address with small offset, then use R[1] + // if R[1] is an rbase, use combined address in [1] + + if (c->opr[1].rbs) addr = c->opr[1].addr; // combined address + else + { + addr = c->opr->addr; // address of offset [0] + if (addr >= (PCORG+0x8)) + { + addr = databank(addr,c); // use fixed offset + } + else + { + addr = o->addr; // fixed offset is too small, use register ? + if (addr >= (PCORG+0x8)) addr = databank(addr,c); else addr = 0; + } + } + // } + } + + + if (addr) + { // addr must be val_rom_addr + k = add_cmd (addr, addr+bytes(o->rfend)-1, bytes(o->rfend), c->ofst); // add data entry + // if (k) + // { // add offset and type for later checks + // k->opcsub = c->opcsub; + // if (c->opcsub == 3 && c->opr[4].addr) + // if (c->opr[4].val > 0 && c->opr[4].val < 17) + // k->soff = c->opr[4].val; // offset 0-16 + // } + } + } + + +} +} +} +} +*/ + + + + + + +void turn_scans_into_code (void) + { + + // turn scan list into code commands. + + uint ix; + SBK *s, *t; + + for (ix = 0; ix < chscan.num; ix++) + { + s = (SBK*) chscan.ptrs[ix]; + if (s->inv) + { + // inv block check. if another block overlaps this one. + // may be a run-on due to faulty args or a stack shortcut, etc. + // also for debugs, can add code up to end, if it doesn't overlap with anything + //s->nextaddr will be address AFTER the inv + // ...THIS IS A HORRIBLE KLUDGE !!! but it works... + if (ix < chscan.num-1) + { + t = (SBK*) chscan.ptrs[ix+1]; + if (!t->inv && t->start < s->nextaddr) + { + // next block not invalid and overlaps this one + #ifdef XDBGX + + DBGPRT(1,"Invalid scan %x-%x overlapped by %x-%x",s->start, s->nextaddr,t->start, t->nextaddr); + #endif + add_cmd (s->start,t->nextaddr,C_CODE,0); + s = NULL; //continue; // return to loop + } + } // ix < chscan.num + } // s->inv + else + if (s && (s->stop)) + { // valid block to turn to code, not inv + add_cmd (s->start,s->nextaddr,C_CODE,0); + } + } // for loop + } + + +void end_scan (SBK *s) +{ + SUB *b; + if (anlpass >= ANLPRT || PMANL ) return; + + s->stop = 1; + if (s->nextaddr > s->start) + { + s->nextaddr--; //is now end of block + + b = get_subr(s->substart); + if (b && s->nextaddr > b->end) b->end = s->nextaddr; + } + + #ifdef XDBGX + else DBGPRT(1," Start > End"); + #endif + + + #ifdef XDBGX + DBGPRT(0,"END scan %x at %x", s->start, s->nextaddr); + if (s->inv) DBGPRT (0," Invalid Set"); + DBGPRT(1,0); + #endif + +} + +/************************************* +Multiple scale generic get value. +for varibale bit fields (0-31) +Auto negates via sign (fend & 32) if required +***********************************/ + +int g_val (uint addr, uint fstart, uint fend) +{ // addr is encoded bank | addr + int val, start,stop; + uint mask, sign; + uchar *b; + uchar *t; + + b = map_addr(&addr); + + if (!b) return 0; // map to required bank + t = (uchar*) &val; + + fstart &= 31; // max field + sign = (fend & 32); + fend &= 31; + + if (fstart > fend) return 0; // safety + + stop = fend /8; + start = fstart /8; + + if (fend == fstart) sign = 0; // no sign for single bit + + val = 0; + + while (start <= stop) + { + t[start] = b[addr+start]; + start++; + } + + // do the field extract and sign + + // size = (fend - fstart) + 1; // field width + // mask = 0xffffffff >> (32 - size); // make mask + + + if (fstart) val >>= fstart; // shift down (start bit -> bit 0) + mask = 0xffffffff >> (31 + fstart - fend); // make mask + val &= mask; // and extract required field + + // set sign, reusing mask + + if (sign) + { + mask = ~mask; // flip for set negative + sign = 1 << fend; // sign mask + if (val & sign) val = val | mask; + } + +return val; + +} + + + +int decode_addr(ADT *a, uint ofst) + { + int rb,val,off; + RBT *r; + + // HERE would be where we go to the subfields setup + +// ALSO have to assume that reqd bank is in (top of) data + + val = g_val (ofst, a->fstart, a->fend); // offsets may be SIGNED + + rb = val; + off = 0; + + if (a->foff) + { // fixed offset (assume bank in a->data) + val += a->data; + return val; + } + + switch (a->enc) + { + case 1: // 1 and 3 - only if top bit set + case 3: + if (! (rb & 0x8000)) + { + if (val > (int) max_reg()) val |= g_bank(a->data); + return val; + } + off = val & 0xfff; + rb >>= 12; // top nibble + if (a->enc == 3) rb &= 7; + rb *=2; + break; + + case 2: // 2 and 4 Always decode + off = val & 0xfff; + rb >>= 12; // top nibble + rb &= 0xf; + break; + + case 4: + off = val & 0x1fff; + rb >>= 12; // top nibble + rb &= 0xe; + break; + + default: + break; + } + + if (a->enc) + { + r = get_rbt((a->data & 0xff) + rb, ofst); + if (r) val = off + r->val; // reset op + #ifdef XDBGX + else + DBGPRT(1,"No rbase for address decode! %x (at %x)", a->data+rb, ofst); + #endif + } + + else + if (a->vaddr) + { // value is address + // val |= g_bank(a->data); // add bank from start addr....not necessarily right + if (val > (int) max_reg()) val |= g_bank(a->bank); + // g-bank(a->addr); ? + } + +// if (val > max_reg()) val |= g_bank(a->data); + // if (!numbanks && val > max_reg() ) < 0x400) val = nobank(val) | 0x80000; // force bank 8 for regs + + return val; +} + + +void avcf (SIG *z, SBK *blk) + { + int start, addr, val, bk; + LBK *s; + ADT *c; + + if (!blk->scanning) return; + + addr = z->v[4]; + start = addr; + bk = g_bank(z->start); + #ifdef XDBGX + DBGPRT(1,"in avcf, start = %05x", addr); + #endif + while (1) + { + val = g_word(addr); + val |= bk; + if (!val_rom_addr(val)) break; + add_scan(val, J_SUB,0); + addr -= 2; + } + s = add_cmd (addr, start, C_VECT, z->start); + + if (s) + { + // do bank here...... + c = add_adt(&chadnl, s->start,1); // only one + if (c) { + if (bk == g_bank(addr)) c->cnt = 0; + c->bank = bk >> 16; // always add bank + } + } + + #ifdef XDBGX + DBGPRT(1,"avcf stop at %05x", addr); + #endif + } + + + +uint reg_eq(uint ra, uint rb, uint size) +{ + + if (ra == rb) return 1; // straight match + + if (ra & 1) + { // odd register (in) - check for word ops to match + if ((size & 3) > 1 && (ra - 1) == rb) return 2; + } + +return 0; // no match +} + +int find_list_base(INST *c, uint ix, uint num) +{ + // try to find a list 'base address'. + // expect an add of an immediate value + // allow for ldx to swop registers + + int ans, xofst; + uint reg; + + ans = 0; + reg = c->opr[ix].addr; + xofst = c->ofst; + + while (xofst) + { // look for an add,ad3w or ldx (max 'num' instructions back) + xofst = find_pn_opcodes (0, xofst, num, 2, 6, 12); + + if (xofst) + { //check word ? + if (sinst.sigix == 12 && sinst.opr[2].addr == reg) + { // ldw + if (!sinst.opcsub) // from another register, change register + reg = sinst.opr[1].addr; + if (sinst.opcsub == 1) // immediate + { + ans = sinst.opr[1].addr; // addr is val+databank + if (val_rom_addr(ans)) return ans; + } + } + + if (sinst.sigix == 6 && sinst.opr[sinst.wop].addr == reg) //2].addr == reg) + { // ad2w, ad3w + if (sinst.opcsub == 1) + { // found add for register + ans = sinst.opr[1].addr; // addr is val+databank + if (val_rom_addr(ans)) return ans; + } + } + } + } + // but even if not found, may still have a valid address already in register. + if (xofst) + { + ans = c->opr[ix].val; + if (val_rom_addr(ans)) return ans; + } + else ans = 0; + +return ans; +} + +void match_opnd(uint xofst, OPC *opl, SFIND *f) +{ + OPS *s; + uint m, ix; + + for (ix = 0; ix < 2; ix++) + { // for each possible answer/register + if (val_general_reg(f->rg[ix]) && !f->ans[ix]) + { + + if (opl->sigix == 12) + { // ldw - get operands + do_one_opcode(xofst); + s = sinst.opr + sinst.wop; // where the write op is + + m = reg_eq(f->rg[ix], s->addr,s->wfend); + + if (m) + { // found register [2] = [1] for ldx + if (sinst.opcsub == 1) + { // immediate, look for wop override + f->ans[ix] = sinst.opr[1].addr; //val; + #ifdef XDBGX + DBGPRT(0," Found imm R%x = %x [%x]", f->rg[ix], f->ans[ix], xofst); + #endif + } + else + if (!sinst.opcsub) + { // ldx from another register, change register, but NOT + #ifdef XDBGX + DBGPRT(0," Chg R%x = R%x [%x]", f->rg[ix], sinst.opr[1].addr, xofst); + #endif + f->rg[ix] &= 1; // is it odd ? + f->rg[ix] += sinst.opr[1].addr; // replace with new reg (may be odd) + } // end if match reg + } + + + } //end ldx + else + + if (opl->sigix == 6 && opl->nops == 3) + { // ad3w (ad2w ??) + do_one_opcode(xofst); + + if (sinst.opr[3].addr == f->rg[ix]) + { + if (sinst.opr[2].rbs) + { // ad3w with an rbase. + if (sinst.opcsub == 1) + { // immediate. op[3].val is correct as bank in rbase. + f->ans[ix] = sinst.opr[3].val; + #ifdef XDBGX + DBGPRT(0," ad3w R%x = %x at %x", f->rg[ix], f->ans[ix], xofst); + #endif + } + else + { + // if it's a register, or indirect, then need to find a list base for multiple structs (tabs or funcs) + // this might be widened to non_rbase ?? + // and check spacing for tabs ??? op[4] is orig register, same subr as vects + f->lst[1] = sinst.opr[2].val; // this is rbase address for possible list start + m = find_list_base(&sinst,4,15); + if (m) + { + f->lst[0] = m; + f->ans[ix] = m; // for stopping the search + #ifdef XDBGX + DBGPRT(0," Found LIST R%x -> %x [%x]", f->rg[ix], m, xofst); + #endif + } + else + { + #ifdef XDBGX + DBGPRT(0," ad3w unknown R%x at %x", f->rg[ix], xofst); + #endif + } + } + } + else + { // not rbase + #ifdef XDBGX + DBGPRT(0," ad3w !rbase R%x at %x", f->rg[ix], xofst); + #endif + } + } + } // end ad3w + + // may be other pointer stuff here xdt2 does ad2w + } // end if f->x + } // end for i + } + + + +void set_xfunc(SFIND *f) + { + // set function start and one row. + // extended later with extend_func + // check start value for sign/unsign + + uint startval, val, fendin; + int rowsize; // increment, entry (read) size for g_val + + ADT *a; + LBK *k; + AUTON *nm; + + nm = anames+f->spf; + + #ifdef XDBGX + DBGPRT(1,"Check Func %x, I %x, O %x", f->ans[0], nm->fendin, nm->fendout); + #endif + + + + fendin = nm->fendin; //local as it may change + startval = get_startval(fendin); //cnv[szin].startval; //(~casg[szin]) & camk[szin]; // start value unsigned + + val = g_val(f->ans[0],0, fendin); // read first input value (as unsigned) + val &= get_sizemask(fendin); // cnv[szin].sizemask; //camk[szin]; // and fit to startval + + if (val != startval) // try alternate sign.... + { + fendin ^= 32; // swop sign flag + #ifdef XDBGX + DBGPRT(1,"require Swop sign in - val %x (%x)", val, startval); + #endif + + startval = get_startval(fendin); //cnv[szin].startval; // (~casg[szin]) & camk[szin]; // start value + + if (val != startval) + { + #ifdef XDBGX + DBGPRT(1,"func %x Invalid start value %x, expect %x", f->ans[0], val, startval); + #endif + return ; + } + } + + rowsize = bytes(fendin) + bytes (nm->fendout); + + k = add_cmd (f->ans[0], f->ans[0] + rowsize-1, C_FUNC, 0); + + if (k && !k->cmd) + { // cmd OK, add size etc (2 sets for func) + + new_autosym(3,f->ans[0]); // auto func name, can fail + k->size = rowsize; // size of one row + + a = add_adt(&chadnl,k->start,1); + a->cnt = 1; + a->fend = fendin; // size, word or byte + a->pfw = get_pfwdef(a); + a->prdx = 2; // default decimal print + + a = add_adt(&chadnl,k->start,2); + a->cnt = 1; + a->fend = nm->fendout; // size out + a->pfw = get_pfwdef(a); + a->prdx = 2; // default decimal print + } + + } + + + + + +void fnplu (SIG *s, SBK *blk) + { + // func lookup subroutine + // set up subroputine only - func added later by jump trace + + SUB *subr; + SBK *t; + SPF *f; + int size; + uint i, taddr; + uint xofst, imask, omask, isize, osize; + + if (!s || !blk) return; + if (!blk->scanning) return; + if (!blk->substart) return; + if (!s->v[3]) return ; //sign holder + + subr = add_subr(blk->substart); // ok if already exists + if (!subr) return; + + f = add_spf(subr->start); + if (f->spf) return; // already processed, assume f always OK + + size = s->v[2]/2; // size from sig (unsigned) as BYTES + taddr = s->v[4]; // address register (need +80000 ??) + + + #ifdef XDBGX + DBGPRT(1,"in fnplu for %x", blk->substart); + #endif + + blk->logdata = 0; // no data to collect (it's where sig found) + #ifdef XDBGX + DBGPRT(1,"set nodata %x", blk->start); + #endif + if (blk->start != blk->substart) + { + t = get_scan(blk->substart); + if (t) + { + t->logdata = 0; + #ifdef XDBGX + DBGPRT(1,"set nodata %x", t->start); + #endif + } + } // stop any data additions in func + + size = (size*8) -1; // convert to fend style + + osize = (s->v[10] & 0x1000) ? 32 : 0; // signed out if bit SET, 1000 = signed if NOT set + isize = (s->v[11] & 0x1000) ? 32 : 0; // signed in if bit SET, 1000 = signed if NOT set + osize |= size; + isize |= size; + + omask = (s->v[10] & 0xff); // bit mask value, sign out flag + imask = (s->v[11] & 0xff); // bit mask value, sign in flag + +// look for OR opcodes if not in 'main' subroutine +// also look for changed register for address holder (ldx or stx) + + xofst = blk->substart; + while (xofst) + { + if (xofst >= s->start && xofst <= s->end) break; + + // look for an OR, LDX, STX in 22 opcodes from front of subr + xofst = find_pn_opcodes (1, xofst, 22, 3, 9, 12, 13); + + if (xofst) + { + if (sinst.sigix == 9 && sinst.opr[2].addr == s->v[3]) + { + #ifdef XDBGX + DBGPRT(1,"find OR at %x", sinst.ofst); + #endif + if (omask & sinst.opr[1].val) osize ^= 32; // flip sign out if match; + if (imask & sinst.opr[1].val) isize ^= 32; // flip sign in if match + } + + if (!sinst.opcsub && sinst.sigix == 12 && sinst.opr[2].addr == taddr) + { + // this may also work for args, as they must be loaded via ldx .... + taddr = sinst.opr[1].addr; + } + + xofst++; // skip to next instruction + } + } + + // set function type + + for (i = 5; i < 13; i++) + { + if (anames[i].fendin == isize && anames[i].fendout == osize) + { + new_autosym(i,blk->substart); // add subr name acc. to type + f->spf = i; + f->fendin = isize; + f->fendout = osize; + f->addrreg = taddr; //address + + #ifdef XDBGX + DBGPRT(1,"func spf=%d (%x)", i, blk->substart); + #endif + break; + } + } + } + + + +void set_xtab(SFIND *f) +{ + + // add table from col and rows size. + // can't fully check, do sanity check on sizes + + uint xend; + LBK *k; + ADT *a; + int ofst, rows, cols; + +// probably need a maxend safety here..... and a sign check.... + + ofst = f->ans[0]; + rows = 2; + cols = nobank(f->ans[1]); + + #ifdef XDBGX + DBGPRT(1,"SET TAB Add %x cols %x Rows %x inx %x ss %d", ofst, cols, rows, f->spf,anames[f->spf].fendout); + #endif + + if (cols < 2 || cols > 32) cols = 1; + + xend = ofst; + xend += (rows*cols)-1; + + k = add_cmd(ofst, xend, C_TABLE, 0); + + if (k && !k->cmd) // in case tab by user cmd.... + { + if (PSTCA) k->argl = 1; // arg layout by default + new_autosym(4,ofst); // auto table name, can fail + a = add_adt(&chadnl,k->start,1); // just ONE adt + k->size = cols; // size for one line ( = cols) + a->cnt = cols; + a->prdx = 2; // default decimal print + // cnvsstofld(a, anames[f->spf].sizeout); + + a->fend = anames[f->spf].fendout; // includes sign + a->pfw = get_pfwdef(a); + + } + + // probably need some more checks, but simple version works for now... + +} + + + +void tbplu (SIG *s, SBK *blk) + +{ + // uint adr; + // int cols, rows; + uint xofst, taddr, tcols, osign, omask; + SIG *t; + SUB *subr; + SBK *x; + SPF *f; + + if (!s || !blk) return; + if (!blk->scanning) return; + if (!blk->substart) return; + if (!s->v[3]) return ; + + subr = add_subr(blk->substart); // ok if it already exists + + if (!subr) return; + + #ifdef XDBGX + DBGPRT(0,"In TBPLU "); + #endif + + f = add_spf(subr->start); + if (f->spf) return; // already processed, assume f always OK + + taddr = s->v[4] ; // table address register + tcols = s->v[3] ; // num cols in this register + osign = 0; + omask = 0; + + + // In tab lookup, sign flag is in the INTERPOLATE subroutine + // subr call address saved in s->v[0] so look for tabinterp sig there + + t = get_sig(s->v[0]); + if (!t) t = scan_asigs (s->v[0]); // no, so scan for it + + + + if (t) + { // found tab signed interpolate + #ifdef XDBGX + DBGPRT(1," SIGN Flag = %x", t->v[3]); + #endif + + osign = (t->v[10] & 0x1000) ? 0 : 1; + omask = (t->v[10] & 0xff); + } + + + xofst = blk->substart; + while (xofst) + { + if (xofst >= s->start && xofst <= s->end) break; + if (t && xofst >= t->start && xofst <= t->end) break; // safety checks + // look for an OR, LDX, STX in 22 opcodes from front of subr + xofst = find_pn_opcodes (1, xofst, 22, 3, 9, 12, 13); + + if (xofst) + { + if (t && sinst.sigix == 9 && sinst.opr[2].addr == t->v[3]) + { // found register to match 't' + #ifdef XDBGX + DBGPRT(1,"find OR at %x", sinst.ofst); + #endif + if (omask & sinst.opr[1].val) osign ^= 1; // flip signout to match + } + + if (!sinst.opcsub && sinst.sigix == 12) + { + // ldx reg-reg this may also work for args, as they must be loaded via ldx .... + // opcsub = 0 and set opr[1].addr + if (sinst.opr[2].addr == taddr) taddr = sinst.opr[1].addr; + if (sinst.opr[2].addr == tcols) tcols = sinst.opr[1].addr; + } + xofst++; // skip to next instruction + } + } + + blk->logdata = 0; // no data logging (it's where sig found) + #ifdef XDBGX + DBGPRT(1,"set nodata %x", blk->start); + #endif + + if (blk->start != blk->substart) + { + x = get_scan(blk->substart); + if (x) + { + x->logdata = 0; + #ifdef XDBGX + DBGPRT(1,"set nodata %x (%x)", x->start, x->nextaddr); + #endif + } + } + + // subr is (blk->substart) from above + new_autosym(13 + osign, blk->substart); // add subr name acc. to type + + f->spf = 13 + osign; + f->fendin = 7; //1; + f->fendout = 7; //sizeout = 1; // need better than this ?? + if (osign) f->fendout |= 32; //sizeout |= 8; + f->addrreg = taddr; //reg holding address + f->sizereg = tcols; // reg holding cols + + #ifdef XDBGX + DBGPRT(1,"tab spf=%d (%x)", f->spf, blk->substart); + #endif +} + + +void encdx (SIG *s, SBK *blk) +{ + + int enc, reg; + RST *r; + + if (!blk || !s) return; + if (!blk->emulating) return; + + #ifdef XDBGX + + DBGPRT(0,"in E encdx"); + DBGPRT(0," for R%x (=%x) at %x", s->v[8], g_word(s->v[8]), blk->curaddr); + DBGPRT(1,0); + #endif + + enc = s->ptn->vflag + 2; + if (enc == 4 && s->v[3] == 0xf) enc = 2; // type 2, not 4 + if (enc == 3 && s->v[3] == 0xfe) enc = 1; // type 1, not 3 + + + if (s->v[9]) + { + reg = s->v[9]; + if (s->v[1] == 2) reg = g_word(reg); //is this always indirect ?? + // and may need to create an rgstat..... + } + else reg = s->v[8]; + + reg = nobank(reg); + r = get_rgstat(reg); + + if (r) + { // mark rg status as encoded first + #ifdef XDBGX + DBGPRT(1,"set enc for %x", reg); + #endif + r->enc = enc; + r->fend = 15; // enc is always a word + r->data = nobank(s->v[5]); // 'start of data' base reg + } +} + + + +void ptimep(SIG *x, SBK *blk) +{ + // don't have to have a subr for this to work, so can do as + // a preprocess, but logic works as main process too (but only once) + + + LBK *k; + ADT *a; + uint start; + int val,z; + uint ofst; + + if (!blk->scanning) return; + if (x->done) return ; + + #ifdef XDBGX + DBGPRT(1,"in ptimep"); //move this later + #endif + + x->v[0] = g_word(x->v[1]); + + // size in v[14]. do a 'find end' for the zero terminator + // cmd size (Y or W) is (2 or 4)= byte (3 or 5)=word + + start = databank(x->v[0],0); + z = x->v[14]; + + #ifdef XDBGX + DBGPRT(0,"Timer list %x size %d", x->v[0], z+3); + //if (s) DBGPRT(0," Sub %x", s->addr); + DBGPRT(1,0); + #endif + + val = 1; + a = 0; + + // should also check for next cmd....and put names in here.... + + ofst = start; + + while (ofst < maxpcadd(ofst)) + { + val = g_byte(ofst); + if (!val) break; + ofst += z; + if (val & 1) ofst+=3; else ofst+=1; + } + + k = add_cmd(start, ofst, C_TIMR, 0); // this basic works, need to add symbols ?.... + if (k) a = add_adt(&chadnl,k->start,1); // only one (for now) + if (a) {a->fend = (z*8) -1; a->cnt = 1; +// a->fnam = 1; +} + + x->done = 1; + +} + + + + /* + * for move into auto timer above. + * void set_time(CPS *cmnd) + { + LBK *blk; + int val,type,bank; + int xofst; + short b, m, sn; + SYM *s; + + char *z; + + blk = set_cmd (cmnd->start,cmnd->end,cmnd->fcom,1); + set_adnl(cmnd,blk); + DBGPRT(1,0); + new_sym(2,cmnd->symname,cmnd->start, -1); // safe for null name + + // up to here is same as set_prm...now add names + + if (!blk) return; // command didn't stick + a = blk->addnl; + if (!a) return; // nothing extra to do + sn = 0; // temp flag after SIGN removed !! + if (a->addr) sn = 1; + if (!a->name && !sn) return; // nothing extra to do + + if (a->ssize < 1) a->ssize = 1; // safety + + xofst = cmnd->start; + bank = xofst &= 0xf0000; //cmnd->start >> 16; + while (xofst < maxadd(xofst)) + { + type = g_byte (xofst++); // first byte - flags of time size + if (!type) break; // end of list; + + val = g_val (xofst, a->ssize); // address of counter + val |= bank; + + if (a->name) // && (cmdopts & T) + { + z = nm; + z += sprintf(z, "%s", anames[0].pname); + z += sprintf(z, "%d", anames[0].nval++); + if (type& 0x4) z += sprintf(z,"D"); else z+=sprintf(z,"U"); + if (type&0x20) z += sprintf(z,"_mS"); + else + if (type&0x40) z += sprintf(z,"_8thS"); + else + if (type&0x80) z += sprintf(z,"_S"); + s = new_sym(0,nm,a->addr,type); // add new (read) sym + } + + + xofst+= casz[a->ssize]; // jump over address (word or byte) + + z = nm; + if (type&1) // int entry + { + if (s && sn) + { // add flags word name too + m = g_byte (xofst++); // bit mask + b = 0; + while (!(m&1)) {m /= 2; b++;} // bit number + val = g_byte (xofst++); // address of bit mask + val |= bank; + if (type & 8) z+= sprintf(z,"Stop_"); else z += sprintf(z,"Go_"); + sprintf(z,"%s",s->name); + new_sym(0,nm, val, b); + } + else xofst+=2; // jump over extras + } + } + + + // upd sym to add flags and times + } + + + +*/ + + + + + +int check_sfill(uint addr) +{ + int val, ofst; + // 4 or more repeated values cannot be code.... + + ofst = addr; + val = g_byte(ofst); + for (ofst = 0; ofst < 4; ofst++) + { + if (g_byte(ofst+addr) != val) break; + } + if (ofst < 4) return 0; + + return 1; +} + + + + + +void build_fake_stack(SBK *s) +{ + + // build a fake stack from caller list of s. + // from the stack point of view, a pushp will always be + // Higher (= older) than its call out. + + int i; + // SBK *x; + FSTK *c, *d; + + c = emulstack; + d = scanstack; + + + memset(c, 0, sizeof(emulstack)); + + s = s->caller; + + i = 0; + + while (i < STKSZ && s) + { + // if (!s->popped) + { + c[i].origadd = s->nextaddr; + c[i].newadd = s->nextaddr; + c[i].psw = 0; + c[i].type = 1; + + if (d[i].origadd == c[i].origadd && d[i].popped) c[i].popped = 1; // temp fixup for A9L, seems to work ?? + + + if (s->php) + { + i++; + c[i].type = 4; + c[i].origadd = s->nextaddr; + c[i].newadd = s->nextaddr; + } + } + s = s->caller; + i++; + } + #ifdef XDBGX +DBG_stack(d); +DBG_stack(c); +#endif +} + + + +void emulate_blk(SBK *s) + { + SBK *emu, *x, *y; + + if (!s) return; // safety + if (anlpass >= ANLPRT) return; + + show_prog (anlpass); + + #ifdef XDBGX + DBGPRT(2,0); + DBGPRT(0,"In Emulate"); + DBG_sbk(0,s); + #endif + + // first, copy the call chain from scans into the emuchain + + x = s; + emu = add_escan(0, s); // copy block for emu (this is actual emu) + x = x->caller; // next (in) block + y = emu; // start of emu chain + + while (x) + { + y->caller = add_escan(0, x); // add new copy of scanblock + y = y->caller; // move to new emu chain end + y->curaddr = x->curaddr; // correct spot for subr call + y->nextaddr = x->nextaddr; + x = x->caller; // and around for next scan block/caller + } + + +// now, build a fake stack for emulate. + + build_fake_stack(emu); + + memset(emuargs, 0, sizeof(emuargs)); + + opcnt = 0; // global so it survives + emuscan = 0; + scan_blk(emu, &einst); + + // but if a push(imd) somewhere, then this won't be called + // ....so fiddle it here by calling it LAST, and hope... + + if (emuscan) scan_blk(emuscan, &einst); + + #ifdef XDBGX + DBGPRT(1,"Emulate run STOP %x at %x", emu->start, emu->nextaddr); + if (emu->inv) DBGPRT(1," with Invalid at %x", emu->curaddr); + #endif + + clearchain(&chemul); // clear all emulate scans + +} + + + +int check_backw(uint addr, SBK *caller, int type) +{ +// check if jump backwards for possible loop condition + + int ans, jump; +// OPC *opl; + + ans = 0; // continue + +// basic address stuff first................ + + jump = addr - caller->curaddr; //difference + + if (bankeq(addr, caller->curaddr)) + { // in same bank + if (addr == caller->nextaddr) ans = 1; // dummy jump, ignore for scans + if (addr == caller->curaddr) ans = 3; // loopstop HERE + + // backward jump, scanned already and conditional + if (type == J_COND && jump < 0 && get_opstart(addr)) ans = 2; + + if (type == J_STAT) + { + // check for bank start, safety loopstops and opening jumps. + if (nobank(addr) < 0x2010) end_scan (caller); // END of this block + + if (jump == -1) + { + //some bins do a loopstop with di,jmp............... + if (g_byte(addr) == 0xfa) ans = 3; + } + } + } + + if (ans > 2 && caller->emulating) + { // only check for real loopstops in emulate + #ifdef XDBGX + DBGPRT(1,"Loopstop detected (%d) at %x", ans, caller->curaddr); + #endif + // if (!caller->lscan) + caller->stop = 1; + } + + return ans; +} + + + +void do_signatures(SBK *s) + { + SIG *g; + + if (anlpass >= ANLPRT) return; + + if (get_opstart(s->curaddr)) // code, has this been scanned already ? + { + // g = (SIG*) chmem(&chsig,0); + // g->start = s->curaddr; + +/* +void *find(CHAIN *x, void *b) +{ + // find item with optional extra pars in b + + uint ix; + ix = bfindix(x, b, 0); + if (x->equal(x,ix,b)) return NULL; // no match + return x->ptrs[ix]; +} + +*/ + + + + // g = (SIG*) find(&chsig,g); // Yes, check for sig at this address + + g = get_sig(s->curaddr); + } + else + { + g = scan_sigs(s->curaddr); // scan sig if not code scanned before (faster) + #ifdef XDBGX + DBGnumops++; + #endif + } + + if (g && g->ptn->pr_sig) + { + g->ptn->pr_sig(g,s); // process signature + } + } + +void fix_args(LBK *l, SBK *s) +{ +// check argument sizes and amend +// first draft.....called when args are skipped +// to ensure all processing has been done + + RST *r; + LBK *k; + ADT *a; + int i, size; + + #ifdef XDBGX + DBGPRT(1,"\nFIX args"); + #endif + + a = start_adnl_loop(&chadnl,l->start); + +//if (!a) +//{ +//DBGPRT(1,0); +//} + + for (i = 0; i < EMUGSZ; i++) + { + k = emuargs[i]; // next args command (LBK) + + if (k && k->start == l->start) + { + size = k->size; + if (a) a->fkey = set_adt_fkey(k->start,0); + + while ((a = get_next_adnl(&chadnl,a))) + { // for each level + r = get_rgstat(a->dreg); // find register + + if (r) + { + #ifdef XDBGX + DBGPRT(0,"R%x match %x", r->reg, l->start); + DBGPRT(1," fend rg(%d) ad(%d)", r->fend, a->fend); + #endif + if (r->enc) + { // convert to encoded........... + #ifdef XDBGX + DBGPRT(1," Convert R%x to encoded %d", r->reg,r->enc); + #endif + a->enc = r->enc; + a->data = r->data; + a->data |= basepars.datbnk; + a->fnam = 1; + a->cnt = 1; // as args added in twos anyway + a->fend = 15; // word + } + else + { // not encoded, adnl in twos anyway, simple change + // if rgstat size bigger (word) (use 3 for byte and word) + if ((r->fend & 31) > (a->fend & 31) && a->cnt > 1) + { //byte + a->cnt = 1; + a->fnam = 1; + + a->fend = (r->fend & 15); //no bigger than word + // a->fend |= (r->fend & 32); // ignore sign + + #ifdef XDBGX + DBGPRT(1,"Change R%x size to %d", r->reg, r->fend); + #endif + } + } + + +// add a data item here ?? +// add = decode_addr(a, s->curaddr); no, need separate subr // val with decode if nec. and sign - READS ofst + } + #ifdef XDBGX + else DBGPRT(1,"R%x not found for %x", a->dreg, l->start); + #endif + } + + // sanity check here + if (size != totsize(&chadnl,k->start)) + { + #ifdef XDBGX + DBGPRT(1,"ADDNL SIZE NOT MATCH %d %d- FIX", size, totsize(&chadnl,k->start) ); + #endif + // free_adnl(&chadnl,k); //all chain ?? + // a = add_adt(&chadnl,k,1); + // a->cnt = size; + } + + } + + } /// for emuargs loop + + #ifdef XDBGX +// if (changed) +DBG_rgchain(); + DBGPRT(1,"\nEnd FIX args"); + + #endif + + + +} + +/* +int do_arg_data (int ofst, ADT *a, int pfwo) + { + +// print a SINGLE ITEM from *a, even if a->cnt is set +// done this way for ARGS printouts. + + int pfw, val; + SYM* sym; + + sym = NULL; + pfw = 1; // min field width + + // pfw is still not always right in a struct, especially if names appear. + // also args always needs min fieldwidths, so allow for a->pfw at zero + + if (pfwo) pfw = plist[pfwo]; + else + { + if (a->pfw) + { // NOT FOR ARGS !! + if (a->pfw < cnv[a->ssize].pfwdef) pfw = cnv[a->ssize].pfwdef; // default to preset + else pfw = a->pfw; // user specified + } + } + val = decode_addr(a, ofst); // val with decode if nec. and sign - READS ofst + if (a->fnam) sym = get_sym(0, val,-1,ofst); // READ sym AFTER any decodes - was xval + + if (sym) pstr(0,"%s",sym->name); // pstr(0,"%*s",-pfw,sym->name); -ve fw doesn't work ?? + else + if (a->fdat) + { // float - probably need to extend pfw to make full sense - allow override with X ? + sprtfl((float) val/a->fdat, pfw+4); // add 4 for float part + pstr(0,"%s", nm+64); + } + else + { + if (a->prdx == 0) pstr(0,"%*x", pfw, nobank(val)); // HEX nobank ?? + if (a->prdx == 1) pstr(0,"%*d", pfw, val); // decimal print + if (a->prdx == 2) pbin(val, a->ssize); // binary print, need size + // else + // { // hex prt + // //if (!numbanks) don't KNOW whether this is a value or an address...vaddr + // val = nobank(val); // but NOT for bank addresses ! + // pstr(0,"%*x", pfw, val); + // } + } + return cnv[a->ssize].bytes; //casz[a->ssize]; +} +*/ + + + void add_arg_data(uint start, uint ofst) + { + ADT *a; + uint val, i,sz; +// LBK *k; + + // DBGPRT(1,"XZARG DATA for %x", ofst); + + a = start_adnl_loop(&chadnl,start); // (ADT*) chmem(&chadnl,0); + // a->fid = fid; + // a->seq = 0; + + while ((a = get_next_adnl(&chadnl,a))) + { + for (i = 0; i < a->cnt; i++) // count within each level + { + val = decode_addr(a, ofst); + sz = bytes(a->fend); + + add_cmd (val, val+sz -1, sz, ofst); //change to dtk ??? + ofst += sz; + } + + } + + +} + + + + +void skip_args(SBK *s) + { + LBK *k, *e; + int i; + + k = (LBK*) 1; // to start loop.. + + while (k) + { + k = get_aux_cmd(s->curaddr,C_ARGS); + if (k) + { + fix_args(k,s); + add_arg_data(k->start, s->curaddr); + + s->curaddr += k->size; + #ifdef XDBGX + DBGPRT(1,"SKIP %d args -> %x", k->size, s->curaddr); + #endif + + if (!k->size) + { + #ifdef XDBGX + DBGPRT(1,"ARGSIZE IS ZERO !! %x", s->curaddr); + #endif + s->curaddr ++; + } + + // fix args with new sizes, encode, etc + // cleanup reg status and arg holders + // clear rgargs matching k->start + // clear emuargs matching k->start + + + + for (i = 0; i < EMUGSZ; i++) + { + e = emuargs[i]; + #ifdef XDBGX + if (e) DBGPRT(1,"EMUARGS = %x (%d)", e->start, i); + #endif + if (e && e->start == k->start) + { + #ifdef XDBGX + DBGPRT(1,"drop EMUARG %d = %x", i, e->start); + + + if (e->end != k->end) + + + DBGPRT(1,"size mismatch EMUARG %d = %x", i, e->start); + #endif + + // emuargs[i] = 0; + } + } + } + } + } + + + +void scan_blk(SBK *s, INST *c) + { + // used for both scan AND emulate + if (!s) return; // safety + + show_prog (anlpass); + + // need to check that it's not a faulty filler block...... + + if (!s->stop && check_sfill(s->curaddr)) + { + s->stop = 1; + s->inv = 1; + #ifdef XDBGX + + DBGPRT(1,"Invalid (scan) %05x from %05x",s->start, s->curaddr); + #endif + return; + } + + if (s->stop || s->inv) + { + #ifdef XDBGX + DBGPRT(1,"Ignore Scan %x", s->start); + #endif + return; + } + + #ifdef XDBGX + DBG_sbk(0,s); + #endif + + tscans++; + + // if (s->scnt > 2000) + // { + // s->scanned = 1; + // DBGPRT(0,"too many scans !"); return; + // } + + s->scnt++; // scan count by block + + while (!s->stop && !s->inv) // inv seems to stop loops, but should it be overridden with args detection ? + { + skip_args(s); // check for ARGS command(s) + do_signatures(s); // moved to here + + do_code (s, c); // do opcode + s->curaddr = s->nextaddr; // next opcode address + + if (s->emulating) + { // safety check for code loops + opcnt++; + + if (opcnt > 10000) + { + s->stop = 1; + #ifdef XDBGX + DBGPRT(1,"Hit opcode limit %x %x", s->start, s->curaddr); + #endif + } + } + } + + #ifdef XDBGX + DBGPRT(0,"Stop "); + if (s->scanning) DBGPRT(0,"scan"); + if (s->emulating) DBGPRT(0,"Emu"); + DBGPRT(1," blk %x at %x", s->start, s->nextaddr); + #endif + +} + +/* +void scan_loop(SBK *s, INST *c, uint ofst) + { + // used for emulate of loop, has a stop ofst... + + int lscans = 0; + + if (!s) return; // safety + + #ifdef XDBGX + DBGPRT(0,"Emu"); + DBGPRT(0," Loop"); + DBGPRT(1," blk %x at %x", s->start, s->nextaddr); + #endif + + + + + + show_prog (anlpass); + + s->scnt++; // scan count by block + + while (!s->stop && !s->inv) // inv seems to stop loops, but should it be overridden with args detection ? + { + skip_args(s); // check for ARGS command(s) + + do_code (s, c); // do opcode + s->curaddr = s->nextaddr; // next opcode address + + if (s->curaddr > ofst) break; + + lscans++; + + if (lscans > 2000) // > 10000) + { + s->stop = 1; + #ifdef XDBGX + DBGPRT(1,"Hit opcode limit %x %x", s->start, s->curaddr); + #endif + } + } + + #ifdef XDBGX + DBGPRT(0,"Stop "); + DBGPRT(0,"Emu"); + DBGPRT(1," blk %x at %x", s->start, s->nextaddr); + #endif + +} +*/ + + +void scan_subbs(SBK* caller, INST *c) + { + // scan proc sub branches too see if stack fiddling + // is in branches. Not reqd if already flagged + // ADAPT for twin role as subr-sub or gap check ?? + + SBK *s; + uint ix, num; + + if (!caller) return; + ix = 0; + + num = chsbcn.num; + + while (ix < chsbcn.num) + { + + s = (SBK*) chsbcn.ptrs[ix]; + + if (!s->stop && !s->inv && s->scnt < 10) + { + if (s->substart == caller->substart) + { + #ifdef XDBGX + DBGPRT(1,0); + DBGPRT(0,"Subb Scan %d ", ix); + #endif + scan_blk(s, c); + if (chsbcn.num != num) + { + #ifdef XDBGX + DBGPRT(1,"NEWCH %d %d", chsbcn.num, num); + #endif + ix = -1; // rescan if changed + num = chsbcn.num; + } + } + } + ix++; + } + + clearchain(&chsbcn); // clear all temp subdiv branches + } + + +void fakepop(SBK *s) +{ + // drop newest stack entry - underflow safe + // as push, copy whole stack if necessary, + // less efficient, but better for keeping track + // KEEP last pop in entry zero, which is useful for + // stack shuffling par getters + + FSTK *t; + + if (s->emulating) t = emulstack; else t = scanstack; + + memmove (t, t+1, sizeof (FSTK) * (STKSZ-1)); // shuffle sce down, (over [0]) + memset (t+ (STKSZ-1),0, sizeof(FSTK)); // and clear last entry +} + + +int get_stack(SBK *s, FSTK *t, int types) +{ + // get stack entry, with bit mask of types allowed + int match, ans; + + match = 0; + ans = t->newadd; // default return; + + if (t->type & types) match = 1; // OK + + switch(t->type) + { + + default: + case 1: // std call, default + break; + + case 2: // imd + ans = t->psw; // return address as pushed + break; + + case 4: // pushp addr = psw bank goes in 10-13 + ans = t->psw; // psw + ans |= ((t->origadd >> 6) & 0x3c00); // add callers bank in correct place + break; + } + +if (!match && s->emulating) + { + ans = t->origadd; + + #ifdef XDBGX + DBGPRT(1,"STACK TYPE not match %x != %x at %x", t->type, types, s->curaddr); + DBG_stack(emulstack); + #endif + } + +return ans; +} + + +void fakepush(SBK *caller, int addr, int type) +{ + +// add stack entry - overflow safe +// stack shuffle is less efficient than a pointer, +// but will always have newest PUSH at zero which is neater. +// also right way round for [R22+x] style multibanks +// type 1 = std call, 2=imd, 4 = pushp as bit +// always have newaddr and origaddr as real addresses, so if it is +// used incorrectly for a RET, it will just continue. + + FSTK *t; + + if (!caller) return; + + if (caller->emulating) t = emulstack; + else + { + // don't do immediate pushes in scan mode + if (type == 2) return; + t = scanstack; + } + + memmove (t+1, t, sizeof (FSTK) * (STKSZ-1)); // shuffle stack up + memset(t,0, sizeof(FSTK)); // clear entry[1] + + t->type = type; + t->newadd = caller->nextaddr; + t->origadd = caller->nextaddr; + t->psw = addr; + +} + + +int check_caller_chain(int addr, SBK *caller) + { + SBK * x; + int ans; + + ans = 0; + x = get_scan(addr); //,0); + if (!x) return ans; // no block + + while (caller) + { + if (x == caller) + { + ans = 1; + break; + } + caller = caller->caller; + } + return ans; +} + + +void do_sjsubr(SBK *caller, INST *c, int type) +{ + /* do subroutine or jump + * new scanned block (or found one) may become holder of this block + * for subroutine special funcs (and any args) + */ + + SBK *newsc, *x; // new scan, or temp holder + SUB *sub; + ADT *a; + int addr, saveddbnk; + + if (PMANL) return; // no scans, use default + if (anlpass >= ANLPRT) return; + + addr = c->opr[1].addr; + + + switch (type) // requested scan type + { + + default: + #ifdef XDBGX + DBGPRT(0,"Error at %x unknown type =%d", caller->curaddr,type); + #endif + break; + + case J_RET: // return + + // end block and drop back to caller emulate and scan + if (caller->caller) add_jump(caller, caller->caller->nextaddr, J_RET); + else add_jump (caller, caller->curaddr, J_RET); + + end_scan (caller); // END of this scan block + break; + + + case J_COND: // conditional jump + + /* don't necessarily need to add another scan here as code CAN continue, but + * necessary to get any alternate addresses etc. via parameter saves (e.g. for tables) + * backwards conditionals are probably always local loops + * duplicates screened out by add_scan + */ + add_jump(caller, addr, type); //jumps BEFORE checks (for loopstops) + if (check_backw(addr, caller, type)) break; // backward jump probably a loop + + + + if (PLABEL) new_autosym (2, addr); // add autolabel if reqd + add_scan (addr,type, caller); // where cond continues + break; + + case J_STAT: // static jump + /* backwards static jumps are also probably a code loop as well, but some bins + * have a common 'return' point, so can't assume this. + * also a static jump can go to a subroutine start, as per signed and + * unsigned function and table lookups, and so on. + */ + + add_jump(caller, addr, type); + if (check_backw(addr, caller, type)) break; // backward jump probably a loop + + if (PLABEL) new_autosym (2, addr); // add autolabel if reqd + + end_scan (caller); // END of this block + newsc = add_scan (addr, type, caller); // new or duplicate scan + scan_blk(newsc,c); + break; + + case J_SUB: + + add_jump(caller, addr, type); + + if (check_backw(addr,caller,type)) break; + if (check_caller_chain(addr, caller)) break; + + newsc = add_scan (addr,type,caller); // nsc = new or zero if dupl/invalid + + fakepush(caller, addr,1); // need this for later emulation check + + scan_blk(newsc,c); + + // scan subbranches of this subr for emulation before subr exit + scan_subbs(newsc, c); + + if (newsc) + { + sub = get_subr(newsc->substart); + a = get_adnl(&chadnl,sub->start,1); + if (a) + { // args set by user command no args, just skip + //note that cannot do multi level args in a command................... + add_arg_data(sub->start, caller->nextaddr); + + #ifdef XDBGX + DBGPRT(0,"CMD ARGS "); + #endif + caller->nextaddr += sub->size; //sub->adnl); + } + else + { + if (newsc->args) + { + // this is already in chain somewhere ?? need to ESCAPE and restart + x = get_scan(caller->substart); // this is only true if single level args getter ?? and not if static jump.... + if (x) + { + #ifdef XDBGX + if (!x->emulreqd && x != newsc) + { + DBGPRT(0,"%x - ARGS Set emulreqd for %x (%x)", c->ofst, x->start, x->substart); + DBGPRT(1," new %x (%x) C %x (s%x) %x", newsc->start, newsc->substart, caller->start, caller->substart, caller->curaddr); + } + #endif + x->emulreqd = 1; + } // from substart + + // but if this is in the currrent caller chain, need to go back to it ??? + // find scan doesn't check if scan is in current chain....and just assumes args are in CALLER, + // which is NOT always true........ + } + + + if (newsc->emulreqd) // && !caller->emulating) + { + newsc->caller = caller; // reset for stack rebuild + saveddbnk = basepars.datbnk; + emulate_blk(newsc); // and emulate it + basepars.datbnk = saveddbnk; + } + } + } + #ifdef XDBGX + DBGPRT(1,"Resume at %x",caller->nextaddr); + DBGPRT(1,0); + #endif + fakepop(caller); + + } // end switch + +} + + + +/*********************************************** + * calculate operand parameter number 'ix' + * from 'pc' (normally the register address) + * assumes read size already set in operand cell (OPS) + * sym names and special fixups done in print phase + ************************************************/ + +void op_calc (int pc, int ix, INST *c) + { + OPS *o; + RBT *b; + + o = c->opr+ix; + + o->addr = pc; + o->rbs = 0; + b = 0; + + if (o->rgf) + { // if register (default) + o->addr &= 0xff; // strip bank and safety check + o->addr += basepars.rambnk; // rambank always zero if 8061 + + // for 8065 direct address word, or greater if odd, + // change to bank+1 if currently even bank + + if (P8065 && (o->addr & 1) && bytes(o->rfend) > 1 ) + { // add 100 for even banks only + // if (!is_special_reg(o->addr) && // ALL registers........ + if ((o->addr & 0x100) == 0) o->addr += 0x100; // loop 3 -> 0 ?? + o->addr ^= 1; // drop odd bit, even if no bank change + } + + b = get_rbt(o->addr,c->ofst); + } // end rgf + else o->addr = databank(o->addr,c); // generic add (data) bank + + if (o->imd || o->bit) o->val = pc; // immediate or bit flag, so pc is absolute value + + else o->val = g_val(o->addr,0, o->rfend); // get value from reg address, sized by op + + if (b && c != &einst) + { // don't do rbase if in emu mode + o->rbs = 1; + o->val = b->val; // get value from RBASE entry instead + } + // indirect and indexed modes handled in calc mult_ops (called by do code) +} + +void op_imd (INST *c) +{ + // convert op[1] to imd + OPS *o; + + o = c->opr + 1; + o->imd = 1; + o->rgf = 0; + o->rbs = 0; +// o->sym ?? + if (basepars.rambnk) o->addr -= basepars.rambnk; // remove any register bank + o->val = nobank(o->addr); // and any ROM bank +} + + + +void do_vect_list(INST *c, uint start, uint end) // SBK *caller + { + +// no checks.................. + uint pc, vcall; + LBK *s; + ADT *a; + + if (anlpass >= ANLPRT) return; + + pc = start; + + // would need to do PC + subpars for A9L proper display + + #ifdef XDBGX + DBGPRT(1,"do vect list %x-%x", start,end); + #endif + + for (pc = start; pc < end; pc +=2) + { + vcall = g_word (pc); // address from list + vcall = codebank(vcall, c); // assume always in CODE not databank + if (val_rom_addr(vcall)) + add_scan (vcall, J_SUB, 0); // vect subr (checked inside cadd_scan) + } + + + s = add_cmd (start, end, C_VECT,0); + if (s && basepars.codbnk != basepars.datbnk) + { + // bank goes in ONE addn block. + a = add_adt(&chadnl,s->start,1); + if (a) { + a->bank = basepars.codbnk >> 16; // add current codbank + if (g_bank(vcall) == g_bank(start)) a->cnt = 0; //don't print if match + } + } + #ifdef XDBGX + DBGPRT(1,"END vect list"); + #endif + } + +/****************************************** +attempt to decode vector or vector list from push word +probably need to split off the vect list (for multibanks) +****************************************/ + +int check_vect_list(SBK *caller, uint start, uint sz, INST *c) +{ + uint score; + uint i, addr, lowcadd, lastiadd, end; + SBK *s; + LBK *l; + + // assume bank is correct in start + if (nobank(start) < PCORG) return 0; + + if (sz) end = start + sz; // 0 to sz is an extra pointer sometimes. + else end = start + 0xff; // 128 vectors max (A9L is 0xa4) + + + score = 0; // max score + lowcadd = maxpcadd(c->ofst); // bank max for call addresses - multibank ? + lastiadd = 0; + + // check start against curinst+cnt to see if an extra offset needs to be added ? + //this needs probably more checks + + if (start & 1) start++; // can't start on odd byte, but end should be + if (!(end & 1)) end --; // drop last byte if even + + #ifdef XDBGX + DBGPRT(0,"In check_vect %x sz=%x lc=%x", start, sz, lowcadd); + DBGPRT(1," from %x", c->ofst); + #endif + + for(i = start; i <= end; i+=2) // always word addresses (score > 0?) + { + + if (i >= lowcadd) + { + #ifdef XDBGX + DBGPRT(1,"hit lc_add %x", i); + #endif + break; + } + + s = (SBK*) get_scan (i); // find(&chscan,t); + if (s) + { + #ifdef XDBGX + DBGPRT(1,"hit scan %x", i); + #endif + end = i-1; + break; + } + + l = (LBK *) get_cmd(i,0); //find(&chcode,k); + if (l) + { + #ifdef XDBGX + DBGPRT(1,"hit code %x %s",i,cmds[l->fcom]); + #endif + end = i-1; + break; + } + + +//CHECK VECT !!! + + + + l = (LBK *) get_cmd(i,0); //find(&chdata, k); + if (l && l->fcom > C_VECT) + { // just score this one + score +=2; + #ifdef XDBGX + DBGPRT(1,"hit Data %x=%x",i, l->start); + #endif + } + + + // now check the vect address itself + + // add extra check for end address, as size can be >,<, <= + + addr = g_word (i); + addr = codebank(addr, c); + + if (!val_rom_addr(addr)) + { + #ifdef XDBGX + DBGPRT(1,"iadd %x (%x)",i, addr); + #endif + score +=2; + if (i == start) start +=2; // first address is invalid, move start + + if (!lastiadd) lastiadd = i; // keep last invalid address + else + { + // end = i-1; // not allow 2 invalids + end = lastiadd-1; + break; + } + addr = 0; // remove address + } + + // lowest call address - but this doesn't work for multibanks !! + + if (addr > start && addr < lowcadd) {lowcadd = addr; } // keep lowest found addr + + if (addr && (addr & 0xff) == 0) // suspicious, but possible + { + score ++; // just add 1 + #ifdef XDBGX + DBGPRT(1,"Suspicious %x = %x",i, addr); + #endif + } + // more scores ?? + + } // end main loop + + if (lowcadd < end) end = lowcadd-1; + + + #ifdef XDBGX + DBGPRT(1,"end at %x", end); + DBGPRT(1,"Check_vect %x-%x (%d)|lowc %x|score %d", start, i, i-start, lowcadd,score); + #endif + + + +// add a SIZE check + + if (end - start < 15) + { // is a minimum of 8 pointers + #ifdef XDBGX + DBGPRT (1,"REJECT LIST %x from %x, %d ptrs",start, c->ofst, (end-start)/2); + #endif + return 0; + } + + addr = i-start; + if (addr > 0) + { + score = (score*100)/addr; + #ifdef XDBGX + DBGPRT (0,"FINAL SCORE %d",score); + if (score < 10) DBGPRT (0,"PASS"); else DBGPRT(0,"FAIL"); + DBGPRT(1,0); + #endif + + if (score < 10) do_vect_list(c, start,end); + return 1; + } + return 0; +} + + + + + + +void pp_hdr (int ofst, const char *com, int cnt) //, short pad) +{ + SYM *s; //char *tt; + int sym; + + if (cnt & P_NOSYM) sym = 0; else sym = 1; + cnt &= (P_NOSYM-1); // max count is 255 + + if (!cnt) return; + + pstr(1,0); // always start at a new line for hdr + if (sym) + { // name allowed + s = get_sym (0,ofst,32,ofst); // is there a symbol here ?(int rw, uint add, uint bitno, uint pc) + if (s) + { + p_pad(3); + pstr (1,"%s:", s->name); // output newline and reset column after symbol + } + } + + paddr(ofst,0,pstr); + pstr(0,": "); +// if (!numbanks) pstr (0,"%4x: ", nobank(ofst)); +// else pstr (0,"%05x: ", ofst); old, replaced by paddr + + while (--cnt) pstr (0,"%02x,", g_byte(ofst++)); + pstr (0,"%02x", g_byte(ofst++)); + + if (com) + { + if (gcol > MNEMPOSN) p_pad(gcol+2); else p_pad(MNEMPOSN); + pstr (0,"%s",com); + if (gcol > OPNDPOSN) p_pad(gcol+2); else p_pad(OPNDPOSN); + } + + } + +/************************************* +get next comment line from file +move to combined bank+addr (as single hex) +**************************************/ + +int get_cmnt (CMNT *f, uint flags) +{ + int ans; + int maxlen, rlen; + uint pcz; + char *t; + + if (f->ctext) return 0; + + ans = 0; + rlen = 0; + maxlen = 0; + pcz = 0; + + f->ctext = flbuf; + + if (fldata.fl[4] == NULL || feof(fldata.fl[4])) + { + f->ofst = 0xfffff; // max possible address with max bank + f->ctext = NULL; //(char *) empty; + return 0; // no file, or end of file + } + + while (ans < 1 && fgets (flbuf, 255, fldata.fl[4])) + { + maxlen = strlen(flbuf); // safety check + ans = sscanf (flbuf, "%x%n", &pcz, &rlen); // n is actual no of chars read to %n + } + + if (rlen > maxlen) rlen = maxlen; + + if (ans > 0) + { + uint x; + x = nobank(pcz); + if (!numbanks) pcz = x | basepars.datbnk; //force 9 + else pcz += 0x10000; // otherwise add one to bank + + f->ofst = pcz; + f->ctext = flbuf+rlen; + while (*(f->ctext) == 0x20) (f->ctext)++; // consume any spaces after number + t = f->ctext; + if (*t == '\\' && *(t+1) == 'n') f->newl = 1; // newline at front of cmt + else f->newl = 0; + // f->nflag = flags; + return 1; + } + + f->ctext = NULL; // anything else is dead and ignored... + f->ofst = 0xfffff; // max possible address with max bank + return 0; + +} + +/* incorrectly stacked comments cause filled spaces (cos of print...) + + +SYM* symfromcmt(char *s, int *c, uint ofst, uint *bit) + { + int ans; + uint add, p; + SYM *z; + + if (!isxdigit(*s)) + { + *c = 0; + return 0; // anything other than digit after special char not allowed + } + + *bit = 32; + z = 0; + + ans = sscanf(s,"%x%n:%x%n",&add,c,&p,c); + + if (ans <= 0) return NULL; + + if (ans > 1) *bit = p; + + z = get_sym (0, add, *bit, ofst); + + return z; + } */ + +// print indent line for extra psuedo code lines + +void p_indl (void) +{ + if (gcol) pstr(1,0); + p_pad(PSCEPOSN); + +} + + +int pp_adt (int ofst, ADT *a, int pfwo) + { + +// print a SINGLE ITEM from *a, even if a->cnt is set +// done this way for ARGS printouts. + + int pfw, val; + SYM* sym; + + sym = NULL; + pfw = 1; // min field width + + // pfw is still not always right in a struct, especially if names appear. + // also args always needs min fieldwidths, so allow for a->pfw of zero + + if (pfwo) pfw = plist[pfwo]; + else + { + if (a->pfw) + { // Not set for ARGS, default to preset if too small. + val = get_pfwdef(a); + if (a->pfw < val) pfw = val; + else pfw = a->pfw; + } + } + val = decode_addr(a, ofst); // val with decode if nec. and sign - READS ofst + + + if (a->fnam) sym = get_sym(0, val,32,ofst); // READ sym AFTER any decodes - was xval + if (sym) pstr(0,"%*s",-pfw,sym->name); + else + if (a->flt) + { // float - probably need to extend pfw to make full sense - allow override with X ? + sprtfl((float) val/a->fldat, pfw+4); // add 4 for float part + pstr(0,"%s", nm+64); + } + else + // original is %*d, or RIGHT justified (make this a prt_radix ??) + { + if (a->prdx == 2) + { //if (val >= 0) {pchar(' '); pfw--;} // for negative + //pdec(val, a->ssize); + pstr(0,"%*d", pfw, val); // decimal print, need full width if negative + } + else + { + if (a->foff) val &= get_sizemask(15); // word sized if offset + else val &= get_sizemask(a->fend); + if (a->prdx < 2) pstr(0,"%*x", pfw, val); // HEX default nobank ?? what about addresses ?? + if (a->prdx == 3) pbin(val, a->fend); // binary print, need size. + } + // else + // { // hex prt + // //if (!numbanks) don't KNOW whether this is a value or an address...vaddr + // val = nobank(val); // but NOT for bank addresses ! + // pstr(0,"%*x", pfw, val); + // } + } + return bytes(a->fend); +} + + + +int pp_lev (uint start, int ofst, uint *seq, uint *mitem) +{ + +// ALWAYS stops at a newline. + +// need to add CUTOFF at an end point so it does not overrun +// unless command verifies and fixes size first (better) + +//start indent at plist[1] and go up from there. +// in here is Ok as it always does one row + + uint i, item; + ADT *a; + + i = 0; + item = 0; + + a = start_adnl_loop(&chadnl,start); //(ADT*) chmem(&chadnl,0); + // a->fid = fid; + + a->fkey = set_adt_fkey(start, seq ? *seq : 1); + + while ((a = get_next_adnl(&chadnl,a))) + { + if (i) pstr(0,", "); // true if looping on a + + for (i = 0; i < a->cnt; i++) // count within each level + { + if (i) pstr(0,", "); + if (mitem) item = *mitem; // for column pads + pp_adt(ofst, a, item); // field width is plist[item] + if (mitem) + { + (*mitem)++; + item = *mitem; // for column pads + } + ofst += bytes(a->fend); + } + +//probably need a wrap check here too.............and comment aligner + + + if (a->newl) + { + if (mitem) // all cols + { + *mitem = 1; + item = 1; // restart column pads + } + pchar(','); + break; + } + } // end of while (a) + +// breaks to here for both newline and end. + +//i = 1; +//item = lastpad; + +//while (plist[i] > 0 && i < 32) {item +=plist[i]; i++;} + + +//for (i = 1; i < + + + if (seq) + { + if (a) *seq = a->fkey & 0xff; else *seq = 0; + } + // return size....? + return 0; +} + + + + + + + + + + + + + + + + + + +//********************************* + +uint pp_text (uint ofst, LBK *x) +{ + int cnt; + short i, v; + int xofst; + + xofst = x->end; + cnt = xofst-ofst+1; + + if (cnt > 15) cnt = 15; // was 16, for copyright notice + if (cnt < 1) cnt = 1; + + pp_hdr (ofst, cmds[x->fcom], cnt); + + xofst = ofst; + + p_pad(CMNTPOSN); + pstr(0,"\""); + for (i = cnt; i > 0; i--) + { + v = g_byte (xofst++); + if (v >=0x20 && v <= 0x7f) pchar (v); else pchar('.'); + } + pchar('\"'); + + return ofst+cnt; +} + +//*********************************** +// byte, word, long - change name position +//*********************************** + +uint pp_wdbl (uint ofst, LBK *x) +{ + ADT *a; + SYM *s; //char *tt; + // DIRS *d; + + // local declares for possible override + uint com, fend, pfw, val; + + a = get_adnl(&chadnl,x->start,1); + + com = x->fcom; + // d = dirs + com; + + if (!a) + { // no addnl for pp-lev + fend = 7; // byte, safety. + if (x->fcom < C_TEXT) fend = (x->fcom *8) -1; // safe for byte to long + pfw = 5; //get_pfwdef(15); //cnv[2].pfwdef; // UNS WORD, so that bytes and word values line up + } + else + { + fend = a->fend; + pfw = a->pfw; + } + + + if (((x->end - ofst) +1 ) < bytes(fend)) + { + // can't be right if it overlaps end, FORCE to byte + fend = 7; + com = C_BYTE; + } + + // no symbol print, do at position 2 + pp_hdr (ofst, cmds[com], bytes(fend) | P_NOSYM); + + //if (pfw < cnv[cafw[d->defsze]) pfw = cafw[d->defsze]; // not reqd ?? + + if (a) pp_lev(x->start, ofst, 0, 0); // use addnl block(s) + else + { + // default print HEX + val = g_val (ofst,0, fend); + pstr(0,"%*x", pfw, val); + } + + // add symbol name in source code position + // but this should only be for SINGLE entries ? + + s = get_sym(0, ofst, 32, ofst); //(int rw, uint add, uint bitno, uint pc) + + if (s) + { + p_pad(PSCEPOSN); + pstr (0,"%s", s->name); + } + + ofst += bytes(fend); + + return ofst; +} + + + + +uint pp_vect(uint ofst, LBK *x) +{ + /************************* + * A9l has param data in its vector list + * do a find subr to decide what to print + * ALWAYS have an adnl blk for bank, even if default + * ***************/ + + int val, bytes, bank; + SYM *s; //char *n; + ADT *a; + + s = NULL; + a = get_adnl(&chadnl,x->start,1); + + bytes = 2; // word (for now) + + val = g_word (ofst); //always word + + if (a) bank = a->bank << 16; else bank = g_bank(x->start); + + val |= bank; // add bank to value + + if (get_opstart(val)) + { // valid code address + pp_hdr (ofst, cmds[x->fcom], bytes); + s = get_sym (0, val, 32, val); + } + else + { + if (ofst & 1) bytes = 1; // byte, safety + pp_hdr (ofst, cmds[bytes], bytes); + } + + paddr(val,0,pstr); + + if (s) + { + p_pad(PSCEPOSN); + pstr (0,"%s", s->name); + } + + return ofst+bytes; +} + +// print a line as default + +uint pp_dmy(uint ofst, LBK *x) +{ + return ofst+1; // don't print anything. +} + + +uint pp_dflt (uint ofst, LBK *x) +{ + int i, cnt, sval; // for big blocks of fill..... + + cnt = x->end-ofst+1; + + sval = g_byte(ofst); // start value for repeats + + for (i = 1; i < cnt; ++i) // count equal values + { + if (g_byte(ofst+i) != sval) break; + } + + if (i > 32) // more than 2 rows of fill - compress + { + pstr (1,0); // newline first + paddr (ofst,0,pstr); + pstr (0," -> "); + paddr (ofst+i-1,0, pstr); + pstr (1," = 0x%x ## fill ## ", sval ); + return ofst+i; + } + + if (cnt <= 0 ) cnt = 1; + if (cnt > 16) cnt = 16; // 16 max in a line + + + if (ofst + cnt > x->end) cnt = x->end - ofst +1; + + // if (i >= cnt) + // { + // pp_hdr (ofst, "???", cnt); + // return ofst+cnt; + // } + + if (!x->dflt) //(x->cmd || !x->dflt) + pp_hdr (ofst, cmds[0], cnt); + else + pp_hdr (ofst, "???", cnt); + + return ofst+cnt; +} + +// this works for actual structure - need to sort up/down etc + +uint pp_timer(uint ofst, LBK *x) +{ + int val,cnt; + int xofst; + + SYM *sym; + short bit, i; + ADT *a; + + sym = 0; + a = get_adnl(&chadnl,x->start,1); + + if (!a) a = add_adt(&chadnl,x->start,1); // safety + + xofst = ofst; + + val = g_byte (xofst++); // first byte + + if (!val) + { + pp_hdr (ofst,cmds[C_BYTE],1); // end of list + pstr(0,"# Terminator", val); + return xofst; + } + + // a->fnam = 1; // TEMP TEST !! + + cnt = (val & 1) ? 3 : 1; // long or short entry + pp_hdr (ofst, cmds[C_TIMR], bytes(a->fend) + cnt); + pstr(0,"%2x, ", val); + val = g_val (xofst,0, a->fend); +//val = fix_sbk_addr(val); //2nd entry. + if (a->fnam) sym = get_sym(0, val,32,xofst); // syname AFTER any decodes + if (sym) pstr(0,"%s, ",sym->name); + else pstr(0,"%5x, ",val); + + xofst += bytes(a->fend); + + if (cnt == 3) + { // int entry + i = g_byte (xofst++); + bit = 0; + while (!(i&1) && bit < 16) {i /= 2; bit++;} // LOOP without bit check !! + val = g_byte (xofst); +//val = fix_sbk_addr(val); + if (a->fnam) {sym = get_sym(0, val,bit,xofst); + if (!sym) sym = get_sym(0,val,32,xofst); } + if (sym) pstr(0,"%s, ",sym->name); + else pstr(0," B%d_%x",bit, val); + // xofst++; // more later ?? + + } + + return ofst+cnt+bytes(a->fend); +} + + + +void p_op (OPS *o) +{ + // bare single operand - print type by flag markers + + if (o->imd) pstr (0,"%x", o->val & get_sizemask(o->rfend)); // immediate (with size) + if (o->rgf) pstr (0,"R%x",nobank(o->addr)); // register + if (o->inc) pstr (0,"++"); // autoincrement + if (o->bit) pstr (0,"B%d",o->val); // bit value + if (o->adr) paddr(o->addr,0,pstr); // address - jumps + if (o->off) pstr (0,"+%x", o->addr & get_sizemask(o->rfend)); // address, as raw offset (indexed) + +} + + +void p_oper (INST *c, int ix) +{ + // bare op print by instance and index - top level + // if indexed op. append "+op[0]" with op[1] + + OPS *o; + + ix &= 0x3; // safety check + + o = c->opr+ix; + + if (o->bkt) pchar ('['); + p_op (o); + if (ix == 1 && c->opcsub == 3) p_op (c->opr); + if (o->bkt) pchar (']'); + return; +} + + +void p_sc (INST *x, int ix, int ws) +{ +// single op print for source code for supplied instance index +// note that cannot use same print as bits, bitno and reg stored +// in separate ops + + + int v; + OPS *o; + SYM *s; + o = x->opr + ix; + s = 0; + + + if (o->sym) + { // if symbol was set/found earlier + s = get_sym (ws, o->addr, o->bit ? o->val : 32, x->ofst); + + if (s) + { + if (o->off) pchar('+'); // if offset with symname + + if (s->prtbit) pstr (0,"B%d_", o->val); // default 'base' name + pstr(0,"%s", s->name); + return; + } + } + + if (o->imd) // immediate operand + { + pstr(0,"%x", o->val & get_sizemask(o->rfend)); + return; + } + + if (o->rgf) + { + // if (!zero_reg(o->addr) || ws) pstr (0,"R"); // non zero reg, or write op + + if (!zero_reg(o) || ws) pstr (0,"R"); // non zero reg, or write op + + pstr (0,"%x", nobank(o->addr)); + if (o->inc) pstr (0,"++"); + return; + } + + if (o->bit) + { + pstr (0,"B%d_", o->val); + return; + } + + if (o->adr) + { + paddr(o->addr,0,pstr); + return; + } + +if (o->off) + { //offset address - signed + v = o->addr; + // pchar(0x20); + if (v < 0) + { + v = -v; + pchar('-'); + } + else pchar('+'); + // pchar(0x20); + // may need more smarts here + // if single bank or < register + if (!numbanks || nobank (v) < 0x400) v &= get_sizemask(15); + // can't do paddr here..............but have to............ + // pstr (0,"%x", v); + paddr(v,0,pstr); + return; + } +} + +void fix_sym_names(const char **, INST *); + + + + +int find_last_psw(INST *c, int syms) + { + // finds last PSW changing opcode for printout, always print phase + int ans, cnt,chkclc; + uint ofst; + OPC *opl; + PSW *p; + + ans = 0; + cnt = 0; + ofst = c->ofst; //from current instance + + // is a psw setter defined by user ?? + p = get_psw(c->ofst); + if (p) + { + ans = 1; // found !! + ofst = p->pswop; + } + + /// ignore clc,stc as psw setters unless JLEU, JGTU, JC, JNC + chkclc = 0; + if (c->opcix > 61 && c->opcix < 64) chkclc = 1; // JLEU/JGTU + if (c->opcix > 55 && c->opcix < 58) chkclc = 1; // JC/JNC + + // This is used by carry and ovf as well + + // what about a popp ?? + + while (!ans) + { + ofst = find_opcode(0, ofst, &opl); + + if (cnt > 32) break; + if (!ofst) break; + + if (opl->sigix == 17) + { + // CALL - find jump and go to end of subr + JMP *j; + SUB *sub; + + j = get_fjump(ofst); //(JMP*) chmem(&chjpf,0); + // j->fromaddr = ofst; + // j = (JMP*) find(&chjpf, j); + + if (j && j->jtype == J_SUB) + { + sub = get_subr(j->toaddr); + if (sub) ofst = sub->end; else break; + } + else break; + } + + if (opl->sigix == 20) break; // ret + + +// must check for rets too !! +// can't go back through rets, or stat jumps ?? + + + + + + + if (opl->pswc) + { // FOUND PSW setter, if set/clear carry, check flag. + if (opl->sigix == 21) + { + if (chkclc) ans = 2; + } + else + { + ans = 1; + } + } + cnt++; + } + + if (ans) + { + do_one_opcode(ofst); + if (syms) fix_sym_names(0,&sinst); + } + else + { + clr_inst(&sinst); + } + + return ans; + } + + +void p_opsc (INST *x, int ix, int ws) +{ + // source code printout - include sign and size markers. + // ws = if set, use write size (equals not printed yet) + // ix+0x10 flag specifies extra sizes + + OPS *o; + int i; + if (!x) + { + pchar('0'); + return; + } + + if (ix >= 0x11 && ix <= 0x13) + { // extra size/sign requested + i = ix & 3; // safety + if (i == 1 && x->opcsub > 1) i = 0; + if (x->opr[i].sym) i = 0; + if (!x->opr[i].rgf) i = 0; + + if (i) + { + if (ws) i = x->opr[i].wfend; else i = x->opr[i].rfend; //(x->opr[i].wsize && ws) + // after an equals, always use read size (ws = 0) + if (i & 32) pstr(0,"s"); + i &= 31; + if (i < 8) pstr(0,"y"); + else if (i < 16) pstr(0,"w"); + else if (i < 24) pstr(0,"t"); + //if (i == 4) + else pstr(0,"l"); + } + } + + ix &= 3; + o = x->opr + ix; // set operand + +//check if index is to a register + if (ix == 1 && x->opcsub == 3 && nobank(o->addr) <= max_reg() && !o->rgf) + { // present as register - seems to work OK + o->bkt = 0; + o->rgf = 1; + p_sc (x, ix, ws); + return; + } + +// push check, turn immediate into address printout +//probably need better check here, to see if 'codebank(addr)' is a code command + + if (ix == 1 && o->imd && x->sigix == 14) + { // immediate - assume current code bank for printout + // if valid address, and change print to address + o->addr = codebank(o->addr,x); + if (val_rom_addr(o->addr)) + { + o->imd = 0; + o->adr = 1; + o->sym = 1; //look for symname too + } + } + + if (o->bkt) pchar ('['); + p_sc (x, ix, ws); + if (ix == 1 && x->opcsub == 3) p_sc (x, 0,ws); // indexed, extra op + if (o->bkt) pchar (']'); + return; +} + +void cmtwrapcheck(char *c) +{ + char *t; + uint col; + // check if next 'word' would wrap after a space + + if (*c != ' ' && *c != ',') return; // not at space + + t = c+1; // next char + while (*t) + { + if (*t == ' ' || *t == ',' || *t == '\n' || *t == '\r' ) break; + t++; + } + +// but this could be end of line and still overlap.... + if (!(*t)) return; // no space + col = t - c; + if (col > 40) return; // safety .... + col += gcol; // where next 'word' would end + + if (col >= WRAPPOSN) + { + wrap(); + } +} + +/* +char * do_seq(char *c,CMNT *m) +{ +int ans, rs, bit,col,chars,charx, go; +uint ofst, addr; +SYM *x; + + // case '\\' : // special sequences + c++; // skip the backslash, process next char. + + rs = toupper(*c); + switch (rs) + { // special sequences + + default: + pchar(*c); // print anything else + break; + +// more opcode/operand options ?? + + case 0x30: //allow zero - NOT TESTED !! + case 0x31: + case 0x32: // for user embedded operands 1-3 + case 0x33: + + bit = *c - 0x30; // char to number + if (bit >= 1 && bit <= 3) + { + p_opsc(m->minst,bit,0); + //c++; done below.......... + } + break; + + case 'n' : // newline + pstr(1,0); + break; + + // case 'P' : // 'P' symname with padding, drop through + // case 'p' : + case 'S' : // 'S' symname with no padding + case 's': + col = gcol; // where print is now. + // if (*c == 'P' || *c == 'p') col+=21; // pad 21 chars from here (deprecated) + c++; //next char + chars = 0; + rs = 0; // read sym by default + ofst = m->ofst; + x = NULL; + + if (*c == 'w' || *c == 'W') {rs = 1; c++;} // write sym + if (isxdigit(*c)) + { // must be hex digit + bit = 32; + ans = sscanf(c,"%x%n",&addr,&chars); //main symbol + + if (ans > 0) c+=chars; else chars = 0; // valid read + + if (chars) + { // bit. this can fail + ans = sscanf(c,":%x%n",&bit,&charx); + // check bit valid ?? + if (ans > 0) c+=charx; // valid read + + // range. this can fail + ans = sscanf(c,"@%x%n",&rs,&charx); + if (ans > 0) + { + if (val_rom_addr(rs)) ofst = rs; + c += charx; + } + + if (chars) x = get_sym (rs, addr, bit, ofst); + } + + + if (x) + { + if (bit < 32 && x->prtbit) pstr(0,"B%d_",bit); // not right for WORDS ? + pstr(0,"%s",x->name); + if (col > gcol) p_pad(col); // pad to col (from above) + } + } + else + { pstr(0,"??"); // failed sym read, print ??? and sequence ??for Bx_Rn dsiplay + //if (ans > 1 && bit < 32) pstr(0,"B%d_",bit); + //if (ans > 0) pstr (0,"%x ", addr); + } // / + + +//if field read, print ?? + + c = c+chars-1; // skip the sequence + whatever read + break; + + case 'T' : + case 't' : + // tab to 'x' + c++; //next char + chars = 0; + + if (isxdigit(*c)) + { // must be digit + ans = sscanf(c,"%u%n",&addr,&chars); + if (ans > 0) { if (addr < WRAPPOSN) p_pad(addr); else wrap();} + } + c = c+chars-1; // skip the sequence + whatever read + break; + + case 'R' : // runout + case 'r' : + + c++; + chars = 0; + rs = ' '; + + if (!isdigit(*c)) + { + rs = *c; + c++; + } + + // runour of spaces only + ans = sscanf(c,"%u%n",&addr,&chars); + if (ans <= 0) addr =1; + p_run(rs, addr); + c = c+addr-1; // skip the sequence + whatever read + break; + + case 'M' : //next tab posn + case 'm' : + chars = gcol; //current col + chars = gcol/4 * 4; //nearest prev tab + chars += 4; + p_pad(chars); + break; + + + case '\n' : // '\n', ignore it + case '\r' : + break; + + case 'w' : // wrap comment, or pad to comment column + case 'W' : + // allow wrap to to other columns ? + if (lastpad >= CMNTPOSN) wrap(); + else p_pad(CMNTPOSN); + break; + + case '|' : // multiple comment lines ? this seems to work............ + if (m->nflag & 2) go = 0; + break; + + } // end switch for '\' sequences + +} + + +*/ + + + + + +void parse_comment(CMNT *m, uint flags) +{ + // process any special chars in a comment line + + char *c; //, *n; + int go, rs, chars, charx, ans; + uint addr, col, bit, ofst; + SYM *x; + + if (!m->ctext) return; // safety + + c = m->ctext; + + if (!*c) return; //empty + + // if no special char at front, pad to cmt column + if (*c != '\\' ) p_pad(CMNTPOSN); + + go = 1; + while (*c && go) + { + switch (*c) + { // top level char check + + default: + pchar(*c); // print anything else - auto wraps + cmtwrapcheck(c); // wrap check for whole word. + break; + + case 1: // for autocomment embedded - NOTE not chars !! + case 2: + case 3: + p_opsc (m->minst,*c,0); + break; + + case '\n' : // '\n', ignore it + case '\r' : + break; + + + case '\\' : // special sequences + c++; // skip the backslash, process next char. + + rs = toupper(*c); + switch (rs) + { // special sequences + + default: + pchar(*c); // print anything else + break; + +// more opcode/operand options ?? + + case 0x30: //allow zero - NOT TESTED !! + case 0x31: + case 0x32: // for user embedded operands 1-3 + case 0x33: + + bit = *c - 0x30; // char to number + if (bit >= 1 && bit <= 3) + { + p_opsc(m->minst,bit,0); + //c++; done below.......... + } + break; + + case 'N' : + pstr(1,0); + break; + + // case 'P' : // 'P' symname with padding, drop through + // case 'p' : + + case 'S' : // 'S' symname with no padding + // case 's': + col = gcol; // where print is now. + // if (*c == 'P' || *c == 'p') col+=21; // pad 21 chars from here (deprecated) + c++; //next char + chars = 0; + rs = 0; // read sym by default + ofst = m->ofst; + x = NULL; + + if (toupper(*c) == 'W') {rs = 1; c++;} // write sym + if (isxdigit(*c)) + { // must be hex digit + bit = 32; + ans = sscanf(c,"%x%n",&addr,&chars); //main symbol + + if (ans > 0) c+=chars; else chars = 0; // valid read + + if (chars) + { // bit. this can fail + ans = sscanf(c,":%x%n",&bit,&charx); + // check bit valid ?? + if (ans > 0) c+=charx; // valid read + + // range. this can fail + ans = sscanf(c,"@%x%n",&ofst,&charx); + if (ans > 0) + { + if (!val_rom_addr(ofst)) ofst = m->ofst; + c += charx; + } + + x = get_sym (rs, addr, bit, ofst); + } + + + if (x) + { + if (bit < 32 && x->prtbit) pstr(0,"B%d_",bit); // not right for WORDS ? + pstr(0,"%s",x->name); + if (col > gcol) p_pad(col); // pad to col (from above) + } + } + else + { pstr(0,"??"); // failed sym read, print ??? and sequence ??for Bx_Rn dsiplay + //if (ans > 1 && bit < 32) pstr(0,"B%d_",bit); + //if (ans > 0) pstr (0,"%x ", addr); + } // + + +//if field read, print ?? + + c--; //= c+chars-1; // skip the sequence + whatever read + break; + + case 'T' : + // case 't' : + // tab to 'x' + c++; //next char + chars = 0; + + if (isxdigit(*c)) + { // must be digit + ans = sscanf(c,"%u%n",&addr,&chars); + if (ans > 0) { if (addr < WRAPPOSN) p_pad(addr); else wrap();} + } + c = c+chars-1; // skip the sequence + whatever read + break; + + case 'R' : // runout + c++; + chars = 0; + rs = ' '; //space is default + + if (!isdigit(*c)) + { + rs = *c; //new runout char + c++; + } + ans = sscanf(c,"%u%n",&addr,&chars); + if (ans <= 0) addr =1; + p_run(rs, addr); + c = c+chars-1; // skip the sequence + whatever read + break; + + case 'M' : //next tab posn + // chars = gcol; //current col + chars = gcol/4 * 4; //nearest prev tab + chars += 4; + p_pad(chars); + break; + + + case '\n' : // '\n', ignore it + case '\r' : + break; + + + case 'W' : // wrap comment, or pad to comment column + if (lastpad >= CMNTPOSN) wrap(); + else p_pad(CMNTPOSN); + break; + + case '|' : // multiple comment lines ? this seems to work............ + if (flags & 2) go = 0; + break; + + } // end switch for '\' sequences + + + +// case '[' : +// new sequences probably SWAP to different reader ?? + + + + + + + } // end switch for char + if (c) c++; + } + +// save char position if in opcode, else clear string + + if ((flags & 2) && *c) m->ctext = c; + else m->ctext = NULL; // clear string for next cmt +} + +void pp_comment (uint ofst, uint flags) +{ + // ofst is the START of an opcode statement so comments + // less than ofst should be printed. + + if (anlpass < ANLPRT) return; // safety check + + + // if (flags&1) set, then add a newline. + // if (flags&2) set, stop on a '|' char (split comment for bit names) + // BUT newline should be AFTER trailing comments (i.e. not beginning with newline) + // but BEFORE ones that do. Therefore, if end set, break at first cmnt with newline at front + // and remember if newline printed so multiple 'end' set doesn't cause multiple newlines + // clear flag if end not set + + while (ofst > fcmnt.ofst) + { + if ((flags & 1) && fcmnt.newl) break; // do on NEXT call + parse_comment(&fcmnt,flags); //prints it; + if (!get_cmnt(&fcmnt,flags)) break; + } + + if (flags & 1) + { + if (!lastcmt) + { + pstr(1,0); + lastcmt = 1; // remember + } + } + else lastcmt = 0; //clear if not end +} + + + +int maxsizes(LBK *x) +{ + // return max fieldwidth of a segment between newlines + // currently for symbols, but need it for floats too. + + ADT *a; + SYM *sym; + int i,item; + uint ofst, end, val,sz; + + ofst = x->start; + memset(&plist,0,sizeof(plist)); // zero the whole thing + + if (x->argl) return 0; + end = x->end - x->term ; // looks OK + + while (ofst < end) + { + + sz = 0; + item = 1; + +// do this for every row (if struct), but only once for tabs and funcs + + //a = (ADT*) chmem(&chadnl,0); + //a->fid = x; + + a = start_adnl_loop(&chadnl,x->start); + while ((a = get_next_adnl(&chadnl,a))) + { + sz += cellsize(a); + + // now try to work out widths -not only symsize, but floats as well ? + + for (i = 0; i < a->cnt; i++) // count within each level + { + sym = 0; + if (a->fnam) + { + val = decode_addr(a, ofst); // val with decode if nec. and sign - READS ofst + sym = get_sym(0, val,32,ofst); // READ sym AFTER any decodes + } + if (sym) + { + val = sym->tsize -1; + if (val > plist[item]) plist[item] = val; + } + else + { + // check pfw in HERE !! + if (a->pfw) val = a->pfw; + else val = get_pfwdef(a); + if (a->flt) val+= 4; // floating point + if (val > plist[item]) plist[item] = val; + } + item++; + ofst += bytes(a->fend); + } + +// this to line up ALL columns even with split lines + if (a->newl) + { + if (sz > plist[0]) plist[0] = sz; // for header + sz = 0; // for next segment + item = 1; // restart items + } + + } + + if (sz > plist[0]) plist[0] = sz; // for last (or only) segment + + if (x->fcom != C_STCT) break; //only need one pass unless struct + + } + + // now add up for pad position. + // ADD a comment column here ? + + plist[0] = (plist[0]*3) + 7; //first pad posn, allow for addr + if (numbanks) plist[0]++; // for exta address digit + + if (plist[0] < MNEMPOSN) plist[0] = MNEMPOSN; //minimum at mnemonic + + for (i = 0; i <= item; i++) + { + plist[31] += plist[i]+2; // space and comma for each item + } + +plist[31] += 5; // for cmnd string + return item; +} + + +int pp_cmpl(uint ofst, uint start, int fcom) +{ + // print 'compact' layout, all args on one line + // unless | char, when it's split + + uint tsize, size, end, seq, itn; + + tsize = totsize(&chadnl,start); + end = ofst + tsize; + +// this is stct format, need a different one for subr (with args afterwards) + seq = 0; // new query + itn = 1; + + while (ofst < end) + { //for newline split............ + size = listsize(&chadnl,start,seq); + + if (fcom != C_ARGS) + { + pp_hdr (ofst,0,size); // no title + p_pad(plist[0]); + pstr (0,"%s", cmds[fcom]); + if (gcol < OPNDPOSN) p_pad(OPNDPOSN); else p_pad(gcol+2); + pp_lev(start,ofst,&seq, &itn); // continue from newline + ofst += size; + if (gcol >= CMNTPOSN) p_pad(plist[31]); + if (ofst < end) pp_comment (ofst,0); // NOT last item + } + else + { // arguments for subr on same line, no split + pp_lev(start,ofst,&seq, 0); + ofst += size; + } + } + + if (!tsize) return 1; + + return tsize; +} + + + +int pp_argl (uint ofst, uint start, int fcom, int *argno) +{ + +// Print 'argument' layout, 1 arg per line +// can get SUB or LBK as fid + +int i; +uint tend; +ADT *a; + + pp_comment (ofst,0); //for any comments BEFORE args + + tend = ofst + totsize(&chadnl,start)-1; // where this 'row' ends + i = 0; + a = start_adnl_loop(&chadnl, start); //(ADT*) chmem(&chadnl,0); +// a->fid = fid; + + while ((a = get_next_adnl(&chadnl,a))) + { + for (i = 0; i < a->cnt; i++) // count within each level + { + if (fcom == C_ARGS) sprintf(nm, " #%s %d", "arg", (*argno)++); + else sprintf(nm, " #%s %d", cmds[fcom], (*argno)++); + + pp_hdr(ofst, nm, bytes(a->fend)); + p_pad(PSCEPOSN+3); + a->pfw = 0; // no print field in argl + pp_adt(ofst, a,0); + + ofst += bytes(a->fend); + if (!bytes(a->fend)) ofst++; // safety + + if (ofst <= tend) + { + pchar(','); + pp_comment (ofst,0); + } + } + + if (a->newl) break; //this breaks seq no ?? + } + + // return size....? + return ofst; +} + + + +int pp_subargs (INST *c, int ofst) +{ + // ofst is effectively nextaddr + + // change pp-lev for pp_args to do + // a line by line print of attached arguments + + SUB *xsub; + LBK *k; + int size, xofst, argno,postprt; + + size = 0; + argno = 1; + postprt = 0; + + if (PSSRC) pstr(0," ("); // args open bracket + + xofst = ofst; + + while(1) //look for args first.........in a loop + { + k = get_aux_cmd(xofst, C_ARGS); + + if (!k || !k->size) break; + if (PSSRC && xofst > ofst) pchar(','); + if (PSSRC && k->size) + { + if (k->cptl) + { + pp_cmpl(xofst,k->start, k->fcom); + postprt = 1; + } + else pp_argl(xofst, k->start, k->fcom, &argno); + } + xofst += k->size; // look for next args + size += k->size; + } + + if (!size) + { + // didn't find any ARGS commands - look for subr arguments + + xsub = get_subr(c->opr[1].addr); + + if (xsub) + { + size = xsub->size; + if (PSSRC && size) + { + if (xsub->cptl) + { + pp_cmpl(xofst,xsub->start, C_ARGS); + postprt = 1; + } + else pp_argl(xofst, xsub->start, C_ARGS, &argno); + } + } + } + + if (PSSRC && size) pchar(' '); // add space if args + if (PSSRC) pstr(0,");"); // args close bracket + + if (size) + { + pp_comment (ofst,0); // do any comment before next args print + if (postprt) pp_hdr (ofst, "#args ", size); // do args as sep line if compact + } + + return size; +} +// print subroutine params - simpler version +//func, table, struct go here + +uint pp_stct (uint ofst, LBK *x) +{ + uint size, end; + int argno; + + +//if (x->start == 0x9c0f8) +// { +//DBGPRT(1,0); +//} + + + + end = ofst + x->size; + argno = 1; // can't reset argno !! + + if (end == ofst) end++; // safety + + if (ofst == x->start) maxsizes(x); // do only once. + + size = x->end - x->term + 1; // where terminator starts + + if (x->term && end > size) + { // print terminator + pp_hdr (ofst, 0, x->term); + p_pad(OPNDPOSN); + pstr(0,"## terminator"); + ofst += x->term; + return ofst; + } + + while (ofst < end) + { + if (x->argl) + { + ofst = pp_argl(ofst,x->start,x->fcom,&argno); + pp_comment (ofst, 1); // last item + pstr(1,0); + } + else + { + ofst += pp_cmpl(ofst,x->start,x->fcom); + if (gcol >= CMNTPOSN) p_pad(plist[31]); //before comments..... + } + } + + return ofst; +} + +void shuffle_ops(const char **tx, INST *c, int ix) +{ + // shuffle ops down by one [1]=[2],[2]=[3] acc. ix + // change pseudo code to ldx string "[2] = [1]" + + c->opr[4] = c->opr[ix]; // save original + while (ix < 3) + { + c->opr[ix] = c->opr[ix+1]; + ix++; + } + if (tx) *tx = scespec[2]; //opctbl[LDWIX].sce; +} + + + + + + + +void fix_sym_names(const char **tx, INST *c) +{ + // adjust operands for correct SOURCE code printout, rbases, indexes etc. + // This CHANGES addresses for indexed and RBS for printout purposes + + OPS *b, *o; + RBT *r; + SYM *s; + int i,w; + int addr; + const OPC* opl; + + opl = opctbl + c->opcix; + + // get and flag all sym names FIRST, then can drop them out later as reqd + + for (i = 1; i <= opl->nops; i++) + { + o = c->opr+i; + o->sym = 0; // no sym + s = NULL; + if (i == c->wop) w = 1; else w = 0; + + s = get_sym(w, o->addr,32, c->ofst); // write sym or read sym + if (s) + { + o->sym = 1; // symbol found + } + } + + if (c->opr[3].bit) // bit jump - if bitname, drop register name + { + o = c->opr+2; + b = c->opr+3; // b = bit number entry, get bit/flag name + b->sym = 0; // clear bit name flag + s = get_sym (0, o->addr, b->val, c->ofst); // read sym + if (s) + { + o->rgf = 0; + o->sym = 0; + b->sym = 1; + b->addr = o->addr; + } + } + + // Shifts, drop sym if immediate + if (c->opcix < 10 && c->opr[1].val < 16) c->opr[1].sym = 0; + + // indexed - check if RBS, or zero base (equiv of = [imd]) + + if (c->opcsub == 3) //indexed, use addr in cell [0] first + { + b = c->opr; // probably redundant, but safety - cell [0] + o = c->opr+1; + if (c->wop == 1) w = 1; else w = 0; + if ((o->addr&0x3ff) < 2) o->val = 0; // safety + + //if (zero_reg(o->addr) || o->rbs) + if (zero_reg(o) || o->rbs) + { + // Rbase+off or R0+off + // merge the two operands to fixed addr and drop register operand + // can't rely on o->val always being correct (pop screws it up...) so reset here + + if (o->rbs) + { // rbase - get true value from rb chain + r = get_rbt(o->addr, c->ofst); + o->val = r->val; // but already done ? + } + + addr = c->opr->addr + o->val; // addr may be NEGATIVE at this point !! + if (addr < 0) addr = -addr; // effective wrap around ? never true....... + +// o->addr = o->val + c->opr->addr; // add [0] to get true address + o->addr = databank(addr, c); // need bank for index addition + + o->rgf = 0; + o->sym = 0; + o->adr = 1; // [o] is now an address + b->off = 0; // no following offset reqd + + s = get_sym(w, o->addr,32,c->ofst); // and sym for true inx address + + if (s) //symbol found at (true) addr + { + o->bkt = 0; // drop brackets (from [1+0]) + o->sym = 1; // mark symbol found + } + + } // end merge (rbs or zero) + else + { + // this is an [Rx + offset]. Sort op[0] symbol if not a small offset + if (!is_special_reg(b->addr) && !b->neg) + { + // not negative, not reserved register, add databank if > PCORG + + s = get_sym(0, databank(b->addr,c),32,c->ofst); + if (s) + { + if (nobank(b->addr) > PCORG || s->immok) b->sym = 1; + } + if (numbanks) b->addr = databank(b->addr,c); // do bank for printout + } + } + } // end inx + + if (c->opcsub == 1) // immediate - check opcodes etc. for removing symbol names + { + // not small values (register names ??) + if (nobank(c->opr[1].addr) < max_reg()) c->opr[1].sym = 0; // not (normally) names for registers + // sub ?? + // not 5 and, 8 mult, 9 or,xor, 10 cmp, 11 div - all must be an abs value + // ldx 12 with rbase - no symname + else if (opl->sigix == 5) c->opr[1].sym = 0; + else if (opl->sigix > 7 && opl->sigix < 12) c->opr[1].sym = 0; + else if (opl->sigix == 12 && c->opr[2].rbs) c->opr[1].sym = 0; + } + + +// TEST !! + +if (opl->nops == 2 && c->wop && zero_reg(c->opr + c->wop)) + { // R0 = R0 + something; + if (tx) *tx = scespec[2]; //opctbl[LDWIX].sce; + } + + // 3 operands. look for rbase or zero in op[1] or op [2] and convert or drop + if (opl->nops > 2) + { //rbase and R0 + if (c->opr[1].rbs && zero_reg(c->opr + 2)) shuffle_ops(tx,c,2); + + if (c->opr[2].rbs && c->opcsub == 1 && c->opcix > 23 && c->opcix < 29) + { // RBASE + offset by addition or subtraction, only if imd and ad3w or sb3w + b = c->opr+1; + addr = b->val + c->opr[2].val; // addr may be NEGATIVE at this point !! + if (addr < 0) addr = -addr; + + b->addr = addr; //b->val + c->opr[2].val; // convert to true address.... + // databank ? + b->imd = 0; + b->adr = 1; + s = get_sym(0, b->addr,32,c->ofst); + + if (s) b->sym = 1; + shuffle_ops(tx,c,2); // drop op [2] + } + + if (zero_reg(c->opr + 2)) shuffle_ops(tx,c,2); // x+0, drop R0 + if (zero_reg(c->opr + 1)) shuffle_ops(tx,c,1); // 0+x, drop R0 + + + + } // end op 3 op checks + + // R3 = R2 + R1 style. is anything rbase ?? replace with address as an imd + if (!c->opcsub && (c->opr[1].rbs || c->opr[2].rbs)) + { + for (i = 1; i < c->numops; i++) + { // op 1 for 2 ops, 1 & 2 for 3 ops + b = c->opr + i; + if (b->rbs) + { + b->imd = 1; + b->rgf = 0; + b->sym = 0; + } + } + } + + // CARRY opcodes - can get 0+x in 2 op adds as well for carry + // "\x2 += \x1 + CY;" nobank is for immediates... + + if (nobank(c->opr[1].addr) == 0 && c->opcix > 41 && c->opcix < 46) + { + if (tx) *tx = scespec[opl->sigix-6]; // drop op [1] via special array + } +} + + + + +void make_mask(uint ix) +{ //make mask of where matching bits are + + SYM *s; + uint i, start, end; + plist[ix] = 0; + s = bitnames[ix]; + + if (!s) plist[ix] |= (1 << ix); + else + { + if (s->prtbit) plist[ix] |= (1 << ix); + else + { + // check for upper byte matches first. + start = s->addb & 7; + end = s->fend; + if (ix > 7) + { + start += (ix/8) *8; + end += (ix/8) * 8;; + } + + for (i = start; i <= end; i++) plist[ix] |= (1 << i); + } + } +} + + + + +uint get_bitnames(INST *c) + { + // get all bit names, count print mask + // for later checks. + uint pcnt, i, x, sz; //ncnt + OPBIT *m; + SYM *s; + + m = (OPBIT*) c->opr + 4; + + x = m->pmask; // print mask + sz = c->opr[1].rfend; // bits to check - not right for variable fields + if (sz > 15) sz = 15; // safety !! + + // first, check for 'F' or 'N' in no bit symbol(s) + + pcnt = c->opr[m->sceix].addr; // base addr of op + + for (i = 0; i <= sz/8; i++) + { + s = get_sym(m->rw,pcnt+i,32, c->ofst); // master base name(s) + + if (s) + { + if (s->flags) m->flags = 1; // flag byte bit + if (s->names) m->names = 1; + } + } + + pcnt = 0; + +// x = m->pmask; // print mask + + for (i = 0; i <= sz; i++) + { + + bitnames[i] = get_sym(m->rw, c->opr[m->sceix].addr, i, c->ofst); + make_mask(i); // preset matching mask + + if (bitnames[i]) + { + if ((x & plist[i]) && !bitnames[i]->prtbit) m->nmfnd |= (1 << i); // matching bit names mask + if (bitnames[i]->flags) m->flags = 1; // 'F' flagsword + if (bitnames[i]->names) m->names = 1; // 'N' names only + } + if (x & plist[i]) {pcnt++; // count of matching syms + x ^= plist[i];} + } + + return pcnt; +} + + +uint prt_bitsym(INST *c, uint ix) +{ + // print bitname or BX_Rn as last resort + // ix is bitno in here.... + uint addr; + SYM *s; + OPBIT *m; + + m = (OPBIT*) c->opr + 4; + addr = c->opr[m->sceix].addr; + + if (bitnames[ix]) + { + s = bitnames[ix]; + if (s->prtbit) pstr (0,"B%d_", ix); + pstr (0,"%s",s->name); // print matched sym bit name + return 1; + } + + if (m->names) return 0; // names only + + fixbitfield(&addr, &ix, 0); // fix anything over B7 + pstr (0,"B%d_R%x", ix, addr); + return 1; +} + + +void prt_bitline(INST *c) +{ + // print bitnames. + // use 'm' struct + + uint i, x, sz, vmask; + uint pmask; + OPBIT *m; + + m = (OPBIT*) c->opr + 4; + + x = 0; + // pmask might be REVERSE of vmask (for ands) + + vmask = c->opr[1].val; // orig (value) mask + pmask = m->pmask; // print mask + sz = c->opr[1].rfend + 1; + if (sz > 16) sz = 16; // safety !! + + for (i = 0; i < sz; i++) + { + if (pmask & plist[i]) + { + if (x) + { // indent+new line if not first sym + p_indl (); + x = 0; + } + + x = prt_bitsym(c, i); //print bit or sym + + if (x) + { + pchar(' '); + if (m->fxor) pstr(0,"^"); + pstr (0,"= %d;", (vmask & plist[i]) >> i); // ** HERE would be a bit FIELD + + if (pmask == plist[i]) break; // exit and stop comment check (last entry) + pp_comment(c->ofst+1,2); // otherwise check for comment + } + pmask ^= plist[i]; + } + } +} + + +void bitwise_replace(const char **tx, INST *c) + + /* check for bit flag printouts (and or operations) + * must be immediate op (as flags mask) and at least one name found + * for ops with a \4 at the front. WRITE symbols for first try. + * ops referenced are - + * c->opr[1].val is actual mask + * c->opr[2].addr is base (sym lookup) addr - same for 2 or 3 op + * ops[4] overlaid with different struct 'm' for ease of reading + * m->pmask // print mask + * m->nfnd // names found mask + * m->fxor // this is an XOR opcode + * m->flags // flags word 'F' in sym or bitsyms + * m->names // names 'N' in sym + * m->sceix // opc source index + * m->rw // look for read or write syms + */ +// need to rework for fields wider than one bit................ +{ + + uint i, x, pcnt, sig; + OPBIT *m; + + if (anlpass < ANLPRT) return; // only for print phase + sig = c->sigix; // opcode sigix + + // only continue if either imd instruction or clrx + + if (c->opcsub != 1 && sig != 1) return; // only if immediate or clr + if (c->opr[4].rbs) return; // NOT if a shuffled out rbs operand + + memset(c->opr+4,0,sizeof(OPS)); // clear ops[4], safety + + m = (OPBIT*) c->opr + 4; // overlay opbit onto ops[4] + m->pmask = c->opr[1].val; // print mask + m->sceix = (sig == 1) ? 1 : 2; // source op index, 1 for clr + m->rw = 1; // write syms by default + if (c->opcix >= 32 && c->opcix <= 33) m->fxor = 1; // XOR Flag + + memset(&bitnames,0,sizeof(bitnames)); // clear bitnames + + // OR, XOR with zero mask + if (sig == 9 && !c->opr[1].val) + { + c->opr[1] = c->opr[2]; + *tx = scespec[2]-1; + // add "redundant" comment ?? + return; + } + + // AND. reverse print mask if not a 3 op, otherwise set READ + + if (sig == 5) + { + if (c->numops < 3) + { // not an3x + m->pmask = ~m->pmask; + m->pmask &= get_sizemask(c->opr[1].rfend); // safety + } + else m->rw = 0; // read sym names for an3x + } + + pcnt = get_bitnames(c); // pcnt is no of bits in print mask + + if (sig == 12 || sig == 10 || sig == 1) + { // ldx, cmp, clr + if (m->flags) + { + m->pmask = get_sizemask(c->opr[1].rfend); // set all bits to print (wrong ??) + } + else return; // do standard print of these ops + } + +// STX ???? + +// if (pcnt > 1) c->opr[4].addr = c->opr[4].val; // printmask = namesmask - only prt sym names + + if (pcnt > 1 && !m->nmfnd) return; + + + // now print names, one per line + + if (c->numops < 3) + { + prt_bitline(c); // one bitname per line, with = + *tx = empty; + } + else + { // an3x a = b|c|d format + int ppos; + + if (sig != 5) return; + + p_opsc(c,3,1); + pstr(0," = "); + if (pcnt > 1) {pchar ('('); ppos = gcol;} + +// newline for | for long lines ? + + sig = c->opr[1].val; // temp vmask + x = 0; + i = 0; + + while (i <= c->opr[1].rfend) //; i++) + { + if (!sig) break; //to stop comment ?? not reqd ?? + if (sig & plist[i]) + { + if (x) + { + pchar('|'); + pp_comment(c->ofst+1,2); // and need a pp_comment here too.......... + pstr(1,0); + p_pad(ppos); + } + x = prt_bitsym(c, i); //name or Bx_Rn + sig ^= plist[i]; + } + i++; + } + + if (pcnt > 1) pchar (')'); + if (pcnt) { pchar(';'); *tx = empty; } // at least one name substituted return a ptr to null + } + + } + + + +/* +void shift_comment(INST *c) + { + // do shift replace here - must be arith shift and not register + int ps; + + if (anlpass < ANLPRT) return ; // redundant ? + + if (c->opcix < 10 && c->opr[1].imd) + { // immediate shift + if (zero_reg(c->opr + 2)) + { // R0, timewaster + ps = sprintf(nm, "#[ Time delay ?] "); + } + else + { + ps = sprintf(nm, "#[ \x2 "); + if (c->opcix < 4) + ps += sprintf(nm+ps,"*"); + else + ps += sprintf(nm+ps,"/"); + ps += sprintf(nm+ps, "= %d] ", 1 << c->opr[1].val); + } + acmnt.ofst = c->ofst; + acmnt.ctext = nm; + acmnt.minst = c; + } +} + +*/ + +void do_carry(INST *c, const char **tx, int ainx) +{ + +// Replace "if (CY)" with various sources +// possible opcodes add,shift R, shift L, cmp, inc, dec, neg, sub + + int ans; + const OPC *opl; + + pstr (0,"if ("); + + + // get last psw changer + ans = find_last_psw(c,1); // returns 1 if found...op in sinst + if (!ans) return; // not found, revert to "if (CY" style + + opl = opctbl + sinst.opcix; // mapped to found inst + + if (opl->sigix == 7) + { + // subtract. Borrow (Carry) cleared if a>=b + // use swopcmpop to remap aix to get correct arith operator + // JNC -> JLT JC -> JGE + + *tx = swopcmpop[ainx&1]; + p_opsc(&sinst,opl->wop,1); // write + pstr(0," %s ",*tx); + p_opsc(0,1,0); + pstr(0,") "); + *tx = scespec[3] -1; //opctbl[JMPIX].sce - 1; //jump sce code to follow this - allow for increment + } + + if (opl->sigix == 10) + { + // compare. Same as subtratc, but no wop + // use swopcmpop to remap aix to get correct arith operator + // JNC -> JLT JC -> JGE + + *tx = swopcmpop[ainx&1]; + + if (zero_reg(sinst.opr + 2)) //sinst.opr[2].addr == 0) + { // jc/jnc after zero first compare, swop order via swopcmpop 8 and 9 + p_opsc(&sinst,1,0); // operand 1 of compare + pstr(0," %s ", swopcmpop[ainx - 48]); // 56 maps to 8 + p_opsc(&sinst,2,0); // operand 2 of compare (zero) + } + else + { + p_opsc(&sinst,2,0); + pstr(0," %s ",*tx); + p_opsc(&sinst, 1,0); + } + pstr(0,") "); + *tx = scespec[3] -1; //opctbl[JMPIX].sce - 1; //jump sce code to follow this - allow for increment + } + + if (opl->sigix == 6 || opl->sigix == 25) + { // add or inc STANDARD CARRY - have wop ? + p_opsc(&sinst,opl->wop,1); + pstr(0," %s", (ainx & 1) ? ">" : "<="); + pstr(0," %x) ", get_sizemask(sinst.opr[opl->wop].wfend)); + *tx = scespec[3] -1; //opctbl[JMPIX].sce - 1; //jump sce code to follow this - allow for increment + } + + + if (opl->sigix == 3 || opl->sigix == 4) + { // shl (3) or shr (4) + + // check no of shifts is imd, otherwise return 0=0 style. + if (!sinst.opr[1].imd) + { + clr_inst(&sinst); // can't do variables + return; + } + + if (opl->sigix == 3) + { //shl + ans = sinst.opr[opl->wop].wfend + 1; // (sinst.opr[opl->wop].wsize & 7) * 8; // casz[sinst.opr[opl->wop].wsize] * 8; // number of bits + ans -= sinst.opr[1].val; //shift from MSig + + if (ans > 16) + { // dl shifts ?? + ans -= 16; + sinst.opr[opl->wop].addr+=2; + } + } + else + { //shl + ans = sinst.opr[1].val-1; + } + + pstr(0,"B%d_", ans); + + p_opsc(&sinst,opl->wop,1); + pstr(0," = %d) ", ainx & 1); + *tx = scespec[3] -1; //opctbl[JMPIX].sce - 1; //jump sce code to follow this - allow for increment + } + + /* if (opl->sigix == 11 || opl->sigix == 8) + { // mult,div STANDARD OVERFLOW all have wop ? + p_opsc(&sinst,opl->wop,0); + pstr(" %s", (ainx & 1) ? ">" : "<=" ); + pstr(" %x) ", camk[sinst.opr[opl->wop].wsize]); + *tx = opctbl[JMPIX].sce - 1; //jump sce code to follow this - allow for increment + } +*/ + + if (opl->sigix == 24) + { // dec + p_opsc(&sinst,opl->wop,1); + pstr(0," %s 0) ", (ainx & 1) ? ">=" : "<" ); + *tx = scespec[3] -1; //opctbl[JMPIX].sce - 1; //jump sce code to follow this - allow for increment + } + +} + + +void do_ovf(INST *c, const char **tx, int ainx) +{ + +// Replace if (OVF) with various sources +// possible opcodes add, shift L, cmp, inc, dec, neg, sub, div. + + // int ans; + // const OPC *opl; + + pstr (0,"if ("); + // get last psw changer + +return; //temp ignore extras + +/* + ans = find_last_psw(c,1); // returns 1 if found...op in sinst + if (!ans) return; // not found + + opl = opctbl + sinst.opcix; // mapped to found inst + + + if (opl->sigix == 7) + { + // subtract. Borrow (Carry) cleared if a>=b + // use swopcmpop to remap aix to get correct arith operator + // JNC -> JLT JC -> JGE + + *tx = swopcmpop[ainx&1]; + p_opsc(&sinst,opl->wop,0); + pstr(" %s ",*tx); + p_opsc(0,1,0); + pstr(") "); + *tx = opctbl[JMPIX].sce - 1; //jump sce code to follow this - allow for increment + } + + + if (opl->sigix == 10) + { + // compare. Borrow (Carry) cleared if a>=b + // use swopcmpop to remap aix to get correct arith operator + // JNC -> JLT JC -> JGE + + *tx = swopcmpop[ainx&1]; + + if (!nobank(sinst.opr[2].addr)) + { // jc/jnc after zero first compare, swop order via swopcmpop 8 and 9 + p_opsc(&sinst,1,0); // operand 1 of compare + pstr(" %s ", swopcmpop[ainx - 48]); // 56 maps to 8 + p_opsc(&sinst,2,0); // operand 2 of compare (zero) + } + else + { + p_opsc(&sinst,2,0); + pstr(" %s ",*tx); + p_opsc(&sinst, 1,0); + } + pstr(") "); + *tx = opctbl[JMPIX].sce - 1; //jump sce code to follow this - allow for increment + } + + if (opl->sigix == 6 || opl->sigix == 25) + { // add or inc STANDARD CARRY - have wop ? + p_opsc(&sinst,opl->wop,0); + pstr(" %s", (ainx & 1) ? ">" : "<="); + pstr(" %x) ", camk[sinst.opr[opl->wop].wsize]); + *tx = opctbl[JMPIX].sce - 1; //jump sce code to follow this - allow for increment + } + + + if (opl->sigix == 3) + { // shl STANDARD CARRY all have wop ? + // need to find out how many shifts + // sinst.opr[1].val is shift amount. wop is shifted value cant do it for other than imm (opr[1].imd) + + if (!sinst.opr[1].imd) + { + clr_inst(&sinst); // can't do variables + return; + } + + //shl + + ans = casz[sinst.opr[opl->wop].wsize] * 8; // number of bits + ans -= sinst.opr[1].val; //shift from MSig + + // dl shifts ?? + + if (ans > 16) + { + ans -= 16; + sinst.opr[opl->wop].addr+=2; + } + + //fiddle as a bit + pstr("B%d_", ans); + p_opsc(&sinst,opl->wop,0); + pstr(" = %d) ", ainx & 1); + *tx = opctbl[JMPIX].sce - 1; //jump sce code to follow this - allow for increment + } + + if (opl->sigix == 11 || opl->sigix == 8) + { // mult,div STANDARD OVERFLOW all have wop ? + p_opsc(&sinst,opl->wop,0); + pstr(" %s", (ainx & 1) ? ">" : "<=" ); + pstr(" %x) ", camk[sinst.opr[opl->wop].wsize]); + *tx = opctbl[JMPIX].sce - 1; //jump sce code to follow this - allow for increment + } + + + if (opl->sigix == 24) + { // dec + p_opsc(&sinst,opl->wop,0); + pstr(" %s 0) ", (ainx & 1) ? ">=" : "<" ); + *tx = opctbl[JMPIX].sce - 1; //jump sce code to follow this - allow for increment + } +*/ +} + +void do_cond_ops(INST *c, const char **tx, int ainx) +{ + // Conditional ops replacements. + // last is ZERO and lwop < 0 if no candidate found. + // lwop is zero ONLY for R0 as wop + + // note that tx is ALREADY SWOPPED for "if{}" or "goto" + + OPC *opl; + const char *t, *s, *x; + int ans; + + t = *tx+1; // skip '\7', t points to operator string + + ans = find_last_psw(c,1); // returns 1 if found...op in sinst + + pstr (0,"if ("); + + // if (!ans) return; // not found + opl = opctbl + sinst.opcix; // mapped to found instance + + if (opl->wop) + { // not a compare + if (!nobank(sinst.opr[opl->wop].addr)) + { + // wop is R0, style"(op) = 0" + // does not work for shortened ops, i.e. "+=" + + + if (nobank(sinst.opr[2].addr)) + { // print only [1] and op if zero register in [2] + s = opctbl[sinst.opcix].sce; // source code of psw op + x = strchr(s, '='); + if (x) + { // do opcode after = in brackets + x += 3; // should now be op + pstr(0,"("); + p_opsc(&sinst,2,1); // operand 2 of compare (write) + while (*x > 0x1f) pchar(*x++); // operator (space & up) + p_opsc(&sinst,1,0); // operand 1 of compare + pstr(0,")"); + } + } + else + { + p_opsc(&sinst,1,1); // operand 1 of compare (write) + } + pstr(0," %s ",t); // orig compare op + p_opsc(0,1,0); // orig compare value + } + else + { // no compare real wop , style [wop] == 0 + p_opsc(&sinst,opl->wop,1); // write + pstr(0," %s ",t); + p_opsc(0,1,0); + } + } + + + if (!opl->wop) + { // compare found, style R2 = R1 + if (!nobank(sinst.opr[2].addr)) + { + if (!nobank(sinst.opr[1].addr) && ans) + { //both ops zero ?? + pstr(0," %s ", zerocmpop[ainx - 62]); //c->opcix - 62]); + } + else + { + // cmp has zero first, so swop operands over via special array. + // NB. NOT same as 'goto' to {} logical swop + // so R1 0 + p_opsc(&sinst,1,0); // operand 1 of compare + pstr(0," %s ", swopcmpop[ainx - 62]); //c->opcix - 62]); + p_opsc(&sinst,2,0); // operand 2 of compare (zero) + } + } + else + { + p_opsc(&sinst,2,0); // operand 2 of compare (reads) + pstr(0," %s ",t); + p_opsc(&sinst,1,0); // operand 1 of compare + } + } + + pstr(0,") "); + *tx = scespec[3] -1; //opctbl[JMPIX].sce - 1; //set \9 for 'goto' processing +} + + +void pp_answer(INST *c) +{ + // print answer, if there is one + uint addr; + SUB *s; + ADT *a; + SYM *sym; + OPS *o; + + addr = c->opr[1].addr; + s = get_subr(addr); + + if (!s) return; + a = get_adnl(&chans,s->start,1); + + if (!a) return; + +// { // found an answer set up phantom opr[3] (rgf set) + o = c->opr+3; + sym = 0; + + o->rfend = a->fend; + o->addr = a->data; + + if (o->addr > 0x3ff) + { + o->rgf = 0; + o->adr = 1; + o->bkt = 1; + } + + if (a->fnam) sym = get_sym(0, o->addr,32,c->ofst); // syname AFTER any decodes + if (sym) {o->sym = 1; o->bkt = 0;} + + p_opsc(c,0x13,0); + + pstr(0," = "); +// } +} + + + + +void do_regstats(INST *c) + { + int i; + OPS *o; + RST *r; + +// register size arguments + + for (i = 1; i <= c->numops; i++) + { + // not wop ! + if (i != c->wop) + { + o = c->opr+i; + r = get_rgstat(o->addr); + if (r && r->arg) + { + set_rgsize(r, o->rfend); + // r->used = 1; + + } + } + } + + if (c->opcsub == 2) + { // indirect use opr[4] for reg details + o = c->opr+4; + r = get_rgstat(o->addr); + + if (r && r->arg) + { + // arg used as address, so will always be WORD + r->inr = 1; + set_rgsize(r, 15); + // r->used = 1; + + +// add here instead ?? + } + } + +} + + + void do_dflt_ops(SBK *s, INST *c) + { // default - any operands as registers + int i; + c->opcsub = 0; // no multiple mode + for (i = 1; i <= c->numops; i++) + { + op_calc (g_byte(s->nextaddr + i), i,c); + // set_opdata(s->nextaddr + i,7); + } + s->nextaddr += c->numops + 1; +// if (s->emulating) + do_regstats(c); + } + + + +void calc_mult_ops(SBK *s, INST *c) +{ + /******************* multiple subtype instructions ***************** + * opcsub values 0 direct 1 immediate 2 indirect 3 indexed + * order of ops 3 ops 2 ops 1 op + * dest reg dest reg sce/dest reg + * sce reg sce reg + * sce2 reg + * NB indexed mode (3) is a SIGNED offset + ******************************************* + * Adjust xofst for true size of opcode + * +2 opcsub 3, if 'long index' flag set + * +1 opcsub 3, if not set (='short index') + * +1 opcsub 1 Word op (immediate) + ******************************************************/ + + int firstb,i, xofst,addr; + OPS *o; + + xofst = s->nextaddr; + + // c->opcsub = g_byte(xofst) & 3; //move outside to save an access + + firstb = g_byte(xofst+1); // first data byte after (true) opcode + // set_opdata(xofst+1,7); + o = c->opr+1; // most action is for opr[1] + + + switch (c->opcsub) + { + case 1: // first op is immediate value, byte or word + op_imd(c); // op[1] is immediate + if ((o->rfend & 31) > 7) // word mode + { // recalc op1 as word + xofst++; + op_calc (g_word(xofst), 1,c); + // set_opdata(xofst,o->rfend); + } + else + { + op_calc (firstb, 1,c); // calc for op[1] + } + + break; + + case 2: // indirect, optionally with increment - add data cmd if valid + // ALWAYS WORD address. Save pre-indirect to [0] for increment by upd_watch + + if (firstb & 1) + { // auto increment + firstb &= 0xFE; // drop flag marker + // c->inc = 1; + o->inc = 1; // always op[1] + } + op_calc (firstb, 1,c); // calc [1] for new value, always even + o->bkt = 1; + c->opr[4] = c->opr[1]; // keep register details + if (anlpass < ANLPRT) + { // get actual indirect values for emulation (restore in upd_watch) + o->addr = g_word(o->addr); // get address from register, always word + o->addr = databank(o->addr, c); // add bank + o->val = g_val(o->addr, 0, o->rfend); // get value from address, sized by op + // add_opr_data(s,c); + } + break; + + case 3: // indexed, op0 is a fixed offset + + if (firstb & 1) + { + c->opr->rfend = 15; // offset is word (unsigned) + firstb &= 0xFE; // and drop flag + } + else + { + c->opr->rfend = 39; // short, byte offset (signed) + } + + c->opr->off = 1; // op [0] is an address offset + o->bkt = 1; + op_calc (firstb, 1,c); // calc op [1] (always even) + if (!o->rbs) o->val = g_word(o->addr); // ALWAYS a WORD for indexed + c->opr[4] = c->opr[1]; // keep register details - is this right for STW ?? + c->opr->addr = g_val(xofst+2,0,c->opr->rfend); // get offset or base value CAN BE NEGATIVE OFFSET + // set_opdata(xofst+2,c->opr->rfend); + addr = c->opr->addr; + if (addr < 0 && (c->opr->rfend & 32)) c->opr->neg = 1; // flag negative if signed + + if (anlpass < ANLPRT) + { // get actual values for emulation + addr = c->opr->addr + o->val; + //o->addr may be NEGATIVE at this point !! probably never.......... + if (addr < 0) + { + addr = -addr; // wrap around ? + } + o->addr = databank(addr, c); // get address and bank + o->val = g_val(o->addr,0, o->rfend); // get value from address, sized by op + } + + xofst += bytes(c->opr->rfend); // size of op zero; + + break; + + default: // direct, all ops are registers + op_calc (firstb, 1,c); // calc for op[1] + break; + } + + //calc rest of ops - register 1 done + + for (i = 2; i <= c->numops; i++) + { + op_calc (g_byte(xofst + i), i,c); + } + s->nextaddr = xofst + c->numops + 1; + + if (s->scanning) add_dtk(s,c); + +// if (s->emulating) +do_regstats(c); + + } + + +void pset_psw(INST *c, int t, int mask) + { + // preset ones or zeroes + + mask &= 0x3f; // safety + + if (t) + { + c->psw |= mask; // set defined bits + } + else + { + mask = ~mask; // flip bits + c->psw &= mask; // clear defined bits + } + + } + +void set_psw(INST *c, int val, int size, int mask) + { + + c->psw &= (~mask); // clear defined bits + + /* set bits according to actual state + #define PSWZ(x) (x->psw & 32) // Z zero flag + #define PSWN(x) (x->psw & 16) // N negative + #define PSWOV(x) (x->psw & 8) // OVF overflow + #define PSWOT(x) (x->psw & 4) // OVT overflow trap + #define PSWCY(x) (x->psw & 2) // CY carry + #define PSWST(x) (x->psw & 1) // STK sticky +*/ + + if ((mask & 32) && !val) c->psw |= 32; // zero + if ((mask & 16) && val < 0) c->psw |= 16; // negative + + if (mask & 2) + { // carry + c->psw &= 0x3d; // clear CY + if (val > (int) get_sizemask(size)) c->psw |= 0x2; //cnv[size].sizemask) c->psw |= 0x2; // set CY camk[size] + } + if (mask & 8) + { // OVF + c->psw &= 0x37; // clear OVF + if (val > (int) get_sizemask(size)) c->psw |= 0x8; // set OVF + } + + if (mask & 4) + { // OVT - don't clear, just set it. + if (val > (int) get_sizemask(size)) c->psw |= 0x4; // set OVT + } +} + + + +int find_vect_size(SBK *s, INST *c, int ix) +{ + // see if a cmp can be found with register. + // go back to see if we can find a size + // allow for later ldx to swop registers + +// IX is always 4 ?? + +// could add nrml here too (would be max 32) + + int size, xofst; + uint reg; + + size = 0; + reg = c->opr[ix].addr; + xofst = c->ofst; + + while (xofst) + { // look for a cmp or ldx (max 10 instructions back) + xofst = find_pn_opcodes (0, xofst, 10, 2, 10, 12); + + if (xofst) + { + if (sinst.sigix == 12 && sinst.opr[2].addr == reg) + { // ldx from another register, change register + reg = sinst.opr[1].addr; + } + + if (sinst.sigix == 10 && sinst.opr[2].addr == reg) + { // cmp + if (sinst.opcsub == 1) + { // found cmp for register + size = sinst.opr[1].val; // immediate compare, use size + return size; + } + } + } + } + +return size; +} + + + + + + +void pshw (SBK *s, INST *c) +{ + OPS *o; + int size, i; + // SIG *g; + FSTK *t; + RST *r; + + calc_mult_ops(s,c); + + // does not change PSW + + // if scanning, check for vect pushes first (list or single) + // immediate value is normally a direct call if valid code addr + // if indexed or indirect do list check + // it std register, assume it's an argument getter + + o = c->opr + 1; + + if (anlpass >= ANLPRT) return; + + if (s->scanning) + { + // change to a switch on opcsub ? + + if (o->imd && val_rom_addr(o->addr)) // c->opcsub = 1 + { // treat as a subr call, but this could be a return address + // this uses CODE bank, not data + //change display to addr, not val + add_scan(codebank(o->addr,c), J_SUB,s); + } + + if (c->opcsub == 3) + { + // indexed - try to find size and 'base' of list + // ops[0] is base (fixed) addr. ops[4] (copy of ops[1]) is saved register. + // go backwards to see if we can find a size via a cmp (find_vect_size) + // only look for list if [0] is a valid address, + + // try [1] too ?? a combined address may be the right one + + i = databank(c->opr->addr,c); // add bank first + if (val_rom_addr(i)) + { // vect list from (valid) fixed addr part of index + size = find_vect_size(s, c, 4); + check_vect_list(s, i, size, c); + } + } + if (c->opcsub == 2) + { + // indirect - try to find size and base of list + // size is same as indexed above, (via cmp) + // but base must have been added earlier, so look for an ADD, X + // where x is immediate + // [4] is saved register + + i = find_list_base(c, 4,10); + size = find_vect_size(s, c, 4); + // i = databank(i,c); + if (i) //val_rom_addr(i)) done in find_list + { + check_vect_list(s, i, size, c); + } + } + + if (o->rgf && !c->opcsub) + { + // register - check for argument getter + r = 0; + for (i=0; i < STKSZ; i++) + { + t = scanstack+i; + if (t->type == 1 && t->reg == o->addr) + { // found + r = get_rgstat(o->addr); + if (r) r->popped = 0; + t->popped = 0; // mark as pushed. + } + } + +/* if (!r && o->addr && nobank(o->val) > PCORG) + { //push goes to different scan queue ?? + i = codebank(o->val,c); + // add gapscan ?? + DBGPRT(1,0); // consider codebank(o->val) as scan_addr...... + } +*/ + + } + } // end of if scanning + + if (s->emulating) + { // similar to push above, but do args if necessary + // note that as this is done after each scan or rescan, + // the stack is already built by sj subr.... + + if (o->imd && val_rom_addr(o->addr)) //opcsub = 1 + { // immediate value (address) pushed + emuscan = add_escan(codebank(o->addr,c),s); // add imd as a special scan + fakepush(s, o->addr, 2); // imd push + } + else + { + for (i=0; i < STKSZ; i++) + { + t = emulstack+i; + if (t->type == 1 && t->reg == o->addr) + { // found + t->popped = 0; // clear popped. + t->newadd = o->val | g_bank(t->origadd); + do_args(s,t); + r = get_rgstat(o->addr); + if (r) + { r->popped = 0; + if (t->newadd == t->origadd) + { // no change, delete it + #ifdef XDBGX + DBGPRT(1,"delete rgstat R%x", o->addr); + #endif + chdelete(&chrgst,chrgst.lastix,1); + } + } + break; + } + } + } + } + + + #ifdef XDBGX + // DBG_stack(s->emulating); + DBGPRT(0,"%x PUSH a=%x v=%x",c->ofst, o->addr, o->val); + if (s->substart) DBGPRT(0," sub %x", s->substart); + DBGPRT(1,0); + #endif + +} + + + +void popw(SBK *s, INST *c) +{ + + OPS *o; + FSTK *t; + int i; + RST *r; + // does not set PSW + + calc_mult_ops(s,c); + + if (anlpass >= ANLPRT) return; + + o = c->opr+1; // w operand + o->val = 0; // safety + + // can get throwaway POP to R0. Ignore it. + if (zero_reg(o)) return; + +// if scanning, don't actually care that there is no stack entry. +// this only really matters in emulate. + + if (s->emulating) t = emulstack; else t = scanstack; + + #ifdef XDBGX + DBG_stack(t); + #endif + + for (i=0; i < STKSZ; i++) + { // scan UP callers + if (!t->type) + { + if (s->scanning) + { + r = add_rgstat(o->addr, 15,0, 0); + if (r) { + r->popped = 1; + // r->used = 0; + } + } + break; + } + + if (!t->popped) + { // not popped yet(or pushed back) + t->reg = o->addr; + t->popped = 1; + o->val = get_stack(s, t, 3); // not pop flags + r = add_rgstat(o->addr, 15, 0, o->val); // find or add (was origadd) + if (r) { + r->popped = 1; // set popped for emu detect + #ifdef XDBGX + DBGPRT(1,"set Popped, WR%x=%x at %x", o->addr, o->val, c->ofst); + #endif + } + break; + } + t++; + } +} + + + + +void clr (SBK *s, INST *c) + { + // clear + do_dflt_ops(s,c); + + c->opr[c->wop].val = 0; + + if (s->emulating) + { + pset_psw(c, 0, 0x1a); // clear bits N,V,C + pset_psw(c, 1, 0x20); // set Z + } + } + +void neg (SBK *s, INST *c) + { + // negate + + do_dflt_ops(s,c); + c->opr[1].val = -c->opr[1].val; + if (s->emulating) set_psw(c, c->opr[1].val, c->opr[1].wfend,0x2e); + } + + +void cpl (SBK *s, INST *c) + { + // complement bits + do_dflt_ops(s,c); + c->opr[1].val = (~c->opr[1].val) & 0xffff; // size ? + + if (s->emulating) + { + pset_psw(c, 0, 0xa); // clear bits V,C + set_psw(c, c->opr[1].val, c->opr[1].wfend, 0x30); // N and Z + } + } + + + +void check_R11(INST *c) +{ + // extra handler for write to Reg 11 for both stx and ldx + // done AFTER assignment, but before upd_watch + // R11 is not written to anyway by upd_watch + int val; + OPS *o; + + if (c->opcsub != 1) return; // immediate only + + o = c->opr + c->wop; // always 2 ?? + + if ((o->addr & 0x3ff) != 0x11) return; + + // specifically write to ibuf, to keep it for reads ? + + val = c->opr[1].val & 0xf; + val++; + upd_ram(0x11,c->opr[1].val,7); + + // do this only if it's an imd as this is reliable, else ignore it ??? + + if (bkmap[val].bok) + { // valid bank + basepars.datbnk = val << 16; + #ifdef XDBGX + DBGPRT(1,"%x R11 Write imd, DataBank=%d",c->ofst, val); + #endif + } + } + + + +void do_rgargs(INST *c) +{ + + RST *org, *nrg; +// OPS *sce, *dst; + uint screg, scfend, scaddr, scval; + uint dsreg; + + + if (c->opcsub == 1) return; //ignore immediates + + screg = 0; + + if (c->sigix == 12) + { //ldx + if (!c->opcsub) screg = c->opr[1].addr; + else screg = c->opr[4].addr; + scfend = c->opr[1].rfend; + scval = c->opr[1].val; + scaddr = c->opr[1].addr; + dsreg = c->opr[2].addr; + } + + if (c->sigix == 13) + { //stx + screg = c->opr[2].addr; + scfend = c->opr[2].rfend; + scval = c->opr[2].val; + scaddr = c->opr[2].addr; + dsreg = c->opr[1].addr; + } + + +// if (!c->opcsub) +// sce = c->opr + six; //ldx +// else sce = c->opr + 4; +// dst = c->opr + c->wop; //dst + + +// sce = c->opr + 2 // for stx +// if (!c->opcsub) dst = c->opr + 1; //stx + // else dst = c->opr + 4; + + + org = get_rgstat(screg); + + if (org) + { // found source reg - check if popped or arg + if (org->arg || org->sarg) + // reg is flagged as arg, so use size + { + // firstly set size, and reflect back to source. + set_rgsize(org,scfend); + nrg = get_rgstat(org->sreg); + set_rgsize(nrg,scfend); + + // if first level arg (next to popped) + if (org->sarg) + { + // this 2nd level arg, copy sce from first level + // how to stop the arg flag when left subr......... + nrg = add_rgstat(dsreg, scfend, org->reg, org->ofst); + if (nrg) {nrg->arg = 1; org->arg = 0; + if (org->enc) { + nrg->enc = org->enc; //src is encoded + nrg->fend = 15; // enc is always a word + nrg->data = org->data; // 'start of data' base reg +#ifdef XDBGX +DBGPRT(1,"ORG ENC"); +#endif +} } + } + + #ifdef XDBGX + if (org) DBGPRT(1,"RG %x=%x to %x", screg, scval, dsreg); + #endif + + } + else + if (org->popped) + // reg flagged as popped so make [wop] an arg - would expect this to be indirect or indexed + { + nrg = add_rgstat(dsreg, scfend, org->reg, scaddr); // [1] for address if indirect + if (nrg) + { + nrg->arg = 1; + nrg->sarg = 1; // source level from pop + + #ifdef XDBGX + DBGPRT(1,"ADD ARG RG %x at %x sz%d", nrg->reg, c->ofst, nrg->fend); + #endif + } + } + } +} + + + + + + + + +void stx (SBK *s, INST *c) + { + /* NB. this can WRITE to indexed and indirect addrs + * but addresses not calc'ed in print phase + * does not change PSW + */ + + FSTK *t, *x; + OPS *o; + RST *r; //, *b; + int val, inx; + + calc_mult_ops(s,c); + + c->opr[1].val = c->opr[2].val; + + if (anlpass >= ANLPRT) return; + + // now check if this is a stack operation + // if it is a stack register, treat similarly to a PUSH + + o = c->opr + 4; // to check inx register + + if (s->scanning) x = scanstack; else x = emulstack; + + if (val_stack_reg(o->addr) && s->emulating) + { + inx = -1; + + if (c->opcsub == 2) inx = 0; // inr + if (c->opcsub == 3) inx = c->opr[0].addr/2; // inx + + if (inx >=0 && inx < STKSZ) + { + t = x+inx; + val = c->opr[2].val |= g_bank(t->origadd); // for stack checks + t->newadd = val; //update both scan and emu + do_args(s,t); + #ifdef XDBGX + DBGPRT(1,"PUSH [%d] via STX R%x=%x over %x at %x", inx, c->opr[2].addr, val, t->origadd, c->ofst); +#endif + r = get_rgstat(c->opr[2].addr); + if (r) r->popped = 0; // clear popped from sce reg + + if (t->type != 1) + { + #ifdef XDBGX + DBGPRT(0,"PSW INV PUSH!! %d ",t->type); + DBG_stack(x); + #endif + return; + } + } + } + + do_rgargs(c); + +check_R11(c); + } + + + + + +void ldx(SBK *s, INST *c) + { + // does not change psw + + int inx; + RST *org; //, *nrg; + OPS *o; //, *sce; + FSTK *t, *x; + + calc_mult_ops(s,c); + + o = c->opr+2; + + o->val = c->opr[1].val; // [2] = [1] as default op + + if (anlpass >= ANLPRT) return; + + if (s->scanning) x = scanstack; else x = emulstack; + + + // check if [4] = stack register. If so this is a load from stack, + // analogous to a POP, and will always be indirect or indexed + + if (val_stack_reg(c->opr[4].addr)) + { + #ifdef XDBGX + DBG_stack(x); + #endif + inx = -1; + if (c->opcsub == 2) inx = 0; // inr + if (c->opcsub == 3) inx = c->opr[0].addr/2; // inx + + if (inx >=0 && inx < STKSZ) + { + t = x + inx; + o->val = get_stack(s, t, 7); // anything + org = add_rgstat(o->addr, 15, 0, o->val); //s->substart); // find or add (word sized) + t->reg = o->addr; + t->popped = 1; // same as a pop + if (org) org->popped = 1; // set popped for emu detect + + #ifdef XDBGX + DBGPRT(1,"%x POP via LDX R%x=%x [%d] T%d", c->ofst, c->opr[2].addr, c->opr[2].val, inx, t->type); + DBGPRT(1,"set popped, size = 2"); + #endif + if (t->type == 1 && g_bank(o->val) != basepars.datbnk) + { + + basepars.datbnk = g_bank(o->val); + #ifdef XDBGX + DBGPRT(1,"set dbnk %d", basepars.datbnk); + #endif + + // set databank here for correct [Rx++] calls ? if t->type == 1 + } + } + } + + check_R11(c); + + // if (s->emulating) + // { + // 2 = 1 for ldx (if not stack reg), + // so get source register from 1 (or 4 if ind or inx) + + +//if (s->curaddr == 0x87aaa) +//{ +//DBGPRT(1,0); +//} + + + do_rgargs(c); +} +/* + org = 0; + sce = 0; + if (!c->opcsub) sce = c->opr + 1; + if (c->opcsub > 1) sce = c->opr + 4; + + if (sce) org = find_rgstat(sce->addr); + + if (org) + { // found source reg + if (org->arg) + // reg is flagged as arg, so use size + { + // really what we are interested in for now is ONLY the SIZE + set_rgsize(org,o->rfend); + + org = find_rgstat(org->sreg); + set_rgsize(org,o->rfend); + + // but now, arg value is copied to a new register/addr, which MAY be used..... + #ifdef XDBGX + if (org) DBGPRT(1,"LDX %x=%x to %x", sce->addr, sce->val, c->opr[c->wop].addr); + #endif + + } + else + if (org->popped) + // reg flagged as popped so make [wop] an arg - would expect this to be indirect or indexed + { + nrg = add_rgstat(o->addr, o->rfend, c->opr[1].addr); //org->ofst); //s->substart); + if (nrg) + { + nrg->arg = 1; + // nrg->sarg = 1; + nrg->sreg = org->reg; + + #ifdef XDBGX + DBGPRT(1,"ADD ARG LDX %x at %x sz%d", nrg->reg, s->start, nrg->fend); + #endif + } + } + } + + // } + + } +*/ + + + +void orx(SBK *s, INST *c) + { + + calc_mult_ops(s,c); + + if (c->opcix < 32) c->opr[2].val |= c->opr[1].val; // std or + else c->opr[2].val ^= c->opr[1].val; // xor + + if (s->emulating) + { + pset_psw(c, 0, 0xa); // clear bits V,C + set_psw(c, c->opr[2].val, c->opr[2].wfend, 0x30); // N and Z + } + } + +void add(SBK *s, INST *c) + { + // 2 or 3 op + calc_mult_ops(s,c); + + c->opr[c->numops].val = c->opr[2].val + c->opr[1].val; + + if (s->emulating) + set_psw(c, c->opr[c->numops].val, c->opr[c->numops].wfend, 0x3e); + } + + +void andx(SBK *s, INST *c) + { + // 2 or 3 op + calc_mult_ops(s,c); + c->opr[c->numops].val = c->opr[2].val & c->opr[1].val; + + if (s->emulating) pset_psw(c, 0, 0xa); + // set_psw(2, 0x30); + + } + + +void sub(SBK *s, INST *c) + { // 2 or 3 op + calc_mult_ops(s,c); + + c->opr[c->numops].val = c->opr[2].val - c->opr[1].val; + + // sub is a BORROW not a carry + if (s->emulating) + { + set_psw(c, c->opr[c->numops].val, c->opr[c->numops].wfend, 0x3e); + c->psw ^= 2; // complement of carry + } + } + + +void cmp(SBK *s, INST *c) + { + // use curops[0] as temp holder for subtract. + // sub is a BORROW not a carry + + calc_mult_ops(s,c); + + if (s->emulating) + { + c->opr[0].val = c->opr[2].val - c->opr[1].val; + set_psw(c, c->opr[0].val, c->opr[1].rfend,0x3e); + c->psw ^= 2; // complement of carry + } + } + +void mlx(SBK *s, INST *c) + { //psw not clear - assume no changes; 2 or 3 op + calc_mult_ops(s,c); + c->opr[c->numops].val = c->opr[2].val * c->opr[1].val; // 2 and 3 ops + // PSW ??? + + } + +void dvx(SBK *s, INST *c) + { + // quotient to low addr, and remainder to high addr + // always 2 op ?? + long x; + int ix; + + calc_mult_ops(s,c); + + ix = c->numops; + + x = c->opr[ix].val; // keep original + if (c->opr[1].val != 0) c->opr[ix].val /= c->opr[1].val; // if 2 ops (wop = 2) + + x -= (x * c->opr[ix].val); // calc remainder + + if (c->opr[ix].rfend > 7) // long/word + { + c->opr[ix].val &= 0xffff; + c->opr[ix].val |= (x << 16); // remainder + } + else + { // word/byte + c->opr[ix].val &= 0xff; + c->opr[ix].val |= (x << 8); // remainder + } + +// PSW ?? + } + + +void calc_shift_ops(SBK *s, INST *c) +{ + short b; + b = g_byte(s->nextaddr+1); // first data byte after (true) opcode + // set_opdata(s->nextaddr+1,7); + op_calc (b, 1,c); // 5 lowest bits only from firstb, from hbook + + if (b < 16) op_imd(c); // convert op 1 to absolute (= immediate) shift value + op_calc (g_byte(s->nextaddr+2),2,c); + // set_opdata(s->nextaddr+2,7); + s->nextaddr += c->numops + 1; + } + +void shl(SBK *s, INST *c) + { + + /* manual - The right bits of the result are filled with zeroes. + The last bit shifted out is saved in the carry flag. + * sticky appears to be last carry ? + */ + + long mask, v; + + calc_shift_ops(s,c); + v = c->opr[2].val; + v <<= c->opr[1].val; // as a 64 long, for DW shifts + + if (s->emulating) + { + set_psw(c, v, c->opr[2].wfend,0x30); // Z and N + mask = get_sizemask(c->opr[1].rfend) + 1; // get to 'size + 1' bit (= last shifted) + if (v & mask) c->psw |= 2; else c->psw &= 0x3d; // set or clear carry flag + } + c->opr[2].val = v; + } + +void shr(SBK *s, INST *c) + { +// arith shift vs uns ?? + + /* manual The last bit shifted out is saved in the carry flag. + The sticky bit flag is set to indicate that, during a right shift, a “1” has been shifted into the + carry flag and then shifted out. +*/ + + calc_shift_ops(s,c); + + + // c->psw &= 0x3b; // clear sticky first + + c->opr[2].val >>= c->opr[1].val; // always 2 + +// if (!s->scanning) return; + // and need to sort out carry and sticky here.... + } + +void inc(SBK *s, INST *c) + { + do_dflt_ops(s,c); + // ALWAYS 1 (only autoinc is one or two) + c->opr[1].val++; + + if (s->scanning) add_dtk(s,c); + if (s->emulating) + { + set_psw(c, c->opr[1].val, c->opr[1].rfend, 0x3e); + } + + } + +void dec(SBK *s, INST *c) + { + do_dflt_ops(s,c); + // ignore for scans + c->opr[1].val--; + if (s->emulating) + { + + set_psw(c, c->opr[1].val, c->opr[1].rfend, 0x3e); + } + } + + +int calc_jump_ops(SBK *s, int jofs, INST *c) +{ + // s->nextaddr must correctly point to next opcode. + // returns jump address + + OPS *o; + + c->opcsub = 0; //no multimode + jofs += s->nextaddr; // offset as calc'ed from NEXT opcode + jofs = nobank(jofs); // pcj allow for wrap in 16 bits + + o = c->opr+1; // set up opr[1] as a jump (=address) + o->adr = 1; + o->rgf = 0; + o->rbs = 0; + o->addr = codebank(jofs, c); // add CODE bank for jumps + if (val_rom_addr(o->addr)) o->val = g_val(o->addr,0, 15); // get value of jump address for display ?? + else o->val = 0; + jofs = c->opr[1].addr; // now is full address with bank + return jofs; + } + + +void cjm(SBK *s, INST *c) + { // conditional jump, (short) + // all types here based upon psw + + s->nextaddr += 2; + calc_jump_ops(s,g_val(s->nextaddr-1,0,39),c); //signed byte offset + + // set_opdata(s->nextaddr-1,7); + + if (s->scanning) + { + do_sjsubr(s, c, J_COND); + return; + } + +if (s->emulating) { + +// do actual jump if emulating +switch (c->opcix) + { + + case 54: // JNST STK sticky is 1 + if (!(c->psw & 1)) s->nextaddr = c->opr[1].addr; + break; + + case 55: // JST + if ((c->psw & 1)) s->nextaddr = c->opr[1].addr; + break; + + case 56: // JNC CY carry is 2 + if (!(c->psw & 2)) s->nextaddr = c->opr[1].addr; + break; + + case 57: // JC + if (c->psw & 2) s->nextaddr = c->opr[1].addr; + break; + + case 58: // JNV OV overflow is 8 + if (!(c->psw & 8)) s->nextaddr = c->opr[1].addr; + break; + + case 59: // JV + if (c->psw & 8) s->nextaddr = c->opr[1].addr; + break; + + case 60: // JNVT OVT overflow trap is 4 + if (!(c->psw & 4)) s->nextaddr = c->opr[1].addr; + break; + + case 61: // JVT + if (c->psw & 4) s->nextaddr = c->opr[1].addr; + break; + + case 62: // JGTU C (2) set and Z (0x20) unset + if ((c->psw & 0x22) == 2) s->nextaddr = c->opr[1].addr; + break; + + case 63: // JLEU C unset OR Z set + if ((c->psw & 0x22) != 2) s->nextaddr = c->opr[1].addr; + break; + + case 64: // JGT N (16) unset and Z (32) unset + if (!(c->psw & 0x30)) s->nextaddr = c->opr[1].addr; + break; + + case 65: // JLE N set OR Z set + if (c->psw & 0x30) s->nextaddr = c->opr[1].addr; + break; + + case 66: // JGE N not set N is 16 + if (!(c->psw & 16)) s->nextaddr = c->opr[1].addr; + break; + + case 67: // JLT N (negative) is set + if (c->psw & 16) s->nextaddr = c->opr[1].addr; + break; + + case 68: // JE (Z set) Z is 32, zero flag + if (c->psw & 32) s->nextaddr = c->opr[1].addr; + break; + + case 69: // JNE (Z not set) + if (!(c->psw & 32)) s->nextaddr = c->opr[1].addr; + break; + + default: + break; + } + +#ifdef XDBGX +if (s->nextaddr == c->opr[1].addr) // debug confirm jump taken + { + DBGPRT(1,"cjumpE %x->%x (psw=%x)", c->ofst, c->opr[1].addr, c->psw); + } +#endif + +} // end of emulating + } + +void bjm(SBK *s, INST *c) + { // Bit Jump. JB, JNB, + // add bit operand but don't change opcode size + int xofst; + xofst = s->nextaddr; + s->nextaddr += 3; + + c->opr[3].bit = 1; // setup bit number in op3 + c->opr[3].rgf = 0; + c->numops++; // local change, NOT opcode size. + op_calc (g_byte(xofst) & 7, 3,c); // bit number (in opcode) + op_calc (g_byte(xofst+1), 2,c); // register + // set_opdata(xofst+1,7); + calc_jump_ops(s, g_val(xofst+2,0,39),c); // jump destination, signed byte + // set_opdata(xofst+2,39); + if (s->scanning) + { + do_sjsubr(s, c, J_COND); + return ; + } + + // 0 is LS bit .... jnb is 70, jb 71 + +if (s->emulating) + { + + if (check_backw(c->opr[1].addr, s, J_STAT)) return; + + xofst = (1 << c->opr[3].val); // set mask + // bit set and JB + if ((c->opr[2].val & xofst) && (c->opcix & 1)) s->nextaddr = c->opr[1].addr; + // bit not set and JNB + if (!(c->opr[2].val & xofst) && !(c->opcix & 1)) s->nextaddr = c->opr[1].addr; + } + + } + +void sjm(SBK *s, INST *c) + { // a short jump + // CANNOT do this as part bit field as it is REVERSE of std word + + int jofs; + + jofs = sjmp_ofst(s->nextaddr); + + // set_opdata(s->nextaddr+1,7); + s->nextaddr += 2; + + calc_jump_ops(s, jofs,c); + + if (s->scanning) + { + do_sjsubr(s, c, J_STAT); + return; + } + +if (s->emulating) + { + if (check_backw(c->opr[1].addr, s, J_STAT)) return; + s->nextaddr = c->opr[1].addr; // go direct in emu mode + } + + } + + +void ljm(SBK *s, INST *c) + { //long jump + int jofs; + + s->nextaddr += 3; + jofs = g_val(s->nextaddr-2,0,47); //signed word + calc_jump_ops(s, jofs,c); + // set_opdata(s->nextaddr-2,47); + if (s->scanning) + { + do_sjsubr(s, c, J_STAT); + return; + } + +if (s->emulating) + { + if (check_backw(c->opr[1].addr, s, J_STAT)) return; + s->nextaddr = c->opr[1].addr; // go direct in emu mode + } + } + + + +void cll(SBK *s, INST *c) + { // long call + SBK *n; + s->nextaddr += 3; + calc_jump_ops(s,g_val(s->nextaddr-2,0,47),c); //signed word + // set_opdata(s->nextaddr-2,47); + + + /* if (s->lscan) + { + #ifdef XDBGX + if (anlpass < ANLPRT) DBGPRT(1,"ignore call %x from %x", c->opr[1].addr, s->curaddr); + #endif + return; + } +*/ + + if (s->scanning) + { + #ifdef XDBGX + if (anlpass < ANLPRT) DBGPRT(1,"call %x from %x", c->opr[1].addr, s->curaddr); + #endif + do_sjsubr(s, c, J_SUB); + return; + } + +if (s->emulating) + { + SUB *sub; + sub = get_subr(c->opr[1].addr); + if (sub && sub->size) + { + #ifdef XDBGX + DBGPRT(1,"Emulation IGNORED, SUB %x has cmd args (%d)", c->opr[1].addr, sub->size); + #endif + s->nextaddr += sub->size; + return; + } + + #ifdef XDBGX + DBGPRT(1,"** enter %x from %x", c->opr[1].addr, s->curaddr); + #endif + + n = add_escan(c->opr[1].addr, s); + fakepush(s, c->opr[1].addr,1); // std subr call, push return details onto stack + + scan_blk(n, &einst); // recursive + +// break here ??? if args set ?? + + } + } + + +void scl(SBK *s, INST *c) + { // short call + int jofs; + SBK *n; + + jofs = sjmp_ofst(s->nextaddr); + + // set_opdata(s->nextaddr+1,7); + s->nextaddr+=2; + calc_jump_ops(s,jofs,c); + + /* if (s->lscan) + { + #ifdef XDBGX + if (anlpass < ANLPRT) DBGPRT(1,"ignore call %x from %x", c->opr[1].addr, s->curaddr); + #endif + return; + } +*/ + + if (s->scanning) + { + + #ifdef XDBGX + if (anlpass < ANLPRT) DBGPRT(1,"call %x from %x", c->opr[1].addr, s->curaddr); + #endif + + do_sjsubr(s, c, J_SUB); + return; + } + + if (s->emulating) + { + SUB *sub; + sub = get_subr(c->opr[1].addr); + if (sub && sub->size) + { + #ifdef XDBGX + DBGPRT(1,"Emulation IGNORED, SUB %x has cmd args (%d)", c->opr[1].addr, sub->size); + #endif + s->nextaddr += sub->size; + return; + } + + + + + #ifdef XDBGX + DBGPRT(1,"** enter %x from %x", c->opr[1].addr, s->curaddr); + #endif + + + + n = add_escan(c->opr[1].addr, s); + fakepush(s, c->opr[1].addr,1); // std subr call, push return details onto stack + + scan_blk(n, &einst); // recursive (as true core) + } + + } + + +void ret(SBK *s, INST *c) + { // return + + s->nextaddr++; + calc_jump_ops(s, -1,c); // do as loopstop jump + + + if (s->scanning) + { + // #ifdef XDBGX + // if (chrgst.num > 0) DBGPRT(1,"Clear rgstats"); + // #endif + + // clearchain(&chrgst); // clear regstat want to ditch this !! + basepars.rambnk = 0; // safety + do_sjsubr(s, c, J_RET); + return; + } + + if (anlpass < ANLPRT) { + if (s->emulating) + { + fakepop(s); // check & drop the stack call + } + + #ifdef XDBGX + DBGPRT(0,"%x RET", s->curaddr); + if (s->emulating) DBGPRT(0," E"); + DBGPRT(2,0); + #endif + + +// always end this block + end_scan(s); + + } + +} + + + +void djm(SBK *s, INST *c) + { // djnz + int xofst; + + + xofst = s->nextaddr; + s->nextaddr += 3; + op_calc (g_byte(xofst+1), 2,c); // do register + // set_opdata(xofst+1,7); + calc_jump_ops(s,g_val(xofst+2,0,39),c); //signed byte + // set_opdata(xofst+2,39); + if (s->scanning) + { + do_sjsubr(s, c, J_COND); + return ; + } + + if (s->emulating) + { + + RST *r; + OPS *o; + o = c->opr+2; + c->opr[2].val--; + if (c->opr[2].val > 0) s->nextaddr = c->opr[1].addr; + else + { + r = get_rgstat(o->addr); + if (r) + {chdelete(&chrgst,chrgst.lastix,0); // delete this - loop variable + #ifdef XDBGX + DBGPRT(1,"delete rgstat %x", o->addr); + #endif +} + } + } + } + +void skj(SBK *s, INST *c) + { // skip, = jump(pc+2) as static jump + s->nextaddr++; + + calc_jump_ops(s,1,c); + + if (s->scanning) + { + do_sjsubr(s, c, J_STAT); + return; + } + + if (s->emulating) + { + s->nextaddr++; // skip next byte + } + } + + + + +void php(SBK *s, INST *c) + { + // push flags (i.e. psw) + + s->nextaddr++; + s->php = 1; + + if (numbanks) + { + fakepush(s, c->psw, 4); + #ifdef XDBGX + if (anlpass < ANLPRT) DBGPRT(0,"%x PUSHP ", c->ofst); + // DBG_stack(s->emulating); + #endif + } + + } + +void ppp(SBK *s, INST *c) + { + // pop flags (i.e. psw) + // ignore in scan + + s->nextaddr++; + s->php = 0; + + FSTK *x; + if (s->emulating) + { + x = emulstack; //else x = fstack; + + if (numbanks) + { + get_stack(s, x, 4); // for error report + fakepop(s); + } +} + + + } + +void nrm(SBK *s, INST *c) + { + do_dflt_ops(s,c); + + // from Ford hbook shift left double until b31 = 1 + // and result size is double too....as it's shifted... + + // TO DO + } + + +void sex(SBK *s, INST *c) + { + // sign extend - not necessary in this setup ? + + do_dflt_ops(s,c); + } + + +void clc(SBK *s, INST *c) + { + // clear carry + c->psw &= 0x3d; + s->nextaddr++; + } + +void stc(SBK *s, INST *c) + { + // set carry + c->psw |= 2; + s->nextaddr++; + } + +void die(SBK *s, INST *c) + { + // enable & disable ints + + s->nextaddr++; + } + +void clv(SBK *s, INST *c) + { // clear trap + c->psw &= 0x3b; + s->nextaddr++; + } + +void nop(SBK *s, INST *c) + { + s->nextaddr++; + } + +void bka(SBK *s, INST *c) + { // RAM bank swopper 8065 only + + int bk; + // According to Ford book, this only sets PSW, so could be cancelled by a POPP + // or PUSHP/POPP save/restore, so may need a PREV_BANK + + bk = c->opcix - 107; // new RAM bank + basepars.rambnk = bk * 0x100; // establish new offset + + op_imd(c); // mark operand 1 as immediate + c->opr[1].val = bk; + c->opr[1].rfend = 7; // byte sized; + c->numops = 1; // fake a single byte [imd] op + s->nextaddr++; + + #ifdef XDBGX + if (s->scanning) DBGPRT(1,"New Rambank = %x at %x", bk, s->curaddr); + #endif + } + + +void do_code (SBK *s, INST *c) + +{ + /* analyse next code statement from ofst and print operands + * s is a scan blk, c is holding instance for decoded op. + * pp-code calls this with a dummy scan block in print phase + */ + + int x, xofst, indx, psw; + OPC *opl; + OPS *o; + + basepars.codbnk = g_bank(s->curaddr); // current code bank from ofst + + if (s->stop) + { + #ifdef XDBGX + DBGPRT(1,"Already Scanned s %x c %x e %x", s->start, s->curaddr, s->nextaddr); + #endif + return; + } + + if (s->curaddr > maxpcadd(s->start) || s->curaddr < minadd(s->start)) + { + #ifdef XDBGX + if (anlpass) DBGPRT(1,"Scan > Illegal address %x from blk %x, STOP", s->curaddr, s->start); + #endif + s->stop = 1; + return; + } + + + if (!val_rom_addr(s->curaddr)) + { + #ifdef XDBGX + if (anlpass) DBGPRT(1,"Invalid code address %05x", s->curaddr); + #endif + s->inv = 1; + s->stop = 1; + s->nextaddr = s->curaddr+1; + return; + } + + xofst = s->curaddr; // don't modify curaddr, use temp instead + +//---------------------------- + + psw = c->psw; // keep psw from supplied instance + memset(c,0,sizeof(INST)); // clear entire INST entry + + x = g_byte(xofst); + + indx = opcind[x]; // opcode table index + opl = opctbl + indx; + + // Ford book states opcodes can have rombank(bank) AND fe as prefix (= 3 bytes) + + if (indx == 112) // bank swop - do as prefix + { + if (!P8065) indx = 0; // invalid for 8061 + else + { + int tbk; + xofst++; + tbk = g_byte(xofst++); // bank number (next byte) + tbk &= 0xf; + tbk++; + + #ifdef XDBGX + if (anlpass && anlpass < ANLPRT && !bkmap[tbk].bok) + DBGPRT(1,"ROMBNK - Invalid bank %d (%x)", tbk, c->ofst); + #endif + c->bank = tbk; // set bank for this opcode + x = g_byte(xofst); // get true opcode + indx = opcind[x]; + } + } + + if (indx == 111) + { // this is the SIGN prefix, find alternate opcode + xofst++; + x = g_byte(xofst); + indx = opcind[x]; // get opcode + opl = opctbl + indx; + if (opl->sign) indx += 1; // get prefix opcode index instead + else indx = 0; // or prefix not valid + } + + if (opl->p65 && !P8065) indx = 0; // 8065 only + + if (indx > 110) indx = 0; // may happen if totally lost..... + + opl = opctbl + indx; + + if (!indx) + { + s->inv = 1; + s->nextaddr = xofst + 2; //to get nextaddr right for end_scan + #ifdef XDBGX + if (anlpass) DBGPRT(1,"Invalid opcode (%x) at %x [%d]", x, s->curaddr, anlpass); + #endif + + if (s->scanning) end_scan (s); + else if (anlpass) wnprt(1,"Invalid opcode at %x [%d]", s->curaddr, anlpass); // report only once + + return; + } + + c->opcsub = x & 3; // this is valid only for multimode opcodes + c->opcix = indx; // opcode index + c->sigix = opl->sigix; // sig index + c->ofst = s->curaddr; // original start of opcode + + set_opstart(s->curaddr); // code start flag + + // These next items can be changed by opcodes, this is default setup + + c->numops = opl->nops; + c->psw = psw; // carry over psw from last instance + o = c->opr; // shortcut to operands (4 off) + c->wop = opl->wop; // write destination index (or zero) + o[opl->wop].wfend = opl->wfend; // set write size (or zero) + o[1].rfend = opl->rfend1; // operands - read size + o[2].rfend = opl->rfend2; + o[3].rfend = opl->rfend3; + o[1].rgf = 1; // set register ops by default + o[2].rgf = 1; + o[3].rgf = 1; + + +//if (s->curaddr == 0x9342a) +//if (s->curaddr == 0x9224e) +//{ +//DBGPRT(1,0); +//} + + + s->nextaddr = xofst; // address of actual opcode - for eml call + + opl->eml(s, c); // opcode handler - modifies nextaddr + // s->nextaddr now points to next opcode + upd_watch(s, c); + + + } + + + +/************************************************************************** +* opcode printout - calls do_code +***************************************************************************/ + +uint pp_code (uint ofst, LBK *k) +{ + int i, cnt, inx, ainx, eqp, jf; + + const char *tx ; + const OPC* opl; + INST *c; + // JMP *j; + +//if (ofst == 0x9211f) +//{ //xdt2 +//DBGPRT(1,0); +//} + + + + memset(&prtscan,0, sizeof(SBK)); // clear it first + c = &cinst; + prtscan.curaddr = ofst; + prtscan.start = ofst; + do_code(&prtscan, c); + + fcmnt.minst = c; // for later comments +// preset_comment(ofst); // begin and end reqd ?? + + if (prtscan.inv) + { + pp_hdr (ofst, opctbl->name, 1); // single invalid opcode (= !INV!) + return prtscan.nextaddr; + } + + // BANK - For printout, show bank swop prefix as a separate opcode + // This is neatest way to show addresses correctly in printout + // internally, keep 'ofst' at original address, but update curaddr for printout. + + if (c->bank) + { + pp_hdr (ofst, "rombk", 2); + pstr (0,"%d", c->bank-1); + prtscan.curaddr +=2; + } + + inx = c->opcix; + ainx = inx; // safety + opl = opctbl + inx; + + cnt = prtscan.nextaddr - prtscan.curaddr; + pp_hdr (prtscan.curaddr, opl->name, cnt); + + i = c->numops; + while (i) + { + p_oper (c, i); // code printout + if (--i) pchar (','); + } + + /********************************************** + * source code generation from here + * check and do substitutes and cleanups, name resolution etc. + *********************************************/ + + +//if (ofst == 0x92ba9) +//{ //xdt2 +//DBGPRT(1,0); +//} + + + + if (PSSRC) + { + if (opl->sce) + { // skip if no pseudo source + p_pad(PSCEPOSN); + // pstr("|%d ", indent); // experiment, for lining up and new brackets + + tx = opl->sce; // source code, before any mods + eqp = 1; // equals not printed yet + + // any mods to source code go here + + fix_sym_names(&tx,c); // for rbases, 3 ops, names, indexed fixes etc. + // shift_comment(c); // add comment to shifts with mutiply/divide - if (curinst.opcix < 10 && ops->imd) + +// j = + find_fjump (ofst, &jf); + + if ((jf & 1) && *tx) + { // invert jump logic for source printout + if (inx > 53 && inx < 72) // all conditional jumps (except djnz) + { + ainx = inx ^ 1; // get alternate opcode index if jump found. + tx = opctbl[ainx].sce; // and source code +//indent++; + } + else jf = 0; + } + + // do 'psuedo source code' output + + while (*tx) + { + + switch (*tx) // 1-12 used 1-31 available (0x1 to 0x1f) + { + case 1: + case 2: + case 3: + case 0x11: // + 0x10 = print size,sign + case 0x12: + case 0x13: + + p_opsc (c, *tx, eqp); + break; + + case 4: + // replace bit masks with flag names - ANDs and ORs //MORE// if (ix > 9 && ix < 14) || (ix > 29 && ix < 34) + bitwise_replace(&tx, c); + break; + + case 5: + // subr call, may have answer attached. + pp_answer(c); + p_opsc (c, 1, eqp); + prtscan.nextaddr += pp_subargs(c,prtscan.nextaddr); + break; + + case 6: // overflow + do_ovf(c, &tx, ainx); + break; + + case 7: // conditonal jumps + do_cond_ops(c, &tx, ainx); + break; + + case 8: // carry + + do_carry(c, &tx, ainx); // JC and JNC + break; + + case 9: + // if (j<3) always true at present +// make this bitmap instead of switch ??? + + switch (jf) //jump handling + { + case 1 : + pstr (0," {"); // [if] case + break; + case 2 : + pstr (0,"return;"); // static jump to a return + break; + case 3: + pchar(';'); + // fall through + case 4: + pstr(0,"} else {"); + break; + default: + pstr (0,"goto "); + p_opsc (c,1,0); + pchar (';'); + break; + } + +/*if (j && j->retn) + { + // if (jf == 1) { pstr(" }"); j->cbkt = 0; } + + + acmnt.minst = c; + acmnt.ctext = nm; + acmnt.ofst = prtscan.nextaddr; + sprintf(nm, "## -> Return"); + +} */ + + break; + + case 11: + p_indl(); // 0xb print new line with indent (DJNZ etc) + break; + + case '=' : + eqp = 0; // equals printed, read syms after this + // fall through + default: + pchar(*tx); + } // end switch tx + tx++; + } // end while tx + } // end if opl->sce + } // end if PSSRC + else + { // subr arguments without source code option - must still print and skip operands + if (!PSSRC && (inx == 73 || inx == 74 || inx == 82 || inx == 83)) // CALL opcodes + { + prtscan.nextaddr += pp_subargs(c,prtscan.nextaddr); + } + } + + i = find_tjump (prtscan.nextaddr); // add any reqd close bracket(s) + + while (i > 0) + { // i = number of brackets (=jumps) found + pstr (0," }"); + i--; + } + + // if (PACOM) parse_comment(&acmnt,0); // print auto comment if one created, but need to check ofst !! + +/* + { + OPS *s; // extra debug printout + + // pp_comment(ofst+cnt); // do comment first + if (opctbl[curinst.opcix].nops) + { + p_pad(cposn[3]); //CDOSTART); + pstr(" SVA "); + + for (i=curinst.numops; i>=0; i--) + { + s = curops+i; + if (s->addr + s->val) + { + // if(curinst.opl->wop == i && i) pstr("*"); + pstr("%c[",i+0x30); + + if (s->sym) pstr("%s ",get_sym_name(0,s->addr,0)); // temp fix..... + + pstr("%x %x",s->val, s->addr); +// if (!i) pstr (" T%x", curinst.tval); //new temp value + pstr("]%x, ",s->ssize); + } + } + } + } // end dubug */ + + // add an extra blank line after END BLOCK opcodes, via comments + // 75, 81 are jumps 76->79 are rets + + if (prtscan.nextaddr <= k->end) + { + if (inx > 75 && inx < 80) pp_comment(prtscan.nextaddr,1); // Rets + if (inx == 75 || inx == 81) pp_comment(prtscan.nextaddr,1); //jumps + } + return prtscan.nextaddr; +} + + + +// --------------------------------------------------------------------------------------------- + + +int do_error(CPS *c, const char *err, ...) + { + // cmnd string in flbuf.... + // use vfprintf directly as it does not work + // if called via wnprt + + const char *t, *x; + va_list args; + int val; + + if (!err) return 0; // safety + if (!*err) return 0; // safety + + if (!c->firsterr) + { + fprintf (fldata.fl[2], "## %*s",c->maxlen,flbuf); //print cmd string once + c->firsterr = 1; + } + + va_start (args, err); + t = err; + + // fist char expected to be a 1 or a 2 for error and warning. + + c->error = 0; // warning (default), continue + if (*t == 15) c->error = 2; // fatal error + if (*t == 1) c->error = 1; // error + + while (*t) + { //go down err string and look for special chars + + if (*t < 16) + { //print compressed str + val = *t; + fprintf(fldata.fl[2],"%s", cmptxts[val]); + } + else + if (*t == '%') + { + x = t+1; //next char + if (*x == 'd') + { + val = va_arg(args, int); + fprintf(fldata.fl[2],"%d", val); + t+=1; + } + else + if (*x == 'x') + { + val = va_arg(args, int); + fprintf(fldata.fl[2],"%x", val); + t+=1; + } + else + if (*x == 's') + { + x = va_arg(args, char*); + fprintf(fldata.fl[2],"%s", x); + t+=1; + } + + + } + else fputc(*t,fldata.fl[2]); + t++; + } + + + va_end(args); + wnprt(1,0); // newline + return 1; +} + + +uint set_rbas (CPS *c) +{ + RBT *x; + +//addresses checked already in parse_cmd + + if (c->npars < 3) + { + c->p[2] = 0; // default address range to ALL (zeroes) otherwise done by cmd + c->p[3] = 0xfffff; // max possible address + } + + x = add_rbase(c->p[0], c->p[1], c->p[2], c->p[3]); + + if (chbase.lasterr) return do_error(c,etxts[chbase.lasterr]); + + x->cmd = 1; // by command + return 0; +} + + +uint set_opts (CPS *c) +{ + //opts bitmask in p[0] + // int x; + // if (P8065) x = 1; else x = 0; + + cmdopts |= c->p[0]; + + if (numbanks) cmdopts |= 0x8; + + #ifdef XDBGX + DBGPRT(1,0); + DBGPRT(0,"SetOpts = "); + prtflgs(cmdopts, DBGPRT); + DBGPRT(2,0); + #endif +return 0; + +} + +uint clr_opts (CPS *c) +{ + cmdopts &= (~c->p[0]); + + #ifdef XDBGX + DBGPRT(1,0); + DBGPRT(0,"ClrOpts = "); + prtflgs(cmdopts, DBGPRT); + DBGPRT(2,0); + #endif +return 0; + +} + + + + + + + +void set_data_vect(LBK *blk) +{ + int ofst, xofst, end, pc; + int bank; + ADT *a; + + // for structs only + if (PMANL) return; + if (!blk || blk->fcom != C_STCT) return; + + bank = g_bank(blk->start); // what if bank is NOT same as struct + + for (ofst = blk->start; ofst < blk->end; ofst += blk->size) + { + a = start_adnl_loop(&chadnl, blk->start); //(ADT*) chmem(&chadnl,0); + // a->fid = blk; + + end = ofst + blk->size-1; + xofst = ofst; + while ((a = get_next_adnl(&chadnl,a)) && xofst < end) + { + if (a->vaddr) + { + pc = g_word(xofst); + pc |= bank; // this assumes vect is always in same bank + add_scan (pc, J_SUB, 0); // auto adds subr + } + xofst += cellsize(a); // always 2 No........ + } + } +} + +void add_ans(uint start, ADT *ca) +{ //special adt copy for subr answers + ADT *a; + +// insert ino answer chain + a = add_adt(&chans, start, 1); // add new one + *a = *ca; + a->fkey = set_adt_fkey(start,1); + + #ifdef XDBGX + DBGPRT(0," with answer params"); + prt_adt(&chans, start, 0, DBGPRT); + DBGPRT(1,0); + #endif + +} + +void cpy_adt(CPS *c, uint start) //, int start) +{ + // move adt blocks from cmnd to new LBK + // NB. create NEW ones as fid will be in worng place otherwise in adt chain + +// int i; + uint seq; + ADT *a, *cp; + if (!start) return; // must have an fid + if (!c->seq) return; // no addnl blocks + + a = start_adnl_loop(&chadcm, 0); //c->start); //(ADT*) chmem(&chadnl,0); + // a->fid = c->fid; //command adnl entries + + while ((a = get_next_adnl(&chadcm,a))) + { + // if (a->seq > c->seq) break; don't need it, chain cleared each command + if (a->ans) + { + add_ans(start,a); // add answer (in different chain) + } + else + + if (a->cnt) + { // use cnt as null checker + if (c->fcom == C_ARGS || c->fcom == C_SUBR) a->pfw = 0; // clear all pfw + cp = add_adt(&chadnl,start,256); // add new one with correct fid and seq + seq = cp->fkey; // save key + *cp = *a; //copy block + cp->fkey = seq; // restore key + //set decimal for tabs and funcs + if ((c->fcom == C_TABLE || c->fcom == C_FUNC) && cp->prdx == 0) cp->prdx = 2; + } + } + + #ifdef XDBGX + DBGPRT(0," with addnl params"); + prt_adt(&chadnl,start, 0, DBGPRT); + DBGPRT(1,0); + #endif + +} + + + +// amend this for everything - change end if relevant +// to command + + +int chk_csize(CPS *c) + { + int bsize, tsize, row, bytes; + DIRS *d; + + // if (!c->fcom) return 0; // not for fill cmd + + d = dirs + c->fcom; + // check addnl levels allowed + +// row = d->maxadt; //max levels +// bytes = d->minadt -1; // min levels + + if (!d->maxadt && c->seq) + { //no extra allowed + return do_error(c,"\001Extra\012\005"); + } + + if (c->seq < d->minadt ) + { // not enough levels + return do_error(c,"\001Extra\012\004"); + } + + if (c->seq > d->maxadt) + { + do_error(c,"\002Extra\012\010"); + c->seq = d->maxadt; + } + + +// check extra levels and sizes + + bsize = c->p[1] - c->p[0]+1-c->term; // byte size of cmd entry in total + + if (d->defsze) tsize = d->defsze; else tsize = c->tsize; + + if (!tsize) + { + do_error(c,"Row size is ZERO !!"); + tsize = 1; + } + + if (tsize > bsize) + { + if (c->p[0] == c->p[1] || d->defsze) + { // if end = start, or preset sizes, allow change + c->p[1] = c->p[0] + tsize-1; + bsize = tsize; + do_error(c,"\002End increased to "); + paddr(c->p[1],0,wnprt); + } + else return do_error(c,"\001End too low for start+data definition"); + } + + row = bsize/tsize; // number of whole rows + bytes = row * tsize; // number of bytes for whole rows + + if (bytes != bsize) + { + +// if close to right ?? + bytes += c->p[0] + c->term - 1; + c->p[1] = bytes; + + // check y against end ? + + do_error(c,"\002End inconsistent with size, reduced to %x", bytes); + + } + + return 0; + } + + +uint set_func (CPS *c) +{ + // for functions. 2 levels only + + int val, fend, startval; + LBK *blk; + ADT *a, *b; + // size row vs. start>end check first + + a = get_adnl(&chadcm, 0, 1); + + if (c->seq < 2) + { // add another level if only one specified + b = add_adt(&chadcm, 0, 256); + *b = *a; // copy all data + b->fkey = 2; // but reset seq + c->seq = 2; // levels = 1; // up levels + c->tsize = totsize(&chadcm, 0); + } + + // c->tsize must be correct first + + if (chk_csize(c)) return 1; + + fend = a->fend; //size and sign as specified + + // check start value consistent with sign + + val = g_val(c->p[0], 0, fend); + startval = get_startval(fend); // start value from fend SIGN + + if (val != startval) // try alternate sign....does not change command though + { + fend ^= 32; // swop sign flag + startval = get_startval(fend); // new start value + if (val != startval) do_error(c,"\002Function Start value\003"); + else do_error(c,"\002First value (%x) indicates wrong sign specified", val); + } + + blk = add_cmd (c->p[0], c->p[1], c->fcom|C_CMD,0); // start,end, cmnd. by cmd + if (chcmd.lasterr) return do_error(c,etxts[chcmd.lasterr]); + + cpy_adt(c,blk->start); + blk->size = totsize(&chadnl, blk->start); + + return 0; +} + +uint set_tab (CPS *c) +{ + // tables, ONE level only + + LBK *blk; + + // size row vs. start>end check first + if (chk_csize(c)) return 1; + + blk = add_cmd (c->p[0], c->p[1], c->fcom|C_CMD,0); // start,end, cmnd. by cmd + if (chcmd.lasterr) return do_error(c,etxts[chcmd.lasterr]); + + + cpy_adt(c,blk->start); + blk->size = totsize(&chadnl,blk->start); + + if (c->argl) blk->argl = 1; // default is argl OFF + return 0; +} + + +uint set_stct (CPS *c) +{ + // structures + + LBK *blk; + + // size row vs. start>end check first + if (chk_csize(c)) return 1; + + blk = add_cmd (c->p[0], c->p[1], c->fcom|C_CMD,0); // start,end, cmnd. by cmd + if (chcmd.lasterr) return do_error(c,etxts[chcmd.lasterr]); //do_error(c,2,0); + + cpy_adt(c,blk->start); + blk->size = totsize(&chadnl,blk->start); + + blk->term = c->term; // terminating byte flag (struct only) + if (c->argl) blk->argl = 1; // default is arg layout + + set_data_vect(blk); // for Refs out in structs + return 0; +} + + + + + +uint set_data (CPS *c) +{ + // change for word, byte, long + // no addnl is fine + + LBK *blk; + ADT *a; + + if (chk_csize(c)) return 1; + + blk = add_cmd (c->p[0], c->p[1], c->fcom|C_CMD,0); // start,end, cmnd. by cmd + if (chcmd.lasterr) return do_error(c,etxts[chcmd.lasterr]); + + a = c->adnl; + + if (a && bytes(a->fend) < c->fcom) a->fend = (c->fcom * 8) -1; // safety for command sizes + + //if (a && a->bsize < c->fcom) a->bsize = c->fcom; + + cpy_adt(c,blk->start); + blk->size = totsize(&chadnl,blk->start); + + return 0; +} + + +uint set_sym (CPS *c) +{ + int fstart,fend,write; //,i, flags + ADT *a; + SYM *s; + +// flagsword. +// allow/inhibit if immediate... + + + write = 0; // read or write + // flags = 0; // flags word + fstart = 32; + fend = 31; + + if (c->npars < 3) + { + c->p[1] = 0; // default address range to ALL (zeroes) otherwise done by cmd + c->p[2] = 0xfffff; // max possible address + } + +// only one adt level allowed + +// for (i = 0; i <= c->levels; i++) + // { + a = get_adnl(&chadcm, 0, 1); //cmdadnl + i; //->adnl; + if (a) + { + if (a->fend == 15) write = 1; // write 'W' + + if (a->enc == 7 && a->fstart < 31) + { // bit marker 'B' + fstart = a->fstart; + fend = a->data & 0x1f; + } + } + + s = add_sym(c->symname, write|C_CMD, c->p[0], fstart, fend, c->p[1], c->p[2]); + + if (chsym.lasterr && chsym.lasterr != E_DUPL) return do_error(c,etxts[chsym.lasterr]); + + if (c->flags) s->flags = 1; // with flags (not for bits...) + if (c->names) s->names = 1; // with flags (not for bits...) + if (c->imm) s->immok = 1; + return 0; +} + + + + +uint set_subr (CPS *c) +{ + + SUB *xsub; + SPF *f; + + // if (chk_csize(c)) return 1; can't do this !! + + xsub = add_subr(c->p[0]); + + // can do name here, even if a duplicate............. + + if (!xsub || xsub->cmd) + { //allow def of existsing system one + if (chsubr.lasterr) return do_error(c,etxts[chsubr.lasterr]); + } + else xsub->cmd = 1; + + // do any special functions first + + f = 0; + + if (c->spf) + { // special func is marked + + f = add_spf(xsub->start); + f->spf = c->spf; // start of funcs + f->fendin = anames[f->spf].fendin; + f->fendout = anames[f->spf].fendout; + f->addrreg = c->adreg; // register + f->sizereg = c->szreg; // cols + + #ifdef XDBGX + DBGPRT(0, " with spf = %x ", f->spf); + #endif + } // end special func + + add_scan (c->p[0], J_SUB,0); + + cpy_adt(c, xsub->start); + + xsub->size = totsize(&chadnl,xsub->start); + if (c->cptl) xsub->cptl = 1; // default is argl ON + return 0; + } + + +uint set_time(CPS *c) + { + LBK *blk; + // int val,type,bank; + // uint xofst; + // short b, m, sn; + // SYM *s; + // char *z; + + blk = add_cmd (c->p[0],c->p[1],c->fcom|C_CMD,0); + if (chcmd.lasterr) return do_error(c,etxts[chcmd.lasterr]); + cpy_adt(c,blk->start); + //s = + // add_sym(C_CMD,c->symname,c->p[0],-1, 0,0); // safe for null name + // if (s) s->cmd = 1; + + /* up to here is same as set_prm...now add names + + if (!blk) return 1; // command didn't stick + a = blk->adnl; + if (!a) return 0; // nothing extra to do + sn = 0; // temp flag after SIGN removed + if (a->data) sn = 1; + if (!a->name && !sn) return 0; // nothing extra to do + + if (a->ssize < 1) a->ssize = 1; // safety + + xofst = c->p[0]; + bank = g_bank(xofst); + while (xofst < maxadd(xofst)) + { + type = g_byte (xofst++); // first byte - flags of time size + if (!type) break; // end of list; + + val = g_val (xofst, a->ssize); // address of counter + val |= bank; + + if (a->name) + { + z = nm; + z += sprintf(z, "%s", anames[0].pname); + z += sprintf(z, "%d", anames[0].nval++); + if (type& 0x4) z += sprintf(z,"D"); else z+=sprintf(z,"U"); + if (type&0x20) z += sprintf(z,"_mS"); + else + if (type&0x40) z += sprintf(z,"_8thS"); + else + if (type&0x80) z += sprintf(z,"_S"); + s = add_sym(0,nm,a->data,0,0); // add new (read) sym + } + + xofst+= casz[a->ssize]; // jump over address (word or byte) + + z = nm; + if (type&1) // int entry + { + if (s && sn) + { // add flags word name too + m = g_byte (xofst++); // bit mask + b = 0; + while (!(m&1)) {m /= 2; b++;} // bit number + val = g_byte (xofst++); // address of bit mask + val |= bank; + if (type & 8) z+= sprintf(z,"Stop_"); else z += sprintf(z,"Go_"); + sprintf(z,"%s",s->name); + add_symbit(0 ,nm, val, b, 0, 0); + } + else xofst+=2; // jump over extras + } + } +*/ + + // upd sym to add flags and times + return 0; + } + + +int find_text(BANK *b) +{ + // check for text in end filler block + // text blocks have either "Copyright at ff63" or ".HEX* ff06" in them... + // check address above ff00 for copyright and then check blocks.... + // works for now, more or less + +// need more than this........................ + + // BUT 0FAB has NO COPYRIGHT STRING !!! + + int ans, val, start, ofst, bk, cnt; + + if (nobank(b->maxbk) < 0xdfff) return 0; // not full bank + + if (strncmp("Copyright", (char *) b->fbuf + 0xff63, 9)) return 0; // not found + + // matched Copyright string + bk = g_bank(b->minpc); + + start = (0xff63 | bk); + ofst = start; + + ans = 0; + val = g_byte(ofst); + + while (!val || (val >=0x20 && val <= 0x7f)) + { + ofst++; + val = g_byte(ofst); + } + add_cmd (start,ofst, C_TEXT |C_SYS,0); + add_cmd (ofst+1,b->maxbk, C_DFLT|C_SYS,0); // treat as cmd (cannot override) + + ans = start - 1; + + ofst = (0xff06 | bk); + cnt = 0; + val = g_byte(ofst); + + while (ofst < (0xff63 | bk)) + { // allow a few non printers...6 ? + if (val < 0x20 || val > 0x7f) + { + if (cnt > 5) add_cmd (ofst-cnt,ofst-1, C_TEXT |C_SYS,0); + cnt = 0; + } + else + { + cnt ++; + start = ofst; + } + ofst++; + val = g_byte(ofst); + } + + ans = start-1; + + return ans; + } + + + + + + + + + + + + +void find_fill_txt(BANK *b) + { + +// look for filler at end of bank. + + uint ofst, last; + int cnt, val; + + last = b->maxbk; + b->maxpc = b->maxbk; // default and safety + + ofst = find_text(b); // any text found ? + + if (!ofst) ofst = b->maxbk; else last = ofst; + + cnt = 0; + for (; ofst > b->minpc; ofst--) + { + val = g_byte(ofst); + + if (val != 0xff) + { + cnt++; // count non 0xff + } + if (cnt > 3) break; // 2 non 0xff and no text match + } + + + ofst += 4; // 2 count plus one - may need more for a terminator byte ? + + if (ofst < b->maxbk) + { + // fill at end + add_cmd (ofst,last, C_DFLT|C_SYS,0); + add_aux_cmd (ofst,b->maxpc,C_XCODE|C_SYS,0); // and xcode to match + b->maxpc = ofst; + } + } + + +void mkbanks(int num, ...) +{ + va_list ixs; + int i, j, k; + BANK *b; + + va_start(ixs,num); + + j = 0; + for (i = 3; i < 7; i++) + { + k = va_arg(ixs,int); + b = bkmap + i; + b->dbank = k; +// b->bprt = 1; //print in msg file + j++; + + # ifdef XDBGX + DBGPRT(1,"Set temp bank %d to %d",i, k); + #endif + + if (j >= num) break; + } + va_end(ixs); + +} + + +/* +uint val_cmdbank(uint bk) +{ // 0,1,8,9 only allowed, val by bitmask + if (bk > 15) return 0; + if (bk & 6) return 0; + return 1; +}*/ + + +uint set_bnk (CPS *c) +{ + int i, bk, bank, add; + BANK *b; + + // bank no , file offset, (opt) pcstart, (opt) bkend , (opt) fillstart + + // use bk[2].cmd as marker to indicate bank command used already for + // following bank commands, as this would not get set otherwise. + + // validate all params before overwriting anything + + if (c->npars < 2) return do_error(c,"\001need two\011"); + + bk = c->p[0]; // first param is BANK + + //if (!val_cmdbank(bk)) return do_error(c,"\001\003 Bank Number"); + + if ((bk & 6) || bk > 15) return do_error(c,"\001\003 Bank Number"); + + if (bkmap[bk].cmd) return do_error(c,"\001Bank already defined"); + + bk++; + bank = bk << 16; // bank in correct form + + // 2nd is file offset + if (c->p[1] < 0 || c->p[1] > (int) fldata.fillen) return do_error(c,"\001\003 File Offset"); + + if (bk > 0 && bkmap[bk-1].bok) + { //check overlap in file offset + if (c->p[1] <= (int) bkmap[bk-1].filend) return do_error(c,"\001File offsets\006"); + } + + if (c->npars > 2) + { // pc start specified (for non std offset) (p[0] bank, p[1] start file offset) + add = nobank(c->p[2]); + if (add < PCORG-2 || add > PCORG+3 ) return do_error(c,"\001 Bank Start"); + } + + if (c->npars > 3) + { // bank end specified. p[0] bank, p[1] start file offset) + add = nobank(c->p[3]); + if (add > 0xffff) return do_error(c,"\001 Bank End"); + + add -= nobank(c->p[2]); // size, end-start. + add++; + + if ((c->p[1] + add) > (int) fldata.fillen) return do_error(c,"\001Bank end > File Size"); + + } + + +// if (c->npars > 4) //fill specified zero otherwise + + +// then sort fillstart + +// NEED MORE CHECKS HERE TO STOP CRASHES and set up data flag markers .............. + + +// clear valid map entries + + if (!bkmap[3].cmd) + { //first use of 'bank' command - clear whole map. + #ifdef XDBGX + DBGPRT(1,"Banks CLEARED ***************"); + #endif + for (i = 0; i < BMAX; i++) + { + b = bkmap+i; + b->bok = 0; + b->cbnk = 0; + b->bprt = 0; + b->bkmask = 0; + } + numbanks = -1; // no banks + bkmap[3].cmd = 1; + } + + b = bkmap+bk; + b->cmd = 1; + b->bok = 1; + b->bprt = 1; // print in msg file + b->filstrt = c->p[1]; + + if (c->npars > 2) b->minpc = nobank(c->p[2]) | bank; + else b->minpc = PCORG | bank; + + if (c->npars > 3) b->maxbk = nobank(c->p[3]) | bank; + else b->maxbk = 0xffff; + + b->filend = b->filstrt + (b->maxbk - b->minpc); + + // fldata is still valid... + if (b->filend > fldata.fillen) + { + b->filend = fldata.fillen-1; + b->maxbk = (b->filend-b->filstrt) + b->minpc; + } + + b->maxpc = b->maxbk; + +// file read ??? anything ?? + + b->fbuf = (uchar*) mem(0,0, 0x10000); // big enough for file TOO BIG + + fseek (fldata.fl[0], b->filstrt,SEEK_SET); // set file start - CHECK ERROR + + fread (b->fbuf + 0x2000, 1, b->filend-b->filstrt+1, fldata.fl[0]); + +/* #ifdef XDBGX + DBGPRT(1,"Read input file (0x%x bytes)", fldata.fillen); + #endif + for (i = 3; i < 7; i++) + { + from = bkmap + i; // temp bank + if (from->bok) + { // valid to transfer + from->dbank++; + to = bkmap + from->dbank; // new bank + bk = from->dbank << 16; + + *to = *from; // lazy, may change this + size = to->filend-to->filstrt+1; + + to->fbuf = (uchar*) mem(0,0, 0x10000); //malloc a full bank. + + memcpy(to->fbuf+nobank(from->minpc), from->fbuf+0x2000, size); + to->minpc &= 0xffff; + to->minpc |= bk; // substitute bank no + + to->maxbk &= 0xffff; + to->maxbk |= bk; + to->bok = 1; + to->maxpc = 0; // do the fill check + find_fill_txt(to); + from->bok = 0; // clear old bank + } + + +/ cross check banks too for maxbk ???? + bkmap[3].opcbt = opcbit; // bit flag array start (uints) + bkmap[4].opcbt = opcbit + 0x800; // = 800*20 = 10000 (32 bit uints) + bkmap[5].opcbt = opcbit + 0x1000; + bkmap[6].opcbt = opcbit + 0x1800; + + bkmap[3].opdbt = opdbit; // bit flag array start + bkmap[4].opdbt = opdbit + 0x800; // = 800*20 = 10000 (32 bit uints) + bkmap[5].opdbt = opdbit + 0x1000; + bkmap[6].opdbt = opdbit + 0x1800; + +*/ + numbanks++; + +b->opcbt = opcbit + (0x800*numbanks); +//b->opdbt = opdbit + (0x800*numbanks); + + basepars.codbnk = 0x90000; // code start always bank 8 + basepars.rambnk = 0; // start at zero for RAM locations + if (numbanks) basepars.datbnk = 0x20000; // data bank default to 1 for multibanks + else basepars.datbnk = 0x90000; // databank 8 for single + + + #ifdef XDBGX + DBGPRT(0,"Bank Set %d %x %x ",bk, b->minpc,b->maxbk); + DBGPRT(1," #(%x - %x fill %x)",b->filstrt, b->filend, b->maxpc); + #endif + return 0; + } + + +uint set_vect (CPS *c) +{ // assumes vect subrs in same bank as pointers + uint ofst, bank; + int i; + LBK *blk; + ADT *a; + + if (chk_csize(c)) return 1; + + blk = add_cmd (c->p[0],c->p[1], C_VECT|C_CMD,0); // by cmd + + if (PMANL) return 0; + if (chcmd.lasterr) return do_error(c,etxts[chcmd.lasterr]); + + if (c->seq) //levels == 0) + { + a = get_adnl(&chadcm,0, 1); // check addn entry + bank = a->bank << 16; + if (!bkmap[a->bank].bok) return do_error(c,"\001Bank\003"); + cpy_adt(c,blk->start); + } + else bank = g_bank(c->p[0]); + + for (i = c->p[0]; i < c->p[1]; i += 2) + { + ofst = g_word (i); + ofst |= g_bank(bank); + add_scan (ofst, J_SUB,0); // adds subr + + } + return 0; +} + +uint set_code (CPS *c) +{ + + add_cmd (c->p[0], c->p[1], c->fcom|C_CMD,0); // by cmd + if (chcmd.lasterr) do_error(c,etxts[chcmd.lasterr]); + return 0; +} + +uint set_scan (CPS *c) +{ + add_scan (c->p[0], J_STAT|C_CMD,0); + if (chscan.lasterr) do_error(c,etxts[chscan.lasterr]); + return 0; +} + + +uint set_args(CPS *c) +{ + LBK *blk; + + if (chk_csize(c)) return 1; + + blk = add_aux_cmd (c->p[0], c->p[1], c->fcom|C_CMD,0); // by cmd + + if (chaux.lasterr) return do_error(c,etxts[chaux.lasterr]); + + // must clear all fieldwidths for args command + + cpy_adt(c,blk->start); + set_data_vect(blk); + + blk->size = totsize(&chadnl,blk->start); + + #ifdef XDBGX + DBGPRT(1,0); + #endif +return 0; +} + +uint set_cdih (CPS *c) +{ + // xcode commands + add_aux_cmd(c->p[0],c->p[1],c->fcom|C_CMD,0); + if (chaux.lasterr) do_error(c,etxts[chaux.lasterr]); + return 0; +} + +uint set_psw (CPS *c) +{ + // psw setter p[0] checked, but not p[1] + c->p[1] = fix_addr_bank(c->p[1]); // force bank 9 if single bank + add_psw(c->p[0],c->p[1]); + if (chpsw.lasterr) do_error(c,etxts[chpsw.lasterr]); + return 0; +} + + + + + + + + + + void do_listing (void) +{ + uint ofst; + int i, lastcom; + LBK *x; + BANK *b; + anlpass = ANLPRT; // safety + + pstr(1,0); //newline + pstr(1,"####################################################################################################"); + pstr(0,"#\n# Disassembly listing of binary '%s' ",fldata.bare); + + //pstr(0," 806%d%c %d bank%c ", P8065 ? 5 : 1, PSTPD ? 'D' : ' ', numbanks+1, numbanks ? 's' : ' '); + pstr(0," 806%d %d bank%c ", P8065 ? 5 : 1, numbanks+1, numbanks ? 's' : ' '); + + pstr(0," SAD Version %s (%s)", SADVERSION, SADDATE); + pstr(0,"\n# See '%s%s' for warnings, results and other information", fldata.bare, fd[2].suffix); + pstr(0,"\n\n# Explanation of extra flags and formats - "); + pstr(0,"\n# R general register. Extra prefix letters shown for mixed size opcodes (e.g DIVW)"); + pstr(0,"\n# l=long (4 bytes), w=word (2 bytes), y=byte, s=signed. Unsigned is default."); + pstr(0,"\n# [ ]=use value as an address (addresses are always word) "); + pstr(0,"\n# @=value is an address (to symbol), '++' increment register after operation." ); + pstr(0,"\n# Processor status flags (for conditional jumps)" ); + pstr(1,"\n# CY=carry, STC=sticky, OVF=overflow, OVT=overflow trap"); +#ifdef XDBGX +pstr(0,"\n# - - - - DEBUG - - - - - "); +#endif + pstr(1,"#\n####################################################################################################"); + + + for (i = 0; i < BMAX; i++) + { + b = bkmap+i; + + if (!b->bok) continue; + + if (numbanks) + { + pstr (2,0); + pstr (1,"###########################################################################"); + pstr (0,"# Bank %d file offset %x-%x, ", i-1, b->filstrt, b->filend); + paddr(b->minpc,0,pstr); pstr(0," - "); paddr(b->maxbk,0,pstr); + if (i == 9) pstr(0, " CODE/BOOT starts HERE"); + pstr(1,0); + pstr (2,"###########################################################################"); + } + + // indent = 0; + lastcom = 0; + + for (ofst = b->minpc; ofst < b->maxbk; ) + { + // first, find command for this offset + + x = get_prt_cmd(&prtblk, b, ofst); + + // add newline if changing block print types + if (dirs[x->fcom].prtcmd != dirs[lastcom].prtcmd || dirs[x->fcom].prtcmd == pp_stct) {pp_comment(ofst,1);} + lastcom = x->fcom; + + while (ofst <= x->end) + { + show_prog (anlpass); + pp_comment(ofst,0); + ofst = dirs[x->fcom].prtcmd (ofst, x); + } + } // ofst loop + + pp_comment(b->maxbk,0); //+1,0); // any trailing comments at bank or listing end + } // bank +} + + + +void scan_all (void) +{ + // check whole scan chain for unscanned items + // a scan may add new entries so track lowest added scan block + SBK *s, *saved; + uint ix; + + ix = 0; + + while (ix < chscan.num) + { + s = (SBK*) chscan.ptrs[ix]; + xscans++; + + if (!s->stop) // && !s->caller) //experiment..NO........ + { + // if (anlpass >= ANLPRT) newl(1); // new line for new block print + saved = s->caller; + s->caller = NULL; // set caller zero as safety for emulate + recurse = 0; // reset recurse level + #ifdef XDBGX + DBGPRT(1,0); + DBGPRT(0,"Chain Scan"); + #endif + scan_blk(s, &cinst); + s->chscan = 1; + s->caller = saved; // and restore caller + + if (tscans > chscan.num * 32) // loop safety check + { + wnprt(2,"*********"); + wnprt(1," Abandon - too many scans - a loop somewhere ?"); + wnprt(1,"*********"); + printf("FAILED - too many scan loops\n"); + break; + } + + } // end if not scanned + + if (lowscan <= ix) ix = lowscan; // recheck from lowest new scan inserted + else ix++; + lowscan = chscan.num; // reset to highest value for next scan + } + + } + + +// ********** command processing section ************** + + +void add_iname(int from, int ofst) +{ + // add interrupt autonames + uint i, x; + char *z; + + // ignore_int can be at the end of a jump in multibanks.... + + // if (!PINTF) return; // moved out to call + from &= 0xffff; // drop bank + from -= 0x2010; + from /= 2; // correct index for table lookup + + x = (P8065) ? 1 : 0; // 8061 or 8065 + z = nm; + + if (numbanks) // sort out bank number + { + i = ofst >> 16; // add bank number after the I + z += sprintf(nm, "I%u_",i-1); + } + else + z += sprintf(nm, "I_"); // no bank number + + if (match_sig(&intign, ofst,0)) // this is 'ignore int' signature + { + sprintf(z,"Ignore"); + add_sym(nm, C_SYS, ofst,32,32, 0,0xfffff); + + return; + } + + for (i = 0; i < NC(inames); i++) + { + if (from <= inames[i].end && from >= inames[i].start && inames[i].p5 == x) + { + z += sprintf(z,"%s",inames[i].name); // number flag set + if (inames[i].num) z+=sprintf(z, "%u", from-inames[i].start); + add_sym(nm, C_SYS, ofst,32,32,0,0xfffff); + break; // stop loop once found for single calls + } + } +return; + } + + + +/*******************command stuff ***************************************************/ + +int ismypunc(char *c) +{ + if (*c == ' ') return 1; + if (*c == ',') return 1; + if (*c == '\t') return 1; + return 0; +} + +int readpunc(CPS *c) + { + // read punctuation between commands and numbers etc. + // keeps track of position (for errors) + + while (c->posn <= c->maxlen) + { + if (ismypunc(flbuf+c->posn)) c->posn++; + else break; + } + + if (c->posn >= c->maxlen) return 0; // hit end of line + + return 1; + } + +int chkpunc(CPS *c, int l) + { + // check end letter of numbers to make sure this isn't + // a string which happens to begin a-f + + char *x; + + x = flbuf+c->posn+l; + + if (*x == ' ') return 0; + if (*x == ',') return 0; + if (*x == ':') return 0; + if (*x == '|') return 0; + if (c->posn+l >= c->maxlen) return 0; // hit end of line + return 1; + } + + + + + + + +int getpx(CPS *c, int ix) + { + // ix is where to start in p params array, + // for max of 8-ix parameters + // answer is params read OK + + int ans, rlen; + uint v; + char *t; + + ans = 0; + + while(ix < 8) + { + if (sscanf(flbuf+c->posn, "%5x%n", &v, &rlen) > 0) + { + if (chkpunc(c, rlen)) break; + // special check for leading zero addresses + // flag as special bank 32 for input bank fix + t = flbuf + c->posn; + if (*t == '0' && rlen == 5) v |= 0x200000; + + c->p[ix] = v; + c->posn += rlen; + ans++; + } + else break; + readpunc(c); + ix++; + } + + return ans; + } + + +int getpd(CPS *c, int ix) + { + // ix is where to start in p params array, 2 max decimal pars + // answer is params read - only ever 1 param ?? + int ans, rlen; + + ans = 0; + + while(ix < 8) + { + if (sscanf(flbuf+c->posn, "%d%n", c->p+ix, &rlen) > 0) + { + if (chkpunc(c, rlen)) break; + c->posn += rlen; + ans++; + } + else break; + readpunc(c); + ix++; + } + + return ans; + } + + + +int getstr(CPS *c, const char **s, uint size) + { + // get string max of 63 chars and compare to string array 's' + int ans, rlen; + uint j; + + ans = sscanf (flbuf+c->posn, "%64s%n", nm, &rlen); // n is no of chars read, to %n + if (ans >= 0) c->posn += rlen; + + if (ans <= 0) return ans; // nothing to read + + ans = -2; // string not found. + + for (j = 0; j < size; j++) + { + if (!strncasecmp (*(s+j), nm, 3) ) // 3 chars min match + { + ans = j; // found + break; + } + } + + return ans; + } + +uint set_lay (CPS *c) +{ +// set layout columns +/* c->npars is what's read in ( + +// npars = 1 p = 5 6-npars 0->5 +// npars = 2 p = 4,5 0->4, 1 ->5 +// npars = 3 p = 3,4,5 0->3, 1->4, 2->5 + +// address and header bytes from zero +// could cut one from these for single bins +#define MNEMPOSN cposn[1] where opcode mneomic starts (26) +#define OPNDPOSN cposn[2] where operands start (32) +#define PSCEPOSN cposn[3] where pseudo code starts (49) +#define CMNTPOSN cposn[4] where comment starts (83) +#define WRAPPOSN cposn[5] where line finishes (180) +//must verify too from stdpos + +rules - + +cannot change first 2, so max of 3 values. +if no psudocode, can move comment start down to 49. +49 is minimum column for everything. +columns must be > than previous entry. + +const int stdpos[6] = {0, 26, 32, 49, 83, 180}; // standard posns + +*/ + +int i, j, x; + +for (i = 0; i < c->npars; i++) +{ +//must be >= psuedocode column + + if ((unsigned) c->p[i] < stdpos[3]) return do_error(c,"\001Column < minimum"); + if (i && c->p[i] <= c->p[i-1]) return do_error(c,"\001 Column <= prevous Col"); +} + + +x = 6 - c->npars; // where to start + +for (i = 0; i < c->npars; i++) + { + for (j = x; j < 6; j++) cposn[j] = c->p[i]; + x++; + } + return 0; +} + + + + + +int chk_cmdaddrs(CPS *c, DIRS* d) + { + // check and fixup any address issues (bank etc) + // can have a pair (start-end) and/or a single start (e.g. rbase has both) + + // check for valid bank and remember to add 1 + // banks set up before here + +// can check numbanks < 0 here abaondon if no banks present. + + int start, end; + + if (d->sgpos < d->maxpars) + { // single address, may have a start/end pair following + start = fix_input_addr(c->p[d->sgpos]); + + if (numbanks < 0) return do_error(c,"\017No valid banks found/defined"); + if (!val_input_bank(start)) return do_error(c,"\001Invalid Bank"); + c->p[d->sgpos] = start; + } + + if (d->prpos < d->maxpars) + { // address pair + start = fix_input_addr(c->p[d->prpos]); + end = fix_input_addr(c->p[d->prpos+1]); + + // if pair not first, optional. + if (numbanks < 0) return do_error(c,"\017No valid banks found/defined"); + if (!val_input_bank(start)) return do_error(c,"\001Invalid Bank"); + + if (!end) end = start; + if (end <= 0xffff) end |= g_bank(start); // add start bank + if (!bankeq(start,end)) return do_error(c,"\001Banks don't match"); + if (end < start) return do_error(c,"\001End < Start"); + + c->p[d->prpos] = start; + c->p[d->prpos+1] = end; + } + + +// do error returns 1; + + return 0; + } + +int check_opt(CPS *c, uint set) + { + char *t; + const char *x; + DIRS *d; + int ans; + + ans = 0; + d = dirs + c->fcom; + t = flbuf+c->posn; // where we are in cmd string + + // check for delimiters first, then options allowed :| + + if (set == 1 && *t == ':') ans = ':'; + // if (*t == '|') ans = '|'; //not yet, only some cmds ?? + + if (*t == '[' || *t == ']') ans = *t; + + if (ans) return ans; + + x = 0; + if (set == 1) x = d->opts; // allowed letter main options + if (set == 2) x = d->gopts; // allowed letter global options + + while (x) + { + if (*t == *x) + { + ans = *t; + break; + } + x++; + } + return ans; +} + +int do_adnl_letter (CPS* c) +{ + // switch by command letter - moved out of parse loop for clarity + + ADT *a; + int ans, rlen, opt; + float fx; + // char *t; + + // get next letter and check, main options + opt = check_opt(c,1); + +// BUT HERE, SWOP TO [] Mode if starts with [ + + + if (opt <= 0) return do_error(c,"\001\003 Option"); // Illegal option + else c->posn +=1; + + if (!c->seq && opt != ':') return do_error(c,"\001Colon\007"); + + if (c->seq > 0) a = c->adnl; //current adt block + + switch(toupper(opt)) + { + // case 'A': // AD voltage ?? / RPM/ IOtime.... + // cadd->volt = 1; + // break; + +// B with T + // case 'C' : + +// to become cell , start bit , size. from zero bit (LS) + + // break; + + case '|' : // split printout for large structs, not for argl + + if (!c->argl) a->newl = 1; // mark split printout here (end of block) + + // fall through + + case ':' : + + +// need max level check !!!! or change to get blocks from chain but would need to delete and rechain +// with new FK..... + +// NB. pp-lev etc uses ->cnt so set cnt = 0, ssize = 1 for hidden entries + + + +// add case '[' : // treat as colon if not 'new' mode. +// open new 'level' based on fid of current adt - meaning adt cannot be in current form.... +// add case ']' : // if 'new mode' must check levels all closed. + + + + // Add a new ADT level + // t = flbuf+ c->posn; + // while (*t + + + if (!readpunc(c)) break; // not if a trailing colon (or new line marker) + +// check for double colons too.......... + + + + // c->levels++; + // if (c->levels > 31) return do_error(c,"\001Too Many data items"); + if (c->seq > 32) return do_error(c,"\001Too Many data items"); + a = add_adt(&chadcm,0, 256); // add new adt + c->adnl = a; // and remember it + c->seq = a->fkey & 0xff; + break; + + case '=' : // an ANSWER definition. + + ans = getpx(c,4); + if (!ans) return do_error(c,"\001Address\004"); + + a->ans = 1; + // check........ MUST BE VALID REGISTER !! + if (!a->cnt) a->cnt = 1; + a->data = c->p[4]; // item + fixed address offset (funcs) - bank? + break; + + case 'B' : // Bit + // read one decimal val b only allowed in sym and timer commands + ans = getpd(c, 4); + if (!ans) return do_error(c,"\001Bit Number\004"); + if (c->p[4] < 0 || c->p[4] > 31) return do_error(c,"\001Bit Number\003"); + a->fstart = c->p[4]; + // can't use a->fend , which is for SIZE (=W) + if (ans > 1) a->data = c->p[5]; // TEMP !! + else a->data = a->fstart; // single bit + a->enc = 7; // temp marker for bitno + break; + + case 'D' : + + // 1 hex value (address offset with bank) WHAT ABOUT NEGATIVES !! + ans = getpx(c,4); + if (!ans) return do_error(c,"\001Address\004"); + a->foff = 1; + + //a->data = fix_input_addr(c->p[4]); // item + fixed address offset (funcs) - bank? + + a->data = c->p[4]; + // if (a->data & 0x8000) + // { a->data &= 0xffff; + // a->data = -a->data; + // } + + + + + if (!a->pfw) a->pfw = get_pfwdef(a); //word sized for offsests + break; + + case 'E' : + + // ENCODED - always size 2 + ans = getpx(c,4); + if (ans != 2 ) return do_error(c,"\001 %d values\004",2); + + a->enc = c->p[4]; + a->data = c->p[5]; + a->fend = 15; // encoded implies word + //a->bsize = 2; // word sized + + if (!a->pfw) a->pfw = get_pfwdef(a); //cnv[1].pfwdef; setdefpfw(a); // if (!a->pfw) a->pfw = cnv[1].pfwdef; //cafw[2]; + if (!a->cnt) a->cnt = 1; + break; + + case 'F' : + + // flags for symbol + + if (c->fcom == C_SYM) + { // flags word/byte for SYM. + c->flags = 1; + break; + } + break; + + + + // G H J + + case 'I' : // immediate fixup for syms + + if (c->fcom == C_SYM) c->imm = 1; + break; + + case 'K' : + + // 1 decimal value (bank, 0,1,8,9) + ans = getpd(c, 4); + if (!ans) return do_error(c,"\001Bank number\004"); + + if (!val_bank(c->p[4])) return do_error(c,"\001Bank Number\003"); + a->bank = c->p[4]+1; + if (!a->cnt) a->cnt = 1; // but size stays at zero + break; + + case 'L' : // long int (= double) + + a->fend &= 32; // keep sign + a->fend |= 31; + // a->bsize = 4; + if (!a->cnt) a->cnt = 1; + if (!a->pfw) a->pfw = get_pfwdef(a); + break; + + // case 'M': // mask pair ?, use esize to decide word or byte + // cadd->mask = 1; + // break; + + case 'N' : + + a->fnam = 1; // Name lookup + if (c->fcom == C_SYM) c->names = 1; + break; + + case 'O' : // Cols or count - 1 decimal value + + ans = getpd(c, 4); + if (!ans) + { + c->p[4] = 1; + do_error(c, "\002 Repeat value\007, 1 assumed"); + } + if (c->p[4] < 1 || c->p[4] > 32) return do_error(c,"\001 Repeat value\003 (1-32)"); + + a->cnt = c->p[4]; + break; + + case 'P' : // Print fieldwidth 1 decimal + + ans = getpd(c, 4); + if (!ans) + { + c->p[4] = 1; + do_error(c, "\002 Field Width\007, 1 assumed"); + } + if (c->p[4] < 1 || c->p[4] > 32) return do_error(c,"\001 Field width\003 (1-32)"); + + a->pfw = c->p[4]; + break; + + case 'R' : // indirect pointer to subroutine (= vect) + + a->fend = 15; // safety, lock to WORD + // a->bsize = 2; + a->vaddr = 1; + // a->name = 1; // with name by default NOT DEFAULT + if (!a->cnt) a->cnt = 1; + + if (!a->pfw) a->pfw = get_pfwdef(a); + break; + + + case 'S': // Signed + a->fend |= 32; + break; + + case 'T': // biT ->Triple + a->fend &= 32; // keep sign + a->fend |= 23; + if (!a->cnt) a->cnt = 1; + if (!a->pfw) a->pfw = get_pfwdef(a); + break; + + + + + case 'U' : //unsigned (default so optional) + a->fend &= 31; // clear sign + break; + + case 'V': // diVisor + + // read one FLOAT val + ans = sscanf(flbuf+c->posn, "%f%n ", &fx, &rlen); // FLOAT + if (ans <=0) return do_error(c,"\001Divsior value\004"); + c->posn+=rlen; + a->flt = 1; //mark float variable + a->fldat = fx; // Full FLOAT variable + break; + + case 'W': // Word - or write for syms + + a->fend &= 32; // keep sign + a->fend |= 15; + + if (!a->cnt) a->cnt = 1; + if (!a->pfw) a->pfw = get_pfwdef(a); + break; + + case 'X': // Print radix ( Hex default) + ans = getpd(c, 4); + if (!ans) + { + a->prdx = 0; + do_error(c, "\002Radix \007, Hex assumed"); + } + +// change to :m 10.z for float, where z is num places. +// t = flbuf+c->posn; // where we are in cmd string +// if (t = '.') go float + + + else if (c->p[4] == 2 ) a->prdx = 3; + else if (c->p[4] == 10) a->prdx = 2; + else if (c->p[4] == 16) a->prdx = 1; + else return do_error(c,"\001 Radix value\003 (2,10,16)"); + break; + + case 'Y': // bYte + a->fend &= 32; // keep sign, set size to 1 + a->fend |= 7; + if (!a->cnt) a->cnt = 1; + if (!a->pfw) a->pfw = get_pfwdef(a); + break; + + // Z + + default: + return do_error(c,"\001\003Option"); + + } // end switch + return 0; + } + + +int do_global_opts(CPS *c) +{ + int inglo, ans, opt; + char *t; + + if (!readpunc(c)) return 0; + + t = flbuf+c->posn; + if (*t != '$') return 0; // no '$', exit + + c->posn += 1; // skip the '$' + + inglo = 1; + + while (inglo) + { + if (!readpunc(c)) break; + + // get next letter and check, main options + opt = check_opt(c,2); + + if (opt <= 0) return do_error(c,"\003Option"); // illegal option char (4) + else c->posn += 1; // Only single char here + + switch(toupper(opt)) + { + case 'Q' : // terminator byte + + // read optional number of bytes max 3. + ans = getpd(c, 4); + if (!ans) c->term = 1; + else + { + if (c->p[4] < 1 || c->p[4] > 3) return do_error(c,"\001Size\003 (1-3)"); + c->term = c->p[4]; + } + break; + + case 'A' : + c->argl = 1; // set args layout + c->cptl = 0; + break; + + case 'C' : + + c->cptl = 1; // set compact layout + c->argl = 0; + break; + + + case 'F' : + // f func add/ tab add, cols + + ans = getstr(c,spfstrs, NC(spfstrs)); // zero is a dummy + if (ans <= 0) return do_error(c,"\001Subr type reqd"); + c->spf = ans + 4; + ans = getpx(c,5); + + // read colons in here and cheat ??? +// register check ?? + + if (c->spf < 13) + { + if (ans < 1) return do_error(c,"\001 At least %d values\004",1); + if (ans > 1) do_error(c,"002Extra Values\010"); + } + else + { + if (ans < 2) return do_error(c,"\001 At least %d values\004",2); + if (ans > 2) do_error(c,"002Extra Values\010"); + } + + c->adreg = c->p[5]; // struct address register + c->szreg = c->p[6]; // tab column register + break; + + case ':' : + inglo = 0; // end of global options + c->posn--; // go back to colon + break; + + default: + do_error(c,"\001\003Option"); + break; + } + } + return 0; +} + + +int do_opt_str (CPS* c) +{ + int ans; + char *t; + + c->p[0] = 0; + ans = 1; + + while (ans) + { + if (!readpunc(c)) break; + t = flbuf+c->posn; + if (*t == ':') c->posn++; + + ans = getstr(c,optstrs, NC(optstrs)); // zero is valid answer (default) + if (ans < 0) do_error(c,"\001\003 Option"); // not found + c->p[0] |= mopts[ans]; + } +return 0; + +} + +int parse_cmd(CPS *c, char *flbuf) +{ + // parse (and check) cmnd return error(s) as bit flags + // return 1 for next line (even if error) and 0 to stop (end of file) + + int rlen, ans; + char *t, *e; // where we are up to + DIRS* d; + + memset(c, 0, sizeof(CPS)); // clear entire struct + clearchain(&chadcm); // clear command adt chain +// c->fid = (void *) 0x100; // dummy fid for adt command chain + + if (!fgets (flbuf, 255, fldata.fl[3])) return 0; + + flbuf[255] = '\0'; + c->maxlen = strlen(flbuf); // safety + + e = flbuf + c->maxlen; // end of input line + + t = strchr(flbuf, '\n'); // check for LF + if (t && t < e) e = t; // mark it + + t = strchr(flbuf, '\r'); // check for CR + if (t && t < e) e = t; // mark it + + //t = strchr(flbuf, '#'); // check for comment - after names + //if (t && t < e) e = t; // mark it + + c->maxlen = e - flbuf; // shorten line as reqd + + if (c->maxlen == 0) return 1; // this is a null line; + + c->posn = 0; + + if (!readpunc(c)) return 1; // null line after punc removed + + t = flbuf+c->posn; + if (*t == '#') return 1; // # comment at front + + ans = getstr(c,cmds, NC(cmds)); // string match against cmds array - zero is valid + if (ans < 0) return do_error(c,"\001 \003 Command"); // cmnd not found + + d = dirs+ans; + c->fcom = ans; + + if (PARGC) c->cptl = 1; // set compact argument layout default + if (PSTCA) c->argl = 1; // set data struct argument layout default + + readpunc(c); + + if (d->maxpars) // read up to 8 following addresses into p[0-n] (if any) + { + c->npars = getpx(c,0); + + if (c->npars < 1) return do_error(c,"\001\011\004"); // params reqd + if (c->npars > d->maxpars) do_error(c,"\002Extra\011\010"); // extra pars ignored (continue) + + // verify addresses, single and/or start-end pair + // but not whether valid for data or code (yet) + ans = chk_cmdaddrs(c, d); + +// ans is not used ??? +// ans id currently 0 or 1 +// (do_error sets c->error to 1 if error. c->error is full int + + if (c->error > 1) return 0; + + if (c->error) return 1; + +//c->error is 0 for warning, 1 for error + +// return 0 if serious bank problem +// "no banks defined, cannot continue" .................. + + + } + + // read name here, if allowed and present - sort out spaces in names ?? + + readpunc(c); + + if (d->namex && c->posn < c->maxlen && flbuf[c->posn] == '\"') + { + t = flbuf+c->posn; + while (*t == '\"') + { + c->posn++; // skip quote(s) + t = flbuf+c->posn; + } + rlen = 0; +// readpunc first, and allow multiple quotes ?? + readpunc(c); // safety check + + while (c->posn < c->maxlen) + { + t = flbuf+c->posn; + if (rlen < SYMSZE) c->symname[rlen] = *t; + if (*t == '\"') break; // end of name + if (!isspace(*t)) c->posn++; // use system isspace not mypunct + else break; + rlen++; + } + + while (*t == '\"') + { + c->posn++; // skip quote(s) + t = flbuf+c->posn; + } + + // if (*t == '\"') + // { // end of name + // c->posn++; + // } + // else do_error(c,"\002\007 close quote (assumed at first space)"); // missing quote + + if (rlen > SYMSZE-1) + { // truncate with warning, but keep c->posn to what was actually read + do_error(c,"\002 Name truncated at %d chars",SYMSZE); + rlen = SYMSZE-1; + } + c->symname[rlen] = '\0'; + } + + // NOW check for comment, AFTER name + t = strchr(flbuf+c->posn, '#'); + if (t) c->maxlen = t - flbuf; // shorten line as reqd + + + if (d->gopts) do_global_opts(c); + + // now read addnl levels + + if (d->opts || d->stropt) + { + // read option chars/strings/markers whilst data left + while (c->posn < c->maxlen) + { + if (!readpunc(c)) break; // safety check + + if (d->stropt) ans = do_opt_str(c); // option strings + if (d->opts) ans = do_adnl_letter (c); // adnl letters style + if (c->error) return 1; + } // end get more options (= colon levels) + + } // end if options allowed + + // any further error reporting is responsibility of each handler command + + c->tsize = totsize(&chadcm, 0); //safety. + + d->setcmd (c); // do setup procedure + + if (d->namex && c->fcom != C_SYM && *c->symname) + { // default add sym name for cmds other than SYM + SYM *s; + // uint add; + // add = nobank(c->p[0]); + // add = fix_sbk_addr(add); // force bank 9 if single bank + s = add_sym(c->symname,C_CMD,c->p[0],32,32, 0,0xfffff); + +// may need error for bank + + if (chsym.lasterr == E_DUPL) + { + // do_error(c,etxts[chsym.lasterr]); //TEST !! + strncpy(nm,s->name, SYMSZE); // current sym + new_symname (s, c->symname); // change sym + chsym.lasterr = 0; + if (!s->sys) do_error(c,etxts[E_SYMNAM], nm); + s->sys = 0; + s->cmd = 1; //clear system, set user cmd + s->xnum = 0; // and clear any autonum + if (chsym.lasterr) return do_error(c,etxts[chsym.lasterr]); + } + } + + + + if (c->firsterr) wnprt(1,0); // extra LF after any errors + show_prog(0); + return 1; // next line +} + + +void do_preset_syms(void) + { + uint i; + DFSYM *z; + + for (i = 0; i < NC(defsyms); i++) + { + z = defsyms+i; + if (((P8065) && z->p85) || (!(P8065) && z->p81)) + { + add_sym(z->symname,z->wrt|C_SYS,z->addr,z->fstart,z->fend,0,0xfffff); + } + } + } + + + +/*************************************************************** + * read directives file + ***************************************************************/ +int getudir (int ans) +{ + int addr,addr2, i, j, bank; + BANK *b; + + // cmdopts will be zero or P8065 here (before dir read) + // and banks should be sorted (auto detect anyway) + + cmdopts |= PDEFLT; // add default + + if (fldata.fl[3] == NULL) + { + wnprt(2,"# ----- No directive file. Use default options"); + } + else + { + wnprt(2,"# Read commands from directive file at '%s'", fldata.fn[3]); + while (parse_cmd(&cmnd, flbuf)); + } + + + + + if (PRSYM) do_preset_syms(); + + if (PMANL) return 0; // entirely manual + + // now setup each bank's first scans and interrupt subr cmds +// ERROR IF NO BANKS !! + + + for (i = 0; i < BMAX; i++) + { + b = bkmap+i; + if (!b->bok) continue; + + #ifdef XDBGX + DBGPRT(1,"--- auto setup for bank %d ---",i); + #endif + + bank = i << 16; + addr = b->minpc; + addr |= bank; + + add_scan (addr,J_INIT,0); // inital scan at PCORG (cmd) + + j = (P8065) ? 0x205f : 0x201f; + j |= bank; + + add_cmd (0x200a|bank, 0x200f|bank, C_WORD|C_SYS,0); // from Ford handbook + add_cmd (0x2010|bank, j, C_VECT|C_SYS,0); // interrupt vects with cmd + + for (j -= 1; j >= (0x200f|bank); j-=2) // counting DOWN to 2010 + { + addr2 = g_word (j); + addr2 |= bank; + if (PINTF) add_iname(j,addr2); + add_scan (addr2, J_SUB,0); // interrupt handling subr + + } + } + + #ifdef XDBGX + DBGPRT(1," END auto setup"); + #endif + +return 0; + } + + + +void prt_subs (uint (*prt) (uint,const char *, ...)) +{ + SUB *s; + SYM *x; +// SPF *p; + // ADT *a; + uint ix, go; + + prt(1,"# ------------ Subroutine list----------"); + + for (ix = 0; ix < chsubr.num; ix++) + { + s = (SUB*) chsubr.ptrs[ix]; + + // decide on print + if (prt == wnprt) + { + go = 0; + if (s->cmd) go = 1; + if (!go && get_spf(s->start)) go = 1; + if (!go && get_adnl(&chadnl,s->start,1)) go = 1; + if (!go && get_adnl(&chans,s->start,1)) go = 1; + } + else go = 1; + + if (go) + { + if (prt == wnprt) {prt(0,"sub "); paddr(s->start,2,prt);} + else prt(0,"sub %x %x ", s->start, s->end); + + x = get_sym(0, s->start,32,0); + if (x) + { + prt(0,"%c%s%c ", '\"', x->name, '\"'); + if (prt == wnprt) x->noprt = 1; // printed sym, don't repeat it + } + + prt_glo(s, prt); + prt_adt(&chadnl,s->start,0,prt); + prt_adt(&chans ,s->start,0,prt); // answer + if (s->cmd) prt(0," #cmd"); + prt(1,0); + } + } + + prt(2,0); + } + + + +void prt_syms (uint (*prt) (uint,const char *, ...)) //void) +{ + SYM *t; + uint ix, b; + + prt(1,"# ------------ Symbol list ----"); + + for (ix = 0; ix < chsym.num; ix++) + { + t = (SYM*) chsym.ptrs[ix]; + + // decide on print + if (prt != wnprt || !t->noprt) + { + + if (prt == wnprt) + { + prt(0,"sym "); + paddr(t->addb >> 5,1,prt); + if (t->rstart) + { + paddr(t->rstart,1,prt); + paddr(t->rend,0,prt); + } + } + else + { // debug + prt(0,"[%x] sym %x ", t->addb, t->addb >> 5); + prt (0," %5x %5x ", t->rstart, t->rend); + } + + prt(0," \"%s\"",t->name); + + b = t->addb & 0x8; + if (!b || t->flags || !(t->addb & 0x10) || t->immok) prt(0," :"); + + if (!(t->addb & 0x8)) + { + prt(0,"B %d" , t->addb & 7); + if (t->fend != (t->addb & 7)) prt(0, " +%d", t->fend); + } + + if (!(t->addb & 0x10)) prt(0," W"); + if (t->flags) prt(0," F"); + if (t->names) prt(0," N"); + if (t->immok) prt(0," I"); + if (t->sys) prt(0," # auto"); + + prt(1,0); + } + } + prt(1,0); + } + + +void prt_dirs() +{ // print all directives for possible re-use to msg file + prt_opts (); + prt_layout(); + prt_banks(wnprt); + prt_rbt (wnprt); + prt_psw (); + prt_scans(); + prt_cmds (wnprt); +// prt_cmds (&chaux, wnprt); + prt_subs (wnprt); + prt_syms (wnprt); + + +} + +short init_structs(void) + { + + // 4 banks of 16k as bit flags + opcbit = (uint *) mem(0,0, 0x8000); // opcode start bit flags (bytes) + // opdbit = (uint *) mem(0,0, 0x8000); // 8*0x8000 = 0x40000 + + memset(bkmap,0,sizeof(bkmap)); + bkmap[3].opcbt = opcbit; // bit flag array start (uints) + bkmap[4].opcbt = opcbit + 0x800; // = 800*20 = 10000 (32 bit uints) + bkmap[5].opcbt = opcbit + 0x1000; + bkmap[6].opcbt = opcbit + 0x1800; + +// bkmap[3].opdbt = opdbit; // bit flag array start +// bkmap[4].opdbt = opdbit + 0x800; // = 800*20 = 10000 (32 bit uints) + // bkmap[5].opdbt = opdbit + 0x1000; +// bkmap[6].opdbt = opdbit + 0x1800; + + memset(opcbit, 0, 0x8000); // clear all opcode flags +// memset(opdbit, 0, 0x8000); // clear all opdata flags + + anlpass = 0; // current pass number + gcol = 0; + lastpad = 0; + cmdopts = 0; + + #ifdef XDBGX + DBGmaxalloc = 0; // malloc tracking + DBGcuralloc = 0; + DBGmcalls = 0; + DBGmrels = 0; + #endif + + tscans = 0; + xscans = 0; + basepars.rambnk = 0; + + return 0; +} + + +int openfiles(void) +{ + int i; + + fldata.error = -1; + + for (i = 0; i < numfiles; i++) + { + fldata.fl[i] = fopen(fldata.fn[i], fd[i].fpars); // open precalc'ed names + if(i < 3 && fldata.fl[i] == NULL) + { + fldata.error = i; + return i; // failed to open a file + } + } + +return -1; +} + + + + + + + + +/* old +void check_ihandler(SIG *s, uint taddr, uint ix) + { + // check pointer values (int handler) for valid jump, return etc + // ix = 7 for 8065, 5 for 8061 + int val; + uint jp,cx; + + val = g_byte(taddr); + + //allow up to 7 nops + jp = taddr+7; + while (taddr < jp) + { + if (val == 0xff) val = g_byte(++taddr); else break; + } + + do_one_opcode(taddr); // do THIS way !! handles 0x10 cmdopts set + +// sinst, banks will be +1 from do_code + + if (ix == 7 && val == 0x10) // sinst.sigix = 16 (jump) + { // bank swop 8065 only, verify bank is valid, + // and check for a valid jump after it, return + jp = g_byte(++taddr); + + if (!(jp & 0xf6)) + { // bank OK (0,1,8,9) + jp = g_byte(++taddr); + jp = do_jumpsig (opcind[jp], taddr, 0x100000); + if (jp) { s->v[ix]++; return;} + } + } + + + // check if valid jump, reload val if so + jp = do_jumpsig (opcind[val], taddr, 0x100000); + if (nobank(jp) >= PCORG) val = g_byte(jp); + +// could also be a further jump here (xdt2) + + if (val >= 0xf0 && val <= 0xf2) s->v[ix]++; // ret, retei or pushp + else + if (ix == 7 && val == 0x10) s->v[ix]++; // jump to a bank swop (8065) + else + { + cx = opcind[val]; //get index + if ((cx > 47 && cx < 50) || (cx > 39 && cx < 41)) // ldx, stx + { + val = g_byte(taddr + 3); + if (val >= 0xf0 && val <= 0xf2) s->v[ix]++; // ret, retei or pushp + } + } + +} */ + +void check_ihandler(SIG *s, uint taddr, uint ix) + { + // check pointer values (int handler) for valid jump, return etc + // ix = 7 for 8065, 5 for 8061 + int val; + uint jp, cx; + + +// allow taddr == d004 as a special ??? + + val = g_byte(taddr); + + // allow up to 7 nops + jp = taddr+7; + while (taddr < jp) + { + if (val == 0xff) val = g_byte(++taddr); else break; + } + + if (val >= 0xf0 && val <= 0xf2) + { // ret, retei or pushp + s->v[ix]++; + return; + } + + do_one_opcode(taddr); + + if (sinst.sigix == 16 || sinst.sigix == 23) + { // jump, jnb + jp = sinst.opr[1].addr; + cx = g_bank(jp); + if (cx != g_bank(taddr)) + { // bank swop + cx-= 0x10000; + if (!(cx & 0xf6)) + { + s->v[ix]++; // bank OK (0,1,8,9) + return; // can't go any further + } + } + + taddr = jp; + if (val_rom_addr(taddr)) + { + val = g_byte(taddr); //not bank swop, get value + if (val >= 0xf0 && val <= 0xf2) {s->v[ix]++; return;} // ret, retei or pushp + } + + } + + taddr = tmpscan.nextaddr; //try after one unmatchd opcode + val = g_byte(taddr); // get value + if (val >= 0xf0 && val <= 0xf2) {s->v[ix]++; return;} // ret, retei or pushp + + } + + + +void check_bank_intv (SIG *s, uint *mx) + { + uint bank, taddr, x; + if (!s) return; // safety, no jump + taddr = 0; + bank = g_bank(s->start); // FAKE bank from sig + + // sig below int vects and a valid jump + if (nobank(s->end) >= 0x2010 || s->v[1] < (0x2000|bank)) return; + + // check interrupt vects 8065 + // counts valid adrress into [6] + // and valid contents into [7] + + cmdopts |= 8; //set 8065 for bank swops + + for (x = (bank|0x2010); x < (bank|0x205f); x+=2) + { + taddr = g_word(x); //pointer + //if (taddr <= 0xffff && taddr >= 0x2060) s->v[6]++; // 8065 count (valid address) + if (taddr > 0x205f) s->v[6]++; // 8065 count (valid address) + else break; + check_ihandler(s, taddr|bank, 7); + } + + if (s->v[6] > *mx) *mx = s->v[6]; // keep highest match in outer loop + + cmdopts = 0; //clear 8065 + + if (*mx <= 11) + { + // check interrupt vects 8061 if max 8065 count below 10 + // counts valid adrress into [4] + // and valid contents into [5] + + for (x = (bank|0x2010); x < (bank|0x201f); x+=2) + { + taddr = g_word(x); //pointer + // if (taddr <= 0xffff && taddr >= 0x2020) s->v[4]++; // valid adress + if (taddr > 0x201f) s->v[4]++; // valid adress + else break; + check_ihandler(s, taddr|bank, 5); + + } + } + + taddr = chsig.num * 0x100; + s->start += taddr; // fiddle to stop dupls & overlaps + s->end += taddr; + + // check for code bank, bank 8 + // sig can't overlap interrupt vects (2010) and must have a jump + + if (s->v[1] <= s->start+s->skp) s->v[3] = 2; // loopstop = Data bank + + if (s->v[6] > 39 && nobank(s->v[1]) > 0x205f) s->v[3] = 1; // jump over intr regs - 8065. + else + if (s->v[4] > 7 && nobank(s->v[1]) > 0x201f) s->v[3] = 1; // jump over intr regs - 8061. + + if (s->v[3]) + { //sig must have a matched jump + if (s->v[4] > 7 || s->v[6] > 35) inssig(s); // matched enough, so keep it for now + } + if (s->v[6] > *mx) *mx = s->v[6]; // keep highest score +} + + + + +void find_banks (uchar *fb) +{ + // file is read in to ibuf [0x2000] onwards - look for start jump and interrupt vectors + // assemble temp sigs into chain and then check interupt vectors. scan around each address + // for missing or extra bytes + + BANK *b, *l; + SIG *s, *t, *x; + uint addr, bank, tix, mx; + int ofs; + + // temp bank setup so accesses work + + bank = 0x30000; //bank 3, temp + b = bkmap+3; + b->maxbk = (bank | 0xffff); + b->maxpc = b->maxbk; + b->minpc = (bank | PCORG); + b->bok = 1; // temp mark as OK + b->cmd = 0; + b->cbnk = 0; + + #ifdef XDBGX + DBGPRT(1,"scan for all banks"); + #endif + + // check full file in loop for ALL banks + + mx = 0; //move this inside loop ??? + numbanks = -1; + + for (addr = 0; addr < fldata.fillen; addr += 0x1000) + { + // keep all matches and then review best candidates + // bank->fbuf MUST point to 0x2000 whether real or virtual + // for missing bytes - mapaddr fixed, PC changes, 0x2002, 0x2001 + // for extra bytes - mapaddr changes +0 to +3, PC set at 0x2000 + + b->filstrt = addr; // move file offset + b->filend = addr + 0xe000-1; // max possible end point + + if (b->filend >= fldata.fillen) + { // make range safe for file end, adjust downwards + b->filend = fldata.fillen-1; // end of file (no bank) + b->maxbk = b->filend - b->filstrt + (PCORG | bank); + b->maxpc = b->maxbk; + } + + b->fbuf = fb + addr - 0x2002; // start addr maps to 0x1ffe, then 1fff + + for (ofs = 0; ofs < 2; ofs++) + { + s = match_sig(&hdr,((0x2002-ofs)|bank),0); + if (s) + { + s->v[15] = addr; // file start addr + s->v[10] = 0x2002-ofs; // code start addr + check_bank_intv (s,&mx); + } + b->fbuf++; + } + + b->fbuf = fb + addr - 0x2000; // start addr fixed 0x2000, file start moves + + for (ofs = 0; ofs < 3; ofs++) + { + s = match_sig(&hdr,(0x2000|bank),0); + if (s) + { + s->v[15] = addr+ofs; // file start addr + s->v[10] = 0x2000; // code start addr + check_bank_intv (s, &mx); + } + b->fbuf++; + } + } + + // look for highest valid intr count. [4] & 5 for 8061, 6 & 7 8065 + // addresses (in s->v[15]) will be grouped. v[2] is bank (and highest) + + t = (SIG*) chsig.ptrs[0]; // to be 8061 highest match + x = t; // to be 8065 highest match + bank = 3; // (fake) start bank + if (t->v[5] > 5) t->v[2] = bank; // start as match if qualifies + if (x->v[7] > 30) x->v[2] = bank; + + for (tix = 1; tix < chsig.num; tix++) + { + s = (SIG*) chsig.ptrs[tix]; + + // interrupt vect cnt MUST be all OK otherwise not correct binary + // check address ranges in s[15] so no interbank overlaps + + if ((s->v[15] - x->v[15]) < 0x800) + { + if (s->v[6] == 40) // 8065 + { + if (s->v[7] > x->v[7]) + { + if (s->v[7] > 30) s->v[2] = bank; // new match + x->v[2] = 0; // kill old match + x = s; + } + } + + if (s->v[4] == 8 && s->v[6] < 10) // 8061 + { + if (s->v[5] > t->v[5]) + { + if (s->v[5] > 6) s->v[2] = bank; // new match + t->v[2] = 0; // kill old match + t = s; + } + } + } + else + { + if (x->v[2] || t->v[2]) bank++; + t = (SIG*) chsig.ptrs[tix]; // to be 8061 highest match + x = t; + if (t->v[5] > 6) t->v[2] = bank; // add bank if qualifies + if (x->v[7] > 30) x->v[2] = bank; + } + } + +// done, now sort out temp banks. +// clear all bank OK markers + + for (tix = 3; tix < 7; tix++) + { + b = bkmap+tix; + b->bok = 0; + } + + for (tix = 0; tix < chsig.num; tix++) + { + s = (SIG*) chsig.ptrs[tix]; + if (s->v[2]) + { //bank set (temp) + bank = s->v[2]; //reset bank............. + b = bkmap+bank; + + if (s->v[6] == 40) b->P65 = 1; else b->P65 = 0; // mark bank as 8065 + b->bok = 1; // bank is valid start + numbanks++; + if (s->v[3] < 2) b->cbnk = 1; // code bank (8) + b->filstrt = s->v[15]; // actual bank start as buffer offset for 0x2000 + b->filend = b->filstrt + 0xe000-1; // default (max) end + + // beyond end of file ??? + if (b->filend >= fldata.fillen) b->filend = fldata.fillen-1; + + // short bank check ............. + l = bkmap + (bank-1); // last bank + if (bank > 3 && b->filstrt < l->filend) + { // shorten previous bank if it overlaps start. + + l->filend = b->filstrt-1; + l->maxbk = l->filend - l->filstrt + l->minpc; + l->maxbk |= g_bank(l->minpc); + l->maxpc = l->maxbk; + #ifdef XDBGX + DBGPRT(1,"** Short bank %d, %x-%x", bank-1, l->filstrt, l->filend); + #endif + } + + b->fbuf = fb + b->filstrt-0x2000; // set correct fbuf + b->minpc = s->v[10] | (bank << 16); // 2000, 2001 or 2002 + b->maxbk = b->filend - b->filstrt + b->minpc; + + #ifdef XDBGX + DBGPRT(1,"bank found at %5x, PC %x", b->filstrt, s->v[10]); + if (b->filstrt & 3) DBGPRT(1," WARNING - file/bank appears to have %s byte(s) at front", "extra"); + if (nobank(b->minpc) > PCORG) DBGPRT(1," WARNING - file/bank appears to have %s byte(s) at front", "missing"); + #endif + + if (b->filstrt & 3) wnprt(1," WARNING - file/bank appears to have %s byte(s) at front", "extra"); + if (nobank(b->minpc) > PCORG) wnprt(1," WARNING - file/bank appears to have %s byte(s) at front", "missing"); + + } + } + + #ifdef XDBGX + DBG_sigs(&chsig); + #endif + clearchain(&chsig); // clear out all sigs + +} + + + + +void clrbkbit(int ix, int bk) +{ + BANK *b; + + b = bkmap+ix; + + switch (bk) + { + case 0: + + #ifdef XDBGX + if (b->bkmask & 1) DBGPRT(1, "bank %d cannot be %d", ix, bk); + #endif + b->bkmask &= 0xe; //clear bit 0 + break; + + case 1: + + #ifdef XDBGX + if (b->bkmask & 2) DBGPRT(1, "bank %d cannot be %d", ix, bk); + #endif + b->bkmask &= 0xd; //clear bit 1 + break; + + case 8: + + #ifdef XDBGX + if (b->bkmask & 4) DBGPRT(1, "bank %d cannot be %d", ix, bk); + #endif + b->bkmask &= 0xb; //clear bit 2 + break; + + case 9: + + #ifdef XDBGX + if (b->bkmask & 8) DBGPRT(1, "bank %d cannot be %d", ix, bk); + #endif + b->bkmask &= 7; //clear bit 3 + break; + + default: + #ifdef XDBGX + DBGPRT(0,"unknown bank %d", bk); + #endif + break; + } +} + +void copy_banks(uchar * fbuf) + { + int i, bk, size; + BANK *to, *from; + +// copy to bank+1 for bankfix with sig names + + for (i = 3; i < 7; i++) + { + from = bkmap + i; // temp bank + if (from->bok) + { // valid to transfer + from->dbank++; + to = bkmap + from->dbank; // new bank + bk = from->dbank << 16; + + *to = *from; // lazy, may change this + size = to->filend-to->filstrt+1; + + to->fbuf = (uchar*) mem(0,0, 0x10000); //malloc a full bank. + + memcpy(to->fbuf+nobank(from->minpc), from->fbuf+0x2000, size); + to->minpc &= 0xffff; + to->minpc |= bk; // substitute bank no + + to->maxbk &= 0xffff; + to->maxbk |= bk; + to->bok = 1; + to->bprt = 1; // print his bank + to->maxpc = 0; // do the fill check + find_fill_txt(to); + from->bok = 0; // clear old bank + from->bprt = 0; // and print + } + + } + + + basepars.codbnk = 0x90000; // code start always bank 8 + basepars.rambnk = 0; // start at zero for RAM locations + if (numbanks) basepars.datbnk = 0x20000; // data bank default to 1 for multibanks + else basepars.datbnk = 0x90000; // databank 8 for single + +} + + +void drop_banks_via_ints (int bk) + { + // check outwards from here for other banks + // from int vect pointers via jumps and bank prefixes. + // numbanks is 4, 2 to 6 of bkmap valid, the temp slots + // but real bank nos (to be) assigned to them + // NB. some bins don't have cross bank int jumps (e.g. 22CA) + + + uint tbank, taddr, x; + int val, rbank; + + bkmap[bk].bkmask = 0xb; // 9, 1, 0 not 8 + bkmap[bk].bok = 1; + + tbank = bk << 16; // TEMP bank no (so it maps OK) + + for (x = (tbank|0x2010); x < (tbank|0x205f); x+=2) + { + taddr = g_word(x); // pointer + taddr |= tbank; // make true address + val = g_byte(taddr); + // for 8065, can be a bank swop. Check next byte is 0,1,8,9 with mask (and THEN a jump ??) + + if (val == 0x10) + { // bank swop prefix. + taddr++; + rbank = g_byte(taddr); // get true bank (from code) + // assume this bank CANNOT be rbank............. + clrbkbit(bk,rbank); + + taddr++; + val = g_byte(taddr); // get opcode after bank prefix + + if (val == 0xe7 || ( val >=0x20 && val <= 0x27)) + { + // a jump - calc destination + taddr = do_jumpsig (opcind[val], taddr,0x100000); + // now try to find a valid opcode in one of the other banks. + // have a guessed order at this point + + /* for (i = 2; i < 6; i++) + { + taddr = nobank(taddr); + taddr |= (i << 16); + val = g_byte(taddr); + if (val >= 0xf0 && val <= 0xf2) + { + if (getflagbnk(i) != rbank) + { // not match !! + #ifdef XDBGX + DBGPRT(1,"Change (%d) from %d to %d",i, getflagbnk(i),rbank); + #endif + setflgbnk(i,rbank); + } + + #ifdef XDBGX + else + { + DBGPRT(1,"confirm bank (%d) %d via intr jump",i, rbank); + } + #endif + + } + } */ + } + } // end bank check 8065 loop + } + +// now check for an allocated bank after shuffle.... + + + + } + +int chk_bank_order (int cbk) + { + // cbk = bank location - so check outwards for other banks + // from int vect pointers + +/* cannot change bank 8 !! + GCM2 is fked up by this code .... + must check if there IS a clash first.... + */ + +// 22CA all point to bank 8 !! + + int i, x, ans; + BANK *b; + + ans = 1; + // first use interrupt handlers to identify any bank jumps + // and know that THIS bank cannot be any that are jumped to + // b->bkmask is bitmask of possible banks + + + for (i = 3; i < 7; i++) + { + b = bkmap + i; + if (i != cbk) drop_banks_via_ints(i); + else + { + b->dbank = 8; // set up bank 8 directly + b->bkmask = 4; + #ifdef XDBGX + DBGPRT(1, "bank %d is 8", i); + #endif + } + } + + // now see if we can definitely find more banks + // if any bank has single bit, can take that as definite ID + // and drop that bit from all other banks + + for (i = 3; i < 7; i++) + { + b = bkmap + i; + + if (i != cbk) + { // not for bank 8 + if (b->bkmask == 1 || b->bkmask == 2 || b->bkmask == 8) + { // found a single bit, drop from other banks + for (x = 3; x < 7; x++) + { + if (x != i) + { + #ifdef XDBGX + if (bkmap[x].bkmask & b->bkmask) + { + DBGPRT(1, "bank %d cannot be %d", x, b->bkmask < 8 ? b->bkmask-1 : 9); + } + #endif + bkmap[x].bkmask &= (~b->bkmask); + } + } + } + } + } + + // now, is there one bit per bank ?? if so have got the + // bank order. if not, then we'll have to guess. + + for (i = 3; i < 7; i++) + { + b = bkmap + i; + switch (b->bkmask) + { + case 1 : + b->dbank = 0; + break; + + case 2 : + b->dbank = 1; + break; + + case 4 : + b->dbank = 8; + break; + + case 8 : + b->dbank = 9; + break; + + default : + ans = 0; // not single bits + break; + } + + if (!ans) + { + wnprt(2, "WARNING - Cannot confirm bank order is correct, selected order is best guess only"); + break; + } + } + +return ans; +} + + + +int do_banks(uchar *fbuf) + { + // set up banks after find_banks, or user command. + + // NO set up RAM bank [16] first 0x2000 of ibuf for ram and regs + // NOTE - not technically safe if file has missing front bytes, but probably OK + +// COPY file into correct banks layout to c + + int ltj,bank,i, ans; + BANK *b; + + // do the checks and bank ordering..... + + bank = -1; // bank with the start jump (= code) + ltj = 0; // temp number of loopback jumps + ans = 0; + + for (i = 0; i <= numbanks; i++) + { + b = bkmap+i+3; // 3 to 6 + // b->fbuf points to bank start.................. + + // any bank with P65 means all banks are 8065 + if (b->P65) cmdopts |= 0x8; + + if (b->cbnk) + { + if (bank >= 0) + { + wnprt(1," Warning - "); + wnprt(1,"Found multiple Start Code banks [bank 8]"); + wnprt(1,"Choosing first bank found, others ignored - use bank command to override"); + #ifdef XDBGX + DBGPRT(1," ** Found multiple code banks"); + #endif + b->bprt = 1; //keep the print + b->bok = 0; + b->dbank = 9; + numbanks--; + } + else bank = i; + + } + else ltj++; // no of other banks + } + + + if (bank < 0) + { + wnprt(1,"Cannot find a Start/Code bank [bank 8]"); + wnprt(1,"Is this file an 8061 or 8065 Binary ??"); + ans = -4; //return -4; + } + + + #ifdef XDBGX + DBGPRT(1,"%d Banks, start in %d", numbanks+1, bank+3); + #endif + + switch (numbanks) + { + + default: + wnprt(1,"0 or > 4 banks found. File corrupt?"); + ans = -2; //return -2; + break; + + case 0: // copybanks autostarts at [3] for source list + mkbanks(1,8); + break; + + case 1: // 2 bank, only 2 options + if (bank == 1) mkbanks(2,1,8); + else mkbanks(2,8,1); + break; + + // no 3 bank bins (case=2) so far ..... + + // MUST handle 3 banks !!! + + + + case 3: + // do best guess bank order and then check it + // can shuffle numbers before the cpy_banks which moves them. + if (!chk_bank_order(bank+3)) // attempt to sort banks by intrpt handlers + + switch(bank) + { + // Alternate bank orders - best guess from bank 8 position + + case 0: + mkbanks(4,8,9,0,1); + break; + + case 1: + mkbanks(4,9,8,0,1); + break; + + case 2: + mkbanks(4,0,1,8,9); + break; + + case 3: + mkbanks(4,0,1,9,8); + break; + } + + break; + } + +// what about multiple 8061 bank 8 ?? + // final copy and fill detection. this subr recalled if user command + // DBG_banks(); + + copy_banks(fbuf); + + #ifdef XDBGX + prt_banks(DBGPRT); + DBGPRT(1,0); + #endif + + return ans; +} + + +int readbin(void) +{ + /* Read binary into main block 0x2000 onwards (ibuf[0x2000]) + * ibuf [0]-[2000] RAM (faked and used in this disassembly) + * ibuf [2000]-[42000] up to 4 banks ROM, at 0x10000 each + */ + + int x; + + uchar *fbuf; //file goes in here first. + + fldata.fillen = fseek (fldata.fl[0], 0, SEEK_END); + fldata.fillen = ftell (fldata.fl[0]); + fseek (fldata.fl[0], 0, SEEK_SET); // get file size & restore to start + + wnprt(2,"# Input file is '%s'",fldata.fn[0]); + wnprt(2,"# File is %dK (0x%x) bytes",fldata.fillen/1024, fldata.fillen); + +// fldata.error = 0; + + if (fldata.fillen > 0x40000) + { + wnprt(1,"# Bin file > 256k. Truncated to 256k"); // is this always right ?? + fldata.fillen = 0x40000; + } + + wnprt(1,0); + + fbuf = (uchar*) mem(0,0, fldata.fillen); // big enough for file + fread (fbuf, 1, fldata.fillen, fldata.fl[0]); + + #ifdef XDBGX + DBGPRT(1,"Read input file (0x%x bytes)", fldata.fillen); + #endif + + // file in temp buffer, now read and layout as per banks. + +// check for file error ? +// end = fldata.fillen; + + find_banks(fbuf); + x = do_banks(fbuf); // auto, may be modded later by command + mfree(fbuf, fldata.fillen); + return x; +} + + +void rbasp(SIG *sx, SBK *b) +{ + + //add rbase entries from signature match + + uint add, add2, reg, i, cnt; //,xcnt,rcnt; +// RBT *r; + uint pc; + + if (b) return; // no processing if subr set + + // rcnt = chbase.num; // may have rbases already. start at 'NEW' point + reg = (sx->v[1] & max_reg()); + add = sx->v[3]; + cnt = g_byte(add); // no of entries in list + + #ifdef XDBGX + DBGPRT(1,"In rbasp - begin reg %x at %x, cnt %d", reg, add, cnt); + #endif + + add_cmd(add, add+1, C_BYTE|C_SYS,0); + add_cmd(add+2, add+(cnt*2)+1, C_WORD|C_SYS, 0); + + if (chcmd.lasterr) + { + #ifdef XDBGX + DBGPRT(1,"Rbase Already Processed"); + #endif + return ; + } + + pc= add+2; + + // check bank is correct ? + add = g_word(pc); // register pointer + add |= basepars.datbnk; // + default data bank + add2 = g_word(add); // check pointer is consistent with next entry + i = g_word(pc+2); // next in list + + #ifdef XDBGX + if (i != add2) + { + // rbases point to different bank + DBGPRT(1,"RBASEDBNK"); + } + #endif + + for (i = 0; i < cnt; i++) + { + add = g_word(pc); // register pointer + add |= basepars.datbnk; // add + default data bank + + if (nobank(add) < 0x2020) break; + add_cmd(add, add+1, C_WORD|C_SYS,0); + add2 = g_word(add); // check pointer is consistent with next entry + // also valid for last pointer for xcode + +// r = +add_rbase(reg,add,0,0xfffff); // dbg message in here.... + // if (r) r->cmd = 1; + upd_ram(reg,add,15); + add_aux_cmd(add,add2-1, C_XCODE|C_SYS,0); // and add an XCODE as this is data + + reg+=2; + pc+=2; + } + + /* now check pointers for XCODE directives for newly added entries + + if (rcnt == chbase.num) return ; // nothing added + + for (i = rcnt; i < rcnt+cnt; i++) + { + if (i >= chbase.num) break; + r = (RBT*) chbase.ptrs[i]; + n = (RBT*) chbase.ptrs[i+1]; + pc = n->rval & 0xffff; + + xcnt =0; + + if (i >= rcnt+cnt-1) + { + n = (RBT*) chbase.ptrs[i-1]; // get bank of last pointer + r->rval = (r->rval & 0xffff) | (n->rval & 0xf0000); + pc = g_word(r->rval); + } + else + { + while (pc != g_word(r->rval) && xcnt < BMAX && i < rcnt+cnt-1) + { + xcnt++; // try to find matching bank... + if (bkmap[xcnt].bok) r->rval = (r->rval & 0xffff) | (xcnt << 16); + } + } + + if (xcnt >= BMAX) + { + #ifdef XDBGX + DBGPRT(1,"Matching rbase addr not found !"); + #endif + } + else + { + // if (xcnt) DBGPRTN("Rbase %x = %x",r->radd, r->rval); + // set_cmd(r->rval, r->rval+1, C_WORD); + // set_auxcmd(r->rval,pc-1, C_XCODE); // and add an XCODE as this is data + add_data_cmd(val, val+1, C_WORD|C_CMD,0); + add_code_cmd(val,val2-1, C_XCODE); // and add an XCODE as this is data + } + } + sx->done = 1; + #ifdef XDBGX + DBGPRT(1,"end rbasp"); + #endif + return ;*/ +} + + +/* +void check_rbase(void) //SIG *sx, SBK *dummy) +{ + +// rewrite to ditch the sig scan +// CAN'T DO THIS - 0FAB does not use f0 as start point !!! + // must go back to signature !! + + //check if rbase caclcon pointer entries are present + // 2020 num of levels (always 8) + // 2021 no of calibrations (1) + // 2022-20A1 cal pointers = 7f 0r 64 pointers + // presume 2060 for 8065. + + int i, cnt,bank, bf; + RBT *r; + uint pc, addr, val, val2; + + #ifdef XDBGX + DBGPRT(1,"In chk_rbasp"); + #endif + +// do NOT use numbanks, as CAN have single bank 8065 !! + if (P8065) addr = 0x82060; else addr = 0x82020; + + cnt = g_byte(addr); // no of entries + bf = 0; + pc = addr+2; + val2 = g_word(pc); + + if (cnt != 8) + { // may be a bigger number somewhere.... + #ifdef XDBGX + DBGPRT(1,"Not Found"); + #endif + return; + } + + bank = 8; + + for (i = 1; i < cnt; i++) + { + addr = val2 | (bank << 16); + if (!val_data_addr(addr)) break; // cancel all + + val = g_word(addr); // value at pointed to + pc += 2; // still in bank 8 + val2 = g_word(pc); // get next match (was addr) + + if (val != val2) + { + if (!numbanks) break; + // check if rbase points to another bank (typically b1) + if (!bf) + { + // not checked yet + for (bank = 0; bank < BMAX; bank++) + { + if (bkmap[bank].bok) + { // bank is OK + addr = nobank(addr) | (bank << 16); + val = g_word(addr); // value at pointed to + if (val == val2) {bf = 1; break;} // bank found + } + } + } + } + } + + if (i < cnt) + { + #ifdef XDBGX + DBGPRT(1,"Not Found"); + #endif + return; + } + + + #ifdef XDBGX + DBGPRT(1,"FOUND RBASE, points to bank %d", bank); + #endif + + // OK, matched list and bank................ + + if (P8065) addr = 0x82062; else addr = 0x82022; + + add_data_cmd(addr-2, addr-1, C_BYTE|C_CMD, 0); + add_data_cmd(addr, addr+(cnt*2)-1, C_WORD|C_CMD, 0); + + pc = 0xf0; // always 0xf0 ?? NO !!!! + for (i = 0; i < cnt; i++) + { + val = g_word(addr); // value at pointed to + val |= (bank << 16); + val2 = g_word(val); + val2 |= (bank << 16); + + r = add_rbase(pc,val,0,0); // message in here.... + if (r) + { + r->cmd = 1; + upd_ram(pc,val,2); // and set register + add_data_cmd(val, val+1, C_WORD|C_CMD,0); + add_code_cmd(val,val2-1, C_XCODE); // and add an XCODE as this is data + } + + pc+=2; + addr+=2; + } + + #ifdef XDBGX + DBGPRT(1,"end rbasp"); + #endif + return ; +}*/ + + +// do branch has a FULL copy of SFIND to ensure that parallel branches have their own +// parameter set established at start of each new branch (= a jump to here) +// this is because the registers may CHANGE in each branch. + +// void (*set_str) (SFIND); + //int anix; // spf number of func moved to SFIND + + + + +void set_struct(SFIND *f) +{ + // check for bank first, and then if truly valid + if (!val_rom_addr(f->ans[0])) f->ans[0] = databank(f->ans[0],0); + + if (!val_rom_addr(f->ans[0])) + { + // #ifdef XDBGX + // DBGPRT(1,"Invalid Address %x", f->ans[0]); + // #endif + return ; + } + + + + + + // function + if (f->spf > 4 && f->spf < 13) set_xfunc(f); + // table + + if (f->spf > 12 && f->spf < 17) set_xtab(f); +} + +void do_list_struct(SFIND *f) +{ + uint m, i, addr; // temp !! + uint start, gap; // for pointer list + LBK *k; + ADT *a; + + +// this sort of works, problem is it proceeds down list and can get size and sign worng....... + +// and overlaps don't work right either............ + + + + + +#ifdef XDBGX + DBGPRT(1," LIST START %x for rbase %x", f->lst[0], f->lst[1]); +#endif + + start = f->lst[0]; +// add_data_cmd (m, m+1, C_WORD, 0); // add data entry - TEST !! + + m = start; + + if (!f->rg[1]) + { // func, check first byte is valid. + + while (1) + { + addr = g_word(m); + addr = addr + f->lst[1]; +#ifdef XDBGX + DBGPRT(0," %x ", addr); +#endif + + if (!val_rom_addr(addr)) break; + i = g_byte(addr); + if ( i != 0x7f && i != 0xff) break; + f->ans[0] = addr; //set func address + set_struct(f); + m += 2; + if (get_cmd(m,0)) break; // is this any good ? (next ptr not done !) + } + } + else + { // Table - check size first + addr = g_word(m); //start + i = g_word(m+2); // next pointer + gap = i - addr; // table size + addr = addr + f->lst[1]; + i = addr; + f->ans[0] = addr; + m +=2; + set_struct(f); + + while(1) + { + addr = g_word(m); + addr = addr + f->lst[1]; +#ifdef XDBGX + DBGPRT(0," %x", addr); +#endif + if ((addr - i) != gap) break; //assume all tabs are same size + i = addr; + f->ans[0] = addr; + set_struct(f); + m +=2; + if (get_cmd(m,0)) break; + } + } + + if (m >= start+2) { k = add_cmd (start, m-1, C_WORD, 0); + + if (k) + { + a = add_adt(&chadnl,k->start,0); // only one + if (a) + { + a->fend = 15; //word + // a->bsize = 1; + a->foff = 1; + a->data = f->lst[1]; + a->fnam = 1; + a->cnt = 1; + } +}} +} + + +void do_branch(uint addr, int cnt, SFIND f) + { + // do a branch from a jump. if jump(s) to here found, do as separate branch + int xcnt; + uint xofst, ix; + JMP *j; + OPC *opl; + + if (cnt <= 0) return; + #ifdef XDBGX + DBGPRT(1,0); + DBGPRT(0,"Branch start %x %d [%x %x] (%x %x)", addr,cnt, f.rg[0], f.rg[1], f.ans[0], f.ans[1]); + #endif + xcnt = cnt; + xofst = addr; + + while (xcnt) + { + // find_opcode just goes down/up list, no checks (pn_opcodes does check). + xofst = find_opcode(0, xofst, &opl); + if (!xofst) break; + xcnt --; + + if (!xcnt && f.ans[0]) + { + #ifdef XDBGX + DBGPRT(0,"END, set %x at %x", f.ans[0], xofst); + #endif + set_struct(&f); // add table with one row if run out of opcodes + } + + if (xcnt <= 0) + { + #ifdef XDBGX + DBGPRT(0,"END %x ", xofst); + #endif + break; + } + + match_opnd(xofst,opl,&f); + + if (f.ans[0] && f.ans[1]) + { + #ifdef XDBGX + DBGPRT(1," MATCH R%x=%x R%x=%x", f.rg[0], f.ans[0], f.rg[1], f.ans[1]); + #endif + + if (f.lst[0]) do_list_struct(&f); + else set_struct(&f); // add func or tab (add override) + f.ans[0] = 0; + f.ans[1] = 0; // are these necessary ?? + f.lst[0] = 0; + break; + } + #ifdef XDBGX + else DBGPRT(0," %x[%d]", xofst, xcnt); + #endif + + // see if any jumps in to xofst. if so, launch new branches. + + ix = get_tjmp_ix(xofst); // start point + + while(ix < chjpt.num) + { + j = (JMP*) chjpt.ptrs[ix]; + if (j->toaddr != xofst) break; + #ifdef XDBGX + DBGPRT(1,0); + DBGPRT(1,"JMP %x<-%x %d", j->toaddr, j->fromaddr, cnt); + #endif + + //j->from-j->cmpcnt to skip the cmp +// copy f for each new branch ?? + do_branch(j->fromaddr,xcnt,f); + ix++; + } // end jump loop + + } // end find opcode loop + +} + + + +// ** abandon this for funcs and do a liear search and verify ??? +// MUCH easier than tables are..................... +// but STIL need a decent table recognizer................ + + + +void do_calls(SUB * sub, SFIND f) +{ + // do the 'base' (i.e. the subr calls outwards) separately to reset params + + JMP *j, *k; + LBK *g; + ADT *a; + uint jx, ix, ofst; + #ifdef XDBGX + DBGPRT(1,0); + DBGPRT(0,"Base calls for sub %x", sub->start); + #endif + + ix = get_tjmp_ix(sub->start); // start point + + while(ix < chjpt.num) + { + j = (JMP*) chjpt.ptrs[ix]; + + if (j->toaddr != sub->start) break; + + if (j->jtype == J_SUB) // sub only for from FIRST call + { + // if (f.rg[1] == 1) f.ans[1] = j->fromaddr + j->size; //func only, for override + #ifdef XDBGX + DBGPRT(1,0); + DBGPRT(0,"To %x - [%x %x] (%x %x) ", j->fromaddr , f.rg[0], f.rg[1], f.ans[0], f.ans[1]); +#endif + //j->from-j->cmpcnt to skip the cmp + //j->from+j->size to check for arguments + // defined args are same as 'args' data command..... + + ofst = j->fromaddr + j->size; + + a = get_adnl(&chadnl,sub->start,1); + if (a) + { // same as data command, but user specified + f.ans[0] = decode_addr(a, ofst); + #ifdef XDBGX + DBGPRT(1,"do args at %x = %x", g->start, f.ans[0]); // assume first parameter ??? + #endif + // need datbank ?? from the + } + + else + { + g = get_aux_cmd(ofst, C_ARGS); + if (g) + { // assume first parameter ??? + a = get_adnl(&chadnl,g->start,1); + f.ans[0] = decode_addr(a, ofst); + + #ifdef XDBGX + DBGPRT(1,"do args at %x = %x", g->start, f.ans[0]); // assume first parameter ??? + #endif + // need datbank ?? + } + } + + do_branch(j->fromaddr, 20,f); + + // do_branch scans back from j->from FIRST before checking for jumps, + // so add an extra check and loop for jumps to j->from + + jx = get_tjmp_ix(j->fromaddr); // start point + while(jx < chjpt.num) + { + k = (JMP*) chjpt.ptrs[jx]; + if (k->toaddr != j->fromaddr) break; + #ifdef XDBGX + DBGPRT(1,0); + DBGPRT(1,"To+ %x - [%x %x] (%x %x) ", j->fromaddr, f.rg[0], f.rg[1], f.ans[0], f.ans[1]); + #endif + //j->from-j->cmpcnt to skip the cmp +// copy f for each new branch ?? + do_branch(k->fromaddr,20,f); //rg1,rg2,rg3,ans1,ans2,ans3); + jx++; + } // end jump loop + + + #ifdef XDBGX + DBGPRT(1,0); + DBGPRT(1,"End call branch %x", j->fromaddr); + #endif + } + ix++; + + } + +} + + +LBK* del_olaps(uint addr, uint ix) +{ + LBK *n; + n = 0; + if (ix < chcmd.num) n = (LBK *) chcmd.ptrs[ix]; + while (n && n->start < addr) + { + // n MUST be wrong - delete it. keep if higher or cmd + if (n->cmd) break; + if (n->fcom < C_TEXT) chdelete(&chcmd,ix,1); else break; + if (ix < chcmd.num) n = (LBK *) chcmd.ptrs[ix]; else n = 0; + } +return n; +} + + +//uint getfunceval(uint size) +//{ + + +//} + +void extend_func(uint ix) +{ + + uint eadr, rsize, adr, val, eval; //size + ADT *a, *b; + LBK *k, *n; + + k = (LBK *) chcmd.ptrs[ix]; + +//if (k->start == 0x22336) +//{ +//DBGPRT(1,0); +//} + + + + + + + + + ix++; + if (ix < chcmd.num) n = (LBK *) chcmd.ptrs[ix]; else n = 0; + // check end of func + + eadr = maxpcadd(k->end); // safety + a = get_adnl(&chadnl,k->start,1); // entry 1 + if (!a) return; + b = get_next_adnl(&chadnl,a); // entry 2 + if (!b) return; + + // extend func to zero or max negative here + + // size = bytes(a->fend); + rsize = bytes(a->fend) + bytes(b->fend); // whole row size in BYTES + eval = get_signmask(a->fend); // end value + adr = k->start; + + // need to check next command as well.... + + while (adr < eadr) // check input value for each row (UNSIGNED) + { + adr += rsize; + val = g_val(adr,0, (a->fend & 31)); // next input value as UNSIGNED + if (val == eval) + { + eadr = adr + rsize-1; // hit end input value, set end + break; + } + } + + k->end = eadr; // stage 1 of extend + + //check here if any command occurs inside the function and delete them + n = del_olaps(eadr, ix); + + //adr is now at first zero row, and val is last value + + eadr = maxpcadd(k->end); // reset end + + if (n) + { + if (n->cmd) eadr = n->start; // can't go past a user command. + if (n->fcom > C_TEXT) eadr = n->start; // or past funcs or tabs (etc) + } + // set row size for whole row for extend check. + // word ->long, byte->word + + rsize = (a->fend & 31) + (b->fend & 31) +1; // row size (unsigned) + + eval = g_val(adr,0, rsize); // and its full row value + +// must check overlap with data HERE + + while (adr < eadr) + { // any matching final whole rows + adr += bytes(rsize); + val = g_val(adr,0, rsize); + if (val != eval) break; + if (n && !eval && n->start == adr) break; // match next cmd for extended zeroes + } + + // adr is now start of next item (end of extend values) + // if end value is zero need extra check + // otherwise need to delete any skipped entries........ + + n = del_olaps(adr, ix); + + //check again for overlaps as del-olaps may have moved n + + if (n && adr > n->start) + { + if (n->cmd) adr = n->start; // can't go past a user command. + if (n->fcom > C_TEXT) adr = n->start; // or past funcs or tabs (etc) + } + + + // need any more here ??? + +// if (eval != 0 || n == 0 || n->start >= adr) +// { // end of list or not zero value or no overlap +// if (adr > k->end) k->end = adr-1; +// } +// else +// { // check next command + + // n is < and value is 0 + // probably wrong, but safe for now + // extend if only one word/byte + // k->end = n->start-1; // TEMP !! + + + k->end = adr-1; + // } + + +//now check if all outputs are in upper byte and < 16 +// if so this is dimension for table, and can autosize it. + + + rsize = bytes(a->fend) + bytes(b->fend); // whole row size in BYTES + +if (rsize == 4) + { //word table - check for table dim style (top byte only) + uint lval, chg; +// prob need more rules (like eval should always be <= lval...but val must change also +// and end up at zero ??) + +// need to check not signed as well !! + + adr = k->start + bytes(b->fend); + eadr = k->end; + eval = 1; // flag + chg = 0; //value changed + lval = 0x2000; // 32 in top byte + while (adr < eadr) // check input value for each row (UNSIGNED) + { + val = g_val(adr,0, (b->fend & 31)); // next input value as UNSIGNED + if (val & 0xff) + { //bottom byte set + eval = 0; + break; + } + if (val != lval) chg = 1; + + if (val > lval) + { // downward trend only + eval = 0; + break; + } + adr += rsize; + lval = val; + } + +if (eval && chg) + { //passes test as dimension func for table + b->flt = 1; + b->fldat = 256.0; //set divisor at 256 +//change name ?? + } + +} + + + + + } + + +/* experimentals + +uint test_func(uint start, uint ssize) +{ +uint addr, end, rsz; +int val,lval; +if (start == 0x827b4) +{ +DBGPRT(1,0); +} + + + +rsz = casz[ssize] * 2; // one row + +addr = start; +end = start + 128; // safety + +lval = g_val(addr, ssize); //start value + + +val = (~casg[ssize]) & camk[ssize]; // start value + +if (lval != val) return start; + + +// need row extender in here for some short tables + +addr += rsz; + +while (addr < end) +{ +val = g_val(addr, ssize); +if (val >= lval) break; +lval = val; +addr += rsz; +} + +// 3 rows = 6 * size +if ( (addr - start) >= (rsz * 3) && (lval & camk[ssize]) == casg[ssize] ) +{ +DBGPRT (1,"possible func at %x-%x (%d)", start, addr, ssize); +} + +return start; +} + + + + +void find_func(uint start, uint end) +{ +uint addr; +int val; + +addr = start; + +while (addr < end) + { + val = g_byte(addr); + if (val == 0xff || val == 0x7f) + { + test_func(addr,1); + test_func(addr,5); // byte + if (!(addr &1)) + { + test_func(addr,2); + test_func(addr,6); // word + } + } + addr++; + + } +} +*/ + + + + +void extend_table(uint ix) +{ + uint apeadr, maxadr, size, val, eval, temp,jx; + ADT *a; + LBK *k, *n; + + k = (LBK *) chcmd.ptrs[ix]; + + + // attempt to extend by data,use external proc + // to do best match by data for row and col sizes + + if (ix >= chcmd.num) + { + apeadr = maxpcadd(k->end)+1; + maxadr = apeadr; + } + else + { + jx = ix+1; + n = (LBK *) chcmd.ptrs[jx]; + apeadr = n->start; // apparent end from next cmd + + while ( n->fcom < C_TEXT && !n->cmd) + { // struct of user command + jx++; + if (jx >= chcmd.num) break; + n = (LBK *) chcmd.ptrs[jx]; //skip all byte and word + } + + maxadr = n->start; // max end. not skippable + } + + a = get_adnl(&chadnl,k->start,1); //k->adnl; // cols in a->cnt + + if (!a) return; + size = apeadr - k->start; // size in bytes to apparent end + eval = size/a->cnt; // max rows + val = eval * a->cnt; // size in whole rows + + temp = apeadr; + do_table_sizes(k->start, a->cnt, &temp, maxadr); // temp, for all tables + + + if (a->cnt > 2) + { // must have valid rows + if (val == size) + { + // MATCH with extra rows.... eadr is next cmd + k->end = apeadr-1; + return; + } + + if (val+1 == size) + { // could be a filler - need to check next data cmd ? + if (g_byte(apeadr-1) == 0xff) k->end = apeadr-2; + return; + } + } + + // default - move table to nearest whole row ?? not perfect, but OK for now. + // then investigate actual data for sizes + k->end = k->start+val-1; // temp + + size = do_table_sizes(k->start, a->cnt, &apeadr, maxadr); + + //need to get end address out as well.......use apeadr + + if (size) + { + a->cnt = size; + k->size = size; // should do a dsize() really // 1st test + if (k->start+apeadr >= k->end) k->end = k->start+apeadr-1; + } + else + { +// er.... + } + + del_olaps(k->end,ix+1); //remove any spanned entries + +} + + + +void do_structs() + + { + uint i; + SUB *sub; + LBK *k; + SFIND f; + SPF *x; + // go down tab and func subroutines and find params (structs) + + // func subroutines + + for (i = 0; i < chsubr.num; i++) + { + sub = (SUB *) chsubr.ptrs[i]; + x = get_spf(sub->start); + + if (x) + { + // common to both + f.lst[0] = 0; + f.spf = x->spf; + f.rg[0] = x->addrreg; // address + f.ans[0] = 0; + + if (x->spf > 4 && x->spf < 13) + { + f.rg[1] = 0; // no size reg + f.ans[1] = 1; // mark as found + } + + // table subroutines + + if (x->spf > 12 && x->spf < 17) + { + f.rg[1] = x->sizereg; // cols + f.ans[1] = 0; + } + + do_calls(sub,f); + } + + } +// now extend the end of funcs for the extra filler rows..... +// probably need to check tables too. + + for (i = 0; i < chcmd.num; i++) + { + k = (LBK *) chcmd.ptrs[i]; + if (!k->cmd) + { + if (k->fcom == C_FUNC) extend_func(i); + if (k->fcom == C_TABLE) extend_table(i); + } + } + +} + + + +char *calcfiles(char *fname) + { + // do filenames + char *t, *x; + int i; + + t = strrchr(fname, '\n'); // DOS and Linux versions may have newline at end + if (t) {*t = '\0';} + + x = fname; // keep possible pathname + + t = strrchr(fname, PATHCHAR); // stop at last backslash + if (t) + { // assume a path in filename, replace path. + *t = '\0'; + fname = t+1; + sprintf(fldata.path, "%s%c", x,PATHCHAR); + } + + + x = strrchr(fname, '.'); // ditch any extension + if (x) *x = '\0'; + + strcpy(fldata.bare,fname); // bare file name + + for (i = 0; i < numfiles; i++) + { + if (! *(fldata.fn[i])) strcpy(fldata.fn[i],fldata.path); // use default path name + + strcat(fldata.fn[i],fldata.bare); // bare name + strcat(fldata.fn[i],fd[i].suffix); // plus suffix + } + + + return fldata.bare; + } + + +/*************************************************************** + * disassemble EEC Binary + ***************************************************************/ +short disassemble (char *fstr) +{ + int ans; + + init_structs(); + calcfiles(fstr); + openfiles(); + + // time(&timenow); + // sprintf(nm, "%s", ctime(&timenow)); + +/* + + +#include +#include + +void main() +{ + time_t t; + time(&t); + clrscr(); + + printf("Today's date and time : %s",ctime(&t)); + getch(); +} + + +*/ + + + if (fldata.error >= 0) + { + printf("File '%s' not found or cannot open\n",fldata.fn[fldata.error] ); + return fldata.error; + } + + wnprt(1,0); + wnprt(1,"# ----------------------------"); + wnprt(1,"# SAD Version %s (%s)", SADVERSION, SADDATE); + wnprt(2,"# ----------------------------"); + + ans = readbin(); + + /* read bin file and sort out banks + but allow getudir in case of manual set banks */ + + show_prog(++anlpass); //anlpass = 1 cmds and setup + ans = getudir(ans); // AFTER readbin... + + if (numbanks < 0 || ans < 0 || cmnd.error > 1) + { + // wrong if no banks or fault + wnprt(1,"Abandoned !!"); + printf("Abandon - can't process - see warnings file\n"); + return 0; + } + + show_prog(++anlpass); //anlpass = 2 signature pass + prescan_sigs(); + + //check_rbase(); // check static rbase setup (2020 etc, not as sig) + + wnprt(2,"# ----- Start Disassembly phase 1 -----"); + + scan_all (); // do all the analysis + + // ---------------------------------------------------------------------- + + show_prog(++anlpass); //anlpass = 3 scan phase + + do_jumps(); + + wnprt(2,"# ----- End Disassembly phase 1 -----"); + wnprt(2,"# ----- Start Disassembly phase 2 -----"); + + // ---------------------------------------------------------------------- + // and do a whole new pass here for the data ??? loops,structs, tables.... + // ---------------------------------------------------------------------- + + + + // scan_dc_olaps(); // check and tidy code and data overlaps + + +// scan funcs +// scan tabs ?? + + // scan_data_gaps(); + + // scan_lpdata(); + + + + show_prog (++anlpass); //anlpass = 4 ANLFFS + + wnprt(2,"# ----- End Disassembly phase 2 -----"); + + turn_scans_into_code(); // turn all valid scans into code + +// scan_code_gaps(); // scan for code gaps TEMP !!! + + do_structs(); // then structs + +// turn_dtk_into_data(); // add data found fom dtk + + check_dtk_links(); + + //scan_gaps(); // BEFORE data, so only code, tabs,vects,funcs + + turn_dtk_into_data(); // add data found fom dtk + + + + // ---------------------------------------------------------------------- + + + + + + + show_prog (++anlpass); //anlpass = 5 = ANLPRT + + #ifdef XDBGX + DBG_data(); + #endif + + wnprt(2,"# ----- Output Listing to file %s", fldata.fn[1]); + + do_listing (); + pstr(2,0); // flush output file + pstr(2, " ########## END of Listing ##########"); + + + +// could put messages in here with wnprt................. + + + wnprt(2,0); + wnprt(1,"# ---------------------------------------------------------------------------------------------"); + wnprt(1,"# The disassembler has scanned the binary and produced the following command list."); + wnprt(1,"# This list is not guaranteed to be perfect, but should be a good base."); + wnprt(1,"# Commented lines for information bu may be uncommented for use (e.g. banks)"); + wnprt(1,"# This following list can be copied and pasted into a directives file."); + wnprt(3,"# ---------------------------------------------------------------------------------------------"); + + prt_dirs(); + free_structs(); + mfree(opcbit, 0x2000); + wnprt(2,0); + wnprt(2, "# ----- END of disassembly run -----"); + printf ("\n END of run\n"); + closefiles(); + return 0; +} + + +int get_config(char** pts) + { + /* pts [0] = exe path (and default) , + * [1] = config file READ path or zero. (use default path) + * both from command line + */ + + short i; + char *t; + + // PATHCHAR defined at beginning with file option flags + + t = pts[0]; + if (*t == '\"' || *t == '\'') t++; // skip any opening quote + strcpy(fldata.dpath, t); + + // if (!t) printf("error in cmdline !!"); // never true ?? + + t = strrchr(fldata.dpath, PATHCHAR); // stop at last backslash to get path + if (t) *t = '\0'; + + if (pts[1]) + { + strncpy(fldata.fn[6],pts[1], 255); // copy max of 255 chars + i = strlen(fldata.fn[6]); // size to get last char + t = fldata.fn[6]+i-1; // t = last char + if (*t == PATHCHAR) *t = 0; else t++; + + sprintf(t, "%c%s",PATHCHAR,fd[6].suffix); // and add sad.ini to it... + + fldata.fl[6] = fopen(fldata.fn[6], fd[6].fpars); + + if (!fldata.fl[6]) + { + printf("\ncan't find config file %s\n", fldata.fn[5]); + return 1; // no config file in -c + } + } + + else + { + // else use bin path to make up file file name of SAD.INI + sprintf(fldata.fn[6], "%s%c%s",fldata.dpath,PATHCHAR,fd[6].suffix); + fldata.fl[6] = fopen(fldata.fn[6], fd[6].fpars); //open it if there + } + + + if (fldata.fl[6]) + { // file exists, read it + for (i = 0; i< 5; i++) + { + if (fgets (flbuf, 256, fldata.fl[6])) + { + t = strchr(flbuf, ' '); // first space + if (!t) t = strchr(flbuf,'#'); // or comment + if (!t) t = strchr(flbuf, '\n'); // or newline + if (t) + { + *t = '\0'; + t--; + if (*t != PATHCHAR) sprintf(t+1, "%c", PATHCHAR); + } + flbuf[255] = '\0'; // safety + strcpy(fldata.fn[i], flbuf); + } + } + fclose(fldata.fl[6]); //close leaves handle addr + fldata.fl[6] = NULL; + strcpy(fldata.path, fldata.fn[0]); + strcpy(fldata.fn[5], fldata.fn[2]); //copy wrn path to dbg path + + // printf("config file OK\n"); + } + else printf(" - not found, assume local dir\n"); + + return 0; + } diff --git a/Development/sources/4.07.16b/chain.cpp b/Development/sources/4.07.16b/chain.cpp new file mode 100644 index 0000000..1874295 --- /dev/null +++ b/Development/sources/4.07.16b/chain.cpp @@ -0,0 +1,5707 @@ + +#include "core.h" // this calls shared.h + + +extern HDATA fldata; // files data holder (in shared.h) +extern DIRS dirs[]; + +extern BASP basepars; // min and max ram register address (for checking) +extern BANK bkmap[]; +extern OPC opctbl[]; + +extern int anlpass; +extern int cmdopts; +extern int numbanks; +extern uint lowscan; + +extern uint rbinv[]; +extern const char *cmds[]; + +extern INST cinst; +extern INST sinst; + +void scan_blk(SBK*, INST*); + +void scan_loop(SBK *, INST *, uint); + +uint get_flag (uint, uint *); + +uint wnprt (uint, const char *, ...); + +uint val_rom_addr(uint); +uint val_general_reg(uint); +uint is_special_reg(uint); +uint get_opdata(uint); +uint codebank(uint, INST *); +uint databank(uint, INST *); +uint get_pn_opstart (uint ofst, int d); + +uint maxbkadd (uint); +uint maxpcadd (uint); +int g_byte (uint addr); +int g_word (uint addr); +int g_val (uint, uint, uint); +uint bytes(uint); + +int cellsize(ADT *); + +uint get_signmask(uint); + +uint fix_addr_bank(uint); + +int new_symname (SYM *, const char *); + +SYM *new_autosym (uint, int); + +int check_sfill(uint addr); + +uint find_opcode(int d, uint xofst, OPC **opl); +void do_one_opcode( uint xofst); + +uint max_reg(void); +uint val_stack_reg(uint); + +#ifdef XDBGX + +extern int DBGrecurse; +extern int DBGnumops; + +extern int DBGmcalls; // malloc calls +extern int DBGmrels; // malloc releases +extern int DBGmaxalloc; // TEMP for malloc tracing +extern int DBGcuralloc; + +extern const char *jtxt[]; + uint DBGPRT (uint, const char *, ...); + void DBGPRTFGS(int ,LBK *,LBK *); + void DBGPBK(int, LBK *, const char *, ...); + +#endif + +void *shuffle[16]; // for chain reorganises (and deletes), a block of void pointers + + + +/********************************************************************************** +* CHAIN declarations, used ordered storage with for binary chop searches +* +* CHAIN structures use common find and insert mechanisms with defined subroutines for +* each chain type, Each type has its own structure associated with it. +* searched with 'binary chop' type algorithm. +* CHAIN params = num entries, num ptrs, allocsize, entrysize, maxptrs, num mallocs, ptr array, +* subroutines - free, compare, equals, (debug) +***********************************************************************************/ + +// Free block + +void fsym(void *); void fsub(void *); +void fcmd(void *); void fblk(void *); + +// compare, for binary chop searches + +int cpjmp (CHAIN *,uint, void *); int cpbase (CHAIN *,uint, void *); +int cpcmd (CHAIN *,uint, void *); int cpscan (CHAIN *,uint, void *); +int cpsub (CHAIN *,uint, void *); int cpsym (CHAIN *,uint, void *); +int cpsig (CHAIN *,uint, void *); int cpsscn (CHAIN *,uint, void *); +int cprgs (CHAIN *,uint, void *); int cpadt (CHAIN *,uint, void *); +int cpspf (CHAIN *,uint, void *); int cppsw (CHAIN *,uint, void *); +int cpdtk (CHAIN *,uint, void *); int cpdtka (CHAIN *,uint, void *); + +// chains for holding data + +CHAIN chjpf = {0,0,200,sizeof(JMP), 0,0,0,0,0, 0 ,cpjmp }; // jump, indexed by 'from' +CHAIN chjpt = {0,0,200,0 , 0,0,0,0,0, 0 ,cpjmp }; // jump, indexed by 'to' [REMOTE] + +CHAIN chsym = {0,0,200,sizeof(SYM), 0,0,0,0,0, fsym ,cpsym }; // symbols + +CHAIN chbase = {0,0,200,sizeof(RBT), 0,0,0,0,0, 0 ,cpbase }; // rbases +CHAIN chsig = {0,0, 20,sizeof(SIG), 0,0,0,0,0, 0 ,cpsig }; // signatures + +CHAIN chcmd = {0,0,200,sizeof(LBK), 0,0,0,0,0, fcmd ,cpcmd }; // main code and data commands +CHAIN chaux = {0,0, 50,sizeof(LBK), 0,0,0,0,0, fcmd ,cpcmd }; // auxilliary commands (arg, xcode, ...) + +CHAIN chscan = {0,0,200,sizeof(SBK), 0,0,0,0,0, 0 ,cpscan }; // code scans +CHAIN chemul = {0,0, 20,sizeof(SBK), 0,0,0,0,0, 0 ,cpscan }; // code emulations +CHAIN chsbcn = {0,0, 20,0 , 0,0,0,0,0, 0 ,cpsscn }; // subroutine scans - [REMOTE] + +CHAIN chsgap = {0,0, 20,0 , 0,0,0,0,0, 0 ,cpsscn }; // gap scans - [REMOTE] + +CHAIN chsubr = {0,0, 30,sizeof(SUB), 0,0,0,0,0, fsub ,cpsub }; // subroutines + +CHAIN chadnl = {0,0, 50,sizeof(ADT), 0,0,0,0,0, 0 ,cpadt }; // additional data, linked by FK + +CHAIN chadcm = {0,0, 20,sizeof(ADT), 0,0,0,0,0, 0 ,cpadt }; // additional data for user commands + +CHAIN chans = {0,0, 50,sizeof(ADT), 0,0,0,0,0, 0 ,cpadt }; // additional data - subroutine answers - ADT ??? would SPF be better ?? + +CHAIN chrgst = {0,0, 20,sizeof(RST), 0,0,0,0,0, 0 ,cprgs }; // register statuses (for arg calcs) + +CHAIN chspf = {0,0, 10,sizeof(SPF), 0,0,0,0,0, 0 ,cpspf }; // special funcs (subroutines, tablu, funclu etc.) + +CHAIN chpsw = {0,0, 20,sizeof(PSW), 0,0,0,0,0, 0 ,cppsw }; // psw setters + +CHAIN chdtk = {0,0,500,sizeof(TRK), 0,0,0,0,0, 0 ,cpdtk }; // data opcode tracking. index by ofst +CHAIN chdtkr = {0,0,500,0 , 0,0,0,0,0, 0 ,cpdtk }; // data opcode tracking. index by register + + +CHAIN chdtko = {0,0,200,sizeof(DTD), 0,0,0,0,0, 0 ,cpdtka }; // data address tracking. index by fk (ofst) +CHAIN chdtkd = {0,0,500,0 , 0,0,0,0,0, 0 ,cpdtka }; // data address tracking. index by start [REMOTE] + +CHAIN mblk = {0,0, 50,sizeof(MBL), 0,0,0,0,0, fblk ,0 }; // malloc tracking (for cleanup) ** must be last ** + + +// CHAIN Pointer array for neater admin and external routines. + +CHAIN *chindex[] = { &chjpf, &chjpt , &chsym, &chbase, &chsig, + &chcmd, &chaux , &chscan, &chemul, &chsbcn, &chsubr , &chadnl, &chadcm, &chans, &chrgst, + &chspf, &chpsw, &chdtk, &chdtkr, &chdtko, &chdtkd, &mblk}; + +#ifdef XDBGX +// names for chains for debug prtouts, and debug block printers... + + const char *chntxt[] = {"jump from", "jump to", "symbol", "rbase", "sign", "cmd", "aux", + "scan", "emulscan", "subscan", "subroutines", "adnl data", "adnl cmd", "adnl ans", "regstat", + "spf" , "psw" , "dtrack" , "dtrack r", "dtrackd ofst", "dtrackd start" , "monblk"}; + +#endif + + + + uint adds [10]; + uint szes [32]; + + + + + + + +void mfree(void *addr, size_t size) + { + // free malloc with tracking + if (!addr) return; // safety + + #ifdef XDBGX + DBGcuralloc -= size; // keep track of mallocs + DBGmrels++; + #endif + + free (addr); + } + + +void* mem(void *ptr, size_t osize, size_t nsize) +{ // malloc with local tracking + void *ans; + if (nsize < 1) wnprt(1,"Invalid malloc size"); + + #ifdef XDBGX + if (ptr) DBGcuralloc -= osize; + DBGmcalls++; + DBGcuralloc += nsize; + if (DBGcuralloc > DBGmaxalloc) DBGmaxalloc = DBGcuralloc; + #endif + + ans = realloc(ptr, nsize); + + if (!ans) wnprt(1,"Run out of malloc space"); + return ans; +} + + +void adpch(CHAIN *); // declared for logblk + +void logblk(void *blk, size_t size) +{ + MBL *b; + + if (!blk) return; + + if ((mblk.pnum - mblk.num) < 2) adpch(&mblk); // need more pointers + + b = (MBL*) mblk.ptrs[mblk.num]; // no point in using chmem + b->blk = blk; + b->size = size; + mblk.num ++; + if (mblk.num > mblk.DBGmax) mblk.DBGmax = mblk.num; // for debug really +} + + //---------------------------------------------------------------- + // add pointers for chains. malloced entries + // pointer block is realloced, so is continuous... + // but data blocks are NOT.... + //---------------------------------------------------------------- + +void adpch(CHAIN *x) + { // add pointers for more chain pointers (malloced entries) + // pointer block is realloced, so is always contiguous. + // data blocks are not. + + int old, nw, msz,i; + char *z, *n; + + // grow pointer array - always as single block for arrays + old = x->pnum; + x->pnum += x->asize; + x->DBGallocs++; + + nw = x->pnum; // new size + x->ptrs = (void**) mem(x->ptrs, old*sizeof(void*), nw*sizeof(void*)); + + // allocate new entries if not a remote chain, and save in mbl chain + + n = NULL; + msz = 0; + + if (x->esize) + { + msz = x->esize*x->asize; // size of new data block to go with ptrs + n = (char *) mem(0,0,msz); + if (!n) wnprt(0,"FATAL - MALLOC ERROR "); + } + + z = n; + for (i = old; i < nw; i++) + { + x->ptrs[i] = z; + z += x->esize; + } + + logblk(n, msz); + + } + + +void* chmem(CHAIN *x, uint ix) + { + // get a free entry of the chain for search or insert. + // this is from x->num onwards (next free entry) + ix for nested use + + void *ptr; + uint num; + + num = x->num + ix; + + if (!x->esize) return NULL; // can't have memory on a remote chain... + + if (x->pnum < (num+2)) adpch(x); // need more pointers (unsigned check) + + ptr = x->ptrs[num]; // use next free block (at end of list) + memset(ptr,0, x->esize); // and clear it + return ptr; + } + + +void chinsert(CHAIN *x, int ix, void *blk) +{ + // insert new pointer (= blk) at index ix + // relies on main pointer block being contiguous and blk being x->num; + + if (!blk) return ; + + if ((x->pnum - x->num) < 2) adpch(x); // need more pointers + + // move entries up one from ix to make a space. + memmove(x->ptrs+ix+1,x->ptrs+ix, sizeof(void*) * (x->num-ix)); // fastest for contiguous + + x->ptrs[ix] = blk; // insert new block (x->num) at ix + x->num++; + + x->lastix = x->num; // invalidate any get_next + #ifdef XDBGX + if (x->num > x->DBGmax) x->DBGmax = x->num; // for debug + #endif + + +} + + +void chdelete(CHAIN *x, uint ix, int num) +{ + // drop num block(s) from list from ix, shuffle down rest of + // list and restore ptr(s) after x->num. + // relies on main pointer block being contiguous + // shuffle array to hold pointers (max 16) + // extra +1 in shuffle is for x->num block (used by chmem) + + uint i; + + if (ix >= x->num) return; + + if (x->cfree) + { //free any additional items + for (i = ix; i < ix+num; i++) x->cfree(x->ptrs[i]); + } + + // save pointers about to be 'deleted' (released) in temp array + memcpy(shuffle, x->ptrs+ix, sizeof(void*) * num); + + x->num -= num; //drop no of defined entries + + // move entries down by 'num' from ix+num to overwrite released entries, with extra chmem(0) pointer. + memmove(x->ptrs+ix, x->ptrs+ix+num, sizeof(void*) * (x->num-ix+1)); + + // put released pointers back after new x->num position so they become unused + memcpy(x->ptrs + x->num+1, shuffle, sizeof(void*) * num); + x->lastix = x->num; // invalidate any get_next +} + + + + +void fblk(void *x) +{ // malloced blocks tracker (free'd last) + MBL *k; + k = (MBL *) x; + mfree(k->blk, k->size); +} + +void clearchain(CHAIN *x) +{ + // clear pointer list contents, but not pointers themselves. Free any attached structures + // NB. count DOWN so that for mblks first entry (which points to itself) is released last. + + int i; + + for (i = x->num-1; i >=0; i--) + { + if (x->cfree) x->cfree(x->ptrs[i]); + } + + x->num = 0; +} + + +void freeptrs(CHAIN *x) +{ + // free the master pointer block of chain x (after clearchain does entries) + if (x->pnum) + { + mfree(x->ptrs, x->pnum * sizeof(void*)); + x->ptrs = NULL; + x->pnum = 0; + } +} + +void freechain(int ix) +{ + CHAIN *x; + + x = chindex[ix]; + + #ifdef XDBGX + int size; + size = x->pnum * sizeof(void *); // number of void* pointers + size += x->pnum * x->esize; // plus structs pointed to + DBGPRT(1,"max=%5d tot=%5d allocs=%2d esize=%3d size=%6d %s", x->DBGmax, x->pnum, x->DBGallocs, x->esize, size, chntxt[ix]); + #endif + + clearchain(x); + freeptrs(x); +} + +void free_structs () +{ // complete tidyup for closing or restart + uint i; +BANK *b; + for (i = 0; i < BMAX; i++) + { + b = bkmap + i; + + if (b->bok) + { + mfree(b->fbuf, 0x10000); + } + } + + for (i = 0; i < NC(chindex); i++) freechain(i); // this includes mblock + + #ifdef XDBGX + DBGPRT(1,"max alloc = %d (%dK)", DBGmaxalloc, DBGmaxalloc/1024); + DBGPRT(1,"cur alloc = %d", DBGcuralloc); + DBGPRT(1,"mcalls = %d", DBGmcalls); + DBGPRT(1,"mrels = %d", DBGmrels); + DBGPRT(1,"Max recurse = %d", DBGrecurse); + DBGPRT(1,"Num Opcodes = %d", DBGnumops); + #endif + + fflush (fldata.fl[1]); // flush output files (safety) + fflush (fldata.fl[2]); + + +} + + + +//---------------------------------------------------------------------------------------/ + +// CHAIN Compare procs used by bfindix for binary chop chain searches +// Straight subtract works nicely for chain ordering, even for pointers +// < 0 less than, 0 equals, > 0 greater than + +int cpjmp (CHAIN *x, uint ix, void *d) +{ + int ans; + JMP *j, *t; + + if (ix >= x->num) return -1; + + j = (JMP*) x->ptrs[ix]; + t = (JMP*) d; + + // 'fromaddr' is unique, 'toaddr' is not. + + if (x == &chjpt) + { // to chain + // includes fromaddr (0 = first from) + ans = j->toaddr - t->toaddr; + if (ans) return ans; + ans = j->fromaddr - t->fromaddr; + } + else + { // from chain. fromaddr is unique + ans = j->fromaddr - t->fromaddr; + } + + return ans; +} + + +int cpsym (CHAIN *x, uint ix, void *d) +{ + SYM *s, *t; + int ans; + if (ix >= x->num) return -1; + + s = (SYM*) x->ptrs[ix]; + t = (SYM*) d; + + ans = s->addb - t->addb; // address,bitno, read/write combined + if (ans) return ans; + + return t->rstart - s->rstart; // by range start, in reverse order + +} + + +int cpbase (CHAIN *x, uint ix, void *d) +{ + RBT *b, *t; + int ans; + + if (ix >= x->num) return -1; + b = (RBT*) x->ptrs[ix]; + t = (RBT*) d; + + ans = b->reg - t->reg; + if (ans) return ans; + + return t->rstart - b->rstart; // by start, in reverse order + +// return ans; +} + + +int cpsig (CHAIN *x, uint ix, void *d) +{ + SIG *g, *t; + int ans; + + if (ix >= x->num) return -1; + g = (SIG*) x->ptrs[ix]; + t = (SIG*) d; + + ans = g->end - t->start; + if (ans) return ans; + + if (t->ptn) ans = g->ptn - t->ptn; + return ans; +} + + +int cprgs (CHAIN *x, uint ix, void *d) +{ + // d (t) is candidate blk + + RST *s, *t; + int ans; + + if (ix >= x->num) return -1; + + s = (RST*) x->ptrs[ix]; + t = (RST*) d; + + ans = s->reg - t->reg; + + return ans; + +} + +int cpspf (CHAIN *x, uint ix, void *d) +{ + // d (t) is candidate blk + //only ever one spf per subr, so unique + + SPF *s, *t; + + if (ix >= x->num) return -1; + + s = (SPF*) x->ptrs[ix]; + t = (SPF*) d; + + return (long) s->pkey - (long) t->pkey; + +} + +int cpadt (CHAIN *x, uint ix, void *d) +{ + // d (t) is candidate blk + + ADT *s, *t; + int ans; + + if (ix >= x->num) return -1; + + s = (ADT*) x->ptrs[ix]; + t = (ADT*) d; + +// ans = (long) s->fid - (long) t->fid; + +// ans = + +// if (ans) return ans; + +// ans = s->seq - t->seq; + +ans = s->fkey - t->fkey; + + return ans; + +} + + +int cpscan (CHAIN *x, uint ix, void *d) +{ + // d (t) is candidate blk + + SBK *s, *t; + int ans; + + if (ix >= x->num) return -1; + + s = (SBK*) x->ptrs[ix]; + t = (SBK*) d; + + ans = s->start - t->start; + if (ans) return ans; + + // and subroutine for scans + + if (t->substart) ans = s->substart - t->substart; +// if (ans) return ans; + +return ans; +} + + +int cpsscn (CHAIN *x, uint ix, void *d) +{ + // subscan - d (t) is candidate blk + + // use subroutine address to ensure + // all branches are scanned + + SBK *s, *t; + int ans; + + if (ix >= x->num) return -1; + + s = (SBK*) x->ptrs[ix]; + t = (SBK*) d; + + ans = s->substart - t->substart; + + return ans; + +} + +int cppsw (CHAIN *x, uint ix, void *d) +{ + // subscan - d (t) is candidate blk + + PSW *s, *t; + int ans; + + if (ix >= x->num) return -1; + + s = (PSW*) x->ptrs[ix]; + t = (PSW*) d; + + ans = s->jstart - t->jstart; + + return ans; + +} + +int cpdtk (CHAIN *x, uint ix, void *d) +{ + TRK *s, *t; + int ans; + + if (ix >= x->num) return -1; + + s = (TRK*) x->ptrs[ix]; + t = (TRK*) d; + + +//if (x == &chdtk) + { + // ofst for main entries + + ans = s->ofst - t->ofst; + return ans; + } + +/* +if ( x == &chdtkr) + { + ans = s->? +*/ + +// return ans; +} + + + + + + + + + +/* if (x == &chdtkd) + { // index by data address - not unique - jmp example......... + + if (t->fromaddr) ans = j->fromaddr - t->fromaddr; + else + { // get to minimum matching 'to' if no 'from' specified + if (ix > 0) + { + j = (JMP*) x->ptrs[ix-1]; + if (j->toaddr == t->toaddr) ans = 1; //go back + } + } + + + + + + + ans = s->start - t->start; + if (ans) return ans; + + if (t->rreg) + ans = s->rreg - t->rreg; + if (ans) return ans; + + if (t->ofst) +ans = s->ofst - t->ofst; + + return ans; +} + + + + if (x == &chdtkr) + { // index by register and offset, not unique ? + + ans = s->reg1 - t->reg1; + if (ans) return ans; + +// ans = s->start - t->start; +// if (ans) return ans; + + if (t->off) + ans = s->off - t->off; + if (ans) return ans; + + ans = s->ofst - t->ofst; + + +} + + return ans; +} +// */ + +int cpdtka (CHAIN *x, uint ix, void *d) +{ + // d (t) is candidate blk + + DTD *s, *t; + int ans; + + if (ix >= x->num) return -1; + + s = (DTD*) x->ptrs[ix]; + t = (DTD*) d; + + if (x == &chdtko) + { //key first + ans = s->fk - t->fk; + if (ans) return ans; + + //ofst first. + ans = s->stdat - t->stdat; + return ans; + } + + ans = s->stdat - t->stdat; + if (ans) return ans; + + ans = s->fk - t->fk; + return ans; + +} + + + + + + + + +int cpsub (CHAIN *x, uint ix, void *d) +{ + // add a check here for pushp subrs ?. + SUB *s, *t; + + if (ix >= x->num) return -1; + + s = (SUB*) x->ptrs[ix]; + t = (SUB*) d; + + //if (s->psp && t->start == s->start+1) return 0; // pushp match + + return s->start - t->start; +} + + +int cpcmd (CHAIN *x, uint ix, void *d) +{ + // this compare checks END with start for address ranges + // therefore [ix].end <= d->start. + + LBK *b, *t; + int ans; + + if (ix >= x->num) return -1; + b = (LBK*) x->ptrs[ix]; + t = (LBK*) d; + + ans = b->end - t->start; + +// if (ans) return ans; + +// reverse order for command (code at very front) +// ans = t->fcom - b->fcom; + + return ans; + +} + + +int eqsig (CHAIN *x, uint ix, void *d) +{ +//cpsig with extra check for skips + +// CHANGE THIS to use END addresses, like cmd, to allow for skips at front + + SIG *g, *t; + int ans; + + if (ix >= x->num) return -1; + g = (SIG*) x->ptrs[ix]; + t = (SIG*) d; + + ans = 1; + + // allow for skips at front......... + if (g->start <= t->start && (g->start + g->skp) >= t->start) ans = 0; + if (!ans && t->ptn) ans = g->ptn - t->ptn; + return ans; + +} + + + +int eqcmd (CHAIN *x, uint ix, void *d) +{ + // make sure candidate block falls within + // indiexed block start and end. + + LBK *b, *t; + + if (ix >= x->num) return -1; + + b = (LBK*) x->ptrs[ix]; + t = (LBK*) d; + + if (t->start >= b->start && t->start <= b->end) + { + if (!t->fcom || t->fcom == b->fcom) return 0; + } + return 1; +} + + +uint bfindix(CHAIN *x, void *blk) //, int (*cmp) (CHAIN *x, uint ix, void *blk)) +{ + // generic binary chop. + // send back an INDEX value to allow access to adjacent blocks. + // use a compare subr as supplied or default for chain struct. + // Candidate block is void* and cast to reqd type inside cmp (compare subroutine). + // answer (min) is always min <= blk, or blk is always >= min (= answer) + // and min+1 is > blk. Answer is therefore INSERT point for new block, or FOUND. + + uint tst, min, max; + +// if (!cmp) cmp = x->comp; // use std chain compare subr if not given + + // NB. this loop could work for subsearches too..... + + min = 0; + max = x->num; + + while (min < max) + { + tst = min + ((max - min) / 2); // safest way to do midpoint of (min, max); + if(x->comp(x,tst,blk) < 0) min = tst + 1; // blk > tst, so new minimum is (tst+1) + else max = tst; // blk <= tst, so new maximum is tst + } + + x->lastix = min; // keep last index (even if x->num) + return min; +} + + + + + +int olchk(CHAIN *x, uint ix, LBK *newb) + { + + /***** overlap and overwrite check if - commands can be merged/overwrite etc + * b is 'candidate' (new) block , t is existing (test) block at ix + * set answer as bit mask - + * 1 new block spans t (t within newb) + * 2 new block within t (t spans newb) + * 4 front overlap or front adjacent + * 8 rear overlap or rear adjacent + + +no overrides, just overlaps and fail if not same command ???? + + + + * 10 t overrides newb (newb cannot be inserted) + * 20 newb overrides t (can overwrite it) + * 40 merge allowed (flag set and commands match) + ************/ + + LBK *t; + int ans; + + if (ix > x->num) return 0; + + t = (LBK*) x->ptrs[ix]; + + if (t == newb) return 0; // safety check + + ans = 0; //clear all flags; + + // NB. check for equal already done in inscmd + + if (newb->start <= t->start && newb->end >= t->end) ans |= 1; // newb spans t + else + if (newb->start >= t->start && newb->end <= t->end) ans |= 2; // newb is spanned by t + else + { // overlap, front or back + if (newb->start < t->start && newb->end >= t->start && newb->end <= t->end) ans |= 4; // overlap front + if (newb->end > t->end && newb->start >= t->start && newb->start <= t->end) ans |= 8; // overlap rear + } + + // if (ans) + // { // check overwrite + // if (!t->cmd && dirs[newb->fcom].maxovr >= t->fcom) ans |= 0x20; // newb can overwrite t + // if (!newb->cmd && dirs[t->fcom].maxovr > newb->fcom) ans |= 0x10; // newb cannot overwrite t - relevant for part overlaps + // } + + if (dirs[t->fcom].merge && t->fcom == newb->fcom) + { //only merge matching commands. + if (ans) ans |= 0x40; // overlap set already, merge allowed + if (newb->end == t->start-1) ans |= 0x44; // adjacent to front of test block (merge+front) + if (newb->start == t->end+1) ans |= 0x48; // adjacent to end of test block (merge+rear) + } + +// also check here for adnl blocks which would break any merges being allowed. + + +// do the dbg print HERE for both blocks + + #ifdef XDBGX + if (ans) DBGPRTFGS(ans,newb,t); + #endif + + return ans; +} + + +uint find_olap(CHAIN *x, uint ix, void *newb, uint up) //upwards + +{ +//up 1 = up, 0 = down + + + + // std compare checks END address with start, for address ranges + // therefore [ix].end >= d->start ALWAYS. + // this is a special version for checking OVERLAPS. + + // If d.start is specified, find the MINIMUM ix where d.start + // is overlapped or adjacent, where [ix].end > d.start. + + // If d.end is specified, find the MAXIMUM ix where d.end is + // is overlapped or adjacent, where [ix].end > d.end. + // for LBK structs (data and code). + + LBK *b, *t; + // int ans; + // uint max; + +// ans = -1; + if (ix >= x->num) return x->num-1; + + b = (LBK*) x->ptrs[ix]; + t = (LBK*) newb; + +// ans = b->start - t->start; is std cmp + + if (up) + { + // get to maximum matching block, overlap or adjacent + // t->start always <= b->start + while (ix < x->num) + { + if (t->end <= (b->start-1)) break; + ix++; + b = (LBK*) x->ptrs[ix]; + } + if (ix >= x->num) ix = x->num - 1; + } + + if (!up) + { // down to minimum extra adjacent check (different logic to END check) + //skip back one first so in front of insert + if (ix == 0) return ix; + ix--; + while (ix < x->num) + { // handles wrap.............. + b = (LBK*) x->ptrs[ix]; + if (b->end < t->start-1) break; + ix--; + } + //if (ix >= 0) ix = 0; + } + return ix; + +} + + +int inschk(CHAIN *x, int ix, LBK *newb) +{ + + /* check if insert is allowed, fix any overlaps later. + * newb is insert candidate. check range of possible overlaps. + * ix is where insert would be (if going recursive....) + * probably better to delete blocks because of ADT issues....but causes issue with A9L + * answer is bit mask from olchck + * 1 new block spans t (t within newb) must check for overlaps........ + * 2 new block within t (t spans newb) + * 4 front overlap or front adjacent + * 8 rear overlap or rear adjacent + * 10 t overrides newb (newb cannot be inserted) for part overlaps + * 20 newb overrides t (can overwrite t) + * 40 merge allowed (flag set and commands match) + */ + + LBK *t; + uint chkf, min, max; + int ans; + + // OK, find min and max overlap + + if (x->num == 0) return 1; //ok to insert + + // ix is where insert would be + + + max = find_olap(x,ix,newb,1); //upwards + min = find_olap(x,ix,newb,0); //downwards + + ans = 1; // set 'insert OK' + + while (min <= max && ans) + { // scan range of possible overlaps + if (max >= x->num) break; + if (min >= x->num) break; // safety for deleted items - perhaps should restart............ + + chkf = olchk(x, min, newb); // check chained block for overlap + + if (chkf) + { + t = (LBK*) x->ptrs[min]; + + if (newb->fcom > t->fcom) // chkf & 0x20) TEMP !! need more here + { // new block overrides chain block + if (chkf & 1) + { //new block spans the existing one (check cmd ?) + #ifdef XDBGX + DBGPBK(0,t,"delete1 (%d)", min); + #endif + chdelete(x, min, 1); + //probably better to redo the whole thing ? + min--; + max--; // match is now 1 shorter (but is it ? min won't be less ??) + } + else + if (chkf & 2) + { // new block is contained within chain block, but overrides it + // if front overlaps (4), move front of chain block + // if end overlaps (8), move end of chain block + // NO !!! + // ans = 0; // no insert + // return ans; + + if (chkf & 4) t->start = newb->end+1; + else + if (chkf & 8) t->end = newb->start-1; + // panic here + } + else + { // overlap, can overwrite + if (chkf & 4) //F) + { // front of new block overlaps chain block + t->end = newb->start-1; // change end + } + if (chkf & 8) //R) + { // end of new block overlaps start of of chain block + t->start = newb->end+1; + } + } + // more cases ??? + + + + + + + + + } // end 0x20 + else + if (chkf & 0x10) //V) + { + // chained block overrides new block + if (chkf & 4) newb->start = t->end+1; + else + if (chkf & 8) //R) + { + newb->end = t->start-1; + } + + // check start and end .... + + if (chkf & 2 || newb->start < newb->end) + { // contains, (or now empty). Reject insert + ans = 0; // no insert + // return ans; + } + + } + else + if (chkf & 2) //C) + { + ans = 0; // no insert + // return ans; + } + else + + if (chkf & 0x40) // TEST for span as well ? fewer errors, but....word over byte ?? + { + // merge allowed, if multiple in this loop, + // merge new block with chain then stop insert ? drop chain entry + + if (t->start < newb->start) newb->start = t->start; + if (t->end > newb->end) newb->end = t->end; + #ifdef XDBGX + DBGPRT(0,"(%d) ", min); + DBGPBK(0,t,"delete2"); + #endif + chdelete(x, min, 1); + min--; + max--; // match is now 1 shorter + } + else + +// but if TWO merges (i.e. new block spans gap between two chains, +// MUST drop one...... + + + +// where is s ? + { + // default catch + #ifdef XDBGX + // can ignore issues for same command + if (newb->fcom != t->fcom) + { + DBGPRT(0,"(%d) ",min); + DBGPBK(0,newb, "DFLT Reject"); + } + #endif + ans = 0; // temp stop insert + } + #ifdef XDBGX + DBGPRT(1,0); + #endif + } // end if chk set + min++; + } // end loop + + return ans; // insert if not zero +} + + + + +LBK* inscmd(CHAIN *x, LBK *blk) + { + /* need extra checks the for address ranges (start->end) + + * at this call 'blk' is always x->ptrs[x->num] .... + * do a simple check, THEN do more complex merging as necessary + + this works but does not merge anything yet. THis assumes you never get an END overlap ? + probably true for current scan setup, but not safe..... + + eqcmd only checks that start falls between start&end of chained block.... + + NB. Probably don't care about overlaps right now, can do later, but must capture + any extended END markers. + + so have to check for exact match, and non_allowed overlaps/overrides ? + + */ + + int ans; + uint ix; + LBK *k; + +// ans = 1; + + ix = bfindix(x, blk); // where to insert (from cpcmd) + + k = (LBK*) x->ptrs[ix]; + + ans = 1; //temp allow overlaps + // check for exact match + if (ix < x->num && k->start == blk->start && k->end == blk->end && k->fcom == blk->fcom) + { + if (!k->sys) x->lasterr = E_DUPL; + // ans = 0; + return 0; //no insert + } + + ans = inschk(x,ix,blk); // found - is insert allowed ? +//this may CHANGE IX !!! + + if (ans) + { + ix = bfindix(x, blk); // find again in case stuff deleted + chinsert(x, ix, blk); // do insert at ix + x->lasterr = 0; // lasterr + } + else + { + blk = (LBK*) x->ptrs[ix]; // overlap. (duplicate above) + x->lasterr = E_OVLP; // what about overlaps ?? + } + return blk; + +} + +// match for signature blocks + +SIG* inssig(SIG *blk) + { + + int ans; + uint ix; + SIG *s; + // ans = 1; // insert by default redundant + + ix = bfindix(&chsig, blk); + ans = eqsig(&chsig,ix,blk); // chsig.equal + + // if (!ans) return 0; // straight duplicate + +// check for duplicates with different skips at front... +// and check for overlap with next sig - but sign sig may be OK ? +// may need better check here for overlaps + + if (ix < chsig.num) + { // new sig overlaps ix (which is next block in chain) + s = (SIG*) chsig.ptrs[ix]; + + if (s->ptn == blk->ptn && s->start+s->skp == blk->start+blk->skp) + { // duplicate, only skips differ... + ans = 0; + } + } + + if (!ans) + { + return 0; + } + + chinsert(&chsig, ix, blk); // do insert + return blk; + +} + + + +LBK* add_cmd(uint start, uint end, uint com, uint from) + { + + LBK *b, *x; + uint tcom; + + chcmd.lasterr = E_INVA; // set "invalid address" + tcom = com & 0x1f; // drop any extra flags + + if (end < start) end = start; // safety + + //only code at 0x2000 in each bank + if (nobank(start) == 0x2000 && tcom != C_CODE) return NULL; + + + if (tcom == C_DFLT) + { // special for fill cmd + if (start > maxbkadd (start)) return NULL; + if (end > maxbkadd (end)) return NULL; + } + else + { + if (!val_rom_addr(start)) return NULL; + if (!val_rom_addr(end)) return NULL; + } + + if (g_bank(start)) + { + if (!g_bank(end)) end |= g_bank(start); // use bank from start addr + + // can get requests for 0xffff with bank, which will break boundary.... + + if (!bankeq(end, start)) + { + chcmd.lasterr = E_BKNM; // set "banks don't match" + return NULL; + } + } + + + if ((tcom == C_WORD || tcom == C_LONG || tcom == C_VECT) && (start & 1)) + { + // must start on even address + chcmd.lasterr = 4; + #ifdef XDBGX + DBGPRT(0,"FAIL add cmd"); + DBGPRT(0, " %x ODD Boundary from %x", start, from); + DBGPRT(1,0); + #endif + + + return NULL; + } + + b = (LBK *) chmem(&chcmd,0); + b->start = start; + b->end = end; + // b->from = from; + b->fcom = tcom; // max 31 as set + if (com & C_CMD) b->cmd = 1; + if (com & C_SYS) b->sys = 1; + if (PARGC) b->cptl = 1; + + + x = inscmd(&chcmd,b); + + // if (!chdata.lasterr) set_opdatar(start,end); // mark data + +// note that b may be WRONG here after merges etc. + + #ifdef XDBGX + if (!chcmd.lasterr) DBGPBK (0,b,"add cmd"); + else DBGPBK (0,b,"FAIL add cmd (%d)", chcmd.lasterr); + if (from) DBGPRT(0, "from %x", from); + DBGPRT(1,0); + #endif + + return x; +} + + + + + + + +// general chain finder for all chains + + + +PSW *add_psw(uint jstart, uint pswaddr) +{ + PSW *x; + uint ix; + int ans; + + chbase.lasterr = E_INVA; + if (!val_rom_addr(jstart)) return 0; + if (!val_rom_addr(pswaddr)) return 0; + + x = (PSW *) chmem(&chpsw,0); + x->jstart = jstart; + x->pswop = pswaddr; + + ix = bfindix(&chpsw, x); + ans = chpsw.comp(&chpsw,ix, x); + + if (ans) + { + chinsert(&chpsw, ix, x); + chpsw.lasterr = 0; + } + + #ifdef XDBGX + if (ans) DBGPRT(0,"Add"); else DBGPRT(0,"Fail"); + DBGPRT(0," pswset %x = %x", jstart, pswaddr); + DBGPRT(1,0); + #endif + + +if (ans) return x; + +chpsw.lasterr = E_DUPL; //duplicate +return 0; +} + +PSW *get_psw(uint addr) +{ + PSW *r; + uint ix; + + r = (PSW*) chmem(&chpsw, 1); + r->jstart = addr; + + ix = bfindix(&chpsw, r); + if (chpsw.comp(&chpsw,ix,r)) return NULL; // no match + return (PSW*) chpsw.ptrs[ix]; +} + +RBT *add_rbase(uint reg, uint addr, uint rstart, uint rend) +{ + RBT *x, *z; // val includes bank + uint ix; + int ans; + + chbase.lasterr = E_INVA; // set "invalid addr" + + if (reg & 1) return 0; // can't have odd registers + if (!val_general_reg(reg)) return 0; // not any special regs + if (addr > maxbkadd(addr)) return 0; // anywhere in address range + + if (rstart && !val_rom_addr(rstart)) return 0; // range must be within ROM - NO !! + + x = (RBT *) chmem(&chbase,0); + x->reg = reg; + x->val = addr; + x->rstart = rstart; + x->rend = rend; + + ix = bfindix(&chbase, x); + ans = chbase.comp(&chbase,ix, x); + + // check for overlap here + + z = (RBT *) chbase.ptrs[ix]; // chain block found, nearest below + + // ranges - can be within existing range, or outside as a new one + // check for overlaps CHANGE <= and >= to < and > FROM OLCHK COMMANDS + + if (ix < chbase.num) + { + int err; + + err = 0; + + if (x->rstart < z->rstart && x->rend >= z->rstart && x->rend <= z->rend) err = 1; //overlaps rstart + if (x->rend > z->rend && x->rstart >= z->rstart && x->rstart <= z->rend) err = 1; // overlap rear + + if (err) + { + chbase.lasterr = E_OVRG; // overlap ranges + + #ifdef XDBGX + if (ans) DBGPRT(0,"Add"); else DBGPRT(0,"Fail OLAP"); + DBGPRT(0," rbase R%x = %05x", reg, addr); + if (rstart) DBGPRT(0,"(%05x - %05x)", rstart, rend); + DBGPRT(1,0); + #endif + return 0; + } + } + + + if (ans) + { + chinsert(&chbase, ix, x); + chbase.lasterr = 0; + } + else + { + chbase.lasterr = E_DUPL; //duplicate + } + + #ifdef XDBGX + if (ans) DBGPRT(0,"Add"); else DBGPRT(0,"DUPL"); + DBGPRT(0," rbase R%x = %05x", reg, addr); + if (rstart) DBGPRT(0,"(%05x - %05x)", rstart, rend); + DBGPRT(1,0); + #endif + + if (ans) return x; + return 0; + +} + + +RBT* get_rbt(uint reg, uint pc) +{ + RBT *r, *x; + uint ix; + + r = (RBT*) chmem(&chbase,1); + r->reg = reg; + r->rstart = pc; + r->rend = pc; + + ix = bfindix(&chbase, r); + + if (ix < chbase.num) + { + x = (RBT*) chbase.ptrs[ix]; + if (!x->cmd && get_flag(reg, rbinv)) return NULL; // already marked as invalid, not user + if (x->inv) return NULL; + if (x->reg == r->reg && r->rstart >= x->rstart && r->rstart <= x->rend) return x; // match + } + +return NULL; + +} + + +RST* get_rgstat(uint reg) +{ + uint ix; + RST *b, *t; + + t = (RST *) chmem(&chrgst,0); + t->reg = reg; + + ix = bfindix(&chrgst, t); + b = (RST*) chrgst.ptrs[ix]; + + if (ix < chrgst.num) + { // exact match reqd + if (b->reg != t->reg) return NULL; + return b; // must match to get here + } + + return NULL; + +} + + +void set_rgsize(RST *r, uint fend) +{ + // need ix anyway so may as well use it + // only allow byte and word sizes.... + + if (!r) return; + + if ((fend & 15) > (r->fend & 15)) + { + r->fend = fend; // just do up to word size + #ifdef XDBGX + DBGPRT(1,"Reg R%x Fend = %d", r->reg, r->fend); + #endif + } +} + + +RST* add_rgstat(uint reg, uint fend, uint sreg, uint ofst) +{ + + RST *x; + int ix, ans; + + if (!val_general_reg(reg)) + { + #ifdef XDBGX + DBGPRT (0,"REJECT Add"); + DBGPRT (0," rgstat %x fend %d from %x", reg, fend, ofst); + DBGPRT(1,0); + #endif + return NULL; +} + + + x = (RST *) chmem(&chrgst,0); + x->reg = nobank(reg); + + + ix = bfindix(&chrgst, x); + ans = chrgst.comp(&chrgst,ix, x); + + if (ans) + { + chinsert(&chrgst, ix, x); + } + else + { + x = (RST*) chrgst.ptrs[ix]; + memset(x,0, sizeof(RST)); // and clear it + x->reg = reg; + } + x->ofst = ofst; + x->fend = fend; + x->sreg = sreg; + + #ifdef XDBGX + if (ans) DBGPRT (0,"Add"); else DBGPRT (0,"Dup"); + DBGPRT (0," rgstat %x fend %d ofst %x", x->reg, x->fend, x->ofst); + DBGPRT(1,0); + #endif + + return x; + +} + +SPF* add_spf (uint start) + { + // add special func block + // only one at the moment, otherwise need seq + + SPF *a; + int ix, ans; + + a = (SPF *) chmem(&chspf,0); + a->pkey = start; + + // always seq 0 + + ix = bfindix(&chspf,a); + ans = chadnl.comp(&chspf,ix, a); + + if (ans) + { + chinsert(&chspf, ix, a); + } + else + { + a = (SPF*) chspf.ptrs[ix]; + } + + return a; +} + +SPF *get_spf(uint start) +{ + int ix; + SPF *s; + + s = (SPF*) chmem(&chspf,1); + s->pkey = start; + + + ix = bfindix(&chspf, s); + + if (!chspf.comp(&chspf,ix,s)) + { + return (SPF*) chspf.ptrs[ix]; + } + +chspf.lastix = chspf.num; // invalid lastix +return NULL; +} + + +uint set_adt_fkey(uint ofst, uint seq) + { + uint fkey; + + fkey = (ofst << 8); // sequence is 1-254 + // check seq limits, 0 is new sequence start + if (seq > MAXSEQ) seq = MAXSEQ; + + fkey |= seq; + return fkey; +} + + + +ADT* add_adt (CHAIN *x, uint ofst, uint seq) + { + // add additional data block to fid + // if seq >= MAXSEQ (255) then add at end of chain (=seq+1) + // else add actual sequence number + + + ADT *a; + uint ix; + int ans; + + a = (ADT *) chmem(x,0); + + a->cnt = 1; + a->fstart = 0; + a->fend = 7; // single byte default + + if (!seq) seq = 1; // starts at 1 + a->fkey = set_adt_fkey(ofst, seq); + + // check if it exists already + + ix = bfindix(x,a); + + if (seq > MAXSEQ) + { // add new block with next avail seqno + ADT *z; + a->fkey = set_adt_fkey(ofst, 1); // default safety + z = (ADT*) x->ptrs[ix]; + + if (ix > 0) + { // get seq number from prev entry + z = (ADT*) x->ptrs[ix-1]; + if ((z->fkey & 0xfffff00) == (ofst << 8)) + { + if ((z->fkey & 0xff) >= MAXSEQ) return NULL; // max seq no reached + a->fkey = z->fkey+1; + } + } + } + + ans = chadnl.comp(x,ix, a); + + if (ans) + { + chinsert(x, ix, a); + } + else + { // exists already. Clear it and reset for use. may not be necessary ? + a = (ADT*) x->ptrs[ix]; + ix = a->fkey; + memset(a,0,sizeof(ADT)); + a->fkey = ix; //reset fk after clear + } + + x->lastix = x->num; //must reset any queries + return a; +} + + +LBK *get_cmd (uint start, uint fcom) +{ + // returns match if ofst within range of a data block (start-end) + LBK *blk; + int ix; + + blk = (LBK*) chmem(&chcmd,0); + blk->start = start; + blk->fcom = fcom; + + ix = bfindix(&chcmd, blk); + + // if command SPANs this address, will not always be selected. + +// while (ix < chcmd.num) +// { + // blk = (LBK*) chcmd.ptrs[ix]; +// if (p->end < start) return NULL; + + + + // if (p->start <= start && p->end >= start) return start = 0; + // } + // if (start) DBGPRT(1,"push %x from %x",start, d->ofst); + // } + + + + + + if (eqcmd(&chcmd,ix,blk)) return NULL; // no match + + return (LBK *) chcmd.ptrs[ix]; +} + +LBK *get_aux_cmd (uint start, uint fcom) +{ + // returns match if ofst within range of a data block (start-end) + LBK *blk; + int ix; + + blk = (LBK*) chmem(&chaux,0); + blk->start = start; + blk->fcom = fcom; + + ix = bfindix(&chaux, blk); + + if (eqcmd(&chaux,ix,blk)) return NULL; // no match + + return (LBK *) chaux.ptrs[ix]; +} + + + + + + + +RST *get_rgarg(uint reg, uint addr) +{ + uint i; + RST *r, *ans; + + ans = 0; + for (i = 0; i < chrgst.num; i++) + { + r = (RST*) chrgst.ptrs[i]; +// if (r->arg && r->sreg == reg) + if (r->ofst == addr) + { + ans = r; + break; + } + } + return ans; +} + + +SBK *get_scan(uint addr) +{ + SBK *x, *ans; + int ix; + + x = (SBK*) chmem(&chscan,1); // dummy block for search + x->start = addr; // symbol addr + + ans = NULL; + ix = bfindix(&chscan, x); + if (!chscan.comp(&chscan,ix,x)) + ans = (SBK *) chscan.ptrs[ix]; + + return ans; +} + + + + +SUB *get_subr(uint addr) +{ + SUB *s; + uint ix; + + if (!addr) return 0; // shortcut.. + s = (SUB*) chmem(&chsubr,1); + s->start = addr; + + ix = bfindix(&chsubr, s); + if (chsubr.comp(&chsubr,ix,s)) return NULL; // no match + return (SUB*) chsubr.ptrs[ix]; +} + + +SBK *get_scan(uint addr, uint subaddr) +{ + int ix; + SBK *s; + + s = (SBK*) chmem(&chscan,1); + s->start = addr; + s->substart = subaddr; + + ix = bfindix(&chscan, s) ; + + if (chscan.comp(&chscan,ix,s)) return NULL; + + return (SBK*) chscan.ptrs[ix]; + +} + + +ADT *start_adnl_loop(CHAIN *x, uint ofst) +{ + ADT *a; + a = (ADT*) chmem(x,0); + if (a) a->fkey = set_adt_fkey(ofst, 0); + return a; +} + + + +ADT *get_adnlk(CHAIN *x, uint fk) +{ + ADT *a; + uint ix; + +// speedup shortcut + + ix = x->lastix + 1; + + if (ix < x->num) + { // if valid lastix - speedup trick for get_next + a = (ADT*) x->ptrs[ix]; + + if (a->fkey == fk) + { // next item in sequence + x->lastix = ix; // last valid + return a; + } + } + + // otherwise, just look for it + + a = (ADT*) chmem(x,1); + a->fkey = fk; + + ix = bfindix(x, a); + if (!cpadt(x,ix,a)) return (ADT*) x->ptrs[ix]; + +// x->lastix = x->num; //invalidate if not found + return NULL; +} + + +ADT *get_adnl(CHAIN *x, uint ofst, uint seq) +{ + + return get_adnlk(x,set_adt_fkey(ofst, seq)); + +} + + +ADT *get_next_adnl(CHAIN *x, ADT *b) +{ + + if (!b) return NULL; + if ((b->fkey & 0xff) >= MAXSEQ) return NULL; + return get_adnlk(x, b->fkey+1); + +} + + +void free_adnl(CHAIN *x, void *fid) + { + + ADT *a, *z; + uint start, end; + + z = (ADT*) fid; + get_adnl(x,z->fkey,0); + + if (x->lastix >= x->num) return; + + start = x->lastix; + end = start; + + + while (1) + { + end++; + if (end >= x->num) break; + a = (ADT*) x->ptrs[end]; + if ((a->fkey & 0xfffff00) != (z->fkey & 0xfffff00)) break; + a->cnt = 0; + } + + //chdelete(&chadnl,start,end-start); //but this is done in tidyup anyway + + + return; + } + + + + + + + + + + + + +TRK *get_dtk(uint ofst) +{ + uint ix; + TRK *s; + + s = (TRK*) chmem(&chdtk,1); //always master chain + s->ofst = ofst; + + ix = bfindix(&chdtk, s); + +if (ix < chdtk.num) + { + s = (TRK*) chdtk.ptrs[ix]; + if (ofst == s-> ofst) return s; + } + +chdtk.lastix = chdtk.num; //invalidate if not found +return NULL; //(DTKD*) x->ptrs[ix]; + +} + +DTD *get_dtkd(CHAIN *x, uint ofst, uint start) +{ + uint ix; + DTD *s; + +// chdtko index by ofst +// chdtkd index by start [REMOTE] + s = (DTD*) chmem(&chdtko,1); //always master chain + s->fk = ofst; + s->stdat = start; + + ix = bfindix(x, s); + +if (ix < x->num) + { + s = (DTD*) x->ptrs[ix]; + if (ofst && s->fk == ofst) return s; + else if (start == s->stdat) return s; + } + + +// x->lastix = x->num; //invalidate if not found +return NULL; //(DTKD*) x->ptrs[ix]; + +} + +DTD *start_dtdk_loop(uint ofst) +{ + DTD *a; + a = (DTD*) chmem(&chdtko,1); + if (a) a->fk = ofst; + return a; +} + + +DTD *get_next_dtkd(DTD *d) +{ + DTD *a; + uint ix; + +// speedup shortcut - FAULTY !! +// NB> can't use dtka as it tests stdat as well... + +// ix = chdtko.lastix + 1; + +// if (ix < chdtko.num) +// { // if valid lastix - speedup trick for get_next + // a = (DTD*) chdtko.ptrs[ix]; + + // if (a->fk == d->fk) + // { // next item in sequence + // chdtko.lastix = ix; // last valid + // return a; +// } +// } + + // otherwise, just look for it + + a = (DTD*) chmem(&chdtko,2); + a->fk = d->fk; + if (d->stdat) a->stdat = d->stdat+1; + + ix = bfindix(&chdtko, a); + + if (ix >= chdtko.num) return NULL; // not found + + a = (DTD*) chdtko.ptrs[ix]; + if (a->fk == d->fk) + { // next item in sequence + chdtko.lastix = ix; // last valid + return a; + } + + + + //if (!cpdtka(&chdtko,ix,a)) return (DTD*) chdtko.ptrs[ix]; + + return NULL; +} + +DTD* add_dtkd (TRK *k, INST *c, int start) + { + // add additional data block to fid + // if seq >= MAXSEQ (255) then add at end of chain (=seq+1) + // else add actual sequence number + + DTD *a, *x; + uint ix; + int ans; + + a = (DTD *) chmem(&chdtko,0); + + a->fk = k->ofst; + a->stdat = start; + + a->psh = k->psh; + a->inc = k->ainc; + a->bsze = bytes(opctbl[c->opcix].rfend1); + + //if (c->opcsub == 1) a->reg = c->opr[c->wop].addr; + + //if (c->opcsub == 2) a->reg = c->opr[4].addr; + + if (c->opcsub == 3) a->off = c->opr[0].addr; + + + if (get_cmd(start,C_CODE)) a->olp = 1; + + a->opcsub = k->opcsub; + + //more to go here + if (c->opcsub == 3) + { + if (c->opr[4].addr == 0) a->opcsub = 1; // R0, make an imd + // if (c->opr[4].rbs) a->hq = 1; // not valid here !! + } + + // a->bsze = k->bsze; + // a->modes = (1 << k->opcsub); + + + + + + ix = bfindix(&chdtko,a); + + ans = chdtko.comp(&chdtko,ix, a); + +// before insert, check for same fk and calc gap + + if (ix > 0 && ix < chdtko.num) + { + x = (DTD*) chdtko.ptrs[ix-1]; + + if (x->fk == k->ofst) + { + if ((start- x->stdat) < 32) x->gap = start-x->stdat; + } + + } + + + if (ans) + { + chinsert(&chdtko, ix, a); + ix = bfindix(&chdtkd,a); + chinsert(&chdtkd, ix, a); + } + else + { // exists already. Clear it and reset for use. may not be necessary ? + return NULL; + } + + chdtko.lastix = chdtko.num; //must reset any queries + return a; +} + + + + + + + + + + + + +TRK* add_dtk(SBK *s,INST *c) + { + TRK *x; + // DTD *a; + int ans; + uint ix, start; //, size; + + OPS *o; //, *w; + + // c instance should have all info required............. BUT NOT FOR ARGS !! + + + if (anlpass >= ANLPRT) return 0; + + if (!s->scanning && !s->emulating) return 0; //may be only scanning ?? + + // would need to sort out qualifying ops (LDX, AD2B, imd etc) here + // normally, source would be [1] which is only mutliple mode, except for stx + + // ([2] = [1]), but not always........ + + /*Signature index value for like instructions - MAX 63 + 0 special 1 clr 2 neg/cpl 3 shift l 4 shift R 5 and + 6 add 7 subt 8 mult 9 or 10 cmp 11 divide + 12 ldx 13 stx 14 push 15 pop 16 sjmp 17 subr + 18 djnz 19 cjmp 20 ret 21 carry 22 other + 24 dec 25 inc + +*/ + + +//not for compares ??? + + // base block setup + + x = (TRK *) chmem(&chdtk,0); // next free block + x->ofst = c->ofst; + x->ocnt = s->nextaddr-c->ofst; + + x->opcix = c->opcix; + + o = c->opr+1; + + memset(x->rgvl, 0, sizeof(x->rgvl)); + + for (ix = 0; ix < 5; ix++) x->rgvl[ix] = c->opr[ix].addr; + + x->opcsub = c->opcsub; + x->numops = c->numops; + +// x->bsze = bytes(opctbl[c->opcix].rfend1); + if (c->sigix == 14) x->psh = 1; + + ans = 0; + start = 0; + + switch (c->opcsub) + { + + case 0: // all register op + + if (c->sigix == 25) + { //incb or incw + // start = databank(o->val,c); + x->ainc = 1; + ans = 1; + + } + + if (c->sigix == 14) + { + // pushw //not always code bank ? + start = codebank(o->val,c); + ans = 1; + } + +//but probably for size checking, need EVERTYHING........... + + + break; + + case 1: // immediate op + + //need to keep immediate ldx to register as an rbase.............. + +//start = o->addr; + + if (c->sigix == 14) + { + // pushw + start = codebank(o->addr,c); + break; + } + + if (c->sigix == 12) // || c->sigix == 6) + { // ldx + start = o->addr; + break; + } + + if (c->sigix == 6) + { + // add - check if answer is valid address. + // need subtract too !!!! + + // xofst = c->opr[2].val + c->opr[1].val; //this could be right too ? ........... + + // xofst = o->addr; + start = o->addr; //xofst; + break; + } + + if (c->sigix == 10) + { //cmp + start = o->addr; + break; + } + + + + + + + + + + break; + + case 2: //indirect - beware of stx ??? + + x->rgvl[1] = c->opr[4].addr; + start = o->addr; + + if (o->inc) x->ainc = bytes(o->rfend); + + break; + + case 3: + // if R0 or rbase treat as indirect + + x->rgvl[1] = c->opr[4].addr; + x->rgvl[4] = c->opr[4].val; + + start = o->addr; // combined address + + start = databank(start,c); + + break; + + } + + +// if (val_rom_addr(start)) ans = 1; +//indexed, check special reg, but zero is OK +//if (x->reg1 && x->opcsub == 3 && is_special_reg(x->reg1)) return 0; //inx + +// not for print, but may need these for loops +if (start && is_special_reg(start)) return 0; +/// if (start <= max_reg()) return 0; + +if (!c->opcsub && !ans) return 0; + + + + +//if (x->ofst == 0x9342a) +//{ +//DBGPRT(1,0); +//} + + ix = bfindix(&chdtk, x); + ans = cpdtk(&chdtk,ix, x); + + + if (ans) + { // no match + chinsert(&chdtk, ix, x); // insert main chain + +// ix = bfindix(&chdtkr, x); +// chinsert(&chdtkr, ix, x); // insert main chain + + + + +//if (c->opcsub) { // not reg to reg +if (val_rom_addr(start)) + { + x = (TRK*) chdtk.ptrs[ix]; + add_dtkd(x,c,start); // and start address + } + + // ix = bfindix(&chdtkr, x); // find for register index + // chinsert(&chdtkr, ix, x); // inser224e: 07,30 incw R30 R30++; # incr ptr to save addresst reg chain + // ix = bfindix(&chdtkd, x); // find for data index can't do this ?? search dtdkd may work.... + // chinsert(&chdtkd, ix, x); // insert data chain + // return x; + } + else + { + // add data address. + + if (val_rom_addr(start)) + { + x = (TRK*) chdtk.ptrs[ix]; + add_dtkd(x,c,start); + } + } + + if (!ans) return 0; +return x; +} +/* +// THIS ONE to change to TRACKING data + + + // LBK *k; + int addr, sig; //x + + if (anlpass >= ANLFFS) return; + // if (s->nodata) return; + + addr = 0; + sig = opctbl[c->opcix].sigix; + + if (sig == 14) return; // not push + + + + / opcsub == 1 for immediates with valid ROM / RAM address ?? + // how to tell what's real though ? Assume it always calls a indirect ?? + + + if (c->opcsub == 1) + { // ldx or add with immediate only. ALSO check it's not a cmp limit check ? + if (sig == 12 || sig == 6) + { + o = c->opr+1; + + // if (val_rom_addr(o->addr)) addr = o->addr ; + + // if (c->wop == 1) addr = o->addr; + + if (nobank(addr) > PCORG) + { + x = find_last_psw(c->ofst,0); + if (x && sinst.opcix > 33 && sinst.opcix < 36) + { // found a cmp + if (sinst.opr[2].addr == c->opr[2].addr + && sinst.opr[2].val == c->opr[2].val) + { // cmp matches the ldx, i.e. a LIMIT CHECK + addr = 0; + } + } + } + } + } / + + +// indirect but NOT for writes (STX) or zero wops + + if (c->opcsub == 2) + { + o = c->opr+1; + if (c->wop > 1) addr = o->addr; + + // if (c->inc) + // { //struct ? + // DBGPRT(1,"INC++ STRUCT?"); + // } + } + + // indexed + + if (c->opcsub == 3) + { + o = c->opr+4; // = saved opr[1] + + // if (!o->wsize) + // { + // if [0] is a valid ROM address, then use that. + // if R[1] is a valid ROM address with small offset, then use R[1] + // if R[1] is an rbase, use combined address in [1] + + if (c->opr[1].rbs) addr = c->opr[1].addr; // combined address + else + { + addr = c->opr->addr; // address of offset [0] + if (addr >= (PCORG+0x8)) + { + addr = databank(addr,c); // use fixed offset + } + else + { + addr = o->addr; // fixed offset is too small, use register ? + if (addr >= (PCORG+0x8)) addr = databank(addr,c); else addr = 0; + } + } + // } + } + + + if (addr) + { // addr must be val_rom_addr +// k = +//add_data_cmd (addr, addr+cnv[o->ssize].bytes-1, cnv[o->ssize].datcmd, c->ofst); // add data entry + // if (k) + // { // add offset and type for later checks + // k->opcsub = c->opcsub; + // if (c->opcsub == 3 && c->opr[4].addr) + // if (c->opr[4].val > 0 && c->opr[4].val < 17) + // k->soff = c->opr[4].val; // offset 0-16 + // } + } + } + +*/ + + + +void fsym(void *x) +{ // free symbol + SYM *s; + + s = (SYM*) x; + if (s->tsize) mfree (s->name, s->tsize); // free symbol name + s->name = NULL; + s->tsize = 0; +} + +void fsub(void *x) + { + free_adnl (&chadnl,x); //&(s->adnl)); + } + +void fcmd(void *x) +{ // free cmd + free_adnl (&chadnl,x); //&(k->adnl)); +} + + +int adtchnsize(CHAIN *x, uint ofst, uint seq, uint nl) +{ + // continue from seq to next newline if nl + // but must save lastix in case it hits end + + ADT *a; + int sz, lastix; + sz = 0; + +//seq check ?? + + a = (ADT*) chmem(x,0); + a->fkey = set_adt_fkey(ofst,seq); + + lastix = x->lastix; + + while ((a = get_next_adnl(x,a))) + { + sz += cellsize(a); // size in bytes + if (nl && a->newl) break; + } + + x->lastix = lastix; + return sz; +} + + + +void set_retn(JMP *x) + { + // set retn flag for anything which jumps to addr + // bfindix on to chain will always get FIRST jump + // called recursively + + uint ix; + JMP *t; + + if (x->jtype != J_STAT) return; + + t = (JMP*) chmem(&chjpf,1); //use forward chain, can't use backward + t->toaddr = x->fromaddr; + ix = bfindix(&chjpt, t); // TO chain, finds first match + + while (ix < chjpt.num) + { + t = (JMP *) chjpt.ptrs[ix]; // match + if (t->toaddr != x->fromaddr) break; + // only if 't' (from) is static + if (t->jtype == J_STAT) + { + t->retn = 1; + set_retn(t); // check jumps to here + } + ix++; + } + } + + +/* +int fwdadds(JMP *j, uint *from, uint *to) + { + // return valid to and from, always forwards + + if (!j->jtype) return 1; + if (j->bswp) return 1; + if (j->jtype == J_SUB) return 1; // ignore subr and return jumps + + if (j->retn) return 1; // TEST !! + + if (j->back) + { // backwards jump + *from = j->toaddr; + *to = j->fromaddr; + } + else + { + *from = j->fromaddr; + *to = j->toaddr; + } + return 0; + } + + +uint faddr(JMP *j) + { + // return valid to and from, always forwards + + if (j->back) return j->toaddr; + return j->fromaddr; + } + +int taddr(JMP *j) + { + // return valid to and from, always forwards + + if (j->back) return j->fromaddr; + return j->toaddr; +} + +*/ +/* +void outside (JMP* b, JMP *t) + { + // jump overlap tester. b is base, t is test + // could flag b directly + + uint bfrom, bto; + uint tfrom, tto; // need these in case jumps are backwards + + if (b == t) return; // don't compare same jump... + if (fwdadds(b, &bfrom, &bto)) return; + if (fwdadds(t, &tfrom, &tto)) return; + +// outside jumps in + + if (tto > bfrom && tto < bto) + { + if (tfrom < bfrom) b->uci = 1; + if (tfrom > bto) b->uci = 1; + } + +/ inside jumps out - allow this as experiment + + if (tfrom > bfrom && tfrom < bto) + { + if (!t->jelse) { + if (tto < bfrom) b->uco = 1; + if (tto > bto) b->uco = 1; +} // else t->cbkt = 0; + } + + + } + + if (f->jtype == J_COND && !f->retn) +zx = ix+1; // next jump + t = (JMP*) chjpf.ptrs[zx]; + while (t->fromaddr <= f->toaddr && zx < chjpf.num) + { + outside(f, t); + zx++; + t = (JMP*) chjpf.ptrs[zx]; + } + +void check_jump_span(JMP *j) + { + // check if j is within a cond jump (and possibly other cases too) + // j is candidate for insert + // get minimum overlap candidate and go forwards + + JMP *x; + uint ix, oix; + + if (j->jtype != J_STAT) return; + + x = (JMP*) schmem(); + x->toaddr = j->fromaddr; // to get to first of overlaps (to >= from) + + oix = bfindix(&chjpt, x, 0); // TO chain + + if (oix >= chjpt.num) return; // not found + + ix = oix; + + // ix is minimum possible match (as to >= from) + + while (ix < chjpt.num) + { + x = (JMP*) chjpt.ptrs[ix]; + + if (x != j) + { + if (x->jtype == J_COND || x->jtype == J_ELSE) + { + // ignore backwards jumps for now + if (!x->back) + { + if (x->fromaddr > j->fromaddr) break; // no more overlaps + if (x->fromaddr < j->fromaddr && x->toaddr > j->fromaddr) // && !x->back + { // static is inside a conditional + // BUT not if cond only jumps a single goto............... (xdt2) + // if (x->to == j->from+j->size) then only jumps over another jump, + // so j remains a STAT + + // if (x->to == j->from+j->size && x->from+x->size == j->from ) + //DBGPRT(0,"Ignore "); +//else + j->jtype = J_ELSE; + #ifdef XDBGX + DBGPRT(1,"CJUMP %x-%x spanned by %x-%x", j->fromaddr, j->toaddr, x->fromaddr, x->toaddr); + #endif + break; + } + } + } + } + ix++; + } + } +*/ + +void do_jumps (void) +{ + +/******* jumps checklist *************** + set conditions flags for various things + whether jumps overlap, multiple end points etc etc. +*****************************************/ + + + uint ix, zx; + // uint initofst; // where initialise is. +// uint tstart, tend, fstart, fend; // have to use indexes here as interleaved scan + +// uint excnt; + uint tend; + JMP *f, *t; + + // first, check for jumps to return opcodes + // true ret has jtype 0 + + for (ix = 0; ix < chjpf.num; ix++) + { // down list of forward jumps + f = (JMP*) chjpf.ptrs[ix]; + + //assume brackets OK for all conds except backwards jumps + + if (f->jtype == J_COND && !f->back) //f->bkt = 1; + { + f->obkt = 1; //assume bkts OK at first + f->cbkt = 1; + // f->done = 0; + } + + if (f->jtype) + { // static and cond jumps + tend = g_byte(f->toaddr); + if (tend == 0xf0 || tend == 0xf1) + { + f->retn = 1; + set_retn(f); // recursive for STAT jumps only + } + } + } + +// check for overlaps in jumps + + for (ix = 0; ix < chjpf.num; ix++) //unsigned !! + { + f = (JMP*) chjpf.ptrs[ix]; // down full list of forward jumps + + + // fend = f->fromaddr + f->size; // end of jump + // fstart = f->fromaddr - f->cmpcnt; // start, from cmp if present + + // if (f->back) + // { + // f->jdo = 1; + // f->bkt = 0; // later !! + // f->cbkt = 0; + // f->done = 1; + // } + + if (f->jtype == J_COND && !f->back) // && !f->done) + { // only for conditional jumps (as base) + // try to sort out heirarchy for whether brackets can be used + // fromaddr is unique, but toaddr is not + // in order of FROM... + +//findix instead ?? + + zx = ix+1; // next jump + // t = (JMP*) chjpf.ptrs[zx]; + + while (zx < chjpf.num) + { // inside -> outside (same chain) + + t = (JMP*) chjpf.ptrs[zx]; + if (t->fromaddr >= f->toaddr) break; + + // if (t->fromaddr < f->toaddr) + // { + if (t->toaddr > f->toaddr) t->obkt = 0; + if (t->toaddr < f->fromaddr) t->obkt = 0; + // } + + /* skip these for now... + // check for 'else' and 'or' etc. + tend = t->fromaddr + t->size; // end of jump + tstart = t->fromaddr - t->cmpcnt; + + if (t->jtype == J_STAT && tend == f->toaddr && tstart > fend && !t->back) + { + t->jelse = 1; //type = J_ELSE; + t->cbkt = 1; + f->cbkt = 0; + // if ( + + } + + if (tstart == fend && f->toaddr == tend) // && tstart > fend) + { + DBGPRT(1,0); + if (t->jtype == J_COND) t->jor = 1; + } +//*/ + + zx++; + // t = (JMP*) chjpf.ptrs[zx]; + + // if (t->fromaddr > f->toaddr) break; moved up + + } //end while (inside->outside) + + + + + + + + +// if (f->jtype == J_COND && !f->back && !f->bswp && !f->uci && !f->uco) f->cbkt = 1; // conditional, forwards, clean. + + } + } + + +// need to check for loops within loops here ....NO, move to dtk processing. +// do an additional check for bins which jump to initialise..... (jump zero ?) No, bank 8 + +//bfindix on 82000 and find nearest one (find or after) + + + // f = (JMP*) chmem(&chjpf,0); + // f->fromaddr = 0x92000; //first/initial from jump + // ix = bfindix(&chjpf, f); + // f = (JMP*) chjpf.ptrs[ix]; + + // initofst = f->toaddr; + +/* j = (JMP*) find(&chjpf,j); // jump from +void *find(CHAIN *x, void *b) +{ + // find item with optional extra pars in b + + int ix; + ix = bfindix(x, b, 0); + if (x->equal(x,ix,b)) return NULL; // no match + return x->ptrs[ix]; +} + + if (j->back && !j->retn && (j->jtype == J_STAT || j->jtype == J_COND)) + +STILL needs more 8481 A9L ..... + + +excnt = 0; + + for (ix = 1; ix < chjpf.num; ix++) + { + f = (JMP*) chjpf.ptrs[ix]; // down full list of forward jumps + + excnt = f->fromaddr - f->toaddr; +//drop init check, but may need something more........... + + if (f->back && !f->retn && !f->bswp && (f->jtype == J_STAT || f->jtype == J_COND) + // && f->toaddr != initofst + && excnt < 256 && excnt > 0) + { + + +//technically ALL backward jumps not ret or loopstop should qualify for a loop check, including +// loops inside loops............. + + +//must verify a loop HERE. .....how ?? ..... must have an increment ??? + + + + + // f is probably a short loop jump......... + + //find toaddr in from chain, for any contained jumps..................or go backwards.... + + +if (f->jtype == J_COND) f->exloop = 1; // conditional IS already an exit + +//DBGPRT(1,0); + + // if f->back set, fromaddr > toaddr..... + + // if (ix > 0) + // { +//go DOWN the chain.......... + + + zx = ix-1; // prev jump + + while (zx > 0) + { + t = (JMP*) chjpf.ptrs[zx]; + if (t->fromaddr < f->toaddr) break; // outside bounds for backwards jump + + + + // t->fromaddr ALWAYS < f->fromaddr AND >= f->toaddr to get here + + // if (t->back) //for loops + // { + // if (t->toaddr < f->fromaddr && t->toaddr >= f->toaddr ) + // { + // t->subloop = 1; + // } + // } + //but exit jumps could be here too........................... + // else + { //std jump + if (t->fromaddr > f->toaddr) + { + +if (t->fromaddr == 0x92042) +{ +DBGPRT(1,0); +} + + if (t->toaddr <= f->fromaddr) {t->inloop = 1;} // t->exloop = 0;} + if (t->toaddr == f->fromaddr+f->size) t->exloop = 1; //> f->fromaddr) t->exloop = 1; + } + } + + zx--; + } + // } + } + } +*/ +} + + + + + + +LBK* add_aux_cmd (uint start, uint end, uint com, uint from) + { + LBK *n; + + if (anlpass >= ANLPRT) return NULL; + + // verify start and end addresses + + if (!g_bank(end)) end |= g_bank(start); // use bank from start addr + + // can get requests for 0xffff with bank, which will break boundary.... + + if (!bankeq(end, start)) + { + #ifdef XDBGX + // if (nobank(start) < 0xffff) to drop warning + chaux.lasterr = E_BKNM; + DBGPRT(1,"Invalid - diff banks %05x - %05x", start, end); + #endif + return NULL; + } + + if (end < start) end = start; + + // check start and end within bounds + + chaux.lasterr = E_INVA; // invalid address + + if (!val_rom_addr(start)) return NULL; + + if ((com &0x1f) == C_XCODE) + { // special for xcode + if (end > maxbkadd (end)) return NULL; + } + else + if (!val_rom_addr(end)) return NULL; + + n = (LBK *) chmem(&chaux,0); + n->start = start; + n->end = end; + n->fcom = com & 0x1f; // max 31 as set + if (com & C_CMD) n->cmd = 1; + if (com & C_SYS) n->sys = 1; + + n = inscmd(&chaux,n); //is this reqd with only aux cmds ? (xcode and args) + + if (chaux.lasterr) return 0; // fail + + #ifdef XDBGX + DBGPBK(1,n, "Add aux"); + #endif + + return n; // successful + } + +SUB * add_subr (uint addr) +{ + // type is 1 add name, 0 no name + // returns valid pointer even for duplicate + + int ans, ix; + SUB *x; + + if (anlpass >= ANLPRT) return NULL; + if (!val_rom_addr(addr)) return NULL; + + if (get_aux_cmd(addr, C_XCODE)) + { + #ifdef XDBGX + DBGPRT(1,"XCODE bans add_subr %x", addr); + #endif + return NULL; + } + + +// insert into subroutine chain + + x = (SUB *) chmem(&chsubr,0); + x->start = addr; + + ix = bfindix(&chsubr, x); + ans = cpsub (&chsubr,ix,x); + + if (ans) + { + chinsert(&chsubr, ix, x); // do insert + chsubr.lasterr = 0; + if (PARGC) x->cptl = 1; // set compact layout + } + else + { + // match - do not insert. Duplicate. + x = (SUB*) chsubr.ptrs[ix]; + chsubr.lasterr = E_DUPL; + } + + #ifdef XDBGX + if (ans) DBGPRT(0,"Add"); else DBGPRT(0,"Dup"); + DBGPRT(0," sub %x", addr); + DBGPRT(1,0); + #endif + return x; + } + + + + + SBK *add_scan (uint add, int type, SBK *caller) +{ + + /* add code scan. Will autocreate a subr for type "SUBR" + * updates type etc even for duplicates to fit 'rescan' type model + * C_CMD (+32) = by user command (can't be changed) + * caller is only set in calls from do-sjsubr and taks lists (vect) + */ + + SBK *s; + + int ans; + uint ix; + + if (anlpass >= ANLPRT) return 0; + + // TEMP add a safety barrier ... + if (chscan.num > 20000) return 0; + + if (!val_rom_addr(add)) + { + chscan.lasterr = E_INVA; + #ifdef XDBGX + ix = (nobank(add)); // reserved addresses + if (ix >= 0xd000 && ix <= 0xd010) return NULL; + if (ix == 0x1f1c || ix == 0x1000 || ix == 0x1800) return NULL; + DBGPRT (1,"Invalid scan %x", add); + #endif + + return NULL; + } + + /* if (get_opdata (add)) breaks stuff. Stops dupls + { + #ifdef XDBGX + DBGPRT (1,"Scan OLAP %x", add); + #endif + chscan.lasterr = E_INVA; + return 0; + } */ + + + + + + + + + + if (get_aux_cmd (add, C_XCODE)) + { + #ifdef XDBGX + DBGPRT(0,"XCODE bans scan %x", add); + DBGPRT(1,0); + #endif + chscan.lasterr = E_XCOD; + return 0; + } + + if (PMANL) + { + #ifdef XDBGX + DBGPRT(1,"no scan %x, manual mode", add); + #endif + return 0; + } + + + + s = (SBK *) chmem(&chscan,0); // new block (zeroed) + s->start = add; + s->curaddr = add; // important !! + s->nextaddr = add; + s->scanning = 1; // scan processing on (default) + s->logdata = 1; // and log data found + + if (type & C_CMD) s->cmd = 1; // added by user command + if (type & C_GAP) s->gapscan = 1; // gap scanning + s->type = type & 0x1f; + + if (caller) + { + s->substart = caller->substart; + s->caller = caller->caller; // default to same level + s->logdata = caller->logdata; // keep nodata flag + s->gapscan = caller->gapscan; // keep gap check flag too + } + + if (s->type == J_SUB) + { + if (caller) s->caller = caller; // new level + add_subr(s->start); + s->substart = s->start; // new subr + new_autosym(1,s->start); // add name (autonumbered subr) + } + + if (s->caller && s->start == s->caller->start) return 0; // safety check + + ix = bfindix(&chscan, s); + ans = cpscan(&chscan,ix,s); // checks start address ONLY, not range + + if (!ans) + { // match - duplicate + SBK *t; + t = (SBK*) chscan.ptrs[ix]; // block which matches + + if (s->caller && t->caller == 0) + { // was a user or system added scan,and now has caller + *t = *s; // replace + #ifdef XDBGX + DBGPRT (0,"Replace"); + DBGPRT (0," scan %05x %s", s->start, jtxt[s->type]); + if (s->substart) DBGPRT (0," sub %x", s->substart); + if (s->caller) DBGPRT(1, " caller %x", s->caller->start); + #endif + if (ix < lowscan) lowscan = ix; // reset lowest scan index + return t; + } + s = (SBK*) chscan.ptrs[ix]; // block which matches + chscan.lasterr = E_DUPL; + } + else + { // no match - do insert +//check flags here ?? + chinsert(&chscan, ix, s); + if (ix < lowscan) lowscan = ix; // reset lowest scan index + + if (s->substart) // scan all blks within subr + { + ix = bfindix(&chsbcn, s); // and insert in subscan chain + chinsert(&chsbcn, ix, s); + } + + if (s->gapscan) + { + ix = bfindix(&chsgap, s); // and insert in gap chain + chinsert(&chsgap, ix, s); + } + chscan.lasterr = 0; + } + + + #ifdef XDBGX + if (ans) DBGPRT (0,"Add"); else DBGPRT (0,"Dup"); + DBGPRT (0," scan %05x %s", s->start, jtxt[s->type]); + if (s->substart) DBGPRT (0," sub %x", s->substart); + if (s->caller) DBGPRT(0, " caller %x", s->caller->start); + + if (!ans) + { //dupl +// if (!s->stop) DBGPRT(0," Not"); + if (s->stop) DBGPRT(0, " Scanned"); + if (s->args) DBGPRT(0," ARGS!"); + if (s->chscan) DBGPRT(0," CHAIN"); + } + DBGPRT(1,0); + #endif + return s; +} + + + +SBK *add_escan (uint add, SBK *caller) +{ + + /* add emu scan. + if addr is zero, COPY caller, else work as 'standard' scan + */ + + SBK *s; + + int ans, ix; + + if (anlpass >= ANLPRT) return 0; + + s = (SBK *) chmem(&chemul,0); // new block (zeroed) + if (add) s->start = add; // standard add emu + else s->start = caller->start; // copy of SBK to new emu + + ix = bfindix(&chemul, s); + ans = cpscan(&chemul,ix,s); // checks start address ONLY + + if (!ans) + { // match - duplicate + s = (SBK*) chemul.ptrs[ix]; // block which matches + } + else + { // no match - do insert (s is valid pointer) + chinsert(&chemul, ix, s); + } + + if (!add) + { + // copy caller block + *s = *caller; // copy whole block and pars + s->scanning = 0; // clear scanning flag + } + else + { + // this add will ALWAYS be the a subr call in EMU, caller will always be set + s->caller = caller; + s->substart = s->start; // new subr + } + + s->curaddr = s->start; + s->nextaddr = s->start; + s->emulating = 1; + s->stop = 0; + s->inv = 0; // and always set for emu rescan + + + #ifdef XDBGX + if (ans) DBGPRT (0,"Add"); else DBGPRT (0,"Dup"); + DBGPRT (0," EMUscan %x", s->start); + if (add) DBGPRT(0," New"); else DBGPRT(0," Copy"); + if (caller) DBGPRT(0," from %x" , caller->curaddr); + DBGPRT(1,0); + #endif + return s; +} + +void fixbitfield(uint *add, uint *fstart, uint *fend) +{ + uint i, sign; + +// move start to byte, and end accordingly +// fstart | 32 for no bit + +if (fend) + { + sign = (*fend) & 32; + (*fend) &= 31; + } +else sign = 0; + + if (*fstart > 7) // && *bitno < 32) + { + i = (*fstart)/8; // this works for doubles too.... + *add += i; // add extra bytes + i *= 8; // bits to subtract + (*fstart) -= i; + + if (fend) + { + (*fend) -= i; + (*fend) |= sign; //keep sign + } + } +} + +uint symadd(SYM *s, uint rw, uint add, uint fstart, uint fend) + { + // make composite address for syms + // this groups in order of bit+write+ranges first, then bit+write, + // then read+ranges, then read. then bits, . + + uint ans; + + if (fstart < 32) fixbitfield(&add,&fstart,&fend); // make into byte + + ans = add << 5; // main address (and bank) + if (fstart >= 32) ans |= 0x8; // max bit is 7 + else ans |= fstart; + if (!(rw & 0xf)) ans |= 0x10; // this is a read sym + + if (s) {s->addb = ans; s->fend = fend;} + return ans; + } + + +SYM *add_sym (const char *fnam, uint rw, uint add, uint fstart, uint fend, uint rstart, uint rend) + +//SYM *add_sym (const char *fnam, uint addb, uint start, uint end) + { + // chain for symbols bitno = -1 for no bit + +/* this needs to be more clever............... +strategy is to be +20 | 1 | 3 | 0 | 1 +addr | nb| bitno | | R/W + +addr 20 bits with bank+1 addr adjusted to BYTE +nb = no bit (or bit 8) +bitno (0-7) +R/W Write = 0 Read = 1 + dropped - NG Range -> range = 0 No range = 1 +*/ + + + + SYM *s, *t; + int ans; + uint ix; + + if (anlpass >= ANLPRT) return 0; + if (!fnam || !*fnam) return 0; // must have a symname. + + // if (bitno > byte size, map into correct address (byte) + + // fix add for single banks ?? + + s = (SYM *) chmem(&chsym,0); + + symadd(s, rw,add,fstart,fend); //composite addr w,add,bit + s->rstart = rstart; + s->rend = rend; + // s->fend = fend; //done inside symadd + + ix = bfindix(&chsym, s); + + ans = chsym.comp(&chsym, ix, s); // zero if matches + + t = (SYM *) chsym.ptrs[ix]; // chain block found + + if (ix < chsym.num) + { + // eqsym checks for EXACT match, not for overlaps. + // check for overlap here + // must check x->end does not overlap next block.... + // start MUST be > than end of previous block... + +/* ranges - can be within existing range, or outside as a new one + // check for overlaps CHANGE <= and >= to < and > FROM OLCHK COMMANDS + + if (s->rstart <= t->rstart && s->rend >= t->rend) OK + else + if (s->rstart >= t->rstart && s->rend <= t->rend) OK + else + + { // overlap, front or back */ + int err; + + err = 0; + + if (s->rstart < t->rstart && s->rend >= t->rstart && s->rend <= t->rend) err = 1; //overlaps rstart + if (s->rend > t->rend && s->rstart >= t->rstart && s->rstart <= t->rend) err = 1; // overlap rear + +// if (ix < chsym.num && start) + + // { + + // if (s->addb == t->addb && s->rend > t->rstart) + if (err) + { + chsym.lasterr = E_OVRG; // overlap ranges + + #ifdef XDBGX + DBGPRT(0,"add sym %x",add); + if (fnam) DBGPRT(0," %c%s%c " ,'"',fnam, '"'); + DBGPRT(0,"overlaps wixth "); + if (fnam) DBGPRT(0," %c%s%c " ,'"',t->name, '"'); + DBGPRT(1,0); + #endif + + + + + + + return 0; + } + } + + // still allow a new symname if it already exists + + if (ans) + { + new_symname (s, fnam); + if (rw & C_CMD) s->cmd = 1; // by user command + if (rw & C_SYS) + { + s->sys = 1; // autogenerated flag + s->xnum = 1; // stop overwrites by autoname + } + + chinsert(&chsym, ix, s); + chsym.lasterr = 0; + + #ifdef XDBGX + DBGPRT(0,"add sym %x",add); + if (fnam) DBGPRT(0," %c%s%c " ,'"',fnam, '"'); + if (s->rstart) DBGPRT (0," %05x - %05x" , s->rstart, s->rend); + if (!(s->addb & 0x8)) DBGPRT (0," B%d", (s->addb & 0x1c) >> 2); + if (!(s->addb & 0x10)) DBGPRT(0," write"); //s->writ + if (s->flags) DBGPRT(0," flags"); + if (s->immok) DBGPRT(0," imm"); + if (s->cmd) DBGPRT(0," CMD"); + DBGPRT(1,0); + #endif + } // do insert + else + { + + #ifdef XDBGX + t = (SYM*) chsym.ptrs[ix]; + DBGPRT(0,"DUPL sym %x",add); + if (fnam) DBGPRT(0," %c%s%c " ,'"',fnam, '"'); + DBGPRT(0," with "); + if (fnam) DBGPRT(0," %c%s%c " ,'"',t->name, '"'); + DBGPRT(1,0); + + #endif + + + + + + + + s = (SYM*) chsym.ptrs[ix]; + chsym.lasterr = E_DUPL; + } + + return s; + +} + + +SYM* get_sym(uint rw, uint add, uint bitno, uint pc) +{ + + SYM *s, *t; //, *x; + uint ix, tries, i; + + uint adds[4]; + + add = fix_addr_bank(add); + + s = (SYM*) chmem(&chsym,1); // block for search + + tries = 32; + adds[0] = symadd(0,rw,add,bitno,tries); //dummy fend for now + s->rstart = pc; // current addr for range check + s->rend = pc; + + // up to 4 options.... + + tries = 1; + + //if bitno set, try without + + if (bitno < 32) adds[tries++] = (adds[0] & 0xfffffff0) | 8; //remove bit (read or write) + + if (rw) + { //if write, try read options + adds[tries++] = adds[0] | 0x10; // set read marker + if (bitno < 32) adds[tries++] = (adds[0] & 0xfffffff0) | 0x18; //set read and remove bit + } + + + + +for (i = 0; i < tries; i++) + { + s->addb = adds[i]; + + // and a special bfindix ?? + ix = bfindix(&chsym, s); + + if (ix < chsym.num) + { + // attempt to match range etc. make sure at first matching addr +/* DON"T NEED THIS !! +while (1) + { + if (ix >= chsym.num) break; // safe for wrap though zero as well + t = (SYM*) chsym.ptrs[ix]; + if (t->addb < s->addb) break; // before first candidate + ix--; // try previous item + } + ix++; // back to first candidate + + // now drop down entries until range match, last entry should always match, if present. + + while (1) + { + if (ix >= chsym.num) break; // for wrap though zero as well + t = (SYM*) chsym.ptrs[ix]; // first possible match + if (t->addb != s->addb) break; // outside list + if (s->rstart >= t->rstart && s->rstart <= t->rend) break; + ix++; + } */ + + t = (SYM*) chsym.ptrs[ix]; + t->prtbit = 0; + if ((t->addb & 8) && (bitno < 32)) t->prtbit = 1; // default name returned for bit request + + if (t->addb == s->addb && s->rstart >= t->rstart && s->rstart <= t->rend) return t; // match + } + } + +return NULL; + +} + + +/* +void scan_dc_olaps(void) +{ + // check for data commands inside code + // could be an indexed offset ldx etc. + // revise to use opdata + + uint ix,i, j, ofst, size; + + LBK *d; //, *n; + SBK *s, *p; + + if (!chscan.num) return; + + + return; + + for (i = 0; i < chdata.num; i++) + { + d = (LBK *) chdata.ptrs[i]; + + if (d->fcom > C_DFLT && d->fcom < C_ARGS) + { // real data pointer + s = (SBK*) chmem(&chscan,1); // dummy block for scan search + s->start = d->start; // addr + ix = bfindix(&chscan, s); + s = (SBK*) chscan.ptrs[ix]; + + if (s->start <= d->start && s->nextaddr >= d->start ) + { // scan before and after data start - assume spanned entirely by code + // delete data + #ifdef XDBGX + LBK *x; + DBGPRT(1,"DATA OVERLAP %d %x %x %s-> %x %x %s (from %x)",1, s->start, s->nextaddr, "SPAN", d->start, d->end, cmds[d->fcom], d->from); //(from %x, %d) ,d->from, d->opcsub); + x = (LBK *) chdata.ptrs[i]; + DBGPBK(1,x,"delete (%d)", i); + #endif + chdelete(&chdata,i,1); + i--; + } + else + { + ofst = 0; + size = 0; + p = (SBK*) chscan.ptrs[ix-1]; // previous scan + + if (p->start <= d->start && p->nextaddr >= d->start ) + { // previous scan olaps at end, but may have 'hole' + #ifdef XDBGX + DBGPRT(1,"DATA OVERLAP %d %x %x %s-> %x %x %s (from %x)", 2, p->start, p->nextaddr, "PREV", d->start, d->end, cmds[d->fcom],d->from); //(from %x, %d), d->from, d->opcsub); + #endif + + if ((p->nextaddr - d->start) < 16) + { // within 16 bytes of end + + + + + for (j = 0; j < 64; j++) + { + if (1) //(!get_opdata(d->start+j)) + { // gap found + if (!ofst) ofst = j; + size++; + } + else if (ofst) break; + } + } + + if (ofst) + { + do_one_opcode(d->from); // get details of opcode + j = bytes(sinst.opr[1].rfend); // size in bytes + if (j > 1 && (ofst & 1)) + { ofst++; size--;} // can't have word etc on add bound. + + if (sinst.opcsub == 3 && size >= j) + { //indexed, and will fit............ + d->start += ofst; + d->end += ofst; + + if (((size/j)*j) == size) + { //consistent with multiple entries...expand + d->end = d->start+size-1; + #ifdef XDBGX + DBGPRT(1,"move+expand data to %x-%x ", d->start, d->end); + #endif + } + #ifdef XDBGX + else + DBGPRT(1,"move data to %x-%x", d->start, d->end); + #endif + // set_opdatar(d->start, d->end); // and mark it + +//but should now check if this has more data after it................2a36 A9L + + + + + } + } + else + { + #ifdef XDBGX + LBK *x; + x = (LBK *) chdata.ptrs[i]; + DBGPBK(1,x,"delete (%d)", i); + #endif + chdelete(&chdata,i,1); + i--; + } + } + } + } + } +} + + +*/ + +void scan_sgap(uint addr) + { + // add a scan for the detected gap, and add_scan keeps + // scans also in extra chain for tracking + + SBK *s, *x; + uint ix, num; + + // check not a 'fill' gap first + + if (check_sfill(addr)) + { + #ifdef XDBGX + DBGPRT(1,"Ignore gscan for %x, fails rpt check", addr); + #endif + return; + } + + x = add_scan(addr, J_STAT | C_GAP,0); + if (!x) return; + + // x->gapscan = 1; //set here for + scan_blk(x, &cinst); + + if (!x->inv) + { + // this may have added new scans, will be in gap chain + +// wnprt(1,"# scan gap at %x as code",cmdaddr(addr)); + ix = 0; + + num = chsgap.num; + + while (ix < chsgap.num) + { + s = (SBK*) chsgap.ptrs[ix]; + if (!s->stop && !s->inv && s->scnt < 10) + { + #ifdef XDBGX + DBGPRT(1,0); + DBGPRT(0,"Sgap Scan (%d) ", ix); + #endif + scan_blk(s, &cinst); + if (chsgap.num != num) + { + #ifdef XDBGX + DBGPRT(1,"NEWCHK %d %d", chsbcn.num, num); + #endif + ix = -1; // rescan if changed + num = chsgap.num; + } + } + ix++; + } + } + + } + + +uint get_jump(CHAIN *x, uint addr) +{ + JMP *j; + j = (JMP*) chmem(&chjpf,0); + + if (x == &chjpf) j->fromaddr = addr; + else j->toaddr = addr; + + return bfindix(x, j); +} + + +JMP * get_fjump(uint addr) +{ + JMP *j; + uint ix; + + j = (JMP*) chmem(&chjpf,0); + j->fromaddr = addr; + + ix = bfindix(&chjpf, j); + + + if (chjpf.comp(&chjpf,ix,j)) return NULL; // no match + return (JMP*) chjpf.ptrs[ix]; +} + + + + + + + + + + +JMP * add_jump (SBK *s, int to, int type) +{ + JMP *j; + OPC *x; + uint ix; +int ans; + + if (anlpass >= ANLPRT) return NULL; + + if (!val_rom_addr(to)) + { + #ifdef XDBGX + ix = (nobank(to)); // don't want calcons + if (ix >= 0xd000 && ix <= 0xd010) return NULL; + if (ix == 0x1f1c || ix == 0x1000 || ix == 0x1800) return NULL; + DBGPRT(1,"Invalid jump to %x from %x", to, s->curaddr); + #endif + return NULL; + } + + j = (JMP *) chmem(&chjpf,0); + j->toaddr = to; + j->fromaddr = s->curaddr; + j->jtype = type; + j->size = s->nextaddr-s->curaddr; // excludes compare + + // is this preceeded by a compare ? + // get size of cmp as well + + ix = find_opcode(0, s->curaddr, &x); + if (x && x->sigix == 10) j->cmpcnt = s->curaddr - ix; // count back for cmp + + + if (!bankeq(j->toaddr, j->fromaddr)) j->bswp = 1; // bank change + else + if (j->jtype && j->fromaddr >= j->toaddr) + { + j->back = 1; + } + + // insert into the two jump chains + + ix = bfindix(&chjpf, j); // FROM chain + ans = chjpf.comp(&chjpf, ix, j); + + if (ans) + { + chinsert(&chjpf, ix, j); // do insert in FROM chain + ix = bfindix(&chjpt, j); // and in TO chain + chinsert(&chjpt, ix, j); + } + else j = (JMP *) chjpf.ptrs[ix]; // match - duplicate + +// check_jump_span(j); + + #ifdef XDBGX + if (ans) DBGPRT (0,"Add"); else DBGPRT (0,"Dup"); + DBGPRT (0," jump %x-%x %s", j->fromaddr, j->toaddr, jtxt[j->jtype]); + if (j->retn) DBGPRT(0," ret"); + DBGPRT(1,0); + #endif + + if (!ans) return 0; // for detection of duplicate (sjsubr) + return j; +} + + + + + + + + + + +uint get_tjmp_ix(uint ofst) +{ + // find to-jump - as this is multiple, + // bfindix (via cpjump) will find first one + + JMP * j; + uint ix; + + if (!chjpt.num) return 1; + + j = (JMP*) chmem(&chjpf,0); + j->toaddr = ofst; + ix = bfindix(&chjpt, j); + + // bfindix WILL find first to jump (code in cpjmp) + + if (ix >= chjpt.num) return ix; + + j = (JMP*) chjpt.ptrs[ix]; + + if (j->toaddr != ofst) return chjpt.num; + + return ix; +} + +SIG * get_sig (uint addr) +{ + SIG *t; + uint ix; + + t = (SIG*) chmem(&chsig,1); + t->start = addr; + + ix = bfindix(&chsig, t); + if (eqsig(&chsig,ix,t)) return NULL; // no match + return (SIG*) chsig.ptrs[ix]; + +} + +LBK *get_prt_cmd(LBK *dflt, BANK *b, uint ofst) + { + // find next command for printout from cmd either code or data chain + // if none found use a default (fill) block + // code always overrides data + + LBK *c ; //, *d; + int ixc ; //, ixd; + + dflt->start = ofst; + dflt->end = 0; + dflt->dflt = 1; + + ixc = bfindix(&chcmd,dflt); // search for code entry first + c = (LBK*) chcmd.ptrs[ixc]; + + if (!eqcmd(&chcmd,ixc,dflt)) return c; // match + + // ixd = bfindix(&chdata,dflt); // search for data entry + // d = (LBK*) chdata.ptrs[ixd]; + + //check code block in case found data block overlaps it + // if (c->start < d->end && c->start >= d->start && c->fcom == C_CODE) + // d->end = c->start-1; + + // if (!eqcmd(&chdata,ixd,dflt)) return d; // match + + // not found either one, look for next blocks to get an end address + + dflt->end = b->maxbk; + + if (dflt->end > c->start && ofst < c->start) dflt->end = (c->start-1); // c is valid code block + + // if (d->start < dflt->end && ofst < d->start) dflt->end = (d->start-1); + + dflt->fcom = C_DFLT; + + if (dflt->end < dflt->start && dflt->end < b->maxbk) + { + #ifdef XDBGX + DBGPRT(0,"PANIC for %x %x!!! ", dflt->start, dflt->end); + DBGPBK(0,c,"CD"); + // DBGPBK(1,d,"DAT"); + #endif + dflt->end = dflt->start+1; + } + +return dflt; + +} + +int find_tjump (uint ofst) +{ + // find trailing jump, for brackets, matches fjump + // prints close bracket for each jump found + + JMP * j; + uint ix, ans; + + if (anlpass < ANLPRT) return 0; + if (!PSSRC) return 0; + + ans = 0; + ix = get_tjmp_ix(ofst); // find first 'to' jump + + while (ix < chjpt.num) + { + j = (JMP*) chjpt.ptrs[ix]; + if (j->toaddr == ofst) + { + if (j->cbkt) ans++; + ix++; + } + else break; //stop as soon as different address + } + return ans; +} + + +JMP * find_fjump (uint ofst, int *x) +{ + // find forward (front) jumps and mark, for bracket sorting. + // returns 8 for 'else', 4 for while, 2 for return, 1 for bracket + // (j=1) for code reverse + + JMP *j; + + if (anlpass < ANLPRT) return 0; + + *x = 0; + + j = get_fjump(ofst); + + if (!j) return 0; + + + if (!j->jtype) *x = 0; // true return + + if (j->retn) *x = 2; // jump to a return + +// if (j->jtype == J_ELSE) *x = 4; + if (j->obkt) *x = 1; // bracket - reverse condition in source + +//if (j->jelse) *x |= 4; //pstr(" } else { "); +//if (j->jor) pstr(0," ** OR **"); + + return j; +} + +/* +old original + ix = bfindix(&cmdch, ofst, 0); // index for command block + x = (LBK*) cmdch.ptrs[ix]; + // if (eq(&cmdch,ix,ofst,0)) + if (ix >= cmdch.num || cmdch.eq(&cmdch,ix,ofst,0)) // was x + { + x = (LBK*) chmem(&cmdch); // NOT found, set up a dummy block + clrblk(x,sizeof(LBK)); //clear block + x->start = ofst; + x->end = end; // safety catch + x->fcom = C_DFLT; + + if (ix < cmdch.num) + { // set end up to next block + y = (LBK*) cmdch.ptrs[ix]; + if (x->end >= y->start) x->end = y->start -1; + } + ix = cmdch.num; + } + + + + + + + + + + + + + +LBK *get_prt_cmd(LBK *dflt, BANK *b, uint ofst) + { + // find next command for printout from cmd either code or data chain + // if none found use a default (fill) block + // code always overrides data + + LBK *c, *d; + int ixc, ixd; + + dflt->start = ofst; + dflt->end = 0; + dflt->dflt = 1; + + ixc = bfindix(&chcode,dflt); // search for code entry first + c = (LBK*) chcode.ptrs[ixc]; + + if (!eqcmd(&chcode,ixc,dflt) && c->fcom != C_XCODE) return c; // match + + ixd = bfindix(&chdata,dflt); // search for data entry + d = (LBK*) chdata.ptrs[ixd]; + + //check code block in case found data block overlaps it + if (c->start < d->end && c->start >= d->start && c->fcom == C_CODE) + d->end = c->start-1; + + if (!eqcmd(&chdata,ixd,dflt)) return d; // match + + // not found either one, look for next blocks to get an end address + + dflt->end = b->maxbk; + + if (c->fcom != C_XCODE && dflt->end > c->start && ofst < c->start) dflt->end = (c->start-1); // c is valid code block + + if (d->start < dflt->end && ofst < d->start) dflt->end = (d->start-1); + + dflt->fcom = C_DFLT; + + if (dflt->end < dflt->start && dflt->end < b->maxbk) + { + #ifdef XDBGX + DBGPRT(0,"PANIC for %x %x!!! ", dflt->start, dflt->end); + DBGPBK(0,c,"CD"); + DBGPBK(1,d,"DAT"); + #endif + dflt->end = dflt->start+1; + } + +return dflt; + +} + + +void scan_loopdata(void) +{ + // try to find data structs from access loops........... + // may add more later on ...... simple first. + + uint ix,jx,sx; //,i; + uint tinc, reg, ofst, xofst; +// uint tstart, tend, fstart, fend; // have to use indexes here as interleaved scan +// uint tend; + JMP *j; + OPC *x; + DTK *k; + SBK *emu; + +//loop pass, for lists of things. + // first, check for jumps to return opcodes + // true ret has jtype 0 + + + #ifdef XDBGX + DBGPRT(2,"\n\n ************** SCAN LOOPDATA"); + #endif + + for (jx = 0; jx < chjpf.num; jx++) + { // down list of forward jumps + j = (JMP*) chjpf.ptrs[jx]; + + // if (j->back && !j->retn && !j->subloop && (j->jtype == J_STAT || j->jtype == J_COND)) +if (j->back && !j->retn && (j->jtype == J_STAT || j->jtype == J_COND)) + { + // backwards jump, check if this is a loop....... + +// data tracking + k = (DTK*) chmem(&chdtk,0); + k->ofst = j->toaddr; + sx = bfindix(&chdtk, k); //keep start in sx + + ix = sx; + tinc = 0; + reg = 0; + + while (ix < chdtk.num) + { + k = (DTK*) chdtk.ptrs[ix]; + + if (k->ofst >= j->fromaddr) break; + + if (k->ainc) + { + if (!reg) reg = k->reg1; + if (k->reg1 == reg) tinc += k->ainc; + } + + // #ifdef XDBGX + // DBGPRT(1,"found %x", k->ofst); + // #endif + ix++; + + } + if (tinc) + { //need register + + #ifdef XDBGX + DBGPRT(0,"\nBJUMP %x-%x", j->fromaddr,j->toaddr); + #endif + + #ifdef XDBGX + DBGPRT(1," found INC %d for R%x", tinc, reg); + #endif + +// find orig ldx for register first................ + + + ofst = j->toaddr; + xofst = j->toaddr; //ofst; + + // ofst = find_opcode(0, j->toaddr, &x); //backwards + + while (xofst) + { + xofst = find_opcode(0,xofst, &x); //backwards + if (!xofst || x->sigix != 12) break; + ofst = xofst; + } + + if (ofst) { //break; //safety +// emulate loop here ?? would need to import a new scan ?? + + emu = add_escan(ofst, 0); + emu->lscan = 1; // mark loop scan + + // emu->logdata = 1; + + scan_loop(emu, &cinst, j->fromaddr); + +} +else { + #ifdef XDBGX + DBGPRT(1," ofst = 0"); + #endif +} + } + + + + + } + + + + + + + } + + +//pass for [Rx+offset] items + + + #ifdef XDBGX + DBGPRT(2,"\n\n ************** END LPDATA"); + #endif + + + } + + // OPCODE chaser + ofst = find_opcode(0, j->toaddr, &x); + while (ofst && ofst < j->fromaddr) + { + ofst = find_opcode(1, ofst, &x); + +//if (ofst == 0x823b6) +//{ +//DBGPRT(1,0); +//} +void *find(CHAIN *x, void *b) +{ + // find item with optional extra pars in b + + uint ix; + ix = bfindix(x, b, 0); + if (x->equal(x,ix,b)) return NULL; // no match + return x->ptrs[ix]; +} + + x = (DTK*) chmem(&chdtk); + x->ofst = ofst; +// r->start = pc; + + x = (DTK*) find(&chdtk, x); + + + + + if (ofst) + { + do_one_opcode(ofst); + } + + + #ifdef XDBGX + DBGPRT(0,"BLoop %x", ofst); + if (!ofst) DBGPRT(1,0); + else + { + DBGPRT(0," (%s) %d", x->name, sinst.opcsub); + for (i = 0; i <= sinst.numops; i++) + { + DBGPRT(0, "R%x," , sinst.opr[i].addr); + } + DBGPRT(1,0); + } + #endif + + } +*/ + +// DTK chaser.................. + + +// init block here +// #ifdef XDBGX + // DBGPRT(1,"BLoop %x", j->toaddr); + // #endif + + + + + +/* +void scan_code_gaps(void) //TEMP !! +{ + uint ix, jx; + LBK *b, *c; + SBK *s, *n; + + // when called, code blocks yet, only scans + // use scans for this still experimental !! + // scan -> scan gaps only + + //b = (LBK*) chmem(&chdata,0); + + ix = 0; + while (ix < chscan.num) + { + s = (SBK*) chscan.ptrs[ix]; + if (s->inv) chdelete(&chscan,ix,1); + else ix++; + } + + ix = 0; + while (ix < chscan.num) + { + s = (SBK*) chscan.ptrs[ix]; + + if (ix+1 > chscan.num) break; + + n = (SBK*) chscan.ptrs[ix+1]; // next scan block + + if (nobank(s->start) <= 0x201e) { ix++; continue;} + + // scan blocks can overlap, so make sure it's a true gap + + if ((n->start - s->nextaddr) > 1) + { + b->start = s->nextaddr; // find nearest data block to end of s + jx = bfindix(&chdata, b); // all together now in cmdch............ + + if (jx < chdata.num) + { // a valid block + c = (LBK*) chdata.ptrs[jx]; + if (c->start > n->start) + { + // and should check it's not XCODE !! + // code gap with no data in it + #ifdef XDBGX + DBGPRT(1,"************************ Code GAP %x-%x", s->nextaddr+1, n->start-1); // not if found something before it. + #endif + scan_sgap(s->nextaddr+1); + } + } + } + ix++; + } + + #ifdef XDBGX + DBGPRT(1,0); + DBGPRT(1,"- - -END Code gap Scan"); + #endif + + } // end func + + + +void sniff_func(uint start, uint end) +{ +// see if we can find a func in this block . + +// just do start addr first................... +int sval, val, size, rsize, sz; +uint addr; + +size = 0; + +sval = g_word(start); + +if (sval == 0xffff) size = 2; +else +if (sval == 0x7fff) size = 10; //signed word +else + { + sval = g_byte(start); + if (sval == 0xff) size = 1; + else + if (sval == 0x7f) size = 9; +} + +if (size <= 0) return; + +sz = get_signmask(size); + + +rsize = (size & 7) * 2; // row size of function + +sval = g_val(start,0,size); // signed value + +addr = start+rsize; // 2nd row + +while (addr < end) +{ +// fix auto sizes later ! + val = g_val(addr,0, size); + if (val > sval) break; + sval = val; + if (val == sz) break; //cnv[size].signmask) break; +//but check continued ends here ?? or not ?? + addr += rsize; +} + +if (val != sz) return; //cnv[size].signmask) return; + +// passed first test. +#ifdef XDBGX +DBGPRT(1,"found func size %d", size); +#endif +} + + + +//establish a median/mean value first ??? +//use preset patterns ?? No use for init lists or tables ?? + +*/ +// int rscore[16][32]; // row, gap, can be negative + int score [16]; + uint mtype[32]; // temp for now whilst testing + int vald [32]; // value difference + +uint do_patt_template(uint start, uint rowsize) + { + uint i, addr; + int a,b; + + for (i = 0; i < rowsize; i++) + { + // establish base set of match types from first two rows of elements + // of possible structure. + + addr = start+i; + a = g_val(addr,0,7); + b = g_val(addr+rowsize,0,7); + // c = g_val(addr+(gap*2), 0,7); + + vald[i] = a-b; // set value difference. + mtype[i] = 0; + + if (a == b) mtype[i] |= 1; // && a == c) // match + + // else if ((c-b) == (b-a)) mtype[i] |= 32; //value difference match + + if (__builtin_popcount(a) == 1 && __builtin_popcount(b) == 1 ) mtype[i] |= 2; // && __builtin_popcount(c) == 1 bit mask positive + if (__builtin_popcount(a) == 7 && __builtin_popcount(b) == 7 ) mtype[i] |= 16; // && __builtin_popcount(c) == 7 bit mask negative + if (a < b ) mtype [i] |= 4; // && b < c increment + if (a > b ) mtype [i] |= 8; // && b > c decrement + } + + +return 1; + +} + + +int score_row_match(uint *start, uint rowsize) + { + uint i, p, addr; + uint m; //matches, for debug + + int a,b, score; + +// perhaps this should be DOWNWARDS ?? + + for (i = 0; i < rowsize; i++) // compare this row to the 'base' + { + //what about words ?? + addr = *start + i; + a = g_val(addr,0,7); // or should this be against FIRST row ??? + b = g_val(addr+rowsize,0,7); + + m = 0; + score = -2 * rowsize; // negative by no of elements + + // if (mtype[i] & 32) + + if ((a-b) == vald[i]) {score += 5; m++; } // matched by value difference + + if ((mtype[i] & 1) && a == b) {score += 5; m++; } // match by value + + if ((mtype[i] & 4) && a < b ) {score += 2; m++; } // increment + + if ((mtype[i] & 8) && a > b ) {score += 2; m++; } // decrement + + if (a != b) + { + p = __builtin_popcount(b); + if ((mtype[i] & 2) && p == 1) {score += 5; m++; } // bit mask +ve + if ((mtype[i] & 16) && p == 7) {score += 5; m++; } // bit maks -ve + } +//others ?? Word ?? constant difference ?? all valid + + } + + + #ifdef XDBGX +// if (score >0) + DBGPRT(1,"sz %d, score %d m %d (%x-%x)", rowsize, score, m, *start, *start+rowsize); + #endif + +*start = addr+rowsize ; + return score; + +} + + + + + + + +uint do_data_patt (uint *start, uint *end) //uint gap +{ + uint addr, rowsize, rownum; + uint highest, ansrow; + +// int tscore[32]; // can be negative -total struct score + +// uint mtype[32]; // temp for now whilst testing +// int vl [32]; // value difference +// int val, a, b, c; + + #ifdef XDBGX + DBGPRT (1, "start patt analysis for %x-%x", *start,*end); + #endif + + + +/*copy table size trick........................ + +// can't do this as may be several structs in here - may or may not be imd........... + +// end not necessarily reliable either.............. + + size = end - start + 1; // max size + term = 0; // no terminator + + val = g_byte(end); + if (val == 0xff) + { // could be terminator + term = 1; + #ifdef XDBGX + DBGPRT(1,"possible terminator"); + #endif +} + + tries = 0; + for (i = 2; i < 32; i++) + { + val = size/i; + if (val*i == size) tries |= (1<> gap) & 1) temp ignore................... + + // try -gap ??? (2286 A9L ?? + + +highest = *start + 1; + +for (rowsize = 2; rowsize < 32; rowsize++) +{ + +//if (rowsize == 16 && *start == 0x92286) +//{ +//DBGPRT(1,0); +//} + + + + + do_patt_template(*start,rowsize); + + for (rownum = 0; rownum < rowsize; rownum++) + { + #ifdef XDBGX + DBGPRT(0," %x", mtype[rownum]); + #endif + } + #ifdef XDBGX + DBGPRT (1,0); + #endif + + addr = *start; + rownum = 0; + +//if rowsize vs size check to go here + + + +while (rownum < 16) + { + addr = *start+(rowsize*rownum); //no point scoring first row....... + + // DBGPRT(0,"rownum = %d size %d start %x ", rownum, rowsize, addr); + score[rownum] = score_row_match(&addr, rowsize); + + // if (score[rownum] > 0) DBGPRT(1,"OK %d", score[rownum]); else + // DBGPRT(1,"ABANDON %d", score[rownum]); + + if (score[rownum] <= 0) break; + rownum++; + +// check addr if highest match for end address + + + + +addr += rowsize; + + if (addr > *end) + { + #ifdef XDBGX + DBGPRT(1,"beyond end"); + #endif + addr = 0; + break; + } + + + + +if (addr > highest) + { + highest = addr; + ansrow = rowsize; + #ifdef XDBGX + DBGPRT(1,"highest addr = %x for size %x",highest, ansrow); + #endif + } + +} + +} + +if (highest == *end) { + #ifdef XDBGX + DBGPRT (1, "matches end ! size %d" ,ansrow ); + #endif + highest = 0;} + +if (highest == *end-1) + { + addr = g_byte(highest+1); + if (addr == 0xff) + + { + #ifdef XDBGX + DBGPRT (1, "matches end with term size %d", ansrow); ansrow |= 0x1000; + #endif + highest = 0;} + } + + + +//if (match) break; //break from above.... + + + +if (highest) + +{ /// no perfect match + // ansrow has best fit = rowsize; + *end = highest; + +} + + +return ansrow; + +} + +/*wrong way around.................. + + for (row = 3; row < 16; row ++) + { + + + + addr = start + (gap * row); // 16 rows ?? // 512 bytes max + + if (addr > maxpcadd(start)) break; + +// for (j = start+gap; j < term; j+=gap) // (start+127)-gap; j+=gap) // row by row until 128 max not big enough !! + // { + // if (j > term) break; + + for (i = 0; i < gap; i++) // compare this row to the 'base' + { + //what about words ?? + addr = start + i; + a = g_val(addr,0,7); + b = g_val(addr+gap,0,7); + + if (mtype[i] & 32) + { + val = a-b; // value difference. + if (val == vl[i]) rscore[row][gap] += 5; else rscore[row][gap] -= 5; // matched by value difference + } + + if (mtype[i] & 1) + { + if (a == b) rscore[row][gap] += 5; else rscore[row][gap] -= 5; // match by value + } + if (mtype[i] & 4) + { + if (a < b) rscore[row][gap] += 2; else rscore[row][gap] -= 2; // increment + } + if (mtype[i] & 8) + { + if (a > b) rscore[row][gap] += 2; else rscore[row][gap] -= 2; // decrement + } + if (mtype[i] & 2) + { //bit mask, drop if any fails + val = __builtin_popcount(b); + // if (val != 1) mtype[i] &= 0xfd; //from type 2 +// else + if (a != b && __builtin_popcount(a) == 1) rscore[row][gap] += 3; else rscore[row][gap] -= 3; + } + + if (mtype[i] & 16) + { + // val = __builtin_popcount(b); + // if (val != 7) mtype[i] &= 0xef; //drop type 16 //bit mask + // else + if (a != b && __builtin_popcount(a) == 7) rscore[row][gap] += 3; else rscore[row][gap] -= 3; + } +//others ?? Word ?? constant difference ?? all valid + + } + + #ifdef XDBGX + DBGPRT(1,"row %d gap %d, score %d", row, gap, score); + #endif +} + +} //end master for gap + +//table divides by number of ITEMS + + val = 0; // use for highest score + gap = 0; // where it is + row = 0; + for (a=3; a < 16; a++) + { + for (j=1; j < 32; j++) + { + b = a*j; + +rscore[a][j] /= b; + + if (rscore[a][j] > val) + { val = b; + gap = j; + row = a; + } + } +} +// terminator ?? + + if (gap) { val = size/gap; + if (val*gap == size-1) gap |= 0x1000; +} + +#ifdef XDBGX +DBGPRT(1,"end analysis at %x", end+1); +DBGPRT(1,"highest score is %d for row %d gap %d", a, row, gap & 0xfff); +#endif +return 1; +} + + + +DTK* get_bdtk(uint addr) +{ + DTK *x; + SBK *t; + uint ix; + + x = (DTK*) chmem(&chdtk); + x->start = addr; + +// temp change for nearest + ix = bfindix(&chdtkd, x, 0); +// if (x->equal(x,ix,b)) return NULL; // no match + if (ix < chdtk.num) + { + x = (DTK*) chdtkd.ptrs[ix]; + DBGPRT(0," Next DTK for %x = %x R%x(%d)",addr, x->start, x->reg, ix); + if (ix > 0) + { + ix--; + x = (DTK*) chdtkd.ptrs[ix]; + DBGPRT(0," Prev DTK for %x = %x (%d)",addr, x->start, ix); + } + +// find scan + // t = find_scan(addr); + t = (SBK*) schmem(); // dummy block for search + t->start = addr; // symbol addr + + ix = bfindix(&chscan, t, 0); + + if (ix > 0) + { + t = (SBK*) chscan.ptrs[ix-1]; + if (t) DBGPRT(0," Prev code for %x = %x-%x",addr, t->start,t->nextaddr, ix); // not if within xcode area............. +} + + return (DTK*) chdtkd.ptrs[ix]; + } +return 0; + +// x = (DTK*) find(&chdtk, x); + +return x; + +} + +*/ + + +/* +uint find_imd_dtk(uint jx, uint forb) + { + uint ix, addr, ans; + DTK *x; + + x = (DTK*) chdtkd.ptrs[jx]; + addr = x->start[0]; + ans = 0; + + if (forb) ix = jx+1; else ix = jx-1; + + while (ix < chdtkd.num) + { + x = (DTK*) chdtkd.ptrs[ix]; + + if (forb) + { if ((x->start[0] - addr) > 256) break; + } + else if ((addr - x->start[0]) > 256) break; // gap too big + + + if (x->opcsub == 1) //imd) + { + ans = x->start[0]; + break; + } + if (forb) ix++; else ix--; + } + +return ans; + +} +*/ + +/* +void sniff_loop(uint ix) + +{ + DTK *x; + uint start, end; + // and inc is set for x + + x = (DTK*) chdtkd.ptrs[ix]; + //DBGPRT(1," assume loop "); + + start = find_imd_dtk(ix, 0); // backwards + end = find_imd_dtk(ix, 1); // forwards + + // DBGPRT(0,"IMD at %x (%x) %x", start, x->start, end); + + // now (hopefully have limits of data struct (may not be right...) + +// DBGPRT(0," ofst %x ", x->ofst); + + /// roll back for ofst ??? + + + + +} + +*/ + + + + +void add_unique(uint *x, uint val, uint max) + { + uint j; + // count in x[0]; + max--; //drop count in [0] from size + + for (j = 1; j <= x[0]; j++) if (val == x[j]) break; + + if (j > x[0] && j < max) + { + x[0]++; + x[x[0]] = val; + + } +} + +/* imd is NOT a guaranteed start for a struct.......need to know if +it's also accessed by an inr or inx, which IS a valid access.... +2284 in A9L ... +ad2w imm 2680, 2c56 +push inr 2c5a **THIS confirms....*** + +2286 has only an IMD and no adjacent following inx so no confirm +2296 has imd + inx confirm but dodgy but following have a G8 ... + +a G is ALWAYS a multiple, so *MUST* be a reliable indicator of size ??? +even for inx as well as inr ???? + +22a6 has imd and following adj inx but also has G12 and G22 markers.... +*/ + + + + +void discover_struct(uint start, uint end) + { + TRK *k; + DTD *d, *s; + // JMP *j; + LBK *x; + ADT *a; + uint ix, jx, kx, row, gap, type; //term, gap; //, stype; + // uint i,j; + + #ifdef XDBGX + DBGPRT(1," in discover"); + #endif + + // szes[0] = 0; // counter + + // look for nearest track (stdat) item + + get_dtkd(&chdtkd,0, start); + kx = chdtkd.lastix; + + if (kx >= chdtkd.num) return; //not found + + s = (DTD *) chdtkd.ptrs[kx]; + + jx = chdtkd.lastix+1; + while (jx < chdtkd.num) + { + d = (DTD *) chdtkd.ptrs[jx]; + if (d->stdat > end) break; + if (d->opcsub == 1) // d->modes & 2) + { //immediate + add_unique(adds,d->stdat,NC(adds)); + } + jx++; + } + + +// debug + #ifdef XDBGX + DBGPRT(1,"start = %x", start); + #endif + + jx = 1; + + while (jx <= adds[0]) + { + #ifdef XDBGX + DBGPRT(1," imd = %x", adds[jx]); + #endif + jx++; + } +#ifdef XDBGX + DBGPRT(1,"possible end = %x", end); + #endif + + + +// end of temp debug + + k = get_dtk(s->fk); //for debug print + + jx = 1; + while (jx <= adds[0]) + { + if (adds[jx] > start && adds[jx] < end) + { + end = adds[jx]-1; + #ifdef XDBGX + DBGPRT(1,"next imd = %x", adds[jx]); + #endif + break; + } + jx++; + } + +gap = 0; +type = 0; + jx = chdtkd.lastix+1; + + memset(szes,0,sizeof(szes)); + + if (s->opcsub > 1) szes[0] = s->bsze; + + while (jx < chdtkd.num) + { // get everything between start and end + // and try to make sense out of it.............. + d = (DTD *) chdtkd.ptrs[jx]; + if (d->stdat > end) break; // finished, maybe + +#ifdef XDBGX + DBGPRT(1,"%x from %x,%d, %d, %d) ", d->stdat, d->fk, d->opcsub, d->bsze, d->gap); + #endif + if (d->opcsub == 2) // d->modes & 4) + { // indirect, check gap + if (d->gap ) gap |= (1 << d->gap); + + // add_unique(szes,d->bsze,NC(szes)); + if (d->inc) type = 2; //probably a loop + } + + // if (d->opcsub == 3) // d->modes & 8) + { //immediate + // add_unique(szes,d->bsze,NC(szes)); + + row = d->stdat - start; + if (start & 1) row++; //always keep row index even + if (row < 32 && d->opcsub > 1) + { + if (d->bsze > szes[row]) + { + if ((row & 1) && szes[row-1] > 1) szes[row] = 0; + else szes[row] = d->bsze; + } + } + + type |= 1; + if (d->gap ) gap |= (1 << d->gap); + + } + + jx++; + } + +// fix up sizes............... + + + +#ifdef XDBGX + DBGPRT(0,"sizes"); + for (row = 0; row < 32; row++) + DBGPRT(0," %d",szes[row]); + DBGPRT(1,0); + + + + + DBGPRT(1,"gap = %x", gap); + DBGPRT(1,"type = %d", type); +#endif + + if (end < start) return; + +//only if entry checks out as not being there................... + +// term = end - start + 1; + + if (end < (start+3)) + { // could be a vect, or a word + // if s-> mattches.... + add_cmd(start,end, s->bsze,0); + return; + } + + + #ifdef XDBGX + DBGPRT(1,"*** investigate %x to %x", start,end); + #endif + + row = do_data_patt(&start,&end); // likely struct size + + if (row & 0x1000) {kx = 1;} else kx = 0; + row &= 0xfff; + + x = add_cmd(start,end,C_STCT,0); + + + + + if (x) + { + a = add_adt(&chadnl,start,1); + if (a) + { + a->fend = 7; //byte for now + if (row > 2 && row < 32) + { + a->cnt = row; + if (kx) { x->term = 1; + #ifdef XDBGX + DBGPRT(1,"with terminator"); + #endif + } + } +else +{ + #ifdef XDBGX + DBGPRT(1,"reject rowsize = %x", row); + #endif +} + } + + + } + + get_dtkd(&chdtkd,0, start); //may not match, use lastix for nearest after start + ix = chdtkd.lastix; + d = (DTD *) chdtkd.ptrs[ix]; + + if (ix > chdtkd.num || d->stdat > end) return; // no data, safety + + k = get_dtk(d->fk); // main entry + + #ifdef XDBGX + DBGPRT(1," earliest ref at %x", k->ofst); + #endif + +// get all data inside start-end, or go to ofst based TRK ?? +// multiple addresses and gaps ?? + +// jx = get_tjmp_ix(k->ofst + k->ocnt); + +// if (jx < chjpt.num) j = (JMP*) chjpt.ptrs[jx]; else j = 0; + + +/* + + d = (DTD *) chdtkd.ptrs[ix]; + + // if (3) then use indexed finder + // if (1) use struct findeer ?? + + if (kx < chdtkd.num) + { //get parent track entry + k = get_dtk(d->fk); + stype = d->opcsub; + if (d->opcsub == 3) + { + // if (k->off > start) start = k->off; //k->base ?? + #ifdef XDBGX + DBGPRT(1,"inx %x (%x,%d) from %x", start, d->fk, d->opcsub,k->ofst); + #endif + } + else + { + // if (d->stdat > start) start = d->stdat; + #ifdef XDBGX + DBGPRT(1,"imd %x (%x,%d) from %x", start, d->fk, d->opcsub,k->ofst); + #endif + } +if (stype ==1) +{ // imd to imd - use data tracking to find struct + + + + +*/ + + +} + + + + + + + + + + + +void turn_gapscans_into_code (void) + { + + // turn gapscan list into code commands. + // if any are invalid, ignore the whole lot + + uint ix; + SBK *s; + + for (ix = 0; ix < chsgap.num; ix++) + { + s = (SBK*) chsgap.ptrs[ix]; + if (s->inv) + { + clearchain(&chsgap); // clear all temp scan blocks + return; + } + } + + for (ix = 0; ix < chsgap.num; ix++) + { + s = (SBK*) chsgap.ptrs[ix]; + if (s && s->stop) + { + add_cmd (s->start,s->nextaddr,C_CODE,0); + } + } // for loop + clearchain(&chsgap); // clear all temp scan blocks + } + + +void check_dtk_links(void) +{ //check links for push (and others?) +uint ix, jx; + +DTD *d, *x; + +for (ix = 0; ix < chdtkd.num; ix++) + { + d = (DTD*) chdtkd.ptrs[ix]; + + +//if (d->stdat == 0x9225f) +//{ +//DBGPRT(1,0); +//} + + + + // d should be lowest ofst occurence of any new start addr. + + jx = ix+1; + + while (jx < chdtkd.num) + { + x = (DTD*) chdtkd.ptrs[jx]; + if (x->stdat != d->stdat) break; + // d->modes |= x->modes; //get all access modes + if (x->psh && x->bsze == 2) d->psh = 1; + + //immediate with confirmed inx or imd after it + if (d->opcsub == 1 && x->opcsub > 1) d->hq = 1; + + // if (get_rbt( + jx++; + } + + // no previous instances + + jx = ix+1; + + while (jx < chdtkd.num) + { + x = (DTD*) chdtkd.ptrs[jx]; + if (x->stdat != d->stdat) break; + if (d->psh && x->bsze == 2) x->psh = 1; + // x->modes = d->modes; + jx++; + } + ix = jx-1; // skip forward entries + +} + +} + + + + +void scan_gaps(void) +{ + // base detect first - combined cmd chain. + + LBK *s, *n, *cb; + DTD *d; + uint ix, jx, i, xend, start, end; + + #ifdef XDBGX + DBGPRT(1,0); + DBGPRT(2, "Gap analysis"); + #endif + + + // while (ix < chcmd.num) + + for (ix = 0; ix < (chcmd.num-1); ix++) + { + s = (LBK*) chcmd.ptrs[ix]; + if (nobank(s->end) <= 0x201e) continue; + + n = (LBK*) chcmd.ptrs[ix+1]; // next scan block + + start = s->end+1; + end = n->start-1; + + if (end < start) continue; + + #ifdef XDBGX + DBGPRT(0,"\n\nGAP %x-%x", start, end); + DBGPRT(1, " %s <-> %s", cmds[s->fcom], cmds[n->fcom]); + #endif + + //get next data item from start + d = 0; + get_dtkd(&chdtkd,0, start); + jx = chdtkd.lastix; + if (jx < chdtkd.num) d = (DTD *) chdtkd.ptrs[jx]; + + //code scan if no data found in this gap + + if (s->fcom == C_CODE && n->fcom == C_CODE) + { // could be code -> code + cb = get_aux_cmd(start, 0); + if (!cb || cb->fcom != C_XCODE) + { // not XCODE + if (d && (d->stdat > end || d->psh)) + { + #ifdef XDBGX + DBGPRT(1,"No data - Code scan "); + #endif + scan_sgap(start); + turn_gapscans_into_code (); + +//but this skips some stuff, so must probably go back and refind the start point.......... + get_cmd(start,0); + if (chcmd.lastix < chcmd.num) ix = chcmd.lastix-1; + + continue; + } + } else { + #ifdef XDBGX + DBGPRT(1,"XCODE set"); + #endif + } + } + + // not code, so look for data structs + // check for fill block first. Must not overlap anything data. + + if (n->fcom > C_BYTE) + { // bigger than byte, not tables, use up to first data item. + uint flag; + i = start; + if (d && d->stdat < end) xend = d->stdat-1; else xend = end; + flag = 0; + + while (i <= xend) + { + flag = 1; + jx = g_byte(i); + if (jx != 0xff) + { + flag = 0; + break; + } + i++; + } + + if (flag) + { + cb = add_cmd(start, xend,0,0); // fill + if (!chcmd.lasterr) continue; // new command OK + } + } + + // not code, not fill, check data, look for structs + + discover_struct(start,end); + + } + #ifdef XDBGX +DBGPRT(2,0); + #endif +} + + + + +/* + +data -> data gaps only + // done AFTER code added + + uint ix, jx, kx, start, end, xofst; //, xdata; + LBK *s, *n, *b, *c; + DTK *x, *z; + SBK *t; + JMP *j; + + #ifdef XDBGX + DBGPRT(1,0); + DBGPRT(2, "Data gaps analysis"); + #endif + +// sniff_func(0x98de2, 0x98e5c); // test + +//DBGPRT(0,"*****Test 1"); + +//do_data_pat(0x8225f, 0x82284); + +//DBGPRT(0,"*****Test 2"); + +//do_data_pat(0x82286, 0x822a5); + +//DBGPRT(0,"*****Test 3"); + +//do_data_pat(0x822a6, 0x82357); + + + b = (LBK*) chmem(&chcode,0); + ix = 0; + while (ix < chdata.num) + { + if (ix+1 >= chdata.num) break; //end of chain + s = (LBK*) chdata.ptrs[ix]; + + if (nobank(s->end) <= 0x201e) { ix++; continue;} + + n = (LBK*) chdata.ptrs[ix+1]; // next scan block + + start = s->end+1; + end = n->start-1; + + // is this a single filler byte = 0xff ?? + + if ((n->start - s->end) == 2) + { + if (n->fcom > C_BYTE) + { + jx = g_byte(start); + if (jx == 0xff) add_data_cmd(start,start,0,0); //fill + } + } + + if ((n->start - s->end) > 2) + { + + #ifdef XDBGX + DBGPRT(0,"\nDATA GAP %x-%x", start, end); + #endif + + + + b->start = s->end; //find nearest code block to n->start + jx = bfindix(&chcode, b); + + if (jx < chcode.num) + { // a valid block + c = (LBK*) chcode.ptrs[jx]; + + if (c->start > start) + { + // possible gap before code block + if (c->start <= n->start && c->end >= n->end) + { + // HERE would check for offset < 16 fixer + //also not for ARGS .... + #ifdef XDBGX + DBGPRT(0,"\n*** delete data entry ? %x-%x o%d[%x %x]", n->start, n->end, n->opcsub, c->start, c->end); + #endif +// and go back ?? +} + + if (c->start < end && c->end >= end) + { //overlaps end + // end = c->start-1; //if (xofst) end = xofst-1; //still not right..................... + #ifdef XDBGX + DBGPRT(0,"\n Mod gap to %x-%x (1) code[%x %x]", start, c->start-1 , c->start, c->end); + #endif + + // if n is completely spanned by code, ditch it................... + + + } +else DBGPRT(0,"\n Catch 1 %x %x", c->start, c->end ); + } + + if (c->start <= start) + { + // overlaps start of gap + if (c->end < end) + { + start = c->end+1; + #ifdef XDBGX + DBGPRT(0,"\n mod gap to %x-%x (2) code[%x %x]", start, end, c->start, c->end); + #endif + } + else DBGPRT(0,"\n Ignore - Code spans 2 %x %x ", c->start, c->end ); + } + + } } + ix++; + } +} + + + +--- move to another subr + if (end > addr) // && !get_opdatar(addr, end)) + { + #ifdef XDBGX + DBGPRT(0,"\n******* DATA GAP %x-%x ", addr, end); + DBGPBK(0,s,"Pre"); + #endif + + x = (DTK*) chmem(&chdtk,0); + x->start = addr; + + jx = bfindix(&chdtkd, x); // find nearest dtk (after) 's' by DATA addr + + x = (DTK*) chdtkd.ptrs[jx]; + +if (x) { + if (x->start != addr) + { // not found, try last address (in 's') + jx--; //previous blk + + if (jx < chdtk.num) + { + x = (DTK*) chdtkd.ptrs[jx]; + } + } + + #ifdef XDBGX + DBGPRT(0,"DTK %x R%x @%x", x->start, x->rreg, x->ofst); + if (x->inc) DBGPRT(0," INC"); // implies list already + if (x->imd) DBGPRT(0," IMD"); // direct + if (x->imd) DBGPRT(0," INX"); // list or struct + #endif + +// if it's an INC already, chances are it's a loop -- need to find the loop !!!! + + // if (x->inc) sniff_loop(jx); + +}} + // if (x->imd) sniff_structs(); or inx then investigate struct ?? + // if (x->inx) sniff_structs(); or inx then investigate struct ?? +/// do_data_pat(s->end+1, n->start-1); + + +// prob need to find the nearest previous imd flagged ? +// or investigate entries with subr..... + +//but first, need nearby DTKs with their attributes ??? +//if it's found as an IMD, then can condsider if a func or table ?? +// if it's an INX, then it might be a list...............or a struct. + + + + jx--; //previous blk + + if (jx < chdtk.num) + { + x = (DTK*) chdtkd.ptrs[jx]; + #ifdef XDBGX + DBGPRT(0," Prev %x R%x @%x ", x->start, x->rreg, x->ofst); + + if (x->inc) DBGPRT(0," INC"); //implies list already + if (x->imd) DBGPRT(0," IMD"); // and look fro another.... + if (x->imd) DBGPRT(0," INX"); // list or struct + + #endif + + +// if this is an IMD, see if there is another entry. + + + xofst = x->ofst; +//could be in loop here..... + + + kx = jx-1; + while (kx < chdtkd.num) //while ?? could be multiple data entries + { + z = (DTK*) chdtkd.ptrs[kx]; + if (x->start - z->start > 256) break; + if (z->imd) + { + #ifdef XDBGX + DBGPRT(0," Prev IMD %x R%x @%x", z->start, z->rreg, z->ofst); + if (z->inc) DBGPRT(0," INC"); //implies list already + if (z->imd) DBGPRT(0," IMD"); // and look fro another.... + if (z->imd) DBGPRT(0," INX"); // list or struct + #endif + // if (x->ofst - z->ofst < 256) + // xofst = z->ofst; else xofst = 0; + break; + } + + + kx--; + } + + +// need a NEXT imd as well + + kx = jx; + while (kx < chdtkd.num) + { + z = (DTK*) chdtkd.ptrs[kx]; + if (z->start - x->start > 256) break; + if (z->imd) + { +#ifdef XDBGX + DBGPRT(0," Next IMD %x R%x @%x", z->start, z->rreg, z->ofst); +#endif + break; + } + + kx++; + } + + } + + + j = (JMP*) chmem(&chjpf,0); + j->fromaddr = x->ofst; + + jx = bfindix(&chjpf, j); // find nearest by Doffset + + // jx--; + + if (jx < chjpf.num) + { + j = (JMP*) chjpf.ptrs[jx]; + if (j->back && j->toaddr <= x->ofst && j->fromaddr > x->ofst) + { +#ifdef XDBGX + DBGPRT(1, " found loop %x-%x", j->fromaddr, j->toaddr); +#endif + } + } + + + +// now swop to OFST chain....................if loop ?? + +//adjacent. + + if (xofst) + { + jx = bfindix(&chdtk, x); // find nearest by ofst + z = (DTK*) chdtk.ptrs[jx]; + jx--; //previous ofst + + while (jx < chdtk.num) + { + x = (DTK*) chdtk.ptrs[jx]; + if (x->imd || x->ofst <= xofst) + { +#ifdef XDBGX + DBGPRT(0," Prev IMD %x R%x @%x (%d)", x->start, x->rreg, x->ofst, jx); +#endif + break; + } + + if (x->ofst == z->ofst && x->start != z->start) +#ifdef XDBGX + DBGPRT(0," ofst %x, data %x %x", x->ofst, x->start, z->start); +#endif +z = x; +jx--; + +// adjacent and loop too, to verify increment size and types, and whether to swap to index instead. + + } + // } +} + + +// only if no xcode ...probably this is not reqd............... + + t = (SBK*) chmem(&chscan,1); // dummy block for search + t->start = addr; // symbol addr + + jx = bfindix(&chscan, t); + + + if (jx && jx < chscan.num) + { + t = (SBK*) chscan.ptrs[jx-1]; + + if (t) { +#ifdef XDBGX + if ((addr - t->start) < 256) DBGPRT(0," Prev code for %x = %x-%x",addr, t->start,t->nextaddr); // not if within xcode area............. +#endif + } + } +#ifdef XDBGX + DBGPRT(1,0); +#endif + } // if data cmd gap + + + // } //if opdata gap + + } + ix++; + } + + +do pass for functions and lists - inside the data blocks.... + +rules for function - at least 4 rows (?) must start and end with defined values. + + + + how to decide to go index, and then what about pointers to tables and funcs ?? + + + + + +test + + for (i = 0; i < BMAX; i++) + { + b = bkmap+i; + if (b->bok) + { + + for (ofst = b->minpc; ofst < b->maxbk; ofst++) + { + // if (!get_opdata(ofst)) {DBGPRT(1, "No flag %x", ofst);} + if (state && !get_opdata(ofst)) { state = 0; DBGPRT(0, "No flag %x", ofst);} + if (!state && get_opdata(ofst)) { state = 1; DBGPRT(1, "-%x",ofst-1) ;} + } + DBGPRT(1,0); + } + + } +} */ \ No newline at end of file diff --git a/Development/sources/4.07.16b/core.h b/Development/sources/4.07.16b/core.h new file mode 100644 index 0000000..8d175cd --- /dev/null +++ b/Development/sources/4.07.16b/core.h @@ -0,0 +1,878 @@ + +/******************************************************************* +common declarations for 'core' code modules (disassembly) +********************************************************************** + + * NOTE - This code assumes 'int' is at least 32 bit (4 bytes), + * + * On some older compilers this would require the use of LONG instead of INT. + * code here also uses a lot of pointer casts. Should be fine on modern compiler, + * but stated here just in case + * + * On Win32 builds (Codeblocks via mingw) int=long=void* all at 32 bits. + * On Linux builds (CodeBlocks via gcc amd64) int at 32 bits, long=void* at 64 bits. + * + * 8065 manual says that bank is an extra 4 bits in CPU, (bits 16-19) to give a 20 bit address (0-0xFFFFF) + * which could be 1 Mb (1048576), but biggest bins seem to be 256k (= 0x40000), or 4 banks, using 2 bits. + * + * Bin addresses used throughout are 20 bit, = bank in 0xf0000 + 16 bit address. Sometimes as unsigned int. + * This code uses a pointer in a BANK structure to map into each malloced memory block (= bank) for best speed. + * This design is therefore bank order independent. + * + * This code can handle all 16 banks with only changes to bank setup subroutines required (detect and check). + * Apart from this, all code uses bank+address everywhere, but not tested beyond 4 banks + * + * NB. Changed above to store (bank+1) internally, so that 'bank present' checks are much easier, which + * means that code now would support 15 banks, as 1-15 internally or 0-14 in bin terms, but only 0,1,8,9 + * are used, and are therefore internally mapped to 1,2,9,10 + *********************************************************/ + +#ifndef _XCOREX_H +#define _XCOREX_H 1 + +#include "shared.h" + + +#define PCORG 0x2000 // standard offset for all EEC bins + +// option defs (as bitmask) + +#define PSSRC (cmdopts & 1) // source code +#define PTABL (cmdopts & 2) // auto table names (+func on old system) +#define PSTRS (cmdopts & 4) // auto subroutine names for function and table lookups +#define P8065 (cmdopts & 8) // 8065 codeset (can be set internally) + +#define PLABEL (cmdopts & 0x10) // auto label names (lab) +#define PMANL (cmdopts & 0x20) // full manual mode +#define PINTF (cmdopts & 0x40) // auto intrp func naming +#define PPROC (cmdopts & 0x80) // auto proc names (xfunc, vfunc, intfunc) + +#define PSIG (cmdopts & 0x100) // auto Lookup Signature subroutine detect +#define PFUNC (cmdopts & 0x200) // auto function names +#define PACOM (cmdopts & 0x400) // do autocomments +#define PRSYM (cmdopts & 0x800) // do preset symbols + +#define PARGC (cmdopts & 0x2000) // set argument compact layout default +#define PSTCA (cmdopts & 0x4000) // set data struct as argument layout (DATAEXT) + +#define PDEFLT (0xFC7) // default collection + + +#define ANLFFS 4 // find funcs, post scan (anlpass) +#define ANLPRT 5 // print phase (anlpass) +#define STKSZ 10 // size of fake stack(s) +#define EMUGSZ 5 // size of emulate arguments list +#define SYMSZE 96 // max size of symbol name + +#define NSGV 16 // num sig values, but sig code allows for 32 +#define MAXSEQ 254 // max seq number + +// command values. Up to 31 commands (32 is cmd flag, can move it) + +#define C_DFLT 0 // preset size data types. +#define C_BYTE 1 +#define C_WORD 2 +#define C_TRPL 3 +#define C_LONG 4 +#define C_TEXT 5 +#define C_VECT 6 + + +#define C_TABLE 7 +#define C_FUNC 8 +#define C_STCT 9 +#define C_TIMR 10 + +#define C_CODE 11 // separate code command chain + +#define C_ARGS 12 // struct data types + +#define C_XCODE 13 + +#define C_SUBR 14 +#define C_SCAN 15 +// rbase + +#define C_SYM 17 +//bank +//setopts +//clropts +//set-psw + +#define C_CMD 32 // by command (can't change or merge) +#define C_SYS 128 // for system 'base generated' cmds +#define C_GAP 256 // for gap scan + + +#define P_NOSYM 256 // nosym for pp_hdr + +// jump and scan request types (and can have C_CMD for user commanded) + +#define J_RET 0 // return opcodes +#define J_INIT 1 +#define J_COND 2 // conditional jump ('if') +#define J_STAT 3 // 'static' jump (goto) +#define J_SUB 4 // subroutine call +//#define J_ELSE 5 //not yet. +//#define J_OR 6 + +//error numbers +#define E_DUPL 1 // duplicate +#define E_INVA 2 // invalid address +#define E_BKNM 3 // banks no match +#define E_ODDB 4 //odd address (WORD etc) +#define E_OVLP 5 //overlap +#define E_OVRG 6 //overlapping ranges +#define E_XCOD 7 // XCODE bans +#define E_SYMNAM 8 // syname replaced +//#define E_PAR 9 + + +// chain index defines + + #define CHJPF 0 + #define CHJPT 1 + #define CHSYM 2 + #define CHBASE 3 + #define CHSIG 4 + #define CHCODE 5 + #define CHDATA 6 + #define CHSCAN 7 + #define CHEMUL 8 + #define CHSBCN 9 + #define CHSGAP 10 + #define CHSUBR 11 + #define CHADNL 12 + #define CHANS 13 + #define CHRGST 14 + #define CHSPF 15 + #define CHPSW 16 +// #define mblk 17 + + +#define g_bank(x) (x & 0xf0000) // get bank +#define nobank(x) (x & 0xffff) // strip bank +#define bankeq(x,y) ((x & 0xf0000) == (y & 0xf0000)) // address banks equal + + +// opcode main definition - called from INDEX structure, not opcode value + +typedef struct // OPCODE definition structure + { + uint sigix : 7; // signature index (='fingerprint') + uint sign : 1; // signed prefix allowed (goes to next entry) + uint p65 : 1; // opcode 8065 only + uint pswc : 1; // op changes the PSW (for conditional ifs) + uint nops : 2; // number of ops (max 3) + uint wop : 2; // write op index (max 3) this gets written to + + uint wfend : 6; // write op field end (zero if not wop) + uint rfend1 : 6; // read op field end + uint rfend2 : 6; // These sizes are field_end + 32 for sign + uint rfend3 : 6; // and get converted for sce code printouts + + void (*eml) (struct sbk *, struct inst *); // emulation func + const char name [6]; // opcode name + const char *sce; // high level code description, with extra markers + } OPC; + + +typedef struct // malloced blocks tracker +{ +void *blk; // pointer to block +size_t size; // size of block +} MBL; + + +// main CHAIN struct to hold stuff for binary chop searches etc +// void* provides for different structs to be used in each chain +// with defined handlers for free mem, compare, and equals +// chain ptr allows same subrs to work for different chains + +typedef struct chain // done like a class with subroutines by chain type +{ +uint num; // no of (used) elements; +uint pnum; // no of pointers in array. +uint asize; // allocation size for pointer blocks +uint esize; // entry (struct) size - ZERO if remote chain +uint DBGmax; // max no of ptrs allocated for debug +uint DBGallocs; // no of reallocs for debug +uint lastix; // last search index, for shortcuts and 'get_next_...' +uint lasterr; // for user commands etc + +void **ptrs; // ptr to array of pointers +void (*cfree) (void*); // block free (by struct pointer) +int (*comp) (struct chain *, uint, void*); // compare, for binary chop +} CHAIN; + + +/****************************************** +* Additional data struct +* used by data structs and subrs for inline params +**********************************************/ + +// Note that float is same size as uint (4), so could perhaps reuse data as a cast (float) + + + +/* + +* to split - structs in unions + +range - name / + +typedef struct rngadt +{ + void *fid; // Foreign key to SYM or RBT + + union uxz{ + struct zxz { + char *name; + uint tsize; + } SXA; + struct xzx { + uint val; + uint testit : 5; +} SXB; + } ; + + uint rstart : 20; // valid between start and end (0 and 0xfffff for 'all') + uint rend : 20; + uint tsize : 8 ; // size of symbol name + null (for mallocs and free) + + //uint val; //if used for RBT as well.......... +// char *ncmnt; // symbol comment for repeated explanation + +} RNGADT; + + + + + + + + + + + + +typedef struct xall { +void *fid; // foreign row id (to another CHAIN or this CHAIN) +uint seq : 8 ; // sequence number, 1-255 (0 reserved for new enquiry) +uint type : 8 ; //data type (= which union option) + +union { + +struct //xadt +{ +float fdat; // full float for divisor (float = int for size) + +int data; // addr, reg, offset, etc. int to allow for -ve + + +uint dreg : 10 ; // data destination register for data - for arg processing (0-0x3ff) + +uint fstart : 5; // 0-31 start of sub field (bit number). +uint fend : 6; // 0-31 end of sub field (bit number), with sign (+32) + +uint cnt : 5 ; // (O) repeat count, 31 max +uint pfw : 5 ; // (P) print min fieldwidth (0-31) +uint prdx : 2 ; // (X) print radix 0 = hex, 1 = dec, 2 bin 3 ? + +uint fnam : 1 ; // (N) look for symbol name +uint newl : 1 ; // (|) break printout with newline (at start of this level) +uint vaddr : 1 ; // (R) value is an addr/pointer to symbol (R) (full address) +uint bank : 1 ; // (K) data holds a valid bank (in 0xf0000) +uint foff : 1 ; // (D) data is an offset address +uint ans : 1 ; // (=) this is ANSWER definition (separate item, for verification) + +uint enc : 3 ; // (E) encoded data type (E) 1-7 (addr & 0xff) +} add; + +struct //xspf + { + void *fid; // Foreign key to SUB + uint fendin : 6; // may not need this... + uint fendout : 6; // or this + uint addrreg : 10; + + uint spf : 5; + uint sizereg : 10; + } spf; + +}; //end of union +} ADT; + +*/ + + + +typedef struct xadt { + +union +{ +float fldat; // full float for divisor (float = int32 for size) +int data; // addr, reg, offset, etc. int to allow for -ve +}; + +uint fkey; // foreign key, (ofst < 8) | seq 1-255 + +uint dreg : 10 ; // data destination register for data - for arg processing (0-0x3ff) +uint fstart : 5 ; // 0-31 start of sub field (bit number). +uint fend : 6 ; // 0-31 end of sub field (bit number), with sign (+32) +uint cnt : 5 ; // (O) repeat count, 31 max +uint pfw : 5 ; // (P) print min fieldwidth (0-31) + +uint bank : 4 ; // (K) holds a bank override, zero if none +uint prdx : 2 ; // (X) print radix 0 = not set, 1 = hex, 2 = dec, 3 bin +uint fnam : 1 ; // (N) look for symbol name +uint newl : 1 ; // (|) break printout with newline (at start of this level) +uint vaddr : 1 ; // (R) value is an addr/pointer to symbol (R) (full address) +uint foff : 1 ; // (D) data is an offset address +uint ans : 1 ; // (=) this is ANSWER definition (separate item, for verification) +uint flt : 1 ; // data is FLOAT, use fldat (union with data) (V, other?) +uint places : 4 ; // 0-15 print decimal places (is 7 enough ?) [forces radix 10] +uint enc : 3 ; // (E) encoded data type (E) 1-7 (addr & 0xff) + +} ADT; + + + + +/******************************** + * RBASE list structure + * map register to fixed address + * only one value per register allowed + ********************************/ + +typedef struct +{ + uint val; // address value pointed to, including bank + uint rstart : 20; // range start + uint inv : 1 ; // invalid flag (value changes somewhere in code) + uint cmd : 1 ; // added by cmd (not changeable) + uint rend : 20; // range end - valid for rstart-r->end + uint reg : 10; // register (as address) + +} RBT; + +/******************************** + * PSW basic link struct + * two straight addresses + ********************************/ + +typedef struct +{ + uint jstart; // address of jump, including bank + uint pswop; // address of psw setter +} PSW; + + + /******************************** + * symbol table structure + ********************************/ + +// includes autonumber symbols (with type). +// addb is combination of address, bitno 4|16|4 bank, address, bitno. +// bitno is max of 7 and split by byte in insert and find. + +typedef struct +{ + + char *name; + // char *ncmnt; // symbol comment for repeated explanation + uint addb; // actual sym address + bitno 4|16|4|1 = bank|address|bitno|r/w + + uint rstart : 20; // valid between start and end (0 and 0xfffff for 'all') + uint fend : 6 ; // 0-31 + signed bit 32, shuffled down with start (in addb) + uint noprt : 1 ; // printed by one of other outputs (prt_xx), suppress sym print + uint cmd : 1 ; // set by dir cmd, do not overwrite + uint sys : 1 ; // system autogenerated + uint flags : 1 ; // split into bitnames for LDX, CLR, CMP + uint names : 1 ; // stop Bx_Rn prints, names only + + uint rend : 20; + uint xnum : 5 ; // autonumber symbol type if > 0 (31 max, for subrs) + uint immok : 1 ; // sym is allowed with immediate ops (below PCORG) + uint prtbit : 1 ; // set if no bitmatch (search only) + uint tsize : 8 ; // size of symbol name + null (for mallocs and free) + + } SYM; + + + +//***** for data addresses tracking found in code analysis + + +// tricky - definitely need search by a) register b) data start. +// c) ofst ?? + +typedef struct dtk +{ + +// first items copy lbk for later transfer and validate or adjust +// mutliple starts in addnl struct below + +// but opcsub+psh+cmp+stx ia already 5 bits......numops is 7 +//perhaps a simple opcix/full and 3 regs and + + + ushort rgvl[5]; // reg 0-4 for indexes etc. + + uint ofst : 20; // where from - PRIMARY KEY + + uint opcsub : 2; // direct,indirect,imd,inx + uint psh : 1; // push instruction or related + + uint ocnt : 4; // total size of 'from' opcode + uint opcix : 8; // opcode index + + uint ainc : 2; // auto inc size + uint numops : 2; // actual numops +} TRK; + +//move inc etc into here to reduce access to main entry for struct analysis, +// opcsub and size, psh, + + +typedef struct dx +{ +uint fk : 20; // foreign key is ofst (opcode) + +int off : 8; // where indexed, may be others....may have to be full 20 bits for base+r as oposed to R+offset + +uint stdat : 20; // no seq, uses unique start address instead. + +//uint modes : 4; // multiple opcsub by bit flag for checks DOESN'T WORK + +uint opcsub : 2; + +//not so much as an imd, but more as fixed or variable, so R0+inx are FIXED + +uint inc : 2; // auto inc size, 1 or 2 +uint olp : 1; // overlaps code (not valid) +uint psh : 1; +uint gap : 5; // where next address is close, keep gap +uint bsze : 3; // 1,2, or 4 bytes +uint hq : 1; //high quality reliable +//more bits fit here anyway + +} DTD; + + +// register indexed ??? +typedef struct dr +{ + + + +} DTR; + + + + + + + + +// general command struct + +typedef struct lbk // command header structure +{ + uint cmd : 1; // added by user command, guess otherwise + uint sys : 1; // system autogenerated + uint dflt : 1; // this is default command made up for print. + uint size : 8; // TOTAL size of data ADT additions + uint start : 20; // start address (inc bank) + + uint term : 2; // data command has terminating byte(s) 0-3 + uint argl : 1; // use args layout + uint cptl : 1; // use compact layout + uint fcom : 5; // command index + uint end : 20; // end address (inc bank) +} LBK ; + + + + + +//overlay struct (on ops[4]) for bitwise searches. + +typedef struct opsx +{ +uint pmask ; // print mask +uint nmfnd ; // names found mask +uint rw : 1; // look for read or write syms ?? +uint fxor : 1; // this is an XOR opcode +uint flags : 1; // flags word 'F' +uint names : 1; // names 'N' +uint sceix : 3; // source index +} OPBIT; + + +// subroutine structure + +typedef struct +{ + uint cmd : 1; // set by command, no mods allowed + uint sys : 1 ; // system autogenerated + uint cptl : 1; // use compact layout (args is default) + uint end : 20; + uint size : 8; // for any attached args (ADT) + uint start : 20; // start address (inc bank) + } SUB; + + +// for subroutines with special functions only ever 1 attached +// add ANSWER to this ! + +typedef struct xspf + { + uint pkey :20; // start addr of subr + uint fendin : 6; // may not need this... + uint fendout : 6; // or this + + uint addrreg : 10; + uint spf : 5; + uint sizereg : 10; + } SPF; + + +/******************************** + * Jump list structure + ********************************/ + +typedef struct jlt +{ + + uint back : 1; // backwards jump + uint obkt : 1; // prt open bracket (and swop logic from goto) + uint cbkt : 1; // prt close bracket + + + uint bswp : 1; // bank change + uint size : 3; + uint fromaddr : 20; // from address + +// uint done : 1; +// uint jelse : 1; +// uint jor : 1; + + + +// uint jwhile : 1; +// uint jdo : 1; + uint retn : 1; // a jump to a 'ret' opcode +// uint subloop : 1; // this is a loop within a loop +// uint inloop : 1; +// uint exloop : 1; + uint jtype : 4; // type (from scan) + uint cmpcnt : 3; // count back to previous compare (if set) + uint toaddr : 20; // this will always be a new SBK, unless a retn + + } JMP; + + +// loop struct ?? + +typedef struct loop +{ +uint start : 20; // start address +uint end : 20; // end address +//uint lastimd : 20; // last immediate assigned. NO !! +uint incr : 4; // at least one incr (count) +uint incr2 : 4; // for inc at same address (diouble check) +uint inrx : 1; // at least one inx or inr +uint increg : 10; // register incremented +int sval ; +uint datastart : 20; + +} LOOP; + + +// scan block struct - can be chained to simulate a stack for nested subr calls + + +typedef struct sbk { // scan block structure + +struct sbk *caller; // caller (for stack builds) + +uint type : 3; // 1 - cond, 2 = stat, 4 = sub +uint start : 20; // start address + +uint scnt : 8; // temp for scan checks +uint chscan : 1; // chain scan +uint gapscan : 1; // gap scan +uint curaddr : 20; // current addr - scan position (inc. start) + +uint logdata : 1; // log data to LBK chain +uint args : 1; // this (proc) is an arg getter +uint scanning : 1; // scan processing (do jumps, scans etc) +uint emulating : 1; // emulating (with dummy scan block) +//uint lscan : 1; // loop scan (emulate), change some things +uint emulreqd : 1; // mark block as emu required (every time called) for args (here) +uint nextaddr : 20; // next addr (opcode) or end of block. + +uint cmd : 1; // added by user command, guess otherwise +uint stop : 1; // scan complete +uint inv : 1; // invalid opcode or flag in scan + +uint php : 1; // block uses pushp (for multibanks) + +uint substart : 20; // start address of related subroutine (for names and sigs) + +} SBK ; + + +/************************ +operand handling for printouts and anlysis +inst has 5 OPS entries (3 ops [1][2][3] + index [0] + save [4]) +***************************/ + +typedef struct // operand storage, max 4 for each instruction +{ + int val ; // value of register contents or an immediate value + uint addr ; // actual operand address (register mostly) + +// (22 bits) + uint rfend : 6; // Read size as field end plus sign + uint wfend : 6; // Write size, zero if no write (can be different to read size) + uint imd : 1; // this is an immediate value + uint rgf : 1; // this is a register value + uint bit : 1; // this is a bit value + uint adr : 1; // this is an address (for jumps - includes bank) + uint off : 1; // this is an indexed OFFSET address (with mask and sign) + uint neg : 1; // offset address is negative + uint inc : 1; // autoinc (easier for printout....) + uint bkt : 1; // print square brackets + uint rbs : 1; // register is an rbase + uint sym : 1; // 1 if sym found +} OPS; + + + + + + + + + + +typedef struct inst +{ +//make OPS a separate struct with fk ??? pk is ofst............... + + OPS opr[5]; // 5 operands. [4] is for SAVED operands, indirect and indexed. + + uint opcix : 8; // opcode index into opcode table (max 112) + uint ofst : 20; // address of opcode, inc bank + + uint sigix : 7; // opcode signature index + uint numops : 3; // number of operands - NOT ALWAYS same as opl (e.g. for BIT jumps) + uint opcsub : 2; // 0 = register (default) 1=imediate, 2 = indirect, 3 = indexed + uint wop : 2; // write op index + uint bank : 4; // bank override for this operand, if set. (by bank prefix) + uint psw : 6; // psw AFTER this operation + +} INST ; + + + +// structure finder (tabs and funcs) +// argument holder for branch analysis subrs + +typedef struct sfx + { + uint spf; + uint ans[2]; + uint rg [2]; + uint lst [2]; + } SFIND; + + +typedef struct cmnt + { + char *ctext; // comment text + INST *minst; // where ops are + uint ofst : 20; // next addr for comment + uint newl : 1; // set if cmt begins with newline + // uint nflag : 2; // newline reqd for cmnd blocks split + } CMNT; + +// Register status for fake stack and arguments handling + + +typedef struct regstx + { + // uint ssize : 4; // size last used, as ADT + +//uint substart : 20; // for subroutine ?? + + uint fend : 5; // + uint popped : 1; // popped reg + uint arg : 1; // argument register (any) + uint sarg : 1; // source arg (1st level from pop) + uint inc : 1; // has been incremented + uint inr : 1; // indirect (address) + // uint used : 1; // when used as arg (for size) + uint enc : 4; // encoded type + uint reg : 10; // register + uint sreg : 10; // source register (i.e. the popped one) for ADT + uint data : 20; // dreg for encode or data + uint ofst : 20; + + } RST; + + + +// Fake Stack - for arg handling and emulation + +// TYPES 1-call 2-imd push 3-pushp (4 ?std push) +// pop set if popped (and register) + + +typedef struct stk +{ // fake stack struct +uint popped : 1; // register entry is popped, cleared when pushed back +uint reg : 10; // register (last) popped to +uint origadd : 20; // original value in nxtadd - for args detect +uint type : 3; // 1 call, 2 imd, 3 psw +uint psw : 6; // PSW value +uint newadd : 20; // current addr value (at push) +} FSTK; + + +// user command holder for parse and processing + +typedef struct // command holder for cmd parsing +{ + ADT *adnl; // current adnl pointer + uint posn; // char posn/column (for errors) + uint maxlen; + int p[8]; // 8 params max (allow for negative values) + int error; // for true error return + + uint tsize : 8; // total size of ADT (bytes) + uint adreg : 10; // address register + + uint spf : 5; // special function subroutine + uint firsterr : 1; // first error, print cmd line if zero + uint szreg : 10; // size register + + uint seq : 8; // seq no for additionals + uint npars : 3; // num of pars in first number set (for error checks) + uint fcom : 5; // command + uint term : 2; // terminating byte(s) + uint argl : 1; // use args layout + uint cptl : 1; // use compact layout + uint imm : 1; // immdok for syms + uint flags : 1; // flagsword for syms + uint names : 1; // names only + + char symname[SYMSZE+1]; // symbol 96 + null + + } CPS; + +/******************************** + * Input Command definition structure + ********************************/ + + typedef struct drs // command definition + { + uint (*setcmd) (CPS*); // command processor(cmd struct) + uint (*prtcmd) (uint, LBK *); // command printer (ofst,cmd index) + + uint maxpars : 3 ; // max pars allowed/expected 0-7 + uint sgpos : 3 ; // where single addr is, in pars + uint prpos : 3 ; // where 'start/end' pair is, in pars + uint namex : 1 ; // name expected/allowed + + uint maxadt : 5 ; // max ADT levels (< 31) + uint minadt : 5 ; // min ADT levels (< 31) + uint defsze : 6 ; // default size, same as ssize in ADT + uint merge : 1 ; // can be merged with same command (adj or olap) + uint stropt : 1 ; // command with strings (e.g. setopt) + + const char* gopts; // global options + const char* opts; // data levels +} DIRS; + + +/******************************** + * Interrupt subr sym names structure + ********************************/ + +typedef struct +{ + uint start : 8; // start offset + uint end : 8; // end (0 - 0x5f) + uint p5 : 1; // 8061 or 8065 + uint num : 1; // with auto number + const char *name; +} NAMES; + + +/******************************** + * Preset sym names structure + ********************************/ + +typedef const struct +{ + uint p81 : 1; // 8061 //prob change to bitmask if more options + uint p85 : 1; // 8065 + uint wrt : 1; // write symbol + uint fstart : 6; // start bit number (0-31) or 32 if no bit + uint fend : 6; // end bit number , 32 for sign + uint addr : 20; // address + const char *symname; // name + +} DFSYM; + +/******************************** + * Autonumbered sym names structure + ********************************/ + +typedef struct +{ // auto numbered symbol names + int mask; // permission mask (a char, against cmdopts) + uint fendin : 6; // as per ADT + uint fendout : 6; // sign flags for func and tab lookup subrs + const char *pname; + } AUTON; + + +/******************************** + * signature structure + ********************************/ + +typedef struct xsig +{ + struct xptn *ptn; // pointer to signature pattern + uint start; // start address of sig found + uint end; // end of signature - only really used to stop multiple pattern matches and overlaps + uint done : 1 ; // processed marker - for debug really + uint skp : 4 ; // skipped opcodes at front + uint v[NSGV]; // 16 saved values +} SIG; + + +typedef struct xptn +{ + uint *sig; // actual sig pattern + const char *name; // name of pattern + void (*pr_sig) (SIG *, SBK*); // sig processor or zero if none + uint size : 16; // no of instructions in pattern + uint vflag : 4; // for encoded addresses +} PAT; + + +typedef struct bpx +{ +uint datbnk; // currently selected data bank - always 8 if single bank +uint codbnk; // currently selected code bank - always 8 if single bank +uint rambnk; // currently selected RAM bank - always 0 for 8061 +} BASP; + +#endif + +// end of header \ No newline at end of file diff --git a/Development/sources/4.07.16b/debug.cpp b/Development/sources/4.07.16b/debug.cpp new file mode 100644 index 0000000..6c88614 --- /dev/null +++ b/Development/sources/4.07.16b/debug.cpp @@ -0,0 +1,570 @@ + +//#include + + + + +#include "shared.h" +#include "core.h" +#include "sign.h" + +#ifdef XDBGX + +extern int anlpass; +extern HDATA fldata; + +extern const char *cmds[]; +extern CHAIN chrgst; +extern CHAIN chscan; +extern CHAIN chsubr; +extern CHAIN chsym; +extern CHAIN chsig; +extern CHAIN chcmd; +extern CHAIN chaux; +extern CHAIN chjpf; +extern CHAIN chjpt; +extern CHAIN chadnl; +extern CHAIN chans; +extern CHAIN chdtk; +//extern CHAIN chdtko; +//extern CHAIN chdtkr; +extern CHAIN chdtkd; + +extern int tscans; +extern int xscans; + +//extern uchar ibuf[]; +extern uint opbit[]; +extern BANK bkmap[]; +extern OPC opctbl[]; + + + +void prt_glo(SUB *, uint (*) (uint,const char *, ...)); +void prt_adt(CHAIN *, void *, int , uint (*) (uint,const char *, ...)); +void prt_cmds(uint (*) (uint,const char *, ...)); + +void prt_subs (uint (*) (uint,const char *, ...)); +void prt_syms (uint (*) (uint,const char *, ...)); +void prt_banks(uint (*) (uint,const char *, ...)); +void prt_rbt (uint (*) (uint,const char *, ...)); + +SYM* get_sym(int , uint , int , int); +//DTKD * get_dtkd(CHAIN*,uint,uint); + +DTD *start_dtdk_loop(uint); +DTD *get_next_dtkd(DTD *); + + + + int DBGmcalls = 0; // malloc calls + int DBGmrels = 0; // malloc releases + int DBGmaxalloc = 0; // TEMP for malloc tracing + int DBGcuralloc = 0; + + + + const char *jtxt[] = {"retp","init","jif","STAT","call", "else"}; // debug text for jump types + const char *csub[] = {"", "imd", "inr", "inx"}; // 0 = register (default) 1=imd, 2 = ind, 3 = inx + + + +// how to have an array of subroutine pointers.....this works. + +// typedef void DBGPB(CHAIN *, int, void*, const char *fmt); +// DBGPB *chdbgpt [4]; + + +uint DBGPRT (uint nl, const char *fmt, ...) +{ // debug file prt with newlines + va_list args; + + if (fmt) + { + va_start (args, fmt); + vfprintf (fldata.fl[5], fmt, args); + va_end (args); + } + while (nl--) fprintf(fldata.fl[5], "\n"); + return 1; // useful for if statements +} + + +void DBGPBK(int nl, LBK *b, const char *fmt, ...) +{ // debug print for command blk + va_list args; + + if (!b) return; + + if (fmt) + { + va_start (args, fmt); + vfprintf (fldata.fl[5], fmt, args); + va_end (args); + } + + DBGPRT(0," %05x %05x %s ", b->start, b->end, cmds[b->fcom]); + va_end (args); + while (nl--) fprintf(fldata.fl[5], "\n"); +} + + + +void DBG_stack (FSTK *t) + { + int i; + + if (anlpass >= ANLPRT) return; + + DBGPRT(0,"STK "); + for (i = 0; i < STKSZ; i++) + { + if (!t->type) break; + DBGPRT (0," [%d]", i); + DBGPRT (0," R%x %x (%x) T%d W%x P%d", t->reg, t->newadd, t->origadd, t->type, t->psw, t->popped); + t++; + } + DBGPRT(1,0); + } + + + +void DBG_rgstat(RST *r) +{ + DBGPRT(0,"R%x (%d) %x", r->reg, r->fend, r->ofst); //ssize + DBGPRT(0," SR%x", r->sreg); + if (r->arg) DBGPRT(0," Arg"); + if (r->popped) DBGPRT(0," Pop"); + if (r->inr) DBGPRT(0," INR"); + // if (r->used) DBGPRT(0," Used"); + if (r->sarg) DBGPRT(0," Sarg"); + if (r->inc) DBGPRT(0," ++"); + if (r->enc) DBGPRT(0," enc %d %x", r->enc, r->data); + DBGPRT(1,0); +} + +void DBG_rgchain(void) +{ + + uint ix; + RST *r; + + DBGPRT(1,"# chrgst num = %d", chrgst.num); + + for (ix = 0; ix < chrgst.num; ix++) + { + r = (RST*) chrgst.ptrs[ix]; + DBG_rgstat(r); + } + DBGPRT(1,0); +} + + +//flags in order of shift 1,2,4,8,10,20,40, 80 + +const char *flgdesc[] = {"Spans","Within","Front","Rear", +"Overriden by","Overrides","Merge", "Equal",}; + + + +void DBGPRTFGS(int ans,LBK *newb,LBK *t) +{ + // debug prt for olchk (next) + int i,f; + + + DBGPRT(0,"Olchk %x for ", ans); + DBGPBK(0,newb,"Insert"); + i = 0; + f = 0; + while (ans) + { + if (ans & 1) + { + if (f) DBGPRT(0,"|"); + DBGPRT(0, flgdesc[i]); + f = 1; + } + i++; + ans >>= 1; + } + DBGPBK(0,t,0); + } + + + + +void DBG_sbk(const char *t, SBK *s) +{ + + if (t) DBGPRT(0,"%s",t); + DBGPRT (0," %05x %05x", s->start, s->nextaddr); // nextaddr is end, effectively + + DBGPRT(0," scnt %d type %d", s->scnt, s->type); + + if (s->substart) DBGPRT(0," sub %x", s->substart); else DBGPRT(0," No sub "); + if (s->caller) DBGPRT(0, " caller %x (%x)", s->caller->start, s->caller->nextaddr); + if (s->emulreqd) DBGPRT(0," EMURQ"); + if (s->args) DBGPRT(0," ARGS"); + if (s->php) DBGPRT(0," PSP"); +// if (s->cmd) DBGPRT(0," cmd"); + if (!s->logdata) DBGPRT(0," NDAT"); + if (s->inv) DBGPRT(0," INV"); + //if (!s->stop) DBGPRT(0," NOT Scanned"); + if (!s->stop) DBGPRT(0," Scanned"); +// if (s->gapchk) DBGPRT(0," GAP"); + + DBGPRT(1,0); +} + + +void DBG_scans(void) +{ + + uint ix; + + DBGPRT(1,"# dbg scans chnum = %d blk scans = %d chain scans %d", chscan.num, tscans, xscans); + + for (ix = 0; ix < chscan.num; ix++) + { + DBG_sbk("Scan",(SBK*) chscan.ptrs[ix]); + + } + DBGPRT(1,0); +} + + +void DBG_dtkx (TRK *k) +{ + // uint start; //,fend; + DTD *d; + uint wop, i; + + wop = opctbl[k->opcix].wop; + + DBGPRT(0,"%5x, %5x, %5s ", k->ofst, k->ofst+k->ocnt, opctbl[k->opcix].name); + + for (i = 0; i <= 4; i++) + { + if (i && wop == i) DBGPRT(0,"*"); else DBGPRT(0," "); + DBGPRT(0,"R%4x ", k->rgvl[i]); + } + + // if (k->numops > 0) {if (wop == 1) DBGPRT(0,"*"); else DBGPRT(0," "); DBGPRT(0,"R%3x ", k->reg1); } else DBGPRT(0," "); + // if (k->numops > 1) {if (wop == 2) DBGPRT(0,"*"); else DBGPRT(0," "); DBGPRT(0,"R%3x ", k->reg2); } else DBGPRT(0," "); + // if (k->numops > 2) {if (wop == 3) DBGPRT(0,"*"); else DBGPRT(0," "); DBGPRT(0,"R%3x ", k->reg3); } else DBGPRT(0," "); + + + switch(k->opcsub) + { + case 0: + DBGPRT(0, "reg "); + break; + case 1: + DBGPRT(0, "imd "); + break; + case 2: + DBGPRT(0, "inr [R%x]",k->rgvl[1]); + break; + case 3: + // if (k->rbsx) DBGPRT(0, "rbs "); else + DBGPRT(0, "inx "); + if (k->rgvl[0] & 0x8000) DBGPRT(0, "[-%x]", 0x10000 - k->rgvl[0]); + else DBGPRT(0, "[+%x]", k->rgvl[0]); + break; + default: + break; + } + if (k->ainc) DBGPRT(0, " +%x", k->ainc); + + +//if (k->ofst == 0x92233) +//{ +//DBGPRT(1,0); +//} + + + + + + d = start_dtdk_loop(k->ofst); + + while ((d = get_next_dtkd(d))) + { + DBGPRT(0," %5x", d->stdat); + // if (d->olp) DBGPRT(0,"O"); + if (d->psh) DBGPRT(0," P"); + if (d->gap) DBGPRT(0," G%d",d->gap); + if (d->olp) DBGPRT(0," O"); + DBGPRT(0,","); + } + DBGPRT(1,0); + } + + +const char* opct [] = {"reg", "imd", "inr", "inx"}; + + + + + + +void DBG_dtk (void) +{ + + TRK *k; + DTD *d; + uint ix; //, f, i; + + DBGPRT(1,0); + DBGPRT(1,"# ------------ data tracking list"); + DBGPRT(1,"# num items = %d", chdtk.num); + + DBGPRT(1,"from, next, addr, size"); + DBGPRT(1,"# by ofst"); + + for (ix = 0; ix < chdtk.num; ix++) + { + k = (TRK*) chdtk.ptrs[ix]; + DBG_dtkx (k); + } + + + + + +// DBGPRT(1,"# by register"); + + // for (ix = 0; ix < chdtkr.num; ix++) + // { + // k = (DTK*) chdtkr.ptrs[ix]; + // DBG_dtkx (k); + // } + + DBGPRT(1,"# by data"); + + for (ix = 0; ix < chdtkd.num; ix++) + { + d = (DTD*) chdtkd.ptrs[ix]; + DBGPRT(0,"%5x, %5x, ", d->fk, d->stdat); + + /* f = d->modes; + i = 0; + while (f) + { + if (f & 1) DBGPRT(0, "%s ", opct[i]); + i++; + f >>= 1; + } */ + + switch(d->opcsub) + { + case 0: + DBGPRT(0, "reg "); + break; + case 1: + DBGPRT(0, "imd "); + break; + case 2: + DBGPRT(0, "inr "); + break; + case 3: + DBGPRT(0, "inx "); + break; + default: + break; + } + + DBGPRT(0,"[%d]", d->off); + if (d->bsze) DBGPRT(0," S%d", d->bsze); + if (d->gap) DBGPRT(0," G%d", d->gap); + if (d->inc) DBGPRT(0," I%d", d->inc); + if (d->psh) DBGPRT(0," PSH", d->gap); + if (d->hq) DBGPRT(0," Q", d->gap); + if (d->olp) DBGPRT(0," OLP", d->gap); + +DBGPRT(1,0); + } + + DBGPRT(2,0); + } + + +/* +void DBG_syms (void) +{ + CHAIN *x; + SYM *t; + uint ix, b; + + DBGPRT(1,"# ---------Symbols num = %d", chsym.num); + + x = &chsym; + + for (ix = 0; ix < x->num; ix++) + { + t = (SYM*) x->ptrs[ix]; + + if (!t->noprt) // not printed as subr or struct + { + DBGPRT(0,"sym %x ", t->addb >> 4); + DBGPRT(0," S%x E%x ", t->rstart, t->rend); + + if (t->cmd) DBGPRT(0," CMD"); + + b = t->addb & 0xf; + if (b) DBGPRT(0," biT %d" , (b-1)); + + if (t->xnum) DBGPRT(0," xnum =%d", t->xnum); + if (t->writ) DBGPRT(0," Write"); + if (t->sys) DBGPRT(0," SYS"); + DBGPRT(0," size %d", t->tsize); + DBGPRT(0," \"%s\"",t->name); + + DBGPRT(0," ab [%x]", t->addb); + DBGPRT(1,0) ; + } + } + DBGPRT(1,0); + + } +*/ + +void DBG_jumps (CHAIN *x, const char *z) +{ + JMP *j; + uint ix; + + DBGPRT(1,"------------ Jump list %s", z); + DBGPRT(1,"num jumps = %d", x->num); + + for (ix = 0; ix < x->num; ix++) + { + j = (JMP*) x->ptrs[ix]; + DBGPRT(0,"%5x->%5x",j->fromaddr, j->toaddr); + if (j->cmpcnt) DBGPRT(0," cmp %5x",j->fromaddr - j->cmpcnt); else DBGPRT(0," "); + DBGPRT(0," e %5x", j->fromaddr + j->size); + DBGPRT(0," %s",jtxt[j->jtype]); + if (j->bswp) DBGPRT (0," bswp"); + if (j->back) DBGPRT (0," back"); + // if (j->uci) DBGPRT (0," uci"); + // if (j->uco) DBGPRT (0," uco"); + if (j->retn) DBGPRT (0," retn"); + + // if (j->subloop) DBGPRT (0," SUB"); + // if (j->jelse) DBGPRT (0," ELSE"); +//if (j->inloop) DBGPRT (0," inloop"); +//if (j->exloop) DBGPRT (0," exloop"); + + if (j->obkt) DBGPRT (0," {"); + if (j->cbkt) DBGPRT (0," }"); + DBGPRT(1,0); + } + DBGPRT(1,0); +} + + +void DBG_jumps () +{ +DBG_jumps (&chjpf,"Forwards"); +DBG_jumps (&chjpt,"To "); +} + +void DBG_sigs(CHAIN *x) +{ + uint i, ix; + SIG *y; + + DBGPRT(1,0); + DBGPRT(1,"-----------Signatures---------[%d]--", x->num); + DBGPRT(1,"d ofst end k name pars" ); + + for (ix = 0; ix < x->num; ix++) + { + y = (SIG*) x->ptrs[ix]; + DBGPRT(0,"%d %5x %5x %2d %7s ", y->done, y->start, y->end, y->skp, y->ptn->name); + for (i = 0; i < NSGV; i++) DBGPRT(0,"%5x," , y->v[i]); + DBGPRT(1,0); + } + + DBGPRT(1,0); +} + +/* +void DBG_banks(void) + { + uint ix; + BANK *b; + +// bank no , file offset, (opt) pcstart, (opt) end file offset, (opt) fillstart + + + DBGPRT(1,0); + DBGPRT(1,"-------DEBUG BANKS---------"); + DBGPRT(1,"bank filstart, filend, maxpc, ## minpc, bkend, maxpc, bok,cmd,bkmask, cbnk, fbuf, opbt" ); + + // DBGPRT(1,"ibuf %x, opbt %x",ibuf, opbit); + DBGPRT(1,"opbt %x",opbit); + + for (ix = 0; ix < BMAX; ix++) + { + b = bkmap + ix; + if (b->bok) + { + DBGPRT(0,"%2d, %5x %5x %5x", ix, b->filstrt, b->filend, b->maxpc); + DBGPRT(0," ## %5x %5x", b->minpc, b->maxbk); + DBGPRT(0," %x %x %x %x %5x", b->bok, b->cmd,b->bkmask, b->cbnk, b->fbuf); + DBGPRT(0," %5x", b->opbt-opbit ); + DBGPRT(1,0);} + } + DBGPRT(1,0); + DBGPRT(1," Other non bok ---"); + + for (ix = 0; ix < BMAX; ix++) + { + b = bkmap + ix; + if (b->fbuf && !b->bok) + { + DBGPRT(0,"[ %2d, %5x %5x %5x", ix, b->filstrt, b->filend, b->maxpc); + DBGPRT(0," ## %5x %5x", b->minpc, b->maxbk); + DBGPRT(0," %x %x %x %x %5x", b->bok, b->cmd,b->bkmask, b->cbnk, b->fbuf); + DBGPRT(1,"]"); + } + } + + DBGPRT(1,0); +} + +*/ + + + + + +void DBG_data() + { + + DBGPRT(1,"max alloc = %d (%dK)", DBGmaxalloc, DBGmaxalloc/1024); + DBGPRT(1,"cur alloc = %d", DBGcuralloc); + DBGPRT(1,"mcalls = %d", DBGmcalls); + DBGPRT(1,"mrels = %d", DBGmrels); + DBGPRT(2,0); + DBGPRT(1," -- DEBUG INFO --"); + + DBG_jumps(); + + DBG_scans(); + prt_banks(DBGPRT); + DBG_rgchain(); + prt_rbt (DBGPRT); + prt_subs(DBGPRT); + prt_syms(DBGPRT); + prt_cmds(DBGPRT); + // prt_cmds(&chaux, DBGPRT); + DBG_dtk(); + DBG_sigs(&chsig); + } + + +#endif + diff --git a/Development/sources/4.07.16b/main.cpp b/Development/sources/4.07.16b/main.cpp new file mode 100644 index 0000000..1e70d0b --- /dev/null +++ b/Development/sources/4.07.16b/main.cpp @@ -0,0 +1,209 @@ + +#include +#include +#include + +#include +#include // linux +#include "shared.h" + +int get_config(char **); +void shutdwn (void); +void startup (void); +short disassemble (char *); +char *get_filemap (void); + +void closefiles(void); + +void DBG_data(); +void prt_dirs(void); + + +extern HDATA fldata; + +#ifdef __linux__ +struct sigaction sa; // for signal capture +#endif + +char fstr[256]; + +short incpg = 0; +uint lastsym = 0; +int lastpass = -1; + +const char *passd [] = {"Checking Binary ", "Process Commands", "Analyse Bin [1] " , + "Analyse Bin [2] ","Analyse Bin [3] ", "Output Listing " }; + + + +char prg[] = { '|', '/', '-', '\\' }; // for rotating symbol display + +void show_prog (int s) +{ + if (s < 0 || s > 5) s = 4; // generic safety + + if (s && s != lastpass) + { + printf ("\n %c %s\r", prg[0], passd[s]); + lastpass = s; + incpg = 0; + lastsym = 0; + } + incpg++; + +//isatty(stdout); +if (isatty(STDOUT_FILENO)) + { + // connected to terminal + + if (incpg > 50 ) // not if redirected to a file.....how to check ?? + { + incpg = 0; + if (lastsym >= sizeof (prg)) lastsym = 0; + printf (" %c\r", prg[lastsym]); + fflush (stdout); + lastsym++; + } +} +} + + + +void prt_stars(void) +{ + short i; + printf ("\n"); + for (i = 0; i < 50; i++) printf ("*"); + printf ("\n"); +} + + +/* change to allow cmd line options +-c config file location +-h help info +-o options ? +then file + +*/ + +#ifdef __linux__ +void segfault(int signal, siginfo_t *si, void *arg) +{ + printf("** Caught CRASH signal at address %p code %x\n", si->si_addr, si->si_code); + printf("** Abandon disassembly\n\n"); + + fprintf (fldata.fl[2], "\n\n ** Caught signal %d ** \n", signal); // same as wrnprt + fprintf (fldata.fl[2],"** Abandon disassembly\n\n"); + + fprintf (fldata.fl[6], "\n\n ** Caught signal %d ** \n", signal); // same as wrnprt + fprintf (fldata.fl[6],"** Abandon disassembly\n\n"); + + + + #ifdef XDBGX + if (signal == SIGINT) DBG_data(); // this seems to work, even if not technically safe... + #endif + + prt_dirs(); + closefiles(); + + exit(0); +} +#endif + + + +int main (int argc, const char **argv) +{ + short go; + char *x, *fn; + char* pth[3]; + +#ifdef __linux__ + // set up signal catcher for disasters + + memset(&sa, 0, sizeof(struct sigaction)); + sigemptyset(&sa.sa_mask); + sa.sa_sigaction = segfault; + sa.sa_flags = SA_SIGINFO; + + sigaction(SIGSEGV, &sa, NULL); + sigaction(SIGINT, &sa, NULL); +#endif + + + + + prt_stars(); + printf ("EEC-IV disassembler Version %s (%s)", SADVERSION, SADDATE); + printf ("\nAbsolutely no warranty or liability"); + prt_stars(); + + fn = 0; + + pth[0] = (char *) argv[0]; + pth[1] = 0; + pth[2] = 0; + for (go = 1; go < argc; go++) + { + if (*argv[go] == '-') + { + x = (char *)(argv[go]+1); + + switch (*x) + { + default: + printf("unknown option %s\n", argv[go]); + return 0; + + case 'c' : + case 'C' : + pth[1] = (char *) argv[go+1]; // path of config file + go++; + break; + + case 'h': + case 'H': + printf ("\nsad -h this help info"); + printf ("\nsad <-c configdir> <-w configdir> "); + printf ("\n options"); + printf ("\n -c for directory of sad.ini config file to read"); + printf ("\n if no -c then looks for a sad.ini in same dir as sad exe"); + printf ("\n asks for bin_filename if none specified\n"); + return 0; + } + + } + else fn = (char*) argv[go]; + } + + // printf("FN=%s\n",fn); + // get_args here + + if (get_config(pth)) return 1; + +/*******************************/ + + + if (fn) + { + printf ("\nFor binary file '%s'\n", fn); + go = disassemble(fn); + + return go; + } + + + go = 1; + while (go) + { // fstat / stat to check file + printf ("\nEnter binary file name (full path allowed)\n"); + fgets (fstr, 64, stdin); + if (!strcmp (fstr, "\n")) return 10; + go = disassemble(fstr); // = 0 if successful + fflush(stdout); + } + + return go; +} + diff --git a/Development/sources/4.07.16b/shared.h b/Development/sources/4.07.16b/shared.h new file mode 100644 index 0000000..80775f5 --- /dev/null +++ b/Development/sources/4.07.16b/shared.h @@ -0,0 +1,102 @@ + + +/************************************************************************** +* structs and defines for shared interface and global scope go here +* minimal declares to keep interface clean - called by sign, main and core. +* also refer to sign.h and core.h for more specific declares +* +* NOTE This code assumes INT and UNSIGNED INT are at least 32 bits (4 bytes). + * and WILL NOT WORK with 16 bit compilers. + * Sizes here (in bits) for information for different platforms + * + * OS compiler int float long void* double + * Win32 mingw 32 32 32 32 64 + * Linux amd64-gcc 32 32 64 64 64 + * + * This code built with CodeBlocks and/or CodeLite IDE environments + * on both Win and Linux +**************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef _XSHARX_H + +#define _XSHARX_H 1 + +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; + + +// Banks can be 0-15, only 0,1,8,9 used. All +1 internally = 1,2,9,A +// All registers in Bank 8, 4 true 64k malloced banks. + +#define BMAX 16 // maximum bank index + +#define SADVERSION "4.0.7.16b" +#define SADDATE "11 Apr 2022" + +// debug defines - when switched on, this causes a LOT of output to xx_dbg file +// DBGPRT id is kept to make debug code more obvious to view + +// #define XDBGX + +#define NC(x) sizeof(x)/sizeof(x[0]) // no of cells in struct 'x' + +// file order is (xx.bin, xx_lst.txt, xx_msg.txt, xx.dir, xx.cmt, xx_dbg.txt, SAD.ini) + +typedef struct +{ + FILE *fl[7]; // file handles + uint fillen; // length of main binary file + int error; // file read error + + char bare [64]; // bare file name (root binary name) + char path [254]; // path of root binary name + char dpath[256]; // default path (SAD progam/exe location) + char fn[7][256]; // full file names with path + +} HDATA; + + +// for preset file permissions ....windows vs. Linux + +typedef struct +{ + const char *suffix; + const char *fpars; +} HOP; + + +// bank holder, details & related pointers. + +typedef struct // indexed by bank num (0-16) + { + uchar *fbuf; // bin file (copy) for this bank ptr. + uint *opcbt; // opcode start flags for this bank (bit array ptr). + + uint filstrt; // start FILE OFFSET. ( = real offset) + uint filend; // end FILE OFFSET. + uint minpc; // min PC (normally PCORG) + uint maxpc; // end PC (= max PC allowed (where fill starts) + uint maxbk; // where bank really ends (after fill) + uint bprt : 1 ; // print this bank in msg file + uint bok : 1 ; // this bank is valid (and found flag) + uint cmd : 1 ; // set by command + uint cbnk : 1 ; // code start bank ( = 8 ) + uint P65 : 1 ; // this is an 8065 bank + uint bkmask : 4 ; // possible banks as bitmask (for 'find') + uint dbank : 4 ; // destination bank as number + } BANK; + + +#endif + diff --git a/Development/sources/4.07.16b/sign.cpp b/Development/sources/4.07.16b/sign.cpp new file mode 100644 index 0000000..3ff5f41 --- /dev/null +++ b/Development/sources/4.07.16b/sign.cpp @@ -0,0 +1,1085 @@ +/***************************************************** + * Signature Declarations and includes + ******************************************************/ +#include "sign.h" + +uint maxpcadd (uint); +uint minadd (uint); +uchar* map_addr (int); +int sjmp_ofst (uint); +int wnprt (const char *, ...); +int wnprtn (const char *, ...); +void* chmem (CHAIN *,uint); +SIG* inssig (SIG *); +void clearchain(CHAIN *); +int get_posn (int); +void show_prog (int); +RBT* add_rbase (int, int); +LBK* set_cmd (int, int, int, int); + +int g_byte (uint); +int g_word (uint); +int g_val (uint, uint, uint); + + +#ifdef XDBGX +uint DBGPRT( uint, const char* fmt, ...); +#endif + +extern OPC opctbl[]; +extern uchar opcind[]; +extern BANK bkmap[]; +extern int cmdopts; +extern int anlpass; +extern BASP basepars; +extern CHAIN chsig; + + +int bgv[NSGV]; // backup signature values +uint tempbank; + +// see sign.h for description of SIGN arrays. + +// startup jump for each bank (save jmp to see if loopstop) + +uint initial[] = +{0xa0012010, 0x2000006}; // single jump (with destination in v[1]) + +// int_ignore (ret or retei) + +uint ignint[] = {0x80001014}; // single ret or reti + + +// this may now be covered by a vect3 ? + +uint avct[] = { +0x8000300c, 0x4008a, 0x00022, // ldw R22,af8a ALTSTACK = af8a; +0x00070000, 0x30201, // any order 2 patts, 1 must match, save in [3] +0x8000300c, 0x00011, 0x70011, // (1) ldb R11,11 BANK_SEL = 11; +0x8801200e, 0x6007b, // (2) push 247b push(247b); +0x400f3000, +0x86001014 // skip up to 3 opcodes to a reta +}; + + +// timer signature - this works for aa through to A9L to C3P2 + +// may need opcodes sooner to confirm bits for word/byte/sec/msec etc etc ?? +// save contents of R30 for timer list start + +/* + Update_timers: DOESNT work for EARS +68fe: a3,72,c0,3c ldw R3c,[R72+c0] R3c = Eighths_ext; # 1/8s remainder (in mSec) +6902: a1,b1,69,30 ldw R30,69b1 R30 = Timer_list; +6906: a0,06,36 ldw R36,R6 R36 = IO_TIMER; +6909: 4b,72,c2,36,38 sb3w R38,R36,[R72+c2] R38 = R36 - Loop_time; # IOTIMER delta time since last mSec +690e: 01,3a clrw R3a R3a = 0; +6910: 0d,07,38 shldw R38,7 R38L *= 80; +6913: 8d,73,cb,38 divw R38,cb73 R38 = R38L / cb73; # *128/52083 = 1 mSec +6917: 88,00,38 cmpw R38,0 +691a: d7,02 jne 691e if (R38 == 0) { +691c: 20,92 sjmp 69b0 return; } # < 1 mS passed - return + +691e: c7,74,5e,38 stb R38,[R74+5e] Bg_timer = R38; # time since last timer update (mSecs) +6922: 65,40,00,3a ad2w R3a,40 R3a += 40; # (remainder IO times*128) + 64 (rounding) +6926: 08,07,3a shrw R3a,7 R3a = R3a / 80; # return to IOTIMES remainder +6929: 48,3a,36,32 sb3w R32,R36,R3a R32 = R36 - R3a; # effective time of this update (IOTIME-remainder) +692d: b1,20,3e ldb R3e,20 R3e = 20; # set Lmillisecs, = "new mS" +6930: 74,38,3c ad2b R3c,R38 R3c += R38; # add new mSs to current mS count +6933: 59,80,3c,3f sb3b R3f,R3c,80 R3f = R3c - 80; # R3f +ve if new 1/8 sec +6937: d3,0f jnc 6948 if ((uns) R3c >= 80) { +6939: b0,3f,3c ldb R3c,R3f R3c = R3f; # remainder =- 1/8 sec (in mS) +693c: 91,40,3e orb R3e,40 Leighths = 1; # = "New 1/8 Sec" +693f: e0,3d,06 djnz R3d,6948 R3d--; + if (R3d != 0) goto 6948; # 1 Sec remainder (in 1/8 secs) +6942: 91,80,3e orb R3e,80 Lseconds = 1; # set "New second" (when R3d = 0) +6945: b1,08,3d ldb R3d,8 R3d = 8; } } # reset 1 sec remainder (= 8/8, counts down) +6948: c3,72,c0,3c stw R3c,[R72+c0] Eighths_ext = R3c; # store 1/8 sec remainder (in mSec) +694c: c3,72,c2,32 stw R32,[R72+c2] Loop_time = R32; # save IOTIME of last timer update (a true mS) + */ + + + +uint times [] = { + +0x8000300c, 0x10030, 0x2003c, //ldb R3c,[R30++] +0x8000300a, 0xf0000, 0x2003c, //cmpb R3c,0 (and save exit value) +0x80002013, 0x2000055, // je +55 # end of list, return +0x900e300c, 0x10030, 0x4070032, // ldzbw R32,[R30++] ** one or two ldx bytes (byte or word) +0x80003017, 0x2003c, 0x00012, // jnb B0,R3c,3e93 # 2/3 word entry, skip mask check +0x8000300c, 0x10030, 0x3003d, // ldb R3d,[R30++] # 4/5 word entry, get flags mask + +0x8000300c, 0x10030, 0x40034, // ldzbw R34,[R30++] # get actual flags/register +0x80003005, 0x40034, 0x3003d, // an2b R3d,[R34] # and mask them +0x80003017, 0x2003c, 0x0004, // jnb B3,R3c,3e91 # Result <> 0 to count +0x80002013, 00004, // je +4 # Result == 0 to count +0x80002010, 0x20000e2, // sjmp (back to start) +0x80002013, 0x20000e0, // je (back to start) +0x80004005, 0x2003c, 0x5003e, 0, // an3b 0,R3e,R3c # mask for time bits. +0x80002013, 0x20000da, // je (back to start) # No time bits overlap - skip count +0x80003017, 0x2003c, 0x5, // jnb B1,R3c +0x8000300c, 0x70032, 0x60036, // ldw R36,[R32] # Count size is word +0x80002010, 0x00003, // sjmp +3 +0x8000300c, 0x70032, 0x60036, // ldsbw R36,[R32] # Count size is byte +0x80003017, 0x2003c, 0x4, // jnb B2,R3c,+4 # up or down +0x80002002, 0x60036 // negw R36 # Negate if counting DOWN +}; + + +// making opcodes optional fixes BWAK, which has separate funcs for each type... + +// ALSO note that AA has jnb, A9L has jb, reversing the sense !! +//can do opposites by checking 0-7 vs 8-15 of opcode, use sgv[17] as marker flags ? + + +uint fnlu[] = { // byte AND WORD function lookup signature make * optional ? + + // why was the signed check made optional - for A9L hot wire ? BWAK ?? + +0x8000400a, 0x40032, 0x20002, 0x80034 , // cmpb/w R34,[R32+2] or 4 + +0x880b3017, 0x300f6, 0x04, // *jnb B1,Rf6,x keep bit - Signed IN ? **JB** +0x880b2013, 0x000a, // *jc x keep carry state - goto unsigned +0x80002010, 0x0002, // *sjmp x + +0x80002013, 0x0006, // jge x + +0x80003006, 0x20002, 0x40032, // ad2w R32,2 (or 4) +0x80002010, 0x20000ed, // sjmp x +0xc0003005, 0x20000df, 0x300f6, // optional an2b RF6 +0x8000300c, 0x40032, 0x60036, // ldb R36,[R32++] +0x8000300c, 0x40033, 0x70038, // ldb R38,[R32++] +0x80003007, 0x40032, 0x60036, // sb2b R36,[R32] +0x80003007, 0x40033, 0x80034, // sb2b R34,[R32++] +0x80003007, 0x40032, 0x70038, // sb2b R38,[R32] + +0x880a3017, 0x300f6, 0x0004, // *jnb B1,Rf6,x THIS is signed OUT **JB*** +0x880a2013, 0x0009, // *jc, x these 4 were opt +0x80002010, 0x0002, // *sjmp, x +0x80002013, 0x0005, // *jge. x + +0x80003009, 0x2000001, 0x300f6, // orb, f6, 1 +0x80002002, 0x70038, // negb R38 +0x80003008, 0x80034, 0x70038, // ml2b R38,R34 +0x8000300b, 0x60036, 0x70038, // divb R38,R36 +0x80003017, 0x300f6, 0x200002, // jnb B0,Rf6, x +0x80002002, 0x70038 // negb R38 + +}; + + +/* +2deb: d3,0b jnc 2df8 if (R50 >= [R52]) { +2ded: 69,04,00,52 sb2w R52,4 R52 -= 4; # back up in MAF transfer function Table to the right value +2df1: 8a,52,50 cmpw R50,[R52] +2df4: d9,f7 jgtu 2ded if (R50 > [R52]) goto 2ded; # loop +2df6: 20,0d sjmp 2e05 goto 2e05; } + +2df8: 65,04,00,52 ad2w R52,4 R52 += 4; # jmp ahead in the MAF transfer function to the right value +2dfc: 8a,52,50 cmpw R50,[R52] +2dff: d3,f7 jnc 2df8 if (R50 < [R52]) goto 2df8; # loop +2e01: 69,04,00,52 sb2w R52,4 R52 -= 4; +2e05: a2,53,58 ldw R58,[R52++] R58 = [R52++]; # interpolate larger MAF +2e08: a2,53,54 ldw R54,[R52++] R54 = [R52++]; # larger air flow +2e0b: 6a,52,58 sb2w R58,[R52] R58 -= [R52]; # MAF difference +2e0e: 6a,53,50 sb2w R50,[R52++] R50 -= [R52++]; # MAF delta to interpolate +2e11: 6a,52,54 sb2w R54,[R52] R54 -= [R52]; # air flow difference +2e14: 6c,50,54 ml2w R54,R50 lR54 *= R50; +2e17: 8c,58,54 divw R54,R58 wR54 /= R58; +2e1a: 66,53,54 ad2w R54,[R52++] R54 += [R52++]; # interpolated air flow +2e1d: 69,04,00,52 sb2w R52,4 R52 -= 4; +2e21: c3,76,b4,52 stw R52,[R76+b4] Maf_ptr = R52; # Save last MAF tfr func ptr - for speed? +*/ + + + +/* cut down version for MAF_tfr lookup (A9L and others) + +uint fstlu[] = { // fast lookup version of fnlu + + // why was the signed check made optional - for A9L hot wire ? BWAK ?? + +//0x8000400a, 0x40032, 0x20002, 0x80034 , // cmpb/w R34,[R32+2] or 4 + +//0x880b3017, 0x300f6, 0x04, // *jnb B1,Rf6,x keep bit - Signed IN ? **JB** +//0x880b2013, 0x000a, // *jc x keep carry state - goto unsigned +//0x80002010, 0x0002, // *sjmp x + +//0x80002013, 0x0006, // jge x + +//0x80003006, 0x20002, 0x40032, // ad2w R32,2 (or 4) +//0x80002010, 0x20000ed, // sjmp x +//0xc0003005, 0x20000df, 0x300f6, // optional an2b RF6 + +0x8000300c, 0x40032, 0x60036, // ldw R36,[R32++] +0x8000300c, 0x40033, 0x70038, // ldw R38,[R32++] +0x80003007, 0x40032, 0x60036, // sb2w R36,[R32] +0x80003007, 0x40033, 0x80034, // sb2w R34,[R32++] +0x80003007, 0x40032, 0x70038, // sb2w R38,[R32] + +0x80003008, 0x80034, 0x70038, // ml2w R38,R34 +0x8000300b, 0x60036, 0x70038 // divw R38,R36 +//ad2w +//sb2w + +}; + +*/ + + + + +/* table signatures + + + + * Sub10: +371f: 37,2d,1a jnb B7,R2d,373c if (Tblsflg = 0) goto 373c; +3722: bc,33,3a ldsbw R3a,R33 R3a = (int)R33; # SIGNED interpolate calc +3725: bc,31,3c ldsbw R3c,R31 R3c = (int)R31; +3728: 68,3c,3a sb2w R3a,R3c R3a -= R3c; +372b: ac,30,3c ldzbw R3c,R30 R3c = (uns)R30; +372e: fe,6c,3a,3c sml2w R3c,R3a (long)R3c = R3c * R3a; +3732: c0,3a,3c stw R3c,R3a R3a = R3c; +3735: 74,31,3b ad2b R3b,R31 R3b += R31; +3738: 37,3b,0f jnb B7,R3b,374a if (B7_R3b = 1) { +373b: f0 ret return; + + * +c 373c: 5c,33,30,3a ml3b R3a,R30,R33 (int) R3a = R30 * R33; # UNSIGNED interpolate calc (AA) +c 3740: 5c,31,30,36 ml3b R36,R30,R31 (int) R36 = R30 * R31; +7 3744: 68,36,3a sb2w R3a,R36 R3a -= R36; +6 3747: 74,31,3b ad2b R3b,R31 R3b += R31; } +6 374a: 65,80,00,3a ad2w R3a,80 R3a += 80; + + * 374e: f0 ret return; + * + + */ + +uint ttrps[] = { // table interpolate - signed. flag is SET for signed + +0x980e2001, 0x2000033, // multiple clrb +0x880a3017, 0x3002d, 0x7001a, // jnb B7,R2d, x table signed flag [10]; +0x40098000, // skip up to 8 opcodes, (cnt in [9]) +0x86003008, 0x2003a, 0x5003c // sml2w R3c,R3a MUST HAVE fe +}; + +uint tblu [] = { // table lookup (byte) signed & unsigned + + // tab addr in [3], cols in [4] + +0x80004008, 0x80033, 0x30034, 0x90036, // ml3b R36,R34,R33 +0x80003006, 0x60031, 0x90036, // ad2b R36,R31 + +0xC0003006, 0x00, 0x50037, // adcb R37,0 OPT Late LU +0xC0002013, 0x02, // jnc carry OPT Early LU +0xC0002019, 0x50037, // incb R37 OPT Early LU + +0x80003006, 0x90036, 0x40038, // ad2w R38,R36 +0x8000300c, 0x40039, 0x60031, // ldb R31,[R38++] +0x8000300c, 0x40038, 0x80033, // ldb R33,[R38] +0xa0002011, 0x200001c, // interpolate (save call) +0x80003006, 0x30034, 0x40038, // ad2w R38,R34 +0x8000300c, 0x40038, 0x80033, // ldb R33,[R38] +0x80002018, 0x40038, // decw R38 +0x8000300c, 0x40038, 0x60031, // ldb R31,[R38] +0x8000300c, 0x7003a, 0x30034, // ldw R34,R3a +0x80002011, 0x200000c // interpolate +}; + + + +uint tbl2 [] = { // later table lookup + + // tab addr in [3], cols in [4] + +0x80004008, 0x80037, 0x30038, 0x1003a, // ml3b R3a, R38, R37 +0x80003006, 0x90035, 0x1003a, // ad2b R3a, R35 +0x80003006, 0x00, 0x2003b, // adcb R3b, 0 +0x80003006, 0x1003a, 0x4003c, // ad2w R3c, R3a +0x8000300c, 0x4003c, 0x90035, // ldb R35, [R3c++] +0x8000300c, 0x4003c, 0x80037, // ldb R37, [R3c] +0x80003006, 0x30038, 0x4003c, // ad2w R3c, R38 r3c tab addr[3] 38 is cols [4] +0x8000300c, 0x70034, 0x60039, // ldb R39, R34 +0x8000300c, 0xa0036, 0x2003b, // ldb R3b, R36 +0xa0002011, 0x200001c, // interpolate(); +0x8000300c, 0x4003c, 0x80037, // ldb R37, [R3c] +0x8000400c, 0x4003c, 0x000ff, 0x90035, // ldb R35, [R3c+ff] +0x8000300c, 0xb003e, 0x4003c, // ldb R3c, R3e +0x80002011, 0x200000c, // interpolate +0x8000300c, 0xb003e, 0xa0036, // ldw R36, R3e +0x8000300c, 0x4003c, 0x70034, // R34 = R3c +0x8000300c, 0x2003b, 0x60039 // R39 = R3b +}; + + +uint enc24 [] = { // encoded addresses type 4 and 2 +0x8000300c, 0x4080037, 0xa003a, // ldzbw R3a,R37; +0x80003005, 0x3001f, 0x4020037, // an2b R37,0x1f (t4) or 0xf (t2) +0x80003004, 0x04, 0xa003a, // shrw R3a,4 +0xC0003005, 0x0e, 0xa003a, // an2b R3a,e not in type 2 +0x80004006, 0xa003a, 0x500f0, 0x80036 // R36 += [R3a+f0]; +}; + + +uint enc13 [] = { // encoded addresses type 3 and 1 (A9L etc) + +0x80003017, 0x4080042, 0x2000014, // jnb B7,R43,3cda (drop bit) +0x8000300c, 0x4080042, 0x4003a, // ldzbw R3a,R43 (drop bit) +0x000b0000, 0x303, // any order opcodes, mult, 11 entries, all 3 patts must match + +0x80003005, 0xf, 0x4080042, // an2b R43,f +0x80003005, 0x30070, 0x4003a, // an2b R3a,70 (0xfe is a t1, 0x70 is a t3) +0x80003004, 0x3, 0x4003a, // shrw R3a,3 + +0x80004006, 0x4003b, 0x500f0, 0x80042, // ad2w R42,[R3a+f0] +0xC101300d, 0x9003e, 0x80042 // stw R42,[R3e] optional + +}; + +uint rbse [] = { // rbase array code + +0x8000300c, 0x100f0, 0x20018, // ldw R18,f0 +0x8000400c, 0x0, 0x30020, 0x5001a, // ldb R1a,[0+2020]; +0x8000300c, 0x60014, 0x7001c, // ldw R1c,[R14++] +0x8000300d, 0x20018, 0x7001c, // stw R1c,[R18++] +0x80003012, 0x5001a, 0x800f7, // djnz R1a,d477 +}; + + + +// process subroutine for sigs + +void fnplu (SIG*, SBK*); +void fsplu (SIG*, SBK*); +void tbplu (SIG*, SBK*); +void encdx (SIG*, SBK*); +void ptimep (SIG*, SBK*); +void avcf (SIG*, SBK*); +void rbasp (SIG*, SBK*); + +// ***** PATS are sign,name, sigproc, size. + +// initial search sigs + +// specifically called +PAT hdr = {initial, "INIT" , 0 , NC(initial) ,0 }; // initial jump +PAT intign = {ignint, "IGNINT" , 0 , NC(ignint) ,0 }; // ignore interrupt + +// prescan sigs + +PAT rbase = {rbse, "RBASE", rbasp, NC(rbse), 0}; // rbase lookup + +PAT *prepats[] = {&rbase}; + + + +//specifically called in tbplu, for signed check + + +PAT tint = {ttrps, "istrp" , 0 , NC(ttrps) ,0 }; // table interpolate (for signed/unsigned checks) + +PAT *apats[] = {&tint}; + + + + +// scanned with code scans if PSIGS set + +PAT avcl = {avct, "AVCT" , avcf , NC(avct) ,0 }; // altstack vector list +PAT timr = {times, "TIMER", ptimep , NC(times) ,0 }; // timer list +PAT fnlp = {fnlu, "FnLU" , fnplu , NC(fnlu) ,0 }; // func lookup - byte or word +PAT tblp = {tbl2, "TBL2" , tbplu , NC(tbl2) ,0 }; // table lookup, later multibank +PAT tblx = {tblu, "TBLU" , tbplu , NC(tblu) ,0 }; // table lookup, early +PAT ec13 = {enc13, "1ENC3", encdx , NC(enc13) ,1 }; // encoded address types 3 and 1 +PAT ec24 = {enc24, "2ENC4", encdx , NC(enc24) ,2 }; // encoded address type 4 and 2, + +PAT *cpats[] = {&avcl, &timr, &fnlp, &tblp, &tblx, &ec13, &ec24}; //,&fnfs}; + + +//************************************************************************ + + + +int do_datsig(SIG *z, uint foff, uint *sx, int psize, int dsize) +{ + /* data part of opcode signature + * (ai) autoinc or int index is ALWAYS for first register.... + * match the DATA after an opcode, return true or false + * foff file offset + * sx start point (data or operands) for pattern + * psize size of pattern + * dsize datasize and flags + *******************************************/ + +int ans, ix; +uint sval,tval,pval, xval, max; // for register vals +uint sg; + +// OPCODE size and true ops in dsize, as well as patt size +// psize should equal real no of ops. dsize is zero for data only match + +ans = 1; + +if (dsize) + { // if opcode at front, check operands. + ix = (dsize >> 4) & 0xf; // true no of ops from opsize. + if (ix != psize) return 0; + } + +ix = 0; +max = maxpcadd(foff); + + + if (dsize & 0x8000) + { //12 and 13 swop...do ops in reverse order + ix = psize -1; + } + + +while (ans && ix < psize && ix >= 0) // extra check for both ways... + { + if (foff >= max) return 0; // hit end of file + + sg = sx[ix]; + + if (sg & OPCOD) return 0; // ->ix this is an opcode flag, stop + + sval = g_byte(foff); // next binfile value + + pval = sg & 0xff; // pattern - mod for autoinc fiddles + tval = (sg >>16) & 0xf; // array index value 1f + + // drop lowest bit + + if ((dsize & 0x1000)|| (sg & SDLBT)) + { + sval &= 0xfe; // autoinc or int indexed, drop flag (value) + pval &= 0xfe; // drop flag (pattern) + dsize &= 0xefff; // drop lowest bit request (0x1000) + } + + // do word check + + if ((ix == 0 && (dsize & 0x2000)) || (ix == 1 && (dsize & 0x4000))) + { // convert to word sized operand (imd or inx data) + xval = g_byte(foff+1); + foff++; + sval |= (xval << 8); + if (sval > PCORG) // only for non-regs + { if (tempbank < 0x100000) sval |= tempbank; else sval |= basepars.datbnk;} //correct address for inx + } + + if (sg & SDINC) + { // increment value by repeat count for saved reg + xval = tval; + tval += z->v[(sg >> 8) & 0xf]; // 1f where repeat count is stored + if (tval > NSGV) tval = xval; // safety check to increment tval + } + + if (tval && ans && (!z->v[tval] || (sg & SDANY))) z->v[tval] = sval; // save register to index + + if ((sg & SDANY) == 0) // not ignore value + { + if (tval) + { + if (sval != z->v[tval]) ans = 0; + } + else + { + if (sval != pval) ans = 0; + } + } + + + if (dsize & 0x8000) ix--; else ix++; // next pattern (f or b) + foff++; + } + + return ans; // hit end of pattern + } + + + +//this is really BAD, to use tempbank................nasty.... + +uint do_jumpsig (int inx, uint foff, uint bank) + { // calc jump address for the different jump types + // need SIGNED values in here + +// need to change to calc-jump ops type which allows for wrap..... + + uint joff; + + joff = 0; // jump offset + + if (bank >= 0x100000) bank = (foff & 0xf0000); //get bank from foff + + if (inx > 49 && inx < 52) + { // pushw + joff = g_word(foff + 1); // direct address + joff |= bank; // add bank + } + + if (inx > 53 && inx < 84) + { // jumps + if (inx > 69 && inx < 73) joff = g_val(foff+2, 0, 39) + 3; // JB, JNB, DJNZ signed byte + else if (inx > 80 && inx < 84) joff = sjmp_ofst (foff) + 2; // short, special calc + else if (inx > 72 && inx < 76) joff = g_val(foff+1, 0, 47) + 3; // long, signed word + else if (inx == 80) joff = 2; // skip = addr + 2 + + + joff+=foff; // gets bank from foff + joff &= 0xffff; // strip bank (and any wrap) + joff |= bank; + + if (joff >= maxpcadd(foff)) joff = 0; + if (inx > 75 && inx < 80) joff = 0; // Don't keep if a ret + } + return joff; + } + + +int do_optopc(uint *foff, uint maxadd) + { + // skip any 'optional' opcodes. max of 16 allowed. + // ignore if f2-f7, fa-ff, + + int ans, opc; + + ans = 0; + + while(1) + { + opc = g_byte(*foff); // code file value + + if (opc < 0xf2) break; // not skippable - break; + if (opc == 0xf8) break; + if (opc == 0xf9) break; + + (*foff)++; + ans++; + if (ans >= 15 ) break; // skip up to 15 opcodes + if (*foff > maxadd) break; + } + return ans; // number of skips + } + +int opsize(int inx, char opc, uint foff) + { + int x, gdsize; + const OPC *ope; + // change answer to include autoinc, op subtype, word + // size (&f), nops (&f0), inc (0x1000), imd word (0x2000), inx word (0x4000) + // swop operands (stx match to ldx) (0x8000) + // nops is true number, so +1 for indexes + + ope = opctbl + inx; + + x = ope->nops; + + gdsize = x+1; // basic size - num operands (2 bits) plus 1 opcode + + if (inx >= 10 && inx <=53) // multiple address mode types only + { + + switch (opc & 3) + { + case 1: + if ((ope->rfend1 & 31) > 7) // bigger than byte + { + gdsize++; + gdsize |= 0x2000; // imd word + } + break; + + case 2: + if (g_byte(foff+1) & 1) gdsize |= 0x1000; // autoinc (set drop lowest bit) + break; + + case 3: + if (g_byte(foff+1) & 1) + { + gdsize += 2; + gdsize |= 0x5000; // inx word (+ drop lowest bit) + } + else gdsize ++; + x++; // extra op for index + break; + + default: // this for non 'M' types too.... + break; + } + } + + if (inx > 76 && inx < 80) gdsize ++; // long jumps have extra byte + + gdsize |= (x << 4); // keep true numops for matching + + return gdsize; +} + +int check_sign(int foff,int sgstate) +{ + // check whether opcode has a sign prefix in front + + int val, ans; + + ans = 0; + val = g_byte(foff-1); // get previous value + + if (sgstate & OPSVL) + { // sign required + if (val == 0xfe) ans = 1; + } + else + { + if (val != 0xfe) ans = 1; + } + +return ans; + +} + + + + +int do_sgentry(SIG *z, uint **sz, uint foff) + { // do ONE sign entry (code+data) + + int ans, skp, rpt; + uint max; + int opc, inx, tval, xval, sval; + int psize, gdsize; + OPC *opl; + uint *sx, sg; + + sg = **sz; + sx = *sz; + + psize = (sg >> 12) & 0xf; // size of this pattern (inc data) ADDED OUTSIDE SUBR + ans = 1; // 1 is match + if ((sg & OPRPT) && (sg & OPSPV)) rpt = 1; else rpt = 0; // allow optional repeat + max = maxpcadd(foff); + + if (foff >= max) return 0; // foff+psize ? + + if (!(sg & OPCOD)) + { + // CAN have just data (subr pars for example) + ans = do_datsig(z,foff, sx, psize, 0); // data match, no opcode size + foff += psize; // psize, gdsize is not reliable here ! + *sz += psize; // and move pointer + if (ans) return foff; //continue; + return -foff; // no match. + } + + + // opcode and operands + + while (ans) // need loop for repeat and optional patterns... + { + skp = do_optopc(&foff, max); + + if (foff >= max) return 0; + if (skp > 15) return 0; // too many skips (0xff) + + opc = g_byte(foff); // code file value - skip prefixes in sigs + + // codbank and datbank OK as default + // but NOT for any subsequent opcodes, so check here + + tempbank = 0x100000; // no bank change + if (opc == 0x10) + { // bank change + sval = g_byte(foff+1); + sval++; //for new offest banks + if (sval >= 1 && sval <= 16) + { // valid, skip this and the bank value + if (bkmap[sval].bok) tempbank = sval << 16; + foff+=2; + opc = g_byte(foff); + } + } + + inx = opcind[opc]; + opl = opctbl+inx; // opcode table entry + + if (opl->p65 && !P8065) inx = 0; // check 8065 + if (!inx) return 0; // invalid opcode, match fails + + xval = opl->sigix; // sval now sig group value but this only a char....128 max + sval = sg & 0xff; + + gdsize = opsize(inx, opc, foff); + + if ((sg & OPSPX) && opl->sign) ans = check_sign(foff,sg); + + if (sg & OPSPX && (xval + sval) == 25) gdsize |= 0x8000; // mark STW or LDW + else + if (xval != sval) ans = 0; // no opcode match (by sigix) + + + tval = (sg >> 16) & 0xf; // array index 1f + + if ((sg & OPJMP)) z->v[tval] = do_jumpsig (inx,foff, tempbank); // jump or call - save dest, and can be sgv[0] + + if (inx > 9 && inx < 54 && (sg & OPSPV) && tval != (opc & 3)) ans = 0; // check opcode subtype for multi modes + + if (tval && !z->v[tval] && (sg & OPSPV)) + { + if (inx >= 70 && inx <= 71) //jnb (70) or jb (71) + { + z->v[tval] = (1 << (opc & 0x7)); // keep bit mask + if (inx == 70) z->v[tval] |= 0x1000; // and state JNB=1, JB=0 (JNB is swop bit) + } + + if (inx == 56 || inx == 57 || inx == 101 || inx == 102) + { if (inx & 1) z->v[tval] |= 0x10000; // save carry state for stc,clc,jc,jnc + } + } + + if (inx > 9 && inx < 54 && (sg & OPSST)) z->v[tval] = (opc & 3); // SAVE opcode subtype for multi modes + + if (sg & OPOPT) memcpy(bgv, z->v, NSGV*sizeof(int)); // save params + + if (ans) ans = do_datsig(z, foff+1, sx+1, psize-1, gdsize-1); // try data match if opcode matches + + if (!ans) + { + // opcode or data params do not match + // this is OK IF optional pattern + // OR end of a repeat patt with at least one match + + if ((sg & OPOPT) || ((sg & OPRPT) && rpt)) + { + if (sg & OPOPT) memcpy(z->v, bgv, NSGV*sizeof(int)); // restore params + ans = 1; + rpt = 0; + *sz += psize; // and move pointer + return foff; + } + } + + foff += (gdsize & 0xf); // next opcode in bin file + + if (!ans) break; + if (!(sg & OPRPT)) break; // only continue (and loop) if ans and OPRPT set + + rpt++; // mark repeat + if (tval) + { + z->v[tval]++; // count of repeats + if (z->v[tval] > 255) ans = 0; // safety limit max repeats + } + } // end of while + + *sz += psize; // and move pointer + if (ans) return foff; else return -foff; + + } + + +int do_subsig(SIG *z, uint **sz, uint foff) + { + // subpattern with optional matches - max of 255 patts (ff) and 255 matches (ff) + // updates pattern pointer (sz) Needs at least ONE match reqd (I think). + + uint *st, *end; + uint pattsz, sg; + + int ans, score, cmask, ix, psize, counts, max, save; + + // not sure if opt trailing works right + + pattsz = (**sz >> 16) & 0xff; + end = *sz + pattsz; // size of this subpattern (first word) + + sg = *(*sz + 1); // next word (subloop master data) + score = sg & 0xff; // count of patts which must match (once) in lower byte + counts = (sg >> 8) & 0xff; // number of patterns - in upper nibble + save = (sg >>16) & 0xf; // 1f index to save matching mask to + + max = counts; //score; // patterns to try + cmask = 0; + + for (; counts > 0; counts--) // set up 'match' bit mask + { + cmask <<= 1; // shift up one; + cmask += 1; // add another bit + } + + counts = 0; + + while (cmask && counts < max) + { // tries left + st = *sz+2; // first sig of optional list + ix = 1; // first sig index for bitmask + + while (st < end) // try all patts within (against cmask) + { + psize = (*st >> 12 & 0xf); // size of this pattern + if (cmask & ix) + { // not matched yet + memcpy(bgv, z->v, NSGV*sizeof(int)); //save pars + ans = do_sgentry(z, &st,foff); // do one (std) entry + if (ans > 0) // matches + { + foff = ans; // move on in binary + score --; // decr outstanding + cmask &= ~ix; // clear this try flag + if (save) z->v[save] |= ix; // save match if reqd + } + else + { + memcpy(z->v, bgv, NSGV*sizeof(int)); //restore pars + } + } + else st += psize; // move pattern if matched already + ix = ix << 1; + } // st < end + counts++; + } // tmask + + if (score > 0) return 0; // > 0 ?? some patt not matched + + *sz += pattsz; // and move to next patt + return foff; + + } + + + + int do_skpsig(SIG *z, uint **sx, uint foff) + { + int ans, counts, max, tval; + uint *st, sg, ptsz; + + // assumes skip down to ONE defined opcode at the moment.... + // may work for DATA too ? but not an OPT pattern at the end... + + sg = **sx; + max = (sg >> 12) & 0xf; // max no of opcodes to skip. + tval = (sg >> 16) & 0xf; // 1f where no of skips get saved + + sg = *(*sx+1); // get the match patt after the skip word + ptsz = ((sg >> 12) & 0xf) + 1; // size of that pattern inc match and start word + + counts = 0; + + while (counts < max) + { // tries left + st = *sx + 1; // reset to 2nd word + memcpy(bgv, z->v, NSGV*sizeof(int)); // save pars + if (*st & 0x3f) + ans = do_sgentry(z, &st,foff); // real entry (2nd word), swop ans + else + ans = do_subsig(z, &st,foff); // subpattern 2nd word on (opts and/or variable order) + + if (ans < 0) // no match + { + foff = (-ans); // next opcode (skip if fail) + memcpy(z->v, bgv, NSGV*sizeof(int)); // restore pars + counts++; // and try again + } + else + { + foff = ans; + break; // match + } + } + + if (tval) z->v[tval] = counts; + + *sx += ptsz; // add pattsize + if (counts < max) return foff; + + return ans; + + } + + + +SIG* match_sig (PAT *ptn, uint xofst,uint ix) +{ + int ans; + + uint *sx, *end; + uint sg, ofst, max, foff; + SIG *z; // for signature chaining + + if (anlpass >= ANLPRT) return 0; + + ofst = xofst; + max = maxpcadd(ofst); // safety + + if (ofst < minadd(ofst) || ofst >= max) return 0; + + z = (SIG *) chmem(&chsig,ix); // use block specified.... + z->ptn = ptn; + + // check for redundant opcodes at front + z->skp = do_optopc(&ofst, max); + + ans = ofst; // non zero is match + foff = ofst; // file binary offset + sx = ptn->sig; // signature entry + end = sx + ptn->size; // end of sig + +// sx now moved by each sub func called + + while (ans > 0 && sx < end) + { + if (foff >= max) return 0; // fail if drop off end + sg = *sx; + + if (sg & 0x3f) // allow up to 63 for sigix.. + ans = do_sgentry(z, &sx,foff); // std pattern - do one entry + else + { // special patterns - sigix is ZERO + if (sg & OPOPT) + ans = do_skpsig(z, &sx,foff); // 'skip to X' pattern + else + ans = do_subsig(z, &sx,foff); // subpattern (opts and/or variable order) + } + if (ans > 0) foff = ans; // next opcode + + } // end of while + + tempbank = 0x100000; + + if (ans > 0 && foff > ofst) + { // must have at least one matched patt + z->start = xofst; // addr before skips + z->end = foff; // end of patt + return z; // match + } + return 0; // no match +} + + +SIG* do_sig (PAT *ptn, uint xofst) +{ + SIG *z, *r; // for signature chaining + + z = match_sig (ptn, xofst,0); + + if (z && z->start) + { + // add sign (in z) to sig chain + r = inssig(z); // insert sig into chain + if (r != z) return r; // duplicate, return original + return z; + } + + return 0; + } + + + +SIG* scan_asigs (int ofst) +{ +// scan ofst for modifier (signed) signatures + + uint j; + SIG *s; + + if (PMANL) return 0; + if (!(PSIG)) return 0; + + for (j = 0; j < NC(apats); j++) + { + s = do_sig (apats[j], ofst); // code list + if (s) break; // stop as soon as one found + } + + return s; +} + + +SIG* scan_sigs (int ofst) +{ +// scan ofst for signatures from list supplied. EXTERNALLY visible + + uint j; + SIG *s; + + if (PMANL) return 0; + if (!(PSIG)) return 0; + + // only scan cpats if PSIG set + + for (j = 0; j < NC(cpats); j++) + { + s = do_sig (cpats[j], ofst); // code list + if (s) break; // stop as soon as one found + } + + return s; +} + + +void prescan_sigs (void) +{ + // scan whole binary file (exclude fill areas) for signatures first. + // do preprocess if reqd + + uint ofst; + uint i, j; + SIG *s; + BANK *b; + + if (PMANL) return; + if (!(PSIG)) return; + + #ifdef XDBGX + DBGPRT(1,0); + DBGPRT(1," -- Scan whole bin for pre sigs --"); + #endif + + for (i = 0; i < BMAX; i++) + { + b = bkmap + i; + if (!b->bok) continue; + + for (ofst = b->minpc; ofst < b->maxbk; ofst++) + { + show_prog (anlpass); + + for (j = 0; j < NC(prepats); j++) + { + s = do_sig (prepats[j], ofst); // prescan list + if (s) + { + ofst = s->end; //sigs can't overlap + if (s->ptn->pr_sig) s->ptn->pr_sig(s,0); // do any required pre-processing + break; + } + } + } + } + + #ifdef XDBGX + DBGPRT(2," -- End Scan whole bin --"); + #endif +} + + +/// END of sign.cpp + + + + + + + + + + + + + + + + diff --git a/Development/sources/4.07.16b/sign.h b/Development/sources/4.07.16b/sign.h new file mode 100644 index 0000000..8824c7f --- /dev/null +++ b/Development/sources/4.07.16b/sign.h @@ -0,0 +1,123 @@ +/************************************************** +* Signature defines +* Signatures are like a 'regular expression' matcher +* a set of pattern matches to find specific items in the binary. +* for function lookups, rbase lists, and so on. +*************************/ + +#ifndef _XSIGNX_H + +#define _XSIGNX_H 1 + +#include "core.h" + +// OPCODE WORD (top nibble) +#define OPCOD 0x80000000 // this is an opcode, data pattern if not set. +#define OPOPT 0x40000000 // optional instruction (single) or 'skip to' subsig if opcode is zero +#define OPJMP 0x20000000 // keep jump destination +#define OPRPT 0x10000000 // repeat opcode - repeats must have an index (tval) + +// next nibble - more flags + +#define OPSPV 0x08000000 // special extras +#define OPSPX 0x04000000 // match stx with ldx, sign prefix +#define OPSVL 0x02000000 // prefix for signed present +#define OPSST 0x01000000 // save opcode subtype + +/* flags + * OPSPV special func + * keep jump bit bit and state from JB or JNB (into params[tval]) + * keep carry state from a JC,JNC,STC,CLC + * allow zero repeats (i.e. optional + repeat) with OPRPT + * + * OPSPX + * allow ldx to match stx (and swop register data i.e LDX a, b is same as STX b, a) + * with 'sign prefix' opcodes, specify whether prefix is there (0xFE) + * OPSVL + * a flag to specify prefix present or not with OPSPX. used for mults and divs (i.e. signed or unsigned) + * OPSST + * save opcode subtype in tval for multitype opcodes +*/ + +// DATA WORD(s) + +#define SDINC 0x8000000 // increment/save index on a repeat +#define SDLBT 0x4000000 // drop lowest bit of register (for autoinc and some byte ops) +#define SDANY 0x2000000 // is allow any value, save first one +//#define SDDAT 0x1000000 // save data from register + +/************************************* + * DESCRIPTION + * + * 32 bit unsigned integer - by byte (top down) + * + * First integer (opcode) + * NB. if sigindex is zero and OPCODE flag is zero, this means a special subpattern (see further below) + * + * 0xff00 0000 flags (opcode, all 8 used) + * 0x000f 0000 index value into params array for saved items, repeat counts, regs etc + + * 0x0000 f000 pattern size (= num of ints including this int) [>> 12 & 0xf] + * 0x0000 003f if opcode flag set, this is opcode signature index + * if not set, this becomes a data value match (not sig index) + * + * All patterns begin with a first integer + * then rest of words are DATA pattern. (as in OPCODE-DATA-DATA, etc) + * + * DATA/operands (follows opcode match) + * + * 0x0f00 0000 flags (data - 3 used) + * 0x000f 0000 index for saved value (register or data) for matching (ixval) + * 0x0000 0f00 count value location if SDINC is set, for incremental list + * 0x0000 0f00 actual register value location if SDDAT is set + * 0x0000 00ff register or data to match (ignore if SDANY is set) + * + * If ixval set, then a param is SAVED into the par array (0-0x1f) + * normally a register, but can be bit number, or repeat count, etc. as modded by some flags. + * First matched occurrence with tval set (ie saved val is zero) is treated as SDANY automatically + * repeats not set as list therefore need SDANY set + * + * Ignores up to 15 'auto ignore' opcodes (NOP etc) + * + * Special subpatterns + * + * if opcode value is zero, do a SUBSIG or a SKIPSIG pattern, selected by OPOPT flag + + * SUBSIG is an 'any order' match of opcodes. + +first val is top and bottom byte + + * top byte - no of patterns in subpatt + * low byte - no of patterns which must match (at least once) + +if ix set then matches are stored in this array index + +then following patts can match in ANY ORDER. + + * SKIPSIG + * + * simple single entry which defines 'up to 'n' opcodes (and operands) + * can be skipped. If tval set count if stored. not safe to follow with an OPT + * opcode I think...(not tested) + ************************************************************** + +* NOTES +* NEW - standardize levels at v[1] and no of pars at v[2] (e.g fpar vpar) +* then use par list later and standardize for consecutive checks at er... +* possibly use 3 and 4 if multilevel thing. + + * sg-val for first opcode is really only ever bottom byte, so can use flags in top..... + * but keep as full short for data so addresses can be used ? + * + * + * + may need marker for bnk, prefix etc (by pattern ?) for some of the more clever multibanks +(BWAK) change main loop so it scans by bank, and keep latest RBNK + + enc24 should be a sub for tables ?? + +*************************/ + + + +#endif \ No newline at end of file From ce0ee13a5856fd720f5d70cb8ca9e3d0d8e938f1 Mon Sep 17 00:00:00 2001 From: jsa2340 <36245808+jsa2340@users.noreply.github.com> Date: Tue, 12 Apr 2022 21:22:18 +1000 Subject: [PATCH 7/8] Delete text --- Development/sources/4.07.16b/text | 1 - 1 file changed, 1 deletion(-) delete mode 100644 Development/sources/4.07.16b/text diff --git a/Development/sources/4.07.16b/text b/Development/sources/4.07.16b/text deleted file mode 100644 index 8b13789..0000000 --- a/Development/sources/4.07.16b/text +++ /dev/null @@ -1 +0,0 @@ - From 756ff6cfc4e83ce3bd840a8ad3736f60c24f1f77 Mon Sep 17 00:00:00 2001 From: jsa2340 <36245808+jsa2340@users.noreply.github.com> Date: Tue, 12 Apr 2022 21:25:07 +1000 Subject: [PATCH 8/8] Added Word Table special functions --- Docs/SAD_Commands_definition_4.0.7.16b.pdf | Bin 0 -> 17784 bytes Docs/SAD_User_manual_4.0.7.16b_onwards.pdf | Bin 0 -> 75559 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 Docs/SAD_Commands_definition_4.0.7.16b.pdf create mode 100644 Docs/SAD_User_manual_4.0.7.16b_onwards.pdf diff --git a/Docs/SAD_Commands_definition_4.0.7.16b.pdf b/Docs/SAD_Commands_definition_4.0.7.16b.pdf new file mode 100644 index 0000000000000000000000000000000000000000..67bf8e1fcfdc72317d967e1484900935ea70608b GIT binary patch literal 17784 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

c>2=G(XPhtg4&+^^M4Kx{R+ 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 TzV{UXFDC?nXlcb?N}&87R*m_l literal 0 HcmV?d00001 diff --git a/Docs/SAD_User_manual_4.0.7.16b_onwards.pdf b/Docs/SAD_User_manual_4.0.7.16b_onwards.pdf new file mode 100644 index 0000000000000000000000000000000000000000..de18c8f9a54e67ce3349f241822057ade8ae2c6b GIT binary patch literal 75559 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 literal 0 HcmV?d00001