home *** CD-ROM | disk | FTP | other *** search
/ CP/M / CPM_CDROM.iso / cpm / utils / squsq / fcrnch11.lbr / UNC.MZC / UNC.MAC
Text File  |  1986-12-24  |  24KB  |  996 lines

  1. title    'UNC & UNCREL uncrunch module'
  2. ; Original version by Steven Greenberg.  Revised by C.B. Falconer to:
  3. ;  Execute on 8080/8085/V20    Check for memory availability
  4. ;  Correct "sigrev" check    Correct error responses.
  5. ;  Generally compacted code module.
  6. ;  Entry UNC avoids input file rewind, i.e. after initial 0 read.
  7. ;  Entries CODES and TROOM allow monitoring codes assigned/re-assigned
  8. ;    (for version 2).  TROOM is also version 1 codes free.
  9. ;
  10. ; Following kept in separate library module.  Thus linking without
  11. ; search will include it, but with search removes from object.
  12. ;    db    'Copyright (c) 86/11/24 by'
  13. ;    db    ' Steven Greenberg  201-670-8724'
  14. ;    db    ' and C.B. Falconer  203-281-1438.'
  15. ;    db    ' May be reproduced for non-profit use only.'
  16. ;
  17. ; error codes (0 for no error)
  18. version    equ    1;        newer uncrunch version required
  19. isnotcr    equ    2;        File is not crunched
  20. fouled    equ    3;        File is fouled
  21. memory    equ    4;        Memory or stack overflow
  22. ;
  23. ver    equ    21;        Kept at UNCREL-1 for referance
  24. ;
  25. ; move right n columns, same row
  26. right    macro    n
  27.      mov    a,h
  28.      adi    n*10h
  29.      mov    h,a
  30.     endm
  31. ;
  32. memtop    equ    6;        CPM keeps top of memory avail here
  33. ;
  34. sigrev    equ    20h;        significant revision level
  35. impred    equ    07fffh;        impossible pred, can never be matched
  36. nopred    equ    0ffffh;        no predecessor code
  37. vacant    equ    080h;        value for a vacant entry
  38. guard    equ    07fh;        protect table entry from use
  39. crnchid    equ    0fe76h;        initial crunched file id wd (lsb 1st)
  40. escape    equ    090h;        repeated byte encoding
  41. ;
  42. ; For version 2 algorithm
  43. initw    equ    9;        initial cell width
  44. maxwide    equ    12;        max width of cells
  45. tblsize    equ    5003
  46. ;
  47. ; Version 2 special codes
  48. eofcod    equ    0100h
  49. rstcod    equ    0101h;        adaptive reset signal
  50. nulcod    equ    0102h;        NOP
  51. sprcod    equ    0103h;        spare for future use
  52. ;
  53. n01    equ    1
  54. n02    equ    2
  55. n08    equ    8
  56. n0f    equ    000fh
  57. n10    equ    0010h
  58. n14    equ    0014h
  59. n20    equ    0020h
  60. n28    equ    0028h
  61. n30    equ    0030h
  62. ndf    equ    00dfh
  63. nfe    equ    00feh
  64. nff    equ    00ffh
  65. t0fff    equ    0fffh
  66. t1000    equ    1000h
  67. t2000    equ    2000h
  68. t2800    equ    2800h
  69. t4000    equ    4000h
  70. ;
  71. ;      TABLE STRUCTURE:   The 4096 x 5 table is realized as five in-
  72. ;  dividual 4096 by one tables.  The first (and hence the rest)  are
  73. ;  page  aligned.  This organization simplifies address  calculation
  74. ;  and    "vertical"  searching at the expense of  slightly  increased
  75. ;  complexity  when  moving "horizontally" through the    table.    More
  76. ;  specifically,  converting  an entry# (index#) to  an  address  is
  77. ;  simply a matter of adding "hitabl" (table base addr, hi-byte)  to
  78. ;  the hi-byte of the index#.  Subtraction reverses the process-  no
  79. ;  multiplication or divide by five necessary.   Horizontal movement
  80. ;  is accomplished by adding 10H, 20H, 30H, or 40H to move "right" 1
  81. ;  to 4 columns respectively. Obviously subtracting the same amounts
  82. ;  moves "left". 
  83. ;
  84. ;    For  discussion  purposes,  however,  the  table  should  be
  85. ;  thought  of    as  having 4096 rows (entries) of  five  bytes    each
  86. ;  (columns).    For any given row, the first two columns  contain  a
  87. ;  "pred" value (hi-byte, then lo), the 3rd column contains a  "suf-
  88. ;  fix" char, and the last two columns may contain a link to another
  89. ;  entry (again, hi-byte first).  The link is not conceptually    part
  90. ;  of  the algorithm but is needed to resolve hash collisions.    Note
  91. ;  that  both the "pred" and the "link" columns contain a value  re-
  92. ;  ferring  to another entry (or contain a null reference).  In  the
  93. ;  case  of the "pred", the number entered is a 12-bit index  number
  94. ;  (0-4095),  with "FFFF" indicating "NOPRED".    In the case  of  the
  95. ;  link  columns, the reference is achieved with an actual  address,
  96. ;  with  0000  meaning "no link" (the difference is one  of  conven-
  97. ;  ience).  Note that link pointers point to the first column of the
  98. ;  intended entry.
  99. ;
  100. ;    The  first column ("pred", hi byte) also flags    whether  the
  101. ;  entry  is  vacant.  Legal values for this column  normally  range
  102. ;  from  00H - 0FH, plus "0FFH" in the case of "NOPRED".  "80H"  has
  103. ;  been  selected to represent a vacant entry.    Entry #0 contains  a
  104. ;  "7FH" in this position, insuring that entry is neither vacant nor
  105. ;  "matchable".   This prevents code 000 from ever  being  generated
  106. ;  under  normal  circumstances, allowing its use as a    special  EOF
  107. ;  code.
  108. ;
  109. ;  TROOM or CODES is used  to keep track of the number of  remaining
  110. ;  vacant entries. TROOM is decremented from 4095 to 0  (entry #0 is
  111. ;  not used), CODES is incremented for ver 2 use, when TROOM  counts
  112. ;  code re-assignments (for reference only).
  113. ; ___________________________________________________________________
  114. ;
  115.     cseg
  116.  
  117.     extrn    out, getbyt
  118.     entry    uncrel, unc;        program area
  119.     entry    codes, troom, endu;    data area
  120. ;
  121. ; This byte kept at UNCREL-1, for external referance
  122.     db    ver;        current revision level
  123. ;
  124. ; Main entry.  Read from start of file/module.
  125. uncrel:    call    malloc;        returns hl = new stack
  126.     jc    xstkov
  127.     sphl;            ok, now switch stacks
  128.     call    inbyte;        Check really is crunched file
  129.     cpi    (crnchid AND 0ffh)
  130.     jnz    xnotcr
  131.     call    inbyte
  132.     cpi    (crnchid shr 8)
  133.     jz    uncra;        validated, go do it from start
  134. ;    "    "
  135. xnotcr:    mvi    a,isnotcr
  136.     jmp    error
  137. ;
  138. xstkov:    mvi    a,memory
  139.     jmp    error
  140. ;
  141. xbadf:    mvi    a,fouled
  142.     jmp    error
  143. ;
  144. xnewv:    mvi    a,version
  145. ;    "    "
  146. error:    stc
  147. ;    "    "
  148. exit:    lhld    spsave
  149.     sphl
  150.     ret
  151. ;
  152. exitok:    xra    a
  153.     jmp    exit
  154. ;
  155. ; Entry here if application has already read the header, and
  156. ; validated the initial id bytes.  This avoids rewinding the file.
  157. ; The next input byte MUST be the revision level.
  158. unc:    call    malloc;        returns hl = new stack
  159.     jc    xstkov
  160.     sphl;            ok, now switch stacks
  161.     jmp    uncrb
  162. ;
  163. ; Set up memory allocation.  base pointer in hl
  164. ; Carry if insufficient space (stack overflow incipient)
  165. malloc:    xchg
  166.     lxi    h,2;        allow for call malloc
  167.     dad    sp
  168.     shld    spsave;        save return from main
  169.     lxi    h,255
  170.     dad    d;        round up to page boundary
  171.     mvi    l,0
  172.     shld    @table
  173.     mvi    a,n30;    '0'
  174.     add    h
  175.     mov    h,a
  176.     shld    xlatbl;        for version 2 system
  177.     mvi    a,n28;    '('
  178.     add    h
  179.     mov    h,a
  180.     push    h
  181.     cma
  182.     mov    h,a;        4 LESS bytes than Z80 coding
  183.     inr    h;        l was zero
  184.     shld    stklim
  185.     pop    h
  186.     mov    a,h
  187.     adi    n08;        proposed stack page
  188.     mov    h,a;        check stack page a suitable
  189.     mvi    l,0
  190.     xchg;            Check memory against memtop and for
  191.     lhld    memtop;        input stack within range
  192.     mov    a,h;        @table thru newstack
  193.     sub    d;        (can exit because stack saved)
  194.     rc;            not enough system memory
  195.     lda    spsave+1
  196.     lhld    @table
  197.     cmp    h
  198.     jc    stkck1;        input stack below table, ok
  199.     mov    h,a;        input stack page
  200.     mov    a,d;        new stack page
  201.     cmp    h
  202. stkck1:    cmc
  203.     xchg
  204.     ret;            with carry if stack overflow
  205. ;
  206. ; Reading from start of file.
  207. uncra:    call    inbyte;        skip over id area
  208.     ora    a
  209.     jnz    uncra
  210. ;    "    "
  211. ; Entry here allowable after reading to initial 0.  Thus applications
  212. ; can first extract the initial info, and then enter here via "uncr"
  213. uncrb:    call    init;        variables etc
  214.     call    inbyte;        ignore revision level
  215.     call    inbyte;        significant revision level
  216.     push    psw
  217.     call    inbyte;        ignore checksum flag
  218.     call    inbyte;        and spare byte
  219.     pop    psw
  220.     cpi    sigrev+1
  221.     jnc    xnewv;        need newer version
  222.     cpi    sigrev
  223.     jnc    uncrc;        ver 20 uncrunching
  224. ;    "    "
  225. ; Ver 10 uncrunching
  226.     call    unc1i
  227.     jmp    unc1
  228.  
  229. uncrc:    call    unc2i;        Ver. 2, initialize tables
  230.     jmp    unc2
  231. ;
  232. ; Version 10 uncrunching initialize. Returns de := nopred
  233. unc1i:    lxi    h,t0fff
  234.     shld    troom
  235.     call    clrmem
  236.     mvi    a,12
  237.     sta    width;        ver 10 tokens are 12 bits
  238.     xra    a
  239.     sta    kind;        0 for version 10 operation
  240.     "    "
  241. ; initialize atomic entries. Set de := nopred
  242. atoms:    xra    a
  243.     lxi    h,nopred
  244. atoms1:    push    psw
  245.     push    h
  246.     call    enterx;        make entry { hl, a }
  247.     pop    h
  248.     pop    psw
  249.     inr    a
  250.     jnz    atoms1
  251.     xchg;            de := nopred
  252.     ret
  253. ;
  254. ; Version 20 setup.  Returns de := nopred
  255. unc2i:    call    clrtbl
  256.     mvi    a,1
  257.     sta    kind;        Version 20 signal
  258.     mvi    a,n20;        force non-bumpable atomic entries
  259.     sta    ffflag
  260.     call    atoms;        init atomic entries
  261.     mvi    b,(sprcod+1) and 0ffh
  262. unc2i2:    push    b
  263.     lxi    h,impred;    impossible pred
  264.     xra    a
  265.     call    enterx;        reserve eof thru sprcod
  266.     pop    b;        unmatchable and unbumpable
  267.     dcr    b
  268.     jnz    unc2i2;
  269.     xra    a
  270.     sta    ffflag;        reset flag
  271.     mov    h,a
  272.     mov    l,a
  273.     shld    troom;        re-used as re-assignment counter
  274.     lxi    d,nopred
  275.     ret
  276. ;
  277. ; Ver 10 uncrunching loop
  278. unc1:    xchg
  279.     shld    lastpr
  280.     call    getok;        new 12 bit code to de
  281.     jc    exitok;        eof or eof node
  282.     push    d
  283.     call    decode
  284.     lxi    h,entflg
  285.     mov    a,m
  286.     mvi    m,0
  287.     ora    a
  288.     cz    entlast;    make new table entry if not done
  289.     pop    d
  290.     lda    fulflg
  291.     ora    a
  292.     jz    unc1;        continue
  293. ;    "    "
  294. ; Speed up when table full, no more entries need be made/checked
  295. unc1b:    call    getok
  296.     jc    exitok
  297.     push    d
  298.     call    decode
  299.     pop    d
  300.     jmp    unc1b;        continue
  301. ;
  302. ; Version 2 uncrunching
  303. unc2:    xchg
  304.     shld    lastpr
  305.     call    getkn
  306.     jc    unc2c;        eof or reset etc.
  307.     push    d
  308.     call    decode
  309.     lxi    h,entflg
  310.     mov    a,m
  311.     mvi    m,0
  312.     ora    a
  313.     cz    entlast;    if not made, then make entry
  314.     pop    d
  315.     lda    fulflg
  316.     ora    a
  317.     jz    unc2;        adaptive system reset
  318.     cpi    nfe;        when this becomes 0ffh all done. First
  319.     jnz    unc2b;         it becomes 0feh, when one more loop
  320.     inr    a;         is required, and set it to 0ffh.
  321.     sta    fulflg
  322.     jmp    unc2;        do the extra loop
  323.  
  324. ; table is full.  No new entries needed
  325. unc2b:    xchg
  326.     shld    lastpr
  327.     call    getkn
  328.     jc    unc2c;        eof etc
  329.     push    d
  330.     call    decode
  331.     lhld    lastpr
  332.     lda    char
  333.     call    recod;        check for code re-assignment
  334.     pop    d
  335.     jmp    unc2b
  336. ;
  337. ; here for input codes in range 100h..103h (eof..sprcod).
  338. unc2c:    mov    a,e;        special code, (eof or adaptive reset)
  339.     cpi    eofcod and 0ffh
  340.     jz    exitok;        done
  341.     cpi    rstcod and 0ffh
  342.     jnz    xnotcr
  343. ;    "    "
  344. ; adaptive reset
  345.     xra    a
  346.     mov    h,a
  347.     mov    l,a
  348.     shld    codes;        init current code to 0
  349.     sta    fulflg;        clear
  350.     call    unc2i
  351.     mvi    a,initw
  352.     sta    width;        reset input code width
  353.     mvi    a,n02
  354.     sta    trgmsk
  355.     mvi    a,n01
  356.     sta    entflg;        1st entry always a special case
  357.     jmp    unc2
  358. ;
  359. ; VAR b : byte;    (* global *)
  360. ;
  361. ; PROCEDURE decode(x : index);
  362. ;
  363. ;   VAR ix : index;     (* index is a record *)
  364. ;
  365. ;   BEGIN (* decode *)
  366. ;   ix := lookup(x);
  367. ;   IF ix.pred = NIL THEN enter(x, b);
  368. ;   IF ix.pred = nopred THEN b := ix.byte
  369. ;   ELSE decode(ix.pred);
  370. ;   send(ix.byte);
  371. ;   END; (* decode *)
  372. ; The char associated with the bottomost recursion level is saved in
  373. ; "char" and is used later to make the next table entry.
  374. ;
  375. ; The code at "ugly" has to do with a peculiar string sequence where
  376. ; the encode "knows" about a string before the decoder so the decoder
  377. ; has to make an emergency entry.  Fortunately there is enough inform-
  378. ; ation available to do this.  It has been shown that this case is
  379. ; unique and that the assumptions are valid.  To understand the LZW
  380. ; algorithm the "ugly" code may be ignored.
  381. ;
  382. ; Universal decoder
  383. ; a,f,b,c,d,e,h,l
  384. decode:    lda    kind
  385.     ora    a
  386.     jz    dcda;        version 1, no setup needed
  387.     push    d
  388.     xchg
  389.     lda    @table+1
  390.     add    h
  391.     mov    h,a;        convert code to table adr.
  392.     mov    a,m
  393.     ori    020h;        mark referenced (not bumpable)
  394.     mov    m,a
  395.     pop    d
  396. ;    "    "
  397. ; decode/output the index in de. Recursive
  398. ; a,f,b,c,d,e,h,l
  399. dcda:    lhld    stklim
  400.     dad    sp
  401.     jnc    xstkov;        stack overflow
  402.     lda    @table+1;    Convert index de to address hl
  403.     add    d
  404.     mov    h,a
  405.     mov    l,e
  406.     mov    a,m
  407.     ani    ndf;        (for 2 only)
  408.     cpi    vacant
  409.     jnz    dcda1;        not vacant, normal case
  410. ;    "    "
  411. ; The "ugly" exception.  Term due to K. Williams
  412.     mvi    a,n01
  413.     sta    entflg
  414.     push    h
  415.     mvi    a,n20;        (for 2 only)
  416.     sta    ffflag
  417.     call    entlast;    make emergency entry
  418.     xra    a
  419.     sta    ffflag;        (for 2 only)
  420.     pop    h
  421.     mov    a,m
  422.     cpi    vacant
  423.     jz    xbadf;        If vacant file is invalid
  424. ;    "    "
  425. dcda1:    mov    d,m;        get "pred" (hi)
  426.     right    1;        move to "pred" (lo)
  427.     mov    e,m;        get it. If msb of hi byte is set value
  428.     mov    a,d;         must be ff (nopred) because not 80h
  429.     ani    not 20h
  430.     jm    decodx;        nopred, terminate recursion
  431.     mov    d,a;        (for 2, remove any accessed flag)
  432.     push    h
  433.     call    dcda;        recursive
  434.     pop    h
  435.     right    1;        move ahead to "suffix" byte
  436.     mov    a,m
  437.     jmp    send;        output suffix byte & exit
  438. ;
  439. ; Exit from decoding recursion.  Unloads all the stacked items.
  440. decodx:    right    1;        move ahead to "suffix" byte
  441.     mov    a,m;        get & save as 1st char of decoded
  442.     sta    char;         string.  Used later to make a new
  443. ;    "    "         table entry.  Send & exit
  444. ; Send char with repeat expansion etc.
  445. ; a,f,b,c,h,l
  446. send:    mov    c,a;        output char
  447.     lhld    outflg
  448.     inr    h
  449.     dcr    h
  450.     jnz    send2;        repeat flag set
  451.     cpi    escape
  452.     jz    send1;        escape char, set flag
  453.     mov    l,a;        save char for possible repeat coming
  454.     dcr    h;        cancel coming inr, not repeat
  455.     call    outbyt
  456. send1:    inr    h;        set repeat flag
  457.     shld    outflg
  458.     ret
  459.  
  460. send2:    mvi    h,0;        clear repeat flag
  461.     shld    outflg;        save result (with l = repeat char)
  462.     ora    a
  463.     jz    send4;        escape 0 represents escape
  464.     dcr    a
  465.     rz;            take care of repeat = 1
  466.     mov    h,a;        set repeat count
  467.     mov    a,l;        repeaat char
  468. send3:    call    outbyt
  469.     dcr    h
  470.     jnz    send3
  471.     ret
  472.      
  473. send4:    mvi    a,escape
  474.     jmp    outbyt
  475. ;
  476. ; Enter lastpr/char into table
  477. ; a,f,b,c,d,e,h,l
  478. entlast:
  479.     lhld    lastpr
  480.     lda    char
  481. ;    "    "
  482. ; enter { <pred>, <suffix> } into table, passed in {hl, a} regs.
  483. ; a,f,b,c,d,e,h,l
  484. enterx:    mov    b,a
  485.     lda    kind
  486.     ora    a
  487.     mov    a,b
  488.     jnz    ent2x;        version 2 decoding
  489. ;    "    "        else version 1 decoding
  490. ; enter { <pred>, <suffix> } into table, passed in {hl, a} regs.
  491. ; a,f,b,c,d,e,h,l
  492. ent1x:    push    psw
  493.     push    h
  494.     call    midsq;        hash index into al
  495.     mov    h,a
  496.     lda    @table+1;    page address
  497.     add    h
  498.     mov    h,a;        into address
  499.     pop    d;        pred
  500.     pop    psw;        suffix
  501.     mov    c,a
  502. ;    "    "
  503. ent1x1:    mov    b,h;        check for match
  504.     mov    a,m
  505.     cpi    vacant
  506.     jz    ent1x3;        Entry does not exist, make it
  507.     right    3;        move to link column
  508.     mov    a,m;        link(hi)
  509.     ora    a
  510.     jz    ent1x2;        no link
  511.     mov    b,a;        save
  512.     right    1;        move to link(lo) field
  513.     mov    l,m
  514.     mov    h,b;        hl := link address
  515.     jmp    ent1x1;        and repeat
  516.  
  517. ent1x2:    mov    h,b;        restore h to left hand column
  518.     call    ffree;        Find new spot and link in. Returns
  519. ;    "    "         hl pointing to new entry.
  520. ent1x3:    call    link;        make the entry.  pred(hi)
  521.     right    1
  522.     mov    m,c;        suffix
  523.     lhld    troom
  524.     dcx    h
  525.     shld    troom
  526.     mov    a,h
  527.     ora    l
  528.     rnz;            not full
  529.     dcr    a
  530.     sta    fulflg;        else set full flag
  531.     ret
  532. ;
  533. ; link entry de at location hl^
  534. link:    mov    m,d;        high
  535.     right    1
  536.     mov    m,e;        lo
  537.     ret
  538. ;
  539. ; Version 2 table entry
  540. ent2x:    push    psw
  541.     push    h
  542.     call    tbladr;        to physical loc only, affects nothing
  543.     pop    d;        and check width etc??
  544.     lhld    codes
  545.     lda    @table+1
  546.     add    h
  547.     mov    h,a;        convert to address
  548. ;    "    "
  549. ; Entry is made here, but normally flagged as "unreferenced" (until
  550. ; received by decode).  Until then entries are "bumpable".  If ffflag
  551. ; is 020h the reference is flagged now, to protect atomic entries and
  552. ; WsWsW string emergency entries (from decode, despite not received)
  553.     lda    ffflag
  554.     ora    d;        may set "referenced" bit
  555.     mov    m,a;        pred(hi)
  556.     right    1
  557.     mov    m,e;        pred(lo)
  558.     right    1
  559.     pop    psw
  560.     mov    m,a;        suffix
  561.     lhld    codes;        advance entry counter
  562.     inx    h
  563.     shld    codes
  564.     inx    h;        Allow for crunch/uncrunch skew delay
  565.     lda    trgmsk;        See if new code length needed
  566.     cmp    h
  567.     rnz
  568.     ral;            carry was clear.  Change to new length
  569.     sta    trgmsk;        new target mask
  570.     lda    width
  571.     inr    a
  572.     cpi    maxwide+1
  573.     jz    ent2x1;        mark table full
  574.     sta    width;        advance to new width
  575.     ret
  576.  
  577. ent2x1:    mvi    a,nfe;        mark table full, at max width
  578.     sta    fulflg
  579.     ret
  580. ;
  581. clrmem:    lhld    @table
  582.     mvi    m,guard;    disallow entry #0
  583.     inx    h;        (used, but unmatchable)
  584.     mvi    e,vacant
  585.     lxi    b,t1000;    mark entries vacant
  586.     call    fill
  587.     lxi    b,t4000
  588. ;    "    "
  589. ; Fill hl^ for bc with zero
  590. fillz:    mvi    e,0
  591. ;    "    "
  592. ; fill hl^ for bc with e
  593. fill:    mov    m,e
  594.     inx    h
  595.     dcx    b
  596.     mov    a,b
  597.     ora    c
  598.     jnz    fill
  599.     ret
  600. ;
  601. ; find a free entry in the event of a hash collision.  Algorithm is to
  602. ; first add 101 (decimal) to the current (end-of-chain) entry.  If
  603. ; that entry is not free keep adding 1.  When a free entry is found
  604. ; the link pointer of the original entry is set to the found entry.
  605. ;
  606. ; Called with adr of an entry in hl, returns hl = adr of new entry.
  607. ; a,f,h,l
  608. ffree:    push    b
  609.     push    d
  610.     push    h;        save pointer to old entry for update
  611.     mov    a,l
  612.     adi    101;        relatively prime to table size
  613.     mov    l,a
  614.     jnc    ffree1;        no carry, thus no wrap
  615.     inr    h
  616.     lda    @table+1
  617.     adi    n10
  618.     cmp    h
  619.     jnz    ffree1;        no wrap-around
  620.     lda    @table+1;    set to table bottom
  621.     mov    h,a
  622. ffree1:    lda    @table+1;    compute # of remaining entries,
  623.     adi    n0f;         counting up (last entry + 1
  624.     sub    h;         - current entry)
  625.     mov    b,a
  626.     mov    a,l;        as far as the low byte is concerned
  627.     cma;            we know we are subtracting from 0.
  628.     inr    a
  629.     jnz    ffree2
  630.     inr    b
  631. ffree2:    mov    c,a;        result in bc
  632.     mov    d,h;        keep copy
  633.     mov    e,l
  634.     call    cmpm;        search for empty entry
  635.     jnc    ffree3;        found vacant entry
  636.     lhld    @table;        Else wrap to start of table
  637.     lda    @table+1
  638.     mov    b,a
  639.     mov    a,d
  640.     sub    b;        (adr to index# conversion)
  641.     mov    b,a
  642.     mov    c,e;        target value
  643.     call    cmpm;        continue search
  644.     jc    xnotcr;        not found.  should not occur
  645. ffree3:    xchg
  646.     pop    h;        original pointer to link
  647.     right    3;        move to link(hi) field
  648.     call    link;        link to new entry
  649.     xchg;            returned in hl
  650.     pop    d
  651.     pop    b
  652.     ret
  653. ;
  654. ; Search for vacant entry from hl^ up. Carry if not found
  655. ; Carry clear if found when hl points to found entry
  656. ; a,f,b,c,h,l
  657. cmpm:    mov    a,m
  658.     cpi    vacant
  659.     rz
  660.     inx    h
  661.     dcx    b
  662.     mov    a,b
  663.     ora    c
  664.     jnz    cmpm
  665.     stc;            signal not found
  666.     ret
  667. ;
  668. ; Return the mid-square of number of "pred" + "suffix" (actually the
  669. ; mid-square of # OR 0800h). Entry a = suffix, hl = pred.  Returns
  670. ; result in a|l (not hl), ready to add a table offset.
  671. ;
  672. ; mid-square means the midddle n bits of the square of an n-bit num.
  673. ; Here n is 12.  Results accumulate in a 16 bit register, with
  674. ; extraneous information overflowing off both ends of the register. 
  675. ;
  676. ; Hash via mid-square of 12 bit input or'd with 800h.
  677. ; input is hl + a.  Output in al registers.
  678. ; NOTE anomalous results for input out of range.  Special handling
  679. ; since really needs to operate on 13 bit words to match the original.
  680. ; The algorithm is due to Robert A. Freed.  This runs on 8080s, takes
  681. ; the identical code space as Mr. Freeds Z80 implementation, and has
  682. ; miniscule or no average performance penalty.  By C.B. Falconer.
  683. ;
  684. ; Entry: a = suffix; hl = pred.  Exit al = midsq
  685. ; a,f,b,c,d,e,h,l
  686. midsq:    add    l;        hl := hl + a
  687.     mov    l,a;        max result fffh+0ffh=010feh
  688.     adc    h;        (normal, except special case)
  689.     sub    l
  690.     mov    d,a;        save for special test
  691.     ori    8;        or with 800h.  Max 18feh
  692. ; following should be 0fh, but modified to agree with original
  693.     ani    1fh;        mask to 13 bits. Max 1fffh
  694.     rar
  695.     mov    h,a;        max 7ffh
  696.     mov    b,a;        m := bc := hl := input DIV 2
  697.     mov    a,l;        using n*n = 4 * (m * m)     (n even)
  698.     rar;            or          4 * m * (m+1)+1 (n odd)
  699.     mov    l,a;        and any final "1" gets discarded.
  700.     mov    c,a
  701.     jnc    midsq1;        even, use m
  702.     inx    h;        hl := m+1
  703. ;    "    "
  704. ; special case test, input = 0ffffh+0 must hash to 800h
  705. ; from initial 1 byte string prefix = nopred, suffix = 0.
  706.     mov    a,d
  707.     ora    a;        did input have high bit?
  708.     mov    a,h;        holds 800h in this case
  709.     rar;            because using 13, not 12 bits
  710.     rm;            yes, return 0800h
  711. ;    "    "    
  712. ; Multiplication. hl := bc * hl (12 lo bits of hl only)
  713. midsq1:    mvi    a,12;        bits in m * m' multiplication
  714.     dad    h
  715.     dad    h;        reposition multiplier
  716.     dad    h
  717.     dad    h;        using 12, not 16 bit multiply
  718.     xchg;            multiplier to de
  719.     mvi    l,0;        clear necessary portion
  720. midsq2:    dad    h;        left shift accum. Main loop.
  721.     xchg;            discarding overflow past 16 bits
  722.     dad    h;        left shift multiplier
  723.     xchg
  724.     jnc    midsq3;        multiplier bit = 0
  725.     dad    b;        =1, add in
  726. midsq3:    dcr    a
  727.     jnz    midsq2;        more bits
  728.     dad    h;        reposition 12 bit result
  729.     ral
  730.     dad    h;        shift 4 bits to A
  731.     ral
  732.     dad    h
  733.     ral
  734.     dad    h
  735.     ral
  736.     mov    l,h;        move down low 8 bits of result
  737.     ani    0fh;        mask off. result in a & l
  738.     ret
  739. ;
  740. ; Get input token, variable width.  Check nops etc
  741. ; Carry for eof
  742. ; a,f,b,c,d,e
  743. getkn:    call    getok
  744.     mov    a,d
  745.     dcr    a
  746.     ana    a;        clear any carry
  747.     rnz;            code not 01xx
  748.     mov    a,e
  749.     cpi    (sprcod+1) and 0ffh;    codes used
  750.     rnc
  751.     cpi    nulcod and 0ffh
  752.     jnc    getkn;        ignore null and spare codes, nop
  753.     ret;            must be rstcod or eof, cy set
  754. ;
  755. ; Get input token, variable width
  756. ; a,f,b,c,d,e
  757. getok:    lxi    d,0
  758.     lda    width
  759.     mov    b,a
  760.     lda    lftovr
  761.     mov    c,a
  762. getok1:    mov    a,c
  763.     add    a;        bit to cy, flags on remainder
  764.     cz    morein;        lftovr was empty, get more
  765.     mov    c,a;        and keep the remainder
  766.     mov    a,e
  767.     ral
  768.     mov    e,a;        shift into de
  769.     mov    a,d
  770.     ral
  771.     mov    d,a
  772.     dcr    b
  773.     jnz    getok1;        more bits to unpack
  774.     mov    a,c
  775.     sta    lftovr;        save any remainder
  776.     mov    a,d
  777.     ora    e
  778.     rnz
  779.     stc;            carry for 0 value (eof)
  780.     ret
  781. ;
  782. ; subroutine for getok.  Next input byte positioned etc.
  783. morein:    call    inbyte
  784.     stc
  785.     ral;            bit to carry, set end marker
  786.     ret
  787. ;
  788. ; clear version 2 tables ??
  789. clrtbl:    lhld    @table;        4096 rows * 3 cols, main table
  790.     lxi    b,t1000
  791.     mvi    e,vacant
  792.     call    fill
  793.     lxi    b,t2000
  794.     call    fillz
  795.     lhld    xlatbl;        Physical to logical translation table
  796.     mvi    m,guard
  797.     inx    h
  798.     lxi    b,t2800;    1400h * 2 entries
  799.     mvi    e,vacant
  800.     jmp    fill
  801. ;
  802. ; Figure out what physical loc'n the cruncher put its entry at by
  803. ; reproducing the hashing process.  Insert the entry # into the
  804. ; corresponding physical location in xlatbl.
  805. tbladr:    mov    b,a
  806.     call    hash;        to hl
  807. tblad1:    mov    c,h
  808.     mov    a,m
  809.     cpi    vacant
  810.     jz    tblad2;        no entry, make it
  811.     call    rehash
  812.     jmp    tblad1
  813.  
  814. tblad2:    xchg
  815.     lhld    codes;        logical entry #
  816.     xchg
  817.     mov    m,d
  818.     mov    a,h;        right 1 for this table
  819.     adi    n14
  820.     mov    h,a
  821.     mov    m,e
  822.     lda    xlatbl+1
  823.     mov    h,a
  824.     mov    a,c
  825.     sub    h
  826.     mov    h,a
  827.     ret
  828. ;
  829. ; rehash
  830. rehash:    xchg
  831.     lhld    nextx;        displacement from hash
  832.     dad    d
  833.     lda    xlatbl+1;    page address
  834.     mov    d,a
  835.     mov    a,h
  836.     cmp    d
  837.     rnc
  838.     lxi    d,tblsize
  839.     dad    d
  840.     ret
  841. ;
  842. ; Check for code reassignment?
  843. recod:    mov    b,a
  844.     mvi    a,nff
  845.     sta    avail+1
  846.     mov    a,b
  847.     call    hash;        to hl
  848. recod1:    mov    c,h
  849.     mov    a,m
  850.     cpi    vacant
  851.     jz    recod4;        end chain. Try make entry (elsewhere)
  852.     lda    avail+1
  853.     cpi    nff
  854.     jnz    recod3;        have an entry
  855.     push    h;        physical table pointer
  856.     mov    d,m;        entry # (hi)
  857.     mov    a,h
  858.     adi    n14;        right 1
  859.     mov    h,a
  860.     mov    l,m;        entry # (lo)
  861.     lda    @table+1;    convert to addres
  862.     add    d
  863.     mov    h,a
  864.     mov    a,m
  865.     ani    020h
  866.     jnz    recod2;        not bumpable, try next
  867.     shld    avail;        save resulting entry # for later use
  868. recod2:    pop    h
  869. recod3:    call    rehash;        to next link in chain
  870.     jmp    recod1
  871.  
  872. recod4:    lhld    avail;        reassign the entry pointed to by avail
  873.     mov    a,h;         (if any), redefine "last pred entrd"
  874.     cpi    nff;         and "last suffix" vars.
  875.     rz;            none available
  876.     xchg
  877.     lhld    troom
  878.     inx    h
  879.     shld    troom;        keep track of codes re-assigned
  880.     lhld    lastpr
  881.     xchg
  882.     lda    char
  883.     mov    b,a
  884.     call    link
  885.     right    1
  886.     mov    m,b
  887. ;    "    "
  888. hash:    mov    e,l
  889.     dad    h
  890.     dad    h
  891.     dad    h
  892.     dad    h
  893.     xra    h
  894.     mov    l,a
  895.     mov    a,e
  896.     ani    n0f
  897.     mov    h,a
  898.     lda    xlatbl+1;    add in table offset
  899.     add    h
  900.     mov    h,a
  901.     inx    h;        eliminate 0 case
  902.     push    h
  903.     xchg
  904.     lhld    tbltop
  905.     dad    d;        make index dependant, not address
  906.     shld    nextx;        rehash value, -ve no.
  907.     pop    h
  908.     ret
  909. ;
  910. ; get next byte to a
  911. ; a,f
  912. inbyte:    push    b
  913.     push    d
  914.     push    h
  915.     call    getbyt
  916.     pop    h
  917.     pop    d
  918.     pop    b
  919.     ret
  920.  
  921. outbyt:    push    psw
  922.     push    b
  923.     push    d
  924.     push    h
  925.     call    out
  926.     pop    h
  927.     pop    d
  928.     pop    b
  929.     pop    psw
  930.     ret
  931. ;
  932. ; Initialize variables, pointers, limits
  933. init:    lhld    xlatbl;        hi byte is 0
  934.     lxi    d,-tblsize
  935.     mov    a,e
  936.     sub    l
  937.     mov    l,a
  938.     mov    a,d
  939.     sbb    h
  940.     mov    h,a
  941.     shld    tbltop;        -(xlatbl + tblsize)
  942.     lxi    h,itable
  943.     lxi    d,fulflg;    copy the "shadow"
  944.     lxi    b,itbsize
  945. ;    "    "
  946. ; Equivalent (almost) to ldir. de^ := hl^ for bc
  947. ; a,f,b,c,d,e,h,l
  948. move:    mov    a,m
  949.     stax    d
  950.     inx    h
  951.     inx    d
  952.     dcx    b
  953.     mov    a,b
  954.     ora    c
  955.     jnz    move
  956.     ret
  957.  
  958. ; initializing table ("shadow") for data area
  959. itable:    db    0
  960.     dw    nopred
  961.     db    1
  962.     dw    0
  963.     db    vacant
  964.     db    initw;    initial cell width
  965.     db    2
  966.     dw    0
  967. itbsize    equ    $-itable
  968. ;
  969.     dseg;        data area
  970. ;
  971. ; area is reinitialized on each call
  972. fulflg:    ds    1;    "ff" when table is full
  973. lastpr:    ds    2;    last pred
  974. entflg:    ds    1;    "already entered in table" flag
  975. codes:    ds    2;    current code?
  976. lftovr:    ds    1;    previous input bits still unused
  977. width:    ds    1;    current encoded cell width
  978. trgmsk:    ds    1
  979. outflg:    ds    1;    last output char
  980.     ds    1;    repeat flag for output (at outflg+1). 0 or 1
  981. ;
  982. spsave:    ds    2
  983. kind    ds    1;    version to be decoded. 0..1 for 10/20 resp.
  984. char:    ds    1;    last char of the previously decoded string
  985. avail:    ds    2;    hi byte is reassigning code flag when 0ffh
  986. ffflag:    ds    1;    force entries to be marked referenced
  987. nextx:    ds    2;    next hashed index ??
  988. @table:    ds    2;    base of decoding table
  989. xlatbl:    ds    2;    translation table, hash to entry
  990. tbltop:    ds    2;    negated address
  991. troom:    ds    2;    space left in table
  992. stklim:    ds    2;    Stack usage limit
  993. endu    equ    $;    available storage above here
  994.     end
  995. ≥I