howtophil

LLM-Generated Epub Reader In ASM (DOS 8088/8086)

Sep 30th, 2026 (edited)
169
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
ASM (NASM) 72.22 KB | Source Code | 0 0
  1. ; ============================================================================
  2. ; epub2txt -- an EPUB reader for 8088/8086 DOS
  3. ; ============================================================================
  4. ;
  5. ; PUBLIC DOMAIN BECAUSE GENERATED CODE CANNOT
  6. ; BE COPYRIGHTED OR COPYLEFTED
  7. ;
  8. ; I tried a second LLM to get the AI-Bros off my back.
  9. ; It is not worth the rivers and the sky.
  10. ;
  11. ; An interactive EPUB reader written entirely in 8086 assembly (NASM
  12. ; syntax), targeting real-mode DOS on original 8088 hardware. It parses
  13. ; the ZIP container, decompresses DEFLATE streams, strips HTML, folds
  14. ; UTF-8 to ASCII, word-wraps, and presents the book through a more-style
  15. ; pager with chapter navigation and resume bookmarks.
  16. ; Tested under dosemu and DOSBox.
  17. ;
  18. ; Final codebase: September 30, 2026
  19. ;
  20. ; HOW THIS WAS MADE
  21. ;
  22. ; This program was generated through an extended interactive session with
  23. ; Kimi (kimi.ai), an AI coding assistant by Moonshot AI, working from the
  24. ; specification "epub to text in 8088 assembler" -- which grew, through
  25. ; use, into a full-screen reader.
  26. ;
  27. ; The development was iterative and test-driven in an unusual way:
  28. ;
  29. ; 1. Algorithm-first verification. Before any machine-code debugging, the
  30. ;    assembly logic (bit reader, Huffman table construction, inflate state
  31. ;    machine, NCX parser) was ported 1:1 to Python and run against real
  32. ;    deflate streams and captured EPUB bytes. This caught design bugs
  33. ;    (illegal addressing modes, register clobbering across int 21h) that
  34. ;    reading alone missed.
  35. ;
  36. ; 2. A hardware-specific bug. The target DOS clobbers DX across int 21h
  37. ;    calls (standard DOS preserves it). Every early build kept a live value
  38. ;    in DX across a file read inside the bit reader, so the very first
  39. ;    getbits call -- and every 2048-byte buffer refill -- silently
  40. ;    corrupted the decode. The fix: all file-I/O wrappers (next_byte,
  41. ;    seek, flush_out) push/pop every register, so no register assumption
  42. ;    is ever made across a DOS call.
  43. ;
  44. ; 3. A console-specific bug. Output originally used Unix-style LF-only
  45. ;    line endings. dosemu's console forgives that (LF implies column 1),
  46. ;    but DOSBox -- like real DOS CON -- keeps the column on LF, producing
  47. ;    a staircase of "extra spaces." The fix routes every output byte
  48. ;    through one routine that expands LF to CRLF.
  49. ;
  50. ; 4. Gradual feature growth. What began as "extract text" grew through
  51. ;    user feedback into: UTF-8 folding, a TOC parsed from toc.ncx with
  52. ;    real chapter titles, anchor-scoped chapter extraction, word wrap at
  53. ;    a configurable width, a paged reader with SPACE/B/Q and
  54. ;    cross-section navigation, screen clearing, and .bkm resume
  55. ;    bookmarks.
  56. ;
  57. ; Notable simplifications (by design, on an 8088): no quoted-attribute
  58. ; tracking in tags, no ZIP64, navPoint-granular TOC capped at 32 entries /
  59. ; 31-char anchors, and single-byte ASCII folds (ss->s rather than ss).
  60. ;
  61. ; ----------------------------------------------------------------------------
  62. ; REQUIREMENTS
  63. ;
  64. ;   - NASM 2.x (developed with NASM 2.11.08; any 2.x should work)
  65. ;   - MS-DOS / PC-DOS / FreeDOS 2.0 or later -- real hardware, DOSBox,
  66. ;     dosemu, or another emulator
  67. ;   - ~50 KB free conventional memory for the .COM
  68. ;
  69. ; COMPILE INSTRUCTIONS
  70. ;
  71. ;   nasm -f bin epub2txt.asm -o epub2txt.com
  72. ;
  73. ; That's it -- flat binary, no linker. The source enforces "cpu 8086", so
  74. ; NASM will reject any instruction the 8088 can't execute.
  75. ;
  76. ; ----------------------------------------------------------------------------
  77. ; USAGE
  78. ;
  79. ;   epub2txt <file.epub> [-w W] [-p L] [-l | N]
  80. ;
  81. ;     -w W   word-wrap width, clamped 20..200   (default 78)
  82. ;     -p L   lines per page, clamped 5..60      (default 24)
  83. ;     -l     list all chapters (titles from toc.ncx)
  84. ;     N      display chapter N only
  85. ;
  86. ; Options may be combined in any order after the filename.
  87. ;
  88. ; Examples:
  89. ;
  90. ;   REM Read a book interactively:
  91. ;   epub2txt dickens.epub
  92. ;
  93. ;   REM List the chapters:
  94. ;   epub2txt dickens.epub -l
  95. ;
  96. ;   REM Read just chapter 3:
  97. ;   epub2txt dickens.epub 3
  98. ;
  99. ;   REM Narrow wrap, short pages, chapter 5:
  100. ;   epub2txt dickens.epub -w 60 -p 20 5
  101. ;
  102. ;   REM Resume where you left off (see bookmarks below):
  103. ;   epub2txt dickens.epub
  104. ;
  105. ; READER KEYS
  106. ;
  107. ;   SPACE  next page
  108. ;   B      previous page; at a section's start, jump to the bottom of the
  109. ;          previous section/chapter
  110. ;   Q      quit (saves bookmark)
  111. ;
  112. ; Every page is displayed on a freshly cleared screen; the "-- more --"
  113. ; prompt erases itself before each new page.
  114. ;
  115. ; BOOKMARKS
  116. ;
  117. ; While reading, Q writes your position (chapter and page) to a file named
  118. ; after the book -- dickens.epub -> dickens.bkm. Running
  119. ; "epub2txt dickens.epub" with no other arguments resumes from that spot.
  120. ; Reading to the end of the book deletes the bookmark, so the next bare
  121. ; run starts fresh. The resume page is re-clamped after re-extraction, so
  122. ; changing -w/-p between sessions is harmless. -l never touches bookmarks.
  123. ;
  124. ; ----------------------------------------------------------------------------
  125. ; HOW IT WORKS (INTERNALS)
  126. ;
  127. ; - Container: ZIP central-directory walk; the End-Of-Central-Directory
  128. ;   record is validated by checking that record + comment length lands
  129. ;   exactly at EOF (rejecting false PK\05\06 signatures inside file data).
  130. ; - Decompression: full DEFLATE -- stored, fixed-Huffman, and
  131. ;   dynamic-Huffman blocks -- with a byte-at-a-time LSB-first bit reader
  132. ;   and a 32 KB sliding window with wraparound.
  133. ; - TOC: toc.ncx is inflated through a small XML state machine that pairs
  134. ;   each <text> title with the following src="..."; anchors after '#' are
  135. ;   kept for chapter scoping.
  136. ; - Chapter extraction: output is suppressed until id="anchor" (or
  137. ;   name="anchor") is seen in the raw byte stream, and stops at the next
  138. ;   navPoint's anchor in the same file.
  139. ; - Output pipeline: HTML tags stripped (block tags become newlines,
  140. ;   script/style bodies dropped, entities resolved), UTF-8 folded to ASCII
  141. ;   via a 256-entry table (Latin-1 + Latin Extended-A + punctuation),
  142. ;   greedy word wrap that never splits words, and CRLF line endings --
  143. ;   emitted at a single choke point so every console (dosemu, DOSBox,
  144. ;   real DOS) renders pages identically.
  145. ; - Pagination: wrapped output is buffered to a temp file (EPUB2TX.$$$,
  146. ;   deleted on exit) while a page-offset index is built; the reader seeks
  147. ;   through that index to display pages and to jump between sections.
  148. ; - Portability hardening: every int 21h wrapper preserves all registers,
  149. ;   because the development target's DOS clobbers DX -- never trust
  150. ;   register preservation across DOS calls on unknown systems.
  151. ;
  152. ; LIMITATIONS
  153. ;
  154. ; - Chapters beyond the 32nd navPoint are not listed; anchors longer than
  155. ;   31 chars are truncated.
  156. ; - Characters with no ASCII equivalent become '?'.
  157. ; - The NCX src matching is case-insensitive suffix matching; a book whose
  158. ;   NCX references files that don't exist in the archive falls back to
  159. ;   archive-order behavior.
  160. ; - 1024 pages max in the page index (~24k lines of wrapped text).
  161. ;
  162. ; CREDITS
  163. ;
  164. ; Written by Kimi (kimi.ai, Moonshot AI) in collaboration with the user,
  165. ; over the course of a single long debugging session on 8088 hardware --
  166. ; including three rounds of Python-level algorithm verification, one very
  167. ; stubborn register-clobbering DOS, and one console that liked Unix
  168. ; newlines more than DOS does.
  169. ; ============================================================================
  170. ;
  171. ; epub2txt.asm -- interactive EPUB reader, 8088/8086 real mode, DOS .COM
  172. ; FINAL BUILD -- HTML stripping, UTF-8->ASCII folding, navPoint TOC/selection
  173. ;               from toc.ncx, word wrap (-w), paged reading (-p) with
  174. ;               SPACE/B/Q, cross-section navigation, clear-screen paging,
  175. ;               and .bkm resume bookmarks.
  176. ; Build:  nasm -f bin epub2txt.asm -o epub2txt.com
  177. ; Run:    epub2txt book.epub [-w W] [-p L] [-l | N]
  178. ;           -w W   wrap width   (20..200, default 78)
  179. ;           -p L   lines/page   (5..60, default 24)
  180. ;           -l     list chapters
  181. ;           N      display chapter N
  182. ;         bare filename resumes from book.bkm if present (Q saves, end deletes)
  183.  
  184. cpu 8086
  185. bits 16
  186. org 0x100
  187.  
  188. INBUF_SIZE  equ 2048
  189. OUTBUF_SIZE equ 512
  190.  
  191. ;---------------------------------------------------------------- anchor matcher
  192. %macro ANCH 3
  193.     mov     bl, [%1]
  194.     cmp     bl, 4
  195.     je      %%q
  196.     cmp     bl, 0
  197.     je      %%s0
  198.     cmp     bl, 1
  199.     je      %%s1
  200.     cmp     bl, 2
  201.     je      %%s2
  202.     cmp     bl, 3
  203.     je      %%s3
  204.     cmp     bl, 10
  205.     je      %%s10
  206.     cmp     bl, 11
  207.     je      %%s11
  208.     cmp     bl, 12
  209.     je      %%s12
  210.     mov     byte [%1], 0
  211.     clc
  212.     jmp     %%done
  213. %%s0:
  214.     cmp     al, 'i'
  215.     je      %%t1
  216.     cmp     al, 'n'
  217.     je      %%t10
  218.     clc
  219.     jmp     %%done
  220. %%t1:
  221.     mov     byte [%1], 1
  222.     clc
  223.     jmp     %%done
  224. %%t10:
  225.     mov     byte [%1], 10
  226.     clc
  227.     jmp     %%done
  228. %%s1:
  229.     cmp     al, 'd'
  230.     je      %%t2
  231.     cmp     al, 'n'
  232.     je      %%t10
  233.     mov     byte [%1], 0
  234.     clc
  235.     jmp     %%done
  236. %%t2:
  237.     mov     byte [%1], 2
  238.     clc
  239.     jmp     %%done
  240. %%s10:
  241.     cmp     al, 'a'
  242.     je      %%t11
  243.     mov     byte [%1], 0
  244.     clc
  245.     jmp     %%done
  246. %%t11:
  247.     mov     byte [%1], 11
  248.     clc
  249.     jmp     %%done
  250. %%s11:
  251.     cmp     al, 'm'
  252.     je      %%t12
  253.     mov     byte [%1], 0
  254.     clc
  255.     jmp     %%done
  256. %%t12:
  257.     mov     byte [%1], 12
  258.     clc
  259.     jmp     %%done
  260. %%s12:
  261.     cmp     al, 'e'
  262.     je      %%t3
  263.     mov     byte [%1], 0
  264.     clc
  265.     jmp     %%done
  266. %%s2:
  267.     cmp     al, '='
  268.     je      %%t3
  269.     mov     byte [%1], 0
  270.     clc
  271.     jmp     %%done
  272. %%t3:
  273.     mov     byte [%1], 3
  274.     clc
  275.     jmp     %%done
  276. %%s3:
  277.     cmp     al, 34
  278.     je      %%t4
  279.     cmp     al, 39
  280.     je      %%t4
  281.     mov     byte [%1], 0
  282.     clc
  283.     jmp     %%done
  284. %%t4:
  285.     mov     [%2], al
  286.     mov     byte [%3], 0
  287.     mov     byte [%1], 4
  288.     clc
  289.     jmp     %%done
  290. %%q:
  291.     mov     bl, [%3]
  292.     mov     bh, 0
  293.     push    si
  294.     add     si, bx
  295.     mov     dl, [si]
  296.     pop     si
  297.     test    dl, dl
  298.     jz      %%atend
  299.     cmp     al, dl
  300.     jne     %%reset
  301.     inc     byte [%3]
  302.     clc
  303.     jmp     %%done
  304. %%atend:
  305.     cmp     al, [%2]
  306.     jne     %%reset
  307.     mov     byte [%1], 0
  308.     stc
  309.     jmp     %%done
  310. %%reset:
  311.     mov     byte [%1], 0
  312.     clc
  313. %%done:
  314. %endmacro
  315.  
  316. ;---------------------------------------------------------------- entry point
  317. start:
  318.     mov     si, 0x81
  319.     call    skip_sp
  320.     mov     di, iname
  321.     call    copy_tok
  322.     cmp     byte [iname], 0
  323.     je      .usage
  324.     mov     word [wrapw], 78
  325.     mov     word [pagel], 24
  326. .argloop:
  327.     call    skip_sp
  328.     cmp     byte [si], 0x0D
  329.     je      .noopt
  330.     mov     al, [si]
  331.     cmp     al, '-'
  332.     jne     .trynum
  333.     inc     si
  334.     mov     al, [si]
  335.     cmp     al, 'w'
  336.     je      .optw
  337.     cmp     al, 'p'
  338.     je      .optp
  339.     cmp     al, 'l'
  340.     je      .optl
  341.     jmp     .badarg
  342. .optw:
  343.     inc     si
  344.     call    parse_num
  345.     jc      .badarg
  346.     cmp     ax, 20
  347.     jae     .w1
  348.     mov     ax, 20
  349. .w1:
  350.     cmp     ax, 200
  351.     jbe     .w2
  352.     mov     ax, 200
  353. .w2:
  354.     mov     [wrapw], ax
  355.     jmp     .argloop
  356. .optp:
  357.     inc     si
  358.     call    parse_num
  359.     jc      .badarg
  360.     cmp     ax, 5
  361.     jae     .p1
  362.     mov     ax, 5
  363. .p1:
  364.     cmp     ax, 60
  365.     jbe     .p2
  366.     mov     ax, 60
  367. .p2:
  368.     mov     [pagel], ax
  369.     jmp     .argloop
  370. .optl:
  371.     inc     si
  372.     mov     byte [mode], 1
  373.     jmp     .argloop
  374. .trynum:
  375.     cmp     al, '0'
  376.     jb      .badarg
  377.     cmp     al, '9'
  378.     ja      .badarg
  379.     call    parse_num
  380.     jc      .badarg
  381.     mov     [chapnum], ax
  382.     mov     byte [mode], 2
  383.     jmp     .argloop
  384. .noopt:
  385.     mov     ax, 0x3D00
  386.     mov     dx, iname
  387.     int     0x21
  388.     jc      .usage
  389.     mov     [fh], ax
  390.     call    find_eocd
  391.     jc      .badzip
  392.     mov     word [oh], 1
  393.     call    make_bkmname
  394.     call    load_toc
  395.     call    load_bookmark
  396.     call    setup_paging
  397.     cmp     byte [mode], 1
  398.     jne     .not_list
  399.     call    list_navpoints
  400.     jmp     .close
  401. .not_list:
  402.     cmp     byte [mode], 2
  403.     jne     .go
  404.     mov     ax, [chapnum]
  405.     test    ax, ax
  406.     jz      .nosec
  407.     cmp     ax, [tcount]
  408.     ja      .nosec
  409.     dec     ax
  410.     mov     [seli], ax
  411.     call    sel_chapter
  412. .go:
  413.     mov     ax, [eocd_cdoff_lo]
  414.     mov     [cdpos_lo], ax
  415.     mov     ax, [eocd_cdoff_hi]
  416.     mov     [cdpos_hi], ax
  417.     mov     ax, [eocd_entries]
  418.     mov     [nentries], ax
  419. .entry_loop:
  420.     cmp     word [nentries], 0
  421.     je      .alldone
  422.     dec     word [nentries]
  423.     call    process_entry
  424.     jnc     .entry_loop
  425. .alldone:
  426.     call    wrap_finish
  427.     call    flush_out
  428. .close:
  429.     cmp     byte [mode], 2
  430.     jne     .c0
  431.     cmp     byte [chapfound], 0
  432.     jne     .c0
  433.     mov     dx, msg_nosec
  434.     mov     ah, 9
  435.     int     0x21
  436. .c0:
  437.     call    cleanup
  438.     mov     ax, 0x4C00
  439.     int     0x21
  440. .nosec:
  441.     mov     dx, msg_nosec
  442.     mov     ah, 9
  443.     int     0x21
  444.     call    cleanup
  445.     mov     ax, 0x4C01
  446.     int     0x21
  447. .usage:
  448.     mov     dx, msg_usage
  449.     mov     ah, 9
  450.     int     0x21
  451.     mov     ax, 0x4C01
  452.     int     0x21
  453. .badarg:
  454.     mov     dx, msg_usage
  455.     mov     ah, 9
  456.     int     0x21
  457.     mov     ax, 0x4C01
  458.     int     0x21
  459. .badzip:
  460.     mov     dx, msg_bad
  461.     mov     ah, 9
  462.     int     0x21
  463.     mov     ax, 0x4C01
  464.     int     0x21
  465.  
  466. ;---------------------------------------------------------------- bookmarks
  467. make_bkmname:               ; iname -> bkmname (strip last ext, append .bkm)
  468.     xor     bx, bx
  469.     xor     si, si
  470. .scan:
  471.     mov     al, [iname + si]
  472.     test    al, al
  473.     jz      .got
  474.     cmp     al, '.'
  475.     jne     .n
  476.     mov     bx, si
  477. .n:
  478.     inc     si
  479.     jmp     .scan
  480. .got:
  481.     test    bx, bx
  482.     jnz     .cp
  483.     mov     bx, si
  484. .cp:
  485.     xor     si, si
  486. .cpl:
  487.     cmp     si, bx
  488.     jae     .app
  489.     mov     al, [iname + si]
  490.     mov     [bkmname + si], al
  491.     inc     si
  492.     jmp     .cpl
  493. .app:
  494.     mov     byte [bkmname + bx], '.'
  495.     mov     byte [bkmname + bx + 1], 'b'
  496.     mov     byte [bkmname + bx + 2], 'k'
  497.     mov     byte [bkmname + bx + 3], 'm'
  498.     mov     byte [bkmname + bx + 4], 0
  499.     ret
  500.  
  501. load_bookmark:              ; bare-filename resume from .bkm
  502.     cmp     byte [mode], 0
  503.     jne     .done
  504.     mov     ax, 0x3D00
  505.     mov     dx, bkmname
  506.     int     0x21
  507.     jc      .done
  508.     mov     bx, ax
  509.     mov     ah, 0x3F
  510.     mov     cx, 16
  511.     mov     dx, bkmline
  512.     int     0x21
  513.     mov     si, ax              ; bytes read
  514.     mov     ah, 0x3E
  515.     int     0x21
  516.     test    si, si
  517.     jz      .done
  518.     mov     byte [bkmline + si], 0
  519.     mov     si, bkmline
  520.     call    parse_num           ; K (0 = whole book)
  521.     jc      .done
  522.     push    ax
  523.     call    parse_num           ; P (page)
  524.     jc      .bdone
  525.     mov     [respage], ax
  526.     pop     ax
  527.     test    ax, ax
  528.     jz      .done
  529.     cmp     ax, [tcount]
  530.     ja      .done
  531.     mov     [chapnum], ax
  532.     mov     byte [mode], 2
  533.     jmp     .done
  534. .bdone:
  535.     pop     ax
  536. .done:
  537.     ret
  538.  
  539. save_bookmark:              ; write "K P\r\n" (K = chapter, 0 = whole book)
  540.     cmp     byte [mode], 1
  541.     je      .done
  542.     mov     di, bkmline
  543.     xor     ax, ax
  544.     cmp     byte [mode], 2
  545.     jne     .k0
  546.     mov     ax, [seli]
  547.     inc     ax
  548. .k0:
  549.     call    fmt_dec_di
  550.     mov     byte [di], ' '
  551.     inc     di
  552.     mov     ax, [page]
  553.     call    fmt_dec_di
  554.     mov     byte [di], 13
  555.     inc     di
  556.     mov     byte [di], 10
  557.     inc     di
  558.     mov     ah, 0x3C
  559.     xor     cx, cx
  560.     mov     dx, bkmname
  561.     int     0x21
  562.     jc      .done
  563.     mov     bx, ax
  564.     mov     cx, di
  565.     sub     cx, bkmline
  566.     mov     dx, bkmline
  567.     mov     ah, 0x40
  568.     int     0x21
  569.     mov     ah, 0x3E
  570.     int     0x21
  571. .done:
  572.     ret
  573.  
  574. delete_bookmark:
  575.     mov     ah, 0x41
  576.     mov     dx, bkmname
  577.     int     0x21
  578.     ret
  579.  
  580. fmt_dec_di:                 ; append decimal of ax at [di], advance di
  581.     mov     bx, scratch + 8
  582.     mov     cx, 10
  583. .d:
  584.     xor     dx, dx
  585.     div     cx
  586.     add     dl, '0'
  587.     dec     bx
  588.     mov     [bx], dl
  589.     test    ax, ax
  590.     jnz     .d
  591. .cp:
  592.     cmp     bx, scratch + 8
  593.     jae     .r
  594.     mov     al, [bx]
  595.     mov     [di], al
  596.     inc     bx
  597.     inc     di
  598.     jmp     .cp
  599. .r:
  600.     ret
  601.  
  602. ;---------------------------------------------------------------- pagination
  603. setup_paging:
  604.     mov     word [paging], 0
  605.     mov     ax, 0x4400
  606.     mov     bx, 1
  607.     int     0x21
  608.     jc      .done
  609.     test    ax, 0x8000
  610.     jz      .done
  611.     mov     ah, 0x3C
  612.     xor     cx, cx
  613.     mov     dx, tmpname
  614.     int     0x21
  615.     jc      .done
  616.     mov     [th], ax
  617.     mov     [oh], ax
  618.     mov     byte [paging], 1
  619.     mov     word [tmppos_lo], 0
  620.     mov     word [tmppos_hi], 0
  621.     mov     word [wlines], 0
  622.     mov     word [pgused], 0
  623.     mov     word [seccnt], 0
  624. .done:
  625.     ret
  626.  
  627. cleanup:
  628.     cmp     byte [paging], 0
  629.     je      .c2
  630.     call    init_pages
  631.     call    pager
  632.     mov     ah, 0x3E
  633.     mov     bx, [th]
  634.     int     0x21
  635.     mov     ah, 0x41
  636.     mov     dx, tmpname
  637.     int     0x21
  638. .c2:
  639.     mov     ah, 0x3E
  640.     mov     bx, [fh]
  641.     int     0x21
  642.     ret
  643.  
  644. init_pages:
  645.     mov     ax, [pgused]
  646.     test    ax, ax
  647.     jz      .set
  648.     dec     ax
  649.     shl     ax, 1
  650.     shl     ax, 1
  651.     mov     si, pgidx
  652.     add     si, ax
  653.     mov     dx, [si]
  654.     mov     cx, [si + 2]
  655.     cmp     cx, [tmppos_hi]
  656.     jne     .set
  657.     cmp     dx, [tmppos_lo]
  658.     jne     .set
  659.     dec     word [pgused]
  660. .set:
  661.     mov     ax, [pgused]
  662.     inc     ax
  663.     mov     [pages], ax
  664.     ret
  665.  
  666. page_off_dxd:               ; ax = page -> dx = off lo, cx = off hi
  667.     test    ax, ax
  668.     jz      .zero
  669.     dec     ax
  670.     shl     ax, 1
  671.     shl     ax, 1
  672.     mov     si, pgidx
  673.     add     si, ax
  674.     mov     dx, [si]
  675.     mov     cx, [si + 2]
  676.     ret
  677. .zero:
  678.     xor     dx, dx
  679.     xor     cx, cx
  680.     ret
  681.  
  682. sec_of_page:                ; [cursec] = section containing current page
  683.     mov     ax, [page]
  684.     call    page_off_dxd
  685.     mov     word [cursec], 0
  686.     xor     bx, bx
  687. .sl:
  688.     cmp     bx, [seccnt]
  689.     jae     .done
  690.     mov     ax, bx
  691.     shl     ax, 1
  692.     shl     ax, 1
  693.     mov     si, secidx
  694.     add     si, ax
  695.     mov     ax, [si]
  696.     push    word [si + 2]
  697.     pop     di
  698.     cmp     di, cx
  699.     jb      .take
  700.     ja      .next
  701.     cmp     ax, dx
  702.     ja      .next
  703. .take:
  704.     mov     [cursec], bx
  705. .next:
  706.     inc     bx
  707.     jmp     .sl
  708. .done:
  709.     ret
  710.  
  711. page_of_secstart:           ; ax = section index -> ax = page containing it
  712.     shl     ax, 1
  713.     shl     ax, 1
  714.     mov     si, secidx
  715.     add     si, ax
  716.     mov     dx, [si]
  717.     mov     cx, [si + 2]
  718.     xor     bx, bx
  719.     xor     di, di
  720. .ct:
  721.     cmp     bx, [pgused]
  722.     jae     .done
  723.     mov     ax, bx
  724.     shl     ax, 1
  725.     shl     ax, 1
  726.     mov     si, pgidx
  727.     add     si, ax
  728.     mov     ax, [si]
  729.     mov     si, [si + 2]
  730.     cmp     si, cx
  731.     jb      .take
  732.     ja      .skip
  733.     cmp     ax, dx
  734.     ja      .skip
  735. .take:
  736.     inc     di
  737. .skip:
  738.     inc     bx
  739.     jmp     .ct
  740. .done:
  741.     mov     ax, di
  742.     ret
  743.  
  744. sel_chapter:                ; [seli] (0-based) -> p_fsrc/p_sanc/p_panc
  745.     mov     ax, [seli]
  746.     mov     bx, 120
  747.     mul     bx
  748.     mov     bx, tsrc
  749.     add     bx, ax
  750.     mov     [p_fsrc], bx
  751.     mov     ax, [seli]
  752.     shl     ax, 1
  753.     shl     ax, 1
  754.     shl     ax, 1
  755.     shl     ax, 1
  756.     shl     ax, 1
  757.     mov     bx, tanc
  758.     add     bx, ax
  759.     mov     [p_sanc], bx
  760.     mov     word [p_panc], 0
  761.     mov     cx, [seli]
  762.     inc     cx
  763. .fs:
  764.     cmp     cx, [tcount]
  765.     jae     .fd
  766.     mov     ax, cx
  767.     mov     bx, 120
  768.     mul     bx
  769.     mov     si, tsrc
  770.     add     si, ax
  771.     mov     di, [p_fsrc]
  772.     call    strcmp_t
  773.     jc      .fy
  774.     inc     cx
  775.     jmp     .fs
  776. .fy:
  777.     mov     ax, cx
  778.     shl     ax, 1
  779.     shl     ax, 1
  780.     shl     ax, 1
  781.     shl     ax, 1
  782.     shl     ax, 1
  783.     mov     bx, tanc
  784.     add     bx, ax
  785.     mov     [p_panc], bx
  786. .fd:
  787.     ret
  788.  
  789. reextract:                  ; mode 2: extract chapter [seli] into fresh temp
  790.     mov     ah, 0x3E
  791.     mov     bx, [th]
  792.     int     0x21
  793.     mov     ah, 0x41
  794.     mov     dx, tmpname
  795.     int     0x21
  796.     mov     ah, 0x3C
  797.     xor     cx, cx
  798.     mov     dx, tmpname
  799.     int     0x21
  800.     mov     [th], ax
  801.     mov     [oh], ax
  802.     mov     word [tmppos_lo], 0
  803.     mov     word [tmppos_hi], 0
  804.     mov     word [wlines], 0
  805.     mov     word [pgused], 0
  806.     mov     word [seccnt], 0
  807.     call    sel_chapter
  808.     mov     byte [chapfound], 0
  809.     mov     ax, [eocd_cdoff_lo]
  810.     mov     [cdpos_lo], ax
  811.     mov     ax, [eocd_cdoff_hi]
  812.     mov     [cdpos_hi], ax
  813.     mov     ax, [eocd_entries]
  814.     mov     [nentries], ax
  815. .rx:
  816.     cmp     word [nentries], 0
  817.     je      .rdone
  818.     dec     word [nentries]
  819.     call    process_entry
  820.     jnc     .rx
  821. .rdone:
  822.     call    wrap_finish
  823.     call    flush_out
  824.     call    init_pages
  825.     ret
  826.  
  827. clear_screen:
  828.     push    ax
  829.     push    bx
  830.     push    cx
  831.     push    dx
  832.     mov     ax, 0x0600
  833.     mov     cx, 0x0000
  834.     mov     dx, 0x184F
  835.     mov     bh, 0x07
  836.     int     0x10
  837.     mov     ah, 0x02
  838.     mov     bh, 0
  839.     mov     dx, 0x0000
  840.     int     0x10
  841.     pop     dx
  842.     pop     cx
  843.     pop     bx
  844.     pop     ax
  845.     ret
  846.  
  847. display_page:
  848.     call    clear_screen
  849.     mov     ax, [page]
  850.     call    page_off_dxd
  851.     mov     bx, [th]
  852.     mov     ah, 0x42
  853.     xor     al, al
  854.     int     0x21
  855.     mov     word [linecnt], 0
  856. .rd:
  857.     mov     ah, 0x3F
  858.     mov     bx, [th]
  859.     mov     cx, 128
  860.     mov     dx, pbuf
  861.     int     0x21
  862.     test    ax, ax
  863.     jz      .done
  864.     mov     [rdlen], ax
  865.     xor     si, si
  866. .sc:
  867.     cmp     si, [rdlen]
  868.     jae     .wrfull
  869.     cmp     byte [pbuf + si], 10
  870.     jne     .nf
  871.     inc     word [linecnt]
  872.     mov     bx, [pagel]
  873.     cmp     [linecnt], bx
  874.     jae     .upto
  875. .nf:
  876.     inc     si
  877.     jmp     .sc
  878. .upto:
  879.     inc     si
  880.     mov     cx, si
  881.     jmp     .wr
  882. .wrfull:
  883.     mov     cx, [rdlen]
  884. .wr:
  885.     mov     ah, 0x40
  886.     mov     bx, 1
  887.     mov     dx, pbuf
  888.     int     0x21
  889.     mov     bx, [pagel]
  890.     cmp     [linecnt], bx
  891.     jb      .rd
  892. .done:
  893.     ret
  894.  
  895. pager:
  896.     mov     ax, [respage]
  897.     cmp     ax, [pages]
  898.     jb      .pset
  899.     mov     ax, [pages]
  900.     dec     ax
  901. .pset:
  902.     mov     [page], ax
  903. .ploop:
  904.     call    display_page
  905. .pr:
  906.     call    erase_prompt
  907.     mov     dx, p_msg
  908.     mov     ah, 9
  909.     int     0x21
  910.     mov     ah, 7
  911.     int     0x21
  912.     cmp     al, 'q'
  913.     je      .kquit
  914.     cmp     al, 'Q'
  915.     je      .kquit
  916.     cmp     al, 'b'
  917.     je      .kback
  918.     cmp     al, 'B'
  919.     je      .kback
  920.     cmp     al, ' '
  921.     je      .knext
  922.     jmp     .pr
  923. .kquit:
  924.     call    erase_prompt
  925.     call    save_bookmark
  926.     jmp     .fin
  927. .kback:
  928.     call    erase_prompt
  929.     jmp     .back
  930. .knext:
  931.     call    erase_prompt
  932.     jmp     .next
  933. .back:
  934.     cmp     word [page], 0
  935.     jne     .pgback
  936.     cmp     byte [mode], 2
  937.     je      .secback2
  938.     cmp     byte [mode], 0
  939.     jne     .ploop
  940.     call    sec_of_page
  941.     mov     ax, [cursec]
  942.     test    ax, ax
  943.     jz      .ploop
  944.     call    page_of_secstart
  945.     dec     ax
  946.     mov     [page], ax
  947.     jmp     .ploop
  948. .pgback:
  949.     dec     word [page]
  950.     jmp     .ploop
  951. .secback2:
  952.     mov     ax, [seli]
  953.     test    ax, ax
  954.     jz      .ploop
  955.     dec     word [seli]
  956.     call    reextract
  957.     mov     ax, [pages]
  958.     dec     ax
  959.     mov     [page], ax
  960.     jmp     .ploop
  961. .next:
  962.     mov     ax, [page]
  963.     inc     ax
  964.     cmp     ax, [pages]
  965.     jae     .secnext
  966.     mov     [page], ax
  967.     jmp     .ploop
  968. .secnext:
  969.     cmp     byte [mode], 2
  970.     je      .secnext2
  971.     cmp     byte [mode], 0
  972.     jne     .endreached
  973.     call    sec_of_page
  974.     mov     ax, [cursec]
  975.     inc     ax
  976.     cmp     ax, [seccnt]
  977.     jae     .endreached
  978.     call    page_of_secstart
  979.     mov     [page], ax
  980.     jmp     .ploop
  981. .secnext2:
  982.     mov     ax, [seli]
  983.     inc     ax
  984.     cmp     ax, [tcount]
  985.     jae     .endreached
  986.     mov     [seli], ax
  987.     call    reextract
  988.     mov     word [page], 0
  989.     jmp     .ploop
  990. .endreached:
  991.     cmp     byte [mode], 1
  992.     je      .fin
  993.     call    delete_bookmark
  994.     jmp     .fin
  995. .fin:
  996.     ret
  997.  
  998. erase_prompt:
  999.     mov     dx, p_erase
  1000.     mov     cx, 15
  1001.     mov     ah, 0x40
  1002.     mov     bx, 1
  1003.     int     0x21
  1004.     ret
  1005.  
  1006. ;---------------------------------------------------------------- args
  1007. skip_sp:
  1008. .l: mov     al, [si]
  1009.     cmp     al, 0x20
  1010.     je      .i
  1011.     cmp     al, 0x09
  1012.     je      .i
  1013.     ret
  1014. .i: inc     si
  1015.     jmp     .l
  1016.  
  1017. copy_tok:
  1018.     xor     dl, dl
  1019. .l: mov     al, [si]
  1020.     cmp     al, 0x20
  1021.     je      .d
  1022.     cmp     al, 0x0D
  1023.     je      .d
  1024.     cmp     dl, 63
  1025.     jae     .d
  1026.     mov     [di], al
  1027.     inc     di
  1028.     inc     dl
  1029.     inc     si
  1030.     jmp     .l
  1031. .d: mov     byte [di], 0
  1032.     ret
  1033.  
  1034. parse_num:                  ; skip spaces, parse decimal -> ax; CF=1 if none
  1035.     call    skip_sp
  1036.     xor     ax, ax
  1037.     xor     cx, cx
  1038. .l:
  1039.     mov     bl, [si]
  1040.     cmp     bl, '0'
  1041.     jb      .done
  1042.     cmp     bl, '9'
  1043.     ja      .done
  1044.     sub     bl, '0'
  1045.     xor     bh, bh
  1046.     mov     dx, ax
  1047.     shl     ax, 1
  1048.     shl     ax, 1
  1049.     add     ax, dx
  1050.     shl     ax, 1
  1051.     add     ax, bx
  1052.     inc     si
  1053.     inc     cx
  1054.     cmp     ax, 500
  1055.     ja      .bad
  1056.     jmp     .l
  1057. .done:
  1058.     test    cx, cx
  1059.     jz      .none
  1060.     clc
  1061.     ret
  1062. .none:
  1063.     stc
  1064.     ret
  1065. .bad:
  1066.     stc
  1067.     ret
  1068.  
  1069. ;---------------------------------------------------------------- file layer
  1070. seek:
  1071.     push    bx
  1072.     push    si
  1073.     push    di
  1074.     mov     ah, 0x42
  1075.     xor     al, al
  1076.     mov     bx, [fh]
  1077.     int     0x21
  1078.     pop     di
  1079.     pop     si
  1080.     pop     bx
  1081.     mov     word [inbuf_len], 0
  1082.     mov     word [inbuf_pos], 0
  1083.     ret
  1084.  
  1085. next_byte:
  1086.     push    bx
  1087.     push    cx
  1088.     push    dx
  1089.     push    si
  1090.     push    di
  1091.     mov     bx, [inbuf_pos]
  1092.     cmp     bx, [inbuf_len]
  1093.     jb      .have
  1094.     mov     ah, 0x3F
  1095.     mov     bx, [fh]
  1096.     mov     cx, INBUF_SIZE
  1097.     mov     dx, inbuf
  1098.     int     0x21
  1099.     jc      .eof
  1100.     test    ax, ax
  1101.     jz      .eof
  1102.     mov     [inbuf_len], ax
  1103.     xor     bx, bx
  1104. .have:
  1105.     mov     al, [inbuf + bx]
  1106.     inc     bx
  1107.     mov     [inbuf_pos], bx
  1108.     pop     di
  1109.     pop     si
  1110.     pop     dx
  1111.     pop     cx
  1112.     pop     bx
  1113.     clc
  1114.     ret
  1115. .eof:
  1116.     pop     di
  1117.     pop     si
  1118.     pop     dx
  1119.     pop     cx
  1120.     pop     bx
  1121.     stc
  1122.     ret
  1123.  
  1124. ;---------------------------------------------------------------- bit reader
  1125. getbit:
  1126.     mov     cl, [bit_idx]
  1127.     cmp     cl, 8
  1128.     jb      .have
  1129.     call    next_byte
  1130.     jc      .eof
  1131.     mov     [cur_byte], al
  1132.     mov     byte [bit_idx], 0
  1133.     xor     cl, cl
  1134. .have:
  1135.     mov     al, [cur_byte]
  1136.     shr     al, cl
  1137.     and     al, 1
  1138.     xor     ah, ah
  1139.     inc     byte [bit_idx]
  1140.     clc
  1141.     ret
  1142. .eof:
  1143.     stc
  1144.     ret
  1145.  
  1146. getbits:
  1147.     mov     bl, cl
  1148.     xor     bh, bh
  1149.     xor     dx, dx
  1150.     test    cl, cl
  1151.     jnz     .g
  1152.     xor     ax, ax
  1153.     clc
  1154.     ret
  1155. .g:
  1156.     test    bl, bl
  1157.     jz      .done
  1158.     call    getbit
  1159.     jc      .eof
  1160.     mov     cl, bh
  1161.     shl     ax, cl
  1162.     or      dx, ax
  1163.     inc     bh
  1164.     dec     bl
  1165.     jmp     .g
  1166. .done:
  1167.     mov     ax, dx
  1168.     clc
  1169.     ret
  1170. .eof:
  1171.     stc
  1172.     ret
  1173.  
  1174. stream_byte:
  1175.     cmp     byte [bit_idx], 8
  1176.     jne     .via_bits
  1177.     jmp     next_byte
  1178. .via_bits:
  1179.     mov     cl, 8
  1180.     call    getbits
  1181.     ret
  1182.  
  1183. ;---------------------------------------------------------------- huffman
  1184. decode:
  1185.     mov     word [h_code], 0
  1186.     mov     word [h_first], 0
  1187.     mov     word [h_index], 0
  1188.     mov     cx, 1
  1189. .loop:
  1190.     push    cx
  1191.     mov     cl, 1
  1192.     call    getbits
  1193.     pop     cx
  1194.     jc      .err
  1195.     or      [h_code], ax
  1196.     mov     bx, cx
  1197.     shl     bx, 1
  1198.     mov     dx, [si + bx]
  1199.     mov     ax, [h_code]
  1200.     sub     ax, dx
  1201.     cmp     ax, [h_first]
  1202.     jl      .found
  1203.     add     [h_index], dx
  1204.     add     dx, [h_first]
  1205.     shl     dx, 1
  1206.     mov     [h_first], dx
  1207.     shl     word [h_code], 1
  1208.     inc     cx
  1209.     cmp     cx, 15
  1210.     jbe     .loop
  1211. .err:
  1212.     stc
  1213.     ret
  1214. .found:
  1215.     mov     ax, [h_code]
  1216.     sub     ax, [h_first]
  1217.     add     ax, [h_index]
  1218.     mov     bx, ax
  1219.     shl     bx, 1
  1220.     mov     ax, [di + bx]
  1221.     clc
  1222.     ret
  1223.  
  1224. build_table:
  1225.     mov     [bt_lens], si
  1226.     mov     [bt_n], cx
  1227.     mov     [bt_cnt], di
  1228.     mov     [bt_sym], bx
  1229.     xor     ax, ax
  1230.     mov     cx, 16
  1231. .z: mov     [di], ax
  1232.     inc     di
  1233.     inc     di
  1234.     loop    .z
  1235.     xor     si, si
  1236.     mov     cx, [bt_n]
  1237. .c: cmp     si, cx
  1238.     jae     .offs
  1239.     mov     bx, [bt_lens]
  1240.     mov     al, [bx + si]
  1241.     test    al, al
  1242.     jz      .cn
  1243.     xor     ah, ah
  1244.     shl     ax, 1
  1245.     mov     di, ax
  1246.     mov     bx, [bt_cnt]
  1247.     inc     word [bx + di]
  1248. .cn:
  1249.     inc     si
  1250.     jmp     .c
  1251. .offs:
  1252.     xor     dx, dx
  1253.     mov     cx, 1
  1254. .o: mov     bx, cx
  1255.     shl     bx, 1
  1256.     mov     [offs + bx], dx
  1257.     mov     di, [bt_cnt]
  1258.     mov     ax, [di + bx]
  1259.     add     dx, ax
  1260.     inc     cx
  1261.     cmp     cx, 15
  1262.     jbe     .o
  1263.     xor     si, si
  1264. .p: cmp     si, [bt_n]
  1265.     jae     .done
  1266.     mov     bx, [bt_lens]
  1267.     mov     al, [bx + si]
  1268.     test    al, al
  1269.     jz      .pn
  1270.     xor     ah, ah
  1271.     shl     ax, 1
  1272.     mov     di, ax
  1273.     mov     ax, [offs + di]
  1274.     inc     word [offs + di]
  1275.     shl     ax, 1
  1276.     mov     bx, [bt_sym]
  1277.     add     bx, ax
  1278.     mov     [bx], si
  1279. .pn:
  1280.     inc     si
  1281.     jmp     .p
  1282. .done:
  1283.     ret
  1284.  
  1285. ;---------------------------------------------------------------- inflate
  1286. inflate:
  1287.     mov     word [wpos], 0
  1288.     mov     byte [bit_idx], 8
  1289.     mov     byte [cur_byte], 0
  1290. .block:
  1291.     mov     cl, 1
  1292.     call    getbits
  1293.     jc      .fail
  1294.     mov     [bfinal], ax
  1295.     mov     cl, 2
  1296.     call    getbits
  1297.     jc      .fail
  1298.     cmp     al, 0
  1299.     je      .stored
  1300.     cmp     al, 1
  1301.     je      .fixed
  1302.     cmp     al, 2
  1303.     je      .dynamic
  1304.     jmp     .fail
  1305. .stored:
  1306.     mov     byte [bit_idx], 8
  1307.     mov     cl, 8
  1308.     call    getbits
  1309.     jc      .fail
  1310.     mov     [slen], ax
  1311.     mov     cl, 8
  1312.     call    getbits
  1313.     jc      .fail
  1314.     mov     bx, ax
  1315.     mov     cl, 8
  1316.     shl     bx, cl
  1317.     or      [slen], bx
  1318.     mov     cl, 8
  1319.     call    getbits
  1320.     mov     [snlen], ax
  1321.     mov     cl, 8
  1322.     call    getbits
  1323.     mov     bx, ax
  1324.     mov     cl, 8
  1325.     shl     bx, cl
  1326.     or      [snlen], bx
  1327.     mov     ax, [slen]
  1328.     not     ax
  1329.     cmp     ax, [snlen]
  1330.     jne     .fail
  1331. .copy:
  1332.     cmp     word [slen], 0
  1333.     je      .bend
  1334.     call    stream_byte
  1335.     jc      .fail
  1336.     call    emit_raw
  1337.     dec     word [slen]
  1338.     jmp     .copy
  1339. .fixed:
  1340.     call    build_fixed
  1341.     jmp     .syms
  1342. .dynamic:
  1343.     call    build_dynamic
  1344.     jc      .fail
  1345. .syms:
  1346.     mov     si, counts_lit
  1347.     mov     di, syms_lit
  1348.     call    decode
  1349.     jc      .fail
  1350.     cmp     ax, 256
  1351.     jb      .lit
  1352.     je      .bend
  1353.     cmp     ax, 285
  1354.     ja      .fail
  1355.     sub     ax, 257
  1356.     mov     byte [cidx], al
  1357.     mov     bl, al
  1358.     xor     bh, bh
  1359.     mov     cl, [len_extra + bx]
  1360.     test    cl, cl
  1361.     jz      .noex
  1362.     call    getbits
  1363.     jc      .fail
  1364.     mov     [mtmp], ax
  1365.     jmp     .base
  1366. .noex:
  1367.     mov     word [mtmp], 0
  1368. .base:
  1369.     mov     bl, [cidx]
  1370.     xor     bh, bh
  1371.     shl     bx, 1
  1372.     mov     ax, [len_base + bx]
  1373.     add     ax, [mtmp]
  1374.     mov     [mlen], ax
  1375.     mov     si, counts_dist
  1376.     mov     di, syms_dist
  1377.     call    decode
  1378.     jc      .fail
  1379.     cmp     ax, 29
  1380.     ja      .fail
  1381.     mov     byte [cidx], al
  1382.     mov     bl, al
  1383.     xor     bh, bh
  1384.     mov     cl, [dist_extra + bx]
  1385.     test    cl, cl
  1386.     jz      .dno
  1387.     call    getbits
  1388.     jc      .fail
  1389.     mov     [mtmp], ax
  1390.     jmp     .dbase
  1391. .dno:
  1392.     mov     word [mtmp], 0
  1393. .dbase:
  1394.     mov     bl, [cidx]
  1395.     xor     bh, bh
  1396.     shl     bx, 1
  1397.     mov     ax, [dist_base + bx]
  1398.     add     ax, [mtmp]
  1399.     mov     [mdist], ax
  1400. .mcopy:
  1401.     mov     bx, [wpos]
  1402.     sub     bx, [mdist]
  1403.     and     bx, 0x7FFF
  1404.     mov     al, [window + bx]
  1405.     call    emit_raw
  1406.     dec     word [mlen]
  1407.     jnz     .mcopy
  1408.     jmp     .syms
  1409. .lit:
  1410.     call    emit_raw
  1411.     jmp     .syms
  1412. .bend:
  1413.     cmp     word [bfinal], 0
  1414.     je      .block
  1415.     clc
  1416.     ret
  1417. .fail:
  1418.     stc
  1419.     ret
  1420.  
  1421. build_fixed:
  1422.     xor     bx, bx
  1423. .l8a:
  1424.     cmp     bx, 144
  1425.     jae     .l9
  1426.     mov     byte [lens + bx], 8
  1427.     inc     bx
  1428.     jmp     .l8a
  1429. .l9:
  1430.     cmp     bx, 256
  1431.     jae     .l7
  1432.     mov     byte [lens + bx], 9
  1433.     inc     bx
  1434.     jmp     .l9
  1435. .l7:
  1436.     cmp     bx, 280
  1437.     jae     .l8b
  1438.     mov     byte [lens + bx], 7
  1439.     inc     bx
  1440.     jmp     .l7
  1441. .l8b:
  1442.     cmp     bx, 288
  1443.     jae     .ld
  1444.     mov     byte [lens + bx], 8
  1445.     inc     bx
  1446.     jmp     .l8b
  1447. .ld:
  1448.     cmp     bx, 320
  1449.     jae     .b
  1450.     mov     byte [lens + bx], 5
  1451.     inc     bx
  1452.     jmp     .ld
  1453. .b:
  1454.     mov     si, lens
  1455.     mov     cx, 288
  1456.     mov     di, counts_lit
  1457.     mov     bx, syms_lit
  1458.     call    build_table
  1459.     mov     si, lens + 288
  1460.     mov     cx, 32
  1461.     mov     di, counts_dist
  1462.     mov     bx, syms_dist
  1463.     call    build_table
  1464.     ret
  1465.  
  1466. build_dynamic:
  1467.     mov     di, cl_lens
  1468.     xor     ax, ax
  1469.     mov     cx, 19
  1470. .z: mov     [di], al
  1471.     inc     di
  1472.     loop    .z
  1473.     mov     cl, 5
  1474.     call    getbits
  1475.     jc      .fail
  1476.     add     ax, 257
  1477.     mov     [nlit], ax
  1478.     mov     cl, 5
  1479.     call    getbits
  1480.     jc      .fail
  1481.     inc     ax
  1482.     mov     [ndist], ax
  1483.     mov     cl, 4
  1484.     call    getbits
  1485.     jc      .fail
  1486.     add     ax, 4
  1487.     mov     [ncl], ax
  1488.     mov     word [hci], 0
  1489. .rd:
  1490.     mov     bx, [hci]
  1491.     cmp     bx, [ncl]
  1492.     jae     .bld
  1493.     mov     cl, 3
  1494.     call    getbits
  1495.     jc      .fail
  1496.     mov     bx, [hci]
  1497.     mov     si, order
  1498.     mov     bl, [si + bx]
  1499.     xor     bh, bh
  1500.     mov     [cl_lens + bx], al
  1501.     inc     word [hci]
  1502.     jmp     .rd
  1503. .bld:
  1504.     mov     si, cl_lens
  1505.     mov     cx, 19
  1506.     mov     di, counts_cl
  1507.     mov     bx, syms_cl
  1508.     call    build_table
  1509.     mov     ax, [nlit]
  1510.     add     ax, [ndist]
  1511.     mov     [totl], ax
  1512.     mov     word [hci], 0
  1513.     mov     si, counts_cl
  1514.     mov     di, syms_cl
  1515. .rl:
  1516.     mov     bx, [hci]
  1517.     cmp     bx, [totl]
  1518.     jae     .split
  1519.     call    decode
  1520.     jc      .fail
  1521.     cmp     ax, 15
  1522.     ja      .rep
  1523.     mov     bx, [hci]
  1524.     mov     [lens + bx], al
  1525.     inc     word [hci]
  1526.     jmp     .rl
  1527. .rep:
  1528.     cmp     ax, 16
  1529.     je      .r16
  1530.     cmp     ax, 17
  1531.     je      .r17
  1532.     cmp     ax, 18
  1533.     je      .r18
  1534.     jmp     .fail
  1535. .r16:
  1536.     mov     cl, 2
  1537.     call    getbits
  1538.     jc      .fail
  1539.     add     ax, 3
  1540.     mov     [repn], ax
  1541.     mov     bx, [hci]
  1542.     test    bx, bx
  1543.     jz      .fail
  1544.     dec     bx
  1545.     mov     al, [lens + bx]
  1546.     jmp     .fill
  1547. .r17:
  1548.     mov     cl, 3
  1549.     call    getbits
  1550.     jc      .fail
  1551.     add     ax, 3
  1552.     mov     [repn], ax
  1553.     xor     al, al
  1554.     jmp     .fill
  1555. .r18:
  1556.     mov     cl, 7
  1557.     call    getbits
  1558.     jc      .fail
  1559.     add     ax, 11
  1560.     mov     [repn], ax
  1561.     xor     al, al
  1562. .fill:
  1563.     mov     cx, [repn]
  1564. .f: mov     bx, [hci]
  1565.     cmp     bx, [totl]
  1566.     jae     .fail
  1567.     mov     [lens + bx], al
  1568.     inc     word [hci]
  1569.     loop    .f
  1570.     jmp     .rl
  1571. .split:
  1572.     mov     si, lens
  1573.     mov     cx, [nlit]
  1574.     mov     di, counts_lit
  1575.     mov     bx, syms_lit
  1576.     call    build_table
  1577.     mov     si, lens
  1578.     add     si, [nlit]
  1579.     mov     cx, [ndist]
  1580.     mov     di, counts_dist
  1581.     mov     bx, syms_dist
  1582.     call    build_table
  1583.     clc
  1584.     ret
  1585. .fail:
  1586.     stc
  1587.     ret
  1588.  
  1589. ;---------------------------------------------------------------- zip layer
  1590. find_eocd:
  1591.     mov     bx, [fh]
  1592.     mov     ax, 0x4202
  1593.     xor     cx, cx
  1594.     xor     dx, dx
  1595.     int     0x21
  1596.     jc      .fail
  1597.     mov     [fsize_lo], ax
  1598.     mov     [fsize_hi], dx
  1599.     sub     ax, 4096
  1600.     sbb     dx, 0
  1601.     jnc     .ok
  1602.     xor     ax, ax
  1603.     xor     dx, dx
  1604. .ok:
  1605.     mov     [scan_lo], ax
  1606.     mov     [scan_hi], dx
  1607. .chunk:
  1608.     mov     cx, [scan_hi]
  1609.     mov     dx, [scan_lo]
  1610.     call    seek
  1611.     mov     ah, 0x3F
  1612.     mov     bx, [fh]
  1613.     mov     cx, INBUF_SIZE
  1614.     mov     dx, inbuf
  1615.     int     0x21
  1616.     jc      .fail
  1617.     test    ax, ax
  1618.     jz      .fail
  1619.     mov     [chunklen], ax
  1620.     xor     si, si
  1621. .sl:
  1622.     mov     ax, [chunklen]
  1623.     sub     ax, 21
  1624.     cmp     si, ax
  1625.     jae     .next
  1626.     cmp     byte [inbuf + si], 'P'
  1627.     jne     .sn
  1628.     cmp     byte [inbuf + si + 1], 'K'
  1629.     jne     .sn
  1630.     cmp     byte [inbuf + si + 2], 5
  1631.     jne     .sn
  1632.     cmp     byte [inbuf + si + 3], 6
  1633.     jne     .sn
  1634.     mov     ax, [scan_lo]
  1635.     add     ax, si
  1636.     mov     [cand_lo], ax
  1637.     mov     ax, [scan_hi]
  1638.     adc     ax, 0
  1639.     mov     [cand_hi], ax
  1640.     mov     dx, [cand_lo]
  1641.     mov     cx, [cand_hi]
  1642.     call    seek
  1643.     mov     ah, 0x3F
  1644.     mov     bx, [fh]
  1645.     mov     cx, 22
  1646.     mov     dx, eocdbuf
  1647.     int     0x21
  1648.     jc      .fail
  1649.     cmp     ax, 22
  1650.     jne     .fail
  1651.     mov     ax, [cand_lo]
  1652.     add     ax, 22
  1653.     mov     dx, [cand_hi]
  1654.     adc     dx, 0
  1655.     add     ax, [eocdbuf + 20]
  1656.     adc     dx, 0
  1657.     cmp     ax, [fsize_lo]
  1658.     jne     .sn
  1659.     cmp     dx, [fsize_hi]
  1660.     jne     .sn
  1661.     mov     ax, [eocdbuf + 10]
  1662.     mov     [eocd_entries], ax
  1663.     mov     ax, [eocdbuf + 16]
  1664.     mov     [eocd_cdoff_lo], ax
  1665.     mov     ax, [eocdbuf + 18]
  1666.     mov     [eocd_cdoff_hi], ax
  1667.     clc
  1668.     ret
  1669. .sn:
  1670.     inc     si
  1671.     jmp     .sl
  1672. .next:
  1673.     mov     ax, [chunklen]
  1674.     sub     ax, 21
  1675.     jbe     .fail
  1676.     add     [scan_lo], ax
  1677.     adc     word [scan_hi], 0
  1678.     jmp     .chunk
  1679. .fail:
  1680.     stc
  1681.     ret
  1682.  
  1683. process_entry:
  1684.     mov     cx, [cdpos_hi]
  1685.     mov     dx, [cdpos_lo]
  1686.     call    seek
  1687.     mov     ah, 0x3F
  1688.     mov     bx, [fh]
  1689.     mov     cx, 46
  1690.     mov     dx, cdbuf
  1691.     int     0x21
  1692.     jc      .next
  1693.     cmp     ax, 46
  1694.     jne     .done
  1695.     cmp     byte [cdbuf], 'P'
  1696.     jne     .done
  1697.     cmp     byte [cdbuf + 1], 'K'
  1698.     jne     .done
  1699.     cmp     byte [cdbuf + 2], 1
  1700.     jne     .done
  1701.     cmp     byte [cdbuf + 3], 2
  1702.     jne     .done
  1703.     mov     ax, [cdbuf + 10]
  1704.     mov     [method], ax
  1705.     mov     ax, [cdbuf + 20]
  1706.     mov     [csz_lo], ax
  1707.     mov     ax, [cdbuf + 22]
  1708.     mov     [csz_hi], ax
  1709.     mov     ax, [cdbuf + 28]
  1710.     mov     [cnamelen], ax
  1711.     mov     ax, [cdbuf + 30]
  1712.     mov     [cextralen], ax
  1713.     mov     ax, [cdbuf + 32]
  1714.     mov     [ccommentlen], ax
  1715.     mov     ax, [cdbuf + 42]
  1716.     mov     [lhoff_lo], ax
  1717.     mov     ax, [cdbuf + 44]
  1718.     mov     [lhoff_hi], ax
  1719.     mov     ax, [cnamelen]
  1720.     add     [cdpos_lo], ax
  1721.     adc     word [cdpos_hi], 0
  1722.     mov     ax, [cextralen]
  1723.     add     [cdpos_lo], ax
  1724.     adc     word [cdpos_hi], 0
  1725.     mov     ax, [ccommentlen]
  1726.     add     [cdpos_lo], ax
  1727.     adc     word [cdpos_hi], 0
  1728.     add     word [cdpos_lo], 46
  1729.     adc     word [cdpos_hi], 0
  1730.     mov     ah, 0x3F
  1731.     mov     bx, [fh]
  1732.     mov     cx, [cnamelen]
  1733.     cmp     cx, 250
  1734.     jbe     .lenok
  1735.     mov     cx, 250
  1736. .lenok:
  1737.     mov     [cnamelen], cx
  1738.     mov     dx, nbuf
  1739.     int     0x21
  1740.     call    lower_name
  1741.     call    check_ext
  1742.     jnc     .next
  1743.     cmp     byte [mode], 0
  1744.     je      .extract
  1745.     mov     si, [p_fsrc]
  1746.     call    ends_with
  1747.     jnc     .next
  1748.     mov     byte [chapfound], 1
  1749. .extract:
  1750.     mov     cx, [lhoff_hi]
  1751.     mov     dx, [lhoff_lo]
  1752.     call    seek
  1753.     mov     ah, 0x3F
  1754.     mov     bx, [fh]
  1755.     mov     cx, 30
  1756.     mov     dx, lhbuf
  1757.     int     0x21
  1758.     jc      .next
  1759.     cmp     ax, 30
  1760.     jne     .next
  1761.     cmp     byte [lhbuf], 'P'
  1762.     jne     .next
  1763.     cmp     byte [lhbuf + 1], 'K'
  1764.     jne     .next
  1765.     cmp     byte [lhbuf + 2], 3
  1766.     jne     .next
  1767.     cmp     byte [lhbuf + 3], 4
  1768.     jne     .next
  1769.     mov     ax, [lhbuf + 26]
  1770.     add     [lhoff_lo], ax
  1771.     adc     word [lhoff_hi], 0
  1772.     mov     ax, [lhbuf + 28]
  1773.     add     [lhoff_lo], ax
  1774.     adc     word [lhoff_hi], 0
  1775.     add     word [lhoff_lo], 30
  1776.     adc     word [lhoff_hi], 0
  1777.     mov     word [inbuf_len], 0
  1778.     mov     word [inbuf_pos], 0
  1779.     mov     byte [bit_idx], 8
  1780.     mov     byte [cur_byte], 0
  1781.     mov     byte [u_count], 0
  1782.     mov     word [wpos], 0
  1783.     mov     byte [pstate], 0
  1784.     mov     byte [tag_len], 0
  1785.     mov     byte [ent_len], 0
  1786.     mov     byte [sk_state], 0
  1787.     mov     byte [out_on], 1
  1788.     mov     byte [anch_scan], 0
  1789.     cmp     byte [mode], 2
  1790.     jne     .noscope
  1791.     mov     byte [anch_scan], 1
  1792.     mov     byte [as_state], 0
  1793.     mov     byte [bs_state], 0
  1794.     mov     si, [p_sanc]
  1795.     cmp     byte [si], 0
  1796.     je      .noscope
  1797.     mov     byte [out_on], 0
  1798. .noscope:
  1799.     cmp     byte [paging], 0
  1800.     je      .norec
  1801.     mov     ax, [seccnt]
  1802.     cmp     ax, 32
  1803.     jae     .norec
  1804.     shl     ax, 1
  1805.     shl     ax, 1
  1806.     mov     di, secidx
  1807.     add     di, ax
  1808.     mov     bx, [tmppos_lo]
  1809.     add     bx, [outbuf_len]
  1810.     mov     [di], bx
  1811.     mov     bx, [tmppos_hi]
  1812.     adc     bx, 0
  1813.     mov     [di + 2], bx
  1814.     inc     word [seccnt]
  1815. .norec:
  1816.     mov     cx, [lhoff_hi]
  1817.     mov     dx, [lhoff_lo]
  1818.     call    seek
  1819.     mov     ax, [method]
  1820.     cmp     ax, 0
  1821.     je      .stored
  1822.     cmp     ax, 8
  1823.     je      .deflate
  1824.     jmp     .next
  1825. .stored:
  1826.     call    extract_stored
  1827.     jmp     .next
  1828. .deflate:
  1829.     call    inflate
  1830. .next:
  1831.     clc
  1832.     ret
  1833. .done:
  1834.     stc
  1835.     ret
  1836.  
  1837. extract_stored:
  1838. .loop:
  1839.     mov     ax, [csz_lo]
  1840.     or      ax, [csz_hi]
  1841.     jz      .done
  1842.     call    next_byte
  1843.     jc      .done
  1844.     call    emit_raw
  1845.     dec     word [csz_lo]
  1846.     cmp     word [csz_lo], 0xFFFF
  1847.     jne     .loop
  1848.     dec     word [csz_hi]
  1849.     jmp     .loop
  1850. .done:
  1851.     ret
  1852.  
  1853. lower_name:
  1854.     xor     si, si
  1855. .l: cmp     si, [cnamelen]
  1856.     jae     .d
  1857.     mov     al, [nbuf + si]
  1858.     cmp     al, 'A'
  1859.     jb      .s
  1860.     cmp     al, 'Z'
  1861.     ja      .s
  1862.     add     al, 32
  1863. .s: mov     [lname + si], al
  1864.     inc     si
  1865.     jmp     .l
  1866. .d: mov     byte [lname + si], 0
  1867.     ret
  1868.  
  1869. check_ext:
  1870.     xor     si, si
  1871. .len:
  1872.     cmp     byte [lname + si], 0
  1873.     je      .got
  1874.     inc     si
  1875.     jmp     .len
  1876. .got:
  1877.     cmp     si, 6
  1878.     jb      .h5
  1879.     cmp     byte [lname + si - 6], '.'
  1880.     jne     .h5
  1881.     cmp     byte [lname + si - 5], 'x'
  1882.     jne     .h5
  1883.     cmp     byte [lname + si - 4], 'h'
  1884.     jne     .h5
  1885.     cmp     byte [lname + si - 3], 't'
  1886.     jne     .h5
  1887.     cmp     byte [lname + si - 2], 'm'
  1888.     jne     .h5
  1889.     cmp     byte [lname + si - 1], 'l'
  1890.     jne     .h5
  1891.     stc
  1892.     ret
  1893. .h5:
  1894.     cmp     si, 5
  1895.     jb      .h4
  1896.     cmp     byte [lname + si - 5], '.'
  1897.     jne     .h4
  1898.     cmp     byte [lname + si - 4], 'h'
  1899.     jne     .h4
  1900.     cmp     byte [lname + si - 3], 't'
  1901.     jne     .h4
  1902.     cmp     byte [lname + si - 2], 'm'
  1903.     jne     .h4
  1904.     cmp     byte [lname + si - 1], 'l'
  1905.     jne     .h4
  1906.     stc
  1907.     ret
  1908. .h4:
  1909.     cmp     si, 4
  1910.     jb      .no
  1911.     cmp     byte [lname + si - 4], '.'
  1912.     jne     .no
  1913.     cmp     byte [lname + si - 3], 'h'
  1914.     jne     .no
  1915.     cmp     byte [lname + si - 2], 't'
  1916.     jne     .no
  1917.     cmp     byte [lname + si - 1], 'm'
  1918.     jne     .no
  1919.     stc
  1920.     ret
  1921. .no:
  1922.     clc
  1923.     ret
  1924.  
  1925. ;---------------------------------------------------------------- ncx loading
  1926. load_toc:
  1927.     mov     ax, [eocd_cdoff_lo]
  1928.     mov     [scan_lo], ax
  1929.     mov     ax, [eocd_cdoff_hi]
  1930.     mov     [scan_hi], ax
  1931.     mov     ax, [eocd_entries]
  1932.     mov     [ncxl], ax
  1933. .loop:
  1934.     cmp     word [ncxl], 0
  1935.     je      .done
  1936.     dec     word [ncxl]
  1937.     mov     cx, [scan_hi]
  1938.     mov     dx, [scan_lo]
  1939.     call    seek
  1940.     mov     ah, 0x3F
  1941.     mov     bx, [fh]
  1942.     mov     cx, 46
  1943.     mov     dx, cdbuf
  1944.     int     0x21
  1945.     jc      .done
  1946.     cmp     ax, 46
  1947.     jne     .done
  1948.     mov     ax, [cdbuf + 10]
  1949.     mov     [method], ax
  1950.     mov     ax, [cdbuf + 20]
  1951.     mov     [csz_lo], ax
  1952.     mov     ax, [cdbuf + 22]
  1953.     mov     [csz_hi], ax
  1954.     mov     ax, [cdbuf + 28]
  1955.     mov     [cnamelen], ax
  1956.     mov     ax, [cdbuf + 30]
  1957.     mov     [cextralen], ax
  1958.     mov     ax, [cdbuf + 32]
  1959.     mov     [ccommentlen], ax
  1960.     mov     ax, [cdbuf + 42]
  1961.     mov     [lhoff_lo], ax
  1962.     mov     ax, [cdbuf + 44]
  1963.     mov     [lhoff_hi], ax
  1964.     mov     ax, [cnamelen]
  1965.     add     [scan_lo], ax
  1966.     adc     word [scan_hi], 0
  1967.     mov     ax, [cextralen]
  1968.     add     [scan_lo], ax
  1969.     adc     word [scan_hi], 0
  1970.     mov     ax, [ccommentlen]
  1971.     add     [scan_lo], ax
  1972.     adc     word [scan_hi], 0
  1973.     add     word [scan_lo], 46
  1974.     adc     word [scan_hi], 0
  1975.     mov     ah, 0x3F
  1976.     mov     bx, [fh]
  1977.     mov     cx, [cnamelen]
  1978.     cmp     cx, 250
  1979.     jbe     .lenok
  1980.     mov     cx, 250
  1981. .lenok:
  1982.     mov     [cnamelen], cx
  1983.     mov     dx, nbuf
  1984.     int     0x21
  1985.     call    lower_name
  1986.     call    check_ncx
  1987.     jnc     .loop
  1988.     mov     cx, [lhoff_hi]
  1989.     mov     dx, [lhoff_lo]
  1990.     call    seek
  1991.     mov     ah, 0x3F
  1992.     mov     bx, [fh]
  1993.     mov     cx, 30
  1994.     mov     dx, lhbuf
  1995.     int     0x21
  1996.     jc      .done
  1997.     cmp     ax, 30
  1998.     jne     .done
  1999.     cmp     byte [lhbuf], 'P'
  2000.     jne     .done
  2001.     cmp     byte [lhbuf + 1], 'K'
  2002.     jne     .done
  2003.     mov     ax, [lhbuf + 26]
  2004.     add     [lhoff_lo], ax
  2005.     adc     word [lhoff_hi], 0
  2006.     mov     ax, [lhbuf + 28]
  2007.     add     [lhoff_lo], ax
  2008.     adc     word [lhoff_hi], 0
  2009.     add     word [lhoff_lo], 30
  2010.     adc     word [lhoff_hi], 0
  2011.     mov     word [inbuf_len], 0
  2012.     mov     word [inbuf_pos], 0
  2013.     mov     byte [bit_idx], 8
  2014.     mov     byte [cur_byte], 0
  2015.     mov     word [wpos], 0
  2016.     mov     byte [nx_state], 0
  2017.     mov     byte [nx_tpend], 0
  2018.     mov     byte [u_count], 0
  2019.     mov     byte [ncx_mode], 1
  2020.     mov     cx, [lhoff_hi]
  2021.     mov     dx, [lhoff_lo]
  2022.     call    seek
  2023.     mov     ax, [method]
  2024.     cmp     ax, 0
  2025.     je      .stored
  2026.     cmp     ax, 8
  2027.     je      .deflate
  2028.     jmp     .clr
  2029. .stored:
  2030.     call    extract_stored
  2031.     jmp     .clr
  2032. .deflate:
  2033.     call    inflate
  2034. .clr:
  2035.     mov     byte [ncx_mode], 0
  2036. .done:
  2037.     ret
  2038.  
  2039. check_ncx:
  2040.     xor     si, si
  2041. .len:
  2042.     cmp     byte [lname + si], 0
  2043.     je      .got
  2044.     inc     si
  2045.     jmp     .len
  2046. .got:
  2047.     cmp     si, 4
  2048.     jb      .no
  2049.     cmp     byte [lname + si - 4], '.'
  2050.     jne     .no
  2051.     cmp     byte [lname + si - 3], 'n'
  2052.     jne     .no
  2053.     cmp     byte [lname + si - 2], 'c'
  2054.     jne     .no
  2055.     cmp     byte [lname + si - 1], 'x'
  2056.     jne     .no
  2057.     stc
  2058.     ret
  2059. .no:
  2060.     clc
  2061.     ret
  2062.  
  2063. ;---------------------------------------------------------------- ncx parser
  2064. ncx_feed:
  2065.     mov     cl, [nx_state]
  2066.     cmp     cl, 0
  2067.     je      .s_scan
  2068.     cmp     cl, 1
  2069.     je      .s_tag
  2070.     cmp     cl, 2
  2071.     je      .s_title
  2072.     cmp     cl, 3
  2073.     je      .s_r
  2074.     cmp     cl, 4
  2075.     je      .s_c
  2076.     cmp     cl, 5
  2077.     je      .s_eq
  2078.     cmp     cl, 6
  2079.     je      .s_quote
  2080.     cmp     cl, 7
  2081.     je      .s_src
  2082.     cmp     cl, 8
  2083.     je      .s_anchor
  2084.     ret
  2085. .s_scan:
  2086.     cmp     al, '<'
  2087.     jne     .ret
  2088.     mov     byte [nx_state], 1
  2089.     mov     byte [nx_tagpos], 0
  2090.     mov     byte [nx_close], 0
  2091.     mov     byte [nx_inname], 1
  2092.     ret
  2093. .s_tag:
  2094.     cmp     al, '>'
  2095.     je      .tag_end
  2096.     cmp     al, '/'
  2097.     jne     .not_slash
  2098.     cmp     byte [nx_tagpos], 0
  2099.     jne     .not_slash
  2100.     mov     byte [nx_close], 1
  2101.     ret
  2102. .not_slash:
  2103.     mov     dl, al
  2104.     or      dl, 0x20
  2105.     cmp     dl, 'a'
  2106.     jb      .notletter
  2107.     cmp     dl, 'z'
  2108.     ja      .notletter
  2109.     cmp     byte [nx_inname], 0
  2110.     je      .attr_letter
  2111.     cmp     byte [nx_tagpos], 8
  2112.     jae     .ret
  2113.     cmp     byte [nx_close], 0
  2114.     jne     .ret
  2115.     mov     bl, [nx_tagpos]
  2116.     xor     bh, bh
  2117.     mov     [nx_tagbuf + bx], dl
  2118.     inc     byte [nx_tagpos]
  2119.     ret
  2120. .attr_letter:
  2121.     cmp     dl, 's'
  2122.     jne     .ret
  2123.     mov     byte [nx_state], 3
  2124.     ret
  2125. .notletter:
  2126.     mov     byte [nx_inname], 0
  2127.     ret
  2128. .tag_end:
  2129.     cmp     byte [nx_close], 0
  2130.     jne     .te_done
  2131.     cmp     byte [nx_tagpos], 4
  2132.     jne     .te_done
  2133.     cmp     word [nx_tagbuf], 'te'
  2134.     jne     .te_done
  2135.     cmp     word [nx_tagbuf + 2], 'xt'
  2136.     jne     .te_done
  2137.     mov     byte [nx_state], 2
  2138.     mov     byte [nx_tpos], 0
  2139.     ret
  2140. .te_done:
  2141.     mov     byte [nx_state], 0
  2142.     ret
  2143. .s_title:
  2144.     cmp     al, '<'
  2145.     jne     .t_store
  2146.     mov     bl, [nx_tpos]
  2147.     xor     bh, bh
  2148.     mov     byte [nx_tbuf + bx], 0
  2149.     mov     byte [nx_tpend], 1
  2150.     mov     byte [nx_state], 1
  2151.     mov     byte [nx_tagpos], 0
  2152.     mov     byte [nx_close], 0
  2153.     mov     byte [nx_inname], 1
  2154.     ret
  2155. .t_store:
  2156.     mov     bl, [nx_tpos]
  2157.     cmp     bl, 63
  2158.     jae     .ret
  2159.     xor     bh, bh
  2160.     mov     [nx_tbuf + bx], al
  2161.     inc     byte [nx_tpos]
  2162.     ret
  2163. .s_r:
  2164.     cmp     al, 'r'
  2165.     je      .r_ok
  2166.     mov     byte [nx_state], 1
  2167.     ret
  2168. .r_ok:
  2169.     mov     byte [nx_state], 4
  2170.     ret
  2171. .s_c:
  2172.     cmp     al, 'c'
  2173.     je      .c_ok
  2174.     mov     byte [nx_state], 1
  2175.     ret
  2176. .c_ok:
  2177.     mov     byte [nx_state], 5
  2178.     ret
  2179. .s_eq:
  2180.     cmp     al, '='
  2181.     je      .eq_ok
  2182.     cmp     al, ' '
  2183.     je      .ret
  2184.     cmp     al, 9
  2185.     je      .ret
  2186.     mov     byte [nx_state], 1
  2187.     ret
  2188. .eq_ok:
  2189.     mov     byte [nx_state], 6
  2190.     ret
  2191. .s_quote:
  2192.     cmp     al, 34
  2193.     je      .q_ok
  2194.     cmp     al, 39
  2195.     je      .q_ok
  2196.     mov     byte [nx_state], 1
  2197.     ret
  2198. .q_ok:
  2199.     mov     [nx_q], al
  2200.     mov     byte [nx_spos], 0
  2201.     mov     byte [nx_apos], 0
  2202.     mov     byte [nx_state], 7
  2203.     ret
  2204. .s_src:
  2205.     cmp     al, [nx_q]
  2206.     je      .src_end
  2207.     cmp     al, '#'
  2208.     je      .anchor
  2209.     mov     bl, [nx_spos]
  2210.     cmp     bl, 119
  2211.     jae     .ret
  2212.     xor     bh, bh
  2213.     mov     [nx_sbuf + bx], al
  2214.     inc     byte [nx_spos]
  2215.     ret
  2216. .anchor:
  2217.     mov     byte [nx_state], 8
  2218.     ret
  2219. .src_end:
  2220.     call    src_done
  2221.     mov     byte [nx_state], 1
  2222.     ret
  2223. .s_anchor:
  2224.     cmp     al, [nx_q]
  2225.     je      .src_end
  2226.     mov     bl, [nx_apos]
  2227.     cmp     bl, 31
  2228.     jae     .ret
  2229.     xor     bh, bh
  2230.     mov     [nx_abuf + bx], al
  2231.     inc     byte [nx_apos]
  2232.     ret
  2233. .ret:
  2234.     ret
  2235.  
  2236. src_done:
  2237.     mov     bl, [nx_spos]
  2238.     xor     bh, bh
  2239.     mov     byte [nx_sbuf + bx], 0
  2240.     mov     bl, [nx_apos]
  2241.     xor     bh, bh
  2242.     mov     byte [nx_abuf + bx], 0
  2243.     cmp     byte [nx_tpend], 0
  2244.     je      .done
  2245.     mov     ax, [tcount]
  2246.     cmp     ax, 32
  2247.     jae     .done
  2248.     mov     cx, ax
  2249.     shl     ax, 1
  2250.     shl     ax, 1
  2251.     shl     ax, 1
  2252.     shl     ax, 1
  2253.     shl     ax, 1
  2254.     shl     ax, 1
  2255.     mov     di, ttit
  2256.     add     di, ax
  2257.     mov     byte [u_count], 0
  2258.     xor     si, si
  2259.     xor     dx, dx
  2260. .tc:
  2261.     cmp     dx, 63
  2262.     jae     .td
  2263.     mov     al, [nx_tbuf + si]
  2264.     test    al, al
  2265.     jz      .td
  2266.     call    fold_to_di
  2267.     inc     si
  2268.     inc     dx
  2269.     jmp     .tc
  2270. .td:
  2271.     mov     byte [di], 0
  2272.     mov     ax, [tcount]
  2273.     mov     bx, 120
  2274.     mul     bx
  2275.     mov     di, tsrc
  2276.     add     di, ax
  2277.     xor     si, si
  2278.     xor     cx, cx
  2279. .sc:
  2280.     cmp     cx, 119
  2281.     jae     .sd
  2282.     mov     al, [nx_sbuf + si]
  2283.     test    al, al
  2284.     jz      .sd
  2285.     cmp     al, 'A'
  2286.     jb      .sl
  2287.     cmp     al, 'Z'
  2288.     ja      .sl
  2289.     add     al, 32
  2290. .sl:
  2291.     mov     [di], al
  2292.     inc     di
  2293.     inc     si
  2294.     inc     cx
  2295.     jmp     .sc
  2296. .sd:
  2297.     mov     byte [di], 0
  2298.     mov     ax, [tcount]
  2299.     shl     ax, 1
  2300.     shl     ax, 1
  2301.     shl     ax, 1
  2302.     shl     ax, 1
  2303.     shl     ax, 1
  2304.     mov     di, tanc
  2305.     add     di, ax
  2306.     xor     si, si
  2307.     xor     cx, cx
  2308. .ac:
  2309.     cmp     cx, 31
  2310.     jae     .ad
  2311.     mov     al, [nx_abuf + si]
  2312.     test    al, al
  2313.     jz      .ad
  2314.     mov     [di], al
  2315.     inc     di
  2316.     inc     si
  2317.     inc     cx
  2318.     jmp     .ac
  2319. .ad:
  2320.     mov     byte [di], 0
  2321.     inc     word [tcount]
  2322.     mov     byte [nx_tpend], 0
  2323. .done:
  2324.     ret
  2325.  
  2326. ;---------------------------------------------------------------- chapter helpers
  2327. strcmp_t:
  2328. .sl:
  2329.     mov     al, [si]
  2330.     cmp     al, [di]
  2331.     jne     .no
  2332.     test    al, al
  2333.     jz      .yes
  2334.     inc     si
  2335.     inc     di
  2336.     jmp     .sl
  2337. .yes:
  2338.     stc
  2339.     ret
  2340. .no:
  2341.     clc
  2342.     ret
  2343.  
  2344. ends_with:
  2345.     mov     bx, si
  2346.     xor     cx, cx
  2347. .sl:
  2348.     cmp     byte [bx], 0
  2349.     je      .gotl
  2350.     inc     bx
  2351.     inc     cx
  2352.     jmp     .sl
  2353. .gotl:
  2354.     mov     si, lname
  2355.     xor     di, di
  2356. .ll:
  2357.     cmp     byte [si], 0
  2358.     je      .gotd
  2359.     inc     si
  2360.     inc     di
  2361.     jmp     .ll
  2362. .gotd:
  2363.     cmp     cx, di
  2364.     ja      .no
  2365.     sub     si, cx
  2366.     sub     bx, cx
  2367. .cmp:
  2368.     cmp     cx, 0
  2369.     je      .yes
  2370.     mov     al, [si]
  2371.     cmp     al, [bx]
  2372.     jne     .no
  2373.     inc     si
  2374.     inc     bx
  2375.     dec     cx
  2376.     jmp     .cmp
  2377. .yes:
  2378.     stc
  2379.     ret
  2380. .no:
  2381.     clc
  2382.     ret
  2383.  
  2384. anchor_scan:
  2385.     cmp     byte [out_on], 0
  2386.     jne     .stopm
  2387.     mov     si, [p_sanc]
  2388.     ANCH    as_state, as_q, as_pos
  2389.     jnc     .ret
  2390.     mov     byte [out_on], 1
  2391.     ret
  2392. .stopm:
  2393.     cmp     word [p_panc], 0
  2394.     je      .ret
  2395.     mov     si, [p_panc]
  2396.     ANCH    bs_state, bs_q, bs_pos
  2397.     jnc     .ret
  2398.     mov     byte [out_on], 0
  2399. .ret:
  2400.     ret
  2401.  
  2402. list_navpoints:
  2403.     mov     word [ncxl], 0
  2404. .ln_loop:
  2405.     mov     ax, [ncxl]
  2406.     cmp     ax, [tcount]
  2407.     jae     .ln_done
  2408.     call    print_nav_line
  2409.     inc     word [ncxl]
  2410.     jmp     .ln_loop
  2411. .ln_done:
  2412.     ret
  2413.  
  2414. print_nav_line:
  2415.     mov     di, scratch + 8
  2416.     mov     ax, [ncxl]
  2417.     inc     ax
  2418.     mov     bx, 10
  2419.     xor     cx, cx
  2420. .dig:
  2421.     xor     dx, dx
  2422.     div     bx
  2423.     add     dl, '0'
  2424.     dec     di
  2425.     mov     [di], dl
  2426.     inc     cx
  2427.     test    ax, ax
  2428.     jnz     .dig
  2429.     mov     si, di
  2430.     mov     di, tocline
  2431. .cp:
  2432.     mov     al, [si]
  2433.     mov     [di], al
  2434.     inc     si
  2435.     inc     di
  2436.     loop    .cp
  2437.     mov     byte [di], ':'
  2438.     inc     di
  2439.     mov     byte [di], ' '
  2440.     inc     di
  2441.     mov     ax, [ncxl]
  2442.     shl     ax, 1
  2443.     shl     ax, 1
  2444.     shl     ax, 1
  2445.     shl     ax, 1
  2446.     shl     ax, 1
  2447.     shl     ax, 1
  2448.     mov     si, ttit
  2449.     add     si, ax
  2450. .tcopy:
  2451.     mov     al, [si]
  2452.     test    al, al
  2453.     jz      .fin
  2454.     mov     [di], al
  2455.     inc     si
  2456.     inc     di
  2457.     jmp     .tcopy
  2458. .fin:
  2459.     mov     byte [di], 13
  2460.     inc     di
  2461.     mov     byte [di], 10
  2462.     inc     di
  2463.     mov     cx, di
  2464.     sub     cx, tocline
  2465.     mov     dx, tocline
  2466.     mov     ah, 0x40
  2467.     mov     bx, [oh]
  2468.     int     0x21
  2469.     ret
  2470.  
  2471. ;---------------------------------------------------------------- text layer
  2472. emit_raw:
  2473.     mov     bx, [wpos]
  2474.     mov     [window + bx], al
  2475.     inc     bx
  2476.     and     bx, 0x7FFF
  2477.     mov     [wpos], bx
  2478.     cmp     byte [ncx_mode], 0
  2479.     jne     ncx_feed
  2480.     cmp     byte [anch_scan], 0
  2481.     je      .tail
  2482.     call    anchor_scan
  2483. .tail:
  2484.     jmp     feed
  2485.  
  2486. feed:
  2487.     mov     cl, [pstate]
  2488.     cmp     cl, 1
  2489.     je      .tag
  2490.     cmp     cl, 2
  2491.     je      .skip
  2492.     cmp     cl, 3
  2493.     je      .ent
  2494.     cmp     al, '<'
  2495.     je      .t_lt
  2496.     cmp     al, '&'
  2497.     je      .t_amp
  2498.     jmp     emit_text
  2499. .t_lt:
  2500.     mov     byte [pstate], 1
  2501.     mov     byte [tag_len], 0
  2502.     mov     byte [tag_nl], 0
  2503.     ret
  2504. .t_amp:
  2505.     mov     byte [pstate], 3
  2506.     mov     byte [ent_len], 0
  2507.     ret
  2508. .tag:
  2509.     cmp     al, '>'
  2510.     je      .t_gt
  2511.     mov     bl, [tag_len]
  2512.     cmp     bl, 8
  2513.     jae     .t_ret
  2514.     cmp     al, 'A'
  2515.     jb      .t_nolow
  2516.     cmp     al, 'Z'
  2517.     ja      .t_nolow
  2518.     add     al, 32
  2519. .t_nolow:
  2520.     xor     bh, bh
  2521.     mov     [tagbuf + bx], al
  2522.     inc     byte [tag_len]
  2523.     cmp     byte [tag_len], 1
  2524.     jne     .t_ret
  2525.     cmp     al, 'p'
  2526.     je      .t_nl
  2527.     cmp     al, 'b'
  2528.     je      .t_nl
  2529.     cmp     al, 'd'
  2530.     je      .t_nl
  2531.     cmp     al, 'h'
  2532.     je      .t_nl
  2533.     cmp     al, 'l'
  2534.     je      .t_nl
  2535.     cmp     al, 't'
  2536.     je      .t_nl
  2537.     jmp     .t_ret
  2538. .t_nl:
  2539.     mov     byte [tag_nl], 1
  2540. .t_ret:
  2541.     ret
  2542. .t_gt:
  2543.     mov     byte [pstate], 0
  2544.     cmp     byte [tag_nl], 0
  2545.     je      .t_gt2
  2546.     mov     al, 10
  2547.     call    out_char
  2548. .t_gt2:
  2549.     cmp     byte [tag_len], 6
  2550.     jb      .t_done
  2551.     cmp     word [tagbuf], 'sc'
  2552.     jne     .t_sty
  2553.     cmp     word [tagbuf + 2], 'ri'
  2554.     jne     .t_done
  2555.     cmp     word [tagbuf + 4], 'pt'
  2556.     jne     .t_done
  2557.     jmp     .t_skip
  2558. .t_sty:
  2559.     cmp     word [tagbuf], 'st'
  2560.     jne     .t_done
  2561.     cmp     word [tagbuf + 2], 'yl'
  2562.     jne     .t_done
  2563.     cmp     byte [tagbuf + 4], 'e'
  2564.     jne     .t_done
  2565. .t_skip:
  2566.     mov     byte [pstate], 2
  2567.     mov     byte [sk_state], 0
  2568. .t_done:
  2569.     ret
  2570. .skip:
  2571.     mov     cl, [sk_state]
  2572.     cmp     cl, 1
  2573.     je      .s1
  2574.     cmp     cl, 2
  2575.     je      .s2
  2576.     cmp     al, '<'
  2577.     jne     .s_ret
  2578.     mov     byte [sk_state], 1
  2579.     ret
  2580. .s1:
  2581.     cmp     al, '/'
  2582.     je      .s1a
  2583.     cmp     al, '<'
  2584.     je      .s_ret
  2585.     mov     byte [sk_state], 0
  2586.     ret
  2587. .s1a:
  2588.     mov     byte [sk_state], 2
  2589.     mov     byte [sk_len], 0
  2590.     ret
  2591. .s2:
  2592.     cmp     al, '>'
  2593.     je      .s2end
  2594.     mov     bl, [sk_len]
  2595.     cmp     bl, 8
  2596.     jae     .s_ret
  2597.     cmp     al, 'A'
  2598.     jb      .s2n
  2599.     cmp     al, 'Z'
  2600.     ja      .s2n
  2601.     add     al, 32
  2602. .s2n:
  2603.     xor     bh, bh
  2604.     mov     [skbuf + bx], al
  2605.     inc     byte [sk_len]
  2606.     ret
  2607. .s2end:
  2608.     cmp     byte [sk_len], 6
  2609.     jb      .s2no
  2610.     cmp     word [skbuf], 'sc'
  2611.     jne     .s2sty
  2612.     cmp     word [skbuf + 2], 'ri'
  2613.     jne     .s2no
  2614.     cmp     word [skbuf + 4], 'pt'
  2615.     jne     .s2no
  2616.     jmp     .s2yes
  2617. .s2sty:
  2618.     cmp     word [skbuf], 'st'
  2619.     jne     .s2no
  2620.     cmp     word [skbuf + 2], 'yl'
  2621.     jne     .s2no
  2622.     cmp     byte [skbuf + 4], 'e'
  2623.     jne     .s2no
  2624. .s2yes:
  2625.     mov     byte [pstate], 0
  2626. .s2no:
  2627.     mov     byte [sk_state], 0
  2628. .s_ret:
  2629.     ret
  2630. .ent:
  2631.     cmp     al, ';'
  2632.     je      .e_end
  2633.     mov     bl, [ent_len]
  2634.     cmp     bl, 7
  2635.     jae     .e_flush
  2636.     xor     bh, bh
  2637.     mov     [entbuf + bx], al
  2638.     inc     byte [ent_len]
  2639.     ret
  2640. .e_end:
  2641.     call    resolve_ent
  2642.     test    ax, ax
  2643.     jz      .e_flush
  2644.     call    fold_out
  2645.     mov     byte [pstate], 0
  2646.     ret
  2647. .e_flush:
  2648.     mov     al, '&'
  2649.     call    out_char
  2650.     xor     bl, bl
  2651. .ef:
  2652.     cmp     bl, [ent_len]
  2653.     jae     .efd
  2654.     xor     bh, bh
  2655.     mov     al, [entbuf + bx]
  2656.     push    bx
  2657.     call    out_char
  2658.     pop     bx
  2659.     inc     bl
  2660.     jmp     .ef
  2661. .efd:
  2662.     mov     byte [pstate], 0
  2663.     ret
  2664.  
  2665. resolve_ent:
  2666.     cmp     byte [entbuf], '#'
  2667.     je      .num
  2668.     mov     bl, [ent_len]
  2669.     cmp     bl, 2
  2670.     je      .l2
  2671.     cmp     bl, 3
  2672.     je      .l3
  2673.     cmp     bl, 4
  2674.     je      .l4
  2675.     xor     ax, ax
  2676.     ret
  2677. .l2:
  2678.     cmp     word [entbuf], 'lt'
  2679.     jne     .n2
  2680.     mov     al, '<'
  2681.     ret
  2682. .n2:
  2683.     cmp     word [entbuf], 'gt'
  2684.     jne     .no
  2685.     mov     al, '>'
  2686.     ret
  2687. .l3:
  2688.     cmp     word [entbuf], 'am'
  2689.     jne     .no
  2690.     cmp     byte [entbuf + 2], 'p'
  2691.     jne     .no
  2692.     mov     al, '&'
  2693.     ret
  2694. .l4:
  2695.     cmp     word [entbuf], 'qu'
  2696.     jne     .a4
  2697.     cmp     word [entbuf + 2], 'ot'
  2698.     jne     .no
  2699.     mov     al, '"'
  2700.     ret
  2701. .a4:
  2702.     cmp     word [entbuf], 'ap'
  2703.     jne     .n4
  2704.     cmp     word [entbuf + 2], 'os'
  2705.     jne     .no
  2706.     mov     al, 39
  2707.     ret
  2708. .n4:
  2709.     cmp     word [entbuf], 'nb'
  2710.     jne     .no
  2711.     cmp     word [entbuf + 2], 'sp'
  2712.     jne     .no
  2713.     mov     al, ' '
  2714.     ret
  2715. .num:
  2716.     xor     ax, ax
  2717.     mov     cl, 1
  2718. .nl:
  2719.     cmp     cl, [ent_len]
  2720.     jae     .nd
  2721.     mov     bl, cl
  2722.     xor     bh, bh
  2723.     mov     dl, [entbuf + bx]
  2724.     cmp     dl, '0'
  2725.     jb      .no
  2726.     cmp     dl, '9'
  2727.     ja      .no
  2728.     push    cx
  2729.     sub     dl, '0'
  2730.     mov     bx, ax
  2731.     shl     ax, 1
  2732.     shl     ax, 1
  2733.     add     ax, bx
  2734.     shl     ax, 1
  2735.     xor     dh, dh
  2736.     add     ax, dx
  2737.     pop     cx
  2738.     inc     cl
  2739.     jmp     .nl
  2740. .nd:
  2741.     ret
  2742. .no:
  2743.     xor     al, al
  2744.     ret
  2745.  
  2746. ;---------------------------------------------------------------- utf-8 -> ascii
  2747. emit_text:
  2748.     cmp     al, 0x80
  2749.     jb      out_char
  2750.     mov     cl, [u_count]
  2751.     test    cl, cl
  2752.     jnz     .cont
  2753.     cmp     al, 0xC0
  2754.     jb      .bad
  2755.     cmp     al, 0xE0
  2756.     jb      .lead2
  2757.     cmp     al, 0xF0
  2758.     jb      .lead3
  2759.     cmp     al, 0xF8
  2760.     jb      .lead4
  2761.     jmp     .bad
  2762. .lead2:
  2763.     and     al, 0x1F
  2764.     xor     ah, ah
  2765.     mov     [u_cp], ax
  2766.     mov     byte [u_count], 1
  2767.     ret
  2768. .lead3:
  2769.     and     al, 0x0F
  2770.     xor     ah, ah
  2771.     mov     [u_cp], ax
  2772.     mov     byte [u_count], 2
  2773.     ret
  2774. .lead4:
  2775.     and     al, 0x07
  2776.     xor     ah, ah
  2777.     mov     [u_cp], ax
  2778.     mov     byte [u_count], 3
  2779.     ret
  2780. .cont:
  2781.     mov     cl, al
  2782.     and     cl, 0xC0
  2783.     cmp     cl, 0x80
  2784.     jne     .bad
  2785.     and     al, 0x3F
  2786.     xor     ah, ah
  2787.     mov     bx, [u_cp]
  2788.     shl     bx, 1
  2789.     shl     bx, 1
  2790.     shl     bx, 1
  2791.     shl     bx, 1
  2792.     shl     bx, 1
  2793.     shl     bx, 1
  2794.     or      bx, ax
  2795.     mov     [u_cp], bx
  2796.     dec     byte [u_count]
  2797.     jnz     .ret
  2798.     mov     ax, [u_cp]
  2799.     call    fold_out
  2800. .ret:
  2801.     ret
  2802. .bad:
  2803.     mov     byte [u_count], 0
  2804.     mov     al, '?'
  2805.     jmp     out_char
  2806.  
  2807. fold_out:
  2808.     cmp     ax, 0x80
  2809.     jb      out_char
  2810.     cmp     ax, 0x180
  2811.     jae     .punct
  2812.     sub     ax, 0x80
  2813.     mov     bx, ax
  2814.     mov     al, [foldtab + bx]
  2815.     jmp     out_char
  2816. .punct:
  2817.     cmp     ax, 0x2018
  2818.     je      .sq
  2819.     cmp     ax, 0x2019
  2820.     je      .sq
  2821.     cmp     ax, 0x201C
  2822.     je      .dq
  2823.     cmp     ax, 0x201D
  2824.     je      .dq
  2825.     cmp     ax, 0x2013
  2826.     je      .dash
  2827.     cmp     ax, 0x2014
  2828.     je      .dash
  2829.     cmp     ax, 0x2026
  2830.     je      .dots
  2831.     mov     al, '?'
  2832.     jmp     out_char
  2833. .sq:
  2834.     mov     al, 39
  2835.     jmp     out_char
  2836. .dq:
  2837.     mov     al, 34
  2838.     jmp     out_char
  2839. .dash:
  2840.     mov     al, '-'
  2841.     jmp     out_char
  2842. .dots:
  2843.     mov     al, '.'
  2844.     jmp     out_char
  2845.  
  2846. fold_to_di:
  2847.     cmp     al, 0x80
  2848.     jb      .asc
  2849.     mov     cl, [u_count]
  2850.     test    cl, cl
  2851.     jnz     .cont
  2852.     cmp     al, 0xC0
  2853.     jb      .bad
  2854.     cmp     al, 0xE0
  2855.     jb      .l2
  2856.     cmp     al, 0xF0
  2857.     jb      .l3
  2858.     cmp     al, 0xF8
  2859.     jb      .l4
  2860.     jmp     .bad
  2861. .l2:
  2862.     and     al, 0x1F
  2863.     xor     ah, ah
  2864.     mov     [u_cp], ax
  2865.     mov     byte [u_count], 1
  2866.     ret
  2867. .l3:
  2868.     and     al, 0x0F
  2869.     xor     ah, ah
  2870.     mov     [u_cp], ax
  2871.     mov     byte [u_count], 2
  2872.     ret
  2873. .l4:
  2874.     and     al, 0x07
  2875.     xor     ah, ah
  2876.     mov     [u_cp], ax
  2877.     mov     byte [u_count], 3
  2878.     ret
  2879. .cont:
  2880.     mov     cl, al
  2881.     and     cl, 0xC0
  2882.     cmp     cl, 0x80
  2883.     jne     .bad
  2884.     and     al, 0x3F
  2885.     xor     ah, ah
  2886.     mov     bx, [u_cp]
  2887.     shl     bx, 1
  2888.     shl     bx, 1
  2889.     shl     bx, 1
  2890.     shl     bx, 1
  2891.     shl     bx, 1
  2892.     shl     bx, 1
  2893.     or      bx, ax
  2894.     mov     [u_cp], bx
  2895.     dec     byte [u_count]
  2896.     jnz     .ret
  2897.     mov     ax, [u_cp]
  2898.     call    fold_char
  2899. .ret:
  2900.     ret
  2901. .asc:
  2902.     mov     [di], al
  2903.     inc     di
  2904.     ret
  2905. .bad:
  2906.     mov     byte [u_count], 0
  2907.     mov     al, '?'
  2908.     mov     [di], al
  2909.     inc     di
  2910.     ret
  2911.  
  2912. fold_char:
  2913.     cmp     ax, 0x80
  2914.     jb      .a
  2915.     cmp     ax, 0x180
  2916.     jae     .p
  2917.     sub     ax, 0x80
  2918.     mov     bx, ax
  2919.     mov     al, [foldtab + bx]
  2920.     jmp     .a
  2921. .p:
  2922.     cmp     ax, 0x2018
  2923.     je      .sq
  2924.     cmp     ax, 0x2019
  2925.     je      .sq
  2926.     cmp     ax, 0x201C
  2927.     je      .dq
  2928.     cmp     ax, 0x201D
  2929.     je      .dq
  2930.     cmp     ax, 0x2013
  2931.     je      .da
  2932.     cmp     ax, 0x2014
  2933.     je      .da
  2934.     cmp     ax, 0x2026
  2935.     je      .do
  2936.     mov     al, '?'
  2937.     jmp     .a
  2938. .sq:
  2939.     mov     al, 39
  2940.     jmp     .a
  2941. .dq:
  2942.     mov     al, 34
  2943.     jmp     .a
  2944. .da:
  2945.     mov     al, '-'
  2946.     jmp     .a
  2947. .do:
  2948.     mov     al, '.'
  2949. .a:
  2950.     mov     [di], al
  2951.     inc     di
  2952.     ret
  2953.  
  2954. ;---------------------------------------------------------------- word wrap
  2955. out_char:
  2956.     cmp     byte [out_on], 0
  2957.     jne     .go
  2958.     ret
  2959. .go:
  2960.     cmp     al, 13
  2961.     je      .drop
  2962.     cmp     al, 9
  2963.     je      .sp
  2964.     cmp     al, 10
  2965.     je      .nl
  2966.     cmp     al, ' '
  2967.     je      .sp
  2968.     mov     bl, [ww_len]
  2969.     cmp     bl, 100
  2970.     jae     .wordfull
  2971.     xor     bh, bh
  2972.     mov     [ww_buf + bx], al
  2973.     inc     byte [ww_len]
  2974.     ret
  2975. .wordfull:
  2976.     call    flush_word_raw
  2977.     mov     bl, [ww_len]
  2978.     xor     bh, bh
  2979.     mov     [ww_buf + bx], al
  2980.     inc     byte [ww_len]
  2981.     ret
  2982. .drop:
  2983.     ret
  2984. .nl:
  2985.     cmp     byte [ww_len], 0
  2986.     je      .nl2
  2987.     cmp     byte [ww_sp], 0
  2988.     je      .nl1
  2989.     cmp     word [ww_col], 0
  2990.     je      .nl1
  2991.     mov     al, ' '
  2992.     call    raw_out
  2993.     inc     word [ww_col]
  2994. .nl1:
  2995.     call    emit_word
  2996. .nl2:
  2997.     mov     al, 10
  2998.     call    raw_out
  2999.     mov     word [ww_col], 0
  3000.     mov     byte [ww_sp], 0
  3001.     ret
  3002. .sp:
  3003.     call    place_word
  3004.     mov     byte [ww_sp], 1
  3005.     ret
  3006.  
  3007. place_word:
  3008.     cmp     byte [ww_len], 0
  3009.     je      .ret
  3010.     cmp     word [ww_col], 0
  3011.     je      .atstart
  3012.     mov     bx, [ww_col]
  3013.     cmp     byte [ww_sp], 0
  3014.     je      .nopre
  3015.     inc     bx
  3016. .nopre:
  3017.     mov     al, [ww_len]
  3018.     xor     ah, ah
  3019.     add     bx, ax
  3020.     cmp     bx, [wrapw]
  3021.     ja      .wrap
  3022.     cmp     byte [ww_sp], 0
  3023.     je      .emit
  3024.     mov     al, ' '
  3025.     call    raw_out
  3026.     inc     word [ww_col]
  3027. .emit:
  3028.     call    emit_word
  3029.     ret
  3030. .wrap:
  3031.     mov     al, 10
  3032.     call    raw_out
  3033.     mov     word [ww_col], 0
  3034.     call    emit_word
  3035.     ret
  3036. .atstart:
  3037.     call    emit_word
  3038. .ret:
  3039.     ret
  3040.  
  3041. emit_word:
  3042.     xor     bx, bx
  3043. .l:
  3044.     cmp     bl, [ww_len]
  3045.     jae     .d
  3046.     mov     al, [ww_buf + bx]
  3047.     push    bx
  3048.     call    raw_out
  3049.     pop     bx
  3050.     inc     bl
  3051.     jmp     .l
  3052. .d:
  3053.     mov     al, [ww_len]
  3054.     xor     ah, ah
  3055.     add     [ww_col], ax
  3056.     mov     byte [ww_len], 0
  3057.     ret
  3058.  
  3059. flush_word_raw:
  3060.     cmp     byte [ww_sp], 0
  3061.     je      .f1
  3062.     cmp     word [ww_col], 0
  3063.     je      .f1
  3064.     mov     al, ' '
  3065.     call    raw_out
  3066.     inc     word [ww_col]
  3067. .f1:
  3068.     mov     al, [ww_len]
  3069.     xor     ah, ah
  3070.     mov     bx, [ww_col]
  3071.     add     bx, ax
  3072.     cmp     bx, [wrapw]
  3073.     jbe     .fit
  3074.     cmp     word [ww_col], 0
  3075.     je      .fit
  3076.     mov     al, 10
  3077.     call    raw_out
  3078.     mov     word [ww_col], 0
  3079. .fit:
  3080.     call    emit_word
  3081.     mov     byte [ww_sp], 0
  3082.     ret
  3083.  
  3084. raw_out:                    ; al -> outbuf; LF expanded to CRLF; page bookkeeping
  3085.     cmp     al, 10
  3086.     jne     .main
  3087.     push    ax
  3088.     mov     al, 13              ; CR first
  3089.     call    raw_store
  3090.     pop     ax
  3091. .main:
  3092.     call    raw_store
  3093.     cmp     byte [paging], 0
  3094.     je      .ret
  3095.     cmp     al, 10
  3096.     jne     .ret
  3097.     inc     word [wlines]
  3098.     mov     bx, [pagel]
  3099.     cmp     [wlines], bx
  3100.     jb      .ret
  3101.     mov     word [wlines], 0
  3102.     mov     ax, [pgused]
  3103.     cmp     ax, 1024
  3104.     jae     .ret
  3105.     shl     ax, 1
  3106.     shl     ax, 1
  3107.     mov     di, pgidx
  3108.     add     di, ax
  3109.     mov     bx, [tmppos_lo]
  3110.     add     bx, [outbuf_len]
  3111.     mov     [di], bx
  3112.     mov     bx, [tmppos_hi]
  3113.     adc     bx, 0
  3114.     mov     [di + 2], bx
  3115.     inc     word [pgused]
  3116. .ret:
  3117.     ret
  3118.  
  3119. raw_store:                  ; al -> outbuf; flush at OUTBUF_SIZE
  3120.     mov     bx, [outbuf_len]
  3121.     mov     [outbuf + bx], al
  3122.     inc     bx
  3123.     mov     [outbuf_len], bx
  3124.     cmp     bx, OUTBUF_SIZE
  3125.     jb      .ok
  3126.     call    flush_out
  3127. .ok:
  3128.     ret
  3129.  
  3130. wrap_finish:
  3131.     call    place_word
  3132.     cmp     word [ww_col], 0
  3133.     je      .wfd
  3134.     mov     al, 10
  3135.     call    raw_out
  3136.     mov     word [ww_col], 0
  3137. .wfd:
  3138.     ret
  3139.  
  3140. flush_out:
  3141.     push    bx
  3142.     push    cx
  3143.     push    dx
  3144.     push    si
  3145.     push    di
  3146.     mov     cx, [outbuf_len]
  3147.     test    cx, cx
  3148.     jz      .z
  3149.     mov     ah, 0x40
  3150.     mov     bx, [oh]
  3151.     mov     dx, outbuf
  3152.     int     0x21
  3153.     cmp     byte [paging], 0
  3154.     je      .nc
  3155.     add     [tmppos_lo], ax
  3156.     adc     word [tmppos_hi], 0
  3157. .nc:
  3158.     mov     word [outbuf_len], 0
  3159. .z:
  3160.     pop     di
  3161.     pop     si
  3162.     pop     dx
  3163.     pop     cx
  3164.     pop     bx
  3165.     ret
  3166.  
  3167. ;---------------------------------------------------------------- data
  3168. order:      db 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15
  3169. len_base:   dw 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59
  3170.             dw 67,83,99,115,131,163,195,227,258
  3171. len_extra:  db 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0
  3172. dist_base:  dw 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769
  3173.             dw 1025,1537,2049,3073,4097,6145,8193,12289,16385,24577
  3174. dist_extra: db 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13
  3175.  
  3176. msg_usage:  db "usage: epub2txt <file.epub> [-w W] [-p L] [-l | N]", 13, 10, "$"
  3177. msg_bad:    db "error: not a valid epub/zip", 13, 10, "$"
  3178. msg_nosec:  db "section not found", 13, 10, "$"
  3179. tmpname:    db "EPUB2TX.$$$", 0
  3180. p_msg:      db "-- more --$"
  3181. p_erase:    db 13, "             ", 13
  3182.  
  3183. foldtab:
  3184.     db '?','?',',','f','"','.','+','+','^','%','S','<','O','?','Z','?'
  3185.     db '?',39,39,34,34,'*','-','-','~','?','s','>','o','?','z','Y'
  3186.     db ' ','!','c','L','?','Y','|','?','?','?','a','<','?','?','?','?'
  3187.     db '?','+','2','3','?','u','?','.','?','1','o','>','?','?','?','?'
  3188.     db 'A','A','A','A','A','A','A','C','E','E','E','E','I','I','I','I'
  3189.     db 'D','N','O','O','O','O','O','*','O','U','U','U','U','Y','T','s'
  3190.     db 'a','a','a','a','a','a','a','c','e','e','e','e','i','i','i','i'
  3191.     db 'd','n','o','o','o','o','o','/','o','u','u','u','u','y','t','y'
  3192.     db 'A','a','A','a','A','a','C','c','C','c','C','c','C','c','D','d'
  3193.     db 'D','d','E','e','E','e','E','e','E','e','E','e','G','g','G','g'
  3194.     db 'G','g','G','g','H','h','H','h','I','i','I','i','I','i','I','i'
  3195.     db 'I','i','I','i','J','j','K','k','k','L','l','L','l','L','l','L'
  3196.     db 'l','L','l','N','n','N','n','N','n','n','N','n','O','o','O','o'
  3197.     db 'O','o','O','o','R','r','R','r','R','r','S','s','S','s','S','s'
  3198.     db 'S','s','T','t','T','t','T','t','U','u','U','u','U','u','U','u'
  3199.     db 'U','u','W','w','Y','y','Y','Z','z','Z','z','Z','z','Z','z','s'
  3200.  
  3201. ; globals
  3202. fh:            dw 0
  3203. oh:            dw 1
  3204. th:            dw 0
  3205. paging:        db 0
  3206. tmppos_lo:     dw 0
  3207. tmppos_hi:     dw 0
  3208. wlines:        dw 0
  3209. pgused:        dw 0
  3210. page:          dw 0
  3211. pages:         dw 0
  3212. linecnt:       dw 0
  3213. rdlen:         dw 0
  3214. seccnt:        dw 0
  3215. cursec:        dw 0
  3216. wrapw:         dw 78
  3217. pagel:         dw 24
  3218. respage:       dw 0
  3219. mode:          db 0
  3220. chapnum:       dw 0
  3221. chapfound:     db 0
  3222. seli:          dw 0
  3223. p_fsrc:        dw 0
  3224. p_sanc:        dw 0
  3225. p_panc:        dw 0
  3226. out_on:        db 1
  3227. anch_scan:     db 0
  3228. as_state:      db 0
  3229. as_q:          db 0
  3230. as_pos:        db 0
  3231. bs_state:      db 0
  3232. bs_q:          db 0
  3233. bs_pos:        db 0
  3234. ww_col:        dw 0
  3235. ww_len:        db 0
  3236. ww_sp:         db 0
  3237. ncx_mode:      db 0
  3238. nx_state:      db 0
  3239. nx_tagpos:     db 0
  3240. nx_close:      db 0
  3241. nx_inname:     db 0
  3242. nx_q:          db 0
  3243. nx_tpend:      db 0
  3244. nx_spos:       db 0
  3245. nx_apos:       db 0
  3246. nx_tpos:       db 0
  3247. tcount:        dw 0
  3248. ncxl:          dw 0
  3249. inbuf_len:     dw 0
  3250. inbuf_pos:     dw 0
  3251. cur_byte:      db 0
  3252. bit_idx:       db 0
  3253. wpos:          dw 0
  3254. outbuf_len:    dw 0
  3255. eocd_entries:  dw 0
  3256. eocd_cdoff_lo: dw 0
  3257. eocd_cdoff_hi: dw 0
  3258. cdpos_lo:      dw 0
  3259. cdpos_hi:      dw 0
  3260. nentries:      dw 0
  3261. scan_lo:       dw 0
  3262. scan_hi:       dw 0
  3263. chunklen:      dw 0
  3264. fsize_lo:      dw 0
  3265. fsize_hi:      dw 0
  3266. cand_lo:       dw 0
  3267. cand_hi:       dw 0
  3268. method:        dw 0
  3269. csz_lo:        dw 0
  3270. csz_hi:        dw 0
  3271. cnamelen:      dw 0
  3272. cextralen:     dw 0
  3273. ccommentlen:   dw 0
  3274. lhoff_lo:      dw 0
  3275. lhoff_hi:      dw 0
  3276. slen:          dw 0
  3277. snlen:         dw 0
  3278. bfinal:        dw 0
  3279. nlit:          dw 0
  3280. ndist:         dw 0
  3281. ncl:           dw 0
  3282. totl:          dw 0
  3283. hci:           dw 0
  3284. repn:          dw 0
  3285. mlen:          dw 0
  3286. mdist:         dw 0
  3287. mtmp:          dw 0
  3288. cidx:          db 0
  3289. u_count:       db 0
  3290. u_cp:          dw 0
  3291. h_code:        dw 0
  3292. h_first:       dw 0
  3293. h_index:       dw 0
  3294. bt_lens:       dw 0
  3295. bt_n:          dw 0
  3296. bt_cnt:        dw 0
  3297. bt_sym:        dw 0
  3298. pstate:        db 0
  3299. tag_len:       db 0
  3300. tag_nl:        db 0
  3301. ent_len:       db 0
  3302. sk_state:      db 0
  3303. sk_len:        db 0
  3304.  
  3305. ; buffers
  3306. iname:   times 64 db 0
  3307. oname:   times 8 db 0
  3308. nbuf:    times 250 db 0
  3309. lname:   times 250 db 0
  3310. cdbuf:   times 46 db 0
  3311. lhbuf:   times 30 db 0
  3312. eocdbuf: times 22 db 0
  3313. tagbuf:  times 8 db 0
  3314. entbuf:  times 8 db 0
  3315. skbuf:   times 8 db 0
  3316. nx_tagbuf: times 9 db 0
  3317. nx_tbuf:   times 64 db 0
  3318. nx_sbuf:   times 120 db 0
  3319. nx_abuf:   times 32 db 0
  3320. tocline: times 280 db 0
  3321. scratch: times 8 db 0
  3322. ww_buf:  times 100 db 0
  3323. pbuf:    times 128 db 0
  3324. bkmname: times 70 db 0
  3325. bkmline: times 20 db 0
  3326. tsrc:    times 3840 db 0
  3327. ttit:    times 2048 db 0
  3328. tanc:    times 1024 db 0
  3329. pgidx:   times 4096 db 0
  3330. secidx:  times 128 db 0
  3331. cl_lens: times 19 db 0
  3332. lens:    times 320 db 0
  3333. counts_lit:  times 16 dw 0
  3334. counts_dist: times 16 dw 0
  3335. counts_cl:   times 16 dw 0
  3336. offs:        times 16 dw 0
  3337. syms_lit:    times 288 dw 0
  3338. syms_dist:   times 32 dw 0
  3339. syms_cl:     times 19 dw 0
  3340. outbuf:  times OUTBUF_SIZE db 0
  3341. inbuf:   times INBUF_SIZE db 0
  3342. window:  times 32768 db 0
Add Comment
Please, Sign In to add comment