home *** CD-ROM | disk | FTP | other *** search
/ Brotikasten / BROTCD01.iso / texte / cbmhac10.txt < prev    next >
Text File  |  1995-08-20  |  431KB  |  10,718 lines

  1.                    ########
  2.              ##################
  3.          ######            ######
  4.       #####
  5.     #####  ####  ####      ##      #####   ####  ####  ####  ####  ####   #####
  6.   #####    ##    ##      ####    ##   ##   ##  ###     ##    ####  ##   ##   ##
  7.  #####    ########     ##  ##   ##        #####       ##    ## ## ##   ##
  8. #####    ##    ##    ########  ##   ##   ##  ###     ##    ##  ####   ##   ##
  9. #####  ####  ####  ####  ####  #####   ####  ####  ####  ####  ####   ######
  10. #####                                                                     ##
  11.  ######            ######            Issue #10
  12.    ##################              June 30, 1995
  13.        ########
  14.  
  15. ---------------------------------------------------------------------(v1.5)---
  16. Editor's Notes
  17. by Craig Taylor
  18.  
  19. This is my last issue of Commodore Hacking (having finally gotten out the
  20. door, but I couldn't break tradition and get it out on time :-) ). I'm having
  21. to give it up because I've gradually lost interest in Commodore computers over
  22. the years and with the search for a job (anyone wanna hire a csc graduate?)
  23. and as I get older I seem to have less and less time.
  24.  
  25. I'm gonna be handing the reigns of Commodore Hacking over to Jim Brain, who is
  26. a very active member of the Commodore Internet community. He will also be
  27. running a mailserver that will take the place of mine (Mine will become
  28. unavailable after July 1st and will send pointers to Jim Brain's mailserver).
  29.  
  30. It's been interesting to watch the Commodore computers evolve, take off like a
  31. rocket and then have Commodore go into liquidation. Commodore computers have
  32. been and still are, (with some exceptions - 1541 head-banging comes to mind),
  33. technologically sound.  For a "hacking" machine they're wonderful.
  34.  
  35. My email address has changed to duck@nando.net. I periodically still check
  36. mail at duck@pembvax1.pembroke.edu but only every 2 weeks or so. I am still
  37. going to try to be in the Commodore community but time will govern my ability
  38. to do that. I'm going to miss editing this rag....
  39.  
  40. And here is Jim Brain: 
  41.  
  42. Mail Server Changes:
  43.  
  44. With Issue 10, the address for the Commodore Hacking mail server has changed.
  45. The new address is brain@mail.msen.com  The commands are the same as before.
  46. Not all of the files have been moved yet, so please email the administrator
  47. (Jim Brain, brain@mail.msen.com) if a file you need is not on the new site.
  48.  
  49. Howdy:
  50. Howdy, my name is Jim Brain, and I will be taking over the position of 
  51. editor for Commodore Hacking starting with Issue 11.  Some of you may know
  52. me as the Commodore Trivia Contest administrator, the USENET newsgroup
  53. comp.sys.cbm FAQ Manitainer, or the keeper of a Commodore Information
  54. WWW Site at http://www.msen.com/~brain/cbmhome.html.  Wherever you have 
  55. heard of me from, or even if you haven't, I will state that I plan on
  56. handling Commodore Hacking in the following way.  The next issue will
  57. possibly look different cosmetically, as I edit somewhat differently than
  58. Craig, but the content and basic layout will remain the same.  The types
  59. of material will not change, and the structure for submitting articles will
  60. change only in the address to mail them to: brain@mail.msen.com.  However,
  61. I do have a few changes in mind:
  62.  
  63. 1) Try to stabilize the issue generation so that Commodore Hacking will
  64.    become a quarterly publication.
  65.  
  66. 2) Attempt a fully "HTML"ized version of the magazine, while still providing a
  67.    text version. 
  68.    
  69. 3) Pursue the possibility of providing a printed version of these issues
  70.    for those who have no online access to them.
  71.    
  72. 4) Encourage User Groups and other CBM related organizations to carry this
  73.    magazine for their members.
  74.    
  75. So, again I say howdy.  As always, Commodore Hacking will accept your 
  76. articles at any time, so please help us keep this quality magazine running.
  77. If you have any questions or comments about the change in editorship, the
  78. possible changes, or other matters, please feel free to drop me a note.
  79. I look forward to hearing from you and publishing your articles.
  80.  
  81. Jim Brain
  82. brain@mail.msen.com
  83. ===========================================================================
  84. Legal Mumbo-Jumbo
  85.  
  86. Permission is granted to re-distribute this "net-magazine", in whole,
  87. freely for non-profit use. However, please contact individual authors for
  88. permission to publish or re-distribute articles separately. A charge of no
  89. greater than 5 US dollars or equivlent may be charged for library service /
  90. diskettte costs for this "net-magazine".
  91.  
  92. -------------------------------------------------------------------------------
  93.  
  94. Please note that this issue and prior ones are available via anonymous FTP
  95. from ccnga.uwaterloo.ca (among others) under /pub/cbm/hacking.mag and via a
  96. mailserver which documentation can be obtained by sending mail to
  97. "brain@mail.msen.com" with a subject line of "mailserver" and the
  98. lines of "help" and "catalog" in the body of the message.
  99.  
  100. -------------------------------------------------------------------------------
  101. In This Issue:
  102.  
  103. Commodore Trivia 
  104.  
  105. Trivia Edition #13-18 are in this article.  As you may know, these questions
  106. form part of a contest in which the monthly winner gets a prize (Thanks to my
  107. various prize donators). The whole thing is mainly just for fun, so please
  108. enjoy.  Try your hand at Commodore trivia!!
  109.  
  110. BFLI - New graphics modes 2
  111.  
  112. FLI gave us more color to the screen, AFLI increased the horizontal
  113. resolution and color selection by using the hires mode.  BFLI stands
  114. for 'Big FLI' and gives us 400 lines instead of the usual two
  115. hundred.  AFLI and BFLI can be combined, but we are not going into
  116. that.
  117.  
  118. Making stable raster routines (C64 and VIC-20)
  119.  
  120. In this article, I document two methods of creating stable raster
  121. routines on Commodore computers.  The principles apply for most 8-bit
  122. computers, not only Commodores, but raster effects are very rarely
  123. seen on other computers.
  124.  
  125. A Differant Perspective - Part III.
  126.  
  127. Yes!!!  It's yet another article on 3D graphics!  Even if you
  128. haven't been following this series, you can use this program.  This
  129. time around we will write a completely general polygon plotter --
  130. if you can type basic data statements, you can create a three-dimensional
  131. object out of polygons and rotate and project it to your heart's content.
  132. For the more technically inclined we will look at optimizations to the
  133. line routine, EOR-buffer filling, and more!  Yow!
  134.  
  135. Second SID Chip Installation
  136.  
  137. This article describes how to add a second sid chip for use in SidPlayer and
  138. other programs. As always, be extra careful when making modifications to your
  139. computer.
  140.  
  141. Solving Large Systems of Linear Equations on a C64 Without Memory
  142.  
  143. OK, now that I have your attention, I lied.  You can't solve dense
  144. linear systems of equations by direct methods without using memory to
  145. store the problem data.  However, I'll come back to this memory free
  146. assertion later.  The main purpose of this article is to rescue a
  147. usefull numerical algorithm, "Quartersolve", and also to provide a brief
  148. look at the COMAL programming language and BLAS routines.
  149.  
  150. The World of IRC - A New Life for the C64/128
  151.  
  152. I've heard people talking about IRC. What is it? Why is it useful to me as a
  153. Commodore user? Bill "Coolhand" Lueck explains the hows and whys in this 
  154. article.
  155.  
  156. SwiftLink-232 Application Notes (version 1.0b)
  157.  
  158. This information is made available from a paper document published by CMD,
  159. with CMD's permission. 
  160.  
  161. Design and Implementation of a Simple/Efficient Upload/Download Protocol
  162.  
  163. This article details how to implement a custom upload/download protocol that
  164. is faster than most of the ones common to the C64/128 computers.
  165.  
  166. Design and Implementation of a 'Real' Operating System for the 128: Part II
  167.  
  168. There has been a slight change in plans.  I originally intended this 
  169. article to give the design of a theoretical distributed multitasking 
  170. microkernel operating systemfor the C128.  I have decided to go a 
  171. different route: to take out the distributed component for now and implement
  172. a real multitasking microkernel OS for a single machine and extend the system
  173. to be distributed later.  The implementation so far is, of course, only in 
  174. the prototype stage and the application for it is only a demo.  Part III of 
  175. this series will extend this demo system into, perhaps, a usable distributed
  176. operating system.
  177. ========================================================================
  178. Trivia
  179. by Jim Brain (brain@mail.msen.com)
  180.  
  181. Well, summer is upon the Brain household, and things are moving at a fast
  182. clip at the house.  However, the trivia still keeps coming.  I appreciate
  183. all the people who contribute to the trivia and all the people who take
  184. part in the monthly contest.  I have collected Trivia Edition #13-18 in this
  185. article.  As you may know, these questions form part of a contest in which
  186. the monthly winner gets a prize (Thanks to my various prize donators).
  187. The whole thing is mainly just for fun, so please enjoy.
  188.  
  189. As the summer months start up, news on the trivia includes:
  190.  
  191. 1) I now have access to some more orphan machines (C65, C116), so expect
  192.    some trivia questions on those models.
  193.    
  194. 2) The new home now has a number of machines set up, so testing answers to
  195.    the trivia is even easier.  I am still trying to get the old PET 
  196.    machines in house, but the others are here.
  197.    
  198. 3) The Commodore World Wide Web Pages (http://www.msen.com/~brain/cbmhome.html)
  199.    that I maintain and place the trivia on caught the eye of USA Today and
  200.    the Pheonix Gazette.  I was interviewed for both articles.  Look in the
  201.    June 20th edition of USA Today for the segment, and possibly a picture of
  202.    Jim Brain and the machines he uses to create the trivia.
  203.  
  204. As always, I welcome any questions (with answers), and encourage people
  205. to enter their responses to the trivia, now at #18. 
  206.  
  207.  
  208. Jim.
  209.  
  210.  
  211. The following article contains the answers to the December edition of trivia
  212. ($0C0 - $0CF), the questions and answers for January ($0D0 - $0DF), 
  213. February ($0E0 - $0EF), March ($0F0 - $0FF), April ($100 - $10F), and the
  214. questions for the May edition ($110 - $11F).  Enjoy them!
  215.  
  216.  
  217.    Here are the answers to Commodore Trivia Edition #13 for December, 1994
  218.  
  219. Q $0C0) The early 1541 drives used a mechanism developed by ______.  Name
  220.         the company.
  221.  
  222. A $0C0) Alps.
  223.  
  224. Q $0C1) On later models, Commodore subsequently changed manufacturers
  225.         for the 1541 drive mechanism.  Name the new manufacturer.
  226.  
  227. A $0C1) Newtronics.
  228.  
  229. Q $0C2) What is the most obvious difference(s).  (Only one difference is
  230.         necessary)
  231.  
  232. A $0C2) Alps:        push-type latch, round LED.
  233.         Newtronics:  lever-type latch, rectangular LED.
  234.                    
  235. Q $0C3) On Commodore BASIC V2.0, what answer does the following give:
  236.         PRINT (SQR(9)=3)
  237.  
  238. A $0C3) 0.  According to Commodore BASIC, the answer should bby -1, which
  239.         is the BASIC value of TRUE.  However, the above equation is NOT
  240.         true.  Doing PRINT SQR(9) yields 3, but doing PRINT (SQR(9)-3)
  241.         yields 9.31322575E-10 (C64).  This anomaly can be attributed to
  242.         roundoff errors in the floating point math routines in Commodore BASIC.
  243.  
  244. Q $0C4) In Commodore BASIC (Any version) what does B equal after the following
  245.         runs: C=0:B=C=0 
  246.            
  247. A $0C4) B = -1.  The second statement is the one to look at.  The second
  248.         equals sign is treated as a comparison, while the first is treated
  249.         as a assignment.  B gets set to the outcome of the comparison, which
  250.         is TRUE (-1).
  251.  
  252. Q $0C5) The first PET cassette decks were actually _______ brand cassette 
  253.         players, modified for the PET computers.  Name the comapny.
  254.  
  255. A $0C5) Sanyo. Specifically, Model M1540A.  What a model number!
  256.  
  257. Q $0C6) In Commodore BASIC (Any version), what happens if the following
  258.         program is run:
  259.         
  260.         10 J=0
  261.         20 IF J=0 GO TO 40
  262.         30 PRINT "J<>0"
  263.         40 PRINT "J=0"
  264.  
  265. A $0C6) On BASIC 2.0 or greater:
  266.    
  267.    ?SYNTAX  ERROR IN 20
  268.         READY.
  269.         
  270.         On BASIC 1.0:  (found on the PET 2001 series)
  271.         
  272.         J=0
  273.         READY.
  274.  
  275.         BASIC 1.0 totally ignored spaces, so line 20 became "IFJ=0GOTO40".
  276.         That statement would be correctly parsed, sicne it contains the "GOTO"
  277.         keyword.  
  278.         
  279.     However, on BASIC 2.0 or greater, spaces weren't ignored so
  280.     completely, and the "TO" in "GO TO" would be tokenized separately, so
  281.     some code was added to BASIC to check to "GO".  As the code that
  282.     accepts GOTO as a special case for THEN after an IF statement wasn't
  283.     patched this way, the above fails, because GO is not a valid keyword
  284.     after IF.  The statement SHOULD work correctly, but does not because
  285.     of this failure to fix the IF command parsing.
  286.  
  287.     On BASIC 2.0 or greater, substituting the following line for line 20
  288.     will cause the program to work:
  289.  
  290.     20 IF J=0 THEN GO TO 40
  291.  
  292. Q $0C7) In question $068, we learned how Jack Tramiel first happened upon the
  293.         name "COMMODORE".  According to the story, though, in what country
  294.         was he in when he first saw it?
  295.  
  296. A $0C7) Germany.  
  297.  
  298. Q $0C8) On the Commodore user port connector, how many edge contacts are
  299.         there?
  300.  
  301. A $0C8) 24.  Two rows of 12 contacts each.
  302.  
  303. Q $0C9) On most Commodore computers, a logical BASIC screen line can contain
  304.         up to 80 characters.  On what Commodore computer(s) is this not true?
  305.  
  306. A $0C9) According to Commodore documentation, a _physical_ screen line is 
  307.         defined as one screen line of characters.  A _logical_ screen line is 
  308.         defined as how many _physical_ lines can be chained together to 
  309.         create a valid BASIC program line.  
  310.  
  311.         With that in mind, most Commodore computers chose a _logical_
  312.         screen line that was a multiple of the screen width.  This works fine
  313.         for 40 and 80 column screens, but what do we do with the VIC-20, with
  314.         its 22 column screen.  Solution:  make the _logical_ line length equal
  315.         to 4 _physical_ lines, or 88 columns.
  316.  
  317.         When the Commdore 128 was introduced, the number rose to 160
  318.         characters, which is 4 _physical_ lines in 40 column mode, or
  319.         2 _physical_ lines in 80 column mode.  However, you can only take
  320.         advantage of this in 128 mode.  64 mode is limited to 80 characters.
  321.         
  322.         To add to all this confusion, a valid BASIC program line (in memory)
  323.         can actually be 255 (tokenized) characters long, but creating such
  324.         a long line cannot be done from the built-in editor in direct mode.
  325.         
  326.         The AmigaBASIC, available on the Amiga, also does not have the 80
  327.         column line limit.  However, that BASIC is SOOO much different that
  328.         I am not surprised.  The older CBM BASICs, on the other hand, were
  329.         all derivatives of the original Level 1 BASIC for the PET.
  330.  
  331. Q $0CA) If a file is saved to a Commodore Disk Drive with the following
  332.         characters: chr$(65);chr$(160);chr$(66), what will the directory
  333.         entry look like?
  334.  
  335. A $0CA) The filename will show up as "A"B, with the 'B' showing up to the
  336.         right of the '"' mark.  This could be used to make program loading
  337.         easier.  A file that showed up as "filename",8,1 could be loaded
  338.         by simply hitting shift-run/stop on that line.
  339.  
  340. Q $0CB) What is the maximum length (in characters) of a CBM datasette
  341.         filename?
  342.  
  343. A $0CB) References I have on hand say 128 characters.  However, the actual
  344.         code on the 8032 and the C64 acts as though 187 characters can
  345.         actually be sent (tape buffer-5 control bytes = 192-5=187).  The
  346.         references that claim 128 characters are Nick Hampshire's
  347.         _The VIC Revealed_ and _The PET Revealed_.  ANyone care to lay
  348.         this one to rest? 
  349.  
  350. Q $0CC) How many keys are on a stock Commodore 64 keyboard?
  351.  
  352. A $0CC) 66 keys.  This is the same number as found on the VIC-20 and the
  353.         Commodore 16.
  354.  
  355. Q $0CD) Commodore BASIC uses keyword "tokens" to save program space.  Token
  356.         129 becomes "FOR".  What two tokens expand to include a left
  357.         parenthesis as well as a BASIC keyword?
  358.  
  359. A $0CD) TAB( (163) and SPC( (166).
  360.  
  361. Q $0CE) There are 6 wires in the Commodore serial bus.  Name the 6 wires.
  362.  
  363. A $0CE) 1) Serial /SRQIN
  364.         2) GND
  365.         3) Serial ATN IN/OUT
  366.         4) Serial CLK IN/OUT
  367.         5) Serial DATA IN/OUT
  368.         6) /RESET
  369.  
  370. Q $0CF) On the Commodore datasette connector, how many logical connections are
  371.         there?
  372.  
  373. A $0CF) 6. Opposing pins on the connector are hooked together electrically.
  374.  
  375.  
  376.    Here are the answers to Commodore Trivia Edition #14 for January, 1995
  377.  
  378. Q $0D0) How many keys were there on the "original" PET and what was special
  379.         about them?
  380.  
  381. A $0D0) the original PET had 73 calculator-style keys that were laid out
  382.         in a rectangular matrix, not typewriter-style.
  383.  
  384. Q $0D1) How do you produce the "hidden" message(s) on the Commodore 128?
  385.  
  386. A $0D1) SYS 32800,123,45,6.  The screen will clear, and the software
  387.         and hardware developers on the 128 project will be named.
  388.  
  389.         The exact text is as follows:
  390.                 
  391. [RVS]   Brought to you by...
  392.  
  393. Software:
  394.  Fred Bowen
  395.  Terry Ryan
  396.  Von Ertwine
  397.  
  398. Herdware:
  399.  Bil Herd
  400.  Dave Haynie
  401.  Frank Palaia
  402.  
  403. [RVS]Link arms,don't make them.
  404.  
  405. Q $0D2) How much memory did the "original" PET show on bootup?
  406.  
  407. A $0D2) The "original" PET came in two configurations, 4K and 8K, so:
  408.          
  409.           The PET 2001-4 had 3071 bytes.
  410.           The PET 2001-8 had 7167 bytes.
  411.  
  412. Q $0D3) We all know the "reboot" sys for the 64 is sys 64738, but who knows
  413.         the same sys location to reboot the CBM 8032?
  414.  
  415. A $0D3) sys 64790
  416.  
  417. Q $0D4) Which computer(s) beeped at bootup?  (May be more than one, but only
  418.         one required)
  419.            
  420. A $0D4) I know some of these are corect, but the sheer size of the
  421.         list prevents me from checking them ALL out.
  422.         
  423.         FAT 40XX series
  424.         80XX series
  425.         PC-10  (I suspect a number of IBM clones did, and these things have
  426.                 no consistent naming convention across country boundaries.)
  427.         PC-20
  428.         Amiga 1000
  429.         SP9000 (SuperPET)
  430.         
  431. Q $0D5) How much memory did the CBM 8032 show on bootup?
  432.  
  433. A $0D5) 31743 bytes.
  434.  
  435. Q $0D6) Certain Commodore computers provided emtpy EPROM sockets on the
  436.         motherboard.  Give me the number of empty sockets on the following
  437.         machines:
  438.  
  439.         a)  CBM 30XX.
  440.         b)  CBM 8XXX.
  441.         c)  CBM C128.
  442.         d)  Plus/4.
  443.  
  444. A $0D6) a)  3 sockets.
  445.         b)  2 sockets.
  446.         c)  1 socket.
  447.         d)  1 socket.
  448.  
  449. Q $0D7) In Germany, the CBM 8032 came with a 4kB EPROM for the EXXX area,
  450.         while the US version only had a 2kB EPROM.  Why?
  451.  
  452. A $0D7) The German version had additional keybaord drivers for umlaut
  453.         characters and dead keys.  
  454.  
  455. Q $0D8) Who published the first PET memory map in the "PET Gazette"?
  456.  
  457. A $0D8) None other than the infamous Jim Butterfield.
  458.  
  459. Q $0D9) Which is faster to move the sursor on a PET/CBM or C64: SYS or 
  460.         PRINT?
  461.  
  462. A $0D9) PRINT is faster, since the sys approach must process the pokes
  463.         before the sys, which are very slow.
  464.  
  465. Q $0DA) On the Amiga 1000, where are the signatures of the first Amiga
  466.         developers located?
  467.  
  468. A $0DA) Inside the top case of the Amiga (1000).
  469.  
  470.         There is an interesting footnote to this question.  It seems
  471.         that at least some original Amiga machines were labeled as
  472.         Amiga (with nu number).  Then, at some later point, the number was
  473.         added.  In addition, Commodore produced some Amiga 1000 machines
  474.         without the signatures, but most had the telltale handwriting on
  475.         the inside of the case.  
  476.  
  477. Q $0DB) On the 6502, what does the accumulator contain after the following
  478.         is executed:
  479.  
  480.         lda #$aa
  481.         sed
  482.         adc #01
  483.  
  484. A $0DB) Assume carry was clear.  If so, then $11 is the correct answer. 
  485.  
  486. Q $0DC) What is the model number of the US NTSC VIC-II chip?
  487.  
  488. A $0DC) Its first number was 6567, and that is the number most people know
  489.         it by, but Commodore produced a VIC-II using a new manufacturing 
  490.         process that was numbered the 8562. 
  491.  
  492. Q $0DD) What is the European PAL VIC-II chip's model number?
  493.         (Not sure if that's its rightful term, but I hope you understand).
  494.  
  495. A $0DD) Same here.  The part number 6569 is the most remembered number, but
  496.         an 8565 will work as well.
  497.  
  498. Q $0DE) Assume you have two computers, one with each of the above chips inside.
  499.         Which chip draws more pixels on the screen per second?
  500.  
  501. A $0DE) Note, for the purposes of the calculation I am performing, "pixels"
  502.         refers to picture elements that can be adddress and modified using
  503.         normal VIC modes, so there are 320*200 "pixels" on both the PAL
  504.         and NTSC screens.  (I probably should have stated this, but it is
  505.         too late now.)  Also, the screen refresh rates used in the 
  506.         calculations are those defined by the respective television
  507.         standards (60Hz U.S., 50Hz European), even though the actual
  508.         frequencies are off by a small percentage. (for example, the actual
  509.         50Hz refresh rate on European VIC-II chips was calculates as 
  510.         50.124567Hz by Andreas Boose)
  511.         
  512.         So, the PAL draws 320*200*50 pixels per second = 3200000 pixels/s
  513.         NTSC draws 320*200*60 pixels per second = 3840000 pixles/s
  514.         
  515.         Now, some people thought I meant the whole screen, not just the 
  516.         display area provided by the VIC-II chip.  Well, I am not sure
  517.         exactly you calculate pixels on a screen, since the numbers could
  518.         vary from display to display, but if we measure in scanlines:
  519.         
  520.         PAL = 312 scanlines * 50 = 15600 scanlines/s
  521.         NTSC = 262 scanlines * 60 = 15720 scanlines/s
  522.         
  523.         The NTSC machines wins both ways.  
  524.  
  525. Q $0DF) In Commodore BASIC, which statement executes faster:
  526.  
  527.         a = 2--2
  528.  
  529.         or
  530.  
  531.         a = 2+2
  532.  
  533. A $0DF) b is the correct answer, and there are a couple of reasons why:
  534.  
  535.         1) 2--2 takes longer to parse in the BASIC interpreter.
  536.         2) Commodore BASIC subtracts by complementing the sign of the
  537.            second number and adding.  This incurs extra time.
  538.  
  539.         There are even more subtle ones, but I leave them as an
  540.         exercise for the reader.  Send me your reason why.
  541.  
  542.  
  543.    Here are the answers to Commodore Trivia Edition #15 for February, 1995
  544.  
  545. Q $0E0) What is the difference(s) between the Newtronics 1541 and the 1541C?
  546.         (only one difference is needed)
  547.  
  548. A $0E0) (George Page, a noted authority on CBM Drives, indicated that Commodore
  549.         made this a tough question to answer.)  By the time the 1541C was 
  550.         introduced, Commodore threw a number of drives together and called
  551.         them 1541Cs.  The theoretical 1541C exhibited the following
  552.         features:
  553.  
  554.         No head banging, and other problems fixed by modified ROMs.
  555.         Case color matches C64C and C128 computers.
  556.  
  557. Q $0E1) What happens when you type 35072121 in direct mode on the C64 and
  558.         hit return?
  559.  
  560. A $0E1) Simple answer:  Most likely, the screen clears and the word READY.
  561.         is printed at screen top.  This is the behavior seen when pressing 
  562.         RUN-STOP/RESTORE.  Alternately, nothing could happen, or the computer
  563.         could lock up.
  564.  
  565.         Involved answer:  There is a bug in BASIC 2.0.  Easily fixed, but 
  566.         destined to live life immortal.  (long)
  567.  
  568.         The bug is in the PETSCII number to binary conversion routine at
  569.         $a69b (LINGET).  The routine basically reads in a character from the
  570.         line, multiplies a partial result by 10 and adds the new character
  571.         to the partial result.  Here is a code snippet:
  572.  
  573.         a96a     rts
  574.         a96b     ldx #$00  ; zero out partial result
  575.         a96d     stx $14
  576.         a96f     stx $15
  577.         a971     bcs $a96a ; not a number, return
  578.         a973     sbc #$2f  ; PETSCII to binary
  579.         a975     sta $07   
  580.         a977     lda $15   ; get hi byte or partial result
  581.         a979     sta $22
  582.         a97b     cmp #$19  ; partial > 6399
  583.         a97d     bcs $a953 ; yes, goto error
  584.         a97f     lda $14   ; load lo byte of result
  585.         a981     asl       ; lo*2
  586.         a982     rol $22   ; hi*2 + c
  587.         a984     asl       ; lo*2
  588.         a985     rol $22   ; hi*2 + c
  589.         a987     adc $14   ; complete lo*5
  590.         a989     sta $14
  591.         a98b     lda $22  
  592.         a98d     adc $15   ; complete hi*5
  593.         a98f     sta $15
  594.         a991     asl $14   ; lo*2 complete lo*10
  595.         a993     rol $15   ; hi*2 complete hi*10
  596.         a995     lda $14
  597.         a997     adc $07   ; add new char
  598.         a999     sta $14
  599.         a99b     bcc $a99f ; did lo overflow?
  600.         a99d     inc $15   ; yes, inc hi
  601.         a99f     jsr $0073 ; get next char
  602.         a9a2     jmp $a971 ; go through it again.
  603.         
  604.         The problem is at $a97d.  when the partial result is greater than 6399,
  605.         (if partial > 6399, then new partial result will be over 63999)
  606.         the routine needs to get to $af08 to print an error, but can't due to
  607.         branch restrictions.  However, a branch that will get there is in the
  608.         preceding function, which handles the ON GOTO/GOSUB keywords ($a94b,
  609.         ONGOTO).  
  610.  
  611.         So, the BASIC writers just branched to the code in ONGOTO; specifically
  612.         $a953:
  613.         
  614.         a94b     jsr $b79e
  615.         a94e     pha
  616.         a94f     cmp #$8d  ; is the keyword GOSUB ($8d)
  617.         a951     beq $a957 ; yes
  618.         a953     cmp #$89  ; is the keyword GOTO ($89)
  619.         a955     bne $a8e8 ; no, print SYNTAX ERROR.
  620.         a957     ...       ; handle ON GOTO/GOSUB
  621.  
  622.         This code is checking to make sure the ON (var) is followed with a
  623.         GOTO or GOSUB keyword.
  624.  
  625.         The LINGET error handler branches to $a953, which compares
  626.         .A (which holds hi byte of partial result) to $89.  Normally, this
  627.         fails, and the normal SYNTAX ERROR code is reached through the branch
  628.         to $a8e8.  However, for partial results of the form $89XX, the check
  629.         succeeds, and BASIC tries to execute an ON GOTO/GOSUB call.
  630.  
  631.         By the way, it is no coincidence that this error occurs on 35072121,
  632.         since one of the partial results is $8900 (hi byte is $89). In fact,
  633.         350721 will achieve the same result.
  634.  
  635.         If the check succeeds, the code limps along until $a96a:
  636.  
  637.         a969     pla       ; complement to $a94e
  638.         a96a     rts       ; return
  639.  
  640.         But we never executed $a94e, the push, so the stack is now
  641.         messed up.  Since the stack held $9e, $79, $a5 before the PLA,
  642.         (The stack could hold other values, but I always saw these)
  643.         the RTS gets address $a579 to return to, which usually holds a BRK
  644.         opcode.  The break handler is invoked, and the screen clears with the
  645.         READY. at the top.
  646.  
  647.         Now, the BASIC 2.0 authors were justified in reusing the error
  648.         handler code in ONGOTO for LINGET, but they calculated the branch
  649.         offset wrong, according to my tests.  If you have the LINGET error
  650.         handler branch to $a955, all these troubles disappear.  You can
  651.         verify this procedure with the following BASIC program on a 64:
  652.         
  653.         10 for t=57344 to 65535:poke t,peek(t):next
  654.         20 for t=40960 to 49151:poke t,peek(t):next
  655.         30 poke 43390, 214
  656.         40 poke 1, peek(1) and 254
  657.  
  658.         Just to be complete, this error occurs when a 6 digit or greater line
  659.         number is entered and the first 6 digits indicate a number in the
  660.         range 35072-35327 ($8900-$89ff).  Also, it appears the error occurs
  661.         on the VIC-20, but I didn't completely verify it.  It would be
  662.         interesting to note if the error is found on all version of CBM BASIC.
  663.  
  664.         Whew, what a mouthful.
  665.  
  666. Q $0E2) If a SID chip is producing a "sawtooth waveform", does the waveform look
  667.         like: 
  668.  
  669.         a) "/|/|/|/|"  or
  670.         b) "|\|\|\|\"  ?
  671.  
  672. A $0E2) a is the correct answer.
  673.  
  674. Q $0E3) On BASIC 2.0, what special precaution(s) must one take when working with
  675.         relative files? (only one is needed)
  676.  
  677. A $0E3) Because BASIC 2.0 doesn't handle positioning in relative files quite
  678.         right, one must position the relative file pointer before AND AFTER
  679.         a read or write to a relative file.
  680.  
  681. Q $0E4) What incompatibility existed between C128 Rev. 0 ROMS and the REU?
  682.            
  683. A $0E4) OK, I admit it.  I placed this answer and its discussion somewhere
  684.         in my store of information, and it must have fallen behind the 
  685.         cabinet, because I cannot find it.  I will post an answer to this
  686.         as soon as I can find it, but the answers really must go out, as
  687.         they have been held up long enough.
  688.  
  689. Q $0E5) What can trigger an NMI interrupt? (count all sources on one chip as
  690.         one)
  691.  
  692. A $0E5) The following sources can trigger an NMI interrupt:
  693.  
  694.         1) The expansion port.
  695.         2) CIA #2.
  696.         3) The RESTORE key.
  697.  
  698. Q $0E6) What can trigger an IRQ interrupt? (count all sources on one chip as
  699.         one)
  700.  
  701. A $0E6) The following sources can trigger an IRQ interrupt:
  702.  
  703.         1) The VIC-II chip.
  704.         2) CIA #1.
  705.         3) The expansion port.
  706.  
  707. Q $0E7) Where is the ROM in a 1541 located in the 64K memory map?
  708.  
  709. A $0E7) The ROM is located from $C000 to $FFFF, yet the ROM code does not
  710.         begin until $C100.
  711.  
  712. Q $0E8) Which VIA on the 1541 is hooked to the read/write head?
  713.  
  714. A $0E8) VIA #2, located in memory from $1C00 to $1C0E.
  715.  
  716. Q $0E9) In the Commodore DOS, what bit in the file type byte denotes a "locked"
  717.         file?
  718.  
  719. A $0E9) bit 6.
  720.  
  721. Q $0EA) If files are "locked" under Commodore DOS, under what condition(s) may
  722.         the file be changed?
  723.  
  724. A $0EA) Depending on the file, the following operations can be done on a 
  725.         locked file:
  726.         
  727.         1) Rename will change file name, although not contents of file.
  728.         2) Random access can be used to alter file.
  729.         3) Formatting the disk will alter the file. (duh!)
  730.         4) Save-with-replace (@0:) will replace file and unlock it.
  731.         5) Opening file in append mode will allow it to be changed, and
  732.            unlock it.
  733.         6) Opening a relative file and adding or changing a record will
  734.            succeed and unlock file.
  735.  
  736. Q $0EB) How big can a program file be on a 1541 or similar?
  737.  
  738. A $0EB) The file can be as large as a sequential file, since both are stored
  739.         in the same way: 168656 bytes.  However, since a program contains its
  740.         load address as bytes 0 and 1, the largest program size is 168654
  741.         bytes.
  742.  
  743. Q $0EC) Under BASIC 2.0, how does one open a random access file on a disk
  744.         drive?
  745.  
  746. A $0EC) Random access (or direct access) files are a misnomer.  What you
  747.         really doing is opening the disk for reading and writing.  You need
  748.         two open command to access a random file: (assume drive 8)
  749.         
  750.         open 15,8,15     and
  751.         
  752.         open 1,8,4,"#1" will open a random access file using buffer 1.
  753.         open 1,8,4,"#" will open a random access file using the first
  754.         available buffer
  755.         
  756.         Now, by using B-R, B-W, B-A or their replacements, you can write
  757.         data to sectors on the disk.
  758.         
  759.         Note that Random access files are different from relative files.
  760.         
  761. Q $0ED) A file that has a '*' immediately before the filetype is called
  762.         a _________ file.
  763.  
  764. A $0ED) a splat file.  This is its correct term, believe it or not.
  765.  
  766. Q $0EE) We know the 1541 and similar drives have 5 internal buffer areas, but
  767.         how many does an 8050 drive have?
  768.  
  769. A $0EE) Since the 8050 has twice the on-board RAM (4kB), it has 16 buffers, but
  770.         only 13 are available.  (All CBM drives use one buffer for zero-page
  771.         memory, one for stack memory, and one for temporary variables.) 
  772.  
  773. Q $0EF) On a "save-with-replace", where is the location of the first track and
  774.         sector of the new copy of the program saved in the directory entry for
  775.         the old copy?
  776.  
  777. A $0EF) The new first track is stored at location 26, and the new first sector
  778.         is stored at location 27.  These values are copied to their
  779.         correct locations after the save is completed.
  780.  
  781.  
  782.    Here are the answers to Commodore Trivia Edition #16 for March, 1995
  783.  
  784. Q $0F0) What size matrix of pixels comprises a character on a PET 2001
  785.         computer?
  786.  
  787. A $0F0) The matrix was 8 by 8.  
  788.  
  789. Q $0F1) How many bytes did the opening screen on a CBM 4016 show as
  790.         available for use by BASIC?
  791.  
  792. A $0F1) 15359 bytes free.
  793.  
  794. Q $0F2) The character set that produces uppercase letters on unshifted keys 
  795.         is the ________________ character set.
  796.  
  797. A $0F2) "standard mode".  
  798.  
  799. Q $0F3) The character set that produces lowercase letters on unshifted keys
  800.         is the ________________ character set.
  801.  
  802. A $0F3) "alternate mode"
  803.  
  804. Q $0F4) To get to the set mentioned in $F2, what character code would be
  805.         printed to the screen?
  806.  
  807. A $0F4) chr$(142)
  808.  
  809. Q $0F5) What character code would one print to the screen to invoke the 
  810.         chararacter set in $F3?
  811.  
  812. A $0F5) chr$(14)
  813.  
  814. Q $0F6) If one does LIST 60-100, will line 100 get "listed"?
  815.  
  816. A $0F6) Yes.  The above translates as: LIST 60 through to and including 100.
  817.  
  818. Q $0F7) The abbreviation for the BASIC 4.0 command "COLLECT" is ________.
  819.  
  820. A $0F7) coL. "C" "O" "SHIFT-L".  For those who are interested, the 
  821.         COLLECT command is analogous to the VALIDATE operation.
  822.  
  823. Q $0F8) When you use a subscripted variable in BASIC, how many elements
  824.         are created by default if no DIM statement is issued?
  825.  
  826. A $0F8) 11 elements.  A(0) - A(10).  Almost everyone who has ever programmed 
  827.         in Commodore BASIC has seen the "BAD SUBSCRIPT" error when they try 
  828.         to use the 12th element in a un-DIMensioned array.
  829.  
  830. Q $0F9) How large is the keyboard buffer in CBM computers?
  831.  
  832. A $0F9) 10 bytes.  Since this area could be POKEd to, many boot programs
  833.         would poke characters into this buffer to simulate keypresses.
  834.  
  835. Q $0FA) On the Commodore 1581, how large is a physical sector in bytes?
  836.  
  837. A $0FA) A physical sector is 512 bytes in length.  Internally, the 1581
  838.         creates 2 256 "logical" sectors in a physical sector, to maintain
  839.         compatibility with older Commodore drives.
  840.  
  841. Q $0FB) You'll find BASIC 3.5 on the _____________ line of CBM computers.
  842.  
  843. A $0FB) The X64 series.  That includes the Commodore 16, the Commodore 116,
  844.         and the Commodore Plus/4.
  845.  
  846. Q $0FC) On the Commodore 1351 mouse, what registers in the Commodore
  847.         computer would the X and Y proportional information be read
  848.         from?
  849.  
  850. A $0FC) Even though you are looking for digital information (how far the
  851.         mouse has traveled since the last movement in a particular axis), 
  852.         the information is read from the "paddle" or potentiometer (POT)
  853.         registers.  On the C64, the POT registers are part of the SID
  854.         chip, and are at 54297 ($D419) for POTX, and 54298 ($D41A) for
  855.         POTY.
  856.  
  857. Q $0FD) What is the maximum size of a sequential file on a 1581 drive?
  858.  
  859. A $0FD) 802640 bytes.
  860.  
  861. Q $0FE) What flaw exists in the early Commodore 1670 modems?
  862.  
  863. A $0FE) When the 1670 modem was first introduced, it powered up in auto-
  864.         answer mode, which means it would answer incoming calls after
  865.         the phong rang.  You could turn this feature off through software
  866.         control, but if the power was reset, the modem would answer the
  867.         phone.  So many people complained to Commodore that CBM revised
  868.         the 1670 to include an extra DIP switch that turned this feature
  869.         off.
  870.  
  871. Q $0FF) What is the model number of the first modem for the VIC and C64?
  872.  
  873. A $0FF) The 1600 manual dial/manual answer 0-300 bps modem.  The author 
  874.         owns one, and used it for many years.  To operate, you must use
  875.         a phone with a detachable handset cord.  You dialed the number
  876.         on the phone, waited for the answer, unplugged the handset, and
  877.         plugged the cord into the 1600.  A switch toggled between using
  878.         originate or answer frequencies.  The 1600 was manufactured by
  879.         Anchor Automation for Commodore.  (As an aside, this unit claimed
  880.         300 bps, but I never could get 300 to work well.  Most of my
  881.         telecommunications happened at 150 bps.)
  882.  
  883.  
  884. -------Commodore Trivia Edition #17 Questions and Answers (BEGIN)--------
  885.  
  886. Q $100) On the MOS Technology's KIM-1, how many keys were on the keypad?
  887.  
  888. A $100) 23 keys.  The keypad has room for 24, but one spot is taken by
  889.         a switch that puts the system into single-step mode.  Interestingly,
  890.         some pictures have the switch on the upper left, some on the upper
  891.         right.
  892.  
  893. Q $101) The KIM-1 keypad had the common 0-9A-F keys on the keypad, but
  894.         also had some special keys.  Name them.
  895.  
  896. A $101) GO (Go) Executes an instruction and displays the address of next,
  897.         ST (Stop) Stops execution of program and return control to monitor, 
  898.         RS (Reset), 
  899.         AD (Address) Address entry mode, 
  900.         DA (Data) Data entry mode, 
  901.         PC (Program Counter) Displays and restores program counter to values
  902.                              in PCL and PCH,
  903.         +  (Increment) Increments the address without changing the entry mode.
  904.  
  905. Q $102) The KIM-1 was a set of modules that could be plugged together to
  906.         expand the system.  Each module had a model number.  What was the 
  907.         model number of the KIM-1 motherboard?
  908.  
  909. A $102) The KIM-4.  
  910.  
  911. Q $103) On the 1525 line of printers, if you wanted to create the following
  912.         graphic, what bytes would you send to the printer after turning on
  913.         graphics mode?
  914.         
  915.         ****
  916.         *  *
  917.         *  *
  918.         *  *
  919.         *  *
  920.         *  *
  921.         ****
  922.  
  923. A $103) I guess I should have stipulated that this is a bitmap.  ASCII just
  924.         has a few limitations.  Anyway, the correct bytes to send are:
  925.         255, 193, 193, 255.  You got these by assigning each bit in a column
  926.         a value, and adding 128 to the result for each column.  
  927.  
  928. Q $104) What is the horizontal resolution of the 1525 line of printers?
  929.  
  930. A $104) Character resolution:   80 chars, or 10 chars/inch (cpi).
  931.         Graphics resolution:    480 dots, or 60 dots/inch (dpi).  
  932.  
  933. Q $105) On Commodore drives, explain the difference between the B-R command
  934.         and the U1 command.
  935.  
  936. A $105) The two commands read in data from a disk sector.  However, the 
  937.         U1 command always reads a full sector (255 bytes).  The B-R
  938.         command reads the number of bytes specified in the first byte of
  939.         the sector.  If the first byte is a 15, B-R will read 15 bytes
  940.         from the sector.  (From the 1581 manual)
  941.  
  942. Q $106) On the Commodore 1541 drive, what does the U: command do?
  943.  
  944. A $106) This command has been traditionally used to reset Commodore drives,
  945.         including the CBM 1541.  However, some early versions of the Drive
  946.         DOS did not correctly handle this command.  In these versions, the 
  947.         drive and computer failed to complete the command transaction 
  948.         successfully, and what looked like a hung machine resulted.  
  949.         Commodore later fixed this problem.  If U: seems to not work on
  950.         your drive, try U; instead.  
  951.  
  952. Q $107) What does the first routine in the 1541 drive ROM actually do?
  953.  
  954. A $107) The function, called SETLDA and residing at $C100, turns on the
  955.         drive active LED for the current drive.  The routine loads the
  956.         current drive from $7F and sets bit 3 of DSKCNT ($1C00).
  957.  
  958. Q $108) How many files will a 1581 disk drive hold?
  959.  
  960. A $108) 296 files.  Note that it is not a multiple of 144.  
  961.  
  962. Q $109) Commodore 1581 drives have a special "autoboot" feature that enables
  963.         the drive to load and run a program off a disk upon drive bootup.
  964.         What is the required name of the file?
  965.  
  966. A $109) COPYRIGHT CBM 86
  967.  
  968. Q $10A) What filetype must the file mentioned in $109 be?
  969.  
  970. A $10A) USR.
  971.  
  972. Q $10B) To power up a 1351 mouse in "joystick mode", what must the user do?
  973.  
  974. A $10B) If one depresses the right mouse button during power-up, the 1351
  975.         will behave just like a joystick.
  976.  
  977. Q $10C) Describe the contents of the POTX or POTY registers when using a
  978.         1351 mouse.
  979.  
  980. A $10C) Each register holds the same type of information, just for a 
  981.         separate axis, so we will describe just one register:
  982.         
  983.         Bit:   Function
  984.         
  985.         7      Don't care
  986.         6-1    Mouse axis position mod 64.
  987.         0      Noise Bit. (check this bit to see whether mouse has moved)
  988.  
  989. Q $10D) Commodore computers typically use most of zero page for temporary
  990.         variables and other items.  However, both the VIC-20 and the 64
  991.         reserve 4 bytes for user programs that need zero page memory.  Where
  992.         are these locations?
  993.  
  994. A $10D) $FB-$FE (251-254).  I am not sure these were "reserved" for 
  995.         programmers as much as they were just not utilized by the 
  996.         CBM programmers.  
  997.  
  998. Q $10E) Name the 16 colors available on the 64.
  999.  
  1000. A $10E) Black
  1001.         White
  1002.         Red
  1003.         Cyan (Light Blue-Green)
  1004.         Purple
  1005.         Green
  1006.         Blue
  1007.         Yellow
  1008.         Orange
  1009.         Brown
  1010.         Light Red
  1011.         Dark Gray (Gray 1)
  1012.         Medium Grey (Gray 2)
  1013.         Light Green 
  1014.         Light Blue
  1015.         Light Gray (Gray 3)
  1016.                          
  1017. Q $10F) Both the VIC-20 and the C64 emulate the operation of the 6551 UART.
  1018.         How many "mock 6551" registers are mapped into the memory map?
  1019.  
  1020. A $10F) 5, from $293-$297 (659-663).  The register contents:
  1021.  
  1022.         $293   6551 Control Register
  1023.         $294   6551 Command Register
  1024.         $295-6 6551 User Defined Baud Rate value.
  1025.         $297   6551 Status Register 
  1026.  
  1027.  
  1028. ------------Commodore Trivia Edition #18 Questions (BEGIN)--------------
  1029.  
  1030. Q $110) What is the name of the company that recently purchased the
  1031.         liquidated Commodore assets?
  1032.  
  1033. Q $111) At one time, Commodore attempted to manufacture a dual drive
  1034.         version of the 1571 called the 1572.  For what technical reason
  1035.         did it utimately fail?
  1036.  
  1037. Q $112) Over what computer system did a User Group sue Commodore and win?
  1038.  
  1039. Q $113) In $103, the question asked how to create a graphic of a small box
  1040.         on the 1525.  In this quesrtion, we have made a different design.
  1041.         If you wanted to create the following graphic using individual
  1042.         dots on the printer, what bytes would you send to the printer after
  1043.         turning on graphics mode?
  1044.         
  1045.          **     * *
  1046.         *       ***
  1047.         *   **  ***
  1048.         *   * * * *
  1049.          ** **  * *
  1050.             * *
  1051.             **
  1052.  
  1053. Q $114) (Some C65 questions)  How many SID chips does the the development
  1054.         Commodore 65 machine contain?
  1055.  
  1056. Q $115) What CPU does the Commodore 65 use?
  1057.  
  1058. Q $116) What is the alternate name for the Commodore 65?
  1059.  
  1060. Q $117) How many processors does the internal 1581-compatible drive 
  1061.         on the C65 contain?
  1062.  
  1063. Q $118) In the tradition of naming certian ICs after famous cartoon
  1064.         characters, one of the ICs in the C65 is named after a Warner
  1065.         Brothers cartoon character.  Which one?
  1066.  
  1067. Q $119) What version of BASIC is included on the Commodore 65 in C65 mode?
  1068.  
  1069. Q $11A) How many I/O ports does a Commodore 65 contain?
  1070.  
  1071. Q $11B) What common Commodore 64 I/O port does the C65 NOT have?
  1072.  
  1073. Q $11C) How many function keys are on a Commodore 65?
  1074.  
  1075. Q $11D) What CBM disk drive DOS was used as the template for the internal
  1076.         C65 drive DOS?
  1077.  
  1078. Q $11E) What resolution of text screen does the C65 power up in? (Please
  1079.         give answers in characters).
  1080.  
  1081. Q $11F) What distinguishing non-textual characteristic in the C65 is not
  1082.         present in othe Commodore 8-bit computers? 
  1083.  
  1084. The information in this between the lines marked by (BEGIN) and (END)
  1085. is copyright 1995 by Jim Brain.  Provided that the information
  1086. between the (BEGIN) and (END) lines is not changed except to correct
  1087. typographical errors, the so marked copyrighted information may be
  1088. reproduced in its entirety on other networks or in other mediums.  For 
  1089. more information about using this file, please contact the address 
  1090. shown below.
  1091.  
  1092. Jim Brain
  1093. brain@mail.msen.com 
  1094. 602 North Lemen
  1095. Fenton, MI  48430
  1096. (810) 737-7300 x8528
  1097.  
  1098. Some are easy, some are hard, try your hand at:
  1099.     Commodore Trivia #18!
  1100. ========================================================================
  1101. BFLI - New graphics modes 2
  1102. by Pasi 'Albert' Ojala <albert@cs.tut.fi>
  1103.  
  1104. One day I was watching some demos that used linecrunch routines for
  1105. whole-screen multicolor-graphics upscrollers.  I already had my
  1106. theories about how and why linecrunch worked, but because I had not
  1107. used it anywhere, the details were a bit vague.  In fact, I have
  1108. many times accidentally created linecrunch effects when trying to do
  1109. something else with $D011.  Probably every demo coder has.
  1110.  
  1111. But you learn by doing.  I had the idea of using linecrunch for FLI
  1112. instead of a simple multicolor picture as it always seemed to be
  1113. used.  However, this has probably been done before and because I
  1114. don't like to do things that have been done before, I decided to use
  1115. linecrunch to show a two-screen-tall FLI picture.
  1116.  
  1117.  
  1118. _Linecrunch Basics_
  1119.  
  1120. For those not familiar with linecrunch routines:  linecrunch is used
  1121. to scroll the screen UPWARDS by convincing VIC-II that it has
  1122. already showed more character rows than it in reality has shown.
  1123. Surprisingly (or then, maybe not :) this consists of fiddling with
  1124. $D011.  The timing is critical as always.
  1125.  
  1126. Linecrunch works by setting $D011 equal the line before the current
  1127. line and VIC-II will happily think that it is time to move on to the
  1128. next character row - add 40 to the video matrix counter, 320 to the
  1129. graphics memory counter and be ready to start a bad line.  Or, maybe
  1130. 'NOT to go back to the current row' would be a more suitable
  1131. description.  (Programming VIC-II is slowly becoming a science.)
  1132.  
  1133. The required timing also does not cause bad lines so that you can
  1134. skip another line immediately on the successive line.  In addition,
  1135. lines can be skipped only after the first character row and half of
  1136. the second character row have been displayed.  This has something to
  1137. do with the way VIC-II decides when there is a bad line.
  1138.  
  1139. Because linecrunch causes VIC-II to skip rows, it will run out of
  1140. video matrix and color memory (and graphics memory) before reaching
  1141. the end of the screen.  However, VIC-II does not stop displaying the
  1142. graphics nor does it reset the internal counters.  The counters keep
  1143. on running and wrap around instead.
  1144.  
  1145. Normally, when VIC-II is displaying the last character row, it is
  1146. showing the memory from offsets $3c0 to $3e7.  If VIC-II has skipped
  1147. one character row, it is displaying from $3e8 to $40f instead.  But,
  1148. there are only 10 bits for the video matrix counter (0..1023), so it
  1149. wraps around to zero after $3ff.  This means that the beginning of
  1150. the video matrix is displayed at the bottom of the screen.  The
  1151. character rows become shifted by 24 character positions to the right
  1152. because there were originally 24 unused memory locations at the end
  1153. of the memory (1000..1023).  (To be honest, sprite image pointers
  1154. are not unused memory, but they are not used with normal FLI.)
  1155.  
  1156.          ____________________    ____________________
  1157.         |abcdefghijklmnopqrst|  |abcdefghijklmnopqrst|
  1158.         |                    |  |--------------------| <- Skipped row
  1159.         :                    :  :                    :
  1160.         :                    :  :                    :
  1161.         :                    :  :                    :
  1162.         |                    |  |normally last line  |
  1163.         |normally last line  |  |XXXXXXXXZZZZabcdefgh|
  1164.         `--------------------'  `--------------------'
  1165.                                 X = unused mem      (1000..1015)
  1166.                                 Z = sprite pointers (1016..1023)
  1167.  
  1168.         Figure 1: Linecrunch
  1169.  
  1170.  
  1171. The same thing happens for color memory because it uses the same
  1172. counter for addressing the memory (in fact, color memory access and
  1173. character data access are performed simultaneosly, 12 bits at a
  1174. time).  The graphics memory behaves the same way, except that the
  1175. counter has three bits more and it counts at eight times the speed,
  1176. so that it wraps at the exact same time as the other counter.
  1177.  
  1178. The first character row can't be used for linecrunch and the second
  1179. one is also lost in the process.  The first usable line to display
  1180. is the third character row.  However, those two lost rows can still
  1181. be used as an extension at the end of the first screen.  You must
  1182. notice, however, that the alignment has been changed.  After these
  1183. two rows have been displayed, the video bank is switched to get new
  1184. fresh data on the screen.
  1185.  
  1186.  
  1187. _Back to BFLI_
  1188.  
  1189. Wrapped data is nothing difficult to work with.  It is just the
  1190. matter of writing the right conversion program.  Also, the normal
  1191. FLI routine can be used, we just have to make sure VIC always has
  1192. the right bank visible - simple LDA bank,x:sta $DD00 can accomplish
  1193. that.  The more difficult aspect is to make the display freely
  1194. locatable.  We have 32 kilobytes of graphics data, this is the main
  1195. reason we can't even think about using copying.  Linecrunch combined
  1196. with the bad line delaying technique will do the job much more
  1197. nicely.
  1198.  
  1199. Figure 2 shows the principles.  To make things simpler I have chosen
  1200. location 0 to mean that the top of the picture is visible, 1 means
  1201. that the picture is scrolled one line upwards and so on.  We can see
  1202. that linecrunch is not used at all for the location 0.  To make the
  1203. picture start at the same point whether linecrunch has crunched
  1204. lines or not we compensate the non-lost raster lines by delaying the
  1205. next bad line.  When the location is n*8 (n=0,1,2..), the sum of the
  1206. linecrunched and delayed lines is constant - the graphics display
  1207. always starts at the same point.
  1208.  
  1209. Then how do we deal with the location values that are not evenly
  1210. dividable by eight ?  Now, lets assume that the location is L, and
  1211. we have C, which is the location divided by eight (C = L/8), and R,
  1212. which is the remainder (R = L%8).  To make the picture scroll to the
  1213. right position, we need to delay the bad line less than before - R
  1214. lines less for location L than for location C*8.  E.g.  for location
  1215. 2 we delay the bad line two lines less than for location 0.  This
  1216. also shows that we need 7 lines more than is needed for to
  1217. compensate for the linecrunch.
  1218.  
  1219. Determining the number of linecrunch lines is a recursive process,
  1220. because when you use more linecrunch lines, that decreases the
  1221. number of lines you have available for the display and you need
  1222. bigger range for the location value.  The linecrunch can be started
  1223. after 12 lines, and we need at least 7 lines to use the soft
  1224. y-scroll.  This makes 181 lines available for the display
  1225. originally.
  1226.  
  1227. Because we need to show 400 lines of graphics, we would need
  1228. (400-181)/8=28 linecrunch lines.  However, this in turn reduces the
  1229. number of lines we have for graphics to 181-28=153 and we need
  1230. (400-153)/8=31 linecrunch lines.  Again, 181-31 is 150.  We get
  1231. (400-150)/8=32 and there it finally converges and we have 149 lines
  1232. for graphics, which makes location values 0..251 valid.
  1233.  
  1234.  
  1235. Location        0       1       2  ..   8       9  ..   251
  1236.  
  1237.                 ___________________..   ___________..   ________
  1238.                 ___________________..   ___________..   ________
  1239. Linecrunch      -------------------..   ___________..
  1240.                 ^       ^       ^
  1241.                 |       |       |       ^       ^
  1242.                 |       |       |       |       |
  1243. Bad line delayed|       |       |       |       |
  1244.                 |       |       |       |       |       ========
  1245.                 |       |       v       |       |       244
  1246.                 |       v       ___..   |       v       :
  1247.                 v       ________0       v       ___..   :
  1248. Gfx Enabled     ________0_______1__..   ________8__..   250_____
  1249.                 0       1       2       8       9       251
  1250.                 1       2       3       9       10      252
  1251.                 2       3       4       10      11      253
  1252.                 3       4       5       11      12      254
  1253.                 4       5       6       12      13      255
  1254.                 5       6       7       13      14      256
  1255.                 6       7       8       14      15      257
  1256.                 7       8       9       15      16      258
  1257.                 :       :       :       :       :       :
  1258.                 :       :       :       :       :       :
  1259.                 148     149     150..   156     157..   399
  1260.  
  1261.         Figure 2: Linecrunch and DMA delay in BFLI
  1262.                   (Graphics lines not in scale)
  1263.  
  1264.  
  1265. _Clipping added_
  1266.  
  1267. Now we can scroll the picture to any location we want, but the top
  1268. of the picture is not clipped and it is very annoying to watch.  We
  1269. need to enable the graphics at the same point regardless of the
  1270. y-scroll value.  The answer is in the extended color mode (ECM).
  1271.  
  1272. When both ECM and multicolor mode (MCM) are selected, VIC-II will
  1273. turn the display to black.  This is because there is a conflicting
  1274. situation and it just can't decide which color scheme to use.  The
  1275. video accesses will continue to happen just like before, the data is
  1276. just not displayed.  When the ECM bit is cleared again, the normal
  1277. multicolor graphics is shown.
  1278.  
  1279. So, we set the ECM bit and start to display the first eight lines of
  1280. the FLI.  Because the FLI routine already writes to $D011, we just
  1281. make sure the ECM bit is set in the first R number of writes to
  1282. $D011 and zero in all other.
  1283.  
  1284. The viewer is now 'complete'.  You can take a look at the code below
  1285. or you can get C64Gfx1_4.lha and see it in action yourself and not
  1286. just rely on my word.  The package includes converter programs for
  1287. BFLI, FLI and Koala (ANSI-C), couple of example pictures and viewers
  1288. for PAL and NTSC machines.
  1289.  
  1290. -Pasi 'Albert' Ojala    albert@cs.tut.fi
  1291.  
  1292. --------------------------------------------------------------------------
  1293.  
  1294. BFLI viewer program for PAL machines
  1295.  
  1296. UPOS   = $C00   ; temporary area for tables
  1297. BANK   = $D00   ;  UPOS for linecrunch, BANK for FLI bank select
  1298. RASTER = 29     ; where to position the sprite -> IRQ 20 lines later
  1299. DUMMY  = $FFF   ; dummy location for timing purposes
  1300. FLISZ  = 19-1   ; visible FLI size in character rows - 1
  1301.  
  1302. *= $810
  1303.         SEI
  1304.         LDA #$7F:STA $DC0D      ; IRQ setup
  1305.         LDA #1:STA $D01A
  1306.         STA $D015:STA KEYW+1
  1307.         LDA #<IRQ:STA $314
  1308.         LDA #>IRQ:STA $315
  1309.         LDA #RASTER:STA $D001:CLC:ADC #20:STA $D012
  1310.         LDA #0:STA $D017
  1311.         LDA #0:STA 2
  1312.         JSR NEWPOS              ; Init the FLI routines
  1313.         LDA #$A:STA $D011       ; Blank screen
  1314.         LDX #23                 ; Init tables
  1315. BLOOP   LDA #$94:STA BANK,X
  1316.         LDA #$96:STA BANK+24,X
  1317.         DEX:BPL BLOOP
  1318.         LDX #15
  1319. LOOP0   LDA YINIT,X:AND #$77    ; Change to $37 to better see the
  1320.         STA UPOS,X              ;  workings of the routines
  1321.         STA UPOS+16,X
  1322.         STA UPOS+32,X
  1323.         DEX:BPL LOOP0
  1324.  
  1325.         LDA #$34:STA 1          ; Copy to the last video bank
  1326.         LDA #$80:STA SRC+2      ; from $8000-$BFFF to $C000-$FFFF
  1327.         LDA #$C0:STA DST+2
  1328.         LDX #0:LDY #$40
  1329. SRC     LDA $8000,X
  1330. DST     STA $C000,X
  1331.         INX:BNE SRC
  1332.         INC SRC+2:INC DST+2
  1333.         DEY:BNE SRC
  1334.         LDA #$37:STA 1
  1335.  
  1336.         LDX #0                  ; Init color memory
  1337. LP      LDA $3C00,X:STA $D800,X ; All 1024 bytes are used
  1338.         LDA $3D00,X:STA $D900,X ;  - some even twice!
  1339.         LDA $3E00,X:STA $DA00,X
  1340.         LDA $3F00,X:STA $DB00,X
  1341.         INX:BNE LP
  1342.         LDA $DC0D:CLI
  1343.  
  1344. KEYW    LDX #0:BNE KEYW         ; Wait for space to be pressed
  1345.         SEI                     ; System to normal
  1346.         LDA #$37:STA 1
  1347.         JSR $FDA3
  1348.         LDA #$97:STA $DD00
  1349.         JSR $E5A0
  1350.         LDY #3
  1351. IRQL    LDA $FD30,Y:STA $314,Y
  1352.         DEY:BPL IRQL
  1353.  
  1354.         LDX #0:LDA #1           ; Clear color memory
  1355. CLL     STA $D800,X:STA $D900,X
  1356.         STA $DA00,X:STA $DB00,X
  1357.         INX:BNE CLL
  1358.         CLI:RTS
  1359.  
  1360. YINIT   BYT $78,$79,$7A,$7B,$7C,$7D,$7E,$7F
  1361.         BYT $78,$79,$7A,$7B,$7C,$7D,$7E,$7F
  1362.  
  1363. *=*-<*+256
  1364.  
  1365. IRQ     LDA #$18:STA $D016:LDX #0:LDA #$5A
  1366.         INC DUMMY:DEC DUMMY             ; Synchronization
  1367.         STX $D020:STX $D021:STA $D011
  1368.  
  1369.         LDA #$15:STA $D018
  1370.         LDA #$97:STA $DD00
  1371.         LDX #44                 ; Wait for the 4th line
  1372. LL      DEX:BPL LL:NOP
  1373.         LDX #0
  1374.  
  1375. LOOP3   NOP                     ; Linecrunch-part routine
  1376.         LDA UPOS+6,X:INC DUMMY:STA $D011
  1377.         NOP:NOP:INC DUMMY
  1378.         NOP:NOP:NOP:NOP:NOP
  1379.         NOP:NOP:NOP:NOP:NOP
  1380.         NOP:NOP:NOP:NOP:NOP
  1381.         INX
  1382. E1      CPX #$10:BNE LOOP3      ; Skip that many character rows-4
  1383.         BIT $EA
  1384. LOOP4   LDA UPOS,X:INC DUMMY:STA $D011
  1385.         NOP:NOP:NOP:INC DUMMY
  1386.         NOP:NOP:NOP:NOP:NOP
  1387.         NOP:NOP:NOP:NOP:NOP
  1388.         NOP:NOP:NOP:NOP:LDA #0
  1389.         INX
  1390. E2      CPX #$1F:BNE LOOP4      ; Delay DMA until we are at the
  1391.                                 ;  'same place' each time
  1392.  
  1393.         LDA #0:STA $D020        ; Now wait for the bad line and start FLI
  1394.         BIT $EA:NOP
  1395.         NOP:NOP:NOP:NOP
  1396.         NOP:NOP:NOP:NOP
  1397.         NOP:NOP:NOP:NOP
  1398. B0      LDA #$92:STA $DD00:NOP  ; The right video bank
  1399.  
  1400.         ; Wait for 0-7 lines to set the ECM mode off
  1401.         ;  (makes the graphics visible)
  1402.  
  1403. F0      LDA #0:STA $D011:LDA #$08:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
  1404. F1      LDA #0:STA $D011:LDA #$18:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
  1405. F2      LDA #0:STA $D011:LDA #$28:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
  1406. F3      LDA #0:STA $D011:LDA #$38:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
  1407. F4      LDA #0:STA $D011:LDA #$48:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
  1408. F5      LDA #0:STA $D011:LDA #$58:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
  1409. F6      LDA #0:STA $D011:LDA #$68:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
  1410. F7      LDA #0:STA $D011:LDA #$78:STA $D018
  1411.         LDX #FLISZ:NOP:NOP:NOP:BIT $EA
  1412.  
  1413.         ; Do FLI 18 more character rows
  1414.  
  1415. F8      LDA #0:STA $D011:LDA #$08:STA $D018
  1416. B1      LDA BANK,X:STA $DD00:BIT $EA
  1417. F9      LDA #0:STA $D011:LDA #$18:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
  1418. FA      LDA #0:STA $D011:LDA #$28:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
  1419. FB      LDA #0:STA $D011:LDA #$38:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
  1420. FC      LDA #0:STA $D011:LDA #$48:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
  1421. FD      LDA #0:STA $D011:LDA #$58:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
  1422. FE      LDA #0:STA $D011:LDA #$68:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
  1423. FF      LDA #0:STA $D011:LDA #$78:STA $D018:NOP:NOP:DEX:BMI EFLI:JMP F8
  1424. EFLI    NOP
  1425.         LDA #$FC
  1426. WL      CMP $D012:BNE WL
  1427.         INC DUMMY:INC DUMMY:INC DUMMY:INC DUMMY
  1428.         INC DUMMY:INC DUMMY:INC DUMMY:INC DUMMY
  1429.         INC DUMMY:INC DUMMY:STA $D020
  1430.  
  1431.         JSR NEWPOS      ; Update the location
  1432.         JSR CHPOS       ; Change to a new location
  1433.         LDA $DC01:AND #$10:BNE OV3      ; Check for the space bar
  1434.         LDA #0:STA KEYW+1
  1435. OV3     LDX #$53:STX $D011:INC $D019:JMP $EA81
  1436.  
  1437. NEWPOS  LDA #0          ; Init the IRQ routine for this position
  1438.         LSR:LSR:LSR:CLC:ADC #4:STA E1+1
  1439.         LDA #7:SEC:SBC NEWPOS+1:AND #7:TAX:TAY:CLC:ADC #35:STA E2+1
  1440.         LDA UPOS+3+7,Y:DEX:BMI J0:AND #$3F
  1441. J0      STA F7+1:AND #$3F:STA FF+1
  1442.         LDA UPOS+3+6,Y:DEX:BMI J1:AND #$3F
  1443. J1      STA F6+1:AND #$3F:STA FE+1
  1444.         LDA UPOS+3+5,Y:DEX:BMI J2:AND #$3F
  1445. J2      STA F5+1:AND #$3F:STA FD+1
  1446.         LDA UPOS+3+4,Y:DEX:BMI J3:AND #$3F
  1447. J3      STA F4+1:AND #$3F:STA FC+1
  1448.         LDA UPOS+3+3,Y:DEX:BMI J4:AND #$3F
  1449. J4      STA F3+1:AND #$3F:STA FB+1
  1450.         LDA UPOS+3+2,Y:DEX:BMI J5:AND #$3F
  1451. J5      STA F2+1:AND #$3F:STA FA+1
  1452.         LDA UPOS+3+1,Y:DEX:BMI J6:AND #$3F
  1453. J6      STA F1+1:AND #$3F:STA F9+1
  1454.         LDA UPOS+3+0,Y:DEX:BMI J7:AND #$3F
  1455. J7      STA F0+1:AND #$3F:STA F8+1
  1456.         LDA #$96:STA B0+1:LDA #199:SEC:SBC NEWPOS+1:BCC OV2
  1457.         LSR:LSR:LSR:CLC:ADC #5:STA B1+1
  1458.         RTS
  1459. OV2     LDA #0:STA B1+1:LDX #$94:STX B0+1:RTS
  1460.  
  1461. CHPOS   LDX NEWPOS+1
  1462.         LDA $DC00:TAY           ; Get joystick
  1463.         AND #$10:BNE DIR        ; If no button pressed
  1464.         TYA:AND #1:BEQ UP       ; If joy up
  1465.         TYA:AND #2:BEQ DOWN     ; If joy down
  1466.         RTS
  1467. DIR     LDA #0:BEQ UP
  1468. DOWN    DEX:CPX #$FF:BNE DOK
  1469.         LDX #0:STX DIR+1        ; Change direction
  1470. DOK     STX NEWPOS+1:RTS
  1471. UP      INX:CPX #$FD:BCC UOK    ; 251(locations)+149(visible)=400
  1472.         LDX #$FC:STX DIR+1      ; Change direction
  1473. UOK     STX NEWPOS+1:RTS
  1474.  
  1475.  
  1476. --------------------------------------------------------------------------
  1477.  
  1478. The BFLI file format:
  1479.  
  1480.                         File            BFLI Display
  1481.         Lines           Offset          Offset          Lines     Size
  1482. Colors  0-1.3           0..55           944..999        22.7-24   56
  1483.   I     1.3-2           56..79          -                         24
  1484.         2-24            80..999         0..919          0-22      920
  1485.         24-24.7         1000..1023      920..943        22-22.7   24
  1486.  
  1487.   II    0-1.3           0..55           1968..2024      49.3-50.6 56
  1488.         1.3-24.7        56..1023        1000..1967      24-49.3   968
  1489.  
  1490.  
  1491. Gfx     0-1.3           0..447          7552..7999      22.7-24   448
  1492.   I     1.3-2           448..639        -                         192
  1493.         2-24            640..7999       0..7359         0-22      7360
  1494.         24-24.7         8000..8191      7360..7551      22-22.7   192
  1495.  
  1496.   II    0-1.3           0..447          15744..16192    49.3-50.6 448
  1497.         1.3-24.7        448..8191       8000..15743     24-49.3   7744
  1498.  
  1499. ========================================================================
  1500. Making stable raster routines (C64 and VIC-20)
  1501. by Marko Makela (Marko.Makela@HUT.FI)
  1502.  
  1503. Preface
  1504.  
  1505. Too many graphical effects, also called raster effects, have been
  1506. coded in a very sloppy way.  For instance, if there are any color bars
  1507. on the screen in a game or demo, the colors often jitter a bit,
  1508. e.g. they are not stable.  And also, it is far too easy to make
  1509. virtually any demo crash by hitting the Restore key, or at least cause
  1510. visual distortions on the screen.
  1511.  
  1512. As late as a year ago I still hadn't coded a stable raster interrupt
  1513. routine myself.  But then I had to do it, since I was researching the
  1514. video chip timing details together with my German friend Andreas
  1515. Boose.  It was ashaming that we had the same level of knowledge when
  1516. it came to the hardware, but he was the only of us who had written a
  1517. stable raster routine.  Well, finally I made me to start coding.  I
  1518. used the same double-interrupt idea as Andreas used in his routine.
  1519.  
  1520. After a couple of errors my routine worked, and I understood how it
  1521. works exactly.  (This is something that separates us normal coders
  1522. from demo people: They often code by instinct; by patching the routine
  1523. until it works, without knowing exactly what is happening.  That's why
  1524. demos often rely on weird things, like crash if the memory is not
  1525. initialized properly.)
  1526.  
  1527. In this article, I document two methods of creating stable raster
  1528. routines on Commodore computers.  The principles apply for most 8-bit
  1529. computers, not only Commodores, but raster effects are very rarely
  1530. seen on other computers.
  1531.  
  1532.  
  1533. Background
  1534.  
  1535. What are raster effects?  They are effects, where you change the
  1536. screen appearance while it is being drawn.  For instance, you can set
  1537. the screen color to white in the top of the screen, and to black in
  1538. the middle of the screen.  In that way, you will get a picture whose
  1539. top half is white and bottom half black.  Normally such effects are
  1540. implemented with interrupt routines that are executed synchronized
  1541. with the screen refresh.
  1542.  
  1543. The video chip on the Commodore 64 and many other videochips have a
  1544. special interrupt feature called the Raster interrupt.  It will
  1545. generate an IRQ in the beginning of a specified raster line.  On other
  1546. computers, like the VIC-20, there is no Raster interrupt, but you can
  1547. generate the interrupts with a timer, provided that the timer and the
  1548. videochip are clocked from the same source.
  1549.  
  1550. Even if the processor gets an interrupt signal at the same position on
  1551. each video frame, it won't always be executing the first instruction
  1552. of the interrupt routine at the same screen position.  The NMOS 6502
  1553. machine instructions can take 2 to 9 machine cycles to execute, and if
  1554. the main program contains instructions of very varying lengths, the
  1555. beginning position of the interrupt can jump between 7 different
  1556. positions.  This is why you need to synchronize the raster routine
  1557. when doing serious effects.
  1558.  
  1559. Also, executing the interrupt sequence will take 7 additional cycles,
  1560. and the interrupt sequence will only start after the current
  1561. instruction if the interrupt arrived at least two cycles before the
  1562. end of the current instruction.  It is even possible that an interrupt
  1563. arrives while interrupts are disabled and the processor is just
  1564. starting to execute a CLI instruction.  Alas, the processor will not
  1565. jump to the interrupt right after the CLI, but it will execute the
  1566. next instruction before jumping to it.  This is natural, since the CLI
  1567. takes only two cycles.  But anyway, this is only a constant in our
  1568. equation, and actually out of the scope of this article.
  1569.  
  1570. How to synchronize a raster interrupt routine?  The only way is to
  1571. check the current screen position and delay appropriately many cycles.
  1572. There are several ways of doing this, some of which are very awful and
  1573. inefficient.  The ugliest ways of doing this on the Commodore 64 I
  1574. know are busy-waiting several raster lines and polling the raster line
  1575. value, or using the Light pen feature, which will fail if the user
  1576. presses the fire button on Joystick port 1.  Here I will present two
  1577. ways, both very elegant in my opinion.
  1578.  
  1579.  
  1580. Using an auxiliary timer
  1581.  
  1582. On the VIC-20, there is no Raster interrupt feature in the video chip.
  1583. All you can do is to use a timer for generating raster interrupts.
  1584. And if you use two timers running at a constant phase difference, you
  1585. can get full synchronization.  The first timer generates the raster
  1586. interrupt, and the second timer, the auxiliary timer, tells the raster
  1587. routine where it is running.  Actually you could even use the first
  1588. timer also for the checking, but the code will look nicer in the way I
  1589. will be presenting now.  Besides, you can use the auxiliary timer idea
  1590. even when real raster interrupts are available.
  1591.  
  1592. The major drawback of using an auxiliary timer is initializing it.
  1593. The initialization routine must synchronize with the screen, that is,
  1594. wait for the beginning of the wanted raster line.  To accomplish this,
  1595. the routine must first wait for a raster line that occurs a bit
  1596. earlier.  About the only way to do this is with a loop like
  1597.  
  1598.                 LDA #value
  1599.         loop    CMP raster
  1600.                 BNE loop
  1601.  
  1602. One round of this loop will take 4+3=7 cycles to execute, assuming
  1603. that absolute addressing is being used.  The loop will be finished if
  1604. the raster register contains the wanted value while the processor
  1605. reads it on the last cycle of the CMP instruction.  The raster
  1606. register can actually have changed already on the first cycle of the
  1607. BNE instruction on the previous run of the loop, that is 7 cycles
  1608. earlier!
  1609.  
  1610. Because of this, the routine must poll the raster register for several
  1611. raster lines, always consuming one cycle more if the raster register
  1612. changed too early.  As the synchronization can be off at most by 7
  1613. cycles, a loop of 7 raster register value changes would do, but I made
  1614. the loop a bit longer in my VIC-20 routine.  (Well, I have to admit it,
  1615. I was too lazy to make it work only with 7 rounds.)
  1616.  
  1617. After the initialization routine is fully synchronized the screen, it
  1618. can set up the timer(s) and interrupts and exit.  The auxiliary timer
  1619. in my VIC-20 demo routine is several dozens of cycles after the
  1620. primary timer, see the source code for comments.  It is arranged so
  1621. that the auxiliary timer will be at least 0 when it is being read in
  1622. the raster routine.  The raster routine will wait as many extra cycles
  1623. as the auxiliary timer reads, however at most 15 cycles.
  1624.  
  1625.  
  1626. Using double raster interrupt
  1627.  
  1628. On the Commodore 64, I have never seen the auxiliary timer scheme
  1629. being used.  Actually I haven't seen it being used anywhere, I was
  1630. probably the first one who made a stable raster interrupt routine on
  1631. the VIC-20.  Instead, the double interrupt method is becoming the
  1632. standard on the C64 side.
  1633.  
  1634. The double interrupt method is based entirely on the Raster interrupt
  1635. feature of the video chip.  In the first raster interrupt routine, the
  1636. program sets up another raster interrupt on a further line, changes
  1637. the interrupt vector and enables interrupts.
  1638.  
  1639. In the place where the second raster interrupt will occur, there will
  1640. be 2-byte instructions in the first interrupt routine.  In this way,
  1641. the beginning of the next raster interrupt will be off at most by one
  1642. cycle.  Some coders might not care about this one cycle, but if you
  1643. can do it right, why wouldn't you do it right until the end?
  1644.  
  1645. At the beginning of the second raster interrupt routine, you will read
  1646. the raster line counter register at the point where it is about to
  1647. change.  When the raster routine is being executed, there are two
  1648. possibilities: Either the raster counter has just changed, or it will
  1649. change on the next cycle.  So, you just need to compare if the
  1650. register changed one cycle too early or not, and delay a cycle when
  1651. needed.  This is easily accomplished with a branch to the next address.
  1652.  
  1653. Of course, somewhere in your second raster interrupt routine you must
  1654. restore the original raster interrupt position and set the interrupt
  1655. vector to point to the first interrupt routine.
  1656.  
  1657.  
  1658. Applying in practice
  1659.  
  1660. I almost forgot my complaints about demos crashing when you actively
  1661. hit the Restore key.  On the VIC-20, you can disable NMI interrupts
  1662. generated by the Restore key, and on the C64, you can generate an NMI
  1663. interrupt with the CIA2 timer and leave the NMI-line low, so that no
  1664. further high-to-low transitions will be recognized on the line.  The
  1665. example programs demonstrate how to do this.
  1666.  
  1667. So far, this article has been pretty theoretical.  To apply these
  1668. results in practice, you must definitely know how many CPU clock
  1669. cycles the video chip consumes while drawing a scan line.  This is
  1670. fairly easy to measure with a timer interrupt, if you patch the
  1671. interrupt handler so that it changes the screen color on each run.
  1672. Set the timer interval to LINES*COLUMNS cycles, where LINES is the
  1673. amount of raster lines and COLUMNS is your guess for the amount of
  1674. clock cycles spent in a raster line.
  1675.  
  1676. If your guess is right, the color will always be changed in the same
  1677. screen position (neglecting the 7-cycle jitter).  When adjusting the
  1678. timer, remember that the timers on the 6522 VIA require 2 cycles for
  1679. re-loading, and the ones on the 6526 CIA need one extra cycle.  Keep
  1680. trying different timer values until you the screen color changes at
  1681. one fixed position.
  1682.  
  1683. Commodore used several different values for LINES and COLUMNS on its
  1684. videochips.  They never managed to make the screen refresh rate
  1685. exactly 50 or 60 Hertz, but they didn't hesitate to claim that their
  1686. computers comply with the PAL-B or NTSC-M standards.  In the following
  1687. tables I have gathered some information of some Commodore video chips.
  1688.  
  1689.  
  1690.   NTSC-M systems:
  1691.  
  1692.             Chip      Crystal  Dot      Processor Cycles/ Lines/
  1693.     Host    ID        freq/Hz  clock/Hz clock/Hz  line    frame
  1694.     ------  --------  -------- -------- --------- ------- ------
  1695.     VIC-20  6560-101  14318181  4090909   1022727      65    261
  1696.     C64     6567R56A  14318181  8181818   1022727      64    262
  1697.     C64     6567R8    14318181  8181818   1022727      65    263
  1698.  
  1699.   Later NTSC-M video chips were most probably like the 6567R8.  Note
  1700.   that the processor clock is a 14th of the crystal frequency on all
  1701.   NTSC-M systems.
  1702.  
  1703.   PAL-B systems:
  1704.  
  1705.             Chip      Crystal  Dot      Processor Cycles/ Lines/
  1706.     Host    ID        freq/Hz  clock/Hz clock/Hz  line    frame
  1707.     ------  --------  -------- -------- --------- ------- ------
  1708.     VIC-20  6561-101   4433618  4433618   1108405      71    312
  1709.     C64     6569      17734472  7881988    985248      63    312
  1710.  
  1711.   On the PAL-B VIC-20, the crystal frequency is simultaneously the dot
  1712.   clock, which is BTW a 4th of the crystal frequency used on the C64.
  1713.   On the C64, the crystal frequency is divided by 18 to generate the
  1714.   processor clock, which in turn is multiplied by 8 to generate the
  1715.   dot clock.
  1716.  
  1717.   The basic timings are the same on all 6569 revisions, and also on
  1718.   any later C64 and C128 video chips.  If I remember correctly, these
  1719.   values were the same on the C16 videochip TED as well.
  1720.  
  1721. Note that the dot clock is 4 times the processor clock on the VIC-20,
  1722. and 8 times that on the C64.  That is, one processor cycle is half a
  1723. character wide on the VIC-20, and a full character on a C64.  I don't
  1724. have exact measurements of the VIC-20 timing, but it seems that while
  1725. the VIC-20 videochips draw the characters on the screen, it first
  1726. reads the character code, and then, on the following video cycle, the
  1727. appearance on the current character line.  There are no bad lines,
  1728. like on the C64, where the character codes (and colors) are fetched on
  1729. every 8th raster line.
  1730.  
  1731. Those ones who got upset when I said that Commodore has never managed
  1732. to make a fully PAL-B or NTSC-M compliant 8-bit computer should take a
  1733. closer look at the "Lines/frame" columns.  If that does not convince
  1734. you, calculate the raster line rate and the screen refresh rate from
  1735. the values in the table and see that they don't comply with the
  1736. standards.  To calculate the line rate, divide the processor clock
  1737. frequency by the amount of cycles per line.  To get the screen refresh
  1738. rate, divide that frequency by the amount of raster lines.
  1739.  
  1740.  
  1741. The Code
  1742.  
  1743. OK, enough theory and background.  Here are the two example programs,
  1744. one for the VIC-20 and one for the C64.  In order to fully understand
  1745. them, you need to know the exact execution times of NMOS 6502
  1746. instructions.  (All 8-bit Commodore computers use the NMOS 6502
  1747. processor core, except the C65 prototype, which used a inferior CMOS
  1748. version with all nice poorly-documented features removed.)  You should
  1749. check the 64doc document, available on my WWW pages at
  1750. http://www.hut.fi/~msmakela/cbm/emul/x64/64doc.html, or via FTP at
  1751. ftp.funet.fi:/pub/cbm/documents/64doc.  I can also e-mail it to you on
  1752. request.
  1753.  
  1754. Also, I have written a complete description of the video timing on the
  1755. 6567R56A, 6567R8 and 6569 video chips, which could maybe be turned
  1756. into another C=Hacking article.  The document is currently partially
  1757. in English and partially in German.  The English part is available
  1758. from ftp.funet.fi as /pub/cbm/documents/pal.timing, and I can send
  1759. copies of the German part (screen resolution, sprite disturbance
  1760. measurements, and more precise timing information) via e-mail.
  1761.  
  1762. The code is written for the DASM assembler, or more precisely for a
  1763. extended ANSI C port of it made by Olaf Seibert.  This excellent
  1764. cross-assembler is available at ftp.funet.fi in /pub/cbm/programming.
  1765.  
  1766. First the raster demo for the VIC-20.  Note that on the VIC-20, the
  1767. $9004 register contains the upper 8 bits of the raster counter.  So,
  1768. this register changes only on every second line.  I have tested the
  1769. program on my 6561-101-based VIC-20, but not on an NTSC-M system.
  1770.  
  1771. It was hard to get in contact with NTSC-M VIC-20 owners.  Daniel
  1772. Dallmann, who has a NTSC-M VIC-20, although he lives in Germany, ran
  1773. my test to determine the amount of cycles per line and lines per frame
  1774. on the 6560-101.  Unfortunately, the second VIA of his VIC-20 is
  1775. partially broken, and because of this, this program did not work on
  1776. his computer.  Craig Bruce ran the program once, and he reported that
  1777. it almost worked.  I corrected a little bug in the code, so that now
  1778. the display should be stable on an NTSC-M system, too.  But the actual
  1779. raster effect, six 16*16-pixel boxes centered at the top border, are
  1780. very likely to be off their position.
  1781.  
  1782.  
  1783.   processor 6502
  1784.  
  1785. NTSC    = 1
  1786. PAL     = 2
  1787.  
  1788. ;SYSTEM = NTSC  ; 6560-101: 65 cycles per raster line, 261 lines
  1789. SYSTEM  = PAL   ; 6561-101: 71 cycles per raster line, 312 lines
  1790.  
  1791. #if SYSTEM & PAL
  1792. LINES = 312
  1793. CYCLES_PER_LINE = 71
  1794. #endif
  1795. #if SYSTEM & NTSC
  1796. LINES = 261
  1797. CYCLES_PER_LINE = 65
  1798. #endif
  1799. TIMER_VALUE = LINES * CYCLES_PER_LINE - 2
  1800.  
  1801.   .org $1001    ; for the unexpanded Vic-20
  1802.  
  1803. ; The BASIC line
  1804.  
  1805. basic:
  1806.   .word 0$      ; link to next line
  1807.   .word 1995    ; line number
  1808.   .byte $9E     ; SYS token
  1809.  
  1810. ; SYS digits
  1811.  
  1812.   .if (* + 8) / 10000
  1813.   .byte $30 + (* + 8) / 10000
  1814.   .endif
  1815.   .if (* + 7) / 1000
  1816.   .byte $30 + (* + 7) % 10000 / 1000
  1817.   .endif
  1818.   .if (* + 6) / 100
  1819.   .byte $30 + (* + 6) % 1000 / 100
  1820.   .endif
  1821.   .if (* + 5) / 10
  1822.   .byte $30 + (* + 5) % 100 / 10
  1823.   .endif
  1824.   .byte $30 + (* + 4) % 10
  1825. 0$:
  1826.   .byte 0,0,0   ; end of BASIC program
  1827.  
  1828. start:
  1829.   lda #$7f
  1830.   sta $912e     ; disable and acknowledge interrupts
  1831.   sta $912d
  1832.   sta $911e     ; disable NMIs (Restore key)
  1833.  
  1834. ;synchronize with the screen
  1835. sync:
  1836.   ldx #28       ; wait for this raster line (times 2)
  1837. 0$:
  1838.   cpx $9004
  1839.   bne 0$        ; at this stage, the inaccuracy is 7 clock cycles
  1840.                 ; the processor is in this place 2 to 9 cycles
  1841.                 ; after $9004 has changed
  1842.   ldy #9
  1843.   bit $24
  1844. 1$:
  1845.   ldx $9004
  1846.   txa
  1847.   bit $24
  1848. #if SYSTEM & PAL
  1849.   ldx #24
  1850. #endif
  1851. #if SYSTEM & NTSC
  1852.   bit $24
  1853.   ldx #21
  1854. #endif
  1855.   dex
  1856.   bne *-1       ; first spend some time (so that the whole
  1857.   cmp $9004     ; loop will be 2 raster lines)
  1858.   bcs *+2       ; save one cycle if $9004 changed too late
  1859.   dey
  1860.   bne 1$
  1861.                 ; now it is fully synchronized
  1862.                 ; 6 cycles have passed since last $9004 change
  1863.                 ; and we are on line 2(28+9)=74
  1864.  
  1865. ;initialize the timers
  1866. timers:
  1867.   lda #$40      ; enable Timer A free run of both VIAs
  1868.   sta $911b
  1869.   sta $912b
  1870.  
  1871.   lda #<TIMER_VALUE
  1872.   ldx #>TIMER_VALUE
  1873.   sta $9116     ; load the timer low byte latches
  1874.   sta $9126
  1875.  
  1876. #if SYSTEM & PAL
  1877.   ldy #7        ; make a little delay to get the raster effect to the
  1878.   dey           ; right place
  1879.   bne *-1
  1880.   nop
  1881.   nop
  1882. #endif
  1883. #if SYSTEM & NTSC
  1884.   ldy #6
  1885.   dey
  1886.   bne *-1
  1887.   bit $24
  1888. #endif
  1889.  
  1890.   stx $9125     ; start the IRQ timer A
  1891.                 ; 6560-101: 65 cycles from $9004 change
  1892.                 ; 6561-101: 77 cycles from $9004 change
  1893.   ldy #10       ; spend some time (1+5*9+4=55 cycles)
  1894.   dey           ; before starting the reference timer
  1895.   bne *-1
  1896.   stx $9115     ; start the reference timer
  1897.  
  1898. pointers:
  1899.   lda #<irq     ; set the raster IRQ routine pointer
  1900.   sta $314
  1901.   lda #>irq
  1902.   sta $315
  1903.   lda #$c0
  1904.   sta $912e     ; enable Timer A underflow interrupts
  1905.   rts           ; return
  1906.  
  1907. irq:
  1908. ; irq (event)   ; > 7 + at least 2 cycles of last instruction (9 to 16 total)
  1909. ; pha           ; 3
  1910. ; txa           ; 2
  1911. ; pha           ; 3
  1912. ; tya           ; 2
  1913. ; pha           ; 3
  1914. ; tsx           ; 2
  1915. ; lda $0104,x   ; 4
  1916. ; and #xx       ; 2
  1917. ; beq           ; 3
  1918. ; jmp ($314)    ; 5
  1919.                 ; ---
  1920.                 ; 38 to 45 cycles delay at this stage
  1921.  
  1922.   lda $9114     ; get the NMI timer A value
  1923.                 ; (42 to 49 cycles delay at this stage)
  1924. ; sta $1e00     ; uncomment these if you want to monitor
  1925. ; ldy $9115     ; the reference timer on the screen
  1926. ; sty $1e01
  1927.   cmp #8        ; are we more than 7 cycles ahead of time?
  1928.   bcc 0$
  1929.   pha           ; yes, spend 8 extra cycles
  1930.   pla
  1931.   and #7        ; and reset the high bit
  1932. 0$:
  1933.   cmp #4
  1934.   bcc 1$
  1935.   bit $24       ; waste 4 cycles
  1936.   and #3
  1937. 1$:
  1938.   cmp #2        ; spend the rest of the cycles
  1939.   bcs *+2
  1940.   bcs *+2
  1941.   lsr
  1942.   bcs *+2       ; now it has taken 82 cycles from the beginning of the IRQ
  1943.  
  1944. effect:
  1945.   ldy #16       ; perform amazing video effect
  1946.   lda $900f
  1947.   tax
  1948.   eor #$f7
  1949. 0$:
  1950.   sta $900f
  1951.   stx $900f
  1952.   sta $900f
  1953.   stx $900f
  1954.   sta $900f
  1955.   stx $900f
  1956.   sta $900f
  1957.   stx $900f
  1958.   sta $900f
  1959.   stx $900f
  1960.   sta $900f
  1961.   stx $900f
  1962.   pha
  1963.   pla
  1964. #if SYSTEM & PAL
  1965.   pha
  1966.   pla
  1967.   nop
  1968. #endif
  1969. #if SYSTEM & NTSC
  1970.   bit $24
  1971. #endif
  1972.   nop
  1973.   dey
  1974.   bne 0$        ; end of amazing video effect
  1975.  
  1976.   jmp $eabf     ; return to normal IRQ
  1977.  
  1978.  
  1979. And after you have recovered from the schock of seeing a VIC-20
  1980. program, here is an example for the C64.  It does also something
  1981. noteworthy; it removes the side borders on a normal screen while
  1982. displaying all eight sprites.  Well, it cannot remove the borders on
  1983. bad lines, and the bad lines look pretty bad.  But I could use the
  1984. program for what I wanted: I measured the sprite distortions on all
  1985. videochip types I had at hand.  (FYI: the sprites 0-2 get distorted at
  1986. the very right of the screen, and the sprites 6 and 7 are invisible at
  1987. the very left of the screen.  You will need a monitor with horizontal
  1988. size controls to witness these effects.)
  1989.  
  1990. This program is really robust, it installs itself nicely to the
  1991. interrupt routine chain.  It even has an entry point for deinstalling
  1992. itself.  But in its robustness it uses self-modifying code to store
  1993. the original interrupt routine address. :-)
  1994.  
  1995. The code also relies on the page boundaries in being where they are.
  1996. The cycles are counted so that the branches "irqloop" must take 4
  1997. cycles.  If the "irqloop" comes to the same CPU page with the branch
  1998. instructions, you must add one cycle to the loop in a way or another.
  1999. When coding the routine, I noticed again how stupid assembly coding
  2000. can be, especially conditional assembling.  In a machine language
  2001. monitor you have far better control on page boundaries.  BTW, you
  2002. might wonder why I disable the Restore key in a subroutine at the end
  2003. and not in the beginning of the program.  Well, the routine was so
  2004. long that it would have affected the "irqloop" page boundaries.  And I
  2005. didn't want to risk the modified programs working on all three
  2006. different videochip types on the first try.
  2007.  
  2008.  
  2009. In the code, there are some comments that document the video timing,
  2010. like this one:
  2011.  
  2012. ;3s4s5s6s7srrrrrgggggggggggggggggggggggggggggggggggggggg--||0s1s2s Phi-1 VIC-II
  2013. ;ssssssssss                                               ||ssssss Phi-2 VIC-II
  2014. ;==========xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx||XXX====== Phi-2 6510
  2015. ;          ^ now we are here
  2016.  
  2017. The two vertical bars "|" denote optional cycles.  On PAL-B systems
  2018. (63 cycles per line), they are not present.  On 6567R56A, which has 64
  2019. cycles per line, there is one additional cycle on this position, and
  2020. the 6567R8 has two additional cycles there.
  2021.  
  2022. The numbers 0 through 7 are sprite pointer fetches (from the end of
  2023. the character matrix, e.g. the text screen), the "s" characters denote
  2024. sprite image fetches, the "r"s are memory refresh, and the "g" are
  2025. graphics fetches.  The two idle video chip cycles are marked with "-".
  2026. On the processor timing line, the "=" signs show halted CPU, "x" means
  2027. free bus, and "X" means that the processor will be halted at once,
  2028. unless it is performing write cycles.
  2029.  
  2030.   processor 6502
  2031.  
  2032. ; Select the video timing (processor clock cycles per raster line)
  2033. CYCLES = 65     ; 6567R8 and above, NTSC-M
  2034. ;CYCLES = 64    ; 6567R5 6A, NTSC-M
  2035. ;CYCLES = 63    ; 6569 (all revisions), PAL-B
  2036.  
  2037. cinv = $314
  2038. cnmi = $318
  2039. raster = 52     ; start of raster interrupt
  2040. m = $fb         ; zero page variable
  2041.  
  2042.   .org $801
  2043. basic:
  2044.   .word 0$      ; link to next line
  2045.   .word 1995    ; line number
  2046.   .byte $9E     ; SYS token
  2047.  
  2048. ; SYS digits
  2049.  
  2050.   .if (* + 8) / 10000
  2051.   .byte $30 + (* + 8) / 10000
  2052.   .endif
  2053.   .if (* + 7) / 1000
  2054.   .byte $30 + (* + 7) % 10000 / 1000
  2055.   .endif
  2056.   .if (* + 6) / 100
  2057.   .byte $30 + (* + 6) % 1000 / 100
  2058.   .endif
  2059.   .if (* + 5) / 10
  2060.   .byte $30 + (* + 5) % 100 / 10
  2061.   .endif
  2062.   .byte $30 + (* + 4) % 10
  2063.  
  2064. 0$:
  2065.   .byte 0,0,0   ; end of BASIC program
  2066.  
  2067. start:
  2068.   jmp install
  2069.   jmp deinstall
  2070.  
  2071. install:        ; install the raster routine
  2072.   jsr restore   ; Disable the Restore key (disable NMI interrupts)
  2073. checkirq:
  2074.   lda cinv      ; check the original IRQ vector
  2075.   ldx cinv+1    ; (to avoid multiple installation)
  2076.   cmp #<irq1
  2077.   bne irqinit
  2078.   cpx #>irq1
  2079.   beq skipinit
  2080. irqinit:
  2081.   sei
  2082.   sta oldirq    ; store the old IRQ vector
  2083.   stx oldirq+1
  2084.   lda #<irq1
  2085.   ldx #>irq1
  2086.   sta cinv      ; set the new interrupt vector
  2087.   stx cinv+1
  2088. skipinit:
  2089.   lda #$1b
  2090.   sta $d011     ; set the raster interrupt location
  2091.   lda #raster
  2092.   sta $d012
  2093.   ldx #$e
  2094.   clc
  2095.   adc #3
  2096.   tay
  2097.   lda #0
  2098.   sta m
  2099. 0$:
  2100.   lda m
  2101.   sta $d000,x   ; set the sprite X
  2102.   adc #24
  2103.   sta m
  2104.   tya
  2105.   sta $d001,x   ; and Y coordinates
  2106.   dex
  2107.   dex
  2108.   bpl 0$
  2109.   lda #$7f
  2110.   sta $dc0d     ; disable timer interrupts
  2111.   sta $dd0d
  2112.   ldx #1
  2113.   stx $d01a     ; enable raster interrupt
  2114.   lda $dc0d     ; acknowledge CIA interrupts
  2115.   lsr $d019     ; and video interrupts
  2116.   ldy #$ff
  2117.   sty $d015     ; turn on all sprites
  2118.   cli
  2119.   rts
  2120.  
  2121. deinstall:
  2122.   sei           ; disable interrupts
  2123.   lda #$1b
  2124.   sta $d011     ; restore text screen mode
  2125.   lda #$81
  2126.   sta $dc0d     ; enable Timer A interrupts on CIA 1
  2127.   lda #0
  2128.   sta $d01a     ; disable video interrupts
  2129.   lda oldirq
  2130.   sta cinv      ; restore old IRQ vector
  2131.   lda oldirq+1
  2132.   sta cinv+1
  2133.   bit $dd0d     ; re-enable NMI interrupts
  2134.   cli
  2135.   rts
  2136.  
  2137. ; Auxiliary raster interrupt (for syncronization)
  2138. irq1:
  2139. ; irq (event)   ; > 7 + at least 2 cycles of last instruction (9 to 16 total)
  2140. ; pha           ; 3
  2141. ; txa           ; 2
  2142. ; pha           ; 3
  2143. ; tya           ; 2
  2144. ; pha           ; 3
  2145. ; tsx           ; 2
  2146. ; lda $0104,x   ; 4
  2147. ; and #xx       ; 2
  2148. ; beq           ; 3
  2149. ; jmp ($314)    ; 5
  2150.                 ; ---
  2151.                 ; 38 to 45 cycles delay at this stage
  2152.   lda #<irq2
  2153.   sta cinv
  2154.   lda #>irq2
  2155.   sta cinv+1
  2156.   nop           ; waste at least 12 cycles
  2157.   nop           ; (up to 64 cycles delay allowed here)
  2158.   nop
  2159.   nop
  2160.   nop
  2161.   nop
  2162.   inc $d012     ; At this stage, $d012 has already been incremented by one.
  2163.   lda #1
  2164.   sta $d019     ; acknowledge the first raster interrupt
  2165.   cli           ; enable interrupts (the second interrupt can now occur)
  2166.   ldy #9
  2167.   dey
  2168.   bne *-1       ; delay
  2169.   nop           ; The second interrupt will occur while executing these
  2170.   nop           ; two-cycle instructions.
  2171.   nop
  2172.   nop
  2173.   nop
  2174. oldirq = * + 1  ; Placeholder for self-modifying code
  2175.   jmp *         ; Return to the original interrupt
  2176.  
  2177. ; Main raster interrupt
  2178. irq2:
  2179. ; irq (event)   ; 7 + 2 or 3 cycles of last instruction (9 or 10 total)
  2180. ; pha           ; 3
  2181. ; txa           ; 2
  2182. ; pha           ; 3
  2183. ; tya           ; 2
  2184. ; pha           ; 3
  2185. ; tsx           ; 2
  2186. ; lda $0104,x   ; 4
  2187. ; and #xx       ; 2
  2188. ; beq           ; 3
  2189. ; jmp (cinv)    ; 5
  2190.                 ; ---
  2191.                 ; 38 or 39 cycles delay at this stage
  2192.   lda #<irq1
  2193.   sta cinv
  2194.   lda #>irq1
  2195.   sta cinv+1
  2196.   ldx $d012
  2197.   nop
  2198. #if CYCLES - 63
  2199. #if CYCLES - 64
  2200.   nop           ; 6567R8, 65 cycles/line
  2201.   bit $24
  2202. #else
  2203.   nop           ; 6567R56A, 64 cycles/line
  2204.   nop
  2205. #endif
  2206. #else
  2207.   bit $24       ; 6569, 63 cycles/line
  2208. #endif
  2209.   cpx $d012     ; The comparison cycle is executed CYCLES or CYCLES+1 cycles
  2210.                 ; after the interrupt has occurred.
  2211.   beq *+2       ; Delay by one cycle if $d012 hadn't changed.
  2212.                 ; Now exactly CYCLES+3 cycles have passed since the interrupt.
  2213.   dex
  2214.   dex
  2215.   stx $d012     ; restore original raster interrupt position
  2216.   ldx #1
  2217.   stx $d019     ; acknowledge the raster interrupt
  2218.   ldx #2
  2219.   dex
  2220.   bne *-1
  2221.   nop
  2222.   nop
  2223.   lda #20       ; set the amount of raster lines-1 for the loop
  2224.   sta m
  2225.   ldx #$c8
  2226. irqloop:
  2227.   ldy #2
  2228.   dey
  2229.   bne *-1       ; delay
  2230.   dec $d016     ; narrow the screen (exact timing required)
  2231. ;3s4s5s6s7srrrrrgggggggggggggggggggggggggggggggggggggggg--||0s1s2s Phi-1 VIC-II
  2232. ;ssssssssss                                               ||ssssss Phi-2 VIC-II
  2233. ;==========xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx||XXX====== Phi-2 6510
  2234. ;          ^ now we are here
  2235.   stx $d016     ; expand the screen
  2236. #if CYCLES - 63
  2237. #if CYCLES - 64
  2238.   bit $24       ; 6567R8
  2239. #else
  2240.   nop           ; 6567R56A
  2241. #endif
  2242. #else
  2243.   nop           ; 6569
  2244. #endif
  2245.   dec m
  2246.   bmi endirq
  2247.   clc
  2248.   lda $d011
  2249.   sbc $d012
  2250.   and #7
  2251.   bne irqloop   ; This instruction takes 4 cycles instead of 3,
  2252.                 ; because the page boundary is crossed.
  2253. badline:
  2254.   dec m
  2255.   nop
  2256.   nop
  2257.   nop
  2258.   nop
  2259.   dec $d016
  2260. ;3s4s5s6s7srrrrrgggggggggggggggggggggggggggggggggggggggg--||0s1s2s Phi-1 VIC-II
  2261. ;ssssssssss    cccccccccccccccccccccccccccccccccccccccc   ||ssssss Phi-2 VIC-II
  2262. ;==========xXXX========================================||***====== Phi-2 6510
  2263. ;          ^ we are here
  2264.   stx $d016
  2265. ;3s4s5s6s7srrrrrgggggggggggggggggggggggggggggggggggggggg--||0s1s2s Phi-1 VIC-II
  2266. ;ssssssssss                                               ||ssssss Phi-2 VIC-II
  2267. ;==========xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx||XXX====== Phi-2 6510
  2268. ;          ^ ^^- we are here (6569)
  2269. ;          | \- or here (6567R56A)
  2270. ;          \- or here (6567R8)
  2271.   ldy #2
  2272.   dey
  2273.   bne *-1
  2274.   nop
  2275.   nop
  2276. #if CYCLES - 63
  2277. #if CYCLES - 64
  2278.   nop           ; 6567R8, 65 cycles/line
  2279.   nop
  2280.   nop
  2281. #else
  2282.   bit $24       ; 6567R56A, 64 cycles/line
  2283. #endif
  2284. #else
  2285.   nop           ; 6569, 63 cycles/line
  2286. #endif
  2287.   dec m
  2288.   bpl irqloop   ; This is a 4-cycle branch (page boundary crossed)
  2289. endirq:
  2290.   jmp $ea81     ; return to the auxiliary raster interrupt
  2291.  
  2292. restore:        ; disable the Restore key
  2293.   lda cnmi
  2294.   ldy cnmi+1
  2295.   pha
  2296.   lda #<nmi     ; Set the NMI vector
  2297.   sta cnmi
  2298.   lda #>nmi
  2299.   sta cnmi+1
  2300.   ldx #$81
  2301.   stx $dd0d     ; Enable CIA 2 Timer A interrupt
  2302.   ldx #0
  2303.   stx $dd05
  2304.   inx
  2305.   stx $dd04     ; Prepare Timer A to count from 1 to 0.
  2306.   ldx #$dd
  2307.   stx $dd0e     ; Cause an interrupt.
  2308. nmi = * + 1
  2309.   lda #$40      ; RTI placeholder
  2310.   pla
  2311.   sta cnmi
  2312.   sty cnmi+1    ; restore original NMI vector (although it won't be used)
  2313.   rts
  2314.  
  2315.  
  2316. Binaries
  2317.  
  2318. Here are the programs in uuencoded format.  First the VIC-20 programs:
  2319.  
  2320. Color boxes for the VIC-20, NTSC-M version (probably distorted display):
  2321.  
  2322. begin 644 copper.6560
  2323. M`1`*$,L'GC0Q,#D```"I?XTND8TMD8T>D:(<[`20T/N@"20DK@20BB0D)"2B
  2324. M%<K0_<T$D+``B-#KJ4"-&Y&-*Y&I0Z)"C1:1C2:1H`:(T/TD)(XED:`*B-#]
  2325. MCA61J6R-%`.I$(T5`ZG`C2Z18*T4D<D(D`1(:"D'R020!"0D*0/)`K``L`!*
  2326. ML`"@$*T/D*I)]XT/D(X/D(T/D(X/D(T/D(X/D(T/D(X/D(T/D(X/D(T/D(X/
  2327. ,D$AH)"3JB-#43+_J
  2328. `
  2329. end
  2330.  
  2331.  
  2332. Color boxes for the VIC-20, PAL-B version:
  2333.  
  2334. begin 644 copper.6561
  2335. M`1`*$,L'GC0Q,#D```"I?XTND8TMD8T>D:(<[`20T/N@"20DK@20BB0DHAC*
  2336. MT/W-!)"P`(C0[:E`C1N1C2N1J8:B5HT6D8TFD:`'B-#]ZNJ.)9&@"HC0_8X5
  2337. MD:EJC10#J1"-%0.IP(TND6"M%)')")`$2&@I!\D$D`0D)"D#R0*P`+``2K``
  2338. MH!"M#Y"J2?>-#Y".#Y"-#Y".#Y"-#Y".#Y"-#Y".#Y"-#Y".#Y"-#Y".#Y!(
  2339. +:$AHZNJ(T--,O^J.
  2340. `
  2341. end
  2342.  
  2343.  
  2344. Removed sideborders with 8 sprites and bad lines, PAL-B version:
  2345.  
  2346. begin 644 raster.63
  2347. M`0@*",L'GC(P-C$```!,$PA,=`@@'0FM%`.N%0/)E=`$X`CP$7B-N0B.N@BI
  2348. ME:((C10#CA4#J1N-$="I-(T2T*(.&&D#J*D`A?NE^YT`T&D8A?N8G0'0RLH0
  2349. M[ZE_C0W<C0W=H@&.&M"M#=Q.&="@_XP5T%A@>*D;C1'0J8&-#=RI`(T:T*VY
  2350. M"(T4`ZVZ"(T5`RP-W5A@J;N-%`.I"(T5`^KJZNKJZNX2T*D!C1G06*`)B-#]
  2351. MZNKJZNI,N`BIE8T4`ZD(C14#KA+0ZB0D[!+0\`#*RHX2T*(!CAG0H@+*T/WJ
  2352. MZJD4A?NBR*`"B-#]SA;0CA;0ZL;[,",8K1'0[1+0*0?0Y<;[ZNKJZLX6T(X6
  2353. MT*`"B-#]ZNKJQOL0S4R!ZJT8`ZP9`TBI0HT8`ZD)C1D#HH&.#=VB`(X%W>B.
  2354. 1!-VBW8X.W:E`:(T8`XP9`V`8
  2355. `
  2356. end
  2357.  
  2358.  
  2359. Removed sideborders with 8 sprites and bad lines, 6567R56A version
  2360. (very old NTSC-M C64s):
  2361.  
  2362. begin 644 raster.64
  2363. M`0@*",L'GC(P-C$```!,$PA,=`@@'@FM%`.N%0/)E=`$X`CP$7B-N0B.N@BI
  2364. ME:((C10#CA4#J1N-$="I-(T2T*(.&&D#J*D`A?NE^YT`T&D8A?N8G0'0RLH0
  2365. M[ZE_C0W<C0W=H@&.&M"M#=Q.&="@_XP5T%A@>*D;C1'0J8&-#=RI`(T:T*VY
  2366. M"(T4`ZVZ"(T5`RP-W5A@J;N-%`.I"(T5`^KJZNKJZNX2T*D!C1G06*`)B-#]
  2367. MZNKJZNI,N`BIE8T4`ZD(C14#KA+0ZNKJ[!+0\`#*RHX2T*(!CAG0H@+*T/WJ
  2368. MZJD4A?NBR*`"B-#]SA;0CA;0ZL;[,"08K1'0[1+0*0?0Y<;[ZNKJZLX6T(X6
  2369. MT*`"B-#]ZNHD),;[$,Q,@>JM&`.L&0-(J4.-&`.I"8T9`Z*!C@W=H@".!=WH
  2370. 2C@3=HMV.#MVI0&B-&`.,&0-@
  2371. `
  2372. end
  2373.  
  2374. Removed sideborders with 8 sprites and bad lines, 6567R8 and above
  2375. (not too old NTSC-M C64s and all C128s)
  2376.  
  2377. begin 644 raster.65
  2378. M`0@*",L'GC(P-C$```!,$PA,=`@@(0FM%`.N%0/)E=`$X`CP$7B-N0B.N@BI
  2379. ME:((C10#CA4#J1N-$="I-(T2T*(.&&D#J*D`A?NE^YT`T&D8A?N8G0'0RLH0
  2380. M[ZE_C0W<C0W=H@&.&M"M#=Q.&="@_XP5T%A@>*D;C1'0J8&-#=RI`(T:T*VY
  2381. M"(T4`ZVZ"(T5`RP-W5A@J;N-%`.I"(T5`^KJZNKJZNX2T*D!C1G06*`)B-#]
  2382. MZNKJZNI,N`BIE8T4`ZD(C14#KA+0ZNHD).P2T/``RLJ.$M"B`8X9T*("RM#]
  2383. MZNJI%(7[HLB@`HC0_<X6T(X6T"0DQOLP)1BM$=#M$M`I!]#DQOOJZNKJSA;0
  2384. MCA;0H`*(T/WJZNKJZL;[$,I,@>JM&`.L&0-(J4:-&`.I"8T9`Z*!C@W=H@".
  2385. 5!=WHC@3=HMV.#MVI0&B-&`.,&0-@
  2386. `
  2387. end
  2388.  
  2389.  
  2390. That was all, folks!  I hope you learned something from this article.
  2391. Feel free to e-mail me at Marko.Makela@HUT.FI, should anything remain
  2392. unclear.
  2393.  
  2394. ========================================================================
  2395. A Different Perspective, part III
  2396. by Stephen Judd --- sjudd@nwu.edu    
  2397.    George Taylor --- aa601@cfn.cs.dal.ca
  2398.  
  2399.         Whew!  What a busy time it's been -- research to get done,
  2400. conferences, classes... between getting things done and blowing other
  2401. things off, I one day reflected for a moment and realized that I had
  2402. three days left to get the next article together for C=Hacking!  So
  2403. everything has been slapped together at the last minute, and I hope
  2404. you'll forgive any bugs or unclear concepts.
  2405.                 >>> ANECDOTE ALERT <<<
  2406.         And that reminds me: I just got JiffyDOS and an FD-2000 drive --
  2407. what a wonderful device.  I have a 1.6 megabyte disk formatted into
  2408. three partitions.  The first contains my Merlin 128 assembler, the
  2409. second is some 4000 blocks large and I use it for all my various
  2410. versions of code while debugging, and the third is maybe 1000 blocks,
  2411. and contains only finished code -- no more swapping disks, no more
  2412. deleting old versions that I hope I don't need to make room on the
  2413. disk.  Also, when I installed JiffyDOS I found a serious bug in my
  2414. 128D -- a cricket, dead among the IC's.
  2415.  
  2416.         This time we will cover a lot of ground which isn't so much
  2417. cutting-edge as it is very useful.  Let's face it: cubes are getting
  2418. more than a little dull.  A worthy end goal is to have a completely
  2419. general routine for plotting a series of polygons -- that is, you supply
  2420. a list of (x,y,z) coordinates from which the program can form a list of
  2421. polygons.  These polygons may then be displayed in 2D, rotated, magnified,
  2422. filled, etc.  And, much to my three-day astonishment, that is exactly
  2423. what we are going to do.
  2424.         But first, a little excursion.  One thing we are of course always
  2425. thinking about is optimization possibilities: in the shower, while
  2426. sleeping/dreaming, out on dates, etc.  So, where to begin?  The biggest
  2427. cycle hogs in the program are line drawing and face filling -- well,
  2428. filling faces is pretty straightforward.  What about line drawing?
  2429.         Well, one downer of the routine is that every single pixel is
  2430. plotted.  But as we know, on a computer any given line is made up of
  2431. several smaller vertical and horizontal lines -- wouldn't it be neat
  2432. if we could think of a way to plot these line chunks all at once,
  2433. instead of a pixel at a time?
  2434.         Heck yes it would!  So here we go:
  2435.         
  2436. Neat-o Enhanced Chunky Line Drawing Routine
  2437. -------------------------------------------
  2438.  
  2439.         First we need to be in the right mindframe.  Let's say you're
  2440. drawing a line where you move three pixels in x before it's time to take
  2441. a step in y.  Instead of plotting all three pixels it would of course
  2442. be much more efficient to just stick a number like %00011100 in the
  2443. drawing buffer.  But somehow we need to keep track of a) how large the
  2444. chunk needs to be, and b) where exactly the chunk is.
  2445.         In the above example, we started at a particular x-value:
  2446.  
  2447.         %00010000
  2448.  
  2449. and we want to keep adding ones to the right of the starting point; three,
  2450. to be exact.  Hmmm... we need to somehow rotate the starting bit in a way
  2451. that leaves a trail of ones behind it.  Maybe rotate and ORA with the
  2452. original bit?  But what happens when you take a step in Y?
  2453.         No, we need something far sneakier.  Let's say that instead of
  2454. %00010000 we start with
  2455.  
  2456.         x = %00011111
  2457.  
  2458. Now, with each step in the x direction, we do an arithmetic shift on x.  So
  2459. after one step we have
  2460.  
  2461.         x = %00001111
  2462.  
  2463. and after two steps
  2464.  
  2465.         x = %00000111
  2466.  
  2467. and at the third step of course
  2468.  
  2469.         x = %00000011
  2470.  
  2471. Now it is time to take a step in Y.  But now look: if we EOR x with its
  2472.  
  2473. original value xold = %00011111, we get
  2474.  
  2475.         x EOR xold = %00011100
  2476.  
  2477. which is exactly the chunk we wanted.  Moreover, x still remembers where it
  2478. is, so we don't have to do anything special each time a step is taken in
  2479. the y-direction.
  2480.  
  2481.         So here is the algorithm for drawing a line in the x-direction:
  2482.  
  2483.         initialize x, dx, etc.
  2484.         xold = x
  2485.         take a step in x: LSR X
  2486.         have we hit the end of a column?  If so, then plot and check on y
  2487.         is it time to take a step in y?
  2488.         if not, take another step in x
  2489.         if it is, then let a=x EOR xold
  2490.                        plot a into the buffer
  2491.                        let xold=x
  2492.         keep on going until we're finished
  2493.  
  2494.         This simple modification gives us a substantial speed increase --
  2495. on the old filled hires cube3d program, I measured a gain of one frame per
  2496. second.  Not earth-shattering, but not bad either!  When faces are not
  2497. filled, the difference is of course much more noticable.
  2498.         There are a few things to be careful of.  There was a bug in the
  2499. old routine when the line was a single point.  In that case dx=dy=0, and
  2500. the program would draw a vertical line on the screen.  There are probably
  2501. some other things to be careful of, but since I wrote this part of the
  2502. code three months ago I really don't remember any of them!
  2503.         This takes care of horizontal line chunks -- what about vertical
  2504. chunks?  Well, because of the way points are plotted there is nothing
  2505. we can do about them.  But, as we shall soon see, if we use an EOR-buffer
  2506. to fill faces we will be forced to take care of the vertical chunks!
  2507.  
  2508. General Polygon Routine
  2509. -----------------------
  2510.  
  2511.         Now we can begin thinking about a general polygon routine.  First
  2512. we need a list of sets of points, where each set corresponds to a
  2513. polygon.  The first number in a set could be the number of (x,y,z) points
  2514. in that set, and the points could then follow.  So a triangle could
  2515. be given by the data set:
  2516.  
  2517.         3 -1 0 0 0 1 0 1 0 0
  2518.  
  2519. This would be a triangle with vertices at (-1,0,0), (0,1,0), and (1,0,0).
  2520. We can mash a bunch of these sets together, but somehow we have to know
  2521. when we've hit the end -- for this we can use a zero, since we don't
  2522. want to plot polygons with zero points in them.
  2523.         For that matter, how many points should there be in a polygon?
  2524. There must be at least three, otherwise it makes no sense.  Since we
  2525. want our polygons to be closed, the computer should be smart enough to
  2526. connect the last point to the first point -- in our triangle above,
  2527. the computer would join (-1,0,0) to (0,1,0), (0,1,0) to (1,0,0), and
  2528. (1,0,0) to (-1,0,0).
  2529.         Now that we have a polygon, we want to rotate it.  You will
  2530. recall that we have calculated a rotation matrix M, which acts on
  2531. points.  So we need apply our rotation transform to each of the
  2532. points in the polygon, i.e. multiply M times each point of the
  2533. polygon.  Furthermore, we need to project each of these points.
  2534.         Uh-oh: matrix multiplication.  In the past we have avoided this
  2535. issue by putting the vertices of our cube at 1 or -1.  So we need to
  2536. use our multiplication routine from last time.  But wait!  As you recall,
  2537. the last program used a specially modified multiplication table.  To get
  2538. a wider range of numbers to multiply we will need another set of
  2539. multiplication tables -- no big whoop.
  2540.         Now, if you review the multiplication routine from last time,
  2541. it adds two numbers and subtracts two numbers.  What kinds of numbers
  2542. will we be dealing with?  The matrix elements vary between -64..64.
  2543. This then fixes our range of polygon coordinates from -64..64.  Why?
  2544. If the matrix element is 64, and we multiply it by 64, the multiplication
  2545. routine will add 64 and 64 and get 128, which is right on the edge of
  2546. our multiplication table.
  2547.         Can we improve this rotation process in any way?  In fact, we can
  2548. cut down on the number of multiplications (i.e. do eight or even seven
  2549. instead of nine multiplications).  However, there is a fair amount of
  2550. overhead involved in doing so, and our multiply routine is fast enough
  2551. that the extra overhead and complexity really gain us very little in all
  2552. but the most complicated of polygons.  In other words, I didn't bother.
  2553.  
  2554.         What about hidden faces?  Again, from last time you may recall
  2555. that a method was described which used the cross-product of the projected
  2556. vectors.  How do we implement this in the program?  Well, if we take
  2557. the first three points of the polygon, we have two vectors.  Let's say
  2558. these points are P1 P2 and P3.  Then V1=P1-P2 and V2=P3-P2 are two
  2559. vectors in the plane of the polygon which are connected at the point P2
  2560. (this analysis will of course only work if the polygon lies in some plane).
  2561. Depending on how we take the cross product, the sign will be positive or
  2562. negative, and this will tell us if the polygon is visible.
  2563.         Depending on how we take the cross product?  Absolutely.
  2564. v1 x v2 = -v2 x v1.  What it really boils down to is how you define the
  2565. points in your polygon.  Specifically, what order they are in.  Points
  2566. that are specified in a clockwise manner will give a face pointing in
  2567. the opposite direction of a polygon with the same points specified in
  2568. a counter-clockwise order.  In my program, the polygons must be entered
  2569. in counter-clockwise order (with you facing the polygon) for hidden
  2570. faces to work the way you want them to ;-).
  2571.  
  2572.         One other neat thing to have is the ability to zoom in and out.
  2573. We know from the very first article that zooming corresponds to multiplying
  2574. the projected points by a number, so that's what we'll do.  The multiplication
  2575. routine returns A=A*Y/64, so a zoom factor of 64 would be like multiplying
  2576. the point by one.  All the program does is multiply the projected points
  2577. by a number zoom, unless zoom=64, in which case the program skips the
  2578. zoom multiply.  Be warned!  No checks of any sort are made in the program,
  2579. so you can zoom at your own risk!
  2580.  
  2581.         The important things to remember are: when entering polygons,
  2582. make sure the numbers range from -64 to 64, and that you enter points
  2583. in counterclockwise.  Our triangle example above really should have been
  2584. entered as, say,
  2585.  
  2586.         3 -64 0 0 64 0 0 0 64 0
  2587.  
  2588. Filled Faces -- Using an EOR buffer
  2589. -----------------------------------
  2590.  
  2591.         Well we still have one thing left, which was alluded to in the
  2592. previous article: using EOR to make a filled face.  Some possible
  2593. difficulties were raised, but when you plot a single polygon at a
  2594. time, the problem becomes vastly simplified.
  2595.         First I should perhaps remind you what exclusive-or is: either
  2596. A or B, but not both.  So 1 EOR 0 = 1, as does 0 EOR 1, but 0 EOR 0 = 0
  2597. and 1 EOR 1 = 0.  As a simple introduction to using this for filling
  2598. faces, consider the following piece of the drawing buffer:
  2599.  
  2600.         00001011 M1
  2601.         00000000 M2
  2602.         00000001 M3
  2603.         00001010 M4
  2604.  
  2605. Lets say we move down memory, EORing as we go.  Let M2 = M1 EOR M2.  Then
  2606. let M3 = M2 EOR M3.  Then let M4 = M3 EOR M4.  Our little piece of memory
  2607. is now:
  2608.  
  2609.         00001011 M1
  2610.         00001011 M2
  2611.         00001010 M3
  2612.         00000000 M4
  2613.  
  2614. What just happened?  Imagine that the original memory was a series of
  2615. pieces of line segments.  We have just filled in the area between the
  2616. two line segments, like magic!
  2617.         If you still aren't getting it, draw a large section of memory,
  2618. and then draw an object in it, like a triangle, or a trapazoid, and
  2619. EOR the memory by hand, starting from the top and moving downwards.
  2620.         EOR flips bits.  If you start with a zero, it stays zero until
  2621. it hits a one.  It will then stay one until it hits another one.  So
  2622. you can see that if you have an object bounded by ones, EORing
  2623. successive memory locations will automagically fill the object.
  2624.         Right?  Well, we have to be careful.  One major problem is
  2625. a vertical line:
  2626.  
  2627.         1                       1
  2628.         1       goes to         0
  2629.         1                       1
  2630.         1                       0
  2631.  
  2632. Not only is the resultant line dashed, but if there are an odd number of
  2633. points in the line segment, the last one will happily move downwards in
  2634. memory, and give you a much longer vertical line than you expected!  Since
  2635. any line with slope greater than one is made up of a series of line
  2636. segments, this is a major consideration.
  2637.         Another problem arises with single points: a one just sitting all
  2638. by itself will also generate a nice streak down your drawing area.
  2639.         If you think about it, what we ideally want to have is an object
  2640. that at any given value of x there are exactly two points, one defining
  2641. the top of the object, and the other defining the bottom.  This gives us
  2642. the insight to solve the above two problems.
  2643.         First let's think about vertical lines.  In principle we could
  2644. plot the first and last endpoints of each vertical line chunk, but that
  2645. is exactly what we don't want!  Remember that these are closed polygons,
  2646. which means that there are _two_ lines we need to think about.  If I
  2647. plot just a single point in each vertical line segment, there must
  2648. be another point somehwere, either above or below it, from another
  2649. line segment, which will close the point to EOR-filling.  Remember, we
  2650. want exactly two points at each value of x: one will come from the
  2651. line, and the other will come from the other line which must lie above
  2652. or below the current one.
  2653.         Furthermore, with any convex polygon there are exactly two
  2654. lines which come together at each vertex of the polygon.  This means
  2655. that there are only certain cases which we need to worry about.
  2656. For instance, two lines might join in any of the following ways:
  2657.  
  2658.         \                  /    \    /
  2659.          \                /      \  /
  2660.           \_____    _____/        \/  etc.
  2661.  
  2662. If you draw out the different cases involving vertical lines, you can see
  2663. that you have to be careful about plotting the lines.  One tricky one
  2664. is where two vertical lines with different slopes overlap at the point
  2665. of intersection.
  2666.         So after staring at these pictures for a while, you can find
  2667. a consistent method which solves these difficulties.  As long as you
  2668. follow the following rules, the problems all disappear; the line routine
  2669. needs to be modified slightly:
  2670.  
  2671.         1) When plotting a vertical line (i.e. big steps in Y direction),
  2672.            don't plot the endpoints (i.e. x1,y1 and x2,y2).
  2673.         2) When plotting a vertical line, consistently plot either the
  2674.            first part of each chunk or the last part of each chunk
  2675.            (excluding the endpoints of course).  In other words, only
  2676.            plot a point when you take a step in x, and then plot one
  2677.            and only one point.
  2678.  
  2679. Now I deduced these by staring at pictures for a few hours and trying
  2680. different things like top/bottom of chunk, left/right, first/last, etc.
  2681. You can see that in some cases this ensures that only one point appears
  2682. on a given line segment.  But to me the only way to convince yourself
  2683. that this really does work is to draw a bunch of pictures, and try it
  2684. out!  You have cases where two vertical lines intersect, and where
  2685. a vertical line intersects a horizontal line.
  2686.         But there is still one thing which we have forgotten -- the
  2687. case of a single point.  This can happen in, for instance, a pointy
  2688. triangle, pointing in the x-direction.  How do we fix this?  By
  2689. simply avoiding the point: in the line drawing routine, use EOR
  2690. to plot the points instead of ORA.  Since vertical lines skip the
  2691. endpoints, vertical-horizontal intersections are OK.  Horizontal-
  2692. horizontal intersections will force the point of intersection to
  2693. be zero.
  2694.         Uh-oh, what about intersections like -----*------.  Quite frankly
  2695. I just thought of it, and I think my program will fail on intersections
  2696. like these.  Drat.  Well, that just gives us something for next time!
  2697.         One other thing needs to be mentioned: for EOR-filling to be useful
  2698. you need to draw the polygon in a special buffer, and then EOR this buffer
  2699. into the main display buffer.  If you try to EOR the display buffer directly
  2700. you are going to have a whole heap of trouble, such as the concerns raised
  2701. last time.
  2702.         Finally, this gives a simple way of filling with patterns instead
  2703. of boring monocolor.  Instead of EOR (EORBUF),Y : ORA (DRAWBUF),Y you can
  2704. use EOR (EORBUF),Y : AND PATTERN,Y : ORA (DRAWBUF),Y (as long as you
  2705. preserve the original EOR (EORBUF),Y).
  2706.  
  2707.         Well I am extremely tired and I hope Craig hasn't sent out C=Hacking
  2708. without me!  I hope you have fun playing with the program, and I would be
  2709. very interested in seeing any neat geometric shapes you might design!
  2710.  
  2711. Program notes:
  2712. --------------
  2713.  
  2714.         - Hidden faces defaults to "on".  If you enter a shape and a black
  2715.           screen comes up, hit 'h' to turn off hidden faces (you probably
  2716.           entered the polygon clockwise).
  2717.         - There is no pattern filling -- just simple EOR with a twist:
  2718.           the EOR buffer is EOR'd into the drawing buffer.
  2719.         - You might start hosing memory if you zoom too large.
  2720.  
  2721. SLJ 6/15/95
  2722.  
  2723. Addendum
  2724. --------
  2725. Stephen Judd sjudd@nwu.edu
  2726.  
  2727.         Last time we put a circle into the 2D graphics toolbox.  Chris
  2728. McBride has pointed something out to me about the algorithm, which makes
  2729. it complete.  As you may recall, the algorithm gave a very squarish
  2730. circle for small radii.  Chris told me that setting the initial counter
  2731. value to R/2, instead of R, gave a perfect circle.  What is going on?
  2732. If you recall the algorithm, we are computing a fractional quantity,
  2733. and when that quantity becomes larger than one, we decrease X.  Wouldn't
  2734. it be a whole lot smarter to round off that fraction instead of
  2735. truncate it?  Of course it would, and that is what starting the counter
  2736. at R/2 does.
  2737.         So, to update the previous algorithm, A should be initialized to
  2738. R/2 instead of R, which means that we change
  2739.  
  2740.         LDA R
  2741.  
  2742. to
  2743.  
  2744.         LDA R
  2745.         LSR
  2746.  
  2747. for a perfect circle every time.
  2748.  
  2749. begin 666 cube3d3.2.s
  2750. M ' J*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*@TJH*"@H*"@H*"@
  2751. MH*"@H*"@H*"@H*"@H*"@H*"@H*"@*@TJH'-415!(14Z@:E5$1*"@H*"@H*"@
  2752. MH*"@H*"@H*"@*@TJH&=%3U)'1:!T05E,3U*@H*"@H*"@H*"@H*"@H*"@*@TJ
  2753. MH'-405)4140ZH#<O,3$O.32@H*"@H*"@H*"@H*"@*@TJH&9)3DE32$5$.J W
  2754. M+S$Y+SDTH*"@H*"@H*"@H*"@*@TJH%8R+C"@8T]-4$Q%5$5$.J Q,B\Q-R\Y
  2755. M-*"@H*"@*@TJH%8S+C"@8T]-4$Q%5$5$.J S+S(P+SDUH*"@H*"@*@TJH%8S
  2756. M+C&@8T]-4$Q%5$5$.J V+S$T+SDUH*"@H*"@*@TJH%8S+C*@8T]-4$Q%5$5$
  2757. M.J V+S$U+SDUH*"@H*"@*@TJH*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@
  2758. MH*"@*@TJH'=%3$PLH$E&H$%,3*!'3T53H%=%3$R@5$A)4Z"@*@TJH%!23T=2
  2759. M04V@5TE,3*!23U1!5$6@0:!#54)%+J"@*@TJH*"@H*"@H*"@H*"@H*"@H*"@
  2760. MH*"@H*"@H*"@H*"@*@TJH%8R+C"@*Z!N15>@04Y$H&E-4%)/5D5$(:"@H*"@
  2761. M*@TJH&Y/5Z!7251(H$9!4U1%4J!23U5424Y%4RR@H*"@*@TJH$A)1$1%3J!3
  2762. M55)&04-%4RR@1DE,3$5$H*"@H*"@*@TJH$9!0T53+*!!3D2@15A44D&@5$]0
  2763. MH%-%0U)%5*"@*@TJH%1%6%2@34534T%'15,AH*"@H*"@H*"@H*"@H*"@*@TJ
  2764. MH*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@*@TJH%8S+C"@*Z!F05-4
  2765. MH$-(54Y+6:!,24Y%H*"@H*"@*@TJH%)/551)3D4NH*"@H*"@H*"@H*"@H*"@
  2766. MH*"@H*"@*@TJH*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@*@TJH%8S
  2767. M+C&@*Z!G14Y%4D%,H%!/3%E'3TZ@4$Q/5*"@*@TJH%=)5$B@2$E$1$5.H$9!
  2768. M0T53H"AX+5!23T150U0I*@TJH$%.1*!:3T]-H$9%05154D4NH*"@H*"@H*"@
  2769. MH*"@*@TJH*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@*@TJH%8S+C*@
  2770. M*Z!E;W(M0E5&1D52H$9)3$Q)3D>@H*"@*@TJH*"@H*"@H*"@H*"@H*"@H*"@
  2771. MH*"@H*"@H*"@H*"@*@TJH'1(25.@4%)/1U)!3:!)4Z!)3E1%3D1%1*!43Z"@
  2772. M*@TJH$%#0T]-4$%.6:!42$6@05)424-,1:!)3J"@H*"@*@TJH&,]:$%#2TE.
  2773. M1RR@:E5.+J Y-:!)4U-512Z@H*"@*@TJH&9/4J!$151!24Q3H$].H%1(25.@
  2774. M4%)/1U)!32R@*@TJH%)%042@5$A%H$%25$E#3$4AH*"@H*"@H*"@H*"@*@TJ
  2775. MH*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@*@TJH'=2251%H%1/H%53
  2776. M(:"@H*"@H*"@H*"@H*"@H*"@*@TJH*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@
  2777. MH*"@H*"@*@TJH&U94T5,1J!72$5.H%E/54Y'H$1)1*"@H*"@H*"@*@TJH$5!
  2778. M1T523%F@1E)%455%3E2@H*"@H*"@H*"@H*"@*@TJH&1/0U1/4J!!3D2@<T%)
  2779. M3E0LH$%.1*!(14%21*"@*@TJH$=214%4H&%21U5-14Y4H*"@H*"@H*"@H*"@
  2780. MH*"@*@TJH*!A0D]55*!)5*!!3D2@04)/550ZH$)55*"@H*"@*@TJH*!%5D52
  2781. M34]21:"@H*"@H*"@H*"@H*"@H*"@H*"@*@TJH&-!346@3U54H$)9H%1(1:!3
  2782. M04U%H&1/3U*@H*"@*@TJH$%3H$E.H&F@5T5.5"Z@H*"@H*"@H*"@H*"@H*"@
  2783. M*@TJH*"@H"V@<E5"04E9052@H*"@H*"@H*"@H*"@H*"@*@TJH*"@H*"@H*"@
  2784. MH*"@H*"@H*"@H*"@H*"@H*"@H*"@*@TJH'1(3U5'2*!IH%-014%+H%=)5$B@
  2785. M5$A%H*"@H*"@*@TJH%1/3D=515.@3T:@345.H$%.1*!/1J!!3D=,15.@*@TJ
  2786. MH$%.1*!(059%H$Y/5*!,3U9%+*!IH$%-H*"@H*"@*@TJH$)%0T]-1:!!4Z!3
  2787. M3U5.1$E.1Z!"4D%34RR@3U*@*@TJH$&@5$E.2TQ)3D>@0UE-0D%,+J"@H*"@
  2788. MH*"@H*"@*@TJH*"@H"V@,:!C3U))3E1(24%.4Z Q,Z"@H*"@H*"@*@TJH*"@
  2789. MH*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@*@TJH' N<RZ@=$A)4Z!705.@
  2790. M5U))5%1%3J!54TE.1Z"@*@TJH*"@H*"@;4523$E.H#$R."Z@H*"@H*"@H*"@
  2791. MH*"@*@TJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*@T-(&]R9R D
  2792. M.# P, T-*J!C3TY35$%.5%,-#6)U9F8Q(&5Q=2 D,S P," [9DE24U2@0TA!
  2793. M4D%#5$52H%-%5 UB=69F,B!E<74@)#,X,# @.W-%0T].1*!#2$%204-415*@
  2794. M4T54#65O<F)U9B!E<74@)#0P,# @.V5O<BU"549&15(-8G5F9F5R(&5Q=2 D
  2795. M83,@.W!215-534%"3%F@5$A%H%1!4$6@5T].)U2@0D6@4E5.3DE.1PUX,2!E
  2796. M<74@)&9B(#MP3TE.5%.@1D]2H$1205=)3D>@0:!,24Y%#7DQ(&5Q=2 D9F,@
  2797. M.W1(15-%H%I%4D^@4$%'1:!!1$1215-315,->#(@97%U("1F9" [1$].)U2@
  2798. M0T].1DQ)0U2@5TE42*!B87-I8PUY,B!E<74@)&9E#6]L9'@@97%U("1F9 UC
  2799. M:'5N:R!E<74@)&9E#61X(&5Q=2 D-C<@.W1(25.@25.@4TA!4D5$H%=)5$B@
  2800. M=#&@0D5,3U<-9'D@97%U("0V. UT96UP,2!E<74@)&9B(#MO1J!#3U524T4L
  2801. MH$-/54Q$H$-/3D9,24-4H%=)5$B@6#$-=&5M<#(@97%U("1F8R [=$5-4$]2
  2802. M05)9H%9!4DE!0DQ%4PUZ=&5M<"!E<74@)# R(#MU4T5$H$9/4J!"549&15*@
  2803. M4U=!4"Z@H&1/3B=4H%1/54-(+@UZ,2!E<74@)#(R(#MU4T5$H$)9H$U!5$B@
  2804. M4D]55$E.10UZ,B!E<74@)#(T(#MD3TXG5*!43U5#2*!42$531:!%251(15(A
  2805. M#7HS(&5Q=2 D,C8->C0@97%U("0R. UK(&5Q=2 D8C8@.V-/3E-404Y4H%53
  2806. M142@1D]2H$A)1$1%3@T@(" [4U521D%#1:!$151%0U1)3TZ@+:!$3TXG5*!4
  2807. M3U5#2 UH:61E(&5Q=2 D8C4@.V%21:!355)&04-%4Z!(241$14X_#69I;&P@
  2808. M97%U("0U," [85)%H%=%H%5324Y'H&5O<BU&24Q,/PUA;F=M87@@97%U(#$R
  2809. M," [=$A%4D6@05)%H#(J4$DO04Y'34%8H$%.1TQ%4PT-*J!V:6,-#79M8W-B
  2810. M(&5Q=2 D9# Q. UB:V=N9"!E<74@)&0P,C -8F]R9&5R(&5Q=2 D9# R,0US
  2811. M<W1A<G0@97%U(#$S-#0@.U)/5Z YH$E.H%-#4D5%3J!-14U/4EF@052@,3 R
  2812. M- T-#2J@:T523D%,#0UC:')O=70@97%U("1F9F0R#6=E=&EN(&5Q=2 D9F9E
  2813. M- T-*J!S3TU%H%9!4DE!0DQ%4PT-9VQO8GAM:6X@/2 D,V8@.W1(15-%H$%2
  2814. M1:!54T5$H$E.H$-,14%224Y'H%1(10UG;&]B>&UA>" ]("0T," [1%)!5TE.
  2815. M1Z H1TQ/0D%,*:!"549&15(-9VQO8GEM:6X@/2 D-#$-9VQO8GEM87@@/2 D
  2816. M-#(-;&]C>&UI;B ]("0U-R [=$A%4T6@05)%H%53142@24Z@0TQ%05))3D>@
  2817. M5$A%#6QO8WAM87@@/2 D-3@@.V5O<J H3$]#04PIH$)51D9%4@UL;V-Y;6EN
  2818. M(#T@)#4Y#6QO8WEM87@@/2 D-C -<#%X(#T@)#DR(#MT2$531:!!4D6@5$5-
  2819. M4$]205)9H%-43U)!1T4-<#%Y(#T@)#DS(#MU4T5$H$E.H%!,3U1424Y'H%1(
  2820. M1:!04D]*14-424].#7 Q>B ]("0Y- UP,G@@/2 D.34@.W1(15F@05)%H$A%
  2821. M4D6@4T^@5$A!5*!710UP,GD@/2 D.38@.T1/3B=4H$A!5D6@5$^@4D5#04Q#
  2822. M54Q!5$6@5$A%32X-<#)Z(#T@)&%E#7 S>" ]("1A9B [=$A%6:!-04M%H$Q)
  2823. M1D6@14%362X-<#-Y(#T@)&(P#7 S>B ]("1B,2 [=TA9H$%21:!93U6@3$]/
  2824. M2TE.1Z!!5*!-1:!,24M%H%1(050_#7 Q=" ]("1B,B [9$].)U2@64]5H%12
  2825. M55-4H$U%/PUP,G0@/2 D8C,-<#-T(#T@)&(T(#MH059)3D>@04Y/5$A%4J!#
  2826. M2$E,1*!705-.)U2@35F@241%02X-:6YD97@@/2 D-3$-8V]U;G1P=',@/2 D
  2827. M-3(->F]O;2 ]("0W,2 [>D]/3:!&04-43U(-9'-X(#T@)#8Q(#MD<WB@25.@
  2828. M5$A%H$E.0U)%345.5*!&3U(-(" @.U)/5$%424Y'H$%23U5.1*!8#61S>2 ]
  2829. M("0V,B [<TE-24Q!4J!&3U*@9'-Y+*!D<WH-9'-Z(#T@)#8S#7-X(#T@)#8T
  2830. M(#MT2$531:!!4D6@5$A%H$%#5%5!3*!!3D=,15.@24Z@6*!9H$%.1*!:#7-Y
  2831. M(#T@)#8U#7-Z(#T@)#8V#70Q(#T@)#8W(#MT2$531:!!4D6@55-%1*!)3J!4
  2832. M2$6@4D]4051)3TX-=#(@/2 D-C@-=#,@/2 D-CD@.W-%1:!42$6@05)424-,
  2833. M1:!&3U*@34]21:!$151!24Q3#70T(#T@)#9A#70U(#T@)#9B#70V(#T@)#9C
  2834. M#70W(#T@)#9D#70X(#T@)#9E#70Y(#T@)#9F#70Q," ]("0W, UA,3$@/2 D
  2835. M834@.W1(15-%H$%21:!42$6@14Q%345.5%.@3T:@5$A%H%)/5$%424].H$U!
  2836. M5%))6 UB,3(@/2 D838@.WAY>@UC,3,@/2 D83<-9#(Q(#T@)&$X(#MT2$6@
  2837. M3E5-0D52H$1%3D]415.@*%)/5RQ#3TQ534XI#64R,B ]("1A.0UF,C,@/2 D
  2838. M86$-9S,Q(#T@)&%B#6@S,B ]("1A8PUI,S,@/2 D860-#0TJ*BJ@;4%#4D]3
  2839. M#0UM;W9E(&UA8PT@;&1A(%TQ#2!S=&$@73(-(#P\/ T-9V5T:V5Y(&UA8R @
  2840. M.W=!252@1D]2H$&@2T594%)%4U,-=V%I="!J<W(@9V5T:6X-(&-M<" C,# -
  2841. M(&)E<2!W86ET#2 \/#P-#61E8G5G(&UA8R @.W!224Y4H$&@0TA!4D%#5$52
  2842. M#2!D;Z PH* [9$].)U2@05-314U"3$4-#2!L9&&@(UTQ#2!J<W*@8VAR;W5T
  2843. M#2!C;&D-(#X^/B!G971K97D@.V%.1*!704E4H%1/H$-/3E1)3E5%#2!C;7 @
  2844. M(R=3)R [;5F@4T5#4D5#5*!35TE40TB@2T59#2!B;F4@;#$-(&IS<B!C;&5A
  2845. M;G5P#2!J;7 @9&]N90UL,2!C;7 @(R=8)R [;5F@4T5#4D54H$%"3U)4H$M%
  2846. M60T@8FYE(&1O;F4-(&IM<"!C;&5A;G5P#2!F:6X-9&]N92 \/#P-#61E8G5G
  2847. M82!M86,-(&1OH# -(&QD82!=,0T@<W1A(#$P,C0-(&9I;@UD;VYE82 \/#P-
  2848. M#2HM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM#0T@;&1A(",D,# -
  2849. M('-T82!B:V=N9 T@<W1A(&)O<F1E<@T@;&1A('9M8W-B#2!A;F0@(R4P,# P
  2850. M,3$Q,2 [<T-2145.H$U%34]26:!43Z Q,#(T#2!O<F$@(R4P,# Q,# P, T@
  2851. M<W1A('9M8W-B#0T@;&1Y(",P, T@;&1A(",\='1E>'0-('-T82!T96UP,0T@
  2852. M;&1A(",^='1E>'0-('-T82!T96UP,@T@:FUP('1I=&QE#71T97AT(&AE>" Y
  2853. M,S U,3$Q,3$Q(#M#3$5!4J!30U)%14XLH%=(251%+*!#4E-2H$1.#2!T>'0@
  2854. M)Z"@H*"@H*"@H*"@H*!#54)%,T2@5C,N,B<L,$0L,$0-('1X=" GH*"@H*"@
  2855. MH*"@H*"@H*"@H*"@0EDG+#!$#2!H97@@.68@.T-904X-('1X=" GH*"@H%-4
  2856. M15!(14Z@2E5$1"<-(&AE>" Y.0T@='AT(">@H*"@1T5/4D=%H%1!64Q/4B<L
  2857. M,$0L,$0-(&AE>" Y8@T@='AT(">@H$-(14-+H$]55*!42$6@2D%.+J Y-:!)
  2858. M4U-51:!/1B<L,$0-(&AE>" Y-@T@='AT(">@H$,]2$%#2TE.1R<-(&AE>" Y
  2859. M8@T@='AT(">@1D]2H$U/4D6@1$5404E,4R$G+#!$#2!H97@@,&0Q9#%D.64Q
  2860. M,@T@='AT("=&,2]&,B<L.3(-('1X=" GH"V@24Y#+T1%0Z!8+5)/5$%424].
  2861. M)RPP1 T@:&5X(#%D,60Q,@T@='AT("=&,R]&-"<L.3(-('1X=" GH"V@24Y#
  2862. M+T1%0Z!9+5)/5$%424].)RPP1 T@:&5X(#%D,60Q,@T@='AT("=&-2]&-B<L
  2863. M.3(-('1X=" GH"V@24Y#+T1%0Z!:+5)/5$%424].)RPP1 T@:&5X(#%D,60Q
  2864. M,@T@='AT(">@1C>@H"<L.3(-('1X=" GH"V@4D53150G+#!$#2!H97@@,60Q
  2865. M9#$R#2!T>'0@)Z K+RV@)RPY,@T@='AT(">@+:!:3T]-H$E.+T]55"<L,$0-
  2866. M(&AE>" Q9#%D,3(-('1X=" GH*!(H* G+#DR#2!T>'0@)Z MH%1/1T=,1:!(
  2867. M241$14Z@4U521D%#15,G+#!$#2!H97@@,60Q9#$R#2!T>'0@)U-004-%)RPY
  2868. M,@T@='AT(">@+:!43T='3$6@4U521D%#1:!&24Q,24Y')RPP1"PP1 T@='AT
  2869. M(">@H%!215-3H%&@5$^@455)5"<L,$0-(&AE>" P9# U#2!T>'0@)Z"@H*"@
  2870. MH%!215-3H$%.6:!+15F@5$^@0D5'24XG+#!$#2!H97@@,# -=&ET;&4@;&1A
  2871. M("AT96UP,2DL>0T@8F5Q(#IC;VYT#2!J<W(@8VAR;W5T#2!I;GD-(&)N92!T
  2872. M:71L90T@:6YC('1E;7 R#2!J;7 @=&ET;&4-.F-O;G0@/CX^(&=E=&ME>0T-
  2873. M*BHJ*J!S152@55"@5$%"3$53*#\I#0TJH'1!0DQ%4Z!!4D6@0U524D5.5$Q9
  2874. MH%-%5*!54*!)3J!B87-I8PTJH$%.1*!"6:!42$6@05-314U"3$52+@T-=&%B
  2875. M;&5S(&QD82 C/G1M871H,0T@<W1A('HQ*S$-('-T82!Z,BLQ#2!L9&$@(SYT
  2876. M;6%T:#(-('-T82!Z,RLQ#2!S=&$@>C0K,0T-*BHJ*J!C3$5!4J!30U)%14Z@
  2877. M04Y$H%-%5*!54* B0DE434%0(@US971U<"!L9&$@(R0P,2 [=TA)5$4-('-T
  2878. M82 D9# R,2 [=$A)4Z!)4Z!$3TY%H%-/H%1(052@3TQ$15(-(&QD82 C,30W
  2879. M(#M-04-(24Y%4Z!724Q,H%-%5*!54 T@:G-R(&-H<F]U= T@;&1A(",D,# @
  2880. M.T-/4E)%0U1,60T@<W1A("1D,#(Q#2!L9&$@(SQS<W1A<G0-(&%D8R C,3(@
  2881. M.W1(1:!'3T%,H$E3H%1/H$-%3E1%4J!42$6@1U)!4$A)0U,-('-T82!T96UP
  2882. M,2 [8T],54U.H#$R#2!L9&$@(SYS<W1A<G0@.W)/5Z Y#2!S=&$@=&5M<#$K
  2883. M,2 [<W-T87)TH%!/24Y44Z!43Z!23U>@.0T@;&1A(",P, T@;&1Y(",P, T@
  2884. M;&1X(",P," [6*!724Q,H$-/54Y4H#$VH%)/5U.@1D]2H%53#2!C;&,-#3IL
  2885. M;V]P('-T82 H=&5M<#$I+'D-(&EN>0T@861C(",Q-@T@8F-C(#IL;V]P#2!C
  2886. M;&,-(&QD82!T96UP,0T@861C(",T," [;D5%1*!43Z!!1$2@-#"@5$^@5$A%
  2887. MH$)!4T6@4$])3E1%4@T@<W1A('1E;7 Q(#MT3Z!*54U0H%1/H%1(1:!.15A4
  2888. MH%)/5PT@;&1A('1E;7 Q*S$-(&%D8R C,# @.W1!2T6@0T%21:!/1J!#05)2
  2889. M2453#2!S=&$@=&5M<#$K,0T@;&1Y(",P, T@:6YX#2!T>&$@(#MXH$E3H$%,
  2890. M4T^@04Z@24Y$15B@24Y43Z!42$6@0TA!4D%#5$52H$Y534)%4@T@8W!X(",Q
  2891. M-@T@8FYE(#IL;V]P(#MN145$H%1/H$1/H$E4H#$VH%1)3453#0TJ*BHJH&-,
  2892. M14%2H$)51D9%4E,-#2!L9&$@(SQB=69F,0T@<W1A(&)U9F9E<@T@;&1A(",^
  2893. M8G5F9C$-('-T82!B=69F97(K,0T@;&1Y(",D,# -(&QD>" C,C0@.V%34U5-
  2894. M24Y'H$%,3*!42%)%1:!"549&15)3H$%210T@;&1A(",D,# @.T)!0TLM5$\M
  2895. M0D%#2PTZ8FQO;W @<W1A("AB=69F97(I+'D-(&EN>0T@8FYE(#IB;&]O< T@
  2896. M:6YC(&)U9F9E<BLQ#2!D97@-(&)N92 Z8FQO;W -#2HJ*BJ@<T54H%50H$)5
  2897. M1D9%4E,-#2!L9&$@(SQB=69F,0T@<W1A(&)U9F9E<@T@;&1A(",^8G5F9C$-
  2898. M('-T82!B=69F97(K,0T@<W1A('IT96UP(#M:5$5-4*!724Q,H$U!2T6@3$E&
  2899. M1:!324U03$6@1D]2H%53#2!L9&$@=FUC<V(-(&%N9" C)3$Q,3$P,# Q(#MS
  2900. M5$%25*!(15)%H%-/H%1(052@4U=!4*!"549&15)3H%=)3$R@5T]22Z!224=(
  2901. M5 T@;W)A(",E,# P,#$Q,3 -('-T82!V;6-S8@T-*BHJ*J!S152@55"@24Y)
  2902. M5$E!3*!604Q515,-#6EN:70@;&1A(",P, T@<W1A(&QO8WAM:6X-('-T82!L
  2903. M;V-X;6%X#2!S=&$@;&]C>6UI;@T@<W1A(&QO8WEM87@-('-T82!G;&]B>&UI
  2904. M;@T@<W1A(&=L;V)Y;6EN#2!S=&$@9VQO8GAM87@-('-T82!G;&]B>6UA> T@
  2905. M<W1A(&1S> T@<W1A(&1S>0T@<W1A(&1S>@T@<W1A('-X#2!S=&$@<WD-('-T
  2906. M82!S>@T@<W1A(&9I;&P-(&QD82 C,#$-('-T82!H:61E#2!L9&$@(S8T#2!S
  2907. M=&$@>F]O;0T-*BTM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2T-*J!M
  2908. M04E.H$Q/3U -#2HJ*BJ@9T54H$M%65!215-3#0UM86EN#2!C;&D-:W!R97-S
  2909. M(&IS<B!G971I;@T@8VUP(",Q,S,@.V8Q/PT@8FYE(#IF,@T@;&1A(&1S> T@
  2910. M8VUP("-A;F=M87@O,B [;D^@34]21:!42$%.H%!)#2!B97$@.F-O;G0Q#2!I
  2911. M;F,@9'-X(#M/5$A%4E=)4T6@24Y#4D5!4T6@6"U23U1!5$E/3@T@:FUP(#IC
  2912. M;VYT#3IF,B!C;7 @(S$S-R [9C(_#2!B;F4@.F8S#2!L9&$@9'-X#2!B97$@
  2913. M.F-O;G0Q#2!D96,@9'-X#2!J;7 @.F-O;G0-.F8S(&-M<" C,3,T#2!B;F4@
  2914. M.F8T#2!L9&$@9'-Y#2!C;7 @(V%N9VUA>"\R#2!B97$@.F-O;G0Q#2!I;F,@
  2915. M9'-Y(#MI3D-214%31:!9+5)/5$%424].#2!J;7 @.F-O;G0-.F8T(&-M<" C
  2916. M,3,X#2!B;F4@.F8U#2!L9&$@9'-Y#2!B97$@.F-O;G0Q#2!D96,@9'-Y#2!J
  2917. M;7 @.F-O;G0-.F8U(&-M<" C,3,U#2!B;F4@.F8V#2!L9&$@9'-Z#2!C;7 @
  2918. M(V%N9VUA>"\R#2!B97$@.F-O;G0Q#2!I;F,@9'-Z(#M:+5)/5$%424].#2!J
  2919. M;7 @.F-O;G0-.F8V(&-M<" C,3,Y#2!B;F4@.F8W#2!L9&$@9'-Z#2!B97$@
  2920. M.F-O;G0Q#2!D96,@9'-Z#2!J;7 @.F-O;G0-.F8W(&-M<" C,3,V#2!B;F4@
  2921. M.G!L=7,-(&IM<"!I;FET#3IC;VYT,2!J;7 @.F-O;G0-.G!L=7,@8VUP(",G
  2922. M*R<-(&)N92 Z;6EN=7,-(&EN8R!Z;V]M(#MB04@LH%=(3Z!.145$4Z!%4E)/
  2923. M4J!#2$5#2TE.1S\-(&EN8R!Z;V]M#2!J;7 @.F-O;G0-.FUI;G5S(&-M<" C
  2924. M)RTG#2!B;F4@.F@-(&1E8R!Z;V]M#2!D96,@>F]O;0T@8G!L(#IC;VYT#2!I
  2925. M;F,@>F]O;0T@:6YC('IO;VT-(&IM<" Z8V]N= TZ:"!C;7 @(R=()PT@8FYE
  2926. M(#IS<&%C90T@;&1A(&AI9&4-(&5O<B C)# Q#2!S=&$@:&ED90T@:FUP(#IC
  2927. M;VYT#3IS<&%C92!C;7 @(R>@)PT@8FYE(#IQ#2!L9&$@9FEL; T@96]R(",D
  2928. M,#$-('-T82!F:6QL#2!J;7 @.F-O;G0-.G$@8VUP(",G42<@.U&@455)5%,-
  2929. M(&)N92 Z8V]N= T@:FUP(&-L96%N=7 -#3IC;VYT('-E:2 @.W-0145$H%1(
  2930. M24Y'4Z!54*!!H$))5 T-*BHJ*J!U4$1!5$6@04Y'3$53#0UU<&1A=&4@8VQC
  2931. M#2!L9&$@<W@-(&%D8R!D<W@-(&-M<" C86YG;6%X(#MA4D6@5T6@/CV@34%8
  2932. M24U53:!!3D=,13\-(&)C8R Z8V]N=#$-('-B8R C86YG;6%X(#II1B!33RP@
  2933. M4D53150-.F-O;G0Q('-T82!S> T@8VQC#2!L9&$@<WD-(&%D8R!D<WD-(&-M
  2934. M<" C86YG;6%X#2!B8V,@.F-O;G0R#2!S8F,@(V%N9VUA>" [<T%-1:!$14%,
  2935. M#3IC;VYT,B!S=&$@<WD-(&-L8PT@;&1A('-Z#2!A9&,@9'-Z#2!C;7 @(V%N
  2936. M9VUA> T@8F-C(#IC;VYT,PT@<V)C("-A;F=M87@-.F-O;G0S('-T82!S>@T-
  2937. M*BHJ*J!R3U1!5$6@0T]/4D1)3D%415,-#7)O=&%T90T-*BHJH&9)4E-4+*!#
  2938. M04Q#54Q!5$6@5#$L5#(L+BXN+%0Q, T-*BJ@=%=/H$U!0U)/4Z!43Z!324U0
  2939. M3$E&6:!/55*@3$E&10UA9&1A(&UA8R @.V%$1*!45T^@04Y'3$53H%1/1T54
  2940. M2$52#2!C;&,-(&QD82!=,0T@861C(%TR#2!C;7 @(V%N9VUA>" [:5.@5$A%
  2941. MH%-53: ^H#(J4$D_#2!B8V,@9&]N90T@<V)C("-A;F=M87@@.VE&H%-/+*!3
  2942. M54)44D%#5* R*E!)#61O;F4@/#P\#0US=6)A(&UA8R @.W-50E1204-4H%17
  2943. M3Z!!3D=,15,-('-E8PT@;&1A(%TQ#2!S8F,@73(-(&)C<R!D;VYE#2!A9&,@
  2944. M(V%N9VUA>" [;T]04RR@5T6@3D5%1*!43Z!!1$2@,BI020UD;VYE(#P\/ T-
  2945. M*BJ@;D]7H$-!3$-53$%41:!4,2Q4,BQ%5$,N#0T@/CX^('-U8F$L<WD[<WH-
  2946. M('-T82!T,2 [5#$]4UDM4UH-(#X^/B!A9&1A+'-Y.W-Z#2!S=&$@=#(@.U0R
  2947. M/5-9*U-:#2 ^/CX@861D82QS>#MS>@T@<W1A('0S(#M4,SU36"M36@T@/CX^
  2948. M('-U8F$L<W@[<WH-('-T82!T-" [5#0]4U@M4UH-(#X^/B!A9&1A+'-X.W0R
  2949. M#2!S=&$@=#4@.U0U/5-8*U0R#2 ^/CX@<W5B82QS>#MT,0T@<W1A('0V(#M4
  2950. M-CU36"U4,0T@/CX^(&%D9&$L<W@[=#$-('-T82!T-R [5#<]4U@K5#$-(#X^
  2951. M/B!S=6)A+'0R.W-X#2!S=&$@=#@@.U0X/50R+5-8#2 ^/CX@<W5B82QS>3MS
  2952. M> T@<W1A('0Y(#M4.3U362U36 T@/CX^(&%D9&$L<W@[<WD-('-T82!T,3 @
  2953. M.U0Q,#U36"M360T-*J!E5*!63TE,02$-#2HJ*J!N15A4+*!#04Q#54Q!5$6@
  2954. M82QB+&,L+BXN+&D-#2HJH&%.3U1(15*@55-%1E5,H$Q)5%1,1:!-04-23PUD
  2955. M:78R(&UA8R @.V1)5DE$1:!!H%-)1TY%1*!.54U"15*@0EF@,@T[:52@25.@
  2956. M05-354U%1*!42$%4H%1(1:!.54U"15(-(&)P;"!P;W,@.TE3H$E.H%1(1:!!
  2957. M0T-5355,051/4@T@8VQC#2!E;W(@(R1F9B [=T6@3D5%1*!43Z!53BU.14=!
  2958. M5$E61:!42$6@3E5-0D52#2!A9&,@(S Q(#M"6:!404M)3D>@250G4Z!#3TU0
  2959. M3$5-14Y4#2!L<W(@(#M$259)1$6@0EF@5%=/#2!C;&,-(&5O<B C)&9F#2!A
  2960. M9&,@(S Q(#MM04M%H$E4H$Y%1T%4259%H$%'04E.#2!J;7 @9&]N961I=@UP
  2961. M;W,@;'-R(" [;E5-0D52H$E3H%!/4TE4259%#61O;F5D:78@/#P\#0UM=6PR
  2962. M(&UA8R @.VU53%1)4$Q9H$&@4TE'3D5$H$Y534)%4J!"6: R#2!B<&P@<&]S
  2963. M;0T@8VQC#2!E;W(@(R1F9@T@861C(",D,#$-(&%S; T@8VQC#2!E;W(@(R1F
  2964. M9@T@861C(",D,#$-(&IM<"!D;VYE;75L#7!O<VT@87-L#61O;F5M=6P@/#P\
  2965. M#0TJ*J!N3U1%H%1(052@5T6@05)%H$-54E)%3E1,6:!-04M)3D>@0:!-24Y/
  2966. M4J!,14%0#2HJH$]&H$9!251(H%1(052@3D^@3U9%4D9,3U=3H%=)3$R@3T-#
  2967. M55(N#0TZ8V%L8V$@8VQC#2!L9'@@=#$-(&QD82!C;W,L> T@;&1X('0R#2!A
  2968. M9&,@8V]S+'@-('-T82!A,3$@.V$]*$-/4RA4,2DK0T]3*%0R*2DO,@TZ8V%L
  2969. M8V(@;&1X('0Q#2!L9&$@<VEN+'@-('-E8PT@;&1X('0R#2!S8F,@<VEN+'@-
  2970. M('-T82!B,3(@.V(]*%-)3BA4,2DM4TE.*%0R*2DO,@TZ8V%L8V,@;&1X('-Y
  2971. M#2!L9&$@<VEN+'@-(#X^/B!M=6PR#2!S=&$@8S$S(#MC/5-)3BA362D-.F-A
  2972. M;&-D('-E8PT@;&1X('0X#2!L9&$@8V]S+'@-(&QD>"!T-PT@<V)C(&-O<RQX
  2973. M#2!S96,-(&QD>"!T-0T@<V)C(&-O<RQX#2!C;&,-(&QD>"!T-@T@861C(&-O
  2974. M<RQX(#MD23TH0T]3*%0X*2U#3U,H5#<I*T-/4RA4-BDM0T]3*%0U*2DO,@T@
  2975. M/CX^(&1I=C(-(&-L8PT@;&1X('0S#2!A9&,@<VEN+'@-('-E8PT@;&1X('0T
  2976. M#2!S8F,@<VEN+'@-('-T82!D,C$@.V0]*%-)3BA4,RDM4TE.*%0T*2MD22DO
  2977. M,@TZ8V%L8V4@<V5C#2!L9'@@=#4-(&QD82!S:6XL> T@;&1X('0V#2!S8F,@
  2978. M<VEN+'@-('-E8PT@;&1X('0W#2!S8F,@<VEN+'@-('-E8PT@;&1X('0X#2!S
  2979. M8F,@<VEN+'@@.V5)/2A324XH5#4I+5-)3BA4-BDM4TE.*%0W*2U324XH5#@I
  2980. M*2\R#2 ^/CX@9&EV,@T@8VQC#2!L9'@@=#,-(&%D8R!C;W,L> T@8VQC#2!L
  2981. M9'@@=#0-(&%D8R!C;W,L> T@<W1A(&4R,B [93TH0T]3*%0S*2M#3U,H5#0I
  2982. M*V5)*2\R#3IC86QC9B!L9'@@=#D-(&QD82!S:6XL> T@<V5C#2!L9'@@=#$P
  2983. M#2!S8F,@<VEN+'@-('-T82!F,C,@.V8]*%-)3BA4.2DM4TE.*%0Q,"DI+S(-
  2984. M.F-A;&-G(&QD>"!T-@T@;&1A('-I;BQX#2!S96,-(&QD>"!T. T@<V)C('-I
  2985. M;BQX#2!S96,-(&QD>"!T-PT@<V)C('-I;BQX#2!S96,-(&QD>"!T-0T@<V)C
  2986. M('-I;BQX(#MG23TH4TE.*%0V*2U324XH5#@I+5-)3BA4-RDM4TE.*%0U*2DO
  2987. M,@T@/CX^(&1I=C(-(&-L8PT@;&1X('0T#2!A9&,@8V]S+'@-('-E8PT@;&1X
  2988. M('0S#2!S8F,@8V]S+'@-('-T82!G,S$@.V<]*$-/4RA4-"DM0T]3*%0S*2MG
  2989. M22DO,@TZ8V%L8V@@8VQC#2!L9'@@=#8-(&QD82!C;W,L> T@;&1X('0W#2!A
  2990. M9&,@8V]S+'@-('-E8PT@;&1X('0U#2!S8F,@8V]S+'@-('-E8PT@;&1X('0X
  2991. M#2!S8F,@8V]S+'@@.VA)/2A#3U,H5#8I*T-/4RA4-RDM0T]3*%0U*2U#3U,H
  2992. M5#@I*2\R#2 ^/CX@9&EV,@T@8VQC#2!L9'@@=#,-(&%D8R!S:6XL> T@8VQC
  2993. M#2!L9'@@=#0-(&%D8R!S:6XL> T@<W1A(&@S,B [:#TH4TE.*%0S*2M324XH
  2994. M5#0I*VA)*2\R#3IW:&5W(&-L8PT@;&1X('0Y#2!L9&$@8V]S+'@-(&QD>"!T
  2995. M,3 -(&%D8R!C;W,L> T@<W1A(&DS,R [:3TH0T]3*%0Y*2M#3U,H5#$P*2DO
  2996. M,@T-*BJ@:50G4Z!!3$R@1$]73DA)3$R@1E)/3:!(15)%+@T-9&]W;FAI;&P-
  2997. M*BHJ*J!C3$5!4J!"549&15(-*J!AH$Q)5%1,1:!-04-23PT-<V5T8G5F(&UA
  2998. M8R @.W!55*!"549&15)3H%=(15)%H%1(15F@0T%.H$)%H$A54E0-(&QD82 C
  2999. M,# -('-T82!B=69F97(-(&QD82!Z=&5M<" [:$E'2*!"651%#7-T86)U9B!S
  3000. M=&$@8G5F9F5R*S$-(#P\/ T-(#X^/B!S971B=68-8VQR9')A=R!L9'@@(S X
  3001. M#2!L9&$@(S P#3IF;V]L(&QD>2 C,# -.F1O<&4@<W1A("AB=69F97(I+'D-
  3002. M(&EN>0T@8FYE(#ID;W!E#2!I;F,@8G5F9F5R*S$-(&1E> T@8FYE(#IF;V]L
  3003. M#0TJ*BHJH&U9H$=/3T1.15-3H$)55*!I)TV@0:!$3U!%#2IC;')D<F%WH&QD
  3004. M8:!G;&]B>&UI;@TJH&QS<J"@.VY%142@5$^@1T54H$E.5$^@5$A%H%))1TA4
  3005. MH$-/3%5-3@TJH&)C8Z Z979E;J [95A03$%)3D5$H$E.H$U/4D6@1$5404E,
  3006. MH$)%3$]7#2J@;&1YH",D.# -*J!S='F@8G5F9F5RH#MP4D5354U!0DQ9H%1(
  3007. M25.@5TE,3*!"1:!!H$Q)5%1,10TJH&-L8Z"@.TU/4D6@149&24-)14Y4+@TJ
  3008. M.F5V96Z@861CH&)U9F9E<BLQ#2J@<W1AH&)U9F9E<BLQ#2J@;&1AH&=L;V)X
  3009. M;6%X#2J@<V5C#2J@<V)CH&=L;V)X;6EN#2J@=&%X#2J@:6YX#2J@;&1YH&=L
  3010. M;V)Y;6%X#2J@8F5QH#IR97-E= TJ.GEA>:!L9&&@(R0P, TJH&QD>:!G;&]B
  3011. M>6UA> TJ.F)L86B@<W1AH"AB=69F97(I+'D-*J!D97D-*J!C<'F@9VQO8GEM
  3012. M:6X-*J!B8W.@.F)L86@-*J!L9&&@8G5F9F5R#2J@96]RH",D.# -*J!S=&&@
  3013. M8G5F9F5R#2J@8FYEH#IW:&]P964-*J!I;F.@8G5F9F5R*S$-*CIW:&]P966@
  3014. M9&5X#2J@8FYEH#IY87D-*CIR97-E=*!L9&&@(S"@.VY%142@5$^@4D53152@
  3015. M5$A%4T6@1U594PTJH'-T8:!G;&]B>&UA> TJH'-T8:!G;&]B>6UA> TJH&QD
  3016. M8: C)&9F#2J@<W1AH&=L;V)X;6EN#2J@<W1AH&=L;V)Y;6EN#0TJ*BHJH&Y%
  3017. M6%0LH%)%042@04Y$H$1205>@4$],64=/3E,-#7)E861D<F%W(&QD>2 C,# -
  3018. M('-T>2!I;F1E> UO8FIL;V]P(&QD>2!I;F1E> T@;&1A('!O;'EL:7-T+'D@
  3019. M.V9)4E-4+*!42$6@3E5-0D52H$]&H%!/24Y44PT@8FYE(#IC;VYT(#MB552@
  3020. M24:@3E5-4$])3E13H$E3H%I%4D^@5$A%3@T@:FUP(&]B:F1O;F4@.U=%H$%2
  3021. M1:!!5*!42$6@14Y$H$]&H%1(1:!,25-4#3IC;VYT('-T82!C;W5N='!T<PT@
  3022. M:6YC(&EN9&5X#0TJH')/5$%41:!04D]*14-4H$%.1*!$4D%7H%1(1:!03TQ9
  3023. M1T].#2J@;4%+1:!355)%H$)51D9%4J!"14E.1Z!$4D%73J!43Z!)4Z!#3$5!
  3024. M4B$-#3ID;VET(&IS<B!R;W1P<F]J#0TJH&-/3E9%4E2@6$U)3J!!3D2@6$U!
  3025. M6*!43Z!#3TQ534Y3#0T@;&1A(&QO8WAM:6X-(&QS<@T@;'-R#2!L<W(@(#M8
  3026. MH$U/1* X#2!S=&$@;&]C>&UI;@T@8VUP(&=L;V)X;6EN#2!B8W,@.FYA: T@
  3027. M<W1A(&=L;V)X;6EN#3IN86@@;&1A(&QO8WEM:6X-(&-M<"!G;&]B>6UI;@T@
  3028. M8F-S(#IU:'5H#2!S=&$@9VQO8GEM:6X-.G5H=6@@;&1A(&QO8WAM87@-(&QS
  3029. M<@T@;'-R#2!L<W(-('-T82!L;V-X;6%X#2!C;7 @9VQO8GAM87@-(&)C8R Z
  3030. M;F]W87D-('-T82!G;&]B>&UA> TZ;F]W87D@;&1A(&QO8WEM87@-(&-M<"!G
  3031. M;&]B>6UA> T@8F-C(&5O<F9I;&P-('-T82!G;&]B>6UA> T-*J!I1J!54TE.
  3032. M1Z!42$6@96]R+4)51D9%4BR@0T]06:!)3E1/H$1205=)3D>@0E5&1D52#2J@
  3033. M84Y$H%1(14Z@0TQ%05*@5$A%H&5O<BU"549&15(-#65O<F9I;&P@;&1A(&9I
  3034. M;&P-(&)E<2!O8FIL;V]P#0T@/CX^('-E=&)U9@T@;&1A(",\96]R8G5F#2!S
  3035. M=&$@=&5M<#$-(&QD82 C/F5O<F)U9@T@<W1A('1E;7 Q*S$-#2!L9&$@;&]C
  3036. M>&UI;B [;&]C>&UI;J!.3U>@0T].5$%)3E.@0T],54U.#2!L<W(@(#ME04-(
  3037. MH$-/3%5-3J!)4Z Q,CB@0EE415,-(&)C8R Z979E;B [<T^@5$A%4D6@34E'
  3038. M2%2@0D6@0:!#05)260T@;&1Y(",D.# -('-T>2!B=69F97(-('-T>2!T96UP
  3039. M,0T@8VQC#3IE=F5N('-T82!T,@T@861C(&)U9F9E<BLQ#2!S=&$@8G5F9F5R
  3040. M*S$@.V5!0TB@0T],54U.H$E3H#$R.*!"651%4PT@;&1A('0R#2!A9&,@=&5M
  3041. M<#$K,2 [;D]7H%=%H%=)3$R@4U1!4E2@052@5$A%#2!S=&$@=&5M<#$K,2 [
  3042. M0T],54U.#0T@;&1A(&QO8WAM87@-('-E8PT@<V)C(&QO8WAM:6X-('1A>" [
  3043. M=$]404R@3E5-0D52H$]&H$-/3%5-3E.@5$^@1$\-(&EN>" @.T4N1RZ@1DE,
  3044. M3*!#3TQ534Y3H#$N+C,-(&QD>2!L;V-Y;6%X#2!B;F4@.F9O;W -(&EN8R!L
  3045. M;V-Y;6%X#3IF;V]P(&QD>2!L;V-Y;6%X#2!L9&$@(S P#3IG;V]P(&5O<B H
  3046. M=&5M<#$I+'D@.V5O<BU"549&15(-('!H80TJH&U!64)%H%!55*!!3J!E;W*@
  3047. M0D5,3U<_#2!E;W(@*&)U9F9E<BDL>0T@<W1A("AB=69F97(I+'D-(&QD82 C
  3048. M,# @.VU)1TA4H$%3H%=%3$R@0TQ%05*@252@3D]7#2!S=&$@*'1E;7 Q*2QY
  3049. M#2!P;&$-(&1E>0T@8W!Y(&QO8WEM:6X-(&)C<R Z9V]O< T@;&1A(&)U9F9E
  3050. M<@T@96]R(",D.# -('-T82!B=69F97(-('-T82!T96UP,0T@8FYE(#IB;V]P
  3051. M#2!I;F,@8G5F9F5R*S$-(&EN8R!T96UP,2LQ#3IB;V]P(&1E> T@8FYE(#IF
  3052. M;V]P#2!J;7 @;V)J;&]O< T-;V)J9&]N90TJ*BHJH'-705"@0E5&1D524PT-
  3053. M<W=A<&)U9B!L9&$@=FUC<V(-(&5O<B C)# R(#MP4D545%F@5%))0TM9+*!%
  3054. M2#\-('-T82!V;6-S8@T@;&1A(",D,#@-(&5O<B!Z=&5M<" [6E1%35 ]2$E'
  3055. M2*!"651%H$I54U2@1DQ)4%,-('-T82!Z=&5M<" [0D545T5%3J D,S"@04Y$
  3056. MH"0S. T-(&IM<"!M86EN(#MA4D]53D2@04Y$H$%23U5.1*!71:!'3RXN+@T-
  3057. M('1X=" G9T5%H&)204E.+*!72$%4H$1/H%E/5:!704Y4H%1/H$1/H"<-('1X
  3058. M=" G5$].24=(5#\G#0TJ*J!R3U1!5$4LH%!23TI%0U0LH$%.1*!35$]21:!4
  3059. M2$6@4$])3E13#2H-*J!T2$E3H%!!4E2@25.@0:!324=.249)0T%.5*!#2$%.
  3060. M1T6@4TE.0T4-*J!6,BXP+J"@;D]7H$E4H$E3H$&@0T]-4$Q%5$5,6:!'14Y%
  3061. M4D%,H%!/3%E'3TZ@4$Q/5%1%4BX-*J!AH%-%5*!/1J!03TE.5%.@25.@4D5!
  3062. M1*!)3BR@4D]4051%1*!!3D2@4%)/2D5#5$5$+*!!3D0-*J!03$]45$5$H$E.
  3063. M5$^@5$A%H$1205=)3D>@0E5&1D52H"AE;W*@3U*@3D]234%,*2X-#7)O='!R
  3064. M;VH-#2J@8:!.14%4H$U!0U)/#6YE9R!M86,@(#MC2$%.1T6@5$A%H%-)1TZ@
  3065. M3T:@0:!45T\G4Z!#3TU03$5-14Y4#2!C;&,-(&QD82!=,2 [3E5-0D52+@T@
  3066. M96]R(",D9F8-(&%D8R C)# Q#2 \/#P-#2HM+2TM+2TM+2TM+2TM+2TM+2TM
  3067. M+2TM+2TM+2TM+2TM#2J@=$A%4T6@34%#4D]3H%)%4$Q!0T6@5$A%H%!2159)
  3068. M3U53H%!23TI%0U1)3TX-*J!354)23U5424Y%+@T-<VUU;'2@;6%CH#MM54Q4
  3069. M25!,6:!45T^@4TE'3D5$H#@M0DE4#2 [3E5-0D524SJ@82IY+S8TH"T^H&$-
  3070. M('-T8:!Z,PT@8VQCH* [=$A)4Z!-54Q425!,6:!)4Z!&3U*@3D]234%,#2!E
  3071. M;W*@(R1F9J [3E5-0D524RR@22Y%+J!8/2TV-"XN-C0-(&%D8Z C)# Q#2!S
  3072. M=&&@>C0-(&QD8: H>C,I+'D-('-E8PT@<V)CH"AZ-"DL>0T@/#P\H* [84Q,
  3073. MH$1/3D6@.BD-#7-M=6QT>B!M86,@.VU53%1)4$Q9H%173Z!324=.142@."U"
  3074. M250-(" @.TY534)%4E,ZH&$J>2\V-* M/J!A#2!S=&$@>C$-(&-L8R @.V%.
  3075. M1*!42$E3H$U53%1)4$Q9H$E3H%-014-)1DE#04Q,60T@96]R(",D9F8@.T9/
  3076. M4J!42$6@4%)/2D5#5$E/3J!005)4+*!72$5210T@861C(",D,#$@.TY534)%
  3077. M4E.@05)%H"TQ,3 N+C$Q,*!!3D2@,"XN-# -('-T82!Z,@T@;&1A("AZ,2DL
  3078. M>0T@<V5C#2!S8F,@*'HR*2QY#2 \/#P@(#MA3$R@1$].1: Z*0T-<')O:F5C
  3079. M="!M86,@(#MT2$6@04-454%,H%!23TI%0U1)3TZ@4D]55$E.10T[=$A%H%)/
  3080. M551)3D6@5$%+15.@5$A%H%!/24Y4#3M=,:!=,J!=,RR@4D]4051%4Z!!3D0-
  3081. M.U!23TI%0U13H$E4+*!!3D2@4U1/4D53H%1(10T[4D5354Q4H$E.H%TQH%TR
  3082. MH%TS+@T-(&QD>2!=,2 [;55,5$E03%F@1DE24U2@4D]4051)3TZ@0T],54U.
  3083. M#2!L9&$@83$Q#2 ^/CX@<VUU;'0-('-T82!P,70-(&QD82!D,C$-(#X^/B!S
  3084. M;75L= T@<W1A(' R= T@;&1A(&<S,0T@/CX^('-M=6QT#2!S=&$@<#-T#2!L
  3085. M9'D@73(@.W-%0T].1*!#3TQ534X-(&QD82!B,3(-(#X^/B!S;75L= T@8VQC
  3086. M#2!A9&,@<#%T#2!S=&$@<#%T#2!L9&$@93(R#2 ^/CX@<VUU;'0-(&-L8PT@
  3087. M861C(' R= T@<W1A(' R= T@;&1A(&@S,@T@/CX^('-M=6QT#2!C;&,-(&%D
  3088. M8R!P,W0-('-T82!P,W0-(&QD>2!=,R [=$A)4D2@0T],54U.#2!L9&$@8S$S
  3089. M#2 ^/CX@<VUU;'0-(&-L8PT@861C(' Q= T@<W1A(' Q= T@;&1A(&8R,PT@
  3090. M/CX^('-M=6QT#2!C;&,-(&%D8R!P,G0-('-T82!P,G0-(&QD82!I,S,-(#X^
  3091. M/B!S;75L= T@8VQC#2!A9&,@<#-T#2!S=&$@73,@.W)/5$%4142@>@T@=&%X
  3092. M#2!L9'D@>F1I=BQX(#MT04),1:!/1J!$+RA:*UHP*0T@(" [;D]7H'F@0T].
  3093. M5$%)3E.@4%)/2D5#5$E/3J!#3TY35 T-(&QD82!P,70-(#X^/B!S;75L='H-
  3094. M(&QD>"!Z;V]M#2!C<'@@(S8T#2!B97$@8V]N='@-('-T>2!T96UP,0T@;&1Y
  3095. M('IO;VT-(#X^/B!S;75L= T@;&1Y('1E;7 Q#6-O;G1X(&-L8PT@861C(",V
  3096. M-" [;T9&4T54H%1(1:!#3T]21$E.051%#2!S=&$@73$@.W)/5$%4142@04Y$
  3097. MH%!23TI%0U1%1 T@8VUP(&QO8WAM:6X@.W-%1:!)1J!)5*!)4Z!!H$Q/0T%,
  3098. MH$U)3DE-54T-(&)C<R!N;W1X;6EN#2!S=&$@;&]C>&UI;@UN;W1X;6EN(&-M
  3099. M<"!L;V-X;6%X#2!B8V,@;F]T>&UA> T@<W1A(&QO8WAM87@-#6YO='AM87@@
  3100. M;&1A(' R= T@/CX^('-M=6QT>@T@8W!X(",V- T@8F5Q(&-O;G1Y#2!L9'D@
  3101. M>F]O;0T@/CX^('-M=6QT#6-O;G1Y(&-L8PT@861C(",V- T@<W1A(%TR(#MR
  3102. M3U1!5$5$H$%.1*!04D]*14-4142@>0T@8VUP(&QO8WEM:6X-(&)C<R!N;W1Y
  3103. M;6EN#2!S=&$@;&]C>6UI;@UN;W1Y;6EN(&-M<"!L;V-Y;6%X#2!B8V,@;F]T
  3104. M>6UA> T@<W1A(&QO8WEM87@-#6YO='EM87@@/#P\(" [84Q,H$1/3D4-#2J@
  3105. M;&1AH",\96]R8G5FH#MF25)35*!71:!.145$H%1/H$-,14%2H%1(10TJH'-T
  3106. M8:!B=69F97*@.V5O<J!"549&15(-*J!L9&&@(SYE;W)B=68-*J!S=&&@8G5F
  3107. M9F5R*S$-#2!L9&$@(S @.W)%4T54H'E-24Z@04Y$H'E-05@-('-T82!L;V-Y
  3108. M;6%X#2!S=&$@;&]C>&UA> T@;&1A(",D9F8-('-T82!L;V-Y;6EN#2!S=&$@
  3109. M;&]C>&UI;@T-<F5A9'!T<R!L9'D@:6YD97@-(&QD82!P;VQY;&ES="QY#2!S
  3110. M=&$@<#%X#2!I;GD-(&QD82!P;VQY;&ES="QY#2!S=&$@<#%Y#2!I;GD-(&QD
  3111. M82!P;VQY;&ES="QY#2!S=&$@<#%Z#2!I;GD-(&1E8R!C;W5N='!T<PT@;&1A
  3112. M('!O;'EL:7-T+'D-('-T82!P,G@-(&EN>0T@;&1A('!O;'EL:7-T+'D-('-T
  3113. M82!P,GD-(&EN>0T@;&1A('!O;'EL:7-T+'D-('-T82!P,GH-(&EN>0T@9&5C
  3114. M(&-O=6YT<'1S#2!L9&$@<&]L>6QI<W0L>0T@<W1A(' S> T@:6YY#2!L9&$@
  3115. M<&]L>6QI<W0L>0T@<W1A(' S>0T@:6YY#2!L9&$@<&]L>6QI<W0L>0T@<W1A
  3116. M(' S>@T@:6YY#2!S='D@:6YD97@-(#X^/B!P<F]J96-T+' Q>#MP,7D[<#%Z
  3117. M#2 ^/CX@<')O:F5C="QP,G@[<#)Y.W R>@T@/CX^('!R;VIE8W0L<#-X.W S
  3118. M>3MP,WH-#2!L9&$@:&ED90T@8F5Q(#ID;VET#2!L9&$@<#)X(#MH241$14Z@
  3119. M1D%#1:!#2$5#2PT@<V5C#2!S8F,@<#%X#2!T87D@(#MY/2A8,BU8,2D-(&QD
  3120. M82!P,WD-('-E8PT@<V)C(' R>2 [83TH63,M63(I#2 ^/CX@<VUU;'0-('-T
  3121. M82!T96UP,0T@;&1A(' S> T@<V5C#2!S8F,@<#)X#2!T87D-(&QD82!P,GD-
  3122. M('-E8PT@<V)C(' Q>0T@/CX^('-M=6QT#2!C;7 @=&5M<#$@.VE&H%@Q*EDR
  3123. M+5DQ*E@RH#Z@,*!42$5.H$9!0T4-(&)M:2 Z9&]I=" [25.@5DE324),10T@
  3124. M9&5C(&-O=6YT<'1S(#MO5$A%4E=)4T6@4D5!1*!)3J!214U!24Y)3D<-(&)E
  3125. M<2 Z86)O<G0@.U!/24Y44Z!!3D2@4D5455).#3IP;V]P(&EN8R!I;F1E> T@
  3126. M:6YC(&EN9&5X#2!I;F,@:6YD97@-(&1E8R!C;W5N='!T<PT@8FYE(#IP;V]P
  3127. M#3IA8F]R="!R=',-#3ID;VET(&QD82!P,7@-('-T82!X,0T@;&1A(' Q>0T@
  3128. M<W1A('DQ#2!L9&$@<#)X#2!S=&$@>#(-(&QD82!P,GD-('-T82!Y,@T@:G-R
  3129. M(&1R87<-(&QD82!P,G@-('-T82!X,0T@;&1A(' R>0T@<W1A('DQ#2!L9&$@
  3130. M<#-X#2!S=&$@>#(-(&QD82!P,WD-('-T82!Y,@T@:G-R(&1R87<-#2!D96,@
  3131. M8V]U;G1P=',-(&)N92!P;VQY;&]O<" [:5.@252@2E535*!!H%1224%.1TQ%
  3132. M/PT@:FUP('!O;'ED;VYE#0UP;VQY;&]O<"!L9'D@:6YD97@-(&QD82!P;VQY
  3133. M;&ES="QY#2!S=&$@<#)X#2!I;GD-(&QD82!P;VQY;&ES="QY#2!S=&$@<#)Y
  3134. M#2!I;GD-(&QD82!P;VQY;&ES="QY#2!S=&$@<#)Z#2!I;GD-('-T>2!I;F1E
  3135. M> T@/CX^('!R;VIE8W0L<#)X.W R>3MP,GH-#2!L9&$@<#)X#2!S=&$@>#$-
  3136. M(&QD82!P,GD-('-T82!Y,0T@;&1A(' S> T@<W1A('@R#2!L9&$@<#-Y#2!S
  3137. M=&$@>3(-(&IS<B!D<F%W#0T@;&1A(' R> T@<W1A(' S> T@;&1A(' R>0T@
  3138. M<W1A(' S>0T@9&5C(&-O=6YT<'1S#2!B97$@<&]L>61O;F4-(&IM<"!P;VQY
  3139. M;&]O< UP;VQY9&]N92!L9&$@<#%X(#MC3$]31:!42$6@4$],64=/3@T@<W1A
  3140. M('@R#2!L9&$@<#%Y#2!S=&$@>3(-(&QD82!P,W@-('-T82!X,0T@;&1A(' S
  3141. M>0T@<W1A('DQ#2!J<W(@9')A=PT@<G1S#0T@='AT("=S04U%H%1(24Y'H%=%
  3142. MH$1/H$5615)9H$Y)1TA4+*!P24Y+63J@)PT@='AT("=44EF@5$^@5$%+1:!/
  3143. M5D52H%1(1:!73U),1"$G#0T-*BTM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM
  3144. M+2TM+2T-*J!G14Y%4D%,H%%515-424].04),12U604Q51:!%4E)/4J!04D]#
  3145. M14154D4-#2IC:&]K9:!L9'B@(S P#2HZ;&]O<*!L9&&@.F-T97AT+'@-*J!B
  3146. M97&@.F1O;F4-*J!J<W*@8VAR;W5T#2J@:6YX#2J@:FUPH#IL;V]P#2HZ9&]N
  3147. M9:!R=',-*CIC=&5X=*!H97B@,&2@.V-R#2J@='ATH"=33TU%5$A)3D>@0TA/
  3148. M2T5$H#HH)PTJH&AE>* P9# P#2H-('1X=" G;D%21B$G#0TJ+2TM+2TM+2TM
  3149. M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+0TJH&1205=)3B>@0:!,24Y%+J"@8:!&
  3150. M04A.H$Q!2$XN#0TJ*BJ@<T]-1:!54T5&54R@34%#4D]3#0UC:6YI="!M86,@
  3151. M(#MM04-23Z!43Z!)3DE424%,25I%H%1(1:!#3U5.5$52#2!L9&$@73$@.T18
  3152. MH$]2H$19#2!L<W(-(#P\/" @.W1(1:!$6"\RH$U!2T53H$&@3DE#15*@3$]/
  3153. M2TE.1Z!,24Y%#0TJ*BHJ*J!M04-23Z!43Z!404M%H$&@4U1%4*!)3J!X#0UX
  3154. M<W1E<"!M86,-(&QD>"!D>" [;E5-0D52H$]&H$Q/3U"@251%4D%424].4PT@
  3155. M/CX^(&-I;FET+&1X#7AL;V]P(&QS<B!C:'5N:PT@8F5Q(&9I>&,@.W501$%4
  3156. M1:!#3TQ534X-('-B8R!D>0T@8F-C(&9I>'D@.W1)346@5$^@4U1%4*!)3J!Y
  3157. M#2!D97@-(&)N92!X;&]O< UD;VYE(&QD82!O;&1X(#MP3$]4H%1(1:!,05-4
  3158. MH$-(54Y+#2!E;W(@8VAU;FL-(&]R82 H8G5F9F5R*2QY#2!S=&$@*&)U9F9E
  3159. M<BDL>0T@<G1S#0UF:7AC('!H80T@;&1A(&]L9'@-(&]R82 H8G5F9F5R*2QY
  3160. M(#MP3$]4#2!S=&$@*&)U9F9E<BDL>0T@;&1A(",D9F8@.W501$%41:!#2%5.
  3161. M2PT@<W1A(&]L9'@-('-T82!C:'5N:PT@;&1A(",D.# @.VE.0U)%05-%H%1(
  3162. M1:!#3TQ534X-(&5O<B!B=69F97(-('-T82!B=69F97(-(&)N92!C,@T@:6YC
  3163. M(&)U9F9E<BLQ#6,R#2!P;&$-('-B8R!D>0T@8F-S(&-O;G0-(&%D8R!D> T@
  3164. M:68@:2Q=,2 [9$^@5T6@55-%H&EN>:!/4J!D97D_#2!I;GD-(&5L<V4-(&1E
  3165. M>0T@9FEN#6-O;G0@9&5X#2!B;F4@>&QO;W -(&IM<"!D;VYE#0UF:7AY(&%D
  3166. M8R!D> T@<&AA#2!L9&$@;VQD> T@96]R(&-H=6YK#2!O<F$@*&)U9F9E<BDL
  3167. M>0T@<W1A("AB=69F97(I+'D-(&QD82!C:'5N:PT@<W1A(&]L9'@-('!L80T@
  3168. M:68@:2Q=,2 [=5!$051%H'D-(&EN>0T@96QS90T@9&5Y#2!F:6X-(&1E> T@
  3169. M8FYE('AL;V]P#2!R=',-(#P\/" @.V5.1*!/1J!M04-23Z!84U1%4 T-*BHJ
  3170. M*BJ@=$%+1:!!H%-415"@24Z@>0T->7-T97 @;6%C#2!L9'@@9'D@.VY534)%
  3171. M4J!/1J!,3T]0H$E415)!5$E/3E,-(&)E<2!D;VYE(#MI1J!$63TPH$E4)U.@
  3172. M2E535*!!H%!/24Y4#2 ^/CX@8VEN:70L9'D-('-E8PUY;&]O<"!P:&$-(&QD
  3173. M82!O;&1X#2!O<F$@*&)U9F9E<BDL>0T@<W1A("AB=69F97(I+'D-('!L80T@
  3174. M:68@:2Q=,0T@:6YY#2!E;'-E#2!D97D-(&9I;@T@<V)C(&1X#2!B8V,@9FEX
  3175. M> T@9&5X#2!B;F4@>6QO;W -9&]N92!L9&$@;VQD> T@;W)A("AB=69F97(I
  3176. M+'D-('-T82 H8G5F9F5R*2QY#2!R=',-#69I>'@@861C(&1Y#2!L<W(@;VQD
  3177. M> T@<V5C(" [:4U03U)404Y4(0T@8F5Q(&9I>&,-(&1E> T@8FYE('EL;V]P
  3178. M#2!J;7 @9&]N90T-9FEX8R!P:&$-(&QD82 C)#@P#2!S=&$@;VQD> T@96]R
  3179. M(&)U9F9E<@T@<W1A(&)U9F9E<@T@8FYE(&,R#2!I;F,@8G5F9F5R*S$-8S(@
  3180. M<&QA#2!D97@-(&)N92!Y;&]O< T@:FUP(&1O;F4-(#P\/" @.V5.1*!/1J!M
  3181. M04-23Z!94U1%4 T-*J!T04M%H$%.H%B@4U1%4*!)3J!42$6@96]RH$)51D9%
  3182. M4@TJH'1(1:!33TQ%H$-(04Y'1:!)4Z!43Z!54T6@96]RH$E.4U1%042@3T:@
  3183. M;W)A#0UE;W)X<W1E<"!M86,-(&QD>"!D>" [;E5-0D52H$]&H$Q/3U"@251%
  3184. M4D%424].4PT@/CX^(&-I;FET+&1X#7AL;V]P(&QS<B!C:'5N:PT@8F5Q(&9I
  3185. M>&,@.W501$%41:!#3TQ534X-('-B8R!D>0T@8F-C(&9I>'D@.W1)346@5$^@
  3186. M4U1%4*!)3J!Y#2!D97@-(&)N92!X;&]O< UD;VYE(&QD82!O;&1X(#MP3$]4
  3187. MH%1(1:!,05-4H$-(54Y+#2!E;W(@8VAU;FL-(&5O<B H8G5F9F5R*2QY#2!S
  3188. M=&$@*&)U9F9E<BDL>0T@<G1S#0UF:7AC('!H80T@;&1A(&]L9'@-(&5O<B H
  3189. M8G5F9F5R*2QY(#MP3$]4#2!S=&$@*&)U9F9E<BDL>0T@;&1A(",D9F8@.W50
  3190. M1$%41:!#2%5.2PT@<W1A(&]L9'@-('-T82!C:'5N:PT@;&1A(",D.# @.VE.
  3191. M0U)%05-%H%1(1:!#3TQ534X-(&5O<B!B=69F97(-('-T82!B=69F97(-(&)N
  3192. M92!C,@T@:6YC(&)U9F9E<BLQ#6,R#2!P;&$-('-B8R!D>0T@8F-S(&-O;G0-
  3193. M(&%D8R!D> T@:68@:2Q=,2 [9$^@5T6@55-%H&EN>:!/4J!D97D_#2!I;GD-
  3194. M(&5L<V4-(&1E>0T@9FEN#6-O;G0@9&5X#2!B;F4@>&QO;W -(&IM<"!D;VYE
  3195. M#0UF:7AY(&%D8R!D> T@<&AA#2!L9&$@;VQD> T@96]R(&-H=6YK#2!E;W(@
  3196. M*&)U9F9E<BDL>0T@<W1A("AB=69F97(I+'D-(&QD82!C:'5N:PT@<W1A(&]L
  3197. M9'@-('!L80T@:68@:2Q=,2 [=5!$051%H'D-(&EN>0T@96QS90T@9&5Y#2!F
  3198. M:6X-(&1E> T@8FYE('AL;V]P#2!R=',-(#P\/" @.V5.1*!/1J!M04-23Z!8
  3199. M4U1%4 T-#2J@=$%+1:!!H%DM4U1%4*!)3J!42$6@96]R+4)51D9%4@TJH&-(
  3200. M04Y'15.@1E)/3:!!0D]61:!!4D4ZH$].3%F@4$Q/5*!,05-4H%!!4E2@3T:@
  3201. M14%#2 TJH%9%4E1)0T%,H$-(54Y++*!$3TXG5*!03$]4H$Q!4U2@4$])3E0L
  3202. MH%!,3U2@5TE42*!E;W(-#65O<GES=&5P(&UA8PT@;&1X(&1Y(#MN54U"15*@
  3203. M3T:@3$]/4*!)5$52051)3TY3#2!B97$@9&]N92 [:4:@1%D],*!)5"=3H$I5
  3204. M4U2@0:!03TE.5 T@/CX^(&-I;FET+&1Y#2!S96,-*GEL;V]PH'!H80TJH&QD
  3205. M8:!O;&1X#2J@;W)AH"AB=69F97(I+'D-*J!S=&&@*&)U9F9E<BDL>0TJH'!L
  3206. M80UY;&]O<"!I9B!I+%TQ#2!I;GD-(&5L<V4-(&1E>0T@9FEN#2!S8F,@9'@-
  3207. M(&)C8R!F:7AX#2!D97@-(&)N92!Y;&]O< TJ9&]N9:!L9&&@;VQD> TJH&]R
  3208. M8: H8G5F9F5R*2QY#2J@<W1AH"AB=69F97(I+'D-9&]N92!R=',-#69I>'@@
  3209. M861C(&1Y#2!P:&$@(#MW1:!/3DQ9H%!,3U2@5$A%H$Q!4U2@4$%25*!/1J!%
  3210. M04-(H$-(54Y+#2!L9&$@;VQD> T@96]R("AB=69F97(I+'D-('-T82 H8G5F
  3211. M9F5R*2QY#2!P;&$-(&QS<B!O;&1X#2!S96,@(#MI35!/4E1!3E0A#2!B97$@
  3212. M9FEX8PT@9&5X#2!B;F4@>6QO;W -(&IM<"!D;VYE#0UF:7AC('!H80T@;&1A
  3213. M(",D.# -('-T82!O;&1X#2!E;W(@8G5F9F5R#2!S=&$@8G5F9F5R#2!B;F4@
  3214. M8S(-(&EN8R!B=69F97(K,0UC,B!P;&$-(&1E> T@8FYE('EL;V]P#2!J;7 @
  3215. M9&]N90T@/#P\(" [94Y$H$]&H&U!0U)/H%E35$50#2HJ*BJ@:4Y)5$E!3*!,
  3216. M24Y%H%-%5%50#0TJ*J!T2$6@0T]-345.5$5$H$Q)3D53H$)%3$]7H$%21:!.
  3217. M3U>@5$%+14Z@0T%21:!/1J!"6:!42$4-*BJ@0T%,3$E.1Z!23U5424Y%+@TJ
  3218. M9')A=Z ^/CZ@;6]V92QT>#$[>#&@H#MM3U9%H%-4549&H$E.5$^@6D523Z!0
  3219. M04=%#2J@/CX^H&UO=F4L='@R.W@RH* [=TA%4D6@252@0T%.H$)%H$U/1$E&
  3220. M245$#2J@/CX^H&UO=F4L='DQ.WDQ#2J@/CX^H&UO=F4L='DR.WDR#0UD<F%W
  3221. M(&QD82!F:6QL#2!B;F4@.G-E=&5O<@T@/CX^('-E=&)U9@T@:FUP(#IS971U
  3222. M< TZ<V5T96]R(&QD82 C/&5O<F)U9B [=5-%H&5O<J!"549&15*@24Y35$5!
  3223. M1*!/1@T@<W1A(&)U9F9E<B [1$E34$Q!6:!"549&15*@1D]2H$1205=)3D<-
  3224. M(&QD82 C/F5O<F)U9@T@<W1A(&)U9F9E<BLQ#0TZ<V5T=7 @<V5C(" [;4%+
  3225. M1:!355)%H%@Q/%@R#2!L9&$@>#(-('-B8R!X,0T@8F-S(#IC;VYT#2!L9&$@
  3226. M>3(@.VE&H$Y/5"R@4U=!4*!P,:!!3D2@<#(-(&QD>2!Y,0T@<W1A('DQ#2!S
  3227. M='D@>3(-(&QD82!X,0T@;&1Y('@R#2!S='D@>#$-('-T82!X,@T-('-E8PT@
  3228. M<V)C('@Q(#MN3U>@83U$6 TZ8V]N="!S=&$@9'@-(&QD>"!X,2 [<%54H%@Q
  3229. MH$E.5$^@>"R@3D]7H%=%H$-!3J!44D%32*!X,0T-8V]L=6UN('1X82 [9DE.
  3230. M1*!42$6@1DE24U2@0T],54U.H$9/4J!X#2!L<W(-(&QS<B @.W1(15)%H$%2
  3231. M1:!8,2\XH#$R.*!"651%H$),3T-+4PT@;'-R(" [=TA)0TB@345!3E.@6#$O
  3232. M,3:@,C4VH$)95$6@0DQ/0TM3#2!L<W(-(&)C8R Z979E;B [=TE42*!!H%!/
  3233. M4U-)0DQ%H$585%)!H#$R.*!"651%H$),3T-+#2!L9'D@(R0X," [24:@4T\L
  3234. MH%-%5*!42$6@2$E'2*!"250-('-T>2!B=69F97(-(&-L8PTZ979E;B!A9&,@
  3235. M8G5F9F5R*S$@.V%$1*!)3J!42$6@3E5-0D52H$]&H#(U-J!"651%H$),3T-+
  3236. M4PT@<W1A(&)U9F9E<BLQ#0T@<V5C#2!L9&$@>3(@.V-!3$-53$%41:!$60T@
  3237. M<V)C('DQ#2!B8W,@.F-O;G0R(#MI4Z!9,CY9,3\-(&5O<B C)&9F(#MO5$A%
  3238. M4E=)4T6@1%D]63$M63(-(&%D8R C)# Q#3IC;VYT,B!S=&$@9'D-(&-M<"!D
  3239. M>" [=TA/)U.@0DE'1T52.J!$6:!/4J!$6#\-(&)C8R!S=&5P:6YX(#MI1J!$
  3240. M6"R@5$A%3BXN+@T@:FUP('-T97!I;GD-#7-T97!I;G@@;&1Y('DQ#2!C<'D@
  3241. M>3(-(&QD82!B:71P+'@@.WB@0U524D5.5$Q9H$-/3E1!24Y3H%@Q#2!S=&$@
  3242. M;VQD> T@<W1A(&-H=6YK#2!B8V,@>&EN8WD@.V1/H%=%H%-415"@1D]25T%2
  3243. M1%.@3U*@0D%#2U=!4D13H$E.H'D_#2!J;7 @>&1E8WD-#7AI;F-Y(&QD82!F
  3244. M:6QL#2!B97$@;F]R;7AI;F,-(#X^/B!E;W)X<W1E<"QI;GD-;F]R;7AI;F,@
  3245. M/CX^('AS=&5P+&EN>0T->&1E8WD@;&1A(&9I;&P-(&)E<2!N;W)M>&1E8PT@
  3246. M/CX^(&5O<GAS=&5P+&1E>0UN;W)M>&1E8R ^/CX@>'-T97 L9&5Y#0US=&5P
  3247. M:6YY(&QD>2!Y,0T@;&1A(&)I=' L>" [>#U8,0T@<W1A(&]L9'@-(&QS<B @
  3248. M.WF@1$]%4TXG5*!54T6@0TA53DM3#2!E;W(@;VQD>" [<T^@5T6@2E535*!7
  3249. M04Y4H%1(1:!"250-('-T82!O;&1X#2!C<'D@>3(-(&)C<R!Y9&5C>0T->6EN
  3250. M8WD@;&1A(&9I;&P-(&)E<2!N;W)M:6YC#2 ^/CX@96]R>7-T97 L:6YY#6YO
  3251. M<FUI;F,@/CX^('ES=&5P+&EN>0T->61E8WD@;&1A(&9I;&P-(&)E<2!N;W)M
  3252. M9&5C#2 ^/CX@96]R>7-T97 L9&5Y#6YO<FUD96,@/CX^('ES=&5P+&1E>0T-
  3253. M#2HM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM#2J@8TQ%04Z@55 -
  3254. M#6-L96%N=7 @;&1A('9M8W-B(#MS5TE40TB@0TA!4J!23TV@0D%#2Z!)3@T@
  3255. M86YD(",E,3$Q,3 Q,#$@.T1%1D%53%0-('-T82!V;6-S8@T-(')T<R @.T)9
  3256. M12$-#2!T>'0@)U-024Y!3*!#4D%#2T52H"<-('1X=" G4TQ*H#8O.34G#0TJ
  3257. M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+0TJH'-%5*!54*!"252@
  3258. M5$%"3$4-#2!D<R!>(#MC3$5!4J!43Z!%3D2@3T:@4$%'10T@(" [<T^@5$A!
  3259. M5*!404),15.@4U1!4E2@3TZ@0:!004=%H$)/54Y$05)9#6)I=' @;'5P(#$V
  3260. M(#LQ,CB@94Y44DE%4Z!&3U*@> T@9&9B("4Q,3$Q,3$Q,0T@9&9B("4P,3$Q
  3261. M,3$Q,0T@9&9B("4P,#$Q,3$Q,0T@9&9B("4P,# Q,3$Q,0T@9&9B("4P,# P
  3262. M,3$Q,0T@9&9B("4P,# P,#$Q,0T@9&9B("4P,# P,# Q,0T@9&9B("4P,# P
  3263. M,# P,0T@+2U>#0US:6X@.W1!0DQ%H$]&H%-)3D53+* Q,C"@0EE415,-8V]S
  3264. M(&5Q=2!S:6XK,3(X(#MT04),1:!/1J!#3U-)3D53#2 @(#MB3U1(H$]&H%1(
  3265. M15-%H%1224>@5$%"3$53H$%210T@(" [0U524D5.5$Q9H%-%5*!54*!&4D]-
  3266. MH&)A<VEC#7ID:78@97%U(&-O<RLQ,C@@.V1)5DE324].H%1!0DQ%#71M871H
  3267. M,2!E<74@>F1I=BLS.#0@.VU!5$B@5$%"3$6@3T:@1BA8*3U8*E@O,C4V#71M
  3268. M871H,B!E<74@=&UA=&@Q*S4Q,B [<T5#3TY$H$U!5$B@5$%"3$4-<&]L>6QI
  3269. M<W0@97%U('1M871H,BLU,3(@.VQ)4U2@3T:@4$],64=/3E,-&AH:&AH:&AH:
  3270. 8&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:
  3271.  
  3272. end
  3273.  
  3274. begin 666 cube3d3.2.o
  3275. M ("I (T@T(TAT*T8T"D/"1"-&-"@ *D?A?NI@(7\3+R!DP41$1$@(" @(" @
  3276. M(" @(" @0U5"13-$(%8S+C(-#2 @(" @(" @(" @(" @(" @($)9#9\@(" @
  3277. M4U1%4$A%3B!*541$F2 @("!'14]21T4@5$%93$]2#0V;("!#2$5#2R!/550@
  3278. M5$A%($I!3BX@.34@25-3544@3T8-EB @0SU(04-+24Y'FR!&3U(@34]212!$
  3279. M151!24Q3(0T-'1V>$D8Q+T8RDB M($E.0R]$14,@6"U23U1!5$E/3@T='1)&
  3280. M,R]&-)(@+2!)3D,O1$5#(%DM4D]4051)3TX-'1T21C4O1C:2("T@24Y#+T1%
  3281. M0R!:+5)/5$%424].#1T=$B!&-R @DB M(%)%4T54#1T=$B K+RT@DB M(%I/
  3282. M3TT@24XO3U54#1T=$B @2" @DB M(%1/1T=,12!(241$14X@4U521D%#15,-
  3283. M'1T24U!!0T62("T@5$]'1TQ%(%-54D9!0T4@1DE,3$E.1PT-("!04D534R!1
  3284. M(%1/(%%5250-#04@(" @("!04D534R!!3ED@2T59(%1/($)%1TE.#0"Q^_ +
  3285. M(-+_R-#VYOQ,O($@Y/_) /#YJ9*%(X4EJ92%)X4IJ0&-(="IDR#2_ZD C2'0
  3286. MJ4!I#(7[J06%_*D H "B !B1^\AI$)#Y&*7[:2B%^Z7\:0"%_*  Z(K@$-#D
  3287. MJ0"%HZDPA:2@ *(8J0"1H\C0^^:DRM#VJ0"%HZDPA:2% JT8T"GQ"0Z-&-"I
  3288. M (57A5B%685@A3^%085 A4*%885BA6.%9(5EA6:%4*D!A;6I0(5Q6"#D_\F%
  3289. MT NE8<D\\%'F84P-@\F)T FE8?!$QF%,#8/)AM +I6+)// UYF),#8/)BM )
  3290. MI6+P*,9B3 V#R8?0"Z5CR3SP&>9C3 V#R8O0":5C\ S&8TP-@\F(T 9,1H),
  3291. M#8/)*] 'YG'F<4P-@\DMT W&<<9Q$"CF<>9Q3 V#R4C0":6U20&%M4P-@\D@
  3292. MT FE4$D!A5!,#8/)4= #3)F.>!BE9&5AR7B0 NEXA608I65E8LEXD +I>(5E
  3293. M&*5F96/)>) "Z7B%9CBE9>5FL )I>(5G&*5E96;)>) "Z7B%:!BE9&5FR7B0
  3294. M NEXA6DXI63E9K ":7B%:ABE9&5HR7B0 NEXA6LXI63E9[ ":7B%;!BE9&5G
  3295. MR7B0 NEXA6TXI6CE9+ ":7B%;CBE9>5DL )I>(5O&*5D967)>) "Z7B%<!BF
  3296. M9[T D*9H?0"0A:6F9[V CSBF:/V CX6FIF6]@(\0#AA)_VD!"AA)_VD!3-V#
  3297. M"H6G.*9NO0"0IFW] ) XIFO] ) 8IFQ] ) 0#AA)_VD!2AA)_VD!3 >$2ABF
  3298. M:7V CSBF:OV CX6H.*9KO8"/IFS]@(\XIFW]@(\XIF[]@(\0#AA)_VD!2AA)
  3299. M_VD!3#V$2ABF:7T D!BF:GT D(6IIF^]@(\XIG#]@(^%JJ9LO8"/.*9N_8"/
  3300. M.*9M_8"/.*9K_8"/$ X82?]I 4H82?]I 4R A$H8IFI] ) XIFG] )"%JQBF
  3301. M;+T D*9M?0"0.*9K_0"0.*9N_0"0$ X82?]I 4H82?]I 4RVA$H8IFE]@(\8
  3302. MIFI]@(^%K!BF;[T D*9P?0"0A:VI (6CI0*%I*((J0"@ )&CR-#[YJ3*T/2@
  3303. M (11I%&Y );0 TR*A852YE$@Q86E5TI*2H57Q3^P H4_I5G%0; "A4&E6$I*
  3304. M2H58Q4"0 H5 I6#%0I "A4*E4/#!J0"%HZ4"A:2I (7[J4"%_*572I 'H("$
  3305. MHX3[&(5H9:2%I*5H9?R%_*58..57JNBD8- "YF"D8*D 4?M(4:.1HZD D?MH
  3306. MB,19L.^EHTF A:.%^] $YJ3F_,K0VDSMA*T8T$D"C1C0J0A% H4"3&Z"9T5%
  3307. M(&)204E.+"!72$%4($1/(%E/52!704Y4(%1/($1/(%1/3DE'2%0_J0"%8(58
  3308. MJ?^%6857I%&Y ):%DLBY ):%D\BY ):%E,C&4KD EH65R+D EH66R+D EH6N
  3309. MR,92N0"6A:_(N0"6A;#(N0"6A;'(A%&DDJ6EA2882?]I 84HL28X\2B%LJ6H
  3310. MA2882?]I 84HL28X\2B%LZ6KA2882?]I 84HL28X\2B%M*23I::%)AA)_VD!
  3311. MA2BQ)CCQ*!AELH6RI:F%)AA)_VD!A2BQ)CCQ*!AELX6SI:R%)AA)_VD!A2BQ
  3312. M)CCQ*!AEM(6TI)2EIX4F&$G_:0&%*+$F./$H&&6RA;*EJH4F&$G_:0&%*+$F
  3313. M./$H&&6SA;.EK84F&$G_:0&%*+$F./$H&&6TA92JO("0I;*%(AA)_VD!A22Q
  3314. M(CCQ)*9QX$#P%(3[I'&%)AA)_VD!A2BQ)CCQ**3[&&E A9+%5[ "A5?%6) "
  3315. MA5BELX4B&$G_:0&%)+$B./$DX$#P$*1QA2882?]I 84HL28X\2@8:4"%D\59
  3316. ML *%6<5@D *%8*25I:6%)AA)_VD!A2BQ)CCQ*(6RI:B%)AA)_VD!A2BQ)CCQ
  3317. M*(6SI:N%)AA)_VD!A2BQ)CCQ*(6TI):EIH4F&$G_:0&%*+$F./$H&&6RA;*E
  3318. MJ84F&$G_:0&%*+$F./$H&&6SA;.EK(4F&$G_:0&%*+$F./$H&&6TA;2DKJ6G
  3319. MA2882?]I 84HL28X\2@89;*%LJ6JA2882?]I 84HL28X\2@89;.%LZ6MA288
  3320. M2?]I 84HL28X\2@89;2%KJJ\@)"ELH4B&$G_:0&%)+$B./$DIG'@0/ 4A/ND
  3321. M<84F&$G_:0&%*+$F./$HI/L8:4"%E<57L *%5\58D *%6*6SA2(82?]I 84D
  3322. ML2(X\23@0/ 0I'&%)AA)_VD!A2BQ)CCQ*!AI0(66Q5FP H59Q6"0 H5@I*^E
  3323. MI84F&$G_:0&%*+$F./$HA;*EJ(4F&$G_:0&%*+$F./$HA;.EJX4F&$G_:0&%
  3324. M*+$F./$HA;2DL*6FA2882?]I 84HL28X\2@89;*%LJ6IA2882?]I 84HL28X
  3325. M\2@89;.%LZ6LA2882?]I 84HL28X\2@89;2%M*2QI:>%)AA)_VD!A2BQ)CCQ
  3326. M*!AELH6RI:J%)AA)_VD!A2BQ)CCQ*!AELX6SI:V%)AA)_VD!A2BQ)CCQ*!AE
  3327. MM(6QJKR D*6RA2(82?]I 84DL2(X\22F<>! \!2$^Z1QA2882?]I 84HL28X
  3328. M\2BD^QAI0(6OQ5>P H57Q5B0 H58I;.%(AA)_VD!A22Q(CCQ).! \!"D<84F
  3329. M&$G_:0&%*+$F./$H&&E A;#%6; "A5G%8) "A6"EM?!'I94XY9*HI; XY9:%
  3330. M)AA)_VD!A2BQ)CCQ*(7[I:\XY96HI98XY9.%)AA)_VD!A2BQ)CCQ*,7[, _&
  3331. M4O *YE'F4>91QE+0]F"EDH7[I9.%_*65A?VEEH7^(-"+I96%^Z66A?REKX7]
  3332. MI;"%_B#0B\92T -,=XND4;D EH65R+D EH66R+D EH6NR(11I)6EI84F&$G_
  3333. M:0&%*+$F./$HA;*EJ(4F&$G_:0&%*+$F./$HA;.EJX4F&$G_:0&%*+$F./$H
  3334. MA;2DEJ6FA2882?]I 84HL28X\2@89;*%LJ6IA2882?]I 84HL28X\2@89;.%
  3335. MLZ6LA2882?]I 84HL28X\2@89;2%M*2NI:>%)AA)_VD!A2BQ)CCQ*!AELH6R
  3336. MI:J%)AA)_VD!A2BQ)CCQ*!AELX6SI:V%)AA)_VD!A2BQ)CCQ*!AEM(6NJKR 
  3337. MD*6RA2(82?]I 84DL2(X\22F<>! \!2$^Z1QA2882?]I 84HL28X\2BD^QAI
  3338. M0(65Q5>P H57Q5B0 H58I;.%(AA)_VD!A22Q(CCQ).! \!"D<84F&$G_:0&%
  3339. M*+$F./$H&&E A9;%6; "A5G%8) "A6"EE87[I9:%_*6OA?VEL(7^(-"+I96%
  3340. MKZ66A;#&4O #3!&*I9*%_:63A?ZEKX7[I;"%_"#0BV!S04U%(%1(24Y'(%=%
  3341. M($1/($5615)9($Y)1TA4+"!P24Y+63H@5%)9(%1/(%1!2T4@3U9%4B!42$4@
  3342. M5T]23$0A;D%21B&E4- +J0"%HZ4"A:1,YXNI (6CJ4"%I#BE_>7[L!.E_J3\
  3343. MA?R$_J7[I/V$^X7]..7[A6>F^XI*2DI*D 6@@(2C&&6DA:0XI?[E_+ $2?]I
  3344. M 85HQ6>0 TR-C:3\Q/Z] (^%_87^D -,XXRE4/!3IF>E9TI&_O 0Y6B0,<K0
  3345. M]:7]1?Y1HY&C8$BE_5&CD:.I_X7]A?ZI@$6CA:/0 N:D:.5HL -E9\C*T,I,
  3346. M38QE9TBE_47^4:.1HZ7^A?UHR,K0LV"F9Z5G2D;^\!#E:) QRM#UI?U%_A&C
  3347. MD:-@2*7]$:.1HZG_A?V%_JF 1:.%H] "YJ1HY6BP V5GR,K0RDR@C&5G2*7]
  3348. M1?X1HY&CI?Z%_6C(RM"S8*50\%.F9Z5G2D;^\!#E:) QRM#UI?U%_E&CD:-@
  3349. M2*7]4:.1HZG_A?V%_JF 1:.%H] "YJ1HY6BP V5GB,K0RDSWC&5G2*7]1?Y1
  3350. MHY&CI?Z%_6B(RM"S8*9GI6=*1O[P$.5HD#'*T/6E_47^$:.1HV!(I?T1HY&C
  3351. MJ?^%_87^J8!%HX6CT +FI&CE:+ #96>(RM#*3$J-96=(I?U%_A&CD:.E_H7]
  3352. M:(C*T+-@I/R] (^%_4I%_87]Q/ZP?J50\#JF:/ ,I6A*.,CE9Y $RM#X8&5H
  3353. M2*7]4:.1HVA&_3CP!LK0Y4RQC4BI@(7]1:.%H] "YJ1HRM#13+&-IFCP%*5H
  3354. M2CA(I?T1HY&C:,CE9Y *RM#PI?T1HY&C8&5H1OTX\ ;*T-],\XU(J8"%_46C
  3355. MA:/0 N:D:,K0RTSSC:50\#JF:/ ,I6A*.(CE9Y $RM#X8&5H2*7]4:.1HVA&
  3356. M_3CP!LK0Y4POCDBI@(7]1:.%H] "YJ1HRM#13"^.IFCP%*5H2CA(I?T1HY&C
  3357. M:(CE9Y *RM#PI?T1HY&C8&5H1OTX\ ;*T-],<8Y(J8"%_46CA:/0 N:D:,K0
  3358. MRTQQCJT8T"GUC1C08%-024Y!3"!#4D%#2T52(%-,2B V+SDU            
  3359. M                                                            
  3360. M                      #_?S\?#P<# ?]_/Q\/!P,!_W\_'P\' P'_?S\?
  3361. M#P<# ?]_/Q\/!P,!_W\_'P\' P'_?S\?#P<# ?]_/Q\/!P,!_W\_'P\' P'_
  3362. M?S\?#P<# ?]_/Q\/!P,!_W\_'P\' P'_?S\?#P<# ?]_/Q\/!P,!_W\_'P\'
  3363. M P'_?S\?#P<# 1H:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:
  3364. M&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:
  3365. M&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:
  3366. !&AH:
  3367.  
  3368. end
  3369.  
  3370. begin 666 shape3.2
  3371. M 1PA'   CR!#54)%,T0@4TA!4$4@24Y)5"!04D]'4D%- $<<!0"7-3$L,C4U
  3372. M.I<U,BPQ,C<ZES4U+#(U-3J7-38L,3(W.IP ;AQB (\@34%)3B!04D]'4D%-
  3373. M(%-405)44R!!5"!,24Y%(#4P,#  =!QC #H B!QD (\@4%)/1U)!32!.3U1%
  3374. M4P":'&X CR!33$H@-B\Q-B\Y-0"P''@ CR!$051!($9/4DU!5"!)4SH UAQ]
  3375. M (\@3E5-4$])3E13+%@Q+%DQ+%HQ+%@R+%DR+%HR+"XN+@#W'(( CR!42$4@
  3376. M4$],64=/3B!,25-4("I-55-4*B!"10 5'8< CR!415)-24Y!5$5$(%=)5$@@
  3377. M02!:15)/(0 ;'8D .@ ^'8P CR!/1B!#3U524T4L(%1(15)%($E3($$@3$]4
  3378. M($]& %X=D0"/($154$Q)0T%424].($1205=)3D<@3$E.15, @QV3 (\@5TA%
  3379. M3B!&04-%4R!!4D5.)U0@0D5)3D<@1DE,3$5$ *8=E@"/($$@1D%35"!04D]'
  3380. M4D%-(%=/54Q$3B=4($1205< RAV@ (\@5$A%(%-!344@3$E.12!45TE#12P@
  3381. M0E54(%1(25, Z!VE (\@25,@1T]/1"!%3D]51T@@1D]2($Y/5R$ "AZG (\@
  3382. M3T8@0T]54E-%+"!&3U(@1DE,3$5$($9!0T53 "L>J "/(%1(25,@25,@3D\@
  3383. M3$].1T52($%.($E34U5% #$>J@ Z %8>M "/($9%14P@1E)%12!43R!0550@
  3384. M64]54B!/5TX@1$%400!\'KD CR!)3BP@0E54($-(04Y'12!42$4@4$]+12!)
  3385. M3B!,24Y% )@>O@"/(#4P,#4@5$\@55-%($,Q($%.1"!#,@">'L@ .@##'M( 
  3386. MCR!33U)262!!0D]55"!33TU%($]&(%1(12!,25143$4 WA[7 (\@0E5'4RP@
  3387. M15(N+BX@1D5!5%5215, _A[< (\@5$A)4R!705,@02!214%,(%)54T@@2D]"
  3388. M+@ $'^8 .@ G'_  CR!!3D0@248@64]5($9%14P@4T\@24Y#3$E.140L $0?
  3389. M^@"/(%=2251%(%1/(%-*541$0$Y752Y%1%4 2A\$ 3H :1\. 8\@04Y$($%"
  3390. M3U9%($%,3"P@2$%612!&54XA &\?& $Z ($?Y@./($9)4E-4(%-(05!% )<?
  3391. MYP-!,;+"*#8Q*3I!,K+"*#8R*0#''^@#@R T+"TR-BPM,C8L-C0L,C8L+3(V
  3392. M+#8T+#(V+#8T+#8T+"TR-BPV-"PV- #S'_(#@R T+"TR-BPM,C8L,"PR-BPM
  3393. M,C8L,"PR-BPV-"PP+"TR-BPV-"PP !<@_ .#(#,L,"PM,C8L,S(L,38L,S(L
  3394. M,S(L+3$V+#,R+#,R !\@!@2#(#  ,B!*!(\@4T5#3TY$(%-(05!% $@@2P1"
  3395. M,;+"*#8Q*3I",K+"*#8R*0!\($P$@R T+"TQ-2PS,"PM,3(L+3$U+"TS,"PM
  3396. M,3(L+34W+"TS,"PM,RPM-3$L+38L+3, K"!6!(,@-"PQ-2PS,"PM,3(L-3$L
  3397. M+38L+3,L-3<L+3,P+"TS+#$U+"TS,"PM,3( X"!@!(,@-"PQ-2PS,"PM,3(L
  3398. M,34L+3,P+"TQ,BPM,34L+3,P+"TQ,BPM,34L,S L+3$R "0A:@2#(#<L,34L
  3399. M+3,P+"TQ,BPU-RPM,S L+3,L,S,L+3,P+#8L,"PM,S L,3(L+3,S+"TS,"PV
  3400. M+"TU-RPM,S L+3, -B%L!(,@+3$U+"TS,"PM,3( 6R%T!(,@,RPS,RPM,S L
  3401. M,"PS,RPM,S L+30L,SDL+3,P+"TR (,A?@2#(#,L+3,S+"TS,"PP+"TS.2PM
  3402. M,S L+3(L+3,S+"TS,"PM- "O(8@$@R T+#8L+3,P+#,L-BPM,S L+38L,3@L
  3403. M+3,P+"TT+#$X+"TS,"PQ -\AD@2#(#0L+38L+3,P+#,L+3$X+"TS,"PQ+"TQ
  3404. M."PM,S L+30L+38L+3,P+"TV  <BG 2#(#,L+34W+"TS,"PM,RPM,S,L+3,P
  3405. M+#8L+34Q+"TQ,BPM,P O(J8$@R S+"TS,RPM,S L-BPM,34L,S L+3$R+"TU
  3406. M,2PM,3(L+3, 4R*P!(,@,RPM,34L,S L+3$R+"TS,RPM,S L-BPP+#8L,3( 
  3407. M=2*Z!(,@,RPP+#8L,3(L+3,S+"TS,"PV+# L+3,P+#$R )DBQ 2#(#,L,34L
  3408. M,S L+3$R+"TQ-2PS,"PM,3(L,"PV+#$R +HBS@2#(#,L,"PM,S L,3(L,S,L
  3409. M+3,P+#8L,"PV+#$R -PBV 2#(#,L,"PV+#$R+#,S+"TS,"PV+#$U+#,P+"TQ
  3410. M,@ !(^($@R S+#$U+#,P+"TQ,BPS,RPM,S L-BPU,2PM,3(L+3, )B/L!(,@
  3411. M,RPS,RPM,S L-BPU-RPM,S L+3,L-3$L+3$R+"TS "XC]@2#(#  0",2!8\@
  3412. M5$A)4D0@4TA!4$4 5B,3!4,QLL(H-C$I.D,RLL(H-C(I ' C% 6/(%E/55(@
  3413. M1$%402!'3T53($A%4D4 >"/0!X,@, ",(X@30E"R,C4VK"@YK#$VJC8I + C
  3414. MC!./($-(04Y'12!"14Q/5R!43R!03TE.5"!43R!$051! ,(CC1.7-C4L0C$Z
  3415. MES8V+$(R .$CDA.'3CJ70E L3CI"4+)"4*HQ.HM.LC"G-3 U, #I(Y<3F2!.
  3416. M  8DG!.!2;(QI#.L3CJ'4#J+4+,PIU"R,C4VJE  &"2F$Y="4"Q0.D)0LD)0
  3417. MJC$ (B2K$YDB+B([ "XDL!.".HDU,#$P #\DNA.>,S(W-C@ZG#J,.H     :
  3418. M&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:
  3419. 0&AH:&AH:&AH:&AH:&AH:&AH:
  3420.  
  3421. end
  3422.  
  3423. begin 666 tables$8f80-$95ff
  3424. M@(\  @,%!P@*"PT/$!$3%!47&!D:&QP='1X>'Q\@(" @(" @'Q\>'AT='!L:
  3425. M&1@7%103$1 /#0L*" <% P( _OW[^?CV]?/Q\._M[.OIZ.?FY>3CX^+BX>'@
  3426. MX.#@X.#@X>'BXN/CY.7FY^CIZ^SM[_#Q\_7V^/G[_?X _P    #_\" @(" ?
  3427. M'QX>'1T<&QH9&!<5%!,1$ \-"PH(!P4# @#^_?OY^/;U\_'P[^WLZ^GHY^;E
  3428. MY./CXN+AX>#@X.#@X.#AX>+BX^/DY>;GZ.GK[.WO\/'S]?;X^?O]_@ " P4'
  3429. M" H+#0\0$1,4%1<8&1H;'!T='AX?'R @("  _____P  (B(B(B(C(R,C(R,C
  3430. M(R,D)"0D)"0D)"4E)24E)24E)B8F)B8F)B8G)R<G)R<G*"@H*"@H*"DI*2DI
  3431. M*2HJ*BHJ*BLK*RLK*RPL+"PL+"TM+2TM+BXN+BXN+R\O+S P,# P,3$Q,3$R
  3432. M,C(R,S,S,S0T-#0U-34U-C8V-C<W-S@X.#@8&!@8&1D9&1D9&1D9&1D9&1D9
  3433. M&1D9&AH:&AH:&AH:&AH:&AH:&AL;&QL;&QL;&QL;&QL;&QP<'!P<'!P<'!P<
  3434. M'!P<'1T='1T='1T='1T='1X>'AX>'AX>'AX>'A\?'Q\?'Q\?'Q\?(" @(" @
  3435. M(" @(" A(2$A(2$A(2$A(B(B(O__     /____\ \   _P#__P    #_____
  3436. M     /____\     _____P    #_ /__     /_P__\     _____P    #_
  3437. M____     /____\     _____P    #_____     /____\     _____P  
  3438. M  #_____     /_P                 0$! 0$! 0$" @(" @(# P,#! 0$
  3439. M! 4%!04&!@8'!P<(" @)"0D*"@L+"PP,#0T.#@\/$! 1$1(2$Q,4%!45%A<7
  3440. M&!@9&AH;'!P='AX?(" A(B,C)"4F)B<H*2DJ*RPM+BXO,#$R,S0U-38W.#DZ
  3441. M.SP]/C] 04)#1$5&1TA)2DM-3D]045)35%976"LJ*2DH)R8F)20C(R(A(" ?
  3442. M'AX='!P;&AH9&!@7%Q85%104$Q,2$A$1$! /#PX.#0T,# L+"PH*"0D)" @(
  3443. M!P<'!@8&!04%!00$! 0# P,# @(" @(" 0$! 0$! 0$                 
  3444. M              $! 0$! 0$! @(" @(" P,# P0$! 0%!04%!@8&!P<'" @(
  3445. M"0D)"@H+"PL,# T-#@X/#Q 0$1$2$A,3%!05%187%Q@8&1H:&QP<'1X>'R @
  3446. M(2(C(R0E)B8G*"DI*BLL+2XN+S Q,C,T-34V-S@Y.CL\/3X_0$%"0T1%1D=(
  3447. M24I+34Y/4%%24U165U@K*BDI*"<F)B4D(R,B(2 @'QX>'1P<&QH:&1@8%Q<6
  3448. M%144%!,3$A(1$1 0#P\.#@T-# P+"PL*"@D)"0@(" <'!P8&!@4%!04$! 0$
  3449. M P,# P(" @(" @$! 0$! 0$!                               ! 0$!
  3450. M 0$! 0(" @(" @,# P,$! 0$!04%!08&!@<'!P@(" D)"0H*"PL+# P-#0X.
  3451. M#P\0$!$1$A(3$Q04%146%Q<8&!D:&AL<'!T>'A\@("$B(R,D)28F)R@I*2HK
  3452. M+"TN+B\P,3(S-#4U-C<X.3H[/#T^/T _/CT\.SHY.#<V-34T,S(Q,"\N+BTL
  3453. M*RHI*2@G)B8E)",C(B$@(!\>'AT<'!L:&AD8&!<7%A45%!03$Q(2$1$0$ \/
  3454. M#@X-#0P,"PL+"@H)"0D(" @'!P<&!@8%!04%! 0$! ,# P," @(" @(! 0$!
  3455. M 0$! 0                               0$! 0$! 0$" @(" @(# P,#
  3456. M! 0$! 4%!04&!@8'!P<(" @)"0D*"@L+"PP,#0T.#@\/$! 1$1(2$Q,4%!45
  3457. M%A<7&!@9&AH;'!P='AX?(" A(B,C)"4F)B<H*2DJ*RPM+BXO,#$R,S0U-38W
  3458. M.#DZ.SP]/C] /SX]/#LZ.3@W-C4U-#,R,3 O+BXM+"LJ*2DH)R8F)20C(R(A
  3459. M(" ?'AX='!P;&AH9&!@7%Q85%104$Q,2$A$1$! /#PX.#0T,# L+"PH*"0D)
  3460. M" @(!P<'!@8&!04%!00$! 0# P,# @(" @(" 0$! 0$! 0$             
  3461. M !H:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:
  3462. M&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:
  3463. E&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:
  3464.  
  3465. end
  3466.  
  3467.  
  3468. ********************************
  3469. *``````````````````````````````*
  3470. *`Stephen`Judd`````````````````*
  3471. *`George`Taylor````````````````*
  3472. *`Started:`7/11/94`````````````*
  3473. *`Finished:`7/19/94````````````*
  3474. *`v2.0`Completed:`12/17/94`````*
  3475. *`v3.0`Completed:`3/20/95``````*
  3476. *`v3.1`Completed:`6/14/95``````*
  3477. *`v3.2`Completed:`6/15/95``````*
  3478. *``````````````````````````````*
  3479. *`Well,`if`all`goes`well`this``*
  3480. *`program`will`rotate`a`cube.``*
  3481. *``````````````````````````````*
  3482. *`v2.0`+`New`and`Improved!`````*
  3483. *`Now`with`faster`routines,````*
  3484. *`hidden`surfaces,`filled``````*
  3485. *`faces,`and`extra`top`secret``*
  3486. *`text`messages!```````````````*
  3487. *``````````````````````````````*
  3488. *`v3.0`+`Fast`chunky`line``````*
  3489. *`routine.`````````````````````*
  3490. *``````````````````````````````*
  3491. *`v3.1`+`General`polygon`plot``*
  3492. *`with`hidden`faces`(X-product)*
  3493. *`and`zoom`feature.````````````*
  3494. *``````````````````````````````*
  3495. *`v3.2`+`EOR-buffer`filling````*
  3496. *``````````````````````````````*
  3497. *`This`program`is`intended`to``*
  3498. *`accompany`the`article`in`````*
  3499. *`C=Hacking,`Jun.`95`issue.````*
  3500. *`For`details`on`this`program,`*
  3501. *`read`the`article!````````````*
  3502. *``````````````````````````````*
  3503. *`Write`to`us!`````````````````*
  3504. *``````````````````````````````*
  3505. *`Myself`when`young`did````````*
  3506. *`eagerly`frequent`````````````*
  3507. *`Doctor`and`Saint,`and`heard``*
  3508. *`great`Argument```````````````*
  3509. *``About`it`and`about:`but`````*
  3510. *``evermore````````````````````*
  3511. *`Came`out`by`the`same`Door````*
  3512. *`as`in`I`went.````````````````*
  3513. *````-`Rubaiyat````````````````*
  3514. *``````````````````````````````*
  3515. *`Though`I`speak`with`the``````*
  3516. *`tongues`of`men`and`of`angles`*
  3517. *`and`have`not`love,`I`am``````*
  3518. *`become`as`sounding`brass,`or`*
  3519. *`a`tinkling`cymbal.```````````*
  3520. *````-`1`Corinthians`13````````*
  3521. *``````````````````````````````*
  3522. *`P.S.`This`was`written`using``*
  3523. *``````Merlin`128.`````````````*
  3524. ********************************
  3525.  
  3526.  ORG $8000
  3527.  
  3528. *`Constants
  3529.  
  3530. BUFF1 EQU $3000 ;First`character`set
  3531. BUFF2 EQU $3800 ;Second`character`set
  3532. EORBUF EQU $4000 ;EOR-buffer
  3533. BUFFER EQU $A3 ;Presumably`the`tape`won't`be`running
  3534. X1 EQU $FB ;Points`for`drawing`a`line
  3535. Y1 EQU $FC ;These`zero`page`addresses
  3536. X2 EQU $FD ;don't`conflict`with`BASIC
  3537. Y2 EQU $FE
  3538. OLDX EQU $FD
  3539. CHUNK EQU $FE
  3540. DX EQU $67 ;This`is`shared`with`T1`below
  3541. DY EQU $68
  3542. TEMP1 EQU $FB ;Of`course,`could`conflict`with`x1
  3543. TEMP2 EQU $FC ;Temporary`variables
  3544. ZTEMP EQU $02 ;Used`for`buffer`swap.``Don't`touch.
  3545. Z1 EQU $22 ;Used`by`math`routine
  3546. Z2 EQU $24 ;Don't`touch`these`either!
  3547. Z3 EQU $26
  3548. Z4 EQU $28
  3549. K EQU $B6 ;Constant`used`for`hidden
  3550.    ;surface`detection`-`don't`touch
  3551. HIDE EQU $B5 ;Are`surfaces`hidden?
  3552. FILL EQU $50 ;Are`we`using`EOR-fill?
  3553. ANGMAX EQU 120 ;There`are`2*pi/angmax`angles
  3554.  
  3555. *`VIC
  3556.  
  3557. VMCSB EQU $D018
  3558. BKGND EQU $D020
  3559. BORDER EQU $D021
  3560. SSTART EQU 1344 ;row`9`in`screen`memory`at`1024
  3561.  
  3562.  
  3563. *`Kernal
  3564.  
  3565. CHROUT EQU $FFD2
  3566. GETIN EQU $FFE4
  3567.  
  3568. *`Some`variables
  3569.  
  3570. GLOBXMIN = $3F ;These`are`used`in`clearing`the
  3571. GLOBXMAX = $40 ;drawing`(global)`buffer
  3572. GLOBYMIN = $41
  3573. GLOBYMAX = $42
  3574. LOCXMIN = $57 ;These`are`used`in`clearing`the
  3575. LOCXMAX = $58 ;EOR`(local)`buffer
  3576. LOCYMIN = $59
  3577. LOCYMAX = $60
  3578. P1X = $92 ;These`are`temporary`storage
  3579. P1Y = $93 ;Used`in`plotting`the`projection
  3580. P1Z = $94
  3581. P2X = $95 ;They`are`here`so`that`we
  3582. P2Y = $96 ;don't`have`to`recalculate`them.
  3583. P2Z = $AE
  3584. P3X = $AF ;They`make`life`easy.
  3585. P3Y = $B0
  3586. P3Z = $B1 ;Why`are`you`looking`at`me`like`that?
  3587. P1T = $B2 ;Don't`you`trust`me?
  3588. P2T = $B3
  3589. P3T = $B4 ;Having`another`child`wasn't`my`idea.
  3590. INDEX = $51
  3591. COUNTPTS = $52
  3592. ZOOM = $71 ;Zoom`factor
  3593. DSX = $61 ;DSX`is`the`increment`for
  3594.    ;rotating`around`x
  3595. DSY = $62 ;Similar`for`DSY,`DSZ
  3596. DSZ = $63
  3597. SX = $64 ;These`are`the`actual`angles`in`x`y`and`z
  3598. SY = $65
  3599. SZ = $66
  3600. T1 = $67 ;These`are`used`in`the`rotation
  3601. T2 = $68
  3602. T3 = $69 ;See`the`article`for`more`details
  3603. T4 = $6A
  3604. T5 = $6B
  3605. T6 = $6C
  3606. T7 = $6D
  3607. T8 = $6E
  3608. T9 = $6F
  3609. T10 = $70
  3610. A11 = $A5 ;These`are`the`elements`of`the`rotation`matrix
  3611. B12 = $A6 ;XYZ
  3612. C13 = $A7
  3613. D21 = $A8 ;The`number`denotes`(row,column)
  3614. E22 = $A9
  3615. F23 = $AA
  3616. G31 = $AB
  3617. H32 = $AC
  3618. I33 = $AD
  3619.  
  3620.  
  3621. ***`Macros
  3622.  
  3623. MOVE MAC
  3624.  LDA ]1
  3625.  STA ]2
  3626.  <<<
  3627.  
  3628. GETKEY MAC  ;Wait`for`a`keypress
  3629. WAIT JSR GETIN
  3630.  CMP #00
  3631.  BEQ WAIT
  3632.  <<<
  3633.  
  3634. DEBUG MAC  ;Print`a`character
  3635.  DO`0``;Don't`assemble
  3636.  
  3637.  LDA`#]1
  3638.  JSR`CHROUT
  3639.  CLI
  3640.  >>> GETKEY ;And`wait`to`continue
  3641.  CMP #'s' ;My`secrect`switch`key
  3642.  BNE L1
  3643.  JSR CLEANUP
  3644.  JMP DONE
  3645. L1 CMP #'x' ;My`secret`abort`key
  3646.  BNE DONE
  3647.  JMP CLEANUP
  3648.  FIN
  3649. DONE <<<
  3650.  
  3651. DEBUGA MAC
  3652.  DO`0
  3653.  LDA ]1
  3654.  STA 1024
  3655.  FIN
  3656. DONEA <<<
  3657.  
  3658. *-------------------------------
  3659.  
  3660.  LDA #$00
  3661.  STA BKGND
  3662.  STA BORDER
  3663.  LDA VMCSB
  3664.  AND #%00001111 ;Screen`memory`to`1024
  3665.  ORA #%00010000
  3666.  STA VMCSB
  3667.  
  3668.  LDY #00
  3669.  LDA #<TTEXT
  3670.  STA TEMP1
  3671.  LDA #>TTEXT
  3672.  STA TEMP2
  3673.  JMP TITLE
  3674. TTEXT HEX 9305111111 ;clear`screen,`white,`crsr`dn
  3675.  TXT '`````````````cube3d`v3.2',0d,0d
  3676.  TXT '``````````````````by',0d
  3677.  HEX 9F ;cyan
  3678.  TXT '````stephen`judd'
  3679.  HEX 99
  3680.  TXT '````george`taylor',0d,0d
  3681.  HEX 9B
  3682.  TXT '``check`out`the`jan.`95`issue`of',0d
  3683.  HEX 96
  3684.  TXT '``c=hacking'
  3685.  HEX 9B
  3686.  TXT '`for`more`details!',0d
  3687.  HEX 0D1D1D9E12
  3688.  TXT 'f1/f2',92
  3689.  TXT '`-`inc/dec`x-rotation',0d
  3690.  HEX 1D1D12
  3691.  TXT 'f3/f4',92
  3692.  TXT '`-`inc/dec`y-rotation',0d
  3693.  HEX 1D1D12
  3694.  TXT 'f5/f6',92
  3695.  TXT '`-`inc/dec`z-rotation',0d
  3696.  HEX 1D1D12
  3697.  TXT '`f7``',92
  3698.  TXT '`-`reset',0d
  3699.  HEX 1D1D12
  3700.  TXT '`+/-`',92
  3701.  TXT '`-`zoom`in/out',0d
  3702.  HEX 1D1D12
  3703.  TXT '``h``',92
  3704.  TXT '`-`toggle`hidden`surfaces',0d
  3705.  HEX 1D1D12
  3706.  TXT 'space',92
  3707.  TXT '`-`toggle`surface`filling',0d,0d
  3708.  TXT '``press`q`to`quit',0d
  3709.  HEX 0D05
  3710.  TXT '``````press`any`key`to`begin',0d
  3711.  HEX 00
  3712. TITLE LDA (TEMP1),Y
  3713.  BEQ :CONT
  3714.  JSR CHROUT
  3715.  INY
  3716.  BNE TITLE
  3717.  INC TEMP2
  3718.  JMP TITLE
  3719. :CONT >>> GETKEY
  3720.  
  3721. ****`Set`up`tables(?)
  3722.  
  3723. *`Tables`are`currently`set`up`in`BASIC
  3724. *`and`by`the`assembler.
  3725.  
  3726. TABLES LDA #>TMATH1
  3727.  STA Z1+1
  3728.  STA Z2+1
  3729.  LDA #>TMATH2
  3730.  STA Z3+1
  3731.  STA Z4+1
  3732.  
  3733. ****`Clear`screen`and`set`up`"bitmap"
  3734. SETUP LDA #$01 ;White
  3735.  STA $D021 ;This`is`done`so`that`older
  3736.  LDA #147 ;machines`will`set`up
  3737.  JSR CHROUT
  3738.  LDA #$00 ;correctly
  3739.  STA $D021
  3740.  LDA #<SSTART
  3741.  ADC #12 ;The`goal`is`to`center`the`graphics
  3742.  STA TEMP1 ;Column`12
  3743.  LDA #>SSTART ;Row`9
  3744.  STA TEMP1+1 ;SSTART`points`to`row`9
  3745.  LDA #00
  3746.  LDY #00
  3747.  LDX #00 ;x`will`count`16`rows`for`us
  3748.  CLC
  3749.  
  3750. :LOOP STA (TEMP1),Y
  3751.  INY
  3752.  ADC #16
  3753.  BCC :LOOP
  3754.  CLC
  3755.  LDA TEMP1
  3756.  ADC #40 ;Need`to`add`40`to`the`base`pointer
  3757.  STA TEMP1 ;To`jump`to`the`next`row
  3758.  LDA TEMP1+1
  3759.  ADC #00 ;Take`care`of`carries
  3760.  STA TEMP1+1
  3761.  LDY #00
  3762.  INX
  3763.  TXA  ;X`is`also`an`index`into`the`character`number
  3764.  CPX #16
  3765.  BNE :LOOP ;Need`to`do`it`16`times
  3766.  
  3767. ****`Clear`buffers
  3768.  
  3769.  LDA #<BUFF1
  3770.  STA BUFFER
  3771.  LDA #>BUFF1
  3772.  STA BUFFER+1
  3773.  LDY #$00
  3774.  LDX #24 ;Assuming`all`three`buffers`are
  3775.  LDA #$00 ;back-to-back
  3776. :BLOOP STA (BUFFER),Y
  3777.  INY
  3778.  BNE :BLOOP
  3779.  INC BUFFER+1
  3780.  DEX
  3781.  BNE :BLOOP
  3782.  
  3783. ****`Set`up`buffers
  3784.  
  3785.  LDA #<BUFF1
  3786.  STA BUFFER
  3787.  LDA #>BUFF1
  3788.  STA BUFFER+1
  3789.  STA ZTEMP ;ztemp`will`make`life`simple`for`us
  3790.  LDA VMCSB
  3791.  AND #%11110001 ;Start`here`so`that`swap`buffers`will`work`right
  3792.  ORA #%00001110
  3793.  STA VMCSB
  3794.  
  3795. ****`Set`up`initial`values
  3796.  
  3797. INIT LDA #00
  3798.  STA LOCXMIN
  3799.  STA LOCXMAX
  3800.  STA LOCYMIN
  3801.  STA LOCYMAX
  3802.  STA GLOBXMIN
  3803.  STA GLOBYMIN
  3804.  STA GLOBXMAX
  3805.  STA GLOBYMAX
  3806.  STA DSX
  3807.  STA DSY
  3808.  STA DSZ
  3809.  STA SX
  3810.  STA SY
  3811.  STA SZ
  3812.  STA FILL
  3813.  LDA #01
  3814.  STA HIDE
  3815.  LDA #64
  3816.  STA ZOOM
  3817.  
  3818. *-------------------------------
  3819. *`Main`loop
  3820.  
  3821. ****`Get`keypress
  3822.  
  3823. MAIN
  3824.  CLI
  3825. KPRESS JSR GETIN
  3826.  CMP #133 ;F1?
  3827.  BNE :F2
  3828.  LDA DSX
  3829.  CMP #ANGMAX/2 ;No`more`than`pi
  3830.  BEQ :CONT1
  3831.  INC DSX ;otherwise`increase`x-rotation
  3832.  JMP :CONT
  3833. :F2 CMP #137 ;F2?
  3834.  BNE :F3
  3835.  LDA DSX
  3836.  BEQ :CONT1
  3837.  DEC DSX
  3838.  JMP :CONT
  3839. :F3 CMP #134
  3840.  BNE :F4
  3841.  LDA DSY
  3842.  CMP #ANGMAX/2
  3843.  BEQ :CONT1
  3844.  INC DSY ;Increase`y-rotation
  3845.  JMP :CONT
  3846. :F4 CMP #138
  3847.  BNE :F5
  3848.  LDA DSY
  3849.  BEQ :CONT1
  3850.  DEC DSY
  3851.  JMP :CONT
  3852. :F5 CMP #135
  3853.  BNE :F6
  3854.  LDA DSZ
  3855.  CMP #ANGMAX/2
  3856.  BEQ :CONT1
  3857.  INC DSZ ;z-rotation
  3858.  JMP :CONT
  3859. :F6 CMP #139
  3860.  BNE :F7
  3861.  LDA DSZ
  3862.  BEQ :CONT1
  3863.  DEC DSZ
  3864.  JMP :CONT
  3865. :F7 CMP #136
  3866.  BNE :PLUS
  3867.  JMP INIT
  3868. :CONT1 JMP :CONT
  3869. :PLUS CMP #'+'
  3870.  BNE :MINUS
  3871.  INC ZOOM ;Bah,`who`needs`error`checking?
  3872.  INC ZOOM
  3873.  JMP :CONT
  3874. :MINUS CMP #'-'
  3875.  BNE :H
  3876.  DEC ZOOM
  3877.  DEC ZOOM
  3878.  BPL :CONT
  3879.  INC ZOOM
  3880.  INC ZOOM
  3881.  JMP :CONT
  3882. :H CMP #'h'
  3883.  BNE :SPACE
  3884.  LDA HIDE
  3885.  EOR #$01
  3886.  STA HIDE
  3887.  JMP :CONT
  3888. :SPACE CMP #'`'
  3889.  BNE :Q
  3890.  LDA FILL
  3891.  EOR #$01
  3892.  STA FILL
  3893.  JMP :CONT
  3894. :Q CMP #'q' ;q`quits
  3895.  BNE :CONT
  3896.  JMP CLEANUP
  3897.  
  3898. :CONT SEI  ;Speed`things`up`a`bit
  3899.  
  3900. ****`Update`angles
  3901.  
  3902. UPDATE CLC
  3903.  LDA SX
  3904.  ADC DSX
  3905.  CMP #ANGMAX ;Are`we`>=`maximum`angle?
  3906.  BCC :CONT1
  3907.  SBC #ANGMAX :If so, reset
  3908. :CONT1 STA SX
  3909.  CLC
  3910.  LDA SY
  3911.  ADC DSY
  3912.  CMP #ANGMAX
  3913.  BCC :CONT2
  3914.  SBC #ANGMAX ;Same`deal
  3915. :CONT2 STA SY
  3916.  CLC
  3917.  LDA SZ
  3918.  ADC DSZ
  3919.  CMP #ANGMAX
  3920.  BCC :CONT3
  3921.  SBC #ANGMAX
  3922. :CONT3 STA SZ
  3923.  
  3924. ****`Rotate`coordinates
  3925.  
  3926. ROTATE
  3927.  
  3928. ***`First,`calculate`t1,t2,...,t10
  3929.  
  3930. **`Two`macros`to`simplify`our`life
  3931. ADDA MAC  ;Add`two`angles`together
  3932.  CLC
  3933.  LDA ]1
  3934.  ADC ]2
  3935.  CMP #ANGMAX ;Is`the`sum`>`2*pi?
  3936.  BCC DONE
  3937.  SBC #ANGMAX ;If`so,`subtract`2*pi
  3938. DONE <<<
  3939.  
  3940. SUBA MAC  ;Subtract`two`angles
  3941.  SEC
  3942.  LDA ]1
  3943.  SBC ]2
  3944.  BCS DONE
  3945.  ADC #ANGMAX ;Oops,`we`need`to`add`2*pi
  3946. DONE <<<
  3947.  
  3948. **`Now`calculate`t1,t2,etc.
  3949.  
  3950.  >>> SUBA,SY;SZ
  3951.  STA T1 ;t1=sy-sz
  3952.  >>> ADDA,SY;SZ
  3953.  STA T2 ;t2=sy+sz
  3954.  >>> ADDA,SX;SZ
  3955.  STA T3 ;t3=sx+sz
  3956.  >>> SUBA,SX;SZ
  3957.  STA T4 ;t4=sx-sz
  3958.  >>> ADDA,SX;T2
  3959.  STA T5 ;t5=sx+t2
  3960.  >>> SUBA,SX;T1
  3961.  STA T6 ;t6=sx-t1
  3962.  >>> ADDA,SX;T1
  3963.  STA T7 ;t7=sx+t1
  3964.  >>> SUBA,T2;SX
  3965.  STA T8 ;t8=t2-sx
  3966.  >>> SUBA,SY;SX
  3967.  STA T9 ;t9=sy-sx
  3968.  >>> ADDA,SX;SY
  3969.  STA T10 ;t10=sx+sy
  3970.  
  3971. *`Et`voila!
  3972.  
  3973. ***`Next,`calculate`A,B,C,...,I
  3974.  
  3975. **`Another`useful`little`macro
  3976. DIV2 MAC  ;Divide`a`signed`number`by`2
  3977. ;It`is`assumed`that`the`number
  3978.  BPL POS ;is`in`the`accumulator
  3979.  CLC
  3980.  EOR #$FF ;We`need`to`un-negative`the`number
  3981.  ADC #01 ;by`taking`it's`complement
  3982.  LSR  ;divide`by`two
  3983.  CLC
  3984.  EOR #$FF
  3985.  ADC #01 ;Make`it`negative`again
  3986.  JMP DONEDIV
  3987. POS LSR  ;Number`is`positive
  3988. DONEDIV <<<
  3989.  
  3990. MUL2 MAC  ;Multiply`a`signed`number`by`2
  3991.  BPL POSM
  3992.  CLC
  3993.  EOR #$FF
  3994.  ADC #$01
  3995.  ASL
  3996.  CLC
  3997.  EOR #$FF
  3998.  ADC #$01
  3999.  JMP DONEMUL
  4000. POSM ASL
  4001. DONEMUL <<<
  4002.  
  4003. **`Note`that`we`are`currently`making`a`minor`leap
  4004. **`of`faith`that`no`overflows`will`occur.
  4005.  
  4006. :CALCA CLC
  4007.  LDX T1
  4008.  LDA COS,X
  4009.  LDX T2
  4010.  ADC COS,X
  4011.  STA A11 ;A=(cos(t1)+cos(t2))/2
  4012. :CALCB LDX T1
  4013.  LDA SIN,X
  4014.  SEC
  4015.  LDX T2
  4016.  SBC SIN,X
  4017.  STA B12 ;B=(sin(t1)-sin(t2))/2
  4018. :CALCC LDX SY
  4019.  LDA SIN,X
  4020.  >>> MUL2
  4021.  STA C13 ;C=sin(sy)
  4022. :CALCD SEC
  4023.  LDX T8
  4024.  LDA COS,X
  4025.  LDX T7
  4026.  SBC COS,X
  4027.  SEC
  4028.  LDX T5
  4029.  SBC COS,X
  4030.  CLC
  4031.  LDX T6
  4032.  ADC COS,X ;Di=(cos(t8)-cos(t7)+cos(t6)-cos(t5))/2
  4033.  >>> DIV2
  4034.  CLC
  4035.  LDX T3
  4036.  ADC SIN,X
  4037.  SEC
  4038.  LDX T4
  4039.  SBC SIN,X
  4040.  STA D21 ;D=(sin(t3)-sin(t4)+Di)/2
  4041. :CALCE SEC
  4042.  LDX T5
  4043.  LDA SIN,X
  4044.  LDX T6
  4045.  SBC SIN,X
  4046.  SEC
  4047.  LDX T7
  4048.  SBC SIN,X
  4049.  SEC
  4050.  LDX T8
  4051.  SBC SIN,X ;Ei=(sin(t5)-sin(t6)-sin(t7)-sin(t8))/2
  4052.  >>> DIV2
  4053.  CLC
  4054.  LDX T3
  4055.  ADC COS,X
  4056.  CLC
  4057.  LDX T4
  4058.  ADC COS,X
  4059.  STA E22 ;E=(cos(t3)+cos(t4)+Ei)/2
  4060. :CALCF LDX T9
  4061.  LDA SIN,X
  4062.  SEC
  4063.  LDX T10
  4064.  SBC SIN,X
  4065.  STA F23 ;F=(sin(t9)-sin(t10))/2
  4066. :CALCG LDX T6
  4067.  LDA SIN,X
  4068.  SEC
  4069.  LDX T8
  4070.  SBC SIN,X
  4071.  SEC
  4072.  LDX T7
  4073.  SBC SIN,X
  4074.  SEC
  4075.  LDX T5
  4076.  SBC SIN,X ;Gi=(sin(t6)-sin(t8)-sin(t7)-sin(t5))/2
  4077.  >>> DIV2
  4078.  
  4079.  CLC
  4080.  LDX T4
  4081.  ADC COS,X
  4082.  SEC
  4083.  LDX T3
  4084.  SBC COS,X
  4085.  STA G31 ;G=(cos(t4)-cos(t3)+Gi)/2
  4086. :CALCH CLC
  4087.  LDX T6
  4088.  LDA COS,X
  4089.  LDX T7
  4090.  ADC COS,X
  4091.  SEC
  4092.  LDX T5
  4093.  SBC COS,X
  4094.  SEC
  4095.  LDX T8
  4096.  SBC COS,X ;Hi=(cos(t6)+cos(t7)-cos(t5)-cos(t8))/2
  4097.  >>> DIV2
  4098.  CLC
  4099.  LDX T3
  4100.  ADC SIN,X
  4101.  CLC
  4102.  LDX T4
  4103.  ADC SIN,X
  4104.  STA H32 ;H=(sin(t3)+sin(t4)+Hi)/2
  4105. :WHEW CLC
  4106.  LDX T9
  4107.  LDA COS,X
  4108.  LDX T10
  4109.  ADC COS,X
  4110.  STA I33 ;I=(cos(t9)+cos(t10))/2
  4111.  
  4112. **`It's`all`downhill`from`here.
  4113.  
  4114. DOWNHILL
  4115. ****`Clear`buffer
  4116. *`A`little`macro
  4117.  
  4118. SETBUF MAC  ;Put`buffers`where`they`can`be`hurt
  4119.  LDA #00
  4120.  STA BUFFER
  4121.  LDA ZTEMP ;High`byte
  4122. STABUF STA BUFFER+1
  4123.  <<<
  4124.  
  4125.  >>> SETBUF
  4126. CLRDRAW LDX #08
  4127.  LDA #00
  4128. :FOOL LDY #00
  4129. :DOPE STA (BUFFER),Y
  4130.  INY
  4131.  BNE :DOPE
  4132.  INC BUFFER+1
  4133.  DEX
  4134.  BNE :FOOL
  4135.  
  4136. ****`My`goodness`but`I'm`a`dope
  4137. *CLRDRAW`LDA`GLOBXMIN
  4138. *`LSR``;Need`to`get`into`the`right`column
  4139. *`BCC`:EVEN`;Explained`in`more`detail`below
  4140. *`LDY`#$80
  4141. *`STY`BUFFER`;Presumably`this`will`be`a`little
  4142. *`CLC``;more`efficient.
  4143. *:EVEN`ADC`BUFFER+1
  4144. *`STA`BUFFER+1
  4145. *`LDA`GLOBXMAX
  4146. *`SEC
  4147. *`SBC`GLOBXMIN
  4148. *`TAX
  4149. *`INX
  4150. *`LDY`GLOBYMAX
  4151. *`BEQ`:RESET
  4152. *:YAY`LDA`#$00
  4153. *`LDY`GLOBYMAX
  4154. *:BLAH`STA`(BUFFER),Y
  4155. *`DEY
  4156. *`CPY`GLOBYMIN
  4157. *`BCS`:BLAH
  4158. *`LDA`BUFFER
  4159. *`EOR`#$80
  4160. *`STA`BUFFER
  4161. *`BNE`:WHOPEE
  4162. *`INC`BUFFER+1
  4163. *:WHOPEE`DEX
  4164. *`BNE`:YAY
  4165. *:RESET`LDA`#0`;Need`to`reset`these`guys
  4166. *`STA`GLOBXMAX
  4167. *`STA`GLOBYMAX
  4168. *`LDA`#$FF
  4169. *`STA`GLOBXMIN
  4170. *`STA`GLOBYMIN
  4171.  
  4172. ****`Next,`read`and`draw`polygons
  4173.  
  4174. READDRAW LDY #00
  4175.  STY INDEX
  4176. OBJLOOP LDY INDEX
  4177.  LDA POLYLIST,Y ;First,`the`number`of`points
  4178.  BNE :CONT ;But`if`numpoints`is`zero`then
  4179.  JMP OBJDONE ;we`are`at`the`end`of`the`list
  4180. :CONT STA COUNTPTS
  4181.  INC INDEX
  4182.  
  4183. *`Rotate`project`and`draw`the`polygon
  4184. *`Make`sure`buffer`being`drawn`to`is`clear!
  4185.  
  4186. :DOIT JSR ROTPROJ
  4187.  
  4188. *`Convert`xmin`and`xmax`to`columns
  4189.  
  4190.  LDA LOCXMIN
  4191.  LSR
  4192.  LSR
  4193.  LSR  ;x`mod`8
  4194.  STA LOCXMIN
  4195.  CMP GLOBXMIN
  4196.  BCS :NAH
  4197.  STA GLOBXMIN
  4198. :NAH LDA LOCYMIN
  4199.  CMP GLOBYMIN
  4200.  BCS :UHUH
  4201.  STA GLOBYMIN
  4202. :UHUH LDA LOCXMAX
  4203.  LSR
  4204.  LSR
  4205.  LSR
  4206.  STA LOCXMAX
  4207.  CMP GLOBXMAX
  4208.  BCC :NOWAY
  4209.  STA GLOBXMAX
  4210. :NOWAY LDA LOCYMAX
  4211.  CMP GLOBYMAX
  4212.  BCC EORFILL
  4213.  STA GLOBYMAX
  4214.  
  4215. *`If`using`the`EOR-buffer,`copy`into`drawing`buffer
  4216. *`And`then`clear`the`EOR-buffer
  4217.  
  4218. EORFILL LDA FILL
  4219.  BEQ OBJLOOP
  4220.  
  4221.  >>> SETBUF
  4222.  LDA #<EORBUF
  4223.  STA TEMP1
  4224.  LDA #>EORBUF
  4225.  STA TEMP1+1
  4226.  
  4227.  LDA LOCXMIN ;LOCXMIN`now`contains`column
  4228.  LSR  ;Each`column`is`128`bytes
  4229.  BCC :EVEN ;So`there`might`be`a`carry
  4230.  LDY #$80
  4231.  STY BUFFER
  4232.  STY TEMP1
  4233.  CLC
  4234. :EVEN STA T2
  4235.  ADC BUFFER+1
  4236.  STA BUFFER+1 ;Each`column`is`128`bytes
  4237.  LDA T2
  4238.  ADC TEMP1+1 ;Now`we`will`start`at`the
  4239.  STA TEMP1+1 ;column
  4240.  
  4241.  LDA LOCXMAX
  4242.  SEC
  4243.  SBC LOCXMIN
  4244.  TAX ;Total`number`of`columns`to`do
  4245.  INX  ;e.g.`fill`columns`1..3
  4246.  LDY LOCYMAX
  4247.  BNE :FOOP
  4248.  INC LOCYMAX
  4249. :FOOP LDY LOCYMAX
  4250.  LDA #00
  4251. :GOOP EOR (TEMP1),Y ;EOR-buffer
  4252.  PHA
  4253. *`Maybe`put`an`EOR`below?
  4254.  EOR (BUFFER),Y
  4255.  STA (BUFFER),Y
  4256.  LDA #00 ;Might`as`well`clear`it`now
  4257.  STA (TEMP1),Y
  4258.  PLA
  4259.  DEY
  4260.  CPY LOCYMIN
  4261.  BCS :GOOP
  4262.  LDA BUFFER
  4263.  EOR #$80
  4264.  STA BUFFER
  4265.  STA TEMP1
  4266.  BNE :BOOP
  4267.  INC BUFFER+1
  4268.  INC TEMP1+1
  4269. :BOOP DEX
  4270.  BNE :FOOP
  4271.  JMP OBJLOOP
  4272.  
  4273. OBJDONE
  4274. ****`Swap`buffers
  4275.  
  4276. SWAPBUF LDA VMCSB
  4277.  EOR #$02 ;Pretty`tricky,`eh?
  4278.  STA VMCSB
  4279.  LDA #$08
  4280.  EOR ZTEMP ;ztemp=high`byte`just`flips
  4281.  STA ZTEMP ;between`$30`and`$38
  4282.  
  4283.  JMP MAIN ;Around`and`around`we`go...
  4284.  
  4285.  TXT 'Gee`Brain,`what`do`you`want`to`do`'
  4286.  TXT 'tonight?'
  4287.  
  4288. **`Rotate,`project,`and`store`the`points
  4289. *
  4290. *`This`part`is`a`significant`change`since
  4291. *`v2.0.``Now`it`is`a`completely`general`polygon`plotter.
  4292. *`A`set`of`points`is`read`in,`rotated`and`projected,`and
  4293. *`plotted`into`the`drawing`buffer`(EOR`or`normal).
  4294.  
  4295. ROTPROJ
  4296.  
  4297. *`A`neat`macro
  4298. NEG MAC  ;Change`the`sign`of`a`two's`complement
  4299.  CLC
  4300.  LDA ]1 ;number.
  4301.  EOR #$FF
  4302.  ADC #$01
  4303.  <<<
  4304.  
  4305. *-------------------------------
  4306. *`These`macros`replace`the`previous`projection
  4307. *`subroutine.
  4308.  
  4309. SMULT`MAC`;Multiply`two`signed`8-bit
  4310.  ;numbers:`A*Y/64`->`A
  4311.  STA`Z3
  4312.  CLC``;This`multiply`is`for`normal
  4313.  EOR`#$FF`;numbers,`i.e.`x=-64..64
  4314.  ADC`#$01
  4315.  STA`Z4
  4316.  LDA`(Z3),Y
  4317.  SEC
  4318.  SBC`(Z4),Y
  4319.  <<<``;All`done`:)
  4320.  
  4321. SMULTZ MAC ;Multiply`two`signed`8-bit
  4322.    ;numbers:`A*Y/64`->`A
  4323.  STA Z1
  4324.  CLC  ;And`this`multiply`is`specifically
  4325.  EOR #$FF ;for`the`projection`part,`where
  4326.  ADC #$01 ;numbers`are`-110..110`and`0..40
  4327.  STA Z2
  4328.  LDA (Z1),Y
  4329.  SEC
  4330.  SBC (Z2),Y
  4331.  <<<  ;All`done`:)
  4332.  
  4333. PROJECT MAC  ;The`actual`projection`routine
  4334. ;The`routine`takes`the`point
  4335. ;]1`]2`]3,`rotates`and
  4336. ;projects`it,`and`stores`the
  4337. ;result`in`]1`]2`]3.
  4338.  
  4339.  LDY ]1 ;Multiply`first`rotation`column
  4340.  LDA A11
  4341.  >>> SMULT
  4342.  STA P1T
  4343.  LDA D21
  4344.  >>> SMULT
  4345.  STA P2T
  4346.  LDA G31
  4347.  >>> SMULT
  4348.  STA P3T
  4349.  LDY ]2 ;Second`column
  4350.  LDA B12
  4351.  >>> SMULT
  4352.  CLC
  4353.  ADC P1T
  4354.  STA P1T
  4355.  LDA E22
  4356.  >>> SMULT
  4357.  CLC
  4358.  ADC P2T
  4359.  STA P2T
  4360.  LDA H32
  4361.  >>> SMULT
  4362.  CLC
  4363.  ADC P3T
  4364.  STA P3T
  4365.  LDY ]3 ;Third`column
  4366.  LDA C13
  4367.  >>> SMULT
  4368.  CLC
  4369.  ADC P1T
  4370.  STA P1T
  4371.  LDA F23
  4372.  >>> SMULT
  4373.  CLC
  4374.  ADC P2T
  4375.  STA P2T
  4376.  LDA I33
  4377.  >>> SMULT
  4378.  CLC
  4379.  ADC P3T
  4380.  STA ]3 ;Rotated`Z
  4381.  TAX
  4382.  LDY ZDIV,X ;Table`of`d/(z+z0)
  4383.    ;Now`Y`contains`projection`const
  4384.  
  4385.  LDA P1T
  4386.  >>> SMULTZ
  4387.  LDX ZOOM
  4388.  CPX #64
  4389.  BEQ CONTX
  4390.  STY TEMP1
  4391.  LDY ZOOM
  4392.  >>> SMULT
  4393.  LDY TEMP1
  4394. CONTX CLC
  4395.  ADC #64 ;Offset`the`coordinate
  4396.  STA ]1 ;Rotated`and`projected
  4397.  CMP LOCXMIN ;See`if`it`is`a`local`minimum
  4398.  BCS NOTXMIN
  4399.  STA LOCXMIN
  4400. NOTXMIN CMP LOCXMAX
  4401.  BCC NOTXMAX
  4402.  STA LOCXMAX
  4403.  
  4404. NOTXMAX LDA P2T
  4405.  >>> SMULTZ
  4406.  CPX #64
  4407.  BEQ CONTY
  4408.  LDY ZOOM
  4409.  >>> SMULT
  4410. CONTY CLC
  4411.  ADC #64
  4412.  STA ]2 ;Rotated`and`projected`Y
  4413.  CMP LOCYMIN
  4414.  BCS NOTYMIN
  4415.  STA LOCYMIN
  4416. NOTYMIN CMP LOCYMAX
  4417.  BCC NOTYMAX
  4418.  STA LOCYMAX
  4419.  
  4420. NOTYMAX <<<  ;All`done
  4421.  
  4422. *`LDA`#<EORBUF`;First`we`need`to`clear`the
  4423. *`STA`BUFFER`;EOR`buffer
  4424. *`LDA`#>EORBUF
  4425. *`STA`BUFFER+1
  4426.  
  4427.  LDA #0 ;Reset`Ymin`and`Ymax
  4428.  STA LOCYMAX
  4429.  STA LOCXMAX
  4430.  LDA #$FF
  4431.  STA LOCYMIN
  4432.  STA LOCXMIN
  4433.  
  4434. READPTS LDY INDEX
  4435.  LDA POLYLIST,Y
  4436.  STA P1X
  4437.  INY
  4438.  LDA POLYLIST,Y
  4439.  STA P1Y
  4440.  INY
  4441.  LDA POLYLIST,Y
  4442.  STA P1Z
  4443.  INY
  4444.  DEC COUNTPTS
  4445.  LDA POLYLIST,Y
  4446.  STA P2X
  4447.  INY
  4448.  LDA POLYLIST,Y
  4449.  STA P2Y
  4450.  INY
  4451.  LDA POLYLIST,Y
  4452.  STA P2Z
  4453.  INY
  4454.  DEC COUNTPTS
  4455.  LDA POLYLIST,Y
  4456.  STA P3X
  4457.  INY
  4458.  LDA POLYLIST,Y
  4459.  STA P3Y
  4460.  INY
  4461.  LDA POLYLIST,Y
  4462.  STA P3Z
  4463.  INY
  4464.  STY INDEX
  4465.  >>> PROJECT,P1X;P1Y;P1Z
  4466.  >>> PROJECT,P2X;P2Y;P2Z
  4467.  >>> PROJECT,P3X;P3Y;P3Z
  4468.  
  4469.  LDA HIDE
  4470.  BEQ :DOIT
  4471.  LDA P2X ;Hidden`face`check
  4472.  SEC
  4473.  SBC P1X
  4474.  TAY  ;Y=(x2-x1)
  4475.  LDA P3Y
  4476.  SEC
  4477.  SBC P2Y ;A=(y3-y2)
  4478.  >>> SMULT
  4479.  STA TEMP1
  4480.  LDA P3X
  4481.  SEC
  4482.  SBC P2X
  4483.  TAY
  4484.  LDA P2Y
  4485.  SEC
  4486.  SBC P1Y
  4487.  >>> SMULT
  4488.  CMP TEMP1 ;If`x1*y2-y1*x2`>`0`then`face
  4489.  BMI :DOIT ;is`visible
  4490.  DEC COUNTPTS ;Otherwise`read`in`remaining
  4491.  BEQ :ABORT ;points`and`return
  4492. :POOP INC INDEX
  4493.  INC INDEX
  4494.  INC INDEX
  4495.  DEC COUNTPTS
  4496.  BNE :POOP
  4497. :ABORT RTS
  4498.  
  4499. :DOIT LDA P1X
  4500.  STA X1
  4501.  LDA P1Y
  4502.  STA Y1
  4503.  LDA P2X
  4504.  STA X2
  4505.  LDA P2Y
  4506.  STA Y2
  4507.  JSR DRAW
  4508.  LDA P2X
  4509.  STA X1
  4510.  LDA P2Y
  4511.  STA Y1
  4512.  LDA P3X
  4513.  STA X2
  4514.  LDA P3Y
  4515.  STA Y2
  4516.  JSR DRAW
  4517.  
  4518.  DEC COUNTPTS
  4519.  BNE POLYLOOP ;Is`it`just`a`triangle?
  4520.  JMP POLYDONE
  4521.  
  4522. POLYLOOP LDY INDEX
  4523.  LDA POLYLIST,Y
  4524.  STA P2X
  4525.  INY
  4526.  LDA POLYLIST,Y
  4527.  STA P2Y
  4528.  INY
  4529.  LDA POLYLIST,Y
  4530.  STA P2Z
  4531.  INY
  4532.  STY INDEX
  4533.  >>> PROJECT,P2X;P2Y;P2Z
  4534.  
  4535.  LDA P2X
  4536.  STA X1
  4537.  LDA P2Y
  4538.  STA Y1
  4539.  LDA P3X
  4540.  STA X2
  4541.  LDA P3Y
  4542.  STA Y2
  4543.  JSR DRAW
  4544.  
  4545.  LDA P2X
  4546.  STA P3X
  4547.  LDA P2Y
  4548.  STA P3Y
  4549.  DEC COUNTPTS
  4550.  BEQ POLYDONE
  4551.  JMP POLYLOOP
  4552. POLYDONE LDA P1X ;Close`the`polygon
  4553.  STA X2
  4554.  LDA P1Y
  4555.  STA Y2
  4556.  LDA P3X
  4557.  STA X1
  4558.  LDA P3Y
  4559.  STA Y1
  4560.  JSR DRAW
  4561.  RTS
  4562.  
  4563.  TXT 'Same`thing`we`do`every`night,`Pinky:`'
  4564.  TXT 'try`to`take`over`the`world!'
  4565.  
  4566.  
  4567. *-------------------------------
  4568. *`General`questionable-value`error`procedure
  4569.  
  4570. *CHOKE`LDX`#00
  4571. *:LOOP`LDA`:CTEXT,X
  4572. *`BEQ`:DONE
  4573. *`JSR`CHROUT
  4574. *`INX
  4575. *`JMP`:LOOP
  4576. *:DONE`RTS
  4577. *:CTEXT`HEX`0D`;CR
  4578. *`TXT`'something`choked`:('
  4579. *`HEX`0D00
  4580. *
  4581.  TXT 'Narf!'
  4582.  
  4583. *-------------------------------
  4584. *`Drawin'`a`line.``A`fahn`lahn.
  4585.  
  4586. ***`Some`useful`macros
  4587.  
  4588. CINIT MAC  ;Macro`to`initialize`the`counter
  4589.  LDA ]1 ;dx`or`dy
  4590.  LSR
  4591.  <<<  ;The`dx/2`makes`a`nicer`looking`line
  4592.  
  4593. *****`Macro`to`take`a`step`in`X
  4594.  
  4595. XSTEP MAC
  4596.  LDX DX ;Number`of`loop`iterations
  4597.  >>> CINIT,DX
  4598. XLOOP LSR CHUNK
  4599.  BEQ FIXC ;Update`column
  4600.  SBC DY
  4601.  BCC FIXY ;Time`to`step`in`Y
  4602.  DEX
  4603.  BNE XLOOP
  4604. DONE LDA OLDX ;Plot`the`last`chunk
  4605.  EOR CHUNK
  4606.  ORA (BUFFER),Y
  4607.  STA (BUFFER),Y
  4608.  RTS
  4609.  
  4610. FIXC PHA
  4611.  LDA OLDX
  4612.  ORA (BUFFER),Y ;Plot
  4613.  STA (BUFFER),Y
  4614.  LDA #$FF ;Update`chunk
  4615.  STA OLDX
  4616.  STA CHUNK
  4617.  LDA #$80 ;Increase`the`column
  4618.  EOR BUFFER
  4619.  STA BUFFER
  4620.  BNE C2
  4621.  INC BUFFER+1
  4622. C2
  4623.  PLA
  4624.  SBC DY
  4625.  BCS CONT
  4626.  ADC DX
  4627.  IF I,]1 ;Do`we`use`INY`or`DEY?
  4628.  INY
  4629.  ELSE
  4630.  DEY
  4631.  FIN
  4632. CONT DEX
  4633.  BNE XLOOP
  4634.  JMP DONE
  4635.  
  4636. FIXY ADC DX
  4637.  PHA
  4638.  LDA OLDX
  4639.  EOR CHUNK
  4640.  ORA (BUFFER),Y
  4641.  STA (BUFFER),Y
  4642.  LDA CHUNK
  4643.  STA OLDX
  4644.  PLA
  4645.  IF I,]1 ;Update`Y
  4646.  INY
  4647.  ELSE
  4648.  DEY
  4649.  FIN
  4650.  DEX
  4651.  BNE XLOOP
  4652.  RTS
  4653.  <<<  ;End`of`Macro`xstep
  4654.  
  4655. *****`Take`a`step`in`Y
  4656.  
  4657. YSTEP MAC
  4658.  LDX DY ;Number`of`loop`iterations
  4659.  BEQ DONE ;If`dy=0`it's`just`a`point
  4660.  >>> CINIT,DY
  4661.  SEC
  4662. YLOOP PHA
  4663.  LDA OLDX
  4664.  ORA (BUFFER),Y
  4665.  STA (BUFFER),Y
  4666.  PLA
  4667.  IF I,]1
  4668.  INY
  4669.  ELSE
  4670.  DEY
  4671.  FIN
  4672.  SBC DX
  4673.  BCC FIXX
  4674.  DEX
  4675.  BNE YLOOP
  4676. DONE LDA OLDX
  4677.  ORA (BUFFER),Y
  4678.  STA (BUFFER),Y
  4679.  RTS
  4680.  
  4681. FIXX ADC DY
  4682.  LSR OLDX
  4683.  SEC  ;Important!
  4684.  BEQ FIXC
  4685.  DEX
  4686.  BNE YLOOP
  4687.  JMP DONE
  4688.  
  4689. FIXC PHA
  4690.  LDA #$80
  4691.  STA OLDX
  4692.  EOR BUFFER
  4693.  STA BUFFER
  4694.  BNE C2
  4695.  INC BUFFER+1
  4696. C2 PLA
  4697.  DEX
  4698.  BNE YLOOP
  4699.  JMP DONE
  4700.  <<<  ;End`of`Macro`ystep
  4701.  
  4702. *`Take`an`x`step`in`the`EOR`buffer
  4703. *`The`sole`change`is`to`use`EOR`instead`of`ORA
  4704.  
  4705. EORXSTEP MAC
  4706.  LDX DX ;Number`of`loop`iterations
  4707.  >>> CINIT,DX
  4708. XLOOP LSR CHUNK
  4709.  BEQ FIXC ;Update`column
  4710.  SBC DY
  4711.  BCC FIXY ;Time`to`step`in`Y
  4712.  DEX
  4713.  BNE XLOOP
  4714. DONE LDA OLDX ;Plot`the`last`chunk
  4715.  EOR CHUNK
  4716.  EOR (BUFFER),Y
  4717.  STA (BUFFER),Y
  4718.  RTS
  4719.  
  4720. FIXC PHA
  4721.  LDA OLDX
  4722.  EOR (BUFFER),Y ;Plot
  4723.  STA (BUFFER),Y
  4724.  LDA #$FF ;Update`chunk
  4725.  STA OLDX
  4726.  STA CHUNK
  4727.  LDA #$80 ;Increase`the`column
  4728.  EOR BUFFER
  4729.  STA BUFFER
  4730.  BNE C2
  4731.  INC BUFFER+1
  4732. C2
  4733.  PLA
  4734.  SBC DY
  4735.  BCS CONT
  4736.  ADC DX
  4737.  IF I,]1 ;Do`we`use`INY`or`DEY?
  4738.  INY
  4739.  ELSE
  4740.  DEY
  4741.  FIN
  4742. CONT DEX
  4743.  BNE XLOOP
  4744.  JMP DONE
  4745.  
  4746. FIXY ADC DX
  4747.  PHA
  4748.  LDA OLDX
  4749.  EOR CHUNK
  4750.  EOR (BUFFER),Y
  4751.  STA (BUFFER),Y
  4752.  LDA CHUNK
  4753.  STA OLDX
  4754.  PLA
  4755.  IF I,]1 ;Update`Y
  4756.  INY
  4757.  ELSE
  4758.  DEY
  4759.  FIN
  4760.  DEX
  4761.  BNE XLOOP
  4762.  RTS
  4763.  <<<  ;End`of`Macro`xstep
  4764.  
  4765.  
  4766. *`Take`a`y-step`in`the`EOR-buffer
  4767. *`Changes`from`above`are:`only`plot`last`part`of`each
  4768. *`vertical`chunk,`don't`plot`last`point,`plot`with`EOR
  4769.  
  4770. EORYSTEP MAC
  4771.  LDX DY ;Number`of`loop`iterations
  4772.  BEQ DONE ;If`dy=0`it's`just`a`point
  4773.  >>> CINIT,DY
  4774.  SEC
  4775. *YLOOP`PHA
  4776. *`LDA`OLDX
  4777. *`ORA`(BUFFER),Y
  4778. *`STA`(BUFFER),Y
  4779. *`PLA
  4780. YLOOP IF I,]1
  4781.  INY
  4782.  ELSE
  4783.  DEY
  4784.  FIN
  4785.  SBC DX
  4786.  BCC FIXX
  4787.  DEX
  4788.  BNE YLOOP
  4789. *DONE`LDA`OLDX
  4790. *`ORA`(BUFFER),Y
  4791. *`STA`(BUFFER),Y
  4792. DONE RTS
  4793.  
  4794. FIXX ADC DY
  4795.  PHA  ;We`only`plot`the`last`part`of`each`chunk
  4796.  LDA OLDX
  4797.  EOR (BUFFER),Y
  4798.  STA (BUFFER),Y
  4799.  PLA
  4800.  LSR OLDX
  4801.  SEC  ;Important!
  4802.  BEQ FIXC
  4803.  DEX
  4804.  BNE YLOOP
  4805.  JMP DONE
  4806.  
  4807. FIXC PHA
  4808.  LDA #$80
  4809.  STA OLDX
  4810.  EOR BUFFER
  4811.  STA BUFFER
  4812.  BNE C2
  4813.  INC BUFFER+1
  4814. C2 PLA
  4815.  DEX
  4816.  BNE YLOOP
  4817.  JMP DONE
  4818.  <<<  ;End`of`Macro`ystep
  4819. ****`Initial`line`setup
  4820.  
  4821. **`The`commented`lines`below`are`now`taken`care`of`by`the
  4822. **`calling`routine.
  4823. *DRAW`>>>`MOVE,TX1;X1``;Move`stuff`into`zero`page
  4824. *`>>>`MOVE,TX2;X2``;Where`it`can`be`modified
  4825. *`>>>`MOVE,TY1;Y1
  4826. *`>>>`MOVE,TY2;Y2
  4827.  
  4828. DRAW LDA FILL
  4829.  BNE :SETEOR
  4830.  >>> SETBUF
  4831.  JMP :SETUP
  4832. :SETEOR LDA #<EORBUF ;Use`EOR`buffer`instead`of
  4833.  STA BUFFER ;display`buffer`for`drawing
  4834.  LDA #>EORBUF
  4835.  STA BUFFER+1
  4836.  
  4837. :SETUP SEC  ;Make`sure`x1<x2
  4838.  LDA X2
  4839.  SBC X1
  4840.  BCS :CONT
  4841.  LDA Y2 ;If`not,`swap`P1`and`P2
  4842.  LDY Y1
  4843.  STA Y1
  4844.  STY Y2
  4845.  LDA X1
  4846.  LDY X2
  4847.  STY X1
  4848.  STA X2
  4849.  
  4850.  SEC
  4851.  SBC X1 ;Now`A=dx
  4852. :CONT STA DX
  4853.  LDX X1 ;Put`x1`into`X,`now`we`can`trash`X1
  4854.  
  4855. COLUMN TXA ;Find`the`first`column`for`X
  4856.  LSR
  4857.  LSR  ;There`are`x1/8`128`byte`blocks
  4858.  LSR  ;Which`means`x1/16`256`byte`blocks
  4859.  LSR
  4860.  BCC :EVEN ;With`a`possible`extra`128`byte`block
  4861.  LDY #$80 ;if`so,`set`the`high`bit
  4862.  STY BUFFER
  4863.  CLC
  4864. :EVEN ADC BUFFER+1 ;Add`in`the`number`of`256`byte`blocks
  4865.  STA BUFFER+1
  4866.  
  4867.  SEC
  4868.  LDA Y2 ;Calculate`dy
  4869.  SBC Y1
  4870.  BCS :CONT2 ;Is`y2>y1?
  4871.  EOR #$FF ;Otherwise`dy=y1-y2
  4872.  ADC #$01
  4873. :CONT2 STA DY
  4874.  CMP DX ;Who's`bigger:`dy`or`dx?
  4875.  BCC STEPINX ;If`dx,`then...
  4876.  JMP STEPINY
  4877.  
  4878. STEPINX LDY Y1
  4879.  CPY Y2
  4880.  LDA BITP,X ;X`currently`contains`x1
  4881.  STA OLDX
  4882.  STA CHUNK
  4883.  BCC XINCY ;Do`we`step`forwards`or`backwards`in`Y?
  4884.  JMP XDECY
  4885.  
  4886. XINCY LDA FILL
  4887.  BEQ NORMXINC
  4888.  >>> EORXSTEP,INY
  4889. NORMXINC >>> XSTEP,INY
  4890.  
  4891. XDECY LDA FILL
  4892.  BEQ NORMXDEC
  4893.  >>> EORXSTEP,DEY
  4894. NORMXDEC >>> XSTEP,DEY
  4895.  
  4896. STEPINY LDY Y1
  4897.  LDA BITP,X ;X=x1
  4898.  STA OLDX
  4899.  LSR  ;Y`doesn't`use`chunks
  4900.  EOR OLDX ;So`we`just`want`the`bit
  4901.  STA OLDX
  4902.  CPY Y2
  4903.  BCS YDECY
  4904.  
  4905. YINCY LDA FILL
  4906.  BEQ NORMINC
  4907.  >>> EORYSTEP,INY
  4908. NORMINC >>> YSTEP,INY
  4909.  
  4910. YDECY LDA FILL
  4911.  BEQ NORMDEC
  4912.  >>> EORYSTEP,DEY
  4913. NORMDEC >>> YSTEP,DEY
  4914.  
  4915.  
  4916. *-------------------------------
  4917. *`Clean`up
  4918.  
  4919. CLEANUP LDA VMCSB ;Switch`char`rom`back`in
  4920.  AND #%11110101 ;default
  4921.  STA VMCSB
  4922.  
  4923.  RTS  ;bye!
  4924.  
  4925.  TXT 'spinal`cracker`'
  4926.  TXT 'slj`6/95'
  4927.  
  4928. *-------------------------------
  4929. *`Set`up`bit`table
  4930.  
  4931.  DS ^ ;Clear`to`end`of`page
  4932.    ;So`that`tables`start`on`a`page`boundary
  4933. BITP LUP 16 ;128`Entries`for`X
  4934.  DFB %11111111
  4935.  DFB %01111111
  4936.  DFB %00111111
  4937.  DFB %00011111
  4938.  DFB %00001111
  4939.  DFB %00000111
  4940.  DFB %00000011
  4941.  DFB %00000001
  4942.  --^
  4943.  
  4944. SIN ;Table`of`sines,`120`bytes
  4945. COS EQU SIN+128 ;Table`of`cosines
  4946.    ;Both`of`these`trig`tables`are
  4947.    ;currently`set`up`from`BASIC
  4948. ZDIV EQU COS+128 ;Division`table
  4949. TMATH1 EQU ZDIV+384 ;Math`table`of`f(x)=x*x/256
  4950. TMATH2 EQU TMATH1+512 ;Second`math`table
  4951. POLYLIST EQU TMATH2+512 ;List`of`polygons
  4952.  
  4953. ========================================================================
  4954. Second SID Chip Installation
  4955. Copyright 1988 Mark A. Dickenson
  4956.  
  4957. This information and software is COPYRIGHTED and made available on a 
  4958. SHAREWARE basis.  This file can be freely copied and distributed as long 
  4959. as it is not SOLD.  This information cannot be used to construct and sell a
  4960. hardware device without receiving prior permission from the author. There is
  4961. not a set fee for the use of this information.  Just send in whatever you feel
  4962. the information is worth.
  4963.  
  4964. If you have any gripes, complaints, suggestions, COMPLIMENTS or DONATIONS of
  4965. any sort please send them to:
  4966.  
  4967.  Mark Dickenson
  4968.  600 South West Street
  4969.  Nevada, Missouri  64772
  4970.  
  4971. Adding an extra SID 6581/6582 chip
  4972.  
  4973. This is not a project to be tackled by the sqeamish or people who are deathly
  4974. afraid of opening their computer just to take a peek inside.
  4975.  
  4976. Now let's get rid of the nasty stuff first.  No liability is assumed with
  4977. respect to the use of the following information.  In other words if you
  4978. screw-up trying to install this modification, then it's your responsability.
  4979.  
  4980.   YOU DO THIS AT YOUR OWN RISK!!!!
  4981.  
  4982. If you do not feel up to it PLEASE take it to a Commodore repair center 
  4983. or a repair service that can work on computers and let them do the 
  4984. installation.  I will warn you that most Commodore Repair Centers will not or
  4985. do not like to do this modification.  When they do, it can be expensive. If
  4986. you belong to a Users Group, tell them about the project and ask if there is
  4987. anyone there that could perform the operation.  This modification will NOT
  4988. hurt the computer in any way, unless it is installed WRONG.
  4989.  
  4990. You can make your own piggy back board or you can do what I am going to
  4991. describe (since it is a little hard to put a schematic in a text file).
  4992.  
  4993. You should ground yourself with a static guard wristband (such as what 
  4994. Radio Shack sells).  Even though the chip is quite durable, just the right
  4995. static discharge can ruin all or part of the SID chip.
  4996.  
  4997. For those of you that are not familier with the way pins are numbered on an
  4998. IC chip here is a short explanation.  On one end of the IC you should find a
  4999. little notch, looking at the chip with the notch at the top the numbering goes
  5000. this way.  The upper left corner of the chip is pin 1 and they are numbered
  5001. consecutively, counter-clockwise around the chip. Some chips do not have a
  5002. notch in one end, but instead dot is placed in one of the chip corners to
  5003. designate that pin 1 starts in that location.
  5004.  
  5005.             notch
  5006.           ----,,----
  5007.         1-!.       !-8
  5008.         2-! dot    !-7
  5009.         3-!        !-6
  5010.         4-!        !-5
  5011.           ----------
  5012.  
  5013.  
  5014. I have included the information that is needed to install this modification
  5015. on the Commodore 64, 64C and 128.  I haven't been able to look inside the
  5016. 128D, so I cannot provide the information with any accuracy.
  5017.  
  5018. There are TWO different 64C circuit boards and both use DIFFERENT SID 
  5019. chips.  You can tell the difference by opening the 64C.  If you see a 64-pin
  5020. chip on the board and the board is only 5.5-6 inches wide then you have the
  5021. narrow board 64C and must use the 9 volt 6582 SID chip.  The number of the
  5022. chip in the 64C narrow is an 8520 and is the same as the 6582.
  5023.  
  5024. ----------------------------------
  5025.  
  5026. Parts Commodore 64, 64C (wide) & 128
  5027.  
  5028. 1 - 6581 SID chip from Jamco or Kassara Microsystems
  5029. 1 - 2N2222 transistor  Radio Shack 276-1617
  5030. 2 - 220pf capacitors  Radio Shack 272-124
  5031.  
  5032. -----------------------------------
  5033.  
  5034. Parts Commodore 64C Narrow Board
  5035.  
  5036. 1 - 6582 SID Chip  From Jamco or Kassara Microsystems
  5037. 1 - 2222A transistor  Radio Shack 276-2009
  5038. 2 - .022uf capacitors  Radio Shack 272-1066
  5039. 2 - 1k ohm 1/4 watt resistors  Radio Shack 271-1321
  5040.  
  5041. -----------------------------------
  5042.  
  5043. Parts 64, 64C (all) & 128
  5044.  
  5045. 2 - 1k ohm 1/4 watt resistors  Radio Shack 271-1321
  5046. 1 - 1000 pf capacitor  Radio Shack 272-126 listed as .001 mf this is 
  5047.     the same as 1000pf
  5048. 1 - 10k ohm 1/4 watt resistor  Radio Shack 271-1335
  5049. 1 - 10 uf electrolitic capacitor  Radio Shack 272-1025
  5050. 1 - 5 inch length of wire
  5051. 1 - 5 inch length of shielded cable
  5052. 1 - surface mount female RCA plug (this is what you normally find on the back
  5053.     of your stereo.
  5054.  
  5055.  
  5056. On the C-64 and 64C (wide) the SID is IC U18 (the IC number will be marked in
  5057. white on the circuit board).  It is usually located in the middle of the
  5058. circuit board, next to the metal video chip case or up between and just
  5059. below the serial and monitor jacks.
  5060.  
  5061. On the C-64C (narrow board) the SID chip is IC U9.  It is located in the
  5062. middle of the board, just a little to the right of center) and called 520.
  5063.  
  5064. On the C-128 the SID is IC U5.  It is located at the back of the circuit 
  5065. board just to the right of the metal housing for the 40 and 80 column video
  5066. chips.
  5067.  
  5068. First bend out pins 23, 24 and 26 and cut them off of the 6581/6582 SID 
  5069. chip.  These are for the two analog and one audio input lines.  They will
  5070. cause problems if connected and since they will not be used it is best to
  5071. remove them.
  5072.  
  5073. Now bend out pins 1, 2, 3, 4, 8, and 27.
  5074.  
  5075. Solder one of the 220pf capacitors (64C narrow uses .022 uf) to pins 1 
  5076. and 2 then solder the other 220pf (64C narrow - .022uf) capacitor to pins 3
  5077. and 4.  The capacitors control the upper and lower frequency range and
  5078. filters of the SID chip.
  5079.  
  5080. The reason I am using 220pf capacitors is because of problems with the 
  5081. filters in the SID chip.  The C-64 first came out with 2200pf capacitors, but
  5082. they were changed to 470pf.  The reason for this was because the filters of
  5083. the SID vary from chip to chip and using 2200pf caused a lot of them to sound
  5084. muffeled when the filters were on.  I have found that by lowering the
  5085. capacitor value to 220 pf helps even more.  If you wish, you can use 470s if
  5086. you feel it would be better, but DO NOT use 2200pf.
  5087.  
  5088. The 6582 SID chip for the 64C narrow must use the .022uf capacitors, as the
  5089. filter range is much different.
  5090.  
  5091. Solder one end of your wire to pin 8 of the SID chip.  This is for the chip
  5092. select line.  We will connect this to the cartridge port.  This tells the
  5093. computer where in memory the chip resides (described later).
  5094.  
  5095. Now solder the remaining pins (excluding the ones we have bent out 
  5096. and/or removed 1, 2, 3, 4, 8, 23, 24, 26 and 27) to the sid chip currently in
  5097. your computer.  You may have to bend those pins inward just a little for them
  5098. to get a good grip on the SID chip.  Be very careful not leave the soldering
  5099. iron on the chip TOO long as you could ruin BOTH SID chips.  I would put some
  5100. heat sink (silicon grease) between the two chips before soldering them
  5101. together.  This will provide better heat dispersal on the bottom chip.
  5102.  
  5103. Now that you have the chips soldered together (place the SID chips back in 
  5104. the socket if you removed them), solder the wire from pin 8 (on the SID chip)
  5105. to pin 7 of the cartridge port on the back of the computer.  Set the computer
  5106. infront of you like to are getting ready to type, with the back of the
  5107. computer away from you.  Look at the cartridge port (located in the upper
  5108. right corner of the circuit board).  You will see two rows of pins connecting
  5109. the cartridge port to the circuit board.  You want the row of pins closest to
  5110. the front of the computer.  Now, count the pins starting at the LEFT side and
  5111. counting to the right.  You want to solder the wire from pin 8 of the extra
  5112. SID chip to pin number 7 of the cartridge port. This is the same place on all
  5113. of the models C-64, 64C and 128.
  5114.  
  5115. This will tell the computer that the extra SID chip is at address $DE00 hex
  5116. or 56832 decimal.  You will access it just like you would the regular sid
  5117. chip but starting at this address.
  5118.  
  5119. I am no longer describing how to connect for address $DF00.  This 
  5120. address causes problems with the RAM Expansion Units and numerous other 
  5121. cartridges.  From now on address $DE00 is the ONLY address for the SID chip.
  5122.  
  5123. Now partially reassemble your computer (be careful that nothing shorts out 
  5124. the pins still sticking out).  Turn the computer on and load the player 
  5125. program provided and tell it to load in 'TEST'.  If you get sound then so far
  5126.  so good.  Turn off the computer and disassemble the case.
  5127.  
  5128. Drill a hole in the back end of the computer just large enough to anchor 
  5129. the RCA plug.  Then solder the center wire of the shielded cable to the 
  5130. center post of the RCA plug.  Insert the wire through the hole you have 
  5131. just drilled and anchor the plug to the case.  Now solder the ground wire to 
  5132. the ground tab on the RCA plug.
  5133.  
  5134. Here comes the difficult part to explain.  This is the coupling circuit 
  5135. for the audio output.  Here is a rough schematic.
  5136.  
  5137.  
  5138. Pin 27 on             12volts dc, 
  5139. 9volts 64C (narrow)
  5140. SID chip  resistor
  5141.     !--.  10k ohm      !collector
  5142. 27!----.--/!/!/--.-----O 2n2222 or 2222A
  5143. --'    !         !     !emitter
  5144.        !         !     !
  5145.        <resistor !     !
  5146.        >1k       !     ! +
  5147.  
  5148. ===========================================================================
  5149. SOLVING LARGE SYSTEMS OF LINEAR EQUATIONS ON A C64 WITHOUT MEMORY
  5150. by Alan Jones  (alan.jones@qcs.org)
  5151.  
  5152. OK, now that I have your attention, I lied.  You can't solve dense
  5153. linear systems of equations by direct methods without using memory to
  5154. store the problem data.  However, I'll come back to this memory free
  5155. assertion later.  The main purpose of this article is to rescue a
  5156. usefull numerical algorithm, "Quartersolve", and also to provide a brief
  5157. look at the COMAL programming language and BLAS routines.
  5158.  
  5159. Linear systems of equations, A(,)*x()=b(), where A is a square matrix
  5160. and x and b are vectors (or arrays), must often be solved for x in the
  5161. solution of a variety of problems.  The size or dimension of the problem
  5162. is n and just storing A requires 5*n*n bytes of memory, assuming C64/128
  5163. 5 byte real variables.  The prefered solution method is a form of
  5164. Gaussian Elimination which requires 1/3 n*n*n multiplications.  I'll
  5165. ignore the additional n*n and n multiplies.  For large problems our C64
  5166. has two serious limitations, small memory size and slow floating point
  5167. arithmetic.  Problems with n=10 can be computed easily.  Problems with
  5168. n=100 will require 100 times more memory and 1000 times more computing
  5169. time.  The computing time is not a real problem.  I don't mind letting
  5170. my computer run while I watch a movie, sleep, or go on a trip.
  5171. Calculating or setting up the problem may take much longer than its
  5172. solution anyway.  Available memory is the practical limiting factor.
  5173. After we use up available RAM we have to resort to other algorithms that
  5174. will use the disk drive to move data in and out of the computer.  The
  5175. 1541 drive is particularly slow and I would not want to subject it to
  5176. undue wear and tear.
  5177.  
  5178. How big a problem do we need to be able to solve?  In many cases the
  5179. problem itself will fix n and there is no way to reduce it.  In other
  5180. cases you might be modeling a real continuous problem with a discrete
  5181. number of elements.  N should be infinity but for problem solution n=50
  5182. might be big enough.  Consider calculating the aerodynamic potential
  5183. flowfield around a body of revolution.  You could fix points on the
  5184. surface of the body (a meridian) and have a series of sort line segments
  5185. make up elements to approximate the shape.  The lager n is the closer
  5186. the smooth shape is aproximated and the more accurate the computed
  5187. solution becomes.  n=100 might be a good choice for a simple shape.  We
  5188. could also use a "higher order" menthod.  In this case we can substitute
  5189. a curved line element for the straight line segment.  Calculating the
  5190. matrix elements will be more difficult but n=40 curved elements might
  5191. give a more accurate solution than 100 flat elements.  Another
  5192. consideration is the resolution of the solution.  You might want to plot
  5193. the solution on the 200 x 320 pixel hi-res C64 screen.  40 points might
  5194. be too coarse and 320 might be overkill.  We might also need to
  5195. calculate the slope or derivatives from the calculated solution which
  5196. will require more closely spaced solution points.  There are often
  5197. choices that you can make in modeling a system and selecting a solution
  5198. algorithm so that a problem can be solved within the limits of a C64.
  5199. There are often interesting tradeoffs in memory requirements and
  5200. execution speed.
  5201.  
  5202. How big a problem can we solve with a C64?  Using Quartersolve with
  5203. assembly language we can probably do n=200 or more.  If we are going to
  5204. store the problem data on a single 1541 diskette and read it in a row at
  5205. time we can only do n=182 or so.  Actually I think n should be well
  5206. under 100.  Different operating systems and languages limit the amount
  5207. of useable RAM; BASIC 40K, COMAL 2.0 30K, GEOS 23K, the initial disk
  5208. loaded COMAL 0.14 10K...  Solving a linear system may only be a small
  5209. subproblem inside a large application program.  The idea is to be able
  5210. to solve reasonable sized problems using your prefered  computing
  5211. environment without having to do a lot of chaining or loading of
  5212. separate programs.  Quartersolve can free up a lot of memory for other
  5213. routines or allow n to be doubled.
  5214.  
  5215. SPEED
  5216.  
  5217. There are a few things that we can do to speed up the calculations.
  5218. First we can select a fast programming language.  I prefere COMAL 2.0
  5219. which is a fast three pass interpreter.  Using an assembler could be the
  5220. fastest and provide the most useable memory.  A true compiler such as C
  5221. or Pascal could also be a good choice.  BASIC is a poor choice except
  5222. that it is built in and free.  In most cases execution can be sped up
  5223. with some machine language routines like the BLAS (Basic Linear Algebra
  5224. Subroutines).  Calculation speed is measured in FLOPS/sec  (Floating
  5225. Point OPerationS) where, c(i#,j#):=c(i#,j#) + a(i#,k#)*b(k#,j#) is the
  5226. operation.  It is one FP multiply, one FP add, and some indexing
  5227. overhead.  With some interpreters the indexing and interpreting overhead
  5228. can far exceed the actual FP multiply time.  With assembled code the FP
  5229. multiply time should dominate.  I use a ML level 1 BLAS package with
  5230. COMAL 2.0.  For example:
  5231.  
  5232.     c(i#,J#):+sdot(n#,a(i#,1),1,b(1,j#),sdb#)
  5233.     FOR k#:=1 to n# do c(i#,j#):+a(i#,k#)*b(k#,j#)
  5234.  
  5235. both calculate the same thing, a dot product with n# FLOPS.  For large
  5236. n# on a C64 the BLAS approach about 320 FLOPS/sec., The overhead of
  5237. calling the procedure from the interpreter is about the equivalent of 4
  5238. FLOPS.  Of course modern computer performance is measured in
  5239. MegaFLOPS/sec. with 8 byte reals (super computers run hundreds or
  5240. thousands of MFLOPS/sec.).  They also inflate the performance by
  5241. counting the multiply and add as two FLOPS.  In his article I use the
  5242. "old flops" or number of multiplies.
  5243.  
  5244. It may also be possible to code 6502 FP arithmetic routines using lookup
  5245. tables that may perform faster than the built in routines.  We could
  5246. also use the CPU in the disk drives to do distributed processing.  But
  5247. this is beyond the scope of this article.
  5248.  
  5249. SOLUTION METHODS
  5250.  
  5251. Consider the following choices for numerical solution algorithms:
  5252.  
  5253. METHOD                  MEMORY       FLOPS
  5254. Gaussian Elimination       n*n     1/3 n*n*n
  5255. Cholesky Decomposition 1/2 n*n     1/6 n*n*n
  5256. QR decomposition           n*n     2/3 n*n*n
  5257. QR updating            1/2 n*n      2  n*n*n
  5258. Gauss-Jordan               n*n     1/2 n*n*n
  5259. Quartersolve           1/4 n*n     1/2 n*n*n
  5260.  
  5261.  
  5262. Gaussian Elimination is the prefered method when enough memory is
  5263. available.  In modern terminology this is LU decomposition where A is
  5264. decomposed or factored into a lower triangular matrix and an upper
  5265. triangular matrix.  Partial pivoting of rows or columns is an additional
  5266. complication often required for a stable solution.  After the LU
  5267. decompostion you can readily solve for any number of right hand side
  5268. vectors in n*n flops each.  In addition you can calculate matrix
  5269. condition number estimates and use iterative improvement techniques.
  5270. The LU decomposition is done in place overwriting the problem matrix A.
  5271.  
  5272. Cholesky Decomposition is a specialized version of Gaussian Elimination
  5273. for symetric positive definite matrices only.  Since A is symetric we
  5274. only need n*(n+1)/2 memory storage locations.  The L and U triangular
  5275. matrices are simply transposes of the other so only one needs to be
  5276. stored and is computed in place overwriting the original storage used
  5277. for A.  No pivoting is required.  This algorithm cannot solve general
  5278. nonsymetric problems and is included only for comparison.
  5279.  
  5280. QR decomposition factors A into an orthogonal matrix Q and a triangular
  5281. matrix R.  QR decomposition is very stable and can be performed without
  5282. pivoting.  Since Q is orthogonal its inverse is just Q transpose.  To
  5283. solve the linear system we multiply the right hand side vector by Q
  5284. transpose then solve the triangular system R.  Q is computed in a
  5285. special compact form and stored in the space below R.  The decomposition
  5286. is done in place in the storage used for A, plus an additional n storage
  5287. locations.  QR decomposition requires about twice as many flops as
  5288. Gaussian Elimination.
  5289.  
  5290. There is a variation of the QR solution known as QR updating.  The
  5291. problem is solved a row at a time.  A Row of A can be read in from disk
  5292. storage or calculated as needed.  Only R needs to be stored in main
  5293. memory, n*(n+1)/2 memory locations.  R is initialy the identity matrix
  5294. and is updated as each row of A and its right hand side element are
  5295. processed.  Q is not stored, but the right hand side vector is
  5296. sequentialy multiplied by Q transpose.  After all n rows have been
  5297. processed the solution is found by simply solving the triangular system
  5298. R.  Since this method only needs half as much memory storage as LU
  5299. decomposition, we can solve problems 40% larger in a limited memory
  5300. space.  However, the cost in flops is high.  Actually QR updating is
  5301. best used for solving large overdetermined least squares problems.
  5302.  
  5303. Gauss-Jordan is a variation of Gaussian Elimination that reduces A to
  5304. the Identity matrix instead of to LU factors.  By applying the same
  5305. transformations to to the right hand side that reduce A to the identity
  5306. matrix, the right hand side becomes the solution at completion.
  5307. Pivoting is requiered.  Gauss-Jordan requires about 50% more flops than
  5308. Gaussian Elimination and most codes use n*n memory storage.  Since the
  5309. LU factors are not computed we can't solve additional right hand side
  5310. vectors later, or estimate the matrix condition number, or use iterative
  5311. improvement techniques.  It will solve multiple right hand sides that
  5312. are available from the start.
  5313.  
  5314. Quartersolve is a clever implementation of Gauss-Jordan(?) that solves
  5315. the problem a row at a time like QR updating but only requires 1/4 n*n
  5316. memory storage.  With fixed available memory Quartersolve can solve a
  5317. problem twice as large as Gaussian Elimination but with a modest
  5318. performance penalty.  Solving a 2n problem with Quartersolve would take
  5319. 12 times longer (instead of 8) than Gaussian Elimination on a size n
  5320. problem.
  5321.  
  5322. My recommendation is to use Gaussian elimination for solving dense
  5323. general systems of linear equations when enough main memory is available
  5324. and switch to Quartersolve for larger problems.  For solving huge
  5325. problems requiering external storage a blocked version of QR
  5326. decomposition might work best.  Cholesky decomposition should be used
  5327. for symetric positive definite problems.  Large problems are often
  5328. sparse, containing lots of zeros that need not be stored.  Specialized
  5329. code exists for solving many sparce problems, particularly banded
  5330. matrices, and many of these methods can be used on a C64.  Codes for
  5331. solving unstructured sparce problems are not very suitable for the C64
  5332. since they are complex and reduce the amount of memory available for
  5333. solving the problem.  However, large sparce problems can also be solved
  5334. on the C64 by iterative methods such as Gauss-Siedel and Conjugate
  5335. Gradient algorithms.
  5336.  
  5337. QUARTERSOLVE
  5338.  
  5339. Quartersolve is a useful method for solving general dense systems of
  5340. linear equations that I discovered almost by accident while doing random
  5341. research in the library.  I have not seen any recent texts or papers
  5342. mentioning this algorithm.  I have not seen any reference to it in the
  5343. C64 literature either.  At least one older text mentioned it in passing
  5344. saying that the code was too long or complex.  This is a valid point
  5345. since usualy the code size directly subtracts from the problem storage.
  5346. The code is longer than the Gaussian Elimination code but in my
  5347. implementation it only takes about 2K of main memory storage and it is a
  5348. real advantage on the C64.  With a C64 we can also put the entire code
  5349. in an EPROM on a cartridge so the code size is of little concern.
  5350.  
  5351. I found Quartersolve in Ref. 1 (R. A. Zamberdino, 1974), which credited
  5352. the algorithm to Ref. 2 (A. Orden, 1960).  I am a little uneasy
  5353. describing the algorithm since I have not seen Ref. 2 or analyzed the
  5354. algorithm.  I have coded the algorithm, tested it, and used it to solve
  5355. some large problems on a C64, up to n=90.  Zambardino makes two
  5356. interesting statements in Ref 1.  "The number of arithmetic operations
  5357. is the same as for the Gaussian Elimination method."  I am reasonably
  5358. sure from the description that he meant Gauss-Jordan which requires
  5359. about 50% more arithmetic than Gaussian Elimination.  After processing
  5360. the ith row only i(n-i) storage locations are required to store the
  5361. reduced matrix.  Max[i(n-i)] = n*n/4.  This maximum memory requirement
  5362. occurs at i = n/2.  As i increases further memory required is reduced.
  5363. Although n*n/4 memory locations must be allocated and dimensioned in an
  5364. array at the start, Quartersolve always uses the first portion of the
  5365. array continuously and does not free up memory in holes scattered
  5366. throughout the array.  The C language could possibly use "heap storage"
  5367. and release the memory for any other use as the procedure advances.
  5368.  
  5369. Now back to my initial memory free claim.  The large problem that I
  5370. actually wanted to solve was: A*x=b, B*x=r, for r given b and the square
  5371. matrices A and B.  Elements of A and B are most efficiently calculated
  5372. at the same time.  I could write B to the drive and read it back in
  5373. after x is computed to calculate r, but I actually wanted to solve this
  5374. repeatedly inside another loop and I did not want to read and write to a
  5375. lousy 1541 that much.  Using Gaussian elimination would require 2n*n
  5376. storage.  Using Quartersolve could require 1.25n*n storage.  However,
  5377. only n*n storage is needed, that for B.  At the ith step the ith row of
  5378. A and B are calculated.  The row of A is processed into the the n*n
  5379. dimensioned array B filling it from the front.  The corresponding row of
  5380. B is stored in the same array B filling from from the end of array B.
  5381. As the process continues Quartersolve "dissuses" array B so that rows of
  5382. B never overwrite storage needed by Quartersolve.  At the end we have
  5383. computed x and  all of B is stored in the array B.  Simple
  5384. multiplication produces r.  So I can say with pride, at the expense of
  5385. honesty, that I have solved A*x=b without any additional memory storage
  5386. for A.
  5387.  
  5388. PROC slv(n#,nr#,i#,REF a(),REF c(),REF b(,),sdb#,REF sw#(),REF fail#) CLOSED
  5389.   // This routine solves a system of equations using the quartersolve
  5390.   // algorithm with partial pivoting.
  5391.   // It is called a "line at a time" and uses only
  5392.   // 0.25*nn memory locations which enables larger problems to be solved.
  5393.   // The LU factorization is not available, nor a condition estimate.
  5394.   // n# is the dimension of the problem
  5395.   // nr# is the number of right hand vectors to be solved for.
  5396.   // b(,) is the right hand side columns
  5397.   // sdb# is the second dimension of the array b(,)
  5398.   USE blas
  5399.   USE strings
  5400.   q#:=i#-1; m#:=n#-q#; mm1#:=m#-1; fail#:=TRUE; ip1#:=i#+1
  5401.   IF i#=1 THEN //initialize pivot array
  5402.     FOR j#:=1 TO n# DO sw#(j#):=j#
  5403.   ENDIF 
  5404.   FOR j#:=1 TO q# DO //adjust for previous pivoting
  5405.     k#:=sw#(j#)
  5406.     WHILE k#<j# DO k#:=sw#(k#)
  5407.     IF k#>j# THEN swap'real(c(j#),c(k#))
  5408.   ENDFOR j#
  5409.   FOR j#:=i# TO n# DO c(j#):-sdot(q#,c(1),1,a(j#-q#),m#)
  5410.   p#:=q#+isamax#(m#,c(i#),1)
  5411.   r:=ABS(c(p#))
  5412.   IF r=0 THEN RETURN
  5413.   fail#:=FALSE
  5414.   IF p#<>i# THEN
  5415.     swap'real(c(i#),c(p#))
  5416.     swap'integer(sw#(i#),sw#(p#))
  5417.     sswap(q#,a(1),m#,a(p#-q#),m#)
  5418.   ENDIF 
  5419.   r:=1/c(i#)
  5420.   IF mm1#<>0 THEN sscal(mm1#,r,c(ip1#),1)
  5421.   FOR j#:=1 TO nr# DO b(i#,j#):=r*(b(i#,j#)-sdot(q#,c(1),1,b(1,j#),sdb#))
  5422.   FOR k#:=1 TO nr# DO saxpy(q#,-b(i#,k#),a(1),m#,b(1,k#),sdb#)
  5423.   IF mm1#>0 THEN
  5424.     t#:=1
  5425.     FOR j#:=1 TO q# DO
  5426.       r:=a(t#); t#:+1
  5427.       scopy(mm1#,a(t#),1,a(t#-j#),1)
  5428.       saxpy(mm1#,-r,c(ip1#),1,a(t#-j#),1)
  5429.       t#:+mm1#
  5430.     ENDFOR j#
  5431.     scopy(mm1#,c(ip1#),1,a(mm1#*q#+1),1)
  5432.   ELSE //unscramble solution from pivoting
  5433.     FOR j#:=1 TO nr# DO
  5434.       FOR k#:=1 TO n# DO c(sw#(k#)):=b(k#,j#)
  5435.       scopy(n#,c(1),1,b(1,j#),sdb#)
  5436.     ENDFOR j#
  5437.   ENDIF 
  5438. ENDPROC slv
  5439. //
  5440. n#:=8; sdrh#:=1; nrh#:=1; nr#:=1
  5441. // a is of  dimension n*n/4
  5442. DIM a(16), b(n#), rhs(n#,nrh#), sw#(n#)
  5443. FOR i#:=1 TO n# DO
  5444.   FOR j#:=1 TO n# DO b(j#):=2297295/(i#+j#-1)
  5445.   s:=0
  5446.   FOR j#:=n# TO 1 STEP -1 DO s:+b(j#)
  5447.   rhs(i#,1):=s
  5448.   slv(n#,nr#,i#,a(),b(),rhs(,),sdrh#,sw#(),fail#)
  5449.   IF fail# THEN
  5450.     PRINT "singularity detected at i=";i#
  5451.     STOP 
  5452.   ENDIF 
  5453. ENDFOR i#
  5454. FOR j#:=1 TO n# DO PRINT rhs(j#,1);
  5455. END 
  5456.  
  5457. The Quartersolve algorithm is presented here as a COMAL 2.0 procedure
  5458. "slv".  COMAL is pretty much a dead language and I don't expect anyone
  5459. to run this code.  However, COMAL is a structured algorithmic language
  5460. that is easy to read.  You can readily translate it into the programming
  5461. language of your choice.  Slv is coded as a CLOSED procedure as a
  5462. personal matter of style.  An open procedure would execute faster.  The
  5463. arrays are passed by REFERENCE and do not allocate additional local
  5464. storage.  The main program is just an example for testing.  It calls slv
  5465. n times to solve the linear system.  The test problem solves a scaled
  5466. Hilbert matrix which is ill conditioned.  In the absence of roundoff
  5467. error the solution should be a vector of ones.  I usually dimension a()
  5468. to n#*(n#+1)/4.  Slv is presented in its full generality, but you may
  5469. want to make some simplifications.
  5470.  
  5471. Slv can handle multiple right hand side vectors with the two dimensional
  5472. array b(,) in most applications you will only use a single vector,
  5473. nr#=1, and you can make some simplifications by just using a one
  5474. dimensional array.
  5475.  
  5476. Pivoting also complicates the code.  Problems which are positive
  5477. definite, or  diagonally dominant, or sometimes just well conditioned
  5478. can be safely solved without pivoting.  Stripping out the pivoting code
  5479. is straight forward and will shorten the code and speed execution.
  5480.  
  5481. Anything following // is a comment and can be deleted from your running
  5482. code.
  5483.  
  5484. In COMAL 2.0 you can also "protect" the code which will strip out
  5485. comments and other information to make a shorter running version.
  5486.  
  5487. The remaining discussion will concern COMAL 2.0 and the BLAS.
  5488.  
  5489. COMAL 2.0
  5490.  
  5491. COMAL 2.0 is an excellent programming language for the C64/128 and I
  5492. can't describe all of its virtues here.  It has one serious limitation.
  5493. It does not use "continuation" lines, so line length is limited.  This
  5494. is most restrictive in function and procedure lines where it limits the
  5495. number of parameters that can be passed.  Line length is limited to 80
  5496. characters.  However, if you use a separate text editor or word
  5497. processor you can enter 120 character lines.  Comal will actually
  5498. execute tokenized lines up to 256 characters so the limitation is really
  5499. in the editor rather than COMAL.  Procedure and variable names can be
  5500. quite long in Comal, but are kept short because of the line length
  5501. limitation.  "Quartersolve" was shortened to "slv" for this reason.
  5502.  
  5503. a:+t is a shorter faster version a:=a+t, and a:-t is a shorter faster
  5504. version of a:=a-t.  This is most usefull when "a" is an array element or
  5505. an integer.
  5506.  
  5507. Comal 2.0 supports ML packages.  A package is a collection of functions
  5508. or procedures that can be called and executed.  A packaged can be ROMMED
  5509. and stored in EPROM on the Comal 2.0 cartridge.  A package can also be
  5510. loaded from disk and will normally be stored in a RAM location that
  5511. COMAL does not use for normal programs.  LINK "filename" will load and
  5512. link the ML package to a Comal program.  It will stay attached to the
  5513. program when the program is saved and loaded, unless it is marked as
  5514. ROMMED.  The entire slv procedure could be coded in assembly language
  5515. and be placed in a package.  The slv procedure uses two packages,
  5516. strings and blas.  The command USE packagename makes all of the
  5517. functions and procedures of the package known.  Alternatively, you could
  5518. place the USE packagename command in the main program and put IMPORT
  5519. procedurename inside all of the closed procedures that call
  5520. procedurename.
  5521.  
  5522. Slv calls the swap'real and swap'integer proocedures from the strings
  5523. package.  The strings package is a ROMMED package on the Super Chip ROM.
  5524.  
  5525. It does exactly what it says, e.g.  swap'real(a,b) is the same as:
  5526. t:=a; a:=b; b:=t.
  5527.  
  5528. Slv calls the sdot, isamax#, sswap, sscal, saxpy, and scopy routines
  5529. from the blas package.  The blas package is LINKed to the program, but
  5530. it could, and should, be placed on EPROM.
  5531.  
  5532. Basic Linear Algebra Subroutines, BLAS
  5533.  
  5534. The BLAS were originally written for the Fortran language to speed
  5535. execution and streamline code used for solving linear algebra and other
  5536. matrix problems.  The LINPACK routines, Ref. 3, use the BLAS and are
  5537. perhaps the best known.  The idea is that the BLAS routines will be
  5538. highly optimized for a particular computer, coded in ML or a High Order
  5539. Language.  Some operating systems even include BLAS like routines.
  5540. Writing fast efficient programs is then a simple matter of selecting the
  5541. best solution algorithm and coding it in a manner that makes best use of
  5542. the blas routines.  There are blas routines for single precision, double
  5543. precision, and complex numbers.  The level 1 BLAS perform operations on
  5544. rows or columns of an array and typicaly do n scalar operations
  5545. replacing the inner most loop of code.  There are also level 2 BLAS that
  5546. perform n*n operations and Level 3 BLAS that perform n*n*n operations.
  5547. Nicholas Higham has coded most of the single precision level 1 blas
  5548. routines and put them in a Comal 2.0 package.  The Comal blas package is
  5549. included on the Packages Library Volume 2 disk.  I am not aware of ML
  5550. blas routines coded for any other C64/128 languages although this is
  5551. certainly possible and recommended.
  5552.  
  5553. The Comal blas routines behave exactly the same way that the Fortran
  5554. blas routines do except that Fortran can pass the starting address of an
  5555. array with just "a", while Comal requires "a(1)".  The Comal blas will
  5556. allow you pass an array, by reference, of single or multiple dimensions
  5557. and start from any position in the array.  If you code the blas routines
  5558. as ordinary Comal routines you have to pass additional parameters and
  5559. have separate routines for single dimensioned arrays and two dimensional
  5560. arrays.  Note also that Fortran stores two dimensional arrays by
  5561. columns, and Comal (like many other languages) stores two dimensional
  5562. arrays by rows.  If you translate code between Fortran and Comal using
  5563. blas routines you will have to change the increment variables.
  5564.  
  5565.             Fortran                          Comal
  5566.     dimension c(n), a(ilda,isda)     DIM c(n#), a(lda#,sda#)
  5567.     scopy(n,c,1,a(i,1),ilda)         scopy(n#,c(1),1,a(i#,1),1)
  5568.     scopy(n,c,1,a(1,j),1)            scopy(n#,c(1),1,a(1,j#),sda#)
  5569.  
  5570. The first scopy copies array c into the ith row of array a.  The second
  5571. scopy copies array c into the jth column of array a.
  5572.  
  5573. This is what scopy does in Fortran:
  5574.  
  5575.     subroutine scopy(n,sx,incx,sy,incy)
  5576.     real sx(1),sy(1)
  5577.     ix=1
  5578.     iy=1
  5579.     do 10 i = 1,n
  5580.       sy(iy) = sx(ix)
  5581.       ix = ix + incx
  5582.       iy = iy + incy
  5583.  10 continue
  5584.     return
  5585.     end
  5586.  
  5587. The Comal BLAS does exactly the same thing.  If coded entirely in COMAL
  5588. rather than as a package it would have to be different.  The call would
  5589. change.
  5590.  
  5591. scopy(n#,c(1),1,a(1,j#),sda#) would have to become,
  5592. scopy(n#,c(),1,1,a(,),1,j#,sda#,sda#) and the Comal procedure might be:
  5593.  
  5594. PROC scopy(n#, REF x(), ix#, incx#, REF y(,), iy#, jy#, sdy#, incy#) CLOSED
  5595.   iyinc#:=incy# DIV sdy#  //assuming y is dimensioned y(?,sdy#)
  5596.   jyinc#:=incy# MOD sdy#
  5597.   FOR i#=1 TO n# DO
  5598.     y(iy#,jy#):=x(ix#)
  5599.     ix#:+incx#; iy#:+iyinc#; jy#:+jyinc#
  5600.   ENDFOR
  5601. ENDPROC scopy
  5602.  
  5603. Note that more information has to be passed to the procedure and used
  5604. that the ML blas picks up automatically.  Also we would need separate
  5605. procedures to handle every combination of single and multi dimensional
  5606. arrays.  The Comal ML blas are indeed wonderful.  For speed
  5607. considerations this should also be left as an open procedure or better
  5608. yet just use in line code.
  5609.  
  5610. Here is a very simplified description of what each of the routines in
  5611. the Comal BLAS package does.
  5612.  
  5613. sum:=sasum(n#,x(1),1)  Returns sum of absolute values in x().
  5614.   sum:=0
  5615.   FOR i#:=1 TO n# DO sum:+ABS(x(i#))
  5616.  
  5617. saxpy(n#,sa,x(1),1,y(1),1)  Add a multiple of x() to y().
  5618.   FOR i#:=1 TO n# DO y(i#):+sa*x(i#)
  5619.  
  5620. prod:=sdot(n#,x(1),1,y(1),1)  Returns dot product of x() and y().
  5621.   prod:=0
  5622.   FOR i#:=1 TO n# DO prod:+x(i#)*y(i#)
  5623.  
  5624. sswap(n#,x(1),1,y(1),1)  Swaps x() and y().
  5625.   FOR i#:=1 TO n# DO t:=x(i#); x(i#):=y(i#); y(i#):=t
  5626.  
  5627. scopy(n#,x(1),1,y(1),1)  Copy x() to y().
  5628.   For i#:=1 TO n# DO y(i#):=x(i#)
  5629.  
  5630. max#:=isamax#(n,x(1),1)  Returns index of the element of x() with the
  5631.                          largest absolute value.
  5632.   t:=0; max#:=1
  5633.   FOR i#:=1 TO n#
  5634.     IF ABS(x(i#))>t THEN t:=ABS(x(i#)); max#:=i#
  5635.   ENDFOR i#
  5636.  
  5637. sscal(n#,sa,x(1),1)  Scale x() by a constant sa.
  5638.   FOR i#:=1 TO n# DO x(i#):=sa*x(i#)
  5639.  
  5640. snrm2(n#,x(1),1)  Returns the 2 norm of x().
  5641.   norm2:=0
  5642.   FOR i#:=1 TO n# DO norm2:+x(i#)*x(i#)
  5643.   norm2:=SQR(norm2)
  5644.  
  5645. srot(n#,x(1),1,y(1),1,c,s)  Apply Givens rotation.
  5646.   FOR i#:=1 TO n# DO
  5647.     t:=c*x(i#) + s*y(i#)
  5648.     y(i#):=s*x(i#) + c*y(i#)
  5649.     x(i#):=t
  5650.   ENDFOR i#
  5651.  
  5652.  
  5653. Bear in mind that each of these simple examples can be more complex as
  5654. was given for scopy.  You now have enough information to write your own
  5655. BLAS routines in ML or the programming language of your choice, or to
  5656. expand the BLAS routine calls in slv to ordinary in line code.
  5657.  
  5658. You can also apply the BLAS routines in creative ways besides just
  5659. operating on rows or columns.  For example you could create the identity
  5660. matrix with:
  5661.  
  5662.   DIM a(n#,n#)
  5663.   a(1,1):=1; a(1,2):=0
  5664.   scopy(n#*n#-2,a(1,2),0,a(1,3),1) // zero the rest of the matrix
  5665.   scopy(n#-1,a(1,1),0,a(2,2),n#+1) // copy ones to the diagonal.
  5666.  
  5667. References
  5668.  
  5669. 1.  Zambardino, R. A., "Solutions of Systems of Linear Equations with
  5670. Partial Pivoting and Reduced Storage Requirements", The Computer Journal
  5671. Vol. 17, No. 4, 1974, pp. 377-378.
  5672.  
  5673. 2.  Orden A., "Matrix Inversion and Related Topics by Direct Methods",
  5674. in Mathematical Methods for Digital Computers, Vol. 1, Edited by A.
  5675. Ralston and H. Wilf, John Wiley and Sons Inc.,  1960.
  5676.  
  5677. 3.  Dongarra, J. J., Moeler, C. B., Bunch, J. R., Stewart, G. W.,
  5678. Linpack Users' Guide, SIAM Press, Philadelphia, 1979.
  5679.  
  5680. ========================================================================
  5681. The World of IRC - A New Life for the C64/128
  5682. by Bill Lueck (coolhand on IRC)
  5683.  
  5684. 1)  Introduction
  5685.  
  5686. With the mysterious and magnificent world of the Internet growing
  5687. at an astounding rate - like doubling every year - readers of this
  5688. magazine should find that the Internet is actually available to them now -
  5689. or at least very soon.  In fact, most readers of C= Hacking probably
  5690. get there copies of this magazine on the Internet.
  5691.  
  5692. The Internet is not simple.  It has complexities and intricacies that
  5693. can baffle the most erudite and experienced computer scientists in the
  5694. world.  But, for the purposes of this article, maybe you can just accept
  5695. that the Internet is a worldwide connection of data lines that let
  5696. computers all over the world talk to each other.. and more importantly,
  5697. that allow the PEOPLE using the computers all over the world to talk to
  5698. other computers.. and to talk to other PEOPLE!  Here, then, lies the
  5699. foundation for IRC: it is the mechanism on the Internet that allows
  5700. PEOPLE to talk to other PEOPLE.
  5701.  
  5702. 2)  Getting on the Net
  5703.  
  5704. If you obtained this magazine via the Internet, then you have passed
  5705. Step 1 (finding a site)!  If you do not have access to the Internet
  5706. (and have not tried), then you need to look around.  Possible sites may
  5707. be a college/university, your employer (use with care), or a commercial
  5708. provider.
  5709.  
  5710. If you are enrolled in college, then you probably have an account, or
  5711. you may be entitled to one, with no or little cost.  The policies on
  5712. student accounts vary a lot from institution to institution, and from
  5713. country to country.  But check into it.. it is one of the most common
  5714. methods of Internet access.
  5715.  
  5716. If you are employed, and your company has access to the Internet, it
  5717. may be possible for you to use their facilities.  Just a word of
  5718. caution - make sure that it is ok with your employer to use his
  5719. facilities... and not on "company time".
  5720.  
  5721. Another way that is becoming increasingly more common is to use
  5722. commercial "Internet providers".  These are companies whose sole
  5723. purpose is to offer you an "account" and give you access to the
  5724. Internet.  The cost, time on line, storage, access, etc., can vary
  5725. greatly..  you must shop around a bit.. if you have this choice at
  5726. all.. for the best deal.
  5727.  
  5728. These commercial sites are not always easy to find.  There may be
  5729. several commercial providers in an area, but, strangely, they tend not
  5730. to advertise.  Word-of-mouth through friends, BBSs, or User Groups seem
  5731. to be the best way to locate the site possibilities.  But they CAN
  5732. provide a very good solution.
  5733.  
  5734. Another variation on commercial sites are national companies such as
  5735. Compuserve, Genie, America Online and Delphi.  They provide varying degrees of
  5736. access.. and possibly at somewhat higher costs than local providers.
  5737. But, again, it is another option.
  5738.  
  5739. There is MUCH to do on the Internet, once you have access to it: telnet,
  5740. ftp, usenet, archie, gopher, www...  These may be just names to you
  5741. now..  but the are all fascinating parts of the Internet.  But this
  5742. article is intended as an introduction to IRC - a fabulous Internet
  5743. resource which allows users who have access to a client program called
  5744. IRCII (most often invoked as "irc") to talk to each other (and often to
  5745. exchange files) in world-wide conversational "channels" (like "party
  5746. lines", often called "rooms" on some BBSs).  Why is this important to
  5747. readers of this magazine?  Well, there is a channel for c64/128 users on
  5748. IRC called #c-64, a place where c64/128 users are able to meet and
  5749. exchange all sorts of information, opinions, and files.  More on this
  5750. later.
  5751.  
  5752. 3)  The IRC Client
  5753.  
  5754. First, to use IRC it is necessary to have access to an IRC client.  A
  5755. client is a program, usually available on your local site, which
  5756. actually interprets and responds to your commands, accepts your typing,
  5757. and shows you the conversation on the channel(s) you have joined.
  5758.  
  5759. The most common way to access IRC from a site is to use the IRCII
  5760. client that your site makes available.  This is most often done by
  5761. simply typing "irc" at your prompt or invoking the irc option from your
  5762. menu if you don't have a shell account.  The first thing you will
  5763. notice is that your client is attempting to connect to a "server".  A
  5764. server is a special program, run only on certain sites, that actually
  5765. provides the backbone of the IRC network.
  5766.  
  5767. Most sites have several servers pre-defined.  You should see the client
  5768. trying one or more servers until it connects with one.
  5769.  
  5770. With Unix irc clients you can define your own unique set of servers by
  5771. starting IRC with:
  5772.  
  5773. irc nick server1 server2 ... serverN
  5774.  
  5775. where "serverX" is the alpha or numeric IP address of the server.  This
  5776. will automatically set your irc nick (handle) and will establish a
  5777. series of servers that your client will switch to if your connection to
  5778. IRC gets broken (or if a server is not available when you invoke
  5779. "irc").
  5780.  
  5781. What is an IP address, you ask?  Well, a basic premise of the Internet
  5782. is that each computer on the net (at all sites) has a UNIQUE address -
  5783. a computer code - that allows other computers to send specific data
  5784. just to that computer.  In that way, computers can make sure that the
  5785. messages and data files that they want (and YOU want) to send to
  5786. certain places get to their proper destinations.
  5787.  
  5788. IP addresses may be used in an alphabetic or numeric form.  In most
  5789. cases they can be used interchangeably.  So, all irc servers have a
  5790. unique alphabetic (and an equivalent numeric) IP address.
  5791.  
  5792. Once an IRC session is in progress, Unix users can change servers by
  5793. typing:/server newserver where "newserver" is as above, the alpha or
  5794. numeric IP address of the server you want to switch to.  More on servers
  5795. later; but just to mention few now:  irc.indiana.edu (midwest);
  5796. irc.virginia.edu (east); irc.ctr.columbia.edu (east); irc.math.byu.edu
  5797. (west); irc.colorado.edu (midwest); irc.texas.net (southwest).  There
  5798. are dozens more.  Just ask someone on IRC...or do a few  /whois nick
  5799. commands.  You will spot many more.
  5800.  
  5801. If your site does not have an irc client, it should be possible to
  5802. install one yourself.  This means that you need to ftp the source code
  5803. for an irc client to your account on your site, make some usually minor
  5804. edits, then compile the code in your home directory or a subdirectory
  5805. below it.
  5806.  
  5807. One good site for obtaining the necessary irc client code is
  5808. cs.bu.edu.  cd to /irc/clients.  Unix users  will find the IRCII client
  5809. source code in two forms:  IRC2.2.9.tar.Z (Unix tar and compress at
  5810. 471k) or IRC2.2.9.tar.gz (Unix tar and GNU compress at 306k).  Both
  5811. files are the same (except for the compression).  Be sure to use
  5812. "BINARY" mode for the ftp transfer.
  5813.  
  5814. Move the file to its own subdirectory if you have not ftp'd it to one
  5815. already.  Then uncompress and untar the file.  You should now find a
  5816. small subdirectory tree of files.  Be sure and read the INSTALL file in
  5817. the top subdirectory.
  5818.  
  5819. Also in the main subdirectory, there should be two files that need
  5820. editing to make the client work with you site.  One is "Makefile".  In
  5821. it there are at least two edits.  Make INSTALL_EXECUTABLE the path name
  5822. that u want the executable to reside in.  This is most often your home
  5823. directory or the "bin" subdirectory under your home directory.  The
  5824. other is IRCII_LIBRARY.  Set this to the top subdirectory where the
  5825. IRCII code resides.  You also must read through the computer system
  5826. options and set them for the type of computer and Operating System that
  5827. your site uses.
  5828.  
  5829. The other file is "config.h".  Change the #define DEFAULT_SERVER line
  5830. to the alpha or numeric addy of your primary default server.  Be sure
  5831. to enclose the server in quotes ("server").
  5832.  
  5833. For VMS users, there is a subdirectory in "clients" named vms.  cd to
  5834. it.  There are two versions - irc176  and ircII-for-vms.  The first is
  5835. a more native VMS version, the second is a Unix-like version.  They are
  5836. both executables, and should run on VMS systems.  Try both.. see which
  5837. you like best.
  5838.  
  5839. Another fairly new area of IRC clients is the personal client, running
  5840. on your own computer which would be connected to the Internet through a
  5841. version of SLIP or PPP, protocols that move much of the overhead of a
  5842. normal Internet provider down to your own machine.  There are IRC clients
  5843. available for the PC, Amiga, MAC... and even the rumor of one to be
  5844. produced for the c64/128.  This type of client is expanding very
  5845. rapidly and will be a significant option for an ever increasing number
  5846. of Internet users.
  5847.  
  5848. If you have Telnet only access from your site, there are some sites
  5849. which offer a "public" irc client, ones which you can use without
  5850. having an account at that site.. sorta like anonymous ftp for those of
  5851. you who know what that is.  There are drawbacks, though.  There are not
  5852. many of these public clients, they are often slow in response time, you
  5853. cannot exchange files with other users (DCC), and many of the sites are
  5854. not always up.  Still, it is one possibility that might work for
  5855. certain situations.  Actually, it is the way that I started on IRC and
  5856. used it for several months (my site did not have a local client, and I
  5857. did not know how to install one myself).
  5858.  
  5859. The public IRC sites I know about now are tiger.itc.univie.ac.at 6668,
  5860. sci.dixie.edu 6667, irc.nsysu.edu.tw, and irc.demon.co.uk.  They are
  5861. not available from all sites, and usage is limited.  But try them if
  5862. you need to.
  5863.  
  5864. Another variation of the "public" options is to apply for a free Unix
  5865. account at nyx.cs.du.edu.  You will have to be validated, which involves
  5866. a little paperwork.  But once completed, you will have a FREE Unix
  5867. account with full IRC privileges, including DCC file exchange.  Of
  5868. course, you need a "local" account somewhere with telnet and ftp
  5869. privileges, but this is often easier to obtain than an account with all
  5870. options locally.
  5871.  
  5872. 4)  Basics of IRC
  5873.  
  5874. Well, hopefully, you will now have an Internet site with a method of
  5875. accessing IRC.  Next, we want to give some tips on using and enjoying
  5876. IRC and introduce DCC, the command for transferring files between people
  5877. on IRC... and between "bots" and people.
  5878.  
  5879. A "bot", you say?  Some of you may laugh; sure of course, a bot.  What
  5880. else is new?  But... I remember that it was ages before I finally
  5881. figured out.. or someone gave me a clue.. as to what a "bot" really was.
  5882. Before we go on, let me give you a VERY brief description of a bot.  We
  5883. can say that a bot may be a "script", a series of IRC language
  5884. statements understood by your IRC client; or it may be a separate
  5885. program (typically written in "C"); which, in either case, runs without
  5886. any help from its "owner" - YOU.
  5887.  
  5888. Instead, a bot is intended to respond to others on IRC who "talk" to it
  5889. by "/msg", "/dcc chat", or even "on-channel" commands like "!list" or
  5890. "&help".  One bot even lists the c64 files it has on-line in response to
  5891. someone typing "load "$",8".
  5892.  
  5893. What a bot does and how you command it varies a LOT.  There really is no
  5894. standard way to talk to a bot.  Try "/msg <botname>" help as a starting
  5895. point and see what happens.  Most often there will be instructions that
  5896. tell you what to do next.  Experiment a little - you will get the hang
  5897. of it.
  5898.  
  5899. Back to the main plot.  The first thing to do after you get connected to
  5900. IRC is to choose a "nick".  This is the handle that you will be known by
  5901. and talked to on IRC.  Do this by typing:
  5902.  
  5903. /nick <nick>
  5904.  
  5905. It's your choice.. unless someone else is already using it.  IRC does
  5906. not let two people use the same nick at the same time.  It will tell you
  5907. about this if you try - sometimes in a rather active way - like "kick"
  5908. you off.  Don't worry - just reconnect.. but try a different nick.  Try
  5909. just changing the nick a little - like even putting a "1" or "2" behind
  5910. it.
  5911.  
  5912. Any number of people, however, can use the same nick at different times.
  5913. This CAN cause a little confusion.. make sure you know you are talking
  5914. to who you think you are.. check a nick's whole address with:
  5915.  
  5916. /whois <nick>
  5917.  
  5918. Next, you will want to join a channel.  Do this by typing:
  5919.  
  5920. /join #<channel>
  5921.  
  5922. A channel is a logical connection of all IRC users anywhere in the world
  5923. that have typed the same /join command.  All lines typed to the channel
  5924. by anyone on the channel are spread by IRC to all other people who are
  5925. on the channel.  This is the real power of IRC...  a world-wide
  5926. "conference" or "party line", where people with the same interests can
  5927. communicate with each other.
  5928.  
  5929. Because of different delays in different parts of the Internet, all the
  5930. lines typed by everyone will not always appear at the same time or even
  5931. in the same order at everyone's terminal.  This usually does not cause
  5932. much of a problem - just be aware that it happens.
  5933.  
  5934. If the channel name does not exist at the time you type /join, it will
  5935. be created for you!  Yes, anyone can "create" a channel.  But #c-64 is
  5936. almost always there.  Give it a try!
  5937.  
  5938. After you get on a channel, you can type:
  5939.  
  5940. /who *
  5941.  
  5942. This will give you a list of who (which nicks) is on the channel and
  5943. what their home sites are.  This address may or may not be the correct
  5944. email address for the nick - so check with the person first (perhaps a
  5945. "/msg <nick>" - see below) if you want to email him.
  5946.  
  5947. As mentioned before, normal channel conversation is seen by everyone who
  5948. has joined the channel.  This is great most of the time.  Occasionally,
  5949. though, you may want to tell just one person (or bot) something that the
  5950. entire channel would not want to hear.  In this case, use the command:
  5951.  
  5952. /msg <nick> <message>
  5953.  
  5954. Type it on a line of its own, and just <nick> will see your <message>.
  5955. Quite handy for the more "personal" or "specialized" conversations.
  5956. Careful, though... use the wrong <nick> or leave out the "/" and people
  5957. other than you intended will see your <message>.
  5958.  
  5959. If you find you are doing a lot of /msg's to the same nick, try:
  5960.  
  5961. /query <nick>
  5962.  
  5963. This will put you in a sort of 'permanent' /msg <nick> mode, so that
  5964. everything you type that would normally go to the channel will not act
  5965. like a "/msg <nick>" preceded it, and it will go just to <nick>.  Type
  5966. just "/query" to cancel this mode.
  5967.  
  5968. Let's jump, now, to /dcc, the command that allows most IRC users to
  5969. transfer files.  DCC stands for "Direct Client to Client".  What it does
  5970. is allow two nicks to transfer files *directly* between their sites, not
  5971. going through either of their servers.  One of the nicks can even be a
  5972. bot; IRC does not make a distinction.
  5973.  
  5974. When two nicks exchange files, the sender must always start by typing:
  5975.  
  5976. /dcc send <nick> <filename>
  5977.  
  5978. The recipient will get a message telling what file is being offered and must
  5979. type:
  5980.  
  5981. /dcc get <nick> [filename]
  5982.  
  5983. The [filename] is optional, but must be used if more than one file is to
  5984. be transferred simultaneously.  Yes, simultaneous transfer of multiple
  5985. files CAN be done.  Many people do not realize this.  Just use the
  5986. [filename] option with the "/dcc get" command.
  5987.  
  5988. The files that you send and the files that you receive with DCC are
  5989. always in the directory you are in when you start IRC.  You can type "/cd"
  5990. to see what that directory is and you can type:
  5991.  
  5992. /cd <pathname>
  5993.  
  5994. to change that directory.  Or, you can give the absolute or relative
  5995. pathname of the file you want to send if it is not in your "local"
  5996. directory.
  5997.  
  5998. There are often a couple of bots on #c-64 that can give you c-64 files.
  5999. "coolhand" is partly a script bot that currently has a lot of c-64 files
  6000. available for DCC.  If coolhand is on IRC, type:
  6001.  
  6002. /msg coolhand xdcc list
  6003.  
  6004. to see a list of lists (of files).  To see the individual files on list n,
  6005. type:
  6006.  
  6007. /msg coolhand xdcc list #n
  6008.  
  6009. To have coolhand's script dcc you file #n, type:
  6010.  
  6011. /msg coolhand xdcc send #n   
  6012.  
  6013. followed by:
  6014.  
  6015. /dcc get coolhand
  6016.  
  6017. when you get the dcc offer message.
  6018.  
  6019. There are many scripts that you can use that will autoget a file that is
  6020. DCC'd to you.  The xdcc script that coolhand uses is one such script.
  6021. (Yes, coolhand will also autoget a file that you send to it.)
  6022.  
  6023. 5)  What/Who is on IRC?
  6024.  
  6025. Ok, now you are on IRC.  So what will you find?  Who is on the #c-64?
  6026. The answers are quite varied..  and constantly changing.  I personally
  6027. have been on IRC for over 2 years.. (or is it 3?)  And I have yet to
  6028. ascertain an absolute pattern of people or topics.  Frustrating?  Well,
  6029. maybe to some.  But interesting?  Yes, most certainly.  IRC, and the
  6030. #c-64 channel, is a microcosm of the world, with all its variety of
  6031. people, personalities, projects, propaganda, and priorities.  It is a
  6032. capability, a tool for communications, that is unexcelled in its scope
  6033. and possibilities.
  6034.  
  6035. IRC is totally international, and so is the #c-64 channel.  Besides the
  6036. U.S. and Canada, Europe is very well represented.  There is also a
  6037. smaller but increasingly active contingent in Australia, as the net
  6038. becomes more accessible there.  You will also find a few c-64 users in
  6039. S. America, Africa, and Asia.  Russia and other former Soviet Union
  6040. countries also have a presence.  English is the accepted language for
  6041. use on #c-64, although you will occasionally see a few other language
  6042. used for brief times.
  6043.  
  6044. What is the channel used for?  Just about anything you can imagine that
  6045. normal conversation would be used for.  With a special emphasis for the
  6046. special interest of most channel participants - the c64/128.  For the
  6047. most part, almost everyone on the channel has had or still has a c64 or
  6048. c128.  Some are active users on a real c64/128, while others use one of
  6049. the several emulators that exist for various platforms.  Many former and
  6050. current 64 "scene" members are finding their way to the channel, but all
  6051. members of the c64 community are always welcome, and all are treated
  6052. equally.
  6053.  
  6054. Many people find IRC and #c-64 a very useful way to exchange information
  6055. quickly without having to wait for email to pass back and forth.  As was
  6056. mentioned before, the DCC capability allows for immediate transfer of
  6057. files, another quick and effective way to pass information and things
  6058. like utilities and coding examples.  Such capabilities have encouraged
  6059. many people to either return to the c64 or take up using and programming
  6060. it for the first time.  Yes, the c64 community is actually growing
  6061. again, thanks in part to the growing presence of the Internet, IRC, and
  6062. #c-64!
  6063.  
  6064. So, when you first get on the channel for the first time, don't be
  6065. afraid to ask for help.  You will probably find that the people on there
  6066. are either new themselves, or were once new at one time and had the same
  6067. uncertainties and questions that you do.  Most everyone is very willing
  6068. to help new people.  So ask.  Also, if you have knowledge or a talent to
  6069. offer or a willingness to help somehow, just make that known.  The
  6070. channel is full of people, some of whom probably need exactly what you
  6071. have to give.
  6072.  
  6073. A key thing:  be patient!  When you are new on the channel, you may not
  6074. be noticed right away, especially if there are several conversations
  6075. already going on.  In other cases, you may find that there is really no
  6076. one on the channel, except maybe a few bots.  So hang in there or come
  6077. back a bit later.  Believe me, the IS a lot of action on #c-64 most of
  6078. the time.
  6079.  
  6080. Besides being patient yourself, be patient with other people on the
  6081. channel.  Like in the non-cyber world, misunderstandings CAN occur, since
  6082. your total communication with other people is via the typed word.  But
  6083. the same rules of courteousness that common society utilizes also apply
  6084. on IRC.  Treat people with respect and kindness, and they will most
  6085. likely respond in a like manner.  Sounds like the golden rule?  I think
  6086. so, and I think you will find that its works pretty well on IRC as it
  6087. does in other life.
  6088.  
  6089. Hopefully, this article will help you get started enjoying IRC and
  6090. particularly #c-64.  There's a lot to be gained there... information,
  6091. files, and even new friends.  It's a way to give our c64 community new
  6092. life and spirit.  Give it a try!  See you there.
  6093.  
  6094. *****************
  6095.  
  6096. Some of the material in this article was previously published in "Driven"
  6097. and is used here by permission.
  6098.  
  6099. ========================================================================
  6100. SwiftLink-232 Application Notes (version 1.0b)
  6101.  
  6102. This information is made available from a paper document published by CMD,
  6103. with CMD's express written permission.  [This version includes a couple of
  6104. grammatical corrections and minor changes, plus, the source code has been
  6105. debugged and extended by Craig Bruce <csbruce@ccnga.uwaterloo.ca>.]
  6106.  
  6107. 1. INTRODUCTION
  6108.  
  6109. The SwiftLink-232 ACIA cartridge replaces the Commodore Kernal RS-232 routines
  6110. with a hardware chip.  The chip handles all the bit-level processing now done
  6111. in software by the Commodore Kernal.  The ACIA may be accessed by polling
  6112. certain memory locations in the I/O block ($D000 - $DFFF) or through
  6113. interrupts.  The ACIA may be programmed to generate interrupts in the
  6114. following situations:
  6115.  
  6116. 1) when a byte of data is received
  6117. 2) when a byte of data may be transmitted (i.e., the data register is empty)
  6118. 3) both (1) and (2)
  6119. 4) never
  6120.  
  6121. The sample code below sets up the ACIA to generate an interrupt each time a
  6122. byte of data is received.  For transmitting, two techniques are shown.  The
  6123. first technique consists of an interrupt handler which enables transmit
  6124. interrupts when there are bytes ready to be sent from a transmit buffer.
  6125. There is a separate routine given that manages the transmit buffer.  In the
  6126. second technique, which can be found at the very end of the sample code,
  6127. neither a transmit buffer or transmit interrupts are used.  Instead, bytes of
  6128. data are sent to the ACIA directly as they are generated by the terminal
  6129. program.
  6130.  
  6131. NOTE: The ACIA will _always_ generate an interrupt when a change of state
  6132. occurs on either the DCD or DSR line (unless the lines are not connected in
  6133. the device's cable).
  6134.  
  6135. The 6551 ACIA was chosen for several reasons, including the low cost and
  6136. compatibility with other Commodore (MOS) integrated circuits.  Commodore used
  6137. the 6551 as a model for the Kernal software.  Control, Command, and Status
  6138. registers in the Kernal routines partially mimic their hardware counterparts
  6139. in the ACIA.
  6140.  
  6141. NOTE: If you're using the Kernal software registers in your program, be sure
  6142. to review the enclosed 6551 data sheet carefully.  Several of the hardware-
  6143. register locations do _not_ perform the same function as their software
  6144. counterparts.  You may need to make a few changes in your program to
  6145. accommodate the differences.
  6146.  
  6147. 2. BUFFERS
  6148.  
  6149. Bytes received are placed in "circular" or "ring" buffers by the sample
  6150. routine below, and also by the first sample transmit routine.  To keep things
  6151. similar to the Kernal RS-232 implementation, we've shown 256-byte buffers.
  6152. You may want to use larger buffers for file transfers or to allow more
  6153. screen-processing time.  Bypassing the Kernal routines free many zero-page
  6154. locations, which could improve performance of pointers to large buffers.
  6155.  
  6156. If your program already directly manipulates the Kernal RS-232 buffers, you'll
  6157. find it very easy to adapt to the ACIA.  If you use calls to the Kernal RS-232
  6158. file routines instead, you'll need to implement lower-level code to get and
  6159. store buffer data.
  6160.  
  6161. Briefly, each buffer has a "head" and "tail" pointer.  The head points to the
  6162. next byte to be removed from the buffer.  The tail points to the next free
  6163. location in which to store a byte.  If the head and tail both point to the
  6164. same location, the buffer is empty.  If (tail+1)==head, the buffer is full.
  6165.  
  6166. The interrupt handler described below will place received bytes at the tail of
  6167. the receive buffer.  Your program should monitor the buffer, either by
  6168. comparing the head and tail pointers (as the Commodore Kernal routines do), or
  6169. by maintaining a byte count through the interrupt handler (as the attached
  6170. sample does).  When bytes are available, your program can process them, move
  6171. the head pointer to the next character, and decrement the counter if you use
  6172. one.
  6173.  
  6174. You should send a "Ctrl-S" (ASCII 19) to the host when the buffer is nearing
  6175. capacity.  At higher baud rates, this "maximum size" point may need to be
  6176. lowered.  We found 50 to 100 bytes worked fairly well at 9600 baud.  You can
  6177. probably do things more efficiently (we were using a _very_ rough
  6178. implementation) and set a higher maximum size.  At some "maximum size", a
  6179. "Ctrl-Q" (ASCII 17) can be sent to the host to resume transmission.
  6180.  
  6181. To transmit a byte using the logic of the first transmit routine below, first
  6182. make sure that the transmit buffer isn't full.  Then store the byte at the
  6183. tail of the transmit buffer, point the tail to the next available location,
  6184. and increment the transmit buffer counter (if used).
  6185.  
  6186. The 6551 transmit interrupt occurs when the transmit register is empty and
  6187. available for transmitting another byte.  Unless there are bytes to transmit,
  6188. this creates unnecessary interrupts and wastes a lot of time.  So, when the
  6189. last byte is removed from the buffer, the interrupt handler in the first
  6190. transmit routine below disables transmit interrupts.
  6191.  
  6192. Your program's code that stuffs new bytes into the transmit buffer must
  6193. re-enable transmit interrupts, or your bytes may never be sent.  A model for a
  6194. main code routine for placing bytes into the transmit buffer follows the
  6195. sample interrupt handler.
  6196.  
  6197. Using a transmit buffer allows  your main program to perform other takes while
  6198. the NMI interrupt routine takes care of sending bytes to the ACIA.  If the
  6199. buffer has more than a few characters, however, you may find that most of the
  6200. processor time is spent servicing the interrupt.  Since the ACIA generates NMI
  6201. interrupts, you can't "mask" them from the processor, and you may have timing
  6202. difficulties in your program.
  6203.  
  6204. One solution is to eliminate the transmit buffer completely.  Your program can
  6205. decide when to send each byte and perform any other necessary tasks in between
  6206. bytes as needed.  A model for the main-code routine for transmitting bytes
  6207. without a transmit buffer is also shown following the sample interrupt-handler
  6208. code.  Feedback from developers to date is that many of them have better luck
  6209. _not_ using transmit interrupts or a transmit buffer.
  6210.  
  6211. Although it's possible to eliminate the receive buffer also, we strongly
  6212. advise that you don't.  The host computer, not your program, decides when
  6213. a new byte will arrive.  Polling the ACIA for received bytes instead of
  6214. using an interrupt-driven buffer just waste's your program's time and
  6215. risks missing data.
  6216.  
  6217. For a thorough discussion of the use of buffers, the Kernal RS-232 routines,
  6218. and the Commodore NMI handler, see "COMPUTE!'s VIC-20 and Commodore 64 Tool
  6219. Kit: Kernal", by Dan Heeb (COMPUTE! Books) and "What's Really Inside the
  6220. Commodore 64", by Milton Bathurst (distributed in the US by Schnedler
  6221. Systems).
  6222.  
  6223. 3. ACIA REGISTERS
  6224.  
  6225. The four ACIA registers are explained in detail in the enclosed data sheets.
  6226. The default location for them in our cartridge is address $DE00--$DE03
  6227. (56832--56836).
  6228.  
  6229. 3.1. DATA REGISTER ($DE00)
  6230.  
  6231. This register has dual functionality: it is used to receive and transmit all
  6232. data bytes (i.e., it is a read/write register).
  6233.  
  6234. Data received by the ACIA is placed in this register.  If receive interrupts
  6235. are enabled, an interrupt will be generated when all bits for a received
  6236. byte have been assembled and the byte is ready to read.
  6237.  
  6238. Transmit interrupts, if enabled, are generated when this register is empty
  6239. (available for transmitting).  A byte to be transmitted can be placed in this
  6240. register.
  6241.  
  6242. 3.2. STATUS REGISTER ($DE01)
  6243.  
  6244. This register has dual functionality: it shows the various ACIA settings when
  6245. read, but when written to (data = anything [i.e., don't care]), this register
  6246. triggers a reset of the chip.
  6247.  
  6248. As the enclosed data sheet shows, the ACIA uses bits in this register to
  6249. indicate data flow and errors.
  6250.  
  6251. If the ACIA generates an interrupt, bit #7 is set.  There are four possible
  6252. sources of interrupts:
  6253.  
  6254. 1) receive (if programmed)
  6255. 2) transmit (if programmed)
  6256. 3) if a connected device changes the state of the DCD line
  6257. 4) if a connected device changes the state of the DSR line
  6258.  
  6259. Some programmers have reported problems with using bit #7 to verify ACIA
  6260. interrupts.  At 9600 bps and higher, the ACIA generates interrupts properly,
  6261. and bits #3--#6 (described below) are set to reflect the cause of the
  6262. interrupt, as they should.  But, bit #7 is not consistently set.  At speeds
  6263. under 9600 bps, bit #7 seems to work as designed.  To avoid any difficulties,
  6264. the sample code below ignores bit #7 and tests the four interrupt source bits
  6265. directly.
  6266.  
  6267. Bit #5 indicates the status of the DSR line connected to the RS-232 device
  6268. (modem, printer, etc.), while bit #6 indicates the status of the DCD line.
  6269. NOTE: The function of these two bits is _reversed_ from the standard
  6270. implementation.  Unlike many ACIAs, the 6551 was designed to use the DCD
  6271. (Data Carrier Detect) signal from the modem to activate the receiver section
  6272. of the chip.  If DCD is inactive (no carrier detected), the modem messages
  6273. and echos of commands would not appear on the user's screen.  We wanted the
  6274. receiver active at all times.  We also wanted to give the you access to the
  6275. DCD signal from the modem.  So, we exchanged the DCD and DSR signals at the
  6276. ACIA.  Both lines are pulled active internally by the cartridge if left
  6277. unconnected by the user (i.e., in an null-modem cable possibility).
  6278.  
  6279. Bit #4 is set if the transmit register is empty.  Your program must monitor
  6280. this bit and not write to the data register until the bit i sset (see the
  6281. sample XMIT code below).
  6282.  
  6283. Bit #3 is set if the receive register is full.
  6284.  
  6285. Bits #2, #1, and #0, when set, indicate overrun, framing, and parity errors in
  6286. received bytes.  The next data byte received erases the error information for
  6287. the preceding byte.  If you wish to use these bits, store them for processing
  6288. by your program.  The sample code below does not implement any error checking,
  6289. but the Kernal software routines do, so adding features to your code might be
  6290. a good idea.
  6291.  
  6292. 3.3. COMMAND REGISTER ($DE02)
  6293.  
  6294. The Command Register control parity checking, echo mode, and transmit/receive
  6295. interrupts.  It is a read/write register, but reading the register simply
  6296. tells you what the settings of the various parameters are.
  6297.  
  6298. You use bits #7, #6, and #5 to choose the parity checking desired.
  6299.  
  6300. Bit #4 should normally be cleared (i.e., no echo)
  6301.  
  6302. Bits #3 and #2 should reflect whether or not you are using transmit
  6303. interrupts, and if so, what kind.  In the first sample transmit routine below,
  6304. bit #3 is set and bit #2 is cleared to disable transmit interrupts (with RTS
  6305. low [active]) on startup.  However, when a byte is placed in the transmit
  6306. buffer, bit #3 is cleared and bit #2 is set to enable transmit interrupts
  6307. (with RTS low).  When all bytes in the buffer have been transmitted, the
  6308. interrupt handler disables transmit interrupts.  NOTE: If you are connected to
  6309. a RS-232 device that uses CTS/RTS handshaking, you can tell the device to stop
  6310. temporarily by bringing RTS high (inactive): clear both bits #2 and #3.
  6311.  
  6312. Bit #1 should reflect whether or not you are using receive interrupts.  In
  6313. the sample code below, it is set to enable receive interrupts.
  6314.  
  6315. Bit #0 acts as a "master control switch" for all interrupts on the chip
  6316. itself.  It _must_ be set to enable any interrupts -- if it is cleared, all
  6317. interrupts are turned off and the receiver section of the chip is disabled.
  6318. This bit also pulls the DTR line low to enable communication with the
  6319. connected RS-232 device.  Clearing this bit causes most Hayes-compatible
  6320. modems to hang up (by bringing DTR high).  This bit should be cleared when a
  6321. session is over and the user exits the terminal program to insure that no
  6322. spurious interrupts are generated.  One fairly elegant way to do this is to
  6323. perform a software reset of the chip (writing any value to the Status
  6324. register).
  6325.  
  6326. NOTE: In the figures on the 6551 data sheet, there are small charts at the
  6327. bottom of each of the labelled "Hardware Reset/Program Reset".  These charts
  6328. indicate what values the bits of these registers contain after a hardware
  6329. reset (like toggling the computer's power) and a program reset (a write to the
  6330. Status register).
  6331.  
  6332. 3.4. CONTROL REGISTER ($DE03)
  6333.  
  6334. You use this register to control the number of stop bits, the word length,
  6335. switch on the internal baud-rate generator, and set the baud rate.  It is a
  6336. read/write register, but reading the register simply tells you what the
  6337. various parameters are.  See the figure in the data sheet for a complete list
  6338. of parameters.
  6339.  
  6340. Be sure that bit #4, the "clock source" bit, is always set to use the on-chip
  6341. crystal-controlled baud-rate generator.
  6342.  
  6343. You use the other bits to choose the baud rate, word length, and number of
  6344. stop bits.  Note that our cartridge uses a double-speed crystal, so values
  6345. given on the data sheet are doubled [this is how they are shown below] (the
  6346. minimum speed is 100 bps and the maximum speed is 38,400 bps).
  6347.  
  6348. 4. ACIA HARDWARE INTERFACING
  6349.  
  6350. The ACIA is mounted on a circuit board designed to plug into the expansion
  6351. (cartridge) port.  The board is housed in a cartridge shell with a male DB-9
  6352. connector at the rear.  The "IBM(R) PC/AT(TM) standard" DB-9 RS-232 pinout is
  6353. implemented.  Commercial DB-9 to DB-25 patch cords are readily available, and
  6354. are sold by us as well.
  6355.  
  6356. Eight of the nine lines from the AT serial port are implemented: TxD, RxD,
  6357. DTR, DSR, RTS, CTS, DCD, & GND.  RI (Ring Indicator) is not implemented
  6358. because the 6551 does not have a pin to handle it.  CTS and RTS are not
  6359. normally used by 2400 bps or slower Hayes-compatible modems, but these lines
  6360. are being used by several newer, faster modems (MNP modems in particular).
  6361. Note that although CTS is connected to the 6551, there is no way to monitor
  6362. what state it is -- the value does not appear in any register.  The 6551
  6363. handles CTS automatically: if it is pulled high (inactive) by the connected
  6364. RS-232 device, the 6551 stops transmitting (clears the "transmit data register
  6365. empty" bit [#4] in the status register).
  6366.  
  6367. The output signals are standard RS-232 level compatible.  We've tested units
  6368. with several commercial modems and with various computers using null-modem
  6369. cables up to 38,400 bps without difficulties.  In addition, there are pull-up
  6370. resistors on three of the four input lines (DCD, DSR, CTS) so that if these
  6371. pins are not connected in a cable, those three lines will pull to the active
  6372. state.  For example, if you happen to use a cable that is missing the DCD
  6373. line, the pull-up resistor will pull the line active, so that bit #6 in the
  6374. status register would be cleared (DCD is active low).
  6375.  
  6376. An on-board crystal provides the baud rate clock signal, with a maximum of
  6377. 38.4 Kbaud, because we are using a double-speed crystal.  If possible, test
  6378. your program at 38.4 Kbaud as well as lower baud rates.  Users may find this
  6379. helpful for local file transfers using the C-64/C-128 as a dumb terminal on
  6380. larger systems.  And, after all, low-cost 28.8 Kb modems for the masses are
  6381. just around the corner.
  6382.  
  6383. Default decoding for the ACIA addresses is done by the I/O #1 line (pin 7) on
  6384. the cartridge port.  This line is infrequently used on either the C-64 or
  6385. C-128 and should allow compatibility with most other cartridge products,
  6386. including the REU.  The circuit board also has pads for users with special
  6387. needs to change the decoding to I/O #2 (pin 10).  This change moves the ACIA
  6388. base address to $DF00, making it incompatible with the REU.
  6389.  
  6390. C-128 users may also elect to decode the ACIA at $D700 (this is a SID-chip
  6391. mirror on the C-64).  Since a $D700 decoding line is not available at the
  6392. expansion port, the user would need to run a clip lead into the computer and
  6393. connect to pin 12 of U2 (a 74LS138).  We have tried this and it works.  $D700
  6394. is an especially attractive location for C-128 BBS authors, because putting
  6395. the SwiftLink there will free up the other two memory slots for devices that
  6396. many BBS sysops use: IEEE and hard-drive interfaces.
  6397.  
  6398. Although we anticipate relatively few people changing ACIA decoding, you
  6399. should allow your software to work with a SwiftLink at any of the three
  6400. locations.  You could either (1) test for the ACIA automatically by writing a
  6401. value to the control register and then attempting to read it back or (2)
  6402. provide a user-configurable switch/poke/menu option.
  6403.  
  6404. The Z80 CPU used for CP/M mode in the C-128 is not connected to the NMI line,
  6405. which poses a problem since the cleanest software interface for C-64/C-128-
  6406. mode programming is with this interrupt.  We have added a switch to allow the
  6407. ACIA interrupt to be connected to either NMI or IRQ, which the Z80 does use.
  6408. The user can move this switch without opening the cartridge.
  6409.  
  6410. 5. SAMPLE CODE
  6411.  
  6412. This section has been translated into ACE-assembler format.  Cut on the dotted
  6413. lines to extract the code, and assemble it using the ACE assembler (ACE is a
  6414. public-domain program).  This program will work on both the C64 and C128.
  6415. To use from BASIC:
  6416.  
  6417. LOAD"SAMPLE",8,1
  6418. SYS8192
  6419.  
  6420. It is a very simple terminal program.  Press the STOP key to exit from it.
  6421.  
  6422. %%%---8<---cut-here---8<---%%%
  6423. ;Sample NMI interrupt handler for 6551 ACIA on Commodore 64/128
  6424.  
  6425. ;(c) 1990 by Noel Nyman, Kent Sullivan, Brian Minugh,
  6426. ;Geoduck Development Systems, and Dr. Evil Labs.
  6427.  
  6428. ;    ---=== EQUATES ===---
  6429.  
  6430. base      =    $DE00    ;base ACIA address
  6431. data      =    base
  6432. status    =    base+1
  6433. command   =    base+2
  6434. control   =    base+3
  6435.  
  6436. ;Using the ACIA frees many addresses in zero page normally used by
  6437. ;Kernel RS-232 routines.  The addresses for the buffer pointers were
  6438. ;chosen arbitrarily.  The buffer vector addresses are those used by
  6439. ;the Kernal routines.
  6440.  
  6441. rhead     =   $A7      ;pointer to next byte to be removed from
  6442.                        ;receive buffer
  6443. rtail     =   $A8      ;pointer to location to store next byte received
  6444. rbuff     =   $F7      ;receive-buffer vector
  6445.  
  6446. thead     =   $A9      ;pointer to next byte to be removed from
  6447.                        ;transmit buffer
  6448. ttail     =   $AA      ;pointer to location to store next byte
  6449.                        ;in transmit buffer
  6450. tbuff     =   $F9      ;transmit buffer
  6451.  
  6452. xmitcount =   $AB      ;count of bytes remaining in transmit (xmit) buffer
  6453. recvcount =   $B4      ;count of bytes remaining in receive buffer
  6454.  
  6455. errors    =   $B5      ;DSR, DCD, and received data errors information
  6456.  
  6457. xmiton    =   $B6      ;storage location for model of command register
  6458.                        ;which turn both receive and transmit interrupts on
  6459. xmitoff   =   $BD      ;storage location for model of command register
  6460.                        ;which turns the receive interrupt on and the
  6461.                        ;transmit interrupts off
  6462.  
  6463. NMINV     =   $0318    ;Commodore Non-Maskable Interrupt vector
  6464. OLDVEC    =   $03fe    ;innocuous location to store old NMI vector (two bytes)
  6465.  
  6466. ;    ---=== INITIALIZATION ===---
  6467.  
  6468. ;Call the following code as part of system initialization.
  6469.  
  6470. ;clear all buffer pointers, buffer counters, and errors location
  6471.  
  6472.       org   $2000      ;change to suit your needs
  6473.       lda   #$00
  6474.       sta   rhead
  6475.       sta   rtail
  6476.       sta   thead
  6477.       sta   ttail
  6478.  
  6479.       sta   xmitcount
  6480.       sta   recvcount
  6481.       sta   errors
  6482.  
  6483. ;store the addresses of the buffers in the zero-page vectors
  6484.  
  6485.       lda   #<TRANSMIT_BUFFER
  6486.       sta   tbuff
  6487.       lda   #>TRANSMIT_BUFFER
  6488.       sta   tbuff + 1
  6489.  
  6490.       lda   #<RECEIVE_BUFFER
  6491.       sta   rbuff
  6492.       lda   #>RECEIVE_BUFFER
  6493.       sta   rbuff + 1
  6494.  
  6495. ;the next four instructions initialize the ACIA to arbitrary values.
  6496. ;These could be program defaults, or replaced by code that picks up
  6497. ;the user's requirements for baud rate, parity, etc.
  6498.  
  6499. ;The ACIA "control" register controls stop bits, word length, the
  6500. ;choice of internal or external baud-rate generator, and the baud
  6501. ;rate when the internal generator is used.  The value below sets the
  6502. ;ACIA for one stop bit, eight-bit word length, and 4800 baud using the
  6503. ;internal generator.
  6504. ;             .------------------------- 0 = one stop bit
  6505. ;             :
  6506. ;             :.-------------------- word length, bits 6-7
  6507. ;             ::.------------------- 00 = eight-bit word
  6508. ;             :::
  6509. ;             :::.------------- clock source, 1 = internal generator
  6510. ;             ::::
  6511. ;             :::: .----- baud
  6512. ;             :::: :.---- rate
  6513. ;             :::: ::.--- bits   ;1010 == 4800 baud, change to what you want
  6514. ;             :::: :::.-- 0-3
  6515.       lda   #%0001_1010
  6516.       sta   control
  6517.  
  6518. ;The ACIA "command" register controls the parity, echo mode, transmit and
  6519. ;receive interrupt enabling, hardware "BRK", and (indirectly) the "RTS"
  6520. ;and "DTR" lines.  The value below sets the ACIA for no parity check,
  6521. ;no echo, disables transmit interrupts, and enables receive interrupts
  6522. ;(RTS and DTR low).
  6523. ;             .------------------------- parity control,
  6524. ;             :.------------------------ bits 5-7
  6525. ;             ::.----------------------- 000 = no parity
  6526. ;             :::
  6527. ;             :::.------------------- echo mode, 0 = normal (no echo)
  6528. ;             ::::
  6529. ;             :::: .----------- transmit interrupt control, bits 2-3
  6530. ;             :::: :.---------- 10 = xmit interrupt off, RTS low
  6531. ;             :::: ::
  6532. ;             :::: ::.------ receive interrupt control, 0 = enabled
  6533. ;             :::: :::
  6534. ;             :::: :::.--- DTR control, 1=DTR low
  6535.       lda   #%0000_1001
  6536.       sta   command
  6537.  
  6538. ;Besides initialization, also call the following code whenever the user
  6539. ;changes parity of echo mode.
  6540. ;It creates the "xmitoff" and "xmiton" models used by the interrupt
  6541. ;handler and main-program transmit routine to control the ACIA
  6542. ;interrupt enabling.  If you don't change the models' parity bits,
  6543. ;you'll revert to "default" parity on the next NMI.
  6544.  
  6545.                         ;initialize with transmit interrupts off since
  6546.                         ;buffer will be empty
  6547.  
  6548.       sta   xmitoff     ;store as a model for future use
  6549.       and   #%1111_0000 ;mask off interrupt bits, keep parity/echo bits
  6550.       ora   #%0000_0101 ;and set bits to enable both transmit and
  6551.                         ;receive interrupts
  6552.       sta   xmiton      ;store also for future use
  6553.  
  6554. ;The standard NMI routine tests th <RESTORE> key, CIA #2, and checks
  6555. ;for the presence of an autostart cartridge.
  6556.  
  6557. ;You can safely bypass the normal routine unless:
  6558. ;       *  you want to keep the user port active
  6559. ;       *  you want to use the TOD clock in CIA #2
  6560. ;       *  you want to detect an autostart cartridge
  6561. ;       *  you want to detect the RESTOR key
  6562. ;
  6563. ;If you need any of these functions, you can wedge the ACIA
  6564. ;interrupt handler in ahead of the Kernal routines.  It's probably
  6565. ;safer to replicate in your own program only the Kernal NMI functions
  6566. ;that you need.  We'll illustrate bypassing all Kernal tests.
  6567.  
  6568. ;BE SURE THE "NEWNMI" ROUTINE IS IN PLACE BEFORE EXITING THIS CODE!
  6569. ;A "stray" NMI that occurs after the vector is changed to NEWNMI's address
  6570. ;will probably cause a system crash if NEWNMI isn't there.  Also, it would
  6571. ;be best to initialize the ACIA to a "no interrupts" state until the
  6572. ;new vector is stored.  Although a power-on reset should disable all
  6573. ;ACIA interrupts, it pays to be sure.
  6574.  
  6575. ;If the user turns the modem off and on, an interrupt will probably be
  6576. ;generated.  At worst, this may leave a stray character in teh receive
  6577. ;buffer, unless you don't have NEWNMI in place.
  6578.  
  6579. NEWVEC:
  6580.       sei               ;A stray IRQ shouldn't cause any problems
  6581.                         ;while we're changing the NMI vector, but
  6582.                         ;why take chances?
  6583.  
  6584. ;If you want all the normal NMI tests to occur after the ACIA check,
  6585. ;save the old vector.  If you don't need the regular stuff, you can
  6586. ;skip the next four lines.  Note that the Kernal NMI routine pushes
  6587. ;the CPU registers to the stack.  If you call it at the normal address,
  6588. ;you should pop the registers first (see EXITINT below).
  6589.  
  6590.       lda   NMINV      ;get low byte of present vector
  6591.       sta   OLDVEC     ;and store it for future use
  6592.       lda   NMINV+1    ;do the same
  6593.       sta   OLDVEC+1   ;with the high byte
  6594.  
  6595.                        ;come here from the SEI if you're not saving
  6596.                        ;the old vector
  6597.       lda   #<NEWNMI   ;get low byte of new NMI routine
  6598.       sta   NMINV      ;store in vector
  6599.       lda   #>NEWNMI   ;and do the same with
  6600.       sta   NMINV+1    ;the high byte
  6601.  
  6602.       cli              ;allow IRQs again
  6603.  
  6604. ;continue initializing your program
  6605.  
  6606. ;     :::   ::::::     ;program initialization continues
  6607.       jmp   TERMINAL   ;go to the example dumb-terminal subroutine
  6608.  
  6609. ;Save two bytes to store the old vector only if you need it
  6610.  
  6611.  
  6612. ;    ---=== New NMI Routine Starts Here ===---
  6613.  
  6614. ;The code below is a simple interrupt patch to control the ACIA.  When
  6615. ;the ACIA generates an interrupt, this routine examines the status
  6616. ;register which contains the following data.
  6617.  
  6618. ;             .---------------------------- high if ACIA caused interrupt;
  6619. ;             :                             not used in code below
  6620. ;             :
  6621. ;             :.------------------------- reflects state of DCD line
  6622. ;             ::
  6623. ;             ::.---------------------- reflects state of DSR line
  6624. ;             :::
  6625. ;             :::.------------------ high if xmit-data register is empty
  6626. ;             ::::
  6627. ;             :::: .--------------- high if receive-data register full
  6628. ;             :::: :
  6629. ;             :::: :.----------- high if overrun error
  6630. ;             :::: ::
  6631. ;             :::: ::.------- high if framing error
  6632. ;             :::: :::
  6633. ;             :::: :::.--- high if parity error
  6634. ;     status  xxxx_xxxx
  6635.  
  6636. NEWNMI:
  6637. ;     sei              ;the Kernal routine already does this before jumping
  6638.                        ;through the NMINV vector
  6639.       pha              ;save A register
  6640.       txa
  6641.       pha              ;save X register
  6642.       tya
  6643.       pha              ;save Y register
  6644.  
  6645. ;As discussed above, the ACIA can generate an interrupt from one of four
  6646. ;different sources.  We'll first check to see if the interrupt was
  6647. ;caused by the receive register being full (bit #3) or the transmit
  6648. ;register being empty (bit #4) since these two activities should receive
  6649. ;priority.  A BEQ (Branch if EQual) tests the status register and branches
  6650. ;if the interrupt was not caused by the data register.
  6651.  
  6652. ;Before testing for the source of the interrupt, we'll prevent more
  6653. ;interrupts from the ACIA by disabling them at the chip.  This prevents
  6654. ;another NMI from interrupting this one.  (SEI won't work because the
  6655. ;CPU can't disable non-maskable interrupts).
  6656.  
  6657. ;At lower baud rates (2400 baud and lower) this may not be necessary.  But,
  6658. ;it's safe and doesn't take much time, either.
  6659.  
  6660. ;The same technique should be used in parts of your program where timing
  6661. ;is critical.  Disk access, for example, uses SEI to mask IRQ interrupts.
  6662. ;You should turn off the ACIA interrupts during disk access also to prevent
  6663. ;disk errors and system crashes.
  6664.  
  6665. ;First, we'll load the status register which contains all the interrupt
  6666. ;and any received-data error information in the 'A' register.
  6667.  
  6668.       lda   status
  6669.  
  6670. ;Now prevent any more NMIs from the ACIA
  6671.  
  6672.       ldx   #%0000_0011   ;disable all interrupts, bring RTS inactive, and
  6673.                           ;leave DTR active
  6674.       stx   command       ;send to ACIA-- code at end of interrupt handler
  6675.                           ;will re-enable interrupts
  6676.  
  6677. ;Store the status-register data only if needed for error checking.
  6678. ;The next received byte will clear the error flags.
  6679.  
  6680. ;     sta   errors        ;only if error checking implemented
  6681.  
  6682.       and   #%0001_1000   ;mask out all but transmit and
  6683.                           ;receive interrupt indicators
  6684.  
  6685. ;If you don't use a transmit buffer you can use
  6686. ;
  6687. ;     and   #%0000_1000
  6688. ;
  6689. ;to test for receive interrupts only and skip the receive test shown
  6690. ;below.
  6691.  
  6692.       beq   TEST_DCD_DSR
  6693.  
  6694. ;if the 'A' register=0, either the interrupt was not caused by the
  6695. ;ACIA or the ACIA interrupt was caused by a change in the DCD or
  6696. ;DSR lines, so we'll branch to check those sources.
  6697.  
  6698. ;If your program ignores DCD and DSR, you can branch to
  6699. ;the end of the interrupt handler instead:
  6700. ;
  6701. ;     beq   NMIEXIT
  6702. ;
  6703.  
  6704. ;Test the status register information to see if a received byte is ready
  6705. ;If you don't use a transmit buffer, skip the next two instructions.
  6706.  
  6707. RECEIVE:                  ;process received byte
  6708.       and   #%0000_1000   ;mask all but bit #3
  6709.       beq   XMITCHAR      ;if not set, no received byte - if you're using
  6710.                           ;a transmit buffer, the interrupt must have been
  6711.                           ;caused by transmit.  So, branch to handle.
  6712.       lda   data          ;get received byte
  6713.       ldy   rtail         ;index to buffer
  6714.       sta   (rbuff),y     ;and store it
  6715.       inc   rtail         ;move index to next slot
  6716.       inc   recvcount     ;increment count of bytes in receive buffer
  6717.                           ;(if used by your program)
  6718.  
  6719. ;Skip the "XMIT" routines below if you decide not to use a transmit buffer.
  6720. ;In that case, the next code executed starts at TEST_DCD_DSR or NMIEXIT.
  6721.  
  6722. ;After processing a received byte, this sample code tests for bytes
  6723. ;in the transmit buffer and sends on if present.  Note that, in this
  6724. ;sample, receive interrupts take precedence.  You may want to reverse the
  6725. ;order in your program.
  6726.  
  6727. ;If the ACIA generated a transmit interrupt and no received byte was
  6728. ;ready, status bit #4 is already set.  The ACIA is ready to accept
  6729. ;the byte to be transmitted and we've branched directly to XMITCHAR below.
  6730.  
  6731. ;If only bit #3 was set on entry to the interrupt handler, the ACIA may have
  6732. ;been in the process of transmitting the last byte, and there may still be
  6733. ;characters in the transmit buffer.  We'll check for that now, and send the
  6734. ;next character if there is one.  Before sending a character to the ACIA to
  6735. ;be transmitted, we must wait until bit #4 of the status register is set.
  6736.  
  6737. XMIT:
  6738.       lda   xmitcount     ;if not zero, characters still in buffer
  6739.                           ;fall through to process xmit buffer
  6740.       beq   TEST_DCD_DSR  ;no characters in buffer-- go to next check
  6741. ;or
  6742. ;
  6743. ;     beq   NMIEXIT
  6744. ;
  6745. ;if you don't check DCD or DSR in your program.
  6746.  
  6747. XMITBYTE:
  6748.       lda   status        ;test bit #4
  6749.       and   #%00010000
  6750.       beq   TEST_DCD_DSR  ;skip if transmitter still busy
  6751.  
  6752. XMITCHAR:                 ;transmit a character
  6753.       ldy   thead
  6754.       lda   (tbuff),y     ;get character at head of buffer
  6755.       sta   data          ;place in ACIA for transmit
  6756.  
  6757.                           ;point to next character in buffer
  6758.       inc   thead         ;and store new index
  6759.       dec   xmitcount     ;subtract one from count of bytes
  6760.                           ;in xmit buffer
  6761.       lda   xmitcount
  6762.       beq   TEST_DCD_DSR
  6763. ;or
  6764. ;
  6765. ;     beq   NMIEXIT
  6766. ;
  6767. ;if you don't check DCD or DSR in your program
  6768.  
  6769. ;If xmitcount decrements to zero, there are no more characters to
  6770. ;transmit.  The code at NMIEXIT turns ACIA transmit interrupts off.
  6771.  
  6772. ;If there are more bytes in the buffer, set up the 'A' register with
  6773. ;the model that turns both transmit and receive interrupts on.  We felt
  6774. ;that was safer, and not much slower, than EORing bits #3 and #4.  Note
  6775. ;that the status of the parity/echo bits is preserved in the way "xmiton"
  6776. ;and "xmitoff" were initialized earlier.
  6777.  
  6778.       lda   xmiton        ;model to leave both interrupts enabled
  6779.  
  6780. ;If you don't use DCD or DSR
  6781.  
  6782.       bne   NMICOMMAND    ;branch always to store model in command register
  6783.  
  6784. ;If your program uses DCD and/or DSR, you'll want to know when the state
  6785. ;of those lines changes.  You can do that outside the interrupt handler
  6786. ;by polling the ACIA status register, but if either of the lines changes,
  6787. ;the chip will generate an NMI anyway.  So, you can let the interrupt
  6788. ;handler do teh work for you.  The cost is the added time required to
  6789. ;execute the DCD_DSR code on each NMI.
  6790.  
  6791. TEST_DCD_DSR:
  6792.  
  6793. ;     pha                 ;only if you use a transmit buffer, 'A' holds
  6794.                           ;the proper mask to re-enable interrupts on
  6795.                           ;the ACIA
  6796. ;     ::
  6797. ;     ::                  ;appropriate code here to compare bit #6 (DCD)
  6798. ;     ::                  ;and/or bit #5 (DSR) with their previous states
  6799. ;     ::                  ;which you've already stored in memory and take
  6800. ;     ::                  ;appropriate action
  6801. ;     ::
  6802. ;     pla                 ;only if you pushed it at the start of the
  6803.                           ;DCD/DSR routine
  6804. ;     bne   NMICOMMAND    ;'A' holds the xmiton mask if it's not zero,
  6805.                           ;implying that we arrived here from xmit routine
  6806.                           ;not used if you're not using a transmit buffer.
  6807.  
  6808. ;If the test for ACIA interrupt failed on entry to the handler, we branch
  6809. ;directly to here.  If you don't use additional handlers, the RESTORE key
  6810. ;(for example) will fall through here and have no effect on your program
  6811. ;or the machine, except for some wasted cycles.
  6812.  
  6813. NMIEXIT:
  6814.       lda   xmitoff       ;load model to turn transmit interrupts off
  6815.  
  6816. ;and this line sets the interrupt status to whatever is in the 'A' register.
  6817.  
  6818. NMICOMMAND:
  6819.       sta   command
  6820.  
  6821. ;That's all we need for the ACIA interrupt handler.  Since we've pushed the
  6822. ;CPU registers to the stack, we need to pop them off.  Note that you must
  6823. ;do this EVEN IF YOU JUMP TO THE KERNAL HANDLER NEXT, since it will push
  6824. ;them again immediately.  You can skip this step only if you're proceeding
  6825. ;to a custom handler.
  6826.  
  6827. EXITINT:                  ;restore things and exit
  6828.       pla                 ;restore 'Y' register
  6829.       tay
  6830.       pla                 ;restore 'X' register
  6831.       tax
  6832.       pla                 ;restore 'A' register
  6833.  
  6834. ;If you want to continue processing the interrupt with the Kernal routines,
  6835.  
  6836.       jmp   (OLDVEC)      ;continue processing interrupt with Kernal handler
  6837.  
  6838. ;Or, if you add your own interrupt routine,
  6839.  
  6840. ;     jmp   YOURCODE      ;continue with your own handler
  6841.  
  6842. ;If you use your own routine, or if you don't add anything, BE SURE to do
  6843. ;this last (C64 only):
  6844.  
  6845. ;     rti                 ;return from interrupt instruction
  6846.  
  6847. ;to restore the flags register the CPU pushes to the stack before jumping
  6848. ;to the Kernal code.  It also returns you to the interrupted part of
  6849. ;your program
  6850.  
  6851. ;-----------------------------------------------------------------------------
  6852. ;Sample routine to store a character in the buffer to be transmitted
  6853. ;by the ACIA.
  6854.  
  6855. ;(c) 1990 by Noel Nyman, Kent Sullivan, Brian Minugh,
  6856. ;Geoduck Developmental Systems, and Dr. Evil Labs.
  6857.  
  6858. ;Assumes the character is in the 'A' register on entry.  Destroys 'Y'--
  6859. ;push to stack if you need to preserve it.
  6860.  
  6861. SENDBYTE:                 ;adds a byte to the xmit buffer and sets
  6862.                           ;the ACIA to enable transmit interrupts (the
  6863.                           ;interrupt handler will disable them again
  6864.                           ;when the buffer is empty)
  6865.  
  6866.       ldy   xmitcount     ;count of bytes in transmit buffer
  6867.       cpy   #255          ;max buffer size
  6868.       beq   NOTHING       ;buffer is full, don't add byte
  6869.  
  6870.       ldy   ttail         ;pointer to end of buffer
  6871.       sta   (tbuff),y     ;store byte in 'A' at end of buffer
  6872.       inc   ttail         ;point to next slot in buffer
  6873.       inc   xmitcount     ;and add one to count of bytes in buffer
  6874.  
  6875.       lda   xmiton        ;get model for turning on transmit interrupts
  6876.       sta   command       ;tell ACIA to do it
  6877.  
  6878.       rts                 ;return to your program
  6879.  
  6880. NOTHING:
  6881.       lda   #$00          ;or whatever flag your program uses to tell that the
  6882.                           ;byte was not transmitted
  6883.       rts                 ;and return
  6884.  
  6885. ;Alternative routine to transmit a character from main program when not using
  6886. ;a transmit buffer.
  6887. ;
  6888. ;(c) 1990 by Noel Nyman, Kent Sullivan, Brian Minugh,
  6889. ;Geoduck Developmental Systems, and Dr. Evil Labs.
  6890. ;
  6891. ;Assumes the character to be transmitted is in the 'A' register on entry.
  6892. ;Destroys 'Y'; push to stack if you need to preserve it.
  6893. ;
  6894. ;SENDBYTE:
  6895. ;     tay                 ;remember byte to be transmitted
  6896. ;
  6897. ;TESTACIA:
  6898. ;     lda   status        ;bit #4 of the status register is set if
  6899. ;                         ;the ACIA is ready to transmit another byte,
  6900. ;                         ;even if transmit interrupts are disabled.
  6901. ;     and   #%0001_0000
  6902. ;     beq   TESTACIA      ;wait for bit #4 to be set
  6903. ;     sty   data          ;give byte to ACIA
  6904. ;     rts
  6905.  
  6906. ;Sample routine to fetch a character that has been received, from the
  6907. ;receive buffer.
  6908.  
  6909. ;by Craig Bruce, 1995, adapted from above
  6910.  
  6911. ;Will return the character in the 'A' register and the carry flag cleared if
  6912. ;a character was available.  If no character was available, will return with
  6913. ;the carry flag set.  Destroys the 'Y' register.
  6914.  
  6915. RECVBYTE:                 ;fetches a byte from the receive buffer.
  6916.                           ;there is no need to fiddle with any interrupts
  6917.  
  6918.       lda   recvcount     ;count of bytes in receive buffer
  6919.       beq   RECVEMPTY     ;buffer is empty, indicate to caller
  6920.  
  6921.       ldy   rhead         ;pointer to start of buffer
  6922.       lda   (rbuff),y     ;fetch byte out of buffer into 'A' register
  6923.       inc   rhead         ;point to next slot in buffer
  6924.       dec   recvcount     ;and add one to count of bytes in buffer
  6925.  
  6926.       clc                 ;indicate that we have a character
  6927.       rts                 ;return to your program
  6928.  
  6929. RECVEMPTY:
  6930.       sec                 ;or whatever flag your program uses to tell that the
  6931.                           ;receive buffer was empty
  6932.       rts                 ;and return
  6933.  
  6934. ;-----------------------------------------------------------------------------
  6935. ;Dumb -- very dumb -- terminal emulator.  Simply polls the receive buffer and
  6936. ;the keyboard and puts received data to the screen and typed data to the send
  6937. ;buffer (thus, it assumes a full-duplex, echoing link).  There is no
  6938. ;PETSCII->ASCII conversion, no cursor, nor any other fancy features.  Press
  6939. ;STOP to exit.
  6940. ;
  6941. ;by Craig Bruce, 1995.
  6942.  
  6943. TERMINAL:
  6944.       jsr   RECVBYTE      ;see if there is a received byte in the recv buffer
  6945.       bcs   TERMTRYSEND   ;if not, continue
  6946.       jsr   $FFD2         ;if received byte, print it to the screen (CHROUT)
  6947. TERMTRYSEND:
  6948.       jsr   $FFE4         ;try to get a character from the keyboard (GETIN)
  6949.       cmp   #$00          ;was there a keystroke available?
  6950.       beq   TERMINAL      ;no--go back to top of polling loop
  6951.       cmp   #$03          ;check for STOP key
  6952.       beq   TERMEXIT      ;  exit if pressed
  6953.       jsr   SENDBYTE      ;have char--put it into the transmit buffer and then
  6954.       jmp   TERMINAL      ;  go back to top of polling loop
  6955. TERMEXIT:
  6956.       lda   #%0000_0010   ;disable transmitter and receiver and all interrupts
  6957.       sta   command
  6958.       sei
  6959.       lda   OLDVEC        ;restore the NMI vector to its original value
  6960.       sta   NMINV
  6961.       lda   OLDVEC+1
  6962.       sta   NMINV+1
  6963.       cli
  6964.       rts                 ;exit
  6965.  
  6966. TRANSMIT_BUFFER = *+0
  6967. RECEIVE_BUFFER  = *+256
  6968. %%%---8<---cut-here---8<---%%%
  6969.  
  6970. ------------------------------------------------------------------------------
  6971.               APPENDIX: 6551 ACIA HARDWARE SPECS (DATA SHEET)
  6972.  
  6973.                      C= Commodore Semiconductor Group
  6974.               a division of Commodore Business Machines, Inc.
  6975.  950 Rittenhouse Road, Nornstown, PA 19400 * 215/666-7950 * TWX 510-660-4168
  6976.                                 (July 1987)
  6977.  
  6978.              6551 ASYNCHRONOUS COMMUNICATION INTERFACE ADAPTER
  6979.  
  6980. CONCEPT:
  6981.  
  6982. The 6551 is an Asynchronous Communication Adapter (ACIA) intended to provide
  6983. for interfacing the 6500/6800 microprocessor families to serial communication
  6984. data sets and modems.  A unique feature is the inclusion of an on-chip
  6985. programmable baud-rate generator, with a crystal being the only external
  6986. component required.
  6987.  
  6988. FEATURES:
  6989.  
  6990. * On-chip baud-rate generator: 15 programmable baud rates derived from a
  6991.   standard standard 1.8432 MHz external crystal (50 to 19,200 baud) [these
  6992.   rates are doubled in the SwiftLink].
  6993.  
  6994. * Programmable interrupt and status register to simplify software design.
  6995.  
  6996. * Single +5 volt power supply.
  6997.  
  6998. * Serial echo mode.
  6999.  
  7000. * False start bit detection.
  7001.  
  7002. * 8-bit bi-directional data bus for direct communication with the
  7003.   microprocessor.
  7004.  
  7005. * External 16x clock input for non-standard baud rates (up to 125 Kbaud).
  7006.  
  7007. * Programmable: word lengths; number of stop bits; and parity-bit generation
  7008.   and detection.
  7009.  
  7010. * Data set and modem control signals provided.
  7011.  
  7012. * Parity: (odd, even, none, mark, space).
  7013.  
  7014. * Full-duplex or half-duplex operation.
  7015.  
  7016. * 5,6,7 and 8-bit transmission.
  7017.  
  7018. * 1-MHz, 2-MHz, and 3-MHz operation.
  7019.  
  7020. ORDER NUMBER
  7021.  
  7022. MXS 6551 ___
  7023.  -        |
  7024.  |        +---- Frequency range
  7025.  |                  Plain = 1 MHz
  7026.  |                      A = 2 MHz
  7027.  |                      B = 3 MHz
  7028.  |
  7029.  +----------- Package Designator
  7030.                      C = Ceramic
  7031.                      P = Plastic
  7032.  
  7033. 6551 PIN CONFIGURATION
  7034.  
  7035.                 +---------------+
  7036.           GND --| 1          28 |-- R-/W
  7037.           CS0 --| 2          27 |-- o2
  7038.          /CS1 --| 3          26 |-- /IRQ
  7039.          /RES --| 4          25 |-- DB7
  7040.           RxC --| 5          24 |-- DB6
  7041.         XTAL1 --| 6          23 |-- DB5
  7042.         XTAL2 --| 7          22 |-- DB4
  7043.          /RTS --| 8          21 |-- DB3
  7044.          /CTS --| 9          20 |-- DB2
  7045.           TxD --| 10         19 |-- DB1
  7046.          /DTR --| 11         18 |-- DB0
  7047.           RxD --| 12         17 |-- /DSR
  7048.           RS0 --| 13         16 |-- /DCD
  7049.           RS1 --| 14         15 |-- Vcc
  7050.                 +---------------+
  7051.  
  7052. BLOCK DIAGRAM                                        +----------+
  7053.                                                      | TRANSMIT |
  7054.                                                      | CONTROL  |<------- CTS
  7055.                                                      +----------+
  7056.                                                            |
  7057.                                                            v
  7058.                                +----------+          +----------+
  7059.                                | TRANSMIT |          | TRANSMIT |
  7060.                          /|===>|   DATA   |=========>|  SHIFT   |-------> TxD
  7061.                          ||    | REGISTER |          | REGISTER |
  7062.                          ||    +----------+          +----------+
  7063.           +---------+    ||
  7064.    o2 --->|         |    ||    +----------+          +----------+
  7065.  R-/W --->|  SELECT |    ||====|  STATUS  |          | INTERRUPT|-------> /IRQ
  7066.   CS0 --->|   AND   |    ||    | REGISTER |<-------->|   LOGIC  |<------- /DCD
  7067.  /CS1 --->| CONTROL |    ||    +----------+          +----------+<------- /DSR
  7068.   RS0 --->|  LOGIC  |    ||
  7069.   RS1 --->|         |    ||    +----------+          +----------+
  7070.  /RES --->|         |    ||===>| CONTROL  |          | BAUD-RATE|<------> RxC
  7071.           +---------+    ||    | REGISTER |          | GENERATOR|<------- XTAL1
  7072.                          ||    +----------+          +----------+<------- XTAL2
  7073.                          ||
  7074.           +---------+    ||    +----------+          +----------+
  7075.   DB0 <-->|  DATA-  |    ||    |  RECEIVE |          |  RECEIVE |
  7076.   ...     |   BUS   |<===||====|   DATA   |<=========|   SHIFT  |<---+--- RxD
  7077.   DB7 <-->| BUFFERS |    ||    | REGISTER |          | REGISTER |    |
  7078.           +---------+    ||    +----------+          +-----.----+    |
  7079.                          ||                                |         |
  7080.                          ||    +----------+          +----------+    |
  7081.    LEGEND:               \|===>| COMMAND  |          |  RECEIVE |    |
  7082.                                | REGISTER |          |  CONTROL |<---+
  7083.    ===> : 8-bit line           +----------+          +----------+
  7084.                                   |    |
  7085.    ---> : 1-bit line              |    +--------------------------------> /DTR
  7086.                                   +-------------------------------------> /RTS
  7087.  
  7088. MAXIMUM RATINGS
  7089.  
  7090. <not included here>
  7091.  
  7092. ELECTRICAL CHARACTERISTICS
  7093.  
  7094. <not included here>
  7095.  
  7096. POWER DISSIPATION vs TEMPERATURE
  7097.  
  7098. <not included here>
  7099.  
  7100. TIMING CHARACTERISTICS
  7101.  
  7102. <not included here>
  7103.  
  7104. INTERFACE SIGNAL DESCRIPTION
  7105.  
  7106. /RES (Reset)
  7107.  
  7108. During system initialization a low on the /RES input will cause internal
  7109. registers to be cleared.
  7110.  
  7111. o2 (Input Clock)
  7112.  
  7113. The input clock is the system o2 clock and is used to trigger all data
  7114. transfers between the system microprocessor and the 6551.
  7115.  
  7116. R-/W (Read/Write)
  7117.  
  7118. The R-/W is generated by the microprocessor and is used to control the
  7119. direction of data transfers.  A high on the R-/W pin allows the processor
  7120. to read the data supplied by the 6551.  A low on the R-/W pin allows a write
  7121. to the 6551.
  7122.  
  7123. /IRQ (Interrupt Request)
  7124.  
  7125. The /IRQ pin is an interrupt signal from the interrupt-control logic.  It is
  7126. an open drain output, permitting several devices to be connected to the common
  7127. /IRQ microprocessor input.  Normally a high level, /IRQ goes low when an
  7128. interrupt occurs.
  7129.  
  7130. DB0--DB7 (Data Bus)
  7131.  
  7132. The DB0--DB7 pins are the eight data lines used for transfer of data between
  7133. the processor and the 6551.  These lines are bi-directional and are normally
  7134. high-impedance except during Read cycles when selected.
  7135.  
  7136. CS0, /CS1 (Chip Selects)
  7137.  
  7138. The two chip-select inputs are normally connected to the processor-address
  7139. lines either directly or through decoders.  The 6551 is selected when CS0 is
  7140. high and /CS1 is low.
  7141.  
  7142. RS0, RS1 (Register Selects)
  7143.  
  7144. The two register-select lines are normally connected to the processor-address
  7145. lines to allow the processor to select the various 6551 internal registers.
  7146. The following table indicates the internal register-select coding:
  7147.  
  7148. RS1   RS0   WRITE                     READ                    SL-Addr
  7149. ---   ---   ----------------------    ---------------------   -------
  7150.   0     0   Transmit Data Register    Receive Data Register     $DE00
  7151.   0     1   Programmed Reset*         Status Register           $DE01
  7152.   1     0   Command Register          Command Register          $DE02
  7153.   1     1   Control Register          Control Register          $DE03
  7154.  
  7155.                 * for programmed reset, data is "don't care".
  7156.  
  7157. The table shows that only the Command and Control registers are read/write.
  7158. The Programmed Reset operation does not cause any data transfer, but is used
  7159. to clear the 6551 registers.  The Programmed Reset is slightly different from
  7160. the Hardware Reset (/RES) and these differences are described in the
  7161. individual register definitions.
  7162.  
  7163. ACIA/MODEM INTERFACE SIGNAL DESCRIPTION
  7164.  
  7165. XTAL1, XTAL2 (Crystal Pins)
  7166.  
  7167. These pins are normally directly connected to the external crystal (1.8432
  7168. MHz) used to derive the various baud rates.  Alternatively, an externally
  7169. generated clock may be used to drive the XTAL1 pin, in which case the XTAL2
  7170. pin must float.  XTAL1 is the input pin for the transmit clock.
  7171.  
  7172. TxD (Transmit Data)
  7173.  
  7174. The TxD output line is used to transfer serial NRZ (non-return-to-zero) data
  7175. to the modem.  The LSB (least-significant bit) of the Transmit Data Register
  7176. is the first data bit transmitted and the reate of data transmission is
  7177. determined by the baud rate selected.
  7178.  
  7179. RxD (Receive Data)
  7180.  
  7181. The RxD input line is used to transfer serial NRZ data into the ACIA from the
  7182. modem, LSB first.  The receiver data rate is either the programmed baud rate
  7183. or the rate of an externally generated receiver clock.  This selection is made
  7184. by programming the Control Register.
  7185.  
  7186. RxC (Receive Clock)
  7187.  
  7188. The RxC is a bi-directional pin which serves as either the receiver 16x clock
  7189. input or the receiver 16x clock output.  The latter mode results if the
  7190. internal baud rate generator is selected for receiver data clocking.
  7191.  
  7192. /RTS (Request to Send)
  7193.  
  7194. The /RTS output pin is used to control the modem from the processor.  The
  7195. state of the /RTS pin is determined by the contents of the Command Register.
  7196.  
  7197. /CTS (Clear to Send)
  7198.  
  7199. The /CTS input pin is used to control the transmitter operation.  The enable
  7200. state is with /CTS low.  The transmitter is automatically disabled if /CTS is
  7201. high.
  7202.  
  7203. /DTR (Data Terminal Ready)
  7204.  
  7205. The output pin is used to indicate the status of the 6551 to the modem.  A low
  7206. of /DTR indicates the 6551 is enabled and a high indicates it is disabled.
  7207. The processor controls this pin via bit 0 of the Command Register.
  7208.  
  7209. /DSR (Data Set Ready)
  7210.  
  7211. The /DSR input pin is used to indicate to the 6551 the status of the modem.  A
  7212. low indicates the "ready" state and a high, "not-ready".  /DSR is a high-
  7213. impedance input and must not be a no-connect.  If unused, it should be driven
  7214. high or low, but not switched.
  7215.  
  7216. Note: If Command Register Bit #0 = 1 and a change of state on /DSR occurs,
  7217. /IRQ will be set and Status Register Bit #[5] will reflect the new level.  The
  7218. state of /DSR does not affect Transmitter operation [but must be low for the
  7219. Receiver to operate].  [This statement reflects the SwiftLink implementation].
  7220.  
  7221. /DCD (Data Carrier Detect)
  7222.  
  7223. The /DCD input pin is used to indicate to the 6551 the status of the carrier-
  7224. detect output of the modem.  A low indicates that the modem carrier signal is
  7225. present and a high, that it is not.  /DCD, like /DSR, is a high-impedance
  7226. input and must not be a no-connect.
  7227.  
  7228. Note: If Command Register Bit #0 = 1 and a change of state on /DSR occurs,
  7229. /IRQ will be set and Status Register Bit #[6] will reflect the new level.  The
  7230. state of /DCD does not affect either Transmitter or Receiver operation.
  7231.  
  7232. INTERNAL ORGANIZATION
  7233.  
  7234. <not included here>
  7235.  
  7236. TRANSMIT AND RECEIVE DATA REGISTERS (SL-Addr: $DE00 / 56832)
  7237.  
  7238. These registers are used as temporary data storage for the 6551 Transmit and
  7239. Receive circuits.  The Transmit Data Register is characterized as follows:
  7240.  
  7241. * Bit 0 is the leading bit to be transmitted.
  7242.  
  7243. * Unused data bits are the high-order bits and are "don't care" for
  7244.   transmission.
  7245.  
  7246. The Receive Data Register is characterized in a similar fashion:
  7247.  
  7248. * Bit 0 is the leading bit received.
  7249.  
  7250. * Unused data bits are the high-order bits and are "0" for the receiver.
  7251.  
  7252. * Parity bits are not contained in the Receive Data Register, but are stripped
  7253.   off after being used for external parity checking.  Parity and all unused
  7254.   high-order bits are "0".
  7255.  
  7256.            Transmit / Receive Data Register
  7257.   +-----+-----+-----+-----+-----+-----+-----+-----+
  7258.   |  7  |  6  |  5  |  4  |  3  |  2  |  1  |  0  |
  7259.   +-----+-----+-----+-----+-----+-----+-----+-----+
  7260.   |                     data                      |
  7261.  
  7262.   The following figure illustrates a single transmitted or received data
  7263.   word, for the example of 8 data bits, parity, and 1 stop bit:
  7264.  
  7265.   "MARK"____    ___________________________________________________"MARK"
  7266.            |    | 0  | 1  | 2  | 3  | 4  | 5  | 6  | 7  | P  | S  .
  7267.            |____|____|____|____|____|____|____|____|____|____|
  7268.             start                                            parity  stop
  7269.              bit                ...data bits...               bit     bit
  7270.  
  7271.  
  7272. STATUS REGISTER (SL-Addr: $DE01 / 56833)
  7273.  
  7274. The Status Register is used to indicate to the processor the status of various
  7275. 6551 functions and is outlined here:
  7276.  
  7277.                    Command Register
  7278.   +-----+-----+-----+-----+-----+-----+-----+-----+
  7279.   |  7  |  6  |  5  |  4  |  3  |  2  |  1  |  0  |
  7280.   +-----+-----+-----+-----+-----+-----+-----+-----+
  7281.   | irq | dcd | dsr | txr | rxr | ovr | fe  | pe  |
  7282.  
  7283.   +---+
  7284.   | 7 |   /IRQ*** : cleared by reading status register
  7285.   +---+   --------------------------------------------
  7286.     0     No interrupt
  7287.     1     Interrupt
  7288.  
  7289.   +---+
  7290.   | 6 |   /DCD : non-resetable, indicates /DCD status
  7291.   +---+   --------------------------------------------
  7292.     0     /DCD low
  7293.     1     /DCD high
  7294.  
  7295.   +---+
  7296.   | 5 |   /DSR : non-resetable, indicates /DSR status
  7297.   +---+   --------------------------------------------
  7298.     0     /DSR low
  7299.     1     /DSR high
  7300.  
  7301.   +---+
  7302.   | 4 |   Transmit Data Register Empty: Cleared by write to Tx Data reg
  7303.   +---+   -------------------------------------------------------------
  7304.     0     Not empty
  7305.     1     Empty
  7306.  
  7307.   +---+
  7308.   | 3 |   Receive Data Register Full: Cleared by read from Rx Data reg
  7309.   +---+   -------------------------------------------------------------
  7310.     0     Not full
  7311.     1     Full
  7312.  
  7313.   +---+
  7314.   | 2 |   Overrun*: Self-clearing**
  7315.   +---+   -------------------------
  7316.     0     No error
  7317.     1     Error
  7318.  
  7319.   +---+
  7320.   | 1 |   Framing Error*: Self-clearing**
  7321.   +---+   -------------------------------
  7322.     0     No error
  7323.     1     Error
  7324.  
  7325.   +---+
  7326.   | 0 |   Parity Error*: Self-clearing**
  7327.   +---+   ------------------------------
  7328.     0     No error
  7329.     1     Error
  7330.  
  7331.   Notes:   * No interrupt generated for these conditions
  7332.           ** Cleared automatically after a read of RDR and the next error-
  7333.                free receipt of data
  7334.          *** Reading status reg. will clear the /IRQ bit except when
  7335.                transmit intr. enabled
  7336.  
  7337.     7   6   5   4   3   2   1   0
  7338.   +---+---+---+---+---+---+---+---+
  7339.   | 0 | x | x | 1 | 0 | 0 | 0 | 0 |  After Hardware reset
  7340.   +---+---+---+---+---+---+---+---+
  7341.   | x | x | x | x | x | 0 | x | x |  After Software reset
  7342.   +---+---+---+---+---+---+---+---+
  7343.  
  7344.  
  7345. COMMAND REGISTER (SL-Addr: $DE02 / 56834)
  7346.  
  7347. The Command Register is used to control specific Transmit/Receive functions
  7348. and is shown here:
  7349.  
  7350.                    Command Register
  7351.   +-----+-----+-----+-----+-----+-----+-----+-----+
  7352.   |  7  |  6  |  5  |  4  |  3  |  2  |  1  |  0  |
  7353.   +-----+-----+-----+-----+-----+-----+-----+-----+
  7354.   |     parity      | echo|  tx ctrl  | rxi | dtr |
  7355.  
  7356.   +---+---+---+
  7357.   | 7 | 6 | 5 |   PARITY CHECK CONTROLS
  7358.   +---+---+---+   ----------------------
  7359.     x   x   0     parity disabled--no parity bit generated or received
  7360.     0   0   1     odd parity receiver and transmitter
  7361.     0   1   1     even parity receiver and transmitter
  7362.     1   0   1     mark parity transmitted, parity check disabled
  7363.     1   1   1     space parity transmitted, parity check disabled
  7364.  
  7365.   +---+
  7366.   | 4 |   NORMAL/ECHO MODE FOR RECEIVER
  7367.   +---+   ------------------------------
  7368.     0     Normal
  7369.     1     Echo (bits 2 and 3 must be "0")
  7370.  
  7371.   +---+---+
  7372.   | 3 | 2 |   Tx INTERRUPT    RTS LEVEL    TRANSMITTER
  7373.   +---+---+   ------------    ---------    ------------
  7374.     0   0       Disabled         High           Off
  7375.     0   1       Enabled          Low            On
  7376.     1   0       Disabled         Low            On
  7377.     1   1       Disabled         Low       Transmit BRK
  7378.  
  7379.   +---+
  7380.   | 1 |   RECEIVE INTERRUPT ENABLE
  7381.   +---+   -------------------------
  7382.     0     /IRQ interrupt Enabled from bit 3 of Status Register
  7383.     1     /IRQ interrupt Disabled
  7384.  
  7385.   +---+
  7386.   | 0 |   DATA TERMINAL READY
  7387.   +---+   --------------------
  7388.     0     Disable Receiver and all interrupts (/DTR high)
  7389.     1     Enable Receiver and all interrupts  (/DTR low)
  7390.  
  7391.     7   6   5   4   3   2   1   0
  7392.   +---+---+---+---+---+---+---+---+
  7393.   | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |  After Hardware reset
  7394.   +---+---+---+---+---+---+---+---+
  7395.   | x | x | x | 0 | 0 | 0 | 0 | 0 |  After Software reset
  7396.   +---+---+---+---+---+---+---+---+
  7397.  
  7398.  
  7399. CONTROL REGISTER (SL-Addr: $DE03 / 56835 / cpm: 0001xxxx)
  7400.  
  7401. The Control Register is used to select the desired mode for the 6551.  The
  7402. word length, number of stop bits, and clock controls are all determined
  7403. by the Control Register, which is shown here:
  7404.  
  7405.                    Control Register
  7406.   +-----+-----+-----+-----+-----+-----+-----+-----+
  7407.   |  7  |  6  |  5  |  4  |  3  |  2  |  1  |  0  |
  7408.   +-----+-----+-----+-----+-----+-----+-----+-----+
  7409.   |stops|  word len | src |       baud rate       |
  7410.  
  7411.   +---+
  7412.   | 7 |   STOP BITS
  7413.   +---+   ----------
  7414.     0     1 stop bit
  7415.     1     2 stop bits
  7416.     1     1 stop bit if word length== 8 bits and parity
  7417.               this allows for 9-bit transmission (8 data bits plus parity)
  7418.     1     1.5 stop bits if word length== 5 bits and no parity
  7419.  
  7420.   +---+---+
  7421.   | 6 | 5 |   WORD LENGTH
  7422.   +---+---+   ------------
  7423.     0   0     8 bits
  7424.     0   1     7 bits
  7425.     1   0     6 bits
  7426.     1   1     5 bits
  7427.  
  7428.   +---+
  7429.   | 4 |   RECEIVER CLOCK SOURCE
  7430.   +---+   ----------
  7431.     0     external receiver clock
  7432.     1     baud rate generator
  7433.  
  7434.   +---+---+---+---+
  7435.   | 3 | 2 | 1 | 0 |   BAUD RATE GENERATOR
  7436.   +---+---+---+---+   --------------------
  7437.     0   0   0   0     16x external clock
  7438.     0   0   0   1     100 baud
  7439.     0   0   1   0     150 baud
  7440.     0   0   1   1     219.84 baud
  7441.     0   1   0   0     269.16 baud
  7442.     0   1   0   1     300 baud
  7443.     0   1   1   0     600 baud
  7444.     0   1   1   1     1200 baud
  7445.     1   0   0   0     2400 baud
  7446.     1   0   0   1     3600 baud
  7447.     1   0   1   0     4800 baud
  7448.     1   0   1   1     7200 baud
  7449.     1   1   0   0     9600 baud
  7450.     1   1   0   1     14400 baud
  7451.     1   1   1   0     19200 baud
  7452.     1   1   1   1     38400 baud
  7453.  
  7454.     7   6   5   4   3   2   1   0
  7455.   +---+---+---+---+---+---+---+---+
  7456.   | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |  After Hardware reset
  7457.   +---+---+---+---+---+---+---+---+
  7458.   | x | x | x | x | x | x | x | x |  After Software reset
  7459.   +---+---+---+---+---+---+---+---+
  7460.  
  7461. ========================================================================
  7462. Design and Implementation of a Simple/Efficient Upload/Download Protocol
  7463. by Craig Bruce  <csbruce@ccnga.uwaterloo.ca>
  7464.  
  7465. 1. INTRODUCTION
  7466.  
  7467. If you use your Commodore for telecommunications, then you are basically
  7468. interested in two things: using your C= to emulate a terminal for interactive
  7469. stuff, and using modem-file-transfer protocols to upload and download files
  7470. from and to your Commodore.
  7471.  
  7472. This document describes a custom upload/download protocol that was designed
  7473. for use with the ACE-128/64 system and is freely available to anyone who wants
  7474. it (well, when I finish with the Release #14 of ACE).  While this protocol
  7475. non-standard, it blows the doors off of all other protocols available for
  7476. Commodore computers, even though it uses a simple "stop-and-wait"
  7477. acknowledgement scheme.  There are two reasons for its speed: the fast device
  7478. drivers available with ACE, and its large packet size, up to about 18K
  7479. (although this could be significantly larger is ACE's memory usage were
  7480. reorganized).
  7481.  
  7482. The name of the protocol is "Craig's File eXchange Protocol", or just "FX" for
  7483. short.  It is "file exchange" rather than "upload" or "download" because you
  7484. will use the same activation of the program to both upload and download all of
  7485. the files you name.
  7486.  
  7487. 2. USAGE
  7488.  
  7489. The current implementation of FX consists of a "client" program for you to run
  7490. on your Commodore computer and a "server" program that you run on your Unix
  7491. host.  There is currently no server program for any other platform, but the
  7492. necessary changes to the C-language program wouldn't be too hard.  The client
  7493. program is written in 6502 assembler, of course (for the ACE-assembler to be
  7494. specific).
  7495.  
  7496. FX is an external program from the terminal program, so (for now) to activate
  7497. FX, you have to exit from the terminal program and enter the FX command line,
  7498. exchange the files, and then re-enter the terminal program from the command
  7499. line.
  7500.  
  7501. When you run FX, you will activate the Server program first on your Unix host
  7502. and then exit the terminal program and run the Client program on your
  7503. Commodore.  You run the command "fx" on both the client and server machines,
  7504. which may be a little confusing (but I think you'll get used to it), and name
  7505. the files that you want to have transferred as arguments to the command on the
  7506. machine that you want to transfer the files FROM.  The usage of the "fx"
  7507. command is as follows:
  7508.  
  7509. fx [-dlvV7] [-m maximums] [-f argfile] [[-b] binfile ...] [-t textfile ...]
  7510.  
  7511. -d = debug mode
  7512. -l = write to log file ("fx.log")
  7513. -v = verbose log/debug mode
  7514. -V = extremely verbose log/debug mode
  7515. -7 = use seven-bit encoding
  7516. -m = set maximum packet sizes; maximums = ulbin/ultxt/dlbin/dltxt (bytes)
  7517. -f = take arguments one-per-line from given argfile
  7518. -b = binary files prefix
  7519. -t = text files prefix
  7520. -help = help
  7521.  
  7522. well, for the server, anyway.  The client program doesn't have the more
  7523. exotic options.  The "-d", "-l", "-v", and "-V" options are available only
  7524. on the Server program, and are for debugging purposes only.
  7525.  
  7526. The "-7" option tells the protocol to use only 7-bit data.  I.e., it tells it
  7527. to not use the 8th bit position in the data is transmitted.  This is useful if
  7528. you are forced into the humiliation of only being able to use a 7-bit channel
  7529. to your Unix host.  You need only need to give this option on either the
  7530. client or the host command line and the other side will be informed.  It may
  7531. be useful to create an alias for this command with all of your options set to
  7532. what you want them to be.
  7533.  
  7534. The protocol has the capacity to use different packet sizes for four types of
  7535. file-transfer situations: uploading binary data, uploading text, downloading
  7536. binary data, and downloading text.  These are useful distinctions, since your
  7537. host may or may not be able to handle the larger packet sizes without losing
  7538. bytes (your Commodore, of course, can handle the larger packet sizes with no
  7539. problems).
  7540.  
  7541. In determining which packet size to use for a file transfer (where the type of
  7542. transfer is known), the protocol finds that largest packet size that both the
  7543. client and the server can handle and then take the minimum of these two
  7544. values.  The defaults for the client are all the same: the maximum amount of
  7545. program-area memory that it can use, about 18K.  For the server program, I
  7546. have programmed in default maximum uploading packet sizes of 1K and maximum
  7547. downloading packet sizes of 64K-1.  You can change these defaults in the C
  7548. program easily by changing some "#define"s.
  7549.  
  7550. The "-m" option allows you to manually set the default packet sizes for a
  7551. transfer.  The argument following the "-m" flag should have four numbers with
  7552. slashes between them, which give the maximum ulbin/ultxt/dlbin/dltxt packet
  7553. sizes, respectively.  Note that the packet sizes only include the size of the
  7554. user data encoded into packets and not the control or quoting information
  7555. (below).
  7556.  
  7557. The "-f" option on the server allows you to read arguments from a file rather
  7558. than the command line.  This is useful if want to generate and edit the list
  7559. of files to download before you run the FX command.  It's also useful if you
  7560. don't want other users to see the names of the files that you are
  7561. downloading.  The name of the file comes in the first argument following the
  7562. "-f" flag and the arguments are put into this file one-per-line.  You can put
  7563. in "-" options in addition to filenames if you wish (like "-t" and "-b").
  7564. This option is not supported on the client program.
  7565.  
  7566. Finally come the "-b", "-t", and filename arguments.  The "-b" argument tells
  7567. FX that all of the following filenames (until the next "-t" option) are binary
  7568. files and the "-t" argument says that the following filenames are all of text
  7569. files.  You can use as many "-b" and "-t" arguments as you want.  If you don't
  7570. use any, then all of the files you name will be assumed to be binary files.
  7571.  
  7572. For each filename you give on a command line, that file will be transferred
  7573. from that machine to the other machine.  On both Unix and ACE, you can use
  7574. wildcards in your filenames, of course, to transfer groups of files.
  7575.  
  7576. The client program controls the file exchange, and it uploads all of its files
  7577. first and then asks the server if the server has any files to be downloaded.
  7578. When the exchange is completed, both the client and server FX programs will
  7579. exit and you will find yourself back on the command lines in both
  7580. environments.  Re-enter the terminal program to continue with your online
  7581. session.  If something goes very wrong during a transfer or if you decide that
  7582. you don't really want to transfer any files after activating the server
  7583. program, you can type three Ctrl-X's to abort the server.  This is the same as
  7584. for the X-modem protocol.
  7585.  
  7586. 3. DESIGN DECISIONS
  7587.  
  7588. There are a number of design decisions to be made about our protocol.  But
  7589. first, we want to recognize and appreciate that since we have a license to
  7590. design a completely new protocol, we are not bound, shackled, gagged, and
  7591. tortured by the "hysterical raisins" and bad design decisions of existing
  7592. compromised and bloated standard protocols... such as Z-modem.
  7593.  
  7594. We want the protocol to understand whether a file is text or binary data and
  7595. to translate them appropriately during downloading.  We want the protocol to
  7596. be aware of filenames, dates, permissions, and we do not want our file
  7597. contents to get mangled like they do with X-modem (it pads them with Ctrl-Z's,
  7598. since it was designed for CP/M), and we want it to translate to/from PETSCII
  7599. if the file is text.  We will require that the user tell us whether the file
  7600. is binary or text (although we may be able to statistically determine this
  7601. from snooping through the file), and we will use a "canonical form" for
  7602. encoding the text data during transfer.  A convenient canonical form to use is
  7603. Unix-ASCII (ASCII-LF).
  7604.  
  7605. We want our protocol to be simultaneously simple and fast.  To make it simple,
  7606. we will use a stop-and-wait acknowledgement scheme.  This means that after
  7607. each packet is uploaded or downloaded, the transfer will pause and wait for
  7608. the receiving host to acknowledge that the packet has been transferred
  7609. correctly, and only then will the protocol continue to transfer more data.
  7610.  
  7611. In fact, this scheme fits well with the Commodore hardware, since it is not
  7612. possible to send or receive serial data while doing disk I/O (in the general
  7613. case), so we would have to stop listening anyway; the protocol makes it so
  7614. that there will be no bytes that we end up ignoring while doing I/O.
  7615.  
  7616. To make the protocol be fast even though we are using a stop-and-wait
  7617. acknowledgement scheme, we will use the largest data-packet sizes that we
  7618. possibly can.  In the (current) ACE environment, this means about 18K.  This
  7619. will maximize the amount of time of transferring data over the modem between
  7620. pauses to do I/O.  If the I/O is to the ACE ramdisk, then the length of this
  7621. pause will be very short and we will achieve a very high link utilization.
  7622. (The ACE ramdisk can process an 18K read/write request in about 20
  7623. milliseconds on a Fast-mode C128 using an REU --- RAMDOS in the same
  7624. environment would require about 9 _seconds_ (450x slower)).
  7625.  
  7626. To allow for future use with other platforms, we will make the protocol define
  7627. the packet sizes using 32-bit fields.  There isn't much data overhead, and
  7628. this allows us to change implementations to be able to transfer entire files
  7629. in one large packet.  Also, the size of an individual packet should be
  7630. flexible: be from one to N bytes.  This eliminates the X-modem padding problem
  7631. and the Y-modem crufty hack of using the small packet size when less than 1K
  7632. of user data remains to be transferred.
  7633.  
  7634. We also want our data to be well protected against corruption.  Detecting
  7635. transmission errors efficiently on Commodore computers is already a well
  7636. solved problem: we will use a table-driven CRC-32 algorithm, the same one that
  7637. ZMODEM, PKZIP, and CRC32 use.  To hide the computation costs of the CRC even
  7638. more (the cost is very low anyway), we will compute it WHILE sending or
  7639. receiving packets.  Oh, actually, I guess that I forgot to mention an a-priori
  7640. design decision: we will be using a packet-oriented approach for transferring
  7641. data (described below); packetization offers so many advantages that this
  7642. decision is really a no-brainer.
  7643.  
  7644. Also, to make the process interaction as straightforward as possible, we want
  7645. to use the Client/Server programming paradigm.  This paradigm combines well
  7646. with the stop-and-wait acknowledgement scheme to produce a Remote Procedure
  7647. Call (RPC) type of interaction between the machines.  For those not familiar
  7648. with this Interprocess Communication (IPC) scheme, you can read a couple
  7649. issues of C= hacking ago where I talked about it for use with a multitasking
  7650. operation system.  RPC is a very useful, powerful, simple, and widely
  7651. applicable IPC scheme.
  7652.  
  7653. To recover from packet corruption, we will be using a timeout+retransmission
  7654. scheme, and to be consistent with the RPC scheme, the client will do all
  7655. timeouts and retransmissions.  This means that after sending a request RPC
  7656. packet out, if we don't receive the reply within a certain period of time, we
  7657. will timeout and send the request again.  Or, to be more precise, since we
  7658. will be working with large packet sizes, we will timeout if we don't receive
  7659. any bytes from the server for a certain period of time, say 5 seconds, while
  7660. we are expecting more bytes from him.
  7661.  
  7662. The way that corrupted packets are dealt with is very simple: they are
  7663. ignored.  The server could possibly send back a negative acknowledgement,
  7664. but we won't try that for now.
  7665.  
  7666. In order to make retransmissions work out correctly, we will be using sequence
  7667. numbers and internal-state variables inside of the server to insure that
  7668. requests aren't carried out more than once.  We need these mechanisms because
  7669. when an RPC fails, we won't know if we got no response because the original
  7670. request was lost and the operations wasn't carried out, or whether the request
  7671. was received and carried out but the reply message was lost.
  7672.  
  7673. For example, if we request that packet #123 be downloaded and the server
  7674. carries out that request but the reply message is lost, then the client will
  7675. time out and retransmit the request.  The server remembers the last request
  7676. number that the client sent it (123 here), so if the client asks for packet
  7677. #123 again, the server will simply retransmit the reply that it gave last
  7678. time.  If, on the other hand, the client were to request packet #124 (or
  7679. simply "not 123"), then the server reads the next chunk of data from the file
  7680. and sends it as the reply.  Our protocol will use an 8-bit sequence number
  7681. even though it only needs a 1-bit sequence number (since eight bits will allow
  7682. for the future expansion of having multiple requests being processed
  7683. concurrently: asynchronous RPC).
  7684.  
  7685. We also want to be able to both upload and download as conveniently as
  7686. possible.  To me, this means doing both operations by calling only one command
  7687. (as described in the previous section).  This arrangement also allows for the
  7688. future expansion of uploading and downloading files _simultaneously_ (the
  7689. protocol as designed places no restrictions on this possibility).
  7690.  
  7691. We also want to make use of an eight-bit clean link between the Unix host and
  7692. your Commodore, but this may not always be possible.  Sometimes you may have
  7693. only a 7-bit connection, and even if you do have an 8-bit connection, there
  7694. may still be some software-flow-control problems with intermediate devices
  7695. between your Commodore and your Unix host.  So, we want our protocol to not
  7696. make use of the X-on and X-off characters, and to use only 7-bit characters if
  7697. it cannot use eight.  The way to achieve this is called "escaping", "quoting"
  7698. or "byte stuffing", and will be discussed in the next section.  It turns out
  7699. that supporting 7-bit characters is pretty simple and the mechanism is
  7700. required by other aspects of the packetization.
  7701.  
  7702. There, that should take care of most of the major design decisions.
  7703.  
  7704. 4. PACKETIZATION
  7705.  
  7706. Packetization refers to the process of taking a stream of data and breaking it
  7707. up into discrete chunks of data.  Each packet is easily identified and is
  7708. processed as a single unit.  There are many general advantages to using
  7709. packets.  If there is a transmission error, then only a single packet is
  7710. corrupted, and the recovery will be easier since the packet is well
  7711. identified, and only it needs to be recovered.  Packetization also means that
  7712. a link can be shared between multiple (logical) communication streams fairly
  7713. and efficiently, and means that a single communication stream can utilize
  7714. multiple physical links where facilities exist.
  7715.  
  7716. Packets also integrate well with many IPC schemes, including Remote Procedure
  7717. Calls.  In fact, you end up emulating a packet-oriented scheme even if you are
  7718. using RPC over a stream-oriented transport system.  Packets also take into
  7719. account the limited buffering capacity of both end systems and intermediate
  7720. systems, and allow for the convenient implementation of flow control (even if
  7721. said flow control consists of simply dropping packets on the floor).  Packets
  7722. are very useful things indeed!  And just think that back in the early 1970s
  7723. packets were dismissed as being infeasible and unusable.
  7724.  
  7725. Each packet used in the FX system has four parts to it: the start character,
  7726. the user data (payload), the error-check characters, and the end character.
  7727. Graphically, a packet has the following format:
  7728.  
  7729. +------------------------+-----------+--------------+----------------------+
  7730. |  Start-of-packet Char  |  Payload  |  ErrorCheck  |  End-of-packet Char  |
  7731. +------------------------+-----------+--------------+----------------------+
  7732.  
  7733. The payload can be arbitrarily long, up to whatever limit the two computers
  7734. involved in the transfer can handle.
  7735.  
  7736. The error check is a 32-bit (4-byte) Cyclic-Redundancy-Check value that
  7737. occupies the last four bytes before the End-of-packet character.  The
  7738. implementation, which is based on a table-lookup method, is so efficient that
  7739. it is as fast as a simple add-up checksum, except much more reliable.  Using
  7740. this error check, there will be approximately a one-in-4,000,000,000 chance
  7741. that a packet with an error in it will be accepted has being error-free.
  7742. These are pretty good odds for our purposes.  The CRC is calculated
  7743. exclusively on the raw payload data.
  7744.  
  7745. The following special characters used by packets are defined:
  7746.  
  7747. NAME         HEX   DEC   Control   Meaning
  7748. ---------   ----   ---   -------   --------
  7749. CHR_START   0x01     1   Ctrl-A    Packet-start indicator
  7750. CHR_END     0x19    25   Ctrl-Y    Packet-end indicator
  7751. CHR_ESC     0x05     5   Ctrl-E    Escape character for next code
  7752. CHR_ABORT   0x18    24   Ctrl-X    Abort transfer if repeated three times
  7753. CHR_XON     0x11    17   Ctrl-Q    Software flow-start: avoided
  7754. CHR_XOFF    0x13    19   Ctrl-S    Software flow-stop: avoided
  7755. CHR_QUOTE8  0x14    20   Ctrl-T    Quote-8 the next 7-bit sequence
  7756.  
  7757. CHR_START is used to signify the start of a new packet.  This character is
  7758. not allowed to be used anywhere else for any other purpose.
  7759.  
  7760. CHR_END is used to signify the end of the current packet, and cannot be used
  7761. anywhere else.  The reason for using special characters to mark the beginning
  7762. and the ending of a packet is to allow for easy error recovery after a
  7763. communication failure.  All you do is search for the next CHR_START character
  7764. after you toss away a garbled packet and you're back in business.  I am
  7765. unaware of any reasonable alternatives to framing packets with a CHR_START
  7766. character.  Using a CHR_END special character is a convenience.
  7767.  
  7768. CHR_ESC is used to "escape" the next character.  Since there are special
  7769. character codes that cannot be used in any other way than their intended
  7770. function (including CHR_START and CHR_ESC itself), this character is needed.
  7771. The character following the CHR_ESC character must be between "@" and "_"
  7772. (0x40 and 0x5f) in the ASCII chart, or be the character "?" (0x3f).  The
  7773. character following the CHR_ESC is then "and"ed with the value 0x1f to mask
  7774. off the "letter" bits and turn it into a control character in the range of
  7775. 0x00 to 0x1f (the same range as the special control characters) and the
  7776. "escape sequence" is treated as a single character of user data.  If the
  7777. character following the CHR_ESC is a "?", then a code of 0x7f is interpreted
  7778. instead.  Using a character following the escape that is different from the
  7779. character being represented allows for greater resiliance of the protocol in
  7780. the presence of bits being garbled or bytes being dropped.  All special
  7781. characters in a packet except for the starting and ending characters are
  7782. escaped as described above.
  7783.  
  7784. CHR_ABORT can be typed by the user into a terminal program at any time to shut
  7785. down the server.
  7786.  
  7787. CHR_XON and CHR_XOFF can cause problems with intermediate devices on some
  7788. systems, so the FX protocol does not use these character codes at all; it
  7789. purposely avoids them and uses escape sequences (CHR_ESC) for them instead.
  7790.  
  7791. CHR_QUOTE8 is used to re-generate 8-bit data over a 7-bit link.  Kermit uses
  7792. this same technique.  When this character is encountered in the receive
  7793. stream, the next character is extracted and is "or"ed with a value of 0x80 to
  7794. give it a "1" in the high-bit position.  The CHR_QUOTE8 character can also be
  7795. followed by a CHR_ESC code, which is interpreted as above and then "or"ed with
  7796. the 0x80 value.
  7797.  
  7798. One of the disadvantages of using this scheme is that each byte in the range
  7799. of 0x80 and 0xff takes at least two bytes to transmit and some of them three.
  7800. If fact, for many binary files it may be faster to uuencode the file and
  7801. transfer the resulting text, since uucode has a static encoding overhead of
  7802. 33% whereas this quoting scheme has an expected overhead of 50% (plus the
  7803. CHR_ESC overhead).  Of course, this feature is intended to be used as a last
  7804. resort if you cannot get an 8-bit connection.
  7805.  
  7806. So there you have it.  Every message sent between the client and the server
  7807. is encapsulated in a packet as specified above.  Packetization allows for
  7808. convenient error detection and recovery and works well with our interprocess
  7809. communication scheme.
  7810.  
  7811. One implementation note about the packetization has to do with buffering.  On
  7812. the Unix host, it is advantageous to encode a packet into a memory buffer and
  7813. then send out that buffer in a single "write" operation.  This less operating-
  7814. system overhead (which may or may not be significant) but more importantly,
  7815. it means that the packet will be sent between intermediate communication
  7816. devices as efficiently as possible.  On my local Unix system, I connect to
  7817. a terminal server and to my Unix host through that.  Performing single-byte
  7818. writes on the Unix host means that the bytes are sent in individual Ethernet
  7819. packets between the Unix host and the terminal server, and encounter more
  7820. overhead and communication delays.  When I changed the program to send the
  7821. FX packet in a single operation, a significant performance gain was realized.
  7822.  
  7823. For receiving data on the Unix host, there isn't much you can do other than
  7824. reading one byte at a time, since the receiver doesn't know when a packet is
  7825. going to end.  However, the same problem is not encountered here that was
  7826. encountered with sending data because data that is received by the Unix host
  7827. but not "read" by the user program are buffered and collected, smoothing out
  7828. the system overhead, which is insignificant compared to the modem speed.  The
  7829. Unix program used the "stdin" and "stdout" file streams for receiving and
  7830. transmitting data, and sets the tty driver to turn off all line-editing
  7831. features to get at the raw bytes.
  7832.  
  7833. On the Commodore end, it is advantageous to read data from the modem driver in
  7834. chunks, since the system overhead is significant compared to the modem speed.
  7835. These are small computers that we are driving to the max, you know.  Data is
  7836. read from the modem in chunks of up to 255 bytes (whatever is available at the
  7837. time) and processed a byte at a time from the read buffer.  The CRC is
  7838. calculated during processing, to avoid doing this on the critical path.  The
  7839. CRC calculation is performed as an operation by itself since the overhead is
  7840. very small on fast processors.  The character-set translation for text files
  7841. will be performed on the critical path (on the Commodore) since it is more
  7842. convenient to do it at a higher layer in the IPC scheme.  The packet- handling
  7843. software is logically at a distinct layer that doesn't have to worry about
  7844. higher layers.  The next layer up is logically the RPC layer and then the
  7845. file-transfer layer.
  7846.  
  7847. 5. CLIENT/SERVER OPERATION
  7848.  
  7849. As discussed previously, the client/server interaction is based on a Remote
  7850. Procedure Call paradigm.  Thus, for each operation, the client sends a request
  7851. packet (message) to the server, and the server performs the requested
  7852. operation and sends back a reply (acknowledgement) message to the client.
  7853.  
  7854. There are eight request/ack interactions that are defined for the protocol:
  7855. two for connection management, three for uploading files, and three for
  7856. downloading files.  The client is in charge of the file-exchange session
  7857. and of the error handling.
  7858.  
  7859. 4.1. CONNECTION MANAGEMENT
  7860.  
  7861. When the client starts up, the first thing that it does is connect to the
  7862. server.  The format of the message that it sends is as follows:
  7863.  
  7864. OFF   SIZ   DESC
  7865. ---   ---   -----
  7866.   0     1   code: REQ_CONNECT ('C')
  7867.   1     1   protocol version := 0x01
  7868.   2     1   transmit byte size: '7' or '8' bits
  7869.   3     -   SIZE
  7870.  
  7871. This is what gets put into the the "payload" portion of the packet.  All of
  7872. the messages used in the protocol have an ASCII letter in the first byte that
  7873. identifies what the message type is.  Each request has an uppercase letter and
  7874. each acknowledgement has the corresponding lowercase letter.
  7875.  
  7876. The connection-request message is fairly simple: it includes the protocol
  7877. version number and the number of bits wide that the client thinks that the
  7878. communication channel is.  The version number is currently always 0x01 and is
  7879. included for cross-compatibility with future versions of the protocol.  The
  7880. channel width is encoded into either a '7' or an '8' ASCII character.  The
  7881. client will think that the channel width is seven bits only if you tell it
  7882. this on the command line.
  7883.  
  7884. When the server receives the connection request, it replies with the following
  7885. message:
  7886.  
  7887. OFF   SIZ   DESC
  7888. ---   ---   -----
  7889.   0     1   code: ACK_CONNECT ('c')
  7890.   1     1   protocol version := 0x01
  7891.   2     1   transmit byte size: '7' or '8' bits
  7892.   3     1   recommended request byte size: '7' or '8' bits
  7893.   4     4   server maximum text-upload data size: H/M/M/L word
  7894.   8     4   server maximum binary-upload data size: H/M/M/L word
  7895.  12     4   server maximum text-download data size: H/M/M/L word
  7896.  16     4   server maximum binary-download data size: H/M/M/L word
  7897.  20     -   SIZE
  7898.  
  7899. The "protocol version" is what the server is using, currently always 0x01.
  7900. The "transmit byte size" is the size that the user has specified on the
  7901. command line that activated the server, and the "recommended request byte
  7902. size" is a '7' if either the "transmit byte size" of the either the client or
  7903. server is seven bits, or '8' otherwise.  This is what should be used for the
  7904. all subsequent messages that are exchanged.
  7905.  
  7906. The server's reply also includes the maximum packet sizes that it can handle
  7907. for uploading and downloading binary and text files.  The client then takes
  7908. the "min" of the server's maximum packet sizes and its own, and uses the
  7909. resulting maximum packet sizes for the rest of the file exchange session.  The
  7910. maximum packet sizes in the server's reply are all 32-bit unsigned integers
  7911. that are stored from most-significant to least-significant bytes (big endian
  7912. order).  I picked big-endian order because that is the order used most
  7913. commonly in inter-machine protocols.
  7914.  
  7915. The reason that the client doesn't have to inform the server of the client's
  7916. maximum packet sizes in its connection message is that the maximum packet
  7917. size to use is included with each request to get the next packet of a download
  7918. file.  It is sufficient that the client knows the full max-packet information.
  7919. Really, the "transmit byte size" field isn't needed in the server reply
  7920. message either, but I wanted the packet-size fields to be size-aligned.
  7921.  
  7922. After all of the file exchanging is completed, the client sends the following
  7923. message to terminate the connection and return the server back to its command-
  7924. line mode:
  7925.  
  7926. OFF   SIZ   DESC
  7927. ---   ---   -----
  7928.   0     1   code: REQ_DISCONNECT ('Q')
  7929.   1     -   SIZE
  7930.  
  7931. When the server receives this request, it replies with:
  7932.  
  7933. OFF   SIZ   DESC
  7934. ---   ---   -----
  7935.   0     1   code: ACK_DISCONNECT ('q')
  7936.   1     -   SIZE
  7937.  
  7938. And then exits like it should.  Note that once the server exits, it cannot
  7939. accept any more packets, since they would be sent to whatever command shell
  7940. you use on your Unix system, and wouldn't do anything useful, so if the client
  7941. sends the disconnect message but doesn't receive any reply, it will time out
  7942. and tell the user that it couldn't disconnect cleanly from the server.  This
  7943. should be a rare occurrence.  Anyway, what the user would do then is re-enter
  7944. his terminal program and send Ctrl-X's at the server until it exits like it
  7945. should have.
  7946.  
  7947. This arrangement allows us to avoid the famous(?) "two armies" problem that is
  7948. inherent in disconnecting two connected processes: there is no "clean" way to
  7949. do it.  What systems like Z-Modem and Berkeley Sockets do is to have the
  7950. server wait for a period of time that is longer than N times the timeout
  7951. period of the client so that if there is a retransmission of the disconnection
  7952. request, it likely that it will be received and processed correctly by the
  7953. server.  This is the reason (presumably) that Z-Modem does an annoying pause
  7954. of 15 seconds or so after you finish transferring files.  I think that my
  7955. solution is much nicer, since the server can exit immediately (even though my
  7956. server delays for 1 second, just so that your shell prompt will be cleanly in
  7957. your modem's ARQ buffer when you re-enter your terminal program, if you have a
  7958. hardware-flow-control modem).
  7959.  
  7960. 4.2. FILE UPLOADING
  7961.  
  7962. Okay, so between connecting to and disconnecting from the server, actual
  7963.  
  7964. useful stuff happens, including uploading and downloading files.  The
  7965. uploading and downloading requests operate much like the regular file
  7966. operations of open, close, read, and write.  Really, the FX protocol makes the
  7967. server program a special kind of file server.
  7968.  
  7969. When the client decides that it wants to upload a file, it first informs the
  7970. server about this by sending the following message:
  7971.  
  7972. OFF   SIZ   DESC
  7973. ---   ---   -----
  7974.   0     1   code: REQ_UPLOAD_OPEN ('U')
  7975.   1     1   data type: 't'=text file, 'b'=binary file: 'd'=directory
  7976.   2     4   estimated file size: H/M/M/L word
  7977.   6     2   permissions ("-----sgr:wxrwxrwx"), like Unix, H:L
  7978.   8    12   modified date: BCD format: <YY:YY:MM:DD:hh:mm:ss:tt:tw:GG:gg:aa>
  7979.  20     n   filename, null-terminated
  7980. 20+n    -   SIZE
  7981.  
  7982. The "data type" field tells whether a text or binary file will be uploaded.
  7983. There is a provision for "uploading" a directory entry (as part of uploading
  7984. and downloading entire directory hierarchies), but support for this is not
  7985. implemented yet.  Also, it makes no difference to a Unix system whether a file
  7986. contains text or binary data, but it may make a difference to other operating
  7987. systems (like Mess-DOS).  The "estimated file size" field isn't really used
  7988. either, but it allows the server to make intelligent decisions about
  7989. pre-allocating space, buffering, etc., if it needed to.  However, it is
  7990. currently not filled in by the client, since file-size information is
  7991. difficult to extract from Commodore-DOS.  The file size is an unsigned 32-bit
  7992. quantity.
  7993.  
  7994. The permissions field is currently not supported by the server, but it is
  7995. intended to allow file permissions to be preserved when passing files from one
  7996. system to another.  The interpretation of the 16 bits of this field is like it
  7997. is with the Unix operating system: "rwx" bits for the owner, group, and other,
  7998. and execute-as-owner, execute-as-group bits.  The owner-id and group-id fields
  7999. aren't included since they are generally not portable across systems, and even
  8000. if they were, we usually want to receive files as our own owner-id and our own
  8001. group-id.
  8002.  
  8003. The "modification date" field is not currently filled in either, since this
  8004. information is even harder to come across with Commodore-DOS, but when it is,
  8005. it will have a 12-byte BCD format.  The "YY:YY:MM:DD:hh:mm:ss" sub-fields
  8006. should be easy enough to figure out, and the "tt:t" fields contain thousandths
  8007. of seconds.  The "w" field contains the day of the week, coded as 0-6 for
  8008. Sunday to Saturday, and 7 for "unknown".  The "GG:gg" fields contain the
  8009. number of hours and minutes that your time zone is off from GMT.  If the
  8010. number is negative (in the western hemisphere), then the regular positive
  8011. number of hours will be used, execept that the 0x80 bit of the hours byte will
  8012. be set.  Finally, the "aa" sub-field is used to encode the accuracy of the
  8013. timestamp.  The way that it is interpreted is that the time value is accurate
  8014. to plus/minus 2^aa milliseconds.  For example, if my clock were accurate to
  8015. within one second, then this field would be set to 10 in BCD (2^10 ==
  8016. 1024ms).  A value of 99 means "unknown" (or that the clock could be off by
  8017. many billions of billions of years).
  8018.  
  8019. I decided to go all out in defining the date field so that it will be useful
  8020. in the future when "world consciousness" will be much more important than
  8021. it is today.
  8022.  
  8023. And last but certainly not least, the filename is encoded in ASCII with a
  8024. trailing zero byte.
  8025.  
  8026. Upon receiving this request, the server will attempt to create a file
  8027. according to your specifications, and will send back a reply of the form:
  8028.  
  8029. OFF   SIZ   DESC
  8030. ---   ---   -----
  8031.   0     1   code: ACK_UPLOAD_OPEN ('u')
  8032.   1     1   error code: 'y'=successful, 'n'=open unsuccessful
  8033.   2     -   SIZE
  8034.  
  8035. The "error code" field tells whether the open operation was successful or
  8036. not.  If it was, then the client can continue with uploading its file; if not,
  8037. then that file cannot be uploaded (and that the upload channel doesn't need to
  8038. be closed).  It's up to the client whether to go on to the next file, abort,
  8039. or ask the user for help.  The client will currently report an error to the
  8040. user and then go onto the next file.  Of course, it's likely that whatever
  8041. caused the error in creating the current file will also cause an error in
  8042. creating subsequent files (insufficient access permissions on the current
  8043. directory, disk full, etc.).  The server will overwrite any existing file
  8044. with the same name (since asking permission, etc., would require extra
  8045. mechanism, and would probably be a nuisance anyway).
  8046.  
  8047. If the upload channel is opened successfully, then the packets of upload
  8048. data should be sent to the server one at a time, until all of the data is
  8049. uploaded.  The client sends the following message to the server to upload
  8050. a packet of data:
  8051.  
  8052. OFF   SIZ   DESC
  8053. ---   ---   -----
  8054.   0     1   code; REQ_UPLOAD_PACKET ('R')
  8055.   1     1   upload sequence number
  8056.   2     4   data length: H/M/M/L word
  8057.   6     n   data
  8058. 6+n     -   SIZE
  8059.  
  8060. The "upload sequence number", which was described before, is used to make sure
  8061. that retransmissions of packets are detected and handled properly, so that
  8062. each packet of data only appears in the file once.  The "data length" field
  8063. tells the number of user data bytes that follow in the packet, and then the
  8064. actual user data bytes appear.  The "data length" field is actually redundant,
  8065. but I figured that it would make programming a little easier, and allows
  8066. additional error checking.  Normally, each upload-data packet will contain
  8067. the maximum-packet-size number of bytes of user data (according to whether
  8068. text or binary data is being uploaded), except for the last packet, which
  8069. will contain the number of data bytes that are left in the file.  However,
  8070. each packet is allowed to contain anywhere from 1 to the maximum-packet-
  8071. size number of bytes: whatever the client wishes to use.  Variable-sized
  8072. packets are a Good Thing (TM, Pat. Pend.).  You will note that the data-
  8073. size values are also what will be used for the "read" and "write" system
  8074. calls on the client and server, respectively.  I/O will be done in big,
  8075. efficient chunks.
  8076.  
  8077. Upon receiving each upload packet, the server replies with the following
  8078. acknowledgement message:
  8079.  
  8080. OFF   SIZ   DESC
  8081. ---   ---   -----
  8082.   0     1   code: ACK_UPLOAD_PACKET ('r')
  8083.   1     1   upload sequence number
  8084.   2     -   SIZE
  8085.  
  8086. I don't think that the "sequence number" field is actually necessary here, but
  8087. it is included to allow for future expansion and to provide redundancy for
  8088. protocol-error checking.
  8089.  
  8090. When the client has uploaded all of the packets of the file currently being
  8091. uploaded, it then sends the following message:
  8092.  
  8093. OFF   SIZ   DESC
  8094. ---   ---   -----
  8095.   0     1   code: REQ_UPLOAD_CLOSE ('V')
  8096.   1     -   SIZE
  8097.  
  8098. This will close the upload channel and will finish writing the uploaded file
  8099. to the Unix file system.  The server will then respond with the following
  8100. message to acknowledge the request:
  8101.  
  8102. OFF   SIZ   DESC
  8103. ---   ---   -----
  8104.   0     1   code: ACK_UPLOAD_CLOSE ('v')
  8105.   1     4   number of bytes uploaded: H/M/M/L word
  8106.   5     -   SIZE
  8107.  
  8108. The "number of bytes" field is actually redundant, but is used for additional
  8109. error checking.
  8110.  
  8111. 4.3. FILE DOWNLOADING
  8112.  
  8113. Downloading files is analogous to uploading them: first we open the download
  8114. channel/file, then we download the packets, and then we close the download
  8115. channel.
  8116.  
  8117. To open the download channel, the client sends the following request to the
  8118. server:
  8119.  
  8120. OFF   SIZ   DESC
  8121. ---   ---   -----
  8122.   0     1   code: REQ_DOWNLOAD_OPEN ('D')
  8123.   1     -   SIZE
  8124.  
  8125. To which the server replies with:
  8126.  
  8127. OFF   SIZ   DESC
  8128. ---   ---   -----
  8129.   0     1   code: ACK_DOWNLOAD_OPEN ('d')
  8130.   1     1   data type: '0'=no more files (eom),'t'=text,'b'=bin,'e'=err,'d'=dir
  8131.   2     4   estimated file size: H/M/M/L word
  8132.   6     2   permissions ("-----sgr:wxrwxrwx"), like Unix, H:L
  8133.   8    12   modified date: BCD format: <YY:YY:MM:DD:hh:mm:ss:tt:tw:GG:gg:aa>
  8134.  20     n   filename, null-terminated
  8135. 20+n    -   SIZE
  8136.  
  8137. The file information is the same as for opening an upload file, except that
  8138. there are more possible return conditions, and all of the "meta data" fields
  8139. are actually filled in by the Unix host (since this information is actually
  8140. conveniently available via the "stat" system call).
  8141.  
  8142. If the server replies with a '0' "data type" code, then this means that the
  8143. server has no more files to offer for downloading.  The filenames to download
  8144. are taken one at a time, from left to right, from the command line that was
  8145. used to start the server.  When the server runs out, then the downloading
  8146. session is complete and the client disconnects (since the client uploads
  8147. its files first).
  8148.  
  8149. Alternatively, the server could reply with a 'e' code, which means that
  8150. it could not open the next filename given on its command line.  An error
  8151. return is generated so that the client can inform the user that the file
  8152. could not be downloaded.  This will normally result from the user giving
  8153. a bad filename on the command line.  The client will continue the downloading
  8154. process by closing the download channel (below) asking for the next file by
  8155. re-opening the download channel.  The download channel needs to be closed
  8156. on this condition since otherwise there would be no way of distinguishing
  8157. retransmissions from new requests at the server.
  8158.  
  8159. Finally, the server can reply with a 't' or 'b' code ('d' for directories is
  8160. not currently implemented) indicating that the file was correctly opened and
  8161. is either text or binary (as specified on the server's command line).  Of the
  8162. meta information about the file, only the filename and file size are currently
  8163. used: the file is named according to the given name, translated to PETSCII and
  8164. truncated to 16 characters, and the file size is reported to the user so that
  8165. he can monitor downloading progress.  I am not sure what to do yet about name
  8166. collisions on the Commodore end: either ask the user whether to overwrite the
  8167. file, automatically overwrite the file anyway, or automatically give the file
  8168. a slightly different name and download normally.  I think that for the time
  8169. being, I will just overwrite the existing file.  This will mean that you'll
  8170. want to be extra careful in putting the filenames onto the correct command
  8171. line (the client's or the server's), although there won't be a problem if the
  8172. file doesn't exist on the machine whose command line you put the name on.
  8173.  
  8174. When the file handling is all squared away and the download channel is opened,
  8175. the client then sucks packets out of the file until the end of the file is
  8176. reached.  The packets are sucked out with the following request:
  8177.  
  8178. OFF   SIZ   DESC
  8179. ---   ---   -----
  8180.   0     1   code: REQ_DOWNLOAD_PACKET ('S')
  8181.   1     1   download sequence number
  8182.   2     4   maximum acceptable data length: H/M/M/L word
  8183.   6     -   SIZE
  8184.  
  8185. The "download sequence number" is used to distinguish retransmissions from
  8186. requests for new packets, and the client tells the server the "maximum
  8187. acceptable data length" for the reply packet.  Although the max-packet
  8188. information is actually static during the connection, I included it here in
  8189. every "read" request since I didn't really want the server to keep that
  8190. particular bit of "state" internally.
  8191.  
  8192. The server replies to the download-packet request with the following message:
  8193.  
  8194. OFF   SIZ   DESC
  8195. ---   ---   -----
  8196.   0     1   code: ACK_DOWNLOAD_PACKET ('s')
  8197.   1     1   download sequence number
  8198.   2     4   data length: H/M/M/L word, 0==EOF
  8199.   6     n   data
  8200. 6+n     -   SIZE
  8201.  
  8202. This is the only "large" message that the server can produce.  It includes the
  8203. sequence number, the number of bytes that are actually included, and the user
  8204. data.  The number of data bytes in the packet is allowed to be smaller than
  8205. the number of bytes requested, but this is normally only the case for the last
  8206. packet of the file.
  8207.  
  8208. To indicate that the end of file has been reached and that no more user data
  8209. is available, the server will return a download packet with zero bytes of user
  8210. data in it.  Upon receiving this, the client will close the download channel
  8211. with the following message:
  8212.  
  8213. OFF   SIZ   DESC
  8214. ---   ---   -----
  8215.   0     1   code: REQ_DOWNLOAD_CLOSE ('E')
  8216.   1     -   SIZE
  8217.  
  8218. And the server will reply with:
  8219.  
  8220. OFF   SIZ   DESC
  8221. ---   ---   -----
  8222.   0     1   code: ACK_DOWNLOAD_CLOSE ('e')
  8223.   1     4   number of file bytes downloaded: H/M/M/L word
  8224.   5     -   SIZE
  8225.  
  8226. The "number of file bytes downloaded" field is redundant but included for
  8227. additional error checking.  After closing a file, the client will then ask
  8228. for the next file, or will disconnect if the last file to download was just
  8229. closed.
  8230.  
  8231. 4.4. ERROR HANDLING
  8232.  
  8233. With all of the server calls except for disconnecting (discussed earlier), the
  8234. is the possibility that either the request message from the client or the
  8235. reply message from the server will become garbled and be dropped by the
  8236. packet-delivery layer of the software.  To recover from this, if the client
  8237. detects an extended period of inactivity on the serial line for received data
  8238. (where "extended period" is defined as being "about five seconds"), then the
  8239. client will assume that something went wrong and it will retransmit the
  8240. request.
  8241.  
  8242. As pointed out way above, there are two possible reasons for a retransmission
  8243. being needed: either the request packet was corrupted and dropped, or the
  8244. reply packet was corrupted and dropped.  In the format case, the request
  8245. wasn't processed by the server, but in the latter case, it was.  Since we
  8246. don't want the server to perform an file operation twice (this is really
  8247. what the six file-transfer client operations really boil down to from the
  8248. server's perspective), the server must keep four pieces of internal state:
  8249. the last upload sequence number, the last download sequence number, whether
  8250. the upload file is open, and whether the download file is open.
  8251.  
  8252. If an upload-open request is received and the file to be uploaded is not open,
  8253. the the request must be a new one and the server processes it and sends back a
  8254. reply like normal.  If an upload-open request is receive and the upload file
  8255. IS currently open, then it must be the case that the current request is a
  8256. retransmission, so all theat the server needs to do is to give a positive
  8257. reply without performing any internal file operations.  The same holds true
  8258. for the download-open call and for both of the close calls (except that the
  8259. operation has already been processed if the file is CLOSED).
  8260.  
  8261. For the packet-upload and packet-download requests, sequence numbers are used
  8262. to detect duplicates.  You will note that these sequence numbers are distinct
  8263. from one another, and, in fact, that the entire upload and download file-
  8264. transfer channels are distinct and independent from each another.  This is to
  8265. allow for the future possibility of simultaneous file uploading and
  8266. downloading.  In fact, if stream numbers (file descriptors) were added to the
  8267. open/read/write/close requests, then we could have us a full-blown remote-host
  8268. over-the-phone interactive file server.  But anywho, sequence numbers start
  8269. from 0x00 for the first packet transferred and increment modulo 256 from
  8270. there.
  8271.  
  8272. Note that for high-speed data-compression modems (like I have) that already
  8273. include error detection and recovery at a level hidden from the user, the FX
  8274. protocol will work particularly well: there will never be an error, never be a
  8275. timeout delay, and never be a retransmission.  And, really, the CRC-32 error
  8276. computation and checking is pretty much a zero cost.  But, if something does
  8277. go wrong, outside of the modem-to-modem connection, the FX protocol is right
  8278. there to pick up the pieces and carry on.
  8279.  
  8280. 6. CONCLUSION
  8281.  
  8282. You'll have to wait to get your hands on the program.  The Unix Server
  8283. program is almost 100% (except for a few design changes that I made while
  8284. writing this document), and the ACE program is implemented except for
  8285. the error handling and text conversion.  Both programs will be released
  8286. with the next release of ACE, which will be Real Soon Now (TM).
  8287.  
  8288. Here is my performance testing so far, using my USR Sportster modem over a
  8289. 14.4-kbps phone connection, with a 38.4-kbps link to my modem from my C128, to
  8290. my usual Unix host:
  8291.  
  8292. Using FX to/from the ACE ramdisk, REU:
  8293.  
  8294. Download 156,260 bytes of ~text:        time= 54.1 sec, rate=2888 cps.
  8295. Download 151,267 bytes of tabular text: time= 45.9 sec, rate=3296 cps.
  8296. Download 141,299 bytes of JPEG image:   time= 92.5 sec, rate=1528 cps.
  8297. Upload   156,260 bytes of ~text:        time= 57.4 sec, rate=2722 cps.
  8298. Upload   151,267 bytes of tabular text: time= 45.3 sec, rate=3339 cps.
  8299. Upload   141,299 bytes of JPEG image:   time= 95.0 sec, rate=1487 cps.
  8300.  
  8301. Using FX to/from my CMD Hard Drive:
  8302.  
  8303. Download 156,260 bytes of ~text:        time= 83.4 sec, rate=1874 cps.
  8304. Download 151,267 bytes of tabular text: time= 75.4 sec, rate=2006 cps.
  8305. Download 141,299 bytes of JPEG image:   time=118.2 sec, rate=1195 cps.
  8306. Upload   156,260 bytes of ~text:        time= 77.9 sec, rate=2006 cps.
  8307. Upload   151,267 bytes of tabular text: time= 66.2 sec, rate=2285 cps.
  8308. Upload   141,299 bytes of JPEG image:   time=114.2 sec, rate=1237 cps.
  8309.  
  8310. Using DesTerm-128 v2.00 to/from my CMD Hard Drive, Y-Modem:
  8311.  
  8312. Download 156,260 bytes of ~text:        time=189.5 sec, rate= 824 cps.
  8313. Download 151,267 bytes of tabular text: time=180.4 sec, rate= 839 cps.
  8314. Download 141,299 bytes of JPEG image:   time=199.9 sec, rate= 707 cps.
  8315. Upload   156,260 bytes of ~text:        time=255.1 sec, rate= 611 cps.
  8316. Upload   151,267 bytes of tabular text: time=238.6 sec, rate= 634 cps.
  8317. Upload   141,299 bytes of JPEG image:   time=233.0 sec, rate= 606 cps.
  8318.  
  8319. Using NovaTerm-64 v9.5 to my CMD Hard Drive, Z-Modem, C64 mode:
  8320.  
  8321. Download 156,260 bytes of ~text:        time=245.8 sec, rate= 636 cps.
  8322. Download 151,267 bytes of tabular text: time=230.0 sec, rate= 658 cps.
  8323. Download 141,299 bytes of JPEG image:   time=262.6 sec, rate= 538 cps.
  8324.  
  8325. (There is no Z-Modem uploading support)
  8326.  
  8327. So there you have it: my simple protocol blows the others away.  QED.
  8328. ========================================================================
  8329. DESIGN AND IMPLEMENTATION OF A 'REAL' OPERATING SYSTEM FOR THE 128: PART II
  8330.  
  8331. by Craig S. Bruce  <csbruce@ccnga.uwaterloo.ca>
  8332.  
  8333. 0. PREFACE
  8334.  
  8335. There has been a slight change in plans.  I originally intended this article
  8336. to give the design of a theoretical distributed multitasking microkernel
  8337. operating system for the C128.  I have decided to go a different route: to
  8338. take out the distributed component for now and implement a real multitasking
  8339. microkernel OS for a single machine and extend the system to be distributed
  8340. later.  The implementation so far is, of course, only in the prototype stage
  8341. and the application for it is only a demo.  Part III of this series will
  8342. extend this demo system into, perhaps, a usable distributed operating system.
  8343.  
  8344. 1. INTRODUCTION
  8345.  
  8346. The previous article talked about the general approach to building a
  8347. multitasking microkernel OS for the C128.  It is assumed here that you have
  8348. read and understood the previous article.  This article goes into the grungy
  8349. details of implementing such a beast.  The prototype kernel implementation
  8350. provides system calls to create and "exit" user processes, obtain status
  8351. information, delay execution of a process for a specified period of time,
  8352. and to perform message-passing interprocess communication.
  8353.  
  8354. Currently, there is no real memory management, no real device drivers, and no
  8355. process-resource reclamation.  More "infrastructure" features need to be added
  8356. before a command-shell environment or any such thing could be supported,
  8357. though not toooo many more; the Commodore-Kernal Server in the demo system
  8358. makes the $FFD2 (CHROUT) routine of the Commodore Kernal available to all
  8359. other processes in the demo system.  It could easily be modified to provide
  8360. all of the Commodore-Kernal features to the other processes, thereby giving
  8361. us a basic I/O sub-system.
  8362.  
  8363. There is also no way to dynamically load external programs, so the test
  8364. programs have to be assembled with the kernel code.  Loading external programs
  8365. in this type of environment has the requirement that the program will have to
  8366. be relocated upon being loaded to an address that would only be known at load
  8367. time.  There are two ways to go on this: load all programs to a fixed address
  8368. or load them to dynamic addresses.  If you load them to a fixed address, then
  8369. you can only have one processes loaded and concurrently running on each bank
  8370. of internal memory of the C128 and then demand-swap all of the other processes
  8371. into and out of these (two) slots, presumably from REU memory (any other type
  8372. would be much too slow).  IHMO, even with REU memory, this would be too slow,
  8373. especially for a microkernel environment.  So, programs will need to be
  8374. loaded to dynamic addresses.  Fortunately, I have a program-relocation
  8375. mechanism in the works.
  8376.  
  8377. The entire kernel and the demo program fits in C128 memory in the slot between
  8378. $1300 and $1BFF of RAM0.  The kernel uses storage from $C00-$CFF and
  8379. $2000-$BFFF.  The latter section of memory is used up in 768-byte chunks by
  8380. each new process, so there can be a total of 53 concurrently executing user
  8381. processes in the system.
  8382.  
  8383. 2. TEST PROGRAM
  8384.  
  8385. The test program includes no provisions for user interaction, so it may not
  8386. be something that you can impress your friends with, but I can assure you
  8387. that all kinds multitasking stuff is going on behind the scenes to make
  8388. everything happen.
  8389.  
  8390. The demo test program creates ten processes.  There are five "delay"
  8391. processes, two "blabbering" processes, a Commodore-Kernal-Server process, one
  8392. SID-banging process, and the Null process.  (Note that when I use the word
  8393. "kernel" I am referring to the OS that I have written, and when I use the word
  8394. "Kernal", I am referring to the Commodore Kernal in ROM).
  8395.  
  8396. The purpose of the Commodore-Kernal Server is to receive requests from the
  8397. worker processes to call the CHROUT routine to print a given line of text out
  8398. to the screen.  The Kernal server is the only processes that is allowed to
  8399. call the Commodore-Kernal routines.  Since it can only process one request
  8400. from a client process at a time, calls to the Commodore Kernal are effectively
  8401. "serialized" (made to happen one after another in time), which is good, since
  8402. things would blow up pretty badly if two accesses the Commodore Kernal were to
  8403. happen concurrently.
  8404.  
  8405. The "delay" processes, numbered from 1 to 5, each delay for a period of N
  8406. seconds and then request the Kernal Server to print a "I'm alive" message to
  8407. the screen.  The number N of seconds to delay is the number of the process.
  8408. You should be able to observe from watching the execution that each delay
  8409. process prints a message to the screen with approximately the correct period
  8410. between messages.  Note that while these processes are delaying, they don't
  8411. use any CPU time, so the CPU time is allocated to other processes.  If you
  8412. try holding down the C= key to slow the scrolling or run the system in Slow
  8413. mode, you will still notice that the delay processes generate their output
  8414. at approximately the right time.
  8415.  
  8416. The two "blabbering" processes, named "blabber" and "spinner", continually
  8417. send print messages to the Commodore-Server process.  You will observe that
  8418. the messages from each comes pretty much prefectly interleaved with each
  8419. other, and with the output of the delay process, because of the interprocess
  8420. communication scheduling policy: FIFO (first-in, first-out).
  8421.  
  8422. The SID-banging process runs continuously in the background.  It increments
  8423. the 16-bit frequency of voice #1 from $0000 to $ffff and then suspends its
  8424. execution for two seconds and then repeats.  A square wave with even pulse
  8425. widths is used.  When the SID process delays for two seconds, you will notice
  8426. that the printing operation of the other processes speeds up a bit.  This is
  8427. because the SID process is a heavy CPU user while it is active.  The amount of
  8428. slow-down of the rest of the system is limited a bit because the SID process
  8429. runs with a slightly lower priority than the other processes in the system
  8430. (which makes the sound increment slightly slower than it otherwise would --
  8431. there's only so much CPU to go around).
  8432.  
  8433. The Null process is not actually needed in this exercise, but it would
  8434. normally be used to insure that the system always had some process to
  8435. schedule.  All that it does is increment the binary value in locations
  8436. $0400-$0403 (on the 40-column screen) whenever it is active.  It has the
  8437. lowest priority in the system and never gets to execute unless all of the
  8438. other processes are blocked (i.e., are suspended for some reason).
  8439.  
  8440. When you get tired of watching the demo, you can just hit the RESTORE key.
  8441. This will cause an NMI which will make the system exit back to BASIC.  The
  8442. system does not have the ability to handle external events (like key strokes)
  8443. at this time.  A couple of locations on the 40-column screen are used for
  8444. status information, so you will want to run the demo on the 80-column screen.
  8445.  
  8446. 3. PROCESS CONTROL
  8447.  
  8448. A process is a user program that is in an active state of execution.  A
  8449. process is periodically given a certain amount of CPU time to execute its
  8450. code, and then CPU attention is taken away from it to execute other
  8451. processes.  This may sound like you're simply making N processes run N times
  8452. slower, and this is true in the worst case, but the normal case is that many
  8453. processes in the system will be blocked (for whatever reason) and will not
  8454. require any more CPU time until they wake up again (for whatever reason).
  8455. Therefore, multitasking is a "winnable" proposition.
  8456.  
  8457. In our system, the process that the CPU is currently executing is changed
  8458. every 1/60 of a second.  This is a convenient "quantum" period for a number of
  8459. reasons, including the fact that, thanks to the MMU of the C128, "context
  8460. switching" can be efficiently performed this quickly.
  8461.  
  8462. 3.1. PROCESS-CONTROL CALLS
  8463.  
  8464. There are six kernel calls that deal with process control:
  8465.  
  8466. CALL NAME     INPUT ARGUMENTS                  RETURN VALUES
  8467. -----------   ------------------------------   -------------------------------
  8468. Create        ( .AY=address, .X=priority )     .AY=newPid, .CS=err(.A=errcode)
  8469. Exit          ( .A=code, .X=$00 )              <none>
  8470. MyPid         ( )                              .AY=pid
  8471. MyParentPid   ( )                              .AY=parentPid
  8472. Suspend       ( )                              <none>
  8473. Delay         ( .AY=jiffies )                  <none>
  8474.  
  8475. 3.1.1. CREATE
  8476.  
  8477. The Create() kernel call is used to create a new process.  The first input
  8478. argument is the code address, and it is passed in the .AY register (.A is
  8479. loaded with the low byte of the address, and the .Y register is loaded with
  8480. the high byte of the address--.AY for short).  The code must be present in
  8481. memory and be ready to be executed, since there is no facility for loading
  8482. external programs.  Also, if the code is not re-entrant, then there must be no
  8483. other process already executing it or things will likely blow up.  Re-entrant
  8484. code is code that can be executed by multiple processes simultaneously without
  8485. conflicts, essentially because there are no global variables that could be
  8486. banged on by more than one process at a time.
  8487.  
  8488. The priority argument is the priority to execute the new process at.  Valid
  8489. values for this argument are on the range 0 to 127.  The system keeps a list
  8490. at all times of all the processes that are ready to execute, called the "ready
  8491. list".  The way that the scheduling works is that a pointer to the "active"
  8492. process is kept (the one that is currently executing) and this pointer cycles
  8493. through the ready list, trying to activate each process in turn, every 1/60 of
  8494. a second.  This is roundrobin scheduling.  The priority of a process
  8495. determines the number of cycles that the active-process pointer has to take
  8496. through the list before the process is activated.  So, if a process has
  8497. priority 1, then it will be activated on every round; if it has a priority of
  8498. 2, then it will be activated on every second round; and if it has a priority
  8499. of 86, then it will be activated only on every 86th round through the ready
  8500. list.  The higher the priority value, the slower the process executes.  This
  8501. policy gives a fair allocation of the CPU to the various processes in the
  8502. system.
  8503.  
  8504. Normally, foreground processes (ones that perform actions right in front of
  8505. the user's face) should have a priority of 1, and background processes should
  8506. have a relatively lower priority.  A priority value of 0 for a process means
  8507. that when the process is activated, it will not be deactivated again until it
  8508. blocks for some reason.  This priority level should be reserved for urgent
  8509. computations that block often, since it has the potential to starve out the
  8510. rest of the system.  The Null process executes at a special priority level
  8511. (255) that makes it so that it will only be activated if there are no other
  8512. processes in the ready list.
  8513.  
  8514. The Create() call returns with the carry flag clear and the process id of the
  8515. newly created process in the .AY register upon success, or returns with the
  8516. carry flag set and an error code in the .A register upon failure.  I do not
  8517. have the complete list of error conditions figured out a this time, but errors
  8518. will usually happen on a call like this because of a lack of resources
  8519. (memory) for the kernel's internal data structures.  Upon successful return,
  8520. the child process is created, made ready, and may be activated by the system
  8521. at any time.  The first instruction to be executed by the child will be at the
  8522. value given for the code address.
  8523.  
  8524. The newly created process will have a clear individual stack, except for a
  8525. couple of bytes on the very bottom of it (high addresses), and a clear
  8526. individual zeropage.  Processes are allowed to make full use of every location
  8527. in their zeropage, except for the I/O registers at locations 0 and 1, and full
  8528. use of their stack, except that they must make sure that about a dozen bytes
  8529. are available on the stack at all times in case an interrupt happens.
  8530.  
  8531. 3.1.2. EXIT
  8532.  
  8533. The Exit() kernel call is used to remove the current process from the system.
  8534. There are two input arguments: the .A register contains the return code that
  8535. will be made available to the parent process if it is interested (though not
  8536. in the current implementation), and a value in the .X register, which must
  8537. currently be $00.  I haven't figured out exactly how the exit mechanism should
  8538. work yet and it currently only has a minimal implementation.  The call does
  8539. make the kernel reclaim the resources that were allocated to the process yet,
  8540. although this functionality will be needed in any real operating system.
  8541.  
  8542. There is no return value from the Exit() call, because the call never returns.
  8543. The semantics of the call is the the process calling Exit() will never be made
  8544. active again.  All processes should call Exit() when they are finished
  8545. executing, or they can achieve the same result by executing an RTS instruction
  8546. at the end of their main routine.  The kernel pushes the address of an Exit()
  8547. stub routine onto the top of the stack of a user process when it is created,
  8548. and the user process will exit with a return code of $00 in this case.
  8549.  
  8550. 3.1.3. MY_PID
  8551.  
  8552. The MyPid() kernel call is used to return the process identifier of the
  8553. current process.  This call is very simple, takes no arguments, and executes
  8554. very quickly.  The return value is the process id of the calling process and
  8555. is returned in the .AY register.  This call cannot fail, because the current
  8556. process must exist in order to make the call in the first place, so there is
  8557. no error-return condition.
  8558.  
  8559. 3.1.4. MY_PARENT_PID
  8560.  
  8561. The MyParentPid() kernel call is used to return the process identifier of the
  8562. parent process of the current process (i.e., the process that created the
  8563. current process).  This call is simple, takes no arguments, and executes
  8564. quickly, very much like the MyPid() call.  No error returns are possible, and
  8565. the process id of the parent to the current process is returned in the .AY
  8566. register.  But note: it is not guaranteed that the parent process will still
  8567. exist either before or after the current process makes this call; it may have
  8568. Exit()ed.  I may re-think this semantic.
  8569.  
  8570. This call is useful for setting up interprocess communication between a child
  8571. process and its parent.
  8572.  
  8573. 3.1.5. SUSPEND
  8574.  
  8575. The Suspend() kernel call is used to suspend the execution of the currently
  8576. executing process for an indefinite period of time.  Currently, this period of
  8577. time is forever, since there is no corresponding "Resume" system call that
  8578. another process can call in order to wake up the process that suspended
  8579. itself.  The reason that this call is made available is because the guts of
  8580. what it does is required by other kernel operations, and the cost of making
  8581. this call user-accessible was three 6502 instructions.  This call may be
  8582. retracted in the future, since it may cause programmers to do bad things.
  8583.  
  8584. The call takes no arguments, returns no values, and currently, will never
  8585. return at all, much like Exit().
  8586.  
  8587. 3.1.6. DELAY
  8588.  
  8589. The Delay() kernel call is used to suspend the execution of the current
  8590. process for a user-specified period of time.  The delay period is given in
  8591. units of jiffies (1/60ths of a second).  The unsigned 16-bit delay period is
  8592. passed in in the .AY registers, giving a maximum possible delay period of
  8593. about 18 minutes.  If a user process requests to delay for a period of zero
  8594. jiffies, its execution will not be suspended at all and the Delay() primitive
  8595. will return immediately.
  8596.  
  8597. Since there may be other processes in the system doing things when the current
  8598. processes wakes up after doing a delay, you can think of the process delaying
  8599. for "at least" the period of time that you specify.  Actually, to muddy things
  8600. even more, your process will always go to sleep at a moment in time that is
  8601. inbetween two ticks of the jiffy clock, so the first "jiffy" that your process
  8602. waits may actually be any period between a couple of microseconds to almost a
  8603. full jiffy, with a statistical average of half a jiffy.  This is an artifact
  8604. of any coarse-tick-based mechanism.
  8605.  
  8606. To muddy things again, the jiffy ticks, which are currently based on VIC
  8607. raster interrupts (one per screen update), may not be processed immediately
  8608. when they occur, since the IRQ may be delayed by a small period of time if
  8609. interrupts are disabled in the processor status register when the jiffy tick
  8610. happens.  And finally, you should note that you will have a difficult time
  8611. using this call for true "real time" periodic operations, like performing some
  8612. specific task precisely every tenth of a second, since the call specifies a
  8613. period to delay for, rather than a time to wake up at.  The actual period of
  8614. your process' activations will be determined by the waiting time plus the time
  8615. skew caused by the processing that your process does.  A DelayUntil() call
  8616. easily could be implemented, if I figure that it will be needed for anything.
  8617.  
  8618. Currently, the scheduling policy is to make processes active immediately after
  8619. they are awakened, so this makes the activities of other processes less of a
  8620. worry to accurate timing.  Unix does a similar thing by giving a freshly
  8621. awakened process a temporarily high priority, since it is probably likely that
  8622. the process will do some small think and then block again.  This policy
  8623. statistically improves concurrency.
  8624.  
  8625. 3.2. PROCESS CONTROL BLOCKS
  8626.  
  8627. A Process Control Block (PCB) is the data structure that the kernel keeps the
  8628. information that it needs to know about a process in.  A Process identifier
  8629. (pid) is actually the RAM0 address of the process control block of a process,
  8630. for convenience, though this will have to change later.  The fields of the
  8631. process control block are shown here, organized into classes:
  8632.  
  8633. OFF   SIZ   CLASS   LABEL
  8634. ---   ---   -----   ------
  8635.   0     2   queue   pcbNext
  8636.   2     2   queue   pcbPrev
  8637.   4     1   queue   pcbIsHead
  8638.   5     1   queue   pcbQCount
  8639.   6     1   ctxt    pcbSP
  8640.   7     1   ctxt    pcbStackPage
  8641.   8     1   ctxt    pcbZeroPage
  8642.   9     1   ctxt    pcbD506
  8643.  10     1   sched   pcbPriority
  8644.  11     1   sched   pcbCountdown
  8645.  12     2   sched   pcbWakeupTime
  8646.  12     2   ipc     pcbSendMsgPtr  (overlap)
  8647.  12     2   ipc     pcbRecvMsgPtr  (overlap)
  8648.  14     2   ipc/q   pcbSendQHead
  8649.  16     2   ipc/q   pcbSendQTail
  8650.  18     1   ipc/q   pcbSendQFlag
  8651.  19     1   ipc/q   pcbSendQCount
  8652.  20     2   ipc     pcbBlockedOn
  8653.  22     2   ipc     pcbReceiveFrom
  8654.  24     2   proc    pcbParent
  8655.  26     1   proc    pcbState
  8656.  27     -   -       SIZE
  8657.  
  8658. 3.2.1. QUEUE-CLASS FIELDS
  8659.  
  8660. The first four fields, of the class "queue" are used for maintaining a process
  8661. control block in queues with other PCBs.  Some general-purpose queue-handling
  8662. routines have been written to make queue management easier:  QueueInit(),
  8663. QueueInsert(), and QueueUnlink().  Each queue has a head node, and the nodes
  8664. in a doubly linked circular order.  This means that each node in the queue has
  8665. a forward ("pcbNext") and a backward ("pcbPrev") pointer and that the first
  8666. node points back to the head and the last node in a list points forward to the
  8667. head.  This organization removes all of the quirks of handling null pointers
  8668. from the code.  Using a doubly linked organization makes it easy to remove an
  8669. arbitrary node from the middle of a queue.
  8670.  
  8671. Each node also has a "pcbIsHead" field which is always False (zero) and a
  8672. "pcbQCount" field which is always zero.  The head is the same as an entry in
  8673. the queue, except that its "pcbIsHead" field is set to True ($ff) and its
  8674. "pcbQCount" field records the number of nodes that are in the queue at any
  8675. time.  The "pcbIsHead" field is checked when scanning a list to tell if you've
  8676. bumped back into the head node again, indicating the end of the list.  The
  8677. "pcbQCount" field is very convenient to check to see whether the queue is
  8678. empty or not.
  8679.  
  8680. All of the processes that are ready to execute in the system are kept in the
  8681. ready queue.  The PCB of the Null process acts as the head for this queue, and
  8682. is also an active node in the queue (a small but harmless kluge).  The pointer
  8683. to the active process is kept in a kernel-zero-page variable, and sweeps
  8684. through the circularly linked ready-process list to activate new processes.
  8685. The active PCB is not removed from the ready list while it is active.
  8686.  
  8687. 3.2.2. CONTEXT-CLASS FIELDS
  8688.  
  8689. The next four fields, of the class "ctxt", store the "context" of a process
  8690. that is not stored on the process' stack when it is not executing.  These
  8691. fields include space for the stack pointer, the stack page, the zeropage, and
  8692. the contents of the MMU register at location $d506.  The stack pointer is
  8693. what was in the SP register of the CPU when the process last paused.  The
  8694. stack page and the zeropage values are the values in MMU registers $d505 and
  8695. $d507, respectively; these are the page numbers of the pages in RAM0 memory
  8696. that are allocated to a process.  These pages can only be in RAM0 unless
  8697. common memory is disabled, for hardware reasons.  I may allow these pages to
  8698. be in either RAM0 or RAM1 if there is a need later.  The $d506 register of
  8699. the MMU stores the most-significant bits of the RAM bank that is selected if
  8700. you have expanded internal memory on your 128 (a la TwinCities-128) and the
  8701. bank selection for REU (DMA) operations.
  8702.  
  8703. The rest of a process' context is stored on its stack.  Here is what a
  8704. process' stack looks like just after it has been created:
  8705.  
  8706. ADDR   SP-REL   DESCRIPTION
  8707. ----   ------   ------------
  8708. $ff    sp+09    exitaddr-1.h
  8709. $fe    sp+08    exitaddr-1.l
  8710. $fd    sp+07    pc.h
  8711. $fc    sp+06    pc.l
  8712. $fb    sp+05    status register
  8713. $fa    sp+04    .A
  8714. $f9    sp+03    .X
  8715. $f8    sp+02    .Y
  8716. $f7    sp+01    $ff00 save
  8717. $f6    sp+00    -empty-
  8718.  
  8719. The "exitaddr" is the address of the routine in the kernel that will terminate
  8720. a process if it executes an RTS from its main routine.  The address is in the
  8721. regular low-high order (although it is pushed on high-low since the stack
  8722. grows downward in memory) but the value pushed is actually one less than the
  8723. address of the routine, because this is what JSR pushes onto the stack and
  8724. this is what RTS expects to find.  The "pc" low and high fields give the
  8725. address of the next instruction to be executed by the process when it is
  8726. activated.  When the process is first created, this will be the address of the
  8727. first instruction.  The "pc" value is the actual address, not one before,
  8728. because this is what a hardware interrupt pushes onto the stack, and this is
  8729. what the RTI instruction expects to find.
  8730.  
  8731. The "status register", ".A", ".X", and ".Y" fields contain the values to be
  8732. loaded into the corresponding registers inside of the CPU when the process is
  8733. activated.  For a new process, these values are all zero.
  8734.  
  8735. The "$ff00 save" is the value to be loaded into the $ff00 "shadow" register of
  8736. the MMU when the process is activated.  This gives the memory context that the
  8737. process is to execute in.  As the kernel currently only works with one bank
  8738. configuration (RAM0, NO BASIC ROM, I/O enabled, KERNAL ROM enabled), this is
  8739. the value put here when a process is created.
  8740.  
  8741. The final field is "-empty-" because the stack pointer in the 6502 points to
  8742. the next location in stack memory that will be used.  All other values in the
  8743. stack are relative to this.  Upon startup, the stack-pointer field of the
  8744. process control block will be set to $f6, which is what the table above
  8745. shows.
  8746.  
  8747. The stack contents look exactly the same after a process has been interrupted
  8748. by a hardware interrupt, except that the stack pointer will likely be lower in
  8749. the stack memory, so the absolute addresses in stack memory in the above table
  8750. no longer apply and the "exitaddr" bytes are not part of the interrupt
  8751. context.  That things look the same is no coincidence; on startup, we set up
  8752. the stack to make things look as if an interrupt had just occurred, and to
  8753. start a process executing, we execute the code that returns from an interrupt,
  8754. which loads the "context" that is on the stack into the process registers, and
  8755. we are ready to rock.
  8756.  
  8757. The above stack organization is exactly the same as it is for processing
  8758. interrupts normally using the Commodore-Kernal environment on the 128, and
  8759. this too is no coincidence because the code that sets up the stack like this
  8760. upon an interrupt is burned into the Kernal ROM and there is very little that
  8761. I can do about it.  Fortunately, the organization is just fine for our
  8762. purposes.
  8763.  
  8764. 3.2.3. SCHEDULE-CLASS FIELDS
  8765.  
  8766. The next three fields, of class "sched", are used to schedule the process.
  8767. The "pcbPriority" field gives the relative priority of the process according
  8768. to the scheme already discussed.  The "pcbCountdown" field is used to keep
  8769. count of the number of remaining times that the process will have to be
  8770. bypassed in cycling through the ready queue before the process will be
  8771. activated again.  When a process gives up the CPU upon the expiration of its
  8772. time quantum, the "pcbCountdown" field is loaded with the pcbPriority of the
  8773. process.  When the "pcbCountdown" value reaches zero, the process is selected
  8774. for activation.
  8775.  
  8776. The "pcbWakeupTime" field is used with the Delay() kernel call to indicate the
  8777. absolute system time when the process should be activated again.  The current
  8778. time in the system is kept in a 16-bit kernel variable, and wraps around every
  8779. 18.2 minutes.  If the process is not currently time-delayed, then the
  8780. WakeupTime field is not used (in fact, the memory may be used to record other
  8781. status information).
  8782.  
  8783. 3.2.4. IPC-CLASS FIELDS
  8784.  
  8785. The eight eight fields, of classes "ipc" and "ipc/q" are used for interprocess
  8786. communication (message passing).  The first two fields, "pcbSendMsgPtr" and
  8787. "pcbRecvMsgPtr", are used for storing temporary values for handling message
  8788. requests; the four "ipc/q"-class fields are used to implement the head of a
  8789. queue of processes that are waiting to communicate with the current process,
  8790. and the "pcbBlockedOn" field indicates which process this process is waiting
  8791. to communicate with, if the current process is waiting.  The first two fields
  8792. actually overlap with each other and with the "pcbWakeupTime" field discussed
  8793. earlier.  This is okay since none of the fields will store active status
  8794. information at the same time.  The last field, "pcbReceiveFrom" is not used at
  8795. this time, but will be used in the future for an primitive to receive a
  8796. message only from a specific process.  The interprocess communication is
  8797. discussed in much greater detail later.
  8798.  
  8799. 3.2.5. PROC-CLASS FIELDS
  8800.  
  8801. The final two fields, of class "proc", store information about the status of
  8802. the process.  I guess the same can be said of all the other fields.  Anyway,
  8803. the "pcbParent" field indicates which process is the process that created the
  8804. current process, and the return value for the MyParentPid() kernel call is
  8805. taken from this field.
  8806.  
  8807. The "pcbState" field gives the current state of the process.  Here are the
  8808. different possible process states:
  8809.  
  8810. STATE NAME        CODE
  8811. ---------------   ----
  8812. STATE_READY       $c0
  8813. STATE_SEND        $c1
  8814. STATE_RECEIVE     $c2
  8815. STATE_REPLY       $c3
  8816. STATE_DELAY       $c4
  8817. STATE_SUSPENDED   $c5
  8818.  
  8819. The STATE_READY state means that the process is in the ready queue.  The
  8820. STATE_SEND, STATE_RECEIVE, and STATE_REPLY states mean that a process is
  8821. waiting for some interprocess communication primitive to be called by the
  8822. process that it is communicating with.  The STATE_DELAY state means that the
  8823. process has called the Delay() primitive and is waiting for some period of
  8824. real time to pass before it can be activated again.  The STATE_SUSPENDED state
  8825. means that a process has called the Suspend() or Exit() primitive and will
  8826. never be made active again (currently, Suspend() and Exit() mean the same
  8827. thing).
  8828.  
  8829. The state information is needed for some operations, and will definitely be
  8830. needed by a Kill()-process operation, to find out what state a process is in
  8831. so that it can be removed from any queue or whatever it is in, in order to
  8832. obliterate all information about the process from the system.  Currently,
  8833. there is no Kill() call.
  8834.  
  8835. 3.3. TASK CREATION
  8836.  
  8837. When the user calls for the creation of a new process in the system, lots of
  8838. stuff has to happen.  First, memory for the process control block, zero page,
  8839. and stack page must be allocated.  Currently, this allocation is performed
  8840. very simply, by keeping a page pointer and incrementing it every time a page
  8841. is allocated.  The process control block ends up getting allocated 256 bytes
  8842. even though it actually requires much less than that.  Thus, each new process
  8843. chews up 768 bytes of memory space (plus code).  Also, there is currently no
  8844. mechanism for recovering the memory allocated to a process, which is okay
  8845. since the mechanism for Exit()ing a process is incomplete too.
  8846.  
  8847. The address that a PCB gets allocated at is used for the process' PID (process
  8848. identifier).  This is particularly useful since the real purpose of a PID is
  8849. to conveniently locate the process control block.  This will have to change
  8850. in the future, however, since the PIDs will also have to locate the machine
  8851. that a process is on.
  8852.  
  8853. Then the process control block must be initialized.  It is initialized as
  8854. follows:
  8855.  
  8856. FIELD            SIZ   CLASS   INITIAL VALUE
  8857. --------------   ---   -----   --------------
  8858. pcbNext            2   queue   0
  8859. pcbPrev            2   queue   0
  8860. pcbIsHead          1   queue   0
  8861. pcbQCount          1   queue   0
  8862. pcbSP              1   ctxt    $f6
  8863. pcbStackPage       1   ctxt    set to newly allocated space
  8864. pcbZeroPage        1   ctxt    set to newly allocated space
  8865. pcbD506            1   ctxt    $04
  8866. pcbPriority        1   sched   set to the given argument
  8867. pcbCountdown       1   sched   set to the same value as "pcbPriority"
  8868. pcbWakeupTime      2   sched   0 (overloaded field)
  8869. pcbSendQHead       2   ipc/q   set by the QueueInit() function for empty queue
  8870. pcbSendQTail       2   ipc/q   set by the QueueInit() function for empty queue
  8871. pcbSendQFlag       1   ipc/q   set by the QueueInit() function for empty queue
  8872. pcbSendQCount      1   ipc/q   set by the QueueInit() function for empty queue
  8873. pcbBlockedOn       2   ipc     0
  8874. pcbReceiveFrom     2   ipc     0
  8875. pcbParent          2   proc    set to the id of the creator process
  8876. pcbState           1   proc    STATE_SUSPENDED
  8877.  
  8878. The values on the top of the stack page are also initialized for the new
  8879. process, as follows:
  8880.  
  8881. ADDR   SP-REL   DESCRIPTION       INITIAL VALUE
  8882. ----   ------   ---------------   --------------
  8883. $ff    sp+09    exitaddr-1.h      high byte of ExitAddr-1: the exit routine
  8884. $fe    sp+08    exitaddr-1.l      low byte of ExitAddr-1: the exit routine
  8885. $fd    sp+07    pc.h              high byte of the code-execution address
  8886. $fc    sp+06    pc.l              high byte of the code-execution address
  8887. $fb    sp+05    status register   $00
  8888. $fa    sp+04    .A                $00
  8889. $f9    sp+03    .X                $00
  8890. $f8    sp+02    .Y                $00
  8891. $f7    sp+01    $ff00 save        $0e  (Kernal ROM, I/O, rest RAM0)
  8892. $f6    sp+00    -empty-           --
  8893.  
  8894. And now, the new process is ready for action.  We insert it into the ready
  8895. queue in the next position after the current process, set its state to
  8896. STATE_READY (actually, both of these operations are performed by the
  8897. MakeReady() function, which is generally useful and is called from a number of
  8898. places) and then we exit back to the calling process, returning the process id
  8899. of the calling process.  I should change this a little bit in the future, to
  8900. make it exit to the newly created child process if the priority of the child
  8901. process is greater than the priority of the parent.
  8902.  
  8903. 3.4. CONTEXT SWITCHING
  8904.  
  8905. Context switching describes the procedure of switching control of the
  8906. processor from a user process to the kernel and then switching control back to
  8907. a user process.  Normally, there is only one "style" of context switching in a
  8908. system, but for a couple of design reasons, BOS actually has three "styles" of
  8909. context switching: IRQ switching, JSR switching, and quick JSR switching.
  8910. IRQ-style switching is the one type normally implemented in operating systems
  8911. for other architectures, so it will be the one that we cover first.
  8912.  
  8913. IRQ-style context switching involves saving the full context of a process onto
  8914. its stack and into its process control block, switching into the kernel, doing
  8915. work, switching back out of the kernel, and reloading the full context of a
  8916. user process and activating to it.  All of the work of saving and restoring
  8917. the the stack portion of a process' context is handled by the ROM routines for
  8918. IRQ (and NMI and BRK) handling.  All we have to do is locate the current
  8919. process control block, save the zero-page, stack-page, stack-pointer, and
  8920. $d506 registers into the PCB, and load a $00 into the zero-page MMU register
  8921. to switch to the kernel's zeropage (where some of the kernel's variables are
  8922. stored).  Note that the interrupt will be executed using the user process'
  8923. stack; therefore, enough space should always be available on user stacks to
  8924. handle this system overhead.
  8925.  
  8926. When we are done processing the interrupt, we execute the priority-management
  8927. algorithm that was described earlier to select the next process to activate,
  8928. and then restore the zero-page, stack-page, stack-pointer, and $d506 registers
  8929. and execute the ROM stack-handling code for exiting from an interrupt.  Note
  8930. that there's a chance that we might well be exiting to a different user
  8931. process from the one that was active when the interrupt occurred.  There
  8932. aren't many registers to save and restore, so context switching has a fairly
  8933. low overhead, so there is no problem in doing it (at least) sixty times a
  8934. second.
  8935.  
  8936. JSR-style context switching is pretty much the same as IRQ-style context
  8937. switching, except that the stack will not have most of the processor registers
  8938. already saved on it; it will only have the return address that performed the
  8939. JSR.  Immediately upon entering the kernel, interrupts are disabled to prevent
  8940. all sorts of bad things from happening.  Then a function is called,
  8941. EnterKernel(), which will pull the return address of the process that called
  8942. the JSR off the stack and increment it by one (since we will be exiting by
  8943. using an RTI instruction rather than an RTS) and saves the other processor
  8944. registers onto the stack in the same way that the interrupt-handling code in
  8945. ROM would.  Then we save the four additional registers into the PCB as before,
  8946. activate the kernel zeropage, and we are switched in.
  8947.  
  8948. This style of context switching is used for kernel calls that will cause the
  8949. calling process to block (like a non-zero Delay()).  It would have been
  8950. possible to organize the kernel calls to be entered by executing a BRK
  8951. instruction, which would have caused the stack to be already set up in the
  8952. same way as with IRQ interrupts, but I decided against this for two reasons:
  8953. efficiency, it would have been slower to do this, and debugging (security?),
  8954. since I only want the BRK condition to signal a bug in the code.  The exit
  8955. from this type of context switch is the same as for the IRQ style of context
  8956. switch, since things are rigged to end up looking the same on the stack.  This
  8957. is a good thing, since the action that will cause a Delay()ed process to be
  8958. re-activated will, in fact, be an IRQ interrupt.
  8959.  
  8960. Quick JSR-style context switching is used for kernel calls that will not block
  8961. or cause a new process to be activated when they finish, such as MyPid() or
  8962. (currently) Create().  No context has to be saved since the function will get
  8963. in and out very quickly; all we have to do is switch to the kernel's zeropage
  8964. and then switch back to the user's zeropage before exit.
  8965.  
  8966. There's one more note to make about return values.  For the quick JSR-style
  8967. context switch, there is no problem with return values, since we just have to
  8968. load them into the processor registers and exit.  With the full JSR-style
  8969. context switch, the return values have to be put onto the user stack into the
  8970. positions in the stack memory the hold the processor register contents, since
  8971. these values will be what are restored into the processor immediately upon the
  8972. return to the user process.  There are no return values associated with the
  8973. IRQ style of context switching (and there'd better not be), since an interrupt
  8974. can happen at any point in the execution of a user process.
  8975.  
  8976. 3.5. DELAY PRIMITIVE
  8977.  
  8978. There are two complementary halves to the implemention of the Delay()
  8979. primitive: the half that is called by the user and causes a process to go to
  8980. sleep, and the half that wakes up a sleeping process at the correct time.
  8981. This latter half is executed by the 60-Hz system interrupt.
  8982.  
  8983. 3.5.1. USER HALF OF THE DELAY PRIMITIVE
  8984.  
  8985. The first thing that the user half of the Delay() primitive does is check to
  8986. see if the delay period is zero jiffies.  If it is, then the primitive returns
  8987. immediately to the calling process without rescheduling (without skipping to
  8988. the next ready process in line).  I may change this semantic, because it is
  8989. often useful to have a primitive that yeilds process execution to the next
  8990. ready process without actually blocking the current process.
  8991.  
  8992. If the delay period is longer than zero jiffies, then the current process is
  8993. suspended and removed from the ready queue, and the absolute time that the
  8994. process is to be reawakened is calculated and put into the "pcbWakeupTime"
  8995. field of the PCB for the current process.  The absolute wakeup time is
  8996. calculated, of course, by adding the number of jiffies to delay to the current
  8997. absolute time, which is maintained by the system and incremented on every
  8998. (60 Hz) system interrupt.
  8999.  
  9000. Then the current process control block is inserted into the delay queue at the
  9001. correct position.  The delay queue is a queue (implemented in the standard
  9002. way) of process control blocks for processes which are asleep, ordered by the
  9003. absolute wakeup time of each process such that the process that will be
  9004. awakened at the nearest time in the future is at the head of the list and that
  9005. the process which will be awakened at the farthest point in the future is at
  9006. the tail.  The following diagram gives an example:
  9007.  
  9008. CurrentTime = 2016
  9009.  
  9010.     +---------+      +---------+      +---------+      +---------+
  9011. --->| Proc A  |----->| Proc B  |----->| Proc C  |----->| Proc D  |
  9012.     | wakeup: |      | wakeup: |      | wakeup: |      | wakeup: |
  9013. <---| @ 2345  |<-----| @ 2765  |<-----| @ 54999 |<-----| @ 441   |
  9014.     +---------+      +---------+      +---------+      +---------+
  9015.     (ct+5.5sec)      (ct+12.5sec)     (ct+14.7min)     (ct+17.8min)
  9016.  
  9017. There is a rub here: only 16 bits are used for storing times, which equals
  9018. about 18.2 minutes, so we have to worry about time quantities overflowing and
  9019. wrapping around.  For example, if the current time is 48232 and a process
  9020. wants to sleep for 18000 jiffies (5 minutes), then its wakeup time would be at
  9021. 696 jiffies, accounting for the 16-bit wraparound, which is a lower numerical
  9022. value than the current time, or than the wakeup time of any other process that
  9023. will wake up before the current-time wraparound.  In fact, all timers have
  9024. this wraparound problem (although with 64-bit times, wraparound periods would
  9025. be expressed in millions of millennia rather than in minutes).  Sixteen bits
  9026. is a good number of bits to use, however, because that is the maximum delay
  9027. period (2^16-1).
  9028.  
  9029. When we insert a new process into the delay queue, we scan the delay queue
  9030. from the head and continue until we find a record that has a time that is
  9031. higher than or equal to the wakeup time of the new process (or we hit the end
  9032. of the queue).  Then, we insert the new process immediately before this
  9033. point.  To handle the wraparound problem, all comparisons of wakeup times are
  9034. done using 17 bits (well, really 24 bits).  For each value in the comparison,
  9035. we add 65536 to it (set its 17th bit) if the value is less than the current
  9036. time.  We don't have to worry about the current time changing while we are
  9037. doing this, because interrupts will be disabled for the entire time that
  9038. we are executing the system call, as per usual.  Things could go horribly
  9039. wrong anyway if interrupts were not disabled.
  9040.  
  9041. Okay, so now our delaying process is removed from the ready queue, its
  9042. complete context is saved, and it is put into the delay queue at the right
  9043. spot.  So, set the active process pointer to the next ready process in the
  9044. system and finish by activating the next ready process.
  9045.  
  9046. 3.5.2. SYSTEM HALF OF THE DELAY PRIMITIVE
  9047.  
  9048. During each 60-Hz system interrupt, the current time (jiffy counter) is
  9049. incremented by one.  Note that since this timer is only 16 bits wide, it is
  9050. not suitable for keeping track of the current time of day; for this purpose,
  9051. the TOD clocks in the CIA chips should (and will) be used.  The jiffy counter
  9052. may also be inaccurate if interrupts are disabled for a long period of time,
  9053. such as they are during some Commodore-Kernal I/O operations.
  9054.  
  9055. After incrementing the time, the kernel checks to see if any Delay()ed
  9056. processes need to be woken up.  If there are no processes in the delay queue,
  9057. then this is a quick check.  If there are any processes in the queue, then if
  9058. the wakeup time of the head process is equal to the current time, then that
  9059. process is woken up and this check is performed repeatedly until the condition
  9060. fails, since there may be multiple processes that want to be woken up at the
  9061. same jiffy of absolute time.  Note that because of the scheduling for a
  9062. freshly unblocked process, the process that Delay()ed first will be the
  9063. first one activated after it is woken up, if there are multiple processes
  9064. woken up at the start of the same jiffy.
  9065.  
  9066. 3.6. SYSTEM BOOTSTRAPPING
  9067.  
  9068. Operating systems always have a bootstrapping problem, because you always need
  9069. to use the services of the operating system in order to start it up, but, of
  9070. course, it's not started up yet, chicken and egg, catch-22.  So, what usually
  9071. ends up happening is that you just "fake it", start from somewhere, get the
  9072. ball rolling, and snowball up to a fully running system.
  9073.  
  9074. The first thing that the kernel does is change all of the interrupt vectors
  9075. (IRQ, NMI, and BRK) to my custom routines.  I need to cover all of the
  9076. interrupts, since I chave the zero page during the execution of the system,
  9077. and if a BRK or NMI were to happen and be serviced by the Commodore-Kernal ROM
  9078. routines, all hell would break loose.  Currently, the NMI and BRK routines
  9079. just clean things up and return to BASIC.
  9080.  
  9081. Then we initialize the kernel variables, including the delay queue and the
  9082. jiffy counter.
  9083.  
  9084. And then we fake the creation of the Null process.  For the purposes of
  9085. bootstrapping, the Null process doubles as the "Boot" process.  Its process
  9086. control block is not allocated in the normal way, either; it is at a fixed
  9087. location, and its PCB doubles as the head of the process list.  A kluge here
  9088. and a hack there and the Null process is initialized and "joined in
  9089. progress".  Then, the Null process creates the Init process, using a standard
  9090. call to the Create() primitive, and then the Null process goes into an endless
  9091. loop of incrementing the 32-bit value at addresses $400-$403, the first four
  9092. locations of the 40-column screen memory.  It doesn't matter whether you run
  9093. BOS with the clock in Fast or Slow mode, except in terms of performance.
  9094.  
  9095. It is the responsibility of the Init process to start up all of the user
  9096. processes in the user application after Init starts running.  In the current
  9097. implementation, Init starts up all of the other processes in the test
  9098. application and then becomes the Commodore-Kernal Server, which is a
  9099. convenient organization, since all of the other processes can find out the pid
  9100. of the Kernal Server merely by calling MyParentPid().
  9101.  
  9102. 4. INTERPROCESS COMMUNICATION
  9103.  
  9104. In this system, processes are not strictly independent and competitive; many
  9105. must cooperate and comunicate to get work done.  To facilitiate this
  9106. interprocess communication (IPC), a particular paradigm was chosen: the Remote
  9107. Procedure Call (RPC) paradigm.  RPC is a message-passing scheme that is used
  9108. with the much-hyped Client/Server system-architecture model.  Its operation
  9109. parallels the implicit operations that take place when you call a local
  9110. procedure (a subroutine).
  9111.  
  9112. The RPC message-passing paradigm is also coupled with a shared-memory paradigm
  9113. to offer greater performance for passing around massive amounts of data.  All
  9114. processes in the system (and in the entire distributed system when this OS is
  9115. extended) have global access to all of the memory in the system.  The coupling
  9116. of the two paradigms is such that you get the best of both worlds: the
  9117. convenence and natural interprocess *coordination* (synchronization) semantics
  9118. of RPC and the convenience and raw performance of shared storage.
  9119.  
  9120. 4.1. MESSAGE-PASSING CALLS
  9121.  
  9122. The kernel provides three primitives for message passing:
  9123.  
  9124. CALL NAME     INPUT ARGUMENTS                  RETURN VALUES
  9125. -----------   ------------------------------   -------------------------------
  9126. Send          ( .AY=msgHead )                  .CS=err(.A=errcode)
  9127. Receive       ( .AY=msgHead )                  .AY=senderPid
  9128. Reply         ( .AY=msgHead[msgRet,msgData] )  .CS=err(.A=errcode)
  9129.  
  9130. These calls will send a message from one process (the client) to another
  9131. process (the server) and wait for a reply, receive a message from another
  9132. process (a client), and reply to a message sent from another process (a
  9133. client) that has been received, respectively.
  9134.  
  9135. 4.1.1. MESSAGE-HEADER DATA STRUCTURE
  9136.  
  9137. Each of the message-passing primitives requires a pointer to a message-header
  9138. data structure that is stored in the user program's data space.  The message
  9139. header must be initialized with appropriate values before a message can be
  9140. sent.  Note that this scheme of passing a pointer to a message header allows
  9141. you to have multiple message headers lying around, initialized and ready for
  9142. action, and you can easily pick between them.  Here is what a message header
  9143. looks like:
  9144.  
  9145. OFF   SIZ   CLASS   LABEL
  9146. ---   ---   -----   ------
  9147.   0     2   pid     msgTo
  9148.   2     2   pid     msgFrom
  9149.   4     4   buf     msgBuf
  9150.   8     2   buf     msgLen
  9151.  10     4   buf     msgRepBuf
  9152.  14     2   buf     msgRepLen
  9153.  16     1   data    msgOp
  9154.  17     1   data    msgRet
  9155.  18     2   data    msgObj
  9156.  20     4   data    msgData
  9157.  24     -   -       SIZE
  9158.  
  9159. You should not put too much faith in the offsets of the fields in the data
  9160. structure remaining static; you should always use the label to access the
  9161. fields of the structure, as in:
  9162.  
  9163. sta myMessageHeader+msgTo+0
  9164. sty myMessageHeader+msgTo+1
  9165.  
  9166. 4.1.1.1. PID-CLASS FIELDS
  9167.  
  9168. The first two fields, of class "pid", are used to identify the processes
  9169. involved in an RPC interaction.  The "msgTo" field is the pid of the process
  9170. that a message is to be/has been sent to, and the "pcbFrom" field is the id of
  9171. the process which a message has been received from.  For security reasons, the
  9172. sender does not fill in the "pcbFrom" field; the kernel does after the message
  9173. has been sent and the sender is blocked.  (Or else the sender could fake being
  9174. someone else).  The "pcbTo" field is used as the destination for when a
  9175. message is being sent and must be filled in with a legitimate value on a send
  9176. operation, and the "pcbFrom" field is used as the destination when a message
  9177. is being replied to, and must be filled in with a legitimate value on a reply
  9178. operation.  The "pcbTo" field is the only field of the message header that
  9179. actually needs to have a legitimate value before a message can be sent.
  9180.  
  9181. 4.1.1.1. BUF-CLASS FIELDS
  9182.  
  9183. The next four fields, of class "buf", point out the send and reply buffers in
  9184. memory and the sizes of each.  The send buffer ("msgBuf"/"msgLen") is expected
  9185. to point to a region of near/far memory that contains valid data for a send
  9186. operation, and the reply buffer ("msgRepBuf"/"msgRepLen") is expected to point
  9187. to a valid area of memory for the server to fill in with any bulky result data
  9188. from an RPC request.  Each of the message-buffer pointers is four bytes in
  9189. size to allow for future expansion when the kernel will support "far" memory
  9190. that will be accessed through 32-bit pointers.  User processes are expected to
  9191. access these "far" buffers directly themselves, through the global shared
  9192. memory.  This eliminates the system overhead of uselessly copying bulky data
  9193. from place to place.
  9194.  
  9195. There are two special notes to make about there "buf" fields.  First, they
  9196. don't actually have to be used how they're intended to be used. as long as
  9197. both the client and the server agree on what the contents of these fields are
  9198. supposed to mean.  In this respect, the fields can be used to quickly pass
  9199. twelve bytes of completely arbitrary information.  This is useful because many
  9200. RPCs only require that a small amount of information be transferred from one
  9201. process to another, or at least that bulky data be passed in only one
  9202. direction (like read or write), so that one of the buffer pointers is free to
  9203. be used quick, tiny data.
  9204.  
  9205. Second, on the sending side, the "buffer" that is pointed to does not have to
  9206. be a "buffer" at all; it can be an arbitrary data structure that has an
  9207. arbitrary number of pieces, scattered throughout the global memory of the
  9208. system.  The only responsibility of the sender is to insure that no one else
  9209. will be attempting to modify the shared data simultaneously while the server
  9210. is accessing it.  This scheme is quite ingenious, I think (thank you, thank
  9211. you).  (The scheme may appear to have a security leak in the design, but our
  9212. system has no real hardware security anyway).
  9213.  
  9214. The expected usage of buffers will be for the sender to use near memory for
  9215. the request and reply buffers and access them as regular near memory to
  9216. construct and interpret request and reply messages.  The receiver will (in the
  9217. future) access the buffers as far memory (which they may very well be since
  9218. processes will be allowed to execute on different banks of internal memory and
  9219. even on different machines), and may wish to fetch parts of messages into near
  9220. memory for processing.  The use of far pointers makes it so that data is
  9221. copied only when necessary, and copied only once.
  9222.  
  9223. 4.1.1.3. DATA-CLASS FIELDS
  9224.  
  9225. The final four fields, of class "data", are intended to be used to
  9226. conveniently pass small amounts of arbitrary data.  This data can be
  9227. arbitrary, but the fields do have a convention that should usually be
  9228. followed, unless both parties agree to an alternative usage.
  9229.  
  9230. The "msgOp" field is intended to be the "operation code" that a client process
  9231. wishes a server to execute.  The "msgRet" field is intended to be the return/
  9232. error code that is returned from the server to the client upon completion of
  9233. an operation.  The "msgObj" field is intended to be used by the client to
  9234. indicate which of the server's "objects" the client wishes to perform the
  9235. operation on.  And the "msgData" field is intended to contain four bytes of
  9236. arbitrary user data that is passed in with an operation and is passed back
  9237. from the server to give return values.  In the spirit of these semantics, the
  9238. data in all of the fields is send with a request, but only the data in the
  9239. "msgRet" and "msgData" fields is passed back in a reply operation.  None of
  9240. the other fields are passed back in a reply operation (the field values will
  9241. remain how they were before the send, for the sender).  Take special note that
  9242. the "msgRepLen" field will not be passed back; if there is less data returned
  9243. than was asked for by an operation, you will have to encode the "actual"
  9244. reply-buffer length into the "msgData" field.
  9245.  
  9246. 4.1.2. SEND
  9247.  
  9248. Send() is used to transmit a message to a remote process and get back a reply
  9249. message.  The .AY register contains the near-memory address of the message
  9250. header, which must have its "msgTo" field filled in to be the pid of the
  9251. process that the message is being sent to.  The sending process will suspend
  9252. its execution while it is waiting for remote process to process its request.
  9253. If there is to be bulky reply data for the request (such as there would be for
  9254. a "read" request to a file server), then space for the reply buffer must be
  9255. allocated and indicated in the message header.  The reply-buffer space should
  9256. normally be owned by the sender.
  9257.  
  9258. If there is an error in passing the message, the the error return will be
  9259. indicated by the carry flag being set and the error code will be returned in
  9260. the .A register.  Some possible errors will be, in the future:  destination
  9261. process is not valid, and that destination process died before receiving/
  9262. replying to your message.  (These conditions are not currently checked).  Also
  9263. in the future, this call will work completely transparently for passing
  9264. messages between machines in a network.
  9265.  
  9266. 4.1.3. RECEIVE
  9267.  
  9268. Receive() is used to receive a message transmitted by a remote process to the
  9269. current process.  The receiver will block until another process does a
  9270. corresponding Send() operation, and then the message header sent by the sender
  9271. will be retrieved into the message-header buffer pointed to by the .AY
  9272. register, for this call.  No error returns are possible.  The pid of the
  9273. sending process will be returned in the .AY register as well as in the
  9274. "msgFrom" field of the receive-message-header buffer.  The receiver is then
  9275. expected to eventually call the Reply() primitive to re-awaken the sender.
  9276. The receiver is free to do anything it wants to after receiving a message from
  9277. a process, including receiving messages from other processes.  Messages are
  9278. received from other processes in FIFO order.
  9279.  
  9280. A similar ReceiveSpecific() primitive may be provided in the future.  It would
  9281. only accept a message from a specifically named process and would enqueue all
  9282. other messages that are received before the specific message, to be received
  9283. later.
  9284.  
  9285. 4.1.4. REPLY
  9286.  
  9287. Reply() is used to re-awaken a process that sent a message that was Receive()d
  9288. by the current process.  The current process is expected to have set up the
  9289. return information in the reply-message-header buffer and the reply buffer
  9290. area according to the client's wishes before calling the Reply() primitive.
  9291. The near address of the reply-message-header buffer is loaded into the .AY
  9292. register as an argument to the call.  Only the "msgFrom", "msgRet", and
  9293. "msgData" fields need to have values.  The "msgFrom" field identifies the
  9294. process to send the reply message to, and that process must be in the state of
  9295. waiting for a reply from the Reply()ing process, or an error will be
  9296. returned.  An error is indicated by the carry flag being set on return and the
  9297. error code is loaded in the .A register.  In the case of an error, no action
  9298. will have been performed by the system.
  9299.  
  9300. 4.2. IMPLEMENTATION
  9301.  
  9302. The fields of the process control block that are used for message passing
  9303. are restated here:
  9304.  
  9305. OFF   SIZ   CLASS   LABEL
  9306. ---   ---   -----   ------
  9307.  12     2   ipc     pcbSendMsgPtr  (overlap)
  9308.  12     2   ipc     pcbRecvMsgPtr  (overlap)
  9309.  14     2   ipc/q   pcbSendQHead
  9310.  16     2   ipc/q   pcbSendQTail
  9311.  18     1   ipc/q   pcbSendQFlag
  9312.  19     1   ipc/q   pcbSendQCount
  9313.  20     2   ipc     pcbBlockedOn
  9314.  22     2   ipc     pcbReceiveFrom
  9315.  
  9316. The "pcbBlockedOn" field is used to allow Reply() to verify that the pid it is
  9317. instructed to send a reply message to is indeed waiting for a reply from the
  9318. task calling Reply().  The "pcbSendQ*" fields constitute a queue head for a
  9319. list of process control blocks that are waiting to send a message to the
  9320. current process.  The "pcbSendMsgPtr" and "pcbRecvMsgPtr" fields are used to
  9321. save the message data parameters of a Send() or Receive() call, respectively,
  9322. when it has to be suspended without a transfer of the message header.  When
  9323. the other process involved performs the corresponding operation, the first
  9324. process' header buffer pointer is recovered from its process control block.
  9325. The "pcbReceiveFrom" field is unused at this time.
  9326.  
  9327. The process states of STATE_SEND, STATE_REPLY, and STATE_RECEIVE are used with
  9328. message passing.  The STATE_SEND state means that the current process has sent
  9329. a message to a server process and is waiting for it to do a Receive().  The
  9330. STATE_REPLY state means that the current process has sent a message to a
  9331. server process, the message has been Receive()d, and that the current process
  9332. is waiting for the server process to perform a Reply().  The STATE_RECEIVE
  9333. state means that the current process has performed a Receive() and is waiting
  9334. for some other process to perform a corresponding Send().  These state
  9335. names/meanings may be a bit inconsistent; deal with it.
  9336.  
  9337. The implementation of the actual Send(), Receive(), and Reply() operations is
  9338. actually quite straight-forward.  Both Send() and Receive() have to handle two
  9339. possibile situations: either the other process involved has already performed
  9340. its corresponding operation and is waiting, or it has not.  Reply() is
  9341. simplified in that it knows that the sender is already waiting for its reply
  9342. so it can proceed to copy the reply-message-header contents directly.
  9343.  
  9344. The Send() primitive (will) checks the given destination pid for validity and
  9345. then checks the state of the recipient process.  If the recipient process is
  9346. in STATE_RECEIVE, the Send() function copies the message-header contents
  9347. directly to the receive-header buffer of the recipient.  The address of the
  9348. receive-header buffer is taken from the "pcbRecvMsgPtr" field of the
  9349. receiver's process control block in this case.  The receiver's return value
  9350. (the sending process' pid) is set up (on the receiving process' stack) and the
  9351. receiver is awakened while the sender is put to sleep, in STATE_REPLY state
  9352. (since the receive has already happened, it is waiting for the corresponding
  9353. Reply()).
  9354.  
  9355. If the recipient process is not in the STATE_RECEIVE state, then the sending
  9356. process will have to wait for the recipient to perform a Receive().  The
  9357. sender's message-header buffer address is stored into its process control
  9358. block, the sender's process control block is linked into the recipient
  9359. process' "pcbSendQ*", and the sender is put to sleep, in the STATE_SEND
  9360. state.
  9361.  
  9362. The Send() function does not set up the return value for the user's
  9363. system call since that will not be known until another process performs the
  9364. corresponding Reply().  A return value is set up immediately only in the case
  9365. of an error.  The possible error returns from Send() are: invalid pid and
  9366. reply too long (in which case the reply is truncated).
  9367.  
  9368. The Receive() primitive first checks its "pcbSendQ*" to see if any processes
  9369. have already tried to send a message to the receiver.  If there is a process
  9370. there, the sender's process control block is removed from the head of the send
  9371. queue then the sender process' state is changed to STATE_REPLY and the sent
  9372. message-header contents (dereferenced by the sender's "pcbSendMsgPtr" pointer)
  9373. are copied into the receiver's message-header buffer.  The Receive() primitive
  9374. then exits returning the pid of the sender.  No error returns are possible.
  9375.  
  9376. If there is no process enqueued in the recipient process' "pcbSendQ*", then
  9377. the receiving process is put to sleep in the STATE_RECEIVE state and its
  9378. message-header buffer pointer is copied into its process control block.
  9379.  
  9380. The Reply() primitive verifies that the destination process is valid (but not
  9381. in the current implementation) and is actually awaiting a reply from the
  9382. replying process.  If not, it craps out.  Otherwise, it copies the two
  9383. message-header fields and awakens the sender.  The return value of the sender
  9384. is (already) set up to be carry-clear (no error) and the Reply() primitive
  9385. returns error-free too.
  9386.  
  9387. The Exit() kernel call does not currently recover from a process performing a
  9388. Receive() and then Exit()ing before performing the corresponding Reply().
  9389. Some care will have to be taken to insure that all process involved in IPC can
  9390. consistently recover if one of the processes gets blown away, for whatever
  9391. reason (including Exit()).  Such consistent recovery has to be carefully
  9392. thought out for any kind of operating system; however, since there are only a
  9393. small number of kernel concepts in this one, consistent recovery is that much
  9394. easier to insure.
  9395.  
  9396. 5. CONCLUSION
  9397.  
  9398. So there ya have it; the start of a real operating system for the Commodore
  9399. 128.  What the operating system needs in terms of features is to be extended
  9400. to execute processes on any bank of internal memory, to access far memory, and
  9401. to be distributed so that it will work across multiple hosts.  What it needs
  9402. in terms of software is: device drivers, a command shell, utility programs,
  9403. and an assembler that can produce relocatable code.  Oh where, oh where shall
  9404. I ever find such software???  ;-)
  9405. ------------------------------------------------------------------------------
  9406. APPENDIX A. SOURCE-CODE LISTING
  9407.  
  9408. The source code follows.  Extract everything between the "-----=-----" lines
  9409. and save into a file named "bos.s" (or whatever) and then run it through the
  9410. ACE assembler to generate the executable program (which is also included below
  9411. for your convenience).  The ACE assembler is available for free with the
  9412. ACE-128/64 system.
  9413.  
  9414. I have not gone through and fully documented the source code, since I have
  9415. been sitting on this program for quite a while and am in a rush to get it out
  9416. the door.  Besides, the functionality of each important component has already
  9417. been discussed.
  9418.  
  9419. -----=-----
  9420. ;simple multitasking kernel by Craig Bruce, started 25-Oct-1994.
  9421.  
  9422. ;This program is written in the ACE-Assembler format.
  9423.  
  9424.    org $1300
  9425.    jmp main
  9426.  
  9427. ;======== declarations ========
  9428.  
  9429. pcbNext         = 00 ;(2) mgmt
  9430. pcbPrev         = 02 ;(2) mgmt
  9431. pcbIsHead       = 04 ;(1) mgmt
  9432. pcbQCount       = 05 ;(1) mgmt
  9433. pcbSP           = 06 ;(1) ctxt
  9434. pcbStackPage    = 07 ;(1) ctxt
  9435. pcbZeroPage     = 08 ;(1) ctxt
  9436. pcbD506         = 09 ;(1) ctxt
  9437. pcbPriority     = 10 ;(1) sche
  9438. pcbCountdown    = 11 ;(1) sche
  9439. pcbWakeupTime   = 12 ;(2) sche (overlap)
  9440. pcbWaitEvent    = 12 ;(1) sche (overlap)
  9441. pcbSendMsgPtr   = 12 ;(2) sche (overlap)
  9442. pcbRecvMsgPtr   = 12 ;(2) sche (overlap)
  9443. pcbSendQHead    = 14 ;(2) ipc
  9444. pcbSendQTail    = 16 ;(2) ipc
  9445. pcbSendQFlag    = 18 ;(1) ipc
  9446. pcbSendQCount   = 19 ;(1) ipc
  9447. pcbBlockedOn    = 20 ;(2) ipc
  9448. pcbReceiveFrom  = 22 ;(2) ipc
  9449. pcbParent       = 24 ;(2) proc
  9450. pcbState        = 26 ;(1) proc
  9451. pcbSize         = 27
  9452.  
  9453. STATE_READY     = $c0
  9454. STATE_SEND      = $c1
  9455. STATE_RECEIVE   = $c2
  9456. STATE_REPLY     = $c3
  9457. STATE_DELAY     = $c4
  9458. STATE_SUSPENDED = $c5
  9459. STATE_EVENT     = $c6
  9460.  
  9461. KERN_ERR_OK            = $e0
  9462. KERN_ERR_PID_NOT_REPLY = $e1
  9463.  
  9464. msgTo           =  0 ;(2)
  9465. msgFrom         =  2 ;(2)
  9466. msgBuf          =  4 ;(4)
  9467. msgLen          =  8 ;(2)
  9468. msgRepBuf       = 10 ;(4)
  9469. msgRepLen       = 14 ;(2)
  9470. msgOp           = 16 ;(1)
  9471. msgRet          = 17 ;(1)
  9472. msgObj          = 18 ;(2)
  9473. msgData         = 20 ;(4)
  9474. msgSize         = 24
  9475.  
  9476. queueHeadSize   = 6
  9477.  
  9478. nullPcb    : buf pcbSize
  9479. delayQueue : buf queueHeadSize
  9480. jiffyTime  : buf 2
  9481.  
  9482. activePid  = 02 ;(2)
  9483. p          = 04 ;(2)
  9484. q          = 06 ;(2)
  9485. pcbPtr     = 08 ;(2)
  9486. msgPtr     = 10 ;(2)
  9487. pageAlloc  = 12 ;(1)
  9488.  
  9489. ;Stack: ($ff)      : exitaddr-1.h
  9490. ;       ($fe)      : exitaddr-1.l
  9491. ;       ($fd) sp+07: pc.h
  9492. ;       ($fc) sp+06: pc.l
  9493. ;       ($fb) sp+05: status register
  9494. ;       ($fa) sp+04: .A
  9495. ;       ($f9) sp+03: .X
  9496. ;       ($f8) sp+02: .Y
  9497. ;       ($f7) sp+01: $ff00 save
  9498. ;       ($f6) sp+00: -empty-
  9499.  
  9500. bkBOS    = $0e
  9501. bkUser   = $0e
  9502. bkSelect = $ff00
  9503. vic      = $d000
  9504. sid      = $d400
  9505. mmuZeroPage  = $d507
  9506. mmuStackPage = $d509
  9507. IrqExit  = $ff33
  9508.  
  9509. ; Create  ( .AY=address, .X=priority ) : .AY=pid
  9510. ; Exit    ( .A=code, .X=$00 )
  9511. ; MyPid   ( ) : .AY=pid
  9512. ; MyParentPid ( ) : .AY=parentPid
  9513. ; Suspend ( )
  9514. ; Delay   ( .AY=jiffies ) : .CS=err
  9515. ; Send    ( .AY=msgBuf ) : .CS:.A=err
  9516. ; Receive ( .AY=msgBuf ) : .AY=senderPid
  9517. ; Reply   ( .AY=msgBuf[msgRet,msgData] ) : .CS:.A=err
  9518.  
  9519. ;======== kernel code ========
  9520.  
  9521. main = *
  9522.    sei
  9523.    ;** entry
  9524.    lda #bkBOS
  9525.    sta bkSelect
  9526.    ;** set interrupt vectors
  9527.    lda #<IrqHandler
  9528.    ldy #>IrqHandler
  9529.    sta $0314
  9530.    sty $0315
  9531.    lda #<BrkHandler
  9532.    ldy #>BrkHandler
  9533.    sta $0316
  9534.    sty $0317
  9535.    lda #<NmiHandler
  9536.    ldy #>NmiHandler
  9537.    sta $0318
  9538.    sty $0319
  9539.    ;** initialize delay queue
  9540.    lda #0
  9541.    sta jiffyTime+0
  9542.    sta jiffyTime+1
  9543.    lda #<delayQueue
  9544.    ldy #>delayQueue
  9545.    sta q+0
  9546.    sty q+1
  9547.    jsr QueueInit
  9548.    ;** initialize null/boot process
  9549.    lda #<nullPcb
  9550.    ldy #>nullPcb
  9551.    sta nullPcb+pcbNext+0
  9552.    sty nullPcb+pcbNext+1
  9553.    sta nullPcb+pcbPrev+0
  9554.    sty nullPcb+pcbPrev+1
  9555.    sta activePid+0
  9556.    sty activePid+1
  9557.    lda #$ff
  9558.    sta nullPcb+pcbIsHead
  9559.    lda #0
  9560.    sta nullPcb+pcbQCount
  9561.    lda #>$2000
  9562.    sta pageAlloc
  9563.    lda #STATE_READY
  9564.    sta nullPcb+pcbState
  9565.    lda #2
  9566.    sta nullPcb+pcbPriority
  9567.    cli
  9568.    jmp Null
  9569.  
  9570. Null = *
  9571.    ;** create init process
  9572.    lda #<Init
  9573.    ldy #>Init
  9574.    ldx #1
  9575.    jsr Create
  9576.    ;** go into endless loop
  9577. -  inc $0400
  9578.    bne +
  9579.    inc $0401
  9580.    bne +
  9581.    inc $0402
  9582.    bne +
  9583.    inc $0403
  9584. +  jmp -
  9585.  
  9586. NmiHandler = *
  9587. BrkHandler = *
  9588. Shutdown = *
  9589.    ;** restore interrupt vectors
  9590.    sei
  9591.    lda #<$fa65
  9592.    ldy #>$fa65
  9593.    sta $0314
  9594.    sty $0315
  9595.    lda #<$fa40
  9596.    ldy #>$fa40
  9597.    sta $0318
  9598.    sty $0319
  9599.    ldx #250
  9600.    txs
  9601.    lda #$00
  9602.    sta mmuZeroPage
  9603.    sta mmuZeroPage+1
  9604.    ldx #$01
  9605.    stx mmuStackPage
  9606.    sta mmuStackPage+1
  9607.    lda #%00000100
  9608.    sta $d506
  9609.    cli
  9610.    jmp $4db7
  9611.    
  9612. zpSave : buf 1
  9613.  
  9614. createAddr     : buf 2
  9615. createPriority : buf 1
  9616. createZeropage : buf 1
  9617. createStack    : buf 1
  9618. createPcb      : buf pcbSize
  9619.  
  9620. Create = *  ;( .AY=address, .X=priority ) : .AY=pid
  9621.    sei
  9622.    ;** switch in
  9623.    sta createAddr+0
  9624.    sty createAddr+1
  9625.    stx createPriority
  9626.    lda mmuZeroPage
  9627.    sta zpSave
  9628.    lda #$00
  9629.    sta mmuZeroPage
  9630.    ;** allocate resources
  9631.    lda #$00
  9632.    ldy pageAlloc
  9633.    sta pcbPtr+0
  9634.    sty pcbPtr+1
  9635.    iny
  9636.    sty createZeropage
  9637.    iny
  9638.    sty createStack
  9639.    iny
  9640.    sty pageAlloc
  9641.    cpy #>$c000
  9642.    bcc +
  9643.    brk   ; recover gracefully from the condition of running out of memory
  9644. +
  9645.    ;** initialize pcb
  9646.    ;** pcbNext         ;(2) mgmt := 0
  9647.    ;** pcbPrev         ;(2) mgmt := 0
  9648.    ;** pcbIsHead       ;(1) mgmt := 0
  9649.    ;** pcbQCount       ;(1) mgmt := 0
  9650.    ;** pcbSP           ;(1) ctxt := $f6
  9651.    ;** pcbStackPage    ;(1) ctxt := new
  9652.    ;** pcbZeroPage     ;(1) ctxt := new
  9653.    ;** pcbD506         ;(1) ctxt := $04
  9654.    ;** pcbPriority     ;(1) sche := given
  9655.    ;** pcbCountdown    ;(1) sche := priority
  9656.    ;** pcbWakeupTime   ;(2) sche := 0
  9657.    ;** pcbSendQHead    ;(2) ipc  := QueueInit
  9658.    ;** pcbSendQTail    ;(2) ipc  := QueueInit
  9659.    ;** pcbSendQFlag    ;(1) ipc  := QueueInit
  9660.    ;** pcbSendQCount   ;(1) ipc  := QueueInit
  9661.    ;** pcbBlockedOn    ;(2) ipc  := 0
  9662.    ;** pcbReceiveFrom  ;(2) ipc  := 0
  9663.    ;** pcbParent       ;(2) proc := creator
  9664.    ;** pcbState        ;(1) proc := STATE_SUSPENDED
  9665.    ldx #pcbSize-1
  9666.    lda #$00
  9667. -  sta createPcb,x
  9668.    dex
  9669.    bpl -
  9670.    lda #$f6
  9671.    sta createPcb+pcbSP
  9672.    lda createStack
  9673.    sta createPcb+pcbStackPage
  9674.    lda createZeropage
  9675.    sta createPcb+pcbZeroPage
  9676.    lda #$04
  9677.    sta createPcb+pcbD506
  9678.    lda createPriority
  9679.    sta createPcb+pcbPriority
  9680.    sta createPcb+pcbCountdown
  9681.    lda activePid+0
  9682.    ldy activePid+1
  9683.    sta createPcb+pcbParent+0
  9684.    sty createPcb+pcbParent+1
  9685.    lda #STATE_SUSPENDED
  9686.    sta createPcb+pcbState
  9687.    ldy #pcbSize-1
  9688. -  lda createPcb,y
  9689.    sta (pcbPtr),y
  9690.    dey
  9691.    bpl -
  9692.    lda pcbPtr+0
  9693.    clc
  9694.    adc #pcbSendQHead
  9695.    sta q+0
  9696.    lda pcbPtr+1
  9697.    adc #0
  9698.    sta q+1
  9699.    jsr QueueInit
  9700.  
  9701.    ;** initialize new stack
  9702.    ;** Stack: ($ff)      : exitaddr-1.h     := >ExitAddr
  9703.    ;**        ($fe)      : exitaddr-1.l     := <ExitAddr
  9704.    ;**        ($fd) sp+07: pc.h             := >Addr
  9705.    ;**        ($fc) sp+06: pc.l             := <Addr
  9706.    ;**        ($fb) sp+05: status register  := $00
  9707.    ;**        ($fa) sp+04: .A               := $00
  9708.    ;**        ($f9) sp+03: .X               := $00
  9709.    ;**        ($f8) sp+02: .Y               := $00
  9710.    ;**        ($f7) sp+01: $ff00 save       := $0e
  9711.    ;**        ($f6) sp+00: -empty-
  9712.    lda #$00
  9713.    ldy createStack
  9714.    sta p+0
  9715.    sty p+1
  9716.    ldy #$f6+1
  9717.    lda #bkUser
  9718.    sta (p),y  ;$ff00
  9719.    iny
  9720.    ldx #4
  9721.    lda #$00
  9722. -  sta (p),y
  9723.    iny
  9724.    dex
  9725.    bne -
  9726.    lda createAddr+0
  9727.    sta (p),y
  9728.    iny
  9729.    lda createAddr+1
  9730.    sta (p),y
  9731.    iny
  9732.    lda #<DefaultExit-1
  9733.    sta (p),y
  9734.    iny
  9735.    lda #>DefaultExit-1
  9736.    sta (p),y
  9737.  
  9738.    ;** make new process ready
  9739.    jsr MakeReady
  9740.  
  9741.    ;** switch out
  9742.    lda pcbPtr+0
  9743.    ldy pcbPtr+1
  9744.    ldx zpSave
  9745.    stx mmuZeroPage
  9746.    clc
  9747.    cli
  9748.    rts
  9749.  
  9750. MakeReady = *  ;( (pcbPtr)=pcb ) ;after activePid
  9751.    ldy #pcbState
  9752.    lda #STATE_READY
  9753.    sta (pcbPtr),y
  9754.    lda #<nullPcb
  9755.    ldy #>nullPcb
  9756.    sta q+0
  9757.    sty q+1
  9758.    lda activePid+0
  9759.    ldy activePid+1
  9760.    sta p+0
  9761.    sty p+1
  9762.    jsr QueueInsert
  9763.    rts
  9764.  
  9765. QueueInit = *  ;( (q)=queueHead )
  9766.    lda q+0
  9767.    ldy q+1
  9768.    sta queueInitVals+pcbNext+0
  9769.    sty queueInitVals+pcbNext+1
  9770.    sta queueInitVals+pcbPrev+0
  9771.    sty queueInitVals+pcbPrev+1
  9772.    lda #$ff
  9773.    sta queueInitVals+pcbIsHead
  9774.    lda #0
  9775.    sta queueInitVals+pcbQCount
  9776.    ldy #queueHeadSize-1
  9777. -  lda queueInitVals,y
  9778.    sta (q),y
  9779.    dey
  9780.    bpl -
  9781.    rts
  9782.    queueInitVals : buf queueHeadSize
  9783.  
  9784. QueueInsert = *  ;( (q)=queueHead, (p)=nodeToInsertAfter, (pcbPtr)=newItem )
  9785.    ;** q->count +:= 1
  9786.    clc
  9787.    ldy #pcbQCount
  9788.    lda (q),y
  9789.    adc #1
  9790.    sta (q),y
  9791.  
  9792.    ;** pcbPtr->next := p->next
  9793.    ldy #pcbNext
  9794.    lda (p),y
  9795.    sta (pcbPtr),y
  9796.    iny
  9797.    lda (p),y
  9798.    sta (pcbPtr),y
  9799.  
  9800.    ;** pcbPtr->prev := p
  9801.    iny
  9802.    lda p+0
  9803.    sta (pcbPtr),y
  9804.    iny
  9805.    lda p+1
  9806.    sta (pcbPtr),y
  9807.  
  9808.    ;** p->next->prev := pcbPtr
  9809.    ldy #pcbNext
  9810.    lda (p),y
  9811.    sta q+0
  9812.    iny
  9813.    lda (p),y
  9814.    sta q+1
  9815.    ldy #pcbPrev
  9816.    lda pcbPtr+0
  9817.    sta (q),y
  9818.    iny
  9819.    lda pcbPtr+1
  9820.    sta (q),y
  9821.  
  9822.    ;** p->next := pcbPtr
  9823.    ldy #pcbNext
  9824.    lda pcbPtr+0
  9825.    sta (p),y
  9826.    iny
  9827.    lda pcbPtr+1
  9828.    sta (p),y
  9829.    rts
  9830.  
  9831. QueueUnlink = *  ;( (q)=queueHead, (pcbPtr)=node )  ;uses p
  9832.    ;** pcbPtr->next->prev := pcbPtr->prev
  9833.    ldy #pcbNext
  9834.    lda (pcbPtr),y
  9835.    sta p+0
  9836.    iny
  9837.    lda (pcbPtr),y
  9838.    sta p+1
  9839.    ldy #pcbPrev
  9840.    lda (pcbPtr),y
  9841.    sta (p),y
  9842.    iny
  9843.    lda (pcbPtr),y
  9844.    sta (p),y
  9845.  
  9846.    ;** pcbPtr->prev->next := pcbPtr->next
  9847.    ldy #pcbPrev
  9848.    lda (pcbPtr),y
  9849.    sta p+0
  9850.    iny
  9851.    lda (pcbPtr),y
  9852.    sta p+1
  9853.    ldy #pcbNext
  9854.    lda (pcbPtr),y
  9855.    sta (p),y
  9856.    iny
  9857.    lda (pcbPtr),y
  9858.    sta (p),y
  9859.  
  9860.    ;** q->count -:= 1
  9861.    ldy #pcbQCount
  9862.    lda (q),y
  9863.    sec
  9864.    sbc #1
  9865.    sta (q),y
  9866.    rts
  9867.  
  9868. IrqHandler = *
  9869.    cld
  9870.    lda #bkBOS
  9871.    sta bkSelect
  9872.    lda vic+$19
  9873.    bpl +
  9874.    and #1
  9875.    bne Sixty
  9876. +  lda $dc0d
  9877.  
  9878. Sixty = *
  9879.    sta vic+$19
  9880.    ;** save full context
  9881.    lda mmuZeroPage
  9882.    ldx #$00
  9883.    stx mmuZeroPage
  9884.    ldy #pcbZeroPage
  9885.    sta (activePid),y
  9886.    ldy #pcbSP
  9887.    tsx
  9888.    txa
  9889.    sta (activePid),y
  9890.    ldy #pcbStackPage
  9891.    lda mmuStackPage
  9892.    sta (activePid),y
  9893.    ldy #pcbD506
  9894.    lda $d506
  9895.    sta (activePid),y
  9896.  
  9897.    ;** process interrupt
  9898.    inc jiffyTime+0
  9899.    bne +
  9900.    inc jiffyTime+1
  9901. +  lda delayQueue+pcbQCount
  9902.    beq +
  9903.    jsr DelayIrqAwake
  9904. +  nop
  9905.  
  9906.    ;** select new process
  9907. -  ldy #pcbPriority   ;give cur full count
  9908.    lda (activePid),y
  9909.    iny
  9910.    sta (activePid),y
  9911.    beq ++
  9912. -  ldy #pcbNext       ;find next proc
  9913.    lda (activePid),y
  9914.    tax
  9915.    iny
  9916.    lda (activePid),y
  9917.    stx activePid+0
  9918.    sta activePid+1
  9919. ExitKernel = *
  9920.    ldy #pcbCountdown
  9921.    lda (activePid),y
  9922.    beq ++
  9923.    sec
  9924.    sbc #1
  9925.    sta (activePid),y
  9926.    beq +
  9927.    jmp -
  9928. +  ;check if null process
  9929.    ldy #pcbIsHead
  9930.    lda (activePid),y
  9931.    bpl +
  9932.    iny
  9933.    lda (activePid),y  ;only run null if only proc
  9934.    bne --
  9935. +  ;we've got a winner
  9936.  
  9937.    ;** restore full context and exit
  9938.    ldy #pcbD506
  9939.    lda (activePid),y
  9940.    sta $d506
  9941.    ldy #pcbStackPage
  9942.    lda (activePid),y
  9943.    sta mmuStackPage
  9944.    ldy #pcbSP
  9945.    lda (activePid),y
  9946.    tax
  9947.    txs
  9948.    ldy #pcbZeroPage
  9949.    lda (activePid),y
  9950.    sta mmuZeroPage
  9951.    jmp IrqExit
  9952.  
  9953. DefaultExit = *
  9954.    lda #$00
  9955.    ldx #$00
  9956. Exit = *  ;( .A=code, .X=$00 )
  9957.    jmp Suspend
  9958.    brk
  9959.  
  9960. MyPid = *  ;( ) : .AY=pid
  9961.    lda #$00
  9962.    ldx mmuZeroPage
  9963.    sta mmuZeroPage
  9964.    lda activePid+0
  9965.    ldy activePid+1
  9966.    stx mmuZeroPage
  9967.    clc
  9968.    rts
  9969.  
  9970. MyParentPid = *  ;( ) : .AY=parentPid
  9971.    lda #$00
  9972.    ldx mmuZeroPage
  9973.    sta mmuZeroPage
  9974.    ldy #pcbParent
  9975.    lda (activePid),y
  9976.    pha
  9977.    iny
  9978.    lda (activePid),y
  9979.    tay
  9980.    pla
  9981.    stx mmuZeroPage
  9982.    clc
  9983.    rts
  9984.  
  9985. enterKernSave : buf 4
  9986.  
  9987. EnterKernel = *
  9988.    ;** set up process stack as if it had performed an interrupt
  9989.    ;** necessary if process will block
  9990.    ;** called as a one-level-deep subroutine of the system call
  9991.    sta enterKernSave+2
  9992.    ;** save system-call return address
  9993.    pla
  9994.    sta enterKernSave+0
  9995.    pla
  9996.    sta enterKernSave+1
  9997.    ;** increment user-process return address (rts -> rti)
  9998.    pla
  9999.    clc
  10000.    adc #1
  10001.    sta enterKernSave+3
  10002.    pla
  10003.    adc #0
  10004.    pha
  10005.    lda enterKernSave+3
  10006.    pha
  10007.    ;** set up processor registers as-is, status $00
  10008.    lda #$00
  10009.    pha
  10010.    lda enterKernSave+2
  10011.    pha
  10012.    txa
  10013.    pha
  10014.    tya
  10015.    pha
  10016.    lda $ff00  ;xxx change for multi-banks
  10017.    pha
  10018.    ;** save info into pcb
  10019.    lda mmuZeroPage
  10020.    ldx #$00
  10021.    stx mmuZeroPage
  10022.    ldy #pcbZeroPage
  10023.    sta (activePid),y
  10024.    dey
  10025.    lda mmuStackPage
  10026.    sta (activePid),y
  10027.    dey
  10028.    tsx
  10029.    txa
  10030.    sta (activePid),y
  10031.    ldy #pcbD506
  10032.    lda $d506
  10033.    sta (activePid),y
  10034.    ;** restore system-call return address
  10035.    ;** (continue to use user-process stack)
  10036.    lda enterKernSave+1
  10037.    pha
  10038.    lda enterKernSave+0
  10039.    pha
  10040.    rts
  10041.  
  10042. Suspend = *  ;( )    ;suspend self
  10043.    sei
  10044.    jsr EnterKernel
  10045.    jsr SuspendSub
  10046.    jmp ExitKernel
  10047.  
  10048. SuspendSub = *  ;( activePid ) : activePid, pcbPtr, q=nullPcb
  10049.    ;** Remove the active pid from the ready queue and set another pid to
  10050.    ;** active; set pcbPtr to point to the suspended process; and set the
  10051.    ;** process state to "suspended".
  10052.    lda activePid+0
  10053.    sta pcbPtr+0
  10054.    lda activePid+1
  10055.    sta pcbPtr+1
  10056.    lda #<nullPcb
  10057.    ldy #>nullPcb
  10058.    sta q+0
  10059.    sty q+1
  10060.    jsr QueueUnlink
  10061.    ldy #pcbNext
  10062.    lda (pcbPtr),y
  10063.    sta activePid+0
  10064.    iny
  10065.    lda (pcbPtr),y
  10066.    sta activePid+1
  10067.    ldy #pcbState
  10068.    lda #STATE_SUSPENDED
  10069.    sta (pcbPtr),y
  10070.    rts
  10071.  
  10072. Delay = *  ;( .AY=jiffies ) : .CS=err
  10073.    cmp #0
  10074.    bne +
  10075.    cpy #0
  10076.    bne +
  10077.    clc
  10078.    rts
  10079. +  sei
  10080.    sta delayTime+0
  10081.    sty delayTime+1
  10082.    jsr EnterKernel
  10083.    jsr SuspendSub
  10084.    ldy #pcbState
  10085.    lda #STATE_DELAY
  10086.    ldy #pcbWakeupTime
  10087.    clc
  10088.    lda delayTime+0
  10089.    adc jiffyTime+0
  10090.    sta delayTime+0
  10091.    sta (pcbPtr),y
  10092.    iny
  10093.    lda delayTime+1
  10094.    adc jiffyTime+1
  10095.    sta delayTime+1
  10096.    sta (pcbPtr),y
  10097.    lda #0
  10098.    rol
  10099.    sta delayTime+2
  10100.    lda #<delayQueue
  10101.    ldy #>delayQueue
  10102.    sta q+0
  10103.    sty q+1
  10104.    sta p+0
  10105.    sty p+1
  10106.    jsr DelayFindSpot
  10107.    jsr QueueInsert
  10108.    jmp ExitKernel
  10109.    delayTime : buf 3
  10110.    pTimeHi   : buf 1
  10111.  
  10112. DelayFindSpot = *  ;( (q)=queue, (p)=queueHead, (pcbPtr) ) : p=prevNode
  10113.    jsr IncPtrP
  10114.    ldy #pcbIsHead
  10115.    lda (p),y
  10116.    bne DelayFindSpotExit
  10117.    ldy #pcbWakeupTime
  10118.    lda (p),y
  10119.    cmp jiffyTime+0
  10120.    iny
  10121.    lda (p),y
  10122.    sbc jiffyTime+1
  10123.    ldx #0
  10124.    bcs +
  10125.    inx
  10126. +  stx pTimeHi
  10127.    dey
  10128.    lda delayTime+0
  10129.    cmp (p),y
  10130.    iny
  10131.    lda delayTime+1
  10132.    sbc (p),y
  10133.    lda delayTime+2
  10134.    sbc pTimeHi
  10135.    bcs DelayFindSpot
  10136.  
  10137.    DelayFindSpotExit = *
  10138.    ;xx fall through
  10139.  
  10140. DecPtrP = *  ;( (p) ) : (p):=(p)->prev
  10141.    ldy #pcbPrev
  10142.    lda (p),y
  10143.    tax
  10144.    iny
  10145.    lda (p),y
  10146.    stx p+0
  10147.    sta p+1
  10148.    rts
  10149.  
  10150. IncPtrP = *  ;( (p) ) : (p):=(p)->next
  10151.    ldy #pcbNext
  10152.    lda (p),y
  10153.    tax
  10154.    iny
  10155.    lda (p),y
  10156.    stx p+0
  10157.    sta p+1
  10158.    rts
  10159.  
  10160. DelayIrqAwake = *
  10161.    lda delayQueue+pcbNext+0
  10162.    ldy delayQueue+pcbNext+1
  10163.    sta pcbPtr+0
  10164.    sty pcbPtr+1
  10165.    ldy #pcbWakeupTime
  10166.    lda (pcbPtr),y
  10167.    cmp jiffyTime+0
  10168.    beq +
  10169.    rts
  10170. +  iny
  10171.    lda (pcbPtr),y
  10172.    cmp jiffyTime+1
  10173.    beq +
  10174.    rts
  10175. +  lda #<delayQueue
  10176.    ldy #>delayQueue
  10177.    sta q+0
  10178.    sty q+1
  10179.    jsr QueueUnlink
  10180.    jsr MakeReady
  10181.    jmp DelayIrqAwake
  10182.  
  10183. msgPtrSave : buf 2
  10184.  
  10185. Send = *  ;( .AY=msgBuf ) : .CS:.A=err
  10186.    sei
  10187.    sta msgPtrSave+0
  10188.    sty msgPtrSave+1
  10189.    jsr EnterKernel
  10190.    jsr SuspendSub
  10191.    lda msgPtrSave+0
  10192.    ldy msgPtrSave+1
  10193.    sta msgPtr+0
  10194.    sty msgPtr+1
  10195.    ldy #msgTo
  10196.    lda (msgPtr),y
  10197.    sta q+0
  10198.    iny
  10199.    lda (msgPtr),y
  10200.    sta q+1
  10201.    ;xx should verify that receiver is a process
  10202.    ldy #pcbSendMsgPtr
  10203.    lda msgPtr+0
  10204.    sta (pcbPtr),y
  10205.    iny
  10206.    lda msgPtr+1
  10207.    sta (pcbPtr),y
  10208.    ldy #pcbBlockedOn
  10209.    lda q+0
  10210.    sta (pcbPtr),y
  10211.    iny
  10212.    lda q+1
  10213.    sta (pcbPtr),y
  10214.    ldy #pcbState
  10215.    lda (q),y
  10216.    cmp #STATE_RECEIVE
  10217.    beq SendToReceiverBlocked
  10218.    lda #STATE_SEND
  10219.    sta (pcbPtr),y
  10220.    clc
  10221.    lda q+0
  10222.    adc #pcbSendQHead
  10223.    sta q+0
  10224.    bcc +
  10225.    inc q+1
  10226. +  ldy #pcbPrev
  10227.    lda (q),y
  10228.    sta p+0
  10229.    iny
  10230.    lda (q),y
  10231.    sta p+1
  10232.    jsr QueueInsert
  10233.    jmp ExitKernel
  10234.  
  10235. SendToReceiverBlocked = *
  10236.    lda #STATE_REPLY
  10237.    sta (pcbPtr),y
  10238.    ldy #pcbRecvMsgPtr
  10239.    lda (q),y
  10240.    sta p+0
  10241.    iny
  10242.    lda (q),y
  10243.    sta p+1
  10244.    jsr CopyMessage
  10245.    lda pcbPtr+0
  10246.    ldy pcbPtr+1
  10247.    ldx q+0
  10248.    stx pcbPtr+0
  10249.    ldx q+1
  10250.    stx pcbPtr+1
  10251.    ldx #$00
  10252.    clc
  10253.    jsr SetReturn 
  10254.    jsr MakeReady
  10255.    jmp ExitKernel
  10256.  
  10257. setretSave : buf 4
  10258.  
  10259. SetReturn = *  ;( (pcbPtr)=proc, .AXY=regvals, .C=cval ) : (p)=junk
  10260.    sta setretSave+2
  10261.    stx setretSave+1
  10262.    sty setretSave+0
  10263.    php
  10264.    pla
  10265.    and #$01
  10266.    sta setretSave+3
  10267.    ldy #pcbStackPage
  10268.    lda (pcbPtr),y
  10269.    sta p+1
  10270.    ldy #pcbSP
  10271.    lda (pcbPtr),y
  10272.    clc
  10273.    adc #2
  10274.    sta p+0
  10275.    ldy #3
  10276. -  lda setretSave,y
  10277.    sta (p),y
  10278.    dey
  10279.    bpl -
  10280.    rts
  10281.  
  10282. CopyMessage = *  ;( (pcbPtr)=sender, (msgPtr)=sendmsg, (p)=recvmsg )
  10283.    ldy #msgFrom
  10284.    lda pcbPtr+0
  10285.    sta (msgPtr),y
  10286.    iny
  10287.    lda pcbPtr+1
  10288.    sta (msgPtr),y
  10289.    ldy #msgSize-1
  10290. -  lda (msgPtr),y
  10291.    sta (p),y
  10292.    dey
  10293.    bpl -
  10294.    rts
  10295.  
  10296. Receive = *  ;( .AY=msgBuf ) : .AY=senderPid
  10297.    sei
  10298.    sta msgPtrSave+0
  10299.    sty msgPtrSave+1
  10300.    lda mmuZeroPage
  10301.    pha
  10302.    lda #$00
  10303.    sta mmuZeroPage
  10304.    ldy #pcbSendQCount
  10305.    lda (activePid),y
  10306.    bne ReceiveFromSender
  10307.    pla
  10308.    sta mmuZeroPage
  10309.    jsr EnterKernel
  10310.    jsr SuspendSub
  10311.    lda #STATE_RECEIVE
  10312.    sta (pcbPtr),y
  10313.    ldy #pcbRecvMsgPtr
  10314.    lda msgPtrSave+0
  10315.    sta (pcbPtr),y
  10316.    iny
  10317.    lda msgPtrSave+1
  10318.    sta (pcbPtr),y
  10319.    jmp ExitKernel
  10320.  
  10321. ReceiveFromSender = *  ;( (activePid), (msgPtrSave) )
  10322.    lda activePid+0
  10323.    ldy activePid+1
  10324.    clc
  10325.    adc #pcbSendQHead
  10326.    bcc +
  10327.    iny
  10328. +  sta q+0
  10329.    sty q+1
  10330.    ldy #pcbSendQHead
  10331.    lda (activePid),y
  10332.    sta pcbPtr+0
  10333.    iny
  10334.    lda (activePid),y
  10335.    sta pcbPtr+1
  10336.    jsr QueueUnlink  ;( (q)=queueHead, (pcbPtr)=node )  ;uses p
  10337.    ldy #pcbSendMsgPtr
  10338.    lda (pcbPtr),y
  10339.    sta msgPtr+0
  10340.    iny
  10341.    lda (pcbPtr),y
  10342.    sta msgPtr+1
  10343.    lda msgPtrSave+0
  10344.    ldy msgPtrSave+1
  10345.    sta p+0
  10346.    sty p+1
  10347.    jsr CopyMessage  ;( (pcbPtr)=sender, (msgPtr)=sendmsg, (p)=recvmsg )
  10348.    ldy #pcbState
  10349.    lda #STATE_REPLY
  10350.    sta (pcbPtr),y
  10351.    ldx pcbPtr+0
  10352.    ldy pcbPtr+1
  10353.    pla
  10354.    sta mmuZeroPage
  10355.    txa
  10356.    cli
  10357.    clc
  10358.    rts
  10359.  
  10360. zpPtrSave : buf 1
  10361.  
  10362. Reply = *  ;( .AY=msgBuf[msgRet,msgData] ) : .CS:.A=err
  10363.    sei
  10364.    ;** switch to kernel
  10365.    ldx mmuZeroPage
  10366.    stx zpPtrSave
  10367.    ldx #$00
  10368.    stx mmuZeroPage
  10369.    sta msgPtr+0
  10370.    sty msgPtr+1
  10371.    ;** find and check the sender
  10372.    ldy #msgFrom
  10373.    lda (msgPtr),y
  10374.    sta pcbPtr+0
  10375.    iny
  10376.    lda (msgPtr),y
  10377.    sta pcbPtr+1
  10378.    ;xx verify that receiver is a pcb here
  10379.    ldy #pcbState
  10380.    lda (pcbPtr),y
  10381.    cmp #STATE_REPLY
  10382.    beq +
  10383. -  lda #KERN_ERR_PID_NOT_REPLY
  10384.    ldx zpPtrSave
  10385.    stx mmuZeroPage
  10386.    sec
  10387.    cli
  10388.    rts
  10389. +  ldy #pcbBlockedOn
  10390.    lda (pcbPtr),y
  10391.    cmp activePid+0
  10392.    bne -
  10393.    iny
  10394.    lda (pcbPtr),y
  10395.    cmp activePid+1
  10396.    bne -
  10397.    ;** copy the reply contents
  10398.    ldy #pcbSendMsgPtr
  10399.    lda (pcbPtr),y
  10400.    sta p+0
  10401.    iny
  10402.    lda (pcbPtr),y
  10403.    sta p+1
  10404.    ldy #msgRet
  10405.    lda (msgPtr),y
  10406.    sta (p),y
  10407.    ldy #msgData
  10408. -  lda (msgPtr),y
  10409.    sta (p),y
  10410.    iny
  10411.    cpy #msgData+4
  10412.    bcc -
  10413.    ;** wake up the sender and exit
  10414.    jsr MakeReady
  10415.    ldx zpPtrSave
  10416.    stx mmuZeroPage
  10417.    clc
  10418.    cli
  10419.    rts
  10420.  
  10421. ;======== test application ========
  10422.  
  10423. testNumber : buf 1
  10424.  
  10425. Init = *
  10426.    lda #1
  10427.    sta testNumber
  10428.    lda #<TestSid1
  10429.    ldy #>TestSid1
  10430.    ldx #2
  10431.    jsr Create
  10432.    lda #<TestDelay1
  10433.    ldy #>TestDelay1
  10434.    ldx #1
  10435.    jsr Create
  10436.    lda #<TestDelay2
  10437.    ldy #>TestDelay2
  10438.    ldx #1
  10439.    jsr Create
  10440.    lda #<TestDelay3
  10441.    ldy #>TestDelay3
  10442.    ldx #1
  10443.    jsr Create
  10444.    lda #<TestDelay4
  10445.    ldy #>TestDelay4
  10446.    ldx #1
  10447.    jsr Create
  10448.    lda #<TestDelay5
  10449.    ldy #>TestDelay5
  10450.    ldx #1
  10451.    jsr Create
  10452.    lda #<Blabber1
  10453.    ldy #>Blabber1
  10454.    ldx #1
  10455.    jsr Create
  10456.    lda #<Spinner1
  10457.    ldy #>Spinner1
  10458.    ldx #1
  10459.    jsr Create
  10460.    jmp KernelServer
  10461.  
  10462. TestSid1 = *
  10463.    ldx #$1c-1
  10464.    lda #$00
  10465. -  sta $d400,x
  10466.    dex
  10467.    bpl -
  10468.    lda #$50
  10469.    sta 2
  10470.    sta 3
  10471.    lda #$08
  10472.    sta $d418
  10473.    lda #$00
  10474.    ldy #$08
  10475.    sta $d402
  10476.    sty $d403
  10477.    lda #$41
  10478.    sta $d404
  10479.    lda #$00
  10480.    sta $d405
  10481.    lda #$f0
  10482.    sta $d406
  10483. -  lda 2
  10484.    ldy 3
  10485.    sta $d400
  10486.    sty $d401
  10487.    lda 2
  10488.    ora 3
  10489.    bne +
  10490.    lda #120
  10491.    ldy #0
  10492.    jsr Delay
  10493. +  inc 2
  10494.    bne +
  10495.    inc 3
  10496. +  inc $d020
  10497.    tsx
  10498.    jmp -
  10499.  
  10500. TestDelay1 = *
  10501.    jsr MyParentPid
  10502.    sta testDelay1Msg+msgTo+0
  10503.    sty testDelay1Msg+msgTo+1
  10504.    lda #<testDelay1Txt
  10505.    ldy #>testDelay1Txt
  10506.    sta testDelay1Msg+msgBuf+0
  10507.    sty testDelay1Msg+msgBuf+1
  10508. -  lda #<60
  10509.    ldy #>60
  10510.    jsr Delay
  10511.    inc $581
  10512.    lda #<testDelay1Msg
  10513.    ldy #>testDelay1Msg
  10514.    jsr Send
  10515.    jmp -
  10516.    testDelay1Txt : db "Hi, this is delay process 1 *\n",0
  10517.  
  10518. TestDelay2 = *
  10519.    jsr MyParentPid
  10520.    sta testDelay2Msg+msgTo+0
  10521.    sty testDelay2Msg+msgTo+1
  10522.    lda #<testDelay2Txt
  10523.    ldy #>testDelay2Txt
  10524.    sta testDelay2Msg+msgBuf+0
  10525.    sty testDelay2Msg+msgBuf+1
  10526. -  lda #<120
  10527.    ldy #>120
  10528.    jsr Delay
  10529.    inc $582
  10530.    lda #<testDelay2Msg
  10531.    ldy #>testDelay2Msg
  10532.    jsr Send
  10533.    jmp -
  10534.    testDelay2Txt : db "Hi, this is delay process 2\n",0
  10535.  
  10536. TestDelay3 = *
  10537.    jsr MyParentPid
  10538.    sta testDelay3Msg+msgTo+0
  10539.    sty testDelay3Msg+msgTo+1
  10540.    lda #<testDelay3Txt
  10541.    ldy #>testDelay3Txt
  10542.    sta testDelay3Msg+msgBuf+0
  10543.    sty testDelay3Msg+msgBuf+1
  10544. -  lda #<180
  10545.    ldy #>180
  10546.    jsr Delay
  10547.    inc $583
  10548.    lda #<testDelay3Msg
  10549.    ldy #>testDelay3Msg
  10550.    jsr Send
  10551.    jmp -
  10552.    testDelay3Txt : db "Hi, this is delay process 3\n",0
  10553.  
  10554. TestDelay4 = *
  10555.    jsr MyParentPid
  10556.    sta testDelay4Msg+msgTo+0
  10557.    sty testDelay4Msg+msgTo+1
  10558.    lda #<testDelay4Txt
  10559.    ldy #>testDelay4Txt
  10560.    sta testDelay4Msg+msgBuf+0
  10561.    sty testDelay4Msg+msgBuf+1
  10562. -  lda #<240
  10563.    ldy #>240
  10564.    jsr Delay
  10565.    inc $584
  10566.    lda #<testDelay4Msg
  10567.    ldy #>testDelay4Msg
  10568.    jsr Send
  10569.    jmp -
  10570.    testDelay4Txt : db "Hi, this is delay process 4\n",0
  10571.  
  10572. TestDelay5 = *
  10573.    jsr MyParentPid
  10574.    sta testDelay5Msg+msgTo+0
  10575.    sty testDelay5Msg+msgTo+1
  10576.    lda #<testDelay5Txt
  10577.    ldy #>testDelay5Txt
  10578.    sta testDelay5Msg+msgBuf+0
  10579.    sty testDelay5Msg+msgBuf+1
  10580. -  lda #<300
  10581.    ldy #>300
  10582.    jsr Delay
  10583.    inc $585
  10584.    lda #<testDelay5Msg
  10585.    ldy #>testDelay5Msg
  10586.    jsr Send
  10587.    jmp -
  10588.    testDelay5Txt : db "Hi, this is delay process 5\n",0
  10589.  
  10590. Blabber1 = *
  10591.    jsr MyParentPid
  10592.    sta blabber1Msg+msgTo+0
  10593.    sty blabber1Msg+msgTo+1
  10594.    lda #<blabber1Txt
  10595.    ldy #>blabber1Txt
  10596.    sta blabber1Msg+msgBuf+0
  10597.    sty blabber1Msg+msgBuf+1
  10598. -  inc $580
  10599.    lda #<blabber1Msg
  10600.    ldy #>blabber1Msg
  10601.    jsr Send
  10602.    jmp -
  10603.    blabber1Txt : db "Hi, this is blabber\n",0
  10604.  
  10605. Spinner1 = *
  10606.    jsr MyParentPid
  10607.    sta spinner1Msg+msgTo+0
  10608.    sty spinner1Msg+msgTo+1
  10609.    lda #<spinner1Txt
  10610.    ldy #>spinner1Txt
  10611.    sta spinner1Msg+msgBuf+0
  10612.    sty spinner1Msg+msgBuf+1
  10613. -  inc $580
  10614.    lda #<spinner1Msg
  10615.    ldy #>spinner1Msg
  10616.    jsr Send
  10617.    jmp -
  10618.    spinner1Txt : db "Hi, this is spinner +\n",0
  10619.  
  10620. KernelServer = *
  10621.    lda #$00
  10622.    sta mmuZeroPage
  10623.    lda #14
  10624.    jsr $ffd2
  10625. -  lda #<ksMsg
  10626.    ldy #>ksMsg
  10627.    jsr Receive
  10628.    lda ksMsg+msgBuf+0
  10629.    ldy ksMsg+msgBuf+1
  10630.    sta $80
  10631.    sty $81
  10632.    ldy #0
  10633. -  lda ($80),y
  10634.    beq +
  10635.    jsr $ffd2
  10636.    iny
  10637.    bne -
  10638. +  lda #<ksMsg
  10639.    ldy #>ksMsg
  10640.    jsr Reply
  10641.    jmp --
  10642.  
  10643. bss = *
  10644. testDelay1Msg = $c00  ;** put these here to save pgm memory
  10645. testDelay2Msg = testDelay1Msg+msgSize
  10646. testDelay3Msg = testDelay2Msg+msgSize
  10647. testDelay4Msg = testDelay3Msg+msgSize
  10648. testDelay5Msg = testDelay4Msg+msgSize
  10649. blabber1Msg   = testDelay5Msg+msgSize
  10650. spinner1Msg   = blabber1Msg+msgSize
  10651. ksMsg         = spinner1Msg+msgSize
  10652. -----=-----
  10653.  
  10654. APPENDIX B. UUENCODED DEMO PROGRAM
  10655.  
  10656. The uuencoded demo system follows.  You can extract it with any uudecoder or
  10657. with version 2.00 or higher of "unbcode" (ACE has only version 1.00).
  10658.  
  10659. -nucode-begin 1 bos
  10660. begin 640 bos
  10661. M`!-,)A,``````````````````````````````````````````````'BI#HT`
  10662. M_ZEVH!6-%`.,%0.IJZ`3C18#C!<#J:N@$XT8`XP9`ZD`C203C243J1Z@$X4&
  10663. MA`<@U12I`Z`3C0,3C`03C043C`83A0*$`ZG_C0<3J0"-"!.I((4,J<"-'1.I
  10664. M`HT-$UA,C1.I.Z`9H@$@_1/N``30#>X!!-`([@($T`/N`P1,EA-XJ66@^HT4
  10665. M`XP5`ZE`H/J-&`.,&0.B^IJI`(T'U8T(U:(!C@G5C0K5J02-!M583+=-````
  10666. M````````````````````````````````````````>(W=$XS>$X[?$ZT'U8W<
  10667. M$ZD`C0?5J0"D#(4(A`G(C.`3R(SA$\B$#,#`D`$`HAJI`)WB$\H0^JGVC>@3
  10668. MK>$3C>D3K>`3C>H3J02-ZQ.MWQ.-[!.-[1.E`J0#C?H3C/L3J<6-_!.@&KGB
  10669. M$Y$(B!#XI0@8:0Z%!J4):0"%!R#5%*D`K.$3A02$!:#WJ0Z1!,BB!*D`D03(
  10670. MRM#ZK=T3D03(K=X3D03(J0F1!,BI%I$$(+L4I0BD":[<$XX'U1A88*`:J<"1
  10671. M"*D#H!.%!H0'I0*D`X4$A`4@`!5@I0:D!XWZ%(S[%(W\%(S]%*G_C?X4J0"-
  10672. M_Q2@!;GZ%)$&B!#X8````````!B@!;$&:0&1!J``L021",BQ!)$(R*4$D0C(
  10673. MI061"*``L02%!LBQ!(4'H`*E")$&R*4)D0:@`*4(D03(I0F1!&"@`+$(A03(
  10674. ML0B%!:`"L0B1!,BQ")$$H`*Q"(4$R+$(A06@`+$(D03(L0B1!*`%L08XZ0&1
  10675. M!F#8J0Z-`/^M&=`0!"D!T`.M#=R-&="M!]6B`(X'U:`(D0*@!KJ*D0*@!ZT)
  10676. MU9$"H`FM!M61`NXD$]`#[B43K2,3\`,@71?JH`JQ`LB1`O`GH`"Q`JK(L0*&
  10677. M`H4#H`NQ`O`5..D!D0+P`TS%%:`$L0(0!<BQ`M#0H`FQ`HT&U:`'L0*-"=6@
  10678. M!K$"JIJ@"+$"C0?53#/_J0"B`$R.%@"I`*X'U8T'U:4"I`..!]488*D`K@?5
  10679. MC0?5H!BQ`DC(L0*H:(X'U1A@`````(T\%FB-.A9HC3L6:!AI`8T]%FAI`$BM
  10680. M/19(J0!(K3P62(I(F$BM`/](K0?5H@".!]6@")$"B*T)U9$"B+J*D0*@":T&
  10681. MU9$"K3L62*TZ%DA@>"`^%B"8%DS1%:4"A0BE`X4)J0.@$X4&A`<@0!6@`+$(
  10682. MA0+(L0B%`Z`:J<61"&#)`-`&P`#0`AA@>(T-%XP.%R`^%B"8%J`:J<2@#!BM
  10683. M#1=M)!.-#1>1",BM#A=M)1.-#A>1"*D`*HT/%ZD>H!.%!H0'A02$!2`1%R``
  10684. M%4S1%0`````@4!>@!+$$T"F@#+$$S203R+$$[243H@"P`>B.$!>(K0T7T03(
  10685. MK0X7\02M#Q?M$!>PSJ`"L02JR+$$A@2%!6"@`+$$JLBQ!(8$A05@K1X3K!\3
  10686. MA0B$":`,L0C-)!/P`6#(L0C-)1/P`6"I'J`3A0:$!R!`%2"[%$Q=%P``>(V+
  10687. M%XR,%R`^%B"8%JV+%ZR,%X4*A`N@`+$*A0;(L0J%!Z`,I0J1",BE"Y$(H!2E
  10688. M!I$(R*4'D0B@&K$&R<+P(*G!D0@8I09I#H4&D`+F!Z`"L0:%!,BQ!H4%(``5
  10689. M3-$5J<.1"*`,L0:%!,BQ!H4%($48I0BD":8&A@BF!X8)H@`8(!L8(+L43-$5
  10690. M`````(T9&(X8&(P7&`AH*0&-&AB@![$(A06@!K$(&&D"A02@`[D7&)$$B!#X
  10691. M8*`"I0B1"LBE"9$*H!>Q"I$$B!#Y8'B-BQ>,C!>M!]5(J0"-!]6@$[$"T!YH
  10692. MC0?5(#X6()@6J<*1"*`,K8L7D0C(K8P7D0A,T16E`J0#&&D.D`'(A0:$!Z`.
  10693. ML0*%",BQ`H4)($`5H`RQ"(4*R+$(A0NMBQ>LC!>%!(0%($48H!JIPY$(I@BD
  10694. M"6B-!]6*6!A@`'BN!]6.U!BB`(X'U84*A`N@`K$*A0C(L0J%":`:L0C)P_`+
  10695. MJ>&NU!B.!]4X6&"@%+$(Q0+0[<BQ",4#T.:@#+$(A03(L0B%!:`1L0J1!*`4
  10696. ML0J1!,C`&)#W(+L4KM08C@?5&%A@`*D!C3H9J8N@&:("(/T3J=V@&:(!(/T3
  10697. MJ2.@&J(!(/T3J6>@&J(!(/T3J:N@&J(!(/T3J>^@&J(!(/T3J3.@&Z(!(/T3
  10698. MJ6B@&Z(!(/T33)\;HANI`)T`U,H0^JE0A0*%`ZD(C1C4J0"@"(T"U(P#U*E!
  10699. MC034J0"-!=2I\(T&U*4"I`.-`-2,`=2E`@4#T`>I>*``(+T6Y@+0`N8#[B#0
  10700. MNDRY&2`C%HT`#(P!#*D$H!J-!`R,!0RI/*``(+T6[H$%J0"@#""-%TSP&<A)
  10701. M+"!42$E3($E3($1%3$%9(%!23T-%4U,@,2`J#0`@(Q:-&`R,&0RI2J`:C1P,
  10702. MC!T,J7B@`""]%NZ"!:D8H`P@C1=,-AK(22P@5$A)4R!)4R!$14Q!62!04D]#
  10703. M15-3(#(-`"`C%HTP#(PQ#*F.H!J--`R,-0RIM*``(+T6[H,%J3"@#""-%TQZ
  10704. M&LA)+"!42$E3($E3($1%3$%9(%!23T-%4U,@,PT`(",6C4@,C$D,J=*@&HU,
  10705. M#(Q-#*GPH``@O1;NA`6I2*`,((T73+X:R$DL(%1(25,@25,@1$5,05D@4%)/
  10706. M0T534R`T#0`@(Q:-8`R,80RI%J`;C60,C&4,J2R@`2"]%NZ%!:E@H`P@C1=,
  10707. M`AO(22P@5$A)4R!)4R!$14Q!62!04D]#15-3(#4-`"`C%HUX#(QY#*E3H!N-
  10708. M?`R,?0SN@`6I>*`,((T73$8;R$DL(%1(25,@25,@0DQ!0D)%4@T`(",6C9`,
  10709. MC)$,J8B@&XV4#(R5#.Z`!:F0H`P@C1=,>QO(22P@5$A)4R!)4R!34$E.3D52
  10710. M("L-`*D`C0?5J0X@TO^IJ*`,(%H8K:P,K*T,A8"$@:``L8#P!B#2_\C0]JFH
  10711. (H`P@U1A,J1L`
  10712. `
  10713. end
  10714. -nucode-end 1 2258 1430bdc2
  10715.  
  10716. ========================================================================END===
  10717.  
  10718.