home *** CD-ROM | disk | FTP | other *** search
/ 8bitfiles.net/archives / archives.tar / archives / genie-commodore-file-library / Information / HACKING6E.SFX / hacking6e
Encoding:
Text File  |  1993-09-17  |  19.0 KB  |  699 lines

  1.   There is one small error that is possible with LZW because of the way
  2. the compressor defines strings. Consider the compression dictionary that
  3. has the following in it:
  4.  
  5.      node #   Code         Parent  character 
  6.               Value         code 
  7.      ------   ------        ------  ---------
  8.         65      65           n/a       A 
  9.        723     259            65       C
  10.       1262     260           259       U
  11.       2104     261           260       T
  12.       2506     262           261       E
  13.  
  14. Now if the compressor was to try to compress the string ACUTEACUTEA The
  15. compressor will find a match for the first five characters 'ACUTE' and
  16. will send a 262 to the output file. Then it will add the following entry
  17. to the dictionary:
  18.  
  19.       3099     263           262       A
  20.  
  21. Now it will try to compress the remaining characters, and it finds that
  22. it can compress the entire string with the code 263, but notice that the
  23. middle A, the one that was just added onto the end of the string 'ACUTE'
  24. was never sent to the output file. The decompressor will not have the
  25. code 263 defined in it's dictionary. The last code it will have defined
  26. will be 262. This problem is easily remedied though, when the
  27. decompressor does not have a code defined, it takes the first letter
  28. from the last phrase defined and tacks it onto the end of the last
  29. phrase. IE It takes the first letter (the A) from the phrase and adds it
  30. on to the end as code #263.
  31.  
  32. This particular implementation is fairly slow because it reads a byte
  33. and then writes one, it could be made much faster with some buffering.
  34. It is also limited to compressing and decompressing one file at a time
  35. and has no error checking capabilities. It is meant strictly to teach
  36. LZW compression, not provide a full fledged compressor.
  37.  
  38. And now for the code:
  39.   
  40. SYS 4000      ; sys 999 on a 64
  41. .DVO 9        ; or whatever drive used for output
  42. .ORG 2500
  43. .OBJ "LZW.ML"
  44.  
  45. TABLESIZE =5021     
  46.  
  47. ; THE TABLESIZE IS ACTUALLY 5021, ABOUT 20% LARGER THEN 4K. THIS GIVES
  48. ; THE HASHING ROUTINE SOME ROOM TO MOVE. IF THE TABLE WAS EXACTLY 4K
  49. ; THERE WOULD BE FREQUENT COLLISIONS WHERE DIFFERENT COMBINATIONS OF
  50. ; CHARACTERS WOULD HAVE THE SAME HASH ADDRESS. INCREASING THE TABLE SIZE
  51. ; REDUCES THE NUMBER OF COLLISIONS.
  52.  
  53. EOS =256          ; eos = End of stream This marks the end of file
  54.  
  55. FIRSTCODE =259
  56. MAXCODE =4096
  57.  
  58. BUMPCODE =257     ; Whenever a 257 is encountered by the decompressor it
  59.                   ; increases the number of bits it reads by 1
  60.  
  61. FLUSHCODE =258   
  62.  
  63. TABLEBASE =14336  ; The location that the dictionary is located at
  64.  
  65. DECODESTACK =9300 ; The location of the 4k LIFO stack
  66.  
  67. ; ORG = DECOMPRESS FILE
  68. ; ORG + 3 = COMPRESS FILE
  69.  
  70. JMP EXPANDFILE
  71.  
  72. ;********************************
  73. ; COMPRESSFILE
  74. ;********************************
  75.  
  76. COMPRESSFILE JSR INITDIC ; EMPTY THE DICTIONARY
  77. LDA #128
  78. STA BITMASK
  79. LDA #0
  80. STA RACK
  81. JSR GETCHAR      ; GET A CHAR FROM THE INPUT FILE
  82. STA STRINGCODE   ; INITIALIZE THE STRINGCODE (PARENT CODE)
  83. LDA #0
  84. STA STRINGCODE+1
  85. NEXTCHAR JSR GETCHAR
  86.          STA CHARACTER
  87.          JSR FINDNODE    ; FINDNODE CALCULATES THE HASHED LOCATION OF
  88.          LDA ($FE),Y     ; THE STRINGCODE AND CHARACTER  IN THE DICT.
  89.          INY             ; AND SETS $FE/$FF POINTING TO IT. IF THE ENTRY
  90.          AND ($FE),Y     ; HAS TWO 255 IN IT THEN IT IS EMPTY AND SHOULD
  91.          CMP #255        ; BE ADDED TO THE DICTIONARY.
  92.          BEQ ADDTODICT
  93.              LDA ($FE),Y     ; IT HAS A DEFINED PHRASE. STORE THE CODE VALUE IN
  94.              STA STRINGCODE+1; THE PARENT CODE
  95.              DEY
  96.              LDA ($FE),Y
  97.              STA STRINGCODE
  98.          JMP EOF
  99.          ADDTODICT LDY #0
  100.          - LDA NEXTCODE,Y
  101.            STA ($FE),Y
  102.            INY
  103.            CPY #5
  104.          BNE -
  105.          INC NEXTCODE             ; INCREASE THE NEXTCODE
  106.          BNE +
  107.              INC NEXTCODE+1
  108.          + JSR OUTPUT
  109.          LDA NEXTCODE+1          ; CHECK IF NEXTCODE=4096 IF SO THEN FLUSH THE
  110.          CMP #>MAXCODE           ; DICTIONARY AND START ANEW
  111.          BNE CHECKBUMP
  112.          LDA NEXTCODE
  113.          CMP #<MAXCODE
  114.          BNE CHECKBUMP
  115.          LDA #<FLUSHCODE        ; SEND THE FLUSH CODE TO THE COMPRESSED FILE SO
  116.          STA STRINGCODE         ; THE DECOMPRESSOR WILL KNOW TO FLUSH THE
  117.          LDA #>FLUSHCODE        ; DICTIONARY
  118.          STA STRINGCODE+1
  119.          JSR OUTPUT
  120.          JSR INITDIC
  121.          JMP CHECKEOF
  122.          CHECKBUMP LDA NEXTBUMP+1
  123.          CMP NEXTCODE+1         ; CHECKBUMP CHECK TO SEE IF THE NEXTCODE HAS
  124.          BNE CHECKEOF        ; REACHED THE MAXIMUM VALUE FOR THE CURRENT
  125.          LDA NEXTBUMP                    ; NUMBER OF BITS BEING OUTPUT.
  126.          CMP NEXTCODE        ; FOR     X  BITS     NEXTCODE HAS Y PHRASES
  127.          BNE CHECKEOF        ;        --------     -----------------------
  128.          LDA #>BUMPCODE      ;           9                 511
  129.          STA STRINGCODE+1    ;          10                1023
  130.          LDA #<BUMPCODE      ;          11                2047
  131.          STA STRINGCODE      ;          12                4095
  132.          JSR OUTPUT
  133.          INC CURRENTBITS
  134.          ASL NEXTBUMP
  135.          ROL NEXTBUMP+1
  136.          CHECKEOF LDA #0
  137.          STA STRINGCODE+1
  138.          LDA CHARACTER
  139.          STA STRINGCODE
  140.          EOF LDA 144
  141.          BNE DONE
  142. JMP NEXTCHAR
  143. DONE JSR OUTPUT
  144. LDA #>EOS               ; SEND A 256 TO INDICATE EOF
  145. STA STRINGCODE+1
  146. LDA #<EOS
  147. STA STRINGCODE
  148. JSR OUTPUT
  149. LDA BITMASK
  150. BEQ +
  151.     JSR $FFCC
  152.     LDX #3
  153.     JSR $FFC9
  154.     LDA RACK              ; SEND WHAT BITS WEREN'T SEND WHEN OUTPUT
  155.     JSR $FFD2
  156. + JSR $FFCC
  157. LDA #3
  158. JSR $FFC3
  159. LDA #2
  160. JMP $FFC3
  161.  
  162. ;**********************************
  163. ; INITDIC
  164. ; INITIALIZES THE DICTIONARY, SETS
  165. ; THE NUMBER OF BITS TO 9
  166. ;**********************************
  167.  
  168. INITDIC LDA #9
  169. STA CURRENTBITS
  170. LDA #>FIRSTCODE
  171. STA NEXTCODE+1
  172. LDA #<FIRSTCODE
  173. STA NEXTCODE
  174. LDA #>512
  175. STA NEXTBUMP+1
  176. LDA #<512
  177. STA NEXTBUMP
  178. LDA #<TABLEBASE
  179. STA $FE
  180. LDA #>TABLEBASE
  181. STA $FF
  182. LDA #<TABLESIZE
  183. STA $FC
  184. LDA #>TABLESIZE
  185. STA $FD
  186. - LDY #0
  187.   LDA #255      ; ALL THE CODE VALUES ARE INIT TO 255+256*255
  188.   STA ($FE),Y   ; OR -1 IN TWO COMPLEMENT
  189.   INY
  190.   STA ($FE),Y
  191.   CLC
  192.   LDA #5        ; EACH ENTRY IN THE TABLE TAKES 5 BYTES
  193.   ADC $FE
  194.   STA $FE
  195.   BCC +
  196.       INC $FF
  197.   + LDA $FC
  198.   BNE +
  199.       DEC $FD
  200.   + DEC $FC
  201.   LDA $FD
  202.   ORA $FC
  203. BNE -
  204. RTS
  205.  
  206. ;************************************
  207. ; GETCHAR
  208. ;************************************
  209.  
  210. GETCHAR JSR $FFCC
  211. LDX #2
  212. JSR $FFC6
  213. JMP $FFCF
  214.  
  215. ;************************************
  216. ; OUTPUT
  217. ;************************************
  218.  
  219. OUTPUT LDA #0      ; THE NUMBER OF BITS OUTPUT CAN BE OF A VARIABLE
  220. STA MASK+1         ; LENGTH,SO THE BITS ARE ACCUMULATED TO A BYTE IS
  221. LDA #1             ; FULL AND THEN IT IS SENT TO THE OUTPUT FILE
  222. LDX CURRENTBITS
  223. DEX
  224. - ASL
  225.   ROL MASK+1
  226.   DEX
  227. BNE -
  228. STA MASK
  229. MASKDONE LDA MASK
  230. ORA MASK+1
  231. BNE +
  232.     RTS
  233. + LDA MASK
  234. AND STRINGCODE
  235. STA 3
  236. LDA MASK+1
  237. AND STRINGCODE+1
  238. ORA 3
  239. BEQ NOBITON
  240.     LDA RACK
  241.     ORA BITMASK
  242.     STA RACK
  243. NOBITON LSR BITMASK
  244. LDA BITMASK
  245. BNE +
  246.     JSR $FFCC
  247.     LDX #3
  248.     JSR $FFC9
  249.     LDA RACK
  250.     JSR $FFD2
  251.     LDA #0
  252.     STA RACK
  253.     LDA #128
  254.     STA BITMASK
  255. + LSR MASK+1
  256. ROR MASK
  257. JMP MASKDONE
  258.  
  259. ;******************************
  260. ; FINDNODE
  261. ; THIS SEARCHES THE DICTIONARY TILL IT FINDS A PARENT NODE THAT MATCHES
  262. ; THE STRINGCODE AND A CHILD NODE THAT MATCHES THE CHARACTER OR A EMPTY
  263. ; NODE.
  264. ;*******************************
  265.  
  266. ; THE HASHING FUNCTION - THE HASHING FUNCTION IS NEEDED BECAUSE
  267. ; THERE ARE 4096 X 4096 (16 MILLION) DIFFERENT COMBINATIONS OF
  268. ; CHARACTER AND STRINGCODE. BY MULTIPLYING THE CHARACTER AND STRINGCODE
  269. ; IN A FORMULA WE CAN DEVELOP OF METHOD OF FINDING THEM IN THE
  270. ; DICTIONARY. IF THE STRINGCODE AND CHARACTER IN THE DICTIONARY
  271. ; DON'T MATCH THE ONES WE ARE LOOKING FOR WE CALCULATE AN OFFSET
  272. ; AND SEARCH THE DICTIONARY FOR THE RIGHT MATCH OR A EMPTY
  273. ; SPACE IS FOUND. IF AN EMPTY SPACE IS FOUND THEN THAT CHARACTER AND
  274. ; STRINGCODE COMBINATION IS NOT IN THE DICTIONARY
  275.  
  276. FINDNODE LDA #0
  277. STA INDEX+1
  278. LDA CHARACTER     ; HERE THE HASHING FUNCTION IS APPLIED TO THE
  279. ASL               ; CHARACTER AND THE STRING CODE. FOR THOSE WHO
  280. ROL INDEX+1       ; CARE THE HASHING FORMULA IS:
  281. EOR STRINGCODE    ; (CHARACTER << 1) ^ STRINGCODE
  282. STA INDEX         ; FIND NODE WILL LOOP TILL IT FINDS A NODE
  283. LDA INDEX+1       ; THAT HAS A EMPTY NODE OR MATCHES THE CURRENT
  284. EOR STRINGCODE+1  ; PARENT CODE AND CHARACTER
  285. STA INDEX+1
  286. ORA INDEX 
  287. BNE +
  288.     LDX #1
  289.     STX OFFSET
  290.     DEX
  291.     STX OFFSET+1
  292.     JMP FOREVELOOP
  293. + SEC
  294. LDA #<TABLESIZE
  295. SBC INDEX
  296. STA OFFSET
  297. LDA #>TABLESIZE
  298. SBC INDEX+1
  299. STA OFFSET+1
  300.  
  301. FOREVELOOP JSR CALCULATE     
  302.            LDY #0
  303.            LDA ($FE),Y
  304.            INY
  305.            AND ($FE),Y
  306.            CMP #255
  307.            BNE +
  308.                LDY #0
  309.                RTS
  310.            + INY
  311.            - LDA ($FE),Y
  312.              CMP STRINGCODE-2,Y
  313.              BNE +
  314.              INY
  315.              CPY #5
  316.              BNE -
  317.           LDY #0
  318.           RTS
  319.           + SEC
  320.           LDA INDEX
  321.           SBC OFFSET
  322.           STA INDEX
  323.           LDA INDEX+1
  324.           SBC OFFSET+1
  325.           STA INDEX+1
  326.           AND #128
  327.           BEQ FOREVELOOP
  328.           CLC
  329.           LDA #<TABLESIZE
  330.           ADC INDEX
  331.           STA INDEX
  332.           LDA #>TABLESIZE
  333.           ADC INDEX+1
  334.           STA INDEX+1
  335. JMP FOREVELOOP
  336.  
  337. ;***************************
  338. ; CALCULATE
  339. ; TAKES THE VALUE IN INDEX AND CALCULATES ITS LOCATION IN THE DICTIONARY
  340. ;****************************
  341.  
  342. CALCULATE LDA INDEX
  343. STA $FE
  344. LDA INDEX+1
  345. STA $FF
  346. ASL $FE
  347. ROL $FF
  348. ASL $FE
  349. ROL $FF
  350. CLC
  351. LDA INDEX
  352. ADC $FE
  353. STA $FE
  354. LDA INDEX+1
  355. ADC $FF
  356. STA $FF
  357. CLC
  358. LDA #<TABLEBASE
  359. ADC $FE
  360. STA $FE
  361. LDA #>TABLEBASE
  362. ADC $FF
  363. STA $FF
  364. LDY #0
  365. RTS
  366.  
  367. ;******************************
  368. ; DECODESTRING
  369. ;******************************
  370.  
  371. DECODESTRING TYA   ; DECODESTRING PUTS THE STRING ON THE STACK
  372. STA COUNT          ; IN A LIFO FASHION.
  373. LDX #>DECODESTACK
  374. CLC
  375. ADC #<DECODESTACK
  376. STA $FC
  377. STX $FD
  378. LDA #0
  379. STA COUNT+1
  380. - LDA INDEX+1
  381.   BEQ +
  382.   JSR CALCULATE
  383.   LDY #4
  384.   LDA ($FE),Y
  385.   LDY #0
  386.   STA ($FC),Y
  387.   LDY #2
  388.   LDA ($FE),Y
  389.   STA INDEX
  390.   INY
  391.   LDA ($FE),Y
  392.   STA INDEX+1
  393.   JSR INFC
  394. JMP -
  395. + LDY #0
  396. LDA INDEX
  397. STA ($FC),Y
  398. INC COUNT
  399. BNE +
  400.     INC COUNT+1
  401. + RTS
  402.  
  403. ;******************************
  404. ; INPUT
  405. ;******************************
  406.  
  407. INPUT LDA #0  ; THE INPUT ROUTINES IS USED BY THE DECOMPRESSOR
  408. STA MASK+1    ; TO READ IN THE VARIABLE LENGTH CODES
  409. STA RETURN
  410. STA RETURN+1
  411. LDA #1
  412. LDX CURRENTBITS
  413. DEX
  414. - ASL
  415.   ROL MASK+1
  416.   DEX
  417. BNE -
  418. STA MASK
  419. - LDA MASK
  420.   ORA MASK+1
  421.   BEQ INPUTDONE
  422.   LDA BITMASK
  423.   BPL +
  424.       JSR GETCHAR
  425.       STA RACK
  426.   + LDA RACK
  427.   AND BITMASK
  428.   BEQ +
  429.       LDA MASK
  430.       ORA RETURN
  431.       STA RETURN
  432.       LDA MASK+1
  433.       ORA RETURN+1
  434.       STA RETURN+1
  435.   + LSR MASK+1
  436.   ROR MASK
  437.   LSR BITMASK
  438.   LDA BITMASK
  439.   BNE +
  440.       LDA #128
  441.       STA BITMASK
  442. + JMP -
  443. INPUTDONE RTS
  444.  
  445. ;*******************************
  446. ; EXPANDFILE
  447. ; WHERE THE DECOMPRESSION IS DONE
  448. ;*******************************
  449.  
  450. EXPANDFILE LDA #0
  451. STA RACK
  452. LDA #128
  453. STA BITMASK
  454. START JSR INITDIC
  455. JSR INPUT
  456. LDA RETURN+1
  457. STA OLDCODE+1       ; Save the first character in OLDCODE
  458. LDA RETURN
  459. STA CHARACTER
  460. STA OLDCODE
  461. CMP #<EOS
  462. BNE +
  463.     LDA RETURN+1    ; If return = EOS (256) then all done 
  464.     CMP #>EOS
  465.     BNE +
  466.     JMP CLOSE
  467. + JSR $FFCC
  468. LDX #3
  469. JSR $FFC9
  470. LDA OLDCODE         ; Send oldcode to the output file
  471. JSR $FFD2
  472. NEXT JSR INPUT
  473.      LDA RETURN
  474.      STA NEWCODE
  475.      LDA RETURN+1
  476.      STA NEWCODE+1
  477.      CMP #1         ; All of the special codes Flushcode,BumpCode & EOS
  478.      BNE ++         ; Have 1 for a MSB.
  479.          LDA NEWCODE
  480.          CMP #<BUMPCODE
  481.          BNE +
  482.              INC CURRENTBITS
  483.              JMP NEXT
  484.          + CMP #<FLUSHCODE
  485.          BEQ START
  486.          CMP #<EOS
  487.          BNE +
  488.          JMP CLOSE
  489.      + SEC          ; Here we compare the newcode just read in to the
  490.      LDA NEWCODE    ; next code. If newcode is greater than it is a 
  491.      SBC NEXTCODE   ; ACUTEACUTEA situation and must be handle differently.
  492.      STA 3
  493.      LDA NEWCODE+1 
  494.      SBC NEXTCODE+1
  495.      ORA 3
  496.      BCC +
  497.          LDA CHARACTER
  498.          STA DECODESTACK
  499.          LDA OLDCODE
  500.          STA INDEX
  501.          LDA OLDCODE+1
  502.          STA INDEX+1
  503.          LDY #1
  504.          BNE ++
  505.      + LDA NEWCODE  ; Point index to newcode spot in the dictionary   
  506.      STA INDEX      ; So DECODESTRING has a place to start
  507.      LDA NEWCODE+1
  508.      STA INDEX+1
  509.      LDY #0
  510.      + JSR DECODESTRING
  511.      LDY #0
  512.      LDA ($FC),Y
  513.      STA CHARACTER
  514.      INC $FC
  515.      BNE +
  516.          INC $FD
  517.      + JSR $FFCC
  518.      LDX #3
  519.      JSR $FFC9
  520.      L1 LDA COUNT+1  ; Count contains the number of characters on the stack
  521.         ORA COUNT
  522.         BEQ +       
  523.         JSR DECFC
  524.         LDY #0
  525.         LDA ($FC),Y
  526.         JSR $FFD2
  527.      JMP L1
  528.      + LDA NEXTCODE  ; Calculate the spot in the dictionary for the string
  529.      STA INDEX       ; that was just entered.
  530.      LDA NEXTCODE+1
  531.      STA INDEX+1
  532.      JSR CALCULATE
  533.      LDY #2          ; The last character read in is tacked onto the end
  534.      LDA OLDCODE     ; of the string that was just taken off the stack
  535.      STA ($FE),Y     ; The nextcode is then incremented to prepare for the 
  536.      INY             ; next entry.
  537.      LDA OLDCODE+1
  538.      STA ($FE),Y
  539.      INY
  540.      LDA CHARACTER
  541.      STA ($FE),Y
  542.      INC NEXTCODE
  543.      BNE +
  544.          INC NEXTCODE+1
  545.      + LDA NEWCODE
  546.      STA OLDCODE
  547.      LDA NEWCODE+1
  548.      STA OLDCODE+1
  549. JMP NEXT
  550. CLOSE JSR $FFCC
  551. LDA #2
  552. JSR $FFC3
  553. LDA #3
  554. JMP $FFC3
  555.  
  556. DECFC LDA $FC
  557. BNE +
  558.     DEC $FD
  559. + DEC $FC
  560. LDA COUNT
  561. BNE +
  562.     DEC COUNT+1
  563. + DEC COUNT
  564. RTS
  565. INFC INC $FC
  566. BNE +
  567.     INC $FD
  568. + INC COUNT
  569. BNE +
  570.     INC COUNT+1
  571. + RTS
  572.  
  573. NEXTCODE .WOR 0
  574. STRINGCODE .WOR 0
  575. CHARACTER .BYT 0
  576. NEXTBUMP .WOR 0
  577. CURRENTBITS .BYT 0
  578. RACK .BYT 0
  579. BITMASK .BYT 0
  580. MASK .WOR 0
  581. INDEX .WOR 0
  582. OFFSET .WOR 0
  583. RETURN .WOR 0
  584. COUNT .WOR 0
  585. NEWCODE .WOR 0
  586. OLDCODE .WOR 0
  587. TEST .BYT 0
  588.  
  589. TO DRIVE THE ML I WROTE THIS SMALL BASIC PROGRAM. NOTE THAT CHANNEL TWO IS
  590. ALWAYS THE INPUT AND CHANNEL THREE IS ALWAYS THE OUTPUT. EX AND CO MAY BE
  591. CHANGED TO SUIT WHATEVER LOCATIONS THE PROGRAM IS REASSEMBLED AT.
  592.  
  593. 1 IFA=.THENA=1:LOAD"LZW.ML",PEEK(186),1
  594. 10 EX=2500:CO=2503
  595. 15 PRINT"[E]XPAND OR [C]OMPRESS?"
  596. 20 GETA$:IFA$<>"C"ANDA$<>"E"THEN20
  597. 30 INPUT"NAME OF INPUT FILE";FI$:IFLEN(FI$)=.THEN30
  598. 40 INPUT"NAME OF OUTPUT FILE";FO$:IFLEN(FO$)=.THEN40
  599. 50 OPEN2,9,2,FI$+",P,R":OPEN3,9,3,FO$+",P,W"
  600. 60 IFA$="E"THENSYSEX
  601. 70 IFA$="C"THENSYSCO
  602. 80 END
  603.  
  604. For those interested in learning more about data
  605. compression/decompression I recommend the book 'The Data Compression
  606. Book' written by Mark Nelson. I learned a great deal from reading this
  607. book. It explains all of the major data compression methods. (huffman
  608. coding, dictionary type compression such as LZW, arithmatic coding,
  609. speech compression and lossy graphics compression)
  610.  
  611. Questions or comments are welcome, they may be directed to me at :
  612.  
  613. Internet   : Blucier@ersys.edmonton.ab.ca
  614. Genie      : b.lucier1
  615.  
  616. ------------------------------------------------------------------------------
  617.  
  618. begin 644 lzw.ml
  619. MQ`E,N`P@GPJI@(WT#:D`C?,-(.L*C>T-J0"-[@T@ZPJ-[PT@6`NQ_L@Q_LG_
  620. M\`ZQ_HWN#8BQ_HWM#4QH"J``N>L-D?[(P`70]N[K#=`#[NP-(/8*K>P-R1#0
  621. M&JWK#<D`T!.I`HWM#:D!C>X-(/8*()\*3%T*K?$-S>P-T!ZM\`W-ZPW0%JD!
  622. MC>X-J0&-[0T@]@KN\@T.\`TN\0VI`(WN#:WO#8WM#:60T`-,WPD@]@JI`8WN
  623. M#:D`C>T-(/8*K?0-\`X@S/^B`R#)_ZWS#2#2_R#,_ZD#(,/_J0),P_^I"8WR
  624. M#:D!C>P-J0.-ZPVI`HWQ#:D`C?`-J0"%_JDXA?^IG87\J1.%_:``J?^1_LB1
  625. M_ABI!67^A?Z0`N;_I?S0`L;]QORE_07\T-Y@(,S_H@(@QO],S_^I`(WV#:D!
  626. MKO(-R@HN]@W*T/F-]0VM]0T-]@W0`6"M]0TM[0V%`ZWV#2WN#04#\`FM\PT-
  627. M]`V-\PU.]`VM]`W0&"#,_Z(#(,G_K?,-(-+_J0"-\PVI@(WT#4[V#6[U#4P+
  628. M"ZD`C?@-K>\-"B[X#4WM#8WW#:WX#4WN#8WX#=`1K?<-T`RB`8[Y#<J.^@U,
  629. MEPLXJ9WM]PV-^0VI$^WX#8WZ#2#C"Z``L?[(,?[)_]`#H`!@R+'^V>L-T`C(
  630. MP`70]*``8#BM]PWM^0V-]PVM^`WM^@V-^`TI@/`1&*F=;?<-C?<-J1-M^`V-
  631. M^`U,EPNM]PV%_JWX#87_!OXF_P;^)O\8K?<-9?Z%_JWX#67_A?\8J0!E_H7^
  632. MJ3AE_X7_H`!@F(W]#:(D&&E4A?R&_:D`C?X-K?@-\!X@XPN@!+'^H`"1_*`"
  633. ML?Z-]PW(L?Z-^`T@W`U,)@R@`*WW#9'\[OT-T`/N_@U@J0"-]@V-^PV-_`VI
  634. M`:[R#<H*+O8-RM#YC?4-K?4-#?8-\#NM]`T0!B#K"HWS#:WS#2WT#?`2K?4-
  635. M#?L-C?L-K?8-#?P-C?P-3O8-;O4-3O0-K?0-T`6I@(WT#4QT#&"I`(WS#:F`
  636. MC?0-()\*(%D,K?P-C0(.K?L-C>\-C0$.R0#0"JW\#<D!T`-,NPT@S/^B`R#)
  637. M_ZT!#B#2_R!9#*W[#8W_#:W\#8T`#LD!T!BM_PW)`=`&[O(-3/,,R0+PJ\D`
  638. MT`-,NPTXK?\-[>L-A0.M``[M[`T%`Y`6K>\-C50DK0$.C?<-K0(.C?@-H`'0
  639. M#JW_#8WW#:T`#HWX#:``(!0,H`"Q_(WO#>;\T`+F_2#,_Z(#(,G_K?X-#?T-
  640. M\`T@R`V@`+'\(-+_3&T-K>L-C?<-K>P-C?@-(.,+H`*M`0Z1_LBM`@Z1_LBM
  641. M[PV1_N[K#=`#[NP-K?\-C0$.K0`.C0(.3/,,(,S_J0(@P_^I`TS#_Z7\T`+&
  642. M_<;\K?T-T`/._@W._0U@YOS0`N;][OT-T`/N_@U@````````````````````
  643. *````````````````
  644. `
  645. end
  646.  
  647. crc32 for lzw.ml = 2460116527
  648.  
  649. begin 644 lzw.bas
  650. M`0@<"`$`BT&R+J=!LC$ZDR),6E<N34PB+#DL,0`P"`H`15BR,C4P,#I#3[(R
  651. M-3`S`%`(#P"9(I,219)84$%.1"!/4B`20Y)/35!215-3/R(`;`@4`*%!)#J+
  652. M022SL2)#(J]!)+.Q(D4BIS(P`)<('@"%(DY!344@3T8@24Y0550@1DE,12([
  653. M1DDD.HO#*$9))"FR+J<S,`##""@`A2).04U%($]&($]55%!55"!&24Q%(CM&
  654. M3R0ZB\,H1D\D*;(NIS0P`.L(,@"?,BPY+#(L1DDDJB(L4RQ2(CJ?,RPY+#,L
  655. M1D\DJB(L4"Q7(@#["#P`BT$DLB)%(J>>15@`"PE&`(M!)+(B0R*GGD-/````
  656. `
  657. end
  658.  
  659. crc32 for lzw.bas = 100674089
  660.  
  661. ===============================================================================
  662. THREE-KEY ROLLOVER for the C-128 and C-64.
  663. by Craig Bruce  <csbruce@neumann.uwaterloo.ca>
  664.  
  665. 1. INTRODUCTION
  666.  
  667. This article examines a three-key rollover mechanism for the keyboards of the
  668. C-128 and C-64 and presents Kernal-wedge implementations for both machines.
  669. Webster's doesn't seem to know, so I'll tell you that this means that the
  670. machine will act sensibly if you are holding down one key and then press
  671. another without releasing the first (or even press a third key while holding
  672. down two others).  This is useful to fast touch typers.  In fact, fast typing
  673. without rollover can be quite annoying; you get a lot of missing letters.
  674.  
  675. Another annoying property of the kernel keyscanning is joystick interference.
  676. If you move the joystick plugged into port #1, you will notice that some junk
  677. keystrokes result.  The keyscanners here eliminate this problem by simply
  678. checking if the joystick is pressed and ignoring the keyboard if it is.
  679.  
  680. The reason that a 3-key rollover is implemented instead of the more general
  681. N-key rollover is that scanning the keyboard becomes more and more unreliable
  682. as more keys are held down.  Key "shaddows" begin to appear to make it look
  683. like you are holding down a certain key when you really are not.  So, by
  684. limiting the number of keys scanned to 3, some of this can be avoided.  You
  685. will get strange results if you hold down more than three keys at a time, and
  686. even sometimes when holding down 3 or less.  The "shift" keys (Shift,
  687. Commodore, Control, Alternate, and CapsLock) don't count in the 3 keys of
  688. rollover, but they do make the keyboard harder to read correctly.
  689. Fortunately, three keys will allow you to type words like "AND" and "THE"
  690. without releasing any keys.
  691.  
  692. 2. USER GUIDE
  693.  
  694. Using these utilities is really easy - you just type away like normal.  To
  695. install the C-128 version, enter:
  696.  
  697. BOOT "KEYSCAN128"
  698.  
  699.