home *** CD-ROM | disk | FTP | other *** search
/ Brotikasten / BROTCD01.iso / texte / cbmhack6.txt < prev    next >
Text File  |  1995-08-20  |  117KB  |  2,868 lines

  1.                    ########
  2.              ##################
  3.          ######            ######
  4.       #####
  5.     #####  ####  ####      ##      #####   ####  ####  ####  ####  ####   #####
  6.   #####    ##    ##      ####    ##   ##   ##  ###     ##    ####  ##   ##   ##
  7.  #####    ########     ##  ##   ##        #####       ##    ## ## ##   ##
  8. #####    ##    ##    ########  ##   ##   ##  ###     ##    ##  ####   ##   ##
  9. #####  ####  ####  ####  ####  #####   ####  ####  ####  ####  ####   ######
  10. #####                                                                    ##
  11.  ######            ######           Issue #6
  12.    ##################            Sept. 5, 1993
  13.        ########
  14.  
  15. ---------------------------------------------------------------------(v1.0)---
  16. Editor's Notes:
  17. by Craig Taylor
  18.  
  19.   School, 2 jobs, a play, and other work has seriously restricted the amount
  20.   of time that I am able to devote to Commodore Hacking. What I am looking at
  21.   doing now is to stop writing articles for Commodore Hacking and hoping that
  22.   I'll still have enough time to write these notes, and organize and pull all
  23.   the other articles by other contributors together. The two articles I
  24.   had hoped to include in this issue : the one about multi-tasking and
  25.   about the 1351 have again been delayed. I've decided to go ahead and
  26.   release issue 6 and hopefully will have them in the next issue.
  27.   (Remember: You get what you pay for.. *smile*)
  28.  
  29.   As always, Commodore Hacking is constantly looking for articles, notes,
  30.   short little programs, what-not on any side of the Commodore 64 and 128 -
  31.   primarily on the technical or hardware side. If you think you have something
  32.   in mind, or already written then feel free to drop me a line letting me
  33.   know.
  34.  
  35.   In regards to several queries recently about reprinting individual articles
  36.   that have appeared in Commodore Hacking. You may reprint Commodore Hacking
  37.   and redistribute in whole, freely. For use of individual articles you _must_
  38.   contanct the individual author as they still retain rights to it. Please see
  39.   the legal notice / mumbo below.
  40.  
  41.   I recently recieved some mail from wolfgang@halcyon regarding a disk
  42.   magazine that he was in the process of starting and he has written the
  43.   following preview of a disk magazine that looks to be exciting:
  44.  
  45.     "_Scenery_, a new disk-magazine focusing on the american and
  46.     european demo scenes, will soon be available for download at a
  47.     site/BBS near you! With articles on everything from coding to
  48.     instrument synthesis to art, _Scenery_ will be the definitive word
  49.     when it comes to creating a demo, or simply coding in general.
  50.     Articles are being written by some of the top names in the scene,
  51.     and promise to help everybody from the Basic Baby to the ML Mogul!
  52.     Set to be released mid-August, _Scenery_ will hopefully be a worthy
  53.     edition to the likes of Coder's World and C=Hacking.  I am making
  54.     the magazine available on various Internet sites, so look for it. We
  55.     are always on the lookout for art, music, and coding talent, and if
  56.     you'd be interested in submitting an article for publication, or
  57.     simply have a question or comment, please mail me at
  58.     'wolfgang@halcyon.com'. Thanks.. and see you on the Net!"
  59.  
  60. ================================================================================
  61.  
  62.   Please note that this issue and prior ones are available via anonymous FTP
  63.   from ccosun.caltech.edu under pub/rknop/hacking.mag and via a mailserver
  64.   which documentation can be obtained by sending mail to
  65.   "duck@pembvax1.pembroke.edu" with a subject line of "mailserver" and the
  66.   line "help" in the body of the message.
  67.  
  68. ================================================================================
  69.  
  70.   NOTICE: Permission is granted to re-distrubte this "net-magazine", in
  71.   whole, freely for non-profit use. However, please contact individual 
  72.   authors for permission to publish or re-distribute articles seperately.
  73.   A charge of no greather than 5 US dollars or equivlent may be charged for 
  74.   library service / diskette costs for this "net-magazine".
  75.  
  76. ================================================================================
  77. In This Issue:
  78.  
  79. DYCP - Horizontal Scrolling
  80.  
  81. DYCP - is a name for a horizontal scroller, where characters go smoothly
  82. up and down during their voyage from right to left. One possibility is a
  83. scroll with 8 characters - one character per sprite, but a real demo coder
  84. won't be satisfied with that.
  85.  
  86. Opening the borders
  87.  
  88. VIC has many features and transparent borders are one of them. You can not
  89. make characters appear in the border, but sprites are displayed in the
  90. border too.
  91.  
  92. A Heavy Duty Power supply for the C-64
  93.  
  94. This article describes how to build a heavier duty power supply for your
  95. Commodore 64 computer and includes a full schematic in GeoPaint format.
  96.  
  97. LZW Compression
  98.  
  99. LZW is perhaps the most widely used form of data compression today. It
  100. is simple to implement and achieves very decent compression at a fairly
  101. quick pace. LZW is used in PKZIP (shrink),PKARC (crunch), gifs,V.42bis
  102. and unix's compress. This article will attempt to explain how the
  103. compression works with a short example and 6502 source code in Buddy
  104. format.
  105.  
  106. THREE-KEY ROLLOVER for the C-128 and C-64.
  107.  
  108. This article examines how a three-key rollover mechanism works for the
  109. keyboards of the C=128 and C=64 and will present Kernal-wedge
  110. implementations for both machines. Webster's doesn't seem to know, so I'll
  111. tell you that this means that the machine will act sensibly if you are
  112. holding down one key and then press another without releasing the first.
  113. This will be useful to fast touch typers.
  114.  
  115. ================================================================================
  116. The Demo Corner: DYCP - Horizontal Scrolling
  117. by Pasi 'Albert' Ojala (po87553@cs.tut.fi or albert@cc.tut.fi))
  118.         Written: 16-May-91 Translation 02-Jun-92
  119.  
  120.     DYCP - too many sprites !?
  121.     --------------------------
  122.  
  123. DYCP - Different Y Character Position - is a name for a horizontal scroller,
  124. where characters go smoothly up and down during their voyage from right to
  125. left. One possibility is a scroll with 8 characters - one character in each
  126. sprite, but a real demo coder won't be satisfied with that.
  127.  
  128. Demo coders thought that it looks good to make the scrolling text change its
  129. vertical position in the same time it proceeded from the right side of the
  130. screen to the left. The only problem is that there is only eight sprites
  131. and that is not even nearly enough to satisfy the requirements needed for
  132. great look. So the only way is to use screen and somehow plot the text in
  133. graphics, because character columns can not be scrolled individually.
  134. Plotting the characters take absolutely too much time, because you have to
  135. handle each byte seperately and the graphics bitmap must be cleared too.
  136.  
  137.  
  138. _Character hack_
  139.  
  140. The whole DYCP started using character graphics. You plot six character
  141. rows where the character (screen) codes increase to the right and down.
  142. This area is then used like a small bitmap screen. Each of the text chars
  143. are displayed one byte at a time on each six rows high character columns.
  144. This 240 character positions big piece of screen can be moved horizontally
  145. using the x-scroll register (three lowest bits in $D016) and after eight
  146. pixels you move the text itself, like in any scroll. The screen is of course
  147. reduced to 38 columns wide to hide the jittering on the sides.
  148.  
  149. A good coder may also change the character sets during the display and
  150. even double the size of the scroll, but because the raster time happens
  151. to go to waste using this technique anyway, that is not very feasible. There
  152. are also other difficulties in this approach, the biggest is the time needed
  153. to clear the display.
  154.  
  155.  
  156. _Save characters - and time_
  157.  
  158. But why should we move an eight-byte-high character image in a 48-line-high
  159. area, when 16 is really enough ?  We can use two characters for the graphics
  160. bitmap and then move this in eight pixel steps up and down. The lowest
  161. three bits of the y-position then gives us the offset where the data must
  162. be plotted inside this graphical region. The two character codes are usually
  163. selected to be consecutive ones so that the image data has also 16
  164. consecutive bytes. [See picture 1.]
  165.  
  166.  
  167. _Demo program might clear things up_
  168.  
  169. The demo program is coded using the latter algorithm. The program first
  170. copies the Character ROM to ram, because it is faster to use it from there.
  171. You can easily change the program to use your own character set instead,
  172. if you like. The sinus data for the vertical movement is created of a 1/4
  173. of a cycle by mirroring it both horizontally and vertically.
  174.  
  175. Two most time critical parts are clearing the character set and plotting the
  176. new one. Neither of these may happen when VIC is drawing the area where the
  177. scroll is, so there is a slight hurry. Using double buffering technique we
  178. could overcome this limitation, but this is just an example program. For
  179. speed there is CLC only when it is absolutely needed.
  180.  
  181. The NTSC version is a bit crippled, it only covers 32 columns and thus the
  182. characters seem to appear from thin air. Anyway, the idea should become
  183. clear.
  184.  
  185.  
  186. _Want to go to the border ?_
  187.  
  188. Some coders are always trying to get all effects ever done using the C64 go
  189. to the border, and even successfully. The easiest way is to use only a region
  190. of 21 pixels high - sprites - and move the text exactly like in characters.
  191. In fact only the different addressing causes the differences in the code.
  192.  
  193. Eight horizontally expanded sprites will be just enough to fill the side
  194. borders. You can also mix these techiques, but then you have the usual
  195. "chars-in-the-screen-while-border-opened"-problems (however, they are
  196. solvable). Unfortunately sprite-dycp is even more slower than char-dycp.
  197.  
  198.  
  199. _More movement vertically_
  200.  
  201. You might think that using the sprites will restrict the sinus to only
  202. 14 pixels. Not really, the only restriction is that the vertical position
  203. difference between three consequent text character must be less than 14
  204. pixel lines. Each sprites' Y-coordinate will be the minimum of the three
  205. characters residing in that sprite. Line offsets inside the sprites
  206. are then obtained by subtracting the sprite y-coordinate from the character
  207. y-coordinate. Maybe a little hard to follow, but maybe a picture will
  208. clear the situation. [See picture 2.]
  209.  
  210. Scrolling horizontally is easy. You just have to move sprites like you would
  211. use the character horizontal scroll register and after eight pixels you
  212. reset the sprite positions and scroll the text one position in memory.
  213. And of course, you fetch a new character for the scroll. When we have
  214. different and changing sprite y-coordinates, opening the side borders become
  215. a great deal more difficult. However, in this case there is at least two
  216. different ways to do it.
  217.  
  218.  
  219. _Stretch the sprites_
  220.  
  221. The easiest way is to position all of the sprites where the scroll will
  222. be when it is in its highest position. Then stretch the first and last line
  223. of each sprite so that the 19 sprite lines in the middle will be on the
  224. desired place. Opening the borders now is trivial, because all of the sprites
  225. are present on all of the scan lines and they steal a constant amount of
  226. time. However, we lose two sprite lines. We might not want to use the first
  227. and the last line for graphics, because they are stretched.
  228. [See previous C=Hacking Issues for more information about stretching and
  229.  stolen cycles.]
  230.  
  231. A more difficult approach is to unroll the routine and let another routine
  232. count the sprites present in each line and then change the time the routine
  233. uses accordingly. In this way you save time during the display for other
  234. effects, like color bars, because stretching will take at least 12 cycles
  235. on each raster line. On the other hand, if the sinus is constant (user is
  236. not allowed to change it), it is usually possible to embedd the count
  237. routine directly to the border opening part of the routine.
  238.  
  239.  
  240. _More sprites_
  241.  
  242. You don't necassarily need to plot the characters in sprites to have more
  243. than eight characters. Using a sprite multiplexing techiques you can double
  244. or triple the number of sprites available. You can divide the scroll
  245. vertically into several areas and because the y-coordinate of the scroll
  246. is a sinus, there always is a fixed maximum number of sprites in each area.
  247. This number is always smaller than the total number of sprites in the
  248. whole scroll. I won't go into detail, but didn't want to leave this out
  249. completely. [See picture 3.]
  250.  
  251.  
  252. _Smoother and smoother_
  253.  
  254. Why be satisfied with a scroll with only 40 different slices horizontally ?
  255. It should be possible to count own coordinates for each pixel column on
  256. the scroll. In fact the program won't be much different, but the routine
  257. must also mask the unwanted bits and write the byte to memory with ORA+STA.
  258. When you think about it more, it is obvious that this takes a generous amount
  259. of time, handling every bit seperately will take much more than eight times
  260. the time a simple LDA+STA takes. Some coders have avoided this by plotting
  261. the same character to different character sets simultaneously and then
  262. changing the charsets appropriately, but the resulting scroll won't be much
  263. larger than 96x32 pixels.
  264.  
  265. --------------------------------------------------------------------------
  266. Picture 1 - Two character codes will make a graphical bitmap
  267.  
  268. Screen memory:
  269.  ____________________________________
  270. |Char   |Char   |Char   |Char   |  ...
  271. |Code   |Code   |Code   |Code   |
  272. |0      |2      |80     |80     | .
  273. |       |**  ** |       |       | .
  274. |       |**  ** |       |       | .
  275. |*****  |****** |       |       |
  276. |****** | ****  |       |       |
  277. |**__**_|__**___|_______|_______|
  278. |**  ** |  **   | ****  |Char   |
  279. |**  ** |  **   |****** |Code   |
  280. |****** |       |**  ** |6      |
  281. |*****  |       |**     |       |
  282. |Char   |Char   |**  ** |       |
  283. |Code   |Code   |****** |       |
  284. |1      |3      |4****  |*****  |
  285. |_______|_______|_______|******_|
  286. |Char   |Char   |       |**  ** |
  287. |Code   |Code   |       |****** |
  288. |80     |80     |       |*****  |
  289. |       |       |       |**     |
  290. |       |       |Char   |**ar   |
  291. |       |       |Code   |Code   |
  292. |       |       |5      |7      |
  293. |_______|_______|_______|_______|
  294.  
  295. Character set memory:
  296.  
  297.  _________________________________________________________________
  298. |Char 0 |Char 1 |Char 2 |Char 3 |Char 4 |Char 5 |Char 6 |Char 7 | ...
  299. |_______|_______|_______|_______|_______|_______|_______|_______|__
  300.       DDDDDDDD      YYYYYYYY     CCCCCCCC              PPPPPPPP
  301.  First column    Second column   Third column    Fourth column
  302.  
  303. --------------------------------------------------------------------------
  304. Picture 2 - DYCP with sprites
  305.  
  306. Sprite 0
  307.  _______________________
  308. |       |**  ** |       |
  309. |       |**  ** |       |
  310. |       |****** |       |
  311. |*****  | ****  |       |
  312. |****** |  **   |       |
  313. |**  ** |  **   |       |
  314. |**  ** |  **   |       |
  315. |**__**_|_______|_______|
  316. |****** |       | ****  |
  317. |*****  |       |****** |
  318. |       |       |**  ** |
  319. |       |       |**     |
  320. |       |       |**  ** |
  321. |       |       |****** |
  322. |       |       | ****  |
  323. |_______|_______|_______| Sprite 1
  324. |       |       |       | _______________________
  325. |       |       |       ||*****  |       |       |
  326. |       |       |       ||****** |       |       |
  327. |       |       |       ||**  ** |       |       |
  328. |_______|_______|_______||****** |       |       |
  329.                          |*****  |       |       |
  330.                          |**     |       |       |
  331.                          |**     |       |       |
  332.                          |_______|_______|_______|
  333.                          |       |       |       |
  334.                          |       |       |       |
  335.                          |       |       |       |
  336.                          |       | ****  |       |
  337.                          |       | ****  |       |
  338.                          |       |       |****** |
  339.                          |       |       |****** |
  340.                          |_______|_______|__**___|
  341.                          |       |       |  **   |
  342.                          |       |       |  **   |
  343.                          |       |       |  **   |
  344.                          |       |       |  **   |
  345.                          |_______|_______|_______|
  346.  
  347. --------------------------------------------------------------------------
  348. Picture 3 - Sprite multiplexing
  349.  
  350.                                __          Set coordinates for eight sprites
  351.                             __|3 |         that start from the top half.
  352.                            |4 |  |__
  353.                          __|  `--|2 |
  354.                         |5 `--'  |  |
  355.                         |  |     `--'__
  356.                         `--'        |1 |
  357.                                     |  |
  358.                       __            `--'
  359.                      |6 |
  360.                      |  |               __
  361.                      `--'              |0 |
  362.                                        |  |
  363. -__------------------------------------`--'When VIC has displayed the last
  364. |0 |               __                      sprite, set coordinates for the
  365. |  |              |6 |                     sprites in the lower half of the
  366. `--'              |  |                     area.
  367.                   `--'
  368.     __
  369.    |1 |         __
  370.    |  |        |5 |
  371.    `-- __      |  |
  372.       |2 |   __`--'
  373.       |  |__|4 |                           You usually have two sprites that
  374.       `--|3 |  |                           are only 'used' once so that you
  375.          |  `--'                           can change other sprites when VIC
  376.          `__'                              is displaying them.
  377. --------------------------------------------------------------------------
  378.  
  379. DYCP demo program (PAL)
  380.  
  381.  
  382. SINUS=  $CF00   ; Place for the sinus table
  383. CHRSET= $3800   ; Here begins the character set memory
  384. GFX=    $3C00   ; Here we plot the dycp data
  385. X16=    $CE00   ; values multiplicated by 16 (0,16,32..)
  386. D16=    $CE30   ; divided by 16  (16 x 0,16 x 1 ...)
  387. START=  $033C   ; Pointer to the start of the sinus
  388. COUNTER= $033D  ; Scroll counter (x-scroll register)
  389. POINTER= $033E  ; Pointer to the text char
  390. YPOS=   $0340   ; Lower 4 bits of the character y positions
  391. YPOSH=  $0368   ; y positions divided by 16
  392. CHAR=   $0390   ; Scroll text characters, multiplicated by eight
  393. ZP=     $FB     ; Zeropage area for indirect addressing
  394. ZP2=    $FD
  395. AMOUNT= 38      ; Amount of chars to plot-1
  396. PADCHAR= 32     ; Code used for clearing the screen
  397.  
  398. *= $C000
  399.  
  400.         SEI             ; Disable interrupts
  401.         LDA #$32        ; Character generator ROM to address space
  402.         STA $01
  403.         LDX #0
  404. LOOP0   LDA $D000,X     ; Copy the character set
  405.         STA CHRSET,X
  406.         LDA $D100,X
  407.         STA CHRSET+256,X
  408.         DEX
  409.         BNE LOOP0
  410.         LDA #$37        ; Normal memory configuration
  411.         STA $01
  412.         LDY #31
  413. LOOP1   LDA #66         ; Compose a full sinus from a 1/4th of a
  414.         CLC             ;   cycle
  415.         ADC SIN,X
  416.         STA SINUS,X
  417.         STA SINUS+32,Y
  418.         LDA #64
  419.         SEC
  420.         SBC SIN,X
  421.         STA SINUS+64,X
  422.         STA SINUS+96,Y
  423.         INX
  424.         DEY
  425.         BPL LOOP1
  426.         LDX #$7F
  427. LOOP2   LDA SINUS,X
  428.         LSR
  429.         CLC
  430.         ADC #32
  431.         STA SINUS+128,X
  432.         DEX
  433.         BPL LOOP2
  434.  
  435.         LDX #39
  436. LOOP3   TXA
  437.         ASL
  438.         ASL
  439.         ASL
  440.         ASL
  441.         STA X16,X       ; Multiplication table (for speed)
  442.         TXA
  443.         LSR
  444.         LSR
  445.         LSR
  446.         LSR
  447.         CLC
  448.         ADC #>GFX
  449.         STA D16,X       ; Dividing table
  450.         LDA #0
  451.         STA CHAR,X      ; Clear the scroll
  452.         DEX
  453.         BPL LOOP3
  454.         STA POINTER     ; Initialize the scroll pointer
  455.         LDX #7
  456.         STX COUNTER
  457. LOOP10  STA CHRSET,X    ; Clear the @-sign..
  458.         DEX
  459.         BPL LOOP10
  460.  
  461.         LDA #>CHRSET    ; The right page for addressing
  462.         STA ZP2+1
  463.         LDA #<IRQ       ; Our interrupt handler address
  464.         STA $0314
  465.         LDA #>IRQ
  466.         STA $0315
  467.         LDA #$7F        ; Disable timer interrupts
  468.         STA $DC0D
  469.         LDA #$81        ; Enable raster interrupts
  470.         STA $D01A
  471.         LDA #$A8        ; Raster compare to scan line $A8
  472.         STA $D012
  473.         LDA #$1B        ; 9th bit
  474.         STA $D011
  475.         LDA #30
  476.         STA $D018       ; Use the new charset
  477.         CLI             ; Enable interrupts and return
  478.         RTS
  479.  
  480. IRQ     INC START       ; Increase counter
  481.         LDY #AMOUNT
  482.         LDX START
  483. LOOP4   LDA SINUS,X     ; Count a pointer for each text char and according
  484.         AND #7          ;  to it fetch a y-position from the sinus table
  485.         STA YPOS,Y      ;   Then divide it to two bytes
  486.         LDA SINUS,X
  487.         LSR
  488.         LSR
  489.         LSR
  490.         STA YPOSH,Y
  491.         INX             ; Chars are two positions apart
  492.         INX
  493.         DEY
  494.         BPL LOOP4
  495.  
  496.         LDA #0
  497.         LDX #79
  498. LOOP11  STA GFX,X       ; Clear the dycp data
  499.         STA GFX+80,X
  500.         STA GFX+160,X
  501.         STA GFX+240,X
  502.         STA GFX+320,X
  503.         STA GFX+400,X
  504.         STA GFX+480,X
  505.         STA GFX+560,X
  506.         DEX
  507.         BPL LOOP11
  508.  
  509. MAKE    LDA COUNTER     ; Set x-scroll register
  510.         STA $D016
  511.         LDX #AMOUNT
  512.         CLC             ; Clear carry
  513. LOOP5   LDY YPOSH,X     ; Determine the position in video matrix
  514.         TXA
  515.         ADC LINESL,Y    ; Carry won't be set here
  516.         STA ZP          ; low byte
  517.         LDA #4
  518.         ADC LINESH,Y
  519.         STA ZP+1        ; high byte
  520.         LDA #PADCHAR    ; First clear above and below the char
  521.         LDY #0          ; 0. row
  522.         STA (ZP),Y
  523.         LDY #120        ; 3. row
  524.         STA (ZP),Y
  525.         TXA             ; Then put consecuent character codes to the places
  526.         ASL             ;  Carry will be cleared
  527.         ORA #$80    ; Inverted chars
  528.         LDY #40         ; 1. row
  529.         STA (ZP),Y
  530.         ADC #1          ; Increase the character code, Carry won't be set
  531.         LDY #80         ; 2. row
  532.         STA (ZP),Y
  533.  
  534.         LDA CHAR,X      ; What character to plot ? (source)
  535.         STA ZP2         ;  (char is already multiplicated by eight)
  536.         LDA X16,X       ; Destination low byte
  537.         ADC YPOS,X      ;  (16*char code + y-position's 3 lowest bits)
  538.         STA ZP
  539.         LDA D16,X       ; Destination high byte
  540.         STA ZP+1
  541.  
  542.         LDY #6          ; Transfer 7 bytes from source to destination
  543.         LDA (ZP2),Y : STA (ZP),Y
  544.         DEY             ; This is the fastest way I could think of.
  545.         LDA (ZP2),Y : STA (ZP),Y
  546.         DEY
  547.         LDA (ZP2),Y : STA (ZP),Y
  548.         DEY
  549.         LDA (ZP2),Y : STA (ZP),Y
  550.         DEY
  551.         LDA (ZP2),Y : STA (ZP),Y
  552.         DEY
  553.         LDA (ZP2),Y : STA (ZP),Y
  554.         DEY
  555.         LDA (ZP2),Y : STA (ZP),Y
  556.         DEX
  557.         BPL LOOP5    ; Get next char in scroll
  558.  
  559.         LDA #1
  560.         STA $D019       ; Acknowledge raster interrupt
  561.  
  562.         DEC COUNTER     ; Decrease the counter = move the scroll by 1 pixel
  563.         BPL OUT
  564. LOOP12  LDA CHAR+1,Y    ; Move the text one position to the left
  565.         STA CHAR,Y      ;  (Y-register is initially zero)
  566.         INY
  567.         CPY #AMOUNT
  568.         BNE LOOP12
  569.         LDA POINTER
  570.         AND #63         ; Text is 64 bytes long
  571.         TAX
  572.         LDA SCROLL,X    ; Load a new char and multiply it by eight
  573.         ASL
  574.         ASL
  575.         ASL
  576.         STA CHAR+AMOUNT ; Save it to the right side
  577.         DEC START       ; Compensation for the text scrolling
  578.         DEC START
  579.         INC POINTER     ; Increase the text pointer
  580.         LDA #7
  581.         STA COUNTER     ; Initialize X-scroll
  582.  
  583. OUT     JMP $EA7E       ; Return from interrupt
  584.  
  585. SIN     BYT 0,3,6,9,12,15,18,21,24,27,30,32,35,38,40,42,45
  586.         BYT 47,49,51,53,54,56,57,59,60,61,62,62,63,63,63
  587.                         ; 1/4 of the sinus
  588.  
  589. LINESL  BYT 0,40,80,120,160,200,240,24,64,104,144,184,224
  590.         BYT 8,48,88,128,168,208,248,32
  591.  
  592. LINESH  BYT 0,0,0,0,0,0,0,1,1,1,1,1,1,2,2,2,2,2,2,2,3
  593.  
  594. SCROLL  SCR "THIS@IS@AN@EXAMPLE@SCROLL@FOR@"
  595.         SCR "COMMODORE@MAGAZINE@BY@PASI@OJALA@@"
  596.                         ; SCR will convert text to screen codes
  597.  
  598. --------------------------------------------------------------------------
  599. Basic loader for the Dycp demo program (PAL)
  600.  
  601. 1 S=49152
  602. 2 DEFFNH(C)=C-48+7*(C>64)
  603. 3 CH=0:READA$,A:PRINTA$:IFA$="END"THENPRINT"<white><clr>":SYS49152:END
  604. 4 FORF=0TO31:Q=FNH(ASC(MID$(A$,F*2+1)))*16+FNH(ASC(MID$(A$,F*2+2)))
  605. 5 CH=CH+Q:POKES,Q:S=S+1:NEXT:IFCH=ATHEN3
  606. 6 PRINT"CHECKSUM ERROR":END
  607. 100 DATA 78A9328501A200BD00D09D0038BD00D19D0039CAD0F1A9378501A01FA942187D, 3441
  608. 101 DATA 75C19D00CF9920CFA94038FD75C19D40CF9960CFE88810E4A27FBD00CF4A1869, 4302
  609. 102 DATA 209D80CFCA10F3A2278A0A0A0A0A9D00CE8A4A4A4A4A18693C9D30CEA9009D90, 
  610. 3231
  611. 103 DATA 03CA10E58D3E03A2078E3D039D0038CA10FAA93885FEA99B8D1403A9C08D1503, 
  612. 3338
  613. 104 DATA A97F8D0DDCA9818D1AD0A9A88D12D0A91B8D11D0A91E8D18D05860EE3C03A026, 
  614. 3864
  615. 105 DATA AE3C03BD00CF2907994003BD00CF4A4A4A996803E8E88810EAA900A24F9D003C, 
  616. 3256
  617. 106 DATA 9D503C9DA03C9DF03C9D403D9D903D9DE03D9D303ECA10E5AD3D038D16D0A226, 
  618. 3739
  619. 107 DATA 18BC68038A7995C185FBA90479AAC185FCA920A00091FBA07891FB8A0A0980A0, 
  620. 4224
  621. 108 DATA 2891FB6901A05091FBBD900385FDBD00CE7D400385FBBD30CE85FCA006B1FD91, 
  622. 4440
  623. 109 DATA FB88B1FD91FB88B1FD91FB88B1FD91FB88B1FD91FB88B1FD91FB88B1FD91FBCA, 6225
  624. 110 DATA 109FEE19D0CE3D031028B99103999003C8C026D0F5AD3E03293FAABDBFC10A0A, 3593
  625. 111 DATA 0A8DB603CE3C03CE3C03EE3E03A9078D3D034C7EEA000306090C0F1215181B1E, 2159
  626. 112 DATA 202326282A2D2F3133353638393B3C3D3E3E3F3F3F00285078A0C8F018406890, 2268
  627. 113 DATA B8E008305880A8D0F82000000000000000010101010101020202020202020314, 1379
  628. 114 DATA 08091300091300010E000518010D100C05001303120F0C0C00060F1200030F0D, 304
  629. 115 DATA 0D0F040F1205000D0107011A090E050002190010011309000F0A010C01000000, 257
  630. 200 DATA END,0
  631.  
  632. --------------------------------------------------------------------------
  633. Uuencoded C64 executable of the basic loader (PAL)
  634.  
  635. begin 644 dycp.64
  636. M`0@-"`$`4[(T.3$U,@`F"`(`EJ5(*$,ILD.K-#BJ-ZPH0[$V-"D`4@@#`$-(@
  637. MLC`ZAT$D+$$ZF4$D.HM!)+(B14Y$(J>9(@63(CJ>-#DQ-3(Z@`")"`0`@4:RE
  638. M,*0S,3I1LJ5(*,8HRBA!)"Q&K#*J,2DI*:PQ-JJE2"C&*,HH020L1JPRJC(IA
  639. M*2D`J@@%`$-(LD-(JE$ZEU,L43I3LE.J,3J".HM#2+)!IS,`P@@&`)DB0TA%.
  640. M0TM354T@15)23U(B.H``#PED`(,@-SA!.3,R.#4P,4$R,#!"1#`P1#`Y1#`P.
  641. M,SA"1#`P1#$Y1#`P,SE#040P1C%!.3,W.#4P,4$P,49!.30R,3@W1"P@,S0T#
  642. M,0!<"64`@R`W-4,Q.40P,$-&.3DR,$-&03DT,#,X1D0W-4,Q.40T,$-&.3DV&
  643. M,$-&13@X.#$P131!,C=&0D0P,$-&-$$Q.#8Y+"`T,S`R`*D)9@"#(#(P.40X3
  644. M,$-&0T$Q,$8S03(R-SA!,$$P03!!,$$Y1#`P0T4X031!-$$T031!,3@V.3-#P
  645. M.40S,$-%03DP,#E$.3`L(#,R,S$`]@EG`(,@,#-#03$P134X1#-%,#-!,C`WY
  646. M.$4S1#`S.40P,#,X0T$Q,$9!03DS.#@U1D5!.3E".$0Q-#`S03E#,#A$,34P@
  647. M,RP@,S,S.`!#"F@`@R!!.3=&.$0P1$1#03DX,3A$,4%$,$$Y03@X1#$R1#!!B
  648. M.3%".$0Q,40P03DQ13A$,3A$,#4X-C!%13-#,#-!,#(V+"`S.#8T`)`*:0"#]
  649. M($%%,T,P,T)$,#!#1C(Y,#<Y.30P,#-"1#`P0T8T031!-$$Y.38X,#-%.$4X$
  650. M.#@Q,$5!03DP,$$R-$8Y1#`P,T,L(#,R-38`W0IJ`(,@.40U,#-#.41!,#-#]
  651. M.41&,#-#.40T,#-$.40Y,#-$.41%,#-$.40S,#-%0T$Q,$4U040S1#`S.$0Q*
  652. M-D0P03(R-BP@,S<S.0`J"VL`@R`Q.$)#-C@P,SA!-SDY-4,Q.#5&0D$Y,#0W^
  653. M.4%!0S$X-49#03DR,$$P,#`Y,49"03`W.#DQ1D(X03!!,#DX,$$P+"`T,C(T:
  654. M`'<+;`"#(#(X.3%&0C8Y,#%!,#4P.3%&0D)$.3`P,S@U1D1"1#`P0T4W1#0P;
  655. M,#,X-49"0D0S,$-%.#5&0T$P,#9",49$.3$L(#0T-#``Q`MM`(,@1D(X.$(Q?
  656. M1D0Y,49".#A",49$.3%&0C@X0C%&1#DQ1D(X.$(Q1D0Y,49".#A",49$.3%&V
  657. M0C@X0C%&1#DQ1D)#02P@-C(R-0`1#&X`@R`Q,#E&144Q.40P0T4S1#`S,3`RK
  658. M.$(Y.3$P,SDY.3`P,T,X0S`R-D0P1C5!1#-%,#,R.3-&04%"1$)&0S$P03!!M
  659. M+"`S-3DS`%X,;P"#(#!!.$1"-C`S0T4S0S`S0T4S0S`S144S13`S03DP-SA$H
  660. M,T0P,S1#-T5%03`P,#,P-C`Y,$,P1C$R,34Q.#%",44L(#(Q-3D`JPQP`(,@0
  661. M,C`R,S(V,C@R03)$,D8S,3,S,S4S-C,X,SDS0C-#,T0S13-%,T8S1C-&,#`R[
  662. M.#4P-SA!,$,X1C`Q.#0P-C@Y,"P@,C(V.`#X#'$`@R!".$4P,#@S,#4X.#!!8
  663. M.$0P1C@R,#`P,#`P,#`P,#`P,#`P,#$P,3`Q,#$P,3`Q,#(P,C`R,#(P,C`R^
  664. M,#(P,S$T+"`Q,S<Y`$0-<@"#(#`X,#DQ,S`P,#DQ,S`P,#$P13`P,#4Q.#`Q7
  665. M,$0Q,#!#,#4P,#$S,#,Q,C!&,$,P0S`P,#8P1C$R,#`P,S!&,$0L(#,P-`"02
  666. M#7,`@R`P1#!&,#0P1C$R,#4P,#!$,#$P-S`Q,4$P.3!%,#4P,#`R,3DP,#$P.
  667. L,#$Q,S`Y,#`P1C!!,#$P0S`Q,#`P,#`P+"`R-3<`G`W(`(,@14Y$+#`````PK
  668. ``
  669. end
  670. size 1439
  671. --------------------------------------------------------------------------
  672. Uuencoded C64 executable of the basic loader (NTSC)
  673.  
  674. begin 644 dycp-ntsc.bas
  675. M`0@-"`$`4[(T.3$U,@`F"`(`EJ5(*$,ILD.K-#BJ-ZPH0[$V-"D`40@#`$-(?
  676. MLC`ZAT$D+$$ZF4$D.HM!)+(B14Y$(J>9(I,B.IXT.3$U,CJ``(@(!`"!1K(P/
  677. MI#,Q.E&RI4@HQBC**$$D+$:L,JHQ*2DIK#$VJJ5(*,8HRBA!)"Q&K#*J,BDI:
  678. M*0"I"`4`0TBR0TBJ43J74RQ1.E.R4ZHQ.H(ZBT-(LD&G,P#!"`8`F2)#2$5#F
  679. M2U-532!%4E)/4B(Z@`#'"%H`@``4"60`@R`W.$$Y,S(X-3`Q03(P,$)$,#!$L
  680. M,#E$,#`S.$)$,#!$,3E$,#`S.4-!1#!&,4$Y,S<X-3`Q03`Q1D$Y-#(Q.#=$I
  681. M+"`S-#0Q`&$)90"#(#<U0S$Y1#`P0T8Y.3(P0T9!.30P,SA&1#<U0S$Y1#0P!
  682. M0T8Y.38P0T9%.#@X,3!%-$$R-T9"1#`P0T8T03$X-CDL(#0S,#(`K@EF`(,@R
  683. M,C`Y1#@P0T9#03$P1C-!,C(W.$$P03!!,$$P03E$,#!#13A!-$$T031!-$$QJ
  684. M.#8Y,T,Y1#,P0T5!.3`P.40Y,"P@,S(S,0#["6<`@R`P,T-!,3!%-3A$,T4P.
  685. M,T$R,#<X13-$,#,Y1#`P,SA#03$P1D%!.3,X.#5&14$Y.4(X1#$T,#-!.4,P;
  686. M.$0Q-3`S+"`S,S,X`$@*:`"#($$Y-T8X1#!$1$-!.3@Q.$0Q040P03E",#A$:
  687. M,3)$,$$Y,4(X1#$Q1#!!.3%%.$0Q.$0P-3@V,$5%,T,P,T$P,4,L(#,X-C(`9
  688. ME0II`(,@044S0S`S0D0P,$-&,CDP-SDY-#`P,T)$,#!#1C1!-$$T03DY-C@PB
  689. M,T4X13@X.#$P14%!.3`P03(T1CE$,#`S0RP@,S(U-@#B"FH`@R`Y1#4P,T,Y$
  690. M1$$P,T,Y1$8P,T,Y1#0P,T0Y1#DP,T0Y1$4P,T0Y1#,P,T5#03$P135!1#-$E
  691. M,#,X1#$V1#!!,C%#+"`S-S(Y`"\+:P"#(#$X0D,V.#`S.$$W.3DU0S$X-49")
  692. M03DP-#<Y04%#,3@U1D-!.3(P03`P,#DQ1D)!,#<X.3%&0CA!,$$P.3@P03`L#
  693. M(#0R,C0`?`ML`(,@,C@Y,49"-CDP,4$P-3`Y,49"0D0Y,#`S.#5&1$)$,#!#H
  694. M13=$-#`P,S@U1D)"1#,P0T4X-49#03`P-D(Q1D0Y,2P@-#0T,`#)"VT`@R!&C
  695. M0C@X0C%&1#DQ1D(X.$(Q1D0Y,49".#A",49$.3%&0C@X0C%&1#DQ1D(X.$(QA
  696. M1D0Y,49".#A",49$.3%&0D-!+"`V,C(U`!8,;@"#(#$P.49%13$Y1#!#13-$T
  697. M,#,Q,#(X0CDY,3`S.3DY,#`S0SA#,#(V1#!&-4%$,T4P,S(Y,T9!04)$0D9#0
  698. M,3!!,$$L(#,U.3,`8PQO`(,@,$$X1$(V,#-#13-#,#-#13-#,#-%13-%,#-!D
  699. M.3`W.$0S1#`S-$,W145!,#`P,S`V,#DP0S!&,3(Q-3$X,4(Q12P@,C$U.0"P2
  700. M#'``@R`R,#(S,C8R.#)!,D0R1C,Q,S,S-3,V,S@S.3-",T,S1#-%,T4S1C-&/
  701. M,T8P,#(X-3`W.$$P0SA&,#$X-#`V.#DP+"`R,C8X`/T,<0"#($(X13`P.#,P2
  702. M-3@X,$$X1#!&.#(P,#`P,#`P,#`P,#`P,#`P,3`Q,#$P,3`Q,#$P,C`R,#(P>
  703. M,C`R,#(P,C`S,30L(#$S-SD`20UR`(,@,#@P.3$S,#`P.3$S,#`P,3!%,#`P3
  704. M-3$X,#$P1#$P,$,P-3`P,3,P,S$R,$8P0S!#,#`P-C!&,3(P,#`S,$8P1"P@J
  705. M,S`T`)4-<P"#(#!$,$8P-#!&,3(P-3`P,$0P,3`W,#$Q03`Y,$4P-3`P,#(Q`
  706. M.3`P,3`P,3$S,#DP,#!&,$$P,3!#,#$P,#`P,#`L(#(U-P"A#7@`@R!%3D0LZ
  707. $,````#`P0
  708. ``
  709. end
  710. size 1444
  711.  
  712. ================================================================================
  713. Opening the borders
  714. by Pasi 'Albert' Ojala (po87553@cs.tut.fi or albert@cc.tut.fi)
  715.         Written: 20-Jul-92
  716.  
  717.        All timings are in PAL, principles will apply to NTSC too.
  718.               Refer to VIC memory map in Hacking Issue 4.
  719.  
  720. VIC has many features and transparent borders are one of them. You can not
  721. make characters appear in the border, but sprites are displayed in the
  722. border too. "How to do this then?" is the big question.
  723.  
  724. The screen resolution in C64 has been and will be 320 x 200 pixels. Most
  725. games need to use the whole screen to be efficient or just plain playable.
  726. But there still is that useless border area, and you can put score and
  727. other status information there instead of having them interfere with the
  728. full-screen smooth-scrolling playing area.
  729.  
  730.  
  731. _How to disable the vertical borders_
  732.  
  733. When VIC (Video Interface Controller) has displayed all character rows,
  734. it will start displaying the vertical border area. It will start displaying
  735. the characters again in top of the screen. The row select register sets the
  736. number of character lines on the screen. If we select the 24-row display
  737. when VIC is drawing the last (25th) row, it does not start to draw the
  738. border at all !  VIC will think that it already started to draw the border.
  739.  
  740. The 25-row display must be selected again in the top of the screen, so that
  741. the border may be opened in the next frame too. The number of displayed rows
  742. can be selected with the bit 3 in $d011. If the bit is set, VIC will display
  743. 25 rows and 24 rows otherwise. We have to clear the bit somewhere during the
  744. last row (raster lines $f2-$fa) and set it again in top of the screen or at
  745. least somewhere before the last row (line $f2). This has to be done in every
  746. frame (50 times per second in PAL).
  747.  
  748.  
  749. _How to open the sideborders_
  750.  
  751. The same trick can be applied to sideborders. When VIC is about to start
  752. displaying the sideborder, just select 38-column mode and restore 40-column
  753. mode so that you can do the trick again in the next scan line. If you need to
  754. open the sideborders in the bottom or top border area, you have to open the
  755. vertical borders also, but there shouldn't be any difficulty in doing that.
  756.  
  757. There is two drawbacks in this. The timing must be precise, one clock cycle
  758. off and the sideborder will not open (the sprites will generally take care of
  759. the timing) and you have to do the opening on each and every line. With
  760. top/bottom borders once in a frame was enough.
  761.  
  762. Another problem is bad-lines. There is not enough time to open the borders
  763. during a bad line and still have all of the sprites enabled. One solution
  764. is to open the borders only on seven lines and leave the bad lines unopened.
  765. Another way is to use less than eight sprites. You can have six of them
  766. on a bad line and still be able to open the sideborders (PAL). The old and
  767. still good solution is to scroll the bad lines, so that VIC will not start
  768. to draw the screen at all until it is allowed to do so.
  769. [Read more about bad lines from previous C=Hacking Issues]
  770.  
  771.  
  772. _Scrolling the screen_
  773.  
  774. VIC begins to draw the screen from the first bad line. VIC will know what
  775. line is a bad line by comparing its scan line counter to the vertical
  776. scroll register : when they match, the next line is a bad line. If we change
  777. the vertical scroll register ($d011), the first bad line will move also.
  778. If we do this on every line, the line counter in VIC will never match with
  779. it and the drawing never starts (until it is allowed to do so).
  780.  
  781. When we don't have to worry about bad lines, we have enough time to open the
  782. borders and do some other effects too. It is not necassary to change the
  783. vertical scroll on every line to get rid of the bad lines, just make sure
  784. that it never matches the line counter (or actually the least significant
  785. 8 bits).
  786.  
  787. You can even scroll the bad lines independently and you have FLD - Flexible
  788. Line Distance. You just allow a bad line when it is time to display the next
  789. character row. With this you can bounce the lines or scroll a hires picture
  790. very fast down the screen. But this has not so much to do with borders, so
  791. I will leave it to another article. (Just send requests and I might start
  792. writing about FLD ..)
  793.  
  794.  
  795. _Garbage appearing_
  796.  
  797. When we open the top and bottom borders, some graphics may appear. Even
  798. though VIC has already completed the graphics data fetches for the screen
  799. area, it will still fetch data for every character position in top and bottom
  800. borders. This will not do any harm though, because it does not generate any
  801. bad lines and happens during video fetch cycles [see Missing Cycles article].
  802. VIC reads the data from the last address in the current video bank, which is
  803. normally $3fff and displays this over and over again.
  804.  
  805. If we change the data in this address in the border area, the change will be
  806. visible right away. And if you synchronize the routine to the beam position,
  807. you can have a different value on each line. If there is nothing else to do
  808. in the border, you can get seven different values on each scan line.
  809.  
  810. The bad thing about this graphics is that it is impossible to change its
  811. color - it is always black. It is of course possible to use inverted graphics
  812. and change the background color. And if you have different data on each line,
  813. you can as easily have different color(s) on each line too.
  814.  
  815. If you don't use $3fff for any effects, it is a good idea to set it to zero,
  816. but remember to check that you do not store anything important in that
  817. address. In one demo I just cleared $3fff and it was right in the middle of
  818. another packed demopart. It took some time to find out what was wrong with
  819. the other part.
  820.  
  821.  
  822. _Horizontal scrolling_
  823.  
  824. This new graphics data also obeys the horizontal scroll register ($D016), so
  825. you can do limited tech-tech effects in the border too. You can also use
  826. sprites and open the sideborders. You can see an example of the tech-tech
  827. effect in the first example program. Multicolor mode select has no effect
  828. on this data. You can read more about tech-tech effects in a future article.
  829.  
  830.  
  831. _Example routine_
  832.  
  833. The example program will show how to open the top and bottom borders and how
  834. to use the $3fff-graphics. It is fairly well commented, so just check it for
  835. details. The program uses a sprite to do the synchronization [see Missing
  836. Cycles article] and reads a part of the character ROM to the display data
  837. buffer. To be honest, I might add that this is almost the same routine than
  838. the one in the Missing Cycles article. I have included both PAL and NTSC
  839. versions of the executables.
  840.  
  841. --------------------------------------------------------------------------
  842. The example program - $3fff-graphics
  843.  
  844. IMAGE0= $CE00   ; First graphics piece to show
  845. IMAGE1= $CF00   ; Second piece
  846. TECH=   $CD00   ; x-shift
  847. RASTER= $FA     ; Rasterline for the interrupt
  848. DUMMY=  $CFFF   ; Dummy-address for timing (refer to missing_cycles-article)
  849.  
  850. *= $C000
  851.         SEI             ; Disable interrupts
  852.         LDA #$7F        ; Disable timer interrupts (CIA)
  853.         STA $DC0D
  854.         LDA #$01        ; Enable raster interrupts (VIC)
  855.         STA $D01A
  856.         STA $D015       ; Enable the timing sprite
  857.         LDA #<IRQ
  858.         STA $0314       ; Interrupt vector to our routine
  859.         LDA #>IRQ
  860.         STA $0315
  861.         LDA #RASTER     ; Set the raster compare (9th bit will be set
  862.         STA $D012       ;  inside the raster routine)
  863.         LDA #RASTER-20  ; Sprite is situated 20 lines before the interrupt
  864.         STA $D001
  865.  
  866.         LDX #111
  867.         LDY #0
  868.         STY $D017       ; Disable y-expand
  869.         LDA #$32
  870.         STA $01         ; Select Character ROM
  871. LOOP0   LDA $D000,X
  872.         STA IMAGE0,Y    ; Copy a part of the charset to be the graphics
  873.         STA IMAGE0+112,Y
  874.         LDA $D800,X
  875.         STA IMAGE1,Y
  876.         STA IMAGE1+112,Y
  877.         INY             ; Until we copied enough
  878.         DEX
  879.         BPL LOOP0
  880.         LDA #$37        ; Char ROM out of the address space
  881.         STA $01
  882.  
  883.         LDY #15
  884. LOOP1   LDA XPOS,Y      ; Take a half of a sinus and mirror it to make
  885.         STA TECH,Y      ;  a whole cycle and then copy it as many times
  886.         STA TECH+32,Y   ;   as necassary
  887.         LDA #24
  888.         SEC
  889.         SBC XPOS,Y
  890.         STA TECH+16,Y
  891.         STA TECH+48,Y
  892.         DEY
  893.         BPL LOOP1
  894.         LDY #64
  895. LOOP2   LDA TECH,Y
  896.         STA TECH+64,Y
  897.         STA TECH+128,Y
  898.         DEY
  899.         BPL LOOP2
  900.         CLI             ; Enable interrupts
  901.         RTS             ; Return to basic (?)
  902.  
  903.  
  904. IRQ     LDA #$13        ; Open the bottom border (top border will open too)
  905.         STA $D011
  906.         NOP
  907.         LDY #111    ; Reduce for NTSC ?
  908.         INC DUMMY       ; Do the timing with a sprite
  909.         BIT $EA         ; Wait a bit (add a NOP for NTSC)
  910.  
  911. LOOP3   LDA TECH,Y      ; Do the x-shift
  912.         STA $D016
  913. FIRST   LDX IMAGE0,Y    ; Load the graphics to registers
  914. SECOND  LDA IMAGE1,Y
  915.         STA $3FFF       ; Alternate the graphics
  916.         STX $3FFF
  917.         STA $3FFF
  918.         STX $3FFF
  919.         STA $3FFF
  920.         STX $3FFF
  921.         STA $3FFF
  922.         STX $3FFF
  923.         STA $3FFF
  924.         STX $3FFF
  925.         LDA #0          ; Throw away 2 cycles (add a NOP for NTSC)
  926.         DEY
  927.         BPL LOOP3
  928.  
  929.         STA $3FFF       ; Clear the graphics
  930.         LDA #8
  931.         STA $D016       ; x-scroll to normal
  932.         LDA #$1B
  933.         STA $D011       ; Normal screen (be ready to open the border again)
  934.         LDA #111
  935.         DEC FIRST+1     ; Move the graphics by changing the low byte of the
  936.         BPL OVER        ;  load instruction
  937.         STA FIRST+1
  938. OVER    SEC
  939.         SBC FIRST+1
  940.         STA SECOND+1    ; Another graphics goes to opposite direction
  941.         LDA LOOP3+1     ; Move the x-shift also
  942.         SEC
  943.         SBC #2
  944.         AND #31         ; Sinus cycle is 32 bytes
  945.         STA LOOP3+1
  946.  
  947.         LDA #1
  948.         STA $D019       ; Acknowledge the raster interrupt
  949.         JMP $EA31       ; jump to the normal irq-handler
  950.  
  951. XPOS    BYT $C,$C,$D,$E,$E,$F,$F,$F,$F,$F,$F,$F,$E,$E,$D,$C
  952.         BYT $C,$B,$A,$9,$9,$8,$8,$8,$8,$8,$8,$8,$9,$9,$A,$B
  953.                         ; half of the sinus
  954.  
  955. --------------------------------------------------------------------------
  956. Basic loader for the $3fff-program (PAL)
  957.  
  958. 1 S=49152
  959. 2 DEFFNH(C)=C-48+7*(C>64)
  960. 3 CH=0:READA$,A:PRINTA$:IFA$="END"THENPRINT"<clear>":SYS49152:END
  961. 4 FORF=0TO31:Q=FNH(ASC(MID$(A$,F*2+1)))*16+FNH(ASC(MID$(A$,F*2+2)))
  962. 5 CH=CH+Q:POKES,Q:S=S+1:NEXT:IFCH=ATHEN3
  963. 6 PRINT"CHECKSUM ERROR":END
  964. 100 DATA 
  965. 78A97F8D0DDCA9018D1AD08D15D0A9718D1403A9C08D1503A9FA8D12D0A9E68D,4003
  966. 101 DATA 01D0A26FA0008C17D0A9328501BD00D09900CE9970CEBD00D89900CF9970CFC8,4030
  967. 102 DATA CA10EAA9378501A00FB9DCC09900CD9920CDA91838F9DCC09910CD9930CD8810,4172
  968. 103 DATA 
  969. E8A040B900CD9940CD9980CD8810F45860A9138D11D0EAA06FEEFFCF24EAB906,4554
  970. 104 DATA 
  971. CD8D16D0BE53CEB91CCF8DFF3F8EFF3F8DFF3F8EFF3F8DFF3F8EFF3F8DFF3F8E,4833
  972. 105 DATA FF3F8DFF3F8EFF3FA9008810D18DFF3FA9088D16D0A91B8D11D0A96FCE85C010,4163
  973. 106 DATA 038D85C038ED85C08D88C0AD7FC018E901291F8D7FC0EE19D04C31EA0C0C0D0E,3719
  974. 107 DATA 0E0F0F0F0F0F0F0F0E0E0D0C0C0B0A09090808080808080809090A0B00000000,318
  975. 200 DATA END,0
  976.  
  977. --------------------------------------------------------------------------
  978. An uuencoded C64 executable $3fff-program (PAL)
  979.  
  980. begin 644 xFFF.64
  981. M`0@-"`$`4[(T.3$U,@`F"`(`EJ5(*$,ILD.K-#BJ-ZPH0[$V-"D`40@#`$-(?
  982. MLC`ZAT$D+$$ZF4$D.HM!)+(B14Y$(J>9(I,B.IXT.3$U,CJ``(@(!`"!1K(P/
  983. MI#,Q.E&RI4@HQBC**$$D+$:L,JHQ*2DIK#$VJJ5(*,8HRBA!)"Q&K#*J,BDI:
  984. M*0"I"`4`0TBR0TBJ43J74RQ1.E.R4ZHQ.H(ZBT-(LD&G,P#!"`8`F2)#2$5#F
  985. M2U-532!%4E)/4B(Z@``."60`@R`W.$$Y-T8X1#!$1$-!.3`Q.$0Q040P.$0QK
  986. M-40P03DW,3A$,30P,T$Y0S`X1#$U,#-!.49!.$0Q,D0P03E%-CA$+"`T,#`S9
  987. M`%L)90"#(#`Q1#!!,C9&03`P,#A#,3=$,$$Y,S(X-3`Q0D0P,$0P.3DP,$-%Y
  988. M.3DW,$-%0D0P,$0X.3DP,$-&.3DW,$-&0S@L(#0P,S``J`EF`(,@0T$Q,$5!S
  989. M03DS-S@U,#%!,#!&0CE$0T,P.3DP,$-$.3DR,$-$03DQ.#,X1CE$0T,P.3DQL
  990. M,$-$.3DS,$-$.#@Q,"P@-#$W,@#U"6<`@R!%.$$P-#!".3`P0T0Y.30P0T0Y0
  991. M.3@P0T0X.#$P1C0U.#8P03DQ,SA$,3%$,$5!03`V1D5%1D9#1C(T14%".3`V5
  992. M+"`T-34T`$(*:`"#($-$.$0Q-D0P0D4U,T-%0CDQ0T-&.$1&1C-&.$5&1C-&%
  993. M.$1&1C-&.$5&1C-&.$1&1C-&.$5&1C-&.$1&1C-&.$4L(#0X,S,`CPII`(,@'
  994. M1D8S1CA$1D8S1CA%1D8S1D$Y,#`X.#$P1#$X1$9&,T9!.3`X.$0Q-D0P03DQ-
  995. M0CA$,3%$,$$Y-D9#13@U0S`Q,"P@-#$V,P#<"FH`@R`P,SA$.#5#,#,X140X+
  996. M-4,P.$0X.$,P040W1D,P,3A%.3`Q,CDQ1CA$-T9#,$5%,3E$,#1#,S%%03!#.
  997. M,$,P1#!%+"`S-S$Y`"@+:P"#(#!%,$8P1C!&,$8P1C!&,$8P13!%,$0P0S!#P
  998. M,$(P03`Y,#DP.#`X,#@P.#`X,#@P.#`Y,#DP03!",#`P,#`P,#`L(#,Q.``T>
  999. -"\@`@R!%3D0L,````#@P1
  1000. ``
  1001. end
  1002. size 823
  1003. --------------------------------------------------------------------------
  1004. An uuencoded C64 executable $3fff-program (NTSC)
  1005.  
  1006. begin 644 xfff-ntsc.64
  1007. M`0@-"`$`4[(T.3$U,@`F"`(`EJ5(*$,ILD.K-#BJ-ZPH0[$V-"D`40@#`$-(?
  1008. MLC`ZAT$D+$$ZF4$D.HM!)+(B14Y$(J>9(I,B.IXT.3$U,CJ``(@(!`"!1K(P/
  1009. MI#,Q.E&RI4@HQBC**$$D+$:L,JHQ*2DIK#$VJJ5(*,8HRBA!)"Q&K#*J,BDI:
  1010. M*0"I"`4`0TBR0TBJ43J74RQ1.E.R4ZHQ.H(ZBT-(LD&G,P#!"`8`F2)#2$5#F
  1011. M2U-532!%4E)/4B(Z@`#'"%H`@``4"60`@R`W.$$Y-T8X1#!$1$-!.3`Q.$0QX
  1012. M040P.$0Q-40P03DW,3A$,30P,T$Y0S`X1#$U,#-!.49!.$0Q,D0P03E%-CA$H
  1013. M+"`T,#`S`&$)90"#(#`Q1#!!,C9&03`P,#A#,3=$,$$Y,S(X-3`Q0D0P,$0PX
  1014. M.3DP,$-%.3DW,$-%0D0P,$0X.3DP,$-&.3DW,$-&0S@L(#0P,S``K@EF`(,@H
  1015. M0T$Q,$5!03DS-S@U,#%!,#!&0CE$14,P.3DP,$-$.3DR,$-$03DQ.#,X1CE$`
  1016. M14,P.3DQ,$-$.3DS,$-$.#@Q,"P@-#$W-@#["6<`@R!%.$$P-#!".3`P0T0Y8
  1017. M.30P0T0Y.3@P0T0X.#$P1C0U.#8P03DQ,SA$,3%$,$5!03`V1D5%1D9#1C(T+
  1018. M14%%04(Y+"`T-S@R`$@*:`"#(#`P0T0X1#$V1#!"13`P0T5".3`P0T8X1$9&>
  1019. M,T8X149&,T8X1$9&,T8X149&,T8X1$9&,T8X149&,T8X1$9&,T8L(#0U.#``?
  1020. ME0II`(,@.$5&1C-&.$1&1C-&.$5&1C-&14%!.3`P.#@Q,$0P.$1&1C-&03DP`
  1021. M.#A$,39$,$$Y,4(X1#$Q1#!!.39&0T4X-BP@-#,S,0#B"FH`@R!#,#$P,#,XY
  1022. M1#@V0S`S.$5$.#9#,#A$.#E#,$%$.#!#,#$X13DP,3(Y,48X1#@P0S!%13$YO
  1023. M1#`T0S,Q14$P0S!#+"`S.3`U`"X+:P"#(#!$,$4P13!&,$8P1C!&,$8P1C!&W
  1024. M,$4P13!$,$,P0S!",$$P.3`Y,#@P.#`X,#@P.#`X,#@P.3`Y,$$P0D$R,#`L%
  1025. 4(#4P-P`["VP`@R!%3D0L+3$````PB
  1026. ``
  1027. end
  1028. size 830
  1029.  
  1030. =============================================================================
  1031. A Heavy-Duty Power Supply for the C-64
  1032. by John C. Andrews (no email address)
  1033.  
  1034.   As a Commodore User for the last 4 plus years, I am aware of the many
  1035.   articles and letters in the press that have bemoaned the burn-out
  1036.   problem of the C-64 power supply. When our Club BBS added a one meg
  1037.   drive and stayed on around the clock, the need for heavy-duty power
  1038.   supply became very apparent.... Three power supplies went in 3
  1039.   successive days!
  1040.  
  1041.   Part of the problem was my ignoring the seasons. You see during the
  1042.   winter I had set the power supply between the window and the screen,
  1043.   Yes, outside! With the advent of Spring... well, you get the picture.
  1044.  
  1045.   The turn-around time forgetting a new commerical supply was not in the
  1046.   best interest of the BBS and its members. Therefore, taking power
  1047.   supply inhand, I proceeded to cut one open on my shop bandsaw. I do
  1048.   not suggest that you do this. The parts are FIRMLY and COMPLETELY
  1049.   encased in a hard plastic potting compound. The purpose of this is not
  1050.   to make the item difficult to repair, but to make the entire unit
  1051.   conductive to the heat generated inside. I doubt the wisedom of
  1052.   potting the fuse as well. However, CBM was probably thinking of the
  1053.   number of little fingers that could fit into an accessable fuse
  1054.   holder. if you want the punch line it is: the final circuit board and
  1055.   its componets are about the size of a box of matches. This includes
  1056.   the built-in metal heat sink.
  1057.  
  1058.   From these minscule innards I traced out the circuit and increased the
  1059.   size of ALL components.
  1060.  
  1061.   The handiest source of electronic parts is, of course, Radio Shack.
  1062.   All but one part can be purchased there.
  1063.  
  1064.         212-1013        Capacitor, 35V, 4700 mF
  1065.         212-1022        Capacitor, 35V, 10 uF
  1066.         273-1515        Transformer, 2 Amp, 9-0-9 VAC
  1067.         276-1184        Rectifier
  1068.  
  1069.         270- 742        Fuse Block
  1070.         270-1275        Fuses
  1071.  
  1072.   Note that there are only five parts. The rest are fuses, fuse blocks,
  1073.   heat sinks, wire and misc. hardware. Note also that I have not listed
  1074.   any plugs and cords. This because you can clip the cords off of both
  1075.   sides of your defunct power supply. This will save you the hassle of
  1076.   wriing the DIN power plug correctly:
  1077.  
  1078.         DIN PIN OUT                   COLOR
  1079.           pin 6         9VAC            black
  1080.           pin 7         9VAC            black
  1081.           pin 5         +5 Volts        blue
  1082.           pin 1,2,3     shield, gnd     orange
  1083.  
  1084.   The part that you can NOT get at Radio Shack is the power regulator.
  1085.   This part will have to be scrounged up from some local, big
  1086.   electronics supply house:
  1087.  
  1088.         SK 9067    5 volt voltage regulator, 3+ amps. (I prefer the 5 amp.)
  1089.  
  1090.   Radio Shack does carry regulators, but their capacity is no larger
  1091.   than that with which you started.
  1092.  
  1093.   The Heat sinks, (yes, more than one!) are the key to the success of
  1094.   this project. The ones I used came from my Model Railroading days.
  1095.   Sorry to say, I did just ahve them 'lying about'. The heat sinks that
  1096.   I priced at the local electronics supply were more costly than the
  1097.   other parts. The worst case situation is that you may need to drill
  1098.   out a couple pieces of aluminum sheet. Try for 12 x 12, and bend them
  1099.   into square bottomed U-shapes to save room. heat sinks should not
  1100.   touch, or be electronically grounded to each other. You can also mount
  1101.   them on stand-offs from your chassis for total air circulation.
  1102.  
  1103.   The Radio Shack transformer is rated at only 2 amps. If you can not
  1104.   find one with a higher rating elsewhere, it is possible to hook two in
  1105.   parallel to get a 4 ampere output. This si tricky, as it can be done
  1106.   either right or wrong!
  1107.  
  1108.   Here is how to do it the right way:
  1109.     Tape off one yellow secondary lead on each transformer. With tape
  1110.     mark the four remaining secondary leads and letter them A and B on
  1111.     one transformer, C and D onthe other. Hook up the black primary
  1112.     leads to a plug to your 120 wall outlet:
  1113.  
  1114.                                     |-------------
  1115.    Note: *'s - indicate connections |            3 ||
  1116.          +'s - indicate skip overs  |            3 || (Transformer)
  1117.                                     |            3 ||
  1118.                                     |            3 ||
  1119.                                     |   ----------
  1120.                                     |   |
  1121.           +--\  /-------------------*---+---------
  1122.         --|120|/                        |        3 ||
  1123.         --|Vlt|             ____        |        3 ||
  1124.          -|Plg|------------|FUSE|-------*        3 ||
  1125.           +--/              ----        |        3 || (Transformer)
  1126.                                         |---------
  1127.  
  1128.     This would now be a good time to install a fuse in your 120 VAC
  1129.     line. Now before plugging this into the wall, tie two of the
  1130.     scondary leads (one from EACH transformer) together.
  1131.  
  1132.     Something like this: A--Xfmr--B+C--Xfmr--D
  1133.  
  1134.     Plug in your 120V side. Now using a VOM meter, measure the voltage
  1135.     between A and D.
  1136.       If the meter reads 18 volts, then:
  1137.         1. unplug from the 120.
  1138.         2. tie A and C together. tie B and D together.
  1139.         3. your 2 transformers will now give you 9 volts at 4 amps.
  1140.       If the meter reads 0 volts, then:
  1141.         1. unplug from the 120.
  1142.         2. tie A and D together. Tie B and C together.
  1143.         3. your 2 transformers will now give you 9 volts at 4 amps.
  1144.  
  1145.   Below is the file corresponding to the full schematic of the power supply.
  1146.   [Ed's note: in GeoPaint format, converted by convert 2.5, then uuencoded]. As
  1147.   you can see in the picture, I used only one transformer. Because it got hot,
  1148.   I epoxied a small heat sink to it. While this solved the heat problem, it did
  1149.   not increase the capacity of the total power supply.
  1150.  
  1151.   Note that I used fuses on all lines.
  1152.  
  1153. -----------------------------------------------------------------------------
  1154. begin 700 schematic.
  1155. M@PD,<V-H96UA=&ECH*"@H*"@H`D&`0=8!P8-,Q(`4%)'(&9O<FUA='1E9"!'
  1156. M14]3(&9I;&4@5C$N,``CF````"```.``0X(``$5P<V]N($U8+3@P`*"@H*``
  1157. MUH>-UH?(F!AE`H4"D`+F`V"@H*"@H*"@H`@%`0A6!`<,`"````"""@A30TA%
  1158. M34%424.@H*"@H*"@````````````$P!"3$%35$52)U,@0T].5D525$52(%8R
  1159. M+C4$6`<X"````@RP#0T].5D525*"@H*"@H*"@H"P2``99`Q$&`10`````
  1160. M```````````````````````````````````````#%;_____```.@``6?__F5
  1161. M55F:JJF555F:JJF555F:JJF555F:JJF?__F@``7```/___\```````-__[:`
  1162. M`/Y__[R#!P$``/__``!086EN="!);6%G92!6,2XQ````````````````````
  1163. M````````````9V5O4&%I;G0@("`@5C(N,``````@*$')!M`"J1*-(D"I`(TG
  1164. M0"#]/Y`%J0"-)T`@#!\@2$&I`(VL7ZD`A1&I!X40J3^%%ZGQA1:I7X4-J:R%
  1165. M#*!`J0X@3S&B_Z4"R0+P)LG_T`8@-$&X4*G)!M`-H$.I)B!/,2"APKA0F*VL
  1166. M7_`&(+T\(($\8%!A:0#_"@#_`3P!>`)+`EL"/P&R`38!B@%Z`38!C@(G`/\`
  1167. M_P#_`/\`_P#_`/\`_P#_`/\`_P#_`/\`_P#_`/\`_P#_`/\`_P#_`/\`_P#_
  1168. M`/\`_P#_`/\`_P#_`/\`````````````````````````````````````````
  1169. M````````````````````````````````````````````````````````````
  1170. M````````````````````````````````````````````````````````````
  1171. M`````````````````````````````````````````````````````````/\`
  1172. M_P#_`/\`_P#_`/\`E0`"/R!%````````_P"%``,0_Q!$````````_P"&``+X
  1173. M"/\`_P"B`/^_H;\``"`@,!@,_PP810``````_P``"!`0,&#@_\#`0P``````
  1174. M_P``A1`#\!`0H`"("/\`_P#*``(@/X8``>#_`+```3"'(*@``F`PAA"8`(@0
  1175. MH`"("/\`_P"B`/^_H;\`(*@`B!"8`(00!!\0$!!$`````/\```"$"`3X"`@(
  1176. M_P#_`,(``O__A@`"X."&((L`#0$!`0($@("`#A$0$!.%``-98D.%``.,4MZ%
  1177. M``/@D)"0``$'0P````````#_AP`!@,\`B""H_P#.``H#1$0HA0`##03$A0`#
  1178. M@("9AP`!!*``B""H`(@0H`"("/\`_P#_`.,`'2D1$1$0````).0$),0```"E
  1179. MI*2DF`````2HJ%!0HP"&(`)@8*(``1^$``P!$!#_````/-,``/B$``&`F`"(
  1180. M"/\`_P"B`/^_H;\``+```3"'(*@``F`PAA"8`(@0H`"("/\`_P"B`/^_H;\`
  1181. M(*@`B!"8`(00!!\0$!!$`````/\```"$"`3X"`@(_P#_`,(``O__A@`"X."&
  1182. M((L`#0$!`0($@("`#A$0$!.%``-98D.%``.,4MZ%``/@D)"0``$'0P``````
  1183. M``#_AP`!@,\`B""HS@`""`B&``(*BH8``@D!O@`"#PA#````````_P"&``R`
  1184. M8``/##`P#`PP`/^%``,\`/^%``/``/^%``$$1`#_`````````P#_`84`"L#_
  1185. M@,#@8"`@`/^'``'PAA"0``(&"(8`A!`,$A(2$V`0```\!(3.A``$<8J*BH0`
  1186. M#,`@("<````X*"!P((<(`0^'``&`_P#_`,<``S\@((8``N`<B``1!`4%`@("
  1187. M``"34E(B(B(``)N$20E(``"8)#P@))BR`(@(D0`Q`0$```$!`&"%A65EA85E
  1188. M,`P,,#`,##`B(CPB(B(\`$!,4DY24DX`!&25AH:59```@(0``8"A`(@@A0`+
  1189. M`0(*!!`0.$2"`0"`A@`"@$"0`(@0`H2$A@`"BG&&``(JRH8`#*"@`0$#`P4%
  1190. M,,```(0%"&`8!`3R"@D1_P#_`)H`_[^AOP``````````````````````````
  1191. M````````````````````````````````````````````````````````````
  1192. M````````````````````````````````````````````````````````````
  1193. M````````````````````````````````````````````````````````````
  1194. M``````````````````````````````````"<``(!`T(```````#__P(``(0@
  1195. M!N#@("`#`88`"/^`0"`0"`0$2O\``````````?B8`!<!`0```0$`986%966%
  1196. MA64P#`\P,`P,,$8``/\```````,``/Z%`H@``3^'`!3X"P0"`0```(#G@(`!
  1197. M@D0X(+]`@$(``````/\``(0``A#_AA!#`/\````````8!?P$`@(!`0"-B8G9
  1198. M<7$`P.$A(2(2#`08_P#_`*T``@$#0@```````/__"P```"`@(.#@("`@B``$
  1199. M`@$!`8@`'("`@$```'E$1'A$1```@("8I9VE```(",DJ#`S)`#<!`0```0$`
  1200. M986%966%A64P#`PP,`P,,`!$"D0H*1$1$0`-!,0DY`0D`("`F:6DI*0````$
  1201. M!*BH4)```0-#`````````/^'``'PAQ"8`(@0CP`!#(<``0B(``(X#X8(`F"`
  1202. M_P#_`*``_[^AOP``````````````````````````````````````````````
  1203. M````````````````````````````````````````````````````````````
  1204. M````````````````````````````````````````````````````````````
  1205. M`````````````````````````````````````````````````````+``B""0
  1206. M`(5`%7]`0$1X````_P``I9P```#_```JR4(```#_``````<```#_```'A`0;
  1207. M_`0$_P`],3TQ,`#_`+.VL['G`/\`O#`\L#P`A8`#_X"`0@``````_P``-0`!
  1208. M`0``_P``986%8&"````P#`PP/P```!````#_````Q````/\```"8````_P``
  1209. M`%````#_AP`$X"`@(*@`B!"8`(00!!\0$!"$`!3_`````@$!`/\``0$("`B(
  1210. MCX@("(0`!/\```"$"`3X"`@(_P#_`,(``O__A@`"X."&((L`#0$!`0($@("`
  1211. M#A$0$!.%``-98D.%``.,4MZ%``/@D)"0``$'0P````````H`_X<``8#/`(@@
  1212. MJ`"($)@`B!"(``L"#```GJ&AH0@("(D`!&"PD("("/\`_P"B`/^_H;\```$,
  1213. MAP`!"(@``C@/A@@"8(#_`/\`H`#_OZ&_````````````````````````````
  1214. M````````````````````````````````````````````````````````````
  1215. M````````````````````````````````````````````````````````````
  1216. M````````````````````````````````````````````````````````````
  1217. M````````````L`"(((4`)@$"'`0($"!`_P``$1$.``#_``!"0D$``/\``!!2
  1218. MC```_P``D)"04```_P``````#0``_P``("`P&`S_#!A%``````#_```($!`P
  1219. M8.#_P,!#``````#_``"%$`/P$!"(``2AH:&>A``)`2(B(AP```#`A(`#````
  1220. MB`C_`/\`R@`"(#^&``'@_P"P``,P(`"%(*@``F`PAA"8`(@0H`"("/\`_P"B
  1221. M`/^_H;\`_P`!`0@("(B/B`@(A``$_P```(0(!/@("`C_`/\`P@`"__^&``+@
  1222. MX(8@BP`-`0$!`@2`@(`.$1`0$X4``UEB0X4``XQ2WH4``^"0D)#_`.D`B""H
  1223. M`(@0F``*B!"@`(@(_P#_`/\`_P"$`(@@J`"($)@`B!"@`(@(_P#_`*(`_[^A
  1224. MOP``#0``_P``("`P&`S_#!A%``````#_```($!`P8.#_P,!#``````#_``"%
  1225. M$`/P$!"(``2AH:&>A``)`2(B(AP```#`A(`#````B`C_`/\`R@`"(#^&``'@
  1226. M_P"P``,P(`"%(*@``F`PAA"8`(@0H`"("/\`_P"B`/^_H;\`_P`!`0@("(B/
  1227. MB`@(A``$_P```(0(!/@("`C_`/\`P@`"__^&``+@X(8@BP`-`0$!`@2`@(`.
  1228. M$1`0$X4``UEB0X4``XQ2WH4``^"0D)#_`.D`B""H`(@0F`"($*``B`C_`/\`
  1229. M_P#S``$#AP(8_P``>V-[8V'_``!G;&9CSO\``'A@>&!XB("8`(@0B`"(`1G_
  1230. M```],3TQ,/\``+.VL['G_P``O#`\L#S`AT")``,?$!"$$Q@(_P``VQO;&P#_
  1231. M```[8S,;`/P$!,0$Q`3_`/\`D@#_OZ&_`(8``>#_`+```S`@`(4@J``"8#"&
  1232. M$)@`B!"@`(@(_P#_`*(`_[^AOP#_``$!"`@(B(^("`B$``3_````A`@$^`@(
  1233. M"/\`_P#"``+__X8``N#@AB"+``T!`0$"!("`@`X1$!`3A0`#66)#A0`#C%+>
  1234. MA0`#X)"0D/\`V0`#`@(#0@``````"@``_X40`P``_X4``X"`@)T`B!"(``,!
  1235. M`0%"`````````/^%"`,``/^%``-`0,"-``03$!`?A``$#@``_X0$!',``/^$
  1236. M``3$!`3\_P#_`/\`]P"($*@`B!"8`(@(H`"(!/\`_P"B`/^_H;\`!,0$Q`3_
  1237. M`/\`D@#_OZ&_`(8``>#_`+```S`@`(4@J``"8#"&$)@`B!"@`(@(_P#_`*(`
  1238. M_[^AOP#_``$!"`@(B(^("`B$``3_````A`@$^`@("/\`_P#"``+__X8``N#@
  1239. MAB"+``T!`0$"!("`@`X1$!`3A0`#66)#A0`#C%+>A0`#X)"0D/\`Z0"($*@`
  1240. MB!"8`(@(H`"(!/\`_P#_`/\`A`"($*@`B!"8`(@(H`"(!/\`_P"B`/^_H;\`
  1241. M_X4``T!`P(T`!!,0$!^$``0.``#_A`0$<P``_X0`!,0$!/S_`/\`_P#W`(@0
  1242. MJ`"($)@`B`B@`(@$_P#_`*(`_[^AOP`$Q`3$!/\`_P"2`/^_H;\`A@`!X/\`
  1243. ML``#,"``A2"H``)@,(80F`"($*``B`C_`/\`H@#_OZ&_`/\``0$("`B(CX@(
  1244. M"(0`!/\```"$"`3X"`@(_P#_`,(``O__A@`"X."&((L`#0$!`0($@("`#A$0
  1245. M$!.%``-98D.%``.,4MZ%``/@D)"0_P#I`(@0J`"($)@`B`B@``J(!/\`_P#_
  1246. M`/@``P,$!(4``X%!0(00!``1$:*%``,.$9"4``0'"`@(A``,`H*!@1`0$``B
  1247. M(D5%A``$'"(@((<`"P,````?$!`>P0@(B0`-<HN#@IH````N*2BHJ(4`$X"`
  1248. M@`````$!!P$!`!\0$![!`1'_`/\`H@#_OZ&_`/\`L``#,"``A2"H``)@,(80
  1249. MF`"($*``B`C_`/\`H@#_OZ&_`/\``0$("`B(CX@("(0`!/\```"$"`3X"`@(
  1250. M_P#_`,(``O__A@`"X."&((L`#0$!`0($@("`#A$0$!.%``-98D.%``.,4MZ%
  1251. M``/@D)"0_P#9``P$`P```P```$#`0("$``VBI$=$1````)!0T%%.DP`*!P`!
  1252. M!@````^!@(4`$1!(CXB(`````Z"@HIP```#@A@`C`P(!$0X```#$(```("!`
  1253. M````BHIR````0<)H:2X```#!(("%``+P((0`#`<$!`0.````B$!;2H0`!`$!
  1254. M@4&$``3P``#@_P#_`/\`XP`3!P0$!`<$!`2(0%M*B@H*"@``@81!"4!@@`#@
  1255. M$!`0X)``"P@("`\("`@`@+>4A!0#````A8`:`"`@0$"`@(```@(#`@("```M
  1256. M)<4%!04``,"%(!X``$!`0$%"2P@0($"```'D!`A`X!`0$.````<$!`2$``2*
  1257. >"@H*A``$0$`*04"$``00$!#@_P#_`)8`_[^AOP`*
  1258. `
  1259. end
  1260.  
  1261. =============================================================================
  1262. LZW Compression
  1263. by Bill Lucier (Blucier@ersys.edmonton.ab.ca, b.lucier1 on Genie)
  1264.  
  1265. LZW is perhaps the most widely used form of data compression today. It
  1266. is simple to implement and achieves very decent compression at a fairly
  1267. quick pace. LZW is used in PKZIP (shrink),PKARC (crunch), gifs,V.42bis
  1268. and unix's compress. This article will attempt to explain how the
  1269. compression works with a short example and 6502 source code in Buddy
  1270. format.
  1271.  
  1272. Originally named lz78, it was invented by Jacob Ziv and Abraham Lempel
  1273. in 1978 , it was later modified by Terry Welch to its present format.
  1274. The patent for the LZW compression method is presently held by Unisys.
  1275.  
  1276. LZW compresses data by taking phrases and compressing them into codes.
  1277. The size of the codes could vary from 9 bits to 16 bits. Although for
  1278. this implementation we will be using only 12 bits. As byte are read in
  1279. from a file they are added to a dictionary. For a 12-bit implementation
  1280. a dictionary will be 4k (2^12=4096) . Each entry in the dictionary
  1281. requires five bytes, which will be built in the form of a tree. It is
  1282. not a binary tree because each node may have more than two offsprings.
  1283. In fact, because our dictionary can hold up to 4096 different codes it
  1284. is possible to have one node with 3800 children nodes, although this is
  1285. not likely to happen. The five bytes that make up our tree will be:
  1286.  
  1287. The parent code: Each node has one and only one parent node. When the parent
  1288.                  code is less then 255 it is the end of a phrase. The codes
  1289.                  0-255 do not actually exist in the tree. The following
  1290.                  values do not appear either as they have special meaning:
  1291.  
  1292.                  256 : End of Stream-This marks the end of one compressed file
  1293.                  257 : This tells the decompressor to increase the number
  1294.                        of bits its reading by one.
  1295.                  258 : Wipe out dictionary
  1296.                  
  1297. The code value : This is the code that will be sent to the compressor. 
  1298. The character  : The value contained at this node. It have a value of 0-255.
  1299.  
  1300. Initially we send out codes that are 9 bits long, this will cover the values
  1301. 0-511. Once we have reached 511, we will need to increase the number of
  1302. bits to write by 1. This will give room for code numbers 512-1023, or
  1303. (2^10)-1. At this point we must ensure that the decompressor knows how
  1304. bits to read in at once so a code number 257 is sent to indicate that
  1305. the number of bits to be read is to be bumped up by one. The size of the
  1306. dictionary is finite so at some point we do have to be concerned with
  1307. what we will do when it does fill up. We could stop compiling new
  1308. phrases and just compress with the ones that are already in the
  1309. dictionary. This is not a very good choice, files tend to change
  1310. frequently (eg. program files as they change from code to data) so
  1311. sticking with the same dictionary will actually increase the size of the
  1312. file or at best, give poor compression. Another choice is to wipe the
  1313. dictionary out and start building new codes and phrases, or wipe out
  1314. some of the dictionary leaving behind only the newer codes and phrases.
  1315. For the sake of simplicity this program will just wipe out the
  1316. dictionary when it becomes full.
  1317.  
  1318. To illustrate how LZW works a small phrase will be compressed : heher.
  1319. To start the first two characters would be read in. The H would be
  1320. treated as the parent code and E becomes the character code. By means of
  1321. a hashing routine (the hashing routine will be explained more fully in
  1322. the source code) the location where HE should be is located. Since we
  1323. have just begun there will be nothing there,so the phrase will be added
  1324. to the dictionary. The codes 0-258 are already taken so we start using
  1325. 259 as our first code. The binary tree would look something like this:
  1326.             
  1327.           node # 72 - H
  1328.                  |
  1329.      node #3200 259 - E
  1330.  
  1331.  The node # for E is an arbitrary one. The compressor may not choose
  1332. that location, 3200 is used strictly for demonstration purposes. So at
  1333. node #3200 the values would be:
  1334.  
  1335. Parent code - 72
  1336. code value  - 259
  1337. character   - E
  1338.  
  1339. The node #72 is not actually used. As soon as a value less than 255 is
  1340. found it is assumed to be the actual value. We can't compress this yet
  1341. so the value 72 is sent to the output file(remember that it is sent in 9
  1342. bits). The E then becomes the parent code and a new character code ( H )
  1343. is read in. After again searching the dictionary the phrase EH is not
  1344. found. It is added to the dictionary as code number 260. Then we send
  1345. the E to the disk and H becomes the new parent code and the next E
  1346. becomes the new character code. After searching the dictionary we find
  1347. that we can compress HE into the code 259,we want to compress as much as
  1348. possible into one code so we make 259 the parent code. There may be a
  1349. longer string then HE that can be compressed. The R is read in as the
  1350. new character code. The dictionary is searched for the a 259 followed a
  1351. R, since it is not found it is added to the dictioary and it looks like
  1352. this:
  1353.  
  1354.            node #72 - H             node #69 - E
  1355.                  |                        | 
  1356.     node #3200  259 - E    node #1600    260 - H
  1357.                  |
  1358.     node #1262  261 - R
  1359.  
  1360. Then the value 259 is sent to the output file (to represent the HE) and
  1361. since that is the EOF the R is sent as well,as well as a 256 to indicate
  1362. the EOF has been reached.
  1363.  
  1364. Decompression is extremely simple. As long as the decompressor maintains
  1365. the dictionary as the compressor did, there will be no problems,except
  1366. for one problem that can be handled as an exceptional case. All of the
  1367. little details of increasing the number of bits to read, and when to
  1368. flush the dictionary are taken care of by the compressor. So if the
  1369. dictionary was increased to 8k, the compressor would have to be set up
  1370. to handle a larger dictionary, but the decompressor only does as the
  1371. compressed file tells it to and will work with any size dictionary. The
  1372. only problem would be that a larger dictionary will creep into the ram
  1373. under the rom or possibly even use all available memory, but assuming
  1374. that the ram is available the decompressor will not change. The
  1375. decompressor would start out reading 9 bits at a time, and starts it
  1376. free code at 259 as the compressor did. To use the above input from the
  1377. compressor as an example, the output was:
  1378.  
  1379. 72  - For the First H
  1380. 69  - For the First E
  1381. 259 - For the Compressed HE
  1382. 82  - For the R
  1383. 256 - Eof indicator
  1384.  
  1385.  To begin decompressing, two values are needed. The H and E are read in,
  1386. (note they will both be 9 bits long). As they are both below 256 they
  1387. are at the end of the string and are sent straight to the output file.
  1388. The first free code is 259 so that is the value assigned to the phrase
  1389. HE. Note when decompressing there is no need for the hashing routine,
  1390. the codes are the absolute locations in the dictionary (i.e. If the
  1391. dictionary was considered to be an array then the entry number 259 would
  1392. be dictionary[259]), because of this, the code value is no longer
  1393. needed. So the decompressor would have an entry that looks like this:
  1394.  
  1395. Node # 259
  1396. Parent Code - H
  1397. Character   - E
  1398.  
  1399. The decompressor will read in the next value (259). Because the node
  1400. number is at the end of the compressed string we will have to take the
  1401. code value and place it on a stack, and take them off in a
  1402. Last-in,First-out (LIFO) fashion. That is to say that the first
  1403. character to go on the stack (in this case the E) will be the last to
  1404. come off. The size of the stack is dependent on the size of the
  1405. dictionary, so for this implementation we need a stack that is 4k long.
  1406. After all the characters from the string have been placed on the stack
  1407. they are taken off and sent to the outputfile.
  1408.  
  1409.   There is one small error that is possible with LZW because of the way
  1410. the compressor defines strings. Consider the compression dictionary that
  1411. has the following in it:
  1412.  
  1413.      node #   Code         Parent  character 
  1414.               Value         code 
  1415.      ------   ------        ------  ---------
  1416.         65      65           n/a       A 
  1417.        723     259            65       C
  1418.       1262     260           259       U
  1419.       2104     261           260       T
  1420.       2506     262           261       E
  1421.  
  1422. Now if the compressor was to try to compress the string ACUTEACUTEA The
  1423. compressor will find a match for the first five characters 'ACUTE' and
  1424. will send a 262 to the output file. Then it will add the following entry
  1425. to the dictionary:
  1426.  
  1427.       3099     263           262       A
  1428.  
  1429. Now it will try to compress the remaining characters, and it finds that
  1430. it can compress the entire string with the code 263, but notice that the
  1431. middle A, the one that was just added onto the end of the string 'ACUTE'
  1432. was never sent to the output file. The decompressor will not have the
  1433. code 263 defined in it's dictionary. The last code it will have defined
  1434. will be 262. This problem is easily remedied though, when the
  1435. decompressor does not have a code defined, it takes the first letter
  1436. from the last phrase defined and tacks it onto the end of the last
  1437. phrase. IE It takes the first letter (the A) from the phrase and adds it
  1438. on to the end as code #263.
  1439.  
  1440. This particular implementation is fairly slow because it reads a byte
  1441. and then writes one, it could be made much faster with some buffering.
  1442. It is also limited to compressing and decompressing one file at a time
  1443. and has no error checking capabilities. It is meant strictly to teach
  1444. LZW compression, not provide a full fledged compressor.
  1445.  
  1446. And now for the code:
  1447.   
  1448. SYS 4000      ; sys 999 on a 64
  1449. .DVO 9        ; or whatever drive used for output
  1450. .ORG 2500
  1451. .OBJ "LZW.ML"
  1452.  
  1453. TABLESIZE =5021     
  1454.  
  1455. ; THE TABLESIZE IS ACTUALLY 5021, ABOUT 20% LARGER THEN 4K. THIS GIVES
  1456. ; THE HASHING ROUTINE SOME ROOM TO MOVE. IF THE TABLE WAS EXACTLY 4K
  1457. ; THERE WOULD BE FREQUENT COLLISIONS WHERE DIFFERENT COMBINATIONS OF
  1458. ; CHARACTERS WOULD HAVE THE SAME HASH ADDRESS. INCREASING THE TABLE SIZE
  1459. ; REDUCES THE NUMBER OF COLLISIONS.
  1460.  
  1461. EOS =256          ; eos = End of stream This marks the end of file
  1462.  
  1463. FIRSTCODE =259
  1464. MAXCODE =4096
  1465.  
  1466. BUMPCODE =257     ; Whenever a 257 is encountered by the decompressor it
  1467.                   ; increases the number of bits it reads by 1
  1468.  
  1469. FLUSHCODE =258   
  1470.  
  1471. TABLEBASE =14336  ; The location that the dictionary is located at
  1472.  
  1473. DECODESTACK =9300 ; The location of the 4k LIFO stack
  1474.  
  1475. ; ORG = DECOMPRESS FILE
  1476. ; ORG + 3 = COMPRESS FILE
  1477.  
  1478. JMP EXPANDFILE
  1479.  
  1480. ;********************************
  1481. ; COMPRESSFILE
  1482. ;********************************
  1483.  
  1484. COMPRESSFILE JSR INITDIC ; EMPTY THE DICTIONARY
  1485. LDA #128
  1486. STA BITMASK
  1487. LDA #0
  1488. STA RACK
  1489. JSR GETCHAR      ; GET A CHAR FROM THE INPUT FILE
  1490. STA STRINGCODE   ; INITIALIZE THE STRINGCODE (PARENT CODE)
  1491. LDA #0
  1492. STA STRINGCODE+1
  1493. NEXTCHAR JSR GETCHAR
  1494.          STA CHARACTER
  1495.          JSR FINDNODE    ; FINDNODE CALCULATES THE HASHED LOCATION OF
  1496.          LDA ($FE),Y     ; THE STRINGCODE AND CHARACTER  IN THE DICT.
  1497.          INY             ; AND SETS $FE/$FF POINTING TO IT. IF THE ENTRY
  1498.          AND ($FE),Y     ; HAS TWO 255 IN IT THEN IT IS EMPTY AND SHOULD
  1499.          CMP #255        ; BE ADDED TO THE DICTIONARY.
  1500.          BEQ ADDTODICT
  1501.              LDA ($FE),Y     ; IT HAS A DEFINED PHRASE. STORE THE CODE VALUE IN
  1502.              STA STRINGCODE+1; THE PARENT CODE
  1503.              DEY
  1504.              LDA ($FE),Y
  1505.              STA STRINGCODE
  1506.          JMP EOF
  1507.          ADDTODICT LDY #0
  1508.          - LDA NEXTCODE,Y
  1509.            STA ($FE),Y
  1510.            INY
  1511.            CPY #5
  1512.          BNE -
  1513.          INC NEXTCODE             ; INCREASE THE NEXTCODE
  1514.          BNE +
  1515.              INC NEXTCODE+1
  1516.          + JSR OUTPUT
  1517.          LDA NEXTCODE+1          ; CHECK IF NEXTCODE=4096 IF SO THEN FLUSH THE
  1518.          CMP #>MAXCODE           ; DICTIONARY AND START ANEW
  1519.          BNE CHECKBUMP
  1520.          LDA NEXTCODE
  1521.          CMP #<MAXCODE
  1522.          BNE CHECKBUMP
  1523.          LDA #<FLUSHCODE        ; SEND THE FLUSH CODE TO THE COMPRESSED FILE SO
  1524.          STA STRINGCODE         ; THE DECOMPRESSOR WILL KNOW TO FLUSH THE
  1525.          LDA #>FLUSHCODE        ; DICTIONARY
  1526.          STA STRINGCODE+1
  1527.          JSR OUTPUT
  1528.          JSR INITDIC
  1529.          JMP CHECKEOF
  1530.          CHECKBUMP LDA NEXTBUMP+1
  1531.          CMP NEXTCODE+1         ; CHECKBUMP CHECK TO SEE IF THE NEXTCODE HAS
  1532.          BNE CHECKEOF        ; REACHED THE MAXIMUM VALUE FOR THE CURRENT
  1533.          LDA NEXTBUMP                    ; NUMBER OF BITS BEING OUTPUT.
  1534.          CMP NEXTCODE        ; FOR     X  BITS     NEXTCODE HAS Y PHRASES
  1535.          BNE CHECKEOF        ;        --------     -----------------------
  1536.          LDA #>BUMPCODE      ;           9                 511
  1537.          STA STRINGCODE+1    ;          10                1023
  1538.          LDA #<BUMPCODE      ;          11                2047
  1539.          STA STRINGCODE      ;          12                4095
  1540.          JSR OUTPUT
  1541.          INC CURRENTBITS
  1542.          ASL NEXTBUMP
  1543.          ROL NEXTBUMP+1
  1544.          CHECKEOF LDA #0
  1545.          STA STRINGCODE+1
  1546.          LDA CHARACTER
  1547.          STA STRINGCODE
  1548.          EOF LDA 144
  1549.          BNE DONE
  1550. JMP NEXTCHAR
  1551. DONE JSR OUTPUT
  1552. LDA #>EOS               ; SEND A 256 TO INDICATE EOF
  1553. STA STRINGCODE+1
  1554. LDA #<EOS
  1555. STA STRINGCODE
  1556. JSR OUTPUT
  1557. LDA BITMASK
  1558. BEQ +
  1559.     JSR $FFCC
  1560.     LDX #3
  1561.     JSR $FFC9
  1562.     LDA RACK              ; SEND WHAT BITS WEREN'T SEND WHEN OUTPUT
  1563.     JSR $FFD2
  1564. + JSR $FFCC
  1565. LDA #3
  1566. JSR $FFC3
  1567. LDA #2
  1568. JMP $FFC3
  1569.  
  1570. ;**********************************
  1571. ; INITDIC
  1572. ; INITIALIZES THE DICTIONARY, SETS
  1573. ; THE NUMBER OF BITS TO 9
  1574. ;**********************************
  1575.  
  1576. INITDIC LDA #9
  1577. STA CURRENTBITS
  1578. LDA #>FIRSTCODE
  1579. STA NEXTCODE+1
  1580. LDA #<FIRSTCODE
  1581. STA NEXTCODE
  1582. LDA #>512
  1583. STA NEXTBUMP+1
  1584. LDA #<512
  1585. STA NEXTBUMP
  1586. LDA #<TABLEBASE
  1587. STA $FE
  1588. LDA #>TABLEBASE
  1589. STA $FF
  1590. LDA #<TABLESIZE
  1591. STA $FC
  1592. LDA #>TABLESIZE
  1593. STA $FD
  1594. - LDY #0
  1595.   LDA #255      ; ALL THE CODE VALUES ARE INIT TO 255+256*255
  1596.   STA ($FE),Y   ; OR -1 IN TWO COMPLEMENT
  1597.   INY
  1598.   STA ($FE),Y
  1599.   CLC
  1600.   LDA #5        ; EACH ENTRY IN THE TABLE TAKES 5 BYTES
  1601.   ADC $FE
  1602.   STA $FE
  1603.   BCC +
  1604.       INC $FF
  1605.   + LDA $FC
  1606.   BNE +
  1607.       DEC $FD
  1608.   + DEC $FC
  1609.   LDA $FD
  1610.   ORA $FC
  1611. BNE -
  1612. RTS
  1613.  
  1614. ;************************************
  1615. ; GETCHAR
  1616. ;************************************
  1617.  
  1618. GETCHAR JSR $FFCC
  1619. LDX #2
  1620. JSR $FFC6
  1621. JMP $FFCF
  1622.  
  1623. ;************************************
  1624. ; OUTPUT
  1625. ;************************************
  1626.  
  1627. OUTPUT LDA #0      ; THE NUMBER OF BITS OUTPUT CAN BE OF A VARIABLE
  1628. STA MASK+1         ; LENGTH,SO THE BITS ARE ACCUMULATED TO A BYTE IS
  1629. LDA #1             ; FULL AND THEN IT IS SENT TO THE OUTPUT FILE
  1630. LDX CURRENTBITS
  1631. DEX
  1632. - ASL
  1633.   ROL MASK+1
  1634.   DEX
  1635. BNE -
  1636. STA MASK
  1637. MASKDONE LDA MASK
  1638. ORA MASK+1
  1639. BNE +
  1640.     RTS
  1641. + LDA MASK
  1642. AND STRINGCODE
  1643. STA 3
  1644. LDA MASK+1
  1645. AND STRINGCODE+1
  1646. ORA 3
  1647. BEQ NOBITON
  1648.     LDA RACK
  1649.     ORA BITMASK
  1650.     STA RACK
  1651. NOBITON LSR BITMASK
  1652. LDA BITMASK
  1653. BNE +
  1654.     JSR $FFCC
  1655.     LDX #3
  1656.     JSR $FFC9
  1657.     LDA RACK
  1658.     JSR $FFD2
  1659.     LDA #0
  1660.     STA RACK
  1661.     LDA #128
  1662.     STA BITMASK
  1663. + LSR MASK+1
  1664. ROR MASK
  1665. JMP MASKDONE
  1666.  
  1667. ;******************************
  1668. ; FINDNODE
  1669. ; THIS SEARCHES THE DICTIONARY TILL IT FINDS A PARENT NODE THAT MATCHES
  1670. ; THE STRINGCODE AND A CHILD NODE THAT MATCHES THE CHARACTER OR A EMPTY
  1671. ; NODE.
  1672. ;*******************************
  1673.  
  1674. ; THE HASHING FUNCTION - THE HASHING FUNCTION IS NEEDED BECAUSE
  1675. ; THERE ARE 4096 X 4096 (16 MILLION) DIFFERENT COMBINATIONS OF
  1676. ; CHARACTER AND STRINGCODE. BY MULTIPLYING THE CHARACTER AND STRINGCODE
  1677. ; IN A FORMULA WE CAN DEVELOP OF METHOD OF FINDING THEM IN THE
  1678. ; DICTIONARY. IF THE STRINGCODE AND CHARACTER IN THE DICTIONARY
  1679. ; DON'T MATCH THE ONES WE ARE LOOKING FOR WE CALCULATE AN OFFSET
  1680. ; AND SEARCH THE DICTIONARY FOR THE RIGHT MATCH OR A EMPTY
  1681. ; SPACE IS FOUND. IF AN EMPTY SPACE IS FOUND THEN THAT CHARACTER AND
  1682. ; STRINGCODE COMBINATION IS NOT IN THE DICTIONARY
  1683.  
  1684. FINDNODE LDA #0
  1685. STA INDEX+1
  1686. LDA CHARACTER     ; HERE THE HASHING FUNCTION IS APPLIED TO THE
  1687. ASL               ; CHARACTER AND THE STRING CODE. FOR THOSE WHO
  1688. ROL INDEX+1       ; CARE THE HASHING FORMULA IS:
  1689. EOR STRINGCODE    ; (CHARACTER << 1) ^ STRINGCODE
  1690. STA INDEX         ; FIND NODE WILL LOOP TILL IT FINDS A NODE
  1691. LDA INDEX+1       ; THAT HAS A EMPTY NODE OR MATCHES THE CURRENT
  1692. EOR STRINGCODE+1  ; PARENT CODE AND CHARACTER
  1693. STA INDEX+1
  1694. ORA INDEX 
  1695. BNE +
  1696.     LDX #1
  1697.     STX OFFSET
  1698.     DEX
  1699.     STX OFFSET+1
  1700.     JMP FOREVELOOP
  1701. + SEC
  1702. LDA #<TABLESIZE
  1703. SBC INDEX
  1704. STA OFFSET
  1705. LDA #>TABLESIZE
  1706. SBC INDEX+1
  1707. STA OFFSET+1
  1708.  
  1709. FOREVELOOP JSR CALCULATE     
  1710.            LDY #0
  1711.            LDA ($FE),Y
  1712.            INY
  1713.            AND ($FE),Y
  1714.            CMP #255
  1715.            BNE +
  1716.                LDY #0
  1717.                RTS
  1718.            + INY
  1719.            - LDA ($FE),Y
  1720.              CMP STRINGCODE-2,Y
  1721.              BNE +
  1722.              INY
  1723.              CPY #5
  1724.              BNE -
  1725.           LDY #0
  1726.           RTS
  1727.           + SEC
  1728.           LDA INDEX
  1729.           SBC OFFSET
  1730.           STA INDEX
  1731.           LDA INDEX+1
  1732.           SBC OFFSET+1
  1733.           STA INDEX+1
  1734.           AND #128
  1735.           BEQ FOREVELOOP
  1736.           CLC
  1737.           LDA #<TABLESIZE
  1738.           ADC INDEX
  1739.           STA INDEX
  1740.           LDA #>TABLESIZE
  1741.           ADC INDEX+1
  1742.           STA INDEX+1
  1743. JMP FOREVELOOP
  1744.  
  1745. ;***************************
  1746. ; CALCULATE
  1747. ; TAKES THE VALUE IN INDEX AND CALCULATES ITS LOCATION IN THE DICTIONARY
  1748. ;****************************
  1749.  
  1750. CALCULATE LDA INDEX
  1751. STA $FE
  1752. LDA INDEX+1
  1753. STA $FF
  1754. ASL $FE
  1755. ROL $FF
  1756. ASL $FE
  1757. ROL $FF
  1758. CLC
  1759. LDA INDEX
  1760. ADC $FE
  1761. STA $FE
  1762. LDA INDEX+1
  1763. ADC $FF
  1764. STA $FF
  1765. CLC
  1766. LDA #<TABLEBASE
  1767. ADC $FE
  1768. STA $FE
  1769. LDA #>TABLEBASE
  1770. ADC $FF
  1771. STA $FF
  1772. LDY #0
  1773. RTS
  1774.  
  1775. ;******************************
  1776. ; DECODESTRING
  1777. ;******************************
  1778.  
  1779. DECODESTRING TYA   ; DECODESTRING PUTS THE STRING ON THE STACK
  1780. STA COUNT          ; IN A LIFO FASHION.
  1781. LDX #>DECODESTACK
  1782. CLC
  1783. ADC #<DECODESTACK
  1784. STA $FC
  1785. STX $FD
  1786. LDA #0
  1787. STA COUNT+1
  1788. - LDA INDEX+1
  1789.   BEQ +
  1790.   JSR CALCULATE
  1791.   LDY #4
  1792.   LDA ($FE),Y
  1793.   LDY #0
  1794.   STA ($FC),Y
  1795.   LDY #2
  1796.   LDA ($FE),Y
  1797.   STA INDEX
  1798.   INY
  1799.   LDA ($FE),Y
  1800.   STA INDEX+1
  1801.   JSR INFC
  1802. JMP -
  1803. + LDY #0
  1804. LDA INDEX
  1805. STA ($FC),Y
  1806. INC COUNT
  1807. BNE +
  1808.     INC COUNT+1
  1809. + RTS
  1810.  
  1811. ;******************************
  1812. ; INPUT
  1813. ;******************************
  1814.  
  1815. INPUT LDA #0  ; THE INPUT ROUTINES IS USED BY THE DECOMPRESSOR
  1816. STA MASK+1    ; TO READ IN THE VARIABLE LENGTH CODES
  1817. STA RETURN
  1818. STA RETURN+1
  1819. LDA #1
  1820. LDX CURRENTBITS
  1821. DEX
  1822. - ASL
  1823.   ROL MASK+1
  1824.   DEX
  1825. BNE -
  1826. STA MASK
  1827. - LDA MASK
  1828.   ORA MASK+1
  1829.   BEQ INPUTDONE
  1830.   LDA BITMASK
  1831.   BPL +
  1832.       JSR GETCHAR
  1833.       STA RACK
  1834.   + LDA RACK
  1835.   AND BITMASK
  1836.   BEQ +
  1837.       LDA MASK
  1838.       ORA RETURN
  1839.       STA RETURN
  1840.       LDA MASK+1
  1841.       ORA RETURN+1
  1842.       STA RETURN+1
  1843.   + LSR MASK+1
  1844.   ROR MASK
  1845.   LSR BITMASK
  1846.   LDA BITMASK
  1847.   BNE +
  1848.       LDA #128
  1849.       STA BITMASK
  1850. + JMP -
  1851. INPUTDONE RTS
  1852.  
  1853. ;*******************************
  1854. ; EXPANDFILE
  1855. ; WHERE THE DECOMPRESSION IS DONE
  1856. ;*******************************
  1857.  
  1858. EXPANDFILE LDA #0
  1859. STA RACK
  1860. LDA #128
  1861. STA BITMASK
  1862. START JSR INITDIC
  1863. JSR INPUT
  1864. LDA RETURN+1
  1865. STA OLDCODE+1       ; Save the first character in OLDCODE
  1866. LDA RETURN
  1867. STA CHARACTER
  1868. STA OLDCODE
  1869. CMP #<EOS
  1870. BNE +
  1871.     LDA RETURN+1    ; If return = EOS (256) then all done 
  1872.     CMP #>EOS
  1873.     BNE +
  1874.     JMP CLOSE
  1875. + JSR $FFCC
  1876. LDX #3
  1877. JSR $FFC9
  1878. LDA OLDCODE         ; Send oldcode to the output file
  1879. JSR $FFD2
  1880. NEXT JSR INPUT
  1881.      LDA RETURN
  1882.      STA NEWCODE
  1883.      LDA RETURN+1
  1884.      STA NEWCODE+1
  1885.      CMP #1         ; All of the special codes Flushcode,BumpCode & EOS
  1886.      BNE ++         ; Have 1 for a MSB.
  1887.          LDA NEWCODE
  1888.          CMP #<BUMPCODE
  1889.          BNE +
  1890.              INC CURRENTBITS
  1891.              JMP NEXT
  1892.          + CMP #<FLUSHCODE
  1893.          BEQ START
  1894.          CMP #<EOS
  1895.          BNE +
  1896.          JMP CLOSE
  1897.      + SEC          ; Here we compare the newcode just read in to the
  1898.      LDA NEWCODE    ; next code. If newcode is greater than it is a 
  1899.      SBC NEXTCODE   ; ACUTEACUTEA situation and must be handle differently.
  1900.      STA 3
  1901.      LDA NEWCODE+1 
  1902.      SBC NEXTCODE+1
  1903.      ORA 3
  1904.      BCC +
  1905.          LDA CHARACTER
  1906.          STA DECODESTACK
  1907.          LDA OLDCODE
  1908.          STA INDEX
  1909.          LDA OLDCODE+1
  1910.          STA INDEX+1
  1911.          LDY #1
  1912.          BNE ++
  1913.      + LDA NEWCODE  ; Point index to newcode spot in the dictionary   
  1914.      STA INDEX      ; So DECODESTRING has a place to start
  1915.      LDA NEWCODE+1
  1916.      STA INDEX+1
  1917.      LDY #0
  1918.      + JSR DECODESTRING
  1919.      LDY #0
  1920.      LDA ($FC),Y
  1921.      STA CHARACTER
  1922.      INC $FC
  1923.      BNE +
  1924.          INC $FD
  1925.      + JSR $FFCC
  1926.      LDX #3
  1927.      JSR $FFC9
  1928.      L1 LDA COUNT+1  ; Count contains the number of characters on the stack
  1929.         ORA COUNT
  1930.         BEQ +       
  1931.         JSR DECFC
  1932.         LDY #0
  1933.         LDA ($FC),Y
  1934.         JSR $FFD2
  1935.      JMP L1
  1936.      + LDA NEXTCODE  ; Calculate the spot in the dictionary for the string
  1937.      STA INDEX       ; that was just entered.
  1938.      LDA NEXTCODE+1
  1939.      STA INDEX+1
  1940.      JSR CALCULATE
  1941.      LDY #2          ; The last character read in is tacked onto the end
  1942.      LDA OLDCODE     ; of the string that was just taken off the stack
  1943.      STA ($FE),Y     ; The nextcode is then incremented to prepare for the 
  1944.      INY             ; next entry.
  1945.      LDA OLDCODE+1
  1946.      STA ($FE),Y
  1947.      INY
  1948.      LDA CHARACTER
  1949.      STA ($FE),Y
  1950.      INC NEXTCODE
  1951.      BNE +
  1952.          INC NEXTCODE+1
  1953.      + LDA NEWCODE
  1954.      STA OLDCODE
  1955.      LDA NEWCODE+1
  1956.      STA OLDCODE+1
  1957. JMP NEXT
  1958. CLOSE JSR $FFCC
  1959. LDA #2
  1960. JSR $FFC3
  1961. LDA #3
  1962. JMP $FFC3
  1963.  
  1964. DECFC LDA $FC
  1965. BNE +
  1966.     DEC $FD
  1967. + DEC $FC
  1968. LDA COUNT
  1969. BNE +
  1970.     DEC COUNT+1
  1971. + DEC COUNT
  1972. RTS
  1973. INFC INC $FC
  1974. BNE +
  1975.     INC $FD
  1976. + INC COUNT
  1977. BNE +
  1978.     INC COUNT+1
  1979. + RTS
  1980.  
  1981. NEXTCODE .WOR 0
  1982. STRINGCODE .WOR 0
  1983. CHARACTER .BYT 0
  1984. NEXTBUMP .WOR 0
  1985. CURRENTBITS .BYT 0
  1986. RACK .BYT 0
  1987. BITMASK .BYT 0
  1988. MASK .WOR 0
  1989. INDEX .WOR 0
  1990. OFFSET .WOR 0
  1991. RETURN .WOR 0
  1992. COUNT .WOR 0
  1993. NEWCODE .WOR 0
  1994. OLDCODE .WOR 0
  1995. TEST .BYT 0
  1996.  
  1997. TO DRIVE THE ML I WROTE THIS SMALL BASIC PROGRAM. NOTE THAT CHANNEL TWO IS
  1998. ALWAYS THE INPUT AND CHANNEL THREE IS ALWAYS THE OUTPUT. EX AND CO MAY BE
  1999. CHANGED TO SUIT WHATEVER LOCATIONS THE PROGRAM IS REASSEMBLED AT.
  2000.  
  2001. 1 IFA=.THENA=1:LOAD"LZW.ML",PEEK(186),1
  2002. 10 EX=2500:CO=2503
  2003. 15 PRINT"[E]XPAND OR [C]OMPRESS?"
  2004. 20 GETA$:IFA$<>"C"ANDA$<>"E"THEN20
  2005. 30 INPUT"NAME OF INPUT FILE";FI$:IFLEN(FI$)=.THEN30
  2006. 40 INPUT"NAME OF OUTPUT FILE";FO$:IFLEN(FO$)=.THEN40
  2007. 50 OPEN2,9,2,FI$+",P,R":OPEN3,9,3,FO$+",P,W"
  2008. 60 IFA$="E"THENSYSEX
  2009. 70 IFA$="C"THENSYSCO
  2010. 80 END
  2011.  
  2012. For those interested in learning more about data
  2013. compression/decompression I recommend the book 'The Data Compression
  2014. Book' written by Mark Nelson. I learned a great deal from reading this
  2015. book. It explains all of the major data compression methods. (huffman
  2016. coding, dictionary type compression such as LZW, arithmatic coding,
  2017. speech compression and lossy graphics compression)
  2018.  
  2019. Questions or comments are welcome, they may be directed to me at :
  2020.  
  2021. Internet   : Blucier@ersys.edmonton.ab.ca
  2022. Genie      : b.lucier1
  2023.  
  2024. ------------------------------------------------------------------------------
  2025.  
  2026. begin 644 lzw.ml
  2027. MQ`E,N`P@GPJI@(WT#:D`C?,-(.L*C>T-J0"-[@T@ZPJ-[PT@6`NQ_L@Q_LG_
  2028. M\`ZQ_HWN#8BQ_HWM#4QH"J``N>L-D?[(P`70]N[K#=`#[NP-(/8*K>P-R1#0
  2029. M&JWK#<D`T!.I`HWM#:D!C>X-(/8*()\*3%T*K?$-S>P-T!ZM\`W-ZPW0%JD!
  2030. MC>X-J0&-[0T@]@KN\@T.\`TN\0VI`(WN#:WO#8WM#:60T`-,WPD@]@JI`8WN
  2031. M#:D`C>T-(/8*K?0-\`X@S/^B`R#)_ZWS#2#2_R#,_ZD#(,/_J0),P_^I"8WR
  2032. M#:D!C>P-J0.-ZPVI`HWQ#:D`C?`-J0"%_JDXA?^IG87\J1.%_:``J?^1_LB1
  2033. M_ABI!67^A?Z0`N;_I?S0`L;]QORE_07\T-Y@(,S_H@(@QO],S_^I`(WV#:D!
  2034. MKO(-R@HN]@W*T/F-]0VM]0T-]@W0`6"M]0TM[0V%`ZWV#2WN#04#\`FM\PT-
  2035. M]`V-\PU.]`VM]`W0&"#,_Z(#(,G_K?,-(-+_J0"-\PVI@(WT#4[V#6[U#4P+
  2036. M"ZD`C?@-K>\-"B[X#4WM#8WW#:WX#4WN#8WX#=`1K?<-T`RB`8[Y#<J.^@U,
  2037. MEPLXJ9WM]PV-^0VI$^WX#8WZ#2#C"Z``L?[(,?[)_]`#H`!@R+'^V>L-T`C(
  2038. MP`70]*``8#BM]PWM^0V-]PVM^`WM^@V-^`TI@/`1&*F=;?<-C?<-J1-M^`V-
  2039. M^`U,EPNM]PV%_JWX#87_!OXF_P;^)O\8K?<-9?Z%_JWX#67_A?\8J0!E_H7^
  2040. MJ3AE_X7_H`!@F(W]#:(D&&E4A?R&_:D`C?X-K?@-\!X@XPN@!+'^H`"1_*`"
  2041. ML?Z-]PW(L?Z-^`T@W`U,)@R@`*WW#9'\[OT-T`/N_@U@J0"-]@V-^PV-_`VI
  2042. M`:[R#<H*+O8-RM#YC?4-K?4-#?8-\#NM]`T0!B#K"HWS#:WS#2WT#?`2K?4-
  2043. M#?L-C?L-K?8-#?P-C?P-3O8-;O4-3O0-K?0-T`6I@(WT#4QT#&"I`(WS#:F`
  2044. MC?0-()\*(%D,K?P-C0(.K?L-C>\-C0$.R0#0"JW\#<D!T`-,NPT@S/^B`R#)
  2045. M_ZT!#B#2_R!9#*W[#8W_#:W\#8T`#LD!T!BM_PW)`=`&[O(-3/,,R0+PJ\D`
  2046. MT`-,NPTXK?\-[>L-A0.M``[M[`T%`Y`6K>\-C50DK0$.C?<-K0(.C?@-H`'0
  2047. M#JW_#8WW#:T`#HWX#:``(!0,H`"Q_(WO#>;\T`+F_2#,_Z(#(,G_K?X-#?T-
  2048. M\`T@R`V@`+'\(-+_3&T-K>L-C?<-K>P-C?@-(.,+H`*M`0Z1_LBM`@Z1_LBM
  2049. M[PV1_N[K#=`#[NP-K?\-C0$.K0`.C0(.3/,,(,S_J0(@P_^I`TS#_Z7\T`+&
  2050. M_<;\K?T-T`/._@W._0U@YOS0`N;][OT-T`/N_@U@````````````````````
  2051. *````````````````
  2052. `
  2053. end
  2054.  
  2055. crc32 for lzw.ml = 2460116527
  2056.  
  2057. begin 644 lzw.bas
  2058. M`0@<"`$`BT&R+J=!LC$ZDR),6E<N34PB+#DL,0`P"`H`15BR,C4P,#I#3[(R
  2059. M-3`S`%`(#P"9(I,219)84$%.1"!/4B`20Y)/35!215-3/R(`;`@4`*%!)#J+
  2060. M022SL2)#(J]!)+.Q(D4BIS(P`)<('@"%(DY!344@3T8@24Y0550@1DE,12([
  2061. M1DDD.HO#*$9))"FR+J<S,`##""@`A2).04U%($]&($]55%!55"!&24Q%(CM&
  2062. M3R0ZB\,H1D\D*;(NIS0P`.L(,@"?,BPY+#(L1DDDJB(L4RQ2(CJ?,RPY+#,L
  2063. M1D\DJB(L4"Q7(@#["#P`BT$DLB)%(J>>15@`"PE&`(M!)+(B0R*GGD-/````
  2064. `
  2065. end
  2066.  
  2067. crc32 for lzw.bas = 100674089
  2068.  
  2069. ===============================================================================
  2070. THREE-KEY ROLLOVER for the C-128 and C-64.
  2071. by Craig Bruce  <csbruce@neumann.uwaterloo.ca>
  2072.  
  2073. 1. INTRODUCTION
  2074.  
  2075. This article examines a three-key rollover mechanism for the keyboards of the
  2076. C-128 and C-64 and presents Kernal-wedge implementations for both machines.
  2077. Webster's doesn't seem to know, so I'll tell you that this means that the
  2078. machine will act sensibly if you are holding down one key and then press
  2079. another without releasing the first (or even press a third key while holding
  2080. down two others).  This is useful to fast touch typers.  In fact, fast typing
  2081. without rollover can be quite annoying; you get a lot of missing letters.
  2082.  
  2083. Another annoying property of the kernel keyscanning is joystick interference.
  2084. If you move the joystick plugged into port #1, you will notice that some junk
  2085. keystrokes result.  The keyscanners here eliminate this problem by simply
  2086. checking if the joystick is pressed and ignoring the keyboard if it is.
  2087.  
  2088. The reason that a 3-key rollover is implemented instead of the more general
  2089. N-key rollover is that scanning the keyboard becomes more and more unreliable
  2090. as more keys are held down.  Key "shaddows" begin to appear to make it look
  2091. like you are holding down a certain key when you really are not.  So, by
  2092. limiting the number of keys scanned to 3, some of this can be avoided.  You
  2093. will get strange results if you hold down more than three keys at a time, and
  2094. even sometimes when holding down 3 or less.  The "shift" keys (Shift,
  2095. Commodore, Control, Alternate, and CapsLock) don't count in the 3 keys of
  2096. rollover, but they do make the keyboard harder to read correctly.
  2097. Fortunately, three keys will allow you to type words like "AND" and "THE"
  2098. without releasing any keys.
  2099.  
  2100. 2. USER GUIDE
  2101.  
  2102. Using these utilities is really easy - you just type away like normal.  To
  2103. install the C-128 version, enter:
  2104.  
  2105. BOOT "KEYSCAN128"
  2106.  
  2107. and you're in business.  The program will display "Keyscan128 installed" and
  2108. go to work.  The program loads into memory at addresses $1500-$17BA (5376-6074
  2109. decimal), so you'll want to watch out for conflicts with other utilities.
  2110. This program also takes over the IRQ vector and the BASIC restart vector
  2111. ($A00).  The program will survive a RUN/STOP+RESTORE.  To uninstall this
  2112. program, you must reset the machine (or poke the kernel values back into the
  2113. vectors); it does not uninstall itself.
  2114.  
  2115. Loading the C-64 version is a bit trickier, so a small BASIC loader program is
  2116. provided.  LOAD and RUN the "KEYSCAN64.BOOT" program.  It will load the
  2117. "KEYSCAN64" program into memory at addresses $C500-$C77E (50432-51070 decimal)
  2118. and execute it (with a SYS 50432).  To uninstall the program, enter SYS 50435.
  2119. The program takes over the IRQ and NMI vectors and only gives them back to the
  2120. kernel upon uninstallation.  The program will survive a RUN/STOP+RESTORE.
  2121.  
  2122. Something that you may or may not know about the C-64 is that its keys can be
  2123. made to repeat by poking to address 650 decimal.  POKE650,128 will enable the
  2124. repeating of all keys.  POKE650,0 will enable only the repeating of the SPACE,
  2125. DELETE, and CURSOR keys.  POKE650,64 will disable the repeating of all keys.
  2126. An unusual side effect of changing this to either full repeat or no repeat is
  2127. that holding down SHIFT+COMMODORE (character set shift) will repeat rapidly.
  2128.  
  2129. To see the rollover in action, hold down the "J" key for a while, and then
  2130. press "K" without releasing "J".  "K" will come out as expected, as it would
  2131. with the kernal.  Now, release the "J" key.  If you are on a C-128, you will
  2132. notice that the "K" key will now stop repeating (this is actually an important
  2133. feature - it avoids problems if you don't release all of the keys you are
  2134. holding down, at once).  Now, press and hold the "J" key again without
  2135. releasing the "K".  "J" will now appear.  It wouldn't using the Kernal key
  2136. scanner.  You can also try this with 3-key combinations.  There will be some
  2137. combinations that cause problems; more on this below.
  2138.  
  2139. Also, take a spaz on the joystick plugged into port #1 and observe that no
  2140. garbage gets typed in.  This was an annoying problem with the kernel of both
  2141. the 64 and 128 and has lead many different games to picking between joystick
  2142. #1 and #2 as the primary controller.  The joystick in port #2 is not a problem
  2143. to either Keyscan-128/64 or the Kernal.
  2144.  
  2145. 3. KEYBOARD SCANNING
  2146.  
  2147. The Kernal scans the keyboard sixty times a second to see what keys you are
  2148. holding down.  Because of hardware peculiarities, there are multiple scanning
  2149. techniques that will give different results.
  2150.  
  2151. 3.1. SCANNING EXAMPLE
  2152.  
  2153. An example program is included to demonstrate different keyboard scanning
  2154. techniques possible.  To run it from a C-128 in 40-column (slow) mode, enter:
  2155.  
  2156. BOOT "KEYSHOW"
  2157.  
  2158. On a C-64, you must:
  2159.  
  2160. LOAD "KEYSHOW",8,1
  2161.  
  2162. and then:
  2163.  
  2164. SYS 4864
  2165.  
  2166. The same program works on both machines.  Four maps of the keyscanning matrix
  2167. will be displayed on the 40-column screen, as scanned by different techniques.
  2168. The leftmost one is scanned from top to bottom "quickly".  The second from the
  2169. left scans from bottom to top "quickly".  The third from the left scans the
  2170. keyboard sideways, and the rightmost matrix scans the keys from top to bottom
  2171. "slowly".
  2172.  
  2173. The mapping of keyscan matrix positions to keys is as follows:
  2174.  
  2175. ROWS: \             COLUMNS:    peek($DC01)
  2176. poke   \
  2177. $DC00   \   128      64      32      16      8       4       2       1
  2178.          +-------+-------+-------+-------+-------+-------+-------+-------+
  2179.   255-1  | DOWN  |   F5  |   F3  |   F1  |   F7  | RIGHT | RETURN| DELETE|
  2180.          +-------+-------+-------+-------+-------+-------+-------+-------+
  2181.   255-2  |LEFT-SH|   E   |   S   |   Z   |   4   |   A   |   W   |   3   |
  2182.          +-------+-------+-------+-------+-------+-------+-------+-------+
  2183.   255-4  |   X   |   T   |   F   |   C   |   6   |   D   |   R   |   5   |
  2184.          +-------+-------+-------+-------+-------+-------+-------+-------+
  2185.   255-8  |   V   |   U   |   H   |   B   |   8   |   G   |   Y   |   7   |
  2186.          +-------+-------+-------+-------+-------+-------+-------+-------+
  2187.  255-16  |   N   |   O   |   K   |   M   |   0   |   J   |   I   |   9   |
  2188.          +-------+-------+-------+-------+-------+-------+-------+-------+
  2189.  255-32  |   ,   |   @   |   :   |   .   |   -   |   L   |   P   |   +   |
  2190.          +-------+-------+-------+-------+-------+-------+-------+-------+
  2191.  255-64  |   /   |   ^   |   =   |RGHT-SH|  HOME |   ;   |   *   |   \   |
  2192.          +-------+-------+-------+-------+-------+-------+-------+-------+
  2193. 255-128  | STOP  |   Q   |COMMODR| SPACE |   2   |CONTROL|   _   |   1   |
  2194.          +-------+-------+-------+-------+-------+-------+-------+-------+
  2195.  
  2196. The following table contains the additional keys which must be scanned on the
  2197. C128 (but which are not displayed by the example scanning program).
  2198.  
  2199. ROWS: \               COLUMNS:    peek($DC01)
  2200. poke   \
  2201. $D02F   \   128      64      32      16      8       4       2       1
  2202.          +-------+-------+-------+-------+-------+-------+-------+-------+
  2203.   255-1  |   1   |   7   |   4   |   2   |  TAB  |   5   |   8   |  HELP |
  2204.          +-------+-------+-------+-------+-------+-------+-------+-------+
  2205.   255-2  |   3   |   9   |   6   | ENTER |   LF  |   -   |   +   |  ESC  |
  2206.          +-------+-------+-------+-------+-------+-------+-------+-------+
  2207.   255-4  |NO-SCRL| RIGHT |  LEFT |  DOWN |   UP  |   .   |   0   |  ALT  |
  2208.          +-------+-------+-------+-------+-------+-------+-------+-------+
  2209.  
  2210. These tables are presented on page 642 of the Commodore 128 Programmer's
  2211. Reference Guide.  The scan codes that are stored in location 212 on the C128
  2212. and location 197 on the C64 are calculated based on the above tables.  The
  2213. entry in the "1" bit position of the first line of the first table (DELETE)
  2214. has a scan code of 0, the "2" entry (RETURN) has a scan code of 1, etc., the
  2215. entry on the second scan line in the "1" position ("3") has a scan code of 8,
  2216. etc., all the way down to code 63.  The scan codes for the 128 go all the way
  2217. to 87, continuing in the second table like the first.
  2218.  
  2219. You will notice some strange effects of the different scanning techniques when
  2220. you hold down multiple keys.  More on this below.  Also try pushing joystick
  2221. #1 around.
  2222.  
  2223. 3.2. SCANNING HARDWARE
  2224.  
  2225. To scan the 128 keyboard, you must poke a value into $DC00 (CIA#1 port A) and
  2226. $D02F (VIC chip keyboard select port) to select the row to be scanned.  The
  2227. Data Direction Register for this port will be set to all outputs by the
  2228. Kernal, so you don't have to worry about it.  Each bit of $DC00 and the three
  2229. least significant bits of $D02F are used for selecting rows.  A "0" bit means
  2230. that a row IS selected, and a "1" means that a row IS NOT selected.  The poke
  2231. value to use for selecting among the various rows are given in the two tables
  2232. in the previous section.
  2233.  
  2234. Using one bit per row allows you to select multiple rows at the same time.  It
  2235. can be useful to select all rows at one time or no rows.  To read the row that
  2236. has been selected, simply peek at location $DC01 (CIA#1 port B).  Each bit
  2237. will tell you whether the corresponding key is currently being held down or
  2238. not.  Again, we have reverse logic; a "0" means that the key is being held
  2239. down, and a "1" means that the key is not held down.  The bit values
  2240. corresponding to the keys are given as the column headings in the tables in
  2241. the previous section.  Since there is no such thing as a perfect mechanical
  2242. switch, it is recommended that you "debounce" each key row read in the
  2243. following way:
  2244.  
  2245. again:
  2246.    lda $dc01
  2247.    cmp $dc01
  2248.    bne again
  2249.  
  2250. So, to scan the entire keyboard, you simply select each scan row in some
  2251. order, and read and remember the keys held down on the row.  As it turns out,
  2252. you have to be a bit careful of exactly how you "select" a row.  Also, there
  2253. is a shortcut that you can take.  In order to find out if any key is being
  2254. held down in one operation, all you have to do is select all rows at once and
  2255. see if there are any "0" bits in the read value.  If so, there is a key being
  2256. held down somewhere; if not, then there is no key being held down, so you
  2257. don't have to bother scanning the entire keyboard.  This will reduce our
  2258. keyscanning significantly, which is important, since the keyboard will be
  2259. scanned every 1/60 of a second.
  2260.  
  2261. As mentioned above, joystick #1 will interfere with the Kernal reading the
  2262. keyboard.  This is because the read value of joystick #1 is wired into CIA#1
  2263. port A, the same place that the keyboard read is wired in.  So, whenever a
  2264. switch in the joystick is pushed, the corresponding bit of the keyboard scan
  2265. register will be forced to "0", regardless of which keys are pressed and
  2266. regardless of which scan row is selected.  There's the catch.  If we were to
  2267. un-select all scan rows and still notice "0"s in the keyboard read register,
  2268. then we would know that the joystick was being pushed and would interfere with
  2269. our keyboard scanning, so we could abort keyboard scanning and handle this
  2270. case as if no keys were being held down.
  2271.  
  2272. It still would be possible but unlikely that the user could push the joystick
  2273. in the middle of us scanning the keyboard and screw up our results, so to
  2274. defend against this, we check for the joystick being pushed both before and
  2275. after scanning the keyboard.  If we find that the joystick is pushed at either
  2276. of these times, then we throw out the results and assume that no keys are held
  2277. down.  This way, the only way that a user could screw up the scanning is if
  2278. he/she/it were to press a switch after we begin scanning and release it before
  2279. we finish scanning.  Not bloody likely for a human.
  2280.  
  2281. You get the same deal for keyboard scanning on the 64, except you only need to
  2282. use $DC00 for selecting the scan rows.  Also note that you will not be able to
  2283. play with keyboard scanning from BASIC because of the interrupt reading of the
  2284. keyboard.  You must make sure that interrupts are disabled when playing with
  2285. the keyboard hardware, or interrupt scanning can come along at any time and
  2286. change all of the register settings.
  2287.  
  2288. 3.3. SCANNING SOURCE CODE
  2289.  
  2290. The four keyboard scanning techniques of the example program are presented
  2291. below.  The declarations required for all of them are:
  2292.  
  2293. pa = $dc00        ;row select
  2294. pb = $dc01        ;column read
  2295. ddra = $dc02      ;ddr for row select
  2296. ddrb = $dc03      ;ddr for column read
  2297. scanTable .buf 8  ;storage for scan
  2298. mask = $03        ;work location
  2299.  
  2300. The code is as follows, in Buddy format.  Each routine scans the keyboard and
  2301. stores the results in the "scanTable" table.
  2302.  
  2303. ------------------+------------------+------------------+------------------+
  2304.  Row forward fast | Row backward fast| Column right     | Row forward slow
  2305. ------------------+------------------+------------------+------------------+
  2306.   sei             |  sei             |  sei             |  sei
  2307.   ldx #0          |  ldx #7          |  lda #$00        |  ldx #0
  2308.   lda #$fe        |  lda #$7f        |  sta ddra        |  lda #$fe
  2309.   sta pa          |  sta pa          |  lda #$ff        |  sta mask
  2310.   nextRow = *     |  nextRow = *     |  sta ddrb        |  nextRow = *
  2311. - lda pb          |- lda pb          |  ldy #7          |  lda mask
  2312.   cmp pb          |  cmp pb          |  lda #$7f        |  sta pa
  2313.   bne -           |  bne -           |  sta mask        |- lda pb
  2314.   eor #$ff        |  eor #$ff        |  nextCol = *     |  cmp pb
  2315.   sta scanTable,x |  sta scanTable,x |  lda mask        |  bne -
  2316.   sec             |  sec             |  sta pb          |  eor #$ff
  2317.   rol pa          |  ror pa          |- lda pa          |  sta scanTable,x
  2318.   inx             |  dex             |  cmp pa          |  sec
  2319.   cpx #8          |  bpl nextRow     |  bne -           |  rol mask
  2320.   bcc nextRow     |  cli             |  ldx #$ff        |  inx
  2321.   cli             |  rts             |  stx pb          |  cpx #8
  2322.   rts             |                  |  eor #$ff        |  bcc nextRow
  2323. ------------------+------------------+  ldx #7          |  cli
  2324.                                      |- asl             |  rts
  2325. The forward "quick" scanning stores  |  rol scanTable,x +------------------+
  2326. the scan row selection mask into     |  dex             |
  2327. the row selection register and       |  bpl -           |
  2328. shifts the "0" bit one position      |  sec             |
  2329. "forward" for each row, directly,    |  ror mask        |
  2330. using a "rol $dc00" instruction.     |  dey             |
  2331. This would probably be the obvious   |  bpl nextCol     |
  2332. solution to an optimizing assembler  |  lda #$ff        |
  2333. programmer.  However, for some       |  sta ddra        |
  2334. reason not quite understood by this  |  lda #$00        |
  2335. author, there are "shadowing"        |  sta ddrb        |
  2336. problems with this approach.  If     |  cli             |
  2337. you were to hold down the two keys   |  rts             |
  2338. "H" and "K" at the same time, you    +------------------+
  2339. would notice that these two keys
  2340. are on the same column of two successive rows.  If you hold them both down,
  2341. you will see the two positions become active, but so will the same column of
  2342. all successive rows after the "H" and "K", even though these other keys are
  2343. not actually held down.  You will get an inaccurate reading if bad keys are
  2344. held down simultaneously.  You will notice the use of the term "active" above.
  2345. This is because although the hardware returns a "0" for active, the routine
  2346. converts that into a "1" for easier processing later.  I am not sure if
  2347. everyone will get this same result, but if your keyboard is wired the same as
  2348. mine, you will.
  2349.  
  2350. The backward "quick" scanning operates quite similarly to the forward
  2351. scanning, except for the direction of the scan and the direction of the
  2352. "shadow"; the shadow goes upwards.  You might think that ANDing together the
  2353. results of the forward and backward scan together would eliminate the shadow,
  2354. but this will not work since any rows between the rows containing the two keys
  2355. held down will be incorrectly read as being active.
  2356.  
  2357. The columnwise right scanning is the most complicated because the rows must be
  2358. converted into columns, to allow the scan matrix to be interpreted as before.
  2359. Also, the Data Direction Registers have to be changed.  You might think that
  2360. combinging row-wise scanning with columnwise scanning would give better
  2361. results, and it probably would, if it weren't for a bizarre hardware problem.
  2362. If you hold down two or more keys on the same scan row, say "W" and "E", some
  2363. of the keys will flicker or disappear, giving an inaccurate reading.
  2364.  
  2365. The forward "slow" scanning is the best of the bunch.  Incidentally, it is
  2366. what the Kernal uses (as near as I can figure - their code is extremely
  2367. convoluted).  This technique is the same as the forward "quick scan," except
  2368. that the row selection mask is shifted in a working storage location and poked
  2369. into the CIA register, rather than being shifted in place.  I don't know why
  2370. this makes a difference, but it does.  There is still a problem with this
  2371. technique, but this problem occurs with all techniques.  If you hold down
  2372. three keys that form three "corners" of a rectangle in the scanning matrix,
  2373. then the missing corner will be read as being held down also.  For example, if
  2374. you hold down "C", "N", and "M", then the keyboard hardware will also think
  2375. that you are holding down the "X" key.  This is why this article implements a
  2376. "three-key" rollover rather than an "N-key" rollover.  Many three-key
  2377. combinations will still be interpreted correctly.  Note, however, that shift
  2378. keys such as SHIFT or CONTROL will add one more key to the hardware scanning
  2379. (but will not be counted in the three-key rollover), making inaccurate results
  2380. more likely if you are holding down multiple other keys at the same time.
  2381.  
  2382. 4. THE C-128 KEYSCANNER
  2383.  
  2384. This section gives the source code for the C-128 implementation of the
  2385. three-key rollover.  The forward "slow" key matrix scanning technique is used,
  2386. extended to work with the extra keys of the 128.  It was a bit of a pain
  2387. wedging into the Kernal, since there is not a convenient indirect JMP into
  2388. scanning the keyboard, like there are for decoding and buffering pressed keys.
  2389. A rather lengthy IRQ "preamble" had to be copied from the ROM, up to the
  2390. point where it JSRs to the keyscanning routine.  This code in included in
  2391. the form of a ".byte" table, to spare you the details.
  2392.  
  2393. Before scanning the keyboard, we check to see if joystick #1 is pushed and if
  2394. a key is actually pressed.  If not, we abort scanning and JMP to the key
  2395. repeat handling in the ROM.  If a key is held down, we scan the keyboard and
  2396. then examine the result.  First we check for the shift keys (SHIFT, COMMODORE,
  2397. CONTROL, ALT, and CAPS LOCK), put them into location $D3 (shift flags) in bit
  2398. postitions 1, 2, 4, 8, and 16, respectively, and remove them from the scan
  2399. matrix.  The CAPS LOCK key is not on the main key matrix; it is read from the
  2400. processor I/O port.  This is good, because otherwise we could not abort
  2401. scanning if it were the only key held down.
  2402.  
  2403. Then we scan the keymatrix for the first three keys that are being held down,
  2404. or as many as are held down if less than three.  We store the scan codes of
  2405. these keys into a 3-element array.  We also retain a copy of the 3-element
  2406. array from the previous scan and we check for different keys being in the two
  2407. arrays.  If the old array contains a key that is not present in the new array,
  2408. then the use has released a key, so we set a flag to inhibit interpretation of
  2409. keys and pretend that no keys are held down.  This is to eliminate undesirable
  2410. effects of having other keys held down repeat if you release the most recently
  2411. pushed key first.  PC keyboards do this.  This inhibiting will be ignored if
  2412. new keys are discovered in the next step.
  2413.  
  2414. If there are keys in the new array that are not in the old, then the user has
  2415. just pressed a new key, so that new key goes to the head of the old array and
  2416. we stop comparing the arrays there.  The key in the first position of the old
  2417. array is poked into the Kernal "key held down" location for the Kernal to
  2418. interpret later.  If more than one new key is discovered at the same time,
  2419. then each of the new keys will be picked up on successive keyboard scans and
  2420. will be interpreted as just being pushed.  So, if you press the "A", "N", and
  2421. "D" keys all at the same time, some permutation of all three of these keys
  2422. will appear on the screen.
  2423.  
  2424. When we are done interpreting the keys, we check the joystick once more and if
  2425. it is still inactive, we present the most recently pushed down key to the
  2426. Kernal and JMP into the ROM keyboard decoding routine.
  2427.  
  2428. Unlike in previous issues, this source code is here in literal form; just
  2429. extract everything between the "-----=-----"s to nab the source for yourself.
  2430. The source is in Buddy assembler format.
  2431.  
  2432. -----=-----
  2433. ;3-Key Rollover-128 by Craig Bruce 18-Jun-93 for C= Hacking magazine
  2434.  
  2435. .org $1500
  2436. .obj "@0:keyscan128"
  2437.  
  2438. scanrows = 11
  2439. rollover = 3
  2440.  
  2441. pa = $dc00
  2442. pb = $dc01
  2443. pk = $d02f
  2444.  
  2445. jmp initialInstall
  2446.  
  2447. ;ugly IRQ patch code.
  2448.  
  2449. irq = *  ;$1503
  2450.    .byte $d8,$20,$0a,$15,$4c,$69,$fa,$38,$ad,$19,$d0,$29,$01,$f0,$07,$8d
  2451.    .byte $19,$d0,$a5,$d8,$c9,$ff,$f0,$6f,$2c,$11,$d0,$30,$04,$29,$40,$d0
  2452.    .byte $31,$38,$a5,$d8,$f0,$2c,$24,$d8,$50,$06,$ad,$34,$0a,$8d,$12,$d0
  2453.    .byte $a5,$01,$29,$fd,$09,$04,$48,$ad,$2d,$0a,$48,$ad,$11,$d0,$29,$7f
  2454.    .byte $09,$20,$a8,$ad,$16,$d0,$24,$d8,$30,$03,$29,$ef,$2c,$09,$10,$aa
  2455.    .byte $d0,$28,$a9,$ff,$8d,$12,$d0,$a5,$01,$09,$02,$29,$fb,$05,$d9,$48
  2456.    .byte $ad,$2c,$0a,$48,$ad,$11,$d0,$29,$5f,$a8,$ad,$16,$d0,$29,$ef,$aa
  2457.    .byte $b0,$08,$a2,$07,$ca,$d0,$fd,$ea,$ea,$aa,$68,$8d,$18,$d0,$68,$85
  2458.    .byte $01,$8c,$11,$d0,$8e,$16,$d0,$b0,$13,$ad,$30,$d0,$29,$01,$f0,$0c
  2459.    .byte $a5,$d8,$29,$40,$f0,$06,$ad,$11,$d0,$10,$01,$38,$58,$90,$07,$20
  2460.    .byte $aa,$15,$20,$e7,$c6,$38,$60
  2461.  
  2462. ;keyscanning entry point
  2463.  
  2464. main = *
  2465.    lda #0               ;check if any keys are held down
  2466.    sta pa
  2467.    sta pk
  2468. -  lda pb
  2469.    cmp pb
  2470.    bne -
  2471.    cmp #$ff
  2472.    beq noKeyPressed     ;if not, then don't scan keyboard, goto Kernal
  2473.  
  2474.    jsr checkJoystick    ;if so, make sure joystick not pressed
  2475.    bcc joystickPressed
  2476.    jsr keyscan          ;scan the keyboard and store results
  2477.    jsr checkJoystick    ;make sure joystick not pressed again
  2478.    bcc joystickPressed
  2479.    jsr shiftdecode      ;decode the shift keys
  2480.    jsr keydecode        ;decode the first 3 regular keys held down
  2481.    jsr keyorder         ;see which new keys pressed, old keys released, and
  2482.                         ;  determine which key to present to the Kernal
  2483.    lda $033e            ;set up for and dispatch to Kernal
  2484.    sta $cc
  2485.    lda $033f
  2486.    sta $cd
  2487.    ldx #$ff
  2488.    bit ignoreKeys
  2489.    bmi ++
  2490.    lda prevKeys+0
  2491.    cmp #$ff
  2492.    bne +
  2493.    lda $d3
  2494.    beq ++
  2495.    lda #88
  2496. +  sta $d4
  2497.    tay
  2498.    jmp ($033a)
  2499.  
  2500.    noKeyPressed = *     ;no keys pressed; select default scan row
  2501.    lda #$7f
  2502.    sta pa
  2503.    lda #$ff
  2504.    sta pk
  2505.  
  2506.    joystickPressed = *
  2507.    lda #$ff             ;record that no keys are down in old 3-key array
  2508.    ldx #rollover-1
  2509. -  sta prevKeys,x
  2510.    dex
  2511.    bpl -
  2512.    jsr scanCaps         ;scan the CAPS LOCK key
  2513.    ldx #$ff
  2514.    lda #0
  2515.    sta ignoreKeys
  2516.  
  2517. +  lda #88              ;present "no key held" to Kernal
  2518.    sta $d4
  2519.    tay
  2520.    jmp $c697
  2521.  
  2522. initialInstall = *      ;install wedge: set restore vector, print message
  2523.    jsr install
  2524.    lda #<reinstall
  2525.    ldy #>reinstall
  2526.    sta $0a00
  2527.    sty $0a01
  2528.    ldx #0
  2529. -  lda installMsg,x
  2530.    beq +
  2531.    jsr $ffd2
  2532.    inx
  2533.    bne -
  2534. +  rts
  2535.  
  2536.    installMsg = *
  2537.    .byte 13
  2538.    .asc "keyscan128 installed"
  2539.    .byte 0
  2540.  
  2541. reinstall = *           ;re-install wedge after a RUN/STOP+RESTORE
  2542.    jsr install
  2543.    jmp $4003
  2544.  
  2545. install = *             ;guts of installation: set IRQ vector to patch code
  2546.    sei                  ;  and initialize scanning variables
  2547.    lda #<irq
  2548.    ldy #>irq
  2549.    sta $0314
  2550.    sty $0315
  2551.    cli
  2552.    ldx #rollover-1
  2553.    lda #$ff
  2554. -  sta prevKeys,x
  2555.    dex
  2556.    bpl -
  2557.    lda #0
  2558.    sta ignoreKeys
  2559.    rts
  2560.  
  2561. mask = $cc
  2562.  
  2563. keyscan = *             ;scan the (extended) keyboard using the forward
  2564.    ldx #$ff             ;  row-wise "slow" technique
  2565.    ldy #$ff
  2566.    lda #$fe
  2567.    sta mask+0
  2568.    lda #$ff
  2569.    sta mask+1
  2570.    jmp +
  2571.    nextRow = *
  2572. -  lda pb
  2573.    cmp pb
  2574.    bne -
  2575.    sty pa
  2576.    sty pk
  2577.    eor #$ff
  2578.    sta scanTable,x
  2579.    sec
  2580.    rol mask+0
  2581.    rol mask+1
  2582. +  lda mask+0
  2583.    sta pa
  2584.    lda mask+1
  2585.    sta pk
  2586.    inx
  2587.    cpx #scanrows
  2588.    bcc nextRow
  2589.    rts
  2590.  
  2591. shiftValue = $d3
  2592.  
  2593. shiftRows .byte $01,$06,$07,$07,$0a
  2594. shiftBits .byte $80,$10,$20,$04,$01
  2595. shiftMask .byte $01,$01,$02,$04,$08
  2596.  
  2597. shiftdecode = *         ;see which "shift" keys are held down, put them into
  2598.    jsr scanCaps         ;  proper positions in $D3 (shift flags), and remove
  2599.    ldy #4               ;  them from the scan matrix
  2600. -  ldx shiftRows,y
  2601.    lda scanTable,x
  2602.    and shiftBits,y
  2603.    beq +
  2604.    lda shiftMask,y
  2605.    ora shiftValue
  2606.    sta shiftValue
  2607.    lda shiftBits,y
  2608.    eor #$ff
  2609.    and scanTable,x
  2610.    sta scanTable,x
  2611. +  dey
  2612.    bpl -
  2613.    rts
  2614.  
  2615. scanCaps = *            ;scan the CAPS LOCK key from the processor I/O port
  2616. -  lda $1
  2617.    cmp $1
  2618.    bne -
  2619.    eor #$ff
  2620.    and #$40
  2621.    lsr
  2622.    lsr
  2623.    sta shiftValue
  2624.    rts
  2625.  
  2626. newpos = $cc
  2627. keycode = $d4
  2628. xsave = $cd
  2629.  
  2630. keydecode = *           ;get the scan codes of the first three keys held down
  2631.    ldx #rollover-1      ;initialize: $ff means no key held
  2632.    lda #$ff
  2633. -  sta newKeys,x
  2634.    dex
  2635.    bpl -
  2636.    ldy #0
  2637.    sty newpos
  2638.    ldx #0
  2639.    stx keycode
  2640.  
  2641.    decodeNextRow = *    ;decode a row, incrementing the current scan code
  2642.    lda scanTable,x
  2643.    beq decodeContinue
  2644.                         ;at this point, we know that the row has a key held
  2645.    ldy keycode
  2646. -  lsr
  2647.    bcc ++
  2648.    pha                  ;here we know which key it is, so store its scan code,
  2649.    stx xsave            ;  up to 3 keys
  2650.    ldx newpos
  2651.    cpx #rollover
  2652.    bcs +
  2653.    tya
  2654.    sta newKeys,x
  2655.    inc newpos
  2656. +  ldx xsave
  2657.    pla
  2658. +  iny
  2659.    cmp #$00
  2660.    bne -
  2661.  
  2662.    decodeContinue = *
  2663.    clc
  2664.    lda keycode
  2665.    adc #8
  2666.    sta keycode
  2667.    inx
  2668.    cpx #scanrows
  2669.    bcc decodeNextRow
  2670.    rts
  2671.  
  2672. ;keyorder: determine what key to present to the Kernal as being logically the
  2673. ;only one pressed, based on which keys previously held have been released and
  2674. ;which new keys have just been pressed
  2675.  
  2676. keyorder = *
  2677.    ;** remove old keys no longer held from old scan code array
  2678.    ldy #0
  2679.    nextRemove = *
  2680.    lda prevKeys,y       ;get current old key
  2681.    cmp #$ff
  2682.    beq ++
  2683.    ldx #rollover-1      ;search for it in the new scan code array
  2684. -  cmp newKeys,x
  2685.    beq +
  2686.    dex
  2687.    bpl -
  2688.    tya                  ;here, old key no longer held; remove it
  2689.    tax
  2690. -  lda prevKeys+1,x
  2691.    sta prevKeys+0,x
  2692.    inx
  2693.    cpx #rollover-1
  2694.    bcc -
  2695.    lda #$ff
  2696.    sta prevKeys+rollover-1
  2697.    sta ignoreKeys
  2698. +  iny                  ;check next old key
  2699.    cpy #rollover
  2700.    bcc nextRemove
  2701.  
  2702.    ;** insert new keys at front of old scan code array 
  2703. +  ldy #0
  2704.    nextInsert = *
  2705.    lda newKeys,y        ;get current new key
  2706.    cmp #$ff
  2707.    beq ++
  2708.    ldx #rollover-1      ;check old scan code array for it
  2709. -  cmp prevKeys,x
  2710.    beq +
  2711.    dex
  2712.    bpl -
  2713.    pha                  ;it's not there, so insert new key at front, exit
  2714.    ldx #rollover-2
  2715. -  lda prevKeys+0,x
  2716.    sta prevKeys+1,x
  2717.    dex
  2718.    bpl -
  2719.    lda #0
  2720.    sta ignoreKeys
  2721.    pla
  2722.    sta prevKeys+0
  2723.    ldy #rollover        ;(trick to exit)
  2724. +  iny
  2725.    cpy #rollover
  2726.    bcc nextInsert
  2727. +  rts                  ;now, the head of the old scan code array contains
  2728.                         ;  the scan code to present to the Kernal, and other
  2729.                         ;  positions represent keys that are also held down
  2730.                         ;  that have already been processed and therefore can
  2731.                         ;  be ignored until they are released
  2732.  
  2733. checkJoystick = *       ;check if joystick is pushed: un-select all keyboard
  2734.    lda #$ff             ;  rows and see if there are any "0"s in the scan
  2735.    sta pa               ;  status register
  2736.    sta pk
  2737. -  lda pb
  2738.    cmp pb
  2739.    bne -
  2740.    cmp #$ff
  2741.    lda #$7f             ;restore to default Kernal row selected (to the one
  2742.    sta pa               ;  containing the STOP key)
  2743.    lda #$ff
  2744.    sta pk
  2745.    rts
  2746.  
  2747. ;global variables
  2748.  
  2749. scanTable  .buf scanrows        ;values of the eleven keyboard scan rows
  2750. newKeys    .buf rollover        ;codes of up to three keys held simultaneously
  2751. ignoreKeys .buf 1               ;flag: if an old key has been released and no
  2752.                                 ;  new key has been pressed, stop all key
  2753.                                 ;  repeating
  2754. prevKeys   .buf rollover+2      ;keys held on previous scan
  2755. -----=-----
  2756.  
  2757. And that's all there is to it.  :-)
  2758.  
  2759. 5. THE C-64 KEYSCANNER
  2760.  
  2761. The boot program for the C-64 keyscanner is as follows:
  2762.  
  2763. 10 d=peek(186)
  2764. 20 if a=1 then 60
  2765. 30 a=1
  2766. 40 load"keyscan64",d,1
  2767. 50 goto 10
  2768. 60 sys 49152+5*256  : rem $c500
  2769.  
  2770. It is very much like boot programs for other machine language programs that
  2771. don't load at the start of BASIC.  It will load the binary from the last
  2772. device accessed, and activate it.
  2773.  
  2774. A listing of the C-64 keyscanning code is not presented here because it is so
  2775. similar to the C-128 listing.  The only things that are different are the
  2776. Kernal patches and the keyboard scanning (because the three extra rows don't
  2777. have to be scanned).  The IRQ had to be substantially copied from the ROM,
  2778. again, to get at the call to the key scanning.  Also, rather than taking
  2779. over the BASIC reset vector (since there isn't one), the NMI vector is
  2780. taken over to insure the survival of the key scanner after a RUN/STOP+RESTORE.
  2781. A bit of its preamble also had to be copied out of ROM to get at the good
  2782. stuff.  If you want a copy of the C-64 listing, you can e-mail me.
  2783.  
  2784. 6. UUENCODED FILES
  2785.  
  2786. Here are the binary executables in uuencoded form.  The CRC32s of the four
  2787. files are as follows:
  2788.  
  2789. crc32 = 3398956287 for "keyscan128"
  2790. crc32 = 2301926894 for "keyscan64.boot"
  2791. crc32 = 1767081474 for "keyscan64"
  2792. crc32 = 1604419896 for "keyshow"
  2793.  
  2794. begin 640 keyscan128
  2795. M`!5,'A;8(`H53&GZ.*T9T"D!\`>-&="EV,G_\&\L$=`P!"E`T#$XI=CP+"38
  2796. M4`:M-`J-$M"E`2G]"01(K2T*2*T1T"E_"2"HK1;0)-@P`RGO+`D0JM`HJ?^-
  2797. M$M"E`0D"*?L%V4BM+`I(K1'0*5^HK1;0*>^JL`BB!\K0_>KJJFB-&-!HA0&,
  2798. M$=".%M"P$ZTPT"D!\`REV"E`\`:M$=`0`3A8D`<@JA4@Y\8X8*D`C0#<C2_0
  2799. MK0'<S0'<T/C)__`Z((D7D#\@<18@B1>0-R"W%B#L%B`L%ZT^`X7,K3\#A<VB
  2800. M_RRT%S`QK;47R?_0!J73\":I6(74J&PZ`ZE_C0#<J?^-+]"I_Z("G;47RA#Z
  2801. M(-T6HO^I`(VT%ZE8A=2H3)?&(%46J4^@%HT`"HP!"J(`O3D6\`8@TO_HT/5@
  2802. M#4M%65-#04XQ,C@@24Y35$%,3$5$`"!5%DP#0'BI`Z`5C10#C!4#6*("J?^=
  2803. MM1?*$/JI`(VT%V"B_Z#_J?Z%S*G_A<U,F!:M`=S-`=S0^(P`W(POT$G_G:87
  2804. M.";,)LVES(T`W*7-C2_0Z.`+D-E@`08'!PJ`$"`$`0$!`@0((-T6H`2^J!:]
  2805. MIA<YK1;P$KFR%@73A=.YK19)_SVF%YVF%X@0X&"E`<4!T/I)_RE`2DJ%TV"B
  2806. M`JG_G;$7RA#ZH`"$S*(`AM2]IA?P'*342I`22(;-ILS@`[`&F)VQ%^;,ILUH
  2807. MR,D`T.88I=1I"(74Z.`+D--@H`"YM1?)__`DH@+=L1?P&,H0^)BJO;87G;47
  2808. MZ.`"D/6I_XVW%XVT%\C``Y#5H`"YL1?)__`FH@+=M1?P&LH0^$BB`;VU%YVV
  2809. M%\H0]ZD`C;07:(VU%Z`#R,`#D--@J?^-`-R-+]"M`=S-`=S0^,G_J7^-`-RI
  2810. 9_XTOT&``````````````````````````````
  2811. `
  2812. end
  2813. begin 640 keyscan64.boot
  2814. M`0@."`H`1++"*#$X-BD`'0@4`(L@0;(Q(*<@-C``)0@>`$&R,0`Z""@`DR)+
  2815. M15E30T%.-C0B+$0L,0!#"#(`B2`Q,`!@"#P`GB`T.3$U,JHUK#(U-B`@.B"/
  2816. )("1#-3`P````````
  2817. `
  2818. end
  2819. begin 640 keyscan64
  2820. M`,5,Q,5,$,9,ZL4@ZO^ES-`IQLW0):D4A<VDTT;/KH<"L=&P$>;/A<X@).JQ
  2821. M\XV'`JZ&`J7.28`@'.JE`2D0\`J@`(3`I0$)(-`(I<#0!J4!*1^%`2!9Q4Q^
  2822. MZJD`C0#<K0'<S0'<T/C)__`Y(%C'D#D@6,8@6,>0,2"-QB"[QB#[QJF!A?6I
  2823. MZX7VHO\L>,<P+:UYQ\G_T`>MC0+P(:E`A<NH;(\"J7^-`-RI_Z("G7G'RA#Z
  2824. M(+7&HO^I`(UXQZE`A<NH3";K(.K%H@"]U<7P!B#2_^C0]6!+15E30T%.-C0@
  2825. M24Y35$%,3$5$#0!XJ0F@Q8T4`XP5`ZDGH,:-&`.,&0-8H@*I_YUYQ\H0^JD`
  2826. MC7C'8'BI,:#JC10#C!4#J4>@_HT8`XP9`UA@2(I(F$BI?XT-W:P-W3`?(`+]
  2827. MT`-L`H`@O/8@X?_0#R`5_2"C_2`8Y2#JQ6P"H$QR_J+_H/^I_H7U3';&K0'<
  2828. MS0'<T/B,`-Q)_YUMQS@F]:7UC0#<Z.`(D.-@`08'!X`0(`0!`0($(+7&H`.^
  2829. M@<:];<<YA<;P%+F)Q@V-`HV-`KF%QDG_/6W'G6W'B!#>8*D`C8T"8*("J?^=
  2830. M=<?*$/J@`(3UH@"&R[UMQ_`<I,M*D!)(AO:F]>`#L`:8G77'YO6F]FC(R0#0
  2831. MYABERVD(A<OHX`B0TV"@`+EYQ\G_\"2B`MUUQ_`8RA#XF*J]>L>=><?HX`*0
  2832. M]:G_C7O'C7C'R,`#D-6@`+EUQ\G_\":B`MUYQ_`:RA#X2*(!O7G'G7K'RA#W
  2833. MJ0"->,=HC7G'H`/(P`.0TV"I_XT`W*T!W,T!W-#XR?^I?XT`W&``````````
  2834. *````````````````
  2835. `
  2836. end
  2837. begin 640 keyshow
  2838. M`!-,"Q,``````````*F3(-+_(#83H@`@%Q0@5A.B"B`7%"!T$Z(4(!<4(/03
  2839. MHAX@%Q0@3!1,$!-XH@"I_HT`W*T!W,T!W-#X2?^=`Q,X+@#<Z.`(D.I88'BB
  2840. M!ZE_C0#<K0'<S0'<T/A)_YT#$SAN`-S*$.Q88'BI`(T"W*G_C0/<H`>I?X4#
  2841. MI0.-`=RM`-S-`-S0^*+_C@'<2?^B!PH^`Q/*$/DX9@.($-VI_XT"W*D`C0/<
  2842. M6&!XJ0"-`MRI_XT#W*`'J7^%`Z4#C0'<K0#<S0#<T/BB_XX!W$G_H@<*/@,3
  2843. MRA#Y.&8#B!#=J?^-`MRI`(T#W%A@>*(`J?Z%`Z4#C0#<K0'<S0'<T/A)_YT#
  2844. M$S@F`^C@")#F6&"@!(8$A`6B`+T#$R`V%!BE!&DHA020`N8%Z.`(D.I@A0*@
  2845. 6!T8"J0!I,)$$B!#U8````````*(`8```
  2846. `
  2847. end
  2848. ================================================================================
  2849. In the Next Issue:
  2850.  
  2851. Next Issue:
  2852.  
  2853. Tech-tech - more resolution to vertical shift
  2854.  
  2855. One time half of the demos had pictures waving horizontally on the width
  2856. of the whole screen. This effect is named tech-tech and it is done using
  2857. character graphics. How exactly and is the same possible with sprites ?
  2858.  
  2859. THE DESIGN OF ACE-128/64
  2860.  
  2861. Design of ACE-128/64 command shell environment (and kernel replacement).  This
  2862. will cover the organization, internal operation, and the kernel interface of
  2863. the still-under-development but possibly catching-on kernel replacement for
  2864. the 128 and 64.  The article will also discuss future directions and designs
  2865. for the ACE environment.  ACE has a number of definite design advantages over
  2866. other kernel replacements, and a few disadvantages as well.
  2867.  
  2868.