home *** CD-ROM | disk | FTP | other *** search
/ 8bitfiles.net/archives / archives.tar / archives / genie-commodore-file-library / Information / COM-HACKING6.TXT < prev    next >
Encoding:
Text File  |  2019-04-13  |  175.4 KB  |  4,352 lines

  1.  
  2.                    ########
  3.              ##################
  4.          ######            ######
  5.       #####
  6.     #####  ####  ####      ##       #####   ####  ####  ####  ####  ####   #####
  7.   #####    ##    ##      ####     ##   ##   ##  ###     ##    ####  ##   ##   ##
  8.  #####    ########     ##  ##    ##        #####       ##    ## ## ##   ##
  9. #####    ##    ##    ########   ##   ##   ##  ###     ##    ##  ####   ##   ##
  10. #####  ####  ####  ####  ####   #####   ####  ####  ####  ####  ####   ######
  11. #####                                                                     ##
  12.  ######            ######           Issue #6
  13.    ##################             Date ##, 1993
  14.        ########
  15.  
  16. -------------------------------------------------------------------------------
  17. Editor's Notes:
  18. by Craig Taylor
  19.  
  20.   School, 2 jobs, a play, and other work has seriously restricted the amount
  21.   of time that I am able to devote to Commodore Hacking. What I am looking at
  22.   doing now is to stop writing articles for Commodore Hacking and hoping that
  23.   I'll still have enough time to write these notes, and organize and pull all
  24.   the other articles by other contributors together. The two articles I
  25.   had hoped to include in this issue : the one about multi-tasking and
  26.   about the 1351 have again been delayed. I've decided to go ahead and
  27.   release issue 6 and hopefully will have them in the next issue.
  28.   (Remember: You get what you pay for.. *smile*)
  29.  
  30.   As always, Commodore Hacking is constantly looking for articles, notes,
  31.   short little programs, what-not on any side of the Commodore 64 and 128 -
  32.   primarily on the technical or hardware side. If you think you have something
  33.   in mind, or already written then feel free to drop me a line letting me
  34.   know.
  35.  
  36.   In regards to several queries recently about reprinting individual articles
  37.   that have appeared in Commodore Hacking. You may reprint Commodore Hacking
  38.   and redistribute in whole, freely. For use of individual articles you _must_
  39.   contanct the individual author as they still retain rights to it. Please see
  40.   the legal notice / mumbo below.
  41.  
  42.   I recently recieved some mail from wolfgang@halcyon regarding a disk
  43.   magazine that he was in the process of starting and he has written the
  44.   following preview of a disk magazine that looks to be exciting:
  45.  
  46.     "_Scenery_, a new disk-magazine focusing on the american and
  47.     european demo scenes, will soon be available for download at a
  48.     site/BBS near you! With articles on everything from coding to
  49.     instrument synthesis to art, _Scenery_ will be the definitive word
  50.     when it comes to creating a demo, or simply coding in general.
  51.     Articles are being written by some of the top names in the scene,
  52.     and promise to help everybody from the Basic Baby to the ML Mogul!
  53.     Set to be released mid-August, _Scenery_ will hopefully be a worthy
  54.     edition to the likes of Coder's World and C=Hacking.  I am making
  55.     the magazine available on various Internet sites, so look for it. We
  56.     are always on the lookout for art, music, and coding talent, and if
  57.     you'd be interested in submitting an article for publication, or
  58.     simply have a question or comment, please mail me at
  59.     'wolfgang@halcyon.com'. Thanks.. and see you on the Net!"
  60.  
  61. ================================================================================
  62.  
  63.   Please note that this issue and prior ones are available via anonymous FTP
  64.   from ccosun.caltech.edu under pub/rknop/hacking.mag and via a mailserver
  65.   which documentation can be obtained by sending mail to
  66.   "duck@pembvax1.pembroke.edu" with a subject line of "mailserver" and the
  67.   line "help" in the body of the message.
  68.  
  69. ================================================================================
  70.  
  71.   NOTICE: Permission is granted to re-distrubte this "net-magazine", in
  72.   whole, freely for non-profit use. However, please contact individual 
  73.   authors for permission to publish or re-distribute articles seperately.
  74.   A charge of no greather than 5 US dollars or equivlent may be charged for 
  75.   library service / diskette costs for this "net-magazine".
  76.  
  77. ================================================================================
  78. In This Issue:
  79.  
  80. DYCP - Horizontal Scrolling
  81.  
  82. DYCP - is a name for a horizontal scroller, where characters go smoothly
  83. up and down during their voyage from right to left. One possibility is a
  84. scroll with 8 characters - one character per sprite, but a real demo coder
  85. won't be satisfied with that.
  86.  
  87. Opening the borders
  88.  
  89. VIC has many features and transparent borders are one of them. You can not
  90. make characters appear in the border, but sprites are displayed in the
  91. border too.
  92.  
  93. 64 Documentation
  94.  
  95. This file, taken directly from a Commodore 64 emulator and development
  96. system being developed is full of information about undocumented
  97. opcodes, processor timings, memory configurations and the such. A
  98. must-have reference article.
  99.  
  100. A Heavy Duty Power supply for the C-64
  101.  
  102. This article describes how to build a heavier duty power supply for your
  103. Commodore 64 computer and includes a full schematic in GeoPaint format.
  104.  
  105. LZW Compression
  106.  
  107. LZW is perhaps the most widely used form of data compression today. It
  108. is simple to implement and achieves very decent compression at a fairly
  109. quick pace. LZW is used in PKZIP (shrink),PKARC (crunch), gifs,V.42bis
  110. and unix's compress. This article will attempt to explain how the
  111. compression works with a short example and 6502 source code in Buddy
  112. format.
  113.  
  114. THREE-KEY ROLLOVER for the C-128 and C-64.
  115.  
  116. This article examines how a three-key rollover mechanism works for the
  117. keyboards of the C=128 and C=64 and will present Kernal-wedge
  118. implementations for both machines. Webster's doesn't seem to know, so I'll
  119. tell you that this means that the machine will act sensibly if you are
  120. holding down one key and then press another without releasing the first.
  121. This will be useful to fast touch typers.
  122.  
  123. ================================================================================
  124. The Demo Corner: DYCP - Horizontal Scrolling
  125. by Pasi 'Albert' Ojala (po87553@cs.tut.fi or albert@cc.tut.fi))
  126.         Written: 16-May-91 Translation 02-Jun-92
  127.  
  128.     DYCP - too many sprites !?
  129.     --------------------------
  130.  
  131. DYCP - Different Y Character Position - is a name for a horizontal scroller,
  132. where characters go smoothly up and down during their voyage from right to
  133. left. One possibility is a scroll with 8 characters - one character in each
  134. sprite, but a real demo coder won't be satisfied with that.
  135.  
  136. Demo coders thought that it looks good to make the scrolling text change its
  137. vertical position in the same time it proceeded from the right side of the
  138. screen to the left. The only problem is that there is only eight sprites
  139. and that is not even nearly enough to satisfy the requirements needed for
  140. great look. So the only way is to use screen and somehow plot the text in
  141. graphics, because character columns can not be scrolled individually.
  142. Plotting the characters take absolutely too much time, because you have to
  143. handle each byte seperately and the graphics bitmap must be cleared too.
  144.  
  145.  
  146. _Character hack_
  147.  
  148. The whole DYCP started using character graphics. You plot six character
  149. rows where the character (screen) codes increase to the right and down.
  150. This area is then used like a small bitmap screen. Each of the text chars
  151. are displayed one byte at a time on each six rows high character columns.
  152. This 240 character positions big piece of screen can be moved horizontally
  153. using the x-scroll register (three lowest bits in $D016) and after eight
  154. pixels you move the text itself, like in any scroll. The screen is of course
  155. reduced to 38 columns wide to hide the jittering on the sides.
  156.  
  157. A good coder may also change the character sets during the display and
  158. even double the size of the scroll, but because the raster time happens
  159. to go to waste using this technique anyway, that is not very feasible. There
  160. are also other difficulties in this approach, the biggest is the time needed
  161. to clear the display.
  162.  
  163.  
  164. _Save characters - and time_
  165.  
  166. But why should we move an eight-byte-high character image in a 48-line-high
  167. area, when 16 is really enough ?  We can use two characters for the graphics
  168. bitmap and then move this in eight pixel steps up and down. The lowest
  169. three bits of the y-position then gives us the offset where the data must
  170. be plotted inside this graphical region. The two character codes are usually
  171. selected to be consecutive ones so that the image data has also 16
  172. consecutive bytes. [See picture 1.]
  173.  
  174.  
  175. _Demo program might clear things up_
  176.  
  177. The demo program is coded using the latter algorithm. The program first
  178. copies the Character ROM to ram, because it is faster to use it from there.
  179. You can easily change the program to use your own character set instead,
  180. if you like. The sinus data for the vertical movement is created of a 1/4
  181. of a cycle by mirroring it both horizontally and vertically.
  182.  
  183. Two most time critical parts are clearing the character set and plotting the
  184. new one. Neither of these may happen when VIC is drawing the area where the
  185. scroll is, so there is a slight hurry. Using double buffering technique we
  186. could overcome this limitation, but this is just an example program. For
  187. speed there is CLC only when it is absolutely needed.
  188.  
  189. The NTSC version is a bit crippled, it only covers 32 columns and thus the
  190. characters seem to appear from thin air. Anyway, the idea should become
  191. clear.
  192.  
  193.  
  194. _Want to go to the border ?_
  195.  
  196. Some coders are always trying to get all effects ever done using the C64 go
  197. to the border, and even successfully. The easiest way is to use only a region
  198. of 21 pixels high - sprites - and move the text exactly like in characters.
  199. In fact only the different addressing causes the differences in the code.
  200.  
  201. Eight horizontally expanded sprites will be just enough to fill the side
  202. borders. You can also mix these techiques, but then you have the usual
  203. "chars-in-the-screen-while-border-opened"-problems (however, they are
  204. solvable). Unfortunately sprite-dycp is even more slower than char-dycp.
  205.  
  206.  
  207. _More movement vertically_
  208.  
  209. You might think that using the sprites will restrict the sinus to only
  210. 14 pixels. Not really, the only restriction is that the vertical position
  211. difference between three consequent text character must be less than 14
  212. pixel lines. Each sprites' Y-coordinate will be the minimum of the three
  213. characters residing in that sprite. Line offsets inside the sprites
  214. are then obtained by subtracting the sprite y-coordinate from the character
  215. y-coordinate. Maybe a little hard to follow, but maybe a picture will
  216. clear the situation. [See picture 2.]
  217.  
  218. Scrolling horizontally is easy. You just have to move sprites like you would
  219. use the character horizontal scroll register and after eight pixels you
  220. reset the sprite positions and scroll the text one position in memory.
  221. And of course, you fetch a new character for the scroll. When we have
  222. different and changing sprite y-coordinates, opening the side borders become
  223. a great deal more difficult. However, in this case there is at least two
  224. different ways to do it.
  225.  
  226.  
  227. _Stretch the sprites_
  228.  
  229. The easiest way is to position all of the sprites where the scroll will
  230. be when it is in its highest position. Then stretch the first and last line
  231. of each sprite so that the 19 sprite lines in the middle will be on the
  232. desired place. Opening the borders now is trivial, because all of the sprites
  233. are present on all of the scan lines and they steal a constant amount of
  234. time. However, we lose two sprite lines. We might not want to use the first
  235. and the last line for graphics, because they are stretched.
  236. [See previous C=Hacking Issues for more information about stretching and
  237.  stolen cycles.]
  238.  
  239. A more difficult approach is to unroll the routine and let another routine
  240. count the sprites present in each line and then change the time the routine
  241. uses accordingly. In this way you save time during the display for other
  242. effects, like color bars, because stretching will take at least 12 cycles
  243. on each raster line. On the other hand, if the sinus is constant (user is
  244. not allowed to change it), it is usually possible to embedd the count
  245. routine directly to the border opening part of the routine.
  246.  
  247.  
  248. _More sprites_
  249.  
  250. You don't necassarily need to plot the characters in sprites to have more
  251. than eight characters. Using a sprite multiplexing techiques you can double
  252. or triple the number of sprites available. You can divide the scroll
  253. vertically into several areas and because the y-coordinate of the scroll
  254. is a sinus, there always is a fixed maximum number of sprites in each area.
  255. This number is always smaller than the total number of sprites in the
  256. whole scroll. I won't go into detail, but didn't want to leave this out
  257. completely. [See picture 3.]
  258.  
  259.  
  260. _Smoother and smoother_
  261.  
  262. Why be satisfied with a scroll with only 40 different slices horizontally ?
  263. It should be possible to count own coordinates for each pixel column on
  264. the scroll. In fact the program won't be much different, but the routine
  265. must also mask the unwanted bits and write the byte to memory with ORA+STA.
  266. When you think about it more, it is obvious that this takes a generous amount
  267. of time, handling every bit seperately will take much more than eight times
  268. the time a simple LDA+STA takes. Some coders have avoided this by plotting
  269. the same character to different character sets simultaneously and then
  270. changing the charsets appropriately, but the resulting scroll won't be much
  271. larger than 96x32 pixels.
  272.  
  273. --------------------------------------------------------------------------
  274. Picture 1 - Two character codes will make a graphical bitmap
  275.  
  276. Screen memory:
  277.  ____________________________________
  278. |Char   |Char   |Char   |Char   |  ...
  279. |Code   |Code   |Code   |Code   |
  280. |0      |2      |80     |80     | .
  281. |       |**  ** |       |       | .
  282. |       |**  ** |       |       | .
  283. |*****  |****** |       |       |
  284. |****** | ****  |       |       |
  285. |**__**_|__**___|_______|_______|
  286. |**  ** |  **   | ****  |Char   |
  287. |**  ** |  **   |****** |Code   |
  288. |****** |       |**  ** |6      |
  289. |*****  |       |**     |       |
  290. |Char   |Char   |**  ** |       |
  291. |Code   |Code   |****** |       |
  292. |1      |3      |4****  |*****  |
  293. |_______|_______|_______|******_|
  294. |Char   |Char   |       |**  ** |
  295. |Code   |Code   |       |****** |
  296. |80     |80     |       |*****  |
  297. |       |       |       |**     |
  298. |       |       |Char   |**ar   |
  299. |       |       |Code   |Code   |
  300. |       |       |5      |7      |
  301. |_______|_______|_______|_______|
  302.  
  303. Character set memory:
  304.  
  305.  _________________________________________________________________
  306. |Char 0 |Char 1 |Char 2 |Char 3 |Char 4 |Char 5 |Char 6 |Char 7 | ...
  307. |_______|_______|_______|_______|_______|_______|_______|_______|__
  308.       DDDDDDDD      YYYYYYYY     CCCCCCCC              PPPPPPPP
  309.  First column    Second column   Third column    Fourth column
  310.  
  311. --------------------------------------------------------------------------
  312. Picture 2 - DYCP with sprites
  313.  
  314. Sprite 0
  315.  _______________________
  316. |       |**  ** |       |
  317. |       |**  ** |       |
  318. |       |****** |       |
  319. |*****  | ****  |       |
  320. |****** |  **   |       |
  321. |**  ** |  **   |       |
  322. |**  ** |  **   |       |
  323. |**__**_|_______|_______|
  324. |****** |       | ****  |
  325. |*****  |       |****** |
  326. |       |       |**  ** |
  327. |       |       |**     |
  328. |       |       |**  ** |
  329. |       |       |****** |
  330. |       |       | ****  |
  331. |_______|_______|_______| Sprite 1
  332. |       |       |       | _______________________
  333. |       |       |       ||*****  |       |       |
  334. |       |       |       ||****** |       |       |
  335. |       |       |       ||**  ** |       |       |
  336. |_______|_______|_______||****** |       |       |
  337.                          |*****  |       |       |
  338.                          |**     |       |       |
  339.                          |**     |       |       |
  340.                          |_______|_______|_______|
  341.                          |       |       |       |
  342.                          |       |       |       |
  343.                          |       |       |       |
  344.                          |       | ****  |       |
  345.                          |       | ****  |       |
  346.                          |       |       |****** |
  347.                          |       |       |****** |
  348.                          |_______|_______|__**___|
  349.                          |       |       |  **   |
  350.                          |       |       |  **   |
  351.                          |       |       |  **   |
  352.                          |       |       |  **   |
  353.                          |_______|_______|_______|
  354.  
  355. --------------------------------------------------------------------------
  356. Picture 3 - Sprite multiplexing
  357.  
  358.                                __          Set coordinates for eight sprites
  359.                             __|3 |         that start from the top half.
  360.                            |4 |  |__
  361.                          __|  `--|2 |
  362.                         |5 `--'  |  |
  363.                         |  |     `--'__
  364.                         `--'        |1 |
  365.                                     |  |
  366.                       __            `--'
  367.                      |6 |
  368.                      |  |               __
  369.                      `--'              |0 |
  370.                                        |  |
  371. -__------------------------------------`--'When VIC has displayed the last
  372. |0 |               __                      sprite, set coordinates for the
  373. |  |              |6 |                     sprites in the lower half of the
  374. `--'              |  |                     area.
  375.                   `--'
  376.     __
  377.    |1 |         __
  378.    |  |        |5 |
  379.    `-- __      |  |
  380.       |2 |   __`--'
  381.       |  |__|4 |                           You usually have two sprites that
  382.       `--|3 |  |                           are only 'used' once so that you
  383.          |  `--'                           can change other sprites when VIC
  384.          `__'                              is displaying them.
  385. --------------------------------------------------------------------------
  386.  
  387. DYCP demo program (PAL)
  388.  
  389.  
  390. SINUS=  $CF00   ; Place for the sinus table
  391. CHRSET= $3800   ; Here begins the character set memory
  392. GFX=    $3C00   ; Here we plot the dycp data
  393. X16=    $CE00   ; values multiplicated by 16 (0,16,32..)
  394. D16=    $CE30   ; divided by 16  (16 x 0,16 x 1 ...)
  395. START=  $033C   ; Pointer to the start of the sinus
  396. COUNTER= $033D  ; Scroll counter (x-scroll register)
  397. POINTER= $033E  ; Pointer to the text char
  398. YPOS=   $0340   ; Lower 4 bits of the character y positions
  399. YPOSH=  $0368   ; y positions divided by 16
  400. CHAR=   $0390   ; Scroll text characters, multiplicated by eight
  401. ZP=     $FB     ; Zeropage area for indirect addressing
  402. ZP2=    $FD
  403. AMOUNT= 38      ; Amount of chars to plot-1
  404. PADCHAR= 32     ; Code used for clearing the screen
  405.  
  406. *= $C000
  407.  
  408.         SEI             ; Disable interrupts
  409.         LDA #$32        ; Character generator ROM to address space
  410.         STA $01
  411.         LDX #0
  412. LOOP0   LDA $D000,X     ; Copy the character set
  413.         STA CHRSET,X
  414.         LDA $D100,X
  415.         STA CHRSET+256,X
  416.         DEX
  417.         BNE LOOP0
  418.         LDA #$37        ; Normal memory configuration
  419.         STA $01
  420.         LDY #31
  421. LOOP1   LDA #66         ; Compose a full sinus from a 1/4th of a
  422.         CLC             ;   cycle
  423.         ADC SIN,X
  424.         STA SINUS,X
  425.         STA SINUS+32,Y
  426.         LDA #64
  427.         SEC
  428.         SBC SIN,X
  429.         STA SINUS+64,X
  430.         STA SINUS+96,Y
  431.         INX
  432.         DEY
  433.         BPL LOOP1
  434.         LDX #$7F
  435. LOOP2   LDA SINUS,X
  436.         LSR
  437.         CLC
  438.         ADC #32
  439.         STA SINUS+128,X
  440.         DEX
  441.         BPL LOOP2
  442.  
  443.         LDX #39
  444. LOOP3   TXA
  445.         ASL
  446.         ASL
  447.         ASL
  448.         ASL
  449.         STA X16,X       ; Multiplication table (for speed)
  450.         TXA
  451.         LSR
  452.         LSR
  453.         LSR
  454.         LSR
  455.         CLC
  456.         ADC #>GFX
  457.         STA D16,X       ; Dividing table
  458.         LDA #0
  459.         STA CHAR,X      ; Clear the scroll
  460.         DEX
  461.         BPL LOOP3
  462.         STA POINTER     ; Initialize the scroll pointer
  463.         LDX #7
  464.         STX COUNTER
  465. LOOP10  STA CHRSET,X    ; Clear the @-sign..
  466.         DEX
  467.         BPL LOOP10
  468.  
  469.         LDA #>CHRSET    ; The right page for addressing
  470.         STA ZP2+1
  471.         LDA #<IRQ       ; Our interrupt handler address
  472.         STA $0314
  473.         LDA #>IRQ
  474.         STA $0315
  475.         LDA #$7F        ; Disable timer interrupts
  476.         STA $DC0D
  477.         LDA #$81        ; Enable raster interrupts
  478.         STA $D01A
  479.         LDA #$A8        ; Raster compare to scan line $A8
  480.         STA $D012
  481.         LDA #$1B        ; 9th bit
  482.         STA $D011
  483.         LDA #30
  484.         STA $D018       ; Use the new charset
  485.         CLI             ; Enable interrupts and return
  486.         RTS
  487.  
  488. IRQ     INC START       ; Increase counter
  489.         LDY #AMOUNT
  490.         LDX START
  491. LOOP4   LDA SINUS,X     ; Count a pointer for each text char and according
  492.         AND #7          ;  to it fetch a y-position from the sinus table
  493.         STA YPOS,Y      ;   Then divide it to two bytes
  494.         LDA SINUS,X
  495.         LSR
  496.         LSR
  497.         LSR
  498.         STA YPOSH,Y
  499.         INX             ; Chars are two positions apart
  500.         INX
  501.         DEY
  502.         BPL LOOP4
  503.  
  504.         LDA #0
  505.         LDX #79
  506. LOOP11  STA GFX,X       ; Clear the dycp data
  507.         STA GFX+80,X
  508.         STA GFX+160,X
  509.         STA GFX+240,X
  510.         STA GFX+320,X
  511.         STA GFX+400,X
  512.         STA GFX+480,X
  513.         STA GFX+560,X
  514.         DEX
  515.         BPL LOOP11
  516.  
  517. MAKE    LDA COUNTER     ; Set x-scroll register
  518.         STA $D016
  519.         LDX #AMOUNT
  520.         CLC             ; Clear carry
  521. LOOP5   LDY YPOSH,X     ; Determine the position in video matrix
  522.         TXA
  523.         ADC LINESL,Y    ; Carry won't be set here
  524.         STA ZP          ; low byte
  525.         LDA #4
  526.         ADC LINESH,Y
  527.         STA ZP+1        ; high byte
  528.         LDA #PADCHAR    ; First clear above and below the char
  529.         LDY #0          ; 0. row
  530.         STA (ZP),Y
  531.         LDY #120        ; 3. row
  532.         STA (ZP),Y
  533.         TXA             ; Then put consecuent character codes to the places
  534.         ASL             ;  Carry will be cleared
  535.         ORA #$80    ; Inverted chars
  536.         LDY #40         ; 1. row
  537.         STA (ZP),Y
  538.         ADC #1          ; Increase the character code, Carry won't be set
  539.         LDY #80         ; 2. row
  540.         STA (ZP),Y
  541.  
  542.         LDA CHAR,X      ; What character to plot ? (source)
  543.         STA ZP2         ;  (char is already multiplicated by eight)
  544.         LDA X16,X       ; Destination low byte
  545.         ADC YPOS,X      ;  (16*char code + y-position's 3 lowest bits)
  546.         STA ZP
  547.         LDA D16,X       ; Destination high byte
  548.         STA ZP+1
  549.  
  550.         LDY #6          ; Transfer 7 bytes from source to destination
  551.         LDA (ZP2),Y : STA (ZP),Y
  552.         DEY             ; This is the fastest way I could think of.
  553.         LDA (ZP2),Y : STA (ZP),Y
  554.         DEY
  555.         LDA (ZP2),Y : STA (ZP),Y
  556.         DEY
  557.         LDA (ZP2),Y : STA (ZP),Y
  558.         DEY
  559.         LDA (ZP2),Y : STA (ZP),Y
  560.         DEY
  561.         LDA (ZP2),Y : STA (ZP),Y
  562.         DEY
  563.         LDA (ZP2),Y : STA (ZP),Y
  564.         DEX
  565.         BPL LOOP5    ; Get next char in scroll
  566.  
  567.         LDA #1
  568.         STA $D019       ; Acknowledge raster interrupt
  569.  
  570.         DEC COUNTER     ; Decrease the counter = move the scroll by 1 pixel
  571.         BPL OUT
  572. LOOP12  LDA CHAR+1,Y    ; Move the text one position to the left
  573.         STA CHAR,Y      ;  (Y-register is initially zero)
  574.         INY
  575.         CPY #AMOUNT
  576.         BNE LOOP12
  577.         LDA POINTER
  578.         AND #63         ; Text is 64 bytes long
  579.         TAX
  580.         LDA SCROLL,X    ; Load a new char and multiply it by eight
  581.         ASL
  582.         ASL
  583.         ASL
  584.         STA CHAR+AMOUNT ; Save it to the right side
  585.         DEC START       ; Compensation for the text scrolling
  586.         DEC START
  587.         INC POINTER     ; Increase the text pointer
  588.         LDA #7
  589.         STA COUNTER     ; Initialize X-scroll
  590.  
  591. OUT     JMP $EA7E       ; Return from interrupt
  592.  
  593. SIN     BYT 0,3,6,9,12,15,18,21,24,27,30,32,35,38,40,42,45
  594.         BYT 47,49,51,53,54,56,57,59,60,61,62,62,63,63,63
  595.                         ; 1/4 of the sinus
  596.  
  597. LINESL  BYT 0,40,80,120,160,200,240,24,64,104,144,184,224
  598.         BYT 8,48,88,128,168,208,248,32
  599.  
  600. LINESH  BYT 0,0,0,0,0,0,0,1,1,1,1,1,1,2,2,2,2,2,2,2,3
  601.  
  602. SCROLL  SCR "THIS@IS@AN@EXAMPLE@SCROLL@FOR@"
  603.         SCR "COMMODORE@MAGAZINE@BY@PASI@OJALA@@"
  604.                         ; SCR will convert text to screen codes
  605.  
  606. --------------------------------------------------------------------------
  607. Basic loader for the Dycp demo program (PAL)
  608.  
  609. 1 S=49152
  610. 2 DEFFNH(C)=C-48+7*(C>64)
  611. 3 CH=0:READA$,A:PRINTA$:IFA$="END"THENPRINT"<white><clr>":SYS49152:END
  612. 4 FORF=0TO31:Q=FNH(ASC(MID$(A$,F*2+1)))*16+FNH(ASC(MID$(A$,F*2+2)))
  613. 5 CH=CH+Q:POKES,Q:S=S+1:NEXT:IFCH=ATHEN3
  614. 6 PRINT"CHECKSUM ERROR":END
  615. 100 DATA 78A9328501A200BD00D09D0038BD00D19D0039CAD0F1A9378501A01FA942187D, 3441
  616. 101 DATA 75C19D00CF9920CFA94038FD75C19D40CF9960CFE88810E4A27FBD00CF4A1869, 4302
  617. 102 DATA 209D80CFCA10F3A2278A0A0A0A0A9D00CE8A4A4A4A4A18693C9D30CEA9009D90, 3231
  618. 103 DATA 03CA10E58D3E03A2078E3D039D0038CA10FAA93885FEA99B8D1403A9C08D1503, 3338
  619. 104 DATA A97F8D0DDCA9818D1AD0A9A88D12D0A91B8D11D0A91E8D18D05860EE3C03A026, 3864
  620. 105 DATA AE3C03BD00CF2907994003BD00CF4A4A4A996803E8E88810EAA900A24F9D003C, 3256
  621. 106 DATA 9D503C9DA03C9DF03C9D403D9D903D9DE03D9D303ECA10E5AD3D038D16D0A226, 3739
  622. 107 DATA 18BC68038A7995C185FBA90479AAC185FCA920A00091FBA07891FB8A0A0980A0, 4224
  623. 108 DATA 2891FB6901A05091FBBD900385FDBD00CE7D400385FBBD30CE85FCA006B1FD91, 4440
  624. 109 DATA FB88B1FD91FB88B1FD91FB88B1FD91FB88B1FD91FB88B1FD91FB88B1FD91FBCA, 6225
  625. 110 DATA 109FEE19D0CE3D031028B99103999003C8C026D0F5AD3E03293FAABDBFC10A0A, 3593
  626. 111 DATA 0A8DB603CE3C03CE3C03EE3E03A9078D3D034C7EEA000306090C0F1215181B1E, 2159
  627. 112 DATA 202326282A2D2F3133353638393B3C3D3E3E3F3F3F00285078A0C8F018406890, 2268
  628. 113 DATA B8E008305880A8D0F82000000000000000010101010101020202020202020314, 1379
  629. 114 DATA 08091300091300010E000518010D100C05001303120F0C0C00060F1200030F0D, 304
  630. 115 DATA 0D0F040F1205000D0107011A090E050002190010011309000F0A010C01000000, 257
  631. 200 DATA END,0
  632.  
  633. --------------------------------------------------------------------------
  634. Uuencoded C64 executable of the basic loader (PAL)
  635.  
  636. begin 644 dycp.64
  637. M`0@-"`$`4[(T.3$U,@`F"`(`EJ5(*$,ILD.K-#BJ-ZPH0[$V-"D`4@@#`$-(@
  638. MLC`ZAT$D+$$ZF4$D.HM!)+(B14Y$(J>9(@63(CJ>-#DQ-3(Z@`")"`0`@4:RE
  639. M,*0S,3I1LJ5(*,8HRBA!)"Q&K#*J,2DI*:PQ-JJE2"C&*,HH020L1JPRJC(IA
  640. M*2D`J@@%`$-(LD-(JE$ZEU,L43I3LE.J,3J".HM#2+)!IS,`P@@&`)DB0TA%.
  641. M0TM354T@15)23U(B.H``#PED`(,@-SA!.3,R.#4P,4$R,#!"1#`P1#`Y1#`P.
  642. M,SA"1#`P1#$Y1#`P,SE#040P1C%!.3,W.#4P,4$P,49!.30R,3@W1"P@,S0T#
  643. M,0!<"64`@R`W-4,Q.40P,$-&.3DR,$-&03DT,#,X1D0W-4,Q.40T,$-&.3DV&
  644. M,$-&13@X.#$P131!,C=&0D0P,$-&-$$Q.#8Y+"`T,S`R`*D)9@"#(#(P.40X3
  645. M,$-&0T$Q,$8S03(R-SA!,$$P03!!,$$Y1#`P0T4X031!-$$T031!,3@V.3-#P
  646. M.40S,$-%03DP,#E$.3`L(#,R,S$`]@EG`(,@,#-#03$P134X1#-%,#-!,C`WY
  647. M.$4S1#`S.40P,#,X0T$Q,$9!03DS.#@U1D5!.3E".$0Q-#`S03E#,#A$,34P@
  648. M,RP@,S,S.`!#"F@`@R!!.3=&.$0P1$1#03DX,3A$,4%$,$$Y03@X1#$R1#!!B
  649. M.3%".$0Q,40P03DQ13A$,3A$,#4X-C!%13-#,#-!,#(V+"`S.#8T`)`*:0"#]
  650. M($%%,T,P,T)$,#!#1C(Y,#<Y.30P,#-"1#`P0T8T031!-$$Y.38X,#-%.$4X$
  651. M.#@Q,$5!03DP,$$R-$8Y1#`P,T,L(#,R-38`W0IJ`(,@.40U,#-#.41!,#-#]
  652. M.41&,#-#.40T,#-$.40Y,#-$.41%,#-$.40S,#-%0T$Q,$4U040S1#`S.$0Q*
  653. M-D0P03(R-BP@,S<S.0`J"VL`@R`Q.$)#-C@P,SA!-SDY-4,Q.#5&0D$Y,#0W^
  654. M.4%!0S$X-49#03DR,$$P,#`Y,49"03`W.#DQ1D(X03!!,#DX,$$P+"`T,C(T:
  655. M`'<+;`"#(#(X.3%&0C8Y,#%!,#4P.3%&0D)$.3`P,S@U1D1"1#`P0T4W1#0P;
  656. M,#,X-49"0D0S,$-%.#5&0T$P,#9",49$.3$L(#0T-#``Q`MM`(,@1D(X.$(Q?
  657. M1D0Y,49".#A",49$.3%&0C@X0C%&1#DQ1D(X.$(Q1D0Y,49".#A",49$.3%&V
  658. M0C@X0C%&1#DQ1D)#02P@-C(R-0`1#&X`@R`Q,#E&144Q.40P0T4S1#`S,3`RK
  659. M.$(Y.3$P,SDY.3`P,T,X0S`R-D0P1C5!1#-%,#,R.3-&04%"1$)&0S$P03!!M
  660. M+"`S-3DS`%X,;P"#(#!!.$1"-C`S0T4S0S`S0T4S0S`S144S13`S03DP-SA$H
  661. M,T0P,S1#-T5%03`P,#,P-C`Y,$,P1C$R,34Q.#%",44L(#(Q-3D`JPQP`(,@0
  662. M,C`R,S(V,C@R03)$,D8S,3,S,S4S-C,X,SDS0C-#,T0S13-%,T8S1C-&,#`R[
  663. M.#4P-SA!,$,X1C`Q.#0P-C@Y,"P@,C(V.`#X#'$`@R!".$4P,#@S,#4X.#!!8
  664. M.$0P1C@R,#`P,#`P,#`P,#`P,#`P,#$P,3`Q,#$P,3`Q,#(P,C`R,#(P,C`R^
  665. M,#(P,S$T+"`Q,S<Y`$0-<@"#(#`X,#DQ,S`P,#DQ,S`P,#$P13`P,#4Q.#`Q7
  666. M,$0Q,#!#,#4P,#$S,#,Q,C!&,$,P0S`P,#8P1C$R,#`P,S!&,$0L(#,P-`"02
  667. M#7,`@R`P1#!&,#0P1C$R,#4P,#!$,#$P-S`Q,4$P.3!%,#4P,#`R,3DP,#$P.
  668. L,#$Q,S`Y,#`P1C!!,#$P0S`Q,#`P,#`P+"`R-3<`G`W(`(,@14Y$+#`````PK
  669. ``
  670. end
  671. size 1439
  672. --------------------------------------------------------------------------
  673. Uuencoded C64 executable of the basic loader (NTSC)
  674.  
  675. begin 644 dycp-ntsc.bas
  676. M`0@-"`$`4[(T.3$U,@`F"`(`EJ5(*$,ILD.K-#BJ-ZPH0[$V-"D`40@#`$-(?
  677. MLC`ZAT$D+$$ZF4$D.HM!)+(B14Y$(J>9(I,B.IXT.3$U,CJ``(@(!`"!1K(P/
  678. MI#,Q.E&RI4@HQBC**$$D+$:L,JHQ*2DIK#$VJJ5(*,8HRBA!)"Q&K#*J,BDI:
  679. M*0"I"`4`0TBR0TBJ43J74RQ1.E.R4ZHQ.H(ZBT-(LD&G,P#!"`8`F2)#2$5#F
  680. M2U-532!%4E)/4B(Z@`#'"%H`@``4"60`@R`W.$$Y,S(X-3`Q03(P,$)$,#!$L
  681. M,#E$,#`S.$)$,#!$,3E$,#`S.4-!1#!&,4$Y,S<X-3`Q03`Q1D$Y-#(Q.#=$I
  682. M+"`S-#0Q`&$)90"#(#<U0S$Y1#`P0T8Y.3(P0T9!.30P,SA&1#<U0S$Y1#0P!
  683. M0T8Y.38P0T9%.#@X,3!%-$$R-T9"1#`P0T8T03$X-CDL(#0S,#(`K@EF`(,@R
  684. M,C`Y1#@P0T9#03$P1C-!,C(W.$$P03!!,$$P03E$,#!#13A!-$$T031!-$$QJ
  685. M.#8Y,T,Y1#,P0T5!.3`P.40Y,"P@,S(S,0#["6<`@R`P,T-!,3!%-3A$,T4P.
  686. M,T$R,#<X13-$,#,Y1#`P,SA#03$P1D%!.3,X.#5&14$Y.4(X1#$T,#-!.4,P;
  687. M.$0Q-3`S+"`S,S,X`$@*:`"#($$Y-T8X1#!$1$-!.3@Q.$0Q040P03E",#A$:
  688. M,3)$,$$Y,4(X1#$Q1#!!.3%%.$0Q.$0P-3@V,$5%,T,P,T$P,4,L(#,X-C(`9
  689. ME0II`(,@044S0S`S0D0P,$-&,CDP-SDY-#`P,T)$,#!#1C1!-$$T03DY-C@PB
  690. M,T4X13@X.#$P14%!.3`P03(T1CE$,#`S0RP@,S(U-@#B"FH`@R`Y1#4P,T,Y$
  691. M1$$P,T,Y1$8P,T,Y1#0P,T0Y1#DP,T0Y1$4P,T0Y1#,P,T5#03$P135!1#-$E
  692. M,#,X1#$V1#!!,C%#+"`S-S(Y`"\+:P"#(#$X0D,V.#`S.$$W.3DU0S$X-49")
  693. M03DP-#<Y04%#,3@U1D-!.3(P03`P,#DQ1D)!,#<X.3%&0CA!,$$P.3@P03`L#
  694. M(#0R,C0`?`ML`(,@,C@Y,49"-CDP,4$P-3`Y,49"0D0Y,#`S.#5&1$)$,#!#H
  695. M13=$-#`P,S@U1D)"1#,P0T4X-49#03`P-D(Q1D0Y,2P@-#0T,`#)"VT`@R!&C
  696. M0C@X0C%&1#DQ1D(X.$(Q1D0Y,49".#A",49$.3%&0C@X0C%&1#DQ1D(X.$(QA
  697. M1D0Y,49".#A",49$.3%&0D-!+"`V,C(U`!8,;@"#(#$P.49%13$Y1#!#13-$T
  698. M,#,Q,#(X0CDY,3`S.3DY,#`S0SA#,#(V1#!&-4%$,T4P,S(Y,T9!04)$0D9#0
  699. M,3!!,$$L(#,U.3,`8PQO`(,@,$$X1$(V,#-#13-#,#-#13-#,#-%13-%,#-!D
  700. M.3`W.$0S1#`S-$,W145!,#`P,S`V,#DP0S!&,3(Q-3$X,4(Q12P@,C$U.0"P2
  701. M#'``@R`R,#(S,C8R.#)!,D0R1C,Q,S,S-3,V,S@S.3-",T,S1#-%,T4S1C-&/
  702. M,T8P,#(X-3`W.$$P0SA&,#$X-#`V.#DP+"`R,C8X`/T,<0"#($(X13`P.#,P2
  703. M-3@X,$$X1#!&.#(P,#`P,#`P,#`P,#`P,#`P,3`Q,#$P,3`Q,#$P,C`R,#(P>
  704. M,C`R,#(P,C`S,30L(#$S-SD`20UR`(,@,#@P.3$S,#`P.3$S,#`P,3!%,#`P3
  705. M-3$X,#$P1#$P,$,P-3`P,3,P,S$R,$8P0S!#,#`P-C!&,3(P,#`S,$8P1"P@J
  706. M,S`T`)4-<P"#(#!$,$8P-#!&,3(P-3`P,$0P,3`W,#$Q03`Y,$4P-3`P,#(Q`
  707. M.3`P,3`P,3$S,#DP,#!&,$$P,3!#,#$P,#`P,#`L(#(U-P"A#7@`@R!%3D0LZ
  708. $,````#`P0
  709. ``
  710. end
  711. size 1444
  712.  
  713. ================================================================================
  714. Opening the borders
  715. by Pasi 'Albert' Ojala (po87553@cs.tut.fi or albert@cc.tut.fi)
  716.         Written: 20-Jul-92
  717.  
  718.        All timings are in PAL, principles will apply to NTSC too.
  719.               Refer to VIC memory map in Hacking Issue 4.
  720.  
  721. VIC has many features and transparent borders are one of them. You can not
  722. make characters appear in the border, but sprites are displayed in the
  723. border too. "How to do this then?" is the big question.
  724.  
  725. The screen resolution in C64 has been and will be 320 x 200 pixels. Most
  726. games need to use the whole screen to be efficient or just plain playable.
  727. But there still is that useless border area, and you can put score and
  728. other status information there instead of having them interfere with the
  729. full-screen smooth-scrolling playing area.
  730.  
  731.  
  732. _How to disable the vertical borders_
  733.  
  734. When VIC (Video Interface Controller) has displayed all character rows,
  735. it will start displaying the vertical border area. It will start displaying
  736. the characters again in top of the screen. The row select register sets the
  737. number of character lines on the screen. If we select the 24-row display
  738. when VIC is drawing the last (25th) row, it does not start to draw the
  739. border at all !  VIC will think that it already started to draw the border.
  740.  
  741. The 25-row display must be selected again in the top of the screen, so that
  742. the border may be opened in the next frame too. The number of displayed rows
  743. can be selected with the bit 3 in $d011. If the bit is set, VIC will display
  744. 25 rows and 24 rows otherwise. We have to clear the bit somewhere during the
  745. last row (raster lines $f2-$fa) and set it again in top of the screen or at
  746. least somewhere before the last row (line $f2). This has to be done in every
  747. frame (50 times per second in PAL).
  748.  
  749.  
  750. _How to open the sideborders_
  751.  
  752. The same trick can be applied to sideborders. When VIC is about to start
  753. displaying the sideborder, just select 38-column mode and restore 40-column
  754. mode so that you can do the trick again in the next scan line. If you need to
  755. open the sideborders in the bottom or top border area, you have to open the
  756. vertical borders also, but there shouldn't be any difficulty in doing that.
  757.  
  758. There is two drawbacks in this. The timing must be precise, one clock cycle
  759. off and the sideborder will not open (the sprites will generally take care of
  760. the timing) and you have to do the opening on each and every line. With
  761. top/bottom borders once in a frame was enough.
  762.  
  763. Another problem is bad-lines. There is not enough time to open the borders
  764. during a bad line and still have all of the sprites enabled. One solution
  765. is to open the borders only on seven lines and leave the bad lines unopened.
  766. Another way is to use less than eight sprites. You can have six of them
  767. on a bad line and still be able to open the sideborders (PAL). The old and
  768. still good solution is to scroll the bad lines, so that VIC will not start
  769. to draw the screen at all until it is allowed to do so.
  770. [Read more about bad lines from previous C=Hacking Issues]
  771.  
  772.  
  773. _Scrolling the screen_
  774.  
  775. VIC begins to draw the screen from the first bad line. VIC will know what
  776. line is a bad line by comparing its scan line counter to the vertical
  777. scroll register : when they match, the next line is a bad line. If we change
  778. the vertical scroll register ($d011), the first bad line will move also.
  779. If we do this on every line, the line counter in VIC will never match with
  780. it and the drawing never starts (until it is allowed to do so).
  781.  
  782. When we don't have to worry about bad lines, we have enough time to open the
  783. borders and do some other effects too. It is not necassary to change the
  784. vertical scroll on every line to get rid of the bad lines, just make sure
  785. that it never matches the line counter (or actually the least significant
  786. 8 bits).
  787.  
  788. You can even scroll the bad lines independently and you have FLD - Flexible
  789. Line Distance. You just allow a bad line when it is time to display the next
  790. character row. With this you can bounce the lines or scroll a hires picture
  791. very fast down the screen. But this has not so much to do with borders, so
  792. I will leave it to another article. (Just send requests and I might start
  793. writing about FLD ..)
  794.  
  795.  
  796. _Garbage appearing_
  797.  
  798. When we open the top and bottom borders, some graphics may appear. Even
  799. though VIC has already completed the graphics data fetches for the screen
  800. area, it will still fetch data for every character position in top and bottom
  801. borders. This will not do any harm though, because it does not generate any
  802. bad lines and happens during video fetch cycles [see Missing Cycles article].
  803. VIC reads the data from the last address in the current video bank, which is
  804. normally $3fff and displays this over and over again.
  805.  
  806. If we change the data in this address in the border area, the change will be
  807. visible right away. And if you synchronize the routine to the beam position,
  808. you can have a different value on each line. If there is nothing else to do
  809. in the border, you can get seven different values on each scan line.
  810.  
  811. The bad thing about this graphics is that it is impossible to change its
  812. color - it is always black. It is of course possible to use inverted graphics
  813. and change the background color. And if you have different data on each line,
  814. you can as easily have different color(s) on each line too.
  815.  
  816. If you don't use $3fff for any effects, it is a good idea to set it to zero,
  817. but remember to check that you do not store anything important in that
  818. address. In one demo I just cleared $3fff and it was right in the middle of
  819. another packed demopart. It took some time to find out what was wrong with
  820. the other part.
  821.  
  822.  
  823. _Horizontal scrolling_
  824.  
  825. This new graphics data also obeys the horizontal scroll register ($D016), so
  826. you can do limited tech-tech effects in the border too. You can also use
  827. sprites and open the sideborders. You can see an example of the tech-tech
  828. effect in the first example program. Multicolor mode select has no effect
  829. on this data. You can read more about tech-tech effects in a future article.
  830.  
  831.  
  832. _Example routine_
  833.  
  834. The example program will show how to open the top and bottom borders and how
  835. to use the $3fff-graphics. It is fairly well commented, so just check it for
  836. details. The program uses a sprite to do the synchronization [see Missing
  837. Cycles article] and reads a part of the character ROM to the display data
  838. buffer. To be honest, I might add that this is almost the same routine than
  839. the one in the Missing Cycles article. I have included both PAL and NTSC
  840. versions of the executables.
  841.  
  842. --------------------------------------------------------------------------
  843. The example program - $3fff-graphics
  844.  
  845. IMAGE0= $CE00   ; First graphics piece to show
  846. IMAGE1= $CF00   ; Second piece
  847. TECH=   $CD00   ; x-shift
  848. RASTER= $FA     ; Rasterline for the interrupt
  849. DUMMY=  $CFFF   ; Dummy-address for timing (refer to missing_cycles-article)
  850.  
  851. *= $C000
  852.         SEI             ; Disable interrupts
  853.         LDA #$7F        ; Disable timer interrupts (CIA)
  854.         STA $DC0D
  855.         LDA #$01        ; Enable raster interrupts (VIC)
  856.         STA $D01A
  857.         STA $D015       ; Enable the timing sprite
  858.         LDA #<IRQ
  859.         STA $0314       ; Interrupt vector to our routine
  860.         LDA #>IRQ
  861.         STA $0315
  862.         LDA #RASTER     ; Set the raster compare (9th bit will be set
  863.         STA $D012       ;  inside the raster routine)
  864.         LDA #RASTER-20  ; Sprite is situated 20 lines before the interrupt
  865.         STA $D001
  866.  
  867.         LDX #111
  868.         LDY #0
  869.         STY $D017       ; Disable y-expand
  870.         LDA #$32
  871.         STA $01         ; Select Character ROM
  872. LOOP0   LDA $D000,X
  873.         STA IMAGE0,Y    ; Copy a part of the charset to be the graphics
  874.         STA IMAGE0+112,Y
  875.         LDA $D800,X
  876.         STA IMAGE1,Y
  877.         STA IMAGE1+112,Y
  878.         INY             ; Until we copied enough
  879.         DEX
  880.         BPL LOOP0
  881.         LDA #$37        ; Char ROM out of the address space
  882.         STA $01
  883.  
  884.         LDY #15
  885. LOOP1   LDA XPOS,Y      ; Take a half of a sinus and mirror it to make
  886.         STA TECH,Y      ;  a whole cycle and then copy it as many times
  887.         STA TECH+32,Y   ;   as necassary
  888.         LDA #24
  889.         SEC
  890.         SBC XPOS,Y
  891.         STA TECH+16,Y
  892.         STA TECH+48,Y
  893.         DEY
  894.         BPL LOOP1
  895.         LDY #64
  896. LOOP2   LDA TECH,Y
  897.         STA TECH+64,Y
  898.         STA TECH+128,Y
  899.         DEY
  900.         BPL LOOP2
  901.         CLI             ; Enable interrupts
  902.         RTS             ; Return to basic (?)
  903.  
  904.  
  905. IRQ     LDA #$13        ; Open the bottom border (top border will open too)
  906.         STA $D011
  907.         NOP
  908.         LDY #111    ; Reduce for NTSC ?
  909.         INC DUMMY       ; Do the timing with a sprite
  910.         BIT $EA         ; Wait a bit (add a NOP for NTSC)
  911.  
  912. LOOP3   LDA TECH,Y      ; Do the x-shift
  913.         STA $D016
  914. FIRST   LDX IMAGE0,Y    ; Load the graphics to registers
  915. SECOND  LDA IMAGE1,Y
  916.         STA $3FFF       ; Alternate the graphics
  917.         STX $3FFF
  918.         STA $3FFF
  919.         STX $3FFF
  920.         STA $3FFF
  921.         STX $3FFF
  922.         STA $3FFF
  923.         STX $3FFF
  924.         STA $3FFF
  925.         STX $3FFF
  926.         LDA #0          ; Throw away 2 cycles (add a NOP for NTSC)
  927.         DEY
  928.         BPL LOOP3
  929.  
  930.         STA $3FFF       ; Clear the graphics
  931.         LDA #8
  932.         STA $D016       ; x-scroll to normal
  933.         LDA #$1B
  934.         STA $D011       ; Normal screen (be ready to open the border again)
  935.         LDA #111
  936.         DEC FIRST+1     ; Move the graphics by changing the low byte of the
  937.         BPL OVER        ;  load instruction
  938.         STA FIRST+1
  939. OVER    SEC
  940.         SBC FIRST+1
  941.         STA SECOND+1    ; Another graphics goes to opposite direction
  942.         LDA LOOP3+1     ; Move the x-shift also
  943.         SEC
  944.         SBC #2
  945.         AND #31         ; Sinus cycle is 32 bytes
  946.         STA LOOP3+1
  947.  
  948.         LDA #1
  949.         STA $D019       ; Acknowledge the raster interrupt
  950.         JMP $EA31       ; jump to the normal irq-handler
  951.  
  952. XPOS    BYT $C,$C,$D,$E,$E,$F,$F,$F,$F,$F,$F,$F,$E,$E,$D,$C
  953.         BYT $C,$B,$A,$9,$9,$8,$8,$8,$8,$8,$8,$8,$9,$9,$A,$B
  954.                         ; half of the sinus
  955.  
  956. --------------------------------------------------------------------------
  957. Basic loader for the $3fff-program (PAL)
  958.  
  959. 1 S=49152
  960. 2 DEFFNH(C)=C-48+7*(C>64)
  961. 3 CH=0:READA$,A:PRINTA$:IFA$="END"THENPRINT"<clear>":SYS49152:END
  962. 4 FORF=0TO31:Q=FNH(ASC(MID$(A$,F*2+1)))*16+FNH(ASC(MID$(A$,F*2+2)))
  963. 5 CH=CH+Q:POKES,Q:S=S+1:NEXT:IFCH=ATHEN3
  964. 6 PRINT"CHECKSUM ERROR":END
  965. 100 DATA 78A97F8D0DDCA9018D1AD08D15D0A9718D1403A9C08D1503A9FA8D12D0A9E68D,4003
  966. 101 DATA 01D0A26FA0008C17D0A9328501BD00D09900CE9970CEBD00D89900CF9970CFC8,4030
  967. 102 DATA CA10EAA9378501A00FB9DCC09900CD9920CDA91838F9DCC09910CD9930CD8810,4172
  968. 103 DATA E8A040B900CD9940CD9980CD8810F45860A9138D11D0EAA06FEEFFCF24EAB906,4554
  969. 104 DATA CD8D16D0BE53CEB91CCF8DFF3F8EFF3F8DFF3F8EFF3F8DFF3F8EFF3F8DFF3F8E,4833
  970. 105 DATA FF3F8DFF3F8EFF3FA9008810D18DFF3FA9088D16D0A91B8D11D0A96FCE85C010,4163
  971. 106 DATA 038D85C038ED85C08D88C0AD7FC018E901291F8D7FC0EE19D04C31EA0C0C0D0E,3719
  972. 107 DATA 0E0F0F0F0F0F0F0F0E0E0D0C0C0B0A09090808080808080809090A0B00000000,318
  973. 200 DATA END,0
  974.  
  975. --------------------------------------------------------------------------
  976. An uuencoded C64 executable $3fff-program (PAL)
  977.  
  978. begin 644 xFFF.64
  979. M`0@-"`$`4[(T.3$U,@`F"`(`EJ5(*$,ILD.K-#BJ-ZPH0[$V-"D`40@#`$-(?
  980. MLC`ZAT$D+$$ZF4$D.HM!)+(B14Y$(J>9(I,B.IXT.3$U,CJ``(@(!`"!1K(P/
  981. MI#,Q.E&RI4@HQBC**$$D+$:L,JHQ*2DIK#$VJJ5(*,8HRBA!)"Q&K#*J,BDI:
  982. M*0"I"`4`0TBR0TBJ43J74RQ1.E.R4ZHQ.H(ZBT-(LD&G,P#!"`8`F2)#2$5#F
  983. M2U-532!%4E)/4B(Z@``."60`@R`W.$$Y-T8X1#!$1$-!.3`Q.$0Q040P.$0QK
  984. M-40P03DW,3A$,30P,T$Y0S`X1#$U,#-!.49!.$0Q,D0P03E%-CA$+"`T,#`S9
  985. M`%L)90"#(#`Q1#!!,C9&03`P,#A#,3=$,$$Y,S(X-3`Q0D0P,$0P.3DP,$-%Y
  986. M.3DW,$-%0D0P,$0X.3DP,$-&.3DW,$-&0S@L(#0P,S``J`EF`(,@0T$Q,$5!S
  987. M03DS-S@U,#%!,#!&0CE$0T,P.3DP,$-$.3DR,$-$03DQ.#,X1CE$0T,P.3DQL
  988. M,$-$.3DS,$-$.#@Q,"P@-#$W,@#U"6<`@R!%.$$P-#!".3`P0T0Y.30P0T0Y0
  989. M.3@P0T0X.#$P1C0U.#8P03DQ,SA$,3%$,$5!03`V1D5%1D9#1C(T14%".3`V5
  990. M+"`T-34T`$(*:`"#($-$.$0Q-D0P0D4U,T-%0CDQ0T-&.$1&1C-&.$5&1C-&%
  991. M.$1&1C-&.$5&1C-&.$1&1C-&.$5&1C-&.$1&1C-&.$4L(#0X,S,`CPII`(,@'
  992. M1D8S1CA$1D8S1CA%1D8S1D$Y,#`X.#$P1#$X1$9&,T9!.3`X.$0Q-D0P03DQ-
  993. M0CA$,3%$,$$Y-D9#13@U0S`Q,"P@-#$V,P#<"FH`@R`P,SA$.#5#,#,X140X+
  994. M-4,P.$0X.$,P040W1D,P,3A%.3`Q,CDQ1CA$-T9#,$5%,3E$,#1#,S%%03!#.
  995. M,$,P1#!%+"`S-S$Y`"@+:P"#(#!%,$8P1C!&,$8P1C!&,$8P13!%,$0P0S!#P
  996. M,$(P03`Y,#DP.#`X,#@P.#`X,#@P.#`Y,#DP03!",#`P,#`P,#`L(#,Q.``T>
  997. -"\@`@R!%3D0L,````#@P1
  998. ``
  999. end
  1000. size 823
  1001. --------------------------------------------------------------------------
  1002. An uuencoded C64 executable $3fff-program (NTSC)
  1003.  
  1004. begin 644 xfff-ntsc.64
  1005. M`0@-"`$`4[(T.3$U,@`F"`(`EJ5(*$,ILD.K-#BJ-ZPH0[$V-"D`40@#`$-(?
  1006. MLC`ZAT$D+$$ZF4$D.HM!)+(B14Y$(J>9(I,B.IXT.3$U,CJ``(@(!`"!1K(P/
  1007. MI#,Q.E&RI4@HQBC**$$D+$:L,JHQ*2DIK#$VJJ5(*,8HRBA!)"Q&K#*J,BDI:
  1008. M*0"I"`4`0TBR0TBJ43J74RQ1.E.R4ZHQ.H(ZBT-(LD&G,P#!"`8`F2)#2$5#F
  1009. M2U-532!%4E)/4B(Z@`#'"%H`@``4"60`@R`W.$$Y-T8X1#!$1$-!.3`Q.$0QX
  1010. M040P.$0Q-40P03DW,3A$,30P,T$Y0S`X1#$U,#-!.49!.$0Q,D0P03E%-CA$H
  1011. M+"`T,#`S`&$)90"#(#`Q1#!!,C9&03`P,#A#,3=$,$$Y,S(X-3`Q0D0P,$0PX
  1012. M.3DP,$-%.3DW,$-%0D0P,$0X.3DP,$-&.3DW,$-&0S@L(#0P,S``K@EF`(,@H
  1013. M0T$Q,$5!03DS-S@U,#%!,#!&0CE$14,P.3DP,$-$.3DR,$-$03DQ.#,X1CE$`
  1014. M14,P.3DQ,$-$.3DS,$-$.#@Q,"P@-#$W-@#["6<`@R!%.$$P-#!".3`P0T0Y8
  1015. M.30P0T0Y.3@P0T0X.#$P1C0U.#8P03DQ,SA$,3%$,$5!03`V1D5%1D9#1C(T+
  1016. M14%%04(Y+"`T-S@R`$@*:`"#(#`P0T0X1#$V1#!"13`P0T5".3`P0T8X1$9&>
  1017. M,T8X149&,T8X1$9&,T8X149&,T8X1$9&,T8X149&,T8X1$9&,T8L(#0U.#``?
  1018. ME0II`(,@.$5&1C-&.$1&1C-&.$5&1C-&14%!.3`P.#@Q,$0P.$1&1C-&03DP`
  1019. M.#A$,39$,$$Y,4(X1#$Q1#!!.39&0T4X-BP@-#,S,0#B"FH`@R!#,#$P,#,XY
  1020. M1#@V0S`S.$5$.#9#,#A$.#E#,$%$.#!#,#$X13DP,3(Y,48X1#@P0S!%13$YO
  1021. M1#`T0S,Q14$P0S!#+"`S.3`U`"X+:P"#(#!$,$4P13!&,$8P1C!&,$8P1C!&W
  1022. M,$4P13!$,$,P0S!",$$P.3`Y,#@P.#`X,#@P.#`X,#@P.3`Y,$$P0D$R,#`L%
  1023. 4(#4P-P`["VP`@R!%3D0L+3$````PB
  1024. ``
  1025. end
  1026. size 830
  1027.  
  1028. ==============================================================================
  1029. 64 Documentation
  1030. by Jarkko Sonninen, Jouko Valta, John West, and Marko M"akel"a
  1031.    (sonninen@lut.fi, jopi@stekt.oulu.fi, john@ucc.gu.uwa.edu.au,
  1032.     msmakela@hylk.helsinki.fi)
  1033.  
  1034. [Ed's Note: I'm leaving this file as is because of its intention to
  1035. serve as a reference guide, and not necessarily to be presented in
  1036. article format. The detail and clarity with which the authors have
  1037. presented the material is wonderful!!]
  1038.  
  1039. #
  1040. # $Id: 64doc,v 1.3 93/06/21 13:37:18 jopi Exp $
  1041. #
  1042. # This file is part of Commodore 64 emulator
  1043. #      and Program Development System.
  1044. #
  1045. # See README for copyright notice
  1046. #
  1047. # This file contains documentation for 6502/6510/8502 instruction set.
  1048. #
  1049. # Written by 
  1050. #   Jarkko Sonninen (sonninen@lut.fi)
  1051. #   Jouko Valta     (jopi@stekt.oulu.fi)
  1052. #   John West       (john@ucc.gu.uwa.edu.au)
  1053. #   Marko M"akel"a  (msmakela@hylk.helsinki.fi)
  1054. #
  1055. # $Log: 64doc,v $
  1056. # Revision 1.3  93/06/21  13:37:18  jopi
  1057. #  X64 version 0.2 PL 0
  1058. # Revision 1.2  93/06/21  13:07:15  jopi
  1059. # *** empty log message ***
  1060. #
  1061. #
  1062.  
  1063.                 6510 Instructions by Addressing Modes
  1064.  
  1065.         ++++++++ Positive ++++++++++    -------- Negative ----------
  1066.         00      20      40      60      80      a0      c0      e0      mode
  1067.  
  1068. +00     BRK     JSR     RTI     RTS     NOP*    LDY     CPY     CPX  Impl/immed
  1069. +01     ORA     AND     EOR     ADC     STA     LDA     CMP     SBC  (indir,x)
  1070. +02      t       t       t       t      NOP*t   LDX     NOP*t   NOP*t  ? /immed
  1071. +03     SLO*    RLA*    SRE*    RRA*    SAX*    LAX*    DCP*    ISB* (indir,x)
  1072. +04     NOP*    BIT     NOP*    NOP*    STY     LDY     CPY     CPX  Zeropage
  1073. +05     ORA     AND     EOR     ADC     STA     LDA     CMP     SBC     -"-
  1074. +06     ASL     ROL     LSR     ROR     STX     LDX     DEC     INC     -"-
  1075. +07     SLO*    RLA*    SRE*    RRA*    SAX*    LAX*    DCP*    ISB*    -"-
  1076.  
  1077. +08     PHP     PLP     PHA     PLA     DEY     TAY     INY     INX  Implied
  1078. +09     ORA     AND     EOR     ADC     NOP*    LDA     CMP     SBC  Immediate
  1079. +0a     ASL     ROL     LSR     ROR     TXA     TAX     DEX     NOP  Accu/impl
  1080. +0b     ANC**   ANC**   ASR**   AR
  1081.  Cancel 
  1082.  
  1083. /duck/mailserv/hacking> 
  1084.  Interrupt 
  1085.  
  1086. /duck/mailserv/hacking> 
  1087.  
  1088.                 6510 Instructions by Addressing Modes
  1089.  
  1090.         ++++++++ Positive ++++++++++    -------- Negative ----------
  1091.         00      20      40      60      80      a0      c0      e0      mode
  1092.  
  1093. +00     BRK     JSR     RTI     RTS     NOP*    LDY     CPY     CPX  Impl/immed
  1094. +01     ORA     AND     EOR     ADC     STA     LDA     CMP     SBC  (indir,x)
  1095. +02      t       t       t       t      NOP*t   LDX     NOP*t   NOP*t  ? /immed
  1096. +03     SLO*    RLA*    SRE*    RRA*    SAX*    LAX*    DCP*    ISB* (indir,x)
  1097. +04     NOP*    BIT     NOP*    NOP*    STY     LDY     CPY     CPX  Zeropage
  1098. +05     ORA     AND     EOR     ADC     STA     LDA     CMP     SBC     -"-
  1099. +06     ASL     ROL     LSR     ROR     STX     LDX     DEC     INC     -"-
  1100. +07     SLO*    RLA*    SRE*    RRA*    SAX*    LAX*    DCP*    ISB*    -"-
  1101.  
  1102. +08     PHP     PLP     PHA     PLA     DEY     TAY     INY     INX  Implied
  1103. +09     ORA     AND     EOR     ADC     NOP*    LDA     CMP     SBC  Immediate
  1104. +0a     ASL     ROL     LSR     ROR     TXA     TAX     DEX     NOP  Accu/impl
  1105. +0b     ANC**   ANC**   ASR**   ARR**   ANE**   LXA**   SBX**   SBC* Immediate
  1106. +0c     NOP*    BIT     JMP     JMP     STY     LDY     CPY     CPX  Absolute
  1107. +0d     ORA     AND     EOR     ADC     STA     LDA     CMP     SBC     -"-
  1108. +0e     ASL     ROL     LSR     ROR     STX     LDX     DEC     INC     -"-
  1109. +0f     SLO*    RLA*    SRE*    RRA*    SAX*    LAX*    DCP*    ISB*    -"-
  1110.  
  1111. +10     BPL     BMI     BVC     BVS     BCC     BCS     BNE     BEQ  Relative
  1112. +11     ORA     AND     EOR     ADC     STA     LDA     CMP     SBC  (indir),y
  1113. +12      t       t       t       t       t       t       t       t      ?
  1114. +13     SLO*    RLA*    SRE*    RRA*    SHA**   LAX*    DCP*    ISB* (indir),y
  1115. +14     NOP*    NOP*    NOP*    NOP*    STY     LDY     NOP*    NOP* Zeropage,x
  1116. +15     ORA     AND     EOR     ADC     STA     LDA     CMP     SBC     -"-
  1117. +16     ASL     ROL     LSR     ROR     STX y)  LDX y)  DEC     INC     -"-
  1118. +17     SLO*    RLA*    SRE*    RRA*    SAX* y) LAX* y) DCP     ISB     -"-
  1119.  
  1120. +18     CLC     SEC     CLI     SEI     TYA     CLV     CLD     SED  Implied
  1121. +19     ORA     AND     EOR     ADC     STA     LDA     CMP     SBC  Absolute,y
  1122. +1a     NOP*    NOP*    NOP*    NOP*    TXS     TSX     NOP*    NOP* Implied
  1123. +1b     SLO*    RLA*    SRE*    RRA*    SHS**   LAS**   DCP*    ISB* Absolute,y
  1124. +1c     NOP*    NOP*    NOP*    NOP*    SHY**   LDY     NOP*    NOP* Absolute,x
  1125. +1d     ORA     AND     EOR     ADC     STA     LDA     CMP     SBC     -"-
  1126. +1e     ASL     ROL     LSR     ROR     SHX**y) LDX y)  DEC     INC     -"-
  1127. +1f     SLO*    RLA*    SRE*    RRA*    SHA**y) LAX* y) DCP     ISB     -"-
  1128.  
  1129.         Legend:
  1130.  
  1131.         t       Jams the machine
  1132.         *t      Jams very rarely
  1133.         *       Undocumented command
  1134.         **      Unusual operation
  1135.         y)      indexed using IY instead of IX
  1136.  
  1137.  
  1138.  
  1139.                 6510/8502 Undocumented Commands
  1140.  
  1141.          -- A brief explanation about what may happen while
  1142.                 using don't care states.
  1143.  
  1144.  
  1145.         ANE $8B         AC = (AC | #$EE) & IX & #byte
  1146.                         same as
  1147.                         AC = ((AC & #$11 & IX) | ( #$EE & IX)) & #byte
  1148.  
  1149.                         In real 6510/8502 the internal parameter #$11 may
  1150.                         occasionally be #$10, #$01 or even #$00. This occurs
  1151.                         probably when the VIC halts the processor right between
  1152.                         the two clock cycles of this instruction.
  1153.  
  1154.         LXA $AB         C=Lehti:   AC = IX = ANE
  1155.                         Alternate: AC = IX = (AC & #byte)
  1156.  
  1157.                         TXA and TAX have to be responsible for these.
  1158.  
  1159.  
  1160.         SHA $93,$9F     Store (AC & IX & (ADDR_HI + 1))
  1161.         SHX $9E         Store (IX & (ADDR_HI + 1))
  1162.         SHY $9C         Store (IY & (ADDR_HI + 1))
  1163.         SHS $9B         SHA and TXS, where X is replaced by (AC & IX).
  1164.  
  1165.                         Note: The value to be stored is copied also
  1166.                         to ADDR_HI if page boundary is crossed.
  1167.  
  1168.  
  1169.         SBX $CB         Carry and Decimal flags are ignored but set in 
  1170.                         substraction. This is due to the CMP command,
  1171.                         which is executed instead of the real SBC.
  1172.  
  1173.  
  1174.  Many undocumented commands do not use AND between registers, the CPU just
  1175.  throws the bytes to a bus simultaneously and lets the open-collector drivers
  1176.  perform the AND. I.e. the command called 'SAX', which is in the STORE section
  1177.  (opcodes $A0...$BF), stores the result of (AC & IX) by this way.
  1178.  
  1179.  More fortunate is its opposite, 'LAX' which just loads a byte simultaeously
  1180.  into both AC and IX.
  1181.  
  1182.  
  1183.         $CB  SBX   IX <- (AC & IX) - Immediate
  1184.  
  1185.  The 'SBX' ($CB) may seem to be very complex operation, even though it is
  1186.  combination of subtraction of accumulator and parameter, as in the 'CMP'
  1187.  instruction, and the command 'DEX'. As a result, both AC and IX are connected
  1188.  to ALU but only the subtraction takes place. Since the comparison logic was
  1189.  used, the result of subtraction should be normally ignored, but the 'DEX' now
  1190.  happily stores to IX the value of (AC & IX) - Immediate.
  1191.  That is why this instruction does not have any decimal mode, and it does not
  1192.  affect the V flag. Also Carry flag is ignored in the subtraction but set
  1193.  according to the result.
  1194.  
  1195.  Proof:
  1196.  
  1197. begin 644 vsbx
  1198. M`0@9$,D'GL(H-#,IJC(U-JS"*#0T*:HR-@```*D`H#V1*Z`_D2N@09$KJ0>%
  1199. M^QBE^VEZJ+$KH#F1*ZD`2"BI`*(`RP`(:-B@.5$K*4#P`E@`H#VQ*SAI`)$K
  1200. JD-Z@/[$K:0"1*Y#4J2X@TO\XH$&Q*VD`D2N0Q,;[$+188/_^]_:_OK>V
  1201. `
  1202. end
  1203.  
  1204.  and
  1205.  
  1206. begin 644 sbx
  1207. M`0@9$,D'GL(H-#,IJC(U-JS"*#0T*:HR-@```'BI`*!-D2N@3Y$KH%&1*ZD#
  1208. MA?L8I?M*2)`#J1@LJ3B@29$K:$J0`ZGX+*G8R)$K&/BXJ?2B8\L)AOP(:(7]
  1209. MV#B@3;$KH$\Q*Z!1\2L(1?SP`0!H1?TIM]#XH$VQ*SAI`)$KD,N@3[$K:0"1
  1210. 9*Y#!J2X@TO\XH%&Q*VD`D2N0L<;[$))88-#X
  1211. `
  1212. end
  1213.  
  1214.  These test programs show if your machine is compatible with ours
  1215.  regarding the opcode $CB. The first test, vsbx, shows that SBX does
  1216.  not affect the V flag. The latter one, sbx, shows the rest of our
  1217.  theory. The vsbx test tests 33554432 SBX combinations (16777216
  1218.  different AC, IX and Immediate combinations, and two different V flag
  1219.  states), and the sbx test doubles that amount (16777216*4 D and C flag
  1220.  combinations). Both tests have run successfully on a C64 and a Vic20.
  1221.  They ought to run on C16, +4 and the PET series as well. The tests
  1222.  stop with BRK, if the opcode $CB does not work expectedly. Successful
  1223.  operation ends in RTS. As the tests are very slow, they print dots on
  1224.  the screen while running so that you know that the machine has
  1225.  not jammed. On computers running at 1 MHz, the first test prints
  1226.  approximately one dot every four seconds and a total of 2048 dots,
  1227.  whereas the second one prints half that amount, one dot every seven seconds.
  1228.  
  1229.  If the tests fail on your machine, please let us know your processor's part
  1230.  number and revision. If possible, save the executable (after it has stopped
  1231.  with BRK) under another name and send it to us so that we know at which stage
  1232.  the program stopped.
  1233.  
  1234.  The following program is a Commodore 64 executable that Marko M"akela
  1235.  developed when trying to find out how the V flag is affected by SBX.
  1236.  (It was believed that the SBX affects the flag in a weird way, and this
  1237.  program shows how SBX sets the flag differently from SBC.)
  1238.  You may find the subroutine at $C150 useful when researching other 
  1239.  undocumented instructions' flags. Run the program in a machine language
  1240.  monitor, as it makes use of the BRK instruction. The result tables will be
  1241.  written on pages $C2 and $C3.
  1242.  
  1243. begin 644 sbx-c100
  1244. M`,%XH`",#L&,$,&,$L&XJ8*B@LL7AOL(:(7\N#BM#L$M$,'M$L$(Q?OP`B@`
  1245. M:$7\\`,@4,'N#L'0U.X0P=#/SB#0[A+!T,<``````````````)BJ\!>M#L$M
  1246. L$,'=_\'0":T2P=W_PM`!8,K0Z:T.P2T0P9D`PID`!*T2P9D`PYD`!<C0Y``M
  1247. `
  1248. end
  1249.  
  1250.  
  1251.  Other undocumented instructions usually cause two preceding opcodes being
  1252.  executed. However 'NOP' seems to completely disappear from 'SBC' code $EB.
  1253.  
  1254.  The most difficult to comprehend are the rest of the instructions located on
  1255.  the '$0B' line.
  1256.  
  1257.  All the instructions located at the positive (left) side of this line should
  1258.  rotate either memory or the accumulator, but the addressing mode turns out
  1259.  to be immediate!
  1260.  No problem. Just read the operand, let it be ANDed with the accumulator
  1261.  and finally use accumulator addressing mode for the instructions above them.
  1262.  
  1263.  The rest two instructions on the same line, called 'ANE' and 'LXA' ($8B and
  1264.  $AB respectively) often give quite unpredictable results.
  1265.  However, the most usual operation is to store ((A | #$ee) & X & #$nn) to
  1266.  accumulator. Note that this does not work reliably in a real 64!
  1267.  On 8502 opcode $8B uses values 8C,CC, EE, and occasionally 0C and 8E for the
  1268.  OR instead of EE,EF,FE and FF used by 6510. With 8502 running at 2 MHz #$EE is
  1269.  always used.
  1270.  Opcode $AB does not cause this OR taking place on 8502 while 6510 always 
  1271.  performs it.  Note that this behaviour depends on chip revision.
  1272.  
  1273.  Let's take a closer look at $8B (6510).
  1274.  
  1275.         AC <- IX & D & (AC | VAL)
  1276.  
  1277.         where VAL comes from this table:
  1278.  
  1279.        IX high  D high  D low   VAL
  1280.         even     even    ---    $EE (1)
  1281.         even     odd     ---    $EE
  1282.         odd      even    ---    $EE
  1283.         odd      odd      0     $EE
  1284.         odd      odd     not 0  $FE (2)
  1285.  
  1286.  (1) If the bottom 2 bits of AC are both 1, then the LSB of the result may
  1287.     be 0. The values of IX and D are different every time I run the test.
  1288.     This appears to be very rare.
  1289.  (2) VAL is $FE most of the time. Sometimes it is $EE - it seems to be random,
  1290.     not related to any of the data. This is much more common than (1).
  1291.  
  1292.   In decimal mode, VAL is usually $FE.
  1293.  
  1294.  
  1295.  Two different functions has been discovered for LAX, opcode $AB. One is
  1296.  AC = IX = ANE (see above) and the other, encountered with 6510 and 8502,
  1297.  is less complicated AC = IX = (AC & #byte). However, according to what is
  1298.  reported, the version altering only the lowest bits of each nybble seems to
  1299.  be more common.
  1300.  
  1301.  What happens, is that $AB loads a value into both AC and IX, ANDing
  1302.  the low bit of each nybble with the corresponding bit of the old AC. However,
  1303.  there are exceptions. Sometimes the low bit is cleared even when AC contains
  1304.  a '1', and sometimes other bits are cleared. The exceptions seem random (they
  1305.  change every time I run the test). Oops - that was in decimal mode. Much
  1306.  the same with D=0.
  1307.  
  1308.  What causes the randomness?  Probably it is that it is marginal logic levels -
  1309.  when too much wired-anding goes on, some of the signals get very close to
  1310.  the threshold. Perhaps we're seeing some of them step over it. The low bit
  1311.  of each nybble is special, since it has to cope with carry differently
  1312.  (remember decimal mode). We never see a '0' turn into a '1'.
  1313.  
  1314.  Since these instructions are unpredictable, they should not be used.
  1315.  
  1316.  
  1317.  There is still very strange instruction left, the one named SHA/X/Y, which is
  1318.  the only one with only indexed addressing modes. Actually, the commands 'SHA',
  1319.  'SHX' and 'SHY' are generated by the indexing algorithm.
  1320.  
  1321.  While using indexed addressing, effective address for page boundary crossing
  1322.  is calculated as soon as possible so it does not slow down operation.
  1323.  As a result, in the case of SHA/X/Y, the address and data are prosessed at the
  1324.  same time making AND between the to take place. Thus, the value to be stored
  1325.  by SAX, for example, is in fact (AC & IX & (ADDR_HI + 1)).
  1326.  On page boundary crossing the same value is copied also to high byte of the
  1327.  effective address.
  1328.  
  1329.  
  1330.  
  1331.   Register selection for load and store
  1332.  
  1333.    bit1 bit0    AC IX IY
  1334.     0   0             x
  1335.     0   1          x
  1336.     1   0       x
  1337.     1   1       x  x
  1338.  
  1339.  So, AC and IX are selected by bits 1 and 0 respectively, while ~(bit1 | bit0)
  1340.  enables IY.
  1341.  
  1342.  Indexing is determined by bit4, even in relative addressing mode, which
  1343.  is one kind of indexing.
  1344.  
  1345.  Lines containing opcodes xxx000x1 (01 and 03) are treated as absolute after
  1346.  the effective address has been loaded into CPU.
  1347.  
  1348.  Zeropage,y and Absolute,y (codes 10x1 x11x) are distinquished by bit5.
  1349.  
  1350.  
  1351.                  Decimal mode in NMOS 6500 series
  1352.  
  1353.    Most sources claim that the NMOS 6500 series sets the N, V and Z flags
  1354.  unpredictably. Of course, this is not true. While testing how the flags are
  1355.  set, I also wanted to see what happens if you use illegal BCD values.
  1356.  
  1357.    ADC works in Decimal mode in a quite complicated way. It is amazing how it
  1358.  can do that all in a single cycle. Here's a pseudo code version of the
  1359.  instruction:
  1360.  
  1361.     AC    accumulator
  1362.     AL    low nybble of accumulator
  1363.     AH    high nybble of accumulator
  1364.  
  1365.     C    Carry flag
  1366.     Z    Zero flag
  1367.     V    oVerflow flag
  1368.     N    Negative flag
  1369.  
  1370.     s    value to be added to accumulator
  1371.  
  1372.     AL = (AC & 15) + (s & 15) + C;        ! Calculate the lower nybble.
  1373.  
  1374.     if (AL > 9)                ! BCD fixup
  1375.       AL += 6;                ! for lower nybble
  1376.  
  1377.     AH = (A >> 4) + (s >> 4) + (AL > 15);    ! Calculate the upper nybble.
  1378.  
  1379.     Z = (AC + s + C != 0);            ! Zero flag is set just
  1380.                         ! like in Binary mode.
  1381.  
  1382.     ! Negative and Overflow flags are set with the same logic than in
  1383.     ! Binary mode, but after fixing the lower nybble.
  1384.  
  1385.     N = (AH & 8 != 0);
  1386.     V = ((AH & 8) ^ (A >> 4)) && (!(A ^ s) & 128);
  1387.  
  1388.     if (AH > 9)                ! BCD fixup
  1389.       AH += 6;                ! for upper nybble
  1390.  
  1391.     
  1392.  
  1393.     ! Carry is the only flag set after fixing the result.
  1394.  
  1395.     C = (AH > 15);
  1396.     AC = ((AH << 4) | (AL & 15)) & 255;
  1397.  
  1398.  
  1399.    The C flag is set as the quiche eaters expect, but the N and V flags 
  1400.  are set after fixing the lower nybble but before fixing the upper one.
  1401.  They use the same logic than binary mode ADC. The Z flag is set before
  1402.  any BCD fixup, so the D flag does not have any influence on it.
  1403.  
  1404.  Proof: The following test program tests all 131072 ADC combinations in
  1405.         Decimal mode, and aborts with BRK if anything breaks this theory.
  1406.         If everything goes well, it ends in RTS.
  1407.  
  1408. begin 600 dadc
  1409. M 0@9",D'GL(H-#,IJC(U-JS"*#0T*:HR-@   'BI&*  A/N$_$B@+)$KH(V1
  1410. M*Q@(I?PI#X7]I?LI#V7]R0J0 FD%J"D/A?VE^RGP9?PI\ C $) ":0^JL @H
  1411. ML ?)H) &""@X:5\X!?V%_0AH*3W@ ! ""8"HBD7[$ JE^T7\, 28"4"H**7[
  1412. M9?S0!)@) J@8N/BE^V7\V A%_= G:(3]1?W0(.;[T(?F_-"#:$D8\ )88*D=
  1413. 0&&4KA?NI &4LA?RI.&S[  A%
  1414.  
  1415. end
  1416.  
  1417.    All programs in this chapter have been successfully tested on a Vic20
  1418.  and a Commodore 64. They should run on C16, +4 and on the PET series as
  1419.  well. If not, please report the problem to Marko M"akel"a. Each test in
  1420.  this chapter should run in less than a minute at 1 MHz.
  1421.  
  1422.    SBC is much easier. Just like CMP, its flags are not affected by
  1423.  the D flag.
  1424.  
  1425.  Proof:
  1426.  
  1427. begin 600 dsbc-cmp-flags
  1428. M 0@9",D'GL(H-#,IJC(U-JS"*#0T*:HR-@   'B@ (3[A/RB XH8:66HL2N@
  1429. M09$KH$R1*XII::BQ*Z!%D2N@4)$K^#BXI?OE_-@(:(7].+BE^^7\"&A%_? !
  1430. 5 .;[T./F_-#?RA"_8!@X&#CEY<7%
  1431.  
  1432. end
  1433.  
  1434.  
  1435.    The only difference in SBC's operation in decimal mode from binary mode
  1436.  is the result-fixup:
  1437.  
  1438.     AC    accumulator
  1439.     AL    low nybble of accumulator
  1440.     AH    high nybble of accumulator
  1441.  
  1442.     C    Carry flag
  1443.     Z    Zero flag
  1444.     V    oVerflow flag
  1445.     N    Negative flag
  1446.  
  1447.     s    value to be added to accumulator
  1448.  
  1449.     AL = (AC & 15) - (s & 15) - !C;        ! Calculate the lower nybble.
  1450.  
  1451.     if (AL & 16)                ! BCD fixup
  1452.       AL -= 6;                ! for lower nybble
  1453.  
  1454.     AH = (AC >> 4) - (s >> 4) - (AL > 15);    ! Calculate the upper nybble.
  1455.  
  1456.     if (AH & 16)                ! BCD fixup
  1457.       AH -= 6;                ! for upper nybble
  1458.  
  1459.     ! Flags are set just like in Binary mode.
  1460.  
  1461.     C = (AC - s - !C > 255);
  1462.     Z = (AC - s - !C != 0);
  1463.     V = ((AC - s - !C) ^ s) && ((AC ^ s) & 128);
  1464.     N = ((AC - s - !C) & 128);
  1465.  
  1466.     AC = ((AH << 4) | (AL & 15)) & 255;
  1467.  
  1468.  
  1469.    Again Z flag is set before any BCD fixup. The N and V flags are set
  1470.  at any time before fixing the high nybble. The C flag may be set in any
  1471.  phase.
  1472.  
  1473.    Decimal subtraction is easier than decimal addition, as you have to
  1474.  make the BCD fixup only when a nybble flows over. In decimal addition,
  1475.  you had to verify if the nybble was greater than 9. The processor has
  1476.  an internal "half carry" flag for the lower nybble, and it uses it to
  1477.  trigger the BCD fixup. When calculating with legal BCD values, the
  1478.  lower nybble cannot flow over again when fixing it. So the processor
  1479.  does not handle overflows while performing the fixup. Similarly, the
  1480.  BCD fixup occurs in the high nybble only if the value flows over,
  1481.  i.e. when the C flag will be cleared.
  1482.  
  1483.    Because SBC's flags are not affected by the Decimal mode flag, you
  1484.  could guess that CMP uses the SBC logic, only setting the C flag
  1485.  first. But the SBX instruction shows that CMP also temporarily clears
  1486.  the D flag, although it is totally unnecessary.
  1487.  
  1488.    The following program, which tests SBC's result and flags,
  1489.  contains the 6502 version of the pseudo code example above.
  1490.  
  1491. begin 600 dsbc
  1492. M 0@9",D'GL(H-#,IJC(U-JS"*#0T*:HR-@   'BI&*  A/N$_$B@+)$KH':1
  1493. M*S@(I?PI#X7]I?LI#^7]L /I!1@I#ZBE_"GPA?VE^RGP"#CE_2GPL KI7RBP
  1494. M#ND/.+ )*+ &Z0^P NE?A/T%_87]*+BE^^7\"&BH.+CXI?OE_-@(1?W0FVB$
  1495. 8_47]T)3F^]">YOS0FFA)&- $J3C0B%A@
  1496.  
  1497. end
  1498.  
  1499.    Obviously the undocumented instructions RRA (ROR+ADC) and ISB
  1500.  (INC+SBC) have inherited also the decimal operation from the official
  1501.  instructions ADC and SBC. The program droradc shows this statement
  1502.  for ROR, and the dincsbc test shows this for ISB. Finally,
  1503.  dincsbc-deccmp shows that ISB's and DCP's (DEC+CMP) flags are not
  1504.  affected by the D flag.
  1505.  
  1506. begin 644 droradc
  1507. M`0@9",D'GL(H-#,IJC(U-JS"*#0T*:HR-@```'BI&*``A/N$_$B@+)$KH(V1
  1508. M*S@(I?PI#X7]I?LI#V7]R0J0`FD%J"D/A?VE^RGP9?PI\`C`$)`":0^JL`@H
  1509. ML`?)H)`&""@X:5\X!?V%_0AH*3W@`!`""8"HBD7[$`JE^T7\,`28"4"H**7[
  1510. M9?S0!)@)`J@XN/BE^R;\9_S8"$7]T"=HA/U%_=`@YOO0A>;\T(%H21CP`EA@
  1511. 2J1T892N%^ZD`92R%_*DX;/L`
  1512. `
  1513. end
  1514.  
  1515. begin 644 dincsbc
  1516. M`0@9",D'GL(H-#,IJC(U-JS"*#0T*:HR-@```'BI&*``A/N$_$B@+)$KH':1
  1517. M*S@(I?PI#X7]I?LI#^7]L`/I!1@I#ZBE_"GPA?VE^RGP"#CE_2GPL`KI7RBP
  1518. M#ND/.+`)*+`&Z0^P`NE?A/T%_87]*+BE^^7\"&BH.+CXI?O&_.?\V`A%_="9
  1519. ::(3]1?W0DN;[T)SF_-"8:$D8T`2I.-"&6&#\
  1520. `
  1521. end
  1522.  
  1523. begin 644 dincsbc-deccmp
  1524. M`0@9",D'GL(H-#,IJC(U-JS"*#0T*:HR-@```'B@`(3[A/RB`XH8:7>HL2N@
  1525. M3Y$KH%R1*XII>ZBQ*Z!3D2N@8)$KBFE_J+$KH%61*Z!BD2OX.+BE^^;\Q_S8
  1526. L"&B%_3BXI?OF_,?\"&A%_?`!`.;[T-_F_-#;RA"M8!@X&#CFYL;&Q\?GYP#8
  1527. `
  1528. end
  1529.  
  1530.  
  1531.  
  1532.                  6510 features
  1533.  
  1534.    o  PHP always pushes the Break (B) flag as a `1' to the stack.
  1535.       Jukka Tapanim"aki claimed in C=lehti issue 3/89, on page 27 that the
  1536.       processor makes a logical OR between the status register's bit 4 
  1537.       and the bit 8 of the stack register (which is always 1).
  1538.  
  1539.    o  Indirect addressing modes do not handle page boundary crossing at all.
  1540.       When the parameter's low byte is $FF, the effective address wraps 
  1541.       around and the CPU fetches high byte from $xx00 instead of $xx00+$0100.
  1542.       E.g. JMP ($01FF) fetches PCL from $01FF and PCH from $0100,
  1543.       and LDA ($FF),Y fetches the base address from $FF and $00.
  1544.  
  1545.    o  Indexed zero page addressing modes never fix the page address on
  1546.       crossing the zero page boundary.
  1547.       E.g. LDX #$01 : LDA ($FF,X) loads the effective address from $00 and $01.
  1548.  
  1549.    o  The processor always fetches the byte following a relative branch
  1550.       instruction. If the branch is taken, the processor reads then the
  1551.       opcode from the destination address. If page boundary is crossed, it
  1552.       first reads a byte from the old page from a location that is bigger
  1553.       or smaller than the correct address by one page.
  1554.  
  1555.    o  If you cross a page boundary in any other indexed mode,
  1556.       the processor reads an incorrect location first, a location that is
  1557.       smaller by one page.
  1558.  
  1559.    o  Read-Modify-Write instructions write unmodified data, then modified
  1560.       (so INC effectively does LDX loc;STX loc;INX;STX loc)
  1561.  
  1562.    o  -RDY is ignored during writes
  1563.       (This is why you must wait 3 cycles before doing any DMA -
  1564.       the maximum number of consecutive writes is 3, which occurs
  1565.       during interrupts except -RESET.)
  1566.  
  1567.    o  Some undefined opcodes may give really unpredictable results.
  1568.  
  1569.    o  All registers except the Program Counter remain the same after -RESET.
  1570.       (This is why you must preset D and I flags in the RESET handler.)
  1571.  
  1572.  
  1573.                 Different CPU types
  1574.  
  1575.  The Rockwell data booklet 29651N52 (technical information about R65C00 
  1576.  microprocessors, dated October 1984), lists the following differences between
  1577.  NMOS R6502 microprocessor and CMOS R65C00 family:
  1578.  
  1579.  1. Indexed addressing across page boundary.
  1580.         NMOS: Extra read of invalid address.
  1581.         CMOS: Extra read of last instruction byte.
  1582.  
  1583.  2. Execution of invalid op codes.
  1584.         NMOS: Some terminate only by reset. Results are undefined.
  1585.         CMOS: All are NOPs (reserved for future use).
  1586.  
  1587.  3. Jump indirect, operand = XXFF.
  1588.         NMOS: Page address does not increment.
  1589.         CMOS: Page address increments and adds one additional cycle.
  1590.  
  1591.  4. Read/modify/write instructions at effective address.
  1592.         NMOS: One read and two write cycles.
  1593.         CMOS: Two read and one write cycle.
  1594.  
  1595.  5. Decimal flag.
  1596.         NMOS: Indeterminate after reset.
  1597.         CMOS: Initialized to binary mode (D=0) after reset and interrupts.
  1598.  
  1599.  6. Flags after decimal operation.
  1600.         NMOS: Invalid N, V and Z flags.
  1601.         CMOS: Valid flag adds one additional cycle.
  1602.  
  1603.  7. Interrupt after fetch of BRK instruction.
  1604.         NMOS: Interrupt vector is loaded, BRK vector is ignored.
  1605.         CMOS: BRK is executed, then interrupt is executed.
  1606.  
  1607.  
  1608.  
  1609.                 6510 Instruction Timing
  1610.  
  1611.    The NMOS 6500 series uses a sort of pipelining. It always reads two
  1612.  bytes for each instruction. If the instruction was only two cycles long,
  1613.  the opcode for the next instruction can be fetched during the third cycle.
  1614.  As most instructions are two or three bytes long, this is quite efficient.
  1615.  But one-byte instructions take two cycles, even though they could be
  1616.  performed in one.
  1617.  
  1618.    The following tables show what happens on the bus while executing different
  1619.  kinds of instructions. The tables having "???" marks at any cycle may be
  1620.  totally wrong, but the rest should be absolutely accurate.
  1621.  
  1622.  
  1623.   Interrupts
  1624.  
  1625.      NMI and IRQ both take 7 cycles. Their timing diagram is much like
  1626.      BRK's. IRQ will be executed only when the I flag is clear.
  1627.      The processor will usually wait for the current instruction to
  1628.      complete before executing the interrupt sequence.
  1629.  
  1630.      There is one exception to this rule: If a NMI occurs while the
  1631.      processor is executing a BRK, the two interrupts may take 7 to 14
  1632.      cycles to execute, and the processor may totally lose the BRK
  1633.      instruction. Probably the results are similar also with IRQ.
  1634.      Marko M"akel"a experimented with BRK/NMI, but he still hasn't
  1635.      analyzed the results.
  1636.  
  1637.      RESET does not push program counter on stack, and we don't know how
  1638.      long it lasts. But we know that RESET preserves all registers
  1639.      (except PC).
  1640.  
  1641.  
  1642.   Accumulator or implied addressing
  1643.  
  1644.      BRK
  1645.  
  1646.         #  address R/W description
  1647.        --- ------- --- -----------------------------------------------
  1648.         1    PC     R  fetch opcode, increment PC
  1649.         2    PC     R  read next instruction byte (and throw it away),
  1650.                increment PCR
  1651.         3  $0100,S  W  push PCH on stack (with B flag set), decrement S
  1652.         4  $0100,S  W  push PCL on stack, decrement S
  1653.         5  $0100,S  W  push P on stack, decrement S
  1654.         6   $FFFE   R  fetch PCL
  1655.         7   $FFFF   R  fetch PCH
  1656.  
  1657.  
  1658.      RTI
  1659.  
  1660.         #  address R/W description
  1661.        --- ------- --- -----------------------------------------------
  1662.         1    PCR    R  fetch opcode, increment PCR
  1663.         2    PCR    R  read next instruction byte (and throw it away),
  1664.                      increment PCR
  1665.         3  $0100,S  R  increment S
  1666.         4  $0100,S  R  pull P from stack, increment S
  1667.         5  $0100,S  R  pull PCL from stack, increment S
  1668.         6  $0100,S  R  pull PCH from stack
  1669.  
  1670.  
  1671.      RTS
  1672.  
  1673.         #  address R/W description
  1674.        --- ------- --- -----------------------------------------------
  1675.         1    PCR    R  fetch opcode, increment PCR
  1676.         2    PCR    R  read next instruction byte (and throw it away),
  1677.                increment PCR
  1678.         3  $0100,S  R  increment S
  1679.         4  $0100,S  R  pull PCL from stack, increment S
  1680.         5  $0100,S  R  pull PCH from stack
  1681.         6    PCR    R  increment PCR
  1682.  
  1683.  
  1684.      PHA, PHP
  1685.  
  1686.         #  address R/W description
  1687.        --- ------- --- -----------------------------------------------
  1688.         1    PCR    R  fetch opcode, increment PCR
  1689.         2    PCR    R  read next instruction byte (and throw it away),
  1690.                increment PCR
  1691.         3  $0100,S  W  push register on stack, decrement S
  1692.  
  1693.  
  1694.      PLA, PLP
  1695.  
  1696.         #  address R/W description
  1697.        --- ------- --- -----------------------------------------------
  1698.         1    PCR    R  fetch opcode, increment PCR
  1699.         2    PCR    R  read next instruction byte (and throw it away),
  1700.                increment PCR
  1701.         3  $0100,S  R  increment S
  1702.         4  $0100,S  R  pull register from stack
  1703.  
  1704.         Note: The 3rd cycle does NOT read from PCR.
  1705.               Maybe it reads from $0100,S.
  1706.  
  1707.  
  1708.      Other instructions
  1709.  
  1710.         #  address R/W description
  1711.        --- ------- --- -----------------------------------------------
  1712.         1    PCR    R  fetch opcode, increment PCR
  1713.         2    PCR    R  read next instruction byte (and throw it away),
  1714.                increment PCR
  1715.  
  1716.  
  1717.   Immediate addressing
  1718.  
  1719.         #  address R/W description
  1720.        --- ------- --- ------------------------------------------
  1721.         1    PCR    R  fetch opcode, increment PCR
  1722.         2    PCR    R  fetch value, increment PCR
  1723.  
  1724.  
  1725.   Absolute addressing
  1726.  
  1727.      JMP
  1728.  
  1729.         #  address R/W description
  1730.        --- ------- --- -------------------------------------------------
  1731.         1    PCR    R  fetch opcode, increment PCR
  1732.         2    PCR    R  fetch address's low byte to latch, increment PCR
  1733.         3    PCR    R  copy latch to PCL, fetch address's high byte to
  1734.                latch, increment PCR, copy latch to PCH
  1735.  
  1736.  
  1737.      JSR
  1738.  
  1739.         #  address R/W description
  1740.        --- ------- --- -------------------------------------------------
  1741.         1    PCR    R  fetch opcode, increment PCR
  1742.         2    PCR    R  fetch address's low byte to latch, increment PCR
  1743.         3  $0100,S  R  store latch
  1744.         4  $0100,S  W  push PCH on stack, decrement S
  1745.         5  $0100,S  W  push PCL on stack, decrement S
  1746.         6    PCR    R  copy latch to PCL, fetch address's high byte to
  1747.                latch, increment PCR, copy latch to PCH
  1748.  
  1749.  
  1750.      Read instructions (LDA, LDX, LDY, EOR, AND, ORA, ADC, SBC, CMP, BIT,
  1751.                         LAX, NOP)
  1752.  
  1753.         #  address R/W description
  1754.        --- ------- --- ------------------------------------------
  1755.         1    PCR    R  fetch opcode, increment PCR
  1756.         2    PCR    R  fetch low byte of address, increment PCR
  1757.         3    PCR    R  fetch high byte of address, increment PCR
  1758.         4  address  R  read from effective address
  1759.  
  1760.  
  1761.      Read-Modify-Write instructions (ASL, LSR, ROL, ROR, INC, DEC,
  1762.                                      SLO, SRE, RLA, RRA, ISB, DCP)
  1763.  
  1764.         #  address R/W description
  1765.        --- ------- --- ------------------------------------------
  1766.         1    PCR    R  fetch opcode, increment PCR
  1767.         2    PCR    R  fetch low byte of address, increment PCR
  1768.         3    PCR    R  fetch high byte of address, increment PCR
  1769.         4  address  R  read from effective address
  1770.         5  address  W  write the value back to effective address,
  1771.                        and do the operation on it
  1772.         6  address  W  write the new value to effective address
  1773.  
  1774.  
  1775.      Write instructions (STA, STX, STY, SAX)
  1776.     
  1777.         #  address R/W description
  1778.        --- ------- --- ------------------------------------------
  1779.         1    PCR    R  fetch opcode, increment PCR
  1780.         2    PCR    R  fetch low byte of address, increment PCR
  1781.         3    PCR    R  fetch high byte of address, increment PCR
  1782.         4  address  W  write register to effective address
  1783.  
  1784.  
  1785.   Zero page addressing
  1786.  
  1787.      Read instructions (LDA, LDX, LDY, EOR, AND, ORA, ADC, SBC, CMP, BIT,
  1788.                         LAX, NOP)
  1789.  
  1790.         #  address R/W description
  1791.        --- ------- --- ------------------------------------------
  1792.         1    PCR    R  fetch opcode, increment PCR
  1793.         2    PCR    R  fetch address, increment PCR
  1794.         3  address  R  read from effective address
  1795.  
  1796.  
  1797.      Read-Modify-Write instructions (ASL, LSR, ROL, ROR, INC, DEC,
  1798.                                      SLO, SRE, RLA, RRA, ISB, DCP)
  1799.  
  1800.         #  address R/W description
  1801.        --- ------- --- ------------------------------------------
  1802.         1    PCR    R  fetch opcode, increment PCR
  1803.         2    PCR    R  fetch address, increment PCR
  1804.         3  address  R  read from effective address
  1805.         4  address  W  write the value back to effective address,
  1806.                        and do the operation on it
  1807.         5  address  W  write the new value to effective address
  1808.  
  1809.  
  1810.      Write instructions (STA, STX, STY, SAX)
  1811.     
  1812.         #  address R/W description
  1813.        --- ------- --- ------------------------------------------
  1814.         1    PCR    R  fetch opcode, increment PCR
  1815.         2    PCR    R  fetch address, increment PCR
  1816.         3  address  W  write register to effective address
  1817.  
  1818.   Zero page indexed addressing
  1819.  
  1820.      Read instructions (LDA, LDX, LDY, EOR, AND, ORA, ADC, SBC, CMP, BIT,
  1821.                         LAX, NOP)
  1822.  
  1823.         #   address  R/W description
  1824.        --- --------- --- ------------------------------------------
  1825.         1     PCR     R  fetch opcode, increment PCR
  1826.         2     PCR     R  fetch address, increment PCR
  1827.     3   address   R  read from address, add index register to it
  1828.         4  address+I* R  read from effective address
  1829.  
  1830.        Notes: I denotes either index register (X or Y).
  1831.  
  1832.               * The high byte of the effective address is always zero,
  1833.                 i.e. page boundary crossings are not handled.
  1834.  
  1835.  
  1836.      Read-Modify-Write instructions (ASL, LSR, ROL, ROR, INC, DEC,
  1837.                                      SLO, SRE, RLA, RRA, ISB, DCP)
  1838.  
  1839.         #   address  R/W description
  1840.        --- --------- --- ---------------------------------------------
  1841.         1     PCR     R  fetch opcode, increment PCR
  1842.         2     PCR     R  fetch address, increment PCR
  1843.     3   address   R  read from address, add index register X to it
  1844.         4  address+X* R  read from effective address
  1845.         5  address+X* W  write the value back to effective address,
  1846.                          and do the operation on it
  1847.         6  address+X* W  write the new value to effective address
  1848.  
  1849.        Note: * The high byte of the effective address is always zero,
  1850.                i.e. page boundary crossings are not handled.
  1851.  
  1852.  
  1853.      Write instructions (STA, STX, STY, SAX)
  1854.  
  1855.         #   address  R/W description
  1856.        --- --------- --- -------------------------------------------
  1857.         1     PCR     R  fetch opcode, increment PCR
  1858.         2     PCR     R  fetch address, increment PCR
  1859.     3   address   R  read from address, add index register to it
  1860.         4  address+I* W  write to effective address
  1861.  
  1862.        Notes: I denotes either index register (X or Y).
  1863.  
  1864.               * The high byte of the effective address is always zero,
  1865.                 i.e. page boundary crossings are not handled.
  1866.  
  1867.  
  1868.   Absolute indexed addressing
  1869.  
  1870.      Read instructions (LDA, LDX, LDY, EOR, AND, ORA, ADC, SBC, CMP, BIT,
  1871.                         LAX, LAE, SHS, NOP)
  1872.  
  1873.         #   address  R/W description
  1874.        --- --------- --- ------------------------------------------
  1875.         1     PCR     R  fetch opcode, increment PCR
  1876.         2     PCR     R  fetch low byte of address, increment PCR
  1877.         3     PCR     R  fetch high byte of address,
  1878.                          add index register to low address byte,
  1879.              increment PCR
  1880.         4  address+I* R  read from effective address,
  1881.                          fix the high byte of effective address
  1882.         4+ address+I  R  re-read from effective address
  1883.  
  1884.        Notes: I denotes either index register (X or Y).
  1885.  
  1886.               * The high byte of the effective address may be invalid
  1887.                 at this time, i.e. it may be smaller by $100.
  1888.  
  1889.               + This cycle will be executed only if the effective address
  1890.                 was invalid during cycle #4, i.e. page boundary was crossed.
  1891.  
  1892.  
  1893.      Read-Modify-Write instructions (ASL, LSR, ROL, ROR, INC, DEC,
  1894.                                      SLO, SRE, RLA, RRA, ISB, DCP)
  1895.  
  1896.         #   address  R/W description
  1897.        --- --------- --- ------------------------------------------
  1898.         1    PCR      R  fetch opcode, increment PCR
  1899.         2    PCR      R  fetch low byte of address, increment PCR
  1900.         3    PCR      R  fetch high byte of address,
  1901.                          add index register X to low address byte,
  1902.                  increment PCR
  1903.         4  address+X* R  read from effective address,
  1904.                          fix the high byte of effective address
  1905.         5  address+X  R  re-read from effective address
  1906.         6  address+X  W  write the value back to effective address,
  1907.                          and do the operation on it
  1908.         7  address+X  W  write the new value to effective address
  1909.  
  1910.        Notes: * The high byte of the effective address may be invalid
  1911.                 at this time, i.e. it may be smaller by $100.
  1912.  
  1913.  
  1914.      Write instructions (STA, STX, STY, SHA, SHX, SHY)
  1915.  
  1916.         #   address  R/W description
  1917.        --- --------- --- ------------------------------------------
  1918.         1     PCR     R  fetch opcode, increment PCR
  1919.         2     PCR     R  fetch low byte of address, increment PCR
  1920.         3     PCR     R  fetch high byte of address,
  1921.                          add index register to low address byte,
  1922.              increment PCR
  1923.         4  address+I* R  read from effective address,
  1924.                          fix the high byte of effective address
  1925.         5  address+I  W  write to effective address
  1926.  
  1927.        Notes: I denotes either index register (X or Y).
  1928.  
  1929.               * The high byte of the effective address may be invalid
  1930.                 at this time, i.e. it may be smaller by $100. Because
  1931.         the processor cannot undo a write to an invalid address,
  1932.                 it always reads from the address first.
  1933.  
  1934.  
  1935.   Relative addressing (BCC, BCS, BNE, BEQ, BPL, BMI, BVC, BVS)
  1936.  
  1937.         #   address  R/W description
  1938.        --- --------- --- ---------------------------------------------
  1939.         1     PCR     R  fetch opcode, increment PCR
  1940.         2     PCR     R  fetch operand, increment PCR
  1941.         3     PCR     R  Fetch opcode of next instruction,
  1942.                          If branch is taken, add operand to PCL.
  1943.              Otherwise increment PCR.
  1944.         3+    PCR*    R  Fetch opcode of next instruction.
  1945.                          Fix PCH. If it did not change, increment PCR.
  1946.         3!    PCR     R  Fetch opcode of next instruction,
  1947.              increment PCR.
  1948.  
  1949.        Notes: * The high byte of Program Counter (PCH) may be invalid
  1950.                 at this time, i.e. it may be smaller or bigger by $100.
  1951.  
  1952.               + If branch is taken, this cycle will be executed.
  1953.  
  1954.               ! If branch occurs to different page, this cycle will be
  1955.                 executed.
  1956.  
  1957.  
  1958.   Indexed indirect addressing
  1959.  
  1960.      Read instructions (LDA, ORA, EOR, AND, ADC, CMP, SBC, LAX)
  1961.  
  1962.         #    address   R/W description
  1963.        --- ----------- --- ------------------------------------------
  1964.         1      PCR      R  fetch opcode, increment PCR
  1965.         2      PCR      R  fetch pointer address, add X to it,
  1966.                increment PCR
  1967.         3      ???      R  internal operation
  1968.         4   pointer+X   R  fetch effective address low
  1969.         5  pointer+X+1  R  fetch effective address high
  1970.         6    address    R  read from effective address
  1971.  
  1972.        Note: The effective address is always fetched from zero page,
  1973.              i.e. the zero page boundary crossing is not handled.
  1974.  
  1975.      Read-Modify-Write instructions (SLO, SRE, RLA, RRA, ISB, DCP)
  1976.  
  1977.         #    address   R/W description
  1978.        --- ----------- --- ------------------------------------------
  1979.         1      PCR      R  fetch opcode, increment PCR
  1980.         2      PCR      R  fetch pointer address, add X to it,
  1981.                increment PCR
  1982.         3      ???      R  internal operation
  1983.         4   pointer+X   R  fetch effective address low
  1984.         5  pointer+X+1  R  fetch effective address high
  1985.         6    address    R  read from effective address
  1986.         7    address    W  write the value back to effective address,
  1987.                            and do the operation on it
  1988.         8    address    W  write the new value to effective address
  1989.  
  1990.        Note: The effective address is always fetched from zero page,
  1991.              i.e. the zero page boundary crossing is not handled.
  1992.  
  1993.      Write instructions (STA, SAX)
  1994.  
  1995.         #    address   R/W description
  1996.        --- ----------- --- ------------------------------------------
  1997.         1      PCR      R  fetch opcode, increment PCR
  1998.         2      PCR      R  fetch pointer address, add X to it,
  1999.                increment PCR
  2000.         3      ???      R  internal operation
  2001.         4   pointer+X   R  fetch effective address low
  2002.         5  pointer+X+1  R  fetch effective address high
  2003.         6    address    W  write to effective address
  2004.  
  2005.        Note: The effective address is always fetched from zero page,
  2006.              i.e. the zero page boundary crossing is not handled.
  2007.  
  2008.   Indirect indexed addressing
  2009.  
  2010.      Read instructions (LDA, EOR, AND, ORA, ADC, SBC, CMP)
  2011.  
  2012.         #    address   R/W description
  2013.        --- ----------- --- ------------------------------------------
  2014.         1      PCR      R  fetch opcode, increment PCR
  2015.         2      PCR      R  fetch pointer address, increment PCR
  2016.         3    pointer    R  fetch effective address low
  2017.         4   pointer+1   R  fetch effective address high,
  2018.                            add Y to low byte of effective address
  2019.         5   address+Y*  R  read from effective address,
  2020.                            fix high byte of effective address
  2021.         5+  address+Y   R  read from effective address
  2022.  
  2023.        Notes: The effective address is always fetched from zero page,
  2024.               i.e. the zero page boundary crossing is not handled.
  2025.  
  2026.               * The high byte of the effective address may be invalid
  2027.                 at this time, i.e. it may be smaller by $100.
  2028.  
  2029.               + This cycle will be executed only if the effective address
  2030.                 was invalid during cycle #5, i.e. page boundary was crossed.
  2031.  
  2032.  
  2033.      Read-Modify-Write instructions (SLO, SRE, RLA, RRA, ISB, DCP)
  2034.  
  2035.         #    address   R/W description
  2036.        --- ----------- --- ------------------------------------------
  2037.         1      PCR      R  fetch opcode, increment PCR
  2038.         2      PCR      R  fetch pointer address, increment PCR
  2039.         3    pointer    R  fetch effective address low
  2040.         4   pointer+1   R  fetch effective address high,
  2041.                            add Y to low byte of effective address
  2042.         5   address+Y*  R  read from effective address,
  2043.                            fix high byte of effective address
  2044.         6   address+Y   W  write to effective address
  2045.         7   address+Y   W  write the value back to effective address,
  2046.                            and do the operation on it
  2047.         8   address+Y   W  write the new value to effective address
  2048.  
  2049.        Notes: The effective address is always fetched from zero page,
  2050.               i.e. the zero page boundary crossing is not handled.
  2051.  
  2052.               * The high byte of the effective address may be invalid
  2053.                 at this time, i.e. it may be smaller by $100.
  2054.  
  2055.  
  2056.      Write instructions (STA, SHA)
  2057.  
  2058.         #    address   R/W description
  2059.        --- ----------- --- ------------------------------------------
  2060.         1      PCR      R  fetch opcode, increment PCR
  2061.         2      PCR      R  fetch pointer address, increment PCR
  2062.         3    pointer    R  fetch effective address low
  2063.         4   pointer+1   R  fetch effective address high,
  2064.                            add Y to low byte of effective address
  2065.         5   address+Y*  R  read from effective address,
  2066.                            fix high byte of effective address
  2067.         6   address+Y   W  write to effective address
  2068.  
  2069.        Notes: The effective address is always fetched from zero page,
  2070.               i.e. the zero page boundary crossing is not handled.
  2071.  
  2072.               * The high byte of the effective address may be invalid
  2073.                 at this time, i.e. it may be smaller by $100.
  2074.  
  2075.  
  2076.   Absolute indirect addressing (JMP)
  2077.  
  2078.         #   address  R/W description
  2079.        --- --------- --- ------------------------------------------
  2080.         1     PCR     R  fetch opcode, increment PCR
  2081.         2     PCR     R  fetch pointer address low, increment PCR
  2082.         3     PCR     R  fetch pointer address high, increment PCR
  2083.         4   pointer   R  fetch low address to latch
  2084.         5  pointer+1* R  fetch PCH, copy latch to PCL
  2085.  
  2086.        Note: * The PCH will always be fetched from the same page
  2087.                than PCL, i.e. page boundary crossing is not handled.
  2088.  
  2089.  
  2090.  
  2091.                 MEMORY MANAGEMENT
  2092.  
  2093.        normal                                                   ultimax
  2094.         1111    101x    011x    001x    1110    0100    1100    xx01
  2095.                 1000            00x0
  2096. 10000
  2097. ------------------------------------------------------------------------
  2098.  F000
  2099.         Kernal  RAM     Kernal  RAM     Kernal  Kernal  Kernal  module
  2100.  E000
  2101. ------------------------------------------------------------------------
  2102.  D000   I/O     I/O**   I/O     RAM     I/O     I/O     I/O     I/O
  2103. ------------------------------------------------------------------------
  2104.  C000   RAM     RAM     RAM     RAM     RAM     RAM     RAM      -
  2105. ------------------------------------------------------------------------
  2106.  B000
  2107.         BASIC   RAM     RAM     RAM     BASIC   module  module   -
  2108.  A000
  2109. ------------------------------------------------------------------------
  2110.  9000
  2111.         RAM     RAM     RAM     RAM     module  RAM     module  module
  2112.  8000
  2113. ------------------------------------------------------------------------
  2114.  7000
  2115.  
  2116.  6000
  2117.         RAM     RAM     RAM     RAM     RAM     RAM     RAM      -
  2118.  5000
  2119.  
  2120.  4000
  2121. ------------------------------------------------------------------------
  2122.  3000    
  2123.  
  2124.  2000
  2125.         RAM     RAM     RAM     RAM     RAM     RAM     RAM      RAM    
  2126.  1000
  2127.     
  2128.  0000
  2129. ------------------------------------------------------------------------
  2130.  
  2131.                 **) Chargen not accessible by the CPU
  2132.  
  2133.  
  2134.                 AUTOSTART CODE
  2135.  
  2136.   If memory places $8004 to $8008 contain 'CBM80' (C3 C2 CD 38 30),
  2137.   the RESET routine jumps to ($8000) and the default NMI handler jumps to
  2138.   ($8002).
  2139.  
  2140.  
  2141.                 HOW REAL PROGRAMMERS ACKNOWLEDGE INTERRUPTS
  2142.  
  2143.   With RMW instructions:
  2144.  
  2145.         ; beginning of combined raster/timer interrupt routine
  2146.         LSR $D019       ; clear VIC interrupts, read raster interrupt flag to C
  2147.         BCS raster      ; jump if VIC caused an interrupt
  2148.         ...             ; timer interrupt routine
  2149.  
  2150.         Operational diagram of LSR $D019:
  2151.  
  2152.           #  data  address  R/W  
  2153.          --- ----  -------  ---  ---------------------------------
  2154.           1   4E     PCR     R   fetch opcode
  2155.           2   19    PCR+1    R   fetch address low
  2156.           3   D0    PCR+2    R   fetch address high
  2157.           4   xx    $D019    R   read memory
  2158.           5   xx    $D019    W   write the value back, rotate right
  2159.           6  xx/2   $D019    W   write the new value back
  2160.  
  2161.     The 5th cycle acknowledges the interrupt by writing the same
  2162.     value back. If only raster interrupts are used, the 6th cycle
  2163.     has no effect on the VIC.
  2164.  
  2165.  
  2166.   With indexed addressing:
  2167.  
  2168.         ; acknowledge interrupts to both CIAs
  2169.         LDX #$10
  2170.         LDA $DCFD,X
  2171.  
  2172.         Operational diagram of LDA $DCFD,X:
  2173.  
  2174.           #  data  address  R/W  description
  2175.          --- ----  -------  ---  ---------------------------------
  2176.           1   BD     PCR     R   fetch opcode
  2177.           2   FD    PCR+1    R   fetch address low
  2178.           3   DC    PCR+2    R   fetch address high, add X to address low
  2179.           4   xx    $DC0D    R   read from address, fix high byte of address
  2180.           5   yy    $DD0D    R   read from right address
  2181.  
  2182.  
  2183.         ; acknowledge interrupts to CIA 2
  2184.         LDX #$10
  2185.         STA $DDFD,X
  2186.  
  2187.         Operational diagram of STA $DDFD,X:
  2188.  
  2189.           #  data  address  R/W  description
  2190.          --- ----  -------  ---  ---------------------------------
  2191.           1   9D     PCR     R   fetch opcode
  2192.           2   FD    PCR+1    R   fetch address low
  2193.           3   DC    PCR+2    R   fetch address high, add X to address low
  2194.           4   xx    $DD0D    R   read from address, fix high byte of address
  2195.           5   ac    $DE0D    W   write to right address
  2196.  
  2197.  
  2198.   With branch instructions:
  2199.  
  2200.         ; acknowledge interrupts to CIA 2
  2201.                 LDA #$00  ; clear N flag
  2202.                 JMP $DD0A
  2203.         DD0A    BPL $DC9D ; branch
  2204.         DC9D    BRK       ; return
  2205.  
  2206.         You need the following preparations to initialize the CIA registers:
  2207.  
  2208.                 LDA #$91  ; argument of BPL
  2209.                 STA $DD0B
  2210.                 LDA #$10  ; BPL
  2211.                 STA $DD0A
  2212.                 STA $DD08 ; load the ToD values from the latches
  2213.                 LDA $DD0B ; jam the ToD display
  2214.                 LDA #$7F
  2215.                 STA $DC0D ; assure that $DC0D is $00
  2216.  
  2217.         Operational diagram of BPL $DC9D:
  2218.  
  2219.           #  data  address  R/W  description
  2220.          --- ----  -------  ---  ---------------------------------
  2221.           1   10    $DD0A    R   fetch opcode
  2222.           2   91    $DD0B    R   fetch argument
  2223.           3   xx    $DD0C    R   fetch opcode, add argument to PCL
  2224.           4   yy    $DD9D    R   fetch opcode, fix PCH
  2225.         ( 5   00    $DC9D    R   fetch opcode )
  2226.  
  2227.  
  2228.         ; acknowledge interrupts to CIA 1
  2229.                 LDA #$00  ; clear N flag
  2230.                 JMP $DCFA
  2231.         DCFA    BPL $DD0D
  2232.         DD0D    BRK
  2233.  
  2234.         ; Again you need to set the ToD registers of CIA 1 and the
  2235.         ; Interrupt Control Register of CIA 2 first.
  2236.  
  2237.         Operational diagram of BPL $DD0D:
  2238.  
  2239.           #  data  address  R/W  description
  2240.          --- ----  -------  ---  ---------------------------------
  2241.           1   10    $DCFA    R   fetch opcode
  2242.           2   11    $DCFB    R   fetch argument
  2243.           3   xx    $DCFC    R   fetch opcode, add argument to PCL
  2244.           4   yy    $DC0D    R   fetch opcode, fix PCH
  2245.         ( 5   00    $DD0D    R   fetch opcode )
  2246.  
  2247.  
  2248.         ; acknowledge interrupts to CIA 2 automagically
  2249.                 ; preparations
  2250.                 LDA #$7F
  2251.                 STA $DD0D       ; disable CIA 2's all interrupt sources
  2252.                 LDA $DD0E
  2253.                 AND #$BE        ; ensure that $DD0C remains constant
  2254.                 STA $DD0E       ; and stop the timer
  2255.                 LDA #$FD
  2256.                 STA $DD0C       ; parameter of BPL
  2257.                 LDA #$10
  2258.                 STA $DD0B       ; BPL
  2259.                 LDA #$40
  2260.                 STA $DD0A       ; RTI/parameter of LSR
  2261.                 LDA #$46
  2262.                 STA $DD09       ; LSR
  2263.                 STA $DD08       ; load the ToD values from the latches
  2264.                 LDA $DD0B       ; jam the ToD display
  2265.                 LDA #$09
  2266.                 STA $0318
  2267.                 LDA #$DD
  2268.                 STA $0319       ; change NMI vector to $DD09
  2269.                 LDA #$FF        ; Try changing this instruction's operand
  2270.                 STA $DD05       ; (see comment below).
  2271.                 LDA #$FF
  2272.                 STA $DD04       ; set interrupt frequency to 1/65536 cycles
  2273.                 LDA $DD0E
  2274.                 AND #$80
  2275.                 ORA #$11
  2276.                 LDX #$81
  2277.                 STX $DD0D       ; enable timer interrupt
  2278.                 STA $DD0E       ; start timer
  2279.  
  2280.                 LDA #$00        ; To see that the interrupts really occur,
  2281.                 STA $D011       ; use something like this and see how
  2282.         LOOP    DEC $D020       ; changing the byte loaded to $DD05 from
  2283.                 BNE LOOP        ; #$FF to #$0F changes the image.
  2284.  
  2285.         When an NMI occurs, the processor jumps to Kernal code, which jumps to
  2286.         ($0318), which points to the following routine:
  2287.  
  2288.         DD09    LSR $40         ; clear N flag
  2289.                 BPL $DD0A       ; Note: $DD0A contains RTI.
  2290.  
  2291.         Operational diagram of BPL $DD0A:
  2292.  
  2293.           #  data  address  R/W  description
  2294.          --- ----  -------  ---  ---------------------------------
  2295.           1   10    $DD0B    R   fetch opcode
  2296.           2   11    $DD0C    R   fetch argument
  2297.           3   xx    $DD0D    R   fetch opcode, add argument to PCL
  2298.           4   40    $DD0A    R   fetch opcode, (fix PCH)
  2299.  
  2300.  
  2301.   With RTI:
  2302.  
  2303.         ; the fastest possible interrupt handler in the 6500 family
  2304.                 ; preparations
  2305.         SEI
  2306.         LDA $01        ; disable ROM and enable I/O
  2307.         AND #$FD
  2308.         ORA #$05
  2309.         STA $01
  2310.                 LDA #$7F
  2311.                 STA $DD0D       ; disable CIA 2's all interrupt sources
  2312.                 LDA $DD0E
  2313.                 AND #$BE        ; ensure that $DD0C remains constant
  2314.                 STA $DD0E       ; and stop the timer
  2315.                 LDA #$40
  2316.                 STA $DD0C       ; store RTI to $DD0C
  2317.                 LDA #$0C
  2318.                 STA $FFFA
  2319.                 LDA #$DD
  2320.                 STA $FFFB       ; change NMI vector to $DD0C
  2321.                 LDA #$FF        ; Try changing this instruction's operand
  2322.                 STA $DD05       ; (see comment below).
  2323.                 LDA #$FF
  2324.                 STA $DD04       ; set interrupt frequency to 1/65536 cycles
  2325.                 LDA $DD0E
  2326.                 AND #$80
  2327.                 ORA #$11
  2328.                 LDX #$81
  2329.                 STX $DD0D       ; enable timer interrupt
  2330.                 STA $DD0E       ; start timer
  2331.  
  2332.                 LDA #$00        ; To see that the interrupts really occur,
  2333.                 STA $D011       ; use something like this and see how
  2334.         LOOP    DEC $D020       ; changing the byte loaded to $DD05 from
  2335.                 BNE LOOP        ; #$FF to #$0F changes the image.
  2336.  
  2337.         When an NMI occurs, the processor jumps to Kernal code, which jumps to
  2338.         ($0318), which points to the following routine:
  2339.  
  2340.         DD0C    RTI
  2341.  
  2342.         How on earth can this clear the interrupts? Remember, the processor
  2343.         always fetches two successive bytes for each instruction.
  2344.  
  2345.         A little more practical version of this is redirecting the NMI (or IRQ)
  2346.         to your own routine, whose last instruction is JMP $DD0C or JMP $DC0C.
  2347.         If you want to confuse more, change the 0 in the address to a
  2348.         hexadecimal digit different from the one you used when writing the RTI.
  2349.  
  2350.         Or you can combine the latter two methods:
  2351.  
  2352.         DD09    LSR $xx         ; xx is any appropriate BCD value 00-59.
  2353.                 BPL $DCFC
  2354.         DCFC    RTI
  2355.  
  2356.         This example acknowledges interrupts to both CIAs.
  2357.  
  2358.  
  2359.    If you want to confuse the examiners of your code, you can use any of
  2360.  these techniques. Although these examples use no undefined opcodes, they do
  2361.  not run correctly on CMOS processors. However, the RTI example should run on
  2362.  65C02 and 65C816, and the latter branch instruction example might work as
  2363.  well.
  2364.  
  2365.    The RMW instruction method has been used in some demos, others were
  2366.  developed by Marko M"akel"a. His favourite is the automagical RTI method,
  2367.  although it does not have any practical applications, except for some
  2368.  time dependent data decryption routines for very complicated copy protections.
  2369.  
  2370.  
  2371.  
  2372.                 MAKING USE OF THE I/O REGISTERS
  2373.  
  2374.    If you are making a resident program and want to make as invisible to the
  2375.  system as possible, probably the best method is keeping most of your code
  2376.  under the I/O area (in the RAM at $D000-$DFFF). You need only a short routine
  2377.  in the normally visible RAM that pushes the current value of the processor's
  2378.  I/O register $01 on stack, switches I/O and ROMs out and jumps to this area.
  2379.  Returning from the $D000-$DFFF area is easy even without any routine in the
  2380.  normally visible RAM area. Just write a RTS to an I/O register and return
  2381.  through it.
  2382.  
  2383.    But what if your program needs to use I/O? And how can you write the RTS
  2384.  to an I/O register while the I/O area is switched off? You need a swap area
  2385.  for your program in normally visible memory. The first thing your routine at
  2386.  $D000-$DFFF does is copying the I/O routines (or the whole program) to
  2387.  normally visible memory, swapping the bytes. For instance, if your I/O
  2388.  routines are initially at $D200-$D3FF, exchange the bytes at $D200-$D3FF
  2389.  with the contents of $C000-$C1FF. Now you can call the I/O routines from
  2390.  your routine at $D000-$DFFF, and the I/O routines can switch the I/O area
  2391.  temporarily on to access the I/O circuitry. And right before exiting your
  2392.  program at $D000-$DFFF swaps the old contents of that I/O routine area in,
  2393.  e.g. exchanges the memory areas $D200-$D3FF and $C000-$C1FF again.
  2394.  
  2395.    What I/O registers can you use for the RTS? There are two alternatives:
  2396.  8-bit VIC sprite registers or CIA serial port register. The CIA register is
  2397.  usually better, as changing the VIC registers might change the screen layout.
  2398.  However, also the SP register has some drawbacks: If the machine's CNT1 and
  2399.  CNT2 lines are connected to a frequency source, you must stop either CIA's
  2400.  Timer A to use the SP register method. Normally the 1st CIA's Timer A is the
  2401.  main hardware interrupt source. And if you use the Kernal's RS232, you cannot
  2402.  stop the 2nd CIA's Timer A either. Also, if you don't want to lose any CIA
  2403.  interrupts, remember that the RTS at SP register causes also the Interrupt
  2404.  Control Register to be read.
  2405.  
  2406.    Also keep in mind that the user could press RESTORE while the Kernal ROM
  2407.  and I/O areas are disabled. You could write your own NMI handler (using
  2408.  the NMI vector at $FFFA), but a fast loader that uses very tight timing
  2409.  would still stop working if the user pressed RESTORE in wrong time. So, to
  2410.  make a robust program, you have to disable NMI interrupts. But how is this
  2411.  possible? They are Non-Maskable after all. The NMI interrupt is
  2412.  edge-sensitive, the processor jumps to NMI handler only when the -NMI line
  2413.  drops from +5V to ground. Just cause a NMI with CIA2's timer, but don't
  2414.  read the Interrupt Control register. If you need to read $DD0D in your
  2415.  program, you must add a NMI handler just in case the user presses RESTORE.
  2416.  And don't forget to raise the -NMI line upon exiting the program. This can
  2417.  be done automatically by the latter two of the three following examples.
  2418.  
  2419.  
  2420.         ; Returning via VIC sprite 7 X coordinate register
  2421.  
  2422.         Initialization:   ; This is executed when I/O is switched on
  2423.                 LDA #$60
  2424.                 STA $D015 ; Write RTS to VIC register $15.
  2425.  
  2426.         Exiting:          ; NOTE: This procedure must start at VIC register
  2427.                           ; $12. You have multiple alternatives, as the VIC
  2428.                           ; appears in memory at $D000+$40*n, where $0<=n<=$F.
  2429.  
  2430.                 PLA       ; Pull the saved 6510 I/O register state from stack
  2431.                 STA $01   ; Restore original memory bank configuration
  2432.                           ; Now the processor fetches the RTS command from the
  2433.                           ; VIC register $15.
  2434.  
  2435.  
  2436.         ; Returning via CIA 2's SP register (assuming that CNT2 is stable)
  2437.  
  2438.         Initialization:   ; This is executed when I/O is switched on
  2439.                 LDA $DD0E ; CIA 2's Control Register A
  2440.                 AND #$BF  ; Set Serial Port to input
  2441.                 STA $DD0E ; (make the SP register to act as a memory place)
  2442.                 LDA #$60
  2443.                 STA $DD0C ; Write RTS to CIA 2 register $C.
  2444.  
  2445.         Exiting:          ; NOTE: This procedure must start at CIA 2 register
  2446.                           ; $9. As the CIA 2 appears in memory at $DD00+$10*n,
  2447.                           ; where 0<=n<=$F, you have sixteen alternatives.
  2448.                 PLA
  2449.                 STA $01   ; Restore original memory bank configuration
  2450.                           ; Now the processor fetches the RTS command from
  2451.                           ; the CIA 2 register $C.
  2452.  
  2453.  
  2454.         ; Returning via CIA 2's SP register, stopping the Timer A
  2455.         ; and forcing SP2 and CNT2 to output
  2456.  
  2457.         Initialization:   ; This is executed when I/O is switched on
  2458.                 LDA $DD0E ; CIA 2's Control Register A
  2459.                 AND #$FE  ; Stop Timer A
  2460.                 ORA #$40  ; Set Serial Port to output
  2461.                 STA $DD0E ; (make the SP register to act as a memory place)
  2462.                 LDA #$60
  2463.                 STA $DD0C ; Write RTS to CIA register $C.
  2464.  
  2465.         Exiting:          ; NOTE: This procedure must start at CIA 2 register
  2466.                           ; $9. As the CIA 2 appears in memory at $DD00+$10*n,
  2467.                           ; where, 0<=n<=$F, you have sixteen alternatives.
  2468.                 PLA
  2469.                 STA $01   ; Restore original memory bank configuration
  2470.                           ; Now the processor fetches the RTS command from
  2471.                           ; the CIA 2 register $C.
  2472.  
  2473.         
  2474.    For instance, if you want to make a highly compatible fast loader, make
  2475.  the ILOAD vector ($0330) point to the beginning of the stack area. Remember
  2476.  that the BASIC interpreter uses the first bytes of stack while converting
  2477.  numbers to text. A good address is $0120. Robust programs practically never
  2478.  use so much stack that it could corrupt this routine. Usually only crunched
  2479.  programs (demos and alike) use all stack in the decompression phase. They
  2480.  also make use of the $D000-$DFFF area.
  2481.  
  2482.    This stack routine will jump to your routine at $D000-$DFFF, as described
  2483.  above. For performance's sake, copy the whole byte transfer loop to the swap
  2484.  area, e.g. $C000-$C1FF, and call that subroutine after doing the preliminary
  2485.  work. But what about files that load over $C000-$C1FF? Wouldn't that destroy
  2486.  the transfer loop and jam the machine? Not necessarily. If you copy those
  2487.  bytes to your swap area at $D000-$DFFF, they will be loaded properly, as
  2488.  your program restores the original $C000-$C1FF area.
  2489.  
  2490.    If you want to make your program user-friendly, put a vector initialization
  2491.  routine to the stack area as well, so that the user can restore the fast
  2492.  loader by issuing a SYS command, rather than loading it each time he has
  2493.  pressed STOP & RESTORE or RESET.
  2494.  
  2495.  
  2496.  
  2497.                 NOTES
  2498.  
  2499.  See MCS 6500 Microcomputer Family Programming Manual for more information.
  2500.  There is also a table showing functional description and timing for complete
  2501.  6510 instruction set on C=Hacking magazine issue 1/92 (available via FTP at
  2502.  ccosun.caltech.edu:/pub/rknop/hacking.mag/ and
  2503.  nic.funet.fi:/pub/cbm/c=hacking/).
  2504.  
  2505.  
  2506.  References:
  2507.   C64 Memory Maps       C64 Programmer's Reference Guide pp. 262-267
  2508.   6510 Block Diagram    C64 Programmer's Reference Guide  p. 404
  2509.   Instruction Set       C64 Programmer's Reference Guide pp. 416-417
  2510.                         C=Hacking Volume 1, issue #1, 1/92
  2511.                         C=Lehti magazine 4/87
  2512. =============================================================================
  2513. A Heavy-Duty Power Supply for the C-64
  2514. by John C. Andrews (no email address)
  2515.  
  2516.   As a Commodore User for the last 4 plus years, I am aware of the many
  2517.   articles and letters in the press that have bemoaned the burn-out
  2518.   problem of the C-64 power supply. When our Club BBS added a one meg
  2519.   drive and stayed on around the clock, the need for heavy-duty power
  2520.   supply became very apparent.... Three power supplies went in 3
  2521.   successive days!
  2522.  
  2523.   Part of the problem was my ignoring the seasons. You see during the
  2524.   winter I had set the power supply between the window and the screen,
  2525.   Yes, outside! With the advent of Spring... well, you get the picture.
  2526.  
  2527.   The turn-around time forgetting a new commerical supply was not in the
  2528.   best interest of the BBS and its members. Therefore, taking power
  2529.   supply inhand, I proceeded to cut one open on my shop bandsaw. I do
  2530.   not suggest that you do this. The parts are FIRMLY and COMPLETELY
  2531.   encased in a hard plastic potting compound. The purpose of this is not
  2532.   to make the item difficult to repair, but to make the entire unit
  2533.   conductive to the heat generated inside. I doubt the wisedom of
  2534.   potting the fuse as well. However, CBM was probably thinking of the
  2535.   number of little fingers that could fit into an accessable fuse
  2536.   holder. if you want the punch line it is: the final circuit board and
  2537.   its componets are about the size of a box of matches. This includes
  2538.   the built-in metal heat sink.
  2539.  
  2540.   From these minscule innards I traced out the circuit and increased the
  2541.   size of ALL components.
  2542.  
  2543.   The handiest source of electronic parts is, of course, Radio Shack.
  2544.   All but one part can be purchased there.
  2545.  
  2546.         212-1013        Capacitor, 35V, 4700 mF
  2547.         212-1022        Capacitor, 35V, 10 uF
  2548.         273-1515        Transformer, 2 Amp, 9-0-9 VAC
  2549.         276-1184        Rectifier
  2550.  
  2551.         270- 742        Fuse Block
  2552.         270-1275        Fuses
  2553.  
  2554.   Note that there are only five parts. The rest are fuses, fuse blocks,
  2555.   heat sinks, wire and misc. hardware. Note also that I have not listed
  2556.   any plugs and cords. This because you can clip the cords off of both
  2557.   sides of your defunct power supply. This will save you the hassle of
  2558.   wriing the DIN power plug correctly:
  2559.  
  2560.         DIN PIN OUT                   COLOR
  2561.           pin 6         9VAC            black
  2562.           pin 7         9VAC            black
  2563.           pin 5         +5 Volts        blue
  2564.           pin 1,2,3     shield, gnd     orange
  2565.  
  2566.   The part that you can NOT get at Radio Shack is the power regulator.
  2567.   This part will have to be scrounged up from some local, big
  2568.   electronics supply house:
  2569.  
  2570.         SK 9067    5 volt voltage regulator, 3+ amps. (I prefer the 5 amp.)
  2571.  
  2572.   Radio Shack does carry regulators, but their capacity is no larger
  2573.   than that with which you started.
  2574.  
  2575.   The Heat sinks, (yes, more than one!) are the key to the success of
  2576.   this project. The ones I used came from my Model Railroading days.
  2577.   Sorry to say, I did just ahve them 'lying about'. The heat sinks that
  2578.   I priced at the local electronics supply were more costly than the
  2579.   other parts. The worst case situation is that you may need to drill
  2580.   out a couple pieces of aluminum sheet. Try for 12 x 12, and bend them
  2581.   into square bottomed U-shapes to save room. heat sinks should not
  2582.   touch, or be electronically grounded to each other. You can also mount
  2583.   them on stand-offs from your chassis for total air circulation.
  2584.  
  2585.   The Radio Shack transformer is rated at only 2 amps. If you can not
  2586.   find one with a higher rating elsewhere, it is possible to hook two in
  2587.   parallel to get a 4 ampere output. This si tricky, as it can be done
  2588.   either right or wrong!
  2589.  
  2590.   Here is how to do it the right way:
  2591.     Tape off one yellow secondary lead on each transformer. With tape
  2592.     mark the four remaining secondary leads and letter them A and B on
  2593.     one transformer, C and D onthe other. Hook up the black primary
  2594.     leads to a plug to your 120 wall outlet:
  2595.  
  2596.                                     |-------------
  2597.    Note: *'s - indicate connections |            3 ||
  2598.          +'s - indicate skip overs  |            3 || (Transformer)
  2599.                                     |            3 ||
  2600.                                     |            3 ||
  2601.                                     |   ----------
  2602.                                     |   |
  2603.           +--\  /-------------------*---+---------
  2604.         --|120|/                        |        3 ||
  2605.         --|Vlt|             ____        |        3 ||
  2606.          -|Plg|------------|FUSE|-------*        3 ||
  2607.           +--/              ----        |        3 || (Transformer)
  2608.                                         |---------
  2609.  
  2610.     This would now be a good time to install a fuse in your 120 VAC
  2611.     line. Now before plugging this into the wall, tie two of the
  2612.     scondary leads (one from EACH transformer) together.
  2613.  
  2614.     Something like this: A--Xfmr--B+C--Xfmr--D
  2615.  
  2616.     Plug in your 120V side. Now using a VOM meter, measure the voltage
  2617.     between A and D.
  2618.       If the meter reads 18 volts, then:
  2619.         1. unplug from the 120.
  2620.         2. tie A and C together. tie B and D together.
  2621.         3. your 2 transformers will now give you 9 volts at 4 amps.
  2622.       If the meter reads 0 volts, then:
  2623.         1. unplug from the 120.
  2624.         2. tie A and D together. Tie B and C together.
  2625.         3. your 2 transformers will now give you 9 volts at 4 amps.
  2626.  
  2627.   Below is the file corresponding to the full schematic of the power
  2628.   supply. [Ed's note: in GeoPaint format, converted, then uuencoded]. As
  2629.   you can see in the picture, I used only one transformer. Because it
  2630.   got hot, I epoxied a small heat sink to it. While this solved the heat
  2631.   problem, it did not increase the capacity of the total power supply.
  2632.  
  2633.   Note that I used fuses on all lines.
  2634.  
  2635. -----------------------------------------------------------------------------
  2636. begin 700 schematic.
  2637. M@PD,<V-H96UA=&ECH*"@H*"@H`D&`0=8!P8-,Q(`4%)'(&9O<FUA='1E9"!'
  2638. M14]3(&9I;&4@5C$N,``CF````"```.``0X(``$5P<V]N($U8+3@P`*"@H*``
  2639. MUH>-UH?(F!AE`H4"D`+F`V"@H*"@H*"@H`@%`0A6!`<,`"````"""@A30TA%
  2640. M34%424.@H*"@H*"@````````````$P!"3$%35$52)U,@0T].5D525$52(%8R
  2641. M+C4$6`<X"````@RP#0T].5D525*"@H*"@H*"@H"P2``99`Q$&`10`````
  2642. M```````````````````````````````````````#%;_____```.@``6?__F5
  2643. M55F:JJF555F:JJF555F:JJF555F:JJF?__F@``7```/___\```````-__[:`
  2644. M`/Y__[R#!P$``/__``!086EN="!);6%G92!6,2XQ````````````````````
  2645. M````````````9V5O4&%I;G0@("`@5C(N,``````@*$')!M`"J1*-(D"I`(TG
  2646. M0"#]/Y`%J0"-)T`@#!\@2$&I`(VL7ZD`A1&I!X40J3^%%ZGQA1:I7X4-J:R%
  2647. M#*!`J0X@3S&B_Z4"R0+P)LG_T`8@-$&X4*G)!M`-H$.I)B!/,2"APKA0F*VL
  2648. M7_`&(+T\(($\8%!A:0#_"@#_`3P!>`)+`EL"/P&R`38!B@%Z`38!C@(G`/\`
  2649. M_P#_`/\`_P#_`/\`_P#_`/\`_P#_`/\`_P#_`/\`_P#_`/\`_P#_`/\`_P#_
  2650. M`/\`_P#_`/\`_P#_`/\`````````````````````````````````````````
  2651. M````````````````````````````````````````````````````````````
  2652. M````````````````````````````````````````````````````````````
  2653. M`````````````````````````````````````````````````````````/\`
  2654. M_P#_`/\`_P#_`/\`E0`"/R!%````````_P"%``,0_Q!$````````_P"&``+X
  2655. M"/\`_P"B`/^_H;\``"`@,!@,_PP810``````_P``"!`0,&#@_\#`0P``````
  2656. M_P``A1`#\!`0H`"("/\`_P#*``(@/X8``>#_`+```3"'(*@``F`PAA"8`(@0
  2657. MH`"("/\`_P"B`/^_H;\`(*@`B!"8`(00!!\0$!!$`````/\```"$"`3X"`@(
  2658. M_P#_`,(``O__A@`"X."&((L`#0$!`0($@("`#A$0$!.%``-98D.%``.,4MZ%
  2659. M``/@D)"0``$'0P````````#_AP`!@,\`B""H_P#.``H#1$0HA0`##03$A0`#
  2660. M@("9AP`!!*``B""H`(@0H`"("/\`_P#_`.,`'2D1$1$0````).0$),0```"E
  2661. MI*2DF`````2HJ%!0HP"&(`)@8*(``1^$``P!$!#_````/-,``/B$``&`F`"(
  2662. M"/\`_P"B`/^_H;\``+```3"'(*@``F`PAA"8`(@0H`"("/\`_P"B`/^_H;\`
  2663. M(*@`B!"8`(00!!\0$!!$`````/\```"$"`3X"`@(_P#_`,(``O__A@`"X."&
  2664. M((L`#0$!`0($@("`#A$0$!.%``-98D.%``.,4MZ%``/@D)"0``$'0P``````
  2665. M``#_AP`!@,\`B""HS@`""`B&``(*BH8``@D!O@`"#PA#````````_P"&``R`
  2666. M8``/##`P#`PP`/^%``,\`/^%``/``/^%``$$1`#_`````````P#_`84`"L#_
  2667. M@,#@8"`@`/^'``'PAA"0``(&"(8`A!`,$A(2$V`0```\!(3.A``$<8J*BH0`
  2668. M#,`@("<````X*"!P((<(`0^'``&`_P#_`,<``S\@((8``N`<B``1!`4%`@("
  2669. M``"34E(B(B(``)N$20E(``"8)#P@))BR`(@(D0`Q`0$```$!`&"%A65EA85E
  2670. M,`P,,#`,##`B(CPB(B(\`$!,4DY24DX`!&25AH:59```@(0``8"A`(@@A0`+
  2671. M`0(*!!`0.$2"`0"`A@`"@$"0`(@0`H2$A@`"BG&&``(JRH8`#*"@`0$#`P4%
  2672. M,,```(0%"&`8!`3R"@D1_P#_`)H`_[^AOP``````````````````````````
  2673. M````````````````````````````````````````````````````````````
  2674. M````````````````````````````````````````````````````````````
  2675. M````````````````````````````````````````````````````````````
  2676. M``````````````````````````````````"<``(!`T(```````#__P(``(0@
  2677. M!N#@("`#`88`"/^`0"`0"`0$2O\``````````?B8`!<!`0```0$`986%966%
  2678. MA64P#`\P,`P,,$8``/\```````,``/Z%`H@``3^'`!3X"P0"`0```(#G@(`!
  2679. M@D0X(+]`@$(``````/\``(0``A#_AA!#`/\````````8!?P$`@(!`0"-B8G9
  2680. M<7$`P.$A(2(2#`08_P#_`*T``@$#0@```````/__"P```"`@(.#@("`@B``$
  2681. M`@$!`8@`'("`@$```'E$1'A$1```@("8I9VE```(",DJ#`S)`#<!`0```0$`
  2682. M986%966%A64P#`PP,`P,,`!$"D0H*1$1$0`-!,0DY`0D`("`F:6DI*0````$
  2683. M!*BH4)```0-#`````````/^'``'PAQ"8`(@0CP`!#(<``0B(``(X#X8(`F"`
  2684. M_P#_`*``_[^AOP``````````````````````````````````````````````
  2685. M````````````````````````````````````````````````````````````
  2686. M````````````````````````````````````````````````````````````
  2687. M`````````````````````````````````````````````````````+``B""0
  2688. M`(5`%7]`0$1X````_P``I9P```#_```JR4(```#_``````<```#_```'A`0;
  2689. M_`0$_P`],3TQ,`#_`+.VL['G`/\`O#`\L#P`A8`#_X"`0@``````_P``-0`!
  2690. M`0``_P``986%8&"````P#`PP/P```!````#_````Q````/\```"8````_P``
  2691. M`%````#_AP`$X"`@(*@`B!"8`(00!!\0$!"$`!3_`````@$!`/\``0$("`B(
  2692. MCX@("(0`!/\```"$"`3X"`@(_P#_`,(``O__A@`"X."&((L`#0$!`0($@("`
  2693. M#A$0$!.%``-98D.%``.,4MZ%``/@D)"0``$'0P````````H`_X<``8#/`(@@
  2694. MJ`"($)@`B!"(``L"#```GJ&AH0@("(D`!&"PD("("/\`_P"B`/^_H;\```$,
  2695. MAP`!"(@``C@/A@@"8(#_`/\`H`#_OZ&_````````````````````````````
  2696. M````````````````````````````````````````````````````````````
  2697. M````````````````````````````````````````````````````````````
  2698. M````````````````````````````````````````````````````````````
  2699. M````````````L`"(((4`)@$"'`0($"!`_P``$1$.``#_``!"0D$``/\``!!2
  2700. MC```_P``D)"04```_P``````#0``_P``("`P&`S_#!A%``````#_```($!`P
  2701. M8.#_P,!#``````#_``"%$`/P$!"(``2AH:&>A``)`2(B(AP```#`A(`#````
  2702. MB`C_`/\`R@`"(#^&``'@_P"P``,P(`"%(*@``F`PAA"8`(@0H`"("/\`_P"B
  2703. M`/^_H;\`_P`!`0@("(B/B`@(A``$_P```(0(!/@("`C_`/\`P@`"__^&``+@
  2704. MX(8@BP`-`0$!`@2`@(`.$1`0$X4``UEB0X4``XQ2WH4``^"0D)#_`.D`B""H
  2705. M`(@0F``*B!"@`(@(_P#_`/\`_P"$`(@@J`"($)@`B!"@`(@(_P#_`*(`_[^A
  2706. MOP``#0``_P``("`P&`S_#!A%``````#_```($!`P8.#_P,!#``````#_``"%
  2707. M$`/P$!"(``2AH:&>A``)`2(B(AP```#`A(`#````B`C_`/\`R@`"(#^&``'@
  2708. M_P"P``,P(`"%(*@``F`PAA"8`(@0H`"("/\`_P"B`/^_H;\`_P`!`0@("(B/
  2709. MB`@(A``$_P```(0(!/@("`C_`/\`P@`"__^&``+@X(8@BP`-`0$!`@2`@(`.
  2710. M$1`0$X4``UEB0X4``XQ2WH4``^"0D)#_`.D`B""H`(@0F`"($*``B`C_`/\`
  2711. M_P#S``$#AP(8_P``>V-[8V'_``!G;&9CSO\``'A@>&!XB("8`(@0B`"(`1G_
  2712. M```],3TQ,/\``+.VL['G_P``O#`\L#S`AT")``,?$!"$$Q@(_P``VQO;&P#_
  2713. M```[8S,;`/P$!,0$Q`3_`/\`D@#_OZ&_`(8``>#_`+```S`@`(4@J``"8#"&
  2714. M$)@`B!"@`(@(_P#_`*(`_[^AOP#_``$!"`@(B(^("`B$``3_````A`@$^`@(
  2715. M"/\`_P#"``+__X8``N#@AB"+``T!`0$"!("`@`X1$!`3A0`#66)#A0`#C%+>
  2716. MA0`#X)"0D/\`V0`#`@(#0@``````"@``_X40`P``_X4``X"`@)T`B!"(``,!
  2717. M`0%"`````````/^%"`,``/^%``-`0,"-``03$!`?A``$#@``_X0$!',``/^$
  2718. M``3$!`3\_P#_`/\`]P"($*@`B!"8`(@(H`"(!/\`_P"B`/^_H;\`!,0$Q`3_
  2719. M`/\`D@#_OZ&_`(8``>#_`+```S`@`(4@J``"8#"&$)@`B!"@`(@(_P#_`*(`
  2720. M_[^AOP#_``$!"`@(B(^("`B$``3_````A`@$^`@("/\`_P#"``+__X8``N#@
  2721. MAB"+``T!`0$"!("`@`X1$!`3A0`#66)#A0`#C%+>A0`#X)"0D/\`Z0"($*@`
  2722. MB!"8`(@(H`"(!/\`_P#_`/\`A`"($*@`B!"8`(@(H`"(!/\`_P"B`/^_H;\`
  2723. M_X4``T!`P(T`!!,0$!^$``0.``#_A`0$<P``_X0`!,0$!/S_`/\`_P#W`(@0
  2724. MJ`"($)@`B`B@`(@$_P#_`*(`_[^AOP`$Q`3$!/\`_P"2`/^_H;\`A@`!X/\`
  2725. ML``#,"``A2"H``)@,(80F`"($*``B`C_`/\`H@#_OZ&_`/\``0$("`B(CX@(
  2726. M"(0`!/\```"$"`3X"`@(_P#_`,(``O__A@`"X."&((L`#0$!`0($@("`#A$0
  2727. M$!.%``-98D.%``.,4MZ%``/@D)"0_P#I`(@0J`"($)@`B`B@``J(!/\`_P#_
  2728. M`/@``P,$!(4``X%!0(00!``1$:*%``,.$9"4``0'"`@(A``,`H*!@1`0$``B
  2729. M(D5%A``$'"(@((<`"P,````?$!`>P0@(B0`-<HN#@IH````N*2BHJ(4`$X"`
  2730. M@`````$!!P$!`!\0$![!`1'_`/\`H@#_OZ&_`/\`L``#,"``A2"H``)@,(80
  2731. MF`"($*``B`C_`/\`H@#_OZ&_`/\``0$("`B(CX@("(0`!/\```"$"`3X"`@(
  2732. M_P#_`,(``O__A@`"X."&((L`#0$!`0($@("`#A$0$!.%``-98D.%``.,4MZ%
  2733. M``/@D)"0_P#9``P$`P```P```$#`0("$``VBI$=$1````)!0T%%.DP`*!P`!
  2734. M!@````^!@(4`$1!(CXB(`````Z"@HIP```#@A@`C`P(!$0X```#$(```("!`
  2735. M````BHIR````0<)H:2X```#!(("%``+P((0`#`<$!`0.````B$!;2H0`!`$!
  2736. M@4&$``3P``#@_P#_`/\`XP`3!P0$!`<$!`2(0%M*B@H*"@``@81!"4!@@`#@
  2737. M$!`0X)``"P@("`\("`@`@+>4A!0#````A8`:`"`@0$"`@(```@(#`@("```M
  2738. M)<4%!04``,"%(!X``$!`0$%"2P@0($"```'D!`A`X!`0$.````<$!`2$``2*
  2739. >"@H*A``$0$`*04"$``00$!#@_P#_`)8`_[^AOP`*
  2740. `
  2741. end
  2742.  
  2743. =============================================================================
  2744. LZW Compression
  2745. by Bill Lucier (Blucier@ersys.edmonton.ab.ca, b.lucier1 on Genie)
  2746.  
  2747. LZW is perhaps the most widely used form of data compression today. It
  2748. is simple to implement and achieves very decent compression at a fairly
  2749. quick pace. LZW is used in PKZIP (shrink),PKARC (crunch), gifs,V.42bis
  2750. and unix's compress. This article will attempt to explain how the
  2751. compression works with a short example and 6502 source code in Buddy
  2752. format.
  2753.  
  2754. Originally named lz78, it was invented by Jacob Ziv and Abraham Lempel
  2755. in 1978 , it was later modified by Terry Welch to its present format.
  2756. The patent for the LZW compression method is presently held by Unisys.
  2757.  
  2758. LZW compresses data by taking phrases and compressing them into codes.
  2759. The size of the codes could vary from 9 bits to 16 bits. Although for
  2760. this implementation we will be using only 12 bits. As byte are read in
  2761. from a file they are added to a dictionary. For a 12-bit implementation
  2762. a dictionary will be 4k (2^12=4096) . Each entry in the dictionary
  2763. requires five bytes, which will be built in the form of a tree. It is
  2764. not a binary tree because each node may have more than two offsprings.
  2765. In fact, because our dictionary can hold up to 4096 different codes it
  2766. is possible to have one node with 3800 children nodes, although this is
  2767. not likely to happen. The five bytes that make up our tree will be:
  2768.  
  2769. The parent code: Each node has one and only one parent node. When the parent
  2770.                  code is less then 255 it is the end of a phrase. The codes
  2771.                  0-255 do not actually exist in the tree. The following
  2772.                  values do not appear either as they have special meaning:
  2773.  
  2774.                  256 : End of Stream-This marks the end of one compressed file
  2775.                  257 : This tells the decompressor to increase the number
  2776.                        of bits its reading by one.
  2777.                  258 : Wipe out dictionary
  2778.                  
  2779. The code value : This is the code that will be sent to the compressor. 
  2780. The character  : The value contained at this node. It have a value of 0-255.
  2781.  
  2782. Initially we send out codes that are 9 bits long, this will cover the values
  2783. 0-511. Once we have reached 511, we will need to increase the number of
  2784. bits to write by 1. This will give room for code numbers 512-1023, or
  2785. (2^10)-1. At this point we must ensure that the decompressor knows how
  2786. bits to read in at once so a code number 257 is sent to indicate that
  2787. the number of bits to be read is to be bumped up by one. The size of the
  2788. dictionary is finite so at some point we do have to be concerned with
  2789. what we will do when it does fill up. We could stop compiling new
  2790. phrases and just compress with the ones that are already in the
  2791. dictionary. This is not a very good choice, files tend to change
  2792. frequently (eg. program files as they change from code to data) so
  2793. sticking with the same dictionary will actually increase the size of the
  2794. file or at best, give poor compression. Another choice is to wipe the
  2795. dictionary out and start building new codes and phrases, or wipe out
  2796. some of the dictionary leaving behind only the newer codes and phrases.
  2797. For the sake of simplicity this program will just wipe out the
  2798. dictionary when it becomes full.
  2799.  
  2800. To illustrate how LZW works a small phrase will be compressed : heher.
  2801. To start the first two characters would be read in. The H would be
  2802. treated as the parent code and E becomes the character code. By means of
  2803. a hashing routine (the hashing routine will be explained more fully in
  2804. the source code) the location where HE should be is located. Since we
  2805. have just begun there will be nothing there,so the phrase will be added
  2806. to the dictionary. The codes 0-258 are already taken so we start using
  2807. 259 as our first code. The binary tree would look something like this:
  2808.             
  2809.           node # 72 - H
  2810.                  |
  2811.      node #3200 259 - E
  2812.  
  2813.  The node # for E is an arbitrary one. The compressor may not choose
  2814. that location, 3200 is used strictly for demonstration purposes. So at
  2815. node #3200 the values would be:
  2816.  
  2817. Parent code - 72
  2818. code value  - 259
  2819. character   - E
  2820.  
  2821. The node #72 is not actually used. As soon as a value less than 255 is
  2822. found it is assumed to be the actual value. We can't compress this yet
  2823. so the value 72 is sent to the output file(remember that it is sent in 9
  2824. bits). The E then becomes the parent code and a new character code ( H )
  2825. is read in. After again searching the dictionary the phrase EH is not
  2826. found. It is added to the dictionary as code number 260. Then we send
  2827. the E to the disk and H becomes the new parent code and the next E
  2828. becomes the new character code. After searching the dictionary we find
  2829. that we can compress HE into the code 259,we want to compress as much as
  2830. possible into one code so we make 259 the parent code. There may be a
  2831. longer string then HE that can be compressed. The R is read in as the
  2832. new character code. The dictionary is searched for the a 259 followed a
  2833. R, since it is not found it is added to the dictioary and it looks like
  2834. this:
  2835.  
  2836.            node #72 - H             node #69 - E
  2837.                  |                        | 
  2838.     node #3200  259 - E    node #1600    260 - H
  2839.                  |
  2840.     node #1262  261 - R
  2841.  
  2842. Then the value 259 is sent to the output file (to represent the HE) and
  2843. since that is the EOF the R is sent as well,as well as a 256 to indicate
  2844. the EOF has been reached.
  2845.  
  2846. Decompression is extremely simple. As long as the decompressor maintains
  2847. the dictionary as the compressor did, there will be no problems,except
  2848. for one problem that can be handled as an exceptional case. All of the
  2849. little details of increasing the number of bits to read, and when to
  2850. flush the dictionary are taken care of by the compressor. So if the
  2851. dictionary was increased to 8k, the compressor would have to be set up
  2852. to handle a larger dictionary, but the decompressor only does as the
  2853. compressed file tells it to and will work with any size dictionary. The
  2854. only problem would be that a larger dictionary will creep into the ram
  2855. under the rom or possibly even use all available memory, but assuming
  2856. that the ram is available the decompressor will not change. The
  2857. decompressor would start out reading 9 bits at a time, and starts it
  2858. free code at 259 as the compressor did. To use the above input from the
  2859. compressor as an example, the output was:
  2860.  
  2861. 72  - For the First H
  2862. 69  - For the First E
  2863. 259 - For the Compressed HE
  2864. 82  - For the R
  2865. 256 - Eof indicator
  2866.  
  2867.  To begin decompressing, two values are needed. The H and E are read in,
  2868. (note they will both be 9 bits long). As they are both below 256 they
  2869. are at the end of the string and are sent straight to the output file.
  2870. The first free code is 259 so that is the value assigned to the phrase
  2871. HE. Note when decompressing there is no need for the hashing routine,
  2872. the codes are the absolute locations in the dictionary (i.e. If the
  2873. dictionary was considered to be an array then the entry number 259 would
  2874. be dictionary[259]), because of this, the code value is no longer
  2875. needed. So the decompressor would have an entry that looks like this:
  2876.  
  2877. Node # 259
  2878. Parent Code - H
  2879. Character   - E
  2880.  
  2881. The decompressor will read in the next value (259). Because the node
  2882. number is at the end of the compressed string we will have to take the
  2883. code value and place it on a stack, and take them off in a
  2884. Last-in,First-out (LIFO) fashion. That is to say that the first
  2885. character to go on the stack (in this case the E) will be the last to
  2886. come off. The size of the stack is dependent on the size of the
  2887. dictionary, so for this implementation we need a stack that is 4k long.
  2888. After all the characters from the string have been placed on the stack
  2889. they are taken off and sent to the outputfile.
  2890.  
  2891.   There is one small error that is possible with LZW because of the way
  2892. the compressor defines strings. Consider the compression dictionary that
  2893. has the following in it:
  2894.  
  2895.      node #   Code         Parent  character 
  2896.               Value         code 
  2897.      ------   ------        ------  ---------
  2898.         65      65           n/a       A 
  2899.        723     259            65       C
  2900.       1262     260           259       U
  2901.       2104     261           260       T
  2902.       2506     262           261       E
  2903.  
  2904. Now if the compressor was to try to compress the string ACUTEACUTEA The
  2905. compressor will find a match for the first five characters 'ACUTE' and
  2906. will send a 262 to the output file. Then it will add the following entry
  2907. to the dictionary:
  2908.  
  2909.       3099     263           262       A
  2910.  
  2911. Now it will try to compress the remaining characters, and it finds that
  2912. it can compress the entire string with the code 263, but notice that the
  2913. middle A, the one that was just added onto the end of the string 'ACUTE'
  2914. was never sent to the output file. The decompressor will not have the
  2915. code 263 defined in it's dictionary. The last code it will have defined
  2916. will be 262. This problem is easily remedied though, when the
  2917. decompressor does not have a code defined, it takes the first letter
  2918. from the last phrase defined and tacks it onto the end of the last
  2919. phrase. IE It takes the first letter (the A) from the phrase and adds it
  2920. on to the end as code #263.
  2921.  
  2922. This particular implementation is fairly slow because it reads a byte
  2923. and then writes one, it could be made much faster with some buffering.
  2924. It is also limited to compressing and decompressing one file at a time
  2925. and has no error checking capabilities. It is meant strictly to teach
  2926. LZW compression, not provide a full fledged compressor.
  2927.  
  2928. And now for the code:
  2929.   
  2930. SYS 4000      ; sys 999 on a 64
  2931. .DVO 9        ; or whatever drive used for output
  2932. .ORG 2500
  2933. .OBJ "LZW.ML"
  2934.  
  2935. TABLESIZE =5021     
  2936.  
  2937. ; THE TABLESIZE IS ACTUALLY 5021, ABOUT 20% LARGER THEN 4K. THIS GIVES
  2938. ; THE HASHING ROUTINE SOME ROOM TO MOVE. IF THE TABLE WAS EXACTLY 4K
  2939. ; THERE WOULD BE FREQUENT COLLISIONS WHERE DIFFERENT COMBINATIONS OF
  2940. ; CHARACTERS WOULD HAVE THE SAME HASH ADDRESS. INCREASING THE TABLE SIZE
  2941. ; REDUCES THE NUMBER OF COLLISIONS.
  2942.  
  2943. EOS =256          ; eos = End of stream This marks the end of file
  2944.  
  2945. FIRSTCODE =259
  2946. MAXCODE =4096
  2947.  
  2948. BUMPCODE =257     ; Whenever a 257 is encountered by the decompressor it
  2949.                   ; increases the number of bits it reads by 1
  2950.  
  2951. FLUSHCODE =258   
  2952.  
  2953. TABLEBASE =14336  ; The location that the dictionary is located at
  2954.  
  2955. DECODESTACK =9300 ; The location of the 4k LIFO stack
  2956.  
  2957. ; ORG = DECOMPRESS FILE
  2958. ; ORG + 3 = COMPRESS FILE
  2959.  
  2960. JMP EXPANDFILE
  2961.  
  2962. ;********************************
  2963. ; COMPRESSFILE
  2964. ;********************************
  2965.  
  2966. COMPRESSFILE JSR INITDIC ; EMPTY THE DICTIONARY
  2967. LDA #128
  2968. STA BITMASK
  2969. LDA #0
  2970. STA RACK
  2971. JSR GETCHAR      ; GET A CHAR FROM THE INPUT FILE
  2972. STA STRINGCODE   ; INITIALIZE THE STRINGCODE (PARENT CODE)
  2973. LDA #0
  2974. STA STRINGCODE+1
  2975. NEXTCHAR JSR GETCHAR
  2976.          STA CHARACTER
  2977.          JSR FINDNODE    ; FINDNODE CALCULATES THE HASHED LOCATION OF
  2978.          LDA ($FE),Y     ; THE STRINGCODE AND CHARACTER  IN THE DICT.
  2979.          INY             ; AND SETS $FE/$FF POINTING TO IT. IF THE ENTRY
  2980.          AND ($FE),Y     ; HAS TWO 255 IN IT THEN IT IS EMPTY AND SHOULD
  2981.          CMP #255        ; BE ADDED TO THE DICTIONARY.
  2982.          BEQ ADDTODICT
  2983.              LDA ($FE),Y     ; IT HAS A DEFINED PHRASE. STORE THE CODE VALUE IN
  2984.              STA STRINGCODE+1; THE PARENT CODE
  2985.              DEY
  2986.              LDA ($FE),Y
  2987.              STA STRINGCODE
  2988.          JMP EOF
  2989.          ADDTODICT LDY #0
  2990.          - LDA NEXTCODE,Y
  2991.            STA ($FE),Y
  2992.            INY
  2993.            CPY #5
  2994.          BNE -
  2995.          INC NEXTCODE             ; INCREASE THE NEXTCODE
  2996.          BNE +
  2997.              INC NEXTCODE+1
  2998.          + JSR OUTPUT
  2999.          LDA NEXTCODE+1          ; CHECK IF NEXTCODE=4096 IF SO THEN FLUSH THE
  3000.          CMP #>MAXCODE           ; DICTIONARY AND START ANEW
  3001.          BNE CHECKBUMP
  3002.          LDA NEXTCODE
  3003.          CMP #<MAXCODE
  3004.          BNE CHECKBUMP
  3005.          LDA #<FLUSHCODE        ; SEND THE FLUSH CODE TO THE COMPRESSED FILE SO
  3006.          STA STRINGCODE         ; THE DECOMPRESSOR WILL KNOW TO FLUSH THE
  3007.          LDA #>FLUSHCODE        ; DICTIONARY
  3008.          STA STRINGCODE+1
  3009.          JSR OUTPUT
  3010.          JSR INITDIC
  3011.          JMP CHECKEOF
  3012.          CHECKBUMP LDA NEXTBUMP+1
  3013.          CMP NEXTCODE+1         ; CHECKBUMP CHECK TO SEE IF THE NEXTCODE HAS
  3014.          BNE CHECKEOF        ; REACHED THE MAXIMUM VALUE FOR THE CURRENT
  3015.          LDA NEXTBUMP                    ; NUMBER OF BITS BEING OUTPUT.
  3016.          CMP NEXTCODE        ; FOR     X  BITS     NEXTCODE HAS Y PHRASES
  3017.          BNE CHECKEOF        ;        --------     -----------------------
  3018.          LDA #>BUMPCODE      ;           9                 511
  3019.          STA STRINGCODE+1    ;          10                1023
  3020.          LDA #<BUMPCODE      ;          11                2047
  3021.          STA STRINGCODE      ;          12                4095
  3022.          JSR OUTPUT
  3023.          INC CURRENTBITS
  3024.          ASL NEXTBUMP
  3025.          ROL NEXTBUMP+1
  3026.          CHECKEOF LDA #0
  3027.          STA STRINGCODE+1
  3028.          LDA CHARACTER
  3029.          STA STRINGCODE
  3030.          EOF LDA 144
  3031.          BNE DONE
  3032. JMP NEXTCHAR
  3033. DONE JSR OUTPUT
  3034. LDA #>EOS               ; SEND A 256 TO INDICATE EOF
  3035. STA STRINGCODE+1
  3036. LDA #<EOS
  3037. STA STRINGCODE
  3038. JSR OUTPUT
  3039. LDA BITMASK
  3040. BEQ +
  3041.     JSR $FFCC
  3042.     LDX #3
  3043.     JSR $FFC9
  3044.     LDA RACK              ; SEND WHAT BITS WEREN'T SEND WHEN OUTPUT
  3045.     JSR $FFD2
  3046. + JSR $FFCC
  3047. LDA #3
  3048. JSR $FFC3
  3049. LDA #2
  3050. JMP $FFC3
  3051.  
  3052. ;**********************************
  3053. ; INITDIC
  3054. ; INITIALIZES THE DICTIONARY, SETS
  3055. ; THE NUMBER OF BITS TO 9
  3056. ;**********************************
  3057.  
  3058. INITDIC LDA #9
  3059. STA CURRENTBITS
  3060. LDA #>FIRSTCODE
  3061. STA NEXTCODE+1
  3062. LDA #<FIRSTCODE
  3063. STA NEXTCODE
  3064. LDA #>512
  3065. STA NEXTBUMP+1
  3066. LDA #<512
  3067. STA NEXTBUMP
  3068. LDA #<TABLEBASE
  3069. STA $FE
  3070. LDA #>TABLEBASE
  3071. STA $FF
  3072. LDA #<TABLESIZE
  3073. STA $FC
  3074. LDA #>TABLESIZE
  3075. STA $FD
  3076. - LDY #0
  3077.   LDA #255      ; ALL THE CODE VALUES ARE INIT TO 255+256*255
  3078.   STA ($FE),Y   ; OR -1 IN TWO COMPLEMENT
  3079.   INY
  3080.   STA ($FE),Y
  3081.   CLC
  3082.   LDA #5        ; EACH ENTRY IN THE TABLE TAKES 5 BYTES
  3083.   ADC $FE
  3084.   STA $FE
  3085.   BCC +
  3086.       INC $FF
  3087.   + LDA $FC
  3088.   BNE +
  3089.       DEC $FD
  3090.   + DEC $FC
  3091.   LDA $FD
  3092.   ORA $FC
  3093. BNE -
  3094. RTS
  3095.  
  3096. ;************************************
  3097. ; GETCHAR
  3098. ;************************************
  3099.  
  3100. GETCHAR JSR $FFCC
  3101. LDX #2
  3102. JSR $FFC6
  3103. JMP $FFCF
  3104.  
  3105. ;************************************
  3106. ; OUTPUT
  3107. ;************************************
  3108.  
  3109. OUTPUT LDA #0      ; THE NUMBER OF BITS OUTPUT CAN BE OF A VARIABLE
  3110. STA MASK+1         ; LENGTH,SO THE BITS ARE ACCUMULATED TO A BYTE IS
  3111. LDA #1             ; FULL AND THEN IT IS SENT TO THE OUTPUT FILE
  3112. LDX CURRENTBITS
  3113. DEX
  3114. - ASL
  3115.   ROL MASK+1
  3116.   DEX
  3117. BNE -
  3118. STA MASK
  3119. MASKDONE LDA MASK
  3120. ORA MASK+1
  3121. BNE +
  3122.     RTS
  3123. + LDA MASK
  3124. AND STRINGCODE
  3125. STA 3
  3126. LDA MASK+1
  3127. AND STRINGCODE+1
  3128. ORA 3
  3129. BEQ NOBITON
  3130.     LDA RACK
  3131.     ORA BITMASK
  3132.     STA RACK
  3133. NOBITON LSR BITMASK
  3134. LDA BITMASK
  3135. BNE +
  3136.     JSR $FFCC
  3137.     LDX #3
  3138.     JSR $FFC9
  3139.     LDA RACK
  3140.     JSR $FFD2
  3141.     LDA #0
  3142.     STA RACK
  3143.     LDA #128
  3144.     STA BITMASK
  3145. + LSR MASK+1
  3146. ROR MASK
  3147. JMP MASKDONE
  3148.  
  3149. ;******************************
  3150. ; FINDNODE
  3151. ; THIS SEARCHES THE DICTIONARY TILL IT FINDS A PARENT NODE THAT MATCHES
  3152. ; THE STRINGCODE AND A CHILD NODE THAT MATCHES THE CHARACTER OR A EMPTY
  3153. ; NODE.
  3154. ;*******************************
  3155.  
  3156. ; THE HASHING FUNCTION - THE HASHING FUNCTION IS NEEDED BECAUSE
  3157. ; THERE ARE 4096 X 4096 (16 MILLION) DIFFERENT COMBINATIONS OF
  3158. ; CHARACTER AND STRINGCODE. BY MULTIPLYING THE CHARACTER AND STRINGCODE
  3159. ; IN A FORMULA WE CAN DEVELOP OF METHOD OF FINDING THEM IN THE
  3160. ; DICTIONARY. IF THE STRINGCODE AND CHARACTER IN THE DICTIONARY
  3161. ; DON'T MATCH THE ONES WE ARE LOOKING FOR WE CALCULATE AN OFFSET
  3162. ; AND SEARCH THE DICTIONARY FOR THE RIGHT MATCH OR A EMPTY
  3163. ; SPACE IS FOUND. IF AN EMPTY SPACE IS FOUND THEN THAT CHARACTER AND
  3164. ; STRINGCODE COMBINATION IS NOT IN THE DICTIONARY
  3165.  
  3166. FINDNODE LDA #0
  3167. STA INDEX+1
  3168. LDA CHARACTER     ; HERE THE HASHING FUNCTION IS APPLIED TO THE
  3169. ASL               ; CHARACTER AND THE STRING CODE. FOR THOSE WHO
  3170. ROL INDEX+1       ; CARE THE HASHING FORMULA IS:
  3171. EOR STRINGCODE    ; (CHARACTER << 1) ^ STRINGCODE
  3172. STA INDEX         ; FIND NODE WILL LOOP TILL IT FINDS A NODE
  3173. LDA INDEX+1       ; THAT HAS A EMPTY NODE OR MATCHES THE CURRENT
  3174. EOR STRINGCODE+1  ; PARENT CODE AND CHARACTER
  3175. STA INDEX+1
  3176. ORA INDEX 
  3177. BNE +
  3178.     LDX #1
  3179.     STX OFFSET
  3180.     DEX
  3181.     STX OFFSET+1
  3182.     JMP FOREVELOOP
  3183. + SEC
  3184. LDA #<TABLESIZE
  3185. SBC INDEX
  3186. STA OFFSET
  3187. LDA #>TABLESIZE
  3188. SBC INDEX+1
  3189. STA OFFSET+1
  3190.  
  3191. FOREVELOOP JSR CALCULATE     
  3192.            LDY #0
  3193.            LDA ($FE),Y
  3194.            INY
  3195.            AND ($FE),Y
  3196.            CMP #255
  3197.            BNE +
  3198.                LDY #0
  3199.                RTS
  3200.            + INY
  3201.            - LDA ($FE),Y
  3202.              CMP STRINGCODE-2,Y
  3203.              BNE +
  3204.              INY
  3205.              CPY #5
  3206.              BNE -
  3207.           LDY #0
  3208.           RTS
  3209.           + SEC
  3210.           LDA INDEX
  3211.           SBC OFFSET
  3212.           STA INDEX
  3213.           LDA INDEX+1
  3214.           SBC OFFSET+1
  3215.           STA INDEX+1
  3216.           AND #128
  3217.           BEQ FOREVELOOP
  3218.           CLC
  3219.           LDA #<TABLESIZE
  3220.           ADC INDEX
  3221.           STA INDEX
  3222.           LDA #>TABLESIZE
  3223.           ADC INDEX+1
  3224.           STA INDEX+1
  3225. JMP FOREVELOOP
  3226.  
  3227. ;***************************
  3228. ; CALCULATE
  3229. ; TAKES THE VALUE IN INDEX AND CALCULATES ITS LOCATION IN THE DICTIONARY
  3230. ;****************************
  3231.  
  3232. CALCULATE LDA INDEX
  3233. STA $FE
  3234. LDA INDEX+1
  3235. STA $FF
  3236. ASL $FE
  3237. ROL $FF
  3238. ASL $FE
  3239. ROL $FF
  3240. CLC
  3241. LDA INDEX
  3242. ADC $FE
  3243. STA $FE
  3244. LDA INDEX+1
  3245. ADC $FF
  3246. STA $FF
  3247. CLC
  3248. LDA #<TABLEBASE
  3249. ADC $FE
  3250. STA $FE
  3251. LDA #>TABLEBASE
  3252. ADC $FF
  3253. STA $FF
  3254. LDY #0
  3255. RTS
  3256.  
  3257. ;******************************
  3258. ; DECODESTRING
  3259. ;******************************
  3260.  
  3261. DECODESTRING TYA   ; DECODESTRING PUTS THE STRING ON THE STACK
  3262. STA COUNT          ; IN A LIFO FASHION.
  3263. LDX #>DECODESTACK
  3264. CLC
  3265. ADC #<DECODESTACK
  3266. STA $FC
  3267. STX $FD
  3268. LDA #0
  3269. STA COUNT+1
  3270. - LDA INDEX+1
  3271.   BEQ +
  3272.   JSR CALCULATE
  3273.   LDY #4
  3274.   LDA ($FE),Y
  3275.   LDY #0
  3276.   STA ($FC),Y
  3277.   LDY #2
  3278.   LDA ($FE),Y
  3279.   STA INDEX
  3280.   INY
  3281.   LDA ($FE),Y
  3282.   STA INDEX+1
  3283.   JSR INFC
  3284. JMP -
  3285. + LDY #0
  3286. LDA INDEX
  3287. STA ($FC),Y
  3288. INC COUNT
  3289. BNE +
  3290.     INC COUNT+1
  3291. + RTS
  3292.  
  3293. ;******************************
  3294. ; INPUT
  3295. ;******************************
  3296.  
  3297. INPUT LDA #0  ; THE INPUT ROUTINES IS USED BY THE DECOMPRESSOR
  3298. STA MASK+1    ; TO READ IN THE VARIABLE LENGTH CODES
  3299. STA RETURN
  3300. STA RETURN+1
  3301. LDA #1
  3302. LDX CURRENTBITS
  3303. DEX
  3304. - ASL
  3305.   ROL MASK+1
  3306.   DEX
  3307. BNE -
  3308. STA MASK
  3309. - LDA MASK
  3310.   ORA MASK+1
  3311.   BEQ INPUTDONE
  3312.   LDA BITMASK
  3313.   BPL +
  3314.       JSR GETCHAR
  3315.       STA RACK
  3316.   + LDA RACK
  3317.   AND BITMASK
  3318.   BEQ +
  3319.       LDA MASK
  3320.       ORA RETURN
  3321.       STA RETURN
  3322.       LDA MASK+1
  3323.       ORA RETURN+1
  3324.       STA RETURN+1
  3325.   + LSR MASK+1
  3326.   ROR MASK
  3327.   LSR BITMASK
  3328.   LDA BITMASK
  3329.   BNE +
  3330.       LDA #128
  3331.       STA BITMASK
  3332. + JMP -
  3333. INPUTDONE RTS
  3334.  
  3335. ;*******************************
  3336. ; EXPANDFILE
  3337. ; WHERE THE DECOMPRESSION IS DONE
  3338. ;*******************************
  3339.  
  3340. EXPANDFILE LDA #0
  3341. STA RACK
  3342. LDA #128
  3343. STA BITMASK
  3344. START JSR INITDIC
  3345. JSR INPUT
  3346. LDA RETURN+1
  3347. STA OLDCODE+1       ; Save the first character in OLDCODE
  3348. LDA RETURN
  3349. STA CHARACTER
  3350. STA OLDCODE
  3351. CMP #<EOS
  3352. BNE +
  3353.     LDA RETURN+1    ; If return = EOS (256) then all done 
  3354.     CMP #>EOS
  3355.     BNE +
  3356.     JMP CLOSE
  3357. + JSR $FFCC
  3358. LDX #3
  3359. JSR $FFC9
  3360. LDA OLDCODE         ; Send oldcode to the output file
  3361. JSR $FFD2
  3362. NEXT JSR INPUT
  3363.      LDA RETURN
  3364.      STA NEWCODE
  3365.      LDA RETURN+1
  3366.      STA NEWCODE+1
  3367.      CMP #1         ; All of the special codes Flushcode,BumpCode & EOS
  3368.      BNE ++         ; Have 1 for a MSB.
  3369.          LDA NEWCODE
  3370.          CMP #<BUMPCODE
  3371.          BNE +
  3372.              INC CURRENTBITS
  3373.              JMP NEXT
  3374.          + CMP #<FLUSHCODE
  3375.          BEQ START
  3376.          CMP #<EOS
  3377.          BNE +
  3378.          JMP CLOSE
  3379.      + SEC          ; Here we compare the newcode just read in to the
  3380.      LDA NEWCODE    ; next code. If newcode is greater than it is a 
  3381.      SBC NEXTCODE   ; ACUTEACUTEA situation and must be handle differently.
  3382.      STA 3
  3383.      LDA NEWCODE+1 
  3384.      SBC NEXTCODE+1
  3385.      ORA 3
  3386.      BCC +
  3387.          LDA CHARACTER
  3388.          STA DECODESTACK
  3389.          LDA OLDCODE
  3390.          STA INDEX
  3391.          LDA OLDCODE+1
  3392.          STA INDEX+1
  3393.          LDY #1
  3394.          BNE ++
  3395.      + LDA NEWCODE  ; Point index to newcode spot in the dictionary   
  3396.      STA INDEX      ; So DECODESTRING has a place to start
  3397.      LDA NEWCODE+1
  3398.      STA INDEX+1
  3399.      LDY #0
  3400.      + JSR DECODESTRING
  3401.      LDY #0
  3402.      LDA ($FC),Y
  3403.      STA CHARACTER
  3404.      INC $FC
  3405.      BNE +
  3406.          INC $FD
  3407.      + JSR $FFCC
  3408.      LDX #3
  3409.      JSR $FFC9
  3410.      L1 LDA COUNT+1  ; Count contains the number of characters on the stack
  3411.         ORA COUNT
  3412.         BEQ +       
  3413.         JSR DECFC
  3414.         LDY #0
  3415.         LDA ($FC),Y
  3416.         JSR $FFD2
  3417.      JMP L1
  3418.      + LDA NEXTCODE  ; Calculate the spot in the dictionary for the string
  3419.      STA INDEX       ; that was just entered.
  3420.      LDA NEXTCODE+1
  3421.      STA INDEX+1
  3422.      JSR CALCULATE
  3423.      LDY #2          ; The last character read in is tacked onto the end
  3424.      LDA OLDCODE     ; of the string that was just taken off the stack
  3425.      STA ($FE),Y     ; The nextcode is then incremented to prepare for the 
  3426.      INY             ; next entry.
  3427.      LDA OLDCODE+1
  3428.      STA ($FE),Y
  3429.      INY
  3430.      LDA CHARACTER
  3431.      STA ($FE),Y
  3432.      INC NEXTCODE
  3433.      BNE +
  3434.          INC NEXTCODE+1
  3435.      + LDA NEWCODE
  3436.      STA OLDCODE
  3437.      LDA NEWCODE+1
  3438.      STA OLDCODE+1
  3439. JMP NEXT
  3440. CLOSE JSR $FFCC
  3441. LDA #2
  3442. JSR $FFC3
  3443. LDA #3
  3444. JMP $FFC3
  3445.  
  3446. DECFC LDA $FC
  3447. BNE +
  3448.     DEC $FD
  3449. + DEC $FC
  3450. LDA COUNT
  3451. BNE +
  3452.     DEC COUNT+1
  3453. + DEC COUNT
  3454. RTS
  3455. INFC INC $FC
  3456. BNE +
  3457.     INC $FD
  3458. + INC COUNT
  3459. BNE +
  3460.     INC COUNT+1
  3461. + RTS
  3462.  
  3463. NEXTCODE .WOR 0
  3464. STRINGCODE .WOR 0
  3465. CHARACTER .BYT 0
  3466. NEXTBUMP .WOR 0
  3467. CURRENTBITS .BYT 0
  3468. RACK .BYT 0
  3469. BITMASK .BYT 0
  3470. MASK .WOR 0
  3471. INDEX .WOR 0
  3472. OFFSET .WOR 0
  3473. RETURN .WOR 0
  3474. COUNT .WOR 0
  3475. NEWCODE .WOR 0
  3476. OLDCODE .WOR 0
  3477. TEST .BYT 0
  3478.  
  3479. TO DRIVE THE ML I WROTE THIS SMALL BASIC PROGRAM. NOTE THAT CHANNEL TWO IS
  3480. ALWAYS THE INPUT AND CHANNEL THREE IS ALWAYS THE OUTPUT. EX AND CO MAY BE
  3481. CHANGED TO SUIT WHATEVER LOCATIONS THE PROGRAM IS REASSEMBLED AT.
  3482.  
  3483. 1 IFA=.THENA=1:LOAD"LZW.ML",PEEK(186),1
  3484. 10 EX=2500:CO=2503
  3485. 15 PRINT"[E]XPAND OR [C]OMPRESS?"
  3486. 20 GETA$:IFA$<>"C"ANDA$<>"E"THEN20
  3487. 30 INPUT"NAME OF INPUT FILE";FI$:IFLEN(FI$)=.THEN30
  3488. 40 INPUT"NAME OF OUTPUT FILE";FO$:IFLEN(FO$)=.THEN40
  3489. 50 OPEN2,9,2,FI$+",P,R":OPEN3,9,3,FO$+",P,W"
  3490. 60 IFA$="E"THENSYSEX
  3491. 70 IFA$="C"THENSYSCO
  3492. 80 END
  3493.  
  3494. For those interested in learning more about data
  3495. compression/decompression I recommend the book 'The Data Compression
  3496. Book' written by Mark Nelson. I learned a great deal from reading this
  3497. book. It explains all of the major data compression methods. (huffman
  3498. coding, dictionary type compression such as LZW, arithmatic coding,
  3499. speech compression and lossy graphics compression)
  3500.  
  3501. Questions or comments are welcome, they may be directed to me at :
  3502.  
  3503. Internet   : Blucier@ersys.edmonton.ab.ca
  3504. Genie      : b.lucier1
  3505.  
  3506. ------------------------------------------------------------------------------
  3507.  
  3508. begin 644 lzw.ml
  3509. MQ`E,N`P@GPJI@(WT#:D`C?,-(.L*C>T-J0"-[@T@ZPJ-[PT@6`NQ_L@Q_LG_
  3510. M\`ZQ_HWN#8BQ_HWM#4QH"J``N>L-D?[(P`70]N[K#=`#[NP-(/8*K>P-R1#0
  3511. M&JWK#<D`T!.I`HWM#:D!C>X-(/8*()\*3%T*K?$-S>P-T!ZM\`W-ZPW0%JD!
  3512. MC>X-J0&-[0T@]@KN\@T.\`TN\0VI`(WN#:WO#8WM#:60T`-,WPD@]@JI`8WN
  3513. M#:D`C>T-(/8*K?0-\`X@S/^B`R#)_ZWS#2#2_R#,_ZD#(,/_J0),P_^I"8WR
  3514. M#:D!C>P-J0.-ZPVI`HWQ#:D`C?`-J0"%_JDXA?^IG87\J1.%_:``J?^1_LB1
  3515. M_ABI!67^A?Z0`N;_I?S0`L;]QORE_07\T-Y@(,S_H@(@QO],S_^I`(WV#:D!
  3516. MKO(-R@HN]@W*T/F-]0VM]0T-]@W0`6"M]0TM[0V%`ZWV#2WN#04#\`FM\PT-
  3517. M]`V-\PU.]`VM]`W0&"#,_Z(#(,G_K?,-(-+_J0"-\PVI@(WT#4[V#6[U#4P+
  3518. M"ZD`C?@-K>\-"B[X#4WM#8WW#:WX#4WN#8WX#=`1K?<-T`RB`8[Y#<J.^@U,
  3519. MEPLXJ9WM]PV-^0VI$^WX#8WZ#2#C"Z``L?[(,?[)_]`#H`!@R+'^V>L-T`C(
  3520. MP`70]*``8#BM]PWM^0V-]PVM^`WM^@V-^`TI@/`1&*F=;?<-C?<-J1-M^`V-
  3521. M^`U,EPNM]PV%_JWX#87_!OXF_P;^)O\8K?<-9?Z%_JWX#67_A?\8J0!E_H7^
  3522. MJ3AE_X7_H`!@F(W]#:(D&&E4A?R&_:D`C?X-K?@-\!X@XPN@!+'^H`"1_*`"
  3523. ML?Z-]PW(L?Z-^`T@W`U,)@R@`*WW#9'\[OT-T`/N_@U@J0"-]@V-^PV-_`VI
  3524. M`:[R#<H*+O8-RM#YC?4-K?4-#?8-\#NM]`T0!B#K"HWS#:WS#2WT#?`2K?4-
  3525. M#?L-C?L-K?8-#?P-C?P-3O8-;O4-3O0-K?0-T`6I@(WT#4QT#&"I`(WS#:F`
  3526. MC?0-()\*(%D,K?P-C0(.K?L-C>\-C0$.R0#0"JW\#<D!T`-,NPT@S/^B`R#)
  3527. M_ZT!#B#2_R!9#*W[#8W_#:W\#8T`#LD!T!BM_PW)`=`&[O(-3/,,R0+PJ\D`
  3528. MT`-,NPTXK?\-[>L-A0.M``[M[`T%`Y`6K>\-C50DK0$.C?<-K0(.C?@-H`'0
  3529. M#JW_#8WW#:T`#HWX#:``(!0,H`"Q_(WO#>;\T`+F_2#,_Z(#(,G_K?X-#?T-
  3530. M\`T@R`V@`+'\(-+_3&T-K>L-C?<-K>P-C?@-(.,+H`*M`0Z1_LBM`@Z1_LBM
  3531. M[PV1_N[K#=`#[NP-K?\-C0$.K0`.C0(.3/,,(,S_J0(@P_^I`TS#_Z7\T`+&
  3532. M_<;\K?T-T`/._@W._0U@YOS0`N;][OT-T`/N_@U@````````````````````
  3533. *````````````````
  3534. `
  3535. end
  3536.  
  3537. crc32 for lzw.ml = 2460116527
  3538.  
  3539. begin 644 lzw.bas
  3540. M`0@<"`$`BT&R+J=!LC$ZDR),6E<N34PB+#DL,0`P"`H`15BR,C4P,#I#3[(R
  3541. M-3`S`%`(#P"9(I,219)84$%.1"!/4B`20Y)/35!215-3/R(`;`@4`*%!)#J+
  3542. M022SL2)#(J]!)+.Q(D4BIS(P`)<('@"%(DY!344@3T8@24Y0550@1DE,12([
  3543. M1DDD.HO#*$9))"FR+J<S,`##""@`A2).04U%($]&($]55%!55"!&24Q%(CM&
  3544. M3R0ZB\,H1D\D*;(NIS0P`.L(,@"?,BPY+#(L1DDDJB(L4RQ2(CJ?,RPY+#,L
  3545. M1D\DJB(L4"Q7(@#["#P`BT$DLB)%(J>>15@`"PE&`(M!)+(B0R*GGD-/````
  3546. `
  3547. end
  3548.  
  3549. crc32 for lzw.bas = 100674089
  3550.  
  3551. ===============================================================================
  3552. THREE-KEY ROLLOVER for the C-128 and C-64.
  3553. by Craig Bruce  <csbruce@neumann.uwaterloo.ca>
  3554.  
  3555. 1. INTRODUCTION
  3556.  
  3557. This article examines a three-key rollover mechanism for the keyboards of the
  3558. C-128 and C-64 and presents Kernal-wedge implementations for both machines.
  3559. Webster's doesn't seem to know, so I'll tell you that this means that the
  3560. machine will act sensibly if you are holding down one key and then press
  3561. another without releasing the first (or even press a third key while holding
  3562. down two others).  This is useful to fast touch typers.  In fact, fast typing
  3563. without rollover can be quite annoying; you get a lot of missing letters.
  3564.  
  3565. Another annoying property of the kernel keyscanning is joystick interference.
  3566. If you move the joystick plugged into port #1, you will notice that some junk
  3567. keystrokes result.  The keyscanners here eliminate this problem by simply
  3568. checking if the joystick is pressed and ignoring the keyboard if it is.
  3569.  
  3570. The reason that a 3-key rollover is implemented instead of the more general
  3571. N-key rollover is that scanning the keyboard becomes more and more unreliable
  3572. as more keys are held down.  Key "shaddows" begin to appear to make it look
  3573. like you are holding down a certain key when you really are not.  So, by
  3574. limiting the number of keys scanned to 3, some of this can be avoided.  You
  3575. will get strange results if you hold down more than three keys at a time, and
  3576. even sometimes when holding down 3 or less.  The "shift" keys (Shift,
  3577. Commodore, Control, Alternate, and CapsLock) don't count in the 3 keys of
  3578. rollover, but they do make the keyboard harder to read correctly.
  3579. Fortunately, three keys will allow you to type words like "AND" and "THE"
  3580. without releasing any keys.
  3581.  
  3582. 2. USER GUIDE
  3583.  
  3584. Using these utilities is really easy - you just type away like normal.  To
  3585. install the C-128 version, enter:
  3586.  
  3587. BOOT "KEYSCAN128"
  3588.  
  3589. and you're in business.  The program will display "Keyscan128 installed" and
  3590. go to work.  The program loads into memory at addresses $1500-$17BA (5376-6074
  3591. decimal), so you'll want to watch out for conflicts with other utilities.
  3592. This program also takes over the IRQ vector and the BASIC restart vector
  3593. ($A00).  The program will survive a RUN/STOP+RESTORE.  To uninstall this
  3594. program, you must reset the machine (or poke the kernel values back into the
  3595. vectors); it does not uninstall itself.
  3596.  
  3597. Loading the C-64 version is a bit trickier, so a small BASIC loader program is
  3598. provided.  LOAD and RUN the "KEYSCAN64.BOOT" program.  It will load the
  3599. "KEYSCAN64" program into memory at addresses $C500-$C77E (50432-51070 decimal)
  3600. and execute it (with a SYS 50432).  To uninstall the program, enter SYS 50435.
  3601. The program takes over the IRQ and NMI vectors and only gives them back to the
  3602. kernel upon uninstallation.  The program will survive a RUN/STOP+RESTORE.
  3603.  
  3604. Something that you may or may not know about the C-64 is that its keys can be
  3605. made to repeat by poking to address 650 decimal.  POKE650,128 will enable the
  3606. repeating of all keys.  POKE650,0 will enable only the repeating of the SPACE,
  3607. DELETE, and CURSOR keys.  POKE650,64 will disable the repeating of all keys.
  3608. An unusual side effect of changing this to either full repeat or no repeat is
  3609. that holding down SHIFT+COMMODORE (character set shift) will repeat rapidly.
  3610.  
  3611. To see the rollover in action, hold down the "J" key for a while, and then
  3612. press "K" without releasing "J".  "K" will come out as expected, as it would
  3613. with the kernal.  Now, release the "J" key.  If you are on a C-128, you will
  3614. notice that the "K" key will now stop repeating (this is actually an important
  3615. feature - it avoids problems if you don't release all of the keys you are
  3616. holding down, at once).  Now, press and hold the "J" key again without
  3617. releasing the "K".  "J" will now appear.  It wouldn't using the Kernal key
  3618. scanner.  You can also try this with 3-key combinations.  There will be some
  3619. combinations that cause problems; more on this below.
  3620.  
  3621. Also, take a spaz on the joystick plugged into port #1 and observe that no
  3622. garbage gets typed in.  This was an annoying problem with the kernel of both
  3623. the 64 and 128 and has lead many different games to picking between joystick
  3624. #1 and #2 as the primary controller.  The joystick in port #2 is not a problem
  3625. to either Keyscan-128/64 or the Kernal.
  3626.  
  3627. 3. KEYBOARD SCANNING
  3628.  
  3629. The Kernal scans the keyboard sixty times a second to see what keys you are
  3630. holding down.  Because of hardware peculiarities, there are multiple scanning
  3631. techniques that will give different results.
  3632.  
  3633. 3.1. SCANNING EXAMPLE
  3634.  
  3635. An example program is included to demonstrate different keyboard scanning
  3636. techniques possible.  To run it from a C-128 in 40-column (slow) mode, enter:
  3637.  
  3638. BOOT "KEYSHOW"
  3639.  
  3640. On a C-64, you must:
  3641.  
  3642. LOAD "KEYSHOW",8,1
  3643.  
  3644. and then:
  3645.  
  3646. SYS 4864
  3647.  
  3648. The same program works on both machines.  Four maps of the keyscanning matrix
  3649. will be displayed on the 40-column screen, as scanned by different techniques.
  3650. The leftmost one is scanned from top to bottom "quickly".  The second from the
  3651. left scans from bottom to top "quickly".  The third from the left scans the
  3652. keyboard sideways, and the rightmost matrix scans the keys from top to bottom
  3653. "slowly".
  3654.  
  3655. The mapping of keyscan matrix positions to keys is as follows:
  3656.  
  3657. ROWS: \             COLUMNS:    peek($DC01)
  3658. poke   \
  3659. $DC00   \   128      64      32      16      8       4       2       1
  3660.          +-------+-------+-------+-------+-------+-------+-------+-------+
  3661.   255-1  | DOWN  |   F5  |   F3  |   F1  |   F7  | RIGHT | RETURN| DELETE|
  3662.          +-------+-------+-------+-------+-------+-------+-------+-------+
  3663.   255-2  |LEFT-SH|   E   |   S   |   Z   |   4   |   A   |   W   |   3   |
  3664.          +-------+-------+-------+-------+-------+-------+-------+-------+
  3665.   255-4  |   X   |   T   |   F   |   C   |   6   |   D   |   R   |   5   |
  3666.          +-------+-------+-------+-------+-------+-------+-------+-------+
  3667.   255-8  |   V   |   U   |   H   |   B   |   8   |   G   |   Y   |   7   |
  3668.          +-------+-------+-------+-------+-------+-------+-------+-------+
  3669.  255-16  |   N   |   O   |   K   |   M   |   0   |   J   |   I   |   9   |
  3670.          +-------+-------+-------+-------+-------+-------+-------+-------+
  3671.  255-32  |   ,   |   @   |   :   |   .   |   -   |   L   |   P   |   +   |
  3672.          +-------+-------+-------+-------+-------+-------+-------+-------+
  3673.  255-64  |   /   |   ^   |   =   |RGHT-SH|  HOME |   ;   |   *   |   \   |
  3674.          +-------+-------+-------+-------+-------+-------+-------+-------+
  3675. 255-128  | STOP  |   Q   |COMMODR| SPACE |   2   |CONTROL|   _   |   1   |
  3676.          +-------+-------+-------+-------+-------+-------+-------+-------+
  3677.  
  3678. The following table contains the additional keys which must be scanned on the
  3679. C128 (but which are not displayed by the example scanning program).
  3680.  
  3681. ROWS: \               COLUMNS:    peek($DC01)
  3682. poke   \
  3683. $D02F   \   128      64      32      16      8       4       2       1
  3684.          +-------+-------+-------+-------+-------+-------+-------+-------+
  3685.   255-1  |   1   |   7   |   4   |   2   |  TAB  |   5   |   8   |  HELP |
  3686.          +-------+-------+-------+-------+-------+-------+-------+-------+
  3687.   255-2  |   3   |   9   |   6   | ENTER |   LF  |   -   |   +   |  ESC  |
  3688.          +-------+-------+-------+-------+-------+-------+-------+-------+
  3689.   255-4  |NO-SCRL| RIGHT |  LEFT |  DOWN |   UP  |   .   |   0   |  ALT  |
  3690.          +-------+-------+-------+-------+-------+-------+-------+-------+
  3691.  
  3692. These tables are presented on page 642 of the Commodore 128 Programmer's
  3693. Reference Guide.  The scan codes that are stored in location 212 on the C128
  3694. and location 197 on the C64 are calculated based on the above tables.  The
  3695. entry in the "1" bit position of the first line of the first table (DELETE)
  3696. has a scan code of 0, the "2" entry (RETURN) has a scan code of 1, etc., the
  3697. entry on the second scan line in the "1" position ("3") has a scan code of 8,
  3698. etc., all the way down to code 63.  The scan codes for the 128 go all the way
  3699. to 87, continuing in the second table like the first.
  3700.  
  3701. You will notice some strange effects of the different scanning techniques when
  3702. you hold down multiple keys.  More on this below.  Also try pushing joystick
  3703. #1 around.
  3704.  
  3705. 3.2. SCANNING HARDWARE
  3706.  
  3707. To scan the 128 keyboard, you must poke a value into $DC00 (CIA#1 port A) and
  3708. $D02F (VIC chip keyboard select port) to select the row to be scanned.  The
  3709. Data Direction Register for this port will be set to all outputs by the
  3710. Kernal, so you don't have to worry about it.  Each bit of $DC00 and the three
  3711. least significant bits of $D02F are used for selecting rows.  A "0" bit means
  3712. that a row IS selected, and a "1" means that a row IS NOT selected.  The poke
  3713. value to use for selecting among the various rows are given in the two tables
  3714. in the previous section.
  3715.  
  3716. Using one bit per row allows you to select multiple rows at the same time.  It
  3717. can be useful to select all rows at one time or no rows.  To read the row that
  3718. has been selected, simply peek at location $DC01 (CIA#1 port B).  Each bit
  3719. will tell you whether the corresponding key is currently being held down or
  3720. not.  Again, we have reverse logic; a "0" means that the key is being held
  3721. down, and a "1" means that the key is not held down.  The bit values
  3722. corresponding to the keys are given as the column headings in the tables in
  3723. the previous section.  Since there is no such thing as a perfect mechanical
  3724. switch, it is recommended that you "debounce" each key row read in the
  3725. following way:
  3726.  
  3727. again:
  3728.    lda $dc01
  3729.    cmp $dc01
  3730.    bne again
  3731.  
  3732. So, to scan the entire keyboard, you simply select each scan row in some
  3733. order, and read and remember the keys held down on the row.  As it turns out,
  3734. you have to be a bit careful of exactly how you "select" a row.  Also, there
  3735. is a shortcut that you can take.  In order to find out if any key is being
  3736. held down in one operation, all you have to do is select all rows at once and
  3737. see if there are any "0" bits in the read value.  If so, there is a key being
  3738. held down somewhere; if not, then there is no key being held down, so you
  3739. don't have to bother scanning the entire keyboard.  This will reduce our
  3740. keyscanning significantly, which is important, since the keyboard will be
  3741. scanned every 1/60 of a second.
  3742.  
  3743. As mentioned above, joystick #1 will interfere with the Kernal reading the
  3744. keyboard.  This is because the read value of joystick #1 is wired into CIA#1
  3745. port A, the same place that the keyboard read is wired in.  So, whenever a
  3746. switch in the joystick is pushed, the corresponding bit of the keyboard scan
  3747. register will be forced to "0", regardless of which keys are pressed and
  3748. regardless of which scan row is selected.  There's the catch.  If we were to
  3749. un-select all scan rows and still notice "0"s in the keyboard read register,
  3750. then we would know that the joystick was being pushed and would interfere with
  3751. our keyboard scanning, so we could abort keyboard scanning and handle this
  3752. case as if no keys were being held down.
  3753.  
  3754. It still would be possible but unlikely that the user could push the joystick
  3755. in the middle of us scanning the keyboard and screw up our results, so to
  3756. defend against this, we check for the joystick being pushed both before and
  3757. after scanning the keyboard.  If we find that the joystick is pushed at either
  3758. of these times, then we throw out the results and assume that no keys are held
  3759. down.  This way, the only way that a user could screw up the scanning is if
  3760. he/she/it were to press a switch after we begin scanning and release it before
  3761. we finish scanning.  Not bloody likely for a human.
  3762.  
  3763. You get the same deal for keyboard scanning on the 64, except you only need to
  3764. use $DC00 for selecting the scan rows.  Also note that you will not be able to
  3765. play with keyboard scanning from BASIC because of the interrupt reading of the
  3766. keyboard.  You must make sure that interrupts are disabled when playing with
  3767. the keyboard hardware, or interrupt scanning can come along at any time and
  3768. change all of the register settings.
  3769.  
  3770. 3.3. SCANNING SOURCE CODE
  3771.  
  3772. The four keyboard scanning techniques of the example program are presented
  3773. below.  The declarations required for all of them are:
  3774.  
  3775. pa = $dc00        ;row select
  3776. pb = $dc01        ;column read
  3777. ddra = $dc02      ;ddr for row select
  3778. ddrb = $dc03      ;ddr for column read
  3779. scanTable .buf 8  ;storage for scan
  3780. mask = $03        ;work location
  3781.  
  3782. The code is as follows, in Buddy format.  Each routine scans the keyboard and
  3783. stores the results in the "scanTable" table.
  3784.  
  3785. ------------------+------------------+------------------+------------------+
  3786.  Row forward fast | Row backward fast| Column right     | Row forward slow
  3787. ------------------+------------------+------------------+------------------+
  3788.   sei             |  sei             |  sei             |  sei
  3789.   ldx #0          |  ldx #7          |  lda #$00        |  ldx #0
  3790.   lda #$fe        |  lda #$7f        |  sta ddra        |  lda #$fe
  3791.   sta pa          |  sta pa          |  lda #$ff        |  sta mask
  3792.   nextRow = *     |  nextRow = *     |  sta ddrb        |  nextRow = *
  3793. - lda pb          |- lda pb          |  ldy #7          |  lda mask
  3794.   cmp pb          |  cmp pb          |  lda #$7f        |  sta pa
  3795.   bne -           |  bne -           |  sta mask        |- lda pb
  3796.   eor #$ff        |  eor #$ff        |  nextCol = *     |  cmp pb
  3797.   sta scanTable,x |  sta scanTable,x |  lda mask        |  bne -
  3798.   sec             |  sec             |  sta pb          |  eor #$ff
  3799.   rol pa          |  ror pa          |- lda pa          |  sta scanTable,x
  3800.   inx             |  dex             |  cmp pa          |  sec
  3801.   cpx #8          |  bpl nextRow     |  bne -           |  rol mask
  3802.   bcc nextRow     |  cli             |  ldx #$ff        |  inx
  3803.   cli             |  rts             |  stx pb          |  cpx #8
  3804.   rts             |                  |  eor #$ff        |  bcc nextRow
  3805. ------------------+------------------+  ldx #7          |  cli
  3806.                                      |- asl             |  rts
  3807. The forward "quick" scanning stores  |  rol scanTable,x +------------------+
  3808. the scan row selection mask into     |  dex             |
  3809. the row selection register and       |  bpl -           |
  3810. shifts the "0" bit one position      |  sec             |
  3811. "forward" for each row, directly,    |  ror mask        |
  3812. using a "rol $dc00" instruction.     |  dey             |
  3813. This would probably be the obvious   |  bpl nextCol     |
  3814. solution to an optimizing assembler  |  lda #$ff        |
  3815. programmer.  However, for some       |  sta ddra        |
  3816. reason not quite understood by this  |  lda #$00        |
  3817. author, there are "shadowing"        |  sta ddrb        |
  3818. problems with this approach.  If     |  cli             |
  3819. you were to hold down the two keys   |  rts             |
  3820. "H" and "K" at the same time, you    +------------------+
  3821. would notice that these two keys
  3822. are on the same column of two successive rows.  If you hold them both down,
  3823. you will see the two positions become active, but so will the same column of
  3824. all successive rows after the "H" and "K", even though these other keys are
  3825. not actually held down.  You will get an inaccurate reading if bad keys are
  3826. held down simultaneously.  You will notice the use of the term "active" above.
  3827. This is because although the hardware returns a "0" for active, the routine
  3828. converts that into a "1" for easier processing later.  I am not sure if
  3829. everyone will get this same result, but if your keyboard is wired the same as
  3830. mine, you will.
  3831.  
  3832. The backward "quick" scanning operates quite similarly to the forward
  3833. scanning, except for the direction of the scan and the direction of the
  3834. "shadow"; the shadow goes upwards.  You might think that ANDing together the
  3835. results of the forward and backward scan together would eliminate the shadow,
  3836. but this will not work since any rows between the rows containing the two keys
  3837. held down will be incorrectly read as being active.
  3838.  
  3839. The columnwise right scanning is the most complicated because the rows must be
  3840. converted into columns, to allow the scan matrix to be interpreted as before.
  3841. Also, the Data Direction Registers have to be changed.  You might think that
  3842. combinging row-wise scanning with columnwise scanning would give better
  3843. results, and it probably would, if it weren't for a bizarre hardware problem.
  3844. If you hold down two or more keys on the same scan row, say "W" and "E", some
  3845. of the keys will flicker or disappear, giving an inaccurate reading.
  3846.  
  3847. The forward "slow" scanning is the best of the bunch.  Incidentally, it is
  3848. what the Kernal uses (as near as I can figure - their code is extremely
  3849. convoluted).  This technique is the same as the forward "quick scan," except
  3850. that the row selection mask is shifted in a working storage location and poked
  3851. into the CIA register, rather than being shifted in place.  I don't know why
  3852. this makes a difference, but it does.  There is still a problem with this
  3853. technique, but this problem occurs with all techniques.  If you hold down
  3854. three keys that form three "corners" of a rectangle in the scanning matrix,
  3855. then the missing corner will be read as being held down also.  For example, if
  3856. you hold down "C", "N", and "M", then the keyboard hardware will also think
  3857. that you are holding down the "X" key.  This is why this article implements a
  3858. "three-key" rollover rather than an "N-key" rollover.  Many three-key
  3859. combinations will still be interpreted correctly.  Note, however, that shift
  3860. keys such as SHIFT or CONTROL will add one more key to the hardware scanning
  3861. (but will not be counted in the three-key rollover), making inaccurate results
  3862. more likely if you are holding down multiple other keys at the same time.
  3863.  
  3864. 4. THE C-128 KEYSCANNER
  3865.  
  3866. This section gives the source code for the C-128 implementation of the
  3867. three-key rollover.  The forward "slow" key matrix scanning technique is used,
  3868. extended to work with the extra keys of the 128.  It was a bit of a pain
  3869. wedging into the Kernal, since there is not a convenient indirect JMP into
  3870. scanning the keyboard, like there are for decoding and buffering pressed keys.
  3871. A rather lengthy IRQ "preamble" had to be copied from the ROM, up to the
  3872. point where it JSRs to the keyscanning routine.  This code in included in
  3873. the form of a ".byte" table, to spare you the details.
  3874.  
  3875. Before scanning the keyboard, we check to see if joystick #1 is pushed and if
  3876. a key is actually pressed.  If not, we abort scanning and JMP to the key
  3877. repeat handling in the ROM.  If a key is held down, we scan the keyboard and
  3878. then examine the result.  First we check for the shift keys (SHIFT, COMMODORE,
  3879. CONTROL, ALT, and CAPS LOCK), put them into location $D3 (shift flags) in bit
  3880. postitions 1, 2, 4, 8, and 16, respectively, and remove them from the scan
  3881. matrix.  The CAPS LOCK key is not on the main key matrix; it is read from the
  3882. processor I/O port.  This is good, because otherwise we could not abort
  3883. scanning if it were the only key held down.
  3884.  
  3885. Then we scan the keymatrix for the first three keys that are being held down,
  3886. or as many as are held down if less than three.  We store the scan codes of
  3887. these keys into a 3-element array.  We also retain a copy of the 3-element
  3888. array from the previous scan and we check for different keys being in the two
  3889. arrays.  If the old array contains a key that is not present in the new array,
  3890. then the use has released a key, so we set a flag to inhibit interpretation of
  3891. keys and pretend that no keys are held down.  This is to eliminate undesirable
  3892. effects of having other keys held down repeat if you release the most recently
  3893. pushed key first.  PC keyboards do this.  This inhibiting will be ignored if
  3894. new keys are discovered in the next step.
  3895.  
  3896. If there are keys in the new array that are not in the old, then the user has
  3897. just pressed a new key, so that new key goes to the head of the old array and
  3898. we stop comparing the arrays there.  The key in the first position of the old
  3899. array is poked into the Kernal "key held down" location for the Kernal to
  3900. interpret later.  If more than one new key is discovered at the same time,
  3901. then each of the new keys will be picked up on successive keyboard scans and
  3902. will be interpreted as just being pushed.  So, if you press the "A", "N", and
  3903. "D" keys all at the same time, some permutation of all three of these keys
  3904. will appear on the screen.
  3905.  
  3906. When we are done interpreting the keys, we check the joystick once more and if
  3907. it is still inactive, we present the most recently pushed down key to the
  3908. Kernal and JMP into the ROM keyboard decoding routine.
  3909.  
  3910. Unlike in previous issues, this source code is here in literal form; just
  3911. extract everything between the "-----=-----"s to nab the source for yourself.
  3912. The source is in Buddy assembler format.
  3913.  
  3914. -----=-----
  3915. ;3-Key Rollover-128 by Craig Bruce 18-Jun-93 for C= Hacking magazine
  3916.  
  3917. .org $1500
  3918. .obj "@0:keyscan128"
  3919.  
  3920. scanrows = 11
  3921. rollover = 3
  3922.  
  3923. pa = $dc00
  3924. pb = $dc01
  3925. pk = $d02f
  3926.  
  3927. jmp initialInstall
  3928.  
  3929. ;ugly IRQ patch code.
  3930.  
  3931. irq = *  ;$1503
  3932.    .byte $d8,$20,$0a,$15,$4c,$69,$fa,$38,$ad,$19,$d0,$29,$01,$f0,$07,$8d
  3933.    .byte $19,$d0,$a5,$d8,$c9,$ff,$f0,$6f,$2c,$11,$d0,$30,$04,$29,$40,$d0
  3934.    .byte $31,$38,$a5,$d8,$f0,$2c,$24,$d8,$50,$06,$ad,$34,$0a,$8d,$12,$d0
  3935.    .byte $a5,$01,$29,$fd,$09,$04,$48,$ad,$2d,$0a,$48,$ad,$11,$d0,$29,$7f
  3936.    .byte $09,$20,$a8,$ad,$16,$d0,$24,$d8,$30,$03,$29,$ef,$2c,$09,$10,$aa
  3937.    .byte $d0,$28,$a9,$ff,$8d,$12,$d0,$a5,$01,$09,$02,$29,$fb,$05,$d9,$48
  3938.    .byte $ad,$2c,$0a,$48,$ad,$11,$d0,$29,$5f,$a8,$ad,$16,$d0,$29,$ef,$aa
  3939.    .byte $b0,$08,$a2,$07,$ca,$d0,$fd,$ea,$ea,$aa,$68,$8d,$18,$d0,$68,$85
  3940.    .byte $01,$8c,$11,$d0,$8e,$16,$d0,$b0,$13,$ad,$30,$d0,$29,$01,$f0,$0c
  3941.    .byte $a5,$d8,$29,$40,$f0,$06,$ad,$11,$d0,$10,$01,$38,$58,$90,$07,$20
  3942.    .byte $aa,$15,$20,$e7,$c6,$38,$60
  3943.  
  3944. ;keyscanning entry point
  3945.  
  3946. main = *
  3947.    lda #0               ;check if any keys are held down
  3948.    sta pa
  3949.    sta pk
  3950. -  lda pb
  3951.    cmp pb
  3952.    bne -
  3953.    cmp #$ff
  3954.    beq noKeyPressed     ;if not, then don't scan keyboard, goto Kernal
  3955.  
  3956.    jsr checkJoystick    ;if so, make sure joystick not pressed
  3957.    bcc joystickPressed
  3958.    jsr keyscan          ;scan the keyboard and store results
  3959.    jsr checkJoystick    ;make sure joystick not pressed again
  3960.    bcc joystickPressed
  3961.    jsr shiftdecode      ;decode the shift keys
  3962.    jsr keydecode        ;decode the first 3 regular keys held down
  3963.    jsr keyorder         ;see which new keys pressed, old keys released, and
  3964.                         ;  determine which key to present to the Kernal
  3965.    lda $033e            ;set up for and dispatch to Kernal
  3966.    sta $cc
  3967.    lda $033f
  3968.    sta $cd
  3969.    ldx #$ff
  3970.    bit ignoreKeys
  3971.    bmi ++
  3972.    lda prevKeys+0
  3973.    cmp #$ff
  3974.    bne +
  3975.    lda $d3
  3976.    beq ++
  3977.    lda #88
  3978. +  sta $d4
  3979.    tay
  3980.    jmp ($033a)
  3981.  
  3982.    noKeyPressed = *     ;no keys pressed; select default scan row
  3983.    lda #$7f
  3984.    sta pa
  3985.    lda #$ff
  3986.    sta pk
  3987.  
  3988.    joystickPressed = *
  3989.    lda #$ff             ;record that no keys are down in old 3-key array
  3990.    ldx #rollover-1
  3991. -  sta prevKeys,x
  3992.    dex
  3993.    bpl -
  3994.    jsr scanCaps         ;scan the CAPS LOCK key
  3995.    ldx #$ff
  3996.    lda #0
  3997.    sta ignoreKeys
  3998.  
  3999. +  lda #88              ;present "no key held" to Kernal
  4000.    sta $d4
  4001.    tay
  4002.    jmp $c697
  4003.  
  4004. initialInstall = *      ;install wedge: set restore vector, print message
  4005.    jsr install
  4006.    lda #<reinstall
  4007.    ldy #>reinstall
  4008.    sta $0a00
  4009.    sty $0a01
  4010.    ldx #0
  4011. -  lda installMsg,x
  4012.    beq +
  4013.    jsr $ffd2
  4014.    inx
  4015.    bne -
  4016. +  rts
  4017.  
  4018.    installMsg = *
  4019.    .byte 13
  4020.    .asc "keyscan128 installed"
  4021.    .byte 0
  4022.  
  4023. reinstall = *           ;re-install wedge after a RUN/STOP+RESTORE
  4024.    jsr install
  4025.    jmp $4003
  4026.  
  4027. install = *             ;guts of installation: set IRQ vector to patch code
  4028.    sei                  ;  and initialize scanning variables
  4029.    lda #<irq
  4030.    ldy #>irq
  4031.    sta $0314
  4032.    sty $0315
  4033.    cli
  4034.    ldx #rollover-1
  4035.    lda #$ff
  4036. -  sta prevKeys,x
  4037.    dex
  4038.    bpl -
  4039.    lda #0
  4040.    sta ignoreKeys
  4041.    rts
  4042.  
  4043. mask = $cc
  4044.  
  4045. keyscan = *             ;scan the (extended) keyboard using the forward
  4046.    ldx #$ff             ;  row-wise "slow" technique
  4047.    ldy #$ff
  4048.    lda #$fe
  4049.    sta mask+0
  4050.    lda #$ff
  4051.    sta mask+1
  4052.    jmp +
  4053.    nextRow = *
  4054. -  lda pb
  4055.    cmp pb
  4056.    bne -
  4057.    sty pa
  4058.    sty pk
  4059.    eor #$ff
  4060.    sta scanTable,x
  4061.    sec
  4062.    rol mask+0
  4063.    rol mask+1
  4064. +  lda mask+0
  4065.    sta pa
  4066.    lda mask+1
  4067.    sta pk
  4068.    inx
  4069.    cpx #scanrows
  4070.    bcc nextRow
  4071.    rts
  4072.  
  4073. shiftValue = $d3
  4074.  
  4075. shiftRows .byte $01,$06,$07,$07,$0a
  4076. shiftBits .byte $80,$10,$20,$04,$01
  4077. shiftMask .byte $01,$01,$02,$04,$08
  4078.  
  4079. shiftdecode = *         ;see which "shift" keys are held down, put them into
  4080.    jsr scanCaps         ;  proper positions in $D3 (shift flags), and remove
  4081.    ldy #4               ;  them from the scan matrix
  4082. -  ldx shiftRows,y
  4083.    lda scanTable,x
  4084.    and shiftBits,y
  4085.    beq +
  4086.    lda shiftMask,y
  4087.    ora shiftValue
  4088.    sta shiftValue
  4089.    lda shiftBits,y
  4090.    eor #$ff
  4091.    and scanTable,x
  4092.    sta scanTable,x
  4093. +  dey
  4094.    bpl -
  4095.    rts
  4096.  
  4097. scanCaps = *            ;scan the CAPS LOCK key from the processor I/O port
  4098. -  lda $1
  4099.    cmp $1
  4100.    bne -
  4101.    eor #$ff
  4102.    and #$40
  4103.    lsr
  4104.    lsr
  4105.    sta shiftValue
  4106.    rts
  4107.  
  4108. newpos = $cc
  4109. keycode = $d4
  4110. xsave = $cd
  4111.  
  4112. keydecode = *           ;get the scan codes of the first three keys held down
  4113.    ldx #rollover-1      ;initialize: $ff means no key held
  4114.    lda #$ff
  4115. -  sta newKeys,x
  4116.    dex
  4117.    bpl -
  4118.    ldy #0
  4119.    sty newpos
  4120.    ldx #0
  4121.    stx keycode
  4122.  
  4123.    decodeNextRow = *    ;decode a row, incrementing the current scan code
  4124.    lda scanTable,x
  4125.    beq decodeContinue
  4126.                         ;at this point, we know that the row has a key held
  4127.    ldy keycode
  4128. -  lsr
  4129.    bcc ++
  4130.    pha                  ;here we know which key it is, so store its scan code,
  4131.    stx xsave            ;  up to 3 keys
  4132.    ldx newpos
  4133.    cpx #rollover
  4134.    bcs +
  4135.    tya
  4136.    sta newKeys,x
  4137.    inc newpos
  4138. +  ldx xsave
  4139.    pla
  4140. +  iny
  4141.    cmp #$00
  4142.    bne -
  4143.  
  4144.    decodeContinue = *
  4145.    clc
  4146.    lda keycode
  4147.    adc #8
  4148.    sta keycode
  4149.    inx
  4150.    cpx #scanrows
  4151.    bcc decodeNextRow
  4152.    rts
  4153.  
  4154. ;keyorder: determine what key to present to the Kernal as being logically the
  4155. ;only one pressed, based on which keys previously held have been released and
  4156. ;which new keys have just been pressed
  4157.  
  4158. keyorder = *
  4159.    ;** remove old keys no longer held from old scan code array
  4160.    ldy #0
  4161.    nextRemove = *
  4162.    lda prevKeys,y       ;get current old key
  4163.    cmp #$ff
  4164.    beq ++
  4165.    ldx #rollover-1      ;search for it in the new scan code array
  4166. -  cmp newKeys,x
  4167.    beq +
  4168.    dex
  4169.    bpl -
  4170.    tya                  ;here, old key no longer held; remove it
  4171.    tax
  4172. -  lda prevKeys+1,x
  4173.    sta prevKeys+0,x
  4174.    inx
  4175.    cpx #rollover-1
  4176.    bcc -
  4177.    lda #$ff
  4178.    sta prevKeys+rollover-1
  4179.    sta ignoreKeys
  4180. +  iny                  ;check next old key
  4181.    cpy #rollover
  4182.    bcc nextRemove
  4183.  
  4184.    ;** insert new keys at front of old scan code array 
  4185. +  ldy #0
  4186.    nextInsert = *
  4187.    lda newKeys,y        ;get current new key
  4188.    cmp #$ff
  4189.    beq ++
  4190.    ldx #rollover-1      ;check old scan code array for it
  4191. -  cmp prevKeys,x
  4192.    beq +
  4193.    dex
  4194.    bpl -
  4195.    pha                  ;it's not there, so insert new key at front, exit
  4196.    ldx #rollover-2
  4197. -  lda prevKeys+0,x
  4198.    sta prevKeys+1,x
  4199.    dex
  4200.    bpl -
  4201.    lda #0
  4202.    sta ignoreKeys
  4203.    pla
  4204.    sta prevKeys+0
  4205.    ldy #rollover        ;(trick to exit)
  4206. +  iny
  4207.    cpy #rollover
  4208.    bcc nextInsert
  4209. +  rts                  ;now, the head of the old scan code array contains
  4210.                         ;  the scan code to present to the Kernal, and other
  4211.                         ;  positions represent keys that are also held down
  4212.                         ;  that have already been processed and therefore can
  4213.                         ;  be ignored until they are released
  4214.  
  4215. checkJoystick = *       ;check if joystick is pushed: un-select all keyboard
  4216.    lda #$ff             ;  rows and see if there are any "0"s in the scan
  4217.    sta pa               ;  status register
  4218.    sta pk
  4219. -  lda pb
  4220.    cmp pb
  4221.    bne -
  4222.    cmp #$ff
  4223.    lda #$7f             ;restore to default Kernal row selected (to the one
  4224.    sta pa               ;  containing the STOP key)
  4225.    lda #$ff
  4226.    sta pk
  4227.    rts
  4228.  
  4229. ;global variables
  4230.  
  4231. scanTable  .buf scanrows        ;values of the eleven keyboard scan rows
  4232. newKeys    .buf rollover        ;codes of up to three keys held simultaneously
  4233. ignoreKeys .buf 1               ;flag: if an old key has been released and no
  4234.                                 ;  new key has been pressed, stop all key
  4235.                                 ;  repeating
  4236. prevKeys   .buf rollover+2      ;keys held on previous scan
  4237. -----=-----
  4238.  
  4239. And that's all there is to it.  :-)
  4240.  
  4241. 5. THE C-64 KEYSCANNER
  4242.  
  4243. The boot program for the C-64 keyscanner is as follows:
  4244.  
  4245. 10 d=peek(186)
  4246. 20 if a=1 then 60
  4247. 30 a=1
  4248. 40 load"keyscan64",d,1
  4249. 50 goto 10
  4250. 60 sys 49152+5*256  : rem $c500
  4251.  
  4252. It is very much like boot programs for other machine language programs that
  4253. don't load at the start of BASIC.  It will load the binary from the last
  4254. device accessed, and activate it.
  4255.  
  4256. A listing of the C-64 keyscanning code is not presented here because it is so
  4257. similar to the C-128 listing.  The only things that are different are the
  4258. Kernal patches and the keyboard scanning (because the three extra rows don't
  4259. have to be scanned).  The IRQ had to be substantially copied from the ROM,
  4260. again, to get at the call to the key scanning.  Also, rather than taking
  4261. over the BASIC reset vector (since there isn't one), the NMI vector is
  4262. taken over to insure the survival of the key scanner after a RUN/STOP+RESTORE.
  4263. A bit of its preamble also had to be copied out of ROM to get at the good
  4264. stuff.  If you want a copy of the C-64 listing, you can e-mail me.
  4265.  
  4266. 6. UUENCODED FILES
  4267.  
  4268. Here are the binary executables in uuencoded form.  The CRC32s of the four
  4269. files are as follows:
  4270.  
  4271. crc32 = 3398956287 for "keyscan128"
  4272. crc32 = 2301926894 for "keyscan64.boot"
  4273. crc32 = 1767081474 for "keyscan64"
  4274. crc32 = 1604419896 for "keyshow"
  4275.  
  4276. begin 640 keyscan128
  4277. M`!5,'A;8(`H53&GZ.*T9T"D!\`>-&="EV,G_\&\L$=`P!"E`T#$XI=CP+"38
  4278. M4`:M-`J-$M"E`2G]"01(K2T*2*T1T"E_"2"HK1;0)-@P`RGO+`D0JM`HJ?^-
  4279. M$M"E`0D"*?L%V4BM+`I(K1'0*5^HK1;0*>^JL`BB!\K0_>KJJFB-&-!HA0&,
  4280. M$=".%M"P$ZTPT"D!\`REV"E`\`:M$=`0`3A8D`<@JA4@Y\8X8*D`C0#<C2_0
  4281. MK0'<S0'<T/C)__`Z((D7D#\@<18@B1>0-R"W%B#L%B`L%ZT^`X7,K3\#A<VB
  4282. M_RRT%S`QK;47R?_0!J73\":I6(74J&PZ`ZE_C0#<J?^-+]"I_Z("G;47RA#Z
  4283. M(-T6HO^I`(VT%ZE8A=2H3)?&(%46J4^@%HT`"HP!"J(`O3D6\`8@TO_HT/5@
  4284. M#4M%65-#04XQ,C@@24Y35$%,3$5$`"!5%DP#0'BI`Z`5C10#C!4#6*("J?^=
  4285. MM1?*$/JI`(VT%V"B_Z#_J?Z%S*G_A<U,F!:M`=S-`=S0^(P`W(POT$G_G:87
  4286. M.";,)LVES(T`W*7-C2_0Z.`+D-E@`08'!PJ`$"`$`0$!`@0((-T6H`2^J!:]
  4287. MIA<YK1;P$KFR%@73A=.YK19)_SVF%YVF%X@0X&"E`<4!T/I)_RE`2DJ%TV"B
  4288. M`JG_G;$7RA#ZH`"$S*(`AM2]IA?P'*342I`22(;-ILS@`[`&F)VQ%^;,ILUH
  4289. MR,D`T.88I=1I"(74Z.`+D--@H`"YM1?)__`DH@+=L1?P&,H0^)BJO;87G;47
  4290. MZ.`"D/6I_XVW%XVT%\C``Y#5H`"YL1?)__`FH@+=M1?P&LH0^$BB`;VU%YVV
  4291. M%\H0]ZD`C;07:(VU%Z`#R,`#D--@J?^-`-R-+]"M`=S-`=S0^,G_J7^-`-RI
  4292. 9_XTOT&``````````````````````````````
  4293. `
  4294. end
  4295. begin 640 keyscan64.boot
  4296. M`0@."`H`1++"*#$X-BD`'0@4`(L@0;(Q(*<@-C``)0@>`$&R,0`Z""@`DR)+
  4297. M15E30T%.-C0B+$0L,0!#"#(`B2`Q,`!@"#P`GB`T.3$U,JHUK#(U-B`@.B"/
  4298. )("1#-3`P````````
  4299. `
  4300. end
  4301. begin 640 keyscan64
  4302. M`,5,Q,5,$,9,ZL4@ZO^ES-`IQLW0):D4A<VDTT;/KH<"L=&P$>;/A<X@).JQ
  4303. M\XV'`JZ&`J7.28`@'.JE`2D0\`J@`(3`I0$)(-`(I<#0!J4!*1^%`2!9Q4Q^
  4304. MZJD`C0#<K0'<S0'<T/C)__`Y(%C'D#D@6,8@6,>0,2"-QB"[QB#[QJF!A?6I
  4305. MZX7VHO\L>,<P+:UYQ\G_T`>MC0+P(:E`A<NH;(\"J7^-`-RI_Z("G7G'RA#Z
  4306. M(+7&HO^I`(UXQZE`A<NH3";K(.K%H@"]U<7P!B#2_^C0]6!+15E30T%.-C0@
  4307. M24Y35$%,3$5$#0!XJ0F@Q8T4`XP5`ZDGH,:-&`.,&0-8H@*I_YUYQ\H0^JD`
  4308. MC7C'8'BI,:#JC10#C!4#J4>@_HT8`XP9`UA@2(I(F$BI?XT-W:P-W3`?(`+]
  4309. MT`-L`H`@O/8@X?_0#R`5_2"C_2`8Y2#JQ6P"H$QR_J+_H/^I_H7U3';&K0'<
  4310. MS0'<T/B,`-Q)_YUMQS@F]:7UC0#<Z.`(D.-@`08'!X`0(`0!`0($(+7&H`.^
  4311. M@<:];<<YA<;P%+F)Q@V-`HV-`KF%QDG_/6W'G6W'B!#>8*D`C8T"8*("J?^=
  4312. M=<?*$/J@`(3UH@"&R[UMQ_`<I,M*D!)(AO:F]>`#L`:8G77'YO6F]FC(R0#0
  4313. MYABERVD(A<OHX`B0TV"@`+EYQ\G_\"2B`MUUQ_`8RA#XF*J]>L>=><?HX`*0
  4314. M]:G_C7O'C7C'R,`#D-6@`+EUQ\G_\":B`MUYQ_`:RA#X2*(!O7G'G7K'RA#W
  4315. MJ0"->,=HC7G'H`/(P`.0TV"I_XT`W*T!W,T!W-#XR?^I?XT`W&``````````
  4316. *````````````````
  4317. `
  4318. end
  4319. begin 640 keyshow
  4320. M`!-,"Q,``````````*F3(-+_(#83H@`@%Q0@5A.B"B`7%"!T$Z(4(!<4(/03
  4321. MHAX@%Q0@3!1,$!-XH@"I_HT`W*T!W,T!W-#X2?^=`Q,X+@#<Z.`(D.I88'BB
  4322. M!ZE_C0#<K0'<S0'<T/A)_YT#$SAN`-S*$.Q88'BI`(T"W*G_C0/<H`>I?X4#
  4323. MI0.-`=RM`-S-`-S0^*+_C@'<2?^B!PH^`Q/*$/DX9@.($-VI_XT"W*D`C0/<
  4324. M6&!XJ0"-`MRI_XT#W*`'J7^%`Z4#C0'<K0#<S0#<T/BB_XX!W$G_H@<*/@,3
  4325. MRA#Y.&8#B!#=J?^-`MRI`(T#W%A@>*(`J?Z%`Z4#C0#<K0'<S0'<T/A)_YT#
  4326. M$S@F`^C@")#F6&"@!(8$A`6B`+T#$R`V%!BE!&DHA020`N8%Z.`(D.I@A0*@
  4327. 6!T8"J0!I,)$$B!#U8````````*(`8```
  4328. `
  4329. end
  4330. ================================================================================
  4331. In the Next Issue:
  4332.  
  4333. Next Issue:
  4334.  
  4335. Tech-tech - more resolution to vertical shift
  4336.  
  4337. One time half of the demos had pictures waving horizontally on the width
  4338. of the whole screen. This effect is named tech-tech and it is done using
  4339. character graphics. How exactly and is the same possible with sprites ?
  4340.  
  4341. THE DESIGN OF ACE-128/64
  4342.  
  4343. Design of ACE-128/64 command shell environment (and kernel replacement).  This
  4344. will cover the organization, internal operation, and the kernel interface of
  4345. the still-under-development but possibly catching-on kernel replacement for
  4346. the 128 and 64.  The article will also discuss future directions and designs
  4347. for the ACE environment.  ACE has a number of definite design advantages over
  4348. other kernel replacements, and a few disadvantages as well.
  4349.  
  4350.